Repository: hkust-taco/mlscript Branch: hkmc2 Commit: fe64200c0ae2 Files: 1787 Total size: 9.9 MB Directory structure: gitextract_ff6x2v5t/ ├── .gitattributes ├── .github/ │ ├── skills/ │ │ └── hkmc2-difftests/ │ │ ├── SKILL.md │ │ ├── agents/ │ │ │ └── openai.yaml │ │ └── references/ │ │ ├── commands-and-policies.md │ │ └── execution-workflow.md │ └── workflows/ │ └── nix.yml ├── .gitignore ├── .sbtopts ├── .scalafmt.conf ├── .vscode/ │ └── settings.json ├── AGENTS.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin/ │ └── mlscript-opt.js ├── build.sbt ├── compiler/ │ └── shared/ │ ├── main/ │ │ └── scala/ │ │ └── mlscript/ │ │ └── compiler/ │ │ ├── ClassLifter.scala │ │ ├── Document.scala │ │ ├── PrettyPrinter.scala │ │ ├── codegen/ │ │ │ ├── CppAst.scala │ │ │ ├── CppCodeGen.scala │ │ │ └── CppCompilerHost.scala │ │ ├── debug/ │ │ │ ├── Debug.scala │ │ │ ├── DebutOutput.scala │ │ │ ├── DummyDebug.scala │ │ │ ├── Printable.scala │ │ │ ├── RainbowDebug.scala │ │ │ └── TreeDebug.scala │ │ ├── ir/ │ │ │ ├── Builder.scala │ │ │ ├── Fresh.scala │ │ │ ├── IR.scala │ │ │ ├── Interp.scala │ │ │ ├── RefResolver.scala │ │ │ └── Validator.scala │ │ ├── optimizer/ │ │ │ ├── Analysis.scala │ │ │ └── TailRecOpt.scala │ │ ├── printer/ │ │ │ └── BlockPrinter.scala │ │ └── simpledef/ │ │ ├── Simpledef.scala │ │ └── Uid.scala │ └── test/ │ ├── diff/ │ │ ├── CompilerScratch.mls │ │ ├── Defunctionalize/ │ │ │ ├── ClassConstructor.mls │ │ │ ├── Classes.mls │ │ │ ├── ClosureCapture.mls │ │ │ ├── Constructor.mls │ │ │ ├── DelayedEvaluation.mls │ │ │ ├── Differentiation.mls │ │ │ ├── FreeVariables.mls │ │ │ ├── FuncsWithParams.mls │ │ │ ├── Inheritance.mls │ │ │ ├── Lambda.mls │ │ │ ├── Lambdas.mls │ │ │ ├── ListConstruction.mls │ │ │ ├── Modules.mls │ │ │ ├── MonoNonLambda.mls │ │ │ ├── MonoTupSelect.mls │ │ │ ├── MutableParams.mls │ │ │ ├── MutualRec.mls │ │ │ ├── NewOperator.mls │ │ │ ├── NuMono.mls │ │ │ ├── ObjFieldAccess.mls │ │ │ ├── ObjFields.mls │ │ │ ├── ObjMultiFields.mls │ │ │ ├── ObjsSelection.mls │ │ │ ├── OldMonoList.mls │ │ │ ├── Polymorphic.mls │ │ │ ├── Record.mls │ │ │ ├── RecursiveFunc.mls │ │ │ ├── SelfReference.mls │ │ │ ├── SimpleClasses.mls │ │ │ ├── SimpleConditionals.mls │ │ │ ├── SimpleFunc.mls │ │ │ ├── Simpledef.mls │ │ │ └── TupleSelect.mls │ │ └── Lifter/ │ │ ├── FunctionTypeAnnotations.mls │ │ ├── LambLift.mls │ │ ├── LiftNew.mls │ │ ├── LiftType.mls │ │ ├── Lifter.mls │ │ ├── LifterBlks.mls │ │ ├── NestedClasses.mls │ │ ├── NestedFuncs.mls │ │ ├── ParameterizedInheritance.mls │ │ └── TypedClassParams.mls │ ├── diff-ir/ │ │ ├── Class.mls │ │ ├── Currying.mls │ │ ├── IR.mls │ │ ├── IRComplex.mls │ │ ├── IRRec.mls │ │ ├── IRTailRec.mls │ │ ├── LiftClass.mls │ │ ├── LiftFun.mls │ │ ├── LiftLambda.mls │ │ ├── NuScratch.mls │ │ ├── Override.mls │ │ ├── cpp/ │ │ │ ├── Makefile │ │ │ └── mlsprelude.h │ │ └── gcd.mls │ └── scala/ │ └── mlscript/ │ └── compiler/ │ ├── Test.scala │ └── TestIR.scala ├── core/ │ └── shared/ │ └── main/ │ └── scala/ │ └── utils/ │ ├── Identity.scala │ ├── Lazy.scala │ ├── algorithms.scala │ ├── package.scala │ └── shorthands.scala ├── doc/ │ ├── Parsing.md │ ├── mls-codebase-doc.md │ └── tuple-patterns.md ├── flake.nix ├── hkmc2/ │ ├── js/ │ │ └── src/ │ │ ├── main/ │ │ │ └── scala/ │ │ │ └── hkmc2/ │ │ │ ├── Compiler.scala │ │ │ ├── io/ │ │ │ │ ├── InMemoryFileSystem.scala │ │ │ │ ├── PlatformFileSystem.scala │ │ │ │ ├── PlatformPath.scala │ │ │ │ ├── VirtualPath.scala │ │ │ │ └── node/ │ │ │ │ ├── NodeFileSystem.scala │ │ │ │ ├── NodePath.scala │ │ │ │ └── package.scala │ │ │ └── utils/ │ │ │ └── PlatformCompilerCache.scala │ │ └── test/ │ │ └── scala/ │ │ └── hkmc2/ │ │ ├── CompilerTest.scala │ │ └── io/ │ │ └── VirtualPathTests.scala │ ├── jvm/ │ │ └── src/ │ │ ├── main/ │ │ │ └── scala/ │ │ │ └── hkmc2/ │ │ │ ├── io/ │ │ │ │ ├── PlatformFileSystem.scala │ │ │ │ └── PlatformPath.scala │ │ │ └── utils/ │ │ │ └── PlatformCompilerCache.scala │ │ └── test/ │ │ └── scala/ │ │ └── hkmc2/ │ │ ├── CompileTestRunner.scala │ │ ├── CompileTestRunnerBase.scala │ │ └── TestFolders.scala │ └── shared/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ ├── hkmc2/ │ │ │ ├── CompilerCtx.scala │ │ │ ├── Config.scala │ │ │ ├── Diagnostic.scala │ │ │ ├── MLsCompiler.scala │ │ │ ├── Message.scala │ │ │ ├── ShowCtx.scala │ │ │ ├── Uid.scala │ │ │ ├── codegen/ │ │ │ │ ├── Block.scala │ │ │ │ ├── BlockChecker.scala │ │ │ │ ├── BlockSimplifier.scala │ │ │ │ ├── BlockTransformer.scala │ │ │ │ ├── BlockTraverser.scala │ │ │ │ ├── BufferableTransform.scala │ │ │ │ ├── CachedAnalysis.scala │ │ │ │ ├── DeadParamElim.scala │ │ │ │ ├── FirstClassFunctionTransformer.scala │ │ │ │ ├── HandlerLowering.scala │ │ │ │ ├── LambdaRewriter.scala │ │ │ │ ├── Lifter.scala │ │ │ │ ├── Lowering.scala │ │ │ │ ├── Printer.scala │ │ │ │ ├── ReflectionInstrumenter.scala │ │ │ │ ├── ScopeData.scala │ │ │ │ ├── ScopeFlattener.scala │ │ │ │ ├── SpecializedSwitch.scala │ │ │ │ ├── StackSafeTransform.scala │ │ │ │ ├── SymbolRefresher.scala │ │ │ │ ├── TailRecOpt.scala │ │ │ │ ├── UsedVarAnalyzer.scala │ │ │ │ ├── cpp/ │ │ │ │ │ ├── Ast.scala │ │ │ │ │ ├── CodeGen.scala │ │ │ │ │ └── CompilerHost.scala │ │ │ │ ├── deforest/ │ │ │ │ │ ├── Deforest.scala │ │ │ │ │ └── Rewrite.scala │ │ │ │ ├── flowAnalysis/ │ │ │ │ │ ├── FlowAnalysis.scala │ │ │ │ │ └── FlowWebComputation.scala │ │ │ │ ├── js/ │ │ │ │ │ └── JSBuilder.scala │ │ │ │ ├── llir/ │ │ │ │ │ ├── Analysis.scala │ │ │ │ │ ├── Builder.scala │ │ │ │ │ ├── Fresh.scala │ │ │ │ │ ├── Interp.scala │ │ │ │ │ └── Llir.scala │ │ │ │ └── wasm/ │ │ │ │ └── text/ │ │ │ │ ├── Ctx.scala │ │ │ │ ├── Instructions.scala │ │ │ │ ├── Wasm.scala │ │ │ │ └── WatBuilder.scala │ │ │ ├── invalml/ │ │ │ │ ├── ConstraintSolver.scala │ │ │ │ ├── InvalML.scala │ │ │ │ ├── NormalForm.scala │ │ │ │ ├── PrettyPrinter.scala │ │ │ │ ├── TypeSimplifier.scala │ │ │ │ ├── TypeTraverser.scala │ │ │ │ └── types.scala │ │ │ ├── io/ │ │ │ │ ├── FileSystem.scala │ │ │ │ └── Path.scala │ │ │ ├── package.scala │ │ │ ├── semantics/ │ │ │ │ ├── BlockImpl.scala │ │ │ │ ├── Elaborator.scala │ │ │ │ ├── Importer.scala │ │ │ │ ├── Pattern.scala │ │ │ │ ├── Resolver.scala │ │ │ │ ├── SimpleSplit.scala │ │ │ │ ├── Split.scala │ │ │ │ ├── Symbol.scala │ │ │ │ ├── SymbolPrinter.scala │ │ │ │ ├── Term.scala │ │ │ │ ├── flow/ │ │ │ │ │ ├── Constraint.scala │ │ │ │ │ └── FlowAnalysis.scala │ │ │ │ ├── ucs/ │ │ │ │ │ ├── FlatPattern.scala │ │ │ │ │ ├── Normalization.scala │ │ │ │ │ ├── SplitElaborator.scala │ │ │ │ │ ├── TermSynthesizer.scala │ │ │ │ │ └── package.scala │ │ │ │ └── ups/ │ │ │ │ ├── Compiler.scala │ │ │ │ ├── Context.scala │ │ │ │ ├── Instantiator.scala │ │ │ │ ├── Pattern.scala │ │ │ │ └── SplitCompiler.scala │ │ │ ├── syntax/ │ │ │ │ ├── Keyword.scala │ │ │ │ ├── Lexer.scala │ │ │ │ ├── ParseRule.scala │ │ │ │ ├── Parser.scala │ │ │ │ ├── Token.scala │ │ │ │ └── Tree.scala │ │ │ ├── syntax.scala │ │ │ ├── typing/ │ │ │ │ └── Type.scala │ │ │ └── utils/ │ │ │ ├── ReplHost.scala │ │ │ ├── ReportFormatter.scala │ │ │ ├── Scope.scala │ │ │ ├── SymbolSubst.scala │ │ │ ├── document/ │ │ │ │ ├── DocBuilder.scala │ │ │ │ ├── Document.scala │ │ │ │ ├── DocumentContext.scala │ │ │ │ ├── Liftable.scala │ │ │ │ └── package.scala │ │ │ └── utils.scala │ │ └── utils/ │ │ ├── EitherOrBoth.scala │ │ ├── FastParseHelpers.scala │ │ └── TraceLogger.scala │ └── test/ │ ├── js/ │ │ ├── MyClass.mjs │ │ └── Test.mjs │ ├── mlscript/ │ │ ├── ChangedTests.cmd │ │ ├── ElabScratch.mls │ │ ├── HkScratch.mls │ │ ├── OverloadedModulesInSignatures.mls │ │ ├── apps/ │ │ │ ├── AccountingTest.mls │ │ │ ├── CSVTest.mls │ │ │ ├── IterTest.mls │ │ │ ├── parsing/ │ │ │ │ ├── .gitignore │ │ │ │ ├── CamlLightTest.mls │ │ │ │ ├── DirectiveTest.mls │ │ │ │ ├── LeftRecursion.mls │ │ │ │ ├── LexerTest.mls │ │ │ │ ├── LoopExpressions.mls │ │ │ │ ├── ParseRuleVisualizerTest.mls │ │ │ │ ├── ParserErrorTest.mls │ │ │ │ ├── ParserTest.mls │ │ │ │ ├── PrattParsingTest.mls │ │ │ │ ├── RecursiveDescentTest.mls │ │ │ │ ├── RulesTest.mls │ │ │ │ └── TypeTest.mls │ │ │ └── parsing-web-demo/ │ │ │ └── ExamplesTest.mls │ │ ├── backlog/ │ │ │ ├── DestructuringBindings.mls │ │ │ ├── ExplicitLabels.mls │ │ │ ├── ForLLM.mls │ │ │ ├── Lifter.mls │ │ │ ├── NewWith.mls │ │ │ ├── NonReturningStatements.mls │ │ │ ├── OfStatements.mls │ │ │ ├── ToTriage.mls │ │ │ └── UCS.mls │ │ ├── basics/ │ │ │ ├── ADTs.mls │ │ │ ├── AppOp.mls │ │ │ ├── Assert.mls │ │ │ ├── BadClasses.mls │ │ │ ├── BadDefs.mls │ │ │ ├── BadMemberProjections.mls │ │ │ ├── BadModuleUses.mls │ │ │ ├── BadModules.mls │ │ │ ├── BadMut.mls │ │ │ ├── BadOverloading.mls │ │ │ ├── BadParams.mls │ │ │ ├── BadTypeClasses.mls │ │ │ ├── BlockArgs.mls │ │ │ ├── ByNameTailPosition.mls │ │ │ ├── CallSyntaxes.mls │ │ │ ├── Classes.mls │ │ │ ├── CompanionFunctions.mls │ │ │ ├── CompanionModules_Classes.mls │ │ │ ├── CompanionModules_Functions.mls │ │ │ ├── CompanionModules_Patterns.mls │ │ │ ├── CompanionModules_Types.mls │ │ │ ├── CyclicModuleForwarders.mls │ │ │ ├── CyclicValues.mls │ │ │ ├── DataClass.mls │ │ │ ├── Declare.mls │ │ │ ├── DisruptiveComments.mls │ │ │ ├── Drop.mls │ │ │ ├── DynamicFields.mls │ │ │ ├── DynamicInstantiation.mls │ │ │ ├── DynamicSelection.mls │ │ │ ├── ExplicitSelections.mls │ │ │ ├── FunDefs.mls │ │ │ ├── FunnyRecordKeys.mls │ │ │ ├── GenericClasses.mls │ │ │ ├── Getters.mls │ │ │ ├── IdentEscape.mls │ │ │ ├── IfThenNewline.mls │ │ │ ├── Indentation.mls │ │ │ ├── Inheritance.mls │ │ │ ├── LazySpreads.mls │ │ │ ├── LetBindings.mls │ │ │ ├── LiteralSelection.mls │ │ │ ├── Literals.mls │ │ │ ├── LocalVal.mls │ │ │ ├── MemberProjections.mls │ │ │ ├── MiscArrayTests.mls │ │ │ ├── ModuleMethods.mls │ │ │ ├── Modules.mls │ │ │ ├── MultiParamListClasses.mls │ │ │ ├── MultiParamLists.mls │ │ │ ├── MultilineExpressions.mls │ │ │ ├── MutArr.mls │ │ │ ├── MutCls.mls │ │ │ ├── MutRcd.mls │ │ │ ├── MutVal.mls │ │ │ ├── NamedArgs.mls │ │ │ ├── NestedBlocks.mls │ │ │ ├── New.mls │ │ │ ├── NewMut.mls │ │ │ ├── NewlineOperatorEnd.mls │ │ │ ├── NewlineOps.mls │ │ │ ├── NewlineSels.mls │ │ │ ├── ObjectExtensions.mls │ │ │ ├── OfLambdaArgs.mls │ │ │ ├── OpBlocks.mls │ │ │ ├── OpDivPrecedence.mls │ │ │ ├── OpenIn.mls │ │ │ ├── OutParams.mls │ │ │ ├── Overloading.mls │ │ │ ├── PartialApps.mls │ │ │ ├── PrefixOps.mls │ │ │ ├── Puns.mls │ │ │ ├── PureTermStatements.mls │ │ │ ├── Records.mls │ │ │ ├── RefinedClasses.mls │ │ │ ├── Return.mls │ │ │ ├── SetIndented.mls │ │ │ ├── ShortcircuitingOps.mls │ │ │ ├── StrTest.mls │ │ │ ├── SuspensionComments.mls │ │ │ ├── ThenRecord.mls │ │ │ ├── Underscores.mls │ │ │ ├── ValMemberSymbols.mls │ │ │ └── WeirdCalls.mls │ │ ├── block-staging/ │ │ │ ├── Functions.mls │ │ │ ├── Nested.mls │ │ │ ├── PrintCode.mls │ │ │ ├── StageSymbols.mls │ │ │ ├── SymbolRenaming.mls │ │ │ └── Syntax.mls │ │ ├── codegen/ │ │ │ ├── Arrays.mls │ │ │ ├── BadConfigDirective.mls │ │ │ ├── BadFunctions.mls │ │ │ ├── BadImport.mls │ │ │ ├── BadInit.mls │ │ │ ├── BadNew.mls │ │ │ ├── BadOpen.mls │ │ │ ├── BadSpreads.mls │ │ │ ├── BadThis.mls │ │ │ ├── BadValInit.mls │ │ │ ├── BadWhile.mls │ │ │ ├── BasicTerms.mls │ │ │ ├── Bindings.mls │ │ │ ├── BlockPrinter.mls │ │ │ ├── BuiltinOps.mls │ │ │ ├── BuiltinSymbols.mls │ │ │ ├── CaseOfCase.mls │ │ │ ├── CaseShorthand.mls │ │ │ ├── ClassInClass.mls │ │ │ ├── ClassInFun.mls │ │ │ ├── ClassMatching.mls │ │ │ ├── Classes.mls │ │ │ ├── CodegenScratch.mls │ │ │ ├── Comma.mls │ │ │ ├── ConfigDirective.mls │ │ │ ├── ConsoleLog.mls │ │ │ ├── CurriedClasses.mls │ │ │ ├── CurriedFunctions.mls │ │ │ ├── DelayedLetInit.mls │ │ │ ├── Do.mls │ │ │ ├── EarlyReturn.mls │ │ │ ├── ElseLess.mls │ │ │ ├── FieldSymbols.mls │ │ │ ├── FirstClassFunctionTransform.mls │ │ │ ├── Formatting.mls │ │ │ ├── FunInClass.mls │ │ │ ├── Functions.mls │ │ │ ├── FunctionsThis.mls │ │ │ ├── FunnyOpen.mls │ │ │ ├── Getters.mls │ │ │ ├── GlobalThis.mls │ │ │ ├── Hygiene.mls │ │ │ ├── IfThenElse.mls │ │ │ ├── ImperativeConditionals.mls │ │ │ ├── ImportAlias.mls │ │ │ ├── ImportConflicts.mls │ │ │ ├── ImportExample.mls │ │ │ ├── ImportJSClass.mls │ │ │ ├── ImportJSModule.mls │ │ │ ├── ImportMLs.mls │ │ │ ├── ImportMLsJS.mls │ │ │ ├── ImportedOps.mls │ │ │ ├── InlineLambdas.mls │ │ │ ├── InlineMultiArgLists.mls │ │ │ ├── Inliner.mls │ │ │ ├── InnerNameHygiene.mls │ │ │ ├── InterleavedRecords.mls │ │ │ ├── Juxtapositions.mls │ │ │ ├── Lambdas.mls │ │ │ ├── Lazy.mls │ │ │ ├── LetPatterns.mls │ │ │ ├── MergeMatchArms.mls │ │ │ ├── Misc.mls │ │ │ ├── ModuleMatching.mls │ │ │ ├── ModuleMethods.mls │ │ │ ├── Modules.mls │ │ │ ├── NestedClasses.mls │ │ │ ├── NestedScoped.mls │ │ │ ├── NestedTypes.mls │ │ │ ├── NoFreeze.mls │ │ │ ├── NoModuleCheck.mls │ │ │ ├── ObjectInit.mls │ │ │ ├── ObjectMethodDebinding.mls │ │ │ ├── Open.mls │ │ │ ├── OpenJS.mls │ │ │ ├── OpenWildcard.mls │ │ │ ├── OptMatch.mls │ │ │ ├── ParamClasses.mls │ │ │ ├── PartialApps.mls │ │ │ ├── PlainClasses.mls │ │ │ ├── PredefUsage.mls │ │ │ ├── Primes.mls │ │ │ ├── Pwd.mls │ │ │ ├── QQImport.mls │ │ │ ├── Quasiquotes.mls │ │ │ ├── RandomStuff.mls │ │ │ ├── ReboundLet.mls │ │ │ ├── RecordSpreads.mls │ │ │ ├── Repl.mls │ │ │ ├── RuntimeUsage.mls │ │ │ ├── SanityChecks.mls │ │ │ ├── ScopedBlocks.mls │ │ │ ├── ScopedBlocksAndHandlers.mls │ │ │ ├── Scoping.mls │ │ │ ├── SelfReferences.mls │ │ │ ├── SetIn.mls │ │ │ ├── SetStmt.mls │ │ │ ├── ShortCircuitReturn.mls │ │ │ ├── SimplePatMat.mls │ │ │ ├── SingletonInit.mls │ │ │ ├── SpecialJSNames.mls │ │ │ ├── Spreads.mls │ │ │ ├── SwitchSpecialization.mls │ │ │ ├── TailRecFormerFailure.mls │ │ │ ├── This.mls │ │ │ ├── ThisCallVariations.mls │ │ │ ├── ThisCalls.mls │ │ │ ├── Throw.mls │ │ │ ├── TraceLog.mls │ │ │ ├── TraceLogIndent.mls │ │ │ ├── UnitValue.mls │ │ │ ├── While.mls │ │ │ ├── WhileDefaults.mls │ │ │ └── i382.mls │ │ ├── ctx/ │ │ │ ├── CascadingTypeClasses.mls │ │ │ ├── ClassCtxParams.mls │ │ │ ├── EtaExpansion.mls │ │ │ ├── ExplicitlySpec.mls │ │ │ ├── ForwardTypeClassUses.mls │ │ │ ├── MissingDefinitions1.mls │ │ │ ├── MissingDefinitions2.mls │ │ │ ├── Summon.mls │ │ │ ├── SymbolResolution.mls │ │ │ ├── TrickyResolution.mls │ │ │ ├── TypeClasses.mls │ │ │ └── TypeResolution.mls │ │ ├── dead-param-elim/ │ │ │ ├── basic.mls │ │ │ ├── clash.mls │ │ │ ├── class-in-fun.mls │ │ │ ├── config-flags.mls │ │ │ ├── dead-ref.mls │ │ │ ├── lambda.mls │ │ │ ├── module.mls │ │ │ ├── multiArgLists.mls │ │ │ ├── recursive.mls │ │ │ ├── refresher.mls │ │ │ ├── todos.mls │ │ │ └── ups.mls │ │ ├── decls/ │ │ │ └── Prelude.mls │ │ ├── deforest/ │ │ │ ├── append.mls │ │ │ ├── basic.mls │ │ │ ├── clashes.mls │ │ │ ├── cyclic.mls │ │ │ ├── determinism.mls │ │ │ ├── dropLast.mls │ │ │ ├── floatoutSafety.mls │ │ │ ├── imperative.mls │ │ │ ├── listComprehension.mls │ │ │ ├── module.mls │ │ │ ├── multiArgLists.mls │ │ │ ├── nestedMatch.mls │ │ │ ├── recursive.mls │ │ │ ├── relaxedPrograms.mls │ │ │ ├── selectionsInNestedMatch.mls │ │ │ ├── simple.mls │ │ │ ├── todos.mls │ │ │ └── zipunzip.mls │ │ ├── flows/ │ │ │ ├── BadFlows.mls │ │ │ ├── BasicFlows.mls │ │ │ ├── Identity.mls │ │ │ ├── LeadingDotAccesses.mls │ │ │ ├── SelExpansion.mls │ │ │ ├── SelExpansionImport.mls │ │ │ └── TypeFlows.mls │ │ ├── handlers/ │ │ │ ├── BadHandlers.mls │ │ │ ├── Debugging.mls │ │ │ ├── EffectInHandler.mls │ │ │ ├── Effects.mls │ │ │ ├── EffectsHygiene.mls │ │ │ ├── EffectsInClasses.mls │ │ │ ├── EffectsInMethods.mls │ │ │ ├── Fallthrough.mls │ │ │ ├── GeneratorStack.mls │ │ │ ├── Generators.mls │ │ │ ├── HandlerBlockBindings.mls │ │ │ ├── HandlerReset.mls │ │ │ ├── HandlersInClasses.mls │ │ │ ├── HandlersInMethods.mls │ │ │ ├── HandlersScratch.mls │ │ │ ├── LeakingEffects.mls │ │ │ ├── ManualEffectBinding.mls │ │ │ ├── ManualStackSafety.mls │ │ │ ├── MultiResumption.mls │ │ │ ├── NestedFun.mls │ │ │ ├── NestedHandlers.mls │ │ │ ├── NoHandler.mls │ │ │ ├── NoStackSafety.mls │ │ │ ├── NonLocalReturns.mls │ │ │ ├── RecursiveHandlers.mls │ │ │ ├── ReturnInHandler.mls │ │ │ ├── SavedVars.mls │ │ │ ├── SetInHandlers.mls │ │ │ ├── StackSafety.mls │ │ │ ├── TailCallOptimization.mls │ │ │ ├── UserThreads.mls │ │ │ ├── UserThreadsSafe.mls │ │ │ ├── UserThreadsUnsafe.mls │ │ │ └── ZCombinator.mls │ │ ├── interop/ │ │ │ ├── Arrays.mls │ │ │ ├── CtorBypass.mls │ │ │ ├── Functions.mls │ │ │ ├── JSCollections.mls │ │ │ ├── JSDerp.mls │ │ │ ├── Null.mls │ │ │ ├── Number.mls │ │ │ ├── Objects.mls │ │ │ ├── Symbols.mls │ │ │ └── Virtualization.mls │ │ ├── invalml/ │ │ │ ├── InvalMLAuthorResponse.mls │ │ │ ├── InvalMLBasicADTs.mls │ │ │ ├── InvalMLBasics.mls │ │ │ ├── InvalMLBorrowing.mls │ │ │ ├── InvalMLBorrowing2.mls │ │ │ ├── InvalMLBounds.mls │ │ │ ├── InvalMLCheck.mls │ │ │ ├── InvalMLCodeGen.mls │ │ │ ├── InvalMLCyclicExtrude.mls │ │ │ ├── InvalMLDP.mls │ │ │ ├── InvalMLDisjoint.mls │ │ │ ├── InvalMLEffectAnnots.mls │ │ │ ├── InvalMLErrors.mls │ │ │ ├── InvalMLExtrude.mls │ │ │ ├── InvalMLFunGenFix.mls │ │ │ ├── InvalMLFuns.mls │ │ │ ├── InvalMLGPCE.mls │ │ │ ├── InvalMLGetters.mls │ │ │ ├── InvalMLId.mls │ │ │ ├── InvalMLLetRegEncoding.mls │ │ │ ├── InvalMLList.mls │ │ │ ├── InvalMLOption.mls │ │ │ ├── InvalMLPoly.mls │ │ │ ├── InvalMLPrelude.mls │ │ │ ├── InvalMLQQ.mls │ │ │ ├── InvalMLRec.mls │ │ │ ├── InvalMLRef.mls │ │ │ ├── InvalMLScratch.mls │ │ │ ├── InvalMLSelfApp.mls │ │ │ ├── InvalMLSeq.mls │ │ │ ├── InvalMLSyntax.mls │ │ │ ├── InvalMLTODOs.mls │ │ │ ├── InvalMLUntyped.mls │ │ │ ├── InvalMLUsefulExtrusion.mls │ │ │ ├── InvalMLVariance.mls │ │ │ └── web-demos/ │ │ │ ├── DynamicProgramming.mls │ │ │ ├── ExamplesInResponse.mls │ │ │ ├── ExamplesInThePaper.mls │ │ │ ├── Exception.mls │ │ │ ├── README.md │ │ │ ├── SimpleConstraintSolver.mls │ │ │ ├── StackMM.mls │ │ │ ├── Staging.mls │ │ │ ├── flix/ │ │ │ │ ├── GUI.mls │ │ │ │ └── Interpreter.mls │ │ │ └── reml/ │ │ │ └── MergeSort.mls │ │ ├── lifter/ │ │ │ ├── ClassInFun.mls │ │ │ ├── ClassWithCompanion.mls │ │ │ ├── CompanionsInFun.mls │ │ │ ├── CurriedClassInFun.mls │ │ │ ├── DefnsInClass.mls │ │ │ ├── EffectHandlers.mls │ │ │ ├── FunInFun.mls │ │ │ ├── FunInMethod.mls │ │ │ ├── Imports.mls │ │ │ ├── Loops.mls │ │ │ ├── ModulesObjects.mls │ │ │ ├── Mutation.mls │ │ │ ├── PatternInFun.mls │ │ │ └── StackSafetyLift.mls │ │ ├── llir/ │ │ │ ├── BadPrograms.mls │ │ │ ├── BasicCpp.mls │ │ │ ├── BasisLLIR.mls │ │ │ ├── Classes.mls │ │ │ ├── ControlFlow.mls │ │ │ ├── Ctor.mls │ │ │ ├── HigherOrder.mls │ │ │ ├── Lazy.mls │ │ │ ├── LazyCycle.mls │ │ │ ├── LlirScratch.mls │ │ │ ├── Method.mls │ │ │ ├── Split.mls │ │ │ ├── Tuple.mls │ │ │ └── nofib/ │ │ │ ├── NofibPrelude.mls │ │ │ ├── atom.mls │ │ │ ├── awards.mls │ │ │ ├── constraints.mls │ │ │ ├── scc.mls │ │ │ └── secretary.mls │ │ ├── meta/ │ │ │ ├── BlockDiffTesting.mls │ │ │ ├── CompilerFiction.mls │ │ │ ├── ImportedTest.mls │ │ │ ├── ImporterTest.mls │ │ │ └── LocDebugging.mls │ │ ├── nofib/ │ │ │ ├── ansi.mls │ │ │ ├── atom.mls │ │ │ ├── awards.mls │ │ │ ├── banner.mls │ │ │ ├── boyer.mls │ │ │ ├── boyer2.mls │ │ │ ├── calendar.mls │ │ │ ├── cichelli.mls │ │ │ ├── circsim.mls │ │ │ ├── clausify.mls │ │ │ ├── constraints.mls │ │ │ ├── cryptarithm1.mls │ │ │ ├── cryptarithm2.mls │ │ │ ├── cse.mls │ │ │ ├── eliza.mls │ │ │ ├── fish.mls │ │ │ ├── gcd.mls │ │ │ ├── input/ │ │ │ │ ├── 1500.1 │ │ │ │ ├── 1500.2 │ │ │ │ ├── Main.hs │ │ │ │ ├── heathcote3.prob │ │ │ │ └── rsa.faststdin │ │ │ ├── integer.mls │ │ │ ├── knights.mls │ │ │ ├── lambda.mls │ │ │ ├── last-piece.mls │ │ │ ├── lcss.mls │ │ │ ├── life.mls │ │ │ ├── mandel.mls │ │ │ ├── mandel2.mls │ │ │ ├── mate.mls │ │ │ ├── minimax.mls │ │ │ ├── para.mls │ │ │ ├── power.mls │ │ │ ├── pretty.mls │ │ │ ├── primetest.mls │ │ │ ├── puzzle.mls │ │ │ ├── rsa.mls │ │ │ ├── scc.mls │ │ │ ├── secretary.mls │ │ │ ├── sorting.mls │ │ │ ├── sphere.mls │ │ │ └── treejoin.mls │ │ ├── objbuf/ │ │ │ ├── Basics.mls │ │ │ ├── Mutation.mls │ │ │ └── ObjectBufferAllocator.mls │ │ ├── opt/ │ │ │ ├── AbortivePrefix.mls │ │ │ ├── DeadObjRemoval.mls │ │ │ ├── DeadSelRemoval.mls │ │ │ ├── DeadStatRemoval.mls │ │ │ └── DeadVarRemoval.mls │ │ ├── parser/ │ │ │ ├── Apply.mls │ │ │ ├── Atoms.mls │ │ │ ├── Block.mls │ │ │ ├── BoolOps.mls │ │ │ ├── Class.mls │ │ │ ├── ConstrainedTypes.mls │ │ │ ├── Extends.mls │ │ │ ├── Handler.mls │ │ │ ├── Indent.mls │ │ │ ├── Let.mls │ │ │ ├── LetLet.mls │ │ │ ├── Modifiers.mls │ │ │ ├── MultiLineCall.mls │ │ │ ├── Mut.mls │ │ │ ├── Of.mls │ │ │ ├── Operators.mls │ │ │ ├── PrefixOps.mls │ │ │ ├── Semis.mls │ │ │ ├── SetCurlyBraces.mls │ │ │ ├── Suspension.mls │ │ │ ├── Then.mls │ │ │ └── Val.mls │ │ ├── std/ │ │ │ ├── FingerTreeListTest.mls │ │ │ ├── IterTest.mls │ │ │ ├── LazyArrayTest.mls │ │ │ ├── LazyFingerTreeTest.mls │ │ │ ├── PredefTest.mls │ │ │ ├── PrettyPrintTest.mls │ │ │ ├── RecordTest.mls │ │ │ ├── RenderingTest.mls │ │ │ └── StackTests.mls │ │ ├── syntax/ │ │ │ ├── AbusiveSuspensions.mls │ │ │ ├── BackslashApps.mls │ │ │ ├── BasicIfs.mls │ │ │ ├── KeywordStutters.mls │ │ │ ├── TypesInTerms.mls │ │ │ ├── WeirdBrackets.mls │ │ │ └── annotations/ │ │ │ ├── AnnotationPrecedence.mls │ │ │ ├── Declarations.mls │ │ │ ├── Pattern.mls │ │ │ └── Unsupported.mls │ │ ├── tailrec/ │ │ │ ├── Annots.mls │ │ │ ├── Errors.mls │ │ │ ├── MultiArgLists.mls │ │ │ ├── Simple.mls │ │ │ └── TailRecOpt.mls │ │ ├── ucs/ │ │ │ ├── AndNewline.mls │ │ │ ├── examples/ │ │ │ │ ├── BinarySearchTree.mls │ │ │ │ ├── EitherOrBoth.mls │ │ │ │ ├── LeftistTree.mls │ │ │ │ ├── ListFold.mls │ │ │ │ ├── Permutations.mls │ │ │ │ ├── SimpleTree.mls │ │ │ │ └── ULC.mls │ │ │ ├── future/ │ │ │ │ ├── AppSplits.mls │ │ │ │ ├── Or.mls │ │ │ │ └── SymbolicClass.mls │ │ │ ├── general/ │ │ │ │ ├── BooleanPatterns.mls │ │ │ │ ├── CardSuits.mls │ │ │ │ ├── CrossModules.mls │ │ │ │ ├── DualOptions.mls │ │ │ │ ├── InterleavedLet.mls │ │ │ │ ├── JoinPoints.mls │ │ │ │ ├── List.mls │ │ │ │ ├── LogicalConnectives.mls │ │ │ │ ├── Seqs.mls │ │ │ │ └── Simple.mls │ │ │ ├── hygiene/ │ │ │ │ ├── CrossBranchCapture.mls │ │ │ │ ├── Hygiene.mls │ │ │ │ ├── HygienicBindings.mls │ │ │ │ └── PatVars.mls │ │ │ ├── normalization/ │ │ │ │ ├── Deduplication.mls │ │ │ │ ├── DeduplicationWhile.mls │ │ │ │ ├── ExcessiveDeduplication.mls │ │ │ │ ├── InheritanceNormalization.mls │ │ │ │ ├── OverlapOfPrimitives.mls │ │ │ │ ├── SimplePairMatches.mls │ │ │ │ ├── UnifySubScrutinees.mls │ │ │ │ └── UnifyTupleElements.mls │ │ │ ├── papers/ │ │ │ │ └── OperatorSplit.mls │ │ │ ├── patterns/ │ │ │ │ ├── AliasPattern.mls │ │ │ │ ├── Compilation.mls │ │ │ │ ├── ConjunctionPattern.mls │ │ │ │ ├── GuardedPatternBindings.mls │ │ │ │ ├── Literals.mls │ │ │ │ ├── NamePattern.mls │ │ │ │ ├── Parameters.mls │ │ │ │ ├── RecordPattern.mls │ │ │ │ ├── Refinement.mls │ │ │ │ ├── RestTuple.mls │ │ │ │ ├── SimpleTuple.mls │ │ │ │ ├── SpreadTrailingIndex.mls │ │ │ │ ├── String.mls │ │ │ │ └── where.mls │ │ │ ├── staging/ │ │ │ │ ├── Unapply.mls │ │ │ │ ├── coverage/ │ │ │ │ │ ├── ConflictedCoveredCases.mls │ │ │ │ │ ├── ConflictedPatterns.mls │ │ │ │ │ ├── CoveredCases.mls │ │ │ │ │ ├── DuplicatedCases.mls │ │ │ │ │ ├── MissingCases.mls │ │ │ │ │ ├── Refinement.mls │ │ │ │ │ ├── SealedClasses.mls │ │ │ │ │ ├── Tautology.mls │ │ │ │ │ └── Unreachable.mls │ │ │ │ ├── edges/ │ │ │ │ │ └── Unconditional.mls │ │ │ │ ├── examples/ │ │ │ │ │ ├── AVLTree.mls │ │ │ │ │ ├── Calculator.mls │ │ │ │ │ ├── JSON.mls │ │ │ │ │ ├── LispInterpreter.mls │ │ │ │ │ ├── List.mls │ │ │ │ │ ├── Option.mls │ │ │ │ │ ├── STLC.mls │ │ │ │ │ ├── SimpleLisp.mls │ │ │ │ │ ├── SimpleList.mls │ │ │ │ │ └── SimpleTree.mls │ │ │ │ ├── legacy/ │ │ │ │ │ ├── DirectLines.mls │ │ │ │ │ ├── ElseIf.mls │ │ │ │ │ ├── ErrorMessage.mls │ │ │ │ │ ├── Exhaustiveness.mls │ │ │ │ │ ├── Humiliation.mls │ │ │ │ │ ├── InterleavedLet.mls │ │ │ │ │ ├── LeadingAnd.mls │ │ │ │ │ ├── LitUCS.mls │ │ │ │ │ ├── MultiwayIf.mls │ │ │ │ │ ├── NestedBranches.mls │ │ │ │ │ ├── NestedPattern.mls │ │ │ │ │ ├── NuPlainConditionals.mls │ │ │ │ │ ├── OverlappedBranches.mls │ │ │ │ │ ├── ParseFailures.mls │ │ │ │ │ ├── ParserFailures.mls │ │ │ │ │ ├── SplitAfterOp.mls │ │ │ │ │ ├── SplitAnd.mls │ │ │ │ │ ├── SplitAroundOp.mls │ │ │ │ │ ├── SplitBeforeOp.mls │ │ │ │ │ ├── SplitOps.mls │ │ │ │ │ ├── SplitScrutinee.mls │ │ │ │ │ ├── ThenIndent.mls │ │ │ │ │ ├── TrivialIf.mls │ │ │ │ │ ├── WeirdIf.mls │ │ │ │ │ ├── WeirdSplit.mls │ │ │ │ │ ├── Wildcard.mls │ │ │ │ │ └── zipWith.mls │ │ │ │ └── stages/ │ │ │ │ ├── PostProcessing.mls │ │ │ │ ├── SpecilizationCollision.mls │ │ │ │ └── Transformation.mls │ │ │ └── syntax/ │ │ │ ├── And.mls │ │ │ ├── BadUCSSyntax.mls │ │ │ ├── ConjunctMatches.mls │ │ │ ├── Do.mls │ │ │ ├── DoProblems.mls │ │ │ ├── Else.mls │ │ │ ├── Empty.mls │ │ │ ├── IfOpSplit.mls │ │ │ ├── Is.mls │ │ │ ├── NestedOpSplits.mls │ │ │ ├── Of.mls │ │ │ ├── PlainConditionals.mls │ │ │ ├── SimpleUCS.mls │ │ │ ├── Split.mls │ │ │ ├── TupleRest.mls │ │ │ └── WithBraces.mls │ │ ├── ups/ │ │ │ ├── BadPatterns.mls │ │ │ ├── BasicStackPatterns.mls │ │ │ ├── Future.mls │ │ │ ├── JoinPatterns.mls │ │ │ ├── LocalPatterns.mls │ │ │ ├── MatchResult.mls │ │ │ ├── RangePatterns.mls │ │ │ ├── RecursiveTransformations.mls │ │ │ ├── SimpleConjunction.mls │ │ │ ├── SimpleTransform.mls │ │ │ ├── TransformFree.mls │ │ │ ├── UpsBugsBacklog.mls │ │ │ ├── examples/ │ │ │ │ ├── BadRecStackParse.mls │ │ │ │ ├── BasicSeqStackParse.mls │ │ │ │ ├── BasicStackParse.mls │ │ │ │ ├── Computation.mls │ │ │ │ ├── DnfCnf.mls │ │ │ │ ├── DoubleOrSum.mls │ │ │ │ ├── DoubleTripleList.mls │ │ │ │ ├── EvaluationContext.mls │ │ │ │ ├── EvaluationContext2.mls │ │ │ │ ├── Extraction.mls │ │ │ │ ├── Flatten.mls │ │ │ │ ├── HindleyMilner.mls │ │ │ │ ├── ListPredicates.mls │ │ │ │ ├── Negation.mls │ │ │ │ ├── PrecedenceClimbStackParse.mls │ │ │ │ ├── Record.mls │ │ │ │ └── TupleSpread.mls │ │ │ ├── nondeterminism/ │ │ │ │ ├── BitArithmetic.mls │ │ │ │ ├── EvenOddTree.mls │ │ │ │ └── LaRbTree.mls │ │ │ ├── parametric/ │ │ │ │ ├── EtaConversion.mls │ │ │ │ ├── HigherOrderPattern.mls │ │ │ │ ├── ListLike.mls │ │ │ │ └── Nullable.mls │ │ │ ├── recursion/ │ │ │ │ ├── BitSeq.mls │ │ │ │ ├── BitTree.mls │ │ │ │ ├── LeafEvenOddTree.mls │ │ │ │ ├── NatBox.mls │ │ │ │ ├── NullTree.mls │ │ │ │ └── SignBox.mls │ │ │ ├── regex/ │ │ │ │ ├── EmailAddress.mls │ │ │ │ ├── EmptyString.mls │ │ │ │ ├── Identifier.mls │ │ │ │ ├── Number.mls │ │ │ │ ├── Separation.mls │ │ │ │ ├── Simplification.mls │ │ │ │ └── TailRepetition.mls │ │ │ ├── specialization/ │ │ │ │ ├── SimpleList.mls │ │ │ │ └── SimpleLiterals.mls │ │ │ ├── syntax/ │ │ │ │ ├── CrossCompilation.mls │ │ │ │ ├── Declaration.mls │ │ │ │ ├── InterestingPatterns.mls │ │ │ │ ├── MixedParameters.mls │ │ │ │ ├── PatternBody.mls │ │ │ │ ├── Precedence.mls │ │ │ │ ├── WrongArguments.mls │ │ │ │ └── WrongArity.mls │ │ │ └── transformation/ │ │ │ └── BindingLess.mls │ │ └── wasm/ │ │ ├── Basics.mls │ │ ├── Binaryen.mls │ │ ├── BuiltinOperators.mls │ │ ├── ClassInheritance.mls │ │ ├── ClassMethods.mls │ │ ├── ControlFlow.mls │ │ ├── Exceptions.mls │ │ ├── MainFunctions.mls │ │ ├── Matching.mls │ │ ├── ReplImports.mls │ │ ├── ScopedLocals.mls │ │ ├── SingletonUnit.mls │ │ ├── Singletons.mls │ │ ├── Strings.mls │ │ └── Tuples.mls │ └── mlscript-compile/ │ ├── .gitignore │ ├── Block.mls │ ├── CSP.mls │ ├── CachedHash.mls │ ├── Char.mls │ ├── Example.mls │ ├── FingerTreeList.mls │ ├── Iter.mls │ ├── LazyArray.mls │ ├── LazyFingerTree.mls │ ├── MutMap.mls │ ├── NoFreeze.mls │ ├── ObjectBuffer.mls │ ├── Option.mls │ ├── Predef.mls │ ├── QuoteExample.mls │ ├── QuoteExample1.mls │ ├── QuoteExample2.mls │ ├── Record.mls │ ├── Rendering.mls │ ├── Runtime.mls │ ├── Shape.mls │ ├── Stack.mls │ ├── StrOps.mls │ ├── Term.mls │ ├── TreeTracer.mls │ ├── XML.mls │ ├── apps/ │ │ ├── Accounting.mls │ │ ├── CSV.mls │ │ ├── parsing/ │ │ │ ├── BasicExpr.mls │ │ │ ├── Expr.mls │ │ │ ├── Extension.mls │ │ │ ├── Keywords.mls │ │ │ ├── Lexer.mls │ │ │ ├── ParseRule.mls │ │ │ ├── ParseRuleVisualizer.mls │ │ │ ├── Parser.mls │ │ │ ├── PrattParsing.mls │ │ │ ├── RecursiveDescent.mls │ │ │ ├── Rules.mls │ │ │ ├── Test.mls │ │ │ ├── Token.mls │ │ │ ├── TokenHelpers.mls │ │ │ ├── Tree.mls │ │ │ ├── TreeHelpers.mls │ │ │ └── vendors/ │ │ │ └── railroad/ │ │ │ └── railroad.css │ │ └── parsing-web-demo/ │ │ ├── Examples.mls │ │ ├── index.html │ │ └── main.mls │ ├── cpp/ │ │ ├── Makefile │ │ ├── mlsaux.cxx │ │ └── mlsprelude.h │ ├── nofib/ │ │ ├── NofibPrelude.mls │ │ ├── ansi.mls │ │ ├── atom.mls │ │ ├── awards.mls │ │ ├── banner.mls │ │ ├── boyer.mls │ │ ├── boyer2.mls │ │ ├── calendar.mls │ │ ├── cichelli.mls │ │ ├── circsim.mls │ │ ├── clausify.mls │ │ ├── constraints.mls │ │ ├── cryptarithm1.mls │ │ ├── cryptarithm2.mls │ │ ├── cse.mls │ │ ├── eliza.mls │ │ ├── fish.mls │ │ ├── gcd.mls │ │ ├── integer.mls │ │ ├── knights.mls │ │ ├── lambda.mls │ │ ├── lastpiece.mls │ │ ├── lcss.mls │ │ ├── life.mls │ │ ├── mandel.mls │ │ ├── mandel2.mls │ │ ├── mate.mls │ │ ├── minimax.mls │ │ ├── para.mls │ │ ├── power.mls │ │ ├── pretty.mls │ │ ├── primetest.mls │ │ ├── puzzle.mls │ │ ├── rsa.mls │ │ ├── scc.mls │ │ ├── secretary.mls │ │ ├── sorting.mls │ │ ├── sphere.mls │ │ └── treejoin.mls │ ├── quotes/ │ │ ├── CSPBar.mls │ │ ├── CSPBaz.mls │ │ ├── CSPFoo.mls │ │ ├── CSPNest.mls │ │ ├── Cubic.mls │ │ ├── Gib12.mls │ │ ├── Opened.mls │ │ ├── QuoteFoo.mls │ │ ├── QuoteInc.mls │ │ └── SafeDiv.mls │ ├── ups/ │ │ ├── DnfCnf.mls │ │ ├── EvaluationContext.mls │ │ ├── EvenOddTree.mls │ │ ├── README.md │ │ └── TruthyFalsy.mls │ └── wasm/ │ └── Wasm.mls ├── hkmc2AppsTests/ │ └── src/ │ └── test/ │ └── scala/ │ └── hkmc2/ │ ├── AppsCompileTestRunner.scala │ └── AppsDiffTestRunner.scala ├── hkmc2Benchmarks/ │ └── src/ │ └── test/ │ ├── bench/ │ │ ├── .gitignore │ │ ├── FingerTreesAsStacks.mls │ │ ├── LazySpreads.mls │ │ ├── mlscript-compile/ │ │ │ └── Benchmark.mls │ │ ├── nofibs-bench1.mls │ │ ├── nofibs-bench2.mls │ │ ├── nofibs-bench3.mls │ │ └── viz.html │ ├── logs/ │ │ └── .gitignore │ └── scala/ │ └── hkmc2/ │ ├── BenchDiffMaker.scala │ ├── BenchTestRunner.scala │ └── Watcher.scala ├── hkmc2DiffTests/ │ └── src/ │ └── test/ │ ├── out/ │ │ └── Basic Document Tests.out │ └── scala/ │ └── hkmc2/ │ ├── DiffMaker.scala │ ├── DiffTestRunner.scala │ ├── DocumentTests.scala │ ├── InvalmlDiffMaker.scala │ ├── JSBackendDiffMaker.scala │ ├── LlirDiffMaker.scala │ ├── MLsDiffMaker.scala │ ├── MainDiffMaker.scala │ ├── WasmDiffMaker.scala │ └── Watcher.scala ├── hkmc2NofibTests/ │ └── src/ │ └── test/ │ └── scala/ │ └── hkmc2/ │ ├── NofibCompileTestRunner.scala │ └── NofibDiffTestRunner.scala ├── hkmc2WasmTests/ │ └── src/ │ └── test/ │ └── scala/ │ └── hkmc2/ │ ├── WasmCompileTestRunner.scala │ └── WasmDiffTestRunner.scala ├── index.css ├── index.html ├── local_testing.html ├── out/ │ ├── apps/ │ │ ├── .gitkeep │ │ └── AccountingTest.md │ └── test.md ├── package.json ├── project/ │ ├── build.properties │ └── plugins.sbt ├── shared/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── mlscript/ │ │ ├── ConstraintSolver.scala │ │ ├── Diagnostic.scala │ │ ├── JSBackend.scala │ │ ├── Lexer.scala │ │ ├── MLParser.scala │ │ ├── Message.scala │ │ ├── NewLexer.scala │ │ ├── NewParser.scala │ │ ├── NormalForms.scala │ │ ├── NuTypeDefs.scala │ │ ├── Parser.scala │ │ ├── Token.scala │ │ ├── TypeDefs.scala │ │ ├── TypeSimplifier.scala │ │ ├── Typer.scala │ │ ├── TyperDatatypes.scala │ │ ├── TyperHelpers.scala │ │ ├── codegen/ │ │ │ ├── Codegen.scala │ │ │ ├── Error.scala │ │ │ ├── Polyfill.scala │ │ │ ├── QQHelper.scala │ │ │ ├── Scope.scala │ │ │ ├── Symbol.scala │ │ │ └── typescript/ │ │ │ └── TsTypegen.scala │ │ ├── helpers.scala │ │ ├── package.scala │ │ ├── pretyper/ │ │ │ ├── Diagnosable.scala │ │ │ ├── PreTyper.scala │ │ │ ├── Scope.scala │ │ │ ├── Symbol.scala │ │ │ └── Traceable.scala │ │ ├── syntax.scala │ │ ├── ucs/ │ │ │ ├── Desugarer.scala │ │ │ ├── context/ │ │ │ │ ├── Context.scala │ │ │ │ ├── Matchable.scala │ │ │ │ ├── Pattern.scala │ │ │ │ └── Scrutinee.scala │ │ │ ├── display.scala │ │ │ ├── package.scala │ │ │ ├── stages/ │ │ │ │ ├── CoverageChecking.scala │ │ │ │ ├── Desugaring.scala │ │ │ │ ├── Normalization.scala │ │ │ │ ├── PartialTerm.scala │ │ │ │ ├── PostProcessing.scala │ │ │ │ ├── Transformation.scala │ │ │ │ └── package.scala │ │ │ └── syntax/ │ │ │ ├── core.scala │ │ │ └── source.scala │ │ └── utils/ │ │ └── FastParseHelpers.scala │ └── test/ │ ├── diff/ │ │ ├── analysis/ │ │ │ ├── Variance.mls │ │ │ └── Weird.mls │ │ ├── basics/ │ │ │ ├── AsBindings.fun │ │ │ ├── Blocks.fun │ │ │ ├── Data.fun │ │ │ ├── Datatypes.fun │ │ │ ├── Either.fun │ │ │ ├── Errors.fun │ │ │ ├── ExplicitDatatypes.fun │ │ │ ├── Flow.fun │ │ │ ├── Intersections.fun │ │ │ ├── Iso.fun │ │ │ ├── Lambdas.fun │ │ │ ├── Literals.fun │ │ │ ├── Negations.fun │ │ │ ├── Operators.fun │ │ │ ├── RecFuns.fun │ │ │ ├── Records.fun │ │ │ ├── Simplesub1.fun │ │ │ ├── Simplesub2.fun │ │ │ ├── Slashes.fun │ │ │ ├── Tuples.fun │ │ │ ├── Twice.fun │ │ │ ├── Unions.fun │ │ │ └── VerboseErrors.fun │ │ ├── codegen/ │ │ │ ├── AuxiliaryConstructors.mls │ │ │ ├── Classes.mls │ │ │ ├── ConstructorStmt.mls │ │ │ ├── Declare.mls │ │ │ ├── Escape.mls │ │ │ ├── FieldOverride.mls │ │ │ ├── IndirectRecursion.mls │ │ │ ├── Inheritance.mls │ │ │ ├── LogInfo.mls │ │ │ ├── MemberInitShadowing.mls │ │ │ ├── Mixin.mls │ │ │ ├── MixinCapture.mls │ │ │ ├── Modules.mls │ │ │ ├── MutFields.mls │ │ │ ├── NamedArgsCurry.mls │ │ │ ├── Nested.mls │ │ │ ├── New.mls │ │ │ ├── NewClasses.mls │ │ │ ├── NewMatching.mls │ │ │ ├── NewMutualRef.mls │ │ │ ├── NuClasses.mls │ │ │ ├── NuFuns.mls │ │ │ ├── NuParentheses.mls │ │ │ ├── NuReplHost.mls │ │ │ ├── ParameterPattern.mls │ │ │ ├── Parentheses.mls │ │ │ ├── PatternMatch.mls │ │ │ ├── Polyfill.mls │ │ │ ├── Recursion.mls │ │ │ ├── ReplHost.mls │ │ │ ├── Res.mls │ │ │ ├── Shadowing.mls │ │ │ ├── SortClass.mls │ │ │ ├── Super.mls │ │ │ ├── SymbolicOps.mls │ │ │ ├── Terms.mls │ │ │ ├── TraitMethods.mls │ │ │ ├── TrickyShadowing.mls │ │ │ ├── TrickyTerms.mls │ │ │ ├── TrickyWiths.mls │ │ │ ├── Unicode.mls │ │ │ ├── ValLet.mls │ │ │ └── While.mls │ │ ├── contys/ │ │ │ ├── AbstractBounds.mls │ │ │ └── ExplicitConstraints.mls │ │ ├── ecoop23/ │ │ │ ├── ComparePointPoly.mls │ │ │ ├── ExpressionProblem.mls │ │ │ ├── Intro.mls │ │ │ ├── PolymorphicVariants.mls │ │ │ ├── SimpleRegionDSL_annot.mls │ │ │ └── SimpleRegionDSL_raw.mls │ │ ├── ex/ │ │ │ ├── RepMin.mls │ │ │ └── UnboxedOptions.mls │ │ ├── fcp/ │ │ │ ├── AA.mls │ │ │ ├── Aleks.mls │ │ │ ├── Aliases.mls │ │ │ ├── Basics.mls │ │ │ ├── Church_CT.mls │ │ │ ├── Church_ST.mls │ │ │ ├── ConstrainedTypes1.mls │ │ │ ├── ConstrainedTypes2.mls │ │ │ ├── Demo.mls │ │ │ ├── Distrib.mls │ │ │ ├── Distrib2.mls │ │ │ ├── DistribRight.mls │ │ │ ├── DistribUnionInter.mls │ │ │ ├── DistribWorsening.mls │ │ │ ├── FCPTony.mls │ │ │ ├── FCPTony3.mls │ │ │ ├── Fiota.mls │ │ │ ├── ForallTerms.mls │ │ │ ├── FunnyId.mls │ │ │ ├── Inst.mls │ │ │ ├── InvariantPolyContainer.mls │ │ │ ├── ListBuild.mls │ │ │ ├── Min1_ex_shallow.mls │ │ │ ├── MoreChurch.mls │ │ │ ├── NestedDataTypes.mls │ │ │ ├── NestedDataTypesGADT.mls │ │ │ ├── NoRecursiveTypes.mls │ │ │ ├── OCamlList.mls │ │ │ ├── OverloadSimplif.mls │ │ │ ├── Overloads.mls │ │ │ ├── Overloads_Precise.mls │ │ │ ├── Paper.mls │ │ │ ├── PaperTable.mls │ │ │ ├── PolyParams.mls │ │ │ ├── PolymorphicTypeAliases.mls │ │ │ ├── Proofs.mls │ │ │ ├── QML_exist_Classes.mls │ │ │ ├── QML_exist_Classes_min.mls │ │ │ ├── QML_exist_Records_D.mls │ │ │ ├── QML_exist_Records_ND.mls │ │ │ ├── QML_exist_Records_min_1.mls │ │ │ ├── QML_exist_Records_min_2.mls │ │ │ ├── QML_exist_Records_min_3.mls │ │ │ ├── QML_exist_nu.mls │ │ │ ├── Rank2.mls │ │ │ ├── RecExtr.mls │ │ │ ├── SelfAppMin.mls │ │ │ ├── Simplif.mls │ │ │ ├── SkolemErrors.mls │ │ │ ├── Skolems.mls │ │ │ ├── Skolems2.mls │ │ │ ├── Skolems3.mls │ │ │ ├── SystemF.mls │ │ │ ├── SystemF_2.mls │ │ │ ├── ToChurchSimplif_CT.mls │ │ │ ├── ToChurchSimplif_ST.mls │ │ │ ├── Vec.mls │ │ │ └── YicongFCP.mls │ │ ├── fcp-lit/ │ │ │ ├── Boxy.mls │ │ │ ├── CPS_LB.mls │ │ │ ├── FreezeML.mls │ │ │ ├── GHC-rank-n.mls │ │ │ ├── HMF.mls │ │ │ ├── HML.mls │ │ │ ├── Jim.mls │ │ │ ├── Leijen.mls │ │ │ ├── MLF.mls │ │ │ ├── Misc.mls │ │ │ ├── PolyML.mls │ │ │ ├── QML.mls │ │ │ ├── Stability.mls │ │ │ └── variations_PolyML.mls │ │ ├── gadt/ │ │ │ ├── Exp1.mls │ │ │ ├── Exp2.mls │ │ │ └── ThisMatching.mls │ │ ├── gen/ │ │ │ └── genTests_v1-0.14-15-x2.fun │ │ ├── mlf-examples/ │ │ │ ├── ex_casparticuliers.mls │ │ │ ├── ex_concrete.mls │ │ │ ├── ex_demo.mls │ │ │ ├── ex_hashtbl.mls │ │ │ ├── ex_predicative.mls │ │ │ ├── ex_propagate.mls │ │ │ ├── ex_rvr_validate.mls │ │ │ ├── ex_selfapp.mls │ │ │ ├── ex_shallow.mls │ │ │ ├── ex_validate.mls │ │ │ ├── ex_voir.mls │ │ │ ├── ex_vr_validate.mls │ │ │ ├── merge_regression_min.mls │ │ │ └── variations_ex_hashtbl.mls │ │ ├── mlscript/ │ │ │ ├── ADTs.mls │ │ │ ├── ADTsRepro.mls │ │ │ ├── Addable.mls │ │ │ ├── AdtStyle.mls │ │ │ ├── AlexJ.mls │ │ │ ├── Annoying.mls │ │ │ ├── Arrays.mls │ │ │ ├── Arrays2.mls │ │ │ ├── Ascribe.mls │ │ │ ├── Baber.mls │ │ │ ├── BadAlias.mls │ │ │ ├── BadClasses.mls │ │ │ ├── BadInherit.mls │ │ │ ├── BadInherit2.mls │ │ │ ├── BadInherit2Co.mls │ │ │ ├── BadMethods.mls │ │ │ ├── BadPolym.mls │ │ │ ├── BadTraits.mls │ │ │ ├── BadTypes.mls │ │ │ ├── Basics.mls │ │ │ ├── Boolean.mls │ │ │ ├── BooleanFail.mls │ │ │ ├── ByNameByValue.mls │ │ │ ├── David.mls │ │ │ ├── David2.mls │ │ │ ├── DavidB.mls │ │ │ ├── Didier.mls │ │ │ ├── Dmitry.mls │ │ │ ├── EitherClasses.mls │ │ │ ├── ExplicitVariance.mls │ │ │ ├── ExprProb.mls │ │ │ ├── ExprProb2.mls │ │ │ ├── ExprProb_Inv.mls │ │ │ ├── FatNegs.mls │ │ │ ├── FunnySubsumptions.mls │ │ │ ├── GenericClasses.mls │ │ │ ├── GenericClasses2.mls │ │ │ ├── Group_2021_12_02.mls │ │ │ ├── Group_2022_05_10.mls │ │ │ ├── Group_2022_06_09.mls │ │ │ ├── HeadOption.mls │ │ │ ├── Huawei2.mls │ │ │ ├── Inherit.mls │ │ │ ├── InheritSimple.mls │ │ │ ├── JetBrains.mls │ │ │ ├── LetPolym.mls │ │ │ ├── LineComments.mls │ │ │ ├── Lists.mls │ │ │ ├── Luyu.mls │ │ │ ├── MLList.mls │ │ │ ├── MLTuples.mls │ │ │ ├── Match1.mls │ │ │ ├── Match2.mls │ │ │ ├── Match3.mls │ │ │ ├── MatchBool.mls │ │ │ ├── MethodAndMatches.mls │ │ │ ├── Methods.mls │ │ │ ├── Methods2.mls │ │ │ ├── Misc.mls │ │ │ ├── MiscExtrusion.mls │ │ │ ├── Mohammad.mls │ │ │ ├── MultiArgs.mls │ │ │ ├── Mut.mls │ │ │ ├── Mut2.mls │ │ │ ├── MutArray.mls │ │ │ ├── Neg.mls │ │ │ ├── NestedClassArgs.mls │ │ │ ├── NestedClassArgs_Co.mls │ │ │ ├── NestedMatch.mls │ │ │ ├── NestedRecursiveMatch.mls │ │ │ ├── OccursCheck.mls │ │ │ ├── Ops.mls │ │ │ ├── OtherErrors.mls │ │ │ ├── Paper.mls │ │ │ ├── PolyVariant.mls │ │ │ ├── PolyVariantCodeReuse.mls │ │ │ ├── Polym.mls │ │ │ ├── PolymExtrusion.mls │ │ │ ├── PolymorphicExtension.mls │ │ │ ├── PreservationFail.mls │ │ │ ├── ProvFlows.mls │ │ │ ├── Random1.mls │ │ │ ├── Random2.mls │ │ │ ├── RecDefs.mls │ │ │ ├── RecErrors.mls │ │ │ ├── RecursiveTypes.mls │ │ │ ├── RecursiveTypes2.mls │ │ │ ├── References.mls │ │ │ ├── Repro.mls │ │ │ ├── RigidVariables.mls │ │ │ ├── SafeDiv.mls │ │ │ ├── Scratch.mls │ │ │ ├── SelfNeg.mls │ │ │ ├── SelfNegs.mls │ │ │ ├── Seqs.mls │ │ │ ├── Sequence.mls │ │ │ ├── Signatures.mls │ │ │ ├── Simple.mls │ │ │ ├── SimpleErrors.mls │ │ │ ├── SimpleMethods.mls │ │ │ ├── Splice.mls │ │ │ ├── Stress.mls │ │ │ ├── StressDNF.mls │ │ │ ├── StressTraits.mls │ │ │ ├── StressUgly.mls │ │ │ ├── Subsume.mls │ │ │ ├── Test.mls │ │ │ ├── This.mls │ │ │ ├── Ticks.mls │ │ │ ├── Tony.mls │ │ │ ├── TraitMatching.mls │ │ │ ├── Traits.mls │ │ │ ├── Trans.mls │ │ │ ├── TrickyExtrusion.mls │ │ │ ├── TrickySimplif.mls │ │ │ ├── Trio.mls │ │ │ ├── TupleArray.mls │ │ │ ├── TupleArray2.mls │ │ │ ├── TypeClasses.mls │ │ │ ├── TypeDefs.mls │ │ │ ├── TypeRanges.mls │ │ │ ├── TypeRefs.mls │ │ │ ├── TypeTags.mls │ │ │ ├── Undef.mls │ │ │ ├── Undefined.mls │ │ │ ├── Under.mls │ │ │ ├── VarCycles.mls │ │ │ ├── Variant-sub-ad-hoc.mls │ │ │ ├── Variant-sub.mls │ │ │ ├── Weird.mls │ │ │ ├── Wildcards.mls │ │ │ ├── With.mls │ │ │ ├── Yicong.mls │ │ │ ├── Yuheng.mls │ │ │ ├── i26.mls │ │ │ ├── i56.mls │ │ │ └── i65.mls │ │ ├── nu/ │ │ │ ├── AbstractClasses.mls │ │ │ ├── Andong.mls │ │ │ ├── Annotations.mls │ │ │ ├── ArrayProg.mls │ │ │ ├── Ascription.mls │ │ │ ├── AuxCtors.mls │ │ │ ├── BadAliases.mls │ │ │ ├── BadBlocks.mls │ │ │ ├── BadClassInherit.mls │ │ │ ├── BadClassSignatures.mls │ │ │ ├── BadClasses.mls │ │ │ ├── BadFieldInit.mls │ │ │ ├── BadLets.mls │ │ │ ├── BadMixins.mls │ │ │ ├── BadScopes.mls │ │ │ ├── BadSignatures.mls │ │ │ ├── BadSuper.mls │ │ │ ├── BadTraits.mls │ │ │ ├── BadUCS.mls │ │ │ ├── BasicClassInheritance.mls │ │ │ ├── BasicClasses.mls │ │ │ ├── BasicMixins.mls │ │ │ ├── CallByName.mls │ │ │ ├── CaseExpr.mls │ │ │ ├── ClassField.mls │ │ │ ├── ClassInstantiation.mls │ │ │ ├── ClassSignatures.mls │ │ │ ├── ClassesInMixins.mls │ │ │ ├── CommaOperator.mls │ │ │ ├── CtorSubtraction.mls │ │ │ ├── CycleTypeDefErrors.mls │ │ │ ├── Darin.mls │ │ │ ├── Dates.mls │ │ │ ├── DecLit.mls │ │ │ ├── Declarations.mls │ │ │ ├── DiamondInherit.mls │ │ │ ├── DidierNu.mls │ │ │ ├── Eduardo.mls │ │ │ ├── EncodedLists.mls │ │ │ ├── Eql.mls │ │ │ ├── EqlClasses.mls │ │ │ ├── Eval.mls │ │ │ ├── EvalNegNeg.mls │ │ │ ├── ExplicitVariance.mls │ │ │ ├── ExpressionProblem_repro.mls │ │ │ ├── ExpressionProblem_small.mls │ │ │ ├── Extrusion.mls │ │ │ ├── FieldRefinement.mls │ │ │ ├── FilterMap.mls │ │ │ ├── FlatIfThenElse.mls │ │ │ ├── FlatIndentFuns.mls │ │ │ ├── FlatMonads.mls │ │ │ ├── FlatMonads_repro.mls │ │ │ ├── FunPatterns.mls │ │ │ ├── FunPoly.mls │ │ │ ├── FunSigs.mls │ │ │ ├── FunnyIndet.mls │ │ │ ├── FunnyPoly.mls │ │ │ ├── GADTMono.mls │ │ │ ├── GenericClassInheritance.mls │ │ │ ├── GenericClasses.mls │ │ │ ├── GenericMethods.mls │ │ │ ├── GenericMixins.mls │ │ │ ├── GenericModules.mls │ │ │ ├── HeungTung.mls │ │ │ ├── Huawei1.mls │ │ │ ├── ImplicitMethodPolym.mls │ │ │ ├── InferredInheritanceTypeArgs.mls │ │ │ ├── InheritanceLevelMismatches.mls │ │ │ ├── IntLit.mls │ │ │ ├── InterfaceGeneric.mls │ │ │ ├── InterfaceMono.mls │ │ │ ├── Interfaces.mls │ │ │ ├── IntraBlockPolymorphism.mls │ │ │ ├── Jonathan.mls │ │ │ ├── LamPatterns.mls │ │ │ ├── LetRec.mls │ │ │ ├── ListConsNil.mls │ │ │ ├── LitMatch.mls │ │ │ ├── LocalLets.mls │ │ │ ├── MIscPoly.mls │ │ │ ├── MemberConfusion.mls │ │ │ ├── MemberIntersections.mls │ │ │ ├── MetaWrap.mls │ │ │ ├── Metaprog.mls │ │ │ ├── MethodSignatures.mls │ │ │ ├── Misc.mls │ │ │ ├── MissingImplBug.mls │ │ │ ├── MissingTypeArg.mls │ │ │ ├── Mixin42.mls │ │ │ ├── MixinParameters.mls │ │ │ ├── ModuleParameters.mls │ │ │ ├── Mut.mls │ │ │ ├── MutLet.mls │ │ │ ├── MutualRec.mls │ │ │ ├── NamedArgs.mls │ │ │ ├── NestedClasses.mls │ │ │ ├── NestedRecords.mls │ │ │ ├── New.mls │ │ │ ├── NewNew.mls │ │ │ ├── NoThisCtor.mls │ │ │ ├── NuAlexJ.mls │ │ │ ├── NuForallTerms.mls │ │ │ ├── NuPolymorphicTypeAliases.mls │ │ │ ├── NuScratch.mls │ │ │ ├── Numbers.mls │ │ │ ├── Object.mls │ │ │ ├── OpLam.mls │ │ │ ├── OptionFilter.mls │ │ │ ├── OverrideShorthand.mls │ │ │ ├── ParamImplementing.mls │ │ │ ├── ParamOverride.mls │ │ │ ├── ParamOverriding.mls │ │ │ ├── ParamPassing.mls │ │ │ ├── Parens.mls │ │ │ ├── PartialApp.mls │ │ │ ├── PolymorphicVariants_Alt.mls │ │ │ ├── PostHocMixinSignature.mls │ │ │ ├── PrivateMemberOverriding.mls │ │ │ ├── RawUnionTraitSignatures.mls │ │ │ ├── Ref.mls │ │ │ ├── RefinedPattern.mls │ │ │ ├── Refinements.mls │ │ │ ├── Res.mls │ │ │ ├── RightAssocOps.mls │ │ │ ├── RigidVariables.mls │ │ │ ├── SelfAppMethods.mls │ │ │ ├── SelfRec.mls │ │ │ ├── Sidney.mls │ │ │ ├── SimpleSymbolicOps.mls │ │ │ ├── SimpleTraitImpl.mls │ │ │ ├── Splices.mls │ │ │ ├── StupidJS.mls │ │ │ ├── Subscripts.mls │ │ │ ├── TODO_Classes.mls │ │ │ ├── ThisRefinedClasses.mls │ │ │ ├── TraitParameters.mls │ │ │ ├── TraitSignatures.mls │ │ │ ├── TrickyExtrusion2.mls │ │ │ ├── TrickyGenericInheritance.mls │ │ │ ├── TupleParamBlunder.mls │ │ │ ├── Tuples.mls │ │ │ ├── TypeAliases.mls │ │ │ ├── TypeOps.mls │ │ │ ├── TypeSelections.mls │ │ │ ├── TypeVariables.mls │ │ │ ├── TypingUnitTerms.mls │ │ │ ├── TypreMembers.mls │ │ │ ├── Unapply.mls │ │ │ ├── UnaryMinus.mls │ │ │ ├── UndefMatching.mls │ │ │ ├── Uninstantiable.mls │ │ │ ├── UnionReordering.mls │ │ │ ├── Unit.mls │ │ │ ├── UserDefinedAnnotations.mls │ │ │ ├── ValSigs.mls │ │ │ ├── Vals.mls │ │ │ ├── Varargs.mls │ │ │ ├── Virtual.mls │ │ │ ├── WeirdDefs.mls │ │ │ ├── WeirdUnions.mls │ │ │ ├── With.mls │ │ │ ├── i180.mls │ │ │ ├── i191.mls │ │ │ ├── repro0.mls │ │ │ ├── repro1.mls │ │ │ ├── repro_EvalNegNeg.mls │ │ │ └── repro_PolymorphicVariants.mls │ │ ├── parser/ │ │ │ ├── Annot.mls │ │ │ ├── Arrays.mls │ │ │ ├── BasicSyntax.mls │ │ │ ├── Binds.mls │ │ │ ├── Blocks.mls │ │ │ ├── Brackets.mls │ │ │ ├── Classes.mls │ │ │ ├── Comments.mls │ │ │ ├── ControversialIfSplits.mls │ │ │ ├── FatArrows.mls │ │ │ ├── FlatMultiArgLams.mls │ │ │ ├── Forall.mls │ │ │ ├── Fun.mls │ │ │ ├── IfThenElse.mls │ │ │ ├── Lambdas.mls │ │ │ ├── Lets.mls │ │ │ ├── Misc.mls │ │ │ ├── MultiLineCalls.mls │ │ │ ├── MultilineFun.mls │ │ │ ├── NamedArrays.mls │ │ │ ├── NegativeLits.mls │ │ │ ├── New.mls │ │ │ ├── Ops.mls │ │ │ ├── Records.mls │ │ │ ├── Select.mls │ │ │ ├── SpecParams.mls │ │ │ ├── Subscripts.mls │ │ │ ├── TypeParams.mls │ │ │ ├── UserDefinedOpsMaybe.mls │ │ │ ├── WeirdIfs.mls │ │ │ ├── Where.mls │ │ │ ├── Whitespace.mls │ │ │ └── boolops.mls │ │ ├── pretyper/ │ │ │ ├── Errors.mls │ │ │ ├── Repro.mls │ │ │ ├── SymbolKind.mls │ │ │ └── ucs/ │ │ │ ├── DualOption.mls │ │ │ ├── NamePattern.mls │ │ │ ├── RecordPattern.mls │ │ │ ├── Refinement.mls │ │ │ ├── SpecilizationCollision.mls │ │ │ ├── Symbol.mls │ │ │ ├── Unapply.mls │ │ │ ├── Unconditional.mls │ │ │ ├── coverage/ │ │ │ │ ├── ConflictedCoveredCases.mls │ │ │ │ ├── ConflictedPatterns.mls │ │ │ │ ├── CoveredCases.mls │ │ │ │ ├── DuplicatedCases.mls │ │ │ │ ├── MissingCases.mls │ │ │ │ ├── Refinement.mls │ │ │ │ ├── SealedClasses.mls │ │ │ │ ├── Tautology.mls │ │ │ │ └── Unreachable.mls │ │ │ ├── examples/ │ │ │ │ ├── AVLTree.mls │ │ │ │ ├── BinarySearchTree.mls │ │ │ │ ├── Calculator.mls │ │ │ │ ├── EitherOrBoth.mls │ │ │ │ ├── JSON.mls │ │ │ │ ├── LeftistTree.mls │ │ │ │ ├── LispInterpreter.mls │ │ │ │ ├── List.mls │ │ │ │ ├── ListFold.mls │ │ │ │ ├── Option.mls │ │ │ │ ├── Permutations.mls │ │ │ │ ├── STLC.mls │ │ │ │ ├── SimpleLisp.mls │ │ │ │ ├── SimpleList.mls │ │ │ │ ├── SimpleTree.mls │ │ │ │ └── ULC.mls │ │ │ ├── patterns/ │ │ │ │ ├── AliasPattern.mls │ │ │ │ ├── Literals.mls │ │ │ │ └── SimpleTuple.mls │ │ │ └── stages/ │ │ │ ├── Normalization.mls │ │ │ ├── PostProcessing.mls │ │ │ └── Transformation.mls │ │ ├── qq/ │ │ │ ├── BadConst.mls │ │ │ ├── Basic.mls │ │ │ ├── Basic2.mls │ │ │ ├── Codegen.mls │ │ │ ├── EffectfulLetInsertion.mls │ │ │ ├── Extrusions.mls │ │ │ ├── Hygiene.mls │ │ │ ├── LetInsertion.mls │ │ │ ├── LetInsertion_repro.mls │ │ │ ├── Multiline.mls │ │ │ ├── Nested.mls │ │ │ ├── NuSyntax.mls │ │ │ ├── PEPM.mls │ │ │ ├── PEPM2.mls │ │ │ ├── PseudoCod.mls │ │ │ ├── QQFlag.mls │ │ │ ├── ScopeTypes.mls │ │ │ ├── Triple.mls │ │ │ ├── Unquote.mls │ │ │ ├── Weird.mls │ │ │ └── WillfulExtrusion.mls │ │ ├── scalac/ │ │ │ └── i13162.mls │ │ ├── tricky/ │ │ │ ├── IrregularSubtypes.mls │ │ │ ├── IrregularSubtypes2.mls │ │ │ └── Pottier.fun │ │ ├── typegen/ │ │ │ ├── TypegenTerms.mls │ │ │ └── TypegenTypedefs.mls │ │ └── ucs/ │ │ ├── AppSplits.mls │ │ ├── CrossBranchCapture.mls │ │ ├── DirectLines.mls │ │ ├── ElseIf.mls │ │ ├── ErrorMessage.mls │ │ ├── Exhaustiveness.mls │ │ ├── Humiliation.mls │ │ ├── Hygiene.mls │ │ ├── HygienicBindings.mls │ │ ├── InterleavedLet.mls │ │ ├── LeadingAnd.mls │ │ ├── LitUCS.mls │ │ ├── MultiwayIf.mls │ │ ├── NestedBranches.mls │ │ ├── NestedOpSplits.mls │ │ ├── NestedPattern.mls │ │ ├── NuPlainConditionals.mls │ │ ├── Or.mls │ │ ├── OverlappedBranches.mls │ │ ├── ParseFailures.mls │ │ ├── ParserFailures.mls │ │ ├── PlainConditionals.mls │ │ ├── SimpleUCS.mls │ │ ├── SplitAfterOp.mls │ │ ├── SplitAnd.mls │ │ ├── SplitAroundOp.mls │ │ ├── SplitBeforeOp.mls │ │ ├── SplitOps.mls │ │ ├── SplitScrutinee.mls │ │ ├── ThenIndent.mls │ │ ├── Tree.mls │ │ ├── TrivialIf.mls │ │ ├── WeirdIf.mls │ │ ├── WeirdSplit.mls │ │ ├── Wildcard.mls │ │ └── zipWith.mls │ └── scala/ │ └── mlscript/ │ ├── CodeGenTestHelpers.scala │ ├── DiffTests.scala │ ├── NodeTest.scala │ └── ReplHost.scala ├── ts/ │ └── dummy.ts ├── ts2mls/ │ ├── js/ │ │ └── src/ │ │ ├── main/ │ │ │ └── scala/ │ │ │ └── ts2mls/ │ │ │ ├── JSWriter.scala │ │ │ ├── TSCompilerInfo.scala │ │ │ ├── TSData.scala │ │ │ ├── TSNamespace.scala │ │ │ ├── TSProgram.scala │ │ │ ├── TSSourceFile.scala │ │ │ └── types/ │ │ │ ├── Converter.scala │ │ │ └── TSType.scala │ │ └── test/ │ │ ├── diff/ │ │ │ ├── Array.d.mls │ │ │ ├── BasicFunctions.d.mls │ │ │ ├── ClassMember.d.mls │ │ │ ├── Dec.d.mls │ │ │ ├── Enum.d.mls │ │ │ ├── Heritage.d.mls │ │ │ ├── HighOrderFunc.d.mls │ │ │ ├── InterfaceMember.d.mls │ │ │ ├── Intersection.d.mls │ │ │ ├── Literal.d.mls │ │ │ ├── MultiFiles.d.mls │ │ │ ├── Namespace.d.mls │ │ │ ├── Optional.d.mls │ │ │ ├── Overload.d.mls │ │ │ ├── Tuple.d.mls │ │ │ ├── Type.d.mls │ │ │ ├── TypeParameter.d.mls │ │ │ ├── Union.d.mls │ │ │ └── Variables.d.mls │ │ ├── scala/ │ │ │ └── ts2mls/ │ │ │ └── TSTypeGenerationTests.scala │ │ └── typescript/ │ │ ├── Array.ts │ │ ├── BasicFunctions.ts │ │ ├── ClassMember.ts │ │ ├── Dec.d.ts │ │ ├── Enum.ts │ │ ├── Heritage.ts │ │ ├── HighOrderFunc.ts │ │ ├── InterfaceMember.ts │ │ ├── Intersection.ts │ │ ├── Literal.ts │ │ ├── Multi1.ts │ │ ├── Multi2.ts │ │ ├── Multi3.ts │ │ ├── Namespace.ts │ │ ├── Optional.ts │ │ ├── Overload.ts │ │ ├── Tuple.ts │ │ ├── Type.ts │ │ ├── TypeParameter.ts │ │ ├── Union.ts │ │ └── Variables.ts │ └── jvm/ │ └── src/ │ └── test/ │ └── scala/ │ └── ts2mls/ │ └── TsTypeDiffTests.scala ├── tsconfig.json └── vim/ └── mlscript.vim ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ * eol=lf bin/mlscript-opt.js linguist-vendored *.mjs linguist-generated hkmc2/shared/src/test/mlscript-compile/RuntimeJS.mjs -linguist-generated ================================================ FILE: .github/skills/hkmc2-difftests/SKILL.md ================================================ --- name: hkmc2-difftests description: Work with HKMC2 DiffTests where golden snapshots are embedded in `.mls` files as `//│ ...` lines and updated in place by the test runner. Use when editing or reviewing files under `hkmc2/shared/src/test/**/*.mls`, running `hkmc2DiffTests` or watcher loops, diagnosing mismatches between diagnostics and expectations (`:e`, `:re`, `:expect`, etc.), or deciding whether rewritten snapshot lines should be committed. --- # HKMC2 DiffTests Run HKMC2 inline golden-file tests and treat file rewrites as first-class test output. ## Quick Workflow 1. Verify local environment once per machine. 2. Edit `.mls` test blocks and test commands. Use `hkmc2/shared/src/test/mlscript/HkScratch.mls` for temporary experiments, or create a new `.mls` file for a new test case. 3. Run direct `sbt` commands (never `cs launch sbt` for this repo). 4. Review rewritten `//│` lines with git diff. 5. Keep intentional rewrites, then rerun until clean. 6. Revert temporary `HkScratch.mls` edits before committing. Read [execution-workflow.md](references/execution-workflow.md) for exact commands and onboarding flow. ## Block Model And Rewrite Model - Parse each `.mls` file as sequential blocks, usually split by blank lines. - Parse `:command` lines as per-block options. - Ignore existing `//│ ...` lines as old snapshots. - Compile/run the block and emit fresh `//│ ...` lines. - Rewrite the file in place when generated output differs. - Fail only on policy violations (unexpected diagnostics, missing expected diagnostics, failed `:expect`, etc.). Do not treat rewritten files as automatic failures. Treat them as candidate snapshot updates and review them. ## Command Semantics To Apply - `:js`: execute JS backend for the block. - `:silent`: suppress auto-printing of defined values. - `:expect `: assert final rendered result equals exact text. - `:pe`, `:e`, `:re`, `:ge`, `:w`: expect parse/type/runtime/codegen/warning diagnostics. - `:fixme` or `:todo`: tolerate temporary failures under current policy. - `:wasm`, `:wat`, `:fwat`, `:swat`, `:llir`: enable lower-level backend outputs. Read [commands-and-policies.md](references/commands-and-policies.md) when you need deeper behavior details or troubleshooting logic. ## Discovery Scope - Discover test files from `hkmc2/shared/src/test/**/*.mls`. - Diff runs exclude paths containing `staging` and `mlscript-compile`. - Assume `.mls` is the only supported extension for these diff tests. ## Guardrails - Use repo README commands as source of truth for test execution. - Use direct `sbt ...` commands for this project. - For focused runs or updates on specific test files, follow the targeted workflows documented in `README.md` (for example `testOnly ... -- -z ...`, focused sets, and `ChangedTests.cmd` with watcher). - Run `npm install` before JS/Wasm-heavy test workflows when dependencies are missing. - Prefer reviewing `git diff`/`git status` immediately after a diff-test run. - Reset only unintended rewritten test files; keep intentional snapshot updates. ================================================ FILE: .github/skills/hkmc2-difftests/agents/openai.yaml ================================================ interface: display_name: "HKMC2 DiffTests" short_description: "Work with HKMC2 inline golden-file DiffTests in .mls files" default_prompt: "Use this skill to edit, run, and validate HKMC2 DiffTests that rewrite //│ snapshots in .mls files." ================================================ FILE: .github/skills/hkmc2-difftests/references/commands-and-policies.md ================================================ # Commands And Policies ## Runner And Discovery - Discovery root: `hkmc2/shared/src/test`. - File type: `.mls`. - Diff test runs exclude paths containing `staging` and `mlscript-compile`. - `DiffMaker.run()` rewrites file content whenever generated output differs from current file text. ## Per-Block Processing Model 1. Read lines sequentially. 2. Parse `:command` lines and update command state. 3. Skip old snapshot lines starting with `//│ ` as input. 4. Execute compilation/runtime pipeline for the block. 5. Emit new `//│ ` output lines. 6. Compare full generated content to original file content; write back if changed. ## High-Value Commands - `:js`: run JS backend path and print/evaluate runtime results. - `:silent`: suppress automatic value printing. - `:expect `: assert the rendered final result equals ``. - `:pe`: expect parse errors. - `:e`: expect type errors. - `:re`: expect runtime errors. - `:ge`: expect compilation/codegen errors. - `:w`: expect warnings. - `:fixme` and `:todo`: expect and tolerate failures (failing in their absence). - `:breakme`: tolerate temporary expected lack of failures. - `:ignore`: ignore failures, but do not expect them, either. - `:wasm`: enable Wasm path. - `:wat`, `:fwat`, `:swat`: print Wasm text variants. - `:llir`: enable LLIR generation (with optional LLIR/C++ helpers in the same family). ## Failure Policy - Unexpected diagnostics fail (error/warning kind mismatches). - Missing expected diagnostics fail (for `:pe`, `:e`, `:re`, `:ge`, `:w`) unless tolerated by `:todo`/`breakme` policy paths. - `:expect` mismatch fails as a runtime diagnostic. - Rewritten files alone are not a failure; they are snapshot updates to review. ## Practical Review Rules - Treat `//│` changes as test artifacts, not source edits. - Commit snapshot rewrites only when behavior changes are intentional. - Prefer narrow reruns (`hkmc2DiffTests/test` or watcher) while iterating on a small set of `.mls` files. ================================================ FILE: .github/skills/hkmc2-difftests/references/execution-workflow.md ================================================ # Execution Workflow ## Prerequisites - Install JDK, `sbt`, and Node.js. - **Run `npm install` in the repository root** to install all required npm packages (TypeScript, Binaryen, etc.). This step is **mandatory** for JS and WASM test paths. In particular, the WASM tests (`hkmc2/shared/src/test/mlscript/wasm/`) depend on the `binaryen` npm package for WAT validation, formatting, and compilation to WebAssembly binary. Skipping `npm install` will cause all WASM tests to fail with module-not-found errors at runtime. ## Core Commands - Full HKMC2 test pass: `sbt hkmc2AllTests/test` - Compiler compile tests: `sbt hkmc2JVM/test` - Diff tests only: `sbt hkmc2DiffTests/test` - Diff watcher: `sbt "~hkmc2DiffTests/Test/run"` Use direct `sbt` commands for this repo. Avoid `cs launch sbt` because of known environment differences. ## Typical Loop 1. Edit a `.mls` test file under `hkmc2/shared/src/test/`. 2. Use `hkmc2/shared/src/test/mlscript/HkScratch.mls` for temporary checks, or create a dedicated new `.mls` file for durable test coverage. 3. Run `sbt hkmc2DiffTests/test` or watcher mode. 4. Inspect rewritten `//│ ...` lines and diagnostics. 5. Check `git status` and `git diff`. 6. Keep intentional output updates; discard accidental rewrites and rerun. 7. Revert temporary `HkScratch.mls` edits before committing. ## Targeted Runs And Updates - Use README-guided focused execution when iterating on specific test files: - `testOnly ... -- -z ` for name-filtered runs. - Focused test path sets in the Scala test harness when needed. - `ChangedTests.cmd` with watcher mode to rerun only unstaged changed tests. - Treat `README.md` as source of truth for exact syntax and current project-specific examples. ## If Results Look Wrong - Confirm you are running from repo root. - Confirm `npm install` completed. - Check whether command markers in the test block changed behavior (`:js`, `:silent`, `:expect`, `:e`, `:re`, etc.). - Re-run with direct `sbt`. ================================================ FILE: .github/workflows/nix.yml ================================================ name: CI with Nix on: pull_request: push: branches: [ mlscript, hkmc2 ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Nix uses: DeterminateSystems/nix-installer-action@main - uses: rrbutani/use-nix-shell-action@v1 with: devShell: .#default - name: Install npm dependencies (TypeScript, Binaryen, etc.) run: npm ci - name: Run test id: run_test # Not running all tests because those outside of hkmc2 are obsolete (will be removed) run: sbt -J-Xmx4096M -J-Xss8M hkmc2AllTests/test # It's useful to see how the tests fail by seeing the diff through the next step continue-on-error: true - name: Check no changes run: | git update-index -q --refresh git diff-files -p --exit-code - name: Fail if tests failed if: steps.run_test.outcome == 'failure' run: exit 1 ================================================ FILE: .gitignore ================================================ target/ .bloop/ .metals/ .bsp/ node_modules/ metals.sbt project/Dependencies.scala project/metals.sbt **.worksheet.sc .DS_Store /sbt/ ================================================ FILE: .sbtopts ================================================ # [2025-09-23] LP: I recommend running the official JDK 21 from Oracle's website, # as it appears to be at least 2x faster than the community editions, at least on an Apple M2 Ultra # (using GraalVM also appears to be similarly slow, surprisingly) -J-server -J-Xss8M # The options below were inspired by a message I got on some runs with more than usual memory retention: # [warn] In the last 10 seconds, 5.067 (50.7%) were spent in GC. [Heap: 0.01GB free of 1.00GB, max 1.00GB] Consider increasing the JVM heap using `-Xmx` or try a different collector, e.g. `-XX:+UseG1GC`, for better performance. # Reduces warm-up time the first time the tests are run -J-Xms2G # Reduces time spent doing GC -J-Xmx4G #-J-XX:+UseG1GC # does not seem to make much of a difference ================================================ FILE: .scalafmt.conf ================================================ version = "3.10.7" runner.dialect = scala3 project { includePaths = [ "glob:**/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/**", "glob:**/hkmc2DiffTests/src/test/scala/hkmc2/WasmDiffMaker.scala" ] } align.preset = none align.stripMargin = false docstrings.style = SpaceAsterisk maxColumn = 120 newlines.source = keep rewrite.rules = [SortModifiers] rewrite.scala3.convertToNewSyntax = true rewrite.scala3.endMarker.insertMinSpan = 20 rewrite.scala3.removeOptionalBraces = true rewrite.trailingCommas.style = always ================================================ FILE: .vscode/settings.json ================================================ { "files.associations": { "*.fun": "typescript", "*.mls": "mlscript" }, "typescript.validate.enable": false, "files.watcherExclude": { "**/target": true }, "[typescript]": { "editor.quickSuggestions": { "comments": "off", "other": "off", "strings": "off" } }, "files.autoSave": "off" } ================================================ FILE: AGENTS.md ================================================ # Using the MLscript code base ## Workflow It is best to leave the SBT shell open (by just typing the `sbt` command line) and issue commands directly in that shell, as SBT startup is very slow. Before running individual tests, make sure to first run `hkmc2JVM/test`, so that the compilation-tests (in `hkmc2/shared/src/test/mlscript-compile/`) are run and produce the JS files that are needed at runtime for the other tests (in `hkmc2/shared/src/test/mlscript/`). Before finishing your work, run `hkmc2AllTests/test` to make sure the codebase compiles and all tests succeed. After you are done fixing all the problems and all the tests pass, *you need to commit the resulting golden test output changes*. Any commit that does not include the latest changes to test outputs will fail the CI. Please also read the files in `.github/skills/hkmc2-difftests`. ## Coding Style Never use `asInstanceOf` unless absolutely necessary. If you find yourself using `asInstanceOf`, it's a sign that your code may need to be refactored to be more type-safe. Never use default arguments in core business logic. Default arguments should be reserved for user-facing APIs. Do not remove existing `end` markers. ================================================ FILE: CONTRIBUTING.md ================================================ Here are some quick notes on contributing to MLscript. (Written by Lionel, aka @LPTK.) ## Contribution Quality The bar for contributing to this project is quite high. If you're a student, you should be ready to put a lot more efforts and polishing into your changes than you would for a course or personal project. In fact, I expect a lot more polishing than would be expected for many other research projects. ### For Draft PRs It is okay for draft PRs to contain work-in-progress changes that are not expected to be merged yet. You can ask my opinion on your changes or on specific design considerations, but you should not expect a full and detailed review from me if your PR is a draft. ### For Non-Draft PRs I am an extremely detail-oriented person, and I will usually look at every single line of changed source code in your PR. Therefore, **before you request a review from me**, please make sure that **you have reviewed _the entire diff_ of your changes carefully**. I should not have to put in more work than you in revieweing the diff – if I end up doing so, it means you have somehow failed at your contribution job and you have wasted some of my time. You should be able to explain and defend every single line of your changes. In particular, please be mindful not to pollute your PR with random whitespace changes and other leftovers from your local experimentations. Ideally, I should be able to review your changes by asking a few clarifying questions and making a few minor suggestions. If some code is not clear enough to understand with a little bit of context, it would be best to document the logic in the code using comments. The way this is _not_ supposed to happen is that I have to keep coming back to your PR, continually finding more problems and more unpolished or unclear changes, and having to go through lots of back-and-forth interactions this way. This will only lead to growing frustration on both ends. Please be considerate of my time, which is often extremely scarce, and make sure I never have to ask for a change or clarification more than once. ## Pull Request Etiquette During the PR review process, we make use of GitHub comments to track suggestions and clarification requests. Resolving all comment threads is necessary for GitHub to allow the PR to be merged. I realize it is not always clear to everyone when comment threads should be resolved, and by whom. The general principles are the following: * If the comment is a change suggestion... * If it's clear and uncontroversial how to apply the suggestion, you should resolve the comment after you have made the corresponding changes to your code _and_ pushed that code to your branch. * If you are not 100% sure that you have applied the suggestion correctly, leave a comment asking me to have a look. DO NOT resolve the comment in this case. * If you don't fully understand or agree with the suggestion, reply to the comment with your questions and rebuttals. DO NOT resolve the comment in this case. * If the comment is a clarification request, answer it as best you can and wait for my feedback. DO NOT resolve the comment in this case. I will either come back with further questions or suggestions, or close the comment MYSELF if I find your answer satisfactory. Essentially, a comment that I made must _either_ receive an answer from you (as a child comment) and left unresolved _or_ be 100% addressed in your code and marked resolved (but only if you are sure about it; if not, leave a child comment and don't resolve the discussion). The reason for these guidleines is mostly that GitHub's UI is poor and rather inappropriate. In particular, I will not see a comment answer you make if you resolve the conversation, because the UI will usually not show your response to me. (As a small digression, I think the UI would be better if _both_ the commenter _and_ the PR author would need to independently mark a comment as resolved before the comment is considered truly resolved by GitHub and subsequently hidden.) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 Lionel Parreaux Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # MLscript What would TypeScript look like if was designed with type inference, soundness, and pattern matching in mind? This original question lead to the creation of the MLscript programming language, a modern object-oriented and functional programming language for the Web. Since then, the goals of the language have evolved, and MLscript has become a more ambitious effort at defining a next-generation high-level programming language, aiming to maximize reliability, expressiveness, and performance. We are notably adding new backends, such as WASM, so this is no longer "just" a scripting language. Hence, the name of the next iteration of the language will also most likely change. This repository contains a few iterations of the MLscript experimental compiler, and most notably: - [MLscript Compiler Version 1](#mlscript-compiler-version-1) - [MLscript Compiler Version 2 (hkmc2)](#mlscript-compiler-version-2-hkmc2) The latter is in active development, on the `hkmc2` branch. The continuous integration (CI) has been set up to only test this version of the compiler. The `mlscript` branch contains the last commit where the CI was set up to test the old version-1 compiler. The webpage at https://hkust-taco.github.io/mlscript/ still demonstrates the old version-1 compiler. An online demo of hkmc2 (already somewhat outdated) can be found at https://mlscript.fun/. # MLscript Compiler Version 1 This legacy version of the compiler provided basic JavaScript code-gen along with advanced type checking for object-oriented and functional programming with records, generic classes, mix-in traits, first-class unions and intersections, instance matching, and ML-style principal type inference. These features can be used to implement expressive class hierarchies as well as extensible sums and products. The approach supports union, intersection, and complement (or negation) connectives, making sure they form a Boolean algebra, and adds enough structure to derive a sound and complete type inference algorithm. Many extensions were also added over time and splintered off into supporting research paper artifacts, such as more advanced extensible programming support, expressive pattern matching syntax and compilation, and first-class polymorphism (see: [Publications](#publications)). ## Getting Started ### Project Structure - The `shared/src/main/scala/mlscript` directory contains the sources of the MLscript compiler. - The `shared/src/test/scala/mlscript` directory contains the sources of the testing infrastructure. - The `shared/src/test/diff` directory contains the actual tests. - The `ts2mls/js/src/main/scala/ts2mls` directory contains the sources of the ts2mls module. - The `ts2mls/js/src/test/scala/ts2mls` directory contains the sources of the ts2mls declaration generation test code. - The `ts2mls/jvm/src/test/scala/ts2mls` directory contains the sources of the ts2mls diff test code. - The `ts2mls/js/src/test/typescript` directory contains the TypeScript test code. - The `ts2mls/js/src/test/diff` directory contains the declarations generated by ts2mls. - The `ts2mls` experimental sub-project allows one to use TypeScript libraries in MLscript. It can generate libraries' declaration information in MLscript by parsing TypeScript AST, which can be used in MLscript type checking. ### Prerequisites You need [JDK supported by Scala][supported-jdk-versions], [sbt][sbt], [Node.js][node.js], and TypeScript to compile the project and run the tests. We recommend you to install JDK and sbt via [coursier][coursier]. The versions of Node.js that passed our tests are from v16.14 to v16.17, v17 and v18. **Run `npm install` in the repository root** to install required npm packages (including TypeScript). **Note that ScalaJS cannot find globally installed TypeScript.** We explicitly support TypeScript v4.7.4. [supported-jdk-versions]: https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html [sbt]: https://www.scala-sbt.org/ [node.js]: https://nodejs.org/ [coursier]: https://get-coursier.io/ ### Running the tests Running the main MLscript tests only requires the Scala Build Tool installed. In the terminal, run `sbt mlscriptJVM/test`. Running the ts2mls MLscript tests requires NodeJS, and TypeScript in addition. In the terminal, run `sbt ts2mlsTest/test`. You can also run all tests simultaneously. In the terminal, run `sbt test`. ### Running tests individually Individual tests can be run with `-z`. For example, `~mlscriptJVM/testOnly mlscript.DiffTests -- -z parser` will watch for file changes and continuously run all parser tests (those that have "parser" in their name). You can also indicate the test you want in `shared/src/test/scala/mlscript/DiffTests.scala`: ```scala // Allow overriding which specific tests to run, sometimes easier for development: private val focused = Set[Str]( // Add the test file path here like this: "shared/src/test/diff/mlscript/Methods.mls" ).map(os.RelPath(_)) ``` To run the tests in ts2mls sub-project individually, you can indicate the test you want in `ts2mls/js/src/test/scala/ts2mls/TSTypeGenerationTests.scala`: ```scala private val testsData = List( // Put all input files in the `Seq` // Then indicate the output file's name (Seq("Array.ts"), "Array.d.mls") ) ``` ### Running the web demo locally To run the demo on your computer, compile the project with `sbt fastOptJS`, then open the `local_testing.html` file in your browser. You can make changes to the type inference code in `shared/src/main/scala/mlscript`, have it compile to JavaScript on file change with command `sbt ~fastOptJS`, and immediately see the results in your browser by refreshing the page with `F5`. # MLscript Compiler Version 2 (hkmc2) This is the second iteration of the MLscript compiler, nicknamed _hkmc2_ (Hong Kong MLscript Compiler v2). ## Getting Started ### Project Structure Most of the important code of the new compiler is in the `hkmc2` folder. ### Prerequisites You need [JDK supported by Scala][supported-jdk-versions], [sbt][sbt], [Node.js][node.js], and TypeScript to compile the project and run the tests. We recommend you to install JDK and sbt via [coursier][coursier]. The versions of Node.js that passed our tests are from v16.14 to v16.17, v17 and v18. **Run `npm install` in the repository root** to install the required npm packages. This installs TypeScript (used by the JS backend tests) as well as [Binaryen][binaryen] (used by the WASM backend tests in `hkmc2/shared/src/test/mlscript/wasm/`). If you skip this step, **WASM tests will fail** at runtime because the `binaryen` module cannot be found by Node.js. **Note that ScalaJS cannot find globally installed TypeScript.** We explicitly support TypeScript v4.7.4. [supported-jdk-versions]: https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html [sbt]: https://www.scala-sbt.org/ [node.js]: https://nodejs.org/ [coursier]: https://get-coursier.io/ [binaryen]: https://github.com/WebAssembly/binaryen Some tests in the `compiler` subproject generate and compile C++ while making use of some libraries. You can run these by installing `nix` (for MacOS, we recommend https://determinate.systems/posts/graphical-nix-installer/) and running `nix develop` before launching SBT. If you don't want to use nix, you can install the dependencies manually as follows, but this has not been tested on non-MacOS systems: ```bash brew install mimalloc boost gmp ``` ### Running the tests Running the tests requires the Scala Build Tool (SBT) installed. We recommend running all tests in the SBT shell, i.e., do not restart SBT every time, but launch it in shell mode (with command `sbt`) and then use one of the following commands. - `hkmc2JVM/test` for running only the main compilation tests, in `hkmc2/shared/src/test/mlscript-compile`. - `hkmc2DiffTests/test` for running only the main diff-tests, in `hkmc2/shared/src/test/mlscript`. - `hkmc2MainTests/test` for running the above two. - `hkmc2AppsTests/test` for running the applications compile and diff tests. - `hkmc2NofibTests/test` for running the nofib compile and diff tests. - `hkmc2WasmTests/test` for running the wasm compile and diff tests. - `hkmc2MostTests/test` for running all of the above. - `hkmc2AllTests/test` for running all hkmc2 tests, including ScalaJS-compiled tests. - `~hkmc2DiffTests/Test/run` for running the test watcher, which updates test files as you save them and recompiles the Scala sources automatically on change. - `test` for compiling all JVM and JS subprojects and running every single test in the repository, including obsolete ones. Another useful SBT incantation is `; hkmc2MostTests/test; ~hkmc2DiffTests/Test/run`. This command runs all hkmc2 tests once and then starts the test watcher. This is a useful command to use periodically while making changes to the compiler, to check that you haven't broken anything. Note that when saved, the special file `ChangedTests.cmd` will trigger the test watcher to run all tests that currently have unstaged changes in git. This is useful when you have a working subset of tests that you want to run periodically. ### Running tests individually Individual tests can be run with `-z`. For example, `~mlscriptJVM/testOnly mlscript.DiffTests -- -z parser` will watch for file changes and continuously run all parser tests (those that have "parser" in their name). # Publications The following research publications notably used various iterations of the MLscript compiler for their implementations. Links to the individual paper artifact repositories are provided at the corresponding paper webpages. * [**OOPSLA '22**] [MLstruct: Principal Type Inference in a Boolean](https://cse.hkust.edu.hk/~parreaux/publication/oopsla22a/) used v1 with the old ML-like syntax. * [**ECOOP '23**] [super-Charging Object-Oriented Programming Through Precise Typing of Open Recursion](https://cse.hkust.edu.hk/~parreaux/publication/ecoop23/) (🏆 Distinguished Artifact!) used v1 with the current Scala-like syntax and a new object system. * [**OOPSLA '23**] [Getting into the Flow: Towards Better Type Error Messages for Constraint-Based Type Inference](https://cse.hkust.edu.hk/~parreaux/publication/oopsla23/) used v1 with an OCaml-syntax parser and heavily modified type checker. * [**POPL '24**] [When Subtyping Constraints Liberate: A Novel Approach to Type Inference for First-Class Polymorphism](https://cse.hkust.edu.hk/~parreaux/publication/popl24/) used v1 with the old ML-like syntax. * [**OOPSLA '24**] [The Ultimate Conditional Syntax](https://cse.hkust.edu.hk/~parreaux/publication/oopsla24b/) (🏆 Distinguished Paper!) used v1 with the current Scala-like syntax; the parser had many problems – the reimplementation of the Ultimate Conditional Syntax in hkmc2 is of much higher quality. * [**GPCE '24**] [Seamless Scope-Safe Metaprogramming Through Polymorphic Subtype Inference](https://cse.hkust.edu.hk/~parreaux/publication/gpce24/) used v1 with the current Scala-like syntax. * [**OOPSLA '25**] [A Lightweight Type-and-Effect System for Invalidation Safety](https://cse.hkust.edu.hk/~parreaux/publication/oopsla25/) used hkmc2. ================================================ FILE: bin/mlscript-opt.js ================================================ let typecheck; (function(){ 'use strict';var f,aaa=Object.freeze({esVersion:6,assumingES6:!0,productionMode:!0,linkerVersion:"1.11.0",fileLevelThis:this}),aa;function baa(a){for(var b in a)return b}function ba(a){this.eQ=a}ba.prototype.toString=function(){return String.fromCharCode(this.eQ)};var daa=function caa(a,b,c){var e=new a.Ia(b[c]);if(c>24===a?da(eaa):a<<16>>16===a?da(faa):da(gaa):ja(a)?da(haa):da(iaa);case "boolean":return da(jaa);case "undefined":return da(la);default:return null===a?a.N0():a instanceof ma?da(kaa):a instanceof ba?da(laa):a&&a.$classData?da(a.$classData):null}} function na(a){switch(typeof a){case "string":return"java.lang.String";case "number":return ha(a)?a<<24>>24===a?"java.lang.Byte":a<<16>>16===a?"java.lang.Short":"java.lang.Integer":ja(a)?"java.lang.Float":"java.lang.Double";case "boolean":return"java.lang.Boolean";case "undefined":return"java.lang.Void";default:return null===a?a.N0():a instanceof ma?"java.lang.Long":a instanceof ba?"java.lang.Character":a&&a.$classData?a.$classData.name:null.qi.name}} function oa(a,b){switch(typeof a){case "string":return pa(a,b);case "number":return maa(qa(),a,b);case "boolean":return a===b?0:a?1:-1;default:return a instanceof ma?ua(xa(),a.W,a.Y,b.W,b.Y):a instanceof ba?Ea(a)-Ea(b)|0:a.sl(b)}} function La(a,b){switch(typeof a){case "string":return a===b;case "number":return Object.is(a,b);case "boolean":return a===b;case "undefined":return a===b;default:return a&&a.$classData||null===a?a=a.i(b):a instanceof ma?b instanceof ma?(b=Za(b),a=a.W===b.W&&a.Y===b.Y):a=!1:a=a instanceof ba?b instanceof ba?Ea(a)===Ea(b):!1:gb.prototype.i.call(a,b),a}} function ib(a){switch(typeof a){case "string":return lb(a);case "number":return mb(a);case "boolean":return a?1231:1237;case "undefined":return 0;default:return a&&a.$classData||null===a?a.B():a instanceof ma?a.W^a.Y:a instanceof ba?Ea(a):gb.prototype.B.call(a)}}function nb(a){return void 0===a?"undefined":a.toString()}function pb(a,b){if(0===b)throw new qb("/ by zero");return a/b|0}function Bb(a,b){if(0===b)throw new qb("/ by zero");return a%b|0} function Eb(a){return 2147483647a?-2147483648:a|0}function Fb(a,b,c,d,e){if(a!==c||d>=BigInt(32);return b;case "boolean":return a?1231:1237;case "undefined":return 0;case "symbol":return a=a.description,void 0===a?0:lb(a);default:if(null===a)return 0;b=Mb.get(a);void 0===b&&(Kb=b=Kb+1|0,Mb.set(a,b));return b}}function Zb(a){return"number"===typeof a&&a<<24>>24===a&&1/a!==1/-0} function $b(a){return"number"===typeof a&&a<<16>>16===a&&1/a!==1/-0}function ha(a){return"number"===typeof a&&(a|0)===a&&1/a!==1/-0}function ja(a){return"number"===typeof a&&(a!==a||Math.fround(a)===a)}function hc(a){return new ba(a)}function Ea(a){return null===a?0:a.eQ}function Za(a){return null===a?aa:a}function tc(){return new gb}function gb(){}gb.prototype.constructor=gb;function p(){}p.prototype=gb.prototype;gb.prototype.B=function(){return Qb(this)}; gb.prototype.i=function(a){return this===a};gb.prototype.u=function(){var a=this.B();return na(this)+"@"+(+(a>>>0)).toString(16)};gb.prototype.toString=function(){return this.u()};function zc(a){if("number"===typeof a){this.a=Array(a);for(var b=0;bh===g;g.name=c;g.isPrimitive=!0;g.isInstance=()=>!1;void 0!==d&&(g.bB=id(g,d,e));return g} function q(a,b,c,d,e){var g=new fd,h=baa(a);g.rb=d;g.lv="L"+c+";";g.sv=k=>!!k.rb[h];g.name=c;g.isInterface=b;g.isInstance=e||(k=>!!(k&&k.$classData&&k.$classData.rb[h]));return g}function id(a,b,c,d){var e=new fd;b.prototype.$classData=e;var g="["+a.lv;e.Ia=b;e.rb={g:1,Ff:1,l:1};e.kB=a;e.fy=a;e.gy=1;e.lv=g;e.name=g;e.isArrayClass=!0;e.sv=d||(h=>e===h);e.iz=c?h=>new b(new c(h)):h=>new b(h);e.isInstance=h=>h instanceof b;return e} function naa(a){function b(k){if("number"===typeof k){this.a=Array(k);for(var l=0;l{var l=k.gy;return l===e?d.sv(k.fy):l>e&&d===jd};c.sv=h;c.iz= k=>new b(k);c.isInstance=k=>{k=k&&k.$classData;return!!k&&(k===c||h(k))};return c}function md(a){a.bB||(a.bB=naa(a));return a.bB}function da(a){a.rJ||(a.rJ=new nd(a));return a.rJ}fd.prototype.isAssignableFrom=function(a){return this===a||this.sv(a)};fd.prototype.checkCast=function(){};fd.prototype.getSuperclass=function(){return this.S2?da(this.S2):null};fd.prototype.getComponentType=function(){return this.kB?da(this.kB):null}; fd.prototype.newArrayOfThisClass=function(a){for(var b=this,c=0;c!a.isPrimitive;jd.name="java.lang.Object";jd.isInstance=a=>null!==a;jd.bB=id(jd,zc,void 0,a=>{var b=a.gy;return 1===b?!a.fy.isPrimitive:1{throw vb;}),!1)),m=new Ke(k,l),n=Le(m),r=Me(m,new Ne(136),new Oe("parseAll"));a:{if(r instanceof z){var v=r.z;if(null!==v){var x=v.h(),A=v.j();for(b=r;;){if(b.b())C=!1;else var B=b.e().h(),C=Pe(new E(B),Se());if(C)b=b.f();else break}var D=b.Jg(),F=D.b()?G(new H,x,A):D.o();if(null===F)throw new w(F); var I=F.h(),M=F.j(),N=new Te(new Ue(J(new K,["Expected end of input; found "," instead"]))),P=[We(Xe(),I.jb())],T=Ye(N,J(new K,P));t();var Y=G(new H,T,new L(M)),Z=O().c;Ze(m,new z(Y,Z));break a}}var S=O().c;if(null===S?null!==r:!S.i(r))throw new w(r);}var ea=new $e,ia=new cf(ef(n));ff(gf(),"Parsed: "+ia+"\n");var X=new hf(!1,!1,!1,!0),sa=new y(vb=>{throw vb;}),Ja=mf(raa(X));t();var Xa=nf(),Fa=of(X,n,t().d,tf(Ja),sa,Xa),za=saa(ea.sb?ea.vb:ke(ea,X),Fa,(t(),new L(!0)),(ea.sb||ke(ea,X),!0),Ja),Qa=uf(X, za,!1,Ja);Q();vf();var Ma=O().c,Ga=wf(0,new z(Qa,Ma),!0,"'"),ab=yf(Qa,0,Ga),Hb=je(je(zf(ab)," ","\x26nbsp;\x26nbsp;"),"\n","\x3cbr/\x3e"),bc=""+he('\x3cdiv\x3e\x3ctable width\x3d"100%"\x3e\n | \x3ctr\x3e\n | \x3ctd colspan\x3d"2"\x3e\x3ch4\x3e\x3ci\x3eTyping Results:\x3c/i\x3e\x3c/h4\x3e\x3c/td\x3e\n | \x3c/tr\x3e\n |')+he('\x3ctr\x3e\n | \x3ctd colspan\x3d"2"\x3e'+ Hb+"\x3c/td\x3e\n |\x3c/tr\x3e\n |"),yb=taa(new Af,ia);if(null===yb)throw new w(yb);var tb=yb.j(),eb=ze(yb.h(),"","\n",""),kb=oaa(a,eb);if(kb instanceof fe)var Rb=kb.aa;else{if(!(kb instanceof Ud))throw new w(kb);Rb=paa(Bf(tb,kb.fa))}var Gb=""+he('\x3ctr\x3e\n | \x3ctd colspan\x3d"2"\x3e\x3ch4\x3e\x3ci\x3eExecution Results:\x3c/i\x3e\x3c/h4\x3e\x3c/td\x3e\n |\x3c/tr\x3e\n |')+ Rb+"\x3c/table\x3e";a=bc+Gb}catch(vb){if(m=vb instanceof Vd?vb:new $d(vb),m instanceof Df){n=null;n=ce();Xe();x=m.vt().m();x=uaa(new Ef(x,new y(Tb=>Tb.h())));if(m instanceof Ff)A='\u2554\u2550\u2550 \x3cstrong style\x3d"color: #E74C3C"\x3e[ERROR]\x3c/strong\x3e ';else{if(!(m instanceof Gf))throw new w(m);A='\u2554\u2550\u2550 \x3cstrong style\x3d"color: #F39C12"\x3e[WARNING]\x3c/strong\x3e '}B=-1+m.vt().K()|0;C=C=0;for(D=Hf(m.vt());!D.b();){I=D.e();a:{if(null!==I&&(F=I.h(),M=I.Sc(),null!==F)){N=F.h(); I=F.j();F=Lf(new E(M),B);N=Mf(N,x);Lf(new E(M),0)?(M=A+N,ee(n,d),ee(n,M)):(M=(F&&I.b()?"\u2559\u2500\u2500":"\u255f\u2500\u2500")+" "+N,ee(n,d),ee(n,M));ee(n,a.as);I.b()?(M=m.vt().K(),M=Lf(new E(M),1)):M=!1;M&&(ee(n,d),ee(n,"\u2559\u2500\u2500"),ee(n,a.as));if(!I.b()){I=I.o();M=Nf(I.dh.Us,I.fh);if(null===M)throw new w(M);P=M.kc|0;T=M.Rd|0;Lf(new E(C),0)&&(C=C+(-1+P|0)|0);N=Nf(I.dh.Us,I.eh);if(null===N)throw new w(N);M=N.kc|0;N=N.Rd|0;for(ia=T;P<=M;)T="l."+(-1+(I.dh.rx+P|0)|0)+": ",Z=I.dh.Us.$C.va(-1+ P|0),ea=Lf(new E(P),M)?N:1+Z.length|0,Y=Of(Q(),Z,0,-1+ia|0),ia='\x3cu style\x3d"text-decoration: #E74C3C dashed underline"\x3e'+Of(Q(),Z,-1+ia|0,-1+ea|0)+"\x3c/u\x3e",Z=Of(Q(),Z,-1+ea|0,Z.length),T="\u2551 "+T+"\t"+Y+ia+Z,ee(n,d),ee(n,T),ee(n,a.as),ia=1,P=1+P|0,F&&(ee(n,d),ee(n,"\u2559\u2500\u2500"),ee(n,a.as))}break a}throw new w(I);}D=D.f()}m.vt().b()&&(ee(n,d),ee(n,"\u2559\u2500\u2500"),ee(n,a.as));a=n.yf.ja}else a='\n \x3cfont color\x3d"Red"\x3e\n Unexpected error: '+m+(vaa(m),"")+ "\x3c/font\x3e"}c.innerHTML=a}oe.prototype.$classData=q({uT:0},!1,"Main$",{uT:1,g:1});var Pf;function Qf(){Pf||(Pf=new oe);return Pf}function Rf(){}Rf.prototype=new p;Rf.prototype.constructor=Rf;Rf.prototype.$classData=q({xT:0},!1,"fastparse.internal.Util$",{xT:1,g:1});var Sf;function nd(a){this.$J=null;this.qi=a}nd.prototype=new p;nd.prototype.constructor=nd;nd.prototype.u=function(){return(this.qi.isInterface?"interface ":Yf(this)?"":"class ")+this.qi.name}; function Zf(a,b){return!!a.qi.isAssignableFrom(b.qi)}function $f(a){return!!a.qi.isArrayClass}function Yf(a){return!!a.qi.isPrimitive} function ag(a){if(null===a.$J){if($f(a))var b=ag(bg(a))+"[]";else{b=a.qi.name;for(var c=-1+b.length|0;;)if(0<=c&&36===b.charCodeAt(c))c=-1+c|0;else break;if(0<=c){var d=b.charCodeAt(c);d=48<=d&&57>=d}else d=!1;if(d){for(c=-1+c|0;;)if(0<=c?(d=b.charCodeAt(c),d=48<=d&&57>=d):d=!1,d)c=-1+c|0;else break;for(;;)if(0<=c&&36===b.charCodeAt(c))c=-1+c|0;else break}for(;;)if(0<=c?(d=b.charCodeAt(c),d=46!==d&&36!==d):d=!1,d)c=-1+c|0;else break;b=b.substring(1+c|0)}a.$J=b}return a.$J} function bg(a){return a.qi.getComponentType()}nd.prototype.$classData=q({W0:0},!1,"java.lang.Class",{W0:1,g:1});function cg(){this.dK=this.cK=this.Dt=this.qB=null;this.bK=!1;this.BQ=this.AQ=0;dg=this;this.qB=new ArrayBuffer(8);this.Dt=new Int32Array(this.qB,0,2);this.cK=new Float32Array(this.qB,0,2);this.dK=new Float64Array(this.qB,0,1);this.Dt[0]=16909060;this.AQ=(this.bK=1===((new Int8Array(this.qB,0,8))[0]|0))?0:1;this.BQ=this.bK?1:0}cg.prototype=new p;cg.prototype.constructor=cg; function ig(a,b){var c=b|0;if(c===b&&-Infinity!==1/b)return c;a.dK[0]=b;return(a.Dt[0]|0)^(a.Dt[1]|0)}function jg(a,b){a.Dt[0]=b;return Math.fround(a.cK[0])}function kg(a,b){a.cK[0]=b;return a.Dt[0]|0}function lg(a,b){a.dK[0]=b;return new ma(a.Dt[a.BQ]|0,a.Dt[a.AQ]|0)}cg.prototype.$classData=q({a1:0},!1,"java.lang.FloatingPointBits$",{a1:1,g:1});var dg;function mg(){dg||(dg=new cg);return dg}function pg(a,b,c,d){this.j1=a;this.DQ=b;this.l1=c;this.k1=d}pg.prototype=new p;pg.prototype.constructor=pg; pg.prototype.$classData=q({i1:0},!1,"java.lang.Long$StringRadixInfo",{i1:1,g:1});function qg(){}qg.prototype=new p;qg.prototype.constructor=qg;qg.prototype.$classData=q({m1:0},!1,"java.lang.Math$",{m1:1,g:1});var rg; function sg(a,b){var c=tg(a);if(ug().ny.call(c,b))a=tg(a)[b];else a:for(c=0;;)if(c<(Dg(a).length|0)){var d=Dg(a)[c];if(0<=b.length&&b.substring(0,d.length)===d){a=""+Eg(a)[d]+b.substring(d.length);break a}c=1+c|0}else{a=0<=b.length&&"L"===b.substring(0,1)?b.substring(1):b;break a}return a.split("_").join(".").split("\uff3f").join("_")} function tg(a){if(0===(1&a.ln)<<24>>24&&0===(1&a.ln)<<24>>24){for(var b={O:"java_lang_Object",T:"java_lang_String"},c=0;22>=c;)2<=c&&(b["T"+c]="scala_Tuple"+c),b["F"+c]="scala_Function"+c,c=1+c|0;a.FQ=b;a.ln=(1|a.ln)<<24>>24}return a.FQ} function Eg(a){0===(2&a.ln)<<24>>24&&0===(2&a.ln)<<24>>24&&(a.GQ={sjsr_:"scala_scalajs_runtime_",sjs_:"scala_scalajs_",sci_:"scala_collection_immutable_",scm_:"scala_collection_mutable_",scg_:"scala_collection_generic_",sc_:"scala_collection_",sr_:"scala_runtime_",s_:"scala_",jl_:"java_lang_",ju_:"java_util_"},a.ln=(2|a.ln)<<24>>24);return a.GQ}function Dg(a){0===(4&a.ln)<<24>>24&&0===(4&a.ln)<<24>>24&&(a.EQ=Object.keys(Eg(a)),a.ln=(4|a.ln)<<24>>24);return a.EQ} function Fg(a){return(a.stack+"\n").replace(Gg("^[\\s\\S]+?\\s+at\\s+")," at ").replace(Rg("^\\s+(at eval )?at\\s+","gm"),"").replace(Rg("^([^\\(]+?)([\\n])","gm"),"{anonymous}() ($1)$2").replace(Rg("^Object.\x3canonymous\x3e\\s*\\(([^\\)]+)\\)","gm"),"{anonymous}() ($1)").replace(Rg("^([^\\(]+|\\{anonymous\\}\\(\\)) \\((.+)\\)$","gm"),"$1@$2").split("\n").slice(0,-1)} function Sg(a){var b=Rg("Line (\\d+).*script (?:in )?(\\S+)","i");a=a.message.split("\n");for(var c=[],d=2,e=a.length|0;dvoid 0===a);function qh(){}qh.prototype=new p;qh.prototype.constructor=qh;function rh(a,b,c){return b.qi.newArrayOfThisClass([c])}qh.prototype.$classData=q({C1:0},!1,"java.lang.reflect.Array$",{C1:1,g:1});var sh;function th(){sh||(sh=new qh);return sh}function uh(a,b){this.nM=a;this.oM=b}uh.prototype=new p; uh.prototype.constructor=uh;uh.prototype.$classData=q({ET:0},!1,"java.math.BigInteger$QuotAndRem",{ET:1,g:1});function xh(){}xh.prototype=new p;xh.prototype.constructor=xh;function yh(a,b){if(0===b.Ya)return 0;a=b.wb<<5;var c=b.Qa.a[-1+b.wb|0];0>b.Ya&&zh(b)===(-1+b.wb|0)&&(c=-1+c|0);return a=a-Math.clz32(c)|0}function Fh(a,b,c){a=c>>5;c&=31;var d=(b.wb+a|0)+(0===c?0:1)|0,e=new Xc(d);Gh(0,e,b.Qa,a,c);b=Hh(b.Ya,d,e);Ih(b);return b} function Gh(a,b,c,d,e){if(0===e)c.wa(0,b,d,b.a.length-d|0);else{a=32-e|0;b.a[-1+b.a.length|0]=0;for(var g=-1+b.a.length|0;g>d;){var h=g;b.a[h]=b.a[h]|c.a[-1+(g-d|0)|0]>>>a|0;b.a[-1+g|0]=c.a[-1+(g-d|0)|0]<>>31|0;e=1+e|0}0!==a&&(b.a[d]=a)} function Oh(a,b,c){a=c>>5;var d=31&c;if(a>=b.wb)return 0>b.Ya?Ph().MC:Ph().nq;c=b.wb-a|0;var e=new Xc(1+c|0);Qh(0,e,c,b.Qa,a,d);if(0>b.Ya){for(var g=0;g>>g|0|d.a[1+(a+e|0)|0]<>>g|0}}xh.prototype.$classData=q({FT:0},!1,"java.math.BitLevel$",{FT:1,g:1});var Rh;function Sh(){Rh||(Rh=new xh);return Rh} function Th(){this.tH=this.uH=null;Uh=this;this.uH=new Xc(new Int32Array([-1,-1,31,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5]));this.tH=new Xc(new Int32Array([-2147483648,1162261467,1073741824,1220703125,362797056,1977326743,1073741824,387420489,1E9,214358881,429981696,815730721,1475789056,170859375,268435456,410338673,612220032,893871739,128E7,1801088541,113379904,148035889,191102976,244140625,308915776,387420489,481890304,594823321,729E6,887503681,1073741824,1291467969, 1544804416,1838265625,60466176]))}Th.prototype=new p;Th.prototype.constructor=Th; function Vh(a,b){a=b.Ya;var c=b.wb,d=b.Qa;if(0===a)return"0";if(1===c)return b=(+(d.a[0]>>>0)).toString(10),0>a?"-"+b:b;b="";var e=new Xc(c);for(d.wa(0,e,0,c);;){var g=0;for(d=-1+c|0;0<=d;){var h=g;g=e.a[d];var k=Wh(xa(),g,h,1E9,0);e.a[d]=k;h=k>>31;var l=65535&k;k=k>>>16|0;var m=Math.imul(51712,l);l=Math.imul(15258,l);var n=Math.imul(51712,k);m=m+((l+n|0)<<16)|0;Math.imul(1E9,h);Math.imul(15258,k);g=g-m|0;d=-1+d|0}d=""+g;for(b="000000000".substring(d.length)+d+b;0!==c&&0===e.a[-1+c|0];)c=-1+c|0;if(0=== c)break}e=0;for(c=b.length;;)if(ea?"-"+b:b} function waa(a,b,c){if(0===b.W&&0===b.Y)switch(c){case 0:return"0";case 1:return"0.0";case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return(0>c?"0E+":"0E")+(-2147483648===c?"2147483648":""+(-c|0))}else{a=0>b.Y;var d="";var e=18;if(a){var g=b.W;b=b.Y;b=new ma(-g|0,0!==g?~b:-b|0)}g=b.W;for(var h=b.Y;;){b=g;var k=h;h=xa();g=$h(h,g,k,10,0);h=h.Qc;e=-1+e|0;k=h;var l=g,m=l>>>16|0;l=Math.imul(10,65535&l);m=Math.imul(10,m);m=l+(m<<16)| 0;Math.imul(10,k);d=""+(b-m|0)+d;b=h;if(0===g&&0===b)break}g=18-e|0;h=g>>31;k=c>>31;b=g-c|0;g=(-2147483648^b)>(-2147483648^g)?-1+(h-k|0)|0:h-k|0;b=-1+b|0;g=-1!==b?g:-1+g|0;if(0>>16|0;var x=65535&d,A=d>>>16|0,B=Math.imul(v,x);x=Math.imul(r,x);v=Math.imul(v,A);v=B+((x+v|0)<<16)|0;Math.imul(m,d);Math.imul(r,A);n=n-v|0;if(0!==g)for(g=1+g|0;;){r=g=-1+g|0;A=k.a[-2+h|0];m=65535&r; r=r>>>16|0;B=65535&A;A=A>>>16|0;v=Math.imul(m,B);B=Math.imul(r,B);x=Math.imul(m,A);m=v+((B+x|0)<<16)|0;v=(v>>>16|0)+x|0;v=(Math.imul(r,A)+(v>>>16|0)|0)+(((65535&v)+B|0)>>>16|0)|0;A=n;r=a.a[-2+e|0];B=n+d|0;if(0===((-2147483648^B)<(-2147483648^n)?1:0)&&(n=B,v^=-2147483648,A^=-2147483648,v===A?(-2147483648^m)>(-2147483648^r):v>A))continue;break}}if(n=0!==g){ei();n=a;m=e-h|0;A=k;r=h;v=g;var C=0;var D;for(B=D=0;B>>16|0;var M=65535&v,N=v>>>16|0,P=Math.imul(I,M); M=Math.imul(F,M);var T=Math.imul(I,N);I=P+((M+T|0)<<16)|0;P=(P>>>16|0)+T|0;N=(Math.imul(F,N)+(P>>>16|0)|0)+(((65535&P)+M|0)>>>16|0)|0;F=I+C|0;C=(-2147483648^F)<(-2147483648^I)?1+N|0:N;N=n.a[m+x|0];F=N-F|0;N=(-2147483648^F)>(-2147483648^N)?-1:0;I=D;D=I>>31;I=F+I|0;D=(-2147483648^I)<(-2147483648^F)?1+(N+D|0)|0:N+D|0;n.a[m+x|0]=I;B=1+B|0}v=n.a[m+r|0];A=v-C|0;v=(-2147483648^A)>(-2147483648^v)?-1:0;x=D;B=x>>31;x=A+x|0;n.a[m+r|0]=x;n=0!==((-2147483648^x)<(-2147483648^A)?1+(v+B|0)|0:v+B|0)}if(n)for(g=-1+ g|0,n=B=v=0;n>>16|0,m=65535&e,n=e>>>16|0,r=Math.imul(k,m);m=Math.imul(l,m);k=Math.imul(k,n);r=r+((m+k|0)<<16)|0;Math.imul(h,e);Math.imul(l,n);a=a-r|0;b.a[d]=g;d=-1+d|0}return a}ci.prototype.$classData=q({HT:0},!1,"java.math.Division$",{HT:1,g:1});var hi;function ei(){hi||(hi=new ci);return hi} function ii(a,b,c,d){var e=new Xc(1+b|0),g=1,h=a.a[0],k=h+c.a[0]|0;e.a[0]=k;h=(-2147483648^k)<(-2147483648^h)?1:0;if(b>=d){for(;g(-2147483648^k)?-1:0;var m=h;h=m>>31;m=l+m|0;l=(-2147483648^m)<(-2147483648^l)?1+(k+h|0)|0:k+h|0;e.a[g]=m;h=l;g=1+g|0}for(;g>31,l=c+l|0,c=(-2147483648^l)<(-2147483648^c)?1+d|0:d,e.a[g]=l,h=c,g=1+g|0;return e}function ki(){}ki.prototype=new p;ki.prototype.constructor=ki; function li(a,b,c){a=b.Ya;var d=c.Ya,e=b.wb,g=c.wb;if(0===a)return c;if(0===d)return b;if(2===(e+g|0)){b=b.Qa.a[0];c=c.Qa.a[0];if(a===d)return d=b+c|0,c=(-2147483648^d)<(-2147483648^b)?1:0,0===c?qi(a,d):Hh(a,2,new Xc(new Int32Array([d,c])));d=Ph();0>a?(a=b=c-b|0,c=(-2147483648^b)>(-2147483648^c)?-1:0):(a=c=b-c|0,c=(-2147483648^c)>(-2147483648^b)?-1:0);return ri(d,new ma(a,c))}if(a===d)d=e>=g?ii(b.Qa,e,c.Qa,g):ii(c.Qa,g,b.Qa,e);else{var h=e!==g?e>g?1:-1:si(0,b.Qa,c.Qa,e);if(0===h)return Ph().nq;1=== h?d=ji(b.Qa,e,c.Qa,g):(c=ji(c.Qa,g,b.Qa,e),a=d,d=c)}a=Hh(a|0,d.a.length,d);Ih(a);return a}function si(a,b,c,d){for(a=-1+d|0;0<=a&&b.a[a]===c.a[a];)a=-1+a|0;return 0>a?0:(-2147483648^b.a[a])<(-2147483648^c.a[a])?-1:1} function ti(a,b,c){var d=b.Ya;a=c.Ya;var e=b.wb,g=c.wb;if(0===a)return b;if(0===d)return ui(c);if(2===(e+g|0))return b=b.Qa.a[0],e=0,c=c.Qa.a[0],g=0,0>d&&(d=b,b=-d|0,e=0!==d?~e:-e|0),0>a&&(a=c,d=g,c=-a|0,g=0!==a?~d:-d|0),a=Ph(),d=b,b=e,e=g,c=d-c|0,ri(a,new ma(c,(-2147483648^c)>(-2147483648^d)?-1+(b-e|0)|0:b-e|0));var h=e!==g?e>g?1:-1:si(vi(),b.Qa,c.Qa,e);if(d===a&&0===h)return Ph().nq;-1===h?(c=d===a?ji(c.Qa,g,b.Qa,e):ii(c.Qa,g,b.Qa,e),a=-a|0):d===a?(c=ji(b.Qa,e,c.Qa,g),a=d):(c=ii(b.Qa,e,c.Qa,g), a=d);a=Hh(a|0,c.a.length,c);Ih(a);return a}ki.prototype.$classData=q({IT:0},!1,"java.math.Elementary$",{IT:1,g:1});var wi;function vi(){wi||(wi=new ki);return wi}function xi(a,b){this.ds=a;this.Fw=b}xi.prototype=new p;xi.prototype.constructor=xi;xi.prototype.i=function(a){return a instanceof xi?this.ds===a.ds?this.Fw===a.Fw:!1:!1};xi.prototype.B=function(){return this.ds<<3|this.Fw.NF};xi.prototype.u=function(){return"precision\x3d"+this.ds+" roundingMode\x3d"+this.Fw}; xi.prototype.$classData=q({JT:0},!1,"java.math.MathContext",{JT:1,g:1});function yi(){this.qM=null;zi=this;Ai();var a=Bi().OC;this.qM=new xi(34,a);Ai();Bi();Ai();Bi();Ai();Bi()}yi.prototype=new p;yi.prototype.constructor=yi;yi.prototype.$classData=q({KT:0},!1,"java.math.MathContext$",{KT:1,g:1});var zi;function Ai(){zi||(zi=new yi);return zi} function Fi(a,b,c,d){for(var e,g=e=0;g>>16|0;var m=65535&d,n=d>>>16|0,r=Math.imul(l,m);m=Math.imul(k,m);var v=Math.imul(l,n);l=r+((m+v|0)<<16)|0;r=(r>>>16|0)+v|0;k=(Math.imul(k,n)+(r>>>16|0)|0)+(((65535&r)+m|0)>>>16|0)|0;e=l+e|0;k=(-2147483648^e)<(-2147483648^l)?1+k|0:k;a.a[h]=e;e=k;g=1+g|0}return e}function Gi(a,b){for(var c=new Xc(a),d=c.a[0]=1;dc;){var d=c;if(18>=d){fi().es.a[d]=ri(Ph(),new ma(b,a));var e=fi().fs,g=Ph(),h=a,k=b;e.a[d]=ri(g,new ma(0===(32&d)?k<>>1|0)>>>(31-d|0)|0|h<>>16|0;d=Math.imul(5,65535&d);e=Math.imul(5,b);b=d+(e<<16)|0;d=(d>>>16|0)+e|0;a=Math.imul(5,a)+(d>>>16|0)|0}else fi().es.a[d]=Ki(fi().es.a[-1+d|0],fi().es.a[1]),fi().fs.a[d]=Ki(fi().fs.a[-1+ d|0],Ph().mq);c=1+c|0}}Hi.prototype=new p;Hi.prototype.constructor=Hi; function Li(a,b,c){for(var d,e=0;e>>16|0;var v=65535&l;l=l>>>16|0;k=Math.imul(r,v);v=Math.imul(d,v);var x=Math.imul(r,l);r=k+((v+x|0)<<16)|0;k=(k>>>16|0)+x|0;d=(Math.imul(d,l)+(k>>>16|0)|0)+(((65535&k)+v|0)>>>16|0)|0;m=r+m|0;d=(-2147483648^m)<(-2147483648^r)?1+d|0:d;n=m+n|0;m=(-2147483648^n)<(-2147483648^m)?1+d|0:d;c.a[g+h|0]=n;d=m;h=1+h|0}c.a[g+b|0]=d;e=1+e|0}Nh(Sh(),c,c,b<<1);for(g=e=d=0;e>>16|0,k=65535&r,d=r>>>16|0,r=Math.imul(l,k),k=Math.imul(m,k),v=Math.imul(l,d),l=r+((k+v|0)<<16)|0,r=(r>>>16|0)+v|0,m=(Math.imul(m,d)+(r>>>16|0)|0)+(((65535&r)+k|0)>>>16|0)|0,n=l+n|0,m=(-2147483648^n)<(-2147483648^l)?1+m|0:m,h=n+h|0,n=(-2147483648^h)<(-2147483648^n)?1+m|0:m,c.a[g]=h,g=1+g|0,h=n+c.a[g]|0,n=(-2147483648^h)<(-2147483648^n)?1:0,c.a[g]=h,d=n,e=1+e|0,g=1+g|0;return c} function Mi(a,b,c){if(c.wb>b.wb)var d=c;else d=b,b=c;var e=d,g=b;if(63>g.wb){d=e.wb;b=g.wb;c=d+b|0;a=e.Ya!==g.Ya?-1:1;if(2===c){d=e.Qa.a[0];b=g.Qa.a[0];c=65535&d;d=d>>>16|0;g=65535&b;b=b>>>16|0;e=Math.imul(c,g);g=Math.imul(d,g);var h=Math.imul(c,b);c=e+((g+h|0)<<16)|0;e=(e>>>16|0)+h|0;d=(Math.imul(d,b)+(e>>>16|0)|0)+(((65535&e)+g|0)>>>16|0)|0;a=0===d?qi(a,c):Hh(a,2,new Xc(new Int32Array([c,d])))}else{e=e.Qa;g=g.Qa;h=new Xc(c);if(0!==d&&0!==b)if(1===d)h.a[b]=Fi(h,g,b,e.a[0]);else if(1===b)h.a[d]=Fi(h, e,d,g.a[0]);else if(e===g&&d===b)Li(e,d,h);else for(var k=0;k>>16|0,C=65535&v;v=v>>>16|0;var D=Math.imul(A,C);C=Math.imul(B,C);var F=Math.imul(A,v);A=D+((C+F|0)<<16)|0;D=(D>>>16|0)+F|0;B=(Math.imul(B,v)+(D>>>16|0)|0)+(((65535&D)+C|0)>>>16|0)|0;x=A+x|0;B=(-2147483648^x)<(-2147483648^A)?1+B|0:B;m=x+m|0;x=(-2147483648^m)<(-2147483648^x)?1+B|0:B;h.a[l+r|0]=m;m=x;r=1+r|0}h.a[l+b|0]=m;k=1+k|0}a=Hh(a,c,h);Ih(a)}return a}d= (-2&e.wb)<<4;c=Ni(e,d);h=Ni(g,d);b=Oi(c,d);k=ti(vi(),e,b);b=Oi(h,d);g=ti(vi(),g,b);e=Mi(a,c,h);b=Mi(a,k,g);a=Mi(a,ti(vi(),c,k),ti(vi(),g,h));c=e;a=li(vi(),a,c);a=li(vi(),a,b);a=Oi(a,d);d=e=Oi(e,d<<1);a=li(vi(),d,a);return li(vi(),a,b)} function Pi(a,b){var c=a.fs.a.length,d=c>>31,e=b.Y;if(e===d?(-2147483648^b.W)<(-2147483648^c):e=(-2147483648^b.W):0>c)return Qi(Ph().mq,b.W);c=b.Y;if(0===c?-1>=(-2147483648^b.W):0>c)return Oi(Qi(a.es.a[1],b.W),b.W);var g=Qi(a.es.a[1],2147483647);c=g;e=b.Y;var h=-2147483647+b.W|0;d=h;h=1>(-2147483648^h)?e:-1+e|0;for(e=Ri(xa(),b.W,b.Y,2147483647,0);;){var k=d,l=h;if(0===l?-1<(-2147483648^k):0(-2147483648^d)?h:-1+h|0; else break}c=Ki(c,Qi(a.es.a[1],e));c=Oi(c,2147483647);a=b.Y;d=b=-2147483647+b.W|0;for(h=1>(-2147483648^b)?a:-1+a|0;;)if(b=d,a=h,0===a?-1<(-2147483648^b):0(-2147483648^a)?b:-1+b|0,d=a,h=b;else break;return Oi(c,e)}Hi.prototype.$classData=q({LT:0},!1,"java.math.Multiplication$",{LT:1,g:1});var Ii;function fi(){Ii||(Ii=new Hi);return Ii}function Si(){}Si.prototype=new p;Si.prototype.constructor=Si; function Ti(a,b){var c=Ui(),d=Ui(),e=b.a.length;16=e||0>=g.Da(h.Wj(b,m),h.Wj(b,n)))?(h.Qo(c,a,h.Wj(b,m)),m=1+m|0):(h.Qo(c,a,h.Wj(b,n)),n=1+n|0),a=1+a|0;c.wa(d,b,d,k)}else Zi(b,d,e,g,h)} function Zi(a,b,c,d,e){c=c-b|0;if(2<=c){var g=e.Wj(a,b),h=e.Wj(a,1+b|0);0d.Da(h,e.Wj(a,-1+(b+g|0)|0))){for(var k=b,l=-1+(b+g|0)|0;1<(l-k|0);){var m=(k+l|0)>>>1|0;0>d.Da(h,e.Wj(a,m))?l=m:k=m}k=k+(0>d.Da(h,e.Wj(a,k))?0:1)|0;for(l=b+g|0;l>k;)e.Qo(a,l,e.Wj(a,-1+l|0)),l=-1+l|0;e.Qo(a,k,h)}g=1+g|0}}} function kj(a,b,c){a=0;for(var d=b.a.length;;){if(a===d)return-1-a|0;var e=(a+d|0)>>>1|0,g=b.a[e];g=c===g?0:cg)d=e;else{if(0===g)return e;a=1+e|0}}}function pj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){var e=b.a[d],g=c.a[d],h=g.Y;if(e.W!==g.W||e.Y!==h)return!1;d=1+d|0}return!0} function qj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(b.a[d]!==c.a[d])return!1;d=1+d|0}return!0}function sj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(b.a[d]!==c.a[d])return!1;d=1+d|0}return!0} function tj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(b.a[d]!==c.a[d])return!1;d=1+d|0}return!0}function uj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(b.a[d]!==c.a[d])return!1;d=1+d|0}return!0} function vj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(b.a[d]!==c.a[d])return!1;d=1+d|0}return!0}function wj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(!Object.is(b.a[d],c.a[d]))return!1;d=1+d|0}return!0} function xj(a,b,c){if(b===c)return!0;if(null===b||null===c)return!1;a=b.a.length;if(c.a.length!==a)return!1;for(var d=0;d!==a;){if(!Object.is(b.a[d],c.a[d]))return!1;d=1+d|0}return!0}function yj(a,b,c){a=b.a.length;for(var d=0;d!==a;)b.a[d]=c,d=1+d|0}function zj(a,b,c){if(0>c)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cc)throw new Aj;a=b.a.length;a=cd)throw Kj(c+" \x3e "+d);a=d-c|0;d=b.a.length-c|0;d=a=b)return"00000000000000000000".substring(0,b);for(a="";20b)return new Vj(a.Ft,"0",0);if(b>=d)return a;if(53>c.charCodeAt(b))return 0===b?new Vj(a.Ft,"0",0):new Vj(a.Ft,c.substring(0,b),a.vr-(d-b|0)|0);for(b=-1+b|0;;)if(0<=b&&57===c.charCodeAt(b))b=-1+b|0;else break;c=0>b?"1":""+c.substring(0,b)+hc(65535&(1+c.charCodeAt(b)|0));return new Vj(a.Ft,c,a.vr-(d-(1+b|0)|0)|0)} function Vj(a,b,c){this.Ft=a;this.wr=b;this.vr=c}Vj.prototype=new p;Vj.prototype.constructor=Vj;function Wj(a,b){Tj();if(!(0b)switch(b){case 94:case 36:case 92:case 46:case 42:case 43:case 63:case 40:case 41:case 91:case 93:case 123:case 125:case 124:return"\\"+c;default:return 2!==(66&a.Ue)?c:65<=b&&90>=b?"["+c+ik(jk(),32+b|0)+"]":97<=b&&122>=b?"["+ik(jk(),-32+b|0)+c+"]":c}else return 56320===(-1024&b)?"(?:"+c+")":c} function kk(a){for(var b=a.ri,c=b.length;;){if(a.P!==c)switch(b.charCodeAt(a.P)){case 32:case 9:case 10:case 11:case 12:case 13:a.P=1+a.P|0;continue;case 35:lk(a);continue}break}} function xaa(a,b,c){var d=a.ri,e=d.length,g=a.P,h=g===e?46:d.charCodeAt(g);if(63===h||42===h||43===h||123===h){g=a.ri;var k=a.P;a.P=1+a.P|0;if(123===h){h=g.length;if(a.P===h)var l=!0;else l=g.charCodeAt(a.P),l=!(48<=l&&57>=l);for(l&&dk(a,"Illegal repetition");;)if(a.P!==h?(l=g.charCodeAt(a.P),l=48<=l&&57>=l):l=!1,l)a.P=1+a.P|0;else break;a.P===h&&dk(a,"Illegal repetition");if(44===g.charCodeAt(a.P))for(a.P=1+a.P|0;;)if(a.P!==h?(l=g.charCodeAt(a.P),l=48<=l&&57>=l):l=!1,l)a.P=1+a.P|0;else break;a.P!== h&&125===g.charCodeAt(a.P)||dk(a,"Illegal repetition");a.P=1+a.P|0}g=g.substring(k,a.P);if(a.P!==e)switch(d.charCodeAt(a.P)){case 43:return a.P=1+a.P|0,yaa(a,b,c,g);case 63:return a.P=1+a.P|0,""+c+g+"?";default:return""+c+g}else return""+c+g}else return c} function yaa(a,b,c,d){for(var e=a.nn.length|0,g=0;gb&&(a.nn[h]=1+k|0);g=1+g|0}c=c.replace(jk().dR,(l,m,n)=>{0!==(m.length%2|0)&&(n=parseInt(n,10)|0,l=n>b?""+m+(1+n|0):l);return l});a.mn=1+a.mn|0;return"(?:(?\x3d("+c+d+"))\\"+(1+b|0)+")"} function zaa(a){var b=a.ri,c=b.length;(1+a.P|0)===c&&dk(a,"\\ at end of pattern");a.P=1+a.P|0;var d=b.charCodeAt(a.P);switch(d){case 100:case 68:case 104:case 72:case 115:case 83:case 118:case 86:case 119:case 87:case 112:case 80:switch(a=mk(a,d),b=a.nK,b){case 0:return"\\p{"+a.yr+"}";case 1:return"\\P{"+a.yr+"}";case 2:return"["+a.yr+"]";case 3:return nk(jk(),a.yr);default:throw new Yj(b);}case 98:if("b{g}"===b.substring(a.P,4+a.P|0))dk(a,"\\b{g} is not supported");else if(0!==(320&a.Ue))ok(a,"\\b with UNICODE_CASE"); else return a.P=1+a.P|0,"\\b";break;case 66:if(0!==(320&a.Ue))ok(a,"\\B with UNICODE_CASE");else return a.P=1+a.P|0,"\\B";break;case 65:return a.P=1+a.P|0,"(?:^)";case 71:dk(a,"\\G in the middle of a pattern is not supported");break;case 90:return a.P=1+a.P|0,"(?\x3d"+(0!==(1&a.Ue)?"\n":"(?:\r\n?|[\n\u0085\u2028\u2029])")+"?$)";case 122:return a.P=1+a.P|0,"(?:$)";case 82:return a.P=1+a.P|0,"(?:\r\n|[\n-\r\u0085\u2028\u2029])";case 88:dk(a,"\\X is not supported");break;case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:var e= a.P;for(d=1+e|0;;){if(d!==c){var g=b.charCodeAt(d);g=48<=g&&57>=g}else g=!1;g?(g=b.substring(e,1+d|0),g=(parseInt(g,10)|0)<=(-1+(a.nn.length|0)|0)):g=!1;if(g)d=1+d|0;else break}b=b.substring(e,d);b=parseInt(b,10)|0;b>(-1+(a.nn.length|0)|0)&&dk(a,"numbered capturing group \x3c"+b+"\x3e does not exist");b=a.nn[b]|0;a.P=d;return"(?:\\"+b+")";case 107:return a.P=1+a.P|0,a.P!==c&&60===b.charCodeAt(a.P)||dk(a,"\\k is not followed by '\x3c' for named capturing group"),a.P=1+a.P|0,b=pk(a),d=a.$F,ug().ny.call(d, b)||dk(a,"named capturing group \x3c"+b+"\x3e does not exit"),b=a.nn[d[b]|0]|0,a.P=1+a.P|0,"(?:\\"+b+")";case 81:d=1+a.P|0;c=b.indexOf("\\E",d)|0;if(0>c)return a.P=b.length,fk(a,b.substring(d));a.P=2+c|0;return fk(a,b.substring(d,c));default:return hk(a,qk(a))}} function qk(a){var b=a.ri,c=gk(b,a.P);switch(c){case 48:return Aaa(a);case 120:return b=a.ri,c=1+a.P|0,c!==b.length&&123===b.charCodeAt(c)?(c=1+c|0,b=b.indexOf("}",c)|0,0>b&&dk(a,"Unclosed hexadecimal escape sequence"),c=rk(a,c,b,"hexadecimal"),a.P=1+b|0,a=c):(b=rk(a,c,2+c|0,"hexadecimal"),a.P=2+c|0,a=b),a;case 117:a:{b=a.ri;var d=1+a.P|0;c=4+d|0;d=rk(a,d,c,"Unicode");a.P=c;var e=2+c|0,g=4+e|0;if(55296===(-1024&d)&&"\\u"===b.substring(c,e)&&(b=rk(a,e,g,"Unicode"),56320===(-1024&b))){a.P=g;a=(64+(1023& d)|0)<<10|1023&b;break a}a=d}return a;case 78:dk(a,"\\N is not supported");break;case 97:return a.P=1+a.P|0,7;case 116:return a.P=1+a.P|0,9;case 110:return a.P=1+a.P|0,10;case 102:return a.P=1+a.P|0,12;case 114:return a.P=1+a.P|0,13;case 101:return a.P=1+a.P|0,27;case 99:return a.P=1+a.P|0,a.P===b.length&&dk(a,"Illegal control escape sequence"),b=gk(b,a.P),a.P=a.P+(65536<=b?2:1)|0,64^b;default:return(65<=c&&90>=c||97<=c&&122>=c)&&dk(a,"Illegal/unsupported escape sequence"),a.P=a.P+(65536<=c?2:1)| 0,c}}function Aaa(a){var b=a.ri,c=b.length,d=a.P,e=(1+d|0)e||7g||7b||7g)&&dk(a,"Illegal "+d+" escape sequence");for(g=b;g=h||65<=h&&70>=h||97<=h&&102>=h||dk(a,"Illegal "+d+" escape sequence");g=1+g|0}6<(c-b|0)?b=1114112:(b=e.substring(b,c),b=parseInt(b,16)|0);1114111e&&dk(a,"Unclosed character family");a.P=e;c=c.substring(d,e)}else c=c.substring(d,1+d|0);d=jk().qK;ug().ny.call(d,c)||ok(a,"Unicode character family");c=2!==(66&a.Ue)||"Lower"!== c&&"Upper"!==c?c:"Alpha";c=jk().qK[c];a.P=1+a.P|0;a=c;break;default:throw new Yj(hc(b));}97<=b?b=a:a.mK?b=a.oK:(b=a,b.mK||(b.oK=new sk(1^b.nK,b.yr),b.mK=!0),b=b.oK);return b} var Caa=function Baa(a){var c=a.ri,d=c.length;a.P=1+a.P|0;var e=a.P!==d?94===c.charCodeAt(a.P):!1;e&&(a.P=1+a.P|0);for(e=new tk(2===(66&a.Ue),e);a.P!==d;){var g=gk(c,a.P);a:{switch(g){case 93:return a.P=1+a.P|0,a=e,c=uk(a),""===a.ZF?c:"(?:"+a.ZF+c+")";case 38:a.P=1+a.P|0;if(a.P!==d&&38===c.charCodeAt(a.P)){a.P=1+a.P|0;g=e;var h=uk(g);g.ZF+=g.XQ?h+"|":"(?\x3d"+h+")";g.bm="";g.pg=""}else vk(a,38,d,c,e);break a;case 91:g=Baa(a);e.bm=""===e.bm?g:e.bm+"|"+g;break a;case 92:a.P=1+a.P|0;a.P===d&&dk(a,"Illegal escape sequence"); h=c.charCodeAt(a.P);switch(h){case 100:case 68:case 104:case 72:case 115:case 83:case 118:case 86:case 119:case 87:case 112:case 80:g=e;h=mk(a,h);var k=h.nK;switch(k){case 0:g.pg=g.pg+("\\p{"+h.yr)+"}";break;case 1:g.pg=g.pg+("\\P{"+h.yr)+"}";break;case 2:g.pg=""+g.pg+h.yr;break;case 3:h=nk(jk(),h.yr);g.bm=""===g.bm?h:g.bm+"|"+h;break;default:throw new Yj(k);}break;case 81:a.P=1+a.P|0;g=c.indexOf("\\E",a.P)|0;0>g&&dk(a,"Unclosed character class");h=e;k=c;for(var l=g,m=a.P;m!==l;){var n=gk(k,m);wk(h, n);m=m+(65536<=n?2:1)|0}a.P=2+g|0;break;default:vk(a,qk(a),d,c,e)}break a;case 32:case 9:case 10:case 11:case 12:case 13:if(0!==(4&a.Ue))a.P=1+a.P|0;else break;break a;case 35:if(0!==(4&a.Ue)){lk(a);break a}}a.P=a.P+(65536<=g?2:1)|0;vk(a,g,d,c,e)}}dk(a,"Unclosed character class")}; function Daa(a){var b=a.ri,c=b.length,d=a.P;if((1+d|0)===c||63!==b.charCodeAt(1+d|0))return a.P=1+d|0,a.mn=1+a.mn|0,a.nn.push(a.mn),"("+xk(a,!0)+")";(2+d|0)===c&&dk(a,"Unclosed group");var e=b.charCodeAt(2+d|0);if(58===e||61===e||33===e)return a.P=3+d|0,""+b.substring(d,3+d|0)+xk(a,!0)+")";if(60===e){(3+d|0)===c&&dk(a,"Unclosed group");b=b.charCodeAt(3+d|0);if(65<=b&&90>=b||97<=b&&122>=b)return a.P=3+d|0,d=pk(a),b=a.$F,ug().ny.call(b,d)&&dk(a,"named capturing group \x3c"+d+"\x3e is already defined"), a.mn=1+a.mn|0,a.nn.push(a.mn),a.$F[d]=-1+(a.nn.length|0)|0,a.P=1+a.P|0,"("+xk(a,!0)+")";61!==b&&33!==b&&dk(a,"Unknown look-behind group");ok(a,"Look-behind group")}else{if(62===e)return a.P=3+d|0,a.mn=1+a.mn|0,d=a.mn,"(?:(?\x3d("+xk(a,!0)+"))\\"+d+")";dk(a,"Embedded flag expression in the middle of a pattern is not supported")}} function pk(a){for(var b=a.ri,c=b.length,d=a.P;;){if(a.P!==c){var e=b.charCodeAt(a.P);e=65<=e&&90>=e||97<=e&&122>=e||48<=e&&57>=e}else e=!1;if(e)a.P=1+a.P|0;else break}a.P!==c&&62===b.charCodeAt(a.P)||dk(a,"named capturing group is missing trailing '\x3e'");return b.substring(d,a.P)} function vk(a,b,c,d,e){0!==(4&a.Ue)&&kk(a);a.P!==c&&45===d.charCodeAt(a.P)?(a.P=1+a.P|0,0!==(4&a.Ue)&&kk(a),a.P===c&&dk(a,"Unclosed character class"),c=gk(d,a.P),91===c||93===c?(wk(e,b),wk(e,45)):(a.P=a.P+(65536<=c?2:1)|0,c=92===c?qk(a):c,cc?c:90,a<=d&&(d=32+d|0,e.pg+=yk(32+a|0)+"-"+yk(d)),b=97c?c:122,b<=c&&(c=-32+c|0,e.pg+=yk(-32+b|0)+"-"+yk(c))))):wk(e,b)} function zk(a,b){this.ri=a;this.Ue=b;this.rK=!1;this.mn=this.P=0;this.nn=[0];this.$F={}}zk.prototype=new p;zk.prototype.constructor=zk;function ok(a,b){dk(a,b+" is not supported because it requires RegExp features of ECMAScript 2018.\nIf you only target environments with ES2018+, you can enable ES2018 features with\n scalaJSLinkerConfig ~\x3d { _.withESFeatures(_.withESVersion(ESVersion.ES2018)) }\nor an equivalent configuration depending on your build tool.")} function xk(a,b){for(var c=a.ri,d=c.length,e="";a.P!==d;){var g=gk(c,a.P);a:{switch(g){case 41:return b||dk(a,"Unmatched closing ')'"),a.P=1+a.P|0,e;case 124:a.rK&&!b&&dk(a,"\\G is not supported when there is an alternative at the top level");a.P=1+a.P|0;e+="|";break a;case 32:case 9:case 10:case 11:case 12:case 13:if(0!==(4&a.Ue))a.P=1+a.P|0;else break;break a;case 35:if(0!==(4&a.Ue))lk(a);else break;break a;case 63:case 42:case 43:case 123:dk(a,"Dangling meta character '"+ik(jk(),g)+"'")}var h= a.mn;switch(g){case 92:g=zaa(a);break;case 91:g=Caa(a);break;case 40:g=Daa(a);break;case 94:a.P=1+a.P|0;g="(?:^)";break;case 36:a.P=1+a.P|0;g="(?:$)";break;case 46:a.P=1+a.P|0;g=0!==(32&a.Ue)?"":0!==(1&a.Ue)?"\n":"\n\r\u0085\u2028\u2029";g=nk(jk(),g);break;default:a.P=a.P+(65536<=g?2:1)|0,g=hk(a,g)}e=""+e+xaa(a,h,g)}}b&&dk(a,"Unclosed group");return e} function lk(a){for(var b=a.ri,c=b.length;;){if(a.P!==c){var d=b.charCodeAt(a.P);d=!(10===d||13===d||133===d||8232===d||8233===d)}else d=!1;if(d)a.P=1+a.P|0;else break}}zk.prototype.$classData=q({G2:0},!1,"java.util.regex.PatternCompiler",{G2:1,g:1});function Ak(a){try{return RegExp("",a),!0}catch(b){return!1}} function Bk(){this.dR=this.cR=null;this.pK=!1;this.qK=this.$Q=this.bR=this.ZQ=this.aR=this.YQ=null;Ck=this;this.cR=RegExp("^\\(\\?([idmsuxU]*)(?:-([idmsuxU]*))?\\)");this.dR=RegExp("(\\\\+)(\\d+)","g");this.pK=Ak("us");Ak("d");this.YQ=new sk(2,"0-9");this.aR=new sk(2,"\t \u00a0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000");this.ZQ=new sk(2,"\t-\r ");this.bR=new sk(2,"\n-\r\u0085\u2028\u2029");this.$Q=new sk(2,"a-zA-Z_0-9");var a={};a.Lower=new sk(2,"a-z");a.Upper=new sk(2,"A-Z");a.ASCII=new sk(2,"\x00-\u007f"); a.Alpha=new sk(2,"A-Za-z");a.Digit=new sk(2,"0-9");a.Alnum=new sk(2,"0-9A-Za-z");a.Punct=new sk(2,"!-/:-@[-`{-~");a.Graph=new sk(2,"!-~");a.Print=new sk(2," -~");a.Blank=new sk(2,"\t ");a.Cntrl=new sk(2,"\x00-\u001f\u007f");a.XDigit=new sk(2,"0-9A-Fa-f");a.Space=new sk(2,"\t-\r ");this.qK=a}Bk.prototype=new p;Bk.prototype.constructor=Bk; function Dk(a){jk();a=new zk(a,0);0!==(256&a.Ue)&&(a.Ue|=64);var b=0!==(16&a.Ue);if(!b){var c=jk().cR.exec(a.ri);if(null!==c){var d=c[1];if(void 0!==d)for(var e=d.length,g=0;g=b?a.pg=""+a.pg+ik(jk(),32+b|0):97<=b&&122>=b&&(a.pg=""+a.pg+ik(jk(),-32+b|0)))}tk.prototype.$classData=q({I2:0},!1,"java.util.regex.PatternCompiler$CharacterClassBuilder",{I2:1,g:1});function sk(a,b){this.oK=null;this.mK=!1;this.nK=a;this.yr=b}sk.prototype=new p;sk.prototype.constructor=sk; sk.prototype.$classData=q({J2:0},!1,"java.util.regex.PatternCompiler$CompiledCharClass",{J2:1,g:1});function Hk(a){if(0===(1&a.ci)<<24>>24&&0===(1&a.ci)<<24>>24){if(Ik()===a)var b="(",c=")";else if(Jk()===a)b="{",c="}";else if(Kk()===a)b="[",c="]";else if(Lk()===a)b="\u2039",c="\u203a";else if(Mk()===a)b="\u2192",c="\u2190";else if(Nk()===a)b='code"',c='"';else if(Ok()===a)b='code"""',c='"""';else{if(Pk()!==a)throw new w(a);b="${";c="}"}a.Wo=G(new H,b,c);a.ci=(1|a.ci)<<24>>24}return a.Wo} function Qk(){this.Vo=this.Uo=this.Wo=null;this.ci=0}Qk.prototype=new p;Qk.prototype.constructor=Qk;function Rk(){}Rk.prototype=Qk.prototype;function Sk(a){0===(2&a.ci)<<24>>24&&0===(2&a.ci)<<24>>24&&(a.Uo=Hk(a).h(),a.ci=(2|a.ci)<<24>>24);return a.Uo}function Tk(a){0===(4&a.ci)<<24>>24&&0===(4&a.ci)<<24>>24&&(a.Vo=Hk(a).j(),a.ci=(4|a.ci)<<24>>24);return a.Vo} Qk.prototype.Ua=function(){if(Ik()===this)return"parenthesis";if(Jk()===this)return"curly brace";if(Kk()===this)return"square bracket";if(Lk()===this)return"angle bracket";if(Mk()===this)return"indentation";if(Nk()===this)return"quasiquote";if(Ok()===this)return"quasiquote triple";if(Pk()===this)return"unquote";throw new w(this);};function Uk(){}Uk.prototype=new p;Uk.prototype.constructor=Uk;function Vk(a,b){return Wk(new Xk).Ob(hc(b),new y(()=>{t();return R()}))} Uk.prototype.$classData=q({$T:0},!1,"mlscript.BracketKind$",{$T:1,g:1});var Yk;function Zk(){Yk||(Yk=new Uk);return Yk}var Eaa=function bl(a){var c=a.vK();if(c instanceof cl&&Pe(new E(c.Yo),a.Yo))c=bl(c);else{c=a.vK();var d=O().c;c=new z(c,d)}d=a.DK();d instanceof cl&&Pe(new E(d.Yo),a.Yo)?a=bl(d):(a=a.DK(),d=O().c,a=new z(a,d));return dl(a,c)}; function Faa(a){var b=k=>a.Yo?Pe(new E(k),el()):Pe(new E(k),gl()),c=Eaa(a);a:for(;;)if(c.b()){b=u();break}else{var d=c.e(),e=c.f();if(!0===!!b(d))c=e;else for(;;){if(e.b())b=c;else{d=e.e();if(!0!==!!b(d)){e=e.f();continue}d=e;e=new z(c.e(),u());var g=c.f();for(c=e;g!==d;){var h=new z(g.e(),u());c=c.p=h;g=g.f()}for(g=d=d.f();!d.b();){h=d.e();if(!0===!!b(h)){for(;g!==d;)h=new z(g.e(),u()),c=c.p=h,g=g.f();g=d.f()}d=d.f()}g.b()||(c.p=g);b=e}break a}}return hl(b)}function il(){}il.prototype=new p; il.prototype.constructor=il;function jl(){}jl.prototype=il.prototype;function Ae(a,b,c){a.TM=b;a.$C=c;Sf||(Sf=new Rf);c=kl();for(var d=0,e=1,g=null;db){c=d;break a}d=1+d|0}c=-1}c=-1===c?a.ZC.a.length:1>c?1:c;d=a.$C.K();d=-1+(cl=>{if(null!==l){var m=l.h(),n=l.j();if(null!==m&&(m=m.x,null!==n&&(n=n.ya,n instanceof vl))){l=wl(k,n.x);m=zl(Al(),m);if(l===m)return l=t().d,G(new H,m,l);t();return G(new H,m,new L(new xl(l)))}}if(null!==l&&(n=l.h(),m=l.j(),null!==n&&(n=n.x,null!==m)))return l=m.ya,m=zl(Al(),n),t(),l=ul(a,l,k),G(new H,m,new L(l));throw new w(l);})(c);if(g===u())c=u();else{b=g.e();e=b=new z(c(b), u());for(g=g.f();g!==u();){var h=g.e();h=new z(c(h),u());e=e.p=h;g=g.f()}c=b}return new Bl(c)}if(e instanceof Cl)b=e.ai;else{if(e instanceof Dl)return new El;if(e instanceof Fl)b=e.gg;else{if(e instanceof Gl){g=e.Ra;c=(k=>l=>{if(null!==l){var m=l.j();if(null!==m)return ul(a,m.ya,k)}throw new w(l);})(c);if(g===u())c=u();else{b=g.e();e=b=new z(c(b),u());for(g=g.f();g!==u();)h=g.e(),h=new z(c(h),u()),e=e.p=h,g=g.f();c=b}return new Hl(c)}if(e instanceof Il)b=e.nl;else if(e instanceof Kl)b=e.cp;else if(e instanceof Ml)b=e.Ml;else{if(e instanceof Ol||e instanceof Pl||e instanceof Ql||e instanceof Rl||e instanceof Sl||e instanceof Tl||e instanceof Ul||e instanceof Vl||e instanceof Wl||e instanceof Xl||e instanceof Yl||e instanceof Zl||e instanceof $l||e instanceof am||e instanceof bm||e instanceof cm||e instanceof dm||e instanceof em||e instanceof fm)throw new gm("term "+b+" is not a valid pattern");throw new w(e);}}}}}; function im(a,b,c){if(b instanceof Gl){var d=b.Ra;b=k=>{if(null!==k){var l=new L(k);if(!l.b()){var m=l.k.h();l=l.k.j();if(t().d===m&&null!==l)return hm(a,l.ya,c)}}if(null!==k&&(l=new L(k),!l.b()&&(m=l.k.h(),l=l.k.j(),m instanceof L&&(m=m.k,null!==l))))return hm(a,m,c);throw new w(k);};if(d===u())return u();var e=d.e(),g=e=new z(b(e),u());for(d=d.f();d!==u();){var h=d.e();h=new z(b(h),u());g=g.p=h;d=d.f()}return e}throw new gm("term "+b+" is not a valid parameter list");} function jm(a,b,c){var d=a.CK();d.b()?c=new km(a.Ua()):(d=d.o(),lm(c,d).sF=!0,c=new km(d),d=a.Ua(),c=om(Al(),c,d));return b&&!a.UJ()?om(Al(),c,"class"):c}function pm(a,b){a=new vl(a);var c=h=>{var k=t().d;h=new sm(tm().Cg,h);return G(new H,k,h)};if(b===u())c=u();else{var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}c=d}return new Pl(a,new Gl(c))} var Iaa=function Haa(a,b,c,d,e){if(b instanceof um){var h=b.Tn,k=b.Un;b=vm(a,b.Xo,c,d,e);h=vm(a,h,c,d,e);a=Haa(a,k,c,d,e);if(a instanceof fe)return a=a.aa,t(),c=O().c,b=pm("Case",new z(b,new z(h,new z(a,c)))),new fe(b);if(a instanceof Ud){a=a.fa;if(b instanceof wm)return t(),b=new um(b,h,a),new Ud(b);xm("Program reached and unexpected state.")}else throw new w(a);}else{if(b instanceof ym){b=b.dn;if(d)return t(),b=vm(a,b,c,d,e),h=O().c,b=pm("Wildcard",new z(b,h)),new fe(b);t();b=new ym(vm(a,b,c,d, e));return new Ud(b)}if(zm()===b){if(d)return t(),b=pm("NoCases",O().c),new fe(b);t();b=zm();return new Ud(b)}throw new w(b);}};function Am(a){switch(a){case "+.":return"+";case "-.":return"-";case "*.":return"*";default:return a}} var vm=function Bm(a,b,c,d,e){for(;;){var h=!1,k=null,l=b;if(l instanceof vl){var m=l.x,n=e.xH.L(m);if(d||n){var r=Cm(c,m);if(r.b())throw new gm("unbound free variable "+m+" is not supported yet.");var v=r.o().zo();if(n){var x=new vl(v),A=O().c;return pm("Var",new z(x,A))}var B=new Dm(v),C=O().c;return pm("Var",new z(B,C))}return b}if(l instanceof Em){var D=l;if(d){var F=O().c;return pm("IntLit",new z(D,F))}return D}if(l instanceof Fm){var I=l;if(d){var M=O().c;return pm("DecLit",new z(I,M))}return I}if(l instanceof Dm){var N=l;if(d){var P=O().c;return pm("StrLit",new z(N,P))}return N}if(l instanceof Gm){var T=l;if(d){var Y=O().c;return pm("UnitLit",new z(T,Y))}return T}if(l instanceof Ol){var Z=l,S=Z.Ej,ea=Z.Fj;if(d){var ia=Hm(c);if(S instanceof Gl){var X=S.Ra,sa=(Jh=>If=>{if(null!==If){var Hg=new L(If);if(!Hg.b()){var He=Hg.k.h();Hg=Hg.k.j();if(t().d===He&&null!==Hg&&(He=Hg.ya,He instanceof vl))return If=He.x,wl(Jh,If),He=Im(Jh,If,(t(),new L(!1)),!1,t().d).re,G(new H,If,He)}}if(null!==If&&(He=new L(If),!He.b()&& (He=He.k.h(),He instanceof L&&(He=He.k,null!==He))))return If=He.x,wl(Jh,If),He=Im(Jh,If,(t(),new L(!1)),!1,t().d).re,G(new H,If,He);throw new gm("parameter "+If+" is not supported in quasiquote");})(ia);if(X===u())var Ja=u();else{for(var Xa=X.e(),Fa=new z(sa(Xa),u()),za=Fa,Qa=X.f();Qa!==u();){var Ma=Qa.e(),Ga=new z(sa(Ma),u());za=za.p=Ga;Qa=Qa.f()}Ja=Fa}var ab=d,Hb=e.xH;if(Ja===u())var bc=u();else{for(var yb=Ja.e(),tb=new z(yb.h(),u()),eb=tb,kb=Ja.f();kb!==u();){var Rb=kb.e(),Gb=new z(Rb.h(),u()); eb=eb.p=Gb;kb=kb.f()}bc=tb}for(var vb=Bm(a,ea,ia,ab,new Jm(a,Hb.Ce(bc))),Tb=Km(Ja);!Tb.b();){var Nb=Tb.e(),ic=vb,Va=new vl(Nb.j()),cb=new Dm(Nb.h()),zb=O().c,Ub=pm("freshName",new z(cb,zb)),jb=new vl(Nb.j()),db=O().c,ub=pm("Var",new z(jb,db)),Aa=O().c;vb=new Rl(!1,Va,Ub,pm("Lam",new z(ub,new z(ic,Aa))));Tb=Tb.f()}return vb}throw new gm("term "+S+" is not a valid parameter list");}return new Ol(S,Bm(a,ea,c,d,e))}if(l instanceof fm){var va=l.jt;if(d){var Ra=Hm(c);b=va;c=Ra;d=!1;continue}else throw new gm("unquoted term should be wrapped by quotes."); }if(l instanceof em){var rb=l.Iq,xb=Hm(c),mc=Bm(a,rb,xb,!0,e);if(d)throw new gm("nested quotation is not allowed.");return mc}if(l instanceof Pl){h=!0;k=l;var Ha=k.Za,Ka=k.Qb;if(Ha instanceof vl){var Oa=Ha.x;if(Ka instanceof Gl){var Na=Ka.Ra;if(Na instanceof z){var Da=Na,ta=Da.z,Ya=Da.p;if(null!==ta){var dc=new L(ta);if(!dc.b()){var ka=dc.k.h(),ya=dc.k.j();if(t().d===ka&&null!==ya){var Sa=ya.yb,xc=ya.ya;if(Ya instanceof z){var Sb=Ya,uc=Sb.z,Lb=Sb.p;if(null!==uc){var lc=new L(uc);if(!lc.b()){var Xb= lc.k.h(),ec=lc.k.j();if(t().d===Xb&&null!==ec){var Ab=ec.yb,Ob=ec.ya,fb=O().c;if((null===fb?null===Lb:fb.i(Lb))&&Lm().gD.L(Am(Oa))&&(!Mm(a,Oa,!0,c).tv()||Nm(new E(Oa),Am(Oa)))){if(d){var Wa=new Dm(Am(Oa)),bb=O().c,Ia=pm("Var",new z(Wa,bb)),Ua=Bm(a,xc,c,d,e),pc=Bm(a,Ob,c,d,e),sc=O().c;return pm("App",new z(Ia,new z(Ua,new z(pc,sc))))}var Ba=new vl(Oa),ob=t().d,nc=new sm(Sa,Bm(a,xc,c,d,e)),Ib=G(new H,ob,nc),vc=t().d,Vb=new sm(Ab,Bm(a,Ob,c,d,e)),fc=G(new H,vc,Vb),Bc=O().c;return new Pl(Ba,new Gl(new z(Ib, new z(fc,Bc))))}}}}}}}}}}}}if(h){var Pb=k.Za,Jb=k.Qb;if(d){var gc=Bm(a,Pb,c,d,e),Cb=Bm(a,Jb,c,d,e),cc=O().c;return pm("App",new z(gc,new z(Cb,cc)))}return new Pl(Bm(a,Pb,c,d,e),Bm(a,Jb,c,d,e))}if(l instanceof yl){var yc=l.ll;if(d){for(var Mc=c,qc=d,oc=yc,Qc=null,jc=null;oc!==u();){for(var sb=oc.e(),Gc=new Dm(sb.h().x),Wb=O().c,Cc=pm("Var",new z(Gc,Wb)),Fc=Bm(a,sb.j().ya,Mc,qc,e),qd=O().c,Yb=new Om(new z(Cc,new z(Fc,qd)));Yb.s();){var Nc=new z(Yb.t(),u());null===jc?Qc=Nc:jc.p=Nc;jc=Nc}oc=oc.f()}return pm("Rcd", null===Qc?u():Qc)}var ad=((Jh,If,Hg)=>He=>G(new H,He.h(),new sm(He.j().yb,Bm(a,He.j().ya,Jh,If,Hg))))(c,d,e);if(yc===u())var Uc=u();else{for(var cd=yc.e(),kc=new z(ad(cd),u()),Vc=kc,Hc=yc.f();Hc!==u();){var rc=Hc.e(),sd=new z(ad(rc),u());Vc=Vc.p=sd;Hc=Hc.f()}Uc=kc}return new yl(Uc)}if(l instanceof Fl){var Kc=l,Qd=Kc.bi,Ad=Kc.gg;if(d){var kd=Bm(a,Ad,c,d,e),Hd=O().c;return pm("Bra",new z(kd,Hd))}return new Fl(Qd,Bm(a,Ad,c,d,e))}if(l instanceof Ql){var Rd=l,Bd=Rd.Vl,ae=Rd.ml;if(null!==ae){var dd=ae.x; if(d){var od=Bm(a,Bd,c,d,e),Ta=new Dm(dd),wb=O().c,$a=pm("Var",new z(Ta,wb)),wa=O().c;return pm("Sel",new z(od,new z($a,wa)))}return new Ql(Bm(a,Bd,c,d,e),ae)}}if(l instanceof Rl){var hb=l,ra=hb.ep,wc=hb.Zn,ac=hb.$n,Id=hb.Mm;if(null!==wc){var ud=wc.x,be=Hm(c);if(d){wl(be,ud);var re=Im(be,ud,(t(),new L(!1)),!1,t().d).re,pe=new vl(re),bd=new Dm(ud),Rc=O().c,Wc=pm("freshName",new z(bd,Rc)),Wd=new vl(re),zd=O().c,Pa=pm("Var",new z(Wd,zd)),Db=Bm(a,ac,c,d,e),Oc=d,Tc=e.xH,Sd=O().c,Jc=Bm(a,Id,be,Oc,new Jm(a, Tc.Ce(new z(ud,Sd)))),vd=O().c;return new Rl(!1,pe,Wc,pm("Let",new z(Pa,new z(Db,new z(Jc,vd)))))}return new Rl(ra,new vl(ud),Bm(a,ac,c,d,e),Bm(a,Id,be,d,e))}}if(l instanceof Sl){var hd=l.Dj,de=((Jh,If,Hg)=>He=>{if(He instanceof Pm)return Bm(a,He,Jh,If,Hg);throw new gm("statement "+He+" is not supported in quasiquotes");})(Hm(c),d,e);if(hd===u())var ye=u();else{for(var jf=hd.e(),af=new z(de(jf),u()),pf=af,kf=hd.f();kf!==u();){var Be=kf.e(),Kd=new z(de(Be),u());pf=pf.p=Kd;kf=kf.f()}ye=af}return d? pm("Blk",ye):new Sl(ye)}if(l instanceof Gl){var ld=l.Ra;if(d){for(var Jd=c,Dd=d,Xd=ld,Yc=null,Ce=null;Xd!==u();){var te=Xd.e();a:{if(null!==te){var Ie=new L(te);if(!Ie.b()){var Jf=Ie.k.h(),df=Ie.k.j();if(Jf instanceof L){var vg=Jf.k;if(null!==vg){var wg=vg.x;if(null!==df){var xg=df.yb,eg=df.ya,vh=new vl(wg),fg=O().c,ih=pm("Var",new z(vh,fg)),Ig=Bm(a,eg,Jd,Dd,e),Tf=Xm(xg),Jg=pm("Fld",new z(Ig,Tf)),jh=O().c;var yg=new z(ih,new z(Jg,jh));break a}}}}}if(null!==te){var gg=new L(te);if(!gg.b()){var Cf= gg.k.h(),Uf=gg.k.j();if(t().d===Cf&&null!==Uf){var $g=Uf.yb,Ah=Bm(a,Uf.ya,Jd,Dd,e),Kg=Xm($g),Vf=pm("Fld",new z(Ah,Kg)),hg=O().c;yg=new z(Vf,hg);break a}}}throw new w(te);}for(var zg=new Om(yg);zg.s();){var Lg=new z(zg.t(),u());null===Ce?Yc=Lg:Ce.p=Lg;Ce=Lg}Xd=Xd.f()}return pm("Tup",null===Yc?u():Yc)}var Mg=((Jh,If,Hg)=>He=>{if(null!==He){var lj=new L(He);if(!lj.b()){var Wi=lj.k.h();lj=lj.k.j();if(null!==lj)return He=new sm(lj.yb,Bm(a,lj.ya,Jh,If,Hg)),G(new H,Wi,He)}}throw new w(He);})(c,d,e);if(ld=== u())var Wf=u();else{for(var Ng=ld.e(),Kf=new z(Mg(Ng),u()),xf=Kf,Og=ld.f();Og!==u();){var mi=Og.e(),Ci=new z(Mg(mi),u());xf=xf.p=Ci;Og=Og.f()}Wf=Kf}return new Gl(Wf)}if(l instanceof Vl){var Xh=l,wh=Xh.lp,Bh=Xh.mp;if(d){var ng=Bm(a,wh,c,d,e),kh=Bm(a,Bh,c,d,e),Kh=O().c;return pm("Subs",new z(ng,new z(kh,Kh)))}return new Vl(Bm(a,wh,c,d,e),Bm(a,Bh,c,d,e))}if(l instanceof Cl){var ni=l,Lh=ni.ai,lh=ni.Fm;if(d){b=Lh;continue}else return new Cl(Bm(a,Lh,c,d,e),lh)}if(l instanceof Tl){var Ch=l,Dh=Ch.br,Yh=Ch.ar; if(null!==Yh){var ah=Yh.ll;if(d){var oi=Bm(a,Dh,c,d,e),mj=Bm(a,Yh,c,d,e),wd=O().c;return pm("With",new z(oi,new z(mj,wd)))}var ge=Bm(a,Dh,c,d,e),De=((Jh,If,Hg)=>He=>G(new H,He.h(),new sm(He.j().yb,Bm(a,He.j().ya,Jh,If,Hg))))(c,d,e);if(ah===u())var qf=u();else{for(var og=ah.e(),Xf=new z(De(og),u()),mh=Xf,Ag=ah.f();Ag!==u();){var Bg=Ag.e(),Eh=new z(De(Bg),u());mh=mh.p=Eh;Ag=Ag.f()}qf=Xf}return new Tl(ge,new yl(qf))}}if(l instanceof Ul){var Pg=l,Di=Pg.Sn,Mh=Iaa(a,Pg.Hm,c,d,e);if(Mh instanceof fe){var pi= Mh.aa,Xi=Bm(a,Di,c,d,e),Qg=O().c;return pm("CaseOf",new z(Xi,new z(pi,Qg)))}if(Mh instanceof Ud){var nh=Mh.fa;return new Ul(Bm(a,Di,c,d,e),nh)}throw new w(Mh);}if(b.oc.b()){if(l instanceof Wl){var bh=l,Mj=bh.Gm,Nj=bh.Qn;if(!d)return new Wl(Bm(a,Mj,c,d,e),Bm(a,Nj,c,d,e))}if(l instanceof Yl){var ie=l.hp;if(!d)return new Yl(Bm(a,ie,c,d,e))}if(l instanceof Il){var Ac=l,Ve=Ac.nl,Td=Ac.np;if(!d)return new Il(Bm(a,Ve,c,d,e),Td)}if(l instanceof Zl){var lf=l,Yi=lf.qq,Jl=lf.$o;if(!d)return new Zl(Yi,Bm(a,Jl, c,d,e))}if(l instanceof Kl){var ll=l.cp;if(!d)return new Kl(Bm(a,ll,c,d,e))}if(l instanceof am&&!d)return b;if(l instanceof bm){var Bj=l,$k=Bj.Vn,Zh=Bj.Zo;if(!d)return new bm($k,Bm(a,Zh,c,d,e))}if(l instanceof Ml){var Ei=l,Yd=Ei.Ml;return new Ml(Bm(a,Ei.gs,c,d,e),Bm(a,Yd,c,d,e))}if(l instanceof dm){var bf=l,rf=bf.$q,Cg=bf.Zq;if(!d)return new dm(Bm(a,rf,c,d,e),Bm(a,Cg,c,d,e))}if(l instanceof Xl||l instanceof $l||l instanceof cm||l instanceof Wl||l instanceof Yl||l instanceof Il||l instanceof Zl||l instanceof Kl||l instanceof am||l instanceof bm||l instanceof dm)throw new gm("this quote syntax is not supported yet.");throw new w(l);}var nj=b.oc;nj.b()&&xm("Program reached and unexpected state.");b=nj.o()}},en=function Jaa(a,b,c,d){if(c instanceof um){var g=c.Un;return Ym(a,b,c.Xo,d).ba(Zm(a,c.Tn,d),Jaa(a,b,g,d))}if(c instanceof ym)return Zm(a,c.dn,d);if(zm()===c)return a=t().d,b=O().c,t(),c=new $m(new km("Error")),d=an("non-exhaustive case expression"),g=O().c,c=new bn(new cn(c,new z(d,g))),d=O().c,new dn(a, b,new Ud(new z(c,d)),O().c);throw new w(c);}; function Ym(a,b,c,d){return new fn((e,g)=>{var h=!1,k=null;a:{if(c instanceof vl&&(h=!0,k=c,"int"===k.x)){h=om(Al(),new km("Number"),"isInteger");k=O().c;h=new cn(h,new z(b,k));break a}if(h&&"bool"===k.x)h=new gn("\x3d\x3d\x3d",om(Al(),b,"constructor"),new hn("Boolean"));else{if(h){var l=k.x;if("true"===l||"false"===l){h=new gn("\x3d\x3d\x3d",b,new hn(l));break a}}if(h&&"string"===k.x)h=new gn("\x3d\x3d\x3d",om(Al(),b,"constructor"),new hn("String"));else{if(h){h=k.x;k=!1;l=null;var m=Cm(d,h);if(m instanceof L&&(k=!0,l=m,m=l.k,m instanceof jn)){h=new kn(b,jm(m,!0,d));break a}if(k&&(k=l.k,k instanceof ln)){h=new kn(b,jm(k,!0,d));break a}k=!1;l=null;m=a.di.ko.U(h);m instanceof L&&(k=!0,l=m);if(k&&(m=l.k,m instanceof mn)){h=new km(m.Tx);h=om(Al(),h,"is");k=J(new K,[b]);h=new cn(h,(Od(),Pd(u(),k)));break a}if(k&&l.k instanceof nn)throw new gm("cannot match type alias "+h);throw new gm("unknown match case: "+h);}if(c instanceof Dl)h=new gn("\x3d\x3d\x3d",b,Zm(a,c,d));else throw new w(c);}}}return new on(h, e,g)})}function Kaa(a,b){if(a instanceof z){var c=a.z;a=a.p;t();for(c=pn(c,new km("Object"),!1,b);!a.b();){var d=a.e();c=pn(d,c,!0,b);a=a.f()}return new L(c)}b=O().c;if(null===b?null===a:b.i(a))return t().d;throw new w(a);} function Laa(a,b,c,d,e){var g=b.uq.x,h=n=>{var r=Im(e,n,new L(!1),!1,t().d);return new qn(r.re,new km("this.#"+n))};if(c===u())h=u();else{var k=c.e(),l=k=new z(h(k),u());for(c=c.f();c!==u();){var m=c.e();m=new z(h(m),u());l=l.p=m;c=c.f()}h=k}l=b.bx.aa;l instanceof Ol?(b=l.Fj,k=im(a,l.Ej,e),t(),k=new L(k),l=b):k=t().d;b=k;a=rn(Zm(a,l,e));k=sn(e.lo);k instanceof L?(k=k.k,l=O().c,k=new z(k,l)):k=O().c;d.b()?d=O().c:(d=d.o(),d=tn(lm(e,d)));t();d=un(un(h,k),d);h=O().c;d=un(d,new z(a,h));d=new Ud(d);if(b instanceof L)return new vn(g,b.k,d);if(t().d===b)return new wn(g,d);throw new w(b);}function xn(a,b){b=a.di.ko.U(b);if(b instanceof L){var c=b.k;if(c instanceof mn){b=yn(c.Sx);c=zn(c.Sx);for(var d=null,e=null;c!==u();){var g=c.e();for(g=xn(a,g).m();g.s();){var h=new z(g.t(),u());null===e?d=h:e.p=h;e=h}c=c.f()}a=null===d?u():d;return un(b,a)}}a=b instanceof L&&null!==b.k?!0:t().d===b?!0:!1;if(a)return O().c;throw new w(b);}function An(a){return a?new vl("true"):new vl("false")} function Xm(a){var b=An(a.je),c=An(a.ch);a=An(a.Bh);var d=O().c;return new z(b,new z(c,new z(a,d)))} function pn(a,b,c,d){a:for(;;)if(a instanceof Pl)a=a.Za;else{if(a instanceof vl){a=a.x;break a}if(a instanceof Ql){var e=a.ml;if(null!==e){a=e.x;break a}}if(a instanceof Il)a=a.nl;else throw new gm("unsupported parents.");}e=!1;var g=null,h=Cm(d,a);if(h instanceof L&&(e=!0,g=h,g.k instanceof mn))return b;if(e){var k=g.k;if(k instanceof Hn)return c=jm(k,!0,d),t(),b=J(new K,[b]),new cn(c,Pd(u(),b))}if(e&&(b=g.k)&&b.$classData&&b.$classData.rb.pF&&!c)return jm(b,!0,d);if(e)throw new gm("unexpected parent symbol "+ g.k+".");if(t().d===h)throw new gm("unresolved parent "+a+".");throw new w(h);}function Mn(a,b){if(null===b)throw le();return b.sb?b.vb:me(b,new On(a))}function Pn(a){a=Qn(a);if(a instanceof fe)return gl();if(a instanceof Ud)return a.fa;throw new w(a);} function Rn(a,b,c,d,e){var g=v=>{if(null!==v){var x=v.h(),A=v.j();if(x instanceof L&&(x=x.k,null!==A)){var B=A.yb;A=A.ya;if(null!==B)return v=B.je,A=Pn(A),v=new Sn(v?(t(),new L(A)):t().d,A),G(new H,x,v)}}if(null!==v&&(x=v.h(),v=v.j(),t().d===x&&null!==v&&(A=v.yb,x=v.ya,null!==A&&(v=A.je,x instanceof vl))))return v?(t(),v=el(),v=new L(v)):v=t().d,v=new Sn(v,gl()),G(new H,x,v);xm("Program reached and unexpected state.")};if(c===u())g=u();else{for(var h=c.e(),k=h=new z(g(h),u()),l=c.f();l!==u();){var m= l.e();m=new z(g(m),u());k=k.p=m;l=l.f()}g=h}h=v=>null!==v&&(v=v.j(),null!==v)?v.yb.Bh:!1;k=c;a:for(;;)if(k.b()){l=u();break}else if(l=k.e(),c=k.f(),!1===!!h(l))k=c;else for(;;){if(c.b())l=k;else{l=c.e();if(!1!==!!h(l)){c=c.f();continue}l=c;c=new z(k.e(),u());m=k.f();for(k=c;m!==l;){var n=new z(m.e(),u());k=k.p=n;m=m.f()}for(m=l=l.f();!l.b();){n=l.e();if(!1===!!h(n)){for(;m!==l;)n=new z(m.e(),u()),k=k.p=n,m=m.f();m=l.f()}l=l.f()}m.b()||(k.p=m);l=c}break a}h=v=>{if(null!==v){var x=v.h();if(x instanceof L)return x.k.x}if(null!==v&&(x=v.h(),v=v.j(),t().d===x&&null!==v&&(v=v.ya,v instanceof vl)))return v.x;xm("Program reached and unexpected state.")};if(l===u())h=u();else{c=l.e();k=c=new z(h(c),u());for(l=l.f();l!==u();)m=l.e(),m=new z(h(m),u()),k=k.p=m,l=l.f();h=c}if(d===u())d=u();else{c=d.e();k=c=new z(Pn(c),u());for(d=d.f();d!==u();)l=d.e(),l=new z(Pn(l),u()),k=k.p=l,d=d.f();d=c}d=d.mf(new Tn(g),Un());c=Vn();g=Wn(ef(e),new Xn(a,c,b));b=Wn(ef(e),new Yn(a,c,b));c=v=>{var x=!1,A=null;if(v instanceof Cl){x=!0;A=v;var B=A.ai;if(B instanceof vl&&"this"===B.x)return!1}return x&&A.ai instanceof am?!1:v instanceof Zn&&(x=v.wd,A=v.Yc,x instanceof L&&!1===!!x.k&&A instanceof fe)?!0:v instanceof Pm?!0:!1};l=ef(e);a:for(;;)if(l.b()){c=u();break}else if(m=l.e(),k=l.f(),!1===!!c(m))l=k;else for(;;){if(k.b())c=l;else{m=k.e();if(!1!==!!c(m)){k=k.f();continue}m=k;k=new z(l.e(),u());n=l.f();for(l=k;n!==m;){var r=new z(n.e(),u());l=l.p=r;n=n.f()}for(n=m=m.f();!m.b();){r=m.e();if(!1===!!c(r)){for(;n!==m;)r=new z(n.e(), u()),l=l.p=r,n=n.f();n=m.f()}m=m.f()}n.b()||(l.p=n);c=k}break a}a=Wn(ef(e),new ao(a));return new bo(d,g,b,c,a,h)}function co(){this.Cs=this.di=null}co.prototype=new p;co.prototype.constructor=co;function eo(){}eo.prototype=co.prototype;function fo(a,b,c,d){a=Mm(a,b,c,d);if(!(a instanceof Ud)){if(a instanceof fe)throw a.aa;throw new w(a);}return a.fa} function Mm(a,b,c,d){O();var e=!1,g=null,h=Cm(d,b);a:{if(h instanceof L){e=!0;g=h;var k=g.k;if(k instanceof go){k.DP=!0;a.Cs.Yz.L(k.Nx)||ho(a.Cs,k.Nx,k.bF);b=new km(k.bF);"error"===k.Nx&&(d=u(),b=new cn(b,d));break a}}if(e&&(k=g.k,k instanceof io)){if(!k.z_)throw new jo(k);b=new km(k.nt);break a}if(e&&(k=g.k,k instanceof ko)){d=k.hJ;if((d.b()?0:d.o())&&!k.iJ)throw new gm("unguarded recursive use of by-value binding "+b);k.sF=!0;b=new km(k.re);k.hJ.b()&&!k.iJ&&(d=u(),b=new cn(b,d));break a}if(e&&(k= g.k)&&k.$classData&&k.$classData.rb.pF){b=jm(k,c,d);break a}if(e&&(c=g.k,c instanceof lo)){e=c.Ox;if((e.b()?0:e.o())&&!c.Px)throw new gm("unguarded recursive use of by-value binding "+b);b=c.FA;if(b.b())throw new gm("unqualified member symbol "+c);b=b.o();c.OP=!0;lm(d,b).sF=!0;c.EA?b=new km(b+".#"+c.dr):(b=new km(b),d=c.dr,b=om(Al(),b,d));c.Ox.b()&&!c.Px&&(d=u(),b=new cn(b,d));break a}if(e&&g.k instanceof mn)return O(),b=new gm("trait used in term position"),new fe(b);if(t().d===h){e=!1;d=d.ko.U(b); if(d instanceof L&&(e=!0,d.k instanceof nn))return O(),b=new gm("type alias "+b+" is not a valid expression"),new fe(b);e&&xm("register mismatch in scope");if(t().d===d)return O(),b=new gm("unresolved symbol "+b),new fe(b);throw new w(d);}throw new w(h);}return new Ud(b)} function Maa(a,b,c){if(null!==b){var d=b.Za,e=b.Qb;if(d instanceof Pl){var g=d.Qb;if(d.Za instanceof vl&&g instanceof Gl&&(d=g.Ra,d instanceof z&&(g=d.z,d=d.p,null!==g))){var h=new L(g);h.b()||(g=h.k.h(),h=h.k.j(),t().d===g&&null!==h&&(g=O().c,(null===g?null===d:g.i(d))&&e instanceof Gl&&(e=e.Ra,e instanceof z&&(d=e.z,e=e.p,null!==d&&(g=new L(d),g.b()||(d=g.k.h(),g=g.k.j(),t().d===d&&null!==g&&(d=O().c,null===d?null===e:d.i(e))))))))}}}if(null!==b&&(e=b.Za,d=b.Qb,e instanceof vl&&(e=e.x,d instanceof Gl&&(g=d.Ra,g instanceof z&&(d=g.z,g=g.p,null!==d&&(h=new L(d),!h.b()&&(d=h.k.h(),h=h.k.j(),t().d===d&&null!==h&&(d=h.ya,g instanceof z&&(h=g.z,g=g.p,null!==h))))))))){var k=new L(h);if(!k.b()&&(h=k.k.h(),k=k.k.j(),t().d===h&&null!==k&&(h=k.ya,k=O().c,(null===k?null===g:k.i(g))&&Lm().gD.L(Am(e))&&(!Mm(a,e,!0,c).tv()||Nm(new E(e),Am(e))))))return new gn(Am(e),Zm(a,d,c),Zm(a,h,c))}if(null!==b&&(g=b.Za,e=b.Qb,g instanceof Pl&&(d=g.Za,g=g.Qb,d instanceof Pl&&(h=d.Za,d=d.Qb,h instanceof vl&&"if"===h.x&& d instanceof Gl&&(h=d.Ra,h instanceof z&&(d=h.z,h=h.p,null!==d&&(d=d.j(),null!==d&&(d=d.ya,k=O().c,(null===k?null===h:k.i(h))&&g instanceof Gl&&(h=g.Ra,h instanceof z&&(g=h.z,h=h.p,null!==g&&(g=g.j(),null!==g&&(g=g.ya,k=O().c,(null===k?null===h:k.i(h))&&e instanceof Gl&&(e=e.Ra,e instanceof z&&(h=e.z,e=e.p,null!==h&&(h=h.j(),null!==h&&(h=h.ya,k=O().c,null===k?null===e:k.i(e)))))))))))))))))return new on(Zm(a,d,c),Zm(a,g,c),Zm(a,h,c));null!==b&&(e=b.Za,e instanceof Pl&&(e=e.Za,e instanceof Pl&&(e= e.Za,e instanceof vl&&"if"===e.x&&xm("Program reached and unexpected state."))));if(null!==b&&(e=b.Za,d=b.Qb,d instanceof Gl)){h=d.Ra;b=Zm(a,e,c);e=l=>{if(null!==l){var m=l.j();if(null!==m)return Zm(a,m.ya,c)}throw new w(l);};if(h===u())e=u();else{d=h.e();g=d=new z(e(d),u());for(h=h.f();h!==u();)k=h.e(),k=new z(e(k),u()),g=g.p=k,h=h.f();e=d}return new cn(b,e)}null!==b&&no();throw new gm("ill-formed application "+b);} function Zm(a,b,c){var d=!1,e=null,g=!1,h=null,k=!1,l=null;if(!b.oc.b()){var m=b.oc;m.b()&&xm("Program reached and unexpected state.");return Zm(a,m.o(),c)}if(b instanceof vl)return fo(a,b.x,!1,c);if(b instanceof am)return new km("super");if(b instanceof Ol){var n=b.Ej,r=b.Fj,v=Hm(c),x=im(a,n,v);return new oo(x,po(v.lo,Zm(a,r,v)))}if(b instanceof Pl)return Maa(a,b,c);if(b instanceof yl){var A=b.ll,B=wa=>{if(null!==wa){var hb=wa.h(),ra=wa.j();if(null!==ra)return wa=hb.x,ra=Zm(a,ra.ya,c),G(new H,wa, ra)}throw new w(wa);};if(A===u())var C=u();else{for(var D=A.e(),F=new z(B(D),u()),I=F,M=A.f();M!==u();){var N=M.e(),P=new z(B(N),u());I=I.p=P;M=M.f()}C=F}return new vo(C,O().c)}if(b instanceof Ql){var T=b.Vl,Y=b.ml;return om(Al(),Zm(a,T,c),Y.x)}if(b instanceof Rl){d=!0;e=b;var Z=e.Zn,S=e.$n,ea=e.Mm;if(!0===e.ep&&null!==Z){var ia=Z.x;if(S instanceof Ol){var X=S.Ej,sa=S.Fj,Ja=Hm(c),Xa=wl(Ja,ia),Fa=Hm(Ja),za=im(a,X,Fa),Qa=po(Fa.lo,Zm(a,sa,Fa));t();var Ma=new L(Xa);if(Qa instanceof Ud)var Ga=Qa.fa;else{if(!(Qa instanceof fe))throw new w(Qa);var ab=rn(Qa.aa),Hb=O().c;Ga=new z(ab,Hb)}var bc=new wo(Ma,za,Ga),yb=t().d,tb=new xl(Xa),eb=O().c,kb=new z(tb,eb),Rb=po(Ja.lo,Zm(a,ea,Ja)),Gb=O().c;return new dn(yb,kb,Rb,new z(bc,Gb))}}}if(d){var vb=e.Zn;if(!0===e.ep&&null!==vb)throw new gm("recursive non-function definition "+vb.x+" is not supported");}if(d){var Tb=e.Zn,Nb=e.$n,ic=e.Mm;if(null!==Tb){var Va=Tb.x,cb=Hm(c),zb=wl(cb,Va),Ub=t().d,jb=new xl(zb),db=O().c,ub=new z(jb,db),Aa=po(cb.lo,Zm(a,ic,cb)),va=Zm(a,Nb,c),Ra=O().c; return new dn(Ub,ub,Aa,new z(va,Ra))}}if(b instanceof Sl){var rb=b.Dj,xb=Hm(c),mc=rb.m(),Ha=new xo(mc,new y(wa=>{if(wa instanceof yo){var hb=O().c;return new z(wa,hb)}if(wa instanceof Zn){hb=wa.Rb;var ra=wa.hj;null!==hb&&(hb=hb.x,ra.b()?ra=R():(ra=ra.o(),ra=new L(ra.x)),zo(xb,hb,t().d,ra))}return wa.nv().j()}));Od();var Ka=Pd(u(),Ha),Oa=t().d,Na=O().c;t();var Da=xb.lo,ta=Ka.m(),Ya=new Ao(ta),dc=new Ef(Ya,new y(wa=>{if(null!==wa){var hb=wa.h(),ra=wa.Sc();if(hb instanceof Pm&&(1+ra|0)===Ka.K())return rn(Zm(a, hb,xb))}if(null!==wa&&(hb=wa.h(),hb instanceof Pm))return new Bo(Zm(a,hb,xb));if(null!==wa){var wc=wa.h();if(wc instanceof Zn){hb=wc.wd;ra=wc.Rb;var ac=wc.hj;wc=wc.Yc;if(null!==ra&&(ra=ra.x,wc instanceof fe))return wa=wc.aa,ac.b()?ac=R():(ac=ac.o(),ac=new L(ac.x)),wc=hb.b()||Co(new Do(a,wa)),hb=Im(xb,ra,hb,wc,ac),t(),hb=hb.re,t(),wa=Zm(a,wa,xb),wa=[G(new H,hb,new L(wa))],wa=J(new K,wa),new Eo(Pd(u(),wa))}}if(null!==wa&&(hb=wa.h(),hb instanceof yo)){a:{wa=O().c;ac=Fo(a,new z(hb,wa),t().d,xb);if(null=== ac)throw new w(ac);ra=ac.oj;wa=ac.oi;ac=ac.Xi;if(ra instanceof z)wa=ra.z,t(),wa=new L(wa);else{wc=O().c;if(null===wc?null!==ra:!wc.i(ra))throw new w(ra);if(wa instanceof z)wa=wa.z,t(),wa=new L(wa);else{ra=O().c;if(null===ra?null!==wa:!ra.i(wa))throw new w(wa);ac instanceof z?(wa=ac.z,t(),wa=new L(wa)):wa=t().d}}ra=!1;ac=null;if(wa instanceof L&&(ra=!0,ac=wa,wa=ac.k,wa instanceof jn)){ra=Hm(xb);hb=Go(a,wa,t().d,!1,ra);ra=Im(ra,"ctor",new L(!1),!1,t().d).re;ac=Ho(hb);if(null===ac)throw new w(ac);wc= ac.h();var Id=ac.j();if(wa.$u)t(),t(),ra=J(new K,[new Io(new L(new km(wa.mi)))]),ra=Pd(u(),ra);else{t();Jo();t();ac=J(new K,[ra]);ac=Ko(Pd(u(),ac));var ud=new km(ra);t();wc=new Bo(new Lo(ud,new oo(wc,new fe(new cn(new $m(new km(wa.mi)),Id)))));Id=new km(ra);Id=new Bo(new Lo(om(Al(),Id,"class"),new km(wa.mi)));t();ra=J(new K,[ac,wc,Id,new Io(new L(new km(ra)))]);ra=Pd(u(),ra)}wa=wa.mi;ac=t().d;wc=O().c;t();wa=new qn(wa,new dn(ac,wc,new Ud(new z(hb,ra)),O().c));break a}if(ra&&(wa=ac.k,wa instanceof Hn)){hb=Hm(xb);ra=Im(hb,"base",new L(!1),!1,t().d);hb=Go(a,wa,(t(),new L(ra)),!1,hb);wa=wa.Hp;t();ra=J(new K,[new xl(ra.re)]);ra=Pd(u(),ra);t();t();t();hb=J(new K,[new Io(new L(new Mo(hb)))]);hb=Pd(u(),hb);wa=new qn(wa,new oo(ra,new Ud(hb)));break a}if(ra&&(ac=ac.k,ac instanceof ln)){wa=Hm(xb);wc=Go(a,ac,t().d,!1,wa);Id=Im(wa,"ins",new L(!1),!1,t().d).re;wa=ac.lj;hb=t().d;ra=O().c;t();t();Jo();t();ud=J(new K,[Id]);ud=Ko(Pd(u(),ud));var be=new Lo(new km(Id),new cn(new $m(new km(ac.lj)),O().c));be= new Bo(be);var re=new km(Id);ac=new Bo(new Lo(om(Al(),re,"class"),new km(ac.lj)));t();ac=J(new K,[wc,ud,be,ac,new Io(new L(new km(Id)))]);ac=Pd(u(),ac);wa=new qn(wa,new dn(hb,ra,new Ud(ac),O().c));break a}throw new gm("unsupported NuTypeDef in local blocks: "+hb);}return wa}if(null!==wa&&(wa.h()instanceof No||wa.h()instanceof Oo||wa.h()instanceof Zn||wa.h()instanceof Po))throw new gm("unsupported definitions in blocks");throw new w(wa);}));Od();var ka=Qo(Da,Pd(u(),dc));return new dn(Oa,Na,new Ud(ka), O().c)}if(b instanceof Ul){g=!0;h=b;var ya=h.Sn,Sa=h.Hm;if(Sa instanceof ym){var xc=Sa.dn,Sb=Zm(a,ya,c),uc=Zm(a,xc,c),Lb=O().c;return new Ro(new z(Sb,new z(uc,Lb)))}}if(g){var lc=h.Sn,Xb=h.Hm;if(Xb instanceof um){var ec=Xb.Xo,Ab=Xb.Tn,Ob=Xb.Un;if(Ob instanceof ym){var fb=Ob.dn;return Ym(a,Zm(a,lc,c),ec,c).ba(Zm(a,Ab,c),Zm(a,fb,c))}}}if(g){var Wa=h.Hm,bb=Zm(a,h.Sn,c);if(bb.VJ())return en(a,bb,Wa,c);var Ia=Naa(c);c.lo.SA.oh(Ia);var Ua=new km(Ia),pc=new Lo(Ua,bb),sc=en(a,Ua,Wa,c),Ba=O().c;return new Ro(new z(pc, new z(sc,Ba)))}if(b instanceof Em){var ob=b.rq,nc=ob.u();So||(So=new To);return new hn(nc+(Oaa(ob)?"":"n"))}if(b instanceof Fm)return new hn(b.uu.gd.u());if(b instanceof Dm)return an(b.Lu);if(b instanceof Gm)return new hn(b.Xq?"undefined":"null");if(b instanceof Cl)return Zm(a,b.ai,c);if(b instanceof Tl){var Ib=b.br,vc=b.ar;if(null!==vc){var Vb=vc.ll,fc=a.Cs.Yz.U("withConstruct");if(fc instanceof L)var Bc=fc.k;else if(t().d===fc)Bc=ho(a.Cs,"withConstruct",Uo(a.di,"withConstruct"));else throw new w(fc); var Pb=new km(Bc),Jb=Zm(a,Ib,c),gc=wa=>{if(null!==wa){var hb=wa.h(),ra=wa.j();if(null!==hb&&(hb=hb.x,null!==ra))return wa=Zm(a,ra.ya,c),G(new H,hb,wa)}throw new w(wa);};if(Vb===u())var Cb=u();else{for(var cc=Vb.e(),yc=new z(gc(cc),u()),Mc=yc,qc=Vb.f();qc!==u();){var oc=qc.e(),Qc=new z(gc(oc),u());Mc=Mc.p=Qc;qc=qc.f()}Cb=yc}var jc=new vo(Cb,O().c),sb=O().c;return new cn(Pb,new z(Jb,new z(jc,sb)))}}if(b instanceof Fl){k=!0;l=b;var Gc=l.gg;if(!1===l.bi){if(Gc instanceof Pl){var Wb=Gc.Za;if(Wb instanceof vl){var Cc=Wb.x;if(Lm().gD.L(Cc))return new Vo(Zm(a,Gc,c))}}return Zm(a,Gc,c)}}if(k)return Zm(a,l.gg,c);if(b instanceof Gl){var Fc=b.Ra,qd=wa=>{if(null!==wa){var hb=wa.j();if(null!==hb)return Zm(a,hb.ya,c)}throw new w(wa);};if(Fc===u())var Yb=u();else{for(var Nc=Fc.e(),ad=new z(qd(Nc),u()),Uc=ad,cd=Fc.f();cd!==u();){var kc=cd.e(),Vc=new z(qd(kc),u());Uc=Uc.p=Vc;cd=cd.f()}Yb=ad}return new Wo(Yb)}if(b instanceof Vl){var Hc=b.mp,rc=Zm(a,b.lp,c),sd=Zm(a,Hc,c);return Xo(new Yo,rc,sd)}if(b instanceof dm){var Kc= b.$q,Qd=b.Zq,Ad=t().d,kd=O().c;t();var Hd=new Zo(Zm(a,Kc,c),Zm(a,Qd,c)),Rd=O().c;return new dn(Ad,kd,new Ud(new z(Hd,Rd)),O().c)}if(b instanceof Wl){var Bd=b.Gm,ae=b.Qn;if(Bd instanceof Vl||Bd instanceof Ql||Bd instanceof vl)return new $o("void",new Lo(Zm(a,Bd,c),Zm(a,ae,c)));throw new gm("illegal assignemnt left-hand side: "+Bd);}if(b instanceof Kl)return Zm(a,b.cp,c);if(b instanceof Xl)throw new gm("if expression was not desugared");if(b instanceof Yl){var dd=b.hp;if(dd instanceof vl){var od=fo(a, dd.x,!0,c);return od instanceof $m?od:new $m(od)}throw new gm("Unsupported `new` class term: "+dd);}if(b instanceof Zl)return Zm(a,b.$o,c);if(b instanceof Il)return Zm(a,b.nl,c);if(b instanceof bm){var Ta=b.Vn;if(null!==Ta)throw new gm("assignment of "+Ta.x+" is not supported outside a constructor");}if(b instanceof em){var wb=b.Iq,$a=Hm(c);return Zm(a,vm(a,wb,$a,!0,new Jm(a,ap())),$a)}if(b instanceof Ml)return Zm(a,b.Ml,c);if(b instanceof Xl||b instanceof $l||b instanceof cm||b instanceof fm)throw new gm("cannot generate code for term "+ b);throw new w(b);}function tn(a){if(a.sF){a=new qn(a.re,new km("this"));var b=O().c;return new z(a,b)}return O().c} function bp(a,b){if(null!==a){var c=a.pb,d=a.gb;if(cp()===c&&null!==d){c=d.V;a=new km("globalThis");a=om(Al(),a,c);d=new xl("base");var e=O().c;d=new z(d,e);t();b=new km(b);b=om(Al(),b,c);c=new km("base");e=O().c;return new Bo(new Lo(a,new oo(d,new fe(new cn(b,new z(c,e))))))}}if(null!==a&&(c=a.gb,null!==c))return a=c.V,c=new km("globalThis"),c=om(Al(),c,a),b=new km(b),b=new Lo(c,om(Al(),b,a)),new Bo(b);throw new w(a);} function Paa(a,b,c,d){c=Im(d,"base",new L(!1),!1,t().d);a=Go(a,b,(t(),new L(c)),!1,d);d=b.CA;d.b()&&xm("Program reached and unexpected state.");d=d.o();d=new qn(d,new km("this"));b=b.Hp;t();c=J(new K,[new xl(c.re)]);c=Pd(u(),c);t();var e=O().c;d=new z(d,e);t();t();a=J(new K,[new Io(new L(new Mo(a)))]);a=dl(Pd(u(),a),d);return new vn(b,c,new Ud(a))} function Qaa(a,b,c,d){d=Go(a,b,t().d,!1,d);c=new km("this.#"+b.lj);a=b.DA;a.b()&&xm("Program reached and unexpected state.");a=a.o();var e=new qn(a,new km("this"));a=b.lj;t();var g=O().c;e=new z(e,g);t();g=new gn("\x3d\x3d\x3d",c,new km("undefined"));t();b=[d,new Bo(new Lo(c,new $m(new cn(new km(b.lj),O().c)))),new Bo(new Lo(om(Al(),c,"class"),new km(b.lj)))];b=J(new K,b);b=[new dp(g,Pd(u(),b),O().c),new Io((t(),new L(c)))];b=J(new K,b);b=dl(Pd(u(),b),e);return new wn(a,new Ud(b))} function Ho(a){var b=a.sq;if(b===u())var c=u();else{c=b.e();var d=c=new z(new xl(c),u());for(b=b.f();b!==u();){var e=b.e();e=new z(new xl(e),u());d=d.p=e;b=b.f()}}b=a.sq;if(b===u())a=u();else for(a=b.e(),d=a=new z(new km(a),u()),b=b.f();b!==u();)e=b.e(),e=new z(new km(e),u()),d=d.p=e,b=b.f();return G(new H,c,a)} function Raa(a,b,c,d){a=Go(a,b,t().d,!1,d);c=Ho(a);if(null===c)throw new w(c);var e=c.h(),g=c.j();d=new km("this.#"+b.mi);c=b.Qx;c.b()&&xm("Program reached and unexpected state.");c=c.o();c=new qn(c,new km("this"));if(b.$u)t(),e=J(new K,[new Bo(new Lo(d,new km(b.mi)))]),e=Pd(u(),e);else{t();t();var h=new km("Object");h=om(Al(),h,"freeze");t();g=J(new K,[new cn(new $m(new km(b.mi)),g)]);g=new cn(h,Pd(u(),g));e=new Bo(new Lo(d,new oo(e,new fe(g))));g=new Bo(new Lo(om(Al(),d,"class"),new km(b.mi))); h=om(Al(),d,"unapply");var k=new km(b.mi);e=[e,g,new Bo(new Lo(h,om(Al(),k,"unapply")))];e=J(new K,e);e=Pd(u(),e)}b=b.mi;t();t();a=[new dp(new gn("\x3d\x3d\x3d",d,new km("undefined")),new z(new Bo(new Mo(a)),e),O().c),new Io((t(),new L(d)))];a=J(new K,a);a=Pd(u(),a);return new wn(b,new Ud(new z(c,a)))} function Go(a,b,c,d,e){var g=new $e,h=Hm(e,b.u()),k=Hm(h,b.Ua()+" inheritance"),l=Hm(h,b.Ua()+" body"),m=Hm(l,b.Ua()+" constructor"),n=u(),r=ep(new fp,n),v=u(),x=ep(new fp,v),A=b.FB(),B=Zd=>{var sf=Hm(l),oj=gp(sf,"qualifier");Zd=Zd.Cf.x;var al=g.sb?g.vb:Mn(a,g);sf=new np(al.fD,sf,oj);return G(new H,Zd,sf)};if(A===u())var C=u();else{for(var D=A.e(),F=new z(B(D),u()),I=F,M=A.f();M!==u();){var N=M.e(),P=new z(B(N),u());I=I.p=P;M=M.f()}C=F}var T=b.EB(),Y=Zd=>{var sf=Hm(l),oj=gp(sf,"qualifier");Zd=Zd.uq.x; var al=g.sb?g.vb:Mn(a,g);sf=new np(al.fD,sf,oj);return G(new H,Zd,sf)};if(T===u())var Z=u();else{for(var S=T.e(),ea=new z(Y(S),u()),ia=ea,X=T.f();X!==u();){var sa=X.e(),Ja=new z(Y(sa),u());ia=ia.p=Ja;X=X.f()}Z=ea}var Xa=un(C,Z);op();var Fa=pp(qp(),Xa),za=new rp(Fa),Qa=sp(za);if(Qa.b()){t();var Ma=gp(m,"qualifier"),Ga=new L(Ma)}else{for(var ab=Qa.o(),Hb=(new rp(Fa)).m();Hb.s();){var bc=Hb.t();if(bc.dp!==ab.dp)throw new Yj("assertion failed: the expected qualifier's runtime name should be "+(ab.dp+ ", "+bc.dp)+" found");}tp();var yb=gp(m,ab.dp);up(0,yb===ab.dp);t();Ga=new L(ab.dp)}for(var tb=b.wK(),eb=zn(b.mv()),kb=null,Rb=null;eb!==u();){for(var Gb=eb.e(),vb=xn(a,Gb).m();vb.s();){var Tb=new z(vb.t(),u());null===Rb?kb=Tb:Rb.p=Tb;Rb=Tb}eb=eb.f()}var Nb=null===kb?u():kb,ic=un(tb,Nb),Va=new fp,cb=b.GF();if(cb.b()){var zb=Zd=>{var sf=new lo(Zd,new L(!1),!1,!b.vy().L(Zd),Ga);vp(l,sf);wp(r,sf);Im(k,Zd,new L(!1),!1,t().d);return Im(m,Zd,new L(!1),!1,t().d).re};if(ic===u())var Ub=u();else{for(var jb= ic.e(),db=new z(zb(jb),u()),ub=db,Aa=ic.f();Aa!==u();){var va=Aa.e(),Ra=new z(zb(va),u());ub=ub.p=Ra;Aa=Aa.f()}Ub=db}}else{var rb=cb.o(),xb=Zd=>{if(Zd.cy()){var sf=new lo(Zd.h(),new L(!1),!1,!1,Ga);vp(l,sf);wp(r,sf);sf=G(new H,!1,Zd.h());wp(Va,sf)}return Im(m,Zd.h(),new L(!1),!1,t().d).re};if(rb===u())Ub=u();else{for(var mc=rb.e(),Ha=new z(xb(mc),u()),Ka=Ha,Oa=rb.f();Oa!==u();){var Na=Oa.e(),Da=new z(xb(Na),u());Ka=Ka.p=Da;Oa=Oa.f()}Ub=Ha}}var ta=Va.ha(),Ya=Zd=>{if(null!==Zd)return Zd=Zd.j(),new Bo(new Lo(new km("this.#"+ Zd),new km(Zd)));throw new w(Zd);};if(ta===u())var dc=u();else{for(var ka=ta.e(),ya=new z(Ya(ka),u()),Sa=ya,xc=ta.f();xc!==u();){var Sb=xc.e(),uc=new z(Ya(Sb),u());Sa=Sa.p=uc;xc=xc.f()}dc=ya}for(var Lb=b.EB();!Lb.b();){var lc=Lb.e(),Xb=new lo(lc.uq.x,t().d,!0,!1,Ga);vp(l,Xb);wp(r,Xb);Lb=Lb.f()}for(var ec=b.ZL();!ec.b();){var Ab=ec.e().uq.x,Ob=t().d,fb=zo(l,Ab,t().d,Ob);wp(r,fb);ec=ec.f()}for(var Wa=b.HF();!Wa.b();){var bb=Wa.e();if(bb instanceof Zn){var Ia=bb,Ua=Ia.wd,pc=Ia.Rb;if(null!==pc){var sc= new lo(pc.x,Ua,!1,!Ia.Pl,Ga);vp(l,sc);wp(r,sc)}}Wa=Wa.f()}var Ba=Fo(a,b.FB(),Ga,l);if(null===Ba)throw new w(Ba);var ob=Ba.oj,nc=Ba.oi,Ib=Ba.Xi;d&&Fo(a,b.FB(),t().d,a.di);for(var vc=ob;!vc.b();){var Vb=vc.e();wp(r,Vb);wp(x,Vb.mi);vc=vc.f()}for(var fc=nc;!fc.b();){var Bc=fc.e();wp(r,Bc);fc=fc.f()}for(var Pb=Ib;!Pb.b();){var Jb=Pb.e();wp(r,Jb);wp(x,Jb.lj);Pb=Pb.f()}var gc=b.EB(),Cb=Zd=>Laa(a,Zd,ic,Ga,Fa.Se(Zd.uq.x,new U(()=>{xm("Program reached and unexpected state.")})).zu);if(gc===u())var cc=u();else{for(var yc= gc.e(),Mc=new z(Cb(yc),u()),qc=Mc,oc=gc.f();oc!==u();){var Qc=oc.e(),jc=new z(Cb(Qc),u());qc=qc.p=jc;oc=oc.f()}cc=Mc}var sb=Zd=>Paa(a,Zd,r.ha(),Fa.Se(Zd.Hp,new U(()=>{xm("Program reached and unexpected state.")})).zu);if(nc===u())var Gc=u();else{for(var Wb=nc.e(),Cc=new z(sb(Wb),u()),Fc=Cc,qd=nc.f();qd!==u();){var Yb=qd.e(),Nc=new z(sb(Yb),u());Fc=Fc.p=Nc;qd=qd.f()}Gc=Cc}var ad=un(cc,Gc),Uc=Zd=>Qaa(a,Zd,r.ha(),Fa.Se(Zd.lj,new U(()=>{xm("Program reached and unexpected state.")})).zu);if(Ib===u())var cd= u();else{for(var kc=Ib.e(),Vc=new z(Uc(kc),u()),Hc=Vc,rc=Ib.f();rc!==u();){var sd=rc.e(),Kc=new z(Uc(sd),u());Hc=Hc.p=Kc;rc=rc.f()}cd=Vc}var Qd=un(ad,cd),Ad=Zd=>Raa(a,Zd,r.ha(),Fa.Se(Zd.mi,new U(()=>{xm("Program reached and unexpected state.")})).zu);if(ob===u())var kd=u();else{for(var Hd=ob.e(),Rd=new z(Ad(Hd),u()),Bd=Rd,ae=ob.f();ae!==u();){var dd=ae.e(),od=new z(Ad(dd),u());Bd=Bd.p=od;ae=ae.f()}kd=Rd}var Ta=un(Qd,kd);if(c instanceof L){var wb=c.k;t();var $a=new L(new km(wb.re))}else $a=Kaa(b.pH(), e);for(var wa=zn(b.mv()),hb=null,ra=null;wa!==u();){var wc=wa.e(),ac=!1,Id=null,ud=e.ko.U(wc);a:{if(ud instanceof L){ac=!0;Id=ud;var be=Id.k;if(be instanceof mn){var re=be.Tx;t();var pe=new L(re);break a}}if(ac&&null!==Id.k)pe=t().d;else if(t().d===ud)pe=t().d;else throw new w(ud);}for(var bd=pe.m();bd.s();){var Rc=new z(bd.t(),u());null===ra?hb=Rc:ra.p=Rc;ra=Rc}wa=wa.f()}var Wc=null===hb?u():hb;if(c.b()){var Wd=b.pH(),zd=Zd=>{if(Zd instanceof Pl&&(Zd=Zd.Qb,Zd instanceof Gl)){var sf=Zd.Ra;Zd=Qm=> {if(null!==Qm){var Rm=Qm.j();if(null!==Rm)return Zm(a,Rm.ya,k)}throw new w(Qm);};if(sf===u())return u();var oj=sf.e(),al=oj=new z(Zd(oj),u());for(sf=sf.f();sf!==u();){var Ll=sf.e();Ll=new z(Zd(Ll),u());al=al.p=Ll;sf=sf.f()}return oj}return O().c};if(Wd===u())var Pa=u();else{for(var Db=Wd.e(),Oc=new z(zd(Db),u()),Tc=Oc,Sd=Wd.f();Sd!==u();){var Jc=Sd.e(),vd=new z(zd(Jc),u());Tc=Tc.p=vd;Sd=Sd.f()}Pa=Oc}for(var hd=Pa,de=null,ye=null;hd!==u();){for(var jf=Km(hd.e()).m();jf.s();){var af=new z(jf.t(),u()); null===ye?de=af:ye.p=af;ye=af}hd=hd.f()}var pf=Km(null===de?u():de),kf=t().d;Jd=pf;Dd=kf}else{var Be=Im(m,"rest",new L(!1),!1,t().d);t();var Kd=J(new K,[new km("..."+Be.re)]),ld=Pd(u(),Kd);t();var Jd=ld,Dd=new L(Be.re)}for(var Xd=Jd,Yc=Dd,Ce=new fp,te=b.HF(),Ie=null,Jf=null;te!==u();){var df=te.e();a:{if(df instanceof bm){var vg=df,wg=vg.Vn,xg=vg.Zo;if(null!==wg){var eg=wg.x;t();var vh=new Lo(new km("this.#"+eg),Zm(a,xg,m)),fg=[new Bo(vh),new qn(Im(m,eg,(t(),new L(!1)),!1,t().d).re,new km("this.#"+ eg))],ih=J(new K,fg);var Ig=Pd(u(),ih);break a}}if(df instanceof Pm){var Tf=new Bo(Zm(a,df,m)),Jg=O().c;Ig=new z(Tf,Jg)}else{if(df instanceof Zn){var jh=df,yg=jh.Rb,gg=jh.Yc;if(null!==yg){var Cf=yg.x;if(gg instanceof fe){var Uf=gg.aa;if(jh.Pl){var $g=!jh.Om.b(),Ah=G(new H,$g,Cf);wp(Va,Ah);t();var Kg=[new Bo(new Lo(new km("this.#"+Cf),Zm(a,Uf,m))),new qn(Im(m,Cf,(t(),new L(!1)),!1,t().d).re,new km("this.#"+Cf))],Vf=J(new K,Kg);Ig=Pd(u(),Vf)}else{var hg=Cm(l,Cf);b:{if(hg instanceof L){var zg=hg.k;if(zg instanceof lo){var Lg=zg;break b}}throw new Yj("error when handling "+Cf);}if(Lg.OP||Ub.L(Cf)){wp(Ce,Cf);t();var Mg=[new Bo(new Lo(new km("this.#"+Cf),Zm(a,Uf,m))),new qn(Im(m,Cf,(t(),new L(!1)),!1,t().d).re,new km("this.#"+Cf))],Wf=J(new K,Mg);Ig=Pd(u(),Wf)}else{var Ng=new qn(Im(m,Cf,(t(),new L(!1)),!1,t().d).re,Zm(a,Uf,m)),Kf=O().c;Ig=new z(Ng,Kf)}}break a}}}Ig=O().c}}for(var xf=Ig.m();xf.s();){var Og=new z(xf.t(),u());null===Jf?Ie=Og:Jf.p=Og;Jf=Og}te=te.f()}var mi=null===Ie?u():Ie,Ci=sn(m.lo);if(Ci instanceof L)var Xh=Ci.k,wh=O().c,Bh=new z(Xh,wh);else Bh=O().c;var ng=b.iM();if(ng instanceof L){var kh=ng.k.bx;a:{if(null!==kh){var Kh=kh.aa;if(Kh instanceof Ol){var ni=Kh.Ej,Lh=Kh.Fj;if(ni instanceof Gl){var lh=ni.Ra;if(lh instanceof z){var Ch=lh.z,Dh=lh.p;if(null!==Ch){var Yh=new L(Ch);if(!Yh.b()){var ah=Yh.k.j();if(null!==ah){var oi=ah.ya;if(oi instanceof vl){var mj=oi.x,wd=O().c;if((null===wd?null===Dh:wd.i(Dh))&&Lh instanceof Rl){var ge=Lh.Mm;if(ge instanceof Gl){var De=ge.Ra,qf=wl(Hm(h,"unapply "+b.Ua()), mj),og=new xl(qf),Xf=O().c,mh=new z(og,Xf);t();var Ag=Zd=>{if(null!==Zd){var sf=new L(Zd);if(!sf.b()&&(sf=sf.k.j(),null!==sf)){Zd=sf.ya;if(Zd instanceof Ql){var oj=Zd.Vl;sf=Zd.ml;if(oj instanceof vl&&(oj=oj.x,null!==sf))return new km(oj+"."+sf.x)}return Zm(a,Zd,e)}}throw new w(Zd);};if(De===u())var Bg=u();else{for(var Eh=De.e(),Pg=new z(Ag(Eh),u()),Di=Pg,Mh=De.f();Mh!==u();){var pi=Mh.e(),Xi=new z(Ag(pi),u());Di=Di.p=Xi;Mh=Mh.f()}Bg=Pg}var Qg=new vn("unapply",mh,new fe(new Wo(Bg))),nh=O().c;var bh= new z(Qg,nh);break a}}}}}}}}}}throw new gm("invalid unapply method in "+b.Ua());}}else bh=O().c;var Mj=Ga.b()?O().c:tn(lm(m,Ga.k)),Nj=b.Ua(),ie=ic;a:for(var Ac;;)if(ie.b()){Ac=u();break}else{var Ve=ie.e(),Td=ie.f();if(!1===!!b.vy().L(Ve))ie=Td;else for(var lf=ie,Yi=Td;;){if(Yi.b())Ac=lf;else{var Jl=Yi.e();if(!1!==!!b.vy().L(Jl)){Yi=Yi.f();continue}for(var ll=Yi,Bj=new z(lf.e(),u()),$k=lf.f(),Zh=Bj;$k!==ll;){var Ei=new z($k.e(),u());Zh=Zh.p=Ei;$k=$k.f()}for(var Yd=ll.f(),bf=Yd;!Yd.b();){var rf=Yd.e(); if(!1===!!b.vy().L(rf)){for(;bf!==Yd;){var Cg=new z(bf.e(),u());Zh=Zh.p=Cg;bf=bf.f()}bf=Yd.f()}Yd=Yd.f()}bf.b()||(Zh.p=bf);Ac=Bj}break a}}if(Ac===u())var nj=u();else{for(var Jh=Ac.e(),If=new z(G(new H,!1,Jh),u()),Hg=If,He=Ac.f();He!==u();){var lj=He.e(),Wi=new z(G(new H,!1,lj),u());Hg=Hg.p=Wi;He=He.f()}nj=If}var Oj=Va.ha(),mo=un(nj,Oj),mm=un(Ce.ha(),ic),nm=un(un(un(Mj,Bh),dc),mi),dq=x.ha();return new xp(Nj,ic,mo,mm,$a,Xd,Ub,Yc,Ta,Wc,nm,dq,!b.GF().b(),bh)} function Fo(a,b,c,d){for(var e=new fp,g=new fp,h=new fp,k=new fp,l=b;!l.b();){var m=l.e();a:{if(null!==m){var n=m.pb,r=m.gb,v=m.hg,x=m.Sg,A=m.Di,B=m.ei;if(cp()===n&&null!==r){var C=r.V,D=Rn(a,C,(x.b()?new Gl(O().c):x.o()).Ra,A,B);if(null===D)throw new w(D);var F=D.lr,I=D.mr,M=D.rt,N=D.st,P=D.tt,T=D.kv;if(v===u())var Y=u();else{for(var Z=v.e(),S=new z(Z.j().V,u()),ea=S,ia=v.f();ia!==u();){var X=ia.e(),sa=new z(X.j().V,u());ea=ea.p=sa;ia=ia.f()}Y=S}var Ja=new Hn(C,Y,F,I,M,N,T,P,c);yp(d,Ja);m.fl.b()&& wp(h,Ja);break a}}if(null!==m){var Xa=m.pb,Fa=m.gb,za=m.hg,Qa=m.Sg,Ma=m.Di,Ga=m.ei;if(zp()===Xa&&null!==Fa){var ab=Fa.V,Hb=Rn(a,ab,(Qa.b()?new Gl(O().c):Qa.o()).Ra,Ma,Ga);if(null===Hb)throw new w(Hb);var bc=Hb.lr,yb=Hb.mr,tb=Hb.rt,eb=Hb.st,kb=Hb.tt;if(za===u())var Rb=u();else{for(var Gb=za.e(),vb=new z(Gb.j().V,u()),Tb=vb,Nb=za.f();Nb!==u();){var ic=Nb.e(),Va=new z(ic.j().V,u());Tb=Tb.p=Va;Nb=Nb.f()}Rb=vb}var cb=new ln(ab,Rb,bc,yb,tb,eb,Ma,kb,c);yp(d,cb);m.fl.b()&&wp(k,cb);break a}}if(null!==m){var zb= m.pb,Ub=m.gb,jb=m.hg,db=m.Hj;if(Ap()===zb&&null!==Ub){var ub=Ub.V;if(jb===u())var Aa=u();else{for(var va=jb.e(),Ra=new z(va.j().V,u()),rb=Ra,xb=jb.f();xb!==u();){var mc=xb.e(),Ha=new z(mc.j().V,u());rb=rb.p=Ha;xb=xb.f()}Aa=Ra}Saa(d,ub,Aa,db.b()?gl():db.o());break a}}if(null!==m){var Ka=m.pb,Oa=m.gb,Na=m.hg,Da=m.Sg,ta=m.Qm,Ya=m.Di,dc=m.ei;if(Bp()===Ka&&null!==Oa){var ka=Oa.V;b:{if(ta instanceof L){var ya=ta.k;if(null!==ya){var Sa=ya.ks,xc=ya.js;if(null!==Sa){var Sb=Sa.Ra;if(null!==xc){var uc=xc.Dj; t();var Lb=(dd=>od=>{if(null!==od){var Ta=od.h(),wb=od.j();if(Ta instanceof L&&(Ta=Ta.k,null!==Ta&&(Ta=Ta.x,null!==wb)))return G(new H,Ta,wb.yb.Bh)}if(null!==od&&(wb=od.h(),od=od.j(),t().d===wb&&null!==od&&(wb=od.yb,od=od.ya,od instanceof vl)))return G(new H,od.x,wb.Bh);throw new gm("Unexpected constructor parameters in "+dd+".");})(ka);if(Sb===u())var lc=u();else{for(var Xb=Sb.e(),ec=new z(Lb(Xb),u()),Ab=ec,Ob=Sb.f();Ob!==u();){var fb=Ob.e(),Wa=new z(Lb(fb),u());Ab=Ab.p=Wa;Ob=Ob.f()}lc=ec}var bb= new L(lc);var Ia=uc;break b}}}}var Ua=t().d,pc=O().c;bb=Ua;Ia=pc}var sc=bb,Ba=Ia,ob=Rn(a,ka,(Da.b()?new Gl(O().c):Da.o()).Ra,Ya,dc);if(null===ob)throw new w(ob);var nc=ob.lr,Ib=ob.mr,vc=ob.rt,Vb=ob.st,fc=ob.tt,Bc=ob.kv;if(Na===u())var Pb=u();else{for(var Jb=Na.e(),gc=new z(Jb.j().V,u()),Cb=gc,cc=Na.f();cc!==u();){var yc=cc.e(),Mc=new z(yc.j().V,u());Cb=Cb.p=Mc;cc=cc.f()}Pb=gc}var qc=Cp(m);b:{if(qc instanceof L){var oc=qc.k;if(null!==oc){var Qc=oc.wd,jc=oc.Rb,sb=oc.Ch,Gc=oc.Yc;if(Gc instanceof fe){var Wb= Gc.aa;t();var Cc=new Dp(!(Qc.b()||!Qc.o()),new Ep(ka),jc,sb,(O(),new fe(Wb)));var Fc=new L(Cc);break b}}}Fc=t().d}var qd=new jn(ka,Pb,sc,nc,Ib,Fc,vc,un(Ba,Vb),Ya,Bc,fc,c,m.Sg.b());yp(d,qd);m.fl.b()&&wp(g,qd);break a}}if(null!==m){var Yb=m.pb,Nc=m.gb,ad=m.hg,Uc=m.Sg,cd=m.Di,kc=m.ei;if(Fp()===Yb&&null!==Nc){var Vc=Nc.V,Hc=Rn(a,Vc,(Uc.b()?new Gl(O().c):Uc.o()).Ra,cd,kc);if(null===Hc)throw new w(Hc);var rc=Hc.lr,sd=Hc.mr;if(ad===u())var Kc=u();else{for(var Qd=ad.e(),Ad=new z(Qd.j().V,u()),kd=Ad,Hd=ad.f();Hd!== u();){var Rd=Hd.e(),Bd=new z(Rd.j().V,u());kd=kd.p=Bd;Hd=Hd.f()}Kc=Ad}var ae=Taa(d,Vc,Kc,rc,sd);m.fl.b()&&wp(e,ae);break a}}throw new w(m);}l=l.f()}return new Gp(e.ha(),g.ha(),h.ha(),k.ha())}function To(){this.kN=this.jN=null;So=this;this.jN=Hp("9007199254740991");this.kN=Hp("-9007199254740991")}To.prototype=new p;To.prototype.constructor=To;function Oaa(a){var b=So;return 0>=b.kN.$l(a)?0>=a.$l(b.jN):!1}To.prototype.$classData=q({nV:0},!1,"mlscript.JSBackend$",{nV:1,g:1});var So; function Jm(a,b){this.xH=b;if(null===a)throw null;}Jm.prototype=new p;Jm.prototype.constructor=Jm;Jm.prototype.$classData=q({uV:0},!1,"mlscript.JSBackend$FreeVars",{uV:1,g:1});function Do(a,b){this.yH=null;this.yV=b;if(null===a)throw null;this.yH=a}Do.prototype=new p;Do.prototype.constructor=Do;function Co(a){var b=a.yV;if(b instanceof Ol)return!0;if(b instanceof Fl){var c=b.gg;if(!1===b.bi)return Co(new Do(a.yH,c))}return b instanceof Cl?Co(new Do(a.yH,b.ai)):!1} Do.prototype.$classData=q({xV:0},!1,"mlscript.JSBackend$TermOps",{xV:1,g:1});function Ip(){}Ip.prototype=new p;Ip.prototype.constructor=Ip;function Jp(){}Jp.prototype=Ip.prototype;function Kp(){}Kp.prototype=new p;Kp.prototype.constructor=Kp;function an(a){Lp();return new hn(Mp(Np(),a))} function Op(a,b){return Pp(Hf(b).De(Qp().ye,new fn((c,d)=>{var e=G(new H,c,d);c=e.y;d=e.w;if(null!==d)return e=d.h(),d=d.Sc(),Rp(Rp(c,e instanceof El?Sp(Qp(),"_"+d):e.xa()),Pe(new E(d),-1+b.K()|0)?Qp().ye:Sp(Qp(),", "));throw new w(e);})),!0)}function Tp(a,b){return Pp(Hf(b).De(Qp().ye,new fn((c,d)=>{var e=G(new H,c,d);c=e.y;d=e.w;if(null!==d)return e=d.Sc(),Rp(Rp(c,Up(d.h(),Vp().Az)),Pe(new E(e),-1+b.K()|0)?Qp().ye:Sp(Qp(),", "));throw new w(e);})),!0)} Kp.prototype.$classData=q({MV:0},!1,"mlscript.JSExpr$",{MV:1,g:1});var Wp;function Lp(){Wp||(Wp=new Kp);return Wp}function Xp(){this.zH=null;Yp=this;var a=new Zp;u();var b=Dk("^[A-Za-z$][A-Za-z0-9$]*$");a.nG=b;this.zH=a}Xp.prototype=new p;Xp.prototype.constructor=Xp;function om(a,b,c){return new $p(b,new km(c))}function aq(a,b){return bq(new cq(a.zH.nG,nb(b)))?!eq().qF.L(b):!1}function zl(a,b){return aq(a,b)?b:Mp(Np(),b)}Xp.prototype.$classData=q({PV:0},!1,"mlscript.JSField$",{PV:1,g:1});var Yp; function Al(){Yp||(Yp=new Xp);return Yp}function fq(a){if(a instanceof Em){tp();a=gq(new Ep("int"));var b=new Ep("number");return a.bc(b)}if(a instanceof Dm)return tp(),gq(new Ep("string"));if(a instanceof Fm)return tp(),gq(new Ep("number"));if(a instanceof Gm)return ap();throw new w(a);} function yq(a){if(a instanceof Em){tp();a=gq(new Ep("Int"));var b=new Ep("Num");a=a.bc(b);b=new Ep("Object");return a.bc(b)}if(a instanceof Dm)return tp(),a=gq(new Ep("Str")),b=new Ep("Object"),a.bc(b);if(a instanceof Fm)return tp(),a=gq(new Ep("Num")),b=new Ep("Object"),a.bc(b);if(a instanceof Gm)return tp(),gq(new Ep("Object"));throw new w(a);}function zq(a){if(a instanceof vl)return tp(),gq(a);a=a.Vj().m();a=new xo(a,new y(b=>b.jn().m()));return Aq(Bq(),a)} function Cq(a,b){b.b()||(b=b.o(),a.fm(b.fh),a.em(b.eh),t(),a.on(new L(b.dh)));return a} function Dq(a){var b=tc();try{if(0>a.rn()){var c=Eq(a),d=new Ef(c,new y(I=>I.fh)),e=Fq(),g=Gq(d,e);if(g.b())throw Hq(new Iq,b,t().d);a.fm(g.o()|0)}if(0>a.qn()){var h=Eq(a),k=new Ef(h,new y(I=>I.eh)),l=Fq(),m=Jq(k,l);if(m.b())throw Hq(new Iq,b,t().d);a.em(m.o()|0)}var n=a.pn();if(n.b()){var r=Eq(a),v=new Ef(r,new y(I=>I.dh));Od();var x=Pd(u(),v),A=hl(x)}else{var B=n.o(),C=O().c;A=new z(B,C)}c=A;var D=c.K();if(!Pe(new E(D),1))throw new Yj("assertion failed: "+c);t();var F=new Kq(a.rn(),a.qn(),c.e()); return new L(F)}catch(I){if(I instanceof Iq){a=I;if(a.Qg===b)return a.Cj();throw a;}throw I;}} function Lq(a){var b=tc();try{var c=Mq(a),d=new Ef(c,new y(N=>N.fh)),e=Fq(),g=Gq(d,e);if(g.b())throw Hq(new Iq,b,t().d);var h=g.o()|0,k=Mq(a),l=new Ef(k,new y(N=>N.eh)),m=Fq(),n=Jq(l,m);if(n.b())throw Hq(new Iq,b,t().d);var r=n.o()|0,v=a.pn();if(v.b()){var x=Mq(a),A=new Ef(x,new y(N=>N.dh));Od();var B=Pd(u(),A),C=hl(B)}else{var D=v.o(),F=O().c;C=new z(D,F)}a=C;tp();var I=a.K();up(0,Pe(new E(I),1));t();var M=new Kq(h,r,a.e());return new L(M)}catch(N){if(N instanceof Iq){h=N;if(h.Qg===b)return h.Cj(); throw h;}throw N;}}function Eq(a){a=a.Vj().m();return new xo(a,new y(b=>b.A().m()))}function Mq(a){var b=a.Vj();return new xo(new Om(new z(a,b)),new y(c=>c.A().m()))}function Nq(a){a.fm(-1);a.em(-1);a.on(t().d)}function Oq(){}Oq.prototype=new p;Oq.prototype.constructor=Oq;function Pq(){}Pq.prototype=Oq.prototype;function Te(a){this.mN=a}Te.prototype=new p;Te.prototype.constructor=Te; function Ye(a,b){tp();var c=b.K();up(0,Pe(new E(c),-1+a.mN.Jv.K()|0));c=a.mN.Jv.Ja(new y(d=>{var e=new Ue(J(new K,[d]));d=u();e=e.Jv;Qq();if(e.K()!==(1+d.K()|0))throw Kj("wrong number of arguments ("+d.K()+") for interpolated string with "+e.K()+" parts");d=e.m();e=Rq().Pa;var g=d.t(),h=new Sq;g=Tq(Qq(),g);for(h=Uq(h,g);e.s();)g=e.t(),h.ja=""+h.ja+g,g=d.t(),g=Tq(Qq(),g),h.ja=""+h.ja+g;return new Vq(h.ja)}));a=c.e();c=c.f();b=new Wq(b,b,c);c=new fn((d,e)=>Xq(d.fp,e));Yq();b=Zq(b,c);return new $q(b.cc(a).ha())} Te.prototype.$classData=q({CW:0},!1,"mlscript.Message$MessageContext",{CW:1,g:1});function ar(){return new Em(br())} function cr(a,b,c){if(b>24&&0===(1&Wb.Jt)<<24>>24&&(Wb.tR=lr(),Wb.Jt=(1|Wb.Jt)<<24>>24);var Cc=Wb.tR;var Fc=new H,qd=""+c+Va+sb,Yb=new mr;nr(Yb,or(qd),qd.length);var Nc=pr(Yb)<=Cc.xy.ds?Cc.xy:new xi(pr(Yb),Bi().OC);var ad=new tr(Yb,Nc);return G(Fc,new Fm(ad),Gc)} function Uaa(a,b,c){var d=a.Le;jr();t();a=gr(a,c,1+c|0);b=G(new H,b,new L(a));a=O().c;d.n(kr(0,new z(b,a),!0,ir()))}function Hr(a,b,c,d,e){a=G(new H,c,gr(a,d,b));return new z(a,e)} function Je(a,b,c){this.$b=this.uN=this.vN=null;this.we=0;this.HH=this.JH=this.IH=null;this.Gs=0;this.OW=a;this.Le=b;this.NW=c;a=a.Us.TM;b=Ir();c=Ir();if(b===c)a=or(a);else if(b=new Jr(a),0<=b.Xh.length)a=new Ic(b.Xh.length),b.Gc(a,0,2147483647);else{a=[];for(b=Kr(new Lr,new Mr(b.Xh));0=a} function Rr(a,b,c,d){return d=c.length}function Vaa(a,b,c,d){for(;b=r||65<=r&&70>=r){var v=new z(hc(r),e);d=1+d|0;e=v;h=!1;continue}if(Pe(new E(hc(r)),hc(95))){var x=1+d|0,A=e.b();d=x;g=A;h=!0;continue}var B=er(e).m(),C=ze(B,"","",""),D=g,F=h,I=d;k=C;l=D;m=F;n=I}else{var M=er(e).m(),N=ze(M,"","",""),P=g,T=h,Y=d;k=N;l=P;m=T;n=Y}break}var Z=k,S=!!m,ea=n|0;if(l){var ia=a.Le;fr(); var X=Ye(new Te(new Ue(J(new K,["Leading separator is not allowed"]))),u());t();var sa=gr(a,-1+c|0,c),Ja=G(new H,X,new L(sa)),Xa=O().c;ia.n(hr(0,new z(Ja,Xa),!0,ir()))}if(S){var Fa=a.Le;fr();var za=Ye(new Te(new Ue(J(new K,["Trailing separator is not allowed"]))),u());t();var Qa=gr(a,-1+ea|0,ea),Ma=G(new H,za,new L(Qa)),Ga=O().c;Fa.n(hr(0,new z(Ma,Ga),!0,ir()))}var ab=G(new H,""===Z?t().d:(t(),new L(Z)),ea),Hb=ab.y,bc=ab.w|0;if(t().d===Hb){var yb=a.Le;jr();var tb=new Te(new Ue(J(new K,["Expect at least one ", " digit"]))),eb=[We(Xe(),"hexadecimal")],kb=Ye(tb,J(new K,eb));t();var Rb=gr(a,c,2+c|0),Gb=G(new H,kb,new L(Rb)),vb=O().c;yb.n(kr(0,new z(Gb,vb),!0,ir()));return G(new H,ar(),bc)}var Tb=ab.y,Nb=ab.w|0;if(Tb instanceof L){var ic=Tb.k;return G(new H,new Em(Sr(Tr(),ic,16)),Nb)}throw new w(ab);case 111:for(var Va=2+b|0,cb=Va,zb=O().c,Ub=!1,jb=!1,db,ub,Aa,va;;){if(cb=Ra){var rb=new z(hc(Ra),zb);cb=1+cb|0;zb=rb;jb=!1;continue}if(Pe(new E(hc(Ra)),hc(95))){var xb=1+ cb|0,mc=zb.b();cb=xb;Ub=mc;jb=!0;continue}var Ha=er(zb).m(),Ka=ze(Ha,"","",""),Oa=Ub,Na=jb,Da=cb;db=Ka;ub=Oa;Aa=Na;va=Da}else{var ta=er(zb).m(),Ya=ze(ta,"","",""),dc=Ub,ka=jb,ya=cb;db=Ya;ub=dc;Aa=ka;va=ya}break}var Sa=db,xc=!!Aa,Sb=va|0;if(ub){var uc=a.Le;fr();var Lb=Ye(new Te(new Ue(J(new K,["Leading separator is not allowed"]))),u());t();var lc=gr(a,-1+Va|0,Va),Xb=G(new H,Lb,new L(lc)),ec=O().c;uc.n(hr(0,new z(Xb,ec),!0,ir()))}if(xc){var Ab=a.Le;fr();var Ob=Ye(new Te(new Ue(J(new K,["Trailing separator is not allowed"]))), u());t();var fb=gr(a,-1+Sb|0,Sb),Wa=G(new H,Ob,new L(fb)),bb=O().c;Ab.n(hr(0,new z(Wa,bb),!0,ir()))}var Ia=G(new H,""===Sa?t().d:(t(),new L(Sa)),Sb),Ua=Ia.y,pc=Ia.w|0;if(t().d===Ua){var sc=a.Le;jr();var Ba=new Te(new Ue(J(new K,["Expect at least one "," digit"]))),ob=[We(Xe(),"octal")],nc=Ye(Ba,J(new K,ob));t();var Ib=gr(a,Va,2+Va|0),vc=G(new H,nc,new L(Ib)),Vb=O().c;sc.n(kr(0,new z(vc,Vb),!0,ir()));return G(new H,ar(),pc)}var fc=Ia.y,Bc=Ia.w|0;if(fc instanceof L){var Pb=fc.k;return G(new H,new Em(Sr(Tr(), Pb,8)),Bc)}throw new w(Ia);case 98:for(var Jb=2+b|0,gc=Jb,Cb=O().c,cc=!1,yc=!1,Mc,qc,oc,Qc;;){if(gc=a.we)return Km(d);var g=a.$b.a[b],h=!1,k=!1;if(32===g){for(var l=b,m=O().c;;)if(lNm(new E(hc(Ea(kf))),hc(10))))),Bc=c.Jg();if((Bc.b()||(Bc.o()|0)fc)Cb=Cb.f();else break;var cc=Cb,yc=c.K()-cc.K()|0,Mc=cc.Jg(),qc=Mc.b()||(Mc.o()|0)=ac){for(var Wc=wc,Wd=O().c;;)if(Wc>>0)).toString(8):String.fromCharCode(a)}} function qaa(a){if(0===(2&a.Gs)<<24>>24&&0===(2&a.Gs)<<24>>24){0===(1&a.Gs)<<24>>24&&0===(1&a.Gs)<<24>>24&&(a.vN=Zaa(a),a.Gs=(1|a.Gs)<<24>>24);a:for(var b=a.vN,c=!1,d=O().c,e=O().c;;){var g=!1,h=null,k=b;if(k instanceof z){g=!0;h=k;var l=h.z,m=h.p;if(null!==l){var n=l.h(),r=l.j();if(Yr()===n&&m instanceof z){var v=m,x=v.z,A=v.p;if(null!==x){var B=x.h(),C=x.j();if(B instanceof bs){var D=B,F=D.ke;if("\x3c"===D.ae&&!0===F){var I=G(new H,new bs("\x3c",!0),C),M=G(new H,Yr(),r),N=new z(I,new z(M,e));b= A;c=!1;e=N;continue}}}}}}if(g){var P=h.z,T=h.p;if(null!==P){var Y=P.h(),Z=P.j();if(Yr()===Y&&T instanceof z){var S=T,ea=S.z,ia=S.p;if(null!==ea){var X=ea.h(),sa=ea.j();if(X instanceof bs){var Ja=X,Xa=Ja.ke;if("\x3e"===Ja.ae&&!0===Xa){var Fa=G(new H,new bs("\x3e",!0),sa),za=G(new H,Yr(),Z),Qa=new z(Fa,new z(za,e));b=ia;c=!1;e=Qa;continue}}}}}}if(g){var Ma=h.z,Ga=h.p;if(null!==Ma){var ab=Ma.h(),Hb=Ma.j();if(ab instanceof Zr){var bc=G(new H,ab.qx,Hb),yb=G(new H,bc,e),tb=new z(yb,d),eb=O().c;b=Ga;c=!1; d=tb;e=eb;continue}}}if(g){var kb=h.z,Rb=h.p;if(null!==kb){var Gb=kb.h(),vb=kb.j();if(Gb instanceof es){var Tb=Gb.Iw,Nb=!1,ic=null,Va=d;if(Va instanceof z){Nb=!0;ic=Va;var cb=ic.z;if(null!==cb){var zb=cb.h();if(null!==zb){var Ub=zb.h();if(Mk()===Ub&&Nm(new E(Tb),Mk())){var jb=new es(Mk()),db=vs(vb),ub=G(new H,jb,db);b=new z(ub,b);c=!1;continue}}}}if(Nb){var Aa=ic.z,va=ic.p;if(null!==Aa){var Ra=Aa.h(),rb=Aa.j();if(null!==Ra){var xb=Ra.h();if(Mk()===xb)if(Pe(new E(Tb),Mk()))b:{for(var mc=e;!mc.b();){var Ha= mc.e();c:{if(null!==Ha){var Ka=Ha.h();if(Se()===Ka||qs()===Ka){var Oa=!0;break c}}Oa=!1}if(!Oa){var Na=!1;break b}mc=mc.f()}Na=!0}else Na=!1;else Na=!1;if(Na){b=Rb;c=!1;d=va;e=rb;continue}}}}if(Nb){var Da=ic.z,ta=ic.p;if(null!==Da){var Ya=Da.h(),dc=Da.j();if(null!==Ya){var ka=Ya.h(),ya=Ya.j();if(Nm(new E(ka),Tb)&&(!Pe(new E(ka),Pk())||!Pe(new E(Tb),Jk()))){var Sa=a.Le;jr();var xc=new Te(new Ue(J(new K,["Mistmatched closing ",""]))),Sb=[We(Xe(),Tb.Ua())],uc=Ye(xc,J(new K,Sb));t();var Lb=G(new H,uc, new L(vb)),lc=new Te(new Ue(J(new K,["does not correspond to opening ",""]))),Xb=[We(Xe(),ka.Ua())],ec=Ye(lc,J(new K,Xb));t();var Ab=G(new H,ec,new L(ya)),Ob=O().c;Sa.n(kr(0,new z(Lb,new z(Ab,Ob)),!0,ws()))}var fb=Km(e),Wa=new cs(ka,fb,xs(new Kq(ya.eh,ya.eh,ya.dh),vs(vb))),bb=xs(ya,vb),Ia=G(new H,Wa,bb),Ua=new z(Ia,dc);b=Rb;c=!0;d=ta;e=Ua;continue}}}var pc=O().c;if(null===pc?null===Va:pc.i(Va)){var sc=a.Le;jr();var Ba=new Te(new Ue(J(new K,["Unexpected closing ",""]))),ob=[We(Xe(),Tb.Ua())],nc=Ye(Ba, J(new K,ob));t();var Ib=G(new H,nc,new L(vb)),vc=O().c;sc.n(kr(0,new z(Ib,vc),!0,ws()));b=Rb;c=!1;continue}throw new w(Va);}}}if(g){var Vb=h.z,fc=h.p;if(null!==Vb){var Bc=Vb.h(),Pb=Vb.j();if(ks()===Bc){var Jb=new Zr(Mk()),gc=G(new H,Jb,Pb);b=new z(gc,fc);c=!1;continue}}}if(g){var Cb=h.z,cc=h.p;if(null!==Cb){var yc=Cb.h(),Mc=Cb.j();if(rs()===yc){var qc=new es(Mk()),oc=G(new H,qc,Mc);b=new z(oc,cc);c=!1;continue}}}if(g){var Qc=h.z,jc=h.p;if(null!==Qc){var sb=Qc.h(),Gc=Qc.j();if(sb instanceof bs){var Wb= sb,Cc=Wb.ke;if("\x3c"===Wb.ae&&!0===Cc&&c){var Fc=new Zr(Lk()),qd=G(new H,Fc,Gc);b=new z(qd,jc);c=!1;continue}}}}if(g){var Yb=h.z,Nc=h.p;if(null!==Yb){var ad=Yb.h(),Uc=Yb.j();if(ad instanceof bs){var cd=ad,kc=cd.ke;if("\x3e"===cd.ae)if(!0===kc)if(c){var Vc=d;b:{if(Vc instanceof z){var Hc=Vc.z;if(null!==Hc){var rc=Hc.h();if(null!==rc){var sd=rc.h();if(Lk()===sd){var Kc=!0;break b}}}}Kc=!1}}else Kc=!1;else Kc=!1;else Kc=!1;if(Kc){var Qd=new es(Lk()),Ad=G(new H,Qd,Uc);b=new z(Ad,Nc);c=!1;continue}}}}if(g){var kd= h.z,Hd=h.p;if(null!==kd){var Rd=kd.h(),Bd=kd.j();if(Rd instanceof bs){var ae=Rd,dd=ae.ae;if(!0===ae.ke){if(c)b:{for(var od=0,Ta=dd.length;od>24}return a.uN}Je.prototype.$classData=q({LW:0},!1,"mlscript.NewLexer",{LW:1,g:1}); function zs(){this.GH=null;As=this;this.GH=Aq(tp().Iv,J(new K,"if then else case fun val var of let rec in mut set do while declare class trait mixin interface extends override super new namespace module type where forall exists in out null undefined abstract constructor virtual".split(" ")))}zs.prototype=new p;zs.prototype.constructor=zs; function aba(a,b){if(null!==b&&(a=b.h(),Se()===a))return" ";if(null!==b&&(a=b.h(),Xr()===a))return",";if(null!==b&&(a=b.h(),ds()===a))return";";if(null!==b&&(a=b.h(),qs()===a))return"\u21b5";if(null!==b&&(a=b.h(),ks()===a))return"\u2192";if(null!==b&&(a=b.h(),rs()===a))return"\u2190";if(null!==b&&(a=b.h(),ts()===a))return"\x3cerror\x3e";if(null!==b&&(a=b.h(),Yr()===a))return"`";if(null!==b&&(a=b.h(),a instanceof fs))return a.Cu.Dh;if(null!==b&&(a=b.h(),a instanceof as&&(a=a.Na,null!==a)))return"#"+ a;if(null!==b&&(a=b.h(),a instanceof bs&&(a=a.ae,null!==a)))return a;if(null!==b&&(a=b.h(),a instanceof ss&&(a=a.ux,null!==a)))return"."+a;if(null!==b&&(a=b.h(),a instanceof Zr))return Sk(a.qx);if(null!==b&&(a=b.h(),a instanceof es))return Tk(a.Iw);if(null!==b){var c=b.h();if(c instanceof cs&&(a=c.Cc,c=c.td,Mk()===a))return""+Sk(a)+Bs(0,c)+Tk(a)}if(null!==b&&(a=b.h(),a instanceof cs))return b=a.Cc,a=a.td,""+Sk(b)+Bs(0,a)+Tk(b);if(null!==b&&(a=b.h(),a instanceof gs&&(a=a.PC,null!==a)))return"/*"+a+ "*/";throw new w(b);}function Bs(a,b){a=b.m();a=new Ef(a,new y(c=>aba($r(),c)));return ze(a,"|","|","|")}zs.prototype.$classData=q({MW:0},!1,"mlscript.NewLexer$",{MW:1,g:1});var As;function $r(){As||(As=new zs);return As}function Cs(a,b,c,d,e){Ds(a,new U(()=>{var h=e.fn,k=Es(b);k=Fs(k)?ze(k,"(",",",")"):Gs(k)?Es(b):"("+Es(b)+")";return"@ "+h+k+" [at l."+d.Zl+"]"}));try{a.le=1+a.le|0;var g=c.n(void 0)}finally{a.le=-1+a.le|0}Ds(a,new U(()=>"\x3d "+g));return g} function Hs(a){if(!a.Kz){if(!a.Kz){var b=Is(a.Lz);b.b()?b=R():(b=b.o().j(),b=new L(new Kq(b.eh,b.eh,b.dh)));a.lE=b.b()?a.kE:b;a.Kz=!0}a.Lz=null}return a.lE}function Js(a){a=a.Ol.Jg();if(a.b())return R();a=a.o();return new L(a.j())}function Ks(a,b){var c=Ye(new Te(new Ue(J(new K,["Expected an expression; found a 'then'/'else' clause instead"]))),u());b=G(new H,c,b);c=O().c;Ze(a,new z(b,c));return Ls(a)} function Ms(a,b){var c=Ye(new Te(new Ue(J(new K,["This quote syntax is not supported yet"]))),u());b=G(new H,c,b);c=O().c;Ze(a,new z(b,c));return Ls(a)} function Ns(a,b,c,d,e){var g=bba(a,O().c,d);if(b){var h=!1,k=null,l=Os(a);a:{if(l instanceof z){h=!0;k=l;var m=k.z,n=k.p;if(null!==m&&(m=m.h(),Yr()===m&&n instanceof z&&(n=n.z,null!==n&&(n=n.h(),n instanceof as&&"in"===n.Na)))){Ps(a,e,new Oe("body"));Ps(a,e,new Oe("body"));c=Qs(a,0,!0,O().c,c,d,e);break a}}if(h&&(n=k.z,null!==n&&(n=n.h(),qs()===n))){Ps(a,e,new Oe("body"));l=Rs(a,c,d);c=r=>{if(r instanceof fe)return Ks(a,r.aa.A());if(r instanceof Ud)return r.fa;throw new w(r);};if(l===u())c=u();else{d= l.e();e=d=new z(c(d),u());for(l=l.f();l!==u();)h=l.e(),h=new z(c(h),u()),e=e.p=h,l=l.f();c=d}t();c=new Sl(c);c=new Ud(c);break a}if(h&&(c=k.z,null!==c)){d=c.h();c=c.j();e=new Te(new Ue(J(new K,["Expected '`in'; found "," instead"])));d=[We(Xe(),d.jb())];d=Ye(e,J(new K,d));t();c=G(new H,d,new L(c));d=O().c;Ze(a,new z(c,d));t();c=Ls(a);c=new Ud(c);break a}c=O().c;if(null===c?null===l:c.i(l))c=Ye(new Te(new Ue(J(new K,["Expected '`in'; found end of input instead"]))),u()),d=Hs(a),c=G(new H,c,d),d=O().c, Ze(a,new z(c,d)),t(),c=Ls(a),c=new Ud(c);else throw new w(l);}}else a:{if(l=!1,h=null,k=Os(a),k instanceof z&&(l=!0,h=k,k=h.z,null!==k&&(k=k.h(),k=k instanceof as&&"in"===k.Na?!0:ds()===k?!0:!1,k))){Ps(a,e,new Oe("body"));c=Qs(a,0,!0,O().c,c,d,e);break a}if(l&&(l=h.z,null!==l&&(l=l.h(),qs()===l))){Ps(a,e,new Oe("body"));c=Qs(a,0,!0,O().c,c,d,e);break a}t();c=new Gm(!0);d=Js(a);d.b()?d=R():(d=d.o(),d=new L(vs(d)));c=Cq(c,d);c=new Ud(c)}for(g=Km(g);!g.b();){d=g.e();c=G(new H,d,c);a:{l=c.y;e=c.w;if(null!== l&&(d=l.h(),l=l.j(),e instanceof Ud&&(e=e.fa,b))){t();c=new em(new Rl(!1,d,new fm(l),new fm(e)));c=new Ud(c);break a}l=c.y;e=c.w;if(null!==l&&(d=l.h(),l=l.j(),e instanceof Ud)){c=e.fa;t();c=new Rl(!1,d,l,c);c=new Ud(c);break a}d=c.w;if(null!==c.y&&d instanceof fe&&(d=d.aa,b)){t();c=Ms(a,d.A());c=new Ud(c);break a}l=c.y;e=c.w;if(null!==l&&(d=l.h(),l=l.j(),e instanceof fe)){c=e.aa;t();c=new Ss(!1,d,l,c);c=new fe(c);break a}throw new w(c);}g=g.f()}return c} function Ts(a,b){for(b=Km(b);!b.b();){var c=b.e();a=new Ml(c,a);b=b.f()}return a}function Ls(a){var b=new Gm(!0);a=Hs(a);return Cq(b,a)}var dba=function cba(a,b){var d=Os(a);return d instanceof z&&(d=d.z,null!==d&&(d=d.h(),Xr()===d))?(Ps(a,new Ne(382),new Oe("otherParents")),d=Us(a,Vs().ao.n(hc(44))|0,b,new Ne(383)),a=cba(a,b),new z(d,a)):O().c}; function Ws(a,b){for(var c=O().c;;){var d=!1,e=null,g=Me(a,new Ne(579),new Oe("rec"));if(g instanceof z&&(d=!0,e=g,g=e.z,null!==g&&(g=g.h(),Se()===g))){Ps(a,new Ne(581),new Oe("rec"));continue}if(d&&(g=e.z,null!==g&&(g=g.h(),qs()===g&&b))){Ps(a,new Ne(584),new Oe("rec"));continue}if(d&&(g=e.z,d=e.p,null!==g)){var h=g.h();e=g.j();if(h instanceof bs&&(g=h,h=g.ke,"@"===g.ae&&!0===h)){Ps(a,new Ne(587),new Oe("rec"));a:{if(d instanceof z&&(g=d.z,null!==g&&(h=g.h(),g=g.j(),h instanceof bs))){var k=h.ae; if(!1===h.ke){d=k;e=g;break a}}d=d.Jg();d.b()?(d="end of input",h=Hs(a)):(g=d.o(),t(),t(),d=g.h().jb(),h=g=it().n(g.j()));g=d;d=h;h=new Te(new Ue(J(new K,["Expected an identifier; found "," instead"])));g=[We(Xe(),g)];g=Ye(h,J(new K,g));d=G(new H,g,d);g=O().c;Ze(a,new z(d,g));d="\x3cerror\x3e"}Ps(a,new Ne(595),new Oe("rec"));d=new vl(d);t();e=Cq(d,new L(e));c=new z(e,c);continue}}return Km(c)}} var fba=function eba(a){var c=Os(a);if(c instanceof z){var d=c.z;if(null!==d&&(c=d.h(),d=d.j(),c instanceof bs)){var e=c.ae;if(!1===c.ke){Ps(a,new Ne(832),new Oe("getIdents"));c=new jt((t(),new Ud(e)),t().d);t();c=Cq(c,new L(d));d=Os(a);if(d instanceof z&&(d=d.z,null!==d&&(d=d.h(),Xr()===d)))return Ps(a,new Ne(836),new Oe("getIdents")),a=eba(a),new z(c,a);a=O().c;return new z(c,a)}}}return O().c}; function kt(a,b,c){if(a.b())return G(new H,b,c);c instanceof fe&&no();if(c instanceof Ud){var d=c.fa;if(null!==d)return c=d.yb,d=d.ya,t(),a=new sm(c,new Sl(Km(new z(d,a)))),G(new H,b,new Ud(a))}throw new w(c);}function lt(a,b,c,d,e,g,h,k){a.mE=b;a.Lz=c;a.hx=d;a.Fu=e;a.Eu=g;a.kE=h;a.jE=k;a.fx=0;a.le=0;a.Ol=c;a.gx=mt(a).KH}function nt(){this.Lz=this.mE=this.dx=this.lE=this.ex=null;this.hx=!1;this.Fu=null;this.Eu=!1;this.jE=this.kE=null;this.le=this.fx=0;this.gx=this.Ol=null;this.Kz=!1} nt.prototype=new p;nt.prototype.constructor=nt;function ot(){}ot.prototype=nt.prototype;function pt(a){null===a.ex&&null===a.ex&&(a.ex=new qt(a));return a.ex}function mt(a){null===a.dx&&null===a.dx&&(a.dx=new rt(a));return a.dx}function gba(a){var b=new vl("_$"+a.fx);a.fx=1+a.fx|0;return b}function st(a,b,c){tt(Vs(),c)||a.Fu.n(Es(b))}function Ze(a,b){tt(Vs(),!1)||a.Fu.n(kr(jr(),b,!0,ws()))}function Ds(a,b){a.nQ(new U(()=>""+ut(Q(),"\u2502 ",a.le)+Es(b)))} function vt(a,b){var c=b.n(a);for(b=Me(a,new Ne(147),new Oe("concludeWith"));;){if(b.b())d=!1;else if(d=b.e(),Pe(new E(d.h()),Se())||Pe(new E(d.h()),qs())){Ps(a,new Ne(147),new Oe("concludeWith"));var d=!0}else d=!1;if(d)b=b.f();else break}var e=b;a:{if(e instanceof z&&(d=e.z,null!==d)){b=d.h();for(d=d.j();;){if(e.b())g=!1;else{g=e.e().h();var g=Pe(new E(g),Se())}if(g)e=e.f();else break}e=e.Jg();b=e.b()?G(new H,b,d):e.o();if(null===b)throw new w(b);d=b.h();b=b.j();e=new Te(new Ue(J(new K,["Unexpected ", " here"])));d=[We(Xe(),d.jb())];d=Ye(e,J(new K,d));t();b=G(new H,d,new L(b));d=O().c;Ze(a,new z(b,d));break a}b=O().c;if(null===b?null!==e:!b.i(e))throw new w(e);}Ds(a,new U(()=>"Concluded with "+c));return c}function wt(a,b){a.Ol=b;a.gx=mt(a).KH}function Me(a,b,c){for(a.Eu&&Ds(a,new U(()=>{var d="? "+c.fn+"\t\tinspects ";var e=Bs($r(),xt(a.Ol,5))+(0"! "+c.fn+"\t\tconsumes "+Bs($r(),xt(a.Ol,1))+" [at l."+b.Zl+"]"));var d=hba(new yt(a.Ol));wt(a,d.b()?O().c:d.o())} function zt(a,b,c,d,e,g){var h=tc();try{var k=new U(()=>new tl(b,c,d)),l=new Ne(188),m=new Oe("skip");Ds(a,new U(()=>{var ia=m.fn,X=Es(k);X=Fs(X)?ze(X,"(",",",")"):Gs(X)?Es(k):"("+Es(k)+")";return"@ "+ia+X+" [at l."+l.Zl+"]"}));try{a.le=1+a.le|0;At(tp(),!c.L(b));var n=Me(a,new Ne(190),new Oe("skip_res"));a:{if(n instanceof z){var r=n.z;if(null!==r){var v=r.h(),x=r.j();if(c.L(v))throw Ps(a,new Ne(193),new Oe("skip_res")),Hq(new Iq,h,zt(a,b,c,d,e,g));if(Nm(new E(v),b)){if(!tt(Vs(),g)){var A=new Te(new Ue(J(new K, ["Expected ","; found "," instead"]))),B=[We(Xe(),b.jb()),We(Xe(),v.jb())],C=Ye(A,J(new K,B));t();var D=G(new H,C,new L(x)),F=Es(e);Ze(a,new z(D,F))}var I=G(new H,!1,(t(),new L(x)))}else I=G(new H,!0,(t(),new L(x)));break a}}var M=O().c;if(null===M?null===n:M.i(n)){if(!d&&!tt(Vs(),g)){var N=new Te(new Ue(J(new K,["Expected ","; found end of input instead"]))),P=[We(Xe(),b.jb())],T=Ye(N,J(new K,P)),Y=Hs(a),Z=G(new H,T,Y),S=Es(e);Ze(a,new z(Z,S))}I=G(new H,d,t().d)}else throw new w(n);}Ps(a,new Ne(206), new Oe("skip"));var ea=I}finally{a.le=-1+a.le|0}Ds(a,new U(()=>"\x3d "+ea));return ea}catch(ia){if(ia instanceof Iq){a=ia;if(a.Qg===h)return a.Cj();throw a;}throw ia;}}function Bt(){tp();var a=[Se()];a=J(new K,a);return Aq(0,a)} function Le(a){var b=Rs(a,!1,!1),c=h=>{var k=!1,l=null;if(h instanceof fe)return l=h.aa,k=Ye(new Te(new Ue(J(new K,["Unexpected 'then'/'else' clause"]))),u()),l=l.A(),k=G(new H,k,l),l=O().c,Ze(a,new z(k,l)),Ls(a);if(h instanceof Ud&&(k=!0,l=h,h=l.fa,h instanceof Ct)||k&&(h=l.fa,h instanceof Pm))return h;if(k&&(k=l.fa,k instanceof Po))return k;no()};if(b===u())c=u();else{var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}c=d}return new Dt(c)} function Et(a){var b=Os(a);if(b instanceof z&&(b=b.z,null!==b)){var c=b.h();if(c instanceof cs){var d=c.Cc;b=c.td;if(Mk()===d){Ps(a,new Ne(243),new Oe("typingUnitMaybeIndented"));t();d=new L(c.Mf);c=c.jb();a=new Ft(a,b,d,c);var e=Le(a);for(b=Me(a,new Ne(147),new Oe("concludeWith"));;)if(b.b()?c=!1:(c=b.e(),Pe(new E(c.h()),Se())||Pe(new E(c.h()),qs())?(Ps(a,new Ne(147),new Oe("concludeWith")),c=!0):c=!1),c)b=b.f();else break;d=b;a:{if(d instanceof z&&(c=d.z,null!==c)){b=c.h();for(c=c.j();;){if(d.b())g= !1;else{g=d.e().h();var g=Pe(new E(g),Se())}if(g)d=d.f();else break}d=d.Jg();b=d.b()?G(new H,b,c):d.o();if(null===b)throw new w(b);c=b.h();b=b.j();d=new Te(new Ue(J(new K,["Unexpected "," here"])));c=[We(Xe(),c.jb())];c=Ye(d,J(new K,c));t();b=G(new H,c,new L(b));c=O().c;Ze(a,new z(b,c));break a}b=O().c;if(null===b?null!==d:!b.i(d))throw new w(d);}Ds(a,new U(()=>"Concluded with "+e));return e}}}return Le(a)} function Gt(a){var b=Os(a);if(b instanceof z&&(b=b.z,null!==b)){var c=b.h();b=b.j();if(c instanceof cs){var d=c.Cc,e=c.td;if(Jk()===d){Ps(a,new Ne(249),new Oe("curlyTypingUnit"));t();t();d=new L(c.Mf);c=c.jb();a=new Ft(a,e,d,c);var g=Et(a);for(c=Me(a,new Ne(147),new Oe("concludeWith"));;)if(c.b()?e=!1:(e=c.e(),Pe(new E(e.h()),Se())||Pe(new E(e.h()),qs())?(Ps(a,new Ne(147),new Oe("concludeWith")),e=!0):e=!1),e)c=c.f();else break;d=c;a:{if(d instanceof z&&(e=d.z,null!==e)){c=e.h();for(e=e.j();;){if(d.b())h= !1;else{h=d.e().h();var h=Pe(new E(h),Se())}if(h)d=d.f();else break}d=d.Jg();c=d.b()?G(new H,c,e):d.o();if(null===c)throw new w(c);e=c.h();c=c.j();d=new Te(new Ue(J(new K,["Unexpected "," here"])));e=[We(Xe(),e.jb())];e=Ye(d,J(new K,e));t();c=G(new H,e,new L(c));e=O().c;Ze(a,new z(c,e));break a}c=O().c;if(null===c?null!==d:!c.i(d))throw new w(d);}Ds(a,new U(()=>"Concluded with "+g));t();b=Cq(g,new L(b));return new L(b)}}}return t().d}function Ht(a,b,c){return It(a,Us(a,0,b,c))} function Rs(a,b,c){for(;;){var d=Ws(a,!0),e=!1,g=null,h=Me(a,new Ne(312),new Oe("block")),k=O().c;if(null===k?null===h:k.i(h))return O().c;if(h instanceof z){e=!0;g=h;var l=g.z;if(null!==l){var m=l.h();if(qs()===m){Ps(a,new Ne(314),new Oe("block"));continue}}}if(e){var n=g.z;if(null!==n){var r=n.h();if(Se()===r){Ps(a,new Ne(315),new Oe("block"));continue}}}if(e){var v=g.z;if(null!==v){var x=v.h(),A=v.j();if(x instanceof as&&"constructor"===x.Na){Ps(a,new Ne(317),new Oe("block"));var B=Os(a);a:{if(B instanceof z){var C=B.z;if(null!==C){var D=C.h(),F=C.j();if(D instanceof cs){var I=D,M=I.Cc,N=I.td;if(Ik()===M){Ps(a,new Ne(320),new Oe("res"));t();var P=new L(I.Mf),T=I.jb(),Y=vt(new Ft(a,N,P,T),new y(((Yd,bf)=>rf=>Jt(rf,Yd,bf))(c,b))),Z=Gt(a),S=new U(()=>new Dt(O().c)),ea=Z.b()?Es(S):Z.o(),ia=new Gl(Y);t();var X=Cq(ia,new L(F)),sa=new Sl(ef(ea)),Ja=ea.A();var Xa=new Po(X,Cq(sa,Ja));break a}}}}var Fa=Ye(new Te(new Ue(J(new K,["Expect parameter list for the constructor"]))),u());t();var za=G(new H,Fa,new L(A)), Qa=O().c;Ze(a,new z(za,Qa));Xa=new Po(new Gl(O().c),new Sl(O().c))}t();t();var Ma=Kt(A,Dq(Xa)),Ga=Cq(Xa,new L(Ma)),ab=new Ud(Ga),Hb=Rs(a,b,c);return new z(ab,Hb)}}}a:{if(null!==h){var bc=mt(a).HC(h);if(!bc.b()){var yb=bc.k.h(),tb=bc.k.j();if(tb instanceof z){var eb=tb.z;if(null!==eb){var kb=eb.h(),Rb=eb.j();if(kb instanceof as){var Gb=kb.Na;if("class"===Gb||"infce"===Gb||"trait"===Gb||"mixin"===Gb||"type"===Gb||"module"===Gb){Ps(a,new Ne(332),new Oe("t"));var vb=Lt(yb,"declare");if(null!==vb)var Tb= G(new H,vb.h(),vb.j());else throw new w(vb);var Nb=Tb.h(),ic=Lt(Tb.j(),"abstract");if(null!==ic)var Va=G(new H,ic.h(),ic.j());else throw new w(ic);var cb=Va.h();Mt(Va.j());switch(Gb){case "class":var zb=Bp();break;case "trait":zb=Fp();break;case "mixin":zb=cp();break;case "type":zb=Ap();break;case "module":zb=zp();break;default:xm("Program reached and unexpected state.")}var Ub=Os(a);b:{if(Ub instanceof z){var jb=Ub.z;if(null!==jb){var db=jb.h(),ub=jb.j();if(db instanceof bs){var Aa=db.ae;Ps(a,new Ne(346), new Oe("x$19"));var va=new Ep(Aa);t();var Ra=G(new H,Cq(va,new L(ub)),!0);break b}}}var rb=Ub.Jg(),xb=new U(()=>G(new H,"end of input",Hs(a))),mc=new y(Yd=>{t();t();Yd=G(new H,(new y(bf=>bf.jb())).n(Yd.h()),Yd.j());return G(new H,Yd.y,it().n(Yd.w))}),Ha=rb.b()?Es(xb):mc.n(rb.o());if(null!==Ha)var Ka=G(new H,Ha.h(),Ha.j());else throw new w(Ha);var Oa=Ka.h(),Na=Ka.j(),Da=new Te(new Ue(J(new K,["Expected a type name; found "," instead"]))),ta=[We(Xe(),Oa)],Ya=Ye(Da,J(new K,ta)),dc=G(new H,Ya,Na),ka= O().c;Ze(a,new z(dc,ka));Ps(a,new Ne(352),new Oe("x$19"));var ya=new Ep("\x3cerror\x3e"),Sa=Js(a),xc=new y(Yd=>vs(Yd)),Sb=Sa.b()?R():new L(xc.n(Sa.o()));Ra=G(new H,Cq(ya,Sb),!1)}if(null!==Ra)var uc=G(new H,Ra.h(),Ra.cy());else throw new w(Ra);var Lb=uc.h(),lc=Os(a);b:{if(lc instanceof z){var Xb=lc.z;if(null!==Xb){var ec=Xb.h();if(ec instanceof cs){var Ab=ec,Ob=Ab.Cc,fb=Ab.td;if(Lk()===Ob||Kk()===Ob){Ps(a,new Ne(358),new Oe("tparams"));t();var Wa=new L(Ab.Mf),bb=Ab.jb();var Ia=vt(new Ft(a,fb,Wa,bb), new y(((Yd,bf)=>rf=>iba(rf,new fn(Cg=>Nt(Cg,Yd,bf))))(c,b)));break b}}}}Ia=O().c}var Ua=Os(a);b:{if(Ua instanceof z){var pc=Ua.z;if(null!==pc){var sc=pc.h(),Ba=pc.j();if(sc instanceof cs){var ob=sc,nc=ob.Cc,Ib=ob.td;if(Ik()===nc){Ps(a,new Ne(375),new Oe("params"));t();var vc=new L(ob.Mf),Vb=ob.jb(),fc=vt(new Ft(a,Ib,vc,Vb),new y(((Yd,bf)=>rf=>Jt(rf,Yd,bf))(c,b)));t();var Bc=new Gl(fc);t();var Pb=Cq(Bc,new L(Ba));var Jb=new L(Pb);break b}}}}Jb=t().d}var gc=!1,Cb=null,cc=Os(a);b:{if(cc instanceof z){gc= !0;Cb=cc;var yc=Cb.z;if(null!==yc){var Mc=yc.h();if(Mc instanceof as&&"\x3d"===Mc.Na&&Ot(new E(zb),Ap())){Ps(a,new Ne(388),new Oe("sigTrm"));t();var qc=Us(a,0,c,new Ne(389));var oc=new L(qc);break b}}}if(gc){var Qc=Cb.z;if(null!==Qc){var jc=Qc.h();if(jc instanceof as&&":"===jc.Na&&!Ot(new E(zb),Ap())){Ps(a,new Ne(391),new Oe("sigTrm"));t();var sb=Us(a,0,c,new Ne(392));oc=new L(sb);break b}}}oc=t().d}var Gc=Os(a);b:{if(Gc instanceof z){var Wb=Gc.z;if(null!==Wb){var Cc=Wb.h();if(Cc instanceof as&&"extends"=== Cc.Na){Ps(a,new Ne(400),new Oe("ps"));var Fc=Us(a,Vs().ao.n(hc(44))|0,c,new Ne(401)),qd=dba(a,c);var Yb=new z(Fc,qd);break b}}}Yb=O().c}var Nc=Gt(a),ad=new U(((Yd,bf)=>()=>{var rf=Is(Yd);if(rf instanceof L){var Cg=rf.k;if(Cg instanceof cm){rf=Cg.Ys;Cg=Cg.Zs;var nj=Km(Yd).f();return new tl(bf,Km(new z(rf,nj)),Cg)}}return bf instanceof L&&(Cg=bf.k,Cg instanceof cm)?(rf=Cg.Ys,Cg=Cg.Zs,new tl((t(),new L(rf)),Yd,Cg)):new tl(bf,Yd,new Dt(O().c))})(Yb,oc)),Uc=new y(((Yd,bf)=>rf=>new tl(Yd,bf,rf))(oc,Yb)), cd=Nc.b()?Es(ad):Uc.n(Nc.o());if(null!==cd)var kc=new tl(cd.kc,cd.hb,cd.Rd);else throw new w(cd);var Vc=kc.kc,Hc=kc.hb,rc=kc.Rd,sd=new y(Yd=>It(a,Yd)),Kc=Vc.b()?R():new L(sd.n(Vc.o())),Qd=ef(rc),Ad=Pt(Qd,new y(Yd=>{if(Yd instanceof Po)return t(),new fe(Yd);t();return new Ud(Yd)}));if(null!==Ad)var kd=G(new H,Ad.h(),Ad.j());else throw new w(Ad);var Hd=kd.h(),Rd=new Dt(kd.j()),Bd=rc.A(),ae=Cq(Rd,Bd);if(0Pe(new E(Yd.h()),Se())));c:{var vg=O().c;if(null===vg?null!==df: !vg.i(df)){if(df instanceof z){var wg=df.z;if(null!==wg){var xg=wg.h(),eg=wg.j(),vh=new Te(new Ue(J(new K,["Unexpected "," after symbolic name"]))),fg=[We(Xe(),xg.jb())],ih=Ye(vh,J(new K,fg));t();var Ig=G(new H,ih,new L(eg)),Tf=O().c;Ze(a,new z(Ig,Tf));break c}}throw new w(df);}}t();var Jg=new vl(Jf);t();var jh=Cq(Jg,new L(te));var yg=new L(jh);break b}}}}if(Jd){var gg=Dd.z;if(null!==gg){var Cf=gg.h(),Uf=gg.j();Ps(a,new Ne(456),new Oe("opStr"));var $g=new Te(new Ue(J(new K,["Expected a symbolic name, found ", " instead"]))),Ah=[We(Xe(),Cf.jb())],Kg=Ye($g,J(new K,Ah));t();var Vf=G(new H,Kg,new L(Uf)),hg=O().c;Ze(a,new z(Vf,hg));yg=t().d;break b}}var zg=O().c;if(null===zg?null===ld:zg.i(ld)){Ps(a,new Ne(460),new Oe("opStr"));var Lg=Ye(new Te(new Ue(J(new K,["Expected a symbolic name between brackets, found nothing"]))),u());t();var Mg=G(new H,Lg,new L(kf)),Wf=O().c;Ze(a,new z(Mg,Wf));yg=t().d;break b}throw new w(ld);}}}}yg=t().d}var Ng=Os(a);b:{if(Ng instanceof z){var Kf=Ng.z;if(null!==Kf){var xf=Kf.h(), Og=Kf.j();if(xf instanceof bs){var mi=xf,Ci=mi.ae;if(!1===mi.ke){Ps(a,new Ne(468),new Oe("x$34"));var Xh=new vl(Ci);t();var wh=G(new H,Cq(Xh,new L(Og)),!0);break b}}}}var Bh=Ng.Jg(),ng=new U(()=>G(new H,"end of input",Hs(a))),kh=new y(Yd=>{t();t();Yd=G(new H,(new y(bf=>bf.jb())).n(Yd.h()),Yd.j());return G(new H,Yd.y,it().n(Yd.w))}),Kh=Bh.b()?Es(ng):kh.n(Bh.o());if(null!==Kh)var ni=G(new H,Kh.h(),Kh.j());else throw new w(Kh);var Lh=ni.h(),lh=ni.j(),Ch=new Te(new Ue(J(new K,["Expected a function name; found ", " instead"]))),Dh=[We(Xe(),Lh)],Yh=Ye(Ch,J(new K,Dh)),ah=G(new H,Yh,lh),oi=O().c;Ze(a,new z(ah,oi));Ps(a,new Ne(473),new Oe("x$34"));var mj=new vl("\x3cerror\x3e"),wd=Js(a),ge=new y(Yd=>vs(Yd)),De=wd.b()?R():new L(ge.n(wd.o()));wh=G(new H,Cq(mj,De),!1)}if(null!==wh)var qf=G(new H,wh.h(),wh.cy());else throw new w(wh);var og=qf.h(),Xf=qf.cy(),mh=tt(Vs(),c)||!Xf;wc=(new y(((Yd,bf,rf,Cg,nj,Jh,If,Hg,He,lj)=>Wi=>{var Oj=!!Wi;if("let"===Yd){t();var mo=u(),mm=Pd(u(),mo)}else{var nm=Os(a);a:{if(nm instanceof z){var dq=nm.z;if(null!==dq){var Zd=dq.h();if(Zd instanceof cs){var sf=Zd.Cc,oj=Zd.td;if(Lk()===sf||Kk()===sf){Ps(a,new Ne(480),new Oe("tparams"));t();var al=new L(Zd.Mf),Ll=Zd.jb();mm=Qt(vt(new Ft(a,oj,al,Ll),new y(Ca=>Jt(Ca,Oj,Oj))),new y(Ca=>{if(null!==Ca){var Lc=Ca.h();Ca=Ca.j();if(t().d===Lc&&null!==Ca&&(Lc=Ca.yb,Ca=Ca.ya,null!==Lc)){var yd=Lc.ch;if(!1===Lc.je&&!1===yd&&Ca instanceof vl)return Lc=new Ep(Ca.x),Ca=Ca.A(),Cq(Lc,Ca)}}no()}));break a}}}}mm=O().c}}var Qm=Os(a);a:{if(Qm instanceof z){var Rm= Qm.z,hq=Qm.p;if(null!==Rm){var Bn=Rm.h(),hp=Rm.j();if(Bn instanceof cs){var ru=Bn.Cc,qr=Bn.td;if(Ik()===ru&&null!==qr){var Xs=pt(a).HC(qr);if(!Xs.b()){var rr=Xs.k.j();if(rr instanceof z){var iq=rr.z,qo=rr.p;if(null!==iq){var qm=iq.h(),jq=iq.j();if(qm instanceof as&&"override"===qm.Na){var pl=new cs(Ik(),qo,Bn.Mf),ro=G(new H,pl,hp);wt(a,new z(ro,hq));var Cn=Rt(a,Oj,Oj,new Ne(492));if(Cn instanceof z){var ip=Cn.z,so=Cn.p;if(null!==ip){var Dn=ip.Ra;if(Dn instanceof z){var sr=Dn.z,kq=Dn.p;if(null!==sr){var ql= new L(sr);if(!ql.b()){var Ys=ql.k.h(),Sm=ql.k.j();if(t().d===Ys&&null!==Sm){var Nl=Sm.yb,jp=Sm.ya;if(null!==Nl){var lq=Nl.ch,mq=Nl.Bh;if(!1===Nl.je)if(!1===lq){var Tm=O().c;if(null===Tm?null===kq:Tm.i(kq))var En=O().c,to=null===En?null===so:En.i(so);else to=!1}else to=!1;else to=!1;if(to){var Fn=gba(a),nq=t().d,Um=new sm(new St(!1,!1,mq),Fn),kp=G(new H,nq,Um),oq=O().c,su=new Gl(new z(kp,oq)),Gn=O().c,ur=new z(su,Gn);t();var In=G(new H,ur,new L(new y(Ca=>{Ca=new Tt(Fn,new vl("is"),new Ut(jp,Ca));t(); var Lc=new am;t();Lc=new Ql(Cq(Lc,new L(jq)),bf);var yd=t().d,Qe=new sm(new St(!1,!1,mq),Fn);yd=G(new H,yd,Qe);Qe=O().c;Lc=new Pl(Lc,new Gl(new z(yd,Qe)));return new Xl(Ca,new L(Lc))})));break a}}}}}}}}var Zs=Ye(new Te(new Ue(J(new K,["Unsupported 'override' parameter list shape"]))),u());t();var $s=G(new H,Zs,new L(Bn.Mf)),pq=O().c;Ze(a,new z($s,pq));In=G(new H,Cn,t().d);break a}}}}}}}}In=G(new H,Rt(a,Oj,Oj,new Ne(505)),t().d)}if(null!==In)var vr=G(new H,In.h(),In.j());else throw new w(In);var Vm= vr.h(),Jn=vr.j(),wr=Os(a);a:{if(wr instanceof z){var at=wr.z;if(null!==at){var xr=at.h();if(xr instanceof as&&":"===xr.Na){Ps(a,new Ne(509),new Oe("asc"));t();var lp=Ht(a,Oj,new Ne(511));var Kn=new L(lp);break a}}}Kn=t().d}var qq=Os(a);if(qq instanceof z){var yr=qq.z;if(null!==yr){var rq=yr.h();if(rq instanceof as&&"\x3d"===rq.Na){Ps(a,new Ne(516),new Oe("t"));var sq=Us(a,0,Oj,new Ne(517)),bt=new U(()=>sq),tq=new y(Ca=>Ca.n(sq)),zr=Jn.b()?Es(bt):tq.n(Jn.o()),ct=new U(()=>zr),Ar=new y(Ca=>new Cl(zr, Ca)),uq=Kn.b()?Es(ct):Ar.n(Kn.o()),Br=Os(a);if(Br instanceof z){var Ln=Br.z;if(null!==Ln){var vq=Ln.h(),Cr=Ln.j();if(vq instanceof as&&"in"===vq.Na&&"let"===Yd){Ps(a,new Ne(522),new Oe("t"));if(!mm.b()){var tu=Ye(new Te(new Ue(J(new K,["Unsupported type parameters on 'let' binding"]))),u());t();var uu=G(new H,tu,new L(Cr)),dt=O().c;Ze(a,new z(uu,dt))}var vu=Us(a,0,Oj,new Ne(524));t();var Dr=new U(()=>{xm("Program reached and unexpected state.")}),uo=new Rl(!(rf.b()?!Es(Dr):!rf.o()),bf,sq,vu);t(); var Er=Kt(Cg,uq.A()),et=Cq(uo,new L(Er));return new Ud(et)}}}t();t();var ft=Vm.mf(uq,new fn((Ca,Lc)=>new Ol(Ca,Lc))),gt=new Zn(rf,bf,nj,mm,new fe(ft),Jh,If,Hg,t().d,t().d,He,lj);t();var Wm=Kt(Cg,uq.A()),Fr=Cq(gt,new L(Wm));return new Ud(Fr)}}}if(Kn instanceof L){var mp=Kn.k;Jn.b()||xm("Program reached and unexpected state.");t();t();var wu=new Vt(O().c,Vm.mf(mp,new fn((Ca,Lc)=>{Ca=Qn(Ca);if(Ca instanceof fe){var yd=Ca.aa;st(a,new U(()=>yd),Oj);Ca=gl()}else if(Ca instanceof Ud)Ca=Ca.fa;else throw new w(Ca); return new Wt(Ca,Lc)}))),ht=new Zn(rf,bf,nj,mm,new Ud(wu),Jh,If,Hg,t().d,t().d,He,lj);t();var wq=Kt(Cg,mp.A()),xq=Cq(ht,new L(wq));return new Ud(xq)}if(t().d===Kn){var Gr=qq.Jg(),xu=new U(()=>G(new H,"end of input",Hs(a))),yu=new y(Ca=>{t();t();Ca=G(new H,(new y(Lc=>Lc.jb())).n(Ca.h()),Ca.j());return G(new H,Ca.y,it().n(Ca.w))}),Re=Gr.b()?Es(xu):yu.n(Gr.o());if(null!==Re)var rj=G(new H,Re.h(),Re.j());else throw new w(Re);var ai=rj.h(),rm=rj.j(),Nn=new Te(new Ue(J(new K,["Expected ':' or '\x3d' followed by a function body or signature; found ", " instead"]))),zu=[We(Xe(),ai)],Au=Ye(Nn,J(new K,zu)),Av=G(new H,Au,rm),oy=O().c;Ze(a,new z(Av,oy));Ps(a,new Ne(545),new Oe("t"));var Bv=Ls(a);t();var Cv=O().c;t();var py=Vm.mf(Bv,new fn((Ca,Lc)=>new Ol(Ca,Lc))),qy=new Zn(rf,bf,nj,Cv,new fe(py),Jh,If,Hg,t().d,t().d,He,lj);t();var Dv=Kt(Cg,Bv.A()),Ee=Cq(qy,new L(Dv));return new Ud(Ee)}throw new w(Kn);})(bd,og,ye,pe,yg,Wd,Db,Sd,Jc,d))).n(mh);break a}}}}}}wc=Qs(a,0,!1,d,b,c,new Ne(554))}var Ag=Os(a);a:{if(Ag instanceof z){var Bg=Ag.z;if(null!==Bg){var Eh= Bg.h();if(Eh instanceof as&&"\x3d"===Eh.Na){var Pg=!1,Di=null;if(wc instanceof Ud){Pg=!0;Di=wc;var Mh=Di.fa;if(Mh instanceof vl){var pi=Mh;Ps(a,new Ne(559),new Oe("finalTerm"));t();var Xi=new bm(pi,Us(a,0,c,new Ne(560)));var Qg=new Ud(Xi);break a}}if(Pg){var nh=Di.fa;if(nh instanceof Pl){var bh=nh,Mj=bh.Za,Nj=bh.Qb;if(Mj instanceof vl){var ie=Mj;Ps(a,new Ne(562),new Oe("finalTerm"));t();var Ac=new bm(ie,new Ol(Nj,Us(a,0,c,new Ne(563))));Qg=new Ud(Ac);break a}}}Qg=wc;break a}}}Qg=wc}var Ve=!1,Td=null, lf=Os(a);if(lf instanceof z){Ve=!0;Td=lf;var Yi=Td.z;if(null!==Yi){var Jl=Yi.h();if(ds()===Jl){Ps(a,new Ne(570),new Oe("block"));var ll=Rs(a,b,c);return new z(Qg,ll)}}}if(Ve){var Bj=Td.z;if(null!==Bj){var $k=Bj.h();if(qs()===$k){Ps(a,new Ne(571),new Oe("block"));var Zh=Rs(a,b,c);return new z(Qg,Zh)}}}var Ei=O().c;return new z(Qg,Ei)}} function Os(a){for(var b=Me(a,new Ne(606),new Oe("yeetSpaces"));;){if(b.b())c=!1;else if(c=b.e(),Pe(new E(c.h()),Se())||c.h()instanceof gs){Ps(a,new Ne(609),new Oe("yeetSpaces"));var c=!0}else c=!1;if(c)b=b.f();else break}return b} function Rt(a,b,c,d){var e=new U(()=>{}),g=new Oe("funParams");Ds(a,new U(()=>{var db=g.fn,ub=Es(e);ub=Fs(ub)?ze(ub,"(",",",")"):Gs(ub)?Es(e):"("+Es(e)+")";return"@ "+db+ub+" [at l."+d.Zl+"]"}));try{a.le=1+a.le|0;var h=!1,k=null,l=Os(a);a:{if(l instanceof z){h=!0;k=l;var m=k.z;if(null!==m){var n=m.h();if(n instanceof as){var r=n.Na;if("\x3d"===r||":"===r){var v=O().c;break a}}}}if(h){var x=k.z;if(null!==x){var A=x.h();if(A instanceof as&&"of"===A.Na){Ps(a,new Ne(615),new Oe("funParams"));var B= new Gl(Xt(a,!1,Vs().Du,c,b)),C=Rt(a,b,c,new Ne(617));v=new z(B,C);break a}}}if(h){var D=k.z;if(null!==D){var F=D.h(),I=D.j();if(F instanceof cs){var M=F.Cc,N=F.td;if(Ik()===M){Ps(a,new Ne(619),new Oe("funParams"));t();for(var P=new L(F.Mf),T=F.jb(),Y=new Ft(a,N,P,T),Z=Jt(Y,c,b),S=Me(Y,new Ne(147),new Oe("concludeWith"));;){if(S.b())ia=!1;else{var ea=S.e();if(Pe(new E(ea.h()),Se())||Pe(new E(ea.h()),qs())){Ps(Y,new Ne(147),new Oe("concludeWith"));var ia=!0}else ia=!1}if(ia)S=S.f();else break}h=S;b:{if(h instanceof z){var X=h.z;if(null!==X){var sa=X.h(),Ja=X.j();for(X=h;;){if(X.b())Fa=!1;else var Xa=X.e().h(),Fa=Pe(new E(Xa),Se());if(Fa)X=X.f();else break}var za=X.Jg(),Qa=za.b()?G(new H,sa,Ja):za.o();if(null===Qa)throw new w(Qa);var Ma=Qa.h(),Ga=Qa.j(),ab=new Te(new Ue(J(new K,["Unexpected "," here"]))),Hb=[We(Xe(),Ma.jb())],bc=Ye(ab,J(new K,Hb));t();var yb=G(new H,bc,new L(Ga)),tb=O().c;Ze(Y,new z(yb,tb));break b}}var eb=O().c;if(null===eb?null!==h:!eb.i(h))throw new w(h);}Ds(Y,new U(()=>"Concluded with "+ Z));var kb=new Gl(Z);t();var Rb=Cq(kb,new L(I)),Gb=Rt(a,b,c,new Ne(621));v=new z(Rb,Gb);break a}}}}if(h){var vb=k.z;if(null!==vb){var Tb=vb.h(),Nb=vb.j(),ic=new Te(new Ue(J(new K,["Expected function parameter list; found "," instead"]))),Va=[We(Xe(),Tb.jb())],cb=Ye(ic,J(new K,Va));t();var zb=G(new H,cb,new L(Nb)),Ub=O().c;Ze(a,new z(zb,Ub));Ps(a,new Ne(626),new Oe("funParams"));v=O().c;break a}}var jb=O().c;if(null===jb?null===l:jb.i(l))v=O().c;else throw new w(l);}}finally{a.le=-1+a.le|0}Ds(a,new U(()=> "\x3d "+v));return v}function Us(a,b,c,d){var e=new U(()=>new Yt(b,!0)),g=new Oe("expr");Ds(a,new U(()=>{var m=g.fn,n=Es(e);n=Fs(n)?ze(n,"(",",",")"):Gs(n)?Es(e):"("+Es(e)+")";return"@ "+m+n+" [at l."+d.Zl+"]"}));try{a.le=1+a.le|0;var h=O().c,k=Qs(a,b,!0,h,!1,c,new Ne(643));if(k instanceof Ud)var l=k.fa;else if(k instanceof fe)l=Ks(a,k.aa.A());else throw new w(k);}finally{a.le=-1+a.le|0}Ds(a,new U(()=>"\x3d "+l));return l} function Zt(a,b,c,d){var e=Os(a);if(e instanceof z&&(e=e.z,null!==e&&(e=e.h(),qs()===e))){Ps(a,d,new Oe("exprOrBlockContinuation"));e=Rs(a,b,c);b=h=>{if(h instanceof fe)return Ks(a,h.aa.A());if(h instanceof Ud)return h.fa;throw new w(h);};if(e===u())b=u();else{c=e.e();d=c=new z(b(c),u());for(e=e.f();e!==u();){var g=e.e();g=new z(b(g),u());d=d.p=g;e=e.f()}b=c}return new Sl(b)}return Us(a,0,c,d)} function Qs(a,b,c,d,e,g,h){var k=tc();try{return Cs(a,new U(()=>new Yt(b,c)),new y(()=>{var l=Ws(a,!1);l.b()||Os(a);var m=un(d,l),n=!1,r=null,v=Me(a,new Ne(710),new Oe("res"));a:{if(v instanceof z){n=!0;r=v;var x=r.z;if(null!==x){var A=x.h();if(Se()===A&&c){Ps(a,new Ne(712),new Oe("res"));var B=Qs(a,b,c,O().c,e,g,new Ne(713));break a}}}if(n){var C=r.z;if(null!==C){var D=C.h();if(D instanceof cs){var F=D.Cc,I=D.td;if(Mk()===F){var M=I.Jg();b:{if(M instanceof L){var N=M.k;if(null!==N){var P=N.h();if(P instanceof as){var T=P.Na;if("then"===T||"else"===T){var Y=!1;break b}}}}Y=!0}}else Y=!1;if(Y){Ps(a,new Ne(718),new Oe("res"));t();var Z=new L(D.Mf),S=D.jb(),ea=vt(new Ft(a,I,Z,S),new y(Ee=>Rs(Ee,e,g))),ia=Qt(ea,new y(Ee=>{if(Ee instanceof fe)throw t(),Ee=new $t(ea),Hq(new Iq,k,new fe(Ee));if(Ee instanceof Ud)return Ee.fa;throw new w(Ee);}));t();var X=new Sl(ia);B=new Ud(X);break a}}}}if(n){var sa=r.z;if(null!==sa){var Ja=sa.h(),Xa=sa.j();if(Yr()===Ja){Ps(a,new Ne(723),new Oe("res"));var Fa=!1,za=null,Qa=Me(a, new Ne(724),new Oe("res"));if(Qa instanceof z){Fa=!0;za=Qa;var Ma=za.z;if(null!==Ma){var Ga=Ma.h(),ab=Ma.j();if(Ga instanceof bs){var Hb=Ga.ae;if(!1===Ga.ke){Ps(a,new Ne(726),new Oe("res"));var bc=new em(new vl(Hb));t();var yb=xs(Xa,ab);B=au(a,Cq(bc,new L(yb)),b,!1,e,g,new Ne(727));break a}}}}if(Fa){var tb=za.z;if(null!==tb){var eb=tb.h(),kb=tb.j();if(eb instanceof fs){var Rb=eb.Cu;Ps(a,new Ne(729),new Oe("res"));t();var Gb=new em(Cq(Rb,new L(kb)));t();var vb=xs(Xa,kb);B=au(a,Cq(Gb,new L(vb)),b,!1, e,g,new Ne(730));break a}}}if(Fa){var Tb=za.z;if(null!==Tb){var Nb=Tb.h(),ic=Tb.j();if(Nb instanceof as&&"let"===Nb.Na){Ps(a,new Ne(732),new Oe("res"));var Va=Ns(a,!0,e,g,new Ne(733));if(Va instanceof Ud){var cb=Va.fa;t();t();var zb=Kt(Xa,cb.A()),Ub=Cq(cb,new L(zb));B=new Ud(Ub)}else{t();var jb=Ms(a,(t(),new L(ic)));B=new Ud(jb)}break a}}}if(Fa){var db=za.z;if(null!==db){var ub=db.h(),Aa=db.j();if(ub instanceof as&&"if"===ub.Na){var va=Qs(a,b,c,O().c,e,g,new Ne(738));if(va instanceof Ud){var Ra=va.fa; if(Ra instanceof Xl){var rb=Ra.bp,xb=Ra.zs;if(rb instanceof Ut){var mc=rb.Km,Ha=rb.Lm;t();var Ka=new Ut(new fm(mc),new fm(Ha)),Oa=new y(Ee=>new fm(Ee)),Na=new em(new Xl(Ka,xb.b()?R():new L(Oa.n(xb.o()))));t();var Da=Kt(Xa,Ra.A()),ta=Cq(Na,new L(Da));B=new Ud(ta);break a}}}t();var Ya=Ms(a,(t(),new L(Aa)));B=new Ud(Ya);break a}}}if(Fa){var dc=za.z;if(null!==dc){var ka=dc.h(),ya=dc.j();if(ka instanceof cs){var Sa=ka.Cc,xc=ka.td;if(Ik()===Sa){Ps(a,new Ne(745),new Oe("res"));t();var Sb=new L(ka.Mf),uc= ka.jb(),Lb=vt(new Ft(a,xc,Sb,uc),new y(Ee=>Jt(Ee,g,e)));b:{if(Lb instanceof z){var lc=Lb.z,Xb=Lb.p;if(null!==lc){var ec=lc.h(),Ab=lc.j();if(t().d===ec&&null!==Ab){var Ob=Ab.yb,fb=Ab.ya;if(null!==Ob){var Wa=Ob.ch;if(!1===Ob.je)if(!1===Wa)var bb=O().c,Ia=null===bb?null===Xb:bb.i(Xb);else Ia=!1;else Ia=!1;if(Ia){var Ua=new em(new Fl(!1,fb));t();var pc=Kt(ya,fb.A());var sc=Cq(Ua,new L(pc));break b}}}}}sc=Ms(a,(t(),new L(ya)))}B=au(a,sc,b,!1,e,g,new Ne(750));break a}}}}t();var Ba=Ms(a,(t(),new L(Xa))); B=new Ud(Ba);break a}}}if(n){var ob=r.z;if(null!==ob){var nc=ob.h(),Ib=ob.j();if(nc instanceof fs){var vc=nc.Cu;Ps(a,new Ne(754),new Oe("res"));t();B=au(a,Cq(vc,new L(Ib)),b,!1,e,g,new Ne(755));break a}}}if(n){var Vb=r.z;if(null!==Vb){var fc=Vb.h(),Bc=Vb.j();if(fc instanceof as){var Pb=fc.Na;if("undefined"===Pb||"null"===Pb){Ps(a,new Ne(757),new Oe("res"));var Jb=new Gm("undefined"===Pb);t();B=au(a,Cq(Jb,new L(Bc)),b,!1,e,g,new Ne(758));break a}}}}if(n){var gc=r.z;if(null!==gc){var Cb=gc.h(),cc=gc.j(); if(Cb instanceof bs){var yc=Cb.ae;if(!1===Cb.ke){Ps(a,new Ne(760),new Oe("res"));var Mc=new vl(yc);t();B=au(a,Cq(Mc,new L(cc)),b,!1,e,g,new Ne(761));break a}}}}if(n){var qc=r.z;if(null!==qc){var oc=qc.h(),Qc=qc.j();if(oc instanceof cs){var jc=oc.Cc,sb=oc.td;if(Nk()===jc||Ok()===jc){Ps(a,new Ne(763),new Oe("res"));t();var Gc=new L(oc.Mf),Wb=oc.jb(),Cc=vt(new Ft(a,sb,Gc,Wb),new y(Ee=>Us(Ee,0,g,new Ne(764)))),Fc=new em(Cc);t();B=au(a,Cq(Fc,new L(Qc)),b,!1,e,g,new Ne(765));break a}}}}if(n){var qd=r.z; if(null!==qd){var Yb=qd.h(),Nc=qd.j();if(Yb instanceof cs){var ad=Yb.Cc,Uc=Yb.td;if(Pk()===ad){Ps(a,new Ne(767),new Oe("res"));t();var cd=new L(Yb.Mf),kc=Yb.jb(),Vc=vt(new Ft(a,Uc,cd,kc),new y(Ee=>Us(Ee,0,g,new Ne(768)))),Hc=new fm(Vc);t();B=au(a,Cq(Hc,new L(Nc)),b,!1,e,g,new Ne(769));break a}}}}if(n){var rc=r.z;if(null!==rc){var sd=rc.h(),Kc=rc.j();if(sd instanceof as&&"super"===sd.Na){Ps(a,new Ne(771),new Oe("res"));var Qd=new am;t();B=au(a,Cq(Qd,new L(Kc)),b,!1,e,g,new Ne(772));break a}}}if(n){var Ad= r.z;if(null!==Ad){var kd=Ad.h(),Hd=Ad.j();if(kd instanceof bs&&"~"===kd.ae){Ps(a,new Ne(774),new Oe("res"));var Rd=Us(a,b,g,new Ne(775)),Bd=new vl("~");t();var ae=new Pl(Cq(Bd,new L(Hd)),Rd);t();var dd=Kt(Hd,Rd.A());B=au(a,Cq(ae,new L(dd)),b,!1,e,g,new Ne(776));break a}}}if(n){var od=r.z;if(null!==od){var Ta=od.h();if(Ta instanceof cs){var wb=Ta.Cc,$a=Ta.td;if(Ik()===wb&&$a instanceof z){var wa=$a.z,hb=$a.p;if(null!==wa){var ra=wa.h(),wc=wa.j();if(ra instanceof bs){var ac=ra.ae;if(!0===ra.ke)var Id= O().c,ud=null===Id?null===hb:Id.i(hb);else ud=!1;if(ud){Ps(a,new Ne(778),new Oe("res"));var be=new vl(ac);t();B=au(a,Cq(be,new L(wc)),b,!1,e,g,new Ne(779));break a}}}}}}}if(n){var re=r.z;if(null!==re){var pe=re.h(),bd=re.j();if(pe instanceof cs){var Rc=pe.Cc,Wc=pe.td;if(Ik()===Rc||Kk()===Rc||Jk()===Rc){Ps(a,new Ne(781),new Oe("res"));t();var Wd=new L(pe.Mf),zd=pe.jb(),Pa=vt(new Ft(a,Wc,Wd,zd),new y(Ee=>Jt(Ee,g,e))),Db=G(new H,Rc,Pa);b:{var Oc=Db.y;if(Jk()===Oc)var Tc=new Fl(!0,new yl(Qt(Pa,new y(Ee=> {if(null!==Ee){var Ca=new L(Ee);if(!Ca.b()){var Lc=Ca.k.h();Ca=Ca.k.j();if(Lc instanceof L)return G(new H,Lc.k,Ca)}}if(null!==Ee&&(Ca=new L(Ee),!Ca.b()&&(Lc=Ca.k.h(),Ca=Ca.k.j(),t().d===Lc&&null!==Ca&&(Lc=Ca.ya,Lc instanceof vl))))return G(new H,Lc,Ca);if(null!==Ee&&(Lc=new L(Ee),!Lc.b()&&(Ca=Lc.k.h(),Lc=Lc.k.j(),t().d===Ca)))return Ee=Ye(new Te(new Ue(J(new K,["Record field should have a name"]))),u()),Ca=Lc.ya.A(),Ee=G(new H,Ee,Ca),Ca=O().c,Ze(a,new z(Ee,Ca)),Ee=new vl("\x3cerror\x3e"),G(new H, Ee,Lc);throw new w(Ee);}))));else{var Sd=Db.y,Jc=Db.w;if(Ik()===Sd&&Jc instanceof z){var vd=Jc.z,hd=Jc.p;if(null!==vd){var de=vd.h(),ye=vd.j();if(t().d===de&&null!==ye){var jf=ye.yb,af=ye.ya;if(null!==jf){var pf=jf.ch;if(!1===jf.je)if(!1===pf)var kf=O().c,Be=null===kf?null===hd:kf.i(hd);else Be=!1;else Be=!1;if(Be){Tc=new Fl(!1,af);break b}}}}}var Kd=Db.y;if(Ik()===Kd){var ld=!1,Jd=null,Dd=Os(a);if(Dd instanceof z){ld=!0;Jd=Dd;var Xd=Jd.z;if(null!==Xd){var Yc=Xd.h();if(Yc instanceof as&&"\x3d\x3e"=== Yc.Na){Ps(a,new Ne(798),new Oe("bra"));var Ce=Us(a,bu("\x3d\x3e").Sc(),g,new Ne(799));Tc=new Ol(new Gl(Pa),Ce);break b}}}if(ld){var te=Jd.z,Ie=Jd.p;if(null!==te){var Jf=te.h();if(Yr()===Jf&&Ie instanceof z){var df=Ie.z;if(null!==df){var vg=df.h();if(vg instanceof as&&"\x3d\x3e"===vg.Na){var wg=au(a,new Gl(Pa),0,!0,e,g,new Ne(802));if(wg instanceof fe){Tc=Ms(a,wg.aa.A());break b}else if(wg instanceof Ud){Tc=wg.fa;break b}else throw new w(wg);}}}}}if(ld){var xg=Jd.z;if(null!==xg){var eg=xg.h();if(eg instanceof bs){var vh=eg.ke;if("-\x3e"===eg.ae&&!0===vh){Ps(a,new Ne(807),new Oe("bra"));var fg=Us(a,bu("-\x3e").Sc(),g,new Ne(808));Tc=new Ol(new Gl(Pa),fg);break b}}}}var ih=O().c;if(null===ih?null===Pa:ih.i(Pa))Tc=new Gm(!0);else{var Ig=Qt(Pa,new y(Ee=>{if(null!==Ee){var Ca=new L(Ee);if(!Ca.b()){var Lc=Ca.k.h();Ca=Ca.k.j();if(t().d===Lc&&null!==Ca){Lc=Ca.yb;Ca=Ca.ya;var yd=tm().Cg;if(null===yd?null===Lc:yd.i(Lc))return Ca}}}if(null!==Ee&&(Ca=new L(Ee),!Ca.b()&&(Lc=Ca.k.h(),Ca=Ca.k.j(),null!==Ca)))return Ee= Ca.ya,Ca=Ye(new Te(new Ue(J(new K,["Illegal position for field specification"]))),u()),cu(),Lc=Lc.ha(),Lc=du(0,Xq(Lc,Ee)),Lc=G(new H,Ca,Lc),Ca=O().c,Ze(a,new z(Lc,Ca)),Ee;throw new w(Ee);}));Tc=kba(Ig,new fn((Ee,Ca)=>{var Lc=new vl(",");cu();var yd=O().c;yd=du(0,new z(Ee,new z(Ca,yd)));Lc=Cq(Lc,yd);eu();return new Pl(Lc,fu(0,J(new K,[Ee,Ca])))}))}}else Tc=new Gl(Pa)}}t();B=au(a,Cq(Tc,new L(bd)),b,!1,e,g,new Ne(827));break a}}}}if(n){var Tf=r.z;if(null!==Tf){var Jg=Tf.h();if(Jg instanceof as&&"forall"=== Jg.Na){Ps(a,new Ne(829),new Oe("res"));var jh=fba(a),yg=Me(a,new Ne(843),new Oe("rest"));b:{if(yg instanceof z){var gg=yg.z;if(null!==gg){var Cf=gg.h();if(Cf instanceof as&&":"===Cf.Na){Ps(a,new Ne(845),new Oe("rest"));var Uf=Us(a,0,g,new Ne(846));break b}}}var $g=Ye(new Te(new Ue(J(new K,["Expected `:` after `forall` section"]))),u()),Ah=Js(a),Kg=new U(()=>Hs(a)),Vf=Ah.b()?Es(Kg):Ah,hg=G(new H,$g,Vf),zg=O().c;Ze(a,new z(hg,zg));Uf=Ls(a)}t();var Lg=new Zl(jh,Uf);B=new Ud(Lg);break a}}}if(n){var Mg= r.z;if(null!==Mg){var Wf=Mg.h(),Ng=Mg.j();if(Wf instanceof as&&"while"===Wf.Na){Ps(a,new Ne(853),new Oe("res"));var Kf=Us(a,0,g,new Ne(854)),xf=zt(a,new as("do"),Bt(),!1,new U(()=>O().c),g);if(null===xf)throw new w(xf);var Og=Us(a,0,g,new Ne(856)),mi=new dm(Kf,Og);t();var Ci=Kt(Ng,Og.A());B=au(a,Cq(mi,new L(Ci)),b,!1,e,g,new Ne(857));break a}}}if(n){var Xh=r.z;if(null!==Xh){var wh=Xh.h(),Bh=Xh.j();if(wh instanceof as&&"set"===wh.Na){Ps(a,new Ne(859),new Oe("res"));var ng=Us(a,0,g,new Ne(860)),kh= zt(a,new as("\x3d"),Bt(),!1,new U(()=>O().c),g);if(null!==kh)var Kh=kh.Rc();else throw new w(kh);var ni=tt(Vs(),g)||!Kh,Lh=Us(a,0,ni,new Ne(862)),lh=new Wl(ng,Lh);t();var Ch=Kt(Bh,Lh.A());B=au(a,Cq(lh,new L(Ch)),b,!1,e,g,new Ne(863));break a}}}if(n){var Dh=r.z;if(null!==Dh){var Yh=Dh.h();if(Yh instanceof as&&"let"===Yh.Na){Ps(a,new Ne(865),new Oe("res"));B=Ns(a,!1,e,g,new Ne(866));break a}}}if(n){var ah=r.z;if(null!==ah){var oi=ah.h(),mj=ah.j();if(oi instanceof as&&"new"===oi.Na){Ps(a,new Ne(868), new Oe("res"));var wd=Us(a,Vs().ao.n(hc(46))|0,g,new Ne(869)),ge=new Yl(wd);t();var De=Kt(mj,wd.A());B=au(a,Cq(ge,new L(De)),b,!1,e,g,new Ne(870));break a}}}if(n){var qf=r.z;if(null!==qf){var og=qf.h(),Xf=qf.j();if(og instanceof as&&"else"===og.Na){Ps(a,new Ne(872),new Oe("res"));var mh=Os(a);if(mh instanceof z){var Ag=mh.z;if(null!==Ag){var Bg=Ag.h();qs()===Bg&&(Ps(a,new Ne(875),new Oe("res")),no())}}var Eh=Us(a,0,g,new Ne(878));t();var Pg=new gu(Eh);t();var Di=Kt(Xf,Eh.A()),Mh=Cq(Pg,new L(Di)); B=new fe(Mh);break a}}}if(n){var pi=r.z;if(null!==pi){var Xi=pi.h(),Qg=pi.j();if(Xi instanceof as&&"case"===Xi.Na){Ps(a,new Ne(882),new Oe("res"));var nh=O().c,bh=Qs(a,0,!0,nh,!0,g,new Ne(883));if(bh instanceof fe){var Mj=bh.aa;t();eu();var Nj=[new vl("case$scrut")],ie=new Ol(fu(0,J(new K,Nj)),new Xl(new Tt(new vl("case$scrut"),new vl("is"),Mj),t().d));B=new Ud(ie);break a}else if(bh instanceof Ud){var Ac=bh.fa,Ve=new Te(new Ue(J(new K,["Expected 'then'/'else' clause after 'case'; found "," instead"]))), Td=[We(Xe(),hu(Ac))],lf=Ye(Ve,J(new K,Td)),Yi=Ac.A(),Jl=G(new H,lf,Yi),ll=Ye(new Te(new Ue(J(new K,["Note: 'case' expression starts here:"]))),u());t();var Bj=G(new H,ll,new L(Qg)),$k=O().c;Ze(a,new z(Jl,new z(Bj,$k)));t();eu();var Zh=[new vl("case$scrut")],Ei=new Ol(fu(0,J(new K,Zh)),new Xl(new gu(Ac),t().d));B=new Ud(Ei);break a}else throw new w(bh);}}}if(n){var Yd=r.z;if(null!==Yd){var bf=Yd.h(),rf=Yd.j();if(bf instanceof as&&"if"===bf.Na){Ps(a,new Ne(893),new Oe("res"));var Cg=O().c,nj=Qs(a,0, !0,Cg,!0,g,new Ne(894));if(nj instanceof fe){var Jh=nj.aa,If=!1,Hg=null,He=Os(a);b:{if(He instanceof z){If=!0;Hg=He;var lj=Hg.z;if(null!==lj){var Wi=lj.h();if(Wi instanceof as&&"else"===Wi.Na){Ps(a,new Ne(898),new Oe("els"));t();var Oj=Zt(a,e,g,new Ne(899));var mo=new L(Oj);break b}}}if(If){var mm=Hg.z,nm=Hg.p;if(null!==mm){var dq=mm.h();if(qs()===dq&&nm instanceof z){var Zd=nm.z;if(null!==Zd){var sf=Zd.h();if(sf instanceof as&&"else"===sf.Na){Ps(a,new Ne(901),new Oe("els"));Ps(a,new Ne(902),new Oe("els")); t();var oj=Us(a,0,g,new Ne(903));mo=new L(oj);break b}}}}}if(If){var al=Hg.z;if(null!==al){var Ll=al.h();if(Ll instanceof cs){var Qm=Ll.Cc,Rm=Ll.td;if(Mk()===Qm&&Rm instanceof z){var hq=Rm.z,Bn=Rm.p;if(null!==hq){var hp=hq.h();if(hp instanceof as&&"else"===hp.Na){Ps(a,new Ne(905),new Oe("els"));t();var ru=new L(Ll.Mf),qr=Ll.jb(),Xs=new Ft(a,Bn,ru,qr);t();var rr=vt(Xs,new y(Ee=>Us(Ee,0,g,new Ne(907))));mo=new L(rr);break b}}}}}}mo=t().d}t();var iq=new Xl(Jh,mo);B=new Ud(iq);break a}else if(nj instanceof Ud){var qo=nj.fa,qm=Os(a);if(qm instanceof z){var jq=qm.z;if(null!==jq){var pl=jq.h();if(pl instanceof cs){var ro=pl.Cc,Cn=pl.td;if(Mk()===ro&&Cn instanceof z){var ip=Cn.z,so=Cn.p;if(null!==ip){var Dn=ip.h();if(Dn instanceof as&&"then"===Dn.Na){Ps(a,new Ne(914),new Oe("res"));t();var sr=new L(pl.Mf),kq=pl.jb(),ql=new Ft(a,so,sr,kq),Ys=Us(ql,0,g,new Ne(916)),Sm=!1,Nl=null,jp=Os(ql);b:{if(jp instanceof z){Sm=!0;Nl=jp;var lq=Nl.z;if(null!==lq){var mq=lq.h();if(mq instanceof as&&"else"===mq.Na){Ps(ql, new Ne(919),new Oe("els"));t();var Tm=vt(ql,new y(Ee=>Us(Ee,0,g,new Ne(920))));var En=new L(Tm);break b}}}if(Sm){var to=Nl.z,Fn=Nl.p;if(null!==to){var nq=to.h();if(qs()===nq&&Fn instanceof z){var Um=Fn.z;if(null!==Um){var kp=Um.h();if(kp instanceof as&&"else"===kp.Na){Ps(ql,new Ne(922),new Oe("els"));Ps(ql,new Ne(923),new Oe("els"));t();var oq=vt(ql,new y(Ee=>Us(Ee,0,g,new Ne(925))));En=new L(oq);break b}}}}}vt(ql,new y(()=>{}));En=t().d}t();var su=new Xl(new Ut(qo,Ys),En);B=new Ud(su);break a}}}}}}b:{if(qm instanceof z){var Gn=qm.z;if(null!==Gn){var ur=Gn.h(),In=Gn.j(),Zs=new Te(new Ue(J(new K,[""," followed by ",""]))),$s=[We(Xe(),hu(qo)),We(Xe(),ur.jb())],pq=Ye(Zs,J(new K,$s));t();var vr=iu(ju(),qo.A()).mf(In,new fn((Ee,Ca)=>xs(Ee,Ca)));var Vm=G(new H,pq,new L(vr));break b}}var Jn=O().c;if(null===Jn?null===qm:Jn.i(qm)){var wr=new Te(new Ue(J(new K,["",""]))),at=[We(Xe(),hu(qo))];Vm=G(new H,Ye(wr,J(new K,at)),qo.A())}else throw new w(qm);}if(null!==Vm)var xr=G(new H,Vm.h(),Vm.j());else throw new w(Vm);var lp= xr.j(),Kn=Ye(new Te(new Ue(J(new K,["Expected 'then'/'else' clause after 'if'; found "," instead"]))),J(new K,[xr.h()])),qq=G(new H,Kn,lp),yr=Ye(new Te(new Ue(J(new K,["Note: 'if' expression starts here:"]))),u());t();var rq=G(new H,yr,new L(rf)),sq=O().c;Ze(a,new z(qq,new z(rq,sq)));t();var bt=new Xl(new Ut(qo,Ls(a)),t().d);B=new Ud(bt);break a}else throw new w(nj);}}}var tq=O().c;if(null===tq?null===v:tq.i(v)){var zr=new Te(new Ue(J(new K,["Unexpected end of ","; an expression was expected here"]))), ct=[We(Xe(),a.jE)],Ar=Ye(zr,J(new K,ct)),uq=Hs(a),Br=G(new H,Ar,uq),Ln=O().c;Ze(a,new z(Br,Ln));t();var vq=Ls(a);B=new Ud(vq)}else{if(n){var Cr=r.z;if(null!==Cr){var tu=Cr.h(),uu=Cr.j();if(ds()===tu){t();var dt=new Gm(!0);t();var vu=Cq(dt,new L(uu));B=new Ud(vu);break a}}}if(n){var Dr=r.z;if(null!==Dr){var uo=Dr.h(),Er=Dr.j();if(uo instanceof bs){var et=uo.ke;if("-"===uo.ae&&!0===et){Ps(a,new Ne(951),new Oe("res"));var ft=new vl("-");t();var gt=Cq(ft,new L(Er)),Wm=Us(a,bu("-").Sc(),g,new Ne(953)); if(Wm instanceof Em){B=au(a,new Em(lba(Wm.rq)),b,!1,e,g,new Ne(955));break a}else if(null!==Wm){if(a.hx){eu();var Fr=[new Em(br()),Wm],mp=new Pl(gt,fu(0,J(new K,Fr)))}else{eu();var wu=[new Em(br())],ht=new Pl(gt,fu(0,J(new K,wu)));eu();mp=new Pl(ht,fu(0,J(new K,[Wm])))}B=au(a,mp,b,!1,e,g,new Ne(957));break a}else throw new w(Wm);}}}}if(n){var wq=r.z;if(null!==wq){var xq=wq.h(),Gr=wq.j(),xu=new Te(new Ue(J(new K,["Unexpected "," in expression position"]))),yu=[We(Xe(),xq.jb())],Re=Ye(xu,J(new K,yu)); t();var rj=G(new H,Re,new L(Gr)),ai=O().c;Ze(a,new z(rj,ai));Ps(a,new Ne(964),new Oe("res"));var rm=O().c;B=Qs(a,b,!0,rm,e,!0,new Ne(965));break a}}throw new w(v);}}if(m.b())return B;if(B instanceof fe){var Nn=B.aa;if(Nn instanceof Ut){var zu=Nn.Km,Au=Nn.Lm;O();var Av=new Ut(Ts(zu,m),Au);return new fe(Av)}var oy=Ye(new Te(new Ue(J(new K,["Unexpected annotation"]))),u()),Bv=m.e().A(),Cv=G(new H,oy,Bv),py=O().c;Ze(a,new z(Cv,py));t();return new fe(Nn)}if(B instanceof Ud){var qy=B.fa;t();var Dv=Ts(qy, m);return new Ud(Dv)}throw new w(B);}),h,new Oe("exprOrIf"))}catch(l){if(l instanceof Iq){h=l;if(h.Qg===k)return h.Cj();throw h;}throw l;}} function au(a,b,c,d,e,g,h){return Cs(a,new U(()=>new tl(c,"`"+b+"`",d)),new y(()=>{var k=!1,l=null,m=Me(a,new Ne(990),new Oe("exprCont"));if(m instanceof z){k=!0;l=m;var n=l.z;if(null!==n){var r=n.h();if(Yr()===r){var v=!1,x=null,A=Me(a,new Ne(991),new Oe("exprCont"));if(A instanceof z){v=!0;x=A;var B=x.p;if(B instanceof z){var C=B.z;if(null!==C){var D=C.h();if(D instanceof as&&"\x3d\x3e"===D.Na&&bu("\x3d\x3e").ut()>c){Ps(a,new Ne(993),new Oe("exprCont"));Ps(a,new Ne(994),new Oe("exprCont"));var F= b instanceof Gl?b:fu(eu(),J(new K,[b]));return au(a,new em(new Ol(F,new fm(Us(a,1,g,new Ne(998))))),c,d,e,g,new Ne(995))}}}}if(v){var I=x.p;if(I instanceof z){var M=I.z;if(null!==M){var N=M.h(),P=M.j();if(N instanceof cs){var T=N.Cc,Y=N.td;if(Ik()===T){Ps(a,new Ne(1E3),new Oe("exprCont"));Ps(a,new Ne(1001),new Oe("exprCont"));t();var Z=new L(N.Mf),S=N.jb(),ea=Qt(vt(new Ft(a,Y,Z,S),new y(ie=>Jt(ie,g,e))),new y(ie=>{if(null!==ie){var Ac=new L(ie);if(!Ac.b()){var Ve=Ac.k.h();Ac=Ac.k.j();if(null!==Ac)return ie= new sm(Ac.yb,new fm(Ac.ya)),G(new H,Ve,ie)}}throw new w(ie);})),ia=new fm(b),X=new Gl(ea);t();var sa=new Pl(ia,Cq(X,new L(P)));return au(a,new em(sa),c,d,e,g,new Ne(1006))}}}}}if(v){var Ja=x.p;if(Ja instanceof z){var Xa=Ja.z;if(null!==Xa){var Fa=Xa.h(),za=Xa.j();if(Fa instanceof bs){var Qa=Fa.ae;if(!0===Fa.ke){if(bu(Qa).ut()>c){Ps(a,new Ne(1009),new Oe("exprCont"));Ps(a,new Ne(1010),new Oe("exprCont"));var Ma=new vl(Qa);t();var Ga=Cq(Ma,new L(za)),ab=Os(a);if(ab instanceof z){var Hb=ab.z;if(null!== Hb){var bc=Hb.h();qs()===bc&&Ps(a,new Ne(1013),new Oe("exprCont"))}}var yb=Qs(a,bu(Qa).Sc(),!0,O().c,e,g,new Ne(1016));if(yb instanceof fe){t();var tb=Ms(a,(t(),new L(za)));return new Ud(tb)}if(yb instanceof Ud){var eb=yb.fa;if("with"===Qa)var kb=Ms(a,(t(),new L(za)));else{eu();var Rb=[new fm(b),new fm(eb)];kb=new em(new Pl(Ga,fu(0,J(new K,Rb))))}return au(a,kb,c,d,e,g,new Ne(1018))}throw new w(yb);}t();return new Ud(b)}}}}}if(v){var Gb=x.p;if(Gb instanceof z){var vb=Gb.z;if(null!==vb){var Tb=vb.h(); if(Tb instanceof as&&"in"===Tb.Na)return t(),new Ud(b)}}}Ps(a,new Ne(1028),new Oe("exprCont"));Ms(a,b.A());t();return new Ud(b)}}}if(k){var Nb=l.z;if(null!==Nb){var ic=Nb.h(),Va=Nb.j();if(Xr()===ic&&Pe(new E(c),0)){Ps(a,new Ne(1033),new Oe("exprCont"));var cb=Os(a);if(cb instanceof z){var zb=cb.z;if(null!==zb){var Ub=zb.h();qs()===Ub&&Ps(a,new Ne(1035),new Oe("exprCont"))}}var jb=Us(a,c,g,new Ne(1038));t();var db=new vl(",");t();var ub=Cq(db,new L(Va));eu();var Aa=new Pl(ub,fu(0,J(new K,[b,jb]))); return new Ud(Aa)}}}if(k){var va=l.z,Ra=l.p;if(null!==va){var rb=va.h();if(rb instanceof as&&"\x3d\x3e"===rb.Na&&Ra instanceof z){var xb=Ra.z;if(null!==xb){var mc=xb.h();if(qs()===mc&&bu("\x3d\x3e").ut()>c){Ps(a,new Ne(1041),new Oe("exprCont"));var Ha=new Sl(ef(Le(a)));t();eu();var Ka=new Ol(fu(0,J(new K,[b])),Ha);return new Ud(Ka)}}}}}if(k){var Oa=l.z;if(null!==Oa){var Na=Oa.h();if(Na instanceof as&&"\x3d\x3e"===Na.Na&&bu("\x3d\x3e").ut()>c){Ps(a,new Ne(1045),new Oe("exprCont"));var Da=Us(a,1,g, new Ne(1046));eu();var ta=new Ol(fu(0,J(new K,[b])),Da);return au(a,ta,c,d,e,g,new Ne(1048))}}}if(k){var Ya=l.z,dc=l.p;if(null!==Ya){var ka=Ya.h(),ya=Ya.j();if(ka instanceof bs&&"."===ka.ae&&dc instanceof z){var Sa=dc.z;if(null!==Sa){var xc=Sa.h(),Sb=Sa.j();if(xc instanceof cs){var uc=xc.Cc,Lb=xc.td;if(Kk()===uc){Ps(a,new Ne(1050),new Oe("exprCont"));Ps(a,new Ne(1051),new Oe("exprCont"));t();var lc=new L(xc.Mf),Xb=xc.jb(),ec=vt(new Ft(a,Lb,lc,Xb),new y(ie=>Us(ie,0,g,new Ne(1053)))),Ab=new Vl(b,ec); t();var Ob=Kt(xs(ya,Sb),ec.A()),fb=Cq(Ab,new L(Ob));return au(a,fb,c,d,e,g,new Ne(1055))}}}}}}if(k){var Wa=l.z;if(null!==Wa){var bb=Wa.h(),Ia=Wa.j();if(bb instanceof bs){var Ua=bb.ae;if(!0===bb.ke&&bu(Ua).ut()>c){Ps(a,new Ne(1057),new Oe("exprCont"));var pc=new vl(Ua);t();var sc=Cq(pc,new L(Ia)),Ba=Os(a);if(Ba instanceof z){var ob=Ba.z;if(null!==ob){var nc=ob.h();qs()===nc&&Ps(a,new Ne(1060),new Oe("exprCont"))}}var Ib=Qs(a,bu(Ua).Sc(),!0,O().c,e,g,new Ne(1063));if(Ib instanceof fe){var vc=Ib.aa; t();var Vb=new Tt(b,sc,vc);return new fe(Vb)}if(Ib instanceof Ud){var fc=Ib.fa;if("with"===Ua)a:if(fc instanceof yl)var Bc=new Tl(b,fc);else{if(fc instanceof Fl){var Pb=fc.gg;if(!0===fc.bi&&Pb instanceof yl){Bc=new Tl(b,Pb);break a}}var Jb=new Te(new Ue(J(new K,["record literal expected here; found ",""]))),gc=[We(Xe(),hu(fc))],Cb=Ye(Jb,J(new K,gc)),cc=fc.A(),yc=G(new H,Cb,cc),Mc=O().c;Ze(a,new z(yc,Mc));Bc=b}else if(a.hx)eu(),Bc=new Pl(sc,fu(0,J(new K,[b,fc])));else{eu();var qc=new Pl(sc,fu(0,J(new K, [b])));eu();Bc=new Pl(qc,fu(0,J(new K,[fc])))}return au(a,Bc,c,d,e,g,new Ne(1067))}throw new w(Ib);}}}}if(k){var oc=l.z;if(null!==oc){var Qc=oc.h();if(Qc instanceof as&&":"===Qc.Na&&c<=(Vs().ao.n(hc(58))|0)){Ps(a,new Ne(1084),new Oe("exprCont"));t();var jc=new Cl(b,Ht(a,g,new Ne(1085)));return new Ud(jc)}}}if(k){var sb=l.z;if(null!==sb){var Gc=sb.h(),Wb=sb.j();if(Gc instanceof as&&"where"===Gc.Na&&1>=c){Ps(a,new Ne(1087),new Oe("exprCont"));var Cc=Et(a),Fc=new $l(b,ef(Cc));t();var qd=Cq(Fc,new L(Wb)); return au(a,qd,c,!1,e,g,new Ne(1090))}}}if(k){var Yb=l.z;if(null!==Yb){var Nc=Yb.h();if(Se()===Nc)return Ps(a,new Ne(1092),new Oe("exprCont")),au(a,b,c,d,e,g,new Ne(1093))}}if(k){var ad=l.z;if(null!==ad){var Uc=ad.h(),cd=ad.j();if(Uc instanceof ss){var kc=Uc.ux;Ps(a,new Ne(1095),new Oe("exprCont"));var Vc=new vl(kc);t();return au(a,new Ql(b,Cq(Vc,new L(cd))),c,d,e,g,new Ne(1096))}}}if(k){var Hc=l.z;if(null!==Hc){var rc=Hc.h();if(rc instanceof cs){var sd=rc.Cc,Kc=rc.td;if(Mk()===sd&&Kc instanceof z){var Qd= Kc.z,Ad=Kc.p;if(null!==Qd){var kd=Qd.h(),Hd=Qd.j();if(kd instanceof ss){var Rd=kd.ux;if(1>=c){Ps(a,new Ne(1099),new Oe("exprCont"));t();var Bd=new L(rc.Mf),ae=rc.jb(),dd=vt(new Ft(a,Ad,Bd,ae),new y(ie=>{var Ac=new vl(Rd);t();return au(ie,new Ql(b,Cq(Ac,new L(Hd))),0,!0,e,g,new Ne(1100))}));if(d){if(dd instanceof fe){var od=dd.aa;t();return new fe(od)}if(dd instanceof Ud)return au(a,dd.fa,0,d,e,g,new Ne(1103));throw new w(dd);}return dd}}}}}}}if(k){var Ta=l.z;if(null!==Ta){var wb=Ta.h();if(wb instanceof cs){var $a=wb.Cc,wa=wb.td;if(Mk()===$a&&wa instanceof z){var hb=wa.z,ra=wa.p;if(null!==hb){var wc=hb.h(),ac=hb.j();if(wc instanceof bs){var Id=wc.ae;if(!0===wc.ke){Ps(a,new Ne(1107),new Oe("exprCont"));t();var ud=new L(wb.Mf),be=wb.jb();return vt(new Ft(a,ra,ud,be),new y(ie=>ku(ie,b,Id,ac,e,g,new Ne(1108))))}}}}}}}if(k){var re=l.z;if(null!==re){var pe=re.h();if(pe instanceof as&&"then"===pe.Na&&Pe(new E(c),0)){Ps(a,new Ne(1111),new Oe("exprCont"));t();var bd=new Ut(b,Zt(a,e,g,new Ne(1112)));return new fe(bd)}}}if(k){var Rc= l.z,Wc=l.p;if(null!==Rc){var Wd=Rc.h();if(qs()===Wd&&Wc instanceof z){var zd=Wc.z;if(null!==zd){var Pa=zd.h();if(Pa instanceof as&&"then"===Pa.Na&&Pe(new E(c),0)){Ps(a,new Ne(1114),new Oe("exprCont"));Ps(a,new Ne(1115),new Oe("exprCont"));t();var Db=new Ut(b,Zt(a,e,g,new Ne(1116)));return new fe(Db)}}}}}if(k){var Oc=l.z;if(null!==Oc){var Tc=Oc.h();if(qs()===Tc&&d)return Ps(a,new Ne(1118),new Oe("exprCont")),au(a,b,0,d,e,g,new Ne(1119))}}if(k){var Sd=l.z;if(null!==Sd){var Jc=Sd.h(),vd=Sd.j();if(Jc instanceof cs){var hd=Jc.Cc,de=Jc.td;if(Jk()===hd&&c<=Vs().MH){Ps(a,new Ne(1122),new Oe("exprCont"));t();var ye=new L(Jc.Mf),jf=Jc.jb(),af=vt(new Ft(a,de,ye,jf),new y(ie=>Et(ie)));t();var pf=Cq(af,new L(vd));return au(a,new cm(b,pf),c,d,e,g,new Ne(1124))}}}}if(k){var kf=l.z;if(null!==kf){var Be=kf.h();a:if(Xr()===Be)var Kd=!0;else if(ds()===Be)Kd=!0;else if(qs()===Be)Kd=!0;else{if(Be instanceof as){var ld=Be.Na;if("then"===ld||"else"===ld||"in"===ld||"\x3d"===ld||"do"===ld){Kd=!0;break a}}if(Be instanceof bs&& !0===Be.ke)Kd=!0;else{if(Be instanceof cs){var Jd=Be.Cc;if(Jk()===Jd){Kd=!0;break a}}Kd=!1}}if(Kd)return t(),new Ud(b)}}if(k){var Dd=l.z;if(null!==Dd){var Xd=Dd.h();if(Xd instanceof as&&"of"===Xd.Na&&1>=c){Ps(a,new Ne(1130),new Oe("exprCont"));var Yc=Jt(a,g,e),Ce=new Pl(b,new Gl(Yc));return au(a,Ce,c,d,e,g,new Ne(1133))}}}if(k){var te=l.z;if(null!==te){var Ie=te.h();if(Ie instanceof cs){var Jf=Ie.Cc,df=Ie.td;if(Mk()===Jf&&df instanceof z){var vg=df.z,wg=df.p;if(null!==vg){var xg=vg.h();if(xg instanceof as&&"of"===xg.Na&&1>=c){Ps(a,new Ne(1135),new Oe("exprCont"));t();var eg=new L(Ie.Mf),vh=Ie.jb(),fg=vt(new Ft(a,wg,eg,vh),new y(ie=>{var Ac=Jt(ie,g,e);return au(ie,new Pl(b,new Gl(Ac)),0,!0,e,g,new Ne(1143))}));if(fg instanceof fe){var ih=fg.aa;t();return new fe(ih)}if(fg instanceof Ud)return au(a,fg.fa,0,d,e,g,new Ne(1148));throw new w(fg);}}}}}}if(k){var Ig=l.z;if(null!==Ig){var Tf=Ig.h();if(Tf instanceof cs){var Jg=Tf.Cc,jh=Tf.td;if(Mk()===Jg&&jh instanceof z){var yg=jh.z;if(null!==yg){var gg= yg.h();if(gg instanceof as){var Cf=gg.Na;if("then"===Cf||"else"===Cf)return t(),new Ud(b)}}}}}}if(k){var Uf=l.z;if(null!==Uf){var $g=Uf.h();if($g instanceof cs){var Ah=$g.Cc,Kg=$g.td;if(Mk()===Ah&&Kg instanceof z){var Vf=Kg.z;if(null!==Vf){var hg=Vf.h();if(hg instanceof cs){var zg=hg.Cc;if((Ik()===zg||Kk()===zg)&&1>=c){Ps(a,new Ne(1164),new Oe("exprCont"));t();var Lg=new L($g.Mf),Mg=$g.jb(),Wf=vt(new Ft(a,Kg,Lg,Mg),new y(ie=>au(ie,b,0,!0,e,g,new Ne(1165))));if(Wf instanceof fe){var Ng=Wf.aa;t();return new fe(Ng)}if(Wf instanceof Ud)return au(a,Wf.fa,0,d,e,g,new Ne(1168));throw new w(Wf);}}}}}}}if(k){var Kf=l.z;if(null!==Kf){var xf=Kf.h(),Og=Kf.j();if(xf instanceof cs){var mi=xf.Cc,Ci=xf.td;if(Lk()===mi||Kk()===mi){Ps(a,new Ne(1172),new Oe("exprCont"));t();var Xh=new L(xf.Mf),wh=xf.jb(),Bh=vt(new Ft(a,Ci,Xh,wh),new y(ie=>Jt(ie,g,e))),ng=new Il(b,Qt(Bh,new y(ie=>{if(null!==ie){var Ac=ie.h();ie=ie.j();if(t().d===Ac&&null!==ie&&(Ac=ie.yb,ie=ie.ya,null!==Ac)){var Ve=Ac.ch;if(!1===Ac.je&&!1===Ve){Ac=Qn(ie);if(Ac instanceof fe){var Td= Ac.aa;st(a,new U(()=>Td),g);return gl()}if(Ac instanceof Ud)return Ac.fa;throw new w(Ac);}}}no()}))),kh=b.A(),Kh=new U(()=>it().n(Og)),ni=new y(ie=>{ie=xs(ie,Og);return it().n(ie)}),Lh=kh.b()?Es(Kh):ni.n(kh.o()),lh=Cq(ng,Lh);return au(a,lh,c,d,e,g,new Ne(1182))}}}}if(k){var Ch=l.z;if(null!==Ch){var Dh=Ch.h(),Yh=Ch.j();if(Dh instanceof cs){var ah=Dh.Cc,oi=Dh.td;if(Ik()===ah&&c<=Vs().MH){Ps(a,new Ne(1191),new Oe("exprCont"));t();var mj=new L(Dh.Mf),wd=Dh.jb(),ge=vt(new Ft(a,oi,mj,wd),new y(ie=>Jt(ie, g,e))),De=new Gl(ge);t();var qf=new Pl(b,Cq(De,new L(Yh)));return au(a,qf,c,d,e,g,new Ne(1194))}}}}if(k){var og=l.z;if(null!==og){var Xf=og.h();if(Xf instanceof as&&"of"===Xf.Na){Ps(a,new Ne(1197),new Oe("exprCont"));var mh=Jt(a,g,e),Ag=new Pl(b,new Gl(mh));return au(a,Ag,c,d,e,g,new Ne(1201))}}}if(k){var Bg=l.z.h();a:{if(Bg instanceof as){var Eh=Bg.Na;if(":"===Eh||"of"===Eh||"where"===Eh||"extends"===Eh){var Pg=!0;break a}}if(ds()===Bg)Pg=!0;else{if(Bg instanceof cs){var Di=Bg.Cc;if(Ik()===Di||Kk()=== Di){Pg=!0;break a}}if(Bg instanceof cs){var Mh=Bg.Cc,pi=Bg.td;if(Mk()===Mh&&pi instanceof z){var Xi=pi.z;if(null!==Xi){var Qg=Xi.h();b:if(Qg instanceof as&&"of"===Qg.Na)var nh=!0;else if(ds()===Qg)nh=!0;else{if(Qg instanceof cs){var bh=Qg.Cc;if(Ik()===bh||Kk()===bh){nh=!0;break b}}nh=Qg instanceof ss?!0:!1}if(nh){Pg=!0;break a}}}}Pg=!1}}if(!Pg){var Mj=Jt(a,g,e),Nj=new Pl(b,new Gl(Mj));st(a,new U(()=>{fr();var ie=Ye(new Te(new Ue(J(new K,["Paren-less applications should use the 'of' keyword"]))),u()), Ac=Nj.A();ie=G(new H,ie,Ac);Ac=O().c;return hr(0,new z(ie,Ac),!0,lu())}),g);return au(a,Nj,c,d,e,g,new Ne(1217))}}t();return new Ud(b)}),h,new Oe("exprCont"))} function ku(a,b,c,d,e,g,h){var k=new U(()=>G(new H,"`"+b+"`",c)),l=new Oe("opBlock");Ds(a,new U(()=>{var Ma=l.fn,Ga=Es(k);Ga=Fs(Ga)?ze(Ga,"(",",",")"):Gs(Ga)?Es(k):"("+Es(k)+")";return"@ "+Ma+Ga+" [at l."+h.Zl+"]"}));try{a.le=1+a.le|0;var m=new vl(c);t();var n=Cq(m,new L(d)),r=Qs(a,0,!0,O().c,e,g,new Ne(1225));if(r instanceof Ud){var v=r.fa;eu();var x=new Pl(n,fu(0,J(new K,[b,v]))),A=Me(a,new Ne(1230),new Oe("opBlock"));a:{if(A instanceof z){var B=A.z,C=A.p;if(null!==B){var D=B.h();if(qs()===D){Ps(a, new Ne(1232),new Oe("opBlock"));d=!1;m=null;if(C instanceof z){d=!0;m=C;var F=m.z;if(null!==F){var I=F.h(),M=F.j();if(I instanceof bs){var N=I.ae;if(!0===I.ke){Ps(a,new Ne(1235),new Oe("opBlock"));var P=ku(a,x,N,M,e,g,new Ne(1236));break a}}}}if(d){var T=m.z;if(null!==T){var Y=T.h(),Z=T.j(),S=new Te(new Ue(J(new K,["Unexpected "," in operator block"]))),ea=[We(Xe(),Y.jb())],ia=Ye(S,J(new K,ea));t();var X=G(new H,ia,new L(Z)),sa=O().c;Ze(a,new z(X,sa));Ps(a,new Ne(1239),new Oe("opBlock"));t();P=new Ud(x); break a}}var Ja=O().c;if(null===Ja?null===C:Ja.i(C)){t();P=new Ud(x);break a}throw new w(C);}}}t();P=new Ud(x)}}else if(r instanceof fe){var Xa=r.aa;t();var Fa=G(new H,n,Xa),za=O().c,Qa=new mu(b,nu(a,new z(Fa,za),e,g));P=new fe(Qa)}else throw new w(r);}finally{a.le=-1+a.le|0}Ds(a,new U(()=>"\x3d "+P));return P} function nu(a,b,c,d){var e=new U(()=>b),g=new Ne(1251),h=new Oe("opIfBlock");Ds(a,new U(()=>{var N=h.fn,P=Es(e);P=Fs(P)?ze(P,"(",",",")"):Gs(P)?Es(e):"("+Es(e)+")";return"@ "+N+P+" [at l."+g.Zl+"]"}));try{a.le=1+a.le|0;var k=Me(a,new Ne(1252),new Oe("opIfBlock"));a:{if(k instanceof z){var l=k.z,m=k.p;if(null!==l){var n=l.h();if(qs()===n){Ps(a,new Ne(1254),new Oe("opIfBlock"));if(m instanceof z){var r=m.z;if(null!==r){var v=r.h(),x=r.j();if(v instanceof bs){var A=v.ae;if(!0===v.ke){Ps(a,new Ne(1257), new Oe("opIfBlock"));var B=Qs(a,0,!0,O().c,c,d,new Ne(1258));if(B instanceof Ud)no();else if(B instanceof fe){var C=B.aa,D=new vl(A);t();var F=Cq(D,new L(x)),I=G(new H,F,C);var M=nu(a,new z(I,b),c,d);break a}else throw new w(B);}}}}no()}}}M=Km(b)}}finally{a.le=-1+a.le|0}Ds(a,new U(()=>"\x3d "+M));return M} function Nt(a,b,c){var d=!1,e=null,g=Os(a);a:{if(g instanceof z){d=!0;e=g;g=e.z;var h=e.p;if(null!==g){var k=g.h();g=g.j();if(k instanceof as&&"in"===k.Na&&h instanceof z&&(k=h.z,null!==k&&(h=k.h(),k=k.j(),h instanceof as&&"out"===h.Na))){Ps(a,new Ne(1284),new Oe("vinfo"));t();d=G(new H,ou().Yl,xs(g,k));d=new L(d);break a}}}if(d&&(h=e.z,null!==h&&(g=h.h(),h=h.j(),g instanceof as&&"in"===g.Na))){Ps(a,new Ne(1287),new Oe("vinfo"));t();d=G(new H,ou().AA,h);d=new L(d);break a}if(d&&(e=e.z,null!==e&&(d= e.h(),e=e.j(),d instanceof as&&"out"===d.Na))){Ps(a,new Ne(1290),new Oe("vinfo"));t();d=G(new H,ou().Zu,e);d=new L(d);break a}d=t().d}e=Os(a);if(e instanceof z&&(g=e.z,null!==g&&(e=g.h(),g=g.j(),e instanceof bs&&(h=e.ae,!1===e.ke)))){Ps(a,new Ne(1296),new Oe("typeParams"));e=new Ep(h);t();e=Cq(e,new L(g));g=Os(a);if(g instanceof z&&(g=g.z,null!==g&&(g=g.h(),Xr()===g)))return Ps(a,new Ne(1300),new Oe("typeParams")),d.b()?d=R():(d=d.o(),d=new L(d.h())),d=G(new H,d,e),a=Nt(a,b,c),new z(d,a);d.b()?a= R():(a=d.o(),a=new L(a.h()));a=G(new H,a,e);b=O().c;return new z(a,b)}a:{if(d instanceof L&&(b=d.k,null!==b)){b=b.j();c=Ye(new Te(new Ue(J(new K,["dangling variance information"]))),u());t();b=G(new H,c,new L(b));c=O().c;Ze(a,new z(b,c));break a}if(t().d!==d)throw new w(d);}return O().c} function iba(a,b){var c=Me(a,new Ne(1317),new Oe("maybeIndented"));if(c instanceof z&&(c=c.z,null!==c)){var d=c.h();if(d instanceof cs){var e=d.Cc;c=d.td;if(Mk()===e)a:{if(e=c.Jg(),e instanceof L&&(e=e.k,null!==e&&(e=e.h(),e instanceof as&&(e=e.Na,"then"===e||"else"===e)))){e=!1;break a}e=!0}else e=!1;if(e){Ps(a,new Ne(1322),new Oe("maybeIndented"));t();e=new L(d.Mf);d=d.jb();a=new Ft(a,c,e,d);var g=b.ba(a,!0);for(b=Me(a,new Ne(147),new Oe("concludeWith"));;)if(b.b()?c=!1:(c=b.e(),Pe(new E(c.h()), Se())||Pe(new E(c.h()),qs())?(Ps(a,new Ne(147),new Oe("concludeWith")),c=!0):c=!1),c)b=b.f();else break;d=b;a:{if(d instanceof z&&(c=d.z,null!==c)){b=c.h();for(c=c.j();;)if(d.b()?e=!1:(e=d.e().h(),e=Pe(new E(e),Se())),e)d=d.f();else break;d=d.Jg();b=d.b()?G(new H,b,c):d.o();if(null===b)throw new w(b);c=b.h();b=b.j();d=new Te(new Ue(J(new K,["Unexpected "," here"])));c=[We(Xe(),c.jb())];c=Ye(d,J(new K,c));t();b=G(new H,c,new L(b));c=O().c;Ze(a,new z(b,c));break a}b=O().c;if(null===b?null!==d:!b.i(d))throw new w(d); }Ds(a,new U(()=>"Concluded with "+g));return g}}}return b.ba(a,!1)} function Jt(a,b,c){var d=Me(a,new Ne(1317),new Oe("maybeIndented"));a:{if(d instanceof z&&(d=d.z,null!==d)){var e=d.h();if(e instanceof cs){var g=e.Cc;d=e.td;if(Mk()===g)b:{if(g=d.Jg(),g instanceof L&&(g=g.k,null!==g&&(g=g.h(),g instanceof as&&(g=g.Na,"then"===g||"else"===g)))){g=!1;break b}g=!0}else g=!1;if(g){Ps(a,new Ne(1322),new Oe("maybeIndented"));t();g=new L(e.Mf);e=e.jb();a=new Ft(a,d,g,e);var h=Xt(a,!0,Vs().Du,b,c);for(b=Me(a,new Ne(147),new Oe("concludeWith"));;)if(b.b()?c=!1:(c=b.e(),Pe(new E(c.h()), Se())||Pe(new E(c.h()),qs())?(Ps(a,new Ne(147),new Oe("concludeWith")),c=!0):c=!1),c)b=b.f();else break;d=b;b:{if(d instanceof z&&(c=d.z,null!==c)){b=c.h();for(c=c.j();;)if(d.b()?e=!1:(e=d.e().h(),e=Pe(new E(e),Se())),e)d=d.f();else break;d=d.Jg();b=d.b()?G(new H,b,c):d.o();if(null===b)throw new w(b);c=b.h();b=b.j();d=new Te(new Ue(J(new K,["Unexpected "," here"])));c=[We(Xe(),c.jb())];c=Ye(d,J(new K,c));t();b=G(new H,c,new L(b));c=O().c;Ze(a,new z(b,c));break b}b=O().c;if(null===b?null!==d:!b.i(d))throw new w(d); }Ds(a,new U(()=>"Concluded with "+h));a=h;break a}}}a=Xt(a,!1,Vs().Du,b,c)}return a} function Xt(a,b,c,d,e){b=pu(a,O().c,O().c,b,c,d,e);for(d=c=null;b!==u();){var g=b.e();a:{if(null!==g){e=g.h();var h=g.j();if(h instanceof fe){h=h.aa;g=Ye(new Te(new Ue(J(new K,["Unexpected 'then'/'else' clause"]))),u());h=h.A();g=G(new H,g,h);h=O().c;Ze(a,new z(g,h));g=new sm(tm().Cg,Ls(a));e=G(new H,e,g);g=O().c;e=new z(e,g);break a}}if(null!==g&&(e=g.h(),h=g.j(),h instanceof Ud)){e=G(new H,e,h.fa);g=O().c;e=new z(e,g);break a}throw new w(g);}for(e=new Om(e);e.s();)g=new z(e.t(),u()),null===d?c= g:d.p=g,d=g;b=b.f()}return null===c?u():c} function pu(a,b,c,d,e,g,h){var k=new U(()=>G(new H,b,c)),l=new Ne(1360),m=new Oe("argsOrIf");Ds(a,new U(()=>{var pc=m.fn,sc=Es(k);sc=Fs(sc)?ze(sc,"(",",",")"):Gs(sc)?Es(k):"("+Es(k)+")";return"@ "+pc+sc+" [at l."+l.Zl+"]"}));try{a.le=1+a.le|0;var n=!1,r=null,v=Me(a,new Ne(1362),new Oe("argsOrIf"));a:{var x=O().c;if(null===x?null===v:x.i(v))if(c instanceof z){var A=c.z,B=c.p,C=t().d;t();var D=new sm(tm().Cg,new Sl(Km(new z(A,B)))),F=G(new H,C,new Ud(D));var I=Km(new z(F,b));break a}else{var M=O().c; if(null===M?null===c:M.i(c)){I=Km(b);break a}else throw new w(c);}if(v instanceof z){n=!0;r=v;var N=r.z;if(null!==N){var P=N.h();if(Se()===P){Ps(a,new Ne(1371),new Oe("argsOrIf"));I=pu(a,b,c,d,e,g,h);break a}}}if(n){var T=r.z;if(null!==T){var Y=T.h();if(qs()===Y){up(tp(),c.b());I=Km(b);break a}}}if(n){var Z=r.z;if(null!==Z){var S=Z.h();if(S instanceof bs){var ea=S.ae;if(!0===S.ke&&Nm(new E(ea),"-")){up(tp(),c.b());I=Km(b);break a}}}}var ia=Os(a);b:{if(ia instanceof z){var X=ia.z;if(null!==X){var sa= X.h(),Ja=X.j();if(sa instanceof as&&"val"===sa.Na){Ps(a,new Ne(1385),new Oe("argVal"));t();var Xa=new L(Ja);break b}}}Xa=t().d}var Fa=Os(a);b:{if(Fa instanceof z){var za=Fa.z;if(null!==za){var Qa=za.h(),Ma=za.j();if(Qa instanceof as&&"mut"===Qa.Na){Ps(a,new Ne(1391),new Oe("argMut"));t();var Ga=new L(Ma);break b}}}Ga=t().d}var ab=Os(a);b:{if(ab instanceof z){var Hb=ab.z;if(null!==Hb){var bc=Hb.h(),yb=Hb.j();if(bc instanceof as&&"#"===bc.Na){Ps(a,new Ne(1397),new Oe("argSpec"));t();var tb=new L(yb); break b}}}tb=t().d}n=!1;r=null;var eb=Os(a);b:{if(eb instanceof z){n=!0;r=eb;var kb=r.z,Rb=r.p;if(null!==kb){var Gb=kb.h(),vb=kb.j();if(Gb instanceof bs){var Tb=Gb.ae;if(!1===Gb.ke&&Rb instanceof z){var Nb=Rb.z;if(null!==Nb){var ic=Nb.h();if(ic instanceof as&&":"===ic.Na){Ps(a,new Ne(1403),new Oe("argName"));Ps(a,new Ne(1404),new Oe("argName"));t();var Va=new vl(Tb);t();var cb=Cq(Va,new L(vb));var zb=new L(cb);break b}}}}}}if(n){var Ub=r.z,jb=r.p;if(null!==Ub){var db=Ub.h(),ub=Ub.j();if(db instanceof fs){var Aa=db.Cu;if(Aa instanceof Em){var va=Aa.rq;if(jb instanceof z){var Ra=jb.z;if(null!==Ra){var rb=Ra.h();if(rb instanceof as&&":"===rb.Na){Ps(a,new Ne(1407),new Oe("argName"));Ps(a,new Ne(1408),new Oe("argName"));t();var xb=new vl(va.u());t();var mc=Cq(xb,new L(ub));zb=new L(mc);break b}}}}}}}zb=t().d}var Ha=Qs(a,e,!0,O().c,h,g,new Ne(1413));if(Ha instanceof Ud)var Ka=Ha.fa,Oa=new Ud(new sm(new St(!Ga.b(),!tb.b(),!Xa.b()),Ka));else Oa=Ha;e=!1;Xa=null;var Na=Me(a,new Ne(1421),new Oe("argsOrIf")); if(Na instanceof z){e=!0;Xa=Na;var Da=Xa.z,ta=Xa.p;if(null!==Da){var Ya=Da.h();if(Xr()===Ya&&ta instanceof z){var dc=ta.z;if(null!==dc){var ka=dc.h();if(qs()===ka){Ps(a,new Ne(1423),new Oe("argsOrIf"));Ps(a,new Ne(1424),new Oe("argsOrIf"));var ya=kt(c,zb,Oa);I=pu(a,new z(ya,b),O().c,d,Vs().Du,g,h);break a}}}}}if(e){var Sa=Xa.z;if(null!==Sa){var xc=Sa.h();if(Xr()===xc){Ps(a,new Ne(1427),new Oe("argsOrIf"));var Sb=kt(c,zb,Oa);I=pu(a,new z(Sb,b),O().c,d,Vs().Du,g,h);break a}}}if(e){var uc=Xa.z;if(null!== uc){var Lb=uc.h();if(qs()===Lb&&d){Ps(a,new Ne(1430),new Oe("argsOrIf"));if(zb instanceof L){var lc=zb.k,Xb=Ye(new Te(new Ue(J(new K,["Unexpected named argument name here"]))),u()),ec=lc.A(),Ab=G(new H,Xb,ec),Ob=O().c;Ze(a,new z(Ab,Ob))}else if(t().d!==zb)throw new w(zb);zb=!1;Oa instanceof fe&&no();if(Oa instanceof Ud){zb=!0;var fb=Oa.fa;if(null!==fb){var Wa=fb.yb,bb=fb.ya;if(null!==Wa){var Ia=Wa.ch;if(!1===Wa.je&&!1===Ia){I=pu(a,b,new z(bb,c),d,Vs().Du,g,h);break a}}}}zb&&no();throw new w(Oa);}}}var Ua= kt(c,zb,Oa);I=Km(new z(Ua,b))}}finally{a.le=-1+a.le|0}Ds(a,new U(()=>"\x3d "+I));return I} function bba(a,b,c){for(;;){var d=!1,e=null,g=Me(a,new Ne(1450),new Oe("bindings"));if(g instanceof z&&(d=!0,e=g,g=e.z,null!==g&&(g=g.h(),Se()===g))){Ps(a,new Ne(1452),new Oe("bindings"));continue}if(d&&(g=e.z,null!==g&&(g=g.h(),g=qs()===g?!0:g instanceof bs&&!0===g.ke?!0:ds()===g?!0:!1,g)))return Km(b);if(d&&(d=e.z,null!==d&&(e=d.h(),d=d.j(),e instanceof bs&&(g=e.ae,!1===e.ke)))){Ps(a,new Ne(1457),new Oe("bindings"));e=zt(a,new as("\x3d"),Bt(),!1,new U(()=>O().c),c);if(null===e)throw new w(e);e= e.Rc();e=tt(Vs(),c)||!e;e=Us(a,0,e,new Ne(1459));g=new vl(g);t();d=Cq(g,new L(d));g=Me(a,new Ne(1466),new Oe("bindings"));if(g instanceof z&&(g=g.z,null!==g&&(g=g.h(),Xr()===g))){Ps(a,new Ne(1468),new Oe("bindings"));d=G(new H,d,e);b=new z(d,b);continue}a=G(new H,d,e);return Km(new z(a,b))}return O().c}}function It(a,b){b=Qn(b);if(b instanceof fe)return b=b.aa,tt(Vs(),!1)||a.Fu.n(b),gl();if(b instanceof Ud)return b.fa;throw new w(b);} function qu(){this.Du=this.xN=0;this.ao=null;this.MH=0;Bu=this;this.xN=0;this.Du=1+this.xN|0;O();var a=J(new K,";;;;;;,;\x3d;@;:;|;/ \\;^;\x26;!;\x3c \x3e;+ -;* %;;.".split(";"));a=Pd(u(),a);a=Hf(a);for(var b=null,c=null;a!==u();){var d=a.e();if(null===d)throw new w(d);var e=d.h();d=d.Sc();for(var g=e.length,h=Cu(g),k=0;k{Es(d);Ps(this.LH,new Ne(96),new Oe("unapply"))}),a);return new L(a)}}t();a=G(new H,new U(()=>{}),a);return new L(a)};qt.prototype.$classData=q({UW:0},!1,"mlscript.NewParser$Spaces$",{UW:1,g:1}); function Ju(a,b){if(a instanceof Ku){var c=a.fc,d=a.vd,e=a.be,g=a.Me;a=b?Lu(e):e;c.b()?e=!0:(e=c.o(),e instanceof Mu?(e=e.pd,Q(),e=new Ep(Nu(0,e.Dh)),e=!!g.U(e).b()):e=!0);e=e?c:R();c=new Ou(g);e.b()||(g=e.o(),e=V(g.q),a=Pu(g,a,e,!1));a=Qu(c,a,new fn((h,k)=>{var l=V(h.q);return Pu(h,k,l,!1)}));if(b){ms();if(0<=d.Q())b=d.Q(),b=new (md(Ru).Ia)(b),d.Gc(b,0,2147483647),d=b;else{b=null;b=[];for(d=d.m();d.s();)c=d.t(),b.push(null===c?null:c);d=new (md(Ru).Ia)(b)}b=Su();c=op().ga;b=Tu(d,new Uu(b,c))}else if(0<= d.Q())b=d.Q(),b=new (md(Ru).Ia)(b),d.Gc(b,0,2147483647);else{b=null;b=[];for(d=d.m();d.s();)c=d.t(),b.push(null===c?null:c);b=new (md(Ru).Ia)(b)}d=(h,k)=>{var l=V(h.q);return Pu(h,k,l,!1)};if(null===b)throw le();if(null!==b){c=b.a.length;for(g=0;gm.ub(a,b)));g=kv(g,new U(()=>{var m=c.m();return new Ef(m,new y(n=>n.ub(a,b)))})).nb(new U(()=>{lv();var m=d.Ba.m();m=mv(0,m);return new Ef(m,new y(n=>n.ub(a,b)))})).nb(new U(()=>{lv();var m=e.m();m=mv(0,m);return new Ef(m,new y(n=>n.ub(a,b)))}));if(g.s()){if(!g.s())throw nv("empty.reduceLeft");for(var h=!0,k=null;g.s();){var l=g.t();h?(k=l,h=!1):(k|=0,l|=0,k=k>l?k:l)}g=new L(k)}else g=R(); return(g.b()?this.Fa.Gd:g.o())|0}g=Vu(this.Fa);if(null!==g&&g===this)return this.Fa.Gd;throw new w(this);};function qba(a,b,c,d,e){if(a instanceof Ku){var g=a.fc,h=a.vd,k=a.be,l=a.Me;a=a.Fa;g.b()?g=R():(g=g.o(),g=new L(g.At(b,c,d,e)));k=ov(k,b,c,d,e);l=new pv(new qv(l),new y(v=>rv(v,b,c,d,e)));var m=sv(),n=Su(),r=op().ga;return new Ku(a,g,h,k,(new tv(m,new Uu(n,r))).vc(l))}h=Vu(a.Fa);if(null!==h&&h===a)return a;throw new w(a);} function rba(a,b){var c=Vu(a.Fa);if(null!==c&&c===a){c=a.Fa;var d=t().d;uv();var e=Su(),g=op().ga;b=vv(b,new Uu(e,g));a=wv(xv(a.Fa));e=sv();g=Su();var h=op().ga;return new Ku(c,d,b,a,e.Hd(new Uu(g,h)))}if(a instanceof Ku)return c=a.be,d=a.Me,new Ku(a.Fa,a.fc,a.vd.bc(b),c,d);throw new w(a);} function yv(a,b,c,d,e){var g=G(new H,a,b),h=g.y,k=g.w;if(Vu(a.Fa)===h&&k instanceof zv){t();var l=a.Fa;t();var m=new L(k),n=uv(),r=Su(),v=op().ga,x=n.ng(new Uu(r,v)),A=Ev(e)?k.kq():wv(xv(a.Fa)),B=sv(),C=Su(),D=op().ga,F=new Ku(l,m,x,A,B.Hd(new Uu(C,D)));return new L(F)}var I=g.y,M=g.w;if(Vu(a.Fa)===I&&M instanceof Fv){t();var N=a.Fa;t();var P=new L(M),T=uv(),Y=Su(),Z=op().ga,S=T.ng(new Uu(Y,Z)),ea=wv(xv(a.Fa)),ia=sv(),X=Su(),sa=op().ga,Ja=new Ku(N,P,S,ea,ia.Hd(new Uu(X,sa)));return new L(Ja)}var Xa= g.y,Fa=g.w;if(Vu(a.Fa)===Xa&&Fa instanceof Gv){t();var za=a.Fa,Qa=t().d;uv();var Ma=Su(),Ga=op().ga,ab=vv(Fa,new Uu(Ma,Ga)),Hb=wv(xv(a.Fa)),bc=sv(),yb=Su(),tb=op().ga,eb=new Ku(za,Qa,ab,Hb,bc.Hd(new Uu(yb,tb)));return new L(eb)}var kb=g.y,Rb=g.w;if(kb instanceof Ku){var Gb=kb.fc,vb=kb.vd,Tb=kb.be,Nb=kb.Me;if(Rb instanceof Gv){t();var ic=new Ku(a.Fa,Gb,vb.bc(Rb),Tb,Nb);return new L(ic)}}var Va=g.y,cb=g.w;if(Va instanceof Ku){var zb=Va.fc,Ub=Va.vd,jb=Va.be,db=Va.Me;if(cb instanceof Fv){var ub=null; ub=jb;var Aa=G(new H,zb,cb);a:{var va=Aa.y,Ra=Aa.w;if(va instanceof L){var rb=va.k;if(rb instanceof Mu&&Ra instanceof Mu){var xb=sba(rb,Ra);break a}}var mc=Aa.y,Ha=Aa.w;if(mc instanceof L){var Ka=mc.k;if(Ka instanceof cv){var Oa=Ka.Nb,Na=Ka.ac;if(Ha instanceof cv){var Da=Ha.Nb,ta=Ha.ac;if(a.Fa.UI&&!c){t();var Ya=a.Fa,dc=V(Oa.q),ka=dv(Oa,Da,dc,!1),ya=V(Na.q),Sa=new cv(Ya,ka,Pu(Na,ta,ya,!1),Ka.Mj);xb=new L(Sa);break a}}}}var xc=Aa.y;if(xc instanceof L){var Sb=xc.k;if(Sb instanceof cv){var uc=Sb.Nb; if(null!==uc){var Lb=Hv(Iv(a.Fa),uc,d);if(!Lb.b()){var lc=Lb.k;if(lc instanceof zv){var Xb=lc.Yb;if(Aa.w instanceof Jv||Aa.w instanceof cv)b:{for(var ec=Xb;!ec.b();){var Ab=ec.e().j().ra;if(Kv(Ab,d)){var Ob=!0;break b}ec=ec.f()}Ob=!1}else Ob=!1;if(Ob){t();xb=new L(cb);break a}}}}}}var fb=Aa.y,Wa=Aa.w;if(fb instanceof L&&fb.k instanceof Jv&&Wa instanceof cv){var bb=Wa.Nb;if(null!==bb){var Ia=Hv(Iv(a.Fa),bb,d);if(!Ia.b()){var Ua=Ia.k;if(Ua instanceof zv){b:{for(var pc=Ua.Yb;!pc.b();){var sc=pc.e().j().ra; if(Kv(sc,d)){var Ba=!0;break b}pc=pc.f()}Ba=!1}if(Ba){xb=fb;break a}}}}}var ob=Aa.y,nc=Aa.w;if(ob instanceof L){var Ib=ob.k;if(Ib instanceof Jv){var vc=Ib.gi;if(nc instanceof cv){t();var Vb=new Jv(a.Fa,nba(a,vc,nc,d),Ib.Mq);xb=new L(Vb);break a}}}var fc=Aa.y;if(fc instanceof L){var Bc=fc.k;if(Bc instanceof cv&&(Aa.w instanceof Jv||Aa.w instanceof cv)){var Pb=a.Fa;t();var Jb=a.Fa,gc=O().c,Cb=new Jv(Jb,new z(Bc,gc),cb.ma());return yv(new Ku(Pb,new L(Cb),Ub,jb,db),cb,c,d,e)}}var cc=Aa.y,yc=Aa.w;if(cc instanceof L&&cc.k instanceof Jv&&yc instanceof Jv){var Mc=yc.gi;if(Mc instanceof z){var qc=Mc.z,oc=Mc.p,Qc=O().c;if(null===Qc?null===oc:Qc.i(oc))return yv(a,qc,c,d,e);var jc=yv(a,qc,c,d,e);return jc.b()?R():yv(jc.o(),new Jv(a.Fa,oc,yc.Mq),c,d,e)}}var sb=Aa.y,Gc=Aa.w;if(sb instanceof L){var Wb=sb.k;if(Wb instanceof zv){var Cc=Wb.Yb;if(Gc instanceof zv){var Fc=Gc.Yb,qd=Cc.K();if(Pe(new E(qd),Fc.K())){if(Ev(e)){for(var Yb=a.Fa,Nc=ub.Ba,ad=Gc.kq().Ba,Uc=Su(),cd=op().ga,kc=new Uu(Uc,cd),Vc=sv(),Hc=Nc.Q()+ad.Q()| 0,rc=Lv(Lv(Mv(8{var Oj=Wi.h();Wi=Nv(ra,Wi.j(),V(ra.Va)); return G(new H,Oj,Wi)};if(wc===u())var jf=u();else{for(var af=wc.e(),pf=new z(ye(af),u()),kf=pf,Be=wc.f();Be!==u();){var Kd=Be.e(),ld=new z(ye(Kd),u());kf=kf.p=ld;Be=Be.f()}jf=pf}var Jd=new zv(de,jf,V(a.Fa));xb=new L(Jd);break a}}}var Dd=Aa.y,Xd=Aa.w;if(Dd instanceof L){var Yc=Dd.k;if(Yc instanceof zv){var Ce=Yc.Yb;if(Xd instanceof Sv){var te=Xd.Fd;t();var Ie=a.Fa,Jf=Wi=>{var Oj=Wi.h();Wi=Nv(Wi.j(),te,V(Wi.j().Va));return G(new H,Oj,Wi)};if(Ce===u())var df=u();else{for(var vg=Ce.e(),wg=new z(Jf(vg), u()),xg=wg,eg=Ce.f();eg!==u();){var vh=eg.e(),fg=new z(Jf(vh),u());xg=xg.p=fg;eg=eg.f()}df=wg}var ih=new zv(Ie,df,V(a.Fa));xb=new L(ih);break a}}}var Ig=Aa.y,Tf=Aa.w;if(Ig instanceof L){var Jg=Ig.k;if(Jg instanceof Sv){var jh=Jg.Fd;if(Tf instanceof Sv){var yg=Tf.Fd;t();var gg=a.Fa,Cf=Nv(jh,yg,V(jh.Va)),Uf=new Sv(gg,Cf,V(a.Fa));xb=new L(Uf);break a}}}var $g=Aa.y,Ah=Aa.w;if($g instanceof L){var Kg=$g.k;if(Kg instanceof Tv){var Vf=Kg.Ic,hg=Kg.kf;if(Ah instanceof Tv){var zg=Ah.Ic;if(Pe(new E(hg),Ah.kf)){t(); var Lg=a.Fa,Mg=V(Vf.q),Wf=Pu(Vf,zg,Mg,!1),Ng=new Tv(Lg,Wf,hg,Kg.vp);xb=new L(Ng);break a}}}}var Kf=Aa.y,xf=Aa.w;if(Kf instanceof L){var Og=Kf.k;if(xf instanceof Tv){t();var mi=a.Fa,Ci=V(Og.q),Xh=Pu(Og,xf,Ci,!1),wh=uv(),Bh=Su(),ng=op().ga,kh=wh.ng(new Uu(Bh,ng)),Kh=new Tv(mi,Xh,kh,V(a.Fa));xb=new L(Kh);break a}}var ni=Aa.y,Lh=Aa.w;if(ni instanceof L){var lh=ni.k;if(lh instanceof Tv){t();var Ch=a.Fa,Dh=V(lh.q),Yh=Pu(lh,Lh,Dh,!1),ah=uv(),oi=Su(),mj=op().ga,wd=ah.ng(new Uu(oi,mj)),ge=new Tv(Ch,Yh,wd, V(a.Fa));xb=new L(ge);break a}}var De=Aa.y;if(De instanceof L&&Uv(De.k)&&De.k.q===a.Fa&&(Aa.w instanceof cv||Aa.w instanceof Vv||Aa.w instanceof Jv))var qf=!0;else{var og=Aa.y;if(og instanceof L&&(og.k instanceof cv||og.k instanceof Vv||og.k instanceof Jv)&&Uv(Aa.w)&&Aa.w.q===a.Fa)qf=!0;else{var Xf=Aa.y;if(Xf instanceof L&&Xf.k instanceof cv&&(Aa.w instanceof Vv||Aa.w instanceof Jv))qf=!0;else{var mh=Aa.y;if(mh instanceof L&&(mh.k instanceof Vv||mh.k instanceof Jv)&&Aa.w instanceof cv)qf=!0;else{var Ag= Aa.y;if(Ag instanceof L&&Ag.k instanceof Vv&&Aa.w instanceof Jv)qf=!0;else{var Bg=Aa.y;qf=Bg instanceof L&&Bg.k instanceof Jv&&Aa.w instanceof Vv?!0:!1}}}}}if(qf)xb=t().d;else{b:{var Eh=Aa.y;if(Eh instanceof L){var Pg=Eh.k;if(Pg instanceof Jv){var Di=Pg.gi,Mh=O().c;if(null===Mh?null===Di:Mh.i(Di)){var pi=!0;break b}}}var Xi=Aa.w;if(Aa.y instanceof L&&Xi instanceof Jv){var Qg=Xi.gi,nh=O().c;if(null===nh?null===Qg:nh.i(Qg)){pi=!0;break b}}pi=!1}pi&&xm("Program reached and unexpected state.");var bh= Aa.y;var Mj=bh instanceof L&&bh.k instanceof Wv?!0:Aa.y instanceof L&&Aa.w instanceof Wv?!0:!1;Mj&&no();var Nj=Aa.y,ie=Aa.w;if(t().d===Nj&&ie instanceof zv){if(Ev(e)){for(var Ac=a.Fa,Ve=ub.Ba,Td=ie.kq().Ba,lf=Su(),Yi=op().ga,Jl=new Uu(lf,Yi),ll=sv(),Bj=Ve.Q()+Td.Q()|0,$k=Lv(Lv(Mv(8{var B=r.rc.e();r.rc=r.rc.f();var C=!1,D=null;if(x instanceof L&&(C= !0,D=x,!0===!!D.k))return x=V(A.q),Pu(A,B,x,!1);if(C&&!1===!!D.k)return x=V(A.q),dv(A,B,x,!1);if(t().d===x){if(c)return x=cw(a.Fa),C=V(A.q),C=dv(A,B,C,!1),D=V(A.q),dw(x,C,Pu(A,B,D,!1),ew(cw(a.Fa)),d);x=cw(a.Fa);C=V(A.q);C=Pu(A,B,C,!1);D=V(A.q);return dw(x,C,dv(A,B,D,!1),ew(cw(a.Fa)),d)}throw new w(x);}),d);n=new fw(a.Fa,b.qb,n,b.Xl)}l=l.Dm(m,n);g=new Ku(a.Fa,g,h,k,l);b=gw(b,d);b.b()?(t(),b=new L(g)):(b=b.o(),b=yv(g,b,c,d,e));return b}throw new w(a);} function hw(a,b,c,d,e){var g=tc();try{var h=G(new H,a,b),k=h.w;if(Vu(a.Fa)===k)return t(),new L(a);var l=h.y;if(Vu(a.Fa)===l)return t(),new L(b);var m=h.w;if(m instanceof Ku){var n=m.fc,r=m.be,v=m.Me,x=m.vd.m(),A=new Ou(v);if(n.b())var B=it().n(Xv(a,r));else{var C=n.o();B=yv(Xv(a,r),C,c,d,e)}for(a=B;A.Ot.s();){b=a;var D=A.t();h=D;if(b.b())throw Hq(new Iq,g,t().d);a=Zv(b.o(),h,c,d,e)}for(A=a;x.s();){D=A;var F=x.t();a=F;if(D.b())throw Hq(new Iq,g,t().d);A=yv(D.o(),a,c,d,e)}return A}throw new w(h);}catch(I){if(I instanceof Iq){c=I;if(c.Qg===g)return c.Cj();throw c;}throw I;}} f.Dw=function(a,b){var c=G(new H,this,a),d=c.y,e=c.w;if(d instanceof Ku&&(d=d.be,e instanceof iw))return a=this.Fa,c=G(new H,e.me,e.Ne),e=O().c,a=new Qv(a,new z(c,e),V(this.Fa)),c=Xu().X(),Zu(d,a,b,!0,c);var g=c.y,h=c.w;if(g instanceof Ku&&(d=g.fc,h instanceof jw)){e=h.mb;c=h.Dc;a:{for(h=h.Xb;!h.b();){var k=h.e();b:{if(d instanceof L){var l=d.k;if(l instanceof Mu){k=Pe(new E(k.uo()),l.pd)?!0:(g.rE?g.qE:kw(g)).L(k.uo());break b}}k=!1}if(k){g=!0;break a}h=h.f()}g=!1}if(g)a=!0;else a:{if(g=!1,h=null, e instanceof L&&(g=!0,h=e,k=h.k,k instanceof Ud&&(k=k.fa,null!==k))){a=this.Dw(k,b);break a}if(g&&(k=h.k,k instanceof fe&&(k=k.aa,k instanceof cv||k instanceof Jv||k instanceof Vv||k instanceof zv))){d.b()?a=!1:(a=d.o(),d=Xu().X(),a=Zu(a,k,b,!0,d));break a}if(g&&(d=h.k,d instanceof fe&&d.aa instanceof Tv))return a=a.mc(),this.mc(),this.mc(),c=Xu().X(),d=this.mc(),Zu(d,a,b,!0,c);if(t().d===e)a=!1;else throw new w(e);}if(a)return!0;d=!1;for(a=c.m();!d&&a.s();){c=a.t();if(null===c)throw new w(c);c=c.j(); this.mc();this.mc();d=Xu().X();e=this.mc();d=Zu(e,c,b,!0,d)}return d}b=c.y;if(Vu(this.Fa)===b)return!1;b=c.w;if(lw(this.Fa)===b)return!1;throw new w(c);}; function mw(a,b,c){var d=G(new H,a,b);b=d.w;if(Vu(a.Fa)===b)return!0;b=d.y;if(Vu(a.Fa)===b)return!1;a=d.y;var e=d.w;if(a instanceof Ku){var g=a.fc,h=a.vd;b=a.be;a=a.Me;if(e instanceof Ku){var k=e.fc,l=e.vd;d=e.be;e=e.Me;if(k.b())g=!0;else if(k=k.o(),g.b())g=!1;else{g=g.o();var m=Xu().X();g=Zu(g,k,c,!0,m)}g&&nw(l,h)?(h=Xu().X(),b=Zu(b,d,c,!0,h)):b=!1;if(b){b=new Ou(e);for(h=!0;h&&b.Ot.s();){h=b.t();d=new Ou(a);for(e=!1;!e&&d.Ot.s();)e=d.t(),l=Xu().X(),e=Zu(e,h,c,!0,l);h=e}return h}return!1}}throw new w(d); }function ow(a){return Vu(a.Fa)===a} function pw(a,b){if(a instanceof iw){var c=a.Aa;b=G(new H,a.me,a.Ne);var d=O().c;return new Qv(c,new z(b,d),V(a.Aa))}if(a instanceof jw){c=a.Xb;var e=a.mb;d=a.Dc;if(e.b())a=a.Aa.ib;else if(a=e.o(),a instanceof Ud)a=a.fa.zf(b);else{if(!(a instanceof fe))throw new w(a);a=a.aa}d=new Ou(d);a=Qu(d,a,new fn((g,h)=>{var k=V(g.q);return dv(g,h,k,!1)}));b&&(b=Su(),d=op().ga,c=qw(c,new Uu(b,d)));return c.De(a,new fn((g,h)=>{var k=V(g.q);return dv(g,h,k,!1)}))}c=lw(a.Aa);if(null!==c&&c===a)return a.Aa.ib;throw new w(a); }function rw(a,b){if(null===b)throw null;a.Aa=b}function sw(){this.Nz=null;this.jx=!1;this.Aa=null}sw.prototype=new p;sw.prototype.constructor=sw;function tw(){}tw.prototype=sw.prototype; function tba(a,b){var c=G(new H,a,b),d=c.y;b=c.w;if(d instanceof iw&&(d=d.me,b instanceof iw))return pa(d.Dh,b.me.Dh);b=c.y;var e=c.w;if(b instanceof jw&&(d=b.Xb,b=b.Dc,e instanceof jw)){c=e.Xb;a=e.Dc;e=Su();var g=op().ga;e=Gq(d,new Uu(e,g));if(e instanceof L){e=e.k;g=Su();var h=op().ga;g=Gq(c,new Uu(g,h));if(g instanceof L)c=uw(e,g.k);else{if(t().d!==g)throw new w(g);d=d.K();d=new vw(d);c=c.K();Fq();d=d.al;c=d===c?0:d{var x=n.rc.e();n.rc=n.rc.f();var A=!1,B=null;if(r instanceof L&&(A=!0,B=r,!0===!!B.k))return r=V(v.q),dv(v,x,r,!1);if(A&&!1===!!B.k)return r=V(v.q),Pu(v,x,r,!1);if(t().d===r){if(c)return r=cw(a.Aa),A=V(v.q),A=Pu(v,x,A,!1),B=V(v.q),dw(r,A,dv(v,x,B,!1),ew(cw(a.Aa)),d);r=cw(a.Aa);A=V(v.q);A=dv(v,x,A,!1);B=V(v.q);return dw(r,A,Pu(v,x,B,!1),ew(cw(a.Aa)),d)}throw new w(r);}),d);b=new fw(a.Aa,b.qb,l,b.Xl)}b=h.Dm(k,b);t();b=new jw(a.Aa,e,g,b);return new L(b)}throw new w(a);} function yw(a,b,c,d){var e=tc();try{if(b instanceof jw){for(var g=b.Xb,h=b.mb,k=new Ou(b.Dc);k.Ot.s();){var l=a,m=k.t(),n=ww(l,m,c,d);if(n.b())throw Hq(new Iq,e,t().d);a=n.o()}for(;!g.b();){c=a;var r=g.e(),v=zw(c,r);if(v.b())throw Hq(new Iq,e,t().d);a=v.o();g=g.f()}r=a;t();if(h.b())var x=r;else{var A=h.o();if(A instanceof Ud){var B=A.fa;var C=G(new H,B.me,B.Ne);var D=Aw(r,C);if(D.b())throw Hq(new Iq,e,t().d);x=D.o()}else{if(!(A instanceof fe))throw new w(A);var F=zw(r,A.aa);if(F.b())throw Hq(new Iq, e,t().d);x=F.o()}}return new L(x)}if(b instanceof iw){l=b.me;var I=b.Ne;h=!1;x=null;var M=lw(a.Aa);if(null!==M&&M===a){t();var N=new iw(a.Aa,l,I);return new L(N)}if(a instanceof iw){var P=a.me,T=a.Ne;if(Pe(new E(P),l)){t();var Y=new iw(a.Aa,P,xw(T,I,V(T.Va)));return new L(Y)}}if(a instanceof jw){h=!0;x=a;var Z=x.Xb,S=x.mb,ea=x.Dc;if(t().d===S){t();var ia=a.Aa;t();t();var X=new iw(a.Aa,l,I),sa=new jw(ia,Z,new L(new Ud(X)),ea);return new L(sa)}}if(h){var Ja=x.Xb,Xa=x.mb,Fa=x.Dc;if(Xa instanceof L){var za= Xa.k;if(za instanceof Ud){var Qa=za.fa;if(null!==Qa){var Ma=Qa.me,Ga=Qa.Ne;if(Pe(new E(Ma),l)){t();var ab=a.Aa;t();t();var Hb=new iw(a.Aa,Ma,xw(Ga,I,V(Ga.Va))),bc=new jw(ab,Ja,new L(new Ud(Hb)),Fa);return new L(bc)}}}}}if(a instanceof iw||a instanceof jw)return t().d;throw new w(a);}if(lw(a.Aa)===b)return t(),new L(a);throw new w(b);}catch(yb){if(yb instanceof Iq){h=yb;if(h.Qg===e)return h.Cj();throw h;}throw yb;}} function uba(a,b,c){var d=lw(a.Aa);if(null!==d&&d===a||lw(a.Aa)===b)return t(),a=lw(a.Aa),new L(a);if(a instanceof iw){d=a.me;var e=a.Ne;if(b instanceof iw){var g=b.Ne;if(Pe(new E(d),b.me))return t(),a=new iw(a.Aa,d,Nv(e,g,V(e.Va))),new L(a)}}if(a instanceof jw&&(d=a.Xb,g=a.mb,e=a.Dc,g instanceof L&&(g=g.k,g instanceof Ud&&(g=g.fa,b instanceof jw)))){var h=b.Xb,k=b.mb,l=b.Dc;if(k instanceof L&&(k=k.k,k instanceof Ud&&(k=k.fa,Pe(new E(d),h)&&Pe(new E(e),l)&&Pe(new E(g.me),k.me))))return t(),c=a.Aa, t(),t(),a=new iw(a.Aa,g.me,Nv(g.Ne,k.Ne,V(g.Ne.Va))),a=new jw(c,d,new L(new Ud(a)),e),new L(a)}return a instanceof jw&&(d=a.Xb,e=a.mb,g=a.Dc,b instanceof jw&&(l=b.mb,h=b.Dc,Pe(new E(d),b.Xb)&&Pe(new E(e),l)?(b=g.AB(),b=Pe(new E(b),h.AB())):b=!1,b))?(t(),a=new jw(a.Aa,d,e,Bw(a.Aa,!1,g,h,c)),new L(a)):t().d} function vba(a,b){var c=lw(a.Aa);if(null!==c&&c===a){c=a.Aa;a=O().c;b=new z(b,a);a=t().d;var d=sv(),e=Su(),g=op().ga;return new jw(c,b,a,d.Hd(new Uu(e,g)))}if(a instanceof iw)return c=a.Aa,d=O().c,b=new z(b,d),t(),t(),a=new L(new Ud(a)),d=sv(),e=Su(),g=op().ga,new jw(c,b,a,d.Hd(new Uu(e,g)));if(a instanceof jw)return new jw(a.Aa,new z(b,a.Xb),a.mb,a.Dc);throw new w(a);} function zw(a,b){var c=G(new H,a,b),d=c.y,e=c.w;if(lw(a.Aa)===d&&Uv(e)){t();var g=a.Aa,h=O().c,k=new z(e,h),l=t().d,m=sv(),n=Su(),r=op().ga,v=new jw(g,k,l,m.Hd(new Uu(n,r)));return new L(v)}var x=c.y,A=c.w;if(lw(a.Aa)===x&&A instanceof Cw){t();var B=a.Aa,C=O().c;t();t();var D=new L(new fe(A)),F=sv(),I=Su(),M=op().ga,N=new jw(B,C,D,F.Hd(new Uu(I,M)));return new L(N)}var P=c.y,T=c.w;if(P instanceof jw){var Y=P.Xb,Z=P.mb,S=P.Dc;if(T instanceof Mu){t();var ea=new jw(a.Aa,Y.L(T)?Y:new z(T,Y),Z,S);return new L(ea)}}var ia= c.y,X=c.w;if(ia instanceof jw){var sa=ia.Xb,Ja=ia.mb,Xa=ia.Dc;if(t().d===Ja&&X instanceof Cw){t();var Fa=a.Aa;t();t();var za=new jw(Fa,sa,new L(new fe(X)),Xa);return new L(za)}}var Qa=c.y,Ma=c.w;if(Qa instanceof jw){var Ga=Qa.Xb,ab=Qa.mb,Hb=Qa.Dc;if(ab instanceof L){var bc=ab.k;if(bc instanceof fe){var yb=bc.aa;if(yb instanceof zv){var tb=yb.Yb;if(Ma instanceof zv){var eb=Ma.Yb,kb=tb.K();if(Nm(new E(kb),eb.K())){var Rb=a.Aa;t();t();var Gb=Dw(yb);return zw(new jw(Rb,Ga,new L(new fe(Gb)),Hb),Dw(Ma))}t(); var vb=a.Aa;t();t();var Tb=a.Aa,Nb=new Wq(tb,tb,eb),ic=new fn((ld,Jd)=>{ld=G(new H,ld,Jd);Jd=ld.y;var Dd=ld.w;if(null!==Jd){var Xd=Jd.h();Jd=Jd.j();if(Xd instanceof L&&(Xd=Xd.k,null!==Dd)){var Yc=Dd.h();Dd=Dd.j();if(Yc instanceof L)return ld=Pe(new E(Xd),Yc.k)?(t(),new L(Xd)):t().d,G(new H,ld,xw(Jd,Dd,V(Jd.Va)))}}Dd=ld.y;Xd=ld.w;if(null!==Dd&&(Jd=Dd.h(),Dd=Dd.j(),null!==Xd))return ld=Xd.h(),Xd=Xd.j(),ld=Jd.b()?ld:Jd,G(new H,ld,xw(Dd,Xd,V(Dd.Va)));throw new w(ld);});Yq();var Va=Ew(Nb,ic),cb=new zv(Tb, Va,V(a.Aa)),zb=new jw(vb,Ga,new L(new fe(cb)),Hb);return new L(zb)}}}}}var Ub=c.y,jb=c.w;if(Ub instanceof jw){var db=Ub.mb;if(db instanceof L){var ub=db.k;if(ub instanceof fe&&ub.aa instanceof Sv&&jb instanceof zv)return zw(a,Dw(jb))}}var Aa=c.y,va=c.w;if(Aa instanceof jw){var Ra=Aa.Xb,rb=Aa.mb,xb=Aa.Dc;if(rb instanceof L){var mc=rb.k;if(mc instanceof fe){var Ha=mc.aa;if(Ha instanceof zv&&va instanceof Sv){var Ka=a.Aa;t();t();var Oa=Dw(Ha);return zw(new jw(Ka,Ra,new L(new fe(Oa)),xb),va)}}}}var Na= c.y,Da=c.w;if(Na instanceof jw){var ta=Na.Xb,Ya=Na.mb,dc=Na.Dc;if(Ya instanceof L){var ka=Ya.k;if(ka instanceof fe){var ya=ka.aa;if(ya instanceof Sv){var Sa=ya.Fd;if(Da instanceof Sv){var xc=Da.Fd;t();var Sb=a.Aa;t();t();var uc=a.Aa,Lb=xw(Sa,xc,V(Sa.Va)),lc=new Sv(uc,Lb,V(a.Aa)),Xb=new jw(Sb,ta,new L(new fe(lc)),dc);return new L(Xb)}}}}}a:{var ec=c.y;if(ec instanceof jw){var Ab=ec.mb;if(Ab instanceof L){var Ob=Ab.k;if(Ob instanceof fe&&Ob.aa instanceof Tv){var fb=!0;break a}}}fb=c.w instanceof Tv? !0:!1}fb&&xm("Program reached and unexpected state.");var Wa=c.y;if(Wa instanceof jw){var bb=Wa.mb;if(bb instanceof L){var Ia=bb.k;if(Ia instanceof fe&&Pe(new E(b),Ia.aa))return t(),new L(a)}}var Ua=c.y,pc=c.w;if(Ua instanceof jw){var sc=Ua.Xb,Ba=Ua.mb,ob=Ua.Dc;if(Ba instanceof L){var nc=Ba.k;if(nc instanceof fe){var Ib=nc.aa;if(Ib instanceof cv){var vc=Ib.Nb,Vb=Ib.ac;if(pc instanceof cv){var fc=pc.Nb,Bc=pc.ac;t();var Pb=a.Aa;t();t();var Jb=a.Aa,gc=V(vc.q),Cb=Pu(vc,fc,gc,!1),cc=V(Vb.q),yc=dv(Vb,Bc, cc,!1),Mc=new cv(Jb,Cb,yc,V(a.Aa)),qc=new jw(Pb,sc,new L(new fe(Mc)),ob);return new L(qc)}}}}}var oc=c.y,Qc=c.w;if(oc instanceof jw){var jc=oc.Xb,sb=oc.mb,Gc=oc.Dc;if(sb instanceof L){var Wb=sb.k;if(Wb instanceof fe){var Cc=Wb.aa;if(Cc instanceof Jv){var Fc=Cc.gi;if(Qc instanceof cv){var qd=Qc.Nb,Yb=Qc.ac;t();var Nc=a.Aa;t();t();var ad=a.Aa,Uc=ld=>{if(null!==ld){var Jd=ld.Nb,Dd=ld.ac,Xd=a.Aa,Yc=V(Jd.q);Jd=Pu(Jd,qd,Yc,!1);Yc=V(Dd.q);return new cv(Xd,Jd,dv(Dd,Yb,Yc,!1),ld.Mj)}throw new w(ld);};if(Fc=== u())var cd=u();else{for(var kc=Fc.e(),Vc=new z(Uc(kc),u()),Hc=Vc,rc=Fc.f();rc!==u();){var sd=rc.e(),Kc=new z(Uc(sd),u());Hc=Hc.p=Kc;rc=rc.f()}cd=Vc}var Qd=new Jv(ad,cd,Cc.Mq),Ad=new jw(Nc,jc,new L(new fe(Qd)),Gc);return new L(Ad)}}}}}var kd=c.y,Hd=c.w;if(kd instanceof jw){var Rd=kd.Xb,Bd=kd.mb,ae=kd.Dc;if(Bd instanceof L){var dd=Bd.k;if(dd instanceof fe){var od=dd.aa;if(od instanceof cv&&Hd instanceof Jv){var Ta=a.Aa;t();t();return zw(new jw(Ta,Rd,new L(new fe(Hd)),ae),od)}}}}a:{var wb=c.y;if(wb instanceof jw){var $a=wb.mb;if($a instanceof L){var wa=$a.k;if(wa instanceof fe&&wa.aa instanceof Jv){var hb=!0;break a}}}hb=c.w instanceof Jv?!0:!1}if(hb)return t().d;var ra=c.y,wc=c.w;if(ra instanceof jw){var ac=ra.Xb,Id=ra.mb,ud=ra.Dc;if(wc instanceof Gv){t();var be=new jw(a.Aa,ac.L(wc)?ac:new z(wc,ac),Id,ud);return new L(be)}}var re=c.y,pe=c.w;if(re instanceof iw&&Uv(pe)){t();var bd=a.Aa,Rc=O().c,Wc=new z(pe,Rc);t();t();var Wd=new L(new Ud(re)),zd=sv(),Pa=Su(),Db=op().ga,Oc=new jw(bd,Wc,Wd,zd.Hd(new Uu(Pa, Db)));return new L(Oc)}if(c.y instanceof iw&&(c.w instanceof cv||c.w instanceof Vv))return t().d;a:{var Tc=c.y;if(Tc instanceof jw){var Sd=Tc.mb;if(Sd instanceof L){var Jc=Sd.k;if(Jc instanceof fe&&Jc.aa instanceof cv&&c.w instanceof Vv){var vd=!0;break a}}}var hd=c.y;if(hd instanceof jw){var de=hd.mb;if(de instanceof L){var ye=de.k;if(ye instanceof fe&&ye.aa instanceof Vv&&c.w instanceof cv){vd=!0;break a}}}var jf=c.y;if(jf instanceof jw){var af=jf.mb;if(af instanceof L&&af.k instanceof Ud&&(c.w instanceof cv||c.w instanceof Vv)){vd=!0;break a}}vd=!1}if(vd)return t().d;a:{var pf=c.y;if(pf instanceof jw){var kf=pf.mb;if(kf instanceof L){var Be=kf.k;if(Be instanceof fe&&Be.aa instanceof Wv){var Kd=!0;break a}}}Kd=c.w instanceof Wv?!0:!1}Kd&&no();throw new w(c);} function Aw(a,b){var c=!1,d=null,e=lw(a.Aa);if(null!==e&&e===a)return t(),a=new iw(a.Aa,b.h(),b.j()),new L(a);if(a instanceof iw){e=a.me;var g=a.Ne;if(Pe(new E(e),b.h()))return t(),a=new iw(a.Aa,e,xw(g,b.j(),V(g.Va))),new L(a)}if(a instanceof jw){c=!0;d=a;e=d.Xb;var h=d.mb;g=d.Dc;if(t().d===h)return t(),d=a.Aa,t(),t(),a=new iw(a.Aa,b.h(),b.j()),a=new jw(d,e,new L(new Ud(a)),g),new L(a)}if(c&&(c=d.Xb,e=d.mb,d=d.Dc,e instanceof L&&(e=e.k,e instanceof Ud&&(g=e.fa,null!==g&&(e=g.me,h=g.Ne,Pe(new E(e), b.h()))))))return t(),g=a.Aa,t(),t(),a=new iw(a.Aa,e,xw(h,b.j(),V(h.Va))),a=new jw(g,c,new L(new Ud(a)),d),new L(a);if(a instanceof iw||a instanceof jw)return t().d;throw new w(a);} sw.prototype.Dw=function(a,b){var c=G(new H,this,a),d=c.y;a=c.w;if(d instanceof jw){var e=d.Xb,g=d.mb;d=d.Dc;if(g instanceof L&&(g=g.k,g instanceof Ud&&(g=g.fa,a instanceof iw))){if(e.b()){c=new Ou(d);for(e=!0;e&&c.Ot.s();){e=c.t();d=a.mc();var h=Xu().X();e=Zu(e,d,b,!0,h)}c=e}else c=!1;return c?g.Dw(a,b):!1}}a=c.w;if(c.y instanceof jw&&a instanceof iw)return!1;a=c.w;if(c.y instanceof iw&&a instanceof jw&&(a=a.mb,a instanceof L&&(a=a.k,a instanceof Ud)))return this.Dw(a.fa,b);a=c.w;if(c.y instanceof iw&&a instanceof jw&&(a=a.mb,a=a instanceof L&&a.k instanceof fe?!0:t().d===a?!0:!1,a))return!1;e=c.y;a=c.w;if(e instanceof iw&&(g=e.me,e=e.Ne,a instanceof iw))return c=a.Ne,Pe(new E(g),a.me)?(a=Xu().X(),Fw(e,c,b,a)):!1;e=c.y;g=c.w;if(e instanceof jw&&(h=e.Xb,a=e.mb,e=e.Dc,g instanceof jw)){d=g.Xb;c=g.mb;g=g.Dc;a:{for(;!h.b();){var k=h.e();b:{for(var l=d;!l.b();){var m=l.e().uo();if(Pe(new E(m),k.uo())){k=!0;break b}l=l.f()}k=!1}if(!k){d=!1;break a}h=h.f()}d=!0}if(d){d=!0;for(e=e.m();d&&e.s();){d= e.t();if(null===d)throw new w(d);d=d.j();k=!1;for(h=g.m();!k&&h.s();){k=h.t();if(null===k)throw new w(k);k=k.j();l=Xu().X();k=Zu(d,k,b,!0,l)}d=k}g=d}else g=!1;return g?a instanceof L&&(g=a.k,g instanceof fe&&(g=g.aa,c instanceof L&&(c=c.k,c instanceof fe)))?(a=c.aa,c=Xu().X(),Zu(g,a,b,!0,c)):t().d===a?!0:!1:!1}b=c.y;if(lw(this.Aa)===b)return!0;b=c.w;if(lw(this.Aa)===b)return!1;throw new w(c);};function Gw(a){return lw(a.Aa)===a} function wba(a){var b=tf(a.cd),c=a.ne,d=a.jg,e=1+b.da|0,g=Hw(),h=Su(),k=op().ga;g=g.Hd(new Uu(h,k));e=new Iw(b.S,b.Ec,b.hc,b.Ed,e,b.Pc,b.Zc,b.Lb,b.yc,b.tb,b.$a,b.od,g);Jw(e,Kw(a));if(Pe(new E(a.LE),!0)){Lw(a.J,Ye(new Te(new Ue(J(new K,["Unhandled cyclic parent specification"]))),u()),a.Ab.A(),a.ne);var l=O().c}else try{a.LE=!0;var m=Mw(a);for(h=g=null;m!==u();){var n=m.e();a:{if(null!==n){var r=n.hv,v=n.qt,x=n.kr,A=n.iv,B=n.jv;if(null!==v){var C=v.x,D=a.J.qa,F=a.J;if(F.F){var I=ut(Q(),"| ",F.r)+(e.da+ ". Typing parent spec ")+r;ff(gf(),I+"\n")}F.r=1+F.r|0;try{var M=x.fQ(a.ne);if(M instanceof Nw){k=M;var N=A.b()?t().d:(t(),new L(A)),P=a.ne,T=Ow(a),Y=Pw(a,k,v,N,e,P,T);if(null===Y)throw new w(Y);var Z=Y.h(),S=a.cd,ea=Qw(k,M.Ea(),!1,S,Z),ia=jv(B,ea.Tl);if(Nm(new E(ia),0)){var X=a.J,sa=new Te(new Ue(J(new K,["mixin "," expects "," parameter(s); got ",""]))),Ja=We(Xe(),C);Xe();var Xa=ea.Tl.K(),Fa=We(0,""+Xa);Xe();var za=B.K(),Qa=[Ja,Fa,We(0,""+za)],Ma=Ye(sa,J(new K,Qa));cu();var Ga=op(),ab=B.Gb(Ga.ga).j(); Lw(X,Ma,du(0,new z(v,ab)),a.ne)}var Hb=ea.Tl,bc=new Wq(Hb,Hb,B),yb=new fn(((Ab,Ob)=>(fb,Wa)=>{var bb=G(new H,fb,Wa);fb=bb.y;var Ia=bb.w;if(null!==fb&&(Wa=new L(fb),!Wa.b()&&(fb=Wa.k.h(),Wa=Wa.k.j(),null!==Ia&&(Ia=new L(Ia),!Ia.b())))){var Ua=Ia.k.j();if(null!==Ua&&(Ia=Ua.yb,Ua=Ua.ya,null!==Ia)){bb=Ia.ch;var pc=Ia.Bh;if(Ia.je||bb||pc)throw new Yj("assertion failed: TODO");bb=Rw(a.J,Ua,Ab,a.ne,Ow(a),!0);Ia=Wa.Oa;if(!Ia.b()){Ia=Ia.o();Ua=a.ne;pc=a.jg;var sc=Sw(a.J).ob;Tw(a.J,Ia,bb,Ua,pc,Ab,sc)}Ia=Wa.ra; Ua=a.ne;pc=a.jg;sc=Sw(a.J).ob;Tw(a.J,bb,Ia,Ua,pc,Ab,sc);Ia=Ob.Jj.n(fb.x).Mp();Wa=Wa.Oa.b()?new Uw(a.J,Wa.Oa,bb,V(a.J)):new Uw(a.J,Wa.Oa,Wa.ra,V(a.J));return iu(ju(),Ia?new L(new Vw(a.J,fb,Wa,Ia,Ab.da)):R())}}throw new w(bb);})(e,ea));Yq();var tb=Zq(bc,yb),eb=a.J;if(eb.F){var kb=ut(Q(),"| ",eb.r)+"Mixin arg members "+tb;ff(gf(),kb+"\n")}t();var Rb=new Gp(ea,tb,nf(),r.A()),Gb=new L(Rb)}else if(M instanceof Ww){k=M;B.b()||Lw(a.J,Ye(new Te(new Ue(J(new K,["trait arguments are not yet supported"]))),u()), r.A(),a.ne);var vb=A.b()?t().d:(t(),new L(A)),Tb=a.ne,Nb=Ow(a),ic=Pw(a,k,v,vb,e,Tb,Nb);if(null===ic)throw new w(ic);var Va=ic.h(),cb=ic.j(),zb=a.cd,Ub=Xw(k,M.Ea(),!1,zb,Va),jb=O().c;t();var db=new Gp(Ub,jb,cb.bf(Ub.Vm),r.A());Gb=new L(db)}else if(M instanceof Yw){k=M;var ub=A.b()?t().d:(t(),new L(A)),Aa=a.ne,va=Ow(a),Ra=Pw(a,k,v,ub,e,Aa,va);if(null===Ra)throw new w(Ra);var rb=Ra.h(),xb=Ra.j(),mc=a.cd,Ha=Zw(k,M.Ea(),!1,mc,rb),Ka=Ha.gl;if(Ka instanceof L){var Oa=Ka.k;$w(a,Oa.K(),B,C,v);var Na=new Wq(Oa, Oa,B),Da=new fn((Ab=>(Ob,fb)=>{fb=G(new H,Ob,fb);a:{Ob=fb.y;var Wa=fb.w;if(null!==Ob&&(Ob=new L(Ob),!Ob.b()&&(Ob=Ob.k.j(),null!==Wa&&(Wa=new L(Wa),!Wa.b())))){var bb=Wa.k.j();if(null!==bb&&(Wa=bb.yb,bb=bb.ya,null!==Wa)){fb=Wa.ch;var Ia=Wa.Bh;if(Wa.je||fb||Ia)throw new Yj("assertion failed: TODO");fb=Rw(a.J,bb,Ab,a.ne,Ow(a),!0);Wa=a.ne;bb=a.jg;Ia=Sw(a.J).ob;Tw(a.J,fb,Ob,Wa,bb,Ab,Ia);break a}}throw new w(fb);}})(e));Yq();Ew(Na,Da);var ta=O().c}else{if(t().d!==Ka)throw new w(Ka);var Ya=Ha.Ij;if(Ya instanceof L){var dc=Ya.k;$w(a,dc.K(),B,C,v);var ka=new Wq(dc,dc,B),ya=new fn(((Ab,Ob)=>(fb,Wa)=>{var bb=G(new H,fb,Wa);fb=bb.y;var Ia=bb.w;if(null!==fb&&(Wa=new L(fb),!Wa.b()&&(fb=Wa.k.h(),Wa=Wa.k.j(),null!==Ia&&(Ia=new L(Ia),!Ia.b())))){var Ua=Ia.k.j();if(null!==Ua&&(Ia=Ua.yb,Ua=Ua.ya,null!==Ia)){bb=Ia.ch;var pc=Ia.Bh;if(Ia.je||bb||pc)throw new Yj("assertion failed: TODO");bb=Rw(a.J,Ua,Ab,a.ne,Ow(a),!0);Ia=Wa.Oa;if(!Ia.b()){Ia=Ia.o();Ua=a.ne;pc=a.jg;var sc=Sw(a.J).ob;Tw(a.J,Ia,bb,Ua,pc,Ab,sc)}Ia=Wa.ra;Ua= a.ne;pc=a.jg;sc=Sw(a.J).ob;Tw(a.J,bb,Ia,Ua,pc,Ab,sc);Ia=Ob.Ei.n(fb.x).Mp();Wa=Wa.Oa.b()?new Uw(a.J,Wa.Oa,bb,V(a.J)):new Uw(a.J,Wa.Oa,Wa.ra,V(a.J));return iu(ju(),Ia?new L(new Vw(a.J,fb,Wa,Ia,Ab.da)):R())}}throw new w(bb);})(e,Ha));Yq();ta=Zq(ka,ya)}else{if(t().d!==Ya)throw new w(Ya);$w(a,0,B,C,v);ta=O().c}}var Sa=a.J;if(Sa.F){var xc=ut(Q(),"| ",Sa.r)+"Class arg members "+ta;ff(gf(),xc+"\n")}t();var Sb=new Gp(Ha,ta,xb.bf(Ha.Tm),r.A());Gb=new L(Sb)}else if(M instanceof ax)Lw(a.J,Ye(new Te(new Ue(J(new K, ["Cannot inherit from a type alias"]))),u()),r.A(),a.ne),Gb=t().d;else if(M instanceof Vw)Lw(a.J,Ye(new Te(new Ue(J(new K,["Cannot inherit from a parameter"]))),u()),r.A(),a.ne),Gb=t().d;else if(M instanceof bx)Lw(a.J,Ye(new Te(new Ue(J(new K,["Cannot inherit from a function"]))),u()),r.A(),a.ne),Gb=t().d;else if(M instanceof cx)Gb=t().d;else throw new w(M);}finally{F.r=-1+F.r|0}if(dx(new E(D),F.qa)&&F.F){var uc=""+ut(Q(),"| ",F.r)+D.n(Gb);ff(gf(),uc+"\n")}var Lb=Gb;break a}}throw new w(n);}for(var lc= Lb.m();lc.s();){var Xb=new z(lc.t(),u());null===h?g=Xb:h.p=Xb;h=Xb}m=m.f()}l=null===g?u():g}finally{a.LE=!1}m=e.cb;up(tp(),b.S.li||e.cb.b());if(!m.b()){n=b.S.qa;Lb=b.S;Lb.F&&(r=ut(Q(),"| ",Lb.r)+"UNSTASHING... (out)",ff(gf(),r+"\n"));Lb.r=1+Lb.r|0;try{m.Ca(new y(Ab=>{if(null!==Ab){var Ob=Ab.h();for(Ab=Ab.j().m();Ab.s();){var fb=Ab.t();a:{if(null!==fb){var Wa=fb.j();if(!0===fb.Rc()){fb=Sw(b.S).ob;Tw(b.S,Wa,Ob,c,d,b,fb);break a}}if(null!==fb&&(Wa=fb.j(),!1===fb.Rc())){fb=Sw(b.S).ob;Tw(b.S,Ob,Wa,c,d, b,fb);break a}throw new w(fb);}}}else throw new w(Ab);}));m.mg();var ec=void 0}finally{Lb.r=-1+Lb.r|0}dx(new E(n),Lb.qa)&&Lb.F&&(ec=""+ut(Q(),"| ",Lb.r)+n.n(ec),ff(gf(),ec+"\n"))}return l} var xba=function ex(a,b,c){var e=O().c;if(null===e?null===b:e.i(b))return c;if(b instanceof z){var g=b.z;e=b.p;if(null!==g){var h=g.qt;g=g.kr;if(null!==h){b=h.x;h=!1;var k=null;if(g instanceof fx){h=g.Qu;if(Fp()===h||Bp()===h||zp()===h)return tp(),b=gq(new Ep(b)),g=gx(g),c=b.Ce(g).Ce(c),ex(a,e,c);if(hx()===h||cp()===h||Ap()===h)return ex(a,e,c);throw new w(h);}if(g instanceof ix){h=!0;k=g;var l=k.kh;if(l instanceof Ww)return tp(),c=gq(new Ep(b)).Ce(l.Gq).Ce(c),ex(a,e,c)}if(h&&(l=k.kh,l instanceof Yw))return tp(),c=gq(new Ep(b)).Ce(l.ip).Ce(c),ex(a,e,c);if(h&&(k.kh instanceof Vw||k.kh instanceof bx||k.kh instanceof ax||k.kh instanceof Nw||k.kh instanceof cx))return ex(a,e,c);throw new w(g);}}}throw new w(b);}; function yba(a){var b=tf(a.cd),c=a.ne,d=a.jg,e=1+b.da|0,g=Hw(),h=Su(),k=op().ga;g=g.Hd(new Uu(h,k));var l=new Iw(b.S,b.Ec,b.hc,b.Ed,e,b.Pc,b.Zc,b.Lb,b.yc,b.tb,b.$a,b.od,g);e=a.Ab;if(e instanceof yo)if(k=e.hg,e=r=>{var v=r.j(),x=a.J,A=a.J,B=r.j().A();t();A=jx(new kx,A,B,"type parameter",new L(r.j().V),!0);B=t().d;t();var C=new L(r.j().V),D=O().c,F=O().c;return new tl(v,new lx(x,l.da,D,F,B,C,!1,A),r.h())},k===u())e=u();else{g=k.e();h=g=new z(e(g),u());for(k=k.f();k!==u();){var m=k.e();m=new z(e(m), u());h=h.p=m;k=k.f()}e=g}else{if(!(e instanceof Zn))throw new w(e);k=e.Ch;e=r=>{var v=a.J,x=a.J,A=r.A();t();x=jx(new kx,x,A,"method type parameter",new L(r.V),!0);A=t().d;t();var B=new L(r.V),C=O().c,D=O().c;return new tl(r,new lx(v,l.da,C,D,A,B,!1,x),t().d)};if(k===u())e=u();else{g=k.e();h=g=new z(e(g),u());for(k=k.f();k!==u();)m=k.e(),m=new z(e(m),u()),h=h.p=m,k=k.f();e=g}}g=l.cb;up(tp(),b.S.li||l.cb.b());if(!g.b()){h=b.S.qa;k=b.S;k.F&&(m=ut(Q(),"| ",k.r)+"UNSTASHING... (out)",ff(gf(),m+"\n")); k.r=1+k.r|0;try{g.Ca(new y(r=>{if(null!==r){var v=r.h();for(r=r.j().m();r.s();){var x=r.t();a:{if(null!==x){var A=x.j();if(!0===x.Rc()){x=Sw(b.S).ob;Tw(b.S,A,v,c,d,b,x);break a}}if(null!==x&&(A=x.j(),!1===x.Rc())){x=Sw(b.S).ob;Tw(b.S,v,A,c,d,b,x);break a}throw new w(x);}}}else throw new w(r);}));g.mg();var n=void 0}finally{k.r=-1+k.r|0}dx(new E(h),k.qa)&&k.F&&(n=""+ut(Q(),"| ",k.r)+h.n(n),ff(gf(),n+"\n"))}return e} function zba(a){var b=a.Ag(),c=h=>{if(null!==h){var k=h.hb;return G(new H,h.kc.V,new mx(a.J,k,k.ji))}throw new w(h);};if(b===u())return u();var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}return d}function Aba(a){var b=Xu();a=a.Ag().m();return b.Ib(new Ef(a,new y(c=>{var d=c.hb;c=c.Rd;c=c.b()?ou().Yl:c.o();return G(new H,d,c)})))} function Bba(a){var b=tf(a.cd),c=a.ne,d=a.jg,e=1+b.da|0,g=Hw(),h=Su(),k=op().ga;g=g.Hd(new Uu(h,k));e=new Iw(b.S,b.Ec,b.hc,b.Ed,e,b.Pc,b.Zc,b.Lb,b.yc,b.tb,b.$a,b.od,g);g=a.Ab;if(g instanceof yo)if(h=g.Sg,h.b())a=R();else{h=h.o().Ra;for(var l=k=null;h!==u();){var m=h.e();a:{if(null!==m){var n=m.h(),r=m.j();if(n instanceof L&&(n=n.k,null!==r)){var v=r.yb;r=r.ya;if(null!==v){m=v.ch;if(v.je||m)throw new Yj("assertion failed: TODO");m=nx(r,a.ne);m=ox(a.J,m,e,a.ne,Ow(a),a.dt);v=a.J;r=t().d;m=new Uw(v,r, m,V(a.J));m=G(new H,n,m);n=O().c;m=new z(m,n);break a}}}if(null!==m&&(v=m.h(),n=m.j(),t().d===v&&null!==n&&(v=n.yb,r=n.ya,null!==v&&(n=v.je,v=v.ch,r instanceof vl)))){m=r;if(n||v)throw new Yj("assertion failed: TODO");n=a.J;v=t().d;r=a.J;var x=new Te(new Ue(J(new K,[""," parameters currently need type annotations"])));Xe();Q();var A=[We(0,Nu(0,g.pb.ld))];r=Lw(r,Ye(x,J(new K,A)),m.A(),a.ne);n=new Uw(n,v,r,V(a.J));m=G(new H,m,n);n=O().c;m=new z(m,n);break a}if(null!==m)m=m.j(),Lw(a.J,Ye(new Te(new Ue(J(new K, ["Unsupported field specification"]))),u()),m.A(),a.ne),m=O().c;else throw new w(m);}for(m=m.m();m.s();)n=new z(m.t(),u()),null===l?k=n:l.p=n,l=n;h=h.f()}a=new L(null===k?u():k)}else{if(!(g instanceof Zn))throw new w(g);a=t().d}g=e.cb;up(tp(),b.S.li||e.cb.b());if(!g.b()){e=b.S.qa;h=b.S;h.F&&(k=ut(Q(),"| ",h.r)+"UNSTASHING... (out)",ff(gf(),k+"\n"));h.r=1+h.r|0;try{g.Ca(new y(C=>{if(null!==C){var D=C.h();for(C=C.j().m();C.s();){var F=C.t();a:{if(null!==F){var I=F.j();if(!0===F.Rc()){F=Sw(b.S).ob;Tw(b.S, I,D,c,d,b,F);break a}}if(null!==F&&(I=F.j(),!1===F.Rc())){F=Sw(b.S).ob;Tw(b.S,D,I,c,d,b,F);break a}throw new w(F);}}}else throw new w(C);}));g.mg();var B=void 0}finally{h.r=-1+h.r|0}dx(new E(e),h.qa)&&h.F&&(B=""+ut(Q(),"| ",h.r)+e.n(B),ff(gf(),B+"\n"))}return a} function Cba(a){var b=px(a),c=b.b()?O().c:b.o();b=h=>{var k=h.h().x;h=new qx(a.J,h.j().ra,h.h());return G(new H,k,h)};if(c===u())return u();var d=c.e(),e=d=new z(b(d),u());for(c=c.f();c!==u();){var g=c.e();g=new z(b(g),u());e=e.p=g;c=c.f()}return d} function Dba(a){var b=a.Ab;if(b instanceof yo){var c=tf(a.cd),d=a.ne,e=a.jg,g=1+c.da|0,h=Hw(),k=Su(),l=op().ga;h=h.Hd(new Uu(k,l));var m=new Iw(c.S,c.Ec,c.hc,c.Ed,g,c.Pc,c.Zc,c.Lb,c.yc,c.tb,c.$a,c.od,h);h=ef(b.ei);Od();g=new fp;Od();for(b=new fp;!h.b();){k=h.e();a:{if(k instanceof Zn){l=k;var n=l.Yc;if(n instanceof Ud&&(n=n.fa,!rx(l))){t();k=G(new H,l,n);k=new fe(k);break a}}t();k=new Ud(k)}if(k instanceof fe)wp(g,k.aa);else if(k instanceof Ud)wp(b,k.fa);else throw new w(k);h=h.f()}l=g.ha();b=Wn(b.ha(), new sx(a));Jw(m,Kw(a));g=v=>{if(null!==v){var x=v.h();v=v.j();var A=a.ne,B=a.jg;Sw(m.S);var C=1+m.da|0,D=Hw(),F=Su(),I=op().ga;D=D.Hd(new Uu(F,I));var M=new Iw(m.S,m.Ec,m.hc,m.Ed,C,m.Pc,m.Zc,m.Lb,m.yc,m.tb,m.$a,m.od,D);C=Ow(a);var N=x.Ch;D=Y=>{var Z=Y.V,S=a.J,ea=a.J,ia=Y.A();t();ea=jx(new kx,ea,ia,"method type parameter",new L(Y.V),!0);ia=t().d;t();Y=new L(Y.V);var X=O().c,sa=O().c;S=new lx(S,M.da,X,sa,ia,Y,!1,ea);return G(new H,Z,S)};if(N===u())D=u();else{F=N.e();I=F=new z(D(F),u());for(N=N.f();N!== u();){var P=N.e();P=new z(D(P),u());I=I.p=P;N=N.f()}D=F}C=C.bf(D);C=ox(a.J,v,M,a.ne,C,a.dt);D=a.J;cu();v=jx(new kx,D,du(0,new z(v,new z(x.Rb,x.Ch))),"signature of member `"+x.Rb.x+"`",(tx(a.J),t().d),(tx(a.J),!1));v=ux(C.q,C,v);up(tp(),m.S.li||M.cb.b());Xu().X();C=vx(m.S);D=M.cb.m();D=new xo(D,new y(Y=>{if(null!==Y){var Z=Y.h();Y=Y.j().m();return new Ef(Y,new y(S=>{if(null!==S){var ea=S.Rc();S=S.j();up(tp(),S.Ea()>m.da);return ea?G(new H,S,Z):G(new H,Z,S)}throw new w(S);}))}throw new w(Y);}));Od(); v=wx(C,Pd(u(),D),v);C=m.S;C.F&&(C=ut(Q(),"| ",C.r)+("Inferred poly constr: "+v+" \u2014\u2014 where ")+xx(v),ff(gf(),C+"\n"));m.S.F&&Nm(new E(v),v)&&(C=m.S,C.F&&(C=ut(Q(),"| ",C.r)+("Refreshed: "+v+" \u2014\u2014 where ")+xx(v),ff(gf(),C+"\n")));v=yx(zx(m.S),m.da,v);M.cb.mg();C=M.cb;up(tp(),m.S.li||M.cb.b());if(!C.b()){D=m.S.qa;F=m.S;F.F&&(I=ut(Q(),"| ",F.r)+"UNSTASHING... (out)",ff(gf(),I+"\n"));F.r=1+F.r|0;try{C.Ca(new y(Y=>{if(null!==Y){var Z=Y.h();for(Y=Y.j().m();Y.s();){var S=Y.t(); a:{if(null!==S){var ea=S.j();if(!0===S.Rc()){S=Sw(m.S).ob;Tw(m.S,ea,Z,A,B,m,S);break a}}if(null!==S&&(ea=S.j(),!1===S.Rc())){S=Sw(m.S).ob;Tw(m.S,Z,ea,A,B,m,S);break a}throw new w(S);}}}else throw new w(Y);}));C.mg();var T=void 0}finally{F.r=-1+F.r|0}dx(new E(D),F.qa)&&F.F&&(T=""+ut(Q(),"| ",F.r)+D.n(T),ff(gf(),T+"\n"))}return G(new H,x,v)}throw new w(v);};if(l===u())g=u();else{h=l.e();k=h=new z(g(h),u());for(l=l.f();l!==u();)n=l.e(),n=new z(g(n),u()),k=k.p=n,l=l.f();g=h}h=m.cb;up(tp(),c.S.li||m.cb.b()); if(!h.b()){k=c.S.qa;l=c.S;l.F&&(n=ut(Q(),"| ",l.r)+"UNSTASHING... (out)",ff(gf(),n+"\n"));l.r=1+l.r|0;try{h.Ca(new y(v=>{if(null!==v){var x=v.h();for(v=v.j().m();v.s();){var A=v.t();a:{if(null!==A){var B=A.j();if(!0===A.Rc()){A=Sw(c.S).ob;Tw(c.S,B,x,d,e,c,A);break a}}if(null!==A&&(B=A.j(),!1===A.Rc())){A=Sw(c.S).ob;Tw(c.S,x,B,d,e,c,A);break a}throw new w(A);}}}else throw new w(v);}));h.mg();var r=void 0}finally{l.r=-1+l.r|0}dx(new E(k),l.qa)&&l.F&&(r=""+ut(Q(),"| ",l.r)+k.n(r),ff(gf(),r+"\n"))}r= g}else{if(!(b instanceof Zn))throw new w(b);r=O().c;b=O().c}return G(new H,r,b)}function Eba(a){var b=Fba(a).m();b=new Ef(b,new y(d=>d.Rb.x));var c=Aq(Bq(),b);b=Ax(a).m();b=new Ef(b,new y(d=>{if(null!==d){var e=d.h(),g=e.Rb.x;d=new bx(a.J,1+a.Ru|0,e,d.j(),c.L(e.Rb.x));return G(new H,g,d)}throw new w(d);}));Od();return Pd(u(),b)} function Gba(a){var b=a.Ab;if(b instanceof yo)return a=Mw(a).m(),a=new xo(a,new y(c=>{c=c.kr;return c instanceof fx?Bx(c):c instanceof ix&&(c=c.kh,c instanceof Cx)?c.px:ap()})),Aq(Bq(),a);if(b instanceof Zn)return ap();throw new w(b);} function Hba(a){a=a.Ab;if(a instanceof yo)return a=(new Dx(a.Sg)).wF,a=(a.b()?O().c:a.o().Ra).m(),a=new xo(a,new y(b=>{if(null!==b){var c=b.h(),d=b.j();if(c instanceof L&&(c=c.k,null!==d))return d.yb.Bh?R():new L(c)}if(null!==b&&(c=b.h(),d=b.j(),t().d===c&&null!==d&&(c=d.yb,d=d.ya,d instanceof vl)))return c.Bh?R():new L(d);null!==b&&(d=b.h(),c=b.j(),t().d===d&&null!==c&&xm("Program reached and unexpected state."));throw new w(b);})),Aq(Bq(),a);if(a instanceof Zn)return ap();throw new w(a);} function Iba(a){var b=a.Ab;if(b instanceof yo){var c=b.Sg;c=(c.b()?new Gl(O().c):c.o()).Ra.m();c=new xo(c,new y(l=>l.h()));c=kv(c,new U(()=>{var l=ef(b.ei).m();return new Ex(l,new Fx(a))}));c=Aq(Bq(),c);var d=Gx(a);c=c.Ce(d);var e=a.Ag();d=l=>{if(null!==l){var m=l.kc;if(null!==m)return l=new vl(b.gb.V+"#"+m.V),m=m.A(),Cq(l,m)}throw new w(l);};if(e===u())d=u();else{var g=e.e(),h=g=new z(d(g),u());for(e=e.f();e!==u();){var k=e.e();k=new z(d(k),u());h=h.p=k;e=e.f()}d=g}return c.Ce(d)}if(b instanceof Zn)return ap();throw new w(b);} function Jba(a){var b=px(a);b=b.b()?O().c:b.o();op();b=b.Ti();var c=Gx(a);b=b.dG(c);c=Ax(a).m();b=b.bf(new Ef(c,new y(k=>{var l=k.h().Rb;if(k.h().Om.b()){k=k.j();var m=V(a.J);k=new Uw(k.q,R(),k,m)}else m=a.J,t(),k=new Uw(m,new L(k.j()),k.j(),k.j().ma());return G(new H,l,k)})));c=Hx(a);for(var d=null,e=null;c!==u();){for(var g=c.e().oi.m();g.s();){var h=new z(g.t(),u());null===e?d=h:e.p=h;e=h}c=c.f()}c=null===d?u():d;for(e=d=null;c!==u();){h=c.e();a:{if(null!==h&&(g=h.h(),h=h.j(),h instanceof Vw)){g= new vl(g);g=G(new H,g,h.ig);h=O().c;g=new z(g,h);break a}g=O().c}for(g=g.m();g.s();)h=new z(g.t(),u()),null===e?d=h:e.p=h,e=h;c=c.f()}return b.bf(null===d?u():d)} function Kba(a){var b=a.Ab;if(b instanceof Zn){var c=a.J;if(c.F){c=ut(Q(),"| ",c.r);var d=b.Rb.x,e=Ix(b),g=a.cd.da,h=b.kx.b(),k=b.Qz;k.b()?k=!1:(k=k.o().CB(),k=dx(new E(k),cp()));ff(gf(),c+("Type "+d+" polymorphically? "+e+" \x26\x26 ("+g+" \x3d\x3d\x3d 0 || "+!h+" || ")+k+"\n")}if(Ix(b)){if(Pe(new E(a.cd.da),0)||!b.kx.b())return!0;a=b.Qz;if(a.b())return!1;a=a.o().CB();return dx(new E(a),cp())}return!1}xm("Program reached and unexpected state.")} function Pw(a,b,c,d,e,g,h){var k=c.x;if(!d.b()){var l=d.o(),m=b.Ag(),n=l.K();m=m.ab(n);if(Nm(new E(m),0)){m=a.J;n=new Te(new Ue(J(new K,[""," "," expects "," type parameter(s); got ",""])));var r=We(Xe(),b.fd().ld);k=We(Xe(),k);Xe();var v=b.Ag().K();v=We(0,""+v);Xe();var x=l.K();r=[r,k,v,We(0,""+x)];Lw(m,Ye(n,J(new K,r)),du(cu(),new z(c,l)),g)}}l=a.J;if(d.b())a=R();else{n=d.o();if(n===u())a=u();else{d=n.e();m=d=new z(ox(a.J,d,e,g,h,a.dt),u());for(n=n.f();n!==u();)r=n.e(),r=new z(ox(a.J,r,e,g,h,a.dt), u()),m=m.p=r,n=n.f();a=d}a=new L(a)}return Jx(l,b,c,a,e)} function Kx(a,b){var c=a.eo,d=new U(()=>a.ct?(Lw(a.J,Ye(new Te(new Ue(J(new K,["Unhandled cyclic definition"]))),u()),a.Ab.A(),b),new cx(a.J,a.Ab)):Lx(a.J,new U(()=>"Completing "+Mx(a.Ab)),new U(()=>{Nx(a.J,new U(()=>{var Ha=a.Ag();return"Type params "+ze(Ha,""," ","")}));Nx(a.J,new U(()=>{var Ha=iu(ju(),px(a));return"Params "+ze(Ha,""," ","")}));try{a.ct=!0;var e=a.Ab;if(e instanceof Zn){var g=!1,h=e.Yc;a:{if(h instanceof Ud){g=!0;var k=h.fa;if(k instanceof Vt){var l=k.Ws,m=k.Vs;Ox(a,e,b);var n= new y(Ha=>{var Ka=Ow(a),Oa=Qt(l,new y(Na=>{if(Na instanceof fe){Na=Na.aa.V;var Da=a.J,ta=V(a.J),Ya=t().d,dc=t().d,ka=O().c,ya=O().c;Da=new lx(Da,1,ka,ya,Ya,dc,!1,ta);return G(new H,Na,Da)}xm("Program reached and unexpected state.")}));op();Ka=Ka.bf(pp(qp(),Oa));Oa=nf();return ox(a.J,m,Ha,b,Ka,Oa)}),r=a.jg;Sw(a.cd.S);var v=Px(a.cd,n,b,r);var x=new bx(a.J,a.cd.da,e,new Qx(a.J,a.cd.da,v),!1);break a}}g&&xm("Program reached and unexpected state.");if(h instanceof fe){var A=h.aa;e.wd.b()||Ox(a,e,b);var B= (Rx(a)?0:!e.wd.b())?!e.Pl:!1;if(Rx(a))var C=Sx(a.cd,new y(Ha=>{var Ka=e.Ch,Oa=Tx(a);Ka=jv(Ka,Oa);Ka=Pe(new E(Ka),0);Oa=new U(()=>G(new H,e.Ch,Tx(a)));if(!Ka)throw new Yj("assertion failed: "+Es(Oa));Ka=Ow(a).bf(Tx(a));return(new y(Na=>Rw(a.J,A,Ha,b,Na,B))).n(Ka)}),b,a.jg);else if(e.Om.b())C=Rw(a.J,A,a.cd,b,Ow(a),B);else{var D=Rw(a.J,A,a.cd,b,Ow(a),B),F=Ux(a),I=a.jg,M=a.cd,N=Sw(a.J).ob;Tw(a.J,D,F,b,I,M,N);C=Ux(a)}var P=a.J,T=e.A(),Y=e.wd;if(Y instanceof L)var Z=e.Pl?"value":"let binding";else if(t().d=== Y)Z="method";else throw new w(Y);var S=jx(new kx,P,T,"definition of "+Z+" "+e.Rb.x,(tx(a.J),t().d),(tx(a.J),!1));x=new bx(a.J,a.cd.da,e,ux(C.q,C,S),!0)}else throw new w(h);}Sx(a.cd,new y(Ha=>{var Ka=x.hh,Oa=Ux(a),Na=a.jg,Da=Sw(a.J).ob;Tw(a.J,Ka,Oa,b,Na,Ha,Da)}),b,a.jg);for(var ea=new y(Ha=>{Ha=Rw(a.J,Ha,a.cd,b,Ow(a),!1);var Ka=a.J.RE,Oa=a.jg,Na=a.cd,Da=Sw(a.J).ob;Tw(a.J,Ha,Ka,b,Oa,Na,Da)}),ia=e.Oz;!ia.b();)ea.n(ia.e()),ia=ia.f();var X=x}else if(e instanceof yo){if((Ot(new E(e.pb),zp())||Ot(new E(e.pb), cp()))&&!e.Qm.b()){g=a.J;var sa=new Te(new Ue(J(new K,["Explicit "," constructors are not supported"]))),Ja=[We(Xe(),e.pb.ld)],Xa=Ye(sa,J(new K,Ja)),Fa=e.Qm,za=new U(()=>t().d),Qa=new y(Ha=>Ha.A());Lw(g,Xa,Fa.b()?Es(za):Qa.n(Fa.o()),b)}for(var Ma=new y(Ha=>{Ha=Rw(a.J,Ha,a.cd,b,Ow(a),!1);var Ka=a.J.RE,Oa=a.jg,Na=a.cd,Da=Sw(a.J).ob;Tw(a.J,Ha,Ka,b,Oa,Na,Da)}),Ga=e.Rz;!Ga.b();)Ma.n(Ga.e()),Ga=Ga.f();var ab=e.pb;if(Fp()===ab){var Hb=e.Sg;if(Hb instanceof L){var bc=Hb.k;Lw(a.J,Ye(new Te(new Ue(J(new K, ["trait parameters are not yet supported"]))),u()),bc.A(),b)}X=Sx(tf(a.cd),new y(Ha=>{Jw(Ha,Kw(a));Jw(Ha,Qt(Ax(a),new y(ya=>{var Sa=ya.h().Cf.x;ya=new qx(a.J,ya.j(),ya.h().Rb);return G(new H,Sa,ya)})));var Ka=new qx(a.J,Vx(a),new vl("this"));Wx(Ha,G(new H,"this",Ka));Ka=Hx(a);var Oa=Xx(a.J,e,V(a.J),Ha),Na=O().c,Da=nf(),ta=e.Hj,Ya=new U(()=>a.J.La),dc=new y(()=>Yx(a,Ha,e,b));Ka=Lba(a,Ka,Oa,Na,Da,ta.b()?Es(Ya):dc.n(ta.o()),b,e);if(null!==Ka)Na=new Gp(Ka.Uj,Ka.oj,Ka.oi,Ka.Xi);else throw new w(Ka);Ka= Na.oj;Oa=Na.oi;Na=Na.Xi;ta=ef(e.ei);for(Da=new y(ya=>{if(ya instanceof Zn&&ya.Yc instanceof fe)return Lw(a.J,Ye(new Te(new Ue(J(new K,["Method implementations in traits are not yet supported"]))),u()),ya.A(),b)});!ta.b();)Da.n(ta.e()),ta=ta.f();var ka=of(a.J,e.ei,(t(),new L(e)),Ha,b,Ow(a));Da=Ka.m();Da=new Ef(Da,new y(ya=>{var Sa=ya.Ua();return G(new H,Sa,ya)}));Da=kv(Da,new U(()=>Qt(ka.xk,new y(ya=>{var Sa=ya.Ua();return G(new H,Sa,ya)})))).nb(new U(()=>Zx(a)));op();Da=pp(qp(),Da);$x(a,Qt(Zx(a), new y(ya=>ya.j())),Ka,O().c,b,e);return new Ww(a.J,a.cd.da,e,a.Ag(),Da,a.J.La,Na,gx(a),Oa)}),b,a.jg)}else if(Ap()===ab){var yb=e.Sg,tb=new U(()=>new Gl(O().c));if(!(yb.b()?Es(tb):yb.o()).Ra.b()){var eb=a.J,kb=Ye(new Te(new Ue(J(new K,["Type alias definitions cannot have value parameters"]))),u()),Rb=e.Sg,Gb=new U(()=>new Gl(O().c)),vb=(Rb.b()?Es(Gb):Rb.o()).A(),Tb=G(new H,kb,vb),Nb=O().c;ay(eb,new z(Tb,Nb),b)}if(!e.Di.b()){var ic=a.J,Va=Ye(new Te(new Ue(J(new K,["Type alias definitions cannot extend parents"]))), u()),cb=du(cu(),e.Di),zb=G(new H,Va,cb),Ub=O().c;ay(ic,new z(zb,Ub),b)}var jb=e.Hj;if(jb instanceof L)var db=jb.k,ub=Sx(a.cd,new y(Ha=>ox(a.J,db,Ha,b,Ow(a),a.dt)),b,a.jg);else if(t().d===jb)ub=Lw(a.J,Ye(new Te(new Ue(J(new K,["Type alias definition requires a right-hand side"]))),u()),e.A(),b);else throw new w(jb);X=new ax(a.J,a.cd.da,e,a.Ag(),ub)}else if(Bp()===ab||zp()===ab)X=Sx(tf(a.cd),new y(Ha=>{var Ka=new $e;if(Ot(new E(e.pb),zp())&&!px(a).b()){var Oa=a.J,Na=new Te(new Ue(J(new K,[""," parameters are not supported"]))); Xe();Q();var Da=[We(0,Nu(0,e.pb.ld))];Na=Ye(Na,J(new K,Da));Da=px(a);var ta=new U(()=>e.gb.A()),Ya=new y(Ba=>{cu();Ba=Ba.m();return du(0,new Ef(Ba,new y(ob=>ob.h())))});Lw(Oa,Na,Da.b()?Es(ta):Ya.n(Da.o()),b)}if(e.Pm.b()&&e.fl.b())if(Oa=e.Hj,Oa instanceof L)Na=Oa.k,Oa=a.J,Da=new Te(new Ue(J(new K,["Self-type annotations have no effects on non-abstract "," definitions"]))),ta=[We(Xe(),e.pb.ld)],Da=Ye(Da,J(new K,ta)),Na=Na.A(),Na=G(new H,Da,Na),Da=Ye(new Te(new Ue(J(new K,["Did you mean to use `extends` and inherit from a parent class?"]))), u()),ta=t().d,Da=G(new H,Da,ta),ta=O().c,by(Oa,new z(Na,new z(Da,ta)),b);else if(t().d!==Oa)throw new w(Oa);Jw(Ha,Kw(a));Jw(Ha,Qt(Ax(a),new y(Ba=>{var ob=Ba.h().Cf.x;Ba=new qx(a.J,Ba.j(),Ba.h().Rb);return G(new H,ob,Ba)})));Oa=e.Hj;Na=new U(()=>a.J.La);Da=new y(()=>Yx(a,Ha,e,b));Oa=Oa.b()?Es(Na):Da.n(Oa.o());Na=a.J;Da=a.Ab.A();Na=jx(new kx,Na,Da,cy(a.Ab),(tx(a.J),t().d),(tx(a.J),!1));Da=Vx(a);ta=Qt(a.Ag(),new y(Ba=>{if(null!==Ba){var ob=Ba.kc;Ba=Ba.hb;var nc=e.gb.V+"#"+ob.V,Ib=new mx(a.J,Ba,Ba.ji), vc=a.J;nc=new Ep(nc);ob=ob.A();return new Vw(vc,Cq(nc,ob),new Uw(a.J,(t(),new L(Ib)),Ib,Ba.ji),!0,Ha.da)}throw new w(Ba);}));var dc=Qt(ta,new y(Ba=>{var ob=Ba.ij.Zr();return G(new H,ob,Ba.ig)}));Ya=px(a);var ka=new y(Ba=>{Ba=dy(lv(),Ba);var ob=dy(lv(),dc);ob=Aq(Bq(),ob);return ey(Ba,ob)});Ya=!(!Ya.b()&&ka.n(Ya.o()));ka=new U(()=>{no()});if(!Ya)throw new Yj("assertion failed: "+Es(ka));Ya=a.J;ka=O().c;var ya=a.J,Sa=du(cu(),e.Di),xc=new y(Ba=>vs(Ba));Ya=new Qv(Ya,ka,jx(new kx,ya,Sa.b()?R():new L(xc.n(Sa.o())), "Object",(tx(a.J),t().d),(tx(a.J),!1)));ka=px(a);ya=new U(()=>O().c);ka=Qt(ka.b()?Es(ya):ka.o(),new y(Ba=>new Vw(a.J,Ba.h(),Ba.j(),!fy(a).L(Ba.h()),Ha.da)));Ka=Mba(a,Hx(a),Nba(Ka.sb?Ka.vb:Oba(a,Ka),Ya,un(ta,ka),t().d,O().c,O().c,nf(),Oa),Da,Ha,b,Na,e,dc,Oa);if(null!==Ka)Da=new bo(Ka.Eq,Ka.Os,Ka.Gu,Ka.Qs,Ka.Ps,Ka.Rm);else throw new w(Ka);ta=Da.lr;var Sb=Da.mr,uc=Da.rt,Lb=Da.st;Ka=Da.tt;Da=Da.kv;Ya=new qx(a.J,Vx(a),new vl("this"));Wx(Ha,G(new H,"this",Ya));ta=new qx(a.J,ta,new vl("super"));Wx(Ha,G(new H, "super",ta));ta=of(a.J,e.ei,(t(),new L(e)),Ha,b,Ow(a));Ya=uc.BK(new y(Ba=>Ba.tr()));if(null!==Ya)Ya=G(new H,Ya.h(),Ya.j());else throw new w(Ya);var lc=Ya.h();Nx(a.J,new U(()=>"baseClsImplemMembers "+lc));var Xb=ta.xk,ec=Qt(Zx(a),new y(Ba=>Ba.j()));Lx(a.J,new U(()=>"Checking `this` accesses..."),new U(()=>{var Ba=gy(Xb,new y(Vb=>Vb.tr()),!1),ob=ef(e.ei);ob=gy(ob,new y(Vb=>!(Vb instanceof Ct)),!1);var nc=e.Qm,Ib=new U(()=>O().c),vc=new y(Vb=>Vb.js.Dj);nc=nc.b()?Es(Ib):vc.n(nc.o());Pba(a,Ba,un(ob,nc), uc,ec,b)}),a.J.qa);ta=Xb.m().nb(new U(()=>Sb));ta=new Ef(ta,new y(Ba=>Ba.Ua()));var Ab=Aq(Bq(),ta);ta=lc.m();ta=new hy(ta,new y(Ba=>Ba.Ua()));ta=new iy(ta,new y(Ba=>Ab.L(Ba.Ua())),!0);Od();var Ob=Pd(u(),ta);Lx(a.J,new U(()=>"Checking base class implementations against inherited signatures..."),new U(()=>{$x(a,Ob,Lb,O().c,b,e)}),a.J.qa);var fb=jy(a,uc,Lb,e,b);ta=Xb.m().nb(new U(()=>Sb));ta=new hy(ta,new y(Ba=>Ba.Ua()));Od();var Wa=Pd(u(),ta);Lx(a.J,new U(()=>"Checking new implementations against inherited signatures..."), new U(()=>{var Ba=ec.m().nb(new U(()=>fb));Ba=new hy(Ba,new y(ob=>ob.Ua()));Od();$x(a,Wa,Pd(u(),Ba),ec,b,e)}),a.J.qa);ta=un(un(Xb,Sb),lc);var bb=ky(ta,new y(Ba=>Ba.Ua()));Lx(a.J,new U(()=>"Checking new signatures against inherited signatures..."),new U(()=>{$x(a,ec,fb,ec,b,e)}),a.J.qa);Lx(a.J,new U(()=>"Checking signature implementations..."),new U(()=>{var Ba=ec.m().nb(new U(()=>fb.m()));Ba=new hy(Ba,new y(ob=>ob.Ua()));Ba=new iy(Ba,new y(ob=>ob.tr()),!0);Od();ly(a,bb,Pd(u(),Ba),e,b)}),a.J.qa);ta= Qt(un(fb,bb),new y(Ba=>{var ob=Ba.Ua();return G(new H,ob,Ba)}));op();var Ia=pp(qp(),ta).bf(Zx(a));Nx(a.J,new U(()=>"allMembers "+Ia));ta=e.Qm;a:{if(ta instanceof L){var Ua=ta.k;if(null!==Ua){var pc=Ua.ks,sc=Ua.js;ya=Sx(tf(a.cd),new y(Ba=>{for(var ob=Qt(pc.Ra,new y(Pb=>{if(null!==Pb){var Jb=Pb.h(),gc=Pb.j();if(Jb instanceof L&&(Jb=Jb.k,null!==gc)){var Cb=gc.yb;gc=gc.ya;if(null!==Cb){var cc=Cb.ch,yc=Cb.Bh,Mc=new U(()=>"TODO");if(Cb.je||cc)throw new Yj("assertion failed: "+Es(Mc));yc&&my(a,Jb.A(),b); Cb=Qn(gc);if(Cb instanceof Ud)return Pb=ox(a.J,Cb.fa,Ba,b,Ow(a),a.dt),G(new H,Jb,Pb);no()}}}if(null!==Pb&&(Cb=Pb.h(),Jb=Pb.j(),t().d===Cb&&null!==Jb&&(cc=Jb.yb,Jb=Jb.ya,null!==cc&&(Cb=cc.je,gc=cc.ch,cc=cc.Bh,Jb instanceof vl)))){Pb=new U(()=>"TODO");if(Cb||gc)throw new Yj("assertion failed: "+Es(Pb));cc&&my(a,Jb.A(),b);Pb=a.J;Cb=ny(a.J,Jb,!1);gc=t().d;t();cc=new L(Jb.x);yc=O().c;Mc=O().c;Pb=new lx(Pb,Ba.da,yc,Mc,gc,cc,!1,Cb);return G(new H,Jb,Pb)}if(null!==Pb&&(Jb=Pb.h(),Cb=Pb.j(),t().d===Jb&&null!== Cb))return Jb=Cb.ya,Pb=new vl("\x3cerror\x3e"),Jb=Lw(a.J,Ye(new Te(new Ue(J(new K,["Unsupported constructor parameter shape"]))),u()),Jb.A(),b),G(new H,Pb,Jb);throw new w(Pb);})),nc=new y(Pb=>{if(null!==Pb){var Jb=Pb.h(),gc=Jb.x;Pb=new qx(a.J,Pb.j(),Jb);Wx(Ba,G(new H,gc,Pb))}else throw new w(Pb);}),Ib=ob;!Ib.b();)nc.n(Ib.e()),Ib=Ib.f();var vc=jx(new kx,a.J,Ua.A(),"auxiliary class constructor",(tx(a.J),t().d),(tx(a.J),!1));null!==sc?nc=sc.Dj:(nc=O().c,nc=new z(sc,nc));Ib=Xu();lv();var Vb=px(a),fc= new U(()=>O().c);Vb=Vb.b()?Es(fc):Vb.o();var Bc=Ib.Ib(ry(0,Vb,it()));for(Ib=new y(Pb=>{if(Pb instanceof bm){var Jb=Pb.Vn,gc=Pb.Zo;return Bc.ou(Jb,new y(Cb=>{var cc=!1,yc=null;if(Cb instanceof L){cc=!0;yc=Cb;var Mc=yc.k;if(Mc instanceof L)return cc=Mc.k,Cb=Rw(a.J,gc,Ba,b,Ow(a),!1),cc=cc.ra,yc=Sw(a.J).ob,Tw(a.J,Cb,cc,b,vc,Ba,yc),cc=Jb.x,Cb=new qx(a.J,Cb,Jb),Wx(Ba,G(new H,cc,Cb)),t(),Cb=t().d,new L(Cb)}if(cc&&(cc=yc.k,t().d===cc))return Cb=a.J,cc=new Te(new Ue(J(new K,["Class parameter '","' was already set"]))), yc=[We(Xe(),Jb.x)],Lw(Cb,Ye(cc,J(new K,yc)),Jb.A(),b),t().d;if(t().d===Cb)return Cb=a.J,cc=new Te(new Ue(J(new K,["Unknown class parameter '","'"]))),yc=[We(Xe(),Jb.x)],Lw(Cb,Ye(cc,J(new K,yc)),Jb.A(),b),t().d;throw new w(Cb);}))}if(sy(Pb))return ty(a.J,Pb,!1,Ba,b,Ow(a),!1);xm("Program reached and unexpected state.")});!nc.b();)Ib.n(nc.e()),nc=nc.f();t();return new L(ob)}),b,Na);break a}}if(t().d===ta)ya=t().d;else throw new w(ta);}Ia.og(new fn((Ba,ob)=>{Ba=G(new H,Ba,ob);var nc=Ba.y,Ib=Ba.w;if(Ib instanceof bx&&!Ib.Mb.Om.b()){var vc=uy(Ib.hh);if(!Ib.Mb.Yc.tv()&&Ib.Mb.kx.b())if(vc instanceof lx&&vc.Sb.b()){Ba=vy(vc);Ba=Qba(Ba,new fn((fc,Bc)=>{var Pb=V(fc.q);return dv(fc,Bc,Pb,!1)}));ob=new U(()=>{var fc=a.J,Bc=new Te(new Ue(J(new K,["Could not infer a type for unused mutable field ",""]))),Pb=[We(Xe(),nc)];return Lw(fc,Ye(Bc,J(new K,Pb)),Ib.Mb.A(),b)});var Vb=Ba.b()?Es(ob):Ba.o();Nx(a.J,new U(()=>"Setting type of "+nc+" based on inferred lower bounds: "+vc+" :\x3d "+Vb));wy(vc,(t(),new L(Vb)))}else xm(vc.u())}})); xy||(xy=new yy);Na=a.J;ta=a.cd.da;Ya=a.Ag();ka=px(a);Sa=new U(()=>{ju();var Ba=px(a).b()&&Ot(new E(e.pb),Bp())?!!e.Pm.b():!1;return zy(0,Ba,new U(()=>O().c))});ya=ya.b()?Es(Sa):ya;Oa=new Yw(Na,ta,e,Ya,ka,ya,Ia,a.J.La,e.Pm.b()?Oa:Da,gx(a),Ka);return Rba(Oa,new y(Ba=>Ay(Ba,Ha)))}),b,a.jg);else if(cp()===ab){if(!e.Di.b()){var Aa=a.J,va=Ye(new Te(new Ue(J(new K,["mixin definitions cannot yet extend parents"]))),u()),Ra=du(cu(),e.Di),rb=G(new H,va,Ra),xb=O().c;ay(Aa,new z(rb,xb),b)}var mc=a.cd;X=Sx(tf(a.cd), new y(Ha=>{Jw(Ha,Kw(a));Jw(Ha,Qt(Ax(a),new y(ya=>{var Sa=ya.h().Cf.x;ya=new qx(a.J,ya.j(),ya.h().Rb);return G(new H,Sa,ya)})));var Ka=px(a),Oa=new y(ya=>Qt(ya,new y(Sa=>{var xc=Sa.h().x;Sa=new Vw(a.J,Sa.h(),Sa.j(),!fy(a).L(Sa.h()),Ha.da);return G(new H,xc,Sa)})));Ka=Ka.b()?R():new L(Oa.n(Ka.o()));Oa=new U(()=>O().c);Ka=Ka.b()?Es(Oa):Ka.o();op();Ka=Ka.Ti();Oa=a.J;var Na=V(a.J),Da=t().d;t();var ta=new L("this"),Ya=O().c,dc=O().c;Oa=new lx(Oa,Ha.da,Ya,dc,Da,ta,!1,Na);Na=a.J;Da=V(a.J);ta=t().d;t();Ya= new L("super");dc=O().c;var ka=O().c;Na=new lx(Na,Ha.da,dc,ka,ta,Ya,!1,Da);Da=new qx(a.J,Oa,new vl("this"));Wx(Ha,G(new H,"this",Da));Da=new qx(a.J,Na,new vl("super"));Wx(Ha,G(new H,"super",Da));Da=of(a.J,e.ei,(t(),new L(e)),Ha,b,Ow(a)).xk;ta=Qt(Zx(a),new y(ya=>ya.j()));$x(a,Da,ta,ta,b,e);ly(a,Da,ta,e,b);Da=Qt(Da,new y(ya=>{var Sa=ya.Ua();return G(new H,Sa,ya)}));op();Ka=Ka.bf(pp(qp(),Da)).bf(Zx(a));Da=a.J;ta=mc.da;Ya=a.Ag();dc=px(a);ka=new U(()=>O().c);return new Nw(Da,ta,e,Oa,Na,Ya,dc.b()?Es(ka): dc.o(),Ka)}),b,a.jg)}else throw new w(ab);}else throw new w(e);}finally{a.ct=!1}a.eo=(t(),new L(X));return X}),new y(e=>{var g=e.bG();var h=O().c;g=new By(g,new z(e,h),t().d);g=xx(g);return"Completed "+e+" where "+g})));return c.b()?Es(d):c.o()} function Cy(a,b,c,d){var e=a.Ab;if(e instanceof Zn){if(a.ct)return b=a.J,b.F&&(b=ut(Q(),"| ",b.r)+"Already computing! Using TV: "+Ux(a),ff(gf(),b+"\n")),Ux(a);a=Kx(a,d);if(a instanceof bx)return a.hh;xm("Program reached and unexpected state.")}else if(e instanceof yo){if(e.Qm.b())return Dy(a.J,b,c,e,a.Ru,a.Ag(),px(a),t().d,gx(a),d);e=Kx(a,d);if(e instanceof Yw)return Dy(e.nc,b,c,e.Rf,e.Sm,e.gh,e.Ij,e.gl,0,d);if(e instanceof cx)return Ey(a.J);xm("Program reached and unexpected state.")}else throw new w(e); }function $w(a,b,c,d,e){var g=c.ab(b);if(Nm(new E(g),0)){g=a.J;var h=new Te(new Ue(J(new K,["class "," expects "," parameter(s); got ",""])));d=We(Xe(),d);b=We(Xe(),""+b);Xe();var k=c.K();b=[d,b,We(0,""+k)];h=Ye(h,J(new K,b));cu();b=op();c=c.Gb(b.ga).j();Lw(g,h,du(0,new z(e,c)),a.ne)}}function Ox(a,b,c){b.Ch.b()||Lw(a.J,Ye(new Te(new Ue(J(new K,["Type parameters are not yet supported in this position"]))),u()),b.Ch.e().A(),c)} function Fy(a,b){if(Gy(a.Mb))return!0;a=b.U(a.Ua());return a instanceof L&&(a=a.k,a instanceof bx)?!a.Mb.lx.b():!1} function Hy(a,b,c,d,e,g,h,k){var l=b.uE;if(l.b()){b=b.Uz;l=t().d;for(b=b.m();b.s();){var m=l;l=b.t();if(m.b())a:{if(m=c.U(l.h().x),m instanceof L&&(m=m.k,m instanceof bx)){if(d.b())var n=!0;else n=d.o(),n=Nm(new E(n),l.h().x);if(n&&!e.L(l.h().x)){n=l.j();if(n instanceof L&&(!g||Fy(m,h))){l=n;break a}m=Iy(m);t();l=Jy(a,m,new L(l.h().x),new z(l.h().x,e),!1,c,k,h);break a}}l=t().d}else l=m}return l}return l} function Jy(a,b,c,d,e,g,h,k){if(c.b())return Hy(a,b,g,c,d,e,k,h);var l=c.o();if(ca(h)!==da(Ky)){var m=h.U(l);if(m instanceof L)h=m.k;else{if(R()!==m)throw new w(m);a=Hy(a,b,g,c,d,e,k,h);Ly(h,l,a,!1);h=a}}else{m=My(W(),l);m^=m>>>16|0;var n=m&(-1+h.eb.a.length|0),r=h.eb.a[n];r=null===r?null:Ny(r,l,m);null!==r?h=r.Ah:(r=h.eb,a=Hy(a,b,g,c,d,e,k,h),(1+h.Xf|0)>=h.hu&&Oy(h,h.eb.a.length<<1),Py(h,l,a,!1,m,r===h.eb?n:m&(-1+h.eb.a.length|0)),h=a)}return h} function Qy(a,b,c,d,e,g){b.Uz.Ca(new y(h=>{if(h.j().b()){var k=d.U(h.h().x);if(k instanceof L){var l=k.k;if(l instanceof bx&&Fy(l,g)){k=a.J;var m=new Te(new Ue(J(new K,["Unqualified access to virtual member ",""])));h=[We(Xe(),h.h().x)];h=Ye(m,J(new K,h));h=G(new H,h,c);m=Ye(new Te(new Ue(J(new K,["Declared here:"]))),u());l=l.Mb.A();l=G(new H,m,l);m=O().c;return ay(k,new z(h,new z(l,m)),e)}}}}))} function Ry(a,b){var c=a.j(),d=b.j();if(c instanceof L&&(c=c.k,d instanceof L&&Sy(c,d.k)))return a=G(new H,a.h(),d),b=O().c,new z(a,b);d=O().c;return new z(a,new z(b,d))} function Pba(a,b,c,d,e,g){Ty();var h=u();h=Uy(h);var k=A=>{var B=A.Ua();return G(new H,B,A)};if(e===u())k=u();else{var l=e.e(),m=l=new z(k(l),u());for(e=e.f();e!==u();){var n=e.e();n=new z(k(n),u());m=m.p=n;e=e.f()}k=l}op();k=pp(qp(),k);d=d.m().nb(new U(()=>b));d=new Ef(d,new y(A=>{var B=A.Ua();return G(new H,B,A)}));op();d=k.bf(pp(qp(),d));for(l=b;!l.b();){m=l.e();if(m instanceof bx){var r=m;m=r.Mb;e=Iy(r);if(!m.wd.b()){t();n=r.Ua();n=new L(n);var v=r.Ua(),x=O().c;n=Jy(a,e,n,new z(v,x),!0,d,h,k); if(n instanceof L)v=n.k,n=a.J,x=new Te(new Ue(J(new K,["Cannot access `this` while initializing field ",""]))),r=[We(Xe(),r.Ua())],r=Ye(x,J(new K,r)),x=m.A(),r=G(new H,r,x),x=Ye(new Te(new Ue(J(new K,["The access to `this` is here"]))),u()),v=v.A(),ay(n,Ry(r,G(new H,x,v)),g);else if(t().d!==n)throw new w(n);}Qy(a,e,m.A(),d,g,k)}l=l.f()}for(;!c.b();){l=c.e();a:{if(l instanceof Cl&&(m=l.ai,m instanceof vl&&"this"===m.x))break a;m=Vy(a.J,l);e=Jy(a,m,t().d,O().c,!1,d,h,k);if(e instanceof L)n=e.k,e=a.J, r=Ye(new Te(new Ue(J(new K,["Cannot access `this` during object initialization"]))),u()),v=l.A(),r=G(new H,r,v),v=Ye(new Te(new Ue(J(new K,["The access to `this` is here"]))),u()),n=n.A(),ay(e,Ry(r,G(new H,v,n)),g);else if(t().d!==e)throw new w(e);Qy(a,m,l.A(),d,g,k)}c=c.f()}} function Wy(a,b,c,d,e){a=a.J;var g=new Te(new Ue(J(new K,["Member `","` is declared (or its declaration is inherited) but is not implemented in `","`"]))),h=[We(Xe(),c.Ua()),We(Xe(),d.gb.V)];g=Ye(g,J(new K,h));d=d.gb.A();d=G(new H,g,d);g=Ye(new Te(new Ue(J(new K,["Declared here:"]))),u());c=c.A();c=G(new H,g,c);ay(a,new z(d,new z(c,b)),e)} function ly(a,b,c,d,e){for(var g=Xu().X();!b.b();){var h=b.e();g.ou(h.Ua(),new y(((m,n,r)=>v=>{if(v instanceof L){v=a.J;var x=new Te(new Ue(J(new K,["Duplicated `","` member definition in `","`"]))),A=[We(Xe(),m.Ua()),We(Xe(),n.Cf.x)];Lw(v,Ye(x,J(new K,A)),m.A(),r);return t().d}if(t().d===v)return t(),new L(m);throw new w(v);})(h,d,e)));b=b.f()}if(d.fl.b()&&Nm(new E(d.pb),Fp())&&d.Pm.b())for(;!c.b();){b=c.e();h=g.U(b.Ua());if(h instanceof L){if(h=h.k,!h.Mp()){var k=new Te(new Ue(J(new K,["Note: ", " member `","` is private and cannot be used as a valid implementation"]))),l=[We(Xe(),h.fd().ld),We(Xe(),b.Ua())];k=Ye(k,J(new K,l));h=h.A();h=G(new H,k,h);k=O().c;Wy(a,new z(h,k),b,d,e)}}else if(t().d===h)Wy(a,O().c,b,d,e);else throw new w(h);c=c.f()}} function $x(a,b,c,d,e,g){var h=a.cd,k=a.jg,l=1+h.da|0,m=Hw(),n=Su(),r=op().ga;m=m.Hd(new Uu(n,r));l=new Iw(h.S,h.Ec,h.hc,h.Ed,l,h.Pc,h.Zc,h.Lb,h.yc,h.tb,h.$a,h.od,m);for(m=Xu().X();!c.b();)n=c.e(),m.ou(n.Ua(),new y((C=>D=>{if(D instanceof L)xm("Program reached and unexpected state.");else{if(t().d===D)return t(),new L(C);throw new w(D);}})(n))),c=c.f();for(;!b.b();){c=b.e();n=a.J;n.F&&(n=ut(Q(),"| ",n.r)+("Checking overriding for "+c+" against "+m.U(c.Ua()))+"...",ff(gf(),n+"\n"));r=G(new H,c,m.U(c.Ua())); a:if(n=r.w,t().d!==n){n=r.y;var v=r.w;if(n&&n.$classData&&n.$classData.rb.xE&&v instanceof L&&(v=v.k)&&v.$classData&&v.$classData.rb.xE){c=v;if(!c.Mp())break a;if(c instanceof bx&&!Gy(c.Mb)&&!d.L(c)){r=a.J;v=new Te(new Ue(J(new K,[""," member '","' is not virtual and cannot be overridden"])));Xe();Q();var x=n.fd().ld;x=[We(0,Nu(0,x)),We(Xe(),n.Ua())];v=Ye(v,J(new K,x));n=n.A();n=G(new H,v,n);v=Ye(new Te(new Ue(J(new K,["Originally declared here:"]))),u());c=c.A();c=G(new H,v,c);v=O().c;ay(r,new z(n, new z(c,v)),e);break a}if(c instanceof Vw&&!d.L(c)){r=a.J;v=new Te(new Ue(J(new K,["Inherited parameter named '","' is not virtual and cannot be overridden"])));x=[We(Xe(),n.Ua())];v=Ye(v,J(new K,x));n=n.A();n=G(new H,v,n);v=Ye(new Te(new Ue(J(new K,["Originally declared here:"]))),u());c=c.A();c=G(new H,v,c);v=O().c;ay(r,new z(n,new z(c,v)),e);break a}if(n instanceof bx&&rx(n.Mb)){r=a.J;v=new Te(new Ue(J(new K,["Cannot implement "," member '","' with a let binding"])));x=[We(Xe(),n.fd().ld),We(Xe(), n.Ua())];v=Ye(v,J(new K,x));x=n.A();v=G(new H,v,x);x=Ye(new Te(new Ue(J(new K,["Originally declared here:"]))),u());var A=c.A();x=G(new H,x,A);A=O().c;ay(r,new z(v,new z(x,A)),e)}n=n.Nn();r=n.ma();c=c.Nn();v=Sw(a.J).ob;Tw(a.J,n,c,e,r,l,v);break a}n=r.w;if(n instanceof L)r=n.k,n=a.J,v=new Te(new Ue(J(new K,[""," member '","' cannot override "," member of the same name declared in parent"]))),Xe(),Q(),x=c.fd().ld,c=[We(0,Nu(0,x)),We(Xe(),c.Ua()),We(Xe(),r.fd().ld)],c=Ye(v,J(new K,c)),v=g.A(),c=G(new H, c,v),v=Ye(new Te(new Ue(J(new K,["Originally declared here:"]))),u()),r=r.A(),r=G(new H,v,r),v=O().c,ay(n,new z(c,new z(r,v)),e);else throw new w(r);}b=b.f()}a=l.cb;up(tp(),h.S.li||l.cb.b());if(!a.b()){d=h.S.qa;g=h.S;g.F&&(l=ut(Q(),"| ",g.r)+"UNSTASHING... (out)",ff(gf(),l+"\n"));g.r=1+g.r|0;try{a.Ca(new y(C=>{if(null!==C){var D=C.h();for(C=C.j().m();C.s();){var F=C.t();a:{if(null!==F){var I=F.j();if(!0===F.Rc()){F=Sw(h.S).ob;Tw(h.S,I,D,e,k,h,F);break a}}if(null!==F&&(I=F.j(),!1===F.Rc())){F=Sw(h.S).ob; Tw(h.S,D,I,e,k,h,F);break a}throw new w(F);}}}else throw new w(C);}));a.mg();var B=void 0}finally{g.r=-1+g.r|0}dx(new E(d),g.qa)&&g.F&&(B=""+ut(Q(),"| ",g.r)+d.n(B),ff(gf(),B+"\n"))}} function Sba(a,b,c,d,e){var g=new y(B=>{var C=B.Ea();C=Pe(new E(C),b.Ea());var D=new U(()=>new Gu(b.Ea(),B.Ea()));if(!C)throw new Yj("assertion failed: "+Es(D));});c.b()||g.n(c.o());g=G(new H,b,c);c=g.y;var h=g.w;if(c instanceof bx&&h instanceof L){var k=h.k;if(k instanceof bx){up(tp(),!(c.Ts&&k.Ts));d=G(new H,c.Mb.wd,k.Mb.wd);a:{e=d.y;d=d.w;if(e instanceof L&&(e=!!e.k,d instanceof L)){d=!!d.k;t();e=new L(e||d);break a}e=t().d}d=c.Mb.Rb;g=t().d;h=c.Mb.Ch;var l=c.Mb.Yc,m=c.Mb.Pz,n=c.Mb.lx,r=c.Mb.Om, v=t().d,x=c.Mb.Qz,A=new U(()=>k.Mb.Qz);e=new Zn(e,d,g,h,l,m,n,r,v,x.b()?Es(A):x,c.Mb.Pl,c.Mb.Oz);t();a=a.J;d=c.jp;g=c.hh;h=k.hh;l=V(c.hh.q);a=new bx(a,d,e,Pu(g,h,l,!1),c.Ts||k.Ts);return new L(a)}}c=g.y;h=g.w;if(c instanceof Vw&&h instanceof L&&(h=h.k,h instanceof Vw)){if(c.bo){if(h.bo)return t(),a=new Vw(a.J,c.ij,Nv(c.ig,h.ig,V(c.ig.Va)),!0,c.Tz),new L(a);t();return new L(c)}t();return new L(h)}c=g.y;h=g.w;if(c instanceof Vw&&h instanceof L&&(h=h.k,h instanceof bx))return t(),a=a.J,e=c.Tz,d=h.Mb, g=c.ig.ra,h=h.hh,c=V(c.ig.ra.q),a=new bx(a,e,d,Pu(g,h,c,!1),!0),new L(a);c=g.y;h=g.w;if(c instanceof bx&&h instanceof L&&(h=h.k,h instanceof Vw))return t(),a=a.J,e=c.jp,d=c.Mb,g=h.ig.ra,c=c.hh,h=V(h.ig.ra.q),a=new bx(a,e,d,Pu(g,c,h,!1),!0),new L(a);c=g.y;h=g.w;if(t().d===h)return t(),new L(c);h=g.y;c=g.w;if(c instanceof L)return c=c.k,a=a.J,g=new Te(new Ue(J(new K,["Intersection of "," member and "," members currently unsupported"]))),l=[We(Xe(),h.fd().ld),We(Xe(),c.fd().ld)],g=Ye(g,J(new K,l)),d= d.A(),d=G(new H,g,d),g=new Te(new Ue(J(new K,["The "," member is defined here:"]))),l=[We(Xe(),h.fd().ld)],g=Ye(g,J(new K,l)),h=h.A(),g=G(new H,g,h),h=new Te(new Ue(J(new K,["The "," member is defined here:"]))),l=[We(Xe(),c.fd().ld)],h=Ye(h,J(new K,l)),c=c.A(),c=G(new H,h,c),h=O().c,ay(a,new z(d,new z(g,new z(c,h))),e),t().d;throw new w(g);} function jy(a,b,c,d,e){var g=m=>{var n=m.Ua();return G(new H,n,m)};if(c===u())g=u();else{var h=c.e(),k=h=new z(g(h),u());for(c=c.f();c!==u();){var l=c.e();l=new z(g(l),u());k=k.p=l;c=c.f()}g=h}op();for(g=pp(qp(),g);!b.b();)h=b.e(),g=g.IC(h.Ua(),new y(((m,n,r)=>v=>Sba(a,m,v,n,r))(h,d,e))),b=b.f();return g.tT().ha()} function Yx(a,b,c,d){var e=tf(b),g=a.jg,h=1+e.da|0,k=Hw(),l=Su(),m=op().ga;k=k.Hd(new Uu(l,m));h=new Iw(e.S,e.Ec,e.hc,e.Ed,h,e.Pc,e.Zc,e.Lb,e.yc,e.tb,e.$a,e.od,k);c=c.Hj;c=ox(a.J,c.b()?gl():c.o(),h,d,Ow(a),a.dt);k=Xu().X();c=c.Kc(b.da,!0,h,k);k=h.cb;up(tp(),e.S.li||h.cb.b());if(!k.b()){h=e.S.qa;l=e.S;l.F&&(m=ut(Q(),"| ",l.r)+"UNSTASHING... (out)",ff(gf(),m+"\n"));l.r=1+l.r|0;try{k.Ca(new y(v=>{if(null!==v){var x=v.h();for(v=v.j().m();v.s();){var A=v.t();a:{if(null!==A){var B=A.j();if(!0===A.Rc()){A= Sw(e.S).ob;Tw(e.S,B,x,d,g,e,A);break a}}if(null!==A&&(B=A.j(),!1===A.Rc())){A=Sw(e.S).ob;Tw(e.S,x,B,d,g,e,A);break a}throw new w(A);}}}else throw new w(v);}));k.mg();var n=void 0}finally{l.r=-1+l.r|0}dx(new E(h),l.qa)&&l.F&&(n=""+ut(Q(),"| ",l.r)+h.n(n),ff(gf(),n+"\n"))}n=a.J;h=V(a.J);k=t().d;l=t().d;m=O().c;var r=O().c;n=new lx(n,b.da,m,r,k,l,!1,h);h=a.jg;k=Sw(a.J).ob;Tw(a.J,c,n,d,h,b,k);b=vy(n);if(b instanceof z&&(a=b.z,b=b.p,c=O().c,null===c?null===b:c.i(b)))return a;xm("Program reached and unexpected state.")} function Lba(a,b,c,d,e,g,h,k){for(;;){var l=!1,m=null,n=b;if(n instanceof z){l=!0;m=n;var r=m.z;b=m.p;if(null!==r){var v=r.Uj,x=r.oj;r=r.oi;if(v instanceof Ww){m=v;if(!x.b())throw new Yj("assertion failed: "+x);n=a;l=c;x=m.uk;c=V(c.q);c=Pu(l,x,c,!1);l=new rp(m.tk);Od();d=jy(a,d,Pd(u(),l),k,h);e=e.bf(r);a=g;r=m.uk;g=V(g.q);g=Pu(a,r,g,!1);a=n;continue}}}if(l&&(b=m.z,r=m.p,null!==b)){b=b.Xi;Lw(a.J,Ye(new Te(new Ue(J(new K,["A trait can only inherit from other traits"]))),u()),b,h);b=r;continue}h=O().c; if(null===h?null===n:h.i(n))return new Gp(c,d,e,g);throw new w(n);}}function Oba(a,b){if(null===b)throw le();return b.sb?b.vb:me(b,new Xy(a))} function Mba(a,b,c,d,e,g,h,k,l,m){for(var n=a;;){var r=b;if(r instanceof z){var v=r,x=v.z,A=v.p;if(null!==x){var B=x.Uj,C=x.oj,D=x.oi,F=x.Xi,I=n.J;if(I.F){var M=ut(Q(),"| ",I.r)+"\x3d\x3e Inheriting from "+B;ff(gf(),M+"\n")}if(B instanceof Nw){var N=B;up(tp(),Pe(new E(d.Xa),e.da));tp();var P=N.hl.Ea();up(0,Pe(new E(P),e.da));tp();var T=N.jl.Ea();up(0,Pe(new E(T),e.da));var Y=c.Eq,Z=N.hl,S=Sw(n.J).ob;Tw(n.J,Y,Z,g,h,e,S);var ea=N.jl,ia=Sw(n.J).ob;Tw(n.J,d,ea,g,h,e,ia);up(tp(),D.b());var X=N.Jj.ie(), sa=new iy(X,new y($a=>Yy($a)),!0),Ja=un(C,sa),Xa=n.J,Fa=c.Eq,za=n.J,Qa=Wn(Ja,new Zy(n)),Ma=new Qv(za,Qa,V(n.J)),Ga=new $y(Xa,Fa,Ma,V(n.J)),ab=n,Hb=c,bc=un(Ja,c.Os),yb=new az(Hb.Sz,Ga,bc,c.Hu,c.Gu,c.Qs,c.Ps,c.Rm);n=ab;b=A;c=yb;continue}else if(B instanceof Ww){var tb=B;if(!C.b())throw new Yj("assertion failed: "+C);var eb=n,kb=n,Rb=c.Qs,Gb=tb.tk.ie(),vb=new iy(Gb,new y($a=>Yy($a)),!0);Od();var Tb=jy(kb,Rb,Pd(u(),vb),k,g),Nb=c.Ps.bf(D),ic=c.Rm,Va=tb.uk,cb=V(c.Rm.q),zb=Pu(ic,Va,cb,!1),Ub=new az(c.Sz, c.Eq,c.Os,c.Hu,c.Gu,Tb,Nb,zb);n=eb;b=A;c=Ub;continue}else if(B instanceof Yw){var jb=B,db=jb.Rf.gb.V,ub=c.Hu,Aa=n;if(!ub.b()){var va=ub.o(),Ra=Aa.J,rb=new Te(new Ue(J(new K,["Cannot inherit from more than one base class: "," and ",""]))),xb=[We(Xe(),va),We(Xe(),db)];Lw(Ra,Ye(rb,J(new K,xb)),F,g)}var mc=jb.Ei.ie();Od();var Ha=Pd(u(),mc);if(Ha.b())var Ka=Od().jC;else{Od();var Oa=new fp;Od();for(var Na=new fp,Da=Ha.m();Da.s();){var ta=Da.t();wp(Yy(ta)?Oa:Na,ta)}var Ya=G(new H,Oa.ha(),Na.ha());var dc= Ya.y;if(u().i(dc))Ka=G(new H,u(),Ha);else{var ka=Ya.w;Ka=u().i(ka)?G(new H,Ha,u()):Ya}}if(null===Ka)throw new w(Ka);var ya=n.J;if(ya.F){var Sa=ut(Q(),"| ",ya.r)+"argMembs "+C;ff(gf(),Sa+"\n")}var xc=n.J;if(xc.F){var Sb=ut(Q(),"| ",xc.r)+"selfSig "+jb.sk;ff(gf(),Sb+"\n")}var uc=n;t();var Lb=new L(db),lc=jb.Ei.ie(),Xb=un(C,lc),ec=c.Ps.bf(D),Ab=c.Rm,Ob=jb.sk,fb=V(c.Rm.q),Wa=Pu(Ab,Ob,fb,!1),bb=new az(c.Sz,c.Eq,c.Os,Lb,Xb,c.Qs,ec,Wa);n=uc;b=A;c=bb;continue}else if(B instanceof ax){b=A;continue}else throw new w(B); }}var Ia=O().c;if(null===Ia?null===r:Ia.i(r)){var Ua=n.J,pc=c;if(Ua.F){var sc=ut(Q(),"| ",Ua.r)+"Done inheriting: "+pc;ff(gf(),sc+"\n")}var Ba=n.J,ob=c.Eq,nc=n.J,Ib=px(n),vc=Ib.b()?O().c:Ib.o(),Vb=k.Sg,fc=Vb.b()?new Gl(O().c):Vb.o(),Bc=new Qv(nc,vc,ny(n.J,fc,!0)),Pb=new $y(Ba,ob,Bc,V(n.J)),Jb=bz(n.J,k,V(n.J),e),gc=V(Pb.q),Cb=Pu(Pb,Jb,gc,!1),cc=n.J;cu();var yc=k.hg;if(yc===u())var Mc=u();else{for(var qc=yc.e(),oc=new z(qc.j(),u()),Qc=oc,jc=yc.f();jc!==u();){var sb=jc.e(),Gc=new z(sb.j(),u());Qc=Qc.p= Gc;jc=jc.f()}Mc=oc}var Wb=du(0,Mc);tx(n.J);var Cc=t().d,Fc=new Qv(cc,l,jx(new kx,n.J,Wb,"type parameters",Cc,!0)),qd=V(Cb.q),Yb=Pu(Cb,Fc,qd,!1),Nc=n,ad=n.J.qa,Uc=n.J;if(Uc.F){var cd=ut(Q(),"| ",Uc.r)+(e.da+". Finalizing inheritance with "+Yb+" \x3c: ")+d;ff(gf(),cd+"\n")}Uc.r=1+Uc.r|0;try{up(tp(),Pe(new E(d.Xa),e.da));var kc=V(Yb.q),Vc=Pu(Yb,m,kc,!1),Hc=Sw(Nc.J).ob;Tw(Nc.J,Vc,d,g,h,e,Hc);var rc=void 0}finally{Uc.r=-1+Uc.r|0}if(dx(new E(ad),Uc.qa)&&Uc.F){var sd=""+ut(Q(),"| ",Uc.r)+ad.n(rc);ff(gf(), sd+"\n")}if(k.Pm.b()){var Kc=n,Qd=c,Ad=n.J.qa,kd=n.J;if(kd.F){var Hd=ut(Q(),"| ",kd.r)+"Checking self signature...";ff(gf(),Hd+"\n")}kd.r=1+kd.r|0;try{var Rd=Qd.Rm,Bd=Sw(Kc.J).ob;Tw(Kc.J,Yb,Rd,g,h,e,Bd);var ae=void 0}finally{kd.r=-1+kd.r|0}if(dx(new E(Ad),kd.qa)&&kd.F){var dd=""+ut(Q(),"| ",kd.r)+Ad.n(ae);ff(gf(),dd+"\n")}}var od=c.Rm,Ta=V(c.Rm.q),wb=Pu(od,m,Ta,!1);return new az(c.Sz,Yb,c.Os,c.Hu,c.Gu,c.Qs,c.Ps,wb)}throw new w(r);}} function my(a,b,c){Lw(a.J,Ye(new Te(new Ue(J(new K,["Cannot use `val` in constructor parameters"]))),u()),b,c)}function Yy(a){return a instanceof Vw?!(a.ij instanceof Ep):!1}function cz(a,b,c){return a.yl(t().d,!1,new fn((d,e)=>b.n(e)),c)}function dz(a){return!!(a&&a.$classData&&a.$classData.rb.ZH)}function ez(){}ez.prototype=new p;ez.prototype.constructor=ez;function fz(a,b){return Wk(new gz).Ob(b,new y(()=>{t();return R()}))}ez.prototype.$classData=q({JX:0},!1,"mlscript.OpApp$",{JX:1,g:1});var hz; function iz(){hz||(hz=new ez);return hz}function jz(){this.ld=null}jz.prototype=new p;jz.prototype.constructor=jz;function kz(){}kz.prototype=jz.prototype;function lz(){}lz.prototype=new p;lz.prototype.constructor=lz;function fu(a,b){a=b.m();a=new Ef(a,new y(c=>G(new H,t().d,new sm(tm().Cg,c))));Od();return new Gl(Pd(u(),a))} function mz(a,b){if(b instanceof Gl){a=b.Ra;a:{for(b=a;!b.b();){var c=b.e();if(!(c.h().b()&&Pe(new E(c.j().yb.je),!1)&&Pe(new E(c.j().yb.ch),!1))){b=!1;break a}b=b.f()}b=!0}if(b){t();if(a===u())a=u();else{b=a.e();c=b=new z(b.j().ya,u());for(a=a.f();a!==u();){var d=a.e();d=new z(d.j().ya,u());c=c.p=d;a=a.f()}a=b}return new L(a)}}return t().d}lz.prototype.$classData=q({OX:0},!1,"mlscript.PlainTup$",{OX:1,g:1});var nz;function eu(){nz||(nz=new lz);return nz} function oz(){this.aI=this.Yz=null;Ty();var a=u();this.Yz=Uy(a);pz();a=u();this.aI=qz(a)}oz.prototype=new p;oz.prototype.constructor=oz;function ho(a,b,c){Ly(a.Yz,b,c,!1);return c}oz.prototype.$classData=q({QX:0},!1,"mlscript.Polyfill",{QX:1,g:1}); function rz(a,b,c){a=new xl("x");var d=new xl("y"),e=O().c;a=new z(a,new z(d,e));d=new km("arguments");d=om(Al(),d,"length");d=new gn("\x3d\x3d\x3d",d,new hn("2"));e=rn(new gn(b,new km("x"),new km("y")));var g=O().c;e=new z(e,g);g=new xl("y");var h=O().c;g=new z(g,h);t();b=rn(new oo(g,new fe(new gn(b,new km("x"),new km("y")))));g=O().c;b=new dp(d,e,new z(b,g));d=O().c;return new sz(c,a,new z(b,d))} function tz(a,b,c){a=J(new K,[new xl("x")]);b=[rn(new $o(b,new km("x")))];b=J(new K,b);return new sz(c,(Od(),Pd(u(),a)),(Od(),Pd(u(),b)))} function uz(){this.PN=this.ON=null;vz=this;var a=kl();ol(a,new wz("prettyPrint",new y(b=>{var c=new km("value"),d=new xl("value"),e=O().c;d=new z(d,e);e=new $o("typeof",c);var g=J(new K,[c]);g=rn(new cn(new km("String"),(Od(),Pd(u(),g))));var h=O().c;g=new z(g,h);h=an("number");var k=O().c;h=G(new H,h,k);k=an("boolean");var l=om(Al(),c,"toString"),m=u();l=rn(new cn(l,m));m=O().c;k=G(new H,k,new z(l,m));l=an("function");m=om(Al(),c,"name");var n=an("\x3canonymous\x3e");m=new gn("??",m,n);n=an("[Function: "); m=new gn("+",n,m);n=an("]");m=rn(new gn("+",m,n));n=O().c;l=G(new H,l,new z(m,n));m=an("string");n=an('"');n=new gn("+",n,c);var r=an('"');n=rn(new gn("+",n,r));r=O().c;m=G(new H,m,new z(n,r));n=an("undefined");r=rn(an("undefined"));var v=O().c;n=G(new H,n,new z(r,v));r=an("object");v=new gn("\x3d\x3d\x3d",c,new km("null"));var x=rn(an("null")),A=O().c;x=new z(x,A);A=om(Al(),c,"constructor");A=om(Al(),A,"name");var B=an(" ");A=new gn("+",A,B);B=new km("JSON");B=om(Al(),B,"stringify");var C=J(new K, [c,new km("undefined"),new km("2")]);B=new cn(B,(Od(),Pd(u(),C)));A=rn(new gn("+",A,B));B=O().c;A=new z(A,B);B=new km("_");c=J(new K,[c]);c=rn(new cn(new km("String"),(Od(),Pd(u(),c))));C=O().c;c=new xz(A,new yz(B,new z(c,C)));A=O().c;c=new dp(v,x,new z(c,A));v=O().c;c=[h,k,l,m,n,G(new H,r,new z(c,v))];e=Tba(e,g,J(new K,c));g=O().c;return new sz(b,d,new z(e,g))})));ol(a,new wz("withConstruct",new y(b=>{var c=new km("Object"),d=new km("target"),e=new km("fields"),g=J(new K,[new xl("target"),new xl("fields")]), h=new $o("typeof",d),k=an("string");h=new gn("\x3d\x3d\x3d",h,k);k=new $o("typeof",d);var l=an("number");h=new gn("||",h,new gn("\x3d\x3d\x3d",k,l));k=new $o("typeof",d);l=an("boolean");h=new gn("||",h,new gn("\x3d\x3d\x3d",k,l));k=new $o("typeof",d);l=an("bigint");h=new gn("||",h,new gn("\x3d\x3d\x3d",k,l));k=new $o("typeof",d);l=an("symbol");h=new gn("||",h,new gn("\x3d\x3d\x3d",k,l));k=om(Al(),c,"assign");l=J(new K,[d,e]);k=rn(new cn(k,(Od(),Pd(u(),l))));l=O().c;h=new dp(h,new z(k,l),O().c);k= new gn("||",new gn("||",new gn("||",new gn("instanceof",d,new km("String")),new gn("instanceof",d,new km("Number"))),new gn("instanceof",d,new km("Boolean"))),new gn("instanceof",d,new km("BigInt")));l=om(Al(),c,"assign");var m=om(Al(),d,"valueOf"),n=u();m=J(new K,[new cn(m,n),d,e]);l=rn(new cn(l,(Od(),Pd(u(),m))));m=O().c;k=new dp(k,new z(l,m),O().c);l=new km("Array");l=om(Al(),l,"isArray");m=J(new K,[d]);l=new cn(l,(Od(),Pd(u(),m)));t();m=new km("Array");m=om(Al(),m,"from");n=J(new K,[d]);m=new cn(m, (Od(),Pd(u(),n)));m=new qn("clone",m);n=new xl("key");var r=[zz(Xo(new Yo,new km("clone"),new km("key")),Xo(new Yo,d,new km("key")))];r=J(new K,r);n=new Az(n,d,(Od(),Pd(u(),r)));r=new xl("key");var v=[zz(Xo(new Yo,new km("clone"),new km("key")),Xo(new Yo,e,new km("key")))];v=J(new K,v);r=new Az(r,e,(Od(),Pd(u(),v)));m=[m,n,r,rn(new km("clone"))];m=J(new K,m);l=new dp(l,Pd(u(),m),O().c);m=new gn("\x3d\x3d",d,new hn("null"));n=om(Al(),c,"assign");r=[new vo(O().c,O().c),new vo(O().c,O().c),e];r=J(new K, r);n=rn(new cn(n,(Od(),Pd(u(),r))));r=O().c;m=new dp(m,new z(n,r),O().c);n=om(Al(),c,"assign");e=[new vo(O().c,O().c),d,e];e=J(new K,e);e=new qn("copy",new cn(n,(Od(),Pd(u(),e))));n=om(Al(),c,"setPrototypeOf");r=new km("copy");c=om(Al(),c,"getPrototypeOf");d=J(new K,[d]);d=[r,new cn(c,(Od(),Pd(u(),d)))];d=J(new K,d);d=new cn(n,(Od(),Pd(u(),d)));d=[h,k,l,m,e,new Bo(d),rn(new km("copy"))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),g)),(Od(),Pd(u(),d)))})));ol(a,new Bz("toString",new y(b=>{var c=J(new K, [new xl("x")]),d=J(new K,[new km("x")]);d=[rn(new cn(new km("String"),(Od(),Pd(u(),d))))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),c)),(Od(),Pd(u(),d)))})));ol(a,new Bz("id",new y(b=>{var c=J(new K,[new xl("x")]),d=[rn(new km("x"))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),c)),(Od(),Pd(u(),d)))})));ol(a,new Bz("emptyArray",new y(b=>{var c=u(),d=[rn(new Wo(O().c))];d=J(new K,d);return new sz(b,c,(Od(),Pd(u(),d)))})));ol(a,new Bz("succ",new y(b=>{var c=J(new K,[new xl("x")]),d=[rn(new gn("+",new km("x"), new hn("1")))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),c)),(Od(),Pd(u(),d)))})));ol(a,new Bz("error",new y(b=>{var c=u(),d=new $m(new km("Error")),e=[an("an error was thrown")];e=J(new K,e);d=new cn(d,(Od(),Pd(u(),e)));d=J(new K,[new bn(d)]);return new sz(b,c,(Od(),Pd(u(),d)))})));ol(a,new Bz("length",new y(b=>{var c=J(new K,[new xl("x")]),d=new km("x");d=[rn(om(Al(),d,"length"))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),c)),(Od(),Pd(u(),d)))})));ol(a,new Bz("concat",new y(b=>rz(Cz(),"+",b)))); ol(a,new Bz("join",new y(b=>{var c=J(new K,[new xl("...xs")]),d=new km("xs");d=om(Al(),d,"join");var e=[new hn(Mp(Np(),""))];e=J(new K,e);d=[rn(new cn(d,(Od(),Pd(u(),e))))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),c)),(Od(),Pd(u(),d)))})));ol(a,new Bz("add",new y(b=>rz(Cz(),"+",b))));ol(a,new Bz("sub",new y(b=>rz(Cz(),"-",b))));ol(a,new Bz("mul",new y(b=>rz(Cz(),"*",b))));ol(a,new Bz("div",new y(b=>rz(Cz(),"/",b))));ol(a,new Bz("gt",new y(b=>rz(Cz(),"\x3e",b))));ol(a,new Bz("not",new y(b=>tz(Cz(), "!",b))));ol(a,new Bz("negate",new y(b=>tz(Cz(),"-",b))));ol(a,new Bz("eq",new y(b=>rz(Cz(),"\x3d\x3d\x3d",b))));ol(a,new Bz("ne",new y(b=>rz(Cz(),"!\x3d\x3d",b))));ol(a,new Bz("sgt",new y(b=>rz(Cz(),"\x3e",b))));ol(a,new Bz("slt",new y(b=>rz(Cz(),"\x3c",b))));ol(a,new Bz("sge",new y(b=>rz(Cz(),"\x3e\x3d",b))));ol(a,new Bz("sle",new y(b=>rz(Cz(),"\x3c\x3d",b))));ol(a,new Bz("eq",new y(b=>rz(Cz(),"\x3d\x3d\x3d",b))));ol(a,new Bz("unit",new y(b=>tz(Cz(),"undefined",b))));ol(a,new Bz("log",new y(b=> {var c=J(new K,[new xl("x")]),d=J(new K,[new km("x")]);d=[rn(new cn(new km("console.info"),(Od(),Pd(u(),d))))];d=J(new K,d);return new sz(b,(Od(),Pd(u(),c)),(Od(),Pd(u(),d)))})));ol(a,new Bz("discard",new y(b=>{var c=J(new K,[new xl("x")]),d=u();return new sz(b,(Od(),Pd(u(),c)),d)})));Od();this.ON=Pd(u(),a);this.PN=Dz(Ez(),Qt(this.ON,new y(b=>G(new H,b.Ua(),b))))}uz.prototype=new p;uz.prototype.constructor=uz;uz.prototype.$classData=q({RX:0},!1,"mlscript.Polyfill$",{RX:1,g:1});var vz; function Cz(){vz||(vz=new uz);return vz}function Fz(){}Fz.prototype=new p;Fz.prototype.constructor=Fz;function Gz(){}Gz.prototype=Fz.prototype;function Hz(a){this.$f=a}Hz.prototype=new p;Hz.prototype.constructor=Hz;function Iz(a,b){return new Hz(un(a.$f,b.$f))} function Rp(a,b){var c=b.$f;if(c instanceof z){var d=c.z;c=c.p;if(a.$f.b())return b;b=a.$f.MJ();t();a=a.$f.Mc();a=J(new K,[new Jz(""+a.Kq+d.Kq,a.$s)]);a=Pd(u(),a);return new Hz(dl(dl(c,a),b))}d=O().c;if(null===d?null===c:d.i(c))return a;throw new w(c);}function Kz(a){var b=a.$f;if(b===u())a=u();else{a=b.e();var c=a=new z(Lz(a),u());for(b=b.f();b!==u();){var d=b.e();d=new z(Lz(d),u());c=c.p=d;b=b.f()}}return new Hz(a)} function Pp(a,b){if(b)switch(a.$f.K()){case 0:return Sp(Qp(),"()");case 1:var c=a.$f;if(c===u())b=u();else for(b=c.e(),a=b=new z(Mz(b),u()),c=c.f();c!==u();){var d=c.e();d=new z(Mz(d),u());a=a.p=d;c=c.f()}return new Hz(b);default:return c=a.$f.e(),b=a.$f.f().Bb(1),d=a.$f.Mc(),a=new Jz("("+c.Kq,c.$s),t(),c=J(new K,[new Jz(""+d.Kq+")",d.$s)]),b=dl(Pd(u(),c),b),new Hz(new z(a,b))}else return a} function Nz(a){switch(a.$f.K()){case 0:case 1:return a;default:var b=a.$f.e(),c=a.$f.f().Bb(1);a=a.$f.Mc();b=new Jz("("+b.Kq,b.$s);t();a=J(new K,[new Jz(""+a.Kq+")",a.$s)]);c=dl(Pd(u(),a),c);return new Hz(new z(b,c))}}function Oz(a){if(0===a.$f.K())return Sp(Qp(),"{}");var b=new Jz("{",0),c=a.$f;if(c===u())a=u();else{a=c.e();var d=a=new z(Lz(a),u());for(c=c.f();c!==u();){var e=c.e();e=new z(Lz(e),u());d=d.p=e;c=c.f()}}t();d=J(new K,[new Jz("}",0)]);a=dl(Pd(u(),d),a);return new Hz(new z(b,a))} Hz.prototype.u=function(){return ze(this.$f,"","\n","")};Hz.prototype.$classData=q({iY:0},!1,"mlscript.SourceCode",{iY:1,g:1});function Pz(){this.SN=this.ye=this.RN=this.cI=this.zx=this.bA=null;Qz=this;Sp(Qp()," \x26 ");this.bA=Sp(Qp()," ");this.zx=Sp(Qp(),";");Sp(Qp(),": ");Sp(Qp()," | ");this.cI=Sp(Qp(),",");this.RN=Sp(Qp(),", ");this.ye=Rz(Qp(),O().c);Sp(Qp(),"{");Sp(Qp(),"}");Sp(Qp(),"\x3c");Sp(Qp(),"\x3e");this.SN=Sp(Qp()," \x3d\x3e ");Sp(Qp()," \x3d ")}Pz.prototype=new p; Pz.prototype.constructor=Pz;function Sp(a,b){t();a=J(new K,[new Jz(b,0)]);return new Hz(Pd(u(),a))}function Rz(a,b){if(b===u())a=u();else{a=b.e();var c=a=new z(new Jz(a,0),u());for(b=b.f();b!==u();){var d=b.e();d=new z(new Jz(d,0),u());c=c.p=d;b=b.f()}}return new Hz(a)}function Sz(a){for(var b=Qp().ye;!a.b();){var c=a.e();b=Iz(b,c);a=a.f()}return b} function Tz(a){var b=O().c;if(null===b?null===a:b.i(a))return Sp(Qp(),"{}");if(a instanceof z){b=a.z;var c=a.p,d=O().c;if(null===d?null===c:d.i(c))return 0{var h=G(new H,e,g);e=h.y;g=h.w;if(null!==g)return h=g.h(),Iz(e,Kz(Pe(new E(1+g.Sc()|0),a.K())?h:Rp(h,Qp().cI)));throw new w(h);})),Sp(Qp(),"}"))} function Uz(a){var b=O().c;if(null===b?null===a:b.i(a))return Sp(Qp(),"[]");if(a instanceof z){b=a.z;var c=a.p,d=O().c;if(null===d?null===c:d.i(c))return 0{var h=G(new H,e,g);e=h.y;g=h.w;if(null!==g)return h=g.h(),Iz(e,Kz(Pe(new E(1+g.Sc()|0),a.K())?h:Rp(h,Qp().cI)));throw new w(h);})),Sp(Qp(),"]"))} function Uba(a,b,c){return Hf(b).De(a.ye,new fn((d,e)=>{var g=G(new H,d,e);d=g.y;e=g.w;if(null!==e)return g=e.Sc(),Rp(Rp(d,e.h()),Pe(new E(g),-1+b.K()|0)?Qp().ye:c);throw new w(g);}))}Pz.prototype.$classData=q({jY:0},!1,"mlscript.SourceCode$",{jY:1,g:1});var Qz;function Qp(){Qz||(Qz=new Pz);return Qz}function Jz(a,b){this.Kq=a;this.$s=b}Jz.prototype=new p;Jz.prototype.constructor=Jz;function Lz(a){return new Jz(a.Kq,1+a.$s|0)}function Mz(a){return new Jz("("+a.Kq+")",a.$s)} Jz.prototype.u=function(){return""+ut(Q()," ",this.$s)+this.Kq};Jz.prototype.$classData=q({kY:0},!1,"mlscript.SourceLine",{kY:1,g:1});function Vz(){}Vz.prototype=new p;Vz.prototype.constructor=Vz;function Wz(){}Wz.prototype=Vz.prototype; Vz.prototype.jb=function(){var a=!1,b=null;if(Se()===this)return"space";if(Xr()===this)return"comma";if(ds()===this)return"semicolon";if(qs()===this)return"newline";if(ks()===this)return"indentation";if(rs()===this)return"deindentation";if(ts()===this)return"error";if(Yr()===this)return"quote";if(this instanceof fs)return"literal";if(this instanceof as)return a=this.Na,b=Xz(Q(),a),b.b()?b=!1:(b=b.o(),b=Ea(b),b=Or(Pr(),b)),b?"'"+a+"' keyword":"'"+a+"'";if(this instanceof bs)return this.ke?"operator": "identifier";if(this instanceof ss)return"selector";if(this instanceof Zr)return"opening "+this.qx.Ua();if(this instanceof es)return"closing "+this.Iw.Ua();if(this instanceof cs){a=!0;b=this;var c=b.Cc;if(Mk()===c)return"indented block"}if(a)return b.Cc.Ua()+" section";if(this instanceof gs)return"comment";throw new w(this);}; function Yz(a){a=a.Ra.m();a=new Ef(a,new y(b=>{if(null!==b){var c=b.h();b=b.j();var d=b.yb.je?"mut ":"",e=b.yb.Bh?"val ":"",g=b.yb.ch?"#":"";c=c.b()?"":c.o().x+": ";return d+e+g+c+Zz(b.ya,!1)+","}throw new w(b);}));return ze(a,""," ","")}function $z(a,b){a=a.Ra.m();a=new Ef(a,new y(c=>{if(null!==c){var d=c.h();c=c.j();var e=c.yb.je?"mut ":"",g=c.yb.Bh?"val ":"",h=c.yb.ch?"#":"";d=d.b()?"":d.o().x+": ";return e+g+h+d+aA(c.ya,!1,b)}throw new w(c);}));return ze(a,"",", ","")}function bA(){} bA.prototype=new p;bA.prototype.constructor=bA;bA.prototype.$classData=q({xY:0},!1,"mlscript.TypeDefs$VarianceStore$",{xY:1,g:1});function cA(a){a=a.m();a=new Ef(a,new y(b=>""+dA(b.j())+b.h()));return ze(a,"",", ","")}function eA(a,b,c,d,e,g){c=fA(gA(a),c);c=hA(b,c,g);if(a.F){var h=ut(Q(),"| ",a.r)+"allVarPols: "+cA(c);ff(gf(),h+"\n")}h=Xu().X();return Vba(a,b,g,h,c,d,e,g)} function iA(a,b,c,d){var e=new $e;if(a.F){var g=ut(Q(),"| ",a.r);if(e.sb)e=e.vb;else{if(null===e)throw le();if(e.sb)e=e.vb;else{var h=fA(gA(a),c);e=me(e,hA(b,h,d))}}g=g+"allVarPols: "+cA(e);ff(gf(),g+"\n")}g=jA().X();return Wba(a,b,c,t().d,d,d,g)} function kA(a,b,c,d,e){var g=new $e,h=new $e,k=new lA;k=Xba(k);var l=jA().X(),m=jA().X();mA(g.sb?g.vb:Yba(a,g,e,k,l,m),fA(gA(a),d),b);a.F&&(g=ut(Q(),"| ",a.r)+"[inv] "+ze(l,"",", ",""),ff(gf(),g+"\n"));if(a.F){g=ut(Q(),"| ",a.r);var n=k.m();n=new Ef(n,new y(B=>{t();return""+dA(new L(B.h().Rc()))+B.h().j()+" "+B.Sc()}));g=g+"[nums] "+ze(n,""," ; ","");ff(gf(),g+"\n")}var r=Xu().X();g=jA().X();n=jA().X();nA(a,b,fA(gA(a),d),h,e,g,n,r);r.Ca(new y(B=>{up(tp(),!B.j().b())}));a.F&&(h=ut(Q(),"| ",a.r),g= r.m(),g=new Ef(g,new y(B=>{t();return""+dA(new L(B.h().Rc()))+B.h().j()+" "+ze(B.j(),"{",",","}")})),h=h+"[occs] "+ze(g,""," ; ",""),ff(gf(),h+"\n"));var v=Xu().X();m=m.m();m=new Ef(m,new y(B=>B.h()));h=Su();g=op().ga;h=new Uu(h,g);m=oA(uv(),m,h);h=pA(m,v,e);var x=new aw(h);a.F&&(h=ut(Q(),"| ",a.r)+"[vars] "+m,ff(gf(),h+"\n"));a.F&&(h=ut(Q(),"| ",a.r)+"[rec] "+x.rc,ff(gf(),h+"\n"));for(h=k.m();h.s();){var A=h.t();a:{if(null!==A&&(g=A.h(),n=A.Sc(),null!==g)){A=g.Rc();g=g.j();up(tp(),0{if(!x.rc.L(B)){var C=G(new H,r.U(G(new H,!0,B)),r.U(G(new H,!1,B)));var D=C.w;if(C.y instanceof L&&R()===D)D=!0;else{D=C.y;var F=C.w;D=R()===D&&F instanceof L?!0:!1}if(D)return a.F&&(C=ut(Q(),"| ",a.r)+"1[!] "+B,ff(gf(),C+"\n")),C=R(),B=G(new H,B,C),v.$(B);if(!Nm(new E(C),G(new H,R(),R())))throw new Yj("assertion failed: "+B+" has no occurrences..."); }}));m.Ca(new y(B=>{if(B.Sb.b()&&!v.L(B)){if(a.F){var C=ut(Q(),"| ",a.r)+("2[v] "+B+" "+r.U(G(new H,!0,B))+" ")+r.U(G(new H,!1,B));ff(gf(),C+"\n")}C=r.U(G(new H,!0,B)).m();for(C=new xo(C,new y(T=>new qA(T)));C.s();){var D=C.t();if(D instanceof Fv||D instanceof fw)if(x.rc.L(B))F=!1;else{F=r.U(G(new H,!1,B));var F=F.b()?!1:F.o().L(D)}else F=!1;if(F){F=cw(a);for(var I=rA(B),M=D;!I.b();){var N=I.e(),P=V(M.q);M=sA(M,N,P);I=I.f()}I=M;M=vy(B);N=D;for(D=M;!D.b();)M=N,N=D.e(),P=V(M.q),N=dv(M,N,P,!1),D=D.f(); F=tA(F,I,N,V(cw(a).Vu));D=a;D.F&&(D=ut(Q(),"| ",D.r)+(" [..] "+B+" :\x3d ")+F,ff(gf(),D+"\n"));t();F=G(new H,B,new L(F));v.$(F)}else if(D instanceof lx&&((Ot(new E(D),B)||v.L(D)||v.L(B)?0:!x.rc.L(B))?(F=r.U(G(new H,!1,B)),F=F.b()?!1:F.o().L(D)):F=!1,F)){F=cw(a);I=rA(B);for(M=D;!I.b();)N=I.e(),P=V(M.q),M=sA(M,N,P),I=I.f();I=M;M=vy(B);N=D;for(D=M;!D.b();)M=N,N=D.e(),P=V(M.q),N=dv(M,N,P,!1),D=D.f();F=tA(F,I,N,V(cw(a).Vu));D=a;D.F&&(D=ut(Q(),"| ",D.r)+(" [..] "+B+" :\x3d ")+F,ff(gf(),D+"\n"));t();F= G(new H,B,new L(F));v.$(F)}}}}));m.Ca(new y(B=>{if(B.Sb.b()&&!v.L(B)){var C=a.qa;if(a.F){var D=ut(Q(),"| ",a.r),F=iu(ju(),r.U(G(new H,!0,B)));F=ze(F,"","","");var I=iu(ju(),r.U(G(new H,!1,B)));D=D+("3[v] "+B+" +"+F+" -")+ze(I,"","","");ff(gf(),D+"\n")}a.r=1+a.r|0;try{uA(a,!0,r,B,v);uA(a,!1,r,B,v);var M=void 0}finally{a.r=-1+a.r|0}dx(new E(C),a.qa)&&a.F&&(B=""+ut(Q(),"| ",a.r)+C.n(M),ff(gf(),B+"\n"))}}));a.F&&(h=ut(Q(),"| ",a.r),g=v.Ja(new y(B=>B.h().u()+" -\x3e "+B.j())),h=h+"[sub] "+ze(g,"",", ", ""),ff(gf(),h+"\n"));a.F&&(h=ut(Q(),"| ",a.r)+"[bounds] "+xx(b),ff(gf(),h+"\n"));x.rc=pA(m,v,e);a.F&&(m=ut(Q(),"| ",a.r)+"[rec] "+x.rc,ff(gf(),m+"\n"));m=Xu().X();h=ap();return Zba(a,b,fA(gA(a),d),h,e,v,c,m,l,x,k,r)} function vA(a,b,c,d){var e=fA(gA(a),(t(),new L(c))),g=hA(b,e,d);a.F&&(e=ut(Q(),"| ",a.r)+"allVarPols: "+cA(g),ff(gf(),e+"\n"));e=jA().X();g=g.m();var h=new wA(a);g=new Ex(g,h);g=new iy(g,new y(k=>{if(null!==k){var l=k.h(),m=k.j();if(null!==l)return xA(l.j(),!1).L(m)}throw new w(k);}),!1);op();g=pp(qp(),g);a.F&&(h=ut(Q(),"| ",a.r)+"consed: "+g,ff(gf(),h+"\n"));return $ba(a,(t(),new L(c)),b,d,e,g)} function aca(a,b,c,d){c=fA(gA(a),c);var e=hA(b,c,d);a.F&&(c=ut(Q(),"| ",a.r)+"allVarPols: "+cA(e),ff(gf(),c+"\n"));jA().X();var g=Xu().X();e.Ca(new y(h=>{if(null!==h){var k=h.h();h=h.j();if(h instanceof L){var l=!!h.k;a.F&&(h=ut(Q(),"| ",a.r)+"Consider "+k,ff(gf(),h+"\n"));h=k.Sb;if(h.b())h=R();else{h=h.o();var m=O().c;h=new L(new z(h,m))}h=h.b()?l?vy(k):rA(k):h.o();if(h instanceof z){var n=h.z;h=h.p;m=O().c;(null===m?null===h:m.i(h))&&e.Ca(new y(r=>{if(null!==r){var v=r.h();r=r.j();if(r instanceof L&&(r=!!r.k,Pe(new E(r),l)&&dx(new E(v),k)&&!g.L(k)&&!g.L(v))){var x=v.Sb;if(x.b())x=R();else{x=x.o();var A=O().c;x=new L(new z(x,A))}x=x.b()?r?vy(v):rA(v):x.o();if(x instanceof z&&(r=x.z,x=x.p,A=O().c,null===A?null===x:A.i(x))&&(a.F&&(x=ut(Q(),"| ",a.r)+("Consider "+k+" ~ ")+v,ff(gf(),x+"\n")),yA(a,n,r,k,v)))return a.F&&(r=ut(Q(),"| ",a.r)+("Yes! "+v+" :\x3d ")+k,ff(gf(),r+"\n")),v=G(new H,v,k),g.$(v)}}}))}}}}));a.F&&(c=ut(Q(),"| ",a.r)+"[subs] "+g,ff(gf(),c+"\n"));return g.b()?b:bca(a,b,(op(),pp(qp(), g)),d)}function cca(a,b,c,d){return c.Hk(b,new U(()=>{if(d)return b;var e=V(a);t();var g=new L(b),h=b.kg,k=O().c,l=O().c;e=new lx(a,b.Xa,k,l,g,h,!1,e);a.F&&(g=ut(Q(),"| ",a.r)+("Renewed "+b+" ~\x3e ")+e,ff(gf(),g+"\n"));return e}))}function Vba(a,b,c,d,e,g,h,k){if(b instanceof zA)return AA(a,b,t().d,t().d,d,e,c,g,h,k);if(b instanceof BA){CA(a);t();var l=new L(b);if(!l.b())return dca(l.k,new y(m=>AA(a,m,t().d,t().d,d,e,c,g,h,k)),c)}throw new w(b);} function DA(a,b,c,d,e,g,h,k,l){if(null===b)throw le();if(b.sb)return b.vb;var m=c.Va,n=c.Oa;n.b()?n=R():(n=n.o(),n=new L(AA(a,n,t().d,t().d,d,e,g,h,k,l)));return me(b,new Uw(m,n,AA(a,c.ra,t().d,t().d,d,e,g,h,k,l),c.Pd))} var AA=function EA(a,b,c,d,e,g,h,k,l,m){var r=tc();try{var v=!1,x=null,A=!1,B=null;if(b instanceof mx){var C=b.hi;if(null!==C)return EA(a,C,c,t().d,e,g,h,k,l,m)}if(b instanceof lx){if(c.b())var D=!0;else{var F=c.o().j();D=Pe(new E(F),b)}var I=D?c:R();if(!I.b()){var M=I.o().Rc();throw Hq(new Iq,r,new FA(a,M,V(a)));}var N=new GA(!1),P=e.Hk(b,new U(()=>{N.Am=!0;return cca(a,b,e,l)}));if(N.Am){var T=b.Sb;if(T instanceof L){var Y=T.k,Z=!1,S=null,ea=HA(g,b);a:if(ea.b()||!P.Sb.b()){t();var ia=EA(a,Y,t().d, t().d,e,g,h,k,l,m);wy(P,new L(ia))}else{t().d===ea&&xm("Program reached and unexpected state.");if(ea instanceof L&&(Z=!0,S=ea,!0===!!S.k)){t();var X=G(new H,!0,b),sa=EA(a,Y,new L(X),t().d,e,g,h,k,l,m),Ja=O().c,Xa=new z(sa,Ja);b:for(var Fa;;)if(Xa.b()){Fa=u();break}else{var za=Xa.e(),Qa=Xa.f();if(!0===!!Kv(za,h))Xa=Qa;else for(var Ma=Xa,Ga=Qa;;){if(Ga.b())Fa=Ma;else{var ab=Ga.e();if(!0!==!!Kv(ab,h)){Ga=Ga.f();continue}for(var Hb=Ga,bc=new z(Ma.e(),u()),yb=Ma.f(),tb=bc;yb!==Hb;){var eb=new z(yb.e(), u());tb=tb.p=eb;yb=yb.f()}for(var kb=Hb.f(),Rb=kb;!kb.b();){var Gb=kb.e();if(!0===!!Kv(Gb,h)){for(;Rb!==kb;){var vb=new z(Rb.e(),u());tb=tb.p=vb;Rb=Rb.f()}Rb=kb.f()}kb=kb.f()}Rb.b()||(tb.p=Rb);Fa=bc}break b}}IA(P,Fa);break a}if(Z&&!1===!!S.k){t();var Tb=G(new H,!1,b),Nb=EA(a,Y,new L(Tb),t().d,e,g,h,k,l,m),ic=O().c,Va=new z(Nb,ic);b:for(var cb;;)if(Va.b()){cb=u();break}else{var zb=Va.e(),Ub=Va.f();if(!0===!!JA(zb,h))Va=Ub;else for(var jb=Va,db=Ub;;){if(db.b())cb=jb;else{var ub=db.e();if(!0!==!!JA(ub, h)){db=db.f();continue}for(var Aa=db,va=new z(jb.e(),u()),Ra=jb.f(),rb=va;Ra!==Aa;){var xb=new z(Ra.e(),u());rb=rb.p=xb;Ra=Ra.f()}for(var mc=Aa.f(),Ha=mc;!mc.b();){var Ka=mc.e();if(!0===!!JA(Ka,h)){for(;Ha!==mc;){var Oa=new z(Ha.e(),u());rb=rb.p=Oa;Ha=Ha.f()}Ha=mc.f()}mc=mc.f()}Ha.b()||(rb.p=Ha);cb=va}break b}}KA(P,cb)}else throw new w(ea);}}else if(t().d===T){var Na=HA(g,b);if(Na.b())var Da=!0;else{var ta=!!Na.o();Da=Pe(new E(ta),!0)}if(Da){if(k)var Ya=vy(b),dc=er(Ya).m();else dc=vy(b).m();var ka= new Ef(dc,new y(ng=>{t();var kh=G(new H,!0,b);return EA(a,ng,new L(kh),t().d,e,g,h,k,l,m)}));if(ka.s()){if(!ka.s())throw nv("empty.reduceLeft");for(var ya=!0,Sa=null;ka.s();){var xc=ka.t();if(ya)Sa=xc,ya=!1;else{var Sb=Sa,uc=xc,Lb=V(Sb.q);Sa=dv(Sb,uc,Lb,!1)}}lc=new L(Sa)}else var lc=R();if(lc.b())var Xb=!0;else{var ec=lc.o();Xb=!Kv(ec,h)}var Ab=(Xb?lc:R()).ha()}else Ab=O().c;IA(P,Ab);var Ob=HA(g,b);if(Ob.b())var fb=!0;else{var Wa=!!Ob.o();fb=Pe(new E(Wa),!1)}if(fb){if(k)var bb=rA(b),Ia=er(bb).m(); else Ia=rA(b).m();var Ua=new Ef(Ia,new y(ng=>{t();var kh=G(new H,!1,b);return EA(a,ng,new L(kh),t().d,e,g,h,k,l,m)}));if(Ua.s()){if(!Ua.s())throw nv("empty.reduceLeft");for(var pc=!0,sc=null;Ua.s();){var Ba=Ua.t();if(pc)sc=Ba,pc=!1;else{var ob=sc,nc=Ba,Ib=V(ob.q);sc=sA(ob,nc,Ib)}}vc=new L(sc)}else var vc=R();if(vc.b())var Vb=!0;else{var fc=vc.o();Vb=!JA(fc,h)}var Bc=(Vb?vc:R()).ha()}else Bc=O().c;KA(P,Bc)}else throw new w(T);}return P}if(b instanceof LA){v=!0;x=b;var Pb=x.ic,Jb=x.jc;if(!0===x.tc){var gc= EA(a,Pb,c,d,e,g,h,k,l,m),Cb=EA(a,Jb,c,d,e,g,h,k,l,m),cc=V(gc.q);return dv(gc,Cb,cc,!1)}}if(v){var yc=x.ic,Mc=x.jc;if(!1===x.tc){var qc=EA(a,yc,c,d,e,g,h,k,l,m),oc=EA(a,Mc,c,d,e,g,h,k,l,m),Qc=V(qc.q);return Pu(qc,oc,Qc,!1)}}if(b instanceof MA){var jc=b.Fc;if(c.b())var sb=R();else{var Gc=c.o();t();sb=new L(G(new H,!Gc.h(),Gc.j()))}var Wb=EA(a,jc,sb,t().d,e,g,h,k,l,m),Cc=jc.ma();return NA(Wb,Cc,!1)}if(b instanceof OA){A=!0;B=b;var Fc=B.Hi;if(l)return new OA(a,EA(a,Fc,c,d,e,g,h,k,l,m),Fc.ma())}if(A)return EA(a, B.Hi,c,d,e,g,h,k,l,m);if(b instanceof fw&&a.XE.L(b.qb)&&PA(b,h))return EA(a,QA(b,h),c,t().d,e,g,h,k,l,m);if(b instanceof Qv){for(var qd=b.Ba,Yb=xv(a),Nc=qd,ad=null,Uc=null;Nc!==u();){var cd=Nc.e();a:{if(null!==cd){var kc=cd.h(),Vc=cd.j();if(null!==kc){var Hc=kc.x,rc=new $e;b:{for(var sd=Hc.length,Kc=0;KcEA(a,kh,t().d,t().d,e,g,h,k,l,m)),h)}catch(ng){if(ng instanceof Iq){var Bh=ng;if(Bh.Qg===r)return Bh.Cj();throw Bh;}throw ng;}}; function eca(a,b,c,d,e,g,h,k){if(null===b)throw le();if(b.sb)return b.vb;lv();var l=x=>{x=x.h().x;a:{for(var A=x.length,B=0;B{var A=x.Va,B=x.Oa;if(B.b())B=R();else{B=B.o();if(e.b())var C=R();else C=!!e.o(),C=new L(!C);B=new L(VA(a,B,C,t().d,g,h,k))}return new Uw(A,B,VA(a,x.ra,e,t().d,g,h,k),x.Pd)})))}function WA(a,b,c,d,e,g,h,k){return b.sb?b.vb:eca(a,b,c,d,e,g,h,k)} function XA(a,b,c,d,e,g,h){Nx(a,new U(()=>"DNF: "+b));var k=Pt(b.xe,new y(n=>{if(null!==n){var r=n.Pf,v=n.Of,x=n.Af;if(ow(n.Nf)&&r.b()&&(!Gw(v)||!x.b()))return t(),new fe(n)}t();return new Ud(n)}));if(null!==k)k=G(new H,k.h(),k.j());else throw new w(k);var l=k.h();k=k.j();if(l.b())l=a.ib;else{l=YA(l,a.La,new fn((n,r)=>{r=r.zf(!0);var v=V(r.q);r=NA(r,v,!1);v=V(n.q);return Pu(n,r,v,!1)}));var m=new y(n=>!n);l=VA(a,l,c.b()?R():new L(m.n(c.o())),t().d,e,g,h);m=V(l.q);l=NA(l,m,!1)}m=ZA($A(a));k=Qt(qw(k, m),new y(n=>{n.Pf.Ca(new y(r=>{aB(a,r,h,e,g)}));n.Af.Ca(new y(r=>{aB(a,r,h,e,g)}));return fca(n,new y(r=>{if(r instanceof Ku){var v=r.fc,x=r.vd,A=r.be,B=r.Me,C=new y(Pa=>{if(null!==Pa){var Db=Pa.h(),Oc=Pa.j();if(null!==Oc)return Pa=new fw(a,Oc.qb,bw(Oc,c,new fn((Tc,Sd)=>VA(a,Sd,Tc,t().d,e,g,h)),e),Oc.Xl),G(new H,Db,Pa)}throw new w(Pa);}),D=Su(),F=op().ga,I=B.gR(C,new Uu(D,F)),M=x.m(),N=new Ex(M,new bB(a)),P=Aq(Bq(),N),T=!1,Y=null;if(v instanceof L){T=!0;Y=v;var Z=Y.k;if(Z instanceof Mu){var S=Z.pd, ea=Z.JE;if(S instanceof vl){var ia=S.x;if(!a.cn.L(ia)&&e.tb.L(Nu(Q(),ia))&&!a.$c){var X=Nu(Q(),ia),sa=new Ep(X),Ja=e.tb.n(X),Xa=A.Ba;op();var Fa=pp(qp(),Xa),za=ry(lv(),A.Ba,new y(Pa=>cB(Pa,new y(Db=>{var Oc=new y(Tc=>!Tc);return VA(a,Db,c.b()?R():new L(Oc.n(c.o())),t().d,e,g,h)}),new y(Db=>VA(a,Db,c,t().d,e,g,h))))),Qa=new Qv(A.q,za,A.Nj);Nx(a,new U(()=>"rcd2 "+Qa));var Ma=dB(Ja),Ga=Ja.Wl,ab=Qt(Hf(Ja.Xm),new y(Pa=>{if(null!==Pa){var Db=Pa.h(),Oc=Pa.Sc();if(null!==Db){var Tc=Db.j(),Sd=new vl(sa.V+ "#"+Db.h().V);Pa=I.U(sa);Db=new y(Jc=>{Jc=eB(Jc.Zb,Oc);return(new y(vd=>{t();return new Uw(a,new L(vd),vd,V(a))})).n(Jc)});Pa=Pa.b()?R():new L(Db.n(Pa.o()));Pa=iu(ju(),Pa);Db=Qa.Ba.m();Db=new iy(Db,new y(Jc=>Pe(new E(Jc.h()),Sd)),!1);Db=new Ef(Db,new y(Jc=>Jc.j()));Pa=Pa.tl(Db).De(G(new H,a.ib,a.La),new fn((Jc,vd)=>{var hd=G(new H,Jc,vd);Jc=hd.y;var de=hd.w;if(null!==Jc&&(vd=Jc.h(),Jc=Jc.j(),null!==de)){hd=de.Oa;de=de.ra;var ye=new U(()=>a.ib);hd=hd.b()?Es(ye):hd.o();ye=V(vd.q);vd=dv(vd,hd,ye,!1); hd=V(Jc.q);return G(new H,vd,Pu(Jc,de,hd,!1))}throw new w(hd);}));return(new y(Jc=>{if(null!==Jc){var vd=Jc.h();Jc=Jc.j();var hd=Ma.n(Tc);if(null!==hd){var de=hd.qe;if(!0===hd.Qd&&!0===de)return dw(cw(a),a.ib,a.La,ew(cw(a)),e)}if(null!==hd&&(de=hd.qe,!1===hd.Qd&&!1===de))return dw(cw(a),vd,Jc,ew(cw(a)),e);if(null!==hd)return hd.Qd?Jc:vd;throw new w(hd);}throw new w(Jc);})).n(Pa)}}throw new w(Pa);})),Hb=new fw(a,Ga,ab,V(a));Nx(a,new U(()=>"typeRef "+Hb));var bc=fB(a,gB(Hb,!0,!1,e),!0,e);Nx(a,new U(()=> "clsFields "+ze(bc,"",", ","")));ea.Ja(new y(Pa=>Nu(Q(),Pa.V))).bc(X).Ce(P);var yb=new Qv(a,gy(Qa.Ba,new y(Pa=>{if(null!==Pa){var Db=Pa.h(),Oc=Pa.j();Pa=bc.U(Db);var Tc=new y(Sd=>{var Jc=Xu().X();if(Fw(Sd,Oc,e,Jc))return!0;Jc=Fa.U(Db);var vd=new y(hd=>{var de=Xu().X();return Fw(Sd,hd,e,de)});return!Jc.b()&&!!vd.n(Jc.o())});return!Pa.b()&&!!Tc.n(Pa.o())}throw new w(Pa);}),!0),Qa.Nj),tb=Qa.Ba,eb=op(),kb=tb.Gb(eb.ga).h(),Rb=Aq(Bq(),kb),Gb=bc.tj(),vb=new iy(Gb,new y(Pa=>hB(ve(),Pa.x)?!0:Rb.L(Pa)),!0), Tb=Su(),Nb=op().ga,ic=new Uu(Tb,Nb),Va=oA(uv(),vb,ic),cb=Va.b()?Hb:iB(Hb,Va);if(Qa.Ba.ul(new y(Pa=>{if(null!==Pa){var Db=Pa.h(),Oc=Pa.j();Pa=bc.U(Db);var Tc=new y(Sd=>{var Jc=Xu().X();if(Fw(Oc,Sd,e,Jc))return!0;Jc=Fa.U(Db);var vd=new y(hd=>{var de=Xu().X();return Fw(hd,Sd,e,de)});return!Jc.b()&&!!vd.n(Jc.o())});return Pa.b()||!!Tc.n(Pa.o())}throw new w(Pa);}))){var zb=Lu(yb),Ub=V(Hb.q);jb=Pu(Hb,zb,Ub,!1)}else if(yb.Ba.b())var jb=cb;else{var db=Lu(yb);jb=new $y(a,cb,db,V(a))}ms();ms();var ub=new jB(da(Ru)), Aa=kB(x,ub),va=Su(),Ra=op().ga,rb=Tu(Aa,new Uu(va,Ra)),xb=lB(rb,jb,new fn((Pa,Db)=>{var Oc=V(Pa.q);return Pu(Pa,Db,Oc,!1)})),mc=I.xj(Ja.Wl),Ha=new Ou(mc);return mB(Ha,xb,new fn((Pa,Db)=>{var Oc=V(Pa.q);return Pu(Pa,Db,Oc,!1)}))}}}}if(T){var Ka=Y.k;if(Ka instanceof Mu){var Oa=Ka.pd,Na=Ka.JE;if(Oa instanceof vl){var Da=Oa.x;if(!a.cn.L(Da)&&e.$a.L(Da)&&!e.$a.n(Da).eo.b()){var ta=new Ep(Da),Ya=e.$a.n(Da).eo;a:{if(Ya instanceof L){var dc=Ya.k;if(dc instanceof Yw){var ka=dc;break a}}xm("Program reached and unexpected state.")}var ya= A.Ba;op();var Sa=pp(qp(),ya),xc=ry(lv(),A.Ba,new y(Pa=>cB(Pa,new y(Db=>{var Oc=new y(Tc=>!Tc);return VA(a,Db,c.b()?R():new L(Oc.n(c.o())),t().d,e,g,h)}),new y(Db=>VA(a,Db,c,t().d,e,g,h))))),Sb=new Qv(A.q,xc,A.Nj);Nx(a,new U(()=>"rcd2 "+Sb));var uc=ka.Rf.gb,Lb=Qt(Hf(ka.gh),new y(Pa=>{if(null!==Pa){var Db=Pa.h(),Oc=Pa.Sc();if(null!==Db){var Tc=Db.hb,Sd=new vl(ta.V+"#"+Db.kc.V);Pa=I.U(ta);Db=new y(Jc=>{Jc=eB(Jc.Zb,Oc);return(new y(vd=>{t();return new Uw(a,new L(vd),vd,V(a))})).n(Jc)});Pa=Pa.b()?R(): new L(Db.n(Pa.o()));Pa=iu(ju(),Pa);Db=Sb.Ba.m();Db=new iy(Db,new y(Jc=>Pe(new E(Jc.h()),Sd)),!1);Db=new Ef(Db,new y(Jc=>Jc.j()));Pa=Pa.tl(Db).De(G(new H,a.ib,a.La),new fn((Jc,vd)=>{var hd=G(new H,Jc,vd);Jc=hd.y;var de=hd.w;if(null!==Jc&&(vd=Jc.h(),Jc=Jc.j(),null!==de)){hd=de.Oa;de=de.ra;var ye=new U(()=>a.ib);hd=hd.b()?Es(ye):hd.o();ye=V(vd.q);vd=dv(vd,hd,ye,!1);hd=V(Jc.q);return G(new H,vd,Pu(Jc,de,hd,!1))}throw new w(hd);}));return(new y(Jc=>{if(null!==Jc){var vd=Jc.h();Jc=Jc.j();var hd=RA(ka,Tc, e);if(null!==hd){var de=hd.qe;if(!0===hd.Qd&&!0===de)return dw(cw(a),a.ib,a.La,ew(cw(a)),e)}if(null!==hd&&(de=hd.qe,!1===hd.Qd&&!1===de))return dw(cw(a),vd,Jc,ew(cw(a)),e);if(null!==hd)return hd.Qd?Jc:vd;throw new w(hd);}throw new w(Jc);})).n(Pa)}}throw new w(Pa);})),lc=new fw(a,uc,Lb,V(a));Nx(a,new U(()=>"typeRef "+lc));var Xb=fB(a,gB(lc,!0,!1,e),!0,e);Nx(a,new U(()=>"clsFields "+ze(Xb,"",", ","")));Na.Ja(new y(Pa=>Nu(Q(),Pa.V))).bc(Da).Ce(P);var ec=new Qv(a,gy(Sb.Ba,new y(Pa=>{if(null!==Pa){var Db= Pa.h(),Oc=Pa.j();Pa=Xb.U(Db);var Tc=new y(Sd=>{var Jc=Xu().X();if(Fw(Sd,Oc,e,Jc))return!0;Jc=Sa.U(Db);var vd=new y(hd=>{var de=Xu().X();return Fw(Sd,hd,e,de)});return!Jc.b()&&!!vd.n(Jc.o())});return!Pa.b()&&!!Tc.n(Pa.o())}throw new w(Pa);}),!0),Sb.Nj),Ab=Sb.Ba,Ob=op(),fb=Ab.Gb(Ob.ga).h();Aq(Bq(),fb);ms();ms();var Wa=new jB(da(Ru)),bb=kB(x,Wa),Ia=Su(),Ua=op().ga,pc=Tu(bb,new Uu(Ia,Ua)),sc=V(lc.q),Ba=lB(pc,Pu(lc,ec,sc,!1),new fn((Pa,Db)=>{var Oc=V(Pa.q);return Pu(Pa,Db,Oc,!1)})),ob=I.xj(ka.Rf.gb),nc= new Ou(ob);return mB(nc,Ba,new fn((Pa,Db)=>{var Oc=V(Pa.q);return Pu(Pa,Db,Oc,!1)}))}}}}var Ib=new $e,vc=!1,Vb=null;a:{if(v instanceof L){vc=!0;Vb=v;var fc=Vb.k;if(fc instanceof zv){var Bc=fc.Yb,Pb=Bc.K(),Jb=gy(A.Ba,new y(Pa=>{Q();return P.L(gca(Pa.h().x,new y(Db=>Nm(new E(hc(Ea(Db))),hc(35)))))}),!0),gc=Pt(Jb,new y(Pa=>{var Db=nB(Pa.h()),Oc=new oB(0,Pb,1),Tc=new y(Sd=>pB(Oc,Sd|0));Db=Db.b()||Tc.n(Db.o())?Db:R();Tc=new y(Sd=>G(new H,Sd|0,Pa.j()));Db=Db.b()?R():new L(Tc.n(Db.o()));Tc=new U(()=>Pa); if(Db.b())return O(),Db=Es(Tc),new Ud(Db);O();Db=Db.o();return new fe(Db)}));if(null!==gc)var Cb=G(new H,gc.h(),gc.j());else throw new w(gc);var cc=Cb.h(),yc=Cb.j();op();var Mc=pp(qp(),cc),qc=Bc.m(),oc=new Ao(qc),Qc=new Ef(oc,new y(Pa=>{if(null!==Pa){var Db=Pa.h(),Oc=Pa.Sc();if(null!==Db)return Pa=Db.h(),Db=Db.j(),Oc=Mc.Se(Oc,new U(()=>{var Tc=a.La,Sd=V(a);return new Uw(Tc.q,R(),Tc,Sd)})),Oc=cB(Nv(Db,Oc,V(Db.Va)),new y(Tc=>{var Sd=new y(Jc=>!Jc);return VA(a,Tc,c.b()?R():new L(Sd.n(c.o())),t().d,e, g,h)}),new y(Tc=>VA(a,Tc,c,t().d,e,g,h))),G(new H,Pa,Oc)}throw new w(Pa);}));Od();var jc=Pd(u(),Qc);t();var sb=new zv(a,jc,fc.Nq),Gc=new L(sb),Wb=ry(lv(),yc,new y(Pa=>cB(Pa,new y(Db=>{var Oc=new y(Tc=>!Tc);return VA(a,Db,c.b()?R():new L(Oc.n(c.o())),t().d,e,g,h)}),new y(Db=>VA(a,Db,c,t().d,e,g,h)))));var Cc=G(new H,Gc,Wb);break a}}if(vc){var Fc=Vb.k;if(Fc instanceof Mu){t();var qd=new L(Fc),Yb=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,qd,Yb);break a}}if(vc){var Nc=Vb.k;if(Nc instanceof cv){var ad=Nc.Nb,Uc= Nc.ac;t();var cd=new y(Pa=>!Pa),kc=VA(a,ad,c.b()?R():new L(cd.n(c.o())),t().d,e,g,h),Vc=new U(()=>zy(ju(),qB(b),new U(()=>b.fb))),Hc=new cv(a,kc,VA(a,Uc,c,d.b()?Es(Vc):d,e,g,h),Nc.Mj),rc=new L(Hc),sd=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,rc,sd);break a}}if(vc){var Kc=Vb.k;if(Kc instanceof Jv){t();var Qd=rB(Kc,c,new fn((Pa,Db)=>VA(a,Db,Pa,t().d,e,g,h))),Ad=new L(Qd),kd=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,Ad,kd);break a}}if(vc){var Hd=Vb.k;if(Hd instanceof Sv){var Rd=Hd.Fd;t();var Bd=new Sv(a,cB(Rd,new y(Pa=> {var Db=new y(Oc=>!Oc);return VA(a,Pa,c.b()?R():new L(Db.n(c.o())),t().d,e,g,h)}),new y(Pa=>VA(a,Pa,c,t().d,e,g,h))),Hd.Fx),ae=new L(Bd),dd=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,ae,dd);break a}}if(vc){var od=Vb.k;if(od instanceof Wv){t();var Ta=sB(od,new y(Pa=>VA(a,Pa,c,t().d,e,g,h)),new y(Pa=>{var Db=new y(Oc=>!Oc);return VA(a,Pa,c.b()?R():new L(Db.n(c.o())),t().d,e,g,h)}),new y(Pa=>VA(a,Pa,c,t().d,e,g,h)),od.Uu),wb=new L(Ta),$a=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,wb,$a);break a}}if(vc){var wa=Vb.k;if(wa instanceof Tv){var hb=wa.Ic,ra=wa.kf;if(hb instanceof LA&&null!==ra&&ra.b()){t();var wc=new Tv(a,tB(hb,new y(Pa=>VA(a,Pa,c,t().d,e,g,h))),ra,wa.vp),ac=new L(wc),Id=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,ac,Id);break a}}}if(vc){var ud=Vb.k;if(ud instanceof Tv){var be=ud.Ic,re=ud.kf;t();var pe=new Tv(a,VA(a,be,c,t().d,e,g,h),re,ud.vp),bd=new L(pe),Rc=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,bd,Rc);break a}}if(t().d===v){var Wc=t().d,Wd=WA(a,Ib,A,P,c,e,g,h);Cc=G(new H,Wc,Wd)}else throw new w(v);}if(null!==Cc)var zd=G(new H,Cc.h(), Cc.j());else throw new w(Cc);return(new Ku(a,zd.h(),x,Lu(new Qv(A.q,zd.j(),A.Nj)),I)).zf(!0)}if(Vu(a)===r)return a.La;throw new w(r);}),new y(r=>{if(lw(a)===r)return a.ib;if(r instanceof iw){var v=r.me,x=cB(r.Ne,new y(D=>{var F=new y(I=>!I);return VA(a,D,c.b()?R():new L(F.n(c.o())),t().d,e,g,h)}),new y(D=>VA(a,D,c,t().d,e,g,h)));v=G(new H,v,x);x=O().c;return new Qv(a,new z(v,x),V(a))}if(r instanceof jw){v=r.Xb;x=r.mb;r=r.Dc;var A=!1,B=null;a:{if(x instanceof L){A=!0;B=x;var C=B.k;if(C instanceof Ud&& (C=C.fa,null!==C)){x=(new iw(a,C.me,cB(C.Ne,new y(D=>{var F=new y(I=>!I);return VA(a,D,c.b()?R():new L(F.n(c.o())),t().d,e,g,h)}),new y(D=>VA(a,D,c,t().d,e,g,h))))).zf(!0);break a}}if(A&&(A=B.k,A instanceof fe)){x=VA(a,A.aa,c,t().d,e,g,h);break a}if(t().d===x)x=a.ib;else throw new w(x);}r=new Ou(r);r=new Ef(r,new y(D=>VA(a,D,c,t().d,e,g,h)));r=mB(r,a.ib,new fn((D,F)=>{var I=V(D.q);return dv(D,F,I,!1)}));A=Su();B=op().ga;v=qw(v,new Uu(A,B)).De(x,new fn((D,F)=>{var I=V(D.q);return dv(D,F,I,!1)}));x= V(r.q);return dv(r,v,x,!1)}throw new w(r);}))}));k=YA(k,a.ib,new fn((n,r)=>{var v=V(n.q);return dv(n,r,v,!1)}));k=(new y(n=>{a:{var r=uB(n,!0);n=O().c;for(r=er(r).m();r.s();){var v=r.t();b:{for(var x=n;!x.b();){var A=x.e(),B=Xu().X();if(Zu(v,A,e,!0,B)){x=!0;break b}x=x.f()}x=!1}x||(n=new z(v,n))}r=O().c;if(null===r?null===n:r.i(n))n=a.ib;else{if(n instanceof z&&(r=n.z,v=n.p,x=O().c,null===x?null===v:x.i(v))){n=r;break a}if(n===u())n=u();else{r=n.e();v=r=new z(uB(r,!1),u());for(n=n.f();n!==u();)x= n.e(),x=new z(uB(x,!1),u()),v=v.p=x,n=n.f();n=r}n=vB(a,n)}}return n})).n(k);m=V(k.q);k=dv(k,l,m,!1);l=Qt(b.Qf,new y(n=>{if(null!==n){var r=n.j();return G(new H,VA(a,n.h(),(t(),new L(!0)),t().d,e,g,h),VA(a,r,(t(),new L(!1)),t().d,e,g,h))}throw new w(n);}));k=wx(vx(a),l,k);return d instanceof L&&(l=d.k|0,a.Wq)?(l=new Iw(g.S,g.Ec,g.hc,g.Ed,1+l|0,g.Pc,g.Zc,g.Lb,g.yc,g.tb,g.$a,g.od,g.cb),k=new Qx(a,b.fb,k),TA(k,l)):yx(zx(a),b.fb,k)} function Wba(a,b,c,d,e,g,h){d=a.qa;if(a.F){var k=ut(Q(),"| ",a.r)+("normLike["+dA(c)+"] ")+b;ff(gf(),k+"\n")}a.r=1+a.r|0;try{a:if(b instanceof zA)var l=VA(a,b,c,t().d,e,g,h);else{if(b instanceof BA){CA(a);t();var m=new L(b);if(!m.b()){l=wB(m.k,c,new fn((n,r)=>VA(a,r,n,t().d,e,g,h)),e);break a}}throw new w(b);}}finally{a.r=-1+a.r|0}dx(new E(d),a.qa)&&a.F&&(b=""+ut(Q(),"| ",a.r)+d.n(l),ff(gf(),b+"\n"));return l} function VA(a,b,c,d,e,g,h){var k=new y(P=>"~\x3e "+P);if(a.F){var l=ut(Q(),"| ",a.r)+("norm["+dA(c)+"] ")+b;ff(gf(),l+"\n")}a.r=1+a.r|0;try{if(c instanceof L){var m=!!c.k,n=a.Df,r=O().c;xB(a);var v=ap(),x=XA(a,yB(xB(a),n,r,b,m,e,!0,!1,v),c,d,e,g,h)}else if(t().d===c){var A=a.Df,B=O().c;xB(a);var C=ap(),D=yB(xB(a),A,B,b,!1,e,!0,!1,C),F=a.Df,I=O().c;xB(a);var M=ap(),N=yB(xB(a),F,I,b,!0,e,!0,!1,M);x=dw(cw(a),XA(a,D,(t(),new L(!1)),d,e,g,h),XA(a,N,(t(),new L(!0)),d,e,g,h),ew(cw(a)),e)}else throw new w(c); }finally{a.r=-1+a.r|0}dx(new E(k),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+k.n(x),ff(gf(),a+"\n"));return x} function aB(a,b,c,d,e){var g=new zB(c);if(!g.en.L(b))if(g.en.$(b),g=b.Sb,g instanceof L)g=g.k,t(),a=VA(a,g,t().d,t().d,d,e,c),wy(b,new L(a));else if(t().d===g){g=vy(b);if(g.b())g=R();else{g=g.m();if(!g.s())throw nv("empty.reduceLeft");for(var h=!0,k=null;g.s();){var l=g.t();if(h)k=l,h=!1;else{var m=V(k.q);k=dv(k,l,m,!1)}}g=new L(k)}g.b()?(t(),g=O().c):(g=g.o(),g=VA(a,g,(t(),new L(!0)),t().d,d,e,c),h=O().c,g=new z(g,h));IA(b,g);g=rA(b);if(g.b())g=R();else{g=g.m();if(!g.s())throw nv("empty.reduceLeft"); h=!0;for(k=null;g.s();)l=g.t(),h?(k=l,h=!1):(m=V(k.q),k=Pu(k,l,m,!1));g=new L(k)}g.b()?(t(),a=O().c):(g=g.o(),a=VA(a,g,(t(),new L(!1)),t().d,d,e,c),c=O().c,a=new z(a,c));KA(b,a)}else throw new w(g);}function Yba(a,b,c,d,e,g){if(null===b)throw le();return b.sb?b.vb:me(b,new AB(a,c,d,e,g))}function nA(a,b,c,d,e,g,h,k){if(d.sb)a=d.vb;else{if(null===d)throw le();a=d.sb?d.vb:me(d,new BB(a,e,g,h,k,d))}mA(a,c,b.rT())} var hca=function CB(a,b,c,d,e,g,h,k,l,m){var r=a.qa;if(a.F){var v=ut(Q(),"| ",a.r)+("go "+b+" ("+ze(c,"",", ",""))+")";ff(gf(),v+"\n")}a.r=1+a.r|0;try{var x=DB(b);a:if(x instanceof lx){if(EB(c,x)){var A=x.Sb;if(A instanceof L)CB(a,A.k,c,d,e,g,h,k,l,m);else if(t().d===A){var B=d.Ig(x.Xa);if(B instanceof L)for(var C=B.k?vy(x):rA(x);!C.b();){var D=C.e();CB(a,D,c,d,e,g,h,k,l,m);C=C.f()}else if(t().d===B){var F=a.qa;if(a.F){var I=ut(Q(),"| ",a.r)+"Analyzing invar-occ of "+x;ff(gf(),I+"\n")}a.r=1+a.r| 0;try{nA(a,x,d,g,h,k,l,m);var M=void 0}finally{a.r=-1+a.r|0}if(dx(new E(F),a.qa)&&a.F){var N=""+ut(Q(),"| ",a.r)+F.n(M);ff(gf(),N+"\n")}}else throw new w(B);}else throw new w(A);}}else{if(x instanceof LA){var P=x.ic,T=x.jc;if(Pe(new E(x.tc),e)){CB(a,P,c,d,e,g,h,k,l,m);CB(a,T,c,d,e,g,h,k,l,m);break a}}FB(c,b)}var Y=void 0}finally{a.r=-1+a.r|0}dx(new E(r),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+r.n(Y),ff(gf(),a+"\n"))}; function GB(a,b,c,d,e,g){Pe(new E(b),c)?c=d:(jA(),c=jA().X().$(e));a.F&&(a=ut(Q(),"| ",a.r)+("\x3e\x3e\x3e\x3e occs["+HB(b)+e+"] :\x3d "+c+" \x3c~ ")+g.U(G(new H,b,e)),ff(gf(),a+"\n"));a=g.U(G(new H,b,e));if(a instanceof L){if(b=a.k,e=c,!b.b()){IB();if(0<=b.Q())g=b.Q(),g=new zc(g),b.Gc(g,0,2147483647);else{g=[];for(c=b.m();c.s();)a=c.t(),g.push(null===a?null:a);g=new zc(g)}c=g.a.length;for(a=0;a{var e;if(e=!b.L(d)){a:{e=d.Sb;if(e.b())e=vy(d);else{e=e.o();var g=O().c;e=new z(e,g)}for(g=d.q.ib;!e.b();){var h=e.e(),k=V(g.q);g=dv(g,h,k,!1);e=e.f()}e=g;g=gA(d.q).Pj;e=hA(e,g,c).U(d);if(e instanceof L&&(e=e.k,e=t().d===e?!0:e instanceof L&&!0===!!e.k?!0:!1,e)){e=!0;break a}e=!1}if(!e)a:{e=d.Sb;e.b()?e=rA(d):(e=e.o(),g=O().c,e=new z(e,g));for(g=d.q.La;!e.b();)h=e.e(),k=V(g.q),g=Pu(g,h,k,!1),e=e.f();e=g;g=gA(d.q).Jx;d=hA(e,g,c).U(d);if(d instanceof L&& (d=d.k,d=t().d===d?!0:d instanceof L&&!1===!!d.k?!0:!1,d)){e=!0;break a}e=!1}}return e}),!1);return Aq(Bq(),a)} function uA(a,b,c,d,e){var g=c.U(G(new H,b,d)).m();for(g=new xo(g,new y(Fa=>new qA(Fa)));g.s();){var h=g.t();if(h instanceof lx){var k=h;if((Ot(new E(k),d)?0:k.Sb.b())&&!e.L(k)&&(!d.kg.b()||k.kg.b()||e.L(d))&&Pe(new E(d.Xa),k.Xa)){h=a.qa;var l=a;if(l.F){var m=ut(Q(),"| ",l.r),n=dA((t(),new L(b))),r=iu(ju(),c.U(G(new H,b,k)));m=m+("[w] "+k+" "+n)+ze(r,"","","");ff(gf(),m+"\n")}l.r=1+l.r|0;try{var v=b?rA(d):vy(d),x=b?rA(k):vy(k),A=Aq(Bq(),v);if(Nm(new E(A),Aq(Bq(),x))){if(m=a,m.F){var B=ut(Q(),"| ", m.r)+(d+" and "+k)+" have non-equal other bounds and won't be merged";ff(gf(),B+"\n")}}else{var C=c.U(G(new H,b,k));if(C.b()||C.o().L(d)){m=a;if(m.F){var D=ut(Q(),"| ",m.r)+(" [U] "+k+" :\x3d ")+d;ff(gf(),D+"\n")}t();var F=G(new H,k,new L(d));e.$(F);IA(d,dl(vy(d),vy(k)));KA(d,dl(rA(d),rA(k)));var I=O().c;IA(k,new z(d,I));var M=O().c;KA(k,new z(d,M));var N=c.U(G(new H,!b,k));if(!N.b()){var P=N.o(),T=c.U(G(new H,!b,d));if(T instanceof L){var Y=T.k;if(!Y.b()){IB();if(0<=Y.ka()){var Z=Y.ka(),S=new zc(Z); NB(Y,S,0,2147483647);var ea=S}else{k=null;k=[];for(var ia=new qA(Y);ia.s();){var X=ia.t();k.push(null===X?null:X)}ea=new zc(k)}var sa=ea.a.length;for(k=0;kQB(a,A,x,d,t().d,d,g,h,e,k,!0,l,m,n,r)),e)}throw new w(b);} function SB(a,b,c,d,e,g,h,k,l,m,n,r,v){if(null!==b){var x=b.Oa,A=b.ra;if(x instanceof L&&Pe(new E(x.k),A))return c=QB(a,A,new TB(c),d,t().d,d,e,g,h,k,l,m,n,r,v),new Uw(a,(t(),new L(c)),c,b.Pd)}x=b.Va;A=b.Oa;A.b()?A=R():(A=A.o(),A=new L(QB(a,A,new UB(c),d,t().d,d,e,g,h,k,l,m,n,r,v)));return new Uw(x,A,QB(a,b.ra,c,d,t().d,d,e,g,h,k,l,m,n,r,v),b.Pd)} function VB(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B){var C=a.qa;a.F&&(c=ut(Q(),"| ",a.r)+("Setting [\u00b1] bounds of "+b+"... (failing "+dA(c)+", inlineBounds "+d+", !occursInvariantly "+!e.L(g)+", !recVars.contains(tv) "+!h.rc.L(g))+")",ff(gf(),c+"\n"));a.r=1+a.r|0;try{var D=g.Sb;if(D instanceof L){var F=D.k;t();var I=QB(a,F,new TB(k),l,m,l,r,v,x,A,d,e,h,n,B);wy(b,new L(I))}else if(t().d===D){if(n.L(G(new H,!0,g))){var M=vy(g);m=Qa=>{var Ma=new RB(k,g.Xa,!0);tp();return QB(a,Qa,Ma,gq(g),t().d,l,r,v,x,A, d,e,h,n,B)};if(M===u())var N=u();else{var P=M.e(),T=new z(m(P),u());P=T;for(var Y=M.f();Y!==u();){var Z=Y.e(),S=new z(m(Z),u());P=P.p=S;Y=Y.f()}N=T}IA(b,N)}if(n.L(G(new H,!1,g))){var ea=rA(g);M=Qa=>{var Ma=new RB(k,g.Xa,!1);tp();return QB(a,Qa,Ma,gq(g),t().d,l,r,v,x,A,d,e,h,n,B)};if(ea===u())var ia=u();else{var X=ea.e(),sa=new z(M(X),u());X=sa;for(var Ja=ea.f();Ja!==u();){var Xa=Ja.e(),Fa=new z(M(Xa),u());X=X.p=Fa;Ja=Ja.f()}ia=sa}KA(b,ia)}}else throw new w(D);var za=b}finally{a.r=-1+a.r|0}dx(new E(C), a.qa)&&a.F&&(b=""+ut(Q(),"| ",a.r)+C.n(za),ff(gf(),b+"\n"));return za} var QB=function WB(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A){var C=new y(kc=>"~\x3e "+kc);if(a.F){var D=ut(Q(),"| ",a.r)+("transform["+dA(c.ol)+"] "+b+" ("+ze(d,"",", ","")+") "+c+" ")+e;ff(gf(),D+"\n")}a.r=1+a.r|0;try{var F=!1,I=null,M=!1,N=null;a:if(b instanceof Qv){var P=b.Ba;var T=new Qv(a,ry(lv(),P,new y(kc=>SB(a,kc,c,g,h,k,l,m,n,r,v,x,A))),b.ma())}else if(b instanceof zv){var Y=b.Yb;T=new zv(a,ry(lv(),Y,new y(kc=>SB(a,kc,c,g,h,k,l,m,n,r,v,x,A))),b.ma())}else if(b instanceof Sv)T=new Sv(a,SB(a,b.Fd, c,g,h,k,l,m,n,r,v,x,A),b.ma());else if(b instanceof Wv){var Z=b.fo,S=kc=>{if(kc instanceof fe)return kc=kc.aa,t(),kc=WB(a,kc,c,g,t().d,g,h,k,l,m,n,r,v,x,A),new fe(kc);if(kc instanceof Ud)return kc=kc.fa,t(),kc=SB(a,kc,c,g,h,k,l,m,n,r,v,x,A),new Ud(kc);throw new w(kc);};if(Z===u())var ea=u();else{for(var ia=Z.e(),X=new z(S(ia),u()),sa=X,Ja=Z.f();Ja!==u();){var Xa=Ja.e(),Fa=new z(S(Xa),u());sa=sa.p=Fa;Ja=Ja.f()}ea=X}T=new Wv(a,ea,b.ma())}else if(b instanceof cv){var za=b.ac;T=new cv(a,WB(a,b.Nb,new UB(c), g,t().d,g,h,k,l,m,n,r,v,x,A),WB(a,za,c,g,e,g,h,k,l,m,n,r,v,x,A),b.ma())}else if(b instanceof Jv)T=jca(b,c,new fn((kc,Vc)=>WB(a,Vc,kc,d,e,g,h,k,l,m,n,r,v,x,A)));else if(b instanceof mx)T=WB(a,b.hi,c,d,t().d,g,h,k,l,m,n,r,v,x,A);else if(XB(b)||b instanceof YB||b instanceof FA)T=b;else{if(b instanceof lx&&(F=!0,I=b,d.L(I))){var Qa=!1,Ma=null,Ga=c.Ig(I.Xa);if(Ga instanceof L&&(Qa=!0,Ma=Ga,!0===!!Ma.k)){T=a.ib;break a}if(Qa&&!1===!!Ma.k){T=a.La;break a}if(t().d===Ga){T=WB(a,I,c,d.Ek(I),t().d,g,h,k,l,m, n,r,v,x,A);break a}throw new w(Ga);}if(F){var ab=!1,Hb=null,bc=h.U(I);if(bc instanceof L){ab=!0;Hb=bc;var yb=Hb.k;if(yb instanceof L){var tb=yb.k;if(a.F){var eb=ut(Q(),"| ",a.r)+"-\x3e "+tb;ff(gf(),eb+"\n")}T=WB(a,tb,c,d.bc(I),e,g,h,k,l,m,n,r,v,x,A);break a}}if(ab){var kb=Hb.k;if(t().d===kb){var Rb=I;if(a.F){var Gb=ut(Q(),"| ",a.r)+"-\x3e bound "+c.Ig(Rb.Xa);ff(gf(),Gb+"\n")}var vb=c.Ig(I.Xa);if(!k&&(vy(I).b()&&vb.L(!0)||rA(I).b()&&vb.L(!1)||vy(I).b()&&rA(I).b())){var Tb=new vl("?"),Nb=ap();T=new Mu(a, Tb,Nb,V(a))}else{var ic=I;vb.b()&&xm("Should not be replacing an invariant type variable by its bound...");var Va=!!vb.o();T=OB(a,Va,c,ic,d.bc(ic),e,g,h,k,l,m,n,r,v,x,A)}break a}}if(t().d===bc){var cb=new GA(!0),zb=m.Hk(I,new U((kc=>()=>{cb.Am=!1;var Vc=V(a);t();var Hc=new L(kc),rc=kc.kg,sd=O().c,Kc=O().c;Vc=new lx(a,kc.Xa,sd,Kc,Hc,rc,!1,Vc);a.F&&(Hc=ut(Q(),"| ",a.r)+("Renewed "+kc+" ~\x3e ")+Vc,ff(gf(),Hc+"\n"));return Vc})(I))),Ub=c.Ig(I.Xa);if(Ub instanceof L){var jb=!!Ub.k;if(n&&!r.L(I)&&!v.rc.L(I)){var db= I;if(a.F){var ub=ut(Q(),"| ",a.r)+("Inlining ["+HB(jb)+"] bounds of "+db+" (~\x3e "+zb)+")";ff(gf(),ub+"\n")}if(jb){var Aa=OB(a,!0,c,I,d.bc(I),e,g,h,k,l,m,n,r,v,x,A),va=V(Aa.q);T=dv(Aa,zb,va,!1)}else{var Ra=OB(a,!1,c,I,d.bc(I),e,g,h,k,l,m,n,r,v,x,A),rb=V(Ra.q);T=Pu(Ra,zb,rb,!1)}break a}}if(!cb.Am){if(Ub instanceof L){var xb=!!Ub.k;if(A.U(G(new H,!xb,I)).b()&&I.Sb.b()){var mc=xb?vy(I):rA(I);b:{for(var Ha=mc;!Ha.b();){var Ka=Ha.e();if(Ka instanceof lx)var Oa=h.U(Ka),Na=Oa.b()?!0:!Oa.o().b();else Na= !1;if(!Na){var Da=!1;break b}Ha=Ha.f()}Da=!0}if(Da){var ta=I;if(a.F){var Ya=ut(Q(),"| ",a.r)+("NEW SUBS "+ta)+" -\x3e N";ff(gf(),Ya+"\n")}var dc=I,ka=t().d,ya=G(new H,dc,ka);h.$(ya);var Sa=PB(a,xb,mc);T=WB(a,Sa,c,d.bc(I),e,g,h,k,l,m,n,r,v,x,A)}else T=VB(a,zb,Ub,n,r,I,v,c,g,e,x,h,k,l,m,A);break a}}T=VB(a,zb,Ub,n,r,I,v,c,g,e,x,h,k,l,m,A);break a}T=zb;break a}throw new w(bc);}if(b instanceof LA){M=!0;N=b;var xc=N.ic,Sb=N.jc;if(!0===N.tc){var uc=WB(a,xc,c,d,e,g,h,k,l,m,n,r,v,x,A),Lb=WB(a,Sb,c,d,e,g,h, k,l,m,n,r,v,x,A),lc=V(uc.q);T=dv(uc,Lb,lc,!1);break a}}if(M){var Xb=N.ic,ec=N.jc;if(!1===N.tc){var Ab=WB(a,Xb,c,d,e,g,h,k,l,m,n,r,v,x,A),Ob=WB(a,ec,c,d,e,g,h,k,l,m,n,r,v,x,A),fb=V(Ab.q);T=Pu(Ab,Ob,fb,!1);break a}}if(b instanceof MA){var Wa=WB(a,b.Fc,new UB(c),g,t().d,g,h,k,l,m,n,r,v,x,A),bb=V(Wa.q);T=NA(Wa,bb,!1)}else{if(b instanceof $y){var Ia=b.Oq,Ua=b.up;if(null!==Ua){var pc=Ua.Ba,sc=WB(a,Ia,c,g,e,g,h,k,l,m,n,r,v,x,A),Ba=ry(lv(),pc,new y(kc=>{var Vc=kc.Va,Hc=kc.Oa;Hc.b()?Hc=R():(Hc=Hc.o(),Hc=new L(WB(a, Hc,new UB(c),g,t().d,g,h,k,l,m,n,r,v,x,A)));return new Uw(Vc,Hc,WB(a,kc.ra,c,g,t().d,g,h,k,l,m,n,r,v,x,A),kc.Pd)})),ob=new Qv(a,Ba,V(a));T=new $y(a,sc,ob,V(a));break a}}if(b instanceof ZB){$B(a);t();var nc=b.mc(),Ib=new L(nc);if(!Ib.b()){T=WB(a,Ib.k,c,d,e,g,h,k,l,m,n,r,v,x,A);break a}}if(b instanceof fw)T=new fw(a,b.qb,aC(b,c,new fn((kc,Vc)=>WB(a,Vc,kc,g,t().d,g,h,k,l,m,n,r,v,x,A)),l),b.Xl);else if(b instanceof Tv){var vc=b.Ic,Vb=b.kf;if(Vb.b())T=WB(a,vc,c,g,e,g,h,k,l,m,n,r,v,x,A);else if(Pe(new E(c.ol), (t(),new L(!0)))){var fc=WB(a,vc,c,g,e,g,h,k,l,m,n,r,v,x,A);T=bC(fc,Vb)}else{var Bc=WB(a,vc,c,g,e,g,h,k,l,m,n,r,v,x,A);T=iB(Bc,Vb)}}else if(b instanceof cC){var Pb=b.Fg,Jb=b.Sf,gc=c.ol;if(gc.b()){var Cb=cw(a),cc=WB(a,Pb,gA(a).Xu,d,e,g,h,k,l,m,n,r,v,x,A),yc=WB(a,Jb,gA(a).Pj,d,e,g,h,k,l,m,n,r,v,x,A);T=tA(Cb,cc,yc,V(a))}else T=gc.o()?WB(a,Jb,c,d,t().d,g,h,k,l,m,n,r,v,x,A):WB(a,Pb,c,d,t().d,g,h,k,l,m,n,r,v,x,A)}else if(b instanceof Qx){var Mc=b.de,qc=WB(a,b.Re,new dC(c,Mc),d,(t(),new L(Mc)),g,h,k,l,m, n,r,v,x,A);if(e instanceof L){var oc=e.k|0;if(a.Wq){var Qc=new Iw(l.S,l.Ec,l.hc,l.Ed,1+oc|0,l.Pc,l.Zc,l.Lb,l.yc,l.tb,l.$a,l.od,l.cb),jc=new Qx(a,Mc,qc);T=TA(jc,Qc);break a}}T=yx(zx(a),Mc,qc)}else if(b instanceof eC){var sb=b.Lj,Gc=b.kj,Wb=kc=>{if(null!==kc){var Vc=kc.j();return G(new H,WB(a,kc.h(),gA(a).Pj,g,t().d,g,h,k,l,m,n,r,v,x,A),WB(a,Vc,gA(a).Jx,g,t().d,g,h,k,l,m,n,r,v,x,A))}throw new w(kc);};if(sb===u())var Cc=u();else{for(var Fc=sb.e(),qd=new z(Wb(Fc),u()),Yb=qd,Nc=sb.f();Nc!==u();){var ad= Nc.e(),Uc=new z(Wb(ad),u());Yb=Yb.p=Uc;Nc=Nc.f()}Cc=qd}T=new eC(a,Cc,WB(a,Gc,c,d,t().d,g,h,k,l,m,n,r,v,x,A))}else throw new w(b);}}}finally{a.r=-1+a.r|0}if(dx(new E(C),a.qa)&&a.F){var cd=""+ut(Q(),"| ",a.r)+C.n(T);ff(gf(),cd+"\n")}return T};function fC(a,b,c,d,e,g,h,k){if(null===b)throw le();return b.sb?b.vb:me(b,UA(c,d,!0,new fn((l,m)=>gC(a,l,m,e,h,g,k)),g))} var gC=function hC(a,b,c,d,e,g,h){for(;;){var l=!1,m=null,n=uy(c);if(n instanceof lx&&(l=!0,m=n,iC(a),n=m.Sb,!n.b()))return b=n.o(),d=new zB(e),c=m,l=a,a=m,d.en.L(c)||(d.en.$(c),t(),e=hC(l,t().d,b,(t(),new L(a)),e,g,h),wy(a,new L(e))),m;if(l){d=new zB(e);c=m;b=a;a=m;if(!d.en.L(c)){d.en.$(c);n=vy(a);d=((v,x,A,B,C)=>D=>hC(v,(t(),new L(!0)),D,(t(),new L(x)),A,B,C))(b,a,e,g,h);if(n===u())d=u();else{c=n.e();l=c=new z(d(c),u());for(n=n.f();n!==u();){var r=n.e();r=new z(d(r),u());l=l.p=r;n=n.f()}d=c}IA(a, d);d=rA(a);e=((v,x,A,B,C)=>D=>hC(v,(t(),new L(!1)),D,(t(),new L(x)),A,B,C))(b,a,e,g,h);if(d===u())e=u();else{g=d.e();h=g=new z(e(g),u());for(b=d.f();b!==u();)d=b.e(),d=new z(e(d),u()),h=h.p=d,b=b.f();e=g}KA(a,e)}return m}m=new $e;if(b instanceof L){n=!!b.k;l=h.U(G(new H,n,c));if(l instanceof L&&(l=l.k,d.b()?r=!0:(r=d.o(),r=dx(new E(r),l)),r)){m=a;m.F&&(m=ut(Q(),"| ",m.r)+("!unskid-1! "+c+" -\x3e ")+l,ff(gf(),m+"\n"));c=l;continue}l=m.sb?m.vb:fC(a,m,c,b,d,g,e,h);l=h.U(G(new H,n,l));if(l instanceof L&&(l=l.k,d.b()?n=!0:(n=d.o(),n=dx(new E(n),l)),n)){r=n=a;n.F&&(m=ut(Q(),"| ",n.r)+("!unskid-2! "+(m.sb?m.vb:fC(r,m,c,b,d,g,e,h))+" -\x3e ")+l,ff(gf(),m+"\n"));c=l;continue}return m.sb?m.vb:fC(a,m,c,b,d,g,e,h)}if(t().d===b)return m.sb?m.vb:fC(a,m,c,b,d,g,e,h);throw new w(b);}};function $ba(a,b,c,d,e,g){if(c instanceof zA)return gC(a,b,c,t().d,e,d,g);if(c instanceof BA){CA(a);t();var h=new L(c);if(!h.b())return wB(h.k,b,new fn((k,l)=>gC(a,k,l,t().d,e,d,g)),d)}throw new w(c);} function jC(a,b,c){a.F&&(a=ut(Q(),"| ",a.r)+("Nope("+ag(ca(b))+"): "+b+" ~ ")+c,ff(gf(),a+"\n"));return!1}function kC(a,b,c,d,e,g,h){if(null!==b){var k=b.Oa,l=b.ra;if(k instanceof L&&(k=k.k,null!==c)){var m=c.Oa,n=c.ra;if(m instanceof L)return yA(a,k,m.k,d,e)&&yA(a,l,n,d,e)}}return null!==b&&(l=b.Oa,b=b.ra,t().d===l&&null!==c&&(l=c.Oa,c=c.ra,t().d===l))?yA(a,b,c,d,e):jC(a,g,h)} var yA=function lC(a,b,c,d,e){for(;;){var h=b,k=c;if((null===d?null===h:mC(d,h))&&(null===e?null===k:mC(e,k))||(null===e?null===h:mC(e,h))&&(null===d?null===k:mC(d,k)))return!0;if(h instanceof lx){var l=h;if(k instanceof lx)return Ot(new E(l),k)||jC(a,b,c)}if(h instanceof MA){var m=h.Fc;if(k instanceof MA){var n=k.Fc;b=m;c=n;continue}}if(h instanceof Mu){var r=h.pd;if(k instanceof Mu)return Pe(new E(r),k.pd)||jC(a,b,c)}if(h instanceof Sv){var v=h.Fd;if(k instanceof Sv)return kC(a,v,k.Fd,d,e,b,c)}if(h instanceof zv){var x=h.Yb;if(k instanceof zv){var A=k.Yb,B=x.K();if(Pe(new E(B),A.K())||jC(a,b,c)){if(x===u())var C=u();else{for(var D=x.e(),F=new z(D.j(),u()),I=F,M=x.f();M!==u();){var N=M.e(),P=new z(N.j(),u());I=I.p=P;M=M.f()}C=F}if(A===u())var T=u();else{for(var Y=A.e(),Z=new z(Y.j(),u()),S=Z,ea=A.f();ea!==u();){var ia=ea.e(),X=new z(ia.j(),u());S=S.p=X;ea=ea.f()}T=Z}for(var sa=new Wq(C,C,T),Ja=a,Xa=b,Fa=c,za=sa.ck.m(),Qa=sa.dk.m(),Ma=!1;!Ma&&za.s()&&Qa.s();){var Ga=za.t(),ab=Qa.t();Ma=!kC(Ja,Ga,ab,d,e, Xa,Fa)}return!Ma}return!1}}if(h instanceof cv){var Hb=h,bc=Hb.Nb,yb=Hb.ac;if(k instanceof cv){var tb=k,eb=tb.ac;if(lC(a,bc,tb.Nb,d,e)){b=yb;c=eb;continue}else return!1}}if(h instanceof Tv){var kb=h,Rb=kb.Ic,Gb=kb.kf;if(k instanceof Tv){var vb=k,Tb=vb.kf;return lC(a,Rb,vb.Ic,d,e)&&(Pe(new E(Gb),Tb)||jC(a,b,c))}}if(h instanceof nC){var Nb=h.rp;if(k instanceof nC)return Pe(new E(Nb),k.rp)||jC(a,b,c)}if(h instanceof mx){var ic=h.hi;if(k instanceof mx)return Pe(new E(ic),k.hi)||jC(a,b,c)}if(h instanceof FA){var Va=h.Eh;if(k instanceof FA)return Pe(new E(Va),k.Eh)||jC(a,b,c)}if(h instanceof cC){var cb=h,zb=cb.Fg,Ub=cb.Sf;if(k instanceof cC){var jb=k,db=jb.Sf;if(lC(a,zb,jb.Fg,d,e)){b=Ub;c=db;continue}else return!1}}if(h instanceof LA){var ub=h,Aa=ub.tc,va=ub.ic,Ra=ub.jc;if(k instanceof LA){var rb=k,xb=rb.ic,mc=rb.jc;if((Pe(new E(Aa),rb.tc)||jC(a,b,c))&&lC(a,va,xb,d,e)){b=Ra;c=mc;continue}else return!1}}if(h instanceof Qv){var Ha=h.Ba;if(k instanceof Qv){var Ka=k.Ba,Oa=Ha.K();if(Pe(new E(Oa),Ka.K())){for(var Na= new Wq(Ha,Ha,Ka),Da=a,ta=b,Ya=c,dc=Na.ck.m(),ka=Na.dk.m(),ya=!1;!ya&&dc.s()&&ka.s();){var Sa=dc.t(),xc=ka.t(),Sb=Sa,uc=xc;ya=!((Pe(new E(Sb.h()),uc.h())||jC(Da,ta,Ya))&&kC(Da,Sb.j(),uc.j(),d,e,ta,Ya))}return!ya}return!1}}if(h instanceof $y){var Lb=h,lc=Lb.Oq,Xb=Lb.up;if(k instanceof $y){var ec=k,Ab=ec.up;if(lC(a,lc,ec.Oq,d,e)){b=Xb;c=Ab;continue}else return!1}}if(h instanceof ZB){var Ob=h;$B(a);t();var fb=Ob.mc(),Wa=new L(fb);if(!Wa.b()){b=Wa.k;continue}}if(k instanceof ZB){var bb=k;$B(a);t();var Ia= bb.mc(),Ua=new L(Ia);if(!Ua.b()){c=Ua.k;continue}}if(h instanceof fw){var pc=h,sc=pc.qb,Ba=pc.Zb;if(k instanceof fw){var ob=k,nc=ob.Zb;if(Pe(new E(sc),ob.qb)||jC(a,b,c)){for(var Ib=new Wq(Ba,Ba,nc),vc=a,Vb=Ib.ck.m(),fc=Ib.dk.m(),Bc=!1;!Bc&&Vb.s()&&fc.s();){var Pb=Vb.t(),Jb=fc.t();Bc=!lC(vc,Pb,Jb,d,e)}return!Bc}return!1}}return jC(a,b,c)}};function oC(){this.op=null}oC.prototype=new p;oC.prototype.constructor=oC;function pC(){}pC.prototype=oC.prototype; function saa(a,b,c,d,e){var g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Initial: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));b=eA(a.op,b,c,!0,!1,e);g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Cleaned up: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));c.b()||(g=!!c.o(),b=vA(a.op,b,g,e));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Unskid: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));b=kA(a.op, b,d,c,e);g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Type after simplification: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));b=iA(a.op,b,c,e);g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Normalized: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));b=eA(a.op,b,c,!1,!0,e);g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Cleaned up: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));c.b()||(g=!!c.o(),b= vA(a.op,b,g,e));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+"\u2b24 Unskid: "+b,ff(gf(),g+"\n"));g=a.Ui;g.F&&(g=ut(Q(),"| ",g.r)+" where: "+xx(b),ff(gf(),g+"\n"));b=kA(a.op,b,d,c,e);d=a.Ui;d.F&&(d=ut(Q(),"| ",d.r)+"\u2b24 Resim: "+b,ff(gf(),d+"\n"));d=a.Ui;d.F&&(d=ut(Q(),"| ",d.r)+" where: "+xx(b),ff(gf(),d+"\n"));b=aca(a.op,b,c,e);c=a.Ui;c.F&&(c=ut(Q(),"| ",c.r)+"\u2b24 Factored: "+b,ff(gf(),c+"\n"));a=a.Ui;a.F&&(a=ut(Q(),"| ",a.r)+" where: "+xx(b),ff(gf(),a+"\n"));return b} function qC(a,b){this.aO=null;this.bO=b;if(null===a)throw null;this.aO=a}qC.prototype=new p;qC.prototype.constructor=qC;function rC(a,b){var c=a.aO;a=a.bO;b=b.m();return new qC(c,a.bf(new Ef(b,new y(d=>{if(null!==d)return G(new H,d.hb,d.kc);throw new w(d);}))))}qC.prototype.$classData=q({QY:0},!1,"mlscript.Typer$ExpCtx$1",{QY:1,g:1});function sC(a){this.rI=null;if(null===a)throw null;this.rI=a}sC.prototype=new p;sC.prototype.constructor=sC; function tC(a,b,c,d){if(c.yc&&kca(b)){c=(new Dx(c.Ec)).wF;c.b()?c=t().d:(c=c.o(),c=uC(c,c,b.x));c=Wk(new vC(a)).Ob(c,new y(()=>{t();return R()}));var e=new wC(a,b,d);Wk(e).Ob(c,new y(()=>{t();return R()}));return xC(yC(a.rI),b,d)}return t().d}sC.prototype.$classData=q({SY:0},!1,"mlscript.Typer$ValidPatVar$",{SY:1,g:1});function zC(a){this.sI=null;if(null===a)throw null;this.sI=a}zC.prototype=new p;zC.prototype.constructor=zC; function xC(a,b,c){t();a.sI.uP.L(b.x)&&Lw(a.sI,We(Xe(),"Illegal use of reserved operator: "+b.x),b.A(),c);return new L(b.x)}zC.prototype.$classData=q({VY:0},!1,"mlscript.Typer$ValidVar$",{VY:1,g:1});function AC(){}AC.prototype=new p;AC.prototype.constructor=AC;AC.prototype.$classData=q({XY:0},!1,"mlscript.TyperDatatypes$AssignedVariable$",{XY:1,g:1});function BC(){}BC.prototype=new p;BC.prototype.constructor=BC;BC.prototype.$classData=q({eZ:0},!1,"mlscript.TyperDatatypes$DelayedTypeInfo$",{eZ:1,g:1}); function CC(){}CC.prototype=new p;CC.prototype.constructor=CC;CC.prototype.$classData=q({rZ:0},!1,"mlscript.TyperDatatypes$OtherTypeLike$",{rZ:1,g:1});function DC(){}DC.prototype=new p;DC.prototype.constructor=DC;DC.prototype.$classData=q({wZ:0},!1,"mlscript.TyperDatatypes$ProxyType$",{wZ:1,g:1});function EC(a){this.PO=null;if(null===a)throw null;this.PO=a}EC.prototype=new p;EC.prototype.constructor=EC;function lca(a,b,c,d){return a.PO.Wq?mca(b,b.Re,ap(),b.de,c,d):t().d} EC.prototype.$classData=q({BZ:0},!1,"mlscript.TyperDatatypes$SplittablePolyFun$",{BZ:1,g:1});function FC(a,b){if(null===b)throw null;a.J=b}function GC(){this.J=null}GC.prototype=new p;GC.prototype.constructor=GC;function HC(){}HC.prototype=GC.prototype;function IC(){}IC.prototype=new p;IC.prototype.constructor=IC;IC.prototype.$classData=q({LZ:0},!1,"mlscript.TyperDatatypes$TypeVariable$",{LZ:1,g:1}); var nca=function JC(a,b,c,d,e,g,h,k){for(;;){var m=c.U(b);if(m instanceof L){if(b=m.k,!d)return b}else{if(t().d===m){var n=!1;m=null;var r=b;if(r instanceof lx){n=!0;m=r;iC(a);var v=m.Sb;if(!v.b())return b=v.o(),e.Se(m,new U(((x,A,B,C,D,F,I,M,N)=>()=>{var P=A.ji;t();var T=new L(A),Y=A.kg,Z=O().c,S=O().c;P=new lx(x,A.Xa,Z,S,T,Y,!1,P);T=G(new H,A,P);B.$(T);t();T=JC(x,C,D,F,B,I,M,N);wy(P,new L(T));return P})(a,m,e,b,c,d,g,h,k)))}if(n&&vy(m).b()&&rA(m).b())return c=G(new H,m,m),e.$(c),m;if(n)return e.Se(m, new U(((x,A,B,C,D,F,I,M)=>()=>{var N=A.ji;t();var P=new L(A),T=A.kg,Y=O().c,Z=O().c;N=new lx(x,A.Xa,Y,Z,P,T,!1,N);P=G(new H,A,N);B.$(P);Z=vy(A);P=(ea=>ia=>JC(ea,ia,C,D,B,F,I,M))(x);if(Z===u())P=u();else{T=Z.e();Y=T=new z(P(T),u());for(Z=Z.f();Z!==u();){var S=Z.e();S=new z(P(S),u());Y=Y.p=S;Z=Z.f()}P=T}IA(N,P);Z=rA(A);P=(ea=>ia=>JC(ea,ia,C,D,B,F,I,M))(x);if(Z===u())P=u();else{T=Z.e();Y=T=new z(P(T),u());for(Z=Z.f();Z!==u();)S=Z.e(),S=new z(P(S),u()),Y=Y.p=S,Z=Z.f();P=T}KA(N,P);return N})(a,m,e,c,d, g,h,k)));if(r instanceof Qx&&(m=r,m.deM=>JC(x,M,A,B,C,D,F,I))(a,c,d,e,g,h,k)))}throw new w(m);}}};function LC(a,b){b=b.m();b=new Ef(b,new y(g=>{for(var h=a.La;!g.b();){var k=g.e(),l=V(h.q);h=Pu(h,k,l,!1);g=g.f()}return h}));for(var c=a.ib;b.s();){var d=b.t(),e=V(c.q);c=dv(c,d,e,!1)}return c}function oca(a){a.Ep=0;a.an=0;a.Gp=0;a.Fp=0;a.qa=new y(()=>"");a.r=0} function MC(){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0}MC.prototype=new p;MC.prototype.constructor=MC;function NC(){}NC.prototype=MC.prototype;function pca(a){null===a.jo&&null===a.jo&&(a.jo=new OC(a));return a.jo}function Iv(a){null===a.ho&&null===a.ho&&(a.ho=new PC(a));return a.ho}function gA(a){null===a.io&&null===a.io&&(a.io=new QC(a));return a.io} function Lx(a,b,c,d){Nx(a,b);a.r=1+a.r|0;try{var e=Es(c)}finally{a.r=-1+a.r|0}dx(new E(d),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+d.n(e),ff(gf(),a+"\n"));return e}function Nx(a,b){a.F&&(a=""+ut(Q(),"| ",a.r)+Es(b),ff(gf(),a+"\n"))}function HB(a){if(!0===a)return"+";if(!1===a)return"-";throw new w(a);}function dA(a){if(a instanceof L)return HB(!!a.k);if(t().d===a)return"\x3d";throw new w(a);} function Yv(a,b){var c=a.Q()+b.Q()|0;b=Lv(Lv(Mv(8cz(m,new y(n=>SC(a,n,c,!0,d)),d);if(g===u())e=u();else{var h=g.e(),k=h=new z(e(h),u());for(g=g.f();g!==u();){var l=g.e();l=new z(e(l),u());k=k.p=l;g=g.f()}e=h}b=b.Ul;b.b()?b=R():(b=b.o(),b=new L(SC(a,b,c,!0,d)));return new By(a,e,b)}}throw new w(b);} function SC(a,b,c,d,e){var g=Xu().X(),h=Xu().X(),k=c.ie();k=new Ef(k,new y(r=>r.Ea()));if(k.s()){if(!k.s())throw nv("empty.reduceLeft");for(var l=!0,m=null;k.s();){var n=k.t();l?(m=n,l=!1):(m|=0,n|=0,m=m>n?m:n)}k=new L(m)}else k=R();k=(k.b()?a.Gd:k.o())|0;return nca(a,b,c,d,g,k,e,h)} function Rv(a,b){tp();var c=a.K();At(0,Pe(new E(c),b.K()));a=new Wq(a,a,b);b=new fn((d,e)=>{d=G(new H,d,e);e=d.y;var g=d.w;if(null!==e){var h=e.h();e=e.j();if(h instanceof L&&(h=h.k,null!==g)){var k=g.h();g=g.j();if(k instanceof L&&Nm(new E(h),k.k))return d=t().d,G(new H,d,Nv(e,g,V(e.Va)))}}g=d.y;h=d.w;if(null!==g&&(e=g.h(),g=g.j(),null!==h))return d=h.h(),h=h.j(),d=e.b()?d:e,G(new H,d,Nv(g,h,V(g.Va)));throw new w(d);});Yq();return Ew(a,b)} function TC(a,b){tp();var c=a.K();At(0,Pe(new E(c),b.K()));a=new Wq(a,a,b);b=new fn((d,e)=>{d=G(new H,d,e);e=d.y;var g=d.w;if(null!==e){var h=e.h();e=e.j();if(h instanceof L&&(h=h.k,null!==g)){var k=g.h();g=g.j();if(k instanceof L)return d=Pe(new E(h),k.k)?new L(h):R(),G(new H,d,xw(e,g,V(e.Va)))}}e=d.y;g=d.w;if(null!==e&&(e=e.j(),null!==g))return d=g.j(),g=t().d,G(new H,g,xw(e,d,V(e.Va)));throw new w(d);});Yq();return Ew(a,b)} function vB(a,b){var c=tc();try{var d=new y(db=>"yes: "+db);if(a.F){var e=ut(Q(),"| ",a.r);if(b===u())var g=u();else{var h=b.e(),k=new z(ze(h,""," \x26 ",""),u());h=k;for(var l=b.f();l!==u();){var m=l.e(),n=new z(ze(m,""," \x26 ",""),u());h=h.p=n;l=l.f()}g=k}var r=e+"factorize? "+ze(g,""," | ","");ff(gf(),r+"\n")}a.r=1+a.r|0;try{if(0>=b.ab(1))throw Hq(new Iq,c,LC(a,b));var v=new lA;for(e=b;!e.b();){for(var x=e.e();!x.b();){var A=x.e();if(A instanceof lx){g=A;var B=v.U(g);if(B instanceof L)var C=B.k; else{if(R()!==B)throw new w(B);C=0}v.bh(g,1+(C|0)|0)}else if(A instanceof Gv){g=A;var D=v.U(g);if(D instanceof L)var F=D.k;else{if(R()!==D)throw new w(D);F=0}v.bh(g,1+(F|0)|0)}else if(A instanceof UC){g=A;var I=v.U(g);if(I instanceof L)var M=I.k;else{if(R()!==I)throw new w(I);M=0}v.bh(g,1+(M|0)|0)}else if(A instanceof VC){g=A;var N=v.U(g);if(N instanceof L)var P=N.k;else{if(R()!==N)throw new w(N);P=0}v.bh(g,1+(P|0)|0)}x=x.f()}e=e.f()}if(a.F){var T=ut(Q(),"| ",a.r)+"Factors "+ze(v,"",", ","");ff(gf(), T+"\n")}var Y=Fq();if(v.b())var Z=R();else{if(v.b())throw nv("empty.maxBy");A=A=x=x=null;B=!1;B=!0;for(var S=v.Gn;null!==S;){var ea=G(new H,S.ju,S.Fn),ia=ea.w|0;if(B||0{var ub=db;a:for(;;)if(ub.b()){db=u();break}else{var Aa=ub.e();db=ub.f();if(!0===!!Pe(new E(Aa),sa))ub=db;else for(;;){if(db.b())db=ub;else{Aa=db.e();if(!0!==!!Pe(new E(Aa),sa)){db=db.f();continue}Aa=db;db=new z(ub.e(),u());var va=ub.f();for(ub=db;va!==Aa;){var Ra=new z(va.e(),u());ub=ub.p=Ra;va=va.f()}for(va= Aa=Aa.f();!Aa.b();){Ra=Aa.e();if(!0===!!Pe(new E(Ra),sa)){for(;va!==Aa;)Ra=new z(va.e(),u()),ub=ub.p=Ra,va=va.f();va=Aa.f()}Aa=Aa.f()}va.b()||(ub.p=va)}break a}}return db};if(Ma===u())var bc=u();else{var yb=Ma.e(),tb=new z(Ja(yb),u());yb=tb;for(var eb=Ma.f();eb!==u();){var kb=eb.e(),Rb=new z(Ja(kb),u());yb=yb.p=Rb;eb=eb.f()}bc=tb}var Gb=vB(a,bc),vb=V(sa.q),Tb=Pu(sa,Gb,vb,!1);if(0{var g=e.Va,h=e.Oa;if(h.b())h=R();else{h=h.o();if(c.b())var k=R();else k=!!c.o(),k=new L(!k);h=new L(d.ba(k,h))}return new Uw(g,h,d.ba(c,e.ra),e.Pd)})),b.Nj)} function qca(a,b,c,d){var e=!1,g=null;if(b instanceof cv){g=b.Nb;e=b.ac;if(c.b())var h=R();else h=!!c.o(),h=new L(!h);return new cv(a,d.ba(h,g),d.ba(c,e),b.ma())}if(b instanceof Jv)return rB(b,c,d);if(b instanceof zv)return g=b.Yb,new zv(a,ry(lv(),g,new y(l=>{var m=l.Va,n=l.Oa;if(n.b())n=R();else{n=n.o();if(c.b())var r=R();else r=!!c.o(),r=new L(!r);n=new L(d.ba(r,n))}return new Uw(m,n,d.ba(c,l.ra),l.Pd)})),b.ma());if(b instanceof Sv){g=b.Fd;e=g.Va;h=g.Oa;if(h.b())h=R();else{h=h.o();if(c.b())var k= R();else k=!!c.o(),k=new L(!k);h=new L(d.ba(k,h))}return new Sv(a,new Uw(e,h,d.ba(c,g.ra),g.Pd),b.ma())}if(b instanceof Wv)return sB(b,new y(l=>d.ba(c,l)),new y(l=>{if(c.b())var m=R();else m=!!c.o(),m=new L(!m);return d.ba(m,l)}),new y(l=>d.ba(c,l)),b.Uu);if(b instanceof Tv&&(e=!0,g=b,h=g.Ic,k=g.kf,h instanceof LA&&null!==k&&k.b()))return new Tv(a,tB(h,new y(l=>d.ba(c,l))),k,g.vp);if(e)return e=g.kf,new Tv(a,d.ba(c,g.Ic),e,b.ma());if(b instanceof Mu)return b;throw new w(b);} function PB(a,b,c){if(b){for(a=a.ib;!c.b();){b=c.e();var d=V(a.q);a=dv(a,b,d,!1);c=c.f()}return a}for(a=a.La;!c.b();)b=c.e(),d=V(a.q),a=Pu(a,b,d,!1),c=c.f();return a}function YC(a,b){return Pe(new E(b),a.Df)?"^":5null!==l));e=c.lR;c=c.e3;e=e.b()||c.n(e.o())?e:R();if(e.b())return R();e=e.o();if(null!==e)c=e.de,k=e.Re,h.Ea()>c?(c=h.Ea(), k=ap(),d=eD(e,c,k,d),a=new Qx(a.Yu,d.de,new cv(a.Yu,h,d.Re,b.Mj))):a=new Qx(a.Yu,c,new cv(a.Yu,h,k,b.Mj));else throw new w(e);return new L(a)}}return t().d}};function OC(a){this.Yu=null;if(null===a)throw null;this.Yu=a}OC.prototype=new p;OC.prototype.constructor=OC;function tca(a,b,c,d){return sca(a,b,ap(),c,d)}OC.prototype.$classData=q({XZ:0},!1,"mlscript.TyperHelpers$PolyFunction$",{XZ:1,g:1}); function TA(a,b){var c=Xu().X(),d=a.q;d.F&&(d=ut(Q(),"| ",d.r)+("INST ["+a.de+"] ")+a,ff(gf(),d+"\n"));d=a.q;d.F&&(d=ut(Q(),"| ",d.r)+" where "+xx(a),ff(gf(),d+"\n"));c=a.Re.Kc(a.de,!1,b,c);d=a.q;d.F&&(b=ut(Q(),"| ",d.r)+("TO ["+b.da+"] ~\x3e ")+c,ff(gf(),b+"\n"));a=a.q;a.F&&(a=ut(Q(),"| ",a.r)+" where "+xx(c),ff(gf(),a+"\n"));return c}function uca(a,b){var c=Xu().X();return a.Re.Kc(a.de,!0,b,c)}function eD(a,b,c,d){var e=Xu().X();return KC(a,b,c,d,e)} function KC(a,b,c,d,e){At(tp(),b>=a.de);if(Pe(new E(b),a.de))return a;var g=a.q,h=a.de,k=a.Re,l=a.q.Df;d=new Iw(d.S,d.Ec,d.hc,d.Ed,1+b|0,d.Pc,d.Zc,d.Lb,d.yc,d.tb,d.$a,d.od,d.cb);return new Qx(g,b,fD(a.q,k,l,d,h,c,e,!1))} function mca(a,b,c,d,e,g){for(;;){var h=!1,k=null;if(b instanceof cv){c=b;k=c.Nb;b=c.ac;h=gD(b,d,a.q.Df);g=a.q;g.F&&(g=ut(Q(),"| ",g.r)+"could be distribbed: "+h,ff(gf(),g+"\n"));if(h.b())return t().d;g=gD(k,d,a.q.Df);var l=a.q;l.F&&(l=ut(Q(),"| ",l.r)+"cannot be distribbed: "+g,ff(gf(),l+"\n"));if(h.wy(g).b())return t().d;h=1+d|0;l=new y(n=>n.Xa);var m=Fq();l=vca(g,l,m);m=a;l=l.b()?m.q.Gd:l.o().Xa;h=h>l?h:l;b=new Qx(a.q,d,b);l=a.q;l.F&&(l=ut(Q(),"| ",l.r)+"inner: "+b,ff(gf(),l+"\n"));e=new cv(a.q, k,eD(b,h,g,e),c.Mj);c=a.q;c.F&&(c=ut(Q(),"| ",c.r)+"raised: "+e,ff(gf(),c+"\n"));c=a.q;c.F&&(c=ut(Q(),"| ",c.r)+" where: "+xx(e),ff(gf(),c+"\n"));if(g.b())return t(),new L(e);t();d=new Qx(a.q,d,e);return new L(d)}if(b instanceof fw&&(l=b,!c.L(l.qb))){k=cD(l,e,g);c=c.bc(l.qb);b=k;continue}if(b instanceof ZB)b=k=b.mc();else{if(b instanceof lx&&(h=!0,k=b,iC(a.q),l=k.Sb,!l.b()&&(l=l.o(),!c.L(k)))){c=c.bc(k);b=l;continue}if(h&&k.Xa>d&&!c.L(k)){b=a;h=vy(k);l=a.q.ib;for(a=h;!a.b();)h=l,l=a.e(),m=V(h.q), l=dv(h,l,m,!1),a=a.f();h=l;c=c.bc(k);a=b;b=h}else if(b instanceof Qx)k=b,b=k.de,d=djD(b)))}function jD(a){Pe(new E(a.q.Im),a.pe)||(a.oe=wca(a),a.pe=a.q.Im);return a.oe} function tB(a,b){if(a instanceof cC){var c=a.Fg,d=a.Sf;return kD(cw(a.q),b.n(c),b.n(d),V(cw(a.q).Vu))}if(a instanceof cv)return c=a.ac,new cv(a.q,b.n(a.Nb),b.n(c),a.ma());if(a instanceof Jv)return rB(a,t().d,new fn((l,m)=>b.n(m)));if(a instanceof Qv)return c=a.Ba,new Qv(a.q,ry(lv(),c,new y(l=>cB(l,b,b))),a.ma());if(a instanceof zv)return c=a.Yb,new zv(a.q,ry(lv(),c,new y(l=>cB(l,b,b))),a.ma());if(a instanceof Wv)return sB(a,b,b,b,a.Uu);if(a instanceof Sv)return new Sv(a.q,cB(a.Fd,b,b),a.ma());if(a instanceof LA)return c=a.jc,new LA(a.q,a.tc,b.n(a.ic),b.n(c),a.ma());if(a instanceof MA)return new MA(a.q,b.n(a.Fc),a.ma());if(a instanceof Tv)return c=a.kf,new Tv(a.q,b.n(a.Ic),c,a.ma());if(a instanceof OA)return new OA(a.q,b.n(a.Hi),a.ma());if(a instanceof $y){c=a.up;d=a.q;var e=b.n(a.Oq),g=a.q;lv();return new $y(d,e,new Qv(g,ry(0,c.Ba,new y(l=>cB(l,b,b))),c.Nj),a.ma())}if(a instanceof ZB&&($B(a.q),t(),c=a.mc(),c=new L(c),!c.b()))return b.n(c.k);if(a instanceof fw){c=a.qb;var h=a.Zb;d=a.q;if(h===u())e=u(); else for(e=h.e(),g=e=new z(b.n(e),u()),h=h.f();h!==u();){var k=h.e();k=new z(b.n(k),u());g=g.p=k;h=h.f()}return new fw(d,c,e,a.ma())}if(a instanceof Qx)return new Qx(a.q,a.de,b.n(a.Re));if(a instanceof eC){h=a.Lj;c=a.kj;a=a.q;d=l=>{var m=b.n(l.h());l=b.n(l.j());return G(new H,m,l)};if(h===u())d=u();else{e=h.e();g=e=new z(d(e),u());for(h=h.f();h!==u();)k=h.e(),k=new z(d(k),u()),g=g.p=k,h=h.f();d=e}return new eC(a,d,b.n(c))}if(a instanceof lx||Uv(a)||a instanceof FA)return a;throw new w(a);} function UA(a,b,c,d,e){var g=!1,h=null,k=!1,l=null,m=!1,n=null;if(a instanceof cC){g=!0;h=a;var r=h.Fg,v=h.Sf;if(c&&!b.b())return b.b()&&xm("Program reached and unexpected state."),b.o()?d.ba((t(),new L(!0)),v):d.ba((t(),new L(!1)),r)}if(g)return c=h.Fg,e=h.Sf,kD(cw(a.q),d.ba((t(),new L(!1)),c),d.ba((t(),new L(!0)),e),V(cw(a.q).Vu));if(a instanceof Qv)return XC(a.q,a,b,d);if(a instanceof Tv&&(g=a.Ic,h=a.kf,c))return a=d.ba(b,g),iB(a,h);if(a instanceof Fv)return qca(a.q,a,b,d);if(a instanceof LA&& (k=!0,l=a,g=l.tc,r=l.ic,h=l.jc,c)){if(g)return a=d.ba(b,r),c=d.ba(b,h),e=V(a.q),dv(a,c,e,!1);a=d.ba(b,r);c=d.ba(b,h);e=V(a.q);return Pu(a,c,e,!1)}if(k)return c=l.jc,new LA(a.q,l.tc,d.ba(b,l.ic),d.ba(b,c),a.ma());if(a instanceof MA&&(m=!0,n=a,l=n.Fc,c))return b.b()?c=R():(c=!!b.o(),c=new L(!c)),c=d.ba(c,l),a=a.ma(),NA(c,a,!1);if(m)return c=n.Fc,e=a.q,b.b()?l=R():(l=!!b.o(),l=new L(!l)),new MA(e,d.ba(l,c),a.ma());if(a instanceof OA)return new OA(a.q,d.ba(b,a.Hi),a.ma());if(a instanceof $y)return c= a.up,e=a.q,l=d.ba(b,a.Oq),m=a.q,lv(),new $y(e,l,new Qv(m,ry(0,c.Ba,new y(x=>{var A=x.Va,B=x.Oa;if(B.b())B=R();else{B=B.o();if(b.b())var C=R();else C=!!b.o(),C=new L(!C);B=new L(d.ba(C,B))}return new Uw(A,B,d.ba(b,x.ra),x.Pd)})),c.Nj),a.ma());if(a instanceof ZB&&($B(a.q),t(),l=a.mc(),l=new L(l),!l.b()))return d.ba(b,l.k);if(a instanceof fw)return new fw(a.q,a.qb,bw(a,b,d,e),a.ma());if(a instanceof Qx)return e=a.de,l=a.Re,c?yx(zx(a.q),e,d.ba(b,l)):new Qx(a.q,e,d.ba(b,l));if(a instanceof eC){n=a.Lj; c=a.kj;a=a.q;e=x=>{var A=d.ba((t(),new L(!0)),x.h());x=d.ba((t(),new L(!1)),x.j());return G(new H,A,x)};if(n===u())e=u();else{l=n.e();m=l=new z(e(l),u());for(n=n.f();n!==u();)k=n.e(),k=new z(e(k),u()),m=m.p=k,n=n.f();e=l}return new eC(a,e,d.ba(b,c))}if(a instanceof lx||Uv(a)||a instanceof FA)return a;throw new w(a);}function lD(a,b){return new Uw(a.q,(t(),new L(a)),a,b)} var dv=function yca(a,b,c,d){var g=a.q.La;if(null===g?null===a:mC(g,a))return a;g=a.q.ib;if(null===g?null===a:mC(g,a))return b;g=a.q.La;if(null===g?null===b:mC(g,b))return b;g=a.q.ib;if(null===g?null===b:mC(g,b))return a;if(a instanceof YB&&!0===a.pA)return b;if(a instanceof YB&&!1===a.pA||a instanceof Qv&&b instanceof cv)return a.q.La;if(a instanceof Qv&&(g=a.Ba,b instanceof Qv))return new Qv(a.q,RC(g,b.Ba),c);if(a instanceof zv&&(g=a.Yb,b instanceof zv)){var h=b.Yb,k=jv(g,h);if(Pe(new E(k),0))return new zv(a.q, TC(g,h),a.Nq)}return d?(null===b?null===a:mC(b,a))?a:a instanceof MA&&(d=a.Fc,null===b?null===d:mC(b,d))?a.q.La:new LA(a.q,!0,b,a,c):yca(b,a,c,!0)},sA=function zca(a,b,c){if(a instanceof cv){var e=a.Nb,g=a.ac;if(b instanceof cv){var h=b.Nb,k=b.ac;if(a.q.UI)return a=a.q,b=V(e.q),e=dv(e,h,b,!1),h=V(g.q),new cv(a,e,zca(g,k,h),c)}}return Pu(a,b,c,!1)},Pu=function mD(a,b,c,d){a:{var g=a.q.La;if(null===g?null===a:mC(g,a))g=!0;else{if(a instanceof Qv){g=a.Ba;var h=O().c;if(null===h?null===g:h.i(g)){g=!0; break a}}g=!1}}if(g)return b;g=a.q.ib;if((null===g?null===a:mC(g,a))||a instanceof Mu&&b instanceof cv)return a.q.ib;if(a instanceof Qv&&(g=a.Ba,b instanceof Qv)){h=b.Ba;a=a.q;b=Su();d=op().ga;b=new Uu(b,d);d=sv();var k=g.Q()+h.Q()|0;h=Lv(Lv(Mv(8oD(Va,Nb,c,d,Rd)&&oD(ic,cb,c,d,Rd);if(h)var Ub=e.ry(new y(Rd=>G(new H,Rd.h(),!0))),jb=zb(Ub);else jb=zb(e);return!!jb}}var db=k.y;if(db instanceof LA){var ub=db.ic,Aa=db.jc;if(!0===db.tc)return oD(ub,b,c,d,e)&&oD(Aa,b,c,d,e)}var va=k.w;if(va instanceof LA){var Ra=va.ic,rb=va.jc;if(!1===va.tc)return oD(a,Ra,c,d,e)&&oD(a,rb,c,d,e)}var xb=k.y;if(xb instanceof LA){var mc=xb.ic,Ha=xb.jc;if(!1===xb.tc)return oD(mc,b,c,d,e)||oD(Ha,b,c,d,e)}var Ka=k.w;if(Ka instanceof LA){var Oa= Ka.ic,Na=Ka.jc;if(!0===Ka.tc)return oD(a,Oa,c,d,e)||oD(a,Na,c,d,e)}var Da=k.y,ta=k.w;if(Da instanceof Qv){var Ya=Da.Ba;if(ta instanceof Qv){var dc=ta.Ba,ka=Rd=>{for(var Bd=dc;!Bd.b();){var ae=Bd.e();a:{for(var dd=Ya;!dd.b();){var od=dd.e().h();if(Pe(new E(od),ae.h())){dd=new L(dd.e());break a}dd=dd.f()}dd=R()}if(dd.b()||!Fw(dd.o().j(),ae.j(),c,Rd))return!1;Bd=Bd.f()}return!0};if(h)var ya=e.ry(new y(Rd=>G(new H,Rd.h(),!0))),Sa=ka(ya);else Sa=ka(e);return!!Sa}}if(k.w instanceof Tv||(k.y instanceof lx|| k.w instanceof lx)&&!h)return!1;if((k.y instanceof lx||k.w instanceof lx)&&e.L(G(new H,a,b)))return!!e.n(G(new H,a,b));var xc=k.y;if(xc instanceof lx){e.bh(G(new H,a,b),!1);var Sb=xc.Sb;if(Sb instanceof L)var uc=oD(Sb.k,b,c,d,e);else{if(t().d!==Sb)throw new w(Sb);a:{for(var Lb=rA(xc);!Lb.b();){var lc=Lb.e();if(oD(lc,b,c,d,e)){uc=!0;break a}Lb=Lb.f()}uc=!1}}uc&&e.bh(G(new H,a,b),!0);return uc}var Xb=k.w;if(Xb instanceof lx){e.bh(G(new H,a,b),!1);var ec=Xb.Sb;if(ec instanceof L)var Ab=oD(a,ec.k,c,d, e);else{if(t().d!==ec)throw new w(ec);a:{for(var Ob=vy(Xb);!Ob.b();){var fb=Ob.e();if(oD(a,fb,c,d,e)){Ab=!0;break a}Ob=Ob.f()}Ab=!1}}Ab&&e.bh(G(new H,a,b),!0);return Ab}var Wa=k.y;if(Wa instanceof eC){var bb=Wa.Lj,Ia=Wa.kj,Ua=O().c;if(null===Ua?null===bb:Ua.i(bb))return oD(Ia,b,c,d,e)}var pc=k.w;if(pc instanceof eC)return oD(a,pc.kj,c,d,e);var sc=k.w;if(sc instanceof MA){var Ba=sc.Fc,ob=V(a.q),nc=Pu(a,Ba,ob,!1);return oD(nc,a.q.ib,c,d,e)}var Ib=k.y;if(Ib instanceof MA){var vc=Ib.Fc,Vb=a.q.La,fc=V(b.q), Bc=dv(b,vc,fc,!1);return oD(Vb,Bc,c,d,e)}var Pb=k.y;if(Pb instanceof fw&&a.q.cn.L(Pb.qb.V)&&PA(Pb,c)){var Jb=QA(Pb,c);return oD(Jb,b,c,d,e)}var gc=k.w;if(gc instanceof fw&&a.q.cn.L(gc.qb.V)&&PA(gc,c)){var Cb=QA(gc,c);return oD(a,Cb,c,d,e)}var cc=k.y;if(cc instanceof fw){var yc=c.tb.U(cc.qb.V);if(yc instanceof L){var Mc=yc.k;if(b instanceof fw&&Pe(new E(b.qb),cc.qb)){for(var qc=dB(Mc),oc=Mc.Xm,Qc=op(),jc=oc.Gb(Qc.ga).j(),sb=qD(new Wq(jc,jc,cc.Zb),b.Zb),Gc=sb.jL.m(),Wb=sb.kL.m(),Cc=sb.lL.m(),Fc=!1;!Fc&& Gc.s()&&Wb.s()&&Cc.s();){var qd=Gc.t(),Yb=Wb.t(),Nc=Cc.t(),ad=Yb,Uc=Nc,cd=qc.n(qd);Fc=!((cd.qe||oD(ad,Uc,c,d,e))&&(cd.Qd||oD(Uc,ad,c,d,e)))}return!Fc}if(Ot(new E(Mc.jj),Bp())){var kc=rD(a.q,Mc,V(a.q),c);return oD(kc,b,c,d,e)}return!1}if(t().d===yc)return!1;throw new w(yc);}if(k.w instanceof fw||k.y instanceof Qx||k.w instanceof Qx)return!1;var Vc=k.w;if(Vc instanceof Jv){for(var Hc=Vc.gi;!Hc.b();){var rc=Hc.e();if(!oD(a,rc,c,d,e))return!1;Hc=Hc.f()}return!0}var sd=k.y;if(sd instanceof Jv){for(var Kc= sd.gi;!Kc.b();){var Qd=Kc.e();if(oD(Qd,b,c,d,e))return!0;Kc=Kc.f()}return!1}if(k.y instanceof eC||k.y instanceof Tv||k.y instanceof Vv||k.w instanceof Vv||k.y instanceof Gv||k.w instanceof Gv||k.y instanceof cv||k.w instanceof cv||k.y instanceof Qv&&Uv(k.w)||Uv(k.y)&&k.w instanceof Qv)return!1;var Ad=k.w;if(Ad instanceof FA&&!0===Ad.Eh)var kd=!0;else{var Hd=k.y;kd=Hd instanceof FA&&!1===Hd.Eh?!0:!1}if(kd)return!1;throw new w(k);};function JA(a,b){var c=Xu().X();return Zu(a.q.La,a,b,!0,c)} function Kv(a,b){var c=a.q.ib,d=Xu().X();return Zu(a,c,b,!0,d)}function iB(a,b){return b.b()?a:a instanceof Tv?new Tv(a.q,a.Ic,a.kf.Ce(b),a.ma()):new Tv(a.q,a,b,V(a.q))} var bC=function sD(a,b){if(b.b())return a;var d=!1,e=null,g=!1,h=null;if(a instanceof Tv)return new Tv(a.q,a.Ic,a.kf.Ce(b),a.ma());if(a instanceof cv)return a;if(a instanceof LA){d=!0;e=a;var k=e.ic,l=e.jc;if(!0===e.tc)return a=iB(k,b),b=iB(l,b),h=V(a.q),dv(a,b,h,!1)}if(d&&(d=e.ic,l=e.jc,!1===e.tc))return a=iB(d,b),b=iB(l,b),h=V(a.q),Pu(a,b,h,!1);if(a instanceof Qv){h=a.q;g=a.Ba;a:for(;;)if(g.b()){b=u();break}else if(l=g.e(),e=g.f(),!1===!b.L(l.h()))g=e;else for(;;){if(e.b())b=g;else{l=e.e();if(!1!== !b.L(l.h())){e=e.f();continue}l=e;e=new z(g.e(),u());d=g.f();for(g=e;d!==l;)k=new z(d.e(),u()),g=g.p=k,d=d.f();for(d=l=l.f();!l.b();){k=l.e();if(!1===!b.L(k.h())){for(;d!==l;)k=new z(d.e(),u()),g=g.p=k,d=d.f();d=l.f()}l=l.f()}d.b()||(g.p=d);b=e}break a}return new Qv(h,b,a.Nj)}if(a instanceof zv){var m=a.Yb;b=b.Fk(new y(n=>{var r=nB(n);n=m.K();if(r.b())return!1;r=r.o()|0;return r!==n&&!(0>r||r>n)}));if(b.b())return a;a=a.kq();e=a.Ba;a:for(;;)if(e.b()){b=u();break}else if(g=e.e(),h=e.f(),!0===!!b.L(g.h()))e= h;else for(;;){if(h.b())b=e;else{g=h.e();if(!0!==!!b.L(g.h())){h=h.f();continue}g=h;h=new z(e.e(),u());l=e.f();for(e=h;l!==g;)d=new z(l.e(),u()),e=e.p=d,l=l.f();for(l=g=g.f();!g.b();){d=g.e();if(!0===!!b.L(d.h())){for(;l!==g;)d=new z(l.e(),u()),e=e.p=d,l=l.f();l=g.f()}g=g.f()}l.b()||(e.p=l);b=h}break a}return new Qv(a.q,b,a.Nj)}if(a instanceof Sv)return a;a instanceof Wv&&no();if(a instanceof MA&&(g=!0,h=a,h.Fc instanceof Mu||h.Fc instanceof cv||h.Fc instanceof Qv))return h;if(g&&(e=h.Fc,e instanceof LA||e instanceof FA||e instanceof MA))return a=NA(e,h.rA,!0),sD(a,b);if(a instanceof FA)return a;if(a instanceof OA)return new OA(a.q,sD(a.Hi,b),a.NE);if(a instanceof ZB&&($B(a.q),t(),h=a.mc(),h=new L(h),!h.b()))return sD(h.k,b);if(Uv(a))return a;if(a instanceof cC)return sD(a.Sf,b);if(a instanceof lx||a instanceof MA||a instanceof fw)return new Tv(a.q,a,b,V(a.q));if(a instanceof Qx)return h=a.de,e=a.Re,yx(zx(a.q),h,sD(e,b));if(a instanceof Jv||a instanceof eC)return a;throw new w(a);},tD=function Bca(a, b){a=DB(a);return a instanceof fw&&PA(a,b)?(a=QA(a,b),Bca(a,b)):a},wD=function uD(a,b,c,d,e){var h=!1,k=null,l=vD(e)?a:tD(a,d);if(l instanceof FA)return new FA(a.q,!l.Eh,V(a.q));if(l instanceof LA){h=!0;k=l;var m=k.ic,n=k.jc;if(!0===k.tc)return a=uD(m,b,c,d,e),c=uD(n,b,c,d,e),b=V(a.q),Pu(a,c,b,!1)}return h&&(h=k.ic,n=k.jc,!1===k.tc)?(a=uD(h,b,c,d,e),c=uD(n,b,c,d,e),b=V(a.q),dv(a,c,b,!1)):l instanceof MA?(b=b.n(l.Fc),ux(b.q,b,c)):l instanceof fw&&!vD(e)&&PA(l,d)?(a=QA(l,d),uD(a,b,c,d,e)):l instanceof Qv||l instanceof cv?a.q.ib:new MA(a.q,b.n(l),c)},Cca=function xD(a,b,c){if(a instanceof MA){var e=a.Fc,g=new y(m=>xD(m,b,c));a=a.ma();return wD(e,g,a,b,c)}if(a instanceof Tv){var h=a.Ic,k=a.kf;if(k.b())return xD(h,b,c);g=!1;e=null;h=vD(c)?DB(h):tD(h,b);k=bC(h,k);if(k instanceof Tv){g=!0;e=k;h=e.Ic;var l=e.kf;if(h instanceof LA)return e=h.jc,new LA(a.q,h.tc,bC(h.ic,l),bC(e,l),h.KE)}return g&&(g=e.Ic,e=e.kf,g instanceof MA)?(g=g.Fc,k=new y(m=>xD(m,b,c)),h=g.ma(),g=wD(g,k,h,b,c),e=bC(g,e),e instanceof Tv&&(g=e.Ic,g instanceof MA)?(g=g.Fc,g instanceof lx||g instanceof Mu||g instanceof Qv?e:vD(c)?e:xm(a+" "+e+" ("+ca(g)+")")):e):k}return a},DB=function Dca(a){if(a instanceof ZB){$B(a.q);t();var c=a.mc();c=new L(c);if(!c.b())return Dca(c.k)}return a},uy=function Eca(a){return a instanceof OA?Eca(a.Hi):a},Fca=function yD(a){var c=DB(a);return c instanceof Tv?yD(c.Ic):c instanceof Qx?yD(c.Re):c instanceof cC?yD(c.Sf):a},uB=function zD(a,b){var d=!1,e=null;if(a instanceof FA&&b===a.Eh)return O().c; if(a instanceof LA){var g=a.ic,h=a.jc;if(b===a.tc)return a=zD(g,b),dl(zD(h,b),a)}if(a instanceof MA&&(d=!0,e=a,h=e.Fc,h instanceof lx&&!b))return a=new UC(a.q,h),b=O().c,new z(a,b);if(d&&(d=e.Fc,d instanceof Gv&&!b))return a=new VC(a.q,d),b=O().c,new z(a,b);if(a instanceof OA)return zD(a.Hi,b);b=O().c;return new z(a,b)};function gD(a,b,c){var d=jA().X(),e=jA().X();Gca(a,a,b,c,e,d);return Aq(Bq(),d)}function AD(a,b){t();return BD(a,new L(!0),a,b)}function CD(a,b){t();return BD(a,new L(!1),a,b)} function BD(a,b,c,d){c=SC(a.q,c,nf(),!1,d);b=iA(a.q,c,b,d);return uf(a.q,b,!0,d)}var Gca=function DD(a,b,c,d,e,g){for(;;){if(cc)if(b instanceof lx){if(!e.L(b))for(e.$(b),b.Xa>c&&b.Xa<=d&&g.$(b),b=ED(b,!0);!b.b();){var k=b.e();DD(a,k,c,d,e,g);b=b.f()}}else if(b instanceof Qx){k=b.de;d=k{this.Tb(h,k)}),this.yA);else if(b instanceof Tv)this.Tb(a,b.Ic);else if(b instanceof cC)d=b.Fg,b=b.Sf,c=(h,k)=>{this.Tb(h,k)},Nm(new E(a.ol),(t(),new L(!0)))&&(e=gA(a.$m).Xu,c(e,d)),Nm(new E(a.ol),(t(),new L(!1)))&&(a=gA(a.$m).Pj,c(a,b));else if(b instanceof Qx)d=b.Re,this.Tb(new dC(a, b.de),d);else if(b instanceof eC){d=b.kj;for(b=b.Lj;!b.b();){e=b.e();if(null!==e)c=e.h(),e=e.j(),this.Tb(gA(this.Vq).Pj,c),this.Tb(gA(this.Vq).Jx,e);else throw new w(e);b=b.f()}this.Tb(a,d)}else throw new w(b);}}};GD.prototype.Kp=function(a,b){var c=b.Oa,d=new UB(a);c.b()||(c=c.o(),this.Tb(d,c));this.Tb(a,b.ra)};function JD(a,b,c){var d=c.Oa;d.b()?d=!1:(d=d.o(),d=Pe(new E(d),c.ra));d?a.Tb(new TB(b),c.ra):GD.prototype.Kp.call(a,b,c)} function KD(a,b,c){var d=!1,e=null;if(a instanceof lx){d=!0;e=a;iC(a.q);var g=e.Sb;if(!g.b()){var h=g.o(),k=G(new H,b,h),l=O().c;return new z(k,l)}}if(d){var m=b.Ig(e.Xa);if(Nm(new E(m),(t(),new L(!1)))){var n=vy(e),r=(bd=>Rc=>{var Wc=new RB(b,bd.Xa,!0);return G(new H,Wc,Rc)})(e);if(n===u())var v=u();else{for(var x=n.e(),A=new z(r(x),u()),B=A,C=n.f();C!==u();){var D=C.e(),F=new z(r(D),u());B=B.p=F;C=C.f()}v=A}}else v=O().c;if(Nm(new E(m),(t(),new L(!0)))){var I=rA(e),M=(bd=>Rc=>{var Wc=new RB(b,bd.Xa, !1);return G(new H,Wc,Rc)})(e);if(I===u())var N=u();else{for(var P=I.e(),T=new z(M(P),u()),Y=T,Z=I.f();Z!==u();){var S=Z.e(),ea=new z(M(S),u());Y=Y.p=ea;Z=Z.f()}N=T}}else N=O().c;return dl(N,v)}if(a instanceof cv){var ia=a.Nb,X=a.ac,sa=new UB(b),Ja=G(new H,sa,ia),Xa=G(new H,b,X),Fa=O().c;return new z(Ja,new z(Xa,Fa))}if(a instanceof Jv){var za=a.gi;if(za===u())return u();for(var Qa=za.e(),Ma=new z(G(new H,b,Qa),u()),Ga=Ma,ab=za.f();ab!==u();){var Hb=ab.e(),bc=new z(G(new H,b,Hb),u());Ga=Ga.p=bc;ab= ab.f()}return Ma}if(a instanceof LA){var yb=a.jc,tb=G(new H,b,a.ic),eb=G(new H,b,yb),kb=O().c;return new z(tb,new z(eb,kb))}if(a instanceof Qv){for(var Rb=a.Ba,Gb=op(),vb=Rb.Gb(Gb.ga).j(),Tb=null,Nb=null;vb!==u();){for(var ic=vb.e(),Va=LD(b,ic).m();Va.s();){var cb=new z(Va.t(),u());null===Nb?Tb=cb:Nb.p=cb;Nb=cb}vb=vb.f()}return null===Tb?u():Tb}if(a instanceof zv){for(var zb=a.Yb,Ub=op(),jb=zb.Gb(Ub.ga).j(),db=null,ub=null;jb!==u();){for(var Aa=jb.e(),va=LD(b,Aa).m();va.s();){var Ra=new z(va.t(), u());null===ub?db=Ra:ub.p=Ra;ub=Ra}jb=jb.f()}return null===db?u():db}if(a instanceof Sv)return LD(b,a.Fd);if(a instanceof Wv){for(var rb=a.fo,xb=null,mc=null;rb!==u();){var Ha=rb.e();if(Ha instanceof fe)var Ka=G(new H,b,Ha.aa),Oa=O().c,Na=new z(Ka,Oa);else{if(!(Ha instanceof Ud))throw new w(Ha);Na=LD(b,Ha.fa)}for(var Da=Na.m();Da.s();){var ta=new z(Da.t(),u());null===mc?xb=ta:mc.p=ta;mc=ta}rb=rb.f()}return null===xb?u():xb}if(a instanceof MA){var Ya=a.Fc,dc=new UB(b),ka=G(new H,dc,Ya),ya=O().c;return new z(ka, ya)}if(a instanceof FA)return O().c;if(a instanceof ZB){$B(a.q);t();var Sa=a.mc(),xc=new L(Sa);if(!xc.b()){var Sb=G(new H,b,xc.k),uc=O().c;return new z(Sb,uc)}}if(XB(a)||a instanceof YB)return O().c;if(a instanceof mx){var Lb=G(new H,b,a.hi),lc=O().c;return new z(Lb,lc)}if(a instanceof fw)return aC(a,b,new fn((bd,Rc)=>G(new H,bd,Rc)),c);if(a instanceof Tv){var Xb=G(new H,b,a.Ic),ec=O().c;return new z(Xb,ec)}if(a instanceof cC){var Ab=a.Fg,Ob=a.Sf,fb=gA(a.q).Xu,Wa=G(new H,fb,Ab),bb=gA(a.q).Pj,Ia=G(new H, bb,Ob),Ua=O().c;return new z(Wa,new z(Ia,Ua))}if(a instanceof Qx){var pc=G(new H,b,a.Re),sc=O().c;return new z(pc,sc)}if(a instanceof eC){for(var Ba=a.kj,ob=a.Lj,nc=null,Ib=null;ob!==u();){for(var vc=ob.e(),Vb=gA(a.q).Pj,fc=G(new H,Vb,vc.h()),Bc=gA(a.q).Jx,Pb=G(new H,Bc,vc.j()),Jb=O().c,gc=new Om(new z(fc,new z(Pb,Jb)));gc.s();){var Cb=new z(gc.t(),u());null===Ib?nc=Cb:Ib.p=Cb;Ib=Cb}ob=ob.f()}var cc=null===nc?u():nc,yc=G(new H,b,Ba),Mc=O().c;return dl(new z(yc,Mc),cc)}if(a instanceof BA){CA(a.q); t();var qc=new L(a);if(!qc.b()){for(var oc=qc.k,Qc=oc.xk,jc=null,sb=null;Qc!==u();){var Gc=Qc.e();if(Gc instanceof ax){var Wb=Gc,Cc=Wb.rk,Fc=(bd=>Rc=>{var Wc=new TB(bd);return G(new H,Wc,Rc.hb)})(b);if(Cc===u())var qd=u();else{for(var Yb=Cc.e(),Nc=new z(Fc(Yb),u()),ad=Nc,Uc=Cc.f();Uc!==u();){var cd=Uc.e(),kc=new z(Fc(cd),u());ad=ad.p=kc;Uc=Uc.f()}qd=Nc}var Vc=G(new H,b,Wb.Ql),Hc=O().c,rc=dl(new z(Vc,Hc),qd)}else if(Gc instanceof bx){var sd=Gc,Kc=gA(a.q).Pj,Qd=G(new H,Kc,sd.hh),Ad=O().c;rc=new z(Qd, Ad)}else if(Gc instanceof Nw){var kd=Gc,Hd=kd.kl.m(),Rd=new Ef(Hd,new y((bd=>Rc=>{var Wc=new TB(bd);return G(new H,Wc,Rc.hb)})(b)));rc=kv(Rd,new U(((bd,Rc,Wc)=>()=>{var Wd=bd.Jj.ie();return new xo(Wd,new y(zd=>MD(a,zd,Rc,Wc)))})(kd,b,c))).nb(new U(((bd,Rc)=>()=>{t();var Wc=new UB(bd);Wc=G(new H,Wc,Rc.hl);return new L(Wc)})(b,kd))).nb(new U(((bd,Rc)=>()=>{t();var Wc=new UB(bd);Wc=G(new H,Wc,Rc.jl);return new L(Wc)})(b,kd)))}else if(Gc instanceof Yw){var Bd=Gc,ae=Bd.gh.m(),dd=new Ef(ae,new y((bd=>Rc=> {var Wc=new TB(bd);return G(new H,Wc,Rc.hb)})(b)));rc=kv(dd,new U((bd=>()=>{for(var Rc=bd.Ij.ha(),Wc=null,Wd=null;Rc!==u();){for(var zd=Rc.e(),Pa=null,Db=null;zd!==u();){var Oc=zd.e();for(Oc=LD(gA(a.q).Pj,Oc.j()).m();Oc.s();){var Tc=new z(Oc.t(),u());null===Db?Pa=Tc:Db.p=Tc;Db=Tc}zd=zd.f()}for(zd=(null===Pa?u():Pa).m();zd.s();)Pa=new z(zd.t(),u()),null===Wd?Wc=Pa:Wd.p=Pa,Wd=Pa;Rc=Rc.f()}return null===Wc?u():Wc})(Bd))).nb(new U((bd=>()=>{for(var Rc=bd.gl.ha(),Wc=null,Wd=null;Rc!==u();){var zd=Rc.e(), Pa=Sd=>{var Jc=gA(a.q).Xu;return G(new H,Jc,Sd.j())};if(zd===u())Pa=u();else{var Db=zd.e(),Oc=Db=new z(Pa(Db),u());for(zd=zd.f();zd!==u();){var Tc=zd.e();Tc=new z(Pa(Tc),u());Oc=Oc.p=Tc;zd=zd.f()}Pa=Db}for(Pa=Pa.m();Pa.s();)Db=new z(Pa.t(),u()),null===Wd?Wc=Db:Wd.p=Db,Wd=Db;Rc=Rc.f()}return null===Wc?u():Wc})(Bd))).nb(new U(((bd,Rc,Wc)=>()=>{var Wd=bd.Ei.ie();return new xo(Wd,new y(zd=>MD(a,zd,Rc,Wc)))})(Bd,b,c))).nb(new U(((bd,Rc)=>()=>{t();var Wc=new UB(bd);Wc=G(new H,Wc,Rc.Um);return new L(Wc)})(b, Bd))).nb(new U(((bd,Rc)=>()=>{t();var Wc=G(new H,bd,Rc.sk);return new L(Wc)})(b,Bd))).nb(new U(((bd,Rc,Wc)=>()=>{var Wd=bd.Tm.ie();return new xo(Wd,new y(zd=>MD(a,zd,Rc,Wc)))})(Bd,b,c)))}else if(Gc instanceof Ww){var od=Gc,Ta=od.wk.m(),wb=new Ef(Ta,new y((bd=>Rc=>{var Wc=new TB(bd);return G(new H,Wc,Rc.hb)})(b)));rc=kv(wb,new U(((bd,Rc,Wc)=>()=>{var Wd=bd.tk.ie();return new xo(Wd,new y(zd=>MD(a,zd,Rc,Wc)))})(od,b,c))).nb(new U(((bd,Rc)=>()=>{t();var Wc=new UB(bd);Wc=G(new H,Wc,Rc.co);return new L(Wc)})(b, od))).nb(new U(((bd,Rc)=>()=>{t();var Wc=G(new H,bd,Rc.uk);return new L(Wc)})(b,od))).nb(new U(((bd,Rc,Wc)=>()=>{var Wd=bd.Vm.ie();return new xo(Wd,new y(zd=>MD(a,zd,Rc,Wc)))})(od,b,c)))}else if(Gc instanceof Vw)rc=LD(b,Gc.ig);else{if(!(Gc instanceof cx))throw new w(Gc);rc=t().d}for(var $a=rc.m();$a.s();){var wa=new z($a.t(),u());null===sb?jc=wa:sb.p=wa;sb=wa}Qc=Qc.f()}var hb=null===jc?u():jc,ra=oc.Ul.ha();if(ra===u())var wc=u();else{for(var ac=ra.e(),Id=new z(G(new H,b,ac),u()),ud=Id,be=ra.f();be!== u();){var re=be.e(),pe=new z(G(new H,b,re),u());ud=ud.p=pe;be=be.f()}wc=Id}return dl(wc,hb)}}throw new w(a);}function hA(a,b,c){var d=Xu().X(),e=jA().X();Hca(a,b,!1,a.q.Df,a,!1,d,e,c);a=Su();b=op().ga;a=new Uu(a,b);return Ov(sv(),d,a)} var Ica=function ND(a,b){if(b instanceof bx){var d=b.hh,e=O().c;return new z(d,e)}if(b instanceof ax)return d=b.rk.m(),d=new Ef(d,new y(h=>h.hb)),kv(d,new U(()=>{t();return new L(b.Ql)}));if(b instanceof Nw)return d=b.kl.m(),d=new Ef(d,new y(h=>h.hb)),kv(d,new U(()=>{var h=b.Jj.ie();return new xo(h,new y(k=>ND(a,k)))})).nb(new U(()=>{t();return new L(b.hl)})).nb(new U(()=>{t();return new L(b.jl)}));if(b instanceof Yw)return d=b.gh.m(),d=new Ef(d,new y(h=>h.hb)),kv(d,new U(()=>{for(var h=b.Ij.ha(), k=null,l=null;h!==u();){for(var m=h.e(),n=null,r=null;m!==u();){var v=m.e(),x=v.j().Oa.ha();v=v.j().ra;var A=O().c;for(x=dl(new z(v,A),x).m();x.s();)v=new z(x.t(),u()),null===r?n=v:r.p=v,r=v;m=m.f()}for(m=(null===n?u():n).m();m.s();)n=new z(m.t(),u()),null===l?k=n:l.p=n,l=n;h=h.f()}return null===k?u():k})).nb(new U(()=>{for(var h=b.gl.ha(),k=null,l=null;h!==u();){var m=h.e();for(m=mv(lv(),m);m.s();){var n=new z(m.t(),u());null===l?k=n:l.p=n;l=n}h=h.f()}return null===k?u():k})).nb(new U(()=>{var h= b.Ei.ie();return new xo(h,new y(k=>ND(a,k)))})).nb(new U(()=>{t();return new L(b.Um)})).nb(new U(()=>{t();return new L(b.sk)}));if(b instanceof Ww)return d=b.wk.m(),d=new Ef(d,new y(h=>h.hb)),kv(d,new U(()=>{var h=b.tk.ie();return new xo(h,new y(k=>ND(a,k)))})).nb(new U(()=>{t();return new L(b.co)})).nb(new U(()=>{t();return new L(b.uk)})).nb(new U(()=>{var h=b.Vm.ie();return new xo(h,new y(k=>ND(a,k)))}));if(b instanceof Vw){d=b.ig.Oa.ha();e=b.ig.ra;var g=O().c;return dl(new z(e,g),d)}if(b instanceof cx)return O().c;throw new w(b);}; function ED(a,b){var c=!1,d=null;if(a instanceof lx){c=!0;d=a;iC(a.q);var e=d.Sb;if(!e.b())return a=e.o(),b?(b=O().c,new z(a,b)):O().c}if(c)return b?(a=vy(d),dl(rA(d),a)):O().c;if(a instanceof cv)return b=a.Nb,a=a.ac,d=O().c,new z(b,new z(a,d));if(a instanceof Jv)return a.gi;if(a instanceof LA)return b=a.ic,a=a.jc,d=O().c,new z(b,new z(a,d));if(a instanceof Qv){a=a.Ba;for(d=b=null;a!==u();){e=a.e();c=e.j().Oa.ha();e=e.j().ra;var g=O().c;for(c=dl(new z(e,g),c).m();c.s();)e=new z(c.t(),u()),null=== d?b=e:d.p=e,d=e;a=a.f()}return null===b?u():b}if(a instanceof zv){a=a.Yb;for(d=b=null;a!==u();){e=a.e();c=e.j().Oa.ha();e=e.j().ra;g=O().c;for(c=dl(new z(e,g),c).m();c.s();)e=new z(c.t(),u()),null===d?b=e:d.p=e,d=e;a=a.f()}return null===b?u():b}if(a instanceof Sv)return b=a.Fd,a=b.Oa.ha(),b=b.ra,d=O().c,un(a,new z(b,d));if(a instanceof MA)return a=a.Fc,b=O().c,new z(a,b);if(a instanceof FA)return O().c;if(a instanceof ZB&&($B(a.q),t(),b=a.mc(),b=new L(b),!b.b()))return a=b.k,b=O().c,new z(a,b);if(XB(a)|| a instanceof YB)return O().c;if(a instanceof mx)return a=a.hi,b=O().c,new z(a,b);if(a instanceof fw)return a.Zb;if(a instanceof Tv)return a=a.Ic,b=O().c,new z(a,b);if(a instanceof cC)return b=a.Fg,a=a.Sf,d=O().c,new z(b,new z(a,d));if(a instanceof Qx)return a=a.Re,b=O().c,new z(a,b);if(a instanceof eC){b=a.kj;a=a.Lj;for(c=d=null;a!==u();){g=a.e();e=g.h();g=g.j();var h=O().c;for(e=new Om(new z(e,new z(g,h)));e.s();)g=new z(e.t(),u()),null===c?d=g:c.p=g,c=g;a=a.f()}a=null===d?u():d;d=O().c;return dl(new z(b, d),a)}if(a instanceof Wv){a=a.fo;for(d=b=null;a!==u();){c=a.e();if(c instanceof fe)c=c.aa,e=O().c,c=new z(c,e);else{if(!(c instanceof Ud))throw new w(c);e=c.fa;c=e.Oa.ha();e=e.ra;g=O().c;c=dl(new z(e,g),c)}for(c=c.m();c.s();)e=new z(c.t(),u()),null===d?b=e:d.p=e,d=e;a=a.f()}return null===b?u():b}if(a instanceof BA&&(CA(a.q),t(),b=new L(a),!b.b())){b=b.k;d=b.xk;for(e=c=null;d!==u();){g=d.e();for(g=Ica(a,g).m();g.s();)h=new z(g.t(),u()),null===e?c=h:e.p=h,e=h;d=d.f()}a=null===c?u():c;return dl(b.Ul.ha(), a)}throw new w(a);}function xA(a,b){var c=jA().X(),d=O().c;Jca(new z(a,d),c,b);a=uv();O();b=new y(e=>e.sp);d=Fq();return oA(a,c,new OD(d,b))} function xx(a){var b=xA(a,!0).m();b=new iy(b,new y(c=>{if(c.Sb.b()){var d=rA(c);c=vy(c);return!un(d,c).b()}return!0}),!1);b=new Ef(b,new y(c=>{if(null!==c){iC(a.q);var d=c.Sb;if(!d.b())return d=d.o(),"\n\t\t"+c.u()+" :\x3d "+d}d=c.u();if(vy(c).b())var e="";else e=vy(c),e=" :\x3e "+ze(e,""," | ","");rA(c).b()?c="":(c=rA(c),c=" \x3c: "+ze(c,""," \x26 ",""));return"\n\t\t"+d+e+c}));return ze(b,"","","")} function LD(a,b){var c=b.Oa;if(c.b())c=R();else{c=c.o();var d=new UB(a);c=new L(G(new H,d,c))}c=c.ha();a=G(new H,a,b.ra);b=O().c;return dl(new z(a,b),c)}function MD(a,b,c,d){if(b instanceof Vw)return c=b.ig,LD(gA(a.q).Pj,c);if(b instanceof bx)return c=G(new H,c,b.hh),d=O().c,new z(c,d);if(b&&b.$classData&&b.$classData.rb.Rs){a=a.q;var e=O().c;b=new By(a,new z(b,e),t().d);return KD(b,c,d)}throw new w(b);} var Hca=function PD(a,b,c,d,e,g,h,k,l){for(;;){if(e instanceof lx){if(g&&e.Xa>d){g=a.q;g.F&&(g=ut(Q(),"| ",g.r)+"Quantified! "+e,ff(gf(),g+"\n"));break}var n=b.Ig(e.Xa);c||h.ou(e,new y((A=>B=>{if(B instanceof L&&Nm(new E(B.k),A))return t(),B=t().d,new L(B);t();return new L(A)})(n)));if(n instanceof L){n=!!n.k;var r=G(new H,e,n);k.L(r)?n=!1:(n=G(new H,e,n),k.$(n),n=!0)}else{if(t().d!==n)throw new w(n);n=G(new H,e,!0);k.L(n)?(n=G(new H,e,!1),n=k.L(n)):n=!1;n?n=!1:(n=G(new H,e,!0),k.$(n),n=G(new H,e, !1),k.$(n),n=!0)}if(n)for(b=KD(e,b,l);!b.b();)e=b.e(),PD(a,e.h(),c,d,e.j(),g,h,k,l),b=b.f()}else{if(e instanceof ZB&&(n=e,$B(a.q),t(),n=n.mc(),n=new L(n),!n.b())){e=n.k;continue}if(e instanceof Jv)for(e=e.gi;!e.b();)n=e.e(),PD(a,b,c,d,n,g,h,k,l),e=e.f();else{if(e instanceof MA){e=e.Fc;b=new UB(b);continue}if(e instanceof Tv){e=e.Ic;continue}if(e instanceof cC){b=e.Fg;e=e.Sf;PD(a,gA(a.q).Xu,c,d,b,g,h,k,l);b=a;n=gA(a.q).Pj;a=b;b=n;continue}if(e instanceof eC){var v=e;e=v.kj;n=a;r=d;for(v=v.Lj;!v.b();){var x= v.e();PD(n,gA(n.q).Pj,!1,r,x.h(),g,h,k,l);PD(n,gA(n.q).Jx,!1,r,x.j(),g,h,k,l);v=v.f()}continue}if(e instanceof LA){n=e.jc;PD(a,b,c,d,e.ic,g,h,k,l);e=n;continue}if(e instanceof Qx){b=new dC(b,e.de);n=e.de;d=d{m=new mx(a.q,m.hb,V(a.q));return G(new H,m,n)});Yq();c=Ew(c,l);op();e=SC(e,g,c.Ti(),!1,d);break a}if(h&&(l=k.k,l instanceof Ww)){tp();h=l.wk.K();up(0,Pe(new E(h),a.Zb.K()));h=a.q;k=new vl(l.vk.gb.V);k=Cq(k,a.Xl.Ga);t();h=Jx(h,l,k,new L(a.Zb),d); if(null===h)throw new w(h);h=h.h();c=c?(Sw(a.q),l.uk.Kc(l.Hq,!1,d,h)):a.q.La;l=Xx(a.q,l.vk,V(a.q),d);h=V(c.q);c=Pu(c,l,h,!1);e=g.sb?g.vb:QD(a,g,e);g=V(c.q);e=Pu(c,e,g,!1);break a}if(h&&(l=k.k,l instanceof Yw)){tp();h=l.gh.K();up(0,Pe(new E(h),a.Zb.K()));h=a.q;k=new vl(l.Rf.gb.V);k=Cq(k,a.Xl.Ga);t();h=Jx(h,l,k,new L(a.Zb),d);if(null===h)throw new w(h);h=h.h();c=c?(Sw(a.q),l.sk.Kc(l.Sm,!1,d,h)):a.q.La;l=bz(a.q,l.Rf,V(a.q),d);h=V(l.q);c=Pu(l,c,h,!1);e=g.sb?g.vb:QD(a,g,e);g=V(c.q);e=Pu(c,e,g,!1);break a}l= !1;c=null;h=e.Ab;if(h instanceof yo&&(l=!0,c=h,Kca(c.pb))){tp();l=c.hg.K();up(0,Pe(new E(l),a.Zb.K()));c=bz(a.q,c,V(a.q),d);e=g.sb?g.vb:QD(a,g,e);g=V(c.q);e=Pu(c,e,g,!1);break a}l&&Ot(new E(c.pb),Fp())?(tp(),l=c.hg.K(),up(0,Pe(new E(l),a.Zb.K())),c=Xx(a.q,c,V(a.q),d),e=g.sb?g.vb:QD(a,g,e),g=V(c.q),e=Pu(c,e,g,!1)):(l&&Ot(new E(c.pb),Ap())&&xm("cannot expand unforced type alias"),e=Ey(a.q))}e=new L(e)}if(e.b()){c=new $e;g=d.tb.n(a.qb.V);tp();e=a.Zb.K();At(0,Pe(new E(e),g.Xm.K()));e=a.q;l=g.jj;if(Ap()=== l)b=g.Cx;else{if(zp()===l)throw RD("Namespaces are not supported yet.");if(Bp()===l)l=rD(a.q,g,a.Xl,d),h=g.Cx,k=V(l.q),l=Pu(l,h,k,!1),b=c.sb?c.vb:SD(a,c,b,g),c=V(l.q),b=Pu(l,b,c,!1);else{if(Fp()!==l)throw cp()===l&&xm("mixins cannot be used as types"),new w(l);l=TD(a.q,g,a.Xl);h=g.Cx;k=V(l.q);l=Pu(l,h,k,!1);b=c.sb?c.vb:SD(a,c,b,g);c=V(l.q);b=Pu(l,b,c,!1)}}g=g.UN;g=new UD(new Wq(g,g,a.Zb));op();d=SC(e,b,pp(qp(),g),!1,d)}else d=e.o();return d} function Lca(a,b){a=b.$a.U(a.qb.V);return a instanceof L?(a=a.k.Ab,a instanceof yo&&a.Pm.b()?!a.Hj.b():!0):!0} function gw(a,b){var c=a.MI;if(c.b()){c=!1;var d=null,e=b.tb.U(a.qb.V);a:{if(e instanceof L&&(c=!0,d=e,e=d.k,null!==e&&(Ot(new E(e.jj),Bp())||Ot(new E(e.jj),zp())))){t();b=rD(a.q,e,V(a.q),b);b=new L(b);break a}if(c&&(c=d.k,null!==c&&Ot(new E(c.jj),Fp()))){b=t().d;break a}c=b.$a.U(a.qb.V);if(c instanceof L&&(c=c.k.Ab,c instanceof yo&&(Ot(new E(c.pb),Bp())||Ot(new E(c.pb),zp())))){t();b=bz(a.q,c,V(a.q),b);b=new L(b);break a}b=t().d}a.MI=(t(),new L(b));a=b}else a=c.o();return a} function bw(a,b,c,d){var e=d.tb.U(a.qb.V);if(e instanceof L)e=e.k,d=e.Nu,e=e.Xm;else{if(t().d!==e)throw new w(e);e=d.$a.n(a.qb.V);d=t().d;var g=e.Ag();if(g===u())e=u();else{e=g.e();var h=e=new z(G(new H,e.kc,e.hb),u());for(g=g.f();g!==u();){var k=g.e();k=new z(G(new H,k.kc,k.hb),u());h=h.p=k;g=g.f()}}}if(d.b()){g=a.Zb;d=m=>c.ba(t().d,m);if(g===u())return u();e=g.e();h=e=new z(d(e),u());for(g=g.f();g!==u();)k=g.e(),k=new z(d(k),u()),h=h.p=k,g=g.f();return e}var l=d.o();tp();d=jv(e,a.Zb);up(0,Pe(new E(d), 0));d=new Wq(e,e,a.Zb);e=new fn((m,n)=>{n=G(new H,m,n);var r=n.y;m=n.w;if(null!==r){n=l.n(r.j());if(null!==n&&(r=n.qe,!0===n.Qd&&!0===r))return m=t().d,c.ba(m,new cC(a.q,a.q.ib,a.q.La,V(a.q)));if(null!==n)return r=n.qe,n.Qd?n=b:r?b.b()?n=R():(n=!!b.o(),n=new L(!n)):n=t().d,c.ba(n,m);throw new w(n);}throw new w(n);});Yq();return Ew(d,e)} function aC(a,b,c,d){var e=tc();try{var g=d.tb.U(a.qb.V);if(g instanceof L)var h=g.k,k=h.Nu,l=h.Xm;else{if(t().d!==g)throw new w(g);var m=d.$a.Se(a.qb.V,new U(()=>{throw Hq(new Iq,e,O().c);})),n=it().n(VD(m)),r=m.Ag();if(r===u())var v=u();else{var x=r.e(),A=new z(G(new H,x.kc,x.hb),u());d=A;for(var B=r.f();B!==u();){var C=B.e(),D=new z(G(new H,C.kc,C.hb),u());d=d.p=D;B=B.f()}v=A}k=n;l=v}if(k.b()){var F=a.Zb,I=ia=>c.ba(new TB(b),ia);if(F===u())return u();var M=F.e(),N=new z(I(M),u());M=N;for(var P= F.f();P!==u();){var T=P.e(),Y=new z(I(T),u());M=M.p=Y;P=P.f()}return N}var Z=k.o();tp();I=jv(l,a.Zb);up(0,Pe(new E(I),0));var S=new Wq(l,l,a.Zb),ea=new fn((ia,X)=>{X=G(new H,ia,X);var sa=X.y;ia=X.w;if(null!==sa){X=Z.n(sa.j());if(null!==X&&(sa=X.qe,!0===X.Qd&&!0===sa))return ia=new TB(b),c.ba(ia,new cC(a.q,a.q.ib,a.q.La,V(a.q)));if(null!==X)return sa=X.qe,c.ba(X.Qd?b:sa?new UB(b):new TB(b),ia);throw new w(X);}throw new w(X);});Yq();return Ew(S,ea)}catch(ia){if(ia instanceof Iq){F=ia;if(F.Qg===e)return F.Cj(); throw F;}throw ia;}}function QD(a,b,c){if(null===b)throw le();if(b.sb)return b.vb;var d=a.q;c=c.Ag();c=new Wq(c,c,a.Zb);var e=new fn((g,h)=>{var k=G(new H,g,h);h=k.y;g=k.w;if(null!==h){k=h.kc;h=h.Rd;var l=new vl(a.qb.V+"#"+k.V);k=k.A();k=Cq(l,k);l=WD(a.q);h=h.b()?ou().Yl:h.o();g=XD(l,h,g,g,V(a.q));return G(new H,k,g)}throw new w(k);});Yq();c=Ew(c,e);return me(b,new Qv(d,c,V(a.q)))} function SD(a,b,c,d){if(null===b)throw le();if(b.sb)return b.vb;if(c){c=xv(a.q);var e=d.Xm,g=m=>{if(null!==m){var n=m.h();m=m.j();var r=dB(d);n=new vl(a.qb.V+"#"+n.V);m=new Uw(a.q,new L(r.n(m).Qd?a.q.ib:m),r.n(m).qe?a.q.La:m,a.Xl);return G(new H,n,m)}throw new w(m);};if(e===u())g=u();else{var h=e.e(),k=h=new z(g(h),u());for(e=e.f();e!==u();){var l=e.e();l=new z(g(l),u());k=k.p=l;e=e.f()}g=h}c=SA(c,g,V(a.q))}else c=a.q.La;return me(b,c)} function YD(a){var b=!1,c=Xz(Q(),a.x);if(c instanceof L&&(b=!0,48===Ea(c.k)))return Pe(new E(a.x.length),1);if(b){a=a.x;b=0;for(c=a.length;bd?48<=d&&57>=d:9===ZD(e,d)))return!1;b=1+b|0}return!0}if(t().d===c)return!1;throw new w(c);}function nB(a){return YD(a)?(a=a.x,$D(aE(),a)):t().d} function kca(a){var b=Hu(Q(),a.x);Or(Pr(),b)?(b=Hu(Q(),a.x),b=bE(Pr(),b)):b=!1;b?b=!0:(b=Hu(Q(),a.x),b=Pe(new E(hc(b)),hc(95)));b?b=!0:(b=Hu(Q(),a.x),b=Pe(new E(hc(b)),hc(36)));return b&&Nm(new E(a.x),"true")?Nm(new E(a.x),"false"):!1}function cE(a){a.xK(yn(a.mv()))}var Nca=function Mca(a,b){if(a.Ip.L(b))return!0;a=a.cJ;if(a.b())return!1;a=a.o();return Mca(a,b)}; function dE(a,b){var c=tc();try{if(""===b)return a.bJ.t();eE();var d=String.fromCharCode(39),e=String.fromCharCode(36),g=b.split(d).join(e);if(a.Ip.L(g)?0:!eq().qF.L(g))return g;for(b=1;;){d=""+g+b;if(!a.Ip.L(d))throw Hq(new Iq,c,d);if(2147483647===b)break;b=1+b|0}throw new gm(""===g?"Cannot allocate a runtime name":"Cannot allocate a runtime name starting with '"+g+"'");}catch(h){if(h instanceof Iq){a=h;if(a.Qg===c)return a.Cj();throw a;}throw h;}} function zo(a,b,c,d){var e=!1,g=null,h=a.Rx.U(b);a:{if(h instanceof L){e=!0;g=h;var k=g.k;if(k instanceof ko&&k.RP){b=new io(b,k.re,!1,c,!0);break a}}if(e)b=new io(b,g.k.zo(),!0,c,!0);else if(t().d===h)b=new io(b,dE(a,b),!1,c,!0);else throw new w(h);}vp(a,b);d.b()||(d=d.o(),vp(a,new io(d,b.nt,!1,c,!0)));return b} function fE(a,b){a.cJ=b;Ty();b=u();a.ko=Uy(b);Ty();b=u();a.Rx=Uy(b);pz();b=u();a.Ip=qz(b);Ty();b=u();a.PA=Uy(b);a.lo=new gE;a.bJ=new xo(new hE(1,1,2147483647,!1),new y(c=>{c|=0;var d=eE().QP;c=Oca(d,c);c=new Ef(c,new y(e=>{var g=ze(e,"","","");return G(new H,e,g)}));c=new iy(c,new y(e=>{if(null!==e)return!Nca(a,e.j());throw new w(e);}),!1);return new Ef(c,new y(e=>{if(null!==e)return e.j();throw new w(e);}))}));return a}function iE(){this.bJ=this.lo=this.PA=this.Ip=this.Rx=this.ko=this.cJ=null} iE.prototype=new p;iE.prototype.constructor=iE;function yp(a,b){a.ko.bj(b.xo(),b);a.Rx.bj(b.xo(),b);a.Ip.oh(b.zo())}function vp(a,b){a.Rx.bj(b.xo(),b);a.Ip.oh(b.zo())}function Cm(a,b){var c=a.Rx.U(b);return c.b()?(a=a.cJ,a.b()?R():Cm(a.o(),b)):c}function Taa(a,b,c,d,e){var g=dE(a,b);b=new mn(b,g,c,d,e);yp(a,b);return b}function Saa(a,b,c,d){b=new nn(b,c,d);a.ko.bj(b.er,b)} function Im(a,b,c,d,e){var g=!1,h=null,k=a.Rx.U(b);a:{if(k instanceof L&&(g=!0,h=k,k=h.k,k instanceof io&&!k.RA)){g=k.nt;break a}if(g&&(k=h.k,k instanceof ko&&k.RP)){g=k.re;break a}if(g&&(g=h.k,g instanceof go&&!g.DP)){g=g.bF;break a}g=dE(a,b)}b=new ko(b,g,c,d,!1);vp(a,b);e.b()||(e=e.o(),vp(a,new ko(e,g,c,d,!1)));return b}function gp(a,b){b=dE(a,b);t();b=new ko("this",b,new L(!1),!1,!1);Ly(a.PA,b.re,b,!1);vp(a,b);return b.re} function lm(a,b){var c=a.PA;a=()=>{throw new gm("qualifier "+b+" not found");};if(ca(c)!==da(Ky))if(c=c.U(b),c instanceof L)a=c.k;else{if(R()!==c)throw new w(c);a=a()}else{var d=My(W(),b);d^=d>>>16|0;c=c.eb.a[d&(-1+c.eb.a.length|0)];c=null===c?null:Ny(c,b,d);a=null===c?a():c.Ah}return a}function Naa(a){var b=a.bJ.t();a.Ip.oh(b);return b}function Uo(a,b){b=dE(a,b);a.Ip.oh(b);return b} function wl(a,b){if(aq(Al(),b))var c=b;else if(eq().qF.L(b))c=b+"$";else{eE();c=String.fromCharCode(39);var d=String.fromCharCode(36);c=b.split(c).join(d)}c=dE(a,c);vp(a,new ko(b,c,new L(!1),!1,!1));return c}function Hm(a){var b=fE(new iE,(t(),new L(a)));a=a.PA;for(var c=a.eb.a.length,d=0;du())).De(c.bc(b),new fn((g,h)=>Pca(a,h,g,d)))};function uE(){}uE.prototype=new p;uE.prototype.constructor=uE; function Rca(a,b){a=b.m();a=new xo(a,new y(e=>{if(null!==e){var g=e.h();e=e.j().m();return new Ef(e,new y(h=>G(new H,h,g)))}throw new w(e);}));for(b=nf();a.s();){var c=a.t();b=G(new H,b,c);a:{c=b.y;var d=b.w;if(null!==d&&(d=new L(d),!d.b())){b=c.IC(d.k.h(),new y((e=>g=>{if(R()===g)return g=O().c,new L(new z(e,g));if(g instanceof L)return new L(new z(e,g.k));throw new w(g);})(d.k.j())));break a}throw new w(b);}}return b} function Sca(a,b){var c=b.zr().Ja(new y(d=>{tp();var e=u();e=Qca(a,d,Aq(0,e),b).Ek(d);Od();e=Pd(u(),e);return G(new H,d,e)}));op();return c.Ti()}function vE(a,b,c,d,e){c.n(new U(()=>"\u2022 "+d));b.b()?c.n(new U(()=>" + \x3cEmpty\x3e")):b.og(new fn((g,h)=>{c.n(new U(()=>" + "+g+" "+e+" "+(h.b()?"{}":ze(h,"{ ",", "," }"))))}))}uE.prototype.$classData=q({R_:0},!1,"mlscript.ucs.Desugarer$",{R_:1,g:1});var wE;function xE(){wE||(wE=new uE);return wE}function yE(){}yE.prototype=new p; yE.prototype.constructor=yE;function zE(){}zE.prototype=yE.prototype;function AE(a,b){if(null===a)throw le();if(a.sb)return a.vb;b=b.ni.m();b=new Ef(b,new y(c=>{if(null!==c){var d=c.Sj,e=c.gr;if(null!==d)return"[binding "+d.x+" \x3d "+Zz(e,!1)+"]"}throw new w(c);}));Od();return me(a,Pd(u(),b))} var Tca=function BE(a,b,c,d){for(;;){var g=new $e,h=ut(Q()," ",c),k=b;if(k instanceof CE){var l=k,m=l.hr;k=l.Dk;l=l.Fh;for(b=g.sb?g.vb:AE(g,b);!b.b();)g=b.e(),d.$(""+h+g),b=b.f();b=h+("if \u00ab"+Zz(m,!1))+"\u00bb";d.$(b);BE(a,k,1+c|0,d);d.$(h+"else");h=1+c|0;b=l;c=h}else{if(k instanceof DE){l=k;k=l.mo;m=l.mh;l=l.nj;for(b=g.sb?g.vb:AE(g,b);!b.b();)g=b.e(),d.$(""+h+g),b=b.f();b=""+h;var n=k;g=Zz(n.nh,!1);n=n.no;a:if(t().d===n)n="";else{if(n instanceof L){var r=n.k;if(null!==r){n=" as "+r.x;break a}}throw new w(n); }d.$(b+("\u00ab"+g+"\u00bb"+n)+" match");b=c;for(m=m.m();m.s();)a:if(n=m.t(),n instanceof EE)g=n.Jp,n=h+" case "+Zz(n.ir,!1)+" \x3d\x3e",d.$(n),BE(a,g,1+b|0,d);else{if(n instanceof FE&&(g=n,r=g.Tj,g=g.pl,null!==r)){var v=new L(r);if(!v.b()&&(r=v.k.h(),v=v.k.j(),null!==r)){d.$(h+" case "+r.x+" \x3d\x3e");for(n=v.m();n.s();)b:{if(v=n.t(),null!==v){r=v.h();var x=v.j();if(null!==x){v=x.x;x=GE(k);r=h+" [pattern "+v+" \x3d "+Zz(x,!1)+"."+r+"]";d.$(r);break b}}throw new w(v);}BE(a,g,2+b|0,d);break a}}throw new w(n); }l.b()||(k=l.o(),d.$(h+" default"),BE(a,k,2+c|0,d))}else if(k instanceof HE){a=k.ot;for(c=g.sb?g.vb:AE(g,b);!c.b();)k=c.e(),d.$(""+h+k),c=c.f();h=h+"\u00ab"+Zz(a,!1)+"\u00bb";d.$(h)}else if(IE()===k){for(a=g.sb?g.vb:AE(g,b);!a.b();)c=a.e(),d.$(""+h+c),a=a.f();d.$(h+"\x3cmissing case\x3e")}else throw new w(k);break}}}; function JE(a,b){if(null===a)throw le();if(a.sb)return a.vb;b=b.lf;if(b.b())b=Od().jC;else{Od();var c=new fp;Od();for(var d=new fp,e=b.m();e.s();){var g=e.t(),h=g;a:{if(null!==h){var k=h.fr;if(KE()===k){h=!1;break a}}if(null!==h)h=!0;else throw new w(h);}wp(h?c:d,g)}c=G(new H,c.ha(),d.ha());d=c.y;u().i(d)?b=G(new H,u(),b):(d=c.w,b=u().i(d)?G(new H,b,u()):c)}if(null===b)throw new w(b);return me(a,G(new H,b.h(),b.j()))} function LE(a,b,c){if(a.sb)a=a.vb;else{if(null===a)throw le();a=a.sb?a.vb:me(a,(b.sb?b.vb:JE(b,c)).h())}return a}function ME(a,b,c){if(a.sb)a=a.vb;else{if(null===a)throw le();a=a.sb?a.vb:me(a,(b.sb?b.vb:JE(b,c)).j())}return a} var cF=function Uca(a,b,c){if(null!==b){var e=b.Wi,g=b.mj;if(e instanceof z){b=e.z;var h=new $e,k=new $e,l=new $e;c=Uca(a,new NE(e.p,g),c);if(b instanceof OE)return a=b.$x,g=b.Zx,e=PE(),l=ME(l,h,b),l=new EE(g,QE(c,l)),c=g.A(),l.pt.zc(c),l=RE(e,J(new K,[l])),l=new DE(a,l,t().d),b=LE(k,h,b),QE(l,b);if(b instanceof SE)return a=b.Wx,e=TE(PE()),t(),l=ME(l,h,b),l=QE(c,l),l=new DE(a,e,new L(l)),b=LE(k,h,b),QE(l,b);if(b instanceof UE){a=b.Yx;g=b.Xx;var m=b.WA;e=PE();m=PE().vl(m);g=G(new H,g,m);l=ME(l,h,b); l=[VE(new FE(g,QE(c,l)),b.Cv())];l=RE(e,J(new K,l));l=new DE(a,l,t().d);b=LE(k,h,b);return QE(l,b)}if(b instanceof WE)return a=b.ev,m=b.dv,g=b.ay,e=PE(),m=new vl("Tuple#"+m),g=PE().vl(g),g=G(new H,m,g),l=ME(l,h,b),l=[VE(new FE(g,QE(c,l)),b.Cv())],l=RE(e,J(new K,l)),l=new DE(a,l,t().d),b=LE(k,h,b),QE(l,b);if(b instanceof XE)return c=new CE(b.Vx,c,IE()),k=LE(k,h,b),k=QE(c,k),b=ME(l,h,b),QE(k,b);if(b instanceof YE)return a=b.bv,e=b.cv,b.Ux?(ZE||(ZE=new $E),g=ZE):g=aF(),k=LE(k,h,b),k=QE(c,k),c=new bF(g, !1,a,e),a=O().c,k=QE(k,new z(c,a)),b=ME(l,h,b),QE(k,b);throw new w(b);}}if(null!==b&&(k=b.Wi,h=b.mj,l=O().c,null===l?null===k:l.i(k)))return b=new HE(c),QE(b,h);throw new w(b);};function dF(){}dF.prototype=new p;dF.prototype.constructor=dF;function eF(a,b){var c=TE(PE());Tca(a,b,0,c);return c.ha()}dF.prototype.$classData=q({X_:0},!1,"mlscript.ucs.MutCaseOf$",{X_:1,g:1});var fF;function gF(){fF||(fF=new dF);return fF}function hF(){this.pt=null}hF.prototype=new p;hF.prototype.constructor=hF; function iF(){}iF.prototype=hF.prototype;function VE(a,b){a.pt.zc(b);return a}function jF(){}jF.prototype=new p;jF.prototype.constructor=jF;function kF(){}kF.prototype=jF.prototype;function lF(a,b){for(b=b.m();b.s();){var c=b.t();a.fv.L(c)||(a.fv.$(c),a.ni.$(c))}}function QE(a,b){lF(a,b);return a} var mF=function Vca(a,b,c,d){for(;;){var g=b;b=O().c;if(null===b?null===g:b.i(g))return d;if(g instanceof z){var h=g;b=h.z;h=h.p;if(null!==b){g=b.ZA;var k=b.Sj;b=b.gr;if(c.L(k)){b=h;continue}else return new Rl(g,k,b,Vca(a,h,c.bc(k),d))}}throw new w(g);}};function nF(){}nF.prototype=new p;nF.prototype.constructor=nF;function oF(a){var b=t().d;a=new sm(tm().Cg,a);b=G(new H,b,a);a=O().c;return new Gl(new z(b,a))} function pF(a,b,c,d,e){return e?(eu(),new Pl(c,fu(0,J(new K,[b,d])))):new Pl(new Pl(c,oF(b)),oF(d))} function qF(a,b){var c=!1,d=null;if(b instanceof Pl){c=!0;d=b;var e=d.Za,g=d.Qb;if(e instanceof Pl){var h=e.Za;e=e.Qb;if(h instanceof vl&&"and"===h.x&&e instanceof Gl&&(e=e.Ra,e instanceof z&&(h=e.z,e=e.p,null!==h&&(h=new L(h),!h.b()&&(h=h.k.j(),null!==h))))){h=h.ya;var k=O().c;if((null===k?null===e:k.i(e))&&g instanceof Gl&&(g=g.Ra,g instanceof z&&(e=g.z,g=g.p,null!==e&&(e=new L(e),!e.b()&&(e=e.k.j(),null!==e&&(e=e.ya,k=O().c,null===k?null===g:k.i(g)))))))return a=qF(a,h),Xq(a,e)}}}if(c&&(c=d.Za, d=d.Qb,c instanceof vl&&"and"===c.x&&null!==d&&(d=mz(eu(),d),!d.b()&&null!==d.o()&&0===d.o().ab(2))))return b=d.o(),b=eB(b,0),d=d.o(),d=eB(d,1),a=qF(a,b),Xq(a,d);a=O().c;return new z(b,a)} function rF(a,b,c){var d=!1,e=null;if(b instanceof Pl){d=!0;e=b;var g=e.Za,h=e.Qb;if(g instanceof Pl){var k=g.Za;g=g.Qb;if(k instanceof vl&&"and"===k.x&&g instanceof Gl){var l=g.Ra;if(l instanceof z&&(g=l.z,l=l.p,null!==g&&(g=new L(g),!g.b()&&(g=g.k.j(),null!==g)))){g=g.ya;var m=O().c;if((null===m?null===l:m.i(l))&&h instanceof Gl&&(l=h.Ra,l instanceof z&&(h=l.z,l=l.p,null!==h&&(h=new L(h),!h.b()&&(h=h.k.j(),null!==h&&(h=h.ya,m=O().c,null===m?null===l:m.i(l))))))){a=rF(a,g,c);if(null!==a&&(b=a.h(), e=a.j(),t().d===e))return G(new H,b,(t(),new L(h)));if(null!==a&&(b=a.h(),e=a.j(),e instanceof L))return a=e.k,t(),c=pF(0,a,k,h,c),G(new H,b,new L(c));throw new w(a);}}}}}if(d&&(k=e.Za,e=e.Qb,k instanceof vl&&"and"===k.x&&null!==e&&(e=mz(eu(),e),!e.b()&&null!==e.o()&&0===e.o().ab(2)))){b=e.o();d=eB(b,0);b=e.o();b=eB(b,1);a=rF(a,d,c);if(null!==a&&(e=a.h(),d=a.j(),t().d===d))return G(new H,e,(t(),new L(b)));if(null!==a&&(e=a.h(),d=a.j(),d instanceof L))return a=d.k,t(),c=pF(0,a,k,b,c),G(new H,e,new L(c)); throw new w(a);}return G(new H,b,t().d)}nF.prototype.$classData=q({j0:0},!1,"mlscript.ucs.helpers$",{j0:1,g:1});var sF;function tF(){sF||(sF=new nF);return sF}q({k0:0},!1,"mlscript.utils.algorithms$",{k0:1,g:1});function uF(){}uF.prototype=new p;uF.prototype.constructor=uF; function Wca(a,b,c,d){a=G(new H,b,c);c=a.y;b=a.w;if(c instanceof L&&(c=c.k,b instanceof L))return a=d.ba(c,b.k),it().n(a);d=a.y;if(d instanceof L)return d;d=a.w;if(d instanceof L)return d;d=a.y;b=a.w;if(R()===d&&R()===b)return R();throw new w(a);}function vF(){wF();throw RD("please add this rare case to test files");}function xm(a){wF();var b=new xF;yF(b,"Internal Error: "+a,null,!0);throw b;}uF.prototype.$classData=q({m0:0},!1,"mlscript.utils.package$",{m0:1,g:1});var zF; function wF(){zF||(zF=new uF);return zF}function E(a){this.gv=a}E.prototype=new p;E.prototype.constructor=E;function Pe(a,b){a=a.gv;return ml(nl(),a,b)}function Nm(a,b){a=a.gv;return!ml(nl(),a,b)}function Ot(a,b){return Object.is(a.gv,b)}function dx(a,b){return!Object.is(a.gv,b)}function Lf(a,b){a=a.gv;return ml(nl(),a,b)}function Xca(a,b){return!!b.n(a.gv)}function Yca(a,b){return b.qo(new y(c=>Pe(new E(c),a.gv)))}E.prototype.$classData=q({n0:0},!1,"mlscript.utils.package$AnyOps",{n0:1,g:1}); function AF(){}AF.prototype=new p;AF.prototype.constructor=AF;function BF(a,b){a=b.m();return a.s()?ze(a,"[",", ","]"):""}function CF(a,b){a=b.m();a=new Ef(a,new y(c=>{ve();c=nb(c);var d=ue(ve(),c);c=new DF;for(d=sl(d).m();d.s();){var e=d.t();EF(c,"\t"+e)}return"\n"+ze(c.lk,"","\n","")}));return ze(a,"","","")}AF.prototype.$classData=q({o0:0},!1,"mlscript.utils.package$IterableOps$",{o0:1,g:1});var FF;function GF(){FF||(FF=new AF);return FF}function yt(a){this.mJ=a}yt.prototype=new p; yt.prototype.constructor=yt;function HF(a){var b=Zca(),c=a.mJ;a=O().c;for(c=Km(c);!c.b();){var d=c.e();a=a.b()||!b.ba(a.e(),d)?new z(d,a):a;c=c.f()}return a}function Zca(){return new fn((a,b)=>Pe(new E(a),b))}function hba(a){if(a.mJ.b())return t().d;t();a=a.mJ.f();return new L(a)}yt.prototype.$classData=q({p0:0},!1,"mlscript.utils.package$ListHelpers",{p0:1,g:1});function zB(a){this.en=a}zB.prototype=new p;zB.prototype.constructor=zB; zB.prototype.$classData=q({q0:0},!1,"mlscript.utils.package$MutSetHelpers",{q0:1,g:1});function Dx(a){this.wF=a}Dx.prototype=new p;Dx.prototype.constructor=Dx;Dx.prototype.$classData=q({r0:0},!1,"mlscript.utils.package$OptionHelpers",{r0:1,g:1});function IF(){}IF.prototype=new p;IF.prototype.constructor=IF;function JF(a,b,c){a=$ca(lv(),b,c);Od();return Pd(u(),a)}function ry(a,b,c){a=KF(lv(),b,c);Od();return Pd(u(),a)} function $ca(a,b,c){a=b.m();return new Ef(a,new y(d=>{var e=c.n(d.h());return G(new H,e,d.j())}))}function KF(a,b,c){a=b.m();return new Ef(a,new y(d=>{var e=d.h();d=c.n(d.j());return G(new H,e,d)}))}function dy(a,b){a=b.m();return new Ef(a,new y(c=>c.h()))}function mv(a,b){a=b.m();return new Ef(a,new y(c=>c.j()))}IF.prototype.$classData=q({s0:0},!1,"mlscript.utils.package$PairIterableOps$",{s0:1,g:1});var LF;function lv(){LF||(LF=new IF);return LF}function gq(a){return MF(NF(new OF,a))} q({t0:0},!1,"mlscript.utils.package$SetObjectHelpers",{t0:1,g:1});function $v(a,b){return sv().Dv(b).$(a).Kb()}q({u0:0},!1,"mlscript.utils.package$SortedMapObjectHelpers",{u0:1,g:1});function vv(a,b){return uv().GB(b).$(a).Kb()}q({v0:0},!1,"mlscript.utils.package$SortedSetObjectHelpers",{v0:1,g:1});function PF(){}PF.prototype=new p;PF.prototype.constructor=PF; function ue(a,b){QF();a=[ce()];a=J(new K,a);a=RF(a);for(var c=b.length,d=0;dnew L(a))}YF.prototype.$classData=q({x0:0},!1,"mlscript.utils.shorthands$",{x0:1,g:1});var ZF;function t(){ZF||(ZF=new YF);return ZF} function ma(a,b){this.W=a;this.Y=b}ma.prototype=new p;ma.prototype.constructor=ma;f=ma.prototype;f.i=function(a){return a instanceof ma?this.W===a.W&&this.Y===a.Y:!1};f.B=function(){return this.W^this.Y};f.u=function(){return bG(xa(),this.W,this.Y)};f.jB=function(){return this.W<<24>>24};f.EC=function(){return this.W<<16>>16};f.Zi=function(){return this.W};f.xl=function(){return Za(this)}; f.pv=function(){xa();var a=this.W,b=this.Y;if(0>b){var c=-a|0;a=0!==a?~b:-b|0}else c=a,a=b;c=4294967296*+(a>>>0)+ +((0===(-2097152&a)||0===(65535&c)?c:32768|-65536&c)>>>0);return Math.fround(0>b?-c:c)};f.Lp=function(){return cG(xa(),this.W,this.Y)};f.sl=function(a){return ua(xa(),this.W,this.Y,a.W,a.Y)};f.$classData=q({B0:0},!1,"org.scalajs.linker.runtime.RuntimeLong",{B0:1,g:1});function dG(a,b,c){return 0===(-2097152&c)?""+(4294967296*c+ +(b>>>0)):eG(a,b,c,1E9,0,2)} function fG(a,b,c,d,e){return 0===(-2097152&c)?0===(-2097152&e)?(c=(4294967296*c+ +(b>>>0))/(4294967296*e+ +(d>>>0)),a.Qc=c/4294967296|0,c|0):a.Qc=0:0===e&&0===(d&(-1+d|0))?(d=31-Math.clz32(d)|0,a.Qc=c>>>d|0,b>>>d|0|c<<1<<(31-d|0)):0===d&&0===(e&(-1+e|0))?(b=31-Math.clz32(e)|0,a.Qc=0,c>>>b|0):eG(a,b,c,d,e,0)|0} function eG(a,b,c,d,e,g){var h=(0!==e?Math.clz32(e):32+Math.clz32(d)|0)-(0!==c?Math.clz32(c):32+Math.clz32(b)|0)|0,k=h,l=0===(32&k)?d<>>1|0)>>>(31-k|0)|0|e<=(-2147483648^x):(-2147483648^v)>=(-2147483648^A))r=n,v=m,n=k-l|0,r=(-2147483648^n)>(-2147483648^k)?-1+(r-v|0)|0:r-v|0,k=n,n=r,32>h?c|=1<>>1|0;l=l>>>1|0|m<<31;m=r}h=n;if(h===e?(-2147483648^k)>=(-2147483648^ d):(-2147483648^h)>=(-2147483648^e))h=4294967296*n+ +(k>>>0),d=4294967296*e+ +(d>>>0),1!==g&&(m=h/d,e=m/4294967296|0,l=c,c=m=l+(m|0)|0,b=(-2147483648^m)<(-2147483648^l)?1+(b+e|0)|0:b+e|0),0!==g&&(d=h%d,k=d|0,n=d/4294967296|0);if(0===g)return a.Qc=b,c;if(1===g)return a.Qc=n,k;a=""+k;return""+(4294967296*b+ +(c>>>0))+"000000000".substring(a.length)+a}function gG(){this.Qc=0}gG.prototype=new p;gG.prototype.constructor=gG; function bG(a,b,c){return c===b>>31?""+b:0>c?"-"+dG(a,-b|0,0!==b?~c:-c|0):dG(a,b,c)}function cG(a,b,c){return 0>c?-(4294967296*+((0!==b?~c:-c|0)>>>0)+ +((-b|0)>>>0)):4294967296*c+ +(b>>>0)}function ua(a,b,c,d,e){return c===e?b===d?0:(-2147483648^b)<(-2147483648^d)?-1:1:c>31){if(e===d>>31){if(-2147483648===b&&-1===d)return a.Qc=0,-2147483648;c=pb(b,d);a.Qc=c>>31;return c}return-2147483648===b&&-2147483648===d&&0===e?a.Qc=-1:a.Qc=0}if(0>c){var g=-b|0;b=0!==b?~c:-c|0}else g=b,b=c;if(0>e){var h=-d|0;d=0!==d?~e:-e|0}else h=d,d=e;g=fG(a,g,b,h,d);if(0<=(c^e))return g;c=a.Qc;a.Qc=0!==g?~c:-c|0;return-g|0} function Wh(a,b,c,d,e){if(0===(d|e))throw new qb("/ by zero");return 0===c?0===e?(a.Qc=0,0===d?pb(0,0):+(b>>>0)/+(d>>>0)|0):a.Qc=0:fG(a,b,c,d,e)} function Ri(a,b,c,d,e){if(0===(d|e))throw new qb("/ by zero");if(c===b>>31){if(e===d>>31)return-1!==d?(c=Bb(b,d),a.Qc=c>>31,c):a.Qc=0;if(-2147483648===b&&-2147483648===d&&0===e)return a.Qc=0;a.Qc=c;return b}if(0>c)var g=-b|0,h=0!==b?~c:-c|0;else g=b,h=c;0>e?(b=-d|0,d=0!==d?~e:-e|0):(b=d,d=e);0===(-2097152&h)?0===(-2097152&d)?(b=(4294967296*h+ +(g>>>0))%(4294967296*d+ +(b>>>0)),a.Qc=b/4294967296|0,b|=0):(a.Qc=h,b=g):0===d&&0===(b&(-1+b|0))?(a.Qc=0,b=g&(-1+b|0)):0===b&&0===(d&(-1+d|0))?(a.Qc=h&(-1+ d|0),b=g):b=eG(a,g,h,b,d,1)|0;return 0>c?(c=a.Qc,a.Qc=0!==b?~c:-c|0,-b|0):b}gG.prototype.$classData=q({C0:0},!1,"org.scalajs.linker.runtime.RuntimeLong$",{C0:1,g:1});var hG;function xa(){hG||(hG=new gG);return hG}function iG(){this.EK=this.KB=null;jG=this;this.KB=new Xc(0);this.EK=new zc(0)}iG.prototype=new p;iG.prototype.constructor=iG;iG.prototype.$classData=q({X2:0},!1,"scala.Array$EmptyArrays$",{X2:1,g:1});var jG;function kG(){jG||(jG=new iG);return jG}function lG(){}lG.prototype=new p; lG.prototype.constructor=lG;function mG(){}mG.prototype=lG.prototype;function dD(a,b){this.lR=null;this.e3=b;if(null===a)throw null;this.lR=a}dD.prototype=new p;dD.prototype.constructor=dD;dD.prototype.$classData=q({d3:0},!1,"scala.Option$WithFilter",{d3:1,g:1});function nG(){this.nR=this.Hv=null;oG=this;this.Hv=new y(()=>pG().Hv);this.nR=new qG}nG.prototype=new p;nG.prototype.constructor=nG;function rG(a,b){return a.Hv===b}nG.prototype.$classData=q({f3:0},!1,"scala.PartialFunction$",{f3:1,g:1}); var oG;function pG(){oG||(oG=new nG);return oG}function sG(){}sG.prototype=new p;sG.prototype.constructor=sG;function cda(a,b){up(tp(),b);return a}sG.prototype.$classData=q({m3:0},!1,"scala.Predef$Ensuring$",{m3:1,g:1});var tG;function uG(a){this.oR=null;if(null===a)throw null;this.oR=a}uG.prototype=new p;uG.prototype.constructor=uG;function dda(a,b){return eda(Qq(),a.oR.Jv,b)}uG.prototype.$classData=q({t3:0},!1,"scala.StringContext$s$",{t3:1,g:1});function vG(){}vG.prototype=new p; vG.prototype.constructor=vG; function ns(a,b,c,d){a=0a){if(b instanceof zc)return Jj(Pj(),b,a,d);if(b instanceof Xc){Pj();if(a>d)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=dd)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=dd)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=dd)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=dd)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=dd)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=dd)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=d d)throw Kj(a+" \x3e "+d);d=d-a|0;c=b.a.length-a|0;c=d=c)return KG(xG(),a);if(a instanceof zc)return c=zj(Pj(),a,c),hj(Pj(),c,b),c;if(a instanceof Xc){if(b===Fq())return c=Fj(Pj(),a,c),Ti(Pj(),c),c}else if(a instanceof Zc){if(b===LG())return c=Gj(Pj(),a,c),$i(Pj(),c),c}else if(a instanceof Ic){if(b===MG())return c=Hj(Pj(),a,c),dj(Pj(),c),c}else if(a instanceof Pc){if(b===NG())return c=Dj(Pj(),a,c),fj(Pj(),c),c}else if(a instanceof Sc){if(b===OG())return c=Ej(Pj(),a,c),bj(Pj(),c),c}else if(a instanceof Ec&&b===PG()){c= Ij(Pj(),a,c);var d=QG();b=PG();RG(d,c,c.a.length,b);return c}300>c?(c=KG(xG(),a),RG(QG(),c,wG(xG(),c),b)):(SG(),TG(),Zf(da(jd),bg(ca(a)))?d=Yf(da(jd))?UG(a,c):Cj(Pj(),a,c,da(md(jd))):(d=new zc(c),VG(SG(),a,0,d,0,wG(xG(),a))),hj(Pj(),d,b),SG(),b=yG(zG(),bg(ca(a))),a=b.uh(),null!==a&&a===da(pd)?c=WG(c):Zf(a,bg(ca(d)))?Yf(a)?c=UG(d,c):(b=rh(th(),a,0),b=ca(b),c=Cj(Pj(),d,c,b)):(c=b.si(c),VG(SG(),d,0,c,0,wG(xG(),d))));return c} function lB(a,b,c){if(null===a)throw le();if(a instanceof zc){for(var d=wG(xG(),a),e=0;e>>14|0;a=a+(a<<4)|0;return a^(a>>>10|0)}ZG.prototype.$classData=q({W4:0},!1,"scala.collection.Hashing$",{W4:1,g:1});var aH;function bH(){aH||(aH=new ZG);return aH}function cH(a,b){for(a=a.m();a.s();)b.n(a.t())}function nw(a,b){var c=!0;for(a=a.m();c&&a.s();)c=!!b.n(a.t());return c}function ey(a,b){var c=!1;for(a=a.m();!c&&a.s();)c=!!b.n(a.t());return c} function dH(a,b){for(a=a.m();a.s();){var c=a.t();if(b.n(c))return new L(c)}return R()}function mB(a,b,c){for(a=a.m();a.s();)b=c.ba(b,a.t());return b}function Qu(a,b,c){return a.ad().De(b,new fn((d,e)=>c.ba(e,d)))}function eH(a,b){a=a.m();if(!a.s())throw nv("empty.reduceLeft");for(var c=!0,d=null;a.s();){var e=a.t();c?(d=e,c=!1):d=b.ba(d,e)}return d}function kba(a,b){if(!a.m().s())throw nv("empty.reduceRight");return a.ad().th(new fn((c,d)=>b.ba(d,c)))} function Qba(a,b){return a.b()?R():new L(a.th(b))}function hs(a){if(0<=a.Q())return a.Q();a=a.m();for(var b=0;a.s();)b=1+b|0,a.t();return b}function NB(a,b,c,d){a=a.m();var e=c,g=wG(xG(),b)-c|0;for(d=c+(d(b|0)+(c|0)|0))}function gH(a,b){if(a.b())throw nv("empty.min");return a.th(new fn((c,d)=>b.Kk(c,d)))}function Gq(a,b){return a.b()?R():new L(a.aj(b))} function hH(a,b){if(a.b())throw nv("empty.max");return a.th(new fn((c,d)=>b.Jk(c,d)))}function Jq(a,b){return a.b()?R():new L(a.$i(b))}function gda(a,b,c){if(a.b())throw nv("empty.maxBy");var d=new aw(null),e=new aw(null),g=new GA(!0);a.Ca(new y(h=>{var k=b.n(h);if(g.Am||c.Xj(k,d.rc))e.rc=h,d.rc=k,g.Am=!1}));return e.rc}function vca(a,b,c){return a.b()?R():new L(gda(a,b,c))}function ze(a,b,c,d){return a.b()?""+b+d:a.Gh(ce(),b,c,d).yf.ja} function iH(a,b,c,d,e){var g=b.yf;0!==c.length&&(g.ja=""+g.ja+c);a=a.m();if(a.s())for(c=a.t(),g.ja=""+g.ja+c;a.s();)g.ja=""+g.ja+d,c=a.t(),g.ja=""+g.ja+c;0!==e.length&&(g.ja=""+g.ja+e);return b}function kB(a,b){if(0<=a.Q())return b=b.si(a.Q()),a.Gc(b,0,2147483647),b;var c=b.uh(),d=c===da(td);b=[];for(a=a.m();a.s();){var e=a.t();b.push(d?Ea(e):null===e?c.qi.jz:e)}return md((c===da(pd)?da(la):c===da(jH)||c===da(kH)?da(jd):c).qi).iz(b)} function er(a){var b=u();for(a=a.m();a.s();){var c=a.t();b=new z(c,b)}return b}function lH(a,b){this.v5=a;this.AG=b}lH.prototype=new p;lH.prototype.constructor=lH;lH.prototype.$classData=q({u5:0},!1,"scala.collection.Iterator$ConcatIteratorCell",{u5:1,g:1});function Wq(a,b,c){this.iL=a;this.ck=b;this.dk=c}Wq.prototype=new p;Wq.prototype.constructor=Wq;function qD(a,b){return new mH(a.iL,a.ck,a.dk,b)}function Ew(a,b){b=new nH(a,b);return a.iL.Ub().Ib(b)} function Zq(a,b){b=new oH(a,b);return a.iL.Ub().Ib(b)}function pH(a,b){var c=a.ck.m();for(a=a.dk.m();c.s()&&a.s();)b.ba(c.t(),a.t())}function qH(a){var b=a.ck.Q();if(0===b)return 0;a=a.dk.Q();return 0===a?0:b=a.length)throw b=new tH,yF(b,"String index out of range: 0",null,!0),b;c.ja=""+a.substring(0,0)+hc(b)+a.substring(1);return c.ja}function uH(a,b,c){return 0<=vH(b,c)}function Of(a,b,c,d){a=0=d?"":b.substring(a,d)}function ut(a,b,c){if(0>=c)return"";a=Cu(Math.imul(b.length,c));for(var d=0;d=c.charCodeAt(e))e=1+e|0;else break;c=e{Q();return c instanceof yH?c.qT():c})).Bj(TG());return jda(zH(),a)}function Hu(a,b){if(""===b)throw AH("head of empty String");return b.charCodeAt(0)}function Xz(a,b){return""===b?R():new L(hc(b.charCodeAt(0)))}function Iu(a,b){if(""===b)throw AH("last of empty String");return b.charCodeAt(-1+b.length|0)}function ys(a,b,c){Q();a=b.length;return Of(0,b,ca)return R();var g=d.charCodeAt(e);g=DH(Pr(),g,10);if(-1===g||-214748364===a&&9===g)return R();e=1+e|0;a=Math.imul(10,a)-g|0}}function EH(){}EH.prototype=new p;EH.prototype.constructor=EH;function $D(a,b){a=b.length;if(0===a)return R();var c=b.charCodeAt(0),d=DH(Pr(),c,10);return 1===a?-1b)throw cI(a,b);if(b>(-1+a.a.length|0))throw cI(a,b);var c=new Xc(-1+a.a.length|0);a.wa(0,c,0,b);a.wa(1+b|0,c,b,-1+(a.a.length-b|0)|0);return c} function hI(a,b,c){if(0>b)throw cI(a,b);if(b>a.a.length)throw cI(a,b);var d=new Xc(1+a.a.length|0);a.wa(0,d,0,b);d.a[b]=c;a.wa(b,d,1+b|0,a.a.length-b|0);return d}var IH=q({lC:0},!1,"scala.collection.immutable.Node",{lC:1,g:1});eI.prototype.$classData=IH;function iI(){this.mC=0;jI=this;this.mC=Eb(+Math.ceil(6.4))}iI.prototype=new p;iI.prototype.constructor=iI;function kI(a,b,c){return 31&(b>>>c|0)}function lI(a,b){return 1<k?tI(b,AI(a,b.sa,c,d,e,g)):0h?yI(b,DI(a,b.ta,c-h|0,d,e)):b},GI=function tda(a,b,c){for(;;){if(null===b||0>=c)return b;if(c>=(2147483647&b.ea))return null;var e=EI(0,b.sa);if(c>e)c=-1+ (c-e|0)|0,b=b.ta;else{if(c===e)return FI(a,null,b.Wa,b.Wb,b.ta);c=tda(a,b.sa,c);return FI(a,c,b.Wa,b.Wb,b.ta)}}},HI=function uda(a,b,c){for(;;){if(null===b||0>=c)return null;if(c>=(2147483647&b.ea))return b;var e=EI(0,b.sa);if(c<=e)b=b.sa;else return c===(1+e|0)?(a=sda(a,b.sa,c,b.Wa,b.Wb),null===a||0>a.ea||(b=a.sa,null!==b&&0<=b.ea?b=!0:(b=a.ta,b=null!==b&&0<=b.ea),a=b?qI(a):a)):a=FI(a,b.sa,b.Wa,b.Wb,uda(a,b.ta,-1+(c-e|0)|0)),a}},PI=function II(a,b,c,d){if(null===b)return null;var g=d.Da(c,b.Wa); if(0>g){a=II(a,b.sa,c,d);if(a===b.sa)return b;c=b.sa;null!==c&&0>c.ea?b=JI(b,a,b.ta):a===b.sa&&0<=b.ea||(c=b.ta,b=new sI(b.Wa,b.Wb,a,b.ta,1+((null===a?0:2147483647&a.ea)+(null===c?0:2147483647&c.ea)|0)|0));return b}if(0c.ea?(c=b.sa,null!==a&&0<=a.ea?b=KI(b,c,qI(a)):null!==c&&0>c.ea?b=LI(b,MI(c),a):(null!==c&&0<=c.ea?(d=c.ta,d=null!==d&&0>d.ea):d=!1,d?b=KI(c.ta,LI(c,MI(c.sa),c.ta.sa),NI(b,c.ta.ta,a)):(OI(),b=void 0))):a===b.ta&&0<=b.ea|| (c=b.sa,b=new sI(b.Wa,b.Wb,b.sa,a,1+((null===c?0:2147483647&c.ea)+(null===a?0:2147483647&a.ea)|0)|0));return b}return vda(a,b.sa,b.ta)}; function LI(a,b,c){if(null!==b&&0<=b.ea){if(null!==c&&0<=c.ea)return KI(a,qI(b),qI(c));var d=b.sa;if(null!==d&&0<=d.ea)return uI(b,qI(b.sa),NI(a,b.ta,c));d=b.ta;return null!==d&&0<=d.ea?uI(b.ta,wI(b,b.ta.sa),NI(a,b.ta.ta,c)):NI(a,b,c)}if(null!==c&&0<=c.ea){d=c.ta;if(null!==d&&0<=d.ea)return uI(c,NI(a,b,c.sa),qI(c.ta));d=c.sa;return null!==d&&0<=d.ea?uI(c.sa,NI(a,b,c.sa.sa),NI(c,c.sa.ta,c.ta)):NI(a,b,c)}return NI(a,b,c)} function JI(a,b,c){if(null!==b&&0<=b.ea)return KI(a,qI(b),c);if(null!==c&&0>c.ea)return LI(a,b,MI(c));if(null!==c&&0<=c.ea){var d=c.sa;d=null!==d&&0>d.ea}else d=!1;if(d)return KI(c.sa,NI(a,b,c.sa.sa),LI(c,c.sa.ta,MI(c.ta)));OI()} var vda=function QI(a,b,c){return null===b?c:null===c?b:0<=b.ea?0<=c.ea?(a=QI(a,b.ta,c.sa),null!==a&&0<=a.ea?uI(a,zI(b,a.sa),xI(c,a.ta)):zI(b,xI(c,a))):zI(b,QI(a,b.ta,c)):0>c.ea?(a=QI(a,b.ta,c.sa),null!==a&&0<=a.ea?uI(a,zI(b,a.sa),xI(c,a.ta)):JI(b,b.sa,xI(c,a))):xI(c,QI(a,b,c.sa))},xda=function wda(a,b,c,d,e,g,h){if((null===b?0:0>b.ea?(-1+g|0)<<1:-1+(g<<1)|0)===(h/2|0)<<1)return BI(c,d,b,e);var l=null!==b&&0>b.ea;a=wda(a,b.ta,c,d,e,l?-1+g|0:g,h);l&&null!==a&&0<=a.ea?(c=a.ta,c=null!==c&&0<=c.ea):c= !1;return c?BI(a.Wa,a.Wb,RI(b.Wa,b.Wb,b.sa,a.sa),qI(a.ta)):rI(l,b.Wa,b.Wb,b.sa,a)},zda=function yda(a,b,c,d,e,g,h){if((null===e?0:0>e.ea?(-1+h|0)<<1:-1+(h<<1)|0)===(g/2|0)<<1)return BI(c,d,b,e);var l=null!==e&&0>e.ea;a=yda(a,b,c,d,e.sa,g,l?-1+h|0:h);l&&null!==a&&0<=a.ea?(b=a.sa,b=null!==b&&0<=b.ea):b=!1;return b?BI(a.Wa,a.Wb,qI(a.sa),RI(e.Wa,e.Wb,a.ta,e.ta)):rI(l,e.Wa,e.Wb,a,e.ta)},TI=function SI(a,b,c,d){if(null===b)return new Gp(null,null,null,c);var g=d.Da(c,b.Wa);if(0===g)return new Gp(b.sa,b, b.ta,b.Wa);if(0>g){c=SI(a,b.sa,c,d);if(null===c)throw new w(c);d=c.Xi;return new Gp(c.Uj,c.oj,FI(a,c.oi,b.Wa,b.Wb,b.ta),d)}c=SI(a,b.ta,c,d);if(null===c)throw new w(c);d=c.oj;g=c.oi;var h=c.Xi;return new Gp(FI(a,b.sa,b.Wa,b.Wb,c.Uj),d,g,h)},Bda=function Ada(a,b){if(null===b.ta)return new tl(b.sa,b.Wa,b.Wb);var d=Ada(a,b.ta);if(null===d)throw new w(d);var e=d.hb,g=d.Rd;return new tl(FI(a,b.sa,b.Wa,b.Wb,d.kc),e,g)},Cda=function UI(a,b,c,d){if(null===b||b===c)return c;if(null===c)return b;var g=TI(a, b,c.Wa,d);if(null===g)throw new w(g);var h=g.oi;b=g.Xi;g=UI(a,g.Uj,c.sa,d);d=UI(a,h,c.ta,d);return FI(a,g,b,c.Wb,d)},XI=function VI(a,b,c,d){if(null===b||null===c)return b;if(b===c)return null;var g=TI(a,b,c.Wa,d);if(null===g)throw new w(g);b=g.oi;g=VI(a,g.Uj,c.sa,d);c=VI(a,b,c.ta,d);return WI(a,g,c)},Dda=function YI(a,b,c,d,e){switch(c){case 0:return null;case 1:return rI(b!==d||1===b,e.t(),null,null,null);default:var h=(-1+c|0)/2|0,k=YI(a,1+b|0,h,d,e),l=e.t();a=YI(a,1+b|0,(-1+c|0)-h|0,d,e);return RI(l, null,k,a)}},Eda=function ZI(a,b,c,d,e){switch(c){case 0:return null;case 1:var h=d.t();if(null===h)throw new w(h);return rI(b!==e||1===b,h.h(),h.j(),null,null);default:var k=(-1+c|0)/2|0;h=ZI(a,1+b|0,k,d,e);var l=d.t();if(null===l)throw new w(l);var m=l.h();l=l.j();b=ZI(a,1+b|0,(-1+c|0)-k|0,d,e);return RI(m,l,h,b)}},Fda=function $I(a,b,c){var e=b.Wa,g=b.Wb,h=b.sa,k=b.ta,l=null===h?null:$I(a,h,c),m=!!c.ba(e,g);c=null===k?null:$I(a,k,c);return m?l===h&&c===k?b:FI(a,l,e,g,c):WI(a,l,c)}; function aJ(a,b){if(null===a)throw le();return a.sb?a.vb:me(a,new bJ(b))}function cJ(a){for(var b=0;;){if(null===a)return 1+b|0;b=0>a.ea?1+b|0:b;a=a.sa}}function dJ(){this.AS=null;eJ=this;this.AS=G(new H,null,null)}dJ.prototype=new p;dJ.prototype.constructor=dJ;function fJ(a,b,c,d){for(;;){if(null===b)return null;a=d.Da(c,b.Wa);if(0>a)b=b.sa;else if(0h?(a=xda(a,b,c,d,e,g,null===e?0:0>e.ea?(-1+h|0)<<1:-1+(h<<1)|0),null!==a&&0<=a.ea?(b=a.ta,b=null!==b&&0<=b.ea):b=!1,b?qI(a):a):h>g?(a=zda(a,b,c,d,e,null===b?0:0>b.ea?(-1+g|0)<<1:-1+(g<<1)|0,h),null!==a&&0<=a.ea?(b=a.sa,b=null!==b&&0<=b.ea):b=!1,b?qI(a):a):rI(null!==b&&0<=b.ea||null!==e&&0<=e.ea,c,d,b,e)}function WI(a,b,c){if(null===b)return c;if(null===c)return b;b=Bda(a,b);if(null===b)throw new w(b);return FI(a,b.kc,b.hb,b.Rd,c)} dJ.prototype.$classData=q({a8:0},!1,"scala.collection.immutable.RedBlackTree$",{a8:1,g:1});var eJ;function nJ(){eJ||(eJ=new dJ);return eJ}function oJ(){this.Uk=null}oJ.prototype=new p;oJ.prototype.constructor=oJ;function pJ(){}pJ.prototype=oJ.prototype;function qJ(a){return null===a?a:0===(2147483647&a.ea)?rJ(sJ(a)):qI(a)} function tJ(a,b){if(0<=b.ea){var c=b.sa,d=b.ta;if(nJ(),null!==c&&0<=c.ea)return c=sJ(c),d=uJ(a,d),vJ(b,c,d);if(nJ(),null!==d&&0<=d.ea)return c=d.ta,b=wJ(b,d.sa),a=uJ(a,c),vJ(d,b,a)}a.sa===b?d=a:0===(2147483647&a.ea)?(a.sa=b,d=a):d=new sI(a.Wa,a.Wb,b,a.ta,-2147483648&a.ea);return d} function xJ(a,b){if(0<=b.ea){var c=b.sa;if(nJ(),null!==c&&0<=c.ea){var d=wJ(a,c.sa);b=uJ(b,c.ta);return vJ(c,d,b)}d=b.ta;if(nJ(),null!==d&&0<=d.ea)return c=wJ(a,c),d=sJ(d),vJ(b,c,d)}a.ta===b?b=a:0===(2147483647&a.ea)?(a.ta=b,b=a):b=new sI(a.Wa,a.Wb,a.sa,b,-2147483648&a.ea);return b}function sI(a,b,c,d,e){this.Wa=a;this.Wb=b;this.sa=c;this.ta=d;this.ea=e}sI.prototype=new p;sI.prototype.constructor=sI; sI.prototype.u=function(){return(0<=this.ea?"RedTree":"BlackTree")+"("+this.Wa+", "+this.Wb+", "+this.sa+", "+this.ta+")"};function rJ(a){if(0===(2147483647&a.ea)){var b=1;null!==a.sa&&(rJ(a.sa),b=b+(2147483647&a.sa.ea)|0);null!==a.ta&&(rJ(a.ta),b=b+(2147483647&a.ta.ea)|0);a.ea|=b}return a}function sJ(a){return 0>a.ea?a:0===(2147483647&a.ea)?(a.ea=-2147483648,a):new sI(a.Wa,a.Wb,a.sa,a.ta,-2147483648)} function yJ(a,b){return Object.is(b,a.Wb)?a:0===(2147483647&a.ea)?(a.Wb=b,a):new sI(a.Wa,b,a.sa,a.ta,-2147483648&a.ea)}function vJ(a,b,c){return a.sa===b&&a.ta===c?a:0===(2147483647&a.ea)?(a.sa=b,a.ta=c,a):new sI(a.Wa,a.Wb,b,c,-2147483648&a.ea)}function uJ(a,b){return a.sa===b&&0>a.ea?a:0===(2147483647&a.ea)?(a.ea=-2147483648,a.sa=b,a):new sI(a.Wa,a.Wb,b,a.ta,-2147483648)} function wJ(a,b){return a.ta===b&&0>a.ea?a:0===(2147483647&a.ea)?(a.ea=-2147483648,a.ta=b,a):new sI(a.Wa,a.Wb,a.sa,b,-2147483648)}function qI(a){return 0>a.ea?a:new sI(a.Wa,a.Wb,a.sa,a.ta,-2147483648^a.ea)}function MI(a){return 0<=a.ea?a:new sI(a.Wa,a.Wb,a.sa,a.ta,-2147483648^a.ea)}function CI(a,b){return Object.is(b,a.Wb)?a:new sI(a.Wa,b,a.sa,a.ta,a.ea)} function xI(a,b){if(b===a.sa)return a;var c=a.ta;return new sI(a.Wa,a.Wb,b,a.ta,-2147483648&a.ea|1+((null===b?0:2147483647&b.ea)+(null===c?0:2147483647&c.ea)|0)|0)}function zI(a,b){if(b===a.ta)return a;var c=a.sa;return new sI(a.Wa,a.Wb,a.sa,b,-2147483648&a.ea|1+((null===c?0:2147483647&c.ea)+(null===b?0:2147483647&b.ea)|0)|0)}function vI(a,b){if(b===a.sa&&0>a.ea)return a;var c=a.ta;return new sI(a.Wa,a.Wb,b,a.ta,1+((null===b?0:2147483647&b.ea)+(null===c?0:2147483647&c.ea)|0)|-2147483648)} function wI(a,b){if(b===a.ta&&0>a.ea)return a;var c=a.sa;return new sI(a.Wa,a.Wb,a.sa,b,1+((null===c?0:2147483647&c.ea)+(null===b?0:2147483647&b.ea)|0)|-2147483648)}function uI(a,b,c){return b===a.sa&&c===a.ta?a:new sI(a.Wa,a.Wb,b,c,-2147483648&a.ea|1+((null===b?0:2147483647&b.ea)+(null===c?0:2147483647&c.ea)|0)|0)}function KI(a,b,c){return b===a.sa&&c===a.ta&&0<=a.ea?a:new sI(a.Wa,a.Wb,b,c,1+((null===b?0:2147483647&b.ea)+(null===c?0:2147483647&c.ea)|0)|0)} function NI(a,b,c){return b===a.sa&&c===a.ta&&0>a.ea?a:new sI(a.Wa,a.Wb,b,c,1+((null===b?0:2147483647&b.ea)+(null===c?0:2147483647&c.ea)|0)|-2147483648)}var zJ=q({f8:0},!1,"scala.collection.immutable.RedBlackTree$Tree",{f8:1,g:1});sI.prototype.$classData=zJ;function bJ(a){this.h8=a;this.ZG=this.$G=null}bJ.prototype=new p;bJ.prototype.constructor=bJ; function lJ(a,b){var c=b.Wa,d=b.Wb,e=b.sa,g=b.ta,h=null,k=null,l=null,m=null;null!==e&&(lJ(a,e),h=a.$G,k=a.ZG);var n=!!a.h8.ba(c,d);null!==g&&(lJ(a,g),l=a.$G,m=a.ZG);h=n?h===e&&l===g?b:FI(nJ(),h,c,d,l):WI(nJ(),h,l);b=n?WI(nJ(),k,m):k===e&&m===g?b:FI(nJ(),k,c,d,m);a.$G=h;a.ZG=b}bJ.prototype.$classData=q({g8:0},!1,"scala.collection.immutable.RedBlackTree$partitioner$1$",{g8:1,g:1});function AJ(){this.mw=null;BJ=this;this.mw=new CJ(0,0,(IB(),new zc(0)),(rl(),new Xc(0)),0,0)}AJ.prototype=new p; AJ.prototype.constructor=AJ;AJ.prototype.$classData=q({w8:0},!1,"scala.collection.immutable.SetNode$",{w8:1,g:1});var BJ;function DJ(){BJ||(BJ=new AJ);return BJ} var Ida=function Hda(a,b,c,d,e){for(;;){if(1===b){b=c;var h=d,k=e;EJ(a,1,0===h&&k===b.a.length?b:Jj(Pj(),b,h,k))}else{h=Math.imul(5,-1+b|0);var l=1<>>h|0;h=e>>>h|0;d&=-1+l|0;e&=-1+l|0;if(0===d)if(0===e)e=c,EJ(a,b,0===k&&h===e.a.length?e:Jj(Pj(),e,k,h));else{h>k&&(d=c,EJ(a,b,0===k&&h===d.a.length?d:Jj(Pj(),d,k,h)));h=c.a[h];b=-1+b|0;c=h;d=0;continue}else if(h===k){h=c.a[k];b=-1+b|0;c=h;continue}else if(Hda(a,-1+b|0,c.a[k],d,l),0===e)h>(1+k|0)&&(e=c,k=1+k|0,EJ(a,b,0===k&&h===e.a.length?e:Jj(Pj(), e,k,h)));else{h>(1+k|0)&&(d=c,k=1+k|0,EJ(a,b,0===k&&h===d.a.length?d:Jj(Pj(),d,k,h)));h=c.a[h];b=-1+b|0;c=h;d=0;continue}}break}};function EJ(a,b,c){b<=a.Vk?b=11-b|0:(a.Vk=b,b=-1+b|0);a.lb.a[b]=c} var Kda=function Jda(a,b){if(null===a.lb.a[-1+b|0])if(b===a.Vk)a.lb.a[-1+b|0]=a.lb.a[11-b|0],a.lb.a[11-b|0]=null;else{Jda(a,1+b|0);var d=a.lb.a[-1+(1+b|0)|0];a.lb.a[-1+b|0]=d.a[0];if(1===d.a.length)a.lb.a[-1+(1+b|0)|0]=null,a.Vk===(1+b|0)&&null===a.lb.a[11-(1+b|0)|0]&&(a.Vk=b);else{var e=d.a.length;a.lb.a[-1+(1+b|0)|0]=Jj(Pj(),d,1,e)}}},Mda=function Lda(a,b){if(null===a.lb.a[11-b|0])if(b===a.Vk)a.lb.a[11-b|0]=a.lb.a[-1+b|0],a.lb.a[-1+b|0]=null;else{Lda(a,1+b|0);var d=a.lb.a[11-(1+b|0)|0];a.lb.a[11- b|0]=d.a[-1+d.a.length|0];if(1===d.a.length)a.lb.a[11-(1+b|0)|0]=null,a.Vk===(1+b|0)&&null===a.lb.a[-1+(1+b|0)|0]&&(a.Vk=b);else{var e=-1+d.a.length|0;a.lb.a[11-(1+b|0)|0]=Jj(Pj(),d,0,e)}}};function FJ(a,b){this.lb=null;this.Vk=this.az=this.Ko=0;this.KS=a;this.JS=b;this.lb=new (md(md(jd)).Ia)(11);this.Vk=this.az=this.Ko=0}FJ.prototype=new p;FJ.prototype.constructor=FJ; function GJ(a,b,c){var d=Math.imul(c.a.length,1<e&&(Ida(a,b,c,e,g),a.Ko=a.Ko+(g-e|0)|0);a.az=a.az+d|0} FJ.prototype.im=function(){if(32>=this.Ko){if(0===this.Ko)return HJ();var a=this.lb.a[0],b=this.lb.a[10];if(null!==a)if(null!==b){var c=a.a.length+b.a.length|0,d=zj(Pj(),a,c);b.wa(0,d,a.a.length,b.a.length);var e=d}else e=a;else if(null!==b)e=b;else{var g=this.lb.a[1];e=null!==g?g.a[0]:this.lb.a[9].a[0]}return new IJ(e)}Kda(this,1);Mda(this,1);var h=this.Vk;if(6>h){var k=this.lb.a[-1+this.Vk|0],l=this.lb.a[11-this.Vk|0];if(null!==k&&null!==l)if(30>=(k.a.length+l.a.length|0)){var m=this.lb,n=this.Vk, r=k.a.length+l.a.length|0,v=zj(Pj(),k,r);l.wa(0,v,k.a.length,l.a.length);m.a[-1+n|0]=v;this.lb.a[11-this.Vk|0]=null}else h=1+h|0;else 30<(null!==k?k:l).a.length&&(h=1+h|0)}var x=this.lb.a[0],A=this.lb.a[10],B=x.a.length,C=h;switch(C){case 2:var D=JJ().bd,F=this.lb.a[1];if(null!==F)var I=F;else{var M=this.lb.a[9];I=null!==M?M:D}var N=new KJ(x,B,I,A,this.Ko);break;case 3:var P=JJ().bd,T=this.lb.a[1],Y=null!==T?T:P,Z=JJ().eg,S=this.lb.a[2];if(null!==S)var ea=S;else{var ia=this.lb.a[8];ea=null!==ia?ia: Z}var X=ea,sa=JJ().bd,Ja=this.lb.a[9];N=new LJ(x,B,Y,B+(Y.a.length<<5)|0,X,null!==Ja?Ja:sa,A,this.Ko);break;case 4:var Xa=JJ().bd,Fa=this.lb.a[1],za=null!==Fa?Fa:Xa,Qa=JJ().eg,Ma=this.lb.a[2],Ga=null!==Ma?Ma:Qa,ab=JJ().jk,Hb=this.lb.a[3];if(null!==Hb)var bc=Hb;else{var yb=this.lb.a[7];bc=null!==yb?yb:ab}var tb=bc,eb=JJ().eg,kb=this.lb.a[8],Rb=null!==kb?kb:eb,Gb=JJ().bd,vb=this.lb.a[9],Tb=B+(za.a.length<<5)|0;N=new MJ(x,B,za,Tb,Ga,Tb+(Ga.a.length<<10)|0,tb,Rb,null!==vb?vb:Gb,A,this.Ko);break;case 5:var Nb= JJ().bd,ic=this.lb.a[1],Va=null!==ic?ic:Nb,cb=JJ().eg,zb=this.lb.a[2],Ub=null!==zb?zb:cb,jb=JJ().jk,db=this.lb.a[3],ub=null!==db?db:jb,Aa=JJ().du,va=this.lb.a[4];if(null!==va)var Ra=va;else{var rb=this.lb.a[6];Ra=null!==rb?rb:Aa}var xb=Ra,mc=JJ().jk,Ha=this.lb.a[7],Ka=null!==Ha?Ha:mc,Oa=JJ().eg,Na=this.lb.a[8],Da=null!==Na?Na:Oa,ta=JJ().bd,Ya=this.lb.a[9],dc=B+(Va.a.length<<5)|0,ka=dc+(Ub.a.length<<10)|0;N=new NJ(x,B,Va,dc,Ub,ka,ub,ka+(ub.a.length<<15)|0,xb,Ka,Da,null!==Ya?Ya:ta,A,this.Ko);break; case 6:var ya=JJ().bd,Sa=this.lb.a[1],xc=null!==Sa?Sa:ya,Sb=JJ().eg,uc=this.lb.a[2],Lb=null!==uc?uc:Sb,lc=JJ().jk,Xb=this.lb.a[3],ec=null!==Xb?Xb:lc,Ab=JJ().du,Ob=this.lb.a[4],fb=null!==Ob?Ob:Ab,Wa=JJ().dH,bb=this.lb.a[5];if(null!==bb)var Ia=bb;else{var Ua=this.lb.a[5];Ia=null!==Ua?Ua:Wa}var pc=Ia,sc=JJ().du,Ba=this.lb.a[6],ob=null!==Ba?Ba:sc,nc=JJ().jk,Ib=this.lb.a[7],vc=null!==Ib?Ib:nc,Vb=JJ().eg,fc=this.lb.a[8],Bc=null!==fc?fc:Vb,Pb=JJ().bd,Jb=this.lb.a[9],gc=B+(xc.a.length<<5)|0,Cb=gc+(Lb.a.length<< 10)|0,cc=Cb+(ec.a.length<<15)|0;N=new OJ(x,B,xc,gc,Lb,Cb,ec,cc,fb,cc+(fb.a.length<<20)|0,pc,ob,vc,Bc,null!==Jb?Jb:Pb,A,this.Ko);break;default:throw new w(C);}return N};FJ.prototype.u=function(){return"VectorSliceBuilder(lo\x3d"+this.KS+", hi\x3d"+this.JS+", len\x3d"+this.Ko+", pos\x3d"+this.az+", maxDim\x3d"+this.Vk+")"};FJ.prototype.$classData=q({Y8:0},!1,"scala.collection.immutable.VectorSliceBuilder",{Y8:1,g:1}); function PJ(){this.dH=this.du=this.jk=this.eg=this.bd=this.PL=null;QJ=this;this.PL=new zc(0);this.bd=new (md(md(jd)).Ia)(0);this.eg=new (md(md(md(jd))).Ia)(0);this.jk=new (md(md(md(md(jd)))).Ia)(0);this.du=new (md(md(md(md(md(jd))))).Ia)(0);this.dH=new (md(md(md(md(md(md(jd)))))).Ia)(0)}PJ.prototype=new p;PJ.prototype.constructor=PJ;function RJ(a,b,c){a=b.a.length;var d=new zc(1+a|0);b.wa(0,d,0,a);d.a[a]=c;return d} function SJ(a,b,c){a=1+b.a.length|0;b=zj(Pj(),b,a);b.a[-1+b.a.length|0]=c;return b}function TJ(a,b,c){a=new zc(1+c.a.length|0);c.wa(0,a,1,c.a.length);a.a[0]=b;return a}function UJ(a,b,c){a=bg(ca(c));var d=1+c.a.length|0;a=rh(th(),a,d);c.wa(0,a,1,c.a.length);a.a[0]=b;return a}function VJ(a,b,c,d){var e=0,g=c.a.length;if(0===b)for(;e=c.$k(32-b.a.length|0))switch(a=c.ka(),a){case 0:return null;case 1:return SJ(0,b,c.e());default:return a=b.a.length+a|0,a=zj(Pj(),b,a),c.Gc(a,b.a.length,2147483647),a}else return null;else return a=c.Q(),0c)return null;a=a.$d}}aK.prototype.Ca=function(a){for(var b=this;;)if(a.n(G(new H,b.Wk,b.Ah)),null!==b.$d)b=b.$d;else break};aK.prototype.og=function(a){for(var b=this;;)if(a.ba(b.Wk,b.Ah),null!==b.$d)b=b.$d;else break}; aK.prototype.u=function(){return"Node("+this.Wk+", "+this.Ah+", "+this.mk+") -\x3e "+this.$d};var bK=q({M9:0},!1,"scala.collection.mutable.HashMap$Node",{M9:1,g:1});aK.prototype.$classData=bK;function cK(a,b,c){this.xm=a;this.nk=b;this.yg=c}cK.prototype=new p;cK.prototype.constructor=cK;cK.prototype.Ca=function(a){for(var b=this;;)if(a.n(b.xm),null!==b.yg)b=b.yg;else break};cK.prototype.u=function(){return"Node("+this.xm+", "+this.nk+") -\x3e "+this.yg}; var dK=q({T9:0},!1,"scala.collection.mutable.HashSet$Node",{T9:1,g:1});cK.prototype.$classData=dK;function eK(){}eK.prototype=new p;eK.prototype.constructor=eK;function fK(a,b,c){a=c>>31;var d=b>>31,e=65535&c,g=c>>>16|0,h=65535&b,k=b>>>16|0,l=Math.imul(e,h);h=Math.imul(g,h);var m=Math.imul(e,k);e=l+((h+m|0)<<16)|0;l=(l>>>16|0)+m|0;b=(((Math.imul(c,d)+Math.imul(a,b)|0)+Math.imul(g,k)|0)+(l>>>16|0)|0)+(((65535&l)+h|0)>>>16|0)|0;return $h(xa(),e,b,1E3,0)} eK.prototype.$classData=q({V9:0},!1,"scala.collection.mutable.HashTable$",{V9:1,g:1});var gK;function hK(){gK||(gK=new eK);return gK}function iK(){}iK.prototype=new p;iK.prototype.constructor=iK;function jK(a,b){if(b!==a)throw new kK;}iK.prototype.$classData=q({o$:0},!1,"scala.collection.mutable.MutationTracker$",{o$:1,g:1});var lK;function mK(){lK||(lK=new iK)}function nK(a,b,c){for(;;){if(null===a)return null;var d=c.Da(b,a.Oo);if(0>d)a=a.Vc;else if(0>24&&0===(2&a.Jt)<<24>>24&&(a.uR=NK(),a.Jt=(2|a.Jt)<<24>>24);return a.uR}BK.prototype.$classData=q({T3:0},!1,"scala.package$",{T3:1,g:1});var CK;function O(){CK||(CK=new BK);return CK}function OK(){} OK.prototype=new p;OK.prototype.constructor=OK;function ml(a,b,c){if(b===c)c=!0;else if(PK(b))a:if(PK(c))c=QK(0,b,c);else{if(c instanceof ba){if("number"===typeof b){c=+b===Ea(c);break a}if(b instanceof ma){a=Za(b);b=a.Y;c=Ea(c);c=a.W===c&&b===c>>31;break a}}c=null===b?null===c:La(b,c)}else c=b instanceof ba?Rda(b,c):null===b?null===c:La(b,c);return c} function QK(a,b,c){if("number"===typeof b)return a=+b,"number"===typeof c?a===+c:c instanceof ma?(b=Za(c),c=b.W,b=b.Y,a===cG(xa(),c,b)):c instanceof yH?c.i(a):!1;if(b instanceof ma){b=Za(b);a=b.W;b=b.Y;if(c instanceof ma){c=Za(c);var d=c.Y;return a===c.W&&b===d}return"number"===typeof c?(c=+c,cG(xa(),a,b)===c):c instanceof yH?c.i(new ma(a,b)):!1}return null===b?null===c:La(b,c)} function Rda(a,b){if(b instanceof ba)return Ea(a)===Ea(b);if(PK(b)){if("number"===typeof b)return+b===Ea(a);if(b instanceof ma){b=Za(b);var c=b.Y;a=Ea(a);return b.W===a&&c===a>>31}return null===b?null===a:La(b,a)}return null===a&&null===b}OK.prototype.$classData=q({Y$:0},!1,"scala.runtime.BoxesRunTime$",{Y$:1,g:1});var RK;function nl(){RK||(RK=new OK);return RK}var jH=q({daa:0},!1,"scala.runtime.Null$",{daa:1,g:1});function SK(){}SK.prototype=new p;SK.prototype.constructor=SK; SK.prototype.$classData=q({gaa:0},!1,"scala.runtime.RichInt$",{gaa:1,g:1});var TK;function UK(){}UK.prototype=new p;UK.prototype.constructor=UK;function XG(a,b,c){if(b instanceof zc||b instanceof Xc||b instanceof ed||b instanceof Zc||b instanceof $c)return b.a[c];if(b instanceof Ic)return hc(b.a[c]);if(b instanceof Pc||b instanceof Sc||b instanceof Ec)return b.a[c];if(null===b)throw le();throw new w(b);} function fH(a,b,c,d){if(b instanceof zc)b.a[c]=d;else if(b instanceof Xc)b.a[c]=d|0;else if(b instanceof ed)b.a[c]=+d;else if(b instanceof Zc)b.a[c]=Za(d);else if(b instanceof $c)b.a[c]=Math.fround(d);else if(b instanceof Ic)b.a[c]=Ea(d);else if(b instanceof Pc)b.a[c]=d|0;else if(b instanceof Sc)b.a[c]=d|0;else if(b instanceof Ec)b.a[c]=!!d;else{if(null===b)throw le();throw new w(b);}} function wG(a,b){th();if(b instanceof zc||b instanceof Ec||b instanceof Ic||b instanceof Pc||b instanceof Sc||b instanceof Xc||b instanceof Zc||b instanceof $c||b instanceof ed)a=b.a.length;else throw Kj("argument type mismatch");return a}function KG(a,b){if(b instanceof zc||b instanceof Xc||b instanceof ed||b instanceof Zc||b instanceof $c||b instanceof Ic||b instanceof Pc||b instanceof Sc||b instanceof Ec)return b.ia();if(null===b)throw le();throw new w(b);} function VK(a){xG();return ze(new iD(a),a.H()+"(",",",")")}UK.prototype.$classData=q({haa:0},!1,"scala.runtime.ScalaRunTime$",{haa:1,g:1});var WK;function xG(){WK||(WK=new UK);return WK}function XK(){}XK.prototype=new p;XK.prototype.constructor=XK;XK.prototype.C=function(a,b){a=this.Pp(a,b);return-430675100+Math.imul(5,a<<13|a>>>19|0)|0};XK.prototype.Pp=function(a,b){b=Math.imul(-862048943,b);b=Math.imul(461845907,b<<15|b>>>17|0);return a^b}; XK.prototype.Ma=function(a,b){a^=b;a=Math.imul(-2048144789,a^(a>>>16|0));a=Math.imul(-1028477387,a^(a>>>13|0));return a^(a>>>16|0)};function YK(a,b){a=b.W;b=b.Y;return b===a>>31?a:a^b}function ZK(a,b){a=Eb(b);if(a===b)return a;a=xa();if(-0x7fffffffffffffff>b){a.Qc=-2147483648;var c=0}else if(0x7fffffffffffffff<=b)a.Qc=2147483647,c=-1;else{c=b|0;var d=b/4294967296|0;a.Qc=0>b&&0!==c?-1+d|0:d}a=a.Qc;return cG(xa(),c,a)===b?c^a:ig(mg(),b)} function My(a,b){return null===b?0:"number"===typeof b?ZK(0,+b):b instanceof ma?(a=Za(b),YK(0,new ma(a.W,a.Y))):ib(b)}function $K(a,b){throw aL(new bL,""+b);}XK.prototype.$classData=q({kaa:0},!1,"scala.runtime.Statics$",{kaa:1,g:1});var cL;function W(){cL||(cL=new XK);return cL}function dL(){}dL.prototype=new p;dL.prototype.constructor=dL;dL.prototype.$classData=q({laa:0},!1,"scala.runtime.Statics$PFMarker$",{laa:1,g:1});var eL;function fL(){eL||(eL=new dL);return eL}function gL(){}gL.prototype=new p; gL.prototype.constructor=gL;function OI(){hL||(hL=new gL);throw iL("Defect: invariance violation");}gL.prototype.$classData=q({i4:0},!1,"scala.sys.package$",{i4:1,g:1});var hL;function yy(){}yy.prototype=new p;yy.prototype.constructor=yy;function Rba(a,b){b.n(a);return a}yy.prototype.$classData=q({j4:0},!1,"scala.util.ChainingOps$",{j4:1,g:1});var xy;function jL(a){this.yR=a}jL.prototype=new p;jL.prototype.constructor=jL;jL.prototype.u=function(){return"DynamicVariable("+this.yR+")"}; jL.prototype.$classData=q({k4:0},!1,"scala.util.DynamicVariable",{k4:1,g:1});function kL(){}kL.prototype=new p;kL.prototype.constructor=kL; function lL(a,b,c,d){c=c-b|0;if(!(2>c)){if(0d.Da(g,XG(xG(),a,-1+(b+e|0)|0))){for(var h=b,k=-1+(b+e|0)|0;1<(k-h|0);){var l=(h+k|0)>>>1|0;0>d.Da(g,XG(xG(),a,l))?k=l:h=l}h=h+(0>d.Da(g,XG(xG(),a,h))?0:1)|0;for(k=b+e|0;k>h;)fH(xG(),a,k,XG(xG(),a,-1+k|0)),k=-1+k|0;fH(xG(),a,h,g)}e=1+e|0}}} function mL(a,b,c,d,e,g,h){if(32>(d-c|0))lL(b,c,d,e);else{var k=(c+d|0)>>>1|0;g=null===g?h.si(k-c|0):g;mL(a,b,c,k,e,g,h);mL(a,b,k,d,e,g,h);nL(b,c,k,d,e,g)}}function nL(a,b,c,d,e,g){if(0e.Da(XG(xG(),a,h),XG(xG(),g,l))?(fH(xG(),a,b,XG(xG(),a,h)),h=1+h|0):(fH(xG(),a,b,XG(xG(),g,l)),l=1+l|0),b=1+b|0;for(;lc)throw Kj("fromIndex(0) \x3e toIndex("+c+")");if(16<(c-0|0)){var g=b.a.length,h=ca(b);Vi(a,b,rh(th(),bg(h),g),0,c,d,e)}else Zi(b,0,c,d,e)}else if(b instanceof Xc)if(d===Fq())Ti(Pj(),b);else if(e=rl(),32>(c-0|0))lL(b,0,c,d);else{g=(0+c|0)>>>1|0;h=new Xc(g-0|0);if(32>(g-0|0))lL(b,0,g,d);else{var k=(0+g|0)>>>1|0;mL(a,b,0,k,d,h,e);mL(a,b,k,g,d,h,e);nL(b,0,k,g,d,h)}32>(c-g| 0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h));nL(b,0,g,c,d,h)}else if(b instanceof ed)e=pL(),32>(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new ed(g-0|0),32>(g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h));else if(b instanceof Zc)d===LG()?$i(Pj(),b):(e=qL(),32>(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new Zc(g-0|0),32> (g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h)));else if(b instanceof $c)e=rL(),32>(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new $c(g-0|0),32>(g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h));else if(b instanceof Ic)d===MG()?dj(Pj(),b):(e=Ir(),32>(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new Ic(g-0|0),32>(g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h)));else if(b instanceof Pc)d===NG()?fj(Pj(),b):(e=sL(),32>(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new Pc(g-0|0),32>(g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)? lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h)));else if(b instanceof Sc)d===OG()?bj(Pj(),b):(e=tL(),32>(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new Sc(g-0|0),32>(g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h)));else if(b instanceof Ec)if(d===PG()){for(d=c=0;c(c-0|0)?lL(b,0,c,d):(g=(0+c|0)>>>1|0,h=new Ec(g-0|0),32>(g-0|0)?lL(b,0,g,d):(k=(0+g|0)>>>1|0,mL(a,b,0,k,d,h,e),mL(a,b,k,g,d,h,e),nL(b,0,k,g,d,h)),32>(c-g|0)?lL(b,g,c,d):(k=(g+c|0)>>>1|0,mL(a,b,g,k,d,h,e),mL(a,b,k,c,d,h,e),nL(b,g,k,c,d,h)),nL(b,0,g,c,d,h));else{if(null===b)throw le();throw new w(b);}}kL.prototype.$classData=q({r4:0},!1,"scala.util.Sorting$",{r4:1,g:1});var vL;function QG(){vL||(vL=new kL);return vL} function wL(){}wL.prototype=new p;wL.prototype.constructor=wL;function xL(){}xL.prototype=wL.prototype;wL.prototype.C=function(a,b){a=this.Pp(a,b);return-430675100+Math.imul(5,a<<13|a>>>19|0)|0};wL.prototype.Pp=function(a,b){b=Math.imul(-862048943,b);b=Math.imul(461845907,b<<15|b>>>17|0);return a^b};wL.prototype.Ma=function(a,b){return yL(a^b)};function yL(a){a=Math.imul(-2048144789,a^(a>>>16|0));a=Math.imul(-1028477387,a^(a>>>13|0));return a^(a>>>16|0)} function zL(a,b,c){var d=a.C(-889275714,lb("Tuple2"));d=a.C(d,b);d=a.C(d,c);return a.Ma(d,2)}function AL(a){var b=BL(),c=a.G();if(0===c)return lb(a.H());var d=b.C(-889275714,lb(a.H()));for(var e=0;e{throw c;}),!1,t().d,(Vs(),"input"))}Ke.prototype=new ot;Ke.prototype.constructor=Ke;Ke.prototype.nQ=function(a){this.Eu&&(a=Es(a),ff(gf(),a+"\n"))};Ke.prototype.$classData=q({vT:0},!1,"Main$$anon$1",{vT:1,PW:1,g:1}); function ne(a){this.op=null;this.Ui=a;if(null===a)throw null;this.op=a}ne.prototype=new pC;ne.prototype.constructor=ne;ne.prototype.$classData=q({wT:0},!1,"Main$SimplifyPipeline$1$",{wT:1,Maa:1,g:1}); function ZD(a,b){0===(4&a.$j)<<24>>24&&0===(4&a.$j)<<24>>24&&(a.tQ=new Xc(new Int32Array([1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,5,1,2,5,1,3,2,1,3,2,1,3,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1, 2,1,3,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,5,2,4,27,4,27,4,27,4,27,4,27,6,1,2,1,2,4,27,1,2,0,4,2,24,0,27,1,24,1,0,1,0,1,2,1,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,25,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,28,6,7,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,1,0,4,24,0,2,0,24,20,0,26,0,6,20,6,24,6,24,6,24,6,0,5,0,5,24,0,16,0,25,24,26,24,28,6,24,0,24,5,4,5,6,9,24,5,6,5,24,5,6,16,28,6,4,6,28,6,5,9,5,28,5,24,0,16,5,6,5,6,0,5,6,5,0,9,5,6,4,28,24,4,0,5,6,4,6,4,6,4,6,0,24,0,5,6,0,24,0,5,0,5,0,6,0,6,8,5,6,8,6,5,8,6,8,6,8,5,6,5,6,24,9,24,4,5,0,5,0, 6,8,0,5,0,5,0,5,0,5,0,5,0,5,0,6,5,8,6,0,8,0,8,6,5,0,8,0,5,0,5,6,0,9,5,26,11,28,26,0,6,8,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,6,0,8,6,0,6,0,6,0,6,0,5,0,5,0,9,6,5,6,0,6,8,0,5,0,5,0,5,0,5,0,5,0,5,0,6,5,8,6,0,6,8,0,8,6,0,5,0,5,6,0,9,24,26,0,6,8,0,5,0,5,0,5,0,5,0,5,0,5,0,6,5,8,6,8,6,0,8,0,8,6,0,6,8,0,5,0,5,6,0,9,28,5,11,0,6,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,8,6,8,0,8,0,8,6,0,5,0,8,0,9,11,28,26,28,0,8,0,5,0,5,0,5,0,5,0,5,0,5,6,8,0,6,0,6,0,6,0,5,0,5,6,0,9,0,11,28,0,8,0,5,0,5,0,5,0,5,0,5,0,6,5,8,6,8,0,6,8, 0,8,6,0,8,0,5,0,5,6,0,9,0,5,0,8,0,5,0,5,0,5,0,5,8,6,0,8,0,8,6,5,0,8,0,5,6,0,9,11,0,28,5,0,8,0,5,0,5,0,5,0,5,0,5,0,6,0,8,6,0,6,0,8,0,8,24,0,5,6,5,6,0,26,5,4,6,24,9,24,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,6,5,6,0,6,5,0,5,0,4,0,6,0,9,0,5,0,5,28,24,28,24,28,6,28,9,11,28,6,28,6,28,6,21,22,21,22,8,5,0,5,0,6,8,6,24,6,5,6,0,6,0,28,6,28,0,28,24,28,24,0,5,8,6,8,6,8,6,8,6,5,9,24,5,8,6,5,6,5,8,5,8,5,6,5,6,8,6,8,6,5,8,9,8,6,28,1,0,1,0,1,0,5,24,4,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5, 0,5,0,5,0,6,24,11,0,5,28,0,5,0,20,5,24,5,12,5,21,22,0,5,24,10,0,5,0,5,6,0,5,6,24,0,5,6,0,5,0,5,0,6,0,5,6,8,6,8,6,8,6,24,4,24,26,5,6,0,9,0,11,0,24,20,24,6,12,0,9,0,5,4,5,0,5,6,5,0,5,0,5,0,6,8,6,8,0,8,6,8,6,0,28,0,24,9,5,0,5,0,5,0,8,5,8,0,9,11,0,28,5,6,8,0,24,5,8,6,8,6,0,6,8,6,8,6,8,6,0,6,9,0,9,0,24,4,24,0,6,8,5,6,8,6,8,6,8,6,8,5,0,9,24,28,6,28,0,6,8,5,8,6,8,6,8,6,8,5,9,5,6,8,6,8,6,8,6,8,0,24,5,8,6,8,6,0,24,9,0,5,9,5,4,24,0,24,0,6,24,6,8,6,5,6,5,8,6,5,0,2,4,2,4,2,4,6,0,6,1,2,1,2,1,2,1,2,1,2,1,2,1,2, 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,1,0,2,1,2,1,2,0,1,0,2,0,1,0, 1,0,1,0,1,2,1,2,0,2,3,2,3,2,3,2,0,2,1,3,27,2,27,2,0,2,1,3,27,2,0,2,1,0,27,2,1,27,0,2,0,2,1,3,27,0,12,16,20,24,29,30,21,29,30,21,29,24,13,14,16,12,24,29,30,24,23,24,25,21,22,24,25,24,23,24,12,16,0,16,11,4,0,11,25,21,22,4,11,25,21,22,0,4,0,26,0,6,7,6,7,6,0,28,1,28,1,28,2,1,2,1,2,28,1,28,25,1,28,1,28,1,28,1,28,1,28,2,1,2,5,2,28,2,1,25,1,2,28,25,28,2,28,11,10,1,2,10,11,0,25,28,25,28,25,28,25,28,25,28,25,28,25,28,25,28,25,28,25,28,25,28,25,28,21,22,28,25,28,25,28,25,28,0,28,0,28,0,11,28,11,28,25,28,25, 28,25,28,25,28,0,28,21,22,21,22,21,22,21,22,21,22,21,22,21,22,11,28,25,21,22,25,21,22,21,22,21,22,21,22,21,22,25,28,25,21,22,21,22,21,22,21,22,21,22,21,22,21,22,21,22,21,22,21,22,21,22,25,21,22,21,22,25,21,22,25,28,25,28,25,0,28,0,1,0,2,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,4,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,28,1,2,1,2,6,1,2,0,24,11,24, 2,0,2,0,2,0,5,0,4,24,0,6,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,6,24,29,30,29,30,24,29,30,24,29,30,24,20,24,20,24,29,30,24,29,30,21,22,21,22,21,22,21,22,24,4,24,20,0,28,0,28,0,28,0,28,0,12,24,28,4,5,10,21,22,21,22,21,22,21,22,21,22,28,21,22,21,22,21,22,21,22,20,21,22,28,10,6,8,20,4,28,10,4,5,24,28,0,5,0,6,27,4,5,20,5,24,4,5,0,5,0,5,0,28,11,28,5,0,28,0,5,28,0,11,28,11,28,11,28,11,28,11,28,5,0,28,5,0,5,4,5,0,28,0,5,4,24,5,4,24,5,9,5,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2, 1,2,1,2,1,2,1,2,1,2,1,2,5,6,7,24,6,24,4,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,6,5,10,6,24,0,27,4,27,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,4,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,4,27,1,2,1,2,0,1,2,1,2,0,1,2,1,2,1,2,1,2,1,2,1,0,4,2,5,6,5,6,5,6,5,8,6,8,28,0,11,28,26,28,0,5,24,0,8,5,8,6,0,24,9,0,6,5,24,5,0,9,5,6,24,5,6,8,0,24,5,0,6,8,5,6,8,6,8,6,8,24,0,4,9,0,24,0,5,6,8,6,8,6,0,5,6,5, 6,8,0,9,0,24,5,4,5,28,5,8,0,5,6,5,6,5,6,5,6,5,6,5,0,5,4,24,5,8,6,8,24,5,4,8,6,0,5,0,5,0,5,0,5,0,5,0,5,8,6,8,6,8,24,8,6,0,9,0,5,0,5,0,5,0,19,18,5,0,5,0,2,0,2,0,5,6,5,25,5,0,5,0,5,0,5,0,5,0,5,27,0,5,21,22,0,5,0,5,0,5,26,28,0,6,24,21,22,24,0,6,0,24,20,23,21,22,21,22,21,22,21,22,21,22,21,22,21,22,21,22,24,21,22,24,23,24,0,24,20,21,22,21,22,21,22,24,25,20,25,0,24,26,24,0,5,0,5,0,16,0,24,26,24,21,22,24,25,24,20,24,9,24,25,24,1,21,24,22,27,23,27,2,21,25,22,25,21,22,24,21,22,24,5,4,5,4,5,0,5,0,5,0,5,0,5, 0,26,25,27,28,26,0,28,25,28,0,16,28,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,24,0,11,0,28,10,11,28,11,0,28,0,28,6,0,5,0,5,0,5,0,11,0,5,10,5,10,0,5,0,24,5,0,5,24,10,0,1,2,5,0,9,0,5,0,5,0,5,0,5,0,5,0,5,0,24,11,0,5,11,0,24,5,0,24,0,5,0,5,0,5,6,0,6,0,6,5,0,5,0,5,0,6,0,6,11,0,24,0,5,11,24,0,5,0,24,5,0,11,5,0,11,0,5,0,11,0,8,6,8,5,6,24,0,11,9,0,6,8,5,8,6,8,6,24,16,24,0,5,0,9,0,6,5,6,8,6,0,9,24,0,6,8,5,8,6,8,5,24,0,9,0,5,6,8,6,8,6,8,6,0,9,0,5,0,10,0,24,0,5,0,5,0,5,0,5,8,0,6,4,0,5,0,28,0,28,0,28,8,6,28,8,16,6,28,6, 28,6,28,0,28,6,28,0,28,0,11,0,1,2,1,2,0,2,1,2,1,0,1,0,1,0,1,0,1,0,1,2,0,2,0,2,0,2,1,2,1,0,1,0,1,0,1,0,2,1,0,1,0,1,0,1,0,1,0,2,1,2,1,2,1,2,1,2,1,2,1,2,0,1,25,2,25,2,1,25,2,25,2,1,25,2,25,2,1,25,2,25,2,1,25,2,25,2,1,2,0,9,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,5,0,25,0,28,0,28,0,28,0,28,0,28,0,28,0,11,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28,0,28, 0,28,0,28,0,28,0,5,0,5,0,5,0,5,0,16,0,16,0,6,0,18,0,18,0])),a.$j=(4|a.$j)<<24>>24);var c=a.tQ.a;if(0===(2&a.$j)<<24>>24&&0===(2&a.$j)<<24>>24){for(var d=new Xc(new Int32Array([257,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,3,2,1,1,1,2,1,3,2,4,1,2,1,3,3,2,1,2,1,1,1,1,1,2,1,1,2,1,1,2,1,3,1,1,1,2,2,1,1, 3,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,7,2,1,2,2,1,1,4,1,1,1,1,1,1,1,1,69,1,27,18,4,12,14,5,7,1,1,1,17,112,1,1,1,1,1,1,1,1,2,1,3,1,5,2,1,1,3,1,1,1,2,1,17,1,9,35,1,2,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,1,1,1,1,2,2,51,48,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,2,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,38,2,1,6,1,39,1,1,1,4,1,1,45,1,1,1,2,1,2,1,1,8,27,5,3,2,11,5,1,3,2,1,2,2,11,1,2,2,32,1,10,21,10,4,2,1,99,1,1,7,1,1,6,2,2,1,4,2,10,3,2,1,14,1,1,1,1,30,27,2,89,11,1,14,10,33,9,2,1,3,1,5,22,4,1,9,1,3,1, 5,2,15,1,25,3,2,1,65,1,1,11,55,27,1,3,1,54,1,1,1,1,3,8,4,1,2,1,7,10,2,2,10,1,1,6,1,7,1,1,2,1,8,2,2,2,22,1,7,1,1,3,4,2,1,1,3,4,2,2,2,2,1,1,8,1,4,2,1,3,2,2,10,2,2,6,1,1,5,2,1,1,6,4,2,2,22,1,7,1,2,1,2,1,2,2,1,1,3,2,4,2,2,3,3,1,7,4,1,1,7,10,2,3,1,11,2,1,1,9,1,3,1,22,1,7,1,2,1,5,2,1,1,3,5,1,2,1,1,2,1,2,1,15,2,2,2,10,1,1,15,1,2,1,8,2,2,2,22,1,7,1,2,1,5,2,1,1,1,1,1,4,2,2,2,2,1,8,1,1,4,2,1,3,2,2,10,1,1,6,10,1,1,1,6,3,3,1,4,3,2,1,1,1,2,3,2,3,3,3,12,4,2,1,2,3,3,1,3,1,2,1,6,1,14,10,3,6,1,1,6,3,1,8,1,3,1,23, 1,10,1,5,3,1,3,4,1,3,1,4,7,2,1,2,6,2,2,2,10,8,7,1,2,2,1,8,1,3,1,23,1,10,1,5,2,1,1,1,1,5,1,1,2,1,2,2,7,2,7,1,1,2,2,2,10,1,2,15,2,1,8,1,3,1,41,2,1,3,4,1,3,1,3,1,1,8,1,8,2,2,2,10,6,3,1,6,2,2,1,18,3,24,1,9,1,1,2,7,3,1,4,3,3,1,1,1,8,18,2,1,12,48,1,2,7,4,1,6,1,8,1,10,2,37,2,1,1,2,2,1,1,2,1,6,4,1,7,1,3,1,1,1,1,2,2,1,4,1,2,6,1,2,1,2,5,1,1,1,6,2,10,2,4,32,1,3,15,1,1,3,2,6,10,10,1,1,1,1,1,1,1,1,1,1,2,8,1,36,4,14,1,5,1,2,5,11,1,36,1,8,1,6,1,2,5,4,2,37,43,2,4,1,6,1,2,2,2,1,10,6,6,2,2,4,3,1,3,2,7,3,4,13,1,2,2, 6,1,1,1,10,3,1,2,38,1,1,5,1,2,43,1,1,332,1,4,2,7,1,1,1,4,2,41,1,4,2,33,1,4,2,7,1,1,1,4,2,15,1,57,1,4,2,67,2,3,9,20,3,16,10,6,85,11,1,620,2,17,1,26,1,1,3,75,3,3,15,13,1,4,3,11,18,3,2,9,18,2,12,13,1,3,1,2,12,52,2,1,7,8,1,2,11,3,1,3,1,1,1,2,10,6,10,6,6,1,4,3,1,1,10,6,35,1,52,8,41,1,1,5,70,10,29,3,3,4,2,3,4,2,1,6,3,4,1,3,2,10,30,2,5,11,44,4,17,7,2,6,10,1,3,34,23,2,3,2,2,53,1,1,1,7,1,1,1,1,2,8,6,10,2,1,10,6,10,6,7,1,6,82,4,1,47,1,1,5,1,1,5,1,2,7,4,10,7,10,9,9,3,2,1,30,1,4,2,2,1,1,2,2,10,44,1,1,2,3,1,1, 3,2,8,4,36,8,8,2,2,3,5,10,3,3,10,30,6,2,64,8,8,3,1,13,1,7,4,1,4,2,1,2,9,44,63,13,1,34,37,39,21,4,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,8,6,2,6,2,8,8,8,8,6,2,6,2,8,1,1,1,1,1,1,1,1,8,8,14,2,8,8,8,8,8,8,5,1,2,4,1,1,1,3,3,1,2,4,1,3,4,2,2,4,1,3,8,5,3,2,3,1,2,4,1,2,1,11,5,6,2,1,1,1,2,1,1,1,8,1,1,5,1,9,1,1,4,2,3,1,1,1,11,1,1,1,10,1,5,5,6,1,1,2,6,3,1,1,1,10,3,1,1,1,13,3,32,16,13,4,1,3,12,15,2,1,4,1,2,1,3,2,3,1,1,1,2,1,5,6,1,1,1,1,1,1,4,1,1,4,1,4,1,2,2,2,5,1,4,1,1,2,1,1,16,35,1,1,4,1,6,5,5,2,4,1,2,1,2,1,7,1,31,2,2,1,1,1,31,268,8,4,20,2,7,1,1,81,1, 30,25,40,6,18,12,39,25,11,21,60,78,22,183,1,9,1,54,8,111,1,144,1,103,1,1,1,1,1,1,1,1,1,1,1,1,1,1,30,44,5,1,1,31,1,1,1,1,1,1,1,1,1,1,16,256,131,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,63,1,1,1,1,32,1,1,258,48,21,2,6,3,10,166,47,1,47,1,1,1,3,2,1,1,1,1,1,1,4,1,1,2,1,6,2,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,6,1,1,1,1,3,1,1,5,4,1,2,38,1, 1,5,1,2,56,7,1,1,14,1,23,9,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,32,2,1,1,1,1,3,1,1,1,1,1,9,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,5,1,10,2,68,26,1,89,12,214,26,12,4,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,9,4,2,1,5,2,3,1,1,1,2,1,86,2,2,2,2,1,1,90,1,3,1,5,41,3,94,1,2,4,10,27,5,36,12,16,31,1,10,30,8,1,15,32,10,39,15,320,6582,10,64,20941,51,21,1,1143,3,55,9,40,6,2,268,1,3,16,10,2,20,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,10,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,7,1,70,10,2,6,8,23,9,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,8,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,12,1,1,1,1,1,1,1,1,1,1,1,77,2,1,7,1,3,1,4,1,23,2,2,1,4,4,6,2,1,1,6,52,4,8,2,50,16,1,9,2,10,6,18,6,3,1,4,10,28,8,2,23,11,2,11,1,29,3,3,1,47,1,2,4,2,1,4,13,1,1,10,4,2,32,41,6,2,2,2,2,9,3,1,8,1,1,2,10,2,4,16,1,6,3,1,1,4,48,1,1,3,2, 2,5,2,1,1,1,24,2,1,2,11,1,2,2,2,1,2,1,1,10,6,2,6,2,6,9,7,1,7,145,35,2,1,2,1,2,1,1,1,2,10,6,11172,12,23,4,49,4,2048,6400,366,2,106,38,7,12,5,5,1,1,10,1,13,1,5,1,1,1,2,1,2,1,108,16,17,363,1,1,16,64,2,54,40,12,1,1,2,16,7,1,1,1,6,7,9,1,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,4,3,3,1,4,1,1,1,1,1,1,1,3,1,1,3,1,1,1,2,4,5,1,135,2,1,1,3,1,3,1,1,1,1,1,1,2,10,2,3,2,26,1,1,1,1,1,1,26,1,1,1,1,1,1,1,1,1,2,10,1,45,2,31,3,6,2,6,2,6,2,3,3,2,1,1,1,2,1,1,4,2,10,3,2,2,12,1,26,1,19,1,2,1,15,2,14,34,123,5,3,4,45,3,9, 53,4,17,1,5,12,52,45,1,130,29,3,49,47,31,1,4,12,17,1,8,1,53,30,1,1,36,4,8,1,5,42,40,40,78,2,10,854,6,2,1,1,44,1,2,3,1,2,23,1,1,8,160,22,6,3,1,26,5,1,64,56,6,2,64,1,3,1,2,5,4,4,1,3,1,27,4,3,4,1,8,8,9,7,29,2,1,128,54,3,7,22,2,8,19,5,8,128,73,535,31,385,1,1,1,53,15,7,4,20,10,16,2,1,45,3,4,2,2,2,1,4,14,25,7,10,6,3,36,5,1,8,1,10,4,60,2,1,48,3,9,2,4,4,7,10,1190,43,1,1,1,2,6,1,1,8,10,2358,879,145,99,13,4,2956,1071,13265,569,1223,69,11,1,46,16,4,13,16480,2,8190,246,10,39,2,60,2,3,3,6,8,8,2,7,30,4,48,34,66, 3,1,186,87,9,18,142,26,26,26,7,1,18,26,26,1,1,2,2,1,2,2,2,4,1,8,4,1,1,1,7,1,11,26,26,2,1,4,2,8,1,7,1,26,2,1,4,1,5,1,1,3,7,1,26,26,26,26,26,26,26,26,26,26,26,26,28,2,25,1,25,1,6,25,1,25,1,6,25,1,25,1,6,25,1,25,1,6,25,1,25,1,6,1,1,2,50,5632,4,1,27,1,2,1,1,2,1,1,10,1,4,1,1,1,1,6,1,4,1,1,1,1,1,1,3,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,4,1,7,1,4,1,4,1,1,1,10,1,17,5,3,1,5,1,17,52,2,270,44,4,100,12,15,2,14,2,15,1,15,32,11,5,31,1,60,4,43,75,29,13,43,5,9,7,2,174,33,15,6,1,70,3,20,12,37,1,5,21,17,15,63,1,1, 1,182,1,4,3,62,2,4,12,24,147,70,4,11,48,70,58,116,2188,42711,41,4149,11,222,16354,542,722403,1,30,96,128,240,65040,65534,2,65534])),e=d.a[0],g=1,h=d.a.length;g!==h;)e=e+d.a[g]|0,d.a[g]=e,g=1+g|0;a.sQ=d;a.$j=(2|a.$j)<<24>>24}a=a.sQ;b=kj(Pj(),a,b);return c[0<=b?1+b|0:-1-b|0]} function RL(a){0===(32&a.$j)<<24>>24&&0===(32&a.$j)<<24>>24&&(a.vQ=new Xc(new Int32Array([1632,1776,1984,2406,2534,2662,2790,2918,3046,3174,3302,3430,3664,3792,3872,4160,4240,6112,6160,6470,6608,6784,6800,6992,7088,7232,7248,42528,43216,43264,43472,43600,44016,65296,66720,69734,69872,69942,70096,71360,120782,120792,120802,120812,120822])),a.$j=(32|a.$j)<<24>>24);return a.vQ}function SL(){this.vQ=this.tQ=this.sQ=this.uQ=null;this.$j=0}SL.prototype=new p;SL.prototype.constructor=SL; function TL(a,b){if(0<=b&&65536>b)return String.fromCharCode(b);if(0<=b&&1114111>=b)return String.fromCharCode(65535&(-64+(b>>10)|55296),65535&(56320|1023&b));throw UL();}function VL(a,b){return 0>b?0:256>b?WL(a).a[b]:ZD(a,b)} function DH(a,b,c){if(256>b)a=48<=b&&57>=b?-48+b|0:65<=b&&90>=b?-55+b|0:97<=b&&122>=b?-87+b|0:-1;else if(65313<=b&&65338>=b)a=-65303+b|0;else if(65345<=b&&65370>=b)a=-65335+b|0;else{var d=kj(Pj(),RL(a),b);d=0>d?-2-d|0:d;0>d?a=-1:(a=b-RL(a).a[d]|0,a=9=b||127<=b&&159>=b} function bE(a,b){return 256>b?170===b||186===b||2===WL(a).a[b]:688<=b&&696>=b||704<=b&&705>=b||736<=b&&740>=b||837===b||890===b||7468<=b&&7530>=b||7544===b||7579<=b&&7615>=b||8305===b||8319===b||8336<=b&&8348>=b||8560<=b&&8575>=b||9424<=b&&9449>=b||11388<=b&&11389>=b||42864===b||43E3<=b&&43001>=b||2===ZD(a,b)}function TF(a,b){return 8544<=b&&8559>=b||9398<=b&&9423>=b||1===VL(a,b)}function Or(a,b){a=VL(a,b);return 1===a||2===a||3===a||4===a||5===a} function hda(a){switch(a){case 8115:case 8131:case 8179:return 9+a|0;default:if(8064<=a&&8111>=a)return 8|a;var b=TL(0,a).toUpperCase();switch(b.length){case 1:return b.charCodeAt(0);case 2:var c=b.charCodeAt(0);b=b.charCodeAt(1);return-671032320===(-67044352&(c<<16|b))?(64+(1023&c)|0)<<10|1023&b:a;default:return a}}} function bda(a){if(304===a)return 105;var b=TL(0,a).toLowerCase();switch(b.length){case 1:return b.charCodeAt(0);case 2:var c=b.charCodeAt(0);b=b.charCodeAt(1);return-671032320===(-67044352&(c<<16|b))?(64+(1023&c)|0)<<10|1023&b:a;default:return a}} function WL(a){0===(1&a.$j)<<24>>24&&0===(1&a.$j)<<24>>24&&(a.uQ=new Xc(new Int32Array([15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,12,24,24,24,26,24,24,24,21,22,24,25,24,20,24,24,9,9,9,9,9,9,9,9,9,9,24,24,25,25,25,24,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,21,24,22,27,23,27,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,21,25,22,25,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,12,24,26,26,26,26,28,24,27,28,5,29,25,16,28,27,28,25,11,11,27,2,24,24,27,11,5,30,11,11,11,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,25,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,25,2,2,2,2,2,2,2,2])),a.$j=(1|a.$j)<<24>>24);return a.uQ}SL.prototype.$classData=q({V0:0},!1,"java.lang.Character$",{V0:1,g:1,l:1});var XL;function Pr(){XL||(XL=new SL);return XL}function YL(a){throw new ZL('For input string: "'+a+'"');}function $L(){this.wQ=this.xQ=null;this.Ct=0} $L.prototype=new p;$L.prototype.constructor=$L; function aM(a,b){0===(1&a.Ct)<<24>>24&&0===(1&a.Ct)<<24>>24&&(a.xQ=RegExp("^[\\x00-\\x20]*([+-]?(?:NaN|Infinity|(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?)[fFdD]?)[\\x00-\\x20]*$"),a.Ct=(1|a.Ct)<<24>>24);var c=a.xQ.exec(b);if(null!==c)b=+parseFloat(c[1]);else{0===(2&a.Ct)<<24>>24&&0===(2&a.Ct)<<24>>24&&(a.wQ=RegExp("^[\\x00-\\x20]*([+-]?)0[xX]([0-9A-Fa-f]*)\\.?([0-9A-Fa-f]*)[pP]([+-]?\\d+)[fFdD]?[\\x00-\\x20]*$"),a.Ct=(2|a.Ct)<<24>>24);var d=a.wQ.exec(b);null===d&&YL(b);a=d[1];c=d[2];var e=d[3];d= d[4];""===c&&""===e&&YL(b);b=bM(0,c,e,d,15);b="-"===a?-b:b}return b} function bM(a,b,c,d,e){a=""+b+c;c=-(c.length<<2)|0;for(b=0;;)if(b!==a.length&&48===a.charCodeAt(b))b=1+b|0;else break;a=a.substring(b);if(""===a)return 0;var g=a.length;if(b=g>e){for(var h=!1,k=e;!h&&k!==g;)48!==a.charCodeAt(k)&&(h=!0),k=1+k|0;g=h?"1":"0";g=a.substring(0,e)+g}else g=a;c=c+(b?(a.length-(1+e|0)|0)<<2:0)|0;e=+parseInt(g,16);d=+parseInt(d,10);c=Eb(d)+c|0;a=c/3|0;d=+Math.pow(2,a);c=+Math.pow(2,c-(a<<1)|0);return e*d*d*c} function maa(a,b,c){return b!==b?c!==c?0:1:c!==c?-1:b===c?0===b?(a=1/b,a===1/c?0:0>a?-1:1):0:b>20;if(0===h)throw new Yj("parseFloatCorrection was given a subnormal mid: "+g);g=1048575&k;g=ri(Ph(),new ma(c,1048576|g));c=-1075+h|0;0<=b?0<=c?(a=Ki(a,Qi(Ph().mq,b)),b=Oi(g,c),a=fM(a,b)):a=fM(Oi(Ki(a,Qi(Ph().mq,b)),-c|0),g):0<=c?(b=-b|0,b=Oi(Ki(g,Qi(Ph().mq,b)),c),a=fM(a,b)):(a=Oi(a,-c|0),b=-b|0,b=Ki(g,Qi(Ph().mq,b)),a=fM(a,b));return 0>a?d:0c||36=b.length&&kM(b);for(var h=0;d!==a;){var k=DH(Pr(),b.charCodeAt(d),c);h=h*c+k;(-1===k||h>g)&&kM(b);d=1+d|0}return e?-h|0:h|0}function nI(a,b){a=b-(1431655765&b>>1)|0;a=(858993459&a)+(858993459&a>>2)|0;return Math.imul(16843009,252645135&(a+(a>>4)|0))>>24}lM.prototype.$classData=q({c1:0},!1,"java.lang.Integer$",{c1:1,g:1,l:1});var mM; function UH(){mM||(mM=new lM);return mM}function nM(a){if(!a.RF){for(var b=[],c=0;2>c;)b.push(null),c=1+c|0;for(;36>=c;){for(var d=pb(2147483647,c),e=c,g=1,h="0";e<=d;)e=Math.imul(e,c),g=1+g|0,h+="0";d=e;e=d>>31;var k=xa(),l=Wh(k,-1,-1,d,e);b.push(new pg(g,new ma(d,e),h,new ma(l,k.Qc)));c=1+c|0}a.QF=b;a.RF=!0}return a.QF} function oM(a,b,c){var d=(a.RF?a.QF:nM(a))[c],e=d.DQ;a=e.W;e=e.Y;d=d.l1;var g=-2147483648^e,h="",k=b.W;for(b=b.Y;;){var l=k,m=-2147483648^b;if(m===g?(-2147483648^l)>=(-2147483648^a):m>g){l=k;m=xa();b=Wh(m,l,b,a,e);l=m.Qc;var n=65535&b;m=b>>>16|0;var r=65535&a,v=a>>>16|0,x=Math.imul(n,r);r=Math.imul(m,r);n=Math.imul(n,v);x=x+((r+n|0)<<16)|0;Math.imul(b,e);Math.imul(l,a);Math.imul(m,v);k=(k-x|0).toString(c);h=""+d.substring(k.length)+k+h;k=b;b=l}else break}return""+k.toString(c)+h} function pM(a){throw new ZL('For input string: "'+a+'"');}function qM(a,b,c){for(var d=0;a!==b;){var e=DH(Pr(),c.charCodeAt(a),10);-1===e&&pM(c);d=Math.imul(d,10)+e|0;a=1+a|0}return d}function rM(){this.QF=null;this.RF=!1}rM.prototype=new p;rM.prototype.constructor=rM;function sM(a,b,c){return 0!==c?(a=(+(c>>>0)).toString(16),b=(+(b>>>0)).toString(16),a+(""+"00000000".substring(b.length)+b)):(+(b>>>0)).toString(16)}rM.prototype.$classData=q({h1:0},!1,"java.lang.Long$",{h1:1,g:1,l:1});var tM; function uM(){tM||(tM=new rM);return tM}function vM(){}vM.prototype=new p;vM.prototype.constructor=vM;function wM(){}wM.prototype=vM.prototype;function PK(a){return a instanceof vM||"number"===typeof a||a instanceof ma}function xM(a,b,c,d,e){this.sB=a;this.SF=b;this.tB=c;this.uB=d;this.rB=e}xM.prototype=new p;xM.prototype.constructor=xM;xM.prototype.i=function(a){return a instanceof xM?this.tB===a.tB&&this.uB===a.uB&&this.rB===a.rB&&this.sB===a.sB&&this.SF===a.SF:!1}; xM.prototype.u=function(){var a="";"\x3cjscode\x3e"!==this.sB&&(a=""+a+this.sB+".");a=""+a+this.SF;null===this.tB?a+="(Unknown Source)":(a=a+"("+this.tB,0<=this.uB&&(a=a+":"+this.uB,0<=this.rB&&(a=a+":"+this.rB)),a+=")");return a};xM.prototype.B=function(){return lb(this.sB)^lb(this.SF)^lb(this.tB)^this.uB^this.rB};var yM=q({t1:0},!1,"java.lang.StackTraceElement",{t1:1,g:1,l:1});xM.prototype.$classData=yM;function zM(){}zM.prototype=new p;zM.prototype.constructor=zM; function AM(a,b,c,d){a=c+d|0;if(0>c||ab.a.length)throw b=new tH,yF(b,null,null,!0),b;for(d="";c!==a;)d=""+d+String.fromCharCode(b.a[c]),c=1+c|0;return d} function jda(a,b){var c=new BM,d=CM();c.wv=null;c.T1=d;c.Gt="";c.gK=!1;if(c.gK)throw new DM;for(var e=0,g=0,h=6,k=0;k!==h;){var l="\\u%04X".indexOf("%",k)|0;if(0>l){EM(c,"\\u%04X".substring(k));break}EM(c,"\\u%04X".substring(k,l));var m=1+l|0,n=Tj().QQ;n.lastIndex=m;var r=n.exec("\\u%04X");if(null===r||(r.index|0)!==m){var v=m===h?37:"\\u%04X".charCodeAt(m);FM(v)}k=n.lastIndex|0;for(var x="\\u%04X".charCodeAt(-1+k|0),A,B=r[2],C=65<=x&&90>=x?256:0,D=B.length,F=0;F!==D;){var I=B.charCodeAt(F);switch(I){case 45:var M= 1;break;case 35:M=2;break;case 43:M=4;break;case 32:M=8;break;case 48:M=16;break;case 44:M=32;break;case 40:M=64;break;case 60:M=128;break;default:throw new Yj(hc(I));}if(0!==(C&M))throw new GM(String.fromCharCode(I));C|=M;F=1+F|0}A=C;var N=HM(r[3]),P=HM(r[4]);if(-2===N)throw new IM(-2147483648);-2===P&&JM(-2147483648);if(110===x){-1!==P&&JM(P);if(-1!==N)throw new IM(N);0!==A&&KM(A);EM(c,"\n")}else if(37===x){-1!==P&&JM(P);17!==(17&A)&&12!==(12&A)||KM(A);if(0!==(1&A)&&-1===N)throw new LM("%"+r[0]); 0!==(-2&A)&&MM(37,A,-2);NM(c,A,N,"%")}else{var T=0!==(256&A)?65535&(32+x|0):x,Y=Tj().PQ.a[-97+T|0];-1!==Y&&0===(256&A&Y)||FM(x);if(0!==(17&A)&&-1===N)throw new LM("%"+r[0]);17!==(17&A)&&12!==(12&A)||KM(A);-1!==P&&0!==(512&Y)&&JM(P);0!==(A&Y)&&MM(T,A,Y);if(0!==(128&A))var Z=g;else{var S=HM(r[1]);if(-1===S)Z=e=1+e|0;else{if(0>=S)throw new OM(0===S?"Illegal format argument index \x3d 0":"Format argument index: (not representable as int)");Z=S}}if(0>=Z||Z>b.a.length)throw new PM("%"+r[0]);g=Z;var ea= b.a[-1+Z|0];if(null===ea&&98!==T&&115!==T)QM(c,CM(),A,N,P,"null");else{var ia=void 0,X=void 0,sa=void 0,Ja=void 0,Xa=void 0,Fa=c,za=ea,Qa=T,Ma=A,Ga=N,ab=P;switch(Qa){case 98:var Hb=!1===za||null===za?"false":"true";QM(Fa,CM(),Ma,Ga,ab,Hb);break;case 104:var bc=(+(ib(za)>>>0)).toString(16);QM(Fa,CM(),Ma,Ga,ab,bc);break;case 115:za&&za.$classData&&za.$classData.rb.Zaa?za.Xaa(Fa,(0!==(1&Ma)?1:0)|(0!==(2&Ma)?4:0)|(0!==(256&Ma)?2:0),Ga,ab):(0!==(2&Ma)&&MM(Qa,Ma,2),QM(Fa,0,Ma,Ga,ab,""+za));break;case 99:if(za instanceof ba)var yb=String.fromCharCode(Ea(za));else{ha(za)||RM(Qa,za);var tb=za|0;if(!(0<=tb&&1114111>=tb))throw new SM(tb);yb=65536>tb?String.fromCharCode(tb):String.fromCharCode(-64+(tb>>10)|55296,56320|1023&tb)}QM(Fa,0,Ma,Ga,-1,yb);break;case 100:if(ha(za))var eb=""+(za|0);else if(za instanceof ma){var kb=Za(za),Rb=kb.W,Gb=kb.Y;eb=bG(xa(),Rb,Gb)}else za instanceof TM||RM(Qa,za),eb=Vh(bi(),za);UM(Fa,Ma,Ga,eb,"");break;case 111:case 120:var vb=111===Qa,Tb=0===(2&Ma)?"":vb?"0":0!==(256&Ma)?"0X":"0x";if(za instanceof TM){var Nb=vb?8:16;CM();var ic=bi(),Va=za.Ya,cb=za.wb,zb=za.Qa,Ub=2>Nb||36Va){var ub=jb,Aa=db;jb=-ub|0;db=0!==ub?~Aa:-Aa|0}var va=uM(),Ra=jb,rb=db;if(10===Nb||2>Nb||36>31===Ha)Ja=mc.toString(Nb);else if(0>Ha){var Ka=xb.W,Oa=xb.Y;Ja="-"+oM(va,new ma(-Ka|0,0!==Ka?~Oa:-Oa|0),Nb)}else Ja=oM(va,xb,Nb)}Xa=Ja}else if(10===Nb||Ub)Xa=Vh(bi(),za);else{var Na=0;Na=+Math.log(Nb)/ +Math.log(2);var Da=0>Va?1:0,ta=VM(za),Ya=yh(Sh(),ta),dc=1+Eb(Ya/Na+Da)|0,ka=null;ka="";var ya=0;ya=dc;var Sa=0;Sa=0;if(16!==Nb){var xc=new Xc(cb);zb.wa(0,xc,0,cb);var Sb=0;Sb=cb;for(var uc=ic.uH.a[Nb],Lb=ic.tH.a[-2+Nb|0];;){Sa=gi(ei(),xc,xc,Sb,Lb);for(var lc=ya;;){ya=-1+ya|0;Pr();var Xb=Bb(Sa,Nb);if(2>Nb||36Xb||Xb>=Nb)var ec=0;else{var Ab=-10+Xb|0;ec=65535&(0>Ab?48+Xb|0:97+Ab|0)}ka=""+String.fromCharCode(ec)+ka;Sa=pb(Sa,Nb);if(0===Sa||0===ya)break}for(var Ob=(uc-lc|0)+ya|0,fb=0;fbIa&&0>(Ia<<2),ya=-1+ya|0,ka=""+(+(Sa>>>0)).toString(16)+ka,Ia=1+Ia|0;Wa=1+Wa|0}for(var Ua=0;;)if(48===ka.charCodeAt(Ua))Ua=1+Ua|0;else break;0!==Ua&&(ka=ka.substring(Ua));Xa=-1===Va?"-"+ka:ka}UM(Fa,Ma,Ga,Xa,Tb)}else{if(ha(za))var pc=za|0,sc=vb?(+(pc>>>0)).toString(8):(+(pc>>>0)).toString(16);else{za instanceof ma||RM(Qa, za);var Ba=Za(za),ob=Ba.W,nc=Ba.Y;if(vb){uM();var Ib=1073741823&ob,vc=1073741823&((ob>>>30|0)+(nc<<2)|0),Vb=nc>>>28|0;if(0!==Vb){var fc=(+(Vb>>>0)).toString(8),Bc=(+(vc>>>0)).toString(8),Pb="0000000000".substring(Bc.length),Jb=(+(Ib>>>0)).toString(8);sa=fc+(""+Pb+Bc)+(""+"0000000000".substring(Jb.length)+Jb)}else if(0!==vc){var gc=(+(vc>>>0)).toString(8),Cb=(+(Ib>>>0)).toString(8);sa=gc+(""+"0000000000".substring(Cb.length)+Cb)}else sa=(+(Ib>>>0)).toString(8)}else sa=sM(uM(),ob,nc);sc=sa}0!==(76& Ma)&&MM(Qa,Ma,76);WM(Fa,CM(),Ma,Ga,Tb,XM(Ma,sc))}break;case 101:case 102:case 103:if("number"===typeof za){var cc=+za;if(cc!==cc||Infinity===cc||-Infinity===cc)YM(Fa,Ma,Ga,cc);else{Tj();if(0===cc)X=new Vj(0>1/cc,"0",0);else{var yc=0>cc,Mc=""+(yc?-cc:cc),qc=vH(Mc,101),oc=0>qc?0:parseInt(Mc.substring(1+qc|0))|0,Qc=0>qc?Mc.length:qc,jc=vH(Mc,46);if(0>jc){var sb=Mc.substring(0,Qc);X=new Vj(yc,sb,-oc|0)}else{for(var Gc=""+Mc.substring(0,jc)+Mc.substring(1+jc|0,Qc),Wb=Gc.length,Cc=0;;)if(Cc>>20|0),sd= 0===ab?1:12Vc?"-":0!==(4&Ma)?"+":0!==(8&Ma)?" ":"";if(0===rc)if(0===kc&&0===Hc)var Qd="0",Ad=aa,kd=0;else if(-1===sd)Qd="0",Ad=new ma(kc,Hc),kd=-1022;else{var Hd=-11+(0!==Hc?Math.clz32(Hc):32+Math.clz32(kc)|0)|0;Qd="1";Ad=new ma(0===(32&Hd)?kc<>>1|0)>>>(31-Hd|0)|0|Hc<>>1|0|ra<<31,ud=ra>>1,be=od&~wc,re=Ta&~ac,pe=od&wc,bd=Ta∾if(bd===ud?(-2147483648^pe)<(-2147483648^Id):bd(-2147483648^Id):bd>ud){var Rc=be+hb|0;wb=Rc;$a=(-2147483648^Rc)<(-2147483648^be)?1+(re+ra|0)|0:re+ra|0}else if(0===(be&hb)&&0===(re&ra))wb=be,$a=re;else{var Wc=be+hb|0;wb=Wc;$a=(-2147483648^Wc)<(-2147483648^be)?1+(re+ra|0)|0:re+ra|0}}var Wd=sM(uM(),wb,$a),zd= ""+"0000000000000".substring(Wd.length)+Wd;Tj();if(13!==zd.length)throw new Yj("padded mantissa does not have the right number of bits");for(var Pa=1>sd?1:sd,Db=zd.length;;)if(Db>Pa&&48===zd.charCodeAt(-1+Db|0))Db=-1+Db|0;else break;var Oc=zd.substring(0,Db),Tc=Kc+(0!==(256&Ma)?"0X":"0x"),Sd=Rd+"."+Oc+"p"+ae;WM(Fa,CM(),Ma,Ga,Tc,XM(Ma,Sd))}}else RM(Qa,za);break;default:throw new Yj("Unknown conversion '"+hc(Qa)+"' was not rejected earlier");}}}}return c.u()} zM.prototype.$classData=q({u1:0},!1,"java.lang.String$",{u1:1,g:1,l:1});var aN;function zH(){aN||(aN=new zM);return aN} function Sda(a,b){bN(a);b(a.u());if(0!==a.my.a.length)for(var c=0;c{ff(b,null===c?"null":c);ff(b,"\n")})} function bN(a){if(null===a.my){if(a.MQ){Ug||(Ug=new Tg);var b=Ug;var c=a.KQ;if(c)if(c.arguments&&c.stack)var d=Fg(c);else if(c.stack&&c.sourceURL)d=c.stack.replace(Rg("\\[native code\\]\\n","m"),"").replace(Rg("^(?\x3d\\w+Error\\:).*$\\n","m"),"").replace(Rg("^@","gm"),"{anonymous}()@").split("\n");else if(c.stack&&c.number)d=c.stack.replace(Rg("^\\s*at\\s+(.*)$","gm"),"$1").replace(Rg("^Anonymous function\\s+","gm"),"{anonymous}() ").replace(Rg("^([^\\(]+|\\{anonymous\\}\\(\\))\\s+\\((.+)\\)$","gm"), "$1@$2").split("\n").slice(1);else if(c.stack&&c.fileName)d=c.stack.replace(Rg("(?:\\n@:0)?\\s+$","m"),"").replace(Rg("^(?:\\((\\S*)\\))?@","gm"),"{anonymous}($1)@").split("\n");else if(c.message&&c["opera#sourceloc"])if(c.stacktrace)if(-1c.stacktrace.split("\n").length)d=Sg(c);else{d=Rg("Line (\\d+).*script (?:in )?(\\S+)(?:: In function (\\S+))?$","i");c=c.stacktrace.split("\n");var e=[];for(var g=0,h=c.length|0;gc.stacktrace.indexOf("called from line")){d=Gg("^(.*)@(.+):(\\d+)$");c=c.stacktrace.split("\n");e=[];g=0;for(h=c.length|0;gv?l:l.substring(0, v)),l=[k,l]):(n=n.exec(l),r=null!==n?n:r.exec(l),null!==r?l=[sg(k,r[1]),"\x3cinit\x3e"]:(v=v.exec(l),l=null!==v?[sg(k,v[1]),"\x3cclinit\x3e"]:["\x3cjscode\x3e",l]));k=l[0];l=l[1];v=h[2];r=parseInt(h[3]);h=h[4];h=void 0!==h?parseInt(h)|0:-1;d.push(new xM(k,l,v,r|0,h))}else d.push(new xM("\x3cjscode\x3e",k,null,-1,-1))|0;c=1+c|0}b=d.length|0;e=new (md(yM).Ia)(b);for(c=0;cb;)c=b,a.a[c]=eN(c,0),b=1+b|0;this.lM=a;a=new (md(iN).Ia)(11);for(b=0;11>b;)c=b,a.a[c]=eN(0,c),b=1+b|0;this.qH=a;this.mM="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"} cN.prototype=new p;cN.prototype.constructor=cN;function jN(a,b,c){0===c?(0<=b.Y?(c=b.Y,c=0===c?-2147483637>(-2147483648^b.W):0>c):c=!1,a=c?a.lM.a[b.W]:kN(b,0)):a=0===b.W&&0===b.Y&&0<=c&&c>31,k=g.W,l=65535&k,m=k>>>16|0,n=65535&b,r=b>>>16|0,v=Math.imul(l,n);n=Math.imul(m,n);var x=Math.imul(l,r);l=v+((n+x|0)<<16)|0;v=(v>>>16|0)+x|0;g=(((Math.imul(k,h)+Math.imul(g.Y,b)|0)+Math.imul(m,r)|0)+(v>>>16|0)|0)+(((65535&v)+n|0)>>>16|0)|0;c.a[e]=new ma(l,g);d=1+d|0}return c} function lN(a,b,c,d){a=0>c?-c|0:c;var e=0===c?0:0>c?-1:1;if(Bi().xM===d)return e;if(Bi().sM===d)return 0;if(Bi().rM===d)return 0e?e:0;if(Bi().vM===d)return 5<=a?e:0;if(Bi().uM===d)return 5(-2147483648^b.W):-1>a)?a=!0:(a=b.Y,a=0===a?-1<(-2147483648^b.W):0b.Y?new ma(~b.W,~b.Y):b;a=b.W;b=b.Y;return 64-(0!==b?Math.clz32(b):32+Math.clz32(a)|0)|0}function nN(a,b,c){return!oN(0,b,c)}function oN(a,b,c){a=c.a.length;for(var d=0;d!==a;){if(c.a[d]===b)return!0;d=1+d|0}return!1}cN.prototype.$classData=q({BT:0},!1,"java.math.BigDecimal$",{BT:1,g:1,l:1});var dN; function hN(){dN||(dN=new cN);return dN}function pN(){this.sH=this.pM=this.MC=this.nq=this.mq=this.Ew=null;qN=this;this.Ew=qi(1,1);this.mq=qi(1,10);this.nq=qi(0,0);this.MC=qi(-1,1);this.pM=new (md(Ji).Ia)([this.nq,this.Ew,qi(1,2),qi(1,3),qi(1,4),qi(1,5),qi(1,6),qi(1,7),qi(1,8),qi(1,9),this.mq]);for(var a=new (md(Ji).Ia)(32),b=0;32>b;){var c=b,d=Ph();a.a[c]=ri(d,new ma(0===(32&c)?1<b.Y)return-1!==b.W||-1!==b.Y?(a=b.W,b=b.Y,rN(-1,new ma(-a|0,0!==a?~b:-b|0))):a.MC;var c=b.Y;return(0===c?-2147483638>=(-2147483648^b.W):0>c)?a.pM.a[b.W]:rN(1,b)}pN.prototype.$classData=q({DT:0},!1,"java.math.BigInteger$",{DT:1,g:1,l:1});var qN;function Ph(){qN||(qN=new pN);return qN} function sN(){this.wM=this.OC=this.uM=this.vM=this.tM=this.rM=this.sM=this.xM=null;tN=this;this.xM=new uN("UP",0);this.sM=new uN("DOWN",1);this.rM=new uN("CEILING",2);this.tM=new uN("FLOOR",3);this.vM=new uN("HALF_UP",4);this.uM=new uN("HALF_DOWN",5);this.OC=new uN("HALF_EVEN",6);this.wM=new uN("UNNECESSARY",7)}sN.prototype=new p;sN.prototype.constructor=sN;sN.prototype.$classData=q({NT:0},!1,"java.math.RoundingMode$",{NT:1,g:1,l:1});var tN;function Bi(){tN||(tN=new sN);return tN}function vN(){} vN.prototype=new p;vN.prototype.constructor=vN;function wN(){}wN.prototype=vN.prototype;vN.prototype.i=function(a){if(a===this)return!0;if(a&&a.$classData&&a.$classData.rb.SQ&&this.ka()===a.ka()){var b=this.JF().vv();a:{for(;b.s();){var c=b.t(),d=a.LF(c.so());c=c.to();if(null===d?null!==c:!La(d,c)){a=!0;break a}}a=!1}return!a}return!1};vN.prototype.B=function(){for(var a=this.JF().vv(),b=0;a.s();){var c=b;b=a.t();c|=0;b=b.B()+c|0}return b|0}; vN.prototype.u=function(){for(var a="{",b=!0,c=this.JF().vv();c.s();){var d=c.t();b?b=!1:a+=", ";a=""+a+d.so()+"\x3d"+d.to()}return a+"}"};function xN(){}xN.prototype=new p;xN.prototype.constructor=xN;xN.prototype.Da=function(a,b){return oa(a,b)};xN.prototype.$classData=q({H1:0},!1,"java.util.Arrays$NaturalComparator$",{H1:1,g:1,Ji:1});var yN;function ij(){yN||(yN=new xN);return yN}function zN(){}zN.prototype=new ak;zN.prototype.constructor=zN; zN.prototype.$classData=q({R1:0},!1,"java.util.Formatter$RootLocaleInfo$",{R1:1,$aa:1,g:1});var AN;function CM(){AN||(AN=new zN);return AN}function BN(){this.vB=this.iK=0;this.hK=this.wB=null}BN.prototype=new p;BN.prototype.constructor=BN;function CN(){}CN.prototype=BN.prototype;BN.prototype.s=function(){if(null!==this.wB)return!0;for(;this.vB>>16|0)^(null===b?0:ib(b))};f.u=function(){return this.xB+"\x3d"+this.xv};var EN=q({W1:0},!1,"java.util.HashMap$Node",{W1:1,g:1,TQ:1});DN.prototype.$classData=EN;function FN(a){this.kK=null;this.kK=new GN(a.yB.zv)}FN.prototype=new p;FN.prototype.constructor=FN;FN.prototype.s=function(){return this.kK.s()};FN.prototype.t=function(){return new HN(this.kK.t())}; FN.prototype.$classData=q({$1:0},!1,"java.util.IdentityHashMap$EntrySet$$anon$2",{$1:1,g:1,p2:1});function HN(a){this.WF=a}HN.prototype=new p;HN.prototype.constructor=HN;f=HN.prototype;f.i=function(a){return ck(a)?Object.is(this.so(),a.so())?Object.is(this.to(),a.to()):!1:!1};f.so=function(){return this.WF.so().py};f.to=function(){return this.WF.to()};f.B=function(){var a=this.WF.to();return Qb(this.WF.so().py)^Qb(a)};f.u=function(){return this.so()+"\x3d"+this.to()}; f.$classData=q({b2:0},!1,"java.util.IdentityHashMap$MapEntry",{b2:1,g:1,TQ:1});function IN(){}IN.prototype=new p;IN.prototype.constructor=IN;IN.prototype.Qo=function(a,b,c){a.a[b]=c};IN.prototype.Wj=function(a,b){return a.a[b]};IN.prototype.$classData=q({C2:0},!1,"java.util.internal.GenericArrayOps$ReusableAnyRefArrayOps$",{C2:1,g:1,zB:1});var JN;function jj(){JN||(JN=new IN);return JN}function KN(a){if(null===a.Bv)throw XH("No match available");return a.Bv} function cq(a,b){this.lK=a;this.UQ=b;this.VQ=0;this.Av=this.UQ;this.YF=0;this.Bv=null;this.qy=0}cq.prototype=new p;cq.prototype.constructor=cq;function bq(a){a.YF=0;a.Bv=null;a.qy=0;a.Bv=a.lK.fR.exec(a.Av);return null!==a.Bv}function LN(a){var b=a.lK;var c=a.Av;var d=b.sK;d.lastIndex=a.YF;c=d.exec(c);b=b.sK.lastIndex|0;a.YF=null!==c?b===(c.index|0)?1+b|0:b:1+a.Av.length|0;a.Bv=c;return null!==c}function MN(a){return(KN(a).index|0)+a.VQ|0}function NN(a){var b=MN(a);a=KN(a)[0];return b+a.length|0} cq.prototype.$classData=q({E2:0},!1,"java.util.regex.Matcher",{E2:1,g:1,bba:1});function Fk(a,b,c,d,e,g,h){this.fR=this.sK=null;this.eR=a;this.Q2=d;this.R2=e;this.O2=g;this.P2=h;this.sK=new RegExp(c,this.Q2+(this.R2?"gy":"g"));this.fR=new RegExp("^(?:"+c+")$",d)}Fk.prototype=new p;Fk.prototype.constructor=Fk;Fk.prototype.u=function(){return this.eR};Fk.prototype.$classData=q({F2:0},!1,"java.util.regex.Pattern",{F2:1,g:1,l:1}); var Uda=function Tda(a){if(a instanceof um){var c=a.Tn;a=Tda(a.Un);return new z(c,a)}if(a instanceof ym)return c=a.dn,a=O().c,new z(c,a);if(zm()===a)return O().c;throw new w(a);},Wda=function Vda(a,b){if(a instanceof um){var d=a.Tn,e=a.Un;return(b?"":"; ")+Zz(a.Xo,!1)+" \x3d\x3e "+Zz(d,!1)+Vda(e,!1)}if(a instanceof ym)return(b?"":"; ")+"_ \x3d\x3e "+Zz(a.dn,!1);if(zm()===a)return"";throw new w(a);},Yda=function Xda(a,b){if(a instanceof um){var d=a.Xo,e=a.Tn;a=a.Un;return""+ON(b)+aA(d,!1,b)+" \x3d\x3e "+ aA(e,!1,b)+Xda(a,b)}if(a instanceof ym)return d=a.dn,ON(b)+"_ \x3d\x3e "+aA(d,!1,b);if(zm()===a)return"";throw new w(a);};function Kca(a){return!!(a&&a.$classData&&a.$classData.rb.AM)}function PN(a){this.ob=null;if(null===a)throw null;this.ob=new QN(a,ap(),ap())}PN.prototype=new p;PN.prototype.constructor=PN;PN.prototype.$classData=q({BU:0},!1,"mlscript.ConstraintSolver$Shadows$",{BU:1,g:1,l:1});function RN(){this.ld=null}RN.prototype=new kz;RN.prototype.constructor=RN;function SN(){} SN.prototype=RN.prototype;function TN(){}TN.prototype=new p;TN.prototype.constructor=TN;function kr(a,b,c,d){return new Ff(UN(b.e().h(),c),b,d)}TN.prototype.$classData=q({PU:0},!1,"mlscript.ErrorReport$",{PU:1,g:1,l:1});var VN;function jr(){VN||(VN=new TN);return VN}function WN(){this.Cg=null;XN=this;this.Cg=new St(!1,!1,!1)}WN.prototype=new p;WN.prototype.constructor=WN;WN.prototype.$classData=q({UU:0},!1,"mlscript.FldFlags$",{UU:1,g:1,l:1});var XN;function tm(){XN||(XN=new WN);return XN} function Zda(a){if(a instanceof $t){var b=a.ap,c=k=>{if(k instanceof Ud)k=k.fa;else{if(!(k instanceof fe))throw new w(k);k=k.aa}return k};if(b===u())return u();a=b.e();var d=a=new z(c(a),u());for(b=b.f();b!==u();){var e=b.e();e=new z(c(e),u());d=d.p=e;b=b.f()}return a}if(a instanceof Ut)return c=a.Km,a=a.Lm,d=O().c,new z(c,new z(a,d));if(a instanceof gu)return c=a.ws,a=O().c,new z(c,a);if(a instanceof Ss)return c=a.xu,d=a.yu,a=a.wu,b=O().c,new z(c,new z(d,new z(a,b)));if(a instanceof Tt)return c= a.Wn,d=a.Xn,a=a.Yn,b=O().c,new z(c,new z(d,new z(a,b)));if(a instanceof mu){c=a.xs;a=a.ys;for(b=d=null;a!==u();){var g=a.e();e=g.h();g=g.j();var h=O().c;for(e=new Om(new z(e,new z(g,h)));e.s();)g=new z(e.t(),u()),null===b?d=g:b.p=g,b=g;a=a.f()}a=null===d?u():d;return new z(c,a)}throw new w(a);} var $da=function YN(a){if(a instanceof Ut){var c=a.Lm;return"("+Zz(a.Km,!1)+") then "+Zz(c,!1)}if(a instanceof gu)return"else "+Zz(a.ws,!1);if(a instanceof $t)return a=a.ap.m(),a=new Ef(a,new y(e=>{if(e instanceof Ud)return e.fa.Wr();if(!(e instanceof fe))throw new w(e);return YN(e.aa)})),"\u2039"+ze(a,"","; ","")+"\u203a";if(a instanceof Tt){c=a.Xn;var d=a.Yn;return Zz(a.Wn,!1)+" "+Zz(c,!1)+" "+YN(d)}if(a instanceof mu)return c=a.ys,a=Zz(a.xs,!1),c=c.m(),c=new Ef(c,new y(e=>{if(null!==e){var g=e.j(); return"\u00b7 "+Zz(e.h(),!1)+" "+YN(g)}throw new w(e);})),a+" \u2039"+ze(c,"","; ","")+"\u203a";if(a instanceof Ss)return c=a.yu,d=a.wu,(a.Qw?"rec ":"")+"let "+Zz(a.xu,!1)+" \x3d "+Zz(c,!1)+" in "+YN(d);throw new w(a);},aea=function ZN(a,b){if(a instanceof Ut){var d=a.Km;a=a.Lm;return aA(d,!(d instanceof wm),b)+" then "+aA(a,!1,b)}if(a instanceof gu)return"else "+aA(a.ws,!1,b);if(a instanceof $t){var e=a.ap;a=k=>{if(k instanceof Ud)k=k.fa;else{if(!(k instanceof fe))throw new w(k);k=k.aa}return k}; if(e===u())a=u();else{d=e.e();var g=d=new z(a(d),u());for(e=e.f();e!==u();){var h=e.e();h=new z(a(h),u());g=g.p=h;e=e.f()}a=d}b=ON(b);return""+ze(a,"",b,"")}if(a instanceof Tt)return d=a.Xn,g=a.Yn,aA(a.Wn,!1,b)+" "+aA(d,!1,b)+" "+ZN(g,b);if(a instanceof mu)return d=a.ys,a=aA(a.xs,!1,b),d=d.m(),d=new Ef(d,new y(k=>{if(null!==k)return"\u00b7 "+k.h()+" "+k.j();throw new w(k);})),b=ON(b),a+" "+ze(d,"",b,"");if(a instanceof Ss)return d=a.yu,g=a.wu,(a.Qw?"rec ":"")+"let "+aA(a.xu,!1,b)+" \x3d "+aA(d,!1, b)+" in "+ZN(g,b);throw new w(a);};function $N(){this.gD=null;aO=this;this.gD=bO().Ht()}$N.prototype=new p;$N.prototype.constructor=$N; function bO(){return Dz(Ez(),J(new K,[G(new H,"**",14),G(new H,"*",13),G(new H,"/",13),G(new H,"%",13),G(new H,"+",12),G(new H,"-",12),G(new H,"\x3c\x3c",11),G(new H,"\x3e\x3e",11),G(new H,"\x3e\x3e\x3e",11),G(new H,"\x3c",10),G(new H,"\x3c\x3d",10),G(new H,"\x3e",10),G(new H,"\x3e\x3d",10),G(new H,"in",10),G(new H,"instanceof",10),G(new H,"\x3d\x3d",9),G(new H,"!\x3d",9),G(new H,"\x3d\x3d\x3d",9),G(new H,"!\x3d\x3d",9),G(new H,"\x26",8),G(new H,"^",7),G(new H,"|",6),G(new H,"\x26\x26",5),G(new H, "||",4),G(new H,"??",4),G(new H,",",1)]))}$N.prototype.$classData=q({AV:0},!1,"mlscript.JSBinary$",{AV:1,g:1,l:1});var aO;function Lm(){aO||(aO=new $N);return aO}function cO(){this.Az=2}cO.prototype=new p;cO.prototype.constructor=cO;cO.prototype.$classData=q({JV:0},!1,"mlscript.JSCommaExpr$",{JV:1,g:1,l:1});var dO;function Vp(){dO||(dO=new cO);return dO}function eO(){}eO.prototype=new Jp;eO.prototype.constructor=eO;function fO(){}fO.prototype=eO.prototype;eO.prototype.VJ=function(){return!1}; function rn(a){return new Io((t(),new L(a)))}function zz(a,b){return new Bo(new Lo(a,b))}function Tba(a,b,c){c=c.Ja(new y(d=>{if(null!==d)return new gO(d.h(),d.j());throw new w(d);})).ha();t();return new hO(a,c,new L(new iO(b)))}function Up(a,b){return a.sh(){var h=t().d;return G(new H,g,h)};if(a===u())b=u();else{var c=a.e(),d=c=new z(b(c),u());for(a=a.f();a!==u();){var e=a.e();e=new z(b(e),u());d=d.p=e;a=a.f()}b=c}return new Eo(b)}jO.prototype.$classData=q({ZV:0},!1,"mlscript.JSLetDecl$",{ZV:1,g:1,l:1});var kO;function Jo(){kO||(kO=new jO)}function lO(){}lO.prototype=new p;lO.prototype.constructor=lO; function Mp(a,b){a=b.length;for(var c=new zc(a),d=0;d=g&&!us(Pr(),g)?String.fromCharCode(g):ida(Q(),J(new K,[g]))}c.a[e]=g;d=1+d|0}return ze(new Fu(c),'"',"",'"')}lO.prototype.$classData=q({aW:0},!1,"mlscript.JSLit$",{aW:1,g:1,l:1});var mO;function Np(){mO||(mO=new lO);return mO} function nO(){}nO.prototype=new Jp;nO.prototype.constructor=nO;function oO(){}oO.prototype=nO.prototype;function pO(){}pO.prototype=new Jp;pO.prototype.constructor=pO;function qO(){}qO.prototype=pO.prototype; function Af(){this.CH=this.DH=this.Cs=this.di=null;eE();var a=new iE;fE(a,t().d);t();var b=J(new K,"true false NaN id emptyArray succ error length concat join add sub mul div gt not ne eq sgt slt sge sle typeof toString String negate eq unit log run Const freshName Lam Var App IntLit StrLit DecLit UnitLit Rcd Bra Sel Blk Tup Fld Let Subs With Quoted CaseOf Case Wildcard NoCases discard window".split(" "));for(b=Pd(u(),b);!b.b();){var c=b.e();vp(a,new go(c,c));b=b.f()}b=new nn("anything",O().c,gl()); a.ko.bj(b.er,b);b=new nn("nothing",O().c,el());a.ko.bj(b.er,b);t();b=J(new K,["int","number","bool","string","unit"]);for(b=Pd(u(),b);!b.b();)c=b.e(),c=new nn(c,O().c,new Ep(c)),a.ko.bj(c.er,c),b=b.f();b=new mn("Annotation","Annotation",O().c,new Tn(O().c),O().c);yp(a,b);this.di=a;this.Cs=(Cz(),new oz);this.DH=Uo(this.di,"results");this.CH=Uo(this.di,"prettyPrint");ho(this.Cs,"prettyPrint",this.CH)}Af.prototype=new eo;Af.prototype.constructor=Af; function taa(a,b){var c=b.Xz;Od();var d=new fp;Od();for(var e=new fp,g=c;!g.b();){var h=g.e();if(h&&h.$classData&&h.$classData.rb.ce){var k=h;t();var l=new Ud(k)}else if(h instanceof Zn){var m=h;t();l=new Ud(m)}else{h instanceof yo||xm("Program reached and unexpected state.");var n=h;t();l=new fe(n)}if(l instanceof fe)wp(d,l.aa);else if(l instanceof Ud)wp(e,l.fa);else throw new w(l);g=g.f()}var r=d.ha(),v=e.ha(),x=a.di,A=O().c,B=dE(x,"TypingUnit");Od();var C=new fp;Od();for(var D=new fp,F=A;!F.b();){var I= F.e();a:{if(I instanceof Zn){var M=I,N=M.wd,P=M.Rb,T=M.Ch,Y=M.Yc;if(null!==P){var Z=P.x;if(Y instanceof fe){var S=Y.aa;if(N.b()||(N.b()?0:N.o())){O();var ea=new Dp(!(N.b()||!N.o()),new Ep(B),new vl(Z),T,(O(),new fe(S)));var ia=new fe(ea);break a}}}}O();ia=new Ud(I)}if(ia instanceof fe)wp(C,ia.aa);else if(ia instanceof Ud)wp(D,ia.fa);else throw new w(ia);F=F.f()}var X=C.ha(),sa=D.ha();Od();var Ja=new fp;Od();for(var Xa=new fp,Fa=sa;!Fa.b();){var za=Fa.e();a:{if(za instanceof Zn){var Qa=za,Ma=Qa.wd, Ga=Qa.Rb,ab=Qa.Ch,Hb=Qa.Yc;if(null!==Ga){var bc=Ga.x;if(Hb instanceof Ud){var yb=Hb.fa;if(Ma.b()||(Ma.b()?0:Ma.o())){O();var tb=new Dp(!(Ma.b()||!Ma.o()),new Ep(B),new vl(bc),ab,(O(),new Ud(yb)));var eb=new fe(tb);break a}}}}O();eb=new Ud(za)}if(eb instanceof fe)wp(Ja,eb.aa);else if(eb instanceof Ud)wp(Xa,eb.fa);else throw new w(eb);Fa=Fa.f()}var kb=Ja.ha(),Rb=Xa.ha(),Gb=new ln(B,O().c,new Tn(O().c),X,kb,Rb,O().c,r,t().d);yp(x,Gb);var vb=Im(a.di,"typing_unit",new L(!1),!1,t().d),Tb=a.di,Nb=Go(a,Gb, t().d,!0,Tb),ic=new qn(vb.re,new $m(new km(Gb.lj))),Va=r;a:for(var cb;;)if(Va.b()){cb=u();break}else{var zb=Va.e(),Ub=Va.f();if(!1===!!zb.fl.b())Va=Ub;else for(var jb=Va,db=Ub;;){if(db.b())cb=jb;else{if(!1!==!!db.e().fl.b()){db=db.f();continue}for(var ub=db,Aa=new z(jb.e(),u()),va=jb.f(),Ra=Aa;va!==ub;){var rb=new z(va.e(),u());Ra=Ra.p=rb;va=va.f()}for(var xb=ub.f(),mc=xb;!xb.b();){if(!1===!!xb.e().fl.b()){for(;mc!==xb;){var Ha=new z(mc.e(),u());Ra=Ra.p=Ha;mc=mc.f()}mc=xb.f()}xb=xb.f()}mc.b()||(Ra.p= mc);cb=Aa}break a}}if(cb===u())var Ka=u();else{for(var Oa=cb.e(),Na=new z(bp(Oa,vb.re),u()),Da=Na,ta=cb.f();ta!==u();){var Ya=ta.e(),dc=new z(bp(Ya,vb.re),u());Da=Da.p=dc;ta=ta.f()}Ka=Na}for(var ka=new km(a.DH),ya=u(),Sa=ep(new fp,ya),xc=new qn(a.DH,new Wo(O().c)),Sb=new z(Nb,new z(ic,Ka)),uc=v,Lb=null,lc=null;uc!==u();){var Xb=uc.e(),ec=!1,Ab=null;a:{if(Xb instanceof Zn){ec=!0;Ab=Xb;var Ob=Ab.wd,fb=Ab.Rb,Wa=Ab.hj,bb=Ab.Yc;if(null!==fb){var Ia=fb.x;if(bb instanceof fe){var Ua=bb.aa,pc=!(!Ob.b()&& !Ob.o()),sc=Ob.b();if(Wa.b())var Ba=R();else{var ob=Wa.o();Ba=new L(ob.x)}if(pc){var nc=sc?R():new L(!0),Ib=Im(a.di,Ia,nc,Co(new Do(a,Ua)),t().d),vc=Zm(a,Ua,a.di),Vb=a.di,fc=Ib;Vb.ko.JB(fc.jJ);Vb.Ip.IB(fc.re);var Bc=sc?R():new L(!1),Pb=Im(a.di,Ia,Bc,Co(new Do(a,Ua)),Ba),Jb=vc,gc=Pb}else{var Cb=Zm(a,Ua,a.di),cc=sc?R():new L(!1),yc=Im(a.di,Ia,cc,Co(new Do(a,Ua)),Ba);Jb=Cb;gc=yc}var Mc=Jb,qc=gc,oc=qc.hJ.b()&&!qc.iJ?new oo(O().c,(t(),new fe(Mc))):Mc;wp(Sa,qc.re);var Qc=a.di.lo,jc=new qn(qc.re,oc),sb= om(Al(),ka,"push"),Gc=new km(qc.re),Wb=O().c,Cc=new Bo(new cn(sb,new z(Gc,Wb))),Fc=O().c;var qd=Qo(Qc,new z(jc,new z(Cc,Fc)));break a}}}if(ec){var Yb=Ab.Yc;if(null!==Ab.Rb&&Yb instanceof Ud){qd=O().c;break a}}if(Xb instanceof No||Xb instanceof Oo||Xb instanceof Po)throw new gm("Def and TypeDef are not supported in NewDef files.");if(Xb instanceof Pm){var Nc=Xb,ad=Zm(a,Nc,a.di),Uc=Nc,cd=wf(vf(),O().c,!0,"'");var kc=aA(Uc,!1,cd);wp(Sa,kc);var Vc=a.di.lo,Hc=om(Al(),ka,"push"),rc=O().c,sd=new Bo(new cn(Hc, new z(ad,rc))),Kc=O().c;qd=Qo(Vc,new z(sd,Kc))}else throw new w(Xb);}for(var Qd=qd.m();Qd.s();){var Ad=new z(Qd.t(),u());null===lc?Lb=Ad:lc.p=Ad;lc=Ad}uc=uc.f()}var kd=null===Lb?u():Lb,Hd=un(Sb,kd),Rd=new z(xc,Hd),Bd=om(Al(),ka,"map"),ae=J(new K,[new km(a.CH)]),dd=rn(new cn(Bd,(Od(),Pd(u(),ae)))),od=O().c,Ta=new z(dd,od),wb=t().d,$a=O().c;t();for(var wa,hb=a.Cs,ra=hb.Yz,wc=rO().Eb(),ac=ra.m();ac.s();){var Id=ac.t();if(null===Id)throw new w(Id);var ud=Id.h(),be=Id.j();if(hb.aI.L(ud))var re=t().d;else{var pe= Cz().PN.U(ud);if(pe instanceof L){var bd=pe.k;hb.aI.oh(ud);t();var Rc=bd.dQ(be);re=new L(Rc)}else{if(t().d!==pe)throw new w(pe);re=t().d}}wc.zc(re)}wa=wc.Kb().ha();var Wc=dl(dl(Ta,Rd),wa),Wd=new H;var zd=(new dn(wb,$a,new Ud(Wc),O().c)).xa().$f;if(zd===u())var Pa=u();else{for(var Db=zd.e(),Oc=new z(Db.u(),u()),Tc=Oc,Sd=zd.f();Sd!==u();){var Jc=Sd.e(),vd=new z(Jc.u(),u());Tc=Tc.p=vd;Sd=Sd.f()}Pa=Oc}return G(Wd,Pa,Sa.ha())}Af.prototype.$classData=q({nW:0},!1,"mlscript.JSWebBackend",{nW:1,Baa:1,g:1}); function sO(){}sO.prototype=new p;sO.prototype.constructor=sO;function du(a,b){a=b.m();t();for(b=R();a.s();){var c=b;b=a.t();c.b()?b=b.A():(b=Kt(c.o(),b.A()),b=it().n(b))}return b}sO.prototype.$classData=q({wW:0},!1,"mlscript.Loc$",{wW:1,g:1,l:1});var tO;function cu(){tO||(tO=new sO);return tO}function uO(){}uO.prototype=new p;uO.prototype.constructor=uO;function uaa(a){vf();a=a.m();return wf(0,new xo(a,new y(b=>Wn(b.fp,new vO(b)))),!0,"?")} function wO(a,b){a=new xO(b);b=O().c;return new $q(new z(a,b))}function We(a,b){a=new Vq(b);b=O().c;return new $q(new z(a,b))}uO.prototype.$classData=q({yW:0},!1,"mlscript.Message$",{yW:1,g:1,l:1});var yO;function Xe(){yO||(yO=new uO);return yO}function Ft(a,b,c,d){this.Lz=this.mE=this.dx=this.lE=this.ex=null;this.hx=!1;this.Fu=null;this.Eu=!1;this.jE=this.kE=null;this.le=this.fx=0;this.gx=this.Ol=null;this.Kz=!1;this.wN=null;if(null===a)throw null;this.wN=a;lt(this,a.mE,b,a.hx,a.Fu,a.Eu,c,d)} Ft.prototype=new ot;Ft.prototype.constructor=Ft;Ft.prototype.nQ=function(a){Ds(this.wN,new U(()=>"\x3e "+Es(a)))};Ft.prototype.$classData=q({RW:0},!1,"mlscript.NewParser$$anon$1",{RW:1,PW:1,g:1});function rt(a){this.gj=this.KH=null;if(null===a)throw null;this.gj=a;this.KH=new zO(a,nf())}rt.prototype=new p;rt.prototype.constructor=rt; rt.prototype.HC=function(){var a;a:for(a=this.gj.gx;;){var b=!1,c=null,d=Me(this.gj,new Ne(271),new Oe("go"));if(d instanceof z){b=!0;c=d;var e=c.z;if(null!==e){var g=e.h();e=e.j();if(g instanceof as&&(g=g.Na,a.Nl.L(g))){d=this.gj;b=new Te(new Ue(J(new K,["Repeated modifier `","`"])));c=[We(Xe(),g)];b=Ye(b,J(new K,c));t();b=G(new H,b,new L(e));c=O().c;Ze(d,new z(b,c));Ps(this.gj,new Ne(274),new Oe("go"));Os(this.gj);continue}}}if(b&&(g=c.z,null!==g&&(e=g.h(),g=g.j(),e instanceof as&&"declare"===e.Na))){Ps(this.gj, new Ne(278),new Oe("go"));Os(this.gj);d=a;a=a.Nl.Pn(G(new H,"declare",g));a=new zO(d.vq,a);continue}if(b&&(g=c.z,null!==g&&(e=g.h(),g=g.j(),e instanceof as&&"virtual"===e.Na))){Ps(this.gj,new Ne(282),new Oe("go"));Os(this.gj);d=a;a=a.Nl.Pn(G(new H,"virtual",g));a=new zO(d.vq,a);continue}if(b&&(g=c.z,null!==g&&(e=g.h(),g=g.j(),e instanceof as&&"mut"===e.Na))){Ps(this.gj,new Ne(286),new Oe("go"));Os(this.gj);d=a;a=a.Nl.Pn(G(new H,"mut",g));a=new zO(d.vq,a);continue}if(b&&(g=c.z,null!==g&&(e=g.h(),g= g.j(),e instanceof as&&"abstract"===e.Na))){Ps(this.gj,new Ne(290),new Oe("go"));Os(this.gj);d=a;a=a.Nl.Pn(G(new H,"abstract",g));a=new zO(d.vq,a);continue}if(a.Nl.b())break a;if(b&&(e=c.z,null!==e&&(e=e.h(),e instanceof as&&(e=e.Na,"class"===e||"infce"===e||"trait"===e||"mixin"===e||"type"===e||"namespace"===e||"module"===e||"fun"===e||"val"===e||"let"===e))))break a;if(b&&(b=c.z,null!==b)){c=b.h();b=b.j();d=this.gj;e=new Te(new Ue(J(new K,["Unexpected "," token after modifier",""])));c=[We(Xe(), c.jb()),0{var e=a.Zf;d=new iw(a.Zf,d.h(),d.j());var g=uv(),h=Su(),k=op().ga;g=g.ng(new Uu(h,k));h=Vu(a.Zf);k=uv();var l=Su(),m=op().ga;return new CO(e,d,g,h,k.ng(new Uu(l,m)))}));Od();return new DO(c,Pd(u(),b))} function EO(a,b){return b?new DO(a.Zf,O().c):BO(a,lw(a.Zf))} function FO(a,b,c,d,e,g,h,k,l){for(;;){var m=!1,n=null,r=d;if(r instanceof Fv){a:{var v=a,x=r,A=tc();try{var B=GO(v.Zf),C=zw(lw(v.Zf),x);if(C.b())throw Hq(new Iq,A,EO(GO(v.Zf),!0));var D=BO(B,C.o())}catch(Cc){if(Cc instanceof Iq){var F=Cc;if(F.Qg===A){D=F.Cj();break a}throw F;}throw Cc;}}return D}if(r instanceof Gv){var I=r,M=a.Zf,N=O().c,P=new z(I,N),T=t().d,Y=sv(),Z=Su(),S=op().ga;return BO(a,new jw(M,P,T,Y.Hd(new Uu(Z,S))))}if(r instanceof Qv)return bea(a,r);if(r instanceof FA)return EO(a,!r.Eh); if(r instanceof LA){var ea=r,ia=ea.jc,X=ea.tc,sa=FO(a,b,c,ea.ic,e,g,h,k,l),Ja=FO(a,b,c,ia,e,g,h,k,l);if(X){var Xa=sa,Fa=X,za=g,Qa=k,Ma=Ja.wq;if(Ma===u())var Ga=u();else{for(var ab=Ma.e(),Hb=new z(HO(Xa,ab,Fa,za,Qa),u()),bc=Hb,yb=Ma.f();yb!==u();){var tb=yb.e(),eb=new z(HO(Xa,tb,Fa,za,Qa),u());bc=bc.p=eb;yb=yb.f()}Ga=Hb}for(var kb=EO(GO(Xa.ix),!1),Rb=Ga;!Rb.b();){var Gb=kb,vb=Rb.e();kb=IO(Gb,vb);Rb=Rb.f()}var Tb=kb}else Tb=IO(sa,Ja);return Tb}if(r instanceof MA){var Nb=r.Fc,ic=a.Zf,Va=yB(xB(a.Zf), b,c,Nb,!e,g,h,k,l).xe;if(Va===u())var cb=u();else{for(var zb=Va.e(),Ub=new z(new CO(zb.zb,zb.Of,zb.Af,zb.Nf,zb.Pf),u()),jb=Ub,db=Va.f();db!==u();){var ub=db.e(),Aa=new z(new CO(ub.zb,ub.Of,ub.Af,ub.Nf,ub.Pf),u());jb=jb.p=Aa;db=db.f()}cb=Ub}return new DO(ic,cb)}if(r instanceof lx){m=!0;n=r;iC(a.Zf);var va=n.Sb;if(!va.b()){var Ra=va.o();if(!vD(h)&&!l.L(n)){var rb=l.bc(n),xb=b,mc=c,Ha=rb;return FO(GO(a.Zf),xb,mc,Ra,e,g,h,k,Ha)}}}if(m){uv();var Ka=n,Oa=Su(),Na=op().ga,Da=void 0,ta=void 0,Ya=vv(Ka,new Uu(Oa, Na)),dc=a.Zf,ka=a.Zf;null===ka.Bq&&null===ka.Bq&&(ka.Bq=new JO(ka));ta=ka.Bq;var ya=ta.oE,Sa=lw(ta.oE),xc=Vu(ta.oE),Sb=uv(),uc=Su(),Lb=op().ga;Da=new CO(ya,Sa,Ya,xc,Sb.ng(new Uu(uc,Lb)));var lc=O().c;return new DO(dc,new z(Da,lc))}if(r instanceof ZB){var Xb=r;$B(a.Zf);t();var ec=Xb.mc(),Ab=new L(ec);if(!Ab.b()){d=Ab.k;continue}}if(r instanceof fw){var Ob=r,fb=Ob.qb;if(vD(h)&&!a.Zf.cn.L(fb.V)||!PA(Ob,g)){var Wa=a.Zf,bb=a.Zf,Ia=a.Zf,Ua=O().c,pc=t().d;sv();var sc=G(new H,fb,Ob),Ba=Su(),ob=op().ga,nc= new jw(Ia,Ua,pc,$v(sc,new Uu(Ba,ob))),Ib=uv(),vc=Su(),Vb=op().ga,fc=Ib.ng(new Uu(vc,Vb)),Bc=Vu(a.Zf),Pb=uv(),Jb=Su(),gc=op().ga,Cb=new CO(bb,nc,fc,Bc,Pb.ng(new Uu(Jb,gc))),cc=O().c;return new DO(Wa,new z(Cb,cc))}d=QA(Ob,g)}else if(r instanceof cC){var yc=r,Mc=yc.Fg,qc=yc.Sf;d=e?qc:Mc}else if(r instanceof Qx){var oc=r,Qc=oc.Re;b=oc.de;d=Qc}else if(r instanceof eC){var jc=r,sb=jc.kj,Gc=g.da,Wb=dl(c,jc.Lj);b=Gc;c=Wb;d=sb}else throw new w(r);}} AO.prototype.$classData=q({XW:0},!1,"mlscript.NormalForms$CNF$",{XW:1,g:1,l:1});function KO(a){this.xq=this.nE=null;if(null===a)throw null;this.xq=a}KO.prototype=new p;KO.prototype.constructor=KO;function ZA(a){null===a.nE&&null===a.nE&&(a.nE=new LO(a));return a.nE}KO.prototype.$classData=q({ZW:0},!1,"mlscript.NormalForms$Conjunct$",{ZW:1,g:1,l:1}); var dea=function cea(a,b,c,d,e,g,h){if(Pe(new E(c),d))return UA(d,b,!1,new fn((l,m)=>cea(a,l,m,d,e,g,h)),e);if(b instanceof L)return MO(a,g,h,c,!!b.k,e,!0,!1);if(t().d===b)return dw(cw(a.Bf),MO(a,g,h,c,!1,e,!0,!1),MO(a,g,h,c,!0,e,!0,!1),ew(cw(a.Bf)),e);throw new w(b);};function NO(a){this.Bf=null;if(null===a)throw null;this.Bf=a}NO.prototype=new p;NO.prototype.constructor=NO; function OO(a,b,c,d){var e=a.Bf,g=uv(),h=Su(),k=op().ga;g=g.ng(new Uu(h,k));h=lw(a.Bf);k=uv();var l=Su(),m=op().ga;d=new PO(e,d,g,h,k.ng(new Uu(l,m)));e=O().c;return QO(a,b,c,new z(d,e))}function QO(a,b,c,d){a=new RO(a.Bf,b,c,d);0===(4&a.Nm)<<24>>24&&0===(4&a.Nm)<<24>>24&&(a.zN=qB(a)?a.fb:a.Ea(),a.Nm=(4|a.Nm)<<24>>24);c=a.zN;return cdea(a,n,r,d,g,b,c)),g)} function yB(a,b,c,d,e,g,h,k,l){for(;;){var m=!1,n=null,r=e?Cca(d,g,h):d;if(r instanceof Fv){var v=r,x=xB(a.Bf),A=b,B=c,C=a.Bf;t();var D=new L(v),F=uv(),I=Su(),M=op().ga,N=F.ng(new Uu(I,M)),P=Ev(k)?v.kq():wv(xv(a.Bf)),T=sv(),Y=Su(),Z=op().ga;return OO(x,A,B,new Ku(C,D,N,P,T.Hd(new Uu(Y,Z))))}if(r instanceof Gv){var S=r,ea=xB(a.Bf),ia=b,X=c,sa=a.Bf,Ja=t().d;uv();var Xa=Su(),Fa=op().ga,za=vv(S,new Uu(Xa,Fa)),Qa=wv(xv(a.Bf)),Ma=sv(),Ga=Su(),ab=op().ga;return OO(ea,ia,X,new Ku(sa,Ja,za,Qa,Ma.Hd(new Uu(Ga, ab))))}if(r instanceof Qv){var Hb=r,bc=xB(a.Bf),yb=b,tb=c,eb=a.Bf,kb=t().d,Rb=uv(),Gb=Su(),vb=op().ga,Tb=Rb.ng(new Uu(Gb,vb)),Nb=sv(),ic=Su(),Va=op().ga;return OO(bc,yb,tb,new Ku(eb,kb,Tb,Hb,Nb.Hd(new Uu(ic,Va))))}if(r instanceof FA)return SO(a,!r.Eh);if(r instanceof LA){var cb=r,zb=cb.jc,Ub=e,jb=yB(a,b,c,cb.ic,e,g,h,k,l),db=yB(a,b,c,zb,e,g,h,k,l),ub=g,Aa=k;return cb.tc?UO(jb,db,ub,Aa):eea(jb,db,Ub,ub,Aa)}if(r instanceof MA){var va=r.Fc,Ra=xB(a.Bf),rb=b,xb=c,mc=FO(GO(a.Bf),b,O().c,va,!e,g,h,k,l).wq; if(mc===u())var Ha=u();else{for(var Ka=mc.e(),Oa=new z(new PO(Ka.Is,Ka.Js,Ka.Ks,Ka.Ls,Ka.Ms),u()),Na=Oa,Da=mc.f();Da!==u();){var ta=Da.e(),Ya=new z(new PO(ta.Is,ta.Js,ta.Ks,ta.Ls,ta.Ms),u());Na=Na.p=Ya;Da=Da.f()}Ha=Oa}return QO(Ra,rb,xb,Ha)}if(r instanceof lx){m=!0;n=r;iC(a.Bf);var dc=n.Sb;if(!dc.b()){var ka=dc.o();if(!vD(h)&&!l.L(n)){var ya=l.bc(n),Sa=b,xc=c,Sb=ya;return yB(xB(a.Bf),Sa,xc,ka,e,g,h,k,Sb)}}}if(m){var uc=xB(a.Bf),Lb=b,lc=c,Xb=$A(a.Bf);uv();var ec=n,Ab=Su(),Ob=op().ga,fb=Xb,Wa=vv(ec, new Uu(Ab,Ob)),bb=fb.xq,Ia=Vu(fb.xq),Ua=lw(fb.xq),pc=uv(),sc=Su(),Ba=op().ga;var ob=new PO(bb,Ia,Wa,Ua,pc.ng(new Uu(sc,Ba)));var nc=O().c;return QO(uc,Lb,lc,new z(ob,nc))}if(r instanceof ZB){var Ib=r;$B(a.Bf);t();var vc=Ib.mc(),Vb=new L(vc);if(!Vb.b()){d=Vb.k;continue}}if(r instanceof fw){var fc=r,Bc=fc.qb;if(vD(h)&&!a.Bf.cn.L(Bc.V)||!PA(fc,g)){var Pb=b,Jb=c,gc=a.Bf,Cb=gw(fc,g),cc=uv(),yc=Su(),Mc=op().ga,qc=cc.ng(new Uu(yc,Mc)),oc=wv(xv(a.Bf)),Qc=sv(),jc=[G(new H,Bc,fc)],sb=J(new K,jc),Gc=Su(),Wb= op().ga;return OO(a,Pb,Jb,new Ku(gc,Cb,qc,oc,Qc.CF(sb,new Uu(Gc,Wb))))}d=QA(fc,g)}else if(r instanceof cC){var Cc=r,Fc=Cc.Fg,qd=Cc.Sf;d=e?qd:Fc}else if(r instanceof Qx){var Yb=r,Nc=Yb.Re;b=Yb.de;d=Nc}else if(r instanceof eC){var ad=r,Uc=ad.kj;c=dl(c,ad.Lj);d=Uc}else throw new w(r);}}NO.prototype.$classData=q({bX:0},!1,"mlscript.NormalForms$DNF$",{bX:1,g:1,l:1});function JO(a){this.oE=null;if(null===a)throw null;this.oE=a}JO.prototype=new p;JO.prototype.constructor=JO; JO.prototype.$classData=q({dX:0},!1,"mlscript.NormalForms$Disjunct$",{dX:1,g:1,l:1});function cy(a){if(a instanceof Zn)return"definition";if(a instanceof yo)return"type declaration";throw new w(a);} function fea(a){var b=!1,c=null;if(a instanceof Zn){b=!0;c=a;var d=c.wd,e=c.Rb,g=c.hj;if(t().d===d)return"fun"+(g.b()?"":" ("+g.o().x+")")+" "+e.x}if(b&&(d=c.wd,e=c.Rb,g=c.hj,d instanceof L&&!1===!!d.k))return"let"+(g.b()?"":" "+g.o().x+")")+" "+e.x;if(b&&(b=c.wd,d=c.Rb,c=c.hj,b instanceof L&&!0===!!b.k))return"let rec"+(c.b()?"":" "+c.o().x+")")+" "+d.x;if(a instanceof yo){d=a.pb;var h=a.hg;g=a.Sg;e=a.Hj;b=a.Di;c=d.ld;a=a.gb.V;if(h.b())var k="";else{if(h===u())k=u();else{k=h.e();var l=k=new z(k.j().V, u());for(h=h.f();h!==u();){var m=h.e();m=new z(m.j().V,u());l=l.p=m;h=h.f()}}k=ze(k,"\u2039",", ","\u203a")}g.b()?g="":(g=g.o(),g="("+Yz(g)+")");e.b()?e="":(e=e.o(),e=": "+VO(e,!0));d=b.b()?"":Pe(new E(d),Ap())?" \x3d ":": ";b=b.m();b=new Ef(b,new y(n=>Zz(n,!1)));return c+" "+a+k+g+e+d+ze(b,"",", ","")}throw new w(a);} function gea(a){if(a instanceof yo&&Ot(new E(a.pb),Bp())){var b=a.Sg;if(b.b())return R();var c=b.o();b=new vl("_");a=new Cl(new vl("x"),new Ep(a.Cf.x));var d=c.Ra;c=k=>{if(null!==k){var l=new L(k);if(!l.b()&&(l=l.k.h(),l instanceof L)){var m=l.k;k=t().d;l=tm().Cg;var n=new vl("x"),r=new vl("#"+m.x);m=m.A();l=new sm(l,new Ql(n,Cq(r,m)));return G(new H,k,l)}}if(null!==k&&(l=new L(k),!l.b()&&(k=l.k.h(),l=l.k.j(),t().d===k&&null!==l&&(m=l.ya,m instanceof vl))))return k=t().d,l=tm().Cg,n=new vl("x"),r= new vl("#"+m.x),m=m.A(),l=new sm(l,new Ql(n,Cq(r,m))),G(new H,k,l);xm("Program reached and unexpected state.")};if(d===u())c=u();else{var e=d.e(),g=e=new z(c(e),u());for(d=d.f();d!==u();){var h=d.e();h=new z(c(h),u());g=g.p=h;d=d.f()}c=e}g=new Rl(!1,b,a,new Gl(c));b=t().d;a=new vl("unapply");c=t().d;e=O().c;t();d=t().d;h=new sm(tm().Cg,new vl("x"));d=G(new H,d,h);h=O().c;g=new Ol(new Gl(new z(d,h)),g);return new L(new Zn(b,a,c,e,new fe(g),t().d,t().d,t().d,t().d,t().d,!0,O().c))}return t().d} function WO(a){this.JN=null;this.OH=!1;this.IN=null;if(null===a)throw null;this.IN=a}WO.prototype=new p;WO.prototype.constructor=WO;WO.prototype.$classData=q({yX:0},!1,"mlscript.NuTypeDefs$RefMap$",{yX:1,g:1,l:1});function XO(a,b,c){if(0<=a.length&&"'"===a.substring(0,1))var d=!0;else d=pE().SP,d=0<=a.length&&a.substring(0,d.length)===d;b=d?"":b;d=c.U(a);if(d instanceof L)return d=d.k|0,c.bh(a,1+d|0),""+b+a+d;if(t().d===d)return c.bh(a,0),""+b+a;throw new w(d);}function YO(){}YO.prototype=new p; YO.prototype.constructor=YO; function wf(a,b,c,d){a=b.m();Od();a=Pd(u(),a);for(var e=b=null;a!==u();){for(var g=ZO(a.e()).m();g.s();){var h=new z(g.t(),u());null===e?b=h:e.p=h;e=h}a=a.f()}a=null===b?u():b;a=hl(a);a=Pt(a,new y(n=>{var r=n.pp;if(r instanceof fe)return t(),n=G(new H,n.oA,n),new fe(n);if(r instanceof Ud)return r=r.fa,t(),n=G(new H,r,n),new Ud(n);throw new w(r);}));if(null===a)throw new w(a);g=a.h();b=a.j();Od();e=new fp;Od();for(a=new fp;!g.b();){h=g.e();a:{if(null!==h){var k=h.h(),l=h.j();if(k instanceof L){h=k.k; t();h=G(new H,h,l);h=new fe(h);break a}}if(null!==h&&(l=h.h(),k=h.j(),t().d===l)){t();h=new Ud(k);break a}throw new w(h);}if(h instanceof fe)wp(e,h.aa);else if(h instanceof Ud)wp(a,h.fa);else throw new w(h);g=g.f()}e=e.ha();a=a.ha();var m=Xu().X();h=un(b,e);b=n=>{if(null!==n){var r=n.h();n=n.j();Q();r=0<=r.length&&r.substring(0,d.length)===d?r.substring(d.length):r;r=XO(r,d,m);return G(new H,n,r)}throw new w(n);};if(h===u())b=u();else{e=h.e();g=e=new z(b(e),u());for(h=h.f();h!==u();)l=h.e(),l=new z(b(l), u()),g=g.p=l,h=h.f();b=e}e=Su();g=op().ga;e=new Uu(e,g);b=Ov(sv(),b,e);e=m.zr();O();e=new iy(new $O(0,new y(n=>{n|=0;var r=n/26|0;t();n=G(new H,String.fromCharCode(65535&(97+(n%26|0)|0))+(Pe(new E(r),0)?"":""+r),1+n|0);return new L(n)})),e,!0);e=new Ef(e,new y(n=>XO(n,d,m)));a=Bf(a,e);return new aP(b.hy(a),!1,0,c,!1)}YO.prototype.$classData=q({gY:0},!1,"mlscript.ShowCtx$",{gY:1,g:1,l:1});var bP;function vf(){bP||(bP=new YO);return bP} function cP(a){var b=!1,c=null;if(a instanceof yo){b=!0;c=a;var d=c.pb;zp()===d&&no()}b&&(d=c.pb,cp()===d&&no());if(b){d=c.pb;var e=c.gb,g=c.hg,h=c.Sg,k=c.Hj,l=c.Di,m=c.Ns,n=c.ei;if(Ap()===d){if(!h.b()&&!h.o().Ra.b())throw Kj("requirement failed: "+h);a=l.K();if(!Pe(new E(a),0))throw Kj("requirement failed: "+l);At(tp(),!k.b());if(!m.b())throw Kj("requirement failed: "+m);if(!ef(n).b())throw Kj("requirement failed: "+n);a=O().c;if(g===u())c=u();else for(c=g.e(),b=c=new z(c.j(),u()),g=g.f();g!==u();)l= g.e(),l=new z(l.j(),u()),b=b.p=l,g=g.f();k.b()&&xm("Program reached and unexpected state.");d=new Oo(d,e,c,k.o(),O().c,O().c,O().c,t().d);e=O().c;return G(new H,a,new z(d,e))}}if(b&&(d=c.pb,e=c.gb,k=c.hg,g=c.Sg,b=c.Di,Bp()===d||Fp()===d)){c=g.b()?new Gl(O().c):g.o();g=c.Ra;var r=TE(PE());a=B=>{if(null!==B){var C=B.h(),D=B.j();if(C instanceof L&&(C=C.k,null!==D)){var F=D.yb;D=D.ya;if(null!==F)return B=F.je,D=dP(D,r),B=new Sn(B?(t(),new L(D)):t().d,D),G(new H,C,B)}}if(null!==B&&(C=B.h(),B=B.j(),t().d=== C&&null!==B&&(D=B.yb,C=B.ya,null!==D&&(B=D.je,C instanceof vl))))return B?(t(),B=el(),B=new L(B)):B=t().d,B=new Sn(B,gl()),G(new H,C,B);xm("Program reached and unexpected state.")};if(g===u())l=u();else for(l=g.e(),m=l=new z(a(l),u()),n=g.f();n!==u();)h=n.e(),h=new z(a(h),u()),m=m.p=h,n=n.f();a=op();a=l.Gb(a.ga).h();if(b===u())b=u();else{m=b.e();n=m=new z(dP(m,r),u());for(b=b.f();b!==u();)h=b.e(),h=new z(dP(h,r),u()),n=n.p=h,b=b.f();b=m}b=b.mf(new Tn(l),Un());l=new vl(e.V);m=e.A();l=Cq(l,m);t();m= t().d;n=tm().Cg;h=B=>{if(null!==B){var C=B.h(),D=B.j();if(C instanceof L)return B=C.k,D=new sm(new St(!1,!1,D.yb.Bh),B),G(new H,B,D)}if(null!==B&&(D=B.h(),B=B.j(),t().d===D&&null!==B&&(D=B.ya,D instanceof vl)))return G(new H,D,B);xm("Program reached and unexpected state.")};if(g===u())g=u();else{var v=g.e(),x=v=new z(h(v),u());for(g=g.f();g!==u();){var A=g.e();A=new z(h(A),u());x=x.p=A;g=g.f()}g=v}g=new sm(n,new yl(g));g=G(new H,m,g);m=O().c;c=new Ol(c,new Pl(l,new Gl(new z(g,m))));c=new No(!1,l, new fe(c),!0);g=r.ha();if(k===u())k=u();else{l=k.e();m=l=new z(l.j(),u());for(k=k.f();k!==u();)n=k.e(),n=new z(n.j(),u()),m=m.p=n,k=k.f();k=l}d=new Oo(d,e,k,b,O().c,O().c,a,t().d);e=O().c;return G(new H,g,new z(d,new z(c,e)))}if(sy(a))return d=O().c,e=O().c,G(new H,d,new z(a,e));throw new w(a);} function eP(a){if(a instanceof Ml){var b=a.gs,c=a.Ml,d=O().c;return new z(b,new z(c,d))}if(a instanceof Fl){var e=a.gg,g=O().c;return new z(e,g)}if(a instanceof vl)return O().c;if(a instanceof Cl){var h=a.ai,k=O().c;return new z(h,k)}if(a instanceof Ol){var l=a.Ej,m=a.Fj,n=O().c;return new z(l,new z(m,n))}if(a instanceof Pl){var r=a.Za,v=a.Qb,x=O().c;return new z(r,new z(v,x))}if(a instanceof Gl){var A=a.Ra;if(A===u())return u();for(var B=A.e(),C=new z(B.j().ya,u()),D=C,F=A.f();F!==u();){var I=F.e(), M=new z(I.j().ya,u());D=D.p=M;F=F.f()}return C}if(a instanceof yl){var N=a.ll;if(N===u())return u();for(var P=N.e(),T=new z(P.j().ya,u()),Y=T,Z=N.f();Z!==u();){var S=Z.e(),ea=new z(S.j().ya,u());Y=Y.p=ea;Z=Z.f()}return T}if(a instanceof Ql){var ia=a.Vl,X=a.ml,sa=O().c;return new z(ia,new z(X,sa))}if(a instanceof Rl){var Ja=a.$n,Xa=a.Mm,Fa=O().c;return new z(Ja,new z(Xa,Fa))}if(a instanceof Sl)return a.Dj;if(a instanceof Dl)return O().c;if(a instanceof Tl){var za=a.br,Qa=a.ar,Ma=O().c;return new z(za, new z(Qa,Ma))}if(a instanceof Ul){var Ga=a.Sn,ab=a.Hm,Hb=O().c;return new z(Ga,new z(ab,Hb))}if(a instanceof No){var bc=a.oq,yb=a.RM,tb=O().c;return new z(bc,new z(yb,tb))}if(a instanceof Oo){var eb=a.eA,kb=a.gA,Rb=a.cA,Gb=a.fA,vb=O().c,Tb=dl(dl(new z(Rb,vb),Gb),kb);return new z(eb,Tb)}if(a instanceof Vl){var Nb=a.lp,ic=a.mp,Va=O().c;return new z(Nb,new z(ic,Va))}if(a instanceof Wl){var cb=a.Gm,zb=a.Qn,Ub=O().c;return new z(cb,new z(zb,Ub))}if(a instanceof dm){var jb=a.$q,db=a.Zq,ub=O().c;return new z(jb, new z(db,ub))}if(a instanceof Xl){var Aa=a.bp,va=a.zs.ha();return new z(Aa,va)}if(a instanceof Zn){var Ra=a.Rb,rb=a.Ch,xb=a.hj.ha(),mc=a.EN,Ha=O().c,Ka=dl(dl(new z(mc,Ha),rb),xb);return new z(Ra,Ka)}if(a instanceof Il)return new z(a.nl,a.np);if(a instanceof Yl){var Oa=a.hp,Na=O().c;return new z(Oa,Na)}if(a instanceof cm){var Da=a.Ys,ta=a.Zs,Ya=O().c;return new z(Da,new z(ta,Ya))}if(a instanceof $l)return new z(a.Yq,a.lt);if(a instanceof Zl){var dc=a.qq,ka=a.$o,ya=O().c;return dl(new z(ka,ya),dc)}if(a instanceof Kl){var Sa=a.cp,xc=O().c;return new z(Sa,xc)}if(a instanceof am)return O().c;if(a instanceof Po){var Sb=a.ks,uc=a.js,Lb=O().c;return new z(Sb,new z(uc,Lb))}if(a instanceof bm){var lc=a.Vn,Xb=a.Zo,ec=O().c;return new z(lc,new z(Xb,ec))}if(a instanceof yo){var Ab=a.gb,Ob=a.hg,fb=a.Sg,Wa=a.Di,bb=a.Ns,Ia=a.ei;if(Ob===u())var Ua=u();else{for(var pc=Ob.e(),sc=new z(pc.j(),u()),Ba=sc,ob=Ob.f();ob!==u();){var nc=ob.e(),Ib=new z(nc.j(),u());Ba=Ba.p=Ib;ob=ob.f()}Ua=sc}var vc=fb.ha(),Vb=bb.ha(),fc=O().c,Bc= dl(dl(dl(dl(new z(Ia,fc),Vb),Wa),vc),Ua);return new z(Ab,Bc)}if(a instanceof em){var Pb=a.Iq,Jb=O().c;return new z(Pb,Jb)}if(a instanceof fm){var gc=a.jt,Cb=O().c;return new z(gc,Cb)}throw new w(a);} function Mx(a){if(a instanceof Po){var b=a.js;return"constructor("+Yz(a.ks)+") "+Zz(b,!1)}if(a instanceof Pm)return Zz(a,!1);if(a instanceof fP){a:{var c=!1;b=null;if(a instanceof No){c=!0;b=a;var d=b.oq;if(!0===b.ls){b="rec def "+Zz(d,!1);break a}}if(c&&(c=b.oq,!1===b.ls)){b="def "+Zz(c,!1);break a}if(a instanceof Oo){var e=a.gA;b=a.fA;c=a.dA.ld;d=a.eA.V;if(e.b())var g="";else{if(e===u())g=u();else{g=e.e();var h=g=new z(g.V,u());for(e=e.f();e!==u();){var k=e.e();k=new z(k.V,u());h=h.p=k;e=e.f()}}g= ze(g,"[",", ","]")}b=c+" "+d+g+(b.b()?"":ze(b,"(",", ",")"))}else throw new w(a);}c=a instanceof Oo&&Ot(new E(a.dA),Ap())?" \x3d ":": ";b+=c;if(a instanceof No)if(a=a.vu,a instanceof Ud)a=VO(a.fa,!0);else{if(!(a instanceof fe))throw new w(a);a=Zz(a.aa,!1)}else if(a instanceof Oo)a=VO(a.cA,!0);else throw new w(a);return b+a}if(a instanceof Ct){b=fea(a);if(a instanceof Zn)c=a.Yc.TJ()?" \x3d ":": ";else{if(!(a instanceof yo))throw new w(a);c=" "}b+=c;if(a instanceof Zn)if(a=a.Yc,a instanceof Ud)a=VO(a.fa, !0);else{if(!(a instanceof fe))throw new w(a);a=Zz(a.aa,!1)}else if(a instanceof yo)a=gP(a.ei);else throw new w(a);return b+a}throw new w(a);}function dP(a,b){a=Qn(a);if(a instanceof fe)return b.$(a.aa),gl();if(a instanceof Ud)return a.fa;throw new w(a);} function hea(a){if(a instanceof Tn){var b=a.Xs;if(b===u())return u();var c=b.e();a=c=new z(c.h().x,u());for(b=b.f();b!==u();){var d=b.e();d=new z(d.h().x,u());a=a.p=d;b=b.f()}return c}if(a instanceof hP)return c=a.Bs,a=yn(a.As),c=yn(c),un(a,c);if(a instanceof iP||a instanceof Wt||a instanceof jP||a instanceof kP||a instanceof lP||a instanceof mP||a instanceof nP||gl()===a||el()===a||a instanceof oP||a instanceof jt||a instanceof pP||a instanceof Ep||a instanceof qP||a instanceof rP||a instanceof sP|| a instanceof Vt||a instanceof tP)return O().c;throw new w(a);} function iea(a){if(a instanceof Ep){a=a.V;var b=O().c;return new z(a,b)}if(a instanceof pP&&(b=a.Gw,null!==b))return a=b.V,b=O().c,new z(a,b);if(a instanceof hP)return b=a.Bs,a=zn(a.As),b=zn(b),un(a,b);if(a instanceof iP||a instanceof Wt||a instanceof Tn||a instanceof jP||a instanceof kP||a instanceof lP||a instanceof mP||a instanceof nP||gl()===a||el()===a||a instanceof Vt||a instanceof oP||a instanceof jt||a instanceof qP||a instanceof rP||a instanceof sP||a instanceof tP)return O().c;throw new w(a); }function VO(a,b){vf();var c=O().c;b=wf(0,new z(a,c),b,"'");return yf(a,0,b)}function uP(a,b){return b?"("+a+")":a} function vP(a,b){if(null!==a){var c=a.Yf,d=a.Rg;if(t().d===c)return yf(d,0,b)}if(null!==a&&(c=a.Yf,d=a.Rg,c instanceof L&&Pe(new E(c.k),d)))return yf(d,0,b);if(null!==a&&(d=a.Yf,c=a.Rg,d instanceof L&&(d=d.k,el()===d)))return"out "+yf(c,0,b);if(null!==a&&(d=a.Yf,c=a.Rg,d instanceof L&&(d=d.k,gl()===c)))return"in "+yf(d,0,b);if(null!==a&&(c=a.Yf,d=a.Rg,c instanceof L))return"in "+yf(c.k,0,b)+" out "+yf(d,0,b);throw new w(a);} function wP(a,b){var c=h=>{var k=h.j().Yf.b()?"":"mut ",l=h.h();l=l.b()?"":l.o().x+": ";return k+l+vP(h.j(),b)};if(a===u())return u();var d=a.e(),e=d=new z(c(d),u());for(a=a.f();a!==u();){var g=a.e();g=new z(c(g),u());e=e.p=g;a=a.f()}return d} var yf=function xP(a,b,c){var e=!1,g=null,h=!1,k=null,l=!1,m=null,n=!1,r=null,v=!1,x=null;if(gl()===a)return"anything";if(el()===a)return"nothing";if(a instanceof Ep)return a.V;if(a instanceof sP)return"#"+a.nA;if(a instanceof jt)return HA(c.kp,a);if(a instanceof nP){var A=a.Mx;return uP(xP(a.Lx,2,c)+" with "+xP(A,0,c),1{var Ve=Ac.h().x;if(hB(ve(),Ve)){Ac=Ac.j();if(null!==Ac){var Td=Ac.Yf,lf=Ac.Rg;a:if(t().d===Td)Td=!0;else{if(Td instanceof L&&(Td=Td.k,el()===Td)){Td=!0;break a}Td=!1}if(Td&&gl()===lf)return""+Ve}if(null!==Ac&&(lf=Ac.Yf,Td=Ac.Rg,lf instanceof L&&Pe(new E(lf.k),Td)))return Ve+" \x3d "+xP(Td,0,c);if(null!==Ac){Td=Ac.Yf;lf=Ac.Rg;a:if(t().d===Td)Td=!0;else{if(Td instanceof L&&(Td=Td.k,el()===Td)){Td=!0;break a}Td=!1}if(Td)return Ve+" \x3c: "+xP(lf, 0,c)}if(null!==Ac&&(Td=Ac.Yf,lf=Ac.Rg,Td instanceof L&&(Td=Td.k,gl()===lf)))return Ve+" :\x3e "+xP(Td,0,c);if(null!==Ac&&(lf=Ac.Yf,Td=Ac.Rg,lf instanceof L))return Ve+" :\x3e "+xP(lf.k,0,c)+" \x3c: "+xP(Td,0,c);throw new w(Ac);}return(Ac.j().Yf.b()?"":"mut ")+Ve+": "+vP(Ac.j(),c)};if(Fa===u())var Qa=u();else{for(var Ma=Fa.e(),Ga=new z(za(Ma),u()),ab=Ga,Hb=Fa.f();Hb!==u();){var bc=Hb.e(),yb=new z(za(bc),u());ab=ab.p=yb;Hb=Hb.f()}Qa=Ga}for(var tb=0,eb=Qa;!eb.b();){var kb=tb,Rb=eb.e();tb=(kb|0)+Rb.length| 0;eb=eb.f()}if(80<(tb|0)){ve();var Gb="{\n"+yP(c),vb=",\n"+yP(c),Tb=ze(Qa,Gb,vb,"");return ada(Tb)+"\n"+yP(c)+"}"}return ze(Qa,"{",", ","}")}if(a instanceof rP){var Nb=a.Ax,ic=Ac=>{if(Ac instanceof fe)return"..."+xP(Ac.aa,0,c);if(Ac instanceof Ud)return""+vP(Ac.fa,c);throw new w(Ac);};if(Nb===u())var Va=u();else{for(var cb=Nb.e(),zb=new z(ic(cb),u()),Ub=zb,jb=Nb.f();jb!==u();){var db=jb.e(),ub=new z(ic(db),u());Ub=Ub.p=ub;jb=jb.f()}Va=zb}return c.fi?ze(Va,"[",", ","]"):ze(Va,"(",", ",")")}if(a instanceof jP){var Aa=a.Mu,va=wP(Aa,c);if(c.fi)return ze(va,"[",", ","]");var Ra=Aa.b()?")":",)";return ze(va,"(",", ",Ra)}a:{if(a instanceof iP){var rb=a.ht,xb=a.it;if(rb instanceof Ep&&"true"===rb.V&&xb instanceof Ep&&"false"===xb.V){var mc=!0;break a}}if(a instanceof iP){var Ha=a.ht,Ka=a.it;if(Ha instanceof Ep&&"false"===Ha.V&&Ka instanceof Ep&&"true"===Ka.V){mc=!0;break a}}mc=!1}if(mc){var Oa=new Ep("bool");return xP(Oa,0,c)}if(a instanceof cl){var Na=a.Yo?20:25,Da=a.Yo?" | ":" \x26 ",ta=a.Jw?a.Kw:zP(a), Ya=O().c;if(null===Ya?null===ta:Ya.i(ta)){var dc=a.Yo?el():gl();return xP(dc,b,c)}if(ta instanceof z){var ka=ta.z,ya=ta.p,Sa=O().c;if(null===Sa?null===ya:Sa.i(ya))return xP(ka,b,c)}var xc=(a.Jw?a.Kw:zP(a)).m(),Sb=new Ef(xc,new y(Ac=>xP(Ac,Na,c)));if(!Sb.s())throw nv("empty.reduceLeft");for(var uc=!0,Lb=null;Sb.s();){var lc=Sb.t();uc?(Lb=lc,uc=!1):Lb=Lb+Da+lc}return uP(Lb,b>Na)}if(a instanceof mP){h=!0;k=a;var Xb=k.Bi,ec=k.Ci;if(el()===Xb&&gl()===ec)return"?"}if(h){var Ab=k.Bi;if(Pe(new E(Ab),k.Ci))return xP(Ab, b,c)}if(h){var Ob=k.Bi,fb=k.Ci;if(el()===Ob)return"out "+xP(fb,0,c)}if(h){var Wa=k.Bi,bb=k.Ci;if(gl()===bb)return"in "+xP(Wa,0,c)}if(h){var Ia=k.Ci;return"in "+xP(k.Bi,0,c)+" out "+xP(Ia,0,c)}if(a instanceof pP){var Ua=a.Hw,pc=a.Gw.V;if(Ua===u())var sc=u();else{for(var Ba=Ua.e(),ob=new z(xP(Ba,0,c),u()),nc=ob,Ib=Ua.f();Ib!==u();){var vc=Ib.e(),Vb=new z(xP(vc,0,c),u());nc=nc.p=Vb;Ib=Ib.f()}sc=ob}return""+pc+ze(sc,c.Jq?"\x3c":"[",", ",c.Jq?"\x3e":"]")}if(a instanceof tP){var fc=a.wx;return xP(a.vx, 100,c)+"."+fc.V}if(a instanceof lP){var Bc=a.Zz,Pb=xP(a.tx,90,c);if(Bc===u())var Jb=u();else{for(var gc=Bc.e(),Cb=new z("\\"+gc.x,u()),cc=Cb,yc=Bc.f();yc!==u();){var Mc=yc.e(),qc=new z("\\"+Mc.x,u());cc=cc.p=qc;yc=yc.f()}Jb=Cb}return""+Pb+ze(Jb,"","","")}if(a instanceof oP){l=!0;m=a;var oc=m.Es;if(oc instanceof Em)return oc.rq.u()}if(l){var Qc=m.Es;if(Qc instanceof Fm)return Qc.uu.gd.u()}if(l){var jc=m.Es;if(jc instanceof Dm)return'"'+jc.Lu+'"'}if(l){var sb=m.Es;if(sb instanceof Gm)return sb.Xq?c.fi? "()":"undefined":"null"}if(a instanceof Vt){n=!0;r=a;var Gc=r.Ws,Wb=r.Vs,Cc=O().c;if(null===Cc?null===Gc:Cc.i(Gc))return xP(Wb,b,c)}if(n){var Fc=r.Vs,qd=r.Ws.m(),Yb=new Ef(qd,new y(Ac=>{if(Ac instanceof Ud)return xP(Ac.fa,0,c);if(!(Ac instanceof fe))throw new w(Ac);return Ac.aa.V}));return uP(ze(Yb,"forall "," ",".")+" "+xP(Fc,0,c),1{if(null!==Ac){var Ve=Ac.h(), Td=Ac.j();if(null!==Td){var lf=Td.Bi;Td=Td.Ci;if(el()===lf)return"\n"+yP(Hc)+HA(Hc.kp,Ve)+" \x3c: "+xP(Td,0,Hc)}}if(null!==Ac&&(Ve=Ac.h(),Td=Ac.j(),null!==Td&&(lf=Td.Bi,Td=Td.Ci,gl()===Td)))return"\n"+yP(Hc)+HA(Hc.kp,Ve)+" :\x3e "+xP(lf,0,Hc);if(null!==Ac&&(Ve=Ac.h(),lf=Ac.j(),null!==lf&&(Td=lf.Bi,Pe(new E(Td),lf.Ci))))return"\n"+yP(Hc)+HA(Hc.kp,Ve)+" :\x3d "+xP(Td,0,Hc);if(null!==Ac&&(Ve=Ac.h(),lf=Ac.j(),null!==lf))return Ac=lf.Bi,lf=lf.Ci,Ve=HA(Hc.kp,Ve),"\n"+yP(Hc)+Ve+" :\x3e "+xP(Ac,0,Hc)+("\n"+ yP(Hc)+ut(Q()," ",Ve.length)+" \x3c: ")+xP(lf,0,Hc);throw new w(Ac);};if(ad===u())var Kc=u();else{for(var Qd=ad.e(),Ad=new z(sd(Qd),u()),kd=Ad,Hd=ad.f();Hd!==u();){var Rd=Hd.e(),Bd=new z(sd(Rd),u());kd=kd.p=Bd;Hd=Hd.f()}Kc=Ad}var ae=ze(Kc,"","",""),dd=Ac=>{if(null!==Ac){var Ve=Ac.Bi;Ac=Ac.Ci;return"\n"+yP(Hc)+xP(Ve,0,Hc)+" \x3c: "+xP(Ac,0,Hc)}throw new w(Ac);};if(Uc===u())var od=u();else{for(var Ta=Uc.e(),wb=new z(dd(Ta),u()),$a=wb,wa=Uc.f();wa!==u();){var hb=wa.e(),ra=new z(dd(hb),u());$a=$a.p=ra; wa=wa.f()}od=wb}return uP(kc+"\n"+rc+(Vc?"":" ")+"where"+ae+ze(od,"","",""),0""+yP(c)+xP(Ac,0,c)+"\n";if(de===u())var af=u();else{for(var pf=de.e(),kf=new z(jf(pf),u()),Be=kf,Kd=de.f();Kd!==u();){var ld=Kd.e(),Jd=new z(jf(ld),u());Be=Be.p=Jd;Kd=Kd.f()}af=kf}if(ye instanceof L)var Dd= ye.k,Xd=""+yP(c)+xP(Dd,0,c)+"\n",Yc=O().c,Ce=new z(Xd,Yc);else{if(t().d!==ye)throw new w(ye);Ce=O().c}var te=dl(Ce,af);return ze(te,"","","")}if(a instanceof yo){v=!0;x=a;var Ie=x.pb,Jf=x.gb,df=x.hg,vg=x.Sg,wg=x.Qm,xg=x.Hj,eg=x.Di,vh=x.mx,fg=x.Ns,ih=x.ei;if(Ap()===Ie){if(!vg.b())throw new Yj("assertion failed: "+vg);if(!wg.b())throw new Yj("assertion failed: "+wg);if(!eg.b())throw new Yj("assertion failed: "+eg);if(!vh.b())throw new Yj("assertion failed: "+vh);if(!fg.b())throw new Yj("assertion failed: "+ fg);if(!ef(ih).b())throw new Yj("assertion failed: "+ih);var Ig=Jf.V;if(df===u())var Tf=u();else{for(var Jg=df.e(),jh=new z(xP(Jg.j(),0,c),u()),yg=jh,gg=df.f();gg!==u();){var Cf=gg.e(),Uf=new z(xP(Cf.j(),0,c),u());yg=yg.p=Uf;gg=gg.f()}Tf=jh}var $g=BF(GF(),Tf);xg.b()&&xm("Program reached and unexpected state.");var Ah=xg.o();return"type "+Ig+$g+" \x3d "+xP(Ah,0,c)}}if(v){var Kg=x.pb,Vf=x.gb,hg=x.hg,zg=x.Sg,Lg=x.Hj,Mg=x.Di,Wf=x.mx,Ng=x.Ns,Kf=x.ei,xf=AP(c),Og=x.fl;if(Og.b())var mi="";else Og.o(),mi= "declare ";var Ci=x.Pm;if(Ci.b())var Xh="";else Ci.o(),Xh="abstract ";var wh=Kg.ld,Bh=Vf.V;if(hg===u())var ng=u();else{for(var kh=hg.e(),Kh=new z(xP(kh.j(),0,c),u()),ni=Kh,Lh=hg.f();Lh!==u();){var lh=Lh.e(),Ch=new z(xP(lh.j(),0,c),u());ni=ni.p=Ch;Lh=Lh.f()}ng=Kh}var Dh=BF(GF(),ng);a:{if(zg instanceof L){var Yh=zg.k;if(null!==Yh){var ah=Yh.Ra,oi=Ac=>{if(null!==Ac){var Ve=Ac.h(),Td=Ac.j();if(t().d===Ve&&null!==Td&&(Td=Td.ya,Td instanceof Cl&&(Ve=Td.ai,Td=Td.Fm,Ve instanceof vl)))return Ve.x+": "+xP(Td, 0,c)}null!==Ac&&(Ve=Ac.h(),(t().d===Ve||Ve instanceof L)&&xm("ill-formed type definition parameter"));throw new w(Ac);};if(ah===u())var mj=u();else{for(var wd=ah.e(),ge=new z(oi(wd),u()),De=ge,qf=ah.f();qf!==u();){var og=qf.e(),Xf=new z(oi(og),u());De=De.p=Xf;qf=qf.f()}mj=ge}var mh="("+ze(mj,"",", ","")+")";break a}}mh=""}if(Lg.b())var Ag="";else{var Bg=Lg.o();Ag=": "+xP(Bg,0,xf)}var Eh=O().c;if(null===Eh?null===Mg:Eh.i(Mg))var Pg="";else{var Di=Mg.m(),Mh=new Ef(Di,new y(Ac=>Zz(Ac,!1)));Pg=" extends "+ ze(Mh,"",", ","")}if(ef(Kf).b()&&Wf.b()&&Ng.b())var pi="";else{if(Wf.b())var Xi="";else{var Qg=Wf.o();Xi=yP(xf)+"super: "+xP(Qg,0,xf)+"\n"}if(Ng.b())var nh="";else{var bh=Ng.o();nh=yP(xf)+"this: "+xP(bh,0,xf)+"\n"}var Mj=Wn(ef(Kf),new CP(a,xf)),Nj=ze(Mj,"","",""),ie=new BP(Wn(ef(Kf),new DP(a)),t().d);pi=" {\n"+Xi+nh+Nj+xP(ie,0,xf)+yP(c)+"}"}return mi+Xh+wh+" "+Bh+Dh+mh+Ag+Pg+pi}throw new w(a);}; function EP(a){if(a instanceof FP)return O().c;if(a instanceof Wt){var b=a.ps,c=a.qs,d=O().c;return new z(b,new z(c,d))}if(a instanceof mP){var e=a.Bi,g=a.Ci,h=O().c;return new z(e,new z(g,h))}if(a instanceof kP){var k=a.cx,l=O().c;return new z(k,l)}if(a instanceof Tn){for(var m=a.Xs,n=null,r=null;m!==u();){for(var v=m.e(),x=v.j().Yf.ha(),A=v.j().Rg,B=O().c,C=un(x,new z(A,B)).m();C.s();){var D=new z(C.t(),u());null===r?n=D:r.p=D;r=D}m=m.f()}return null===n?u():n}if(a instanceof jP){for(var F=a.Mu, I=null,M=null;F!==u();){for(var N=F.e(),P=iu(ju(),N.j().Yf),T=N.j().Rg,Y=O().c,Z=P.tl(new z(T,Y)).m();Z.s();){var S=new z(Z.t(),u());null===M?I=S:M.p=S;M=S}F=F.f()}return null===I?u():I}if(a instanceof iP){var ea=a.ht,ia=a.it,X=O().c;return new z(ea,new z(ia,X))}if(a instanceof hP){var sa=a.As,Ja=a.Bs,Xa=O().c;return new z(sa,new z(Ja,Xa))}if(a instanceof pP)return a.Hw;if(a instanceof tP){var Fa=a.vx,za=a.wx,Qa=O().c;return new z(Fa,new z(za,Qa))}if(a instanceof lP){var Ma=a.tx,Ga=O().c;return new z(Ma, Ga)}if(a instanceof nP){var ab=a.Lx,Hb=a.Mx,bc=O().c;return new z(ab,new z(Hb,bc))}if(a instanceof Vt){var yb=a.Ws,tb=a.Vs,eb=oc=>{if(oc instanceof Ud)oc=oc.fa;else{if(!(oc instanceof fe))throw new w(oc);oc=oc.aa}return oc};if(yb===u())var kb=u();else{for(var Rb=yb.e(),Gb=new z(eb(Rb),u()),vb=Gb,Tb=yb.f();Tb!==u();){var Nb=Tb.e(),ic=new z(eb(Nb),u());vb=vb.p=ic;Tb=Tb.f()}kb=Gb}return Xq(kb,tb)}if(a instanceof rP){for(var Va=a.Ax,cb=null,zb=null;Va!==u();){var Ub=Va.e();if(Ub instanceof fe)var jb= Ub.aa,db=O().c,ub=new z(jb,db);else{if(!(Ub instanceof Ud))throw new w(Ub);var Aa=Ub.fa,va=Aa.Yf.ha(),Ra=Aa.Rg,rb=O().c;ub=un(va,new z(Ra,rb))}for(var xb=ub.m();xb.s();){var mc=new z(xb.t(),u());null===zb?cb=mc:zb.p=mc;zb=mc}Va=Va.f()}return null===cb?u():cb}if(a instanceof qP){for(var Ha=a.Lw,Ka=a.Nw,Oa=a.Mw,Na=null,Da=null;Oa!==u();){for(var ta=Oa.e(),Ya=ta.h(),dc=ta.j(),ka=O().c,ya=new Om(new z(Ya,new z(dc,ka)));ya.s();){var Sa=new z(ya.t(),u());null===Da?Na=Sa:Da.p=Sa;Da=Sa}Oa=Oa.f()}for(var xc= null===Na?u():Na,Sb=Ka,uc=null,Lb=null;Sb!==u();){for(var lc=Sb.e(),Xb=lc.Bi,ec=lc.Ci,Ab=O().c,Ob=new Om(new z(Xb,new z(ec,Ab)));Ob.s();){var fb=new z(Ob.t(),u());null===Lb?uc=fb:Lb.p=fb;Lb=fb}Sb=Sb.f()}var Wa=dl(null===uc?u():uc,xc);return new z(Ha,Wa)}if(a instanceof BP){var bb=a.yx;return dl(a.aA.ha(),bb)}if(a instanceof Zn){var Ia=a.Ch;return dl(GP(a.Yc).ha(),Ia)}if(a instanceof yo){var Ua=a.hg,pc=a.Sg,sc=a.Hj,Ba=a.mx,ob=a.Ns,nc=a.ei;if(Ua===u())var Ib=u();else{for(var vc=Ua.e(),Vb=new z(vc.j(), u()),fc=Vb,Bc=Ua.f();Bc!==u();){var Pb=Bc.e(),Jb=new z(Pb.j(),u());fc=fc.p=Jb;Bc=Bc.f()}Ib=Vb}var gc=Wn((pc.b()?new Gl(O().c):pc.o()).Ra,new HP(a)),Cb=sc.ha(),cc=Ba.ha(),yc=ob.ha(),Mc=new BP(Wn(ef(nc),new IP(a)),t().d),qc=O().c;return dl(dl(dl(dl(dl(new z(Mc,qc),yc),cc),Cb),gc),Ib)}throw new w(a);} var jea=function JP(a,b,c,d){var g=DB(b);if(g instanceof lx)return tp(),a=c.Ig(g.Xa),gq(G(new H,a,g));if(g instanceof LA){var h=g.ic;b=g.jc;if(Pe(new E(g.tc),d.tc))return g=JP(a,h,c,d),a=JP(a,b,c,d),g.Ce(a);tp();a=t().d;return gq(G(new H,a,g))}tp();g=t().d;return gq(G(new H,g,b))};function BB(a,b,c,d,e,g){this.mA=this.kA=this.lA=this.Pu=this.bt=this.Lq=this.Vq=this.yA=null;if(null===a)throw null;this.Lq=a;this.bt=c;this.Pu=d;this.lA=e;this.kA=g;this.mA=b;FD(this,a,b)}BB.prototype=new HD; BB.prototype.constructor=BB; BB.prototype.Tb=function(a,b){var c=this.Lq.qa,d=this.Lq;if(d.F){var e=ut(Q(),"| ",d.r)+("analyze2["+a+"] ")+b;ff(gf(),e+"\n")}d.r=1+d.r|0;try{if(b instanceof lx){var g=a.Ig(b.Xa);if(g instanceof L){var h=!!g.k;this.bt.oh(G(new H,h,b))&&LB(this.Lq,b,a,h,this.lA,this.kA,this.mA,this.bt,this.Pu)}else if(t().d===g){if(this.bt.oh(G(new H,!0,b))){var k=this.Lq,l=new RB(a,b.Xa,!0);LB(k,b,l,!0,this.lA,this.kA,this.mA,this.bt,this.Pu)}if(this.bt.oh(G(new H,!1,b))){var m=this.Lq,n=new RB(a,b.Xa,!1);LB(m,b, n,!1,this.lA,this.kA,this.mA,this.bt,this.Pu)}}else throw new w(g);}else if(b instanceof LA){var r=jea(this,b,a,b),v=this.Lq;if(v.F){var x=ut(Q(),"| ",v.r)+"Components "+r;ff(gf(),x+"\n")}if(this.Pu.oh(r))LB(this.Lq,b,a,b.tc,this.lA,this.kA,this.mA,this.bt,this.Pu);else{var A=this.Lq;if(A.F){var B=ut(Q(),"| ",A.r)+"Found in "+this.Pu;ff(gf(),B+"\n")}}}else GD.prototype.Tb.call(this,a,b);var C=void 0}finally{d.r=-1+d.r|0}dx(new E(c),d.qa)&&d.F&&(a=""+ut(Q(),"| ",d.r)+c.n(C),ff(gf(),a+"\n"))}; BB.prototype.$classData=q({HY:0},!1,"mlscript.TypeSimplifier$Analyze2$1$",{HY:1,kP:1,g:1}); function KP(a){this.Dx=this.Kj=null;if(null===a)throw null;this.Dx=a;var b=t().d,c=Xu(),d=a.nP.m();c=c.Ib(new Ef(d,new y(x=>{var A=x.h();x=new qx(this.Dx,x.j(),new vl(x.h()));return G(new H,A,x)})));d=Xu().X();var e=a.Gd,g=Xu().X(),h=new MB;tp();var k=a.XE;if(k===u())var l=u();else{l=k.e();var m=l=new z(G(new H,l.Wl.V,l),u());for(k=k.f();k!==u();){var n=k.e();n=new z(G(new H,n.Wl.V,n),u());m=m.p=n;k=k.f()}}l=pp(0,l);m=Xu().X();k=t().d;n=Hw();var r=Su(),v=op().ga;this.Kj=new Iw(a,b,c,d,e,g,h,!1,!1, l,m,k,n.Hd(new Uu(r,v)));mf(this)}KP.prototype=new p;KP.prototype.constructor=KP; function mf(a){if(a.Dx.$c){var b=Xu(),c=a.Dx.qP,d=k=>{var l=new fx(a.Dx,k,nf(),a.Kj,new y(r=>{xm(r.pq)})),m=a.Kj.hc,n=G(new H,k.gb.V,l);m.$(n);return G(new H,k.gb.V,l)};if(c===u())d=u();else{var e=c.e(),g=e=new z(d(e),u());for(c=c.f();c!==u();){var h=c.e();h=new z(d(h),u());g=g.p=h;c=c.f()}d=e}b=b.Ib(d);b=new Iw(a.Kj.S,a.Kj.Ec,a.Kj.hc,a.Kj.Ed,a.Kj.da,a.Kj.Pc,a.Kj.Zc,a.Kj.Lb,a.Kj.yc,a.Kj.tb,b,a.Kj.od,a.Kj.cb);d=new y(k=>{throw k;});for(e=b.$a.ie();e.s();)g=e.t(),h=Kx(g,d),g=b.hc,c=h.Ua(),h=new ix(a.Dx, h),c=G(new H,c,h),g.$(c);return b}return a.Kj}KP.prototype.$classData=q({PY:0},!1,"mlscript.Typer$Ctx$",{PY:1,g:1,l:1});function LP(){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0;this.zk=this.Pq=this.Uq=this.xp=this.Bp=this.Cp=this.Sq=this.zp=this.Rq=this.wp=this.Ap=this.yp=this.Qq=this.Tq=null;this.Dp=0}LP.prototype=new NC;LP.prototype.constructor=LP;function MP(){}MP.prototype=LP.prototype;function tx(a){null===a.Tq&&null===a.Tq&&(a.Tq=new NP(a))} function zx(a){null===a.yp&&null===a.yp&&(a.yp=new OP(a));return a.yp}function kea(a){null===a.Ap&&null===a.Ap&&(a.Ap=new EC(a));return a.Ap}function vx(a){null===a.wp&&null===a.wp&&(a.wp=new PP(a));return a.wp}function CA(a){null===a.Rq&&null===a.Rq&&(a.Rq=new CC(a))}function xv(a){null===a.zp&&null===a.zp&&(a.zp=new QP(a));return a.zp}function $B(a){null===a.Sq&&null===a.Sq&&(a.Sq=new DC(a))}function cw(a){null===a.Bp&&null===a.Bp&&(a.Bp=new RP(a));return a.Bp} function WD(a){null===a.xp&&null===a.xp&&(a.xp=new SP(a));return a.xp}function TP(a){null===a.Uq&&null===a.Uq&&(a.Uq=new IC(a))}function iC(a){null===a.Pq&&null===a.Pq&&(a.Pq=new AC(a))}function UP(a){a.Dp=1+a.Dp|0;return-1+a.Dp|0}function PP(a){this.gO=null;if(null===a)throw null;this.gO=a}PP.prototype=new p;PP.prototype.constructor=PP;function wx(a,b,c){return b.b()?c:new eC(a.gO,b,c)}PP.prototype.$classData=q({cZ:0},!1,"mlscript.TyperDatatypes$ConstrainedType$",{cZ:1,g:1,l:1}); function SP(a){this.Su=null;if(null===a)throw null;this.Su=a}SP.prototype=new p;SP.prototype.constructor=SP;function XD(a,b,c,d,e){if(null!==b){var g=b.qe;if(!0===b.Qd&&!0===g)return new Uw(a.Su,t().d,a.Su.La,e)}if(null!==b&&(g=b.qe,!0===b.Qd&&!1===g))return new Uw(a.Su,t().d,d,e);if(null!==b&&(g=b.qe,!1===b.Qd&&!0===g))return new Uw(a.Su,(t(),new L(c)),a.Su.La,e);if(null!==b&&(g=b.qe,!1===b.Qd&&!1===g))return new Uw(a.Su,(t(),new L(c)),d,e);throw new w(b);} SP.prototype.$classData=q({lZ:0},!1,"mlscript.TyperDatatypes$FieldType$",{lZ:1,g:1,l:1});function VP(){this.J=null}VP.prototype=new HC;VP.prototype.constructor=VP;function WP(){}WP.prototype=VP.prototype;function OP(a){this.sA=null;if(null===a)throw null;this.sA=a}OP.prototype=new p;OP.prototype.constructor=OP; function yx(a,b,c){for(;;){At(tp(),b<=a.sA.Df);if(Pe(new E(b),a.sA.Df)||c.Ea()<=b)return c;var d=uy(c);if(d instanceof Qx)c=d.de,d=d.Re,a=zx(a.sA),b=bthis.QI?(t(),new L(!0)):this.wA.Ig(a)};dC.prototype.Ar=function(a){return a>this.QI?this:this.wA.Ar(a)};dC.prototype.Bw=function(){return this.wA+";Q("+this.wA.gt+")"};dC.prototype.$classData=q({SZ:0},!1,"mlscript.TyperHelpers$PolMap$$anon$1",{SZ:1,OE:1,g:1});function TB(a){this.ol=null;this.gt=0;this.RI=this.$m=null;if(null===a)throw null;this.RI=a;ZC(this,a.$m,t().d)}TB.prototype=new aD;TB.prototype.constructor=TB;TB.prototype.Ig=function(){return t().d}; TB.prototype.Ar=function(a){return this.RI.Ar(a)};TB.prototype.Bw=function(){return this.RI+";\x3d"};TB.prototype.$classData=q({TZ:0},!1,"mlscript.TyperHelpers$PolMap$$anon$2",{TZ:1,OE:1,g:1});function UB(a){this.ol=null;this.gt=0;this.PE=this.$m=null;if(null===a)throw null;this.PE=a;var b=a.$m;a=a.ol;a.b()?a=R():(a=!!a.o(),a=new L(!a));ZC(this,b,a)}UB.prototype=new aD;UB.prototype.constructor=UB;UB.prototype.Ig=function(a){a=this.PE.Ig(a);if(a.b())return R();a=!!a.o();return new L(!a)}; UB.prototype.Ar=function(a){return this.PE.Ar(a)};UB.prototype.Bw=function(){return this.PE+";-"};UB.prototype.$classData=q({UZ:0},!1,"mlscript.TyperHelpers$PolMap$$anon$3",{UZ:1,OE:1,g:1});function RB(a,b,c){this.ol=null;this.gt=0;this.QE=this.hP=this.$m=null;this.gP=0;this.xA=!1;if(null===a)throw null;this.QE=a;this.gP=b;this.xA=c;ZC(this,a.$m,a.ol);this.hP=this.Ar(b)}RB.prototype=new aD;RB.prototype.constructor=RB; RB.prototype.Ig=function(a){if(a>=this.gP)return t(),new L(this.xA);var b=!1,c=null;a=this.hP.Ig(a);if(a instanceof L&&(b=!0,c=a,!0===!!c.k))return t(),new L(this.xA);if(b&&!1===!!c.k)return t(),new L(!this.xA);if(t().d===a)return t().d;throw new w(a);};RB.prototype.Ar=function(a){return this.QE.Ar(a)};RB.prototype.Bw=function(){var a=this.QE;t();return a+";@["+dA(new L(this.xA))+"]("+this.QE.gt+")"};RB.prototype.$classData=q({VZ:0},!1,"mlscript.TyperHelpers$PolMap$$anon$4",{VZ:1,OE:1,g:1}); function bD(a,b){this.ol=null;this.gt=0;this.SI=this.$m=null;if(null===a)throw null;this.SI=b;ZC(this,a.iP,b)}bD.prototype=new aD;bD.prototype.constructor=bD;bD.prototype.Ig=function(){return this.SI};bD.prototype.Ar=function(){return this};bD.prototype.Bw=function(){return""+dA(this.SI)};bD.prototype.$classData=q({WZ:0},!1,"mlscript.TyperHelpers$PolMap$$anon$5",{WZ:1,OE:1,g:1}); function gP(a){a=ef(a).m();a=new Ef(a,new y(b=>{if(b instanceof Pm)return Zz(b,!1);if(b instanceof Ct||b instanceof Po)return Mx(b);xm("Unexpected typing unit entity: "+b)}));return ze(a,"{","; ","}")}function lea(a,b){var c=AP(b),d=ef(a);a=k=>k instanceof Pm?aA(k,!1,c):k instanceof Ct?VO(k,c.fi):b.fi?no():k.u();if(d===u())a=u();else{var e=d.e(),g=e=new z(a(e),u());for(d=d.f();d!==u();){var h=d.e();h=new z(a(h),u());g=g.p=h;d=d.f()}a=e}e="{"+ON(c);g=ON(c);d=ON(b)+"}";return ze(a,e,g,d)} function mea(a){var b=Wn(a.Kx,new aQ(a)),c=Aq(Bq(),b),d=a.Kx;a=h=>{if(h instanceof bm){var k=h.Vn,l=h.Zo;if(c.L(k.x))return new Zn(t().d,k,t().d,O().c,(t(),new fe(l)),t().d,t().d,t().d,t().d,t().d,!0,O().c)}return h};if(d===u())return u();b=d.e();var e=b=new z(a(b),u());for(d=d.f();d!==u();){var g=d.e();g=new z(a(g),u());e=e.p=g;d=d.f()}return b}function bQ(){this.Yl=this.AA=this.Zu=this.CP=null;cQ=this;this.CP=new dQ(!0,!0);this.Zu=new dQ(!0,!1);this.AA=new dQ(!1,!0);this.Yl=new dQ(!1,!1)} bQ.prototype=new p;bQ.prototype.constructor=bQ;bQ.prototype.$classData=q({g_:0},!1,"mlscript.VarianceInfo$",{g_:1,g:1,l:1});var cQ;function ou(){cQ||(cQ=new bQ);return cQ}function eQ(){}eQ.prototype=new p;eQ.prototype.constructor=eQ;function hr(a,b,c,d){return new Gf(UN(b.e().h(),c),b,d)}eQ.prototype.$classData=q({i_:0},!1,"mlscript.WarningReport$",{i_:1,g:1,l:1});var fQ;function fr(){fQ||(fQ=new eQ);return fQ}function gQ(){this.lf=null}gQ.prototype=new sE;gQ.prototype.constructor=gQ; function hQ(){}hQ.prototype=gQ.prototype;function iQ(a){a.fv=jA().X();a.ni=TE(PE());O()}function jQ(){this.ni=this.fv=null}jQ.prototype=new p;jQ.prototype.constructor=jQ;function kQ(){}kQ.prototype=jQ.prototype;function lQ(a){if(a instanceof HE)return"Consequent";if(IE()===a)return"MissingCase";if(a instanceof CE)return"IfThenElse";if(a instanceof DE)return"Match";throw new w(a);}function mQ(a,b,c,d,e){a.dm(b,new y(()=>{}),c,d,e)}function nQ(){this.ga=null;oQ=this;this.ga=new pQ}nQ.prototype=new p; nQ.prototype.constructor=nQ;nQ.prototype.$classData=q({U2:0},!1,"scala.$less$colon$less$",{U2:1,g:1,l:1});var oQ;function op(){oQ||(oQ=new nQ);return oQ}function WG(a){a=new (md(la).Ia)(a);yj(Pj(),a,void 0);return a}function qQ(){}qQ.prototype=new p;qQ.prototype.constructor=qQ; function rQ(a,b,c){a=b.Q();if(-1b)throw new Aj;var c=a.a.length;c=bb)throw new Aj;c=a.a.length;c=b=k)a=new Xc(0);else{a=new Xc(k);for(var l=0;l=k)l=new Xc(0);else{l=new Xc(k);for(var m=0;m{n.ve=n.ve+N.length|0}));var r=n.ve,v=new Sc(r),x=new IQ(0),A=new GA(!0);b.Ca(new y(N=>{A.Am?A.Am=!1:(v.a[x.ve]=-1,x.ve=1+x.ve|0);for(var P=N.length,T=0;T>16;x.ve=1+x.ve|0;T=1+T|0}}));var B=1+r|0;rl();if(0>=B)var C= new Xc(0);else{for(var D=new Xc(B),F=0;F{M.veF?D:F),l.a[B]=D),d=b)c=new (md(fa).Ia)(0); else{d=new (md(fa).Ia)(b);for(e=0;ed&&MQ(e,b,d,c);d=1+c|0;if(d>=a)throw new NQ(b,c);switch(b.charCodeAt(d)){case 117:c=117;break;case 98:c=8;break;case 116:c=9;break;case 110:c=10;break;case 102:c=12;break;case 114:c=13;break;case 34:c=34;break;case 39:c=39;break;case 92:c=92;break;default:throw new NQ(b,c);}if(117===c)a:for(var g=c=d,h=b.length,k=b;;){if(c>=h)throw new OQ(k,-1+c|0);if(117===k.charCodeAt(c))c=1+c|0;else{b:for(var l=0,m=0;;){if(4<= l){c=new PQ(65535&m,(c-g|0)+l|0);break b}if((l+c|0)>=h)throw new OQ(k,c+l|0);var n=k.charCodeAt(l+c|0);n=DH(Pr(),n,36);if(0<=n&&15>=n)m=(m<<4)+n|0,l=1+l|0;else throw new OQ(k,c+l|0);}break a}}else c=new PQ(c,1);if(null===c)throw new w(c);g=c.aQ();d=d+c.Sc()|0;c=String.fromCharCode(g);e.ja=""+e.ja+c;c=d;g=b;h=TL(Pr(),92);g=g.indexOf(h,d)|0;d=c;c=g}de?tJ(b,fR(a,b.sa,c,d)):0d?tJ(b,iR(a,b.sa,c)):0a.pT()){b=a.bl().a.length<<1;c=a.bl();a.gM(new (md($J).Ia)(b));rR(a,a.bl().a.length);for(var d=-1+c.a.length|0;0<=d;){for(var e=c.a[d];null!==e;){var g=e.BB();g=My(W(),g);g=nR(a,g);var h=e.Ev();e.ty(a.bl().a[g]);a.bl().a[g]=e;e=h;qR(a,g)}d=-1+d|0}a.hM(fK(hK(),a.sJ(),b))}}function sR(a,b,c){var d=My(W(),b);d=nR(a,d);var e=oR(a,b,d);if(null!==e)return e;b=a.CJ(b,c);pR(a,b,d);return null} function tR(a,b){var c=My(W(),b);c=nR(a,c);return uR(a,b,c)}function uR(a,b,c){var d=a.bl().a[c];if(null!==d){var e=d.BB();if(ml(nl(),e,b))return a.bl().a[c]=d.Ev(),a.hz(-1+a.nu()|0),vR(a,c),d.ty(null),d;for(e=d.Ev();;){if(null!==e){var g=e.BB();g=!ml(nl(),g,b)}else g=!1;if(g)d=e,e=e.Ev();else break}if(null!==e)return d.ty(e.Ev()),a.hz(-1+a.nu()|0),vR(a,c),e.ty(null),e}return null}function qR(a,b){null!==a.lu()&&(a=a.lu(),b>>=5,a.a[b]=1+a.a[b]|0)} function vR(a,b){null!==a.lu()&&(a=a.lu(),b>>=5,a.a[b]=-1+a.a[b]|0)}function rR(a,b){if(null!==a.lu())if(b=1+(b>>5)|0,a.lu().a.length!==b)a.$L(new Xc(b));else{a=a.lu();Pj();b=a.a.length;for(var c=0;c!==b;)a.a[c]=0,c=1+c|0}}function nR(a,b){var c=-1+a.bl().a.length|0,d=Math.clz32(c);a=a.kT();OL||(OL=new NL);b=Math.imul(-1640532531,b);UH();b=Math.imul(-1640532531,b<<24|16711680&b<<8|65280&(b>>>8|0)|b>>>24|0);return((b>>>a|0|b<<(-a|0))>>>d|0)&c} function wR(a){a.bQ(750);hK();a.gM(new (md($J).Ia)(1<<(-Math.clz32(15)|0)));a.hz(0);var b=a.sJ();hK();hK();a.hM(fK(0,b,1<<(-Math.clz32(15)|0)));a.$L(null);b=a.jT;var c=-1+a.bl().a.length|0;c=nI(UH(),c);b.call(a,c)}function xR(a,b){this.YS=null;this.ju=a;this.Fn=b;this.zm=this.ww=null}xR.prototype=new p;xR.prototype.constructor=xR;xR.prototype.BB=function(){return this.ju};xR.prototype.ty=function(a){this.YS=a};xR.prototype.Ev=function(){return this.YS}; xR.prototype.$classData=q({e$:0},!1,"scala.collection.mutable.LinkedHashMap$LinkedEntry",{e$:1,g:1,VL:1});function yR(a){this.eT=null;this.zw=a;this.Mo=this.yw=null}yR.prototype=new p;yR.prototype.constructor=yR;yR.prototype.BB=function(){return this.zw};yR.prototype.ty=function(a){this.eT=a};yR.prototype.Ev=function(){return this.eT};yR.prototype.$classData=q({k$:0},!1,"scala.collection.mutable.LinkedHashSet$Entry",{k$:1,g:1,VL:1});function zR(){this.xy=null;AR=this;this.xy=Ai().qM} zR.prototype=new p;zR.prototype.constructor=zR; function BR(a,b){var c=""+a;a=new mr;nr(a,or(c),c.length);c=b.ds;var d=pr(a)-c|0;if(!(CR(a)=d))if(64>a.$h){c=hN().lz.a[d];var e=c.W,g=c.Y,h=a.xb,k=h>>31,l=d>>31;c=h-d|0;h=(-2147483648^c)>(-2147483648^h)?-1+(k-l|0)|0:k-l|0;d=a.Bg;l=d.W;var m=d.Y;k=xa();d=$h(k,l,m,e,g);k=k.Qc;var n=xa();l=Ri(n,l,m,e,g);m=n.Qc;if(0!==l||0!==m){hN();if(0>m){var r=-l|0;n=0!==l?~m:-m|0}else r=l,n=m;n=new ma(r<<1,r>>>31|0|n<<1);e=new ma(e,g);g=n.Y;r=e.Y;(g===r?(-2147483648^n.W)>(-2147483648^e.W):g>r)?e=1:(g= n.Y,r=e.Y,e=(g===r?(-2147483648^n.W)<(-2147483648^e.W):gm?-1:0===m&&0===l?0:1,5+e|0);e=lN(hN(),1&d,e,b.Fw);g=e>>31;e=d+e|0;d=(-2147483648^e)<(-2147483648^d)?1+(k+g|0)|0:k+g|0;0>d?(k=-e|0,g=0!==e?~d:-d|0):(k=e,g=d);k=cG(xa(),k,g);+Math.log10(k)>=b.ds?(c=-1+c|0,k=-1!==c?h:-1+h|0,h=xa(),d=$h(h,e,d,10,0),c=new ma(c,k),h=new ma(d,h.Qc)):(c=new ma(c,h),h=new ma(e,d))}else c=new ma(c,h),h=new ma(d,k);c=Za(c);d=Za(h);h=Za(new ma(c.W,c.Y));c=h.W;h=h.Y;k=Za(new ma(d.W,d.Y));d=k.W;k= k.Y;a.xb=mN(hN(),new ma(c,h));a.cs=b.ds;a.Bg=new ma(d,k);a.$h=gN(hN(),new ma(d,k));a.bs=null}else e=Pi(fi(),new ma(d,d>>31)),h=DR($M(a),e),k=a.xb,g=k>>31,l=d>>31,d=k-d|0,k=(-2147483648^d)>(-2147483648^k)?-1+(g-l|0)|0:g-l|0,0!==h.a[1].Ya&&(g=VM(h.a[1]),0!==g.Ya&&(Sh(),l=g.wb,m=1+l|0,n=new Xc(m),Nh(0,n,g.Qa,l),g=Hh(g.Ya,m,n),Ih(g)),g=fM(g,e),e=ER(h.a[0],0)?1:0,g=Math.imul(h.a[1].Ya,5+g|0),e=lN(hN(),e,g,b.Fw),0!==e&&(e=ri(Ph(),new ma(e,e>>31)),g=h.a[0],h.a[0]=li(vi(),g,e)),e=new mr,FR(e,h.a[0],0),pr(e)> c&&(h.a[0]=GR(h.a[0],Ph().mq),d=e=-1+d|0,k=-1!==e?k:-1+k|0)),a.xb=mN(hN(),new ma(d,k)),a.cs=c,HR(a,h.a[0]);return new tr(a,b)}zR.prototype.$classData=q({v3:0},!1,"scala.math.BigDecimal$",{v3:1,g:1,l:1});var AR;function lr(){AR||(AR=new zR);return AR}function IR(a,b){var c=b-a.yy|0,d=a.KK.a[c];null===d&&(d=JR(new KR,null,new ma(b,b>>31)),a.KK.a[c]=d);return d} function LR(){this.pR=this.LK=null;this.MB=this.yy=0;this.qR=this.KK=null;MR=this;this.LK=ri(Ph(),new ma(0,-2147483648));this.pR=JR(new KR,this.LK,new ma(0,-2147483648));this.yy=-1024;this.MB=1024;this.KK=new (md(NR).Ia)(1+(this.MB-this.yy|0)|0);this.qR=ri(Ph(),new ma(-1,-1))}LR.prototype=new p;LR.prototype.constructor=LR;function br(){var a=Tr();return 0>=a.yy&&0<=a.MB?IR(a,0):OR(a,new ma(0,0))} function OR(a,b){var c=a.yy,d=c>>31,e=b.Y;(d===e?(-2147483648^c)<=(-2147483648^b.W):d>31,e=b.Y,c=e===d?(-2147483648^b.W)<=(-2147483648^c):e=yh(Sh(),b)){var c=b.xl(),d=c.W;c=c.Y;var e=a.yy,g=e>>31;(g===c?(-2147483648^e)<=(-2147483648^d):g>31,e=c===g?(-2147483648^d)<=(-2147483648^e):c>24&&0===(1&this.Rn)<<24>>24&&(this.mz=zq(this),this.Rn=(1|this.Rn)<<24>>24);return this.mz};f.rn=function(){return this.pz};f.fm=function(a){this.pz=a};f.qn=function(){return this.oz}; f.em=function(a){this.oz=a};f.pn=function(){return this.nz};f.on=function(a){this.nz=a};f.A=function(){0===(2&this.Rn)<<24>>24&&0===(2&this.Rn)<<24>>24&&(this.qz=Dq(this),this.Rn=(2|this.Rn)<<24>>24);return this.qz};function BS(){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0}BS.prototype=new p;BS.prototype.constructor=BS;function CS(){}f=CS.prototype=BS.prototype;f.Vj=function(){return Zda(this)}; f.jn=function(){0===(1&this.dl)<<24>>24&&0===(1&this.dl)<<24>>24&&(this.rs=zq(this),this.dl=(1|this.dl)<<24>>24);return this.rs};f.rn=function(){return this.us};f.fm=function(a){this.us=a};f.qn=function(){return this.ts};f.em=function(a){this.ts=a};f.pn=function(){return this.ss};f.on=function(a){this.ss=a};f.A=function(){0===(2&this.dl)<<24>>24&&0===(2&this.dl)<<24>>24&&(this.vs=Dq(this),this.dl=(2|this.dl)<<24>>24);return this.vs};function DS(){}DS.prototype=new qO;DS.prototype.constructor=DS; function ES(){}ES.prototype=DS.prototype;function Xo(a,b,c){a.Bu=b;a.AH=c;return a}function Yo(){this.AH=this.Bu=null}Yo.prototype=new fO;Yo.prototype.constructor=Yo;function FS(){}FS.prototype=Yo.prototype;Yo.prototype.sh=function(){return 20};Yo.prototype.xa=function(){return Rp(Rp(Rp(Pp(this.Bu.xa(),20>this.Bu.sh()||this.Bu instanceof vo||this.Bu instanceof $m),Sp(Qp(),"[")),this.AH.xa()),Sp(Qp(),"]"))};Yo.prototype.VJ=function(){return this.Bu.VJ()}; Yo.prototype.$classData=q({lN:0},!1,"mlscript.JSMember",{lN:1,Vi:1,Xc:1,g:1});function GS(){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0;this.zk=this.Pq=this.Uq=this.xp=this.Bp=this.Cp=this.Sq=this.zp=this.Rq=this.wp=this.Ap=this.yp=this.Qq=this.Tq=null;this.Dp=0;this.yq=this.Aq=this.Bq=this.zq=this.Dq=this.Cq=null}GS.prototype=new MP;GS.prototype.constructor=GS;function HS(){}HS.prototype=GS.prototype; function Vu(a){null===a.Cq&&null===a.Cq&&(a.Cq=new IS(a));return a.Cq}function lw(a){null===a.Dq&&null===a.Dq&&(a.Dq=new JS(a));return a.Dq}function $A(a){null===a.zq&&null===a.zq&&(a.zq=new KO(a));return a.zq}function xB(a){null===a.Aq&&null===a.Aq&&(a.Aq=new NO(a));return a.Aq}function GO(a){null===a.yq&&null===a.yq&&(a.yq=new AO(a));return a.yq}function vD(a){return Pe(new E(a),!0)}function Ev(a){return Pe(new E(a),!0)} function Bw(a,b,c,d,e){var g=Su(),h=op().ga;g=new Uu(g,h);h=sv();var k=c.Q()+d.Q()|0;d=Lv(Lv(Mv(8(x,A)=>{if(r){var B=cw(a),C=V(x.q);C=Pu(x,A,C,!1);var D=V(x.q);return dw(B,C,dv(x,A,D,!1),ew(cw(a)),v)}B=cw(a);C=V(x.q);C=dv(x,A,C,!1);D=V(x.q);return dw(B,C,Pu(x, A,D,!1),ew(cw(a)),v)})(b,e));Yq();m=Ew(m,n);l=new fw(a,l,m,V(a))}else{if(R()!==m)throw new w(m);l=l.j()}c.bj(k,l)}b=qp();return Ov(h,(new Pv(b)).vc(c),g)}class KS extends Vd{constructor(a){super();this.lX=a;yF(this,null,null,!0)}}KS.prototype.$classData=q({kX:0},!1,"mlscript.NotAType",{kX:1,pc:1,g:1,l:1});function LS(a,b,c,d){this.QH=this.KN=this.vE=this.Vq=this.yA=null;if(null===a)throw null;this.vE=a;this.KN=c;this.QH=d;FD(this,a.nc,b)}LS.prototype=new HD;LS.prototype.constructor=LS; LS.prototype.Kp=function(a,b){JD(this,a,b)}; LS.prototype.Tb=function(a,b){var c=this.vE.nc.qa,d=this.vE.nc;if(d.F){var e=ut(Q(),"| ",d.r)+("Trav("+a+")("+b)+")";ff(gf(),e+"\n")}d.r=1+d.r|0;try{if(b instanceof lx){var g=this.KN,h=a.Ig(b.Xa);if(g.oh(G(new H,h,b))){var k=this.QH,l=this.QH.Se(b,new U(()=>ou().CP)),m=!1,n=null,r=a.Ig(b.Xa);a:{if(r instanceof L&&(m=!0,n=r,!0===!!n.k)){var v=ou().Zu;break a}if(m&&!1===!!n.k)v=ou().AA;else if(t().d===r)v=ou().Yl;else throw new w(r);}k.bh(b,new dQ(l.Qd&&v.Qd,l.qe&&v.qe));GD.prototype.Tb.call(this,a, b)}}else if(b instanceof Qv){m=this.vE.nc;k=Z=>uH(Q(),Z.h().x,35);var x=b.Ba;a:for(;;)if(x.b()){n=u();break}else{var A=x.e(),B=x.f();if(!0===!!k(A))x=B;else for(l=x,r=B;;){if(r.b())n=l;else{var C=r.e();if(!0!==!!k(C)){r=r.f();continue}C=r;var D=new z(l.e(),u()),F=l.f();for(l=D;F!==C;){var I=new z(F.e(),u());l=l.p=I;F=F.f()}var M=C.f();for(F=M;!M.b();){var N=M.e();if(!0===!!k(N)){for(;F!==M;){var P=new z(F.e(),u());l=l.p=P;F=F.f()}F=M.f()}M=M.f()}F.b()||(l.p=F);n=D}break a}}var T=new Qv(m,n,b.Nj); GD.prototype.Tb.call(this,a,T)}else GD.prototype.Tb.call(this,a,b);var Y=void 0}finally{d.r=-1+d.r|0}dx(new E(c),d.qa)&&d.F&&(a=""+ut(Q(),"| ",d.r)+c.n(Y),ff(gf(),a+"\n"))};LS.prototype.$classData=q({CX:0},!1,"mlscript.NuTypeDefs$TypedNuCls$Trav$1$",{CX:1,kP:1,g:1,YZ:1});function MS(a,b){if(null===b)throw null;a.nc=b;var c=a.aG().ie();c=new Ef(c,new y(e=>{e=e.Ua();return new vl(e)}));a.px=Aq(Bq(),c);c=a.Yr().A();a=cy(a.Yr());tx(b);var d=t().d;jx(new kx,b,c,a,d,!0)} function Cx(){this.nc=this.px=null}Cx.prototype=new p;Cx.prototype.constructor=Cx;function NS(){}NS.prototype=Cx.prototype;Cx.prototype.A=function(){return this.Yr().A()};Cx.prototype.bG=function(){return this.nc};function OS(a,b){a.nd=(t(),new L(b));b=b.A();return Cq(a,b)} var hu=function PS(a){var c=a.nd;if(c instanceof L)return PS(c.k);if(t().d===c){var d=!1,e=null,g=!1,h=null,k=!1,l=!1,m=null;c=!1;var n=null;if(a instanceof Ml){d=!0;e=a;var r=e.Ml;if(r instanceof Ml)return PS(r.Ml)}if(d)return"annotated "+PS(e.Ml);if(a instanceof Fl){g=!0;h=a;d=h.gg;if(!0===h.bi)a:{if(d instanceof Gl&&(e=d.Ra,e instanceof z&&e.p instanceof z)){d=!0;break a}if(d instanceof Gl&&(e=d.Ra,e instanceof z&&(e=e.z,null!==e&&e.h()instanceof L))){d=!0;break a}d=d instanceof Sl?!0:!1}else d= !1;if(d)return"record"}if(g)return PS(h.gg);if(a instanceof Sl&&(k=!0,h=a.Dj,h instanceof z&&(g=h.z,h=h.p,g instanceof Pm&&(d=O().c,null===d?null===h:d.i(h)))))return PS(g);if(k)return"block of statements";if(a instanceof Em)return"integer literal";if(a instanceof Fm)return"decimal literal";if(a instanceof Dm)return"string literal";if(a instanceof Gm)return a.Xq?"undefined literal":"null literal";if(a instanceof vl)return"reference";if(a instanceof Cl)return"type ascription";if(a instanceof Ol)return"lambda expression"; if(a instanceof Pl&&(l=!0,m=a,k=m.Za,null!==k&&(k=fz(iz(),k),!k.b()&&(k=k.o().h(),k instanceof vl&&"|"===k.x))))return"type union";if(l&&(k=m.Za,null!==k&&(k=fz(iz(),k),!k.b()&&(k=k.o().h(),k instanceof vl&&"\x26"===k.x))))return"type intersection";if(l&&(m=m.Za,null!==m&&!fz(iz(),m).b())||null!==a&&!fz(iz(),a).b())return"operator application";if(l)return"application";if(a instanceof yl)return"record";if(a instanceof Ql)return"field selection";if(a instanceof Rl)return"let binding";if(a instanceof Gl&&(c=!0,n=a,l=n.Ra,l instanceof z&&(m=l.z,l=l.p,null!==m&&(k=m.h(),m=m.j(),t().d===k&&null!==m&&(m=m.ya,k=O().c,null===k?null===l:k.i(l))))))return PS(m);if(c&&(l=n.Ra,l instanceof z&&(n=l.z,l=l.p,null!==n&&(n.h()instanceof L?(n=O().c,n=null===n?null===l:n.i(l)):n=!1,n))))return"binding";if(c)return"tuple";if(a instanceof Tl)return"`with` extension";if(a instanceof Ul)return"`case` expression";if(a instanceof Vl)return"array access";if(a instanceof Wl)return"assignment";if(a instanceof dm)return"while loop"; if(a instanceof Yl)return"new instance";if(a instanceof cm)return"refinement";if(a instanceof Xl)return"if-else block";if(a instanceof Il)return"type application";if(a instanceof $l)return"constraint clause";if(a instanceof Zl)return"forall clause";if(a instanceof Kl)return"explicit instantiation";if(a instanceof am)return"super";if(a instanceof bm)return"assign for ctor";if(a instanceof em)return"quasiquote";if(a instanceof fm)return"unquote";throw new w(a);}throw new w(c);},Zz=function QS(a,b){var d= !1,e=null,g=!1,h=null,k=!1,l=null;if(a instanceof Ml)return l=a.Ml,""+RS("@"+QS(a.gs,!1)+" ",b)+QS(l,!1);if(a instanceof Fl){d=!0;e=a;var m=e.gg;if(!0===e.bi)return"'{' "+QS(m,!1)+" '}'"}if(d&&(d=e.gg,!1===e.bi))return"'(' "+QS(d,!1)+" ')'";if(a instanceof Sl)return b=a.Dj.m(),b=new Ef(b,new y(n=>n.Wr())),ze(b,"{","; ","}");if(a instanceof Em)return a.rq.u();if(a instanceof Fm)return a.uu.gd.u();if(a instanceof Dm)return b=a.Lu,""+String.fromCharCode(34)+b+'"';if(a instanceof Gm)return a.Xq?"undefined": "null";if(a instanceof vl)return b=a.x,a=a.kt,a=a.b()?"":"::"+(a.o()|0),b+a;if(a instanceof Cl)return l=a.Fm,a=QS(a.ai,!1)+" : "+VO(l,!0),RS(a,b);if(a instanceof Ol&&(g=!0,h=a,e=h.Ej,d=h.Fj,e instanceof Gl))return a="("+Yz(e)+") \x3d\x3e "+QS(d,!1),RS(a,b);if(g)return a=h.Fj,a="(..."+QS(h.Ej,!1)+") \x3d\x3e "+QS(a,!1),RS(a,b);if(a instanceof Pl&&(k=!0,l=a,h=l.Za,g=l.Qb,g instanceof Gl))return a=QS(h,!(h instanceof Pl))+"("+Yz(g)+")",RS(a,b);if(k)return a=l.Za,l=l.Qb,a=QS(a,!(a instanceof Pl))+"(..."+ QS(l,!0)+")",RS(a,b);if(a instanceof yl)return b=a.ll.m(),b=new Ef(b,new y(n=>(n.j().yb.je?"mut ":"")+n.h().x+": "+QS(n.j().ya,!1))),ze(b,"{",", ","}");if(a instanceof Ql)return b=a.ml,"("+QS(a.Vl,!1)+")."+QS(b,!1);if(a instanceof Rl&&(l=a.ep,k=a.Zn,h=a.$n,g=a.Mm,null!==k))return a="let"+(l?" rec":"")+" "+k.x+" \x3d "+QS(h,!1)+" in "+QS(g,!1),RS(a,b);if(a instanceof Gl)return"["+Yz(a)+"]";if(a instanceof Tl)return l=a.ar,a=QS(a.br,!1)+" with "+QS(l,!1),RS(a,b);if(a instanceof Ul)return l=a.Hm,a="case "+ QS(a.Sn,!1)+" of { "+Wda(l,!0)+" }",RS(a,b);if(a instanceof Vl)return b=a.mp,"("+QS(a.lp,!1)+")["+QS(b,!1)+"]";if(a instanceof Wl)return l=a.Qn,a=QS(a.Gm,!1)+" \x3c- "+QS(l,!1),RS(a,b);if(a instanceof dm)return l=a.Zq,a="while "+QS(a.$q,!1)+" do "+QS(l,!1),RS(a,b);if(a instanceof Yl)return a="new "+QS(a.hp,!1),RS(a,b);if(a instanceof Xl)return l=a.zs,a=$da(a.bp),l.b()?l="":(l=l.o(),l=" else "+QS(l,!1)),RS("if "+a+l,b);if(a instanceof Il)return l=a.np,b=QS(a.nl,!1),a=l.m(),a=new Ef(a,new y(n=>VO(n, !0))),b+"\u2039"+ze(a,"",", ","")+"\u203a";if(a instanceof $l)return l=a.lt,b=QS(a.Yq,!1),a=l.m(),a=new Ef(a,new y(n=>n.Wr())),b+" where {"+ze(a,"","; ","")+"}";if(a instanceof Zl)return b=a.$o,"forall "+ze(a.qq,"",", ","")+". "+QS(b,!1);if(a instanceof Kl)return QS(a.cp,!0)+"!";if(a instanceof am)return"super";if(a instanceof bm)return b=a.Zo,QS(a.Vn,!1)+" \x3d "+QS(b,!1);if(a instanceof em)return'code"'+QS(a.Iq,!1)+'"';if(a instanceof fm)return"${"+QS(a.jt,!1)+"}";if(a instanceof cm)return b=a.Zs, QS(a.Ys,!1)+" "+gP(b);throw new w(a);},aA=function SS(a,b,c){var e=!1,g=null,h=!1,k=null,l=!1,m=null;if(a instanceof Ml)return m=a.Ml,TS("@"+a.gs.u()+" "+SS(m,!1,c),b);if(a instanceof Fl){e=!0;g=a;var n=g.gg;if(!0===g.bi)return SS(n,!1,c)}if(e&&(e=g.gg,!1===g.bi))return"("+SS(e,!1,c)+")";if(a instanceof Sl){l=a.Dj;var r=AP(c);b=x=>x instanceof Pm?SS(x,!1,r):x instanceof Ct?VO(x,r.fi):c.fi?no():x.u();if(l===u())b=u();else{a=l.e();m=a=new z(b(a),u());for(l=l.f();l!==u();)k=l.e(),k=new z(b(k),u()),m= m.p=k,l=l.f();b=a}a="{"+ON(r);m=ON(r);l=yP(c)+"\n}";return ze(b,a,m,l)}if(a instanceof Em)return a.rq.u();if(a instanceof Fm)return a.uu.gd.u();if(a instanceof Dm)return b=a.Lu,""+String.fromCharCode(34)+b+'"';if(a instanceof Gm)return b=a.Xq,c.fi?"()":b?"undefined":"null";if(a instanceof vl)return a.x;if(a instanceof Cl)return a=a.ai+" : "+VO(a.Fm,c.fi),TS(a,b);if(a instanceof Ol&&(h=!0,k=a,g=k.Ej,e=k.Fj,g instanceof Gl))return a="("+$z(g,c)+") \x3d\x3e "+SS(e,!1,c),TS(a,b);if(h)return a=k.Fj,a= "(..."+SS(k.Ej,!1,c)+") \x3d\x3e "+SS(a,!1,c),TS(a,b);if(a instanceof Pl&&(l=!0,m=a,k=m.Za,h=m.Qb,h instanceof Gl))return a=SS(k,!(k instanceof Pl),c)+"("+$z(h,c)+")",TS(a,b);if(l)return a=m.Za,m=m.Qb,a=SS(a,!(a instanceof Pl),c)+"(..."+SS(m,!0,c)+")",TS(a,b);if(a instanceof yl){var v=AP(c);b=a.ll.m();b=new Ef(b,new y(x=>(x.j().yb.je?"mut ":"")+x.h().x+": "+SS(x.j().ya,!1,v)));a="{"+ON(v);m=","+ON(v);l=ON(c)+"}";return ze(b,a,m,l)}if(a instanceof Ql)return b=a.Vl,a=a.ml,SS(b,!(b instanceof wm),c)+ "."+a;if(a instanceof Rl)return m=a.$n,l=a.Mm,a="let"+(a.ep?" rec":"")+" "+SS(a.Zn,!1,c)+" \x3d "+SS(m,!1,c)+" in "+SS(l,!1,c),TS(a,b);if(a instanceof Gl)return"["+$z(a,c)+"]";if(a instanceof Tl)return m=a.ar,a=SS(a.br,!1,c)+" with "+SS(m,!1,c),TS(a,b);if(a instanceof Ul)return m=a.Hm,a="case "+SS(a.Sn,!1,c)+" of {"+Yda(m,AP(c))+ON(c)+"}",TS(a,b);if(a instanceof Vl)return b=a.mp,"("+SS(a.lp,!1,c)+")["+SS(b,!1,c)+"]";if(a instanceof Wl)return m=a.Qn,a=SS(a.Gm,!1,c)+" \x3c- "+SS(m,!1,c),TS(a,b);if(a instanceof dm)return m=a.Zq,a="while "+SS(a.$q,!1,c)+" do "+SS(m,!1,c),TS(a,b);if(a instanceof Yl)return a="new "+SS(a.hp,!1,c),TS(a,b);if(a instanceof Xl)return m=a.zs,a=aea(a.bp,AP(c)),m.b()?m="":(m=m.o(),m=" else "+SS(m,!1,AP(c))),TS("if "+a+m,b);if(a instanceof Il){k=a.np;b=SS(a.nl,!1,c);a=c.Jq?"\x3c":"[";if(k===u())m=u();else for(m=k.e(),l=m=new z(VO(m,c.fi),u()),k=k.f();k!==u();)h=k.e(),h=new z(VO(h,c.fi),u()),l=l.p=h,k=k.f();return""+b+a+ze(m,"",", ","")+(c.Jq?"\x3e":"]")}if(a instanceof $l)return b= a.lt,a=SS(a.Yq,!1,c),b=new Sl(b),a+" where "+SS(b,!1,AP(c));if(a instanceof Zl){l=a.qq;b=a.$o;if(l===u())a=u();else for(a=l.e(),m=a=new z(VO(a,c.fi),u()),l=l.f();l!==u();)k=l.e(),k=new z(VO(k,c.fi),u()),m=m.p=k,l=l.f();return"forall "+ze(a,"",", ","")+". "+SS(b,!1,c)}if(a instanceof Kl)return SS(a.cp,!0,c)+"!";if(a instanceof am)return"super";if(a instanceof bm)return b=a.Zo,SS(a.Vn,!1,c)+" \x3d "+SS(b,!1,c);if(a instanceof cm)return b=a.Zs,SS(a.Ys,!1,c)+" { "+lea(b,c)+" }";if(a instanceof em)return'code"'+ SS(a.Iq,!1,c)+'"';if(a instanceof fm)return"${"+SS(a.jt,!1,c)+"}";throw new w(a);};function nx(a,b){a=Qn(a);if(a instanceof fe)return b.n(a.aa),el();if(a instanceof Ud)return a.fa;throw new w(a);}function Qn(a){try{t();var b=nea(a),c=a.A(),d=Cq(b,c);return new Ud(d)}catch(e){if(e instanceof KS)return b=e,t(),jr(),a=Ye(new Te(new Ue(J(new K,["Not a recognized type"]))),u()),b=b.lX.A(),a=G(new H,a,b),b=O().c,a=kr(0,new z(a,b),!0,ws()),new fe(a);throw e;}} var nea=function US(a){var c=!1,d=null,e=!1,g=null;a:{if(a instanceof vl){c=!0;d=a;var h=d.x;if(0<=h.length&&"`"===h.substring(0,1)){t();var k=Of(Q(),h,1,h.length);var l=new jt(new Ud(k),t().d);break a}}if(c){var m=d.x;if(0<=m.length&&"'"===m.substring(0,1)){l=new jt((t(),new Ud(m)),t().d);break a}}if(c)l=new Ep(d.x);else if(a instanceof Dl)l=new oP(a);else{if(a instanceof Pl){e=!0;g=a;var n=g.Za,r=g.Qb;if(n instanceof vl&&"-\x3e"===n.x&&null!==r){var v=mz(eu(),r);if(!v.b()&&null!==v.o()&&0===v.o().ab(2)){var x= v.o(),A=eB(x,0),B=v.o(),C=eB(B,1);var D=A instanceof Gl?!0:A instanceof Fl&&!1===A.bi&&A.gg instanceof Gl?!0:!1;if(D){l=new Wt(US(A),US(C));break a}}}}if(e){var F=g.Za,I=g.Qb;if(F instanceof vl&&"-\x3e"===F.x&&null!==I){var M=mz(eu(),I);if(!M.b()&&null!==M.o()&&0===M.o().ab(2)){var N=M.o(),P=eB(N,0),T=M.o(),Y=eB(T,1),Z=t().d,S=new Sn(t().d,US(P)),ea=G(new H,Z,S),ia=O().c;l=new Wt(new jP(new z(ea,ia)),US(Y));break a}}}if(e){var X=g.Za,sa=g.Qb;if(X instanceof vl&&"|"===X.x&&null!==sa){var Ja=mz(eu(), sa);if(!Ja.b()&&null!==Ja.o()&&0===Ja.o().ab(2)){var Xa=Ja.o(),Fa=eB(Xa,0),za=Ja.o(),Qa=eB(za,1);l=new iP(US(Fa),US(Qa));break a}}}if(e){var Ma=g.Za,Ga=g.Qb;if(Ma instanceof vl&&"\x26"===Ma.x&&null!==Ga){var ab=mz(eu(),Ga);if(!ab.b()&&null!==ab.o()&&0===ab.o().ab(2)){var Hb=ab.o(),bc=eB(Hb,0),yb=ab.o(),tb=eB(yb,1);l=new hP(US(bc),US(tb));break a}}}if(e){var eb=g.Za,kb=g.Qb;if(eb instanceof vl&&"\\"===eb.x&&null!==kb){var Rb=mz(eu(),kb);if(!Rb.b()&&null!==Rb.o()&&0===Rb.o().ab(2)){var Gb=Rb.o(),vb= eB(Gb,0),Tb=Rb.o(),Nb=eB(Tb,1),ic=US(vb),Va=new kP(US(Nb));cu();var cb=O().c,zb=du(0,new z(eb,new z(Nb,cb))),Ub=new hP(ic,Cq(Va,zb)),jb=Lq(g);l=Cq(Ub,jb);break a}}}if(e){var db=g.Za,ub=g.Qb;if(db instanceof vl&&"~"===db.x){l=new kP(US(ub));break a}}if(a instanceof Ol){var Aa=a.Fj;l=new Wt(US(a.Ej),US(Aa))}else{if(e){var va=g.Za,Ra=g.Qb;if(null!==Ra){var rb=mz(eu(),Ra);if(!rb.b()){var xb=rb.o(),mc=US(va);if(!(mc instanceof Ep))throw new KS(a);var Ha=xb.m(),Ka=new Ef(Ha,new y(Fc=>US(Fc)));Od();l=new pP(mc, Pd(u(),Ka));break a}}}if(a instanceof Gl){var Oa=a.Ra,Na=Fc=>{var qd=Fc.h(),Yb=Fc.j();a:{if(null!==Yb){var Nc=Yb.yb;Fc=Yb.ya;if(null!==Nc){Yb=Nc.je;Fc=US(Fc);Fc=new Sn(Yb?new L(Fc):R(),Fc);break a}}throw new w(Yb);}return G(new H,qd,Fc)};if(Oa===u())var Da=u();else{for(var ta=Oa.e(),Ya=new z(Na(ta),u()),dc=Ya,ka=Oa.f();ka!==u();){var ya=ka.e(),Sa=new z(Na(ya),u());dc=dc.p=Sa;ka=ka.f()}Da=Ya}l=new jP(Da)}else if(a instanceof Fl){var xc=a.bi,Sb=a.gg;if(Sb instanceof yl){if(!xc)throw new KS(a);}else if(xc)throw new KS(a); l=US(Sb)}else if(a instanceof Il){var uc=a.np,Lb=US(a.nl);if(!(Lb instanceof Ep))throw new KS(a);l=new pP(Lb,uc)}else if(a instanceof yl){var lc=a.ll,Xb=Fc=>{var qd=Fc.h(),Yb=Fc.j();a:{if(null!==Yb){var Nc=Yb.yb;Fc=Yb.ya;if(null!==Nc){Yb=Nc.je;Fc=US(Fc);Fc=new Sn(Yb?new L(Fc):R(),Fc);break a}}throw new w(Yb);}return G(new H,qd,Fc)};if(lc===u())var ec=u();else{for(var Ab=lc.e(),Ob=new z(Xb(Ab),u()),fb=Ob,Wa=lc.f();Wa!==u();){var bb=Wa.e(),Ia=new z(Xb(bb),u());fb=fb.p=Ia;Wa=Wa.f()}ec=Ob}l=new Tn(ec)}else if(a instanceof $l){var Ua=a.lt,pc=US(a.Yq),sc=O().c,Ba=Fc=>{if(Fc instanceof Cl){var qd=Fc.Fm;return new mP(US(Fc.ai),qd)}throw new KS(Fc);};if(Ua===u())var ob=u();else{for(var nc=Ua.e(),Ib=new z(Ba(nc),u()),vc=Ib,Vb=Ua.f();Vb!==u();){var fc=Vb.e(),Bc=new z(Ba(fc),u());vc=vc.p=Bc;Vb=Vb.f()}ob=Ib}l=new qP(pc,sc,ob)}else if(a instanceof Zl){var Pb=a.qq,Jb=a.$o,gc=Fc=>{t();return new Ud(Fc)};if(Pb===u())var Cb=u();else{for(var cc=Pb.e(),yc=new z(gc(cc),u()),Mc=yc,qc=Pb.f();qc!==u();){var oc=qc.e(),Qc=new z(gc(oc), u());Mc=Mc.p=Qc;qc=qc.f()}Cb=yc}l=new Vt(Cb,US(Jb))}else if(a instanceof Ql){var jc=a.ml,sb=US(a.Vl),Gc=new Ep(jc.x),Wb=jc.A();l=new tP(sb,Cq(Gc,Wb))}else throw new KS(a);}}}var Cc=a.A();return Cq(l,Cc)},oea=function VS(a){if(a instanceof Ol)return!0;if(a instanceof Fl){var c=a.gg;if(!1===a.bi)return VS(c)}if(a instanceof $l)return VS(a.Yq);if(a instanceof Ul){for(a=Uda(a.Hm);!a.b();){c=a.e();if(!VS(c))return!1;a=a.f()}return!0}return!1};function RS(a,b){return b?"("+a+")":a} function TS(a,b){return b?"("+a+")":a}function WS(){this.ld=null}WS.prototype=new SN;WS.prototype.constructor=WS;function XS(){}XS.prototype=WS.prototype;function YS(){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0}YS.prototype=new p;YS.prototype.constructor=YS;function ZS(){}ZS.prototype=YS.prototype; function ZO(a){if(0===(1&a.sc)<<24>>24&&0===(1&a.sc)<<24>>24){if(a instanceof jt){var b=O().c;b=new z(a,b)}else{b=EP(a);for(var c=null,d=null;b!==u();){for(var e=ZO(b.e()).m();e.s();){var g=new z(e.t(),u());null===d?c=g:d.p=g;d=g}b=b.f()}b=null===c?u():c}a.jf=b;a.sc=(1|a.sc)<<24>>24}return a.jf} function $S(a){if(0===(2&a.sc)<<24>>24&&0===(2&a.sc)<<24>>24){if(a instanceof jt){tp();var b=gq(a)}else{var c=EP(a);b=ap();for(c=Km(c);!c.b();){var d=c.e();b=$S(d).Ce(b);c=c.f()}}a.cf=b;a.sc=(2|a.sc)<<24>>24}return a.cf}f=YS.prototype;f.jn=function(){0===(4&this.sc)<<24>>24&&0===(4&this.sc)<<24>>24&&(this.df=zq(this),this.sc=(4|this.sc)<<24>>24);return this.df};f.rn=function(){return this.gf};f.fm=function(a){this.gf=a};f.qn=function(){return this.ff};f.em=function(a){this.ff=a};f.pn=function(){return this.ef}; f.on=function(a){this.ef=a};f.A=function(){0===(8&this.sc)<<24>>24&&0===(8&this.sc)<<24>>24&&(this.hf=Dq(this),this.sc=(8|this.sc)<<24>>24);return this.hf};function AB(a,b,c,d,e){this.HE=this.YN=this.Ou=this.kI=this.Vq=this.yA=null;if(null===a)throw null;this.kI=a;this.Ou=c;this.YN=d;this.HE=e;FD(this,a,b)}AB.prototype=new HD;AB.prototype.constructor=AB;AB.prototype.Kp=function(a,b){JD(this,a,b)}; AB.prototype.Tb=function(a,b){var c=this.kI.qa,d=this.kI;if(d.F){var e=ut(Q(),"| ",d.r)+("analyze1["+a+"] ")+b;ff(gf(),e+"\n")}d.r=1+d.r|0;try{if(b instanceof lx){var g=a.Ig(b.Xa);if(g instanceof L){var h=G(new H,!!g.k,b);this.Ou.bh(h,1+(this.Ou.n(h)|0)|0)}else if(t().d===g){this.YN.$(b);var k=G(new H,!0,b);this.Ou.bh(k,1+(this.Ou.n(k)|0)|0);var l=G(new H,!1,b);this.Ou.bh(l,1+(this.Ou.n(l)|0)|0)}else throw new w(g);var m=b.Sb;if(m instanceof L){var n=m.k,r=new zB(this.HE),v=a.Ig(b.Xa),x=v.b()?!1: v.o(),A=G(new H,b,x);r.en.L(A)||(r.en.$(A),this.Tb(a,n))}else if(t().d===m){var B=a.Ig(b.Xa);if(Nm(new E(B),(t(),new L(!1)))){var C=new zB(this.HE),D=G(new H,b,!0);if(!C.en.L(D)){C.en.$(D);var F=vy(b),I=new RB(a,b.Xa,!0);for(e=F;!e.b();){var M=e.e();this.Tb(I,M);e=e.f()}}}var N=a.Ig(b.Xa);if(Nm(new E(N),(t(),new L(!0)))){var P=new zB(this.HE),T=G(new H,b,!1);if(!P.en.L(T)){P.en.$(T);var Y=rA(b),Z=new RB(a,b.Xa,!1);for(a=Y;!a.b();){var S=a.e();this.Tb(Z,S);a=a.f()}}}}else throw new w(m);}else GD.prototype.Tb.call(this, a,b);var ea=void 0}finally{d.r=-1+d.r|0}dx(new E(c),d.qa)&&d.F&&(c=""+ut(Q(),"| ",d.r)+c.n(ea),ff(gf(),c+"\n"))};AB.prototype.$classData=q({GY:0},!1,"mlscript.TypeSimplifier$Analyze1$1$",{GY:1,kP:1,g:1,YZ:1});function aT(a){var b=GP(a.pp);return b.b()?a.oA:b} function bT(a,b){a=a.pp;if(a instanceof Ud)a=G(new H,0,a.fa);else if(a instanceof fe)a=G(new H,a.aa|0,"");else throw new w(a);var c=Fq(),d=cT();a=new dT(new eT(c,d),a);b=b.pp;if(b instanceof Ud)b=G(new H,0,b.fa);else if(b instanceof fe)b=G(new H,b.aa|0,"");else throw new w(b);return a.$l(b)}function BA(){this.q=null}BA.prototype=new YP;BA.prototype.constructor=BA;function fT(){}fT.prototype=BA.prototype;BA.prototype.rT=function(){return this}; var uw=function pea(a,b){a=G(new H,a,b);b=a.y;var d=a.w;if(XB(b)&&XB(d))return pa(b.rr().Dh,d.rr().Dh);d=a.y;b=a.w;if(d instanceof mx&&(d=d.hi,b instanceof mx))return gT(d,b.hi);d=a.y;b=a.w;if(d instanceof YB&&(d=d.Ym,b instanceof YB))return pea(d,b.Ym);if(XB(a.y)&&(a.w instanceof mx||a.w instanceof YB))return-1;if((a.y instanceof mx||a.y instanceof YB)&&XB(a.w))return 1;if(a.y instanceof mx&&a.w instanceof YB)return-1;if(a.y instanceof YB&&a.w instanceof mx)return 1;throw new w(a);}; function Uv(a){return!!(a&&a.$classData&&a.$classData.rb.uA)}function ko(a,b,c,d,e){this.jJ=a;this.re=b;this.hJ=c;this.iJ=d;this.RP=e;this.sF=!1}ko.prototype=new p;ko.prototype.constructor=ko;ko.prototype.xo=function(){return this.jJ};ko.prototype.zo=function(){return this.re};ko.prototype.u=function(){return"value "+this.jJ};ko.prototype.$classData=q({F_:0},!1,"mlscript.codegen.ValueSymbol",{F_:1,g:1,mt:1,cr:1});function hT(a,b){a.VP=b;yF(a,null,null,!0);return a} function iT(a,b,c){b=G(new H,b,c);c=O().c;hT(a,new z(b,c));return a}class jT extends Vd{constructor(){super();this.VP=null}}jT.prototype.$classData=q({S_:0},!1,"mlscript.ucs.DesugaringException",{S_:1,pc:1,g:1,l:1});function kT(){}kT.prototype=new p;kT.prototype.constructor=kT;function lT(){}lT.prototype=kT.prototype;function mT(){this.Iv=this.JK=null;nT=this;O();Od();this.JK=qp();this.Iv=Bq()}mT.prototype=new xQ;mT.prototype.constructor=mT; function up(a,b){if(!b)throw new Yj("assertion failed");}function At(a,b){if(!b)throw Kj("requirement failed");}function no(){tp();var a=new oT;yF(a,"an implementation is missing",null,!0);throw a;}mT.prototype.$classData=q({l3:0},!1,"scala.Predef$",{l3:1,eba:1,fba:1,g:1});var nT;function tp(){nT||(nT=new mT);return nT}function qea(a,b){switch(b){case 0:return a.Uj;case 1:return a.oj;case 2:return a.oi;case 3:return a.Xi;default:throw aL(new bL,b+" is out of bounds (min 0, max 3)");}} function rea(a,b){switch(b){case 0:return a.hv;case 1:return a.qt;case 2:return a.kr;case 3:return a.iv;case 4:return a.jv;default:throw aL(new bL,b+" is out of bounds (min 0, max 4)");}}function sea(a,b){switch(b){case 0:return a.lr;case 1:return a.mr;case 2:return a.rt;case 3:return a.st;case 4:return a.tt;case 5:return a.kv;default:throw aL(new bL,b+" is out of bounds (min 0, max 5)");}}function pT(){qT=this}pT.prototype=new p;pT.prototype.constructor=pT; pT.prototype.$classData=q({Q4:0},!1,"scala.collection.BuildFrom$",{Q4:1,g:1,Kba:1,Lba:1});var qT;function Yq(){qT||(qT=new pT)}function rT(){this.RB=null}rT.prototype=new p;rT.prototype.constructor=rT;function sT(){}sT.prototype=rT.prototype;rT.prototype.X=function(){return this.RB.ng(IB())};rT.prototype.Ib=function(a){return this.RB.pr(a,IB())};rT.prototype.Eb=function(){var a=this.RB,b=IB();return a.sy(b)};rT.prototype.Hh=function(a){var b=this.RB,c=IB();return b.pr(a,c)}; function tT(){this.Mt=null}tT.prototype=new p;tT.prototype.constructor=tT;function uT(){}uT.prototype=tT.prototype;tT.prototype.ng=function(a){return this.Mt.ng(a)};tT.prototype.pr=function(a,b){return this.Mt.pr(a,b)};tT.prototype.GB=function(a){return this.Mt.GB(a)};function vT(){this.sm=null}vT.prototype=new p;vT.prototype.constructor=vT;function wT(){}wT.prototype=vT.prototype;vT.prototype.X=function(){return this.sm.X()};vT.prototype.Ib=function(a){return this.sm.Ib(a)};vT.prototype.Eb=function(){return this.sm.Eb()}; function xT(a){this.c5=a}xT.prototype=new p;xT.prototype.constructor=xT;xT.prototype.vc=function(a){return this.c5.Ib(a)};xT.prototype.$classData=q({b5:0},!1,"scala.collection.IterableFactory$ToFactory",{b5:1,g:1,TK:1,l:1});function sp(a){a=a.m();return a.s()?new L(a.t()):R()}function yT(a){a=a.m();for(var b=a.t();a.s();)b=a.t();return b}function Is(a){return a.b()?R():new L(a.Mc())} function iv(a,b){if(0>b)return 1;var c=a.Q();if(0<=c)return c===b?0:c{var k=b.n(h);k=d.Hk(k,new U(()=>a.Ub().Eb()));h=c.n(h);return k.$(h)}));var e=nf(),g=new aw(e);d.Ca(new y(h=>{if(null!==h)g.rc=g.rc.Pn(G(new H,h.h(),h.j().Kb()));else throw new w(h);}));return g.rc}function FT(a,b){return a.Ub().Ib(GT(new HT,a,b))}function IT(a,b){var c=a.Ub();a=Fs(b)?new JT(a,b):a.m().nb(new U(()=>b.m()));return c.Ib(a)} function KT(a,b){var c=GT(new HT,a,new y(e=>b.n(e).h())),d=GT(new HT,a,new y(e=>b.n(e).j()));return G(new H,a.Ub().Ib(c),a.Ub().Ib(d))}function tea(a,b){for(var c=!1;!c&&a.s();)c=a.t(),c=ml(nl(),c,b);return c}function LT(a,b,c){var d=0c?-1:c<=b?0:c-b|0;return 0===c?Rq().Pa:new PT(a,b,c)} function QT(){this.Pa=null;RT=this;this.Pa=new ST}QT.prototype=new p;QT.prototype.constructor=QT;QT.prototype.Eb=function(){return new TT};QT.prototype.X=function(){return this.Pa};QT.prototype.Ib=function(a){return a.m()};QT.prototype.$classData=q({d5:0},!1,"scala.collection.Iterator$",{d5:1,g:1,bg:1,l:1});var RT;function Rq(){RT||(RT=new QT);return RT}function uea(a){var b=qp();a.Nt=b}function UT(){this.Nt=null}UT.prototype=new p;UT.prototype.constructor=UT;function VT(){}VT.prototype=UT.prototype; UT.prototype.Hh=function(a){return this.Nt.Hh(a)};UT.prototype.Ib=function(a){return this.Nt.Ib(a)};UT.prototype.X=function(){return this.Nt.X()};UT.prototype.Eb=function(){return this.Nt.Eb()};function Pv(a){this.K5=a}Pv.prototype=new p;Pv.prototype.constructor=Pv;Pv.prototype.vc=function(a){return this.K5.Ib(a)};Pv.prototype.$classData=q({J5:0},!1,"scala.collection.MapFactory$ToFactory",{J5:1,g:1,TK:1,l:1});function WT(){this.Pt=null}WT.prototype=new p;WT.prototype.constructor=WT; function XT(){}XT.prototype=WT.prototype;WT.prototype.CF=function(a,b){return this.Pt.CF(a,b)};WT.prototype.qr=function(a,b){return this.Pt.qr(a,b)};WT.prototype.Hd=function(a){return this.Pt.Hd(a)};WT.prototype.Dv=function(a){return this.Pt.Dv(a)};function tv(a,b){this.b6=a;this.a6=b}tv.prototype=new p;tv.prototype.constructor=tv;tv.prototype.vc=function(a){return this.b6.qr(a,this.a6)};tv.prototype.$classData=q({$5:0},!1,"scala.collection.SortedMapFactory$ToFactory",{$5:1,g:1,TK:1,l:1}); function YT(){}YT.prototype=new p;YT.prototype.constructor=YT;function ZT(a,b){if(b&&b.$classData&&b.$classData.rb.jd)return b;if(Fs(b))return new $T(new U(()=>b.m()));a=aU(FK(),b);return bU(new cU,a)}YT.prototype.Eb=function(){var a=new DF;return new dU(a,new y(b=>ZT(eU(),b)))};YT.prototype.X=function(){fU||(fU=new gU);return fU};YT.prototype.Ib=function(a){return ZT(0,a)};YT.prototype.$classData=q({r6:0},!1,"scala.collection.View$",{r6:1,g:1,bg:1,l:1});var hU; function eU(){hU||(hU=new YT);return hU}function aI(a,b,c,d,e,g){this.Db=a;this.qc=b;this.te=c;this.Vg=d;this.kd=e;this.ui=g}aI.prototype=new XQ;aI.prototype.constructor=aI;f=aI.prototype;f.ka=function(){return this.kd};f.uc=function(){return this.ui};f.Ef=function(a){return this.te.a[a<<1]};f.Tf=function(a){return this.te.a[1+(a<<1)|0]};f.rv=function(a){return G(new H,this.te.a[a<<1],this.te.a[1+(a<<1)|0])};f.Jb=function(a){return this.Vg.a[a]}; f.qh=function(a){return this.te.a[(-1+this.te.a.length|0)-a|0]};f.BJ=function(a,b,c,d){var e=kI(HH(),c,d),g=lI(HH(),e);if(0!==(this.Db&g)){if(b=oI(HH(),this.Db,e,g),ml(nl(),a,this.Ef(b)))return this.Tf(b)}else if(0!==(this.qc&g))return this.qh(oI(HH(),this.qc,e,g)).BJ(a,b,c,5+d|0);throw AH("key not found: "+a);}; f.KF=function(a,b,c,d){var e=kI(HH(),c,d),g=lI(HH(),e);return 0!==(this.Db&g)?(b=oI(HH(),this.Db,e,g),c=this.Ef(b),ml(nl(),a,c)?new L(this.Tf(b)):R()):0!==(this.qc&g)?(e=oI(HH(),this.qc,e,g),this.qh(e).KF(a,b,c,5+d|0)):R()};f.KJ=function(a,b,c,d,e){var g=kI(HH(),c,d),h=lI(HH(),g);return 0!==(this.Db&h)?(b=oI(HH(),this.Db,g,h),c=this.Ef(b),ml(nl(),a,c)?this.Tf(b):Es(e)):0!==(this.qc&h)?(g=oI(HH(),this.qc,g,h),this.qh(g).KJ(a,b,c,5+d|0,e)):Es(e)}; f.DF=function(a,b,c,d){var e=kI(HH(),c,d),g=lI(HH(),e);return 0!==(this.Db&g)?(c=oI(HH(),this.Db,e,g),this.Vg.a[c]===b&&ml(nl(),a,this.Ef(c))):0!==(this.qc&g)&&this.qh(oI(HH(),this.qc,e,g)).DF(a,b,c,5+d|0)}; function iU(a,b,c,d,e,g,h){var k=kI(HH(),e,g),l=lI(HH(),k);if(0!==(a.Db&l)){var m=oI(HH(),a.Db,k,l);k=a.Ef(m);var n=a.Jb(m);if(n===d&&ml(nl(),k,b))return h?(e=a.Tf(m),Object.is(k,b)&&Object.is(e,c)||(l=a.pi(l)<<1,b=a.te,e=new zc(b.a.length),b.wa(0,e,0,b.a.length),e.a[1+l|0]=c,a=new aI(a.Db,a.qc,e,a.Vg,a.kd,a.ui)),a):a;m=a.Tf(m);h=$G(bH(),n);c=jU(a,k,m,n,h,b,c,d,e,5+g|0);e=a.pi(l);d=e<<1;g=(-2+a.te.a.length|0)-a.hm(l)|0;k=a.te;b=new zc(-1+k.a.length|0);k.wa(0,b,0,d);k.wa(2+d|0,b,d,g-d|0);b.a[g]=c; k.wa(2+g|0,b,1+g|0,-2+(k.a.length-g|0)|0);e=gI(a.Vg,e);return new aI(a.Db^l,a.qc|l,b,e,(-1+a.kd|0)+c.ka()|0,(a.ui-h|0)+c.uc()|0)}if(0!==(a.qc&l))return k=oI(HH(),a.qc,k,l),k=a.qh(k),c=k.KC(b,c,d,e,5+g|0,h),c===k?a:kU(a,l,k,c);g=a.pi(l);k=g<<1;n=a.te;h=new zc(2+n.a.length|0);n.wa(0,h,0,k);h.a[k]=b;h.a[1+k|0]=c;n.wa(k,h,2+k|0,n.a.length-k|0);c=hI(a.Vg,g,d);return new aI(a.Db|l,a.qc,h,c,1+a.kd|0,a.ui+e|0)} function lU(a,b,c,d,e,g,h){var k=kI(HH(),e,g),l=lI(HH(),k);if(0!==(a.Db&l)){var m=oI(HH(),a.Db,k,l);k=a.Ef(m);var n=a.Jb(m);if(n===d&&ml(nl(),k,b))return d=a.Tf(m),Object.is(k,b)&&Object.is(d,c)||(l=a.pi(l)<<1,a.te.a[1+l|0]=c),h;var r=a.Tf(m);m=$G(bH(),n);c=jU(a,k,r,n,m,b,c,d,e,5+g|0);mU(a,l,m,c);return h|l}if(0!==(a.qc&l))return k=oI(HH(),a.qc,k,l),r=a.qh(k),k=r.ka(),n=r.uc(),m=h,r instanceof aI&&0!==(l&h)?(lU(r,b,c,d,e,5+g|0,0),h=r):(h=r.KC(b,c,d,e,5+g|0,!0),h!==r&&(m|=l)),a.te.a[(-1+a.te.a.length| 0)-a.hm(l)|0]=h,a.kd=(a.kd-k|0)+h.ka()|0,a.ui=(a.ui-n|0)+h.uc()|0,m;g=a.pi(l);k=g<<1;n=a.te;m=new zc(2+n.a.length|0);n.wa(0,m,0,k);m.a[k]=b;m.a[1+k|0]=c;n.wa(k,m,2+k|0,n.a.length-k|0);a.Db|=l;a.te=m;a.Vg=hI(a.Vg,g,d);a.kd=1+a.kd|0;a.ui=a.ui+e|0;return h} function nU(a,b,c,d,e){var g=kI(HH(),d,e),h=lI(HH(),g);if(0!==(a.Db&h)){if(g=oI(HH(),a.Db,g,h),c=a.Ef(g),ml(nl(),c,b)){b=a.Db;2===nI(UH(),b)?(b=a.qc,b=0===nI(UH(),b)):b=!1;if(b)return h=0===e?a.Db^h:lI(HH(),kI(HH(),d,0)),0===g?new aI(h,0,new zc([a.Ef(1),a.Tf(1)]),new Xc(new Int32Array([a.Vg.a[1]])),1,$G(bH(),a.Jb(1))):new aI(h,0,new zc([a.Ef(0),a.Tf(0)]),new Xc(new Int32Array([a.Vg.a[0]])),1,$G(bH(),a.Jb(0)));e=a.pi(h);b=e<<1;c=a.te;g=new zc(-2+c.a.length|0);c.wa(0,g,0,b);c.wa(2+b|0,g,b,-2+(c.a.length- b|0)|0);e=gI(a.Vg,e);return new aI(a.Db^h,a.qc,g,e,-1+a.kd|0,a.ui-d|0)}}else if(0!==(a.qc&h)){g=oI(HH(),a.qc,g,h);g=a.qh(g);d=g.hR(b,c,d,5+e|0);if(d===g)return a;e=d.ka();if(1===e)if(a.kd===g.ka())a=d;else{b=(-1+a.te.a.length|0)-a.hm(h)|0;c=a.pi(h);var k=c<<1,l=d.Ef(0),m=d.Tf(0),n=a.te;e=new zc(1+n.a.length|0);n.wa(0,e,0,k);e.a[k]=l;e.a[1+k|0]=m;n.wa(k,e,2+k|0,b-k|0);n.wa(1+b|0,e,2+b|0,-1+(n.a.length-b|0)|0);b=hI(a.Vg,c,d.Jb(0));a=new aI(a.Db|h,a.qc^h,e,b,1+(a.kd-g.ka()|0)|0,(a.ui-g.uc()|0)+d.uc()| 0)}else a=1ml(nl(),g.h(),a)),!0);if(1===d.K()){var e=d.va(0);if(null===e)throw new w(e);d=e.h();e=e.j();return new aI(lI(HH(),kI(HH(),c,0)),0,new zc([d,e]),new Xc(new Int32Array([b])),1,c)}return new oU(b,c,d)}return this};f.nB=function(){return!1};f.HB=function(){return 0};f.qh=function(){throw aL(new bL,"No sub-nodes present in hash-collision leaf node.");};f.jy=function(){return!0};f.uy=function(){return this.of.K()};f.Ef=function(a){return this.of.va(a).h()}; f.Tf=function(a){return this.of.va(a).j()};f.rv=function(a){return this.of.va(a)};f.Jb=function(){return this.Py};f.Ca=function(a){this.of.Ca(a)};f.og=function(a){this.of.Ca(new y(b=>{if(null!==b)return a.ba(b.h(),b.j());throw new w(b);}))};f.GJ=function(a){for(var b=this.of.m();b.s();){var c=b.t();(0,a.aM)(c.h(),c.j(),this.Py)}}; f.i=function(a){if(a instanceof oU){if(this===a)return!0;if(this.Eo===a.Eo&&this.of.K()===a.of.K()){for(var b=this.of.m();b.s();){var c=b.t();if(null===c)throw new w(c);var d=c.j();c=KU(a,c.h());if(0>c||!ml(nl(),d,a.of.va(c).j()))return!1}return!0}}return!1}; f.qQ=function(a,b){a=MU(this.of,a,b);b=a.K();if(0===b)return bI().Ty;if(1===b){b=a.e();if(null===b)throw new w(b);a=b.h();b=b.j();return new aI(lI(HH(),kI(HH(),this.Eo,0)),0,new zc([a,b]),new Xc(new Int32Array([this.Py])),1,this.Eo)}return b===this.of.K()?this:new oU(this.Py,this.Eo,a)};f.B=function(){throw nv("Trie nodes do not support hashing.");};f.uc=function(){return Math.imul(this.of.K(),this.Eo)};f.kQ=function(){return new oU(this.Py,this.Eo,this.of)}; f.iQ=function(a){if(a instanceof oU)if(a===this)a=this;else{for(var b=null,c=this.of.m();c.s();){var d=c.t();0>KU(a,d.h())&&(null===b&&(b=new NU,OU(b,a.of)),PU(b,d))}a=null===b?a:new oU(this.Py,this.Eo,b.im())}else{if(a instanceof aI)throw nv("Cannot concatenate a HashCollisionMapNode with a BitmapIndexedMapNode");throw new w(a);}return a};f.mB=function(a){return this.qh(a)};f.$classData=q({T6:0},!1,"scala.collection.immutable.HashCollisionMapNode",{T6:1,O7:1,lC:1,g:1}); function FU(a,b,c){this.Yv=a;this.Wp=b;this.Kg=c;At(tp(),2<=this.Kg.K())}FU.prototype=new kR;FU.prototype.constructor=FU;f=FU.prototype;f.yt=function(a,b,c){return this.Wp===c?QU(this.Kg,a):!1};f.JC=function(a,b,c,d){return this.yt(a,b,c,d)?this:new FU(b,c,this.Kg.hn(a))};f.eG=function(a,b,c,d){return this.yt(a,b,c,d)?(d=MU(this.Kg,new y(e=>ml(nl(),e,a)),!0),1===d.K()?new CJ(lI(HH(),kI(HH(),c,0)),0,new zc([d.va(0)]),new Xc(new Int32Array([b])),1,c):new FU(b,c,d)):this};f.nB=function(){return!1}; f.HB=function(){return 0};f.Nh=function(){throw aL(new bL,"No sub-nodes present in hash-collision leaf node.");};f.jy=function(){return!0};f.uy=function(){return this.Kg.K()};f.ed=function(a){return this.Kg.va(a)};f.Jb=function(){return this.Yv};f.ka=function(){return this.Kg.K()};f.Ca=function(a){for(var b=this.Kg.m();b.s();)a.n(b.t())};f.uc=function(){return Math.imul(this.Kg.K(),this.Wp)}; f.EJ=function(a,b){a=MU(this.Kg,a,b);b=a.K();return 0===b?DJ().mw:1===b?new CJ(lI(HH(),kI(HH(),this.Wp,0)),0,new zc([a.e()]),new Xc(new Int32Array([this.Yv])),1,this.Wp):a.K()===this.Kg.K()?this:new FU(this.Yv,this.Wp,a)};f.mQ=function(a,b){return this.EJ(new y(c=>a.yt(c,this.Yv,this.Wp,b)),!0)};f.i=function(a){if(a instanceof FU){if(this===a)return!0;if(this.Wp===a.Wp&&this.Kg.K()===a.Kg.K()){a=a.Kg;for(var b=!0,c=this.Kg.m();b&&c.s();)b=c.t(),b=QU(a,b);return b}}return!1}; f.B=function(){throw nv("Trie nodes do not support hashing.");};f.jQ=function(a){if(a instanceof FU){if(a===this)return this;var b=null;for(a=a.Kg.m();a.s();){var c=a.t();QU(this.Kg,c)||(null===b&&(b=new NU,OU(b,this.Kg)),PU(b,c))}return null===b?this:new FU(this.Yv,this.Wp,b.im())}if(a instanceof CJ)throw nv("Cannot concatenate a HashCollisionSetNode with a BitmapIndexedSetNode");throw new w(a);};f.FJ=function(a){for(var b=this.Kg.m();b.s();){var c=b.t();a.ba(c,this.Yv)}}; f.lQ=function(){return new FU(this.Yv,this.Wp,this.Kg)};f.mB=function(a){return this.Nh(a)};f.$classData=q({U6:0},!1,"scala.collection.immutable.HashCollisionSetNode",{U6:1,v8:1,lC:1,g:1});function RU(){this.Mr=null;SU=this;var a=bI();this.Mr=new TU(a.Ty)}RU.prototype=new p;RU.prototype.constructor=RU;f=RU.prototype;f.Hh=function(a){return Dz(0,a)};function Dz(a,b){return b instanceof TU?b:UU(VU(new WU,b))}f.Eb=function(){return new WU};f.Ib=function(a){return Dz(0,a)};f.X=function(){return this.Mr}; f.$classData=q({W6:0},!1,"scala.collection.immutable.HashMap$",{W6:1,g:1,Ey:1,l:1});var SU;function Ez(){SU||(SU=new RU);return SU}function XU(){this.Fo=null;YU=this;var a=DJ();this.Fo=new ZU(a.mw)}XU.prototype=new p;XU.prototype.constructor=XU;function $U(a,b){return b instanceof ZU?b:0===b.Q()?a.Fo:aV(bV(new cV,b))}XU.prototype.Eb=function(){return new cV};XU.prototype.Ib=function(a){return $U(this,a)};XU.prototype.X=function(){return this.Fo}; XU.prototype.$classData=q({b7:0},!1,"scala.collection.immutable.HashSet$",{b7:1,g:1,bg:1,l:1});var YU;function dV(){YU||(YU=new XU);return YU}function eV(a,b){this.o7=a;this.p7=b}eV.prototype=new p;eV.prototype.constructor=eV;eV.prototype.e=function(){return this.o7};eV.prototype.Lf=function(){return this.p7};eV.prototype.$classData=q({n7:0},!1,"scala.collection.immutable.LazyList$State$Cons",{n7:1,g:1,m7:1,l:1});function fV(){}fV.prototype=new p;fV.prototype.constructor=fV; fV.prototype.LJ=function(){throw AH("head of empty lazy list");};fV.prototype.Lf=function(){throw nv("tail of empty lazy list");};fV.prototype.e=function(){this.LJ()};fV.prototype.$classData=q({q7:0},!1,"scala.collection.immutable.LazyList$State$Empty$",{q7:1,g:1,m7:1,l:1});var gV;function hV(){gV||(gV=new fV);return gV}function iV(){}iV.prototype=new p;iV.prototype.constructor=iV;f=iV.prototype;f.Hh=function(a){return pp(0,a)}; function pp(a,b){ZJ(b)&&b.b()?a=nf():jV(b)?a=b:(a=kV(new lV,b),a=a.Sy?UU(a.Wt):a.bq);return a}f.Eb=function(){return new lV};f.Ib=function(a){return pp(0,a)};f.X=function(){return nf()};f.$classData=q({u7:0},!1,"scala.collection.immutable.Map$",{u7:1,g:1,Ey:1,l:1});var mV;function qp(){mV||(mV=new iV);return mV}function nV(){}nV.prototype=new p;nV.prototype.constructor=nV; function Aq(a,b){return b&&b.$classData&&b.$classData.rb.OL?MF(oV(new OF,b)):0===b.Q()?ap():b&&b.$classData&&b.$classData.rb.dq?b:MF(oV(new OF,b))}nV.prototype.Eb=function(){return new OF};nV.prototype.Ib=function(a){return Aq(0,a)};nV.prototype.X=function(){return ap()};nV.prototype.$classData=q({j8:0},!1,"scala.collection.immutable.Set$",{j8:1,g:1,bg:1,l:1});var pV;function Bq(){pV||(pV=new nV);return pV}function qV(){}qV.prototype=new p;qV.prototype.constructor=qV;f=qV.prototype; f.CF=function(a,b){return rV(0,a,b)};function rV(a,b,c){if(b instanceof sV&&(a=b.We,null===c?null===a:c.i(a)))return b;if(b&&b.$classData&&b.$classData.rb.YB&&(a=b.se(),null===c?null===a:c.i(a))){a=new sV;var d=nJ(),e=b.m();b=b.ka();var g=32-Math.clz32(b)|0;b=Eda(d,1,b,e,g);return tV(a,b,c)}a=null;for(b=b.m();b.s();){e=b.t();if(null===e)throw new w(e);d=e.h();e=e.j();a=gJ(nJ(),a,d,e,!0,c)}return tV(new sV,a,c)}f.Dv=function(a){return new uV(a)};f.qr=function(a,b){return rV(0,a,b)};f.Hd=function(a){return vV(a)}; f.$classData=q({H8:0},!1,"scala.collection.immutable.TreeMap$",{H8:1,g:1,xL:1,l:1});var wV;function xV(){wV||(wV=new qV);return wV}function yV(a){this.aH=this.Yy=null;if(null===a)throw null;this.aH=a;this.Yy=null}yV.prototype=new gS;yV.prototype.constructor=yV;yV.prototype.ey=function(a,b){this.Yy=fR(this.aH,this.Yy,a,b)};yV.prototype.ba=function(a,b){this.ey(a,b)};yV.prototype.$classData=q({K8:0},!1,"scala.collection.immutable.TreeMap$TreeMapBuilder$adder$",{K8:1,nH:1,g:1,kz:1}); function zV(a){this.GS=this.cH=null;if(null===a)throw null;this.GS=a;this.cH=a.Hf}zV.prototype=new cS;zV.prototype.constructor=zV;zV.prototype.n=function(a){var b=nJ();this.cH=pI(PI(b,this.cH,a,this.GS.Xe))};zV.prototype.$classData=q({O8:0},!1,"scala.collection.immutable.TreeSet$sub$1$",{O8:1,dM:1,g:1,la:1});function AV(){}AV.prototype=new p;AV.prototype.constructor=AV;f=AV.prototype;f.Hh=function(a){return Uy(a)};function Uy(a){var b=a.Q();return BV(CV(new DV,0"boolean"===typeof a),laa=q({U0:0},!1,"java.lang.Character",{U0:1,g:1,l:1,nf:1,Et:1},a=>a instanceof ba);function iL(a){var b=new iW;yF(b,a,null,!0);return b}class iW extends xF{}iW.prototype.$classData=q({Te:0},!1,"java.lang.RuntimeException",{Te:1,qd:1,pc:1,g:1,l:1});function jW(){this.ly=null}jW.prototype=new p;jW.prototype.constructor=jW; function kW(a,b){a=a.ly;a.ja=""+a.ja+b}function lW(a,b){a=a.ly;b=String.fromCharCode(b);a.ja=""+a.ja+b}jW.prototype.eM=function(a,b){return this.ly.ja.substring(a,b)};jW.prototype.u=function(){return this.ly.ja};jW.prototype.uJ=function(a){var b=this.ly;b.ja=""+b.ja+a};jW.prototype.$classData=q({v1:0},!1,"java.lang.StringBuffer",{v1:1,g:1,ZJ:1,rQ:1,l:1});function LQ(a){a.ja="";return a}function Uq(a,b){LQ(a);if(null===b)throw le();a.ja=b;return a} function Cu(a){var b=new Sq;LQ(b);if(0>a)throw new Aj;return b}function Sq(){this.ja=null}Sq.prototype=new p;Sq.prototype.constructor=Sq;function MQ(a,b,c,d){b=null===b?"null":b;c="string"===typeof b?b.substring(c,d):b.eM(c,d);a.ja=""+a.ja+c}function mW(a,b){b=AM(zH(),b,0,b.a.length);a.ja=""+a.ja+b}f=Sq.prototype;f.u=function(){return this.ja};f.K=function(){return this.ja.length};f.eM=function(a,b){return this.ja.substring(a,b)};f.uJ=function(a){this.ja=""+this.ja+a}; f.$classData=q({w1:0},!1,"java.lang.StringBuilder",{w1:1,g:1,ZJ:1,rQ:1,l:1});function nW(a){return 0===a.$h?(a=a.Bg,!(-1===a.W&&-1===a.Y)):!1}function oW(a,b){var c=a.xb,d=c>>31,e=-c|0;c=0!==c?~d:-d|0;var g=CR(a);d=g>>31;g=e+g|0;e=(-2147483648^g)<(-2147483648^e)?1+(c+d|0)|0:c+d|0;if(0===e?-2147483629<(-2147483648^g):0a.$h&&(a.Bg=b.xl())}function qW(a){a.pu=null;a.lq=0;a.$h=0;a.Bg=aa;a.xb=0;a.cs=0}function kN(a,b){var c=new mr;qW(c);c.Bg=a;c.xb=b;c.$h=gN(hN(),a);return c}function eN(a,b){var c=new mr;qW(c);c.Bg=new ma(a,a>>31);c.xb=b;hN();a=32-Math.clz32(0>a?~a:a)|0;c.$h=a;return c} function nr(a,b,c){qW(a);var d=-1+(0+c|0)|0;if(null===b)throw oL("in \x3d\x3d null");if(d>=b.a.length||0>=c||0>d)throw new ZL("Bad offset/length: offset\x3d0 len\x3d"+c+" in.length\x3d"+b.a.length);var e=0;if(0<=d&&43===b.a[0]){if(e=1+e|0,e>31,h= TH(UH(),e,10),e=h>>31,h=b-h|0,a.xb=h,k=a.xb,h!==k||((-2147483648^h)>(-2147483648^b)?-1+(d-e|0)|0:d-e|0)!==k>>31))throw new ZL("Scale out of range");if(19>g){e=uM();""===c&&pM(c);d=0;b=!1;switch(c.charCodeAt(0)){case 43:d=1;break;case 45:d=1,b=!0}g=c.length;if(d>=g)pM(c),e=void 0;else{h=(e.RF?e.QF:nM(e))[10];for(k=h.j1;;){if(e=dl?48===l:0<=kj(Pj(),RL(e),l)}if(e)d=1+d|0;else break}(g-d|0)>Math.imul(3,k)&&pM(c);e=1+Bb(-1+(g-d|0)|0,k)|0;l=d+e|0;var m=qM(d,l,c);if(l=== g)e=new ma(m,0);else{e=h.DQ;d=e.W;e=e.Y;k=l+k|0;var n=65535&m,r=m>>>16|0,v=65535&d,x=d>>>16|0,A=Math.imul(n,v);v=Math.imul(r,v);var B=Math.imul(n,x);n=A+((v+B|0)<<16)|0;A=(A>>>16|0)+B|0;m=((Math.imul(m,e)+Math.imul(r,x)|0)+(A>>>16|0)|0)+(((65535&A)+v|0)>>>16|0)|0;l=qM(l,k,c);l=n+l|0;m=(-2147483648^l)<(-2147483648^n)?1+m|0:m;k===g?e=new ma(l,m):(n=h.k1,h=n.W,n=n.Y,g=qM(k,g,c),(m===n?(-2147483648^l)>(-2147483648^h):m>n)&&pM(c),n=65535&l,h=l>>>16|0,x=65535&d,k=d>>>16|0,r=Math.imul(n,x),x=Math.imul(h, x),A=Math.imul(n,k),n=r+((x+A|0)<<16)|0,r=(r>>>16|0)+A|0,e=(((Math.imul(l,e)+Math.imul(m,d)|0)+Math.imul(h,k)|0)+(r>>>16|0)|0)+(((65535&r)+x|0)>>>16|0)|0,d=n+g|0,e=(-2147483648^d)<(-2147483648^n)?1+e|0:e,-2147483648===(-2147483648^e)&&(-2147483648^d)<(-2147483648^g)&&pM(c),e=new ma(d,e))}}d=e.W;e=e.Y;b?(b=-d|0,d=0!==d?~e:-e|0,(0===d?0!==b:0e&&pM(c),c=new ma(d,e));a.Bg=c;a.$h=gN(hN(),a.Bg)}else HR(a,eM(c))} function FR(a,b,c){qW(a);if(null===b)throw oL("unscaledVal \x3d\x3d null");a.xb=c;HR(a,b);return a}function mr(){this.pu=null;this.lq=0;this.bs=null;this.$h=0;this.Bg=aa;this.cs=this.xb=0}mr.prototype=new wM;mr.prototype.constructor=mr;function rW(a){if(64>a.$h){if(0>a.Bg.Y)return-1;a=a.Bg;var b=a.Y;return(0===b?0!==a.W:0a.$h){var c=a.Bg;if(0===c.W&&-2147483648===c.Y)b=19;else{Pj();b=hN().lz;if(0>c.Y){var d=c.W;c=c.Y;d=new ma(-d|0,0!==d?~c:-c|0)}else d=c;b:{c=0;for(var e=b.a.length;;){if(c===e){b=-1-c|0;break b}var g=(c+e|0)>>>1|0,h=b.a[g],k=Za(new ma(h.W,h.Y));h=k.W;k=k.Y;h=ua(xa(),d.W,d.Y,h,k);if(0>h)e=g;else{if(0===h){b=g;break b}c=1+g|0}}}b=0>b?-1-b|0:1+b|0}}else b=1+Eb(.3010299956639812*(-1+a.$h|0))|0,d=$M(a),c=fi(),b=0!==GR(d,Pi(c,new ma(b,b>>31))).Ya? 1+b|0:b;a.cs=b}return a.cs}function sW(a){if(nW(a))return a;var b=-1+fi().fs.a.length|0,c=1,d=$M(a),e=a=a.xb;for(a>>=31;;){if(ER(d,0))c=e,b=d,c=new ma(c,a);else{var g=tW(d,fi().fs.a[c]);if(0===g.oM.Ya){d=g.nM;var h=c;g=h>>31;var k=a;a=e-h|0;e=(-2147483648^a)>(-2147483648^e)?-1+(k-g|0)|0:k-g|0;c=ca.$h&&64>b.$h){d=a.Bg;c=b.Bg;var e=d.Y,g=c.Y;if(e===g?(-2147483648^d.W)<(-2147483648^c.W):e(-2147483648^b.W):d>c)?1:0}e=a.xb;g=e>>31;d=b.xb;var h=d>>31;d=e-d|0;e=(-2147483648^d)>(-2147483648^e)?-1+(g-h|0)|0:g-h|0;g=CR(a)-CR(b)|0;h=g>>31;var k=1+d|0,l=0===k?1+e|0:e;if(h===l?(-2147483648^g)>(-2147483648^k):h>l)return c;h=g>>31;k=-1+d|0;l=-1!==k?e:-1+e|0;if(h===l?(-2147483648^ g)<(-2147483648^k):he)c=fi(),a=Ki(a,Pi(c,new ma(-d|0,0!==d?~e:-e|0)));else if(0===e?0!==d:0this.$h){a=a.Bg;var b=this.Bg;return a.W===b.W&&a.Y===b.Y}return this.bs.i(a.bs)}return!1}; f.B=function(){if(0===this.lq)if(64>this.$h){this.lq=this.Bg.W;var a=this.Bg.Y;this.lq=Math.imul(33,this.lq)+a|0;this.lq=Math.imul(17,this.lq)+this.xb|0}else this.lq=Math.imul(17,this.bs.B())+this.xb|0;return this.lq}; f.u=function(){if(null!==this.pu)return this.pu;if(32>this.$h)return this.pu=waa(bi(),this.Bg,this.xb);var a=$M(this);a=Vh(bi(),a);if(0===this.xb)return a;var b=0>$M(this).Ya?2:1,c=a.length,d=this.xb,e=d>>31,g=-d|0;e=0!==d?~e:-e|0;var h=c>>31;d=g+c|0;e=(-2147483648^d)<(-2147483648^g)?1+(e+h|0)|0:e+h|0;h=b>>31;g=d-b|0;d=(-2147483648^g)>(-2147483648^d)?-1+(e-h|0)|0:e-h|0;0a.xb){var b=$M(a),c=fi();a=a.xb;var d=a>>31;return Ki(b,Pi(c,new ma(-a|0,0!==a?~d:-d|0)))}b=$M(a);c=fi();a=a.xb;return GR(b,Pi(c,new ma(a,a>>31)))} function pW(a){if(0===a.xb||nW(a))return $M(a);if(0>a.xb){var b=$M(a),c=fi();a=a.xb;var d=a>>31;return Ki(b,Pi(c,new ma(-a|0,0!==a?~d:-d|0)))}if(a.xb>CR(a)||a.xb>wW($M(a)))throw new qb("Rounding necessary");b=$M(a);c=fi();a=a.xb;a=DR(b,Pi(c,new ma(a,a>>31)));if(0!==a.a[1].Ya)throw new qb("Rounding necessary");return a.a[0]}f.xl=function(){return-64>=this.xb||this.xb>CR(this)?aa:vW(this).xl()};f.Zi=function(){return-32>=this.xb||this.xb>CR(this)?0:vW(this).Zi()}; f.pv=function(){return hM(jM(),$M(this)+"e"+(-this.xb|0))};f.Lp=function(){return aM(qa(),$M(this)+"e"+(-this.xb|0))};function $M(a){null===a.bs&&(a.bs=ri(Ph(),a.Bg));return a.bs}f.sl=function(a){return uW(this,a)};var iN=q({AT:0},!1,"java.math.BigDecimal",{AT:1,ur:1,g:1,l:1,nf:1});mr.prototype.$classData=iN;function xW(a){a.NC=-2;a.qu=0} function QR(a,b,c){xW(a);Ph();if(null===b)throw le();if(2>c||36a.Ya?Hh(1,a.wb,a.Qa):a}function fM(a,b){return a.Ya>b.Ya?1:a.Yab.wb?a.Ya:a.wbg?1:-1:si(vi(),a.Qa,b.Qa,e);if(0===h)return d===c?Ph().Ew:Ph().MC;if(-1===h)return Ph().nq;h=1+(e-g|0)|0;var k=new Xc(h);c=d===c?1:-1;1===g?gi(ei(),k,a.Qa,e,b.Qa.a[0]):di(ei(),k,h,a.Qa,e,b.Qa,g); c=Hh(c,h,k);Ih(c);return c}function DR(a,b){a=tW(a,b);return new (md(Ji).Ia)([a.nM,a.oM])} function tW(a,b){var c=b.Ya;if(0===c)throw new qb("BigInteger divide by zero");var d=b.wb;b=b.Qa;if(1===d){ei();b=b.a[0];var e=a.Qa,g=a.wb;d=a.Ya;1===g?(e=e.a[0],a=0===b?pb(0,0):+(e>>>0)/+(b>>>0)|0,g=0,b=0===b?Bb(0,0):+(e>>>0)%+(b>>>0)|0,e=0,d!==c&&(c=a,a=-c|0,g=0!==c?~g:-g|0),0>d&&(c=b,d=e,b=-c|0,e=0!==c?~d:-d|0),c=new uh(ri(Ph(),new ma(a,g)),ri(Ph(),new ma(b,e)))):(c=d===c?1:-1,a=new Xc(g),b=gi(0,a,e,g,b),b=new Xc(new Int32Array([b])),c=Hh(c,g,a),d=Hh(d,1,b),Ih(c),Ih(d),c=new uh(c,d));return c}g= a.Qa;e=a.wb;if(0>(e!==d?e>d?1:-1:si(vi(),g,b,e)))return new uh(Ph().nq,a);a=a.Ya;var h=1+(e-d|0)|0;c=a===c?1:-1;var k=new Xc(h);b=di(ei(),k,h,g,e,b,d);c=Hh(c,h,k);d=Hh(a,d,b);Ih(c);Ih(d);return new uh(c,d)}f=TM.prototype;f.i=function(a){if(a instanceof TM){var b;if(b=this.Ya===a.Ya&&this.wb===a.wb)a:{for(b=0;b!==this.wb;){if(this.Qa.a[b]!==a.Qa.a[b]){b=!1;break a}b=1+b|0}b=!0}a=b}else a=!1;return a}; function wW(a){if(0===a.Ya)return-1;var b=zh(a);a=a.Qa.a[b];return(b<<5)+(0===a?32:31-Math.clz32(a&(-a|0))|0)|0}f.B=function(){if(0===this.qu){for(var a=this.wb,b=0;b>31,e=65535&c,g=c>>>16|0,h=65535&a,k=a>>>16|0,l=Math.imul(e,h);h=Math.imul(g,h);var m=Math.imul(e,k);e=l+((h+m|0)<<16)|0;l=(l>>>16|0)+m|0;b=(((Math.imul(c,b)+Math.imul(d,a)|0)+Math.imul(g,k)|0)+(l>>>16|0)|0)+(((65535&l)+h|0)>>>16|0)|0;return new ma(e,b)};function Ki(a,b){return 0===b.Ya||0===a.Ya?Ph().nq:Mi(fi(),a,b)}function ui(a){return 0===a.Ya?a:Hh(-a.Ya|0,a.wb,a.Qa)} function Qi(a,b){if(0>b)throw new qb("Negative exponent");if(0===b)return Ph().Ew;if(1===b||a.i(Ph().Ew)||a.i(Ph().nq))return a;if(ER(a,0)){fi();for(var c=Ph().Ew,d=a;1>=1,c=a;return Ki(c,d)}for(c=1;!ER(a,c);)c=1+c|0;d=Ph();var e=Math.imul(c,b);if(e>5;e&=31;var g= new Xc(1+d|0);g.a[d]=1<>5;if(0===b)return 0!==(1&a.Qa.a[0]);if(0>b)throw new qb("Negative bit address");if(c>=a.wb)return 0>a.Ya;if(0>a.Ya&&ca.Ya&&(d=zh(a)===c?-d|0:~d);return 0!==(d&1<<(31&b))}f.u=function(){return Vh(bi(),this)}; function Ih(a){for(;;){if(0=a?Eb(a):-2):-1} function BW(a){return(0!==(1&a)?"-":"")+(0!==(2&a)?"#":"")+(0!==(4&a)?"+":"")+(0!==(8&a)?" ":"")+(0!==(16&a)?"0":"")+(0!==(32&a)?",":"")+(0!==(64&a)?"(":"")+(0!==(128&a)?"\x3c":"")}function CW(a,b,c){var d=Wj(a,1+b|0);a=d.Ft?"-":"";var e=d.wr,g=-1+e.length|0,h=b-g|0;b=e.substring(0,1);e=""+e.substring(1)+Sj(Tj(),h);d=g-d.vr|0;g=""+(0>d?-d|0:d);return a+(""!==e||c?b+"."+e:b)+"e"+(0>d?"-":"+")+(1===g.length?"0"+g:g)} function DW(a,b,c){var d=Uj(a,(a.wr.length+b|0)-a.vr|0);Tj();if(!("0"===d.wr||d.vr<=b))throw new Yj("roundAtPos returned a non-zero value with a scale too large");d="0"===d.wr||d.vr===b?d:new Vj(a.Ft,""+d.wr+Sj(Tj(),b-d.vr|0),b);a=d.Ft?"-":"";d=d.wr;var e=d.length,g=1+b|0;d=e>=g?d:""+Sj(Tj(),g-e|0)+d;e=d.length-b|0;a+=d.substring(0,e);return 0!==b||c?a+"."+d.substring(e):a}function QM(a,b,c,d,e,g){b=0>e||e>=g.length?g:g.substring(0,e);b=0!==(256&c)?b.toUpperCase():b;NM(a,c,d,b)} function YM(a,b,c,d){NM(a,b,c,XM(b,d!==d?"NaN":0=c&&0===(110&b))b=XM(b,d),EM(a,b);else if(0===(126&b))NM(a,b,c,XM(b,d));else{if(45!==d.charCodeAt(0))var g=0!==(4&b)?"+":0!==(8&b)?" ":"";else 0!==(64&b)?(d=d.substring(1)+")",g="("):(d=d.substring(1),g="-");e=""+g+e;if(0!==(32&b)){var h=d.length;for(g=0;;){if(g!==h){var k=d.charCodeAt(g);k=48<=k&&57>=k}else k=!1;if(k)g=1+g|0;else break}g=-3+g|0;if(!(0>=g)){for(h=d.substring(g);3=c?EM(a,d):0!==(1&b)?zW(a,d,EW(" ",c-e|0)):zW(a,EW(" ",c-e|0),d)}function WM(a,b,c,d,e,g){b=e.length+g.length|0;b>=d?zW(a,e,g):0!==(16&c)?AW(a,e,EW("0",d-b|0),g):0!==(1&c)?AW(a,e,g,EW(" ",d-b|0)):AW(a,EW(" ",d-b|0),e,g)}function EW(a,b){for(var c="",d=0;d!==b;)c=""+c+a,d=1+d|0;return c}function FM(a){throw new FW(String.fromCharCode(a));}function JM(a){throw new GW(a);} function ZM(a,b,c,d,e,g){var h=0!==(2&c);d=0<=d?d:6;switch(e){case 101:h=CW(b,d,h);break;case 102:h=DW(b,d,h);break;default:e=0===d?1:d,b=Wj(b,e),d=(-1+b.wr.length|0)-b.vr|0,-4<=d&&de?0:e,h)):h=CW(b,-1+e|0,h)}UM(a,c,g,h,"")}function BM(){this.Gt=this.T1=this.wv=null;this.gK=!1}BM.prototype=new p;BM.prototype.constructor=BM;BM.prototype.u=function(){if(this.gK)throw new DM;return null===this.wv?this.Gt:this.wv.u()};function KM(a){throw new HW(BW(a));} function MM(a,b,c){throw new IW(BW(b&c),a);}function RM(a,b){throw new JW(a,ca(b));}BM.prototype.$classData=q({O1:0},!1,"java.util.Formatter",{O1:1,g:1,yT:1,R0:1,zT:1});function KW(){}KW.prototype=new p;KW.prototype.constructor=KW;KW.prototype.Da=function(a,b){return(a|0)-(b|0)|0};KW.prototype.Qo=function(a,b,c){a.a[b]=c|0};KW.prototype.Wj=function(a,b){return a.a[b]};KW.prototype.$classData=q({y2:0},!1,"java.util.internal.GenericArrayOps$ByteArrayOps$",{y2:1,g:1,zB:1,XF:1,Ji:1});var LW; function gj(){LW||(LW=new KW);return LW}function MW(){}MW.prototype=new p;MW.prototype.constructor=MW;MW.prototype.Da=function(a,b){return Ea(a)-Ea(b)|0};MW.prototype.Qo=function(a,b,c){a.a[b]=Ea(c)};MW.prototype.Wj=function(a,b){return hc(a.a[b])};MW.prototype.$classData=q({z2:0},!1,"java.util.internal.GenericArrayOps$CharArrayOps$",{z2:1,g:1,zB:1,XF:1,Ji:1});var NW;function ej(){NW||(NW=new MW);return NW}function OW(){}OW.prototype=new p;OW.prototype.constructor=OW; OW.prototype.Da=function(a,b){a|=0;b|=0;return a===b?0:aY=>{var Z=Y.Va,S=Y.Oa;S.b()?S=R():(S=S.o(),S=new L(UW(C,S,D, !F,I,M,N,P,T)));return new Uw(Z,S,UW(C,Y.ra,D,F,I,M,N,P,T),Y.Pd)})(a,c,d,e,g,h,k,l))),b.Nj);if(v instanceof zv)return b=v,r=b.Yb,v=a,lv(),new zv(v,ry(0,r,new y(((C,D,F,I,M,N,P,T)=>Y=>{var Z=Y.Va,S=Y.Oa;S.b()?S=R():(S=S.o(),S=new L(UW(C,S,D,!F,I,M,N,P,T)));return new Uw(Z,S,UW(C,Y.ra,D,F,I,M,N,P,T),Y.Pd)})(a,c,d,e,g,h,k,l))),b.Nq);if(v instanceof Sv){b=v;r=b.Fd;n=v=a;var x=d,A=r.Va,B=r.Oa;B.b()?n=R():(B=B.o(),n=new L(UW(n,B,c,!x,e,g,h,k,l)));return new Sv(v,new Uw(A,n,UW(a,r.ra,c,d,e,g,h,k,l),r.Pd), b.Fx)}if(v instanceof Tv)return b=v,r=b.kf,new Tv(a,UW(a,b.Ic,c,d,e,g,h,k,l),r,b.vp);if(v instanceof lx&&(n=!0,r=v,iC(a),x=r.Sb,!x.b()))return d=x.o(),b=G(new H,r,!0),h.Se(b,new U(((C,D,F,I,M,N,P,T,Y)=>()=>{var Z=D.ji;t();var S=new L(D),ea=D.kg,ia=O().c,X=O().c;Z=new lx(C,F,ia,X,S,ea,!1,Z);S=G(new H,D,!0);S=G(new H,S,Z);I.$(S);ea=UW(C,M,F,!0,N,P,I,T,Y);S=UW(C,M,F,!1,N,P,I,T,Y);Pe(new E(ea),S)?wy(Z,(t(),new L(ea))):(up(tp(),vy(Z).b()),up(tp(),rA(Z).b()),ia=O().c,IA(Z,new z(ea,ia)),ea=O().c,KA(Z,new z(S, ea)));return Z})(a,r,c,h,d,e,g,k,l)));if(n&&r.Xa>e){d=!h.L(G(new H,r,!1));b=r;if(!d)throw new Yj("assertion failed: "+G(new H,b,h));if(vy(r).b()&&rA(r).b())return r;d=G(new H,r,!0);return h.Se(d,new U(((C,D,F,I,M,N,P,T)=>()=>{var Y=D.ji;t();var Z=new L(D),S=D.kg,ea=O().c,ia=O().c;Y=new lx(C,D.Xa,ea,ia,Z,S,!1,Y);Z=G(new H,D,!0);Z=G(new H,Z,Y);F.$(Z);ia=vy(D);Z=(sa=>Ja=>UW(sa,Ja,I,!0,M,N,F,P,T))(C);if(ia===u())Z=u();else{S=ia.e();ea=S=new z(Z(S),u());for(ia=ia.f();ia!==u();){var X=ia.e();X=new z(Z(X), u());ea=ea.p=X;ia=ia.f()}Z=S}IA(Y,Z);ia=rA(D);Z=(sa=>Ja=>UW(sa,Ja,I,!1,M,N,F,P,T))(C);if(ia===u())Z=u();else{S=ia.e();ea=S=new z(Z(S),u());for(ia=ia.f();ia!==u();)X=ia.e(),X=new z(Z(X),u()),ea=ea.p=X,ia=ia.f();Z=S}KA(Y,Z);return Y})(a,r,h,c,e,g,k,l)))}if(v instanceof Wv)return b=v,sB(b,new y(((C,D,F,I,M,N,P,T)=>Y=>UW(C,Y,D,F,I,M,N,P,T))(a,c,d,e,g,h,k,l)),new y(((C,D,F,I,M,N,P,T)=>Y=>UW(C,Y,D,!F,I,M,N,P,T))(a,c,d,e,g,h,k,l)),new y(((C,D,F,I,M,N,P,T)=>Y=>UW(C,Y,D,F,I,M,N,P,T))(a,c,d,e,g,h,k,l)),b.Uu); if(n)return b=G(new H,r,d),h.Se(b,new U(((C,D,F,I,M,N,P,T,Y)=>()=>{var Z=D.ji;t();var S=new L(D),ea=D.kg,ia=O().c,X=O().c;Z=new lx(C,F,ia,X,S,ea,!1,Z);S=G(new H,D,M);S=G(new H,S,Z);I.$(S);if(M){S=rA(D);KA(D,new z(Z,S));X=vy(D);S=(Ja=>Xa=>UW(Ja,Xa,F,M,N,P,I,T,Y))(C);if(X===u())S=u();else{ea=X.e();ia=ea=new z(S(ea),u());for(X=X.f();X!==u();){var sa=X.e();sa=new z(S(sa),u());ia=ia.p=sa;X=X.f()}S=ea}IA(Z,S)}else{S=vy(D);IA(D,new z(Z,S));X=rA(D);S=(Ja=>Xa=>UW(Ja,Xa,F,M,N,P,I,T,Y))(C);if(X===u())S=u(); else{ea=X.e();ia=ea=new z(S(ea),u());for(X=X.f();X!==u();)sa=X.e(),sa=new z(S(sa),u()),ia=ia.p=sa,X=X.f();S=ea}KA(Z,S)}return Z})(a,r,c,h,d,e,g,k,l)));if(v instanceof MA)return b=v,new MA(a,UW(a,b.Fc,c,!d,e,g,h,k,l),b.rA);if(v instanceof FA)return v;if(v instanceof OA)return b=v,new OA(a,UW(a,b.Hi,c,d,e,g,h,k,l),b.NE);if(v instanceof ZB&&(r=v,$B(a),t(),r=r.mc(),r=new L(r),!r.b())){b=r.k;continue}if(v instanceof mx){r=v;n=r.hi;if(r.Tu>e){if(n=UW(a,n,c,d,e,g,h,k,l),n instanceof lx)return new mx(a,n, r.et)}else if(r.Tu>c)return new YB(a,!d,r,jx(new kx,r.et.Wu,r.et.Ga,"extruded type variable reference",r.et.go,r.et.Zm),l);xm("Program reached and unexpected state.")}if(v instanceof Mu||v instanceof nC||v instanceof YB)return b;if(v instanceof fw)return b=v,r=b.qb,v=a,t(),new fw(v,r,bw(b,new L(d),new fn(((C,D,F,I,M,N,P)=>(T,Y)=>{T=G(new H,T,Y);Y=T.y;var Z=T.w;if(t().d===Y)return dw(cw(C),UW(C,Z,D,!1,F,I,M,N,P),UW(C,Z,D,!0,F,I,M,N,P),ew(cw(C)),I);Y=T.y;Z=T.w;if(Y instanceof L)return UW(C,Z,D,!!Y.k, F,I,M,N,P);throw new w(T);})(a,c,e,g,h,k,l)),g),b.Xl);if(v instanceof Qx)return b=v,r=b.de,new Qx(a,r,UW(a,b.Re,c,d,eT=>{if(null!==T){var Y=T.j();T=UW(C,T.h(),D,!0,F,I,M,N,P);Y=UW(C,Y,D,!1,F,I,M,N,P);return G(new H,T,Y)}throw new w(T);})(a,c,e,g,h,k,l);if(A===u())v=u();else{n=A.e();x=n=new z(v(n),u());for(A=A.f();A!==u();)B=A.e(),B=new z(v(B),u()),x=x.p=B,A=A.f();v=n}return new eC(r,v,UW(a,b,c,d,e,g,h,k,l))}if(v instanceof Jv)return VW(v,new y(((C,D,F,I,M,N,P,T)=>Y=>UW(C,Y,D,!F,I,M,N,P,T))(a,c,d,e,g,h,k,l)),new y(((C,D,F,I,M,N,P,T)=>Y=>UW(C,Y,D,F,I,M,N,P,T))(a,c,d,e,g,h,k,l)));throw new w(v);}}}; function WW(a,b,c,d,e){var g=!1,h=null,k=b.U(c.x);if(k instanceof L&&(g=!0,h=k,b=h.k,b instanceof bx))return rx(b.Mb)&&(e=new Te(new Ue(J(new K,["Let binding '","' cannot tbe accessed as a field"]))),g=[We(Xe(),b.Ua())],e=Ye(e,J(new K,g)),c=c.A(),c=G(new H,e,c),e=Ye(new Te(new Ue(J(new K,["Use a `val` declaration to make it a field"]))),u()),g=b.Mb.A(),e=G(new H,e,g),g=O().c,ay(a,new z(c,new z(e,g)),d)),d=b.Nn(),t(),b.Mb.Om.b()?(a=b.ma(),a=new Uw(d.q,R(),d,a)):a=new Uw(a,(t(),new L(d)),d,b.ma()), new L(a);if(g&&(b=h.k,b instanceof Vw))return e||b.bo||(e=new Te(new Ue(J(new K,["Parameter '","' cannot be accessed as a field"]))),g=[We(Xe(),b.ij.Ua())],e=Ye(e,J(new K,g)),c=c.A(),c=G(new H,e,c),e=Ye(new Te(new Ue(J(new K,["Either make the parameter a `val` or access it through destructuring"]))),u()),g=b.ij.A(),e=G(new H,e,g),g=O().c,ay(a,new z(c,new z(e,g)),d)),t(),new L(b.ig);if(g)return e=h.k,t(),b=new Te(new Ue(J(new K,["Access to "," member not yet supported"]))),e=[We(Xe(),e.fd().ld)],d= Lw(a,Ye(b,J(new K,e)),c.A(),d),a=V(a),a=new Uw(d.q,R(),d,a),new L(a);if(t().d===k)return t().d;throw new w(k);}function yea(a){if(0>=a.ab(15))return a;var b=xt(a,15),c=Ye(new Te(new Ue(J(new K,["......"]))),u()),d=t().d;c=G(new H,c,d);d=Ye(new Te(new Ue(J(new K,["......"]))),u());var e=t().d;d=G(new H,d,e);a=er(a).m().Mn(15);Od();a=Km(Pd(u(),a));return dl(new z(c,new z(d,a)),b)} function XW(a,b,c){var d=new Te(new Ue(J(new K,["Subtyping constraint of the form `"," \x3c: ","`"])));a=[wO(Xe(),AD(a,b)),wO(Xe(),CD(c,b))];return Ye(d,J(new K,a))} function YW(a,b,c,d,e,g,h,k,l,m,n){var r=(g.Ke-g.Zd|0)&(-1+g.ec.a.length|0);0<(250===r?0:250>r?-1:1)?(b=new Te(new Ue(J(new K,[""," exceeded recursion depth limit (",")"]))),c=[XW(d,c,e),We(Xe(),"250")],b=Ye(b,J(new K,c)),h=G(new H,b,h.Ga),a.zA?a.$E?(Od(),b=Pd(u(),g),b=ZW(HF(new yt(b)),new y(v=>{if(null!==v){var x=v.h();v=v.j();var A=new Te(new Ue(J(new K,["while constraining: ",""]))),B=[We(Xe(),""+x)];A=Ye(A,J(new K,B));x=x.ma().Ga;x=G(new H,A,x);A=new Te(new Ue(J(new K,[" \x3c!\x3c ", ""])));B=[We(Xe(),""+v)];A=Ye(A,J(new K,B));v=v.ma().Ga;v=G(new H,A,v);A=O().c;return new z(x,new z(v,A))}throw new w(v);}))):(Od(),b=Pd(u(),g),b=yea(Qt(HF(new yt(b)),new y(v=>{var x=new Te(new Ue(J(new K,["while constraining: ",""])));v=[We(Xe(),v.h()+" \x3c!\x3c "+v.j())];x=Ye(x,J(new K,v));v=t().d;return G(new H,x,v)})))):(b=Ye(new Te(new Ue(J(new K,["Note: use flag `:ex` to see internal error info."]))),u()),g=t().d,b=G(new H,b,g),g=O().c,b=new z(b,g)),ay(a,new z(h,b),k),Es(l)):0>=m.ve?(g= new Te(new Ue(J(new K,[""," took too many steps and ran out of fuel (",")"]))),c=[XW(d,c,e),We(Xe(),""+n)],g=Ye(g,J(new K,c)),h=G(new H,g,h.Ga),a.zA?(g=Qt(b.h(),new y(v=>{var x=new Te(new Ue(J(new K,[" + ",""]))),A=[We(Xe(),""+v)];x=Ye(x,J(new K,A));v=v.ma().Ga;return G(new H,x,v)})),b=dl(Qt(b.j(),new y(v=>{var x=new Te(new Ue(J(new K,[" - ",""]))),A=[We(Xe(),""+v)];x=Ye(x,J(new K,A));v=v.ma().Ga;return G(new H,x,v)})),g)):(b=Ye(new Te(new Ue(J(new K,["Note: use flag `:ex` to see internal error info."]))), u()),g=t().d,b=G(new H,b,g),g=O().c,b=new z(b,g)),ay(a,new z(h,b),k),Es(l)):m.ve=-1+m.ve|0}function $W(a,b,c,d){b=d+"."+b;a.F&&(a=ut(Q(),"| ",a.r)+b,ff(gf(),a+"\n"));c.n(b)} function aX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A){c=c.cb;var B=a.qa;if(a.F){var C=ut(Q(),"| ",a.r)+"UNSTASHING...";ff(gf(),C+"\n")}a.r=1+a.r|0;try{c.Ca(new y(F=>{if(null!==F){var I=F.h();F=F.j();if(a.F){var M=ut(Q(),"| ",a.r)+("where("+I+") ")+xx(I);ff(gf(),M+"\n")}for(F=F.m();F.s();)a:{if(M=F.t(),null!==M){var N=M.j();if(!0===M.Rc()){a.F&&(M=ut(Q(),"| ",a.r)+("UNSTASH "+N+" \x3c: "+I+" where ")+xx(N),ff(gf(),M+"\n"));bX(a,N,I,!1,g,d,e,b,h,k,l,m,n,g,r,v,x,A,h);break a}}if(null!==M&&(N=M.j(),!1===M.Rc())){a.F&& (M=ut(Q(),"| ",a.r)+("UNSTASH "+I+" \x3c: "+N+" where ")+xx(N),ff(gf(),M+"\n"));bX(a,I,N,!1,g,d,e,b,h,k,l,m,n,g,r,v,x,A,h);break a}throw new w(M);}}else throw new w(F);}));c.mg();var D=void 0}finally{a.r=-1+a.r|0}dx(new E(B),a.qa)&&a.F&&(D=""+ut(Q(),"| ",a.r)+B.n(D),ff(gf(),D+"\n"))} function cX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C){var D=a.Df,F=O().c;xB(a);xB(a);xB(a);var I=ap();b=TO(xB(a),D,F,b,!0,g,I);D=a.Df;F=O().c;xB(a);xB(a);xB(a);I=ap();var M=TO(xB(a),D,F,c,!1,g,I);if(qB(M)){c=1+g.da|0;D=Hw();F=Su();I=op().ga;D=D.Hd(new Uu(F,I));var N=new Iw(g.S,g.Ec,g.hc,g.Ed,c,g.Pc,g.Zc,g.Lb,g.yc,g.tb,g.$a,g.od,D),P=Xu().X();c=a.Df;D=JF(lv(),M.Qf,new y(S=>S.Kc(M.fb,!0,N,P)));var T=M.xe;if(T===u())F=u();else for(F=T.e(),I=F=new z(dX(F,M.fb,!0,N,P),u()),T=T.f();T!==u();){var Y=T.e();Y=new z(dX(Y, M.fb,!0,N,P),u());I=I.p=Y;T=T.f()}c=new RO(a,c,D,F);a.F&&(D=ut(Q(),"| ",a.r)+("DNF BUMP TO LEVEL "+N.da+" --\x3e ")+c,ff(gf(),D+"\n"));eX(a,b,c,d,e,N,h,k,r,v,n,l,x,A,B,C,m);aX(a,g,N,d,e,k,m,n,r,v,l,x,A,B,C);a=N.cb;up(tp(),g.S.li||N.cb.b());if(!a.b()){d=g.S.qa;e=g.S;e.F&&(h=ut(Q(),"| ",e.r)+"UNSTASHING... (out)",ff(gf(),h+"\n"));e.r=1+e.r|0;try{a.Ca(new y(((S,ea)=>ia=>{if(null!==ia){var X=ia.h();for(ia=ia.j().m();ia.s();){var sa=ia.t();a:{if(null!==sa){var Ja=sa.j();if(!0===sa.Rc()){sa=Sw(S.S).ob; Tw(S.S,Ja,X,k,l,ea,sa);break a}}if(null!==sa&&(Ja=sa.j(),!1===sa.Rc())){sa=Sw(S.S).ob;Tw(S.S,X,Ja,k,l,ea,sa);break a}throw new w(sa);}}}else throw new w(ia);})(g,g)));a.mg();var Z=void 0}finally{e.r=-1+e.r|0}dx(new E(d),e.qa)&&e.F&&(g=""+ut(Q(),"| ",e.r)+d.n(Z),ff(gf(),g+"\n"))}}else eX(a,b,M,d,e,g,h,k,r,v,n,l,x,A,B,C,m)} function zea(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D,F,I){var M=tc();try{var N=sp(b);if(N instanceof L){var P=N.k,T=c.zf(!1),Y=(new PO(a,d,b.Ek(P),e,g)).zf(!1),Z=V(Y.q),S=NA(Y,Z,!1),ea=V(T.q);bX(a,P,dv(T,S,ea,!1),!0,h,k,l,m,n,r,v,x,A,h,B,C,D,F,I)}else if(t().d===N){var ia=g.m(),X=new Ef(ia,new y(bb=>{var Ia=a.Df,Ua=O().c;xB(a);xB(a);var pc=ap();return TO(xB(a),Ia,Ua,bb,!0,m,pc)})),sa=a.Df,Ja=O().c,Xa=e.zf(!1);xB(a);xB(a);for(var Fa=ap(),za=UO(c,TO(xB(a),sa,Ja,Xa,!1,m,Fa),m,!0);X.s();){c=za;var Qa=X.t(); za=UO(c,Qa,m,!0)}X=za;if(a.F){var Ma=ut(Q(),"| ",a.r)+("Consider "+d+" \x3c: ")+X;ff(gf(),Ma+"\n")}Ma=!1;za=null;if(d instanceof Ku){Ma=!0;za=d;var Ga=za.fc,ab=za.vd,Hb=za.be,bc=za.Me;if(Ga instanceof L){var yb=Ga.k;if(yb instanceof Tv){var tb=yb.Ic,eb=yb.kf;if(tb instanceof MA){var kb=tb.Fc;if(kb instanceof lx){var Rb=X.zf(!1),Gb=(new Ku(a,t().d,ab,Hb,bc)).zf(!1),vb=V(Gb.q),Tb=NA(Gb,vb,!1),Nb=V(Rb.q),ic=dv(Rb,Tb,Nb,!1),Va=iB(ic,eb),cb=V(Va.q);bX(a,NA(Va,cb,!1),kb,!0,h,k,l,m,n,r,v,x,A,h,B,C,D,F,I); return}}}}}if(Ma){var zb=za.fc,Ub=za.vd,jb=za.be,db=za.Me;if(zb instanceof L){var ub=zb.k;if(ub instanceof Tv){var Aa=ub.Ic,va=ub.kf;up(tp(),Aa instanceof lx);var Ra=X.zf(!1),rb=(new Ku(a,t().d,Ub,jb,db)).zf(!1),xb=V(rb.q),mc=NA(rb,xb,!1),Ha=V(Ra.q),Ka=dv(Ra,mc,Ha,!1);bX(a,Aa,iB(Ka,va),!0,h,k,l,m,n,r,v,x,A,h,B,C,D,F,I);return}}}Ga=bb=>{if(Ot(new E(bb.Of),lw(a))&&bb.Pf.b()&&bb.Af.b()&&mw(d,bb.Nf,m))throw a.F&&(bb=ut(Q(),"| ",a.r)+("OK "+d+" \x3c: ")+bb,ff(gf(),bb+"\n")),new fX(M);if(ey(b,bb.Af)?0: !hw(d,bb.Nf,!1,m,!1).b()){bb=bb.Of;if(d instanceof Ku){var Ia=d.vd;if(bb instanceof jw){a:{for(var Ua=bb.Xb;!Ua.b();){var pc=Ua.e();if(pc instanceof nC&&Ia.L(pc)){Ia=!0;break a}Ua=Ua.f()}Ia=!1}if(Ia)return!1}}return d instanceof Ku&&(Ia=d.fc,Ia instanceof L&&(Ia=Ia.k,Ia instanceof Mu&&bb instanceof jw))?!bb.Xb.L(Ia):!0}return!1};var Oa=X.xe;a:for(var Na;;)if(Oa.b()){Na=u();break}else{var Da=Oa.e(),ta=Oa.f();if(!1===!!Ga(Da))Oa=ta;else for(;;){if(ta.b())Na=Oa;else{var Ya=ta.e();if(!1!==!!Ga(Ya)){ta= ta.f();continue}Ya=ta;var dc=new z(Oa.e(),u()),ka=Oa.f();for(ta=dc;ka!==Ya;){var ya=new z(ka.e(),u());ta=ta.p=ya;ka=ka.f()}var Sa=Ya.f();for(ka=Sa;!Sa.b();){var xc=Sa.e();if(!1===!!Ga(xc)){for(;ka!==Sa;){var Sb=new z(ka.e(),u());ta=ta.p=Sb;ka=ka.f()}ka=Sa.f()}Sa=Sa.f()}ka.b()||(ta.p=ka);Na=dc}break a}}if(a.F){var uc=ut(Q(),"| ",a.r)+"Possible: "+Na;ff(gf(),uc+"\n")}var Lb=O().c;if(Na===u())var lc=u();else{var Xb=Na.e(),ec=new z(Xb.zf(!1),u());dc=ec;for(var Ab=Na.f();Ab!==u();){var Ob=Ab.e(),fb=new z(Ob.zf(!1), u());dc=dc.p=fb;Ab=Ab.f()}lc=ec}var Wa=lw(a);gX(a,Lb,d,lc,Wa,k,l,n,m,"Case",v,x,r,A,h,B,C,D,F,I)}else throw new w(N);}catch(bb){if(bb instanceof Iq){if(h=bb,h.Qg!==M)throw h;}else throw bb;}} function eX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C){var D=a.qa;if(a.F){var F=ut(Q(),"| ",a.r)+(g.da+". ARGH "+b+" \x3c! ")+c;ff(gf(),F+"\n")}a.r=1+a.r|0;try{a.an=1+a.an|0;YW(a,d,g,l,m,n,r,k,v,x,A);At(tp(),!qB(c));c.Qf.b()||no();var I=Aea(b,g);if(null===I)throw new w(I);var M=I.h(),N=I.j(),P=a.qa;if(a.F){var T=ut(Q(),"| ",a.r)+"DNF DISCHARGE CONSTRAINTS";ff(gf(),T+"\n")}a.r=1+a.r|0;try{for(b=M;!b.b();){var Y=b.e();bX(a,Y.h(),Y.j(),!1,k,d,e,g,h,n,l,m,r,k,v,x,A,B,C);b=b.f()}var Z=void 0}finally{a.r=-1+ a.r|0}if(dx(new E(P),a.qa)&&a.F){var S=""+ut(Q(),"| ",a.r)+P.n(Z);ff(gf(),S+"\n")}for(;!N.b();){var ea=N.e();if(null!==ea)zea(a,ea.Pf,c,ea.Nf,ea.Of,ea.Af,k,d,e,g,h,n,l,m,r,v,x,A,B,C);else throw new w(ea);N=N.f()}var ia=void 0}finally{a.r=-1+a.r|0}dx(new E(D),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+D.n(ia),ff(gf(),a+"\n"))}function gX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D,F,I){a.an=1+a.an|0;YW(a,g,l,n,r,v,x,A,B,C,D);Bea(a,b,c,d,e,g,h,l,k,m,A,x,v,n,r,B,C,D,F,I)} function Cea(a,b,c,d){a=a.m().nb(new U(()=>b.GC()));a=new Ef(a,new y(e=>{var g=V(e.q);return NA(e,g,!1)}));return kv(a,new U(()=>c.m())).nb(new U(()=>d.GC()))}function Dea(a,b,c,d,e){b=Cea(b,c,d,e);if(b.s()){if(!b.s())throw nv("empty.reduceLeft");c=!0;for(d=null;b.s();)if(e=b.t(),c)d=e,c=!1;else{var g=V(d.q);d=dv(d,e,g,!1)}b=new L(d)}else b=R();return b.b()?a.ib:b.o()} function Eea(a,b,c,d){return a.m().nb(new U(()=>b.GC())).nb(new U(()=>{var e=c.m().nb(new U(()=>d.GC()));return new Ef(e,new y(g=>{var h=V(g.q);return NA(g,h,!1)}))}))}function Fea(a,b,c,d,e){b=Eea(c,d,b,e);if(b.s()){if(!b.s())throw nv("empty.reduceLeft");c=!0;for(d=null;b.s();)if(e=b.t(),c)d=e,c=!1;else{var g=V(d.q);d=Pu(d,e,g,!1)}b=new L(d)}else b=R();return b.b()?a.La:b.o()} function Bea(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D,F,I){var M=tc();try{Lx(a,new U(()=>k.da+". A "+c+" % "+b+" \x3c! "+d+" % "+e),new U(()=>{var P=G(new H,b,d);a:{var T=P.y;if(T instanceof z){var Y=T.z,Z=T.p;if(Y instanceof lx){bX(a,Y,Dea(a,Z,c,d,e),ow(c)&&Z.ul(new y(Re=>JA(Re,k))),n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);break a}}var S=P.w;if(S instanceof z){var ea=S.z,ia=S.p;if(ea instanceof lx){bX(a,Fea(a,ia,b,c,e),ea,Gw(e)&&ia.ul(new y(Re=>Kv(Re,k))),n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);break a}}var X=P.y; if(X instanceof z){var sa=X.z,Ja=X.p;if(sa instanceof cC){gX(a,new z(sa.Sf,Ja),c,d,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var Xa=P.w;if(Xa instanceof z){var Fa=Xa.z,za=Xa.p;if(Fa instanceof cC){gX(a,b,c,new z(Fa.Fg,za),e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var Qa=P.y;if(Qa instanceof z){var Ma=Qa.z,Ga=Qa.p;if(Ma instanceof LA){var ab=Ma.ic,Hb=Ma.jc;if(!0===Ma.tc){$W(a,"1",new y(Re=>{gX(a,new z(ab,Ga),c,d,e,g,h,l,k,Re,x,A,v,r,n,B,C,D,F,I)}),m);$W(a,"2",new y(Re=>{gX(a,new z(Hb,Ga),c,d,e,g,h, l,k,Re,x,A,v,r,n,B,C,D,F,I)}),m);break a}}}var bc=P.w;if(bc instanceof z){var yb=bc.z,tb=bc.p;if(yb instanceof LA){var eb=yb.ic,kb=yb.jc;if(!1===yb.tc){$W(a,"1",new y(Re=>{gX(a,b,c,new z(eb,tb),e,g,h,l,k,Re,x,A,v,r,n,B,C,D,F,I)}),m);$W(a,"2",new y(Re=>{gX(a,b,c,new z(kb,tb),e,g,h,l,k,Re,x,A,v,r,n,B,C,D,F,I)}),m);break a}}}var Rb=P.w;if(Rb instanceof z){var Gb=Rb.z,vb=Rb.p;if(Gb instanceof LA){var Tb=Gb.ic,Nb=Gb.jc;if(!0===Gb.tc){gX(a,b,c,new z(Tb,new z(Nb,vb)),e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}}var ic= P.y;if(ic instanceof z){var Va=ic.z,cb=ic.p;if(Va instanceof LA){var zb=Va.ic,Ub=Va.jc;if(!1===Va.tc){gX(a,new z(zb,new z(Ub,cb)),c,d,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}}var jb=P.y;if(jb instanceof z){var db=jb.z,ub=jb.p;if(db instanceof ZB){$B(a);t();var Aa=db.mc(),va=new L(Aa);if(!va.b()){gX(a,new z(va.k,ub),c,d,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}}var Ra=P.w;if(Ra instanceof z){var rb=Ra.z,xb=Ra.p;if(rb instanceof ZB){$B(a);t();var mc=rb.mc(),Ha=new L(mc);if(!Ha.b()){gX(a,b,c,new z(Ha.k, xb),e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}}var Ka=P.y;if(Ka instanceof z){var Oa=Ka.z,Na=Ka.p;if(Oa instanceof MA){gX(a,Na,c,new z(Oa.Fc,d),e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var Da=P.w;if(Da instanceof z){var ta=Da.z,Ya=Da.p;if(ta instanceof MA){gX(a,new z(ta.Fc,b),c,Ya,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var dc=P.y;if(dc instanceof z){var ka=dc.z;if(ka instanceof FA&&!0===ka.Eh)break a}var ya=P.w;if(ya instanceof z){var Sa=ya.z;if(Sa instanceof FA&&!1===Sa.Eh)break a}var xc=P.y, Sb=P.w;if(xc instanceof z){var uc=xc.z,Lb=xc.p;if(uc instanceof FA&&!1===uc.Eh){gX(a,Lb,c,Sb,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var lc=P.y,Xb=P.w;if(Xb instanceof z){var ec=Xb.z,Ab=Xb.p;if(ec instanceof FA&&!0===ec.Eh){gX(a,lc,c,Ab,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var Ob=P.y,fb=P.w;if(Ob instanceof z){var Wa=Ob.z,bb=Ob.p;if(Wa instanceof fw){var Ia=Zv(c,Wa,!0,k,!1),Ua=new U(()=>{throw new fX(M,(Nx(a,new U(()=>"OK "+c+" \x26 "+Wa+" \x3d:\x3d "+a.ib)),void 0));});gX(a,bb,Ia.b()?Es(Ua): Ia.o(),fb,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var pc=P.y,sc=P.w;if(sc instanceof z){var Ba=sc.z,ob=sc.p;if(Ba instanceof fw){var nc=ww(e,Ba,!1,k),Ib=new U(()=>{throw new fX(M,(Nx(a,new U(()=>"OK "+e+" \x26 "+Ba+" \x3d:\x3d "+a.La)),void 0));});gX(a,pc,c,ob,nc.b()?Es(Ib):nc.o(),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var vc=P.y,Vb=P.w;if(vc instanceof z){var fc=vc.z,Bc=vc.p;if(fc instanceof hX){var Pb=yv(c,fc,!0,k,!0),Jb=new U(()=>{throw new fX(M,(Nx(a,new U(()=>"OK "+c+" \x26 "+fc+" \x3d:\x3d "+ a.ib)),void 0));});gX(a,Bc,Pb.b()?Es(Jb):Pb.o(),Vb,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var gc=P.y,Cb=P.w;if(Cb instanceof z){var cc=Cb.z,yc=Cb.p;if(cc instanceof hX){var Mc=zw(e,cc),qc=new U(()=>{throw new fX(M,(Nx(a,new U(()=>"OK "+e+" | "+cc+" \x3d:\x3d "+a.La)),void 0));});gX(a,gc,c,yc,Mc.b()?Es(qc):Mc.o(),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var oc=P.y,Qc=P.w;if(oc instanceof z){var jc=oc.z,sb=oc.p;if(jc instanceof Qv){gX(a,sb,Xv(c,jc),Qc,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var Gc= P.w;if(Gc instanceof z){var Wb=Gc.z;if(Wb instanceof Qv){var Cc=Wb.Ba,Fc=O().c;if(null===Fc?null===Cc:Fc.i(Cc))break a}}var qd=P.y,Yb=P.w;if(Yb instanceof z){var Nc=Yb.z,ad=Yb.p;if(Nc instanceof Qv){var Uc=Nc.Ba;if(Uc instanceof z){var cd=Uc.z,kc=Uc.p,Vc=O().c;if(null===Vc?null===kc:Vc.i(kc)){var Hc=Aw(e,cd),rc=new U(()=>{throw new fX(M,(Nx(a,new U(()=>"OK "+e+" | "+cd+" \x3d:\x3d "+a.La)),void 0));});gX(a,qd,c,ad,Hc.b()?Es(rc):Hc.o(),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}}}var sd=P.y,Kc=P.w; if(Kc instanceof z){var Qd=Kc.z,Ad=Kc.p;if(Qd instanceof Qv){var kd=Gea(Qd);gX(a,sd,c,new z(kd,Ad),e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break a}}var Hd=P.w;if(Hd instanceof z&&Hd.z instanceof Qx)var Rd=!0;else{var Bd=P.y;Rd=Bd instanceof z&&Bd.z instanceof Qx?!0:!1}Rd&&xm("Program reached and unexpected state.");var ae=P.w;if(ae instanceof z&&ae.z instanceof eC)var dd=!0;else{var od=P.y;dd=od instanceof z&&od.z instanceof eC?!0:!1}dd&&xm("Program reached and unexpected state.");var Ta=P.y,wb=P.w,$a=O().c; if(null===$a?null===Ta:$a.i(Ta))var wa=O().c,hb=null===wa?null===wb:wa.i(wb);else hb=!1;if(hb){var ra=G(new H,c,e);b:{var wc=ra.y,ac=ra.w;if(wc instanceof Ku){var Id=wc.fc;if(Id instanceof L){var ud=Id.k;if(ud instanceof Tv){var be=ud.Ic;if(lw(a)===ac){bX(a,be,a.ib,!0,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);break b}}}}c:{var re=ra.y;if(Vu(a)===re)var pe=!0;else{var bd=ra.y;if(bd instanceof Ku){var Rc=bd.fc,Wc=bd.vd,Wd=bd.be,zd=bd.Me;if(t().d===Rc&&null!==Wc&&Wc.b()&&null!==Wd){var Pa=Wd.Ba,Db=O().c;if((null=== Db?null===Pa:Db.i(Pa))&&null!==zd&&zd.b()){pe=!0;break c}}}pe=!1}}if(pe)iX(a,t().d,g,k,r,n);else{var Oc=ra.y,Tc=ra.w;if(Oc instanceof Ku){var Sd=Oc.vd;if(Tc instanceof jw){var Jc=Tc.Xb;if(ey(Sd,new y(Re=>Jc.L(Re))))break b}}var vd=ra.y;if(vd instanceof Ku){var hd=vd.fc,de=vd.vd,ye=vd.be,jf=vd.Me;if(!jf.b()){var af=new Ou(jf),pf=new Ef(af,new y(Re=>cD(Re,k,n)));Od();var kf=Pd(u(),pf),Be=sv(),Kd=Su(),ld=op().ga;gX(a,kf,new Ku(a,hd,de,ye,Be.Hd(new Uu(Kd,ld))),O().c,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break b}}var Jd= ra.w;if(Jd instanceof jw){var Dd=Jd.Xb,Xd=Jd.mb,Yc=Jd.Dc;if(!Yc.b()){var Ce=O().c,te=new Ou(Yc),Ie=new Ef(te,new y(Re=>cD(Re,k,n)));Od();var Jf=Pd(u(),Ie),df=sv(),vg=Su(),wg=op().ga;gX(a,Ce,c,Jf,new jw(a,Dd,Xd,df.Hd(new Uu(vg,wg))),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break b}}var xg=ra.w;if(xg instanceof jw){var eg=xg.Xb,vh=xg.mb,fg=xg.Dc;if(vh instanceof L){var ih=vh.k;if(ih instanceof fe){var Ig=ih.aa;if(Ig instanceof Jv){for(var Tf=new y(Re=>{var rj=O().c,ai=O().c;t();t();gX(a,rj,c,ai,new jw(a,eg,new L(new fe(Re)), fg),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I)}),Jg=Ig.gi;!Jg.b();)Tf.n(Jg.e()),Jg=Jg.f();break b}}}}var jh=ra.y;jh instanceof Ku&&(jh.Me.b()||xm("Program reached and unexpected state."));var yg=ra.w;yg instanceof jw&&(yg.Dc.b()||xm("Program reached and unexpected state."));c:{var gg=ra.w;if(lw(a)===gg)var Cf=!0;else{var Uf=ra.w;if(Uf instanceof jw){var $g=Uf.Xb,Ah=Uf.mb,Kg=O().c;if((null===Kg?null===$g:Kg.i($g))&&t().d===Ah){Cf=!0;break c}}Cf=!1}}if(Cf)iX(a,t().d,g,k,r,n);else{var Vf=ra.y,hg=ra.w;if(Vf instanceof Ku){var zg=Vf.fc;if(zg instanceof L){var Lg=zg.k;if(Lg instanceof cv&&hg instanceof jw){var Mg=hg.mb;if(Mg instanceof L){var Wf=Mg.k;if(Wf instanceof fe){var Ng=Wf.aa;if(Ng instanceof cv){bX(a,Lg,Ng,!0,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);break b}}}}}}var Kf=ra.y,xf=ra.w;if(Kf instanceof Ku){var Og=Kf.fc,mi=Kf.vd,Ci=Kf.be,Xh=Kf.Me;if(Og instanceof L&&Og.k instanceof cv&&xf instanceof jw){gX(a,O().c,new Ku(a,t().d,mi,Ci,Xh),O().c,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break b}}var wh=ra.y,Bh=ra.w;if(wh instanceof Ku){var ng=wh.fc;if(ng instanceof L&&ng.k instanceof Mu&&Bh instanceof jw&&Yca(new E(a.lP),Bh.Xb)){Nx(a,new U(()=>"OK ~ magic Eql ~"));break b}}var kh=ra.y,Kh=ra.w;if(kh instanceof Ku){var ni=kh.fc;if(ni instanceof L){var Lh=ni.k;if(Lh instanceof Mu){var lh=Lh.pd;if(lh instanceof Dl&&Kh instanceof jw){var Ch=Kh.mb;if(Ch instanceof L){var Dh=Ch.k;if(Dh instanceof Ud){var Yh=Dh.fa;if(null!==Yh){var ah=Yh.me,oi=Yh.Ne;if(null!==ah&&"Eql#A"===ah.x){if(lh instanceof Em||lh instanceof Fm){var mj=oi.Oa,wd= new U(()=>a.La);bX(a,mj.b()?Es(wd):mj.o(),a.Ak,!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I)}else if(lh instanceof Dm){var ge=oi.Oa,De=new U(()=>a.La);bX(a,ge.b()?Es(De):ge.o(),a.Qj,!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I)}else if(lh instanceof Gm)iX(a,t().d,g,k,r,n);else throw new w(lh);break b}}}}}}}}var qf=ra.y,og=ra.w;if(qf instanceof Ku){var Xf=qf.fc,mh=qf.vd,Ag=qf.be;if(Xf instanceof L){var Bg=Xf.k;if(Bg instanceof Mu){var Eh=Bg.pd;if(Eh instanceof vl){var Pg=Eh.x;if(og instanceof jw){var Di=og.mb;if(Di instanceof L){var Mh=Di.k;if(Mh instanceof Ud){var pi=Mh.fa;if(null!==pi){var Xi=pi.me,Qg=pi.Ne;if(k.$a.L(Pg)){if(a.$c&&Nm(new E(Pg),"Eql")&&"Eql#A"===Xi.x){var nh=k.$a.n(Pg);if(px(nh).b()&&!a.cn.L(Pg)){var bh=new Te(new Ue(J(new K,[""," '","' does not support equality comparison because it does not have a parameter list"])));Xe();Q();var Mj=nh.Ab.fd().ld,Nj=We(0,Nu(0,Mj));Xe();var ie=[Nj,We(0,nh.Ab.Cf.x)];Lw(a,Ye(bh,J(new K,ie)),r.Ga,n)}for(var Ac=px(nh),Ve=new U(()=>O().c),Td=Ac.b()?Es(Ve):Ac.o(),lf=new y(Re=> {var rj=new U(()=>c.zf(!0));t();var ai=new L(Pg),rm=Ag.Ba;op();var Nn=pp(qp(),rm);ai=jX(a,rj,ai,new y(Au=>Nn.U(Au)),mh,Re.h(),k,n);rj=Qg.Oa;rm=new U(()=>{xm("Program reached and unexpected state.")});rj=rj.b()?Es(rm):rj.o();Re=Re.h();rm=new Ep("Eql");ai=ai.ra;var zu=O().c;ai=new fw(a,rm,new z(ai,zu),V(a));rm=V(a);ai=new Uw(ai.q,R(),ai,rm);Re=G(new H,Re,ai);ai=O().c;bX(a,rj,new Qv(a,new z(Re,ai),V(a)),!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I)}),Yi=Td;!Yi.b();)lf.n(Yi.e()),Yi=Yi.f()}else{var Jl=new U(()=>c.zf(!0)); t();var ll=new L(Pg),Bj=Ag.Ba;op();var $k=pp(qp(),Bj),Zh=jX(a,Jl,ll,new y(Re=>$k.U(Re)),mh,Xi,k,n);bX(a,Zh.ra,Qg.ra,!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);kX(a,Qg,Zh,n,g,h,k,l,v,r,n,x,A,B,C,D,F,I)}break b}}}}}}}}}var Ei=ra.y,Yd=ra.w;if(Ei instanceof Ku){var bf=Ei.fc,rf=Ei.vd,Cg=Ei.be,nj=Ei.Me;if(bf instanceof L){var Jh=bf.k;if(Jh instanceof Mu&&Yd instanceof jw){var If=Yd.Xb,Hg=Yd.mb,He=Yd.Dc;Nx(a,new U(()=>"class checking "+Jh+" "+If));If.qo(new y(Re=>Pe(new E(Re.uo()),Jh.pd)?!0:(Ei.rE?Ei.qE:kw(Ei)).L(Re.uo())))? Nx(a,new U(()=>"OK "+Jh+" \x3c: "+ze(If,""," | ",""))):gX(a,O().c,new Ku(a,t().d,rf,Cg,nj),O().c,new jw(a,O().c,Hg,He),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);void 0;break b}}}var lj=ra.y,Wi=ra.w;if(lj instanceof Ku&&Wi instanceof iw){var Oj=O().c,mo=O().c,mm=O().c;t();t();var nm=new L(new Ud(Wi)),dq=sv(),Zd=Su(),sf=op().ga;gX(a,Oj,lj,mo,new jw(a,mm,nm,dq.Hd(new Uu(Zd,sf))),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I)}else{var oj=ra.y,al=ra.w;if(oj instanceof Ku){var Ll=oj.fc,Qm=oj.vd,Rm=oj.be;if(t().d===Ll&&al instanceof jw){var hq=al.mb;if(hq instanceof L){var Bn=hq.k;if(Bn instanceof Ud){var hp=Bn.fa;if(null!==hp){var ru=hp.me,qr=hp.Ne;if(a.$c){var Xs=new U(()=>c.zf(!0)),rr=t().d,iq=Rm.Ba;op();var qo=pp(qp(),iq),qm=jX(a,Xs,rr,new y(Re=>qo.U(Re)),Qm,ru,k,n);bX(a,qm.ra,qr.ra,!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);kX(a,qr,qm,n,g,h,k,l,v,r,n,x,A,B,C,D,F,I);break b}}}}}}var jq=ra.y,pl=ra.w;if(jq instanceof Ku){var ro=jq.fc,Cn=jq.be;if(pl instanceof jw){var ip=pl.Xb,so=pl.mb,Dn=pl.Dc;if(so instanceof L){var sr=so.k;if(sr instanceof Ud){var kq=sr.fa;if(null!==kq){var ql=kq.me,Ys=kq.Ne,Sm=lX(Cn.Ba,new y(Re=>Pe(new E(Re.h()),ql)));if(Sm instanceof L){var Nl=Sm.k;kX(a,Ys,Nl.j(),n,g,h,k,l,v,r,n,x,A,B,C,D,F,I);bX(a,Nl.j().ra,Ys.ra,!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I)}else if(t().d===Sm)c:{if(ro instanceof L){var jp=ro.k;if(jp instanceof Tv){var lq=jp.Ic;if(jp.kf.L(ql)){var mq=new jw(a,ip,t().d,Dn);bX(a,lq,mq.zf(!1),!0,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I)}else bX(a,lq,e.zf(!1),!0,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);break c}}iX(a,t().d,g,k,r,n)}else throw new w(Sm); break b}}}}}var Tm=ra.y,En=ra.w;if(Tm instanceof Ku){var to=Tm.fc,Fn=Tm.vd;if(t().d===to&&En instanceof jw){var nq=En.Xb,Um=En.mb;if(t().d===Um){Nx(a,new U(()=>"Tag checking "+Fn+" "+nq));nq.qo(new y(Re=>{var rj=Fn.m();rj=new xo(rj,new y(ai=>{if(ai instanceof nC){var rm=ai.rp;ai=ai.JI;ai=Qt((Od(),Pd(u(),ai)),new y(Nn=>new vl(Nn.V)));return new z(rm,ai)}return O().c}));return tea(rj,Re.uo())}))?Nx(a,new U(()=>"OK "+Fn+" \x3c: "+nq)):iX(a,t().d,g,k,r,n);void 0;break b}}}var kp=ra.y,oq=ra.w;if(kp instanceof Ku){var su=kp.fc;if(t().d===su&&oq instanceof jw){var Gn=oq.mb;if(Gn instanceof L){var ur=Gn.k;if(ur instanceof fe&&(ur.aa instanceof cv||ur.aa instanceof Vv)){iX(a,t().d,g,k,r,n);break b}}}}var In=ra.y,Zs=ra.w;if(In instanceof Ku){var $s=In.fc;if($s instanceof L){var pq=$s.k;if(pq instanceof zv&&Zs instanceof jw){var vr=Zs.mb;if(vr instanceof L){var Vm=vr.k;if(Vm instanceof fe){var Jn=Vm.aa;if(Jn instanceof zv){var wr=pq.Yb.K();if(Pe(new E(wr),Jn.Yb.K())){var at=pq.Yb,xr=op(),lp=at.Gb(xr.ga).j(), Kn=Jn.Yb,qq=op(),yr=Kn.Gb(qq.ga).j();pH(new Wq(lp,lp,yr),new fn((Re,rj)=>{bX(a,Re.ra,rj.ra,!1,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);kX(a,rj,Re,n,g,h,k,l,v,r,n,x,A,B,C,D,F,I)}));break b}}}}}}}var rq=ra.y,sq=ra.w;if(rq instanceof Ku){var bt=rq.fc;if(bt instanceof L){var tq=bt.k;if(tq instanceof Vv&&sq instanceof jw){var zr=sq.mb;if(zr instanceof L){var ct=zr.k;if(ct instanceof fe){var Ar=ct.aa;if(Ar instanceof Sv){kX(a,Ar.Fd,tq.Zj(),n,g,h,k,l,v,r,n,x,A,B,C,D,F,I);bX(a,tq.Zj().ra,Ar.Fd.ra,!1,n,g,h,k,l,v,x, A,r,n,B,C,D,F,I);break b}}}}}}var uq=ra.y;if(uq instanceof Ku){var Br=uq.fc;if(Br instanceof L&&Br.k instanceof Vv){iX(a,t().d,g,k,r,n);break b}}var Ln=ra.y;if(Ln instanceof Ku){var vq=Ln.fc,Cr=Ln.vd,tu=Ln.be,uu=Ln.Me;if(vq instanceof L){var dt=vq.k;if(dt instanceof Jv){var vu=O().c;t();var Dr=mX(dt);gX(a,vu,new Ku(a,new L(Dr),Cr,tu,uu),O().c,e,g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break b}}}var uo=ra.y,Er=ra.w;if(uo instanceof Ku){var et=uo.fc;if(et instanceof L){var ft=et.k;if(ft instanceof Tv){var gt= ft.Ic;if(Er instanceof jw){var Wm=Er.mb;var Fr=t().d===Wm?!0:Wm instanceof L&&Wm.k instanceof fe?!0:!1;if(Fr){bX(a,gt,e.zf(!1),!0,n,g,h,k,l,v,x,A,r,n,B,C,D,F,I);break b}}}}}var mp=ra.w;if(mp instanceof jw){var wu=mp.Xb,ht=mp.mb;if(ht instanceof L){var wq=ht.k;if(wq instanceof fe){var xq=wq.aa;if(xq instanceof Tv){var Gr=xq.Ic,xu=Qt(wu,new y(Re=>{var rj=V(Re.q);return NA(Re,rj,!1)})),yu=O().c;gX(a,xu,c,new z(Gr,yu),lw(a),g,h,l,k,m,x,A,v,r,n,B,C,D,F,I);break b}}}}throw new w(ra);}}}}}else throw new w(P); }}),a.qa)}catch(P){if(P instanceof Iq){var N=P;if(N.Qg!==M)throw N;}else throw P;}} function kX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D){var F=G(new H,b.Oa,c.Oa);a:{var I=F.y,M=F.w;if(I instanceof L&&(I=I.k,M instanceof L)){bX(a,I,M.k,!1,d,e,g,h,k,l,r,v,m,n,x,A,B,C,D);break a}d=F.y;e=F.w;if(d instanceof L&&(d=d.k,R()===e)){Nx(a,new U(()=>"RHS not mutable! in "+b+" \x3c- "+c));b.Pd.Ga.b()||c.Pd.Ga.b()?(t(),F=Ye(new Te(new Ue(J(new K,["cannot be reassigned"]))),u()),F=new L(F)):(t(),F=Ye(new Te(new Ue(J(new K,["is not mutable"]))),u()),F=new L(F));e=c.ra;e=ux(e.q,e,c.Pd);g=ux(d.q,d,b.Pd); k=O().c;e=new z(e,new z(g,k));g=V(a);d=ux(d.q,d,g);g=O().c;iX(a,F,G(new H,e,new z(d,g)),h,m,n);break a}a=F.y;h=F.w;R()===a&&h instanceof L?a=!0:(a=F.y,h=F.w,a=R()===a&&R()===h?!0:!1);if(!a)throw new w(F);}} function nX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D,F,I,M){var N=xA(b,!0),P=Xu().X(),T=Hw(),Y=Su(),Z=op().ga;b=xea(a,b,c,d,e,m,P,T.Hd(new Uu(Y,Z)),g);N=xA(b,!0).wy(N);if(!N.b()){d=a.qa;a.F&&(e=ut(Q(),"| ",a.r)+"RECONSTRAINING TVs",ff(gf(),e+"\n"));a.r=1+a.r|0;try{N.Ca(new y(ea=>{a:{if(null!==ea&&(iC(a),!ea.Sb.b())){a.F&&(ea=ut(Q(),"| ",a.r)+"No need to reconstrain assigned "+ea,ff(gf(),ea+"\n"));break a}if(a.F){var ia=ut(Q(),"| ",a.r)+"Reconstraining "+ea;ff(gf(),ia+"\n")}if(ea.Xa>c)for(ia=vy(ea);!ia.b();){for(var X= ia.e(),sa=rA(ea);!sa.b();){var Ja=sa.e();bX(a,X,Ja,!1,h,k,l,m,n,r,v,x,A,B,C,D,F,I,M);sa=sa.f()}ia=ia.f()}}}));var S=void 0}finally{a.r=-1+a.r|0}dx(new E(d),a.qa)&&a.F&&(S=""+ut(Q(),"| ",a.r)+d.n(S),ff(gf(),S+"\n"))}return b} function bX(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D,F){a.Ep=1+a.Ep|0;var I=G(new H,b,c);Hea(m,I);YW(a,g,k,n,r,m,v,x,A,B,C);if(d){I=g.h().Jg();var M=new y(P=>Ot(new E(P),b));I=!I.b()&&M.n(I.o())?g.h():new z(b,g.h());M=g.j().Jg();var N=new y(P=>Ot(new E(P),c));M=!M.b()&&N.n(M.o())?g.j():new z(c,g.j());I=G(new H,I,M)}else I=O().c,I=new z(b,I),M=O().c,I=G(new H,I,new z(c,M));g=d||h.b()?h:new z(g,h);d||(d=ap(),l=new QN(l.QC,d,l.tu));Iea(a,b,c,e,I,g,k,l,D,v,m,n,r,x,A,B,C,F);wU(m)} function Iea(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C,D){var F=tc();try{Lx(a,new U(()=>h.da+". C "+b+" \x3c! "+c+" ("+k.ka()+")"),new U(()=>{var M=Xu().X();if(Zu(b,c,h,!1,M))Nx(a,new U(()=>"Already a subtype by \x3c:\x3c"));else{var N=G(new H,b,c);if(N.y instanceof OA||N.w instanceof OA)M=k;else{if(!a.ZE&&l.L(N))throw new fX(F,(Nx(a,new U(()=>"Cached!")),void 0));M=jD(b);var P=jD(c);P=G(new H,M,P);if(k.su.L(N))throw new fX(F,(Nx(a,new U(()=>"Spurious cycle involving "+N)),void 0));if(!a.pP&&k.tu.L(P)&& !k.su.L(P)){Nx(a,new U(()=>"SHADOWING DETECTED!"));M=new oX(a);if(!Vr(new Wr(M),N).L(!0)){M=new Te(new Ue(J(new K,["Cyclic-looking constraint while typing ","; a type annotation may be required"])));var T=[We(Xe(),m.lh)];M=Ye(M,J(new K,T));M=G(new H,M,m.Ga);if(a.zA){T=Ye(new Te(new Ue(J(new K,["\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014 Additional debugging info: \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014"]))),u());var Y=t().d;T=G(new H,T,Y);Y=new Te(new Ue(J(new K,["this constraint: ", " \x3c: "," "," ",""])));var Z=[We(Xe(),b.u()),We(Xe(),c.u()),We(Xe(),ag(ca(b))),We(Xe(),ag(ca(c)))];Y=Ye(Y,J(new K,Z));Z=t().d;Y=G(new H,Y,Z);Z=new Te(new Ue(J(new K,[" ... looks like: "," \x3c: ",""])));P=[We(Xe(),nb(P.y)),We(Xe(),nb(P.w))];P=Ye(Z,J(new K,P));Z=t().d;P=G(new H,P,Z);Z=O().c;P=new z(T,new z(Y,new z(P,Z)))}else P=Ye(new Te(new Ue(J(new K,["Note: use flag `:ex` to see internal error info."]))),u()),T=t().d,P=G(new H,P,T),T=O().c,P=new z(P,T);ay(a,new z(M,P),d)}throw new fX(F); }a.ZE||l.$(N);M=k.su.bc(N).bc(P);M=new QN(a,M,k.tu.bc(P))}(new y(S=>{a:{var ea=N.y;if(!(ea instanceof FA&&!0===ea.Eh)){var ia=N.w;b:if(ia instanceof FA&&!1===ia.Eh)var X=!0;else{if(ia instanceof Qv){var sa=ia.Ba,Ja=O().c;if(null===Ja?null===sa:Ja.i(sa)){X=!0;break b}}X=!1}if(!X){var Xa=N.y;if(Xa instanceof cC)bX(a,Xa.Sf,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var Fa=N.w;if(Fa instanceof cC)bX(a,b,Fa.Fg,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var za=N.y;if(za instanceof OA)bX(a,za.Hi,c,!0,d,e,g,h, S,n,r,v,m,x,A,B,C,l,D);else{var Qa=N.w;if(Qa instanceof OA)bX(a,b,Qa.Hi,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else if(!(Uv(N.y)&&Uv(N.w)&&Pe(new E(b),c))){var Ma=N.y,Ga=N.w;if(Ma instanceof MA){var ab=Ma.Fc;if(Ga instanceof MA){bX(a,Ga.Fc,ab,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Hb=N.y,bc=N.w;if(Hb instanceof Mu){var yb=Hb.pd;if(yb instanceof vl){var tb=yb.x;if(bc instanceof Qv&&a.$c&&hB(ve(),tb)){h.$a.n(tb);for(var eb=new y(wd=>{a:{if(null!==wd){var ge=wd.h(),De=wd.j();if(null!==ge&&"Eql#A"=== ge.x){ge=G(new H,ge,De);wd=O().c;cX(a,b,new Qv(a,new z(ge,wd),V(a)),e,g,h,S,x,m,D,n,r,v,A,B,C,l);break a}}if(null!==wd){ge=wd.h();wd=wd.j();De=new U(()=>b);t();var qf=new L(tb),og=new y(()=>t().d),Xf=uv(),mh=Su(),Ag=op().ga;ge=jX(a,De,qf,og,Xf.ng(new Uu(mh,Ag)),ge,h,d);bX(a,ge.ra,wd.ra,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);kX(a,wd,ge,d,e,g,h,S,n,m,x,r,v,A,B,C,l,D)}else throw new w(wd);}}),kb=bc.Ba;!kb.b();)eb.n(kb.e()),kb=kb.f();break a}}}var Rb=N.y,Gb=N.w;if(Rb instanceof cv){var vb=Rb.Nb,Tb=Rb.ac;if(Gb instanceof cv){var Nb=Gb.ac;bX(a,Gb.Nb,vb,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);bX(a,Tb,Nb,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var ic=N.y,Va=N.w;if(!(ic instanceof Mu&&Uv(Va)&&ic.Fv().L(Va.uo()))){var cb=N.y,zb=N.w;if(cb instanceof lx){iC(a);var Ub=cb.Sb;if(!Ub.b()){var jb=Ub.o();bX(a,jb,zb,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var db=N.y,ub=N.w;if(ub instanceof lx){iC(a);var Aa=ub.Sb;if(!Aa.b()){var va=Aa.o();bX(a,db,va,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Ra=N.y,rb=N.w;if(Ra instanceof lx&& rb.Ea()<=Ra.Xa){Nx(a,new U(()=>"NEW "+Ra+" UB ("+rb.Ea()+")"));var xb=e.h(),mc=dl(Km(e.j()),xb).mf(rb,new fn((wd,ge)=>{var De=wd.ma();return Ot(new E(De),V(a))?ge:ux(a,ge,wd.ma())})),Ha=rA(Ra);KA(Ra,new z(mc,Ha));for(var Ka=vy(Ra),Oa=new y(wd=>{bX(a,wd,rb,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}),Na=Ka;!Na.b();)Oa.n(Na.e()),Na=Na.f()}else{var Da=N.y,ta=N.w;if(ta instanceof lx&&Da.Ea()<=ta.Xa){Nx(a,new U(()=>"NEW "+ta+" LB ("+Da.Ea()+")"));var Ya=e.h(),dc=dl(Km(e.j()),Ya),ka=YA(dc,Da,new fn((wd,ge)=>{var De= ge.ma();return Ot(new E(De),V(a))?wd:ux(a,wd,ge.ma())})),ya=vy(ta);IA(ta,new z(ka,ya));for(var Sa=rA(ta),xc=new y(wd=>{bX(a,Da,wd,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}),Sb=Sa;!Sb.b();)xc.n(Sb.e()),Sb=Sb.f()}else{var uc=N.y,Lb=N.w;if(uc instanceof lx)if(Nx(a,new U(()=>"wrong level: "+Lb.Ea())),a.li&&Lb.Ea()<=h.da){Nx(a,new U(()=>"STASHING "+uc+" bound in extr ctx"));var lc=pX(h.cb,uc,new U(()=>TE(PE()))),Xb=G(new H,!1,Lb);lc.$(Xb);var ec=G(new H,uc,Lb);l.Bm(ec)}else{var Ab=uc.Xa,Ob=a.Df,fb=e.h(),Wa=op(), bb=g.Gb(Wa.ga).h(),Ia=op(),Ua=dl(g.Gb(Ia.ga).j(),bb),pc=nX(a,Lb,Ab,!1,Ob,new z(fb,Ua),d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);Nx(a,new U(()=>"EXTR RHS ~\x3e "+pc+" to "+uc.Xa));Nx(a,new U(()=>" where "+xx(pc)));bX(a,uc,pc,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}else{var sc=N.y,Ba=N.w;if(Ba instanceof lx)if(Nx(a,new U(()=>"wrong level: "+sc.Ea())),a.li&&sc.Ea()<=h.da){Nx(a,new U(()=>"STASHING "+Ba+" bound in extr ctx"));var ob=pX(h.cb,Ba,new U(()=>TE(PE()))),nc=G(new H,!0,sc);ob.$(nc);var Ib=G(new H,sc,Ba);l.Bm(Ib)}else{var vc= Ba.Xa,Vb=a.Df,fc=e.j(),Bc=op(),Pb=g.Gb(Bc.ga).j(),Jb=op(),gc=dl(g.Gb(Jb.ga).h(),Pb),Cb=nX(a,sc,vc,!0,Vb,new z(fc,gc),d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);Nx(a,new U(()=>"EXTR LHS ~\x3e "+Cb+" to "+Ba.Xa));Nx(a,new U(()=>" where "+xx(Cb)));bX(a,Cb,Ba,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}else{var cc=N.y,yc=N.w;if(cc instanceof zv){var Mc=cc.Yb;if(yc instanceof zv){var qc=yc.Yb,oc=Mc.K();if(Pe(new E(oc),qc.K())){pH(new Wq(Mc,Mc,qc),new fn((wd,ge)=>{var De=G(new H,wd,ge);a:{ge=De.y;var qf=De.w;if(null!==ge&& (wd=ge.h(),ge=ge.j(),null!==qf)){var og=qf.h();De=qf.j();qf=new y(Xf=>{var mh=new y(Ag=>{if(Nm(new E(Xf),Ag)){var Bg=new Te(new Ue(J(new K,["Wrong tuple field name: found '","' instead of '","'"])));Ag=[We(Xe(),Xf.x),We(Xe(),Ag.x)];return Lw(a,Ye(Bg,J(new K,Ag)),b.ma().Ga,d)}});og.b()||mh.n(og.o())});wd.b()||qf.n(wd.o());kX(a,De,ge,d,e,g,h,S,n,m,x,r,v,A,B,C,l,D);bX(a,ge.ra,De.ra,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}throw new w(De);}}));break a}}}var Qc=N.y,jc=N.w;if(Qc instanceof Vv&&jc instanceof Sv)kX(a,jc.Fd,Qc.Zj(),d,e,g,h,S,n,m,x,r,v,A,B,C,l,D),bX(a,Qc.Zj().ra,jc.Fd.ra,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var sb=N.y;if(sb instanceof LA){var Gc=sb.ic,Wb=sb.jc;if(!0===sb.tc){bX(a,Gc,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);bX(a,Wb,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Cc=N.w;if(Cc instanceof LA){var Fc=Cc.ic,qd=Cc.jc;if(!1===Cc.tc){bX(a,b,Fc,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);bX(a,b,qd,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Yb=N.y;if(Yb instanceof ZB){$B(a);t();var Nc=Yb.mc(), ad=new L(Nc);if(!ad.b()){bX(a,ad.k,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Uc=N.w;if(Uc instanceof ZB){$B(a);t();var cd=Uc.mc(),kc=new L(cd);if(!kc.b()){bX(a,b,kc.k,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Vc=N.w;if(Vc instanceof zv){var Hc=Vc.Yb;if(Hc instanceof z){var rc=Hc.p,sd=O().c;null===sd||sd.i(rc)}}var Kc=N.y,Qd=N.w;if(Kc instanceof Mu){var Ad=Kc.pd,kd=a.Bk;if((null===kd?null===Ad:kd.i(Ad))&&Qd instanceof cv){var Hd=Qd.ac;bX(a,Qd.Nb,Kc,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);bX(a, Kc,Hd,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var Rd=N.y,Bd=N.w;if(Rd instanceof cv){var ae=Rd.Nb,dd=Rd.ac;if(Bd instanceof Mu){var od=Bd.pd,Ta=a.Bk;if(null===Ta?null===od:Ta.i(od)){bX(a,Bd,ae,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);bX(a,dd,Bd,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}}var wb=N.y,$a=N.w;if(wb instanceof Qv){var wa=wb.Ba;if($a instanceof Qv){for(var hb=new y(wd=>{if(null!==wd){var ge=wd.h(),De=wd.j();wd=lX(wa,new y(Xf=>Pe(new E(Xf.h()),ge)));var qf=new U(()=>{iX(a,t().d,e,h,m,x)}),og= new y(Xf=>{if(null!==Xf)Xf=Xf.j(),kX(a,De,Xf,d,e,g,h,S,n,m,x,r,v,A,B,C,l,D),bX(a,Xf.ra,De.ra,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else throw new w(Xf);});wd.b()?Es(qf):og.n(wd.o())}else throw new w(wd);}),ra=$a.Ba;!ra.b();)hb.n(ra.e()),ra=ra.f();break a}}var wc=N.y;if(wc instanceof zv&&N.w instanceof Qv)bX(a,wc.kq(),c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var ac=N.y,Id=N.w;if(ac instanceof Mu){var ud=ac.pd,be=a.Bk;if((null===be?null===ud:be.i(ud))&&Id instanceof Qv){for(var re=new y(wd=>{bX(a,ac,wd.j().ra, !1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}),pe=Id.Ba;!pe.b();)re.n(pe.e()),pe=pe.f();break a}}if(N.w instanceof Qv)cX(a,b,c,e,g,h,S,x,m,D,n,r,v,A,B,C,l);else{var bd=N.y,Rc=N.w;if(bd instanceof Qv){var Wc=bd.Ba;if(Rc instanceof Mu){var Wd=Rc.pd,zd=a.Bk;if(null===zd?null===Wd:zd.i(Wd)){for(var Pa=new y(wd=>{bX(a,wd.j().ra,Rc,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}),Db=Wc;!Db.b();)Pa.n(Db.e()),Db=Db.f();break a}}}var Oc=N.y,Tc=N.w;if(Oc instanceof fw&&Tc instanceof fw&&Nm(new E(Oc.qb.V),"Array")&&Nm(new E(Tc.qb.V), "Eql"))if(Pe(new E(Oc.qb),Tc.qb)){tp();var Sd=jv(Oc.Zb,Tc.Zb);up(0,Pe(new E(Sd),0));var Jc=h.tb.U(Oc.qb.V);if(Jc instanceof L){var vd=Jc.k,hd=dB(vd),de=vd.Xm,ye=op(),jf=de.Gb(ye.ga).j();rH(qD(new Wq(jf,jf,Oc.Zb),Tc.Zb),new fW((wd,ge,De)=>{wd=hd.n(wd);wd.qe||bX(a,ge,De,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);wd.Qd||bX(a,De,ge,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}))}else if(t().d===Jc){var af=h.$a.U(Oc.qb.V);if(af instanceof L){var pf=af.k,kf=Qt(pf.Ag(),new y(wd=>wd.hb));rH(qD(new Wq(kf,kf,Oc.Zb),Tc.Zb),new fW((wd, ge,De)=>{wd=VD(pf).U(wd);wd=wd.b()?ou().Yl:wd.o();wd.qe||bX(a,ge,De,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);wd.Qd||bX(a,De,ge,!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}))}else if(t().d===af)no();else throw new w(af);}else throw new w(Jc);}else if(Lca(Oc,h))bX(a,cD(Oc,h,d),cD(Tc,h,d),!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var Be=G(new H,gw(Oc,h),gw(Tc,h));b:{var Kd=Be.y,ld=Be.w;if(Kd instanceof L){var Jd=Kd.k;if(ld instanceof L){var Dd=ld.k,Xd=Xu().X();if(!Zu(Jd,Dd,h,!0,Xd)){iX(a,t().d,e,h,m,x);break b}}}bX(a,cD(Oc, h,d),cD(Tc,h,d),!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}}else{var Yc=N.y;if(Yc instanceof fw)bX(a,cD(Yc,h,d),c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var Ce=N.y,te=N.w;if(Ce instanceof Mu){var Ie=Ce.pd,Jf=a.Bk;if((null===Jf?null===Ie:Jf.i(Ie))&&te instanceof fw)break a}var df=N.w;if(df instanceof fw)bX(a,b,cD(df,h,d),!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else{var vg=N.y;if(vg instanceof Mu){var wg=vg.pd,xg=a.Bk;if(null===xg?null===wg:xg.i(wg))break a}var eg=N.w;if(eg instanceof Mu){var vh=eg.pd,fg=a.Bk;if(null=== fg?null===vh:fg.i(vh))break a}var ih=N.w;if(ih instanceof Tv){var Ig=ih.Ic;bX(a,iB(b,ih.kf),Ig,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}else{var Tf=N.w;if(Tf instanceof MA){var Jg=Tf.Fc;if(Jg instanceof Tv){var jh=Jg.Ic;bX(a,new Tv(a,b,Jg.kf,Jg.vp),new MA(a,jh,Tf.rA),!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}var yg=N.w;if(yg instanceof Qx)Sx(h,new y(wd=>{var ge=uca(yg,wd);Nx(a,new U(()=>"BUMP TO LEVEL "+wd.da+" --\x3e "+ge));Nx(a,new U(()=>"where "+xx(ge)));bX(a,b,ge,!0,d,e,g,wd,S,n,r,v,m,x,A,B,C,l,D); aX(a,h,wd,e,g,x,D,n,r,v,m,A,B,C,l)}),d,m);else{var gg=N.y;if(null!==gg){var Cf=Hv(Iv(a),gg,h);if(!Cf.b()){var Uf=Cf.k;if(Uf instanceof Qx){var $g=Uf.de,Ah=Uf.Re;if(Ah.Ea()<=$g){bX(a,Ah,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}}}var Kg=N.w;if(null!==Kg){var Vf=tca(pca(a),Kg,h,d);if(!Vf.b()){var hg=Vf.o();if(a.Wq){Nx(a,new U(()=>"DISTRIB-R ~\x3e "+hg));bX(a,b,hg,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}}var zg=N.y;if(null!==zg){var Lg=Hv(Iv(a),zg,h);if(!Lg.b()){var Mg=Lg.k;if(Mg instanceof Qx){var Wf= Mg.de,Ng=Mg.Re;if(null!==Ng){var Kf=Hv(Iv(a),Ng,h);if(!Kf.b()){var xf=Kf.k;if(xf instanceof cv){var Og=xf.Nb,mi=xf.ac;if(a.Wq&&Og.Ea()<=Wf&&mi.Ea()>Wf){var Ci=new cv(a,Og,new Qx(a,Wf,mi),c.ma());Nx(a,new U(()=>"DISTRIB-L ~\x3e "+Ci));bX(a,Ci,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}}}}}}var Xh=N.y;if(Xh instanceof Qx){var wh=lca(kea(a),Xh,h,d);if(!wh.b()){var Bh=wh.o();if(a.Wq){Nx(a,new U(()=>"DISTRIB-L' ~\x3e "+Bh));bX(a,Bh,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);break a}}}var ng=N.y;if(ng instanceof Qx){if(g.b())var kh=O().c,Kh=O().c,ni=G(new H,kh,Kh),Lh=O().c,lh=new z(ni,Lh);else lh=g;(new y(wd=>{bX(a,TA(ng,h),c,!0,d,e,wd,h,S,n,r,v,m,x,A,B,C,l,D)})).n(lh)}else{var Ch=N.y;if(Ch instanceof eC){var Dh=Ch.Lj,Yh=Ch.kj;Lx(a,new U(()=>"DISCHARGE CONSTRAINTS"),new U(()=>{for(var wd=new y(De=>{if(null!==De)bX(a,De.h(),De.j(),!1,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D);else throw new w(De);}),ge=Dh;!ge.b();)wd.n(ge.e()),ge=ge.f()}),a.qa);bX(a,Yh,c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D)}else{N.w instanceof eC&&no(); var ah=N.w;if(ah instanceof LA&&!0===ah.tc)cX(a,b,c,e,g,h,S,x,m,D,n,r,v,A,B,C,l);else{var oi=N.y;if(oi instanceof LA&&!1===oi.tc)cX(a,b,c,e,g,h,S,x,m,D,n,r,v,A,B,C,l);else{var mj=N.y;mj instanceof Jv?bX(a,mX(mj),c,!0,d,e,g,h,S,n,r,v,m,x,A,B,C,l,D):N.y instanceof MA||N.y instanceof Tv||N.w instanceof MA||N.w instanceof Tv?cX(a,b,c,e,g,h,S,x,m,D,n,r,v,A,B,C,l):(N.y instanceof Mu||N.y instanceof nC)&&N.w instanceof nC?cX(a,b,c,e,g,h,S,x,m,D,n,r,v,A,B,C,l):iX(a,t().d,e,h,m,x)}}}}}}}}}}}}}}}}}}}}}}}}})).n(M)}}), a.qa)}catch(M){if(M instanceof Iq){var I=M;if(I.Qg!==F)throw I;}else throw M;}}function qX(a,b){var c=new Te(new Ue(J(new K,["does not match type `","`"])));a=[wO(Xe(),CD(a,b))];return Ye(c,J(new K,a))}function rX(a){var b=new Te(new Ue(J(new K,["does not have field '","'"])));a=[We(Xe(),a)];return Ye(b,J(new K,a))}function sX(a,b){if(!a.b()){var c=a.o();b.$(c)}return a} function tX(a){if(a.Zm)return Ye(new Te(new Ue(J(new K,["type"]))),u());var b=new Te(new Ue(J(new K,[""," of type"])));a=[We(Xe(),a.lh)];return Ye(b,J(new K,a))}var Kea=function Jea(a,b,c,d){if(b instanceof z){var g=b.p;b=b.z.m();b=new Ef(b,new y(h=>h.ma()));b=new Ex(b,new uX(a,c,d));Od();b=Pd(u(),b);return dl(Jea(a,g,c,d),b)}a=O().c;if(null===a?null===b:a.i(b))return O().c;throw new w(b);}; function Lea(a,b){var c=jA().X(),d=jA().X(),e=k=>{k=k.j();if(k.b())return!1;k=k.o();return d.L(k)};b=Kea(a,b,c,d);a:for(;;)if(b.b()){e=u();break}else if(c=b.e(),a=b.f(),!0===!!e(c))b=a;else for(;;){if(a.b())e=b;else{c=a.e();if(!0!==!!e(c)){a=a.f();continue}c=a;a=new z(b.e(),u());var g=b.f();for(b=a;g!==c;){var h=new z(g.e(),u());b=b.p=h;g=g.f()}for(g=c=c.f();!c.b();){h=c.e();if(!0===!!e(h)){for(;g!==c;)h=new z(g.e(),u()),b=b.p=h,g=g.f();g=c.f()}c=c.f()}g.b()||(b.p=g);e=a}break a}return e} function vX(a,b,c,d,e){var g=a.Ga;if(g instanceof L&&(g=g.k,!b.Ga.L(g)&&!c.L(g))){var h=new Te(new Ue(J(new K,["Note: constraint arises from ",":"]))),k=[We(Xe(),a.lh)];h=Ye(h,J(new K,k));a=sX(a.Ga,c);a=G(new H,h,a);h=d.Ga;a:{if(h instanceof L&&(h=h.k,Nm(new E(h),g)&&!b.Ga.L(h)&&!e.Ga.L(h)&&!c.L(h))){b=new Te(new Ue(J(new K,["from ",":"])));e=[We(Xe(),d.lh)];b=Ye(b,J(new K,e));c=sX(d.Ga,c);c=G(new H,b,c);d=O().c;c=new z(c,d);break a}c=O().c}return new z(a,c)}return O().c} function iX(a,b,c,d,e,g){var h=tc();try{var k=c.h().e(),l=c.j().e();Nx(a,new U(()=>"CONSTRAINT FAILURE: "+k+" \x3c: "+l));var m=c.h(),n=c.j(),r=m.m(),v=new iy(r,new y(ka=>ka.ma().Ix),!0),x=dH(v,new y(ka=>!ka.ma().Ga.b())),A=new y(ka=>ka.ma()),B=x.b()?R():new L(A.n(x.o())),C=new U(()=>k.ma()),D=B.b()?Es(C):B.o(),F=n.m(),I=new iy(F,new y(ka=>ka.ma().Ix),!0),M=dH(I,new y(ka=>!ka.ma().Ga.b())),N=new y(ka=>ka.ma()),P=M.b()?R():new L(N.n(M.o())),T=new U(()=>l.ma()),Y=P.b()?Es(T):P.o(),Z=er(n).m(),S=new iy(Z, new y(ka=>ka.ma().Ix),!0),ea=dH(S,new y(ka=>!ka.ma().Ga.b())),ia=new y(ka=>ka.ma()),X=ea.b()?R():new L(ia.n(ea.o())),sa=new U(()=>l.ma()),Ja=X.b()?Es(sa):X.o(),Xa=Wn(m,new wX(a,D,e)).Jg(),Fa=jA().X(),za=iu(ju(),m.Jg()),Qa=Is(n),Ma=za.tl(Qa).m(),Ga=new Ef(Ma,new y(ka=>ka.ma())),ab=new Ex(Ga,new xX(a));Od();var Hb=Pd(u(),ab),bc=ky(Hb,new y(ka=>ka.j().Ga)),yb=Fca(k),tb=new U(()=>{var ka=G(new H,yb,uy(l));if(ka.y instanceof YB||ka.w instanceof YB){var ya=ka.y,Sa=ka.w;if(ya instanceof YB&&Sa instanceof YB){t();var xc=new L(ya);t();var Sb=new Gp(ya,xc,new L(Sa),un(ya.ME,Sa.ME))}else{var uc=ka.y;if(uc instanceof YB)Sb=new Gp(uc,(t(),new L(uc)),t().d,uc.ME);else{var Lb=ka.w;Lb instanceof YB?Sb=new Gp(Lb,t().d,(t(),new L(Lb)),Lb.ME):xm("Program reached and unexpected state.")}}if(null!==Sb)var lc=new Gp(Sb.Uj,Sb.oj,Sb.oi,Sb.Xi);else throw new w(Sb);var Xb=lc.Uj,ec=lc.oj,Ab=lc.oi,Ob=Lea(a,lc.Xi),fb=sX(D.Ga,Fa),Wa=new U(()=>sX(Xb.Ym.et.Ga,Fa)),bb=fb.b()?Es(Wa):fb,Ia=new U(()=>sX(Xb.Ym.hi.ji.Ga,Fa)),Ua= bb.b()?Es(Ia):bb,pc=new Te(new Ue(J(new K,["Type error in ",""]))),sc=[We(Xe(),e.lh)],Ba=Ye(pc,J(new K,sc)),ob=sX(e.Ga,Fa),nc=G(new H,Ba,ob),Ib=new Te(new Ue(J(new K,["type variable `","` leaks out of its scope"]))),vc=[wO(Xe(),AD(Xb.Ym,d))],Vb=Ye(Ib,J(new K,vc)),fc=G(new H,Vb,Ua);if(Ab instanceof L){var Bc=Ab.k,Pb=sX(Y.Ga,Fa),Jb=new U(()=>sX(Bc.Ym.et.Ga,Fa)),gc=Pb.b()?Es(Jb):Pb,Cb=new U(()=>sX(Bc.Ym.hi.ji.Ga,Fa)),cc=gc.b()?Es(Cb):gc;if(!ec.b()&&Nm(new E(cc),Ua))var yc=new Te(new Ue(J(new K,["back into type variable `", "`"]))),Mc=[wO(Xe(),CD(Bc.Ym,d))],qc=Ye(yc,J(new K,Mc)),oc=G(new H,qc,cc),Qc=O().c,jc=new z(oc,Qc);else jc=O().c}else if(t().d===Ab){var sb=new Te(new Ue(J(new K,["into "," `","`"]))),Gc=[tX(Y),wO(Xe(),CD(l,d))],Wb=Ye(sb,J(new K,Gc)),Cc=sX(Y.Ga,Fa),Fc=G(new H,Wb,Cc),qd=O().c;jc=new z(Fc,qd)}else throw new w(Ab);if(Ob.b())Uc=O().c;else var Yb=Ye(new Te(new Ue(J(new K,["adding a type annotation to any of the following terms may help resolve the problem"]))),u()),Nc=t().d,ad=G(new H,Yb,Nc),Uc=new z(ad, Ob);var cd=Ja.Ga;a:{if(cd instanceof L){var kc=cd.k;if(!(Y.Ga.L(kc)||e.Ga.L(kc)||D.Ga.L(kc)||Fa.L(kc))){var Vc=new Te(new Ue(J(new K,["Note: constraint arises from ",":"]))),Hc=[We(Xe(),Ja.lh)],rc=Ye(Vc,J(new K,Hc)),sd=sX(Ja.Ga,Fa),Kc=G(new H,rc,sd),Qd=O().c;var Ad=new z(Kc,Qd);break a}}Ad=O().c}var kd=dl(dl(Ad,Uc),jc),Hd=new z(nc,new z(fc,kd));throw new fX(h,g.n(kr(jr(),dl(vX(Y,e,Fa,Ja,D),Hd),a.$c,lu())));}if(ka.y instanceof lx||ka.y instanceof ZB)return qX(l,d);var Rd=ka.y,Bd=ka.w;if(Rd instanceof Qv){var ae=Rd.Ba;if(Bd instanceof Qv){var dd=Qt(Bd.Ba,new y(Yc=>Yc.h())),od=Aq(Bq(),dd),Ta=Qt(ae,new y(Yc=>Yc.h())),wb=Aq(Bq(),Ta),$a=od.wy(wb),wa=sp($a),hb=new U(()=>qX(l,d)),ra=new y(Yc=>rX(Yc.x));return wa.b()?Es(hb):ra.n(wa.o())}}var wc=ka.w;if(XB(wc)&&wc.rr()instanceof vl){var ac=new Te(new Ue(J(new K,["is not an instance of type `","`"])));if(a.cn.L(wc.rr().Dh))var Id=We(Xe(),wc.rr().Dh);else Xe(),Q(),Id=We(0,Nu(0,wc.rr().Dh));return Ye(ac,J(new K,[Id]))}var ud=ka.w;if(ud instanceof fw){var be= new Te(new Ue(J(new K,["is not an instance of `","`"]))),re=[wO(Xe(),CD(ud,d))];return Ye(be,J(new K,re))}var pe=ka.y,bd=ka.w;if(bd instanceof zv){var Rc=bd.Yb;if(!(pe instanceof zv)){var Wc=new Te(new Ue(J(new K,["is not a ","-element tuple"])));Xe();var Wd=Rc.K(),zd=[We(0,""+Wd)];return Ye(Wc,J(new K,zd))}}var Pa=ka.y;if(ka.w instanceof cv&&!(Pa instanceof cv))return Ye(new Te(new Ue(J(new K,["is not a function"]))),u());var Db=ka.y,Oc=ka.w;if(Oc instanceof Qv){var Tc=Oc.Ba;if(Tc instanceof z){var Sd= Tc.z,Jc=Tc.p;if(null!==Sd){var vd=Sd.h(),hd=O().c;if((null===hd?null===Jc:hd.i(Jc))&&!(Db instanceof Qv))return rX(vd.x)}}}var de=ka.y,ye=ka.w;if(ye instanceof Qv){var jf=ye.Ba;if(jf instanceof z&&Vr(new Wr(new yX(a)),yb).L(!0)&&!(de instanceof Qv)){var af=new Te(new Ue(J(new K,["is not a record (expected a record with field",": ",")"]))),pf=0Yc.h().x)),Be=[pf,We(0,ze(kf,"",", ",""))];return Ye(af,J(new K,Be))}}var Kd=ka.w;if(Kd instanceof Qv){var ld=Kd.Ba;if(ld instanceof z){var Jd=new Te(new Ue(J(new K,["does not have all required fields ",""])));Xe();var Dd=Qt(ld,new y(Yc=>"'"+Yc.h().x+"'")),Xd=[We(0,ze(Dd,"",", ",""))];return Ye(Jd,J(new K,Xd))}}return qX(l,d)}),eb=b.b()?Es(tb):b.o(),kb=new Te(new Ue(J(new K,["Type mismatch in ",":"]))),Rb=[We(Xe(),e.lh)],Gb=Ye(kb,J(new K,Rb)),vb=sX(e.Ga,Fa),Tb=G(new H,Gb,vb),Nb=new Te(new Ue(J(new K,[""," `","` ",""]))),ic=[tX(D),wO(Xe(),AD(k,d)),eb],Va=Ye(Nb,J(new K,ic)),cb=Pe(new E(D.Ga),e.Ga)? t().d:sX(D.Ga,Fa),zb=G(new H,Va,cb),Ub=O().c,jb=new z(Tb,new z(zb,Ub)),db=new y(ka=>{var ya=new Te(new Ue(J(new K,[" with expected type `","`"]))),Sa=[wO(Xe(),CD(l,d))];Sa=Ye(ya,J(new K,Sa));ya=new Te(new Ue(J(new K,["but it flows into ","",""])));Sa=[We(Xe(),ka.ma().lh),Sa];ya=Ye(ya,J(new K,Sa));ka=sX(ka.ma().Ga,Fa);ka=G(new H,ya,ka);ya=O().c;return new z(ka,ya)}),ub=(Xa.b()?R():new L(db.n(Xa.o()))).ha(),Aa=op().ga,va=zX(ub,Aa),Ra=vX(Y,e,Fa,Ja,D),rb=Wn(bc,new AX(a,Fa,new GA(!0)));if(a.zA)var xb= Ye(new Te(new Ue(J(new K,["\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d Additional explanations below \x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d"]))),u()),mc=t().d,Ha=G(new H,xb,mc),Ka=ZW(m,new y(ka=>{if(a.F){var ya=new Te(new Ue(J(new K,["[info] LHS \x3e\x3e "," : ",""]))),Sa=[We(Xe(),ka.ma().u()),wO(Xe(),AD(ka,d))];ya=Ye(ya,J(new K,Sa));ka=ka.ma().Ga;ka=G(new H,ya,ka);ya=O().c;return new z(ka,ya)}ya=new Te(new Ue(J(new K,["[info] flowing from "," `","`"])));Sa=[tX(ka.ma()),wO(Xe(),AD(ka,d))];ya=Ye(ya,J(new K, Sa));ka=ka.ma().Ga;ka=G(new H,ya,ka);ya=O().c;return new z(ka,ya)})),Oa=dl(ZW(Km(n),new y(ka=>{if(a.F){var ya=new Te(new Ue(J(new K,["[info] RHS \x3c\x3c "," : ",""]))),Sa=[We(Xe(),ka.ma().u()),wO(Xe(),CD(ka,d))];ya=Ye(ya,J(new K,Sa));ka=ka.ma().Ga;ka=G(new H,ya,ka);ya=O().c;return new z(ka,ya)}ya=new Te(new Ue(J(new K,["[info] flowing into "," `","`"])));Sa=[tX(ka.ma()),wO(Xe(),CD(ka,d))];ya=Ye(ya,J(new K,Sa));ka=ka.ma().Ga;ka=G(new H,ya,ka);ya=O().c;return new z(ka,ya)})),Ka),Na=new z(Ha,Oa);else Na= O().c;t();var Da=J(new K,[jb,va,Ra,rb,Na]),ta=Pd(u(),Da),Ya=op().ga,dc=zX(ta,Ya);g.n(kr(jr(),dc,a.$c,lu()))}catch(ka){if(ka instanceof Iq){if(b=ka,b.Qg!==h)throw b;}else throw ka;}} var fD=function BX(a,b,c,d,e,g,h,k){At(tp(),c>=d.da);if(b.Ea()<=e)return b;var m=!1,n=null,r=!1,v=null;if(b instanceof lx&&(m=!0,n=b,g.L(n)))return n;if(m){iC(a);var x=n.Sb;if(!x.b()){var A=x.o();return h.Se(n,new U((ra=>()=>{var wc=ra.ji;t();var ac=new L(ra),Id=ra.kg,ud=O().c,be=O().c;wc=new lx(a,ra.Xa>c?ra.Xa:d.da,ud,be,ac,Id,!1,wc);ac=G(new H,ra,wc);h.$(ac);ac=BX(a,A,c,d,e,g,h,k);wy(wc,(t(),new L(ac)));return wc})(n)))}}if(m){var B=!1,C=h.U(n);if(C instanceof L)return C.k;if(R()===C&&(B=!0,k&& n.Xa<=c)){var D=V(a);t();var F=new L(n),I=n.kg,M=O().c,N=O().c,P=new mx(a,new lx(a,d.da,M,N,F,I,!1,D),n.ji),T=n;if(a.F){var Y=ut(Q(),"| ",a.r)+("New skolem: "+T+" ~\x3e ")+P;ff(gf(),Y+"\n")}if(vy(n).b()?!rA(n).b():1){var Z=n.ji;t();var S=new L(n),ea=n.kg,ia=O().c,X=O().c,sa=new lx(a,d.da,ia,X,S,ea,!1,Z),Ja=G(new H,n,sa);h.$(Ja);var Xa=vy(sa),Fa=rA(n);if(Fa===u())var za=u();else{for(var Qa=Fa.e(),Ma=new z(BX(a,Qa,c,d,e,g,h,k),u()),Ga=Ma,ab=Fa.f();ab!==u();){var Hb=ab.e(),bc=new z(BX(a,Hb,c,d,e,g,h, k),u());Ga=Ga.p=bc;ab=ab.f()}za=Ma}for(var yb=P,tb=za;!tb.b();){var eb=yb,kb=tb.e(),Rb=eb,Gb=kb,vb=V(Rb.q);yb=Pu(Rb,Gb,vb,!1);tb=tb.f()}IA(sa,new z(yb,Xa));if(a.F){var Tb=ut(Q(),"| ",a.r)+(sa+" :\x3e ")+vy(sa);ff(gf(),Tb+"\n")}var Nb=rA(sa),ic=vy(n);if(ic===u())var Va=u();else{for(var cb=ic.e(),zb=new z(BX(a,cb,c,d,e,g,h,k),u()),Ub=zb,jb=ic.f();jb!==u();){var db=jb.e(),ub=new z(BX(a,db,c,d,e,g,h,k),u());Ub=Ub.p=ub;jb=jb.f()}Va=zb}for(var Aa=P,va=Va;!va.b();){var Ra=Aa,rb=va.e(),xb=Ra,mc=rb,Ha=V(xb.q); Aa=dv(xb,mc,Ha,!1);va=va.f()}KA(sa,new z(Aa,Nb));if(a.F){var Ka=ut(Q(),"| ",a.r)+(sa+" \x3c: ")+rA(sa);ff(gf(),Ka+"\n")}return sa}var Oa=G(new H,n,P);h.$(Oa);return P}if(B){var Na=n.ji;t();var Da=new L(n),ta=n.kg,Ya=O().c,dc=O().c;if(n.Xa>c)var ka=n.Xa;else{if(!(d.da<=c))throw new Yj("assertion failed: this condition should be false for the result to be correct");ka=d.da}var ya=new lx(a,ka,Ya,dc,Da,ta,!1,Na),Sa=G(new H,n,ya);h.$(Sa);for(var xc=vy(n),Sb=null,uc=null,Lb=xc,lc=xc,Xb;;)if(lc.b()){null=== Sb?Xb=Lb:(uc.p=Lb,Xb=Sb);break}else{var ec=lc.e(),Ab=BX(a,ec,c,d,e,g,h,k);if(Ab===ec)lc=lc.f();else{for(var Ob=Lb,fb=Sb,Wa=uc;Ob!==lc;){var bb=new z(Ob.e(),u());null===fb&&(fb=bb);null!==Wa&&(Wa.p=bb);Wa=bb;Ob=Ob.f()}var Ia=new z(Ab,u());null===fb&&(fb=Ia);null!==Wa&&(Wa.p=Ia);Wa=Ia;var Ua=lc.f(),pc=Wa;Sb=fb;uc=pc;lc=Lb=Ua}}IA(ya,Xb);for(var sc=rA(n),Ba=null,ob=null,nc=sc,Ib=sc,vc;;)if(Ib.b()){null===Ba?vc=nc:(ob.p=nc,vc=Ba);break}else{var Vb=Ib.e(),fc=BX(a,Vb,c,d,e,g,h,k);if(fc===Vb)Ib=Ib.f();else{for(var Bc= nc,Pb=Ba,Jb=ob;Bc!==Ib;){var gc=new z(Bc.e(),u());null===Pb&&(Pb=gc);null!==Jb&&(Jb.p=gc);Jb=gc;Bc=Bc.f()}var Cb=new z(fc,u());null===Pb&&(Pb=Cb);null!==Jb&&(Jb.p=Cb);Jb=Cb;var cc=Ib.f(),yc=Jb;Ba=Pb;ob=yc;Ib=nc=cc}}KA(ya,vc);return ya}throw new w(C);}if(b instanceof cC){var Mc=b.Sf;return new cC(a,BX(a,b.Fg,c,d,e,g,h,k),BX(a,Mc,c,d,e,g,h,k),b.TO)}if(b instanceof cv){var qc=b.ac;return new cv(a,BX(a,b.Nb,c,d,e,g,h,k),BX(a,qc,c,d,e,g,h,k),b.Mj)}if(b instanceof LA){var oc=b.jc;return new LA(a,b.tc,BX(a, b.ic,c,d,e,g,h,k),BX(a,oc,c,d,e,g,h,k),b.KE)}if(b instanceof Qv){var Qc=b.Ba;return new Qv(a,ry(lv(),Qc,new y(ra=>{var wc=ra.Va,ac=ra.Oa;ac.b()?ac=R():(ac=ac.o(),ac=new L(BX(a,ac,c,d,e,g,h,k)));return new Uw(wc,ac,BX(a,ra.ra,c,d,e,g,h,k),ra.Pd)})),b.Nj)}if(b instanceof zv){var jc=b.Yb;return new zv(a,ry(lv(),jc,new y(ra=>{var wc=ra.Va,ac=ra.Oa;ac.b()?ac=R():(ac=ac.o(),ac=new L(BX(a,ac,c,d,e,g,h,k)));return new Uw(wc,ac,BX(a,ra.ra,c,d,e,g,h,k),ra.Pd)})),b.Nq)}if(b instanceof Sv){var sb=b.Fd,Gc=sb.Va, Wb=sb.Oa;if(Wb.b())var Cc=R();else{var Fc=Wb.o();Cc=new L(BX(a,Fc,c,d,e,g,h,k))}return new Sv(a,new Uw(Gc,Cc,BX(a,sb.ra,c,d,e,g,h,k),sb.Pd),b.Fx)}if(b instanceof Wv)return sB(b,new y(ra=>BX(a,ra,c,d,e,g,h,k)),new y(ra=>BX(a,ra,c,d,e,g,h,k)),new y(ra=>BX(a,ra,c,d,e,g,h,k)),b.Uu);if(b instanceof MA)return new MA(a,BX(a,b.Fc,c,d,e,g,h,k),b.rA);if(b instanceof FA)return b;if(b instanceof OA)return new OA(a,BX(a,b.Hi,c,d,e,g,h,k),b.NE);if(b instanceof ZB){$B(a);t();var qd=b.mc(),Yb=new L(qd);if(!Yb.b())return BX(a, Yb.k,c,d,e,g,h,k)}if(b instanceof mx){var Nc=b.hi;if(b.Tu>e&&b.Tu<=c)return BX(a,Nc,c,d,e,g,h,k)}if(b instanceof Mu||b instanceof nC||b instanceof mx||b instanceof YB)return b;if(b instanceof Tv){var ad=b.kf;return new Tv(a,BX(a,b.Ic,c,d,e,g,h,k),ad,b.vp)}if(b instanceof fw){var Uc=b.qb,cd=b.Zb;if(cd===u())var kc=u();else{for(var Vc=cd.e(),Hc=new z(BX(a,Vc,c,d,e,g,h,k),u()),rc=Hc,sd=cd.f();sd!==u();){var Kc=sd.e(),Qd=new z(BX(a,Kc,c,d,e,g,h,k),u());rc=rc.p=Qd;sd=sd.f()}kc=Hc}return new fw(a,Uc,kc, b.Xl)}if(b instanceof Qx&&(r=!0,v=b,v.Ea()<=e))return v;if(r){var Ad=v.de,kd=v.Re;if(d.da>Ad){var Hd=KC(v,d.da,g,d,h);return BX(a,Hd,c,d,e,g,h,k)}return new Qx(a,Ad,BX(a,kd,c{var wc=BX(a,ra.h(),c,d,e,g,h,k);ra=BX(a,ra.j(),c,d,e,g,h,k);return G(new H,wc,ra)};if(Rd===u())var dd=u();else{for(var od=Rd.e(),Ta=new z(ae(od),u()),wb=Ta,$a=Rd.f();$a!==u();){var wa=$a.e(),hb=new z(ae(wa),u());wb=wb.p=hb;$a=$a.f()}dd=Ta}return new eC(a,dd, BX(a,Bd,c,d,e,g,h,k))}if(b instanceof Jv)return VW(b,new y(ra=>BX(a,ra,c,d,e,g,h,k)),new y(ra=>BX(a,ra,c,d,e,g,h,k)));throw new w(b);};function CX(){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0;this.zk=this.Pq=this.Uq=this.xp=this.Bp=this.Cp=this.Sq=this.zp=this.Rq=this.wp=this.Ap=this.yp=this.Qq=this.Tq=null;this.Dp=0;this.hs=this.yq=this.Aq=this.Bq=this.zq=this.Dq=this.Cq=null;this.Im=this.Pw=0}CX.prototype=new HS;CX.prototype.constructor=CX; function DX(){}DX.prototype=CX.prototype;function Sw(a){null===a.hs&&null===a.hs&&(a.hs=new PN(a));return a.hs}function Mea(a,b,c,d,e){b=d.$a.Se(b,new U(()=>{no()}));if(!b.ct&&(e=Kx(b,e),e instanceof Yw)){e=e.Ei.U(c.x);if(e instanceof L)return a=e.k,t(),new Ud(a);if(t().d===e)return t(),a=wea(a,b,c),new fe(a);throw new w(e);}no()} function jX(a,b,c,d,e,g,h,k){var l=a.qa;if(a.F){var m=ut(Q(),"| ",a.r)+("Looking up field "+g.x+" in "+c+" \x26 "+e)+" \x26 {...}";ff(gf(),m+"\n")}a.r=1+a.r|0;try{var n=g.x;if(0<=n.length&&"#"===n.substring(0,1))var r=g.x,v=new vl(Of(Q(),r,1,r.length)),x=g.A(),A=Cq(v,x),B=!0;else A=g,B=!1;g=A;B=!!B;var C=d.n(g),D=t().d,F=new aw(D);if(c.b())var I=R();else{var M=c.o();I=EX(a,h.$a.n(M),g,B,k,F,d,h)}var N=Wn((Od(),Pd(u(),e)),new FX(a,h,g,B,k,F,d)),P=op().ga,T=zX(N,P),Y=C.ha(),Z=I.ha(),S=dl(dl(T,Z),Y); if(a.F){var ea=ut(Q(),"| ",a.r)+(" \x26 "+C)+" (from refinement)";ff(gf(),ea+"\n")}if(S instanceof z){for(var ia=S.z,X=Km(S.p);!X.b();){var sa=X.e();ia=Nv(sa,ia,V(sa.Va));X=X.f()}var Ja=ia}else{var Xa=O().c;if(null===Xa?null===S:Xa.i(S)){var Fa=F.rc;if(Fa instanceof L){var za=GX(a,Fa.k,k),Qa=V(a);Ja=lD(za,Qa)}else if(t().d===Fa){var Ma=new Te(new Ue(J(new K,["Type `","` does not contain member `","`"])));Xe();var Ga=Es(b),ab=[wO(0,AD(Ga,h)),We(Xe(),g.x)],Hb=Ye(Ma,J(new K,ab)),bc=g.A(),yb=G(new H, Hb,bc),tb=O().c,eb=ay(a,new z(yb,tb),k),kb=V(a);Ja=lD(eb,kb)}else throw new w(Fa);}else throw new w(S);}}finally{a.r=-1+a.r|0}dx(new E(l),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+l.n(Ja),ff(gf(),a+"\n"));return Ja}function Tw(a,b,c,d,e,g,h){var k=tc();try{a.Im=1+a.Im|0,HX(a,b,c,d,e,g,h)}catch(l){if(l instanceof Iq){if(a=l,a.Qg!==k)throw a;}else throw l;}} function HX(a,b,c,d,e,g,h){var k=tc();try{var l=jA().X(),m=a.Pw,n=new IQ(m),r=new IX(16);Nx(a,new U(()=>"CONSTRAIN "+b+" \x3c! "+c));Nx(a,new U(()=>{var B=new cv(a,b,c,V(a));return" where "+xx(B)}));var v=new U(()=>{throw new fX(k);}),x=O().c,A=O().c;bX(a,b,c,!0,d,G(new H,x,A),O().c,g,h,r,b,c,e,d,v,n,m,l,h)}catch(B){if(B instanceof Iq){if(d=B,d.Qg!==k)throw d;}else throw B;}} function Nea(a,b,c,d,e,g){var h=tc();try{Nx(a,new U(()=>"CHECKING SUBSUMPTION..."));var k=new IQ(0);Tw(a,b,c,new y(l=>{k.ve=1+k.ve|0;if(3this.uD.sh()||this.uD instanceof vo||this.uD instanceof $m);Qp();var b=Al().zH;if(bq(new cq(b.nG,nb(this.vD.tq))))b="."+this.vD.tq;else if(b=this.vD.tq,b=$D(aE(),b),b instanceof L)b="["+(b.k|0)+"]";else{if(t().d!==b)throw new w(b);b="["+Mp(Np(),this.vD.tq)+"]"}return Rp(a,Sp(0,b))};$p.prototype.$classData=q({OV:0},!1,"mlscript.JSField",{OV:1,lN:1,Vi:1,Xc:1,g:1});function gO(a,b){this.UD=a;this.TD=b}gO.prototype=new p;gO.prototype.constructor=gO; f=gO.prototype;f.xa=function(){for(var a=Rp(Rp(Sp(Qp(),"case "),this.UD.xa()),Sp(Qp(),": ")),b=this.TD,c=Qp().ye;!b.b();){var d=b.e();c=Iz(c,Kz(d.xa()));b=b.f()}return Iz(a,c)};f.H=function(){return"JSSwitchCase"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.UD;case 1:return this.TD;default:return $K(W(),a)}};f.D=function(a){return a instanceof gO};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof gO){var b=this.UD,c=a.UD;if(null===b?null===c:b.i(c))return b=this.TD,a=a.TD,null===b?null===a:b.i(a)}return!1};f.$classData=q({hW:0},!1,"mlscript.JSSwitchCase",{hW:1,g:1,E:1,v:1,l:1});function Kq(a,b,c){this.fh=a;this.eh=b;this.dh=c;up(tp(),0<=a);up(tp(),b>=a)}Kq.prototype=new p;Kq.prototype.constructor=Kq;function Sy(a,b){return Pe(new E(b.dh),a.dh)?b.fh>=a.fh&&b.eh<=a.eh:!1} function OX(a,b){return Pe(new E(b.dh),a.dh)?b.fh>=a.fh&&b.fh<=a.eh||b.eh<=a.eh&&b.eh>=a.fh:!1}function xs(a,b){At(tp(),Ot(new E(a.dh),b.dh));var c=a.fh,d=b.fh,e=a.eh;b=b.eh;return new Kq(cb?e:b,a.dh)}function Kt(a,b){if(b.b())return a;b=b.o();return xs(a,b)}function vs(a){return new Kq(a.fh,a.fh,a.dh)}f=Kq.prototype;f.H=function(){return"Loc"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.fh;case 1:return this.eh;case 2:return this.dh;default:return $K(W(),a)}}; f.D=function(a){return a instanceof Kq};f.B=function(){var a=lb("Loc");a=W().C(-889275714,a);var b=this.fh;a=W().C(a,b);b=this.eh;a=W().C(a,b);b=this.dh;b=My(W(),b);a=W().C(a,b);return W().Ma(a,3)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Kq&&this.fh===a.fh&&this.eh===a.eh){var b=this.dh;a=a.dh;return null===b?null===a:b.i(a)}return!1};f.$classData=q({vW:0},!1,"mlscript.Loc",{vW:1,g:1,E:1,v:1,l:1});function $q(a){this.fp=a}$q.prototype=new p; $q.prototype.constructor=$q;function UN(a,b){b=wf(vf(),Wn(a.fp,new vO(a)),b,"'");return Mf(a,b)}function Mf(a,b){var c=a.fp;a=h=>{if(h instanceof xO)return yf(h.ax,0,b);if(h instanceof Vq)return h.Jz;throw new w(h);};if(c===u())a=u();else{var d=c.e(),e=d=new z(a(d),u());for(c=c.f();c!==u();){var g=c.e();g=new z(a(g),u());e=e.p=g;c=c.f()}a=d}return ze(a,"","","")}f=$q.prototype; f.Wr=function(){var a=this.fp.m();a=new Ef(a,new y(b=>{if(b instanceof xO)return""+b.ax;if(b instanceof Vq)return b.Jz;throw new w(b);}));return ze(a,"","","")};function PX(a,b){return new $q(un(a.fp,b.fp))}f.H=function(){return"Message"};f.G=function(){return 1};f.I=function(a){return 0===a?this.fp:$K(W(),a)};f.D=function(a){return a instanceof $q};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof $q){var b=this.fp;a=a.fp;return null===b?null===a:b.i(a)}return!1};f.$classData=q({xW:0},!1,"mlscript.Message",{xW:1,g:1,E:1,v:1,l:1});function zO(a,b){this.vq=null;this.Nl=b;if(null===a)throw null;this.vq=a}zO.prototype=new p;zO.prototype.constructor=zO;function Lt(a,b){var c=a.Nl.U(b);b=a.Nl.xj(b);a=new zO(a.vq,b);return G(new H,c,a)} function Mt(a){a.Nl.og(new fn((b,c)=>{var d=a.vq,e=new Te(new Ue(J(new K,["Unrecognized modifier `","` in this position"])));b=[We(Xe(),b)];e=Ye(e,J(new K,b));t();c=G(new H,e,new L(c));e=O().c;Ze(d,new z(c,e))}))}f=zO.prototype;f.H=function(){return"ModifierSet"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Nl:$K(W(),a)};f.D=function(a){return a instanceof zO};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof zO&&a.vq===this.vq){var b=this.Nl;a=a.Nl;return null===b?null===a:b.i(a)}return!1};f.$classData=q({SW:0},!1,"mlscript.NewParser$ModifierSet",{SW:1,g:1,E:1,v:1,l:1});function DO(a,b){this.ix=null;this.wq=b;if(null===a)throw null;this.ix=a}DO.prototype=new p;DO.prototype.constructor=DO;function IO(a,b){for(b=b.wq;!b.b();){var c=b.e();a=new DO(a.ix,new z(c,a.wq));b=b.f()}return a} function HO(a,b,c,d,e){var g=a.ix;a=a.wq;for(var h=null,k=null;a!==u();){a:{var l=a.e(),m=b,n=c,r=d,v=e,x=tc();try{t();var A=l.Is,B=yw(l.Ls,m.Ls,n,r);if(B.b())throw Hq(new Iq,x,t().d);var C=B.o(),D=l.Ms.Ce(m.Ms),F=hw(l.Js,m.Js,n,r,v);if(F.b())throw Hq(new Iq,x,t().d);var I=F.o(),M=new CO(A,C,D,I,l.Ks.Ce(m.Ks));var N=new L(M)}catch(P){if(P instanceof Iq){N=P;if(N.Qg===x){N=N.Cj();break a}throw N;}throw P;}}for(x=N.m();x.s();)l=new z(x.t(),u()),null===k?h=l:k.p=l,k=l;a=a.f()}return new DO(g,null=== h?u():h)}f=DO.prototype;f.u=function(){return"CNF("+ze(this.wq,""," \x26 ","")+")"};f.H=function(){return"CNF"};f.G=function(){return 1};f.I=function(a){return 0===a?this.wq:$K(W(),a)};f.D=function(a){return a instanceof DO};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof DO&&a.ix===this.ix){var b=this.wq;a=a.wq;return null===b?null===a:b.i(a)}return!1};f.$classData=q({WW:0},!1,"mlscript.NormalForms$CNF",{WW:1,g:1,E:1,v:1,l:1}); function Oea(a){if(0===(2&a.Hs)<<24>>24){O();var b=new QX(J(new K,[a.Nf]));b=Kr(new Lr,b);b=new iy(b,new y(c=>Nm(new E(c),Vu(a.zb))),!1);b=kv(b,new U(()=>a.Pf)).nb(new U(()=>{O();var c=new QX(J(new K,[a.Of]));c=Kr(new Lr,c);c=new iy(c,new y(d=>Nm(new E(d),lw(a.zb))),!1);c=kv(c,new U(()=>a.Af));return new Ef(c,new y(d=>"~("+d+")"))}));a.NH=ze(b,"","\u2227","");a.Hs=(2|a.Hs)<<24>>24}return a.NH} function PO(a,b,c,d,e){this.yN=0;this.NH=null;this.Hs=0;this.zb=null;this.Nf=b;this.Pf=c;this.Of=d;this.Af=e;if(null===a)throw null;this.zb=a}PO.prototype=new p;PO.prototype.constructor=PO;function Pea(a,b){var c=oba(a.Nf,b.Nf);if(Nm(new E(c),0))return c;c=tba(a.Of,b.Of);if(Nm(new E(c),0))return c;c=-hv(a.Pf,b.Pf)|0;return Nm(new E(c),0)?c:c=-hv(a.Af,b.Af)|0}f=PO.prototype; f.zf=function(a){var b=new y(h=>h.zf(a));if(a){ms();ms();var c=this.Pf;if(0<=c.Q()){var d=c.Q();d=new (md(RX).Ia)(d);c.Gc(d,0,2147483647);c=d}else{d=null;d=[];for(c=c.m();c.s();){var e=c.t();d.push(null===e?null:e)}c=new (md(RX).Ia)(d)}d=Su();e=op().ga;c=Tu(c,new Uu(d,e));c=AG(c)}else c=this.Pf.m();c=c.nb(new U(()=>{O();var h=b.n(this.Of),k=V(h.q);h=[NA(h,k,!1)];h=J(new K,h);h=new QX(h);return Kr(new Lr,h)})).nb(new U(()=>{if(a){ms();ms();var h=this.Af;if(0<=h.Q()){var k=h.Q();k=new (md(RX).Ia)(k); h.Gc(k,0,2147483647);h=k}else{k=null;k=[];for(h=h.m();h.s();){var l=h.t();k.push(null===l?null:l)}h=new (md(RX).Ia)(k)}k=Su();l=op().ga;h=Tu(h,new Uu(k,l));h=AG(h)}else h=this.Af;return h.Ja(new y(m=>{var n=V(m.q);return NA(m,n,!1)}))}));for(d=this.Nf.zf(a);c.s();){e=c.t();var g=V(d.q);d=Pu(d,e,g,!1)}return d}; function fca(a,b,c){ms();ms();var d=a.Pf;if(0<=d.Q()){var e=d.Q();e=new (md(RX).Ia)(e);d.Gc(e,0,2147483647);d=e}else{e=null;e=[];for(d=d.m();d.s();){var g=d.t();e.push(null===g?null:g)}d=new (md(RX).Ia)(e)}e=Su();g=op().ga;d=Tu(d,new Uu(e,g));d=AG(d).nb(new U(()=>{O();var h=c.n(a.Of),k=V(h.q);h=[NA(h,k,!1)];h=J(new K,h);h=new QX(h);return Kr(new Lr,h)})).nb(new U(()=>{ms();ms();var h=a.Af;if(0<=h.Q()){var k=h.Q();k=new (md(RX).Ia)(k);h.Gc(k,0,2147483647);h=k}else{k=null;k=[];for(h=h.m();h.s();){var l= h.t();k.push(null===l?null:l)}h=new (md(RX).Ia)(k)}k=Su();l=op().ga;h=Tu(h,new Uu(k,l));return AG(h).Ja(new y(m=>{var n=V(m.q);return NA(m,n,!1)}))}));for(b=b.n(a.Nf);d.s();)e=d.t(),g=V(b.q),b=Pu(b,e,g,!1);return b}f.Ea=function(){0===(1&this.Hs)<<24>>24&&0===(1&this.Hs)<<24>>24&&(this.yN=this.ub(this.zb.Df,jA().X()),this.Hs=(1|this.Hs)<<24>>24);return this.yN}; f.ub=function(a,b){var c=this.Pf.m().nb(new U(()=>this.Af));c=new Ef(c,new y(e=>e.ub(a,b)));c=kv(c,new U(()=>{O();var e=[this.Nf.ub(a,b),this.Of.mc().ub(a,b)];e=J(new K,e);e=new QX(e);return Kr(new Lr,e)}));var d=Fq();return hH(c,d)|0}; function dX(a,b,c,d,e){var g=a.Pf;g=PE().vl(g);g=Pt(g,new y(v=>{v=v.Kc(b,c,d,e);if(v instanceof lx)return t(),new fe(v);if(v instanceof Gv)return t(),new Ud(v);xm("Program reached and unexpected state.")}));if(null===g)throw new w(g);var h=g.h(),k=g.j();g=a.Af;g=PE().vl(g);var l=Pt(g,new y(v=>{v=v.Kc(b,c,d,e);if(v instanceof lx)return t(),new fe(v);if(v instanceof Gv)return t(),new Ud(v);xm("Program reached and unexpected state.")}));if(null===l)throw new w(l);g=l.h();var m=l.j();l=a.zb;var n=qba(a.Nf, b,c,d,e);for(k=k.m();k.s();){var r=k.t();n=rba(n,r)}k=n;n=Su();r=op().ga;n=new Uu(n,r);h=oA(uv(),h,n);n=a.Of.HJ(b,c,d,e);for(a=m.m();a.s();)m=n,n=a.t(),n=vba(m,n);a=n;m=Su();n=op().ga;m=new Uu(m,n);return new PO(l,k,h,a,oA(uv(),g,m))}function SX(a,b,c){return nw(b.Pf,a.Pf)&&mw(a.Nf,b.Nf,c)&&b.Of.Dw(a.Of,c)?nw(b.Af,a.Af):!1} function Qea(a,b,c,d){var e=tc();try{if(SX(a,b,c))return t(),new L(b);if(SX(b,a,c))return t(),new L(a);var g=a.Nf,h=a.Pf,k=a.Of,l=a.Af;if(Vu(a.zb)===g&&null!==b){var m=b.Nf,n=b.Pf,r=b.Of,v=b.Af;if(Vu(a.zb)===m&&Pe(new E(h),n)&&Pe(new E(l),v)){t();var x=a.zb,A=Vu(a.zb),B=uba(k,r,c);if(B.b())throw Hq(new Iq,e,t().d);var C=new PO(x,A,h,B.o(),l);return new L(C)}}var D=a.Nf,F=a.Pf,I=a.Of,M=a.Af;if(D instanceof Ku){var N=D.fc,P=D.vd,T=D.be,Y=D.Me;if(null!==b){var Z=b.Nf,S=b.Pf,ea=b.Of,ia=b.Af;if(Z instanceof Ku){var X=Z.vd,sa=Z.be,Ja=Z.Me;if(Pe(new E(N),Z.fc)&&Pe(new E(P),X)&&Pe(new E(F),S)&&Pe(new E(I),ea)&&Pe(new E(M),ia))var Xa=Y.AB(),Fa=Pe(new E(Xa),Ja.AB());else Fa=!1;if(Fa){var za=Bw(a.zb,!0,Y,Ja,c),Qa=a.zb,Ma=RC(T.Ba,sa.Ba),Ga=new Qv(Qa,Ma,V(a.zb));t();var ab=new PO(a.zb,new Ku(a.zb,N,P,Ga,za),F,I,M);return new L(ab)}}}}var Hb=a.Nf,bc=a.Pf,yb=a.Of,tb=a.Af;if(Hb instanceof Ku){var eb=Hb.fc,kb=Hb.vd,Rb=Hb.be,Gb=Hb.Me;if(null!==b){var vb=b.Nf,Tb=b.Pf,Nb=b.Of,ic=b.Af;if(vb instanceof Ku){var Va=vb.fc, cb=vb.be,zb=vb.Me;if(Pe(new E(kb),vb.vd)&&Pe(new E(bc),Tb)&&Pe(new E(yb),Nb)&&Pe(new E(tb),ic)&&Pe(new E(Gb),zb)){var Ub=a.zb;if(Ev(d))var jb=Rb.Ba;else{if(eb.b())var db=R();else{var ub=eb.o();db=new L(ub.kq().Ba)}if(db.b())jb=Rb.Ba;else{var Aa=db.o();jb=Yv(Rb.Ba,Aa)}}if(Ev(d))var va=cb.Ba;else{if(Va.b())var Ra=R();else{var rb=Va.o();Ra=new L(rb.kq().Ba)}if(Ra.b())va=cb.Ba;else{var xb=Ra.o();va=Yv(cb.Ba,xb)}}var mc=RC(jb,va),Ha=new Qv(Ub,mc,V(a.zb));if(eb instanceof L){var Ka=eb.k;if(Ka instanceof cv){var Oa=Ka.Nb,Na=Ka.ac;if(Va instanceof L){var Da=Va.k;if(Da instanceof cv){var ta=Da.Nb,Ya=Da.ac;t();var dc=a.zb,ka=a.zb;t();var ya=a.zb,Sa=V(Oa.q),xc=Pu(Oa,ta,Sa,!1),Sb=V(Na.q),uc=dv(Na,Ya,Sb,!1),Lb=new cv(ya,xc,uc,V(a.zb)),lc=new PO(dc,new Ku(ka,new L(Lb),kb,Ha,Gb),bc,lw(a.zb),tb);return new L(lc)}}}}if(eb instanceof L){var Xb=eb.k;if(Xb instanceof zv){var ec=Xb.Yb;if(Va instanceof L){var Ab=Va.k;if(Ab instanceof zv){var Ob=Ab.Yb,fb=ec.K();if(Nm(new E(fb),Ob.K())){t();var Wa=a.zb,bb=a.zb;t(); var Ia=a.zb,Ua=Xb.Zj(),pc=Ab.Zj(),sc=Xb.Zj().Va,Ba=xw(Ua,pc,V(sc)),ob=new Sv(Ia,Ba,V(a.zb)),nc=new PO(Wa,new Ku(bb,new L(ob),kb,Ha,Gb),bc,lw(a.zb),tb);return new L(nc)}t();var Ib=a.zb,vc=a.zb;t();var Vb=a.zb,fc=TC(ec,Ob),Bc=new zv(Vb,fc,V(a.zb)),Pb=new PO(Ib,new Ku(vc,new L(Bc),kb,Ha,Gb),bc,lw(a.zb),tb);return new L(Pb)}}}}if(eb instanceof L){var Jb=eb.k;if(Jb instanceof zv&&Va instanceof L){var gc=Va.k;if(gc instanceof Sv){var Cb=gc.Fd;t();var cc=a.zb,yc=a.zb;t();var Mc=a.zb,qc=Jb.Zj(),oc=Jb.Zj().Va, Qc=xw(qc,Cb,V(oc)),jc=new Sv(Mc,Qc,V(a.zb)),sb=new PO(cc,new Ku(yc,new L(jc),kb,Ha,Gb),bc,lw(a.zb),tb);return new L(sb)}}}if(eb instanceof L){var Gc=eb.k;if(Gc instanceof Sv){var Wb=Gc.Fd;if(Va instanceof L){var Cc=Va.k;if(Cc instanceof zv){t();var Fc=a.zb,qd=a.zb;t();var Yb=a.zb,Nc=Cc.Zj(),ad=Cc.Zj().Va,Uc=xw(Nc,Wb,V(ad)),cd=new Sv(Yb,Uc,V(a.zb)),kc=new PO(Fc,new Ku(qd,new L(cd),kb,Ha,Gb),bc,lw(a.zb),tb);return new L(kc)}}}}if(eb instanceof L){var Vc=eb.k;if(Vc instanceof Sv){var Hc=Vc.Fd;if(Va instanceof L){var rc=Va.k;if(rc instanceof Sv){var sd=rc.Fd;t();var Kc=a.zb,Qd=a.zb;t();var Ad=a.zb,kd=xw(Hc,sd,V(Hc.Va)),Hd=new Sv(Ad,kd,V(a.zb)),Rd=new PO(Kc,new Ku(Qd,new L(Hd),kb,Ha,Gb),bc,lw(a.zb),tb);return new L(Rd)}}}}var Bd=t().d===eb&&t().d===Va?!0:eb instanceof L&&eb.k instanceof cv&&Va instanceof L&&Va.k instanceof Vv?!0:eb instanceof L&&eb.k instanceof Vv&&Va instanceof L&&Va.k instanceof cv?!0:!1;if(Bd){t();var ae=new PO(a.zb,new Ku(a.zb,t().d,kb,Ha,Gb),bc,lw(a.zb),tb);return new L(ae)}}}}}return t().d}catch(od){if(od instanceof Iq){var dd=od;if(dd.Qg===e)return dd.Cj();throw dd;}throw od;}}f.u=function(){return 0===(2&this.Hs)<<24>>24?Oea(this):this.NH};f.H=function(){return"Conjunct"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.Nf;case 1:return this.Pf;case 2:return this.Of;case 3:return this.Af;default:return $K(W(),a)}};f.D=function(a){return a instanceof PO};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof PO&&a.zb===this.zb){var b=this.Nf,c=a.Nf;(null===b?null===c:b.i(c))?(b=this.Pf,c=a.Pf,b=null===b?null===c:b.i(c)):b=!1;if(b&&(b=this.Of,c=a.Of,null===b?null===c:b.i(c)))return b=this.Af,a=a.Af,null===b?null===a:b.i(a)}return!1};f.$classData=q({YW:0},!1,"mlscript.NormalForms$Conjunct",{YW:1,g:1,E:1,v:1,l:1});function TX(a,b){var c=a.xe.m();c=new Ef(c,new y(e=>e.ub(b,jA().X())));var d=Fq();c=Jq(c,d);return(c.b()?a.Gj.Gd:c.o())|0} function UX(a,b,c){var d=Xu().X();if(VX(a)<=b.fb&&VX(b)<=a.fb){c=a.fb;var e=b.fb;return new WX(ca.fb){var g=new Iw(c.S,c.Ec,c.hc,c.Ed,1+b.fb|0,c.Pc,c.Zc,c.Lb,c.yc,c.tb,c.$a,c.od,c.cb);tp();c=a.fb;e=b.fb;up(0,Pe(new E(c>e?c:e),b.fb));c=a.fb;e=b.fb;var h=a.xe;if(h===u())var k=u();else{k=h.e();var l=k=new z(dX(k,a.fb,!1, g,d),u());for(h=h.f();h!==u();){var m=h.e();m=new z(dX(m,a.fb,!1,g,d),u());l=l.p=m;h=h.f()}}l=b.xe;lv();h=JF(lv(),a.Qf,new y(v=>v.Kc(a.fb,!1,g,d)));return new WX(c>e?c:e,k,l,ry(0,h,new y(v=>v.Kc(a.fb,!1,g,d))),b.Qf)}if(a.fb>b.fb){var n=new Iw(c.S,c.Ec,c.hc,c.Ed,1+a.fb|0,c.Pc,c.Zc,c.Lb,c.yc,c.tb,c.$a,c.od,c.cb);tp();c=a.fb;e=b.fb;up(0,Pe(new E(c>e?c:e),a.fb));c=a.fb;e=b.fb;k=a.xe;m=b.xe;if(m===u())l=u();else for(l=m.e(),h=l=new z(dX(l,b.fb,!1,n,d),u()),m=m.f();m!==u();){var r=m.e();r=new z(dX(r,b.fb, !1,n,d),u());h=h.p=r;m=m.f()}h=a.Qf;lv();b=JF(lv(),b.Qf,new y(v=>v.Kc(a.fb,!1,n,d)));return new WX(c>e?c:e,k,l,h,ry(0,b,new y(v=>v.Kc(a.fb,!1,n,d))))}tG||(tG=new sG);return cda(new WX(a.fb,a.xe,b.xe,a.Qf,b.Qf),Pe(new E(b.fb),a.fb))}function RO(a,b,c,d){this.Nm=this.zN=this.AN=this.BN=0;this.Gj=null;this.fb=b;this.Qf=c;this.xe=d;if(null===a)throw null;this.Gj=a;if(!(b<=a.Df))throw new Yj("assertion failed: "+this.fb);}RO.prototype=new p;RO.prototype.constructor=RO; function VX(a){0===(1&a.Nm)<<24>>24&&0===(1&a.Nm)<<24>>24&&(a.BN=TX(a,a.fb),a.Nm=(1|a.Nm)<<24>>24);return a.BN}f=RO.prototype; f.zf=function(a){var b=vx(this.Gj),c=this.Qf;if(this.xe.b())var d=this.Gj.ib;else{if(a){d=this.xe;var e=ZA($A(this.Gj)),g=qw(d,e)}else g=this.xe;d=zx(this.Gj);e=this.fb;if(g===u())a=u();else{var h=g.e(),k=h=new z(h.zf(a),u());for(g=g.f();g!==u();){var l=g.e();l=new z(l.zf(a),u());k=k.p=l;g=g.f()}a=h}for(h=this.Gj.ib;!a.b();)k=a.e(),g=V(h.q),h=dv(h,k,g,!1),a=a.f();d=yx(d,e,h)}return wx(b,c,d)}; f.Ea=function(){if(0===(2&this.Nm)<<24>>24&&0===(2&this.Nm)<<24>>24){var a=this.xe,b=Fq();if(a.b())b=R();else{if(a.b())throw nv("empty.maxBy");var c=null;var d=null;var e;for(e=!0;!a.b();){var g=a.e(),h=g.Ea();if(e||0>24}return this.AN};function qB(a){return a.Ea()>a.fb} function Aea(a,b){if(qB(a)){var c=Xu().X(),d=a.Qf,e=l=>{if(null!==l){var m=l.j();return G(new H,l.h().Kc(a.fb,!1,b,c),m.Kc(a.fb,!1,b,c))}throw new w(l);};if(d===u())e=u();else{var g=d.e(),h=g=new z(e(g),u());for(d=d.f();d!==u();){var k=d.e();k=new z(e(k),u());h=h.p=k;d=d.f()}e=g}d=a.xe;if(d===u())g=u();else for(g=d.e(),h=g=new z(dX(g,a.fb,!1,b,c),u()),d=d.f();d!==u();)k=d.e(),k=new z(dX(k,a.fb,!1,b,c),u()),h=h.p=k,d=d.f();return G(new H,e,g)}return G(new H,a.Qf,a.xe)} function eea(a,b,c,d,e){b=UX(a,b,d);if(null===b)throw new w(b);var g=b.hv|0,h=b.qt,k=b.kr,l=b.iv,m=b.jv;b=x=>{var A=new RO(a.Gj,g,dl(m,l),h),B=A.Gj,C=A.fb,D=A.Qf;A=A.xe;for(var F=null,I=null;A!==u();){a:{var M=A.e(),N=x,P=c,T=d,Y=e,Z=tc();try{if(M.Nf.Dw(N.Of,T))var S=t().d;else{t();var ea=$A(M.zb),ia=hw(M.Nf,N.Nf,P,T,Y);if(ia.b())throw Hq(new Iq,Z,t().d);var X=ia.o(),sa=M.Pf.Ce(N.Pf),Ja=yw(M.Of,N.Of,P,T);if(Ja.b())throw Hq(new Iq,Z,t().d);var Xa=Ja.o(),Fa=void 0,za=ea,Qa=X,Ma=sa,Ga=Xa,ab=M.Af.Ce(N.Af); M=P;var Hb=za.xq;if(Ga instanceof iw)var bc=new iw(za.xq,Ga.me,Ga.Ne);else if(Ga instanceof jw){var yb=Ga.mb,tb=Ga.Dc,eb=za.xq,kb=Ga.Xb;b:for(;;)if(kb.b()){Fa=u();break}else{var Rb=kb.e(),Gb=kb.f();if(!1===!yv(Qa,Rb,M,T,Y).b())kb=Gb;else for(za=kb,Ga=Gb;;){if(Ga.b())Fa=za;else{var vb=Ga.e();if(!1!==!yv(Qa,vb,M,T,Y).b()){Ga=Ga.f();continue}var Tb=new z(za.e(),u()),Nb=za.f();for(za=Tb;Nb!==Ga;){var ic=new z(Nb.e(),u());za=za.p=ic;Nb=Nb.f()}var Va=Ga.f();for(Ga=Va;!Va.b();){var cb=Va.e();if(!1===!yv(Qa, cb,M,T,Y).b()){for(;Ga!==Va;){var zb=new z(Ga.e(),u());za=za.p=zb;Ga=Ga.f()}Ga=Va.f()}Va=Va.f()}Ga.b()||(za.p=Ga);Fa=Tb}break b}}if(yb.b())var Ub=!0;else{var jb=yb.o();if(jb instanceof fe)Ub=!yv(Qa,jb.aa,M,T,Y).b();else{if(!(jb instanceof Ud))throw new w(jb);Ub=!0}}bc=new jw(eb,Fa,Ub?yb:R(),tb)}else if(lw(za.xq)===Ga)bc=lw(za.xq);else throw new w(Ga);var db=new PO(Hb,Qa,Ma,bc,ab);S=new L(db)}}catch(ub){if(ub instanceof Iq){S=ub;if(S.Qg===Z){S=S.Cj();break a}throw S;}throw ub;}}for(Z=S.m();Z.s();)Fa= new z(Z.t(),u()),null===I?F=Fa:I.p=Fa,I=Fa;A=A.f()}return new RO(B,C,D,null===F?u():F)};if(k===u())b=u();else{var n=k.e(),r=n=new z(b(n),u());for(k=k.f();k!==u();){var v=k.e();v=new z(b(v),u());r=r.p=v;k=k.f()}b=n}for(n=SO(xB(a.Gj),!1);!b.b();)r=b.e(),n=UO(n,r,d,e),b=b.f();return n} function UO(a,b,c,d){var e=UX(a,b,c);if(null===e)throw new w(e);var g=e.qt;b=e.kr;e=new RO(a.Gj,e.hv|0,dl(e.jv,e.iv),g);for(a=b;!a.b();){var h=e,k=a.e(),l=c,m=d;b=h.Gj;e=h.fb;g=h.Qf;a:{var n=h.xe;for(h=O().c;;)if(n instanceof z){var r=n.z;n=n.p;if(SX(r,k,l)){h=Km(h);h=dl(new z(k,n),h);break a}if(SX(k,r,l)){h=Km(h);h=dl(new z(r,n),h);break a}var v=Qea(r,k,l,m);if(v instanceof L){k=v.k;h=Km(h);h=dl(new z(k,n),h);break a}if(R()===v)h=new z(r,h);else throw new w(v);}else{l=O().c;if(null===l?null===n: l.i(n)){h=Km(new z(k,h));break a}throw new w(n);}}e=new RO(b,e,g,h);a=a.f()}return e}f.u=function(){var a=this.fb,b=ze(this.xe,""," | ",""),c=this.Qf.b()?"":"{"+ze(this.Qf,"",", ","")+"}";return"DNF("+a+", "+b+")"+c};f.H=function(){return"DNF"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.fb;case 1:return this.Qf;case 2:return this.xe;default:return $K(W(),a)}};f.D=function(a){return a instanceof RO}; f.B=function(){var a=lb("DNF");a=W().C(-889275714,a);var b=this.fb;a=W().C(a,b);b=this.Qf;b=My(W(),b);a=W().C(a,b);b=this.xe;b=My(W(),b);a=W().C(a,b);return W().Ma(a,3)};f.i=function(a){if(this===a)return!0;if(a instanceof RO&&a.Gj===this.Gj){if(this.fb===a.fb){var b=this.Qf,c=a.Qf;b=null===b?null===c:b.i(c)}else b=!1;if(b)return b=this.xe,a=a.xe,null===b?null===a:b.i(a)}return!1};f.$classData=q({aX:0},!1,"mlscript.NormalForms$DNF",{aX:1,g:1,E:1,v:1,l:1}); function CO(a,b,c,d,e){this.Is=null;this.Ls=b;this.Ms=c;this.Js=d;this.Ks=e;if(null===a)throw null;this.Is=a}CO.prototype=new p;CO.prototype.constructor=CO;f=CO.prototype; f.u=function(){O();var a=new QX(J(new K,[this.Ls]));a=Kr(new Lr,a);a=new iy(a,new y(b=>Nm(new E(b),lw(this.Is))),!1);a=kv(a,new U(()=>this.Ms)).nb(new U(()=>{O();var b=new QX(J(new K,[this.Js]));b=Kr(new Lr,b);b=new iy(b,new y(c=>Nm(new E(c),Vu(this.Is))),!1);b=kv(b,new U(()=>this.Ks));return new Ef(b,new y(c=>"~"+c))}));return ze(a,"","\u2228","")};f.H=function(){return"Disjunct"};f.G=function(){return 4}; f.I=function(a){switch(a){case 0:return this.Ls;case 1:return this.Ms;case 2:return this.Js;case 3:return this.Ks;default:return $K(W(),a)}};f.D=function(a){return a instanceof CO};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof CO&&a.Is===this.Is){var b=this.Ls,c=a.Ls;(null===b?null===c:b.i(c))?(b=this.Ms,c=a.Ms,b=null===b?null===c:b.i(c)):b=!1;if(b&&(b=this.Js,c=a.Js,null===b?null===c:b.i(c)))return b=this.Ks,a=a.Ks,null===b?null===a:b.i(a)}return!1}; f.$classData=q({cX:0},!1,"mlscript.NormalForms$Disjunct",{cX:1,g:1,E:1,v:1,l:1});function az(a,b,c,d,e,g,h,k){this.Sz=null;this.Eq=b;this.Os=c;this.Hu=d;this.Gu=e;this.Qs=g;this.Ps=h;this.Rm=k;if(null===a)throw null;this.Sz=a}az.prototype=new p;az.prototype.constructor=az;f=az.prototype;f.H=function(){return"Pack"};f.G=function(){return 7}; f.I=function(a){switch(a){case 0:return this.Eq;case 1:return this.Os;case 2:return this.Hu;case 3:return this.Gu;case 4:return this.Qs;case 5:return this.Ps;case 6:return this.Rm;default:return $K(W(),a)}};f.D=function(a){return a instanceof az};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof az){var b=this.Eq,c=a.Eq;(null===b?null===c:mC(b,c))?(b=this.Os,c=a.Os,b=null===b?null===c:b.i(c)):b=!1;b?(b=this.Hu,c=a.Hu,(null===b?null===c:b.i(c))?(b=this.Gu,c=a.Gu,b=null===b?null===c:b.i(c)):b=!1):b=!1;if(b&&(b=this.Qs,c=a.Qs,(null===b?null===c:b.i(c))?(b=this.Ps,c=a.Ps,b=null===b?null===c:b.i(c)):b=!1,b))return b=this.Rm,a=a.Rm,null===b?null===a:mC(b,a)}return!1}; f.$classData=q({uX:0},!1,"mlscript.NuTypeDefs$DelayedTypeInfoImpl$Pack$1",{uX:1,g:1,E:1,v:1,l:1});function Xy(a){this.GN=null;if(null===a)throw null;this.GN=a}Xy.prototype=new mS;Xy.prototype.constructor=Xy;Xy.prototype.u=function(){return"Pack"};function Nba(a,b,c,d,e,g,h,k){return new az(a.GN,b,c,d,e,g,h,k)}Xy.prototype.$classData=q({vX:0},!1,"mlscript.NuTypeDefs$DelayedTypeInfoImpl$Pack$2$",{vX:1,aca:1,g:1,raa:1,l:1}); function XX(a,b,c){this.PH=null;this.uE=b;this.Uz=c;if(null===a)throw null;this.PH=a}XX.prototype=new p;XX.prototype.constructor=XX;f=XX.prototype;f.H=function(){return"RefMap"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.uE;case 1:return this.Uz;default:return $K(W(),a)}};f.D=function(a){return a instanceof XX};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof XX&&a.PH===this.PH){var b=this.uE,c=a.uE;if(null===b?null===c:b.i(c))return b=this.Uz,a=a.Uz,null===b?null===a:b.i(a)}return!1};f.$classData=q({xX:0},!1,"mlscript.NuTypeDefs$RefMap",{xX:1,g:1,E:1,v:1,l:1});function Ge(a,b,c){this.Wz=a;this.rx=b;this.Us=c}Ge.prototype=new p;Ge.prototype.constructor=Ge;f=Ge.prototype;f.u=function(){return this.Wz+":+"+this.rx};f.H=function(){return"Origin"};f.G=function(){return 3}; f.I=function(a){switch(a){case 0:return this.Wz;case 1:return this.rx;case 2:return this.Us;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ge};f.B=function(){var a=lb("Origin");a=W().C(-889275714,a);var b=this.Wz;b=My(W(),b);a=W().C(a,b);b=this.rx;a=W().C(a,b);b=this.Us;b=My(W(),b);a=W().C(a,b);return W().Ma(a,3)};f.i=function(a){return this===a?!0:a instanceof Ge?this.rx===a.rx?this.Wz===a.Wz?this.Us===a.Us:!1:!1:!1};f.$classData=q({LX:0},!1,"mlscript.Origin",{LX:1,g:1,E:1,v:1,l:1}); function aP(a,b,c,d,e){this.QN=null;this.bI=!1;this.kp=a;this.$z=b;this.xx=c;this.fi=d;this.Jq=e}aP.prototype=new p;aP.prototype.constructor=aP;function yP(a){a.bI||a.bI||(a.QN=ut(Q()," ",a.xx),a.bI=!0);return a.QN}function ON(a){return"\n"+yP(a)}function AP(a){return new aP(a.kp,a.$z,1+a.xx|0,a.fi,a.Jq)}f=aP.prototype;f.H=function(){return"ShowCtx"};f.G=function(){return 5}; f.I=function(a){switch(a){case 0:return this.kp;case 1:return this.$z;case 2:return this.xx;case 3:return this.fi;case 4:return this.Jq;default:return $K(W(),a)}};f.D=function(a){return a instanceof aP};f.B=function(){var a=lb("ShowCtx");a=W().C(-889275714,a);var b=this.kp;b=My(W(),b);a=W().C(a,b);b=this.$z?1231:1237;a=W().C(a,b);b=this.xx;a=W().C(a,b);b=this.fi?1231:1237;a=W().C(a,b);b=this.Jq?1231:1237;a=W().C(a,b);return W().Ma(a,5)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof aP&&this.$z===a.$z&&this.xx===a.xx&&this.fi===a.fi&&this.Jq===a.Jq){var b=this.kp;a=a.kp;return null===b?null===a:b.i(a)}return!1};f.$classData=q({fY:0},!1,"mlscript.ShowCtx",{fY:1,g:1,E:1,v:1,l:1}); function YX(a,b,c,d,e,g,h,k,l,m,n){this.FE=this.Nu=this.UN=this.VN=null;this.jj=b;this.Wl=c;this.Xm=d;this.Cx=e;this.hI=g;this.iI=h;this.Bx=k;this.GE=l;this.at=m;this.gI=n;if(null===a)throw null;this.FE=a;a=op();d=d.Gb(a.ga);a:{if(null!==d&&(a=d.h(),b=d.j(),null!==a&&null!==b)){d=G(new H,a,b);break a}throw new w(d);}this.VN=d;this.UN=this.VN.j();this.Nu=t().d}YX.prototype=new p;YX.prototype.constructor=YX; function ZX(a,b,c){var d=a.Bx.Ja(new y(e=>new Ep(e.V)));a=a.Bx.m();a=new iy(a,c,!0);a=new xo(a,new y(e=>{var g=b.tb.U(e.V);return g.b()?ap():ZX(g.o(),b,c.bc(e))}));return d.Ce(a)}function dB(a){a=a.Nu;if(a.b()){a=nf();var b=ou().Yl;a=$X(a,b)}else a=a.o();return a}f=YX.prototype;f.H=function(){return"TypeDef"};f.G=function(){return 10}; f.I=function(a){switch(a){case 0:return this.jj;case 1:return this.Wl;case 2:return this.Xm;case 3:return this.Cx;case 4:return this.hI;case 5:return this.iI;case 6:return this.Bx;case 7:return this.GE;case 8:return this.at;case 9:return this.gI;default:return $K(W(),a)}};f.D=function(a){return a instanceof YX};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof YX&&a.FE===this.FE){if(this.jj===a.jj){var b=this.Wl,c=a.Wl;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.Xm,c=a.Xm,(null===b?null===c:b.i(c))?(b=this.Cx,c=a.Cx,(null===b?null===c:mC(b,c))?(b=this.hI,c=a.hI,b=null===b?null===c:b.i(c)):b=!1):b=!1):b=!1;if(b&&(b=this.iI,c=a.iI,(null===b?null===c:b.i(c))?(b=this.Bx,c=a.Bx,(null===b?null===c:b.i(c))?(b=this.GE,c=a.GE,b=null===b?null===c:b.i(c)):b=!1):b=!1,b&&(b=this.at,c=a.at,null===b?null===c:b.i(c))))return b= this.gI,a=a.gI,null===b?null===a:b.i(a)}return!1};f.$classData=q({vY:0},!1,"mlscript.TypeDefs$TypeDef",{vY:1,g:1,E:1,v:1,l:1});function aY(a){if(null===a)throw null;}aY.prototype=new eS;aY.prototype.constructor=aY;aY.prototype.u=function(){return"TypeDef"};aY.prototype.$classData=q({wY:0},!1,"mlscript.TypeDefs$TypeDef$",{wY:1,Yba:1,g:1,oaa:1,l:1});function bY(){}bY.prototype=new cS;bY.prototype.constructor=bY;bY.prototype.u=function(){return"TypeName"};bY.prototype.n=function(a){return new Ep(a)}; bY.prototype.$classData=q({DY:0},!1,"mlscript.TypeName$",{DY:1,dM:1,g:1,la:1,l:1}); var cY,Sea=function Rea(a,b){var d=a.Ed.U(b);return d.b()?(a=(new Dx(a.Ec)).wF,a.b()?b=t().d:(a=a.o(),b=Rea(a,b)),b):d},uC=function dY(a,b,c){for(;;){if(Pe(new E(a.Lb),b.Lb)){var e=b.hc.U(c);if(e.b()){e=b.Ec;if(e.b())return R();e=e.o();return dY(a,e,c)}return e}if(a.Lb)if(e=b.Ec,e instanceof L)b=e.k;else return b.hc.U(c);else{e=b.hc.U(c);if(e instanceof L&&(e=e.k,e instanceof qx)){e=e.tp;var g=eY(b,c);if(g instanceof L){b=g.k;t();var h=g=a.S,k=new Ep("Var"),l=O().c;a=new qx(g,new fw(h,k,new z(e,new z(b, l)),V(a.S)),new vl(c));return new L(a)}e=b.Ec;if(e.b())return R();e=e.o();return dY(a,e,c)}e=b.Ec;if(e.b())return R();e=e.o();return dY(a,e,c)}}};function Iw(a,b,c,d,e,g,h,k,l,m,n,r,v){this.S=this.$N=null;this.Ec=b;this.hc=c;this.Ed=d;this.da=e;this.Pc=g;this.Zc=h;this.Lb=k;this.yc=l;this.tb=m;this.$a=n;this.od=r;this.cb=v;if(null===a)throw null;this.S=a;this.$N=Xu().X()}Iw.prototype=new p;Iw.prototype.constructor=Iw; function Wx(a,b){a.hc.$(b);if(a.Lb){var c=a.S,d=a.S,e=V(a.S),g=t().d;t();var h=new L(b.h()),k=O().c,l=O().c;c=new mx(c,new lx(d,a.da,k,l,g,h,!1,e),V(a.S));d=a.S;d.F&&(d=ut(Q(),"| ",d.r)+("Create skolem tag "+c+" for "+b.j())+" in quasiquote.",ff(gf(),d+"\n"));a=a.Pc;b=G(new H,b.h(),c);a.$(b)}}function Jw(a,b){for(b=b.m();b.s();){var c=b.t();Wx(a,c)}}function eY(a,b){var c=a.Pc.U(b);return c.b()?(a=(new Dx(a.Ec)).wF,a.b()?t().d:eY(a.o(),b)):c} function Tea(a,b){for(a=a.Pc.m();a.s();){var c=a.t(),d=c;c=d.j();d=V(d.j().q);b=dv(c,b,d,!1)}return b}function fY(a){var b=a.S.ib;for(a=new qA(a.Zc);a.s();){var c=a.t(),d=V(b.q);b=dv(b,c,d,!1)}return b}function gY(a,b){var c=a.S;c.F&&(c=ut(Q(),"| ",c.r)+"Capture free variable type "+b,ff(gf(),c+"\n"));FB(a.Zc,b)}function hY(a,b,c){t();b=G(new H,b,c);return Sea(a,new Ud(b))} function tf(a){up(tp(),!a.Lb);var b=new L(a),c=Xu().X(),d=Xu().X();return new Iw(a.S,b,c,d,a.da,a.Pc,a.Zc,a.Lb,a.yc,a.tb,a.$a,a.od,a.cb)}function iY(a){var b=new L(a),c=Xu().X(),d=Xu().X(),e=1+a.da|0,g=Xu().X(),h=new MB;return new Iw(a.S,b,c,d,e,g,h,!0,a.yc,a.tb,a.$a,a.od,a.cb)}function Uea(a){var b=new L(a),c=Xu().X(),d=Xu().X();return new Iw(a.S,b,c,d,a.da,a.Pc,a.Zc,!1,a.yc,a.tb,a.$a,a.od,a.cb)} function Sx(a,b,c,d){var e=1+a.da|0,g=Hw(),h=Su(),k=op().ga;g=g.Hd(new Uu(h,k));g=new Iw(a.S,a.Ec,a.hc,a.Ed,e,a.Pc,a.Zc,a.Lb,a.yc,a.tb,a.$a,a.od,g);b=b.n(g);e=g.cb;up(tp(),a.S.li||g.cb.b());if(!e.b()){g=a.S.qa;h=a.S;h.F&&(k=ut(Q(),"| ",h.r)+"UNSTASHING... (out)",ff(gf(),k+"\n"));h.r=1+h.r|0;try{e.Ca(new y(m=>{if(null!==m){var n=m.h();for(m=m.j().m();m.s();){var r=m.t();a:{if(null!==r){var v=r.j();if(!0===r.Rc()){r=Sw(a.S).ob;Tw(a.S,v,n,c,d,a,r);break a}}if(null!==r&&(v=r.j(),!1===r.Rc())){r=Sw(a.S).ob; Tw(a.S,n,v,c,d,a,r);break a}throw new w(r);}}}else throw new w(m);}));e.mg();var l=void 0}finally{h.r=-1+h.r|0}dx(new E(g),h.qa)&&h.F&&(l=""+ut(Q(),"| ",h.r)+g.n(l),ff(gf(),l+"\n"))}return b} function Px(a,b,c,d){var e=1+a.da|0,g=Hw(),h=Su(),k=op().ga;g=g.Hd(new Uu(h,k));e=new Iw(a.S,a.Ec,a.hc,a.Ed,e,a.Pc,a.Zc,a.Lb,a.yc,a.tb,a.$a,a.od,g);b=b.n(e);up(tp(),a.S.li||e.cb.b());Xu().X();g=vx(a.S);h=e.cb.m();h=new xo(h,new y(m=>{if(null!==m){var n=m.h();m=m.j().m();return new Ef(m,new y(r=>{if(null!==r){var v=r.Rc();r=r.j();up(tp(),r.Ea()>a.da);return v?G(new H,r,n):G(new H,n,r)}throw new w(r);}))}throw new w(m);}));Od();b=wx(g,Pd(u(),h),b);g=a.S;g.F&&(g=ut(Q(),"| ",g.r)+("Inferred poly constr: "+ b+" \u2014\u2014 where ")+xx(b),ff(gf(),g+"\n"));a.S.F&&Nm(new E(b),b)&&(g=a.S,g.F&&(g=ut(Q(),"| ",g.r)+("Refreshed: "+b+" \u2014\u2014 where ")+xx(b),ff(gf(),g+"\n")));b=yx(zx(a.S),a.da,b);e.cb.mg();g=e.cb;up(tp(),a.S.li||e.cb.b());if(!g.b()){e=a.S.qa;h=a.S;h.F&&(k=ut(Q(),"| ",h.r)+"UNSTASHING... (out)",ff(gf(),k+"\n"));h.r=1+h.r|0;try{g.Ca(new y(m=>{if(null!==m){var n=m.h();for(m=m.j().m();m.s();){var r=m.t();a:{if(null!==r){var v=r.j();if(!0===r.Rc()){r=Sw(a.S).ob;Tw(a.S,v,n,c,d,a, r);break a}}if(null!==r&&(v=r.j(),!1===r.Rc())){r=Sw(a.S).ob;Tw(a.S,n,v,c,d,a,r);break a}throw new w(r);}}}else throw new w(m);}));g.mg();var l=void 0}finally{h.r=-1+h.r|0}dx(new E(e),h.qa)&&h.F&&(l=""+ut(Q(),"| ",h.r)+e.n(l),ff(gf(),l+"\n"))}return b}function jY(a,b){return a.$N.Hk(b,new U(()=>{var c=a.tb.U(b);return c.b()?ap():ZX(c.o(),a,ap())}))}f=Iw.prototype;f.H=function(){return"Ctx"};f.G=function(){return 12}; f.I=function(a){switch(a){case 0:return this.Ec;case 1:return this.hc;case 2:return this.Ed;case 3:return this.da;case 4:return this.Pc;case 5:return this.Zc;case 6:return this.Lb;case 7:return this.yc;case 8:return this.tb;case 9:return this.$a;case 10:return this.od;case 11:return this.cb;default:return $K(W(),a)}};f.D=function(a){return a instanceof Iw}; f.B=function(){var a=lb("Ctx");a=W().C(-889275714,a);var b=this.Ec;b=My(W(),b);a=W().C(a,b);b=this.hc;b=My(W(),b);a=W().C(a,b);b=this.Ed;b=My(W(),b);a=W().C(a,b);b=this.da;a=W().C(a,b);b=this.Pc;b=My(W(),b);a=W().C(a,b);b=this.Zc;b=My(W(),b);a=W().C(a,b);b=this.Lb?1231:1237;a=W().C(a,b);b=this.yc?1231:1237;a=W().C(a,b);b=this.tb;b=My(W(),b);a=W().C(a,b);b=this.$a;b=My(W(),b);a=W().C(a,b);b=this.od;b=My(W(),b);a=W().C(a,b);b=this.cb;b=My(W(),b);a=W().C(a,b);return W().Ma(a,12)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Iw&&a.S===this.S){if(this.da===a.da&&this.Lb===a.Lb&&this.yc===a.yc){var b=this.Ec,c=a.Ec;(null===b?null===c:b.i(c))?(b=this.hc,c=a.hc,(null===b?null===c:b.i(c))?(b=this.Ed,c=a.Ed,b=null===b?null===c:b.i(c)):b=!1):b=!1}else b=!1;if(b&&(b=this.Pc,c=a.Pc,(null===b?null===c:b.i(c))?(b=this.Zc,c=a.Zc,(null===b?null===c:kY(b,c))?(b=this.tb,c=a.tb,b=null===b?null===c:b.i(c)):b=!1):b=!1,b&&(b=this.$a,c=a.$a,(null===b?null===c:b.i(c))?(b=this.od,c=a.od, b=null===b?null===c:b.i(c)):b=!1,b)))return b=this.cb,a=a.cb,null===b?null===a:lY(b,a)}return!1};f.$classData=q({OY:0},!1,"mlscript.Typer$Ctx",{OY:1,g:1,E:1,v:1,l:1}); function fx(a,b,c,d,e){this.J=null;this.ct=!1;this.eo=null;this.Ru=0;this.uO=this.dt=this.jg=this.sO=this.Qu=null;this.LE=!1;this.AO=null;this.wI=!1;this.yO=this.iO=this.vO=this.lO=this.BO=this.kO=this.CO=this.qO=this.tO=this.zO=this.pO=this.jO=this.xO=this.wO=this.mO=null;this.nO=!1;this.oO=this.rO=null;this.Ha=0;this.Ab=b;this.fZ=c;this.cd=d;this.ne=e;FC(this,a);this.ct=!1;this.eo=t().d;this.Ru=this.cd.da;this.Qu=this.Ab.fd();this.sO=this.Ab.Cf.x;a=this.J;b=this.Ab.A();this.jg=jx(new kx,a,b,cy(this.Ab), (tx(this.J),t().d),(tx(this.J),!1));this.dt=nf();a=this.J;a.F&&(a=ut(Q(),"| ",a.r)+(this.cd.da+". Created lazy type info for ")+this.Ab,ff(gf(),a+"\n"));this.wI=this.LE=!1}fx.prototype=new WP;fx.prototype.constructor=fx;f=fx.prototype;f.u=function(){var a=this.Ab.Cf.x;if(this.ct)var b="\x3ccomputing\x3e";else b=this.eo,b=b.b()?"\x3cuncomputed\x3e":b.o().u();return a+" ~\x3e "+b};f.fd=function(){return this.Qu}; function Mw(a){if(0===(1&a.Ha)&&0===(1&a.Ha)){var b=a.Ab;if(b instanceof yo){b=b.Di;for(var c=null,d=null;b!==u();){var e=b.e(),g=!1,h=null;b:if(e instanceof vl)h=e,t(),h=new Gp(h,h,O().c,O().c),h=new L(h);else{if(e instanceof Pl){g=!0;h=e;var k=h.Za,l=h.Qb;if(k instanceof vl&&l instanceof Gl){e=l.Ra;t();h=new Gp(h,k,O().c,e);h=new L(h);break b}}if(e instanceof Il&&(k=e,l=k.nl,k=k.np,l instanceof vl)){h=l;t();h=new Gp(h,h,k,O().c);h=new L(h);break b}if(g&&(l=h.Za,g=h.Qb,l instanceof Il&&(k=l.nl,l= l.np,k instanceof vl&&g instanceof Gl))){e=g.Ra;t();h=new L(new Gp(h,k,l,e));break b}Lw(a.J,Ye(new Te(new Ue(J(new K,["Unsupported parent specification"]))),u()),e.A(),a.ne);h=t().d}for(h=h.m();h.s();)e=new z(h.t(),u()),null===d?c=e:d.p=e,d=e;b=b.f()}b=null===c?u():c;for(d=c=null;b!==u();){k=b.e();b:{if(null!==k&&(h=k.Uj,e=k.oj,g=k.oi,l=k.Xi,null!==e)){k=e.x;var m=!1,n=a.cd;n=uC(n,n,k);if(n instanceof L){m=!0;var r=n;r=r.k;if(r instanceof VP){k=r;t();h=new L(new WX(h,e,k,g,l));break b}}if(m){Lw(a.J, Ye(new Te(new Ue(J(new K,["Cannot inherit from this"]))),u()),h.A(),a.ne);h=t().d;break b}if(t().d===n){e=a.J;g=new Te(new Ue(J(new K,["Could not find definition `","`"])));l=[We(Xe(),k)];Lw(e,Ye(g,J(new K,l)),h.A(),a.ne);h=t().d;break b}throw new w(n);}throw new w(k);}for(h=h.m();h.s();)e=new z(h.t(),u()),null===d?c=e:d.p=e,d=e;b=b.f()}b=null===c?u():c}else b=O().c;a.uO=b;a.Ha|=1}return a.uO}function Hx(a){0===(2&a.Ha)&&0===(2&a.Ha)&&(a.AO=wba(a),a.Ha|=2);return a.AO} function gx(a){if(0===(4&a.Ha)&&0===(4&a.Ha)){if(a.wI)var b=ap();else{a.wI=!0;b=Mw(a);var c=ap();b=xba(a,b,c)}a.mO=b;a.Ha|=4}return a.mO}f.Ag=function(){0===(8&this.Ha)&&0===(8&this.Ha)&&(this.wO=yba(this),this.Ha|=8);return this.wO};function Tx(a){0===(16&a.Ha)&&0===(16&a.Ha)&&(a.xO=zba(a),a.Ha|=16);return a.xO}function VD(a){0===(32&a.Ha)&&0===(32&a.Ha)&&(a.jO=Aba(a),a.Ha|=32);return a.jO}function Ow(a){0===(64&a.Ha)&&0===(64&a.Ha)&&(a.pO=a.fZ.bf(Tx(a)),a.Ha|=64);return a.pO} function px(a){0===(128&a.Ha)&&0===(128&a.Ha)&&(a.zO=Bba(a),a.Ha|=128);return a.zO}function Kw(a){0===(256&a.Ha)&&0===(256&a.Ha)&&(a.tO=Cba(a),a.Ha|=256);return a.tO}function mY(a){0===(512&a.Ha)&&0===(512&a.Ha)&&(a.qO=Dba(a),a.Ha|=512);return a.qO}function Ax(a){0===(1024&a.Ha)&&0===(1024&a.Ha)&&(a.CO=mY(a).h(),a.Ha|=1024);return a.CO}function Fba(a){0===(2048&a.Ha)&&0===(2048&a.Ha)&&(a.kO=mY(a).j(),a.Ha|=2048);return a.kO} function Zx(a){0===(4096&a.Ha)&&0===(4096&a.Ha)&&(a.BO=Eba(a),a.Ha|=4096);return a.BO}function Gx(a){0===(8192&a.Ha)&&0===(8192&a.Ha)&&(a.lO=Gba(a),a.Ha|=8192);return a.lO}function fy(a){0===(16384&a.Ha)&&0===(16384&a.Ha)&&(a.vO=Hba(a),a.Ha|=16384);return a.vO}function Bx(a){0===(32768&a.Ha)&&0===(32768&a.Ha)&&(a.iO=Iba(a),a.Ha|=32768);return a.iO}function Rx(a){0===(131072&a.Ha)&&0===(131072&a.Ha)&&(a.nO=Kba(a),a.Ha|=131072);return a.nO} function Ux(a){if(0===(262144&a.Ha)&&0===(262144&a.Ha)){var b;if(a.Ab instanceof Zn){var c=b=a.J,d=a.Ab.A(),e=cy(a.Ab);t();c=jx(new kx,c,d,e,new L(a.Ab.Cf.x),a.Ab instanceof yo);d=t().d;t();e=new L(a.Ab.Cf.x);var g=O().c,h=O().c,k=Rx(a)?1+a.Ru|0:a.Ru;b=new lx(b,k,g,h,d,e,!1,c)}else xm("Not supposed to use mutRecTV for "+a.Ab.fd()),b=void 0;a.rO=b;a.Ha|=262144}return a.rO} function Vx(a){if(0===(524288&a.Ha)&&0===(524288&a.Ha)){var b=a.J;var c=V(a.J),d=t().d;t();var e=UF(ve(),a.Ab.Cf.x);e=new L(e);var g=O().c,h=O().c;b=new lx(b,1+a.cd.da|0,g,h,d,e,!1,c);a.oO=b;a.Ha|=524288}return a.oO}f.fQ=function(a){return Kx(this,a)};f.$classData=q({dZ:0},!1,"mlscript.TyperDatatypes$DelayedTypeInfo",{dZ:1,nZ:1,UO:1,g:1,Eaa:1});function Uw(a,b,c,d){this.Va=null;this.Oa=b;this.ra=c;this.Pd=d;if(null===a)throw null;this.Va=a}Uw.prototype=new p;Uw.prototype.constructor=Uw;f=Uw.prototype; f.Ea=function(){var a=this.Oa;a.b()?a=R():(a=a.o(),a=new L(a.Ea()));a=(a.b()?this.ra.Ea():a.o())|0;var b=this.ra.Ea();return a>b?a:b};f.ub=function(a,b){var c=this.Oa;c=c.b()?this.Va.Gd:c.o().ub(a,b);a=this.ra.ub(a,b);return c>a?c:a};function Fw(a,b,c,d){var e=b.Oa;e=e.b()?a.Va.ib:e.o();var g=a.Oa;g=g.b()?a.Va.ib:g.o();return Zu(e,g,c,!0,d)?Zu(a.ra,b.ra,c,!0,d):!1} function Nv(a,b,c){var d=a.Va,e=a.Oa;if(e.b())e=b.Oa;else{e=e.o();var g=b.Oa;if(!g.b()){g=g.o();var h=V(e.q);e=dv(e,g,h,!1)}e=new L(e)}g=a.ra;b=b.ra;a=V(a.ra.q);return new Uw(d,e,Pu(g,b,a,!1),c)}function xw(a,b,c){var d=a.Va,e=a.Oa;if(e.b())e=R();else{e=e.o();var g=b.Oa;if(g.b())e=R();else{g=g.o();var h=V(e.q);e=new L(Pu(e,g,h,!1))}}g=a.ra;b=b.ra;a=V(a.ra.q);return new Uw(d,e,dv(g,b,a,!1),c)}function cB(a,b,c){var d=a.Oa;return new Uw(a.Va,d.b()?R():new L(b.n(d.o())),c.n(a.ra),a.Pd)} function LX(a,b,c,d,e){var g=a.Va,h=a.Oa;h.b()?h=R():(h=h.o(),h=new L(h.Kc(b,c,d,e)));return new Uw(g,h,a.ra.Kc(b,c,d,e),a.Pd)}f.u=function(){var a=this.Oa;if(a.b())return""+this.ra;a=a.o();return"mut "+(Pe(new E(a),this.Va.ib)?"":a)+".."+this.ra};f.H=function(){return"FieldType"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Oa;case 1:return this.ra;default:return $K(W(),a)}};f.D=function(a){return a instanceof Uw};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Uw&&a.Va===this.Va){var b=this.Oa,c=a.Oa;if(null===b?null===c:b.i(c))return b=this.ra,a=a.ra,null===b?null===a:mC(b,a)}return!1};f.$classData=q({kZ:0},!1,"mlscript.TyperDatatypes$FieldType",{kZ:1,g:1,E:1,v:1,l:1});function nY(a){a=a.AK().m();a=new Ef(a,new y(b=>new vl(b.V)));return Aq(Bq(),a)}function oY(a){var b=a.rr().Dh;a=a.AK().Ja(new y(c=>c.V));return"#"+(b+("\x3c"+ze(a,"",",",""))+"\x3e")} function XB(a){return!!(a&&a.$classData&&a.$classData.rb.KO)}function pY(a,b){if(null===b)throw null;a.q=b;a.Be=a.q.Im;a.pe=-1+a.Be|0;a.oe=a;b.Fp=1+b.Fp|0}function zA(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}zA.prototype=new YP;zA.prototype.constructor=zA;function qY(){}qY.prototype=zA.prototype;zA.prototype.i=function(a){return mC(this,a)}; zA.prototype.B=function(){if(0===(1&this.dd)<<24>>24&&0===(1&this.dd)<<24>>24){if(this instanceof lx)var a=this.sp;else if(this instanceof OA)a=this.Hi.B();else if(null!==this&&Gs(this))a=AL(this);else throw new w(this);this.ze=a;this.dd=(1|this.dd)<<24>>24}return this.ze};function pD(a){if(0===(2&a.dd)<<24>>24&&0===(2&a.dd)<<24>>24){a:if(a instanceof cC)var b=!0;else{for(b=ED(a,!1);!b.b();){if(pD(b.e())){b=!0;break a}b=b.f()}b=!1}a.Ae=b;a.dd=(2|a.dd)<<24>>24}return a.Ae} zA.prototype.Kc=function(a,b,c,d){var e=this.q,g=this.q.Df,h=ap();return fD(e,this,g,c,a,h,d,b)};zA.prototype.rT=function(){return uy(this)};function jx(a,b,c,d,e,g){a.Ga=c;a.lh=d;a.go=e;a.Zm=g;if(null===b)throw null;a.Wu=b;a.Ix=!e.b();return a}function kx(){this.go=this.lh=this.Ga=null;this.Ix=this.Zm=!1;this.Wu=null}kx.prototype=new p;kx.prototype.constructor=kx;function rY(){}f=rY.prototype=kx.prototype; f.u=function(){var a=this.Ix?"o: ":"",b=this.Ga;b.b()?b=this.lh:(b=b.o(),b=this.lh+":"+b);return a+"\u2039"+b+"\u203a"};f.H=function(){return"TypeProvenance"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.Ga;case 1:return this.lh;case 2:return this.go;case 3:return this.Zm;default:return $K(W(),a)}};f.D=function(a){return a instanceof kx}; f.B=function(){var a=lb("TypeProvenance");a=W().C(-889275714,a);var b=this.Ga;b=My(W(),b);a=W().C(a,b);b=this.lh;b=My(W(),b);a=W().C(a,b);b=this.go;b=My(W(),b);a=W().C(a,b);b=this.Zm?1231:1237;a=W().C(a,b);return W().Ma(a,4)};f.i=function(a){if(this===a)return!0;if(a instanceof kx&&a.Wu===this.Wu){if(this.Zm===a.Zm){var b=this.Ga,c=a.Ga;b=null===b?null===c:b.i(c)}else b=!1;if(b&&this.lh===a.lh)return b=this.go,a=a.go,null===b?null===a:b.i(a)}return!1}; f.$classData=q({VO:0},!1,"mlscript.TyperDatatypes$TypeProvenance",{VO:1,g:1,E:1,v:1,l:1});function NP(a){if(null===a)throw null;}NP.prototype=new kS;NP.prototype.constructor=NP;NP.prototype.u=function(){return"TypeProvenance"};NP.prototype.$classData=q({HZ:0},!1,"mlscript.TyperDatatypes$TypeProvenance$",{HZ:1,$ba:1,g:1,qaa:1,l:1});function dQ(a,b){this.Qd=a;this.qe=b}dQ.prototype=new p;dQ.prototype.constructor=dQ;f=dQ.prototype;f.u=function(){return this.Bw()}; f.Bw=function(){var a=this.qe;if(!0===this.Qd&&!0===a)return"\u00b1";a=this.qe;if(!1===this.Qd&&!0===a)return"-";a=this.qe;if(!0===this.Qd&&!1===a)return"+";a=this.qe;if(!1===this.Qd&&!1===a)return"\x3d";throw new w(this);};f.H=function(){return"VarianceInfo"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Qd;case 1:return this.qe;default:return $K(W(),a)}};f.D=function(a){return a instanceof dQ}; f.B=function(){var a=lb("VarianceInfo");a=W().C(-889275714,a);var b=this.Qd?1231:1237;a=W().C(a,b);b=this.qe?1231:1237;a=W().C(a,b);return W().Ma(a,2)};f.i=function(a){return this===a?!0:a instanceof dQ?this.Qd===a.Qd&&this.qe===a.qe:!1};f.$classData=q({f_:0},!1,"mlscript.VarianceInfo",{f_:1,g:1,E:1,v:1,l:1});function gE(){this.SA=null;pz();var a=u();this.SA=qz(a)}gE.prototype=new p;gE.prototype.constructor=gE; function sn(a){if(a.SA.b())return t().d;Jo();var b=a.SA;Od();b=Ko(Pd(u(),b));a.SA.mg();t();return new L(b)}function Qo(a,b){a=sn(a);if(a instanceof L)return new z(a.k,b);if(t().d===a)return b;throw new w(a);}function po(a,b){a=sn(a);if(a instanceof L){a=a.k;t();b=rn(b);var c=O().c;return new Ud(new z(a,new z(b,c)))}if(t().d===a)return t(),new fe(b);throw new w(a);}f=gE.prototype;f.H=function(){return"TemporaryVariableEmitter"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)}; f.D=function(a){return a instanceof gE};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return a instanceof gE};f.$classData=q({B_:0},!1,"mlscript.codegen.TemporaryVariableEmitter",{B_:1,g:1,E:1,v:1,l:1});function NE(a,b){this.Wi=a;this.mj=b}NE.prototype=new p;NE.prototype.constructor=NE;f=NE.prototype; f.u=function(){var a=ze(this.Wi,""," and ",""),b=this.mj.b()?"":" ",c=this.mj,d=O().c;if(null===d?null===c:d.i(c))d="";else{if(c===u())d=u();else{d=c.e();var e=d=new z(d.Sj.x,u());for(c=c.f();c!==u();){var g=c.e();g=new z(g.Sj.x,u());e=e.p=g;c=c.f()}}d=ze(d,"(",", ",")")}return a+b+d}; function sY(a,b){var c=a.Wi;a=a.mj;if(null===b)throw new w(b);var d=b.Wi;b=b.mj;var e=O().c;if(null===e?null===d:e.i(d))return new NE(c,dl(b,a));if(d instanceof z)return e=d.z,e.lf=dl(e.lf,a),new NE(dl(d,c),b);throw new w(d);}function tY(a,b){var c=O().c;if(null===c?null===b:c.i(b))return a;if(b instanceof z)return c=b.z,c.lf=dl(c.lf,a.mj),new NE(dl(b,a.Wi),O().c);throw new w(b);} function Vea(a,b){a:for(var c=O().c,d=a.Wi,e=R();;){var g=!1,h=null,k=O().c;if(null===k?null===d:k.i(d)){b=e;break a}if(d instanceof z){g=!0;h=d;var l=h.z;k=h.p;if(l instanceof OE){h=l;if(Pe(new E(h.$x),b)){t();b=new L(new tl(c,h,k));break a}c=Xq(c,h);d=k;continue}}if(g&&(l=h.z,k=h.p,l instanceof UE)){h=l;if(Pe(new E(h.Yx),b)){t();b=new L(new tl(c,h,k));break a}c=Xq(c,h);d=k;continue}if(g&&(l=h.z,k=h.p,l instanceof SE)){h=l;Pe(new E(h.Wx),b)?(g=c,e.b()&&(t(),e=new L(new tl(g,h,k))),d=k):(c=Xq(c,h), d=k);continue}if(g)g=h.p,c=Xq(c,h.z),d=g;else throw new w(d);}if(b.b())return R();b=b.o();if(null!==b)a=G(new H,b.hb,new NE(dl(b.Rd,b.kc),a.mj));else throw new w(b);return new L(a)}function uY(a,b){var c=a.Wi,d=O().c;if(null===d?null===c:d.i(c))return c=O().c,b=b.ha(),new NE(c,dl(a.mj,b));if(c instanceof z)return c=c.z,d=c.lf,c.lf=dl(b.ha(),d),a;throw new w(c);}f.H=function(){return"Conjunction"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.Wi;case 1:return this.mj;default:return $K(W(),a)}};f.D=function(a){return a instanceof NE};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof NE){var b=this.Wi,c=a.Wi;if(null===b?null===c:b.i(c))return b=this.mj,a=a.mj,null===b?null===a:b.i(a)}return!1};f.$classData=q({Q_:0},!1,"mlscript.ucs.Conjunction",{Q_:1,g:1,E:1,v:1,l:1});function bF(a,b,c,d){this.fr=a;this.ZA=b;this.Sj=c;this.gr=d}bF.prototype=new p; bF.prototype.constructor=bF;f=bF.prototype;f.H=function(){return"LetBinding"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.fr;case 1:return this.ZA;case 2:return this.Sj;case 3:return this.gr;default:return $K(W(),a)}};f.D=function(a){return a instanceof bF}; f.B=function(){var a=lb("LetBinding");a=W().C(-889275714,a);var b=this.fr;b=My(W(),b);a=W().C(a,b);b=this.ZA?1231:1237;a=W().C(a,b);b=this.Sj;b=My(W(),b);a=W().C(a,b);b=this.gr;b=My(W(),b);a=W().C(a,b);return W().Ma(a,4)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof bF&&this.ZA===a.ZA&&this.fr===a.fr){var b=this.Sj,c=a.Sj;if(null===b?null===c:b.i(c))return b=this.gr,a=a.gr,null===b?null===a:b.i(a)}return!1}; f.$classData=q({T_:0},!1,"mlscript.ucs.LetBinding",{T_:1,g:1,E:1,v:1,l:1});class vY extends wS{constructor(a){super();yF(this,a,null,!0)}}vY.prototype.$classData=q({h0:0},!1,"mlscript.ucs.PartialTermError",{h0:1,OF:1,pc:1,g:1,l:1});function wY(a,b,c){this.no=a;this.nh=b;this.ZP=c}wY.prototype=new p;wY.prototype.constructor=wY;function GE(a){var b=a.no;b.b()?(a=a.nh,a instanceof wm||xm("`term` must be a `SimpleTerm` when `local` is empty")):a=b.o();return a} function xY(a){var b=a.no;if(b.b())return R();b=b.o();return new L(new bF(aF(),!1,b,a.nh))}f=wY.prototype;f.u=function(){var a=this.no;a:if(t().d===a)a="";else{if(a instanceof L){var b=a.k;if(null!==b){a=b.x+" @ ";break a}}throw new w(a);}return a+""+Zz(this.nh,!1)};f.H=function(){return"Scrutinee"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.no;case 1:return this.nh;default:return $K(W(),a)}};f.D=function(a){return a instanceof wY};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof wY){var b=this.no,c=a.no;if(null===b?null===c:b.i(c))return b=this.nh,a=a.nh,null===b?null===a:b.i(a)}return!1};f.$classData=q({i0:0},!1,"mlscript.ucs.Scrutinee",{i0:1,g:1,E:1,v:1,l:1});q({l0:0},!1,"mlscript.utils.algorithms$CyclicGraphError",{l0:1,qd:1,pc:1,g:1,l:1});function yY(){}yY.prototype=new lT;yY.prototype.constructor=yY;function zY(){}zY.prototype=yY.prototype;function RD(a){var b=new oT;yF(b,a,null,!0);return b}class oT extends wS{} oT.prototype.$classData=q({a3:0},!1,"scala.NotImplementedError",{a3:1,OF:1,pc:1,g:1,l:1});function qG(){}qG.prototype=new p;qG.prototype.constructor=qG;f=qG.prototype;f.Ob=function(a,b){return GQ(this,a,b)};f.u=function(){return"\x3cfunction1\x3e"};f.Lc=function(){return!1};f.AJ=function(a){throw new w(a);};f.n=function(a){this.AJ(a)};f.$classData=q({g3:0},!1,"scala.PartialFunction$$anon$1",{g3:1,g:1,za:1,la:1,l:1});function FQ(a,b){this.GK=a;this.mR=b}FQ.prototype=new p; FQ.prototype.constructor=FQ;f=FQ.prototype;f.u=function(){return"\x3cfunction1\x3e"};f.Lc=function(a){return this.GK.Lc(a)};f.n=function(a){return this.mR.n(this.GK.n(a))};f.Ob=function(a,b){var c=this.GK.Ob(a,pG().Hv);return rG(pG(),c)?b.n(a):this.mR.n(c)};f.$classData=q({h3:0},!1,"scala.PartialFunction$AndThen",{h3:1,g:1,za:1,la:1,l:1});function EQ(a,b){this.IK=a;this.HK=b}EQ.prototype=new p;EQ.prototype.constructor=EQ;f=EQ.prototype;f.u=function(){return"\x3cfunction1\x3e"}; f.Lc=function(a){a=this.IK.Ob(a,pG().Hv);return!rG(pG(),a)&&this.HK.Lc(a)};f.n=function(a){return this.HK.n(this.IK.n(a))};f.Ob=function(a,b){var c=this.IK.Ob(a,pG().Hv);return rG(pG(),c)?b.n(a):this.HK.Ob(c,new y(()=>b.n(a)))};f.$classData=q({i3:0},!1,"scala.PartialFunction$Combined",{i3:1,g:1,za:1,la:1,l:1});function Wr(a){this.k3=a}Wr.prototype=new cS;Wr.prototype.constructor=Wr;function Vr(a,b){a=a.k3.Ob(b,pG().Hv);return rG(pG(),a)?R():new L(a)}Wr.prototype.n=function(a){return Vr(this,a)}; Wr.prototype.$classData=q({j3:0},!1,"scala.PartialFunction$Lifted",{j3:1,dM:1,g:1,la:1,l:1});function Ue(a){this.gG=null;this.Jv=a}Ue.prototype=new p;Ue.prototype.constructor=Ue;function Wea(){var a=new Ue(J(new K,["",".",""]));null===a.gG&&null===a.gG&&(a.gG=new uG(a));return a.gG}f=Ue.prototype;f.H=function(){return"StringContext"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Jv:$K(W(),a)};f.D=function(a){return a instanceof Ue};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Ue){var b=this.Jv;a=a.Jv;return null===b?null===a:b.i(a)}return!1};f.$classData=q({p3:0},!1,"scala.StringContext",{p3:1,g:1,E:1,v:1,l:1});function AY(){}AY.prototype=new p;AY.prototype.constructor=AY;function BY(){}f=BY.prototype=AY.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return this.qk(a,-1)}; f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)}; f.Q=function(){return-1};f.Ja=function(a){return new Ef(this,a)};function CY(){this.sm=null;this.sm=DY()}CY.prototype=new wT;CY.prototype.constructor=CY;CY.prototype.$classData=q({$4:0},!1,"scala.collection.Iterable$",{$4:1,pG:1,g:1,bg:1,l:1});var EY;function DK(){EY||(EY=new CY);return EY}function FY(){this.VR=this.UR=this.Nt=null;uea(this);GY=this;this.UR=tc();this.VR=new U(()=>HY().UR)}FY.prototype=new VT;FY.prototype.constructor=FY; FY.prototype.$classData=q({H5:0},!1,"scala.collection.Map$",{H5:1,I5:1,g:1,Ey:1,l:1});var GY;function HY(){GY||(GY=new FY);return GY}function IY(){this.YR=null;JY=this;this.YR=new KY}IY.prototype=new p;IY.prototype.constructor=IY;f=IY.prototype;f.Eb=function(){var a=new EV(16,.75);return new dU(a,new y(b=>new qv(b)))};f.Hh=function(a){return(a=(op(),pp(qp(),a)))&&a.$classData&&a.$classData.rb.oL?a:new qv(a)};f.Ib=function(a){return ZT(eU(),a)};f.X=function(){return this.YR}; f.$classData=q({O5:0},!1,"scala.collection.MapView$",{O5:1,g:1,Nba:1,Ey:1,l:1});var JY;function LY(){this.Bl=null}LY.prototype=new p;LY.prototype.constructor=LY;function MY(){}MY.prototype=LY.prototype;function RE(a,b){return a.Bl.Hh(b)}function TE(a){return a.Bl.X()}f=LY.prototype;f.vl=function(a){return this.Bl.Ib(a)};f.Eb=function(){return this.Bl.Eb()};f.Ib=function(a){return this.vl(a)};f.X=function(){return TE(this)};f.Hh=function(a){return RE(this,a)}; function hl(a){return a.Ii(new y(b=>b))}function NY(a,b){return a.vc(new OY(a,b))}function Xea(a,b){return a.wo(new y(c=>ml(nl(),b,c)),0)}function QU(a,b){return a.qo(new y(c=>ml(nl(),c,b)))}function Oca(a,b){return 0>b||b>a.K()?Rq().Pa:new PY(a,b)}function qw(a,b){var c=a.K(),d=a.ti();if(1===c)c=a.e(),d.$(c);else if(1{e=b.n(e);c.$(e.h());return d.$(e.j())}));return G(new H,c.Kb(),d.Kb())}function YY(a,b){var c=a.Ub().Eb();for(a=a.m();a.s();){var d=b.n(a.t());c.$(d)}return c.Kb()}function zX(a,b){var c=a.Ub().Eb();for(a=a.m();a.s();){var d=b.n(a.t());c.zc(d)}return c.Kb()}function Bf(a,b){var c=a.Ub().Eb();a=a.m();for(b=b.m();a.s()&&b.s();){var d=G(new H,a.t(),b.t());c.$(d)}return c.Kb()} function Hf(a){var b=a.Ub().Eb(),c=0;for(a=a.m();a.s();){var d=G(new H,a.t(),c);b.$(d);c=1+c|0}return b.Kb()}function ZY(a,b){var c=a.ti();for(a=a.m();a.s();){var d=a.t();!1!==!!b.n(d)&&c.$(d)}return c.Kb()}function Pt(a,b){var c=a.Ub().Eb(),d=a.Ub().Eb();a.Ca(new y(e=>{e=b.n(e);if(e instanceof fe)return c.$(e.aa);if(e instanceof Ud)return d.$(e.fa);throw new w(e);}));return G(new H,c.Kb(),d.Kb())} function $Y(a,b){var c=a.ti();if(0<=b){var d=-b|0,e=a.Q();-1!==e&&c.he(e+d|0)}b=a.m().ph(b);for(a=a.m();b.s();)d=a.t(),c.$(d),b.t();return c.Kb()}function Yea(a,b,c){a=a.Eb();a.he(b);for(var d=0;dhV())))}dZ.prototype=new p;dZ.prototype.constructor=dZ;f=dZ.prototype;f.Hh=function(a){return aU(this,a)}; function Zea(a,b,c){var d=new aw(b),e=new IQ(c);return new gZ(new U(()=>{for(var g=d.rc,h=e.ve;0iZ(FK(),b.m())))}function jZ(a,b,c){return b.s()?(a=b.t(),new eV(a,new gZ(new U(()=>jZ(FK(),b,c))))):Es(c)}function iZ(a,b){return b.s()?(a=b.t(),new eV(a,new gZ(new U(()=>iZ(FK(),b))))):hV()} function kZ(a,b,c){return new gZ(new U(()=>{FK();var d=kZ(FK(),b+c|0,c);return new eV(b,d)}))}f.Eb=function(){return new lZ};f.X=function(){return this.Ry};f.Ib=function(a){return aU(this,a)};f.$classData=q({i7:0},!1,"scala.collection.immutable.LazyList$",{i7:1,g:1,Nk:1,bg:1,l:1});var eZ;function FK(){eZ||(eZ=new dZ);return eZ}function mZ(){this.Pt=null;this.Pt=xV()}mZ.prototype=new XT;mZ.prototype.constructor=mZ; function Ov(a,b,c){if(b&&b.$classData&&b.$classData.rb.FS){O();var d=b.se();if(null===c?null===d:c.i(d))return b}return WT.prototype.qr.call(a,b,c)}mZ.prototype.qr=function(a,b){return Ov(this,a,b)};mZ.prototype.$classData=q({y8:0},!1,"scala.collection.immutable.SortedMap$",{y8:1,Z5:1,g:1,xL:1,l:1});var nZ;function sv(){nZ||(nZ=new mZ);return nZ}function oZ(a){this.Uk=a.We;this.bu=a.Jd}oZ.prototype=new eR;oZ.prototype.constructor=oZ;oZ.prototype.u=function(){return"\x3cfunction1\x3e"}; oZ.prototype.n=function(a){this.bu=fR(this,this.bu,a.h(),a.j())};oZ.prototype.$classData=q({I8:0},!1,"scala.collection.immutable.TreeMap$Adder",{I8:1,e8:1,yS:1,g:1,la:1});function pZ(){}pZ.prototype=new p;pZ.prototype.constructor=pZ; function qZ(a,b,c){if(b instanceof rZ&&(a=b.Xe,null===c?null===a:c.i(a)))return b;if(b&&b.$classData&&b.$classData.rb.PG&&(a=b.se(),null===c?null===a:c.i(a)))return sZ(new rZ,jJ(nJ(),b.m(),b.ka()),c);if(b instanceof tZ&&(c===Fq()?a=!0:(a=Fq(),a=c===a.NB),a))return c===Fq()===0new Jr(b)))};AZ.prototype.vc=function(a){return CZ(this,a)}; AZ.prototype.$classData=q({a9:0},!1,"scala.collection.immutable.WrappedString$",{a9:1,g:1,Pba:1,TK:1,l:1});var BZ;function DZ(){BZ||(BZ=new AZ);return BZ}function dU(a,b){this.OS=this.sC=null;if(null===a)throw null;this.sC=a;this.OS=b}dU.prototype=new p;dU.prototype.constructor=dU;f=dU.prototype;f.he=function(a){this.sC.he(a)};f.Kb=function(){return this.OS.n(this.sC.Kb())};f.zc=function(a){this.sC.zc(a);return this};f.$=function(a){this.sC.$(a);return this}; f.$classData=q({v9:0},!1,"scala.collection.mutable.Builder$$anon$1",{v9:1,g:1,xg:1,xf:1,wf:1});function PV(a,b){a.lk=b;return a}function QV(){this.lk=null}QV.prototype=new p;QV.prototype.constructor=QV;function EZ(){}f=EZ.prototype=QV.prototype;f.he=function(){};function EF(a,b){a.lk.$(b);return a}function FZ(a,b){a.lk.zc(b);return a}f.zc=function(a){return FZ(this,a)};f.$=function(a){return EF(this,a)};f.Kb=function(){return this.lk}; f.$classData=q({tC:0},!1,"scala.collection.mutable.GrowableBuilder",{tC:1,g:1,xg:1,xf:1,wf:1});function GZ(){this.sm=null;this.sm=QF()}GZ.prototype=new wT;GZ.prototype.constructor=GZ;GZ.prototype.$classData=q({Y9:0},!1,"scala.collection.mutable.Iterable$",{Y9:1,pG:1,g:1,bg:1,l:1});var HZ;function rO(){HZ||(HZ=new GZ);return HZ}function IZ(){this.Nt=null;this.Nt=Ty()}IZ.prototype=new VT;IZ.prototype.constructor=IZ; IZ.prototype.$classData=q({n$:0},!1,"scala.collection.mutable.Map$",{n$:1,I5:1,g:1,Ey:1,l:1});var JZ;function Xu(){JZ||(JZ=new IZ);return JZ}function KZ(){this.sm=null;this.sm=pz()}KZ.prototype=new wT;KZ.prototype.constructor=KZ;KZ.prototype.$classData=q({A$:0},!1,"scala.collection.mutable.Set$",{A$:1,pG:1,g:1,bg:1,l:1});var LZ;function jA(){LZ||(LZ=new KZ);return LZ}function MZ(){this.Pt=null;this.Pt=$V()}MZ.prototype=new XT;MZ.prototype.constructor=MZ; MZ.prototype.$classData=q({D$:0},!1,"scala.collection.mutable.SortedMap$",{D$:1,Z5:1,g:1,xL:1,l:1});var NZ;function Hw(){NZ||(NZ=new MZ);return NZ}function Hq(a,b,c){a.Qg=b;a.mT=c;yF(a,null,null,!1);return a}class Iq extends vea{constructor(){super();this.mT=this.Qg=null}Cj(){return this.mT}pQ(){}}Iq.prototype.$classData=q({lT:0},!1,"scala.runtime.NonLocalReturnControl",{lT:1,s4:1,pc:1,g:1,l:1});function OZ(){}OZ.prototype=new p;OZ.prototype.constructor=OZ;function PZ(){}PZ.prototype=OZ.prototype; function GP(a){return a instanceof Ud?new L(a.fa):R()}function QZ(){}QZ.prototype=new hW;QZ.prototype.constructor=QZ;function RZ(){}RZ.prototype=QZ.prototype;class qb extends iW{constructor(a){super();yF(this,a,null,!0)}}qb.prototype.$classData=q({O0:0},!1,"java.lang.ArithmeticException",{O0:1,Te:1,qd:1,pc:1,g:1,l:1});var eaa=q({T0:0},!1,"java.lang.Byte",{T0:1,ur:1,g:1,l:1,nf:1,Et:1},a=>Zb(a));function Kj(a){var b=new SZ;yF(b,a,null,!0);return b} function UL(){var a=new SZ;yF(a,null,null,!0);return a}class SZ extends iW{}SZ.prototype.$classData=q({ak:0},!1,"java.lang.IllegalArgumentException",{ak:1,Te:1,qd:1,pc:1,g:1,l:1});function XH(a){var b=new TZ;yF(b,a,null,!0);return b}class TZ extends iW{}TZ.prototype.$classData=q({CQ:0},!1,"java.lang.IllegalStateException",{CQ:1,Te:1,qd:1,pc:1,g:1,l:1});function aL(a,b){yF(a,b,null,!0);return a}class bL extends iW{} bL.prototype.$classData=q({eK:0},!1,"java.lang.IndexOutOfBoundsException",{eK:1,Te:1,qd:1,pc:1,g:1,l:1});class Aj extends iW{constructor(){super();yF(this,null,null,!0)}}Aj.prototype.$classData=q({n1:0},!1,"java.lang.NegativeArraySizeException",{n1:1,Te:1,qd:1,pc:1,g:1,l:1});function oL(a){var b=new UZ;yF(b,a,null,!0);return b}function le(){var a=new UZ;yF(a,null,null,!0);return a}class UZ extends iW{}UZ.prototype.$classData=q({o1:0},!1,"java.lang.NullPointerException",{o1:1,Te:1,qd:1,pc:1,g:1,l:1}); var faa=q({q1:0},!1,"java.lang.Short",{q1:1,ur:1,g:1,l:1,nf:1,Et:1},a=>$b(a));function DT(){var a=new VZ;yF(a,null,null,!0);return a}function nv(a){var b=new VZ;yF(b,a,null,!0);return b}class VZ extends iW{}VZ.prototype.$classData=q({A1:0},!1,"java.lang.UnsupportedOperationException",{A1:1,Te:1,qd:1,pc:1,g:1,l:1});function WZ(){}WZ.prototype=new yS;WZ.prototype.constructor=WZ;function XZ(){}XZ.prototype=WZ.prototype; WZ.prototype.i=function(a){if(a===this)a=!0;else if(a&&a.$classData&&a.$classData.rb.OQ){var b;if(b=a.ka()===this.ka()){a=a.vv();a:{for(;a.s();)if(b=a.t(),!this.L(b)){a=!0;break a}a=!1}b=!a}a=b}else a=!1;return a};WZ.prototype.B=function(){for(var a=this.vv(),b=0;a.s();){var c=b;b=a.t();c|=0;b=ib(b)+c|0}return b|0};class kK extends iW{constructor(){super();yF(this,"mutation occurred during iteration",null,!0)}} kK.prototype.$classData=q({I1:0},!1,"java.util.ConcurrentModificationException",{I1:1,Te:1,qd:1,pc:1,g:1,l:1});function YZ(a,b){if(null===b)var c=0;else c=ib(b),c^=c>>>16|0;a=ZZ(a,b,c,c&(-1+a.wl.a.length|0));return null===a?null:a.xv} function $Z(a,b){this.wl=null;this.yv=this.jK=0;this.RQ=b;if(0>a)throw Kj("initialCapacity \x3c 0");if(0>=b)throw Kj("loadFactor \x3c\x3d 0.0");a=-1+a|0;a=4>Math.clz32(a)&a)<<1;this.wl=new (md(EN).Ia)(1073741824>a?a:1073741824);this.jK=Eb(this.wl.a.length*this.RQ);this.yv=0}$Z.prototype=new wN;$Z.prototype.constructor=$Z;f=$Z.prototype;f.ka=function(){return this.yv};f.LF=function(a){return YZ(this,a)}; f.EF=function(a){if(null===a)var b=0;else b=ib(a),b^=b>>>16|0;return null!==ZZ(this,a,b,b&(-1+this.wl.a.length|0))};f.JF=function(){return new a_(this)};function ZZ(a,b,c,d){for(a=a.wl.a[d];;){if(null===a)return null;c===a.oy?(d=a.xB,d=null===b?null===d:La(b,d)):d=!1;if(d)return a;if(c{if(Sy(k,e))return this.xU.$(k)})),this.Ow.oh(e),a=new Te(new Ue(J(new K,["\u2022 this ",":"]))),d=[We(Xe(),d)],d=Ye(a,J(new K,d)),G(new H,d,c)}}return b.n(a)}; f.RJ=function(a){if(null!==a){var b=a.Ga;if(b instanceof L){b=b.k;if(this.Ow.L(b)||a.Zm)b=!1;else{var c=!1;for(a=this.Ow.m();!c&&a.s();)c=a.t(),c=Sy(b,c);b=!c}if(b)return!0}}return!1};f.Lc=function(a){return this.RJ(a)};f.Ob=function(a,b){return this.yJ(a,b)};f.$classData=q({wU:0},!1,"mlscript.ConstraintSolver$$anonfun$4",{wU:1,Kf:1,g:1,la:1,za:1,l:1});function AX(a,b,c){this.KM=b;this.JM=c}AX.prototype=new eW;AX.prototype.constructor=AX;f=AX.prototype; f.nr=function(a,b){if(null!==a){var c=a.h(),d=a.j(),e=d.Ga;e.b()?e=!1:(e=e.o(),e=!this.KM.L(e));if(e)return this.JM.Am?(a=new Te(new Ue(J(new K,["Note: "," ",""]))),c=[We(Xe(),d.lh),We(Xe(),c)],c=Ye(a,J(new K,c))):(a=new Te(new Ue(J(new K,[" "," ",""]))),c=[We(Xe(),d.lh),We(Xe(),c)],c=Ye(a,J(new K,c))),this.JM.Am=!1,c=Ye(new Te(new Ue(J(new K,[""," is defined at:"]))),J(new K,[c])),G(new H,c,d.Ga)}return b.n(a)}; f.sr=function(a){return null!==a&&(a=a.j().Ga,a.b()?a=!1:(a=a.o(),a=!this.KM.L(a)),a)?!0:!1};f.Lc=function(a){return this.sr(a)};f.Ob=function(a,b){return this.nr(a,b)};f.$classData=q({yU:0},!1,"mlscript.ConstraintSolver$$anonfun$5",{yU:1,Kf:1,g:1,la:1,za:1,l:1});function yX(){}yX.prototype=new eW;yX.prototype.constructor=yX;f=yX.prototype;f.xJ=function(a,b){return a instanceof cv||a instanceof Qv||Uv(a)||a instanceof zv||a instanceof fw||a instanceof FA||b.n(a)}; f.QJ=function(a){return a instanceof cv||a instanceof Qv||Uv(a)||a instanceof zv||a instanceof fw||a instanceof FA};f.Lc=function(a){return this.QJ(a)};f.Ob=function(a,b){return this.xJ(a,b)};f.$classData=q({zU:0},!1,"mlscript.ConstraintSolver$$anonfun$lhsIsPlain$1$1",{zU:1,Kf:1,g:1,la:1,za:1,l:1});function x_(){}x_.prototype=new Wz;x_.prototype.constructor=x_;f=x_.prototype;f.H=function(){return"DEINDENT"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)}; f.D=function(a){return a instanceof x_};f.B=function(){return 1524287597};f.u=function(){return"DEINDENT"};f.$classData=q({DU:0},!1,"mlscript.DEINDENT$",{DU:1,yk:1,g:1,E:1,v:1,l:1});var y_;function rs(){y_||(y_=new x_);return y_}function sy(a){return!!(a&&a.$classData&&a.$classData.rb.ud)}function z_(){}z_.prototype=new jl;z_.prototype.constructor=z_;f=z_.prototype;f.H=function(){return"Lexing"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof z_}; f.B=function(){return-2022196861};f.u=function(){return"Lexing"};f.$classData=q({JU:0},!1,"mlscript.Diagnostic$Lexing$",{JU:1,SM:1,g:1,E:1,v:1,l:1});var A_;function ir(){A_||(A_=new z_);return A_}function B_(){}B_.prototype=new jl;B_.prototype.constructor=B_;f=B_.prototype;f.H=function(){return"Parsing"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof B_};f.B=function(){return 871689872};f.u=function(){return"Parsing"}; f.$classData=q({KU:0},!1,"mlscript.Diagnostic$Parsing$",{KU:1,SM:1,g:1,E:1,v:1,l:1});var C_;function ws(){C_||(C_=new B_);return C_}function D_(){}D_.prototype=new jl;D_.prototype.constructor=D_;f=D_.prototype;f.H=function(){return"Typing"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof D_};f.B=function(){return-1774931561};f.u=function(){return"Typing"};f.$classData=q({LU:0},!1,"mlscript.Diagnostic$Typing$",{LU:1,SM:1,g:1,E:1,v:1,l:1});var E_; function lu(){E_||(E_=new D_);return E_}function F_(){}F_.prototype=new Wz;F_.prototype.constructor=F_;f=F_.prototype;f.H=function(){return"INDENT"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof F_};f.B=function(){return-2130910036};f.u=function(){return"INDENT"};f.$classData=q({YU:0},!1,"mlscript.INDENT$",{YU:1,yk:1,g:1,E:1,v:1,l:1});var G_;function ks(){G_||(G_=new F_);return G_}function Xn(a,b,c){this.pV=b;this.qV=c}Xn.prototype=new eW; Xn.prototype.constructor=Xn;f=Xn.prototype;f.pj=function(a,b){if(a instanceof Zn){var c=a.wd,d=a.Rb,e=a.Ch,g=a.Yc;if(g instanceof fe&&(g=g.aa,c.b()||(c.b()?0:c.o())))return this.pV.oh(d.x),new Dp(!(c.b()||!c.o()),new Ep(this.qV),d,e,(O(),new fe(g)))}return b.n(a)};f.rj=function(a){if(a instanceof Zn){var b=a.wd;if(a.Yc instanceof fe&&(b.b()||(b.b()?0:b.o())))return!0}return!1};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)}; f.$classData=q({oV:0},!1,"mlscript.JSBackend$$anonfun$1",{oV:1,Kf:1,g:1,la:1,za:1,l:1});function Yn(a,b,c){this.iN=b;this.sV=c}Yn.prototype=new eW;Yn.prototype.constructor=Yn;f=Yn.prototype;f.pj=function(a,b){if(a instanceof Zn){var c=a.wd,d=a.Rb,e=a.Ch,g=a.Yc;if(g instanceof Ud&&(g=g.fa,a.Pl&&!this.iN.L(d.x)))return new Dp(!(c.b()||!c.o()),new Ep(this.sV),d,e,(O(),new Ud(g)))}return b.n(a)};f.rj=function(a){if(a instanceof Zn){var b=a.Rb;if(a.Yc instanceof Ud&&a.Pl&&!this.iN.L(b.x))return!0}return!1}; f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)};f.$classData=q({rV:0},!1,"mlscript.JSBackend$$anonfun$2",{rV:1,Kf:1,g:1,la:1,za:1,l:1});function ao(){}ao.prototype=new eW;ao.prototype.constructor=ao;f=ao.prototype;f.pj=function(a,b){return a instanceof yo?a:b.n(a)};f.rj=function(a){return a instanceof yo};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)};f.$classData=q({tV:0},!1,"mlscript.JSBackend$$anonfun$3",{tV:1,Kf:1,g:1,la:1,za:1,l:1}); function yz(a,b){this.kD=a;this.jD=b}yz.prototype=new Jp;yz.prototype.constructor=yz;f=yz.prototype;f.xa=function(){for(var a=Sp(Qp()," catch ("+this.kD.tq+") "),b=this.jD,c=Qp().ye;!b.b();){var d=b.e();c=Iz(c,d.xa());b=b.f()}return Rp(a,Oz(c))};f.H=function(){return"JSCatchClause"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.kD;case 1:return this.jD;default:return $K(W(),a)}};f.D=function(a){return a instanceof yz};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof yz){var b=this.kD,c=a.kD;if(null===b?null===c:b.i(c))return b=this.jD,a=a.jD,null===b?null===a:b.i(a)}return!1};f.$classData=q({BV:0},!1,"mlscript.JSCatchClause",{BV:1,Xc:1,g:1,E:1,v:1,l:1});function vO(){}vO.prototype=new eW;vO.prototype.constructor=vO;vO.prototype.Lc=function(a){return a instanceof xO};vO.prototype.Ob=function(a,b){return a instanceof xO?a.ax:b.n(a)}; vO.prototype.$classData=q({zW:0},!1,"mlscript.Message$$anonfun$typeBits$1",{zW:1,Kf:1,g:1,la:1,za:1,l:1});function xO(a){this.ax=a}xO.prototype=new Pq;xO.prototype.constructor=xO;f=xO.prototype;f.H=function(){return"Code"};f.G=function(){return 1};f.I=function(a){return 0===a?this.ax:$K(W(),a)};f.D=function(a){return a instanceof xO};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof xO){var b=this.ax;a=a.ax;return null===b?null===a:b.i(a)}return!1};f.$classData=q({BW:0},!1,"mlscript.Message$Code",{BW:1,AW:1,g:1,E:1,v:1,l:1});function Vq(a){this.Jz=a}Vq.prototype=new Pq;Vq.prototype.constructor=Vq;f=Vq.prototype;f.H=function(){return"Text"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Jz:$K(W(),a)};f.D=function(a){return a instanceof Vq};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){return this===a?!0:a instanceof Vq?this.Jz===a.Jz:!1};f.$classData=q({DW:0},!1,"mlscript.Message$Text",{DW:1,AW:1,g:1,E:1,v:1,l:1});function Dp(a,b,c,d,e){this.pN=this.oN=this.nN=null;this.rN=this.sN=0;this.tN=this.qN=null;this.Fs=0;this.hE=a;this.gE=b;this.uq=c;this.iE=d;this.bx=e;Nq(this);if(e instanceof Ud)a=e.fa;else{if(!(e instanceof fe))throw new w(e);a=e.aa}a=this.nN=a;b=O().c;this.oN=new z(c,new z(a,b))}Dp.prototype=new p;Dp.prototype.constructor=Dp;f=Dp.prototype; f.jn=function(){0===(1&this.Fs)<<24>>24&&0===(1&this.Fs)<<24>>24&&(this.pN=zq(this),this.Fs=(1|this.Fs)<<24>>24);return this.pN};f.rn=function(){return this.sN};f.fm=function(a){this.sN=a};f.qn=function(){return this.rN};f.em=function(a){this.rN=a};f.pn=function(){return this.qN};f.on=function(a){this.qN=a};f.A=function(){0===(2&this.Fs)<<24>>24&&0===(2&this.Fs)<<24>>24&&(this.tN=Dq(this),this.Fs=(2|this.Fs)<<24>>24);return this.tN};f.Vj=function(){return this.oN};f.H=function(){return"MethodDef"}; f.G=function(){return 5};f.I=function(a){switch(a){case 0:return this.hE;case 1:return this.gE;case 2:return this.uq;case 3:return this.iE;case 4:return this.bx;default:return $K(W(),a)}};f.D=function(a){return a instanceof Dp};f.B=function(){var a=lb("MethodDef");a=W().C(-889275714,a);var b=this.hE?1231:1237;a=W().C(a,b);b=this.gE;b=My(W(),b);a=W().C(a,b);b=this.uq;b=My(W(),b);a=W().C(a,b);b=this.iE;b=My(W(),b);a=W().C(a,b);b=this.bx;b=My(W(),b);a=W().C(a,b);return W().Ma(a,5)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Dp){if(this.hE===a.hE){var b=this.gE,c=a.gE;b=null===b?null===c:b.i(c)}else b=!1;if(b&&(b=this.uq,c=a.uq,null===b?null===c:b.i(c))&&(b=this.iE,c=a.iE,null===b?null===c:b.i(c)))return b=this.bx,a=a.bx,null===b?null===a:b.i(a)}return!1};f.$classData=q({EW:0},!1,"mlscript.MethodDef",{EW:1,g:1,Ta:1,E:1,v:1,l:1}); function kw(a){if(!a.rE){var b=a.vd.m(),c=a.fc;a:{if(c instanceof L&&(c=c.k,c instanceof Mu)){c=c.Fv().bc(c.pd);break a}c=ap()}for(;b.s();){var d=b.t();if(d instanceof nC){var e=d.Fv();c=c.Ce(e).bc(d.rp)}}a.qE=c;a.rE=!0}return a.qE}function Ku(a,b,c,d,e){this.pE=null;this.Mz=!1;this.qE=this.Fa=null;this.rE=!1;this.fc=b;this.vd=c;this.be=d;this.Me=e;if(null===a)throw null;this.Fa=a}Ku.prototype=new gv;Ku.prototype.constructor=Ku;f=Ku.prototype; f.u=function(){var a=this.fc;a=a.b()?"":a.o();var b=this.be,c=this.vd.m().nb(new U(()=>new Ou(this.Me)));c=new Ef(c,new y(d=>"\u2227"+d));return""+a+b+ze(c,"","","")};f.H=function(){return"LhsRefined"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.fc;case 1:return this.vd;case 2:return this.be;case 3:return this.Me;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ku};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Ku&&a.Fa===this.Fa){var b=this.fc,c=a.fc;(null===b?null===c:b.i(c))?(b=this.vd,c=a.vd,b=null===b?null===c:b.i(c)):b=!1;if(b&&(b=this.be,c=a.be,null===b?null===c:mC(b,c)))return b=this.Me,a=a.Me,null===b?null===a:b.i(a)}return!1};f.$classData=q({fX:0},!1,"mlscript.NormalForms$LhsRefined",{fX:1,eX:1,g:1,E:1,v:1,l:1});function IS(a){this.pE=null;this.Mz=!1;this.Fa=null;if(null===a)throw null;this.Fa=a}IS.prototype=new gv;IS.prototype.constructor=IS; f=IS.prototype;f.u=function(){return"\u22a4"};f.H=function(){return"LhsTop"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof IS};f.B=function(){return-2019595394};f.$classData=q({gX:0},!1,"mlscript.NormalForms$LhsTop$",{gX:1,eX:1,g:1,E:1,v:1,l:1});function jw(a,b,c,d){this.Nz=null;this.jx=!1;this.Aa=null;this.Xb=b;this.mb=c;this.Dc=d;rw(this,a)}jw.prototype=new tw;jw.prototype.constructor=jw; function cfa(a,b,c,d,e){var g=a.Aa,h=a.Xb,k=a.mb;if(k.b())k=R();else{k=k.o();if(k instanceof fe)k=k.aa,t(),k=k.qv(b,c,d,e),k=new fe(k);else if(k instanceof Ud)k=k.fa,t(),k=H_(k,b,c,d,e),k=new Ud(k);else throw new w(k);k=new L(k)}a=new pv(new qv(a.Dc),new y(r=>rv(r,b,c,d,e)));var l=sv(),m=Su(),n=op().ga;return new jw(g,h,k,(new tv(l,new Uu(m,n))).vc(a))}f=jw.prototype; f.u=function(){var a=ze(this.Xb,"","|",""),b=this.mb;if(b.b())b="";else{b=b.o();if(b instanceof Ud)b=""+b.fa;else{if(!(b instanceof fe))throw new w(b);b=""+b.aa}b="|"+b}var c=new Ou(this.Dc);c=new Ef(c,new y(d=>"|"+d));return a+b+ze(c,"","","")};f.H=function(){return"RhsBases"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.Xb;case 1:return this.mb;case 2:return this.Dc;default:return $K(W(),a)}};f.D=function(a){return a instanceof jw};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof jw&&a.Aa===this.Aa){var b=this.Xb,c=a.Xb;(null===b?null===c:b.i(c))?(b=this.mb,c=a.mb,b=null===b?null===c:b.i(c)):b=!1;if(b)return b=this.Dc,a=a.Dc,null===b?null===a:b.i(a)}return!1};f.HJ=function(a,b,c,d){return cfa(this,a,b,c,d)};f.$classData=q({hX:0},!1,"mlscript.NormalForms$RhsBases",{hX:1,CN:1,g:1,E:1,v:1,l:1});function JS(a){this.Nz=null;this.jx=!1;this.Aa=null;rw(this,a)}JS.prototype=new tw;JS.prototype.constructor=JS;f=JS.prototype; f.u=function(){return"\u22a5"};f.H=function(){return"RhsBot"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof JS};f.B=function(){return-1847837782};f.HJ=function(){return this};f.$classData=q({iX:0},!1,"mlscript.NormalForms$RhsBot$",{iX:1,CN:1,g:1,E:1,v:1,l:1});function iw(a,b,c){this.Nz=null;this.jx=!1;this.Aa=null;this.me=b;this.Ne=c;rw(this,a)}iw.prototype=new tw;iw.prototype.constructor=iw; function H_(a,b,c,d,e){var g=a.Aa,h=a.me,k=a.Ne,l=k.Va,m=k.Oa;if(m.b())m=R();else{m=m.o();var n=a.Aa,r=a.Aa.Df,v=ap();m=new L(fD(n,m,r,d,b,v,e,c))}n=k.ra;r=a.Aa;a=a.Aa.Df;v=ap();return new iw(g,h,new Uw(l,m,fD(r,n,a,d,b,v,e,c),k.Pd))}f=iw.prototype;f.u=function(){return"{"+this.me.x+":"+this.Ne+"}"};f.H=function(){return"RhsField"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.me;case 1:return this.Ne;default:return $K(W(),a)}};f.D=function(a){return a instanceof iw};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof iw&&a.Aa===this.Aa){var b=this.me,c=a.me;if(null===b?null===c:b.i(c))return b=this.Ne,a=a.Ne,null===b?null===a:b.i(a)}return!1};f.HJ=function(a,b,c,d){return H_(this,a,b,c,d)};f.$classData=q({jX:0},!1,"mlscript.NormalForms$RhsField",{jX:1,CN:1,g:1,E:1,v:1,l:1}); var efa=function dfa(a,b,c){if(b instanceof Ql){var e=b.Vl,g=b.ml;if(e instanceof vl&&"this"===e.x)return c.oh(G(new H,g,(t(),new L(e)))),t().d}if(b instanceof vl)return"this"===b.x?(t(),new L(b)):(c.oh(G(new H,b,t().d)),t().d);if(b instanceof I_)return t().d;if(b instanceof Pm||b&&b.$classData&&b.$classData.rb.md||b instanceof Ct||b instanceof BS||b instanceof zS||b instanceof Dt){b=b.Vj();for(e=t().d;!b.b();)g=e,e=b.e(),e=g.b()?dfa(a,e,c):g,b=b.f();return e}throw new w(b);}; function ffa(a,b,c,d,e,g,h){for(;;)if(b instanceof z){var k=b.z;b=b.p;if(k instanceof Ct)var l=t().d;else if(k instanceof Pm){var m=k;l=Rw(a,m,c,d,e,!0);if(!(g||b.b()&&h))if(m instanceof Dl||m instanceof Ol)JX(a,We(Xe(),"Pure expression does nothing in statement position."),m.A(),d);else{k=ux(a,l,jx(new kx,a,Lq(m),"expression in statement position",(tx(a),t().d),(tx(a),!1)));var n=a.Ck,r=new y(((x,A)=>B=>{fr();var C=Ye(new Te(new Ue(J(new K,["Expression in statement position should have type `()`."]))), u()),D=t().d;C=G(new H,C,D);D=Ye(new Te(new Ue(J(new K,["Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer."]))),u());var F=t().d;D=G(new H,D,F);A.n(hr(0,new z(C,new z(D,B.vt())),x.$c,lu()))})(a,d));m=jx(new kx,a,m.A(),hu(m),(tx(a),t().d),(tx(a),!1));var v=Sw(a).ob;Tw(a,k,n,r,m,c,v)}t();l=new L(l)}else sy(k)?(l=k,k=a,n=new Te(new Ue(J(new K,["Illegal position for this "," statement."]))),r=[We(Xe(),l.jb())],Lw(k,Ye(n,J(new K,r)),l.A(),d),l=t().d): xm("Program reached and unexpected state.");k=O().c;if(null===k?null===b:k.i(b))return l}else{a=O().c;if(null===a?null===b:a.i(b))return t().d;throw new w(b);}}function J_(){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0;this.zk=this.Pq=this.Uq=this.xp=this.Bp=this.Cp=this.Sq=this.zp=this.Rq=this.wp=this.Ap=this.yp=this.Qq=this.Tq=null;this.Dp=0;this.hs=this.yq=this.Aq=this.Bq=this.zq=this.Dq=this.Cq=null;this.Im=this.Pw=0;this.Ku=null}J_.prototype=new DX; J_.prototype.constructor=J_;function K_(){}K_.prototype=J_.prototype; function Dy(a,b,c,d,e,g,h,k,l,m){if(Ot(new E(d.pb),zp())&&h.b()){if(g.b())return e=d.gb,g=O().c,new fw(a,e,g,V(a));c=zx(a);d=d.gb;if(g===u())g=u();else{m=g.e();h=m=new z(m.hb,u());for(g=g.f();g!==u();)k=g.e(),k=new z(k.hb,u()),h=h.p=k,g=g.f();g=m}return yx(c,e,new fw(a,d,g,V(a)))}if(Ot(new E(d.pb),Bp())||Ot(new E(d.pb),zp())){Ot(new E(d.pb),zp())&&Lw(a,Ye(new Te(new Ue(J(new K,["Parameterized modules are not yet supported"]))),u()),c,m);a.F&&(l=ut(Q(),"| ",a.r)+("params: "+h+" ")+k,ff(gf(),l+"\n")); if(!b)if(h.b()){if(k.b())return e=new Te(new Ue(J(new K,["Class "," cannot be instantiated as it exposes no constructor"]))),g=[We(Xe(),d.gb.V)],Lw(a,Ye(e,J(new K,g)),c,m);b=new Te(new Ue(J(new K,["Construction of unparameterized class "," should use the `new` keyword"])));l=[We(Xe(),d.gb.V)];Lw(a,Ye(b,J(new K,l)),c,m)}else k.b()||Lw(a,Ye(new Te(new Ue(J(new K,["Construction of class with auxiliary constructor should use the `new` keyword"]))),u()),c,m);if(k instanceof L)c=k.k,m=ry(lv(),c,new y(n=> {var r=V(a);return new Uw(n.q,R(),n,r)}));else{if(t().d!==k)throw new w(k);m=h.b()?O().c:h.o()}c=zx(a);m=JF(lv(),m,it());m=new zv(a,m,V(a));d=d.gb;if(g===u())g=u();else{h=g.e();k=h=new z(h.hb,u());for(g=g.f();g!==u();)b=g.e(),b=new z(b.hb,u()),k=k.p=b,g=g.f();g=h}g=new fw(a,d,g,V(a));return yx(c,e,new cv(a,m,g,V(a)))}return Ey(a)}function Vy(a,b){pz();var c=u();c=qz(c);return new XX(a,efa(a,b,c),Aq(Bq(),c))} function of(a,b,c,d,e,g){var h=a.qa;if(a.F){var k=ut(Q(),"| ",a.r)+(d.da+". Typing ")+b;ff(gf(),k+"\n")}a.r=1+a.r|0;try{for(var l=c.b(),m=ef(b);!m.b();){var n=m.e();if(n instanceof Zn){k=n;if(k.wd.b())if(c.b())v=!1;else var r=c.o().CB(),v=Ot(new E(r),g_());else v=!1;v&&Lw(a,Ye(new Te(new Ue(J(new K,["Cannot use `val` or `fun` in local block; use `let` instead."]))),u()),k.A(),e)}m=m.f()}var x=Xu().X(),A=ef(b);Od();var B=new fp;Od();var C=new fp;for(b=A;!b.b();){var D=b.e();if(D instanceof Ct){m=D; t();var F=new fe(m)}else t(),F=new Ud(D);if(F instanceof fe)wp(B,F.aa);else if(F instanceof Ud)wp(C,F.fa);else throw new w(F);b=b.f()}var I=B.ha(),M=C.ha(),N=Xu().X();B=va=>{if(va instanceof Zn){var Ra=va.Rb;if(va.Yc instanceof Ud)return rx(va)&&Lw(a,We(Xe(),"`let` bindings must have a right-hand side"),va.A(),e),N.ou(Ra.x,new y(rb=>{if(rb instanceof L)return rb=rb.k,Lw(a,We(Xe(),"A type signature for '"+Ra.x+"' was already given"),va.A(),e),t(),new L(rb);if(t().d===rb)return t(),new L(va);throw new w(rb); })),!1}return!0};a:for(var P;;)if(I.b()){P=u();break}else{var T=I.e(),Y=I.f();if(!1===!!B(T))I=Y;else for(T=I;;){if(Y.b())P=T;else{var Z=Y.e();if(!1!==!!B(Z)){Y=Y.f();continue}Z=Y;var S=new z(T.e(),u()),ea=T.f();for(Y=S;ea!==Z;){var ia=new z(ea.e(),u());Y=Y.p=ia;ea=ea.f()}var X=Z.f();for(ea=X;!X.b();){var sa=X.e();if(!1===!!B(sa)){for(;ea!==X;){var Ja=new z(ea.e(),u());Y=Y.p=Ja;ea=ea.f()}ea=X.f()}X=X.f()}ea.b()||(Y.p=ea);P=S}break a}}var Xa=l?N.ry(new y(va=>{if(null!==va){va=va.j();var Ra=new fx(a, va,g,d,e),rb=va.hj;rb.b()||(rb=rb.o().x,Wx(d,G(new H,rb,Ra)));return G(new H,va.Cf.x,Ra)}throw new w(va);})):O().c;S=va=>{if(null!==va){if(va instanceof Zn){up(tp(),va.kx.b());var Ra=N.U(va.Rb.x);if(Ra instanceof L){Ra=Ra.k;var rb=va.wd,xb=va.Rb,mc=va.hj,Ha=va.Ch,Ka=va.Yc,Oa=va.Pz,Na=va.lx,Da=va.Om;t();var ta=new Zn(rb,xb,mc,Ha,Ka,Oa,Na,Da,new L(Ra),c,va.Pl,va.Oz)}else ta=new Zn(va.wd,va.Rb,va.hj,va.Ch,va.Yc,va.Pz,va.lx,va.Om,va.kx,c,va.Pl,va.Oz)}else{if(!(va instanceof yo))throw new w(va);Xca(new E(va.gb.V), a.tP)&&(Ra=new Te(new Ue(J(new K,["Type name '","' is reserved"]))),rb=[We(Xe(),va.gb.V)],Lw(a,Ye(Ra,J(new K,rb)),va.A(),e));ta=va}var Ya=new fx(a,ta,g,d,e);if(ta instanceof yo)va=d.$a,Ra=G(new H,ta.gb.V,Ya),va.$(Ra);else if(ta instanceof Zn)va=ta.hj,va.b()||(va=va.o().x,Wx(d,G(new H,va,Ya)));else throw new w(ta);x.ou(ta.Cf.x,new y(dc=>{if(dc instanceof L){if(!(ta instanceof Zn&&ta.wd instanceof L)){dc=new Te(new Ue(J(new K,["Redefinition of '","'"])));var ka=[We(Xe(),ta.Cf.x)];Lw(a,Ye(dc,J(new K, ka)),ta.A(),e)}t();return new L(Ya)}if(t().d===dc)return t(),new L(Ya);throw new w(dc);}));return G(new H,ta.Cf.x,Ya)}throw new w(va);};if(P===u())var Fa=u();else{var za=P.e(),Qa=new z(S(za),u());za=Qa;for(var Ma=P.f();Ma!==u();){var Ga=Ma.e(),ab=new z(S(Ga),u());za=za.p=ab;Ma=Ma.f()}Fa=Qa}Jw(d,Fa);Jw(d,Xa);var Hb=ry(lv(),Xa,new y(va=>{var Ra=!1,rb=null;va=Kx(va,e);if(va instanceof bx&&(Ra=!0,rb=va,!rb.Mb.Pz.b()))return a.La;if(Ra)return rb.Nn();xm("Program reached and unexpected state.")}));op(); var bc=pp(qp(),Hb);lv();var yb=un(Fa,Xa),tb=ry(0,yb,new y(va=>{va=Kx(va,e);if(va instanceof bx){var Ra=bc.U(va.Ua());if(Ra instanceof L){Ra=Ra.k;var rb=va.Mb.A();rb=jx(new kx,a,rb,cy(va.Mb),(tx(a),t().d),(tx(a),!1));Nea(a,va.Nn(),Ra,d,e,rb)}else Ra=va.Ua(),rb=new qx(a,va.Nn(),va.Mb.Rb),Wx(d,G(new H,Ra,rb)),Ra=va.Mb.hj,Ra.b()?Ra=R():(Ra=Ra.o(),Ra=new L(Ra.x)),Ra.b()||(Ra=Ra.o(),rb=new qx(a,va.Nn(),va.Mb.Rb),Wx(d,G(new H,Ra,rb)))}return new ix(a,va)}));Jw(d,tb);if(c.b())var eb=R();else{var kb=c.o(); eb=new L(kb.CB())}b:if(t().d===eb)var Rb=!0;else{if(eb instanceof L){var Gb=eb.k;if(g_()===Gb||hx()===Gb){Rb=!0;break b}}Rb=!1}if(Rb)var vb=!0;else if(eb instanceof L&&eb.k instanceof WS)vb=!1;else throw new w(eb);var Tb=new y(va=>": "+va);if(a.F){var Nb=ut(Q(),"| ",a.r)+"Typing unit statements";ff(gf(),Nb+"\n")}a.r=1+a.r|0;try{var ic=ffa(a,M,d,e,g,l,vb)}finally{a.r=-1+a.r|0}if(dx(new E(Tb),a.qa)&&a.F){var Va=""+ut(Q(),"| ",a.r)+Tb.n(ic);ff(gf(),Va+"\n")}if(tb===u())var cb=u();else{var zb=tb.e(), Ub=new z(zb.j().kh,u());l=Ub;for(var jb=tb.f();jb!==u();){var db=jb.e(),ub=new z(db.j().kh,u());l=l.p=ub;jb=jb.f()}cb=Ub}var Aa=new By(a,cb,ic)}finally{a.r=-1+a.r|0}dx(new E(h),a.qa)&&a.F&&(h=""+ut(Q(),"| ",a.r)+h.n(Aa),ff(gf(),h+"\n"));return Aa} function Jx(a,b,c,d,e){var g=Xu().X(),h=c.x;c=b.Ag();if(d.b()){var k=b.Ag();b=n=>{if(null!==n){n=n.hb;var r=n.ji;t();var v=new L(n),x=n.kg,A=O().c,B=O().c;return new lx(a,n.Xa,A,B,v,x,!1,r)}throw new w(n);};if(k===u())b=u();else{d=k.e();var l=d=new z(b(d),u());for(k=k.f();k!==u();){var m=k.e();m=new z(b(m),u());l=l.p=m;k=k.f()}b=d}}else b=d.o();c=new Wq(c,c,b);b=new fn((n,r)=>{n=G(new H,n,r);var v=n.y;r=n.w;if(null!==v){n=v.kc;v=v.hb;if(r instanceof lx){if(a.F){var x=ut(Q(),"| ",a.r)+("Passing "+ n.V+" :: "+v+" \x3c\x3d\x3c ")+r;ff(gf(),x+"\n")}}else{a.F&&(x=ut(Q(),"| ",a.r)+("Assigning "+n.V+" :: "+v+" :\x3d "+r+" where ")+xx(r),ff(gf(),x+"\n"));x=v.ji;t();var A=new L(v),B=v.kg,C=vy(v),D=rA(v),F=r.Ea();x=new lx(a,F,C,D,A,B,!1,x);a.F&&(A=ut(Q(),"| ",a.r)+("Set "+x+" ~\x3e ")+v,ff(gf(),A+"\n"));up(tp(),x.Sb.b());if(!vy(x).b())throw new Yj("assertion failed: "+vy(x));if(!rA(x).b())throw new Yj("assertion failed: "+rA(x));wy(x,(t(),new L(r)));r=x}v=G(new H,v,r);g.$(v);v=h+"#"+n.V;t();n=new Vw(a, n,new Uw(a,new L(r),r,V(a)),!0,e.da);return G(new H,v,n)}throw new w(n);});Yq();c=Ew(c,b);op();c=pp(qp(),c);return G(new H,g,c)}function Fx(){}Fx.prototype=new eW;Fx.prototype.constructor=Fx;f=Fx.prototype;f.pj=function(a,b){return a instanceof Zn&&!rx(a)?a.Rb:b.n(a)};f.rj=function(a){return a instanceof Zn&&!rx(a)?!0:!1};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)}; f.$classData=q({rX:0},!1,"mlscript.NuTypeDefs$DelayedTypeInfoImpl$$anonfun$$nestedInanonfun$allFields$4$1",{rX:1,Kf:1,g:1,la:1,za:1,l:1});function sx(){}sx.prototype=new eW;sx.prototype.constructor=sx;f=sx.prototype;f.pj=function(a,b){if(a instanceof Zn){var c=a.wd,d=a.Yc;c=t().d===c?!0:c instanceof L&&!1===!!c.k?!0:!1;if(c&&d instanceof fe)return a}return b.n(a)};f.rj=function(a){if(a instanceof Zn){var b=a.wd;a=a.Yc;b=t().d===b?!0:b instanceof L&&!1===!!b.k?!0:!1;if(b&&a instanceof fe)return!0}return!1}; f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)};f.$classData=q({sX:0},!1,"mlscript.NuTypeDefs$DelayedTypeInfoImpl$$anonfun$2",{sX:1,Kf:1,g:1,la:1,za:1,l:1});function Zy(a){this.FN=null;if(null===a)throw null;this.FN=a}Zy.prototype=new eW;Zy.prototype.constructor=Zy;Zy.prototype.Lc=function(a){return a instanceof Vw||a instanceof bx}; Zy.prototype.Ob=function(a,b){if(a instanceof Vw)b=a.ij.Zr(),a=G(new H,b,a.ig);else if(a instanceof bx){b=a.hh;a=a.Mb.Rb;var c=V(this.FN.J);b=new Uw(b.q,R(),b,c);a=G(new H,a,b)}else a=b.n(a);return a};Zy.prototype.$classData=q({tX:0},!1,"mlscript.NuTypeDefs$DelayedTypeInfoImpl$$anonfun$3",{tX:1,Kf:1,g:1,la:1,za:1,l:1});function L_(){}L_.prototype=new eW;L_.prototype.constructor=L_;L_.prototype.Lc=function(a){return null!==a&&a.Rd instanceof L?!0:!1}; L_.prototype.Ob=function(a,b){a:{if(null!==a){var c=a.hb,d=a.Rd;if(d instanceof L){a=G(new H,c,d.k);break a}}a=b.n(a)}return a};L_.prototype.$classData=q({BX:0},!1,"mlscript.NuTypeDefs$TypedNuCls$$anonfun$1",{BX:1,Kf:1,g:1,la:1,za:1,l:1});function Zr(a){this.qx=a}Zr.prototype=new Wz;Zr.prototype.constructor=Zr;f=Zr.prototype;f.H=function(){return"OPEN_BRACKET"};f.G=function(){return 1};f.I=function(a){return 0===a?this.qx:$K(W(),a)};f.D=function(a){return a instanceof Zr};f.B=function(){return AL(this)}; f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof Zr?this.qx===a.qx:!1};f.$classData=q({IX:0},!1,"mlscript.OPEN_BRACKET",{IX:1,yk:1,g:1,E:1,v:1,l:1});function gz(){}gz.prototype=new eW;gz.prototype.constructor=gz;f=gz.prototype;f.vJ=function(a,b){if(a instanceof Pl){var c=a.Za,d=a.Qb,e=c.A();if(e.b())e=!1;else{e=e.o();var g=d.A();g.b()?e=!1:(g=g.o(),e=e.fh>g.fh)}if(e)return G(new H,c,d)}return b.n(a)}; f.OJ=function(a){if(a instanceof Pl){var b=a.Qb;a=a.Za.A();a.b()?b=!1:(a=a.o(),b=b.A(),b.b()?b=!1:(b=b.o(),b=a.fh>b.fh));if(b)return!0}return!1};f.Lc=function(a){return this.OJ(a)};f.Ob=function(a,b){return this.vJ(a,b)};f.$classData=q({KX:0},!1,"mlscript.OpApp$$anonfun$unapply$1",{KX:1,Kf:1,g:1,la:1,za:1,l:1});function cf(a){this.NN=null;this.$H=!1;this.Xz=a}cf.prototype=new p;cf.prototype.constructor=cf;f=cf.prototype; f.nv=function(){if(!this.$H&&!this.$H){for(var a=TE(PE()),b=this.Xz,c=null,d=null;b!==u();){var e=b.e().nv();if(null===e)throw new w(e);var g=e.j();a.zc(e.h());for(e=g.m();e.s();)g=new z(e.t(),u()),null===d?c=g:d.p=g,d=g;b=b.f()}d=null===c?u():c;Od();b=new fp;Od();for(c=new fp;!d.b();){e=d.e();if(e instanceof Oo)t(),e=new fe(e);else if(e&&e.$classData&&e.$classData.rb.ce)t(),e=new Ud(e);else{if(!(e instanceof Zn))throw e instanceof Po&&xm("Program reached and unexpected state."),new w(e);var h=e; e=h.wd;g=h.Rb;h=h.Yc;t();e=new No(!(!e.b()&&!e.o()),g,h,e.b());e=new Ud(e)}if(e instanceof fe)wp(b,e.aa);else if(e instanceof Ud)wp(c,e.fa);else throw new w(e);d=d.f()}b=G(new H,b.ha(),c.ha());a=a.ha();this.NN=G(new H,a,b);this.$H=!0}return this.NN};f.H=function(){return"Pgrm"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Xz:$K(W(),a)};f.D=function(a){return a instanceof cf};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof cf){var b=this.Xz;a=a.Xz;return null===b?null===a:b.i(a)}return!1};f.$classData=q({NX:0},!1,"mlscript.Pgrm",{NX:1,g:1,Faa:1,E:1,v:1,l:1});function Bz(a,b){this.zE=a;this.yE=b}Bz.prototype=new Gz;Bz.prototype.constructor=Bz;f=Bz.prototype;f.Ua=function(){return this.zE};f.dQ=function(a){return this.yE.n(a)};f.H=function(){return"BuiltinFunc"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.zE;case 1:return this.yE;default:return $K(W(),a)}};f.D=function(a){return a instanceof Bz};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Bz&&this.zE===a.zE){var b=this.yE;a=a.yE;return null===b?null===a:b.i(a)}return!1};f.$classData=q({SX:0},!1,"mlscript.Polyfill$BuiltinFunc",{SX:1,TX:1,g:1,E:1,v:1,l:1});function wz(a,b){this.BE=a;this.AE=b}wz.prototype=new Gz; wz.prototype.constructor=wz;f=wz.prototype;f.Ua=function(){return this.BE};f.dQ=function(a){return this.AE.n(a)};f.H=function(){return"RuntimeHelper"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.BE;case 1:return this.AE;default:return $K(W(),a)}};f.D=function(a){return a instanceof wz};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof wz&&this.BE===a.BE){var b=this.AE;a=a.AE;return null===b?null===a:b.i(a)}return!1}; f.$classData=q({UX:0},!1,"mlscript.Polyfill$RuntimeHelper",{UX:1,TX:1,g:1,E:1,v:1,l:1});function I_(){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0}I_.prototype=new ZS;I_.prototype.constructor=I_;function M_(){}M_.prototype=I_.prototype;I_.prototype.Vj=function(){return EP(this)};function yn(a){0===(1&a.Qe)<<24>>24&&0===(1&a.Qe)<<24>>24&&(a.Dg=hea(a),a.Qe=(1|a.Qe)<<24>>24);return a.Dg} function zn(a){0===(2&a.Qe)<<24>>24&&0===(2&a.Qe)<<24>>24&&(a.Eg=iea(a),a.Qe=(2|a.Qe)<<24>>24);return a.Eg}function HP(){}HP.prototype=new eW;HP.prototype.constructor=HP;f=HP.prototype;f.nr=function(a,b){if(null!==a){var c=a.j();if(null!==c&&(c=c.ya,c instanceof Cl))return c.Fm}return b.n(a)};f.sr=function(a){return null!==a&&(a=a.j(),null!==a&&a.ya instanceof Cl)?!0:!1};f.Lc=function(a){return this.sr(a)};f.Ob=function(a,b){return this.nr(a,b)}; f.$classData=q({yY:0},!1,"mlscript.TypeLikeImpl$$anonfun$1",{yY:1,Kf:1,g:1,la:1,za:1,l:1});function IP(){}IP.prototype=new eW;IP.prototype.constructor=IP;f=IP.prototype;f.pj=function(a,b){return a instanceof Ct?a:b.n(a)};f.rj=function(a){return a instanceof Ct};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)};f.$classData=q({zY:0},!1,"mlscript.TypeLikeImpl$$anonfun$2",{zY:1,Kf:1,g:1,la:1,za:1,l:1});function CP(a,b){this.WN=b}CP.prototype=new eW; CP.prototype.constructor=CP;f=CP.prototype; f.pj=function(a,b){if(a instanceof Po){b=a.ks;a=yP(this.WN);var c=b.Ra;b=h=>{if(null!==h){var k=new L(h);if(!k.b()&&(h=k.k.h(),k=k.k.j(),t().d===h&&null!==k)){h=k.yb;k=k.ya;var l=tm().Cg;if((null===l?null===h:l.i(h))&&k instanceof Cl&&(h=k.ai,k=k.Fm,h instanceof vl))return h.x+": "+yf(k,0,this.WN)}}xm("ill-formed constructor parameter")};if(c===u())b=u();else{var d=c.e(),e=d=new z(b(d),u());for(c=c.f();c!==u();){var g=c.e();g=new z(b(g),u());e=e.p=g;c=c.f()}b=d}return a+"constructor("+ze(b,"",", ", "")+")\n"}return b.n(a)};f.rj=function(a){return a instanceof Po};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)};f.$classData=q({AY:0},!1,"mlscript.TypeLikeImpl$$anonfun$showIn$38",{AY:1,Kf:1,g:1,la:1,za:1,l:1});function DP(){}DP.prototype=new eW;DP.prototype.constructor=DP;f=DP.prototype;f.pj=function(a,b){return a instanceof Ct?a:b.n(a)};f.rj=function(a){return a instanceof Ct};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)}; f.$classData=q({BY:0},!1,"mlscript.TypeLikeImpl$$anonfun$showIn$39",{BY:1,Kf:1,g:1,la:1,za:1,l:1});function bB(){}bB.prototype=new eW;bB.prototype.constructor=bB;f=bB.prototype;f.wJ=function(a,b){if(a instanceof nC){var c=a.rp;if(null!==c)return a=c.x,Nu(Q(),a)}return b.n(a)};f.PJ=function(a){return a instanceof nC&&null!==a.rp?!0:!1};f.Lc=function(a){return this.PJ(a)};f.Ob=function(a,b){return this.wJ(a,b)};f.$classData=q({EY:0},!1,"mlscript.TypeSimplifier$$anonfun$1",{EY:1,Kf:1,g:1,la:1,za:1,l:1}); function wA(a){this.jA=null;if(null===a)throw null;this.jA=a}wA.prototype=new eW;wA.prototype.constructor=wA;f=wA.prototype; f.nr=function(a,b){if(null!==a){var c=a.h(),d=a.j();if(null!==c){iC(this.jA);var e=c.Sb;if(!e.b()&&(e=e.o(),d instanceof L)){if(d.k)return a=G(new H,!0,e),G(new H,a,c);a=G(new H,!1,e);return G(new H,a,c)}}}if(null!==a&&(c=a.h(),d=a.j(),d instanceof L)){if(d.k){a=vy(c);for(b=this.jA.ib;!a.b();)d=a.e(),e=V(b.q),b=dv(b,d,e,!1),a=a.f();a=G(new H,!0,b);return G(new H,a,c)}a=rA(c);for(b=this.jA.La;!a.b();)d=a.e(),e=V(b.q),b=sA(b,d,e),a=a.f();a=G(new H,!1,b);return G(new H,a,c)}return b.n(a)}; f.sr=function(a){if(null!==a){var b=a.h(),c=a.j();if(null!==b&&(iC(this.jA),!b.Sb.b()&&c instanceof L))return!0}return null!==a&&a.j()instanceof L?!0:!1};f.Lc=function(a){return this.sr(a)};f.Ob=function(a,b){return this.nr(a,b)};f.$classData=q({FY:0},!1,"mlscript.TypeSimplifier$$anonfun$2",{FY:1,Kf:1,g:1,la:1,za:1,l:1});function N_(a,b){this.ZN=b}N_.prototype=new eW;N_.prototype.constructor=N_;N_.prototype.Lc=function(a){return a instanceof qx}; N_.prototype.Ob=function(a,b){a instanceof qx?(b=a.tp,a=a.vA,up(tp(),!this.ZN.kt.b()),this.ZN.kt=a.kt,a=b):a=b.n(a);return a};N_.prototype.$classData=q({LY:0},!1,"mlscript.Typer$$anonfun$$nestedInanonfun$typeTerm$2$1",{LY:1,Kf:1,g:1,la:1,za:1,l:1});function O_(){}O_.prototype=new eW;O_.prototype.constructor=O_;f=O_.prototype;f.vJ=function(a,b){if(a instanceof vl)return a;if(a instanceof Cl){var c=a.ai;if(c instanceof vl)return c}return b.n(a)}; f.OJ=function(a){return a instanceof vl||a instanceof Cl&&a.ai instanceof vl?!0:!1};f.Lc=function(a){return this.OJ(a)};f.Ob=function(a,b){return this.vJ(a,b)};f.$classData=q({MY:0},!1,"mlscript.Typer$$anonfun$1",{MY:1,Kf:1,g:1,la:1,za:1,l:1});function P_(a,b,c,d,e,g){this.nI=this.oI=this.lI=null;this.qI=!1;this.mI=this.pI=null;if(null===a)throw null;this.lI=a;this.oI=b;this.nI=c;this.qI=d;this.pI=e;this.mI=g}P_.prototype=new eW;P_.prototype.constructor=P_;f=P_.prototype; f.nr=function(a,b){if(null!==a){var c=a.j();if(c instanceof bx)return Q_(this.lI,c,this.oI,this.nI,this.qI,this.pI,this.mI)}return null!==a&&(c=a.j(),c instanceof Cx)?Q_(this.lI,c,this.oI,this.nI,this.qI,this.pI,this.mI):b.n(a)};f.sr=function(a){return null!==a&&a.j()instanceof bx||null!==a&&a.j()instanceof Cx?!0:!1};f.Lc=function(a){return this.sr(a)};f.Ob=function(a,b){return this.nr(a,b)};f.$classData=q({NY:0},!1,"mlscript.Typer$$anonfun$mkTypingUnit$1$1",{NY:1,Kf:1,g:1,la:1,za:1,l:1}); function R_(a){this.go=this.lh=this.Ga=null;this.Ix=this.Zm=!1;this.Wu=null;jx(this,a,t().d,"expression",(tx(a),t().d),(tx(a),!1))}R_.prototype=new rY;R_.prototype.constructor=R_;R_.prototype.u=function(){return"[NO PROV]"};R_.prototype.$classData=q({RY:0},!1,"mlscript.Typer$NoProv$",{RY:1,VO:1,g:1,E:1,v:1,l:1});function vC(){}vC.prototype=new eW;vC.prototype.constructor=vC;f=vC.prototype;f.zJ=function(a,b){if(a instanceof L){var c=a.k;if(c instanceof qx&&(c=c.tp,null!==c))return DB(c)}return b.n(a)}; f.SJ=function(a){return a instanceof L&&(a=a.k,a instanceof qx&&null!==a.tp)?!0:!1};f.Lc=function(a){return this.SJ(a)};f.Ob=function(a,b){return this.zJ(a,b)};f.$classData=q({TY:0},!1,"mlscript.Typer$ValidPatVar$$anonfun$unapply$3",{TY:1,Kf:1,g:1,la:1,za:1,l:1});function wC(a,b,c){this.dO=this.Ex=this.cO=null;if(null===a)throw null;this.cO=a;this.Ex=b;this.dO=c}wC.prototype=new eW;wC.prototype.constructor=wC;f=wC.prototype; f.zJ=function(a,b){if(a instanceof L){var c=a.k;if(c instanceof Mu&&(c=c.pd,c instanceof vl&&this.Ex.x===c.x)){a=this.cO.rI;b=new Te(new Ue(J(new K,["Variable name '","' already names a symbol in scope. "])));c=[We(Xe(),this.Ex.x)];JX(a,PX(PX(Ye(b,J(new K,c)),We(Xe(),"If you want to refer to that symbol, you can use `scope."+this.Ex.x+"`; ")),We(Xe(),"if not, give your future readers a break and use another name :^)")),this.Ex.A(),this.dO);return}}return b.n(a)}; f.SJ=function(a){return a instanceof L&&(a=a.k,a instanceof Mu&&(a=a.pd,a instanceof vl&&this.Ex.x===a.x))?!0:!1};f.Lc=function(a){return this.SJ(a)};f.Ob=function(a,b){return this.zJ(a,b)};f.$classData=q({UY:0},!1,"mlscript.Typer$ValidPatVar$$anonfun$unapply$4",{UY:1,Kf:1,g:1,la:1,za:1,l:1});function hX(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}hX.prototype=new qY;hX.prototype.constructor=hX;function S_(){}S_.prototype=hX.prototype; function ZB(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}ZB.prototype=new qY;ZB.prototype.constructor=ZB;function T_(){}T_.prototype=ZB.prototype;ZB.prototype.Ea=function(){return this.mc().Ea()};ZB.prototype.ub=function(a,b){return this.mc().ub(a,b)};ZB.prototype.u=function(){return"["+this.mc()+"]"};function qx(a,b,c){this.J=null;this.tp=b;this.vA=c;FC(this,a)}qx.prototype=new HC;qx.prototype.constructor=qx;f=qx.prototype;f.H=function(){return"VarSymbol"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.tp;case 1:return this.vA;default:return $K(W(),a)}};f.D=function(a){return a instanceof qx};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof qx&&a.J===this.J){var b=this.tp,c=a.tp;if(null===b?null===c:mC(b,c))return b=this.vA,a=a.vA,null===b?null===a:b.i(a)}return!1};f.$classData=q({MZ:0},!1,"mlscript.TyperDatatypes$VarSymbol",{MZ:1,UO:1,g:1,E:1,v:1,l:1});function aQ(){}aQ.prototype=new eW; aQ.prototype.constructor=aQ;f=aQ.prototype;f.pj=function(a,b){return a instanceof Zn&&a.Yc.tv()?a.Rb.x:b.n(a)};f.rj=function(a){return a instanceof Zn&&a.Yc.tv()?!0:!1};f.Lc=function(a){return this.rj(a)};f.Ob=function(a,b){return this.pj(a,b)};f.$classData=q({$Z:0},!1,"mlscript.TypingUnitImpl$$anonfun$3",{$Z:1,Kf:1,g:1,la:1,za:1,l:1});function YE(a,b,c,d){this.lf=null;this.bv=a;this.cv=b;this.Ux=c;this.I_=d;qE(this)}YE.prototype=new sE;YE.prototype.constructor=YE;f=YE.prototype;f.Cv=function(){return this.I_}; f.u=function(){return"\u00ab"+this.bv+" \x3d "+this.cv+"\u00bb"+tE(this)};f.H=function(){return"Binding"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.bv;case 1:return this.cv;case 2:return this.Ux;default:return $K(W(),a)}};f.D=function(a){return a instanceof YE};f.B=function(){var a=lb("Binding");a=W().C(-889275714,a);var b=this.bv;b=My(W(),b);a=W().C(a,b);b=this.cv;b=My(W(),b);a=W().C(a,b);b=this.Ux?1231:1237;a=W().C(a,b);return W().Ma(a,3)}; f.i=function(a){if(this===a)return!0;if(a instanceof YE&&this.Ux===a.Ux){var b=this.bv,c=a.bv;if(null===b?null===c:b.i(c))return b=this.cv,a=a.cv,null===b?null===a:b.i(a)}return!1};f.$classData=q({H_:0},!1,"mlscript.ucs.Clause$Binding",{H_:1,VA:1,g:1,E:1,v:1,l:1});function XE(a,b){this.lf=null;this.Vx=a;this.K_=b;qE(this)}XE.prototype=new sE;XE.prototype.constructor=XE;f=XE.prototype;f.Cv=function(){return this.K_};f.u=function(){return"\u00ab"+this.Vx+"\u00bb"+tE(this)};f.H=function(){return"BooleanTest"}; f.G=function(){return 1};f.I=function(a){return 0===a?this.Vx:$K(W(),a)};f.D=function(a){return a instanceof XE};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof XE){var b=this.Vx;a=a.Vx;return null===b?null===a:b.i(a)}return!1};f.$classData=q({J_:0},!1,"mlscript.ucs.Clause$BooleanTest",{J_:1,VA:1,g:1,E:1,v:1,l:1});function WE(a,b,c,d){this.lf=null;this.ev=a;this.dv=b;this.ay=c;this.UP=d;qE(this)}WE.prototype=new sE;WE.prototype.constructor=WE;f=WE.prototype; f.Cv=function(){return this.UP};f.u=function(){return"\u00ab"+this.ev+" is Tuple#"+this.dv+"\u00bb"+tE(this)};f.H=function(){return"MatchTuple"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.ev;case 1:return this.dv;case 2:return this.ay;default:return $K(W(),a)}};f.D=function(a){return a instanceof WE}; f.B=function(){var a=lb("MatchTuple");a=W().C(-889275714,a);var b=this.ev;b=My(W(),b);a=W().C(a,b);b=this.dv;a=W().C(a,b);b=this.ay;b=My(W(),b);a=W().C(a,b);return W().Ma(a,3)};f.i=function(a){if(this===a)return!0;if(a instanceof WE&&this.dv===a.dv){var b=this.ev,c=a.ev;if(null===b?null===c:b.i(c))return b=this.ay,a=a.ay,null===b?null===a:b.i(a)}return!1};f.$classData=q({P_:0},!1,"mlscript.ucs.Clause$MatchTuple",{P_:1,VA:1,g:1,E:1,v:1,l:1});function $E(){}$E.prototype=new zE; $E.prototype.constructor=$E;f=$E.prototype;f.u=function(){return"pattern destruction"};f.H=function(){return"FieldExtraction"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof $E};f.B=function(){return 1536009313};f.$classData=q({U_:0},!1,"mlscript.ucs.LetBinding$Kind$FieldExtraction$",{U_:1,WP:1,g:1,E:1,v:1,l:1});var ZE;function U_(){}U_.prototype=new zE;U_.prototype.constructor=U_;f=U_.prototype;f.u=function(){return"interleaved let"};f.H=function(){return"InterleavedLet"}; f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof U_};f.B=function(){return 1258608914};f.$classData=q({V_:0},!1,"mlscript.ucs.LetBinding$Kind$InterleavedLet$",{V_:1,WP:1,g:1,E:1,v:1,l:1});var V_;function KE(){V_||(V_=new U_);return V_}function W_(){}W_.prototype=new zE;W_.prototype.constructor=W_;f=W_.prototype;f.u=function(){return"scrutinee alias"};f.H=function(){return"ScrutineeAlias"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)}; f.D=function(a){return a instanceof W_};f.B=function(){return-1538996278};f.$classData=q({W_:0},!1,"mlscript.ucs.LetBinding$Kind$ScrutineeAlias$",{W_:1,WP:1,g:1,E:1,v:1,l:1});var X_;function aF(){X_||(X_=new W_);return X_}function FE(a,b){this.pt=null;this.Tj=a;this.pl=b;this.pt=jA().X()}FE.prototype=new iF;FE.prototype.constructor=FE;f=FE.prototype;f.xt=function(){return this.pl};f.oQ=function(){var a=this.Tj.j().ia();return VE(new FE(G(new H,this.Tj.iy(),a),this.pl.or()),this.pt)}; f.FF=function(a,b){var c=!1,d=null;if(a instanceof Dl)return!1;if(a instanceof vl){c=!0;d=a;var e=d.x;if("true"===e||"false"===e)return!1}if(c&&d.x===this.Tj.h().x)return!0;if(c){a=b.U(d.x);if(a instanceof L)return a.k.L(this.Tj.h().x);if(R()===a)return!1;throw new w(a);}throw new w(a);};function Y_(a,b){var c=a.Tj.j();b=b.m();b=new iy(b,new y(d=>!a.Tj.j().L(d)),!1);c.zc(b)}f.H=function(){return"Constructor"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.Tj;case 1:return this.pl;default:return $K(W(),a)}};f.D=function(a){return a instanceof FE};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof FE){var b=this.Tj,c=a.Tj;if(null===b?null===c:b.i(c))return b=this.pl,a=a.pl,null===b?null===a:b.i(a)}return!1};f.$classData=q({c0:0},!1,"mlscript.ucs.MutCaseOf$MutCase$Constructor",{c0:1,b0:1,g:1,E:1,v:1,l:1}); function EE(a,b){this.pt=null;this.ir=a;this.Jp=b;this.pt=jA().X()}EE.prototype=new iF;EE.prototype.constructor=EE;f=EE.prototype;f.xt=function(){return this.Jp};f.oQ=function(){return VE(new EE(this.ir,this.Jp.or()),this.pt)};f.FF=function(a){var b=a instanceof Dl?!0:a instanceof vl&&"true"===a.x?!0:a instanceof vl&&"false"===a.x?!0:!1;if(b)return Pe(new E(a),this.ir);if(a instanceof vl)return!1;throw new w(a);};f.H=function(){return"Literal"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.ir;case 1:return this.Jp;default:return $K(W(),a)}};f.D=function(a){return a instanceof EE};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof EE){var b=this.ir,c=a.ir;if(null===b?null===c:b.i(c))return b=this.Jp,a=a.Jp,null===b?null===a:b.i(a)}return!1};f.$classData=q({d0:0},!1,"mlscript.ucs.MutCaseOf$MutCase$Literal",{d0:1,b0:1,g:1,E:1,v:1,l:1});function Z_(){$_=this;O()}Z_.prototype=new kF; Z_.prototype.constructor=Z_;f=Z_.prototype;f.oo=function(a){var b=O().c;return new a0(a,new z(a,b))};f.H=function(){return"Empty"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof Z_};f.B=function(){return 67081517};f.u=function(){return"Empty"};f.$classData=q({e0:0},!1,"mlscript.ucs.PartialTerm$Empty$",{e0:1,YP:1,g:1,E:1,v:1,l:1});var $_;function b0(){$_||($_=new Z_);return $_}function c0(a,b,c){this.uF=a;this.vF=b;this.$A=c}c0.prototype=new kF; c0.prototype.constructor=c0;f=c0.prototype;f.oo=function(a,b){var c=rF(tF(),a,b);if(null===c)throw new w(c);a=c.h();c=c.j();a=pF(tF(),this.uF,this.vF,a,b);if(t().d===c)return new a0(a,this.$A);if(c instanceof L)return c=c.k,b=pF(tF(),a,new vl("and"),c,b),new a0(b,new z(c,this.$A));throw new w(c);};f.H=function(){return"Half"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.uF;case 1:return this.vF;case 2:return this.$A;default:return $K(W(),a)}}; f.D=function(a){return a instanceof c0};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof c0){var b=this.uF,c=a.uF;if(null===b?null===c:b.i(c))if(b=this.vF,c=a.vF,null===b?null===c:b.i(c))return b=this.$A,a=a.$A,null===b?null===a:b.i(a)}return!1};f.$classData=q({f0:0},!1,"mlscript.ucs.PartialTerm$Half",{f0:1,YP:1,g:1,E:1,v:1,l:1});function a0(a,b){this.jr=a;this.aB=b}a0.prototype=new kF;a0.prototype.constructor=a0;f=a0.prototype; f.oo=function(a){throw new vY("expect an operator but term "+a+" was given");};function d0(a,b){return new c0(a.jr,b,new z(b,a.aB))}f.H=function(){return"Total"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.jr;case 1:return this.aB;default:return $K(W(),a)}};f.D=function(a){return a instanceof a0};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof a0){var b=this.jr,c=a.jr;if(null===b?null===c:b.i(c))return b=this.aB,a=a.aB,null===b?null===a:b.i(a)}return!1};f.$classData=q({g0:0},!1,"mlscript.ucs.PartialTerm$Total",{g0:1,YP:1,g:1,E:1,v:1,l:1});function pQ(){}pQ.prototype=new zY;pQ.prototype.constructor=pQ;pQ.prototype.n=function(a){return a};pQ.prototype.u=function(){return"generalized constraint"}; pQ.prototype.$classData=q({V2:0},!1,"scala.$less$colon$less$$anon$1",{V2:1,cba:1,dba:1,g:1,la:1,l:1});class w extends iW{constructor(a){super();this.kR=null;this.FK=!1;this.fG=a;yF(this,null,null,!0)}qj(){if(!this.FK&&!this.FK){if(null===this.fG)var a="null";else try{a=nb(this.fG)+" (of class "+na(this.fG)+")"}catch(b){a="an instance of class "+na(this.fG)}this.kR=a;this.FK=!0}return this.kR}}w.prototype.$classData=q({Z2:0},!1,"scala.MatchError",{Z2:1,Te:1,qd:1,pc:1,g:1,l:1});function e0(){} e0.prototype=new p;e0.prototype.constructor=e0;function f0(){}f=f0.prototype=e0.prototype;f.b=function(){return this===R()};f.Q=function(){return this.b()?0:1};f.L=function(a){return!this.b()&&ml(nl(),this.o(),a)};f.m=function(){if(this.b())return Rq().Pa;Rq();var a=this.o();return new g0(a)};f.ha=function(){if(this.b()){O();var a=u();return Pd(u(),a)}return new z(this.o(),O().c)};function G(a,b,c){a.y=b;a.w=c;return a}function H(){this.w=this.y=null}H.prototype=new p;H.prototype.constructor=H; function h0(){}f=h0.prototype=H.prototype;f.G=function(){return 2};f.I=function(a){a:switch(a){case 0:a=this.h();break a;case 1:a=this.j();break a;default:throw aL(new bL,a+" is out of bounds (min 0, max 1)");}return a};f.h=function(){return this.y};f.j=function(){return this.w};f.u=function(){return"("+this.h()+","+this.j()+")"};f.Cw=function(){return G(new H,this.j(),this.h())};f.iy=function(){return this.h()};f.H=function(){return"Tuple2"};f.D=function(a){return a instanceof H};f.B=function(){return AL(this)}; f.i=function(a){return this===a?!0:a instanceof H?ml(nl(),this.h(),a.h())&&ml(nl(),this.j(),a.j()):!1};f.Rc=function(){return!!this.h()};f.aQ=function(){return Ea(this.h())};f.ut=function(){return this.h()|0};f.cy=function(){return!!this.j()};f.Sc=function(){return this.j()|0};f.$classData=q({by:0},!1,"scala.Tuple2",{by:1,g:1,LB:1,E:1,v:1,l:1});function tl(a,b,c){this.kc=a;this.hb=b;this.Rd=c}tl.prototype=new p;tl.prototype.constructor=tl;f=tl.prototype;f.G=function(){return 3}; f.I=function(a){a:switch(a){case 0:a=this.kc;break a;case 1:a=this.hb;break a;case 2:a=this.Rd;break a;default:throw aL(new bL,a+" is out of bounds (min 0, max 2)");}return a};f.u=function(){return"("+this.kc+","+this.hb+","+this.Rd+")"};f.H=function(){return"Tuple3"};f.D=function(a){return a instanceof tl};f.B=function(){return AL(this)};f.i=function(a){return this===a?!0:a instanceof tl?ml(nl(),this.kc,a.kc)&&ml(nl(),this.hb,a.hb)&&ml(nl(),this.Rd,a.Rd):!1}; f.$classData=q({J0:0},!1,"scala.Tuple3",{J0:1,g:1,hba:1,E:1,v:1,l:1});function Gp(a,b,c,d){this.Uj=a;this.oj=b;this.oi=c;this.Xi=d}Gp.prototype=new p;Gp.prototype.constructor=Gp;f=Gp.prototype;f.G=function(){return 4};f.I=function(a){return qea(this,a)};f.u=function(){return"("+this.Uj+","+this.oj+","+this.oi+","+this.Xi+")"};f.H=function(){return"Tuple4"};f.D=function(a){return a instanceof Gp};f.B=function(){return AL(this)}; f.i=function(a){return this===a?!0:a instanceof Gp?ml(nl(),this.Uj,a.Uj)&&ml(nl(),this.oj,a.oj)&&ml(nl(),this.oi,a.oi)&&ml(nl(),this.Xi,a.Xi):!1};f.$classData=q({K0:0},!1,"scala.Tuple4",{K0:1,g:1,iba:1,E:1,v:1,l:1});function WX(a,b,c,d,e){this.hv=a;this.qt=b;this.kr=c;this.iv=d;this.jv=e}WX.prototype=new p;WX.prototype.constructor=WX;f=WX.prototype;f.G=function(){return 5};f.I=function(a){return rea(this,a)};f.u=function(){return"("+this.hv+","+this.qt+","+this.kr+","+this.iv+","+this.jv+")"}; f.H=function(){return"Tuple5"};f.D=function(a){return a instanceof WX};f.B=function(){return AL(this)};f.i=function(a){return this===a?!0:a instanceof WX?ml(nl(),this.hv,a.hv)&&ml(nl(),this.qt,a.qt)&&ml(nl(),this.kr,a.kr)&&ml(nl(),this.iv,a.iv)&&ml(nl(),this.jv,a.jv):!1};f.$classData=q({L0:0},!1,"scala.Tuple5",{L0:1,g:1,jba:1,E:1,v:1,l:1});function bo(a,b,c,d,e,g){this.lr=a;this.mr=b;this.rt=c;this.st=d;this.tt=e;this.kv=g}bo.prototype=new p;bo.prototype.constructor=bo;f=bo.prototype;f.G=function(){return 6}; f.I=function(a){return sea(this,a)};f.u=function(){return"("+this.lr+","+this.mr+","+this.rt+","+this.st+","+this.tt+","+this.kv+")"};f.H=function(){return"Tuple6"};f.D=function(a){return a instanceof bo};f.B=function(){return AL(this)};f.i=function(a){return this===a?!0:a instanceof bo?ml(nl(),this.lr,a.lr)&&ml(nl(),this.mr,a.mr)&&ml(nl(),this.rt,a.rt)&&ml(nl(),this.st,a.st)&&ml(nl(),this.tt,a.tt)&&ml(nl(),this.kv,a.kv):!1};f.$classData=q({M0:0},!1,"scala.Tuple6",{M0:1,g:1,kba:1,E:1,v:1,l:1}); function i0(a){this.RB=a}i0.prototype=new sT;i0.prototype.constructor=i0;i0.prototype.$classData=q({U4:0},!1,"scala.collection.ClassTagSeqFactory$AnySeqDelegate",{U4:1,Mba:1,g:1,bg:1,l:1,Nk:1});function j0(){this.Bl=null;this.Bl=xe()}j0.prototype=new MY;j0.prototype.constructor=j0;j0.prototype.$classData=q({X4:0},!1,"scala.collection.IndexedSeq$",{X4:1,FG:1,g:1,Nk:1,bg:1,l:1});var k0;function l0(a,b,c){for(a=a.Sd();a.s();)b=c.ba(a.t(),b);return b} function n0(a,b){return a.Ub().Ib(o0(new p0,b,a))}function q0(a,b){return a.vc(r0(new s0,a,b))}function t0(a,b){return a.Ub().Ib(u0(new v0,a,b))}function SF(a){return a.va(-1+a.K()|0)}function w0(a){return ze(a,a.Ih()+"(",", ",")")}function Fs(a){return!!(a&&a.$classData&&a.$classData.rb.na)}function xo(a,b){this.zy=null;this.Lv=0;this.GR=this.XK=null;if(null===a)throw null;this.XK=a;this.GR=b;this.zy=Rq().Pa;this.Lv=-1}xo.prototype=new BY;xo.prototype.constructor=xo; xo.prototype.s=function(){if(-1===this.Lv){for(;!this.zy.s();){if(!this.XK.s())return this.Lv=0,this.zy=Rq().Pa,!1;this.zy=null;this.zy=this.GR.n(this.XK.t()).m();this.Lv=-1}this.Lv=1;return!0}return 1===this.Lv};xo.prototype.t=function(){this.s()&&(this.Lv=-1);return this.zy.t()};xo.prototype.$classData=q({e5:0},!1,"scala.collection.Iterator$$anon$10",{e5:1,Sa:1,g:1,Ka:1,M:1,N:1});function is(a,b){this.YK=null;this.g5=b;this.qG=!1;this.ZK=a}is.prototype=new BY;is.prototype.constructor=is; is.prototype.s=function(){return this.qG?!0:this.ZK.s()?(this.YK=this.ZK.t(),this.g5.n(this.YK)?this.qG=!0:this.ZK=Rq().Pa,this.qG):!1};is.prototype.t=function(){return this.s()?(this.qG=!1,this.YK):Rq().Pa.t()};is.prototype.$classData=q({f5:0},!1,"scala.collection.Iterator$$anon$11",{f5:1,Sa:1,g:1,Ka:1,M:1,N:1});function x0(a,b){this.rG=this.sG=null;if(null===a)throw null;this.rG=a;this.sG=b.m()}x0.prototype=new BY;x0.prototype.constructor=x0;f=x0.prototype; f.Q=function(){var a=this.rG.Q(),b=this.sG.Q();return aa.Co)return-1;a=a.Co-b|0;return 0>a?0:a} function PT(a,b,c){this.By=a;this.Co=c;this.Nv=b}PT.prototype=new BY;PT.prototype.constructor=PT;f=PT.prototype;f.Q=function(){var a=this.By.Q();if(0>a)return-1;a=a-this.Nv|0;a=0>a?0:a;if(0>this.Co)return a;var b=this.Co;return bthis.Co?this.By.t():Rq().Pa.t()}; f.qk=function(a,b){a=0b)b=A0(this,a);else if(b<=a)b=0;else if(0>this.Co)b=b-a|0;else{var c=A0(this,a);b=b-a|0;b=cb)throw aL(new bL,""+b);a=a.Jc(b);if(a.b())throw aL(new bL,""+b);return a.e()}function YA(a,b,c){for(;!a.b();)b=c.ba(b,a.e()),a=a.f();return b} function G0(a,b){if(b&&b.$classData&&b.$classData.rb.CG)a:for(;;){if(a===b){a=!0;break a}if((a.b()?0:!b.b())&&ml(nl(),a.e(),b.e()))a=a.f(),b=b.f();else{a=a.b()&&b.b();break a}}else a=TY(a,b);return a}function H0(a,b,c){var d=0{if(ca(c)!==da(Ky)){var r=c.U(n);if(r instanceof L)r=r.k;else if(R()===r)r=c.Xf,Ly(c,n,r,!1);else throw new w(r);}else{r=My(W(),n);r^=r>>>16|0;var v=r&(-1+c.eb.a.length|0),x=c.eb.a[v];x=null===x?null:Ny(x,n,r);if(null!==x)r=x.Ah;else{x=c.eb;var A=c.Xf;(1+c.Xf|0)>=c.hu&&Oy(c,c.eb.a.length<<1);Py(c,n,A,!1,r,x===c.eb?v:r&(-1+c.eb.a.length|0));r=A}}return G(new H,n,r)}));var d=new y(n=>n.Sc()),e= Fq();b=QY(b,d,e);d=op();d=b.Gb(d.ga);if(null===d)throw new w(d);b=d.h();d=d.j();var g=new Xc(c.Xf);d.Ca(new y(n=>{n|=0;g.a[n]=1+g.a[n]|0}));d=new Xc(g.a.length);e=0;e=a.U5;var h=d.a.length;a=-1+h|0;if(!(0>=h))for(h=0;;){var k=h,l=e,m=g.a[k];d.a[k]=l=b))for(b=0;;){var d=b,e=this.vm.a[d],g=-1+e|0;if(!(0>=e))for(e=0;;){var h=this.ZR.va(this.$R.a[d]+e|0);a.$(h);if(e===g)break;e=1+e|0}if(b===c)break;b=1+b|0}a=a.Kb();for(b=-1+this.vm.a.length|0;0<=b&&this.vm.a[b]===this.HG.a[b];)b=-1+b|0;c=this.vm;b=-1+b|0;a:{d=-1+c.a.length|0;for(b=bb)this.GG=!1;else{c=1;for(d=1+b|0;d=g))for(;;){g=d;e=c;h=this.HG.a[g];this.vm.a[g]=eb.m()));return a.vc(c)}function K0(){this.Mt=null}K0.prototype=new uT;K0.prototype.constructor=K0;function L0(){}L0.prototype=K0.prototype; function Om(a){this.QG=a}Om.prototype=new BY;Om.prototype.constructor=Om;Om.prototype.s=function(){return!this.QG.b()};Om.prototype.t=function(){var a=this.QG.e();this.QG=this.QG.f();return a};Om.prototype.$classData=q({h6:0},!1,"scala.collection.StrictOptimizedLinearSeqOps$$anon$1",{h6:1,Sa:1,g:1,Ka:1,M:1,N:1});function wH(a,b){this.SG=a;this.m6=b;this.Jy=a.length;this.cj=0}wH.prototype=new BY;wH.prototype.constructor=wH;wH.prototype.s=function(){return this.cj=a.Jy)a=Rq().Pa.t();else{for(var b=a.cj;;){if(a.cja?a:256);this.Uph)throw P0();if(h>c.a.length)throw P0();d=new Xc(1+c.a.length|0);c.wa(0,d,0,h);d.a[h]=e;c.wa(h,d,1+h|0,c.a.length-h|0);b.Db|=l;b.te=a;b.Vg=d;b.kd=1+b.kd|0;b.ui=b.ui+g|0}}else if(b instanceof oU)e=KU(b,c),b.of=0>e?b.of.hn(G(new H,c,d)):b.of.$r(e,G(new H,c,d));else throw new w(b);}function UU(a){if(0===a.Nr.kd)return Ez().Mr;null===a.gC&&(a.gC=new TU(a.Nr));return a.gC} function Q0(a,b){O0(a);var c=b.h();c=My(W(),c);var d=$G(bH(),c);SQ(a,a.Nr,b.h(),b.j(),c,d,0);return a}function R0(a,b,c){O0(a);var d=My(W(),b);SQ(a,a.Nr,b,c,d,$G(bH(),d),0);return a}function VU(a,b){O0(a);if(b instanceof TU)new RQ(a,b);else if(b instanceof DV)for(b=S0(b);b.s();){var c=b.t(),d=c.mk;d^=d>>>16|0;var e=$G(bH(),d);SQ(a,a.Nr,c.Wk,c.Ah,d,e,0)}else if(jV(b))b.og(new fn((g,h)=>R0(a,g,h)));else for(b=b.m();b.s();)Q0(a,b.t());return a}f.zc=function(a){return VU(this,a)}; f.$=function(a){return Q0(this,a)};f.Kb=function(){return UU(this)};f.$classData=q({Z6:0},!1,"scala.collection.immutable.HashMapBuilder",{Z6:1,g:1,Po:1,xg:1,xf:1,wf:1});function cV(){this.Or=this.$v=null;this.Or=new CJ(0,0,kG().EK,kG().KB,0,0)}cV.prototype=new p;cV.prototype.constructor=cV;f=cV.prototype;f.he=function(){}; function UQ(a,b,c,d,e,g){if(b instanceof CJ){var h=kI(HH(),e,g),k=lI(HH(),h);if(0!==(b.db&k)){h=oI(HH(),b.db,h,k);a=b.ed(h);var l=b.Jb(h);l===d&&ml(nl(),a,c)?(d=b.pi(k),b.Uc.a[d]=a):(h=$G(bH(),l),d=zU(b,a,l,h,c,d,e,5+g|0),CU(b,k,h,d))}else if(0!==(b.Pb&k))k=oI(HH(),b.Pb,h,k),k=b.Nh(k),h=k.ka(),l=k.uc(),UQ(a,k,c,d,e,5+g|0),b.Fb=b.Fb+(k.ka()-h|0)|0,b.Ve=b.Ve+(k.uc()-l|0)|0;else{g=b.pi(k);h=b.Uc;a=new zc(1+h.a.length|0);h.wa(0,a,0,g);a.a[g]=c;h.wa(g,a,1+g|0,h.a.length-g|0);c=b.Ud;if(0>g)throw P0();if(g> c.a.length)throw P0();h=new Xc(1+c.a.length|0);c.wa(0,h,0,g);h.a[g]=d;c.wa(g,h,1+g|0,c.a.length-g|0);b.db|=k;b.Uc=a;b.Ud=h;b.Fb=1+b.Fb|0;b.Ve=b.Ve+e|0}}else if(b instanceof FU)d=Xea(b.Kg,c),b.Kg=0>d?b.Kg.hn(c):b.Kg.$r(d,c);else throw new w(b);}function aV(a){if(0===a.Or.Fb)return dV().Fo;null===a.$v&&(a.$v=new ZU(a.Or));return a.$v}function T0(a,b){null!==a.$v&&(a.Or=IU(a.Or));a.$v=null;var c=My(W(),b),d=$G(bH(),c);UQ(a,a.Or,b,c,d,0);return a} function bV(a,b){null!==a.$v&&(a.Or=IU(a.Or));a.$v=null;if(b instanceof ZU)new TQ(a,b);else for(b=b.m();b.s();)T0(a,b.t());return a}f.zc=function(a){return bV(this,a)};f.$=function(a){return T0(this,a)};f.Kb=function(){return aV(this)};f.$classData=q({c7:0},!1,"scala.collection.immutable.HashSetBuilder",{c7:1,g:1,Po:1,xg:1,xf:1,wf:1});function U0(){this.Bl=null;this.Bl=GK()}U0.prototype=new MY;U0.prototype.constructor=U0;function we(a,b){return V0(b)?b:LY.prototype.vl.call(a,b)} U0.prototype.Ib=function(a){return we(this,a)};U0.prototype.vl=function(a){return we(this,a)};U0.prototype.$classData=q({e7:0},!1,"scala.collection.immutable.IndexedSeq$",{e7:1,FG:1,g:1,Nk:1,bg:1,l:1});var W0;function xe(){W0||(W0=new U0);return W0}function lZ(){this.mS=this.Qy=null;this.mg()}lZ.prototype=new p;lZ.prototype.constructor=lZ;f=lZ.prototype;f.he=function(){};f.mg=function(){var a=new VH;FK();this.mS=new gZ(new U(()=>WH(a)));this.Qy=a}; function hfa(a){YH(a.Qy,new U(()=>hV()));return a.mS}function ifa(a,b){var c=new VH;YH(a.Qy,new U(()=>{FK();FK();return new eV(b,new gZ(new U(()=>WH(c))))}));a.Qy=c;return a}function jfa(a,b){if(0!==b.Q()){var c=new VH;YH(a.Qy,new U(()=>jZ(FK(),b.m(),new U(()=>WH(c)))));a.Qy=c}return a}f.zc=function(a){return jfa(this,a)};f.$=function(a){return ifa(this,a)};f.Kb=function(){return hfa(this)};f.$classData=q({j7:0},!1,"scala.collection.immutable.LazyList$LazyBuilder",{j7:1,g:1,Po:1,xg:1,xf:1,wf:1}); function X0(a){this.hC=a}X0.prototype=new BY;X0.prototype.constructor=X0;X0.prototype.s=function(){return!this.hC.b()};X0.prototype.t=function(){if(this.hC.b())return Rq().Pa.t();var a=hZ(this.hC).e();this.hC=hZ(this.hC).Lf();return a};X0.prototype.$classData=q({l7:0},!1,"scala.collection.immutable.LazyList$LazyIterator",{l7:1,Sa:1,g:1,Ka:1,M:1,N:1});function Y0(){this.iC=this.jC=null;Z0=this;this.jC=G(new H,u(),u());this.iC=new VQ}Y0.prototype=new p;Y0.prototype.constructor=Y0;f=Y0.prototype; f.Hh=function(a){return Pd(u(),a)};f.Eb=function(){return new fp};f.X=function(){return u()};f.Ib=function(a){return Pd(u(),a)};f.$classData=q({s7:0},!1,"scala.collection.immutable.List$",{s7:1,g:1,Qt:1,Nk:1,bg:1,l:1});var Z0;function Od(){Z0||(Z0=new Y0);return Z0}function $0(a,b){if(null===b)throw null;a.Vt=b;a.Yp=0}function a1(){this.Yp=0;this.Vt=null}a1.prototype=new BY;a1.prototype.constructor=a1;function b1(){}b1.prototype=a1.prototype;a1.prototype.s=function(){return 2>this.Yp}; a1.prototype.t=function(){switch(this.Yp){case 0:var a=this.wj(this.Vt.hk,this.Vt.xn);break;case 1:a=this.wj(this.Vt.ik,this.Vt.yn);break;default:a=Rq().Pa.t()}this.Yp=1+this.Yp|0;return a};a1.prototype.ph=function(a){this.Yp=this.Yp+a|0;return this};function c1(a,b){if(null===b)throw null;a.Zp=b;a.$p=0}function d1(){this.$p=0;this.Zp=null}d1.prototype=new BY;d1.prototype.constructor=d1;function e1(){}e1.prototype=d1.prototype;d1.prototype.s=function(){return 3>this.$p}; d1.prototype.t=function(){switch(this.$p){case 0:var a=this.wj(this.Zp.dj,this.Zp.Fl);break;case 1:a=this.wj(this.Zp.Mi,this.Zp.Qk);break;case 2:a=this.wj(this.Zp.Ni,this.Zp.Rk);break;default:a=Rq().Pa.t()}this.$p=1+this.$p|0;return a};d1.prototype.ph=function(a){this.$p=this.$p+a|0;return this};function f1(a,b){if(null===b)throw null;a.zn=b;a.aq=0}function g1(){this.aq=0;this.zn=null}g1.prototype=new BY;g1.prototype.constructor=g1;function h1(){}h1.prototype=g1.prototype; g1.prototype.s=function(){return 4>this.aq};g1.prototype.t=function(){switch(this.aq){case 0:var a=this.wj(this.zn.Th,this.zn.Aj);break;case 1:a=this.wj(this.zn.xh,this.zn.ej);break;case 2:a=this.wj(this.zn.Wg,this.zn.Oi);break;case 3:a=this.wj(this.zn.Xg,this.zn.Pi);break;default:a=Rq().Pa.t()}this.aq=1+this.aq|0;return a};g1.prototype.ph=function(a){this.aq=this.aq+a|0;return this};function lV(){this.bq=null;this.Sy=!1;this.Wt=null;this.bq=nf();this.Sy=!1}lV.prototype=new p; lV.prototype.constructor=lV;f=lV.prototype;f.he=function(){};function kV(a,b){return a.Sy?(VU(a.Wt,b),a):lR(a,b)}f.zc=function(a){return kV(this,a)};f.$=function(a){var b=a.h();a=a.j();if(this.Sy)R0(this.Wt,b,a);else if(4>this.bq.ka())this.bq=this.bq.Em(b,a);else if(this.bq.L(b))this.bq=this.bq.Em(b,a);else{this.Sy=!0;null===this.Wt&&(this.Wt=new WU);var c=this.bq;R0(R0(R0(R0(this.Wt,c.Th,c.Aj),c.xh,c.ej),c.Wg,c.Oi),c.Xg,c.Pi);R0(this.Wt,b,a)}return this}; f.Kb=function(){return this.Sy?UU(this.Wt):this.bq};f.$classData=q({J7:0},!1,"scala.collection.immutable.MapBuilderImpl",{J7:1,g:1,Po:1,xg:1,xf:1,wf:1});function i1(a){this.wn=this.Nc=0;this.wh=null;this.vi=0;this.Do=this.Ok=null;JH(this,a)}i1.prototype=new LH;i1.prototype.constructor=i1;f=i1.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)}; f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)}; f.Q=function(){return-1};f.t=function(){if(!this.s())throw LU();var a=this.wh.Ef(this.Nc);this.Nc=1+this.Nc|0;return a};f.Ja=function(a){return new Ef(this,a)};f.$classData=q({K7:0},!1,"scala.collection.immutable.MapKeyIterator",{K7:1,Vv:1,g:1,Ka:1,M:1,N:1});function j1(a){this.gk=0;this.Ut=null;this.Pk=0;this.Xv=this.Wv=null;this.LL=0;this.uS=null;OH(this,a);this.LL=0}j1.prototype=new QH;j1.prototype.constructor=j1;f=j1.prototype;f.m=function(){return this};f.b=function(){return!this.s()}; f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)};f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)}; f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)};f.Q=function(){return-1};f.B=function(){var a=BL(),b=this.uS;return zL(a,this.LL,My(W(),b))};f.Ja=function(a){return new Ef(this,a)};f.t=function(){if(!this.s())throw LU();this.LL=this.Ut.Jb(this.gk);this.uS=this.Ut.Tf(this.gk);this.gk=-1+this.gk|0;return this};f.$classData=q({L7:0},!1,"scala.collection.immutable.MapKeyValueTupleHashIterator",{L7:1,jS:1,g:1,Ka:1,M:1,N:1}); function k1(a){this.wn=this.Nc=0;this.wh=null;this.vi=0;this.Do=this.Ok=null;JH(this,a)}k1.prototype=new LH;k1.prototype.constructor=k1;f=k1.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)};f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)}; f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)};f.Q=function(){return-1};f.gm=function(){if(!this.s())throw LU();var a=this.wh.rv(this.Nc);this.Nc=1+this.Nc|0;return a}; f.Ja=function(a){return new Ef(this,a)};f.t=function(){return this.gm()};f.$classData=q({M7:0},!1,"scala.collection.immutable.MapKeyValueTupleIterator",{M7:1,Vv:1,g:1,Ka:1,M:1,N:1});function l1(a){this.gk=0;this.Ut=null;this.Pk=0;this.Xv=this.Wv=null;OH(this,a)}l1.prototype=new QH;l1.prototype.constructor=l1;f=l1.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)}; f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)}; f.Q=function(){return-1};f.gm=function(){if(!this.s())throw LU();var a=this.Ut.rv(this.gk);this.gk=-1+this.gk|0;return a};f.Ja=function(a){return new Ef(this,a)};f.t=function(){return this.gm()};f.$classData=q({N7:0},!1,"scala.collection.immutable.MapKeyValueTupleReverseIterator",{N7:1,jS:1,g:1,Ka:1,M:1,N:1});function m1(a){this.wn=this.Nc=0;this.wh=null;this.vi=0;this.Do=this.Ok=null;JH(this,a)}m1.prototype=new LH;m1.prototype.constructor=m1;f=m1.prototype;f.m=function(){return this};f.b=function(){return!this.s()}; f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)};f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)}; f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)};f.Q=function(){return-1};f.t=function(){if(!this.s())throw LU();var a=this.wh.Tf(this.Nc);this.Nc=1+this.Nc|0;return a};f.Ja=function(a){return new Ef(this,a)};f.$classData=q({R7:0},!1,"scala.collection.immutable.MapValueIterator",{R7:1,Vv:1,g:1,Ka:1,M:1,N:1}); function n1(a){a.xi<=a.Lg&&Rq().Pa.t();a.ew=1+a.ew|0;for(var b=a.wS.To(a.ew);0===b.a.length;)a.ew=1+a.ew|0,b=a.wS.To(a.ew);a.YG=a.Yt;var c=a.T7/2|0,d=a.ew-c|0;a.dw=(1+c|0)-(0>d?-d|0:d)|0;c=a.dw;switch(c){case 1:a.Go=b;break;case 2:a.aw=b;break;case 3:a.bw=b;break;case 4:a.cw=b;break;case 5:a.Uy=b;break;case 6:a.ML=b;break;default:throw new w(c);}a.Yt=a.YG+Math.imul(b.a.length,1<a.Bn&&(a.Yt=a.Bn);1c?a.Go=a.aw.a[31&(b>>>5|0)]:(32768>c?a.aw=a.bw.a[31&(b>>>10|0)]:(1048576>c?a.bw=a.cw.a[31&(b>>>15|0)]:(33554432>c?a.cw=a.Uy.a[31&(b>>>20|0)]:(a.Uy=a.ML.a[b>>>25|0],a.cw=a.Uy.a[0]),a.bw=a.cw.a[0]),a.aw=a.bw.a[0]),a.Go=a.aw.a[0]);a.kC=b}a.xi=a.xi-a.Lg|0;b=a.Go.a.length;c=a.xi;a.cq=bthis.Lg}; f.t=function(){this.Lg===this.cq&&o1(this);var a=this.Go.a[this.Lg];this.Lg=1+this.Lg|0;return a}; f.ph=function(a){if(0=this.Yt;)n1(this);b=a-this.YG|0;if(1c||(32768>c||(1048576>c||(33554432>c||(this.Uy=this.ML.a[b>>>25|0]),this.cw=this.Uy.a[31&(b>>>20|0)]),this.bw=this.cw.a[31&(b>>>15|0)]),this.aw=this.bw.a[31&(b>>>10|0)]);this.Go=this.aw.a[31&(b>>>5|0)];this.kC=b}this.cq=this.Go.a.length;this.Lg=31&b;this.xi=this.Lg+(this.Bn-a|0)|0;this.cq>this.xi&& (this.cq=this.xi)}}return this};f.Mn=function(a){a<(this.xi-this.Lg|0)&&(a=(this.xi-this.Lg|0)-(0>a?0:a)|0,this.Bn=this.Bn-a|0,this.xi=this.xi-a|0,this.xia.kw.ka())a.kw=a.kw.bc(b);else if(!a.kw.L(b)){a.oC=!0;null===a.lw&&(a.lw=new cV);var c=a.kw;a.lw.$(c.Ho).$(c.eq).$(c.fq).$(c.gq);T0(a.lw,b)}return a}function oV(a,b){return a.oC?(bV(a.lw,b),a):lR(a,b)}f.zc=function(a){return oV(this,a)};f.$=function(a){return NF(this,a)};f.Kb=function(){return MF(this)};f.$classData=q({s8:0},!1,"scala.collection.immutable.SetBuilderImpl",{s8:1,g:1,Po:1,xg:1,xf:1,wf:1}); function x1(a){this.wn=this.Nc=0;this.wh=null;this.vi=0;this.Do=this.Ok=null;this.NL=0;JH(this,a);this.NL=0}x1.prototype=new LH;x1.prototype.constructor=x1;f=x1.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)};f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)}; f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)};f.Q=function(){return-1};f.B=function(){return this.NL};f.t=function(){if(!this.s())throw LU();this.NL=this.wh.Jb(this.Nc);this.Nc=1+this.Nc|0;return this}; f.Ja=function(a){return new Ef(this,a)};f.$classData=q({t8:0},!1,"scala.collection.immutable.SetHashIterator",{t8:1,Vv:1,g:1,Ka:1,M:1,N:1});function y1(a){this.wn=this.Nc=0;this.wh=null;this.vi=0;this.Do=this.Ok=null;JH(this,a)}y1.prototype=new LH;y1.prototype.constructor=y1;f=y1.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)};f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)};f.qk=function(a,b){return OT(this,a,b)}; f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)};f.Q=function(){return-1}; f.t=function(){if(!this.s())throw LU();var a=this.wh.ed(this.Nc);this.Nc=1+this.Nc|0;return a};f.Ja=function(a){return new Ef(this,a)};f.$classData=q({u8:0},!1,"scala.collection.immutable.SetIterator",{u8:1,Vv:1,g:1,Ka:1,M:1,N:1});function z1(a){this.gk=0;this.Ut=null;this.Pk=0;this.Xv=this.Wv=null;OH(this,a)}z1.prototype=new QH;z1.prototype.constructor=z1;f=z1.prototype;f.m=function(){return this};f.b=function(){return!this.s()};f.nb=function(a){return kv(this,a)}; f.Mn=function(a){return NT(this,a)};f.ph=function(a){return OT(this,a,-1)};f.qk=function(a,b){return OT(this,a,b)};f.u=function(){return"\x3citerator\x3e"};f.Ca=function(a){cH(this,a)};f.De=function(a,b){return mB(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)}; f.Bj=function(a){return kB(this,a)};f.ad=function(){return er(this)};f.Q=function(){return-1};f.t=function(){if(!this.s())throw LU();var a=this.Ut.ed(this.gk);this.gk=-1+this.gk|0;return a};f.Ja=function(a){return new Ef(this,a)};f.$classData=q({x8:0},!1,"scala.collection.immutable.SetReverseIterator",{x8:1,jS:1,g:1,Ka:1,M:1,N:1}); function A1(){this.HS=0;this.IS=null;B1=this;try{var a=fh(gh(),"scala.collection.immutable.Vector.defaultApplyPreferredMaxLength","250");var b=TH(UH(),a,10)}catch(c){throw c;}this.HS=b;this.IS=new p1(HJ(),0,0)}A1.prototype=new p;A1.prototype.constructor=A1;f=A1.prototype;f.Hh=function(a){return pU(0,a)}; function pU(a,b){if(b instanceof C1)return b;a=b.Q();if(0===a)return HJ();if(0=a){a:{if(b instanceof Fu){var c=b.ag().uh();if(null!==c&&c===da(jd)){b=b.vn;break a}}ZJ(b)?(a=new zc(a),b.Gc(a,0,2147483647),b=a):(a=new zc(a),b.m().Gc(a,0,2147483647),b=a)}return new IJ(b)}return OU(new NU,b).im()}f.Eb=function(){return new NU};f.Ib=function(a){return pU(0,a)};f.X=function(){return HJ()};f.$classData=q({P8:0},!1,"scala.collection.immutable.Vector$",{P8:1,g:1,Qt:1,Nk:1,bg:1,l:1});var B1; function GK(){B1||(B1=new A1);return B1}function D1(a,b){var c=b.a.length;if(0h?-h|0:h)|0;1===g?D1(a,e):VJ(JJ(),-2+g|0,e,new y(k=>{D1(a,k)}));d=1+d|0}return a} function E1(a){var b=32+a.yi|0,c=b^a.yi;a.yi=b;a.Yd=0;if(1024>c)1===a.wg&&(a.sd=new (md(md(jd)).Ia)(32),a.sd.a[0]=a.af,a.wg=1+a.wg|0),a.af=new zc(32),a.sd.a[31&(b>>>5|0)]=a.af;else if(32768>c)2===a.wg&&(a.ue=new (md(md(md(jd))).Ia)(32),a.ue.a[0]=a.sd,a.wg=1+a.wg|0),a.af=new zc(32),a.sd=new (md(md(jd)).Ia)(32),a.sd.a[31&(b>>>5|0)]=a.af,a.ue.a[31&(b>>>10|0)]=a.sd;else if(1048576>c)3===a.wg&&(a.Jf=new (md(md(md(md(jd)))).Ia)(32),a.Jf.a[0]=a.ue,a.wg=1+a.wg|0),a.af=new zc(32),a.sd=new (md(md(jd)).Ia)(32), a.ue=new (md(md(md(jd))).Ia)(32),a.sd.a[31&(b>>>5|0)]=a.af,a.ue.a[31&(b>>>10|0)]=a.sd,a.Jf.a[31&(b>>>15|0)]=a.ue;else if(33554432>c)4===a.wg&&(a.Pg=new (md(md(md(md(md(jd))))).Ia)(32),a.Pg.a[0]=a.Jf,a.wg=1+a.wg|0),a.af=new zc(32),a.sd=new (md(md(jd)).Ia)(32),a.ue=new (md(md(md(jd))).Ia)(32),a.Jf=new (md(md(md(md(jd)))).Ia)(32),a.sd.a[31&(b>>>5|0)]=a.af,a.ue.a[31&(b>>>10|0)]=a.sd,a.Jf.a[31&(b>>>15|0)]=a.ue,a.Pg.a[31&(b>>>20|0)]=a.Jf;else if(1073741824>c)5===a.wg&&(a.Si=new (md(md(md(md(md(md(jd)))))).Ia)(64), a.Si.a[0]=a.Pg,a.wg=1+a.wg|0),a.af=new zc(32),a.sd=new (md(md(jd)).Ia)(32),a.ue=new (md(md(md(jd))).Ia)(32),a.Jf=new (md(md(md(md(jd)))).Ia)(32),a.Pg=new (md(md(md(md(md(jd))))).Ia)(32),a.sd.a[31&(b>>>5|0)]=a.af,a.ue.a[31&(b>>>10|0)]=a.sd,a.Jf.a[31&(b>>>15|0)]=a.ue,a.Pg.a[31&(b>>>20|0)]=a.Jf,a.Si.a[31&(b>>>25|0)]=a.Pg;else throw Kj("advance1("+b+", "+c+"): a1\x3d"+a.af+", a2\x3d"+a.sd+", a3\x3d"+a.ue+", a4\x3d"+a.Jf+", a5\x3d"+a.Pg+", a6\x3d"+a.Si+", depth\x3d"+a.wg);} function NU(){this.af=this.sd=this.ue=this.Jf=this.Pg=this.Si=null;this.wg=this.Gl=this.yi=this.Yd=0;this.af=new zc(32);this.Gl=this.yi=this.Yd=0;this.wg=1}NU.prototype=new p;NU.prototype.constructor=NU;f=NU.prototype;f.he=function(){};function lfa(a,b){a.wg=1;var c=b.a.length;a.Yd=31&c;a.yi=c-a.Yd|0;a.af=32===b.a.length?b:Jj(Pj(),b,0,32);0===a.Yd&&0=a){if(32===b)return new IJ(this.af);var c=this.af;return new IJ(zj(Pj(),c,b))}if(1024>=a){var d=31&(-1+a|0),e=(-1+a|0)>>>5|0,g=this.sd,h=Jj(Pj(),g,1,e),k=this.sd.a[0],l=this.sd.a[e],m=1+d|0,n=l.a.length===m?l:zj(Pj(),l,m);return new KJ(k,32-this.Gl|0,h,n,b)}if(32768>=a){var r=31&(-1+a|0),v=31&((-1+a|0)>>>5|0),x=(-1+a|0)>>>10|0,A=this.ue,B=Jj(Pj(),A,1,x),C=this.ue.a[0],D=C.a.length,F=Jj(Pj(),C,1,D),I=this.ue.a[0].a[0], M=this.ue.a[x],N=zj(Pj(),M,v),P=this.ue.a[x].a[v],T=1+r|0,Y=P.a.length===T?P:zj(Pj(),P,T),Z=I.a.length;return new LJ(I,Z,F,Z+(F.a.length<<5)|0,B,N,Y,b)}if(1048576>=a){var S=31&(-1+a|0),ea=31&((-1+a|0)>>>5|0),ia=31&((-1+a|0)>>>10|0),X=(-1+a|0)>>>15|0,sa=this.Jf,Ja=Jj(Pj(),sa,1,X),Xa=this.Jf.a[0],Fa=Xa.a.length,za=Jj(Pj(),Xa,1,Fa),Qa=this.Jf.a[0].a[0],Ma=Qa.a.length,Ga=Jj(Pj(),Qa,1,Ma),ab=this.Jf.a[0].a[0].a[0],Hb=this.Jf.a[X],bc=zj(Pj(),Hb,ia),yb=this.Jf.a[X].a[ia],tb=zj(Pj(),yb,ea),eb=this.Jf.a[X].a[ia].a[ea], kb=1+S|0,Rb=eb.a.length===kb?eb:zj(Pj(),eb,kb),Gb=ab.a.length,vb=Gb+(Ga.a.length<<5)|0;return new MJ(ab,Gb,Ga,vb,za,vb+(za.a.length<<10)|0,Ja,bc,tb,Rb,b)}if(33554432>=a){var Tb=31&(-1+a|0),Nb=31&((-1+a|0)>>>5|0),ic=31&((-1+a|0)>>>10|0),Va=31&((-1+a|0)>>>15|0),cb=(-1+a|0)>>>20|0,zb=this.Pg,Ub=Jj(Pj(),zb,1,cb),jb=this.Pg.a[0],db=jb.a.length,ub=Jj(Pj(),jb,1,db),Aa=this.Pg.a[0].a[0],va=Aa.a.length,Ra=Jj(Pj(),Aa,1,va),rb=this.Pg.a[0].a[0].a[0],xb=rb.a.length,mc=Jj(Pj(),rb,1,xb),Ha=this.Pg.a[0].a[0].a[0].a[0], Ka=this.Pg.a[cb],Oa=zj(Pj(),Ka,Va),Na=this.Pg.a[cb].a[Va],Da=zj(Pj(),Na,ic),ta=this.Pg.a[cb].a[Va].a[ic],Ya=zj(Pj(),ta,Nb),dc=this.Pg.a[cb].a[Va].a[ic].a[Nb],ka=1+Tb|0,ya=dc.a.length===ka?dc:zj(Pj(),dc,ka),Sa=Ha.a.length,xc=Sa+(mc.a.length<<5)|0,Sb=xc+(Ra.a.length<<10)|0;return new NJ(Ha,Sa,mc,xc,Ra,Sb,ub,Sb+(ub.a.length<<15)|0,Ub,Oa,Da,Ya,ya,b)}var uc=31&(-1+a|0),Lb=31&((-1+a|0)>>>5|0),lc=31&((-1+a|0)>>>10|0),Xb=31&((-1+a|0)>>>15|0),ec=31&((-1+a|0)>>>20|0),Ab=(-1+a|0)>>>25|0,Ob=this.Si,fb=Jj(Pj(), Ob,1,Ab),Wa=this.Si.a[0],bb=Wa.a.length,Ia=Jj(Pj(),Wa,1,bb),Ua=this.Si.a[0].a[0],pc=Ua.a.length,sc=Jj(Pj(),Ua,1,pc),Ba=this.Si.a[0].a[0].a[0],ob=Ba.a.length,nc=Jj(Pj(),Ba,1,ob),Ib=this.Si.a[0].a[0].a[0].a[0],vc=Ib.a.length,Vb=Jj(Pj(),Ib,1,vc),fc=this.Si.a[0].a[0].a[0].a[0].a[0],Bc=this.Si.a[Ab],Pb=zj(Pj(),Bc,ec),Jb=this.Si.a[Ab].a[ec],gc=zj(Pj(),Jb,Xb),Cb=this.Si.a[Ab].a[ec].a[Xb],cc=zj(Pj(),Cb,lc),yc=this.Si.a[Ab].a[ec].a[Xb].a[lc],Mc=zj(Pj(),yc,Lb),qc=this.Si.a[Ab].a[ec].a[Xb].a[lc].a[Lb],oc=1+ uc|0,Qc=qc.a.length===oc?qc:zj(Pj(),qc,oc),jc=fc.a.length,sb=jc+(Vb.a.length<<5)|0,Gc=sb+(nc.a.length<<10)|0,Wb=Gc+(sc.a.length<<15)|0;return new OJ(fc,jc,Vb,sb,nc,Gc,sc,Wb,Ia,Wb+(Ia.a.length<<20)|0,fb,Pb,gc,cc,Mc,Qc,b)};f.u=function(){return"VectorBuilder(len1\x3d"+this.Yd+", lenRest\x3d"+this.yi+", offset\x3d"+this.Gl+", depth\x3d"+this.wg+")"};f.Kb=function(){return this.im()};f.zc=function(a){return OU(this,a)};f.$=function(a){return PU(this,a)}; f.$classData=q({X8:0},!1,"scala.collection.immutable.VectorBuilder",{X8:1,g:1,Po:1,xg:1,xf:1,wf:1});function G1(){}G1.prototype=new p;G1.prototype.constructor=G1;f=G1.prototype;f.Hh=function(a){return RF(a)};function RF(a){var b=a.Q();if(0<=b){var c=new zc(16>>Math.clz32(b)|0)<<1;if(!(0<=a))throw Kj("requirement failed: ArrayDeque too big - cannot allocate ArrayDeque of length "+b);return new zc(16((b.Ke-b.Zd|0)&(-1+b.ec.a.length|0))&&a>=b.ec.a.length&&U1(b,a)};R1.prototype.$classData=q({i9:0},!1,"scala.collection.mutable.ArrayDeque$$anon$1",{i9:1,tC:1,g:1,xg:1,xf:1,wf:1});function V1(){this.Bl=null;this.Bl=W1()} V1.prototype=new MY;V1.prototype.constructor=V1;V1.prototype.$classData=q({u9:0},!1,"scala.collection.mutable.Buffer$",{u9:1,FG:1,g:1,Nk:1,bg:1,l:1});var X1;function PE(){X1||(X1=new V1);return X1}function EV(a,b){this.lk=null;PV(this,CV(new DV,a,b))}EV.prototype=new EZ;EV.prototype.constructor=EV;EV.prototype.he=function(a){this.lk.he(a)};EV.prototype.$classData=q({L9:0},!1,"scala.collection.mutable.HashMap$$anon$6",{L9:1,tC:1,g:1,xg:1,xf:1,wf:1}); function Y1(a,b){if(null===b)throw null;a.uw=b;a.hq=0;a.Lo=null;a.vw=b.eb.a.length}function Z1(){this.hq=0;this.Lo=null;this.vw=0;this.uw=null}Z1.prototype=new BY;Z1.prototype.constructor=Z1;function $1(){}$1.prototype=Z1.prototype;Z1.prototype.s=function(){if(null!==this.Lo)return!0;for(;this.hqg?d.Vc:d.Bc;e=0>=g?b:vK(0,b)}}a.iq=e;q2(a)}function s2(){this.iq=this.DC=this.fz=null}s2.prototype=new BY;s2.prototype.constructor=s2;function t2(){}t2.prototype=s2.prototype;s2.prototype.s=function(){return null!==this.iq}; s2.prototype.t=function(){var a=this.iq;if(null===a)throw AH("next on empty iterator");this.iq=vK(xK(),a);q2(this);return this.zK(a)};function u2(){}u2.prototype=new p;u2.prototype.constructor=u2;f=u2.prototype;f.Hh=function(a){return vU(new IX(16),a)};f.Eb=function(){return PV(new QV,new IX(16))};f.X=function(){return new IX(16)};f.Ib=function(a){return vU(new IX(16),a)};f.$classData=q({H$:0},!1,"scala.collection.mutable.Stack$",{H$:1,g:1,Qt:1,Nk:1,bg:1,l:1});var v2; function w2(){v2||(v2=new u2);return v2}function x2(a,b,c){return 0===a.Da(b,c)}function y2(a,b,c){return a.Yj(b,c)?b:c}function z2(a,b,c){return a.uj(b,c)?b:c}function A2(a,b){return b instanceof B2?(b=b.sn,null!==b&&b.i(a)):!1}var nfa=function mfa(a,b){return $f(b)?"Array["+mfa(a,bg(b))+"]":b.qi.name};class fX extends Iq{constructor(a){super();Hq(this,a,void 0)}Cj(){}}fX.prototype.$classData=q({baa:0},!1,"scala.runtime.NonLocalReturnControl$mcV$sp",{baa:1,lT:1,s4:1,pc:1,g:1,l:1}); function iD(a){this.nT=0;this.jaa=a;this.oH=0;this.nT=a.G()}iD.prototype=new BY;iD.prototype.constructor=iD;iD.prototype.s=function(){return this.oHJ(new K,a.Xr)))};f.Ib=function(a){return H2(this,a)}; f.X=function(){var a=new K;J(a,[]);return a};f.$classData=q({W$:0},!1,"scala.scalajs.runtime.WrappedVarArgs$",{W$:1,g:1,Qt:1,Nk:1,bg:1,l:1});var I2;function J2(){I2||(I2=new G2);return I2}function fe(a){this.aa=a}fe.prototype=new PZ;fe.prototype.constructor=fe;f=fe.prototype;f.TJ=function(){return!0};f.tv=function(){return!1};f.H=function(){return"Left"};f.G=function(){return 1};f.I=function(a){return 0===a?this.aa:$K(W(),a)};f.D=function(a){return a instanceof fe};f.B=function(){return AL(this)}; f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof fe?ml(nl(),this.aa,a.aa):!1};f.$classData=q({n4:0},!1,"scala.util.Left",{n4:1,l4:1,g:1,E:1,v:1,l:1});function Ud(a){this.fa=a}Ud.prototype=new PZ;Ud.prototype.constructor=Ud;f=Ud.prototype;f.TJ=function(){return!1};f.tv=function(){return!0};f.H=function(){return"Right"};f.G=function(){return 1};f.I=function(a){return 0===a?this.fa:$K(W(),a)};f.D=function(a){return a instanceof Ud};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){return this===a?!0:a instanceof Ud?ml(nl(),this.fa,a.fa):!1};var K2=q({p4:0},!1,"scala.util.Right",{p4:1,l4:1,g:1,E:1,v:1,l:1});Ud.prototype.$classData=K2;function Ne(a){this.Zl=a}Ne.prototype=new QL;Ne.prototype.constructor=Ne;f=Ne.prototype;f.H=function(){return"Line"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Zl:$K(W(),a)};f.D=function(a){return a instanceof Ne};f.B=function(){var a=lb("Line");a=W().C(-889275714,a);var b=this.Zl;a=W().C(a,b);return W().Ma(a,1)}; f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof Ne?this.Zl===a.Zl:!1};f.$classData=q({y0:0},!1,"sourcecode.Line",{y0:1,A0:1,g:1,E:1,v:1,l:1});function Oe(a){this.fn=a}Oe.prototype=new QL;Oe.prototype.constructor=Oe;f=Oe.prototype;f.H=function(){return"Name"};f.G=function(){return 1};f.I=function(a){return 0===a?this.fn:$K(W(),a)};f.D=function(a){return a instanceof Oe};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){return this===a?!0:a instanceof Oe?this.fn===a.fn:!1};f.$classData=q({z0:0},!1,"sourcecode.Name",{z0:1,A0:1,g:1,E:1,v:1,l:1});function P0(){var a=new dI;yF(a,null,null,!0);return a}class dI extends bL{}dI.prototype.$classData=q({P0:0},!1,"java.lang.ArrayIndexOutOfBoundsException",{P0:1,eK:1,Te:1,qd:1,pc:1,g:1,l:1});function mb(a){return ig(mg(),a)} var iaa=q({X0:0},!1,"java.lang.Double",{X0:1,ur:1,g:1,l:1,nf:1,Et:1,UF:1},a=>"number"===typeof a),haa=q({Z0:0},!1,"java.lang.Float",{Z0:1,ur:1,g:1,l:1,nf:1,Et:1,UF:1},a=>ja(a)),gaa=q({b1:0},!1,"java.lang.Integer",{b1:1,ur:1,g:1,l:1,nf:1,Et:1,UF:1},a=>ha(a)),kaa=q({g1:0},!1,"java.lang.Long",{g1:1,ur:1,g:1,l:1,nf:1,Et:1,UF:1},a=>a instanceof ma);class ZL extends SZ{constructor(a){super();yF(this,a,null,!0)}} ZL.prototype.$classData=q({p1:0},!1,"java.lang.NumberFormatException",{p1:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});function gk(a,b){return a.codePointAt(b)|0}function lb(a){for(var b=0,c=1,d=-1+a.length|0;0<=d;)b=b+Math.imul(a.charCodeAt(d),c)|0,c=Math.imul(31,c),d=-1+d|0;return b}function pa(a,b){for(var c=a.length,d=b.length,e=ca.length||0>b||0>b)throw a=new tH,yF(a,"Index out of Bound",null,!0),a;d=d-0|0;for(var e=0;e=m}else m=!1;if(m)k=1+k|0;else break}UH();l=g.substring(l,k);m=TH(0,l,10);l=d;var n=KN(l);l=l.lK;if(0>m||m>l.O2)throw aL(new bL,""+m);l=n[l.P2[m]|0];l=void 0!==l?l:null;null!==l&&kW(e,l);break;case 92:k= 1+k|0;k(b.length|0)&&LN(c);){if(0!==NN(c)){var e=MN(c);b.push(a.substring(d,e))}d=NN(c)}b.push(a.substring(d));for(c=b.length|0;;)if(0!==c?(a=b[-1+c|0],a=null!==a&&La(a,"")):a=!1,a)c=-1+c|0;else break;a=new (md(fa).Ia)(c);for(d=0;d"string"===typeof a);class tH extends bL{}tH.prototype.$classData=q({x1:0},!1,"java.lang.StringIndexOutOfBoundsException",{x1:1,eK:1,Te:1,qd:1,pc:1,g:1,l:1});class DM extends TZ{constructor(){super();yF(this,null,null,!0)}}DM.prototype.$classData=q({S1:0},!1,"java.util.FormatterClosedException",{S1:1,CQ:1,Te:1,qd:1,pc:1,g:1,l:1});function a_(a){this.VF=null;if(null===a)throw null;this.VF=a}a_.prototype=new XZ; a_.prototype.constructor=a_;a_.prototype.vv=function(){return new GN(this.VF)};a_.prototype.ka=function(){return this.VF.yv};a_.prototype.L=function(a){if(ck(a)){var b=this.VF,c=a.so();if(null===c)var d=0;else d=ib(c),d^=d>>>16|0;b=ZZ(b,c,d,d&(-1+b.wl.a.length|0));if(null!==b)return b=b.xv,a=a.to(),null===b?null===a:La(b,a)}return!1};a_.prototype.$classData=q({V1:0},!1,"java.util.HashMap$EntrySet",{V1:1,F1:1,D1:1,g:1,OQ:1,d1:1,v2:1});function c_(a){this.yB=null;if(null===a)throw null;this.yB=a} c_.prototype=new XZ;c_.prototype.constructor=c_;c_.prototype.vv=function(){return new FN(this)};c_.prototype.ka=function(){return this.yB.zv.yv};c_.prototype.L=function(a){if(ck(a)){var b=a.so();if(this.yB.EF(b))return b=this.yB.LF(b),Object.is(b,a.to())}return!1};c_.prototype.$classData=q({Z1:0},!1,"java.util.IdentityHashMap$EntrySet",{Z1:1,F1:1,D1:1,g:1,OQ:1,d1:1,v2:1});class N2 extends SZ{} class ek extends SZ{constructor(a,b,c){super();this.L2=a;this.N2=b;this.M2=c;yF(this,null,null,!0)}qj(){var a=this.M2,b=this.N2,c=this.L2+(0>a?"":" near index "+a)+"\n"+b;if(0<=a&&null!==b&&aa)throw UL();a=" ".repeat(a);c=c+"\n"+a+"^"}return c}}ek.prototype.$classData=q({K2:0},!1,"java.util.regex.PatternSyntaxException",{K2:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});function cs(a,b,c){this.Cc=a;this.td=b;this.Mf=c}cs.prototype=new Wz;cs.prototype.constructor=cs;f=cs.prototype;f.H=function(){return"BRACKETS"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Cc;case 1:return this.td;default:return $K(W(),a)}};f.D=function(a){return a instanceof cs};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof cs&&this.Cc===a.Cc){var b=this.td;a=a.td;return null===b?null===a:b.i(a)}return!1};f.$classData=q({UT:0},!1,"mlscript.BRACKETS",{UT:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});function O2(){}O2.prototype=new Wz; O2.prototype.constructor=O2;f=O2.prototype;f.H=function(){return"COMMA"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof O2};f.B=function(){return 64305845};f.u=function(){return"COMMA"};f.$classData=q({kU:0},!1,"mlscript.COMMA$",{kU:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});var P2;function Xr(){P2||(P2=new O2);return P2}function gs(a){this.PC=a}gs.prototype=new Wz;gs.prototype.constructor=gs;f=gs.prototype;f.H=function(){return"COMMENT"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.PC:$K(W(),a)};f.D=function(a){return a instanceof gs};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof gs?this.PC===a.PC:!1};f.$classData=q({lU:0},!1,"mlscript.COMMENT",{lU:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});function Q2(){}Q2.prototype=new Wz;Q2.prototype.constructor=Q2;f=Q2.prototype;f.H=function(){return"ERROR"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)}; f.D=function(a){return a instanceof Q2};f.B=function(){return 66247144};f.u=function(){return"ERROR"};f.$classData=q({MU:0},!1,"mlscript.ERROR$",{MU:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});var R2;function ts(){R2||(R2=new Q2);return R2}function Sn(a,b){this.UM=null;this.WM=this.XM=0;this.YM=this.VM=null;this.ms=0;this.Yf=a;this.Rg=b;Nq(this)}Sn.prototype=new p;Sn.prototype.constructor=Sn;f=Sn.prototype;f.Vj=function(){var a=this.Yf.ha(),b=this.Rg,c=O().c;return dl(new z(b,c),a)}; f.jn=function(){0===(1&this.ms)<<24>>24&&0===(1&this.ms)<<24>>24&&(this.UM=zq(this),this.ms=(1|this.ms)<<24>>24);return this.UM};f.rn=function(){return this.XM};f.fm=function(a){this.XM=a};f.qn=function(){return this.WM};f.em=function(a){this.WM=a};f.pn=function(){return this.VM};f.on=function(a){this.VM=a};f.A=function(){0===(2&this.ms)<<24>>24&&0===(2&this.ms)<<24>>24&&(this.YM=Dq(this),this.ms=(2|this.ms)<<24>>24);return this.YM};f.H=function(){return"Field"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.Yf;case 1:return this.Rg;default:return $K(W(),a)}};f.D=function(a){return a instanceof Sn};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Sn){var b=this.Yf,c=a.Yf;if(null===b?null===c:b.i(c))return b=this.Rg,a=a.Rg,null===b?null===a:b.i(a)}return!1};f.$classData=q({RU:0},!1,"mlscript.Field",{RU:1,g:1,yaa:1,Ta:1,E:1,v:1,l:1}); function sm(a,b){this.dN=null;this.fN=this.gN=0;this.hN=this.eN=null;this.os=0;this.yb=a;this.ya=b;Nq(this)}sm.prototype=new p;sm.prototype.constructor=sm;f=sm.prototype;f.Vj=function(){var a=this.ya,b=O().c;return new z(a,b)};f.jn=function(){0===(1&this.os)<<24>>24&&0===(1&this.os)<<24>>24&&(this.dN=zq(this),this.os=(1|this.os)<<24>>24);return this.dN};f.rn=function(){return this.gN};f.fm=function(a){this.gN=a};f.qn=function(){return this.fN};f.em=function(a){this.fN=a};f.pn=function(){return this.eN}; f.on=function(a){this.eN=a};f.A=function(){0===(2&this.os)<<24>>24&&0===(2&this.os)<<24>>24&&(this.hN=Dq(this),this.os=(2|this.os)<<24>>24);return this.hN};f.H=function(){return"Fld"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.yb;case 1:return this.ya;default:return $K(W(),a)}};f.D=function(a){return a instanceof sm};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof sm){var b=this.yb,c=a.yb;if(null===b?null===c:b.i(c))return b=this.ya,a=a.ya,null===b?null===a:b.i(a)}return!1};f.$classData=q({SU:0},!1,"mlscript.Fld",{SU:1,g:1,Aaa:1,Ta:1,E:1,v:1,l:1});function St(a,b,c){this.ZM=null;this.aN=this.bN=0;this.cN=this.$M=null;this.ns=0;this.je=a;this.ch=b;this.Bh=c;Nq(this)}St.prototype=new p;St.prototype.constructor=St;f=St.prototype;f.Vj=function(){return O().c}; f.u=function(){if(null===this)throw new w(this);var a=(this.je?"m":"")+(this.ch?"s":"")+(this.Bh?"g":"");return""===a?"_":a};f.jn=function(){0===(1&this.ns)<<24>>24&&0===(1&this.ns)<<24>>24&&(this.ZM=zq(this),this.ns=(1|this.ns)<<24>>24);return this.ZM};f.rn=function(){return this.bN};f.fm=function(a){this.bN=a};f.qn=function(){return this.aN};f.em=function(a){this.aN=a};f.pn=function(){return this.$M};f.on=function(a){this.$M=a}; f.A=function(){0===(2&this.ns)<<24>>24&&0===(2&this.ns)<<24>>24&&(this.cN=Dq(this),this.ns=(2|this.ns)<<24>>24);return this.cN};f.H=function(){return"FldFlags"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.je;case 1:return this.ch;case 2:return this.Bh;default:return $K(W(),a)}};f.D=function(a){return a instanceof St}; f.B=function(){var a=lb("FldFlags");a=W().C(-889275714,a);var b=this.je?1231:1237;a=W().C(a,b);b=this.ch?1231:1237;a=W().C(a,b);b=this.Bh?1231:1237;a=W().C(a,b);return W().Ma(a,3)};f.i=function(a){return this===a?!0:a instanceof St?this.je===a.je&&this.ch===a.ch&&this.Bh===a.Bh:!1};f.$classData=q({TU:0},!1,"mlscript.FldFlags",{TU:1,g:1,zaa:1,Ta:1,E:1,v:1,l:1});function bs(a,b){this.ae=a;this.ke=b}bs.prototype=new Wz;bs.prototype.constructor=bs;f=bs.prototype;f.H=function(){return"IDENT"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.ae;case 1:return this.ke;default:return $K(W(),a)}};f.D=function(a){return a instanceof bs};f.B=function(){var a=lb("IDENT");a=W().C(-889275714,a);var b=this.ae;b=My(W(),b);a=W().C(a,b);b=this.ke?1231:1237;a=W().C(a,b);return W().Ma(a,2)};f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof bs?this.ke===a.ke&&this.ae===a.ae:!1};f.$classData=q({XU:0},!1,"mlscript.IDENT",{XU:1,yk:1,g:1,Wm:1,E:1,v:1,l:1}); function Wo(a){this.bD=a}Wo.prototype=new fO;Wo.prototype.constructor=Wo;f=Wo.prototype;f.sh=function(){return 22};f.xa=function(){Qp();var a=this.bD;if(a===u())var b=u();else{b=a.e();var c=b=new z(Up(b,Vp().Az),u());for(a=a.f();a!==u();){var d=a.e();d=new z(Up(d,Vp().Az),u());c=c.p=d;a=a.f()}}return Uz(b)};f.H=function(){return"JSArray"};f.G=function(){return 1};f.I=function(a){return 0===a?this.bD:$K(W(),a)};f.D=function(a){return a instanceof Wo};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Wo){var b=this.bD;a=a.bD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({jV:0},!1,"mlscript.JSArray",{jV:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function Hl(a){this.aD=a}Hl.prototype=new oO;Hl.prototype.constructor=Hl;f=Hl.prototype;f.xa=function(){Qp();var a=this.aD;if(a===u())var b=u();else{b=a.e();var c=b=new z(b.xa(),u());for(a=a.f();a!==u();){var d=a.e();d=new z(d.xa(),u());c=c.p=d;a=a.f()}}return Uz(b)};f.H=function(){return"JSArrayPattern"}; f.G=function(){return 1};f.I=function(a){return 0===a?this.aD:$K(W(),a)};f.D=function(a){return a instanceof Hl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Hl){var b=this.aD;a=a.aD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({kV:0},!1,"mlscript.JSArrayPattern",{kV:1,BH:1,Xc:1,g:1,E:1,v:1,l:1});function oo(a,b){this.wz=a;this.cD=b}oo.prototype=new fO;oo.prototype.constructor=oo;f=oo.prototype;f.sh=function(){return 2}; f.xa=function(){var a=Rp(Pp(Hf(this.wz).De(Qp().ye,new fn((g,h)=>{var k=G(new H,g,h);g=k.y;h=k.w;if(null!==h)return k=h.h(),h=h.Sc(),Rp(Rp(g,k instanceof El?Sp(Qp(),"_"+h):k.xa()),Pe(new E(h),-1+this.wz.K()|0)?Qp().ye:Sp(Qp(),", "));throw new w(k);})),!0),Qp().SN),b=!1,c=null,d=this.cD;a:{if(d instanceof fe){b=!0;c=d;var e=c.aa;if(e instanceof vo){b=Pp(e.xa(),!0);break a}}if(b)b=Up(c.aa,2);else if(d instanceof Ud){d=d.fa;Qp();if(d===u())b=u();else for(b=d.e(),c=b=new z(b.xa(),u()),d=d.f();d!==u();)e= d.e(),e=new z(e.xa(),u()),c=c.p=e,d=d.f();b=Oz(Sz(b))}else throw new w(d);}return Rp(a,b)};f.H=function(){return"JSArrowFn"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.wz;case 1:return this.cD;default:return $K(W(),a)}};f.D=function(a){return a instanceof oo};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof oo){var b=this.wz,c=a.wz;if(null===b?null===c:b.i(c))return b=this.cD,a=a.cD,null===b?null===a:b.i(a)}return!1}; f.$classData=q({lV:0},!1,"mlscript.JSArrowFn",{lV:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function Lo(a,b){this.dD=a;this.eD=b}Lo.prototype=new fO;Lo.prototype.constructor=Lo;f=Lo.prototype;f.sh=function(){return 3};f.xa=function(){return Rp(Rp(Up(this.dD,3),Sp(Qp()," \x3d ")),Up(this.eD,3))};f.H=function(){return"JSAssignExpr"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.dD;case 1:return this.eD;default:return $K(W(),a)}};f.D=function(a){return a instanceof Lo};f.B=function(){return AL(this)}; f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Lo){var b=this.dD,c=a.dD;if(null===b?null===c:b.i(c))return b=this.eD,a=a.eD,null===b?null===a:b.i(a)}return!1};f.$classData=q({mV:0},!1,"mlscript.JSAssignExpr",{mV:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function gn(a,b,c){this.Rw=a;this.hD=b;this.iD=c}gn.prototype=new fO;gn.prototype.constructor=gn;f=gn.prototype; f.sh=function(){var a=bO(Lm()).U(this.Rw);if(a instanceof L)return a.k|0;if(R()===a)throw vS(new wS,"Unknown binary operator: "+this.Rw);throw new w(a);};f.xa=function(){return Rp(Rp(Up(this.hD,this.sh()),Sp(Qp()," "+this.Rw+" ")),Up(this.iD,this.sh()))};f.H=function(){return"JSBinary"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.Rw;case 1:return this.hD;case 2:return this.iD;default:return $K(W(),a)}};f.D=function(a){return a instanceof gn};f.B=function(){return AL(this)}; f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof gn&&this.Rw===a.Rw){var b=this.hD,c=a.hD;if(null===b?null===c:b.i(c))return b=this.iD,a=a.iD,null===b?null===a:b.i(a)}return!1};f.$classData=q({zV:0},!1,"mlscript.JSBinary",{zV:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});q({CV:0},!1,"mlscript.JSClassDecl",{CV:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function Mo(a){this.lD=a}Mo.prototype=new fO;Mo.prototype.constructor=Mo;f=Mo.prototype;f.sh=function(){return 22};f.xa=function(){return this.lD.xa()}; f.H=function(){return"JSClassExpr"};f.G=function(){return 1};f.I=function(a){return 0===a?this.lD:$K(W(),a)};f.D=function(a){return a instanceof Mo};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Mo){var b=this.lD;a=a.lD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({DV:0},!1,"mlscript.JSClassExpr",{DV:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1}); function xp(a,b,c,d,e,g,h,k,l,m,n,r,v,x){this.Ds=a;this.Au=b;this.Sw=c;this.Xw=d;this.Tw=e;this.Zw=g;this.sq=h;this.zz=k;this.Ww=l;this.Uw=m;this.Vw=n;this.yz=r;this.xz=v;this.Yw=x;$U(dV(),b)}xp.prototype=new qO;xp.prototype.constructor=xp;f=xp.prototype; f.xa=function(){var a=new fp,b=this.sq.m();b=new Ao(b);var c=this.zz;b=Qu(b,c instanceof L?"..."+c.k:"",new fn((k,l)=>""===l?""+k.h():k.h()+", "+l));for(c=this.yz;!c.b();){var d=" #"+c.e()+";";wp(a,d);c=c.f()}for(c=hl(this.Xw);!c.b();)d=" #"+c.e()+";",wp(a,d),c=c.f();for(c=hl(this.Sw);!c.b();){var e=c.e();if(null!==e)d=e.Rc(),e=e.j(),this.Xw.L(e)||wp(a," #"+e+";"),wp(a," get "+e+"() { return this.#"+e+"; }"),d&&wp(a," set "+e+"($value) { return this.#"+e+" \x3d $value; }");else throw new w(e); c=c.f()}wp(a," constructor("+b+") {");if(!this.Tw.b()){b=this.Zw.m();b=new Ao(b);for(c="";b.s();)d=c,c=b.gm(),c=Pe(new E(c.Sc()),-1+this.Zw.K()|0)?""+d+c.h().xa():""+d+c.h().xa()+", ";wp(a," super("+c+");")}for(b=this.Uw;!b.b();)c=" "+b.e()+".implement(this);",wp(a,c),b=b.f();if(!this.xz){b=this.Au.K();if(!Pe(new E(b),this.sq.K()))throw new Yj("assertion failed: fields and ctorParams have different size in class "+this.Ds+".");b=this.Au;c=new Wq(b,b,this.sq);b=c.ck.m();for(c=c.dk.m();b.s()&& c.s();)d=b.t(),e=c.t(),wp(a," this.#"+d+" \x3d "+e+";")}for(b=this.Vw;!b.b();){c=Kz(Kz(b.e().xa())).$f;c=ze(c,"","\n","");var g=M2(c,"\n");c=(k=>l=>wp(k,l))(a);d=g.a.length;e=0;if(null!==g)for(;e{var e=G(new H,c,d);c=e.y;d=e.w;if(null!==d)return e=d.Sc(),Rp(Rp(c,Up(d.h(),Vp().Az)),Pe(new E(e),-1+this.Gz.K()|0)?Qp().ye:Sp(Qp(),", "));throw new w(e);}));return Rp(a,Pp(b,!0))};f.H=function(){return"JSInvoke"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.KD;case 1:return this.Gz;default:return $K(W(),a)}};f.D=function(a){return a instanceof cn};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof cn){var b=this.KD,c=a.KD;if(null===b?null===c:b.i(c))return b=this.Gz,a=a.Gz,null===b?null===a:b.i(a)}return!1};f.$classData=q({XV:0},!1,"mlscript.JSInvoke",{XV:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function Eo(a){this.Hz=a} Eo.prototype=new qO;Eo.prototype.constructor=Eo;f=Eo.prototype;f.xa=function(){return Rp(Rp(Sp(Qp(),"let "),Hf(this.Hz).De(Qp().ye,new fn((a,b)=>{var c=G(new H,a,b);a=c.y;b=c.w;if(null!==b){c=b.h();b=b.Sc();a:{if(null!==c){var d=c.h(),e=c.j();if(t().d===e){c=Sp(Qp(),d);break a}}if(null!==c&&(d=c.h(),e=c.j(),e instanceof L)){c=e.k;c=Rp(Rp(Sp(Qp(),d),Sp(Qp()," \x3d ")),c.xa());break a}throw new w(c);}return Rp(Rp(a,c),Pe(new E(b),-1+this.Hz.K()|0)?Qp().ye:Sp(Qp(),", "))}throw new w(c);}))),Qp().zx)}; f.H=function(){return"JSLetDecl"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Hz:$K(W(),a)};f.D=function(a){return a instanceof Eo};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Eo){var b=this.Hz;a=a.Hz;return null===b?null===a:b.i(a)}return!1};f.$classData=q({YV:0},!1,"mlscript.JSLetDecl",{YV:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function hn(a){this.LD=a}hn.prototype=new fO;hn.prototype.constructor=hn;f=hn.prototype; f.sh=function(){return 22};f.xa=function(){return Sp(Qp(),this.LD)};f.H=function(){return"JSLit"};f.G=function(){return 1};f.I=function(a){return 0===a?this.LD:$K(W(),a)};f.D=function(a){return a instanceof hn};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof hn?this.LD===a.LD:!1};f.$classData=q({$V:0},!1,"mlscript.JSLit",{$V:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function xl(a){this.MD=a}xl.prototype=new oO;xl.prototype.constructor=xl;f=xl.prototype; f.xa=function(){return Sp(Qp(),this.MD)};f.H=function(){return"JSNamePattern"};f.G=function(){return 1};f.I=function(a){return 0===a?this.MD:$K(W(),a)};f.D=function(a){return a instanceof xl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof xl?this.MD===a.MD:!1};f.$classData=q({bW:0},!1,"mlscript.JSNamePattern",{bW:1,BH:1,Xc:1,g:1,E:1,v:1,l:1});function $m(a){this.ND=a}$m.prototype=new fO;$m.prototype.constructor=$m;f=$m.prototype; f.sh=function(){return 21};f.xa=function(){return Rp(Sp(Qp(),"new "),this.ND.xa())};f.H=function(){return"JSNew"};f.G=function(){return 1};f.I=function(a){return 0===a?this.ND:$K(W(),a)};f.D=function(a){return a instanceof $m};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof $m){var b=this.ND;a=a.ND;return null===b?null===a:b.i(a)}return!1};f.$classData=q({cW:0},!1,"mlscript.JSNew",{cW:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1}); function Bl(a){this.OD=a}Bl.prototype=new oO;Bl.prototype.constructor=Bl;f=Bl.prototype; f.xa=function(){Qp();var a=this.OD,b=g=>{if(null!==g){var h=new L(g);if(!h.b()){var k=h.k.h();h=h.k.j();if(h instanceof L&&h.k instanceof El)return Sp(Qp(),k)}}if(null!==g&&(h=new L(g),!h.b()&&(k=h.k.h(),h=h.k.j(),h instanceof L)))return g=h.k,Rp(Sp(Qp(),k+": "),g.xa());if(null!==g&&(h=new L(g),!h.b()&&(k=h.k.h(),h=h.k.j(),t().d===h)))return Sp(Qp(),k);throw new w(g);};if(a===u())b=u();else{var c=a.e(),d=c=new z(b(c),u());for(a=a.f();a!==u();){var e=a.e();e=new z(b(e),u());d=d.p=e;a=a.f()}b=c}return Tz(b)}; f.H=function(){return"JSObjectPattern"};f.G=function(){return 1};f.I=function(a){return 0===a?this.OD:$K(W(),a)};f.D=function(a){return a instanceof Bl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Bl){var b=this.OD;a=a.OD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({dW:0},!1,"mlscript.JSObjectPattern",{dW:1,BH:1,Xc:1,g:1,E:1,v:1,l:1});function Vo(a){this.PD=a}Vo.prototype=new fO;Vo.prototype.constructor=Vo;f=Vo.prototype; f.sh=function(){return 0};f.xa=function(){return Up(this.PD,0)};f.H=function(){return"JSParenthesis"};f.G=function(){return 1};f.I=function(a){return 0===a?this.PD:$K(W(),a)};f.D=function(a){return a instanceof Vo};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Vo){var b=this.PD;a=a.PD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({eW:0},!1,"mlscript.JSParenthesis",{eW:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1}); function vo(a,b){this.QD=a;this.RD=b}vo.prototype=new fO;vo.prototype.constructor=vo;f=vo.prototype;f.sh=function(){return 22}; f.xa=function(){Qp();var a=this.QD,b=g=>{if(null!==g){var h=g.h();g=g.j();return Rp(Sp(Qp(),zl(Al(),h)+": "),Up(g,Vp().Az))}throw new w(g);};if(a===u())b=u();else{var c=a.e(),d=c=new z(b(c),u());for(a=a.f();a!==u();){var e=a.e();e=new z(b(e),u());d=d.p=e;a=a.f()}b=c}a=this.RD;if(a===u())c=u();else for(c=a.e(),d=c=new z(c.xa(),u()),a=a.f();a!==u();)e=a.e(),e=new z(e.xa(),u()),d=d.p=e,a=a.f();return Tz(un(b,c))};f.H=function(){return"JSRecord"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.QD;case 1:return this.RD;default:return $K(W(),a)}};f.D=function(a){return a instanceof vo};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof vo){var b=this.QD,c=a.QD;if(null===b?null===c:b.i(c))return b=this.RD,a=a.RD,null===b?null===a:b.i(a)}return!1};f.$classData=q({fW:0},!1,"mlscript.JSRecord",{fW:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function Io(a){this.SD=a}Io.prototype=new qO; Io.prototype.constructor=Io;f=Io.prototype;f.xa=function(){var a=this.SD;if(a instanceof L)a=a.k,a=Rp(Sp(Qp(),"return "),Nz(a.xa()));else{if(R()!==a)throw new w(a);a=Sp(Qp(),"return")}return Rp(a,Qp().zx)};f.H=function(){return"JSReturnStmt"};f.G=function(){return 1};f.I=function(a){return 0===a?this.SD:$K(W(),a)};f.D=function(a){return a instanceof Io};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Io){var b=this.SD;a=a.SD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({gW:0},!1,"mlscript.JSReturnStmt",{gW:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function hO(a,b,c){this.XD=a;this.VD=b;this.WD=c}hO.prototype=new qO;hO.prototype.constructor=hO;f=hO.prototype; f.xa=function(){for(var a=Rp(Rp(Sp(Qp(),"switch ("),this.XD.xa()),Sp(Qp(),") {")),b=this.VD,c=Qp().ye;!b.b();){var d=b.e();c=Iz(c,Kz(d.xa()));b=b.f()}a=Iz(a,c);b=this.WD;if(b instanceof L)b=Iz(Kz(b.k.xa()),Sp(Qp(),"}"));else{if(t().d!==b)throw new w(b);b=Sp(Qp(),"}")}return Iz(a,b)};f.H=function(){return"JSSwitchStmt"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.XD;case 1:return this.VD;case 2:return this.WD;default:return $K(W(),a)}};f.D=function(a){return a instanceof hO}; f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof hO){var b=this.XD,c=a.XD;if(null===b?null===c:b.i(c))if(b=this.VD,c=a.VD,null===b?null===c:b.i(c))return b=this.WD,a=a.WD,null===b?null===a:b.i(a)}return!1};f.$classData=q({iW:0},!1,"mlscript.JSSwitchStmt",{iW:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function on(a,b,c){this.$D=a;this.ZD=b;this.YD=c}on.prototype=new fO;on.prototype.constructor=on;f=on.prototype;f.sh=function(){return 4}; f.xa=function(){return Rp(Rp(Rp(Rp(Up(this.$D,4),Sp(Qp()," ? ")),Up(this.ZD,4)),Sp(Qp()," : ")),Up(this.YD,4))};f.H=function(){return"JSTenary"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.$D;case 1:return this.ZD;case 2:return this.YD;default:return $K(W(),a)}};f.D=function(a){return a instanceof on};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof on){var b=this.$D,c=a.$D;if(null===b?null===c:b.i(c))if(b=this.ZD,c=a.ZD,null===b?null===c:b.i(c))return b=this.YD,a=a.YD,null===b?null===a:b.i(a)}return!1};f.$classData=q({jW:0},!1,"mlscript.JSTenary",{jW:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function bn(a){this.aE=a}bn.prototype=new qO;bn.prototype.constructor=bn;f=bn.prototype;f.xa=function(){return Rp(Rp(Sp(Qp(),"throw "),Nz(this.aE.xa())),Qp().zx)};f.H=function(){return"JSThrowStmt"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.aE:$K(W(),a)};f.D=function(a){return a instanceof bn};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof bn){var b=this.aE;a=a.aE;return null===b?null===a:b.i(a)}return!1};f.$classData=q({kW:0},!1,"mlscript.JSThrowStmt",{kW:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function xz(a,b){this.bE=a;this.cE=b}xz.prototype=new qO;xz.prototype.constructor=xz;f=xz.prototype; f.xa=function(){for(var a=Sp(Qp(),"try "),b=this.bE,c=Qp().ye;!b.b();){var d=b.e();c=Iz(c,d.xa());b=b.f()}return Rp(Rp(a,Oz(c)),this.cE.xa())};f.H=function(){return"JSTryStmt"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.bE;case 1:return this.cE;default:return $K(W(),a)}};f.D=function(a){return a instanceof xz};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof xz){var b=this.bE,c=a.bE;if(null===b?null===c:b.i(c))return b=this.cE,a=a.cE,null===b?null===a:b.i(a)}return!1};f.$classData=q({lW:0},!1,"mlscript.JSTryStmt",{lW:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function $o(a,b){this.Iz=a;this.dE=b}$o.prototype=new fO;$o.prototype.constructor=$o;f=$o.prototype;f.sh=function(){return 15};f.xa=function(){return Rp("typeof"===this.Iz?Sp(Qp(),"typeof "):Sp(Qp(),this.Iz),Up(this.dE,15))};f.H=function(){return"JSUnary"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Iz;case 1:return this.dE;default:return $K(W(),a)}};f.D=function(a){return a instanceof $o};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof $o&&this.Iz===a.Iz){var b=this.dE;a=a.dE;return null===b?null===a:b.i(a)}return!1};f.$classData=q({mW:0},!1,"mlscript.JSUnary",{mW:1,Vi:1,Xc:1,g:1,E:1,v:1,l:1});function Zo(a,b){this.fE=a;this.eE=b}Zo.prototype=new qO; Zo.prototype.constructor=Zo;f=Zo.prototype;f.xa=function(){return Rp(Rp(Rp(Sp(Qp(),"while ("),this.fE.xa()),Sp(Qp(),") ")),Oz(this.eE.xa()))};f.H=function(){return"JSWhileStmt"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.fE;case 1:return this.eE;default:return $K(W(),a)}};f.D=function(a){return a instanceof Zo};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Zo){var b=this.fE,c=a.fE;if(null===b?null===c:b.i(c))return b=this.eE,a=a.eE,null===b?null===a:b.i(a)}return!1};f.$classData=q({oW:0},!1,"mlscript.JSWhileStmt",{oW:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function El(){}El.prototype=new oO;El.prototype.constructor=El;f=El.prototype;f.xa=function(){return Qp().ye};f.H=function(){return"JSWildcardPattern"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof El}; f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return a instanceof El};f.$classData=q({pW:0},!1,"mlscript.JSWildcardPattern",{pW:1,BH:1,Xc:1,g:1,E:1,v:1,l:1});function as(a){this.Na=a}as.prototype=new Wz;as.prototype.constructor=as;f=as.prototype;f.H=function(){return"KEYWORD"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Na:$K(W(),a)};f.D=function(a){return a instanceof as};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){return this===a?!0:a instanceof as?this.Na===a.Na:!1};f.$classData=q({qW:0},!1,"mlscript.KEYWORD",{qW:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});function fs(a){this.Cu=a}fs.prototype=new Wz;fs.prototype.constructor=fs;f=fs.prototype;f.H=function(){return"LITVAL"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Cu:$K(W(),a)};f.D=function(a){return a instanceof fs};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof fs){var b=this.Cu;a=a.Cu;return null===b?null===a:b.i(a)}return!1};f.$classData=q({rW:0},!1,"mlscript.LITVAL",{rW:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});function S2(){}S2.prototype=new Wz;S2.prototype.constructor=S2;f=S2.prototype;f.H=function(){return"NEWLINE"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof S2};f.B=function(){return-1731062412};f.u=function(){return"NEWLINE"}; f.$classData=q({HW:0},!1,"mlscript.NEWLINE$",{HW:1,yk:1,g:1,Wm:1,E:1,v:1,l:1});var T2;function qs(){T2||(T2=new S2);return T2}function LO(){}LO.prototype=new p;LO.prototype.constructor=LO;f=LO.prototype;f.uj=function(a,b){return 0>=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0h.L(k.h())))}if(b instanceof cC)return fB(a,b.Sf,c,d);if(Uv(b)||b instanceof cv||b instanceof Vv||b instanceof lx||b instanceof MA||b instanceof FA||b instanceof LA||b instanceof Wv||b instanceof eC||b instanceof Qx||b instanceof Jv)return nf();throw new w(b);}function Fv(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}Fv.prototype=new S_;Fv.prototype.constructor=Fv;function d3(){}d3.prototype=Fv.prototype; function pba(a,b){b=G(new H,a,b);var c=b.y,d=b.w;if(Uv(c)&&c.q===a.q&&Uv(d)&&d.q===a.q)return uw(c,d);c=b.y;if(Uv(c)&&c.q===a.q)return-1;c=b.w;if(Uv(c)&&c.q===a.q)return 1;if(b.y instanceof cv&&b.w instanceof cv)return 0;if(b.y instanceof cv)return-1;if(b.w instanceof cv)return 1;if(b.y instanceof Sv&&b.w instanceof Sv)return 0;if(b.y instanceof Sv)return-1;if(b.w instanceof Sv)return 1;if(b.y instanceof zv&&b.w instanceof zv)return 0;if(b.y instanceof zv)return-1;if(b.w instanceof zv)return 1;if(b.y instanceof Tv&&b.w instanceof Tv)return 0;if(b.y instanceof Tv)return-1;if(b.w instanceof Tv)return 1;if(b.y instanceof Jv&&b.w instanceof Jv)return 0;if(b.y instanceof Jv)return-1;if(b.w instanceof Jv)return 1;if(b.y instanceof Wv&&b.w instanceof Wv)return 0;throw new w(b);}Fv.prototype.kq=function(){return wv(xv(this.q))};Fv.prototype.Kc=function(a,b,c,d){return this.At(a,b,c,d)};function ix(a,b){this.J=null;this.kh=b;FC(this,a);b.Ua()}ix.prototype=new WP;ix.prototype.constructor=ix;f=ix.prototype;f.fQ=function(){return this.kh}; f.fd=function(){return this.kh.fd()};f.H=function(){return"CompletedTypeInfo"};f.G=function(){return 1};f.I=function(a){return 0===a?this.kh:$K(W(),a)};f.D=function(a){return a instanceof ix};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof ix&&a.J===this.J){var b=this.kh;a=a.kh;return null===b?null===a:b.i(a)}return!1};f.$classData=q({$Y:0},!1,"mlscript.TyperDatatypes$CompletedTypeInfo",{$Y:1,nZ:1,UO:1,g:1,E:1,v:1,l:1}); function Dt(a){this.xP=this.wP=this.vP=null;this.zP=this.AP=0;this.BP=this.yP=null;this.Rj=0;this.Kx=a;Nq(this)}Dt.prototype=new p;Dt.prototype.constructor=Dt;f=Dt.prototype;f.Vj=function(){0===(1&this.Rj)<<24>>24&&0===(1&this.Rj)<<24>>24&&(this.vP=this.Kx,this.Rj=(1|this.Rj)<<24>>24);return this.vP};function ef(a){0===(2&a.Rj)<<24>>24&&0===(2&a.Rj)<<24>>24&&(a.wP=mea(a),a.Rj=(2|a.Rj)<<24>>24);return a.wP} f.jn=function(){0===(4&this.Rj)<<24>>24&&0===(4&this.Rj)<<24>>24&&(this.xP=zq(this),this.Rj=(4|this.Rj)<<24>>24);return this.xP};f.rn=function(){return this.AP};f.fm=function(a){this.AP=a};f.qn=function(){return this.zP};f.em=function(a){this.zP=a};f.pn=function(){return this.yP};f.on=function(a){this.yP=a};f.A=function(){0===(8&this.Rj)<<24>>24&&0===(8&this.Rj)<<24>>24&&(this.BP=Dq(this),this.Rj=(8|this.Rj)<<24>>24);return this.BP};f.H=function(){return"TypingUnit"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.Kx:$K(W(),a)};f.D=function(a){return a instanceof Dt};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Dt){var b=this.Kx;a=a.Kx;return null===b?null===a:b.i(a)}return!1};f.$classData=q({ZZ:0},!1,"mlscript.TypingUnit",{ZZ:1,g:1,Taa:1,Ta:1,E:1,v:1,l:1});function e3(){this.ld="value"}e3.prototype=new SN;e3.prototype.constructor=e3;f=e3.prototype;f.H=function(){return"Val"};f.G=function(){return 0}; f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof e3};f.B=function(){return 85761};f.u=function(){return"Val"};f.$classData=q({d_:0},!1,"mlscript.Val$",{d_:1,sz:1,sx:1,g:1,E:1,v:1,l:1});var f3;function hx(){f3||(f3=new e3);return f3}function go(a,b){this.BA=a;this.Nx=b;this.bF=a;this.DP=!1}go.prototype=new p;go.prototype.constructor=go;f=go.prototype;f.xo=function(){return this.BA};f.zo=function(){return this.bF};f.u=function(){return"function "+this.BA};f.H=function(){return"BuiltinSymbol"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.BA;case 1:return this.Nx;default:return $K(W(),a)}};f.D=function(a){return a instanceof go};f.B=function(){return AL(this)};f.i=function(a){return this===a?!0:a instanceof go?this.BA===a.BA&&this.Nx===a.Nx:!1};f.$classData=q({o_:0},!1,"mlscript.codegen.BuiltinSymbol",{o_:1,g:1,mt:1,cr:1,E:1,v:1,l:1}); class gm extends xF{constructor(a){super();this.ZI=a;yF(this,a,null,!0)}H(){return"CodeGenError"}G(){return 1}I(a){return 0===a?this.ZI:$K(W(),a)}D(a){return a instanceof gm}B(){return AL(this)}i(a){return this===a?!0:a instanceof gm?this.ZI===a.ZI:!1}}gm.prototype.$classData=q({q_:0},!1,"mlscript.codegen.CodeGenError",{q_:1,qd:1,pc:1,g:1,l:1,E:1,v:1});function lo(a,b,c,d,e){this.dr=a;this.Ox=b;this.Px=c;this.EA=d;this.FA=e;this.OP=!1}lo.prototype=new p;lo.prototype.constructor=lo;f=lo.prototype; f.u=function(){return"new class member "+this.dr};f.xo=function(){return this.dr};f.zo=function(){return this.dr};f.H=function(){return"NewClassMemberSymbol"};f.G=function(){return 5};f.I=function(a){switch(a){case 0:return this.dr;case 1:return this.Ox;case 2:return this.Px;case 3:return this.EA;case 4:return this.FA;default:return $K(W(),a)}};f.D=function(a){return a instanceof lo}; f.B=function(){var a=lb("NewClassMemberSymbol");a=W().C(-889275714,a);var b=this.dr;b=My(W(),b);a=W().C(a,b);b=this.Ox;b=My(W(),b);a=W().C(a,b);b=this.Px?1231:1237;a=W().C(a,b);b=this.EA?1231:1237;a=W().C(a,b);b=this.FA;b=My(W(),b);a=W().C(a,b);return W().Ma(a,5)};f.i=function(a){if(this===a)return!0;if(a instanceof lo&&this.Px===a.Px&&this.EA===a.EA&&this.dr===a.dr){var b=this.Ox,c=a.Ox;if(null===b?null===c:b.i(c))return b=this.FA,a=a.FA,null===b?null===a:b.i(a)}return!1}; f.$classData=q({u_:0},!1,"mlscript.codegen.NewClassMemberSymbol",{u_:1,g:1,mt:1,cr:1,E:1,v:1,l:1});function io(a,b,c,d,e){this.av=a;this.nt=b;this.RA=c;this.QA=d;this.z_=e}io.prototype=new p;io.prototype.constructor=io;f=io.prototype;f.xo=function(){return this.av};f.zo=function(){return this.nt};f.u=function(){return"value "+this.av};f.H=function(){return"StubValueSymbol"};f.G=function(){return 4}; f.I=function(a){switch(a){case 0:return this.av;case 1:return this.nt;case 2:return this.RA;case 3:return this.QA;default:return $K(W(),a)}};f.D=function(a){return a instanceof io};f.B=function(){var a=lb("StubValueSymbol");a=W().C(-889275714,a);var b=this.av;b=My(W(),b);a=W().C(a,b);b=this.nt;b=My(W(),b);a=W().C(a,b);b=this.RA?1231:1237;a=W().C(a,b);b=this.QA;b=My(W(),b);a=W().C(a,b);return W().Ma(a,4)}; f.i=function(a){if(this===a)return!0;if(a instanceof io&&this.RA===a.RA&&this.av===a.av&&this.nt===a.nt){var b=this.QA;a=a.QA;return null===b?null===a:b.i(a)}return!1};f.$classData=q({y_:0},!1,"mlscript.codegen.StubValueSymbol",{y_:1,g:1,mt:1,cr:1,E:1,v:1,l:1});function nn(a,b,c){this.er=a;this.fJ=b;this.rF=c}nn.prototype=new p;nn.prototype.constructor=nn;f=nn.prototype;f.xo=function(){return this.er};f.mv=function(){return this.rF};f.u=function(){return"type "+this.er};f.H=function(){return"TypeAliasSymbol"}; f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.er;case 1:return this.fJ;case 2:return this.rF;default:return $K(W(),a)}};f.D=function(a){return a instanceof nn};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof nn){if(this.er===a.er){var b=this.fJ,c=a.fJ;b=null===b?null===c:b.i(c)}else b=!1;if(b)return b=this.rF,a=a.rF,null===b?null===a:b.i(a)}return!1}; f.$classData=q({D_:0},!1,"mlscript.codegen.TypeAliasSymbol",{D_:1,g:1,UA:1,cr:1,E:1,v:1,l:1}); class jo extends xF{constructor(a){super();this.gJ=a;var b=kl();for(a=new L(a);;){var c=a;if(c instanceof L)a=c.k,ol(b,a.av),a=a.QA,c=!0;else if(R()===c)c=!1;else throw new w(c);if(!c)break}a=ze(b,"",", ","");c=a.lastIndexOf(", ")|0;a=-1b?-1:1)?"are":"is")+" not implemented",null,!0)}H(){return"UnimplementedError"}G(){return 1}I(a){return 0===a?this.gJ:$K(W(),a)}D(a){return a instanceof jo}B(){return AL(this)}i(a){if(this=== a)return!0;if(a instanceof jo){var b=this.gJ;a=a.gJ;return null===b?null===a:b.i(a)}return!1}}jo.prototype.$classData=q({E_:0},!1,"mlscript.codegen.UnimplementedError",{E_:1,qd:1,pc:1,g:1,l:1,E:1,v:1});function SE(a,b){this.lf=null;this.Wx=a;this.M_=b;qE(this)}SE.prototype=new hQ;SE.prototype.constructor=SE;f=SE.prototype;f.Cv=function(){return this.M_};f.u=function(){return"\u00ab"+this.Wx+" is any"+tE(this)};f.H=function(){return"MatchAny"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.Wx:$K(W(),a)};f.D=function(a){return a instanceof SE};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof SE){var b=this.Wx;a=a.Wx;return null===b?null===a:b.i(a)}return!1};f.$classData=q({L_:0},!1,"mlscript.ucs.Clause$MatchAny",{L_:1,XP:1,VA:1,g:1,E:1,v:1,l:1});function UE(a,b,c,d){this.lf=null;this.Yx=a;this.Xx=b;this.WA=c;this.tF=d;qE(this)}UE.prototype=new hQ;UE.prototype.constructor=UE;f=UE.prototype;f.Cv=function(){return this.tF}; f.u=function(){return"\u00ab"+this.Yx+" is "+this.Xx+"\u00bb"+tE(this)};f.H=function(){return"MatchClass"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.Yx;case 1:return this.Xx;case 2:return this.WA;default:return $K(W(),a)}};f.D=function(a){return a instanceof UE};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof UE){var b=this.Yx,c=a.Yx;if(null===b?null===c:b.i(c))if(b=this.Xx,c=a.Xx,null===b?null===c:b.i(c))return b=this.WA,a=a.WA,null===b?null===a:b.i(a)}return!1};f.$classData=q({N_:0},!1,"mlscript.ucs.Clause$MatchClass",{N_:1,XP:1,VA:1,g:1,E:1,v:1,l:1});function OE(a,b,c){this.lf=null;this.$x=a;this.Zx=b;this.TP=c;qE(this)}OE.prototype=new hQ;OE.prototype.constructor=OE;f=OE.prototype;f.Cv=function(){return this.TP}; f.u=function(){return"\u00ab"+this.$x+" is "+this.Zx+tE(this)};f.H=function(){return"MatchLiteral"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.$x;case 1:return this.Zx;default:return $K(W(),a)}};f.D=function(a){return a instanceof OE};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof OE){var b=this.$x,c=a.$x;if(null===b?null===c:b.i(c))return b=this.Zx,a=a.Zx,null===b?null===a:b.i(a)}return!1}; f.$classData=q({O_:0},!1,"mlscript.ucs.Clause$MatchLiteral",{O_:1,XP:1,VA:1,g:1,E:1,v:1,l:1});function HE(a){this.ni=this.fv=null;this.ot=a;iQ(this)}HE.prototype=new kQ;HE.prototype.constructor=HE;f=HE.prototype;f.jb=function(){return"Consequent("+Zz(this.ot,!1)+")"};f.zt=function(){};f.or=function(){var a=new HE(this.ot);return QE(a,this.ni)};f.Bt=function(){return!0};f.ky=function(){return!0}; f.dm=function(a,b){var c=new fp,d=We(Xe(),"Found a duplicated branch"),e=t().d;d=G(new H,d,e);wp(c,d);d=We(Xe(),"This branch");a:{if(null!==a&&(e=new L(a),!e.b())){var g=e.k.j();if(null!==e.k.h())break a}throw new w(a);}a=g.A();a=G(new H,d,a);wp(c,a);a=We(Xe(),"is subsumed by the branch here.");d=this.ot.A();a=G(new H,a,d);wp(c,a);b.n(hr(fr(),c.ha(),!0,lu()))};f.Op=function(){return 0};f.H=function(){return"Consequent"};f.G=function(){return 1};f.I=function(a){return 0===a?this.ot:$K(W(),a)}; f.D=function(a){return a instanceof HE};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof HE){var b=this.ot;a=a.ot;return null===b?null===a:b.i(a)}return!1};f.$classData=q({Y_:0},!1,"mlscript.ucs.MutCaseOf$Consequent",{Y_:1,kJ:1,g:1,lJ:1,E:1,v:1,l:1});function CE(a,b,c){this.ni=this.fv=null;this.hr=a;this.Dk=b;this.Fh=c;iQ(this)}CE.prototype=new kQ;CE.prototype.constructor=CE;f=CE.prototype; f.jb=function(){return"IfThenElse("+Zz(this.hr,!1)+", whenTrue \x3d "+lQ(this.Dk)+", whenFalse \x3d "+lQ(this.Fh)+")"};f.or=function(){var a=new CE(this.hr,this.Dk.or(),this.Fh.or());return QE(a,this.ni)};f.zt=function(a){this.Dk.zt(a);Pe(new E(this.Fh),IE())?this.Fh=a:this.Fh.zt(a)};f.Bt=function(){return this.Dk.Bt()&&this.Fh.Bt()};f.ky=function(a,b){return this.Dk.ky(a,b)&&this.Fh.ky(a,b)}; f.dm=function(a,b,c,d,e){if(null!==a){var g=new L(a);if(!g.b()){var h=g.k.h();g=g.k.j();if(null!==h){var k=h.Wi;h=h.mj;var l=O().c;if(null===l?null===k:l.i(k)){a=this.Op(h,g,b,c,d,e);Pe(new E(a),0)&&(fr(),a=Ye(new Te(new Ue(J(new K,["Found a redundant else branch"]))),u()),c=g.A(),a=G(new H,a,c),c=O().c,b.n(hr(0,new z(a,c),!0,lu())));return}}}}if(null!==a&&(g=new L(a),!g.b()&&(h=g.k.h(),g=g.k.j(),null!==h&&(k=h.Wi,h=h.mj,k instanceof z&&(l=k.z,k=k.p,l instanceof XE&&Pe(new E(l.Vx),this.hr)))))){lF(this.Dk, l.lf);this.Dk.dm(G(new H,new NE(k,h),g),b,c,d,e);return}if(null!==a&&(g=new L(a),!g.b()&&(g=g.k.h(),null!==g&&(g=g.Wi,g instanceof z)))){g=g.z;mQ(this.Dk,a,c,d,e);k=this.Fh;k instanceof HE?(fr(),a=We(Xe(),"duplicated else in the if-then-else"),c=t().d,a=G(new H,a,c),c=O().c,b.n(hr(0,new z(a,c),!0,lu()))):IE()===k?(b=gF(),this.Fh=cF(b,a.h(),a.j()),lF(this.Fh,g.lf)):this.Fh.dm(a,b,c,d,e);return}throw new w(a);}; f.Op=function(a,b,c,d,e,g){var h=this.Dk.Op(a,b,c,d,e,g),k=this.Fh;if(k instanceof HE)a=0;else if(IE()===k)b=new HE(b),this.Fh=QE(b,a),a=1;else{if(!(k instanceof CE||k instanceof DE))throw new w(k);a=this.Fh.Op(a,b,c,d,e,g)}return h+a|0};f.H=function(){return"IfThenElse"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.hr;case 1:return this.Dk;case 2:return this.Fh;default:return $K(W(),a)}};f.D=function(a){return a instanceof CE};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof CE){var b=this.hr,c=a.hr;if(null===b?null===c:b.i(c))if(b=this.Dk,c=a.Dk,null===b?null===c:b.i(c))return b=this.Fh,a=a.Fh,null===b?null===a:b.i(a)}return!1};f.$classData=q({Z_:0},!1,"mlscript.ucs.MutCaseOf$IfThenElse",{Z_:1,kJ:1,g:1,lJ:1,E:1,v:1,l:1});function DE(a,b,c){this.ni=this.fv=null;this.mo=a;this.mh=b;this.nj=c;iQ(this)}DE.prototype=new kQ;DE.prototype.constructor=DE;f=DE.prototype; f.jb=function(){var a=this.mh.K(),b=this.mo,c=WF(ve(),"branch",a,!0),d=this.nj;d=d.b()?"no wildcard":"wildcard \x3d "+lQ(d.o());return"Match("+b+", "+a+" "+c+", "+d+")"};f.or=function(){var a=this.mo,b=this.mh.Ja(new y(d=>d.oQ())),c=this.nj;c.b()?c=R():(c=c.o(),c=new L(c.or()));a=new DE(a,b,c);return QE(a,this.ni)};f.zt=function(a){for(var b=this.mh.m();b.s();)b.t().xt().zt(a);b=this.nj;b.b()||b.o().zt(a)}; f.Bt=function(){for(var a=!0,b=this.mh.m();a&&b.s();)a=b.t().xt().Bt();return a?(a=this.nj,a.b()?!0:a.o().Bt()):!1}; f.ky=function(a,b){a=b.U(a.n(this.mo));if(R()===a)no();else{if(a instanceof L)return a=a.k.tj(),!(new iy(a,new y(c=>{if(c instanceof fe){c=c.aa|0;for(var d=this.mh.m(),e=!1;!e&&d.s();)a:if(e=d.t(),e instanceof EE)e=!1;else{if(e instanceof FE){var g=e.Tj;if(null!==g&&(g=new L(g),!g.b()&&(g=g.k.h(),null!==g))){e=g.x==="Tuple#"+c;break a}}throw new w(e);}return e}if(c instanceof Ud){c=c.fa;d=this.mh.m();for(e=!1;!e&&d.s();)a:if(e=d.t(),e instanceof EE)e=Pe(new E(c),e.ir);else{if(e instanceof FE&&(g= e.Tj,null!==g&&(g=new L(g),!g.b()))){e=Pe(new E(c),g.k.h());break a}throw new w(e);}return e}throw new w(c);}),!0)).s();throw new w(a);}}; f.dm=function(a,b,c,d,e){var g=Cc=>{if(Cc instanceof YE){var Fc=Cc.bv,qd=Cc.cv;if(!1===Cc.Ux){var Yb=!1;for(Cc=this.ni.m();!Yb&&Cc.s();){var Nc=Cc.t();a:{if(null!==Nc){Yb=Nc.fr;var ad=Nc.Sj;Nc=Nc.gr;if(aF()===Yb){Yb=Pe(new E(ad),Fc)&&Pe(new E(Nc),qd);break a}}Yb=!1}}Fc=Yb}else Fc=!1;if(Fc)return!1}return!0},h=a.h().Wi;a:for(var k;;)if(h.b()){k=u();break}else{var l=h.e(),m=h.f();if(!1===!!g(l))h=m;else for(var n=h,r=m;;){if(r.b())k=n;else{var v=r.e();if(!1!==!!g(v)){r=r.f();continue}for(var x=r,A= new z(n.e(),u()),B=n.f(),C=A;B!==x;){var D=new z(B.e(),u());C=C.p=D;B=B.f()}for(var F=x.f(),I=F;!F.b();){var M=F.e();if(!1===!!g(M)){for(;I!==F;){var N=new z(I.e(),u());C=C.p=N;I=I.f()}I=F.f()}F=F.f()}I.b()||(C.p=I);k=A}break a}}var P=G(new H,new NE(k,a.h().mj),a.j()),T=!1,Y=null,Z=Vea(P.y,this.mo);a:if(t().d===Z)b:{var S=new L(P);if(!S.b()){var ea=S.k.h(),ia=S.k.j();if(null!==ea){var X=ea.Wi,sa=ea.mj;if(X instanceof z){var Ja=X.z,Xa=X.p;if(Ja instanceof WE){var Fa=Ja.dv,za=Ja.ay;if(Pe(new E(Ja.ev), this.mo)){var Qa=new vl("Tuple#"+Fa);c:{for(var Ma=this.mh.m();Ma.s();){var Ga=Ma.t();if(Ga.FF(Qa,e)){var ab=new L(Ga);break c}}ab=R()}c:{var Hb=t().d;var bc=null!==Hb&&Hb===ab?!0:ab instanceof L&&ab.k instanceof EE?!0:!1;if(bc){var yb=gF(),tb=cF(yb,new NE(Xa,sa),ia);lF(tb,Ja.lf);var eb=this.mh,kb=PE().vl(za),Rb=VE(new FE(G(new H,Qa,kb),tb),Ja.UP);eb.$(Rb)}else{if(ab instanceof L){var Gb=ab.k;if(Gb instanceof FE){lF(Gb.pl,Ja.lf);Y_(Gb,za);Gb.pl.dm(G(new H,new NE(Xa,sa),ia),b,c,d,e);break c}}throw new w(ab); }}break b}}}}}var vb=new L(P);if(!vb.b()){var Tb=vb.k.h(),Nb=vb.k.j();if(null!==Tb){var ic=Tb.Wi,Va=Tb.mj,cb=O().c;if(null===cb?null===ic:cb.i(ic)){var zb=this.Op(Va,Nb,b,c,d,e);if(Pe(new E(zb),0)){fr();var Ub=Ye(new Te(new Ue(J(new K,["Found a redundant else branch"]))),u()),jb=Nb.A(),db=G(new H,Ub,jb),ub=O().c;b.n(hr(0,new z(db,ub),!0,lu()))}break b}}}var Aa=new L(P);if(Aa.b())throw new w(P);for(var va=Aa.k.h(),Ra=Aa.k.j(),rb=this.mh.m();rb.s();)mQ(rb.t().xt(),G(new H,va,Ra),c,d,e);var xb=this.nj; if(t().d===xb){t();var mc=gF(),Ha=cF(mc,va,Ra);this.nj=new L(Ha)}else if(xb instanceof L)xb.k.dm(G(new H,va,Ra),b,c,d,e);else throw new w(xb);}else{if(Z instanceof L){T=!0;Y=Z;var Ka=Y.k;if(null!==Ka){var Oa=new L(Ka);if(!Oa.b()){var Na=Oa.k.h(),Da=Oa.k.j();if(Na instanceof UE){var ta=Na.Xx,Ya=Na.WA,dc=this.mh.m(),ka=new iy(dc,new y(Cc=>Cc.FF(ta,e)),!1);if(ka.s())for(;ka.s();){var ya=ka.t();b:if(!(ya instanceof EE)){if(ya instanceof FE){var Sa=ya,xc=Sa.Tj;if(null!==xc){var Sb=new L(xc);if(!Sb.b()){var uc= Sb.k.h();if(null!==uc){uc.x===ta.x?(lF(Sa.pl,Na.lf),Y_(Sa,Ya),Sa.pl.dm(G(new H,Da,P.w),b,c,d,e)):(lF(Sa.pl,Na.lf),Y_(Sa,Ya),Sa.pl.dm(P,b,c,d,e));break b}}}}throw new w(ya);}}else{var Lb=this.nj;b:{if(Lb instanceof L){var lc=Lb.k;if(!lc.Bt()){var Xb=lc.or(),ec=gF();Xb.zt(cF(ec,Da,P.w));lF(Xb,Na.lf);var Ab=this.mh,Ob=PE().vl(Ya),fb=VE(new FE(G(new H,ta,Ob),Xb),Na.tF);Ab.$(fb);break b}}if(Lb instanceof L||R()===Lb){var Wa=gF(),bb=cF(Wa,Da,P.w);lF(bb,Na.lf);var Ia=this.mh,Ua=PE().vl(Ya),pc=VE(new FE(G(new H, ta,Ua),bb),Na.tF);Ia.$(pc)}else throw new w(Lb);}}break a}}}}if(T){var sc=Y.k;if(null!==sc){var Ba=new L(sc);if(!Ba.b()){var ob=Ba.k.h(),nc=Ba.k.j();if(ob instanceof OE){var Ib=ob.Zx;b:{for(var vc=this.mh.m();vc.s();){var Vb=vc.t();if(Vb.FF(Ib,e)){var fc=new L(Vb);break b}}fc=R()}b:{var Bc=t().d;var Pb=null!==Bc&&Bc===fc?!0:fc instanceof L&&fc.k instanceof FE?!0:!1;if(Pb){var Jb=gF(),gc=cF(Jb,nc,P.w);lF(gc,ob.lf);var Cb=this.mh,cc=VE(new EE(Ib,gc),ob.TP);Cb.$(cc)}else{if(fc instanceof L){var yc=fc.k; if(yc instanceof EE){lF(yc.Jp,ob.lf);yc.Jp.dm(G(new H,nc,P.w),b,c,d,e);break b}}throw new w(fc);}}break a}}}}if(T){var Mc=Y.k;if(null!==Mc){var qc=new L(Mc);if(!qc.b()){var oc=qc.k.j();if(qc.k.h()instanceof SE){for(var Qc=this.mh.m(),jc=new iy(Qc,new y(Cc=>Cc.xt().ky(c,d)),!0);jc.s();)mQ(jc.t().xt(),G(new H,oc,P.w),c,d,e);var sb=this.nj;if(t().d===sb){t();var Gc=gF(),Wb=cF(Gc,oc,P.w);this.nj=new L(Wb)}else if(sb instanceof L)sb.k.dm(G(new H,oc,P.w),b,c,d,e);else throw new w(sb);break a}}}}throw new w(Z); }};f.Op=function(a,b,c,d,e,g){var h=this.mh.m();h=new Ef(h,new y(l=>{if(l instanceof FE)return l.pl.Op(a,b,c,d,e,g);if(l instanceof EE)return l.Jp.Op(a,b,c,d,e,g);throw new w(l);}));g3||(g3=new h3);h=fda(h);var k=this.nj;if(t().d===k)t(),k=new HE(b),k=QE(k,a),this.nj=new L(k),k=1;else{if(!(k instanceof L))throw new w(k);k=k.k.Op(a,b,c,d,e,g)}return(h|0)+k|0};f.H=function(){return"Match"};f.G=function(){return 3}; f.I=function(a){switch(a){case 0:return this.mo;case 1:return this.mh;case 2:return this.nj;default:return $K(W(),a)}};f.D=function(a){return a instanceof DE};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof DE){var b=this.mo,c=a.mo;if(null===b?null===c:b.i(c))if(b=this.mh,c=a.mh,null===b?null===c:i3(b,c))return b=this.nj,a=a.nj,null===b?null===a:b.i(a)}return!1}; f.$classData=q({$_:0},!1,"mlscript.ucs.MutCaseOf$Match",{$_:1,kJ:1,g:1,lJ:1,E:1,v:1,l:1});function j3(){this.ni=this.fv=null;iQ(this)}j3.prototype=new kQ;j3.prototype.constructor=j3;f=j3.prototype;f.jb=function(){return"MissingCase"};f.zt=function(){};f.Bt=function(){return!1};f.ky=function(){return!1};f.dm=function(){xm("`MissingCase` is a placeholder and cannot be merged")};f.Op=function(){return 0};f.H=function(){return"MissingCase"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)}; f.D=function(a){return a instanceof j3};f.B=function(){return-1279461994};f.u=function(){return"MissingCase"};f.or=function(){return IE()};f.$classData=q({a0:0},!1,"mlscript.ucs.MutCaseOf$MissingCase$",{a0:1,kJ:1,g:1,lJ:1,E:1,v:1,l:1});var k3;function IE(){k3||(k3=new j3);return k3}function l3(){}l3.prototype=new f0;l3.prototype.constructor=l3;f=l3.prototype;f.H=function(){return"None"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof l3};f.B=function(){return 2433880}; f.u=function(){return"None"};f.o=function(){throw AH("None.get");};f.$classData=q({$2:0},!1,"scala.None$",{$2:1,b3:1,g:1,M:1,E:1,v:1,l:1});var m3;function R(){m3||(m3=new l3);return m3}function L(a){this.k=a}L.prototype=new f0;L.prototype.constructor=L;f=L.prototype;f.o=function(){return this.k};f.H=function(){return"Some"};f.G=function(){return 1};f.I=function(a){return 0===a?this.k:$K(W(),a)};f.D=function(a){return a instanceof L};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){return this===a?!0:a instanceof L?ml(nl(),this.k,a.k):!1};f.$classData=q({n3:0},!1,"scala.Some",{n3:1,b3:1,g:1,M:1,E:1,v:1,l:1});class NQ extends SZ{constructor(a,b){super();At(tp(),0<=b&&ba)a=this.Al;else{var b=this.Al;a=ba?0:a);return this};f.qk=function(a,b){a=0>a?0:a>this.bk?this.bk:a;b=(0>b?0:b>this.bk?this.bk:b)-a|0;this.bk=0>b?0:b;this.Er=this.Er+a|0;return this}; f.$classData=q({CR:0},!1,"scala.collection.IndexedSeqView$IndexedSeqViewIterator",{CR:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function uZ(a,b){a.VK=b;a.zj=b.K();a.Kv=-1+a.zj|0;return a}function vZ(){this.VK=null;this.Kv=this.zj=0}vZ.prototype=new BY;vZ.prototype.constructor=vZ;function w3(){}w3.prototype=vZ.prototype;vZ.prototype.s=function(){return 0=a?0<=b&&b{Rq();return new g0(b)}));return a} TT.prototype.$=function(a){return pfa(this,a)};TT.prototype.$classData=q({m5:0},!1,"scala.collection.Iterator$$anon$21",{m5:1,Vba:1,g:1,Po:1,xg:1,xf:1,wf:1});function Ex(a,b){this.JR=null;this.Ay=0;this.KR=this.$K=null;if(null===a)throw null;this.$K=a;this.KR=b;this.Ay=0}Ex.prototype=new BY;Ex.prototype.constructor=Ex;f=Ex.prototype;f.u=function(){return"\x3cfunction1\x3e"};f.n=function(){return fL()}; f.s=function(){for(var a=fL();0===this.Ay;)if(this.$K.s()){var b=this.$K.t();b=this.KR.Ob(b,this);a!==b&&(this.JR=b,this.Ay=1)}else this.Ay=-1;return 1===this.Ay};f.t=function(){return this.s()?(this.Ay=0,this.JR):Rq().Pa.t()};f.$classData=q({q5:0},!1,"scala.collection.Iterator$$anon$7",{q5:1,Sa:1,g:1,Ka:1,M:1,N:1,la:1});function x3(a,b,c){a=a.U(b);if(a instanceof L)return a.k;if(R()===a)return Es(c);throw new w(a);} function HA(a,b){var c=a.U(b);if(R()===c)return a.IF(b);if(c instanceof L)return c.k;throw new w(c);}function y3(a,b,c){return a.Se(b,new U(()=>c.n(b)))}function z3(a){throw AH("key not found: "+a);}function A3(a,b){var c=a.vj();a=Fs(b)?new JT(a,b):a.m().nb(new U(()=>b.m()));return c.Ib(a)}function B3(a,b,c,d,e){a=a.m();a=new Ef(a,new y(g=>{if(null!==g)return g.h()+" -\x3e "+g.j();throw new w(g);}));return iH(a,b,c,d,e)}function C3(){this.Mt=null;this.Mt=uv()}C3.prototype=new L0; C3.prototype.constructor=C3;C3.prototype.$classData=q({e6:0},!1,"scala.collection.SortedSet$",{e6:1,Y5:1,V4:1,g:1,oG:1,l:1,dS:1});var D3;function E3(a,b){var c=a.ti(),d=Vn();for(a=a.m();a.s();){var e=a.t();d.oh(b.n(e))&&c.$(e)}return c.Kb()}function F3(a,b){var c=a.Ik().Eb();0<=a.Q()&&c.he(1+a.K()|0);c.$(b);c.zc(a);return c.Kb()}function Xq(a,b){var c=a.Ik().Eb();0<=a.Q()&&c.he(1+a.K()|0);c.zc(a);c.$(b);return c.Kb()}function G3(a,b){var c=a.Ik().Eb();c.zc(a);c.zc(b);return c.Kb()} function H3(){this.FL=this.iS=null;this.EL=!1;I3=this;this.FL=new i0(this)}H3.prototype=new p;H3.prototype.constructor=H3;function J3(a,b){return a instanceof K3?a:KQ(0,rQ(SG(),a,b))}f=H3.prototype;f.sy=function(a){var b=new DF;return new dU(b,new y(c=>KQ(JQ(),kB(c,a))))}; function KQ(a,b){if(null===b)return null;if(b instanceof zc)return new Fu(b);if(b instanceof Xc)return new L3(b);if(b instanceof ed)return new M3(b);if(b instanceof Zc)return new N3(b);if(b instanceof $c)return new O3(b);if(b instanceof Ic)return new P3(b);if(b instanceof Pc)return new Q3(b);if(b instanceof Sc)return new R3(b);if(b instanceof Ec)return new S3(b);if(ph(b))return new T3(b);throw new w(b);}f.GB=function(a){return this.sy(a)};f.pr=function(a,b){return J3(a,b)}; f.ng=function(){this.EL||this.EL||(this.iS=new Fu(new zc(0)),this.EL=!0);return this.iS};f.$classData=q({G6:0},!1,"scala.collection.immutable.ArraySeq$",{G6:1,g:1,f6:1,T4:1,S4:1,oG:1,l:1});var I3;function JQ(){I3||(I3=new H3);return I3}function ZJ(a){return!!(a&&a.$classData&&a.$classData.rb.xc)}function U3(a){this.Yp=0;this.Vt=null;$0(this,a)}U3.prototype=new b1;U3.prototype.constructor=U3;U3.prototype.wj=function(a,b){return G(new H,a,b)}; U3.prototype.$classData=q({y7:0},!1,"scala.collection.immutable.Map$Map2$$anon$1",{y7:1,qS:1,Sa:1,g:1,Ka:1,M:1,N:1});function V3(a){this.Yp=0;this.Vt=null;$0(this,a)}V3.prototype=new b1;V3.prototype.constructor=V3;V3.prototype.wj=function(a){return a};V3.prototype.$classData=q({z7:0},!1,"scala.collection.immutable.Map$Map2$$anon$2",{z7:1,qS:1,Sa:1,g:1,Ka:1,M:1,N:1});function W3(a){this.Yp=0;this.Vt=null;$0(this,a)}W3.prototype=new b1;W3.prototype.constructor=W3;W3.prototype.wj=function(a,b){return b}; W3.prototype.$classData=q({A7:0},!1,"scala.collection.immutable.Map$Map2$$anon$3",{A7:1,qS:1,Sa:1,g:1,Ka:1,M:1,N:1});function X3(a){this.$p=0;this.Zp=null;c1(this,a)}X3.prototype=new e1;X3.prototype.constructor=X3;X3.prototype.wj=function(a,b){return G(new H,a,b)};X3.prototype.$classData=q({C7:0},!1,"scala.collection.immutable.Map$Map3$$anon$4",{C7:1,rS:1,Sa:1,g:1,Ka:1,M:1,N:1});function Y3(a){this.$p=0;this.Zp=null;c1(this,a)}Y3.prototype=new e1;Y3.prototype.constructor=Y3;Y3.prototype.wj=function(a){return a}; Y3.prototype.$classData=q({D7:0},!1,"scala.collection.immutable.Map$Map3$$anon$5",{D7:1,rS:1,Sa:1,g:1,Ka:1,M:1,N:1});function Z3(a){this.$p=0;this.Zp=null;c1(this,a)}Z3.prototype=new e1;Z3.prototype.constructor=Z3;Z3.prototype.wj=function(a,b){return b};Z3.prototype.$classData=q({E7:0},!1,"scala.collection.immutable.Map$Map3$$anon$6",{E7:1,rS:1,Sa:1,g:1,Ka:1,M:1,N:1});function $3(a){this.aq=0;this.zn=null;f1(this,a)}$3.prototype=new h1;$3.prototype.constructor=$3; $3.prototype.wj=function(a,b){return G(new H,a,b)};$3.prototype.$classData=q({G7:0},!1,"scala.collection.immutable.Map$Map4$$anon$7",{G7:1,sS:1,Sa:1,g:1,Ka:1,M:1,N:1});function a4(a){this.aq=0;this.zn=null;f1(this,a)}a4.prototype=new h1;a4.prototype.constructor=a4;a4.prototype.wj=function(a){return a};a4.prototype.$classData=q({H7:0},!1,"scala.collection.immutable.Map$Map4$$anon$8",{H7:1,sS:1,Sa:1,g:1,Ka:1,M:1,N:1});function b4(a){this.aq=0;this.zn=null;f1(this,a)}b4.prototype=new h1; b4.prototype.constructor=b4;b4.prototype.wj=function(a,b){return b};b4.prototype.$classData=q({I7:0},!1,"scala.collection.immutable.Map$Map4$$anon$9",{I7:1,sS:1,Sa:1,g:1,Ka:1,M:1,N:1});function hE(a,b,c,d){this.nC=b;this.Wy=c;this.fw=!d;this.Vy=a}hE.prototype=new BY;hE.prototype.constructor=hE;f=hE.prototype;f.Q=function(){return this.fw?1+pb(this.Wy-this.Vy|0,this.nC)|0:0};f.s=function(){return this.fw};function c4(a){a.fw||Rq().Pa.t();var b=a.Vy;a.fw=b!==a.Wy;a.Vy=b+a.nC|0;return b} f.ph=function(a){if(0>31;a=Math.imul(this.nC,a);var d=a>>31;a=b+a|0;b=(-2147483648^a)<(-2147483648^b)?1+(c+d|0)|0:c+d|0;0>31,this.Vy=(d===b?(-2147483648^c)<(-2147483648^a):d>31,this.fw=b===d?(-2147483648^a)<=(-2147483648^c):bthis.nC&&(c=this.Wy,d=c>>31,this.Vy=(d===b?(-2147483648^c)>(-2147483648^a):d>b)?c:a,c=this.Wy,d=c>>31,this.fw=b===d?(-2147483648^a)>=(-2147483648^c):b>d)}return this};f.t=function(){return c4(this)}; f.$classData=q({$7:0},!1,"scala.collection.immutable.RangeIterator",{$7:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function d4(a,b,c){this.Cn=this.hw=this.Xy=null;this.Wd=0;this.Hc=null;q1(this,a,b,c)}d4.prototype=new t1;d4.prototype.constructor=d4;d4.prototype.yK=function(a){return G(new H,a.Wa,a.Wb)};d4.prototype.$classData=q({b8:0},!1,"scala.collection.immutable.RedBlackTree$EntriesIterator",{b8:1,zS:1,Sa:1,g:1,Ka:1,M:1,N:1}); function e4(a,b){this.Cn=this.hw=this.Xy=null;this.Wd=0;this.Hc=null;q1(this,a,R(),b)}e4.prototype=new t1;e4.prototype.constructor=e4;e4.prototype.yK=function(){no()};e4.prototype.$classData=q({c8:0},!1,"scala.collection.immutable.RedBlackTree$EqualsIterator",{c8:1,zS:1,Sa:1,g:1,Ka:1,M:1,N:1});function f4(a,b,c){this.Cn=this.hw=this.Xy=null;this.Wd=0;this.Hc=null;q1(this,a,b,c)}f4.prototype=new t1;f4.prototype.constructor=f4;f4.prototype.yK=function(a){return a.Wa}; f4.prototype.$classData=q({d8:0},!1,"scala.collection.immutable.RedBlackTree$KeysIterator",{d8:1,zS:1,Sa:1,g:1,Ka:1,M:1,N:1});function g4(){this.Dn=this.Io=0}g4.prototype=new BY;g4.prototype.constructor=g4;function h4(){}h4.prototype=g4.prototype;g4.prototype.Q=function(){return this.Dn};g4.prototype.s=function(){return 0a?0:a);return this};function i4(){this.Mt=null;this.Mt=zZ()}i4.prototype=new L0;i4.prototype.constructor=i4;function oA(a,b,c){if(b&&b.$classData&&b.$classData.rb.OL){O();var d=b.se();if(null===c?null===d:c.i(d))return b}return tT.prototype.pr.call(a,b,c)}i4.prototype.pr=function(a,b){return oA(this,a,b)};i4.prototype.$classData=q({C8:0},!1,"scala.collection.immutable.SortedSet$",{C8:1,Y5:1,V4:1,g:1,oG:1,l:1,dS:1});var j4; function uv(){j4||(j4=new i4);return j4}function k4(){}k4.prototype=new p;k4.prototype.constructor=k4;function l4(){}l4.prototype=k4.prototype;k4.prototype.he=function(){};function m4(){this.SL=this.TL=null;n4=this;this.TL=new i0(this);this.SL=new zQ(new zc(0))}m4.prototype=new p;m4.prototype.constructor=m4;f=m4.prototype;f.sy=function(a){a=new o4(a.uh());return new dU(a,new y(b=>p4(yQ(),b)))}; function p4(a,b){if(null===b)return null;if(b instanceof zc)return new zQ(b);if(b instanceof Xc)return new q4(b);if(b instanceof ed)return new r4(b);if(b instanceof Zc)return new s4(b);if(b instanceof $c)return new t4(b);if(b instanceof Ic)return new AQ(b);if(b instanceof Pc)return new u4(b);if(b instanceof Sc)return new v4(b);if(b instanceof Ec)return new w4(b);if(ph(b))return new x4(b);throw new w(b);}f.GB=function(a){return this.sy(a)};f.pr=function(a,b){return p4(0,rQ(SG(),a,b))};f.ng=function(){return this.SL}; f.$classData=q({j9:0},!1,"scala.collection.mutable.ArraySeq$",{j9:1,g:1,f6:1,T4:1,S4:1,oG:1,l:1});var n4;function yQ(){n4||(n4=new m4);return n4}function y4(a){this.hq=0;this.Lo=null;this.vw=0;this.uw=null;Y1(this,a)}y4.prototype=new $1;y4.prototype.constructor=y4;y4.prototype.lB=function(a){return G(new H,a.Wk,a.Ah)};y4.prototype.$classData=q({G9:0},!1,"scala.collection.mutable.HashMap$$anon$1",{G9:1,fH:1,Sa:1,g:1,Ka:1,M:1,N:1}); function z4(a){this.hq=0;this.Lo=null;this.vw=0;this.uw=null;Y1(this,a)}z4.prototype=new $1;z4.prototype.constructor=z4;z4.prototype.lB=function(a){return a.Wk};z4.prototype.$classData=q({H9:0},!1,"scala.collection.mutable.HashMap$$anon$2",{H9:1,fH:1,Sa:1,g:1,Ka:1,M:1,N:1});function A4(a){this.hq=0;this.Lo=null;this.vw=0;this.uw=null;Y1(this,a)}A4.prototype=new $1;A4.prototype.constructor=A4;A4.prototype.lB=function(a){return a.Ah}; A4.prototype.$classData=q({I9:0},!1,"scala.collection.mutable.HashMap$$anon$3",{I9:1,fH:1,Sa:1,g:1,Ka:1,M:1,N:1});function B4(a){this.hq=0;this.Lo=null;this.vw=0;this.uw=null;Y1(this,a)}B4.prototype=new $1;B4.prototype.constructor=B4;B4.prototype.lB=function(a){return a};B4.prototype.$classData=q({J9:0},!1,"scala.collection.mutable.HashMap$$anon$4",{J9:1,fH:1,Sa:1,g:1,Ka:1,M:1,N:1}); function C4(a){this.hq=0;this.Lo=null;this.vw=0;this.uw=null;this.WL=0;if(null===a)throw null;Y1(this,a);this.WL=0}C4.prototype=new $1;C4.prototype.constructor=C4;C4.prototype.B=function(){return this.WL};C4.prototype.lB=function(a){var b=BL(),c=a.mk;a=a.Ah;this.WL=pS(b,c^(c>>>16|0),My(W(),a));return this};C4.prototype.$classData=q({K9:0},!1,"scala.collection.mutable.HashMap$$anon$5",{K9:1,fH:1,Sa:1,g:1,Ka:1,M:1,N:1});function D4(a){this.iu=0;this.Ur=null;this.vC=0;this.uC=null;a2(this,a)} D4.prototype=new c2;D4.prototype.constructor=D4;D4.prototype.DJ=function(a){return a.xm};D4.prototype.$classData=q({P9:0},!1,"scala.collection.mutable.HashSet$$anon$1",{P9:1,RS:1,Sa:1,g:1,Ka:1,M:1,N:1});function E4(a){this.iu=0;this.Ur=null;this.vC=0;this.uC=null;a2(this,a)}E4.prototype=new c2;E4.prototype.constructor=E4;E4.prototype.DJ=function(a){return a};E4.prototype.$classData=q({Q9:0},!1,"scala.collection.mutable.HashSet$$anon$2",{Q9:1,RS:1,Sa:1,g:1,Ka:1,M:1,N:1}); function F4(a){this.iu=0;this.Ur=null;this.vC=0;this.uC=null;this.XL=0;if(null===a)throw null;a2(this,a);this.XL=0}F4.prototype=new c2;F4.prototype.constructor=F4;F4.prototype.B=function(){return this.XL};F4.prototype.DJ=function(a){this.XL=G4(a.nk);return this};F4.prototype.$classData=q({R9:0},!1,"scala.collection.mutable.HashSet$$anon$3",{R9:1,RS:1,Sa:1,g:1,Ka:1,M:1,N:1});function H4(a,b,c,d){this.iq=this.DC=this.fz=null;r2(this,a,b,c,d)}H4.prototype=new t2;H4.prototype.constructor=H4; H4.prototype.zK=function(a){return G(new H,a.Oo,a.Vr)};H4.prototype.$classData=q({u$:0},!1,"scala.collection.mutable.RedBlackTree$EntriesIterator",{u$:1,iT:1,Sa:1,g:1,Ka:1,M:1,N:1});function I4(a,b,c,d){this.iq=this.DC=this.fz=null;r2(this,a,b,c,d)}I4.prototype=new t2;I4.prototype.constructor=I4;I4.prototype.zK=function(a){return a.Oo};I4.prototype.$classData=q({v$:0},!1,"scala.collection.mutable.RedBlackTree$KeysIterator",{v$:1,iT:1,Sa:1,g:1,Ka:1,M:1,N:1}); function J4(a,b,c,d){this.iq=this.DC=this.fz=null;r2(this,a,b,c,d)}J4.prototype=new t2;J4.prototype.constructor=J4;J4.prototype.zK=function(a){return a.Vr};J4.prototype.$classData=q({y$:0},!1,"scala.collection.mutable.RedBlackTree$ValuesIterator",{y$:1,iT:1,Sa:1,g:1,Ka:1,M:1,N:1});function Uu(a,b){this.z3=b}Uu.prototype=new p;Uu.prototype.constructor=Uu;f=Uu.prototype;f.uj=function(a,b){return 0>=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)}; f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)}; f.Xj=function(a,b){return 0e;){var g=e;switch(g){case 0:g=a;break;case 1:g=b;break;default:throw aL(new bL,g+" is out of bounds (min 0, max 1)");}d=c.C(d,My(W(),g));e=1+e|0}return c.Ma(d,2)};f.Da=function(a,b){var c=this.jG.Da(a.h(),b.h());return 0!==c?c:this.kG.Da(a.j(),b.j())};f.$classData=q({Q3:0},!1,"scala.math.Ordering$Tuple2Ordering",{Q3:1,g:1,lm:1,Ji:1,mm:1,km:1,l:1});function jB(a){this.lG=a}jB.prototype=new p; jB.prototype.constructor=jB;f=jB.prototype;f.D=function(a){return!!(a&&a.$classData&&a.$classData.rb.Lk)};f.i=function(a){if(a&&a.$classData&&a.$classData.rb.Lk){var b=this.uh();a=a.uh();b=b===a}else b=!1;return b};f.B=function(){var a=this.lG;return My(W(),a)};f.u=function(){return nfa(this,this.lG)};f.uh=function(){return this.lG};f.si=function(a){var b=this.lG;return rh(th(),b,a)};f.$classData=q({V3:0},!1,"scala.reflect.ClassTag$GenericClassTag",{V3:1,g:1,Lk:1,nm:1,pm:1,l:1,v:1}); function K4(){}K4.prototype=new RZ;K4.prototype.constructor=K4;function L4(){}L4.prototype=K4.prototype;K4.prototype.uJ=function(a){a=null===a?"null":nb(a);ff(this,null===a?"null":a)};class GM extends N2{constructor(a){super();this.K1=a;yF(this,null,null,!0);if(null===a)throw le();}qj(){return"Flags \x3d '"+this.K1+"'"}}GM.prototype.$classData=q({J1:0},!1,"java.util.DuplicateFormatFlagsException",{J1:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1}); class IW extends N2{constructor(a,b){super();this.N1=a;this.M1=b;yF(this,null,null,!0);if(null===a)throw le();}qj(){return"Conversion \x3d "+hc(this.M1)+", Flags \x3d "+this.N1}}IW.prototype.$classData=q({L1:0},!1,"java.util.FormatFlagsConversionMismatchException",{L1:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class OM extends N2{constructor(a){super();this.d2=a;yF(this,null,null,!0)}qj(){return this.d2}} OM.prototype.$classData=q({c2:0},!1,"java.util.IllegalFormatArgumentIndexException",{c2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class SM extends N2{constructor(a){super();this.f2=a;yF(this,null,null,!0)}qj(){return"Code point \x3d 0x"+(+(this.f2>>>0)).toString(16)}}SM.prototype.$classData=q({e2:0},!1,"java.util.IllegalFormatCodePointException",{e2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1}); class JW extends N2{constructor(a,b){super();this.i2=a;this.h2=b;yF(this,null,null,!0);if(null===b)throw le();}qj(){return String.fromCharCode(this.i2)+" !\x3d "+this.h2.qi.name}}JW.prototype.$classData=q({g2:0},!1,"java.util.IllegalFormatConversionException",{g2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class HW extends N2{constructor(a){super();this.k2=a;yF(this,null,null,!0);if(null===a)throw le();}qj(){return"Flags \x3d '"+this.k2+"'"}} HW.prototype.$classData=q({j2:0},!1,"java.util.IllegalFormatFlagsException",{j2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class GW extends N2{constructor(a){super();this.m2=a;yF(this,null,null,!0)}qj(){return""+this.m2}}GW.prototype.$classData=q({l2:0},!1,"java.util.IllegalFormatPrecisionException",{l2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class IM extends N2{constructor(a){super();this.o2=a;yF(this,null,null,!0)}qj(){return""+this.o2}} IM.prototype.$classData=q({n2:0},!1,"java.util.IllegalFormatWidthException",{n2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class PM extends N2{constructor(a){super();this.r2=a;yF(this,null,null,!0);if(null===a)throw le();}qj(){return"Format specifier '"+this.r2+"'"}}PM.prototype.$classData=q({q2:0},!1,"java.util.MissingFormatArgumentException",{q2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1}); class LM extends N2{constructor(a){super();this.t2=a;yF(this,null,null,!0);if(null===a)throw le();}qj(){return this.t2}}LM.prototype.$classData=q({s2:0},!1,"java.util.MissingFormatWidthException",{s2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1});class FW extends N2{constructor(a){super();this.x2=a;yF(this,null,null,!0);if(null===a)throw le();}qj(){return"Conversion \x3d '"+this.x2+"'"}}FW.prototype.$classData=q({w2:0},!1,"java.util.UnknownFormatConversionException",{w2:1,Np:1,ak:1,Te:1,qd:1,pc:1,g:1,l:1}); function M4(){this.ld="type alias"}M4.prototype=new XS;M4.prototype.constructor=M4;f=M4.prototype;f.H=function(){return"Als"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof M4};f.B=function(){return 65928};f.u=function(){return"Als"};f.$classData=q({OT:0},!1,"mlscript.Als$",{OT:1,EE:1,sz:1,sx:1,g:1,E:1,v:1,l:1});var N4;function Ap(){N4||(N4=new M4);return N4} function um(a,b,c){this.mz=null;this.oz=this.pz=0;this.qz=this.nz=null;this.Rn=0;this.Xo=a;this.Tn=b;this.Un=c;Nq(this)}um.prototype=new AS;um.prototype.constructor=um;f=um.prototype;f.H=function(){return"Case"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.Xo;case 1:return this.Tn;case 2:return this.Un;default:return $K(W(),a)}};f.D=function(a){return a instanceof um};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof um){var b=this.Xo,c=a.Xo;if(null===b?null===c:b.i(c))if(b=this.Tn,c=a.Tn,null===b?null===c:b.i(c))return b=this.Un,a=a.Un,null===b?null===a:b.i(a)}return!1};f.$classData=q({mU:0},!1,"mlscript.Case",{mU:1,yM:1,g:1,zM:1,Ta:1,E:1,v:1,l:1});function zP(a){a.Jw||(a.Kw=Faa(a),a.Jw=!0);return a.Kw} function cl(){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.Yo=!1;this.Kw=null;this.Jw=!1}cl.prototype=new M_;cl.prototype.constructor=cl;function O4(){}O4.prototype=cl.prototype;function fP(){this.SC=this.RC=null;this.UC=this.VC=0;this.WC=this.TC=null;this.cl=0}fP.prototype=new p;fP.prototype.constructor=fP;function P4(){}f=P4.prototype=fP.prototype; f.jb=function(){if(this instanceof No)var a="definition";else if(this instanceof Oo)a="type declaration";else throw new w(this);return a};f.Vj=function(){return eP(this)};f.Wr=function(){return Mx(this)};f.nv=function(){0===(1&this.cl)<<24>>24&&0===(1&this.cl)<<24>>24&&(this.RC=cP(this),this.cl=(1|this.cl)<<24>>24);return this.RC};f.jn=function(){0===(2&this.cl)<<24>>24&&0===(2&this.cl)<<24>>24&&(this.SC=zq(this),this.cl=(2|this.cl)<<24>>24);return this.SC};f.rn=function(){return this.VC}; f.fm=function(a){this.VC=a};f.qn=function(){return this.UC};f.em=function(a){this.UC=a};f.pn=function(){return this.TC};f.on=function(a){this.TC=a};f.A=function(){0===(4&this.cl)<<24>>24&&0===(4&this.cl)<<24>>24&&(this.WC=Dq(this),this.cl=(4|this.cl)<<24>>24);return this.WC}; class Ff extends Df{constructor(a,b,c){super();this.YC=b;this.wH=c;this.pq=a;yF(this,a,null,!0)}vt(){return this.YC}H(){return"ErrorReport"}G(){return 3}I(a){switch(a){case 0:return this.pq;case 1:return this.YC;case 2:return this.wH;default:return $K(W(),a)}}D(a){return a instanceof Ff}B(){return AL(this)}i(a){if(this===a)return!0;if(a instanceof Ff&&this.pq===a.pq){var b=this.YC,c=a.YC;return(null===b?null===c:b.i(c))?this.wH===a.wH:!1}return!1}} Ff.prototype.$classData=q({OU:0},!1,"mlscript.ErrorReport",{OU:1,IU:1,qd:1,pc:1,g:1,l:1,E:1,v:1});function $t(a){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0;this.ap=a;Nq(this)}$t.prototype=new CS;$t.prototype.constructor=$t;f=$t.prototype;f.H=function(){return"IfBlock"};f.G=function(){return 1};f.I=function(a){return 0===a?this.ap:$K(W(),a)};f.D=function(a){return a instanceof $t};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof $t){var b=this.ap;a=a.ap;return null===b?null===a:b.i(a)}return!1};f.$classData=q({$U:0},!1,"mlscript.IfBlock",{$U:1,uz:1,g:1,vz:1,Ta:1,E:1,v:1,l:1});function gu(a){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0;this.ws=a;Nq(this)}gu.prototype=new CS;gu.prototype.constructor=gu;f=gu.prototype;f.H=function(){return"IfElse"};f.G=function(){return 1};f.I=function(a){return 0===a?this.ws:$K(W(),a)}; f.D=function(a){return a instanceof gu};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof gu){var b=this.ws;a=a.ws;return null===b?null===a:b.i(a)}return!1};f.$classData=q({aV:0},!1,"mlscript.IfElse",{aV:1,uz:1,g:1,vz:1,Ta:1,E:1,v:1,l:1});function Ss(a,b,c,d){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0;this.Qw=a;this.xu=b;this.yu=c;this.wu=d;Nq(this)}Ss.prototype=new CS;Ss.prototype.constructor=Ss;f=Ss.prototype; f.H=function(){return"IfLet"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.Qw;case 1:return this.xu;case 2:return this.yu;case 3:return this.wu;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ss};f.B=function(){var a=lb("IfLet");a=W().C(-889275714,a);var b=this.Qw?1231:1237;a=W().C(a,b);b=this.xu;b=My(W(),b);a=W().C(a,b);b=this.yu;b=My(W(),b);a=W().C(a,b);b=this.wu;b=My(W(),b);a=W().C(a,b);return W().Ma(a,4)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Ss){if(this.Qw===a.Qw){var b=this.xu,c=a.xu;b=null===b?null===c:b.i(c)}else b=!1;if(b&&(b=this.yu,c=a.yu,null===b?null===c:b.i(c)))return b=this.wu,a=a.wu,null===b?null===a:b.i(a)}return!1};f.$classData=q({bV:0},!1,"mlscript.IfLet",{bV:1,uz:1,g:1,vz:1,Ta:1,E:1,v:1,l:1});function Tt(a,b,c){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0;this.Wn=a;this.Xn=b;this.Yn=c;Nq(this)}Tt.prototype=new CS;Tt.prototype.constructor=Tt;f=Tt.prototype; f.H=function(){return"IfOpApp"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.Wn;case 1:return this.Xn;case 2:return this.Yn;default:return $K(W(),a)}};f.D=function(a){return a instanceof Tt};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Tt){var b=this.Wn,c=a.Wn;if(null===b?null===c:b.i(c))if(b=this.Xn,c=a.Xn,null===b?null===c:b.i(c))return b=this.Yn,a=a.Yn,null===b?null===a:b.i(a)}return!1}; f.$classData=q({cV:0},!1,"mlscript.IfOpApp",{cV:1,uz:1,g:1,vz:1,Ta:1,E:1,v:1,l:1});function mu(a,b){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0;this.xs=a;this.ys=b;Nq(this)}mu.prototype=new CS;mu.prototype.constructor=mu;f=mu.prototype;f.H=function(){return"IfOpsApp"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.xs;case 1:return this.ys;default:return $K(W(),a)}};f.D=function(a){return a instanceof mu};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof mu){var b=this.xs,c=a.xs;if(null===b?null===c:b.i(c))return b=this.ys,a=a.ys,null===b?null===a:b.i(a)}return!1};f.$classData=q({dV:0},!1,"mlscript.IfOpsApp",{dV:1,uz:1,g:1,vz:1,Ta:1,E:1,v:1,l:1});function Ut(a,b){this.rs=null;this.ts=this.us=0;this.vs=this.ss=null;this.dl=0;this.Km=a;this.Lm=b;Nq(this)}Ut.prototype=new CS;Ut.prototype.constructor=Ut;f=Ut.prototype;f.H=function(){return"IfThen"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.Km;case 1:return this.Lm;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ut};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Ut){var b=this.Km,c=a.Km;if(null===b?null===c:b.i(c))return b=this.Lm,a=a.Lm,null===b?null===a:b.i(a)}return!1};f.$classData=q({eV:0},!1,"mlscript.IfThen",{eV:1,uz:1,g:1,vz:1,Ta:1,E:1,v:1,l:1});function wn(a,b){this.nD=a;this.mD=b}wn.prototype=new ES; wn.prototype.constructor=wn;f=wn.prototype;f.xa=function(){var a=Sp(Qp(),"get "+zl(Al(),this.nD)+"() "),b=this.mD;if(b instanceof fe)b=b.aa,b=(new Io((t(),new L(b)))).xa();else{if(!(b instanceof Ud))throw new w(b);b=b.fa;for(var c=Qp().ye;!b.b();){var d=b.e();c=Iz(c,d.xa());b=b.f()}b=c}return Rp(a,Oz(b))};f.H=function(){return"JSClassGetter"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.nD;case 1:return this.mD;default:return $K(W(),a)}}; f.D=function(a){return a instanceof wn};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof wn&&this.nD===a.nD){var b=this.mD;a=a.mD;return null===b?null===a:b.i(a)}return!1};f.$classData=q({EV:0},!1,"mlscript.JSClassGetter",{EV:1,FV:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function vn(a,b,c){this.pD=a;this.qD=b;this.oD=c}vn.prototype=new ES;vn.prototype.constructor=vn;f=vn.prototype; f.xa=function(){var a=Rp(Rp(Sp(Qp(),zl(Al(),this.pD)),Op(Lp(),this.qD)),Qp().bA),b=this.oD;if(b instanceof fe)b=b.aa,b=(new Io((t(),new L(b)))).xa();else{if(!(b instanceof Ud))throw new w(b);b=b.fa;for(var c=Qp().ye;!b.b();){var d=b.e();c=Iz(c,d.xa());b=b.f()}b=c}return Rp(a,Oz(b))};f.H=function(){return"JSClassMethod"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.pD;case 1:return this.qD;case 2:return this.oD;default:return $K(W(),a)}}; f.D=function(a){return a instanceof vn};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof vn&&this.pD===a.pD){var b=this.qD,c=a.qD;if(null===b?null===c:b.i(c))return b=this.oD,a=a.oD,null===b?null===a:b.i(a)}return!1};f.$classData=q({GV:0},!1,"mlscript.JSClassMethod",{GV:1,FV:1,el:1,Xc:1,g:1,E:1,v:1,l:1});function Q4(){this.ld="mixin"}Q4.prototype=new XS;Q4.prototype.constructor=Q4;f=Q4.prototype;f.H=function(){return"Mxn"};f.G=function(){return 0}; f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof Q4};f.B=function(){return 77827};f.u=function(){return"Mxn"};f.$classData=q({GW:0},!1,"mlscript.Mxn$",{GW:1,EE:1,sz:1,sx:1,g:1,E:1,v:1,l:1});var R4;function cp(){R4||(R4=new Q4);return R4}function S4(){this.mz=null;this.oz=this.pz=0;this.qz=this.nz=null;this.Rn=0;Nq(this)}S4.prototype=new AS;S4.prototype.constructor=S4;f=S4.prototype;f.H=function(){return"NoCases"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)}; f.D=function(a){return a instanceof S4};f.B=function(){return-546441758};f.u=function(){return"NoCases"};f.$classData=q({VW:0},!1,"mlscript.NoCases$",{VW:1,yM:1,g:1,zM:1,Ta:1,E:1,v:1,l:1});var T4;function zm(){T4||(T4=new S4);return T4}function U4(a){Nq(a);if(a instanceof yo)var b=a.gb.Zr();else{if(!(a instanceof Zn))throw new w(a);b=a.Rb}a.Cf=b}function Ct(){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.sE=this.tE=this.Cf=null;this.gp=0}Ct.prototype=new ZS; Ct.prototype.constructor=Ct;function V4(){}V4.prototype=Ct.prototype;Ct.prototype.jb=function(){return cy(this)};Ct.prototype.Vj=function(){return eP(this)};Ct.prototype.Wr=function(){return Mx(this)};function Cp(a){0===(1&a.gp)<<24>>24&&0===(1&a.gp)<<24>>24&&(a.tE=gea(a),a.gp=(1|a.gp)<<24>>24);return a.tE}Ct.prototype.nv=function(){0===(2&this.gp)<<24>>24&&0===(2&this.gp)<<24>>24&&(this.sE=cP(this),this.gp=(2|this.gp)<<24>>24);return this.sE}; function ax(a,b,c,d,e){this.nc=this.px=null;this.Ju=b;this.Rl=c;this.rk=d;this.Ql=e;MS(this,a,Ap())}ax.prototype=new NS;ax.prototype.constructor=ax;f=ax.prototype;f.Ea=function(){return this.Ju};f.Yr=function(){return this.Rl};f.Ag=function(){return this.rk};f.fd=function(){return this.Rl.pb};f.Ua=function(){return this.Rl.gb.V};f.cG=function(){return this.Rl.gb};f.aG=function(){return nf()};f.tr=function(){return!this.Rl.Hj.b()};f.Mp=function(){return!0}; function qfa(a,b,c,d,e){var g=new Iw(d.S,d.Ec,d.hc,d.Ed,1+d.da|0,d.Pc,d.Zc,d.Lb,d.yc,d.tb,d.$a,d.od,d.cb),h=a.nc;d=d.da;var k=a.Rl,l=a.rk,m=x=>{var A=x.kc,B=x.hb.Kc(b,c,g,e);return new tl(A,hD(B),x.Rd)};if(l===u())m=u();else{var n=l.e(),r=n=new z(m(n),u());for(l=l.f();l!==u();){var v=l.e();v=new z(m(v),u());r=r.p=v;l=l.f()}m=n}return new ax(h,d,k,m,a.Ql.Kc(b,c,g,e))} function rfa(a,b,c){var d=a.nc,e=a.Ju,g=a.Rl,h=a.rk,k=r=>{var v=r.kc,x=c.ba(t().d,r.hb);return new tl(v,hD(x),r.Rd)};if(h===u())k=u();else{var l=h.e(),m=l=new z(k(l),u());for(h=h.f();h!==u();){var n=h.e();n=new z(k(n),u());m=m.p=n;h=h.f()}k=l}return new ax(d,e,g,k,c.ba(b,a.Ql))} function sfa(a,b,c){var d=a.nc,e=a.Ju,g=a.Rl,h=a.rk,k=r=>{var v=r.kc,x=c.ba(new TB(b),r.hb);return new tl(v,hD(x),r.Rd)};if(h===u())k=u();else{var l=h.e(),m=l=new z(k(l),u());for(h=h.f();h!==u();){var n=h.e();n=new z(k(n),u());m=m.p=n;h=h.f()}k=l}return new ax(d,e,g,k,c.ba(b,a.Ql))}f.H=function(){return"TypedNuAls"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.Ju;case 1:return this.Rl;case 2:return this.rk;case 3:return this.Ql;default:return $K(W(),a)}}; f.D=function(a){return a instanceof ax};f.B=function(){var a=lb("TypedNuAls");a=W().C(-889275714,a);var b=this.Ju;a=W().C(a,b);b=this.Rl;b=My(W(),b);a=W().C(a,b);b=this.rk;b=My(W(),b);a=W().C(a,b);b=this.Ql;b=My(W(),b);a=W().C(a,b);return W().Ma(a,4)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof ax&&a.nc===this.nc){if(this.Ju===a.Ju){var b=this.Rl,c=a.Rl;b=null===b?null===c:b.i(c)}else b=!1;if(b&&(b=this.rk,c=a.rk,null===b?null===c:b.i(c)))return b=this.Ql,a=a.Ql,null===b?null===a:mC(b,a)}return!1};f.cm=function(a,b){return sfa(this,a,b)};f.yl=function(a,b,c){return rfa(this,a,c)};f.ro=function(a,b,c,d){return qfa(this,a,b,c,d)};f.$classData=q({zX:0},!1,"mlscript.NuTypeDefs$TypedNuAls",{zX:1,YH:1,g:1,Rs:1,nx:1,E:1,v:1,l:1}); function By(a,b,c){this.q=null;this.xk=b;this.Ul=c;if(null===a)throw null;this.q=a}By.prototype=new fT;By.prototype.constructor=By;function dca(a,b,c){var d=a.q,e=a.xk;if(e===u())c=u();else{var g=e.e(),h=g=new z(cz(g,b,c),u());for(e=e.f();e!==u();){var k=e.e();k=new z(cz(k,b,c),u());h=h.p=k;e=e.f()}c=g}a=a.Ul;return new By(d,c,a.b()?R():new L(b.n(a.o())))} function wB(a,b,c,d){var e=a.q,g=a.xk;if(g===u())d=u();else{var h=g.e(),k=h=new z(h.yl(b,!0,c,d),u());for(g=g.f();g!==u();){var l=g.e();l=new z(l.yl(b,!0,c,d),u());k=k.p=l;g=g.f()}d=h}a=a.Ul;a.b()?b=R():(a=a.o(),b=new L(c.ba(b,a)));return new By(e,d,b)} function ica(a,b,c,d){var e=a.q,g=a.xk;if(g===u())d=u();else{var h=g.e(),k=h=new z(h.cm(b,c,d),u());for(g=g.f();g!==u();){var l=g.e();l=new z(l.cm(b,c,d),u());k=k.p=l;g=g.f()}d=h}a=a.Ul;a.b()?b=R():(a=a.o(),b=new L(c.ba(b,a)));return new By(e,d,b)}f=By.prototype;f.u=function(){var a=Xq(this.xk,this.Ul);return"TypedTypingUnit("+CF(GF(),a)+")"};f.H=function(){return"TypedTypingUnit"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.xk;case 1:return this.Ul;default:return $K(W(),a)}};f.D=function(a){return a instanceof By};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof By&&a.q===this.q){var b=this.xk,c=a.xk;if(null===b?null===c:b.i(c))return b=this.Ul,a=a.Ul,null===b?null===a:b.i(a)}return!1};f.$classData=q({HX:0},!1,"mlscript.NuTypeDefs$TypedTypingUnit",{HX:1,Paa:1,Gg:1,g:1,Hg:1,E:1,v:1,l:1}); function Cw(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}Cw.prototype=new d3;Cw.prototype.constructor=Cw;function W4(){}W4.prototype=Cw.prototype; class Gf extends Df{constructor(a,b,c){super();this.aF=b;this.YI=c;this.pq=a;yF(this,a,null,!0)}vt(){return this.aF}H(){return"WarningReport"}G(){return 3}I(a){switch(a){case 0:return this.pq;case 1:return this.aF;case 2:return this.YI;default:return $K(W(),a)}}D(a){return a instanceof Gf}B(){return AL(this)}i(a){if(this===a)return!0;if(a instanceof Gf&&this.pq===a.pq){var b=this.aF,c=a.aF;return(null===b?null===c:b.i(c))?this.YI===a.YI:!1}return!1}} Gf.prototype.$classData=q({h_:0},!1,"mlscript.WarningReport",{h_:1,IU:1,qd:1,pc:1,g:1,l:1,E:1,v:1});function ym(a){this.mz=null;this.oz=this.pz=0;this.qz=this.nz=null;this.Rn=0;this.dn=a;Nq(this)}ym.prototype=new AS;ym.prototype.constructor=ym;f=ym.prototype;f.H=function(){return"Wildcard"};f.G=function(){return 1};f.I=function(a){return 0===a?this.dn:$K(W(),a)};f.D=function(a){return a instanceof ym};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof ym){var b=this.dn;a=a.dn;return null===b?null===a:b.i(a)}return!1};f.$classData=q({l_:0},!1,"mlscript.Wildcard",{l_:1,yM:1,g:1,zM:1,Ta:1,E:1,v:1,l:1});function mn(a,b,c,d,e){this.TA=a;this.Tx=b;this.eJ=c;this.Sx=d;this.dJ=e}mn.prototype=new p;mn.prototype.constructor=mn;f=mn.prototype;f.xo=function(){return this.TA};f.zo=function(){return this.Tx};f.mv=function(){return this.Sx};f.u=function(){return"trait "+this.TA};f.H=function(){return"TraitSymbol"}; f.G=function(){return 5};f.I=function(a){switch(a){case 0:return this.TA;case 1:return this.Tx;case 2:return this.eJ;case 3:return this.Sx;case 4:return this.dJ;default:return $K(W(),a)}};f.D=function(a){return a instanceof mn};f.B=function(){return AL(this)};f.i=function(a){if(this===a)return!0;if(a instanceof mn&&this.TA===a.TA&&this.Tx===a.Tx){var b=this.eJ,c=a.eJ;if(null===b?null===c:b.i(c))if(b=this.Sx,c=a.Sx,null===b?null===c:b.i(c))return b=this.dJ,a=a.dJ,null===b?null===a:b.i(a)}return!1}; f.$classData=q({C_:0},!1,"mlscript.codegen.TraitSymbol",{C_:1,g:1,UA:1,cr:1,mt:1,E:1,v:1,l:1});function X4(a,b){a.Hb&&Nx(a,b)}function tfa(a,b,c,d){a.Hb?Lx(a,b,c,d):Es(c)}function Y4(a){var b="tmp"+a.XA;a.XA=1+a.XA|0;return b}function Z4(a,b){for(var c=Y4(a);b.hc.L(c);)c=Y4(a);return c}function $4(a,b,c,d,e){e.Hk(a5(a,b,d),new U(()=>Xu().X()))}function ufa(a,b,c,d,e,g){g.Hk(a5(a,b,e),new U(()=>Xu().X())).Hk((t(),new fe(c)),new U(()=>TE(PE()))).zc(d)} function b5(a,b,c,d,e,g){g.Hk(a5(a,b,e),new U(()=>Xu().X())).Hk((t(),new Ud(c)),new U(()=>TE(PE()))).zc(d)} function c5(a,b,c,d,e,g){var h=TE(PE());c=c.m();d=new x0(c,d);d=new xo(d,new y(k=>{if(null!==k){var l=k.h(),m=k.j();if(l instanceof vl&&"_"===l.x)return t(),k=new vl("_"),k=G(new H,m,k),new L(k)}if(null!==k&&(m=k.h(),l=k.j(),m instanceof vl)){var n=m.x;n=Xz(Q(),n);n.b()?n=!1:(n=n.o(),n=Ea(n),n=bE(Pr(),n));if(n)return t(),k=G(new H,l,m),new L(k)}if(null!==k){var r=k.h();m=k.j();if(null!==r)return k=g.Hk(GE(b),new U(()=>Xu().X())).Hk(m,new U(()=>{var v=new vl(Z4(a,e));return OS(v,r)})),l=G(new H,k, r),h.$(l),t(),k=G(new H,m,k),new L(k)}throw new w(k);}));Od();d=Pd(u(),d);return G(new H,h.ha(),d)}function d5(a,b,c,d,e,g,h){c=c.m();c=new xo(c,new y(k=>{if(null!==k){var l=k.j();k=e5(a,k.h(),b.ZP,d);var m=O().c;return f5(a,k,l,!1,d,e,g,h,m)}throw new w(k);}));Od();return Pd(u(),c)} function e5(a,b,c,d){var e=()=>{if(b instanceof vl){if(a.Hb&&a.F){var l=ut(Q(),"| ",a.r)+"The scrutinee does not need an alias.";ff(gf(),l+"\n")}return new wY(t().d,b,c)}if(a.YA.EF(b))l=a.YA.LF(b);else{l=new vl(Z4(a,d));l=OS(l,b);var m=a.YA.zv,n=new bk(b),r=Qb(n.py);b:{var v=r^(r>>>16|0);r=1+m.yv|0;if(r>=m.jK){var x=m.wl,A=x.a.length,B=A<<1,C=new (md(EN).Ia)(B);m.wl=C;m.jK=Eb(B*m.RQ);for(B=0;B"[Desugarer.destructPattern] scrutinee \x3d "+b.nh+"; pattern \x3d "+c),new U(()=>{var n=!1,r=null,v=!1,x=null,A=!1,B=null;if(c instanceof vl&&(n=!0,r=c,"_"===r.x&&d)){$4(a,b,iu(ju(),r.A()),e,h);var C=new SE(b,r.A().ha()),D=O().c;return new z(C,D)}if(n&&"_"===r.x)return O().c;if(n&&("true"===r.x||"false"===r.x)){b5(a,b,r,iu(ju(),r.A()),e,h);var F=r,I=b.nh.A().ha(),M=new OE(b,F,dl(r.A().ha(),I));M.lf=xY(b).ha();X4(a,new U(()=>"Add bindings to the clause: "+ xY(b)));var N=O().c;return new z(M,N)}if(c instanceof Dl){b5(a,b,c,iu(ju(),c.A()),e,h);var P=b.nh.A().ha(),T=new OE(b,c,dl(c.A().ha(),P));T.lf=xY(b).ha();X4(a,new U(()=>"Add bindings to the clause: "+xY(b)));var Y=O().c;return new z(T,Y)}if(n){var Z=r.x,S=Xz(Q(),Z),ea=new y(rc=>{rc=Ea(rc);return bE(Pr(),rc)});if(!S.b()&&ea.n(S.o())){var ia=b.nh.A().ha(),X=dl(r.A().ha(),ia);if(d){$4(a,b,iu(ju(),r.A()),e,h);var sa=new SE(b,X),Ja=new YE(r,GE(b),!d,X),Xa=O().c;return new z(sa,new z(Ja,Xa))}var Fa=new YE(r, b.nh,!d,X),za=O().c;return new z(Fa,za)}}if(n){var Qa=r.x,Ma=!1,Ga=null,ab=e.tb.U(Qa),Hb=new U(()=>uC(e,e,Qa)),bc=ab.b()?Es(Hb):ab;a:{if(bc instanceof L){Ma=!0;Ga=bc;var yb=Ga.k;if(yb instanceof VP&&yb.J===a){var tb=yb.fd();if(Ot(new E(tb),Bp()))var eb=!0;else{var kb=yb.fd();eb=Ot(new E(kb),zp())}if(eb)break a}}if(Ma){var Rb=Ga.k;if(Rb instanceof VP&&Rb.J===a){var Gb=Rb.fd();if(Ot(new E(Gb),Fp())){var vb=new Te(new Ue(J(new K,["Cannot match on trait `","`"]))),Tb=[We(Xe(),Qa)];throw iT(new jT,Ye(vb, J(new K,Tb)),r.A());}}}if(!(Ma&&Ga.k instanceof YX&&Ga.k.FE===a)){var Nb=new Te(new Ue(J(new K,["Cannot find constructor `","` in scope"]))),ic=[We(Xe(),Qa)];throw iT(new jT,Ye(Nb,J(new K,ic)),r.A());}}X4(a,new U((rc=>()=>"Build a Clause.MatchClass from "+b+" where pattern is "+rc)(r)));b5(a,b,r,iu(ju(),r.A()),e,h);var Va=new UE(b,r,O().c,g5()),cb=O().c;return new z(Va,cb)}if(c instanceof Pl){v=!0;x=c;var zb=x.Za,Ub=x.Qb;if(zb instanceof vl){var jb=zb.x;if(Ub instanceof Gl){var db=Ub.Ra,ub=e.tb.U(jb), Aa=new y(rc=>G(new H,rc.jj,rc.at)),va=ub.b()?R():new L(Aa.n(ub.o())),Ra=new U(()=>{var rc=!1,sd=null,Kc=uC(e,e,jb);if(Kc instanceof L&&(rc=!0,sd=Kc,Kc=sd.k,Kc instanceof fx)){var Qd=Kc.Ab.fd();if(Ot(new E(Qd),Bp()))return t(),rc=Kc.Ab.fd(),sd=px(Kc),Kc=new U(()=>O().c),rc=G(new H,rc,Qt(sd.b()?Es(Kc):sd.o(),new y(Ad=>Ad.h().x))),new L(rc)}if(rc&&(rc=sd.k,rc instanceof ix&&(rc=rc.kh,rc instanceof Yw)))return t(),sd=rc.Ij,Kc=new U(()=>O().c),rc=G(new H,rc.Rf.pb,Qt(sd.b()?Es(Kc):sd.o(),new y(Ad=>Ad.h().x))), new L(rc);rc=new Te(new Ue(J(new K,["Illegal pattern `","`"])));sd=[We(Xe(),jb)];throw iT(new jT,Ye(rc,J(new K,sd)),zb.A());}),rb=va.b()?Es(Ra):va;if(t().d===rb){var xb=new Te(new Ue(J(new K,["Cannot find class `","` in scope"]))),mc=[We(Xe(),jb)];throw iT(new jT,Ye(xb,J(new K,mc)),zb.A());}if(rb instanceof L){var Ha=rb.k;if(null!==Ha){var Ka=Ha.h(),Oa=Ha.j(),Na=db.K();if(Pe(new E(Na),Oa.K())){var Da=db.m(),ta=c5(a,b,new Ef(Da,new y(rc=>rc.j().ya)),Oa,e,k);if(null!==ta)var Ya=G(new H,ta.h(),ta.j()); else throw new w(ta);var dc=Ya.h(),ka=Ya.j();b5(a,b,zb,iu(ju(),x.A()),e,h);var ya=c.A().ha(),Sa=new UE(b,zb,ka,dl(g5(),ya));X4(a,new U(()=>"Build a Clause.MatchClass from "+b+" where pattern is "+c));X4(a,new U(()=>"Fragments: "+l));X4(a,new U(()=>"The locations of the clause: "+Sa.tF));var xc=d5(a,b,dc,e,g,h,k);return new z(Sa,xc)}var Sb=Oa.K();db.K();var uc=new Te(new Ue(J(new K,"; ; expects ; ; but found ; ;".split(";")))),Lb=We(Xe(),Ka.ld),lc=We(Xe(),jb),Xb=We(Xe(),""+Sb),ec=We(Xe(),WF(ve(),"parameter", Sb,!1));Xe();var Ab=db.K(),Ob=[Lb,lc,Xb,ec,We(0,""+Ab),We(Xe(),WF(ve(),"parameter",Sb,!1))];throw iT(new jT,Ye(uc,J(new K,Ob)),x.A());}}throw new w(rb);}}}if(v){var fb=x.Za,Wa=x.Qb;if(fb instanceof Pl){var bb=fb.Za,Ia=fb.Qb;if(bb instanceof vl){var Ua=bb.x;if(Ia instanceof Gl){var pc=Ia.Ra;if(pc instanceof z){var sc=pc.z,Ba=pc.p;if(null!==sc){var ob=new L(sc);if(!ob.b()){var nc=ob.k.j();if(null!==nc){var Ib=nc.ya,vc=O().c;if((null===vc?null===Ba:vc.i(Ba))&&Wa instanceof Gl){var Vb=Wa.Ra;if(Vb instanceof z){var fc=Vb.z,Bc=Vb.p;if(null!==fc){var Pb=new L(fc);if(!Pb.b()){var Jb=Pb.k.j();if(null!==Jb){var gc=Jb.ya,Cb=O().c;if(null===Cb?null===Bc:Cb.i(Bc)){var cc=!1,yc=null,Mc=e.tb.U(Ua);if(t().d===Mc){var qc=new Te(new Ue(J(new K,["Cannot find operator `","` in the context"]))),oc=[We(Xe(),Ua)];throw iT(new jT,Ye(qc,J(new K,oc)),bb.A());}if(Mc instanceof L){cc=!0;yc=Mc;var Qc=yc.k,jc=Qc.at.K();if(Pe(new E(jc),2)){var sb=O().c,Gc=c5(a,b,new z(Ib,new z(gc,sb)),Qc.at,e,k);if(null!==Gc)var Wb=G(new H,Gc.h(), Gc.j());else throw new w(Gc);var Cc=Wb.h(),Fc=Wb.j();b5(a,b,bb,iu(ju(),x.A()),e,h);var qd=new UE(b,bb,Fc,g5());X4(a,new U(()=>"Build a Clause.MatchClass from "+b+" where operator is "+bb));var Yb=d5(a,b,Cc,e,g,h,k);return new z(qd,Yb)}}if(cc){var Nc=yc.k;Nc.at.K();var ad=Nc.at.K(),Uc=new Te(new Ue(J(new K,[""," `","` expects "," "," but found two parameters"]))),cd=[We(Xe(),Nc.jj.ld),We(Xe(),Ua),We(Xe(),""+ad),We(Xe(),WF(ve(),"parameter",ad,!1))];throw iT(new jT,Ye(Uc,J(new K,cd)),x.A());}throw new w(Mc); }}}}}}}}}}}}}}if(c instanceof Fl){A=!0;B=c;var kc=B.gg;if(!1===B.bi&&kc instanceof Gl)return h5(a,kc,b,e,k,g,h)}if(c instanceof Gl)return h5(a,c,b,e,k,g,h);if(A){var Vc=B.gg;if(!1===B.bi)return vfa(a,b,Vc,d,e,g,h,k,l)}var Hc=new Ue(J(new K,["illegal pattern"]));throw iT(new jT,Ye(new Te(Hc),u()),c.A());}),new y(n=>"[Desugarer.destructPattern] Result: "+ze(n,"",", ","")))};function g5(){return TE(PE()).ha()} var xfa=function wfa(a,b,c){var e=()=>{var l=b.zs;if(l instanceof L){var m=l.k;if(m instanceof Xl)return wfa(a,m,new z(b.bp,c))}if(c.b())return G(new H,b.bp,l);m=er(new z(b.bp,c)).m();m=new xo(m,new y(n=>{if(n instanceof $t)return n.ap;O();t();return new CQ(new fe(n))}));Od();m=Pd(u(),m);return G(new H,new $t(m),l)},g=new y(l=>"[unfoldNestedIf] ("+ag(ca(l.h()))+", "+l.j()+")");if(a.Hb){if(a.F){var h=ut(Q(),"| ",a.r)+"[unfoldNestedIf]";ff(gf(),h+"\n")}a.r=1+a.r|0;try{var k=e()}finally{a.r=-1+a.r|0}dx(new E(g), a.qa)&&a.F&&(e=""+ut(Q(),"| ",a.r)+g.n(k),ff(gf(),e+"\n"))}else k=e();return k}; function yfa(a,b,c,d,e,g){var h=()=>{var n=Xu().X(),r=TE(PE()),v=TE(PE());i5(a,b,b0(),new NE(O().c,O().c),v,r,d,e,g,n);c.b()||(n=c.o(),v=new NE(O().c,O().c),n=G(new H,v,n),r.$(n));a.Hb&&a.F&&(n=ut(Q(),"| ",a.r)+"Decision paths:",ff(gf(),n+"\n"));for(n=r.m();n.s();){var x=n.t();a:{if(null!==x&&(v=new L(x),!v.b())){x=v.k.h();v=v.k.j();a.Hb&&a.F&&(v=ut(Q(),"| ",a.r)+("+ "+x+" \x3d\x3e ")+v,ff(gf(),v+"\n"));break a}throw new w(x);}}return r.ha()},k=new y(n=>{var r=n.K();n=n.K();return"[desugarIf] produces "+ r+" "+WF(ve(),"path",n,!1)});if(a.Hb){if(a.F){var l=ut(Q(),"| ",a.r)+"[desugarIf] with fallback "+c;ff(gf(),l+"\n")}a.r=1+a.r|0;try{var m=h()}finally{a.r=-1+a.r|0}dx(new E(k),a.qa)&&a.F&&(h=""+ut(Q(),"| ",a.r)+k.n(m),ff(gf(),h+"\n"))}else m=h();return m} function a5(a,b,c){var d=()=>{var k=b.nh;if(k instanceof vl){var l=k.x;if(a.Hb&&a.F){var m=ut(Q(),"| ",a.r)+"The original scrutinee is an reference.";ff(gf(),m+"\n")}l=c.hc.U(l);if(l instanceof L&&(m=l.k,m instanceof qx))return l=m.vA.kt,l.b()?(t(),k=new fe(k.x)):(k=l.o()|0,t(),k=new Ud(k)),k;if(l instanceof L||t().d===l)return t(),new fe(k.x);throw new w(l);}a.Hb&&a.F&&(k=ut(Q(),"| ",a.r)+"The scrutinee was localized because it might be effectful.",ff(gf(),k+"\n"));k=b.no;if(t().d===k)throw vS(new wS, "check your `makeScrutinee`");if(k instanceof L)return k=k.k,t(),new fe(k.x);throw new w(k);},e=a.qa;if(a.Hb){if(a.F){var g=ut(Q(),"| ",a.r)+"[getScrutineeKey] "+b;ff(gf(),g+"\n")}a.r=1+a.r|0;try{var h=d()}finally{a.r=-1+a.r|0}dx(new E(e),a.qa)&&a.F&&(d=""+ut(Q(),"| ",a.r)+e.n(h),ff(gf(),d+"\n"))}else h=d();return h} var zfa=function j5(a,b,c,d,e,g,h){tfa(a,new U(()=>"[checkExhaustive] "+b.jb()),new U(()=>{if(!(b instanceof HE)){if(IE()===b){var l=!1,m=null;if(c instanceof L){l=!0;m=c;var n=m.k;if(n instanceof CE){var r=n.hr;if(Pe(new E(n.Fh),b))throw l=new Te(new Ue(J(new K,["The case when this is false is not handled: ",""]))),m=[We(Xe(),Zz(r,!1))],iT(new jT,Ye(l,J(new K,m)),r.A());xm("`MissingCase` are not supposed to be the true branch of `IfThenElse`")}}l&&m.k instanceof DE&&xm("`MissingCase` are not supposed to be a case of `Match`"); a:if(c instanceof L&&c.k instanceof HE)r=!0;else{if(c instanceof L&&(r=c.k,IE()===r)){r=!0;break a}r=t().d===c?!0:!1}r&&xm("Program reached and unexpected state.");throw new w(c);}if(b instanceof CE)r=b.Dk,j5(a,b.Fh,(t(),new L(b)),d,e,g,h),j5(a,r,(t(),new L(b)),d,e,g,h);else if(b instanceof DE){var v=b.mo;r=b.mh;l=b.nj;m=!1;n=null;var x=g.U(a5(a,v,d));a:{t().d===x&&xm("unreachable case: unknown scrutinee "+v.nh);if(x instanceof L&&(m=!0,n=x,!l.b())){X4(a,new U(()=>"The match has a default branch. So, it is always safe.")); break a}if(m){m=n.k;X4(a,new U(()=>"The exhaustiveness map is"));g.og(new fn((C,D)=>{X4(a,new U(()=>{var F=D.tj();return"- "+C+" -\x3e "+ze(F,"",", ","")}))}));X4(a,new U(()=>"The scrutinee key is "+a5(a,v,d)));X4(a,new U(()=>"Pattern map of the scrutinee:"));m.b()?X4(a,new U(()=>"\x3cEmpty\x3e")):m.og(new fn((C,D)=>{X4(a,new U(()=>"- "+C+" \x3d\x3e "+D))}));tp();n=r.m();var A=Aq(0,new xo(n,new y(C=>{if(C instanceof EE)return O().c;if(C instanceof FE){var D=C.Tj;if(null!==D&&(D=new L(D),!D.b()&&(D= D.k.h(),null!==D))){C=h.U(D.x);D=new U(()=>O().c);var F=new y(I=>I);return C.b()?Es(D):F.n(C.o())}}throw new w(C);})));X4(a,new U(()=>"The match can cover following classes"));X4(a,new U(()=>ze(A,"{",", ","}")));n=r.m();m=m.dG(new Ef(n,new y(C=>{if(C instanceof EE)return C=C.ir,t(),new Ud(C);if(C instanceof FE){var D=C.Tj;if(null!==D&&(D=new L(D),!D.b())){C=D.k.h();tp();Q();D=C.x;Q();D=M2(D,"\\"+hc(35));D=ps(D);Od();D=Pd(u(),D);if(D instanceof z){var F=D.p;if("Tuple"===D.z&&F instanceof z){D=F.z; F=F.p;var I=O().c;if(null===I?null===F:I.i(F)){D=$D(aE(),D);if(t().d===D)return t(),new Ud(C);if(D instanceof L)return C=D.k|0,t(),new fe(C);throw new w(D);}}}t();return new Ud(C)}}throw new w(C);}))).Fk(new y(C=>{if(null!==C){var D=new L(C);if(!D.b()&&(D=D.k.h(),D instanceof Ud&&(D=D.fa,D instanceof vl)))return!A.L(D.x)}if(null!==C&&(D=new L(C),!D.b()&&D.k.h()instanceof fe)||null!==C&&(D=new L(C),!D.b()&&D.k.h()instanceof Ud))return!0;throw new w(C);}));X4(a,new U(()=>"Missing cases"));m.og(new fn((C, D)=>{X4(a,new U(()=>"- "+C+" -\x3e "+D))}));if(m.b())break a;else{var B=m.ka();r=Ye(new Te(new Ue(J(new K,["The match is not exhaustive."]))),u());r=G(new H,r,v.ZP);l=new Te(new Ue(J(new K,["The scrutinee at this position misses "," ","."])));n=[We(Xe(),""+B),We(Xe(),WF(ve(),"case",B,!1))];l=Ye(l,J(new K,n));n=v.nh.A();l=G(new H,l,n);m=m.m();m=new Ao(m);m=new xo(m,new y(C=>{if(null!==C){var D=C.h(),F=C.Sc();if(null!==D){C=D.h();D=D.j();if(C instanceof fe)C=(C.aa|0)+"-ary tuple";else if(C instanceof Ud)C=Zz(C.fa,!1);else throw new w(C);var I="[Missing Case "+(1+F|0)+"/"+B+"]";F=new Te(new Ue(J(new K,[""," `","`"])));C=[We(Xe(),I),We(Xe(),C)];F=Ye(F,J(new K,C));C=t().d;F=G(new H,F,C);D=D.m();D=new Ao(D);D=new Ef(D,new y(M=>{if(null!==M){var N=M.h();M=Pe(new E(M.Sc()),0)?Ye(new Te(new Ue(J(new K,["It first appears here."]))),u()):Ye(new Te(new Ue(J(new K,["And here."]))),u());t();return G(new H,M,new L(N))}throw new w(M);}));Od();D=Pd(u(),D);return new z(F,D)}}throw new w(C);}));Od();m=Pd(u(), m);l=new z(l,m);throw hT(new jT,new z(r,l));}}throw new w(x);}m=new y(C=>{j5(a,C,(t(),new L(b)),d,e,g,h)});l.b()||m.n(l.o());cH(r,new y(C=>{j5(a,C.xt(),(t(),new L(b)),d,e,g,h)}))}else throw new w(b);}}),new y(()=>"[checkExhaustive] "+b.jb()))}; function Afa(a,b,c){var d=()=>{tp();var k=b.ni.m();k=k5(a,b,Aq(0,new Ef(k,new y(r=>r.Sj))),c);var l=tF(),m=b.ni.ha(),n=ap();return mF(l,m,n,k)},e=new y(()=>"[constructTerm]");if(a.Hb){if(a.F){var g=ut(Q(),"| ",a.r)+"[constructTerm]";ff(gf(),g+"\n")}a.r=1+a.r|0;try{var h=d()}finally{a.r=-1+a.r|0}dx(new E(e),a.qa)&&a.F&&(d=""+ut(Q(),"| ",a.r)+e.n(h),ff(gf(),d+"\n"))}else h=d();return h} function Bfa(a,b,c,d,e,g){var h=()=>{var n=O().c;if(null===n?null===b:n.i(b))return IE();if(b instanceof z){var r=b.z;n=b.p;if(null!==r){var v=new L(r);if(!v.b()){r=v.k.h();v=v.k.j();var x=gF(),A=cF(x,r,v);r=()=>{for(var D=eF(gF(),A);!D.b();){var F=D.e();a.Hb&&a.F&&(F=""+ut(Q(),"| ",a.r)+F,ff(gf(),F+"\n"));D=D.f()}};v=a.qa;if(a.Hb){a.F&&(x=ut(Q(),"| ",a.r)+"*** Initial tree ***",ff(gf(),x+"\n"));a.r=1+a.r|0;try{var B=r()}finally{a.r=-1+a.r|0}dx(new E(v),a.qa)&&a.F&&(B=""+ut(Q(),"| ",a.r)+v.n(B),ff(gf(), B+"\n"))}else r();for(;!n.b();){B=n.e();A.dm(B,c,d,e,g);a.Hb&&a.F&&(B=ut(Q(),"| ",a.r)+("*** Merging `"+B.h()+" \x3d\x3e "+B.j())+"` ***",ff(gf(),B+"\n"));B=(D=>()=>{for(var F=eF(gF(),D);!F.b();){var I=F.e();a.Hb&&a.F&&(I=""+ut(Q(),"| ",a.r)+I,ff(gf(),I+"\n"));F=F.f()}})(A);r=a.qa;if(a.Hb){a.F&&(v=ut(Q(),"| ",a.r)+"*** Updated tree ***",ff(gf(),v+"\n"));a.r=1+a.r|0;try{var C=B()}finally{a.r=-1+a.r|0}dx(new E(r),a.qa)&&a.F&&(B=""+ut(Q(),"| ",a.r)+r.n(C),ff(gf(),B+"\n"))}else B();n=n.f()}return A}}}throw new w(b); },k=new y(()=>"[buildCaseTree]");if(a.Hb){if(a.F){var l=ut(Q(),"| ",a.r)+"[buildCaseTree]";ff(gf(),l+"\n")}a.r=1+a.r|0;try{var m=h()}finally{a.r=-1+a.r|0}dx(new E(k),a.qa)&&a.F&&(h=""+ut(Q(),"| ",a.r)+k.n(m),ff(gf(),h+"\n"))}else m=h();return m} function Cfa(a,b){var c=()=>{var h=b.tb.m();h=new iy(h,new y(k=>!k.j().GE.b()),!1);h=new Ef(h,new y(k=>{if(null!==k){var l=k.h();k=k.j().Bx.m();k=new Ef(k,new y(m=>m.V));Od();k=Pd(u(),k);return G(new H,l,k)}throw new w(k);}));h=pp(tp().JK,h);return Sca(xE(),h)},d=new y(()=>"[getClassHierarchy]");if(a.Hb){if(a.F){var e=ut(Q(),"| ",a.r)+"[getClassHierarchy]";ff(gf(),e+"\n")}a.r=1+a.r|0;try{var g=c()}finally{a.r=-1+a.r|0}dx(new E(d),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+d.n(g),ff(gf(),a+"\n"))}else g=c(); return g}function h5(a,b,c,d,e,g,h){var k=b.Ra.m();k=new Ef(k,new y(v=>v.j().ya));var l=b.Ra.K(),m=1>l;if(m)var n=0;else{var r=l>>31;n=-1+l|0;r=-1!==n?r:-1+r|0;n=1+n|0;r=0===n?1+r|0:r;n=(0===r?-1<(-2147483648^n):0n&&$Q(bR(),1,l,1,!0);n=xe().Eb();for(l=new hE(1,1,l,m);l.fw;)m="_"+c4(l),n.$(m);l=n.Kb();Od();l=c5(a,c,k,Pd(u(),l),d,e);if(null===l)throw new w(l);k=l.h();l=l.j();ufa(a,c,b.Ra.K(),iu(ju(),b.A()),d,h);b=new WE(c,b.Ra.K(),l,g5());a=d5(a,c,k,d,g,h,e);return new z(b,a)} function l5(a,b,c,d,e,g){for(var h=null,k=null;b!==u();){var l=b.e(),m=!1,n=null;a:{if(l instanceof Pl){m=!0;n=l;var r=n.Za,v=n.Qb;if(r instanceof Pl){var x=r;r=x.Za;x=x.Qb;if(r instanceof vl&&"is"===r.x&&x instanceof Gl&&(r=x.Ra,r instanceof z&&(x=r,r=x.z,x=x.p,null!==r&&(r=new L(r),!r.b()&&(r=r.k.j(),null!==r))))){r=r.ya;var A=O().c;if((null===A?null===x:A.i(x))&&v instanceof Gl&&(v=v.Ra,v instanceof z&&(x=v.z,v=v.p,null!==x&&(x=new L(x),!x.b()&&(x=x.k.j(),null!==x&&(x=x.ya,A=O().c,(null===A?null=== v:A.i(v))&&!a.$c)))))){n=n.A();n=e5(a,r,n,c);l=O().c;n=f5(a,n,x,!0,c,d,e,g,l);break a}}}}if(m&&(m=n.Za,v=n.Qb,m instanceof vl&&"is"===m.x&&null!==v&&(m=mz(eu(),v),!m.b()&&null!==m.o()&&0===m.o().ab(2)))){l=m.o();l=eB(l,0);m=m.o();m=eB(m,1);n=n.A();n=e5(a,l,n,c);l=O().c;n=f5(a,n,m,!0,c,d,e,g,l);break a}n=new XE(l,g5());O();n=new CQ(n)}for(n=n.m();n.s();)l=new z(n.t(),u()),null===k?h=l:k.p=l,k=l;b=b.f()}return null===h?u():h} var Dfa=function m5(a,b,c,d,e,g,h,k,l,m,n){var v=()=>{var C=!1,D=null,F=!1,I=null;a:{if(c instanceof fe){C=!0;D=c;var M=D.aa;if(M instanceof gu){F=G(new H,e,M.ws);h.$(F);break a}}if(C){var N=D.aa;if(N instanceof Ut&&(M=N.Km,N=N.Lm,M instanceof vl&&"_"===M.x)){F=G(new H,e,N);h.$(F);break a}}if(C&&(M=D.aa,M instanceof Ut)){I=M.Km;F=M.Lm;C=rF(tF(),I,a.$c);if(null===C)throw new w(C);I=C.j();C=d.oo(C.h(),a.$c).jr;D=O().c;C=f5(a,b,C,!0,k,l,m,n,D);C=sY(e,uY(new NE(C,O().c),g));a.Hb&&a.F&&(D=ut(Q(),"| ", a.r)+"Result: "+ze(C.Wi,"",", ",""),ff(gf(),D+"\n"));if(t().d===I)F=G(new H,C,F),h.$(F);else if(I instanceof L)i5(a,new Ut(I.k,F),b0(),C,g,h,k,l,m,n);else throw new w(I);break a}if(C&&(M=D.aa,M instanceof Tt)){N=M.Wn;var P=M.Xn;M=M.Yn;if(null!==P&&"and"===P.x){I=rF(tF(),N,a.$c);if(null===I)throw new w(I);F=I.h();I=I.j();C=O().c;F=f5(a,b,F,!0,k,l,m,n,C);I.b()?I=O().c:(I=I.o(),I=qF(tF(),I),O(),I=l5(a,I,k,l,m,n));F=sY(e,uY(new NE(dl(I,F),O().c),g));i5(a,M,b0(),F,g,h,k,l,m,n);break a}}if(C&&(M=D.aa,M instanceof Tt)){C=M.Wn;I=M.Xn;F=M.Yn;C=rF(tF(),C,a.$c);b:{if(null!==C&&(D=C.h(),M=C.j(),M instanceof L)){I=M.k;C=O().c;C=f5(a,b,D,!0,k,l,m,n,C);I=qF(tF(),I);O();I=l5(a,I,k,l,m,n);I=sY(e,uY(new NE(dl(I,C),O().c),g));i5(a,F,b0(),I,g,h,k,l,m,n);break b}if(null!==C&&(D=C.h(),M=C.j(),t().d===M)){F=(t(),new fe(F));I=d0(d.oo(D,a.$c),I);m5(a,b,F,I,e,g,h,k,l,m,n);break b}throw new w(C);}break a}if(C&&(M=D.aa,M instanceof mu)){I=M.xs;F=M.ys;I=rF(tF(),I,a.$c);b:{if(null!==I&&(C=I.h(),D=I.j(),t().d===D)){for(I=d.oo(C,a.$c);!F.b();){D= F.e();c:{if(null!==D&&(C=new L(D),!C.b())){D=C.k.h();C=C.k.j();m5(a,b,(t(),new fe(C)),d0(I,D),e,g,h,k,l,m,n);break c}throw new w(D);}F=F.f()}break b}if(null!==I&&(C=I.h(),D=I.j(),D instanceof L)){I=D.k;C=d.oo(C,a.$c).jr;D=O().c;C=f5(a,b,C,!0,k,l,m,n,D);I=qF(tF(),I);D=I.MJ();O();D=l5(a,D,k,l,m,n);for(C=sY(e,uY(new NE(dl(D,C),O().c),g));!F.b();){D=F.e();c:{if(null!==D&&(M=new L(D),!M.b())){D=M.k.j();M=I.Mc();N=O().c;i5(a,D,new a0(M,new z(M,N)),C,g,h,k,l,m,n);break c}throw new w(D);}F=F.f()}break b}throw new w(I); }break a}if(C&&(M=D.aa,M instanceof $t)){for(F=M.ap;!F.b();)I=F.e(),m5(a,b,I,d,e,g,h,k,l,m,n),F=F.f();break a}C&&D.aa instanceof Ss&&vF();if(c instanceof Ud&&(F=!0,I=c,M=I.fa,M instanceof Zn&&(D=M.wd,C=M.Rb,M=M.Yc,D instanceof L&&(D=!!D.k,M instanceof fe)))){F=M.aa;F=new bF(KE(),D,C,F);g.$(F);break a}if(F)throw F=I.fa,I=new Te(new Ue(J(new K,["Illegal interleaved statement ",""]))),C=[We(Xe(),F.u())],iT(new jT,Ye(I,J(new K,C)),F.A());throw new w(c);}},x=new y(()=>"[desugarMatchBranch]");if(a.Hb){if(a.F){var A= ut(Q(),"| ",a.r)+"[desugarMatchBranch]";ff(gf(),A+"\n")}a.r=1+a.r|0;try{var B=v()}finally{a.r=-1+a.r|0}dx(new E(x),a.qa)&&a.F&&(v=""+ut(Q(),"| ",a.r)+x.n(B),ff(gf(),v+"\n"))}else v()},i5=function n5(a,b,c,d,e,g,h,k,l,m){var r=()=>{var B=!1,C=null,D=!1,F=null;a:if(b instanceof mu)for(D=b.ys,F=c.oo(b.xs,a.$c);!D.b();){C=D.e();b:{if(null!==C&&(B=new L(C),!B.b())){n5(a,B.k.j(),d0(F,B.k.h()),d,e,g,h,k,l,m);break b}throw new w(C);}D=D.f()}else{if(b instanceof Ut){B=!0;C=b;var I=C.Km,M=C.Lm;if(I instanceof vl&&"_"===I.x){F=G(new H,d,M);g.$(F);break a}}if(B)F=C.Lm,D=c.oo(C.Km,a.$c),C=qF(tF(),D.jr),dl(D.aB,C),D=l5(a,C,h,k,l,m),D=uY(tY(d,D),e),F=G(new H,D,F),g.$(F);else{if(b instanceof Tt&&(D=!0,F=b,B=F.Wn,C=F.Xn,I=F.Yn,null!==C&&"is"===C.x&&I instanceof $t)){F=I.ap;D=c.oo(B,a.$c).jr;B=D.A();C=C.A();b:{if(B instanceof L&&(B=B.k,C instanceof L)){C=C.k;t();C=xs(B,C);C=new L(C);break b}C=t().d}D=e5(a,D,C,h);C=D.no;if(C instanceof L)C=d;else{if(t().d!==C)throw new w(C);C=d}for(B=e.ia();!F.b();)I=F.e(),Dfa(a, D,I,b0(),C,B,g,h,k,l,m),F=F.f();break a}if(D&&(B=F.Wn,I=F.Xn,C=F.Yn,null!==I&&"and"===I.x)){F=c.oo(B,a.$c).jr;D=O().c;F=new z(F,D);O();F=tY(d,l5(a,F,h,k,l,m));n5(a,C,b0(),F,e,g,h,k,l,m);break a}if(D)D=F.Yn,C=F.Xn,F=d0(c.oo(F.Wn,a.$c),C),n5(a,D,F,d,e,g,h,k,l,m);else if(b instanceof Ss&&vF(),b instanceof gu)F=b.ws,D=uY(d,e),F=G(new H,D,F),g.$(F);else if(b instanceof $t)for(F=b.ap;!F.b();){D=F.e();C=!1;b:if(D instanceof fe)n5(a,D.aa,c,d,e,g,h,k,l,m);else{if(D instanceof Ud&&(C=!0,B=D,B=B.fa,B instanceof Zn&&(M=B,I=M.wd,B=M.Rb,M=M.Yc,I instanceof L&&(I=!!I.k,M instanceof fe)))){D=M.aa;a.Hb&&a.F&&(C=ut(Q(),"| ",a.r)+"Found interleaved binding "+B.x,ff(gf(),C+"\n"));D=new bF(KE(),I,B,D);e.$(D);break b}if(C)throw vS(new wS,"unexpected statements at desugarIfBody");throw new w(D);}F=F.f()}else throw new w(b);}}},v=new y(()=>"[desugarIfBody]");if(a.Hb){if(a.F){var x=ut(Q(),"| ",a.r)+"[desugarIfBody]";ff(gf(),x+"\n")}a.r=1+a.r|0;try{var A=r()}finally{a.r=-1+a.r|0}dx(new E(v),a.qa)&&a.F&&(r=""+ut(Q(),"| ", a.r)+v.n(A),ff(gf(),r+"\n"))}else r()},p5=function o5(a,b,c,d,e,g){var k=!1,l=null;if(b instanceof z){k=!0;l=b;var m=l.z,n=l.p;if(m instanceof FE){var r=m.Tj;m=m.pl;if(null!==r&&(r=new L(r),!r.b())){b=r.k.h();l=r.k.j();a.Hb&&a.F&&(k=ut(Q(),"| ",a.r),r=l.m(),r=new Ef(r,new y(D=>D.h()+" -\x3e "+D.j())),k=k+("\u2022 Constructor pattern: "+b+"("+ze(r,"",", ",""))+")",ff(gf(),k+"\n"));k=l.m();k=new Ef(k,new y(D=>D.j()));m=k5(a,m,c.Ce(k),g);r=!1;var v=null;k=uC(g,g,b.x);a:{if(k instanceof L){r=!0;v=k;var x= v.k;if(x instanceof ix&&(x=x.kh,x instanceof Cx)){k=Cp(x.Yr());break a}}if(r&&(r=v.k,r instanceof fx)){k=Cp(r.Ab);break a}r=k instanceof L&&k.k instanceof VP?!0:k instanceof L&&k.k instanceof qx?!0:t().d===k?!0:!1;if(r)k=t().d;else throw new w(k);}a:{if(GE(d)instanceof vl&&k instanceof L&&(k=k.k,!l.b())){v=FV();r=new fp;for(x=l.m();x.s();){var A=x.t();b:{if(null!==A){var B=new L(A);if(!B.b()){var C=B.k.h();B=B.k.j();if(null!==B){A=B.x;B=v.U(C);B instanceof L?wp(r,G(new H,B.k,A)):v.bj(C,A);break b}}}throw new w(A); }}v=t().d;l=new sm(tm().Cg,new Gl(l.Ii(new y(D=>D.h())).Ja(new y(D=>{if(null!==D){var F=new L(D);if(!F.b()&&(F=F.k.j(),null!==F)){F=F.x;if("_"===F)return D=t().d,F=new sm(tm().Cg,new vl(Z4(a,g))),G(new H,D,F);D=t().d;F=new sm(tm().Cg,new vl(F));return G(new H,D,F)}}throw new w(D);})).ha()));l=G(new H,v,l);v=O().c;l=new Gl(new z(l,v));r=r.ha();for(r=Km(r);!r.b();)v=r.e(),m=new Rl(!1,new vl(v.j()),new vl(v.h()),m),r=r.f();l=new Ol(l,m);m=t().d;r=tm().Cg;k=new Ql(b,new vl(k.Cf.x));v=t().d;x=new sm(tm().Cg, GE(d));v=G(new H,v,x);x=O().c;k=new sm(r,new Pl(k,new Gl(new z(v,x))));m=G(new H,m,k);k=O().c;l=new Pl(l,new Gl(new z(m,k)));break a}l=l.Fk(new y(D=>Nm(new E(D.j().x),"_"))).ha();l=Efa(a,GE(d),l,m,d,g)}return new um(b,l,o5(a,n,c,d,e,g))}}}if(k&&(m=l.z,n=l.p,m instanceof EE))return b=m.ir,l=m.Jp,a.Hb&&a.F&&(m=ut(Q(),"| ",a.r)+"\u2022 Literal pattern: "+b,ff(gf(),m+"\n")),new um(b,k5(a,l,c,g),o5(a,n,c,d,e,g));d=O().c;if(null===d?null===b:d.i(b)){if(R()===e)return a.Hb&&a.F&&(c=ut(Q(),"| ",a.r)+"\u2022 No wildcard branch", ff(gf(),c+"\n")),zm();if(e instanceof L)return e=e.k,a.Hb&&a.F&&(d=ut(Q(),"| ",a.r)+"\u2022 Wildcard branch",ff(gf(),d+"\n")),new ym(k5(a,e,c,g));throw new w(e);}throw new w(b);},k5=function q5(a,b,c,d){var g=()=>{if(b instanceof HE){var m=b.ot,n=tF(),r=b.ni.ha();return mF(n,r,c,m)}if(b instanceof DE){m=b.mo;var v=b.mh,x=b.nj;a.Hb&&a.F&&(n=ut(Q(),"| ",a.r)+"\u2022 Owned let bindings",ff(gf(),n+"\n"));n=b.ni.m();n=new iy(n,new y(F=>Pe(new E(F.fr),KE())),!0);Od();n=Pd(u(),n);if(n.b()){if(a.Hb&&a.F){var A= ut(Q(),"| ",a.r)+" * \x3cNo bindings\x3e";ff(gf(),A+"\n")}}else for(A=n;!A.b();){var B=A.e();if(null!==B){var C=B.fr,D=B.Sj;B=B.gr;a.Hb&&a.F&&(C=ut(Q(),"| ",a.r)+(" * ("+C+") "+D+" \x3d ")+B,ff(gf(),C+"\n"))}else throw new w(B);A=A.f()}A=v.m();A=new Ef(A,new y(F=>F.xt()));A=kv(A,new U(()=>x));A=new xo(A,new y(F=>F.ni));A=new iy(A,new y(F=>Pe(new E(F.fr),KE())),!1);Od();A=Pd(u(),A);a.Hb&&a.F&&(C=ut(Q(),"| ",a.r)+"\u2022 Collect interleaved let bindings from case branches",ff(gf(),C+"\n"));if(A.b())a.Hb&& a.F&&(C=ut(Q(),"| ",a.r)+" * \x3cNo interleaved bindings\x3e",ff(gf(),C+"\n"));else for(C=A;!C.b();){B=C.e();if(null!==B)D=B.Sj,B=B.gr,a.Hb&&a.F&&(D=ut(Q(),"| ",a.r)+(" * "+D+" \x3d ")+B,ff(gf(),D+"\n"));else throw new w(B);C=C.f()}if(v.b()){if(R()===x)throw a.Hb&&a.F&&(n=ut(Q(),"| ",a.r)+"\u2022 The match has neither branches nor default case",ff(gf(),n+"\n")),n=new Ue(J(new K,["found an empty match"])),iT(new jT,Ye(new Te(n),u()),m.nh.A());if(!(x instanceof L))throw new w(x);r=x.k;a.Hb&&a.F&& (v=ut(Q(),"| ",a.r)+"\u2022 Degenerated case: the match only has a wildcard",ff(gf(),v+"\n"));r=q5(a,r,c,d);v=m.no;if(t().d!==v){if(!(v instanceof L))throw new w(v);r=new Rl(!1,v.k,m.nh,r)}}else{a.Hb&&a.F&&(C=ut(Q(),"| ",a.r)+"\u2022 The match has some case branches",ff(gf(),C+"\n"));C=new y(()=>"\u2022 End for each");if(a.Hb){a.F&&(D=ut(Q(),"| ",a.r)+"\u2022 For each case branch",ff(gf(),D+"\n"));a.r=1+a.r|0;try{r=p5(a,v.ha(),c,m,x,d)}finally{a.r=-1+a.r|0}dx(new E(C),a.qa)&&a.F&&(v=""+ut(Q(),"| ", a.r)+C.n(r),ff(gf(),v+"\n"))}else r=p5(a,v.ha(),c,m,x,d);v=m.no;if(t().d===v)r=new Ul(m.nh,r);else{if(!(v instanceof L))throw new w(v);v=v.k;r=new Rl(!1,v,m.nh,new Ul(v,r))}}m=tF();v=tF();r=mF(v,A,c,r);return mF(m,n,c,r)}if(IE()===b)throw m=new Ue(J(new K,["missing a default branch"])),iT(new jT,Ye(new Te(m),u()),t().d);if(b instanceof CE)return m=b.hr,n=b.Dk,A=b.Fh,r=tF(),v=A.ni.ha(),C=A.ni.m(),C=new Ef(C,new y(F=>F.Sj)),A=q5(a,A,c.Ce(C),d),r=mF(r,v,c,A),v=tF(),A=n.ni.ha(),C=n.ni.m(),C=new Ef(C, new y(F=>F.Sj)),n=q5(a,n,c.Ce(C),d),n=mF(v,A,c,n),r=new ym(r),n=new um(new vl("true"),n,r),new Ul(m,n);throw new w(b);},h=a.qa;if(a.Hb){if(a.F){var k=ut(Q(),"| ",a.r)+("[rec] "+b.jb()+" -| {"+ze(c,"",", ",""))+"}";ff(gf(),k+"\n")}a.r=1+a.r|0;try{var l=g()}finally{a.r=-1+a.r|0}dx(new E(h),a.qa)&&a.F&&(g=""+ut(Q(),"| ",a.r)+h.n(l),ff(gf(),g+"\n"))}else l=g();return l},Efa=function r5(a,b,c,d,e,g){var k=O().c;if(null===k?null===c:k.i(c))return d;if(c instanceof z){var l=c.z;k=c.p;if(null!==l&&(l=new L(l), !l.b())){var m=l.k.h();l=l.k.j();if(null!==l){c=l.x;var n=e.nh;if(n instanceof vl&&c===n.x)return b=new vl(Z4(a,g)),c=GE(e),m=new Ql(b,new vl(m)),new Rl(!1,b,c,new Rl(!1,l,OS(m,e.nh),r5(a,b,k,d,e,g)));m=new Ql(b,new vl(m));return new Rl(!1,l,OS(m,e.nh),r5(a,b,k,d,e,g))}}}throw new w(c);}; function s5(){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0;this.zk=this.Pq=this.Uq=this.xp=this.Bp=this.Cp=this.Sq=this.zp=this.Rq=this.wp=this.Ap=this.yp=this.Qq=this.Tq=null;this.Dp=0;this.hs=this.yq=this.Aq=this.Bq=this.zq=this.Dq=this.Cq=null;this.Im=this.Pw=0;this.iA=this.hA=this.Ku=null;this.Hb=!1;this.XA=0;this.YA=null}s5.prototype=new b3;s5.prototype.constructor=s5;function t5(){}t5.prototype=s5.prototype; function Ffa(a,b,c,d){var e=()=>{var l=Cfa(a,c);vE(xE(),l,new y(A=>{X4(a,A)}),"Super-class map","\x3c:");var m=Rca(xE(),l);vE(xE(),m,new y(A=>{X4(a,A)}),"Sub-class map",":\x3e");var n=xfa(a,b,O().c);if(null===n)throw new w(n);var r=n.h(),v=n.j();n=Xu().X();if(a.Hb&&a.F){var x=ut(Q(),"| ",a.r)+"### Desugar the UCS to decision paths ###";ff(gf(),x+"\n")}r=yfa(a,r,v,c,d,n);a.Hb&&a.F&&(v=ut(Q(),"| ",a.r)+"Exhaustiveness map",ff(gf(),v+"\n"));n.b()?a.Hb&&a.F&&(v=ut(Q(),"| ",a.r)+" * \x3cNo entries\x3e", ff(gf(),v+"\n")):n.og(new fn((A,B)=>{a.Hb&&a.F&&(A=ut(Q(),"| ",a.r)+" * Patterns of "+A,ff(gf(),A+"\n"));B.b()?a.Hb&&a.F&&(B=ut(Q(),"| ",a.r)+" + \x3cNo patterns\x3e",ff(gf(),B+"\n")):B.og(new fn((C,D)=>{if(C instanceof fe)C="()^"+(C.aa|0);else{if(!(C instanceof Ud))throw new w(C);C=Zz(C.fa,!1)}D=ze(D,"[",", ","]");a.Hb&&a.F&&(D=ut(Q(),"| ",a.r)+(" + "+C+" -\x3e ")+D,ff(gf(),D+"\n"))}))}));a.Hb&&a.F&&(v=ut(Q(),"| ",a.r)+"### Build a case tree from decision paths ###",ff(gf(),v+"\n"));tp(); n=n.m();n=pp(0,new Ef(n,new y(A=>{if(null!==A){var B=A.h();A=A.j();A=pp(tp().JK,A);return G(new H,B,A)}throw new w(A);})));l=Bfa(a,r,d,new y(A=>a5(a,A,c)),n,l);a.Hb&&a.F&&(r=ut(Q(),"| ",a.r)+"### Checking exhaustiveness of the case tree ###",ff(gf(),r+"\n"));zfa(a,l,t().d,c,d,n,m);a.Hb&&a.F&&(m=ut(Q(),"| ",a.r)+"### Construct a term from the case tree ###",ff(gf(),m+"\n"));m=Afa(a,l,c);a.F&&(l=ut(Q(),"| ",a.r)+"Desugared term: "+Zz(m,!1),ff(gf(),l+"\n"));b.oc=(t(),new L(m));return m},g=a.qa;if(a.Hb){if(a.F){var h= ut(Q(),"| ",a.r)+"[desugarIf]";ff(gf(),h+"\n")}a.r=1+a.r|0;try{var k=e()}finally{a.r=-1+a.r|0}dx(new E(g),a.qa)&&a.F&&(e=""+ut(Q(),"| ",a.r)+g.n(k),ff(gf(),e+"\n"))}else k=e();return k}function Gu(a,b){this.w=this.y=null;this.zF=a;this.oJ=b;G(this,null,null)}Gu.prototype=new h0;Gu.prototype.constructor=Gu;f=Gu.prototype;f.ut=function(){return this.zF};f.Sc=function(){return this.oJ};f.Cw=function(){return new Gu(this.oJ,this.zF)};f.iy=function(){return this.zF};f.j=function(){return this.oJ}; f.h=function(){return this.zF};f.$classData=q({G0:0},!1,"scala.Tuple2$mcII$sp",{G0:1,by:1,g:1,LB:1,E:1,v:1,l:1,gba:1});function GG(a){this.tn=null;this.Al=this.wc=0;this.A4=a;Du(this,a)}GG.prototype=new u3;GG.prototype.constructor=GG;GG.prototype.t=function(){try{var a=this.A4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=Rq().Pa.t()|0;else throw c;}return b}; GG.prototype.$classData=q({z4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcB$sp",{z4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function FG(a){this.tn=null;this.Al=this.wc=0;this.C4=a;Du(this,a)}FG.prototype=new u3;FG.prototype.constructor=FG;FG.prototype.t=function(){try{var a=this.C4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=Ea(Rq().Pa.t());else throw c;}return hc(b)}; FG.prototype.$classData=q({B4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcC$sp",{B4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function CG(a){this.tn=null;this.Al=this.wc=0;this.E4=a;Du(this,a)}CG.prototype=new u3;CG.prototype.constructor=CG;CG.prototype.t=function(){try{var a=this.E4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=+Rq().Pa.t();else throw c;}return b}; CG.prototype.$classData=q({D4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcD$sp",{D4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function EG(a){this.tn=null;this.Al=this.wc=0;this.G4=a;Du(this,a)}EG.prototype=new u3;EG.prototype.constructor=EG;EG.prototype.t=function(){try{var a=this.G4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=Math.fround(Rq().Pa.t());else throw c;}return b}; EG.prototype.$classData=q({F4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcF$sp",{F4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function BG(a){this.tn=null;this.Al=this.wc=0;this.I4=a;Du(this,a)}BG.prototype=new u3;BG.prototype.constructor=BG;BG.prototype.t=function(){try{var a=this.I4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=Rq().Pa.t()|0;else throw c;}return b}; BG.prototype.$classData=q({H4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcI$sp",{H4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function DG(a){this.tn=null;this.Al=this.wc=0;this.K4=a;Du(this,a)}DG.prototype=new u3;DG.prototype.constructor=DG;DG.prototype.t=function(){try{var a=this.K4.a[this.wc],b=a.W,c=a.Y;this.wc=1+this.wc|0;var d=new ma(b,c)}catch(e){if(e instanceof dI)d=Za(Rq().Pa.t());else throw e;}return d}; DG.prototype.$classData=q({J4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcJ$sp",{J4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function HG(a){this.tn=null;this.Al=this.wc=0;this.M4=a;Du(this,a)}HG.prototype=new u3;HG.prototype.constructor=HG;HG.prototype.t=function(){try{var a=this.M4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=Rq().Pa.t()|0;else throw c;}return b}; HG.prototype.$classData=q({L4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcS$sp",{L4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function JG(a){this.tn=null;this.Al=this.wc=0;Du(this,a)}JG.prototype=new u3;JG.prototype.constructor=JG;JG.prototype.t=function(){try{this.wc=1+this.wc|0}catch(a){if(a instanceof dI)Rq().Pa.t();else throw a;}};JG.prototype.$classData=q({N4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcV$sp",{N4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1}); function IG(a){this.tn=null;this.Al=this.wc=0;this.P4=a;Du(this,a)}IG.prototype=new u3;IG.prototype.constructor=IG;IG.prototype.t=function(){try{var a=this.P4.a[this.wc];this.wc=1+this.wc|0;var b=a}catch(c){if(c instanceof dI)b=!!Rq().Pa.t();else throw c;}return b};IG.prototype.$classData=q({O4:0},!1,"scala.collection.ArrayOps$ArrayIterator$mcZ$sp",{O4:1,Qp:1,Sa:1,g:1,Ka:1,M:1,N:1,l:1});function CQ(a){this.WK=a}CQ.prototype=new q3;CQ.prototype.constructor=CQ;f=CQ.prototype;f.m=function(){Rq();return new g0(this.WK)}; f.Q=function(){return 1};f.e=function(){return this.WK};f.Mc=function(){return this.WK};f.Bb=function(a){return 0c||c>=e)throw aL(new bL,c+" is out of bounds (min 0, max "+(-1+e|0)+")");e=(a.Ke-a.Zd|0)&(-1+a.ec.a.length|0)|0;var g=wG(xG(),b)-c|0;e=e=e)throw aL(new bL,"0 is out of bounds (min 0, max "+(-1+e|0)+")");e=(a.Zd+0|0)&(-1+a.ec.a.length|0);g=a.ec.a.length-e|0;g=d=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)}; f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0c)a.PF=""+a.PF+b,b="";else{var d=""+a.PF+b.substring(0,c);"undefined"!==typeof console&&(a.f1&&console.error?console.error(d):console.log(d));a.PF="";b=b.substring(1+c|0)}}}Zg.prototype.$classData=q({e1:0},!1,"java.lang.JSConsoleBasedPrintStream",{e1:1,uaa:1,saa:1,taa:1,g:1,yT:1,R0:1,zT:1,rQ:1});function Vw(a,b,c,d,e){this.Iu=null;this.ij=b;this.ig=c;this.bo=d;this.Tz=e;if(null===a)throw null;this.Iu=a} Vw.prototype=new p;Vw.prototype.constructor=Vw;f=Vw.prototype;f.Mp=function(){return this.bo};f.Ea=function(){return this.Tz};f.Ua=function(){return this.ij.Ua()};f.fd=function(){return this.ij instanceof Ep?Ap():hx()};f.A=function(){return this.ij.A()};f.tr=function(){return!0};f.Nn=function(){return this.ig.ra};f.H=function(){return"NuParam"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.ij;case 1:return this.ig;case 2:return this.bo;default:return $K(W(),a)}}; f.D=function(a){return a instanceof Vw};f.B=function(){var a=lb("NuParam");a=W().C(-889275714,a);var b=this.ij;b=My(W(),b);a=W().C(a,b);b=this.ig;b=My(W(),b);a=W().C(a,b);b=this.bo?1231:1237;a=W().C(a,b);return W().Ma(a,3)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Vw&&a.Iu===this.Iu){if(this.bo===a.bo){var b=this.ij,c=a.ij;b=null===b?null===c:b.i(c)}else b=!1;if(b)return b=this.ig,a=a.ig,null===b?null===a:b.i(a)}return!1};f.bG=function(){return this.Iu}; f.cm=function(a,b){var c=this.Iu,d=this.ij,e=this.ig,g=e.Va,h=e.Oa;h.b()?h=R():(h=h.o(),h=new L(b.ba(new UB(a),h)));return new Vw(c,d,new Uw(g,h,b.ba(a,e.ra),e.Pd),this.bo,this.Tz)};f.yl=function(a,b,c){b=this.Iu;var d=this.ij,e=this.ig,g=e.Va,h=e.Oa;if(h.b())h=R();else{h=h.o();if(a.b())var k=R();else k=!!a.o(),k=new L(!k);h=new L(c.ba(k,h))}return new Vw(b,d,new Uw(g,h,c.ba(a,e.ra),e.Pd),this.bo,this.Tz)};f.ro=function(a,b,c,d){return new Vw(this.Iu,this.ij,LX(this.ig,a,b,c,d),this.bo,c.da)}; f.$classData=q({wX:0},!1,"mlscript.NuTypeDefs$NuParam",{wX:1,g:1,nx:1,xE:1,Rs:1,TN:1,E:1,v:1,l:1});function Hfa(a){if(!a.SH){var b=a.Ei,c=a.gh,d=k=>{if(null!==k){var l=k.kc,m=k.hb;if(null!==l){k=a.Rf.gb.V+"#"+l.V;var n=a.nc,r=a.nc;t();l=new Vw(n,l,new Uw(r,new L(m),m,V(a.nc)),!0,a.Sm);return G(new H,k,l)}}throw new w(k);};if(c===u())d=u();else{var e=c.e(),g=e=new z(d(e),u());for(c=c.f();c!==u();){var h=c.e();h=new z(d(h),u());g=g.p=h;c=c.f()}d=e}a.TH=b.bf(d).bf(a.Tm);a.SH=!0}return a.TH} function Yw(a,b,c,d,e,g,h,k,l,m,n){this.RH=this.TH=this.nc=this.px=null;this.SH=!1;this.Sm=b;this.Rf=c;this.gh=d;this.Ij=e;this.gl=g;this.Ei=h;this.Um=k;this.sk=l;this.ip=m;this.Tm=n;MS(this,a,Bp());this.RH=t().d}Yw.prototype=new NS;Yw.prototype.constructor=Yw;f=Yw.prototype;f.Ea=function(){return this.Sm};f.Yr=function(){return this.Rf};f.Ag=function(){return this.gh};f.aG=function(){return this.Ei};f.fd=function(){return this.Rf.pb};f.cG=function(){return this.Rf.gb};f.Ua=function(){return this.Rf.gb.V}; f.tr=function(){return!0};f.Mp=function(){return!0};f.LC=function(){return this.SH?this.TH:Hfa(this)}; function Ay(a,b){var c=a.RH;if(c instanceof L)return c.k;if(t().d===c){c=a.nc;var d=new y(r=>"\x3d "+r);if(c.F){var e=ut(Q(),"| ",c.r)+"Computing variances of "+a.Rf.gb.V;ff(gf(),e+"\n")}c.r=1+c.r|0;try{var g=new $e;ofa(a.nc);var h=Xu().X(),k=jA().X();a.Ei.og(new fn((r,v)=>{if(!(v instanceof Vw&&v.ij instanceof Ep)){r=ID;if(g.sb)var x=g.vb;else{if(null===g)throw le();x=g.sb?g.vb:me(g,new LS(a,b,k,h))}r(x,gA(a.nc).Pj,v)}}));var l=a.gh.m(),m=h.bf(new Ex(l,new L_(a)));a.RH=(t(),new L(m));var n=m}finally{c.r= -1+c.r|0}dx(new E(d),c.qa)&&c.F&&(l=""+ut(Q(),"| ",c.r)+d.n(n),ff(gf(),l+"\n"));return n}throw new w(c);}function RA(a,b,c){return Ay(a,c).Se(b,new U(()=>ou().Yl))} function Zw(a,b,c,d,e){var g=new Iw(d.S,d.Ec,d.hc,d.Ed,1+d.da|0,d.Pc,d.Zc,d.Lb,d.yc,d.tb,d.$a,d.od,d.cb),h=a.nc;d=d.da;var k=a.Rf,l=a.gh,m=B=>{var C=B.kc,D=B.hb.Kc(b,c,g,e);return new tl(C,hD(D),B.Rd)};if(l===u())m=u();else{var n=l.e(),r=n=new z(m(n),u());for(l=l.f();l!==u();){var v=l.e();v=new z(m(v),u());r=r.p=v;l=l.f()}m=n}n=a.Ij;n.b()?n=R():(n=n.o(),n=new L(ry(lv(),n,new y(B=>LX(B,b,c,g,e)))));r=a.gl;r.b()?r=R():(r=r.o(),r=new L(ry(lv(),r,new y(B=>B.Kc(b,c,g,e)))));l=KF(lv(),a.Ei,new y(B=>B.ro(b, c,g,e)));op();l=pp(qp(),l);v=a.Um.Kc(b,c,g,e);var x=a.sk.Kc(b,c,g,e),A=a.ip;a=KF(lv(),a.Tm,new y(B=>B.ro(b,c,g,e)));op();return new Yw(h,d,k,m,n,r,l,v,x,A,pp(qp(),a))} function Ifa(a,b,c,d,e){var g=a.nc,h=a.Sm,k=a.Rf,l=a.gh,m=B=>{var C=B.kc,D=d.ba(t().d,B.hb);return new tl(C,hD(D),B.Rd)};if(l===u())m=u();else{var n=l.e(),r=n=new z(m(n),u());for(l=l.f();l!==u();){var v=l.e();v=new z(m(v),u());r=r.p=v;l=l.f()}m=n}n=a.Ij;n.b()?n=R():(n=n.o(),n=new L(ry(lv(),n,new y(B=>{var C=B.Va,D=B.Oa;if(D.b())D=R();else{D=D.o();if(b.b())var F=R();else F=!!b.o(),F=new L(!F);D=new L(d.ba(F,D))}return new Uw(C,D,d.ba(b,B.ra),B.Pd)}))));r=a.gl;r.b()?r=R():(r=r.o(),r=new L(ry(lv(),r, new y(B=>{if(b.b())var C=R();else C=!!b.o(),C=new L(!C);return d.ba(C,B)}))));l=KF(lv(),a.Ei,new y(B=>B.yl(b,c,d,e)));op();l=pp(qp(),l);b.b()?v=R():(v=!!b.o(),v=new L(!v));v=d.ba(v,a.Um);var x=d.ba(b,a.sk),A=a.ip;a=KF(lv(),a.Tm,new y(B=>B.yl(b,c,d,e)));op();return new Yw(g,h,k,m,n,r,l,v,x,A,pp(qp(),a))} function Jfa(a,b,c,d){var e=a.nc,g=a.Sm,h=a.Rf,k=a.gh,l=A=>{var B=A.kc,C=c.ba(new TB(b),A.hb);return new tl(B,hD(C),A.Rd)};if(k===u())l=u();else{var m=k.e(),n=m=new z(l(m),u());for(k=k.f();k!==u();){var r=k.e();r=new z(l(r),u());n=n.p=r;k=k.f()}l=m}m=a.Ij;m.b()?m=R():(m=m.o(),m=new L(ry(lv(),m,new y(A=>{var B=A.Va,C=A.Oa;C.b()?C=R():(C=C.o(),C=new L(c.ba(new UB(b),C)));return new Uw(B,C,c.ba(b,A.ra),A.Pd)}))));n=a.gl;n.b()?n=R():(n=n.o(),n=new L(ry(lv(),n,new y(A=>c.ba(new UB(b),A)))));k=KF(lv(), a.Ei,new y(A=>A.cm(b,c,d)));op();k=pp(qp(),k);r=c.ba(new UB(b),a.Um);var v=c.ba(b,a.sk),x=a.ip;a=KF(lv(),a.Tm,new y(A=>A.cm(b,c,d)));op();return new Yw(e,g,h,l,m,n,k,r,v,x,pp(qp(),a))}f.u=function(){var a=this.Ei;return"TypedNuCls("+this.Sm+", "+this.Rf.gb+",\n\t"+this.gh+",\n\t"+this.Ij+",\n\tthis: "+this.Um+", "+CF(GF(),a)+",\n\t: "+this.sk+", "+this.ip+", "+this.Tm+")"};f.H=function(){return"TypedNuCls"};f.G=function(){return 10}; f.I=function(a){switch(a){case 0:return this.Sm;case 1:return this.Rf;case 2:return this.gh;case 3:return this.Ij;case 4:return this.gl;case 5:return this.Ei;case 6:return this.Um;case 7:return this.sk;case 8:return this.ip;case 9:return this.Tm;default:return $K(W(),a)}};f.D=function(a){return a instanceof Yw}; f.B=function(){var a=lb("TypedNuCls");a=W().C(-889275714,a);var b=this.Sm;a=W().C(a,b);b=this.Rf;b=My(W(),b);a=W().C(a,b);b=this.gh;b=My(W(),b);a=W().C(a,b);b=this.Ij;b=My(W(),b);a=W().C(a,b);b=this.gl;b=My(W(),b);a=W().C(a,b);b=this.Ei;b=My(W(),b);a=W().C(a,b);b=this.Um;b=My(W(),b);a=W().C(a,b);b=this.sk;b=My(W(),b);a=W().C(a,b);b=this.ip;b=My(W(),b);a=W().C(a,b);b=this.Tm;b=My(W(),b);a=W().C(a,b);return W().Ma(a,10)}; f.i=function(a){if(this===a)return!0;if(a instanceof Yw&&a.nc===this.nc){if(this.Sm===a.Sm){var b=this.Rf,c=a.Rf;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.gh,c=a.gh,(null===b?null===c:b.i(c))?(b=this.Ij,c=a.Ij,(null===b?null===c:b.i(c))?(b=this.gl,c=a.gl,b=null===b?null===c:b.i(c)):b=!1):b=!1):b=!1;if(b&&(b=this.Ei,c=a.Ei,(null===b?null===c:b.i(c))?(b=this.Um,c=a.Um,(null===b?null===c:mC(b,c))?(b=this.sk,c=a.sk,b=null===b?null===c:mC(b,c)):b=!1):b=!1,b&&(b=this.ip,c=a.ip,null===b?null===c:b.i(c))))return b= this.Tm,a=a.Tm,null===b?null===a:b.i(a)}return!1};f.cm=function(a,b,c){return Jfa(this,a,b,c)};f.yl=function(a,b,c,d){return Ifa(this,a,b,c,d)};f.ro=function(a,b,c,d){return Zw(this,a,b,c,d)};f.$classData=q({AX:0},!1,"mlscript.NuTypeDefs$TypedNuCls",{AX:1,YH:1,g:1,Rs:1,nx:1,HN:1,E:1,v:1,l:1});function cx(a,b){this.ox=null;this.wE=b;if(null===a)throw null;this.ox=a}cx.prototype=new p;cx.prototype.constructor=cx;f=cx.prototype;f.Ea=function(){return this.ox.Gd};f.fd=function(){return hx()};f.A=function(){return t().d}; f.Ua=function(){return this.wE.Cf.x};f.tr=function(){return!0};f.Mp=function(){return!0};f.Nn=function(){return Ey(this.ox)};f.H=function(){return"TypedNuDummy"};f.G=function(){return 1};f.I=function(a){return 0===a?this.wE:$K(W(),a)};f.D=function(a){return a instanceof cx};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof cx&&a.ox===this.ox){var b=this.wE;a=a.wE;return null===b?null===a:b.i(a)}return!1};f.bG=function(){return this.ox}; f.cm=function(){return this};f.yl=function(){return this};f.ro=function(){return this};f.$classData=q({DX:0},!1,"mlscript.NuTypeDefs$TypedNuDummy",{DX:1,g:1,Rs:1,nx:1,xE:1,TN:1,E:1,v:1,l:1});function bx(a,b,c,d,e){this.MN=this.LN=null;this.Ss=0;this.Sl=null;this.jp=b;this.Mb=c;this.hh=d;this.Ts=e;if(null===a)throw null;this.Sl=a}bx.prototype=new p;bx.prototype.constructor=bx;f=bx.prototype;f.Ea=function(){return this.jp};f.tr=function(){return this.Ts};f.fd=function(){return hx()};f.Ua=function(){return this.Mb.Rb.x}; f.A=function(){return this.Mb.A()};f.ma=function(){0===(1&this.Ss)<<24>>24&&0===(1&this.Ss)<<24>>24&&(this.LN=jx(new kx,this.Sl,this.Mb.A(),"member",(tx(this.Sl),t().d),(tx(this.Sl),!1)),this.Ss=(1|this.Ss)<<24>>24);return this.LN};f.Mp=function(){return!rx(this.Mb)};f.Nn=function(){0===(2&this.Ss)<<24>>24&&0===(2&this.Ss)<<24>>24&&(this.MN=this.Mb.Om.b()?yx(zx(this.Sl),this.jp,this.hh):this.hh,this.Ss=(2|this.Ss)<<24>>24);return this.MN}; function Iy(a){var b=a.Mb.Yc;b instanceof fe?a=Vy(a.Sl,b.aa):(a=a.Sl,null===a.Ku&&null===a.Ku&&(a.Ku=new WO(a)),a=a.Ku,a.OH||a.OH||(a.JN=new XX(a.IN,t().d,ap()),a.OH=!0),a=a.JN);return a}f.H=function(){return"TypedNuFun"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.jp;case 1:return this.Mb;case 2:return this.hh;default:return $K(W(),a)}};f.D=function(a){return a instanceof bx}; f.B=function(){var a=lb("TypedNuFun");a=W().C(-889275714,a);var b=this.jp;a=W().C(a,b);b=this.Mb;b=My(W(),b);a=W().C(a,b);b=this.hh;b=My(W(),b);a=W().C(a,b);return W().Ma(a,3)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof bx&&a.Sl===this.Sl){if(this.jp===a.jp){var b=this.Mb,c=a.Mb;b=null===b?null===c:b.i(c)}else b=!1;if(b)return b=this.hh,a=a.hh,null===b?null===a:mC(b,a)}return!1};f.bG=function(){return this.Sl}; f.cm=function(a,b){return new bx(this.Sl,this.jp,this.Mb,b.ba(a,this.hh),this.Ts)};f.yl=function(a,b,c){return new bx(this.Sl,this.jp,this.Mb,c.ba(a,this.hh),this.Ts)};f.ro=function(a,b,c,d){var e=new Iw(c.S,c.Ec,c.hc,c.Ed,1+c.da|0,c.Pc,c.Zc,c.Lb,c.yc,c.tb,c.$a,c.od,c.cb);return new bx(this.Sl,c.da,this.Mb,this.hh.Kc(a,b,e,d),this.Ts)};f.$classData=q({EX:0},!1,"mlscript.NuTypeDefs$TypedNuFun",{EX:1,g:1,Rs:1,nx:1,xE:1,TN:1,E:1,v:1,l:1}); function Kfa(a){if(!a.UH){var b=a.Jj,c=a.kl,d=k=>{if(null!==k){var l=k.kc,m=k.hb;if(null!==l){k=a.il.gb.V+"#"+l.V;var n=a.nc,r=a.nc;t();l=new Vw(n,l,new Uw(r,new L(m),m,V(a.nc)),!1,a.Fq);return G(new H,k,l)}}throw new w(k);};if(c===u())d=u();else{var e=c.e(),g=e=new z(d(e),u());for(c=c.f();c!==u();){var h=c.e();h=new z(d(h),u());g=g.p=h;c=c.f()}d=e}a.VH=b.bf(d);a.UH=!0}return a.VH} function Nw(a,b,c,d,e,g,h,k){this.VH=this.nc=this.px=null;this.UH=!1;this.Fq=b;this.il=c;this.jl=d;this.hl=e;this.kl=g;this.Tl=h;this.Jj=k;MS(this,a,cp())}Nw.prototype=new NS;Nw.prototype.constructor=Nw;f=Nw.prototype;f.Ea=function(){return this.Fq};f.Yr=function(){return this.il};f.Ag=function(){return this.kl};f.aG=function(){return this.Jj};f.fd=function(){return this.il.pb};f.cG=function(){return this.il.gb};f.Ua=function(){return this.il.gb.V};f.tr=function(){return!0};f.Mp=function(){return!0}; f.LC=function(){return this.UH?this.VH:Kfa(this)}; function Qw(a,b,c,d,e){var g=new Iw(d.S,d.Ec,d.hc,d.Ed,1+d.da|0,d.Pc,d.Zc,d.Lb,d.yc,d.tb,d.$a,d.od,d.cb),h=a.nc;d=d.da;var k=a.il,l=a.jl.Kc(b,c,g,e),m=a.hl.Kc(b,c,g,e),n=a.kl,r=B=>{var C=B.kc,D=B.hb.Kc(b,c,g,e);return new tl(C,hD(D),B.Rd)};if(n===u())r=u();else{var v=n.e(),x=v=new z(r(v),u());for(n=n.f();n!==u();){var A=n.e();A=new z(r(A),u());x=x.p=A;n=n.f()}r=v}v=ry(lv(),a.Tl,new y(B=>LX(B,b,c,g,e)));a=KF(lv(),a.Jj,new y(B=>B.ro(b,c,g,e)));op();return new Nw(h,d,k,l,m,r,v,pp(qp(),a))} function Lfa(a,b,c,d,e){var g=a.nc,h=a.Fq,k=a.il;if(b.b())var l=R();else l=!!b.o(),l=new L(!l);l=d.ba(l,a.jl);if(b.b())var m=R();else m=!!b.o(),m=new L(!m);m=d.ba(m,a.hl);var n=a.kl,r=B=>{var C=B.kc,D=d.ba(t().d,B.hb);return new tl(C,hD(D),B.Rd)};if(n===u())r=u();else{var v=n.e(),x=v=new z(r(v),u());for(n=n.f();n!==u();){var A=n.e();A=new z(r(A),u());x=x.p=A;n=n.f()}r=v}v=ry(lv(),a.Tl,new y(B=>{var C=B.Va,D=B.Oa;if(D.b())D=R();else{D=D.o();if(b.b())var F=R();else F=!!b.o(),F=new L(!F);D=new L(d.ba(F, D))}return new Uw(C,D,d.ba(b,B.ra),B.Pd)}));a=KF(lv(),a.Jj,new y(B=>B.yl(b,c,d,e)));op();return new Nw(g,h,k,l,m,r,v,pp(qp(),a))} function Mfa(a,b,c,d){var e=a.nc,g=a.Fq,h=a.il,k=c.ba(new UB(b),a.jl),l=c.ba(new UB(b),a.hl),m=a.kl,n=A=>{var B=A.kc,C=c.ba(new TB(b),A.hb);return new tl(B,hD(C),A.Rd)};if(m===u())n=u();else{var r=m.e(),v=r=new z(n(r),u());for(m=m.f();m!==u();){var x=m.e();x=new z(n(x),u());v=v.p=x;m=m.f()}n=r}r=ry(lv(),a.Tl,new y(A=>{var B=A.Va,C=A.Oa;C.b()?C=R():(C=C.o(),C=new L(c.ba(new UB(b),C)));return new Uw(B,C,c.ba(b,A.ra),A.Pd)}));a=KF(lv(),a.Jj,new y(A=>A.cm(b,c,d)));op();return new Nw(e,g,h,k,l,n,r,pp(qp(), a))}f.u=function(){var a=this.Jj;return"TypedNuMxn("+this.Fq+", "+this.il.gb+",\n\tthis: "+this.jl+",\n\tsuper: "+this.hl+",\n\ttparams: "+this.kl+",\n\tparams: "+this.Tl+",\n\tmembers: "+CF(GF(),a)+"\n)"};f.H=function(){return"TypedNuMxn"};f.G=function(){return 7};f.I=function(a){switch(a){case 0:return this.Fq;case 1:return this.il;case 2:return this.jl;case 3:return this.hl;case 4:return this.kl;case 5:return this.Tl;case 6:return this.Jj;default:return $K(W(),a)}}; f.D=function(a){return a instanceof Nw};f.B=function(){var a=lb("TypedNuMxn");a=W().C(-889275714,a);var b=this.Fq;a=W().C(a,b);b=this.il;b=My(W(),b);a=W().C(a,b);b=this.jl;b=My(W(),b);a=W().C(a,b);b=this.hl;b=My(W(),b);a=W().C(a,b);b=this.kl;b=My(W(),b);a=W().C(a,b);b=this.Tl;b=My(W(),b);a=W().C(a,b);b=this.Jj;b=My(W(),b);a=W().C(a,b);return W().Ma(a,7)}; f.i=function(a){if(this===a)return!0;if(a instanceof Nw&&a.nc===this.nc){if(this.Fq===a.Fq){var b=this.il,c=a.il;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.jl,c=a.jl,(null===b?null===c:mC(b,c))?(b=this.hl,c=a.hl,b=null===b?null===c:mC(b,c)):b=!1):b=!1;if(b&&(b=this.kl,c=a.kl,(null===b?null===c:b.i(c))?(b=this.Tl,c=a.Tl,b=null===b?null===c:b.i(c)):b=!1,b))return b=this.Jj,a=a.Jj,null===b?null===a:b.i(a)}return!1};f.cm=function(a,b,c){return Mfa(this,a,b,c)}; f.yl=function(a,b,c,d){return Lfa(this,a,b,c,d)};f.ro=function(a,b,c,d){return Qw(this,a,b,c,d)};f.$classData=q({FX:0},!1,"mlscript.NuTypeDefs$TypedNuMxn",{FX:1,YH:1,g:1,Rs:1,nx:1,HN:1,E:1,v:1,l:1}); function Nfa(a){if(!a.WH){var b=a.tk,c=a.wk,d=k=>{if(null!==k){var l=k.kc,m=k.hb;if(null!==l){k=a.vk.gb.V+"#"+l.V;var n=a.nc,r=a.nc;t();l=new Vw(n,l,new Uw(r,new L(m),m,V(a.nc)),!0,a.Hq);return G(new H,k,l)}}throw new w(k);};if(c===u())d=u();else{var e=c.e(),g=e=new z(d(e),u());for(c=c.f();c!==u();){var h=c.e();h=new z(d(h),u());g=g.p=h;c=c.f()}d=e}a.XH=b.bf(d).bf(a.Vm);a.WH=!0}return a.XH} function Ww(a,b,c,d,e,g,h,k,l){this.XH=this.nc=this.px=null;this.WH=!1;this.Hq=b;this.vk=c;this.wk=d;this.tk=e;this.co=g;this.uk=h;this.Gq=k;this.Vm=l;MS(this,a,Fp())}Ww.prototype=new NS;Ww.prototype.constructor=Ww;f=Ww.prototype;f.Ea=function(){return this.Hq};f.Yr=function(){return this.vk};f.Ag=function(){return this.wk};f.aG=function(){return this.tk};f.fd=function(){return this.vk.pb};f.cG=function(){return this.vk.gb};f.Ua=function(){return this.vk.gb.V};f.tr=function(){return!0};f.Mp=function(){return!0}; f.LC=function(){return this.WH?this.XH:Nfa(this)}; function Xw(a,b,c,d,e){var g=new Iw(d.S,d.Ec,d.hc,d.Ed,1+d.da|0,d.Pc,d.Zc,d.Lb,d.yc,d.tb,d.$a,d.od,d.cb),h=a.nc;d=d.da;var k=a.vk,l=a.wk,m=x=>{var A=x.kc,B=x.hb.Kc(b,c,g,e);return new tl(A,hD(B),x.Rd)};if(l===u())m=u();else{var n=l.e(),r=n=new z(m(n),u());for(l=l.f();l!==u();){var v=l.e();v=new z(m(v),u());r=r.p=v;l=l.f()}m=n}n=KF(lv(),a.tk,new y(x=>x.ro(b,c,g,e)));op();n=pp(qp(),n);r=a.co.Kc(b,c,g,e);l=a.uk.Kc(b,c,g,e);v=a.Gq;a=KF(lv(),a.Vm,new y(x=>x.ro(b,c,g,e)));op();return new Ww(h,d,k,m,n,r, l,v,pp(qp(),a))}function Ofa(a,b,c,d,e){var g=a.nc,h=a.Hq,k=a.vk,l=a.wk,m=x=>{var A=x.kc,B=d.ba(t().d,x.hb);return new tl(A,hD(B),x.Rd)};if(l===u())m=u();else{var n=l.e(),r=n=new z(m(n),u());for(l=l.f();l!==u();){var v=l.e();v=new z(m(v),u());r=r.p=v;l=l.f()}m=n}n=KF(lv(),a.tk,new y(x=>x.yl(b,c,d,e)));op();n=pp(qp(),n);b.b()?r=R():(r=!!b.o(),r=new L(!r));r=d.ba(r,a.co);l=d.ba(b,a.uk);v=a.Gq;a=KF(lv(),a.Vm,new y(x=>x.yl(b,c,d,e)));op();return new Ww(g,h,k,m,n,r,l,v,pp(qp(),a))} function Pfa(a,b,c,d){var e=a.nc,g=a.Hq,h=a.vk,k=a.wk,l=v=>{var x=v.kc,A=c.ba(new TB(b),v.hb);return new tl(x,hD(A),v.Rd)};if(k===u())l=u();else{var m=k.e(),n=m=new z(l(m),u());for(k=k.f();k!==u();){var r=k.e();r=new z(l(r),u());n=n.p=r;k=k.f()}l=m}m=KF(lv(),a.tk,new y(v=>v.cm(b,c,d)));op();m=pp(qp(),m);n=c.ba(new UB(b),a.co);k=c.ba(b,a.uk);r=a.Gq;a=KF(lv(),a.Vm,new y(v=>v.cm(b,c,d)));op();return new Ww(e,g,h,l,m,n,k,r,pp(qp(),a))}f.H=function(){return"TypedNuTrt"};f.G=function(){return 8}; f.I=function(a){switch(a){case 0:return this.Hq;case 1:return this.vk;case 2:return this.wk;case 3:return this.tk;case 4:return this.co;case 5:return this.uk;case 6:return this.Gq;case 7:return this.Vm;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ww}; f.B=function(){var a=lb("TypedNuTrt");a=W().C(-889275714,a);var b=this.Hq;a=W().C(a,b);b=this.vk;b=My(W(),b);a=W().C(a,b);b=this.wk;b=My(W(),b);a=W().C(a,b);b=this.tk;b=My(W(),b);a=W().C(a,b);b=this.co;b=My(W(),b);a=W().C(a,b);b=this.uk;b=My(W(),b);a=W().C(a,b);b=this.Gq;b=My(W(),b);a=W().C(a,b);b=this.Vm;b=My(W(),b);a=W().C(a,b);return W().Ma(a,8)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Ww&&a.nc===this.nc){if(this.Hq===a.Hq){var b=this.vk,c=a.vk;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.wk,c=a.wk,(null===b?null===c:b.i(c))?(b=this.tk,c=a.tk,b=null===b?null===c:b.i(c)):b=!1):b=!1;if(b&&(b=this.co,c=a.co,(null===b?null===c:mC(b,c))?(b=this.uk,c=a.uk,b=null===b?null===c:mC(b,c)):b=!1,b&&(b=this.Gq,c=a.Gq,null===b?null===c:b.i(c))))return b=this.Vm,a=a.Vm,null===b?null===a:b.i(a)}return!1}; f.cm=function(a,b,c){return Pfa(this,a,b,c)};f.yl=function(a,b,c,d){return Ofa(this,a,b,c,d)};f.ro=function(a,b,c,d){return Xw(this,a,b,c,d)};f.$classData=q({GX:0},!1,"mlscript.NuTypeDefs$TypedNuTrt",{GX:1,YH:1,g:1,Rs:1,nx:1,HN:1,E:1,v:1,l:1});function BP(a,b){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.yx=a;this.aA=b;Nq(this)}BP.prototype=new ZS;BP.prototype.constructor=BP;f=BP.prototype;f.Vj=function(){return this.yx};f.H=function(){return"Signature"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.yx;case 1:return this.aA;default:return $K(W(),a)}};f.D=function(a){return a instanceof BP};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof BP){var b=this.yx,c=a.yx;if(null===b?null===c:b.i(c))return b=this.aA,a=a.aA,null===b?null===a:b.i(a)}return!1};f.$classData=q({hY:0},!1,"mlscript.Signature",{hY:1,ih:1,g:1,jh:1,Ta:1,Haa:1,E:1,v:1,l:1}); function W5(a){Nq(a);a.oc=t().d;a.nd=t().d}function Pm(){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0}Pm.prototype=new p;Pm.prototype.constructor=Pm;function X5(){}f=X5.prototype=Pm.prototype;f.jb=function(){return hu(this)};f.Wr=function(){return Zz(this,!1)};f.Vj=function(){return eP(this)};f.nv=function(){0===(1&this.gc)<<24>>24&&0===(1&this.gc)<<24>>24&&(this.yd=cP(this),this.gc=(1|this.gc)<<24>>24);return this.yd}; f.jn=function(){0===(2&this.gc)<<24>>24&&0===(2&this.gc)<<24>>24&&(this.zd=zq(this),this.gc=(2|this.gc)<<24>>24);return this.zd};f.rn=function(){return this.Cd};f.fm=function(a){this.Cd=a};f.qn=function(){return this.Bd};f.em=function(a){this.Bd=a};f.pn=function(){return this.Ad};f.on=function(a){this.Ad=a};f.A=function(){0===(4&this.gc)<<24>>24&&0===(4&this.gc)<<24>>24&&(this.Dd=Dq(this),this.gc=(4|this.gc)<<24>>24);return this.Dd};function Y5(){this.ld="trait"}Y5.prototype=new XS; Y5.prototype.constructor=Y5;f=Y5.prototype;f.H=function(){return"Trt"};f.G=function(){return 0};f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof Y5};f.B=function(){return 84374};f.u=function(){return"Trt"};f.$classData=q({qY:0},!1,"mlscript.Trt$",{qY:1,EE:1,sz:1,sx:1,g:1,ZH:1,E:1,v:1,l:1});var Z5;function Fp(){Z5||(Z5=new Y5);return Z5}function Vv(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}Vv.prototype=new W4;Vv.prototype.constructor=Vv; function $5(){}$5.prototype=Vv.prototype;function LA(a,b,c,d,e){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.tc=b;this.ic=c;this.jc=d;this.KE=e;pY(this,a)}LA.prototype=new qY;LA.prototype.constructor=LA;f=LA.prototype;f.ma=function(){return this.KE};f.Ea=function(){var a=this.ic.Ea(),b=this.jc.Ea();return a>b?a:b};f.ub=function(a,b){var c=this.ic.ub(a,b);a=this.jc.ub(a,b);return c>a?c:a};f.u=function(){return"("+this.ic+" "+(this.tc?"|":"\x26")+" "+this.jc+")"}; f.H=function(){return"ComposedType"};f.G=function(){return 3};f.I=function(a){switch(a){case 0:return this.tc;case 1:return this.ic;case 2:return this.jc;default:return $K(W(),a)}};f.D=function(a){return a instanceof LA};f.$classData=q({aZ:0},!1,"mlscript.TyperDatatypes$ComposedType",{aZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function Qfa(a){if(!a.uI){var b=ED(a,!1).m();b=new Ef(b,new y(d=>d.Ea()));var c=Fq();a.vI=hH(b,c)|0;a.uI=!0}return a.vI} function eC(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.vI=this.dd=0;this.hO=null;this.uI=!1;this.Lj=b;this.kj=c;pY(this,a);this.hO=c.ma()}eC.prototype=new qY;eC.prototype.constructor=eC;f=eC.prototype;f.ma=function(){return this.hO};f.Ea=function(){return this.uI?this.vI:Qfa(this)};f.ub=function(a,b){var c=ED(this,!1).m();c=new Ef(c,new y(e=>e.ub(a,b)));var d=Fq();return hH(c,d)|0}; f.u=function(){var a=this.kj,b=this.Lj,c=h=>{if(null!==h)return h.h()+" \x3c: "+h.j();throw new w(h);};if(b===u())c=u();else{var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}c=d}return"{"+a+" where: "+ze(c,"",", ","")+"}"};f.H=function(){return"ConstrainedType"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Lj;case 1:return this.kj;default:return $K(W(),a)}};f.D=function(a){return a instanceof eC}; f.$classData=q({bZ:0},!1,"mlscript.TyperDatatypes$ConstrainedType",{bZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function FA(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.Eh=b;this.hZ=c;pY(this,a)}FA.prototype=new qY;FA.prototype.constructor=FA;f=FA.prototype;f.ma=function(){return this.hZ};f.Ea=function(){return this.q.Gd};f.ub=function(){return this.q.Gd};f.u=function(){return this.Eh?"\u22a5":"\u22a4"};f.H=function(){return"ExtrType"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.Eh:$K(W(),a)};f.D=function(a){return a instanceof FA};f.$classData=q({gZ:0},!1,"mlscript.TyperDatatypes$ExtrType",{gZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function MA(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.Fc=b;this.rA=c;pY(this,a)}MA.prototype=new qY;MA.prototype.constructor=MA;f=MA.prototype;f.ma=function(){return this.rA};f.Ea=function(){return this.Fc.Ea()};f.ub=function(a,b){return this.Fc.ub(a,b)}; f.u=function(){return"~("+this.Fc+")"};f.H=function(){return"NegType"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Fc:$K(W(),a)};f.D=function(a){return a instanceof MA};f.$classData=q({pZ:0},!1,"mlscript.TyperDatatypes$NegType",{pZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function Qv(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.OO=this.dd=0;this.FI=!1;this.Ba=b;this.Nj=c;pY(this,a)}Qv.prototype=new qY;Qv.prototype.constructor=Qv;f=Qv.prototype;f.ma=function(){return this.Nj}; f.Ea=function(){this.FI||this.FI||(this.OO=this.ub(this.q.Df,jA().X()),this.FI=!0);return this.OO};f.ub=function(a,b){var c=this.Ba.m();c=new Ef(c,new y(e=>e.j().ub(a,b)));var d=Fq();c=Jq(c,d);return(c.b()?this.q.Gd:c.o())|0};function ov(a,b,c,d,e){return XC(a.q,a,t().d,new fn((g,h)=>h.Kc(b,c,d,e)))} function Gea(a){var b=a.Ba,c=h=>{var k=a.q,l=O().c;return new Qv(k,new z(h,l),a.Nj)};if(b===u())c=u();else{var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}c=d}for(d=a.q.La;!c.b();)e=c.e(),d=new LA(a.q,!1,d,e,V(a.q)),c=c.f();return d}function Lu(a){var b=a.q,c=a.Ba,d=new y(h=>h.h()),e=Su(),g=op().ga;return new Qv(b,QY(c,d,new Uu(e,g)),a.Nj)} f.u=function(){var a=this.Ba;if(a===u())var b=u();else{b=a.e();var c=b=new z(b.h().x+": "+b.j(),u());for(a=a.f();a!==u();){var d=a.e();d=new z(d.h().x+": "+d.j(),u());c=c.p=d;a=a.f()}}return"{"+ze(b,"",", ","")+"}"};f.H=function(){return"RecordType"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Ba:$K(W(),a)};f.D=function(a){return a instanceof Qv};f.Kc=function(a,b,c,d){return ov(this,a,b,c,d)}; f.$classData=q({xZ:0},!1,"mlscript.TyperDatatypes$RecordType",{xZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function cC(a,b,c,d){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.Fg=b;this.Sf=c;this.TO=d;pY(this,a)}cC.prototype=new qY;cC.prototype.constructor=cC;f=cC.prototype;f.ma=function(){return this.TO};f.Ea=function(){var a=this.Fg.Ea(),b=this.Sf.Ea();return a>b?a:b};f.ub=function(a,b){var c=this.Fg.ub(a,b);a=this.Sf.ub(a,b);return c>a?c:a}; f.u=function(){return this.Fg+".."+this.Sf};f.H=function(){return"TypeBounds"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Fg;case 1:return this.Sf;default:return $K(W(),a)}};f.D=function(a){return a instanceof cC};f.$classData=q({FZ:0},!1,"mlscript.TyperDatatypes$TypeBounds",{FZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1}); function Hn(a,b,c,d,e,g,h,k,l){this.HP=null;this.FP=!1;this.GP=this.IP=this.EP=null;this.Hp=a;this.$I=b;this.cF=c;this.eF=d;this.hF=e;this.dF=g;this.gF=h;this.fF=k;this.CA=l;cE(this);this.HP=O().c;this.FP=!0;this.EP=t().d;this.IP=t().d}Hn.prototype=new p;Hn.prototype.constructor=Hn;f=Hn.prototype;f.wK=function(){return this.GP};f.xK=function(a){this.GP=a};f.Ua=function(){return this.Hp};f.mv=function(){return this.cF};f.EB=function(){return this.eF};f.ZL=function(){return this.hF};f.HF=function(){return this.dF}; f.vy=function(){return this.gF};f.FB=function(){return this.fF};f.CK=function(){return this.CA};f.u=function(){return"mixin "+this.Hp};f.xo=function(){return this.Hp};f.zo=function(){return this.Hp};f.pH=function(){return this.HP};f.UJ=function(){return this.FP};f.GF=function(){return this.EP};f.iM=function(){return this.IP};f.H=function(){return"MixinSymbol"};f.G=function(){return 9}; f.I=function(a){switch(a){case 0:return this.Hp;case 1:return this.$I;case 2:return this.cF;case 3:return this.eF;case 4:return this.hF;case 5:return this.dF;case 6:return this.gF;case 7:return this.fF;case 8:return this.CA;default:return $K(W(),a)}};f.D=function(a){return a instanceof Hn};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Hn){if(this.Hp===a.Hp){var b=this.$I,c=a.$I;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.cF,c=a.cF,(null===b?null===c:b.i(c))?(b=this.eF,c=a.eF,b=null===b?null===c:b.i(c)):b=!1):b=!1;if(b&&(b=this.hF,c=a.hF,(null===b?null===c:b.i(c))?(b=this.dF,c=a.dF,b=null===b?null===c:b.i(c)):b=!1,b&&(b=this.gF,c=a.gF,null===b?null===c:b.i(c)))&&(b=this.fF,c=a.fF,null===b?null===c:b.i(c)))return b=this.CA,a=a.CA,null===b?null===a:b.i(a)}return!1}; f.$classData=q({r_:0},!1,"mlscript.codegen.MixinSymbol",{r_:1,g:1,UA:1,cr:1,mt:1,pF:1,E:1,v:1,l:1});function ln(a,b,c,d,e,g,h,k,l){this.KP=!1;this.LP=this.NP=this.MP=this.JP=null;this.lj=a;this.aJ=b;this.iF=c;this.kF=d;this.mF=e;this.jF=g;this.nF=h;this.lF=k;this.DA=l;cE(this);this.KP=!1;this.JP=t().d;this.MP=O().c;this.NP=t().d}ln.prototype=new p;ln.prototype.constructor=ln;f=ln.prototype;f.wK=function(){return this.LP};f.xK=function(a){this.LP=a};f.Ua=function(){return this.lj};f.mv=function(){return this.iF}; f.EB=function(){return this.kF};f.ZL=function(){return this.mF};f.HF=function(){return this.jF};f.pH=function(){return this.nF};f.FB=function(){return this.lF};f.CK=function(){return this.DA};f.u=function(){return"module "+this.lj};f.xo=function(){return this.lj};f.zo=function(){return this.lj};f.UJ=function(){return this.KP};f.GF=function(){return this.JP};f.vy=function(){return this.MP};f.iM=function(){return this.NP};f.H=function(){return"ModuleSymbol"};f.G=function(){return 9}; f.I=function(a){switch(a){case 0:return this.lj;case 1:return this.aJ;case 2:return this.iF;case 3:return this.kF;case 4:return this.mF;case 5:return this.jF;case 6:return this.nF;case 7:return this.lF;case 8:return this.DA;default:return $K(W(),a)}};f.D=function(a){return a instanceof ln};f.B=function(){return AL(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof ln){if(this.lj===a.lj){var b=this.aJ,c=a.aJ;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.iF,c=a.iF,(null===b?null===c:b.i(c))?(b=this.kF,c=a.kF,b=null===b?null===c:b.i(c)):b=!1):b=!1;if(b&&(b=this.mF,c=a.mF,(null===b?null===c:b.i(c))?(b=this.jF,c=a.jF,b=null===b?null===c:b.i(c)):b=!1,b&&(b=this.nF,c=a.nF,null===b?null===c:b.i(c)))&&(b=this.lF,c=a.lF,null===b?null===c:b.i(c)))return b=this.DA,a=a.DA,null===b?null===a:b.i(a)}return!1}; f.$classData=q({t_:0},!1,"mlscript.codegen.ModuleSymbol",{t_:1,g:1,UA:1,cr:1,mt:1,pF:1,E:1,v:1,l:1});function jn(a,b,c,d,e,g,h,k,l,m,n,r,v){this.PP=null;this.mi=a;this.oF=b;this.IA=c;this.GA=d;this.JA=e;this.OA=g;this.MA=h;this.HA=k;this.NA=l;this.LA=m;this.KA=n;this.Qx=r;this.$u=v;cE(this)}jn.prototype=new p;jn.prototype.constructor=jn;f=jn.prototype;f.wK=function(){return this.PP};f.xK=function(a){this.PP=a};f.Ua=function(){return this.mi};f.GF=function(){return this.IA};f.mv=function(){return this.GA}; f.EB=function(){return this.JA};f.iM=function(){return this.OA};f.ZL=function(){return this.MA};f.HF=function(){return this.HA};f.pH=function(){return this.NA};f.vy=function(){return this.LA};f.FB=function(){return this.KA};f.CK=function(){return this.Qx};f.UJ=function(){return this.$u};f.u=function(){return"new class "+this.mi};f.xo=function(){return this.mi};f.zo=function(){return this.mi};f.H=function(){return"NewClassSymbol"};f.G=function(){return 13}; f.I=function(a){switch(a){case 0:return this.mi;case 1:return this.oF;case 2:return this.IA;case 3:return this.GA;case 4:return this.JA;case 5:return this.OA;case 6:return this.MA;case 7:return this.HA;case 8:return this.NA;case 9:return this.LA;case 10:return this.KA;case 11:return this.Qx;case 12:return this.$u;default:return $K(W(),a)}};f.D=function(a){return a instanceof jn}; f.B=function(){var a=lb("NewClassSymbol");a=W().C(-889275714,a);var b=this.mi;b=My(W(),b);a=W().C(a,b);b=this.oF;b=My(W(),b);a=W().C(a,b);b=this.IA;b=My(W(),b);a=W().C(a,b);b=this.GA;b=My(W(),b);a=W().C(a,b);b=this.JA;b=My(W(),b);a=W().C(a,b);b=this.OA;b=My(W(),b);a=W().C(a,b);b=this.MA;b=My(W(),b);a=W().C(a,b);b=this.HA;b=My(W(),b);a=W().C(a,b);b=this.NA;b=My(W(),b);a=W().C(a,b);b=this.LA;b=My(W(),b);a=W().C(a,b);b=this.KA;b=My(W(),b);a=W().C(a,b);b=this.Qx;b=My(W(),b);a=W().C(a,b);b=this.$u?1231: 1237;a=W().C(a,b);return W().Ma(a,13)}; f.i=function(a){if(this===a)return!0;if(a instanceof jn){if(this.$u===a.$u)if(this.mi===a.mi){var b=this.oF,c=a.oF;b=null===b?null===c:b.i(c)}else b=!1;else b=!1;b?(b=this.IA,c=a.IA,(null===b?null===c:b.i(c))?(b=this.GA,c=a.GA,(null===b?null===c:b.i(c))?(b=this.JA,c=a.JA,b=null===b?null===c:b.i(c)):b=!1):b=!1):b=!1;if(b&&(b=this.OA,c=a.OA,(null===b?null===c:b.i(c))?(b=this.MA,c=a.MA,(null===b?null===c:b.i(c))?(b=this.HA,c=a.HA,b=null===b?null===c:b.i(c)):b=!1):b=!1,b&&(b=this.NA,c=a.NA,(null===b? null===c:b.i(c))?(b=this.LA,c=a.LA,b=null===b?null===c:b.i(c)):b=!1,b&&(b=this.KA,c=a.KA,null===b?null===c:b.i(c)))))return b=this.Qx,a=a.Qx,null===b?null===a:b.i(a)}return!1};f.$classData=q({v_:0},!1,"mlscript.codegen.NewClassSymbol",{v_:1,g:1,UA:1,cr:1,mt:1,pF:1,E:1,v:1,l:1});function a6(a,b){for(;;){if(0>=a||b.b())return b;a=-1+a|0;b=b.f()}}function jba(a,b){for(;;)if(!a.b()&&b.n(a.e()))a=a.f();else break;return a} function b6(a,b){var c=a.vj().Eb();for(a=a.m();a.s();){var d=b.n(a.t());c.$(d)}return c.Kb()}function c6(a,b){var c=a.vj().Eb();c.zc(a);c.zc(b);return c.Kb()}function ky(a,b){if(0>=a.ab(1))return a;for(var c=a.ti(),d=Vn(),e=a.m(),g=!1;e.s();){var h=e.t();d.oh(b.n(h))?c.$(h):g=!0}return g?c.Kb():a}function tr(a,b){this.hG=0;this.gd=a;if(null===a)throw Kj("null value for BigDecimal");if(null===b)throw Kj("null MathContext for BigDecimal");this.hG=1565550863}tr.prototype=new cW; tr.prototype.constructor=tr;f=tr.prototype;f.sl=function(a){return uW(this.gd,a.gd)}; f.B=function(){if(1565550863===this.hG){if(this.uv()&&4934>(pr(this.gd)-this.gd.xb|0))var a=d6(new KR,vW(this.gd)).B();else{a=this.gd.Lp();if(Infinity!==a&&-Infinity!==a){var b=lr();a=e6(this,BR(a,b.xy))}else a=!1;if(a)a=this.gd.Lp(),a=ZK(W(),a);else{a=sW(this.gd);b=BL();var c=b.Pp,d;var e=d=a.xb,g=e>>31,h=d>>31;d=e-d|0;g=(-2147483648^d)>(-2147483648^e)?-1+(g-h|0)|0:g-h|0;64>a.$h?(e=a.Bg,0===e.W&&0===e.Y?(e=hN(),d=new ma(d,g),g=d.W,d=d.W===g&&d.Y===g>>31?jN(e,aa,d.W):0<=d.Y?eN(0,2147483647):eN(0, -2147483648)):d=jN(hN(),a.Bg,mN(hN(),new ma(d,g)))):d=FR(new mr,$M(a),mN(hN(),new ma(d,g)));a=c.call(b,vW(d).B(),a.xb)}}this.hG=a}return this.hG}; f.i=function(a){if(a instanceof tr)return e6(this,a);if(a instanceof KR){var b=f6(a),c=pr(this.gd);if(b>3.3219280948873626*(-2+(c-this.gd.xb|0)|0)){if(this.uv())try{var d=new L(d6(new KR,pW(this.gd)))}catch(e){if(e instanceof qb)d=R();else throw e;}else d=R();if(d.b())return!1;b=d.o();return g6(a,b)}return!1}return"number"===typeof a?(b=+a,Infinity!==b&&-Infinity!==b&&(a=this.gd.Lp(),Infinity!==a&&-Infinity!==a&&a===b)?(b=lr(),e6(this,BR(a,b.xy))):!1):ja(a)?(b=Math.fround(a),Infinity!==b&&-Infinity!== b&&(a=this.gd.pv(),Infinity!==a&&-Infinity!==a&&a===b)?(b=lr(),e6(this,BR(a,b.xy))):!1):this.pB()&&AK(this,a)};f.WJ=function(){try{return oW(this.gd,8),!0}catch(a){if(a instanceof qb)return!1;throw a;}};f.YJ=function(){try{return oW(this.gd,16),!0}catch(a){if(a instanceof qb)return!1;throw a;}};f.XJ=function(){return this.MF()&&0<=oW(this.gd,32).W&&65535>=oW(this.gd,32).W};f.MF=function(){try{return oW(this.gd,32),!0}catch(a){if(a instanceof qb)return!1;throw a;}}; f.pB=function(){try{return oW(this.gd,64),!0}catch(a){if(a instanceof qb)return!1;throw a;}};f.uv=function(){return 0>=this.gd.xb?!0:0>=sW(this.gd).xb};function e6(a,b){return 0===uW(a.gd,b.gd)}f.jB=function(){return this.gd.Zi()<<24>>24};f.EC=function(){return this.gd.Zi()<<16>>16};f.Zi=function(){return this.gd.Zi()};f.xl=function(){return this.gd.xl()};f.pv=function(){return this.gd.pv()};f.Lp=function(){return this.gd.Lp()};f.u=function(){return this.gd.u()}; f.$l=function(a){return uW(this.gd,a.gd)};f.qT=function(){return this.gd};f.$classData=q({u3:0},!1,"scala.math.BigDecimal",{u3:1,R3:1,ur:1,g:1,l:1,S3:1,sR:1,yj:1,nf:1});function h6(a){a=a.Uf;return!(0===a.W&&-2147483648===a.Y)}function i6(a){a=Ni(j6(a),2147483647);return 0!==a.Ya&&!a.i(NK().qR)}function JR(a,b,c){a.jm=b;a.Uf=c;return a}function d6(a,b){JR(a,b,63>=yh(Sh(),b)?b.xl():new ma(0,-2147483648));return a}function KR(){this.jm=null;this.Uf=aa}KR.prototype=new cW;KR.prototype.constructor=KR; f=KR.prototype;f.sl=function(a){return k6(this,a)};function j6(a){var b=a.jm;if(null!==b)return b;var c=a.Uf;b=c.W;c=c.Y;b=ri(Ph(),new ma(b,c));return a.jm=b}f.B=function(){if(this.pB()){var a=this.xl(),b=a.W;a=a.Y;return(-1===a?0<=(-2147483648^b):-1=(-2147483648^b):0>a)?b:YK(W(),new ma(b,a))}b=j6(this);return My(W(),b)}; f.i=function(a){if(a instanceof KR)return g6(this,a);if(a instanceof tr)return a.i(this);if("number"===typeof a){a=+a;var b=f6(this);if(53>=b)b=!0;else{var c=l6(this);b=1024>=b&&c>=(-53+b|0)&&1024>c}return(b?!i6(this):!1)&&this.Lp()===a}return ja(a)?(a=Math.fround(a),b=f6(this),24>=b?b=!0:(c=l6(this),b=128>=b&&c>=(-24+b|0)&&128>c),b&&!i6(this)?(b=j6(this),hM(jM(),Vh(bi(),b))===a):!1):this.pB()&&AK(this,a)}; f.WJ=function(){var a=this.Uf,b=a.Y;return(-1===b?2147483520<=(-2147483648^a.W):-1=(-2147483648^a.W):0>b):!1};f.YJ=function(){var a=this.Uf,b=a.Y;return(-1===b?2147450880<=(-2147483648^a.W):-1=(-2147483648^a.W):0>b):!1};f.XJ=function(){if(0<=this.Uf.Y){var a=this.Uf,b=a.Y;return 0===b?-2147418113>=(-2147483648^a.W):0>b}return!1}; f.MF=function(){var a=this.Uf,b=a.Y;return(-1===b?0<=(-2147483648^a.W):-1=(-2147483648^a.W):0>b):!1};f.pB=function(){return h6(this)||QK(nl(),this.jm,NK().LK)};f.uv=function(){return!0};function g6(a,b){return h6(a)?h6(b)?(a=a.Uf,b=b.Uf,a.W===b.W&&a.Y===b.Y):!1:!h6(b)&&QK(nl(),a.jm,b.jm)}function k6(a,b){if(h6(a)){if(h6(b)){var c=a.Uf;a=c.W;c=c.Y;var d=b.Uf;b=d.W;d=d.Y;return ua(xa(),a,c,b,d)}return-b.jm.Ya|0}return h6(b)?a.jm.Ya:fM(a.jm,b.jm)} function lba(a){if(h6(a)){var b=NK(),c=a.Uf;a=c.W;c=c.Y;return OR(b,new ma(-a|0,0!==a?~c:-c|0))}return PR(NK(),ui(j6(a)))}function l6(a){if(h6(a)){var b=a.Uf;if(0===b.W&&0===b.Y)return-1;b=a.Uf;a=b.W;b=b.Y;return 0!==a?0===a?32:31-Math.clz32(a&(-a|0))|0:32+(0===b?32:31-Math.clz32(b&(-b|0))|0)|0}return wW(j6(a))} function f6(a){if(h6(a)){if(0>a.Uf.Y){a=a.Uf;var b=a.Y;a=1+a.W|0;var c=0===a?1+b|0:b;b=-a|0;a=0!==a?~c:-c|0;return 64-(0!==a?Math.clz32(a):32+Math.clz32(b)|0)|0}b=a.Uf;a=b.W;b=b.Y;return 64-(0!==b?Math.clz32(b):32+Math.clz32(a)|0)|0}a=a.jm;return yh(Sh(),a)}f.jB=function(){return this.Zi()<<24>>24};f.EC=function(){return this.Zi()<<16>>16};f.Zi=function(){return h6(this)?this.Uf.W:j6(this).Zi()};f.xl=function(){return h6(this)?this.Uf:this.jm.xl()}; f.pv=function(){var a=j6(this);return hM(jM(),Vh(bi(),a))};f.Lp=function(){if(this.pB())if(-2097152<=this.Uf.Y){var a=this.Uf,b=a.Y;a=2097152===b?0===a.W:2097152>b}else a=!1;else a=!1;if(a)return a=this.Uf,cG(xa(),a.W,a.Y);a=j6(this);return aM(qa(),Vh(bi(),a))};f.u=function(){if(h6(this)){var a=this.Uf;return bG(xa(),a.W,a.Y)}a=this.jm;return Vh(bi(),a)};f.$l=function(a){return k6(this,a)};f.qT=function(){return j6(this)}; var NR=q({w3:0},!1,"scala.math.BigInt",{w3:1,R3:1,ur:1,g:1,l:1,S3:1,sR:1,yj:1,nf:1});KR.prototype.$classData=NR;function m6(){this.NB=null;n6=this;this.NB=new B2(this)}m6.prototype=new p;m6.prototype.constructor=m6;f=m6.prototype;f.sj=function(a){return a===this.NB};f.uj=function(a,b){return 0>=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0Lw(a,We(Xe(),"type identifier not found: "+c),b,g)));O();e=e.o();return new Ud(e)} function Rfa(a,b,c,d,e,g,h){var k=tD(b,c);k instanceof fw&&xm("Program reached and unexpected state.");if(k instanceof Mu&&(k=k.pd,k instanceof vl)){h=!1;b=null;k=Mea(a,k.x,d.Zr(),c,e);if(k instanceof Ud){h=!0;b=k;var l=b.fa;if(l instanceof Yw)return l.gh.b()||no(),e=l.Rf,d=g.A(),tx(a),g=t().d,bz(a,e,jx(new kx,a,d,"type selection",g,!0),c)}if(h&&(c=b.fa,c instanceof ax))return c.rk.b()||no(),c.Ql;if(h)return g=b.fa,c=new Te(new Ue(J(new K,["Illegal selection of "," member in type position"]))),g= [We(Xe(),g.fd().ld)],Lw(a,Ye(c,J(new K,g)),d.A(),e);if(k instanceof fe)return GX(a,k.aa,e);throw new w(k);}d=new Te(new Ue(J(new K,["Illegal prefix of type selection: ",""])));c=[wO(Xe(),AD(b,c))];return Lw(a,Ye(d,J(new K,c)),h.A(),e)} var Sfa=function P6(a,b,c,d,e,g,h,k,l,m,n){return Lx(a,new U(()=>c.da+". type "+wO(Xe(),b).Wr()),new U(()=>{var v=!1,x=null,A=!1,B=null;if(gl()===b){var C=b.A(),D=t().d;return new FA(a,!1,jx(new kx,a,C,"top type",D,!0))}if(el()===b){var F=b.A(),I=t().d;return new FA(a,!0,jx(new kx,a,F,"bottom type",I,!0))}if(b instanceof mP){v=!0;x=b;var M=x.Bi,N=x.Ci;if(el()===M&&gl()===N){var P=b.A(),T=t().d,Y=jx(new kx,a,P,"type wildcard",T,!0);return new cC(a,new FA(a,!0,Y),new FA(a,!1,Y),Y)}}if(v){var Z=x.Ci, S=P6(a,x.Bi,c,d,e,g,h,k,l,m,n),ea=P6(a,Z,c,d,e,g,h,k,l,m,n),ia=b.A(),X=t().d,sa=jx(new kx,a,ia,"type bounds",X,!0),Ja=Sw(a).ob;Tw(a,S,ea,e,sa,c,Ja);return new cC(a,S,ea,sa)}if(b instanceof jP){var Xa=b.Mu,Fa=ry(lv(),Xa,new y(Ta=>{var wb=Ta.Yf,$a=new y(wa=>P6(a,wa,c,d,e,g,h,k,l,m,n));return new Uw(a,wb.b()?R():new L($a.n(wb.o())),P6(a,Ta.Rg,c,d,e,g,h,k,l,m,n),jx(new kx,a,Ta.A(),"tuple field",(tx(a),t().d),(tx(a),!1)))})),za=b.A(),Qa=t().d;return new zv(a,Fa,jx(new kx,a,za,"tuple type",Qa,!0))}if(b instanceof rP){var Ma=Qt(b.Ax,new y(Ta=>{if(Ta instanceof fe){Ta=P6(a,Ta.aa,c,d,e,g,h,k,l,m,n);var wb=Ta.ma(),$a=t().d,wa=t().d,hb=O().c,ra=O().c;wb=new lx(a,c.da,hb,ra,$a,wa,!1,wb);$a=Ta.ma();wb=new Sv(a,new Uw(wb.q,R(),wb,$a),Ta.ma());$a=Ta.ma();wa=Sw(a).ob;Tw(a,Ta,wb,e,$a,c,wa);t();return new fe(Ta)}if(Ta instanceof Ud)return Ta=Ta.fa,t(),wb=Ta.Yf,$a=new y(wc=>P6(a,wc,c,d,e,g,h,k,l,m,n)),Ta=new Uw(a,wb.b()?R():new L($a.n(wb.o())),P6(a,Ta.Rg,c,d,e,g,h,k,l,m,n),jx(new kx,a,Ta.A(),"splice field",(tx(a),t().d), (tx(a),!1))),new Ud(Ta);throw new w(Ta);})),Ga=b.A(),ab=t().d;return new Wv(a,Ma,jx(new kx,a,Ga,"splice type",ab,!0))}if(b instanceof hP){var Hb=b.As,bc=b.Bs;if(g)var yb=new y(Ta=>{var wb=P6(a,Hb,c,d,e,g,h,k,l,m,n),$a=P6(a,bc,c,d,e,g,h,k,l,m,n);return Pu(wb,$a,Ta,!1)});else{var tb=P6(a,Hb,c,d,e,g,h,k,l,m,n),eb=P6(a,bc,c,d,e,g,h,k,l,m,n);yb=new y(Ta=>new LA(a,!1,tb,eb,Ta))}var kb=b.A(),Rb=t().d;return yb.n(jx(new kx,a,kb,"intersection type",Rb,!0))}if(b instanceof iP){var Gb=b.ht,vb=b.it;if(g)var Tb= new y(Ta=>{var wb=P6(a,Gb,c,d,e,g,h,k,l,m,n),$a=P6(a,vb,c,d,e,g,h,k,l,m,n);return dv(wb,$a,Ta,!1)});else{var Nb=P6(a,Gb,c,d,e,g,h,k,l,m,n),ic=P6(a,vb,c,d,e,g,h,k,l,m,n);Tb=new y(Ta=>new LA(a,!0,Nb,ic,Ta))}var Va=b.A(),cb=t().d;return Tb.n(jx(new kx,a,Va,"union type",cb,!0))}if(b instanceof kP){var zb=P6(a,b.cx,c,d,e,g,h,k,l,m,n),Ub=b.A(),jb=t().d;return new MA(a,zb,jx(new kx,a,Ub,"type negation",jb,!0))}if(b instanceof Tn){var db=b.Xs,ub=b.A(),Aa=t().d,va=jx(new kx,a,ub,"record type",Aa,!0);ET(db, new y(Ta=>Ta.h().x),new y(Ta=>Ta.h())).Ca(new y(Ta=>{if(null!==Ta&&(Ta=new L(Ta),!Ta.b())){var wb=Ta.k.h();Ta=Ta.k.j();if(0{var hb=Ye(new Te(new Ue(J(new K,["Declared at"]))),u());wa=wa.A();return G(new H,hb,wa)}));return ay(a,new z(wb,Ta),e)}}}));return SA(xv(a),Qt(db,new y(Ta=>{hB(ve(),Ta.h().x)&&Lw(a,Ye(new Te(new Ue(J(new K, ["Field identifiers must start with a small letter"]))),u()),Ta.h().A(),e);var wb=Ta.h(),$a=Ta.j().Yf,wa=new y(ac=>P6(a,ac,c,d,e,g,h,k,l,m,n));$a=$a.b()?R():new L(wa.n($a.o()));wa=P6(a,Ta.j().Rg,c,d,e,g,h,k,l,m,n);var hb=Ta.h(),ra=new vl(""),wc=Ta.j().A();hb=new Pl(hb,Cq(ra,wc));hb=Lq(hb);Ta=new Uw(a,$a,wa,jx(new kx,a,hb,(Ta.j().Yf.b()?"":"mutable ")+"record field",(tx(a),t().d),(tx(a),!1)));return G(new H,wb,Ta)})),va)}if(b instanceof Wt){var Ra=b.qs,rb=P6(a,b.ps,c,d,e,g,h,k,l,m,n),xb=P6(a,Ra,c, d,e,g,h,k,l,m,n),mc=b.A(),Ha=t().d;return new cv(a,rb,xb,jx(new kx,a,mc,"function type",Ha,!0))}if(b instanceof nP){var Ka=b.Mx,Oa=P6(a,b.Lx,c,d,e,g,h,k,l,m,n),Na=Qt(Ka.Xs,new y(Ta=>{if(null!==Ta){var wb=Ta.h(),$a=Ta.j();Ta=$a.Yf;var wa=new y(ra=>P6(a,ra,c,d,e,g,h,k,l,m,n));Ta=Ta.b()?R():new L(wa.n(Ta.o()));wa=P6(a,$a.Rg,c,d,e,g,h,k,l,m,n);var hb=new vl("");$a=$a.A();$a=new Pl(wb,Cq(hb,$a));$a=Lq($a);hb=t().d;Ta=new Uw(a,Ta,wa,jx(new kx,a,$a,"extension field",hb,!0));return G(new H,wb,Ta)}throw new w(Ta); })),Da=Ka.A(),ta=t().d,Ya=new Qv(a,Na,jx(new kx,a,Da,"extension record",ta,!0)),dc=b.A(),ka=t().d;return new $y(a,Oa,Ya,jx(new kx,a,dc,"extension type",ka,!0))}if(b instanceof oP){var ya=b.Es,Sa=a.$c?yq(ya):fq(ya),xc=b.A(),Sb=t().d;return new Mu(a,ya,Sa,jx(new kx,a,xc,"literal type",Sb,!0))}if(b instanceof Ep&&(A=!0,B=b,"this"===B.V)){var uc=!1,Lb=null,lc=c.hc.U("this");lc instanceof L&&(uc=!0,Lb=lc,Lb.k instanceof VP&&xm("Program reached and unexpected state."));if(uc){var Xb=Lb.k;if(Xb instanceof qx){var ec=Xb.tp;if(null!==ec)return ec}}if(t().d===lc){var Ab=Ye(new Te(new Ue(J(new K,["undeclared `this`"]))),u()),Ob=b.A(),fb=G(new H,Ab,Ob),Wa=O().c;return ay(a,new z(fb,Wa),e)}throw new w(lc);}if(b instanceof sP){var bb=b.nA;return P6(a,new Ep(UF(ve(),bb)),c,d,e,g,h,k,l,m,n)}if(A){var Ia=B.V,Ua=b.A(),pc=t().d,sc=jx(new kx,a,Ua,"type reference",pc,!0);return h.Se(Ia,new U((Ta=>()=>{var wb=O6(a,Ua,Ia,m,n,e);if(wb instanceof Ud){var $a=wb.fa;if(null!==$a){$a=$a.Sc();if(Pe(new E($a),0))return new fw(a, Ta,O().c,sc);var wa=c.$a.U(Ia);if(wa instanceof L){var hb=!1,ra=null;wa=wa.k.Ab;if(wa instanceof yo&&(hb=!0,ra=wa,wa=ra.pb,Bp()===wa||zp()===wa))return wb=c.$a.n(Ia).Ab,$a=t().d,bz(a,wb,jx(new kx,a,Ua,"class tag",$a,!0),c);if(hb&&(wa=ra.pb,Fp()===wa))return wb=c.$a.n(Ia).Ab,$a=t().d,Xx(a,wb,jx(new kx,a,Ua,"class tag",$a,!0),c);if(hb&&(hb=ra.pb,Ap()===hb))return wb=O().xR,new fw(a,Ta,Yea(wb,$a,new U(()=>{var wc=V(a),ac=t().d,Id=t().d,ud=O().c,be=O().c;return new lx(a,c.da,ud,be,ac,Id,!1,wc)})),sc); xm("Program reached and unexpected state.")}else return wb=new Te(new Ue(J(new K,["Type "," takes parameters"]))),$a=[We(Xe(),Ia)],Lw(a,Ye(wb,J(new K,$a)),Ua,e)}}if(wb instanceof fe){wb=wb.aa;""===Ia?$a=!0:($a=Hu(Q(),Ia),$a=!bE(Pr(),$a));if(!$a&&($a=G(new H,O6(a,Ua,Nu(Q(),Ia),m,n,e),c.tb.U(Nu(Q(),Ia))),hb=$a.y,$a=$a.w,hb instanceof Ud&&(hb=hb.fa,null!==hb&&(hb=hb.h(),$a instanceof L)))){wb=$a.k;if(Bp()===hb)return $a=t().d,rD(a,wb,jx(new kx,a,Ua,"class tag",$a,!0),c);if(Fp()===hb)return $a=t().d, TD(a,wb,jx(new kx,a,Ua,"trait tag",$a,!0));if(Ap()===hb)return wb=new Te(new Ue(J(new K,["Type alias "," cannot be used as a type tag"]))),$a=[We(Xe(),Nu(Q(),Ia))],Lw(a,Ye(wb,J(new K,$a)),Ua,e);if(zp()===hb)return wb=new Te(new Ue(J(new K,["Module "," cannot be used as a type tag"]))),$a=[We(Xe(),Nu(Q(),Ia))],Lw(a,Ye(wb,J(new K,$a)),Ua,e);if(cp()===hb)return wb=new Te(new Ue(J(new K,["Mixin "," cannot be used as a type tag"]))),$a=[We(Xe(),Nu(Q(),Ia))],Lw(a,Ye(wb,J(new K,$a)),Ua,e);throw new w(hb); }return Es(wb)}throw new w(wb);})(B)))}if(b instanceof jt){var Ba=GP(b.pp),ob=new y(Ta=>h.U(Ta)),nc=Ba.b()?R():ob.n(Ba.o()),Ib=new U(()=>{var Ta=d.Se(b,new U(()=>k.Hk(b,new U(()=>{var wa=V(a),hb=t().d,ra=aT(b),wc=new y(Id=>kda(Q(),Id,new y(ud=>Nm(new E(hc(Ea(ud))),hc(39)))));ra=ra.b()||wc.n(ra.o())?ra:R();wc=O().c;var ac=O().c;return new lx(a,l,wc,ac,hb,ra,!1,wa)})))),wb=b.A(),$a=t().d;wb=jx(new kx,a,wb,"type variable",$a,!0);return ux(Ta.q,Ta,wb)});return nc.b()?Es(Ib):nc.o()}if(b instanceof pP){var vc= b.Gw,Vb=b.Hw,fc=b.A(),Bc=t().d,Pb=jx(new kx,a,fc,"applied type reference",Bc,!0),Jb=O6(a,b.A(),vc.V,m,n,e);if(Jb instanceof Ud){var gc=Jb.fa;if(null!==gc){var Cb=gc.Sc(),cc=Vb.K();if(Pe(new E(cc),Cb))var yc=Qt(Vb,new y(Ta=>P6(a,Ta,c,d,e,g,h,k,l,m,n)));else{var Mc=new Te(new Ue(J(new K,["Wrong number of type arguments \u2013 expected ",", found ",""]))),qc=We(Xe(),""+Cb);Xe();var oc=Vb.K(),Qc=[qc,We(0,""+oc)];Lw(a,Ye(Mc,J(new K,Qc)),b.A(),e);var jc=Vb.m(),sb=new Ef(jc,new y(Ta=>P6(a,Ta,c,d,e,g,h,k, l,m,n))),Gc=kv(sb,new U(()=>{O();return new y0(new U(()=>{var Ta=V(a),wb=t().d,$a=t().d,wa=O().c,hb=O().c;return new lx(a,c.da,wa,hb,wb,$a,!1,Ta)}))})).Mn(Cb);Od();yc=Pd(u(),Gc)}return new fw(a,vc,yc,Pb)}}if(Jb instanceof fe)return Es(Jb.aa);throw new w(Jb);}if(b instanceof tP){var Wb=b.vx,Cc=b.wx,Fc=P6(a,Wb,c,d,e,g,h,k,l,m,n);return Rfa(a,Fc,c,Cc,e,b,Wb)}if(b instanceof lP){var qd=b.Zz,Yb=P6(a,b.tx,c,d,e,g,h,k,l,m,n),Nc=Su(),ad=op().ga,Uc=new Uu(Nc,ad),cd=oA(uv(),qd,Uc),kc=b.A(),Vc=t().d;return new Tv(a, Yb,cd,jx(new kx,a,kc,"field removal type",Vc,!0))}if(b instanceof qP){var Hc=b.Lw,rc=b.Mw,sd=b.Nw;if(Hc instanceof I_)var Kc=Hc;else xm("Program reached and unexpected state.");for(var Qd=P6(a,Kc,c,d,e,g,h,k,l,m,n),Ad=new y(Ta=>{a:{if(null!==Ta){var wb=Ta.h(),$a=Ta.j();if(null!==$a){var wa=$a.Bi;$a=$a.Ci;Ta=P6(a,wa,c,d,e,g,h,k,l,m,n);var hb=d.n(wb);wa=jx(new kx,a,wa.A(),"lower bound specifiation",(tx(a),t().d),(tx(a),!1));var ra=Sw(a).ob;Tw(a,Ta,hb,e,wa,c,ra);wb=d.n(wb);Ta=P6(a,$a,c,d,e,g,h,k,l,m, n);$a=jx(new kx,a,$a.A(),"upper bound specifiation",(tx(a),t().d),(tx(a),!1));hb=Sw(a).ob;Tw(a,wb,Ta,e,$a,c,hb);break a}}throw new w(Ta);}}),kd=rc;!kd.b();)Ad.n(kd.e()),kd=kd.f();for(var Hd=new y(Ta=>{if(null!==Ta){var wb=Ta.Bi,$a=Ta.Ci;Ta=P6(a,wb,c,d,e,g,h,k,l,m,n);var wa=P6(a,$a,c,d,e,g,h,k,l,m,n);wb=jx(new kx,a,Wca(wF(),wb.A(),$a.A(),new fn((hb,ra)=>xs(hb,ra))),"constraint specifiation",(tx(a),t().d),(tx(a),!1));$a=Sw(a).ob;Tw(a,Ta,wa,e,wb,c,$a)}else throw new w(Ta);}),Rd=sd;!Rd.b();)Hd.n(Rd.e()), Rd=Rd.f();return Qd}if(b instanceof Vt){var Bd=b.Ws,ae=b.Vs,dd=jx(new kx,a,ae.A(),"polymorphic type",(tx(a),t().d),(tx(a),!1)),od=new y(Ta=>{var wb=new aw(d);Qt(Bd,new y($a=>{if($a instanceof fe)xm("Program reached and unexpected state.");else{if($a instanceof Ud){$a=$a.fa;var wa=$a.A(),hb=aT($a);wa=jx(new kx,a,wa,"quantified type variable",hb,!0);hb=t().d;var ra=aT($a),wc=O().c,ac=O().c;wa=new lx(a,Ta.da,wc,ac,hb,ra,!1,wa);wb.rc=wb.rc.Pn(G(new H,$a,wa));return wa}throw new w($a);}}));return P6(a, ae,Ta,wb.rc,e,g,h,k,l,m,n)});Sw(c.S);return Px(c,od,e,dd)}throw new w(b);}),new y(v=>"\x3d\x3e "+v))};function Q6(a,b,c,d,e,g,h){var k=tc();try{var l=new IQ(0),m=new y(r=>{if(r instanceof Ff){if(Pe(new E(l.ve),0)){var v=Ey(a),x=new y(()=>{}),A=V(a),B=Sw(a).ob;Tw(a,v,d,x,A,e,B);g.n(r)}else if(3<=l.ve)throw Hq(new Iq,k,d);l.ve=1+l.ve|0}else g.n(r)}),n=Sw(a).ob;Tw(a,b,c,m,h,e,n);return d}catch(r){if(r instanceof Iq){b=r;if(b.Qg===k)return b.Cj();throw b;}throw r;}} function R6(a,b,c,d){if(!b.tJ().b()){var e=new Te(new Ue(J(new K,["Class "," is abstract and cannot be instantiated"])));b=[We(Xe(),b.Cf.x)];Lw(a,Ye(e,J(new K,b)),c.A(),d)}}function S6(a,b,c,d){if(b.Om.b()){var e=new Te(new Ue(J(new K,[""," `","` is not mutable and cannot be reassigned"])));b=[We(Xe(),cy(b)),We(Xe(),b.Rb.x)];Lw(a,Ye(e,J(new K,b)),c.Ga,d)}} function T6(a,b,c,d,e,g){if(a.F){var h=""+ut(Q(),"| ",a.r)+G(new H,b,c.$a.U(b));ff(gf(),h+"\n")}c=c.$a.U(b);if(t().d===c)return g=new Te(new Ue(J(new K,["Type `","` cannot be used in `new` expression"]))),b=[We(Xe(),b)],Lw(a,Ye(g,J(new K,b)),d.A(),e);if(c instanceof L){b=c.k;if(null!==b&&!Ot(new E(b.Qu),Bp()))return d=new Te(new Ue(J(new K,[""," "," cannot be used in `new` expression"]))),Xe(),Q(),b=[We(0,Nu(0,b.Qu.ld)),We(Xe(),b.sO)],Lw(a,Ye(d,J(new K,b)),g.Ga,e);if(null!==b)return h=b.Ab,h.tJ().b()|| (c=new Te(new Ue(J(new K,["Class "," is abstract and cannot be instantiated"]))),h=[We(Xe(),h.Cf.x)],Lw(a,Ye(c,J(new K,h)),d.A(),e)),Cy(b,!0,g.Ga,e);throw new w(b);}throw new w(c);} var Tfa=function U6(a,b,c){b=uy(b);if(b instanceof Qx){var e=b.Re;if(null!==e&&(e=Hv(Iv(a),e,c),!e.b()&&(e=e.k,e instanceof cv)))return O(),a=J(new K,[e]),Pd(u(),a)}if(b instanceof cv)return O(),a=J(new K,[b]),Pd(u(),a);if(b instanceof lx){var g=vy(b);if(g===u())a=u();else{b=g.e();e=b=new z(U6(a,b,c),u());for(g=g.f();g!==u();){var h=g.e();h=new z(U6(a,h,c),u());e=e.p=h;g=g.f()}a=b}c=op().ga;return zX(a,c)}return b instanceof LA&&(g=b.ic,e=b.jc,Pe(new E(b.tc),!1))?(b=U6(a,g,c),a=U6(a,e,c),un(b,a)): O().c}; function V6(a,b,c,d,e,g){if(a.oP){Sw(c.S);var h=1+c.da|0,k=Hw(),l=Su(),m=op().ga;k=k.Hd(new Uu(l,m));h=new Iw(c.S,c.Ec,c.hc,c.Ed,h,c.Pc,c.Zc,c.Lb,c.yc,c.tb,c.$a,c.od,k);a=Rw(a,b,h,d,e,!0);up(tp(),c.S.li||h.cb.b());Xu().X();b=vx(c.S);e=h.cb.m();e=new xo(e,new y((r=>v=>{if(null!==v){var x=v.h();v=v.j().m();return new Ef(v,new y(A=>{if(null!==A){var B=A.Rc();A=A.j();up(tp(),A.Ea()>r.da);return B?G(new H,A,x):G(new H,x,A)}throw new w(A);}))}throw new w(v);})(c)));Od();a=wx(b,Pd(u(),e),a);b=c.S;b.F&&(b= ut(Q(),"| ",b.r)+("Inferred poly constr: "+a+" \u2014\u2014 where ")+xx(a),ff(gf(),b+"\n"));c.S.F&&Nm(new E(a),a)&&(b=c.S,b.F&&(b=ut(Q(),"| ",b.r)+("Refreshed: "+a+" \u2014\u2014 where ")+xx(a),ff(gf(),b+"\n")));a=yx(zx(c.S),c.da,a);h.cb.mg();b=h.cb;up(tp(),c.S.li||h.cb.b());if(!b.b()){h=c.S.qa;e=c.S;e.F&&(k=ut(Q(),"| ",e.r)+"UNSTASHING... (out)",ff(gf(),k+"\n"));e.r=1+e.r|0;try{b.Ca(new y(((r,v)=>x=>{if(null!==x){var A=x.h();for(x=x.j().m();x.s();){var B=x.t();a:{if(null!==B){var C= B.j();if(!0===B.Rc()){B=Sw(r.S).ob;Tw(r.S,C,A,d,g,v,B);break a}}if(null!==B&&(C=B.j(),!1===B.Rc())){B=Sw(r.S).ob;Tw(r.S,A,C,d,g,v,B);break a}throw new w(B);}}}else throw new w(x);})(c,c)));b.mg();var n=void 0}finally{e.r=-1+e.r|0}dx(new E(h),e.qa)&&e.F&&(c=""+ut(Q(),"| ",e.r)+h.n(n),ff(gf(),c+"\n"))}return a}return Rw(a,b,c,d,e,!0)} function Ufa(a,b,c,d,e,g,h){var k=W6(a,b,d,e,g);g=t().d;t();var l=c.x;l=(0<=l.length&&"_"===l.substring(0,1)?0:!YD(c))?new L(c.x):R();var m=O().c,n=O().c;g=new lx(a,d.da,m,n,g,l,!1,h);b=ux(a,k,jx(new kx,a,Lq(b),"receiver",(tx(a),t().d),(tx(a),!1)));k=xv(a);l=jx(new kx,a,c.A(),"field selector",(tx(a),t().d),(tx(a),!1));l=new Uw(g.q,R(),g,l);c=G(new H,c,l);l=O().c;c=SA(k,new z(c,l),h);return Q6(a,b,c,g,d,e,h)} function X6(a,b,c,d,e,g,h,k){if(a.$c)var l=t().d;else{var m=c.x;a:{if(null!==m&&(l=dda(Wea(),m),!l.b()&&null!==l.o()&&0===l.o().ab(2))){m=l.o().va(0);l=l.o().va(1);l=hY(d,(t(),new L(m)),l);break a}l=hY(d,t().d,m)}}if(l instanceof L){l=l.k;if(l.Waa().b()){if(!(0{A=d.tb.n(A.V);var B=new Te(new Ue(J(new K,["\u2022 "," ",""]))),C=[We(Xe(),A.jj.ld),wO(Xe(),A.Wl)];B=Ye(B,J(new K,C));A=A.Wl.A();return G(new H,B,A)};if(r===u())m=u();else{n=r.e();var v=n=new z(m(n),u());for(r=r.f();r!==u();){var x=r.e();x=new z(m(x),u());v=v.p=x;r=r.f()}m=n}ay(a,new z(e,new z(c,m)),g)}h=W6(a,b,d,g,h);b=t().d;c=t().d;e=O().c;m=O().c;b=new lx(a,d.da,e,m,b,c,!1,k);l=l.maa();return Q6(a, TA(l,d),new cv(a,Y6(a,h),b,k),b,d,g,k)}if(t().d===l){if(a.$c?0:hB(ve(),c.x))return k=new Te(new Ue(J(new K,["Method "," not found"]))),h=[We(Xe(),c.x)],Lw(a,Ye(k,J(new K,h)),e.A(),g);b instanceof am&&(l=new vl("super"),b=b.A(),b=Cq(l,b));return Ufa(a,b,c,d,g,h,k)}throw new w(l);}function Vfa(a,b,c){for(;;){var d=tD(a,b);if(d instanceof Qx)a=d,c.Am=!0,a=TA(a,b);else return a}} var Yfa=function Wfa(a,b,c,d,e,g,h){for(;;){var l=b;if(l instanceof z){b=l;var m=b.z;b=b.p;if(null!==m){var n=m.h();m=m.cy();if(null!==n)if(l=n.h(),n=n.j(),m)if(m=n.ya,m instanceof Dl||m instanceof vl){t();c=c.Pn(G(new H,l,new Ud(n.ya)));continue}else return m=new vl(Xfa(l,d.jn())),n=n.ya,t(),new Rl(!1,m,n,Wfa(a,b,c.Pn(G(new H,l,new fe(m))),d,e,g,h));else{t();c=c.Pn(G(new H,l,new Ud(n.ya)));continue}}}b=O().c;if(null===b?null===l:b.i(l)){d=((r,v,x)=>A=>{var B=!1,C=null,D=r.U(A.x);if(D instanceof L){B= !0;C=D;var F=C.k;if(F instanceof fe)return A=F.aa,G(new H,R(),new sm(tm().Cg,A))}if(B&&(B=C.k,B instanceof Ud))return A=B.fa,G(new H,R(),new sm(tm().Cg,A));if(R()===D)return D=new Te(new Ue(J(new K,["Argument named '","' is missing from this function call"]))),A=[We(Xe(),A.x)],Lw(a,Ye(D,J(new K,A)),v.A(),x),G(new H,R(),new sm(tm().Cg,new vl("error")));throw new w(D);})(c,d,g);if(e===u())e=u();else{g=e.e();b=g=new z(d(g),u());for(e=e.f();e!==u();)c=e.e(),c=new z(d(c),u()),b=b.p=c,e=e.f();e=g}e=new Gl(e); return new Pl(h,e)}throw new w(l);}};function Z6(a,b,c,d,e,g,h){if(null!==b){var k=b.Oa,l=b.ra;if(k instanceof L&&(k=k.k,k instanceof lx&&l instanceof lx&&Pe(new E(k),l)))return a=$6(a,l,c,d,e,g,h),new Sn((t(),new L(a)),a)}l=b.Oa;l.b()?l=R():(l=l.o(),l=new L($6(a,l,c,d,e,g,h)));return new Sn(l,$6(a,b.ra,c,d,e,g,h))}function a7(a,b,c,d,e,g,h){Od();b=Pd(u(),b);var k=new y(m=>m.h()),l=cT();b=QY(b,k,l);return new Dt(Wn(b,new P_(a,c,d,e,g,h)))} var $6=function b7(a,b,c,d,e,g,h){for(;;){var l=!1,m=null,n=!1,r=null,v=!1,x=null,A=!1,B=null,C=!1,D=null,F=uy(b);if(F instanceof lx&&(l=!0,m=F,d))return c7(m);if(l)return c.bO.Se(m,new U(((Kd,ld,Jd,Dd,Xd,Yc)=>()=>{var Ce=c7(Kd);if(ld.oh(Kd)){var te=Kd.Sb;if(te instanceof L){var Ie=b7(a,te.k,Jd,Xd,ld,Dd,Yc);te=Dd.rc;Ie=new mP(Ie,Ie);Ie=G(new H,Ce,Ie);Dd.rc=new z(Ie,te)}else if(t().d===te){te=vy(Kd);for(Ie=a.ib;!te.b();){var Jf=te.e(),df=V(Ie.q);Ie=dv(Ie,Jf,df,!1);te=te.f()}te=b7(a,Ie,Jd,Xd,ld,Dd, Yc);Ie=rA(Kd);for(Jf=a.La;!Ie.b();){df=Ie.e();var vg=V(Jf.q);Jf=sA(Jf,df,vg);Ie=Ie.f()}Jf=b7(a,Jf,Jd,Xd,ld,Dd,Yc);if(Nm(new E(te),el())||Nm(new E(Jf),gl()))Ie=Dd.rc,te=new mP(te,Jf),te=G(new H,Ce,te),Dd.rc=new z(te,Ie)}else throw new w(te);}return Ce})(m,e,c,g,d,h)));if(F instanceof cv){var I=F,M=I.ac;return new Wt(b7(a,I.Nb,c,d,e,g,h),b7(a,M,c,d,e,g,h))}if(F instanceof LA){n=!0;r=F;var N=r.ic,P=r.jc;if(!0===r.tc){var T=a.UE,Y=a.TI,Z=V(a.UE.q),S=dv(T,Y,Z,!1);return Aca(r,S,h)?new Ep("Bool"):new iP(b7(a, N,c,d,e,g,h),b7(a,P,c,d,e,g,h))}}if(n){var ea=r.ic,ia=r.jc;if(!1===r.tc)return new hP(b7(a,ea,c,d,e,g,h),b7(a,ia,c,d,e,g,h))}if(F instanceof Qv){var X=F.Ba;return new Tn(ry(lv(),X,new y(((Kd,ld,Jd,Dd,Xd)=>Yc=>Z6(a,Yc,Kd,ld,Jd,Dd,Xd))(c,d,e,g,h))))}if(F instanceof zv){var sa=F.Yb;return new jP(ry(lv(),sa,new y(((Kd,ld,Jd,Dd,Xd)=>Yc=>Z6(a,Yc,Kd,ld,Jd,Dd,Xd))(c,d,e,g,h))))}if(F instanceof Sv){v=!0;x=F;var Ja=x.Fd;if(null!==Ja){var Xa=Ja.Oa,Fa=Ja.ra;if(R()===Xa){var za=new Ep("Array"),Qa=b7(a,Fa,c,d, e,g,h),Ma=O().c;return new pP(za,new z(Qa,Ma))}}}if(v){var Ga=Z6(a,x.Fd,c,d,e,g,h),ab=new Ep("MutArray"),Hb=Ga.Yf,bc=new mP(Hb.b()?el():Hb.o(),Ga.Rg),yb=O().c;return new pP(ab,new z(bc,yb))}if(F instanceof Wv){var tb=F.fo,eb=((Kd,ld,Jd,Dd,Xd)=>Yc=>{if(Yc instanceof fe)return Yc=Yc.aa,t(),Yc=b7(a,Yc,Kd,ld,Jd,Dd,Xd),new fe(Yc);if(Yc instanceof Ud){Yc=Yc.fa;t();var Ce=Yc.Oa;Ce.b()?Ce=R():(Ce=Ce.o(),Ce=new L(b7(a,Ce,Kd,ld,Jd,Dd,Xd)));Yc=new Sn(Ce,b7(a,Yc.ra,Kd,ld,Jd,Dd,Xd));return new Ud(Yc)}throw new w(Yc); })(c,d,e,g,h);if(tb===u())var kb=u();else{for(var Rb=tb.e(),Gb=new z(eb(Rb),u()),vb=Gb,Tb=tb.f();Tb!==u();){var Nb=Tb.e(),ic=new z(eb(Nb),u());vb=vb.p=ic;Tb=Tb.f()}kb=Gb}return new rP(kb)}if(F instanceof MA)return new kP(b7(a,F.Fc,c,d,e,g,h));if(F instanceof FA&&(A=!0,B=F,!0===B.Eh))return el();if(A&&!1===B.Eh)return gl();if(F instanceof $y){var Va=F,cb=Va.up,zb=b7(a,Va.Oq,c,d,e,g,h);lv();return new nP(zb,new Tn(ry(0,cb.Ba,new y(((Kd,ld,Jd,Dd,Xd)=>Yc=>Z6(a,Yc,Kd,ld,Jd,Dd,Xd))(c,d,e,g,h)))))}if(F instanceof ZB){var Ub=F;$B(a);t();var jb=Ub.mc(),db=new L(jb);if(!db.b()){b=db.k;continue}}if(XB(F)){var ub=F.rr();if(ub instanceof vl){var Aa=ub.x;return a.cn.L(Aa)||"this"===Aa?new Ep(Aa):new sP(Nu(Q(),Aa))}if(ub instanceof Dl)return new oP(ub);throw new w(ub);}if(F instanceof mx){var va=F.hi,Ra=va.kg;if(Ra instanceof L){var rb=Ra.k;if(hB(ve(),rb))return new Ep(rb)}b=va}else{if(F instanceof YB){var xb=F,mc=xb.pA,Ha=xb.Ym;if(null!==Ha){var Ka=Ha.hi;return mc?Zfa(Ka):$fa(Ka)}}if(F instanceof fw){C=!0;D=F;var Oa= D.qb,Na=D.Zb,Da=O().c;if(null===Da?null===Na:Da.i(Na))return Oa}if(C){var ta=D.qb,Ya=D;t();return new pP(ta,bw(Ya,new L(!0),new fn(((Kd,ld,Jd,Dd,Xd)=>(Yc,Ce)=>{a:{if(Yc instanceof L){if(!0===!!Yc.k){var te=a.La;te=null===te?null===Ce:mC(te,Ce)}else te=!1;if(te){Yc=!0;break a}}if(Yc instanceof L&&(!1===!!Yc.k?(Yc=a.ib,Yc=null===Yc?null===Ce:mC(Yc,Ce)):Yc=!1,Yc)){Yc=!0;break a}Yc=!1}return Yc?new mP(el(),gl()):b7(a,Ce,Kd,ld,Jd,Dd,Xd)})(c,d,e,g,h)),h))}if(F instanceof cC){var dc=F,ka=dc.Sf;return new mP(b7(a, dc.Fg,c,d,e,g,h),b7(a,ka,c,d,e,g,h))}if(F instanceof Tv){var ya=F,Sa=ya.kf;return new lP(b7(a,ya.Ic,c,d,e,g,h),(Od(),Pd(u(),Sa)))}if(F instanceof Jv){var xc=F.gi,Sb=((Kd,ld,Jd,Dd,Xd)=>Yc=>b7(a,Yc,Kd,ld,Jd,Dd,Xd))(c,d,e,g,h);if(xc===u())var uc=u();else{for(var Lb=xc.e(),lc=new z(Sb(Lb),u()),Xb=lc,ec=xc.f();ec!==u();){var Ab=ec.e(),Ob=new z(Sb(Ab),u());Xb=Xb.p=Ob;ec=ec.f()}uc=lc}var fb=Un();return eH(uc,fb)}if(F instanceof Qx){var Wa=F,bb=Wa.de,Ia=Wa.Re,Ua=g.rc.K(),pc=b7(a,Ia,c,d,e,g,h),sc=er(g.rc).m().ph(Ua), Ba=PE().vl(sc),ob=gD(Ia,bb,a.Df).m(),nc=$S(pc),Ib=Ba.m(),vc=new Ef(Ib,new y(Kd=>Kd.h())),Vb=nc.Ce(vc),fc=Ba.m(),Bc=new xo(fc,new y(Kd=>$S(Kd.j()))),Pb=Vb.Ce(Bc),Jb=new iy(ob,new y((Kd=>ld=>Kd.L(c7(ld)))(Pb)),!1);if(Jb.s()){tp();ms();var gc=null;for(gc=[];Jb.s();){var Cb=Jb.t();gc.push(null===Cb?null:Cb)}var cc=new (md(RX).Ia)(gc),yc=Su(),Mc=op().ga,qc=Tu(cc,new Uu(yc,Mc)),oc=Kd=>{Kd=c7(Kd);t();return new Ud(Kd)},Qc=qc.a.length,jc=new (md(K2).Ia)(Qc);if(0Kd.th(new fn((ld,Jd)=>{var Dd=V(ld.q);return sA(ld,Jd,Dd)})))),Wd=ry(lv(),Rc,new y(Kd=>Kd.th(new fn((ld,Jd)=>{var Dd=V(ld.q);return dv(ld,Jd,Dd,!1)}))));if(Wd===u())var zd=u();else{for(var Pa=Wd.e(),Db=new z(Pa.Cw(),u()),Oc=Db,Tc=Wd.f();Tc!==u();){var Sd=Tc.e(),Jc=new z(Sd.Cw(),u());Oc=Oc.p=Jc;Tc=Tc.f()}zd=Db}var vd=un(Wc,zd),hd=((Kd,ld,Jd,Dd,Xd)=>Yc=>{if(null!==Yc){var Ce=Yc.j();return new mP(b7(a, Yc.h(),Kd,ld,Jd,Dd,Xd),b7(a,Ce,Kd,ld,Jd,Dd,Xd))}throw new w(Yc);})(c,d,e,g,h);if(vd===u())var de=u();else{for(var ye=vd.e(),jf=new z(hd(ye),u()),af=jf,pf=vd.f();pf!==u();){var kf=pf.e(),Be=new z(hd(kf),u());af=af.p=Be;pf=pf.f()}de=jf}return new qP(b7(a,Hc,c,d,e,g,h),O().c,de)}throw new w(F);}}}; function hf(a,b,c,d){this.io=this.ho=this.jo=null;this.Fp=this.Gp=this.an=this.Ep=0;this.qa=null;this.r=0;this.zk=this.Pq=this.Uq=this.xp=this.Bp=this.Cp=this.Sq=this.zp=this.Rq=this.wp=this.Ap=this.yp=this.Qq=this.Tq=null;this.Dp=0;this.hs=this.yq=this.Aq=this.Bq=this.zq=this.Dq=this.Cq=null;this.Im=this.Pw=0;this.iA=this.hA=this.Ku=null;this.Hb=!1;this.XA=0;this.VE=this.WE=this.TE=this.SE=this.YA=null;this.sP=this.li=this.ZE=this.pP=this.oP=this.Wq=this.rP=this.UI=this.VI=this.$c=this.zA=this.$E= this.F=!1;this.Df=this.Gd=0;this.uP=this.nP=this.XI=this.tP=this.cn=this.XE=this.qP=this.bn=this.WI=this.Bk=this.lP=this.RE=this.TI=this.UE=this.Qj=this.Ak=this.ki=this.mP=this.lg=this.Ck=this.ib=this.La=null;this.YE=0;this.F=a;this.$E=b;this.zA=c;this.$c=d;oca(this);cY||(cY=new bY);null===this.Cp&&null===this.Cp&&(this.Cp=new ZP(this));this.zk=this.Cp;TE(PE());this.Dp=0;this.Pw=1E4;this.Im=0;this.Hb=!1;this.XA=0;this.YA=$ea();this.li=this.ZE=this.pP=this.oP=this.Wq=this.rP=this.UI=this.VI=!1;this.sP= !0;this.Gd=0;this.Df=1024;this.La=new FA(this,!1,d7(this));this.ib=new FA(this,!0,d7(this));this.Ck=d?new Mu(this,new Gm(!0),ap(),d7(this)):new Mu(this,new vl("unit"),ap(),d7(this));this.lg=d?$P(this.zk,new Ep("Bool"),O().c,d7(this)):new Mu(this,new vl("bool"),ap(),d7(this));new Mu(this,new vl("Object"),ap(),d7(this));this.mP=d?$P(this.zk,new Ep("Object"),O().c,d7(this)):this.La;this.ki=d?$P(this.zk,new Ep("Int"),O().c,d7(this)):new Mu(this,new vl("int"),M6(new Ep("number")),d7(this));this.Ak=d?$P(this.zk, new Ep("Num"),O().c,d7(this)):new Mu(this,new vl("number"),ap(),d7(this));this.Qj=d?$P(this.zk,new Ep("Str"),O().c,d7(this)):new Mu(this,new vl("string"),ap(),d7(this));this.UE=d?$P(this.zk,new Ep("true"),O().c,d7(this)):new Mu(this,new vl("true"),M6(new Ep("bool")),d7(this));this.TI=d?$P(this.zk,new Ep("false"),O().c,d7(this)):new Mu(this,new vl("false"),M6(new Ep("bool")),d7(this));this.RE=$P(this.zk,new Ep("Annotation"),O().c,d7(this));this.lP=new nC(this,new vl("Eql"),ap(),V(this));this.Bk=new vl("error"); O();var e=G(new H,"unit",this.Ck),g=G(new H,"bool",this.lg),h=G(new H,"int",this.ki),k=G(new H,"number",this.Ak),l=G(new H,"string",this.Qj),m=G(new H,"anything",this.La),n=[e,g,h,k,l,m,G(new H,"nothing",this.ib)],r=J(new K,n);this.WI=Pd(u(),r);this.bn=new Kq(0,0,new Ge("\x3cprelude\x3e",0,Gaa()));t();var v=Bp(),x=new Ep("Object"),A=O().c,B=t().d,C=t().d,D=t().d,F=O().c,I=t().d,M=t().d,N=new Dt(O().c),P=t().d;t();var T=new yo(v,x,A,B,C,D,F,I,M,N,P,new L(this.bn),O().c),Y=Fp(),Z=new Ep("Eql");t(); var S=ou().AA,ea=G(new H,new L(S),new Ep("A")),ia=O().c,X=new z(ea,ia),sa=t().d,Ja=t().d,Xa=t().d,Fa=O().c,za=t().d,Qa=t().d,Ma=new Dt(O().c),Ga=t().d;t();var ab=new yo(Y,Z,X,sa,Ja,Xa,Fa,za,Qa,Ma,Ga,new L(this.bn),O().c),Hb=Bp(),bc=new Ep("Num"),yb=O().c,tb=t().d,eb=t().d,kb=t().d,Rb=O().c,Gb=t().d,vb=t().d,Tb=new Dt(O().c),Nb=t().d;t();var ic=new yo(Hb,bc,yb,tb,eb,kb,Rb,Gb,vb,Tb,Nb,new L(this.bn),O().c),Va=Bp(),cb=new Ep("Int"),zb=O().c,Ub=t().d,jb=t().d,db=t().d,ub=new vl("Num"),Aa=O().c,va=new z(ub, Aa),Ra=t().d,rb=t().d,xb=new Dt(O().c),mc=t().d;t();var Ha=new yo(Va,cb,zb,Ub,jb,db,va,Ra,rb,xb,mc,new L(this.bn),O().c),Ka=Bp(),Oa=new Ep("Bool"),Na=O().c,Da=t().d,ta=t().d;t();var Ya=new iP(new Ep("true"),new Ep("false")),dc=new L(Ya),ka=O().c,ya=t().d,Sa=t().d,xc=new Dt(O().c),Sb=t().d;t();var uc=new yo(Ka,Oa,Na,Da,ta,dc,ka,ya,Sa,xc,Sb,new L(this.bn),O().c),Lb=zp(),lc=new Ep("true"),Xb=O().c,ec=t().d,Ab=t().d,Ob=t().d,fb=new vl("Bool"),Wa=O().c,bb=new yo(Lb,lc,Xb,ec,Ab,Ob,new z(fb,Wa),t().d,t().d, new Dt(O().c),t().d,t().d,O().c),Ia=zp(),Ua=new Ep("false"),pc=O().c,sc=t().d,Ba=t().d,ob=t().d,nc=new vl("Bool"),Ib=O().c,vc=new yo(Ia,Ua,pc,sc,Ba,ob,new z(nc,Ib),t().d,t().d,new Dt(O().c),t().d,t().d,O().c),Vb=Bp(),fc=new Ep("Str"),Bc=O().c,Pb=t().d,Jb=t().d,gc=t().d,Cb=O().c,cc=t().d,yc=t().d,Mc=new Dt(O().c),qc=t().d;t();var oc=new yo(Vb,fc,Bc,Pb,Jb,gc,Cb,cc,yc,Mc,qc,new L(this.bn),O().c),Qc=Ap(),jc=new Ep("undefined"),sb=O().c,Gc=t().d,Wb=t().d;t();var Cc=new oP(new Gm(!0)),Fc=new L(Cc),qd=O().c, Yb=t().d,Nc=t().d,ad=new Dt(O().c),Uc=t().d;t();var cd=new yo(Qc,jc,sb,Gc,Wb,Fc,qd,Yb,Nc,ad,Uc,new L(this.bn),O().c),kc=Ap(),Vc=new Ep("null"),Hc=O().c,rc=t().d,sd=t().d;t();var Kc=new oP(new Gm(!1)),Qd=new L(Kc),Ad=O().c,kd=t().d,Hd=t().d,Rd=new Dt(O().c),Bd=t().d;t();var ae=new yo(kc,Vc,Hc,rc,sd,Qd,Ad,kd,Hd,Rd,Bd,new L(this.bn),O().c),dd=Bp(),od=new Ep("Annotation"),Ta=O().c,wb=t().d,$a=t().d,wa=t().d,hb=O().c,ra=t().d,wc=t().d,ac=new Dt(O().c),Id=t().d;t();var ud=new yo(dd,od,Ta,wb,$a,wa,hb,ra, wc,ac,Id,new L(this.bn),O().c),be=Bp(),re=new Ep("Code");t();var pe=ou().Zu,bd=new L(pe),Rc=new Ep("T"),Wc=G(new H,bd,Rc);t();var Wd=ou().Zu,zd=new L(Wd),Pa=new Ep("C"),Db=G(new H,zd,Pa),Oc=O().c,Tc=new z(Wc,new z(Db,Oc)),Sd=t().d,Jc=t().d,vd=t().d,hd=O().c,de=t().d,ye=t().d,jf=new Dt(O().c),af=t().d;t();var pf=new yo(be,re,Tc,Sd,Jc,vd,hd,de,ye,jf,af,new L(this.bn),O().c),kf=Bp(),Be=new Ep("Var");t();var Kd=ou().Yl,ld=new L(Kd),Jd=new Ep("T"),Dd=G(new H,ld,Jd);t();var Xd=ou().Yl,Yc=new L(Xd),Ce=new Ep("C"), te=G(new H,Yc,Ce),Ie=O().c,Jf=new z(Dd,new z(te,Ie)),df=t().d,vg=t().d,wg=t().d,xg=new vl("Code"),eg=new Ep("T"),vh=new Ep("C"),fg=O().c,ih=new Il(xg,new z(eg,new z(vh,fg))),Ig=O().c,Tf=new z(ih,Ig),Jg=t().d,jh=t().d,yg=new Dt(O().c),gg=t().d;t();var Cf=[T,ab,ic,Ha,uc,bb,vc,oc,cd,ae,ud,pf,new yo(kf,Be,Jf,df,vg,wg,Tf,Jg,jh,yg,gg,new L(this.bn),O().c)],Uf=J(new K,Cf);this.qP=Pd(u(),Uf);var $g=new YX(this,Bp(),new Ep("?"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Ah=new YX(this,Bp(), new Ep("int"),O().c,this.La,O().c,O().c,M6(new Ep("number")),t().d,O().c,(c3(this),t().d)),Kg=new YX(this,Bp(),new Ep("number"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Vf=new YX(this,Bp(),new Ep("bool"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),hg=new YX(this,Bp(),new Ep("true"),O().c,this.La,O().c,O().c,M6(new Ep("bool")),t().d,O().c,(c3(this),t().d)),zg=new YX(this,Bp(),new Ep("false"),O().c,this.La,O().c,O().c,M6(new Ep("bool")),t().d,O().c,(c3(this),t().d)), Lg=new YX(this,Bp(),new Ep("string"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Mg=new YX(this,Ap(),new Ep("undefined"),O().c,new Mu(this,new Gm(!0),ap(),V(this)),O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Wf=new YX(this,Ap(),new Ep("null"),O().c,new Mu(this,new Gm(!1),ap(),V(this)),O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Ng=new YX(this,Ap(),new Ep("anything"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Kf=new YX(this,Ap(),new Ep("nothing"),O().c,this.ib, O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),xf=new YX(this,Bp(),new Ep("error"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),Og=new YX(this,Bp(),new Ep("unit"),O().c,this.La,O().c,O().c,ap(),t().d,O().c,(c3(this),t().d)),mi=d7(this),Ci=t().d,Xh=t().d,wh=O().c,Bh=O().c,ng=new lx(this,1,wh,Bh,Ci,Xh,!1,mi),kh=Ap(),Kh=new Ep("Array");O();var ni=new Ep("A"),Lh=[G(new H,ni,ng)],lh=J(new K,Lh),Ch=new YX(this,kh,Kh,Pd(u(),lh),new Sv(this,new Uw(this,R(),ng,d7(this)),d7(this)),O().c,O().c, ap(),t().d,O().c,(c3(this),t().d));t();var Dh=Xu(),Yh=ou().Zu,ah=[G(new H,ng,Yh)],oi=Dh.Hh(J(new K,ah));Ch.Nu=new L(oi);var mj=d7(this),wd=t().d,ge=t().d,De=O().c,qf=O().c,og=new lx(this,1,De,qf,wd,ge,!1,mj),Xf=Ap(),mh=new Ep("MutArray");O();var Ag=new Ep("A"),Bg=[G(new H,Ag,og)],Eh=J(new K,Bg),Pg=new YX(this,Xf,mh,Pd(u(),Eh),new Sv(this,new Uw(this,new L(og),og,d7(this)),d7(this)),O().c,O().c,ap(),t().d,O().c,(c3(this),t().d));t();var Di=Xu(),Mh=ou().Yl,pi=[G(new H,og,Mh)],Xi=Di.Hh(J(new K,pi)); Pg.Nu=new L(Xi);var Qg=O().c;this.XE=new z($g,new z(Ah,new z(Kg,new z(Vf,new z(hg,new z(zg,new z(Lg,new z(Mg,new z(Wf,new z(Ng,new z(Kf,new z(xf,new z(Og,new z(Ch,new z(Pg,Qg)))))))))))))));var nh=this.XE.m(),bh=new Ef(nh,new y(ev=>ev.Wl.V)),Mj=new xo(bh,new y(ev=>{var m0=UF(ve(),ev);ev=Nu(Q(),ev);var iga=O().c;return new z(m0,new z(ev,iga))}));this.cn=Aq(Bq(),Mj).bc("Object").bc("Num").bc("Str");this.tP=this.cn.bc("Eql");var Nj=V(this),ie=t().d,Ac=t().d,Ve=O().c,Td=O().c;this.XI=new lx(this,1,Ve, Td,ie,Ac,!1,Nj);var lf=V(this),Yi=t().d,Jl=t().d,ll=O().c,Bj=O().c,$k=new lx(this,1,ll,Bj,Yi,Jl,!1,lf),Zh=d?new Gp(new cv(this,e7(this,this.ki,this.ki),this.ki,V(this)),new cv(this,e7(this,this.Ak,this.Ak),this.Ak,V(this)),new cv(this,e7(this,this.Ak,this.Ak),this.lg,V(this)),new cv(this,e7(this,this.Qj,this.Qj),this.lg,V(this))):new Gp(new cv(this,Y6(this,this.ki),new cv(this,Y6(this,this.ki),this.ki,V(this)),V(this)),new cv(this,Y6(this,this.Ak),new cv(this,Y6(this,this.Ak),this.Ak,V(this)),V(this)), new cv(this,Y6(this,this.Ak),new cv(this,Y6(this,this.Ak),this.lg,V(this)),V(this)),new cv(this,Y6(this,this.Qj),new cv(this,Y6(this,this.Qj),this.lg,V(this)),V(this)));if(null!==Zh)var Ei=new Gp(Zh.Uj,Zh.oj,Zh.oi,Zh.Xi);else throw new w(Zh);var Yd=Ei.Uj,bf=Ei.oj,rf=Ei.oi,Cg=Ei.Xi;tp();var nj=G(new H,"true",this.UE),Jh=G(new H,"false",this.TI),If=new fw(this,new Ep("True"),O().c,V(this)),Hg=G(new H,"True",If),He=new fw(this,new Ep("False"),O().c,V(this)),lj=G(new H,"False",He),Wi=G(new H,"NaN",this.Ak), Oj=G(new H,"document",this.ib),mo=G(new H,"window",this.ib),mm=new cv(this,Y6(this,this.La),this.Qj,V(this)),nm=G(new H,"typeof",mm),dq=new cv(this,Y6(this,this.La),this.Qj,V(this)),Zd=G(new H,"toString",dq),sf=new cv(this,Y6(this,this.La),this.Qj,V(this)),oj=G(new H,"String",sf),al=new cv(this,Y6(this,this.lg),this.lg,V(this)),Ll=G(new H,"not",al),Qm=new cv(this,Y6(this,this.ki),this.ki,V(this)),Rm=G(new H,"succ",Qm),hq=new Qx(this,this.Gd,new cv(this,Y6(this,$k),this.Ck,V(this))),Bn=G(new H,"log", hq),hp=new Qx(this,this.Gd,new cv(this,Y6(this,$k),this.Ck,V(this))),ru=G(new H,"discard",hp),qr=new cv(this,Y6(this,this.ki),this.ki,V(this)),Xs=G(new H,"negate",qr),rr=new cv(this,Y6(this,this.Ak),this.ki,V(this)),iq=G(new H,"round",rr),qo=G(new H,"add",Yd),qm=G(new H,"sub",Yd),jq=G(new H,"mul",Yd),pl=G(new H,"div",Yd),ro=new cv(this,Y6(this,this.ki),this.ki,V(this)),Cn=G(new H,"sqrt",ro),ip=G(new H,"lt",rf),so=G(new H,"le",rf),Dn=G(new H,"gt",rf),sr=G(new H,"ge",rf),kq=G(new H,"slt",Cg),ql=G(new H, "sle",Cg),Ys=G(new H,"sgt",Cg),Sm=G(new H,"sge",Cg),Nl=new cv(this,Y6(this,this.Qj),this.ki,V(this)),jp=G(new H,"length",Nl),lq=new cv(this,Y6(this,this.Qj),new cv(this,Y6(this,this.Qj),this.Qj,V(this)),V(this)),mq=G(new H,"concat",lq),Tm=this.Qj,En=V(this),to=new cv(this,new Sv(this,new Uw(Tm.q,R(),Tm,En),V(this)),this.Qj,V(this)),Fn=G(new H,"join",to),nq=V(this),Um=t().d,kp=t().d,oq=O().c,su=O().c,Gn=new lx(this,1,oq,su,Um,kp,!1,nq),ur=new Qx(this,this.Gd,new cv(this,Y6(this,Gn),new cv(this,Y6(this, Gn),this.lg,V(this)),V(this))),In=G(new H,"eq",ur),Zs=V(this),$s=t().d,pq=t().d,vr=O().c,Vm=O().c,Jn=new lx(this,1,vr,Vm,$s,pq,!1,Zs),wr=new Qx(this,this.Gd,new cv(this,Y6(this,Jn),new cv(this,Y6(this,Jn),this.lg,V(this)),V(this))),at=G(new H,"ne",wr),xr=G(new H,"error",this.ib),lp=this.XI,Kn=this.Gd,qq=t().d,yr=this.La,rq=V(this),sq=new Uw(yr.q,R(),yr,rq),bt=G(new H,qq,sq),tq=t().d,zr=V(this),ct=new Uw(lp.q,R(),lp,zr),Ar=G(new H,tq,ct),uq=O().c,Br=new Qx(this,Kn,new cv(this,new zv(this,new z(bt, new z(Ar,uq)),V(this)),lp,V(this))),Ln=G(new H,",",Br),vq=G(new H,"+",Yd),Cr=G(new H,"-",Yd),tu=G(new H,"*",Yd),uu=G(new H,"+.",bf),dt=G(new H,"-.",bf),vu=G(new H,"*.",bf),Dr=G(new H,"%",Yd),uo=G(new H,"/",bf),Er=G(new H,"\x3c",rf),et=G(new H,"\x3e",rf),ft=G(new H,"\x3c\x3d",rf),gt=G(new H,"\x3e\x3d",rf),Wm=G(new H,"\x3d\x3d",rf),Fr=this.XI,mp=new Ep("Eql"),wu=O().c,ht=new fw(this,mp,new z(Fr,wu),V(this)),wq=new Qx(this,this.Gd,new cv(this,e7(this,ht,Fr),this.lg,V(this))),xq=G(new H,"\x3d\x3d\x3d", wq),Gr=G(new H,"\x3c\x3e",rf),xu=d?new cv(this,e7(this,this.lg,this.lg),this.lg,V(this)):new cv(this,Y6(this,this.lg),new cv(this,Y6(this,this.lg),this.lg,V(this)),V(this)),yu=G(new H,"\x26\x26",xu),Re=d?new cv(this,e7(this,this.lg,this.lg),this.lg,V(this)):new cv(this,Y6(this,this.lg),new cv(this,Y6(this,this.lg),this.lg,V(this)),V(this)),rj=G(new H,"||",Re),ai=V(this),rm=t().d,Nn=t().d,zu=O().c,Au=O().c,Av=new lx(this,1,zu,Au,rm,Nn,!1,ai),oy=new Qx(this,this.Gd,new cv(this,Y6(this,Av),Av,V(this))), Bv=G(new H,"id",oy),Cv=V(this),py=t().d,qy=t().d,Dv=O().c,Ee=O().c,Ca=new lx(this,1,Dv,Ee,py,qy,!1,Cv),Lc=new Qx(this,this.Gd,new cv(this,Y6(this,this.lg),new cv(this,Y6(this,Ca),new cv(this,Y6(this,Ca),Ca,V(this)),V(this)),V(this))),yd=G(new H,"if",Lc),Qe=V(this),Xj=t().d,fl=t().d,Gk=O().c,$n=O().c,$R=new lx(this,1,Gk,$n,Xj,fl,!1,Qe),jga=new Qx(this,0,new Sv(this,new Uw(this,(t(),new L($R)),$R,V(this)),V(this))),kga=G(new H,"emptyArray",jga),lga=V(this),mga=t().d,nga=t().d,oga=O().c,pga=O().c,a$= new lx(this,1,oga,pga,mga,nga,!1,lga),qga=new Ep("Code"),rga=this.ib,sga=O().c,tga=new Qx(this,0,new cv(this,Y6(this,new fw(this,qga,new z(a$,new z(rga,sga)),V(this))),a$,V(this))),uga=G(new H,"run",tga),vga=Y6(this,this.ki),wga=new Ep("Code"),xga=this.ki,yga=this.ib,zga=O().c,Aga=new cv(this,vga,new fw(this,wga,new z(xga,new z(yga,zga)),V(this)),V(this)),Bga=[nj,Jh,Hg,lj,Wi,Oj,mo,nm,Zd,oj,Ll,Rm,Bn,ru,Xs,iq,qo,qm,jq,pl,Cn,ip,so,Dn,sr,kq,ql,Ys,Sm,jp,mq,Fn,In,at,xr,Ln,vq,Cr,tu,uu,dt,vu,Dr,uo,Er,et, ft,gt,Wm,xq,Gr,yu,rj,Bv,yd,kga,uga,G(new H,"Const",Aga)],Cga=J(new K,Bga),Dga=pp(0,Cga);if(d)b$=O().c;else var Ega=this.WI,Fga=Qt(this.WI,new y(ev=>{var m0=Nu(Q(),ev.h());return G(new H,m0,ev.j())})),b$=un(Ega,Fga);this.nP=Dga.bf(b$);this.uP=Aq(tp().Iv,J(new K,"| \x26 ~ neg and or is".split(" ")));this.YE=0}hf.prototype=new t5;hf.prototype.constructor=hf;function raa(a){null===a.SE&&null===a.SE&&(a.SE=new KP(a));return a.SE}function V(a){null===a.TE&&null===a.TE&&(a.TE=new R_(a));return a.TE} function yC(a){null===a.WE&&null===a.WE&&(a.WE=new zC(a));return a.WE}function f7(a){null===a.VE&&null===a.VE&&(a.VE=new sC(a));return a.VE}function ny(a,b,c){var d=b.A();b=hu(b);tx(a);var e=t().d;return jx(new kx,a,d,b,e,c)}function d7(a){var b=t().d;tx(a);var c=t().d;return jx(new kx,a,b,"type",c,!0)}function Y6(a,b){var c=t().d,d=b.ma();b=G(new H,c,new Uw(b.q,R(),b,d));c=O().c;return new zv(a,new z(b,c),V(a))} function e7(a,b,c){var d=t().d,e=b.ma();b=new Uw(b.q,R(),b,e);d=G(new H,d,b);b=t().d;e=c.ma();c=new Uw(c.q,R(),c,e);c=G(new H,b,c);b=O().c;return new zv(a,new z(d,new z(c,b)),V(a))}function ox(a,b,c,d,e,g){return aga(a,b,c,d,e,g).h()} function aga(a,b,c,d,e,g){var h=new y(v=>"\x3d\x3e "+v.h()+" \u2014\u2014\u2014 "+ze(v.j(),"",", ",""));if(a.F){var k=ut(Q(),"| ",a.r)+"Typing type "+wO(Xe(),b).Wr();ff(gf(),k+"\n")}a.r=1+a.r|0;try{if(a.F){var l=ut(Q(),"| ",a.r)+("vars\x3d"+e+" newDefsInfo\x3d")+g;ff(gf(),l+"\n")}var m=c.da,n=Xu().X(),r=G(new H,Sfa(a,b,c,nf(),d,!0,e,n,m,g,c),new rp(n))}finally{a.r=-1+a.r|0}dx(new E(h),a.qa)&&a.F&&(a=""+ut(Q(),"| ",a.r)+h.n(r),ff(gf(),a+"\n"));return r} function g7(a,b,c,d,e){return Rw(a,b,new Iw(c.S,c.Ec,c.hc,c.Ed,c.da,c.Pc,c.Zc,c.Lb,!0,c.tb,c.$a,c.od,c.cb),d,e,!1)} function ty(a,b,c,d,e,g,h){var k=!1,l=null;if(b instanceof No){k=!0;l=b;var m=l.oq,n=l.vu;if(!1===l.ls&&null!==m&&"_"===m.x&&n instanceof fe)return ty(a,n.aa,c,d,e,g,h)}if(k&&(m=l.ls,k=l.oq,l=l.vu,l instanceof fe))return b=l.aa,"_"===k.x&&(h=new Te(new Ue(J(new K,["Illegal definition name: ",""]))),c=[We(Xe(),k.x)],Lw(a,Ye(h,J(new K,c)),k.A(),e)),b=h7(a,m,k.x,b,d,e,g),t(),g=i7(a),k.kt=new L(g),g=k.x,h=new qx(a,b,k),Wx(d,G(new H,g,h)),t(),t(),d=G(new H,k.x,b),new Ud(new L(d));if(b instanceof Gl&&(k= b.Ra,!c)){a:{if(k instanceof z&&(m=k.z,c=k.p,null!==m&&(m.h()instanceof L?(m=O().c,c=null===m?null===c:m.i(c)):c=!1,c))){k="field";break a}c=O().c;k=(null===c?null===k:c.i(k))?"empty tuple":"tuple"}JX(a,We(Xe(),"Useless "+k+" in statement position."),b.A(),e);t();d=new Qx(a,a.Gd,Rw(a,b,d,e,g,h));return new fe(d)}if(b instanceof Pm)return g=Rw(a,b,d,e,g,h),c||(b instanceof vl||b instanceof Dl?JX(a,We(Xe(),"Pure expression does nothing in statement position."),b.A(),e):(h=ux(a,g,jx(new kx,a,Lq(b),"expression in statement position", (tx(a),t().d),(tx(a),!1))),k=a.Ck,c=new y(r=>{fr();var v=Ye(new Te(new Ue(J(new K,["Expression in statement position should have type `unit`."]))),u()),x=t().d;v=G(new H,v,x);x=Ye(new Te(new Ue(J(new K,["Use the `discard` function to discard non-unit values, making the intent clearer."]))),u());var A=t().d;x=G(new H,x,A);e.n(hr(0,new z(v,new z(x,r.vt())),a.$c,lu()))}),b=jx(new kx,a,b.A(),hu(b),(tx(a),t().d),(tx(a),!1)),m=Sw(a).ob,Tw(a,h,k,c,b,d,m))),t(),new fe(g);d=new Te(new Ue(J(new K,["Illegal position for this ", " statement."])));g=[We(Xe(),b.jb())];Lw(a,Ye(d,J(new K,g)),b.A(),e);t();d=t().d;return new Ud(d)} function h7(a,b,c,d,e,g,h){var k=jx(new kx,a,d.A(),"binding of "+hu(d),(tx(a),t().d),(tx(a),!1));if(b){var l=V(a);b=t().d;t();var m=new L(c),n=O().c,r=O().c;l=new lx(a,1+e.da|0,n,r,b,m,!0,l);b=new qx(a,l,new vl(c));Wx(e,G(new H,c,b));t();c=new vl(c);var v=new Iw(e.S,e.Ec,e.hc,e.Ed,e.da,e.Pc,e.Zc,e.Lb,e.yc,e.tb,e.$a,new L(c),e.cb);c=1+v.da|0;b=Hw();m=Su();n=op().ga;b=b.Hd(new Uu(m,n));c=new Iw(v.S,v.Ec,v.hc,v.Ed,c,v.Pc,v.Zc,v.Lb,v.yc,v.tb,v.$a,v.od,b);t();d=Rw(a,d,c,g,h,a.rP);h=Sw(a).ob;Tw(a,d,l,g, k,c,h);wy(l,(t(),new L(d)));d=c.cb;up(tp(),v.S.li||c.cb.b());if(!d.b()){h=v.S.qa;c=v.S;c.F&&(b=ut(Q(),"| ",c.r)+"UNSTASHING... (out)",ff(gf(),b+"\n"));c.r=1+c.r|0;try{d.Ca(new y(A=>{if(null!==A){var B=A.h();for(A=A.j().m();A.s();){var C=A.t();a:{if(null!==C){var D=C.j();if(!0===C.Rc()){C=Sw(v.S).ob;Tw(v.S,D,B,g,k,v,C);break a}}if(null!==C&&(D=C.j(),!1===C.Rc())){C=Sw(v.S).ob;Tw(v.S,B,D,g,k,v,C);break a}throw new w(C);}}}else throw new w(A);}));d.mg();var x=void 0}finally{c.r=-1+c.r|0}dx(new E(h), c.qa)&&c.F&&(d=""+ut(Q(),"| ",c.r)+h.n(x),ff(gf(),d+"\n"))}d=l}else if(x=1+e.da|0,c=Hw(),b=Su(),m=op().ga,c=c.Hd(new Uu(b,m)),x=new Iw(e.S,e.Ec,e.hc,e.Ed,x,e.Pc,e.Zc,e.Lb,e.yc,e.tb,e.$a,e.od,c),d=Rw(a,d,x,g,h,!0),h=x.cb,up(tp(),e.S.li||x.cb.b()),!h.b()){x=e.S.qa;c=e.S;c.F&&(b=ut(Q(),"| ",c.r)+"UNSTASHING... (out)",ff(gf(),b+"\n"));c.r=1+c.r|0;try{h.Ca(new y(((A,B)=>C=>{if(null!==C){var D=C.h();for(C=C.j().m();C.s();){var F=C.t();a:{if(null!==F){var I=F.j();if(!0===F.Rc()){F=Sw(A.S).ob;Tw(A.S,I,D, g,k,B,F);break a}}if(null!==F&&(I=F.j(),!1===F.Rc())){F=Sw(A.S).ob;Tw(A.S,D,I,g,k,B,F);break a}throw new w(F);}}}else throw new w(C);})(e,e))),h.mg(),l=void 0}finally{c.r=-1+c.r|0}dx(new E(x),c.qa)&&c.F&&(h=""+ut(Q(),"| ",c.r)+x.n(l),ff(gf(),h+"\n"))}return new Qx(a,e.da,d)}function ux(a,b,c){if(a.sP){var d=b.ma();return Ot(new E(d),c)?b:new OA(a,b,c)}return b}function W6(a,b,c,d,e){return Rw(a,b,c,d,e,!1)} function j7(a,b,c,d){if(b.Lb){var e=d7(a),g=t().d,h=t().d,k=O().c,l=O().c,m=new lx(a,b.da,k,l,g,h,!1,e);e=fY(c);c=Tea(c,m);g=new y(n=>{if(n instanceof Ff){var r=Ey(a),v=new y(()=>{}),x=V(a),A=Sw(a).ob;Tw(a,r,m,v,x,b,A)}d.n(n)});h=V(a);k=Sw(a).ob;Tw(a,e,c,g,h,b,k);gY(b,m)}} function Rw(a,b,c,d,e,g){return Lx(a,new U(()=>c.da+". Typing "+(c.yc?"pattern":"term")+" "+b),new U(()=>{var h=ny(a,b,!1),k=!1,l=null,m=!1,n=null,r=!1,v=null,x=!1,A=null,B=!1,C=null,D=!1,F=null,I=!1,M=null,N=!1,P=null;if(b instanceof vl&&(k=!0,l=b,"_"===l.x)){if(c.yc){var T=jx(new kx,a,l.A(),"wildcard",(tx(a),t().d),(tx(a),!1)),Y=t().d,Z=t().d,S=O().c,ea=O().c;return new lx(a,c.da,S,ea,Y,Z,!1,T)}return Lw(a,Ye(new Te(new Ue(J(new K,["Widlcard in expression position."]))),u()),l.A(),d)}if(b instanceof Ml){var ia=b.Ml,X=Rw(a,b.gs,c,d,e,g);Q6(a,X,a.RE,a.Ck,c,d,h);return Rw(a,ia,c,d,e,g)}if(b instanceof Cl){m=!0;n=b;var sa=n.ai,Ja=n.Fm;if(sa instanceof vl){var Xa=tC(f7(a),sa,c,d);if(!Xa.b()){var Fa=Xa.o(),za=new Iw(c.S,c.Ec,c.hc,c.Ed,c.da,c.Pc,c.Zc,c.Lb,!1,c.tb,c.$a,c.od,c.cb),Qa=nf(),Ma=ox(a,Ja,za,d,e,Qa);jx(new kx,a,a.$E?sa.A():t().d,"variable",(tx(a),t().d),(tx(a),!1));var Ga=c.hc.U(Fa);if(Ga instanceof L)return Lw(a,We(Xe(),"Duplicate use of annotated pattern variable "+Fa),sa.A(),d);if(t().d=== Ga){var ab=new qx(a,Ma,sa);Wx(c,G(new H,Fa,ab));return Ma}throw new w(Ga);}}}if(m){var Hb=n.Fm,bc=Rw(a,n.ai,c,d,e,!0),yb=new Iw(c.S,c.Ec,c.hc,c.Ed,c.da,c.Pc,c.Zc,c.Lb,!1,c.tb,c.$a,c.od,c.cb),tb=nf(),eb=ox(a,Hb,yb,d,e,tb);if(c.yc){var kb=Sw(a).ob;KX(a,bc,eb,d,h,c,kb);return eb}return Q6(a,bc,eb,eb,c,d,h)}if(k){var Rb=tC(f7(a),l,c,d);if(!Rb.b()){var Gb=Rb.o(),vb=jx(new kx,a,a.$E?l.A():t().d,"variable",(tx(a),t().d),(tx(a),!1)),Tb=c.hc.U(Gb),Nb=new N_(a,l);if(Tb.b())cb=R();else var ic=new Wr(Nb),Va= Tb.o(),cb=Vr(ic,Va);var zb=new U((Ca=>()=>{var Lc=new lx(a,c.da,O().c,O().c,t().d,zy(ju(),a.F,new U(()=>Gb)),(TP(a),!1),vb);t();var yd=i7(a);Ca.kt=new L(yd);yd=new qx(a,Lc,Ca);Wx(c,G(new H,Gb,yd));return Lc})(l));return cb.b()?Es(zb):cb.o()}}if(k){var Ub=xC(yC(a),l,d);if(!Ub.b()){var jb=Ub.k,db=uC(c,c,jb),ub=new U(()=>Lw(a,We(Xe(),"identifier not found: "+jb),b.A(),d)),Aa=new y(Ca=>{if(Ca instanceof qx){Ca=Ca.tp;if(c.Lb){var Lc=eY(c,jb),yd=new y(Qe=>{gY(c,Qe)});Lc.b()||yd.n(Lc.o())}return Ca}if(Ca instanceof VP){if(Ca instanceof ix){Lc=Ca.kh;if(Lc instanceof bx)return Lc.Nn();if(Lc instanceof Vw)return Lc.ig.ra;if(Lc instanceof Yw)return R6(a,Lc.Rf,b,d),Dy(Lc.nc,!1,h.Ga,Lc.Rf,Lc.Sm,Lc.gh,Lc.Ij,Lc.gl,0,d);if(Lc&&Lc.$classData&&Lc.$classData.rb.Rs)return Ca=new Te(new Ue(J(new K,[""," "," cannot be used in term position"]))),Lc=[We(Xe(),Lc.fd().ld),We(Xe(),Lc.Ua())],Lw(a,Ye(Ca,J(new K,Lc)),h.Ga,d);throw new w(Lc);}if(Ca instanceof fx)return R6(a,Ca.Ab,b,d),Cy(Ca,!1,h.Ga,d)}throw new w(Ca);}),va=db.b()? Es(ub):Aa.n(db.o());return ux(a,va,h)}}if(b instanceof Dl)return new Mu(a,b,a.$c?yq(b):fq(b),h);if(b instanceof am){Lw(a,We(Xe(),"Illegal use of `super`"),b.A(),d);var Ra=new vl("super"),rb=b.A();return Rw(a,Cq(Ra,rb),c,d,e,g)}if(b instanceof Pl){r=!0;v=b;var xb=v.Za;if(xb instanceof vl){var mc=xb.x;"neg"!==mc&&"~"===mc}}if(r){var Ha=v.Za;if(Ha instanceof Pl){var Ka=Ha.Za;Ka instanceof vl&&"|"===Ka.x}}if(r){var Oa=v.Za;if(Oa instanceof Pl){var Na=Oa.Za;Na instanceof vl&&"\x26"===Na.x}}if(b instanceof yl){var Da=b.ll,ta=jx(new kx,a,b.A(),"record literal",(tx(a),t().d),(tx(a),!1));ET(Da,new y(Ca=>Ca.h().x),new y(Ca=>Ca.h())).Ca(new y(Ca=>{if(null!==Ca&&(Ca=new L(Ca),!Ca.b())){var Lc=Ca.k.h();Ca=Ca.k.j();if(0{var Xj=Ye(new Te(new Ue(J(new K,["Declared at"]))),u());Qe=Qe.A();return G(new H,Xj,Qe)}));return ay(a, new z(Lc,Ca),d)}}}));return SA(xv(a),Qt(Da,new y(Ca=>{if(null!==Ca){var Lc=Ca.h(),yd=Ca.j();if(null!==yd){var Qe=yd.yb;yd=yd.ya;if(null!==Qe){Qe=Qe.je;hB(ve(),Lc.x)&&Lw(a,Ye(new Te(new Ue(J(new K,["Field identifiers must start with a small letter"]))),u()),b.A(),d);Ca=Rw(a,yd,c,d,e,!0);yd=jx(new kx,a,(new Pl(Lc,yd)).A(),(Qe?"mutable ":"")+"record field",(tx(a),t().d),(tx(a),!1));if(Qe){Qe=t().d;t();var Xj=new L(Lc.x),fl=O().c,Gk=O().c;Qe=new lx(a,c.da,fl,Gk,Qe,Xj,!1,yd);Ca=Q6(a,Ca,Qe,Qe,c,d,h);return G(new H, Lc,new Uw(a,new L(Ca),Ca,yd))}return G(new H,Lc,new Uw(Ca.q,R(),Ca,yd))}}}throw new w(Ca);})),ta)}b instanceof Gl&&(x=!0,A=b);if(x){var Ya=A.Ra,dc=Qt(bga(Ya,Ya,new y(Ca=>{if(null!==Ca){var Lc=Ca.h(),yd=Ca.j();if(null!==yd){var Qe=yd.yb,Xj=yd.ya;if(Lc instanceof L&&(yd=Lc.k,c.yc)){Ca=new Cl(yd,nx(Xj,d));yd=yd.A();var fl=new U(()=>Xj.A()),Gk=new y($n=>{$n=Kt($n,Xj.A());return it().n($n)});yd=yd.b()?Es(fl):Gk.n(yd.o());return G(new H,Lc,new sm(Qe,Cq(Ca,yd)))}return Ca}}throw new w(Ca);})),new y(Ca=> {if(null!==Ca){var Lc=Ca.h(),yd=Ca.j();if(null!==yd){var Qe=yd.yb;yd=yd.ya;if(null!==Qe){Ca=Qe.je;if(Qe.Bh){Qe=Ye(new Te(new Ue(J(new K,["Cannot use `val` in this position"]))),u());cu();var Xj=Lc.ha();Lw(a,Qe,du(0,new z(yd,Xj)),d)}Qe=Rw(a,yd,c,d,e,!0);yd=jx(new kx,a,yd.A(),(Ca?"mutable ":"")+"tuple field",(tx(a),t().d),(tx(a),!1));if(Ca){Ca=t().d;Xj=new y($n=>$n.x);Xj=Lc.b()?R():new L(Xj.n(Lc.o()));var fl=O().c,Gk=O().c;Ca=new lx(a,c.da,fl,Gk,Ca,Xj,!1,yd);Qe=Q6(a,Qe,Ca,Ca,c,d,h);return G(new H,Lc, new Uw(a,new L(Qe),Qe,yd))}return G(new H,Lc,new Uw(Qe.q,R(),Qe,yd))}}}throw new w(Ca);}));a:{var ka=O().c;if(null===ka?null===Ya:ka.i(Ya))var ya=!0;else{if(Ya instanceof z){var Sa=Ya.z,xc=Ya.p;if(null!==Sa){var Sb=Sa.h();if(t().d===Sb)var uc=O().c,Lb=null===uc?null===xc:uc.i(xc);else Lb=!1;if(Lb){ya=!0;break a}}}ya=!1}}return new zv(a,dc,ya?V(a):jx(new kx,a,b.A(),"tuple literal",(tx(a),t().d),(tx(a),!1)))}if(b instanceof Vl){var lc=b.mp,Xb=W6(a,b.lp,c,d,e),ec=W6(a,lc,c,d,e);Q6(a,ec,a.ki,a.La,c,d, h);var Ab=t().d,Ob=t().d,fb=O().c,Wa=O().c,bb=new lx(a,c.da,fb,Wa,Ab,Ob,!1,h),Ia=rA(bb),Ua=new fw(a,new Ep("undefined"),O().c,V(a)),pc=jx(new kx,h.Wu,h.Ga,"prohibited undefined element",h.go,h.Zm),sc=NA(Ua,pc,!1);KA(bb,new z(sc,Ia));var Ba=jx(new kx,a,lc.A(),"array element",(tx(a),t().d),(tx(a),!1)),ob=Q6(a,Xb,new Sv(a,new Uw(bb.q,R(),bb,Ba),h),bb,c,d,h),nc=new Ep("undefined"),Ib=O().c,vc=new fw(a,nc,Ib,jx(new kx,h.Wu,h.Ga,"possibly-undefined array access",h.go,h.Zm)),Vb=V(ob.q);return dv(ob,vc,Vb, !1)}if(b instanceof dm){var fc=b.Zq,Bc=W6(a,b.$q,c,d,e);Q6(a,Bc,a.lg,a.Ck,c,d,h);var Pb=new Gm(!0),Jb=O().c;return Rw(a,new Sl(new z(fc,new z(Pb,Jb))),c,d,e,g)}if(b instanceof Wl){B=!0;C=b;var gc=C.Gm,Cb=C.Qn;if(gc instanceof Ql){var cc=gc.Vl,yc=gc.ml,Mc=W6(a,cc,c,d,e),qc=jx(new kx,a,gc.A(),"assigned selection",(tx(a),t().d),(tx(a),!1)),oc=t().d;t();var Qc=yc.x,jc=zy(0,!(0<=Qc.length&&"_"===Qc.substring(0,1)),new U(()=>yc.x)),sb=O().c,Gc=O().c,Wb=new lx(a,c.da,sb,Gc,oc,jc,!1,qc),Cc=ux(a,Mc,jx(new kx, a,Lq(cc),"receiver",(tx(a),t().d),(tx(a),!1))),Fc=xv(a),qd=G(new H,yc,new Uw(a,new L(Wb),a.La,jx(new kx,a,yc.A(),"assigned field",(tx(a),t().d),(tx(a),!1)))),Yb=O().c;Q6(a,Cc,SA(Fc,new z(qd,Yb),qc),Wb,c,d,h);var Nc=W6(a,Cb,c,d,e),ad=a.Ck;return Q6(a,Nc,Wb,ux(ad.q,ad,h),c,d,h)}}if(B){var Uc=C.Gm,cd=C.Qn;if(Uc instanceof Vl){var kc=Uc.lp,Vc=Uc.mp,Hc=W6(a,kc,c,d,e),rc=jx(new kx,a,Uc.A(),"assigned array element",(tx(a),t().d),(tx(a),!1)),sd=t().d,Kc=t().d,Qd=O().c,Ad=O().c,kd=new lx(a,c.da,Qd,Ad,sd,Kc, !1,rc),Hd=ux(a,Hc,jx(new kx,a,Lq(kc),"receiver",(tx(a),t().d),(tx(a),!1)));Q6(a,Hd,new Sv(a,new Uw(a,new L(kd),kd,rc),h),a.La,c,d,h);var Rd=W6(a,Vc,c,d,e);Q6(a,Rd,a.ki,a.La,c,d,h);var Bd=W6(a,cd,c,d,e),ae=a.Ck;return Q6(a,Bd,kd,ux(ae.q,ae,h),c,d,h)}}if(B){var dd=C.Gm,od=C.Qn;if(dd instanceof vl){var Ta=Rw(a,od,c,d,e,g),wb=!1,$a=null,wa=uC(c,c,dd.x);if(wa instanceof L){wb=!0;$a=wa;var hb=$a.k;if(hb instanceof qx){var ra=a.Ck;return Q6(a,Ta,hb.tp,ux(ra.q,ra,h),c,d,h)}}if(wb){var wc=$a.k;if(wc instanceof ix){var ac=wc.kh;if(ac instanceof bx){S6(a,ac.Mb,h,d);var Id=ac.Nn(),ud=a.Ck;return Q6(a,Ta,Id,ux(ud.q,ud,h),c,d,h)}}}if(wb){var be=$a.k;if(be instanceof fx){null===a.Qq&&null===a.Qq&&(a.Qq=new BC(a));t();var re=new L(be.Ab);if(!re.b()){var pe=re.k;if(pe instanceof Zn){S6(a,pe,h,d);var bd=Ux(be),Rc=a.Ck;return Q6(a,Ta,bd,ux(Rc.q,Rc,h),c,d,h)}}}}var Wc=Ye(new Te(new Ue(J(new K,["Illegal assignment"]))),u()),Wd=G(new H,Wc,h.Ga),zd=new Te(new Ue(J(new K,["cannot assign to ",""]))),Pa=[We(Xe(),hu(dd))], Db=Ye(zd,J(new K,Pa)),Oc=dd.A(),Tc=G(new H,Db,Oc),Sd=O().c;return ay(a,new z(Wd,new z(Tc,Sd)),d)}}if(B){var Jc=C.Gm,vd=Ye(new Te(new Ue(J(new K,["Illegal assignment"]))),u()),hd=G(new H,vd,h.Ga),de=new Te(new Ue(J(new K,["cannot assign to ",""]))),ye=[We(Xe(),hu(Jc))],jf=Ye(de,J(new K,ye)),af=Jc.A(),pf=G(new H,jf,af),kf=O().c;return ay(a,new z(hd,new z(pf,kf)),d)}if(b instanceof Fl){D=!0;F=b;var Be=F.gg;if(!1===F.bi&&Be instanceof Sl)return Rw(a,Be,c,d,e,g)}if(D)return Rw(a,F.gg,c,d,e,g);if(b instanceof Sl){I=!0;M=b;var Kd=M.Dj;if(Kd instanceof z){var ld=Kd.z,Jd=Kd.p;if(ld instanceof Pm){var Dd=O().c;if(null===Dd?null===Jd:Dd.i(Jd))return Rw(a,ld,c,d,e,g)}}}if(I){var Xd=M.Dj,Yc=O().c;if(null===Yc?null===Xd:Yc.i(Xd)){var Ce=a.Ck;return ux(Ce.q,Ce,h)}}if(c.yc){var te=new Te(new Ue(J(new K,["Unsupported pattern shape",":"]))),Ie=[a.F?We(Xe()," ("+ca(b).u()+")"):We(Xe(),"")];return Lw(a,Ye(te,J(new K,Ie)),b.A(),d)}if(b instanceof Ol){N=!0;P=b;var Jf=P.Ej,df=P.Fj;if(Pe(new E(g),!0)){Nx(a,new U(()=>"TYPING POLY LAM")); var vg=c.Lb?iY(c):tf(c),wg=new y(Ca=>{var Lc=g7(a,Jf,Ca,d,e),yd=Rw(a,df,Ca,d,e,a.VI||Pe(new E(g),!0)&&a.li);j7(a,c,Ca,d);return new cv(a,Lc,yd,jx(new kx,a,b.A(),"function",(tx(a),t().d),(tx(a),!1)))});Sw(vg.S);return Px(vg,wg,d,h)}}if(N){var xg=P.Ej,eg=P.Fj,vh=c.Lb?iY(c):tf(c),fg=g7(a,xg,vh,d,e);up(tp(),!Pe(new E(g),!0));var ih=Rw(a,eg,vh,d,e,a.VI||Pe(new E(g),!0));j7(a,c,vh,d);return new cv(a,fg,ih,jx(new kx,a,b.A(),"function",(tx(a),t().d),(tx(a),!1)))}if(b instanceof Yl){var Ig=new Yl(b.hp),Tf= new Gl(O().c),Jg=b.A(),jh=new y(Ca=>new Kq(Ca.eh,Ca.eh,Ca.dh)),yg=Jg.b()?R():new L(jh.n(Jg.o()));return W6(a,new Pl(Ig,Cq(Tf,yg)),c,d,e)}if(r){var gg=v.Za,Cf=v.Qb;if(gg instanceof Yl){var Uf=gg.hp;Uf instanceof Il&&Lw(a,Ye(new Te(new Ue(J(new K,["Type arguments in `new` expressions are not yet supported"]))),u()),h.Ga,d);var $g=nx(Uf,d),Ah=nf(),Kg=ox(a,$g,c,d,e,Ah),Vf=!1,hg=null,zg=DB(Kg);a:if(zg instanceof fw)var Lg=T6(a,zg.qb.V,c,b,d,h);else{if(zg instanceof Mu){Vf=!0;hg=zg;var Mg=hg.pd,Wf=a.Bk; if(null===Wf?null===Mg:Wf.i(Mg)){Lg=hg;break a}}if(Vf){var Ng=hg.pd;if(Ng instanceof vl){Lg=T6(a,Ng.x,c,b,d,h);break a}}var Kf=new Te(new Ue(J(new K,["Unexpected type `","` after `new` keyword"]))),xf=[wO(Xe(),AD(Kg,c))],Og=Ye(Kf,J(new K,xf)),mi=Uf.A(),Ci=G(new H,Og,mi),Xh=O().c;Lg=ay(a,new z(Ci,Xh),d)}var wh=t().d,Bh=t().d,ng=O().c,kh=O().c,Kh=new lx(a,c.da,ng,kh,wh,Bh,!1,h),ni=jx(new kx,a,Cf.A(),"argument list",(tx(a),t().d),(tx(a),!1)),Lh=Rw(a,Cf,c,d,e,g);return Q6(a,Lg,new cv(a,ux(Lh.q,Lh,ni), Kh,V(a)),Kh,c,d,h)}}if(r){var lh=v.Za;if(lh instanceof Pl){var Ch=lh.Za;if(Ch instanceof vl&&"is"===Ch.x){var Dh=new Ut(b,new vl("true"));t();var Yh=new vl("false"),ah=new Xl(Dh,new L(Yh));b.oc=(t(),new L(ah));return Rw(a,ah,c,d,e,g)}}}if(r){var oi=v.Za;if(oi instanceof vl&&"is"===oi.x){var mj=new Ut(b,new vl("true"));t();var wd=new vl("false"),ge=new Xl(mj,new L(wd));b.oc=(t(),new L(ge));return Rw(a,ge,c,d,e,g)}}if(r){var De=v.Za,qf=v.Qb;if(De instanceof Pl){var og=De.Za,Xf=De.Qb;if(og instanceof vl&&"and"===og.x&&null!==Xf){var mh=mz(eu(),Xf);if(!mh.b()&&null!==mh.o()&&0===mh.o().ab(1)){var Ag=mh.o(),Bg=eB(Ag,0);if(null!==qf){var Eh=mz(eu(),qf);if(!Eh.b()&&null!==Eh.o()&&0===Eh.o().ab(1)){var Pg=Eh.o(),Di=eB(Pg,0),Mh=new Ut(Bg,Di);t();var pi=new vl("false"),Xi=new Xl(Mh,new L(pi));b.oc=(t(),new L(Xi));return Rw(a,Xi,c,d,e,g)}}}}}}if(r){var Qg=v.Za,nh=v.Qb;if(Qg instanceof vl&&"and"===Qg.x&&null!==nh){var bh=mz(eu(),nh);if(!bh.b()&&null!==bh.o()&&0===bh.o().ab(2)){var Mj=bh.o(),Nj=eB(Mj,0), ie=bh.o(),Ac=eB(ie,1),Ve=new Ut(Nj,Ac);t();var Td=new vl("false"),lf=new Xl(Ve,new L(Td));b.oc=(t(),new L(lf));return Rw(a,lf,c,d,e,g)}}}if(r){var Yi=v.Za,Jl=v.Qb;if(null!==Yi&&Jl instanceof Gl&&Jl.Ra.qo(new y(Ca=>!Ca.h().b()))){var ll=Rw(a,Yi,c,d,e,g),Bj=Tfa(a,ll,c),$k=!1,Zh=null;if(Bj instanceof z){$k=!0;Zh=Bj;var Ei=Zh.z,Yd=Zh.p;if(null!==Ei){var bf=Ei.Nb;if(bf instanceof zv){var rf=bf.Yb,Cg=O().c;if(null===Cg?null===Yd:Cg.i(Yd)){if(rf.qo(new y(Ca=>Ca.h().b())))return Lw(a,We(Xe(),"Cannot use named arguments as the function type has untyped arguments"), Jl.A(),d);var nj=Qt(rf,new y(Ca=>{Ca=Ca.h();if(Ca instanceof L)return Ca.k;if(t().d===Ca)xm("Program reached and unexpected state.");else throw new w(Ca);}));return cga(a,b,Yi,Jl,nj,ll,c,d,e)}}}}if($k&&Zh.p instanceof z){var Jh=new Te(new Ue(J(new K,["More than one function signature found in type `","` for function call with named arguments"]))),If=[wO(Xe(),AD(ll,c))];return Lw(a,Ye(Jh,J(new K,If)),Yi.A(),d)}a:{var Hg=O().c;if(null===Hg?null===Bj:Hg.i(Bj))var He=!0;else{if(Bj instanceof z){var lj= Bj.p,Wi=O().c;if(null===Wi?null===lj:Wi.i(lj)){He=!0;break a}}He=!1}}if(He){var Oj=new Te(new Ue(J(new K,["Cannot retrieve appropriate function signature from type `","` for applying named arguments"]))),mo=[wO(Xe(),AD(ll,c))];return Lw(a,Ye(Oj,J(new K,mo)),Yi.A(),d)}throw new w(Bj);}}if(r){var mm=v.Za,nm=v.Qb,dq=W6(a,mm,c,d,e);if(nm instanceof Gl){var Zd=nm.Ra,sf=Qt(Zd,new y(Ca=>{if(null!==Ca){var Lc=Ca.h(),yd=Ca.j();if(null!==yd){var Qe=yd.ya;if(null!==yd.yb)return Ca=jx(new kx,a,Qe.A(),"argument", (tx(a),t().d),(tx(a),!1)),Qe=V6(a,Qe,c,d,e,h),G(new H,Lc,new Uw(Qe.q,R(),Qe,Ca))}}throw new w(Ca);}));a:{var oj=O().c;if(null===oj?null===Zd:oj.i(Zd))var al=!0;else{if(Zd instanceof z){var Ll=Zd.z,Qm=Zd.p;if(null!==Ll){var Rm=Ll.h();if(t().d===Rm)var hq=O().c,Bn=null===hq?null===Qm:hq.i(Qm);else Bn=!1;if(Bn){al=!0;break a}}}al=!1}}var hp=new zv(a,sf,al?V(a):jx(new kx,a,nm.A(),"argument list",(tx(a),t().d),(tx(a),!1)))}else hp=V6(a,nm,c,d,e,h);var ru=t().d,qr=t().d,Xs=O().c,rr=O().c,iq=new lx(a,c.da, Xs,rr,ru,qr,!1,h),qo=ux(a,hp,jx(new kx,a,Lq(nm),"argument",(tx(a),t().d),(tx(a),!1))),qm=jx(new kx,a,Lq(mm),"applied expression",(tx(a),t().d),(tx(a),!1)),jq=ux(a,dq,qm);return Q6(a,jq,new cv(a,qo,iq,h),iq,c,d,h)}if(b instanceof Ql){var pl=b.Vl,ro=b.ml,Cn=!1,ip=null;if(pl instanceof vl){Cn=!0;ip=pl;var so=ip.x;if(hB(ve(),so)&&c.tb.L(so)){var Dn=hY(c,(t(),new L(so)),ro.x);if(Dn instanceof L){var sr=Dn.k.maa();return TA(sr,c)}if(t().d===Dn){var kq=new Te(new Ue(J(new K,["Class "," has no method ",""]))), ql=[We(Xe(),so),We(Xe(),ro.x)];Lw(a,Ye(kq,J(new K,ql)),b.A(),d);return X6(a,pl,ro,c,b,d,e,h)}throw new w(Dn);}}if(Cn){var Ys=ip.x;if("unapply"===ro.x){var Sm=!1,Nl=null,jp=uC(c,c,Ys);a:{if(jp instanceof L){Sm=!0;Nl=jp;var lq=Nl.k;if(lq instanceof ix){var mq=lq.kh;if(mq instanceof Yw){var Tm=Cp(mq.Rf);break a}}}if(Sm){var En=Nl.k;if(En instanceof fx){Tm=Cp(En.Ab);break a}}Tm=t().d}if(Tm instanceof L){var to=Tm.k;if(null!==to){var Fn=to.Yc;if(Fn instanceof fe)return Rw(a,Fn.aa,c,d,e,!0)}}}}return X6(a, pl,ro,c,b,d,e,h)}if(b instanceof Rl){var nq=b.ep,Um=b.Zn,kp=b.$n,oq=b.Mm;if(c.Lb){var su=Rw(a,kp,c,d,e,g),Gn=iY(c),ur=Um.x,In=new qx(a,su,Um);Wx(Gn,G(new H,ur,In));var Zs=Rw(a,oq,Gn,d,e,g);j7(a,c,Gn,d);return Zs}if(a.$c&&!nq){var $s=Rw(a,kp,c,d,e,g),pq=tf(c),vr=Um.x,Vm=new qx(a,$s,Um);Wx(pq,G(new H,vr,Vm));return Rw(a,oq,pq,d,e,g)}var Jn=h7(a,nq,Um.x,kp,c,d,e),wr=tf(c),at=Um.x,xr=new qx(a,Jn,Um);Wx(wr,G(new H,at,xr));return Rw(a,oq,wr,d,e,g)}if(I){var lp=M.Dj;if(a.$c){var Kn=new Dt(lp);t();var qq= of(a,Kn,new L(M),c,d,e).Ul,yr=new U(()=>a.Ck);return qq.b()?Es(yr):qq.o()}return k7(a,lp,!1,O().c,!1,tf(c),d,h,e,g)}if(b instanceof Tl){var rq=b.ar,sq=W6(a,b.br,c,d,e),bt=W6(a,rq,c,d,e),tq=rq.ll.m(),zr=new Ef(tq,new y(Ca=>Ca.h())),ct=Su(),Ar=op().ga,uq=new Uu(ct,Ar),Br=oA(uv(),zr,uq),Ln=iB(sq,Br);return Pu(Ln,bt,h,!1)}if(b instanceof Ul){var vq=b.Sn,Cr=b.Hm,tu=c.Lb?iY(c):c;return(new y(Ca=>{var Lc=W6(a,vq,Ca,d,e);if(a.$c){var yd=a.mP;Q6(a,Lc,ux(yd.q,yd,h),a.La,Ca,d,h)}yd=l7(a,Wk(new O_(a)).Ob(vq, new y(()=>{t();return R()})),Cr,Ca,d,e,g);if(null!==yd)var Qe=G(new H,yd.h(),yd.j());else throw new w(yd);yd=Qe.h();Qe=Qe.j();j7(a,c,Ca,d);yd=yd.mf(a.ib,new fn((Xj,fl)=>{var Gk=G(new H,Xj,fl);fl=Gk.y;Xj=Gk.w;if(null!==fl){Gk=fl.h();fl=fl.j();var $n=V(Gk.q);fl=Pu(Gk,fl,$n,!1);$n=V(Gk.q);Gk=NA(Gk,$n,!1);$n=V(Xj.q);Xj=Pu(Xj,Gk,$n,!1);Gk=V(fl.q);return dv(fl,Xj,Gk,!1)}throw new w(Gk);}));return Q6(a,Lc,yd,Qe,Ca,d,h)})).n(tu)}if(b instanceof Xl)try{return Rw(a,Ffa(a,b,c,d),c,d,e,g)}catch(Ca){if(Ca instanceof jT)return ay(a,Ca.VP,d);throw Ca;}if(b instanceof Il){var uu=b.nl;Lw(a,Ye(new Te(new Ue(J(new K,["Type application syntax is not yet supported"]))),u()),b.A(),d);return Rw(a,uu,c,d,e,g)}if(b instanceof $l)return k7(a,Xq(b.lt,b.Yq),!1,O().c,!0,c,d,h,e,g);if(b instanceof Zl){var dt=b.qq,vu=b.$o,Dr=new y(Ca=>{var Lc=Qt(dt,new y(yd=>{if(null!==yd){var Qe=yd.pp;if(Qe instanceof Ud){Qe=Qe.fa;var Xj=jx(new kx,a,yd.A(),"quantified type variable",(tx(a),t().d),(tx(a),!1)),fl=t().d;t();var Gk=new L(Qe),$n= O().c,$R=O().c;yd=new mx(a,new lx(a,Ca.da,$n,$R,fl,Gk,!1,Xj),jx(new kx,a,yd.A(),"rigid type variable",(tx(a),t().d),(tx(a),!1)));return G(new H,Qe,yd)}}xm("Program reached and unexpected state.")}));Lc=e.bf(Lc);return(new y(yd=>W6(a,vu,Ca,d,yd))).n(Lc)});Sw(c.S);return Px(c,Dr,d,h)}if(b instanceof Kl){var uo=Rw(a,b.cp,c,d,e,!0),Er=new GA(!1),et=Vfa(uo,c,Er);if(!Er.Am){var ft=new Te(new Ue(J(new K,["Inferred type `","` of this "," cannot be instantiated"]))),gt=[wO(Xe(),AD(uo,c)),We(Xe(),uo.ma().lh)]; JX(a,Ye(ft,J(new K,gt)),h.Ga,d)}return et}if(b instanceof bm)return Lw(a,Ye(new Te(new Ue(J(new K,["Unexpected equation in this position"]))),u()),b.A(),d);if(b instanceof em){var Wm=b.Iq;if(c.Lb)return Lw(a,Ye(new Te(new Ue(J(new K,["Nested quotation is not allowed."]))),u()),b.A(),d);var Fr=iY(c),mp=Rw(a,Wm,Fr,d,e,g),wu=new Ep("Code"),ht=fY(Fr),wq=O().c;return new fw(a,wu,new z(mp,new z(ht,wq)),jx(new kx,a,b.A(),"code fragment",(tx(a),t().d),(tx(a),!1)))}if(b instanceof fm){var xq=b.jt;if(c.Lb){var Gr= Uea(c),xu=Rw(a,xq,Gr,d,e,g),yu=jx(new kx,a,b.A(),"code fragment body type",(tx(a),t().d),(tx(a),!1)),Re=t().d,rj=t().d,ai=O().c,rm=O().c,Nn=new lx(a,c.da,ai,rm,Re,rj,!1,yu),zu=jx(new kx,a,xq.A(),"code fragment context type",(tx(a),t().d),(tx(a),!1)),Au=t().d,Av=t().d,oy=O().c,Bv=O().c,Cv=new lx(a,c.da,oy,Bv,Au,Av,!1,zu),py=new Ep("Code"),qy=O().c,Dv=Q6(a,xu,new fw(a,py,new z(Nn,new z(Cv,qy)),jx(new kx,a,xq.A(),"unquote body",(tx(a),t().d),(tx(a),!1))),Nn,Gr,d,h);gY(c,Cv);var Ee=jx(new kx,a,b.A(), "unquote",(tx(a),t().d),(tx(a),!1));return ux(Dv.q,Dv,Ee)}return Lw(a,We(Xe(),"Unquotes should be enclosed with a quasiquote."),b.A(),d)}if(b instanceof cm)return Lw(a,Ye(new Te(new Ue(J(new K,["Refinement terms are not yet supported"]))),u()),b.A(),d);throw new w(b);}),new y(h=>c.da+". : "+h))} function l7(a,b,c,d,e,g,h){var k=tc();try{if(zm()===c){var l=O().c;return G(new H,l,a.ib)}if(c instanceof ym){var m=c.dn,n=jx(new kx,a,c.A(),"wildcard pattern",(tx(a),t().d),(tx(a),!1)),r=t().d,v=t().d,x=O().c,A=O().c,B=new lx(a,d.da,x,A,r,v,!1,n),C=d.Lb?iY(d):tf(d);if(b instanceof L){var D=b.k,F=D.x,I=new qx(a,B,D);Wx(C,G(new H,F,I));var M=Rw(a,m,C,e,g,h),N=G(new H,B,a.La),P=O().c,T=G(new H,new z(N,P),M)}else{var Y=G(new H,B,a.La),Z=O().c,S=new z(Y,Z),ea=Rw(a,m,d,e,g,h);T=G(new H,S,ea)}j7(a,d,C, e);return T}if(c instanceof um){var ia=c.Xo,X=c.Tn,sa=c.Un;if(ia instanceof Dl)var Ja=new Mu(a,ia,a.$c?yq(ia):fq(ia),jx(new kx,a,ia.A(),"literal pattern",(tx(a),t().d),(tx(a),!1))),Xa=G(new H,Ja,Ja);else{if(!(ia instanceof vl))throw new w(ia);var Fa=ia.x,za=jx(new kx,a,ia.A(),"type pattern",(tx(a),t().d),(tx(a),!1)),Qa=d.tb.U(Fa);if(R()===Qa){var Ma=()=>{var Yb=new Mu(a,a.Bk,ap(),za),Nc=G(new H,Yb,Yb),ad=O().c;Nc=new z(Nc,ad);throw Hq(new Iq,k,G(new H,Nc,Yb));},Ga=uC(d,d,Fa);a:{if(Ga instanceof L){var ab= Ga.k;if(ab instanceof VP){var Hb=ab.fd();if(dx(new E(Hb),Bp()))var bc=ab.fd(),yb=dx(new E(bc),zp());else yb=!1;if(yb)var tb=ab.fd(),eb=dx(new E(tb),Fp());else eb=!1;eb&&Lw(a,Ye(new Te(new Ue(J(new K,["can only match on classes and traits"]))),u()),ia.A(),e);var kb=jx(new kx,a,ia.A(),"class pattern",(tx(a),t().d),(tx(a),!1)),Rb=!1,Gb=null;if(ab instanceof fx){var vb=ab.Ab;vb instanceof yo||xm("Program reached and unexpected state.");var Tb=bz(a,vb,kb,d),Nb=xv(a),ic=ab.Ag(),Va=Yb=>{if(null!==Yb){var Nc= Yb.kc,ad=Yb.hb;Yb=Yb.Rd;var Uc=ad.ji;t();var cd=new L(ad);ad=ad.kg;var kc=O().c,Vc=O().c;Uc=new lx(a,d.da,kc,Vc,cd,ad,!1,Uc);cd=new vl(Fa+"#"+Nc.V);Nc=Nc.A();return G(new H,Cq(cd,Nc),XD(WD(a),Yb.b()?ou().Yl:Yb.o(),Uc,Uc,V(a)))}throw new w(Yb);};if(ic===u())var cb=u();else{for(var zb=ic.e(),Ub=new z(Va(zb),u()),jb=Ub,db=ic.f();db!==u();){var ub=db.e(),Aa=new z(Va(ub),u());jb=jb.p=Aa;db=db.f()}cb=Ub}var va=SA(Nb,cb,V(a));if(a.F){var Ra=ut(Q(),"| ",a.r)+("Match arm "+Fa+": "+Tb+" \x26 ")+va;ff(gf(), Ra+"\n")}Xa=G(new H,Tb,va);break a}if(ab instanceof ix){Rb=!0;Gb=ab;var rb=Gb.kh;if(rb instanceof Yw){var xb=bz(a,rb.Rf,kb,d),mc=xv(a),Ha=rb.gh,Ka=Yb=>{if(null!==Yb){var Nc=Yb.kc,ad=Yb.hb;Yb=Yb.Rd;var Uc=ad.ji;t();var cd=new L(ad),kc=ad.kg,Vc=O().c,Hc=O().c;Uc=new lx(a,d.da,Vc,Hc,cd,kc,!1,Uc);cd=new vl(Fa+"#"+Nc.V);Nc=Nc.A();return G(new H,Cq(cd,Nc),XD(WD(a),Yb.b()?RA(rb,ad,d):Yb.o(),Uc,Uc,V(a)))}throw new w(Yb);};if(Ha===u())var Oa=u();else{for(var Na=Ha.e(),Da=new z(Ka(Na),u()),ta=Da,Ya=Ha.f();Ya!== u();){var dc=Ya.e(),ka=new z(Ka(dc),u());ta=ta.p=ka;Ya=Ya.f()}Oa=Da}var ya=SA(mc,Oa,V(a));if(a.F){var Sa=ut(Q(),"| ",a.r)+("Match arm "+Fa+": "+xb+" \x26 ")+ya;ff(gf(),Sa+"\n")}Xa=G(new H,xb,ya);break a}}if(Rb){Xa=Ma();break a}throw new w(ab);}}Lw(a,We(Xe(),"type identifier not found: "+Fa),ia.A(),e);Xa=Ma()}}else{if(!(Qa instanceof L))throw new w(Qa);var xc=Qa.k,Sb=xc.jj;if(Ap()===Sb||zp()===Sb||cp()===Sb){var uc=Lw(a,Ye(new Te(new Ue(J(new K,["can only match on classes and traits"]))),u()),ia.A(), e);Xa=G(new H,uc,uc)}else if(Bp()===Sb){var Lb=rD(a,xc,jx(new kx,a,ia.A(),"class pattern",(tx(a),t().d),(tx(a),!1)),d);Xa=G(new H,Lb,Lb)}else if(Fp()===Sb){var lc=TD(a,xc,jx(new kx,a,ia.A(),"trait pattern",(tx(a),t().d),(tx(a),!1)));Xa=G(new H,lc,lc)}else throw new w(Sb);}}if(null===Xa)throw new w(Xa);var Xb=Xa.h(),ec=Xa.j(),Ab=d.Lb?iY(d):tf(d);if(b instanceof L){var Ob=b.k;if(a.$c){var fb=Ob.x,Wa=V(Xb.q),bb=new qx(a,Pu(Xb,ec,Wa,!1),Ob);Wx(Ab,G(new H,fb,bb));var Ia=Rw(a,X,Ab,e,g,h),Ua=new tl(G(new H, Xb,ec),Ia,l7(a,b,sa,d,e,g,h))}else{var pc=jx(new kx,a,Ob.A(),"refined scrutinee",(tx(a),t().d),(tx(a),!1)),sc=t().d,Ba=t().d,ob=O().c,nc=O().c,Ib=new lx(a,d.da,ob,nc,sc,Ba,!1,pc),vc=Ob.x,Vb=new qx(a,Ib,Ob);Wx(Ab,G(new H,vc,Vb));var fc=Rw(a,X,Ab,e,g,h);Ua=new tl(G(new H,ec,Ib),fc,l7(a,b,sa,d,e,g,h))}}else if(t().d===b){var Bc=Rw(a,X,Ab,e,g,h);Ua=new tl(G(new H,Xb,a.La),Bc,l7(a,b,sa,d,e,g,h))}else throw new w(b);a:{if(null!==Ua){var Pb=Ua.kc,Jb=Ua.hb,gc=Ua.Rd;if(null!==gc){var Cb=gc.h(),cc=gc.j();var yc= Pb;var Mc=Jb;var qc=Cb;var oc=cc;break a}}throw new w(Ua);}var Qc=yc,jc=Mc,sb=qc,Gc=oc;j7(a,d,Ab,e);var Wb=new z(Qc,sb),Cc=V(jc.q),Fc=dv(jc,Gc,Cc,!1);return G(new H,Wb,Fc)}throw new w(c);}catch(Yb){if(Yb instanceof Iq){var qd=Yb;if(qd.Qg===k)return qd.Cj();throw qd;}throw Yb;}} function k7(a,b,c,d,e,g,h,k,l,m){var n=!1,r=null;if(b instanceof z){n=!0;r=b;var v=r.z,x=r.p;if(v instanceof vl&&c)return t(),e=new L(v),v=new sm(tm().Cg,v),e=G(new H,e,v),v=O().c,e=new Gl(new z(e,v)),k7(a,new z(e,x),c,d,!1,g,h,k,l,m)}if(n&&(x=r.z,v=r.p,x instanceof Sl))return k7(a,dl(v,x.Dj),c,d,!1,g,h,k,l,m);if(n&&(v=r.z,x=r.p,v instanceof Gl)){v=v.Ra;var A=O().c;if(null===A?null===v:A.i(v))return k7(a,x,c,d,!1,g,h,k,l,m)}if(n&&(v=r.z,x=r.p,v instanceof Gl&&(v=v.Ra,v instanceof z))){var B=v.z;A= v.p;if(null!==B){v=B.h();var C=B.j();if(null!==C&&(B=C.ya,null!==C.yb)){a:{if(B instanceof Fl&&(e=B.gg,!1===B.bi&&g.yc)){e=g7(a,e,g,h,l);break a}e=g.yc&&v.b();e=new Iw(g.S,g.Ec,g.hc,g.Ed,g.da,g.Pc,g.Zc,g.Lb,e,g.tb,g.$a,g.od,g.cb);e=A.b()?Rw(a,new Fl(c,B),e,h,l,m):Rw(a,B,e,h,l,m)}r=!1;n=null;a:{if(v instanceof L&&(r=!0,n=v,b=n.k,g.yc)){n=jx(new kx,a,B.A(),"parameter type",(tx(a),t().d),(tx(a),!1));r=new lx(a,g.da,O().c,O().c,t().d,(TP(a),t().d),(TP(a),!1),n);B=Sw(a).ob;Tw(a,r,e,h,n,g,B);e=b.x;b=new qx(a, r,b);Wx(g,G(new H,e,b));e=r;break a}r&&(r=n.k,b=r.x,r=new qx(a,e,r),Wx(g,G(new H,b,r)))}A=new Gl(A);x=new z(A,x);e=G(new H,v,e);return k7(a,x,c,new z(e,d),!1,g,h,k,l,m)}}}if(n&&(x=r.z,v=r.p,x instanceof Pm&&(A=O().c,null===A?null===v:A.i(v))))return d.b()||JX(a,We(Xe(),"Previous field definitions are discarded by this returned expression."),x.A(),h),Rw(a,x,g,h,l,m);if(n){x=r.p;A=r.z.nv();if(null===A)throw new w(A);v=A.j();for(A=A.h();!A.b();)h.n(A.e()),A=A.f();for(b=A=null;v!==u();){r=v.e();for(r= GP(ty(a,r,e,g,h,l,m)).m();r.s();)n=new z(r.t(),u()),null===b?A=n:b.p=n,b=n;v=v.f()}e=(null===A?u():A).m();v=op().ga;e=new xo(e,v);Jw(g,new Ef(e,new y(D=>{var F=D.h();D=new qx(a,D.j(),new vl(D.h()));return G(new H,F,D)})));return k7(a,x,c,d,!1,g,h,k,l,m)}g=O().c;if(null===g?null===b:g.i(b)){if(c)return c=er(d).m(),c=new Ao(c),c=new Ef(c,new y(D=>{if(null!==D){var F=D.h();if(null!==F){var I=F.h();F=F.j();if(I instanceof L)return D=I.k,I=V(a),F=new Uw(F.q,R(),F,I),G(new H,D,F)}}if(null!==D&&(F=D.h(), I=D.Sc(),null!==F)){var M=F.h();F=F.j();if(t().d===M)return JX(a,We(Xe(),"Missing name for record field"),F.ma().Ga,h),D=new vl("_"+(1+I|0)),I=V(a),G(new H,D,new Uw(F.q,R(),F,I))}throw new w(D);})),Od(),c=Pd(u(),c),SA(xv(a),c,k);lv();c=er(d).m();return new zv(a,ry(0,c,new y(D=>{var F=V(a);return new Uw(D.q,R(),D,F)})),k)}throw new w(b);} function Xfa(a,b){for(var c=kZ(O().wR,1,1);;){if(c.b())b=R();else{var d=hZ(c).e();if(b.L(new vl(a+"_"+(d|0)))){c=hZ(c).Lf();continue}b=new L(d)}break}b.b()&&xm("Program reached and unexpected state.");return a+"_"+b.o()} function cga(a,b,c,d,e,g,h,k,l){a:{for(var m=d.Ra;!m.b();){if(!m.e().h().b()){m=!0;break a}m=m.f()}m=!1}a:{for(var n=d.Ra;!n.b();){if(n.e().h().b()){n=!0;break a}n=n.f()}n=!1}a:{for(var r=0,v=a6(0,d.Ra);!v.b();){if(!v.e().h().b())break a;r=1+r|0;v=v.f()}r=-1}v=0;for(var x=d.Ra,A=-1;!x.b();)x.e().h().b()&&(A=v),x=x.f(),v=1+v|0;if(m&&n&&r{if(null!== B){var C=B.h();B=B.Sc();var D=C.h();if(D instanceof L)return G(new H,G(new H,D.k.x,C.j()),!0);if(t().d===D)return G(new H,G(new H,eB(e,B).x,C.j()),!1);throw new w(D);}throw new w(B);};if(r===u())g=u();else{m=r.e();n=m=new z(g(m),u());for(r=r.f();r!==u();)v=r.e(),v=new z(g(v),u()),n=n.p=v,r=r.f();g=m}m=Xu().X();for(n=g.m();n.s();)r=n.t(),m.Hk(r.h().h(),new U(()=>{Od();return new fp})).$(r);n=Ez().Mr;for(m=m.m();m.s();){r=m.t();if(null===r)throw new w(r);n=m7(n,r.h(),r.j().Kb())}m=n;0>hv(m,e)&&m.dc.Ca(new y(B=> {var C=B.j();if(C instanceof z&&C.p instanceof z)return C=new Te(new Ue(J(new K,["Argument for parameter '","' is duplicated"]))),B=[We(Xe(),B.h())],Lw(a,Ye(C,J(new K,B)),d.A(),k)}));tp();m=u();c=Yfa(a,g,pp(0,m),d,e,k,c);a.F&&(g=ut(Q(),"| ",a.r)+"Desugared is here \x3d\x3e "+c,ff(gf(),g+"\n"));b.oc=(t(),new L(c));return Rw(a,c,h,k,l,!1)}b=new Te(new Ue(J(new K,["Number of arguments doesn't match function signature `","`"])));h=[wO(Xe(),AD(g,h))];return Lw(a,Ye(b,J(new K,h)),d.A(),k)} function uf(a,b,c,d){var e=O().c;e=new aw(e);var g=jA().X();a:{var h=new qC(a,nf());if(b instanceof zA)a=$6(a,b,h,c,g,e,d);else{if(b instanceof BA){CA(a);t();var k=new L(b);if(!k.b()){b=k.k;var l=b.xk;if(l===u())k=u();else{k=l.e();var m=k=new z(Q_(a,k,h,d,c,g,e),u());for(l=l.f();l!==u();){var n=l.e();n=new z(Q_(a,n,h,d,c,g,e),u());m=m.p=n;l=l.f()}}b=b.Ul;b.b()?a=R():(b=b.o(),a=new L($6(a,b,h,c,g,e,d)));a=new BP(k,a);break a}}throw new w(b);}}return e.rc.b()?a:new qP(a,e.rc,O().c)} function i7(a){var b=a.YE;a.YE=1+a.YE|0;return b} function Q_(a,b,c,d,e,g,h){if(b instanceof ax){var k=b.Rl,l=b.Ql,m=rC(c,b.rk),n=k.pb,r=k.gb,v=k.hg,x=t().d,A=t().d;t();var B=$6(a,l,m,e,g,h,d);return new yo(n,r,v,x,A,new L(B),O().c,t().d,t().d,new Dt(O().c),k.fl,k.Pm,k.Rz)}if(b instanceof Nw){var C=b.il,D=b.jl,F=b.hl,I=b.Tl,M=b.Jj,N=rC(c,b.kl),P=C.pb,T=C.gb,Y=C.hg;t();var Z=Kc=>{var Qd=t().d;Kc=new sm(tm().Cg,new Cl(Kc.h(),$6(a,Kc.j().ra,N,e,g,h,d)));return G(new H,Qd,Kc)};if(I===u())var S=u();else{for(var ea=I.e(),ia=new z(Z(ea),u()),X=ia,sa=I.f();sa!== u();){var Ja=sa.e(),Xa=new z(Z(Ja),u());X=X.p=Xa;sa=sa.f()}S=ia}var Fa=new Gl(S),za=new L(Fa),Qa=t().d,Ma=t().d,Ga=O().c,ab=Xu().X(),Hb=Zu(a.La,F,d,!0,ab)?R():new L($6(a,F,N,e,g,h,d)),bc=Xu().X(),yb=!Zu(a.La,D,d,!0,bc);return new yo(P,T,Y,za,Qa,Ma,Ga,Hb,yb?new L($6(a,D,N,e,g,h,d)):R(),a7(a,M,N,d,e,g,h),C.fl,C.Pm,C.Rz)}if(b instanceof Yw){var tb=b.Rf,eb=b.Ij,kb=b.gl,Rb=b.Ei,Gb=b.Um,vb=b.sk,Tb=b.ip,Nb=rC(c,b.gh),ic=tb.pb,Va=tb.gb,cb=tb.hg;if(eb.b())var zb=R();else{var Ub=eb.o(),jb=Kc=>{var Qd=t().d; Kc=new sm(tm().Cg,new Cl(Kc.h(),$6(a,Kc.j().ra,Nb,e,g,h,d)));return G(new H,Qd,Kc)};if(Ub===u())var db=u();else{for(var ub=Ub.e(),Aa=new z(jb(ub),u()),va=Aa,Ra=Ub.f();Ra!==u();){var rb=Ra.e(),xb=new z(jb(rb),u());va=va.p=xb;Ra=Ra.f()}db=Aa}zb=new L(new Gl(db))}var mc=tb.Qm,Ha=Xu().X(),Ka=Zu(a.La,vb,d,!0,Ha)?R():new L($6(a,vb,Nb,e,g,h,d));Od();var Oa=Pd(u(),Tb),Na=Su(),Da=op().ga,ta=qw(Oa,new Uu(Na,Da));if(ta===u())var Ya=u();else{for(var dc=ta.e(),ka=new z(dc.Zr(),u()),ya=ka,Sa=ta.f();Sa!==u();){var xc= Sa.e(),Sb=new z(xc.Zr(),u());ya=ya.p=Sb;Sa=Sa.f()}Ya=ka}var uc=t().d,Lb=Xu().X(),lc=Zu(a.La,Gb,d,!0,Lb)?R():new L($6(a,Gb,Nb,e,g,h,d)),Xb=a7(a,Rb,Nb,d,e,g,h);if(kb instanceof L){var ec=kb.k,Ab=Kc=>{var Qd=t().d;Kc=new sm(tm().Cg,new Cl(Kc.h(),$6(a,Kc.j(),Nb,e,g,h,d)));return G(new H,Qd,Kc)};if(ec===u())var Ob=u();else{for(var fb=ec.e(),Wa=new z(Ab(fb),u()),bb=Wa,Ia=ec.f();Ia!==u();){var Ua=Ia.e(),pc=new z(Ab(Ua),u());bb=bb.p=pc;Ia=Ia.f()}Ob=Wa}var sc=new Po(new Gl(Ob),new Sl(O().c)),Ba=ef(Xb),ob= new Dt(new z(sc,Ba))}else{if(t().d!==kb)throw new w(kb);ob=Xb}return new yo(ic,Va,cb,zb,mc,Ka,Ya,uc,lc,ob,tb.fl,tb.Pm,tb.Rz)}if(b instanceof Ww){var nc=b.vk,Ib=b.tk,vc=b.co,Vb=b.uk,fc=b.Gq,Bc=rC(c,b.wk),Pb=nc.pb,Jb=nc.gb,gc=nc.hg,Cb=t().d,cc=nc.Qm,yc=Xu().X(),Mc=Zu(a.La,Vb,d,!0,yc)?R():new L($6(a,Vb,Bc,e,g,h,d));Od();var qc=Pd(u(),fc),oc=Su(),Qc=op().ga,jc=qw(qc,new Uu(oc,Qc));if(jc===u())var sb=u();else{for(var Gc=jc.e(),Wb=new z(Gc.Zr(),u()),Cc=Wb,Fc=jc.f();Fc!==u();){var qd=Fc.e(),Yb=new z(qd.Zr(), u());Cc=Cc.p=Yb;Fc=Fc.f()}sb=Wb}var Nc=t().d,ad=Xu().X(),Uc=!Zu(a.La,vc,d,!0,ad);return new yo(Pb,Jb,gc,Cb,cc,Mc,sb,Nc,Uc?new L($6(a,vc,Bc,e,g,h,d)):R(),a7(a,Ib,Bc,d,e,g,h),nc.fl,nc.Pm,nc.Rz)}if(b instanceof bx){var cd=b.Mb,kc=cd.wd,Vc=cd.Rb,Hc=cd.hj,rc=O().c;t();var sd=$6(a,b.Nn(),c,e,g,h,d);return new Zn(kc,Vc,Hc,rc,new Ud(sd),cd.Pz,cd.lx,cd.Om,cd.kx,cd.Qz,cd.Pl,cd.Oz)}if(b instanceof Vw)no();else if(b instanceof cx)no();else throw new w(b);} hf.prototype.$classData=q({KY:0},!1,"mlscript.Typer",{KY:1,Vaa:1,Jaa:1,Daa:1,waa:1,Caa:1,Oaa:1,Qaa:1,g:1,Laa:1});function Qx(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.MO=this.dd=0;this.NO=null;this.DI=!1;this.de=b;this.Re=c;pY(this,a);if(!(bc.Ea()));var b=Fq();a=Jq(a,b);return(a.b()?this.q.Gd:a.o())|0};f.ub=function(a,b){var c=this.Zb.m();c=new Ef(c,new y(e=>e.ub(a,b)));var d=Fq();c=Jq(c,d);return(c.b()?this.q.Gd:c.o())|0};function rv(a,b,c,d,e){var g=a.q,h=a.qb,k=a.Zb;if(k===u())b=u();else{var l=k.e(),m=l=new z(l.Kc(b,c,d,e),u());for(k=k.f();k!==u();){var n=k.e();n=new z(n.Kc(b,c,d,e),u());m=m.p=n;k=k.f()}b=l}return new fw(g,h,b,a.Xl)} f.u=function(){var a=this.q.cn.L(this.qb.V)?Nu(Q(),this.qb.V):this.qb.V;return this.Zb.b()?a:a+"["+ze(this.Zb,"",",","")+"]"};f.H=function(){return"TypeRef"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.qb;case 1:return this.Zb;default:return $K(W(),a)}};f.D=function(a){return a instanceof fw};f.Kc=function(a,b,c,d){return rv(this,a,b,c,d)};f.$classData=q({IZ:0},!1,"mlscript.TyperDatatypes$TypeRef",{IZ:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,Saa:1,E:1,v:1,l:1}); function dga(a){if(!a.OI){var b=a.Oq,c=a.up.Ba.m();c=new Ef(c,new y(g=>g.h()));var d=Su(),e=op().ga;d=new Uu(d,e);c=oA(uv(),c,d);b=iB(b,c);c=a.up;d=V(b.q);a.PI=Pu(b,c,d,!1);a.OI=!0}return a.PI}function $y(a,b,c,d){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.PI=null;this.OI=!1;this.Oq=b;this.up=c;this.OZ=d;pY(this,a)}$y.prototype=new T_;$y.prototype.constructor=$y;f=$y.prototype;f.ma=function(){return this.OZ};f.mc=function(){return this.OI?this.PI:dga(this)}; f.u=function(){return this.Oq+" w/ "+this.up};f.H=function(){return"WithType"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Oq;case 1:return this.up;default:return $K(W(),a)}};f.D=function(a){return a instanceof $y};f.$classData=q({NZ:0},!1,"mlscript.TyperDatatypes$WithType",{NZ:1,EI:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1}); function nP(a,b){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.Lx=a;this.Mx=b;Nq(this)}nP.prototype=new M_;nP.prototype.constructor=nP;f=nP.prototype;f.H=function(){return"WithExtension"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Lx;case 1:return this.Mx;default:return $K(W(),a)}};f.D=function(a){return a instanceof nP};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof nP){var b=this.Lx,c=a.Lx;if(null===b?null===c:b.i(c))return b=this.Mx,a=a.Mx,null===b?null===a:b.i(a)}return!1};f.$classData=q({n_:0},!1,"mlscript.WithExtension",{n_:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,E:1,v:1,l:1});q({p_:0},!1,"mlscript.codegen.ClassSymbol",{p_:1,g:1,UA:1,cr:1,mt:1,yj:1,nf:1,E:1,v:1,l:1});function n7(){}n7.prototype=new q3;n7.prototype.constructor=n7;function o7(){}o7.prototype=n7.prototype;n7.prototype.Ub=function(){return eU()}; n7.prototype.u=function(){return w5(this)};n7.prototype.Oc=function(){return"View"};function rp(a){this.mL=null;if(null===a)throw null;this.mL=a}rp.prototype=new q3;rp.prototype.constructor=rp;rp.prototype.Q=function(){return this.mL.Q()};rp.prototype.m=function(){return this.mL.ie()};rp.prototype.$classData=q({L5:0},!1,"scala.collection.MapOps$$anon$1",{L5:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,sg:1,l:1}); function kY(a,b){if(a===b)return!0;if(b&&b.$classData&&b.$classData.rb.fk)if(a.ka()===b.ka())try{return a.fM(b)}catch(c){throw c;}else return!1;else return!1}function p7(){this.Br=0;this.OB="Any";O();this.Br=Qb(this)}p7.prototype=new D6;p7.prototype.constructor=p7;p7.prototype.uh=function(){return da(jd)};p7.prototype.si=function(a){return new zc(a)};p7.prototype.$classData=q({W3:0},!1,"scala.reflect.ManifestFactory$AnyManifest$",{W3:1,OK:1,NK:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var q7; function IB(){q7||(q7=new p7);return q7}function r7(){this.Gf=0;this.Ki="Boolean";this.Gf=Qb(this)}r7.prototype=new p6;r7.prototype.constructor=r7;r7.prototype.$classData=q({X3:0},!1,"scala.reflect.ManifestFactory$BooleanManifest$",{X3:1,zba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var s7;function uL(){s7||(s7=new r7);return s7}function t7(){this.Gf=0;this.Ki="Byte";this.Gf=Qb(this)}t7.prototype=new r6;t7.prototype.constructor=t7; t7.prototype.$classData=q({Y3:0},!1,"scala.reflect.ManifestFactory$ByteManifest$",{Y3:1,Aba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var u7;function sL(){u7||(u7=new t7);return u7}function v7(){this.Gf=0;this.Ki="Char";this.Gf=Qb(this)}v7.prototype=new t6;v7.prototype.constructor=v7;v7.prototype.$classData=q({Z3:0},!1,"scala.reflect.ManifestFactory$CharManifest$",{Z3:1,Bba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var w7;function Ir(){w7||(w7=new v7);return w7} function x7(){this.Gf=0;this.Ki="Double";this.Gf=Qb(this)}x7.prototype=new v6;x7.prototype.constructor=x7;x7.prototype.$classData=q({$3:0},!1,"scala.reflect.ManifestFactory$DoubleManifest$",{$3:1,Cba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var y7;function pL(){y7||(y7=new x7);return y7}function z7(){this.Gf=0;this.Ki="Float";this.Gf=Qb(this)}z7.prototype=new x6;z7.prototype.constructor=z7; z7.prototype.$classData=q({a4:0},!1,"scala.reflect.ManifestFactory$FloatManifest$",{a4:1,Dba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var A7;function rL(){A7||(A7=new z7);return A7}function B7(){this.Gf=0;this.Ki="Int";this.Gf=Qb(this)}B7.prototype=new z6;B7.prototype.constructor=B7;B7.prototype.$classData=q({b4:0},!1,"scala.reflect.ManifestFactory$IntManifest$",{b4:1,Eba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var C7;function rl(){C7||(C7=new B7);return C7} function D7(){this.Gf=0;this.Ki="Long";this.Gf=Qb(this)}D7.prototype=new B6;D7.prototype.constructor=D7;D7.prototype.$classData=q({c4:0},!1,"scala.reflect.ManifestFactory$LongManifest$",{c4:1,Fba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var E7;function qL(){E7||(E7=new D7);return E7}function UR(){this.Br=0;this.OB="Nothing";O();this.Br=Qb(this)}UR.prototype=new D6;UR.prototype.constructor=UR;UR.prototype.uh=function(){return da(kH)};UR.prototype.si=function(a){return new zc(a)}; UR.prototype.$classData=q({d4:0},!1,"scala.reflect.ManifestFactory$NothingManifest$",{d4:1,OK:1,NK:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var TR;function WR(){this.Br=0;this.OB="Null";O();this.Br=Qb(this)}WR.prototype=new D6;WR.prototype.constructor=WR;WR.prototype.uh=function(){return da(jH)};WR.prototype.si=function(a){return new zc(a)};WR.prototype.$classData=q({e4:0},!1,"scala.reflect.ManifestFactory$NullManifest$",{e4:1,OK:1,NK:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var VR; function F7(){this.Br=0;this.OB="Object";O();this.Br=Qb(this)}F7.prototype=new D6;F7.prototype.constructor=F7;F7.prototype.uh=function(){return da(jd)};F7.prototype.si=function(a){return new zc(a)};F7.prototype.$classData=q({f4:0},!1,"scala.reflect.ManifestFactory$ObjectManifest$",{f4:1,OK:1,NK:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var G7;function TG(){G7||(G7=new F7);return G7}function H7(){this.Gf=0;this.Ki="Short";this.Gf=Qb(this)}H7.prototype=new F6;H7.prototype.constructor=H7; H7.prototype.$classData=q({g4:0},!1,"scala.reflect.ManifestFactory$ShortManifest$",{g4:1,Gba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var I7;function tL(){I7||(I7=new H7);return I7}function J7(){this.Gf=0;this.Ki="Unit";this.Gf=Qb(this)}J7.prototype=new H6;J7.prototype.constructor=J7;J7.prototype.$classData=q({h4:0},!1,"scala.reflect.ManifestFactory$UnitManifest$",{h4:1,Hba:1,Kt:1,g:1,om:1,Lk:1,nm:1,pm:1,l:1,v:1});var K7;function SR(){K7||(K7=new J7);return K7}function vw(a){this.al=a} vw.prototype=new p;vw.prototype.constructor=vw;f=vw.prototype;f.$l=function(a){Fq();var b=this.al;a|=0;return b===a?0:b=this.Zi()};f.Lp=function(){return this.al};f.pv=function(){return Math.fround(this.al)}; f.xl=function(){var a=this.al;return new ma(a,a>>31)};f.Zi=function(){return this.al};f.jB=function(){return this.al<<24>>24};f.EC=function(){return this.al<<16>>16};f.uv=function(){return!0};f.MF=function(){return!0};f.B=function(){return this.al};f.i=function(a){TK||(TK=new SK);return a instanceof vw?this.al===a.al:!1};f.$classData=q({faa:0},!1,"scala.runtime.RichInt",{faa:1,g:1,dca:1,sR:1,mba:1,lba:1,bca:1,yj:1,nf:1,cca:1}); function pP(a,b){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.Gw=a;this.Hw=b;Nq(this)}pP.prototype=new M_;pP.prototype.constructor=pP;f=pP.prototype;f.H=function(){return"AppliedType"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Gw;case 1:return this.Hw;default:return $K(W(),a)}};f.D=function(a){return a instanceof pP};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof pP){var b=this.Gw,c=a.Gw;if(null===b?null===c:b.i(c))return b=this.Hw,a=a.Hw,null===b?null===a:b.i(a)}return!1};f.$classData=q({RT:0},!1,"mlscript.AppliedType",{RT:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,JW:1,E:1,v:1,l:1});function L7(){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;Nq(this)}L7.prototype=new U2;L7.prototype.constructor=L7;f=L7.prototype;f.H=function(){return"Bot"};f.G=function(){return 0}; f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof L7};f.B=function(){return 66983};f.u=function(){return"Bot"};f.$classData=q({XT:0},!1,"mlscript.Bot$",{XT:1,Vz:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,E:1,v:1,l:1});var M7;function el(){M7||(M7=new L7);return M7}function Po(a,b){this.MM=this.LM=null;this.OM=this.PM=0;this.QM=this.NM=null;this.Jm=0;this.ks=a;this.js=b;Nq(this)}Po.prototype=new p;Po.prototype.constructor=Po;f=Po.prototype;f.jb=function(){return"constructor"};f.Vj=function(){return eP(this)}; f.Wr=function(){return Mx(this)};f.nv=function(){0===(1&this.Jm)<<24>>24&&0===(1&this.Jm)<<24>>24&&(this.LM=cP(this),this.Jm=(1|this.Jm)<<24>>24);return this.LM};f.jn=function(){0===(2&this.Jm)<<24>>24&&0===(2&this.Jm)<<24>>24&&(this.MM=zq(this),this.Jm=(2|this.Jm)<<24>>24);return this.MM};f.rn=function(){return this.PM};f.fm=function(a){this.PM=a};f.qn=function(){return this.OM};f.em=function(a){this.OM=a};f.pn=function(){return this.NM};f.on=function(a){this.NM=a}; f.A=function(){0===(4&this.Jm)<<24>>24&&0===(4&this.Jm)<<24>>24&&(this.QM=Dq(this),this.Jm=(4|this.Jm)<<24>>24);return this.QM};f.H=function(){return"Constructor"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.ks;case 1:return this.js;default:return $K(W(),a)}};f.D=function(a){return a instanceof Po};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Po){var b=this.ks,c=a.ks;if(null===b?null===c:b.i(c))return b=this.js,a=a.js,null===b?null===a:b.i(a)}return!1};f.$classData=q({CU:0},!1,"mlscript.Constructor",{CU:1,g:1,ud:1,md:1,xd:1,Ta:1,Od:1,xaa:1,E:1,v:1,l:1});function oP(a){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.Es=a;Nq(this)}oP.prototype=new U2;oP.prototype.constructor=oP;f=oP.prototype;f.H=function(){return"Literal"}; f.G=function(){return 1};f.I=function(a){return 0===a?this.Es:$K(W(),a)};f.D=function(a){return a instanceof oP};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof oP){var b=this.Es;a=a.Es;return null===b?null===a:b.i(a)}return!1};f.$classData=q({uW:0},!1,"mlscript.Literal",{uW:1,Vz:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,E:1,v:1,l:1}); function Vt(a,b){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.Ws=a;this.Vs=b;Nq(this)}Vt.prototype=new M_;Vt.prototype.constructor=Vt;f=Vt.prototype;f.H=function(){return"PolyType"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Ws;case 1:return this.Vs;default:return $K(W(),a)}};f.D=function(a){return a instanceof Vt};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Vt){var b=this.Ws,c=a.Ws;if(null===b?null===c:b.i(c))return b=this.Vs,a=a.Vs,null===b?null===a:b.i(a)}return!1};f.$classData=q({PX:0},!1,"mlscript.PolyType",{PX:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,Gaa:1,E:1,v:1,l:1});function N7(){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;Nq(this)}N7.prototype=new U2;N7.prototype.constructor=N7;f=N7.prototype;f.H=function(){return"Top"};f.G=function(){return 0}; f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof N7};f.B=function(){return 84277};f.u=function(){return"Top"};f.$classData=q({pY:0},!1,"mlscript.Top$",{pY:1,Vz:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,E:1,v:1,l:1});var O7;function gl(){O7||(O7=new N7);return O7}function sP(a){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.nA=a;Nq(this)}sP.prototype=new U2;sP.prototype.constructor=sP;f=sP.prototype;f.H=function(){return"TypeTag"}; f.G=function(){return 1};f.I=function(a){return 0===a?this.nA:$K(W(),a)};f.D=function(a){return a instanceof sP};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return this===a?!0:a instanceof sP?this.nA===a.nA:!1};f.$classData=q({IY:0},!1,"mlscript.TypeTag",{IY:1,Vz:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,E:1,v:1,l:1});function Gv(){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0}Gv.prototype=new S_;Gv.prototype.constructor=Gv;function P7(){} P7.prototype=Gv.prototype;Gv.prototype.sl=function(a){return uw(this,a)};Gv.prototype.$l=function(a){return uw(this,a)};var Ru=q({IE:0},!1,"mlscript.TyperDatatypes$AbstractTag",{IE:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,uA:1,yj:1,nf:1,Hx:1});Gv.prototype.$classData=Ru;function VC(a,b){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.GO=this.HO=null;this.yI=!1;this.zI=b;pY(this,a);this.GO=V(a)}VC.prototype=new T_;VC.prototype.constructor=VC;f=VC.prototype; f.mc=function(){if(!this.yI&&!this.yI){var a=this.zI,b=V(this.zI.q);this.HO=NA(a,b,!1);this.yI=!0}return this.HO};f.ma=function(){return this.GO};f.H=function(){return"NegAbsTag"};f.G=function(){return 1};f.I=function(a){return 0===a?this.zI:$K(W(),a)};f.D=function(a){return a instanceof VC};f.$classData=q({oZ:0},!1,"mlscript.TyperDatatypes$NegAbsTag",{oZ:1,EI:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,Hx:1,E:1,v:1,l:1}); function UC(a,b){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.IO=this.JO=null;this.AI=!1;this.BI=b;pY(this,a);this.IO=V(a)}UC.prototype=new T_;UC.prototype.constructor=UC;f=UC.prototype;f.mc=function(){if(!this.AI&&!this.AI){var a=this.BI,b=V(this.BI.q);this.JO=NA(a,b,!1);this.AI=!0}return this.JO};f.ma=function(){return this.IO};f.H=function(){return"NegVar"};f.G=function(){return 1};f.I=function(a){return 0===a?this.BI:$K(W(),a)}; f.D=function(a){return a instanceof UC};f.$classData=q({qZ:0},!1,"mlscript.TyperDatatypes$NegVar",{qZ:1,EI:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,Hx:1,E:1,v:1,l:1});function Q7(a){if(0===(1&a.ii)<<24>>24&&0===(1&a.ii)<<24>>24){var b=a.NI;b.b()?b=R():(b=b.o(),b=Q7(b),b=b.b()?a.NI:b);a.dP=b;a.ii=(1|a.ii)<<24>>24}return a.dP} function R7(a){if(0===(4&a.ii)<<24>>24&&0===(4&a.ii)<<24>>24){t();var b=pE().SP,c=a.kg;c=c.b()?"_":c.o();a:{for(var d=c.length,e=0;e>24}return a.eP} function lx(a,b,c,d,e,g,h,k){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.Sb=this.$O=this.aP=this.eP=this.bP=this.dP=null;this.ii=this.sp=this.cP=0;this.Xa=b;this.YO=c;this.ZO=d;this.NI=e;this.kg=g;this.ji=k;pY(this,a);At(tp(),b<=a.Df);this.Sb=t().d;this.cP=a.Im;this.sp=UP(a)}lx.prototype=new qY;lx.prototype.constructor=lx;f=lx.prototype;f.sl=function(a){return gT(this,a)};f.Ea=function(){return this.Xa};f.ma=function(){return this.ji}; function wy(a,b){tp();var c=b.b()?!0:b.o().Ea()<=a.Xa;At(0,c);a.Sb=b}function vy(a){if(!a.Sb.b())throw Kj("requirement failed: "+a);return a.YO}function rA(a){if(!a.Sb.b())throw Kj("requirement failed: "+a);return a.ZO}function IA(a,b){if(!a.Sb.b())throw Kj("requirement failed: "+a);a.YO=b}function KA(a,b){if(!a.Sb.b())throw Kj("requirement failed: "+a);a.ZO=b}function xca(a){if(Pe(new E(a.q.Im),a.cP)){var b=a.NI;return b.b()?a:b.o()}return a} f.ub=function(a,b){if(this.Xa<=a)return this.Xa;if(b.L(this))return this.q.Gd;b.$(this);var c=this.Sb;if(c instanceof L)return c.k.ub(a,b);if(t().d===c){c=vy(this).m().nb(new U(()=>rA(this).m()));c=new Ef(c,new y(e=>e.ub(a,b)));var d=Fq();c=Jq(c,d);return(c.b()?this.q.Gd:c.o())|0}throw new w(c);};function c7(a){0===(2&a.ii)<<24>>24&&0===(2&a.ii)<<24>>24&&(t(),a.bP=new jt(new fe(a.sp),a.kg),a.ii=(2|a.ii)<<24>>24);return a.bP} function Zfa(a){0===(8&a.ii)<<24>>24&&0===(8&a.ii)<<24>>24&&(a.aP=R7(a).h(),a.ii=(8|a.ii)<<24>>24);return a.aP}function $fa(a){0===(16&a.ii)<<24>>24&&0===(16&a.ii)<<24>>24&&(a.$O=R7(a).j(),a.ii=(16|a.ii)<<24>>24);return a.$O}function gT(a,b){a=new vw(a.sp);b=b.sp;Fq();a=a.al;return a===b?0:aY7(this).m()))};f.Q=function(){return this.Sp}; f.b=function(){return 0===this.Sp};f.ad=function(){return new X7(this)};f.cM=function(a){var b=this.Hy;return(null===a?null===b:a.i(b))?this:a.sj(this.Hy)?new X7(this):U7(new W7,Z7(this),this.Sp,a)};f.vc=function(a){return ZT(eU(),a)};f.Bb=function(a){return $7(new a8,this,a)};f.Jc=function(a){return b8(new c8,this,a)};f.cc=function(a){return d8(new e8,a,this)};f.Ja=function(a){return f8(new g8,this,a)};f.Zh=function(a){return this.cM(a)}; f.$classData=q({V5:0},!1,"scala.collection.SeqView$Sorted",{V5:1,g:1,Li:1,kb:1,pa:1,M:1,N:1,jd:1,na:1,oa:1,l:1});function h8(a){if(!a.OG){var b=new i8,c=Y7(a.un);b.Rv=c;a.NG=b;a.OG=!0}return a.NG}function X7(a){this.NG=null;this.OG=!1;this.un=null;if(null===a)throw null;this.un=a}X7.prototype=new p;X7.prototype.constructor=X7;f=X7.prototype;f.Ub=function(){return eU()};f.u=function(){return w5(this)};f.Ih=function(){return"SeqView"};f.ti=function(){return eU().Eb()}; f.tl=function(a){return IT(this,a)};f.Ii=function(a){return NY(this,a)};f.Sd=function(){return this.un.m()};f.wo=function(a,b){var c=this.m();return LT(c,a,b)};f.L=function(a){return QU(this,a)};f.$k=function(a){return iv(this,a)};f.ab=function(a){return iv(this,a)};f.e=function(){return this.m().t()};f.Mc=function(){return yT(this)};f.Gb=function(a){return KT(this,a)};f.Ca=function(a){cH(this,a)};f.qo=function(a){return ey(this,a)};f.De=function(a,b){return mB(this,a,b)}; f.mf=function(a,b){return Qu(this,a,b)};f.th=function(a){return eH(this,a)};f.Gc=function(a,b,c){return NB(this,a,b,c)};f.aj=function(a){return gH(this,a)};f.$i=function(a){return hH(this,a)};f.Gh=function(a,b,c,d){return iH(this,a,b,c,d)};f.ha=function(){Od();return Pd(u(),this)};f.Ti=function(){return pp(qp(),this)};f.FC=function(){return v1(EK(),this)};f.Bj=function(a){return kB(this,a)};f.va=function(a){return(this.OG?this.NG:h8(this)).va(a)};f.K=function(){return this.un.Sp}; f.m=function(){return Rq().Pa.nb(new U(()=>(this.OG?this.NG:h8(this)).m()))};f.Q=function(){return this.un.Sp};f.b=function(){return 0===this.un.Sp};f.ad=function(){return this.un};f.cM=function(a){var b=this.un.Hy;return(null===a?null===b:a.i(b))?this.un:a.sj(this.un.Hy)?this:U7(new W7,Z7(this.un),this.un.Sp,a)};f.vc=function(a){return ZT(eU(),a)};f.Bb=function(a){return $7(new a8,this,a)};f.Jc=function(a){return b8(new c8,this,a)};f.cc=function(a){return d8(new e8,a,this)}; f.Ja=function(a){return f8(new g8,this,a)};f.Zh=function(a){return this.cM(a)};f.$classData=q({W5:0},!1,"scala.collection.SeqView$Sorted$ReverseSorted",{W5:1,g:1,Li:1,kb:1,pa:1,M:1,N:1,jd:1,na:1,oa:1,l:1});function $T(a){this.t6=a}$T.prototype=new o7;$T.prototype.constructor=$T;$T.prototype.m=function(){return Es(this.t6)};$T.prototype.$classData=q({s6:0},!1,"scala.collection.View$$anon$1",{s6:1,Td:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,jd:1,l:1});function JT(a,b){this.AL=a;this.BL=b}JT.prototype=new o7; JT.prototype.constructor=JT;JT.prototype.m=function(){return this.AL.m().nb(new U(()=>this.BL.m()))};JT.prototype.Q=function(){var a=this.AL.Q();if(0<=a){var b=this.BL.Q();return 0<=b?a+b|0:-1}return-1};JT.prototype.b=function(){return this.AL.b()&&this.BL.b()};JT.prototype.$classData=q({u6:0},!1,"scala.collection.View$Concat",{u6:1,Td:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,jd:1,l:1});function OY(a,b){this.CL=a;this.w6=b}OY.prototype=new o7;OY.prototype.constructor=OY; OY.prototype.m=function(){var a=this.CL.m();return new hy(a,this.w6)};OY.prototype.Q=function(){return 0===this.CL.Q()?0:-1};OY.prototype.b=function(){return this.CL.b()};OY.prototype.$classData=q({v6:0},!1,"scala.collection.View$DistinctBy",{v6:1,Td:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,jd:1,l:1});function AT(a,b,c){a.My=b;a.cC=c;a.Tv=0=b)){var c=a.Q();a=0<=c?a.Mn(c-b|0):new M0(a,b)}return a};t3.prototype.Q=function(){var a=this.Ly.Q();return 0<=a?(a=a-this.bC|0,0=this.Da(a,b)};f.Yj=function(a,b){return 0<=this.Da(a,b)};f.Xj=function(a,b){return 0b?a:b;this.xI=!0}return this.FO}; f.ub=function(a,b){var c=this.Nb.ub(a,b);a=this.ac.ub(a,b);return c>a?c:a};function q8(a,b,c,d,e){return new cv(a.q,a.Nb.Kc(b,c,d,e),a.ac.Kc(b,c,d,e),a.Mj)} f.u=function(){var a=!1,b=null,c=this.Nb;a:{if(c instanceof zv){a=!0;b=c;var d=b.Yb;if(d instanceof z){var e=d.z;d=d.p;if(null!==e){var g=e.h();e=e.j();if(t().d===g&&null!==e&&(g=e.Oa,e=e.ra,t().d===g&&e instanceof zv&&(g=O().c,null===g?null===d:g.i(d)))){c="["+r8(e)+"]";break a}}}}a&&(a=b.Yb,a instanceof z&&(b=a.z,a=a.p,null!==b&&(d=b.h(),b=b.j(),t().d===d?(d=O().c,a=null===d?null===a:d.i(a)):a=!1,a&&(c=b.u()))))}return"("+c+" -\x3e "+this.ac+")"};f.H=function(){return"FunctionType"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.Nb;case 1:return this.ac;default:return $K(W(),a)}};f.D=function(a){return a instanceof cv};f.At=function(a,b,c,d){return q8(this,a,b,c,d)};f.qv=function(a,b,c,d){return q8(this,a,b,c,d)};f.$classData=q({mZ:0},!1,"mlscript.TyperDatatypes$FunctionType",{mZ:1,qA:1,Gx:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1}); function Jv(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.LO=this.dd=0;this.CI=!1;this.gi=b;this.Mq=c;pY(this,a);At(tp(),0new cv(a.q,b.n(m.Nb),c.n(m.ac),m.Mj);if(e===u())g=u();else{var h=e.e(),k=h=new z(g(h),u());for(e=e.f();e!==u();){var l=e.e();l=new z(g(l),u());k=k.p=l;e=e.f()}g=h}return new Jv(d,g,a.Mq)} function rB(a,b,c){var d=a.q,e=a.gi,g=m=>{var n=a.q;if(b.b())var r=R();else r=!!b.o(),r=new L(!r);return new cv(n,c.ba(r,m.Nb),c.ba(b,m.ac),m.Mj)};if(e===u())g=u();else{var h=e.e(),k=h=new z(g(h),u());for(e=e.f();e!==u();){var l=e.e();l=new z(g(l),u());k=k.p=l;e=e.f()}g=h}return new Jv(d,g,a.Mq)} function jca(a,b,c){var d=a.q,e=a.gi,g=m=>new cv(a.q,c.ba(new UB(b),m.Nb),c.ba(b,m.ac),m.Mj);if(e===u())g=u();else{var h=e.e(),k=h=new z(g(h),u());for(e=e.f();e!==u();){var l=e.e();l=new z(g(l),u());k=k.p=l;e=e.f()}g=h}return new Jv(d,g,a.Mq)} function mX(a){var b=a.gi;if(b===u())var c=u();else{c=b.e();var d=c=new z(G(new H,c.Nb,c.ac),u());for(b=b.f();b!==u();){var e=b.e();e=new z(G(new H,e.Nb,e.ac),u());d=d.p=e;b=b.f()}}d=op();b=c.Gb(d.ga);if(null===b)throw new w(b);d=b.j();c=a.q;b=b.h().m();if(!b.s())throw nv("empty.reduceLeft");e=!0;for(var g=null;b.s();){var h=b.t();if(e)g=h,e=!1;else{var k=V(g.q);g=dv(g,h,k,!1)}}b=g;d=d.m();if(!d.s())throw nv("empty.reduceLeft");e=!0;for(g=null;d.s();)h=d.t(),e?(g=h,e=!1):(k=V(g.q),g=dv(g,h,k,!1)); return new cv(c,b,g,a.Mq)}f.Ea=function(){this.CI||this.CI||(this.LO=this.ub(this.q.Df,jA().X()),this.CI=!0);return this.LO};f.ub=function(a,b){var c=this.gi.m();c=new Ef(c,new y(e=>e.ub(a,b)));var d=Fq();return hH(c,d)|0};function s8(a,b,c,d,e){var g=a.q,h=a.gi;if(h===u())b=u();else{var k=h.e(),l=k=new z(q8(k,b,c,d,e),u());for(h=h.f();h!==u();){var m=h.e();m=new z(q8(m,b,c,d,e),u());l=l.p=m;h=h.f()}b=k}return new Jv(g,b,a.Mq)}f.H=function(){return"Overload"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.gi:$K(W(),a)};f.D=function(a){return a instanceof Jv};f.u=function(){return VK(this)};f.At=function(a,b,c,d){return s8(this,a,b,c,d)};f.qv=function(a,b,c,d){return s8(this,a,b,c,d)};f.$classData=q({sZ:0},!1,"mlscript.TyperDatatypes$Overload",{sZ:1,qA:1,Gx:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function Tv(a,b,c,d){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.Ic=b;this.kf=c;this.vp=d;pY(this,a)}Tv.prototype=new W4; Tv.prototype.constructor=Tv;f=Tv.prototype;f.ma=function(){return this.vp};f.Ea=function(){return this.Ic.Ea()};f.ub=function(a,b){return this.Ic.ub(a,b)};f.u=function(){return this.Ic+"\\"+ze(this.kf,"","-","")};f.H=function(){return"Without"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Ic;case 1:return this.kf;default:return $K(W(),a)}};f.D=function(a){return a instanceof Tv};f.At=function(a,b,c,d){return new Tv(this.q,this.Ic.Kc(a,b,c,d),this.kf,this.vp)}; f.qv=function(a,b,c,d){return new Tv(this.q,this.Ic.Kc(a,b,c,d),this.kf,this.vp)};f.$classData=q({PZ:0},!1,"mlscript.TyperDatatypes$Without",{PZ:1,qA:1,Gx:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function iP(a,b){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.Kw=null;this.Jw=!1;this.ht=a;this.it=b;this.Yo=!0;Nq(this)}iP.prototype=new O4;iP.prototype.constructor=iP;f=iP.prototype;f.vK=function(){return this.ht};f.DK=function(){return this.it}; f.H=function(){return"Union"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.ht;case 1:return this.it;default:return $K(W(),a)}};f.D=function(a){return a instanceof iP};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof iP){var b=this.ht,c=a.ht;if(null===b?null===c:b.i(c))return b=this.it,a=a.it,null===b?null===a:b.i(a)}return!1}; f.$classData=q({a_:0},!1,"mlscript.Union",{a_:1,pU:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,qU:1,E:1,v:1,l:1});function t8(){}t8.prototype=new q3;t8.prototype.constructor=t8;function u8(){}f=u8.prototype=t8.prototype;f.D=function(){return!0};f.i=function(a){return kY(this,a)};f.B=function(){var a=BL();return CL(a,this,a.PB)};f.Ub=function(){return WY()};f.Oc=function(){return"Set"};f.u=function(){return w0(this)};f.fM=function(a){return this.ul(a)};f.Ce=function(a){return J0(this,a)};f.n=function(a){return this.L(a)}; function v8(a,b){if(a===b)return!0;if(b&&b.$classData&&b.$classData.rb.Mk)if(a.ka()===b.ka())try{return a.ul(new y(c=>ml(nl(),b.Se(c.h(),HY().VR),c.j())))}catch(c){throw c;}else return!1;else return!1}function w8(a,b){var c=a.jq().Dv(a.se());c.zc(a);c.zc(b);return c.Kb()}function Ml(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.gs=a;this.Ml=b;W5(this)}Ml.prototype=new X5;Ml.prototype.constructor=Ml;f=Ml.prototype;f.H=function(){return"Ann"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.gs;case 1:return this.Ml;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ml};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Ml){var b=this.gs,c=a.gs;if(null===b?null===c:b.i(c))return b=this.Ml,a=a.Ml,null===b?null===a:b.i(a)}return!1};f.$classData=q({PT:0},!1,"mlscript.Ann",{PT:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function Pl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Za=a;this.Qb=b;W5(this)}Pl.prototype=new X5;Pl.prototype.constructor=Pl;f=Pl.prototype;f.H=function(){return"App"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Za;case 1:return this.Qb;default:return $K(W(),a)}};f.D=function(a){return a instanceof Pl};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Pl){var b=this.Za,c=a.Za;if(null===b?null===c:b.i(c))return b=this.Qb,a=a.Qb,null===b?null===a:b.i(a)}return!1};f.$classData=q({QT:0},!1,"mlscript.App",{QT:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Cl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.ai=a;this.Fm=b;W5(this)}Cl.prototype=new X5;Cl.prototype.constructor=Cl;f=Cl.prototype;f.H=function(){return"Asc"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.ai;case 1:return this.Fm;default:return $K(W(),a)}};f.D=function(a){return a instanceof Cl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Cl){var b=this.ai,c=a.ai;if(null===b?null===c:b.i(c))return b=this.Fm,a=a.Fm,null===b?null===a:b.i(a)}return!1};f.$classData=q({ST:0},!1,"mlscript.Asc",{ST:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function Wl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Gm=a;this.Qn=b;W5(this)}Wl.prototype=new X5;Wl.prototype.constructor=Wl;f=Wl.prototype;f.H=function(){return"Assign"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Gm;case 1:return this.Qn;default:return $K(W(),a)}};f.D=function(a){return a instanceof Wl};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Wl){var b=this.Gm,c=a.Gm;if(null===b?null===c:b.i(c))return b=this.Qn,a=a.Qn,null===b?null===a:b.i(a)}return!1};f.$classData=q({TT:0},!1,"mlscript.Assign",{TT:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Fl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.bi=a;this.gg=b;W5(this)}Fl.prototype=new X5;Fl.prototype.constructor=Fl;f=Fl.prototype;f.H=function(){return"Bra"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.bi;case 1:return this.gg;default:return $K(W(),a)}};f.D=function(a){return a instanceof Fl};f.B=function(){var a=lb("Bra");a=W().C(-889275714,a);var b=this.bi?1231:1237;a=W().C(a,b);b=this.gg;b=My(W(),b);a=W().C(a,b);return W().Ma(a,2)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Fl&&this.bi===a.bi){var b=this.gg;a=a.gg;return null===b?null===a:b.i(a)}return!1}; f.$classData=q({ZT:0},!1,"mlscript.Bra",{ZT:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Ul(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Sn=a;this.Hm=b;W5(this)}Ul.prototype=new X5;Ul.prototype.constructor=Ul;f=Ul.prototype;f.H=function(){return"CaseOf"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Sn;case 1:return this.Hm;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ul};f.B=function(){return AL(this)}; f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Ul){var b=this.Sn,c=a.Sn;if(null===b?null===c:b.i(c))return b=this.Hm,a=a.Hm,null===b?null===a:b.i(a)}return!1};f.$classData=q({nU:0},!1,"mlscript.CaseOf",{nU:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function No(a,b,c,d){this.SC=this.RC=null;this.UC=this.VC=0;this.WC=this.TC=null;this.cl=0;this.RM=null;this.ls=a;this.oq=b;this.vu=c;this.XC=d;Nq(this);if(c instanceof Ud)a=c.fa;else{if(!(c instanceof fe))throw new w(c);a=c.aa}this.RM=a}No.prototype=new P4;No.prototype.constructor=No;f=No.prototype;f.H=function(){return"Def"};f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.ls;case 1:return this.oq;case 2:return this.vu;case 3:return this.XC;default:return $K(W(),a)}}; f.D=function(a){return a instanceof No};f.B=function(){var a=lb("Def");a=W().C(-889275714,a);var b=this.ls?1231:1237;a=W().C(a,b);b=this.oq;b=My(W(),b);a=W().C(a,b);b=this.vu;b=My(W(),b);a=W().C(a,b);b=this.XC?1231:1237;a=W().C(a,b);return W().Ma(a,4)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof No&&this.ls===a.ls&&this.XC===a.XC){var b=this.oq,c=a.oq;if(null===b?null===c:b.i(c))return b=this.vu,a=a.vu,null===b?null===a:b.i(a)}return!1}; f.$classData=q({HU:0},!1,"mlscript.Def",{HU:1,FU:1,g:1,ud:1,md:1,xd:1,Ta:1,Od:1,GU:1,ce:1,E:1,v:1,l:1});function bm(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Vn=a;this.Zo=b;W5(this)}bm.prototype=new X5;bm.prototype.constructor=bm;f=bm.prototype;f.H=function(){return"Eqn"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Vn;case 1:return this.Zo;default:return $K(W(),a)}};f.D=function(a){return a instanceof bm};f.B=function(){return AL(this)}; f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof bm){var b=this.Vn,c=a.Vn;if(null===b?null===c:b.i(c))return b=this.Zo,a=a.Zo,null===b?null===a:b.i(a)}return!1};f.$classData=q({NU:0},!1,"mlscript.Eqn",{NU:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Zl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.qq=a;this.$o=b;W5(this)}Zl.prototype=new X5;Zl.prototype.constructor=Zl;f=Zl.prototype;f.H=function(){return"Forall"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.qq;case 1:return this.$o;default:return $K(W(),a)}};f.D=function(a){return a instanceof Zl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Zl){var b=this.qq,c=a.qq;if(null===b?null===c:b.i(c))return b=this.$o,a=a.$o,null===b?null===a:b.i(a)}return!1};f.$classData=q({VU:0},!1,"mlscript.Forall",{VU:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function Xl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.bp=a;this.zs=b;W5(this)}Xl.prototype=new X5;Xl.prototype.constructor=Xl;f=Xl.prototype;f.H=function(){return"If"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.bp;case 1:return this.zs;default:return $K(W(),a)}};f.D=function(a){return a instanceof Xl};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Xl){var b=this.bp,c=a.bp;if(null===b?null===c:b.i(c))return b=this.zs,a=a.zs,null===b?null===a:b.i(a)}return!1};f.$classData=q({ZU:0},!1,"mlscript.If",{ZU:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Kl(a){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.cp=a;W5(this)}Kl.prototype=new X5;Kl.prototype.constructor=Kl;f=Kl.prototype;f.H=function(){return"Inst"};f.G=function(){return 1}; f.I=function(a){return 0===a?this.cp:$K(W(),a)};f.D=function(a){return a instanceof Kl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Kl){var b=this.cp;a=a.cp;return null===b?null===a:b.i(a)}return!1};f.$classData=q({fV:0},!1,"mlscript.Inst",{fV:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function Ol(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Ej=a;this.Fj=b;W5(this)}Ol.prototype=new X5;Ol.prototype.constructor=Ol;f=Ol.prototype;f.H=function(){return"Lam"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Ej;case 1:return this.Fj;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ol};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Ol){var b=this.Ej,c=a.Ej;if(null===b?null===c:b.i(c))return b=this.Fj,a=a.Fj,null===b?null===a:b.i(a)}return!1};f.$classData=q({sW:0},!1,"mlscript.Lam",{sW:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Rl(a,b,c,d){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.ep=a;this.Zn=b;this.$n=c;this.Mm=d;W5(this)}Rl.prototype=new X5;Rl.prototype.constructor=Rl;f=Rl.prototype;f.H=function(){return"Let"}; f.G=function(){return 4};f.I=function(a){switch(a){case 0:return this.ep;case 1:return this.Zn;case 2:return this.$n;case 3:return this.Mm;default:return $K(W(),a)}};f.D=function(a){return a instanceof Rl};f.B=function(){var a=lb("Let");a=W().C(-889275714,a);var b=this.ep?1231:1237;a=W().C(a,b);b=this.Zn;b=My(W(),b);a=W().C(a,b);b=this.$n;b=My(W(),b);a=W().C(a,b);b=this.Mm;b=My(W(),b);a=W().C(a,b);return W().Ma(a,4)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Rl){if(this.ep===a.ep){var b=this.Zn,c=a.Zn;b=null===b?null===c:b.i(c)}else b=!1;if(b&&(b=this.$n,c=a.$n,null===b?null===c:b.i(c)))return b=this.Mm,a=a.Mm,null===b?null===a:b.i(a)}return!1};f.$classData=q({tW:0},!1,"mlscript.Let",{tW:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Yl(a){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.hp=a;W5(this)}Yl.prototype=new X5; Yl.prototype.constructor=Yl;f=Yl.prototype;f.H=function(){return"NuNew"};f.G=function(){return 1};f.I=function(a){return 0===a?this.hp:$K(W(),a)};f.D=function(a){return a instanceof Yl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Yl){var b=this.hp;a=a.hp;return null===b?null===a:b.i(a)}return!1};f.$classData=q({pX:0},!1,"mlscript.NuNew",{pX:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function yo(a,b,c,d,e,g,h,k,l,m,n,r,v){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.sE=this.tE=this.Cf=null;this.gp=0;this.pb=a;this.gb=b;this.hg=c;this.Sg=d;this.Qm=e;this.Hj=g;this.Di=h;this.mx=k;this.Ns=l;this.ei=m;this.fl=n;this.Pm=r;this.Rz=v;U4(this)}yo.prototype=new V4;yo.prototype.constructor=yo;f=yo.prototype;f.tJ=function(){return this.Pm};f.H=function(){return"NuTypeDef"};f.G=function(){return 10}; f.I=function(a){switch(a){case 0:return this.pb;case 1:return this.gb;case 2:return this.hg;case 3:return this.Sg;case 4:return this.Qm;case 5:return this.Hj;case 6:return this.Di;case 7:return this.mx;case 8:return this.Ns;case 9:return this.ei;default:return $K(W(),a)}};f.D=function(a){return a instanceof yo};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof yo){if(this.pb===a.pb){var b=this.gb,c=a.gb;b=null===b?null===c:b.i(c)}else b=!1;b?(b=this.hg,c=a.hg,(null===b?null===c:b.i(c))?(b=this.Sg,c=a.Sg,(null===b?null===c:b.i(c))?(b=this.Qm,c=a.Qm,b=null===b?null===c:b.i(c)):b=!1):b=!1):b=!1;if(b&&(b=this.Hj,c=a.Hj,(null===b?null===c:b.i(c))?(b=this.Di,c=a.Di,b=null===b?null===c:b.i(c)):b=!1,b&&(b=this.mx,c=a.mx,null===b?null===c:b.i(c)))&&(b=this.Ns,c=a.Ns,null===b?null===c:b.i(c)))return b=this.ei, a=a.ei,null===b?null===a:b.i(a)}return!1};f.fd=function(){return this.pb};f.CB=function(){return this.pb};f.$classData=q({qX:0},!1,"mlscript.NuTypeDef",{qX:1,mX:1,ih:1,g:1,jh:1,Ta:1,md:1,xd:1,nX:1,MX:1,E:1,v:1,l:1});function em(a){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Iq=a;W5(this)}em.prototype=new X5;em.prototype.constructor=em;f=em.prototype;f.H=function(){return"Quoted"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Iq:$K(W(),a)}; f.D=function(a){return a instanceof em};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof em){var b=this.Iq;a=a.Iq;return null===b?null===a:b.i(a)}return!1};f.$classData=q({WX:0},!1,"mlscript.Quoted",{WX:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function yl(a){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.ll=a;W5(this)}yl.prototype=new X5;yl.prototype.constructor=yl; f=yl.prototype;f.H=function(){return"Rcd"};f.G=function(){return 1};f.I=function(a){return 0===a?this.ll:$K(W(),a)};f.D=function(a){return a instanceof yl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof yl){var b=this.ll;a=a.ll;return null===b?null===a:b.i(a)}return!1};f.$classData=q({XX:0},!1,"mlscript.Rcd",{XX:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function cm(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Ys=a;this.Zs=b;W5(this)}cm.prototype=new X5;cm.prototype.constructor=cm;f=cm.prototype;f.H=function(){return"Rft"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Ys;case 1:return this.Zs;default:return $K(W(),a)}};f.D=function(a){return a instanceof cm};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof cm){var b=this.Ys,c=a.Ys;if(null===b?null===c:b.i(c))return b=this.Zs,a=a.Zs,null===b?null===a:b.i(a)}return!1};f.$classData=q({$X:0},!1,"mlscript.Rft",{$X:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Ql(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Vl=a;this.ml=b;W5(this)}Ql.prototype=new X5;Ql.prototype.constructor=Ql;f=Ql.prototype;f.H=function(){return"Sel"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.Vl;case 1:return this.ml;default:return $K(W(),a)}};f.D=function(a){return a instanceof Ql};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Ql){var b=this.Vl,c=a.Vl;if(null===b?null===c:b.i(c))return b=this.ml,a=a.ml,null===b?null===a:b.i(a)}return!1};f.$classData=q({dY:0},!1,"mlscript.Sel",{dY:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function Vl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.lp=a;this.mp=b;W5(this)}Vl.prototype=new X5;Vl.prototype.constructor=Vl;f=Vl.prototype;f.H=function(){return"Subs"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.lp;case 1:return this.mp;default:return $K(W(),a)}};f.D=function(a){return a instanceof Vl};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof Vl){var b=this.lp,c=a.lp;if(null===b?null===c:b.i(c))return b=this.mp,a=a.mp,null===b?null===a:b.i(a)}return!1};f.$classData=q({nY:0},!1,"mlscript.Subs",{nY:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function am(){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;W5(this)}am.prototype=new X5;am.prototype.constructor=am;f=am.prototype;f.H=function(){return"Super"};f.G=function(){return 0}; f.I=function(a){return $K(W(),a)};f.D=function(a){return a instanceof am};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){return a instanceof am};f.$classData=q({oY:0},!1,"mlscript.Super",{oY:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Il(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.nl=a;this.np=b;W5(this)}Il.prototype=new X5;Il.prototype.constructor=Il;f=Il.prototype;f.H=function(){return"TyApp"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.nl;case 1:return this.np;default:return $K(W(),a)}};f.D=function(a){return a instanceof Il};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Il){var b=this.nl,c=a.nl;if(null===b?null===c:b.i(c))return b=this.np,a=a.np,null===b?null===a:b.i(a)}return!1};f.$classData=q({tY:0},!1,"mlscript.TyApp",{tY:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function Sv(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.Fd=b;this.Fx=c;pY(this,a)}Sv.prototype=new $5;Sv.prototype.constructor=Sv;f=Sv.prototype;f.Zj=function(){return this.Fd};f.ma=function(){return this.Fx};f.Ea=function(){return this.Fd.Ea()};f.ub=function(a,b){return this.Fd.ub(a,b)};f.u=function(){return"Array\u2039"+this.Fd+"\u203a"};f.H=function(){return"ArrayType"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Fd:$K(W(),a)}; f.D=function(a){return a instanceof Sv};f.At=function(a,b,c,d){return new Sv(this.q,LX(this.Fd,a,b,c,d),this.Fx)};f.qv=function(a,b,c,d){return new Sv(this.q,LX(this.Fd,a,b,c,d),this.Fx)};f.$classData=q({WY:0},!1,"mlscript.TyperDatatypes$ArrayType",{WY:1,eO:1,qA:1,Gx:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1}); function ega(a){if(0===(1&a.ft)<<24>>24){var b=a.fo,c=h=>{if(h instanceof fe)return h.aa.Ea();if(h instanceof Ud)return h.fa.Ea();throw new w(h);};if(b===u())c=u();else{var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}c=d}d=Fq();a.HI=hH(c,d)|0;a.ft=(1|a.ft)<<24>>24}return a.HI} function fga(a){if(0===(2&a.ft)<<24>>24){var b=a.fo,c=h=>{if(h instanceof fe){h=h.aa;if(h instanceof Vv)return h.Zj();no()}else{if(h instanceof Ud)return h.fa;throw new w(h);}};if(b===u())c=u();else{var d=b.e(),e=d=new z(c(d),u());for(b=b.f();b!==u();){var g=b.e();g=new z(c(g),u());e=e.p=g;b=b.f()}c=d}c=c.m();if(!c.s())throw nv("empty.reduceLeft");d=!0;for(b=null;c.s();)e=c.t(),d?(b=e,d=!1):b=xw(b,e,V(b.Va));a.GI=b;a.ft=(2|a.ft)<<24>>24}return a.GI} function Wv(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.HI=this.dd=0;this.GI=null;this.ft=0;this.fo=b;this.Uu=c;pY(this,a);At(tp(),!b.b())}Wv.prototype=new $5;Wv.prototype.constructor=Wv;f=Wv.prototype;f.ma=function(){return this.Uu};f.Ea=function(){return 0===(1&this.ft)<<24>>24?ega(this):this.HI}; f.qv=function(a,b,c,d){var e=this.q,g=this.fo,h=n=>{if(n instanceof fe)return n=n.aa,t(),n=n.Kc(a,b,c,d),new fe(n);if(n instanceof Ud)return n=n.fa,t(),n=LX(n,a,b,c,d),new Ud(n);throw new w(n);};if(g===u())h=u();else{var k=g.e(),l=k=new z(h(k),u());for(g=g.f();g!==u();){var m=g.e();m=new z(h(m),u());l=l.p=m;g=g.f()}h=k}return new Wv(e,h,this.Uu)}; f.ub=function(a,b){var c=this.fo,d=k=>{if(k instanceof fe)return k.aa.ub(a,b);if(k instanceof Ud)return k.fa.ub(a,b);throw new w(k);};if(c===u())d=u();else{var e=c.e(),g=e=new z(d(e),u());for(c=c.f();c!==u();){var h=c.e();h=new z(d(h),u());g=g.p=h;c=c.f()}d=e}e=Fq();return hH(d,e)|0};f.Zj=function(){return 0===(2&this.ft)<<24>>24?fga(this):this.GI}; function sB(a,b,c,d,e){var g=a.q,h=a.fo;a=n=>{if(n instanceof fe)return n=n.aa,t(),n=b.n(n),new fe(n);if(n instanceof Ud)return n=n.fa,t(),n=cB(n,c,d),new Ud(n);throw new w(n);};if(h===u())a=u();else{var k=h.e(),l=k=new z(a(k),u());for(h=h.f();h!==u();){var m=h.e();m=new z(a(m),u());l=l.p=m;h=h.f()}a=k}return new Wv(g,a,e)}f.H=function(){return"SpliceType"};f.G=function(){return 1};f.I=function(a){return 0===a?this.fo:$K(W(),a)};f.D=function(a){return a instanceof Wv};f.u=function(){return VK(this)}; f.At=function(a,b,c,d){return this.qv(a,b,c,d)};f.$classData=q({AZ:0},!1,"mlscript.TyperDatatypes$SpliceType",{AZ:1,eO:1,qA:1,Gx:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function gga(a){if(0===(2&a.Oj)<<24>>24){var b=a.Yb.m();b=new Ef(b,new y(d=>d.j().Ea()));var c=Fq();b=Jq(b,c);a.KI=(b.b()?0:b.o())|0;a.Oj=(2|a.Oj)<<24>>24}return a.KI} function hga(a){if(0===(8&a.Oj)<<24>>24){var b=a.q,c=Hf(a.Yb),d=k=>{if(null!==k){var l=k.h(),m=k.Sc();if(null!==l)return k=l.j(),G(new H,new vl(""+m),k)}throw new w(k);};if(c===u())d=u();else{var e=c.e(),g=e=new z(d(e),u());for(c=c.f();c!==u();){var h=c.e();h=new z(d(h),u());g=g.p=h;c=c.f()}d=e}a.LI=new Qv(b,d,a.Nq);a.Oj=(8|a.Oj)<<24>>24}return a.LI} function zv(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.RO=null;this.KI=0;this.LI=this.SO=null;this.Oj=0;this.Yb=b;this.Nq=c;pY(this,a)}zv.prototype=new $5;zv.prototype.constructor=zv;f=zv.prototype;f.ma=function(){return this.Nq}; f.Zj=function(){if(0===(1&this.Oj)<<24>>24&&0===(1&this.Oj)<<24>>24){var a=this.Yb;if(a===u())var b=u();else{b=a.e();var c=b=new z(b.j(),u());for(a=a.f();a!==u();){var d=a.e();d=new z(d.j(),u());c=c.p=d;a=a.f()}}if(b.b())b=R();else{b=b.m();if(!b.s())throw nv("empty.reduceLeft");c=!0;for(d=null;b.s();)a=b.t(),c?(d=a,c=!1):d=xw(d,a,V(d.Va));b=new L(d)}b.b()?(b=this.q.ib,c=V(this.q),b=new Uw(b.q,R(),b,c)):b=b.o();this.RO=b;this.Oj=(1|this.Oj)<<24>>24}return this.RO}; f.Ea=function(){return 0===(2&this.Oj)<<24>>24?gga(this):this.KI};f.ub=function(a,b){var c=this.Yb.m();c=new Ef(c,new y(e=>e.j().ub(a,b)));var d=Fq();c=Jq(c,d);return(c.b()?this.q.Gd:c.o())|0};function x8(a,b,c,d,e){var g=a.q;lv();return new zv(g,ry(0,a.Yb,new y(h=>LX(h,b,c,d,e))),a.Nq)}function Dw(a){0===(4&a.Oj)<<24>>24&&0===(4&a.Oj)<<24>>24&&(a.SO=new Sv(a.q,a.Zj(),a.Nq),a.Oj=(4|a.Oj)<<24>>24);return a.SO}f.kq=function(){return 0===(8&this.Oj)<<24>>24?hga(this):this.LI}; function r8(a){var b=a.Yb;a=g=>{var h=g.h();return(h.b()?"":h.o().x+": ")+g.j()+","};if(b===u())a=u();else{var c=b.e(),d=c=new z(a(c),u());for(b=b.f();b!==u();){var e=b.e();e=new z(a(e),u());d=d.p=e;b=b.f()}a=c}return ze(a,""," ","")}f.u=function(){return"("+r8(this)+")"};f.H=function(){return"TupleType"};f.G=function(){return 1};f.I=function(a){return 0===a?this.Yb:$K(W(),a)};f.D=function(a){return a instanceof zv};f.At=function(a,b,c,d){return x8(this,a,b,c,d)}; f.qv=function(a,b,c,d){return x8(this,a,b,c,d)};f.$classData=q({EZ:0},!1,"mlscript.TyperDatatypes$TupleType",{EZ:1,eO:1,qA:1,Gx:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,E:1,v:1,l:1});function fm(a){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.jt=a;W5(this)}fm.prototype=new X5;fm.prototype.constructor=fm;f=fm.prototype;f.H=function(){return"Unquoted"};f.G=function(){return 1};f.I=function(a){return 0===a?this.jt:$K(W(),a)};f.D=function(a){return a instanceof fm}; f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof fm){var b=this.jt;a=a.jt;return null===b?null===a:b.i(a)}return!1};f.$classData=q({c_:0},!1,"mlscript.Unquoted",{c_:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function $l(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Yq=a;this.lt=b;W5(this)}$l.prototype=new X5;$l.prototype.constructor=$l;f=$l.prototype;f.H=function(){return"Where"}; f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.Yq;case 1:return this.lt;default:return $K(W(),a)}};f.D=function(a){return a instanceof $l};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof $l){var b=this.Yq,c=a.Yq;if(null===b?null===c:b.i(c))return b=this.lt,a=a.lt,null===b?null===a:b.i(a)}return!1};f.$classData=q({j_:0},!1,"mlscript.Where",{j_:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1}); function dm(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.$q=a;this.Zq=b;W5(this)}dm.prototype=new X5;dm.prototype.constructor=dm;f=dm.prototype;f.H=function(){return"While"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.$q;case 1:return this.Zq;default:return $K(W(),a)}};f.D=function(a){return a instanceof dm};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){if(this===a)return!0;if(a instanceof dm){var b=this.$q,c=a.$q;if(null===b?null===c:b.i(c))return b=this.Zq,a=a.Zq,null===b?null===a:b.i(a)}return!1};f.$classData=q({k_:0},!1,"mlscript.While",{k_:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function Tl(a,b){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.br=a;this.ar=b;W5(this)}Tl.prototype=new X5;Tl.prototype.constructor=Tl;f=Tl.prototype;f.H=function(){return"With"};f.G=function(){return 2}; f.I=function(a){switch(a){case 0:return this.br;case 1:return this.ar;default:return $K(W(),a)}};f.D=function(a){return a instanceof Tl};f.B=function(){return AL(this)};f.u=function(){return VK(this)};f.i=function(a){if(this===a)return!0;if(a instanceof Tl){var b=this.br,c=a.br;if(null===b?null===c:b.i(c))return b=this.ar,a=a.ar,null===b?null===a:b.i(a)}return!1};f.$classData=q({m_:0},!1,"mlscript.With",{m_:1,Oe:1,g:1,ce:1,ud:1,md:1,xd:1,Ta:1,Od:1,Pe:1,E:1,v:1,l:1});function y8(){}y8.prototype=new q3; y8.prototype.constructor=y8;function z8(){}f=z8.prototype=y8.prototype;f.D=function(){return!0};f.i=function(a){return i3(this,a)};f.B=function(){return qS(this)};f.u=function(){return w0(this)};f.gn=function(a){return IT(this,a)};f.tl=function(a){return this.gn(a)};f.ka=function(){return this.K()};f.Ii=function(a){return NY(this,a)};f.Sd=function(){return this.ad().m()};f.NJ=function(a){return 0<=a&&0b))}function jV(a){return!!(a&&a.$classData&&a.$classData.rb.Pr)}function Dl(){this.zd=this.yd=this.nd=this.oc=null;this.Bd=this.Cd=0;this.Dd=this.Ad=null;this.gc=0;this.Dh=null}Dl.prototype=new D8;Dl.prototype.constructor=Dl;function U8(){}U8.prototype=Dl.prototype;function Ep(a){this.df=this.cf=this.jf=null;this.ff=this.gf=0;this.hf=this.ef=null;this.sc=0;this.Eg=this.Dg=null;this.Qe=0;this.XN=null;this.jI=!1;this.V=a;Nq(this)}Ep.prototype=new U2; Ep.prototype.constructor=Ep;f=Ep.prototype;f.sl=function(a){return pa(this.V,a.V)};f.Zr=function(){if(!this.jI&&!this.jI){var a=new vl(this.V),b=this.A();this.XN=Cq(a,b);this.jI=!0}return this.XN};f.Ua=function(){return this.V};f.H=function(){return"TypeName"};f.G=function(){return 1};f.I=function(a){return 0===a?this.V:$K(W(),a)};f.D=function(a){return a instanceof Ep};f.B=function(){return AL(this)};f.u=function(){return VK(this)}; f.i=function(a){return this===a?!0:a instanceof Ep?this.V===a.V:!1};f.$l=function(a){return pa(this.V,a.V)};f.$classData=q({CY:0},!1,"mlscript.TypeName",{CY:1,Vz:1,Fi:1,ih:1,g:1,jh:1,Ta:1,Gi:1,JW:1,Kaa:1,yj:1,nf:1,IW:1,E:1,v:1,l:1});function YB(a,b,c,d,e){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.EO=this.dd=0;this.DO=null;this.pA=b;this.Ym=c;this.jZ=d;this.ME=e;pY(this,a);this.EO=a.Gd;this.DO=c.hi}YB.prototype=new P7;YB.prototype.constructor=YB;f=YB.prototype;f.ma=function(){return this.jZ}; f.Ea=function(){return this.EO};f.ub=function(){return 0};f.u=function(){return this.pA?"\u22a5("+this.Ym+")":"\u22a4("+this.Ym+")"};f.H=function(){return"Extruded"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.pA;case 1:return this.Ym;default:return $K(W(),a)}};f.D=function(a){return a instanceof YB};f.uo=function(){return this.DO};f.$classData=q({iZ:0},!1,"mlscript.TyperDatatypes$Extruded",{iZ:1,IE:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,uA:1,yj:1,nf:1,Hx:1,XO:1,E:1,v:1,l:1}); function mx(a,b,c){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.Tu=this.dd=0;this.hi=b;this.et=c;pY(this,a);this.Tu=b.Xa}mx.prototype=new P7;mx.prototype.constructor=mx;f=mx.prototype;f.ma=function(){return this.et};f.ub=function(a,b){return this.hi.ub(a,b)};f.Ea=function(){return this.Tu};f.u=function(){var a=S7(this.hi),b=0<=a.length&&"'"===a.substring(0,1)?Of(Q(),a,1,a.length):a;a=Iu(Q(),a);return"\u2018"+b+(Pe(new E(hc(a)),hc(39))?"_":"")+YC(this.q,this.Tu)};f.H=function(){return"SkolemTag"}; f.G=function(){return 1};f.I=function(a){return 0===a?this.hi:$K(W(),a)};f.D=function(a){return a instanceof mx};f.uo=function(){return this.hi};f.$classData=q({zZ:0},!1,"mlscript.TyperDatatypes$SkolemTag",{zZ:1,IE:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,uA:1,yj:1,nf:1,Hx:1,XO:1,E:1,v:1,l:1});function nC(a,b,c,d){this.q=null;this.pe=this.Be=this.ze=0;this.oe=null;this.Ae=!1;this.dd=0;this.QO=null;this.II=!1;this.rp=b;this.JI=c;this.DZ=d;pY(this,a)}nC.prototype=new P7;nC.prototype.constructor=nC;f=nC.prototype; f.u=function(){return oY(this)};f.Fv=function(){this.II||this.II||(this.QO=nY(this),this.II=!0);return this.QO};f.AK=function(){return this.JI};f.ma=function(){return this.DZ};f.ub=function(){return this.q.Gd};f.Ea=function(){return this.q.Gd};f.H=function(){return"TraitTag"};f.G=function(){return 2};f.I=function(a){switch(a){case 0:return this.rp;case 1:return this.JI;default:return $K(W(),a)}};f.D=function(a){return a instanceof nC};f.uo=function(){return this.rp};f.rr=function(){return this.rp}; f.$classData=q({CZ:0},!1,"mlscript.TyperDatatypes$TraitTag",{CZ:1,IE:1,qp:1,Tg:1,Gg:1,g:1,Hg:1,Ug:1,uA:1,yj:1,nf:1,Hx:1,KO:1,E:1,v:1,l:1});function V8(){}V8.prototype=new B8;V8.prototype.constructor=V8;function W8(){}f=W8.prototype=V8.prototype;f.m=function(){return Kr(new Lr,this)};f.Sd=function(){return uZ(new vZ,this)};f.Qh=function(a){return o0(new p0,a,this)};f.Lh=function(a){return r0(new s0,this,a)};f.Jh=function(a){return X8(new Y8,this,a)};f.Oh=function(a){return u0(new v0,this,a)}; f.Oc=function(){return"IndexedSeqView"};f.mf=function(a,b){return l0(this,a,b)};f.ad=function(){return new Z8(this)};f.e=function(){return this.va(0)};f.Mc=function(){return SF(this)};f.ab=function(a){var b=this.K();return b===a?0:bG(new H,b.h(),this.XR.n(b.j()))))}; f.U=function(a){a=this.EG.U(a);var b=this.XR;return a.b()?R():new L(b.n(a.o()))};f.Q=function(){return this.EG.Q()};f.b=function(){return this.EG.b()};f.$classData=q({R5:0},!1,"scala.collection.MapView$MapValues",{R5:1,zR:1,Td:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,jd:1,l:1,oL:1,ek:1,za:1,la:1}); function lY(a,b){if(a===b)return!0;if(b&&b.$classData&&b.$classData.rb.YB){var c=b.se(),d=a.se();if(null===c?null===d:c.i(d)){if(a.ka()===b.ka()){c=a.m();b=b.m();for(d=!0;d&&c.s();){d=c.t();var e=b.t();d=a.se().Yi(d.h(),e.h())&&ml(nl(),d.j(),e.j())}return d}return!1}}return v8(a,b)}function a9(){}a9.prototype=new u8;a9.prototype.constructor=a9;function b9(){}b9.prototype=a9.prototype;a9.prototype.Ub=function(){return Bq()};a9.prototype.wy=function(a){a=a.m();for(var b=this;a.s();){var c=a.t();b=b.Ek(c)}return b}; function r0(a,b,c){b8(a,b,c);return a}function s0(){this.My=null;this.Tv=this.cC=0;this.Gy=null;this.KG=0}s0.prototype=new I8;s0.prototype.constructor=s0;function c9(){}f=c9.prototype=s0.prototype;f.m=function(){return Kr(new Lr,this)};f.Sd=function(){return uZ(new vZ,this)};f.Qh=function(a){return o0(new p0,a,this)};f.Lh=function(a){return r0(new s0,this,a)};f.Jh=function(a){return X8(new Y8,this,a)};f.Oh=function(a){return u0(new v0,this,a)};f.Oc=function(){return"IndexedSeqView"}; f.mf=function(a,b){return l0(this,a,b)};f.ad=function(){return new Z8(this)};f.e=function(){return this.va(0)};f.Mc=function(){return SF(this)};f.ab=function(a){var b=this.K();return b===a?0:b>31;var k=g>>>31|0|g>>31<<1;for(g=(h===k?(-2147483648^c)>(-2147483648^g<<1):h>k)?g:c;eb))}function Xba(a){return Q9(new R9,a,new y(()=>0))}function S9(a,b){return a.Id===b?a:new ZU(b)} function T9(a,b){b=b.m();for(var c=a.Id;b.s();){var d=b.t(),e=My(W(),d),g=$G(bH(),e);c=DU(c,d,e,g,0);if(c!==a.Id){if(0===c.Fb)return dV().Fo;for(;b.s();)if(a=b.t(),d=My(W(),a),e=$G(bH(),d),EU(c,a,d,e),0===c.Fb)return dV().Fo;return new ZU(c)}}return a}function ZU(a){this.Id=a}ZU.prototype=new b9;ZU.prototype.constructor=ZU;f=ZU.prototype;f.Gb=function(a){return XY(this,a)};f.Ja=function(a){return YY(this,a)};f.Ub=function(){return dV()};f.Q=function(){return this.Id.Fb};f.ka=function(){return this.Id.Fb}; f.b=function(){return 0===this.Id.Fb};f.m=function(){return this.b()?Rq().Pa:new y1(this.Id)};f.L=function(a){var b=My(W(),a),c=$G(bH(),b);return this.Id.yt(a,b,c,0)};function B9(a,b){var c=My(W(),b),d=$G(bH(),c);b=yU(a.Id,b,c,d,0);return S9(a,b)} function Gga(a,b){if(b instanceof ZU){if(a.b())return b;var c=JU(a.Id,b.Id,0);return c===b.Id?b:S9(a,c)}if(b instanceof KV)for(b=new E4(b),c=a.Id;b.s();){var d=b.t(),e=G4(d.nk),g=$G(bH(),e);c=yU(c,d.xm,e,g,0);if(c!==a.Id){for(a=lI(HH(),kI(HH(),g,0));b.s();)d=b.t(),e=G4(d.nk),g=$G(bH(),e),a=BU(c,d.xm,e,g,0,a);return new ZU(c)}}else for(b=b.m(),c=a.Id;b.s();)if(d=b.t(),e=My(W(),d),g=$G(bH(),e),c=yU(c,d,e,g,0),c!==a.Id){for(a=lI(HH(),kI(HH(),g,0));b.s();)d=b.t(),e=My(W(),d),g=$G(bH(),e),a=BU(c,d,e,g, 0,a);return new ZU(c)}return a}f.e=function(){return this.m().t()};f.Mc=function(){return(new z1(this.Id)).t()};f.Ca=function(a){this.Id.Ca(a)};f.i=function(a){if(a instanceof ZU){if(this===a)return!0;var b=this.Id;a=a.Id;return null===b?null===a:b.i(a)}return kY(this,a)};f.Ih=function(){return"HashSet"};f.B=function(){var a=new x1(this.Id);return CL(BL(),a,BL().PB)}; function Hga(a,b){if(a.b())return a;if(b instanceof ZU)return b.b()?a:0===HU(a.Id,b.Id,0).Fb?dV().Fo:S9(a,HU(a.Id,b.Id,0));if(b instanceof KV){for(var c=new E4(b),d=a.Id;c.s();){var e=c.t(),g=G4(e.nk),h=$G(bH(),g);d=DU(d,e.xm,g,h,0);if(d!==a.Id){if(0===d.Fb)return dV().Fo;for(;c.s();)if(a=c.t(),e=G4(a.nk),g=$G(bH(),e),EU(d,a.xm,e,g),0===d.Fb)return dV().Fo;return new ZU(d)}}return a}c=b.Q();return 0===c?a:c<=a.Id.Fb?T9(a,b):U9(a,new y(k=>b.L(k)),!0)} function Iga(a,b){return b&&b.$classData&&b.$classData.rb.fk?Hga(a,b):b instanceof tZ&&b.K()>a.Id.Fb?U9(a,new y(c=>ha(c)?!pB(b,c|0):!0),!1):T9(a,b)}function U9(a,b,c){b=GU(a.Id,b,c);return b===a.Id?a:0===b.Fb?dV().Fo:new ZU(b)}f.Bb=function(a){return $Y(this,a)};f.Jc=function(a){return zT(this,a)};f.wy=function(a){return Iga(this,a)};f.Ce=function(a){return Gga(this,a)};f.Ek=function(a){var b=My(W(),a),c=$G(bH(),b);a=DU(this.Id,a,b,c,0);return S9(this,a)};f.bc=function(a){return B9(this,a)}; f.$classData=q({a7:0},!1,"scala.collection.immutable.HashSet",{a7:1,Fr:1,rm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,fk:1,Cl:1,la:1,v:1,dq:1,xc:1,Rr:1,F8:1,i6:1,Cb:1,sg:1,l:1});function V9(){}V9.prototype=new u8;V9.prototype.constructor=V9;function W9(){}W9.prototype=V9.prototype;V9.prototype.oh=function(a){return EB(this,a)};V9.prototype.he=function(){};V9.prototype.zc=function(a){return lR(this,a)};V9.prototype.Kb=function(){return this}; var Kga=function Jga(a,b){FK();return new gZ(new U(()=>{if(a.b())return hV();FK();var d=b.n(hZ(a).e()),e=Jga(hZ(a).Lf(),b);return new eV(d,e)}))},Mga=function Lga(a,b){if(b.b())return hV();FK();var d=hZ(a).e();FK();return new eV(d,new gZ(new U(()=>Lga(hZ(a).Lf(),hZ(b).Lf()))))}; function X9(a,b,c,d,e){b.ja=""+b.ja+c;if(!a.Dl)b.ja+="\x3cnot computed\x3e";else if(!a.b()){c=hZ(a).e();b.ja=""+b.ja+c;c=a;var g=hZ(a).Lf();if(c!==g&&(!g.Dl||hZ(c)!==hZ(g))&&(c=g,g.Dl&&!g.b()))for(g=hZ(g).Lf();c!==g&&g.Dl&&!g.b()&&hZ(c)!==hZ(g);){b.ja=""+b.ja+d;var h=hZ(c).e();b.ja=""+b.ja+h;c=hZ(c).Lf();g=hZ(g).Lf();g.Dl&&!g.b()&&(g=hZ(g).Lf())}if(!g.Dl||g.b()){for(;c!==g;)b.ja=""+b.ja+d,a=hZ(c).e(),b.ja=""+b.ja+a,c=hZ(c).Lf();c.Dl||(b.ja=""+b.ja+d,b.ja+="\x3cnot computed\x3e")}else{h=a;for(a=0;;){var k= h,l=g;if(k!==l&&hZ(k)!==hZ(l))h=hZ(h).Lf(),g=hZ(g).Lf(),a=1+a|0;else break}h=c;k=g;(h===k||hZ(h)===hZ(k))&&0a)a=1;else a:for(var b=this,c=0;;){if(c===a){a=b.b()?0:1;break a}if(b.b()){a=-1;break a}c=1+c|0;b=b.f()}return a};f.NJ=function(a){return F0(this,a)};f.va=function(a){return eB(this,a)}; f.qo=function(a){a:{for(var b=this;!b.b();){if(a.n(b.e())){a=!0;break a}b=b.f()}a=!1}return a};f.L=function(a){a:{for(var b=this;!b.b();){if(ml(nl(),b.e(),a)){a=!0;break a}b=b.f()}a=!1}return a};f.Lt=function(a){return G0(this,a)};f.wo=function(a,b){return H0(this,a,b)};function hZ(a){if(!a.IL&&!a.IL){if(a.JL)throw iL("self-referential LazyList or a derivation thereof has no more elements");a.JL=!0;try{var b=Es(a.nS)}finally{a.JL=!1}a.Dl=!0;a.nS=null;a.oS=b;a.IL=!0}return a.oS} f.b=function(){return hZ(this)===hV()};f.Q=function(){return this.Dl&&this.b()?0:-1};f.e=function(){return hZ(this).e()};function fZ(a){var b=a,c=a;for(b.b()||(b=hZ(b).Lf());c!==b&&!b.b();){b=hZ(b).Lf();if(b.b())break;b=hZ(b).Lf();if(b===c)break;c=hZ(c).Lf()}return a}f.m=function(){return this.Dl&&this.b()?Rq().Pa:new X0(this)};f.Ca=function(a){for(var b=this;!b.b();)a.n(hZ(b).e()),b=hZ(b).Lf()};f.De=function(a,b){for(var c=this;;){if(c.b())return a;var d=hZ(c).Lf();a=b.ba(a,hZ(c).e());c=d}}; f.Ih=function(){return"LazyList"};function Y9(a,b){FK();return new gZ(new U(()=>{if(a.b()){var c=Es(b);return c instanceof gZ?hZ(c):0===c.Q()?hV():iZ(FK(),c.m())}FK();c=hZ(a).e();var d=Y9(hZ(a).Lf(),b);return new eV(c,d)}))}function Nga(a,b){return a.Dl&&a.b()?aU(FK(),b):Y9(a,new U(()=>b))}f.th=function(a){if(this.b())throw nv("empty.reduceLeft");for(var b=hZ(this).e(),c=hZ(this).Lf();!c.b();)b=a.ba(b,hZ(c).e()),c=hZ(c).Lf();return b}; function Oga(a,b){FK();return new gZ(new U(()=>{FK();return new eV(b,a)}))}function Z9(a,b){return a.Dl&&a.b()?FK().Ry:Kga(a,b)}f.Gb=function(a){return G(new H,Z9(this,new y(b=>a.n(b).h())),Z9(this,new y(b=>a.n(b).j())))};function Pga(a,b){if(0>=b)return a;if(a.Dl&&a.b())return FK().Ry;FK();return new gZ(new U(()=>{for(var c=a,d=b;0=a?this:this.Dl&&this.b()?FK().Ry:Zea(FK(),this,a)};f.Ja=function(a){return Z9(this,a)};f.cc=function(a){return Oga(this,a)};f.gn=function(a){return Nga(this,a)};f.f=function(){return hZ(this).Lf()};f.Ub=function(){return FK()}; f.$classData=q({h7:0},!1,"scala.collection.immutable.LazyList",{h7:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,KL:1,CG:1,TR:1,pS:1,l:1});function Jr(a){this.Xh=a}Jr.prototype=new i9;Jr.prototype.constructor=Jr;f=Jr.prototype;f.D=function(a){return t9(this,a)};f.Oc=function(){return"IndexedSeq"};f.m=function(){return Kr(new Lr,new Mr(this.Xh))};f.Sd=function(){return uZ(new vZ,new Mr(this.Xh))};f.mf=function(a,b){return l0(this,a,b)};f.ad=function(){return new Z8(this)}; f.cc=function(a){return n0(this,a)};f.Jc=function(a){return q0(this,a)};f.Bb=function(a){return this.vc(X8(new Y8,this,a))};f.Ja=function(a){return t0(this,a)};f.e=function(){return hc(this.Xh.charCodeAt(0))};f.Mc=function(){return SF(this)};f.ab=function(a){var b=this.Xh.length;return b===a?0:b>>16|0;var g=$G(bH(),e);c=iU(c,d.Wk,d.Ah,e,g,0,!0);if(c!==a.dc){for(a=lI(HH(),kI(HH(),g,0));b.s();)d=b.t(),e=d.mk,e^=e>>>16|0,a=lU(c,d.Wk,d.Ah,e,$G(bH(),e),0,a);return new TU(c)}}return a}if(jV(b)){if(b.b())return a;c=new aZ(a);b.og(c);b=c.Zv;return b===a.dc?a:new TU(b)}b=b.m();return b.s()?(c=new aZ(a), cH(b,c),b=c.Zv,b===a.dc?a:new TU(b)):a}f.Ca=function(a){this.dc.Ca(a)};f.og=function(a){this.dc.og(a)};f.i=function(a){if(a instanceof TU){if(this===a)return!0;var b=this.dc;a=a.dc;return null===b?null===a:b.i(a)}return v8(this,a)};f.B=function(){if(this.b())return BL().mG;var a=new j1(this.dc);return CL(BL(),a,BL().Cr)};f.Ih=function(){return"HashMap"}; function Rga(a,b){if(a.b())return a;if(b instanceof ZU){if(b.b())return a;b=new YQ(b.Id);for(var c=a.dc;0=b.kd?Ez().Mr:new TU(b)}if(b instanceof KV){if(b.b())return a;b=new E4(b);for(d=a.dc;b.s();)if(c=b.t(),e=G4(c.nk),g=$G(bH(),e),d=nU(d,c.xm,e,g,0),0===d.kd)return Ez().Mr;b=d;return b===a.dc?a:new TU(b)}b=b.m();for(d=a.dc;b.s();)if(c=b.t(),e=My(W(),c),g=$G(bH(),e),d=nU(d,c,e,g,0), 0===d.kd)return Ez().Mr;b=d;return b===a.dc?a:new TU(b)}f.Jc=function(a){return zT(this,a)};f.Bb=function(a){return $Y(this,a)};f.dG=function(a){return Rga(this,a)};f.Mc=function(){return this.Sd().t()};f.e=function(){return this.m().t()};f.am=function(a){return Qga(this,a)};f.xj=function(a){return K9(this,a)};f.IC=function(a,b){return y5(this,a,b)};f.Em=function(a,b){return m7(this,a,b)};f.zr=function(){return this.Ht()}; f.$classData=q({V6:0},!1,"scala.collection.immutable.HashMap",{V6:1,Rt:1,qm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Mk:1,ek:1,za:1,la:1,um:1,v:1,Pr:1,xc:1,Xt:1,E8:1,RG:1,Cb:1,sg:1,l:1});function $9(){}$9.prototype=new D9;$9.prototype.constructor=$9;function c$(){}c$.prototype=$9.prototype;$9.prototype.zc=function(a){return lR(this,a)};function d$(){}d$.prototype=new F8;d$.prototype.constructor=d$;function e$(){}f=e$.prototype=d$.prototype;f.vj=function(){return Xu()}; f.kM=function(a){return Q9(new R9,this,a)};f.bj=function(a,b){this.U(a);this.bh(a,b)};f.bh=function(a,b){a=G(new H,a,b);this.$(a)};f.ou=function(a,b){return P8(this,a,b)};f.Hk=function(a,b){return pX(this,a,b)};f.JB=function(a){this.U(a).b()||this.Bm(a)};f.he=function(){};f.zc=function(a){return lR(this,a)};f.Ub=function(){return rO()};f.Kb=function(){return this};function f$(a){this.Jo=null;if(null===a)throw null;this.Jo=a}f$.prototype=new b9;f$.prototype.constructor=f$;f=f$.prototype;f.se=function(){return this.Jo.se()}; f.m=function(){return new I0(this.Jo)};f.L=function(a){return!this.Jo.U(a).b()};f.ka=function(){return this.Jo.ka()};f.Q=function(){return this.Jo.Q()};f.b=function(){return this.Jo.b()};f.Oc=function(){return"SortedSet"};f.i=function(a){return O8(this,a)};f.aj=function(a){return u5(this,a)};f.$i=function(a){return v5(this,a)};f.vc=function(a){return oA(uv(),a,this.Jo.se())};f.Gk=function(a){return oA(uv(),a,this.Jo.se())};f.Ek=function(a){return oA(uv(),this,this.Jo.se()).Ek(a)}; f.bc=function(a){return oA(uv(),this,this.Jo.se()).bc(a)};f.$classData=q({B8:0},!1,"scala.collection.immutable.SortedMapOps$ImmutableKeySortedSet",{B8:1,Fr:1,rm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,fk:1,Cl:1,la:1,v:1,dq:1,xc:1,Rr:1,OL:1,PG:1,fS:1,Iy:1,eS:1,D8:1,UB:1,c6:1}); function g$(a,b,c){var d=c&(-1+a.zg.a.length|0),e=a.zg.a[d];if(null===e)a.zg.a[d]=new cK(b,c,null);else{for(var g=null,h=e;null!==h&&h.nk<=c;){if(h.nk===c&&ml(nl(),b,h.xm))return!1;g=h;h=h.yg}null===g?a.zg.a[d]=new cK(b,c,e):g.yg=new cK(b,c,g.yg)}a.ym=1+a.ym|0;return!0} function h$(a,b){var c=a.zg.a.length;a.YL=Eb(b*a.hH);if(0===a.ym)a.zg=new (md(dK).Ia)(b);else{var d=a.zg;a.zg=zj(Pj(),d,b);d=new cK(null,0,null);for(var e=new cK(null,0,null);c>Math.clz32(a)&a)<<1;return 1073741824>a?a:1073741824}function JV(a,b,c){a.hH=c;a.zg=new (md(dK).Ia)(i$(b));a.YL=Eb(a.zg.a.length*a.hH);a.ym=0;return a}function Vn(){var a=new KV;JV(a,16,.75);return a}function KV(){this.hH=0;this.zg=null;this.ym=this.YL=0}KV.prototype=new W9;KV.prototype.constructor=KV;f=KV.prototype;f.Gb=function(a){return XY(this,a)};f.Ja=function(a){return YY(this,a)};f.Bb=function(a){return $Y(this,a)};f.ka=function(){return this.ym}; function G4(a){return a^(a>>>16|0)}f.L=function(a){var b=G4(My(W(),a)),c=this.zg.a[b&(-1+this.zg.a.length|0)];if(null===c)a=null;else a:for(;;){if(b===c.nk&&ml(nl(),a,c.xm)){a=c;break a}if(null===c.yg||c.nk>b){a=null;break a}c=c.yg}return null!==a};f.he=function(a){a=i$(Eb((1+a|0)/this.hH));a>this.zg.a.length&&h$(this,a)};f.oh=function(a){(1+this.ym|0)>=this.YL&&h$(this,this.zg.a.length<<1);return g$(this,a,G4(My(W(),a)))}; function IV(a,b){a.he(b.Q());if(b instanceof ZU)return b.Id.FJ(new fn((d,e)=>{g$(a,d,G4(e|0))})),a;if(b instanceof KV){for(b=new E4(b);b.s();){var c=b.t();g$(a,c.xm,c.nk)}return a}return lR(a,b)}f.IB=function(a){a:{var b=G4(My(W(),a)),c=b&(-1+this.zg.a.length|0),d=this.zg.a[c];if(null!==d)if(d.nk===b&&ml(nl(),d.xm,a))this.zg.a[c]=d.yg,this.ym=-1+this.ym|0;else for(c=d,d=d.yg;null!==d&&d.nk<=b;){if(d.nk===b&&ml(nl(),d.xm,a)){c.yg=d.yg;this.ym=-1+this.ym|0;break a}c=d;d=d.yg}}};f.m=function(){return new D4(this)}; f.mg=function(){var a=this.zg;yj(Pj(),a,null);this.ym=0};f.Ub=function(){return pz()};f.Q=function(){return this.ym};f.b=function(){return 0===this.ym};f.Ca=function(a){for(var b=this.zg.a.length,c=0;c>31,d=a.Vd;a=d>>31;d=b-d|0;return new ma(d,(-2147483648^d)>(-2147483648^b)?-1+(c-a|0)|0:c-a|0)}function k$(a){var b=j$(a),c=a.lc,d=c>>31;a=xa();b=Ri(a,b.W,b.Y,c,d);a=a.Qc;return 0===b&&0===a} function l$(a,b,c,d){a.Vd=b;a.Sk=c;a.lc=d;a.fj=b>c&&0d||b===c&&!a.kn();if(0===d)throw Kj("step cannot be 0.");if(a.fj)b=0;else{b=j$(a);var e=a.lc,g=e>>31;var h=xa();b=$h(h,b.W,b.Y,e,g);h=h.Qc;g=a.kn()||!k$(a)?1:0;e=g>>31;g=b+g|0;h=new ma(g,(-2147483648^g)<(-2147483648^b)?1+(h+e|0)|0:h+e|0);b=h.W;h=h.Y;b=(0===h?-1<(-2147483648^b):0>31,b=Ri(xa(),b.W,b.Y,d,h),c=0!==b?c-b|0:a.kn()?c: c-d|0}a.gw=c}function tZ(){this.lc=this.Sk=this.Vd=0;this.fj=!1;this.gw=this.Tk=0}tZ.prototype=new i9;tZ.prototype.constructor=tZ;function m$(){}f=m$.prototype=tZ.prototype;f.Ii=function(a){return ky(this,a)};f.cc=function(a){return F3(this,a)};f.gn=function(a){return G3(this,a)};f.Gb=function(a){return XY(this,a)};f.D=function(a){return t9(this,a)};f.Ik=function(){return xe()};f.Oc=function(){return"IndexedSeq"};f.Sd=function(){var a=new QX(this);return uZ(new vZ,a)}; f.mf=function(a,b){return l0(this,a,b)};f.ad=function(){return new Z8(this)};f.ab=function(a){var b=this.K();return b===a?0:bthis.Tk?$Q(bR(),this.Vd,this.Sk,this.lc,this.kn()):this.Tk};function n$(a){if(a.fj)throw a=aR("last"),a instanceof $d?a.mu:a;return a.gw}function o$(a){if(a.fj)throw a=aR("head"),a instanceof $d?a.mu:a;return a.Vd} function p$(a){0>a.Tk&&$Q(bR(),a.Vd,a.Sk,a.lc,a.kn())}f.Ca=function(a){if(!this.fj)for(var b=this.Vd;;){a.n(b);if(b===this.gw)break;b=b+this.lc|0}};f.Lt=function(a){if(a instanceof tZ){var b=this.K();switch(b){case 0:return a.fj;case 1:return 1===a.K()&&this.Vd===a.Vd;default:return a.K()===b&&this.Vd===a.Vd&&this.lc===a.lc}}else return u9(this,a)}; function Sga(a,b){if(0>=b)return a;if(0<=a.Tk)return b=a.Tk-b|0,0>=b||a.fj?(b=a.Vd,a=new oB(b,b,a.lc)):a=b>=a.Tk&&0<=a.Tk?a:new q$(a.Vd,a.Vd+Math.imul(a.lc,-1+b|0)|0,a.lc),a;b=n$(a)-Math.imul(a.lc,b)|0;return 0a.lc&&b>a.Vd?(b=a.Vd,new oB(b,b,a.lc)):new q$(a.Vd,b,a.lc)}function pB(a,b){return!(b===a.Sk&&!a.kn())&&(0a.Sk)&&(1===a.lc||0===Bb(b-a.Vd|0,a.lc)):!(ba.Vd)&&(-1===a.lc||0===Bb(b-a.Vd|0,a.lc)))}f.L=function(a){return ha(a)?pB(this,a|0):QU(this,a)}; f.dy=function(){return 2147483647};f.i=function(a){if(a instanceof tZ){if(this.fj)return a.fj;if(a.fj||this.Vd!==a.Vd)return!1;var b=n$(this);return b===n$(a)&&(this.Vd===b||this.lc===a.lc)}return i3(this,a)};f.B=function(){if(2<=this.K()){var a=BL(),b=this.lc,c=this.gw;return yL(a.C(a.C(a.C(a.rg,this.Vd),b),c))}return qS(this)};f.u=function(){var a=this.kn()?"to":"until",b=1===this.lc?"":" by "+this.lc;return(this.fj?"empty ":k$(this)?"":"inexact ")+"Range "+this.Vd+" "+a+" "+this.Sk+b};f.Ih=function(){return"Range"}; f.wt=function(a){p$(this);if(0>a||a>=this.Tk)throw aL(new bL,a+" is out of bounds (min 0, max "+(-1+this.Tk|0)+")");return this.Vd+Math.imul(this.lc,a)|0};f.Ub=function(){return xe()};f.Zh=function(a){return a===Fq()?0=a||this.fj)a=this;else if(a>=this.Tk&&0<=this.Tk)a=this.Sk,a=new oB(a,a,this.lc);else{a=this.Vd+Math.imul(this.lc,a)|0;var b=this.Sk,c=this.lc;a=this.kn()?new q$(a,b,c):new oB(a,b,c)}return a};f.n=function(a){return this.wt(a|0)};f.va=function(a){return this.wt(a)}; f.Ja=function(a){p$(this);return YY(this,a)};f.f=function(){if(this.fj){var a=aR("tail");throw a instanceof $d?a.mu:a;}1===this.Tk?(a=this.Sk,a=new oB(a,a,this.lc)):a=this.kn()?new q$(this.Vd+this.lc|0,this.Sk,this.lc):new oB(this.Vd+this.lc|0,this.Sk,this.lc);return a};f.e=function(){return o$(this)};f.Mc=function(){return n$(this)};function MB(){this.yC=this.ku=this.zC=null;this.zC=new VV(this)}MB.prototype=new W9;MB.prototype.constructor=MB;f=MB.prototype;f.Gb=function(a){return XY(this,a)}; f.Ja=function(a){return YY(this,a)};f.Bb=function(a){return $Y(this,a)};f.Ub=function(){return KB()};f.Mc=function(){if(0=wG(xG(),this.fg()))return this;SG();var b=this.fg(),c=this.K();TG();Zf(da(jd),bg(ca(b)))?b=Yf(da(jd))?UG(b,c):Cj(Pj(),b,c,da(md(jd))):(c=new zc(c),VG(SG(),b,0,c,0,wG(xG(),b)),b=c);hj(Pj(),b,a);return new Fu(b)};f.vc=function(a){JQ();var b=this.ag();return J3(a,b)};f.Zh=function(a){return this.Ai(a)};f.f=function(){JQ();ms();var a=this.fg();if(0===wG(xG(),a))throw nv("tail of empty array");a=ns(ms(),a,1,wG(xG(),a));return KQ(0,a)}; f.Bb=function(a){if(0>=a)var b=this;else JQ(),ms(),b=this.fg(),a=wG(xG(),b)-(0=a?this:KQ(JQ(),ls(ms(),this.fg(),a))};f.gn=function(a){if(a instanceof K3){var b=Tga(this,a);a=null===b?s$(this,a):b}else a=s$(this,a);return a};f.cc=function(a){return this.qg(a)};f.Ja=function(a){for(var b=new zc(this.K()),c=0;cv=>!!m.n(v)!==n?PU(r,v):void 0)(b,c,h)));return h.im()}if(0===e)return HJ();h=new zc(e);a.R.wa(0,h,0,d);for(k=1+d|0;d!==e;)0!==(1<!!b.n(m)!==c?PU(l,m):void 0));return l.im()}return a}f.ql=function(a,b){var c=4+this.On()|0;if(0{d.rc=d.rc.hn(e)}));else for(a=a.m();a.s();)b=a.t(),d.rc=d.rc.hn(b);return d.rc}if(this.K()<(b>>>5|0)&&a instanceof C1){b=new QX(this);for(b=uZ(new vZ,b);0g?-g|0:g)|0)|0,this.To(c),a);c=1+c|0}};f.Bb=function(a){a=this.K()-(0=this.K())return this;if(a===PG()){a=this.Hr.ia();var b=QG(),c=PG();RG(b,a,a.a.length,c);return new S3(a)}return K3.prototype.Ai.call(this,a)};f.m=function(){return new IG(this.Hr)}; f.qg=function(a){if("boolean"===typeof a){a=!!a;var b=this.Hr;uL();var c=new Ec(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new S3(c)}return K3.prototype.qg.call(this,a)};f.fB=function(a){return this.Hr.a[a]};f.cc=function(a){return this.qg(a)};f.Zh=function(a){return this.Ai(a)};f.n=function(a){return this.fB(a|0)};f.va=function(a){return this.fB(a)};f.ag=function(){return uL()};f.fg=function(){return this.Hr}; f.$classData=q({H6:0},!1,"scala.collection.immutable.ArraySeq$ofBoolean",{H6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function Q3(a){this.Ir=a}Q3.prototype=new t$;Q3.prototype.constructor=Q3;f=Q3.prototype;f.K=function(){return this.Ir.a.length};f.gB=function(a){return this.Ir.a[a]};f.B=function(){var a=BL();return FL(a,this.Ir,a.rg)}; f.i=function(a){if(a instanceof Q3){var b=this.Ir;a=a.Ir;return uj(Pj(),b,a)}return i3(this,a)};f.Ai=function(a){return 1>=this.K()?this:a===NG()?(a=this.Ir.ia(),fj(Pj(),a),new Q3(a)):K3.prototype.Ai.call(this,a)};f.m=function(){return new GG(this.Ir)};f.qg=function(a){if(Zb(a)){a|=0;var b=this.Ir;sL();var c=new Pc(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new Q3(c)}return K3.prototype.qg.call(this,a)};f.cc=function(a){return this.qg(a)};f.Zh=function(a){return this.Ai(a)}; f.n=function(a){return this.gB(a|0)};f.va=function(a){return this.gB(a)};f.ag=function(){return sL()};f.fg=function(){return this.Ir};f.$classData=q({I6:0},!1,"scala.collection.immutable.ArraySeq$ofByte",{I6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function P3(a){this.Vp=a}P3.prototype=new t$;P3.prototype.constructor=P3;f=P3.prototype;f.K=function(){return this.Vp.a.length};f.hB=function(a){return this.Vp.a[a]}; f.B=function(){var a=BL();return GL(a,this.Vp,a.rg)};f.i=function(a){if(a instanceof P3){var b=this.Vp;a=a.Vp;return tj(Pj(),b,a)}return i3(this,a)};f.Ai=function(a){return 1>=this.K()?this:a===MG()?(a=this.Vp.ia(),dj(Pj(),a),new P3(a)):K3.prototype.Ai.call(this,a)};f.m=function(){return new FG(this.Vp)};f.qg=function(a){if(a instanceof ba){a=Ea(a);var b=this.Vp;Ir();var c=new Ic(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new P3(c)}return K3.prototype.qg.call(this,a)}; f.Gh=function(a,b,c,d){return(new AQ(this.Vp)).Gh(a,b,c,d)};f.cc=function(a){return this.qg(a)};f.Zh=function(a){return this.Ai(a)};f.n=function(a){return hc(this.hB(a|0))};f.va=function(a){return hc(this.hB(a))};f.ag=function(){return Ir()};f.fg=function(){return this.Vp};f.$classData=q({J6:0},!1,"scala.collection.immutable.ArraySeq$ofChar",{J6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1}); function M3(a){this.St=a}M3.prototype=new t$;M3.prototype.constructor=M3;f=M3.prototype;f.K=function(){return this.St.a.length};f.B=function(){var a=BL();return HL(a,this.St,a.rg)};f.i=function(a){if(a instanceof M3){var b=this.St;a=a.St;return wj(Pj(),b,a)}return i3(this,a)};f.m=function(){return new CG(this.St)};f.qg=function(a){if("number"===typeof a){a=+a;var b=this.St;pL();var c=new ed(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new M3(c)}return K3.prototype.qg.call(this,a)}; f.cB=function(a){return this.St.a[a]};f.cc=function(a){return this.qg(a)};f.n=function(a){return this.cB(a|0)};f.va=function(a){return this.cB(a)};f.ag=function(){return pL()};f.fg=function(){return this.St};f.$classData=q({K6:0},!1,"scala.collection.immutable.ArraySeq$ofDouble",{K6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function O3(a){this.Tt=a}O3.prototype=new t$;O3.prototype.constructor=O3;f=O3.prototype; f.K=function(){return this.Tt.a.length};f.B=function(){var a=BL();return IL(a,this.Tt,a.rg)};f.i=function(a){if(a instanceof O3){var b=this.Tt;a=a.Tt;return xj(Pj(),b,a)}return i3(this,a)};f.m=function(){return new EG(this.Tt)};f.qg=function(a){if(ja(a)){a=Math.fround(a);var b=this.Tt;rL();var c=new $c(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new O3(c)}return K3.prototype.qg.call(this,a)};f.dB=function(a){return this.Tt.a[a]};f.cc=function(a){return this.qg(a)}; f.n=function(a){return this.dB(a|0)};f.va=function(a){return this.dB(a)};f.ag=function(){return rL()};f.fg=function(){return this.Tt};f.$classData=q({L6:0},!1,"scala.collection.immutable.ArraySeq$ofFloat",{L6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function L3(a){this.Jr=a}L3.prototype=new t$;L3.prototype.constructor=L3;f=L3.prototype;f.K=function(){return this.Jr.a.length}; f.B=function(){var a=BL();return JL(a,this.Jr,a.rg)};f.i=function(a){if(a instanceof L3){var b=this.Jr;a=a.Jr;return qj(Pj(),b,a)}return i3(this,a)};f.Ai=function(a){return 1>=this.K()?this:a===Fq()?(a=this.Jr.ia(),Ti(Pj(),a),new L3(a)):K3.prototype.Ai.call(this,a)};f.m=function(){return new BG(this.Jr)};f.qg=function(a){if(ha(a)){a|=0;var b=this.Jr;rl();var c=new Xc(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new L3(c)}return K3.prototype.qg.call(this,a)};f.wt=function(a){return this.Jr.a[a]}; f.cc=function(a){return this.qg(a)};f.Zh=function(a){return this.Ai(a)};f.n=function(a){return this.wt(a|0)};f.va=function(a){return this.wt(a)};f.ag=function(){return rl()};f.fg=function(){return this.Jr};f.$classData=q({M6:0},!1,"scala.collection.immutable.ArraySeq$ofInt",{M6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function N3(a){this.Kr=a}N3.prototype=new t$;N3.prototype.constructor=N3;f=N3.prototype; f.K=function(){return this.Kr.a.length};f.B=function(){var a=BL();return KL(a,this.Kr,a.rg)};f.i=function(a){if(a instanceof N3){var b=this.Kr;a=a.Kr;return pj(Pj(),b,a)}return i3(this,a)};f.Ai=function(a){return 1>=this.K()?this:a===LG()?(a=this.Kr.ia(),$i(Pj(),a),new N3(a)):K3.prototype.Ai.call(this,a)};f.m=function(){return new DG(this.Kr)}; f.qg=function(a){if(a instanceof ma){var b=Za(a);a=b.W;b=b.Y;var c=this.Kr;qL();var d=new Zc(1+c.a.length|0);d.a[0]=Za(new ma(a,b));VG(SG(),c,0,d,1,c.a.length);return new N3(d)}return K3.prototype.qg.call(this,a)};f.eB=function(a){return this.Kr.a[a]};f.cc=function(a){return this.qg(a)};f.Zh=function(a){return this.Ai(a)};f.n=function(a){return this.eB(a|0)};f.va=function(a){return this.eB(a)};f.ag=function(){return qL()};f.fg=function(){return this.Kr}; f.$classData=q({N6:0},!1,"scala.collection.immutable.ArraySeq$ofLong",{N6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function Fu(a){this.vn=a}Fu.prototype=new t$;Fu.prototype.constructor=Fu;f=Fu.prototype;f.ag=function(){return yG(zG(),bg(ca(this.vn)))};f.K=function(){return this.vn.a.length};f.va=function(a){return this.vn.a[a]};f.B=function(){var a=BL();return DL(a,this.vn,a.rg)}; f.i=function(a){return a instanceof Fu?sQ(SG(),this.vn,a.vn):i3(this,a)};function E$(a,b){if(1>=a.vn.a.length)return a;a=a.vn.ia();hj(Pj(),a,b);return new Fu(a)}f.m=function(){return Du(new Eu,this.vn)};f.Zh=function(a){return E$(this,a)};f.Ai=function(a){return E$(this,a)};f.n=function(a){return this.va(a|0)};f.fg=function(){return this.vn}; f.$classData=q({O6:0},!1,"scala.collection.immutable.ArraySeq$ofRef",{O6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function R3(a){this.Lr=a}R3.prototype=new t$;R3.prototype.constructor=R3;f=R3.prototype;f.K=function(){return this.Lr.a.length};f.iB=function(a){return this.Lr.a[a]};f.B=function(){var a=BL();return LL(a,this.Lr,a.rg)}; f.i=function(a){if(a instanceof R3){var b=this.Lr;a=a.Lr;return sj(Pj(),b,a)}return i3(this,a)};f.Ai=function(a){return 1>=this.K()?this:a===OG()?(a=this.Lr.ia(),bj(Pj(),a),new R3(a)):K3.prototype.Ai.call(this,a)};f.m=function(){return new HG(this.Lr)};f.qg=function(a){if($b(a)){a|=0;var b=this.Lr;tL();var c=new Sc(1+b.a.length|0);c.a[0]=a;VG(SG(),b,0,c,1,b.a.length);return new R3(c)}return K3.prototype.qg.call(this,a)};f.cc=function(a){return this.qg(a)};f.Zh=function(a){return this.Ai(a)}; f.n=function(a){return this.iB(a|0)};f.va=function(a){return this.iB(a)};f.ag=function(){return tL()};f.fg=function(){return this.Lr};f.$classData=q({P6:0},!1,"scala.collection.immutable.ArraySeq$ofShort",{P6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1});function T3(a){this.Oy=a}T3.prototype=new t$;T3.prototype.constructor=T3;f=T3.prototype;f.K=function(){return this.Oy.a.length}; f.B=function(){var a=BL();return ML(a,this.Oy,a.rg)};f.i=function(a){return a instanceof T3?this.Oy.a.length===a.Oy.a.length:i3(this,a)};f.m=function(){return new JG(this.Oy)};f.n=function(){};f.va=function(){};f.ag=function(){return SR()};f.fg=function(){return this.Oy};f.$classData=q({Q6:0},!1,"scala.collection.immutable.ArraySeq$ofUnit",{Q6:1,Gr:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,Dr:1,l:1}); function gy(a,b,c){a:for(;;){if(a.b()){c=u();break a}var d=a.e(),e=a.f();if(!!b.n(d)!==c){b:for(var g=c;;){if(e.b()){c=a;break b}c=e.e();if(!!b.n(c)!==g)e=e.f();else{var h=a;d=e;c=b;b=g;a=new z(h.e(),u());g=h.f();for(e=a;g!==d;)h=new z(g.e(),u()),e=e.p=h,g=g.f();for(g=d=d.f();!d.b();){h=d.e();if(!!c.n(h)===b){for(;g!==d;)h=new z(g.e(),u()),e=e.p=h,g=g.f();g=d.f()}d=d.f()}g.b()||(e.p=g);c=a;break b}}break a}a=e}return c} function bga(a,b,c){for(var d=null,e=null;;){if(b.b()){if(null===d)return a;e.p=a;return d}var g=b.e(),h=c.n(g);if(Object.is(h,g))b=b.f();else{for(;a!==b;)g=new z(a.e(),u()),null===d&&(d=g),null!==e&&(e.p=g),e=g,a=a.f();h=new z(h,u());null===d&&(d=h);null!==e&&(e.p=h);e=h;b=b.f();e=h=e;a=b}}}function rS(){}rS.prototype=new i9;rS.prototype.constructor=rS;function F$(){}f=F$.prototype=rS.prototype;f.Ii=function(a){return ky(this,a)};f.Zh=function(a){return qw(this,a)};f.m=function(){return new Om(this)}; f.Gb=function(a){return XY(this,a)};f.Bb=function(a){return $Y(this,a)};f.Oc=function(){return"LinearSeq"};f.NJ=function(a){return F0(this,a)};f.va=function(a){return eB(this,a)};f.De=function(a,b){return YA(this,a,b)};f.Lt=function(a){return G0(this,a)};f.wo=function(a,b){return H0(this,a,b)};f.Ik=function(){return Od()};function dl(a,b){if(a.b())return b;if(b.b())return a;var c=new z(b.e(),a),d=c;for(b=b.f();!b.b();){var e=new z(b.e(),a);d=d.p=e;b=b.f()}return c}f.b=function(){return this===u()}; function Pd(a,b){if(b instanceof rS)return dl(a,b);if(0===b.Q())return a;if(b instanceof fp&&a.b())return b.ha();b=b.m();if(b.s()){for(var c=new z(b.t(),a),d=c;b.s();){var e=new z(b.t(),a);d=d.p=e}return c}return a}function un(a,b){return b instanceof rS?dl(b,a):G3(a,b)}function xt(a,b){if(a.b()||0>=b)return u();for(var c=new z(a.e(),u()),d=c,e=a.f(),g=1;;){if(e.b())return a;if(ga)a=1;else a:for(var b=this,c=0;;){if(c===a){a=b.b()?0:1;break a}if(b.b()){a=-1;break a}c=1+c|0;b=b.f()}return a};f.ul=function(a){for(var b=this;!b.b();){if(!a.n(b.e()))return!1;b=b.f()}return!0};f.qo=function(a){for(var b=this;!b.b();){if(a.n(b.e()))return!0;b=b.f()}return!1};f.L=function(a){for(var b=this;!b.b();){if(ml(nl(),b.e(),a))return!0;b=b.f()}return!1};function lX(a,b){for(;!a.b();){if(b.n(a.e()))return new L(a.e());a=a.f()}return R()} f.Mc=function(){if(this.b())throw AH("List.last");for(var a=this,b=this.f();!b.b();)a=b,b=b.f();return a.e()};f.Ih=function(){return"List"};f.BK=function(a){if(this.b())return Od().jC;for(var b=this.ti(),c=this.ti(),d=this.m();d.s();){var e=d.t();(a.n(e)?b:c).$(e)}a=G(new H,b.Kb(),c.Kb());return null!==a&&(b=a.h(),u().i(b))?G(new H,u(),this):null!==a&&(b=a.j(),u().i(b))?G(new H,this,u()):a};f.ha=function(){return this}; f.i=function(a){var b;if(a instanceof rS)a:for(b=this;;){if(b===a){b=!0;break a}var c=b.b(),d=a.b();if(c||d||!ml(nl(),b.e(),a.e())){b=c&&d;break a}b=b.f();a=a.f()}else b=i3(this,a);return b};f.n=function(a){return eB(this,a|0)};f.Lc=function(a){return F0(this,a|0)};f.Jc=function(a){return a6(a,this)};f.Ja=function(a){return Qt(this,a)};f.gn=function(a){return un(this,a)};f.cc=function(a){return new z(a,this)};f.Ub=function(){return Od()};function G$(){this.R=null}G$.prototype=new x$; G$.prototype.constructor=G$;function H$(){}H$.prototype=G$.prototype;function A$(a,b,c){b=0=a.hu&&Oy(a,a.eb.a.length<<1);return Py(a,b,c,!1,d,d&(-1+a.eb.a.length|0))}function Ly(a,b,c,d){(1+a.Xf|0)>=a.hu&&Oy(a,a.eb.a.length<<1);var e=My(W(),b);e^=e>>>16|0;return Py(a,b,c,d,e,e&(-1+a.eb.a.length|0))} function Py(a,b,c,d,e,g){var h=a.eb.a[g];if(null===h)a.eb.a[g]=new aK(b,e,c,null);else{for(var k=null,l=h;null!==l&&l.mk<=e;){if(l.mk===e&&ml(nl(),b,l.Wk))return a=l.Ah,l.Ah=c,d?new L(a):null;k=l;l=l.$d}null===k?a.eb.a[g]=new aK(b,e,c,h):k.$d=new aK(b,e,c,k.$d)}a.Xf=1+a.Xf|0;return null} function J$(a,b){var c=My(W(),b);a:{c^=c>>>16|0;var d=c&(-1+a.eb.a.length|0),e=a.eb.a[d];if(null===e)a=null;else if(e.mk===c&&ml(nl(),e.Wk,b))a.eb.a[d]=e.$d,a.Xf=-1+a.Xf|0,a=e;else{d=e;for(e=e.$d;null!==e&&e.mk<=c;){if(e.mk===c&&ml(nl(),e.Wk,b)){d.$d=e.$d;a.Xf=-1+a.Xf|0;a=e;break a}d=e;e=e.$d}a=null}}return a} function Oy(a,b){if(0>b)throw iL("new HashMap table size "+b+" exceeds maximum");var c=a.eb.a.length;a.hu=Eb(b*a.gH);if(0===a.Xf)a.eb=new (md(bK).Ia)(b);else{var d=a.eb;a.eb=zj(Pj(),d,b);d=new aK(null,0,null,null);for(var e=new aK(null,0,null,null);c>Math.clz32(a)&a)<<1;return 1073741824>a?a:1073741824}function CV(a,b,c){a.gH=c;a.eb=new (md(bK).Ia)(K$(b));a.hu=Eb(a.eb.a.length*a.gH);a.Xf=0;return a}function FV(){var a=new DV;CV(a,16,.75);return a}function DV(){this.gH=0;this.eb=null;this.Xf=this.hu=0}DV.prototype=new e$;DV.prototype.constructor=DV;f=DV.prototype;f.ry=function(a){return b6(this,a)};f.am=function(a){return c6(this,a)};f.Gb=function(a){return XY(this,a)}; f.Ja=function(a){return YY(this,a)};f.Bb=function(a){return $Y(this,a)};f.ka=function(){return this.Xf};f.L=function(a){var b=My(W(),a);b^=b>>>16|0;var c=this.eb.a[b&(-1+this.eb.a.length|0)];return null!==(null===c?null:Ny(c,a,b))};f.he=function(a){a=K$(Eb((1+a|0)/this.gH));a>this.eb.a.length&&Oy(this,a)}; function BV(a,b){a.he(b.Q());if(b instanceof TU)return b.dc.GJ(new fW((d,e,g)=>{g|=0;I$(a,d,e,g^(g>>>16|0))})),a;if(b instanceof DV){for(b=S0(b);b.s();){var c=b.t();I$(a,c.Wk,c.Ah,c.mk)}return a}return b&&b.$classData&&b.$classData.rb.CC?(b.og(new fn((d,e)=>{var g=My(W(),d);return I$(a,d,e,g^(g>>>16|0))})),a):lR(a,b)} f.ou=function(a,b){if(ca(this)!==da(Ky))return P8(this,a,b);var c=My(W(),a);c^=c>>>16|0;var d=c&(-1+this.eb.a.length|0);var e=null;var g=null;var h=this.eb.a[d];if(null!==h)for(var k=null;;){if(c===h.mk&&ml(nl(),a,h.Wk))g=k,e=h;else if(!(null===h.$d||h.mk>c)){var l=h.$d;k=h;h=l;continue}break}k=e;k=null===k?R():new L(k.Ah);b=b.n(k);k=G(new H,k,b);h=k.y;l=k.w;if(R()!==h||R()!==l)if(h=k.w,k.y instanceof L&&R()===h)null!==g?g.$d=e.$d:this.eb.a[d]=e.$d,this.Xf=-1+this.Xf|0;else if(g=k.y,h=k.w,R()===g&& h instanceof L)e=h.k,d=(1+this.Xf|0)>=this.hu?(Oy(this,this.eb.a.length<<1),c&(-1+this.eb.a.length|0)):d,Py(this,a,e,!1,c,d);else if(a=k.w,k.y instanceof L&&a instanceof L)e.Ah=a.k;else throw new w(k);return b};f.m=function(){return 0===this.Xf?Rq().Pa:new y4(this)};f.tj=function(){return 0===this.Xf?Rq().Pa:new z4(this)};f.ie=function(){return 0===this.Xf?Rq().Pa:new A4(this)};function S0(a){return 0===a.Xf?Rq().Pa:new B4(a)}f.mg=function(){var a=this.eb;yj(Pj(),a,null);this.Xf=0}; f.U=function(a){var b=My(W(),a);b^=b>>>16|0;var c=this.eb.a[b&(-1+this.eb.a.length|0)];a=null===c?null:Ny(c,a,b);return null===a?R():new L(a.Ah)};f.n=function(a){var b=My(W(),a);b^=b>>>16|0;var c=this.eb.a[b&(-1+this.eb.a.length|0)];b=null===c?null:Ny(c,a,b);return null===b?z3(a):b.Ah};f.Se=function(a,b){if(ca(this)!==da(Ky))return x3(this,a,b);var c=My(W(),a);c^=c>>>16|0;var d=this.eb.a[c&(-1+this.eb.a.length|0)];a=null===d?null:Ny(d,a,c);return null===a?Es(b):a.Ah}; f.Hk=function(a,b){if(ca(this)!==da(Ky))return pX(this,a,b);var c=My(W(),a);c^=c>>>16|0;var d=c&(-1+this.eb.a.length|0),e=this.eb.a[d];e=null===e?null:Ny(e,a,c);if(null!==e)return e.Ah;e=this.eb;b=Es(b);(1+this.Xf|0)>=this.hu&&Oy(this,this.eb.a.length<<1);Py(this,a,b,!1,c,e===this.eb?d:c&(-1+this.eb.a.length|0));return b};f.bj=function(a,b){null===Ly(this,a,b,!0)&&R()};f.JB=function(a){null===J$(this,a)&&R()};f.bh=function(a,b){Ly(this,a,b,!1)};f.Q=function(){return this.Xf}; f.b=function(){return 0===this.Xf};f.Ca=function(a){for(var b=this.eb.a.length,c=0;ch?-h|0:h)|0)|0,a.To(d),b);d=1+d|0}}function IJ(a){this.R=a}IJ.prototype=new H$;IJ.prototype.constructor=IJ;f=IJ.prototype;f.va=function(a){if(0<=a&&athis.R.a.length)return new IJ(RJ(JJ(),this.R,a));var b=this.R,c=JJ().bd,d=new zc(1);d.a[0]=a;return new KJ(b,32,c,d,33)};f.zl=function(a){var b=this.R.a.length;if(32>b)return new IJ(TJ(JJ(),a,this.R));var c=new zc(1);c.a[0]=a;return new KJ(c,1,JJ().bd,this.R,1+b|0)};f.yo=function(a){return new IJ(WJ(JJ(),this.R,a))};f.Ln=function(a,b){var c=this.R;return new IJ(Jj(Pj(),c,a,b))}; f.Cm=function(){if(1===this.R.a.length)return HJ();var a=this.R,b=a.a.length;return new IJ(Jj(Pj(),a,1,b))};f.On=function(){return 1};f.To=function(){return this.R};f.ql=function(a,b){var c=YJ(JJ(),this.R,a);return null!==c?new IJ(c):C1.prototype.ql.call(this,a,b)};f.f=function(){return this.Cm()};f.Ja=function(a){return this.yo(a)};f.cc=function(a){return this.zl(a)};f.n=function(a){a|=0;if(0<=a&&a!!b.n(c))))} f.i=function(a){if(a instanceof rZ){var b=this.Xe,c=a.Xe;if(null===b?null===c:b.i(c)){nJ();b=this.Hf;a=a.Hf;c=this.Xe;var d;if(!(d=b===a)&&(d=null!==b)&&(d=null!==a)&&(d=(2147483647&b.ea)===(2147483647&a.ea))){b=new e4(b,c);a=new e4(a,c);for(c=!0;c&&null!==b.Hc&&null!==a.Hc;)b.Hc===a.Hc?(0===b.Wd?d=null:(b.Wd=-1+b.Wd|0,d=b.Cn.a[b.Wd]),b.Hc=d,0===a.Wd?d=null:(a.Wd=-1+a.Wd|0,d=a.Cn.a[a.Wd]),a.Hc=d):(c=Object.is(b.Hc.Wa,a.Hc.Wa)?!0:b.hw.Yi(b.Hc.Wa,a.Hc.Wa),b.Hc=r1(b,b.Hc.ta),a.Hc=r1(a,a.Hc.ta));d=c&& null===b.Hc&&null===a.Hc}return d}}return O8(this,a)};f.Ih=function(){return"TreeSet"};f.vc=function(a){return qZ(zZ(),a,this.Xe)};f.Gk=function(a){return qZ(zZ(),a,this.Xe)};f.Fk=function(a){return Vga(this,a)};f.wy=function(a){a:{if(a instanceof rZ){var b=this.Xe,c=a.Xe;if(null===b?null===c:b.i(c)){b=nJ();a=pI(XI(b,this.Hf,a.Hf,this.Xe));a=P$(this,a);break a}}b=new $e;a=a.m();c=b.sb?b.vb:Uga(this,b);cH(a,c);a=P$(this,(b.sb?b.vb:Uga(this,b)).cH)}return a}; f.Ce=function(a){a:{if(a instanceof rZ){var b=this.Xe,c=a.Xe;if(null===b?null===c:b.i(c)){a=mJ(nJ(),this.Hf,a.Hf,this.Xe);break a}}a=a.m();for(b=this.Hf;a.s();)b=gJ(nJ(),b,a.t(),null,!1,this.Xe);a=b}return P$(this,a)};f.Ek=function(a){var b=nJ();a=pI(PI(b,this.Hf,a,this.Xe));return P$(this,a)};f.bc=function(a){return P$(this,gJ(nJ(),this.Hf,a,null,!1,this.Xe))}; f.Bb=function(a){var b=EI(nJ(),this.Hf)-(0=b)a=xZ(this.Xe);else if(b>=EI(nJ(),this.Hf))a=this;else{a=new rZ;var c=nJ();b=pI(HI(c,this.Hf,b));a=sZ(a,b,this.Xe)}return a};f.Jc=function(a){if(0>=a)var b=this;else if(a>=EI(nJ(),this.Hf))b=xZ(this.Xe);else{b=new rZ;var c=nJ();a=pI(GI(c,this.Hf,a));b=sZ(b,a,this.Xe)}return b}; f.$classData=q({L8:0},!1,"scala.collection.immutable.TreeSet",{L8:1,Fr:1,rm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,fk:1,Cl:1,la:1,v:1,dq:1,xc:1,Rr:1,OL:1,PG:1,fS:1,Iy:1,eS:1,D8:1,Tba:1,Qba:1,i6:1,Cb:1,F8:1,sg:1,l:1});function Q$(){this.Z=this.R=null;this.ca=0;L$(this,JJ().PL,JJ().PL,0)}Q$.prototype=new M$;Q$.prototype.constructor=Q$;f=Q$.prototype;f.$r=function(a){throw this.rh(a);};f.hn=function(a){var b=new zc(1);b.a[0]=a;return new IJ(b)};f.zl=function(a){var b=new zc(1);b.a[0]=a;return new IJ(b)}; f.Cm=function(){throw nv("empty.tail");};f.Ln=function(){return this};f.On=function(){return 0};f.To=function(){return null};f.i=function(a){return this===a||!(a instanceof C1)&&i3(this,a)};f.ql=function(a){return pU(GK(),a)};f.rh=function(a){return aL(new bL,a+" is out of bounds (empty vector)")};f.f=function(){return this.Cm()};f.Ja=function(){return this};f.cc=function(a){return this.zl(a)};f.n=function(a){throw this.rh(a|0);};f.va=function(a){throw this.rh(a);}; f.$classData=q({Q8:0},!1,"scala.collection.immutable.Vector0$",{Q8:1,fC:1,$y:1,Zy:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,sg:1,l:1});var R$;function HJ(){R$||(R$=new Q$);return R$}function KJ(a,b,c,d,e){this.Z=this.R=null;this.ca=0;this.Ri=b;this.yh=c;L$(this,a,d,e)}KJ.prototype=new M$;KJ.prototype.constructor=KJ;f=KJ.prototype; f.va=function(a){if(0<=a&&a>>5|0,a=this.Ri){var c=a-this.Ri|0;a=c>>>5|0;c&=31;if(athis.Z.a.length)return a=RJ(JJ(),this.Z,a),new KJ(this.R,this.Ri,this.yh,a,1+this.ca|0);if(30>this.yh.a.length){var b=SJ(JJ(),this.yh,this.Z),c=new zc(1);c.a[0]=a;return new KJ(this.R,this.Ri,b,c,1+this.ca|0)}b=this.R;c=this.Ri;var d=this.yh,e=this.Ri,g=JJ().eg,h=this.Z,k=new (md(md(jd)).Ia)(1);k.a[0]=h;h=new zc(1);h.a[0]=a;return new LJ(b,c,d,960+e|0,g,k,h,1+this.ca|0)}; f.zl=function(a){if(32>this.Ri){var b=TJ(JJ(),a,this.R);return new KJ(b,1+this.Ri|0,this.yh,this.Z,1+this.ca|0)}if(30>this.yh.a.length)return b=new zc(1),b.a[0]=a,a=UJ(JJ(),this.R,this.yh),new KJ(b,1,a,this.Z,1+this.ca|0);b=new zc(1);b.a[0]=a;a=this.R;var c=new (md(md(jd)).Ia)(1);c.a[0]=a;return new LJ(b,1,c,1+this.Ri|0,JJ().eg,this.yh,this.Z,1+this.ca|0)};f.yo=function(a){var b=WJ(JJ(),this.R,a),c=XJ(JJ(),2,this.yh,a);a=WJ(JJ(),this.Z,a);return new KJ(b,this.Ri,c,a,this.ca)}; f.Ln=function(a,b){a=new FJ(a,b);GJ(a,1,this.R);GJ(a,2,this.yh);GJ(a,1,this.Z);return a.im()};f.Cm=function(){if(1>>5|0,b>>10|0;var c=31&(b>>>5|0);b&=31;return a=this.Vh?(b=a-this.Vh|0,this.Wh.a[b>>>5|0].a[31&b]):this.R.a[a]}throw this.rh(a);}; f.$r=function(a,b){if(0<=a&&a=this.zh){var c=a-this.zh|0,d=c>>>10|0;a=31&(c>>>5|0);c&=31;if(d= this.Vh)return c=a-this.Vh|0,a=c>>>5|0,c&=31,d=this.Wh.ia(),e=d.a[a].ia(),e.a[c]=b,d.a[a]=e,new LJ(this.R,this.Vh,d,this.zh,this.cg,this.tg,this.Z,this.ca);c=this.R.ia();c.a[a]=b;return new LJ(c,this.Vh,this.Wh,this.zh,this.cg,this.tg,this.Z,this.ca)}throw this.rh(a);}; f.hn=function(a){if(32>this.Z.a.length)return a=RJ(JJ(),this.Z,a),new LJ(this.R,this.Vh,this.Wh,this.zh,this.cg,this.tg,a,1+this.ca|0);if(31>this.tg.a.length){var b=SJ(JJ(),this.tg,this.Z),c=new zc(1);c.a[0]=a;return new LJ(this.R,this.Vh,this.Wh,this.zh,this.cg,b,c,1+this.ca|0)}if(30>this.cg.a.length){b=SJ(JJ(),this.cg,SJ(JJ(),this.tg,this.Z));c=JJ().bd;var d=new zc(1);d.a[0]=a;return new LJ(this.R,this.Vh,this.Wh,this.zh,b,c,d,1+this.ca|0)}b=this.R;c=this.Vh;d=this.Wh;var e=this.zh,g=this.cg,h= this.zh,k=JJ().jk,l=SJ(JJ(),this.tg,this.Z),m=new (md(md(md(jd))).Ia)(1);m.a[0]=l;l=JJ().bd;var n=new zc(1);n.a[0]=a;return new MJ(b,c,d,e,g,30720+h|0,k,m,l,n,1+this.ca|0)}; f.zl=function(a){if(32>this.Vh){var b=TJ(JJ(),a,this.R);return new LJ(b,1+this.Vh|0,this.Wh,1+this.zh|0,this.cg,this.tg,this.Z,1+this.ca|0)}if(1024>this.zh)return b=new zc(1),b.a[0]=a,a=UJ(JJ(),this.R,this.Wh),new LJ(b,1,a,1+this.zh|0,this.cg,this.tg,this.Z,1+this.ca|0);if(30>this.cg.a.length){b=new zc(1);b.a[0]=a;a=JJ().bd;var c=UJ(JJ(),UJ(JJ(),this.R,this.Wh),this.cg);return new LJ(b,1,a,1,c,this.tg,this.Z,1+this.ca|0)}b=new zc(1);b.a[0]=a;a=JJ().bd;c=UJ(JJ(),this.R,this.Wh);var d=new (md(md(md(jd))).Ia)(1); d.a[0]=c;return new MJ(b,1,a,1,d,1+this.zh|0,JJ().jk,this.cg,this.tg,this.Z,1+this.ca|0)};f.yo=function(a){var b=WJ(JJ(),this.R,a),c=XJ(JJ(),2,this.Wh,a),d=XJ(JJ(),3,this.cg,a),e=XJ(JJ(),2,this.tg,a);a=WJ(JJ(),this.Z,a);return new LJ(b,this.Vh,c,this.zh,d,e,a,this.ca)};f.Ln=function(a,b){a=new FJ(a,b);GJ(a,1,this.R);GJ(a,2,this.Wh);GJ(a,3,this.cg);GJ(a,2,this.tg);GJ(a,1,this.Z);return a.im()}; f.Cm=function(){if(1>>10|0;var c=31&(a>>>5|0);a&=31;return b=this.Vh?(a=b-this.Vh|0,this.Wh.a[a>>>5|0].a[31&a]):this.R.a[b]}throw this.rh(b);};f.$classData=q({T8:0},!1,"scala.collection.immutable.Vector3",{T8:1,fC:1,$y:1,Zy:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,sg:1,l:1}); function MJ(a,b,c,d,e,g,h,k,l,m,n){this.Z=this.R=null;this.ca=0;this.Ng=b;this.ug=c;this.Og=d;this.vg=e;this.dg=g;this.Ye=h;this.qf=k;this.pf=l;L$(this,a,m,n)}MJ.prototype=new M$;MJ.prototype.constructor=MJ;f=MJ.prototype; f.va=function(a){if(0<=a&&a>>15|0;var c=31&(b>>>10|0),d=31&(b>>>5|0);b&=31;return a=this.Og?(b=a-this.Og|0,this.vg.a[b>>>10|0].a[31&(b>>>5|0)].a[31&b]):a>=this.Ng?(b=a-this.Ng|0,this.ug.a[b>>>5|0].a[31&b]):this.R.a[a]}throw this.rh(a);}; f.$r=function(a,b){if(0<=a&&a=this.dg){var c=a-this.dg|0,d=c>>>15|0,e=31&(c>>>10|0);a=31&(c>>>5|0);c&=31;if(d=this.Og)return e=a-this.Og|0,a=e>>>10|0,c=31&(e>>>5|0),e&=31,d=this.vg.ia(),g=d.a[a].ia(),h=g.a[c].ia(),h.a[e]=b,g.a[c]=h,d.a[a]=g,new MJ(this.R,this.Ng,this.ug,this.Og,d,this.dg,this.Ye,this.qf,this.pf, this.Z,this.ca);if(a>=this.Ng)return c=a-this.Ng|0,a=c>>>5|0,c&=31,e=this.ug.ia(),d=e.a[a].ia(),d.a[c]=b,e.a[a]=d,new MJ(this.R,this.Ng,e,this.Og,this.vg,this.dg,this.Ye,this.qf,this.pf,this.Z,this.ca);c=this.R.ia();c.a[a]=b;return new MJ(c,this.Ng,this.ug,this.Og,this.vg,this.dg,this.Ye,this.qf,this.pf,this.Z,this.ca)}throw this.rh(a);}; f.hn=function(a){if(32>this.Z.a.length)return a=RJ(JJ(),this.Z,a),new MJ(this.R,this.Ng,this.ug,this.Og,this.vg,this.dg,this.Ye,this.qf,this.pf,a,1+this.ca|0);if(31>this.pf.a.length){var b=SJ(JJ(),this.pf,this.Z),c=new zc(1);c.a[0]=a;return new MJ(this.R,this.Ng,this.ug,this.Og,this.vg,this.dg,this.Ye,this.qf,b,c,1+this.ca|0)}if(31>this.qf.a.length){b=SJ(JJ(),this.qf,SJ(JJ(),this.pf,this.Z));c=JJ().bd;var d=new zc(1);d.a[0]=a;return new MJ(this.R,this.Ng,this.ug,this.Og,this.vg,this.dg,this.Ye,b, c,d,1+this.ca|0)}if(30>this.Ye.a.length){b=SJ(JJ(),this.Ye,SJ(JJ(),this.qf,SJ(JJ(),this.pf,this.Z)));c=JJ().eg;d=JJ().bd;var e=new zc(1);e.a[0]=a;return new MJ(this.R,this.Ng,this.ug,this.Og,this.vg,this.dg,b,c,d,e,1+this.ca|0)}b=this.R;c=this.Ng;d=this.ug;e=this.Og;var g=this.vg,h=this.dg,k=this.Ye,l=this.dg,m=JJ().du,n=SJ(JJ(),this.qf,SJ(JJ(),this.pf,this.Z)),r=new (md(md(md(md(jd)))).Ia)(1);r.a[0]=n;n=JJ().eg;var v=JJ().bd,x=new zc(1);x.a[0]=a;return new NJ(b,c,d,e,g,h,k,983040+l|0,m,r,n,v,x,1+ this.ca|0)}; f.zl=function(a){if(32>this.Ng){var b=TJ(JJ(),a,this.R);return new MJ(b,1+this.Ng|0,this.ug,1+this.Og|0,this.vg,1+this.dg|0,this.Ye,this.qf,this.pf,this.Z,1+this.ca|0)}if(1024>this.Og)return b=new zc(1),b.a[0]=a,a=UJ(JJ(),this.R,this.ug),new MJ(b,1,a,1+this.Og|0,this.vg,1+this.dg|0,this.Ye,this.qf,this.pf,this.Z,1+this.ca|0);if(32768>this.dg){b=new zc(1);b.a[0]=a;a=JJ().bd;var c=UJ(JJ(),UJ(JJ(),this.R,this.ug),this.vg);return new MJ(b,1,a,1,c,1+this.dg|0,this.Ye,this.qf,this.pf,this.Z,1+this.ca|0)}if(30> this.Ye.a.length){b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;var d=UJ(JJ(),UJ(JJ(),UJ(JJ(),this.R,this.ug),this.vg),this.Ye);return new MJ(b,1,a,1,c,1,d,this.qf,this.pf,this.Z,1+this.ca|0)}b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;d=UJ(JJ(),UJ(JJ(),this.R,this.ug),this.vg);var e=new (md(md(md(md(jd)))).Ia)(1);e.a[0]=d;return new NJ(b,1,a,1,c,1,e,1+this.dg|0,JJ().du,this.Ye,this.qf,this.pf,this.Z,1+this.ca|0)}; f.yo=function(a){var b=WJ(JJ(),this.R,a),c=XJ(JJ(),2,this.ug,a),d=XJ(JJ(),3,this.vg,a),e=XJ(JJ(),4,this.Ye,a),g=XJ(JJ(),3,this.qf,a),h=XJ(JJ(),2,this.pf,a);a=WJ(JJ(),this.Z,a);return new MJ(b,this.Ng,c,this.Og,d,this.dg,e,g,h,a,this.ca)};f.Ln=function(a,b){a=new FJ(a,b);GJ(a,1,this.R);GJ(a,2,this.ug);GJ(a,3,this.vg);GJ(a,4,this.Ye);GJ(a,3,this.qf);GJ(a,2,this.pf);GJ(a,1,this.Z);return a.im()}; f.Cm=function(){if(1>>15|0;var c=31&(a>>>10|0),d=31&(a>>>5|0);a&=31;return b=this.Og?(a=b-this.Og|0,this.vg.a[a>>>10|0].a[31&(a>>>5|0)].a[31&a]):b>=this.Ng?(a=b-this.Ng|0,this.ug.a[a>>>5|0].a[31&a]):this.R.a[b]}throw this.rh(b);}; f.$classData=q({U8:0},!1,"scala.collection.immutable.Vector4",{U8:1,fC:1,$y:1,Zy:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,sg:1,l:1});function NJ(a,b,c,d,e,g,h,k,l,m,n,r,v,x){this.Z=this.R=null;this.ca=0;this.Vf=b;this.rf=c;this.Wf=d;this.sf=e;this.If=g;this.tf=h;this.Ze=k;this.Xd=l;this.ge=m;this.fe=n;this.ee=r;L$(this,a,v,x)}NJ.prototype=new M$;NJ.prototype.constructor=NJ;f=NJ.prototype; f.va=function(a){if(0<=a&&a>>20|0;var c=31&(b>>>15|0),d=31&(b>>>10|0),e=31&(b>>>5|0);b&=31;return a=this.If?(b=a-this.If|0,this.tf.a[b>>>15|0].a[31&(b>>>10|0)].a[31&(b>>>5|0)].a[31&b]):a>=this.Wf?(b=a-this.Wf|0,this.sf.a[b>>>10|0].a[31&(b>>>5|0)].a[31&b]):a>=this.Vf? (b=a-this.Vf|0,this.rf.a[b>>>5|0].a[31&b]):this.R.a[a]}throw this.rh(a);}; f.$r=function(a,b){if(0<=a&&a=this.Ze){var c=a-this.Ze|0,d=c>>>20|0,e=31&(c>>>15|0),g=31&(c>>>10|0);a=31&(c>>>5|0);c&=31;if(d=this.If)return e=a-this.If|0,a=e>>>15|0,c=31&(e>>>10|0),g=31&(e>>>5|0),e&=31,d=this.tf.ia(),h=d.a[a].ia(),k=h.a[c].ia(),l=k.a[g].ia(),l.a[e]=b,k.a[g]=l,h.a[c]=k,d.a[a]=h,new NJ(this.R,this.Vf,this.rf,this.Wf,this.sf,this.If,d,this.Ze,this.Xd,this.ge,this.fe,this.ee,this.Z,this.ca);if(a>=this.Wf)return g=a-this.Wf|0,a=g>>>10| 0,c=31&(g>>>5|0),g&=31,e=this.sf.ia(),d=e.a[a].ia(),h=d.a[c].ia(),h.a[g]=b,d.a[c]=h,e.a[a]=d,new NJ(this.R,this.Vf,this.rf,this.Wf,e,this.If,this.tf,this.Ze,this.Xd,this.ge,this.fe,this.ee,this.Z,this.ca);if(a>=this.Vf)return c=a-this.Vf|0,a=c>>>5|0,c&=31,g=this.rf.ia(),e=g.a[a].ia(),e.a[c]=b,g.a[a]=e,new NJ(this.R,this.Vf,g,this.Wf,this.sf,this.If,this.tf,this.Ze,this.Xd,this.ge,this.fe,this.ee,this.Z,this.ca);c=this.R.ia();c.a[a]=b;return new NJ(c,this.Vf,this.rf,this.Wf,this.sf,this.If,this.tf, this.Ze,this.Xd,this.ge,this.fe,this.ee,this.Z,this.ca)}throw this.rh(a);}; f.hn=function(a){if(32>this.Z.a.length)return a=RJ(JJ(),this.Z,a),new NJ(this.R,this.Vf,this.rf,this.Wf,this.sf,this.If,this.tf,this.Ze,this.Xd,this.ge,this.fe,this.ee,a,1+this.ca|0);if(31>this.ee.a.length){var b=SJ(JJ(),this.ee,this.Z),c=new zc(1);c.a[0]=a;return new NJ(this.R,this.Vf,this.rf,this.Wf,this.sf,this.If,this.tf,this.Ze,this.Xd,this.ge,this.fe,b,c,1+this.ca|0)}if(31>this.fe.a.length){b=SJ(JJ(),this.fe,SJ(JJ(),this.ee,this.Z));c=JJ().bd;var d=new zc(1);d.a[0]=a;return new NJ(this.R,this.Vf, this.rf,this.Wf,this.sf,this.If,this.tf,this.Ze,this.Xd,this.ge,b,c,d,1+this.ca|0)}if(31>this.ge.a.length){b=SJ(JJ(),this.ge,SJ(JJ(),this.fe,SJ(JJ(),this.ee,this.Z)));c=JJ().eg;d=JJ().bd;var e=new zc(1);e.a[0]=a;return new NJ(this.R,this.Vf,this.rf,this.Wf,this.sf,this.If,this.tf,this.Ze,this.Xd,b,c,d,e,1+this.ca|0)}if(30>this.Xd.a.length){b=SJ(JJ(),this.Xd,SJ(JJ(),this.ge,SJ(JJ(),this.fe,SJ(JJ(),this.ee,this.Z))));c=JJ().jk;d=JJ().eg;e=JJ().bd;var g=new zc(1);g.a[0]=a;return new NJ(this.R,this.Vf, this.rf,this.Wf,this.sf,this.If,this.tf,this.Ze,b,c,d,e,g,1+this.ca|0)}b=this.R;c=this.Vf;d=this.rf;e=this.Wf;g=this.sf;var h=this.If,k=this.tf,l=this.Ze,m=this.Xd,n=this.Ze,r=JJ().dH,v=SJ(JJ(),this.ge,SJ(JJ(),this.fe,SJ(JJ(),this.ee,this.Z))),x=new (md(md(md(md(md(jd))))).Ia)(1);x.a[0]=v;v=JJ().jk;var A=JJ().eg,B=JJ().bd,C=new zc(1);C.a[0]=a;return new OJ(b,c,d,e,g,h,k,l,m,31457280+n|0,r,x,v,A,B,C,1+this.ca|0)}; f.zl=function(a){if(32>this.Vf){var b=TJ(JJ(),a,this.R);return new NJ(b,1+this.Vf|0,this.rf,1+this.Wf|0,this.sf,1+this.If|0,this.tf,1+this.Ze|0,this.Xd,this.ge,this.fe,this.ee,this.Z,1+this.ca|0)}if(1024>this.Wf)return b=new zc(1),b.a[0]=a,a=UJ(JJ(),this.R,this.rf),new NJ(b,1,a,1+this.Wf|0,this.sf,1+this.If|0,this.tf,1+this.Ze|0,this.Xd,this.ge,this.fe,this.ee,this.Z,1+this.ca|0);if(32768>this.If){b=new zc(1);b.a[0]=a;a=JJ().bd;var c=UJ(JJ(),UJ(JJ(),this.R,this.rf),this.sf);return new NJ(b,1,a,1, c,1+this.If|0,this.tf,1+this.Ze|0,this.Xd,this.ge,this.fe,this.ee,this.Z,1+this.ca|0)}if(1048576>this.Ze){b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;var d=UJ(JJ(),UJ(JJ(),UJ(JJ(),this.R,this.rf),this.sf),this.tf);return new NJ(b,1,a,1,c,1,d,1+this.Ze|0,this.Xd,this.ge,this.fe,this.ee,this.Z,1+this.ca|0)}if(30>this.Xd.a.length){b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;d=JJ().jk;var e=UJ(JJ(),UJ(JJ(),UJ(JJ(),UJ(JJ(),this.R,this.rf),this.sf),this.tf),this.Xd);return new NJ(b,1,a,1,c,1,d,1,e,this.ge,this.fe, this.ee,this.Z,1+this.ca|0)}b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;d=JJ().jk;e=UJ(JJ(),UJ(JJ(),UJ(JJ(),this.R,this.rf),this.sf),this.tf);var g=new (md(md(md(md(md(jd))))).Ia)(1);g.a[0]=e;return new OJ(b,1,a,1,c,1,d,1,g,1+this.Ze|0,JJ().dH,this.Xd,this.ge,this.fe,this.ee,this.Z,1+this.ca|0)}; f.yo=function(a){var b=WJ(JJ(),this.R,a),c=XJ(JJ(),2,this.rf,a),d=XJ(JJ(),3,this.sf,a),e=XJ(JJ(),4,this.tf,a),g=XJ(JJ(),5,this.Xd,a),h=XJ(JJ(),4,this.ge,a),k=XJ(JJ(),3,this.fe,a),l=XJ(JJ(),2,this.ee,a);a=WJ(JJ(),this.Z,a);return new NJ(b,this.Vf,c,this.Wf,d,this.If,e,this.Ze,g,h,k,l,a,this.ca)};f.Ln=function(a,b){a=new FJ(a,b);GJ(a,1,this.R);GJ(a,2,this.rf);GJ(a,3,this.sf);GJ(a,4,this.tf);GJ(a,5,this.Xd);GJ(a,4,this.ge);GJ(a,3,this.fe);GJ(a,2,this.ee);GJ(a,1,this.Z);return a.im()}; f.Cm=function(){if(1>>20|0;var c=31&(a>>>15|0),d=31&(a>>>10|0),e=31&(a>>>5|0);a&=31;return b=this.If?(a=b-this.If|0,this.tf.a[a>>>15|0].a[31&(a>>>10|0)].a[31&(a>>>5|0)].a[31&a]):b>=this.Wf?(a=b-this.Wf|0,this.sf.a[a>>>10|0].a[31&(a>>>5|0)].a[31&a]):b>= this.Vf?(a=b-this.Vf|0,this.rf.a[a>>>5|0].a[31&a]):this.R.a[b]}throw this.rh(b);};f.$classData=q({V8:0},!1,"scala.collection.immutable.Vector5",{V8:1,fC:1,$y:1,Zy:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,sg:1,l:1}); function OJ(a,b,c,d,e,g,h,k,l,m,n,r,v,x,A,B,C){this.Z=this.R=null;this.ca=0;this.uf=b;this.Ge=c;this.vf=d;this.He=e;this.$e=g;this.Ie=h;this.Ee=k;this.Je=l;this.Fe=m;this.rd=n;this.Nd=r;this.Md=v;this.Ld=x;this.Kd=A;L$(this,a,B,C)}OJ.prototype=new M$;OJ.prototype.constructor=OJ;f=OJ.prototype; f.va=function(a){if(0<=a&&a>>25|0;var c=31&(b>>>20|0),d=31&(b>>>15|0),e=31&(b>>>10|0),g=31&(b>>>5|0);b&=31;return a=this.Ee?(b=a-this.Ee|0,this.Je.a[b>>>20|0].a[31&(b>>>15|0)].a[31&(b>>>10|0)].a[31&(b>>>5| 0)].a[31&b]):a>=this.$e?(b=a-this.$e|0,this.Ie.a[b>>>15|0].a[31&(b>>>10|0)].a[31&(b>>>5|0)].a[31&b]):a>=this.vf?(b=a-this.vf|0,this.He.a[b>>>10|0].a[31&(b>>>5|0)].a[31&b]):a>=this.uf?(b=a-this.uf|0,this.Ge.a[b>>>5|0].a[31&b]):this.R.a[a]}throw this.rh(a);}; f.$r=function(a,b){if(0<=a&&a=this.Fe){var c=a-this.Fe|0,d=c>>>25|0,e=31&(c>>>20|0),g=31&(c>>>15|0),h=31&(c>>>10|0);a=31&(c>>>5|0);c&=31;if(d=this.Ee)return e=a-this.Ee|0,a=e>>>20|0,c=31&(e>>>15|0),h=31&(e>>>10|0),g=31&(e>>>5|0),e&=31,d=this.Je.ia(),k=d.a[a].ia(),l=k.a[c].ia(),m=l.a[h].ia(),n=m.a[g].ia(),n.a[e]=b,m.a[g]=n,l.a[h]=m,k.a[c]=l,d.a[a]=k,new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,d,this.Fe,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,this.ca);if(a>=this.$e)return g=a-this.$e|0,a=g>>>15|0,c=31&(g>>>10|0),h=31&(g>>>5|0), g&=31,e=this.Ie.ia(),d=e.a[a].ia(),k=d.a[c].ia(),l=k.a[h].ia(),l.a[g]=b,k.a[h]=l,d.a[c]=k,e.a[a]=d,new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,e,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,this.ca);if(a>=this.vf)return h=a-this.vf|0,a=h>>>10|0,c=31&(h>>>5|0),h&=31,g=this.He.ia(),e=g.a[a].ia(),d=e.a[c].ia(),d.a[h]=b,e.a[c]=d,g.a[a]=e,new OJ(this.R,this.uf,this.Ge,this.vf,g,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,this.ca); if(a>=this.uf)return c=a-this.uf|0,a=c>>>5|0,c&=31,h=this.Ge.ia(),g=h.a[a].ia(),g.a[c]=b,h.a[a]=g,new OJ(this.R,this.uf,h,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,this.ca);c=this.R.ia();c.a[a]=b;return new OJ(c,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,this.ca)}throw this.rh(a);}; f.hn=function(a){if(32>this.Z.a.length)return a=RJ(JJ(),this.Z,a),new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,this.Ld,this.Kd,a,1+this.ca|0);if(31>this.Kd.a.length){var b=SJ(JJ(),this.Kd,this.Z),c=new zc(1);c.a[0]=a;return new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,this.Ld,b,c,1+this.ca|0)}if(31>this.Ld.a.length){b=SJ(JJ(),this.Ld,SJ(JJ(),this.Kd,this.Z));c=JJ().bd; var d=new zc(1);d.a[0]=a;return new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,this.Md,b,c,d,1+this.ca|0)}if(31>this.Md.a.length){b=SJ(JJ(),this.Md,SJ(JJ(),this.Ld,SJ(JJ(),this.Kd,this.Z)));c=JJ().eg;d=JJ().bd;var e=new zc(1);e.a[0]=a;return new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,this.Nd,b,c,d,e,1+this.ca|0)}if(31>this.Nd.a.length){b=SJ(JJ(),this.Nd,SJ(JJ(),this.Md,SJ(JJ(),this.Ld,SJ(JJ(), this.Kd,this.Z))));c=JJ().jk;d=JJ().eg;e=JJ().bd;var g=new zc(1);g.a[0]=a;return new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,this.rd,b,c,d,e,g,1+this.ca|0)}if(62>this.rd.a.length){b=SJ(JJ(),this.rd,SJ(JJ(),this.Nd,SJ(JJ(),this.Md,SJ(JJ(),this.Ld,SJ(JJ(),this.Kd,this.Z)))));c=JJ().du;d=JJ().jk;e=JJ().eg;g=JJ().bd;var h=new zc(1);h.a[0]=a;return new OJ(this.R,this.uf,this.Ge,this.vf,this.He,this.$e,this.Ie,this.Ee,this.Je,this.Fe,b,c,d,e,g,h,1+this.ca|0)}throw UL(); }; f.zl=function(a){if(32>this.uf){var b=TJ(JJ(),a,this.R);return new OJ(b,1+this.uf|0,this.Ge,1+this.vf|0,this.He,1+this.$e|0,this.Ie,1+this.Ee|0,this.Je,1+this.Fe|0,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,1+this.ca|0)}if(1024>this.vf)return b=new zc(1),b.a[0]=a,a=UJ(JJ(),this.R,this.Ge),new OJ(b,1,a,1+this.vf|0,this.He,1+this.$e|0,this.Ie,1+this.Ee|0,this.Je,1+this.Fe|0,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,1+this.ca|0);if(32768>this.$e){b=new zc(1);b.a[0]=a;a=JJ().bd;var c=UJ(JJ(),UJ(JJ(), this.R,this.Ge),this.He);return new OJ(b,1,a,1,c,1+this.$e|0,this.Ie,1+this.Ee|0,this.Je,1+this.Fe|0,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,1+this.ca|0)}if(1048576>this.Ee){b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;var d=UJ(JJ(),UJ(JJ(),UJ(JJ(),this.R,this.Ge),this.He),this.Ie);return new OJ(b,1,a,1,c,1,d,1+this.Ee|0,this.Je,1+this.Fe|0,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,1+this.ca|0)}if(33554432>this.Fe){b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;d=JJ().jk;var e=UJ(JJ(),UJ(JJ(),UJ(JJ(), UJ(JJ(),this.R,this.Ge),this.He),this.Ie),this.Je);return new OJ(b,1,a,1,c,1,d,1,e,1+this.Fe|0,this.rd,this.Nd,this.Md,this.Ld,this.Kd,this.Z,1+this.ca|0)}if(62>this.rd.a.length){b=new zc(1);b.a[0]=a;a=JJ().bd;c=JJ().eg;d=JJ().jk;e=JJ().du;var g=UJ(JJ(),UJ(JJ(),UJ(JJ(),UJ(JJ(),UJ(JJ(),this.R,this.Ge),this.He),this.Ie),this.Je),this.rd);return new OJ(b,1,a,1,c,1,d,1,e,1,g,this.Nd,this.Md,this.Ld,this.Kd,this.Z,1+this.ca|0)}throw UL();}; f.yo=function(a){var b=WJ(JJ(),this.R,a),c=XJ(JJ(),2,this.Ge,a),d=XJ(JJ(),3,this.He,a),e=XJ(JJ(),4,this.Ie,a),g=XJ(JJ(),5,this.Je,a),h=XJ(JJ(),6,this.rd,a),k=XJ(JJ(),5,this.Nd,a),l=XJ(JJ(),4,this.Md,a),m=XJ(JJ(),3,this.Ld,a),n=XJ(JJ(),2,this.Kd,a);a=WJ(JJ(),this.Z,a);return new OJ(b,this.uf,c,this.vf,d,this.$e,e,this.Ee,g,this.Fe,h,k,l,m,n,a,this.ca)}; f.Ln=function(a,b){a=new FJ(a,b);GJ(a,1,this.R);GJ(a,2,this.Ge);GJ(a,3,this.He);GJ(a,4,this.Ie);GJ(a,5,this.Je);GJ(a,6,this.rd);GJ(a,5,this.Nd);GJ(a,4,this.Md);GJ(a,3,this.Ld);GJ(a,2,this.Kd);GJ(a,1,this.Z);return a.im()};f.Cm=function(){if(1>>25|0;var c=31&(a>>>20|0),d=31&(a>>>15|0),e=31&(a>>>10|0),g=31&(a>>>5|0);a&=31;return b=this.Ee?(a=b-this.Ee|0,this.Je.a[a>>>20|0].a[31&(a>>>15|0)].a[31&(a>>>10|0)].a[31&(a>>> 5|0)].a[31&a]):b>=this.$e?(a=b-this.$e|0,this.Ie.a[a>>>15|0].a[31&(a>>>10|0)].a[31&(a>>>5|0)].a[31&a]):b>=this.vf?(a=b-this.vf|0,this.He.a[a>>>10|0].a[31&(a>>>5|0)].a[31&a]):b>=this.uf?(a=b-this.uf|0,this.Ge.a[a>>>5|0].a[31&a]):this.R.a[b]}throw this.rh(b);};f.$classData=q({W8:0},!1,"scala.collection.immutable.Vector6",{W8:1,fC:1,$y:1,Zy:1,vh:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,Mg:1,xc:1,Yg:1,Sh:1,hd:1,Vb:1,wi:1,Uh:1,id:1,Cb:1,sg:1,l:1}); function ce(){var a=new S$;a.yf=LQ(new Sq);return a}function S$(){this.yf=null}S$.prototype=new D9;S$.prototype.constructor=S$;f=S$.prototype;f.Oc=function(){return"IndexedSeq"};f.m=function(){var a=new QX(this);return Kr(new Lr,a)};f.Sd=function(){var a=new QX(this);return uZ(new vZ,a)};f.mf=function(a,b){return l0(this,a,b)};f.ad=function(){return new Z8(this)};f.cc=function(a){return n0(this,a)};f.Jc=function(a){return q0(this,a)};f.Bb=function(a){return this.vc(X8(new Y8,this,a))}; f.Ja=function(a){return t0(this,a)};f.e=function(){return hc(this.yf.ja.charCodeAt(0))};f.Mc=function(){return SF(this)};f.ab=function(a){var b=this.yf.K();return b===a?0:bthis.BC))};f.Ik=function(){return l2()};f.va=function(a){return eB(this.Hn,a)};f.K=function(){return this.In};f.Q=function(){return this.In};f.b=function(){return 0===this.In}; f.ha=function(){this.AC=!this.b();return this.Hn};function wp(a,b){a.BC=1+a.BC|0;a.AC&&Zga(a);b=new z(b,u());0===a.In?a.Hn=b:a.No.p=b;a.No=b;a.In=1+a.In|0;return a}function ep(a,b){b=b.m();if(b.s()){var c=1,d=new z(b.t(),u());for(a.Hn=d;b.s();){var e=new z(b.t(),u());d=d.p=e;c=1+c|0}a.In=c;a.No=d}return a}f.Mc=function(){if(null===this.No)throw AH("last of empty ListBuffer");return this.No.z};f.Oc=function(){return"ListBuffer"}; f.zc=function(a){a=a.m();a.s()&&(a=ep(new fp,a),this.BC=1+this.BC|0,this.AC&&Zga(this),0===this.In?this.Hn=a.Hn:this.No.p=a.Hn,this.No=a.No,this.In=this.In+a.In|0);return this};f.$=function(a){return wp(this,a)};f.Kb=function(){return this.ha()};f.n=function(a){return eB(this.Hn,a|0)};f.Ub=function(){return l2()}; f.$classData=q({l$:0},!1,"scala.collection.mutable.ListBuffer",{l$:1,pC:1,kk:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,ok:1,ah:1,pk:1,$g:1,Ff:1,rC:1,xf:1,wf:1,Jn:1,id:1,Cb:1,Po:1,xg:1,sg:1,l:1});function T$(a,b){return b===a.Jd?a:tV(new sV,b,a.We)}function tV(a,b,c){a.Jd=b;a.We=c;return a}function vV(a){var b=new sV;tV(b,null,a);return b}function sV(){this.We=this.Jd=null}sV.prototype=new q9;sV.prototype.constructor=sV;f=sV.prototype; f.gR=function(a,b){b=this.jq().Dv(b);for(var c=this.m();c.s();){var d=a.n(c.t());b.$(d)}return b.Kb()};f.Gb=function(a){return XY(this,a)};f.Ja=function(a){return YY(this,a)};f.Oc=function(){return"SortedMap"};f.ti=function(){return new uV(this.We)};f.se=function(){return this.We};f.jq=function(){return xV()};f.m=function(){nJ();var a=this.Jd;nJ();var b=R();return new d4(a,b,this.We)};f.U=function(a){nJ();a=fJ(0,this.Jd,a,this.We);return null===a?R():new L(a.Wb)}; f.Se=function(a,b){a=fJ(nJ(),this.Jd,a,this.We);return null===a?Es(b):a.Wb};function U$(a,b,c){return T$(a,gJ(nJ(),a.Jd,b,c,!0,a.We))}function V$(a,b){a:{if(b instanceof sV){var c=a.We,d=b.We;if(null===c?null===d:c.i(d)){b=mJ(nJ(),a.Jd,b.Jd,a.We);break a}}if(b&&b.$classData&&b.$classData.rb.KL)if(b.b())b=a.Jd;else{for(c=new oZ(a);!b.b();)d=b.e(),c.bu=fR(c,c.bu,d.h(),d.j()),b=b.f();b=qJ(c.bu)}else{c=new oZ(a);for(b=b.m();b.s();)d=b.t(),c.bu=fR(c,c.bu,d.h(),d.j());b=qJ(c.bu)}}return T$(a,b)} f.Ca=function(a){var b=nJ(),c=this.Jd;null!==c&&mda(b,c,a)};f.og=function(a){var b=nJ(),c=this.Jd;null!==c&&qda(b,c,a)};f.ka=function(){return EI(nJ(),this.Jd)};f.Q=function(){return EI(nJ(),this.Jd)};f.b=function(){return 0===EI(nJ(),this.Jd)};f.oB=function(){var a=hJ(nJ(),this.Jd);return G(new H,a.Wa,a.Wb)};f.DB=function(){var a=iJ(nJ(),this.Jd);return G(new H,a.Wa,a.Wb)};function $ga(a,b){return T$(a,kJ(nJ(),a.Jd,new fn((c,d)=>!!b.n(G(new H,c,d)))))} f.BK=function(a){var b=Gda(nJ(),this.Jd,new fn((d,e)=>!!a.n(G(new H,d,e))));if(null===b)throw new w(b);var c=b.j();return G(new H,T$(this,b.h()),T$(this,c))}; f.i=function(a){if(a instanceof sV){var b=this.We,c=a.We;if(null===b?null===c:b.i(c)){nJ();b=this.Jd;a=a.Jd;c=this.We;var d;if(!(d=b===a)&&(d=null!==b)&&(d=null!==a)&&(d=(2147483647&b.ea)===(2147483647&a.ea))){b=new e4(b,c);a=new e4(a,c);for(c=!0;c&&null!==b.Hc&&null!==a.Hc;)b.Hc===a.Hc?(0===b.Wd?d=null:(b.Wd=-1+b.Wd|0,d=b.Cn.a[b.Wd]),b.Hc=d,0===a.Wd?d=null:(a.Wd=-1+a.Wd|0,d=a.Cn.a[a.Wd]),a.Hc=d):(c=Object.is(b.Hc.Wa,a.Hc.Wa)||b.hw.Yi(b.Hc.Wa,a.Hc.Wa)?ml(nl(),b.Hc.Wb,a.Hc.Wb):!1,b.Hc=r1(b,b.Hc.ta), a.Hc=r1(a,a.Hc.ta));d=c&&null===b.Hc&&null===a.Hc}return d}}return lY(this,a)};f.Ih=function(){return"TreeMap"};f.bf=function(a){return V$(this,a)};f.vc=function(a){return rV(xV(),a,this.We)};f.Gk=function(a){return rV(xV(),a,this.We)};f.IC=function(a,b){return p8(this,a,b)};f.Pn=function(a){return U$(this,a.h(),a.j())};f.jM=function(a){return O9(this,a)};f.Fk=function(a){return $ga(this,a)}; f.Bb=function(a){var b=EI(nJ(),this.Jd)-(0=b)a=vV(this.We);else if(b>=EI(nJ(),this.Jd))a=this;else{a=new sV;var c=nJ();b=pI(HI(c,this.Jd,b));a=tV(a,b,this.We)}return a};f.Jc=function(a){if(0>=a)var b=this;else if(a>=EI(nJ(),this.Jd))b=vV(this.We);else{b=new sV;var c=nJ();a=pI(GI(c,this.Jd,a));b=tV(b,a,this.We)}return b};f.Mc=function(){return this.DB()};f.e=function(){return this.oB()}; f.dG=function(a){a:{if(a instanceof rZ){var b=this.We,c=a.Xe;if(null===b?null===c:b.i(c)){b=nJ();a=pI(XI(b,this.Jd,a.Hf,this.We));a=T$(this,a);break a}}a=x5(this,a)}return a};f.am=function(a){return V$(this,a)};f.hy=function(a){return V$(this,a)};f.Em=function(a,b){return U$(this,a,b)};f.Dm=function(a,b){return U$(this,a,b)};f.xj=function(a){var b=nJ();a=pI(PI(b,this.Jd,a,this.We));return T$(this,a)};f.zr=function(){return sZ(new rZ,this.Jd,this.We)};f.Ht=function(){return sZ(new rZ,this.Jd,this.We)}; f.AB=function(){return sZ(new rZ,this.Jd,this.We)};f.$classData=q({G8:0},!1,"scala.collection.immutable.TreeMap",{G8:1,Rt:1,qm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Mk:1,ek:1,za:1,la:1,um:1,v:1,Pr:1,xc:1,Xt:1,FS:1,YB:1,zL:1,Iy:1,yL:1,A8:1,Sba:1,j6:1,RG:1,Cb:1,E8:1,sg:1,l:1});function W$(a,b){this.Aw=this.Yh=null;Q9(this,a,b)}W$.prototype=new r$;W$.prototype.constructor=W$;f=W$.prototype;f.Oc=function(){return"SortedMap"};f.i=function(a){return lY(this,a)};f.jq=function(){return this.Yh.jq()};f.se=function(){return this.Yh.se()}; function X$(a,b){b=a.Yh.hy(b);return new W$(b,a.Aw)}function Y$(a,b){return new W$(a.Yh.jq().qr(b,a.Yh.se()),a.Aw)}f.bf=function(a){return X$(this,a)};f.zr=function(){return new g9(this)};f.kM=function(a){return new W$(this,a)};f.vc=function(a){return Y$(this,a)};f.Gk=function(a){return Y$(this,a)};f.JJ=function(a){return Y$(this,a)};f.am=function(a){return X$(this,a)};f.hQ=function(a){return X$(this,a)};f.hy=function(a){return X$(this,a)};f.$=function(a){this.Yh.$(a);return this}; f.cQ=function(a){this.Yh.$(a);return this};f.Bm=function(a){this.Yh.Bm(a)};f.oT=function(a){this.Yh.Bm(a)};f.$classData=q({E$:0},!1,"scala.collection.mutable.SortedMap$WithDefault",{E$:1,fT:1,eH:1,qm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Mk:1,ek:1,za:1,la:1,um:1,v:1,CC:1,ah:1,mH:1,$g:1,Ff:1,xg:1,xf:1,wf:1,Jn:1,l:1,C$:1,YB:1,zL:1,Iy:1,yL:1,F$:1});function H1(a,b,c){a.fu=0;a.eu=b;a.Zg=c;return a}function kl(){var a=new I1;H1(a,new zc(16),0);return a} function Mv(a){var b=new I1;H1(b,new zc(1>>31|0|e>>31<<1;g=(0===e?-2147483632<(-2147483648^g):0>31,l=e;if(l===k?(-2147483648^h)<(-2147483648^b):l>>31|0|e<<1,g<<=1;else break}b=e;if(0===b?-1>=(-2147483648^g):0>b)b=g;else{if(2147483647===d)throw a=new xF,yF(a,"Collections can not have more than 2147483647 elements",null,!0),a;b=2147483647}b=new zc(b);VG(SG(),c,0,b,0,d);c=b}a.eu= c}f.va=function(a){var b=1+a|0;if(0>a)throw aL(new bL,a+" is out of bounds (min 0, max "+(-1+this.Zg|0)+")");if(b>this.Zg)throw aL(new bL,(-1+b|0)+" is out of bounds (min 0, max "+(-1+this.Zg|0)+")");return this.eu.a[a]};function N0(a,b,c){var d=1+b|0;if(0>b)throw aL(new bL,b+" is out of bounds (min 0, max "+(-1+a.Zg|0)+")");if(d>a.Zg)throw aL(new bL,(-1+d|0)+" is out of bounds (min 0, max "+(-1+a.Zg|0)+")");a.fu=1+a.fu|0;a.eu.a[b]=c}f.K=function(){return this.Zg}; function sl(a){return new j9(a,new U(()=>a.fu))}f.Ik=function(){return QF()};function ol(a,b){a.fu=1+a.fu|0;var c=a.Zg;K1(a,1+c|0);a.Zg=1+c|0;N0(a,c,b);return a}function Lv(a,b){if(b instanceof I1){var c=b.Zg;0c||c>=e)throw aL(new bL,c+" is out of bounds (min 0, max "+(-1+e|0)+")");e=b.a.length;if(0>d||d>=e)throw aL(new bL,d+" is out of bounds (min 0, max "+(-1+e|0)+")");a.ec=b;a.Zd=c;a.Ke=d}function O1(a,b,c,d){a.ec=b;a.Zd=c;a.Ke=d;aha(a,a.ec,a.Zd,a.Ke);return a}function Q1(){var a=new P1;O1(a,N1(T1(),16),0,0);return a} function P1(){this.ec=null;this.Ke=this.Zd=0}P1.prototype=new c$;P1.prototype.constructor=P1;function Z$(){}f=Z$.prototype=P1.prototype;f.ia=function(){return this.tK()};f.Ii=function(a){return E3(this,a)};f.cc=function(a){return F3(this,a)};f.gn=function(a){return G3(this,a)};f.Gb=function(a){return XY(this,a)};f.Ja=function(a){return YY(this,a)};f.Fk=function(a){return ZY(this,a)};f.Bb=function(a){return $Y(this,a)};f.m=function(){var a=new QX(this);return Kr(new Lr,a)}; f.Sd=function(){var a=new QX(this);return uZ(new vZ,a)};f.mf=function(a,b){return l0(this,a,b)};f.ad=function(){return new Z8(this)};f.Jc=function(a){return q0(this,a)};f.e=function(){return this.va(0)};f.Mc=function(){return SF(this)};f.ab=function(a){var b=(this.Ke-this.Zd|0)&(-1+this.ec.a.length|0);return b===a?0:ba||a>=b)throw aL(new bL,a+" is out of bounds (min 0, max "+(-1+b|0)+")");return this.ec.a[(this.Zd+a|0)&(-1+this.ec.a.length|0)]};function uU(a,b){var c=1+((a.Ke-a.Zd|0)&(-1+a.ec.a.length|0))|0;c>((a.Ke-a.Zd|0)&(-1+a.ec.a.length|0))&&c>=a.ec.a.length&&U1(a,c);a.ec.a[a.Ke]=b;a.Ke=(1+a.Ke|0)&(-1+a.ec.a.length|0);return a} function Hea(a,b){var c=1+((a.Ke-a.Zd|0)&(-1+a.ec.a.length|0))|0;c>((a.Ke-a.Zd|0)&(-1+a.ec.a.length|0))&&c>=a.ec.a.length&&U1(a,c);a.Zd=(-1+a.Zd|0)&(-1+a.ec.a.length|0);a.ec.a[a.Zd]=b}function vU(a,b){var c=b.Q();if(0((a.Ke-a.Zd|0)&(-1+a.ec.a.length|0))&&c>=a.ec.a.length&&U1(a,c),b=b.m();b.s();)c=b.t(),a.ec.a[a.Ke]=c,a.Ke=(1+a.Ke|0)&(-1+a.ec.a.length|0);else for(b=b.m();b.s();)c=b.t(),uU(a,c);return a} function wU(a){if(a.b())throw AH("empty collection");var b=a.ec.a[a.Zd];a.ec.a[a.Zd]=null;a.Zd=(1+a.Zd|0)&(-1+a.ec.a.length|0);return b}f.K=function(){return(this.Ke-this.Zd|0)&(-1+this.ec.a.length|0)};f.b=function(){return this.Zd===this.Ke};f.uK=function(){return O1(new P1,this.ec.ia(),this.Zd,this.Ke)};f.Ik=function(){return T1()};f.Gc=function(a,b,c){var d=(this.Ke-this.Zd|0)&(-1+this.ec.a.length|0),e=wG(xG(),a);d=c=a.ec.a.length||16b){var c=(a.Ke-a.Zd|0)&(-1+a.ec.a.length|0);b=N1(T1(),b);b=D5(a,b,0,c);aha(a,b,0,c)}}f.Oc=function(){return"ArrayDeque"};f.Ub=function(){return this.Ik()};f.tK=function(){return this.uK()};f.zc=function(a){return vU(this,a)};f.$=function(a){return uU(this,a)}; f.n=function(a){return this.va(a|0)};f.$classData=q({RL:0},!1,"scala.collection.mutable.ArrayDeque",{RL:1,pC:1,kk:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,ok:1,ah:1,pk:1,$g:1,Ff:1,rC:1,xf:1,wf:1,Jn:1,iH:1,Xk:1,hd:1,Vb:1,Yk:1,id:1,Cb:1,NS:1,sg:1,l:1});function tU(a){this.ec=null;this.Ke=this.Zd=0;a=N1(T1(),a);O1(this,a,0,0)}tU.prototype=new Z$;tU.prototype.constructor=tU;f=tU.prototype;f.Ik=function(){return p2()};f.Oc=function(){return"Queue"}; function bha(a){var b=PV(new QV,new tU(16));FZ(b,a);return b.lk}f.tK=function(){return bha(this)};f.uK=function(){return bha(this)};f.Ub=function(){return p2()};f.$classData=q({r$:0},!1,"scala.collection.mutable.Queue",{r$:1,RL:1,pC:1,kk:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,ok:1,ah:1,pk:1,$g:1,Ff:1,rC:1,xf:1,wf:1,Jn:1,iH:1,Xk:1,hd:1,Vb:1,Yk:1,id:1,Cb:1,NS:1,sg:1,l:1});function IX(a){this.ec=null;this.Ke=this.Zd=0;a=N1(T1(),a);O1(this,a,0,0)}IX.prototype=new Z$; IX.prototype.constructor=IX;f=IX.prototype;f.Ik=function(){return w2()};f.Oc=function(){return"Stack"};function cha(a){var b=PV(new QV,new IX(16));FZ(b,a);return b.lk}f.tK=function(){return cha(this)};f.uK=function(){return cha(this)};f.Ub=function(){return w2()};f.$classData=q({G$:0},!1,"scala.collection.mutable.Stack",{G$:1,RL:1,pC:1,kk:1,Tc:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Ac:1,za:1,la:1,kb:1,v:1,ok:1,ah:1,pk:1,$g:1,Ff:1,rC:1,xf:1,wf:1,Jn:1,iH:1,Xk:1,hd:1,Vb:1,Yk:1,id:1,Cb:1,NS:1,sg:1,l:1}); function YV(a){var b=new $$;b.Zk=new zK(null,0);b.Kn=a;return b}function $$(){this.Kn=this.Zk=null}$$.prototype=new e$;$$.prototype.constructor=$$;f=$$.prototype;f.hy=function(a){return w8(this,a)};f.ry=function(a){return b6(this,a)};f.Gb=function(a){return XY(this,a)};f.Ja=function(a){return YY(this,a)};f.Bb=function(a){return $Y(this,a)};f.Oc=function(){return"SortedMap"};f.i=function(a){return lY(this,a)};f.ti=function(){return $V().Dv(this.Kn)};f.se=function(){return this.Kn}; f.m=function(){if(this.b())return Rq().Pa;var a=this.Zk,b=R(),c=R();return new H4(a,b,c,this.Kn)};f.tj=function(){if(this.b())return Rq().Pa;var a=this.Zk,b=R(),c=R();return new I4(a,b,c,this.Kn)};f.ie=function(){if(this.b())return Rq().Pa;var a=this.Zk,b=R(),c=R();return new J4(a,b,c,this.Kn)};f.mg=function(){xK();var a=this.Zk;a.zi=null;a.gz=0};f.U=function(a){xK();a=nK(this.Zk.zi,a,this.Kn);return null===a?R():new L(a.Vr)};f.Ca=function(a){var b=xK(),c=this.Zk.zi;null!==c&&Oda(b,c,a)}; f.og=function(a){var b=xK(),c=this.Zk.zi;null!==c&&Qda(b,c,a)};f.ka=function(){return this.Zk.gz};f.Q=function(){return this.ka()};f.b=function(){xK();return null===this.Zk.zi};f.L=function(a){xK();return null!==nK(this.Zk.zi,a,this.Kn)};f.oB=function(){xK();var a=this.Zk.zi;a=null===a?null:uK(a);return(null===a?R():new L(G(new H,a.Oo,a.Vr))).o()};f.DB=function(){xK();var a=this.Zk.zi;if(null===a)a=null;else a:for(;;){if(null===a.Bc)break a;a=a.Bc}return(null===a?R():new L(G(new H,a.Oo,a.Vr))).o()}; f.Ih=function(){return"TreeMap"};f.bf=function(a){return w8(this,a)};f.zr=function(){return new g9(this)};f.vc=function(a){return XV($V(),a,this.Kn)};f.Gk=function(a){return XV($V(),a,this.Kn)};f.kM=function(a){return new W$(this,a)};f.am=function(a){return w8(this,a)};f.Mc=function(){return this.DB()};f.e=function(){return this.oB()}; f.Bm=function(a){xK();var b=this.Zk,c=nK(b.zi,a,this.Kn);if(null!==c){var d=c.Wc;if(null===c.Vc){var e=c.Bc;qK(b,c,c.Bc);a=c.bb}else if(null===c.Bc)e=c.Vc,qK(b,c,c.Vc),a=c.bb;else{var g=uK(c.Bc);d=g.Wc;e=g.Bc;g.bb===c?a=g:(a=g.bb,qK(b,g,g.Bc),g.Bc=c.Bc,g.Bc.bb=g);qK(b,c,g);g.Vc=c.Vc;g.Vc.bb=g;g.Wc=c.Wc}if(!d){for(c=e;c!==b.zi&&tK(c);)c===a.Vc?(c=a.Bc,c.Wc&&(c.Wc=!1,a.Wc=!0,oK(b,a),c=a.Bc),tK(c.Vc)&&tK(c.Bc)?(c.Wc=!0,c=a):(tK(c.Bc)&&(c.Vc.Wc=!1,c.Wc=!0,pK(b,c),c=a.Bc),c.Wc=a.Wc,a.Wc=!1,c.Bc.Wc=!1, oK(b,a),c=b.zi)):(c=a.Vc,c.Wc&&(c.Wc=!1,a.Wc=!0,pK(b,a),c=a.Vc),tK(c.Bc)&&tK(c.Vc)?(c.Wc=!0,c=a):(tK(c.Vc)&&(c.Bc.Wc=!1,c.Wc=!0,oK(b,c),c=a.Vc),c.Wc=a.Wc,a.Wc=!1,c.Vc.Wc=!1,pK(b,a),c=b.zi)),a=c.bb;null!==c&&(c.Wc=!1)}b.gz=-1+b.gz|0}}; f.$=function(a){xK();var b=this.Zk,c=a.h(),d=a.j(),e=this.Kn;a=null;for(var g=b.zi,h=1;null!==g&&0!==h;)a=g,h=e.Da(c,g.Oo),g=0>h?g.Vc:g.Bc;if(0===h)a.Vr=d;else{c=new yK(c,d,!0,null,null,a);for(null===a?b.zi=c:0>h?a.Vc=c:a.Bc=c;sK(c.bb);)c.bb===c.bb.bb.Vc?(a=c.bb.bb.Bc,sK(a)?(c.bb.Wc=!1,a.Wc=!1,c.bb.bb.Wc=!0,c=c.bb.bb):(c===c.bb.Bc&&(c=c.bb,oK(b,c)),c.bb.Wc=!1,c.bb.bb.Wc=!0,pK(b,c.bb.bb))):(a=c.bb.bb.Vc,sK(a)?(c.bb.Wc=!1,a.Wc=!1,c.bb.bb.Wc=!0,c=c.bb.bb):(c===c.bb.Vc&&(c=c.bb,pK(b,c)),c.bb.Wc=!1,c.bb.bb.Wc= !0,oK(b,c.bb.bb)));b.zi.Wc=!1;b.gz=1+b.gz|0}return this};f.jq=function(){return $V()};f.$classData=q({J$:0},!1,"scala.collection.mutable.TreeMap",{J$:1,eH:1,qm:1,ua:1,g:1,na:1,M:1,pa:1,N:1,oa:1,Mk:1,ek:1,za:1,la:1,um:1,v:1,CC:1,ah:1,mH:1,$g:1,Ff:1,xg:1,xf:1,wf:1,Jn:1,C$:1,YB:1,zL:1,Iy:1,yL:1,F$:1,Cb:1,RG:1,j6:1,sg:1,l:1});aa=new ma(0,0);Fd.jz=aa;typecheck=function(a){qe(Qf(),a)};new (md(fa).Ia)([]); (function(a){var b=document.querySelector("#mlscript-input");se(a,b.textContent);b.addEventListener("input",c=>{qe(Qf(),c)})})(Qf()); }).call(this); //# sourceMappingURL=mlscript-opt.js.map ================================================ FILE: build.sbt ================================================ import Wart._ import org.scalajs.linker.interface.OutputPatterns enablePlugins(ScalaJSPlugin) val scala3Version = "3.8.3" val directoryWatcherVersion = "0.18.0" val scalaTestVersion = "3.2.19" ThisBuild / scalaVersion := "2.13.18" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "hkust-taco.github.io" ThisBuild / organizationName := "HKUST-TACO" ThisBuild / scalacOptions ++= Seq( "-deprecation", "-feature", "-unchecked", "-language:higherKinds", "-language:implicitConversions", if (insideCI.value) "-Wconf:any:error" else "-Wconf:any:warning", ) lazy val root = project.in(file(".")) .aggregate(mlscriptJS, mlscriptJVM, ts2mlsTest, compilerJVM, hkmc2AllTests, coreJS, coreJVM) .settings( publish := {}, publishLocal := {}, ) lazy val hkmc2 = crossProject(JSPlatform, JVMPlatform).in(file("hkmc2")) .settings( scalaVersion := scala3Version, watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"src"/"test"/"diff", "*.mls", NothingFilter), // TODO remove when codebase becomes production-ready scalacOptions -= "-Wconf:any:error", // scalacOptions ++= Seq("-indent", "-rewrite"), scalacOptions ++= Seq("-new-syntax", "-rewrite"), // scalacOptions ++= Seq("-language:experimental.modularity"), // https://docs.scala-lang.org/scala3/reference/experimental/modularity.html libraryDependencies += "io.methvin" % "directory-watcher" % directoryWatcherVersion, libraryDependencies += "io.methvin" %% "directory-watcher-better-files" % directoryWatcherVersion, libraryDependencies += "com.lihaoyi" %%% "fansi" % "0.5.0", // Scala.js or Scala-Native libraryDependencies += "com.lihaoyi" %%% "sourcecode" % "0.4.2", // Scala.js / Scala Native libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.9.3", libraryDependencies += "org.scalactic" %%% "scalactic" % scalaTestVersion, libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % "test", watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"src"/"test"/"mlscript", "*.mls", NothingFilter), watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"src"/"test"/"mlscript-compile", "*.mls", NothingFilter), watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"src"/"test"/"mlscript", "*.cmd", NothingFilter), ) .jvmSettings( ) .jsSettings( scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) .withOutputPatterns(OutputPatterns.fromJSFile("MLscript.mjs")) }, libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0", ) .dependsOn(core) lazy val hkmc2JVM = hkmc2.jvm lazy val hkmc2JS = hkmc2.js lazy val hkmc2DiffTests = project.in(file("hkmc2DiffTests")) .dependsOn(hkmc2JVM % "compile->compile;test->test") .settings( scalaVersion := scala3Version, libraryDependencies += "org.scalactic" %%% "scalactic" % scalaTestVersion, libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % "test", Test/run/fork := true, // so that CTRL+C actually terminates the watcher ) /** Helper to create test subprojects that compile `.mls` files then run diff tests. * Each subproject depends on `hkmc2JVM` and `hkmc2DiffTests` for shared test infrastructure, * and uses `Def.sequential` to guarantee compile tests complete before diff tests start. */ def hkmc2TestSubproject(dirName: String, compileRunner: String, diffRunner: String): Project = Project(dirName, file(dirName)) .dependsOn(hkmc2JVM % "compile->compile;test->test") .dependsOn(hkmc2DiffTests % "compile->compile;test->test") .settings( scalaVersion := scala3Version, libraryDependencies += "org.scalactic" %%% "scalactic" % scalaTestVersion, libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % "test", Test / test := Def.sequential( (Test / testOnly).toTask(s" hkmc2.$compileRunner"), (Test / testOnly).toTask(s" hkmc2.$diffRunner"), ).value, Test/run/fork := true, // so that CTRL+C actually terminates the watcher ) lazy val hkmc2NofibTests = hkmc2TestSubproject("hkmc2NofibTests", "NofibCompileTestRunner", "NofibDiffTestRunner") lazy val hkmc2AppsTests = hkmc2TestSubproject("hkmc2AppsTests", "AppsCompileTestRunner", "AppsDiffTestRunner") lazy val hkmc2WasmTests = hkmc2TestSubproject("hkmc2WasmTests", "WasmCompileTestRunner", "WasmDiffTestRunner") lazy val hkmc2MainTests = project.in(file("hkmc2MainTests")) .settings( Test / test := ( (hkmc2DiffTests / Test / test) .dependsOn(hkmc2JVM / Test / test) ).value ) lazy val hkmc2MostTests = project.in(file("hkmc2MostTests")) .settings( Test / test := ( (hkmc2DiffTests / Test / test) .dependsOn(hkmc2NofibTests / Test / test) .dependsOn(hkmc2AppsTests / Test / test) .dependsOn(hkmc2WasmTests / Test / test) .dependsOn(hkmc2JVM / Test / test) ).value ) lazy val hkmc2AllTests = project.in(file("hkmc2AllTests")) .settings( Test / test := ( (hkmc2DiffTests / Test / test) .dependsOn(hkmc2NofibTests / Test / test) .dependsOn(hkmc2AppsTests / Test / test) .dependsOn(hkmc2WasmTests / Test / test) .dependsOn(hkmc2JVM / Test / test) .dependsOn(hkmc2JS / Test / test) .dependsOn(hkmc2Benchmarks / Test / compile) ).value ) lazy val core = crossProject(JSPlatform, JVMPlatform).in(file("core")) .settings( sourceDirectory := baseDirectory.value.getParentFile()/"shared", ) lazy val coreJVM = core.jvm lazy val coreJS = core.js lazy val mlscript = crossProject(JSPlatform, JVMPlatform).in(file(".")) .settings( name := "mlscript", scalacOptions ++= Seq( "-Ywarn-value-discard", "-Ypatmat-exhaust-depth:160", ), wartremoverWarnings ++= Warts.allBut( Recursion, Throw, Nothing, Return, While, IsInstanceOf, Var, MutableDataStructures, NonUnitStatements, DefaultArguments, ImplicitParameter, ImplicitConversion, StringPlusAny, Any, ToString, JavaSerializable, Serializable, Product, ToString, LeakingSealed, Overloading, Option2Iterable, IterableOps, ListAppend, SeqApply, TripleQuestionMark, PartialFunctionApply, ), libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.12" % Test, libraryDependencies += "com.lihaoyi" %%% "sourcecode" % "0.3.1", libraryDependencies += "com.lihaoyi" %%% "fastparse" % "2.3.3", libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.8.0", // watchSources += WatchSource( sourceDirectory.value.getParentFile().getParentFile()/"shared/src/test/diff", "*.fun", NothingFilter), watchSources += WatchSource( sourceDirectory.value.getParentFile().getParentFile()/"shared/src/test/diff", "*.mls", NothingFilter), Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oC"), ) .jsSettings( scalaJSUseMainModuleInitializer := true, libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0", ) .dependsOn(core) lazy val mlscriptJVM = mlscript.jvm lazy val mlscriptJS = mlscript.js lazy val ts2mls = crossProject(JSPlatform, JVMPlatform).in(file("ts2mls")) .settings( name := "ts2mls", ) .jvmSettings() .jsSettings( libraryDependencies += "org.scalatest" %%% "scalatest" % "3.2.12" % "test" ) .dependsOn(mlscript % "compile->compile;test->test") lazy val ts2mlsJS = ts2mls.js lazy val ts2mlsJVM = ts2mls.jvm lazy val ts2mlsTest = project.in(file("ts2mls")) .settings( Test / test := ((ts2mlsJVM / Test / test) dependsOn (ts2mlsJS / Test / test)).value ) lazy val compiler = crossProject(JSPlatform, JVMPlatform).in(file("compiler")) .settings( name := "mlscript-compiler", scalaVersion := scala3Version, sourceDirectory := baseDirectory.value.getParentFile()/"shared", watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"test"/"diff", "*.mls", NothingFilter), watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"test"/"diff-ir", "*.mls", NothingFilter), ) .dependsOn(mlscript % "compile->compile;test->test") lazy val compilerJVM = compiler.jvm lazy val compilerJS = compiler.js lazy val hkmc2Benchmarks = project.in(file("hkmc2Benchmarks")) .settings( name := "benchmark", scalaVersion := scala3Version, sourceDirectory := baseDirectory.value/"src", libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % "test", watchSources += WatchSource( baseDirectory.value/"src"/"test"/"bench", "*.mls", NothingFilter), Test/run/fork := true, // so that CTRL+C actually terminates the watcher ) .dependsOn(hkmc2JVM) .dependsOn(hkmc2DiffTests % "compile->compile;test->test") ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ClassLifter.scala ================================================ package mlscript package compiler import mlscript.utils.* import mlscript.utils.shorthands.* import scala.collection.mutable.StringBuilder as StringBuilder import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.ArrayBuffer as ArrayBuffer import mlscript.codegen.CodeGenError class CompilerError(error: String) extends Error(error) class ClassLifter(logDebugMsg: Boolean = false) { type ClassName = String type FieldName = String case class LocalContext(vSet: Set[Var], tSet: Set[TypeName]){ def ++(rst: LocalContext) = LocalContext(vSet ++ rst.vSet, tSet ++ rst.tSet) def -+(rst: LocalContext) = LocalContext(vSet -- rst.vSet, tSet ++ rst.tSet) def addV(rst: IterableOnce[Var]) = LocalContext(vSet ++ rst, tSet) def addV(nV: Var) = LocalContext(vSet + nV, tSet) def extV(rst: IterableOnce[Var]) = LocalContext(vSet -- rst, tSet) def extV(nV: Var) = LocalContext(vSet - nV, tSet) def addT(rst: IterableOnce[TypeName]) = LocalContext(vSet, tSet ++ rst) def addT(nT: TypeName) = LocalContext(vSet, tSet + nT) def extT(rst: IterableOnce[TypeName]) = LocalContext(vSet, tSet -- rst) def vSet2tSet = LocalContext(Set(), vSet.map(t => TypeName(t.name))) def moveT2V(ts: Set[TypeName]) = { val inters = tSet.intersect(ts) LocalContext(vSet ++ inters.map(x => Var(x.name)), tSet -- inters) } def intersect(rst: LocalContext) = LocalContext(vSet intersect rst.vSet, tSet intersect rst.tSet) def intersectV(rst: Set[Var]) = LocalContext(vSet.intersect(rst), tSet) def contains(v: Var) = vSet.contains(v) || tSet.contains(TypeName(v.name)) def contains(tv: TypeName) = vSet.contains(Var(tv.name)) || tSet.contains(tv) override def toString(): String = "(" ++ vSet.mkString(", ") ++ "; " ++ tSet.mkString(", ") ++ ")" } private def asContext(v: Var) = LocalContext(Set(v), Set()) private def asContextV(vS: IterableOnce[Var]) = LocalContext(vS.iterator.toSet, Set()) private def asContext(t: TypeName) = LocalContext(Set(), Set(t)) private def asContextT(tS: IterableOnce[TypeName]) = LocalContext(Set(), tS.iterator.toSet) private def emptyCtx = LocalContext(Set(), Set()) private def emptyCtxObj = LocalContext(Set(Var("this")), Set()) case class ClassInfoCache( originNm: TypeName, liftedNm: TypeName, var capturedParams: LocalContext, fields: MutSet[Var], innerClses: MutMap[TypeName, ClassInfoCache], supClses: Set[TypeName], outerCls: Option[ClassInfoCache], body: NuTypeDef, depth: Int ){ override def toString(): String = liftedNm.name ++ "@" ++ capturedParams.toString() ++ "^" ++ outerCls.map(_.liftedNm.toString()).getOrElse("_") } type ClassCache = Map[TypeName, ClassInfoCache] type NamePath = List[String] var retSeq: List[NuTypeDef] = Nil val globalFunctions: ArrayBuffer[NuFunDef] = ArrayBuffer() var anonymCnt: Int = 0 var clsCnt: Int = 0 val logOutput: StringBuilder = new StringBuilder val primiTypes = new mlscript.Typer(false, false, false, true).primitiveTypes private def log(str: String): Unit = { logOutput.append(str+"\n") if(logDebugMsg){ println(str) } } def getLog: String = logOutput.toString() private def genAnoName(textNm: String = "Ano"): String = { anonymCnt = anonymCnt + 1 textNm ++ "$" ++ anonymCnt.toString() } private def genParName(clsNm: String): String = "par$" ++ clsNm private def genInnerName(outerClsNm: TypeName, innerNm: String) = { clsCnt = clsCnt+1 outerClsNm.name ++ "_" ++ innerNm ++ "$" ++ clsCnt.toString() } private def tupleEntityToVar(fld: (Option[Var], Fld)): Option[Var] = fld match{ case (None, Fld(_, v: Var)) => Some(v) case (Some(v: Var), _) => Some(v) case _ => None } private def getFields(etts: List[Statement]): Set[Var] = { etts.flatMap{ case NuFunDef(_, nm, _, _, _) => Some(nm) case nuty: NuTypeDef => Some(Var(nuty.name)) case Let(_, name, _, _) => Some(name) case _ => None }.toSet } private def selPath2Term(path: List[String], varNm: Var): Term = path match { case Nil => varNm case (head :: tail) => Sel(selPath2Term(tail, Var(head)), varNm) } private def buildPathToVar(v: Var)(using outer: Option[ClassInfoCache]): Option[Term] = { def findField(ot: Option[ClassInfoCache]): Option[List[TypeName]] = { ot match{ case None => None case Some(info) => if(info.fields.contains(v) || info.capturedParams.vSet.contains(v)) Some(List(info.liftedNm)) else findField(info.outerCls).map(l => info.liftedNm :: l) } } val tmp = findField(outer) tmp.map(l => { selPath2Term(l.map(x => genParName(x.name)).updated(0, "this").reverse, v) }) } private def toFldsEle(trm: Term): (Option[Var], Fld) = (None, Fld(FldFlags(false, false, false), trm)) def getSupClsInfoByTerm(parentTerm: Term): (List[TypeName], List[(Var, Fld)]) = parentTerm match{ case Var(nm) => List(TypeName(nm)) -> Nil case App(Var(nm), _: Tup) => List(TypeName(nm)) -> Nil case App(App(Var("&"), t1), t2) => val ret1 = getSupClsInfoByTerm(t1) val ret2 = getSupClsInfoByTerm(t2) (ret1._1 ++ ret2._1) -> (ret1._2 ++ ret2._2) case TyApp(trm, targs) => getSupClsInfoByTerm(trm) //SPECIAL CARE: Tup related issue case Tup(flds) => val ret = flds.filter(_._1.isEmpty).map(fld => getSupClsInfoByTerm(fld._2.value)).unzip ret._1.flatten -> ret._2.fold(Nil)(_ ++ _) case Bra(rcd, trm) => getSupClsInfoByTerm(trm) case Rcd(fields) => (Nil, fields) case _ => (Nil, Nil) } private def splitEntities(etts: List[Statement]) = { val tmp = etts.map{ case cls: NuTypeDef => (Some(cls), None, None) case func: NuFunDef => (None, Some(func), None) case trm: Term => (None, None, Some(trm)) case others => throw CodeGenError(s"Not supported entity type: $others") }.unzip3 (tmp._1.flatten, tmp._2.flatten, tmp._3.flatten) } private def genClassNm(orgNm: String)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): TypeName = { TypeName(outer match{ case None => clsCnt = clsCnt+1 orgNm ++ "$" ++ clsCnt.toString() case Some(value) => genInnerName(value.liftedNm, orgNm) }) } private def getFreeVars(stmt: Located)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): LocalContext = stmt match{ case v:Var => val caseEmpty = ctx.vSet.contains(v) || cache.contains(TypeName(v.name)) || globFuncs.contains(v) || primiTypes.contains(v.name) val caseThis = buildPathToVar(v).isDefined && !ctx.vSet.contains(Var("this")) log(s"get free var find $v: $caseEmpty/$caseThis") if(caseEmpty) then emptyCtx else if(caseThis) asContext(Var("this")) else asContext(v) case t: NamedType => log(s"get type $t under $ctx, $cache, $outer") asContextT(t.collectTypeNames.map(TypeName(_)).filterNot(x => ctx.contains(x) || cache.contains(x) || primiTypes.contains(x.name))) case Lam(lhs, rhs) => val lhsVs = getFreeVars(lhs) getFreeVars(rhs)(using ctx ++ lhsVs) -+ lhsVs case NuFunDef(_, vm, _, tps, Left(trm)) => getFreeVars(trm).extV(vm).extT(tps) case OpApp(_, trm) => getFreeVars(trm) case Sel(trm, _) => getFreeVars(trm) case Asc(trm, tp) => getFreeVars(trm) ++ getFreeVars(tp) case Tup(tupLst) => tupLst.map{ case (Some(v), Fld(_, trm)) => getFreeVars(trm).vSet2tSet.addV(v) case (_, Fld(_, rhs)) => getFreeVars(rhs) }.fold(emptyCtx)(_ ++ _) case Rcd(fields) => fields.map{ case (v, Fld(_, trm)) => getFreeVars(trm).vSet2tSet }.fold(emptyCtx)(_ ++ _) case TyApp(trm, tpLst) => getFreeVars(trm).addT(tpLst.flatMap(_.collectTypeNames.map(TypeName(_)))) case NuTypeDef(_, nm, tps, param, _, _, pars, _, _, body) => val prmVs = param.map(getFreeVars(_)(using emptyCtx, Map(), globFuncs, None)).getOrElse(emptyCtx) val newVs = prmVs.vSet ++ getFields(body.entities) + Var(nm.name) val nCtx = ctx.addV(newVs).addT(nm).addT(tps.map(_._2)) val parVs = pars.map(getFreeVars(_)(using nCtx)).fold(emptyCtx)(_ ++ _) val bodyVs = body.entities.map(getFreeVars(_)(using nCtx)).fold(emptyCtx)(_ ++ _) (bodyVs ++ parVs -+ prmVs).extT(tps.map(_._2)) case Blk(stmts) => val newVs = getFields(stmts) stmts.map(getFreeVars(_)(using ctx.addV(newVs))).fold(emptyCtx)(_ ++ _) case Let(isRec, name, rhs, body) => getFreeVars(rhs)(using ctx.addV(name)) ++ getFreeVars(body)(using ctx.addV(name)) case others => others.children.map(getFreeVars).fold(emptyCtx)(_ ++ _) } private def collectClassInfo(cls: NuTypeDef, preClss: Set[TypeName])(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): ClassInfoCache = { val NuTypeDef(_, nm, tps, param, _, _, pars, _, _, body) = cls log(s"grep context of ${cls.nme.name} under $ctx # $cache # $globFuncs # $outer ") val (clses, funcs, trms) = splitEntities(cls.body.entities) val (supNms, rcdFlds) = pars.map(getSupClsInfoByTerm).unzip val flds = rcdFlds.flatten.map{ case (v, Fld(_, trm)) => val tmp = getFreeVars(trm)(using emptyCtxObj) val ret = tmp.tSet ++ tmp.vSet.map(x => TypeName(x.name)) (v, ret) }.unzip log(s"par record: ${flds._2.flatten}") val fields = (param.fold(Nil)(t => t.fields).flatMap(tupleEntityToVar) ++ funcs.map(_.nme) ++ clses.map(x => Var(x.nme.name)) ++ trms.flatMap(grepFieldsInTrm) ++ flds._1).toSet val nCtx = ctx.addV(fields).addV(flds._1).extT(tps.map(_._2)).addV(Var("this")) val tmpCtx = ((body.entities.map(getFreeVars(_)(using nCtx)) ++ pars.map(getFreeVars(_)(using nCtx))).fold(emptyCtx)(_ ++ _).moveT2V(preClss) ).addT(flds._2.flatten.toSet).extV(supNms.flatten.map(x => Var(x.name))) log(s"ret ctx for ${cls.nme.name}: $tmpCtx") val ret = ClassInfoCache(nm, genClassNm(nm.name), tmpCtx, MutSet(fields.toSeq*), MutMap(), supNms.flatten.toSet, outer, cls, outer.map(_.depth).getOrElse(0)+1) ret } private def liftCaseBranch(brn: CaseBranches)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (CaseBranches, LocalContext) = brn match{ case k @ Case(v: Var, body, rest) => val nTrm = liftTerm(body)(using ctx.addV(v)) val nRest = liftCaseBranch(rest) (Case(v, nTrm._1, nRest._1)(k.refined), nTrm._2 ++ nRest._2) case k @ Case(pat, body, rest) => val nTrm = liftTerm(body) val nRest = liftCaseBranch(rest) (Case(pat, nTrm._1, nRest._1)(k.refined), nTrm._2 ++ nRest._2) case Wildcard(body) => val nTrm = liftTerm(body) (Wildcard(nTrm._1), nTrm._2) case NoCases => (brn, emptyCtx) } private def liftIf(body: IfBody)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (IfBody, LocalContext) = body match{ case IfElse(expr) => val ret = liftTerm(expr) (IfElse(ret._1), ret._2) case IfThen(expr, rhs) => val nE = liftTerm(expr) val nR = liftTerm(rhs) (IfThen(nE._1, nR._1), nE._2 ++ nR._2) case _ => throw CompilerError(s"Unknown IfBody: ${body}") } private def liftTuple(tup: Tup)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Tup, LocalContext) = { val ret = tup.fields.map{ case (None, Fld(flags, trm)) => val tmp = liftTerm(trm) ((None, Fld(flags, tmp._1)), tmp._2) case (Some(v), Fld(flags, trm)) => val nTrm = liftTermAsType(trm) ((Some(v), Fld(flags, nTrm._1)), nTrm._2) }.unzip (Tup(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) } private def liftConstr(tp: TypeName, prm: Tup)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (TypeName, Tup, LocalContext) = { def findAncestor(crt: ClassInfoCache, target: Option[ClassInfoCache]): Option[(List[String], Option[String])] = { (crt.outerCls, target) match{ case (None, None) => None case (Some(c1), Some(c2)) if c1.depth == c2.depth => Some((crt.liftedNm.name :: Nil, Some(genParName(c1.liftedNm.name)))) case (Some(c1), _) => findAncestor(c1, target).map(l => (crt.liftedNm.name :: l._1, l._2)) case (None, _) => Some(Nil, None) } } log(s"lift constr for $tp$prm under $ctx, $cache, $outer") if(!cache.contains(tp)){ throw new CodeGenError(s"Cannot find type ${tp.name}. Class values are not supported in lifter. ") } else { val cls@ClassInfoCache(_, nm, capParams, _, _, _, out, _, _) = cache.get(tp).get val nParams = liftTuple(Tup(prm.fields ++ capParams.vSet.toList.map(toFldsEle(_)))) if(outer.isDefined){ log("find ancestor " + outer.get + " & " + cls) findAncestor(outer.get, out) match{ case None => log("case 1") (nm, nParams._1, nParams._2) case Some((selPt, Some(varNm))) => log("case 2") (nm, Tup(toFldsEle(selPath2Term(selPt.map(genParName).updated(0, "this").reverse, Var(varNm))) :: nParams._1.fields), nParams._2) case Some((_, None)) => log("case 3") (nm, Tup(toFldsEle(Var("this")) :: nParams._1.fields), nParams._2) } } else (nm, nParams._1, nParams._2) } } private def newLambObj(lhs: Term, rhs: Term) = New(None, TypingUnit(List(NuFunDef(None, Var("apply"), None, Nil, Left(Lam(lhs, rhs)))(N, N, N, N, N, false, Nil)))) private def liftTerm(target: Term)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Term, LocalContext) = log(s"liftTermNew $target in $ctx, $cache, $globFuncs, $outer") target match { case v: Var => if(globFuncs.contains(v)) { (globFuncs.get(v).get) } else if(cache.contains(TypeName(v.name))){ val cls@ClassInfoCache(_, nm, capParams, _, _, _, out, _, _) = cache.get(TypeName(v.name)).get (Var(nm.name), emptyCtx) } else if(ctx.contains(v) || v.name.equals("this") || primiTypes.contains(v.name)) (v, emptyCtx) else { buildPathToVar(v) match{ case Some(value) => (value, emptyCtx) case None => (v, asContext(v)) } } case Lam(lhs, rhs) => val prmCnt = getFreeVars(lhs)(using emptyCtx, cache, globFuncs, None).vSet.size val nTpNm = TypeName(genAnoName("Lambda"+prmCnt)) val anoCls = NuTypeDef( Cls, nTpNm, Nil, S(Tup(Nil)), N, N, Nil, N, N, TypingUnit(List(NuFunDef(None, Var("apply"), N, Nil, Left(Lam(lhs, rhs)))(N, N, N, N, N, false, Nil))))(N, N, Nil) val nSta = App(Var(nTpNm.name),Tup(Nil)) val ret = liftEntities(List(anoCls, nSta)) (Blk(ret._1), ret._2) case t: Tup => liftTuple(t) case Rcd(fields) => val ret = fields.map{ case (v, Fld(flags, trm)) => val tmp = liftTermAsType(trm) ((v, Fld(flags, tmp._1)), tmp._2) }.unzip (Rcd(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) case Asc(trm, ty) => val ret = liftTerm(trm) val nTy = liftType(ty) (Asc(ret._1, nTy._1), ret._2 ++ nTy._2) case NuNew(cls) => liftTerm(App(NuNew(cls), Tup(Nil))) case App(NuNew(cls), args) => (cls, args) match { case (v: Var, args: Tup) => val ret = liftConstr(TypeName(v.name), args) (App(NuNew(Var(ret._1.name)), ret._2), ret._3) case _ => ??? } case Rft(NuNew(cls), tu) => liftTerm(Rft(App(NuNew(cls), Tup(Nil)), TypingUnit(Nil))) case Rft(App(NuNew(cls), args), tu) => (cls, args) match { case (v: Var, args: Tup) => liftTerm(New(Some((TypeName(v.name), args)), tu)) case _ => ??? } case App(v: Var, prm: Tup) if cache.contains(TypeName(v.name)) => val ret = liftConstr(TypeName(v.name), prm) (App(Var(ret._1.name), ret._2), ret._3) case App(v: Var, prm: Tup) if globFuncs.contains(v) => val (nFuncName, nCtxs) = globFuncs.get(v).get val addiArgs = nCtxs.vSet.toList.map(toFldsEle(_)) val nPrm = liftTuple(prm) (App(nFuncName, Tup(nPrm._1.fields ++ addiArgs)), nPrm._2) case App(lhs, rhs) => val (ltrm, lctx) = liftTerm(lhs) val (rtrm, rctx) = liftTerm(rhs) (App(ltrm, rtrm), lctx ++ rctx) case Assign(lhs, rhs) => val (ltrm, lctx) = liftTerm(lhs) val (rtrm, rctx) = liftTerm(rhs) (Assign(ltrm, rtrm), lctx ++ rctx) case Bind(lhs, rhs) => val (ltrm, lctx) = liftTerm(lhs) val (rtrm, rctx) = liftTermAsType(rhs) (Bind(ltrm, rtrm), lctx ++ rctx) case Bra(rcd, trm) => val ret = liftTerm(trm) (Bra(rcd, ret._1), ret._2) case CaseOf(trm, cases) => val nTrm = liftTerm(trm) val nCases = liftCaseBranch(cases) (CaseOf(nTrm._1, nCases._1), nTrm._2 ++ nCases._2) case If(body, None) => val ret = liftIf(body) (If(ret._1, None), ret._2) case If(body, Some(trm)) => val ret = liftIf(body) val nTrm = liftTerm(trm) (If(ret._1, Some(nTrm._1)), ret._2 ++ nTrm._2) case Let(isRec, name, rhs, body) => val nRhs = if(isRec) liftTerm(rhs)(using ctx.addV(name), cache, globFuncs.-(name)) else liftTerm(rhs) val nBody = liftTerm(body)(using ctx.addV(name), cache, globFuncs.-(name)) (Let(isRec, name, nRhs._1, nBody._1), nRhs._2 ++ nBody._2) case Sel(receiver, fieldName) => val nRec = liftTerm(receiver) (Sel(nRec._1, fieldName), nRec._2) case Splc(fields) => throw CompilerError(s"Unimplemented liftTerm: ${target}") case Subs(arr, idx) => val (ltrm, lctx) = liftTerm(arr) val (rtrm, rctx) = liftTerm(idx) (Subs(ltrm, rtrm), lctx ++ rctx) case Test(trm, ty) => val (ltrm, lctx) = liftTerm(trm) val (rtrm, rctx) = liftTermAsType(ty) (Test(ltrm, rtrm), lctx ++ rctx) case TyApp(lhs, targs) => val ret = liftTerm(lhs) val nTs = targs.map(liftType).unzip (TyApp(ret._1, nTs._1), nTs._2.fold(ret._2)(_ ++ _)) case With(trm, fields) => throw CompilerError(s"Unimplemented liftTerm: ${target}") case New(Some((t: TypeName, prm: Tup)), TypingUnit(Nil)) => val ret = liftConstr(t, prm) (New(Some((ret._1, ret._2)), TypingUnit(Nil)), ret._3) case New(Some((t: TypeName, prm: Tup)), tu) => log(s"new $t in ctx $ctx, $cache, $outer") val nTpNm = TypeName(genAnoName(t.name)) val cls = cache.get(t).get val supArgs = Tup(cls.body.params.fold(Nil)(t => t.fields).flatMap(tupleEntityToVar).map(toFldsEle)) val anoCls = NuTypeDef(Cls, nTpNm, Nil, cls.body.params, None, None, List(App(Var(t.name), supArgs)), None, None, tu)(None, None, Nil) val nSta = New(Some((nTpNm, prm)), TypingUnit(Nil)) val ret = liftEntities(List(anoCls, nSta)) (Blk(ret._1), ret._2) case New(None, tu) => val nTpNm = TypeName(genAnoName()) val anoCls = NuTypeDef(Cls, nTpNm, Nil, None, None, None, Nil, None, None, tu)(None, None, Nil) val nSta = New(Some((nTpNm, Tup(Nil))), TypingUnit(Nil)) val ret = liftEntities(List(anoCls, nSta)) (Blk(ret._1), ret._2) case New(head, body) => throw CompilerError(s"Unimplemented liftTerm: ${target}") case Blk(stmts) => val ret = liftEntities(stmts) (Blk(ret._1), ret._2) case lit: Lit => (lit, emptyCtx) case Inst(bod) => val (trm, ctx) = liftTerm(bod) (Inst(trm), ctx) case Forall(ps, bod) => val (trm, ctx) = liftTerm(bod) (Forall(ps, trm), ctx) case Where(bod, sts) => val (bod2, ctx) = liftTerm(bod) val (sts2, ctx2) = liftEntities(sts) (Where(bod2, sts2), ctx2) case _: Eqn | _: Super | _: Rft | _: While | _: Quoted | _: Unquoted | _: Ann => throw CompilerError(s"Unimplemented liftTerm: ${target}") // TODO case patmat: AdtMatchWith => lastWords(s"Cannot liftTermNew ${patmat}") } //serves for lifting Tup(Some(_), Fld(_, _, trm)), where trm refers to a type private def liftTermAsType(target: Term)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Term, LocalContext) = log(s"liftTermAsType $target in $ctx, $cache") target match{ case v: Var => if (!ctx.contains(v) && !primiTypes.contains(v.name)) cache.get(TypeName(v.name)).map(x => Var(x.liftedNm.name) -> emptyCtx).getOrElse(v -> asContext(TypeName(v.name))) else (v -> emptyCtx) case Lam(lhs, rhs) => val lret = liftTermAsType(lhs) val rret = liftTermAsType(rhs) Lam(lret._1, rret._1) -> (lret._2 ++ rret._2) case TyApp(lhs, targs) => val lret = liftTermAsType(lhs) val tRets = targs.map(liftType).unzip TyApp(lret._1, tRets._1) -> (tRets._2.fold(lret._2)(_ ++ _)) case Tup(fields) => val ret = fields.map{ case (oV, Fld(flags, trm)) => val tmp = liftTermAsType(trm) (oV, Fld(flags, tmp._1)) -> tmp._2 }.unzip Tup(ret._1) -> ret._2.fold(emptyCtx)(_ ++ _) case Bra(rcd, trm) => val ret = liftTermAsType(trm) Bra(rcd, ret._1) -> ret._2 case Rcd(fields) => val ret = fields.map{ case (v, Fld(flags, trm)) => val tmp = liftTermAsType(trm) ((v, Fld(flags, tmp._1)), tmp._2) }.unzip (Rcd(ret._1), ret._2.fold(emptyCtx)(_ ++ _)) case _ => throw CompilerError(s"Unimplemented liftTermAsType: ${target}") } private def liftTypeName(target: TypeName)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (TypeName, LocalContext) = { if(ctx.contains(target) || primiTypes.contains(target.name)) { target -> emptyCtx } else { cache.get(target).map(x => (x.liftedNm -> emptyCtx)).getOrElse(target -> asContext(target)) } } private def liftTypeField(target: Field)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Field, LocalContext) = { val (inT, iCtx) = target.in.map(liftType).unzip val (outT, oCtx) = liftType(target.out) Field(inT, outT) -> (iCtx.getOrElse(emptyCtx) ++ oCtx) } private def liftType(target: Type)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (Type, LocalContext) = target match{ case AppliedType(base, targs) => val (nTargs, nCtx) = targs.map(liftType).unzip val (nBase, bCtx) = liftTypeName(base) AppliedType(nBase, nTargs) -> (nCtx.fold(emptyCtx)(_ ++ _) ++ bCtx) case Bounds(lb, ub) => val nlhs = liftType(lb) val nrhs = liftType(ub) Bounds(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) case Constrained(base: Type, bounds, where, tscs) => val (nTargs, nCtx) = bounds.map { case (tv, Bounds(lb, ub)) => val nlhs = liftType(lb) val nrhs = liftType(ub) (tv, Bounds(nlhs._1, nrhs._1)) -> (nlhs._2 ++ nrhs._2) }.unzip val (bounds2, nCtx2) = where.map { case Bounds(lb, ub) => val nlhs = liftType(lb) val nrhs = liftType(ub) Bounds(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) }.unzip val (tscs0, nCtx3) = tscs.map { case (tvs, cs) => val (ntvs,c0) = tvs.map { case (p,v) => val (nv, c) = liftType(v) (p,nv) -> c }.unzip val (ncs,c1) = cs.map(_.map(liftType).unzip).unzip (ntvs,ncs) -> (c0 ++ c1.flatten) }.unzip val (nBase, bCtx) = liftType(base) Constrained(nBase, nTargs, bounds2, tscs0) -> ((nCtx ++ nCtx2 ++ nCtx3.flatten).fold(emptyCtx)(_ ++ _) ++ bCtx) case Constrained(_, _, _, _) => die case Function(lhs, rhs) => val nlhs = liftType(lhs) val nrhs = liftType(rhs) Function(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) case Neg(base) => val ret = liftType(base) Neg(ret._1) -> ret._2 case Record(fields) => val ret = fields.map(x => val (nX, nXCtx) = liftTypeName(TypeName(x._1.name)) val (nFld, nFldCtx) = liftTypeField(x._2) (Var(nX.name) -> nFld) -> (nXCtx ++ nFldCtx) ).unzip Record(ret._1) -> ret._2.fold(emptyCtx)(_ ++ _) case Recursive(uv, body) => val ret = liftType(body) Recursive(uv, ret._1) -> ret._2 case Rem(base, names) => val ret = liftType(base) Rem(ret._1, names) -> ret._2 case Splice(fields) => val ret = fields.map{ case Left(tp) => val tmp = liftType(tp) Left(tmp._1) -> tmp._2 case Right(fld) => val tmp = liftTypeField(fld) Right(tmp._1) -> tmp._2 }.unzip Splice(ret._1) -> ret._2.fold(emptyCtx)(_ ++ _) case Tuple(fields) => val ret = fields.map(x => val (nFld, nFldCtx) = liftTypeField(x._2) (x._1 -> nFld) -> nFldCtx ).unzip Tuple(ret._1) -> ret._2.fold(emptyCtx)(_ ++ _) case WithExtension(base, rcd) => val nBase = liftType(base) val nRcd = liftType(rcd) WithExtension(nBase._1, nRcd._1.asInstanceOf[Record]) -> (nBase._2 ++ nRcd._2) case Inter(lhs, rhs) => val nlhs = liftType(lhs) val nrhs = liftType(rhs) Inter(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) case Union(lhs, rhs) => val nlhs = liftType(lhs) val nrhs = liftType(rhs) Union(nlhs._1, nrhs._1) -> (nlhs._2 ++ nrhs._2) case x : TypeName => liftTypeName(x) case PolyType(targs, body) => val (body2, ctx) = liftType(body) PolyType(targs, body2) -> ctx case Top | Bot | _: Literal | _: TypeTag | _: TypeVar => target.asInstanceOf[Type] -> emptyCtx case _: Selection => throw CompilerError(s"Unimplemented liftType: ${target}") // TODO } private def liftMemberFunc(func: NuFunDef)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (NuFunDef, LocalContext) = { log(s"liftMemberFunc $func under $ctx # $cache # $globFuncs # $outer") val NuFunDef(rec, nm, sn, tpVs, body) = func body match{ case Left(Lam(lhs@Tup(etts), rhs)) => val lctx = getFreeVars(lhs)(using emptyCtx, cache, globFuncs, None) val lret = liftTuple(lhs)(using ctx.addV(lctx.vSet)) val ret = liftTerm(rhs)(using ctx.addV(lctx.vSet).addT(tpVs)) (func.copy(rhs = Left(Lam(lret._1, ret._1)))(func.declareLoc, func.virtualLoc, func.mutLoc, func.signature, func.outer, func.genField, func.annotations), ret._2 -+ lret._2) case Left(value) => val ret = liftTerm(value)(using ctx.addT(tpVs)) (func.copy(rhs = Left(ret._1))(func.declareLoc, func.virtualLoc, func.mutLoc, func.signature, func.outer, func.genField, func.annotations), ret._2) case Right(PolyType(targs, body)) => val nBody = liftType(body)(using ctx.addT(tpVs)) val nTargs = targs.map { case L(tp) => liftTypeName(tp)(using ctx.addT(tpVs)).mapFirst(Left.apply) case R(tv) => R(tv) -> emptyCtx }.unzip (func.copy(rhs = Right(PolyType(nTargs._1, nBody._1)))(func.declareLoc, func.virtualLoc, func.mutLoc, func.signature, func.outer, func.genField, func.annotations), nTargs._2.fold(nBody._2)(_ ++ _)) case _ => throw CompilerError(s"Unimplemented liftMemberFunc: ${func}") // TODO } } private def liftGlobalFunc(func: NuFunDef)(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): Unit = { log(s"liftGlobalFunc $func under $ctx # $cache # $globFuncs # $outer") val NuFunDef(rec, nm, _, tpVs, body) = func val nTpVs = tpVs ++ globFuncs.get(nm).get._2.tSet.toList globalFunctions.addOne(body match{ case Left(Lam(lhs@Tup(etts), rhs)) => val tmp = globFuncs.get(nm).get._2.vSet.toList.map(toFldsEle) val lctx = getFreeVars(lhs)(using emptyCtx, cache, globFuncs, None) val lret = liftTuple(lhs)(using ctx.addV(lctx.vSet) ++ globFuncs.get(nm).get._2, cache, globFuncs) val ret = liftTerm(rhs)(using ctx.addV(lctx.vSet) ++ globFuncs.get(nm).get._2, cache, globFuncs) NuFunDef(rec, globFuncs.get(nm).get._1, N, nTpVs, Left(Lam(Tup(lret._1.fields ++ tmp), ret._1)))(N, N, N, N, N, true, Nil) case Left(rhs) => val tmp = globFuncs.get(nm).get._2.vSet.toList.map(toFldsEle) val ret = liftTerm(rhs)(using ctx ++ globFuncs.get(nm).get._2, cache, globFuncs) NuFunDef(rec, globFuncs.get(nm).get._1, N, nTpVs, Left(ret._1))(N, N, N, N, N, true, Nil) case Right(PolyType(targs, body)) => val nBody = liftType(body)(using ctx ++ globFuncs.get(nm).get._2, cache, globFuncs, None) val nTargs = targs.map({ case L(tn) => liftTypeName(tn)(using ctx.addT(nTpVs), cache, globFuncs, None) match case (tn, ctx) => (L(tn), ctx) case R(tv) => R(tv) -> emptyCtx}).unzip NuFunDef(rec, globFuncs.get(nm).get._1, N, nTpVs, Right(PolyType(nTargs._1, nBody._1)))(N, N, N, N, N, true, Nil) case _ => throw CompilerError(s"Unimplemented liftGlobalFunc: ${func}") }) } private def grepFieldsInTrm(trm: Term): Option[Var] = trm match{ case Let(isRec, name, rhs, body) => Some(name) case _ => None } private def mixClsInfos(clsInfos: Map[String, ClassInfoCache], funcInfos: Map[String, LocalContext])(using cache: ClassCache): (Map[String, ClassInfoCache], Map[String, LocalContext]) = { val nameInfoMap: MutMap[String, ClassInfoCache] = MutMap(clsInfos.toSeq*) val nameFuncMap: MutMap[String, LocalContext] = MutMap(funcInfos.toSeq*) log(s"mix cls infos $nameInfoMap, $nameFuncMap") val clsNmsAsTypeNm = clsInfos.keySet.map(x => TypeName(x)) val len = clsInfos.size + nameFuncMap.size for(_ <- 0 to len){ nameInfoMap.toList.foreach{case (nmOfCls, infoOfCls@ClassInfoCache(_, _, ctx, flds, inners, sups, _, _, _)) => { val usedClsNmList = ctx.vSet.map(_.name).intersect(clsInfos.keySet) val newCtxForCls_tmp = usedClsNmList.foldLeft(ctx)((c1, c2) => c1 ++ nameInfoMap.get(c2).get.capturedParams) val usedFuncNmList = ctx.vSet.map(_.name).intersect(funcInfos.keySet) val newCtxForCls = usedFuncNmList.foldLeft(newCtxForCls_tmp)((c, x) => c ++ nameFuncMap.get(x).get) val supClsNmList = infoOfCls.supClses supClsNmList.foreach(c2 => flds.addAll( nameInfoMap.get(c2.name).map(_.fields).getOrElse(cache.get(c2).map(_.fields).getOrElse(Nil)) )) supClsNmList.foreach(c2 => inners.addAll( nameInfoMap.get(c2.name).map(_.innerClses).getOrElse(cache.get(c2).map(_.innerClses).getOrElse(Nil)) )) val newCtxFromSup = supClsNmList.map(c2 => nameInfoMap.get(c2.name).map(_.capturedParams).getOrElse(cache.get(c2).map(_.capturedParams).getOrElse(emptyCtx)) ).fold(emptyCtx)(_ ++ _) infoOfCls.capturedParams = newCtxForCls ++ newCtxFromSup }} nameFuncMap.toList.foreach((nm, ctx) => { val usedClsNmList = ctx.vSet.map(_.name).intersect(clsInfos.keySet) val usedFuncNmList = ctx.vSet.map(_.name).intersect(funcInfos.keySet) val nCtx = (usedClsNmList.map(x => nameInfoMap.get(x).get.capturedParams) ++ usedFuncNmList.map(x => nameFuncMap.get(x).get)).foldLeft(ctx)(_ ++ _) nameFuncMap.update(nm, nCtx) }) } nameInfoMap.foreach((x1, x2) => x2.capturedParams = (x2.capturedParams.extV(clsInfos.keySet.map(Var(_)))).extV(funcInfos.keySet.map(Var(_))).extT(x2.innerClses.keySet)) nameFuncMap.toList.foreach((x, c) => nameFuncMap.update(x, c.extV(clsInfos.keySet.map(Var(_))).extV(funcInfos.keySet.map(Var(_))))) log(s"mix result: $nameInfoMap, $nameFuncMap") nameInfoMap.toMap -> nameFuncMap.toMap } private def liftEntities(etts: List[Statement])(using ctx: LocalContext, cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): (List[Statement], LocalContext) = { log("liftEntities: " ++ etts.headOption.map(_.toString()).getOrElse("")) val (newCls, newFuncs, rstTrms) = splitEntities(etts) val newClsNms = newCls.map(x => Var(x.nme.name)).toSet val newFuncNms = newFuncs.map(_.nme) val nmsInTrm = rstTrms.flatMap(grepFieldsInTrm) val clsInfos = newCls.map(x => { val infos = collectClassInfo(x, newCls.map(_.nme).toSet)(using emptyCtx) infos.capturedParams = infos.capturedParams.intersect(ctx.addT(infos.capturedParams.tSet).addV(newClsNms ++ newFuncNms ++ nmsInTrm -- globFuncs.keySet ++ outer.map(_ => Var("this")))) x.nme.name -> infos}).toMap val funcInfos = newFuncs.map(x => x.nme.name -> (x.rhs match { case Left(trm) => getFreeVars(trm)(using emptyCtx) .intersect(ctx.addV(newClsNms ++ newFuncNms ++ nmsInTrm -- globFuncs.keySet ++ outer.map(_ => Var("this")))) .extT(x.tparams) case _ => emptyCtx}) ).toMap log("captured cls infos: \n" ++ clsInfos.toString()) log("captured func infos: \n" ++ funcInfos.toString()) val (refinedClsInfo, refinedFuncInfo) = mixClsInfos(clsInfos, funcInfos) val newCache = cache ++ refinedClsInfo.map(x => (TypeName(x._1) -> x._2)) refinedClsInfo.foreach((_, clsi) => completeClsInfo(clsi)(using newCache)) val newGlobalFuncs = refinedFuncInfo.map((nm, vs) => (Var(nm) -> (Var(genAnoName(nm)), vs))) newCls.foreach(x => liftTypeDef(x)(using newCache, globFuncs ++ newGlobalFuncs)) (newFuncs zip refinedFuncInfo).foreach((f, c) => liftGlobalFunc(f)(using ctx, newCache, globFuncs ++ newGlobalFuncs)) val (liftedTerms, termVs) = rstTrms.map(liftTerm(_)(using ctx.addV(newFuncNms), newCache, globFuncs ++ newGlobalFuncs)).unzip (liftedTerms, (termVs).fold(emptyCtx)(_ ++ _)) } private def completeClsInfo(clsInfo: ClassInfoCache)(using cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)]): Unit = { val ClassInfoCache(_, nName, freeVs, flds, inners, _, _, cls, _) = clsInfo val (clsList, _, _) = splitEntities(cls.body.entities) val innerClsNmSet = clsList.map(_.nme).toSet val innerClsInfos = clsList.map(x => x.nme.name -> collectClassInfo(x, innerClsNmSet)(using asContextV(freeVs.vSet ++ flds), cache, globFuncs, Some(clsInfo))).toMap val refinedInfos = mixClsInfos(innerClsInfos, Map())._1.map(x => (TypeName(x._1) -> x._2)) refinedInfos.foreach((_, info) => completeClsInfo(info)(using cache ++ refinedInfos)) inners.addAll(refinedInfos) } private def liftTypeDef(target: NuTypeDef)(using cache: ClassCache, globFuncs: Map[Var, (Var, LocalContext)], outer: Option[ClassInfoCache]): Unit = { def getAllInners(sups: Set[TypeName]): ClassCache = { sups.flatMap( t => cache.get(t).map(x => getAllInners(x.supClses) ++ x.innerClses) ).flatten.toMap } log("lift type " + target.toString() + " with cache " + cache.toString()) val NuTypeDef(kind, nme, tps, params, _, sig, pars, supAnn, thisAnn, body) = target val nOuter = cache.get(nme) val ClassInfoCache(_, nName, freeVs, flds, inners, sups, _, _, _) = nOuter.get val (clsList, funcList, termList) = splitEntities(body.entities) val innerNmsSet = clsList.map(_.nme).toSet val nCache = cache ++ inners ++ getAllInners(sups) val nTps = (tps.map(_._2) ++ (freeVs.tSet -- nCache.keySet).toList).distinct val nCtx = freeVs.addT(nTps) val nParams = outer.map(x => List(toFldsEle(Var(genParName(x.liftedNm.name))))).getOrElse(Nil) ++ params.fold(Nil)(t => t.fields.map{ case (Some(nm), Fld(flags, term)) => (Some(nm), Fld(flags, liftTerm(term)(using emptyCtx, nCache, globFuncs, nOuter)._1)) case other => other }) ++ freeVs.vSet.map(toFldsEle) val nPars = pars.map(liftTerm(_)(using emptyCtx, nCache, globFuncs, nOuter)).unzip val nFuncs = funcList.map(liftMemberFunc(_)(using emptyCtx, nCache, globFuncs, nOuter)).unzip val nTerms = termList.map(liftTerm(_)(using emptyCtx, nCache, globFuncs, nOuter)).unzip clsList.foreach(x => liftTypeDef(x)(using nCache, globFuncs, nOuter)) retSeq = retSeq.appended(NuTypeDef( kind, nName, nTps.map((None, _)), kind match case Mod => None case _ => S(Tup(nParams)) , None, None, nPars._1, None, None, TypingUnit(nFuncs._1 ++ nTerms._1))(None, None, Nil)) } def liftTypingUnit(rawUnit: TypingUnit): TypingUnit = { log("=========================\n") log(s"lifting: \n$rawUnit\n") retSeq = Nil globalFunctions.clear() val re = liftEntities(rawUnit.entities)(using emptyCtx, Map(), Map(), None) log(s"freeVars: ${re._2}") TypingUnit(retSeq.toList ++ globalFunctions.toList ++ re._1) } } ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/Document.scala ================================================ package mlscript.compiler.utils enum Document: case Indented(content: Document) case Unindented(content: Document) case Stacked(docs: List[Document], emptyLines: Boolean = false) case Lined(docs: List[Document], separator: Document) case Raw(s: String) def <:>(other: Document) = line(List(this, other)) def <#>(other: Document) = line(List(this, other), sep = "") override def toString: String = print def print: String = { val sb = StringBuffer() def rec(d: Document)(implicit ind: Int, first: Boolean): Unit = d match { case Raw(s) => if (first && s.nonEmpty) sb.append(" " * ind) sb.append(s) case Indented(doc) => rec(doc)(using ind + 1, first) case Unindented(doc) => assume(ind > 0) rec(doc)(using ind - 1, first) case Lined(Nil, _) => // skip case Lined(docs, sep) => rec(docs.head) docs.tail foreach { doc => rec(sep)(using ind, false) rec(doc)(using ind, false) } case Stacked(Nil, _) => // skip case Stacked(docs, emptyLines) => rec(docs.head) docs.tail foreach { doc => sb.append("\n") if (emptyLines) sb.append("\n") rec(doc)(using ind, true) } } rec(this)(using 0, true) sb.toString } def stack(docs: Document*) = Document.Stacked(docs.toList) def stack_list(docs: List[Document]) = Document.Stacked(docs) def line(docs: List[Document], sep: String = " ") = Document.Lined(docs, Document.Raw(sep)) def raw(s: String) = Document.Raw(s) def indent(doc: Document) = Document.Indented(doc) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/PrettyPrinter.scala ================================================ package mlscript.compiler import mlscript.{TypingUnit, NuFunDef, NuTypeDef, Term, Tup} import mlscript.compiler.DebugOutput // For pretty printing terms in debug output. object PrettyPrinter: def show(term: Term): DebugOutput = DebugOutput.Code(term.showDbg.linesIterator.toList) def show(unit: TypingUnit): DebugOutput = DebugOutput.Code(showTypingUnit(unit, 0).linesIterator.toList) def show(funDef: NuFunDef): DebugOutput = DebugOutput.Code(showFunDef(funDef).linesIterator.toList) def show(tyDef: NuTypeDef): DebugOutput = DebugOutput.Code(showTypeDef(tyDef, 0).linesIterator.toList) def showTypingUnit(unit: TypingUnit, indent: Int = 0): String = val head = if indent == 0 then "TypingUnit " else " " val singleLine = head + unit.entities.iterator.map { case term: Term => show(term) case tyDef: NuTypeDef => showTypeDef(tyDef) case funDef: NuFunDef => showFunDef(funDef) case others => others.showDbg }.mkString("{", "; ", "}") if (singleLine.length < 60) singleLine else val indentStr = " " * (indent * 2) head + unit.entities.iterator.map { case term: Term => show(term) case tyDef: NuTypeDef => showTypeDef(tyDef) case funDef: NuFunDef => showFunDef(funDef) case others => others.showDbg }.map(indentStr + " " + _).mkString("{\n", "\n", s"\n$indentStr}") def showFunDef(funDef: NuFunDef): String = val st = (funDef.isLetRec) match { case None => "fun" case Some(false) => "let" case Some(true) => "let'" } s"$st ${funDef.nme.name}" + (if funDef.tparams.isEmpty then "" else funDef.tparams.map(_.name).mkString("[", ", ", "]")) + " = " + funDef.rhs.fold(_.showDbg, _.show(newDefs = true)) def showTypeDef(tyDef: NuTypeDef, indent: Int = 0): String = s"${tyDef.kind.str} ${tyDef.nme.name}" + (if tyDef.tparams.isEmpty then "" else tyDef.tparams.map(_._2.name).mkString("[", ",", "]")) + tyDef.params.fold("")(params => s"(${params.showDbg})") + (if tyDef.parents.isEmpty then "" else ": " + tyDef.parents.map(_.showDbg).mkString(", ")) + showTypingUnit(tyDef.body, indent + 1) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/codegen/CppAst.scala ================================================ package mlscript.compiler.codegen.cpp import mlscript._ import mlscript.utils._ import mlscript.utils.shorthands._ import mlscript.compiler.utils._ import scala.language.implicitConversions given Conversion[String, Document] = raw enum Specifier: case Extern case Static case Inline def toDocument = raw: this match case Extern => "extern" case Static => "static" case Inline => "inline" override def toString: Str = toDocument.print object Type: def toDocuments(args: Ls[Type], sep: Document, extraTypename: Bool = false): Document = args.iterator.zipWithIndex.map { case (x, 0) => x.toDocument(extraTypename) case (x, _) => sep <#> x.toDocument(extraTypename) }.fold(raw(""))(_ <#> _) def toDocuments(args: Ls[(Str, Type)], sep: Document): Document = args.iterator.zipWithIndex.map { case (x, 0) => x._2.toDocument() <:> raw(x._1) case (x, _) => sep <#> x._2.toDocument() <:> raw(x._1) }.fold(raw(""))(_ <#> _) enum Type: case Prim(name: Str) case Ptr(inner: Type) case Ref(inner: Type) case Array(inner: Type, size: Opt[Int]) case FuncPtr(ret: Type, args: List[Type]) case Struct(name: Str) case Enum(name: Str) case Template(name: Str, args: List[Type]) case Var(name: Str) case Qualifier(inner: Type, qual: Str) def toDocument(extraTypename: Bool = false): Document = def aux(x: Type): Document = x match case Prim(name) => name case Ptr(inner) => aux(inner) <#> "*" case Ref(inner) => aux(inner) <#> "&" case Array(inner, size) => aux(inner) <#> "[" <#> size.fold(raw(""))(x => x.toString) <#> "]" case FuncPtr(ret, args) => aux(ret) <#> "(" <#> Type.toDocuments(args, sep = ", ") <#> ")" case Struct(name) => s"struct $name" case Enum(name) => s"enum $name" case Template(name, args) => s"$name" <#> "<" <#> Type.toDocuments(args, sep = ", ") <#> ">" case Var(name) => name case Qualifier(inner, qual) => aux(inner) <:> qual aux(this) override def toString: Str = toDocument().print object Stmt: def toDocuments(decl: Ls[Decl], stmts: Ls[Stmt]): Document = stack_list(decl.map(_.toDocument) ++ stmts.map(_.toDocument)) enum Stmt: case AutoBind(lhs: Ls[Str], rhs: Expr) case Assign(lhs: Str, rhs: Expr) case Return(expr: Expr) case If(cond: Expr, thenStmt: Stmt, elseStmt: Opt[Stmt]) case While(cond: Expr, body: Stmt) case For(init: Stmt, cond: Expr, update: Stmt, body: Stmt) case ExprStmt(expr: Expr) case Break case Continue case Block(decl: Ls[Decl], stmts: Ls[Stmt]) case Switch(expr: Expr, cases: Ls[(Expr, Stmt)]) case Raw(stmt: Str) def toDocument: Document = def aux(x: Stmt): Document = x match case AutoBind(lhs, rhs) => lhs match case Nil => rhs.toDocument case x :: Nil => "auto" <:> x <:> "=" <:> rhs.toDocument <#> ";" case _ => "auto" <:> lhs.mkString("[", ",", "]") <:> "=" <:> rhs.toDocument <#> ";" case Assign(lhs, rhs) => lhs <#> " = " <#> rhs.toDocument <#> ";" case Return(expr) => "return " <#> expr.toDocument <#> ";" case If(cond, thenStmt, elseStmt) => "if (" <#> cond.toDocument <#> ")" <#> thenStmt.toDocument <:> elseStmt.fold(raw(""))(x => "else" <:> x.toDocument) case While(cond, body) => "while (" <#> cond.toDocument <#> ")" <#> body.toDocument case For(init, cond, update, body) => "for (" <#> init.toDocument <#> "; " <#> cond.toDocument <#> "; " <#> update.toDocument <#> ")" <#> body.toDocument case ExprStmt(expr) => expr.toDocument <#> ";" case Break => "break;" case Continue => "continue;" case Block(decl, stmts) => stack( "{", Stmt.toDocuments(decl, stmts) |> indent, "}") case Switch(expr, cases) => "switch (" <#> expr.toDocument <#> ")" <#> "{" <#> stack_list(cases.map { case (cond, stmt) => "case " <#> cond.toDocument <#> ":" <#> stmt.toDocument }) <#> "}" case Raw(stmt) => stmt aux(this) object Expr: def toDocuments(args: Ls[Expr], sep: Document): Document = args.zipWithIndex.map { case (x, i) => if i == 0 then x.toDocument else sep <#> x.toDocument }.fold(raw(""))(_ <#> _) enum Expr: case Var(name: Str) case IntLit(value: BigInt) case DoubleLit(value: Double) case StrLit(value: Str) case CharLit(value: Char) case Call(func: Expr, args: Ls[Expr]) case Member(expr: Expr, member: Str) case Index(expr: Expr, index: Expr) case Unary(op: Str, expr: Expr) case Binary(op: Str, lhs: Expr, rhs: Expr) case Initializer(exprs: Ls[Expr]) case Constructor(name: Str, init: Expr) def toDocument: Document = def aux(x: Expr): Document = x match case Var(name) => name case IntLit(value) => value.toString case DoubleLit(value) => value.toString case StrLit(value) => s"\"$value\"" // need more reliable escape utils case CharLit(value) => value.toInt.toString case Call(func, args) => aux(func) <#> "(" <#> Expr.toDocuments(args, sep = ", ") <#> ")" case Member(expr, member) => aux(expr) <#> "->" <#> member case Index(expr, index) => aux(expr) <#> "[" <#> aux(index) <#> "]" case Unary(op, expr) => "(" <#> op <#> aux(expr) <#> ")" case Binary(op, lhs, rhs) => "(" <#> aux(lhs) <#> op <#> aux(rhs) <#> ")" case Initializer(exprs) => "{" <#> Expr.toDocuments(exprs, sep = ", ") <#> "}" case Constructor(name, init) => name <#> init.toDocument aux(this) case class CompilationUnit(includes: Ls[Str], decls: Ls[Decl], defs: Ls[Def]): def toDocument: Document = stack_list(includes.map(x => raw(x)) ++ decls.map(_.toDocument) ++ defs.map(_.toDocument)) def toDocumentWithoutHidden: Document = val hiddenNames = Set( "HiddenTheseEntities", "True", "False", "Callable", "List", "Cons", "Nil", "Option", "Some", "None", "Pair", "Tuple2", "Tuple3", "Nat", "S", "O" ) stack_list(defs.filterNot { case d: Def.StructDef => hiddenNames.contains(d.name.stripPrefix("_mls_")) case _ => false }.map(_.toDocument)) enum Decl: case StructDecl(name: Str) case EnumDecl(name: Str) case FuncDecl(ret: Type, name: Str, args: Ls[Type]) case VarDecl(name: Str, typ: Type) def toDocument: Document = def aux(x: Decl): Document = x match case StructDecl(name) => s"struct $name;" case EnumDecl(name) => s"enum $name;" case FuncDecl(ret, name, args) => ret.toDocument() <#> s" $name(" <#> Type.toDocuments(args, sep = ", ") <#> ");" case VarDecl(name, typ) => typ.toDocument() <#> s" $name;" aux(this) enum Def: case StructDef(name: Str, fields: Ls[(Str, Type)], inherit: Opt[Ls[Str]], methods: Ls[Def] = Ls.empty) case EnumDef(name: Str, fields: Ls[(Str, Opt[Int])]) case FuncDef(specret: Type, name: Str, args: Ls[(Str, Type)], body: Stmt.Block, or: Bool = false, virt: Bool = false) case VarDef(typ: Type, name: Str, init: Opt[Expr]) case RawDef(raw: Str) def toDocument: Document = def aux(x: Def): Document = x match case StructDef(name, fields, inherit, defs) => stack( s"struct $name" <#> (if inherit.nonEmpty then ": public" <:> inherit.get.mkString(", ") else "" ) <:> "{", stack_list(fields.map { case (name, typ) => typ.toDocument() <#> " " <#> name <#> ";" }) |> indent, stack_list(defs.map(_.toDocument)) |> indent, "};" ) case EnumDef(name, fields) => s"enum $name" <:> "{" <#> stack_list(fields.map { case (name, value) => value.fold(s"$name")(x => s"$name = $x") }) <#> "};" case FuncDef(specret, name, args, body, or, virt) => (if virt then "virtual " else "") <#> specret.toDocument() <#> s" $name(" <#> Type.toDocuments(args, sep = ", ") <#> ")" <#> (if or then " override" else "") <#> body.toDocument case VarDef(typ, name, init) => typ.toDocument() <#> s" $name" <#> init.fold(raw(""))(x => " = " <#> x.toDocument) <#> raw(";") case RawDef(x) => x aux(this) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/codegen/CppCodeGen.scala ================================================ package mlscript.compiler.codegen.cpp import mlscript.compiler.ir.{Expr => IExpr, _} import mlscript.compiler.utils._ import mlscript.utils._ import mlscript.utils.shorthands._ import scala.collection.mutable.ListBuffer def codegen(prog: Program): CompilationUnit = val codegen = CppCodeGen() codegen.codegen(prog) private class CppCodeGen: def mapName(name: Name): Str = "_mls_" + name.str.replace('$', '_').replace('\'', '_') def mapName(name: Str): Str = "_mls_" + name.replace('$', '_').replace('\'', '_') val freshName = Fresh(div = '_'); val mlsValType = Type.Prim("_mlsValue") val mlsUnitValue = Expr.Call(Expr.Var("_mlsValue::create<_mls_Unit>"), Ls()); val mlsRetValue = "_mls_retval" val mlsRetValueDecl = Decl.VarDecl(mlsRetValue, mlsValType) val mlsMainName = "_mlsMain" val mlsPrelude = "#include \"mlsprelude.h\"" val mlsPreludeImpl = "#include \"mlsprelude.cpp\"" val mlsInternalClass = Set("True", "False", "Boolean", "Callable") val mlsObject = "_mlsObject" val mlsBuiltin = "builtin" val mlsEntryPoint = s"int main() { return _mlsLargeStack(_mlsMainWrapper); }"; def mlsIntLit(x: BigInt) = Expr.Call(Expr.Var("_mlsValue::fromIntLit"), Ls(Expr.IntLit(x))) def mlsStrLit(x: Str) = Expr.Call(Expr.Var("_mlsValue::fromStrLit"), Ls(Expr.StrLit(x))) def mlsCharLit(x: Char) = Expr.Call(Expr.Var("_mlsValue::fromIntLit"), Ls(Expr.CharLit(x))) def mlsNewValue(cls: Str, args: Ls[Expr]) = Expr.Call(Expr.Var(s"_mlsValue::create<$cls>"), args) def mlsIsValueOf(cls: Str, scrut: Expr) = Expr.Call(Expr.Var(s"_mlsValue::isValueOf<$cls>"), Ls(scrut)) def mlsIsIntLit(scrut: Expr, lit: mlscript.IntLit) = Expr.Call(Expr.Var("_mlsValue::isIntLit"), Ls(scrut, Expr.IntLit(lit.value))) def mlsDebugPrint(x: Expr) = Expr.Call(Expr.Var("_mlsValue::print"), Ls(x)) def mlsTupleValue(init: Expr) = Expr.Constructor("_mlsValue::tuple", init) def mlsAs(name: Str, cls: Str) = Expr.Var(s"_mlsValue::as<$cls>($name)") def mlsAsUnchecked(name: Str, cls: Str) = Expr.Var(s"_mlsValue::cast<$cls>($name)") def mlsObjectNameMethod(name: Str) = s"constexpr static inline const char *typeName = \"${name}\";" def mlsTypeTag() = s"constexpr static inline uint32_t typeTag = nextTypeTag();" def mlsTypeTag(n: Int) = s"constexpr static inline uint32_t typeTag = $n;" def mlsCommonCreateMethod(cls: Str, fields: Ls[Str], id: Int) = val parameters = fields.map{x => s"_mlsValue $x"}.mkString(", ") val fieldsAssignment = fields.map{x => s"_mlsVal->$x = $x; "}.mkString s"static _mlsValue create($parameters) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) $cls; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; $fieldsAssignment return _mlsValue(_mlsVal); }" def mlsCommonPrintMethod(fields: Ls[Str]) = if fields.isEmpty then s"virtual void print() const override { std::printf(\"%s\", typeName); }" else val fieldsPrint = fields.map{x => s"this->$x.print(); "}.mkString("std::printf(\", \"); ") s"virtual void print() const override { std::printf(\"%s\", typeName); std::printf(\"(\"); $fieldsPrint std::printf(\")\"); }" def mlsCommonDestructorMethod(cls: Str, fields: Ls[Str]) = val fieldsDeletion = fields.map{x => s"_mlsValue::destroy(this->$x); "}.mkString s"virtual void destroy() override { $fieldsDeletion operator delete (this, std::align_val_t(_mlsAlignment)); }" def mlsThrowNonExhaustiveMatch = Stmt.Raw("_mlsNonExhaustiveMatch();"); def mlsCall(fn: Str, args: Ls[Expr]) = Expr.Call(Expr.Var("_mlsCall"), Expr.Var(fn) :: args) def mlsMethodCall(cls: ClassRef, method: Str, args: Ls[Expr]) = Expr.Call(Expr.Member(Expr.Call(Expr.Var(s"_mlsMethodCall<${cls.name |> mapName}>"), Ls(args.head)), method), args.tail) def mlsFnWrapperName(fn: Str) = s"_mlsFn_$fn" def mlsFnCreateMethod(fn: Str) = s"static _mlsValue create() { static _mlsFn_$fn mlsFn alignas(_mlsAlignment); mlsFn.refCount = stickyRefCount; mlsFn.tag = typeTag; return _mlsValue(&mlsFn); }" def mlsNeverValue(n: Int) = if (n <= 1) then Expr.Call(Expr.Var(s"_mlsValue::never"), Ls()) else Expr.Call(Expr.Var(s"_mlsValue::never<$n>"), Ls()) case class Ctx( defnCtx: Set[Str], ) def codegenClassInfo(using ctx: Ctx)(cls: ClassInfo): (Opt[Def], Decl) = val fields = cls.fields.map{x => (x |> mapName, mlsValType)} val parents = if cls.parents.nonEmpty then cls.parents.toList.map(mapName) else mlsObject :: Nil val decl = Decl.StructDecl(cls.name |> mapName) if mlsInternalClass.contains(cls.name) then return (None, decl) val theDef = Def.StructDef( cls.name |> mapName, fields, if parents.nonEmpty then Some(parents) else None, Ls(Def.RawDef(mlsObjectNameMethod(cls.name)), Def.RawDef(mlsTypeTag()), Def.RawDef(mlsCommonPrintMethod(cls.fields.map(mapName))), Def.RawDef(mlsCommonDestructorMethod(cls.name |> mapName, cls.fields.map(mapName))), Def.RawDef(mlsCommonCreateMethod(cls.name |> mapName, cls.fields.map(mapName), cls.id))) ++ cls.methods.map{case (name, defn) => { val (theDef, decl) = codegenDefn(using Ctx(ctx.defnCtx + cls.name))(defn) theDef match case x @ Def.FuncDef(_, name, _, _, _, _) => x.copy(virt = true) case _ => theDef }} ) (S(theDef), decl) def toExpr(texpr: TrivialExpr, reifyUnit: Bool = false)(using ctx: Ctx): Opt[Expr] = texpr match case IExpr.Ref(name) => S(Expr.Var(name |> mapName)) case IExpr.Literal(mlscript.IntLit(x)) => S(mlsIntLit(x)) case IExpr.Literal(mlscript.DecLit(x)) => S(mlsIntLit(x.toBigInt)) case IExpr.Literal(mlscript.StrLit(x)) => S(mlsStrLit(x)) case IExpr.Literal(mlscript.UnitLit(_)) => if reifyUnit then S(mlsUnitValue) else None def toExpr(texpr: TrivialExpr)(using ctx: Ctx): Expr = texpr match case IExpr.Ref(name) => Expr.Var(name |> mapName) case IExpr.Literal(mlscript.IntLit(x)) => mlsIntLit(x) case IExpr.Literal(mlscript.DecLit(x)) => mlsIntLit(x.toBigInt) case IExpr.Literal(mlscript.StrLit(x)) => mlsStrLit(x) case IExpr.Literal(mlscript.UnitLit(_)) => mlsUnitValue def wrapMultiValues(exprs: Ls[TrivialExpr])(using ctx: Ctx): Expr = exprs match case x :: Nil => toExpr(x, reifyUnit = true).get case _ => val init = Expr.Initializer(exprs.map{x => toExpr(x)}) mlsTupleValue(init) def codegenCaseWithIfs(scrut: Name, cases: Ls[(Pat, Node)], default: Opt[Node], storeInto: Str)(using decls: Ls[Decl], stmts: Ls[Stmt])(using ctx: Ctx): (Ls[Decl], Ls[Stmt]) = val scrutName = mapName(scrut) val init: Stmt = default.fold(mlsThrowNonExhaustiveMatch)(x => { val (decls2, stmts2) = codegen(x, storeInto)(using Ls.empty, Ls.empty[Stmt]) Stmt.Block(decls2, stmts2) }) val stmt = cases.foldRight(S(init)) { case ((Pat.Class(cls), arm), nextarm) => val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) val stmt = Stmt.If(mlsIsValueOf(cls.name |> mapName, Expr.Var(scrutName)), Stmt.Block(decls2, stmts2), nextarm) S(stmt) case ((Pat.Lit(i @ mlscript.IntLit(_)), arm), nextarm) => val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) val stmt = Stmt.If(mlsIsIntLit(Expr.Var(scrutName), i), Stmt.Block(decls2, stmts2), nextarm) S(stmt) case _ => ??? } (decls, stmt.fold(stmts)(x => stmts :+ x)) def codegenJumpWithCall(defn: DefnRef, args: Ls[TrivialExpr], storeInto: Opt[Str])(using decls: Ls[Decl], stmts: Ls[Stmt])(using ctx: Ctx): (Ls[Decl], Ls[Stmt]) = val call = Expr.Call(Expr.Var(defn.name |> mapName), args.map(toExpr)) val stmts2 = stmts ++ Ls(storeInto.fold(Stmt.Return(call))(x => Stmt.Assign(x, call))) (decls, stmts2) def codegenOps(op: Str, args: Ls[TrivialExpr])(using ctx: Ctx) = op match case "+" => Expr.Binary("+", toExpr(args(0)), toExpr(args(1))) case "-" => Expr.Binary("-", toExpr(args(0)), toExpr(args(1))) case "*" => Expr.Binary("*", toExpr(args(0)), toExpr(args(1))) case "/" => Expr.Binary("/", toExpr(args(0)), toExpr(args(1))) case "%" => Expr.Binary("%", toExpr(args(0)), toExpr(args(1))) case "==" => Expr.Binary("==", toExpr(args(0)), toExpr(args(1))) case "!=" => Expr.Binary("!=", toExpr(args(0)), toExpr(args(1))) case "<" => Expr.Binary("<", toExpr(args(0)), toExpr(args(1))) case "<=" => Expr.Binary("<=", toExpr(args(0)), toExpr(args(1))) case ">" => Expr.Binary(">", toExpr(args(0)), toExpr(args(1))) case ">=" => Expr.Binary(">=", toExpr(args(0)), toExpr(args(1))) case "&&" => Expr.Binary("&&", toExpr(args(0)), toExpr(args(1))) case "||" => Expr.Binary("||", toExpr(args(0)), toExpr(args(1))) case "!" => Expr.Unary("!", toExpr(args(0))) case _ => mlscript.utils.TODO("codegenOps") def codegen(expr: IExpr)(using ctx: Ctx): Expr = expr match case x @ (IExpr.Ref(_) | IExpr.Literal(_)) => toExpr(x, reifyUnit = true).get case IExpr.CtorApp(cls, args) => mlsNewValue(cls.name |> mapName, args.map(toExpr)) case IExpr.Select(name, cls, field) => Expr.Member(mlsAsUnchecked(name |> mapName, cls.name |> mapName), field |> mapName) case IExpr.BasicOp(name, args) => codegenOps(name, args) case IExpr.AssignField(assignee, cls, field, value) => mlscript.utils.TODO("Assign field in the backend") def codegenBuiltin(names: Ls[Name], builtin: Str, args: Ls[TrivialExpr])(using ctx: Ctx): Ls[Stmt] = builtin match case "error" => Ls(Stmt.Raw("throw std::runtime_error(\"Error\");"), Stmt.AutoBind(names.map(mapName), mlsNeverValue(names.size))) case _ => Ls(Stmt.AutoBind(names.map(mapName), Expr.Call(Expr.Var("_mls_builtin_" + builtin), args.map(toExpr)))) def codegen(body: Node, storeInto: Str)(using decls: Ls[Decl], stmts: Ls[Stmt])(using ctx: Ctx): (Ls[Decl], Ls[Stmt]) = body match case Node.Result(res) => val expr = wrapMultiValues(res) val stmts2 = stmts ++ Ls(Stmt.Assign(storeInto, expr)) (decls, stmts2) case Node.Jump(defn, args) => codegenJumpWithCall(defn, args, S(storeInto)) case Node.LetExpr(name, expr, body) => val stmts2 = stmts ++ Ls(Stmt.AutoBind(Ls(name |> mapName), codegen(expr))) codegen(body, storeInto)(using decls, stmts2) case Node.LetMethodCall(names, cls, method, IExpr.Ref(Name("builtin")) :: args, body) => val stmts2 = stmts ++ codegenBuiltin(names, args.head.toString.replace("\"", ""), args.tail) codegen(body, storeInto)(using decls, stmts2) case Node.LetMethodCall(names, cls, method, args, body) => val call = mlsMethodCall(cls, method.str |> mapName, args.map(toExpr)) val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(mapName), call)) codegen(body, storeInto)(using decls, stmts2) case Node.LetCall(names, defn, args, _, body) => val call = Expr.Call(Expr.Var(defn.name |> mapName), args.map(toExpr)) val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(mapName), call)) codegen(body, storeInto)(using decls, stmts2) case Node.Case(scrut, cases, default) => codegenCaseWithIfs(scrut, cases, default, storeInto) private def codegenDefn(using ctx: Ctx)(defn: Defn): (Def, Decl) = defn match case Defn(id, name, params, resultNum, body, _, _) => val decls = Ls(mlsRetValueDecl) val stmts = Ls.empty[Stmt] val (decls2, stmts2) = codegen(body, mlsRetValue)(using decls, stmts) val stmtsWithReturn = stmts2 :+ Stmt.Return(Expr.Var(mlsRetValue)) val theDef = Def.FuncDef(mlsValType, name |> mapName, params.map(x => (x |> mapName, mlsValType)), Stmt.Block(decls2, stmtsWithReturn)) val decl = Decl.FuncDecl(mlsValType, name |> mapName, params.map(x => mlsValType)) (theDef, decl) def codegenTopNode(node: Node)(using ctx: Ctx): (Def, Decl) = val decls = Ls(mlsRetValueDecl) val stmts = Ls.empty[Stmt] val (decls2, stmts2) = codegen(node, mlsRetValue)(using decls, stmts) val stmtsWithReturn = stmts2 :+ Stmt.Return(Expr.Var(mlsRetValue)) val theDef = Def.FuncDef(mlsValType, mlsMainName, Ls(), Stmt.Block(decls2, stmtsWithReturn)) val decl = Decl.FuncDecl(mlsValType, mlsMainName, Ls()) (theDef, decl) // Topological sort of classes based on inheritance relationships def sortClasses(prog: Program): Ls[ClassInfo] = var depgraph = prog.classes.map(x => (x.name, x.parents)).toMap var degree = depgraph.view.mapValues(_.size).toMap def removeNode(node: Str) = degree -= node depgraph -= node depgraph = depgraph.view.mapValues(_.filter(_ != node)).toMap degree = depgraph.view.mapValues(_.size).toMap val sorted = ListBuffer.empty[ClassInfo] var work = degree.filter(_._2 == 0).keys.toSet while work.nonEmpty do val node = work.head work -= node sorted.addOne(prog.classes.find(_.name == node).get) removeNode(node) val next = degree.filter(_._2 == 0).keys work ++= next if depgraph.nonEmpty then val cycle = depgraph.keys.mkString(", ") throw new Exception(s"Cycle detected in class hierarchy: $cycle") sorted.toList def codegen(prog: Program): CompilationUnit = val sortedClasses = sortClasses(prog) val defnCtx = prog.defs.map(_.name) val (defs, decls) = sortedClasses.map(codegenClassInfo(using Ctx(defnCtx))).unzip val (defs2, decls2) = prog.defs.map(codegenDefn(using Ctx(defnCtx))).unzip val (defMain, declMain) = codegenTopNode(prog.main)(using Ctx(defnCtx)) CompilationUnit(Ls(mlsPrelude), decls ++ decls2 :+ declMain, defs.flatten ++ defs2 :+ defMain :+ Def.RawDef(mlsEntryPoint)) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/codegen/CppCompilerHost.scala ================================================ package mlscript.compiler.codegen.cpp import mlscript._ import mlscript.utils.shorthands._ import scala.collection.mutable.ListBuffer final class CppCompilerHost(val auxPath: Str): import scala.sys.process._ private def ifAnyCppCompilerExists(): Boolean = Seq("g++", "--version").! == 0 || Seq("clang++", "--version").! == 0 private def isMakeExists(): Boolean = import scala.sys.process._ Seq("make", "--version").! == 0 val ready = ifAnyCppCompilerExists() && isMakeExists() def compileAndRun(src: Str, output: Str => Unit): Unit = if !ready then return val srcPath = os.temp(contents = src, suffix = ".cpp") val binPath = os.temp(suffix = ".mls.out") var stdout = ListBuffer[Str]() var stderr = ListBuffer[Str]() val buildLogger = ProcessLogger(stdout :+= _, stderr :+= _) val buildResult = Seq("make", "-B", "-C", auxPath, "auto", s"SRC=$srcPath", s"DST=$binPath") ! buildLogger if buildResult != 0 then output("Compilation failed: ") for line <- stdout do output(line) for line <- stderr do output(line) return stdout.clear() stderr.clear() val runCmd = Seq(binPath.toString) val runResult = runCmd ! buildLogger if runResult != 0 then output("Execution failed: ") for line <- stdout do output(line) for line <- stderr do output(line) return output("Execution succeeded: ") for line <- stdout do output(line) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/debug/Debug.scala ================================================ package mlscript.compiler abstract class Debug: def trace[T](name: String, pre: Any*) (thunk: => T) (post: T => Any = Debug.noPostTrace): T def log(msg: => String): Unit def writeLine(line: String): Unit def indent(): Unit def outdent(): Unit object Debug: val noPostTrace: Any => Any = _ => "" ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/debug/DebutOutput.scala ================================================ package mlscript.compiler import scala.collection.mutable.ArrayBuffer import RainbowDebug.black enum DebugOutput extends Printable: case Code(lines: List[String]) case Map(entries: List[(String, String)]) case Plain(content: String) def getDebugOutput: DebugOutput = this import DebugOutput._ def toLines(using color: Boolean): List[String] = this match case Code(lines) => if lines.length == 1 then lines else box(lines) case Map(entries) => if entries.length <= 3 then entries.iterator.map { (key, value) => s"$key: $value" }.mkString("{ ", ", ", " }") :: Nil else boxMap(entries) case Plain(content) => content.linesIterator.toList object DebugOutput: def box(lines: List[String])(using color: Boolean = true): List[String] = val maxWidth = lines.iterator.map(_.length).max val gutterWidth = if lines.length == 1 then 1 else scala.math.log10(lines.length).ceil.toInt val newLines = ArrayBuffer[String]() newLines += "┌" + "─" * (gutterWidth + 2) + "┬" + "─" * (maxWidth + 2) + "┐" lines.iterator.zipWithIndex.foreach { (line, index) => newLines += ("│ " + (index + 1).toString + " │ " + (if color then black else identity)(line.padTo(maxWidth, ' ')) + " │") } newLines += "└" + "─" * (gutterWidth + 2) + "┴" + "─" * (maxWidth + 2) + "┘" newLines.toList private val KEY_TEXT = "(key)" private val VALUE_TEXT = "(value)" def boxMap(entries: List[(String, String)])(using color: Boolean = true): List[String] = val keyMaxWidth = entries.iterator.map(_._1.length).max.max(KEY_TEXT.length) val valueMaxWidth = entries.iterator.map(_._2.length).max.max(VALUE_TEXT.length) val newLines = ArrayBuffer[String]() newLines += "┌" + "─" * (keyMaxWidth + 2) + "┬" + "─" * (valueMaxWidth + 2) + "┐" newLines += ("│ " + KEY_TEXT.padTo(keyMaxWidth, ' ') + " │ " + VALUE_TEXT.padTo(valueMaxWidth, ' ') + " │") newLines += "├" + "─" * (keyMaxWidth + 2) + "┼" + "─" * (valueMaxWidth + 2) + "┤" entries.foreach { (key, value) => val reprKey = key.padTo(keyMaxWidth, ' ') val reprValue = (if color then black else identity)(value.padTo(valueMaxWidth, ' ')) newLines += ("│ " + reprKey + " │ " + reprValue + " │") } newLines += "└" + "─" * (keyMaxWidth + 2) + "┴" + "─" * (valueMaxWidth + 2) + "┘" newLines.toList ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/debug/DummyDebug.scala ================================================ package mlscript.compiler object DummyDebug extends Debug: def trace[T](name: String, pre: Any*) (thunk: => T) (post: T => Any = Debug.noPostTrace): T = thunk inline def log(msg: => String): Unit = () def writeLine(line: String): Unit = () def indent(): Unit = () def outdent(): Unit = () ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/debug/Printable.scala ================================================ package mlscript.compiler trait Printable: def getDebugOutput: DebugOutput ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/debug/RainbowDebug.scala ================================================ package mlscript.compiler import scala.collection.mutable.ArrayBuffer import scala.collection.immutable.StringOps class RainbowDebug(color: Boolean = true) extends Debug: import RainbowDebug._ private inline def currentColor = if color then colors(privIndent % colors.size) else identity[String] private inline def beginMark = currentColor("┌") private inline def endMark = currentColor("└") private var privIndent = 0 def indent(): Unit = () def outdent(): Unit = () def trace[T](name: String, pre: Any*) (thunk: => T) (post: T => Any): T = { printPrologue(name, pre.map(toLines(_)(using color))) privIndent += 1 val res = try thunk finally privIndent -= 1 if (post eq Debug.noPostTrace) { log(s"$endMark $name") } else { val result = post(res) printEpilogue(name, toLines(result)(using color)) } res } private def printPrologue(name: String, things: Iterable[List[String]]): Unit = val leadingLength = name.length + 1 val leadingSpaces = " " * leadingLength things.headOption.foreach { case Nil => () case head :: Nil => log(s"$beginMark ${name} ${if color then black(head) else head}") case list => log(s"$beginMark ${name}") privIndent += 1 list.foreach { line => log(line) } privIndent -= 1 } privIndent += 1 things.tail.foreach { _.foreach { log(_) } } privIndent -= 1 private def printEpilogue(name: String, lines: List[String]): Unit = val leadingLength = name.length + 3 val leadingSpaces = " " * leadingLength lines match { case Nil => () case head :: Nil => log(s"$endMark $name ${if color then black(head) else head}") case items => log(s"$endMark $name") items.foreach { line => log(s" $line") } } inline def log(msg: => String): Unit = import scala.collection.mutable.StringBuilder val indentBuilder = StringBuilder() for i <- 0 until privIndent do indentBuilder ++= (if color then colors(i % colors.size) else identity[String])("│ ") writeLine("[mono] " + indentBuilder.toString + msg) def writeLine(line: String): Unit = println(line) object RainbowDebug: def toLines(thingy: Any)(using color: Boolean): List[String] = thingy match case printable: Printable => printable.getDebugOutput.toLines case _ => thingy.toString.linesIterator.toList val colors = ArrayBuffer(red, yellow, green, cyan, blue, magenta) def red(content: String): String = Console.RED + content + Console.RESET def yellow(content: String): String = Console.YELLOW + content + Console.RESET def green(content: String): String = Console.GREEN + content + Console.RESET def cyan(content: String): String = Console.CYAN + content + Console.RESET def blue(content: String): String = Console.BLUE + content + Console.RESET def magenta(content: String): String = Console.MAGENTA + content + Console.RESET def black(content: String): String = Console.BLACK + content + Console.RESET def underline(content: String): String = Console.UNDERLINED + content + Console.RESET def bold(content: String): String = Console.BOLD + content + Console.RESET ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/debug/TreeDebug.scala ================================================ package mlscript.compiler import scala.collection.mutable.ArrayBuffer class TreeDebug(output: String => Unit) extends RainbowDebug(false): private val lines = ArrayBuffer[String]() private var indentation: Int = 0 override inline def writeLine(line: String): Unit = output("║"*indentation ++ line) lines += line override def indent(): Unit = indentation += 1 override def outdent(): Unit = indentation -= 1 def getLines: List[String] = lines.toList ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ir/Builder.scala ================================================ package mlscript.compiler.ir import mlscript.compiler.optimizer.FreeVarAnalysis import mlscript.utils.shorthands._ import mlscript.utils._ import mlscript._ import mlscript.Message.MessageContext import scala.collection.mutable.ListBuffer final val ops = Set("+", "-", "*", "/", ">", "<", ">=", "<=", "!=", "==") final val builtin = Set("builtin") final class Builder(fresh: Fresh, fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit, verbose: Boolean = false): import Node._ import Expr._ private def log(x: Any) = if verbose then println(x) private final case class ClassInfoPartial(name: Str, fields: Ls[Str], methods: Set[Str]): var freeVars = Ls.empty[Str] var ctx = ctxEmpty private final case class DefnInfoPartial(name: Str, params: Ls[Str]): var freeVars = Ls.empty[Str] var ctx = ctxEmpty private type NameCtx = Map[Str, Name] // name -> new name private type ClassCtx = Map[Str, ClassInfoPartial] // class name -> partial class info private type FnCtx = Map[Str, DefnInfoPartial] // fn name -> partial defn info private type OpCtx = Set[Str] // op names private final case class Ctx( nameCtx: NameCtx = Map.empty, tyNameCtx: NameCtx = Map.empty, classCtx: ClassCtx = Map.empty, fnCtx: FnCtx = Map.empty, opCtx: OpCtx = Set.empty, jpAcc: ListBuffer[Defn], defAcc: ListBuffer[NuFunDef], lcAcc: ListBuffer[NuTypeDef], ): def hasClassLifted = lcAcc.nonEmpty def hasLifted = jpAcc.nonEmpty || defAcc.nonEmpty || lcAcc.nonEmpty private def ctxEmpty = Ctx( nameCtx = Map.empty, tyNameCtx = Map.empty, classCtx = Map.empty, fnCtx = Map.empty, opCtx = ops, jpAcc = ListBuffer.empty, defAcc = ListBuffer.empty, lcAcc = ListBuffer.empty, ) private def ctxJoin(c1: Ctx, c2: Ctx) = Ctx( nameCtx = c1.nameCtx ++ c2.nameCtx, tyNameCtx = c1.tyNameCtx ++ c2.tyNameCtx, classCtx = c1.classCtx ++ c2.classCtx, fnCtx = c1.fnCtx ++ c2.fnCtx, opCtx = c1.opCtx ++ c2.opCtx, jpAcc = c1.jpAcc, defAcc = c1.defAcc, lcAcc = c1.lcAcc, ) private def ref(x: Name) = Ref(x) private def result(x: Ls[TrivialExpr]) = Result(x).attachTag(tag) private def sresult(x: TrivialExpr) = Result(Ls(x)).attachTag(tag) private def unexpectedNode(x: Node) = throw IRError(s"unsupported node $x") private def unexpectedTerm(x: Statement) = throw IRError(s"unsupported term $x") private def buildBinding(using ctx: Ctx)(name: Str, e: Term, body: Term)(k: Node => Node): Node = buildResultFromTerm(e) { case Result((r: Ref) :: Nil) => given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (name -> r.name)) buildResultFromTerm(body)(k) case Result((lit: Literal) :: Nil) => val v = fresh.make given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (name -> v)) LetExpr(v, lit, buildResultFromTerm(body)(k)).attachTag(tag) case node @ _ => node |> unexpectedNode } private def buildResultFromTup(using ctx: Ctx)(tup: Tup)(k: Node => Node): Node = tup match case Tup(N -> Fld(FldFlags.empty, x) :: xs) => buildResultFromTerm(x) { case Result(x :: Nil) => buildResultFromTup(Tup(xs)) { case Result(xs) => x :: xs |> result |> k case node @ _ => node |> unexpectedNode } case node @ _ => node |> unexpectedNode } case Tup(Nil) => Nil |> result |> k case _ => ??? private def bindingPatternVariables(scrut: Str, tup: Tup, cls: ClassInfoPartial, rhs: Term): Term = val params = tup |> getTupleFields val fields = cls.fields val tm = params.zip(fields).foldLeft(rhs) { case (tm, (param, field)) => Let(false, Var(param), App(Sel(Var(cls.name), Var(field)), Tup(Ls(N -> Fld(FldFlags.empty, Var(scrut))))), tm) } tm private def freeVariablesIf(ucs: If): Set[Str] = val ifbody: IfBody = ucs.body val els: Opt[Term] = ucs.els def f(ifbody: IfBody): Set[Str] = ifbody match case IfBlock(lines) => lines.foldLeft(Set.empty[Str]) { case (acc, Left(ifbody2)) => acc ++ f(ifbody2) case (acc, Right(rhs)) => acc ++ freeVariables(rhs)._2 } case IfElse(expr) => freeVariables(expr) case IfLet(isRec, name, rhs, body) => if isRec then (freeVariables(rhs) -- freeVariables(name)) ++ (f(body) -- freeVariables(name)) else freeVariables(rhs) ++ (f(body) -- freeVariables(name)) case IfOpApp(lhs, op, rhs) => freeVariables(lhs) ++ f(rhs) case IfOpsApp(lhs, opsRhss) => freeVariables(lhs) ++ opsRhss.foldLeft(Set.empty[Str]) { case (acc, (op, rhs)) => acc ++ f(rhs) } case IfThen(expr, rhs) => freeVariables(rhs) -- freeVariables(expr) val fvs1 = f(ifbody) val fvs2 = els.fold(Set.empty[Str]) { freeVariables } fvs1 ++ fvs2 private def freeVariables(tu: TypingUnit): Set[Str] = var all_defined = tu.rawEntities.foldLeft(Set.empty[Str]) { case (defined, stmt) => defined ++ freeVariables(stmt)._1 } tu.rawEntities.foldLeft(Set.empty[Str]) { case (acc, stmt) => acc ++ (freeVariables(stmt)._2 -- all_defined) } // for this fv analysis, we also need to return the variables that are defined in this statement private def freeVariables(stmt: Statement): (Set[Str], Set[Str]) = stmt match case DataDefn(body) => throw IRError("unsupported DataDefn") case DatatypeDefn(head, body) => throw IRError("unsupported DatatypeDefn") case LetS(isRec, pat, rhs) => throw IRError("unsupported LetS") case ntd: NuTypeDef => val fvs = freeVariables(ntd.body) -- ntd.params.fold(Set.empty)(freeVariables) (Set(ntd.nme.name), fvs) case Constructor(params, body) => throw IRError("unsupported Constructor") case nfd: NuFunDef => val fvs = nfd.isLetRec match case None | Some(true) => nfd.rhs.fold(tm => freeVariables(tm) -- freeVariables(nfd.nme), ty => Set.empty) case Some(false) => nfd.rhs.fold(tm => freeVariables(tm), ty => Set.empty) (freeVariables(nfd.nme), fvs) case Def(rec, nme, rhs, isByname) => throw IRError("unsupported Def") case TypeDef(kind, nme, tparams, body, mthDecls, mthDefs, positionals, adtInfo) => throw IRError("unsupported TypeDef") case x: Term => val fvs = freeVariables(x) (Set.empty, fvs) private def freeVariables(tm: Term): Set[Str] = tm match case AdtMatchWith(cond, arms) => val inner: Set[Str] = arms.foldLeft(Set.empty){ case (acc, AdtMatchPat(pat, body)) => acc ++ freeVariables(body) -- freeVariables(pat) } freeVariables(cond) ++ inner case Ann(ann, receiver) => freeVariables(receiver) case App(lhs, rhs) => freeVariables(lhs) ++ freeVariables(rhs) case Asc(trm, ty) => freeVariables(trm) case Assign(lhs, rhs) => freeVariables(lhs) ++ freeVariables(rhs) case Bind(lhs, rhs) => freeVariables(lhs) case Blk(stmts) => var fvs = Set.empty[Str] var defined = Set.empty[Str] stmts.foreach { stmt => { val stmt_fvs = freeVariables(stmt) fvs ++= stmt_fvs._2 fvs --= defined defined ++= stmt_fvs._1 } } fvs case Bra(rcd, trm) => freeVariables(trm) case CaseOf(trm, cases) => import mlscript.{Case => CCase} def f(pat: CaseBranches): Set[Str] = pat match case CCase(pat, body, rest) => freeVariables(body) -- freeVariables(pat) ++ f(rest) case NoCases => Set.empty case Wildcard(body) => freeVariables(body) freeVariables(trm) ++ f(cases) case Eqn(lhs, rhs) => freeVariables(rhs) case Forall(params, body) => freeVariables(body) case x: If => freeVariablesIf(x) case Inst(body) => freeVariables(body) case Lam(lhs, rhs) => freeVariables(rhs) -- freeVariables(lhs) case Let(isRec, name, rhs, body) => if isRec then (freeVariables(rhs) -- freeVariables(name)) ++ (freeVariables(body) -- freeVariables(name)) else freeVariables(rhs) ++ (freeVariables(body) -- freeVariables(name)) case New(head, body) => throw IRError("unsupported New") case NuNew(cls) => freeVariables(cls) case Quoted(body) => throw IRError("unsupported Quoted") case Rcd(fields) => fields.foldLeft(Set.empty[Str]) { case (acc, (_, Fld(_, trm))) => acc ++ freeVariables(trm) } case Rft(base, decls) => throw IRError("unsupported Rft") case Sel(receiver, fieldName) => freeVariables(receiver) case Splc(fields) => fields.foldLeft(Set.empty[Str]) { case (acc, Left(trm)) => acc ++ freeVariables(trm) case (acc, Right(Fld(_, trm))) => acc ++ freeVariables(trm) } case Subs(arr, idx) => freeVariables(arr) ++ freeVariables(idx) case Super() => Set.empty case Test(trm, ty) => freeVariables(trm) case Tup(fields) => fields.foldLeft(Set.empty[Str]) { case (acc, (_, Fld(_, trm))) => acc ++ freeVariables(trm) } case TyApp(lhs, targs) => freeVariables(lhs) case Unquoted(body) => throw IRError("unsupported Unquoted") case Where(body, where) => throw IRError("unsupported Where") case While(cond, body) => freeVariables(cond) ++ freeVariables(body) case With(trm, fields) => freeVariables(trm) ++ freeVariables(fields: Term) case Var(name) => Set(name) case DecLit(value) => Set.empty case IntLit(value) => Set.empty case StrLit(value) => Set.empty case UnitLit(undefinedOrNull) => Set.empty private def buildLetCall(using ctx: Ctx)(f: Var, xs: Tup, ann: Option[Term])(k: Node => Node) = buildResultFromTup(xs) { case Result(args) => val v = fresh.make ctx.nameCtx.get(f.name) match case None => throw IRError(s"unknown name $f in $ctx") case Some(f2) => ctx.fnCtx.get(f2.str) match case None => throw IRError(s"unknown function $f2 in $ctx") case Some(dInfo) => val args2 = if args.size != dInfo.params.size then args ++ dInfo.freeVars.map(x => Ref(ctx.nameCtx(x))) // it's possible that the free vars as parameters have been filled when do eta expansion else args ann match case Some(ann @ Var(nme)) => if nme === "tailcall" then LetCall(List(v), DefnRef(Right(f2.str)), args2, true, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) else if nme === "tailrec" then raise(ErrorReport(List(msg"@tailrec is for annotating functions; try @tailcall instead" -> ann.toLoc), true, Diagnostic.Compilation)) LetCall(List(v), DefnRef(Right(f2.str)), args2, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case Some(_) => throw IRError("unsupported annotation") case None => LetCall(List(v), DefnRef(Right(f2.str)), args2, false, v |> ref |> sresult |> k)(f.toLoc).attachTag(tag) case node @ _ => node |> unexpectedNode } private def buildResultFromTerm(using ctx: Ctx)(tm: Statement)(k: Node => Node): Node = val res = tm match case lit: Lit => Literal(lit) |> sresult |> k case v @ Var(name) => if (name.isCapitalized) ctx.tyNameCtx.get(name) match case Some(x) => val v = fresh.make ctx.classCtx.get(x.str) match case Some(clsinfo) => val args = (if clsinfo.freeVars.isEmpty then Nil else clsinfo.freeVars.map(x => Ref(ctx.nameCtx(x)))) LetExpr(v, CtorApp(ClassRef(Right(x.str)), args), v |> ref |> sresult |> k).attachTag(tag) case None => throw IRError(s"unknown class $name in $ctx") case None => throw IRError(s"unknown type name $name in $ctx") else ctx.nameCtx.get(name) match { case Some(x) => if (ctx.fnCtx.contains(x.str)) val info = ctx.fnCtx(x.str) val arity = info.params.size - info.freeVars.size val range = 0 until arity val xs = range.map(_ => fresh.make.str).toList val params = Tup(xs.map(x => N -> Fld(FldFlags.empty, Var(x)))) val args = Tup(params.fields ++ info.freeVars.map(x => N -> Fld(FldFlags.empty, Var(x)))) val lam = Lam(params, App(Var(x.str), args)) buildResultFromTerm(lam)(k) else x |> ref |> sresult |> k case None => throw IRError(s"unknown name $name in $ctx") } case lam @ Lam(Tup(fields), body) => val tmp = fresh.make val lambdaName = fresh.make("Lambda") val result = Var(lambdaName.str) val localCls = Blk( NuTypeDef(Cls, TypeName(lambdaName.str), Nil, N, N, N, Ls(Var("Callable")), N, N, TypingUnit( NuFunDef(N, Var(s"apply${fields.size}"), None, Nil, L(Lam(Tup(fields), body)))(tm.getLoc, N, N, N, N, false, Nil) :: Nil ))(tm.getLoc, tm.getLoc, Nil) :: result :: Nil) buildResultFromTerm(localCls)(k) case App( App(Var(name), Tup((_ -> Fld(_, e1)) :: Nil)), Tup((_ -> Fld(_, e2)) :: Nil)) if ctx.opCtx.contains(name) => buildResultFromTerm(e1) { case Result(v1 :: Nil) => buildResultFromTerm(e2) { case Result(v2 :: Nil) => val v = fresh.make LetExpr(v, BasicOp(name, List(v1, v2)), v |> ref |> sresult |> k).attachTag(tag) case node @ _ => node |> unexpectedNode } case node @ _ => node |> unexpectedNode } case x @ App(Var(name), xs @ Tup(_)) if name.isCapitalized || ctx.opCtx.contains(name) => if name.isCapitalized then buildResultFromTerm(xs) { case Result(args) => ctx.tyNameCtx.get(name) match case Some(x) => val v = fresh.make ctx.classCtx.get(x.str) match case Some(clsinfo) => val args2 = args ++ clsinfo.freeVars.map(x => Ref(ctx.nameCtx(x))) LetExpr(v, CtorApp(ClassRef(Right(x.str)), args2), v |> ref |> sresult |> k).attachTag(tag) case None => throw IRError(s"unknown class $name in $ctx") case None => throw IRError(s"unknown type name $name in $ctx") case node @ _ => node |> unexpectedNode } else buildResultFromTerm(xs) { case Result(args) => val v = fresh.make LetExpr(v, BasicOp(name, args), v |> ref |> sresult |> k).attachTag(tag) case node @ _ => node |> unexpectedNode } case App( member @ Sel(Var(clsName), Var(fld)), xs @ Tup((_ -> Fld(_, Var(s))) :: _)) if clsName.isCapitalized => buildResultFromTup(xs) { case Result(xs @ (Ref(name) :: args)) => ctx.tyNameCtx.get(clsName) match case Some(x) => val v = fresh.make val cls = ctx.classCtx(x.str) val isField = cls.fields.contains(fld) if isField then LetExpr(v, Select(name, ClassRef(Right(x.str)), fld), v |> ref |> sresult |> k).attachTag(tag) else if cls.methods.contains(fld) then LetMethodCall(Ls(v), ClassRef(Right(x.str)), Name(fld), xs, v |> ref |> sresult |> k).attachTag(tag) else throw IRError(s"unknown field or method $fld in $cls") case None => throw IRError(s"unknown type name $clsName in $ctx") case node @ _ => node |> unexpectedNode } case App(vf @ Var(f), xs @ Tup(_)) if ctx.fnCtx.contains(f) || ctx.nameCtx.get(f).fold(false)(x => ctx.fnCtx.contains(x.str)) => buildLetCall(vf, xs, None)(k) case Ann(ann, App(vf @ Var(f), xs @ Tup(_))) if ctx.fnCtx.contains(f) || ctx.nameCtx.get(f).fold(false)(x => ctx.fnCtx.contains(x.str)) => buildLetCall(vf, xs, Some(ann))(k) case App(f, xs @ Tup(_)) => buildResultFromTerm(f) { case Result(Ref(f) :: Nil) => buildResultFromTerm(xs) { case Result(args) => val v = fresh.make // LetApply(List(v), f, args, v |> ref |> sresult |> k).attachTag(tag) LetMethodCall(List(v), ClassRef(R("Callable")), Name("apply" + args.length), (Ref(f): TrivialExpr) :: args, v |> ref |> sresult |> k).attachTag(tag) case node @ _ => node |> unexpectedNode } case node @ _ => node |> unexpectedNode } case Ann(ann @ Var(name), recv) => if name === "tailcall" then raise(ErrorReport(List(msg"@tailcall may only be used to annotate function calls" -> ann.toLoc), true, Diagnostic.Compilation)) else if name === "tailrec" then raise(ErrorReport(List(msg"@tailrec may only be used to annotate functions" -> ann.toLoc), true, Diagnostic.Compilation)) buildResultFromTerm(recv)(k) case Let(false, Var(name), rhs, body) => buildBinding(name, rhs, body)(k) case If(IfThen(cond, tru), Some(fls)) => buildResultFromTerm(cond) { case Result(Ref(cond) :: Nil) => if (!ctx.classCtx.contains("True") || !ctx.classCtx.contains("False")) throw IRError("True or False class not found, unable to use 'if then else'") val jp = fresh.make("j") val res = fresh.make val jpbody = res |> ref |> sresult |> k val fvs = FreeVarAnalysis(extended_scope = false).run_with(jpbody, Set(res.str)).toList val jpdef = Defn( fnUid.make, jp.str, params = res :: fvs.map(x => Name(x)), resultNum = 1, jpbody, false, None ) ctx.jpAcc.addOne(jpdef) val tru2 = buildResultFromTerm(tru) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode } val fls2 = buildResultFromTerm(fls) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode } Case(cond, Ls( (Pat.Class(ClassRef(Right("True"))), tru2), (Pat.Class(ClassRef(Right("False"))), fls2)), None).attachTag(tag) case node @ _ => node |> unexpectedNode } case If(IfOpApp(lhs, Var("is"), IfBlock(lines)), N) => buildResultFromTerm(lhs) { case Result(Ref(scrut) :: Nil) => val jp = fresh.make("j") val res = fresh.make val jpbody = res |> ref |> sresult |> k val fvs = FreeVarAnalysis(extended_scope = false).run_with(jpbody, Set(res.str)).toList val jpdef = Defn( fnUid.make, jp.str, params = res :: fvs.map(x => Name(x)), resultNum = 1, jpbody, false, None, ) ctx.jpAcc.addOne(jpdef) var defaultCase: Opt[Node] = None val cases: Ls[(Pat, Node)] = lines flatMap { case L(IfThen(App(Var(ctor), params: Tup), rhs)) => S(Pat.Class(ClassRef(Right(ctor))) -> { // need this because we have built terms (selections in case arms) containing names that are not in the original term given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (scrut.str -> scrut)) buildResultFromTerm( bindingPatternVariables(scrut.str, params, ctx.classCtx(ctor), rhs)) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode } }) case L(IfThen(lit @ IntLit(_), rhs)) => S(Pat.Lit(lit) -> buildResultFromTerm(rhs) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode }) case L(IfThen(Var("_"), rhs)) => defaultCase = Some(buildResultFromTerm(rhs) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode }) N case L(IfThen(Var(ctor), rhs)) => S(Pat.Class(ClassRef(Right(ctor))) -> buildResultFromTerm(rhs) { case Result(xs) => Jump(DefnRef(Right(jp.str)), xs ++ fvs.map(x => Ref(Name(x)))).attachTag(tag) case node @ _ => node |> unexpectedNode }) case _ => throw IRError(s"not supported UCS") } Case(scrut, cases, defaultCase).attachTag(tag) case node @ _ => node |> unexpectedNode } case Bra(false, tm) => buildResultFromTerm(tm)(k) case Blk(stmts) => val (nfds, ntds, terms) = collectInfo(stmts) val (ctx2, nfds2, ntds2) = renameToBeLifted(nfds, ntds) val ctx3 = initContextForStatementsFrom( nfds2, ntds2, terms, scanNamesInThisScope(stmts) )(using ctx2) ctx.lcAcc.addAll(ntds2) ctx.defAcc.addAll(nfds2) buildResultFromTerms(using ctx3)(terms)(k) case tup: Tup => buildResultFromTup(tup)(k) case term => term |> unexpectedTerm res private def buildResultFromTerms(using ctx: Ctx)(tms: Ls[Statement])(k: Node => Node): Node = tms match case Nil => throw IRError("empty term list") case NuFunDef(S(false), Var(name), _, _, Left(tm)) :: xs => xs match case Nil => throw IRError("unsupported NuFunDef at tail position") case _ => buildResultFromTerm(tm) { case Result((r: Ref) :: Nil) => given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (name -> r.name)) buildResultFromTerms(xs)(k) case Result((lit: Literal) :: Nil) => val v = fresh.make given Ctx = ctx.copy(nameCtx = ctx.nameCtx + (name -> v)) LetExpr(v, lit, buildResultFromTerms(xs)(k)).attachTag(tag) case node @ _ => node |> unexpectedNode } case x :: Nil => buildResultFromTerm(x)(k) case x :: xs => buildResultFromTerm(x) { case _ => buildResultFromTerms(xs)(k) } private def getTupleFields(tup: Tup) = tup.fields.map { case N -> Fld(FldFlags.empty, Var(name)) => name case S(Var(name)) -> Fld(_, ty) => name case _ => throw IRError("unsupported field") } private def buildDefFromMethod(using ctx: Ctx)(fields: List[Str], nfd: Statement): Defn = nfd match case nfd @ NuFunDef(_, Var(name), None, Nil, L(Lam(xs @ Tup(_), body))) => val defnInfoPartial = getDefnInfoPartial(ctx.fnCtx.keySet ++ ctx.classCtx.keySet ++ fields, ctx)(nfd).get val params = defnInfoPartial.params val names = params map (fresh.make(_)) val ctx2 = ctxJoin(ctx, defnInfoPartial.ctx) given Ctx = ctx2.copy(nameCtx = ctx2.nameCtx ++ (params zip names)) Defn( fnUid.make, name, params = names, resultNum = 1, body = buildResultFromTerm(body) { x => x }, isTailRec = false, loc = nfd.getLoc, ) case fd @ _ => throw IRError("unsupported NuFunDef " + fd.toString) private def buildDefFromNuFunDef(using ctx: Ctx)(nfd: Statement): Defn = nfd match case nfd @ NuFunDef(_, Var(name), None, Nil, L(Lam(xs @ Tup(_), body))) => val defnInfoPartial = getDefnInfoPartial(ctx.fnCtx.keySet ++ ctx.classCtx.keySet, ctx)(nfd).get val params = defnInfoPartial.params val names = params map (fresh.make(_)) val ctx2 = ctxJoin(ctx, defnInfoPartial.ctx) given Ctx = ctx2.copy(nameCtx = ctx2.nameCtx ++ (params zip names)) val trAnn = nfd.annotations.find { case Var("tailrec") => true case ann @ Var("tailcall") => raise(ErrorReport(List(msg"@tailcall is for annotating function calls; try @tailrec instead" -> ann.toLoc), true, Diagnostic.Compilation)) false case _ => false } Defn( fnUid.make, name, params = names, resultNum = 1, buildResultFromTerm(body) { x => x }, trAnn.isDefined, trAnn.flatMap(_.toLoc) ) case fd @ _ => throw IRError("unsupported NuFunDef " + fd.toString) private def buildClassInfo(using ctx: Ctx)(ntd: Statement): ClassInfo = ntd match case ntd @ NuTypeDef(Cls | Mod, TypeName(name), _, params, N, _, parents, N, N, TypingUnit(methods)) => val clsInfoPartial = getClassInfoPartial(ctx.classCtx.keySet ++ ctx.fnCtx.keySet, ctx)(ntd) val cls = ClassInfo(classUid.make, name, clsInfoPartial.fields) val ctx2 = ctxJoin(ctx, clsInfoPartial.ctx) given Ctx = ctx2.copy( nameCtx = ctx2.nameCtx ++ clsInfoPartial.fields.map(x => x -> Name(x)), classCtx = ctx2.classCtx + (name -> clsInfoPartial) ) def resolveParentName(parent: Term): String = parent match { case Var(name) if name.isCapitalized => name case TyApp(lhs, _) => resolveParentName(lhs) case App(lhs, _) => resolveParentName(lhs) case _ => throw IRError("unsupported parent") } cls.parents = parents.map(resolveParentName).toSet cls.methods = methods.map { case x: NuFunDef => x.name -> buildDefFromMethod(clsInfoPartial.fields, x) case x @ _ => throw IRError(f"unsupported method $x") }.toMap cls case x @ _ => throw IRError(f"unsupported NuTypeDef $x") private def getDefnInfoPartial(names: Set[Str], ctx: Ctx)(nfd: NuFunDef): Opt[DefnInfoPartial] = nfd match case NuFunDef(_, Var(name), _, _, Left(Lam(Var(x), _))) => val originalFvs = freeVariables(nfd)._2 val fvs = (originalFvs -- builtin -- ops -- names).toList val params = x :: fvs val dip = DefnInfoPartial(name, params) dip.freeVars = fvs dip.ctx = ctx S(dip) case NuFunDef(_, Var(name), _, _, Left(Lam(tp @ Tup(fields), _))) => val originalFvs = freeVariables(nfd)._2 val fvs = (originalFvs -- builtin -- ops -- names).toList val params = getTupleFields(tp) ++ fvs val dip = DefnInfoPartial(name, params) dip.freeVars = fvs dip.ctx = ctx S(dip) case NuFunDef(_, Var(name), _, _, _) => N private def getClassInfoPartial(names: Set[Str], ctx: Ctx)(ntd: NuTypeDef): ClassInfoPartial = ntd match case ntd @ NuTypeDef(Cls | Mod, TypeName(name), _, params, N, _, parents, N, N, TypingUnit(other)) => val originalFvs = freeVariables(ntd)._2 log(s"getClassInfoPartial $name") log(originalFvs) log(names) val fvs = (originalFvs -- builtin -- ops -- names).toList val fields = params.fold(fvs)(xs => getTupleFields(xs) ++ fvs) val methods = other.map { case x: NuFunDef => x.name case x @ _ => throw IRError(f"unsupported method $x") } val cls = ClassInfoPartial(name, fields, methods.toSet) cls.freeVars = fvs cls.ctx = ctx cls case x @ _ => throw IRError(f"unsupported NuTypeDef $x") private def collectInfo(stmts: Ls[Statement]): (Ls[NuFunDef], Ls[NuTypeDef], Ls[Statement]) = var nfds = ListBuffer.empty[NuFunDef] var ntds = ListBuffer.empty[NuTypeDef] var terms = ListBuffer.empty[Statement] stmts.foreach { case tm @ NuFunDef(S(false), Var(_), _, _, Left(_)) => terms.addOne(tm) case nfd: NuFunDef => nfds.addOne(nfd) case ntd: NuTypeDef => ntds.addOne(ntd) case tm: Term => terms.addOne(tm) case _ => throw IRError("unsupported statement") } (nfds.toList, ntds.toList, terms.toList) private def makeNameMap(str: IterableOnce[Str]) = str.iterator.map(x => (x, Name(x))).toMap private def scanNamesInThisScope(stmt: Ls[Statement]): Set[Str] = val names = stmt flatMap { case NuTypeDef(_, TypeName(name), _, _, _, _, _, _, _, _) => S(name) case NuFunDef(_, Var(name), _, _, _) => S(name) case _ => Nil } names.toSet private def renameToBeLifted(nfds: Ls[NuFunDef], ntds: Ls[NuTypeDef])(using ctx: Ctx): (Ctx, Ls[NuFunDef], Ls[NuTypeDef]) = val oldFnNames = scanNamesInThisScope(nfds).toList val oldTyNames = scanNamesInThisScope(ntds).toList // TODO: currently, rename cause bugs //val newFnNames = oldFnNames.map(x => fresh.make(x)) //val newTyNames = oldTyNames.map(x => if x.startsWith("Lambda$") then Name(x) else fresh.make(x)) val newFnNames = oldFnNames.map(Name(_)) val newTyNames = oldTyNames.map(Name(_)) val nameCtx = oldFnNames.zip(newFnNames).toMap val tyNameCtx = oldTyNames.zip(newTyNames).toMap val nfds2 = nfds.map(x => x.copy(nme = Var(nameCtx(x.name).str))(x.declareLoc, x.virtualLoc, x.mutLoc, x.signature, x.outer, x.genField, x.annotations)) val ntds2 = ntds.map(x => x.copy(nme = TypeName(tyNameCtx(x.name).str))(x.declareLoc, x.abstractLoc, x.annotations)) (ctx.copy(nameCtx = ctx.nameCtx ++ nameCtx, tyNameCtx = ctx.tyNameCtx ++ tyNameCtx), nfds2, ntds2) private def initContextForStatementsFrom(nfds: Ls[NuFunDef], ntds: Ls[NuTypeDef], terms: Ls[Statement], excluded: Set[Str])(using ctx: Ctx): Ctx = // they are in the same mutual group or higher group, mustn't capture them val excludedNames = excluded ++ scanNamesInThisScope(nfds) ++ scanNamesInThisScope(ntds) ++ ctx.fnCtx.keySet ++ ctx.classCtx.keySet val partialDefnInfo = nfds flatMap getDefnInfoPartial(excludedNames, ctx) val partialClassInfo = ntds map getClassInfoPartial(excludedNames, ctx) val fnNames = partialDefnInfo.map(_.name) val fnCtx = fnNames.zip(partialDefnInfo).toMap val nameCtx = makeNameMap(builtin) ++ makeNameMap(fnNames) ++ makeNameMap(ops) val tyNames = partialClassInfo.map(_.name) val tyNameCtx = makeNameMap(tyNames) val classCtx = tyNames.zip(partialClassInfo).toMap ctx.copy( tyNameCtx = ctx.tyNameCtx ++ tyNameCtx, nameCtx = ctx.nameCtx ++ nameCtx, classCtx = ctx.classCtx ++ classCtx, fnCtx = ctx.fnCtx ++ fnCtx, ) private def initContextForStatements(nfds: Ls[NuFunDef], ntds: Ls[NuTypeDef], terms: Ls[Statement]): Ctx = val ctx = Ctx( nameCtx = Map.empty, tyNameCtx = Map.empty, classCtx = Map.empty, fnCtx = Map.empty, opCtx = ops, jpAcc = ListBuffer.empty, defAcc = ListBuffer.empty, lcAcc = ListBuffer.empty, ) initContextForStatementsFrom(nfds, ntds, terms, Set.empty)(using ctx) def getHiddenNames(prelude: TypingUnit): Set[Str] = def resolveTypeName(x: Term): Str = x match case Var(name) => name case TyApp(lhs, _) => resolveTypeName(lhs) case App(lhs, _) => resolveTypeName(lhs) case _ => throw IRError("unsupported type name") val hidden = prelude.rawEntities.flatMap { case NuFunDef(_, Var(name), _, _, _) => Nil case NuTypeDef(_, TypeName(name), _, params, _, _, _, _, _, _) if name == "HiddenTheseEntities" => params.fold{Nil}{ x => x.fields.flatMap { case S(Var(name)) -> Fld(FldFlags.empty, ty) => resolveTypeName(ty) :: Nil case _ => Nil } } case NuTypeDef(_, TypeName(name), _, _, _, _, _, _, _, _) => Nil case _ => Nil } hidden.toSet def buildGraph(unit: TypingUnit, prelude: TypingUnit, addPrelude: Boolean = true): Program = val unit2 = if addPrelude then TypingUnit(prelude.rawEntities ++ unit.rawEntities) else unit val hiddenNames = getHiddenNames(unit2) val (nfds, ntds, terms) = collectInfo(unit2.rawEntities) var ctx = initContextForStatements(nfds, ntds, terms) val definitions = ListBuffer.empty[Defn] val classes = ListBuffer.empty[ClassInfo] var first = true var curNfds = nfds var curNtds = ntds val main = buildResultFromTerm(using ctx)(Blk(terms)){ k => k } while first || ctx.hasLifted do first = false ctx.jpAcc.clear() ctx.defAcc.clear() ctx.lcAcc.clear() definitions.addAll(curNfds.map(buildDefFromNuFunDef(using ctx))) definitions.addAll(ctx.jpAcc) classes.addAll(curNtds.map(buildClassInfo(using ctx))) curNfds = ctx.defAcc.toList curNtds = ctx.lcAcc.toList ctx = initContextForStatementsFrom(curNfds, curNtds, Nil, Set.empty)(using ctx) val clsInfo = classes.toSet val defs = definitions.toSet resolveRef(main, defs, clsInfo, true) validate(main, defs, clsInfo) Program(clsInfo, defs, main) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ir/Fresh.scala ================================================ package mlscript.compiler.ir import collection.mutable.{HashMap => MutHMap} import mlscript.utils.shorthands._ final class Fresh(div : Char = '$'): private val counter = MutHMap[Str, Int]() private def gensym(s: Str) = { val n = s.lastIndexOf(div) val (ts, suffix) = s.splitAt(if n == -1 then s.length() else n) var x = if suffix.stripPrefix(div.toString).forall(_.isDigit) then ts else s val count = counter.getOrElse(x, 0) val tmp = s"$x$div$count" counter.update(x, count + 1) Name(tmp) } def make(s: Str) = gensym(s) def make = gensym("x") final class FreshInt: private var counter = 0 def make: Int = { val tmp = counter counter += 1 tmp } ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ir/IR.scala ================================================ package mlscript.compiler.ir import mlscript._ import mlscript.utils._ import mlscript.utils.shorthands._ import mlscript.compiler.utils._ import mlscript.compiler.optimizer._ import mlscript.compiler.ir._ import mlscript.Loc import collection.mutable.{Map as MutMap, Set as MutSet, HashMap, ListBuffer} import annotation.unused import util.Sorting import scala.collection.immutable.SortedSet import scala.language.implicitConversions final case class IRError(message: String) extends Exception(message) case class Program( classes: Set[ClassInfo], defs: Set[Defn], main: Node, ): override def toString: String = val t1 = classes.toArray val t2 = defs.toArray Sorting.quickSort(t1) Sorting.quickSort(t2) s"Program({${t1.mkString(",\n")}}, {\n${t2.mkString("\n")}\n},\n$main)" def show(hiddenNames: Set[Str] = Set.empty) = toDocument(hiddenNames).print def toDocument(hiddenNames: Set[Str] = Set.empty) : Document = val t1 = classes.toArray val t2 = defs.toArray Sorting.quickSort(t1) Sorting.quickSort(t2) given Conversion[String, Document] = raw stack( "Program:", stack_list(t1.filter(x => !hiddenNames.contains(x.name)).map(_.toDocument).toList) |> indent, stack_list(t2.map(_.toDocument).toList) |> indent, main.toDocument |> indent ) implicit object ClassInfoOrdering extends Ordering[ClassInfo] { def compare(a: ClassInfo, b: ClassInfo) = a.id.compare(b.id) } case class ClassInfo( id: Int, name: Str, fields: Ls[Str], ): var parents: Set[Str] = Set.empty var methods: Map[Str, Defn] = Map.empty override def hashCode: Int = id override def toString: String = s"ClassInfo($id, $name, [${fields mkString ","}], parents: ${parents mkString ","}, methods:\n${methods mkString ",\n"})" def show = toDocument.print def toDocument: Document = given Conversion[String, Document] = raw val extension = if parents.isEmpty then "" else " extends " + parents.mkString(", ") if methods.isEmpty then "class" <:> name <#> "(" <#> fields.mkString(",") <#> ")" <#> extension else stack( "class" <:> name <#> "(" <#> fields.mkString(",") <#> ")" <#> extension <:> "{", stack_list( methods.map { (_, defn) => defn.toDocument |> indent }.toList), "}" ) case class Name(val str: Str): def copy: Name = Name(str) def trySubst(map: Map[Str, Name]) = map.getOrElse(str, this) override def toString: String = str class DefnRef(var defn: Either[Defn, Str]): def name: String = defn.fold(_.name, x => x) def expectDefn: Defn = defn match { case Left(defn) => defn case Right(name) => throw Exception(s"Expected a def, but got $name") } def getDefn: Opt[Defn] = defn.left.toOption override def equals(o: Any): Bool = o match { case o: DefnRef => o.name == this.name case _ => false } class ClassRef(var cls: Either[ClassInfo, Str]): def name: String = cls.fold(_.name, x => x) def expectClass: ClassInfo = cls match { case Left(cls) => cls case Right(name) => throw Exception(s"Expected a class, but got $name") } def getClass: Opt[ClassInfo] = cls.left.toOption override def equals(o: Any): Bool = o match { case o: ClassRef => o.name == this.name case _ => false } implicit object DefOrdering extends Ordering[Defn] { def compare(a: Defn, b: Defn) = a.id.compare(b.id) } case class Defn( id: Int, name: Str, params: Ls[Name], resultNum: Int, body: Node, isTailRec: Bool, loc: Opt[Loc] = None ): override def hashCode: Int = id override def toString: String = val ps = params.map(_.toString).mkString("[", ",", "]") s"Def($id, $name, $ps,\n$resultNum, \n$body\n)" def show = toDocument.print def toDocument: Document = given Conversion[String, Document] = raw stack( "def" <:> name <#> "(" <#> params.map(_.toString).mkString(",") <#> ")" <:> "=", body.toDocument |> indent ) sealed trait TrivialExpr: import Expr._ override def toString: String def show: String def toDocument: Document def mapNameOfTrivialExpr(f: Name => Name): TrivialExpr = this match case x: Ref => Ref(f(x.name)) case x: Literal => x def toExpr: Expr = this match { case x: Expr => x } private def showArguments(args: Ls[TrivialExpr]) = args map (_.show) mkString "," enum Expr: case Ref(name: Name) extends Expr, TrivialExpr case Literal(lit: Lit) extends Expr, TrivialExpr case CtorApp(cls: ClassRef, args: Ls[TrivialExpr]) case Select(name: Name, cls: ClassRef, field: Str) case BasicOp(name: Str, args: Ls[TrivialExpr]) case AssignField(assignee: Name, cls: ClassRef, field: Str, value: TrivialExpr) override def toString: String = show def show: String = toDocument.print def toDocument: Document = given Conversion[String, Document] = raw this match case Ref(s) => s.toString case Literal(IntLit(lit)) => s"$lit" case Literal(DecLit(lit)) => s"$lit" case Literal(StrLit(lit)) => s"$lit" case Literal(UnitLit(lit)) => s"$lit" case CtorApp(cls, args) => cls.name <#> "(" <#> (args |> showArguments) <#> ")" case Select(s, cls, fld) => cls.name <#> "." <#> fld <#> "(" <#> s.toString <#> ")" case BasicOp(name: Str, args) => name <#> "(" <#> (args |> showArguments) <#> ")" case AssignField(assignee, clsInfo, fieldName, value) => stack( "assign" <:> (assignee.toString + "." + fieldName) <:> ":=" <:> value.toDocument ) def mapName(f: Name => Name): Expr = this match case Ref(name) => Ref(f(name)) case Literal(lit) => Literal(lit) case CtorApp(cls, args) => CtorApp(cls, args.map(_.mapNameOfTrivialExpr(f))) case Select(x, cls, field) => Select(f(x), cls, field) case BasicOp(name, args) => BasicOp(name, args.map(_.mapNameOfTrivialExpr(f))) case AssignField(assignee, clsInfo, fieldName, value) => AssignField(f(assignee), clsInfo, fieldName, value.mapNameOfTrivialExpr(f)) def locMarker: LocMarker = this match case Ref(name) => LocMarker.MRef(name.str) case Literal(lit) => LocMarker.MLit(lit) case CtorApp(cls, args) => LocMarker.MCtorApp(cls, args.map(_.toExpr.locMarker)) case Select(name, cls, field) => LocMarker.MSelect(name.str, cls, field) case BasicOp(name, args) => LocMarker.MBasicOp(name, args.map(_.toExpr.locMarker)) case AssignField(assignee, clsInfo, fieldName, value) => LocMarker.MAssignField(assignee.str, fieldName, value.toExpr.locMarker) enum Pat: case Lit(lit: mlscript.Lit) case Class(cls: ClassRef) def isTrue = this match case Class(cls) => cls.name == "True" case _ => false def isFalse = this match case Class(cls) => cls.name == "False" case _ => false override def toString: String = this match case Lit(lit) => s"$lit" case Class(cls) => s"${cls.name}" enum Node: // Terminal forms: case Result(res: Ls[TrivialExpr]) case Jump(defn: DefnRef, args: Ls[TrivialExpr]) case Case(scrut: Name, cases: Ls[(Pat, Node)], default: Opt[Node]) // Intermediate forms: case LetExpr(name: Name, expr: Expr, body: Node) case LetMethodCall(names: Ls[Name], cls: ClassRef, method: Name, args: Ls[TrivialExpr], body: Node) // Deprecated: // LetApply(names, fn, args, body) => LetMethodCall(names, ClassRef(R("Callable")), Name("apply" + args.length), (Ref(fn): TrivialExpr) :: args, body) // case LetApply(names: Ls[Name], fn: Name, args: Ls[TrivialExpr], body: Node) case LetCall(names: Ls[Name], defn: DefnRef, args: Ls[TrivialExpr], isTailRec: Bool, body: Node)(val loc: Opt[Loc] = None) var tag = DefnTag(-1) def attachTag(x: FreshInt): Node = this.tag = DefnTag(x.make) this def attachTagAs[V](x: FreshInt): V = attachTag(x).asInstanceOf[V] def copyTag(x: Node) = this.tag = x.tag this override def toString: String = show def show: String = toDocument.print def mapName(f: Name => Name): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(f))) case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(f))) case Case(scrut, cases, default) => Case(f(scrut), cases.map { (cls, arm) => (cls, arm.mapName(f)) }, default.map(_.mapName(f))) case LetExpr(name, expr, body) => LetExpr(f(name), expr.mapName(f), body.mapName(f)) case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names.map(f), cls, f(method), args.map(_.mapNameOfTrivialExpr(f)), body.mapName(f)) case lc @ LetCall(names, defn, args, itr, body) => LetCall(names.map(f), defn, args.map(_.mapNameOfTrivialExpr(f)), itr, body.mapName(f))(lc.loc) def copy(ctx: Map[Str, Name]): Node = this match case Result(res) => Result(res.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) case Jump(defn, args) => Jump(defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx)))) case Case(scrut, cases, default) => Case(ctx(scrut.str), cases.map { (cls, arm) => (cls, arm.copy(ctx)) }, default.map(_.copy(ctx))) case LetExpr(name, expr, body) => val name_copy = name.copy LetExpr(name_copy, expr.mapName(_.trySubst(ctx)), body.copy(ctx + (name_copy.str -> name_copy))) case LetMethodCall(names, cls, method, args, body) => val names_copy = names.map(_.copy) LetMethodCall(names_copy, cls, method.copy, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), body.copy(ctx ++ names_copy.map(x => x.str -> x))) case lc @ LetCall(names, defn, args, itr, body) => val names_copy = names.map(_.copy) LetCall(names_copy, defn, args.map(_.mapNameOfTrivialExpr(_.trySubst(ctx))), itr, body.copy(ctx ++ names_copy.map(x => x.str -> x)))(lc.loc) def toDocument: Document = given Conversion[String, Document] = raw this match case Result(res) => (res |> showArguments) <:> s"-- $tag" case Jump(jp, args) => "jump" <:> jp.name <#> "(" <#> (args |> showArguments) <#> ")" <:> s"-- $tag" case Case(x, Ls((tpat, tru), (fpat, fls)), N) if tpat.isTrue && fpat.isFalse => val first = "if" <:> x.toString <:> s"-- $tag" val tru2 = indent(stack("true" <:> "=>", tru.toDocument |> indent)) val fls2 = indent(stack("false" <:> "=>", fls.toDocument |> indent)) Document.Stacked(Ls(first, tru2, fls2)) case Case(x, cases, default) => val first = "case" <:> x.toString <:> "of" <:> s"-- $tag" val other = cases flatMap { case (pat, node) => Ls(pat.toString <:> "=>", node.toDocument |> indent) } default match case N => stack(first, (Document.Stacked(other) |> indent)) case S(dc) => val default = Ls("_" <:> "=>", dc.toDocument |> indent) stack(first, (Document.Stacked(other ++ default) |> indent)) case LetExpr(x, expr, body) => stack( "let" <:> x.toString <:> "=" <:> expr.toDocument <:> "in" <:> s"-- $tag", body.toDocument) case LetMethodCall(xs, cls, method, args, body) => stack( "let" <:> xs.map(_.toString).mkString(",") <:> "=" <:> cls.name <#> "." <#> method.toString <#> "(" <#> args.map{ x => x.toString }.mkString(",") <#> ")" <:> "in" <:> s"-- $tag", body.toDocument) case LetCall(xs, defn, args, itr, body) => stack( "let*" <:> "(" <#> xs.map(_.toString).mkString(",") <#> ")" <:> "=" <:> (if itr then "@tailcall " else "") + defn.name <#> "(" <#> args.map{ x => x.toString }.mkString(",") <#> ")" <:> "in" <:> s"-- $tag", body.toDocument) def locMarker: LocMarker = val marker = this match case Result(res) => LocMarker.MResult(res.map(_.toExpr.locMarker)) case Jump(defn, args) => LocMarker.MJump(defn.name, args.map(_.toExpr.locMarker)) case Case(scrut, cases, default) => LocMarker.MCase(scrut.str, cases.map(_._1), default.isDefined) case LetExpr(name, expr, _) => LocMarker.MLetExpr(name.str, expr.locMarker) case LetMethodCall(names, cls, method, args, _) => LocMarker.MLetMethodCall(names.map(_.str), cls, method.str, args.map(_.toExpr.locMarker)) case LetCall(names, defn, args, _, _) => LocMarker.MLetCall(names.map(_.str), defn.name, args.map(_.toExpr.locMarker)) marker.tag = this.tag marker trait DefTraversalOrdering: def ordered(entry: Node, defs: Set[Defn]): Ls[Defn] def orderedNames(entry: Node, defs: Set[Defn]): Ls[Str] object DefDfs: import Node._ private object Successors: def find(node: Node)(using acc: Ls[Defn]): Ls[Defn] = node match case Result(res) => acc case Jump(defn, args) => defn.expectDefn :: acc case Case(scrut, cases, default) => val acc2 = cases.map(_._2) ++ default.toList acc2.foldLeft(acc)((acc, x) => find(x)(using acc)) case LetExpr(name, expr, body) => find(body) case LetMethodCall(names, cls, method, args, body) => find(body) case LetCall(names, defn, args, _, body) => find(body)(using defn.expectDefn :: acc) def find(defn: Defn)(using acc: Ls[Defn]): Ls[Defn] = find(defn.body) def findNames(node: Node)(using acc: Ls[Str]): Ls[Str] = node match case Result(res) => acc case Jump(defn, args) => defn.name :: acc case Case(scrut, cases, default) => val acc2 = cases.map(_._2) ++ default.toList acc2.foldLeft(acc)((acc, x) => findNames(x)(using acc)) case LetExpr(name, expr, body) => findNames(body) case LetMethodCall(names, cls, method, args, body) => findNames(body) case LetCall(names, defn, args, _, body) => findNames(body)(using defn.name :: acc) def findNames(defn: Defn)(using acc: Ls[Str]): Ls[Str] = findNames(defn.body) private def dfs(using visited: HashMap[Str, Bool], out: ListBuffer[Defn], postfix: Bool)(x: Defn): Unit = visited.update(x.name, true) if (!postfix) out += x Successors.find(x)(using Nil).foreach { y => if (!visited(y.name)) dfs(y) } if (postfix) out += x private def dfs(using visited: HashMap[Str, Bool], out: ListBuffer[Defn], postfix: Bool)(x: Node): Unit = Successors.find(x)(using Nil).foreach { y => if (!visited(y.name)) dfs(y) } private def dfsNames(using visited: HashMap[Str, Bool], defs: Set[Defn], out: ListBuffer[Str], postfix: Bool)(x: Defn): Unit = visited.update(x.name, true) if (!postfix) out += x.name Successors.findNames(x)(using Nil).foreach { y => if (!visited(y)) dfsNames(defs.find(_.name == y).get) } if (postfix) out += x.name private def dfsNames(using visited: HashMap[Str, Bool], defs: Set[Defn], out: ListBuffer[Str], postfix: Bool)(x: Node): Unit = Successors.findNames(x)(using Nil).foreach { y => if (!visited(y)) dfsNames(defs.find(_.name == y).get) } def dfs(entry: Node, defs: Set[Defn], postfix: Bool): Ls[Defn] = val visited = HashMap[Str, Bool]() visited ++= defs.map(_.name -> false) val out = ListBuffer[Defn]() dfs(using visited, out, postfix)(entry) out.toList def dfsNames(entry: Node, defs: Set[Defn], postfix: Bool): Ls[Str] = val visited = HashMap[Str, Bool]() visited ++= defs.map(_.name -> false) val out = ListBuffer[Str]() dfsNames(using visited, defs, out, postfix)(entry) out.toList object DefRevPostOrdering extends DefTraversalOrdering: def ordered(entry: Node, defs: Set[Defn]): Ls[Defn] = DefDfs.dfs(entry, defs, true).reverse def orderedNames(entry: Node, defs: Set[Defn]): Ls[Str] = DefDfs.dfsNames(entry, defs, true).reverse object DefRevPreOrdering extends DefTraversalOrdering: def ordered(entry: Node, defs: Set[Defn]): Ls[Defn] = DefDfs.dfs(entry, defs, false).reverse def orderedNames(entry: Node, defs: Set[Defn]): Ls[Str] = DefDfs.dfsNames(entry, defs, false).reverse case class DefnTag(inner: Int): def is_valid = inner >= 0 override def equals(x: Any): Bool = x match case o: DefnTag => (this, o) match case (DefnTag(a), DefnTag(b)) => this.is_valid && o.is_valid && a == b case _ => false override def toString: String = if is_valid then s"#$inner" else "#x" case class DefnLocMarker(val defn: Str, val marker: LocMarker): override def toString: String = s"$defn:$marker" def matches = marker.matches enum LocMarker: case MRef(name: Str) case MLit(lit: Lit) case MCtorApp(name: ClassRef, args: Ls[LocMarker]) case MSelect(name: Str, cls: ClassRef, field: Str) case MBasicOp(name: Str, args: Ls[LocMarker]) case MAssignField(assignee: Str, field: Str, value: LocMarker) case MResult(res: Ls[LocMarker]) case MJump(name: Str, args: Ls[LocMarker]) case MCase(scrut: Str, cases: Ls[Pat], default: Bool) case MLetExpr(name: Str, expr: LocMarker) case MLetMethodCall(names: Ls[Str], cls: ClassRef, method: Str, args: Ls[LocMarker]) case MLetCall(names: Ls[Str], defn: Str, args: Ls[LocMarker]) var tag = DefnTag(-1) def toDocument: Document = given Conversion[String, Document] = raw this match case MResult(res) => "..." case MJump(jp, args) => "jump" <:> jp <:> "..." case MCase(x, Ls(tpat, fpat), false) if tpat.isTrue && fpat.isFalse => "if" <:> x.toString <:> "..." case MCase(x, cases, default) => "case" <:> x.toString <:> "of" <:> "..." case MLetExpr(x, expr) => "let" <:> x.toString <:> "=" <:> "..." case MLetMethodCall(xs, cls, method, args) => "let" <:> xs.map(_.toString).mkString(",") <:> "=" <:> cls.name <:> "." <:> method <:> "..." case MLetCall(xs, defn, args) => "let*" <:> "(" <#> xs.map(_.toString).mkString(",") <#> ")" <:> "=" <:> defn <:> "..." case MRef(s) => s.toString case MLit(IntLit(lit)) => s"$lit" case MLit(DecLit(lit)) => s"$lit" case MLit(StrLit(lit)) => s"$lit" case MLit(UnitLit(undefinedOrNull)) => if undefinedOrNull then "undefined" else "null" case _ => "..." def show = s"$tag-" + toDocument.print override def toString: String = show def matches(x: Node): Bool = this.tag == x.tag ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ir/Interp.scala ================================================ package mlscript.compiler.ir import mlscript._ import mlscript.compiler.ir._ import mlscript.compiler.utils._ import mlscript.compiler.optimizer._ import mlscript.utils._ import scala.collection.immutable._ import scala.annotation._ import shorthands._ import scala.collection.mutable.ListBuffer import scala.util.boundary, boundary.break enum Stuck: case StuckExpr(expr: Expr, msg: Str) case StuckNode(node: Node, msg: Str) override def toString: String = this match case StuckExpr(expr, msg) => s"StuckExpr(${expr.show}, $msg)" case StuckNode(node, msg) => s"StuckNode(${node.show}, $msg)" final case class InterpreterError(message: String) extends Exception(message) class Interpreter(verbose: Bool): private def log(x: Any) = if verbose then println(x) import Stuck._ private case class Configuration( var context: Ctx ) private type Result[T] = Either[Stuck, T] private enum Value: case Class(cls: ClassInfo, var fields: Ls[Value]) case Literal(lit: Lit) override def toString: String = this match case Class(cls, fields) => s"${cls.name}(${fields.mkString(",")})" case Literal(IntLit(lit)) => lit.toString case Literal(DecLit(lit)) => lit.toString case Literal(StrLit(lit)) => lit.toString case Literal(UnitLit(undefinedOrNull)) => if undefinedOrNull then "undefined" else "null" private final case class Ctx( bindingCtx: Map[Str, Value], classCtx: Map[Str, ClassInfo], defnCtx: Map[Str, Defn], ) import Node._ import Expr._ private def getTrue(using ctx: Ctx) = Value.Class(ctx.classCtx("True"), Ls.empty) private def getFalse(using ctx: Ctx) = Value.Class(ctx.classCtx("False"), Ls.empty) private def eval(op: Str, x1: Value, x2: Value)(using ctx: Ctx): Opt[Value] = import Value.{Literal => Li} (op, x1, x2) match case ("+", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x + y))) case ("-", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x - y))) case ("*", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x * y))) case ("/", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x / y))) case ("==", Li(IntLit(x)), Li(IntLit(y))) => S(if x == y then getTrue else getFalse) case ("!=", Li(IntLit(x)), Li(IntLit(y))) => S(if x != y then getTrue else getFalse) case ("<=", Li(IntLit(x)), Li(IntLit(y))) => S(if x <= y then getTrue else getFalse) case (">=", Li(IntLit(x)), Li(IntLit(y))) => S(if x >= y then getTrue else getFalse) case (">", Li(IntLit(x)), Li(IntLit(y))) => S(if x > y then getTrue else getFalse) case ("<", Li(IntLit(x)), Li(IntLit(y))) => S(if x < y then getTrue else getFalse) case _ => N private def evalArgs(using ctx: Ctx)(exprs: Ls[TrivialExpr]): Result[Ls[Value]] = var values = ListBuffer.empty[Value] var stuck: Opt[Stuck] = None exprs foreach { expr => stuck match case None => eval(expr) match case L(x) => stuck = Some(x) case R(x) => values += x case _ => () } stuck.toLeft(values.toList) private def eval(expr: TrivialExpr)(using ctx: Ctx): Result[Value] = expr match case e @ Ref(name) => ctx.bindingCtx.get(name.str).toRight(StuckExpr(e, s"undefined variable $name")) case Literal(lit) => R(Value.Literal(lit)) private def eval(expr: Expr)(using ctx: Ctx): Result[Value] = expr match case Ref(Name(x)) => ctx.bindingCtx.get(x).toRight(StuckExpr(expr, s"undefined variable $x")) case Literal(x) => R(Value.Literal(x)) case CtorApp(cls, args) => for { xs <- evalArgs(args) cls <- ctx.classCtx.get(cls.name).toRight(StuckExpr(expr, s"undefined class ${cls.name}")) } yield Value.Class(cls, xs) case Select(name, cls, field) => ctx.bindingCtx.get(name.str).toRight(StuckExpr(expr, s"undefined variable $name")).flatMap { case Value.Class(cls2, xs) if cls.name == cls2.name => xs.zip(cls2.fields).find{_._2 == field} match case Some((x, _)) => R(x) case None => L(StuckExpr(expr, s"unable to find selected field $field")) case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) case x => L(StuckExpr(expr, s"unexpected value $x")) } case BasicOp(name, args) => boundary: evalArgs(args).flatMap( xs => name match case "+" | "-" | "*" | "/" | "==" | "!=" | "<=" | ">=" | "<" | ">" => if xs.length < 2 then break: L(StuckExpr(expr, s"not enough arguments for basic operation $name")) else eval(name, xs.head, xs.tail.head).toRight(StuckExpr(expr, s"unable to evaluate basic operation")) case _ => L(StuckExpr(expr, s"unexpected basic operation $name"))) case AssignField(assignee, cls, field, value) => for { x <- eval(Ref(assignee): TrivialExpr) y <- eval(value) res <- x match case obj @ Value.Class(cls2, xs) if cls.name == cls2.name => xs.zip(cls2.fields).find{_._2 == field} match case Some((_, _)) => obj.fields = xs.map(x => if x == obj then y else x) // Ideally, we should return a unit value here, but here we return the assignee value for simplicity. R(obj) case None => L(StuckExpr(expr, s"unable to find selected field $field")) case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) case x => L(StuckExpr(expr, s"unexpected value $x")) } yield res private def eval(node: Node)(using ctx: Ctx): Result[Ls[Value]] = node match case Result(res) => evalArgs(res) case Jump(defn, args) => for { xs <- evalArgs(args) defn <- ctx.defnCtx.get(defn.name).toRight(StuckNode(node, s"undefined function ${defn.name}")) ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ defn.params.map{_.str}.zip(xs)) res <- eval(defn.body)(using ctx1) } yield res case Case(scrut, cases, default) => eval(Ref(scrut): Expr) flatMap { case Value.Class(cls, fields) => cases.find { case (Pat.Class(cls2), _) => cls.name == cls2.name case _ => false } match { case Some((_, x)) => eval(x) case None => default match case S(x) => eval(x) case N => L(StuckNode(node, s"can not find the matched case, ${cls.name} expected")) } case Value.Literal(lit) => cases.find { case (Pat.Lit(lit2), _) => lit == lit2 case _ => false } match { case Some((_, x)) => eval(x) case None => default match case S(x) => eval(x) case N => L(StuckNode(node, s"can not find the matched case, $lit expected")) } } case LetExpr(name, expr, body) => for { x <- eval(expr) ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx + (name.str -> x)) res <- eval(body)(using ctx1) } yield res case LetMethodCall(names, cls, method, args, body) => for { ys <- evalArgs(args).flatMap { case Value.Class(cls2, xs) :: args => cls2.methods.get(method.str).toRight(StuckNode(node, s"undefined method ${method.str}")).flatMap { method => val ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ cls2.fields.zip(xs) ++ method.params.map{_.str}.zip(args)) eval(method.body)(using ctx1) } case _ => L(StuckNode(node, s"not enough arguments for method call, or the first argument is not a class")) } ctx2 = ctx.copy(bindingCtx = ctx.bindingCtx ++ names.map{_.str}.zip(ys)) res <- eval(body)(using ctx2) } yield res // case LetApply(names, fn, args, body) => eval(LetMethodCall(names, ClassRef(R("Callable")), Name("apply" + args.length), (Ref(fn): TrivialExpr) :: args, body)) case LetCall(names, defn, args, _, body) => for { xs <- evalArgs(args) defn <- ctx.defnCtx.get(defn.name).toRight(StuckNode(node, s"undefined function ${defn.name}")) ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ defn.params.map{_.str}.zip(xs)) ys <- eval(defn.body)(using ctx1) ctx2 = ctx.copy(bindingCtx = ctx.bindingCtx ++ names.map{_.str}.zip(ys)) res <- eval(body)(using ctx2) } yield res private def f(prog: Program): Ls[Value] = val Program(classes, defs, main) = prog given Ctx = Ctx( bindingCtx = Map.empty, classCtx = classes.map{cls => cls.name -> cls}.toMap, defnCtx = defs.map{defn => defn.name -> defn}.toMap, ) eval(main) match case R(x) => x case L(x) => throw InterpreterError("Stuck evaluation: " + x.toString) def interpret(prog: Program): Str = val node = f(prog) node.map(_.toString).mkString(",") ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ir/RefResolver.scala ================================================ package mlscript.compiler.ir import mlscript.utils.shorthands._ import mlscript.compiler.ir._ import Node._ // Resolves the definition references by turning them from Right(name) to Left(Defn). private final class RefResolver(defs: Map[Str, Defn], classes: Map[Str, ClassInfo], allowInlineJp: Bool): private def f(x: Expr): Unit = x match case Expr.Ref(name) => case Expr.Literal(lit) => case Expr.CtorApp(cls, args) => classes.get(cls.name) match case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") case Some(value) => cls.cls = Left(value) case Expr.Select(name, cls, field) => classes.get(cls.name) match case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") case Some(value) => cls.cls = Left(value) case Expr.BasicOp(name, args) => case Expr.AssignField(assigneee, cls, field, value) => classes.get(cls.name) match case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") case Some(value) => cls.cls = Left(value) private def f(x: Pat): Unit = x match case Pat.Lit(lit) => case Pat.Class(cls) => classes.get(cls.name) match case None => throw IRError(f"unknown class ${cls.name} in ${classes.keySet.mkString(",")}") case Some(value) => cls.cls = Left(value) private def f(x: Node): Unit = x match case Result(res) => case Case(scrut, cases, default) => cases foreach { (_, body) => f(body) }; default foreach f case LetExpr(name, expr, body) => f(expr); f(body) case LetMethodCall(names, cls, method, args, body) => f(body) case LetCall(resultNames, defnref, args, _, body) => defs.get(defnref.name) match case Some(defn) => defnref.defn = Left(defn) case None => throw IRError(f"unknown function ${defnref.name} in ${defs.keySet.mkString(",")}") f(body) case Jump(defnref, _) => // maybe not promoted yet defs.get(defnref.name) match case Some(defn) => defnref.defn = Left(defn) case None => if (!allowInlineJp) throw IRError(f"unknown function ${defnref.name} in ${defs.keySet.mkString(",")}") def run(node: Node) = f(node) def run(node: Defn) = f(node.body) def resolveRef(entry: Node, defs: Set[Defn], classes: Set[ClassInfo], allowInlineJp: Bool = false): Unit = val defsMap = defs.map(x => x.name -> x).toMap val classesMap = classes.map(x => x.name -> x).toMap val rl = RefResolver(defsMap, classesMap, allowInlineJp) rl.run(entry) defs.foreach(rl.run(_)) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/ir/Validator.scala ================================================ package mlscript.compiler.ir import mlscript.utils.shorthands._ import mlscript.compiler.ir._ private final class DefnRefInSet(defs: Set[Defn], classes: Set[ClassInfo]): import Node._ import Expr._ private def f(x: Expr): Unit = x match case Ref(name) => case Literal(lit) => case CtorApp(name, args) => case Select(name, clsref, field) => clsref.getClass match { case Some(real_class) => if (!classes.exists(_ eq real_class)) throw IRError("ref is not in the set") case _ => } case BasicOp(name, args) => case AssignField(assignee, clsref, _, value) => clsref.getClass match { case Some(real_class) => if (!classes.exists(_ eq real_class)) throw IRError("ref is not in the set") case _ => } private def f(x: Node): Unit = x match case Result(res) => case Jump(defn, args) => case Case(scrut, cases, default) => cases foreach { (_, body) => f(body) }; default foreach f case LetExpr(name, expr, body) => f(body) case LetMethodCall(names, cls, method, args, body) => f(body) case LetCall(res, defnref, args, _, body) => defnref.getDefn match { case Some(real_defn) => if (!defs.exists(_ eq real_defn)) throw IRError("ref is not in the set") case _ => } f(body) def run(node: Node) = f(node) def run(defn: Defn) = f(defn.body) def validateRefInSet(entry: Node, defs: Set[Defn], classes: Set[ClassInfo]): Unit = val dris = DefnRefInSet(defs, classes) defs.foreach(dris.run(_)) def validate(entry: Node, defs: Set[Defn], classes: Set[ClassInfo]): Unit = validateRefInSet(entry, defs, classes) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/optimizer/Analysis.scala ================================================ package mlscript.compiler.optimizer import mlscript._ import mlscript.compiler.ir._ import mlscript.utils._ import shorthands._ import scala.annotation.tailrec import scala.collection.immutable.* import scala.collection.mutable.{HashMap => MutHMap} import scala.collection.mutable.{HashSet => MutHSet, Set => MutSet} class UsefulnessAnalysis(verbose: Bool = false): import Expr._ import Node._ def log(x: Any) = if verbose then println(x) val uses = MutHMap[(Name, Int), Int]() val defs = MutHMap[Name, Int]() private def addDef(x: Name) = defs.update(x, defs.getOrElse(x, 0) + 1) private def addUse(x: Name) = val def_count = defs.get(x) match case None => throw IRError(s"Use of undefined variable $x") case Some(value) => value val key = (x, defs(x)) uses.update(key, uses.getOrElse(key, 0) + 1) private def f(x: TrivialExpr): Unit = x match case Ref(name) => addUse(name) case _ => () private def f(x: Expr): Unit = x match case Ref(name) => addUse(name) case Literal(lit) => case CtorApp(name, args) => args.foreach(f) case Select(name, cls, field) => addUse(name) case BasicOp(name, args) => args.foreach(f) case AssignField(assignee, _, _, value) => addUse(assignee) f(value) private def f(x: Node): Unit = x match case Result(res) => res.foreach(f) case Jump(defn, args) => args.foreach(f) case Case(scrut, cases, default) => addUse(scrut); cases.foreach { case (cls, body) => f(body) }; default.foreach(f) case LetMethodCall(names, cls, method, args, body) => addUse(method); args.foreach(f); names.foreach(addDef); f(body) case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) case LetCall(names, defn, args, _, body) => args.foreach(f); names.foreach(addDef); f(body) def run(x: Defn) = x.params.foreach(addDef) f(x.body) uses.toMap class FreeVarAnalysis(extended_scope: Bool = true, verbose: Bool = false): import Expr._ import Node._ private val visited = MutHSet[Str]() private def f(using defined: Set[Str])(defn: Defn, fv: Set[Str]): Set[Str] = val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) f(using defined2)(defn.body, fv) private def f(using defined: Set[Str])(expr: Expr, fv: Set[Str]): Set[Str] = expr match case Ref(name) => if (defined.contains(name.str)) fv else fv + name.str case Literal(lit) => fv case CtorApp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Select(name, cls, field) => if (defined.contains(name.str)) fv else fv + name.str case BasicOp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case AssignField(assignee, _, _, value) => f(using defined)( value.toExpr, if defined.contains(assignee.str) then fv + assignee.str else fv ) private def f(using defined: Set[Str])(node: Node, fv: Set[Str]): Set[Str] = node match case Result(res) => res.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Jump(defnref, args) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) if (extended_scope && !visited.contains(defnref.name)) val defn = defnref.expectDefn visited.add(defn.name) val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) fv2 = f(using defined2)(defn, fv2) fv2 case Case(scrut, cases, default) => val fv2 = if (defined.contains(scrut.str)) fv else fv + scrut.str val fv3 = cases.foldLeft(fv2) { case (acc, (cls, body)) => f(using defined)(body, acc) } fv3 case LetMethodCall(resultNames, cls, method, args, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) f(using defined2)(body, fv2) case LetExpr(name, expr, body) => val fv2 = f(using defined)(expr, fv) val defined2 = defined + name.str f(using defined2)(body, fv2) case LetCall(resultNames, defnref, args, _, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name.str) if (extended_scope && !visited.contains(defnref.name)) val defn = defnref.expectDefn visited.add(defn.name) val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param.str) fv2 = f(using defined2)(defn, fv2) f(using defined2)(body, fv2) def run(node: Node) = f(using Set.empty)(node, Set.empty) def run_with(node: Node, defined: Set[Str]) = f(using defined)(node, Set.empty) ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala ================================================ package mlscript package compiler.optimizer import scala.annotation.tailrec import utils.shorthands._ import Message.MessageContext import compiler.ir._ import compiler.ir.Node._ /* DOCUMENTATION OF SEMANTICS OF @tailcall and @tailrec @tailcall: Used to annotate specific function calls. Calls annotated with @tailcall must be tail calls or tail modulo-cons calls. These calls must be optimized to not consume additional stack space. If such an optimization is not possible, then the compiler will throw an error. If there are multiple possible candidates for tail modulo-cons calls in a single branch of an expression, then @tailcall can be uesd to indicate which one will be optimized. For instance in fun foo() = A(foo(), bar()) we can use @tailcall to annotate the call foo() or bar(). If a call other than the last call is annotated with @tailcall, then the remaining functions must be pure to ensure that reordering computations does not change the result. If bar() is impure but you still want to optimize the call foo(), then you can do fun foo() = let b = bar() let a = @tailcall foo() A(a, b) because here, you are taking responsibility for the reordering of the computations. @tailrec: Used to annotate functions. When this annotation is used on a function, say @tailrec fun foo(), the compiler will ensure no sequence of direct recursive calls back to foo() consume stack space, i.e. they are all tail calls. Note that a call to foo() may consume an arbitrary amount of stack space as long as foo() is only consuming finite stack space. For example, @tailrec fun foo() = bar() fun bar() = bar() bar() is valid. However, @tailrec fun foo() = bar() fun bar() = foo() bar() is invalid. If we swap the position of foo() and bar() in the body of bar, it is still invalid. Equivalently, if fun foo() is annotated with @tailrec, let S be the largest strongly connected component in the call-graph of the program that contains foo. Then an error will be thrown unless all edges (calls) connecting the nodes of the strongly connected component are tail calls or tail modulo-cons calls. */ // fnUid should be the same FreshInt that was used to build the graph being passed into this class class TailRecOpt(fnUid: FreshInt, classUid: FreshInt, tag: FreshInt, raise: Diagnostic => Unit): case class LetCtorNodeInfo(node: LetExpr, ctor: Expr.CtorApp, cls: ClassInfo, ctorValName: Name, fieldName: String, idx: Int) enum CallInfo: case NormalCallInfo(src: Defn, defn: Defn)(val loc: Option[Loc]) extends CallInfo case TailCallInfo(src: Defn, defn: Defn) extends CallInfo case ModConsCallInfo(src: Defn, startNode: Node, defn: Defn, letCallNode: LetCall, letCtorNode: LetCtorNodeInfo, retName: Name, retNode: Node) extends CallInfo override def toString(): String = this match case NormalCallInfo(src, defn) => f"Call { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" case TailCallInfo(src, defn) => f"TailCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id} }" case ModConsCallInfo(src, startNode, defn, letCallNode, letCtorNode, _, _) => f"ModConsCall { ${src.name}$$${src.id} -> ${defn.name}$$${defn.id}, class: ${letCtorNode.cls.name}, field: ${letCtorNode.fieldName} }" def getSrc = this match case NormalCallInfo(src, _) => src case TailCallInfo(src, _) => src case ModConsCallInfo(src, _, _, _, _, _, _) => src def getDefn = this match case NormalCallInfo(_, defn) => defn case TailCallInfo(_, defn) => defn case ModConsCallInfo(_, _, defn, _, _, _, _) => defn private class DefnGraph(val nodes: Set[DefnNode], val edges: Set[CallInfo], val joinPoints: Set[Defn]): def removeMetadata: ScComponent = ScComponent(nodes.map(_.defn), edges, joinPoints) private class ScComponent(val nodes: Set[Defn], val edges: Set[CallInfo], val joinPoints: Set[Defn]) import CallInfo._ def filterOptCalls(calls: Iterable[CallInfo]) = calls.collect { case c: TailCallInfo => c; case c: ModConsCallInfo => c } def filterNormalCalls(calls: Iterable[CallInfo]) = calls.collect { case c: NormalCallInfo => c } // Hack to make scala think discoverJoinPoints is tail recursive and be // partially optimized :P def casesToJps(cases: List[(Pat, Node)], default: Opt[Node], acc: Set[Defn]): Set[Defn] = cases.foldLeft(default.fold(acc)(x => discoverJoinPoints(x, acc)))((jps, branch) => discoverJoinPoints(branch._2, jps)) def discoverJoinPointsCont(defn: Defn, acc: Set[Defn]) = discoverJoinPoints(defn.body, acc) + defn // TODO: implement proper purity checking. This is a very simple purity check that only allows the last // parameter of a mod cons call to be optimised. private val pureCache: scala.collection.mutable.Map[Int, Bool] = scala.collection.mutable.Map[Int, Bool]() private def isPure(node: Node): Bool = pureCache.get(node.tag.inner) match case None => val ret = node match case Jump(defn, args) => isIdentityJp(defn.expectDefn) case _: LetCall => false case LetMethodCall(names, cls, method, args, body) => false case Case(scrut, cases, default) => cases.foldLeft(default.fold(false)(isPure))((value, branch) => value && isPure(branch._2)) case LetExpr(name, expr: Expr.AssignField, body) => false case x: LetExpr => true case Result(res) => true pureCache.put(node.tag.inner, ret) ret case Some(value) => value // do a DFS to discover join points @tailrec private def discoverJoinPoints(node: Node, acc: Set[Defn]): Set[Defn] = node match case Result(res) => Set() case Jump(defn_, args) => val defn = defn_.expectDefn if isIdentityJp(defn) then acc else if acc.contains(defn) then acc else discoverJoinPointsCont(defn, acc + defn) case Case(scrut, cases, default) => casesToJps(cases, default, acc) case LetExpr(name, expr, body) => discoverJoinPoints(body, acc) case LetMethodCall(names, cls, method, args, body) => discoverJoinPoints(body, acc) case LetCall(names, defn, args, isTailRec, body) => discoverJoinPoints(body, acc) private def getRetName(names: Set[Name], retVals: List[TrivialExpr]): Option[Name] = val names = retVals.collect { case Expr.Ref(nme) => nme } if names.length != 1 then None else val nme = names.head if names.contains(nme) then Some(nme) else None // would prefer to have this inside discoverOptCalls, but scala does not support partially tail recursive functions directly def shadowAndCont(next: Node, nme: Name)(implicit acc: Set[CallInfo], src: Defn, scc: Set[Defn], start: Node, calledDefn: Option[Defn], letCallNode: Option[LetCall], letCtorNode: Option[LetCtorNodeInfo], containingCtors: Set[Name] ) = searchOptCalls(next)(using acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors - nme) // same here... def invalidateAndCont(body: Node)(implicit acc: Set[CallInfo], src: Defn, scc: Set[Defn], start: Node, calledDefn: Option[Defn], letCallNode: Option[LetCall], letCtorNode: Option[LetCtorNodeInfo], containingCtors: Set[Name] ) = letCallNode match case None => searchOptCalls(body)(using acc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered case Some(x: LetCall) => val LetCall(_, defn, _, isTailRec, _) = x if isTailRec then raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) val newAcc = acc + NormalCallInfo(src, defn.expectDefn)(x.loc) searchOptCalls(body)(using newAcc, src, scc, start, None, None, None, Set()) // invalidate everything that's been discovered @tailrec private def searchOptCalls(node: Node)(implicit acc: Set[CallInfo], src: Defn, scc: Set[Defn], start: Node, calledDefn: Option[Defn], // The definition that was called in a tailrec mod cons call letCallNode: Option[LetCall], // The place where that definition was called letCtorNode: Option[LetCtorNodeInfo], // The place where the result from that call was put into a constructor containingCtors: Set[Name], // Value names of ctors containing the constructor containing the result from the call ): Either[Set[CallInfo], List[Node]] = def updateMapSimple(c: CallInfo) = acc + c def returnNoneCont = calledDefn match case None => Left(acc) case Some(dest) => Left(updateMapSimple(NormalCallInfo(src, dest)(letCallNode.flatMap(_.loc)))) // treat the discovered call as a normal call def returnNone = letCallNode match case Some(x: LetCall) => val LetCall(_, _, _, isTailRec, _) = x if isTailRec then raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) returnNoneCont case _ => returnNoneCont node match // Left if mod cons call found, Right if none was found -- we return the next nodes to be scanned case Result(res) => (calledDefn, letCallNode, letCtorNode) match case (Some(defn), Some(letCallNode), Some(letCtorName)) => getRetName(containingCtors, res) match case None => returnNone case Some(value) => Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) case _ => returnNone case Jump(jp, args) => // different cases (calledDefn, letCallNode, letCtorNode) match case (Some(defn), Some(letCallNode), Some(letCtorName)) => getRetName(containingCtors, args) match case Some(value) if isIdentityJp(jp.expectDefn) => Left(updateMapSimple(ModConsCallInfo(src, start, defn, letCallNode, letCtorName, value, node))) case _ => returnNone case _ => returnNone case Case(scrut, cases, default) => Right(cases.map(_._2) ++ default.toList) case x @ LetExpr(name, expr, body) => expr match // Check if this let binding references the mod cons call. case Expr.Ref(name) => letCallNode match case None => shadowAndCont(body, name) // OK case Some(LetCall(names, _, _, isTailRec, _)) => // for it to be mod cons, other values cannot use the return value from the call. if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue invalidateAndCont(body) else shadowAndCont(body, name) // OK case Expr.Literal(lit) => shadowAndCont(body, name) // OK case y @ Expr.CtorApp(clsInfo, ctorArgs) => // if expr is a constructor with a call to some function as a parameter letCallNode match case None => shadowAndCont(body, name) // OK case Some(LetCall(letCallNames, _, _, isTailRec, _)) => // there was a previous call // 1. Check if the ctor application contains this call val argNames = ctorArgs.collect { case Expr.Ref(name) => name }.toSet val namesSet = letCallNames.toSet val inters = argNames.intersect(namesSet) if inters.isEmpty then // OK, this constructor does not use the mod cons call // Now check if the constructor uses any previous ctor containing the call. // If it does, then add this name to the list of constructors containing the call val inters = containingCtors.intersect(argNames) if inters.isEmpty then shadowAndCont(body, name) // does not use, OK to ignore this one else // add this name to the list of constructors containing the call searchOptCalls(body)(using acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors + name) else // it does use it, further analyse letCtorNode match case None => // First constructor discovered using this call as a parameter. // This is OK. Add this discovered information // TODO: for now, assume functions return only one value. handling multiple // values is a bit more complicated val ctorArgName = inters.head val ctorArgIndex = ctorArgs.indexWhere { case Expr.Ref(nme) => nme == ctorArgName case _ => false } val fieldName = clsInfo.expectClass.fields(ctorArgIndex) // populate required values searchOptCalls(body)(using acc, src, scc, start, calledDefn, letCallNode, Some(LetCtorNodeInfo(x, y, clsInfo.expectClass, name, fieldName, ctorArgIndex)), Set(name)) case Some(_) => // another constructor is already using the call. Not OK // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue invalidateAndCont(body) case Expr.Select(name, cls, field) => letCallNode match case None => shadowAndCont(body, name) // OK case Some(LetCall(names, _, _, isTailRec, _)) => // for it to be mod cons, other values cannot use the return value from the call. if names.contains(name) then // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue invalidateAndCont(body) else shadowAndCont(body, name) // OK case Expr.BasicOp(_, args) => letCallNode match case None => shadowAndCont(body, name) // OK case Some(LetCall(names, _, _, isTailRec, _)) => // for it to be mod cons, other values cannot use the return value from the call. val argNames = args.collect { case Expr.Ref(name) => name }.toSet val namesSet = names.toSet val inters = argNames.intersect(namesSet) if inters.isEmpty then shadowAndCont(body, name) // OK else // if the is marked as tail recursive, we must use that call as the mod cons call, so error. otherwise, // invalidate the discovered call and continue invalidateAndCont(body) case Expr.AssignField(assignee, clsInfo, assignmentFieldName, value) => // make sure `value` is not the mod cons call letCallNode match case None => searchOptCalls(body) // OK case Some(LetCall(names, defn, args, isTailRec, _)) => value match case Expr.Ref(name) => invalidateAndCont(body) case _ => letCtorNode match case None => searchOptCalls(body) // OK case Some(LetCtorNodeInfo(_, ctor, _, name, fieldName, _)) => // If this assignment overwrites the mod cons value, forget it if containingCtors.contains(assignee) then invalidateAndCont(body) else searchOptCalls(body) case LetMethodCall(names, cls, method, args, body) => // method call is unresolved, just ignore it // `containingCtors -- names.toSet` takes care of variable shadowing searchOptCalls(body)(using acc, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names.toSet) case x @ LetCall(names, defn, args, isTailRec, body) => val callInScc = scc.contains(defn.expectDefn) // Only deal with calls in the scc if callInScc && isTailCall(x) then // If there is an old call marked as @tailcall, it cannot be a tail call, error val updatedMap = letCallNode match case Some(y) => // If both these calls are marked @tailrec, error if y.isTailRec && x.isTailRec then raise(ErrorReport( List( msg"multiple calls in the same branch marked with @tailcall" -> None, msg"first call" -> y.loc, msg"second call" -> x.loc, ), true, Diagnostic.Compilation ) ) if y.isTailRec then raise(ErrorReport(List(msg"not a tail call" -> y.loc), true, Diagnostic.Compilation)) updateMapSimple(NormalCallInfo(src, y.defn.expectDefn)(y.loc)) case None => acc Left(updatedMap + TailCallInfo(src, defn.expectDefn)) else val restIsPure = isPure(body) letCallNode match case None => // OK, we may use this LetCall as the mod cons // For now, only optimize functions which return one value if callInScc && defn.expectDefn.resultNum == 1 && restIsPure then searchOptCalls(body)(using acc, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else if isTailRec then if !restIsPure then raise(ErrorReport(List(msg"not a tail call, as the remaining functions may be impure" -> x.loc), true, Diagnostic.Compilation)) else raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) // Treat this as a normal call val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) searchOptCalls(body)(using newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) case Some(y: LetCall) => val LetCall(namesOld, defnOld, argsOld, isTailRecOld, bodyOld) = y if isTailRecOld then // 1. If both the old and newly discovered call are marked with tailrec, error if isTailRec then raise(ErrorReport( List( msg"multiple calls in the same branch marked with @tailcall" -> None, msg"first call" -> y.loc, msg"second call" -> x.loc, ), true, Diagnostic.Compilation ) ) // 2. old call is marked as tailrec so we must continue using it as the mod cons call. // make sure the newly discovered call does not use the current call as a parameter val argNames = args.collect { case Expr.Ref(name) => name }.toSet val namesSet = namesOld.toSet val inters = argNames.intersect(namesSet) if !inters.isEmpty then raise(ErrorReport(List(msg"not a tail call" -> y.loc), true, Diagnostic.Compilation)) // Treat new call as a normal call val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) searchOptCalls(body)(using newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors) // OK else // only include mod cons calls that have one return value if callInScc && defn.expectDefn.resultNum == 1 && restIsPure then // old call is not tailrec, so we can override it however we want // we take a lucky guess and mark this as the mod cons call, but the // user really should mark which calls should be tailrec // Treat the old call as a normal call val newMap = updateMapSimple(NormalCallInfo(src, defnOld.expectDefn)(y.loc)) searchOptCalls(body)(using newMap, src, scc, start, Some(defn.expectDefn), Some(x), None, Set()) else if isTailRec then if !restIsPure then raise(ErrorReport(List(msg"not a tail call, as the remaining functions may be impure" -> x.loc), true, Diagnostic.Compilation)) else raise(ErrorReport(List(msg"not a tail call" -> x.loc), true, Diagnostic.Compilation)) // shadow all the variables in this letcall // Treat this as a normal call val newMap = updateMapSimple(NormalCallInfo(src, defn.expectDefn)(x.loc)) searchOptCalls(body)(using newMap, src, scc, start, calledDefn, letCallNode, letCtorNode, containingCtors -- names) // checks whether a list of names is equal to a list of trivial expressions referencing those names private def argsListEqual(names: List[Name], exprs: List[TrivialExpr]) = if names.length == exprs.length then val results = exprs.collect { case Expr.Ref(name) => name } names == results else false private def isIdentityJp(d: Defn): Bool = d.body match case Result(res) => argsListEqual(d.params, res) case Jump(defn, args) => argsListEqual(d.params, args) && isIdentityJp(defn.expectDefn) case _ => false private def isTailCall(node: Node): Boolean = node match case LetCall(names, defn, args, _, body) => body match case Result(res) => argsListEqual(names, res) case Jump(defn, args) => argsListEqual(names, args) && isIdentityJp(defn.expectDefn) case _ => false case _ => false private def discoverOptCallsNode(node: Node)(implicit src: Defn, scc: Set[Defn], acc: Set[CallInfo]): Set[CallInfo] = searchOptCalls(node)(using acc, src, scc, node, None, None, None, Set()) match case Left(acc) => acc case Right(nodes) => nodes.foldLeft(acc)((acc, node) => discoverOptCallsNode(node)(using src, scc, acc)) private def discoverOptCalls(defn: Defn, jps: Set[Defn])(implicit scc: Set[Defn], acc: Set[CallInfo]): Set[CallInfo] = val combined = jps + defn combined.foldLeft(acc)((acc, defn_) => discoverOptCallsNode(defn_.body)(using defn, scc, acc)) private def searchCalls(node: Node)(implicit src: Defn, acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = node match case Result(res) => acc case Jump(defn, args) => acc case Case(scrut, cases, default) => cases.foldLeft(default.fold(acc)(x => searchCalls(x)(using src, acc)))((acc, item) => searchCalls(item._2)(using src, acc)) case LetExpr(name, expr, body) => searchCalls(body) case LetMethodCall(names, cls, method, args, body) => searchCalls(body) case LetCall(names, defn, args, isTailRec, body) => val newSet = acc.get(src.id) match case None => Set(defn.expectDefn) case Some(defns) => defns + defn.expectDefn searchCalls(body)(using src, acc + (src.id -> newSet)) private def discoverCalls(defn: Defn, jps: Set[Defn])(implicit acc: Map[Int, Set[Defn]]): Map[Int, Set[Defn]] = val combined = jps + defn combined.foldLeft(acc)((acc, defn_) => searchCalls(defn_.body)(using defn, acc)) // Partions a tail recursive call graph into strongly connected components // Refernece: https://en.wikipedia.org/wiki/Strongly_connected_component // Implements Tarjan's algorithm. // Wikipedia: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm // Implementation Reference: https://www.baeldung.com/cs/scc-tarjans-algorithm private class DefnNode(val defn: Defn): override def hashCode(): Int = defn.hashCode var num: Int = Int.MaxValue var lowest: Int = Int.MaxValue var visited: Boolean = false var processed: Boolean = false private def partitionNodes(implicit nodeMap: Map[Int, DefnNode]): List[DefnGraph] = val defns = nodeMap.values.toSet val inital = Map[Int, Set[Defn]]() val joinPoints = defns.map(d => (d.defn.id -> discoverJoinPoints(d.defn.body, Set()))).toMap val allJoinPoints = joinPoints.values.flatMap(x => x).toSet val edges = defns.foldLeft(inital)((acc, defn) => discoverCalls(defn.defn, joinPoints(defn.defn.id))(using acc)).withDefaultValue(Set()) var ctr = 0 // nodes, edges var stack: List[DefnNode] = Nil var sccs: List[DefnGraph] = Nil def dfs(src: DefnNode): Unit = src.num = ctr src.lowest = ctr ctr += 1 src.visited = true val tailCalls = edges(src.defn.id) stack = src :: stack for u <- tailCalls do val neighbour = nodeMap(u.id) if (neighbour.visited) then if (!neighbour.processed) src.lowest = neighbour.num.min(src.lowest) else dfs(neighbour) src.lowest = neighbour.lowest.min(src.lowest) src.processed = true if (src.num == src.lowest) then var scc: Set[DefnNode] = Set() def pop(): DefnNode = val ret = stack.head stack = stack.tail ret var vertex = pop() while (vertex != src) { scc = scc + vertex val next = pop() vertex = next } scc = scc + vertex val sccIds = scc.map { d => d.defn.id } val sccJoinPoints = scc.foldLeft(Set[Defn]())((jps, defn) => joinPoints(defn.defn.id)) val sccDefns = scc.map(d => d.defn) val categorizedEdges = scc .foldLeft(Set[CallInfo]())( (calls, defn) => discoverOptCalls(defn.defn, joinPoints(defn.defn.id))(using sccDefns, calls) ) .filter(c => sccDefns.contains(c.getDefn)) sccs = DefnGraph(scc, categorizedEdges, sccJoinPoints) :: sccs for v <- defns do if !allJoinPoints.contains(v.defn) && !v.visited then dfs(v) sccs private case class DefnInfo(defn: Defn, stackFrameIdx: Int) def asLit(x: Int) = Expr.Literal(IntLit(x)) private def makeSwitch(scrutName: Name, cases: List[(Int, Node)], default: Node)(implicit trueClass: ClassInfo, falseClass: ClassInfo): Node = // given expressions value, e1, e2, transform it into // let scrut = tailrecBranch == value // in case scrut of True -> e1 // False -> e2 def makeCaseBranch(value: Int, e1: Node, e2: Node): Node = val name = Name("scrut") val cases = Case(name, List((Pat.Class(ClassRef(L(trueClass))), e1), (Pat.Class(ClassRef(L(falseClass))), e2)), None).attachTag(tag) LetExpr( name, Expr.BasicOp("==", List(asLit(value), Expr.Ref(scrutName))), cases ).attachTag(tag) cases.foldLeft(default)((elz, item) => val cmpValue = item._1 val nodeIfTrue = item._2 makeCaseBranch(cmpValue, nodeIfTrue, elz) ) // TAIL RECURSION MOD CONS // Uses the ideas in section 2.2 of the paper `Tail Recursion Modulo Context` // by Leijen and Lorenzen: https://dl.acm.org/doi/abs/10.1145/3571233 // of whom attribute the method to Risch, Friedman, Wise, Minamide. final val ID_CONTEXT_NAME = "_IdContext" final val CONTEXT_NAME = "_Context" // `ctx` class for tailrec mod cons. // The paper uses two values `res: T` and `hole: ptr` to represent the context. // We represent the context as three values instead of two to avoid needing pointers: // // acc: The accumulated value. This is the same as `res` in the paper. If the functions f1, ..., fn // in the compoennt return type T1, ..., Tn, then acc has type T1 | ... | Tn. // // The following together represent `hole` in the paper: // ptr: Represents the object containing the "hole" to be written to. // field: Integer representing which class and field the "hole" belongs to. Which class and field this // represents is different for each strongly connected component. // // The idea to use `ptr` and `field` to represent a pointer is by @LPTK. final val ID_CTX_CLASS = ClassInfo(classUid.make, ID_CONTEXT_NAME, Nil) final val CTX_CLASS = ClassInfo(classUid.make, CONTEXT_NAME, List("acc", "ptr", "field")) final val ID_CTX_CLASS_REF = ClassRef(L(ID_CTX_CLASS)) final val CTX_CLASS_REF = ClassRef(L(CTX_CLASS)) // Given a strongly connected component `defns` of mutually // tail recursive functions, returns a strongly connected component contaning the // optimized functions and their associated join points, and also // new function definitions not in this component, such as the // original functions pointing to an optimized function and the context // composition and application functions. private def optimizeModCons(component: ScComponent, classes: Set[ClassInfo]): (ScComponent, Set[Defn]) = val modConsCalls = component.edges.collect { case x: ModConsCallInfo => x } val defns = component.nodes val defnsIdSet = defns.map(_.id).toSet // no mod cons, just return the original if modConsCalls.isEmpty then (component, Set()) else val trueClass = classes.find(c => c.name == "True").get val falseClass = classes.find(c => c.name == "False").get // CONTEXT APPLICATION val mergedNames = defns.foldLeft("")(_ + "_" + _.name) val ctxAppId = fnUid.make val ctxAppName = mergedNames + "_ctx_app$" + ctxAppId val ctxCompId = fnUid.make val ctxCompName = mergedNames + "_ctx_comp$" + ctxCompId // map integers to classes and fields which will be assigned to val classIdMap = classes.map(c => c.id -> c).toMap val possibleAssigns = modConsCalls.map(call => (call.letCtorNode.cls.id, call.letCtorNode.fieldName)).toSet val possibleAssignsIdxes = possibleAssigns.toList.zipWithIndex val assignToIdx = possibleAssignsIdxes.map((item, idx) => item -> idx).toMap // fun app(ctx, x: T): T val appCtxName = Name("ctx") val appValName = Name("x") val assignmentCases = possibleAssignsIdxes.map((item, idx) => val clsId = item._1 val fieldName = item._2 val cls = classIdMap(clsId) // let ptr = ctx.ptr in // ptr. = x in // let acc = ctx.acc // acc val node = LetExpr( Name("ptr"), Expr.Select(appCtxName, CTX_CLASS_REF, "ptr"), LetExpr( Name("_"), Expr.AssignField( Name("ptr"), ClassRef(L(cls)), fieldName, Expr.Ref(appValName) ), LetExpr( Name("acc"), Expr.Select(appCtxName, CTX_CLASS_REF, "acc"), // this could be a join point but it's not that bad Result( List(Expr.Ref(Name("acc"))) ).attachTag(tag) ).attachTag(tag) ).attachTag(tag) ).attachTag(tag) (idx, node) ) val ctxBranch = LetExpr( Name("field"), Expr.Select(appCtxName, CTX_CLASS_REF, "field"), makeSwitch(Name("field"), assignmentCases.tail, assignmentCases.head._2)(using trueClass, falseClass) ).attachTag(tag) val idBranch = Result(List(Expr.Ref(appValName))).attachTag(tag) val appNode = Case(appCtxName, List( (Pat.Class(ID_CTX_CLASS_REF), idBranch), (Pat.Class(CTX_CLASS_REF), ctxBranch) ), None ).attachTag(tag) val appDefn = Defn(ctxAppId, ctxAppName, List(appCtxName, appValName), 1, appNode, false) // CONTEXT COMPOSITION val cmpCtx1Name = Name("ctx1") val cmpCtx2Name = Name("ctx2") // Note that ctx2 may never be an identity context. If we ever want to compose ctx1 and ctx2 // where ctx2 is the identity, just use ctx1 directly. // Ctx(app(ctx1, ctx2), ctx2.ptr, ctx2.field) -> // let ctx2acc = ctx2.acc in // let ctx2ptr = ctx2.ptr in // let ctx2field = ctx2.field in // let newAcc = app(ctx1, ctx2acc) in // let ret = Ctx(newAcc, ctx2ptr, ctx2field) in // ret val cmpNode = LetExpr( Name("ctx2acc"), Expr.Select(cmpCtx2Name, CTX_CLASS_REF, "acc"), LetExpr( Name("ctx2ptr"), Expr.Select(cmpCtx2Name, CTX_CLASS_REF, "ptr"), LetExpr( Name("ctx2field"), Expr.Select(cmpCtx2Name, CTX_CLASS_REF, "field"), LetCall( List(Name("newAcc")), DefnRef(Left(appDefn)), List(Expr.Ref(cmpCtx1Name), Expr.Ref(Name("ctx2acc"))), false, LetExpr( Name("ret"), Expr.CtorApp(CTX_CLASS_REF, List("newAcc", "ctx2ptr", "ctx2field").map(n => Expr.Ref(Name(n)))), Result( List(Expr.Ref(Name("ret"))) ).attachTag(tag) ).attachTag(tag), )().attachTag(tag) ).attachTag(tag) ).attachTag(tag) ).attachTag(tag) val cmpDefn = Defn(ctxCompId, ctxCompName, List(cmpCtx1Name, cmpCtx2Name), 1, cmpNode, false) // We use tags to identify nodes // a bit hacky but it's the most elegant way // First, build a map of all branches that contain a mod cons call val modConsBranches = modConsCalls.toList.map(call => (call.startNode.tag.inner -> call)).toMap val modConsRefs = defns.map(d => d.id -> DefnRef(Right(d.name + "_modcons"))).toMap val jpRefs = component.joinPoints.map(jp => jp.id -> DefnRef(Right(jp.name + "_modcons"))).toMap def makeRet(ret: TrivialExpr): Node = LetCall( List(Name("res")), DefnRef(Left(appDefn)), List(Expr.Ref(Name("ctx")), ret), false, Result(List(Expr.Ref(Name("res")))).attachTag(tag) )().attachTag(tag) // Here, we assume we are inside the modcons version of the function and hence have an extra // `ctx` parameter at the start. def transformNode(node: Node): Node = modConsBranches.get(node.tag.inner) match case Some(call) => transformModConsBranch(node)(using call) case None => node match case Result(res) => makeRet(res.head) case Jump(defn, args) => if isIdentityJp(defn.expectDefn) then makeRet(args.head) else jpRefs.get(defn.expectDefn.id) match case None => throw IRError("could not find jump point with id" + defn.expectDefn.id) case Some(value) => Jump(value, Expr.Ref(Name("ctx")) :: args) case Case(scrut, cases, default) => Case(scrut, cases.map { (cls, body) => (cls, transformNode(body)) }, default.map(transformNode)).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names, cls, method, args, transformNode(body)).attachTag(tag) case LetCall(names, defn, args, isTailRec, body) => // Handle the case when we see a tail call. // This case is not handled by the paper. The way to transform this is: // let res = foo(*args) in res // --> let res = foo_modcons(ctx, *args) in res if isTailCall(node) && defnsIdSet.contains(defn.expectDefn.id) then // Transform it into a tail recursive call where we pass on the current context LetCall( List(Name("res")), modConsRefs(defn.expectDefn.id), Expr.Ref(Name("ctx")) :: args, isTailRec, Result(List(Expr.Ref(Name("res")))).attachTag(tag) )().attachTag(tag) else LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) def transformModConsBranch(node: Node)(implicit call: ModConsCallInfo): Node = def makeCall = val field = assignToIdx((call.letCtorNode.cls.id, call.letCtorNode.fieldName)) // let composed = comp(ctx, Ctx(retVal, ptr, field)) in // f(composed, *args) LetExpr( Name("ctx2"), Expr.CtorApp(CTX_CLASS_REF, List(Expr.Ref(call.retName), Expr.Ref(call.letCtorNode.ctorValName), asLit(field))), LetCall( List(Name("composed")), DefnRef(Left(cmpDefn)), List("ctx", "ctx2").map(n => Expr.Ref(Name(n))), false, LetCall( List(Name("res")), modConsRefs(call.defn.id), Expr.Ref(Name("composed")) :: call.letCallNode.args, false, Result( List(Expr.Ref(Name("res"))) ).attachTag(tag) )().attachTag(tag) )().attachTag(tag) ).attachTag(tag) node match case Result(res) if node.tag.inner == call.retNode.tag.inner => makeCall case Jump(defn, args) if node.tag.inner == call.retNode.tag.inner => makeCall case LetExpr(name, expr, body) => if node.tag.inner == call.letCtorNode.node.tag.inner then // rewrite the ctor, but set the field containing the call as to 0 val idx = call.letCtorNode.idx val argsList = call.letCtorNode.ctor.args.updated(idx, asLit(0)) LetExpr(name, Expr.CtorApp(ClassRef(L(call.letCtorNode.cls)), argsList), transformModConsBranch(body)).attachTag(tag) else LetExpr(name, expr, transformModConsBranch(body)).attachTag(tag) case LetCall(names, defn, args, isTailRec, body) => if node.tag.inner == call.letCallNode.tag.inner then // discard it transformModConsBranch(body) else LetCall(names, defn, args, isTailRec, transformModConsBranch(body))().attachTag(tag) case _ => throw IRError("unreachable case when transforming mod cons call") def rewriteDefn(d: Defn): Defn = val transformed = transformNode(d.body) val id = fnUid.make Defn(id, d.name + "_modcons$" + id, Name("ctx") :: d.params, d.resultNum, transformed, d.isTailRec) // returns (new defn, mod cons defn) // where new defn has the same signature and ids as the original, but immediately calls the mod cons defn // and mod cons defn is the rewritten definition def replaceDefn(d: Defn): (Defn, Defn) = val modConsDefn = rewriteDefn(d) val modConsCall = LetExpr( Name("idCtx"), Expr.CtorApp(ID_CTX_CLASS_REF, Nil), LetCall( List(Name("res")), DefnRef(Left(modConsDefn)), Expr.Ref(Name("idCtx")) :: d.params.map(Expr.Ref(_)), false, Result(List(Expr.Ref(Name("res")))).attachTag(tag) )().attachTag(tag) ).attachTag(tag) val newDefn = Defn(d.id, d.name, d.params, d.resultNum, modConsCall, false) (newDefn, modConsDefn) val jpsTransformed = component.joinPoints.map(d => d.id -> rewriteDefn(d)).toMap val defnsTransformed = component.nodes.map(d => d.id -> replaceDefn(d)).toMap // update defn refs for (id, ref) <- jpRefs do ref.defn = Left(jpsTransformed(id)) for (id, ref) <- modConsRefs do ref.defn = Left(defnsTransformed(id)._2) // set it to the mod cons defn, not the one with the original signature val jps = jpsTransformed.values.toSet val modConsDefs = defnsTransformed.values.map((a, b) => b).toSet val normalDefs = defnsTransformed.values.map((a, b) => a).toSet + appDefn + cmpDefn // the edges are not used later, but still, rewrite them for correctness val newEdges = component.edges.map { c => val src = c.getSrc val defn = c.getDefn TailCallInfo(defnsTransformed(src.id)._2, defnsTransformed(defn.id)._2) } (ScComponent(modConsDefs, newEdges, jps), normalDefs) // Given a strongly connected component `defns` of mutually // tail recursive functions, returns a set containing the optimized function and the // original functions pointing to an optimized function. // Explicitly returns the merged function in case tailrec needs to be checked. private def optimizeTailRec(component: ScComponent, classes: Set[ClassInfo]): (Set[Defn], Defn) = // To build the case block, we need to compare integers and check if the result is "True" val trueClass = classes.find(c => c.name == "True").get val falseClass = classes.find(c => c.name == "False").get // undefined for dummy values val dummyVal = Expr.Literal(UnitLit(true)) // join points need to be rewritten. For now, just combine them into the rest of the function. They will be inlined anyways val defns = component.nodes ++ component.joinPoints val defnsNoJp = component.nodes val edges = component.edges // dummy case, should not happen if (defns.size == 0) throw IRError("strongly connected component was empty") // for single tail recursive functions, just move the body into a join point if (defns.size <= 1) val defn = defns.head // if the function does not even tail call itself, just return if filterOptCalls(edges).size == 0 then return (defns, defns.head) val jpName = defn.name + "_jp" val jpDefnRef = DefnRef(Right(jpName)) def transformNode(node: Node): Node = node match case Result(res) => node.attachTag(tag) case Jump(defn, args) => node.attachTag(tag) case Case(scrut, cases, default) => Case(scrut, cases.map((cls, body) => (cls, transformNode(body))), default.map(transformNode)).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names, cls, method, args, transformNode(body)).attachTag(tag) case LetCall(names, defn_, args, isTailRec, body) => if isTailCall(node) && defn_.expectDefn.id == defn.id then Jump(jpDefnRef, args).attachTag(tag) else LetCall(names, defn_, args, isTailRec, transformNode(body))().attachTag(tag) val jpDef = Defn(fnUid.make, jpName, defn.params, defn.resultNum, transformNode(defn.body), false) val rets = (0 until defn.resultNum).map(n => Name("r" + n.toString)).toList val callJpNode = LetCall( rets, DefnRef(Left(jpDef)), defn.params.map(Expr.Ref(_)), false, Result(rets.map(Expr.Ref(_))).attachTag(tag), )().attachTag(tag) val newDefn = Defn(fnUid.make, defn.name, defn.params, defn.resultNum, callJpNode, true) (Set(newDefn, jpDef), newDefn) else // Note that we do not use the actual edges in ScCompoennt here. // We assume the only things we can optimize are tail calls, which // are cheap to identify, and nothing else. // concretely order the functions as soon as possible, since the order of the functions matter val defnsList = defns.toList // assume all defns have the same number of results // in fact, they should theoretically have the same return type if the program type checked val resultNum = defnsList.head.resultNum val trName = Name("tailrecBranch$"); // To be used to replace variable names inside a definition to avoid variable name clashes val nameMaps: Map[Int, Map[Name, Name]] = defnsList.map(defn => defn.id -> defn.params.map(n => n -> Name(defn.name + "_" + n.str)).toMap).toMap val stackFrameIdxes = defnsList.foldLeft(1 :: Nil)((ls, defn) => defn.params.size + ls.head :: ls).drop(1).reverse val defnInfoMap: Map[Int, DefnInfo] = (defnsList zip stackFrameIdxes) .foldLeft(Map.empty)((map, item) => map + (item._1.id -> DefnInfo(item._1, item._2))) val stackFrame = trName :: defnsList.flatMap(d => d.params.map(n => nameMaps(d.id)(n))) // take union of stack frames val newId = fnUid.make val newName = defns.foldLeft("")(_ + "_" + _.name) + "_opt$" + newId val jpId = fnUid.make val jpName = defns.foldLeft("")(_ + "_" + _.name) + "_opt_jp$" + jpId val newDefnRef = DefnRef(Right(newName)) val jpDefnRef = DefnRef(Right(jpName)) def transformStackFrame(args: List[TrivialExpr], info: DefnInfo) = val start = stackFrame.take(info.stackFrameIdx).drop(1).map { Expr.Ref(_) } // we drop tailrecBranch and replace it with the defn id val end = stackFrame.drop(info.stackFrameIdx + args.size).map { Expr.Ref(_) } asLit(info.defn.id) :: start ::: args ::: end // Build the node which will be contained inside the jump point. def transformNode(node: Node): Node = node match case Jump(defn, args) => if defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else node.attachTag(tag) case Result(_) => node.attachTag(tag) case Case(scrut, cases, default) => Case(scrut, cases.map(n => (n._1, transformNode(n._2))), default.map(transformNode)).attachTag(tag) case LetExpr(name, expr, body) => LetExpr(name, expr, transformNode(body)).attachTag(tag) case LetMethodCall(names, cls, method, args, body) => LetMethodCall(names, cls, method, args, transformNode(body)).attachTag(tag) case LetCall(names, defn, args, isTailRec, body) => if isTailCall(node) && defnInfoMap.contains(defn.expectDefn.id) then Jump(jpDefnRef, transformStackFrame(args, defnInfoMap(defn.expectDefn.id))).attachTag(tag) else LetCall(names, defn, args, isTailRec, transformNode(body))().attachTag(tag) // Tail calls to another function in the component will be replaced with a call // to the merged function // i.e. for mutually tailrec functions f(a, b) and g(c, d), // f's body will be replaced with a call f_g(a, b, *, *), where * is a dummy value def transformDefn(defn: Defn): Defn = val info = defnInfoMap(defn.id) val start = stackFrame.take(info.stackFrameIdx).drop(1).map { _ => dummyVal } // we drop tailrecBranch and replace it with the defn id val end = stackFrame.drop(info.stackFrameIdx + defn.params.size).map { _ => dummyVal } val args = asLit(info.defn.id) :: start ::: defn.params.map(Expr.Ref(_)) ::: end // We use a let call instead of a jump to avoid newDefn from being turned into a join point, // which would cause it to be inlined and result in code duplication. val names = (0 until resultNum).map(i => Name("r" + i.toString())).toList val namesExpr = names.map(Expr.Ref(_)) val res = Result(namesExpr).attachTag(tag) val call = LetCall(names, newDefnRef, args, false, res)().attachTag(tag) Defn(defn.id, defn.name, defn.params, defn.resultNum, call, false) def getOrKey[T](m: Map[T, T])(key: T): T = m.get(key) match case None => key case Some(value) => value val first = defnsList.head; val firstMap = nameMaps(first.id) val firstBodyRenamed = first.body.mapName(getOrKey(firstMap)) val firstNode = transformNode(firstBodyRenamed) val valsAndNodes = defnsList.map(defn => val nmeMap = nameMaps(defn.id) val renamed = defn.body.mapName(getOrKey(nmeMap)) val transformed = transformNode(renamed) (defn.id, transformed) ) val newNode = makeSwitch(trName, valsAndNodes.tail, valsAndNodes.head._2)(using trueClass, falseClass) val jpDefn = Defn(jpId, jpName, stackFrame, resultNum, newNode, false) val jmp = Jump(jpDefnRef, stackFrame.map(Expr.Ref(_))).attachTag(tag) val newDefn = Defn(newId, newName, stackFrame, resultNum, jmp, defnsNoJp.find { _.isTailRec }.isDefined ) jpDefnRef.defn = Left(jpDefn) newDefnRef.defn = Left(newDefn) (defnsNoJp.map { d => transformDefn(d) } + newDefn + jpDefn, newDefn) private def partition(defns: Set[Defn]): List[ScComponent] = val nodeMap: Map[Int, DefnNode] = defns.foldLeft(Map.empty)((m, d) => m + (d.id -> DefnNode(d))) partitionNodes(using nodeMap).map(_.removeMetadata) private def optimizeParition(component: ScComponent, classes: Set[ClassInfo]): Set[Defn] = val trFn = component.nodes.find { _.isTailRec }.headOption val normalCall = filterNormalCalls(component.edges).headOption (trFn, normalCall) match case (Some(fn), Some(call)) => raise(ErrorReport( List( msg"function `${fn.name}` is not tail-recursive, but is marked as @tailrec" -> fn.loc, msg"it could self-recurse through this call, which may not be a tail-call" -> call.loc ), true, Diagnostic.Compilation) ) case _ => val (modConsComp, other) = optimizeModCons(component, classes) val (trOpt, mergedDefn) = optimizeTailRec(modConsComp, classes) other ++ trOpt def apply(p: Program) = run(p) def run_debug(p: Program): (Program, List[Set[String]]) = val partitions = partition(p.defs) val newDefs = partitions.flatMap { optimizeParition(_, p.classes) }.toSet val newClasses = p.classes + ID_CTX_CLASS + CTX_CLASS // update the definition refs newDefs.foreach { defn => resolveRef(defn.body, newDefs, newClasses, true) } resolveRef(p.main, newDefs, newClasses, true) (Program(newClasses, newDefs, p.main), partitions.map(t => t.nodes.map(f => f.name))) def run(p: Program): Program = run_debug(p)._1 ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/printer/BlockPrinter.scala ================================================ package mlscript.compiler.printer import scala.collection.mutable.{ArrayBuffer, Stack, StringBuilder} class BlockPrinter: import BlockPrinter.Indentation private val indent = Indentation() private val lines = ArrayBuffer[String]() private val currentLine = StringBuilder() private val scopes = Stack[Option[String]]() def endLine(): Unit = if currentLine.isEmpty then () else { lines += indent(currentLine.toString) currentLine.clear() } def enter(): Unit = endLine() indent.increase() scopes.push(None) def enter(begin: String, end: String): Unit = currentLine ++= begin endLine() indent.increase() scopes.push(Some(end)) def leave(): Unit = endLine() // ↵ indent.decrease() scopes.pop().foreach { currentLine ++= _ } endLine() // ↵ def print(content: String): Unit = currentLine ++= content def toLines: List[String] = endLine() lines.toList override def toString(): String = lines.mkString("\n") object BlockPrinter: class Indentation(mark: String = " "): private var indent = 0 private var spaces = ArrayBuffer[String]("") def increase(): Unit = indent += 1 spaces += spaces.last + mark def decrease(): Unit = indent -= 1 def apply(content: String): String = spaces(indent) + content ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/simpledef/Simpledef.scala ================================================ package mlscript package compiler package simpledef import mlscript.utils.*, shorthands.* import scala.collection.mutable import java.util.IdentityHashMap import scala.jdk.CollectionConverters.* type TypeVar type TermId = Uid[Term] type TypeVarId = Uid[TypeVar] type Cnstr = ProdStrat -> ConsStrat /** Performs defunctionalization on selections on objects using simple-sub as for control-flow analysis. * First we traverse the program and process all terms, constraining them to Producers and Consumers. * During the constraining, we keep track of the input points of selection terms. * Lastly, we rewrite selection terms by generating pattern matches on their possible inputs. */ enum ProdStrat(using val euid: TermId) { case NoProd()(using TermId) case ProdObj(ctor: Option[Var], fields: Ls[Var -> ProdStrat], parents: Ls[ProdStrat] = Nil)(using TermId) extends ProdStrat, ProdObjImpl case ProdFun(lhs: ConsStrat, rhs: ProdStrat)(using TermId) case ProdVar(uid: TypeVarId, name: String)(boundary: Option[Var] = None)(using TermId) case ProdTup(fields: Ls[ProdStrat])(using TermId) } enum ConsStrat(using val euid: TermId) { case NoCons()(using TermId) case ConsObj(name: Option[Var], fields: Ls[Var -> ConsStrat])(using TermId) extends ConsStrat, ConsObjImpl case ConsFun(lhs: ProdStrat, rhs: ConsStrat)(using TermId) case ConsVar(uid: TypeVarId, name: String)(boundary: Option[Var] = None)(using TermId) case ConsTup(fields: Ls[ConsStrat])(using TermId) } import ProdStrat.*, ConsStrat.* trait ConsObjImpl { self: ConsObj => var selectionSource: Set[ProdStrat] = Set() } trait ProdObjImpl { self: ProdObj => var objDestination: Set[ConsStrat] = Set() } class Context( variables: Map[Var, ProdVar], classes: Map[Var, ProdObj], ) { def apply(v: Var): ProdVar = variables(v) def ++(other: IterableOnce[(Var, ProdVar)]): Context = Context(variables ++ other, classes) def +(other: (Var -> ProdVar)): Context = Context(variables + other, classes) override def toString(): String = s"${variables}" } class SimpleDef(debug: Debug) { extension (t: Term) { def uid = termMap.getOrElse(t, { val id = euid.nextUid termMap.addOne((t, euid.nextUid)) id }) } val termMap = new IdentityHashMap[Term, TermId]().asScala val varsName = mutable.Map.empty[TypeVarId, Str] val vuid = Uid.TypeVar.State() val euid = Uid.Term.State() val noExprId = euid.nextUid def freshVar(n: String)(using TermId): (ProdVar, ConsVar) = val vid = vuid.nextUid val pv = ProdVar(vid, n)() val cv = ConsVar(vid, n)() varsName += vid -> n (pv, cv) def freshVar(n: Var)(using TermId): (ProdVar, ConsVar) = freshVar(n.name) def apply(p: TypingUnit)(using ctx: Context = Context(Map(), Map())): (Ls[Var -> ProdStrat], ProdStrat) = // Top-level def prototypes val vars: Map[Var, ProdVar] = p.rawEntities.collect { case fun: NuFunDef => fun.nme -> freshVar(fun.name)(using noExprId)._1 }.toMap // Top-level constructor prototypes val constructorPrototypes: Map[Var, Cnstr] = p.rawEntities.collect { case ty: NuTypeDef => ty.nameVar -> freshVar(ty.nameVar)(using noExprId) }.toMap // Prototypes of constructor outputs, used for inheritance val objectPrototypes: Map[Var, Cnstr] = p.rawEntities.collect { case ty: NuTypeDef => ty.nameVar -> freshVar(ty.nameVar)(using noExprId) }.toMap val fullCtx = ctx ++ vars ++ constructorPrototypes.map((v, s) => (v, s._1.asInstanceOf[ProdVar])) val classes: Map[Var, ProdObj] = p.rawEntities.collect { case ty: NuTypeDef => debug.writeLine(s"Completing type info for class ${ty.nameVar} with ctors ${constructorPrototypes.map((v, s) => (v, s._1))}") given TermId = noExprId val argTup = ty.params.getOrElse(Tup(Nil)) val (pList, cList) = argTup.fields.map{ case (Some(v), Fld(flags, _)) if flags.genGetter => val fldVar = freshVar(s"${argTup.uid}_${v.name}")(using noExprId) ((v, fldVar._1), (v, fldVar._2)) case (Some(v), Fld(flags, _)) if !flags.genGetter => lastWords(s"Non val ${v} class parameter is not supported.") case other => lastWords(s"${other} class parameter is not supported.") }.unzip val bodyStrats = apply(ty.body)(using fullCtx ++ pList.toMap) val parents = ty.parents.map{ case v: Var => objectPrototypes(v)._1 case App(v: Var, _) => objectPrototypes(v)._1 case other => lastWords(s"Unsupported inheritance pattern ${other}") } if parents.length > 1 then lastWords(s"Multiple Inheritance at ${ty} not supported yet") ty.kind match case Cls => ty.ctor match case None => val obj = ProdObj(Some(ty.nameVar), bodyStrats._1 ++ pList, parents) val func = ProdFun(ConsTup(cList.map(_._2)),obj) constrain(func, constructorPrototypes(ty.nameVar)._2) constrain(obj, objectPrototypes(ty.nameVar)._2) ty.nameVar -> obj case Some(Constructor(t @ Tup(params), body)) => val mapping = params.map{ case (None, Fld(_, v: Var)) => (v, freshVar(s"${t.uid}_${v.name}")(using noExprId)) case (Some(v1: Var), Fld(_, v2: Var)) => (v1, freshVar(s"${t.uid}_${v1.name}")(using noExprId)) case other => lastWords(s"Unsupported term ${other}") } process(body)(using fullCtx ++ pList.toMap ++ mapping.map((i, s) => (i, s._1))) val obj = ProdObj(Some(ty.nameVar), bodyStrats._1 ++ pList, parents) val func = ProdFun(ConsTup(mapping.map(_._2._2)),obj) constrain(func, constructorPrototypes(ty.nameVar)._2) constrain(obj, objectPrototypes(ty.nameVar)._2) ty.nameVar -> obj case Mod => val obj = ProdObj(Some(ty.nameVar), bodyStrats._1 ++ pList, parents) constrain(obj, constructorPrototypes(ty.nameVar)._2) constrain(obj, objectPrototypes(ty.nameVar)._2) ty.nameVar -> obj case other => lastWords(s"Unsupported class kind ${other}") }.toMap val tys = p.rawEntities.flatMap{ case f: NuFunDef => { f.rhs match case Left(value) => val p = process(value)(using fullCtx) val v = vars(f.nme) constrain(p, ConsVar(v.uid, v.name)()(using noExprId)) Some(p) case Right(value) => None } case t: Term => { val topLevelProd = process(t)(using fullCtx) Some(topLevelProd) } case other => { debug.writeLine(s"Skipping ${other}") None } } (vars.toList, tys.lastOption.getOrElse(ProdObj(Some(Var("prim$Unit")), Nil)(using noExprId))) val termToProdType = mutable.Map.empty[TermId, ProdStrat] // Selection terms -> Object types that they Consume val selTermToType = mutable.Map.empty[TermId, ConsObj] def builtinOps: Map[Var, ProdFun] = { given TermId = noExprId Map( (Var("+") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Int")), Nil))), (Var("-") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Int")), Nil))), (Var("*") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Int")), Nil))), (Var(">") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Bool")), Nil))), (Var("==") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil),ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$Bool")), Nil))), (Var("is") -> ProdFun(ConsTup(List(freshVar("is$rhs")._2,freshVar("is$lhs")._2)), ProdObj(Some(Var("prim$Bool")), Nil))), (Var("concat") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$String")), Nil))), ProdFun(ConsTup(List(ConsObj(Some(Var("prim$String")), Nil))), ProdObj(Some(Var("prim$String")), Nil)))), (Var("log") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$String")), Nil))), ProdObj(Some(Var("prim$Unit")), Nil))), (Var("toString") -> ProdFun(ConsTup(List(ConsObj(Some(Var("prim$Int")), Nil))), ProdObj(Some(Var("prim$String")), Nil))) ) } def process(t: Term)(using ctx: Context): ProdStrat = debug.writeLine(s"Processing term ${t}") debug.indent() val res: ProdStrat = t match case IntLit(_) => ProdObj(Some(Var("prim$Int")), Nil)(using t.uid) case StrLit(_) => ProdObj(Some(Var("prim$String")), Nil)(using t.uid) case UnitLit(_) => ProdObj(Some(Var("prim$Unit")), Nil)(using t.uid) case Var("true") | Var("false") => ProdObj(Some(Var("prim$Bool")), Nil)(using t.uid) case v @ Var(id) if builtinOps.contains(v) => builtinOps(v) case v @ Var(id) => ctx(v).copy()(Some(v))(using t.uid) case Asc(trm, ty) => // TODO: Enforce type ascription? process(trm) case Let(isRec, nme, rhs, body) => val rhsRes = process(rhs) val sv = freshVar(s"${t.uid}_let")(using t.uid) constrain(rhsRes, sv._2) process(body)(using ctx + (nme -> sv._1)) case NuNew(cls) => process(App(NuNew(cls), Tup(Nil).withLoc(t.toLoc.map(_.right)))) case App(NuNew(cls), arg) => val clsRes = process(cls) val argRes = process(arg) val sv = freshVar(s"${t.uid}_callres")(using t.uid) constrain(clsRes, ConsFun(argRes, sv._2)(using noExprId)) sv._1 case App(func, arg) => val funcRes = process(func) val argRes = process(arg) val sv = freshVar(s"${t.uid}_callres")(using t.uid) constrain(funcRes, ConsFun(argRes, sv._2)(using noExprId)) sv._1 case Lam(t @ Tup(args), body) => val mapping = args.map{ case (None, Fld(_, v: Var)) => (v, freshVar(s"${t.uid}_${v.name}")(using noExprId)) case (Some(v1: Var), Fld(_, v2: Var)) => (v1, freshVar(s"${t.uid}_${v1.name}")(using noExprId)) case other => lastWords(s"Unsupported term ${other}") } ProdFun(ConsTup(mapping.map(_._2._2))(using t.uid), process(body)(using ctx ++ mapping.map((i, s) => (i, s._1))))(using t.uid) case If(IfThen(scrut, thenn), S(elze)) => constrain(process(scrut), ConsObj(Some(Var("prim$Bool")), Nil)(using noExprId)) val res = freshVar(s"${t.uid}_ifres")(using t.uid) constrain(process(thenn), res._2) constrain(process(elze), res._2) res._1 case elf: If => elf.desugaredTerm match { case S(desugared) => process(desugared) case N => lastWords(s"Undesugared UCS term ${t} found") } case Tup(fields) => val mapping = fields.map{ case (None, Fld(_, fieldTerm: Term)) => process(fieldTerm) case other => lastWords(s"Unsupported tuple structure ${other}") } ProdTup(mapping)(using t.uid) case Sel(receiver, fieldName) => val selRes = freshVar(s"${t.uid}_selres")(using t.uid) val selector = ConsObj(None, List(fieldName -> selRes._2))(using t.uid) constrain(process(receiver), selector) selTermToType += (t.uid -> selector.asInstanceOf[ConsObj]) selRes._1 case Bra(true, t) => process(t) case Rcd(fields) => ProdObj(None, fields.map{ case (v, Fld(_, t)) => (v -> process(t)) })(using t.uid) case Blk(stmts) => apply(TypingUnit(stmts))._2 case Bra(false, term) => process(term) case CaseOf(trm, cases) => // TODO: Complete constraining in conjunction with processCases ??? case Eqn(lhs, rhs) => process(lhs) process(rhs) case other => lastWords(s"Unsupported term ${other}") debug.outdent() registerTermToType(t, res) // TODO: Complete constraining for CaseBranches after implementing negative types and intersections def processCases(scrut: ProdVar, cs: CaseBranches)(using ctx: Context, resCons: ConsVar): Unit = cs match case Wildcard(body) => constrain(process(body), resCons) case NoCases => () case Case(pat, body, rest) => ??? val constraintCache = mutable.Set.empty[(ProdStrat, ConsStrat)] val upperBounds = mutable.Map.empty[TypeVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = mutable.Map.empty[TypeVarId, Ls[ProdStrat]].withDefaultValue(Nil) def constrain(prod: ProdStrat, cons: ConsStrat): Unit = { debug.writeLine(s"constraining ${prod} -> ${cons}") if (constraintCache.contains(prod -> cons)) return () else constraintCache += (prod -> cons) (prod, cons) match case (ProdVar(v, pn), ConsVar(w, cn)) if v === w => () case (pv@ProdVar(v, _), _) => cons match { case c: ConsObj if lowerBounds(v).isEmpty => c.selectionSource = c.selectionSource + pv case _ => () } upperBounds += v -> (cons :: upperBounds(v)) lowerBounds(v).foreach(lb_strat => constrain(lb_strat, cons)) case (_, cv@ConsVar(v, _)) => prod match { case p: ProdObj if upperBounds(v).isEmpty => p.objDestination = p.objDestination + cv case _ => () } lowerBounds += v -> (prod :: lowerBounds(v)) upperBounds(v).foreach(ub_strat => constrain(prod, ub_strat)) case (ProdFun(lhs1, rhs1), ConsFun(lhs2, rhs2)) => constrain(lhs2, lhs1) constrain(rhs1, rhs2) case (pt@ProdTup(fields1), ct@ConsTup(fields2)) => if pt.fields.length != ct.fields.length then lastWords("Tuple size mismatch") (fields1 zip fields2).map((p, c) => constrain(p, c) ) case (pv@ProdObj(nme1, fields1, parents), cv@ConsObj(nme2, fields2)) => nme2 match case Some(name) if name != nme1.get => lastWords(s"Could not constrain ${(prod -> cons)}") case _ => () fields2.map((key, res2) => { fields1.find(_._1 == key) match case None => debug.writeLine("field not found, try parent") // TODO: Handle multiple inheritance properly, currently assume only one parent parents match case head :: next => constrain(head, cv) case Nil => lastWords(s"Could not constrain ${(prod -> cons)}") case Some((_, res1)) => cv.selectionSource = cv.selectionSource + pv pv.objDestination = pv.objDestination + cv constrain(res1, res2) }) case (pv@ProdTup(fields1), cv@ConsObj(nme2, fields2)) => nme2 match case Some(name) => lastWords(s"Could not constrain ${(prod -> cons)}") case _ => () fields2.map((key, res2) => { cv.selectionSource = cv.selectionSource + pv constrain(fields1(key.name.toInt), res2) }) case other => lastWords(s"Could not constrain ${other}") } // Selection terms -> Producers that they consume lazy val selToResTypes: Map[TermId, Set[ProdStrat]] = selTermToType.map((termId, cons) => (termId, cons.selectionSource) ).toMap // Rewrite terms, replacing selections with pattern matches if they only select on objects def rewriteTerm(t: Term): Term = def objSetToMatchBranches(receiver: Var, fieldName: Var, objSet: List[ProdObj], acc: CaseBranches = NoCases)(using funcApp: Option[Term] = None): CaseBranches = objSet match case Nil => acc case (p :: rest) if p.ctor.isDefined => if funcApp.isDefined then objSetToMatchBranches(receiver, fieldName, rest, Case(p.ctor.get, App(Sel(receiver, fieldName), funcApp.get), acc)(false)) else objSetToMatchBranches(receiver, fieldName, rest, Case(p.ctor.get, Sel(receiver, fieldName), acc)(false)) case other => lastWords(s"Unexpected ${other}") t match case Var(_) | IntLit(_) | UnitLit(_) | StrLit(_) => t // TODO: Remove the following case when eta expansion is supported, currently a workaround. case App(t @ Sel(receiver, fieldName), arg) => if (selToResTypes(t.uid).forall{ case _: ProdVar => true case x: ProdObj if x.ctor.isDefined => true case _ => false }) { val letName = Var(s"selRes$$${t.uid}") Let(false, letName, rewriteTerm(receiver), CaseOf(letName, objSetToMatchBranches(letName, fieldName, selToResTypes(t.uid).collect{case x: ProdObj => x}.toList)(using Some(rewriteTerm(arg)))) ) } else { debug.writeLine(s"${selToResTypes(t.uid)}") Sel(rewriteTerm(receiver), fieldName) } case App(func, arg) => App(rewriteTerm(func), rewriteTerm(arg)) case Lam(t @ Tup(args), body) => Lam(rewriteTerm(t), rewriteTerm(body)) case If(IfThen(scrut, thenn), S(elze)) => If(IfThen(rewriteTerm(scrut), rewriteTerm(thenn)), S(rewriteTerm(elze))) case Tup(fields) => Tup(fields.map{ case (x, Fld(flags, fieldTerm: Term)) => (x, Fld(flags, rewriteTerm(fieldTerm))) }) case Sel(receiver, fieldName) => if (selToResTypes(t.uid).forall{ case _: ProdVar => true case x: ProdObj if x.ctor.isDefined => true case _ => false }) { val letName = Var(s"selRes$$${t.uid}") Let(false, letName, rewriteTerm(receiver), CaseOf(letName, objSetToMatchBranches(letName, fieldName, selToResTypes(t.uid).collect{case x: ProdObj => x}.toList)) ) } else { debug.writeLine(s"${selToResTypes(t.uid)}") Sel(rewriteTerm(receiver), fieldName) } case Bra(true, t) => Bra(true, rewriteTerm(t)) case Rcd(fields) => Rcd(fields.map{ case (v, Fld(flags, t)) => (v, Fld(flags, rewriteTerm(t))) }) case Blk(stmts) => Blk(rewriteStatements(stmts)) case Bra(false, term) => Bra(false, rewriteTerm(term)) case NuNew(cls) => NuNew(rewriteTerm(cls)) case other => lastWords(s"Unsupported term ${other}") def rewriteStatements(stmts: List[Statement]): List[Statement] = stmts.map{ case ty: NuTypeDef => ty.copy(body = rewriteProgram(ty.body))(ty.declareLoc, ty.abstractLoc, ty.annotations) case f: NuFunDef => f.copy(rhs = f.rhs match case Left(t) => Left(rewriteTerm(t)) case Right(_) => f.rhs )(f.declareLoc, f.virtualLoc, f.mutLoc, f.signature, f.outer, f.genField, f.annotations) case t: Term => rewriteTerm(t) case other => lastWords(s"Unsupported term ${other}") } def rewriteProgram(t: TypingUnit): TypingUnit = TypingUnit(rewriteStatements(t.rawEntities)) private def registerTermToType(t: Term, s: ProdStrat) = { termToProdType.get(t.uid) match { case None => { termToProdType += t.uid -> s s } case Some(value) => lastWords(s"${t} registered two prod strategies:\n already has ${value}, but got ${s}") } } } ================================================ FILE: compiler/shared/main/scala/mlscript/compiler/simpledef/Uid.scala ================================================ package mlscript package compiler package simpledef opaque type Uid[T] = Int object Uid: class Handler[T]: class State: private val uidStore = scala.collection.mutable.Map.empty[String, Uid[T]] def nextUid: Uid[T] = nextUid("") def nextUid(key: String): Uid[T] = uidStore.updateWith(key) { case None => Some(0) case Some(v) => Some(v + 1) }.get object TypeVar extends Handler[TypeVar] object Term extends Handler[Term] ================================================ FILE: compiler/shared/test/diff/CompilerScratch.mls ================================================ :NewDefs ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ClassConstructor.mls ================================================ :NewDefs class X { val num = 5 } class Y(val num: Int) { constructor(y: Int) { num = y+5 } } let y = new Y(6) let x = new X (if true then y else x).num //│ class X { //│ constructor() //│ val num: 5 //│ } //│ class Y(num: Int) { //│ constructor(y: Int) //│ } //│ let y: Y //│ let x: X //│ Int //│ //│ Simpledef: //│ {class X {let num = 5} //│ class Y(val num: Int,) {} //│ let y = (new Y)(6,) //│ let x = new X //│ let selRes$40 = '(' if (true) then y else x ')' in case selRes$40 of { Y => (selRes$40).num //│ X => (selRes$40).num }} //│ End simpledef //│ //│ y //│ = Y {} //│ x //│ = X {} //│ res //│ = 11 class Z(val num1: Int) { constructor(y, x) { num1 = y+x } } class W(val num1: Int, val num2: Int) { constructor(w) { num1 = w num2 = w } } val w = new W(3) val z = new Z(6, 11) (if true then w else z).num1 //│ class Z(num1: Int) { //│ constructor(y: Int, x: Int) //│ } //│ class W(num1: Int, num2: Int) { //│ constructor(w: Int) //│ } //│ val w: W //│ val z: Z //│ Int //│ //│ Simpledef: //│ {class Z(val num1: Int,) {} //│ class W(val num1: Int, val num2: Int,) {} //│ let w = (new W)(3,) //│ let z = (new Z)(6, 11,) //│ let selRes$58 = '(' if (true) then w else z ')' in case selRes$58 of { W => (selRes$58).num1 //│ Z => (selRes$58).num1 }} //│ End simpledef //│ //│ w //│ = W {} //│ z //│ = Z {} //│ res //│ = 3 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Classes.mls ================================================ :NewDefs class Bar(val x: Int) { fun foo(x) = x fun FooMinus(y: Int) = x + y fun car = foo(2) } class Car() { fun da(b: Bar) = b.foo(2) } fun baz(b: Bar) = b.foo(2) let bar = Bar(42) baz(bar) (Car()).da(Bar(1337)) bar.car //│ class Bar(x: Int) { //│ fun FooMinus: (y: Int) -> Int //│ fun car: 2 //│ fun foo: forall 'a. 'a -> 'a //│ } //│ class Car() { //│ fun da: (b: Bar) -> 2 //│ } //│ fun baz: (b: Bar) -> 2 //│ let bar: Bar //│ 2 //│ //│ Simpledef: //│ {class Bar(val x: Int,) {fun foo = (x::0,) => x //│ fun FooMinus = (y: Int,) => +(x, y,) //│ fun car = foo(2,)} //│ class Car() {fun da = (b: Bar,) => let selRes$34 = b in case selRes$34 of { Bar => (selRes$34).foo(2,) }} //│ fun baz = (b: Bar,) => let selRes$48 = b in case selRes$48 of { Bar => (selRes$48).foo(2,) } //│ let bar = Bar(42,) //│ baz(bar,) //│ let selRes$76 = '(' Car() ')' in case selRes$76 of { Car => (selRes$76).da(Bar(1337,),) } //│ let selRes$98 = bar in case selRes$98 of { Bar => (selRes$98).car }} //│ End simpledef //│ //│ bar //│ = Bar {} //│ res //│ = 2 //│ res //│ = 2 //│ res //│ = 2 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ClosureCapture.mls ================================================ :NewDefs :AllowRuntimeErrors fun foo(x) = (f => f(x))(z => z+1) foo(2) //│ fun foo: Int -> Int //│ Int //│ //│ Simpledef: //│ {fun foo = (x::0,) => {'(' (f::1,) => f(x,) ')'((z::2,) => +(z, 1,),)} //│ foo(2,)} //│ End simpledef //│ //│ res //│ = 3 fun f(x) = (y => f(x+y))(x+1) f(1) //│ fun f: Int -> nothing //│ nothing //│ //│ Simpledef: //│ {fun f = (x::3,) => {'(' (y::4,) => f(+(x, y,),) ')'(+(x, 1,),)} //│ f(1,)} //│ End simpledef //│ //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Constructor.mls ================================================ :NewDefs class X() { val a = log("ok") 6 } val object = X() (new X()).a (new X()).a object.a object.a //│ class X() { //│ val a: 6 //│ } //│ val object: X //│ 6 //│ //│ Simpledef: //│ {class X() {let a = {log("ok",) //│ 6}} //│ let object = X() //│ let selRes$20 = '(' (new X)() ')' in case selRes$20 of { X => (selRes$20).a } //│ let selRes$30 = '(' (new X)() ')' in case selRes$30 of { X => (selRes$30).a } //│ let selRes$40 = object in case selRes$40 of { X => (selRes$40).a } //│ let selRes$44 = object in case selRes$44 of { X => (selRes$44).a }} //│ End simpledef //│ //│ object //│ = X {} //│ // Output //│ ok //│ res //│ = 6 //│ // Output //│ ok //│ res //│ = 6 //│ // Output //│ ok //│ res //│ = 6 //│ res //│ = 6 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/DelayedEvaluation.mls ================================================ :NewDefs class G(val num: Int) { val x = log("once on construction") num+2 val y() = log("once every call") num+2 } val g = new G(6) g.y() g.x g.y() g.x //│ class G(num: Int) { //│ val x: Int //│ val y: () -> Int //│ } //│ val g: G //│ Int //│ //│ Simpledef: //│ {class G(val num: Int,) {let x = {log("once on construction",) //│ +(num, 2,)} //│ let y = () => {log("once every call",) //│ +(num, 2,)}} //│ let g = (new G)(6,) //│ let selRes$56 = g in case selRes$56 of { G => (selRes$56).y() } //│ let selRes$64 = g in case selRes$64 of { G => (selRes$64).x } //│ let selRes$68 = g in case selRes$68 of { G => (selRes$68).y() } //│ let selRes$76 = g in case selRes$76 of { G => (selRes$76).x }} //│ End simpledef //│ //│ g //│ = G {} //│ // Output //│ once on construction //│ res //│ = 8 //│ // Output //│ once every call //│ res //│ = 8 //│ res //│ = 8 //│ // Output //│ once every call //│ res //│ = 8 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Differentiation.mls ================================================ // A usecase test for defunctionalization :NewDefs abstract class Exp() { virtual fun derive(): Exp virtual fun getVal: Str } class Numeric(val i: Int) extends Exp { fun derive() = Numeric(0) fun getNum = i fun getVal = toString(i) } class Variable(val nm: Str) extends Exp { fun derive() = Numeric(1) fun getVal = nm } class Sum(val lhs: Exp, val rhs: Exp) extends Exp { fun derive() = Sum(lhs.derive(), rhs.derive()) fun getVal = concat("(")(concat(concat(concat(lhs.getVal)(" + "))(rhs.getVal))(")")) } class Mul(val lhs: Exp, val rhs: Exp) extends Exp { fun derive() = Sum(Mul(lhs.derive(), rhs), Mul(lhs, rhs.derive())) fun getVal = concat("(")(concat(concat(concat(lhs.getVal)(" * "))(rhs.getVal))(")")) } class Pow(val lhs: Variable, val rhs: Numeric) extends Exp { fun derive() = Mul(rhs, Pow(lhs, Numeric(rhs.getNum - 1))) fun getVal = concat("(")(concat(concat(concat(lhs.getVal)(" ^ "))(rhs.getVal))(")")) } Sum(Variable("x"), Numeric(3)).derive().getVal Mul(Variable("x"), Numeric(3)).derive().getVal Pow(Variable("x"), Numeric(3)).derive().getVal Mul(Pow(Variable("x"), Numeric(2)), Pow(Variable("y"), Numeric(2))).derive().getVal //│ abstract class Exp() { //│ fun derive: () -> Exp //│ fun getVal: Str //│ } //│ class Numeric(i: Int) extends Exp { //│ fun derive: () -> Numeric //│ fun getNum: Int //│ fun getVal: Str //│ } //│ class Variable(nm: Str) extends Exp { //│ fun derive: () -> Numeric //│ fun getVal: Str //│ } //│ class Sum(lhs: Exp, rhs: Exp) extends Exp { //│ fun derive: () -> Sum //│ fun getVal: Str //│ } //│ class Mul(lhs: Exp, rhs: Exp) extends Exp { //│ fun derive: () -> Sum //│ fun getVal: Str //│ } //│ class Pow(lhs: Variable, rhs: Numeric) extends Exp { //│ fun derive: () -> Mul //│ fun getVal: Str //│ } //│ Str //│ //│ Simpledef: //│ {class Exp() {fun derive: () -> Exp //│ fun getVal: Str} //│ class Numeric(val i: Int,): Exp {fun derive = () => Numeric(0,) //│ fun getNum = i //│ fun getVal = toString(i,)} //│ class Variable(val nm: Str,): Exp {fun derive = () => Numeric(1,) //│ fun getVal = nm} //│ class Sum(val lhs: Exp, val rhs: Exp,): Exp {fun derive = () => Sum(let selRes$48 = lhs in case selRes$48 of { Variable => (selRes$48).derive() //│ Mul => (selRes$48).derive() //│ Sum => (selRes$48).derive() //│ Numeric => (selRes$48).derive() }, let selRes$56 = rhs in case selRes$56 of { Numeric => (selRes$56).derive() //│ Sum => (selRes$56).derive() //│ Mul => (selRes$56).derive() },) //│ fun getVal = concat("(",)(concat(concat(concat(let selRes$84 = lhs in case selRes$84 of { Variable => (selRes$84).getVal //│ Mul => (selRes$84).getVal //│ Sum => (selRes$84).getVal //│ Numeric => (selRes$84).getVal },)(" + ",),)(let selRes$102 = rhs in case selRes$102 of { Numeric => (selRes$102).getVal //│ Sum => (selRes$102).getVal //│ Mul => (selRes$102).getVal },),)(")",),)} //│ class Mul(val lhs: Exp, val rhs: Exp,): Exp {fun derive = () => Sum(Mul(let selRes$132 = lhs in case selRes$132 of { Variable => (selRes$132).derive() //│ Mul => (selRes$132).derive() //│ Sum => (selRes$132).derive() //│ Numeric => (selRes$132).derive() //│ Pow => (selRes$132).derive() }, rhs,), Mul(lhs, let selRes$150 = rhs in case selRes$150 of { Mul => (selRes$150).derive() //│ Sum => (selRes$150).derive() //│ Numeric => (selRes$150).derive() //│ Pow => (selRes$150).derive() },),) //│ fun getVal = concat("(",)(concat(concat(concat(let selRes$182 = lhs in case selRes$182 of { Variable => (selRes$182).getVal //│ Mul => (selRes$182).getVal //│ Sum => (selRes$182).getVal //│ Numeric => (selRes$182).getVal //│ Pow => (selRes$182).getVal },)(" * ",),)(let selRes$200 = rhs in case selRes$200 of { Mul => (selRes$200).getVal //│ Sum => (selRes$200).getVal //│ Numeric => (selRes$200).getVal //│ Pow => (selRes$200).getVal },),)(")",),)} //│ class Pow(val lhs: Variable, val rhs: Numeric,): Exp {fun derive = () => Mul(rhs, Pow(lhs, Numeric(-(let selRes$238 = rhs in case selRes$238 of { Numeric => (selRes$238).getNum }, 1,),),),) //│ fun getVal = concat("(",)(concat(concat(concat(let selRes$276 = lhs in case selRes$276 of { Variable => (selRes$276).getVal },)(" ^ ",),)(let selRes$294 = rhs in case selRes$294 of { Numeric => (selRes$294).getVal },),)(")",),)} //│ let selRes$316 = let selRes$318 = Sum(Variable("x",), Numeric(3,),) in case selRes$318 of { Sum => (selRes$318).derive() } in case selRes$316 of { Sum => (selRes$316).getVal } //│ let selRes$346 = let selRes$348 = Mul(Variable("x",), Numeric(3,),) in case selRes$348 of { Mul => (selRes$348).derive() } in case selRes$346 of { Sum => (selRes$346).getVal } //│ let selRes$376 = let selRes$378 = Pow(Variable("x",), Numeric(3,),) in case selRes$378 of { Pow => (selRes$378).derive() } in case selRes$376 of { Mul => (selRes$376).getVal } //│ let selRes$406 = let selRes$408 = Mul(Pow(Variable("x",), Numeric(2,),), Pow(Variable("y",), Numeric(2,),),) in case selRes$408 of { Mul => (selRes$408).derive() } in case selRes$406 of { Sum => (selRes$406).getVal }} //│ End simpledef //│ //│ res //│ = '(1 + 0)' //│ res //│ = '((1 * 3) + (x * 0))' //│ res //│ = '(3 * (x ^ 2))' //│ res //│ = '(((2 * (x ^ 1)) * (y ^ 2)) + ((x ^ 2) * (2 * (y ^ 1))))' ================================================ FILE: compiler/shared/test/diff/Defunctionalize/FreeVariables.mls ================================================ :NewDefs class X() { val num = 5 fun get() = num } X().get() //│ class X() { //│ fun get: () -> 5 //│ val num: 5 //│ } //│ 5 //│ //│ Simpledef: //│ {class X() {let num = 5 //│ fun get = () => num} //│ let selRes$10 = X() in case selRes$10 of { X => (selRes$10).get() }} //│ End simpledef //│ //│ res //│ = 5 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/FuncsWithParams.mls ================================================ :NewDefs abstract class Arithmetic() { virtual fun use(num1: Int, num2: Int): Int } class Add() extends Arithmetic { fun use(num1, num2) = num1+num2 } class Sub() extends Arithmetic { fun use(num1, num2) = num1-num2 } fun getArith(choice) = if choice == 1 then Add() else Sub() getArith(1).use(4,6) getArith(2).use(4,6) //│ abstract class Arithmetic() { //│ fun use: (num1: Int, num2: Int) -> Int //│ } //│ class Add() extends Arithmetic { //│ fun use: (Int, Int) -> Int //│ } //│ class Sub() extends Arithmetic { //│ fun use: (Int, Int) -> Int //│ } //│ fun getArith: Num -> (Add | Sub) //│ Int //│ //│ Simpledef: //│ {class Arithmetic() {fun use: (num1: Int, num2: Int) -> Int} //│ class Add(): Arithmetic {fun use = (num1::0, num2::1,) => +(num1, num2,)} //│ class Sub(): Arithmetic {fun use = (num1::2, num2::3,) => -(num1, num2,)} //│ fun getArith = (choice::4,) => if (==(choice, 1,)) then Add() else Sub() //│ let selRes$58 = getArith(1,) in case selRes$58 of { Sub => (selRes$58).use(4, 6,) //│ Add => (selRes$58).use(4, 6,) } //│ let selRes$76 = getArith(2,) in case selRes$76 of { Sub => (selRes$76).use(4, 6,) //│ Add => (selRes$76).use(4, 6,) }} //│ End simpledef //│ //│ res //│ = 10 //│ res //│ = -2 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Inheritance.mls ================================================ :NewDefs // FIXME: Pattern matches on superclass instead of subclass class Sup() { fun add(num1, num2) = num1+num2 } class Sub1() extends Sup() {} class Sub2() extends Sub1() {} Sub1().add(3,4) Sub2().add(5,6) //│ class Sup() { //│ fun add: (Int, Int) -> Int //│ } //│ class Sub1() extends Sup { //│ fun add: (Int, Int) -> Int //│ } //│ class Sub2() extends Sub1, Sup { //│ fun add: (Int, Int) -> Int //│ } //│ Int //│ //│ Simpledef: //│ {class Sup() {fun add = (num1::0, num2::1,) => +(num1, num2,)} //│ class Sub1(): Sup() {} //│ class Sub2(): Sub1() {} //│ let selRes$16 = Sub1() in case selRes$16 of { Sup => (selRes$16).add(3, 4,) } //│ let selRes$32 = Sub2() in case selRes$32 of { Sup => (selRes$32).add(5, 6,) }} //│ End simpledef //│ //│ res //│ = 7 //│ res //│ = 11 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Lambda.mls ================================================ :NewDefs class X(val foo: Int => Int){} class Y(val foo: Int => Bool){} fun x(pred) = if pred then X(x => x+1) else Y(x => true) x(true).foo(5) //│ class X(foo: Int -> Int) //│ class Y(foo: Int -> Bool) //│ fun x: Bool -> (X | Y) //│ Int | false | true //│ //│ Simpledef: //│ {class X(val foo: (Int,) => Int,) {} //│ class Y(val foo: (Int,) => Bool,) {} //│ fun x = (pred::0,) => if (pred) then X((x::1,) => +(x, 1,),) else Y((x::2,) => true,) //│ let selRes$46 = x(true,) in case selRes$46 of { Y => (selRes$46).foo(5,) //│ X => (selRes$46).foo(5,) }} //│ End simpledef //│ //│ res //│ = 6 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Lambdas.mls ================================================ :NewDefs ((f, g) => f(g))(f => f, true) //│ true //│ //│ Simpledef: //│ {'(' (f::0, g::1,) => f(g,) ')'((f::2,) => f, true,)} //│ End simpledef //│ //│ res //│ = true (b => if b then true else false) (true) //│ Bool //│ //│ Simpledef: //│ {'(' (b::3,) => if (b) then true else false ')'(true,)} //│ End simpledef //│ //│ res //│ = true ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ListConstruction.mls ================================================ :NewDefs abstract class List() { fun getRes: Str fun map: error } class Cons(val x: Int, val xs: List) extends List { fun getRes = concat(concat(toString(x))(" :: "))(xs.getRes) fun map(f) = Cons(f(x), xs.map(f)) } class Nil() extends List { fun getRes = "Nil" fun map(f) = Nil() } fun mkList(len) = if len == 0 then Nil() else Cons(len, mkList(len-1)) mkList(5).map(x => x*2).getRes //│ abstract class List() { //│ fun getRes: Str //│ fun map: error //│ } //│ class Cons(x: Int, xs: List) extends List { //│ fun getRes: Str //│ fun map: (Int -> Int) -> Cons //│ } //│ class Nil() extends List { //│ fun getRes: "Nil" //│ fun map: anything -> Nil //│ } //│ fun mkList: Int -> (Cons | Nil) //│ Str //│ //│ Simpledef: //│ {class List() {fun getRes: Str //│ fun map: error} //│ class Cons(val x: Int, val xs: List,): List {fun getRes = concat(concat(toString(x,),)(" :: ",),)(let selRes$30 = xs in case selRes$30 of { Nil => (selRes$30).getRes //│ Cons => (selRes$30).getRes },) //│ fun map = (f::0,) => Cons(f(x,), let selRes$50 = xs in case selRes$50 of { Nil => (selRes$50).map(f,) //│ Cons => (selRes$50).map(f,) },)} //│ class Nil(): List {fun getRes = "Nil" //│ fun map = (f::1,) => Nil()} //│ fun mkList = (len::2,) => {if (==(len, 0,)) then Nil() else Cons(len, mkList(-(len, 1,),),)} //│ let selRes$126 = let selRes$128 = mkList(5,) in case selRes$128 of { Cons => (selRes$128).map((x::3,) => *(x, 2,),) //│ Nil => (selRes$128).map((x::3,) => *(x, 2,),) } in case selRes$126 of { Cons => (selRes$126).getRes //│ Nil => (selRes$126).getRes }} //│ End simpledef //│ //│ res //│ = '10 :: 8 :: 6 :: 4 :: 2 :: Nil' ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Modules.mls ================================================ :NewDefs class Foo() {fun f = 0} module x { val y = Foo() } x.y.f //│ class Foo() { //│ fun f: 0 //│ } //│ module x { //│ val y: Foo //│ } //│ 0 //│ //│ Simpledef: //│ {class Foo() {fun f = 0} //│ module x {let y = Foo()} //│ let selRes$10 = let selRes$12 = x in case selRes$12 of { x => (selRes$12).y } in case selRes$10 of { Foo => (selRes$10).f }} //│ End simpledef //│ //│ res //│ = 0 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/MonoNonLambda.mls ================================================ :NewDefs class A() { val x = 2 val y() = 3 fun z = 4 fun w() = 5 } val a = A() a.x a.y() a.z a.w() //│ class A() { //│ fun w: () -> 5 //│ val x: 2 //│ val y: () -> 3 //│ fun z: 4 //│ } //│ val a: A //│ 5 //│ //│ Simpledef: //│ {class A() {let x = 2 //│ let y = () => 3 //│ fun z = 4 //│ fun w = () => 5} //│ let a = A() //│ let selRes$24 = a in case selRes$24 of { A => (selRes$24).x } //│ let selRes$28 = a in case selRes$28 of { A => (selRes$28).y() } //│ let selRes$36 = a in case selRes$36 of { A => (selRes$36).z } //│ let selRes$40 = a in case selRes$40 of { A => (selRes$40).w() }} //│ End simpledef //│ //│ a //│ = A {} //│ res //│ = 2 //│ res //│ = 3 //│ res //│ = 4 //│ res //│ = 5 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/MonoTupSelect.mls ================================================ :NewDefs class Foo() {fun f() = 0} class Bar() {fun f = 0} [Foo(), Bar()].0.f() [Foo(), Bar()].1.f //│ class Foo() { //│ fun f: () -> 0 //│ } //│ class Bar() { //│ fun f: 0 //│ } //│ 0 //│ //│ Simpledef: //│ {class Foo() {fun f = () => 0} //│ class Bar() {fun f = 0} //│ let selRes$10 = ([Foo(), Bar(),]).0 in case selRes$10 of { Foo => (selRes$10).f() } //│ let selRes$32 = ([Foo(), Bar(),]).1 in case selRes$32 of { Bar => (selRes$32).f }} //│ End simpledef //│ //│ res //│ = 0 //│ res //│ = 0 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/MutableParams.mls ================================================ :NewDefs // TODO: Mutable Parameters //class Bar(#x) //fun foo(#b) = b //let a = foo(new Bar(1)) //let b = foo(new Bar(2)) //class OneInt(#a){ // fun inc() = a+1 //} //(new OneInt(1)).inc() //class OneInt(#a){ // fun add(x) = // new OneInt(a+x.a) //} //(new OneInt(1)).add(new OneInt(2)) //trait AnyFoo { //} //class FooPlus(#a): AnyFoo { // fun bar(b) = a + b //} //class FooMinus(#a): AnyFoo { // fun bar(b) = a - b //} //fun f(x) = x.bar(42) //f(new FooPlus(1)) //f(new FooMinus(2)) ================================================ FILE: compiler/shared/test/diff/Defunctionalize/MutualRec.mls ================================================ :NewDefs :AllowRuntimeErrors val any = -20 fun f(x) = if x > any then 0 else g(x - 1) fun g(x) = if x > any then g(x - 1) else f(x - 2) g(1) //│ val any: -20 //│ fun f: Int -> 0 //│ fun g: Int -> 0 //│ 0 //│ //│ Simpledef: //│ {let any = -20 //│ fun f = (x::0,) => {if (>(x, any,)) then 0 else g(-(x, 1,),)} //│ fun g = (x::1,) => {if (>(x, any,)) then g(-(x, 1,),) else f(-(x, 2,),)} //│ g(1,)} //│ End simpledef //│ //│ any //│ = -20 //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: compiler/shared/test/diff/Defunctionalize/NewOperator.mls ================================================ :NewDefs class Foo(val x: Int) { } new Foo(5).x //│ class Foo(x: Int) //│ Int //│ //│ Simpledef: //│ {class Foo(val x: Int,) {} //│ let selRes$4 = (new Foo)(5,) in case selRes$4 of { Foo => (selRes$4).x }} //│ End simpledef //│ //│ res //│ = 5 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/NuMono.mls ================================================ :NewDefs // old "new" syntax //class A() { // val num() = 0 //} //class B() { // val num() = 1 //} //fun foo(num: Int) = if false then new A() else new B() //foo(10).num() class A() { val num() = 0 } class B() { val num() = 1 } fun foo(num: Int) = if num > 5 then A() else B() foo(10).num() //│ class A() { //│ val num: () -> 0 //│ } //│ class B() { //│ val num: () -> 1 //│ } //│ fun foo: (num: Int) -> (A | B) //│ 0 | 1 //│ //│ Simpledef: //│ {class A() {let num = () => 0} //│ class B() {let num = () => 1} //│ fun foo = (num: Int,) => if (>(num, 5,)) then A() else B() //│ let selRes$42 = foo(10,) in case selRes$42 of { B => (selRes$42).num() //│ A => (selRes$42).num() }} //│ End simpledef //│ //│ res //│ = 0 class A(val num1: Int, val num2: Int) { fun foo() = num1-num2 } class B(val num1: Int, val num2: Int) { fun foo() = num1+num2 } fun foo(num: Int) = if num > 5 then A(10,6) else B(8,7) foo(10).foo() foo(0).foo() //│ class A(num1: Int, num2: Int) { //│ fun foo: () -> Int //│ } //│ class B(num1: Int, num2: Int) { //│ fun foo: () -> Int //│ } //│ fun foo: (num: Int) -> (A | B) //│ Int //│ //│ Simpledef: //│ {class A(val num1: Int, val num2: Int,) {fun foo = () => -(num1, num2,)} //│ class B(val num1: Int, val num2: Int,) {fun foo = () => +(num1, num2,)} //│ fun foo = (num: Int,) => if (>(num, 5,)) then A(10, 6,) else B(8, 7,) //│ let selRes$70 = foo(10,) in case selRes$70 of { B => (selRes$70).foo() //│ A => (selRes$70).foo() } //│ let selRes$84 = foo(0,) in case selRes$84 of { B => (selRes$84).foo() //│ A => (selRes$84).foo() }} //│ End simpledef //│ //│ res //│ = 4 //│ res //│ = 15 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ObjFieldAccess.mls ================================================ :NewDefs class A(val i: Int) { fun get1() = i fun get2 = i } val a = A(6) a.get1() a.get2 //│ class A(i: Int) { //│ fun get1: () -> Int //│ fun get2: Int //│ } //│ val a: A //│ Int //│ //│ Simpledef: //│ {class A(val i: Int,) {fun get1 = () => i //│ fun get2 = i} //│ let a = A(6,) //│ let selRes$20 = a in case selRes$20 of { A => (selRes$20).get1() } //│ let selRes$28 = a in case selRes$28 of { A => (selRes$28).get2 }} //│ End simpledef //│ //│ a //│ = A {} //│ res //│ = 6 //│ res //│ = 6 class A(val i: Str) { fun get1() = i fun get2 = i } val a = A("6") a.get1() a.get2 //│ class A(i: Str) { //│ fun get1: () -> Str //│ fun get2: Str //│ } //│ val a: A //│ Str //│ //│ Simpledef: //│ {class A(val i: Str,) {fun get1 = () => i //│ fun get2 = i} //│ let a = A("6",) //│ let selRes$20 = a in case selRes$20 of { A => (selRes$20).get1() } //│ let selRes$28 = a in case selRes$28 of { A => (selRes$28).get2 }} //│ End simpledef //│ //│ a //│ = A {} //│ res //│ = '6' //│ res //│ = '6' class X() class Y(val foo: X) { fun get1() = foo fun get2 = foo } val a = Y(X()) a.get1() a.get2 //│ class X() //│ class Y(foo: X) { //│ fun get1: () -> X //│ fun get2: X //│ } //│ val a: Y //│ X //│ //│ Simpledef: //│ {class X() {} //│ class Y(val foo: X,) {fun get1 = () => foo //│ fun get2 = foo} //│ let a = Y(X(),) //│ let selRes$24 = a in case selRes$24 of { Y => (selRes$24).get1() } //│ let selRes$32 = a in case selRes$32 of { Y => (selRes$32).get2 }} //│ End simpledef //│ //│ a //│ = Y {} //│ res //│ = X {} //│ res //│ = X {} class I() {} class J() {} class K(val foo: I, val bar: J) { fun getFoo = foo fun getBar = bar } val k = K(I(), J()) k.getFoo k.getBar //│ class I() //│ class J() //│ class K(foo: I, bar: J) { //│ fun getBar: J //│ fun getFoo: I //│ } //│ val k: K //│ J //│ //│ Simpledef: //│ {class I() {} //│ class J() {} //│ class K(val foo: I, val bar: J,) {fun getFoo = foo //│ fun getBar = bar} //│ let k = K(I(), J(),) //│ let selRes$26 = k in case selRes$26 of { K => (selRes$26).getFoo } //│ let selRes$30 = k in case selRes$30 of { K => (selRes$30).getBar }} //│ End simpledef //│ //│ k //│ = K {} //│ res //│ = I {} //│ res //│ = J {} ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ObjFields.mls ================================================ :NewDefs class X(val bar: Int) {} class Y(val bar: Str) {} class A(val foo: X) {} class B(val foo: Y) {} fun getObj(pred) = if pred then A(X(1)) else B(Y("abc")) val x = getObj(true) x.foo.bar //│ class X(bar: Int) //│ class Y(bar: Str) //│ class A(foo: X) //│ class B(foo: Y) //│ fun getObj: Bool -> (A | B) //│ val x: A | B //│ Int | Str //│ //│ Simpledef: //│ {class X(val bar: Int,) {} //│ class Y(val bar: Str,) {} //│ class A(val foo: X,) {} //│ class B(val foo: Y,) {} //│ fun getObj = (pred::0,) => if (pred) then A(X(1,),) else B(Y("abc",),) //│ let x = getObj(true,) //│ let selRes$54 = let selRes$56 = x in case selRes$56 of { A => (selRes$56).foo //│ B => (selRes$56).foo } in case selRes$54 of { Y => (selRes$54).bar //│ X => (selRes$54).bar }} //│ End simpledef //│ //│ x //│ = A {} //│ res //│ = 1 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ObjMultiFields.mls ================================================ :NewDefs class X(val foo: Int, val bar: Bool) {} class Y(val foo: Str, val bar: Int) {} class A(val foo: X) {} class B(val foo: Y) {} fun foo(pred) = if pred then A(X(1, false)) else B(Y("abc", 5)) val x = foo(true) x.foo.bar foo(false).foo.bar //│ class X(foo: Int, bar: Bool) //│ class Y(foo: Str, bar: Int) //│ class A(foo: X) //│ class B(foo: Y) //│ fun foo: Bool -> (A | B) //│ val x: A | B //│ Int | false | true //│ //│ Simpledef: //│ {class X(val foo: Int, val bar: Bool,) {} //│ class Y(val foo: Str, val bar: Int,) {} //│ class A(val foo: X,) {} //│ class B(val foo: Y,) {} //│ fun foo = (pred::0,) => if (pred) then A(X(1, false,),) else B(Y("abc", 5,),) //│ let x = foo(true,) //│ let selRes$58 = let selRes$60 = x in case selRes$60 of { A => (selRes$60).foo //│ B => (selRes$60).foo } in case selRes$58 of { Y => (selRes$58).bar //│ X => (selRes$58).bar } //│ let selRes$64 = let selRes$66 = foo(false,) in case selRes$66 of { B => (selRes$66).foo //│ A => (selRes$66).foo } in case selRes$64 of { X => (selRes$64).bar //│ Y => (selRes$64).bar }} //│ End simpledef //│ //│ x //│ = A {} //│ res //│ = false //│ res //│ = 5 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/ObjsSelection.mls ================================================ :NewDefs class X() { val num = 6 } class Y() { val num = true } fun foo(pred) = if pred then X() else Y() fun id(x) = x val a = foo(true) val b = id(a) b.num //│ class X() { //│ val num: 6 //│ } //│ class Y() { //│ val num: true //│ } //│ fun foo: Bool -> (X | Y) //│ fun id: forall 'a. 'a -> 'a //│ val a: X | Y //│ val b: X | Y //│ 6 | true //│ //│ Simpledef: //│ {class X() {let num = 6} //│ class Y() {let num = true} //│ fun foo = (pred::0,) => if (pred) then X() else Y() //│ fun id = (x::1,) => x //│ let a = foo(true,) //│ let b = id(a,) //│ let selRes$48 = b in case selRes$48 of { Y => (selRes$48).num //│ X => (selRes$48).num }} //│ End simpledef //│ //│ a //│ = X {} //│ b //│ = X {} //│ res //│ = 6 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/OldMonoList.mls ================================================ :NewDefs class List(val e: Int, val tail: List | Nil) { fun map: (Int -> Int) -> List fun map(f)= List(f(e), tail.map(f)) fun count(): Int fun count() = 1 + tail.count() } class Nil() { fun map(f) = Nil() fun count() = 0 } fun add2(x) = x+2 (List(1, List(2, Nil()))).map(x => x+1).map(x => add2(x)) //│ class List(e: Int, tail: List | Nil) { //│ fun count: () -> Int //│ fun map: (Int -> Int) -> List //│ } //│ class Nil() { //│ fun count: () -> 0 //│ fun map: anything -> Nil //│ } //│ fun add2: Int -> Int //│ List //│ //│ Simpledef: //│ {class List(val e: Int, val tail: |(List, Nil,),) {fun map: (Int -> Int) -> List //│ fun map = (f::0,) => List(f(e,), let selRes$16 = tail in case selRes$16 of { List => (selRes$16).map(f,) //│ Nil => (selRes$16).map(f,) },) //│ fun count: () -> Int //│ fun count = () => +(1, let selRes$38 = tail in case selRes$38 of { List => (selRes$38).count() //│ Nil => (selRes$38).count() },)} //│ class Nil() {fun map = (f::1,) => Nil() //│ fun count = () => 0} //│ fun add2 = (x::2,) => +(x, 2,) //│ let selRes$82 = let selRes$84 = '(' List(1, List(2, Nil(),),) ')' in case selRes$84 of { List => (selRes$84).map((x::3,) => +(x, 1,),) } in case selRes$82 of { List => (selRes$82).map((x::4,) => add2(x,),) }} //│ End simpledef //│ //│ res //│ = List {} class List(val e: Int, val tail: List | Nil) { fun count(): Int fun count() = 1 + tail.count() } class Nil() { fun count() = 0 } fun foo(x) = x.count() fun generate(x) = if x > 0 then List(x, generate(x-1)) else Nil() foo(List(1, List(2, Nil()))) foo(generate(50)) //│ class List(e: Int, tail: List | Nil) { //│ fun count: () -> Int //│ } //│ class Nil() { //│ fun count: () -> 0 //│ } //│ fun foo: forall 'a. {count: () -> 'a} -> 'a //│ fun generate: Int -> (List | Nil) //│ Int //│ //│ Simpledef: //│ {class List(val e: Int, val tail: |(List, Nil,),) {fun count: () -> Int //│ fun count = () => +(1, let selRes$10 = tail in case selRes$10 of { List => (selRes$10).count() //│ Nil => (selRes$10).count() },)} //│ class Nil() {fun count = () => 0} //│ fun foo = (x::5,) => let selRes$32 = x in case selRes$32 of { Nil => (selRes$32).count() //│ List => (selRes$32).count() } //│ fun generate = (x::6,) => {if (>(x, 0,)) then List(x, generate(-(x, 1,),),) else Nil()} //│ foo(List(1, List(2, Nil(),),),) //│ foo(generate(50,),)} //│ End simpledef //│ //│ res //│ = 2 //│ res //│ = 50 class Cons(val e: 'A, val tail: Cons | Nil) { fun count(): Int fun count() = 1 + tail.count() } class Nil() { fun count() = 0 } class Lambda(){ fun apply(l) = l.count() } class Lambda2(val a: Int){ fun apply(l) = ( Cons(a, l)).count() } fun foo(x) = x.apply(Cons(1, Nil())) + x.apply(Nil()) foo(Lambda()) foo(Lambda2(2)) //│ class Cons(e: nothing, tail: Cons | Nil) { //│ fun count: () -> Int //│ } //│ class Nil() { //│ fun count: () -> 0 //│ } //│ class Lambda() { //│ fun apply: forall 'a. {count: () -> 'a} -> 'a //│ } //│ class Lambda2(a: Int) { //│ fun apply: (Cons | Nil) -> Int //│ } //│ fun foo: {apply: (Cons | Nil) -> Int} -> Int //│ Int //│ //│ Simpledef: //│ {class Cons(val e: 'A, val tail: |(Cons, Nil,),) {fun count: () -> Int //│ fun count = () => +(1, let selRes$10 = tail in case selRes$10 of { Cons => (selRes$10).count() //│ Nil => (selRes$10).count() },)} //│ class Nil() {fun count = () => 0} //│ class Lambda() {fun apply = (l::7,) => {let selRes$32 = l in case selRes$32 of { Cons => (selRes$32).count() //│ Nil => (selRes$32).count() }}} //│ class Lambda2(val a: Int,) {fun apply = (l::8,) => {let selRes$48 = '(' Cons(a, l,) ')' in case selRes$48 of { Cons => (selRes$48).count() }}} //│ fun foo = (x::9,) => {+(let selRes$74 = x in case selRes$74 of { Lambda2 => (selRes$74).apply(Cons(1, Nil(),),) //│ Lambda => (selRes$74).apply(Cons(1, Nil(),),) }, let selRes$96 = x in case selRes$96 of { Lambda2 => (selRes$96).apply(Nil(),) //│ Lambda => (selRes$96).apply(Nil(),) },)} //│ foo(Lambda(),) //│ foo(Lambda2(2,),)} //│ End simpledef //│ //│ res //│ = 1 //│ res //│ = 3 class Cons(val e: Int, val tail: Cons | Nil) { fun count(): Int fun count() = 1 + tail.count() } class Nil() { fun count() = 0 } fun foo(x) = x(Cons(1, Nil())) + x(Nil()) foo(l => l.count()) foo(l => (Cons(2, l)).count()) //│ class Cons(e: Int, tail: Cons | Nil) { //│ fun count: () -> Int //│ } //│ class Nil() { //│ fun count: () -> 0 //│ } //│ fun foo: ((Cons | Nil) -> Int) -> Int //│ Int //│ //│ Simpledef: //│ {class Cons(val e: Int, val tail: |(Cons, Nil,),) {fun count: () -> Int //│ fun count = () => +(1, let selRes$10 = tail in case selRes$10 of { Cons => (selRes$10).count() //│ Nil => (selRes$10).count() },)} //│ class Nil() {fun count = () => 0} //│ fun foo = (x::10,) => {+(x(Cons(1, Nil(),),), x(Nil(),),)} //│ foo((l::11,) => let selRes$78 = l in case selRes$78 of { Cons => (selRes$78).count() //│ Nil => (selRes$78).count() },) //│ foo((l::12,) => let selRes$96 = '(' Cons(2, l,) ')' in case selRes$96 of { Cons => (selRes$96).count() },)} //│ End simpledef //│ //│ res //│ = 1 //│ res //│ = 3 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Polymorphic.mls ================================================ :NewDefs let b = true class OneInt(val a: Int){ fun get = () -> a } class OneBool(val b: Bool){ fun get = () -> b } (if b then OneInt(1) else OneBool(true)).get() //│ let b: true //│ class OneInt(a: Int) { //│ fun get: () -> Int //│ } //│ class OneBool(b: Bool) { //│ fun get: () -> Bool //│ } //│ Int | false | true //│ //│ Simpledef: //│ {let b = true //│ class OneInt(val a: Int,) {fun get = () => a} //│ class OneBool(val b: Bool,) {fun get = () => b} //│ let selRes$20 = '(' if (b) then OneInt(1,) else OneBool(true,) ')' in case selRes$20 of { OneInt => (selRes$20).get() //│ OneBool => (selRes$20).get() }} //│ End simpledef //│ //│ b //│ = true //│ res //│ = 1 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Record.mls ================================================ :NewDefs class K() { val f = true } val x = { 5: "five", 7: "seven", f: 6 } fun muddle(pred) = if pred then K() else x muddle(false).f //│ class K() { //│ val f: true //│ } //│ val x: {5: "five", 7: "seven", f: 6} //│ fun muddle: Bool -> (K | {5: "five", 7: "seven", f: 6}) //│ 6 | true //│ //│ Simpledef: //│ {class K() {let f = true} //│ let x = '{' {5: "five", 7: "seven", f: 6} '}' //│ fun muddle = (pred::0,) => if (pred) then K() else x //│ (muddle(false,)).f} //│ End simpledef //│ //│ x //│ = { '5': 'five', '7': 'seven', f: 6 } //│ res //│ = 6 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/RecursiveFunc.mls ================================================ :NewDefs fun fac(n) = if (n > 1) then fac(n - 1) * n else 1 fac(5) //│ fun fac: Int -> Int //│ Int //│ //│ Simpledef: //│ {fun fac = (n::0,) => {if ('(' >(n, 1,) ')') then *(fac(-(n, 1,),), n,) else 1} //│ fac(5,)} //│ End simpledef //│ //│ res //│ = 120 // TODO: Support for specialized pattern matching types // In this example, the type of l in count(l) would need to be constrained to // object & ~(), which requires implementing neg types, intersections, etc. class List(val l: List | Nil | undefined, val hasTail: Bool) {} class Nil(val l: List | Nil | undefined, val hasTail: Bool) {} fun count(lst) = if lst.hasTail then let l = lst.l if l is undefined then 1 else count(l)+1 else 0 count(new List(new List(new Nil(undefined, false), true), true)) //│ class List(l: List | Nil | (), hasTail: Bool) //│ class Nil(l: List | Nil | (), hasTail: Bool) //│ fun count: forall 'a. 'a -> Int //│ Int //│ where //│ 'a <: {hasTail: Bool, l: Object & 'a & ~() | ()} //│ //│ Simpledef: //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Could not constrain (ProdObj(Some(Var(prim$Unit)),List(),List()),ConsObj(None,List((Var(l),ConsVar(15,13_selres))))) ================================================ FILE: compiler/shared/test/diff/Defunctionalize/SelfReference.mls ================================================ :NewDefs :AllowRuntimeErrors fun f(x) = f(x) f(0) f(1) //│ fun f: anything -> nothing //│ nothing //│ //│ Simpledef: //│ {fun f = (x::0,) => f(x,) //│ f(0,) //│ f(1,)} //│ End simpledef //│ //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: compiler/shared/test/diff/Defunctionalize/SimpleClasses.mls ================================================ :NewDefs class Foo(val x: Int){ fun bar(y) = x+y fun boo(z) = bar(z)+x } (Foo(1)).boo(2) //│ class Foo(x: Int) { //│ fun bar: Int -> Int //│ fun boo: Int -> Int //│ } //│ Int //│ //│ Simpledef: //│ {class Foo(val x: Int,) {fun bar = (y::0,) => +(x, y,) //│ fun boo = (z::1,) => +(bar(z,), x,)} //│ let selRes$38 = '(' Foo(1,) ')' in case selRes$38 of { Foo => (selRes$38).boo(2,) }} //│ End simpledef //│ //│ res //│ = 4 class OneInt(val a: Int){ fun fac: () -> Int fun fac = () -> if(a > 0) then (OneInt(a - 1)).fac() else 1 } (OneInt(10)).fac() //│ class OneInt(a: Int) { //│ fun fac: () -> Int //│ } //│ Int //│ //│ Simpledef: //│ {class OneInt(val a: Int,) {fun fac: () -> Int //│ fun fac = () => {if ('(' >(a, 0,) ')') then let selRes$20 = '(' OneInt(-(a, 1,),) ')' in case selRes$20 of { OneInt => (selRes$20).fac() } else 1}} //│ let selRes$50 = '(' OneInt(10,) ')' in case selRes$50 of { OneInt => (selRes$50).fac() }} //│ End simpledef //│ //│ res //│ = 1 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/SimpleConditionals.mls ================================================ :NewDefs if true then 1 else 0 if 1+1 > 1 then 1 - 1 else 1*1 //│ Int //│ //│ Simpledef: //│ {if (true) then 1 else 0 //│ if (>(+(1, 1,), 1,)) then -(1, 1,) else *(1, 1,)} //│ End simpledef //│ //│ res //│ = 1 //│ res //│ = 0 let b = true if(b) then 1 else 2 //│ let b: true //│ 1 | 2 //│ //│ Simpledef: //│ {let b = true //│ if ('(' b ')') then 1 else 2} //│ End simpledef //│ //│ b //│ = true //│ res //│ = 1 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/SimpleFunc.mls ================================================ :NewDefs fun f(x: Bool) = if x then 42 else 1337 //│ fun f: (x: Bool) -> (1337 | 42) //│ //│ Simpledef: //│ {fun f = (x: Bool,) => if (x) then 42 else 1337} //│ End simpledef //│ fun foo() = 42 //│ fun foo: () -> 42 //│ //│ Simpledef: //│ {fun foo = () => 42} //│ End simpledef //│ fun f(x) = if(x > 0) then x+1 else x - 1 f(2)+3 //│ fun f: Int -> Int //│ Int //│ //│ Simpledef: //│ {fun f = (x::0,) => {if ('(' >(x, 0,) ')') then +(x, 1,) else -(x, 1,)} //│ +(f(2,), 3,)} //│ End simpledef //│ //│ res //│ = 6 fun foo(x, #b) = if b then x else 1337 let a = foo(42, true) let b = foo(23, false) //│ fun foo: forall 'a. ('a, Bool) -> (1337 | 'a) //│ let a: 1337 | 42 //│ let b: 1337 | 23 //│ //│ Simpledef: //│ {fun foo = (x::1, #b::2,) => if (b) then x else 1337 //│ let a = foo(42, true,) //│ let b = foo(23, false,)} //│ End simpledef //│ //│ a //│ = 42 //│ b //│ = 1337 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/Simpledef.mls ================================================ :NewDefs class X() { val num = 6 } class Y() { val num = true } fun foo(pred) = if pred then X() else Y() foo(true).num //│ class X() { //│ val num: 6 //│ } //│ class Y() { //│ val num: true //│ } //│ fun foo: Bool -> (X | Y) //│ 6 | true //│ //│ Simpledef: //│ {class X() {let num = 6} //│ class Y() {let num = true} //│ fun foo = (pred::0,) => if (pred) then X() else Y() //│ let selRes$26 = foo(true,) in case selRes$26 of { Y => (selRes$26).num //│ X => (selRes$26).num }} //│ End simpledef //│ //│ res //│ = 6 class X() { val num = 6 } class Y() { val num = true } class A() { val num = X() } class B() { val num = Y() } class C() { val num = X() } fun foo(pred) = if pred == 1 then A() else if pred == 2 then B() else C() foo(5).num.num //│ class X() { //│ val num: 6 //│ } //│ class Y() { //│ val num: true //│ } //│ class A() { //│ val num: X //│ } //│ class B() { //│ val num: Y //│ } //│ class C() { //│ val num: X //│ } //│ fun foo: Num -> (A | B | C) //│ 6 | true //│ //│ Simpledef: //│ {class X() {let num = 6} //│ class Y() {let num = true} //│ class A() {let num = X()} //│ class B() {let num = Y()} //│ class C() {let num = X()} //│ fun foo = (pred::1,) => {if (==(pred, 1,)) then A() else {if (==(pred, 2,)) then B() else C()}} //│ let selRes$74 = let selRes$76 = foo(5,) in case selRes$76 of { B => (selRes$76).num //│ C => (selRes$76).num //│ A => (selRes$76).num } in case selRes$74 of { X => (selRes$74).num //│ Y => (selRes$74).num }} //│ End simpledef //│ //│ res //│ = 6 ================================================ FILE: compiler/shared/test/diff/Defunctionalize/TupleSelect.mls ================================================ :NewDefs [0,"abc",()].2 //│ () //│ //│ Simpledef: //│ {([0, "abc", undefined,]).2} //│ End simpledef //│ //│ res //│ = undefined ================================================ FILE: compiler/shared/test/diff/Lifter/FunctionTypeAnnotations.mls ================================================ :NewDefs :ParseOnly :lift fun foo(x) = (y) => x+y foo(1)(2) foo(2)(2) //│ |#fun| |foo|(|x|)| |#=|→|(|y|)| |#=>| |x|+|y|←|↵|foo|(|1|)|(|2|)|↵|foo|(|2|)|(|2|)| //│ Parsed: {fun foo = (x,) => {('(' y ')',) => +(x, y,)}; foo(1,)(2,); foo(2,)(2,)} //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([x,]) {fun apply = ('(' y ')',) => +((this).x, y,)} //│ fun foo$1 = (x,) => {{Lambda1$2$1(x,)}} //│ Code(List(foo$1(1,)(2,))) //│ Code(List(foo$1(2,)(2,))) //│ } //│ ================================================ FILE: compiler/shared/test/diff/Lifter/LambLift.mls ================================================ :NewDefs :ParseOnly :lift fun foo() = let local(x) = class Foo { fun bar = x + foo() } (new Foo()).bar local(1) foo() //│ |#fun| |foo|(||)| |#=|→|#let| |local|(|x|)| |#=|→|#class| |Foo| |{|→|#fun| |bar| |#=| |x| |+| |foo|(||)|←|↵|}|↵|(|#new| |Foo|(||)|)|.bar|←|↵|local|(|1|)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {let local = (x,) => {class Foo {fun bar = +(x, foo(),)}; ('(' (new Foo)() ')').bar}; local(1,)}; foo()} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x,]) {fun bar = +((this).x, foo$1(),)} //│ let local$2 = (x,) => {('(' (new Foo$1)(x,) ')').bar} //│ fun foo$1 = () => {local$2(1,)} //│ Code(List(foo$1())) //│ } //│ :lift fun foo(f) = f(1) foo(x => x+1) //│ |#fun| |foo|(|f|)| |#=| |→|f|(|1|)|←|↵|foo|(|x| |#=>| |x|+|1|)| //│ Parsed: {fun foo = (f,) => {f(1,)}; foo((x,) => +(x, 1,),)} //│ Lifted: //│ TypingUnit { //│ class Lambda1$2$1([]) {fun apply = (x,) => +(x, 1,)} //│ fun foo$1 = (f,) => {f(1,)} //│ Code(List(foo$1({Lambda1$2$1()},))) //│ } //│ :lift fun foo(x) = let bar(f) = f(x) bar(y => y+x) foo(1) //│ |#fun| |foo|(|x|)| |#=| |→|#let| |bar|(|f|)| |#=| |→|f|(|x|)|←|↵|bar|(|y| |#=>| |y|+|x|)|←|↵|foo|(|1|)| //│ Parsed: {fun foo = (x,) => {let bar = (f,) => {f(x,)}; bar((y,) => +(y, x,),)}; foo(1,)} //│ Lifted: //│ TypingUnit { //│ class Lambda1$3$1([x,]) {fun apply = (y,) => +(y, (this).x,)} //│ let bar$2 = (f, x,) => {f(x,)} //│ fun foo$1 = (x,) => {bar$2({Lambda1$3$1(x,)}, x,)} //│ Code(List(foo$1(1,))) //│ } //│ :lift fun foo(f) = f(1) class A(y: Int){ fun bar(z) = y+z } fun app(a) = foo(z => a.bar(z)) app(new A(1)) //│ |#fun| |foo|(|f|)| |#=| |→|f|(|1|)|←|↵|#class| |A|(|y|#:| |Int|)|{|→|#fun| |bar|(|z|)| |#=| |y|+|z|←|↵|}|↵|#fun| |app|(|a|)| |#=| |→|foo|(|z| |#=>| |a|.bar|(|z|)|)|←|↵|app|(|#new| |A|(|1|)|)| //│ Parsed: {fun foo = (f,) => {f(1,)}; class A(y: Int,) {fun bar = (z,) => +(y, z,)}; fun app = (a,) => {foo((z,) => (a).bar(z,),)}; app((new A)(1,),)} //│ Lifted: //│ TypingUnit { //│ class A$1([y: Int,]) {fun bar = (z,) => +((this).y, z,)} //│ class Lambda1$3$2([a,]) {fun apply = (z,) => ((this).a).bar(z,)} //│ fun foo$2 = (f,) => {f(1,)} //│ fun app$1 = (a,) => {foo$2({Lambda1$3$2(a,)},)} //│ Code(List(app$1((new A$1)(1,),))) //│ } //│ ================================================ FILE: compiler/shared/test/diff/Lifter/LiftNew.mls ================================================ :NewDefs :lift class A(num: Int) { } new A(5) //│ Lifted: //│ TypingUnit { //│ class A$1([num: Int,]) {} //│ Code(List((new A$1)(5,))) //│ } //│ class A$1(num: Int) //│ A$1 //│ res //│ = A$1 {} ================================================ FILE: compiler/shared/test/diff/Lifter/LiftType.mls ================================================ :NewDefs :ParseOnly :lift class CTX{ class A {} fun foo(f: A => A): (A => A) => A = f(new A) } //│ |#class| |CTX|{|→|#class| |A| |{||}|↵|#fun| |foo|(|f|#:| |A| |#=>| |A|)|#:| |(|A| |#=>| |A|)| |#=>| |A| |#=| |f|(|#new| |A|)|←|↵|}| //│ Parsed: {class CTX {class A {}; fun foo = (f: (A,) => A,) => f(new A,) : (A -> A) -> A}} //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {} //│ class CTX$1([]) { //│ fun foo = (f: (CTX$1_A$2,) => CTX$1_A$2,) => f((new CTX$1_A$2)(this,),) : (CTX$1_A$2 -> CTX$1_A$2) -> CTX$1_A$2 //│ } //│ } //│ :lift class CTX(x, y){ class A{ fun foo = x} class B: A { fun foo = y} fun foo(any: [A, B]): [B, A] = [any._2, any._1] } //│ |#class| |CTX|(|x|,| |y|)|{|→|#class| |A|{| |#fun| |foo| |#=| |x|}|↵|#class| |B|#:| |A| |{| |#fun| |foo| |#=| |y|}|↵|#fun| |foo|(|any|#:| |[|A|,| |B|]|)|#:| |[|B|,| |A|]| |#=| |[|any|._2|,| |any|._1|]|←|↵|}| //│ Parsed: {class CTX(x, y,) {class A {fun foo = x}; class B: A {fun foo = y}; fun foo = (any: [A, B,],) => [(any)._2, (any)._1,] : [B, A]}} //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {fun foo = ((this).par$CTX$1).x} //│ class CTX$1_B$3([par$CTX$1,]) {fun foo = ((this).par$CTX$1).y} //│ class CTX$1([x, y,]) { //│ fun foo = (any: [CTX$1_A$2, CTX$1_B$3,],) => [(any)._2, (any)._1,] : [CTX$1_B$3, CTX$1_A$2] //│ } //│ } //│ :lift class CTX(x, y){ class A{ fun foo = x} class B: A { fun foo = y} fun foo(any: {p1: A, p2: B}): [B, A] = [any.p2, any.p1] } //│ |#class| |CTX|(|x|,| |y|)|{|→|#class| |A|{| |#fun| |foo| |#=| |x|}|↵|#class| |B|#:| |A| |{| |#fun| |foo| |#=| |y|}|↵|#fun| |foo|(|any|#:| |{|p1|#:| |A|,| |p2|#:| |B|}|)|#:| |[|B|,| |A|]| |#=| |[|any|.p2|,| |any|.p1|]|←|↵|}| //│ Parsed: {class CTX(x, y,) {class A {fun foo = x}; class B: A {fun foo = y}; fun foo = (any: '{' {p1: A, p2: B} '}',) => [(any).p2, (any).p1,] : [B, A]}} //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {fun foo = ((this).par$CTX$1).x} //│ class CTX$1_B$3([par$CTX$1,]) {fun foo = ((this).par$CTX$1).y} //│ class CTX$1([x, y,]) { //│ fun foo = (any: '{' {p1: CTX$1_A$2, p2: CTX$1_B$3} '}',) => [(any).p2, (any).p1,] : [CTX$1_B$3, CTX$1_A$2] //│ } //│ } //│ :lift class CTX(x, y){ class A{ fun foo = x} class B { fun foo = y} fun foo(any: [A, B]): [[B, A], A] = [any, any._1] } //│ |#class| |CTX|(|x|,| |y|)|{|→|#class| |A|{| |#fun| |foo| |#=| |x|}|↵|#class| |B|‹|T|›| |{| |#fun| |foo| |#=| |y|}|↵|#fun| |foo|(|any|#:| |[|A|,| |B|‹|A|›|]|)|#:| |[|[|B|‹|A|›|,| |A|]|,| |A|]| |#=| |[|any|,| |any|._1|]|←|↵|}| //│ Parsed: {class CTX(x, y,) {class A {fun foo = x}; class B‹T› {fun foo = y}; fun foo = (any: [A, B‹A›,],) => [any, (any)._1,] : [[B[A], A], A]}} //│ Lifted: //│ TypingUnit { //│ class CTX$1_A$2([par$CTX$1,]) {fun foo = ((this).par$CTX$1).x} //│ class CTX$1_B$3[T]([par$CTX$1,]) {fun foo = ((this).par$CTX$1).y} //│ class CTX$1([x, y,]) { //│ fun foo = (any: [CTX$1_A$2, CTX$1_B$3‹CTX$1_A$2›,],) => [any, (any)._1,] : [[CTX$1_B$3[CTX$1_A$2], CTX$1_A$2], CTX$1_A$2] //│ } //│ } //│ :lift class CTX{ fun ctx(x,y) = class A{ fun foo = x } fun bar(any: T): A = let x = new T new A (new CTX).bar } //│ |#class| |CTX|{|→|#fun| |ctx|(|x|,|y|)| |#=| |→|#class| |A|{| |#fun| |foo| |#=| |x| |}|↵|#fun| |bar|‹|T|›|(|any|#:| |T|)|#:| |A| |#=| |→|#let| |x| |#=| |#new| |T|↵|#new| |A|←|↵|(|#new| |CTX|)|.bar|‹|CTX|›|←|←|↵|}| //│ Parsed: {class CTX {fun ctx = (x, y,) => {class A {fun foo = x}; fun bar = (any: T,) => {let x = new T; new A} : A; ('(' new CTX ')').bar‹CTX›}}} //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type T. Class values are not supported in lifter. //│ ================================================ FILE: compiler/shared/test/diff/Lifter/Lifter.mls ================================================ :NewDefs :ParseOnly :lift class A(x) { class B(y) { fun getX = x fun getB1 = B1(y) class C(z) { fun inc() = x + 1 fun getY = y fun getA = A(z) fun getB(w) = B(w) fun getC = new C(inc()) fun getSelf = this } } class B1(y) { fun getX = x fun getY = y fun getB = new B(y) fun getB1 = new B1(y) } fun getB = new B(x) fun getB2(y) = B1(y) fun getB3(z) = getB2(z) fun getA = A(x) } //│ |#class| |A|(|x|)| |{|→|#class| |B|(|y|)| |{|→|#fun| |getX| |#=| |x|↵|#fun| |getB1| |#=| |B1|(|y|)|↵|#class| |C|(|z|)| |{|→|#fun| |inc|(||)| |#=| |x| |+| |1|↵|#fun| |getY| |#=| |y|↵|#fun| |getA| |#=| |A|(|z|)|↵|#fun| |getB|(|w|)| |#=| |B|(|w|)|↵|#fun| |getC| |#=| |#new| |C|(|inc|(||)|)|↵|#fun| |getSelf| |#=| |this|←|↵|}|←|↵|}|↵|#class| |B1|(|y|)| |{|→|#fun| |getX| |#=| |x|↵|#fun| |getY| |#=| |y|↵|#fun| |getB| |#=| |#new| |B|(|y|)|↵|#fun| |getB1| |#=| |#new| |B1|(|y|)|←|↵|}|↵|#fun| |getB| |#=| |#new| |B|(|x|)|↵|#fun| |getB2|(|y|)| |#=| |B1|(|y|)|↵|#fun| |getB3|(|z|)| |#=| |getB2|(|z|)|↵|#fun| |getA| |#=| |A|(|x|)|←|↵|}| //│ Parsed: {class A(x,) {class B(y,) {fun getX = x; fun getB1 = B1(y,); class C(z,) {fun inc = () => +(x, 1,); fun getY = y; fun getA = A(z,); fun getB = (w,) => B(w,); fun getC = (new C)(inc(),); fun getSelf = this}}; class B1(y,) {fun getX = x; fun getY = y; fun getB = (new B)(y,); fun getB1 = (new B1)(y,)}; fun getB = (new B)(x,); fun getB2 = (y,) => B1(y,); fun getB3 = (z,) => getB2(z,); fun getA = A(x,)}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2_C$4([par$A$1_B$2, z, x,]) { //│ fun inc = () => +((this).x, 1,) //│ fun getY = ((this).par$A$1_B$2).y //│ fun getA = A$1((this).z,) //│ fun getB = (w,) => A$1_B$2(((this).par$A$1_B$2).par$A$1, w,) //│ fun getC = (new A$1_B$2_C$4)((this).par$A$1_B$2, (this).inc(), (this).x,) //│ fun getSelf = this //│ } //│ class A$1_B$2([par$A$1, y,]) { //│ fun getX = ((this).par$A$1).x //│ fun getB1 = A$1_B1$3((this).par$A$1, (this).y,) //│ } //│ class A$1_B1$3([par$A$1, y,]) { //│ fun getX = ((this).par$A$1).x //│ fun getY = (this).y //│ fun getB = (new A$1_B$2)((this).par$A$1, (this).y,) //│ fun getB1 = (new A$1_B1$3)((this).par$A$1, (this).y,) //│ } //│ class A$1([x,]) { //│ fun getB = (new A$1_B$2)(this, (this).x,) //│ fun getB2 = (y,) => A$1_B1$3(this, y,) //│ fun getB3 = (z,) => (this).getB2(z,) //│ fun getA = A$1((this).x,) //│ } //│ } //│ :lift class A(x) { class B(y) { class C(z) { fun sum = x + y + z } } } //│ |#class| |A|(|x|)| |{|→|#class| |B|(|y|)| |{|→|#class| |C|(|z|)| |{|→|#fun| |sum| |#=| |x| |+| |y| |+| |z|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A(x,) {class B(y,) {class C(z,) {fun sum = +(+(x, y,), z,)}}}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2_C$3([par$A$1_B$2, z, x,]) { //│ fun sum = +(+((this).x, ((this).par$A$1_B$2).y,), (this).z,) //│ } //│ class A$1_B$2([par$A$1, y,]) {} //│ class A$1([x,]) {} //│ } //│ :lift class A(x) { class B{ fun foo = 1 fun bar = 11 } fun getB = new B{ fun foo = 2 fun bar = 12 } fun bar = 13 } class C: A{ fun getB = new B{ fun foo = 3 fun bar = 14 } fun bar = 15 } new C{ fun getB = new B{ fun foo = 4 fun bar = 16 } fun bar = 17 } //│ |#class| |A|(|x|)| |{|→|#class| |B|{|→|#fun| |foo| |#=| |1|↵|#fun| |bar| |#=| |11|←|↵|}|↵|#fun| |getB| |#=| |#new| |B|{|→|#fun| |foo| |#=| |2|↵|#fun| |bar| |#=| |12|←|↵|}|↵|#fun| |bar| |#=| |13|←|↵|}|↵|#class| |C|#:| |A|{|→|#fun| |getB| |#=| |#new| |B|{|→|#fun| |foo| |#=| |3|↵|#fun| |bar| |#=| |14|←|↵|}|↵|#fun| |bar| |#=| |15|←|↵|}|↵|#new| |C|{|→|#fun| |getB| |#=| |#new| |B|{|→|#fun| |foo| |#=| |4|↵|#fun| |bar| |#=| |16|←|↵|}|↵|#fun| |bar| |#=| |17|←|↵|}| //│ Parsed: {class A(x,) {class B {fun foo = 1; fun bar = 11}; fun getB = new B {fun foo = 2; fun bar = 12}; fun bar = 13}; class C: A {fun getB = new B {fun foo = 3; fun bar = 14}; fun bar = 15}; new C {fun getB = new B {fun foo = 4; fun bar = 16}; fun bar = 17}} //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type B. Class values are not supported in lifter. //│ :lift class Parent(x) { fun foo(x: Int): T = x+1 class Inner(y: Int){ fun bar(z: U) = foo(y) fun boo(z: W) = z } } //│ |#class| |Parent|‹|T|,| |U|,| |V|›|(|x|)| |{| |→|#fun| |foo|(|x|#:| |Int|)|#:| |T| |#=| |x|+|1|↵|#class| |Inner|‹|W|›|(|y|#:| |Int|)|{|→|#fun| |bar|(|z|#:| |U|)| |#=| |foo|(|y|)|↵|#fun| |boo|(|z|#:| |W|)| |#=| |z|←|↵|}|←|↵|}| //│ Parsed: {class Parent‹T, U, V›(x,) {fun foo = (x: Int,) => +(x, 1,) : T; class Inner‹W›(y: Int,) {fun bar = (z: U,) => foo(y,); fun boo = (z: W,) => z}}} //│ Lifted: //│ TypingUnit { //│ class Parent$1_Inner$2[W,U]([par$Parent$1, y: Int,]) { //│ fun bar = (z: U,) => ((this).par$Parent$1).foo((this).y,) //│ fun boo = (z: W,) => z //│ } //│ class Parent$1[T,U,V]([x,]) {fun foo = (x: Int,) => +(x, 1,) : T} //│ } //│ :lift class B {} class C {} class D(y: Int) {} class A(x: Int): ({a1: Int} & B & D(x)) { fun getA() = new C{ fun foo(x: T) = x } } //│ |#class| |B|‹|T|›| |{||}|↵|#class| |C| |{||}|↵|#class| |D|(|y|#:| |Int|)| |{||}|↵|#class| |A|‹|T|,| |U|›|(|x|#:| |Int|)|#:| |(|{|a1|#:| |Int|}| |&| |B|‹|T|›| |&| |D|(|x|)|)| |{|→|#fun| |getA|(||)| |#=| |#new| |C|{|→|#fun| |foo|(|x|#:| |T|)| |#=| |x|←|↵|}|←|↵|}| //│ Parsed: {class B‹T› {}; class C {}; class D(y: Int,) {}; class A‹T, U›(x: Int,): {a1: Int} & B[T] & D[x] {fun getA = () => new C {fun foo = (x: T,) => x}}} //│ Lifted: //│ TypingUnit { //│ class B$1[T]([]) {} //│ class C$2([]) {} //│ class D$3([y: Int,]) {} //│ class A$4[T,U]([x: Int,]) {fun getA = () => new C$2([]) {}} //│ } //│ // │ TypingUnit(NuTypeDef(class, B, (TypeName(T)), Tup(), (), TypingUnit()), NuTypeDef(class, C, (), Tup(), (), TypingUnit()), NuTypeDef(class, A, (TypeName(T), TypeName(U)), Tup(x: Var(Int)), (App(App(Var(&), Tup(_: Bra(rcd = true, Rcd(Var(a1) = Var(Int)})))), Tup(_: TyApp(Var(B), List(TypeName(T)))))), TypingUnit(NuFunDef(None, getA, [], Lam(Tup(), New(Some((TypeName(C),)), TypingUnit(List(fun foo = x: T, => x)))))))) :lift class B {} class C {} class D(y: Int) {} class A(x: Int) extends {a1: Int}, B, D(x){ fun getA() = new C{ fun foo(x) = x } } //│ |#class| |B|‹|T|›| |{||}|↵|#class| |C| |{||}|↵|#class| |D|(|y|#:| |Int|)| |{||}|↵|#class| |A|‹|T|,| |U|›|(|x|#:| |Int|)| |#extends| |{|a1|#:| |Int|}|,| |B|‹|T|›|,| |D|(|x|)|{|→|#fun| |getA|(||)| |#=| |#new| |C|{|→|#fun| |foo|(|x|)| |#=| |x|←|↵|}|←|↵|}| //│ Parsed: {class B‹T› {}; class C {}; class D(y: Int,) {}; class A‹T, U›(x: Int,): '{' {a1: Int} '}', B‹T›, D(x,) {fun getA = () => new C {fun foo = (x,) => x}}} //│ Lifted: //│ TypingUnit { //│ class B$1[T]([]) {} //│ class C$2([]) {} //│ class D$3([y: Int,]) {} //│ class A$4[T,U]([x: Int,]): '{' {a1: Int} '}', B$1‹T›, D$3((this).x,) {fun getA = () => new C$2([]) {}} //│ } //│ :lift class Child(x): ({ age: T } & { name: String}) { class Inner{ fun foo = age } fun bar = age fun boo = new Inner } //│ |#class| |Child|‹|T|,| |U|›|(|x|)|#:| |(|{| |age|#:| |T| |}| |&| |{| |name|#:| |String|}|)| |{|→|#class| |Inner|{|→|#fun| |foo| |#=| |age|←|↵|}|↵|#fun| |bar| |#=| |age|↵|#fun| |boo| |#=| |#new| |Inner|←|↵|}| //│ Parsed: {class Child‹T, U›(x,): {age: T} & {name: String} {class Inner {fun foo = age}; fun bar = age; fun boo = new Inner}} //│ Lifted: //│ TypingUnit { //│ class Child$1_Inner$2([par$Child$1, age,]) {fun foo = (this).age} //│ class Child$1[T,U]([x,]) { //│ fun bar = age //│ fun boo = (new Child$1_Inner$2)(this, age,) //│ } //│ } //│ :lift class A(x: Int) { fun getA: Int = 0 fun getA1 = 1 } new A(0) { fun getA = 3 fun getA2 = 2 } //│ |#class| |A|(|x|#:| |Int|)| |{|→|#fun| |getA|#:| |Int| |#=| |0|↵|#fun| |getA1| |#=| |1|←|↵|}|↵|#new| |A|(|0|)| |{|→|#fun| |getA| |#=| |3|↵|#fun| |getA2| |#=| |2|←|↵|}| //│ Parsed: {class A(x: Int,) {fun getA = 0 : Int; fun getA1 = 1}; (new A)(0,) {fun getA = 3; fun getA2 = 2}} //│ Lifted: //│ TypingUnit { //│ class A$1([x: Int,]) {fun getA = 0 : Int; fun getA1 = 1} //│ class A$1$2([x: Int,]): A$1((this).x,) {fun getA = 3; fun getA2 = 2} //│ Code(List({new A$1$2([0,]) {}})) //│ } //│ :lift class A(x) { class B(y) { } } new A(1) { fun getB = new B(2){ fun getB = new B(3) } } //│ |#class| |A|(|x|)| |{|→|#class| |B|(|y|)| |{| |}|←|↵|}|↵|#new| |A|(|1|)| |{|→|#fun| |getB| |#=| |#new| |B|(|2|)|{|→|#fun| |getB| |#=| |#new| |B|(|3|)|←|↵|}|←|↵|}| //│ Parsed: {class A(x,) {class B(y,) {}}; (new A)(1,) {fun getB = (new B)(2,) {fun getB = (new B)(3,)}}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1, y,]) {} //│ class A$1([x,]) {} //│ class A$1$3_B$2$4([par$A$1$3, y,]): A$1_B$2((this).par$A$1$3, (this).y,) {fun getB = (new A$1_B$2)((this).par$A$1$3, 3,)} //│ class A$1$3([x,]): A$1((this).x,) {fun getB = {new A$1$3_B$2$4([this, 2,]) {}}} //│ Code(List({new A$1$3([1,]) {}})) //│ } //│ :lift class A { fun getA = 0 fun funcA = 10 } class B: A{ fun getA = 1 fun funcB = 11 } new A new B fun f(x) = if x is A then 0 else 1 f(new A{ fun getA = 2 }) new B{ fun getA = funcB } //│ |#class| |A| |{|→|#fun| |getA| |#=| |0|↵|#fun| |funcA| |#=| |10|←|↵|}|↵|#class| |B|#:| |A|{|→|#fun| |getA| |#=| |1|↵|#fun| |funcB| |#=| |11|←|↵|}|↵|#new| |A|↵|#new| |B|↵|#fun| |f|(|x|)| |#=| |#if| |x| |is| |A| |#then| |0| |#else| |1|↵|f|(|#new| |A|{|→|#fun| |getA| |#=| |2|←|↵|}|)|↵|#new| |B|{|→|#fun| |getA| |#=| |funcB|←|↵|}| //│ Parsed: {class A {fun getA = 0; fun funcA = 10}; class B: A {fun getA = 1; fun funcB = 11}; new A; new B; fun f = (x,) => if (is(x, A,)) then 0 else 1; f(new A {fun getA = 2},); new B {fun getA = funcB}} //│ Lifted: //│ TypingUnit { //│ class A$1([]) {fun getA = 0; fun funcA = 10} //│ class B$2([]) {fun getA = 1; fun funcB = 11} //│ fun f$1 = (x,) => if (is(x, A$1,)) then 0 else 1 //│ Code(List((new A$1)())) //│ Code(List((new B$2)())) //│ Code(List(f$1(new A$1([]) {},))) //│ Code(List(new B$2([]) {})) //│ } //│ :lift class A{ class B{ fun funB = 1 fun foo = 100 } class C: B{ fun funC = 2 fun foo = 1000 } class D{ fun funD = 3 fun foo = 10000 class E: C{ fun funE = 4 fun foo = 100000 } class F: E{ fun funF = 5 fun foo = 1000000 } } } //│ |#class| |A|{|→|#class| |B|{|→|#fun| |funB| |#=| |1|↵|#fun| |foo| |#=| |100|←|↵|}|↵|#class| |C|#:| |B|{|→|#fun| |funC| |#=| |2|↵|#fun| |foo| |#=| |1000|←|↵|}|↵|#class| |D|{|→|#fun| |funD| |#=| |3|↵|#fun| |foo| |#=| |10000| |↵|#class| |E|#:| |C|{|→|#fun| |funE| |#=| |4|↵|#fun| |foo| |#=| |100000|←|↵|}|↵|#class| |F|#:| |E|{|→|#fun| |funF| |#=| |5|↵|#fun| |foo| |#=| |1000000|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A {class B {fun funB = 1; fun foo = 100}; class C: B {fun funC = 2; fun foo = 1000}; class D {fun funD = 3; fun foo = 10000; class E: C {fun funE = 4; fun foo = 100000}; class F: E {fun funF = 5; fun foo = 1000000}}}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {fun funB = 1; fun foo = 100} //│ class A$1_C$3([par$A$1,]) {fun funC = 2; fun foo = 1000} //│ class A$1_D$4_E$5([par$A$1_D$4,]) {fun funE = 4; fun foo = 100000} //│ class A$1_D$4_F$6([par$A$1_D$4,]) {fun funF = 5; fun foo = 1000000} //│ class A$1_D$4([par$A$1,]) {fun funD = 3; fun foo = 10000} //│ class A$1([]) {} //│ } //│ :lift class A{ class B{ fun funB = 1 fun foo = 100 } class C: B{ fun funC = 2 fun foo = 1000 fun getB = new B } class D{ fun funD = 3 fun foo = 10000 class E: C{ fun funE = 4 fun foo = 100000 fun getD = new D } class F: E{ fun funF = 5 fun foo = 1000000 fun getE = new E{ fun foo = 0 } } } } //│ |#class| |A|{|→|#class| |B|{|→|#fun| |funB| |#=| |1|↵|#fun| |foo| |#=| |100|←|↵|}|↵|#class| |C|#:| |B|{|→|#fun| |funC| |#=| |2|↵|#fun| |foo| |#=| |1000|↵|#fun| |getB| |#=| |#new| |B|←|↵|}|↵|#class| |D|{|→|#fun| |funD| |#=| |3|↵|#fun| |foo| |#=| |10000| |↵|#class| |E|#:| |C|{|→|#fun| |funE| |#=| |4|↵|#fun| |foo| |#=| |100000|↵|#fun| |getD| |#=| |#new| |D|←|↵|}|↵|#class| |F|#:| |E|{|→|#fun| |funF| |#=| |5|↵|#fun| |foo| |#=| |1000000|↵|#fun| |getE| |#=| |#new| |E|{|→|#fun| |foo| |#=| |0|←|↵|}|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A {class B {fun funB = 1; fun foo = 100}; class C: B {fun funC = 2; fun foo = 1000; fun getB = new B}; class D {fun funD = 3; fun foo = 10000; class E: C {fun funE = 4; fun foo = 100000; fun getD = new D}; class F: E {fun funF = 5; fun foo = 1000000; fun getE = new E {fun foo = 0}}}}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {fun funB = 1; fun foo = 100} //│ class A$1_C$3([par$A$1,]) { //│ fun funC = 2 //│ fun foo = 1000 //│ fun getB = (new A$1_B$2)((this).par$A$1,) //│ } //│ class A$1_D$4_E$5([par$A$1_D$4,]) { //│ fun funE = 4 //│ fun foo = 100000 //│ fun getD = (new A$1_D$4)(((this).par$A$1_D$4).par$A$1,) //│ } //│ class A$1_D$4_F$6([par$A$1_D$4,]) { //│ fun funF = 5 //│ fun foo = 1000000 //│ fun getE = new A$1_D$4_E$5([(this).par$A$1_D$4,]) {} //│ } //│ class A$1_D$4([par$A$1,]) {fun funD = 3; fun foo = 10000} //│ class A$1([]) {} //│ } //│ :lift class A{ class B{ fun foo = 1 } fun bar = new B } new A //│ |#class| |A|{|→|#class| |B|{|→|#fun| |foo| |#=| |1|←|↵|}|↵|#fun| |bar| |#=| |#new| |B|←|↵|}|↵|#new| |A| //│ Parsed: {class A {class B {fun foo = 1}; fun bar = new B}; new A} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {fun foo = 1} //│ class A$1([]) {fun bar = (new A$1_B$2)(this,)} //│ Code(List((new A$1)())) //│ } //│ :lift class A(x) { fun foo = 0 fun bar = x } let x = new A{ fun foo = 1 fun newFun = 2 fun bar = new A(foo){ fun foo = bar + 1 fun bar2 = newFun + 1 } } //│ |#class| |A|(|x|)| |{|→|#fun| |foo| |#=| |0|↵|#fun| |bar| |#=| |x|←|↵|}|↵|#let| |x| |#=| |#new| |A|{|→|#fun| |foo| |#=| |1|↵|#fun| |newFun| |#=| |2|↵|#fun| |bar| |#=| |#new| |A|(|foo|)|{|→|#fun| |foo| |#=| |bar| |+| |1|↵|#fun| |bar2| |#=| |newFun| |+| |1|←|↵|}|←|↵|}| //│ Parsed: {class A(x,) {fun foo = 0; fun bar = x}; let x = new A {fun foo = 1; fun newFun = 2; fun bar = (new A)(foo,) {fun foo = +(bar, 1,); fun bar2 = +(newFun, 1,)}}} //│ Lifted: //│ TypingUnit { //│ class A$1([x,]) {fun foo = 0; fun bar = x$1} //│ let x$1 = new A$1([]) {} //│ } //│ :lift class A {} new A{ fun foo = 1 fun bar = new A{ fun foo1 = foo fun bar1 = new A{ fun foo2 = foo fun bar2 = new A{ fun foo3 = foo fun bar3 = new A{ fun foo4 = foo fun bar4 = 0 } } } } } //│ |#class| |A| |{||}|↵|#new| |A|{|→|#fun| |foo| |#=| |1|↵|#fun| |bar| |#=| |#new| |A|{|→|#fun| |foo1| |#=| |foo|↵|#fun| |bar1| |#=| |#new| |A|{|→|#fun| |foo2| |#=| |foo|↵|#fun| |bar2| |#=| |#new| |A|{|→|#fun| |foo3| |#=| |foo|↵|#fun| |bar3| |#=| |#new| |A|{|→|#fun| |foo4| |#=| |foo|↵|#fun| |bar4| |#=| |0|←|↵|}|←|↵|}|←|↵|}|←|↵|}|←|↵|}| //│ Parsed: {class A {}; new A {fun foo = 1; fun bar = new A {fun foo1 = foo; fun bar1 = new A {fun foo2 = foo; fun bar2 = new A {fun foo3 = foo; fun bar3 = new A {fun foo4 = foo; fun bar4 = 0}}}}}} //│ Lifted: //│ TypingUnit {class A$1([]) {}; Code(List(new A$1([]) {}))} //│ ================================================ FILE: compiler/shared/test/diff/Lifter/LifterBlks.mls ================================================ :NewDefs :ParseOnly :lift fun foo = print("ok") print("ko") //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|←| //│ Parsed: {fun foo = {print("ok",); print("ko",)}} //│ Lifted: //│ TypingUnit {fun foo$1 = {print("ok",); print("ko",)}} //│ :lift class A{ class B {} fun foo(x: B) = (x : B) } //│ |#class| |A|{|→|#class| |B| |{||}|↵|#fun| |foo|(|x|#:| |B|)| |#=| |(|x| |#:| |B|)|←|↵|}| //│ Parsed: {class A {class B {}; fun foo = (x: B,) => '(' x : B ')'}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1,]) {} //│ class A$1([]) {fun foo = (x: A$1_B$2,) => '(' x : A$1_B$2 ')'} //│ } //│ :lift fun foo = let local(x) = class Foo { fun bar = x + 1 } Foo().bar print of local(0) + local(1) print of (local of 0) + local of 1 fun tmp = 1 print of local of 0 + local of 1 fun tmp = 2 //│ |#fun| |foo| |#=|→|#let| |local|(|x|)| |#=|→|#class| |Foo| |{|→|#fun| |bar| |#=| |x| |+| |1|←|↵|}|↵|Foo|(||)|.bar|←|↵|print| |#of| |local|(|0|)| |+| |local|(|1|)|↵|print| |#of| |(|local| |#of| |0|)| |+| |local| |#of| |1|↵|#fun| |tmp| |#=| |1|↵|print| |#of| |local| |#of| |0| |+| |local| |#of| |1|↵|#fun| |tmp| |#=| |2|←| //│ Parsed: {fun foo = {let local = (x,) => {class Foo {fun bar = +(x, 1,)}; (Foo()).bar}; print(+(local(0,), local(1,),),); print(+('(' local(0,) ')', local(1,),),); fun tmp = 1; print(local(+(0, local(1,),),),); fun tmp = 2}} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x,]) {fun bar = +((this).x, 1,)} //│ let local$3 = (x,) => {(Foo$1(x,)).bar} //│ fun tmp$2 = 1 //│ fun foo$1 = {print(+(local$3(0,), local$3(1,),),); print(+('(' local$3(0,) ')', local$3(1,),),); print(local$3(+(0, local$3(1,),),),)} //│ } //│ :lift class A(y){} let f = x => new A(0){fun bar = x+y} f(0) //│ |#class| |A|(|y|)|{||}|↵|#let| |f| |#=| |x| |#=>| |#new| |A|(|0|)|{|#fun| |bar| |#=| |x|+|y|}|↵|f|(|0|)| //│ Parsed: {class A(y,) {}; let f = (x,) => (new A)(0,) {fun bar = +(x, y,)}; f(0,)} //│ Lifted: //│ TypingUnit { //│ class A$1([y,]) {} //│ class A$2$2([y, x,]): A$1((this).y,) {fun bar = +((this).x, (this).y,)} //│ let f$1 = (x,) => {new A$2$2([0, x,]) {}} //│ Code(List(f$1(0,))) //│ } //│ :lift class A(x){ fun w = x fun foo(y) = class B(z){ fun bar = x+y+z } new B(0){ fun bar = w+y+z } } //│ |#class| |A|(|x|)|{|→|#fun| |w| |#=| |x|↵|#fun| |foo|(|y|)| |#=| |→|#class| |B|(|z|)|{|→|#fun| |bar| |#=| |x|+|y|+|z|←|↵|}|↵|#new| |B|(|0|)|{|→|#fun| |bar| |#=| |w|+|y|+|z|←|↵|}|←|←|↵|}| //│ Parsed: {class A(x,) {fun w = x; fun foo = (y,) => {class B(z,) {fun bar = +(+(x, y,), z,)}; (new B)(0,) {fun bar = +(+(w, y,), z,)}}}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2([par$A$1, z, y,]) {fun bar = +(+(((this).par$A$1).x, (this).y,), (this).z,)} //│ class A$1_B$1$3([par$A$1, z, y,]): A$1_B$2((this).par$A$1, (this).z, (this).y,) {fun bar = +(+(((this).par$A$1).w, (this).y,), (this).z,)} //│ class A$1([x,]) { //│ fun w = (this).x //│ fun foo = (y,) => {{new A$1_B$1$3([this, 0, y,]) {}}} //│ } //│ } //│ :lift fun f(x,y,z) = class A{ fun foo = new B fun bar1 = x } class B{ fun foo = new A fun bar2 = y } class C extends A, B { fun bar = bar1 + bar2 } //│ |#fun| |f|(|x|,|y|,|z|)| |#=| |→|#class| |A|{|→|#fun| |foo| |#=| |#new| |B|↵|#fun| |bar1| |#=| |x|←|↵|}|↵|#class| |B|{|→|#fun| |foo| |#=| |#new| |A|↵|#fun| |bar2| |#=| |y|←|↵|}|↵|#class| |C| |#extends| |A|,| |B| |{|→|#fun| |bar| |#=| |bar1| |+| |bar2|←|↵|}|←| //│ Parsed: {fun f = (x, y, z,) => {class A {fun foo = new B; fun bar1 = x}; class B {fun foo = new A; fun bar2 = y}; class C: A, B {fun bar = +(bar1, bar2,)}}} //│ Lifted: //│ TypingUnit { //│ class A$1([x, y,]) { //│ fun foo = (new B$2)((this).y, (this).x,) //│ fun bar1 = (this).x //│ } //│ class B$2([y, x,]) { //│ fun foo = (new A$1)((this).x, (this).y,) //│ fun bar2 = (this).y //│ } //│ class C$3([x, y,]): A$1, B$2 {fun bar = +((this).bar1, (this).bar2,)} //│ fun f$1 = (x, y, z,) => {} //│ } //│ :lift fun f(x,y,z) = class C{ class A{ fun foo = new B fun bar1 = x } class B{ fun foo = new A fun bar2 = y } fun boo = (new A).bar1 + B().bar2 + z } //│ |#fun| |f|(|x|,|y|,|z|)| |#=| |→|#class| |C|{|→|#class| |A|{|→|#fun| |foo| |#=| |#new| |B|↵|#fun| |bar1| |#=| |x|←|↵|}|↵|#class| |B|{|→|#fun| |foo| |#=| |#new| |A|↵|#fun| |bar2| |#=| |y|←|↵|}|↵|#fun| |boo| |#=| |(|#new| |A|)|.bar1| |+| |B|(||)|.bar2| |+| |z|←|↵|}|←| //│ Parsed: {fun f = (x, y, z,) => {class C {class A {fun foo = new B; fun bar1 = x}; class B {fun foo = new A; fun bar2 = y}; fun boo = +(+(('(' new A ')').bar1, (B()).bar2,), z,)}}} //│ Lifted: //│ TypingUnit { //│ class C$1_A$2([par$C$1,]) { //│ fun foo = (new C$1_B$3)((this).par$C$1,) //│ fun bar1 = ((this).par$C$1).x //│ } //│ class C$1_B$3([par$C$1,]) { //│ fun foo = (new C$1_A$2)((this).par$C$1,) //│ fun bar2 = ((this).par$C$1).y //│ } //│ class C$1([x, y, z,]) { //│ fun boo = +(+(('(' (new C$1_A$2)(this,) ')').bar1, (C$1_B$3(this,)).bar2,), (this).z,) //│ } //│ fun f$1 = (x, y, z,) => {} //│ } //│ :lift fun f(y) = let g(x) = x + y + 1 class Foo(x) { fun h = g(x) } Foo(1).h Foo(x).h //│ |#fun| |f|(|y|)| |#=|→|#let| |g|(|x|)| |#=| |x| |+| |y| |+| |1|↵|#class| |Foo|(|x|)| |{|→|#fun| |h| |#=| |g|(|x|)|←|↵|}|↵|Foo|(|1|)|.h| |↵|Foo|(|x|)|.h|←| //│ Parsed: {fun f = (y,) => {let g = (x,) => +(+(x, y,), 1,); class Foo(x,) {fun h = g(x,)}; (Foo(1,)).h; (Foo(x,)).h}} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]) {fun h = g$2((this).x, y,)} //│ let g$2 = (x, y,) => +(+(x, y,), 1,) //│ fun f$1 = (y,) => {(Foo$1(1, y,)).h; (Foo$1(x, y,)).h} //│ } //│ :lift fun f(x) = let g(x) = let h(x) = x + 2 Foo(h(x), x).bar class Foo(x, y) { fun bar = g(x)+y } Foo(x, x).bar //│ |#fun| |f|(|x|)| |#=|→|#let| |g|(|x|)| |#=| |→|#let| |h|(|x|)| |#=| |x| |+| |2|↵|Foo|(|h|(|x|)|,| |x|)|.bar|←|↵|#class| |Foo|(|x|,| |y|)| |{|→|#fun| |bar| |#=| |g|(|x|)|+|y|←|↵|}|↵|Foo|(|x|,| |x|)|.bar|←| //│ Parsed: {fun f = (x,) => {let g = (x,) => {let h = (x,) => +(x, 2,); (Foo(h(x,), x,)).bar}; class Foo(x, y,) {fun bar = +(g(x,), y,)}; (Foo(x, x,)).bar}} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]) {fun bar = +(g$2((this).x,), (this).y,)} //│ let h$3 = (x,) => +(x, 2,) //│ let g$2 = (x,) => {(Foo$1(h$3(x,), x,)).bar} //│ fun f$1 = (x,) => {(Foo$1(x, x,)).bar} //│ } //│ :lift class Foo(x, y) extends Bar(y, x), Baz(x + y) //│ |#class| |Foo|(|x|,| |y|)| |#extends| |Bar|(|y|,| |x|)|,| |Baz|(|x| |+| |y|)| //│ Parsed: {class Foo(x, y,): Bar(y, x,), Baz(+(x, y,),) {}} //│ Lifted: //│ TypingUnit { //│ class Foo$1([x, y,]): Bar((this).y, (this).x,), Baz(+((this).x, (this).y,),) {} //│ } //│ :lift fun foo(x: T): string = class A(y) extends B, C(y: U) { fun bar = this } "rua" //│ |#fun| |foo|‹|T|,| |U|›|(|x|#:| |T|)|#:| |string| |#=| |→|#class| |A|(|y|)| |#extends| |B|‹|T|›|,| |C|(|y|#:| |U|)| |{|→|#fun| |bar| |#=| |this|←|↵|}|↵|"rua"|←| //│ Parsed: {fun foo = (x: T,) => {class A(y,): B‹T›, C(y: U,) {fun bar = this}; "rua"} : string} //│ Lifted: //│ TypingUnit { //│ class A$1[T,U]([y,]): B‹T›, C(y: U,) {fun bar = this} //│ fun foo$1[T, U] = (x: T,) => {"rua"} : string //│ } //│ :lift class A{ class B{ fun f = x => y => x fun g: T => B => T } } //│ |#class| |A|‹|T|›|{|→|#class| |B|{|→|#fun| |f| |#=| |x| |#=>| |y| |#=>| |x|↵|#fun| |g|#:| |T| |#=>| |B| |#=>| |T|←|↵|}|←|↵|}| //│ Parsed: {class A‹T› {class B {fun f = (x,) => (y,) => x; fun g: T -> B -> T}}} //│ Lifted: //│ TypingUnit { //│ class A$1_B$2_Lambda1$1$3([par$A$1_B$2, x,]) {fun apply = (y,) => (this).x} //│ class A$1_B$2[T]([par$A$1,]) { //│ fun f = (x,) => {A$1_B$2_Lambda1$1$3(this, x,)} //│ fun g = T -> A$1_B$2 -> T //│ } //│ class A$1[T]([]) {} //│ } //│ :lift class Foo{ class RectangleBox: Box & { breadth: T } class StackedRectangleBoxes : RectangleBox & { size: N } class Bar: {any: RectangleBox => StackedRectangleBoxes} } //│ |#class| |Foo|‹|T|›|{|→|#class| |RectangleBox|#:| |Box|‹|T|›| |&| |{| |breadth|#:| |T| |}|↵|#class| |StackedRectangleBoxes|‹|N|›| |#:| |RectangleBox|‹|T|›| |&| |{| |size|#:| |N| |}|↵|#class| |Bar|#:| |{|any|#:| |RectangleBox| |#=>| |StackedRectangleBoxes|}|←|↵|}| //│ Parsed: {class Foo‹T› {class RectangleBox: Box[T] & {breadth: T} {}; class StackedRectangleBoxes‹N›: RectangleBox[T] & {size: N} {}; class Bar: {any: RectangleBox -> StackedRectangleBoxes} {}}} //│ Lifted: //│ TypingUnit { //│ class Foo$1_RectangleBox$2([par$Foo$1,]) {} //│ class Foo$1_StackedRectangleBoxes$3[N]([par$Foo$1,]) {} //│ class Foo$1_Bar$4([par$Foo$1,]) {} //│ class Foo$1[T]([]) {} //│ } //│ :lift class Func { fun apply: T => U } class Lambda : Func {} fun ctx(a,b) = fun foo(f: Func, x) = f.apply(x) foo(new Lambda{ fun apply(x) = a+x }, b) //│ |#class| |Func|‹|T|,| |U|›| |{|→|#fun| |apply|#:| |T| |#=>| |U|←|↵|}|↵|#class| |Lambda|‹|T|,| |U|›| |#:| |Func|‹|T|,| |U|›| |{||}|↵|#fun| |ctx|(|a|,|b|)| |#=|→|#fun| |foo|(|f|#:| |Func|,| |x|)| |#=| |→|f|.apply|(|x|)|←|↵|foo|(|#new| |Lambda|{|→|#fun| |apply|(|x|)| |#=| |a|+|x|←|↵|}|,| |b|)|←| //│ Parsed: {class Func‹T, U› {fun apply: T -> U}; class Lambda‹T, U›: Func[T, U] {}; fun ctx = (a, b,) => {fun foo = (f: Func, x,) => {(f).apply(x,)}; foo(new Lambda {fun apply = (x,) => +(a, x,)}, b,)}} //│ Lifted: //│ TypingUnit { //│ class Func$1[T,U]([]) {fun apply = T -> U} //│ class Lambda$2[T,U]([]) {} //│ fun foo$2 = (f: Func$1, x,) => {(f).apply(x,)} //│ fun ctx$1 = (a, b,) => {foo$2(new Lambda$2([]) {}, b,)} //│ } //│ :lift fun f(T) = new T() f(MyClass) //│ |#fun| |f|(|T|)| |#=| |→|#new| |T|(||)|←|↵|f|(|MyClass|)| //│ Parsed: {fun f = (T,) => {(new T)()}; f(MyClass,)} //│ Lifted: //│ Lifting failed: mlscript.codegen.CodeGenError: Cannot find type T. Class values are not supported in lifter. //│ :lift class A { fun foo = fun bar = foo() bar() } //│ |#class| |A| |{|→|#fun| |foo| |#=| |→|#fun| |bar| |#=| |foo|(||)|↵|bar|(||)|←|←|↵|}| //│ Parsed: {class A {fun foo = {fun bar = foo(); bar()}}} //│ Lifted: //│ TypingUnit { //│ class A$1([]) {fun foo = {bar$1(this,)}} //│ fun bar$1 = (this).foo() //│ } //│ ================================================ FILE: compiler/shared/test/diff/Lifter/NestedClasses.mls ================================================ :NewDefs :e // FIXME: Nested class references not updated :lift class X() { class Y() {} } X.Y() //│ Lifted: //│ TypingUnit { //│ class X$1_Y$2([par$X$1,]) {} //│ class X$1([]) {} //│ Code(List((X$1).Y())) //│ } //│ ╔══[ERROR] Class parameters currently need type annotations //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.8: X.Y() //│ ║ ^^ //│ ╙── expression of type `() -> X$1` does not have field 'Y' //│ class X$1_Y$2(par$X$1: error) //│ class X$1() //│ error //│ res //│ Runtime error: //│ TypeError: X$1.Y is not a function ================================================ FILE: compiler/shared/test/diff/Lifter/NestedFuncs.mls ================================================ :NewDefs :e // FIXME: Incorrect lifting :lift fun test(a) = let f(x) = a + x f test(6)(4) //│ Lifted: //│ TypingUnit { //│ let f$2 = (x, a,) => +(a, x,) //│ fun test$1 = (a,) => {f$2} //│ Code(List(test$1(6,)(4,))) //│ } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.8: test(6)(4) //│ ║ ^^^^ //│ ╟── argument of type `[4]` does not match type `[?a, ?b]` //│ ║ l.8: test(6)(4) //│ ║ ^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.6: let f(x) = a + x //│ ╙── ^^^^^^ //│ val f$2: (Int, Int) -> Int //│ fun test$1: anything -> (Int, Int) -> Int //│ Int | error //│ f$2 //│ = [Function: f$2] //│ res //│ = NaN ================================================ FILE: compiler/shared/test/diff/Lifter/ParameterizedInheritance.mls ================================================ :NewDefs // FIXME: correctly lift parameterized inheritance :lift val c = 5 class Sup(val a: Int){ virtual fun foo = () -> a } class Sub(b: Int) extends Sup(b+b){ } class Sub2(c: Int) extends Sub(c+c){ fun foo = () -> c+c } (Sub(10)).foo() (Sub2(c)).foo() //│ Lifted: //│ TypingUnit { //│ class Sup$1([val a: Int,]) {fun foo = () => (this).a} //│ class Sub$2([b: Int,]): Sup$1(+((this).b, (this).b,),) {} //│ class Sub2$3([c: Int,]): Sub$2(+(c$1, c$1,),) {fun foo = () => +(c$1, c$1,)} //│ let c$1 = 5 //│ Code(List(('(' Sub$2(10,) ')').foo())) //│ Code(List(('(' Sub2$3(c$1,) ')').foo())) //│ } //│ ╔══[ERROR] identifier not found: this //│ ╙── //│ ╔══[ERROR] identifier not found: this //│ ╙── //│ class Sup$1(a: Int) { //│ fun foo: () -> Int //│ } //│ class Sub$2(b: Int) extends Sup$1 { //│ fun foo: () -> Int //│ } //│ class Sub2$3(c: Int) extends Sub$2, Sup$1 { //│ fun foo: () -> Int //│ } //│ val c$1: 5 //│ Int //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding c$1 ================================================ FILE: compiler/shared/test/diff/Lifter/TypedClassParams.mls ================================================ :NewDefs :lift class A() {} class B(foo: A) {} //│ Lifted: //│ TypingUnit {class A$1([]) {}; class B$2([foo: A$1,]) {}} //│ class A$1() //│ class B$2(foo: A$1) ================================================ FILE: compiler/shared/test/diff-ir/Class.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :genCpp :runCpp :showCpp module Fn extends Callable { fun apply1(x) = builtin("println", x) } class Fn2(a) extends Callable { fun apply1(x) = builtin("println", a) builtin("println", x) } class Demo(n) { fun x() = n } fun f(fn) = fn(1) fun main() = let d1 = Demo(2) Demo.x(d1) let print = Fn() Fn.apply1(print, 3) f(print) let print2 = Fn2(4) Fn2.apply1(print2, 5) print2(6) f(print2) main() //│ |#module| |Fn| |#extends| |Callable| |{|→|#fun| |apply1|(|x|)| |#=| |builtin|(|"println"|,| |x|)|←|↵|}|↵|#class| |Fn2|(|a|)| |#extends| |Callable| |{|→|#fun| |apply1|(|x|)| |#=|→|builtin|(|"println"|,| |a|)|↵|builtin|(|"println"|,| |x|)|←|←|↵|}|↵|#class| |Demo|(|n|)| |{|→|#fun| |x|(||)| |#=| |n|←|↵|}|↵|#fun| |f|(|fn|)| |#=| |fn|(|1|)|↵|#fun| |main|(||)| |#=|→|#let| |d1| |#=| |Demo|(|2|)|↵|Demo|.x|(|d1|)|↵|#let| |print| |#=| |Fn|(||)|↵|Fn|.apply1|(|print|,| |3|)|↵|f|(|print|)|↵|#let| |print2| |#=| |Fn2|(|4|)|↵|Fn2|.apply1|(|print2|,| |5|)|↵|print2|(|6|)|↵|f|(|print2|)|←|↵|main|(||)| //│ Parsed: {module Fn: Callable {fun apply1 = (x,) => builtin("println", x,)}; class Fn2(a,): Callable {fun apply1 = (x,) => {builtin("println", a,); builtin("println", x,)}}; class Demo(n,) {fun x = () => n}; fun f = (fn,) => fn(1,); fun main = () => {let d1 = Demo(2,); (Demo).x(d1,); let print = Fn(); (Fn).apply1(print, 3,); f(print,); let print2 = Fn2(4,); (Fn2).apply1(print2, 5,); print2(6,); f(print2,)}; main()} //│ //│ //│ IR: //│ Program: //│ class Fn() extends Callable { //│ def apply1(x$11) = //│ let x$12 = Callable.apply2(builtin,println,x$11) in -- #64 //│ x$12 -- #63 //│ } //│ class Fn2(a) extends Callable { //│ def apply1(x$13) = //│ let x$14 = Callable.apply2(builtin,println,a) in -- #80 //│ let x$15 = Callable.apply2(builtin,println,x$13) in -- #79 //│ x$15 -- #78 //│ } //│ class Demo(n) { //│ def x() = //│ n -- #81 //│ } //│ def f(fn$0) = //│ let x$1 = Callable.apply1(fn$0,1) in -- #8 //│ x$1 -- #7 //│ def main() = //│ let x$2 = Demo(2) in -- #56 //│ let x$3 = Demo.x(x$2) in -- #55 //│ let x$4 = Fn() in -- #54 //│ let x$5 = Fn.apply1(x$4,3) in -- #53 //│ let* (x$6) = f(x$4) in -- #52 //│ let x$7 = Fn2(4) in -- #51 //│ let x$8 = Fn2.apply1(x$7,5) in -- #50 //│ let x$9 = Callable.apply1(x$7,6) in -- #49 //│ let* (x$10) = f(x$7) in -- #48 //│ x$10 -- #47 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Cpp: //│ struct _mls_Demo: public _mlsObject { //│ _mlsValue _mls_n; //│ constexpr static inline const char *typeName = "Demo"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); std::printf("("); this->_mls_n.print(); std::printf(")"); } //│ virtual void destroy() override { _mlsValue::destroy(this->_mls_n); operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create(_mlsValue _mls_n) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Demo; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->_mls_n = _mls_n; return _mlsValue(_mlsVal); } //│ virtual _mlsValue _mls_x(){ //│ _mlsValue _mls_retval; //│ _mls_retval = _mls_n; //│ return _mls_retval; //│ } //│ }; //│ struct _mls_Fn: public _mls_Callable { //│ //│ constexpr static inline const char *typeName = "Fn"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); } //│ virtual void destroy() override { operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Fn; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } //│ virtual _mlsValue _mls_apply1(_mlsValue _mls_x_11){ //│ _mlsValue _mls_retval; //│ auto _mls_x_12 = _mls_builtin_println(_mls_x_11); //│ _mls_retval = _mls_x_12; //│ return _mls_retval; //│ } //│ }; //│ struct _mls_Fn2: public _mls_Callable { //│ _mlsValue _mls_a; //│ constexpr static inline const char *typeName = "Fn2"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); std::printf("("); this->_mls_a.print(); std::printf(")"); } //│ virtual void destroy() override { _mlsValue::destroy(this->_mls_a); operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create(_mlsValue _mls_a) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Fn2; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->_mls_a = _mls_a; return _mlsValue(_mlsVal); } //│ virtual _mlsValue _mls_apply1(_mlsValue _mls_x_13){ //│ _mlsValue _mls_retval; //│ auto _mls_x_14 = _mls_builtin_println(_mls_a); //│ auto _mls_x_15 = _mls_builtin_println(_mls_x_13); //│ _mls_retval = _mls_x_15; //│ return _mls_retval; //│ } //│ }; //│ _mlsValue _mls_f(_mlsValue _mls_fn_0){ //│ _mlsValue _mls_retval; //│ auto _mls_x_1 = _mlsMethodCall<_mls_Callable>(_mls_fn_0)->_mls_apply1(_mlsValue::fromIntLit(1)); //│ _mls_retval = _mls_x_1; //│ return _mls_retval; //│ } //│ _mlsValue _mls_main(){ //│ _mlsValue _mls_retval; //│ auto _mls_x_2 = _mlsValue::create<_mls_Demo>(_mlsValue::fromIntLit(2)); //│ auto _mls_x_3 = _mlsMethodCall<_mls_Demo>(_mls_x_2)->_mls_x(); //│ auto _mls_x_4 = _mlsValue::create<_mls_Fn>(); //│ auto _mls_x_5 = _mlsMethodCall<_mls_Fn>(_mls_x_4)->_mls_apply1(_mlsValue::fromIntLit(3)); //│ auto _mls_x_6 = _mls_f(_mls_x_4); //│ auto _mls_x_7 = _mlsValue::create<_mls_Fn2>(_mlsValue::fromIntLit(4)); //│ auto _mls_x_8 = _mlsMethodCall<_mls_Fn2>(_mls_x_7)->_mls_apply1(_mlsValue::fromIntLit(5)); //│ auto _mls_x_9 = _mlsMethodCall<_mls_Callable>(_mls_x_7)->_mls_apply1(_mlsValue::fromIntLit(6)); //│ auto _mls_x_10 = _mls_f(_mls_x_7); //│ _mls_retval = _mls_x_10; //│ return _mls_retval; //│ } //│ _mlsValue _mlsMain(){ //│ _mlsValue _mls_retval; //│ auto _mls_x_0 = _mls_main(); //│ _mls_retval = _mls_x_0; //│ return _mls_retval; //│ } //│ int main() { return _mlsLargeStack(_mlsMainWrapper); } //│ //│ //│ Execution succeeded: //│ 3 //│ 1 //│ 4 //│ 5 //│ 4 //│ 6 //│ 4 //│ 1 //│ Unit //│ ================================================ FILE: compiler/shared/test/diff-ir/Currying.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :interpIR :genCpp :runCpp fun add2c(a)(b) = a + b fun add2(a, b) = a + b fun add3c(a)(b)(c) = a + b + c fun main() = add2c(1)(2) add2(1, 2) add3c(1)(2)(3) main() //│ |#fun| |add2c|(|a|)|(|b|)| |#=| |a| |+| |b|↵|#fun| |add2|(|a|,| |b|)| |#=| |a| |+| |b|↵|#fun| |add3c|(|a|)|(|b|)|(|c|)| |#=| |a| |+| |b| |+| |c|↵|#fun| |main|(||)| |#=|→|add2c|(|1|)|(|2|)|↵|add2|(|1|,| |2|)|↵|add3c|(|1|)|(|2|)|(|3|)|←|↵|main|(||)| //│ Parsed: {fun add2c = (a,) => (b,) => +(a, b,); fun add2 = (a, b,) => +(a, b,); fun add3c = (a,) => (b,) => (c,) => +(+(a, b,), c,); fun main = () => {add2c(1,)(2,); add2(1, 2,); add3c(1,)(2,)(3,)}; main()} //│ //│ //│ IR: //│ Program: //│ class Lambda$0(a) extends Callable { //│ def apply1(b$1) = //│ let x$12 = +(a,b$1) in -- #58 //│ x$12 -- #57 //│ } //│ class Lambda$1(a) extends Callable { //│ def apply1(b$2) = //│ let x$14 = Lambda$2(a,b$2) in -- #60 //│ x$14 -- #59 //│ } //│ class Lambda$2(a,b) extends Callable { //│ def apply1(c$0) = //│ let x$15 = +(a,b) in -- #73 //│ let x$16 = +(x$15,c$0) in -- #72 //│ x$16 -- #71 //│ } //│ def add2c(a$0) = //│ let x$2 = Lambda$0(a$0) in -- #4 //│ x$2 -- #3 //│ def add2(a$1,b$0) = //│ let x$3 = +(a$1,b$0) in -- #11 //│ x$3 -- #10 //│ def add3c(a$2) = //│ let x$5 = Lambda$1(a$2) in -- #13 //│ x$5 -- #12 //│ def main() = //│ let* (x$6) = add2c(1) in -- #45 //│ let x$7 = Callable.apply1(x$6,2) in -- #44 //│ let* (x$8) = add2(1,2) in -- #43 //│ let* (x$9) = add3c(1) in -- #42 //│ let x$10 = Callable.apply1(x$9,2) in -- #41 //│ let x$11 = Callable.apply1(x$10,3) in -- #40 //│ x$11 -- #39 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 6 //│ //│ //│ Execution succeeded: //│ 6 //│ ================================================ FILE: compiler/shared/test/diff-ir/IR.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat module Either[A, B] class Left[A, B](x: A) extends Either[A, B] class Right[A, B](y: B) extends Either[A, B] class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O, _16: Either, _17: Left, _18: Right) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#module| |Either|[|A|,| |B|]|↵|#class| |Left|[|A|,| |B|]|(|x|#:| |A|)| |#extends| |Either|[|A|,| |B|]|↵|#class| |Right|[|A|,| |B|]|(|y|#:| |B|)| |#extends| |Either|[|A|,| |B|]|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|,| |_16|#:| |Either|,| |_17|#:| |Left|,| |_18|#:| |Right|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; module Either‹A, B› {}; class Left‹A, B›(x: A,): Either‹A, B› {}; class Right‹A, B›(y: B,): Either‹A, B› {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O, _16: Either, _17: Left, _18: Right,) {}} //│ //│ Preluded. //│ :interpIR fun mktup2(x, y) = mktup(x, y) fun mktup(x, y) = Pair(x, y) fun foo() = mktup2(1, 2) foo() //│ |#fun| |mktup2|(|x|,| |y|)| |#=| |mktup|(|x|,| |y|)|↵|#fun| |mktup|(|x|,| |y|)| |#=| |Pair|(|x|,| |y|)|↵|#fun| |foo|(||)| |#=|→|mktup2|(|1|,| |2|)|←|↵|foo|(||)| //│ Parsed: {fun mktup2 = (x, y,) => mktup(x, y,); fun mktup = (x, y,) => Pair(x, y,); fun foo = () => {mktup2(1, 2,)}; foo()} //│ //│ //│ IR: //│ Program: //│ //│ def mktup2(x$1,y$0) = //│ let* (x$2) = mktup(x$1,y$0) in -- #9 //│ x$2 -- #8 //│ def mktup(x$3,y$1) = //│ let x$4 = Pair(x$3,y$1) in -- #16 //│ x$4 -- #15 //│ def foo() = //│ let* (x$5) = mktup2(1,2) in -- #23 //│ x$5 -- #22 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ Pair(1,2) //│ :interpIR fun foo(pair) = if pair is Pair(x, y) then Pair(x, y) fun bar() = foo(Pair(1, 2)) bar() //│ |#fun| |foo|(|pair|)| |#=|→|#if| |pair| |is|→|Pair|(|x|,| |y|)| |#then| |Pair|(|x|,| |y|)|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |2|)|)|←|↵|bar|(||)| //│ Parsed: {fun foo = (pair,) => {if pair is ‹(Pair(x, y,)) then Pair(x, y,)›}; fun bar = () => {foo(Pair(1, 2,),)}; bar()} //│ //│ //│ IR: //│ Program: //│ //│ def foo(pair$0) = //│ case pair$0 of -- #23 //│ Pair => //│ let x$2 = Pair.y(pair$0) in -- #22 //│ let x$3 = Pair.x(pair$0) in -- #21 //│ let x$4 = Pair(x$3,x$2) in -- #20 //│ jump j$0(x$4) -- #19 //│ def j$0(x$1) = //│ x$1 -- #4 //│ def bar() = //│ let x$5 = Pair(1,2) in -- #34 //│ let* (x$6) = foo(x$5) in -- #33 //│ x$6 -- #32 //│ let* (x$0) = bar() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ Pair(1,2) //│ :interpIR fun silly(pair) = let _ = 0 let n = if pair is Pair(x1, x2) then if pair is Pair (x3, x4) then x3 + 1 n + 1 fun foo() = let a = Pair(0, 1) let b = silly(a) b foo() //│ |#fun| |silly|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#let| |n| |#=| |#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then|→|#if| |pair| |is|→|Pair| |(|x3|,| |x4|)| |#then| |x3| |+| |1|←|←|←|↵|n| |+| |1|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |silly|(|a|)|↵|b|←|↵|foo|(||)| //│ Parsed: {fun silly = (pair,) => {let _ = 0; let n = if pair is ‹(Pair(x1, x2,)) then {if pair is ‹(Pair(x3, x4,)) then +(x3, 1,)›}›; +(n, 1,)}; fun foo = () => {let a = Pair(0, 1,); let b = silly(a,); b}; foo()} //│ //│ //│ IR: //│ Program: //│ //│ def silly(pair$0) = //│ let x$1 = 0 in -- #46 //│ case pair$0 of -- #45 //│ Pair => //│ let x$4 = Pair.y(pair$0) in -- #44 //│ let x$5 = Pair.x(pair$0) in -- #43 //│ case pair$0 of -- #42 //│ Pair => //│ let x$7 = Pair.y(pair$0) in -- #41 //│ let x$8 = Pair.x(pair$0) in -- #40 //│ let x$9 = +(x$8,1) in -- #39 //│ jump j$1(x$9) -- #38 //│ def j$0(x$2) = //│ let x$3 = +(x$2,1) in -- #12 //│ x$3 -- #11 //│ def j$1(x$6) = //│ jump j$0(x$6) -- #23 //│ def foo() = //│ let x$10 = Pair(0,1) in -- #59 //│ let* (x$11) = silly(x$10) in -- #58 //│ x$11 -- #57 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 2 //│ :interpIR fun inc_fst(pair) = let c = 2 if pair is Pair(x1, x2) then x1 + c fun foo() = let a = Pair(0, 1) let b = inc_fst(a) b foo() //│ |#fun| |inc_fst|(|pair|)| |#=|→|#let| |c| |#=| |2|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x1| |+| |c|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |a| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |inc_fst|(|a|)|↵|b|←|↵|foo|(||)| //│ Parsed: {fun inc_fst = (pair,) => {let c = 2; if pair is ‹(Pair(x1, x2,)) then +(x1, c,)›}; fun foo = () => {let a = Pair(0, 1,); let b = inc_fst(a,); b}; foo()} //│ //│ //│ IR: //│ Program: //│ //│ def inc_fst(pair$0) = //│ let x$1 = 2 in -- #25 //│ case pair$0 of -- #24 //│ Pair => //│ let x$3 = Pair.y(pair$0) in -- #23 //│ let x$4 = Pair.x(pair$0) in -- #22 //│ let x$5 = +(x$4,x$1) in -- #21 //│ jump j$0(x$5) -- #20 //│ def j$0(x$2) = //│ x$2 -- #5 //│ def foo() = //│ let x$6 = Pair(0,1) in -- #38 //│ let* (x$7) = inc_fst(x$6) in -- #37 //│ x$7 -- #36 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 2 //│ :interpIR fun inc_fst(pair) = let _ = 0 if pair is Pair(x1, x2) then x2 + 1 fun foo() = let b = inc_fst(Pair(0, 1)) b foo() //│ |#fun| |inc_fst|(|pair|)| |#=|→|#let| |_| |#=| |0|↵|#if| |pair| |is|→|Pair|(|x1|,| |x2|)| |#then| |x2| |+| |1|←|←|↵|#fun| |foo|(||)| |#=|→|#let| |b| |#=| |inc_fst|(|Pair|(|0|,| |1|)|)|↵|b|←|↵|foo|(||)| //│ Parsed: {fun inc_fst = (pair,) => {let _ = 0; if pair is ‹(Pair(x1, x2,)) then +(x2, 1,)›}; fun foo = () => {let b = inc_fst(Pair(0, 1,),); b}; foo()} //│ //│ //│ IR: //│ Program: //│ //│ def inc_fst(pair$0) = //│ let x$1 = 0 in -- #25 //│ case pair$0 of -- #24 //│ Pair => //│ let x$3 = Pair.y(pair$0) in -- #23 //│ let x$4 = Pair.x(pair$0) in -- #22 //│ let x$5 = +(x$3,1) in -- #21 //│ jump j$0(x$5) -- #20 //│ def j$0(x$2) = //│ x$2 -- #5 //│ def foo() = //│ let x$6 = Pair(0,1) in -- #37 //│ let* (x$7) = inc_fst(x$6) in -- #36 //│ x$7 -- #35 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 2 //│ :interpIR fun foo(a, b) = let t = if a is Left(x) then Left(x + 1) Right(y) then Right(b) if t is Left(x) then x Right(y) then y fun bar() = foo(Right(2), 2) bar() //│ |#fun| |foo|(|a|,| |b|)| |#=|→|#let| |t| |#=| |#if| |a| |is|→|Left|(|x|)| |#then| |Left|(|x| |+| |1|)|↵|Right|(|y|)| |#then| |Right|(|b|)|←|↵|#if| |t| |is|→|Left|(|x|)| |#then| |x|↵|Right|(|y|)| |#then| |y|←|←|↵|#fun| |bar|(||)| |#=|→|foo|(|Right|(|2|)|,| |2|)|←|↵|bar|(||)| //│ Parsed: {fun foo = (a, b,) => {let t = if a is ‹(Left(x,)) then Left(+(x, 1,),); (Right(y,)) then Right(b,)›; if t is ‹(Left(x,)) then x; (Right(y,)) then y›}; fun bar = () => {foo(Right(2,), 2,)}; bar()} //│ //│ //│ IR: //│ Program: //│ //│ def foo(a$0,b$0) = //│ case a$0 of -- #50 //│ Left => //│ let x$5 = Left.x(a$0) in -- #38 //│ let x$6 = +(x$5,1) in -- #37 //│ let x$7 = Left(x$6) in -- #36 //│ jump j$0(x$7) -- #35 //│ Right => //│ let x$8 = Right.y(a$0) in -- #49 //│ let x$9 = Right(b$0) in -- #48 //│ jump j$0(x$9) -- #47 //│ def j$1(x$2) = //│ x$2 -- #6 //│ def j$0(x$1) = //│ case x$1 of -- #21 //│ Left => //│ let x$3 = Left.x(x$1) in -- #13 //│ jump j$1(x$3) -- #12 //│ Right => //│ let x$4 = Right.y(x$1) in -- #20 //│ jump j$1(x$4) -- #19 //│ def bar() = //│ let x$10 = Right(2) in -- #61 //│ let* (x$11) = foo(x$10,2) in -- #60 //│ x$11 -- #59 //│ let* (x$0) = bar() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 2 //│ :interpIR fun foo(a) = Pair.x(a) + Pair.y(a) fun bar() = foo(Pair(1, 0)) bar() //│ |#fun| |foo|(|a|)| |#=| |Pair|.x|(|a|)| |+| |Pair|.y|(|a|)|↵|#fun| |bar|(||)| |#=|→|foo|(|Pair|(|1|,| |0|)|)|←|↵|bar|(||)| //│ Parsed: {fun foo = (a,) => +((Pair).x(a,), (Pair).y(a,),); fun bar = () => {foo(Pair(1, 0,),)}; bar()} //│ //│ //│ IR: //│ Program: //│ //│ def foo(a$0) = //│ let x$1 = Pair.x(a$0) in -- #17 //│ let x$2 = Pair.y(a$0) in -- #16 //│ let x$3 = +(x$1,x$2) in -- #15 //│ x$3 -- #14 //│ def bar() = //│ let x$4 = Pair(1,0) in -- #28 //│ let* (x$5) = foo(x$4) in -- #27 //│ x$5 -- #26 //│ let* (x$0) = bar() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 1 //│ :interpIR fun foo(a, b) = let x1 = Pair.x(a) let y1 = Pair.y(a) let x2 = Pair.x(b) let y2 = Pair.y(b) x1 + y1 + x2 + y2 fun bar(c) = foo(Pair(0, 1), c) foo(c, Pair(2, 3)) foo(Pair(0, 1), Pair(2, 3)) fun baz() = bar(Pair(4,5)) baz() //│ |#fun| |foo|(|a|,| |b|)| |#=|→|#let| |x1| |#=| |Pair|.x|(|a|)|↵|#let| |y1| |#=| |Pair|.y|(|a|)|↵|#let| |x2| |#=| |Pair|.x|(|b|)|↵|#let| |y2| |#=| |Pair|.y|(|b|)|↵|x1| |+| |y1| |+| |x2| |+| |y2|←|↵|#fun| |bar|(|c|)| |#=|→|foo|(|Pair|(|0|,| |1|)|,| |c|)|↵|foo|(|c|,| |Pair|(|2|,| |3|)|)|↵|foo|(|Pair|(|0|,| |1|)|,| |Pair|(|2|,| |3|)|)|←|↵|#fun| |baz|(||)| |#=|→|bar|(|Pair|(|4|,|5|)|)|←|↵|baz|(||)| //│ Parsed: {fun foo = (a, b,) => {let x1 = (Pair).x(a,); let y1 = (Pair).y(a,); let x2 = (Pair).x(b,); let y2 = (Pair).y(b,); +(+(+(x1, y1,), x2,), y2,)}; fun bar = (c,) => {foo(Pair(0, 1,), c,); foo(c, Pair(2, 3,),); foo(Pair(0, 1,), Pair(2, 3,),)}; fun baz = () => {bar(Pair(4, 5,),)}; baz()} //│ //│ //│ IR: //│ Program: //│ //│ def foo(a$0,b$0) = //│ let x$1 = Pair.x(a$0) in -- #41 //│ let x$2 = Pair.y(a$0) in -- #40 //│ let x$3 = Pair.x(b$0) in -- #39 //│ let x$4 = Pair.y(b$0) in -- #38 //│ let x$5 = +(x$1,x$2) in -- #37 //│ let x$6 = +(x$5,x$3) in -- #36 //│ let x$7 = +(x$6,x$4) in -- #35 //│ x$7 -- #34 //│ def bar(c$0) = //│ let x$8 = Pair(0,1) in -- #86 //│ let* (x$9) = foo(x$8,c$0) in -- #85 //│ let x$10 = Pair(2,3) in -- #84 //│ let* (x$11) = foo(c$0,x$10) in -- #83 //│ let x$12 = Pair(0,1) in -- #82 //│ let x$13 = Pair(2,3) in -- #81 //│ let* (x$14) = foo(x$12,x$13) in -- #80 //│ x$14 -- #79 //│ def baz() = //│ let x$15 = Pair(4,5) in -- #97 //│ let* (x$16) = bar(x$15) in -- #96 //│ x$16 -- #95 //│ let* (x$0) = baz() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 6 //│ :interpIR fun foo() = let p = Pair(0, 1) let b = Pair.x(p) b foo() //│ |#fun| |foo|(||)| |#=|→|#let| |p| |#=| |Pair|(|0|,| |1|)|↵|#let| |b| |#=| |Pair|.x|(|p|)|↵|b|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {let p = Pair(0, 1,); let b = (Pair).x(p,); b}; foo()} //│ //│ //│ IR: //│ Program: //│ //│ def foo() = //│ let x$1 = Pair(0,1) in -- #15 //│ let x$2 = Pair.x(x$1) in -- #14 //│ x$2 -- #13 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 0 //│ :interpIR fun foo() = bar(S(O)) fun bar(x) = baz(x) fun baz(x) = if x is S(s) then s O then x foo() //│ |#fun| |foo|(||)| |#=|→|bar|(|S|(|O|)|)|←|↵|#fun| |bar|(|x|)| |#=|→|baz|(|x|)|←|↵|#fun| |baz|(|x|)| |#=|→|#if| |x| |is|→|S|(|s|)| |#then| |s|↵|O| |#then| |x|←|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {bar(S(O,),)}; fun bar = (x,) => {baz(x,)}; fun baz = (x,) => {if x is ‹(S(s,)) then s; (O) then x›}; foo()} //│ //│ //│ IR: //│ Program: //│ //│ def foo() = //│ let x$1 = O() in -- #12 //│ let x$2 = S(x$1) in -- #11 //│ let* (x$3) = bar(x$2) in -- #10 //│ x$3 -- #9 //│ def bar(x$4) = //│ let* (x$5) = baz(x$4) in -- #17 //│ x$5 -- #16 //│ def baz(x$6) = //│ case x$6 of -- #29 //│ S => //│ let x$8 = S.s(x$6) in -- #26 //│ jump j$0(x$8) -- #25 //│ O => //│ jump j$0(x$6) -- #28 //│ def j$0(x$7) = //│ x$7 -- #19 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ O() //│ ================================================ FILE: compiler/shared/test/diff-ir/IRComplex.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :interpIR class A(x, y, z) class B(m, n) fun complex_foo(t) = let r = if t is A(x, y, z) then x + y * z B(m, n) then m - n let s = B(1, 2) let u = if s is A(x, y, z) then 3 B(m, n) then 4 r + u fun bar() = complex_foo(A(6, 7, 8)) complex_foo(B(9, 10)) bar() //│ |#class| |A|(|x|,| |y|,| |z|)|↵|#class| |B|(|m|,| |n|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |r| |#=| |#if| |t| |is|→|A|(|x|,| |y|,| |z|)| |#then| |x| |+| |y| |*| |z|↵|B|(|m|,| |n|)| |#then| |m| |-| |n|←|↵|#let| |s| |#=| |B|(|1|,| |2|)|↵|#let| |u| |#=| |#if| |s| |is|→|A|(|x|,| |y|,| |z|)| |#then| |3|↵|B|(|m|,| |n|)| |#then| |4|←|↵|r| |+| |u|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|6|,| |7|,| |8|)|)|↵|complex_foo|(|B|(|9|,| |10|)|)|←|↵|bar|(||)| //│ Parsed: {class A(x, y, z,) {}; class B(m, n,) {}; fun complex_foo = (t,) => {let r = if t is ‹(A(x, y, z,)) then +(x, *(y, z,),); (B(m, n,)) then -(m, n,)›; let s = B(1, 2,); let u = if s is ‹(A(x, y, z,)) then 3; (B(m, n,)) then 4›; +(r, u,)}; fun bar = () => {complex_foo(A(6, 7, 8,),); complex_foo(B(9, 10,),)}; bar()} //│ //│ //│ IR: //│ Program: //│ class A(x,y,z) //│ class B(m,n) //│ def complex_foo(t$0) = //│ case t$0 of -- #98 //│ A => //│ let x$10 = A.z(t$0) in -- #79 //│ let x$11 = A.y(t$0) in -- #78 //│ let x$12 = A.x(t$0) in -- #77 //│ let x$13 = *(x$11,x$10) in -- #76 //│ let x$14 = +(x$12,x$13) in -- #75 //│ jump j$0(x$14) -- #74 //│ B => //│ let x$15 = B.n(t$0) in -- #97 //│ let x$16 = B.m(t$0) in -- #96 //│ let x$17 = -(x$16,x$15) in -- #95 //│ jump j$0(x$17) -- #94 //│ def j$1(x$3,x$1) = //│ let x$4 = +(x$1,x$3) in -- #19 //│ x$4 -- #18 //│ def j$0(x$1) = //│ let x$2 = B(1,2) in -- #50 //│ case x$2 of -- #49 //│ A => //│ let x$5 = A.z(x$2) in -- #36 //│ let x$6 = A.y(x$2) in -- #35 //│ let x$7 = A.x(x$2) in -- #34 //│ jump j$1(3,x$1) -- #33 //│ B => //│ let x$8 = B.n(x$2) in -- #48 //│ let x$9 = B.m(x$2) in -- #47 //│ jump j$1(4,x$1) -- #46 //│ def bar() = //│ let x$18 = A(6,7,8) in -- #122 //│ let* (x$19) = complex_foo(x$18) in -- #121 //│ let x$20 = B(9,10) in -- #120 //│ let* (x$21) = complex_foo(x$20) in -- #119 //│ x$21 -- #118 //│ let* (x$0) = bar() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 3 //│ :interpIR class A(w, x) class B(y) class C(z) fun complex_foo(t) = let a = 1 + 2 let b = 1 * 2 let x = if t is A(x, y) then y B(x) then B(x + b) C(x) then C(0) let z = A(5, x) let v = B(6) let y = if x is A(x, y) then let m = x + a + b if y is A(x, y) then x B(x) then m C(x) then 0 B(x) then 2 C(x) then 3 if z is A(x, y) then x B(x) then 4 C(x) then if v is A(x, y) then x B(x) then 7 C(x) then 8 fun bar() = complex_foo(A(10, A(9, B(10)))) bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |y|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| //│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1, 2,); let b = *(1, 2,); let x = if t is ‹(A(x, y,)) then y; (B(x,)) then B(+(x, b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x, a,), b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} //│ //│ //│ IR: //│ Program: //│ class A(w,x) //│ class B(y) //│ class C(z) //│ def complex_foo(t$0) = //│ let x$1 = +(1,2) in -- #198 //│ let x$2 = *(1,2) in -- #197 //│ case t$0 of -- #196 //│ A => //│ let x$28 = A.x(t$0) in -- #167 //│ let x$29 = A.w(t$0) in -- #166 //│ jump j$0(x$28,x$1,x$2) -- #165 //│ B => //│ let x$30 = B.y(t$0) in -- #184 //│ let x$31 = +(x$30,x$2) in -- #183 //│ let x$32 = B(x$31) in -- #182 //│ jump j$0(x$32,x$1,x$2) -- #181 //│ C => //│ let x$33 = C.z(t$0) in -- #195 //│ let x$34 = C(0) in -- #194 //│ jump j$0(x$34,x$1,x$2) -- #193 //│ def j$2(x$7) = //│ x$7 -- #30 //│ def j$3(x$12) = //│ jump j$2(x$12) -- #56 //│ def j$1(x$6,x$4,x$5) = //│ case x$4 of -- #85 //│ A => //│ let x$8 = A.x(x$4) in -- #42 //│ let x$9 = A.w(x$4) in -- #41 //│ jump j$2(x$9) -- #40 //│ B => //│ let x$10 = B.y(x$4) in -- #49 //│ jump j$2(4) -- #48 //│ C => //│ let x$11 = C.z(x$4) in -- #84 //│ case x$5 of -- #83 //│ A => //│ let x$13 = A.x(x$5) in -- #68 //│ let x$14 = A.w(x$5) in -- #67 //│ jump j$3(x$14) -- #66 //│ B => //│ let x$15 = B.y(x$5) in -- #75 //│ jump j$3(7) -- #74 //│ C => //│ let x$16 = C.z(x$5) in -- #82 //│ jump j$3(8) -- #81 //│ def j$4(x$21,x$4,x$5) = //│ jump j$1(x$21,x$4,x$5) -- #107 //│ def j$0(x$3,x$1,x$2) = //│ let x$4 = A(5,x$3) in -- #155 //│ let x$5 = B(6) in -- #154 //│ case x$3 of -- #153 //│ A => //│ let x$17 = A.x(x$3) in -- #138 //│ let x$18 = A.w(x$3) in -- #137 //│ let x$19 = +(x$18,x$1) in -- #136 //│ let x$20 = +(x$19,x$2) in -- #135 //│ case x$17 of -- #134 //│ A => //│ let x$22 = A.x(x$17) in -- #119 //│ let x$23 = A.w(x$17) in -- #118 //│ jump j$4(x$23,x$4,x$5) -- #117 //│ B => //│ let x$24 = B.y(x$17) in -- #126 //│ jump j$4(x$20,x$4,x$5) -- #125 //│ C => //│ let x$25 = C.z(x$17) in -- #133 //│ jump j$4(0,x$4,x$5) -- #132 //│ B => //│ let x$26 = B.y(x$3) in -- #145 //│ jump j$1(2,x$4,x$5) -- #144 //│ C => //│ let x$27 = C.z(x$3) in -- #152 //│ jump j$1(3,x$4,x$5) -- #151 //│ def bar() = //│ let x$35 = B(10) in -- #219 //│ let x$36 = A(9,x$35) in -- #218 //│ let x$37 = A(10,x$36) in -- #217 //│ let* (x$38) = complex_foo(x$37) in -- #216 //│ x$38 -- #215 //│ let* (x$0) = bar() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 5 //│ :interpIR class A(w, x) class B(y) class C(z) fun complex_foo(t) = let a = 1 + 2 let b = 1 * 2 let x = if t is A(x, y) then A(x, C(0)) B(x) then B(x + b) C(x) then C(0) let z = A(5, x) let v = B(6) let y = if x is A(x, y) then let m = x + a + b if y is A(x, y) then x B(x) then m C(x) then 0 B(x) then 2 C(x) then 3 if z is A(x, y) then x B(x) then 4 C(x) then if v is A(x, y) then x B(x) then 7 C(x) then 8 fun bar() = complex_foo(A(10, A(9, B(10)))) bar() //│ |#class| |A|(|w|,| |x|)|↵|#class| |B|(|y|)|↵|#class| |C|(|z|)|↵|#fun| |complex_foo|(|t|)| |#=|→|#let| |a| |#=| |1| |+| |2|↵|#let| |b| |#=| |1| |*| |2|↵|#let| |x| |#=| |#if| |t| |is|→|A|(|x|,| |y|)| |#then| |A|(|x|,| |C|(|0|)|)|↵|B|(|x|)| |#then| |B|(|x| |+| |b|)|↵|C|(|x|)| |#then| |C|(|0|)|←|↵|#let| |z| |#=| |A|(|5|,| |x|)|↵|#let| |v| |#=| |B|(|6|)|↵|#let| |y| |#=| |#if| |x| |is|→|A|(|x|,| |y|)| |#then|→|#let| |m| |#=| |x| |+| |a| |+| |b|↵|#if| |y| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |m|↵|C|(|x|)| |#then| |0|←|←|↵|B|(|x|)| |#then| |2|↵|C|(|x|)| |#then| |3|←|↵|#if| |z| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |4|↵|C|(|x|)| |#then|→|#if| |v| |is|→|A|(|x|,| |y|)| |#then| |x|↵|B|(|x|)| |#then| |7|↵|C|(|x|)| |#then| |8|←|←|←|←|↵|#fun| |bar|(||)| |#=|→|complex_foo|(|A|(|10|,| |A|(|9|,| |B|(|10|)|)|)|)|←|↵|bar|(||)| //│ Parsed: {class A(w, x,) {}; class B(y,) {}; class C(z,) {}; fun complex_foo = (t,) => {let a = +(1, 2,); let b = *(1, 2,); let x = if t is ‹(A(x, y,)) then A(x, C(0,),); (B(x,)) then B(+(x, b,),); (C(x,)) then C(0,)›; let z = A(5, x,); let v = B(6,); let y = if x is ‹(A(x, y,)) then {let m = +(+(x, a,), b,); if y is ‹(A(x, y,)) then x; (B(x,)) then m; (C(x,)) then 0›}; (B(x,)) then 2; (C(x,)) then 3›; if z is ‹(A(x, y,)) then x; (B(x,)) then 4; (C(x,)) then {if v is ‹(A(x, y,)) then x; (B(x,)) then 7; (C(x,)) then 8›}›}; fun bar = () => {complex_foo(A(10, A(9, B(10,),),),)}; bar()} //│ //│ //│ IR: //│ Program: //│ class A(w,x) //│ class B(y) //│ class C(z) //│ def complex_foo(t$0) = //│ let x$1 = +(1,2) in -- #208 //│ let x$2 = *(1,2) in -- #207 //│ case t$0 of -- #206 //│ A => //│ let x$28 = A.x(t$0) in -- #177 //│ let x$29 = A.w(t$0) in -- #176 //│ let x$30 = C(0) in -- #175 //│ let x$31 = A(x$29,x$30) in -- #174 //│ jump j$0(x$31,x$1,x$2) -- #173 //│ B => //│ let x$32 = B.y(t$0) in -- #194 //│ let x$33 = +(x$32,x$2) in -- #193 //│ let x$34 = B(x$33) in -- #192 //│ jump j$0(x$34,x$1,x$2) -- #191 //│ C => //│ let x$35 = C.z(t$0) in -- #205 //│ let x$36 = C(0) in -- #204 //│ jump j$0(x$36,x$1,x$2) -- #203 //│ def j$2(x$7) = //│ x$7 -- #30 //│ def j$3(x$12) = //│ jump j$2(x$12) -- #56 //│ def j$1(x$6,x$4,x$5) = //│ case x$4 of -- #85 //│ A => //│ let x$8 = A.x(x$4) in -- #42 //│ let x$9 = A.w(x$4) in -- #41 //│ jump j$2(x$9) -- #40 //│ B => //│ let x$10 = B.y(x$4) in -- #49 //│ jump j$2(4) -- #48 //│ C => //│ let x$11 = C.z(x$4) in -- #84 //│ case x$5 of -- #83 //│ A => //│ let x$13 = A.x(x$5) in -- #68 //│ let x$14 = A.w(x$5) in -- #67 //│ jump j$3(x$14) -- #66 //│ B => //│ let x$15 = B.y(x$5) in -- #75 //│ jump j$3(7) -- #74 //│ C => //│ let x$16 = C.z(x$5) in -- #82 //│ jump j$3(8) -- #81 //│ def j$4(x$21,x$4,x$5) = //│ jump j$1(x$21,x$4,x$5) -- #107 //│ def j$0(x$3,x$1,x$2) = //│ let x$4 = A(5,x$3) in -- #155 //│ let x$5 = B(6) in -- #154 //│ case x$3 of -- #153 //│ A => //│ let x$17 = A.x(x$3) in -- #138 //│ let x$18 = A.w(x$3) in -- #137 //│ let x$19 = +(x$18,x$1) in -- #136 //│ let x$20 = +(x$19,x$2) in -- #135 //│ case x$17 of -- #134 //│ A => //│ let x$22 = A.x(x$17) in -- #119 //│ let x$23 = A.w(x$17) in -- #118 //│ jump j$4(x$23,x$4,x$5) -- #117 //│ B => //│ let x$24 = B.y(x$17) in -- #126 //│ jump j$4(x$20,x$4,x$5) -- #125 //│ C => //│ let x$25 = C.z(x$17) in -- #133 //│ jump j$4(0,x$4,x$5) -- #132 //│ B => //│ let x$26 = B.y(x$3) in -- #145 //│ jump j$1(2,x$4,x$5) -- #144 //│ C => //│ let x$27 = C.z(x$3) in -- #152 //│ jump j$1(3,x$4,x$5) -- #151 //│ def bar() = //│ let x$37 = B(10) in -- #229 //│ let x$38 = A(9,x$37) in -- #228 //│ let x$39 = A(10,x$38) in -- #227 //│ let* (x$40) = complex_foo(x$39) in -- #226 //│ x$40 -- #225 //│ let* (x$0) = bar() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 5 //│ ================================================ FILE: compiler/shared/test/diff-ir/IRRec.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :interpIR :genCpp fun fib(n) = if n < 2 then n else fib(n-1) + fib(n-2) fib(20) //│ |#fun| |fib|(|n|)| |#=| |#if| |n| |<| |2| |#then| |n| |#else| |fib|(|n|-|1|)| |+| |fib|(|n|-|2|)|↵|fib|(|20|)| //│ Parsed: {fun fib = (n,) => if (<(n, 2,)) then n else +(fib(-(n, 1,),), fib(-(n, 2,),),); fib(20,)} //│ //│ //│ IR: //│ Program: //│ //│ def fib(n$0) = //│ let x$1 = <(n$0,2) in -- #43 //│ if x$1 -- #42 //│ true => //│ jump j$0(n$0) -- #13 //│ false => //│ let x$3 = -(n$0,1) in -- #41 //│ let* (x$4) = fib(x$3) in -- #40 //│ let x$5 = -(n$0,2) in -- #39 //│ let* (x$6) = fib(x$5) in -- #38 //│ let x$7 = +(x$4,x$6) in -- #37 //│ jump j$0(x$7) -- #36 //│ def j$0(x$2) = //│ x$2 -- #11 //│ let* (x$0) = fib(20) in -- #4 //│ x$0 -- #3 //│ //│ Interpreted: //│ 6765 //│ :interpIR :genCpp fun odd(x) = if x == 0 then False else even(x-1) fun even(x) = if x == 0 then True else odd(x-1) fun foo() = odd(10) foo() //│ |#fun| |odd|(|x|)| |#=| |#if| |x| |==| |0| |#then| |False| |#else| |even|(|x|-|1|)|↵|#fun| |even|(|x|)| |#=| |#if| |x| |==| |0| |#then| |True| |#else| |odd|(|x|-|1|)|↵|#fun| |foo|(||)| |#=| |odd|(|10|)|↵|foo|(||)| //│ Parsed: {fun odd = (x,) => if (==(x, 0,)) then False else even(-(x, 1,),); fun even = (x,) => if (==(x, 0,)) then True else odd(-(x, 1,),); fun foo = () => odd(10,); foo()} //│ //│ //│ IR: //│ Program: //│ //│ def odd(x$1) = //│ let x$2 = ==(x$1,0) in -- #26 //│ if x$2 -- #25 //│ true => //│ let x$4 = False() in -- #12 //│ jump j$0(x$4) -- #11 //│ false => //│ let x$5 = -(x$1,1) in -- #24 //│ let* (x$6) = even(x$5) in -- #23 //│ jump j$0(x$6) -- #22 //│ def j$0(x$3) = //│ x$3 -- #9 //│ def even(x$7) = //│ let x$8 = ==(x$7,0) in -- #50 //│ if x$8 -- #49 //│ true => //│ let x$10 = True() in -- #36 //│ jump j$1(x$10) -- #35 //│ false => //│ let x$11 = -(x$7,1) in -- #48 //│ let* (x$12) = odd(x$11) in -- #47 //│ jump j$1(x$12) -- #46 //│ def j$1(x$9) = //│ x$9 -- #33 //│ def foo() = //│ let* (x$13) = odd(10) in -- #55 //│ x$13 -- #54 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ False() //│ :interpIR :genCpp fun not(x) = if x then False else True fun foo(x) = if x then None else Some(foo(not(x))) fun main() = foo(False) main() //│ |#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |None|→|#else| |Some|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=| |foo|(|False|)|↵|main|(||)| //│ Parsed: {fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then None else Some(foo(not(x,),),)}; fun main = () => foo(False,); main()} //│ //│ //│ IR: //│ Program: //│ //│ def not(x$1) = //│ if x$1 -- #11 //│ true => //│ let x$3 = False() in -- #7 //│ jump j$0(x$3) -- #6 //│ false => //│ let x$4 = True() in -- #10 //│ jump j$0(x$4) -- #9 //│ def j$0(x$2) = //│ x$2 -- #4 //│ def foo(x$5) = //│ if x$5 -- #31 //│ true => //│ let x$7 = None() in -- #16 //│ jump j$1(x$7) -- #15 //│ false => //│ let* (x$8) = not(x$5) in -- #30 //│ let* (x$9) = foo(x$8) in -- #29 //│ let x$10 = Some(x$9) in -- #28 //│ jump j$1(x$10) -- #27 //│ def j$1(x$6) = //│ x$6 -- #13 //│ def main() = //│ let x$11 = False() in -- #37 //│ let* (x$12) = foo(x$11) in -- #36 //│ x$12 -- #35 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ Some(None()) //│ :interpIR :genCpp fun aaa() = let m = 1 let n = 2 let p = 3 let q = 4 m + n - p + q fun bbb() = let x = aaa() x * 100 + 4 fun not(x) = if x then False else True fun foo(x) = if x then None else Some(foo(not(x))) fun main() = let x = foo(False) if x is None then aaa() Some(b1) then bbb() main() //│ |#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |None|→|#else| |Some|(|foo|(|not|(|x|)|)|)|←|←|↵|#fun| |main|(||)| |#=|→|#let| |x| |#=| |foo|(|False|)|↵|#if| |x| |is|→|None| |#then| |aaa|(||)|↵|Some|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(||)| //│ Parsed: {fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m, n,), p,), q,)}; fun bbb = () => {let x = aaa(); +(*(x, 100,), 4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then None else Some(foo(not(x,),),)}; fun main = () => {let x = foo(False,); if x is ‹(None) then aaa(); (Some(b1,)) then bbb()›}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def aaa() = //│ let x$1 = 1 in -- #29 //│ let x$2 = 2 in -- #28 //│ let x$3 = 3 in -- #27 //│ let x$4 = 4 in -- #26 //│ let x$5 = +(x$1,x$2) in -- #25 //│ let x$6 = -(x$5,x$3) in -- #24 //│ let x$7 = +(x$6,x$4) in -- #23 //│ x$7 -- #22 //│ def bbb() = //│ let* (x$8) = aaa() in -- #45 //│ let x$9 = *(x$8,100) in -- #44 //│ let x$10 = +(x$9,4) in -- #43 //│ x$10 -- #42 //│ def not(x$11) = //│ if x$11 -- #54 //│ true => //│ let x$13 = False() in -- #50 //│ jump j$0(x$13) -- #49 //│ false => //│ let x$14 = True() in -- #53 //│ jump j$0(x$14) -- #52 //│ def j$0(x$12) = //│ x$12 -- #47 //│ def foo(x$15) = //│ if x$15 -- #74 //│ true => //│ let x$17 = None() in -- #59 //│ jump j$1(x$17) -- #58 //│ false => //│ let* (x$18) = not(x$15) in -- #73 //│ let* (x$19) = foo(x$18) in -- #72 //│ let x$20 = Some(x$19) in -- #71 //│ jump j$1(x$20) -- #70 //│ def j$1(x$16) = //│ x$16 -- #56 //│ def main() = //│ let x$21 = False() in -- #96 //│ let* (x$22) = foo(x$21) in -- #95 //│ case x$22 of -- #94 //│ None => //│ let* (x$24) = aaa() in -- #84 //│ jump j$2(x$24) -- #83 //│ Some => //│ let x$25 = Some.x(x$22) in -- #93 //│ let* (x$26) = bbb() in -- #92 //│ jump j$2(x$26) -- #91 //│ def j$2(x$23) = //│ x$23 -- #80 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 404 //│ :interpIR fun odd(x) = if x is O then False S(s) then even(s) fun even(x) = if x is O then True S(s) then odd(s) fun foo() = odd(S(S(S(O)))) foo() //│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|S|(|O|)|)|)|)|↵|foo|(||)| //│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(S(S(S(O,),),),); foo()} //│ //│ //│ IR: //│ Program: //│ //│ def odd(x$1) = //│ case x$1 of -- #19 //│ O => //│ let x$3 = False() in -- #7 //│ jump j$0(x$3) -- #6 //│ S => //│ let x$4 = S.s(x$1) in -- #18 //│ let* (x$5) = even(x$4) in -- #17 //│ jump j$0(x$5) -- #16 //│ def j$0(x$2) = //│ x$2 -- #4 //│ def even(x$6) = //│ case x$6 of -- #36 //│ O => //│ let x$8 = True() in -- #24 //│ jump j$1(x$8) -- #23 //│ S => //│ let x$9 = S.s(x$6) in -- #35 //│ let* (x$10) = odd(x$9) in -- #34 //│ jump j$1(x$10) -- #33 //│ def j$1(x$7) = //│ x$7 -- #21 //│ def foo() = //│ let x$11 = O() in -- #54 //│ let x$12 = S(x$11) in -- #53 //│ let x$13 = S(x$12) in -- #52 //│ let x$14 = S(x$13) in -- #51 //│ let* (x$15) = odd(x$14) in -- #50 //│ x$15 -- #49 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ True() //│ :interpIR :genCpp fun odd(x) = if x is O then False S(s) then even(s) fun even(x) = if x is O then True S(s) then odd(s) fun mk(n) = if n > 0 then S(mk(n - 1)) else O fun foo() = odd(mk(10)) foo() //│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|mk|(|10|)|)|↵|foo|(||)| | //│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n, 0,)) then S(mk(-(n, 1,),),) else O; fun foo = () => odd(mk(10,),); foo()} //│ //│ //│ IR: //│ Program: //│ //│ def odd(x$1) = //│ case x$1 of -- #19 //│ O => //│ let x$3 = False() in -- #7 //│ jump j$0(x$3) -- #6 //│ S => //│ let x$4 = S.s(x$1) in -- #18 //│ let* (x$5) = even(x$4) in -- #17 //│ jump j$0(x$5) -- #16 //│ def j$0(x$2) = //│ x$2 -- #4 //│ def even(x$6) = //│ case x$6 of -- #36 //│ O => //│ let x$8 = True() in -- #24 //│ jump j$1(x$8) -- #23 //│ S => //│ let x$9 = S.s(x$6) in -- #35 //│ let* (x$10) = odd(x$9) in -- #34 //│ jump j$1(x$10) -- #33 //│ def j$1(x$7) = //│ x$7 -- #21 //│ def mk(n$0) = //│ let x$11 = >(n$0,0) in -- #64 //│ if x$11 -- #63 //│ true => //│ let x$13 = -(n$0,1) in -- #59 //│ let* (x$14) = mk(x$13) in -- #58 //│ let x$15 = S(x$14) in -- #57 //│ jump j$2(x$15) -- #56 //│ false => //│ let x$16 = O() in -- #62 //│ jump j$2(x$16) -- #61 //│ def j$2(x$12) = //│ x$12 -- #43 //│ def foo() = //│ let* (x$17) = mk(10) in -- #73 //│ let* (x$18) = odd(x$17) in -- #72 //│ x$18 -- #71 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ False() //│ :interpIR :genCpp fun odd(x) = if x is O then False S(s) then even(s) fun even(x) = if x is O then True S(s) then odd(s) fun mk(n) = if n > 0 then S(mk(n - 1)) else O fun foo() = odd(S(S(mk(10)))) foo() //│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |mk|(|n|)| |#=| |#if| |n| |>| |0| |#then| |S|(|mk|(|n| |-| |1|)|)| |#else| |O|↵|#fun| |foo|(||)| |#=| |odd|(|S|(|S|(|mk|(|10|)|)|)|)|↵|foo|(||)| //│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun mk = (n,) => if (>(n, 0,)) then S(mk(-(n, 1,),),) else O; fun foo = () => odd(S(S(mk(10,),),),); foo()} //│ //│ //│ IR: //│ Program: //│ //│ def odd(x$1) = //│ case x$1 of -- #19 //│ O => //│ let x$3 = False() in -- #7 //│ jump j$0(x$3) -- #6 //│ S => //│ let x$4 = S.s(x$1) in -- #18 //│ let* (x$5) = even(x$4) in -- #17 //│ jump j$0(x$5) -- #16 //│ def j$0(x$2) = //│ x$2 -- #4 //│ def even(x$6) = //│ case x$6 of -- #36 //│ O => //│ let x$8 = True() in -- #24 //│ jump j$1(x$8) -- #23 //│ S => //│ let x$9 = S.s(x$6) in -- #35 //│ let* (x$10) = odd(x$9) in -- #34 //│ jump j$1(x$10) -- #33 //│ def j$1(x$7) = //│ x$7 -- #21 //│ def mk(n$0) = //│ let x$11 = >(n$0,0) in -- #64 //│ if x$11 -- #63 //│ true => //│ let x$13 = -(n$0,1) in -- #59 //│ let* (x$14) = mk(x$13) in -- #58 //│ let x$15 = S(x$14) in -- #57 //│ jump j$2(x$15) -- #56 //│ false => //│ let x$16 = O() in -- #62 //│ jump j$2(x$16) -- #61 //│ def j$2(x$12) = //│ x$12 -- #43 //│ def foo() = //│ let* (x$17) = mk(10) in -- #81 //│ let x$18 = S(x$17) in -- #80 //│ let x$19 = S(x$18) in -- #79 //│ let* (x$20) = odd(x$19) in -- #78 //│ x$20 -- #77 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ False() //│ :interpIR :genCpp fun odd(x) = if x is O then False S(s) then even(s) fun even(x) = if x is O then True S(s) then odd(s) fun foo() = odd(if 10 > 0 then S(O) else O) fun bar() = if 10 > 0 then odd(S(O)) else odd(O) fun main() = foo() bar() main() //│ |#fun| |odd|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |False|↵|S|(|s|)| |#then| |even|(|s|)|←|←|↵|#fun| |even|(|x|)| |#=|→|#if| |x| |is|→|O| |#then| |True|↵|S|(|s|)| |#then| |odd|(|s|)|←|←|↵|#fun| |foo|(||)| |#=| |odd|(|#if| |10| |>| |0| |#then| |S|(|O|)| |#else| |O|)|↵|#fun| |bar|(||)| |#=| |#if| |10| |>| |0| |#then| |odd|(|S|(|O|)|)| |#else| |odd|(|O|)|↵|#fun| |main|(||)| |#=|→|foo|(||)|↵|bar|(||)|←|↵|main|(||)| //│ Parsed: {fun odd = (x,) => {if x is ‹(O) then False; (S(s,)) then even(s,)›}; fun even = (x,) => {if x is ‹(O) then True; (S(s,)) then odd(s,)›}; fun foo = () => odd(if (>(10, 0,)) then S(O,) else O,); fun bar = () => if (>(10, 0,)) then odd(S(O,),) else odd(O,); fun main = () => {foo(); bar()}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def odd(x$1) = //│ case x$1 of -- #19 //│ O => //│ let x$3 = False() in -- #7 //│ jump j$0(x$3) -- #6 //│ S => //│ let x$4 = S.s(x$1) in -- #18 //│ let* (x$5) = even(x$4) in -- #17 //│ jump j$0(x$5) -- #16 //│ def j$0(x$2) = //│ x$2 -- #4 //│ def even(x$6) = //│ case x$6 of -- #36 //│ O => //│ let x$8 = True() in -- #24 //│ jump j$1(x$8) -- #23 //│ S => //│ let x$9 = S.s(x$6) in -- #35 //│ let* (x$10) = odd(x$9) in -- #34 //│ jump j$1(x$10) -- #33 //│ def j$1(x$7) = //│ x$7 -- #21 //│ def foo() = //│ let x$11 = >(10,0) in -- #59 //│ if x$11 -- #58 //│ true => //│ let x$14 = O() in -- #54 //│ let x$15 = S(x$14) in -- #53 //│ jump j$2(x$15) -- #52 //│ false => //│ let x$16 = O() in -- #57 //│ jump j$2(x$16) -- #56 //│ def j$2(x$12) = //│ let* (x$13) = odd(x$12) in -- #47 //│ x$13 -- #46 //│ def bar() = //│ let x$17 = >(10,0) in -- #86 //│ if x$17 -- #85 //│ true => //│ let x$19 = O() in -- #77 //│ let x$20 = S(x$19) in -- #76 //│ let* (x$21) = odd(x$20) in -- #75 //│ jump j$3(x$21) -- #74 //│ false => //│ let x$22 = O() in -- #84 //│ let* (x$23) = odd(x$22) in -- #83 //│ jump j$3(x$23) -- #82 //│ def j$3(x$18) = //│ x$18 -- #66 //│ def main() = //│ let* (x$24) = foo() in -- #92 //│ let* (x$25) = bar() in -- #91 //│ x$25 -- #90 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ True() //│ :interpIR :genCpp fun aaa() = let m = 1 let n = 2 let p = 3 let q = 4 m + n - p + q fun bbb() = let x = aaa() x * 100 + 4 fun not(x) = if x then False else True fun foo(x) = if x then None else Some(foo(not(x))) fun main(flag) = let x = foo(flag) if x is None then aaa() Some(b1) then bbb() main(False) //│ |#fun| |aaa|(||)| |#=|→|#let| |m| |#=| |1|↵|#let| |n| |#=| |2|↵|#let| |p| |#=| |3|↵|#let| |q| |#=| |4|↵|m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |bbb|(||)| |#=|→|#let| |x| |#=| |aaa|(||)|↵|x| |*| |100| |+| |4|←|↵|#fun| |not|(|x|)| |#=|→|#if| |x| |#then| |False| |#else| |True|←|↵|#fun| |foo|(|x|)| |#=|→|#if| |x| |#then| |None| |#else| |Some|(|foo|(|not|(|x|)|)|)|←|↵|#fun| |main|(|flag|)| |#=|→|#let| |x| |#=| |foo|(|flag|)|↵|#if| |x| |is|→|None| |#then| |aaa|(||)|↵|Some|(|b1|)| |#then| |bbb|(||)|←|←|↵|main|(|False|)| //│ Parsed: {fun aaa = () => {let m = 1; let n = 2; let p = 3; let q = 4; +(-(+(m, n,), p,), q,)}; fun bbb = () => {let x = aaa(); +(*(x, 100,), 4,)}; fun not = (x,) => {if (x) then False else True}; fun foo = (x,) => {if (x) then None else Some(foo(not(x,),),)}; fun main = (flag,) => {let x = foo(flag,); if x is ‹(None) then aaa(); (Some(b1,)) then bbb()›}; main(False,)} //│ //│ //│ IR: //│ Program: //│ //│ def aaa() = //│ let x$2 = 1 in -- #32 //│ let x$3 = 2 in -- #31 //│ let x$4 = 3 in -- #30 //│ let x$5 = 4 in -- #29 //│ let x$6 = +(x$2,x$3) in -- #28 //│ let x$7 = -(x$6,x$4) in -- #27 //│ let x$8 = +(x$7,x$5) in -- #26 //│ x$8 -- #25 //│ def bbb() = //│ let* (x$9) = aaa() in -- #48 //│ let x$10 = *(x$9,100) in -- #47 //│ let x$11 = +(x$10,4) in -- #46 //│ x$11 -- #45 //│ def not(x$12) = //│ if x$12 -- #57 //│ true => //│ let x$14 = False() in -- #53 //│ jump j$0(x$14) -- #52 //│ false => //│ let x$15 = True() in -- #56 //│ jump j$0(x$15) -- #55 //│ def j$0(x$13) = //│ x$13 -- #50 //│ def foo(x$16) = //│ if x$16 -- #77 //│ true => //│ let x$18 = None() in -- #62 //│ jump j$1(x$18) -- #61 //│ false => //│ let* (x$19) = not(x$16) in -- #76 //│ let* (x$20) = foo(x$19) in -- #75 //│ let x$21 = Some(x$20) in -- #74 //│ jump j$1(x$21) -- #73 //│ def j$1(x$17) = //│ x$17 -- #59 //│ def main(flag$0) = //│ let* (x$22) = foo(flag$0) in -- #98 //│ case x$22 of -- #97 //│ None => //│ let* (x$24) = aaa() in -- #87 //│ jump j$2(x$24) -- #86 //│ Some => //│ let x$25 = Some.x(x$22) in -- #96 //│ let* (x$26) = bbb() in -- #95 //│ jump j$2(x$26) -- #94 //│ def j$2(x$23) = //│ x$23 -- #83 //│ let x$0 = False() in -- #5 //│ let* (x$1) = main(x$0) in -- #4 //│ x$1 -- #3 //│ //│ Interpreted: //│ 404 //│ :interpIR :genCpp fun head_opt(l) = if l is Nil then None Cons(h, t) then Some(h) fun is_none(o) = if o is None then True Some(x) then False fun is_empty(l) = is_none(head_opt(l)) fun main() = is_empty(Cons(1, Cons(2, Nil))) main() //│ |#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|Cons|(|1|,| |Cons|(|2|,| |Nil|)|)|)|←|↵|main|(||)| //│ Parsed: {fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(Cons(1, Cons(2, Nil,),),)}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def head_opt(l$0) = //│ case l$0 of -- #24 //│ Nil => //│ let x$2 = None() in -- #7 //│ jump j$0(x$2) -- #6 //│ Cons => //│ let x$3 = Cons.t(l$0) in -- #23 //│ let x$4 = Cons.h(l$0) in -- #22 //│ let x$5 = Some(x$4) in -- #21 //│ jump j$0(x$5) -- #20 //│ def j$0(x$1) = //│ x$1 -- #4 //│ def is_none(o$0) = //│ case o$0 of -- #38 //│ None => //│ let x$7 = True() in -- #29 //│ jump j$1(x$7) -- #28 //│ Some => //│ let x$8 = Some.x(o$0) in -- #37 //│ let x$9 = False() in -- #36 //│ jump j$1(x$9) -- #35 //│ def j$1(x$6) = //│ x$6 -- #26 //│ def is_empty(l$1) = //│ let* (x$10) = head_opt(l$1) in -- #47 //│ let* (x$11) = is_none(x$10) in -- #46 //│ x$11 -- #45 //│ def main() = //│ let x$12 = Nil() in -- #65 //│ let x$13 = Cons(2,x$12) in -- #64 //│ let x$14 = Cons(1,x$13) in -- #63 //│ let* (x$15) = is_empty(x$14) in -- #62 //│ x$15 -- #61 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ False() //│ :interpIR :genCpp fun mk_list(n) = if n == 0 then Nil else Cons(n, mk_list(n - 1)) fun head_opt(l) = if l is Nil then None Cons(h, t) then Some(h) fun is_none(o) = if o is None then True Some(x) then False fun is_empty(l) = is_none(head_opt(l)) fun main() = is_empty(mk_list(10)) main() //│ |#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |head_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then| |Some|(|h|)|←|←|↵|#fun| |is_none|(|o|)| |#=|→|#if| |o| |is|→|None| |#then| |True|↵|Some|(|x|)| |#then| |False|←|←|↵|#fun| |is_empty|(|l|)| |#=|→|is_none|(|head_opt|(|l|)|)|←|↵|#fun| |main|(||)| |#=|→|is_empty|(|mk_list|(|10|)|)|←|↵|main|(||)| //│ Parsed: {fun mk_list = (n,) => {if (==(n, 0,)) then Nil else Cons(n, mk_list(-(n, 1,),),)}; fun head_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then Some(h,)›}; fun is_none = (o,) => {if o is ‹(None) then True; (Some(x,)) then False›}; fun is_empty = (l,) => {is_none(head_opt(l,),)}; fun main = () => {is_empty(mk_list(10,),)}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def mk_list(n$0) = //│ let x$1 = ==(n$0,0) in -- #32 //│ if x$1 -- #31 //│ true => //│ let x$3 = Nil() in -- #12 //│ jump j$0(x$3) -- #11 //│ false => //│ let x$4 = -(n$0,1) in -- #30 //│ let* (x$5) = mk_list(x$4) in -- #29 //│ let x$6 = Cons(n$0,x$5) in -- #28 //│ jump j$0(x$6) -- #27 //│ def j$0(x$2) = //│ x$2 -- #9 //│ def head_opt(l$0) = //│ case l$0 of -- #54 //│ Nil => //│ let x$8 = None() in -- #37 //│ jump j$1(x$8) -- #36 //│ Cons => //│ let x$9 = Cons.t(l$0) in -- #53 //│ let x$10 = Cons.h(l$0) in -- #52 //│ let x$11 = Some(x$10) in -- #51 //│ jump j$1(x$11) -- #50 //│ def j$1(x$7) = //│ x$7 -- #34 //│ def is_none(o$0) = //│ case o$0 of -- #68 //│ None => //│ let x$13 = True() in -- #59 //│ jump j$2(x$13) -- #58 //│ Some => //│ let x$14 = Some.x(o$0) in -- #67 //│ let x$15 = False() in -- #66 //│ jump j$2(x$15) -- #65 //│ def j$2(x$12) = //│ x$12 -- #56 //│ def is_empty(l$1) = //│ let* (x$16) = head_opt(l$1) in -- #77 //│ let* (x$17) = is_none(x$16) in -- #76 //│ x$17 -- #75 //│ def main() = //│ let* (x$18) = mk_list(10) in -- #86 //│ let* (x$19) = is_empty(x$18) in -- #85 //│ x$19 -- #84 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ False() //│ :interpIR :genCpp fun mk_list(n) = if n == 0 then Nil else Cons(n, mk_list(n - 1)) fun last_opt(l) = if l is Nil then None Cons(h, t) then if t is Nil then Some(h) Cons(h2, t2) then last_opt(t) fun main() = last_opt(mk_list(10)) main() //│ |#fun| |mk_list|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |Nil| |#else| |Cons|(|n|,| |mk_list|(|n| |-| |1|)|)|←|↵|#fun| |last_opt|(|l|)| |#=|→|#if| |l| |is|→|Nil| |#then| |None|↵|Cons|(|h|,| |t|)| |#then|→|#if| |t| |is|→|Nil| |#then| |Some|(|h|)|↵|Cons|(|h2|,| |t2|)| |#then| |last_opt|(|t|)|←|←|←|←|↵|#fun| |main|(||)| |#=|→|last_opt|(|mk_list|(|10|)|)|←|↵|main|(||)| //│ Parsed: {fun mk_list = (n,) => {if (==(n, 0,)) then Nil else Cons(n, mk_list(-(n, 1,),),)}; fun last_opt = (l,) => {if l is ‹(Nil) then None; (Cons(h, t,)) then {if t is ‹(Nil) then Some(h,); (Cons(h2, t2,)) then last_opt(t,)›}›}; fun main = () => {last_opt(mk_list(10,),)}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def mk_list(n$0) = //│ let x$1 = ==(n$0,0) in -- #32 //│ if x$1 -- #31 //│ true => //│ let x$3 = Nil() in -- #12 //│ jump j$0(x$3) -- #11 //│ false => //│ let x$4 = -(n$0,1) in -- #30 //│ let* (x$5) = mk_list(x$4) in -- #29 //│ let x$6 = Cons(n$0,x$5) in -- #28 //│ jump j$0(x$6) -- #27 //│ def j$0(x$2) = //│ x$2 -- #9 //│ def last_opt(l$0) = //│ case l$0 of -- #74 //│ Nil => //│ let x$8 = None() in -- #37 //│ jump j$1(x$8) -- #36 //│ Cons => //│ let x$9 = Cons.t(l$0) in -- #73 //│ let x$10 = Cons.h(l$0) in -- #72 //│ case x$9 of -- #71 //│ Nil => //│ let x$12 = Some(x$10) in -- #54 //│ jump j$2(x$12) -- #53 //│ Cons => //│ let x$13 = Cons.t(x$9) in -- #70 //│ let x$14 = Cons.h(x$9) in -- #69 //│ let* (x$15) = last_opt(x$9) in -- #68 //│ jump j$2(x$15) -- #67 //│ def j$1(x$7) = //│ x$7 -- #34 //│ def j$2(x$11) = //│ jump j$1(x$11) -- #48 //│ def main() = //│ let* (x$16) = mk_list(10) in -- #83 //│ let* (x$17) = last_opt(x$16) in -- #82 //│ x$17 -- #81 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ Some(1) //│ :interpIR :genCpp fun is_some(o) = if o is Some(x) then True None then False fun e0(w) = w + 8 + 9 + 10 fun e1(a, c) = a + 1 + 2 + 3 + 4 fun e3(c) = let m = 4 let n = 5 let p = 6 let q = 7 if c then m + n + p + q else m + n - p + q fun e2(x) = x + 12 + 13 + 14 fun f(x) = let c1 = is_some(x) let z = e3(c1) let w = if x is Some(a) then e1(a, z) None then e2(z) e0(w) fun main() = f(Some(2)) + f(None) main() //│ |#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |c|)| |#=|→|a| |+| |1| |+| |2| |+| |3| |+| |4|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| //│ Parsed: {fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w, 8,), 9,), 10,)}; fun e1 = (a, c,) => {+(+(+(+(a, 1,), 2,), 3,), 4,)}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m, n,), p,), q,) else +(-(+(m, n,), p,), q,)}; fun e2 = (x,) => {+(+(+(x, 12,), 13,), 14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),), f(None,),)}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def is_some(o$0) = //│ case o$0 of -- #16 //│ Some => //│ let x$2 = Some.x(o$0) in -- #12 //│ let x$3 = True() in -- #11 //│ jump j$0(x$3) -- #10 //│ None => //│ let x$4 = False() in -- #15 //│ jump j$0(x$4) -- #14 //│ def j$0(x$1) = //│ x$1 -- #4 //│ def e0(w$0) = //│ let x$5 = +(w$0,8) in -- #35 //│ let x$6 = +(x$5,9) in -- #34 //│ let x$7 = +(x$6,10) in -- #33 //│ x$7 -- #32 //│ def e1(a$0,c$0) = //│ let x$8 = +(a$0,1) in -- #60 //│ let x$9 = +(x$8,2) in -- #59 //│ let x$10 = +(x$9,3) in -- #58 //│ let x$11 = +(x$10,4) in -- #57 //│ x$11 -- #56 //│ def e3(c$1) = //│ let x$12 = 4 in -- #111 //│ let x$13 = 5 in -- #110 //│ let x$14 = 6 in -- #109 //│ let x$15 = 7 in -- #108 //│ if c$1 -- #107 //│ true => //│ let x$17 = +(x$12,x$13) in -- #86 //│ let x$18 = +(x$17,x$14) in -- #85 //│ let x$19 = +(x$18,x$15) in -- #84 //│ jump j$1(x$19) -- #83 //│ false => //│ let x$20 = +(x$12,x$13) in -- #106 //│ let x$21 = -(x$20,x$14) in -- #105 //│ let x$22 = +(x$21,x$15) in -- #104 //│ jump j$1(x$22) -- #103 //│ def j$1(x$16) = //│ x$16 -- #66 //│ def e2(x$23) = //│ let x$24 = +(x$23,12) in -- #130 //│ let x$25 = +(x$24,13) in -- #129 //│ let x$26 = +(x$25,14) in -- #128 //│ x$26 -- #127 //│ def f(x$27) = //│ let* (x$28) = is_some(x$27) in -- #167 //│ let* (x$29) = e3(x$28) in -- #166 //│ case x$27 of -- #165 //│ Some => //│ let x$32 = Some.x(x$27) in -- #158 //│ let* (x$33) = e1(x$32,x$29) in -- #157 //│ jump j$2(x$33) -- #156 //│ None => //│ let* (x$34) = e2(x$29) in -- #164 //│ jump j$2(x$34) -- #163 //│ def j$2(x$30) = //│ let* (x$31) = e0(x$30) in -- #145 //│ x$31 -- #144 //│ def main() = //│ let x$35 = Some(2) in -- #187 //│ let* (x$36) = f(x$35) in -- #186 //│ let x$37 = None() in -- #185 //│ let* (x$38) = f(x$37) in -- #184 //│ let x$39 = +(x$36,x$38) in -- #183 //│ x$39 -- #182 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 115 //│ :interpIR :genCpp fun is_some(o) = if o is Some(x) then True None then False fun e0(w) = w + 8 + 9 + 10 fun e1(a, z) = if a > 0 then f(Some(a - 1)) else z fun e3(c) = let m = 4 let n = 5 let p = 6 let q = 7 if c then m + n + p + q else m + n - p + q fun e2(x) = x + 12 + 13 + 14 fun f(x) = let c1 = is_some(x) let z = e3(c1) let w = if x is Some(a) then e1(a, z) None then e2(z) e0(w) fun main() = f(Some(2)) + f(None) main() //│ |#fun| |is_some|(|o|)| |#=|→|#if| |o| |is|→|Some|(|x|)| |#then| |True|↵|None| |#then| |False|←|←|↵|#fun| |e0|(|w|)| |#=|→|w| |+| |8| |+| |9| |+| |10|←|↵|#fun| |e1|(|a|,| |z|)| |#=|→|#if| |a| |>| |0| |#then| |f|(|Some|(|a| |-| |1|)|)| |#else| |z|←|↵|#fun| |e3|(|c|)| |#=|→|#let| |m| |#=| |4|↵|#let| |n| |#=| |5|↵|#let| |p| |#=| |6|↵|#let| |q| |#=| |7|↵|#if| |c| |#then| |m| |+| |n| |+| |p| |+| |q| |#else| |m| |+| |n| |-| |p| |+| |q|←|↵|#fun| |e2|(|x|)| |#=|→|x| |+| |12| |+| |13| |+| |14|←|↵|#fun| |f|(|x|)| |#=|→|#let| |c1| |#=| |is_some|(|x|)|↵|#let| |z| |#=| |e3|(|c1|)|↵|#let| |w| |#=| |#if| |x| |is|→|Some|(|a|)| |#then| |e1|(|a|,| |z|)|↵|None| |#then| |e2|(|z|)|←|↵|e0|(|w|)|←|↵|#fun| |main|(||)| |#=|→|f|(|Some|(|2|)|)| |+| |f|(|None|)|←|↵|main|(||)| //│ Parsed: {fun is_some = (o,) => {if o is ‹(Some(x,)) then True; (None) then False›}; fun e0 = (w,) => {+(+(+(w, 8,), 9,), 10,)}; fun e1 = (a, z,) => {if (>(a, 0,)) then f(Some(-(a, 1,),),) else z}; fun e3 = (c,) => {let m = 4; let n = 5; let p = 6; let q = 7; if (c) then +(+(+(m, n,), p,), q,) else +(-(+(m, n,), p,), q,)}; fun e2 = (x,) => {+(+(+(x, 12,), 13,), 14,)}; fun f = (x,) => {let c1 = is_some(x,); let z = e3(c1,); let w = if x is ‹(Some(a,)) then e1(a, z,); (None) then e2(z,)›; e0(w,)}; fun main = () => {+(f(Some(2,),), f(None,),)}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def is_some(o$0) = //│ case o$0 of -- #16 //│ Some => //│ let x$2 = Some.x(o$0) in -- #12 //│ let x$3 = True() in -- #11 //│ jump j$0(x$3) -- #10 //│ None => //│ let x$4 = False() in -- #15 //│ jump j$0(x$4) -- #14 //│ def j$0(x$1) = //│ x$1 -- #4 //│ def e0(w$0) = //│ let x$5 = +(w$0,8) in -- #35 //│ let x$6 = +(x$5,9) in -- #34 //│ let x$7 = +(x$6,10) in -- #33 //│ x$7 -- #32 //│ def e1(a$0,z$0) = //│ let x$8 = >(a$0,0) in -- #62 //│ if x$8 -- #61 //│ true => //│ let x$10 = -(a$0,1) in -- #58 //│ let x$11 = Some(x$10) in -- #57 //│ let* (x$12) = f(x$11) in -- #56 //│ jump j$1(x$12) -- #55 //│ false => //│ jump j$1(z$0) -- #60 //│ def j$1(x$9) = //│ x$9 -- #42 //│ def e3(c$0) = //│ let x$13 = 4 in -- #113 //│ let x$14 = 5 in -- #112 //│ let x$15 = 6 in -- #111 //│ let x$16 = 7 in -- #110 //│ if c$0 -- #109 //│ true => //│ let x$18 = +(x$13,x$14) in -- #88 //│ let x$19 = +(x$18,x$15) in -- #87 //│ let x$20 = +(x$19,x$16) in -- #86 //│ jump j$2(x$20) -- #85 //│ false => //│ let x$21 = +(x$13,x$14) in -- #108 //│ let x$22 = -(x$21,x$15) in -- #107 //│ let x$23 = +(x$22,x$16) in -- #106 //│ jump j$2(x$23) -- #105 //│ def j$2(x$17) = //│ x$17 -- #68 //│ def e2(x$24) = //│ let x$25 = +(x$24,12) in -- #132 //│ let x$26 = +(x$25,13) in -- #131 //│ let x$27 = +(x$26,14) in -- #130 //│ x$27 -- #129 //│ def f(x$28) = //│ let* (x$29) = is_some(x$28) in -- #169 //│ let* (x$30) = e3(x$29) in -- #168 //│ case x$28 of -- #167 //│ Some => //│ let x$33 = Some.x(x$28) in -- #160 //│ let* (x$34) = e1(x$33,x$30) in -- #159 //│ jump j$3(x$34) -- #158 //│ None => //│ let* (x$35) = e2(x$30) in -- #166 //│ jump j$3(x$35) -- #165 //│ def j$3(x$31) = //│ let* (x$32) = e0(x$31) in -- #147 //│ x$32 -- #146 //│ def main() = //│ let x$36 = Some(2) in -- #189 //│ let* (x$37) = f(x$36) in -- #188 //│ let x$38 = None() in -- #187 //│ let* (x$39) = f(x$38) in -- #186 //│ let x$40 = +(x$37,x$39) in -- #185 //│ x$40 -- #184 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 179 //│ :genCpp fun pred(n) = if n is S(p) then p O then O fun plus(n1, n2) = if n1 is O then n2 S(p) then S(plus(p, n2)) fun fib(n) = if n is O then S(O) S(p) then if p is O then S(O) S(q) then plus(fib(p), fib(q)) fun to_int(n) = if n is O then 0 S(p) then 1 + to_int(p) fun to_nat(n) = if n == 0 then O else S(to_nat(n - 1)) fun main() = to_int(fib(to_nat(30))) main() //│ |#fun| |pred|(|n|)| |#=|→|#if| |n| |is|→|S|(|p|)| |#then| |p|↵|O| |#then| |O|←|←|↵|#fun| |plus|(|n1|,| |n2|)| |#=|→|#if| |n1| |is|→|O| |#then| |n2|↵|S|(|p|)| |#then| |S|(|plus|(|p|,| |n2|)|)|←|←|↵|#fun| |fib|(|n|)| |#=|→|#if| |n| |is|→|O| |#then| |S|(|O|)|↵|S|(|p|)| |#then|→|#if| |p| |is|→|O| |#then| |S|(|O|)|↵|S|(|q|)| |#then| |plus|(|fib|(|p|)|,| |fib|(|q|)|)|←|←|←|←|↵|#fun| |to_int|(|n|)| |#=|→|#if| |n| |is|→|O| |#then| |0|↵|S|(|p|)| |#then| |1| |+| |to_int|(|p|)|←|←|↵|#fun| |to_nat|(|n|)| |#=|→|#if| |n| |==| |0| |#then| |O|↵|#else| |S|(|to_nat|(|n| |-| |1|)|)|←|↵|#fun| |main|(||)| |#=|→|to_int|(|fib|(|to_nat|(|30|)|)|)|←|↵|main|(||)| //│ Parsed: {fun pred = (n,) => {if n is ‹(S(p,)) then p; (O) then O›}; fun plus = (n1, n2,) => {if n1 is ‹(O) then n2; (S(p,)) then S(plus(p, n2,),)›}; fun fib = (n,) => {if n is ‹(O) then S(O,); (S(p,)) then {if p is ‹(O) then S(O,); (S(q,)) then plus(fib(p,), fib(q,),)›}›}; fun to_int = (n,) => {if n is ‹(O) then 0; (S(p,)) then +(1, to_int(p,),)›}; fun to_nat = (n,) => {if (==(n, 0,)) then O else S(to_nat(-(n, 1,),),)}; fun main = () => {to_int(fib(to_nat(30,),),)}; main()} //│ //│ //│ IR: //│ Program: //│ //│ def pred(n$0) = //│ case n$0 of -- #15 //│ S => //│ let x$2 = S.s(n$0) in -- #11 //│ jump j$0(x$2) -- #10 //│ O => //│ let x$3 = O() in -- #14 //│ jump j$0(x$3) -- #13 //│ def j$0(x$1) = //│ x$1 -- #4 //│ def plus(n1$0,n2$0) = //│ case n1$0 of -- #37 //│ O => //│ jump j$1(n2$0) -- #19 //│ S => //│ let x$5 = S.s(n1$0) in -- #36 //│ let* (x$6) = plus(x$5,n2$0) in -- #35 //│ let x$7 = S(x$6) in -- #34 //│ jump j$1(x$7) -- #33 //│ def j$1(x$4) = //│ x$4 -- #17 //│ def fib(n$1) = //│ case n$1 of -- #84 //│ O => //│ let x$9 = O() in -- #46 //│ let x$10 = S(x$9) in -- #45 //│ jump j$2(x$10) -- #44 //│ S => //│ let x$11 = S.s(n$1) in -- #83 //│ case x$11 of -- #82 //│ O => //│ let x$13 = O() in -- #60 //│ let x$14 = S(x$13) in -- #59 //│ jump j$3(x$14) -- #58 //│ S => //│ let x$15 = S.s(x$11) in -- #81 //│ let* (x$16) = fib(x$11) in -- #80 //│ let* (x$17) = fib(x$15) in -- #79 //│ let* (x$18) = plus(x$16,x$17) in -- #78 //│ jump j$3(x$18) -- #77 //│ def j$2(x$8) = //│ x$8 -- #39 //│ def j$3(x$12) = //│ jump j$2(x$12) -- #53 //│ def to_int(n$2) = //│ case n$2 of -- #106 //│ O => //│ jump j$4(0) -- #88 //│ S => //│ let x$20 = S.s(n$2) in -- #105 //│ let* (x$21) = to_int(x$20) in -- #104 //│ let x$22 = +(1,x$21) in -- #103 //│ jump j$4(x$22) -- #102 //│ def j$4(x$19) = //│ x$19 -- #86 //│ def to_nat(n$3) = //│ let x$23 = ==(n$3,0) in -- #134 //│ if x$23 -- #133 //│ true => //│ let x$25 = O() in -- #116 //│ jump j$5(x$25) -- #115 //│ false => //│ let x$26 = -(n$3,1) in -- #132 //│ let* (x$27) = to_nat(x$26) in -- #131 //│ let x$28 = S(x$27) in -- #130 //│ jump j$5(x$28) -- #129 //│ def j$5(x$24) = //│ x$24 -- #113 //│ def main() = //│ let* (x$29) = to_nat(30) in -- #147 //│ let* (x$30) = fib(x$29) in -- #146 //│ let* (x$31) = to_int(x$30) in -- #145 //│ x$31 -- #144 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ ================================================ FILE: compiler/shared/test/diff-ir/IRTailRec.mls ================================================ :NewParser :ParseOnly :UseIR :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. :noTailRec :interpIR fun fact(acc, n) = if n == 0 then acc else fact(acc * n, n - 1) fact(1, 5) //│ |#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ //│ //│ IR: //│ Program: //│ //│ def fact(acc$0,n$0) = //│ let x$1 = ==(n$0,0) in -- #28 //│ if x$1 -- #27 //│ true => //│ jump j$0(acc$0) -- #12 //│ false => //│ let x$3 = *(acc$0,n$0) in -- #26 //│ let x$4 = -(n$0,1) in -- #25 //│ let* (x$5) = fact(x$3,x$4) in -- #24 //│ jump j$0(x$5) -- #23 //│ def j$0(x$2) = //│ x$2 -- #10 //│ let* (x$0) = fact(1,5) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ 120 :interpIR @tailrec fun fact(acc, n) = if n == 0 then acc else fact(acc * n, n - 1) fact(1, 5) //│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#if| |n| |==| |0| |#then| |acc|↵|#else| |fact|(|acc| |*| |n|,| |n| |-| |1|)|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {if (==(n,)(0,)) then acc else fact(*(acc,)(n,), -(n,)(1,),)}; fact(1, 5,)} //│ //│ //│ IR: //│ Program: //│ //│ def fact(acc$0,n$0) = //│ let x$1 = ==(n$0,0) in -- #28 //│ if x$1 -- #27 //│ true => //│ jump j$0(acc$0) -- #12 //│ false => //│ let x$3 = *(acc$0,n$0) in -- #26 //│ let x$4 = -(n$0,1) in -- #25 //│ let* (x$5) = fact(x$3,x$4) in -- #24 //│ jump j$0(x$5) -- #23 //│ def j$0(x$2) = //│ x$2 -- #10 //│ let* (x$0) = fact(1,5) in -- #6 //│ x$0 -- #5 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(fact)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def j$0(x$2) = //│ x$2 -- #10 //│ def fact_jp(acc$0,n$0) = //│ let x$1 = ==(n$0,0) in -- #40 //│ if x$1 -- #39 //│ true => //│ jump j$0(acc$0) -- #35 //│ false => //│ let x$3 = *(acc$0,n$0) in -- #38 //│ let x$4 = -(n$0,1) in -- #37 //│ jump fact_jp(x$3,x$4) -- #36 //│ def fact(acc$0,n$0) = //│ let* (r0) = fact_jp(acc$0,n$0) in -- #42 //│ r0 -- #41 //│ let* (x$0) = fact(1,5) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ 120 :interpIR @tailrec fun fact(acc, n) = val x = if n > 0 then n - 1 else 0 if x <= 0 then acc else @tailcall fact(n * acc, x) fact(1, 5) //│ |@|tailrec|↵|#fun| |fact|(|acc|,| |n|)| |#=|→|#val| |x| |#=| |#if| |n| |>| |0| |#then| |n| |-| |1|→|#else| |0|←|↵|#if| |x| |<=| |0| |#then|→|acc|←|↵|#else| |→|@|tailcall| |fact|(|n| |*| |acc|,| |x|)| |←|←|↵|fact|(|1|,| |5|)| //│ Parsed: {fun fact = (acc, n,) => {let x = if (>(n,)(0,)) then -(n,)(1,) else 0; if (<=(x,)(0,)) then {acc} else {@tailcall fact(*(n,)(acc,), x,)}}; fact(1, 5,)} //│ //│ //│ IR: //│ Program: //│ //│ def fact(acc$0,n$0) = //│ let x$1 = >(n$0,0) in -- #38 //│ if x$1 -- #37 //│ true => //│ let x$7 = -(n$0,1) in -- #34 //│ jump j$0(x$7,acc$0,n$0) -- #33 //│ false => //│ jump j$0(0,acc$0,n$0) -- #36 //│ def j$1(x$4) = //│ x$4 -- #14 //│ def j$0(x$2,acc$0,n$0) = //│ let x$3 = <=(x$2,0) in -- #29 //│ if x$3 -- #28 //│ true => //│ jump j$1(acc$0) -- #16 //│ false => //│ let x$5 = *(n$0,acc$0) in -- #27 //│ let* (x$6) = @tailcall fact(x$5,x$2) in -- #26 //│ jump j$1(x$6) -- #25 //│ let* (x$0) = fact(1,5) in -- #6 //│ x$0 -- #5 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(fact)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def fact(acc$0,n$0) = //│ let* (r0) = _fact_j$0_opt$9(0,acc$0,n$0,true,true,true) in -- #64 //│ r0 -- #63 //│ def j$1(x$4) = //│ x$4 -- #14 //│ def _fact_j$0_opt$9(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$2,j$0_acc$0,j$0_n$0) = //│ jump _fact_j$0_opt_jp$10(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$2,j$0_acc$0,j$0_n$0) -- #62 //│ def _fact_j$0_opt_jp$10(tailrecBranch$,fact_acc$0,fact_n$0,j$0_x$2,j$0_acc$0,j$0_n$0) = //│ let scrut = ==(2,tailrecBranch$) in -- #61 //│ if scrut -- #60 //│ true => //│ let x$3 = <=(j$0_x$2,0) in -- #59 //│ if x$3 -- #58 //│ true => //│ jump j$1(j$0_acc$0) -- #55 //│ false => //│ let x$5 = *(j$0_n$0,j$0_acc$0) in -- #57 //│ jump _fact_j$0_opt_jp$10(0,x$5,j$0_x$2,j$0_x$2,j$0_acc$0,j$0_n$0) -- #56 //│ false => //│ let x$1 = >(fact_n$0,0) in -- #54 //│ if x$1 -- #53 //│ true => //│ let x$7 = -(fact_n$0,1) in -- #51 //│ jump _fact_j$0_opt_jp$10(2,fact_acc$0,fact_n$0,x$7,fact_acc$0,fact_n$0) -- #50 //│ false => //│ jump _fact_j$0_opt_jp$10(2,fact_acc$0,fact_n$0,0,fact_acc$0,fact_n$0) -- #52 //│ let* (x$0) = fact(1,5) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ 120 :noTailRec :interpIR fun double(x) = x * 2 fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(6, 0) //│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ //│ IR: //│ Program: //│ //│ def double(x$1) = //│ let x$2 = *(x$1,2) in -- #10 //│ x$2 -- #9 //│ def f(n$0,acc$0) = //│ let x$3 = ==(n$0,0) in -- #36 //│ if x$3 -- #35 //│ true => //│ let* (x$5) = double(acc$0) in -- #20 //│ jump j$0(x$5) -- #19 //│ false => //│ let x$6 = -(n$0,1) in -- #34 //│ let x$7 = +(acc$0,1) in -- #33 //│ let* (x$8) = g(x$6,x$7) in -- #32 //│ jump j$0(x$8) -- #31 //│ def j$0(x$4) = //│ x$4 -- #14 //│ def g(m$0,acc$1) = //│ let x$9 = ==(m$0,0) in -- #65 //│ if x$9 -- #64 //│ true => //│ let* (x$11) = double(acc$1) in -- #49 //│ let x$12 = -(0,x$11) in -- #48 //│ jump j$1(x$12) -- #47 //│ false => //│ let x$13 = -(m$0,1) in -- #63 //│ let x$14 = +(acc$1,1) in -- #62 //│ let* (x$15) = f(x$13,x$14) in -- #61 //│ jump j$1(x$15) -- #60 //│ def j$1(x$10) = //│ x$10 -- #40 //│ let* (x$0) = g(6,0) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ -12 :interpIR fun double(x) = x * 2 @tailrec fun f(n, acc) = if n == 0 then double(acc) else g(n - 1, acc + 1) @tailrec fun g(m, acc) = if m == 0 then -double(acc) else f(m - 1, acc + 1) g(6, 0) //│ |#fun| |double|(|x|)| |#=| |x| |*| |2|↵|@|tailrec| |#fun| |f|(|n|,| |acc|)| |#=| |#if| |n| |==| |0| |#then| |double|(|acc|)| |#else| |g|(|n| |-| |1|,| |acc| |+| |1|)|↵|@|tailrec| |#fun| |g|(|m|,| |acc|)| |#=| |#if| |m| |==| |0| |#then| |-|double|(|acc|)| |#else| |f|(|m| |-| |1|,| |acc| |+| |1|)|↵|g|(|6|,| |0|)| //│ Parsed: {fun double = (x,) => *(x,)(2,); fun f = (n, acc,) => if (==(n,)(0,)) then double(acc,) else g(-(n,)(1,), +(acc,)(1,),); fun g = (m, acc,) => if (==(m,)(0,)) then -(0,)(double(acc,),) else f(-(m,)(1,), +(acc,)(1,),); g(6, 0,)} //│ //│ //│ IR: //│ Program: //│ //│ def double(x$1) = //│ let x$2 = *(x$1,2) in -- #10 //│ x$2 -- #9 //│ def f(n$0,acc$0) = //│ let x$3 = ==(n$0,0) in -- #36 //│ if x$3 -- #35 //│ true => //│ let* (x$5) = double(acc$0) in -- #20 //│ jump j$0(x$5) -- #19 //│ false => //│ let x$6 = -(n$0,1) in -- #34 //│ let x$7 = +(acc$0,1) in -- #33 //│ let* (x$8) = g(x$6,x$7) in -- #32 //│ jump j$0(x$8) -- #31 //│ def j$0(x$4) = //│ x$4 -- #14 //│ def g(m$0,acc$1) = //│ let x$9 = ==(m$0,0) in -- #65 //│ if x$9 -- #64 //│ true => //│ let* (x$11) = double(acc$1) in -- #49 //│ let x$12 = -(0,x$11) in -- #48 //│ jump j$1(x$12) -- #47 //│ false => //│ let x$13 = -(m$0,1) in -- #63 //│ let x$14 = +(acc$1,1) in -- #62 //│ let* (x$15) = f(x$13,x$14) in -- #61 //│ jump j$1(x$15) -- #60 //│ def j$1(x$10) = //│ x$10 -- #40 //│ let* (x$0) = g(6,0) in -- #6 //│ x$0 -- #5 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(g, f), Set(double)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def double(x$1) = //│ let x$2 = *(x$1,2) in -- #10 //│ x$2 -- #9 //│ def f(n$0,acc$0) = //│ let* (r0) = _g_f_opt$11(1,true,true,n$0,acc$0) in -- #101 //│ r0 -- #100 //│ def j$0(x$4) = //│ x$4 -- #14 //│ def g(m$0,acc$1) = //│ let* (r0) = _g_f_opt$11(3,m$0,acc$1,true,true) in -- #99 //│ r0 -- #98 //│ def j$1(x$10) = //│ x$10 -- #40 //│ def _g_f_opt$11(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) = //│ jump _g_f_opt_jp$12(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) -- #97 //│ def _g_f_opt_jp$12(tailrecBranch$,g_m$0,g_acc$1,f_n$0,f_acc$0) = //│ let scrut = ==(1,tailrecBranch$) in -- #96 //│ if scrut -- #95 //│ true => //│ let x$3 = ==(f_n$0,0) in -- #94 //│ if x$3 -- #93 //│ true => //│ let* (x$5) = double(f_acc$0) in -- #89 //│ jump j$0(x$5) -- #88 //│ false => //│ let x$6 = -(f_n$0,1) in -- #92 //│ let x$7 = +(f_acc$0,1) in -- #91 //│ jump _g_f_opt_jp$12(3,x$6,x$7,f_n$0,f_acc$0) -- #90 //│ false => //│ let x$9 = ==(g_m$0,0) in -- #87 //│ if x$9 -- #86 //│ true => //│ let* (x$11) = double(g_acc$1) in -- #82 //│ let x$12 = -(0,x$11) in -- #81 //│ jump j$1(x$12) -- #80 //│ false => //│ let x$13 = -(g_m$0,1) in -- #85 //│ let x$14 = +(g_acc$1,1) in -- #84 //│ jump _g_f_opt_jp$12(1,g_m$0,g_acc$1,x$13,x$14) -- #83 //│ let* (x$0) = g(6,0) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ -12 @tailrec fun f(a, b, c) = g(0, 0) @tailrec fun g(d, e) = h(0, 0, 0, 0) @tailrec fun h(p, q, r, s) = f(0, 0, 0) 2 //│ |@|tailrec| |#fun| |f|(|a|,| |b|,| |c|)| |#=| |g|(|0|,| |0|)|↵|@|tailrec| |#fun| |g|(|d|,| |e|)| |#=| |h|(|0|,| |0|,| |0|,| |0|)|↵|@|tailrec| |#fun| |h|(|p|,| |q|,| |r|,| |s|)| |#=| |f|(|0|,| |0|,| |0|)|↵|2| | //│ Parsed: {fun f = (a, b, c,) => g(0, 0,); fun g = (d, e,) => h(0, 0, 0, 0,); fun h = (p, q, r, s,) => f(0, 0, 0,); 2} //│ //│ //│ IR: //│ Program: //│ //│ def f(a$0,b$0,c$0) = //│ let* (x$0) = g(0,0) in -- #7 //│ x$0 -- #6 //│ def g(d$0,e$0) = //│ let* (x$1) = h(0,0,0,0) in -- #18 //│ x$1 -- #17 //│ def h(p$0,q$0,r$0,s$0) = //│ let* (x$2) = f(0,0,0) in -- #27 //│ x$2 -- #26 //│ 2 -- #0 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(h, g, f)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def f(a$0,b$0,c$0) = //│ let* (r0) = _h_g_f_opt$9(0,true,true,true,true,true,true,a$0,b$0,c$0) in -- #48 //│ r0 -- #47 //│ def g(d$0,e$0) = //│ let* (r0) = _h_g_f_opt$9(1,true,true,true,true,d$0,e$0,true,true,true) in -- #46 //│ r0 -- #45 //│ def h(p$0,q$0,r$0,s$0) = //│ let* (r0) = _h_g_f_opt$9(2,p$0,q$0,r$0,s$0,true,true,true,true,true) in -- #44 //│ r0 -- #43 //│ def _h_g_f_opt$9(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) = //│ jump _h_g_f_opt_jp$10(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #42 //│ def _h_g_f_opt_jp$10(tailrecBranch$,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) = //│ let scrut = ==(0,tailrecBranch$) in -- #41 //│ if scrut -- #40 //│ true => //│ jump _h_g_f_opt_jp$10(1,h_p$0,h_q$0,h_r$0,h_s$0,0,0,f_a$0,f_b$0,f_c$0) -- #37 //│ false => //│ let scrut = ==(1,tailrecBranch$) in -- #39 //│ if scrut -- #38 //│ true => //│ jump _h_g_f_opt_jp$10(2,0,0,0,0,g_d$0,g_e$0,f_a$0,f_b$0,f_c$0) -- #36 //│ false => //│ jump _h_g_f_opt_jp$10(0,h_p$0,h_q$0,h_r$0,h_s$0,g_d$0,g_e$0,0,0,0) -- #35 //│ 2 -- #0 :ce fun hello() = @tailcall hello() @tailcall hello() 2 hello() //│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {fun hello = () => {@tailcall hello(); @tailcall hello(); 2}; hello()} //│ //│ //│ IR: //│ Program: //│ //│ def hello() = //│ let* (x$1) = @tailcall hello() in -- #9 //│ let* (x$2) = @tailcall hello() in -- #8 //│ 2 -- #7 //│ let* (x$0) = hello() in -- #2 //│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure //│ ║ l.396: @tailcall hello() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] not a tail call //│ ║ l.397: @tailcall hello() //│ ╙── ^^^^^ //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def hello() = //│ let* (x$1) = @tailcall hello() in -- #9 //│ let* (x$2) = @tailcall hello() in -- #8 //│ 2 -- #7 //│ let* (x$0) = hello() in -- #2 //│ x$0 -- #1 :ce fun hello() = @tailcall hello() 2 hello() //│ |#fun| |hello|(||)| |#=|→|@|tailcall| |hello|(||)|↵|2|←|↵|hello|(||)| | //│ Parsed: {fun hello = () => {@tailcall hello(); 2}; hello()} //│ //│ //│ IR: //│ Program: //│ //│ def hello() = //│ let* (x$1) = @tailcall hello() in -- #6 //│ 2 -- #5 //│ let* (x$0) = hello() in -- #2 //│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] not a tail call //│ ║ l.435: @tailcall hello() //│ ╙── ^^^^^ //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(hello)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def hello() = //│ let* (x$1) = @tailcall hello() in -- #6 //│ 2 -- #5 //│ let* (x$0) = hello() in -- #2 //│ x$0 -- #1 :interpIR @tailrec fun addOne(xs) = if xs is Cons(h, t) then Cons(h + 1, @tailcall addOne(t)) Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ |@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is|→|Cons|(|h|,| |t|)| |#then| |Cons|(|h| |+| |1|,| |@|tailcall| |addOne|(|t|)|)|↵|Nil| |#then| |Nil|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| //│ Parsed: {fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then Cons(+(h,)(1,), @tailcall addOne(t,),); (Nil) then Nil›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: //│ Program: //│ //│ def addOne(xs$0) = //│ case xs$0 of -- #54 //│ Cons => //│ let x$6 = Cons.t(xs$0) in -- #50 //│ let x$7 = Cons.h(xs$0) in -- #49 //│ let x$8 = +(x$7,1) in -- #48 //│ let* (x$9) = @tailcall addOne(x$6) in -- #47 //│ let x$10 = Cons(x$8,x$9) in -- #46 //│ jump j$0(x$10) -- #45 //│ Nil => //│ let x$11 = Nil() in -- #53 //│ jump j$0(x$11) -- #52 //│ def j$0(x$5) = //│ x$5 -- #25 //│ let x$0 = Nil() in -- #23 //│ let x$1 = Cons(3,x$0) in -- #22 //│ let x$2 = Cons(2,x$1) in -- #21 //│ let x$3 = Cons(1,x$2) in -- #20 //│ let* (x$4) = addOne(x$3) in -- #19 //│ x$4 -- #18 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def addOne(xs$0) = //│ let idCtx = _IdContext() in -- #88 //│ let* (res) = addOne_modcons$10(idCtx,xs$0) in -- #87 //│ res -- #86 //│ def j$0(x$5) = //│ x$5 -- #25 //│ def _addOne_ctx_app$8(ctx,x) = //│ case ctx of -- #67 //│ _IdContext => //│ x -- #66 //│ _Context => //│ let field = _Context.field(ctx) in -- #65 //│ let ptr = _Context.ptr(ctx) in -- #64 //│ let _ = assign ptr.t := x in -- #63 //│ let acc = _Context.acc(ctx) in -- #62 //│ acc -- #61 //│ def _addOne_ctx_comp$9(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #73 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #72 //│ let ctx2field = _Context.field(ctx2) in -- #71 //│ let* (newAcc) = _addOne_ctx_app$8(ctx1,ctx2acc) in -- #70 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #69 //│ ret -- #68 //│ def addOne_modcons$10_jp(ctx,xs$0) = //│ case xs$0 of -- #99 //│ Cons => //│ let x$6 = Cons.t(xs$0) in -- #95 //│ let x$7 = Cons.h(xs$0) in -- #94 //│ let x$8 = +(x$7,1) in -- #93 //│ let x$10 = Cons(x$8,0) in -- #92 //│ let ctx2 = _Context(x$10,x$10,0) in -- #91 //│ let* (composed) = _addOne_ctx_comp$9(ctx,ctx2) in -- #90 //│ jump addOne_modcons$10_jp(composed,x$6) -- #89 //│ Nil => //│ let x$11 = Nil() in -- #98 //│ let* (res) = _addOne_ctx_app$8(ctx,x$11) in -- #97 //│ res -- #96 //│ def addOne_modcons$10(ctx,xs$0) = //│ let* (r0) = addOne_modcons$10_jp(ctx,xs$0) in -- #101 //│ r0 -- #100 //│ let x$0 = Nil() in -- #23 //│ let x$1 = Cons(3,x$0) in -- #22 //│ let x$2 = Cons(2,x$1) in -- #21 //│ let x$3 = Cons(1,x$2) in -- #20 //│ let* (x$4) = addOne(x$3) in -- #19 //│ x$4 -- #18 //│ //│ Interpreted: //│ Cons(2,0) :noTailRec :interpIR class Zero class S(x) fun a(n) = if n is S(x) then S(@tailcall b(x)) Zero then S(Zero) fun b(n) = if n is S(x) then S(S(@tailcall a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) //│ |#class| |Zero|↵|#class| |S|(|x|)|↵|#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ //│ IR: //│ Program: //│ class Zero() //│ def a(n$0) = //│ case n$0 of -- #42 //│ S => //│ let x$6 = S.s(n$0) in -- #34 //│ let* (x$7) = @tailcall b(x$6) in -- #33 //│ let x$8 = S(x$7) in -- #32 //│ jump j$0(x$8) -- #31 //│ Zero => //│ let x$9 = Zero() in -- #41 //│ let x$10 = S(x$9) in -- #40 //│ jump j$0(x$10) -- #39 //│ def j$0(x$5) = //│ x$5 -- #19 //│ def b(n$1) = //│ case n$1 of -- #75 //│ S => //│ let x$12 = S.s(n$1) in -- #63 //│ let* (x$13) = @tailcall a(x$12) in -- #62 //│ let x$14 = S(x$13) in -- #61 //│ let x$15 = S(x$14) in -- #60 //│ jump j$1(x$15) -- #59 //│ Zero => //│ let x$16 = Zero() in -- #74 //│ let x$17 = S(x$16) in -- #73 //│ let x$18 = S(x$17) in -- #72 //│ jump j$1(x$18) -- #71 //│ def j$1(x$11) = //│ x$11 -- #44 //│ let x$0 = Zero() in -- #17 //│ let x$1 = S(x$0) in -- #16 //│ let x$2 = S(x$1) in -- #15 //│ let x$3 = S(x$2) in -- #14 //│ let* (x$4) = a(x$3) in -- #13 //│ x$4 -- #12 //│ //│ Interpreted: //│ S(S(S(S(S(S(Zero())))))) :interpIR class Zero class S(x) @tailrec fun a(n) = if n is S(x) then S(@tailcall b(x)) Zero then S(Zero) @tailrec fun b(n) = if n is S(x) then S(S(@tailcall a(x))) Zero then S(S(Zero)) a(S(S(S(Zero)))) //│ |#class| |Zero|↵|#class| |S|(|x|)|↵|@|tailrec| |#fun| |a|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|@|tailcall| |b|(|x|)|)|↵|Zero| |#then| |S|(|Zero|)|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |is|→|S|(|x|)| |#then| |S|(|S|(|@|tailcall| |a|(|x|)|)|)|↵|Zero| |#then| |S|(|S|(|Zero|)|)|←|←|↵|a|(|S|(|S|(|S|(|Zero|)|)|)|)| //│ Parsed: {class Zero {}; class S(x,) {}; fun a = (n,) => {if n is ‹(S(x,)) then S(@tailcall b(x,),); (Zero) then S(Zero,)›}; fun b = (n,) => {if n is ‹(S(x,)) then S(S(@tailcall a(x,),),); (Zero) then S(S(Zero,),)›}; a(S(S(S(Zero,),),),)} //│ //│ //│ IR: //│ Program: //│ class Zero() //│ def a(n$0) = //│ case n$0 of -- #42 //│ S => //│ let x$6 = S.s(n$0) in -- #34 //│ let* (x$7) = @tailcall b(x$6) in -- #33 //│ let x$8 = S(x$7) in -- #32 //│ jump j$0(x$8) -- #31 //│ Zero => //│ let x$9 = Zero() in -- #41 //│ let x$10 = S(x$9) in -- #40 //│ jump j$0(x$10) -- #39 //│ def j$0(x$5) = //│ x$5 -- #19 //│ def b(n$1) = //│ case n$1 of -- #75 //│ S => //│ let x$12 = S.s(n$1) in -- #63 //│ let* (x$13) = @tailcall a(x$12) in -- #62 //│ let x$14 = S(x$13) in -- #61 //│ let x$15 = S(x$14) in -- #60 //│ jump j$1(x$15) -- #59 //│ Zero => //│ let x$16 = Zero() in -- #74 //│ let x$17 = S(x$16) in -- #73 //│ let x$18 = S(x$17) in -- #72 //│ jump j$1(x$18) -- #71 //│ def j$1(x$11) = //│ x$11 -- #44 //│ let x$0 = Zero() in -- #17 //│ let x$1 = S(x$0) in -- #16 //│ let x$2 = S(x$1) in -- #15 //│ let x$3 = S(x$2) in -- #14 //│ let* (x$4) = a(x$3) in -- #13 //│ x$4 -- #12 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(b, a)) //│ Program: //│ class Zero() //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def a(n$0) = //│ let idCtx = _IdContext() in -- #124 //│ let* (res) = a_modcons$13(idCtx,n$0) in -- #123 //│ res -- #122 //│ def j$0(x$5) = //│ x$5 -- #19 //│ def b(n$1) = //│ let idCtx = _IdContext() in -- #110 //│ let* (res) = b_modcons$12(idCtx,n$1) in -- #109 //│ res -- #108 //│ def j$1(x$11) = //│ x$11 -- #44 //│ def _b_a_ctx_app$10(ctx,x) = //│ case ctx of -- #88 //│ _IdContext => //│ x -- #87 //│ _Context => //│ let field = _Context.field(ctx) in -- #86 //│ let ptr = _Context.ptr(ctx) in -- #85 //│ let _ = assign ptr.s := x in -- #84 //│ let acc = _Context.acc(ctx) in -- #83 //│ acc -- #82 //│ def _b_a_ctx_comp$11(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #94 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #93 //│ let ctx2field = _Context.field(ctx2) in -- #92 //│ let* (newAcc) = _b_a_ctx_app$10(ctx1,ctx2acc) in -- #91 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #90 //│ ret -- #89 //│ def b_modcons$12(ctx,n$1) = //│ let* (r0) = _b_modcons$12_a_modcons$13_opt$14(12,ctx,n$1,true,true) in -- #163 //│ r0 -- #162 //│ def a_modcons$13(ctx,n$0) = //│ let* (r0) = _b_modcons$12_a_modcons$13_opt$14(13,true,true,ctx,n$0) in -- #165 //│ r0 -- #164 //│ def _b_modcons$12_a_modcons$13_opt$14(tailrecBranch$,b_modcons$12_ctx,b_modcons$12_n$1,a_modcons$13_ctx,a_modcons$13_n$0) = //│ jump _b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,b_modcons$12_ctx,b_modcons$12_n$1,a_modcons$13_ctx,a_modcons$13_n$0) -- #161 //│ def _b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,b_modcons$12_ctx,b_modcons$12_n$1,a_modcons$13_ctx,a_modcons$13_n$0) = //│ let scrut = ==(13,tailrecBranch$) in -- #160 //│ if scrut -- #159 //│ true => //│ case a_modcons$13_n$0 of -- #158 //│ S => //│ let x$6 = S.s(a_modcons$13_n$0) in -- #153 //│ let x$8 = S(0) in -- #152 //│ let ctx2 = _Context(x$8,x$8,0) in -- #151 //│ let* (composed) = _b_a_ctx_comp$11(a_modcons$13_ctx,ctx2) in -- #150 //│ jump _b_modcons$12_a_modcons$13_opt_jp$15(12,composed,x$6,a_modcons$13_ctx,a_modcons$13_n$0) -- #149 //│ Zero => //│ let x$9 = Zero() in -- #157 //│ let x$10 = S(x$9) in -- #156 //│ let* (res) = _b_a_ctx_app$10(a_modcons$13_ctx,x$10) in -- #155 //│ res -- #154 //│ false => //│ case b_modcons$12_n$1 of -- #148 //│ S => //│ let x$12 = S.s(b_modcons$12_n$1) in -- #142 //│ let x$14 = S(0) in -- #141 //│ let x$15 = S(x$14) in -- #140 //│ let ctx2 = _Context(x$15,x$14,0) in -- #139 //│ let* (composed) = _b_a_ctx_comp$11(b_modcons$12_ctx,ctx2) in -- #138 //│ jump _b_modcons$12_a_modcons$13_opt_jp$15(13,b_modcons$12_ctx,b_modcons$12_n$1,composed,x$12) -- #137 //│ Zero => //│ let x$16 = Zero() in -- #147 //│ let x$17 = S(x$16) in -- #146 //│ let x$18 = S(x$17) in -- #145 //│ let* (res) = _b_a_ctx_app$10(b_modcons$12_ctx,x$18) in -- #144 //│ res -- #143 //│ let x$0 = Zero() in -- #17 //│ let x$1 = S(x$0) in -- #16 //│ let x$2 = S(x$1) in -- #15 //│ let x$3 = S(x$2) in -- #14 //│ let* (x$4) = a(x$3) in -- #13 //│ x$4 -- #12 //│ //│ Interpreted: //│ S(0) :interpIR @tailrec fun addOne(xs) = if xs is Cons(h, t) then val next = @tailcall addOne(t) val ret = Cons(h + 1, next) val rett = ret rett Nil then Nil addOne(Cons(1, Cons(2, Cons(3, Nil)))) //│ |@|tailrec| |#fun| |addOne|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#val| |next| |#=| |@|tailcall| |addOne|(|t|)|↵|#val| |ret| |#=| |Cons|(|h| |+| |1|,| |next|)|↵|#val| |rett| |#=| |ret|↵|rett|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|addOne|(|Cons|(|1|,| |Cons|(|2|,| |Cons|(|3|,| |Nil|)|)|)|)| //│ Parsed: {fun addOne = (xs,) => {if xs is ‹(Cons(h, t,)) then {let next = @tailcall addOne(t,); let ret = Cons(+(h,)(1,), next,); let rett = ret; rett}; (Nil) then {Nil}›}; addOne(Cons(1, Cons(2, Cons(3, Nil,),),),)} //│ //│ //│ IR: //│ Program: //│ //│ def addOne(xs$0) = //│ case xs$0 of -- #57 //│ Cons => //│ let x$6 = Cons.t(xs$0) in -- #53 //│ let x$7 = Cons.h(xs$0) in -- #52 //│ let* (x$8) = @tailcall addOne(x$6) in -- #51 //│ let x$9 = +(x$7,1) in -- #50 //│ let x$10 = Cons(x$9,x$8) in -- #49 //│ jump j$0(x$10) -- #48 //│ Nil => //│ let x$11 = Nil() in -- #56 //│ jump j$0(x$11) -- #55 //│ def j$0(x$5) = //│ x$5 -- #25 //│ let x$0 = Nil() in -- #23 //│ let x$1 = Cons(3,x$0) in -- #22 //│ let x$2 = Cons(2,x$1) in -- #21 //│ let x$3 = Cons(1,x$2) in -- #20 //│ let* (x$4) = addOne(x$3) in -- #19 //│ x$4 -- #18 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(addOne)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def addOne(xs$0) = //│ let idCtx = _IdContext() in -- #91 //│ let* (res) = addOne_modcons$10(idCtx,xs$0) in -- #90 //│ res -- #89 //│ def j$0(x$5) = //│ x$5 -- #25 //│ def _addOne_ctx_app$8(ctx,x) = //│ case ctx of -- #70 //│ _IdContext => //│ x -- #69 //│ _Context => //│ let field = _Context.field(ctx) in -- #68 //│ let ptr = _Context.ptr(ctx) in -- #67 //│ let _ = assign ptr.t := x in -- #66 //│ let acc = _Context.acc(ctx) in -- #65 //│ acc -- #64 //│ def _addOne_ctx_comp$9(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #76 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #75 //│ let ctx2field = _Context.field(ctx2) in -- #74 //│ let* (newAcc) = _addOne_ctx_app$8(ctx1,ctx2acc) in -- #73 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #72 //│ ret -- #71 //│ def addOne_modcons$10_jp(ctx,xs$0) = //│ case xs$0 of -- #102 //│ Cons => //│ let x$6 = Cons.t(xs$0) in -- #98 //│ let x$7 = Cons.h(xs$0) in -- #97 //│ let x$9 = +(x$7,1) in -- #96 //│ let x$10 = Cons(x$9,0) in -- #95 //│ let ctx2 = _Context(x$10,x$10,0) in -- #94 //│ let* (composed) = _addOne_ctx_comp$9(ctx,ctx2) in -- #93 //│ jump addOne_modcons$10_jp(composed,x$6) -- #92 //│ Nil => //│ let x$11 = Nil() in -- #101 //│ let* (res) = _addOne_ctx_app$8(ctx,x$11) in -- #100 //│ res -- #99 //│ def addOne_modcons$10(ctx,xs$0) = //│ let* (r0) = addOne_modcons$10_jp(ctx,xs$0) in -- #104 //│ r0 -- #103 //│ let x$0 = Nil() in -- #23 //│ let x$1 = Cons(3,x$0) in -- #22 //│ let x$2 = Cons(2,x$1) in -- #21 //│ let x$3 = Cons(1,x$2) in -- #20 //│ let* (x$4) = addOne(x$3) in -- #19 //│ x$4 -- #18 //│ //│ Interpreted: //│ Cons(2,0) :interpIR @tailrec fun a(x) = if x is Cons(m, n) then if m < 0 then Cons(-1, Nil) else Cons(m * 4, b(m - 2)) Nil then Nil @tailrec fun b(n) = if n <= 0 then Cons(0, Nil) else a(Cons(n, Nil)) b(16) //│ |@|tailrec| |#fun| |a|(|x|)| |#=|→|#if| |x| |is|→|Cons|(|m|,| |n|)| |#then|→|#if| |m| |<| |0| |#then|→|Cons|(|-|1|,| |Nil|)|←|↵|#else| |→|Cons|(|m| |*| |4|,| |b|(|m| |-| |2|)|)|←|←|↵|Nil| |#then| |Nil|←|←|↵|@|tailrec| |#fun| |b|(|n|)| |#=|→|#if| |n| |<=| |0| |#then| |→|Cons|(|0|,| |Nil|)|←|↵|#else| |→|a|(|Cons|(|n|,| |Nil|)|)|←|←|↵|b|(|16|)| //│ Parsed: {fun a = (x,) => {if x is ‹(Cons(m, n,)) then {if (<(m,)(0,)) then {Cons(-1, Nil,)} else {Cons(*(m,)(4,), b(-(m,)(2,),),)}}; (Nil) then Nil›}; fun b = (n,) => {if (<=(n,)(0,)) then {Cons(0, Nil,)} else {a(Cons(n, Nil,),)}}; b(16,)} //│ //│ //│ IR: //│ Program: //│ //│ def a(x$1) = //│ case x$1 of -- #54 //│ Cons => //│ let x$3 = Cons.t(x$1) in -- #50 //│ let x$4 = Cons.h(x$1) in -- #49 //│ let x$5 = <(x$4,0) in -- #48 //│ if x$5 -- #47 //│ true => //│ let x$7 = Nil() in -- #28 //│ let x$8 = Cons(-1,x$7) in -- #27 //│ jump j$1(x$8) -- #26 //│ false => //│ let x$9 = *(x$4,4) in -- #46 //│ let x$10 = -(x$4,2) in -- #45 //│ let* (x$11) = b(x$10) in -- #44 //│ let x$12 = Cons(x$9,x$11) in -- #43 //│ jump j$1(x$12) -- #42 //│ Nil => //│ let x$13 = Nil() in -- #53 //│ jump j$0(x$13) -- #52 //│ def j$0(x$2) = //│ x$2 -- #6 //│ def j$1(x$6) = //│ jump j$0(x$6) -- #19 //│ def b(n$0) = //│ let x$14 = <=(n$0,0) in -- #82 //│ if x$14 -- #81 //│ true => //│ let x$16 = Nil() in -- #67 //│ let x$17 = Cons(0,x$16) in -- #66 //│ jump j$2(x$17) -- #65 //│ false => //│ let x$18 = Nil() in -- #80 //│ let x$19 = Cons(n$0,x$18) in -- #79 //│ let* (x$20) = a(x$19) in -- #78 //│ jump j$2(x$20) -- #77 //│ def j$2(x$15) = //│ x$15 -- #58 //│ let* (x$0) = b(16) in -- #4 //│ x$0 -- #3 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$2), Set(j$1), Set(j$0), Set(b, a)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def a(x$1) = //│ let idCtx = _IdContext() in -- #136 //│ let* (res) = a_modcons$14(idCtx,x$1) in -- #135 //│ res -- #134 //│ def j$0(x$2) = //│ x$2 -- #6 //│ def j$1(x$6) = //│ jump j$0(x$6) -- #19 //│ def b(n$0) = //│ let idCtx = _IdContext() in -- #114 //│ let* (res) = b_modcons$13(idCtx,n$0) in -- #113 //│ res -- #112 //│ def j$2(x$15) = //│ x$15 -- #58 //│ def _b_a_ctx_app$11(ctx,x) = //│ case ctx of -- #95 //│ _IdContext => //│ x -- #94 //│ _Context => //│ let field = _Context.field(ctx) in -- #93 //│ let ptr = _Context.ptr(ctx) in -- #92 //│ let _ = assign ptr.t := x in -- #91 //│ let acc = _Context.acc(ctx) in -- #90 //│ acc -- #89 //│ def _b_a_ctx_comp$12(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #101 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #100 //│ let ctx2field = _Context.field(ctx2) in -- #99 //│ let* (newAcc) = _b_a_ctx_app$11(ctx1,ctx2acc) in -- #98 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #97 //│ ret -- #96 //│ def b_modcons$13(ctx,n$0) = //│ let* (r0) = _b_modcons$13_a_modcons$14_opt$15(13,ctx,n$0,true,true) in -- #177 //│ r0 -- #176 //│ def a_modcons$14(ctx,x$1) = //│ let* (r0) = _b_modcons$13_a_modcons$14_opt$15(14,true,true,ctx,x$1) in -- #179 //│ r0 -- #178 //│ def _b_modcons$13_a_modcons$14_opt$15(tailrecBranch$,b_modcons$13_ctx,b_modcons$13_n$0,a_modcons$14_ctx,a_modcons$14_x$1) = //│ jump _b_modcons$13_a_modcons$14_opt_jp$16(tailrecBranch$,b_modcons$13_ctx,b_modcons$13_n$0,a_modcons$14_ctx,a_modcons$14_x$1) -- #175 //│ def _b_modcons$13_a_modcons$14_opt_jp$16(tailrecBranch$,b_modcons$13_ctx,b_modcons$13_n$0,a_modcons$14_ctx,a_modcons$14_x$1) = //│ let scrut = ==(14,tailrecBranch$) in -- #174 //│ if scrut -- #173 //│ true => //│ case a_modcons$14_x$1 of -- #172 //│ Cons => //│ let x$3 = Cons.t(a_modcons$14_x$1) in -- #168 //│ let x$4 = Cons.h(a_modcons$14_x$1) in -- #167 //│ let x$5 = <(x$4,0) in -- #166 //│ if x$5 -- #165 //│ true => //│ let x$7 = Nil() in -- #158 //│ let x$8 = Cons(-1,x$7) in -- #157 //│ let* (res) = _b_a_ctx_app$11(a_modcons$14_ctx,x$8) in -- #156 //│ res -- #155 //│ false => //│ let x$9 = *(x$4,4) in -- #164 //│ let x$10 = -(x$4,2) in -- #163 //│ let x$12 = Cons(x$9,0) in -- #162 //│ let ctx2 = _Context(x$12,x$12,0) in -- #161 //│ let* (composed) = _b_a_ctx_comp$12(a_modcons$14_ctx,ctx2) in -- #160 //│ jump _b_modcons$13_a_modcons$14_opt_jp$16(13,composed,x$10,a_modcons$14_ctx,a_modcons$14_x$1) -- #159 //│ Nil => //│ let x$13 = Nil() in -- #171 //│ let* (res) = _b_a_ctx_app$11(a_modcons$14_ctx,x$13) in -- #170 //│ res -- #169 //│ false => //│ let x$14 = <=(b_modcons$13_n$0,0) in -- #154 //│ if x$14 -- #153 //│ true => //│ let x$16 = Nil() in -- #149 //│ let x$17 = Cons(0,x$16) in -- #148 //│ let* (res) = _b_a_ctx_app$11(b_modcons$13_ctx,x$17) in -- #147 //│ res -- #146 //│ false => //│ let x$18 = Nil() in -- #152 //│ let x$19 = Cons(b_modcons$13_n$0,x$18) in -- #151 //│ jump _b_modcons$13_a_modcons$14_opt_jp$16(14,b_modcons$13_ctx,b_modcons$13_n$0,b_modcons$13_ctx,x$19) -- #150 //│ let* (x$0) = b(16) in -- #4 //│ x$0 -- #3 //│ //│ Interpreted: //│ Cons(64,0) :noTailRec :interpIR fun foo(xs) = if xs is Cons(h, t) then if h > 5 then foo(t) else val item = if h < 3 then -1 else 100 Cons(item, Cons(h, foo(t))) Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| //│ Parsed: {fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ //│ IR: //│ Program: //│ //│ def foo(xs$0) = //│ case xs$0 of -- #104 //│ Cons => //│ let x$10 = Cons.t(xs$0) in -- #100 //│ let x$11 = Cons.h(xs$0) in -- #99 //│ let x$12 = >(x$11,5) in -- #98 //│ if x$12 -- #97 //│ true => //│ let* (x$14) = foo(x$10) in -- #68 //│ jump j$1(x$14) -- #67 //│ false => //│ let x$15 = <(x$11,3) in -- #96 //│ if x$15 -- #95 //│ true => //│ jump j$2(-1,x$10,x$11) -- #92 //│ false => //│ jump j$2(100,x$10,x$11) -- #94 //│ Nil => //│ let x$20 = Nil() in -- #103 //│ jump j$0(x$20) -- #102 //│ def j$0(x$9) = //│ x$9 -- #49 //│ def j$1(x$13) = //│ jump j$0(x$13) -- #62 //│ def j$2(x$16,x$10,x$11) = //│ let* (x$17) = foo(x$10) in -- #90 //│ let x$18 = Cons(x$11,x$17) in -- #89 //│ let x$19 = Cons(x$16,x$18) in -- #88 //│ jump j$1(x$19) -- #87 //│ let x$0 = Nil() in -- #47 //│ let x$1 = Cons(9,x$0) in -- #46 //│ let x$2 = Cons(3,x$1) in -- #45 //│ let x$3 = Cons(2,x$2) in -- #44 //│ let x$4 = Cons(4,x$3) in -- #43 //│ let x$5 = Cons(7,x$4) in -- #42 //│ let x$6 = Cons(6,x$5) in -- #41 //│ let x$7 = Cons(1,x$6) in -- #40 //│ let* (x$8) = foo(x$7) in -- #39 //│ x$8 -- #38 //│ //│ Interpreted: //│ Cons(-1,Cons(1,Cons(100,Cons(4,Cons(-1,Cons(2,Cons(100,Cons(3,Nil())))))))) :interpIR @tailrec fun foo(xs) = if xs is Cons(h, t) then if h > 5 then @tailcall foo(t) else val item = if h < 3 then -1 else 100 Cons(item, Cons(h, @tailcall foo(t))) Nil then Nil foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil)))))))) //│ |@|tailrec| |#fun| |foo|(|xs|)| |#=|→|#if| |xs| |is| |→|Cons|(|h|,| |t|)| |#then|→|#if| |h| |>| |5| |#then| |@|tailcall| |foo|(|t|)|↵|#else| |→|#val| |item| |#=| |#if| |h| |<| |3| |#then| |-|1| |#else| |100| |↵|Cons|(|item|,| |Cons|(|h|,| |@|tailcall| |foo|(|t|)|)|)|←|←|↵|Nil| |#then| |→|Nil|←|←|←|↵|foo|(|Cons|(|1|,| |Cons|(|6|,| |Cons|(|7|,| |Cons|(|4|,| |Cons|(|2|,| |Cons|(|3|,| |Cons|(|9|,| |Nil|)|)|)|)|)|)|)|)| //│ Parsed: {fun foo = (xs,) => {if xs is ‹(Cons(h, t,)) then {if (>(h,)(5,)) then @tailcall foo(t,) else {let item = if (<(h,)(3,)) then -1 else 100; Cons(item, Cons(h, @tailcall foo(t,),),)}}; (Nil) then {Nil}›}; foo(Cons(1, Cons(6, Cons(7, Cons(4, Cons(2, Cons(3, Cons(9, Nil,),),),),),),),)} //│ //│ //│ IR: //│ Program: //│ //│ def foo(xs$0) = //│ case xs$0 of -- #104 //│ Cons => //│ let x$10 = Cons.t(xs$0) in -- #100 //│ let x$11 = Cons.h(xs$0) in -- #99 //│ let x$12 = >(x$11,5) in -- #98 //│ if x$12 -- #97 //│ true => //│ let* (x$14) = @tailcall foo(x$10) in -- #68 //│ jump j$1(x$14) -- #67 //│ false => //│ let x$15 = <(x$11,3) in -- #96 //│ if x$15 -- #95 //│ true => //│ jump j$2(-1,x$10,x$11) -- #92 //│ false => //│ jump j$2(100,x$10,x$11) -- #94 //│ Nil => //│ let x$20 = Nil() in -- #103 //│ jump j$0(x$20) -- #102 //│ def j$0(x$9) = //│ x$9 -- #49 //│ def j$1(x$13) = //│ jump j$0(x$13) -- #62 //│ def j$2(x$16,x$10,x$11) = //│ let* (x$17) = @tailcall foo(x$10) in -- #90 //│ let x$18 = Cons(x$11,x$17) in -- #89 //│ let x$19 = Cons(x$16,x$18) in -- #88 //│ jump j$1(x$19) -- #87 //│ let x$0 = Nil() in -- #47 //│ let x$1 = Cons(9,x$0) in -- #46 //│ let x$2 = Cons(3,x$1) in -- #45 //│ let x$3 = Cons(2,x$2) in -- #44 //│ let x$4 = Cons(4,x$3) in -- #43 //│ let x$5 = Cons(7,x$4) in -- #42 //│ let x$6 = Cons(6,x$5) in -- #41 //│ let x$7 = Cons(1,x$6) in -- #40 //│ let* (x$8) = foo(x$7) in -- #39 //│ x$8 -- #38 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$1), Set(j$0), Set(foo)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def foo(xs$0) = //│ let idCtx = _IdContext() in -- #144 //│ let* (res) = foo_modcons$13(idCtx,xs$0) in -- #143 //│ res -- #142 //│ def j$0(x$9) = //│ x$9 -- #49 //│ def j$1(x$13) = //│ jump j$0(x$13) -- #62 //│ def _foo_ctx_app$10(ctx,x) = //│ case ctx of -- #117 //│ _IdContext => //│ x -- #116 //│ _Context => //│ let field = _Context.field(ctx) in -- #115 //│ let ptr = _Context.ptr(ctx) in -- #114 //│ let _ = assign ptr.t := x in -- #113 //│ let acc = _Context.acc(ctx) in -- #112 //│ acc -- #111 //│ def _foo_ctx_comp$11(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #123 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #122 //│ let ctx2field = _Context.field(ctx2) in -- #121 //│ let* (newAcc) = _foo_ctx_app$10(ctx1,ctx2acc) in -- #120 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #119 //│ ret -- #118 //│ def foo_modcons$13(ctx,xs$0) = //│ let* (r0) = _foo_modcons$13_j$2_modcons$12_opt$14(13,ctx,xs$0,true,true,true,true) in -- #180 //│ r0 -- #179 //│ def _foo_modcons$13_j$2_modcons$12_opt$14(tailrecBranch$,foo_modcons$13_ctx,foo_modcons$13_xs$0,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) = //│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(tailrecBranch$,foo_modcons$13_ctx,foo_modcons$13_xs$0,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) -- #178 //│ def _foo_modcons$13_j$2_modcons$12_opt_jp$15(tailrecBranch$,foo_modcons$13_ctx,foo_modcons$13_xs$0,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) = //│ let scrut = ==(12,tailrecBranch$) in -- #177 //│ if scrut -- #176 //│ true => //│ let x$18 = Cons(j$2_modcons$12_x$11,0) in -- #175 //│ let x$19 = Cons(j$2_modcons$12_x$16,x$18) in -- #174 //│ let ctx2 = _Context(x$19,x$18,0) in -- #173 //│ let* (composed) = _foo_ctx_comp$11(j$2_modcons$12_ctx,ctx2) in -- #172 //│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(13,composed,j$2_modcons$12_x$10,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) -- #171 //│ false => //│ case foo_modcons$13_xs$0 of -- #170 //│ Cons => //│ let x$10 = Cons.t(foo_modcons$13_xs$0) in -- #166 //│ let x$11 = Cons.h(foo_modcons$13_xs$0) in -- #165 //│ let x$12 = >(x$11,5) in -- #164 //│ if x$12 -- #163 //│ true => //│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(13,foo_modcons$13_ctx,x$10,j$2_modcons$12_ctx,j$2_modcons$12_x$16,j$2_modcons$12_x$10,j$2_modcons$12_x$11) -- #158 //│ false => //│ let x$15 = <(x$11,3) in -- #162 //│ if x$15 -- #161 //│ true => //│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(12,foo_modcons$13_ctx,foo_modcons$13_xs$0,foo_modcons$13_ctx,-1,x$10,x$11) -- #159 //│ false => //│ jump _foo_modcons$13_j$2_modcons$12_opt_jp$15(12,foo_modcons$13_ctx,foo_modcons$13_xs$0,foo_modcons$13_ctx,100,x$10,x$11) -- #160 //│ Nil => //│ let x$20 = Nil() in -- #169 //│ let* (res) = _foo_ctx_app$10(foo_modcons$13_ctx,x$20) in -- #168 //│ res -- #167 //│ let x$0 = Nil() in -- #47 //│ let x$1 = Cons(9,x$0) in -- #46 //│ let x$2 = Cons(3,x$1) in -- #45 //│ let x$3 = Cons(2,x$2) in -- #44 //│ let x$4 = Cons(4,x$3) in -- #43 //│ let x$5 = Cons(7,x$4) in -- #42 //│ let x$6 = Cons(6,x$5) in -- #41 //│ let x$7 = Cons(1,x$6) in -- #40 //│ let* (x$8) = foo(x$7) in -- #39 //│ x$8 -- #38 //│ //│ Interpreted: //│ Cons(-1,Cons(1,0)) :ce fun b() = a() a() @tailrec fun a() = if 0 < 1 then a() else b() a() //│ |#fun| |b|(||)| |#=|→|a|(||)|↵|a|(||)|←|↵|@|tailrec| |↵|#fun| |a|(||)| |#=| |→|#if| |0| |<| |1| |#then| |a|(||)|↵|#else| |b|(||)|←|↵|a|(||)| //│ Parsed: {fun b = () => {a(); a()}; fun a = () => {if (<(0,)(1,)) then a() else b()}; a()} //│ //│ //│ IR: //│ Program: //│ //│ def b() = //│ let* (x$1) = a() in -- #8 //│ let* (x$2) = a() in -- #7 //│ x$2 -- #6 //│ def a() = //│ let x$3 = <(0,1) in -- #22 //│ if x$3 -- #21 //│ true => //│ let* (x$5) = a() in -- #16 //│ jump j$0(x$5) -- #15 //│ false => //│ let* (x$6) = b() in -- #20 //│ jump j$0(x$6) -- #19 //│ def j$0(x$4) = //│ x$4 -- #12 //│ let* (x$0) = a() in -- #2 //│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec //│ ║ l.1203: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call //│ ║ l.1201: a() //│ ╙── ^ //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(j$0), Set(a, b)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def b() = //│ let* (r0) = _a_b_opt$9(0) in -- #45 //│ r0 -- #44 //│ def a() = //│ let* (r0) = _a_b_opt$9(1) in -- #43 //│ r0 -- #42 //│ def j$0(x$4) = //│ x$4 -- #12 //│ def _a_b_opt$9(tailrecBranch$) = //│ jump _a_b_opt_jp$10(tailrecBranch$) -- #41 //│ def _a_b_opt_jp$10(tailrecBranch$) = //│ let scrut = ==(0,tailrecBranch$) in -- #40 //│ if scrut -- #39 //│ true => //│ let* (x$1) = a() in -- #38 //│ jump _a_b_opt_jp$10(1) -- #37 //│ false => //│ let x$3 = <(0,1) in -- #36 //│ if x$3 -- #35 //│ true => //│ jump _a_b_opt_jp$10(1) -- #33 //│ false => //│ jump _a_b_opt_jp$10(0) -- #34 //│ let* (x$0) = a() in -- #2 //│ x$0 -- #1 :ce class A(a, b) @tailrec fun a() = A(b(), 1) fun b() = A(c(), @tailcall a()) fun c() = A(b(), 1) a() //│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|c|(||)|,| |@|tailcall| |a|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|a|(||)| //│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(c(), @tailcall a(),); fun c = () => A(b(), 1,); a()} //│ //│ //│ IR: //│ Program: //│ class A(a,b) //│ def a() = //│ let* (x$1) = b() in -- #11 //│ let x$2 = A(x$1,1) in -- #10 //│ x$2 -- #9 //│ def b() = //│ let* (x$3) = c() in -- #22 //│ let* (x$4) = @tailcall a() in -- #21 //│ let x$5 = A(x$3,x$4) in -- #20 //│ x$5 -- #19 //│ def c() = //│ let* (x$6) = b() in -- #31 //│ let x$7 = A(x$6,1) in -- #30 //│ x$7 -- #29 //│ let* (x$0) = a() in -- #2 //│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec //│ ║ l.1273: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call //│ ║ l.1275: fun b() = A(c(), @tailcall a()) //│ ╙── ^ //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(c, b, a)) //│ Program: //│ class A(a,b) //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def a() = //│ let idCtx = _IdContext() in -- #81 //│ let* (res) = a_modcons$13(idCtx) in -- #80 //│ res -- #79 //│ def b() = //│ let idCtx = _IdContext() in -- #73 //│ let* (res) = b_modcons$12(idCtx) in -- #72 //│ res -- #71 //│ def c() = //│ let idCtx = _IdContext() in -- #64 //│ let* (res) = c_modcons$11(idCtx) in -- #63 //│ res -- #62 //│ def _c_b_a_ctx_app$9(ctx,x) = //│ case ctx of -- #50 //│ _IdContext => //│ x -- #49 //│ _Context => //│ let field = _Context.field(ctx) in -- #48 //│ let scrut = ==(1,field) in -- #47 //│ if scrut -- #46 //│ true => //│ let ptr = _Context.ptr(ctx) in -- #45 //│ let _ = assign ptr.b := x in -- #44 //│ let acc = _Context.acc(ctx) in -- #43 //│ acc -- #42 //│ false => //│ let ptr = _Context.ptr(ctx) in -- #41 //│ let _ = assign ptr.a := x in -- #40 //│ let acc = _Context.acc(ctx) in -- #39 //│ acc -- #38 //│ def _c_b_a_ctx_comp$10(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #56 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #55 //│ let ctx2field = _Context.field(ctx2) in -- #54 //│ let* (newAcc) = _c_b_a_ctx_app$9(ctx1,ctx2acc) in -- #53 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #52 //│ ret -- #51 //│ def c_modcons$11(ctx) = //│ let* (r0) = _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(11,ctx,true,true) in -- #105 //│ r0 -- #104 //│ def b_modcons$12(ctx) = //│ let* (r0) = _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(12,true,ctx,true) in -- #107 //│ r0 -- #106 //│ def a_modcons$13(ctx) = //│ let* (r0) = _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(13,true,true,ctx) in -- #109 //│ r0 -- #108 //│ def _c_modcons$11_b_modcons$12_a_modcons$13_opt$14(tailrecBranch$,c_modcons$11_ctx,b_modcons$12_ctx,a_modcons$13_ctx) = //│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,c_modcons$11_ctx,b_modcons$12_ctx,a_modcons$13_ctx) -- #103 //│ def _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(tailrecBranch$,c_modcons$11_ctx,b_modcons$12_ctx,a_modcons$13_ctx) = //│ let scrut = ==(13,tailrecBranch$) in -- #102 //│ if scrut -- #101 //│ true => //│ let x$2 = A(0,1) in -- #98 //│ let ctx2 = _Context(x$2,x$2,0) in -- #97 //│ let* (composed) = _c_b_a_ctx_comp$10(a_modcons$13_ctx,ctx2) in -- #96 //│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(12,c_modcons$11_ctx,composed,a_modcons$13_ctx) -- #95 //│ false => //│ let scrut = ==(12,tailrecBranch$) in -- #100 //│ if scrut -- #99 //│ true => //│ let* (x$3) = c() in -- #94 //│ let x$5 = A(x$3,0) in -- #93 //│ let ctx2 = _Context(x$5,x$5,1) in -- #92 //│ let* (composed) = _c_b_a_ctx_comp$10(b_modcons$12_ctx,ctx2) in -- #91 //│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(13,c_modcons$11_ctx,b_modcons$12_ctx,composed) -- #90 //│ false => //│ let x$7 = A(0,1) in -- #89 //│ let ctx2 = _Context(x$7,x$7,0) in -- #88 //│ let* (composed) = _c_b_a_ctx_comp$10(c_modcons$11_ctx,ctx2) in -- #87 //│ jump _c_modcons$11_b_modcons$12_a_modcons$13_opt_jp$15(12,c_modcons$11_ctx,composed,a_modcons$13_ctx) -- #86 //│ let* (x$0) = a() in -- #2 //│ x$0 -- #1 // TODO: Purity check class A(a, b) @tailrec fun a() = A(b(), 1) fun b() = A(@tailcall a(), c()) fun c() = A(0, 1) a() //│ |#class| |A|(|a|,| |b|)|↵|@|tailrec|↵|#fun| |a|(||)| |#=| |A|(|b|(||)|,| |1|)|↵|#fun| |b|(||)| |#=| |A|(|@|tailcall| |a|(||)|,| |c|(||)|)|↵|#fun| |c|(||)| |#=| |A|(|0|,| |1|)|↵|a|(||)| //│ Parsed: {class A(a, b,) {}; fun a = () => A(b(), 1,); fun b = () => A(@tailcall a(), c(),); fun c = () => A(0, 1,); a()} //│ //│ //│ IR: //│ Program: //│ class A(a,b) //│ def a() = //│ let* (x$1) = b() in -- #11 //│ let x$2 = A(x$1,1) in -- #10 //│ x$2 -- #9 //│ def b() = //│ let* (x$3) = @tailcall a() in -- #22 //│ let* (x$4) = c() in -- #21 //│ let x$5 = A(x$3,x$4) in -- #20 //│ x$5 -- #19 //│ def c() = //│ let x$6 = A(0,1) in -- #29 //│ x$6 -- #28 //│ let* (x$0) = a() in -- #2 //│ x$0 -- #1 //│ ╔══[COMPILATION ERROR] not a tail call, as the remaining functions may be impure //│ ║ l.1391: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] function `a` is not tail-recursive, but is marked as @tailrec //│ ║ l.1389: @tailrec //│ ║ ^^^^^^^ //│ ╟── it could self-recurse through this call, which may not be a tail-call //│ ║ l.1391: fun b() = A(@tailcall a(), c()) //│ ╙── ^ //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(b, a), Set(c)) //│ Program: //│ class A(a,b) //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def a() = //│ let idCtx = _IdContext() in -- #64 //│ let* (res) = a_modcons$12(idCtx) in -- #63 //│ res -- #62 //│ def b() = //│ let idCtx = _IdContext() in -- #56 //│ let* (res) = b_modcons$11(idCtx) in -- #55 //│ res -- #54 //│ def c() = //│ let x$6 = A(0,1) in -- #29 //│ x$6 -- #28 //│ def _b_a_ctx_app$9(ctx,x) = //│ case ctx of -- #42 //│ _IdContext => //│ x -- #41 //│ _Context => //│ let field = _Context.field(ctx) in -- #40 //│ let ptr = _Context.ptr(ctx) in -- #39 //│ let _ = assign ptr.a := x in -- #38 //│ let acc = _Context.acc(ctx) in -- #37 //│ acc -- #36 //│ def _b_a_ctx_comp$10(ctx1,ctx2) = //│ let ctx2acc = _Context.acc(ctx2) in -- #48 //│ let ctx2ptr = _Context.ptr(ctx2) in -- #47 //│ let ctx2field = _Context.field(ctx2) in -- #46 //│ let* (newAcc) = _b_a_ctx_app$9(ctx1,ctx2acc) in -- #45 //│ let ret = _Context(newAcc,ctx2ptr,ctx2field) in -- #44 //│ ret -- #43 //│ def b_modcons$11(ctx) = //│ let* (r0) = _b_modcons$11_a_modcons$12_opt$13(11,ctx,true) in -- #83 //│ r0 -- #82 //│ def a_modcons$12(ctx) = //│ let* (r0) = _b_modcons$11_a_modcons$12_opt$13(12,true,ctx) in -- #85 //│ r0 -- #84 //│ def _b_modcons$11_a_modcons$12_opt$13(tailrecBranch$,b_modcons$11_ctx,a_modcons$12_ctx) = //│ jump _b_modcons$11_a_modcons$12_opt_jp$14(tailrecBranch$,b_modcons$11_ctx,a_modcons$12_ctx) -- #81 //│ def _b_modcons$11_a_modcons$12_opt_jp$14(tailrecBranch$,b_modcons$11_ctx,a_modcons$12_ctx) = //│ let scrut = ==(12,tailrecBranch$) in -- #80 //│ if scrut -- #79 //│ true => //│ let x$2 = A(0,1) in -- #78 //│ let ctx2 = _Context(x$2,x$2,0) in -- #77 //│ let* (composed) = _b_a_ctx_comp$10(a_modcons$12_ctx,ctx2) in -- #76 //│ jump _b_modcons$11_a_modcons$12_opt_jp$14(11,composed,a_modcons$12_ctx) -- #75 //│ false => //│ let* (x$3) = @tailcall a() in -- #74 //│ let* (x$4) = c() in -- #73 //│ let x$5 = A(x$3,x$4) in -- #72 //│ let* (res) = _b_a_ctx_app$9(b_modcons$11_ctx,x$5) in -- #71 //│ res -- #70 //│ let* (x$0) = a() in -- #2 //│ x$0 -- #1 :ce @tailcall 1 //│ |@|tailcall| |1| //│ Parsed: {@tailcall 1} //│ ╔══[COMPILATION ERROR] @tailcall may only be used to annotate function calls //│ ║ l.1486: @tailcall 1 //│ ╙── ^^^^^^^^ //│ //│ //│ IR: //│ Program: //│ //│ //│ 1 -- #0 //│ //│ //│ Strongly Connected Tail Calls: //│ List() //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ //│ 1 -- #0 :ce @tailrec 1 //│ |@|tailrec| |1| //│ Parsed: {@tailrec 1} //│ ╔══[COMPILATION ERROR] @tailrec may only be used to annotate functions //│ ║ l.1510: @tailrec 1 //│ ╙── ^^^^^^^ //│ //│ //│ IR: //│ Program: //│ //│ //│ 1 -- #0 //│ //│ //│ Strongly Connected Tail Calls: //│ List() //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ //│ 1 -- #0 :ce fun foo() = @tailrec foo() foo() //│ |#fun| |foo|(||)| |#=|→|@|tailrec| |foo|(||)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {@tailrec foo()}; foo()} //│ ╔══[COMPILATION ERROR] @tailrec is for annotating functions; try @tailcall instead //│ ║ l.1535: @tailrec foo() //│ ╙── ^^^^^^^ //│ //│ //│ IR: //│ Program: //│ //│ def foo() = //│ let* (x$1) = foo() in -- #5 //│ x$1 -- #4 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(foo)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def foo_jp() = //│ jump foo_jp() -- #12 //│ def foo() = //│ let* (r0) = foo_jp() in -- #14 //│ r0 -- #13 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 :ce @tailcall fun foo() = foo() foo() //│ |@|tailcall|↵|#fun| |foo|(||)| |#=|→|foo|(||)|←|↵|foo|(||)| //│ Parsed: {fun foo = () => {foo()}; foo()} //│ ╔══[COMPILATION ERROR] @tailcall is for annotating function calls; try @tailrec instead //│ ║ l.1568: @tailcall //│ ╙── ^^^^^^^^ //│ //│ //│ IR: //│ Program: //│ //│ def foo() = //│ let* (x$1) = foo() in -- #5 //│ x$1 -- #4 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 //│ //│ //│ Strongly Connected Tail Calls: //│ List(Set(foo)) //│ Program: //│ class _IdContext() //│ class _Context(acc,ptr,field) //│ def foo_jp() = //│ jump foo_jp() -- #12 //│ def foo() = //│ let* (r0) = foo_jp() in -- #14 //│ r0 -- #13 //│ let* (x$0) = foo() in -- #2 //│ x$0 -- #1 ================================================ FILE: compiler/shared/test/diff-ir/LiftClass.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :genCpp :runCpp :interpIR fun main(x) = class InnerClass(y) extends Callable { fun apply1(z) = x + y + z } let ic = InnerClass(1) ic(2) + ic(3) main(4) //│ |#fun| |main|(|x|)| |#=|→|#class| |InnerClass|(|y|)| |#extends| |Callable| |{|→|#fun| |apply1|(|z|)| |#=| |x| |+| |y| |+| |z|←|↵|}|↵|#let| |ic| |#=| |InnerClass|(|1|)|↵|ic|(|2|)| |+| |ic|(|3|)|←|↵|main|(|4|)| //│ Parsed: {fun main = (x,) => {class InnerClass(y,): Callable {fun apply1 = (z,) => +(+(x, y,), z,)}; let ic = InnerClass(1,); +(ic(2,), ic(3,),)}; main(4,)} //│ //│ //│ IR: //│ Program: //│ class InnerClass(y,x) extends Callable { //│ def apply1(z$0) = //│ let x$6 = +(x,y) in -- #45 //│ let x$7 = +(x$6,z$0) in -- #44 //│ x$7 -- #43 //│ } //│ def main(x$1) = //│ let x$2 = InnerClass(1,x$1) in -- #26 //│ let x$3 = Callable.apply1(x$2,2) in -- #25 //│ let x$4 = Callable.apply1(x$2,3) in -- #24 //│ let x$5 = +(x$3,x$4) in -- #23 //│ x$5 -- #22 //│ let* (x$0) = main(4) in -- #4 //│ x$0 -- #3 //│ //│ Interpreted: //│ 15 //│ //│ //│ Execution succeeded: //│ 15 //│ :genCpp :runCpp :interpIR fun main(x) = class InnerClass(y) extends Callable { fun apply1(z) = module InnerClass2 extends Callable { fun apply1(w) = w + z } InnerClass2 } let ic = InnerClass(1) ic(2)(2) + ic(3)(1) main(4) //│ |#fun| |main|(|x|)| |#=|→|#class| |InnerClass|(|y|)| |#extends| |Callable| |{|→|#fun| |apply1|(|z|)| |#=|→|#module| |InnerClass2| |#extends| |Callable| |{|→|#fun| |apply1|(|w|)| |#=| |w| |+| |z|←|↵|}|↵|InnerClass2|←|←|↵|}|↵|#let| |ic| |#=| |InnerClass|(|1|)|↵|ic|(|2|)|(|2|)| |+| |ic|(|3|)|(|1|)|←|↵|main|(|4|)| //│ Parsed: {fun main = (x,) => {class InnerClass(y,): Callable {fun apply1 = (z,) => {module InnerClass2: Callable {fun apply1 = (w,) => +(w, z,)}; InnerClass2}}; let ic = InnerClass(1,); +(ic(2,)(2,), ic(3,)(1,),)}; main(4,)} //│ //│ //│ IR: //│ Program: //│ class InnerClass(y) extends Callable { //│ def apply1(z$0) = //│ let x$8 = InnerClass2(z$0) in -- #44 //│ x$8 -- #43 //│ } //│ class InnerClass2(z) extends Callable { //│ def apply1(w$0) = //│ let x$9 = +(w$0,z) in -- #51 //│ x$9 -- #50 //│ } //│ def main(x$1) = //│ let x$2 = InnerClass(1) in -- #36 //│ let x$3 = Callable.apply1(x$2,2) in -- #35 //│ let x$4 = Callable.apply1(x$3,2) in -- #34 //│ let x$5 = Callable.apply1(x$2,3) in -- #33 //│ let x$6 = Callable.apply1(x$5,1) in -- #32 //│ let x$7 = +(x$4,x$6) in -- #31 //│ x$7 -- #30 //│ let* (x$0) = main(4) in -- #4 //│ x$0 -- #3 //│ //│ Interpreted: //│ 8 //│ //│ //│ Execution succeeded: //│ 8 //│ :genCpp :runCpp :interpIR fun main(x) = class InnerClass(y) extends Callable { fun f(x) = y } let ic = InnerClass(1) InnerClass.f(ic, Nil) main(2) //│ |#fun| |main|(|x|)| |#=|→|#class| |InnerClass|(|y|)| |#extends| |Callable| |{|→|#fun| |f|(|x|)| |#=| |y|←|↵|}|↵|#let| |ic| |#=| |InnerClass|(|1|)|↵|InnerClass|.f|(|ic|,| |Nil|)|←|↵|main|(|2|)| //│ Parsed: {fun main = (x,) => {class InnerClass(y,): Callable {fun f = (x,) => y}; let ic = InnerClass(1,); (InnerClass).f(ic, Nil,)}; main(2,)} //│ //│ //│ IR: //│ Program: //│ class InnerClass(y) extends Callable { //│ def f(x$5) = //│ y -- #24 //│ } //│ def main(x$1) = //│ let x$2 = InnerClass(1) in -- #17 //│ let x$3 = Nil() in -- #16 //│ let x$4 = InnerClass.f(x$2,x$3) in -- #15 //│ x$4 -- #14 //│ let* (x$0) = main(2) in -- #4 //│ x$0 -- #3 //│ //│ Interpreted: //│ 1 //│ //│ //│ Execution succeeded: //│ 1 //│ ================================================ FILE: compiler/shared/test/diff-ir/LiftFun.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :genCpp :runCpp :interpIR fun main(init, key) = fun r(x) = if x <= 0 then key else r(x - 1) r(init) main(1, 42) //│ |#fun| |main|(|init|,| |key|)| |#=|→|#fun| |r|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |#else| |r|(|x| |-| |1|)|↵|r|(|init|)|←|↵|main|(|1|,| |42|)| //│ Parsed: {fun main = (init, key,) => {fun r = (x,) => if (<=(x, 0,)) then key else r(-(x, 1,),); r(init,)}; main(1, 42,)} //│ //│ //│ IR: //│ Program: //│ //│ def main(init$0,key$0) = //│ let* (x$1) = r(init$0,key$0) in -- #11 //│ x$1 -- #10 //│ def r(x$2,key$1) = //│ let x$3 = <=(x$2,0) in -- #40 //│ if x$3 -- #39 //│ true => //│ jump j$0(key$1) -- #26 //│ false => //│ let x$5 = -(x$2,1) in -- #38 //│ let* (x$6) = r(x$5,key$1) in -- #37 //│ jump j$0(x$6) -- #36 //│ def j$0(x$4) = //│ x$4 -- #24 //│ let* (x$0) = main(1,42) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ 42 //│ //│ //│ Execution succeeded: //│ 42 //│ :genCpp :runCpp :interpIR fun main(init, key) = fun ping(x) = if x <= 0 then key + 1 else pong(x - 1) fun pong(x) = if x <= 0 then key + 2 else ping(x - 1) ping(init) main(1, 42) //│ |#fun| |main|(|init|,| |key|)| |#=|→|#fun| |ping|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |1| |#else| |pong|(|x| |-| |1|)|↵|#fun| |pong|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |2| |#else| |ping|(|x| |-| |1|)|↵|ping|(|init|)|←|↵|main|(|1|,| |42|)| //│ Parsed: {fun main = (init, key,) => {fun ping = (x,) => if (<=(x, 0,)) then +(key, 1,) else pong(-(x, 1,),); fun pong = (x,) => if (<=(x, 0,)) then +(key, 2,) else ping(-(x, 1,),); ping(init,)}; main(1, 42,)} //│ //│ //│ IR: //│ Program: //│ //│ def main(init$0,key$0) = //│ let* (x$1) = ping(init$0,key$0) in -- #11 //│ x$1 -- #10 //│ def ping(x$2,key$1) = //│ let x$3 = <=(x$2,0) in -- #46 //│ if x$3 -- #45 //│ true => //│ let x$5 = +(key$1,1) in -- #32 //│ jump j$0(x$5) -- #31 //│ false => //│ let x$6 = -(x$2,1) in -- #44 //│ let* (x$7) = pong(x$6,key$1) in -- #43 //│ jump j$0(x$7) -- #42 //│ def j$0(x$4) = //│ x$4 -- #24 //│ def pong(x$8,key$2) = //│ let x$9 = <=(x$8,0) in -- #75 //│ if x$9 -- #74 //│ true => //│ let x$11 = +(key$2,2) in -- #61 //│ jump j$1(x$11) -- #60 //│ false => //│ let x$12 = -(x$8,1) in -- #73 //│ let* (x$13) = ping(x$12,key$2) in -- #72 //│ jump j$1(x$13) -- #71 //│ def j$1(x$10) = //│ x$10 -- #53 //│ let* (x$0) = main(1,42) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ 44 //│ //│ //│ Execution succeeded: //│ 44 //│ :genCpp :runCpp :interpIR fun main(init, key) = let ping = fun ping(x) = if x <= 0 then key + 1 else pong(x - 1) fun pong(x) = if x <= 0 then key + 2 else ping(x - 1) ping ping(init) main(1, 42) //│ |#fun| |main|(|init|,| |key|)| |#=|→|#let| |ping| |#=|→|#fun| |ping|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |1| |#else| |pong|(|x| |-| |1|)|↵|#fun| |pong|(|x|)| |#=| |#if| |x| |<=| |0| |#then| |key| |+| |2| |#else| |ping|(|x| |-| |1|)|↵|ping|←|↵|ping|(|init|)|←|↵|main|(|1|,| |42|)| //│ Parsed: {fun main = (init, key,) => {let ping = {fun ping = (x,) => if (<=(x, 0,)) then +(key, 1,) else pong(-(x, 1,),); fun pong = (x,) => if (<=(x, 0,)) then +(key, 2,) else ping(-(x, 1,),); ping}; ping(init,)}; main(1, 42,)} //│ //│ //│ IR: //│ Program: //│ class Lambda$0(key) extends Callable { //│ def apply1(x$17) = //│ let* (x$18) = ping(x$17,key) in -- #85 //│ x$18 -- #84 //│ } //│ def main(init$0,key$0) = //│ let x$3 = Lambda$0(key$0) in -- #14 //│ let x$4 = Callable.apply1(x$3,init$0) in -- #13 //│ x$4 -- #12 //│ def ping(x$5,key$1) = //│ let x$6 = <=(x$5,0) in -- #49 //│ if x$6 -- #48 //│ true => //│ let x$8 = +(key$1,1) in -- #35 //│ jump j$0(x$8) -- #34 //│ false => //│ let x$9 = -(x$5,1) in -- #47 //│ let* (x$10) = pong(x$9,key$1) in -- #46 //│ jump j$0(x$10) -- #45 //│ def j$0(x$7) = //│ x$7 -- #27 //│ def pong(x$11,key$2) = //│ let x$12 = <=(x$11,0) in -- #78 //│ if x$12 -- #77 //│ true => //│ let x$14 = +(key$2,2) in -- #64 //│ jump j$1(x$14) -- #63 //│ false => //│ let x$15 = -(x$11,1) in -- #76 //│ let* (x$16) = ping(x$15,key$2) in -- #75 //│ jump j$1(x$16) -- #74 //│ def j$1(x$13) = //│ x$13 -- #56 //│ let* (x$0) = main(1,42) in -- #6 //│ x$0 -- #5 //│ //│ Interpreted: //│ 44 //│ //│ //│ Execution succeeded: //│ 44 //│ ================================================ FILE: compiler/shared/test/diff-ir/LiftLambda.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :genCpp :runCpp :interpIR fun compose(f)(g)(x) = f(g(x)) fun main(x) = let y = 1 let lam = z => x + y + z compose(lam)(lam)(2) main(3) //│ |#fun| |compose|(|f|)|(|g|)|(|x|)| |#=| |f|(|g|(|x|)|)|↵|#fun| |main|(|x|)| |#=|→|#let| |y| |#=| |1|↵|#let| |lam| |#=| |z| |#=>| |x| |+| |y| |+| |z|↵|compose|(|lam|)|(|lam|)|(|2|)|←|↵|main|(|3|)| //│ Parsed: {fun compose = (f,) => (g,) => (x,) => f(g(x,),); fun main = (x,) => {let y = 1; let lam = (z,) => +(+(x, y,), z,); compose(lam,)(lam,)(2,)}; main(3,)} //│ //│ //│ IR: //│ Program: //│ class Lambda$0(f) extends Callable { //│ def apply1(g$0) = //│ let x$11 = Lambda$2(f,g$0) in -- #33 //│ x$11 -- #32 //│ } //│ class Lambda$1(x,y) extends Callable { //│ def apply1(z$0) = //│ let x$12 = +(x,y) in -- #46 //│ let x$13 = +(x$12,z$0) in -- #45 //│ x$13 -- #44 //│ } //│ class Lambda$2(f,g) extends Callable { //│ def apply1(x$14) = //│ let x$15 = Callable.apply1(g,x$14) in -- #57 //│ let x$16 = Callable.apply1(f,x$15) in -- #56 //│ x$16 -- #55 //│ } //│ def compose(f$0) = //│ let x$2 = Lambda$0(f$0) in -- #6 //│ x$2 -- #5 //│ def main(x$3) = //│ let x$4 = 1 in -- #25 //│ let x$6 = Lambda$1(x$3,x$4) in -- #24 //│ let* (x$7) = compose(x$6) in -- #23 //│ let x$8 = Callable.apply1(x$7,x$6) in -- #22 //│ let x$9 = Callable.apply1(x$8,2) in -- #21 //│ x$9 -- #20 //│ let* (x$0) = main(3) in -- #4 //│ x$0 -- #3 //│ //│ Interpreted: //│ 10 //│ //│ //│ Execution succeeded: //│ 10 //│ ================================================ FILE: compiler/shared/test/diff-ir/NuScratch.mls ================================================ :NewParser :ParseOnly :UseIR ================================================ FILE: compiler/shared/test/diff-ir/Override.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :genCpp :runCpp :interpIR module Base { fun f() = 1 } module Child extends Base { fun f() = 2 } fun main() = let c = Child() Base.f(c) Child.f(c) main() //│ |#module| |Base| |{|→|#fun| |f|(||)| |#=| |1|←|↵|}|↵|#module| |Child| |#extends| |Base| |{|→|#fun| |f|(||)| |#=| |2|←|↵|}|↵|#fun| |main|(||)| |#=|→|#let| |c| |#=| |Child|(||)|↵|Base|.f|(|c|)|↵|Child|.f|(|c|)|←|↵|main|(||)| //│ Parsed: {module Base {fun f = () => 1}; module Child: Base {fun f = () => 2}; fun main = () => {let c = Child(); (Base).f(c,); (Child).f(c,)}; main()} //│ //│ //│ IR: //│ Program: //│ class Base() { //│ def f() = //│ 1 -- #16 //│ } //│ class Child() extends Base { //│ def f() = //│ 2 -- #17 //│ } //│ def main() = //│ let x$1 = Child() in -- #15 //│ let x$2 = Base.f(x$1) in -- #14 //│ let x$3 = Child.f(x$1) in -- #13 //│ x$3 -- #12 //│ let* (x$0) = main() in -- #2 //│ x$0 -- #1 //│ //│ Interpreted: //│ 2 //│ //│ //│ Execution succeeded: //│ 2 //│ ================================================ FILE: compiler/shared/test/diff-ir/cpp/Makefile ================================================ CXX := g++ CFLAGS += -O3 -Wall -Wextra -std=c++20 -I. -Wno-inconsistent-missing-override -I/opt/homebrew/include LDFLAGS += -L/opt/homebrew/lib LDLIBS := -lmimalloc -lgmp SRC := INCLUDES := mlsprelude.h DST := DEFAULT_TARGET := mls TARGET := $(or $(DST),$(DEFAULT_TARGET)) .PHONY: pre all run clean auto all: $(TARGET) run: $(TARGET) ./$(TARGET) pre: $(SRC) sed -i '' 's#^//│ ##g' $(SRC) clean: rm -r $(TARGET) $(TARGET).dSYM auto: $(TARGET) $(TARGET): $(SRC) $(INCLUDES) $(CXX) $(CFLAGS) $(LDFLAGS) $(SRC) $(LDLIBS) -o $(TARGET) ================================================ FILE: compiler/shared/test/diff-ir/cpp/mlsprelude.h ================================================ #include #include #include #include #include #include #include #include #include #include #include #include constexpr std::size_t _mlsAlignment = 8; template class tuple_type { template > struct impl; template struct impl> { template using wrap = T; using type = std::tuple...>; }; public: using type = typename impl<>::type; }; template struct counter { using tag = counter; struct generator { friend consteval auto is_defined(tag) { return true; } }; friend consteval auto is_defined(tag); template static consteval auto exists(auto) { return true; } static consteval auto exists(...) { return generator(), false; } }; template consteval auto nextTypeTag() { if constexpr (not counter::exists(Id)) return Id; else return nextTypeTag(); } struct _mlsObject { uint32_t refCount; uint32_t tag; constexpr static inline uint32_t stickyRefCount = std::numeric_limits::max(); void incRef() { if (refCount != stickyRefCount) ++refCount; } bool decRef() { if (refCount != stickyRefCount && --refCount == 0) return true; return false; } virtual void print() const = 0; virtual void destroy() = 0; }; struct _mls_True; struct _mls_False; class _mlsValue { using uintptr_t = std::uintptr_t; using uint64_t = std::uint64_t; void *value alignas(_mlsAlignment); bool isInt63() const { return (reinterpret_cast(value) & 1) == 1; } bool isPtr() const { return (reinterpret_cast(value) & 1) == 0; } uint64_t asInt63() const { return reinterpret_cast(value) >> 1; } uintptr_t asRawInt() const { return reinterpret_cast(value); } static _mlsValue fromRawInt(uintptr_t i) { return _mlsValue(reinterpret_cast(i)); } static _mlsValue fromInt63(uint64_t i) { return _mlsValue(reinterpret_cast((i << 1) | 1)); } void *asPtr() const { assert(!isInt63()); return value; } _mlsObject *asObject() const { assert(isPtr()); return static_cast<_mlsObject *>(value); } bool eqInt63(const _mlsValue &other) const { return asRawInt() == other.asRawInt(); } _mlsValue addInt63(const _mlsValue &other) const { return fromRawInt(asRawInt() + other.asRawInt() - 1); } _mlsValue subInt63(const _mlsValue &other) const { return fromRawInt(asRawInt() - other.asRawInt() + 1); } _mlsValue mulInt63(const _mlsValue &other) const { return fromInt63(asInt63() * other.asInt63()); } _mlsValue divInt63(const _mlsValue &other) const { return fromInt63(asInt63() / other.asInt63()); } _mlsValue gtInt63(const _mlsValue &other) const { return asInt63() > other.asInt63() ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue ltInt63(const _mlsValue &other) const { return asInt63() < other.asInt63() ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue geInt63(const _mlsValue &other) const { return asInt63() >= other.asInt63() ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue leInt63(const _mlsValue &other) const { return asInt63() <= other.asInt63() ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } public: explicit _mlsValue() : value(nullptr) {} explicit _mlsValue(void *value) : value(value) {} _mlsValue(const _mlsValue &other) : value(other.value) { if (isPtr()) asObject()->incRef(); } _mlsValue &operator=(const _mlsValue &other) { if (value != nullptr && isPtr()) asObject()->decRef(); value = other.value; if (isPtr()) asObject()->incRef(); return *this; } ~_mlsValue() { if (isPtr()) if (asObject()->decRef()) { asObject()->destroy(); value = nullptr; } } uint64_t asInt() const { assert(isInt63()); return asInt63(); } static _mlsValue fromIntLit(uint64_t i) { return fromInt63(i); } template static tuple_type<_mlsValue, N> never() { __builtin_unreachable(); } static _mlsValue never() { __builtin_unreachable(); } template static _mlsValue create(U... args) { return _mlsValue(T::create(args...)); } static void destroy(_mlsValue &v) { v.~_mlsValue(); } template static bool isValueOf(const _mlsValue &v) { return v.asObject()->tag == T::typeTag; } static bool isIntLit(const _mlsValue &v, uint64_t n) { return v.asInt63() == n; } static bool isIntLit(const _mlsValue &v) { return v.isInt63(); } template static T *as(const _mlsValue &v) { return dynamic_cast(v.asObject()); } template static T *cast(_mlsValue &v) { return static_cast(v.asObject()); } // Operators _mlsValue operator==(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return eqInt63(other) ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); assert(false); } _mlsValue operator+(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return addInt63(other); assert(false); } _mlsValue operator-(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return subInt63(other); assert(false); } _mlsValue operator*(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return mulInt63(other); assert(false); } _mlsValue operator/(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return divInt63(other); assert(false); } _mlsValue operator>(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return gtInt63(other); assert(false); } _mlsValue operator<(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return ltInt63(other); assert(false); } _mlsValue operator>=(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return geInt63(other); assert(false); } _mlsValue operator<=(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return leInt63(other); assert(false); } // Auxiliary functions void print() const { if (isInt63()) std::printf("%" PRIu64, asInt63()); else if (isPtr() && asObject()) asObject()->print(); } }; struct _mls_Callable : public _mlsObject { virtual _mlsValue _mls_apply0() { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply1(_mlsValue) { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply2(_mlsValue, _mlsValue) { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply3(_mlsValue, _mlsValue, _mlsValue) { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply4(_mlsValue, _mlsValue, _mlsValue, _mlsValue) { throw std::runtime_error("Not implemented"); } virtual void destroy() override {} }; inline static _mls_Callable *_mlsToCallable(_mlsValue fn) { auto *ptr = _mlsValue::as<_mls_Callable>(fn); if (!ptr) throw std::runtime_error("Not a callable object"); return ptr; } template inline static _mlsValue _mlsCall(_mlsValue f, U... args) { static_assert(sizeof...(U) <= 4, "Too many arguments"); if constexpr (sizeof...(U) == 0) return _mlsToCallable(f)->_mls_apply0(); else if constexpr (sizeof...(U) == 1) return _mlsToCallable(f)->_mls_apply1(args...); else if constexpr (sizeof...(U) == 2) return _mlsToCallable(f)->_mls_apply2(args...); else if constexpr (sizeof...(U) == 3) return _mlsToCallable(f)->_mls_apply3(args...); else if constexpr (sizeof...(U) == 4) return _mlsToCallable(f)->_mls_apply4(args...); } template inline static T *_mlsMethodCall(_mlsValue self) { auto *ptr = _mlsValue::as(self); if (!ptr) throw std::runtime_error("unable to convert object for method calls"); return ptr; } inline int _mlsLargeStack(void *(*fn)(void *)) { pthread_t thread; pthread_attr_t attr; size_t stacksize = 512 * 1024 * 1024; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, stacksize); int rc = pthread_create(&thread, &attr, fn, nullptr); if (rc) { printf("ERROR: return code from pthread_create() is %d\n", rc); return 1; } pthread_join(thread, NULL); return 0; } _mlsValue _mlsMain(); inline void *_mlsMainWrapper(void *) { _mlsValue res = _mlsMain(); res.print(); return nullptr; } struct _mls_Unit final : public _mlsObject { constexpr static inline const char *typeName = "Unit"; constexpr static inline uint32_t typeTag = nextTypeTag(); virtual void print() const override { std::printf(typeName); } static _mlsValue create() { static _mls_Unit mlsUnit alignas(_mlsAlignment); mlsUnit.refCount = stickyRefCount; mlsUnit.tag = typeTag; return _mlsValue(&mlsUnit); } virtual void destroy() override {} }; struct _mls_Boolean : public _mlsObject {}; struct _mls_True final : public _mls_Boolean { constexpr static inline const char *typeName = "True"; constexpr static inline uint32_t typeTag = nextTypeTag(); virtual void print() const override { std::printf(typeName); } static _mlsValue create() { static _mls_True mlsTrue alignas(_mlsAlignment); mlsTrue.refCount = stickyRefCount; mlsTrue.tag = typeTag; return _mlsValue(&mlsTrue); } virtual void destroy() override {} }; struct _mls_False final : public _mls_Boolean { constexpr static inline const char *typeName = "False"; constexpr static inline uint32_t typeTag = nextTypeTag(); virtual void print() const override { std::printf(typeName); } static _mlsValue create() { static _mls_False mlsFalse alignas(_mlsAlignment); mlsFalse.refCount = stickyRefCount; mlsFalse.tag = typeTag; return _mlsValue(&mlsFalse); } virtual void destroy() override {} }; #include struct _mls_ZInt final : public _mlsObject { boost::multiprecision::mpz_int z; constexpr static inline const char *typeName = "Z"; constexpr static inline uint32_t typeTag = nextTypeTag(); virtual void print() const override { std::printf(typeName); std::printf("("); std::printf("%s", z.str().c_str()); std::printf(")"); } virtual void destroy() override { z.~number(); operator delete(this, std::align_val_t(_mlsAlignment)); } static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_ZInt; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } static _mlsValue create(_mlsValue z) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_ZInt; _mlsVal->z = z.asInt(); _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } _mlsValue operator+(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z + other.z; return _mlsVal; } _mlsValue operator-(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z - other.z; return _mlsVal; } _mlsValue operator*(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z * other.z; return _mlsVal; } _mlsValue operator/(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z / other.z; return _mlsVal; } _mlsValue operator%(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z % other.z; return _mlsVal; } _mlsValue operator==(const _mls_ZInt &other) const { return z == other.z ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue operator>(const _mls_ZInt &other) const { return z > other.z ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue operator<(const _mls_ZInt &other) const { return z < other.z ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue operator>=(const _mls_ZInt &other) const { return z >= other.z ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue operator<=(const _mls_ZInt &other) const { return z <= other.z ? _mlsValue::create<_mls_True>() : _mlsValue::create<_mls_False>(); } _mlsValue toInt() const { return _mlsValue::fromIntLit(z.convert_to()); } static _mlsValue fromInt(uint64_t i) { return _mlsValue::create<_mls_ZInt>(_mlsValue::fromIntLit(i)); } }; __attribute__((noinline)) inline void _mlsNonExhaustiveMatch() { throw std::runtime_error("Non-exhaustive match"); } inline _mlsValue _mls_builtin_z_add(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) + *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_sub(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) - *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_mul(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) * *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_div(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) / *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_mod(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) % *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_equal(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) == *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_gt(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) > *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_lt(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) < *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_geq(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) >= *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_leq(_mlsValue a, _mlsValue b) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) <= *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_to_int(_mlsValue a) { assert(_mlsValue::isValueOf<_mls_ZInt>(a)); return _mlsValue::cast<_mls_ZInt>(a)->toInt(); } inline _mlsValue _mls_builtin_z_of_int(_mlsValue a) { assert(_mlsValue::isIntLit(a)); return _mlsValue::create<_mls_ZInt>(a); } inline _mlsValue _mls_builtin_print(_mlsValue a) { a.print(); return _mlsValue::create<_mls_Unit>(); } inline _mlsValue _mls_builtin_println(_mlsValue a) { a.print(); std::puts(""); return _mlsValue::create<_mls_Unit>(); } inline _mlsValue _mls_builtin_debug(_mlsValue a) { a.print(); std::puts(""); return a; } ================================================ FILE: compiler/shared/test/diff-ir/gcd.mls ================================================ :NewDefs :ParseOnly :UseIR :NoTailRec :prelude module True module False module Callable { fun apply0() = 0 fun apply1(x0) = 0 fun apply2(x0,x1) = 0 fun apply3(x0,x1,x2) = 0 fun apply4(x0,x1,x2,x3) = 0 fun apply5(x0,x1,x2,x3,x4) = 0 } module List[A, B] class Cons[A, B](h: A, t: Cons[A, B]) extends List[A, B] module Nil[A, B] extends List[A, B] module Option[A] class Some[A](x: A) extends Option[A] module None[A] extends Option[A] class Pair[A, B](x: A, y: B) class Tuple2[A, B](x: A, y: B) class Tuple3[A, B, C](x: A, y: B, z: C) module Nat class S(s: Nat) extends Nat module O extends Nat class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O) //│ |#module| |True|↵|#module| |False|↵|#module| |Callable| |{|→|#fun| |apply0|(||)| |#=| |0|↵|#fun| |apply1|(|x0|)| |#=| |0|↵|#fun| |apply2|(|x0|,|x1|)| |#=| |0|↵|#fun| |apply3|(|x0|,|x1|,|x2|)| |#=| |0|↵|#fun| |apply4|(|x0|,|x1|,|x2|,|x3|)| |#=| |0|↵|#fun| |apply5|(|x0|,|x1|,|x2|,|x3|,|x4|)| |#=| |0|←|↵|}|↵|#module| |List|[|A|,| |B|]|↵|#class| |Cons|[|A|,| |B|]|(|h|#:| |A|,| |t|#:| |Cons|[|A|,| |B|]|)| |#extends| |List|[|A|,| |B|]|↵|#module| |Nil|[|A|,| |B|]| |#extends| |List|[|A|,| |B|]|↵|#module| |Option|[|A|]|↵|#class| |Some|[|A|]|(|x|#:| |A|)| |#extends| |Option|[|A|]|↵|#module| |None|[|A|]| |#extends| |Option|[|A|]|↵|#class| |Pair|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple2|[|A|,| |B|]|(|x|#:| |A|,| |y|#:| |B|)|↵|#class| |Tuple3|[|A|,| |B|,| |C|]|(|x|#:| |A|,| |y|#:| |B|,| |z|#:| |C|)|↵|#module| |Nat|↵|#class| |S|(|s|#:| |Nat|)| |#extends| |Nat|↵|#module| |O| |#extends| |Nat|↵|#class| |HiddenTheseEntities|(|_0|#:| |HiddenTheseEntities|,| |_1|#:| |True|,| |_2|#:| |False|,| |_3|#:| |Callable|,| |_4|#:| |List|,| |_5|#:| |Cons|,| |_6|#:| |Nil|,| |_7|#:| |Option|,| |_8|#:| |Some|,| |_9|#:| |None|,| |_10|#:| |Pair|,| |_11|#:| |Tuple2|,| |_12|#:| |Tuple3|,| |_13|#:| |Nat|,| |_14|#:| |S|,| |_15|#:| |O|)| //│ Parsed: {module True {}; module False {}; module Callable {fun apply0 = () => 0; fun apply1 = (x0,) => 0; fun apply2 = (x0, x1,) => 0; fun apply3 = (x0, x1, x2,) => 0; fun apply4 = (x0, x1, x2, x3,) => 0; fun apply5 = (x0, x1, x2, x3, x4,) => 0}; module List‹A, B› {}; class Cons‹A, B›(h: A, t: Cons‹A, B›,): List‹A, B› {}; module Nil‹A, B›: List‹A, B› {}; module Option‹A› {}; class Some‹A›(x: A,): Option‹A› {}; module None‹A›: Option‹A› {}; class Pair‹A, B›(x: A, y: B,) {}; class Tuple2‹A, B›(x: A, y: B,) {}; class Tuple3‹A, B, C›(x: A, y: B, z: C,) {}; module Nat {}; class S(s: Nat,): Nat {}; module O: Nat {}; class HiddenTheseEntities(_0: HiddenTheseEntities, _1: True, _2: False, _3: Callable, _4: List, _5: Cons, _6: Nil, _7: Option, _8: Some, _9: None, _10: Pair, _11: Tuple2, _12: Tuple3, _13: Nat, _14: S, _15: O,) {}} //│ //│ Preluded. //│ :genCpp :runCpp fun error() = builtin("error") fun z_of_int(x) = builtin("z_of_int", x) fun z_to_int(x) = builtin("z_to_int", x) fun z_add(x, y) = builtin("z_add", x, y) fun z_sub(x, y) = builtin("z_sub", x, y) fun z_div(x, y) = builtin("z_div", x, y) fun z_mul(x, y) = builtin("z_mul", x, y) fun z_mod(x, y) = builtin("z_mod", x, y) fun z_lt(x, y) = builtin("z_lt", x, y) fun z_leq(x, y) = builtin("z_leq", x, y) fun z_equal(x, y) = builtin("z_equal", x, y) fun z_gt(x, y) = builtin("z_gt", x, y) fun z_geq(x, y) = builtin("z_geq", x, y) fun println(x) = builtin("println", x) fun print(x) = builtin("print", x) fun debug(x) = builtin("debug", x) fun map(f, ls) = if ls is Cons (h, t) then Cons (f(h), map(f, t)) Nil then Nil fun filter(f_2, ls_2) = if ls_2 is Cons (h_2, t_2) then if f_2(h_2) then Cons (h_2, filter(f_2, t_2)) else (filter(f_2, t_2)) Nil then Nil fun foldl(f_4, i, ls_4) = if ls_4 is Cons (h_4, t_4) then foldl(f_4, f_4(i, h_4), t_4) Nil then i fun foldr(f_5, i_1, ls_5) = if ls_5 is Cons (h_5, t_5) then f_5(h_5, foldr(f_5, i_1, t_5)) Nil then i_1 fun zip(xs, ys) = if xs is Cons (hx, tx) then if ys is Cons (hy, ty) then Cons (Tuple2 (hx, hy), zip(tx, ty)) Nil then Nil Nil then Nil fun zipWith(f_7, xs_4, ys_4) = if xs_4 is Cons (hx_4, tx_4) then if ys_4 is Cons (hy_4, ty_4) then Cons (f_7(hx_4, hy_4), zipWith(f_7, tx_4, ty_4)) Nil then Nil Nil then Nil fun head(ls_7) = if ls_7 is Cons (h_7, t_7) then h_7 Nil then error fun tail(ls_9) = if ls_9 is Cons (h_9, t_9) then t_9 Nil then error fun enumFromTo(a, b) = if a <= b then Cons (a, enumFromTo(a + 1, b)) else (Nil) fun enumFromThenTo(a_1, t_11, b_1) = if a_1 <= b_1 then Cons (a_1, enumFromThenTo(t_11, 2 * t_11 - a_1, b_1)) else (Nil) fun take(n, ls_11) = if n > 0 then if ls_11 is Cons (h_11, t_13) then Cons (h_11, take(n - 1, t_13)) Nil then Nil else (Nil) fun length(ls_13) = if ls_13 is Cons (h_13, t_15) then 1 + (length(t_15)) Nil then 0 fun mappend(xs_8, ys_8) = if xs_8 is Cons (h_14, t_16) then Cons (h_14, mappend(t_16, ys_8)) Nil then ys_8 fun sum(ls_14) = sumAux(ls_14, 0) fun sumAux(ls_15, a_4) = if ls_15 is Nil then a_4 Cons (h_15, t_17) then sumAux(t_17, a_4 + h_15) fun atIndex(n_2, ls_16) = if n_2 < 0 then error else if ls_16 is Cons (h_16, t_18) then if n_2 == 0 then h_16 else (atIndex(n_2 - 1, t_18)) Nil then error fun concat(lss) = if lss is Cons (h_18, t_20) then mappend(h_18, concat(t_20)) Nil then Nil fun reverse(ls_18) = reverse_helper(ls_18, Nil) fun reverse_helper(ls_19, a_5) = if ls_19 is Cons (h_19, t_21) then reverse_helper(t_21, Cons (h_19, a_5)) Nil then a_5 fun listcomp_fun1(ms, listcomp_fun_para) = if listcomp_fun_para is Cons(listcomp_fun_ls_h, listcomp_fun_ls_t) then listcomp_fun2(ms, listcomp_fun_ls_h, listcomp_fun_ls_t, ms) Nil then Nil fun listcomp_fun2(ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_para) = if listcomp_fun_para is Cons(listcomp_fun_ls_h, listcomp_fun_ls_t) then Cons(Tuple2 (listcomp_fun_ls_h_out, listcomp_fun_ls_h), listcomp_fun2(ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_ls_t)) Nil then listcomp_fun1(ms, listcomp_fun_ls_t_out) fun test(test_arg1) = let ns = z_enumFromTo(const5000(), z_add(const5000(), test_arg1)) let ms = z_enumFromTo(const10000(), z_add(const10000(), test_arg1)) let tripls = map(f1, listcomp_fun1(ms, ns)) let rs = map(f2, tripls) max'(rs) fun const10000() = z_of_int(10000) fun f1(f1_arg1) = if f1_arg1 is Tuple2 (f1_Tuple2_0, f1_Tuple2_1) then Tuple3 (f1_Tuple2_0, f1_Tuple2_1, gcdE(f1_Tuple2_0, f1_Tuple2_1)) fun quotRem(quotRem_arg1, quotRem_arg2) = Tuple2 (z_div(quotRem_arg1, quotRem_arg2), z_mod(quotRem_arg1, quotRem_arg2)) fun max'(max'_arg1) = if max'_arg1 is Cons (max'_Cons_0, max'_Cons_1) then if max'_Cons_1 is Nil then max'_Cons_0 Cons (max'_Cons_0_1, max'_Cons_1_1) then if z_lt(max'_Cons_0, max'_Cons_0_1) then max'(Cons (max'_Cons_0_1, max'_Cons_1_1)) else (max'(Cons (max'_Cons_0, max'_Cons_1_1))) fun g(g_arg1, g_arg2) = if g_arg1 is Tuple3 (g_Tuple3_0, g_Tuple3_1, g_Tuple3_2) then if g_arg2 is Tuple3 (g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1) then if z_equal(g_Tuple3_2_1, const0()) then Tuple3 (g_Tuple3_2, g_Tuple3_0, g_Tuple3_1) else let matchIdent = quotRem(g_Tuple3_2, g_Tuple3_2_1) if matchIdent is Tuple2 (g_Tuple2_0, g_Tuple2_1) then g(Tuple3 (g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1), Tuple3 (z_sub(g_Tuple3_0, z_mul(g_Tuple2_0, g_Tuple3_0_1)), z_sub(g_Tuple3_1, z_mul(g_Tuple2_0, g_Tuple3_1_1)), g_Tuple2_1)) fun abs(abs_arg1) = if z_lt(abs_arg1, const0()) then z_sub(const0(), abs_arg1) else abs_arg1 fun f2(f2_arg1) = if f2_arg1 is Tuple3 (f2_Tuple3_0, f2_Tuple3_1, f2_Tuple3_2) then if f2_Tuple3_2 is Tuple3 (f2_Tuple3_0_1, f2_Tuple3_1_1, f2_Tuple3_2_1) then abs(z_add(z_add(f2_Tuple3_0_1, f2_Tuple3_1_1), f2_Tuple3_2_1)) fun const0() = z_of_int(0) fun gcdE(gcdE_arg1, gcdE_arg2) = if z_equal(gcdE_arg1, const0()) then Tuple3 (gcdE_arg2, const0(), const1()) else (g(Tuple3 (const1(), const0(), gcdE_arg1), Tuple3 (const0(), const1(), gcdE_arg2))) fun const1() = z_of_int(1) fun const5000() = z_of_int(5000) fun testGcd_nofib(testGcd_nofib_arg1) = test(testGcd_nofib_arg1) fun z_enumFromTo(z_enumFromTo_arg1, z_enumFromTo_arg2) = if z_leq(z_enumFromTo_arg1, z_enumFromTo_arg2) then Cons (z_enumFromTo_arg1, z_enumFromTo(z_add(z_enumFromTo_arg1, const1()), z_enumFromTo_arg2)) else (Nil) testGcd_nofib(z_of_int(400)) //│ |#fun| |error|(||)| |#=| |builtin|(|"error"|)|↵|#fun| |z_of_int|(|x|)| |#=| |builtin|(|"z_of_int"|,| |x|)|↵|#fun| |z_to_int|(|x|)| |#=| |builtin|(|"z_to_int"|,| |x|)|↵|#fun| |z_add|(|x|,| |y|)| |#=| |builtin|(|"z_add"|,| |x|,| |y|)|↵|#fun| |z_sub|(|x|,| |y|)| |#=| |builtin|(|"z_sub"|,| |x|,| |y|)|↵|#fun| |z_div|(|x|,| |y|)| |#=| |builtin|(|"z_div"|,| |x|,| |y|)|↵|#fun| |z_mul|(|x|,| |y|)| |#=| |builtin|(|"z_mul"|,| |x|,| |y|)|↵|#fun| |z_mod|(|x|,| |y|)| |#=| |builtin|(|"z_mod"|,| |x|,| |y|)|↵|#fun| |z_lt|(|x|,| |y|)| |#=| |builtin|(|"z_lt"|,| |x|,| |y|)|↵|#fun| |z_leq|(|x|,| |y|)| |#=| |builtin|(|"z_leq"|,| |x|,| |y|)|↵|#fun| |z_equal|(|x|,| |y|)| |#=| |builtin|(|"z_equal"|,| |x|,| |y|)|↵|#fun| |z_gt|(|x|,| |y|)| |#=| |builtin|(|"z_gt"|,| |x|,| |y|)|↵|#fun| |z_geq|(|x|,| |y|)| |#=| |builtin|(|"z_geq"|,| |x|,| |y|)|↵|#fun| |println|(|x|)| |#=| |builtin|(|"println"|,| |x|)|↵|#fun| |print|(|x|)| |#=| |builtin|(|"print"|,| |x|)|↵|#fun| |debug|(|x|)| |#=| |builtin|(|"debug"|,| |x|)|↵|#fun| |map|(|f|,| |ls|)| |#=|→|#if| |ls| |is|→|Cons| |(|h|,| |t|)| |#then|→|Cons| |(|f|(|h|)|,| |map|(|f|,| |t|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |filter|(|f_2|,| |ls_2|)| |#=|→|#if| |ls_2| |is|→|Cons| |(|h_2|,| |t_2|)| |#then|→|#if| |f_2|(|h_2|)| |#then|→|Cons| |(|h_2|,| |filter|(|f_2|,| |t_2|)|)|←|↵|#else|→|(|filter|(|f_2|,| |t_2|)|)|←|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |foldl|(|f_4|,| |i|,| |ls_4|)| |#=|→|#if| |ls_4| |is|→|Cons| |(|h_4|,| |t_4|)| |#then|→|foldl|(|f_4|,| |f_4|(|i|,| |h_4|)|,| |t_4|)|←|↵|Nil| |#then|→|i|←|←|←|↵|#fun| |foldr|(|f_5|,| |i_1|,| |ls_5|)| |#=|→|#if| |ls_5| |is|→|Cons| |(|h_5|,| |t_5|)| |#then|→|f_5|(|h_5|,| |foldr|(|f_5|,| |i_1|,| |t_5|)|)|←|↵|Nil| |#then|→|i_1|←|←|←|↵|#fun| |zip|(|xs|,| |ys|)| |#=|→|#if| |xs| |is|→|Cons| |(|hx|,| |tx|)| |#then|→|#if| |ys| |is|→|Cons| |(|hy|,| |ty|)| |#then|→|Cons| |(|Tuple2| |(|hx|,| |hy|)|,| |zip|(|tx|,| |ty|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |zipWith|(|f_7|,| |xs_4|,| |ys_4|)| |#=|→|#if| |xs_4| |is|→|Cons| |(|hx_4|,| |tx_4|)| |#then|→|#if| |ys_4| |is|→|Cons| |(|hy_4|,| |ty_4|)| |#then|→|Cons| |(|f_7|(|hx_4|,| |hy_4|)|,| |zipWith|(|f_7|,| |tx_4|,| |ty_4|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |head|(|ls_7|)| |#=|→|#if| |ls_7| |is|→|Cons| |(|h_7|,| |t_7|)| |#then|→|h_7|←|↵|Nil| |#then|→|error|←|←|←|↵|#fun| |tail|(|ls_9|)| |#=|→|#if| |ls_9| |is|→|Cons| |(|h_9|,| |t_9|)| |#then|→|t_9|←|↵|Nil| |#then|→|error|←|←|←|↵|#fun| |enumFromTo|(|a|,| |b|)| |#=|→|#if| |a| |<=| |b| |#then|→|Cons| |(|a|,| |enumFromTo|(|a| |+| |1|,| |b|)|)|←|↵|#else|→|(|Nil|)|←|←|↵|#fun| |enumFromThenTo|(|a_1|,| |t_11|,| |b_1|)| |#=|→|#if| |a_1| |<=| |b_1| |#then|→|Cons| |(|a_1|,| |enumFromThenTo|(|t_11|,| |2| |*| |t_11| |-| |a_1|,| |b_1|)|)|←|↵|#else|→|(|Nil|)|←|←|↵|#fun| |take|(|n|,| |ls_11|)| |#=|→|#if| |n| |>| |0| |#then|→|#if| |ls_11| |is|→|Cons| |(|h_11|,| |t_13|)| |#then|→|Cons| |(|h_11|,| |take|(|n| |-| |1|,| |t_13|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#else|→|(|Nil|)|←|←|↵|#fun| |length|(|ls_13|)| |#=|→|#if| |ls_13| |is|→|Cons| |(|h_13|,| |t_15|)| |#then|→|1| |+| |(|length|(|t_15|)|)|←|↵|Nil| |#then|→|0|←|←|←|↵|#fun| |mappend|(|xs_8|,| |ys_8|)| |#=|→|#if| |xs_8| |is|→|Cons| |(|h_14|,| |t_16|)| |#then|→|Cons| |(|h_14|,| |mappend|(|t_16|,| |ys_8|)|)|←|↵|Nil| |#then|→|ys_8|←|←|←|↵|#fun| |sum|(|ls_14|)| |#=|→|sumAux|(|ls_14|,| |0|)|←|↵|#fun| |sumAux|(|ls_15|,| |a_4|)| |#=|→|#if| |ls_15| |is|→|Nil| |#then|→|a_4|←|↵|Cons| |(|h_15|,| |t_17|)| |#then|→|sumAux|(|t_17|,| |a_4| |+| |h_15|)|←|←|←|↵|#fun| |atIndex|(|n_2|,| |ls_16|)| |#=|→|#if| |n_2| |<| |0| |#then|→|error|←|↵|#else|→|#if| |ls_16| |is|→|Cons| |(|h_16|,| |t_18|)| |#then|→|#if| |n_2| |==| |0| |#then|→|h_16|←|↵|#else|→|(|atIndex|(|n_2| |-| |1|,| |t_18|)|)|←|←|↵|Nil| |#then|→|error|←|←|←|←|↵|#fun| |concat|(|lss|)| |#=|→|#if| |lss| |is|→|Cons| |(|h_18|,| |t_20|)| |#then|→|mappend|(|h_18|,| |concat|(|t_20|)|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |reverse|(|ls_18|)| |#=|→|reverse_helper|(|ls_18|,| |Nil|)|←|↵|#fun| |reverse_helper|(|ls_19|,| |a_5|)| |#=|→|#if| |ls_19| |is|→|Cons| |(|h_19|,| |t_21|)| |#then|→|reverse_helper|(|t_21|,| |Cons| |(|h_19|,| |a_5|)|)|←|↵|Nil| |#then|→|a_5|←|←|←|↵|#fun| |listcomp_fun1|(|ms|,| |listcomp_fun_para|)| |#=|→|#if| |listcomp_fun_para| |is|→|Cons|(|listcomp_fun_ls_h|,| |listcomp_fun_ls_t|)| |#then|→|listcomp_fun2|(|ms|,| |listcomp_fun_ls_h|,| |listcomp_fun_ls_t|,| |ms|)|←|↵|Nil| |#then|→|Nil|←|←|←|↵|#fun| |listcomp_fun2|(|ms|,| |listcomp_fun_ls_h_out|,| |listcomp_fun_ls_t_out|,| |listcomp_fun_para|)| |#=|→|#if| |listcomp_fun_para| |is|→|Cons|(|listcomp_fun_ls_h|,| |listcomp_fun_ls_t|)| |#then|→|Cons|(|Tuple2| |(|listcomp_fun_ls_h_out|,| |listcomp_fun_ls_h|)|,| |listcomp_fun2|(|ms|,| |listcomp_fun_ls_h_out|,| |listcomp_fun_ls_t_out|,| |listcomp_fun_ls_t|)|)|←|↵|Nil| |#then|→|listcomp_fun1|(|ms|,| |listcomp_fun_ls_t_out|)|←|←|←|↵|#fun| |test|(|test_arg1|)| |#=|→|#let| |ns| |#=| |z_enumFromTo|(|const5000|(||)|,| |z_add|(|const5000|(||)|,| |test_arg1|)|)|↵|#let| |ms| |#=| |z_enumFromTo|(|const10000|(||)|,| |z_add|(|const10000|(||)|,| |test_arg1|)|)|↵|#let| |tripls| |#=| |map|(|f1|,| |listcomp_fun1|(|ms|,| |ns|)|)|↵|#let| |rs| |#=| |map|(|f2|,| |tripls|)|↵|max'|(|rs|)|←|↵|#fun| |const10000|(||)| |#=|→|z_of_int|(|10000|)|←|↵|#fun| |f1|(|f1_arg1|)| |#=|→|#if| |f1_arg1| |is|→|Tuple2| |(|f1_Tuple2_0|,| |f1_Tuple2_1|)| |#then|→|Tuple3| |(|f1_Tuple2_0|,| |f1_Tuple2_1|,| |gcdE|(|f1_Tuple2_0|,| |f1_Tuple2_1|)|)|←|←|←|↵|#fun| |quotRem|(|quotRem_arg1|,| |quotRem_arg2|)| |#=|→|Tuple2| |(|z_div|(|quotRem_arg1|,| |quotRem_arg2|)|,| |z_mod|(|quotRem_arg1|,| |quotRem_arg2|)|)|←|↵|#fun| |max'|(|max'_arg1|)| |#=|→|#if| |max'_arg1| |is|→|Cons| |(|max'_Cons_0|,| |max'_Cons_1|)| |#then|→|#if| |max'_Cons_1| |is|→|Nil| |#then|→|max'_Cons_0|←|↵|Cons| |(|max'_Cons_0_1|,| |max'_Cons_1_1|)| |#then|→|#if| |z_lt|(|max'_Cons_0|,| |max'_Cons_0_1|)| |#then|→|max'|(|Cons| |(|max'_Cons_0_1|,| |max'_Cons_1_1|)|)|←|↵|#else|→|(|max'|(|Cons| |(|max'_Cons_0|,| |max'_Cons_1_1|)|)|)|←|←|←|←|←|←|↵|#fun| |g|(|g_arg1|,| |g_arg2|)| |#=|→|#if| |g_arg1| |is|→|Tuple3| |(|g_Tuple3_0|,| |g_Tuple3_1|,| |g_Tuple3_2|)| |#then|→|#if| |g_arg2| |is|→|Tuple3| |(|g_Tuple3_0_1|,| |g_Tuple3_1_1|,| |g_Tuple3_2_1|)| |#then|→|#if| |z_equal|(|g_Tuple3_2_1|,| |const0|(||)|)| |#then|→|Tuple3| |(|g_Tuple3_2|,| |g_Tuple3_0|,| |g_Tuple3_1|)|←|↵|#else|→|#let| |matchIdent| |#=| |quotRem|(|g_Tuple3_2|,| |g_Tuple3_2_1|)|↵|#if| |matchIdent| |is|→|Tuple2| |(|g_Tuple2_0|,| |g_Tuple2_1|)| |#then|→|g|(|Tuple3| |(|g_Tuple3_0_1|,| |g_Tuple3_1_1|,| |g_Tuple3_2_1|)|,| |Tuple3| |(|z_sub|(|g_Tuple3_0|,| |z_mul|(|g_Tuple2_0|,| |g_Tuple3_0_1|)|)|,| |z_sub|(|g_Tuple3_1|,| |z_mul|(|g_Tuple2_0|,| |g_Tuple3_1_1|)|)|,| |g_Tuple2_1|)|)|←|←|←|←|←|←|←|←|↵|#fun| |abs|(|abs_arg1|)| |#=|→|#if| |z_lt|(|abs_arg1|,| |const0|(||)|)| |#then|→|z_sub|(|const0|(||)|,| |abs_arg1|)|←|↵|#else|→|abs_arg1|←|←|↵|#fun| |f2|(|f2_arg1|)| |#=|→|#if| |f2_arg1| |is|→|Tuple3| |(|f2_Tuple3_0|,| |f2_Tuple3_1|,| |f2_Tuple3_2|)| |#then|→|#if| |f2_Tuple3_2| |is|→|Tuple3| |(|f2_Tuple3_0_1|,| |f2_Tuple3_1_1|,| |f2_Tuple3_2_1|)| |#then|→|abs|(|z_add|(|z_add|(|f2_Tuple3_0_1|,| |f2_Tuple3_1_1|)|,| |f2_Tuple3_2_1|)|)|←|←|←|←|←|↵|#fun| |const0|(||)| |#=|→|z_of_int|(|0|)|←|↵|#fun| |gcdE|(|gcdE_arg1|,| |gcdE_arg2|)| |#=|→|#if| |z_equal|(|gcdE_arg1|,| |const0|(||)|)| |#then|→|Tuple3| |(|gcdE_arg2|,| |const0|(||)|,| |const1|(||)|)|←|↵|#else|→|(|g|(|Tuple3| |(|const1|(||)|,| |const0|(||)|,| |gcdE_arg1|)|,| |Tuple3| |(|const0|(||)|,| |const1|(||)|,| |gcdE_arg2|)|)|)|←|←|↵|#fun| |const1|(||)| |#=|→|z_of_int|(|1|)|←|↵|#fun| |const5000|(||)| |#=|→|z_of_int|(|5000|)|←|↵|#fun| |testGcd_nofib|(|testGcd_nofib_arg1|)| |#=|→|test|(|testGcd_nofib_arg1|)|←|↵|#fun| |z_enumFromTo|(|z_enumFromTo_arg1|,| |z_enumFromTo_arg2|)| |#=|→|#if| |z_leq|(|z_enumFromTo_arg1|,| |z_enumFromTo_arg2|)| |#then|→|Cons| |(|z_enumFromTo_arg1|,| |z_enumFromTo|(|z_add|(|z_enumFromTo_arg1|,| |const1|(||)|)|,| |z_enumFromTo_arg2|)|)|←|↵|#else|→|(|Nil|)|←|←|↵|testGcd_nofib|(|z_of_int|(|400|)|)| //│ Parsed: {fun error = () => builtin("error",); fun z_of_int = (x,) => builtin("z_of_int", x,); fun z_to_int = (x,) => builtin("z_to_int", x,); fun z_add = (x, y,) => builtin("z_add", x, y,); fun z_sub = (x, y,) => builtin("z_sub", x, y,); fun z_div = (x, y,) => builtin("z_div", x, y,); fun z_mul = (x, y,) => builtin("z_mul", x, y,); fun z_mod = (x, y,) => builtin("z_mod", x, y,); fun z_lt = (x, y,) => builtin("z_lt", x, y,); fun z_leq = (x, y,) => builtin("z_leq", x, y,); fun z_equal = (x, y,) => builtin("z_equal", x, y,); fun z_gt = (x, y,) => builtin("z_gt", x, y,); fun z_geq = (x, y,) => builtin("z_geq", x, y,); fun println = (x,) => builtin("println", x,); fun print = (x,) => builtin("print", x,); fun debug = (x,) => builtin("debug", x,); fun map = (f, ls,) => {if ls is ‹(Cons(h, t,)) then {Cons(f(h,), map(f, t,),)}; (Nil) then {Nil}›}; fun filter = (f_2, ls_2,) => {if ls_2 is ‹(Cons(h_2, t_2,)) then {if (f_2(h_2,)) then {Cons(h_2, filter(f_2, t_2,),)} else {'(' filter(f_2, t_2,) ')'}}; (Nil) then {Nil}›}; fun foldl = (f_4, i, ls_4,) => {if ls_4 is ‹(Cons(h_4, t_4,)) then {foldl(f_4, f_4(i, h_4,), t_4,)}; (Nil) then {i}›}; fun foldr = (f_5, i_1, ls_5,) => {if ls_5 is ‹(Cons(h_5, t_5,)) then {f_5(h_5, foldr(f_5, i_1, t_5,),)}; (Nil) then {i_1}›}; fun zip = (xs, ys,) => {if xs is ‹(Cons(hx, tx,)) then {if ys is ‹(Cons(hy, ty,)) then {Cons(Tuple2(hx, hy,), zip(tx, ty,),)}; (Nil) then {Nil}›}; (Nil) then {Nil}›}; fun zipWith = (f_7, xs_4, ys_4,) => {if xs_4 is ‹(Cons(hx_4, tx_4,)) then {if ys_4 is ‹(Cons(hy_4, ty_4,)) then {Cons(f_7(hx_4, hy_4,), zipWith(f_7, tx_4, ty_4,),)}; (Nil) then {Nil}›}; (Nil) then {Nil}›}; fun head = (ls_7,) => {if ls_7 is ‹(Cons(h_7, t_7,)) then {h_7}; (Nil) then {error}›}; fun tail = (ls_9,) => {if ls_9 is ‹(Cons(h_9, t_9,)) then {t_9}; (Nil) then {error}›}; fun enumFromTo = (a, b,) => {if (<=(a, b,)) then {Cons(a, enumFromTo(+(a, 1,), b,),)} else {'(' Nil ')'}}; fun enumFromThenTo = (a_1, t_11, b_1,) => {if (<=(a_1, b_1,)) then {Cons(a_1, enumFromThenTo(t_11, -(*(2, t_11,), a_1,), b_1,),)} else {'(' Nil ')'}}; fun take = (n, ls_11,) => {if (>(n, 0,)) then {if ls_11 is ‹(Cons(h_11, t_13,)) then {Cons(h_11, take(-(n, 1,), t_13,),)}; (Nil) then {Nil}›} else {'(' Nil ')'}}; fun length = (ls_13,) => {if ls_13 is ‹(Cons(h_13, t_15,)) then {+(1, '(' length(t_15,) ')',)}; (Nil) then {0}›}; fun mappend = (xs_8, ys_8,) => {if xs_8 is ‹(Cons(h_14, t_16,)) then {Cons(h_14, mappend(t_16, ys_8,),)}; (Nil) then {ys_8}›}; fun sum = (ls_14,) => {sumAux(ls_14, 0,)}; fun sumAux = (ls_15, a_4,) => {if ls_15 is ‹(Nil) then {a_4}; (Cons(h_15, t_17,)) then {sumAux(t_17, +(a_4, h_15,),)}›}; fun atIndex = (n_2, ls_16,) => {if (<(n_2, 0,)) then {error} else {if ls_16 is ‹(Cons(h_16, t_18,)) then {if (==(n_2, 0,)) then {h_16} else {'(' atIndex(-(n_2, 1,), t_18,) ')'}}; (Nil) then {error}›}}; fun concat = (lss,) => {if lss is ‹(Cons(h_18, t_20,)) then {mappend(h_18, concat(t_20,),)}; (Nil) then {Nil}›}; fun reverse = (ls_18,) => {reverse_helper(ls_18, Nil,)}; fun reverse_helper = (ls_19, a_5,) => {if ls_19 is ‹(Cons(h_19, t_21,)) then {reverse_helper(t_21, Cons(h_19, a_5,),)}; (Nil) then {a_5}›}; fun listcomp_fun1 = (ms, listcomp_fun_para,) => {if listcomp_fun_para is ‹(Cons(listcomp_fun_ls_h, listcomp_fun_ls_t,)) then {listcomp_fun2(ms, listcomp_fun_ls_h, listcomp_fun_ls_t, ms,)}; (Nil) then {Nil}›}; fun listcomp_fun2 = (ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_para,) => {if listcomp_fun_para is ‹(Cons(listcomp_fun_ls_h, listcomp_fun_ls_t,)) then {Cons(Tuple2(listcomp_fun_ls_h_out, listcomp_fun_ls_h,), listcomp_fun2(ms, listcomp_fun_ls_h_out, listcomp_fun_ls_t_out, listcomp_fun_ls_t,),)}; (Nil) then {listcomp_fun1(ms, listcomp_fun_ls_t_out,)}›}; fun test = (test_arg1,) => {let ns = z_enumFromTo(const5000(), z_add(const5000(), test_arg1,),); let ms = z_enumFromTo(const10000(), z_add(const10000(), test_arg1,),); let tripls = map(f1, listcomp_fun1(ms, ns,),); let rs = map(f2, tripls,); max'(rs,)}; fun const10000 = () => {z_of_int(10000,)}; fun f1 = (f1_arg1,) => {if f1_arg1 is ‹(Tuple2(f1_Tuple2_0, f1_Tuple2_1,)) then {Tuple3(f1_Tuple2_0, f1_Tuple2_1, gcdE(f1_Tuple2_0, f1_Tuple2_1,),)}›}; fun quotRem = (quotRem_arg1, quotRem_arg2,) => {Tuple2(z_div(quotRem_arg1, quotRem_arg2,), z_mod(quotRem_arg1, quotRem_arg2,),)}; fun max' = (max'_arg1,) => {if max'_arg1 is ‹(Cons(max'_Cons_0, max'_Cons_1,)) then {if max'_Cons_1 is ‹(Nil) then {max'_Cons_0}; (Cons(max'_Cons_0_1, max'_Cons_1_1,)) then {if (z_lt(max'_Cons_0, max'_Cons_0_1,)) then {max'(Cons(max'_Cons_0_1, max'_Cons_1_1,),)} else {'(' max'(Cons(max'_Cons_0, max'_Cons_1_1,),) ')'}}›}›}; fun g = (g_arg1, g_arg2,) => {if g_arg1 is ‹(Tuple3(g_Tuple3_0, g_Tuple3_1, g_Tuple3_2,)) then {if g_arg2 is ‹(Tuple3(g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1,)) then {if (z_equal(g_Tuple3_2_1, const0(),)) then {Tuple3(g_Tuple3_2, g_Tuple3_0, g_Tuple3_1,)} else {let matchIdent = quotRem(g_Tuple3_2, g_Tuple3_2_1,); if matchIdent is ‹(Tuple2(g_Tuple2_0, g_Tuple2_1,)) then {g(Tuple3(g_Tuple3_0_1, g_Tuple3_1_1, g_Tuple3_2_1,), Tuple3(z_sub(g_Tuple3_0, z_mul(g_Tuple2_0, g_Tuple3_0_1,),), z_sub(g_Tuple3_1, z_mul(g_Tuple2_0, g_Tuple3_1_1,),), g_Tuple2_1,),)}›}}›}›}; fun abs = (abs_arg1,) => {if (z_lt(abs_arg1, const0(),)) then {z_sub(const0(), abs_arg1,)} else {abs_arg1}}; fun f2 = (f2_arg1,) => {if f2_arg1 is ‹(Tuple3(f2_Tuple3_0, f2_Tuple3_1, f2_Tuple3_2,)) then {if f2_Tuple3_2 is ‹(Tuple3(f2_Tuple3_0_1, f2_Tuple3_1_1, f2_Tuple3_2_1,)) then {abs(z_add(z_add(f2_Tuple3_0_1, f2_Tuple3_1_1,), f2_Tuple3_2_1,),)}›}›}; fun const0 = () => {z_of_int(0,)}; fun gcdE = (gcdE_arg1, gcdE_arg2,) => {if (z_equal(gcdE_arg1, const0(),)) then {Tuple3(gcdE_arg2, const0(), const1(),)} else {'(' g(Tuple3(const1(), const0(), gcdE_arg1,), Tuple3(const0(), const1(), gcdE_arg2,),) ')'}}; fun const1 = () => {z_of_int(1,)}; fun const5000 = () => {z_of_int(5000,)}; fun testGcd_nofib = (testGcd_nofib_arg1,) => {test(testGcd_nofib_arg1,)}; fun z_enumFromTo = (z_enumFromTo_arg1, z_enumFromTo_arg2,) => {if (z_leq(z_enumFromTo_arg1, z_enumFromTo_arg2,)) then {Cons(z_enumFromTo_arg1, z_enumFromTo(z_add(z_enumFromTo_arg1, const1(),), z_enumFromTo_arg2,),)} else {'(' Nil ')'}}; testGcd_nofib(z_of_int(400,),)} //│ //│ //│ IR: //│ Program: //│ class Lambda$0() extends Callable { //│ def apply0() = //│ let* (x$270) = error() in -- #1354 //│ x$270 -- #1353 //│ } //│ class Lambda$1() extends Callable { //│ def apply0() = //│ let* (x$271) = error() in -- #1357 //│ x$271 -- #1356 //│ } //│ class Lambda$2() extends Callable { //│ def apply0() = //│ let* (x$272) = error() in -- #1360 //│ x$272 -- #1359 //│ } //│ class Lambda$3() extends Callable { //│ def apply0() = //│ let* (x$273) = error() in -- #1363 //│ x$273 -- #1362 //│ } //│ class Lambda$4() extends Callable { //│ def apply1(x$274) = //│ let* (x$275) = f1(x$274) in -- #1368 //│ x$275 -- #1367 //│ } //│ class Lambda$5() extends Callable { //│ def apply1(x$276) = //│ let* (x$277) = f2(x$276) in -- #1373 //│ x$277 -- #1372 //│ } //│ def error() = //│ let x$2 = Callable.apply1(builtin,error) in -- #14 //│ x$2 -- #13 //│ def z_of_int(x$3) = //│ let x$4 = Callable.apply2(builtin,z_of_int,x$3) in -- #22 //│ x$4 -- #21 //│ def z_to_int(x$5) = //│ let x$6 = Callable.apply2(builtin,z_to_int,x$5) in -- #30 //│ x$6 -- #29 //│ def z_add(x$7,y$0) = //│ let x$8 = Callable.apply3(builtin,z_add,x$7,y$0) in -- #40 //│ x$8 -- #39 //│ def z_sub(x$9,y$1) = //│ let x$10 = Callable.apply3(builtin,z_sub,x$9,y$1) in -- #50 //│ x$10 -- #49 //│ def z_div(x$11,y$2) = //│ let x$12 = Callable.apply3(builtin,z_div,x$11,y$2) in -- #60 //│ x$12 -- #59 //│ def z_mul(x$13,y$3) = //│ let x$14 = Callable.apply3(builtin,z_mul,x$13,y$3) in -- #70 //│ x$14 -- #69 //│ def z_mod(x$15,y$4) = //│ let x$16 = Callable.apply3(builtin,z_mod,x$15,y$4) in -- #80 //│ x$16 -- #79 //│ def z_lt(x$17,y$5) = //│ let x$18 = Callable.apply3(builtin,z_lt,x$17,y$5) in -- #90 //│ x$18 -- #89 //│ def z_leq(x$19,y$6) = //│ let x$20 = Callable.apply3(builtin,z_leq,x$19,y$6) in -- #100 //│ x$20 -- #99 //│ def z_equal(x$21,y$7) = //│ let x$22 = Callable.apply3(builtin,z_equal,x$21,y$7) in -- #110 //│ x$22 -- #109 //│ def z_gt(x$23,y$8) = //│ let x$24 = Callable.apply3(builtin,z_gt,x$23,y$8) in -- #120 //│ x$24 -- #119 //│ def z_geq(x$25,y$9) = //│ let x$26 = Callable.apply3(builtin,z_geq,x$25,y$9) in -- #130 //│ x$26 -- #129 //│ def println(x$27) = //│ let x$28 = Callable.apply2(builtin,println,x$27) in -- #138 //│ x$28 -- #137 //│ def print(x$29) = //│ let x$30 = Callable.apply2(builtin,print,x$29) in -- #146 //│ x$30 -- #145 //│ def debug(x$31) = //│ let x$32 = Callable.apply2(builtin,debug,x$31) in -- #154 //│ x$32 -- #153 //│ def map(f$0,ls$0) = //│ case ls$0 of -- #189 //│ Cons => //│ let x$34 = Cons.t(ls$0) in -- #185 //│ let x$35 = Cons.h(ls$0) in -- #184 //│ let x$36 = Callable.apply1(f$0,x$35) in -- #183 //│ let* (x$37) = map(f$0,x$34) in -- #182 //│ let x$38 = Cons(x$36,x$37) in -- #181 //│ jump j$0(x$38) -- #180 //│ Nil => //│ let x$39 = Nil() in -- #188 //│ jump j$0(x$39) -- #187 //│ def j$0(x$33) = //│ x$33 -- #156 //│ def filter(f_2$0,ls_2$0) = //│ case ls_2$0 of -- #236 //│ Cons => //│ let x$41 = Cons.t(ls_2$0) in -- #232 //│ let x$42 = Cons.h(ls_2$0) in -- #231 //│ let x$43 = Callable.apply1(f_2$0,x$42) in -- #230 //│ if x$43 -- #229 //│ true => //│ let* (x$45) = filter(f_2$0,x$41) in -- #220 //│ let x$46 = Cons(x$42,x$45) in -- #219 //│ jump j$2(x$46) -- #218 //│ false => //│ let* (x$47) = filter(f_2$0,x$41) in -- #228 //│ jump j$2(x$47) -- #227 //│ Nil => //│ let x$48 = Nil() in -- #235 //│ jump j$1(x$48) -- #234 //│ def j$1(x$40) = //│ x$40 -- #191 //│ def j$2(x$44) = //│ jump j$1(x$44) -- #206 //│ def foldl(f_4$0,i$0,ls_4$0) = //│ case ls_4$0 of -- #268 //│ Cons => //│ let x$50 = Cons.t(ls_4$0) in -- #265 //│ let x$51 = Cons.h(ls_4$0) in -- #264 //│ let x$52 = Callable.apply2(f_4$0,i$0,x$51) in -- #263 //│ let* (x$53) = foldl(f_4$0,x$52,x$50) in -- #262 //│ jump j$3(x$53) -- #261 //│ Nil => //│ jump j$3(i$0) -- #267 //│ def j$3(x$49) = //│ x$49 -- #238 //│ def foldr(f_5$0,i_1$0,ls_5$0) = //│ case ls_5$0 of -- #300 //│ Cons => //│ let x$55 = Cons.t(ls_5$0) in -- #297 //│ let x$56 = Cons.h(ls_5$0) in -- #296 //│ let* (x$57) = foldr(f_5$0,i_1$0,x$55) in -- #295 //│ let x$58 = Callable.apply2(f_5$0,x$56,x$57) in -- #294 //│ jump j$4(x$58) -- #293 //│ Nil => //│ jump j$4(i_1$0) -- #299 //│ def j$4(x$54) = //│ x$54 -- #270 //│ def zip(xs$0,ys$0) = //│ case xs$0 of -- #353 //│ Cons => //│ let x$60 = Cons.t(xs$0) in -- #349 //│ let x$61 = Cons.h(xs$0) in -- #348 //│ case ys$0 of -- #347 //│ Cons => //│ let x$63 = Cons.t(ys$0) in -- #343 //│ let x$64 = Cons.h(ys$0) in -- #342 //│ let x$65 = Tuple2(x$61,x$64) in -- #341 //│ let* (x$66) = zip(x$60,x$63) in -- #340 //│ let x$67 = Cons(x$65,x$66) in -- #339 //│ jump j$6(x$67) -- #338 //│ Nil => //│ let x$68 = Nil() in -- #346 //│ jump j$6(x$68) -- #345 //│ Nil => //│ let x$69 = Nil() in -- #352 //│ jump j$5(x$69) -- #351 //│ def j$5(x$59) = //│ x$59 -- #302 //│ def j$6(x$62) = //│ jump j$5(x$62) -- #313 //│ def zipWith(f_7$0,xs_4$0,ys_4$0) = //│ case xs_4$0 of -- #409 //│ Cons => //│ let x$71 = Cons.t(xs_4$0) in -- #405 //│ let x$72 = Cons.h(xs_4$0) in -- #404 //│ case ys_4$0 of -- #403 //│ Cons => //│ let x$74 = Cons.t(ys_4$0) in -- #399 //│ let x$75 = Cons.h(ys_4$0) in -- #398 //│ let x$76 = Callable.apply2(f_7$0,x$72,x$75) in -- #397 //│ let* (x$77) = zipWith(f_7$0,x$71,x$74) in -- #396 //│ let x$78 = Cons(x$76,x$77) in -- #395 //│ jump j$8(x$78) -- #394 //│ Nil => //│ let x$79 = Nil() in -- #402 //│ jump j$8(x$79) -- #401 //│ Nil => //│ let x$80 = Nil() in -- #408 //│ jump j$7(x$80) -- #407 //│ def j$7(x$70) = //│ x$70 -- #355 //│ def j$8(x$73) = //│ jump j$7(x$73) -- #366 //│ def head(ls_7$0) = //│ case ls_7$0 of -- #427 //│ Cons => //│ let x$82 = Cons.t(ls_7$0) in -- #423 //│ let x$83 = Cons.h(ls_7$0) in -- #422 //│ jump j$9(x$83) -- #421 //│ Nil => //│ let x$85 = Lambda$0() in -- #426 //│ jump j$9(x$85) -- #425 //│ def j$9(x$81) = //│ x$81 -- #411 //│ def tail(ls_9$0) = //│ case ls_9$0 of -- #445 //│ Cons => //│ let x$87 = Cons.t(ls_9$0) in -- #441 //│ let x$88 = Cons.h(ls_9$0) in -- #440 //│ jump j$10(x$87) -- #439 //│ Nil => //│ let x$90 = Lambda$1() in -- #444 //│ jump j$10(x$90) -- #443 //│ def j$10(x$86) = //│ x$86 -- #429 //│ def enumFromTo(a$0,b$0) = //│ let x$91 = <=(a$0,b$0) in -- #477 //│ if x$91 -- #476 //│ true => //│ let x$93 = +(a$0,1) in -- #472 //│ let* (x$94) = enumFromTo(x$93,b$0) in -- #471 //│ let x$95 = Cons(a$0,x$94) in -- #470 //│ jump j$11(x$95) -- #469 //│ false => //│ let x$96 = Nil() in -- #475 //│ jump j$11(x$96) -- #474 //│ def j$11(x$92) = //│ x$92 -- #452 //│ def enumFromThenTo(a_1$0,t_11$0,b_1$0) = //│ let x$97 = <=(a_1$0,b_1$0) in -- #517 //│ if x$97 -- #516 //│ true => //│ let x$99 = *(2,t_11$0) in -- #512 //│ let x$100 = -(x$99,a_1$0) in -- #511 //│ let* (x$101) = enumFromThenTo(t_11$0,x$100,b_1$0) in -- #510 //│ let x$102 = Cons(a_1$0,x$101) in -- #509 //│ jump j$12(x$102) -- #508 //│ false => //│ let x$103 = Nil() in -- #515 //│ jump j$12(x$103) -- #514 //│ def j$12(x$98) = //│ x$98 -- #484 //│ def take(n$0,ls_11$0) = //│ let x$104 = >(n$0,0) in -- #566 //│ if x$104 -- #565 //│ true => //│ case ls_11$0 of -- #561 //│ Cons => //│ let x$107 = Cons.t(ls_11$0) in -- #557 //│ let x$108 = Cons.h(ls_11$0) in -- #556 //│ let x$109 = -(n$0,1) in -- #555 //│ let* (x$110) = take(x$109,x$107) in -- #554 //│ let x$111 = Cons(x$108,x$110) in -- #553 //│ jump j$14(x$111) -- #552 //│ Nil => //│ let x$112 = Nil() in -- #560 //│ jump j$14(x$112) -- #559 //│ false => //│ let x$113 = Nil() in -- #564 //│ jump j$13(x$113) -- #563 //│ def j$13(x$105) = //│ x$105 -- #524 //│ def j$14(x$106) = //│ jump j$13(x$106) -- #527 //│ def length(ls_13$0) = //│ case ls_13$0 of -- #593 //│ Cons => //│ let x$115 = Cons.t(ls_13$0) in -- #590 //│ let x$116 = Cons.h(ls_13$0) in -- #589 //│ let* (x$117) = length(x$115) in -- #588 //│ let x$118 = +(1,x$117) in -- #587 //│ jump j$15(x$118) -- #586 //│ Nil => //│ jump j$15(0) -- #592 //│ def j$15(x$114) = //│ x$114 -- #568 //│ def mappend(xs_8$0,ys_8$0) = //│ case xs_8$0 of -- #622 //│ Cons => //│ let x$120 = Cons.t(xs_8$0) in -- #619 //│ let x$121 = Cons.h(xs_8$0) in -- #618 //│ let* (x$122) = mappend(x$120,ys_8$0) in -- #617 //│ let x$123 = Cons(x$121,x$122) in -- #616 //│ jump j$16(x$123) -- #615 //│ Nil => //│ jump j$16(ys_8$0) -- #621 //│ def j$16(x$119) = //│ x$119 -- #595 //│ def sum(ls_14$0) = //│ let* (x$124) = sumAux(ls_14$0,0) in -- #629 //│ x$124 -- #628 //│ def sumAux(ls_15$0,a_4$0) = //│ case ls_15$0 of -- #658 //│ Nil => //│ jump j$17(a_4$0) -- #633 //│ Cons => //│ let x$126 = Cons.t(ls_15$0) in -- #657 //│ let x$127 = Cons.h(ls_15$0) in -- #656 //│ let x$128 = +(a_4$0,x$127) in -- #655 //│ let* (x$129) = sumAux(x$126,x$128) in -- #654 //│ jump j$17(x$129) -- #653 //│ def j$17(x$125) = //│ x$125 -- #631 //│ def atIndex(n_2$0,ls_16$0) = //│ let x$130 = <(n_2$0,0) in -- #713 //│ if x$130 -- #712 //│ true => //│ let x$133 = Lambda$2() in -- #668 //│ jump j$18(x$133) -- #667 //│ false => //│ case ls_16$0 of -- #711 //│ Cons => //│ let x$135 = Cons.t(ls_16$0) in -- #707 //│ let x$136 = Cons.h(ls_16$0) in -- #706 //│ let x$137 = ==(n_2$0,0) in -- #705 //│ if x$137 -- #704 //│ true => //│ jump j$20(x$136) -- #689 //│ false => //│ let x$139 = -(n_2$0,1) in -- #703 //│ let* (x$140) = atIndex(x$139,x$135) in -- #702 //│ jump j$20(x$140) -- #701 //│ Nil => //│ let x$142 = Lambda$3() in -- #710 //│ jump j$19(x$142) -- #709 //│ def j$18(x$131) = //│ x$131 -- #665 //│ def j$19(x$134) = //│ jump j$18(x$134) -- #671 //│ def j$20(x$138) = //│ jump j$19(x$138) -- #687 //│ def concat(lss$0) = //│ case lss$0 of -- #741 //│ Cons => //│ let x$144 = Cons.t(lss$0) in -- #737 //│ let x$145 = Cons.h(lss$0) in -- #736 //│ let* (x$146) = concat(x$144) in -- #735 //│ let* (x$147) = mappend(x$145,x$146) in -- #734 //│ jump j$21(x$147) -- #733 //│ Nil => //│ let x$148 = Nil() in -- #740 //│ jump j$21(x$148) -- #739 //│ def j$21(x$143) = //│ x$143 -- #715 //│ def reverse(ls_18$0) = //│ let x$149 = Nil() in -- #749 //│ let* (x$150) = reverse_helper(ls_18$0,x$149) in -- #748 //│ x$150 -- #747 //│ def reverse_helper(ls_19$0,a_5$0) = //│ case ls_19$0 of -- #778 //│ Cons => //│ let x$152 = Cons.t(ls_19$0) in -- #775 //│ let x$153 = Cons.h(ls_19$0) in -- #774 //│ let x$154 = Cons(x$153,a_5$0) in -- #773 //│ let* (x$155) = reverse_helper(x$152,x$154) in -- #772 //│ jump j$22(x$155) -- #771 //│ Nil => //│ jump j$22(a_5$0) -- #777 //│ def j$22(x$151) = //│ x$151 -- #751 //│ def listcomp_fun1(ms$0,listcomp_fun_para$0) = //│ case listcomp_fun_para$0 of -- #806 //│ Cons => //│ let x$157 = Cons.t(listcomp_fun_para$0) in -- #802 //│ let x$158 = Cons.h(listcomp_fun_para$0) in -- #801 //│ let* (x$159) = listcomp_fun2(ms$0,x$158,x$157,ms$0) in -- #800 //│ jump j$23(x$159) -- #799 //│ Nil => //│ let x$160 = Nil() in -- #805 //│ jump j$23(x$160) -- #804 //│ def j$23(x$156) = //│ x$156 -- #780 //│ def listcomp_fun2(ms$1,listcomp_fun_ls_h_out$0,listcomp_fun_ls_t_out$0,listcomp_fun_para$1) = //│ case listcomp_fun_para$1 of -- #851 //│ Cons => //│ let x$162 = Cons.t(listcomp_fun_para$1) in -- #842 //│ let x$163 = Cons.h(listcomp_fun_para$1) in -- #841 //│ let x$164 = Tuple2(listcomp_fun_ls_h_out$0,x$163) in -- #840 //│ let* (x$165) = listcomp_fun2(ms$1,listcomp_fun_ls_h_out$0,listcomp_fun_ls_t_out$0,x$162) in -- #839 //│ let x$166 = Cons(x$164,x$165) in -- #838 //│ jump j$24(x$166) -- #837 //│ Nil => //│ let* (x$167) = listcomp_fun1(ms$1,listcomp_fun_ls_t_out$0) in -- #850 //│ jump j$24(x$167) -- #849 //│ def j$24(x$161) = //│ x$161 -- #808 //│ def test(test_arg1$0) = //│ let* (x$168) = const5000() in -- #912 //│ let* (x$169) = const5000() in -- #911 //│ let* (x$170) = z_add(x$169,test_arg1$0) in -- #910 //│ let* (x$171) = z_enumFromTo(x$168,x$170) in -- #909 //│ let* (x$172) = const10000() in -- #908 //│ let* (x$173) = const10000() in -- #907 //│ let* (x$174) = z_add(x$173,test_arg1$0) in -- #906 //│ let* (x$175) = z_enumFromTo(x$172,x$174) in -- #905 //│ let x$178 = Lambda$4() in -- #904 //│ let* (x$179) = listcomp_fun1(x$175,x$171) in -- #903 //│ let* (x$180) = map(x$178,x$179) in -- #902 //│ let x$183 = Lambda$5() in -- #901 //│ let* (x$184) = map(x$183,x$180) in -- #900 //│ let* (x$185) = max'(x$184) in -- #899 //│ x$185 -- #898 //│ def const10000() = //│ let* (x$186) = z_of_int(10000) in -- #917 //│ x$186 -- #916 //│ def f1(f1_arg1$0) = //│ case f1_arg1$0 of -- #946 //│ Tuple2 => //│ let x$188 = Tuple2.y(f1_arg1$0) in -- #945 //│ let x$189 = Tuple2.x(f1_arg1$0) in -- #944 //│ let* (x$190) = gcdE(x$189,x$188) in -- #943 //│ let x$191 = Tuple3(x$189,x$188,x$190) in -- #942 //│ jump j$25(x$191) -- #941 //│ def j$25(x$187) = //│ x$187 -- #919 //│ def quotRem(quotRem_arg1$0,quotRem_arg2$0) = //│ let* (x$192) = z_div(quotRem_arg1$0,quotRem_arg2$0) in -- #965 //│ let* (x$193) = z_mod(quotRem_arg1$0,quotRem_arg2$0) in -- #964 //│ let x$194 = Tuple2(x$192,x$193) in -- #963 //│ x$194 -- #962 //│ def max'(max'_arg1$0) = //│ case max'_arg1$0 of -- #1028 //│ Cons => //│ let x$196 = Cons.t(max'_arg1$0) in -- #1027 //│ let x$197 = Cons.h(max'_arg1$0) in -- #1026 //│ case x$196 of -- #1025 //│ Nil => //│ jump j$27(x$197) -- #980 //│ Cons => //│ let x$199 = Cons.t(x$196) in -- #1024 //│ let x$200 = Cons.h(x$196) in -- #1023 //│ let* (x$201) = z_lt(x$197,x$200) in -- #1022 //│ if x$201 -- #1021 //│ true => //│ let x$203 = Cons(x$200,x$199) in -- #1008 //│ let* (x$204) = max'(x$203) in -- #1007 //│ jump j$28(x$204) -- #1006 //│ false => //│ let x$205 = Cons(x$197,x$199) in -- #1020 //│ let* (x$206) = max'(x$205) in -- #1019 //│ jump j$28(x$206) -- #1018 //│ def j$26(x$195) = //│ x$195 -- #967 //│ def j$27(x$198) = //│ jump j$26(x$198) -- #978 //│ def j$28(x$202) = //│ jump j$27(x$202) -- #996 //│ def g(g_arg1$0,g_arg2$0) = //│ case g_arg1$0 of -- #1156 //│ Tuple3 => //│ let x$208 = Tuple3.z(g_arg1$0) in -- #1155 //│ let x$209 = Tuple3.y(g_arg1$0) in -- #1154 //│ let x$210 = Tuple3.x(g_arg1$0) in -- #1153 //│ case g_arg2$0 of -- #1152 //│ Tuple3 => //│ let x$212 = Tuple3.z(g_arg2$0) in -- #1151 //│ let x$213 = Tuple3.y(g_arg2$0) in -- #1150 //│ let x$214 = Tuple3.x(g_arg2$0) in -- #1149 //│ let* (x$215) = const0() in -- #1148 //│ let* (x$216) = z_equal(x$212,x$215) in -- #1147 //│ if x$216 -- #1146 //│ true => //│ let x$218 = Tuple3(x$208,x$210,x$209) in -- #1076 //│ jump j$31(x$218) -- #1075 //│ false => //│ let* (x$219) = quotRem(x$208,x$212) in -- #1145 //│ case x$219 of -- #1144 //│ Tuple2 => //│ let x$221 = Tuple2.y(x$219) in -- #1143 //│ let x$222 = Tuple2.x(x$219) in -- #1142 //│ let x$223 = Tuple3(x$214,x$213,x$212) in -- #1141 //│ let* (x$224) = z_mul(x$222,x$214) in -- #1140 //│ let* (x$225) = z_sub(x$210,x$224) in -- #1139 //│ let* (x$226) = z_mul(x$222,x$213) in -- #1138 //│ let* (x$227) = z_sub(x$209,x$226) in -- #1137 //│ let x$228 = Tuple3(x$225,x$227,x$221) in -- #1136 //│ let* (x$229) = g(x$223,x$228) in -- #1135 //│ jump j$32(x$229) -- #1134 //│ def j$29(x$207) = //│ x$207 -- #1030 //│ def j$30(x$211) = //│ jump j$29(x$211) -- #1045 //│ def j$31(x$217) = //│ jump j$30(x$217) -- #1066 //│ def j$32(x$220) = //│ jump j$31(x$220) -- #1085 //│ def abs(abs_arg1$0) = //│ let* (x$230) = const0() in -- #1179 //│ let* (x$231) = z_lt(abs_arg1$0,x$230) in -- #1178 //│ if x$231 -- #1177 //│ true => //│ let* (x$233) = const0() in -- #1174 //│ let* (x$234) = z_sub(x$233,abs_arg1$0) in -- #1173 //│ jump j$33(x$234) -- #1172 //│ false => //│ jump j$33(abs_arg1$0) -- #1176 //│ def j$33(x$232) = //│ x$232 -- #1164 //│ def f2(f2_arg1$0) = //│ case f2_arg1$0 of -- #1234 //│ Tuple3 => //│ let x$236 = Tuple3.z(f2_arg1$0) in -- #1233 //│ let x$237 = Tuple3.y(f2_arg1$0) in -- #1232 //│ let x$238 = Tuple3.x(f2_arg1$0) in -- #1231 //│ case x$236 of -- #1230 //│ Tuple3 => //│ let x$240 = Tuple3.z(x$236) in -- #1229 //│ let x$241 = Tuple3.y(x$236) in -- #1228 //│ let x$242 = Tuple3.x(x$236) in -- #1227 //│ let* (x$243) = z_add(x$242,x$241) in -- #1226 //│ let* (x$244) = z_add(x$243,x$240) in -- #1225 //│ let* (x$245) = abs(x$244) in -- #1224 //│ jump j$35(x$245) -- #1223 //│ def j$34(x$235) = //│ x$235 -- #1181 //│ def j$35(x$239) = //│ jump j$34(x$239) -- #1196 //│ def const0() = //│ let* (x$246) = z_of_int(0) in -- #1239 //│ x$246 -- #1238 //│ def gcdE(gcdE_arg1$0,gcdE_arg2$0) = //│ let* (x$247) = const0() in -- #1296 //│ let* (x$248) = z_equal(gcdE_arg1$0,x$247) in -- #1295 //│ if x$248 -- #1294 //│ true => //│ let* (x$250) = const0() in -- #1261 //│ let* (x$251) = const1() in -- #1260 //│ let x$252 = Tuple3(gcdE_arg2$0,x$250,x$251) in -- #1259 //│ jump j$36(x$252) -- #1258 //│ false => //│ let* (x$253) = const1() in -- #1293 //│ let* (x$254) = const0() in -- #1292 //│ let x$255 = Tuple3(x$253,x$254,gcdE_arg1$0) in -- #1291 //│ let* (x$256) = const0() in -- #1290 //│ let* (x$257) = const1() in -- #1289 //│ let x$258 = Tuple3(x$256,x$257,gcdE_arg2$0) in -- #1288 //│ let* (x$259) = g(x$255,x$258) in -- #1287 //│ jump j$36(x$259) -- #1286 //│ def j$36(x$249) = //│ x$249 -- #1247 //│ def const1() = //│ let* (x$260) = z_of_int(1) in -- #1301 //│ x$260 -- #1300 //│ def const5000() = //│ let* (x$261) = z_of_int(5000) in -- #1306 //│ x$261 -- #1305 //│ def testGcd_nofib(testGcd_nofib_arg1$0) = //│ let* (x$262) = test(testGcd_nofib_arg1$0) in -- #1311 //│ x$262 -- #1310 //│ def z_enumFromTo(z_enumFromTo_arg1$0,z_enumFromTo_arg2$0) = //│ let* (x$263) = z_leq(z_enumFromTo_arg1$0,z_enumFromTo_arg2$0) in -- #1345 //│ if x$263 -- #1344 //│ true => //│ let* (x$265) = const1() in -- #1340 //│ let* (x$266) = z_add(z_enumFromTo_arg1$0,x$265) in -- #1339 //│ let* (x$267) = z_enumFromTo(x$266,z_enumFromTo_arg2$0) in -- #1338 //│ let x$268 = Cons(z_enumFromTo_arg1$0,x$267) in -- #1337 //│ jump j$37(x$268) -- #1336 //│ false => //│ let x$269 = Nil() in -- #1343 //│ jump j$37(x$269) -- #1342 //│ def j$37(x$264) = //│ x$264 -- #1318 //│ let* (x$0) = z_of_int(400) in -- #8 //│ let* (x$1) = testGcd_nofib(x$0) in -- #7 //│ x$1 -- #6 //│ //│ //│ Execution succeeded: //│ Z(5201) //│ ================================================ FILE: compiler/shared/test/scala/mlscript/compiler/Test.scala ================================================ package mlscript package compiler import mlscript.utils.shorthands._ import scala.util.control.NonFatal import scala.collection.mutable.StringBuilder import mlscript.compiler.TreeDebug import simpledef.SimpleDef import DiffTestCompiler.* class DiffTestCompiler extends DiffTests(State) { override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() var rstUnit = unit; try val lifter = ClassLifter(mode.fullExceptionStack) if (mode.lift) { output("Lifted:") rstUnit = lifter.liftTypingUnit(unit) output(PrettyPrinter.showTypingUnit(rstUnit)) } if (mode.showParse) output(rstUnit.toString()) if (mode.dbgLifting) output(lifter.getLog) catch case NonFatal(err) => output("Lifting failed: " ++ err.toString()) if mode.fullExceptionStack then outputBuilder ++= "\n" ++ err.getStackTrace().map(_.toString()).mkString("\n") if (mode.lift) { (outputBuilder.toString().linesIterator.toList, Some(rstUnit)) } else { (outputBuilder.toString().linesIterator.toList, None) } override def postTypingProcess(mode: ModeType, basePath: List[Str], testName: Str, unit: TypingUnit, output: Str => Unit): Option[TypingUnit] = if(mode.simpledef || basePath.contains("Defunctionalize")) { output("\nSimpledef:") val treeDebug = new TreeDebug(if mode.dbgSimpledef then output else (str) => ()) val pd = SimpleDef(treeDebug) pd(unit) val defuncAST = pd.rewriteProgram(unit) output(defuncAST.showDbg.replace(";", "\n")) output("End simpledef\n") return Some(defuncAST) } None } object DiffTestCompiler { lazy val State = new DiffTests.State(DiffTests.pwd/"compiler"/"shared"/"test"/"diff") } ================================================ FILE: compiler/shared/test/scala/mlscript/compiler/TestIR.scala ================================================ package mlscript.compiler import mlscript.utils.shorthands._ import mlscript.compiler.ir._ import scala.collection.mutable.{ListBuffer, StringBuilder} import mlscript.Statement import mlscript.{DiffTests, ModeType, TypingUnit} import mlscript.compiler.ir.{Fresh, FreshInt, Builder} import mlscript.compiler.codegen.cpp._ import mlscript.Diagnostic import mlscript.compiler.optimizer.TailRecOpt import IRDiffTestCompiler.* class IRDiffTestCompiler extends DiffTests(State) { def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) = { val p = new java.io.PrintWriter(f) try { op(p) } finally { p.close() } } val preludeSource = ListBuffer[Statement]() override def postProcess(mode: ModeType, basePath: List[Str], testName: Str, originalUnit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (List[Str], Option[TypingUnit]) = val outputBuilder = StringBuilder() if (mode.prelude) preludeSource.addAll(originalUnit.rawEntities) output("\nPreluded.") else if (mode.useIR || mode.irVerbose) try val (fresh, freshFnId, freshClassId, freshTag) = (Fresh(), FreshInt(), FreshInt(), FreshInt()) val gb = Builder(fresh, freshFnId, freshClassId, freshTag, raise, mode.irVerbose) val prelude = TypingUnit(preludeSource.toList) var graph = gb.buildGraph(prelude, originalUnit) val hiddenNames = gb.getHiddenNames(prelude) output("\n\nIR:") output(graph.show(hiddenNames)) if !mode.noTailRecOpt then val tailRecOpt = new TailRecOpt(freshFnId, freshClassId, freshTag, raise) val (g, comps) = tailRecOpt.run_debug(graph) output("\n\nStrongly Connected Tail Calls:") output(comps.toString) graph = g output(graph.show(hiddenNames)) var interp_result: Opt[Str] = None if (mode.interpIR) output("\nInterpreted:") val ir = Interpreter(mode.irVerbose).interpret(graph) interp_result = Some(ir) output(ir) if (mode.genCpp) val cpp = codegen(graph) if (mode.showCpp) output("\nCpp:") if (mode.irVerbose) output(cpp.toDocument.print) else output(cpp.toDocumentWithoutHidden.print) if (mode.writeCpp) printToFile(java.io.File(s"compiler/shared/test/diff-ir/cpp/${testName}.cpp")) { p => p.println(cpp.toDocument.print) } if (mode.runCpp) val auxPath = os.pwd/"compiler"/"shared"/"test"/"diff-ir"/"cpp" val cppHost = CppCompilerHost(auxPath.toString) if !cppHost.ready then output("\nCpp Compilation Failed: Cpp compiler or GNU Make not found") else output("\n") cppHost.compileAndRun(cpp.toDocument.print, output) catch case err: Exception => output(s"\nIR Processing Failed: ${err.getMessage()}") output("\n" ++ err.getStackTrace().mkString("\n")) case err: StackOverflowError => output(s"\nIR Processing Failed: ${err.getMessage()}") output("\n" ++ err.getStackTrace().mkString("\n")) (outputBuilder.toString.linesIterator.toList, None) } object IRDiffTestCompiler { lazy val State = new DiffTests.State(DiffTests.pwd/"compiler"/"shared"/"test"/"diff-ir") } ================================================ FILE: core/shared/main/scala/utils/Identity.scala ================================================ package mlscript.utils class Identity[T <: AnyRef](val value: T) { override def equals(other: Any): Boolean = other match { case that: Identity[_] => (that.value: Any) is this.value case _ => false } override def hashCode(): Int = System.identityHashCode(value) } ================================================ FILE: core/shared/main/scala/utils/Lazy.scala ================================================ package mlscript.utils import shorthands._ abstract class Box[+A] { def force: Opt[A] def force_! : A def isComputing: Bool } class Eager[+A](val value: A) extends Box[A] { def isComputing = false lazy val force = S(value) def force_! = value } object Lazy { def apply[A](thunk: => A): Lazy[A] = new Lazy[A] { def compute: A = thunk } } abstract class Lazy[A] extends Box[A] { protected def compute: A def isComputing = _isComputing def isEmpty: Bool = _value.isEmpty private var _isComputing = false private var _value: Opt[A] = N def force = if (_isComputing) N else S(force_!) def force_! = { assert(!_isComputing) _value.getOrElse(_compute) } private def _compute = { _isComputing = true try { val v = compute _value = S(v) v } finally { _isComputing = false } } } ================================================ FILE: core/shared/main/scala/utils/algorithms.scala ================================================ package mlscript.utils import scala.annotation.tailrec import scala.collection.immutable.SortedMap object algorithms { final class CyclicGraphError(message: String) extends Exception(message) /** * Sort a graph topologically. * * @param edges edges (target, source) in the directed acyclic graph * @param nodes provide if you want to include some isolated nodes in the result * @return */ def topologicalSort[A: Ordering](edges: Iterable[(A, A)], nodes: Iterable[A] = Nil): Iterable[A] = { @tailrec def sort(toPreds: SortedMap[A, Set[A]], done: Iterable[A]): Iterable[A] = { val (noPreds, hasPreds) = toPreds.partition { _._2.isEmpty } if (noPreds.isEmpty) { if (hasPreds.isEmpty) done else throw new CyclicGraphError(hasPreds.toString) } else { val found = noPreds.map { _._1 } sort(SortedMap.from(hasPreds.view.mapValues(_ -- found)), done ++ found) } } val toPred = edges.foldLeft(SortedMap.from(nodes.map { _ -> Set.empty[A] })) { (acc, e) => acc + (e._1 -> (acc.getOrElse(e._1, Set()) + e._2)) + (e._2 -> acc.getOrElse(e._2, Set())) } sort(toPred, Seq()) } /** * Partitions a graph into its strongly connected components. The input type must be able to * be hashed efficiently as it will be used as a key. * * @param edges The edges of the graph. * @param nodes Any additional nodes that are not necessarily in the edges list. (Overlap is fine) * @return A list of strongly connected components of the graph. */ def partitionScc[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { case class SccNode[A]( val node: A, val id: Int, var num: Int = -1, var lowlink: Int = -1, var visited: Boolean = false, var onStack: Boolean = false ) // pre-process: assign each node an id val nodesUniq = { val seen = collection.mutable.LinkedHashSet.empty[A] for ((a, b) <- edges) { seen.add(a); seen.add(b) } for (n <- nodes) seen.add(n) seen.toList } val nodesN = nodesUniq.zipWithIndex.map { case (node, idx) => SccNode(node, idx) } val nodeToIdx = nodesN.map(node => node.node -> node.id).toMap val nodesIdx = nodesN.map { case node => node.id -> node }.toMap val neighbours = edges .map { case (a, b) => (nodeToIdx(a), nodesIdx(nodeToIdx(b))) } .groupBy(_._1) .map { case (a, b) => a -> b.map(_._2) } .withDefault(_ => Nil) // Tarjan's algorithm var stack: List[SccNode[A]] = List.empty var sccs: List[List[A]] = List.empty var i = 0 def dfs(node: SccNode[A], depth: Int = 0): Unit = { def printlnsp(s: String) = { println(s) } node.num = i node.lowlink = node.num node.visited = true stack = node :: stack i += 1 for (n <- neighbours(node.id)) { if (!n.visited) { dfs(n, depth + 1) node.lowlink = n.lowlink.min(node.lowlink) } else if (!n.onStack) { node.lowlink = n.num.min(node.lowlink) } } if (node.lowlink == node.num) { var scc: List[A] = List.empty var cur = stack.head stack = stack.tail cur.onStack = true while (cur.id != node.id) { scc = cur.node :: scc cur = stack.head stack = stack.tail cur.onStack = true } scc = cur.node :: scc sccs = scc :: sccs } } for (n <- nodesN) { if (!n.visited) dfs(n) } sccs } /** * Info about a graph partitioned into its strongly-connected sets. The input type must be able to * be hashed efficiently as it will be used as a key. * * @param sccs The strongly connected sets. * @param edges The edges of the strongly-connected sets. Together with `sccs`, this forms an acyclic graph. * @param inDegs The in-degrees of the above described graph. * @param outDegs The out-degrees of the above described graph. */ case class SccsInfo[A]( sccs: Map[Int, List[A]], edges: Map[Int, Iterable[Int]], inDegs: Map[Int, Int], outDegs: Map[Int, Int], ) /** * Partitions a graph into its strongly connected components and returns additional information * about the partition. The input type must be able to be hashed efficiently as it will be used as a key. * * @param edges The edges of the graph. * @param nodes Any additional nodes that are not necessarily in the edges list. (Overlap is fine) * @return The partitioned graph and info about it. */ def sccsWithInfo[A](edges: Iterable[(A, A)], nodes: Iterable[A]): SccsInfo[A] = { val sccs = partitionScc(edges, nodes) val withIdx = sccs.zipWithIndex.map(_.swap).toMap val lookup = ( for { (id, scc) <- withIdx node <- scc } yield node -> id ).toMap val notInSccEdges = edges.map { case (a, b) => (lookup(a), lookup(b)) }.filter { case (a, b) => a != b } val outs = notInSccEdges.groupBy { case (a, b) => a } val sccEdges = withIdx.map { case (a, _) => a -> Nil // add default case } ++ outs.map { case (a, edges) => a -> edges.map(_._2) }.toMap val inDegs = notInSccEdges.groupBy { case (a, b) => b }.map { case (b, edges) => b -> edges.size } val outDegs = outs.map { case (a, edges) => a -> edges.size } SccsInfo(withIdx, sccEdges, inDegs, outDegs) } } ================================================ FILE: core/shared/main/scala/utils/package.scala ================================================ package mlscript import utils.shorthands._ import scala.collection.mutable package object utils { import scala.collection.mutable import scala.collection.immutable.{SortedSet, SortedMap} @SuppressWarnings(Array( "org.wartremover.warts.Equals", "org.wartremover.warts.AsInstanceOf")) implicit final class AnyOps[A](self: A) { def ===(other: A): Bool = self == other def =/=(other: A): Bool = self != other def is(other: A): Bool = self.asInstanceOf[AnyRef] eq other.asInstanceOf[AnyRef] def isnt(other: AnyRef): Bool = !(self.asInstanceOf[AnyRef] eq other) /** An alternative to === when in ScalaTest, which shadows our === */ def =:=(other: A): Bool = self == other def in(xs: A => Bool): Bool = xs(self) def in(xs: Seq[_ >: A]): Bool = xs.exists(_ === self) } implicit class IntOps(private val self: Int) extends AnyVal { def toOrdinalWord: String = { require(self >= 0) self + 1 match { case 1 => "first" case 2 => "second" case 3 => "third" case n @ (11 | 12 | 13) => s"${n}th" case n => self.toString + (n % 10 match { case 1 => "st" case 2 => "nd" case 3 => "rd" case _ => "th" }) } } } implicit class StringOps(private val self: String) extends AnyVal { import collection.mutable def splitSane(sep: Char): mutable.ArrayBuffer[Str] = { val buf = mutable.ArrayBuffer(new StringBuilder) self.foreach { c => if (c === sep) buf += new StringBuilder else buf.last append c; () } buf.map(_.toString) } def mapLines(f: String => String): String = splitSane('\n') map f mkString "\n" def indent(pre: String): String = mapLines(pre + _) def indent: String = indent("\t") def indentNewLines(pre: String = "\t"): String = splitSane('\n').toList match { case head :: (rest @ _ :: _) => head + "\n" + rest.map(pre + _).mkString("\n") case _ => self } def truncate(maxChars: Int, replace: String): String = { val newStr = self.take(maxChars) if (newStr.length < self.length) newStr + replace else newStr } def isCapitalized: Bool = self.nonEmpty && self.head.isUpper def isUncapitalized: Bool = self.nonEmpty && self.head.isLower def decapitalize: String = if (self.length === 0 || !self.charAt(0).isUpper) self else self.updated(0, self.charAt(0).toLower) def pluralize(quantity: Int, inclusive: Boolean = false, es: Boolean = false): String = (if (inclusive) quantity.toString + " " else "") + (if (quantity > 1 || quantity === 0) self + (if (es) "es" else "s") else self) @SuppressWarnings(Array("org.wartremover.warts.Equals")) def ===(other: String): Bool = self.equals(other) } implicit class IterableOps[A](private val self: IterableOnce[A]) extends AnyVal { def mkStringOr( sep: String = "", start: String = "", end: String = "", els: String = "" ): String = { val ite = self.iterator if (ite.nonEmpty) ite.mkString(start, sep, end) else els } def lnIndent(pre: String = "\t"): Str = self.iterator.map("\n" + _.toString.indent(pre)).mkString def collectLast[B](f: Paf[A, B]): Opt[B] = self.iterator.collect(f).foldLeft[Opt[B]](N)((_, a) => S(a)) def toSortedSet(implicit ord: Ordering[A]): SortedSet[A] = SortedSet.from(self) } implicit class OptIterableOps[A](private val self: IterableOnce[Opt[A]]) extends AnyVal { def firstSome: Opt[A] = self.iterator.collectFirst { case Some(v) => v } } implicit class PairIterableOps[A, B](private val self: IterableOnce[A -> B]) extends AnyVal { def mapKeys[C](f: A => C): List[C -> B] = mapKeysIter(f).toList def mapValues[C](f: B => C): List[A -> C] = mapValuesIter(f).toList def mapKeysIter[C](f: A => C): Iterator[C -> B] = self.iterator.map(p => f(p._1) -> p._2) def mapValuesIter[C](f: B => C): Iterator[A -> C] = self.iterator.map(p => p._1 -> f(p._2)) def keys: Iterator[A] = self.iterator.map(_._1) def values: Iterator[B] = self.iterator.map(_._2) def toSortedMap(implicit ord: Ordering[A]): SortedMap[A, B] = SortedMap.from(self) } implicit class MapOps[A, B](private val self: Map[A, B]) extends AnyVal { def +++(that: Map[A, B]): Map[A, B] = { require(!self.keysIterator.exists(that.keySet), (self.keySet, that.keySet)) self ++ that } def +++(that: Iterable[A -> B]): Map[A, B] = { val thatKeySet = that.iterator.map(_._1).toSet require(!self.keysIterator.exists(thatKeySet), (self.keySet, thatKeySet)) self ++ that } } def mergeOptionsFlat[A](lhs: Option[A], rhs: Option[A])(f: (A, A) => Opt[A]): Option[A] = (lhs, rhs) match { case (Some(l), Some(r)) => f(l, r) case (lhs @ Some(_), _) => lhs case (_, rhs @ Some(_)) => rhs case (None, None) => None } def mergeOptions[A](lhs: Option[A], rhs: Option[A])(f: (A, A) => A): Option[A] = mergeOptionsFlat(lhs, rhs)(f(_, _) |> some) def mergeMap[A, B](lhs: Iterable[(A, B)], rhs: Iterable[(A, B)])(f: (B, B) => B): Map[A,B] = new mutable.ArrayBuffer(lhs.knownSize + rhs.knownSize max 8) .addAll(lhs).addAll(rhs).groupMapReduce(_._1)(_._2)(f) def mergeSortedMap[A: Ordering, B](lhs: Iterable[(A, B)], rhs: Iterable[(A, B)])(f: (B, B) => B): SortedMap[A,B] = SortedMap.from(mergeMap(lhs, rhs)(f)) implicit final class PafHelper[A,B](private val self: Paf[A,B]) extends AnyVal { // def appOrElse[C](arg: A)(els: A => ) def appOrElse[A1 <: A, B1 >: B](x: A1)(default: A1 => B1): B1 = self.applyOrElse(x, default) } implicit final class GenHelper[A](private val self: A) extends AnyVal { @inline def into [B] (rhs: A => B): B = rhs(self) @inline def |> [B] (rhs: A => B): B = rhs(self) @inline def |>? [B] (rhs: PartialFunction[A, B]): Option[B] = rhs.andThen(some).appOrElse(self)(_ => none) @inline def |>?? [B] (rhs: PartialFunction[A, Option[B]]): Option[B] = rhs.appOrElse(self)(_ => none) @inline def |>! [B] (rhs: PartialFunction[A, B]): B = rhs(self) /** Like |> but expects the function to return the same type */ @inline def |>= (rhs: A => A): A = rhs(self) @inline def |>=? (rhs: PartialFunction[A, A]): A = rhs.appOrElse(self)(_ => self) /** A lesser precedence one! */ @inline def /> [B] (rhs: A => B): B = rhs(self) @inline def matches(pf: PartialFunction[A, Bool]): Bool = pf.lift(self).contains(true) @inline def optionIf(cond: A => Bool): Option[A] = if (cond(self)) Some(self) else None @inline def optionUnless(cond: A => Bool): Option[A] = if (!cond(self)) Some(self) else None /** * A helper to write left-associative applications, mainly used to get rid of paren hell * Example: * println(Id(Sym(f(chars)))) * println(Id <|: Sym.apply <|: f <|: chars) // `Sym` needs `.apply` because it's overloaded */ @inline def <|: [B] (lhs: A => B): B = lhs(self) def withTypeOf[T >: A](x: T): T = self: T } implicit final class LazyGenHelper[A](self: => A) { @inline def optionIf(cond: Bool): Option[A] = if (cond) Some(self) else None @inline def optionUnless(cond: Bool): Option[A] = if (!cond) Some(self) else None } implicit final class ListHelpers[A](ls: Ls[A]) { def filterOutConsecutive(f: (A, A) => Bool = _ === _): Ls[A] = ls.foldRight[List[A]](Nil) { case (x, xs) => if (xs.isEmpty || !f(xs.head, x)) x :: xs else xs } def tailOption: Opt[Ls[A]] = if (ls.isEmpty) N else S(ls.tail) def headOr(els: => A): A = if (ls.isEmpty) els else ls.head def tailOr(els: => Ls[A]): Ls[A] = if (ls.isEmpty) els else ls.tail def mapHead(f: A => A): Ls[A] = ls match { case h :: t => f(h) :: t case Nil => Nil } } implicit final class OptionHelpers[A](opt: Opt[A]) { def dlof[B](f: A => B)(b: => B): B = opt.fold(b)(f) } implicit class MutSetHelpers[A](self: mutable.Set[A]) { def setAndIfUnset(x: A)(thunk: => Unit): Unit = { if (!self.contains(x)) { self += x thunk } } def setAnd[R](x: A)(ifSet: => R)(ifUnset: => R): R = { if (self.contains(x)) ifSet else { self += x ifUnset } } } // * The goal of these is to avoid the use of varargs, which I've found to be a source of // * overhead in the past, due to the allocation of intermediate arrays. // * Remains to be seen if using these is always (or ever?) necessarily a win. implicit class MutSetObjectHelpers(self: mutable.Set.type) { def single[A](a: A): mutable.Set[A] = mutable.Set.empty[A] += a } implicit class SetObjectHelpers(self: Set.type) { def single[A](a: A): Set[A] = (Set.newBuilder[A] += a).result() } implicit class SortedSetObjectHelpers(self: SortedSet.type) { def single[A: Ordering](a: A): SortedSet[A] = (SortedSet.newBuilder[A] += a).result() } implicit class MapObjectHelpers(self: Map.type) { def single[A, B](ab: A -> B): Map[A, B] = (Map.newBuilder[A, B] += ab).result() } implicit class SortedMapObjectHelpers(self: SortedMap.type) { def single[A: Ordering, B](ab: A -> B): SortedMap[A, B] = (SortedMap.newBuilder[A, B] += ab).result() } implicit class VectorObjectHelpers(self: Vector.type) { def single[A](a: A): Vector[A] = a +: Vector.empty def double[A](a: A, b: A): Vector[A] = a +: b +: Vector.empty def triple[A](a: A, b: A, c: A): Vector[A] = a +: b +: c +: Vector.empty } def TODO(msg: Any): Nothing = throw new NotImplementedError( msg.toString + s" (of class ${msg.getClass().getSimpleName()})") def TODO(msg: Any, cond: Bool): Unit = if (cond) TODO(msg) def die: Nothing = lastWords("Program reached an unexpected state.") def lastWords(msg: String): Nothing = throw new Exception(s"Internal Error: $msg") def wat(msg: String, obj: Any): Nothing = lastWords(s"$msg ($obj)") def wat(obj: Any): Nothing = wat(s"unexpected value", obj) /** To make Scala unexhaustivity warnings believed to be spurious go away, * while clearly indicating the intent. */ def spuriousWarning: Nothing = lastWords("Case was reached that was thought to be unreachable.") def checkless[A, B](pf: Paf[A, B]): A => B = pf private val _id: Any => Any = x => x def id[A]: A => A = _id.asInstanceOf[A => A] def closeOver[A](xs: Set[A])(f: A => Set[A]): Set[A] = closeOverCached(Set.empty, xs)(f) def closeOverCached[A](done: Set[A], todo: Set[A])(f: A => Set[A]): Set[A] = if (todo.isEmpty) done else { val newDone = done ++ todo closeOverCached(newDone, todo.flatMap(f) -- newDone)(f) } object EmptyColl { def unapply(it: Iterable[Any]): Bool = it.isEmpty } } ================================================ FILE: core/shared/main/scala/utils/shorthands.scala ================================================ package mlscript.utils import scala.annotation.showAsInfix import scala.util.chaining._ object shorthands { /** We have Int instead of Integer; why not Bool instead of Boolean? */ type Bool = Boolean /** Dotty syntax for intersection types */ @showAsInfix type & [+A,+B] = A with B type Ls[+A] = List[A] val Ls: List.type = List type NELs[+A] = ::[A] type Str = String type Ite[+A] = Iterator[A] val Ite: Iterator.type = Iterator type Opt[+A] = Option[A] val Opt: Option.type = Option type S[+A] = Some[A] val S: Some.type = Some val N: None.type = None def some[A]: A => Option[A] = Some(_) def none[A]: Option[A] = None def nil[A]: List[A] = Nil type Paf[-A,+B] = PartialFunction[A,B] type Exc = Exception type Err = Error type Ord[A] = Ordering[A] type Num[A] = Numeric[A] type \/[+A, +B] = Either[A, B] val \/ : Either.type = Either def left[A]: A => Left[A, Nothing] = Left(_) def right[B]: B => Right[Nothing, B] = Right(_) type L[+A] = Left[A, Nothing] val L: Left.type = Left type R[+B] = Right[Nothing, B] val R: Right.type = Right @showAsInfix type -> [+A,+B] = (A,B) object -> { def unapply[A, B](ab: (A, B)): Some[(A, B)] = Some(ab) } implicit class Tuple2Helper[A,B](private val self: (A,B)) extends AnyVal { @inline def mapFirst[C](f: A => C): (C,B) = (self._1 pipe f, self._2) @inline def mapSecond[C](f: B => C): (A,C) = (self._1, self._2 pipe f) } } ================================================ FILE: doc/Parsing.md ================================================ # Advanced MLscript Parser ## Indentation The parsing is indentation-sensitive, but it follows some very simple rules uniformly. Indentation is used only for two things: * to form indented blocks of code * to form single expressions spanning multiple line ### Indented blocks of code In general, you can understand an indented block of code as a syntax for nesting a list of `;`-separated statements inside an argument expression. An indented block of the form: ```ts some header expression aaa bbb ccc ddd eee fff ggg hhh iii ``` is equivalent to the form: ```ts some header expression / (aaa bbb ccc; ddd eee fff; ggg hhh iii) ``` where `/` is a right-associated expression separator with low precedence (whose goal is to avoid having to nest parentheses, similar to Haskell's `$`). So in particular, the following: ```ts foo x bar y baz z ``` is equivalent to: ```ts foo x / bar y / baz z ``` which is shorthand for: ```ts foo x (bar y (baz z)) ``` ### Single expressions spanning multiple line **Rule 1:** if a line finishes on an operator, then the following indented block is understood as an argument to that operator: ```ts some header expression + aaa bbb ccc ddd eee fff ggg hhh iii ``` is equivalent to: ```ts some header expression + (aaa bbb ccc; ddd eee fff; ggg hhh iii) ``` **Rule 2:** a header expression followed by a succession of indented lines all starting by an operator is understood as the application of the corresponding operators, in order (operator precedence is ignored), to the header expression: ```ts some header expression + aaa bbb ccc * ddd eee fff - ggg hhh iii ``` is equivalent to: ```ts (((some header expression) + (aaa bbb ccc)) * (ddd eee fff)) - (ggg hhh iii) ``` **Note:** In both of these rules, one can use `/` as an operator; so one can write: ```ts foo / arg 1 / arg 2 // i.e., (foo / arg 1) / arg 2 foo / bar / // this last `/` is not actually useful! arg 1 arg 2 // i.e., foo / bar / (arg 1; arg 2) ``` [TODO actually implement] **Note:** field accessors are considered operators (with tightest precedence): ```ts foo .bar 1 .baz 2 // i.e., (foo.bar 1).baz 2 ``` [TODO actually implement] ================================================ FILE: doc/mls-codebase-doc.md ================================================ # Documentation of the MLscript Codebase This is the documentation of the MLscript codebase. ## Overview This codebase of the MLscript Programming Language has all the basic components of a static-typed programming language compiler: lexer, parser, typer, and code generator. For testing, there is a web demo of MLscript as well as a test suite. We now give a high-level introduction to each compiler component and its correspondence to our Scala sources. Note that source file paths are rooted in `/shared/src/main/scala/mlscript`. ### Lexing The lexer accepts source strings and returns tokens to be parsed. The corresponding files are: - `NewLexer.scala` contains the lexer class. - `Token.scala` contains the token data types. ### Parsing The parser accepts tokens generated by the lexer and returns an abstract syntax tree of the input program in the surface syntax. The corresponding files are: - `NewParser.scala` contains the parser class. - `syntax.scala` contains the **surface** syntax data types of the language. ### Typing The typer accepts an abstract syntax tree of a program and performs type checking. MLscript's typer supports principal type inference with subtyping. For more information about the type system, please refer to [MLstruct](https://dl.acm.org/doi/abs/10.1145/3563304). The corresponding files are: - `Typer.scala` contains the typer class. - `TypeSimplifier.scala` contains type simplification algorithms to simplify inferred types. - `ucs/Desugarer.scala` contains class `ucs.Desugarer` which implements desugaring methods. - `TypeDefs.scala` and `NuTypeDefs.scala` contain class `TypeDef` and methods for declarations like classes, interfaces, and type aliases. - `ConstraitSolver.scala` contains class `ConstraintSolver` which solves subtyping constraints. - `NormalForms.scala` contains class `NormalForms` which provides the infrastructure to solve tricky subtyping constraints with disjunct normal forms (DNF) on the left and conjunct normal forms (CNF) on the right. - `TyperDatatypes.scala` contains class `TyperDatatypes` which includes data types to support **internal** representation of types with mutable states to support type inference with subtyping. - `TyperHelpers.scala` contains class `TyperHelpers` that provides helper methods for the typer. Note that the inheritance relationships between these typer classes do *not* have any actual semantics - we are following Scala's *Cake Pattern*. Typer classes will be finally composed into the `Typer` class by inheritance. ### Code Generation The code generator translates MLscript AST into JavaScript AST and generates the corresponding JavaScript code. The corresponding files are: - `codegen/Codegen.scala` contains definitions of JavaScript AST nodes and methods for JavaScript code generation. - `codegen/Scope.scala` contains class `Scope` which manages symbols and provides hygienic runtime name generation. - `codegen/Symbol.scala` contains classes `NewClassSymbol`, `MixinSymbol`, and `ModuleSymbol` which include information on `class`, `mixin` and `module` definitions. - `JSBackend.scala` contains class `JSBackend` that translates an MLscript AST into a JavaScript AST. Classes `JSWebBackend` and `JSTestBackend` inherit class `JSBackend` and generate adapted code for the web demo and the test suite. ### Web Demo and Testing Testing of MLscript works as follows: - the MLscript compiler reads the given test file one code block at a time (code blocks are separated by empty lines); - after reading the code block, it outputs the inferred types as well as any type errors encountered; - after that, it executes the code block in NodeJS (by shelling out to a `node` process) and outputs the results. We have a web demo for users to test our implementation in any modern browser. It has a textbox for MLscript source code input and it produces typing and running results live. The implementation can be tried online at https://hkust-taco.github.io/superoop/ and locally in `/js/src/main/scala/Main.scala`. We have a "`diff`-based" test suite for our implementation. It detects changes to MLscript test sources (using git), generates typing and running results, and inserts those results into test sources. The diff-based testing implementation is in `/shared/src/test/scala/mlscript/DiffTests.scala`. MLscript test sources are in `/shared/src/test/diff`. ## Detailed Introduction We now introduce the implementation of each compiler component in more detail. ### Lexing Class `NewLexer` in `NewLexer.scala` is the lexer class. It takes an `origin` object, which contains the original source string together with the source file name, the number of the first line, and some helper functions. Lazy value `tokens` generates a list of tokens with their location in the source code. Lazy value `bracketedTokens` converts the lexed tokens into *structured tokens*, which use `BRACKETS` constructs instead of `OPEN_BRACKET`/`CLOSE_BRACKET` and `INDENT`/`DEINDENT`. Token and structured token data types can be found in `Tokens.scala`. ### Parsing Class `NewParser` in `NewParser.scala` is the parser class. It takes a list of structured tokens with their location information. Method `typingUnit` calls method `block` to parse the token list into a list of `Statement` or `IfBody` (defined in `syntax.scala`), filters out unexpected `then/else` clauses introduced by `Ifbody`, and returns a `TypingUnit` (a list of `Statement`). File `syntax.scala` contains *immutable* surface syntax data types of MLscript, which are different from the internal representations in the typer for later type inference. Here we introduce several surface syntax data types: - Classes `Decl`, `TypeDef`, `MethodDef` are deprecated. - Class `TypeDefKind` includes type definition kinds: classes and mixins, etc. - Class `Term` includes MLscript term data types. Case class `Bind` is no longer used. Case class `Splc` is for the rest of a parameter list, similar to the rest parameter in JavaScript. Case classes `Forall` and `Inst` are for first-class polymorphism. - Class `IfBody` includes if-then-else structure data types. - Class `CaseBranches` includes case branch data types for MLscript pattern matching. - Class `TypeLike` includes `Type`, `Signature`, and `NuDecl`. - Class `Type` includes MLscript type data types. Case class `Rem` is for record member removal. Case class `WithExtension` is for record type extension. For example, `A with {x : int}` is equivalent to `A\x & {x : int}`. - Class `TypeVar` represents the type variable. Its identifier can be an `Int` generated internally by the compiler or `Str` specified by the user. - Class `NuTypeDef` is a `NuDecl` for type definitions. Note that it has optional `superAnnot` and `thisAnnot` for precisely-typed open recursion. - Class `NuFunDef` is a `NuDecl` for function and let-bindings. ### Typing The MLscript typer (class `Typer`) works with a typing context (class `Ctx`) which mainly maintains all global and local bindings of names to their types. The typer accepts a typing unit from the parser, types the typing unit, and returns a typed typing unit. The typed typing unit is sent to the type simplifier and is finally expanded, i.e., converted back to types in the surface syntax for presentation. The typer has **internal** representations of types (defined in `TyperDatatypes.scala`) with mutable states for type inference with subtyping. We first introduce several typer data types defined in `TyperDatatypes.scala`: - Class `TypeProvenance` stores the location where a type is introduced. - Class `LazyTypeInfo` is for type definitions including classes, mixins, modules. Its type is lazily computed to support *mutual recursive* type definitions. It has a `complete` method to complete typing lazily typed definitions. - Class `PolymorphicType` represents a type with universally quantified type variables. By convention, in the type body, type variables of levels greater than the polymorphic type's level are polymorphic. - Class `SimpleType` is a general type form of all types. It requires a method `level` for level-based polymorphism. - Class `BaseType` includes base types such as function, array, tuple, and class tag types. It can later be refined by `RecordType`. - Class `RecordType` is a record type. It has a list of bindings from record fields to their types. - Class `SpliceType` is not used for now. - Class `ProxyType` is a derived type form to store more type provenance information. - Class `TypeRef` is a reference to named types such as type definitions like classes. It has a list of type arguments. A type reference with type arguments is expanded to a class tag type with the class's type parameters refined by the corresponding type arguments as type members. For example, `Foo[Int]` is expanded to `#Foo & {Foo.A: int..int}`. - Class `TypeTag` has different kinds of type tags including class tags and abstract tags, etc. - Class `FieldType` represents a term field type or a type member. When it represents a term field type, `lb` represents if the type is mutable. Otherwise, `lb` is the lower bound of the type member. - Class `TypeVariable` represents a type variable, which has upper and lower bounds for type inference with subtyping. Method `typeTypingUnit` in class `NuTypeDefs` accepts the typing unit to type. It inspects each statement in the typing unit. If the statement is a type definition, a `DelayedTypeInfo` (which is a subclass of `LazyTypeInfo`) is produced and stored in the typing context (note the typing context only uses `tyDefs2` to store type definitions). Otherwise, it desugars the statement and calls `typeTerms` to type the desugared statements. For a single `Term`, it is passed to `typeTerm` to type. Method `typeTerm` in class `Typer` types a term. If the term needs type information of a `LazyTypeInfo`, the typing of that lazily typed definition will be completed. Subtyping constraints are generated during typing and sent to `ConstraintSolver` to propagate constraints to type variables. For more about type inference of subtyping, please refer to [MLstruct](https://dl.acm.org/doi/abs/10.1145/3563304). Of particular interest, we introduce how classes and mixins are typed to implement precisely-typed open recursion in more detail. Method `complete` of `DelayedTypeInfoImpl`, types type definitions: classes, modules, and mixins and let-/fun-bindings. When a class (`Cls` which is a `NuTypeDef`) is typed, class fields are first added into the typing context, and `this` is associated with a fresh type variable. The `inherit` helper methods deal with the inheritance clauses of the type definitions. The inheritance process starts with an empty record type as the initial `super` type. It inspects each parent, accumulates members of parents, and updates the `super` type on the way. For each parent, if it is a mixin, and the typing context has that mixin defined, it completes the type of the mixin and freshens each type variable of the mixin, as each mixin's type should be constrained differently at different use-sites. Then, two subtyping constraints are generated: the current `super` type and the final object type (`this` type) should be subtypes of the mixin's `super` and `this` type refinements. Finally, the mixin's members are accumulated to the class, and the current `super` type is updated using `WithType` because methods in mixins are always *overriding*. After processing the whole inheritance clause, we update the current `super` type with the class fields' types as `thisType`, and we constrain that the resulting `thisType` (i.e. the final object type) should be a subtype of `finalType` which is a type variable with all `this` type refinements of mixins accumulated. Typing of mixins is not that surprising. We associate `this` and `super` with fresh type variables in the typing context and then type the mixin body. ### Code Generation The code generation consists of three steps. Firstly, class `JSBackend` translates MLscript data types (i.e. class `NuTypeDef`) into corresponding symbols. Then class `JSBackend` generates JavaScript AST nodes based on those symbols. Finally, we generate JavaScript code from JavaScript AST nodes. The first step is implemented in the method `declareNewTypeDefs`. Here we extract information (including name, parameter list, type, members, parents, and so on) of classes, mixins, and modules from the given `NuTypeDef` list and generate a hygienic runtime name for each symbol. In the second step, we translate `NewClassSymbol`, `MixinSymbol`, and `ModuleSymbol` into JavaScript AST nodes by using methods `translateNewClassDeclaration`, `translateMixinDeclaration`, and `translateModuleDeclaration`. These three methods invoke another method `translateNewTypeDefinition` to translate classes, mixins, and modules into JavaScript classes. The method `translateNewClassMember` contains the translation of members. We call `translateParents` to get the parent class of a type. Assuming we have code: ```ts module A extends B, C ``` The method `translateParents` processes the inheritance clause in a left-to-right way: - First, we process the parent `B`: - If `B` is a `class` or a `module`, the JS class definition would be `class A extends B`. - If `B` is a `mixin`, we need a base class for `B`. Here we choose `Object` in JavaScript and the JS class definition would be `class A extends B(Object)` - Then we process the parent `C`: - If `C` is a `mixin`, we can use `B(Object)` as `C`'s base class. The JS class definition would be `class A extends C(B(Object))`. - Otherwise, we reject the code because a JavaScript class can have only one parent class. - If module `A` has more parents on the right of `C`, we process them similarly as we deal with `C`. If there are initialization parameters in the parent list, we move the arguments into the class constructor and pass them to `super()`. Note we need to reverse the order of arguments of `mixin`. For example, assume we have MLscript code below: ```ts module A extends MixinA(1), MixinB(2, 3), MixinC(4) ``` The parameters in `super()` of `A` would be: ```js super(4, 2, 3, 1); ``` We generate the JavaScript classes inside `typing_unit` objects. Note we create `...rest` parameters in each constructor of `mixin` because we have no information about the actual parent mixin until the mixin composition is finished. For modules, we store the instance of the JavaScript class in the cache. For classes, if they have primitive parameter lists, we store the arrow functions in the cache as class constructors that instantiate classes. Mixins have no constructor because of the uncertainty of the `base` parameter of mixins. In the final step, we emit the JavaScript code by using `toSourceCode` methods in each JavaScript AST node class. For a class in MLscript: ```ts class Lit(n: int) ``` The generated code would be: ```js class TypingUnit { #Lit; constructor() { } get Lit() { const qualifier = this; if (this.#Lit === undefined) { class Lit { #n; constructor(n) { this.#n = n; } static unapply(x) { return [x.#n]; } }; this.#Lit = ((n) => Object.freeze(new Lit(n))); this.#Lit.class = Lit; this.#Lit.unapply = Lit.unapply; } return this.#Lit; } } const typing_unit = new TypingUnit; globalThis.Lit = typing_unit.Lit; ``` For a mixin in MLscript: ```ts mixin EvalBase { fun eval(e) = if e is Lit(n) then n: int } ``` The generated code would be: ```js class TypingUnit { constructor() { } EvalBase(base) { const qualifier = this; return (class EvalBase extends base { constructor(...rest) { super(...rest); } eval(e) { return ((() => { let a; return (a = e, a instanceof Lit.class ? (([n]) => n)(Lit.unapply(e)) : (() => { throw new Error("non-exhaustive case expression"); })()); })()); } }); } } const typing_unit = new TypingUnit; globalThis.EvalBase = ((base) => typing_unit.EvalBase(base)); ``` For a module in MLscript: ```ts module TestLang extends EvalBase, EvalNeg, EvalNegNeg ``` The generated code would be like this: ```js class TypingUnit { #TestLang; constructor() { } get TestLang() { const qualifier = this; if (this.#TestLang === undefined) { class TestLang extends EvalNegNeg(EvalNeg(EvalBase(Object))) { constructor() { super(); } } this.#TestLang = new TestLang(); this.#TestLang.class = TestLang; } return this.#TestLang; } } const typing_unit = new TypingUnit; globalThis.TestLang = typing_unit.TestLang; ``` For more code generation examples, please check the test source `shared/src/test/diff/codegen/Mixin.mls`. ================================================ FILE: doc/tuple-patterns.md ================================================ # Tuple Patterns ``` b is refined D and let y = b.y ... // pattern with fixed length b is Array { length: 3 } b is [_, _, _] // let bindings single field selector and range selector let z = b.z let w = b[1..] let w = b[..-1] ``` ================================================ FILE: flake.nix ================================================ { description = "mlscript"; inputs.nixpkgs.url = "github:NixOS/nixpkgs"; inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.sbt-deriv.url = "github:zaninime/sbt-derivation"; inputs.sbt-deriv.inputs.nixpkgs.follows = "nixpkgs"; outputs = { self, nixpkgs, flake-utils, sbt-deriv }: flake-utils.lib.eachDefaultSystem (system: let sbtOverlay = self: super: { sbt = super.sbt.override { jre = super.jdk17_headless; }; }; pkgs = import nixpkgs { inherit system; overlays = [ sbtOverlay ]; }; in with pkgs; { devShells.default = mkShell { buildInputs = [ clang gcc gnumake boost gmp mimalloc sbt nodejs_24 ]; }; }); } ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/Compiler.scala ================================================ package hkmc2 import scala.util.Try import scala.scalajs.js.annotation.* import org.scalajs.dom import org.scalajs.dom.document import mlscript.utils._ import mlscript.utils.shorthands._ import scala.util.matching.Regex import scala.scalajs.js, js.JSConverters.* import scala.collection.immutable import scala.collection.mutable.Map as MutMap import io.* import scala.collection.mutable.{ArrayBuffer, Buffer} @JSExportTopLevel("Compiler") class Compiler(paths: MLsCompiler.Paths)(using cctx: CompilerCtx): private given Config = Config.default(io.Path("/")) private var pathDiagnosticsMap = MutMap.empty[Str, (Int, Buffer[Diagnostic])] private def mkRaise(path: io.Path): Raise = d => pathDiagnosticsMap.getOrElseUpdate(path.toString, (pathDiagnosticsMap.size, Buffer.empty))._2 += d private val compiler = MLsCompiler(paths, mkRaise) private def collectDiagnostics(): js.Array[js.Dynamic] = pathDiagnosticsMap.toArray.sortBy(_._2._1).map: case (path, (_, diagnostics)) => js.Dynamic.literal( path = path, diagnostics = diagnostics.iterator.map: d => js.Dynamic.literal( kind = d.kind.toString().toLowerCase(), source = d.source.toString().toLowerCase(), mainMessage = d.theMsg, allMessages = d.allMsgs.iterator.map: case (message, loc) => lazy val ctx = ShowCtx.mk: message.bits.collect: case Message.Code(t) => t js.Dynamic.literal( messageBits = message.bits.map: case Message.Text(text) => js.Dynamic.literal(text = text) case Message.Code(ty) => ty.showIn(0)(using ctx) .toJSArray, location = loc match case S(loc) => js.Dynamic.literal( start = loc.spanStart, end = loc.spanEnd ) case N => null ) .toJSArray ) .toJSArray) .toJSArray @JSExport def compile(filePath: Str): js.Array[js.Dynamic] = compiler.compileModule(Path(filePath)) val perFileDiagnostics = collectDiagnostics() pathDiagnosticsMap = MutMap.empty perFileDiagnostics @JSExportTopLevel("Paths") final class Paths(prelude: Str, runtime: Str, term: Str) extends MLsCompiler.Paths: val preludeFile = Path(prelude) val runtimeFile = Path(runtime) val termFile = Path(term) ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/InMemoryFileSystem.scala ================================================ package hkmc2.io import mlscript.utils._, shorthands._ import collection.mutable.Map as MutMap import scala.scalajs.js, js.annotation.JSExport, js.JSConverters.* import scala.scalajs.js.annotation.JSExportTopLevel /** * In-memory file system for testing and web compiler. Stores files as a map * from path strings to content strings. Note that separators are not normalized. */ class InMemoryFileSystem(initialFiles: Map[String, String]) extends FileSystem: // We assume that all paths are normalized here. private val files: MutMap[String, (Int, String)] = MutMap.from: initialFiles.map { case (k, v) => (k, (0, v)) } def read(path: Path): String = read(path.toString) def write(path: Path, content: String): Unit = write(path.toString, content) def exists(path: Path): Bool = files.contains(path.toString) def getLastChangedTimestamp(path: Path): Long = files.get(path.toString) match case Some((ts, _)) => ts.toLong case None => throw new FileSystem.FileNotFoundException(path) @JSExport("write") def write(path: Str, content: Str): Unit = files.updateWith(path): case Some((ts, _)) => Some((ts + 1, content)) case None => Some((0, content)) @JSExport("read") def read(path: Str): Str = files.getOrElse(path, throw new FileSystem.FileNotFoundException(Path(path)))._2 @JSExport("list") def list: js.Array[Str] = allFiles.keys.toJSArray /** Get all files (for debugging) */ def allFiles: Map[String, String] = files.view.mapValues(_._2).toMap object InMemoryFileSystem: /** Create an empty in-memory file system. */ @JSExportTopLevel("InMemoryFileSystem") def apply(files: js.Array[js.Tuple2[Str, Str]]): InMemoryFileSystem = new InMemoryFileSystem(files.map(t => t._1 -> t._2).toMap) ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/PlatformFileSystem.scala ================================================ package hkmc2 package io private[io] object PlatformFileSystem: def default: FileSystem = new node.NodeFileSystem ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/PlatformPath.scala ================================================ package hkmc2 package io /** * Platform-specific factory for creating Path instances */ private[io] object PathFactory: def fromString(str: String) = new VirtualPath(str) def separator: String = VirtualPath.sep def relPathFromString(str: String) = new VirtualRelPath(str) def relPathUp = new VirtualRelPath("..") ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/VirtualPath.scala ================================================ package hkmc2.io import scala.scalajs.js import mlscript.utils._, shorthands._ import VirtualPath.sep /** * Pure JavaScript implementation of Path without using Node.js path module */ private[io] case class VirtualPath(val pathString: String) extends Path: private def normalizePath(path: String): String = if path.isEmpty then path else // Split by separator and filter out empty segments val segments = path.split(sep).filter(_.nonEmpty) val isAbs = path.startsWith(sep) // Resolve . and .. segments val normalized = segments.foldLeft(List.empty[String]): (acc, seg) => seg match case "." => acc // Current directory, skip it case ".." => // Parent directory, pop the last segment if possible if acc.isEmpty || acc.last == ".." then acc :+ seg else acc.dropRight(1) case _ => acc :+ seg if isAbs then sep + normalized.mkString(sep) else if normalized.isEmpty then "." else normalized.mkString(sep) override def toString: String = pathString def last: String = val idx = pathString.lastIndexOf(sep) if idx < 0 then pathString else pathString.substring(idx + 1) def baseName: String = val filename = last val dotIdx = filename.lastIndexOf('.') if dotIdx <= 0 then filename // .hidden files or no extension else filename.substring(0, dotIdx) def ext: String = val filename = last val dotIdx = filename.lastIndexOf('.') if dotIdx <= 0 then "" // .hidden files or no extension else filename.substring(dotIdx + 1) def up: Path = val idx = pathString.lastIndexOf(sep) if idx < 0 then new VirtualPath(".") else if idx == 0 then new VirtualPath(sep) // root case else new VirtualPath(pathString.substring(0, idx)) def /(relPath: RelPath): Path = val combined = if pathString.endsWith(sep) then pathString + relPath.toString else pathString + sep + relPath.toString new VirtualPath(normalizePath(combined)) def /(fragment: String): Path = val combined = if pathString.endsWith(sep) then pathString + fragment else pathString + sep + fragment new VirtualPath(normalizePath(combined)) def relativeTo(base: Path): Opt[RelPath] = try val baseSegs = base.segments val targetSegs = segments // Find common prefix var i = 0 while i < baseSegs.length && i < targetSegs.length && baseSegs(i) == targetSegs(i) do i += 1 // Build relative path val upCount = baseSegs.length - i val ups = List.fill(upCount)("..") val downs = targetSegs.drop(i) val relSegs = ups ++ downs if relSegs.isEmpty then S(new VirtualRelPath(".")) else S(new VirtualRelPath(relSegs.mkString(sep))) catch case _: Exception => N def segments: Ls[String] = pathString.split(sep).toList.filter(_.nonEmpty) def isAbsolute: Bool = pathString.startsWith(sep) private[io] object VirtualPath: val sep = "/" /** * Pure JavaScript implementation of RelPath without using Node.js path module */ private[io] class VirtualRelPath(val pathString: String) extends RelPath: override def toString: String = pathString def segments: Ls[String] = pathString.split(sep).toList.filter(_.nonEmpty) def /(other: RelPath): RelPath = val combined = if pathString.endsWith(sep) then pathString + other.toString else pathString + sep + other.toString new VirtualRelPath(combined) ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/node/NodeFileSystem.scala ================================================ package hkmc2 package io package node import scala.scalajs.js import scala.scalajs.js.annotation._ import mlscript.utils._, shorthands._ /** * JavaScript implementation of [[FileSystem]] using Node.js fs module. */ private[io] class NodeFileSystem extends FileSystem: def read(path: Path): String = fs.readFileSync(path.toString, "utf8") def write(path: Path, content: String): Unit = fs.writeFileSync(path.toString, content) def exists(path: Path): Bool = fs.existsSync(path.toString) def getLastChangedTimestamp(path: Path): Long = fs.lstatSync(path.toString).mtime.getTime().toLong ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/node/NodePath.scala ================================================ package hkmc2 package io package node import scala.scalajs.js import scala.scalajs.js.annotation._ import mlscript.utils._, shorthands._ /** * JavaScript implementation of Path using Node.js path module */ private[io] case class NodePath(val pathString: String) extends Path: private lazy val parsed = path.parse(pathString) override def toString: String = pathString def last: String = parsed.base def baseName: String = parsed.name def ext: String = if parsed.ext.startsWith(".") then parsed.ext.substring(1) else parsed.ext def up: Path = new NodePath(path.dirname(pathString)) def /(relPath: RelPath): Path = new NodePath(path.join(pathString, relPath.toString)) def /(fragment: String): Path = new NodePath(pathString + path.sep + fragment) def relativeTo(base: Path): Opt[RelPath] = try S(new NodeRelPath(path.relative(base.toString, pathString))) catch case _: Exception => N def segments: Ls[String] = pathString.split(path.sep).toList.filter(_.nonEmpty) def isAbsolute: Bool = path.isAbsolute(pathString) /** * JavaScript implementation of RelPath using Node.js path module */ private[io] class NodeRelPath(val pathString: String) extends RelPath: override def toString: String = pathString def segments: Ls[String] = pathString.split(path.sep).toList.filter(_.nonEmpty) def /(other: RelPath): RelPath = new NodeRelPath(path.join(pathString, other.toString)) ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/io/node/package.scala ================================================ package hkmc2 package io import scala.scalajs.js import scala.scalajs.js.annotation._ import mlscript.utils._, shorthands._ /** * The package object contains facades for Node.js modules. So, it can be used * conveniently in */ package object node: /** * Node.js fs module facade. */ @js.native @JSImport("fs", JSImport.Namespace) object fs extends js.Object: def readFileSync(path: Str, encoding: Str): Str = js.native def writeFileSync(path: Str, data: Str): Unit = js.native def readdirSync(path: Str): js.Array[Str] = js.native def existsSync(path: Str): Bool = js.native def lstatSync(path: Str): Stats = js.native /** * The facade for [[fs.Stats]]. Only a few useful members are listed here. * Refer to https://nodejs.org/api/fs.html#class-fsstats for the full list * of methods. */ @js.native trait Stats extends js.Object: def isDirectory(): Bool = js.native def isFile(): Bool = js.native def isSymbolicLink(): Bool = js.native /** The size of the file in bytes. */ val size: Long = js.native /** The timestamp indicating the last time this file was accessed. */ def atime: js.Date = js.native /** The timestamp indicating the last time this file was modified. */ def mtime: js.Date = js.native /** The timestamp indicating the last time the file status was changed. */ def ctime: js.Date = js.native /** The timestamp indicating the creation time of this file. */ def birthtime: js.Date = js.native @js.native trait ParsedPath extends js.Object: val base: Str = js.native val name: Str = js.native val ext: Str = js.native /** * Node.js path module facade. */ @js.native @JSImport("path", JSImport.Namespace) object path extends js.Object: def sep: Str = js.native def parse(path: Str): ParsedPath = js.native def relative(from: Str, to: Str): Str = js.native def join(paths: Str*): Str = js.native def isAbsolute(path: Str): Bool = js.native def dirname(path: Str): Str = js.native /** * Node.js process module facade. */ @js.native @JSGlobal("process") object process extends js.Object: def cwd(): Str = js.native ================================================ FILE: hkmc2/js/src/main/scala/hkmc2/utils/PlatformCompilerCache.scala ================================================ package hkmc2 package utils import CompilerCache.Artifact import collection.mutable.Map as MutMap import mlscript.utils.*, shorthands.* class PlatformCompilerCache extends CompilerCache: val elabCache: MutMap[io.Path, Artifact] = MutMap.empty ================================================ FILE: hkmc2/js/src/test/scala/hkmc2/CompilerTest.scala ================================================ package hkmc2 import org.scalatest.funsuite.AnyFunSuite import io.{InMemoryFileSystem, Path, node} import mlscript.utils._, shorthands._ import scala.scalajs.js import scala.scalajs.js.annotation._ import scala.scalajs.js.Dynamic.global class CompilerTest extends AnyFunSuite: private def loadStandardLibrary(): Map[String, String] = val projectRoot = node.process.cwd() val compilePath = node.path.join(projectRoot, "hkmc2", "shared", "src", "test", "mlscript-compile") val preludePath = node.path.join(projectRoot, "hkmc2", "shared", "src", "test", "mlscript", "decls", "Prelude.mls") node.fs.readdirSync(compilePath).filter(_.endsWith(".mls")).toSeq.flatMap: fileName => val filePath = node.path.join(compilePath, fileName) if node.fs.existsSync(filePath) then Some(s"/std/$fileName" -> node.fs.readFileSync(filePath, "utf-8")) else None .toMap + ("/std/Prelude.mls" -> node.fs.readFileSync(preludePath, "utf-8")) private val paths = new Paths("/std/Prelude.mls", "/std/Runtime.mjs", "/std/Term.mjs") private def createCompiler(): (InMemoryFileSystem, Compiler) = val stdLib = loadStandardLibrary() val fs = new InMemoryFileSystem(stdLib) given CompilerCtx = CompilerCtx.fresh(fs) (fs, new Compiler(paths)) test("compiler can compile a simple program"): val (fs, compiler) = createCompiler() // Write test program to the file system val code = """|import "./std/Option.mls" |import "./std/Stack.mls" |import "./std/Predef.mls" | |open Stack |open Option |open Predef | |fun findFirst(xs, f) = if xs is | Nil then None | Cons(x, xs') and | f(x) then Some(x) | else findFirst(xs', f) | |let nums = 1 :: 2 :: 3 :: 4 :: 5 :: Nil |let result = nums \findFirst of x => x * 6 is 24 |""".stripMargin val inputPath = "/findFirstTest.mls" val outputPath = "/findFirstTest.mjs" fs.write(inputPath, code) val diagnostics = compiler.compile(inputPath) val hasErrors = diagnostics.exists: perFile => val fileDiagnostics = perFile.diagnostics.asInstanceOf[scala.scalajs.js.Array[scala.scalajs.js.Dynamic]] fileDiagnostics.exists(_.kind is "error") assert(!hasErrors, "Compilation should succeed without errors") val outputExists = fs.exists(Path(outputPath)) assert(outputExists, "Output JavaScript file should be generated") val output = fs.read(outputPath) assert(output.contains("findFirst"), "Output should contain the findFirst function") test("compiler can compile two files that import each other"): val (fs, compiler) = createCompiler() val foo = """|import "./std/Predef.mls" | |open Predef | |module Foo with... | |fun sayHello() = print of "Hello, world!" |""".stripMargin fs.write("/Foo.mls", foo) val bar = """|import "./Foo.mls" | |Foo.sayHello() |""".stripMargin fs.write("/Bar.mls", bar) val diag1 = compiler.compile("/Foo.mls") val diag2 = compiler.compile("/Bar.mls") assert(fs.exists(Path("/Foo.mjs")), "First output should exist") assert(fs.exists(Path("/Bar.mjs")), "Second output should exist") test("compiler can report errors"): val (fs, compiler) = createCompiler() fs.write("/test.mls", """fun f(x) = x + y""") // `y` is not defined. val diagnostics = compiler.compile("/test.mls") assert(diagnostics.length is 1, "Should report diagnostics for only one file") val hasErrors = diagnostics.exists: perFile => val fileDiagnostics = perFile.diagnostics.asInstanceOf[scala.scalajs.js.Array[scala.scalajs.js.Dynamic]] fileDiagnostics.exists(_.kind is "error") assert(hasErrors, "Compilation should report errors") ================================================ FILE: hkmc2/js/src/test/scala/hkmc2/io/VirtualPathTests.scala ================================================ package hkmc2.io import org.scalatest.funsuite.AnyFunSuite import mlscript.utils._, shorthands._ class VirtualPathTests extends AnyFunSuite: test("basic path creation and toString"): val path = VirtualPath("foo/bar") assert(path.toString == "foo/bar") test("/ operator with simple fragment"): val path = VirtualPath("foo") val result = path / "bar" assert(result.toString == "foo/bar") test("/ operator with fragment starting with ."): val path = VirtualPath("foo") val result = path / "./bar" assert(result.toString == "foo/bar", "Current directory '.' should be removed") test("/ operator with fragment starting with ./"): val path = VirtualPath("foo/baz") val result = path / "./bar" assert(result.toString == "foo/baz/bar", "Current directory '.' should be removed") test("/ operator with fragment containing .."): val path = VirtualPath("foo/baz") val result = path / "../bar" assert(result.toString == "foo/bar", "Parent directory '..' should navigate up one level") test("/ operator with multiple .. segments"): val path = VirtualPath("a/b/c") val result = path / "../../d" assert(result.toString == "a/d", "Multiple '..' should navigate up multiple levels") test("/ operator with . in the middle of path"): val path = VirtualPath("foo") val result = path / "bar/./baz" assert(result.toString == "foo/bar/baz", "Current directory '.' in middle should be removed") test("/ operator with .. in the middle of path"): val path = VirtualPath("foo") val result = path / "bar/../baz" assert(result.toString == "foo/baz", "Parent directory '..' in middle should collapse segments") test("/ operator with absolute path"): val path = VirtualPath("/abs/path") val result = path / "./file.txt" assert(result.toString == "/abs/path/file.txt", "Should work with absolute paths") test("/ operator with RelPath containing ."): val path = VirtualPath("foo") val relPath = VirtualRelPath("./bar") val result = path / relPath assert(result.toString == "foo/bar", "RelPath with '.' should be normalized") test("/ operator with RelPath containing .."): val path = VirtualPath("foo/baz") val relPath = VirtualRelPath("../bar") val result = path / relPath assert(result.toString == "foo/bar", "RelPath with '..' should be normalized") test("/ operator with RelPath containing multiple . and .."): val path = VirtualPath("a/b") val relPath = VirtualRelPath("./c/../d") val result = path / relPath assert(result.toString == "a/b/d", "Complex RelPath should be normalized") test("normalization with too many .. segments"): val path = VirtualPath("foo") val result = path / "../../bar" assert(result.toString == "../bar", "Extra '..' should be preserved for relative paths") test("normalization resulting in just ."): val path = VirtualPath("foo") val result = path / ".." assert(result.toString == ".", "Navigating up from single segment should result in '.'") test("/ operator with trailing slash"): val path = VirtualPath("foo/") val result = path / "bar" assert(result.toString == "foo/bar", "Should handle trailing slash correctly") test("path segments"): val path = VirtualPath("foo/bar/baz") assert(path.segments == List("foo", "bar", "baz")) test("last segment"): val path = VirtualPath("foo/bar/baz.txt") assert(path.last == "baz.txt") test("baseName"): val path = VirtualPath("foo/bar/baz.txt") assert(path.baseName == "baz") test("ext"): val path = VirtualPath("foo/bar/baz.txt") assert(path.ext == "txt") test("up"): val path = VirtualPath("foo/bar/baz") assert(path.up.toString == "foo/bar") test("isAbsolute - relative path"): val path = VirtualPath("foo/bar") assert(!path.isAbsolute) test("isAbsolute - absolute path"): val path = VirtualPath("/foo/bar") assert(path.isAbsolute) test("complex normalization case"): val path = VirtualPath("a/b/c") val result = path / "./d/../e/./f" assert(result.toString == "a/b/c/e/f", "Complex path with mixed . and .. should normalize correctly") test("normalization with only ."): val path = VirtualPath("foo") val result = path / "." assert(result.toString == "foo", "Single '.' should result in same path") test("normalization preserves absolute paths"): val path = VirtualPath("/a/b") val result = path / "../c" assert(result.toString == "/a/c", "Absolute paths should remain absolute after normalization") test("RelPath / operator"): val rel1 = VirtualRelPath("foo/bar") val rel2 = VirtualRelPath("baz") val result = rel1 / rel2 assert(result.toString == "foo/bar/baz") ================================================ FILE: hkmc2/jvm/src/main/scala/hkmc2/io/PlatformFileSystem.scala ================================================ package hkmc2 package io import mlscript.utils._, shorthands._ private[io] class JavaFileSystem extends FileSystem: def read(path: Path): String = os.read(unwrap(path)) def write(path: Path, content: String): Unit = os.write.over(unwrap(path), content) def exists(path: Path): Bool = os.exists(unwrap(path)) def getLastChangedTimestamp(path: Path): Long = os.mtime(unwrap(path)) private def unwrap(path: Path): os.Path = path match case path: WrappedPath => path.underlying case _ => lastWords(s"The given path is not compatible with the current platform (JVM).") object PlatformFileSystem: val default: FileSystem = new JavaFileSystem ================================================ FILE: hkmc2/jvm/src/main/scala/hkmc2/io/PlatformPath.scala ================================================ package hkmc2 package io import mlscript.utils._, shorthands._ /** * JVM implementation of [[Path]] that wraps [[os.Path]]. */ private[io] case class WrappedPath(private[io] val underlying: os.Path) extends Path: override def toString: String = underlying.toString def last: String = underlying.last def baseName: String = underlying.baseName def ext: String = underlying.ext def up: Path = new WrappedPath(underlying / os.up) def /(relPath: RelPath): Path = new WrappedPath(underlying / relPath.asInstanceOf[WrappedRelPath].underlying) def /(fragment: Str): Path = new WrappedPath(underlying / fragment) def relativeTo(base: Path): Opt[RelPath] = try S(new WrappedRelPath(underlying.relativeTo(base.asInstanceOf[WrappedPath].underlying))) catch case _: Exception => N def segments: Ls[String] = underlying.segments.toList def isAbsolute: Bool = underlying.startsWith(os.root) /** * JVM implementation of [[RelPath]] that wraps [[os.RelPath]]. */ private[io] class WrappedRelPath(private[io] val underlying: os.RelPath) extends RelPath: override def toString: String = underlying.toString def segments: Ls[String] = underlying.segments.toList def /(other: RelPath): RelPath = new WrappedRelPath(underlying / other.asInstanceOf[WrappedRelPath].underlying) /** * Platform-specific factory for creating Path instances */ private[io] object PathFactory: def fromString(str: String) = new WrappedPath(os.Path(str)) def separator: String = "/" def relPathFromString(str: String) = new WrappedRelPath(os.RelPath(str)) def relPathUp = new WrappedRelPath(os.up) /** * JVM-specific utilities for Path conversion */ object PlatformPath: /** Convert os.Path to io.Path */ def fromOsPath(osPath: os.Path): Path = new WrappedPath(osPath) /** Convert os.RelPath to io.RelPath */ def fromOsRelPath(osRelPath: os.RelPath): RelPath = new WrappedRelPath(osRelPath) /** Implicit conversion from os.Path to io.Path (import to use) */ given Conversion[os.Path, Path] = fromOsPath /** Implicit conversion from os.RelPath to io.RelPath (import to use) */ given Conversion[os.RelPath, RelPath] = fromOsRelPath ================================================ FILE: hkmc2/jvm/src/main/scala/hkmc2/utils/PlatformCompilerCache.scala ================================================ package hkmc2 package utils import CompilerCache.Artifact import collection.concurrent.{Map => ConcMap, TrieMap} import mlscript.utils.*, shorthands.* class PlatformCompilerCache extends CompilerCache: val elabCache: ConcMap[io.Path, Artifact] = TrieMap.empty ================================================ FILE: hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala ================================================ package hkmc2 import mlscript.utils._, shorthands._ import io.PlatformPath.given class CompileTestRunner extends CompileTestRunnerBase( compileDirs = TestFolders.mainCompileDirs(os.pwd), excludedDirs = TestFolders.mainExcludedCompileDirs(os.pwd), ): protected def cctx: CompilerCtx = CompileTestRunner.cctx end CompileTestRunner object CompileTestRunner: given cctx: CompilerCtx = CompilerCtx.fresh(io.FileSystem.default) end CompileTestRunner ================================================ FILE: hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunnerBase.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import org.scalatest.concurrent.{TimeLimitedTests, Signaler} import mlscript.utils._, shorthands._ import io.PlatformPath.given // Reusable base class for compile test runners. Subclasses provide the // directories to scan and the compiler context to use, and this base class // handles walking directories, filtering for mlscript-compile files, // and registering ScalaTest tests. abstract class CompileTestRunnerBase( compileDirs: Ls[os.Path], excludedDirs: Ls[os.Path] = Nil, ) extends funsuite.AnyFunSuite with ParallelTestExecution : given CompilerCtx = cctx protected def cctx: CompilerCtx private val inParallel = isInstanceOf[ParallelTestExecution] val workingDir: os.Path = os.pwd val mainTestDir: os.Path = TestFolders.mainTestDir(workingDir) val validExt = Set("mls") for dir <- compileDirs do { val allFiles = os.walk(dir) .filter(_.toIO.isFile) .filter(_.ext in validExt) lazy val compileTestFiles = allFiles.filter: file => file.segments.contains("mlscript-compile") && !TestFolders.isExcluded(file, excludedDirs) compileTestFiles.foreach: file => val basePath = file.segments.drop(dir.segmentCount).toList.init val relativeName = basePath.map(_ + "/").mkString + file.baseName test(relativeName): this.synchronized: println(s"Compiling: $relativeName") // * Stack safety relies on the fact that runtime uses while loops for resumption // * and does not create extra stack depth. Hence, while loop rewriting should be disabled here. // * (It used to be on by default, but now is off by default, so nothing to do.) given Config = Config.default(mainTestDir) // Synchronize diagnostic output to avoid interleaving since the compiler tests run in parallel. val wrap: (=> Unit) => Unit = body => this.synchronized(body) val report = ReportFormatter(System.out.println, colorize = true, wrap = Some(wrap)) val compiler = MLsCompiler( paths = new MLsCompiler.Paths: val preludeFile = mainTestDir / "mlscript" / "decls" / "Prelude.mls" val runtimeFile = mainTestDir / "mlscript-compile" / "Runtime.mjs" val termFile = mainTestDir / "mlscript-compile" / "Term.mjs", mkRaise = report.mkRaise ) compiler.compileModule(file) if report.badLines.nonEmpty then fail(s"Unexpected diagnostic at: " + report.badLines.distinct.sorted .map("\n\t"+relativeName+"."+file.ext+":"+_).mkString(", ")) } end CompileTestRunnerBase ================================================ FILE: hkmc2/jvm/src/test/scala/hkmc2/TestFolders.scala ================================================ package hkmc2 import mlscript.utils._, shorthands._ /** Centralized definitions of which test directories belong to which SBT project. * Each project specifies the `mlscript/` subdirectories (for diff tests) and the * `mlscript-compile/` subdirectories (for compile tests) that it owns. */ object TestFolders: /** Helper to check whether `file` is inside (i.e. a descendant of) `dir`. */ def isInDir(file: os.Path, dir: os.Path): Bool = file.startsWith(dir) /** The base test directory: `hkmc2/shared/src/test`. */ def mainTestDir(wd: os.Path): os.Path = wd/"hkmc2"/"shared"/"src"/"test" /** The diff test directory: `hkmc2/shared/src/test/mlscript`. */ def diffTestDir(wd: os.Path): os.Path = mainTestDir(wd)/"mlscript" /** The compile test directory: `hkmc2/shared/src/test/mlscript-compile`. */ def compileTestDir(wd: os.Path): os.Path = mainTestDir(wd)/"mlscript-compile" // ——— Diff test subdirectories excluded from the main DiffTestRunner ——— /** Diff test subdirectories that belong to the hkmc2NofibTests project. */ def nofibDiffDir(wd: os.Path): os.Path = diffTestDir(wd)/"nofib" /** Diff test subdirectories that belong to the hkmc2AppsTests project. */ def appsDiffDir(wd: os.Path): os.Path = diffTestDir(wd)/"apps" /** Diff test subdirectories that belong to the hkmc2WasmTests project. */ def wasmDiffDir(wd: os.Path): os.Path = diffTestDir(wd)/"wasm" /** Diff test directories that are always excluded (staging, mlscript-compile). */ def alwaysExcludedDiffDirs(wd: os.Path): Ls[os.Path] = (diffTestDir(wd)/"ucs"/"staging") :: compileTestDir(wd) :: Nil /** All diff test directories excluded from the main DiffTestRunner. */ def mainExcludedDiffDirs(wd: os.Path): Ls[os.Path] = nofibDiffDir(wd) :: appsDiffDir(wd) :: wasmDiffDir(wd) :: alwaysExcludedDiffDirs(wd) /** Check whether a file should be excluded from the given list of excluded * directories and/or individual files. */ def isExcluded(file: os.Path, excludedDirs: Ls[os.Path]): Bool = excludedDirs.exists(dir => isInDir(file, dir)) // ——— Compile test directories ——— /** Compile test directories for the main hkmc2JVM project. * We walk from `mainTestDir` so test names include the `mlscript-compile/` prefix. */ def mainCompileDirs(wd: os.Path): Ls[os.Path] = mainTestDir(wd) :: Nil /** Directories whose compile files are excluded from the main CompileTestRunner. */ def mainExcludedCompileDirs(wd: os.Path): Ls[os.Path] = nofibCompileDirs(wd) ::: appsCompileDirs(wd) ::: wasmCompileDirs(wd) /** Compile test directories for the hkmc2NofibTests project. * We walk from `bench/` so test names include the `mlscript-compile/` prefix. */ def nofibCompileDirs(wd: os.Path): Ls[os.Path] = compileTestDir(wd)/"nofib" :: Nil /** Compile test directories for the hkmc2AppsTests project. * We walk from `mlscript-compile/apps/` directly. */ def appsCompileDirs(wd: os.Path): Ls[os.Path] = compileTestDir(wd)/"apps" :: Nil /** Compile test directories for the hkmc2WasmTests project. * We walk from `mainTestDir` so test names include the `mlscript-compile/` prefix.. */ def wasmCompileDirs(wd: os.Path): Ls[os.Path] = compileTestDir(wd)/"wasm" :: Nil end TestFolders ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala ================================================ package hkmc2 import scala.collection.mutable import scala.annotation.tailrec import collection.mutable.Map as MutMap import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.Message.MessageContext import hkmc2.io import utils.TraceLogger import semantics.* import Elaborator.* import hkmc2.syntax.LetBind import CompilerCache.* class CompilerCtx( val importing: Opt[(io.Path, CompilerCtx)], val beingCompiled: Set[io.Path], val fs: io.FileSystem, cache: CompilerCache, ): def allFilesBeingImported: Ls[io.Path] = importing match case S((path, parent)) => path :: parent.allFilesBeingImported case N => Nil def derive(newFile: io.Path): CompilerCtx = CompilerCtx(S(newFile, this), beingCompiled + newFile, fs, cache) def getElaboratedBlock (file: io.Path, prelude: Ctx) (using TL, Raise, Config) : Artifact = // println(s"Cache has: ${cache.elabCache.contains(file)} ${cache.elabCache.keys}") // * FIXME: // * This is not quite correct, but might be good enough for now // * (to be fixed when we overhaul the symbol and elaboration systems). // * The problem is that different modules will see different builtin symbols // * for things like `unitSymbol` and `termSymbol`, which could in theory cause problems // * if the compiler later wants to compare them as part of the type checking/compilation/optimization logic. // * Technically, we should also have the same problem with the symbols loaded from the prelude, // * which are passed on to imported modules from the first importer // * (and the "first importer" is nondeterministic, due to concurrent tests), // * and since the imported modules are cached, // * this means subsequent importers will not have see same prelude symbols. // * The correct approach should be to only cache a *single* State and prelude Ctx at the start, // * and reuse it for every compilation unit (each compilation unit duplicating the root State). given Elaborator.State = new Elaborator.State val lastMod = fs.getLastChangedTimestamp(file) def mk = val parse = given CompilerCtx = this ParserSetup(file, dbgParsing = false) val resBlk = parse.resultBlk given Elaborator.Ctx = prelude.copy(mode = Mode.Light).nestLocal("prelude") val elab = given CompilerCtx = derive(parse.origin.fileName) Elaborator(tl, file.up, prelude) val elabbed = elab.importFrom(resBlk) Artifact(resBlk, elabbed._1, lastMod) cache.upsert(file): case N => mk case cur @ S(art) => if art.lastChangedTimestamp < lastMod then mk else art object CompilerCtx: inline def get(using cctx: CompilerCtx) = cctx def fresh(fs: io.FileSystem): CompilerCtx = CompilerCtx(N, Set.empty, fs, new PlatformCompilerCache) end CompilerCtx object CompilerCache: class Artifact(val tree: syntax.Tree.Block, val term: semantics.Term.Blk, val lastChangedTimestamp: Long) end CompilerCache trait CompilerCache: // TODO also use hash comparison to avoid needless re-parses? def elabCache: MutMap[io.Path, Artifact] /** Create or update an artifact at the given path in the cache. */ def upsert(path: io.Path)(update: Option[Artifact] => Artifact): Artifact = elabCache .updateWith(path): case N => S(update(N)) case cur @ S(oldArt) => val newArt = update(cur) if newArt is oldArt then cur else S(newArt) .get // * above, we always returns Some end CompilerCache ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/Config.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import utils.* import Config.* import Message.MessageContext /** The compilation target of a program. */ enum CompilationTarget: case JS case Wasm def config(using Config): Config = summon type Cfg[A] = Config ?=> A case class Config( baseDir: io.Path, sanityChecks: Opt[SanityChecks], effectHandlers: Opt[EffectHandlers], liftDefns: Opt[LiftDefns], patMatConsequentSharingThreshold: Opt[Int], stageCode: Bool, target: CompilationTarget, rewriteWhileLoops: Bool, tailRecOpt: Bool, deforest: Opt[Deforest], inlining: Opt[Inliner], qqEnabled: Bool, funcToCls: Bool, commentGeneratedCode: Bool, noFreeze: Bool, noModuleCheck: Bool, deadParamElim: Opt[DeadParamElim], ): def stackSafety: Opt[StackSafety] = effectHandlers.flatMap(_.stackSafety) def checkInstantiateEffect: Bool = effectHandlers.exists(_.checkInstantiateEffect) // NOTE: We force the rewriting of while loops to functions when handler lowering is on // to prevent the "floating out" of definitions done by handler lowering, // which currently does not respect scopes introduced by `Scoped` blocks. // Currently, this is only a problem with while loops because we do not yet // construct nested Scoped blocks in other places (but will in the future). // see https://github.com/hkust-taco/mlscript/pull/356#discussion_r2579529893 // and https://github.com/hkust-taco/mlscript/pull/356#discussion_r2585183902 def shouldRewriteWhile: Bool = rewriteWhileLoops || effectHandlers.isDefined end Config object Config: def default(baseDir: io.Path): Config = Config( baseDir = baseDir, sanityChecks = N, // TODO make the default S // sanityChecks = S(SanityChecks(light = true)), effectHandlers = N, liftDefns = N, patMatConsequentSharingThreshold = default.patMatConsequentSharingThreshold, // minimum: 1 target = CompilationTarget.JS, rewriteWhileLoops = false, stageCode = false, tailRecOpt = true, deforest = N, inlining = S(Inliner(1)), qqEnabled = false, funcToCls = false, commentGeneratedCode = false, noFreeze = false, noModuleCheck = false, deadParamElim = S(DeadParamElim.default) ) object default: val patMatConsequentSharingThreshold = S(15) case class SanityChecks(light: Bool) case class EffectHandlers( debug: Bool, stackSafety: Opt[StackSafety], // Whether we check `Instantiate` nodes for effects. Currently, effects cannot be raised in constructors. checkInstantiateEffect: Bool = false, // A debug option that allows codegen to continue even if an unlifted definition is encountered. softLifterError: Bool = false, // Skips instrumenting module constructors, this can be used when the file is statically known to not // raise any effect and cannot use the Runtime.mls module during module construction due to cyclic dependency. // One specific scenario is Rendering.mls, which Runtime.mls depends on, and hence using stack safety will // reference Runtime.mls during construction of the Rendering module, causing a cyclic dependency error. doNotInstrumentTopLevelModCtor: Bool = false, ) case class StackSafety(stackLimit: Int) object StackSafety: val default: StackSafety = StackSafety( stackLimit = 1000, ) case class LiftDefns() // there may be other settings in the future, having it as a case class now case class Deforest(val debug: Boolean, val mono: Boolean) object Deforest: val default = Deforest(true, false) case class DeadParamElim(val debug: Boolean, val mono: Boolean) object DeadParamElim: val default = DeadParamElim(false, true) case class Inliner(inlineThreshold: Int) def extractConfigFromStats(prgm: semantics.Term.Blk)(using Config) = // Extract cumulative config modifications from SetConfig statements val configModify = prgm.stats.collect: case sc: semantics.SetConfig => sc.modify .foldLeft(identity[Config]): (acc, modify) => cfg => modify(acc(cfg)) configModify(config) end Config object ConfigParser: import syntax.Tree import syntax.Tree.* import syntax.Keyword /** Parse a list of config override arguments (from the Tup tree) into a Config modification function. */ def parseOverrides(args: Ls[Tree])(using Raise): Config => Config = args.foldLeft(identity[Config]): (acc, arg) => val override_ = parseOverride(arg) cfg => override_(acc(cfg)) /** Parse a single config override argument. */ def parseOverride(arg: Tree)(using Raise): Config => Config = arg match case InfixApp(Ident(name), Keywrd(Keyword.`:`), value) => parseField(name, value) case _ => raise(ErrorReport( msg"Unsupported config override syntax" -> arg.toLoc :: Nil, source = Diagnostic.Source.Compilation)) identity private def parseBool(tree: Tree)(using Raise): Opt[Bool] = tree match case BoolLit(v) => S(v) case Ident("true") => S(true) case Ident("false") => S(false) case _ => raise(ErrorReport( msg"Expected a boolean value" -> tree.toLoc :: Nil, source = Diagnostic.Source.Compilation)) N private def parseInt(tree: Tree)(using Raise): Opt[Int] = tree match case IntLit(v) => S(v.toInt) case App(Ident("-"), Tup(IntLit(v) :: Nil)) => S(-v.toInt) case _ => raise(ErrorReport( msg"Expected an integer value" -> tree.toLoc :: Nil, source = Diagnostic.Source.Compilation)) N /** Parse the `None`/`Some(...)` syntax for optional config fields. * Also accepts unwrapped values as a convenience (treated as `Some(value)`). */ private def parseOpt[A](tree: Tree)(parseInner: Tree => Opt[A])(using Raise): Opt[Opt[A]] = tree match case Ident("None") | Ident("N") => S(N) case App(Ident("Some") | Ident("S"), Tup(inner :: Nil)) => parseInner(inner).map(v => S(v)) case other => parseInner(other).map(v => S(v)) private def parseStackSafety(tree: Tree)(using Raise): Opt[Config.StackSafety] = tree match case App(Ident("StackSafety"), Tup(args)) => var stackLimit = Config.StackSafety.default.stackLimit args.foreach: case InfixApp(Ident("stackLimit"), Keywrd(Keyword.`:`), value) => parseInt(value).foreach(v => stackLimit = v) case IntLit(v) => stackLimit = v.toInt case other => raise(ErrorReport( msg"Unsupported StackSafety argument" -> other.toLoc :: Nil, source = Diagnostic.Source.Compilation)) S(Config.StackSafety(stackLimit)) case IntLit(v) => S(Config.StackSafety(v.toInt)) case _ => raise(ErrorReport( msg"Expected StackSafety(...) or an integer" -> tree.toLoc :: Nil, source = Diagnostic.Source.Compilation)) N private def parseEffectHandlers(tree: Tree, current: Opt[Config.EffectHandlers])(using Raise): Opt[Config.EffectHandlers] = tree match case App(Ident("EffectHandlers"), Tup(args)) => val base = current.getOrElse(Config.EffectHandlers(debug = false, stackSafety = N)) var debug = base.debug var stackSafety = base.stackSafety var checkInstantiateEffect = base.checkInstantiateEffect var softLifterError = base.softLifterError var doNotInstrumentTopLevelModCtor = base.doNotInstrumentTopLevelModCtor args.foreach: case InfixApp(Ident("debug"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => debug = v) case InfixApp(Ident("stackSafety"), Keywrd(Keyword.`:`), value) => parseOpt(value)(parseStackSafety).foreach(v => stackSafety = v) case InfixApp(Ident("checkInstantiateEffect"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => checkInstantiateEffect = v) case InfixApp(Ident("softLifterError"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => softLifterError = v) case InfixApp(Ident("doNotInstrumentTopLevelModCtor"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => doNotInstrumentTopLevelModCtor = v) case other => raise(ErrorReport( msg"Unsupported EffectHandlers argument" -> other.toLoc :: Nil, source = Diagnostic.Source.Compilation)) S(Config.EffectHandlers(debug, stackSafety, checkInstantiateEffect, softLifterError, doNotInstrumentTopLevelModCtor)) case _ => raise(ErrorReport( msg"Expected EffectHandlers(...)" -> tree.toLoc :: Nil, source = Diagnostic.Source.Compilation)) N private def parseDeforest(tree: Tree, current: Opt[Config.Deforest])(using Raise): Opt[Config.Deforest] = tree match case BoolLit(true) | Ident("true") | Ident("Deforest") => S(current.getOrElse(Config.Deforest.default)) case App(Ident("Deforest"), Tup(args)) => val base = current.getOrElse(Config.Deforest.default) var debug = base.debug var mono = base.mono args.foreach: case InfixApp(Ident("debug"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => debug = v) case InfixApp(Ident("mono"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => mono = v) case other => raise(ErrorReport( msg"Unsupported Deforest argument" -> other.toLoc :: Nil, source = Diagnostic.Source.Compilation)) S(Config.Deforest(debug, mono)) case _ => raise(ErrorReport( msg"Expected Deforest(...), Deforest, or true" -> tree.toLoc :: Nil, source = Diagnostic.Source.Compilation)) N private def parseDeadParamElim(tree: Tree, current: Opt[Config.DeadParamElim])(using Raise): Opt[Config.DeadParamElim] = tree match case BoolLit(true) | Ident("true") | Ident("DeadParamElim") => S(current.getOrElse(Config.DeadParamElim.default)) case App(Ident("DeadParamElim"), Tup(args)) => val base = current.getOrElse(Config.DeadParamElim.default) var debug = base.debug var mono = base.mono args.foreach: case InfixApp(Ident("debug"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => debug = v) case InfixApp(Ident("mono"), Keywrd(Keyword.`:`), value) => parseBool(value).foreach(v => mono = v) case other => raise(ErrorReport( msg"Unsupported DeadParamElim argument" -> other.toLoc :: Nil, source = Diagnostic.Source.Compilation)) S(Config.DeadParamElim(debug, mono)) case _ => raise(ErrorReport( msg"Expected DeadParamElim(...), DeadParamElim, or true" -> tree.toLoc :: Nil, source = Diagnostic.Source.Compilation)) N /** Parse a single field override like `tailRecOpt: false`. */ private def parseField(name: Str, value: Tree)(using Raise): Config => Config = name match case "tailRecOpt" => parseBool(value) match case S(v) => _.copy(tailRecOpt = v) case N => identity case "noFreeze" => parseBool(value) match case S(v) => _.copy(noFreeze = v) case N => identity case "noModuleCheck" => parseBool(value) match case S(v) => _.copy(noModuleCheck = v) case N => identity case "rewriteWhileLoops" => parseBool(value) match case S(v) => _.copy(rewriteWhileLoops = v) case N => identity case "stageCode" => parseBool(value) match case S(v) => _.copy(stageCode = v) case N => identity case "qqEnabled" => parseBool(value) match case S(v) => _.copy(qqEnabled = v) case N => identity case "funcToCls" => parseBool(value) match case S(v) => _.copy(funcToCls = v) case N => identity case "commentGeneratedCode" => parseBool(value) match case S(v) => _.copy(commentGeneratedCode = v) case N => identity case "effectHandlers" => cfg => parseOpt(value)(v => parseEffectHandlers(v, cfg.effectHandlers)) match case S(v) => cfg.copy(effectHandlers = v) case N => cfg case "liftDefns" => parseOpt(value)(_ => S(Config.LiftDefns())) match case S(v) => _.copy(liftDefns = v) case N => identity case "deforest" => cfg => parseOpt(value)(v => parseDeforest(v, cfg.deforest)) match case S(v) => cfg.copy(deforest = v) case N => cfg case "deadParamElim" => cfg => parseOpt(value)(v => parseDeadParamElim(v, cfg.deadParamElim)) match case S(v) => cfg.copy(deadParamElim = v) case N => cfg case "sanityChecks" => parseOpt(value)(_ => S(Config.SanityChecks(light = true))) match case S(v) => _.copy(sanityChecks = v) case N => identity case "patMatConsequentSharingThreshold" => parseInt(value) match case S(v) => _.copy(patMatConsequentSharingThreshold = S(v)) case N => identity case "inlining" => parseOpt(value)(parseInt) match case S(v) => _.copy(inlining = v.map(Inliner.apply)) case _ => identity case _ => raise(ErrorReport( msg"Unknown config field '${name}'" -> value.toLoc :: Nil, source = Diagnostic.Source.Compilation)) identity end ConfigParser ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/Diagnostic.scala ================================================ package hkmc2 import scala.util.chaining._ import sourcecode.{Name, Line, FileName} import mlscript.utils._, shorthands._ import hkmc2.io import Diagnostic._ sealed abstract class Diagnostic (val theMsg: String) (using val srcLine: Line, val srcName: Name, val srcFile: FileName) extends Exception(theMsg): val allMsgs: Ls[Message -> Opt[Loc]] val kind: Kind val source: Source val mkExtraInfo: () => Opt[Any] def srcLoc: Str = s"${srcName.value} (${srcFile.value}:${srcLine.value})" object Diagnostic: enum Kind: case Error case Warning case Internal enum Source: case Lexing case Parsing case Typing case Compilation case Runtime final case class ErrorReport( mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], mkExtraInfo: () => Opt[Any], source: Source, )(using Line, Name, FileName) extends Diagnostic(mainMsg): val kind: Kind = Kind.Error object ErrorReport: def apply(using Line, FileName) (msgs: Ls[Message -> Opt[Loc]], extraInfo: => Opt[Any] = N, source: Source = Source.Compilation) (using Name): ErrorReport = ErrorReport(msgs.head._1.show, msgs, () => extraInfo, source) final case class WarningReport( mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], mkExtraInfo: () => Opt[Any], source: Source, )(using Line, Name, FileName) extends Diagnostic(mainMsg): val kind: Kind = Kind.Warning object WarningReport: def apply(using Line, FileName) (msgs: Ls[Message -> Opt[Loc]], extraInfo: => Opt[Any] = N, source: Source = Source.Compilation) (using Name): WarningReport = WarningReport(msgs.head._1.show, msgs, () => extraInfo, source) final case class InternalError( mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], mkExtraInfo: () => Opt[Any], source: Source )(using Line, Name, FileName) extends Diagnostic(mainMsg): val kind: Kind = Kind.Internal object InternalError: def apply(using Line, FileName) (msgs: Ls[Message -> Opt[Loc]], extraInfo: => Opt[Any] = N, source: Source = Source.Compilation) (using Name): InternalError = InternalError(msgs.head._1.show, msgs, () => extraInfo, source) final case class Loc(spanStart: Int, spanEnd: Int, origin: Origin): assert(spanStart >= 0) assert(spanEnd >= spanStart) def covers(that: Loc): Bool = that.origin === this.origin && ( that.spanStart >= this.spanStart && that.spanEnd <= this.spanEnd ) def touches(that: Loc): Bool = that.origin === this.origin && ( that.spanStart >= this.spanStart && that.spanStart <= this.spanEnd || that.spanEnd <= this.spanEnd && that.spanEnd >= this.spanStart ) def ++(that: Loc): Loc = require(this.origin is that.origin) Loc(this.spanStart min that.spanStart, this.spanEnd max that.spanEnd, origin) def ++(that: Opt[Loc]): Loc = that.fold(this)(this ++ _) def right: Loc = copy(spanStart = spanEnd) def left: Loc = copy(spanEnd = spanStart) object Loc: def apply(xs: Located*): Opt[Loc] = apply(xs.iterator) def apply(xs: IterableOnce[Located]): Opt[Loc] = xs.iterator.foldLeft(none[Loc])((acc, l) => acc.fold(l.toLoc)(_ ++ l.toLoc |> some)) final case class Origin(fileName: io.Path, startLineNum: Int, fph: FastParseHelpers): override def toString = s"${fileName.last}:+$startLineNum" ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import hkmc2.io import utils.* import hkmc2.semantics.* import hkmc2.syntax.Keyword.`override` import semantics.Elaborator.{Ctx, State} class ParserSetup(file: io.Path, dbgParsing: Bool)(using state: Elaborator.State, raise: Raise, cctx: CompilerCtx): val block = cctx.fs.read(file) val fph = new FastParseHelpers(block) val origin = Origin(file, 0, fph) val lexer = new syntax.Lexer(origin, dbg = dbgParsing) val tokens = lexer.bracketedTokens // if showParse.isSet || dbgParsing.isSet then // output(syntax.Lexer.printTokens(tokens)) val rules = syntax.ParseRules() val parser = new syntax.Parser(origin, tokens, rules, raise, dbg = dbgParsing): def doPrintDbg(msg: => Str): Unit = // if dbg then output(msg) if dbg then println(msg) val result = parser.parseAll(parser.block(allowNewlines = true)) val resultBlk = new syntax.Tree.Block(result) object MLsCompiler: /** The class contains the necessary paths to files for the MLscript compiler. */ trait Paths: def preludeFile: io.Path def runtimeFile: io.Path def termFile: io.Path /** * The compiler that compiles MLscript code into JavaScript modules. * * @param paths required paths needed by the compiler * @param mkRaise generates a separate `Raise` function for each file. * @param config the compiler's configuration object * @param fs the file system interface */ class MLsCompiler (paths: MLsCompiler.Paths, mkRaise: io.Path => Raise) (using cctx: CompilerCtx, config: Config): import paths.* // TODO adapt logic given DebugPrinter = new DebugPrinter val etl = new TraceLogger{override def doTrace: Bool = false} val ltl = new TraceLogger{override def doTrace: Bool = false} // val ltl = new TraceLogger{override def doTrace: Bool = true} val rtl = new TraceLogger{override def doTrace: Bool = false} var dbgParsing = false var dbgElab = false def compileModule(file: io.Path): Unit = val wd = file.up given Raise = mkRaise(file) given Elaborator.State = new Elaborator.State: override def dbg: Bool = dbgElab val preludeParse = ParserSetup(preludeFile, dbgParsing) val mainParse = ParserSetup(file, dbgParsing) val elab = Elaborator(etl, wd, Ctx.empty) val initState = State.init.nestLocal("prelude") val (pblk, newCtx) = elab.importFrom(preludeParse.resultBlk)(using initState) newCtx.nestLocal("file:"+file.baseName).givenIn: given CompilerCtx = cctx.derive(file) val elab = Elaborator(etl, wd, newCtx) val parsed = mainParse.resultBlk val (blk0, _) = elab.importFrom(parsed) Config.extractConfigFromStats(blk0).givenIn { val resolver = Resolver(rtl) resolver.traverseBlock(blk0)(using Resolver.ICtx.empty) def findQuote(t: semantics.Statement): Bool = t match case Term.Quoted(_) | Term.Unquoted(_) => true case Term.Ref(sym) => sym === State.termSymbol case _ => t.subTerms.exists(findQuote) val hasQuote = findQuote(blk0) val blk = new Term.Blk( Import(State.runtimeSymbol, runtimeFile.toString, runtimeFile) :: // Only import `Term.mls` when necessary. (if hasQuote then Import(State.termSymbol, termFile.toString, termFile) :: blk0.stats else blk0.stats), blk0.res ) val low = ltl.givenIn: new codegen.Lowering() with codegen.LoweringSelSanityChecks val jsb = ltl.givenIn: codegen.js.JSBuilder() val le_0 = low.program(blk) val nme = file.baseName val exportedSymbol = parsed.definedSymbols.find(_._1 === nme).map(_._2) val le_1 = ltl.givenIn: codegen.BlockSimplifier(exportedSymbol.toSet)(le_0) val le_2 = ltl.givenIn: codegen.DeadParamElim(le_1) val baseScp: utils.Scope = utils.Scope.empty(utils.Scope.Cfg.default) // * This line serves for `import.meta.url`, which retrieves directory and file names of mjs files. // * Having `module id"import" with ...` in `prelude.mls` will generate `globalThis.import` that is undefined. baseScp.addToBindings(Elaborator.State.importSymbol, "import", shadow = false) val nestedScp = baseScp.nest val je = nestedScp.givenIn: jsb.program(le_2, exportedSymbol, wd) val jsStr = je.stripBreaks.mkString(100) val out = file.up / io.RelPath(file.baseName + ".mjs") cctx.fs.write(out, jsStr) } end MLsCompiler ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/Message.scala ================================================ package hkmc2 import scala.language.implicitConversions import mlscript.utils._, shorthands._ final case class Message(bits: Ls[Message.Bit]): def show: Str = val ctx = ShowCtx.mk(typeBits) showIn(using ctx) def typeBits: Ls[TypeLike] = bits.collect{ case Message.Code(t) => t } def showIn(implicit ctx: ShowCtx): Str = bits.map { case Message.Code(ty) => ty.showIn(0) case Message.Text(txt) => txt }.mkString def showDbg: Str = bits.iterator.map { case Message.Code(trm) => s"$trm" case Message.Text(txt) => txt }.mkString def +(that: Message): Message = Message(bits ++ that.bits) object Message: def mkCtx(msgs: IterableOnce[Message],pre: Str = "'"): ShowCtx = ShowCtx.mk(msgs.iterator.flatMap(_.typeBits), pre) def join(msgs: Seq[Message]): Message = Message(msgs.iterator.flatMap(_.bits).toList) sealed abstract class Bit final case class Text(str: Str) extends Bit final case class Code(ty: TypeLike) extends Bit implicit def fromType(ty: TypeLike): Message = Message(Code(ty) :: Nil) implicit def fromStr(str: Str): Message = Message(Text(str) :: Nil) implicit def fromInt(int: Int): Message = Message(Text(int.toString) :: Nil) implicit class MessageContext(private val ctx: StringContext): def msg(inserted: Message*): Message = assert(inserted.length === ctx.parts.length - 1) val parts = ctx.parts.map(str => Text(StringContext(str).s())) val h = parts.head val t = parts.tail Message((h +: inserted.lazyZip(t).flatMap(_.bits :+ _)).toList) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/ShowCtx.scala ================================================ package hkmc2 import scala.util.chaining._ import scala.collection.mutable.{Map => MutMap, SortedMap => SortedMutMap, Set => MutSet, Buffer} import scala.collection.immutable.SortedMap import math.Ordered.orderingToOrdered import mlscript.utils._, shorthands._ final case class ShowCtx( vs: SortedMap[TypeVar, Str], indentLevel: Int, angletards: Bool = false, ) : lazy val indStr: Str = ShowCtx.indentation * indentLevel def lnIndStr: Str = "\n" + indStr def indent: ShowCtx = copy(indentLevel = indentLevel + 1) def < : Str = if angletards then "<" else "[" def > : Str = if angletards then ">" else "]" object ShowCtx: val ExtrusionPrefix: Str = "??" def indentation: Str = " " /** * Create a context from a list of types. For named variables and * hinted variables use what is given. For unnamed variables generate * completely new names. If same name exists increment counter suffix * in the name. */ def mk(tys: IterableOnce[TypeLike], _pre: Str = "'"): ShowCtx = val (otherVars, namedVars) = tys.iterator.toList.flatMap(_.typeVarsList).distinct.partitionMap { tv => tv.identifier match { case L(_) => L(tv.nameHint -> tv); case R(nh) => R(nh -> tv) } } val (hintedVars, unnamedVars) = otherVars.partitionMap: case (S(nh), tv) => L(nh -> tv) case (N, tv) => R(tv) val usedNames = MutMap.empty[Str, Int] def assignName(n: Str): Str = val pre = if n.startsWith("'") || n.startsWith(ExtrusionPrefix) then "" else _pre usedNames.get(n) match case S(cnt) => usedNames(n) = cnt + 1 pre + n + cnt case N => usedNames(n) = 0 pre + n val namedMap = (namedVars ++ hintedVars).map { case (nh, tv) => // tv -> assignName(nh.dropWhile(_ === '\'')) tv -> assignName(nh.stripPrefix(_pre)) }.toSortedMap val used = usedNames.keySet // * Generate names for unnamed variables val numLetters = 'z' - 'a' + 1 val names = Iterator.unfold(0) { idx => val postfix = idx/numLetters S(('a' + idx % numLetters).toChar.toString + (if postfix === 0 then "" else postfix.toString), idx + 1) }.filterNot(used).map(assignName) ShowCtx(namedMap ++ unnamedVars.zip(names), indentLevel = 0) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/Uid.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* opaque type Uid[T] = Int object Uid: class Handler[T]: class State: private var curUid = -1 def nextUid: Uid[T] = curUid += 1 curUid def reset = curUid = -1 object Symbol extends Handler[semantics.Symbol] object Result extends Handler[codegen.Result] object StratVar extends Handler[codegen.flowAnalysis.StratVar] extension [T] (x: Uid[T]) def <=(rhs: Uid[T]) = x <= rhs private val ord = Ordering.Int given [A]: Ordering[Uid[A]] = ord ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala ================================================ package hkmc2 package codegen import mlscript.utils.*, shorthands.* import utils.* import hkmc2.Message.MessageContext import hkmc2.{semantics => sem} import hkmc2.semantics.{Term => st} import syntax.{Literal, Tree, SpreadKind, Keyword} import semantics.* import semantics.Term.* import sem.Elaborator.State case class Program( imports: Ls[Local -> Str], main: Block, ) sealed abstract class Block extends Product: def ~(that: Block): Block = Begin(this, that) def isEmpty: Bool = this match case _: End => true case _ => false lazy val isAbortive: Bool = this match case _: End => false case _: Throw | _: Break | _: Continue | _: Unreachable => true case ret: Return => !ret.implct case Begin(sub, rst) => sub.isAbortive || rst.isAbortive case Assign(_, _, rst) => rst.isAbortive case AssignField(_, _, _, rst) => rst.isAbortive case AssignDynField(_, _, _, _, rst) => rst.isAbortive case Match(_, arms, dflt, rst) => rst.isAbortive || arms.forall(_._2.isAbortive) && dflt.exists(_.isAbortive) case Define(_, rst) => rst.isAbortive case TryBlock(sub, fin, rst) => rst.isAbortive || sub.isAbortive || fin.isAbortive case Label(sym, loop, bod, rst) => // * Note: the body may be abortive for the reason of breaking to the rest! // * So we can't really use the result of bod.isAbortive even when `loop` is false. rst.isAbortive case Scoped(_, body) => body.isAbortive // * Note: it seems most historical uses of `definedVars` would be better removed, // * now that we properly put everything in proper Scoped blocks; // * and `definedVars` itself should be removed. lazy val definedVars: Set[Local] = this match case _: Return | _: Throw | _: Unreachable => Set.empty case Begin(sub, rst) => sub.definedVars ++ rst.definedVars case Assign(l: TermSymbol, r, rst) => rst.definedVars case Assign(l, r, rst) => rst.definedVars + l case AssignField(l, n, r, rst) => rst.definedVars case AssignDynField(l, n, ai, r, rst) => rst.definedVars case Match(scrut, arms, dflt, rst) => arms.flatMap(_._2.definedVars).toSet ++ dflt.toList.flatMap(_.definedVars) ++ rst.definedVars case End(_) => Set.empty case Break(_) => Set.empty case Continue(_) => Set.empty case Define(defn, rst) => val rest = rst.definedVars if defn.isOwned then rest else rest + defn.sym case TryBlock(sub, fin, rst) => sub.definedVars ++ fin.definedVars ++ rst.definedVars case Label(lbl, _, bod, rst) => bod.definedVars ++ rst.definedVars case Scoped(syms, body) => body.definedVars ++ syms lazy val size: Int = this match case _: Return | _: Throw | _: End | _: Break | _: Continue | _: Unreachable => 1 case Begin(sub, rst) => sub.size + rst.size case Assign(_, _, rst) => 1 + rst.size case AssignField(_, _, _, rst) => 1 + rst.size case AssignDynField(_, _, _, _, rst) => 1 + rst.size case Match(_, arms, dflt, rst) => 1 + arms.map(_._2.size).sum + dflt.map(_.size).getOrElse(0) + rst.size case Define(_, rst) => 1 + rst.size case TryBlock(sub, fin, rst) => 1 + sub.size + fin.size + rst.size case Label(_, _, bod, rst) => 1 + bod.size + rst.size case Scoped(_, body) => body.size // TODO: make patmat use unreach def mapReturn(f: Return => Block): Block = new BlockTransformerShallow(SymbolSubst.Id): override def applyBlock(b: Block): Block = b match case ret: Return => f(ret) case _ => super.applyBlock(b) .applyBlock(this) lazy val freeVars: Set[Local] = this match case Match(scrut, arms, dflt, rest) => scrut.freeVars ++ dflt.toList.flatMap(_.freeVars) ++ rest.freeVars ++ arms.flatMap: (pat, arm) => arm.freeVars -- pat.freeVars case Return(res, implct) => res.freeVars case Throw(exc) => exc.freeVars case Label(label, _, body, rest) => (body.freeVars - label) ++ rest.freeVars case Break(label) => Set.single(label) case Continue(label) => Set.single(label) case Begin(sub, rest) => sub.freeVars ++ rest.freeVars case TryBlock(sub, finallyDo, rest) => sub.freeVars ++ finallyDo.freeVars ++ rest.freeVars case Assign(lhs, rhs, rest) => Set.single(lhs) ++ rhs.freeVars ++ rest.freeVars case AssignField(lhs, nme, rhs, rest) => lhs.freeVars ++ rhs.freeVars ++ rest.freeVars case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVars ++ fld.freeVars ++ rhs.freeVars ++ rest.freeVars case Define(defn, rest) => defn.freeVars ++ rest.freeVars case Scoped(syms, body) => body.freeVars case End(msg) => Set.empty case Unreachable(msg) => Set.empty lazy val freeVarsLLIR: Set[Local] = this match case Match(scrut, arms, dflt, rest) => scrut.freeVarsLLIR ++ dflt.toList.flatMap(_.freeVarsLLIR) ++ rest.freeVarsLLIR ++ arms.flatMap: (pat, arm) => arm.freeVarsLLIR -- pat.freeVarsLLIR case Return(res, implct) => res.freeVarsLLIR case Throw(exc) => exc.freeVarsLLIR case Label(label, _, body, rest) => (body.freeVarsLLIR - label) ++ rest.freeVarsLLIR case Break(label) => Set.empty case Continue(label) => Set.empty case Begin(sub, rest) => sub.freeVarsLLIR ++ rest.freeVarsLLIR case TryBlock(sub, finallyDo, rest) => sub.freeVarsLLIR ++ finallyDo.freeVarsLLIR ++ rest.freeVarsLLIR case Assign(lhs, rhs, rest) => rhs.freeVarsLLIR ++ (rest.freeVarsLLIR - lhs) case AssignField(lhs, nme, rhs, rest) => lhs.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVarsLLIR ++ fld.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR case Define(defn, rest) => defn.freeVarsLLIR ++ (rest.freeVarsLLIR - defn.sym) case Scoped(syms, body) => body.freeVarsLLIR case End(msg) => Set.empty case Unreachable(msg) => Set.empty lazy val subBlocks: Ls[Block] = this match case Match(p, arms, dflt, rest) => p.subBlocks ++ arms.map(_._2) ++ dflt.toList :+ rest case Begin(sub, rest) => sub :: rest :: Nil case TryBlock(sub, finallyDo, rest) => sub :: finallyDo :: rest :: Nil case Assign(_, rhs, rest) => rhs.subBlocks ::: rest :: Nil case AssignField(_, _, rhs, rest) => rhs.subBlocks ::: rest :: Nil case AssignDynField(_, _, _, rhs, rest) => rhs.subBlocks ::: rest :: Nil case Define(d, rest) => d.subBlocks ::: rest :: Nil case Label(_, _, body, rest) => body :: rest :: Nil case Scoped(_, body) => body :: Nil // TODO rm Lam from values and thus the need for these cases case Return(r, _) => r.subBlocks case Throw(r) => r.subBlocks case _: Return | _: Throw | _: Break | _: Continue | _: End | _: Unreachable => Nil // Moves definitions in a block to the top. Only scans the top-level definitions of the block; // i.e, definitions inside other definitions are not moved out. Definitions inside `match`/`if` // and `while` statements are moved out. // // Note that this returns the definitions in reverse order, with the bottommost definiton appearing // last. This is so that using defns.foldLeft later to add the definitions to the front of a block, // we don't need to reverse the list again to preserve the order of the definitions. def extractDefns( ignore: Defn => Bool = _ => false, preserve: Defn => Bool = _ => false ): (Block, List[Defn]) = var defns: List[Defn] = Nil val transformer = new BlockTransformerShallow(SymbolSubst.Id): override def applyBlock(b: Block): Block = b match case Define(defn, rest) if !ignore(defn) => defn match case v: ValDefn => super.applyBlock(b) case _ => defns ::= defn if preserve(defn) then super.applyBlock(b) else applyBlock(rest) case _ => super.applyBlock(b) (transformer.applyBlock(this), defns) def gatherDefns( ignore: Defn => Bool = _ => false, preserve: Defn => Bool = _ => false ): List[Defn] = extractDefns(ignore, preserve)._2 // TODO: fix this very inefficient implementation lazy val flattened: Block = this.flatten(identity) private def flatten(k: End => Block): Block = this match case Match(scrut, arms, dflt, rest) => val newRest = rest.flatten(k) val newArms = arms.mapConserve: arm => val newBody = arm._2.flattened if newBody is arm._2 then arm else (arm._1, newBody) val newDflt = dflt.mapConserve(_.flattened) if (newRest is rest) && (newArms is arms) && (newDflt is dflt) then this else Match(scrut, newArms, newDflt, newRest) case Label(label, loop, body, rest) => val newBody = body.flattened val newRest = rest.flatten(k) if (newBody is body) && (newRest is rest) then this else Label(label, loop, newBody, newRest) case Begin(sub, rest) => sub.flatten(_ => rest.flatten(k)) case TryBlock(sub, finallyDo, rest) => val newSub = sub.flattened val newFinallyDo = finallyDo.flattened val newRest = rest.flatten(k) if (newSub is sub) && (newFinallyDo is finallyDo) && (newRest is rest) then this else TryBlock(newSub, newFinallyDo, newRest) case Assign(lhs, rhs, rest) => val newRest = rest.flatten(k) if newRest is rest then this else Assign(lhs, rhs, newRest) case a @ AssignField(lhs, nme, rhs, rest) => val newRest = rest.flatten(k) if newRest is rest then this else AssignField(lhs, nme, rhs, newRest)(a.symbol) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => val newRest = rest.flatten(k) if newRest is rest then this else AssignDynField(lhs, fld, arrayIdx, rhs, newRest) case Define(defn, rest) => val newDefn = defn match case d: FunDefn => val newBody = d.body.flattened if newBody is d.body then d else d.copy(body = newBody)(configOverride = d.configOverride, annotations = d.annotations) case v: ValDefn => v case c: ClsLikeDefn => val newPreCtor = c.preCtor.flattened val newCtor = c.ctor.flattened def flattenMethods(ms: List[FunDefn]) = ms.mapConserve: case f@FunDefn(owner, sym, dSym, params, body) => val newBody = body.flattened if newBody is body then f else f.copy(body = newBody)(configOverride = f.configOverride, annotations = f.annotations) val newMethods = flattenMethods(c.methods) val newCompanion = c.companion.mapConserve: c => val newCtor = c.ctor.flattened val newMethods = flattenMethods(c.methods) if (newCtor is c.ctor) && (newMethods is c.methods) then c else c.copy(ctor = newCtor, methods = newMethods) if (newPreCtor is c.preCtor) && (newCtor is c.ctor) && (newMethods is c.methods) && (newCompanion is c.companion) then c else c.copy( preCtor = newPreCtor, ctor = newCtor, methods = newMethods, companion = newCompanion, )(c.configOverride, c.annotations) val newRest = rest.flatten(k) if (newDefn is defn) && (newRest is rest) then this else Define(newDefn, newRest) case Scoped(syms, body) => val newBody = body.flatten(k) if newBody is body then this else Scoped(syms, newBody) case e: End => k(e) case t: BlockTail => this end Block sealed abstract class BlockTail extends Block sealed abstract trait NonBlockTail: val rest: Block case class Match( scrut: Path, arms: Ls[Case -> Block], dflt: Opt[Block], rest: Block, ) extends Block with ProductWithTail with NonBlockTail // * `implct`: metadata indicating whether this is a JS implicit return, without the `return` keyword. // * This is currenlty only used for the main blocks of modules and diff-test blocks; // * for all intents and purposes, one can view an implicit return as a normal return. // * I would remove it, but it helps print cleaner outputs for diff tests (eg, using `:sir`). case class Return(res: Result, implct: Bool) extends BlockTail case class Throw(exc: Result) extends BlockTail case class Label(label: LabelSymbol, loop: Bool, body: Block, rest: Block) extends Block with NonBlockTail with ProductWithTail case class Break(label: LabelSymbol) extends BlockTail case class Continue(label: LabelSymbol) extends BlockTail case class Scoped(syms: collection.Set[Local], body: Block) extends Block with NonBlockTail: val rest = body // TODO: remove this form? case class Begin(sub: Block, rest: Block) extends Block with ProductWithTail with NonBlockTail case class TryBlock(sub: Block, finallyDo: Block, rest: Block) extends Block with ProductWithTail with NonBlockTail case class Assign(lhs: Local, rhs: Result, rest: Block) extends Block with ProductWithTail with NonBlockTail // case class Assign(lhs: Path, rhs: Result, rest: Block) extends Block with ProductWithTail case class AssignField(lhs: Path, nme: Tree.Ident, rhs: Result, rest: Block)(val symbol: Opt[MemberSymbol]) extends Block with ProductWithTail with NonBlockTail case class AssignDynField(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, rest: Block) extends Block with ProductWithTail with NonBlockTail case class Define(defn: Defn, rest: Block) extends Block with ProductWithTail with NonBlockTail inline def whenValidatingIR(inline code: => Unit): Unit = () // code // * uncomment to run on-the fly IR validations object Label: def apply(label: LabelSymbol, loop: Bool, body: Block, rest: Block): Block = body match case _: Unreachable => body case _ => rest match case Scoped(syms, rest) => Scoped(syms, Label(label, loop, body, rest)) case _ => new Label(label, loop, body, rest) object Scoped: def apply(syms: collection.Set[Local], body: Block): Block = body match case _: Unreachable => body case _ if syms.isEmpty => body case Scoped(syms2, body) => whenValidatingIR: assert(!syms2.exists(syms.contains), "overlapping symbols in nested Scoped") Scoped(syms ++ syms2, body) case _ => new Scoped(syms, body) object TryBlock: def apply(body: Block, finallyDo: Block, rest: Block): Block = body match case _: Unreachable => body case _ => rest match case Scoped(syms, innerRest) => Scoped(syms, TryBlock(body, finallyDo, innerRest)) case _ => new TryBlock(body, finallyDo, rest) object Assign: def apply(lhs: Local, rhs: Result, rest: Block): Block = rest match case _: Unreachable => if rhs.isPure then rest else new Assign(lhs, rhs, rest) case Scoped(syms, body) => Scoped(syms, Assign(lhs, rhs, body)) case _ => lhs match case _: NoSymbol => if rhs.isPure then rest else new Assign(lhs, rhs, rest) case _ => new Assign(lhs, rhs, rest) def discard(res: Result, rest: Block)(using State): Block = res match case _: Value | _: Lambda => rest case p: Path if p.isPure => rest case r => Assign(State.noSymbol, r, rest) object AssignField: def apply(lhs: Path, nme: Tree.Ident, rhs: Result, rest: Block)(symbol: Opt[MemberSymbol]): Block = rest match case Scoped(syms, body) => Scoped(syms, AssignField(lhs, nme, rhs, body)(symbol)) case _ => new AssignField(lhs, nme, rhs, rest)(symbol) object AssignDynField: def apply(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, rest: Block): Block = rest match case Scoped(syms, body) => Scoped(syms, AssignDynField(lhs, fld, arrayIdx, rhs, body)) case _ => new AssignDynField(lhs, fld, arrayIdx, rhs, rest) object Define: def apply(defn: Defn, rest: Block): Block = rest match case Scoped(syms, body) => Scoped(syms, Define(defn, body)) case _ => new Define(defn, rest) object Match: def apply(scrut: Path, _arms: Ls[Case -> Block], _dflt: Opt[Block], rest: Block): Block = val emptyDflt = _dflt.forall(_.isEmpty) val dflt = if emptyDflt then N else _dflt val arms = if emptyDflt then _arms.filterNot(_._2.isEmpty) else _arms if arms.isEmpty && scrut.isPure then dflt.fold(rest)(Begin(_, rest)) else dflt match case S(Match(`scrut`, arms2, dflt2, _: End)) => // TODO: also handle non-End rest (may require a join point) // * Currently, this branch does not seem used often (or at all?), // * because the UCS and (especially) MergeMatchArmTransformer already do a good job at merging matches Match(scrut, arms ::: arms2, dflt2, rest) case _ => val numNonAbortive = arms.count(!_._2.isAbortive) def mapDflt = dflt match case S(d) => S(if d.isAbortive then d else Begin(d, rest)) case N => S(rest) if numNonAbortive === 0 then if rest.isEmpty then new Match(scrut, arms, mapDflt, rest) else new Match(scrut, arms, mapDflt, End("(Unreachable:) rest of abortive match")) else if numNonAbortive === 1 && dflt.exists(_.isAbortive) || rest.size <= 1 then new Match(scrut, arms.map: a => if a._2.isAbortive then a else (a._1, Begin(a._2, rest)), mapDflt, // * We used to produce an `Unreachable` here, but that got in the way of the useless-break optimization; // * Indeed, `L: { match scrut { C => break L }; end }` can no longer be optimized // * if we replace `end` with `unreachable`, since the break is no longer jumping over nothing, // * ie no longer in tail position of the label (trying to treat it as such is unsound). End("Rest moved to non-abortive branch(es)")) else rest match case Scoped(syms, body) => Scoped(syms, Match(scrut, arms, dflt, body)) case _ => new Match(scrut, arms, dflt, rest) object Begin: def apply(sub: Block, rest: Block): Block = if sub.isEmpty then rest else if rest.isEmpty then sub else if sub.isAbortive then sub else (sub, rest) match case (Scoped(symsSub, bodySub), Scoped(symsRest, bodyRest)) => whenValidatingIR: assert( !symsSub.exists(symsRest.contains), "overlapping symbols when trying to merge Scoped blocks") Scoped(symsSub ++ symsRest, Begin(bodySub, bodyRest)) case (Scoped(symsSub, bodySub), _) => Scoped(symsSub, Begin(bodySub, rest)) case (_, Scoped(symsRest, bodyRest)) => Scoped(symsRest, Begin(sub, bodyRest)) case _ => new Begin(sub, rest) object HandleBlock: def suspend(tag: Path, handlerFun: Path)(using Elaborator.Ctx): Result = Call(Value.Ref(Elaborator.ctx.builtins.runtime.suspend, N), (tag.asArg :: handlerFun.asArg :: Nil) ne_:: Nil)(true, true, false) def handleSuspension(tag: Path, bodyFun: Path)(using Elaborator.Ctx): Result = Call(Value.Ref(Elaborator.ctx.builtins.runtime.handle_suspension, N), (tag.asArg :: bodyFun.asArg :: Nil) ne_:: Nil)(true, true, false) private def create( lhs: Local, res: Local, par: Path, args: Ls[Path], cls: ClassSymbol, handlers: Ls[Handler], body: Block, rest: Block )(using Elaborator.State, Elaborator.Ctx) = val sym = new BlockMemberSymbol("handleBlock$", Nil, false) val bodyDefn = FunDefn.withFreshSymbol(N, sym, PlainParamList(Nil) :: Nil, body)(N, annotations = Nil) val handlerMtds = handlers.map: handler => val sym = BlockMemberSymbol(cls.nme + handler.sym.nme, Nil, true) val fDef = FunDefn.withFreshSymbol( N, sym, PlainParamList(Param(FldFlags.empty, handler.resumeSym, N, Modulefulness.none) :: Nil) :: Nil, handler.body )(N, annotations = Nil) val rSym = TempSymbol(N, "suspendRes") FunDefn.withFreshSymbol( S(cls), handler.sym, handler.params, Scoped(Set(sym, rSym), Define( fDef, Return(suspend(cls.asPath, Value.Ref(sym, S(fDef.dSym))), false))))(N, annotations = Nil) val clsDefn = ClsLikeDefn( N, // no owner cls, BlockMemberSymbol(cls.id.name, Nil), N, syntax.Cls, N, Nil, S(par), handlerMtds, Nil, Nil, // Apparently, the lifter is not happy with any assignment in the preCtor... Return(Call(Value.Ref(State.builtinOpsMap("super")), args.map(_.asArg) ne_:: Nil)(true, true, false), true), End(), N, N, )(N, Nil) blockBuilder .scopedVars(Set(clsDefn.sym, sym)) .define(clsDefn) .assign(lhs, Instantiate(mut = true, Value.Ref(clsDefn.sym, S(cls)), Nil :: Nil)) .define(bodyDefn) .assign(res, handleSuspension(lhs.asPath, Value.Ref(bodyDefn.sym, S(bodyDefn.dSym)))) .rest(rest) def apply( lhs: Local, res: Local, par: Path, args: Ls[Path], cls: ClassSymbol, handlers: Ls[Handler], body: Block, rest: Block )(using Elaborator.State, Elaborator.Ctx) = rest match case Scoped(syms, rest) => Scoped(syms, create(lhs, res, par, args, cls, handlers, body, rest)) case _ => create(lhs, res, par, args, cls, handlers, body, rest) sealed abstract class Defn: val innerSym: Opt[MemberSymbol] val sym: BlockMemberSymbol val annotations: Ls[Annot] def isStaged: Bool = annotations.exists: case Annot.Modifier(Keyword.`staged`) => true case _ => false def isOwned: Bool = owner.isDefined def owner: Opt[InnerSymbol] /** Whether this definition as a statement has any side effect (if unused). */ def isPure: Bool = this match case vd: ValDefn => vd.rhs.isPure && vd.tsym.owner.isEmpty case fd: FunDefn => fd.owner.isEmpty case c: ClsLikeDefn => // * Simple heuristic. TODO: check the purity of the ctor somehow? (ignore pure local field inits) c.companion.isEmpty && (!(c.k is syntax.Obj) || c.ctor.isEmpty) def subBlocks: Ls[Block] = this match case FunDefn(body = body) => body :: Nil case _: ValDefn => Nil case ClsLikeDefn(preCtor = preCtor, ctor = ctor, methods = mtds, companion = comp) => preCtor :: ctor :: mtds.flatMap(_.subBlocks) ::: comp.toList.flatMap(_.subBlocks) // * Note that `privateFields` abd `publicFields` can't possibly be free since they are never // * referred to directly (they are only accessed through selections). // * At some point we'll want to make `Local` more specific than `Symbol` to express this // * in the type system. lazy val freeVars: Set[Local] = this match case FunDefn(own, sym, dSym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) ++ sym.optionIf(own.isEmpty) case ValDefn(tsym, sym, rhs) => rhs.freeVars ++ sym.optionIf(tsym.owner.isEmpty) case ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor, stat, bufferable) => preCtor.freeVars ++ ctor.freeVars ++ methods.flatMap(_.freeVars) -- auxParams.flatMap(_.paramSyms) ++ stat.iterator.flatMap(_.freeVars) ++ sym.optionIf(own.isEmpty) lazy val freeVarsLLIR: Set[Local] = this match case FunDefn(own, sym, dSym, params, body) => body.freeVarsLLIR -- params.flatMap(_.paramSyms) - sym case ValDefn(tsym, sym, rhs) => rhs.freeVarsLLIR case ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor, stat, bufferable) => preCtor.freeVarsLLIR ++ ctor.freeVarsLLIR ++ methods.flatMap(_.freeVarsLLIR) ++ stat.iterator.flatMap(_.freeVarsLLIR) -- auxParams.flatMap(_.paramSyms) // NOTE: Setting isTailRec to false does not affect whether the function is optimized. // It only affects whether a warning is thrown if the function is not actually tailrec. final case class FunDefn( owner: Opt[InnerSymbol], sym: BlockMemberSymbol, dSym: TermSymbol, params: Ls[ParamList], body: Block, )( val configOverride: Opt[Config], val annotations: Ls[Annot], ) extends Defn: val innerSym = N val asPath = Value.Ref(sym, S(dSym)) lazy val forceTailRec: Bool = annotations.contains(Annot.TailRec) lazy val visibility: Visibility = annotations.collectFirst: case Annot.Modifier(Keyword.`private`) => Visibility.Private case Annot.Modifier(Keyword.`public`) => Visibility.Public .getOrElse(Visibility.Public) object FunDefn: def withFreshSymbol(owner: Opt[InnerSymbol], sym: BlockMemberSymbol, params: Ls[ParamList], body: Block)(configOverride: Opt[Config], annotations: Ls[Annot])(using State) = val tSym = TermSymbol(syntax.Fun, owner, Tree.Ident(sym.nme)) sym.tsym = S(tSym) FunDefn(owner, sym, tSym, params, body)(configOverride, annotations) final case class ValDefn( tsym: TermSymbol, sym: BlockMemberSymbol, rhs: Path, )( val configOverride: Opt[Config], val annotations: Ls[Annot], ) extends Defn: val innerSym = S(tsym) val owner: Opt[InnerSymbol] = tsym.owner object ValDefn: def mk( owner: Opt[InnerSymbol], k: syntax.Val, sym: BlockMemberSymbol, rhs: Path, configOverride: Opt[Config], annotations: Ls[Annot], )(using State) : ValDefn = ValDefn(tsym = TermSymbol(k, owner, Tree.Ident(sym.nme)), sym = sym, rhs = rhs)(configOverride, annotations) /* The following explains the difference between paramsOpt, auxParams, privateFields and publicFields. paramsOpt is the main parameter list of a class, i.e. in `class A(plist0)`, `plist0` will be in paramsOpt. If there is no such parameter list, for example `class A`, then paramsOpt will be None. auxParams are the secondary parameter lists, and in the future, will be defined using the syntax class A with constructor(plist1)(plist2) = ... with the difference being that they are not printed in the class's toString function. If paramsOpt is None, the class won't have a `.class` field and must be instantiated using `new`. Otherwise, it can be instantiated using either a function call or `new A` or even `new A.class` (the latter is the recommended way when done in JS). The first parameter list will always be passed to `paramsOpt`, if it exists. Private and public fields are defined by the user using `let` and `val` in the class's constructor. Each parameter in the main parameter list **defined by the user** will automatically have an asociated public/private field. The field will be public if the parameter is marked `val`, i.e. class `A(val x)`, and private otherwise. Fields in the main parameter list created after lowering will not automatically have a field created. For example: class A(privateField0, val publicField0) with let privateField1 = 0 val publicField1 = 0 In the codegen, private and public fields are initialized by an assignment to a member symbol and a term definition respectively. The symbols must match what is defined in `privateFields` and `publicFields`. (An assignment to a flow symbol will be treated as a local symbol to the constructor, not a field assignment.) */ // * This is only supposed to be for classes, objects, and patterns; // * a lone module is represented as an empty class with a `companion` module. final case class ClsLikeDefn( owner: Opt[InnerSymbol], isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, sym: BlockMemberSymbol, ctorSym: Opt[TermSymbol], k: syntax.ClsLikeKind, paramsOpt: Opt[ParamList], auxParams: List[ParamList], parentPath: Opt[Path], methods: Ls[FunDefn], privateFields: Ls[TermSymbol], publicFields: Ls[BlockMemberSymbol -> TermSymbol], preCtor: Block, ctor: Block, companion: Opt[ClsLikeBody], bufferable: Option[Bool], )( val configOverride: Opt[Config], val annotations: Ls[Annot], ) extends Defn: require(k isnt syntax.Mod) val innerSym = S(isym.asMemSym) // * This is only supposed to be for companion module definitions (notably, not for `object`) final case class ClsLikeBody( isym: DefinitionSymbol[? <: ModuleOrObjectDef] & InnerSymbol, methods: Ls[FunDefn], privateFields: Ls[TermSymbol], publicFields: Ls[BlockMemberSymbol -> TermSymbol], ctor: Block, annotations: Ls[Annot], ): def isStaged: Bool = annotations.exists: case Annot.Modifier(Keyword.`staged`) => true case _ => false def subBlocks: Ls[Block] = ctor :: methods.flatMap(_.subBlocks) lazy val freeVars: Set[Local] = ctor.freeVars ++ methods.flatMap(_.freeVars) lazy val freeVarsLLIR: Set[Local] = ??? /* object ClsLikeBody: // TODO rm `empty`? it's currently unused def empty(id: Tree.Ident)(using State) = ClsLikeBody( isym = ModuleOrObjectSymbol(Tree.DummyTypeDef(syntax.Mod), id), methods = Nil, privateFields = Nil, publicFields = Nil, ctor = End(), ) */ final case class Handler( sym: BlockMemberSymbol, resumeSym: VarSymbol, params: Ls[ParamList], body: Block, ): lazy val freeVars: Set[Local] = body.freeVars -- params.flatMap(_.paramSyms) - sym - resumeSym lazy val freeVarsLLIR: Set[Local] = body.freeVarsLLIR -- params.flatMap(_.paramSyms) - sym - resumeSym /* Represents either unreachable code (for functions that must return a result) * or the end of a non-returning function or a REPL block */ case class End(msg: Str = "") extends BlockTail with ProductWithTail case class Unreachable(cause: Str) extends BlockTail with ProductWithTail enum Case: case Lit(lit: Literal) case Cls(cls: ClassLikeSymbol, path: Path) case Tup(len: Int, inf: Bool) /** checks field existence * @param safe true will omit the instanceof Object check */ case Field(name: Tree.Ident, safe: Bool) lazy val freeVars: Set[Local] = this match case Lit(_) => Set.empty case Cls(_, path) => path.freeVars case Tup(_, _) => Set.empty case Field(_, _) => Set.empty lazy val freeVarsLLIR: Set[Local] = this match case Lit(_) => Set.empty case Cls(_, path) => path.freeVarsLLIR case Tup(_, _) => Set.empty case Field(_, _) => Set.empty sealed trait TrivialResult extends Result sealed abstract class Result extends AutoLocated: // // * Used for debugging locations: // sealed abstract class Result extends AutoLocated with ProductWithExtraInfo: // def extraInfo: Str = toLoc.toString lazy val isPure: Bool = this match case _: Value => true case sel @ Select(q, n) => q.isPure && sel.symbol.exists(_.isPure) case Call(Value.Ref(bs: BuiltinSymbol, _), ass) if bs.isPure => ass.forall(_.forall(_.value.isPure)) case Record(mut, args) => args.forall(_.value.isPure) case Tuple(mut, elems) => elems.forall(_.value.isPure) // case Instantiate(mut, cls, args) => // TODO? case _ => false // * Note: this function is used to piece together a location; // * for the location to be valid, we should NOT have it include children whose location // * is from some different place (with a different Origin), such as the location attached to symbols. // * That's why for example, we're not adding the `l` of `Value.Ref` to the children list. protected def children: Vector[Located] = this match case Call(fun, argss) => fun +: argss.iterator.flatten.map(_.value).toVector case Instantiate(mut, cls, argss) => cls +: argss.iterator.flatten.map(_.value).toVector case Select(qual, name) => Vector.double(qual, name) case DynSelect(qual, fld, arrayIdx) => Vector.double(qual, fld) case Lambda(params, body) => Vector.single(params) case Tuple(mut, elems) => elems.iterator.map(_.value).toVector case Record(mut, elems) => elems.iterator.map(_.value).toVector case Value.Ref(l, disamb) => Vector.empty case Value.This(sym) => Vector.empty case Value.Lit(lit) => Vector.single(lit) // TODO rm Lam from values and thus the need for this method def subBlocks: Ls[Block] = this match case Call(fun, argss) => fun.subBlocks ::: argss.flatten.flatMap(_.value.subBlocks) case Instantiate(mut, cls, argss) => argss.flatten.flatMap(_.value.subBlocks) case Select(qual, name) => qual.subBlocks case Lambda(params, body) => body :: Nil case Tuple(mut, elems) => elems.flatMap(_.value.subBlocks) case _ => Nil lazy val freeVars: Set[Local] = this match case Call(fun, argss) => fun.freeVars ++ argss.flatten.flatMap(_.value.freeVars).toSet case Instantiate(mut, cls, argss) => cls.freeVars ++ argss.flatten.flatMap(_.value.freeVars).toSet case Select(qual, name) => qual.freeVars case Lambda(params, body) => body.freeVars -- params.paramSyms case Tuple(mut, elems) => elems.flatMap(_.value.freeVars).toSet case Record(mut, args) => args.flatMap(arg => arg.idx.fold(Set.empty)(_.freeVars) ++ arg.value.freeVars).toSet case Value.Ref(l, disamb) => Set(l) case Value.This(sym) => Set.empty case Value.Lit(lit) => Set.empty case DynSelect(qual, fld, arrayIdx) => qual.freeVars ++ fld.freeVars lazy val freeVarsLLIR: Set[Local] = this match case Call(fun, argss) => fun.freeVarsLLIR ++ argss.flatten.flatMap(_.value.freeVarsLLIR).toSet case Instantiate(mut, cls, argss) => cls.freeVarsLLIR ++ argss.flatten.flatMap(_.value.freeVarsLLIR).toSet case Select(qual, name) => qual.freeVarsLLIR case Lambda(params, body) => body.freeVarsLLIR -- params.paramSyms case Tuple(mut, elems) => elems.flatMap(_.value.freeVarsLLIR).toSet case Record(mut, args) => args.flatMap(arg => arg.idx.fold(Set.empty)(_.freeVarsLLIR) ++ arg.value.freeVarsLLIR).toSet case Value.Ref(l: (BuiltinSymbol | TopLevelSymbol | ClassSymbol | TermSymbol), disamb) => Set.empty case Value.Ref(l: BlockMemberSymbol, S(disamb)) => disamb.defn match case Some(d: ClassLikeDef) => Set.empty case Some(d: TermDefinition) if d.companionClass.isDefined => Set.empty case _ => Set(l) case Value.Ref(l: DefinitionSymbol[?], N) => l.defn match case Some(d: ClassLikeDef) => Set.empty case Some(d: TermDefinition) if d.companionClass.isDefined => Set.empty case _ => Set(l) case Value.Ref(l, disamb) => Set(l) case Value.This(sym) => Set.empty case Value.Lit(lit) => Set.empty case DynSelect(qual, fld, arrayIdx) => qual.freeVarsLLIR ++ fld.freeVarsLLIR // type Local = LocalSymbol type Local = Symbol /* mayRaiseEffects indicates whether this call may raise effect (algebraic effect), * regardless of whether the check for effect is inserted or not. * Note that the check for effect is inserted during HandlerLowering and setting this to true * after handler is lowered does not have any effect on the code generation. */ case class Call(fun: Path, argss: NELs[Ls[Arg]])(val isMlsFun: Bool, val mayRaiseEffects: Bool, val explicitTailCall: Bool) extends Result case class Instantiate(mut: Bool, cls: Path, argss: Ls[Ls[Arg]]) extends Result case class Lambda(params: ParamList, body: Block) extends Result case class Tuple(mut: Bool, elems: Ls[Arg]) extends Result case class Record(mut: Bool, elems: Ls[RcdArg]) extends Result sealed abstract class Path extends TrivialResult: def selN(id: Tree.Ident): Path = Select(this, id)(N) def sel(id: Tree.Ident, sym: DefinitionSymbol[?]): Path = Select(this, id)(S(sym)) def selSN(id: Str): Path = selN(new Tree.Ident(id)) def asArg = Arg(spread = N, this) def targetSymbol: Opt[DefinitionSymbol[?]] = this match case sel: Select => sel.symbol case Value.Ref(l, d) => d case _ => N /** * @param symbol The symbol representing the definition that the selection refers to, if known. */ case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[DefinitionSymbol[?]]) extends Path with ProductWithExtraInfo: def extraInfo(using DebugPrinter): Str = symbol.map(s => s"sym=${s.showAsPlain}").mkString case class DynSelect(qual: Path, fld: Path, arrayIdx: Bool) extends Path enum Value extends Path with ProductWithExtraInfo: /** * @param disamb The symbol disambiguating the definition that the reference refers to. This * exists if and only if l is a BlockMemberSymbol. */ case Ref(l: Local, disamb: Opt[DefinitionSymbol[?]]) case This(sym: InnerSymbol) // TODO rm – just use Ref case Lit(lit: Literal) override def extraInfo(using DebugPrinter): Str = this match case Ref(l, disamb) => disamb.map(s => s"disamb=${s.showAsPlain}").mkString case _ => "" object Value: object Ref: // * Some helper constructors that allow omitting the disambiguation symbol. // * If the ref itself is a DefinitionSymbol, then disambiguating it results in itself. def apply(l: DefinitionSymbol[?]): Ref = Ref(l, S(l)) // * If the ref is a symbol that does not refer to a definition, then there is no disambiguation. def apply(l: TempSymbol | VarSymbol | BuiltinSymbol): Ref = Ref(l, N) case class Arg(spread: Opt[SpreadKind], value: Path) // * `IndxdArg(S(idx), value)` represents a key-value pair in a record `(idx): value` // * `IndxdArg(N, value)` represents a spread element in a record `...value` case class RcdArg(idx: Opt[Path], value: Path): def spread: Bool = idx.isEmpty extension (k: Block => Block) def chain(other: Block => Block): Block => Block = b => k(other(b)) def rest(b: Block): Block = k(b) def transform(f: (Block => Block) => (Block => Block)) = f(k) def assign(l: Local, r: Result) = k.chain(Assign(l, r, _)) def assignScoped(l: Local, r: Result) = k.scopedVars(Set.single(l)).assign(l, r) def assignFieldN(lhs: Path, nme: Tree.Ident, rhs: Result) = k.chain(AssignField(lhs, nme, rhs, _)(N)) def break(l: LabelSymbol): Block = k.rest(Break(l)) def continue(l: LabelSymbol): Block = k.rest(Continue(l)) def define(defn: Defn) = k.chain(Define(defn, _)) def end = k.rest(End()) def ifthen(scrut: Path, cse: Case, trm: Block, els: Opt[Block] = N): Block => Block = k.chain(Match(scrut, cse -> trm :: Nil, els, _)) def label(label: LabelSymbol, loop: Bool, body: Block) = k.chain(Label(label, loop, body, _)) def ret(r: Result) = k.rest(Return(r, false)) def scopedVars(s: collection.Set[Local]) = k.chain(Scoped(s, _)) def staticif(b: Boolean, f: (Block => Block) => (Block => Block)) = if b then k.transform(f) else k def foldLeft[A](xs: Iterable[A])(f: (Block => Block, A) => Block => Block) = xs.foldLeft(k)(f) def blockBuilder: Block => Block = identity extension (l: Local) def asPath: Path = Value.Ref(l, N) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/BlockChecker.scala ================================================ package hkmc2 package codegen import scala.collection.mutable.{Map => MutMap, Set => MutSet} import sourcecode.Line import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.Message.MessageContext import semantics.* import semantics.Elaborator.State /** An invariant of the IR is that each symbol should be bound at most once * (this simplifies various analyses and transformations). * This class checks this invariant. */ class BlockChecker()(using DebugPrinter, State, Raise) extends BlockTraverser: val definedSyms = MutSet.empty[Local] private def checkSymbol(sym: Local, info: => Any): Unit = if !definedSyms.add(sym) then raise: InternalError( msg"[BlockChecker] Invalid IR: symbol ${sym.showAsPlain} is bound more than once" -> sym.toLoc :: Nil, extraInfo = S(info) ) override def applyBlock(b: Block): Unit = b match case Scoped(syms, body) => syms.foreach(checkSymbol(_, b)) case _ => () super.applyBlock(b) override def applyParamList(pl: ParamList): Unit = pl.paramSyms.foreach(checkSymbol(_, pl)) super.applyParamList(pl) end BlockChecker ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala ================================================ package hkmc2 package codegen import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer} import scala.annotation.tailrec import sourcecode.Line import mlscript.utils.*, shorthands.* import hkmc2.utils.* import semantics.* import semantics.Elaborator.State import mlscript.utils.algorithms.partitionScc /** `symbolsToPreserve` is the set of local symbols we want to leave alone; * typically, these will be top-level symbols that are being exported from a diff-test block; * we don't want to eliminate these. */ class BlockSimplifier(symbolsToPreserve: Set[Local])(using DebugPrinter, State, Config, TL): private var changed = true def registerChange = changed = true // * For debugging: // def registerChange(using line: Line) = { println(s"Change at line ${line.value}"); changed = true } def apply(prog: Program): Program = var res = prog while changed do changed = false res = new DeadCodeElim().apply(res) summon[Config].inlining.foreach: cfg => res = new Inliner.Inliner(using cfg).applyProgram(res) // TODO: other simplifications, such as inlining res end apply class DeadCodeElim() extends BlockTransformer(SymbolSubst.Id): val usedLabels = MutSet.empty[LabelSymbol] val definedVars = MutSet.empty[Local] val localVars = MutSet.empty[Local] val usedVars = MutSet.empty[Local] var tailLabels = MutSet.empty[LabelSymbol] def apply(prog: Program): Program = new BlockTraverser: applyProgram(prog) override def applyDefn(defn: Defn): Unit = defn match case cls: ClsLikeDefn => localVars ++= cls.privateFields cls.companion.foreach(localVars ++= _.privateFields) case _ => super.applyDefn(defn) override def applyPath(p: Path): Unit = p match case Value.Ref(loc, _) => usedVars += loc case _ => super.applyPath(p) override def applyBlock(b: Block): Unit = b match case Define(defn, rst) => definedVars += defn.sym case Scoped(syms, _) => localVars ++= syms case Break(lbl) => usedLabels += lbl case Continue(lbl) => usedLabels += lbl case Assign(lhs, rhs, rst) => definedVars += lhs case _ => super.applyBlock(b) applyProgram(prog) // Evaluate `thunk` with a new tail label set. This is used for evaluating any sub blocks that is not in the tail position. // For example, the match arms within a `Match` node are not in the tail position unless the rest block is `End`. // When evaluating the match arms, the tail labels should not be considered to be at tail. // The tail label set is restored after `thunk` completes. inline def nestLabelCtx[T](inline thunk: => T): T = val oldTailLabels = tailLabels tailLabels = MutSet.empty val result = thunk tailLabels = oldTailLabels result // Add the new label to the tail label set during the execution of `thunk`. inline def withTailLabel[T](newLabel: LabelSymbol)(inline thunk: => T): T = assert(!tailLabels.contains(newLabel)) tailLabels += newLabel val result = thunk tailLabels -= newLabel result // * Cached analysis to find which labels are the targets of `break`s in a given block object BrokenLabels extends CachedAnalysis[Block, Set[LabelSymbol]]: def analyzeUncached(block: Block): Set[LabelSymbol] = block match case Break(lbl) => Set.single(lbl) case _ => block.subBlocks.iterator.flatMap(analyze).toSet end BrokenLabels // * Cached analysis to find whether a block is abortive // * (i.e. always throws, returns, breaks, continues, or is unreachable) object AbortiveAnalysis extends CachedAnalysis[Block, Bool]: def analyzeUncached(block: Block): Bool = block match case Scoped(syms, body) => body.analyze case Match(scrut, arms, dflt, rest) => rest.analyze || arms.forall(_._2.analyze) && dflt.exists(_.analyze) case Begin(sub, rest) => sub.analyze || rest.analyze case Define(defn, rest) => // TODO: we could also analyse the effects of the extends clauses and companion module ctor rest.analyze case x: (Assign | AssignField | AssignDynField) => x.rest.analyze case TryBlock(sub, finallyDo, rest) => sub.analyze || rest.analyze case Label(lbl, loop, bod, rst) => bod.analyze && !BrokenLabels.analyze(bod).contains(lbl) // if `bod` breaks to `lbl`, then we must consider `rst` || rst.analyze case _: Throw | Return(_, false) | _: Unreachable | _: Continue | _: Break => true case Return(_, true) => false case _: End => false end AbortiveAnalysis val removedLocals: MutSet[Local] = MutSet.empty override def applyValue(v: Value)(k: Value => Block) = v match // * Replace with `undefined` those references to local variables that are never assigned case Value.Ref(loc, N) if localVars.contains(loc) && !definedVars.contains(loc) => registerChange if !symbolsToPreserve(loc) then removedLocals += loc k(Value.Lit(syntax.Tree.UnitLit(false))) case _ => super.applyValue(v)(k) override def applyBlock(b: Block): Block = b match // * Discard assignments to local variables that are never read (and are not preserved) case Assign(lhs, rhs, rst) if localVars(lhs) && !usedVars(lhs) && !symbolsToPreserve(lhs) => registerChange removedLocals += lhs applyResult(rhs)(r => Assign.discard(r, applyBlock(rst))) // * Remove local pure definitions that are never read (and are not preserved) case Define(defn, rest) => if !defn.isPure || !localVars(defn.sym) || usedVars(defn.sym) || symbolsToPreserve(defn.sym) then super.applyBlock(b) else registerChange removedLocals += defn.sym applyBlock(rest) // * Simplify labelled blocks case Label(lbl, loop, bod, rst) => if !BrokenLabels.analyze(bod).contains(lbl) && AbortiveAnalysis.analyze(bod) && !rst.isInstanceOf[Unreachable] then registerChange val unr = Unreachable("Rest of abortive labelled block") if usedLabels.contains(lbl) then Label(lbl, loop, nestLabelCtx(applyBlock(bod)), unr) else Begin(nestLabelCtx(applyBlock(bod)), unr) else if usedLabels.contains(lbl) then def computeBod = withTailLabel(lbl): applyBlock(bod) val lbl2 = lbl.subst val bod2 = if rst.isEmpty && !loop then computeBod else nestLabelCtx(computeBod) val rst2 = applySubBlock(rst) if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then b else Label(lbl2, loop, bod2, rst2) else registerChange Begin(nestLabelCtx(applyBlock(bod)), applyBlock(rst)) // * Remove useless break case Break(label) if tailLabels.contains(label) => tl.log(s"Break ${label} is eliminated: current tail label list is ${tailLabels}") registerChange End() case x => super.applyBlock(x) // FIXME: refactor transformers so this is not so error-prone (adding this case to `applyBlock` doesn't work) override def applyScopedBlock(b: Block): Block = b match // * Delete removed local variables from Scoped blocks case Scoped(syms, body) => val body2 = applyBlock(body) // println(s">> $body2 ${body is body2}") // println(s">> $body2 ${changed}") if changed then // if changed || (body isnt body2) then val syms2 = syms.filterNot(removedLocals) // println(s">> $syms $syms2 ${removedLocals}") if syms2.size === syms.size && (body2 is body) then b else Scoped(syms2, body2) else b case _ => super.applyScopedBlock(b) override def applyFunBodyLikeBlock(b: Block): Block = nestLabelCtx: super.applyFunBodyLikeBlock(b) override def applySubBlockNonTail(b: Block): Block = nestLabelCtx: super.applySubBlockNonTail(b) end DeadCodeElim object Inliner: object Inliner: // Reference to a function body can occur as a.f or f, this handles both cases. object TermSymbolPath: def unapply(p: Path) = p match case Value.Ref(l, S(ts: TermSymbol)) => S(ts) case s: Select => s.symbol match case S(ts: TermSymbol) => S(ts) case _ => N case _ => N def matchArgs(args: List[Arg], params: ParamList): Option[List[(VarSymbol, Result)]] = if args.exists(_.spread.isDefined) then // we require a precise match when any arg is a spread arg if params.restParam.isEmpty then return N if args.exists(_.spread.exists(!_.isEager)) then return N if args.size =/= params.params.size then return N val pairs = args.zip(params.params.iterator.map((_, false)) ++ params.restParam.map((_, true))) if pairs.exists((arg, param) => arg.spread.isDefined =/= param._2) then return N S(pairs.map((arg, param) => (param._1.sym, arg.value))) else // otherwise arg list is a simple list, and // we can perform manual array instantiation if params contain a spread param if params.restParam.isEmpty then if args.size =/= params.params.size then return N S(args.zip(params.params).map((arg, param) => (param.sym, arg.value))) else if args.size < params.params.size then return N val (fixedArgs, restArgs) = args.splitAt(params.params.size) S(fixedArgs.zip(params.params).map((arg, param) => (param.sym, arg.value)) ++ List((params.restParam.get.sym, Tuple(true, restArgs)))) /** Match multiple argument lists against multiple parameter lists. * Returns None if any arg list fails to match its corresponding param list, * or if there are fewer arg lists than param lists (partial application). * Extra arg lists beyond the param lists are ignored here and should be * handled by the caller (e.g., applied as a chained call on the result). */ def matchAllArgs(argss: List[List[Arg]], params: List[ParamList]): Option[List[(VarSymbol, Result)]] = if argss.length < params.length then return N val matchedPairs = argss.zip(params) val allMatched = matchedPairs.foldLeft[Option[List[(VarSymbol, Result)]]](S(Nil)): case (N, _) => N case (S(acc), (args, paramList)) => matchArgs(args, paramList).map(acc ::: _) allMatched import Inliner.* object InlinerAnalyzer: case class InlinerFunInfo( defn: FunDefn, isMethod: Bool, private[InlinerAnalyzer] var useCount: Int, private[InlinerAnalyzer] var disallowElimination: Bool, private[InlinerAnalyzer] var isLoopBreaker: Bool, ): def isPrivate = !symbolsToPreserve.contains(defn.sym) // Whether this function can be inlined without causing any code duplication, // i.e. the original definition can be removed and there is only one usage. def canBeInlineEliminated = isPrivate && !isMethod && useCount <= 1 && !disallowElimination && !isLoopBreaker // false def shouldBeInlined(newBlk: Block)(using Config.Inliner): Bool = if isLoopBreaker then return false // method requires the capturing of `this`, which is not supported currently. if isMethod then return false val threshold = summon[Config.Inliner].inlineThreshold newBlk.size <= threshold || canBeInlineEliminated type InlinerMap = Map[TermSymbol, InlinerFunInfo] case class FunLikeContext( curFunSym: Opt[TermSymbol], ) class Traverser extends BlockTraverser: var map: InlinerMap = Map.empty val useCnt = MutMap.WithDefault(MutMap.empty[TermSymbol, Int], _ => 0) val usages = MutMap.WithDefault(MutMap.empty[TermSymbol, List[(Option[TermSymbol], Call)]], _ => Nil) val hasNakedRef = MutMap.WithDefault(MutMap.empty[TermSymbol, Bool], _ => false) var contextList: List[FunLikeContext] = FunLikeContext(N) :: Nil def currentContext = contextList.head def currentFunSym = currentContext.curFunSym def nested(ts: Option[TermSymbol])(thunk: => Unit) = contextList = FunLikeContext(ts) :: contextList thunk val res = contextList.head contextList = contextList.tail res def addFunctionAndApplyBody(f: FunDefn, isMethod: Bool) = val r = nested(S(f.dSym)): applyBlock(f.body) map = map + (f.dSym -> InlinerFunInfo(f, isMethod, 0, false, false)) override def applyDefn(defn: Defn): Unit = defn match case f: FunDefn => addFunctionAndApplyBody(f, false) case c: ClsLikeDefn => c.parentPath.foreach(applyPath) c.methods.foreach: f => addFunctionAndApplyBody(f, true) // Note: no tracking, since `Instantiate` will not be inlined and won't cause cycles. nested(N): applySubBlock(c.preCtor) applySubBlock(c.ctor) c.companion.foreach: m => m.methods.foreach: f => addFunctionAndApplyBody(f, true) // This inherits the previous context as the module ctor is run with the constructor. applySubBlock(m.ctor) case _ => super.applyDefn(defn) override def applyResult(r: Result): Unit = r match case c @ Call(TermSymbolPath(ts), argss) => useCnt(ts) += 1 usages(ts) ::= (currentFunSym, c) argss.foreach(_.foreach(applyArg)) case _ => super.applyResult(r) override def applySymbol(sym: Symbol): Unit = sym.asTrm.foreach: ts => useCnt(ts) += 1 hasNakedRef(ts) = true def analyze(blk: Block): InlinerMap = applyBlock(blk) map.foreach: (sym, info) => info.useCount = useCnt(sym) info.disallowElimination = info.disallowElimination || hasNakedRef(sym) val edges: Buffer[(TermSymbol, TermSymbol)] = Buffer.empty usages.foreach: (sym, calls) => calls.foreach: (caller, call) => if map.contains(sym) then map(sym).disallowElimination = map(sym).disallowElimination || map(sym).defn.params.isEmpty || matchAllArgs(call.argss, map(sym).defn.params).isEmpty caller.foreach: caller => edges.append((caller, sym)) @tailrec def assignLoopBreakers(): Unit = val sccs = partitionScc(edges.filterNot((from, to) => map(to).isLoopBreaker), map.keys) if sccs.forall(_.sizeIs == 1) then return sccs.foreach: sccComp => if sccComp.sizeIs > 1 then // TODO: Score computation map(sccComp.minBy(_.uid)).isLoopBreaker = true assignLoopBreakers() edges.foreach: (from, to) => if from === to then map(from).isLoopBreaker = true assignLoopBreakers() map def walk(blk: Block): InlinerMap = Traverser().analyze(blk) import InlinerAnalyzer.InlinerMap object InlinerReplacer: class Copier(resSym: Symbol, existingMapping: Map[Symbol, Symbol])(using State): val lblSym = LabelSymbol(N, "inlinedLbl") object Copier extends SymbolRefresher(existingMapping): var currentlyNested = false override def applyFunBodyLikeBlock(b: Block): Block = val saved = currentlyNested currentlyNested = true val res = super.applyFunBodyLikeBlock(b) currentlyNested = saved res override def applyBlock(b: Block): Block = b match case Return(res, false) if !currentlyNested => applyResult(res): r2 => Assign(resSym, r2, Break(lblSym)) case _ => super.applyBlock(b) def applyBlock(blk: Block) = Label(lblSym, false, Copier.applyBlock(blk), _) class Transformer(m: InlinerMap)(using Config.Inliner, State) extends BlockTransformer(SymbolSubst()): // The call graph may be cyclic, in which case we break the infinite loop using this map by // assuring that the block corresponding to a term symbol may only be transformed once. // This map also allows the function block to be optimized on first use before its declaration. // Key not in map -> not yet analyzed // Key in map but value is None -> the optimized body is being computed // Key in map with value -> the function is optimized val newFunctionBody = MutMap.empty[TermSymbol, Option[Block]] override def applyMainBlock(main: Block): Block = super.applyMainBlock(main).flattened override def applyBlock(blk: Block) = blk match case Define(defn: FunDefn, rest) if m(defn.dSym).canBeInlineEliminated => tl.log(s"Inline elimination: ${defn.dSym}") registerChange applyBlock(rest) case _ => super.applyBlock(blk) override def applyFunDefn(fun: FunDefn): FunDefn = newFunctionBody.get(fun.dSym) match case N => newFunctionBody(fun.dSym) = N val newBdy = applyBlock(fun.body) newFunctionBody(fun.dSym) = S(newBdy) if newBdy is fun.body then fun else FunDefn(fun.owner, fun.sym, fun.dSym, fun.params, newBdy)(fun.configOverride, fun.annotations) case S(N) => // The expansion of the function body itself reaches its own definition, which is impossible lastWords("Function body contains its own definition.") case S(S(blk)) => if blk is fun.body then fun else FunDefn(fun.owner, fun.sym, fun.dSym, fun.params, blk)(fun.configOverride, fun.annotations) override def applyResult(r: Result)(k: Result => Block): Block = r match case c @ Call(TermSymbolPath(ts), argss) if m.contains(ts) && argss.nonEmpty => newFunctionBody.get(ts) .getOrElse: newFunctionBody(ts) = N val newBdy = applyBlock(m(ts).defn.body) newFunctionBody(ts) = S(newBdy) S(newBdy) .fold(super.applyResult(r)(k)): blk => val info = m(ts) if !info.shouldBeInlined(blk) then super.applyResult(r)(k) else val matchedArgs = matchAllArgs(argss, info.defn.params) matchedArgs match case N => super.applyResult(r)(k) case S(matchedArgs) => registerChange tl.log(s"Inline call for ${ts}, with args ${argss}") val extraArgss = argss.drop(info.defn.params.length) def go(acc: Block => Block, args: List[(VarSymbol, Result)], mapping: Map[Symbol, Symbol]): Block = args match case Nil => val resSym = TempSymbol(N, "inlinedVal") val copier = Copier(resSym, mapping) val newBlk = copier.applyBlock(blk) if extraArgss.isEmpty then acc(Scoped(Set.single(resSym), newBlk(k(Value.Ref(resSym))))) else acc(Scoped(Set(resSym), newBlk( k(Call(resSym.asPath, extraArgss.ne_!)(c.isMlsFun, c.mayRaiseEffects, false))))) case (sym, value) :: argRest => val newSym = VarSymbol(sym.id) go(acc.assignScoped(newSym, value), argRest, mapping + (sym -> newSym)) go(blockBuilder, matchedArgs, Map.empty) case _ => super.applyResult(r)(k) def replace(m: InlinerMap, prog: Program)(using Config.Inliner, State): Program = Transformer(m).applyProgram(prog) class Inliner(using Config.Inliner, State): def applyProgram(prog: Program): Program = val m = InlinerAnalyzer.walk(prog.main) InlinerReplacer.replace(m, prog) end Inliner end BlockSimplifier ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala ================================================ package hkmc2 package codegen import mlscript.utils.*, shorthands.* import hkmc2.utils.* import semantics.* // Default implementation: nothing is transformed class BlockTransformer(subst: SymbolSubst): given SymbolSubst = subst def applyProgram(prog: Program): Program = val imports2 = prog.imports.mapConserve(applyImport) val main2 = applyMainBlock(prog.main) if (imports2 is prog.imports) && (main2 is prog.main) then prog else Program(imports2, main2) def applyMainBlock(main: Block): Block = applyBlock(main) def applyImport(imp: Local -> Str): Local -> Str = val (l, s) = imp val l2 = applyLocal(l) if l2 is l then imp else l2 -> s def applySubBlock(b: Block): Block = applyBlock(b) /** Called for any sub block not in the `rest` position (when `rest` is nonempty). * This is not called for Label body or function body. */ def applySubBlockNonTail(b: Block): Block = applySubBlock(b) def applyBlock(b: Block): Block = b match case _: End | _: Unreachable => b case Break(lbl) => val lbl2 = lbl.subst if lbl2 is lbl then b else Break(lbl2) case Continue(lbl) => val lbl2 = lbl.subst if lbl2 is lbl then b else Continue(lbl2) case Return(res, implct) => applyResult(res): res2 => if res2 is res then b else Return(res2, implct) case Throw(exc) => applyResult(exc): exc2 => if exc2 is exc then b else Throw(exc2) case Match(scrut, arms, dflt, rst) => def applySub(b: Block) = if rst.isEmpty then applySubBlock(b) else applySubBlockNonTail(b) applyPath(scrut): scrut2 => applyListOf( arms, (tup, k) => val (cse, blk) = tup val blk2 = applySub(blk) applyCase(cse): cse2 => if (cse2 is cse) && (blk is blk2) then k(tup) else k(cse2 -> blk2) ): arms2 => val dflt2 = dflt.mapConserve(applySub) val rst2 = applySubBlock(rst) if (scrut2 is scrut) && (arms2 is arms) && (dflt2 is dflt) && (rst2 is rst) then b else Match(scrut2, arms2, dflt2, rst2) case Label(lbl, loop, bod, rst) => val lbl2 = lbl.subst val bod2 = if loop then applyScopedBlock(bod) else applySubBlock(bod) val rst2 = applySubBlock(rst) if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then b else Label(lbl2, loop, bod2, rst2) case Begin(sub, rst) => def applySub(b: Block) = if rst.isEmpty then applySubBlock(b) else applySubBlockNonTail(b) val sub2 = applySub(sub) val rst2 = applySubBlock(rst) if (sub2 is sub) && (rst2 is rst) then b else Begin(sub2, rst2) case TryBlock(sub, fin, rst) => def applySub(b: Block) = if rst.isEmpty then applySubBlock(b) else applySubBlockNonTail(b) val sub2 = applySub(sub) val fin2 = applySub(fin) val rst2 = applySubBlock(rst) if (sub2 is sub) && (fin2 is fin) && (rst2 is rst) then b else TryBlock(sub2, fin2, rst2) case Assign(l, r, rst) => applyResult(r): r2 => val l2 = applyLocal(l) val rst2 = applySubBlock(rst) if (l2 is l) && (r2 is r) && (rst2 is rst) then b else Assign(l2, r2, rst2) case b @ AssignField(l, n, r, rst) => applyResult(r): r2 => applyPath(l): l2 => val rst2 = applySubBlock(rst) val sym = b.symbol.mapConserve(_.subst) if (l2 is l) && (r2 is r) && (rst2 is rst) && (sym is b.symbol) then b else AssignField(l2, n, r2, rst2)(sym) case Define(defn, rst) => applyDefn(defn): defn2 => val rst2 = applySubBlock(rst) if (defn2 is defn) && (rst2 is rst) then b else Define(defn2, rst2) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => applyResult(rhs): rhs2 => applyPath(lhs): lhs2 => applyPath(fld): fld2 => val rest2 = applySubBlock(rest) if (lhs2 is lhs) && (fld2 is fld) && (rhs2 is rhs) && (rest2 is rest) then b else AssignDynField(lhs2, fld2, arrayIdx, rhs2, rest2) case _: Scoped => applyScopedBlock(b) // FunDefn body, Lambda body, Handler body, ctor and pCtor are considered "funBodyLike" def applyFunBodyLikeBlock(b: Block): Block = applyScopedBlock(b) // Apply to Blocks that are conceptually "scoped", which includes: // - "funBodyLike" blocks // - loop body blocks // - manually nested `Scoped` blocks // These blocks are usually instances of `Scoped`, but "funBodyLike" and loop bodies // may not be `Scoped` in practice, because empty `Scoped` blocks may be ignored def applyScopedBlock(b: Block): Block = b match case Scoped(s, bd) => val nb = applySubBlock(bd) if nb is bd then b else Scoped(s, nb) case _ => applySubBlock(b) def applyRcdArg(rcdArg: RcdArg)(k: RcdArg => Block): Block = val RcdArg(idx, p) = rcdArg val toBeIdxed = (idx2: Opt[Path]) => applyPath(p): p2 => k(if (p2 is p) && (idx2 is idx) then rcdArg else RcdArg(idx2, p2)) idx match case Some(i) => applyPath(i): i2 => toBeIdxed(if i is i2 then idx else Some(i2)) case None => toBeIdxed(idx) def applyRcdArgs(rcdArgs: List[RcdArg])(k: List[RcdArg] => Block): Block = applyListOf(rcdArgs, applyRcdArg(_)(_))(k) def applyArgs(args: List[Arg])(k: List[Arg] => Block): Block = applyListOf(args, applyArg(_)(_))(k) def applyArgss(argss: NELs[List[Arg]])(k: NELs[List[Arg]] => Block): Block = applyListOf(argss, applyArgs(_)(_)): newArgss => k(newArgss.ne_!) def applyArgss(argss: List[List[Arg]])(k: List[List[Arg]] => Block): Block = applyListOf(argss, applyArgs(_)(_))(k) def applyResult(r: Result)(k: Result => Block): Block = r match case r @ Call(fun, argss) => applyPath(fun): fun2 => applyListOf(argss, (args, k2) => applyArgs(args)(k2)): argss2 => k(if (fun2 is fun) && (argss2 is argss) then r else Call(fun2, argss2.ne_!)(r.isMlsFun, r.mayRaiseEffects, r.explicitTailCall).withLocOf(r)) case Instantiate(mut, cls, argss) => applyPath(cls): cls2 => applyListOf(argss, (args, k2) => applyArgs(args)(k2)): argss2 => k(if (cls2 is cls) && (argss2 is argss) then r else Instantiate(mut, cls2, argss2).withLocOf(r)) case l: Lambda => k(applyLam(l)) case Tuple(mut, elems) => applyArgs(elems): elems2 => k(if (elems2 is elems) then r else Tuple(mut, elems2).withLocOf(r)) case Record(mut, fields) => applyRcdArgs(fields): fields2 => k(if fields2 is fields then r else Record(mut, fields2).withLocOf(r)) case p: Path => applyPath(p)(k) def applyPath(p: Path)(k: Path => Block): Block = p match case DynSelect(qual, fld, arrayIdx) => applyPath(qual): qual2 => applyPath(fld): fld2 => k(if (qual2 is qual) && (fld2 is fld) then p else DynSelect(qual2, fld2, arrayIdx).withLocOf(p)) case p @ Select(qual, name) => applyPath(qual): qual2 => val sym2 = p.symbol.mapConserve(_.subst) k(if (qual2 is qual) && (sym2 is p.symbol) then p else Select(qual2, name)(sym2).withLocOf(p)) case v: Value => applyValue(v)(k) def applyValue(v: Value)(k: Value => Block) = v match case Value.Ref(l, disamb) => val l2 = applyLocal(l) k(if (l2 is l) then v else Value.Ref(l2, disamb).withLocOf(v)) case Value.This(sym) => val sym2 = sym.subst k(if (sym2 is sym) then v else Value.This(sym2).withLocOf(v)) case Value.Lit(lit) => k(v) def applyLocal(sym: Local): Local = sym.subst def applyFunDefn(fun: FunDefn): FunDefn = val own2 = fun.owner.mapConserve(_.subst) val sym2 = fun.sym.subst val dSym2 = fun.dSym.subst val params2 = fun.params.mapConserve(applyParamList) val body2 = applyFunBodyLikeBlock(fun.body) if (own2 is fun.owner) && (sym2 is fun.sym) && (dSym2 is fun.dSym) && (params2 is fun.params) && (body2 is fun.body) then fun else FunDefn(own2, sym2, dSym2, params2, body2)(fun.configOverride, fun.annotations) def applyValDefn(defn: ValDefn)(k: ValDefn => Block): Block = val ValDefn(tsym, sym, rhs) = defn val tsym2 = tsym.subst val sym2 = sym.subst applyPath(rhs): rhs2 => if (tsym2 is tsym) && (sym2 is sym) && (rhs2 is rhs) then k(defn) else k(ValDefn(tsym2, sym2, rhs2)(defn.configOverride, defn.annotations)) def applyPublicField(f: BlockMemberSymbol -> TermSymbol): BlockMemberSymbol -> TermSymbol = val f_1_2 = f._1.subst val f_2_2 = f._2.subst if (f_1_2 is f._1) && (f_2_2 is f._2) then f else f_1_2 -> f_2_2 def applyObjBody(defn: ClsLikeBody): ClsLikeBody = val isym2 = defn.isym.subst val methods2 = defn.methods.mapConserve(applyFunDefn) val privateFields2 = defn.privateFields.mapConserve(_.subst) val publicFields2 = defn.publicFields.mapConserve(applyPublicField) val ctor2 = applyFunBodyLikeBlock(defn.ctor) if (methods2 is defn.methods) && (privateFields2 is defn.privateFields) && (publicFields2 is defn.publicFields) && (ctor2 is defn.ctor) then defn else ClsLikeBody(isym2, methods2, privateFields2, publicFields2, ctor2, defn.annotations) def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case defn: FunDefn => k(applyFunDefn(defn)) case defn: ValDefn => applyValDefn(defn)(k) case defn @ ClsLikeDefn(own, isym, sym, ctorSym, kind, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) => val own2 = own.mapConserve(_.subst) val isym2 = isym.subst val sym2 = sym.subst val ctorSym2 = ctorSym.mapConserve(_.subst) val paramsOpt2 = paramsOpt.mapConserve(applyParamList) val auxParams2 = auxParams.mapConserve(applyParamList) def helper(parentPath2: Opt[Path]) = val methods2 = methods.mapConserve(applyFunDefn) val privateFields2 = privateFields.mapConserve(_.subst) val publicFields2 = publicFields.mapConserve(applyPublicField) val preCtor2 = applyFunBodyLikeBlock(preCtor) val ctor2 = applyFunBodyLikeBlock(ctor) val mod2 = mod.mapConserve(applyObjBody) k: if (own2 is own) && (isym2 is isym) && (sym2 is sym) && (ctorSym2 is ctorSym) && (paramsOpt2 is paramsOpt) && (auxParams2 is auxParams) && (parentPath2 is parentPath) && (methods2 is methods) && (privateFields2 is privateFields) && (publicFields2 is publicFields) && (preCtor2 is preCtor) && (ctor2 is ctor) && (mod2 is mod) then defn else ClsLikeDefn(own2, isym2, sym2, ctorSym2, kind, paramsOpt2, auxParams2, parentPath2, methods2, privateFields2, publicFields2, preCtor2, ctor2, mod2, bufferable)(defn.configOverride, defn.annotations) parentPath match case Some(pp) => applyPath(pp): pp2 => helper: if pp2 is pp then parentPath else Some(pp2) case None => helper(parentPath) def applyArg(arg: Arg)(k: Arg => Block): Block = applyPath(arg.value): val2 => k(if val2 is arg.value then arg else Arg(arg.spread, val2)) def applyParamList(pl: ParamList): ParamList = def applyParam(p: Param): Param = val sym2 = p.sym.subst if sym2 is p.sym then p else p.copy(sym = sym2) val params2 = pl.params.mapConserve(applyParam) val rest2 = pl.restParam.mapConserve(applyParam) if (params2 is pl.params) && (rest2 is pl.restParam) then pl else ParamList(pl.flags, params2, rest2) def applyCase(cse: Case)(k: Case => Block): Block = cse match case Case.Lit(lit) => k(cse) case Case.Cls(cls, path) => val cls2 = cls.subst applyPath(path): path2 => k(if (cls2 is cls) && (path2 is path) then cse else Case.Cls(cls2, path2)) case Case.Tup(len, inf) => k(cse) case Case.Field(name, safe) => k(cse) def applyHandler(hdr: Handler): Handler = val sym2 = hdr.sym.subst val resumeSym2 = hdr.resumeSym.subst val params2 = hdr.params.mapConserve(applyParamList) val body2 = applyFunBodyLikeBlock(hdr.body) if (sym2 is hdr.sym) && (resumeSym2 is hdr.resumeSym) && (params2 is hdr.params) && (body2 is hdr.body) then hdr else Handler(sym2, resumeSym2, params2, body2) def applyLam(lam: Lambda): Lambda = val params2 = applyParamList(lam.params) val body2 = applyFunBodyLikeBlock(lam.body) if (params2 is lam.params) && (body2 is lam.body) then lam else Lambda(params2, body2) def applyListOf[A](ls: List[A], f: (A, (A => Block)) => Block)(k: List[A] => Block): Block = def rec(ls: List[A], k: List[A] => Block): Block = ls match case Nil => k(Nil) case a :: t => f(a, a2 => rec(t, t2 => if (a2 is a) && (t2 is t) then k(ls) else k(a2 :: t2))) rec(ls, k) class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst): override def applyLam(lam: Lambda) = lam // Note: no need to override things like applyFunDefn, as they are only called by applyDefn override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case _: FunDefn | _: ClsLikeDefn => k(defn) case _: ValDefn => super.applyDefn(defn)(k) override def applyHandler(hdr: Handler): Handler = hdr // Does not traverse into sub-blocks or definitions. The purpose of this is is to only rewrite a block's data, i.e. // paths, values, cases, etc. within a block. Can be used in tandem with `BlockTransformer` or `BlockTransformerShallow` // to traverse sub-blocks while using this class to perform more complicated transformations on the blocks themselves. class BlockDataTransformer(subst: SymbolSubst) extends BlockTransformerShallow(subst): override def applySubBlock(b: Block): Block = b ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala ================================================ package hkmc2 package codegen import mlscript.utils.*, shorthands.* import hkmc2.utils.* import semantics.* // These all work like BlockTransformer and its derivatives, but do not rewrite the block. See BlockTransformer.scala. // Please use this instead of BlockTransformer for static analysis. class BlockTraverser: extension (sym: Symbol) inline def traverse: Unit = applySymbol(sym) def applyProgram(prog: Program): Unit = prog.imports.foreach(applyImport) applyBlock(prog.main) def applyImport(imp: Local -> Str): Unit = applyLocal(imp._1) def applySymbol(sym: Symbol): Unit = () def applySubBlock(b: Block): Unit = applyBlock(b) def applyBlock(b: Block): Unit = b match case _: End | _: Unreachable => () case Break(lbl) => applyLocal(lbl) case Continue(lbl) => applyLocal(lbl) case Return(res, implct) => applyResult(res) case Throw(exc) => applyResult(exc) case Match(scrut, arms, dflt, rst) => val scrut2 = applyPath(scrut) arms.foreach: arm => applyCase(arm._1); applySubBlock(arm._2) dflt.foreach(applySubBlock) applySubBlock(rst) case Label(lbl, loop, bod, rst) => applyLocal(lbl); applySubBlock(bod); applySubBlock(rst) case Begin(sub, rst) => applySubBlock(sub); applySubBlock(rst) case TryBlock(sub, fin, rst) => applySubBlock(sub); applySubBlock(fin); applySubBlock(rst) case Assign(l, r, rst) => applyLocal(l); applyResult(r); applySubBlock(rst) case b @ AssignField(l, n, r, rst) => applyPath(l); applyResult(r); applySubBlock(rst); b.symbol.foreach(_.traverse) case Define(defn, rst) => applyDefn(defn); applySubBlock(rst) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => applyPath(lhs) applyResult(rhs) applyPath(fld) applySubBlock(rest) case Scoped(_, body) => applySubBlock(body) def applyResult(r: Result): Unit = r match case r @ Call(fun, argss) => applyPath(fun); argss.foreach(_.foreach(applyArg)) case Instantiate(mut, cls, argss) => applyPath(cls); argss.foreach(_.foreach(applyArg)) case l @ Lambda(params, body) => applyLam(l) case Tuple(mut, elems) => elems.foreach(applyArg) case Record(mut, fields) => fields.foreach: case RcdArg(idx, value) => idx.foreach(applyPath); applyPath(value) case p: Path => applyPath(p) def applyPath(p: Path): Unit = p match case DynSelect(qual, fld, arrayIdx) => applyPath(qual); applyPath(fld) case p @ Select(qual, name) => applyPath(qual); p.symbol.foreach(_.traverse) case v: Value => applyValue(v) def applyValue(v: Value): Unit = v match case Value.Ref(l, disamb) => l.traverse disamb.foreach(_.traverse) case Value.This(sym) => sym.traverse case Value.Lit(lit) => () def applyLocal(sym: Local): Unit = sym.traverse def applyFunDefn(fun: FunDefn): Unit = fun.owner.foreach(_.traverse) fun.sym.traverse fun.dSym.traverse fun.params.foreach(applyParamList) applySubBlock(fun.body) def applyValDefn(defn: ValDefn): Unit = val ValDefn(tsym, sym, rhs) = defn tsym.owner.foreach(_.traverse); sym.traverse; applyPath(rhs) def applyDefn(defn: Defn): Unit = defn match case defn: FunDefn => applyFunDefn(defn) case defn: ValDefn => applyValDefn(defn) case ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) => own.foreach(_.traverse) isym.traverse sym.traverse ctorSym.foreach(_.traverse) paramsOpt.foreach(applyParamList) auxParams.foreach(applyParamList) parentPath.foreach(applyPath) methods.foreach(applyFunDefn) privateFields.foreach(_.traverse) publicFields.foreach: f => f._1.traverse; f._2.traverse applySubBlock(preCtor) applySubBlock(ctor) mod.foreach(applyCompanionModule) def applyCompanionModule(b: ClsLikeBody): Unit = b.isym.traverse b.methods.foreach(applyFunDefn) b.privateFields.foreach(_.traverse) b.publicFields.foreach: f => f._1.traverse; f._2.traverse applySubBlock(b.ctor) def applyArg(arg: Arg): Unit = applyPath(arg.value) def applyParamList(pl: ParamList): Unit = pl.params.foreach(_.sym.traverse) pl.restParam.foreach(_.sym.traverse) def applyCase(cse: Case): Unit = cse match case Case.Lit(lit) => () case Case.Cls(cls, path) => cls.traverse applyPath(path) case Case.Tup(len, inf) => () case Case.Field(_, _) => () def applyHandler(hdr: Handler): Unit = hdr.sym.traverse hdr.resumeSym.traverse hdr.params.foreach(applyParamList) applySubBlock(hdr.body) def applyLam(lam: Lambda): Unit = applyParamList(lam.params) applySubBlock(lam.body) class BlockTraverserShallow extends BlockTraverser: override def applyLam(lam: Lambda) = () override def applyFunDefn(fun: FunDefn): Unit = () override def applyDefn(defn: Defn): Unit = defn match case _: FunDefn | _: ClsLikeDefn => () case _: ValDefn => super.applyDefn(defn) override def applyHandler(hdr: Handler): Unit = () class BlockDataTraverser extends BlockTraverserShallow: override def applySubBlock(b: Block): Unit = () ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala ================================================ package hkmc2 package codegen import scala.annotation.tailrec import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.utils.SymbolSubst import syntax.{Literal, Tree} import semantics.* import semantics.Elaborator.{Ctx, ctx} import semantics.Elaborator.State import hkmc2.Message.MessageContext import hkmc2.syntax.Tree.DummyTypeDef class BufferableTransform()(using Ctx, State, Raise): def transform(blk: Block): Block = val transformer = new BlockTransformer(SymbolSubst.Id): override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case cls: ClsLikeDefn if cls.k is syntax.Cls => cls.bufferable.fold(super.applyDefn(defn)(k)): bufferable => val companionSym = ModuleOrObjectSymbol(DummyTypeDef(syntax.Mod), new Tree.Ident(cls.sym.nme)) val clsSizeSym = BlockMemberSymbol("size", Nil, false) val clsSizeTermSym = TermSymbol(syntax.ImmutVal, S(companionSym), new Tree.Ident("size")) val pubFieldMap: Map[Symbol, Symbol] = cls.publicFields.toMap val fields = cls.privateFields ++ cls.publicFields.map(_._2) val fieldMap: Map[Symbol, Int] = fields.zipWithIndex.toMap def mkSymbolReplacer(params: List[ParamList]): (List[ParamList], Map[Symbol, Symbol]) = val allVars = params.flatMap(_.allParams).map(_.sym) val varMap = allVars .map: sym => (sym, VarSymbol(sym.id)) .toMap def mapParam(p: Param) = Param(p.flags, varMap(p.sym), p.sign, p.modulefulness) (params.map(pl => ParamList(pl.flags, pl.params.map(mapParam), pl.restParam.map(mapParam))), varMap.toMap) def mkFieldReplacer(buf: Local, baseIdx: Local, symMap: Map[Symbol, Symbol]) = def getOffset(off: Int)(k: Path => Block): Block = val idxSymbol = new TempSymbol(N, "idx") Scoped(Set.single(idxSymbol), Assign(idxSymbol, Call(State.builtinOpsMap("+").asPath, (baseIdx.asPath.asArg :: Value.Lit(Tree.IntLit(off)).asArg :: Nil) ne_:: Nil)(true, false, false), k(DynSelect(buf.asPath.selSN("buf"), idxSymbol.asPath, true)))) def assignToOffset(off: Int, r: Result, rst: Block) = val idxSymbol = new TempSymbol(N, "idx") Scoped(Set.single(idxSymbol), Assign(idxSymbol, Call(State.builtinOpsMap("+").asPath, (baseIdx.asPath.asArg :: Value.Lit(Tree.IntLit(off)).asArg :: Nil) ne_:: Nil)(true, false, false), AssignDynField(buf.asPath.selSN("buf"), idxSymbol.asPath, true, r, applyBlock(rst)))) new BlockTransformer(SymbolSubst.Id): override def applyLocal(sym: Local): Local = symMap.getOrElse(sym, sym) override def applyBlock(b: Block): Block = b match case Assign(l, r, rst) => fieldMap.get(l).fold(super.applyBlock(b)): off => applyResult(r): r2 => assignToOffset(off, r2, applyBlock(rst)) case af @ AssignField(l, n, r, rst) => af.symbol.flatMap(pubFieldMap.get(_)).flatMap(fieldMap.get(_)) .fold(super.applyBlock(b)): off => applyResult(r): r2 => assignToOffset(off, r2, applyBlock(rst)) case Define(defn: ValDefn, rst) => fieldMap.get(defn.tsym).fold(super.applyBlock(b)): off => applyResult(defn.rhs): r2 => assignToOffset(off, r2, applyBlock(rst)) case _ => super.applyBlock(b) override def applyPath(p: Path)(k: Path => Block): Block = p match case sel: Select => sel.symbol.fold(super.applyPath(p)(k)): sym => fieldMap.get(sym).orElse(pubFieldMap.get(sym).flatMap(fieldMap.get(_))).fold(super.applyPath(p)(k)): off => getOffset(off): res => k(res) case Value.Ref(l, _) => fieldMap.get(l).fold(super.applyPath(p)(k)): off => getOffset(off): res => k(res) case _ => super.applyPath(p)(k) def transformFunDefn(f: FunDefn, isCtor: Bool): FunDefn = val buf = VarSymbol(new Tree.Ident("buf")) val idx = VarSymbol(new Tree.Ident("idx")) val (newParams, symMap) = mkSymbolReplacer(f.params) val blk = mkFieldReplacer(buf, idx, symMap).applyBlock(f.body) FunDefn(f.owner, f.sym, TermSymbol(f.dSym.k, f.dSym.owner, f.dSym.id), PlainParamList( Param(FldFlags.empty, buf, N, Modulefulness.none) :: Param(FldFlags.empty, idx, N, Modulefulness.none) :: Nil) :: newParams, if isCtor then Begin(blk, Return(idx.asPath, false)) else blk)(configOverride = f.configOverride, annotations = f.annotations) val fakeCtor = transformFunDefn(FunDefn.withFreshSymbol( S(companionSym), BlockMemberSymbol("ctor", Nil, false), cls.paramsOpt.toList, Begin(cls.preCtor, cls.ctor), )(N, annotations = Nil), true) val fakeCompanion = ClsLikeBody( companionSym, fakeCtor :: cls.methods.map(transformFunDefn(_, false)), Nil, clsSizeSym -> clsSizeTermSym :: Nil, Define(ValDefn(clsSizeTermSym, clsSizeSym, Value.Lit(Tree.IntLit(fields.size)))(N, Nil), End()), annotations = Nil, ) k: ClsLikeDefn( cls.owner, cls.isym, cls.sym, cls.ctorSym, cls.k, if bufferable then cls.paramsOpt else N, if bufferable then cls.auxParams else Nil, cls.parentPath, if bufferable then cls.methods else Nil, cls.privateFields, cls.publicFields, if bufferable then cls.preCtor else End(), if bufferable then cls.ctor else End(), S(fakeCompanion), cls.bufferable, )(cls.configOverride, cls.annotations) case _ => super.applyDefn(defn)(k) transformer.applyBlock(blk) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/CachedAnalysis.scala ================================================ package hkmc2 package codegen import scala.collection.mutable.{Map => MutMap, Set => MutSet} import mlscript.utils.*, shorthands.* import hkmc2.utils.* trait CachedAnalysis[A, R]: def analyzeUncached(a: A): R import java.util.IdentityHashMap val cache: IdentityHashMap[A, R] = new IdentityHashMap extension (a: A) def analyze: R = cache.get(a) match case null => val res = analyzeUncached(a) cache.put(a, res) res case res => res end CachedAnalysis ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala ================================================ package hkmc2 package codegen import utils.* import mlscript.utils.*, shorthands.* import semantics.* import syntax.Tree import hkmc2.codegen.flowAnalysis.* import hkmc2.syntax.Fun import scala.collection.mutable.{Set as MutSet, Map as MutMap, LinkedHashMap, Buffer} type ConcreteFunId = FunId -> InstantiationId type ConcreteCallSiteId = ResultId -> InstantiationId class DeadParamElimSolver(val constraintSolver: FlowConstraintSolver): given tl: TraceLogger = constraintSolver.tl given fState: FlowAnalysis.State = constraintSolver.fState given eState: Elaborator.State = constraintSolver.eState val collector: FlowConstraintsCollector = constraintSolver.collector val funDests: collection.Map[ProdFun, Set[ConsFun | NoCons.type]] = constraintSolver.funDests val funSrcs: collection.Map[ConsFun, Set[ProdFun | NoProd.type]] = constraintSolver.funSrcs extension (prodFun: ProdFun) def concreteId: ConcreteFunId = prodFun.funId -> prodFun.instantiationId.get extension (consFun: ConsFun) def concreteId: ConcreteCallSiteId = consFun.exprId -> consFun.instantiationId.get // handle clashes for dead param elim val (liveParams, liveCallSiteParams) = def isSyntheticRoot(prodFun: ProdFun): Bool = val instId = prodFun.instantiationId.get collector.synthesizedInstIdToFunSym.get(instId).exists: rootFunSym => prodFun.funId match case (funSym: TermSymbol, _) => (collector.funToSccRep(funSym), collector.funToSccRep(rootFunSym)) match case S(rep1) -> S(rep2) => rep1 is rep2 case _ => false case _ => false end isSyntheticRoot val prodRoots = Buffer.empty[(ProdFun, Int)] val consRoots = Buffer.empty[(ConsFun, Int)] for (prodFun, dests) <- funDests do if isSyntheticRoot(prodFun) || dests.contains(NoCons) then prodFun.params.indices.foreach: i => prodRoots += prodFun -> i else prodFun.params.zipWithIndex.foreach: case (ConsVar(s), i) => val ubs = constraintSolver.upperBounds(s.uid) if ubs.exists(!_.isInstanceOf[ConsVar]) then prodRoots += prodFun -> i case (_, i) => prodRoots += prodFun -> i for (consFun, srcs) <- funSrcs do if srcs.contains(NoProd) then consFun.params.indices.foreach: i => consRoots += consFun -> i else val minSize = srcs .collect: case p: ProdFun if p.restParam.isDefined => p.params.size .minOption minSize match case None => () case Some(s) => (s until consFun.params.size).foreach: i => consRoots += consFun -> i val result = FlowWebComputation[(ProdFun, Int), (ConsFun, Int)]( (prodFun, idx) => funDests(prodFun).collect: case c: ConsFun => (c, idx), (consFun, idx) => funSrcs(consFun).collect: case p: ProdFun => (p, idx), prodRoots, consRoots, ) (result.markedProducers, result.markedConsumers) end val val eliminableParamsById = LinkedHashMap.empty[ConcreteFunId, Set[Int]].withDefaultValue(Set.empty) val eliminableCallSiteArgsById = LinkedHashMap.empty[ConcreteCallSiteId, Set[Int]].withDefaultValue(Set.empty) for (prodFun, _) <- funDests do val eliminable = prodFun.params.indices.filterNot: i => liveParams.contains(prodFun -> i) if eliminable.nonEmpty then eliminableParamsById.get(prodFun.concreteId) match case None => eliminableParamsById(prodFun.concreteId) = eliminable.toSet case S(existing) => assert(existing.toList.sorted === eliminable) for (consFun, _) <- funSrcs do val eliminable = consFun.params.indices.filterNot: i => liveCallSiteParams.contains(consFun -> i) if eliminable.nonEmpty then eliminableCallSiteArgsById.get(consFun.concreteId) match case None => eliminableCallSiteArgsById(consFun.concreteId) = eliminable.toSet case S(existing) => assert(existing.toList.sorted === eliminable) if tl.doTrace then def showRefSite(resultId: ResultId): Str = resultId.getReferredFun match case Some(fun) => s"${fun.nme}@$resultId" case None => resultId.getResult match case Value.Ref(sym, _) => s"${sym.nme}@$resultId" case res => s"$res@$resultId" end showRefSite def showInstId(instId: InstantiationId): Str = if instId.isEmpty then "" else instId.map(showRefSite).mkString(".") end showInstId def showProdFun(prodFun: ProdFun): Str = def showFunId(funId: FunId): Str = funId match case (funSym: Symbol, whichParamList) => s"${funSym.nme}#$whichParamList" case exprId: ResultId => exprId.getResult match case Lambda(_, _) => s"lambda@$exprId" case _ => showRefSite(exprId) val inst = prodFun.instantiationId.fold("")(instId => s" @ ${showInstId(instId)}") s"prodfun ${showFunId(prodFun.funId)}$inst" end showProdFun assert(eliminableCallSiteArgsById.nonEmpty === eliminableParamsById.nonEmpty) tl.log(">>> dead-param-elim results >>>") for (prodFun, _) <- funDests.toSeq.sortBy(pair => showProdFun(pair._1)) do eliminableParamsById.get(prodFun.concreteId) match case Some(elim) => tl.log(s"${showProdFun(prodFun)} -> eliminable: {${elim.toSeq.sorted.mkString(", ")}}") case _ => () tl.log("<<< dead-param-elim results <<<") end if end DeadParamElimSolver class Rewrite(val deadParamElimSolver: DeadParamElimSolver)(using Raise): def apply(): Program = if newBody is pre.pgrm.main then pre.pgrm else Program(pre.pgrm.imports, newBody) val constraintSolver = deadParamElimSolver.constraintSolver val collector = deadParamElimSolver.collector given tl: TraceLogger = constraintSolver.tl given fState: FlowAnalysis.State = constraintSolver.fState given eState: Elaborator.State = constraintSolver.eState given pre: FlowPreAnalyzer = constraintSolver.preAnalyzer private val _symSubst = SymbolSubst.Id val newPolyFnSyms = LinkedHashMap.empty[InstantiationId, Map[TermSymbol, (BlockMemberSymbol, TermSymbol)]] // compute necessary poly fun syms locally { def mkNewPolyFnSyms(instId: InstantiationId): Unit = val referredFun = instId.last.getReferredFun.get val groupFuns = collector.funToSccGroups(referredFun) newPolyFnSyms.getOrElseUpdate( instId, groupFuns .map: f => val name = instId.mkFunName + s"$$${f.nme}" f -> ( new BlockMemberSymbol(name, Nil, true), new TermSymbol(Fun, N, Tree.Ident(name))) .toMap) end mkNewPolyFnSyms for (_, instId) <- deadParamElimSolver.eliminableParamsById.keysIterator ++ deadParamElimSolver.eliminableCallSiteArgsById.keysIterator path <- instId.inits if path.nonEmpty && !collector.synthesizedInstIdToFunSym.contains(path) do mkNewPolyFnSyms(path) } class Rewriter(instId: InstantiationId) extends BlockTransformer(_symSubst): private val activeEliminatedParams = MutSet.empty[VarSymbol] private def withEliminatedParams[A](removed: Set[VarSymbol])(thunk: => A): A = if removed.isEmpty then thunk else activeEliminatedParams ++= removed try thunk finally activeEliminatedParams --= removed private def filterParamList(pl: ParamList, eliminable: Set[Int]): (ParamList, Set[VarSymbol]) = if eliminable.isEmpty then pl -> Set.empty else val removed = MutSet.empty[VarSymbol] val keptParams = Buffer.empty[Param] pl.params.zipWithIndex.foreach: case (param, i) => if eliminable(i) then removed.add(param.sym) else keptParams.append(param) ParamList(pl.flags, keptParams.toList, pl.restParam) -> removed.toSet private def filterFunParams(funSym: TermSymbol, params: Ls[ParamList]): (Ls[ParamList], Set[VarSymbol]) = val removed = MutSet.empty[VarSymbol] var changed = false val params2 = params.zipWithIndex.map: case (pl, whichParamList) => val (pl2, removed2) = filterParamList(pl, deadParamElimSolver.eliminableParamsById((funSym, whichParamList), instId)) if pl2 isnt pl then changed = true removed ++= removed2 pl2 (if changed then params2 else params) -> removed.toSet def rewriteFunBody(funSym: TermSymbol, params: Ls[ParamList], body: Block): Block = val (_, removed) = filterFunParams(funSym, params) withEliminatedParams(removed): applyFunBodyLikeBlock(body) override def applyPath(p: Path)(k: Path => Block): Block = def newRefId(refId: ResultId, refSym: TermSymbol): InstantiationId = instId match case Nil => refId :: Nil case pathTo :+ called => val lastRefedSymbol = called.getReferredFun.get val funToSccRepMap = collector.funToSccRep (funToSccRepMap(lastRefedSymbol), funToSccRepMap(refSym)) match case (Some(a), Some(b)) if a is b => instId case _ => instId :+ refId end newRefId p match case ref@FunRef(f) if newPolyFnSyms.isDefinedAt(newRefId(ref.uid, f)) => val (bms, tSym) = newPolyFnSyms(newRefId(ref.uid, f))(f) k(Value.Ref(bms, S(tSym))) case _ => super.applyPath(p)(k) override def applyValue(v: Value)(k: Value => Block): Block = v match case ref@Value.Ref(l: VarSymbol, _) if activeEliminatedParams(l) => k(Value.Lit(Tree.UnitLit(false)).withLocOf(ref)) case _ => super.applyValue(v)(k) override def applyBlock(b: Block): Block = b match case Assign(lhs: VarSymbol, rhs, rst) if activeEliminatedParams(lhs) => applyResult(rhs): rhs2 => Assign.discard(rhs2, applySubBlock(rst)) case _ => super.applyBlock(b) override def applyResult(r: Result)(k: Result => Block): Block = def rewriteArgs(args: Ls[Arg], eliminable: Set[Int])(k: Ls[Arg] => Block): Block = if eliminable.isEmpty then applyArgs(args)(k) else def rec(rest: Ls[Arg], idx: Int, changed: Bool, accRev: Ls[Arg]): Block = rest match case Nil => k(if changed then accRev.reverse else args) case arg :: tl if eliminable(idx) => rec(tl, idx + 1, true, accRev) case arg :: tl => applyArg(arg): arg2 => rec(tl, idx + 1, changed || !(arg2 is arg), arg2 :: accRev) rec(args, 0, false, Nil) end rewriteArgs r match case c@Call(fun, args :: restArgss) if args.forall(_.spread.isEmpty) => val eliminable = deadParamElimSolver.eliminableCallSiteArgsById(c.uid, instId) applyPath(fun): fun2 => rewriteArgs(args, eliminable): args2 => k( if (fun2 is fun) && (args2 is args) then c else Call(fun2, args2 ne_:: restArgss)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall).withLocOf(c) ) case i@Instantiate(mut, cls, args :: restArgss) if args.forall(_.spread.isEmpty) => val eliminable = deadParamElimSolver.eliminableCallSiteArgsById(i.uid, instId) applyPath(cls): cls2 => rewriteArgs(args, eliminable): args2 => k( if (cls2 is cls) && (args2 is args) then i else Instantiate(mut, cls2, args2 :: restArgss).withLocOf(i) ) case _ => super.applyResult(r)(k) override def applyLam(lam: Lambda): Lambda = val (params2, removed) = filterParamList(lam.params, deadParamElimSolver.eliminableParamsById(lam.uid, instId)) val body2 = withEliminatedParams(removed): applyFunBodyLikeBlock(lam.body) if (params2 is lam.params) && (body2 is lam.body) then lam else Lambda(params2, body2) override def applyFunDefn(fun: FunDefn): FunDefn = val own2 = fun.owner.mapConserve(_.subst) val sym2 = fun.sym.subst val dSym2 = fun.dSym.subst val (params2, removed) = filterFunParams(fun.dSym, fun.params) val body2 = withEliminatedParams(removed): applyFunBodyLikeBlock(fun.body) if (own2 is fun.owner) && (sym2 is fun.sym) && (dSym2 is fun.dSym) && (params2 is fun.params) && (body2 is fun.body) then fun else FunDefn(own2, sym2, dSym2, params2, body2)(fun.configOverride, fun.annotations) end Rewriter val newBody = def filterParamList(pl: ParamList, eliminable: Set[Int]): ParamList = if eliminable.isEmpty then pl else ParamList( pl.flags, pl.params.zipWithIndex.collect: case (param, i) if !eliminable(i) => param, pl.restParam ) end filterParamList def filterFunParams(funSym: TermSymbol, params: Ls[ParamList], instId: InstantiationId): Ls[ParamList] = params.zipWithIndex.map: case (pl, whichParamList) => filterParamList(pl, deadParamElimSolver.eliminableParamsById((funSym, whichParamList), instId)) end filterFunParams class RefreshSymbol(existingMapping: Map[Symbol, Symbol]) extends SymbolRefresher(existingMapping): override def applyValue(v: Value)(k: Value => Block): Block = v match case Value.Ref(l, x) => pre.res.modSymToBms.get(l) match case Some(bms) => k(Value.Ref(bms, l.asMod)) case None => super.applyValue(v)(k) case _ => super.applyValue(v)(k) end RefreshSymbol def makeRefreshedParams(params: Ls[ParamList]): (Ls[ParamList], Map[Symbol, Symbol]) = val refreshParamMap = MutMap.empty[Symbol, Symbol] val refreshedParams = params.map: case ParamList(flags, params, restParam) => val params2 = params.map: case p => val newSym = new VarSymbol(Tree.Ident(p.sym.name)) refreshParamMap(p.sym) = newSym Param(p.flags, newSym, p.sign, p.modulefulness) val rest2 = restParam.map: case p => val newSym = new VarSymbol(Tree.Ident(p.sym.name)) refreshParamMap(p.sym) = newSym Param(p.flags, newSym, p.sign, p.modulefulness) ParamList(flags, params2, rest2) refreshedParams -> refreshParamMap.toMap end makeRefreshedParams val newPolyFuns = for (instId, funSymMap) <- newPolyFnSyms (referringFun, (bms, tSym)) <- funSymMap.toList.sortBy(_._1.uid) yield val fDefn = pre.res.funSymToFunDefn(referringFun) val filteredParams = filterFunParams(fDefn.dSym, fDefn.params, instId) val transformedBody = new Rewriter(instId).rewriteFunBody(fDefn.dSym, fDefn.params, fDefn.body) val (refreshedParams, refreshParamMap) = makeRefreshedParams(filteredParams) val bodyWithCorrectSymbols = new RefreshSymbol(refreshParamMap).applyBlock(transformedBody) FunDefn( N, bms, tSym, refreshedParams, bodyWithCorrectSymbols)(fDefn.configOverride, fDefn.annotations) val inplaceRewrittenFunBodies = Map.from[TermSymbol, Block]: for (selfInstId, funSym) <- collector.synthesizedInstIdToFunSym yield val fDefn = pre.res.funSymToFunDefn(funSym) funSym -> new Rewriter(selfInstId).rewriteFunBody(funSym, fDefn.params, fDefn.body) val newMainBody = object mainRewriter extends Rewriter(Nil): override def applyFunDefn(fun: FunDefn): FunDefn = inplaceRewrittenFunBodies.get(fun.dSym) match case Some(rewrittenBody) => FunDefn(fun.owner, fun.sym, fun.dSym, fun.params, rewrittenBody)(fun.configOverride, fun.annotations) case None => super.applyFunDefn(fun) Scoped( Set.from(newPolyFuns.map(_.sym)), mainRewriter.applyBlock(pre.pgrm.main)) newPolyFuns.foldRight(newMainBody): (fdef, rest) => Define(fdef, rest) end newBody end Rewrite object DeadParamElim: def apply(p: Program)(using cfg: Config, tl: TL, raise: Raise, eState: Elaborator.State, ): Program = cfg.deadParamElim match case None => p case Some(dCfg) => val outerTl = tl (new TraceLogger: override def doTrace: Bool = dCfg.debug override def emitDbg(str: Str): Unit = outerTl.emitDbg(s"dead-param-elim > $str") ).givenIn: val flowAnalysisRes = FlowAnalysis(p, mono = dCfg.mono) val deadParamElimSolver = new DeadParamElimSolver(flowAnalysisRes) if deadParamElimSolver.eliminableParamsById.isEmpty && deadParamElimSolver.eliminableCallSiteArgsById.isEmpty then p else val rewrite = new Rewrite(deadParamElimSolver) rewrite() ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala ================================================ package hkmc2 package codegen import mlscript.utils.*, shorthands.* import utils.* import semantics.* import syntax.Tree import semantics.Elaborator.{ctx, State} import hkmc2.Message.MessageContext import collection.mutable.HashMap class FirstClassFunctionTransformer(using Elaborator.State, Elaborator.Ctx, Raise) extends BlockTransformer(new SymbolSubst): // Anonymous lambdas' parameter lists cannot be retrieved from the term symbol private val funDefns = HashMap.empty[BlockMemberSymbol, FunDefn] class CollectFunDefns extends BlockTraverser: override def applyFunDefn(fun: FunDefn) = funDefns += (fun.sym -> fun) super.applyFunDefn(fun) private def generateFCFunctionClass(p: Path, params: ParamList) = val clsSym = ClassSymbol( syntax.Tree.DummyTypeDef(syntax.Cls), syntax.Tree.Ident("Function$") ) val defSym = new BlockMemberSymbol("Function$", Nil, false) val callDef = FunDefn.withFreshSymbol(Some(clsSym), new BlockMemberSymbol("call", Nil, true), params :: Nil, Return(Call(p, params.params.map(_.sym.asPath.asArg) ne_:: Nil)(true, false, false), false))(N, annotations = Nil) ClsLikeDefn(None, clsSym, defSym, None, syntax.Cls, None, Nil, Some(Select(Value.Ref(State.globalThisSymbol, Some(State.globalThisSymbol)), Tree.Ident("Function"))(Some(ctx.builtins.Function))), callDef :: Nil, Nil, Nil, Return(Call(Value.Ref(State.builtinOpsMap("super")), Nil ne_:: Nil)(false, false, false), true), End(), None, None)(N, annotations = Nil) private def getParamList(l: BlockMemberSymbol): Option[ParamList] = funDefns.get(l) match case Some(fd) => fd.params.headOption case _ => l.tsym.flatMap(getParamList) private def getParamList(ts: TermSymbol): Option[ParamList] = ts.defn.flatMap(_.params.headOption) override def applyPath(p: Path)(k: Path => Block): Block = p match case ref @ Value.Ref(l: BlockMemberSymbol, disamb) => disamb match case Some(s: TermSymbol) if s.k is syntax.Fun => val params = getParamList(l).getOrElse(lastWords(s"Cannot get ${l.nme}'s parameter list.")) val clsDef = generateFCFunctionClass(ref, params) val tmp = new TempSymbol(None) val cls = Value.Ref(clsDef.sym, Some(clsDef.isym)) Scoped(Set(clsDef.sym, tmp), Define(clsDef, Assign(tmp, Instantiate(false, cls, Nil :: Nil), k(Value.Ref(tmp, None))))) case Some(_) => k(p) case None => lastWords(s"${l.nme}'s disamb cannot be empty.") case sel: Select => sel.symbol match case Some(s: TermSymbol) if (s.k is syntax.Fun) => val params = getParamList(s).getOrElse(lastWords(s"Cannot get ${s.nme}'s parameter list.")) val clsDef = generateFCFunctionClass(sel, params) val tmp = new TempSymbol(None) val cls = Value.Ref(clsDef.sym, Some(clsDef.isym)) Scoped(Set(clsDef.sym, tmp), Define(clsDef, Assign(tmp, Instantiate(false, cls, Nil :: Nil), k(Value.Ref(tmp, None))))) case Some(_) => k(p) case _ => raise(ErrorReport(msg"Cannot determine if ${sel.name.name} is a function." -> sel.toLoc :: Nil, source = Diagnostic.Source.Compilation)) k(p) case _ => k(p) private def pathStartsWith(p: Path, symbol: Local): Bool = p match case Value.Ref(l, _) => l is symbol case Select(p, _) => pathStartsWith(p, symbol) case DynSelect(p, _, _) => pathStartsWith(p, symbol) case _ => false override def applyResult(r: Result)(k: Result => Block): Block = r match case c @ Call(fun, argss) => applyListOf(argss, (args, k2) => applyArgs(args)(k2)): argss2 => def call(f: Path) = Call(f, argss2.ne_!)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) fun match case ref @ Value.Ref(sym, _) => sym match case _: VarSymbol | _: TempSymbol => k(call(ref.selSN("call"))) case _ => k(call(fun)) case sel: Select => sel.symbol match case Some(s: TermSymbol) => if s.k is syntax.Fun then k(call(fun)) else k(call(sel.selSN("call"))) case _ => raise(ErrorReport(msg"Cannot determine if ${sel.name.name} is a function object." -> fun.toLoc :: Nil, source = Diagnostic.Source.Compilation)) k(call(fun)) case s: DynSelect => raise(ErrorReport(msg"Cannot determine if the dynamic selection is a function object." -> s.toLoc :: Nil, source = Diagnostic.Source.Compilation)) k(call(fun)) case _ => k(call(fun)) case _: Lambda => lastWords("Lambda functions should be rewritten into function definitions first.") case _ => super.applyResult(r)(k) class DesugarMultipleParamList extends BlockTransformer(new SymbolSubst): override def applyFunDefn(fd: FunDefn): FunDefn = fd.params match case Nil => fd case _ :: Nil => fd case head :: tail => def rec(params: List[ParamList]): Block = params match case head :: rest => val newBody = rec(rest) val funSym = new BlockMemberSymbol("lambda$", Nil, false) val funDef = FunDefn.withFreshSymbol(None, funSym, head :: Nil, newBody)(N, annotations = Nil) Scoped(Set(funSym), Define(funDef, Return(Value.Ref(funDef.sym, Some(funDef.dSym)), false))) case Nil => fd.body FunDefn.withFreshSymbol(fd.owner, fd.sym, head :: Nil, rec(tail))(fd.configOverride, fd.annotations) def transform(b: Block): Block = val desugared = new DesugarMultipleParamList().applyBlock(b) new CollectFunDefns().applyBlock(desugared) applyBlock(desugared) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala ================================================ package hkmc2 package codegen import scala.annotation.tailrec import scala.collection.mutable import scala.util.boundary import sourcecode.{ Line, FileName, Name } import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.utils.SymbolSubst import hkmc2.Message.MessageContext import syntax.{Literal, Tree} import semantics.* import semantics.Elaborator.ctx import semantics.Elaborator.State import hkmc2.Config.EffectHandlers object HandlerLowering: private val pcIdent: Tree.Ident = Tree.Ident("pc") private val nextIdent: Tree.Ident = Tree.Ident("next") private val lastIdent: Tree.Ident = Tree.Ident("last") private val contTraceIdent: Tree.Ident = Tree.Ident("contTrace") private def unit = Value.Lit(Tree.UnitLit(true)) private def intLit(i: BigInt) = Value.Lit(Tree.IntLit(i)) private def locToStr(loc: Loc) = val (line, _, col) = loc.origin.fph.getLineColAt(loc.spanStart) Value.Lit(Tree.StrLit(s"${loc.origin.fileName.last}:${line + loc.origin.startLineNum - 1}:$col")) extension (p: Path) def pc = p.selN(pcIdent) def value = p.selN(Tree.Ident("value")) def next = p.selN(nextIdent) def last = p.selN(lastIdent) def contTrace = p.selN(contTraceIdent) private case class LinkState(res: Local, cls: Path, uid: Path) type FnOrCls = Either[BlockMemberSymbol, DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol] private enum HandlerCtx: case FunctionLike(ctx: FunctionCtx) case Ctor case ModCtor(trulyNested: Bool) case TopLevel def inCtor = this === Ctor || this.isInstanceOf[ModCtor] def inTopLevel = this === TopLevel def allowDefn = inTopLevel || this.isInstanceOf[ModCtor] def innerDefIsTrulyNested = this match case FunctionLike(_) => true case Ctor => true case ModCtor(trulyNested) => trulyNested case TopLevel => false // currentFun: path to the current function for resumption // thisPath: path to `this` binding if the function is a method, `this` will be rebinded on resumption private case class FunctionCtx(currentFun: Path, thisPath: Option[Path], resumeInfo: ResumeInfo, debugInfo: DebugInfo, inGetter: Bool): def doUnwind(loc: Value, stateId: BigInt, restoreList: List[Local])(using paths: HandlerPaths) = Return(Call(paths.unwindPath, ( currentFun :: intLit(stateId) :: loc :: debugInfo.debugInfoPath :: thisPath.getOrElse(unit) :: resumeInfo.argLists ++: (intLit(restoreList.length) :: restoreList.map(_.asPath)) ).map(_.asArg) ne_:: Nil)(true, true, false), false) // argLists: length-encoded argument list used for resumption. // currentLocals: All locals to be saved and reloaded, this cannot include any variables in outer scopes // currentStackSafetySym: The symbol to be used for stack safety private case class ResumeInfo( argLists: List[Path], currentLocals: List[Local], currentStackSafetySym: FnOrCls, ) private case class DebugInfo( debugNme: Str, debugInfoPath: Path, ) type StateId = BigInt import HandlerLowering.* class HandlerPaths(using Elaborator.State): val runtimePath: Path = State.runtimeSymbol.asPath val contClsPath: Path = runtimePath.selSN("FunctionContFrame").selSN("class") val mkEffectPath: Path = runtimePath.selSN("mkEffect") val handleBlockImplPath: Path = runtimePath.selSN("handleBlockImpl") val stackDelayClsPath: Path = runtimePath.selSN("StackDelay") val topLevelEffectPath: Path = runtimePath.selSN("topLevelEffect") val illegalEffectPath: Path = runtimePath.selSN("illegalEffect") val enterHandleBlockPath: Path = runtimePath.selSN("enterHandleBlock") val stackDepthIdent = new Tree.Ident("stackDepth") val stackDepthPath: Path = runtimePath.selN(stackDepthIdent) val fnLocalsPath: Path = runtimePath.selSN("FnLocalsInfo").selSN("class") val localVarInfoPath: Path = runtimePath.selSN("LocalVarInfo").selSN("class") val curEffect: Path = runtimePath.selSN("curEffect") val unwindPath: Path = runtimePath.selSN("unwind") val resetEffects: Path = runtimePath.selSN("resetEffects") val resumePc: Path = runtimePath.selSN("resumePc") val resumeIdx: Path = runtimePath.selSN("resumeIdx") val resumeValueIdent = new Tree.Ident("resumeValue") val resumeValue: Path = runtimePath.selN(resumeValueIdent) type StackSafetyMap = collection.Map[FnOrCls, (Int, Block)] class HandlerLowering(paths: HandlerPaths, opt: EffectHandlers)(using TL, Raise, Elaborator.State, Elaborator.Ctx): private def freshTmp(dbgNme: Str = "tmp") = new TempSymbol(N, dbgNme) private def freshLabel(nme: Str) = new LabelSymbol(N, nme) private def rtThrowMsg(msg: Str) = Throw( Instantiate(mut = false, State.globalThisSymbol.asPath.selN(Tree.Ident("Error")), (Value.Lit(Tree.StrLit(msg)).asArg :: Nil) :: Nil) ) object PureCall: def apply(fun: Path, args: List[Path]) = Call(fun, args.map(Arg(N, _)) ne_:: Nil)(true, false, false) def unapply(res: Result) = res match case Call(fun, args :: Nil) => args.foldRight[Opt[List[Path]]](S(Nil)): (arg, acc) => acc.flatMap: acc => arg match case Arg(N, p) => S(p :: acc) case _ => N .map((fun, _)) case _ => N object StateTransition: private val transitionSymbol = freshTmp("transition") def apply(uid: StateId) = Return(PureCall(Value.Ref(transitionSymbol), List(Value.Lit(Tree.IntLit(uid)))), false) def unapply(blk: Block) = blk match case Return(PureCall(Value.Ref(`transitionSymbol`, _), List(Value.Lit(Tree.IntLit(uid)))), false) => S(uid) case _ => N object Unwind: private val unwindSymbol = freshTmp("unwind") def apply(uid: StateId, loc: Value) = Return(PureCall(Value.Ref(unwindSymbol), List(Value.Lit(Tree.IntLit(uid)), loc)), false) def unapply(blk: Block) = blk match case Return(PureCall(Value.Ref(`unwindSymbol`, _), List(Value.Lit(Tree.IntLit(uid)), loc: Value)), false) => S(uid, loc) case _ => N abstract class LazyId extends Lazy[StateId]: def isUsed: Bool = !isEmpty def transitionOrBlk(blk: => Block) = if isEmpty then blk else StateTransition(force_!) private class IdAllocator: var id: Int = 0 def apply() = val tmp = id id += 1 tmp // blk: the block of code within this state private case class BlockPartition(blk: Block, resumable: Bool) private case class PartitionedBlock( entry: StateId, states: Map[StateId, BlockPartition], allocId: IdAllocator, containsCall: Bool, containsError: Bool ) object EffectfulResult: def unapply(r: Result) = r match case c: Call if c.mayRaiseEffects => S(r) case _: Instantiate if opt.checkInstantiateEffect => S(r) case _ => N private def partitionBlock(blk: Block): PartitionedBlock = val result = mutable.HashMap.empty[StateId, BlockPartition] val labelIds = mutable.HashMap.empty[LabelSymbol, (LazyId, LazyId)] val allocId = new IdAllocator() var containsCall = false var containsError = false // * blk: The block to transform // * partitioned: whether we are already in a partitioned state // * if we are not partitioned, we do not need to jump to afterEnd, // * this is because we are still in the original block, which shares // * the same code path. // * labelIds: maps label IDs to the state at the start of the label and the state after the label // * afterEnd: The block that follows End, None if the function ends. def go(blk: Block)(using afterEnd: Option[LazyId], partitioned: Bool): Block = boundary: // First check if the current block contain any non trivial call, if so we need a partition def forceId(blk: Block, resumable: Bool): StateId = blk match case StateTransition(uid) => if !result(uid).resumable && resumable then result(uid) = BlockPartition(result(uid).blk, true) uid case _ => val id = allocId() result(id) = BlockPartition(blk, resumable) id def doNewEffectPartition(res: Result, rst: Block) = val stateId = forceId(go(rst)(using partitioned = true), true) val newBlock = blockBuilder .assignFieldN(paths.runtimePath, paths.resumeValueIdent, res) .ifthen( paths.curEffect, Case.Lit(Tree.UnitLit(true)), End(), S(Unwind(stateId, res.toLoc.fold(unit)(locToStr(_)))) ) .rest(StateTransition(stateId)) boundary.break(newBlock) class RestLazyId(rst: Block) extends LazyId: def compute: StateId = forceId(go(rst)(using partitioned = true), false) def transitionSoft: Block = transitionOrBlk(go(rst)) val nonTrivialBlockChecker = new BlockDataTransformer(SymbolSubst.Id): override def applyBlock(b: Block) = b match // Special handling for tail calls case Return(c @ Call(fun, args), false) => containsCall = true b // Prevents the recursion into applyResult case _ => super.applyBlock(b) override def applyResult(r: Result)(k: Result => Block) = r match case EffectfulResult(r) => containsCall = true doNewEffectPartition(r, k(paths.resumeValue)) case _ => super.applyResult(r)(k) // If current block contains direct effectful result the following call will early exit. nonTrivialBlockChecker.applyBlock(blk) blk match case Match(scrut, arms, dflt, rest) => val restId = RestLazyId(rest) val newArms = arms.map((cse, blkk) => (cse, go(blkk)(using afterEnd = S(restId)))) val newDflt = dflt.map(blkk => go(blkk)(using afterEnd = S(restId))) Match(scrut, newArms, newDflt, restId.transitionSoft) case Label(label, loop, body, rest) => val restId = RestLazyId(rest) val startId = new LazyId: def compute = allocId() labelIds(label) = (startId, restId) val newBody = go(body)(using S(restId)) if startId.isUsed then // We break down the label, and force the usage of rest so that all Break will be rewritten later result(startId.force_!) = BlockPartition(Begin(newBody, StateTransition(restId.force_!)), false) StateTransition(startId.force_!) else Label(label, loop, newBody, restId.transitionSoft) case Break(label) => val (start, end) = labelIds.get(label) match case N => raise(InternalError( msg"Could not find label '${label.nme}'" -> label.toLoc :: Nil, source = Diagnostic.Source.Compilation)) return blk case S(value) => value if partitioned then StateTransition(end.force_!) else // We might still need to do a StateTransition if the label is broken down. // This is done afterwards in a replacement pass. Break(label) case Continue(label) => val (start, end) = labelIds.get(label) match case N => raise(InternalError( msg"Could not find label '${label.nme}'" -> label.toLoc :: Nil, source = Diagnostic.Source.Compilation)) return blk case S(value) => value if partitioned then StateTransition(start.force_!) else // Same as above. Continue(label) case Begin(sub, rest) => val restId = RestLazyId(rest) val newSub = go(sub)(using afterEnd = S(restId)) Begin(newSub, restId.transitionSoft) case u: Unreachable => u case End(_) => if partitioned then afterEnd.fold(blk)(id => StateTransition(id.force_!)) else blk // Currently, implicit returns are only used in top level and tail call of constructor // The former case never enters the partitioning function, so it must be the later case here. // We no longer handle the later case, hence we can ignore this case. // case Return(_, true) => afterEnd match // case None => End() // case Some(id) => StateTransition(id) // identity cases case Define(defn, rest) => Define(defn, go(rest)) case Assign(lhs, rhs, rest) => Assign(lhs, rhs, go(rest)) case blk @ AssignField(lhs, nme, rhs, rest) => AssignField(lhs, nme, rhs, go(rest))(blk.symbol) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => AssignDynField(lhs, fld, arrayIdx, rhs, go(rest)) case _: Return => blk // ignored cases case TryBlock(sub, finallyDo, rest) => containsError = true Lowering.fail(ErrorReport( msg"`try`-`finally` blocks are not currently supported with effect handlers enabled." -> N :: Nil, source = Diagnostic.Source.Compilation)) case Throw(_) => blk case Scoped(_, body) => go(body) // PreHandlerLowering val initId = allocId() // Note: initial part will only be resumed if stack safety is on. val initPart = BlockPartition(go(blk)(using N, false), opt.stackSafety.isDefined) result(initId) = initPart val replaceStaleLabels = new BlockTransformerShallow(SymbolSubst.Id): override def applyBlock(b: Block): Block = b match case Break(label) if labelIds(label)._2.isUsed => StateTransition(labelIds(label)._2.force_!) case Continue(label) if labelIds(label)._1.isUsed => StateTransition(labelIds(label)._2.force_!) case _ => super.applyBlock(b) val newMap = Map.from(result.map: (id, part) => id -> BlockPartition(replaceStaleLabels.applyBlock(part.blk), part.resumable)) PartitionedBlock(initId, newMap, allocId, containsCall, containsError) private def computeRestoreList(parts: PartitionedBlock)(using ctx: FunctionCtx): List[Local] = // We compute the restore list by taking the union of live variables at each resumption point // The live variable analysis uses a classic work list approach val locals = ctx.resumeInfo.currentLocals val localSetMap = locals.zipWithIndex.toMap val allocId = parts.allocId type PartitionVarInfo = (used: mutable.BitSet, assigned: mutable.BitSet, outgoing: List[StateId]) val states = mutable.HashMap.from(parts.states) val labelMap = mutable.HashMap.empty[LabelSymbol, (StateId, StateId)] def createState(blk: Block): StateId = val newId = allocId() states(newId) = BlockPartition(blk, false) newId def computeVarInfo(blk: Block): PartitionVarInfo = // Variables that are assigned in the block val assigned = mutable.BitSet.empty // Variables that are used before any assignment in the block, which means they must be live val used = mutable.BitSet.empty val outgoing = mutable.HashSet.empty[StateId] def assignToSym(l: Local) = localSetMap.get(l).foreach: idx => assigned += idx new BlockTraverserShallow(): applyBlock(blk) override def applyBlock(b: Block): Unit = b match case Unwind(uid, loc) => () case StateTransition(uid) => outgoing += uid case Match(scrut, arms, dflt, rest) => applyPath(scrut) val restId = createState(rest) arms.foreach: arm => val newId = createState(Begin(arm._2, StateTransition(restId))) outgoing += newId dflt match case N => outgoing += restId case S(blk) => outgoing += createState(Begin(blk, StateTransition(restId))) case Label(label, loop, body, rest) => val restId = createState(rest) val bodyId = createState(Begin(body, StateTransition(restId))) labelMap(label) = (bodyId, restId) outgoing += bodyId case Break(label) => outgoing += labelMap(label)._2 case Continue(label) => outgoing += labelMap(label)._1 case Assign(lhs, rhs, rest) => applyResult(rhs) assignToSym(lhs) applyBlock(rest) case Define(defn: ValDefn, rest) => applyPath(defn.rhs) assignToSym(defn.sym) applyBlock(rest) case Define(defn, rest) => assignToSym(defn.sym) applyBlock(rest) case _ => super.applyBlock(b) override def applySymbol(sym: Symbol): Unit = localSetMap.get(sym).foreach: idx => if !assigned.contains(idx) then used += idx (used, assigned, outgoing.toList) val worklist = mutable.Queue.empty[StateId] val worklistSet = mutable.Set.empty[StateId] val stateInfo = mutable.HashMap.empty[StateId, (live: mutable.BitSet, varInfo: PartitionVarInfo, incoming: mutable.ArrayBuffer[StateId])] def traverse(id: StateId): Unit = if stateInfo.contains(id) then return () val info = computeVarInfo(states(id).blk) stateInfo(id) = (mutable.BitSet.empty, info, mutable.ArrayBuffer.empty) info.outgoing.foreach: entry => traverse(entry) stateInfo(entry).incoming += id worklist.enqueue(id) worklistSet += id traverse(parts.entry) while worklist.nonEmpty do val cur = worklist.dequeue() worklistSet -= cur val info = stateInfo(cur) val newLive = info.varInfo.outgoing .map: entry => stateInfo(entry).live .fold(mutable.BitSet.empty)(_ | _).diff(info.varInfo.assigned) | info.varInfo.used if newLive != info.live then stateInfo(cur).live |= newLive stateInfo(cur).incoming.foreach: id => if !worklistSet.contains(id) then worklist.enqueue(id) worklistSet += id parts.states .flatMap: (id, part) => if !part.resumable then N else S(stateInfo.get(id).fold(mutable.BitSet.empty)(_.live)) .fold(mutable.BitSet.empty)(_ | _) .toList .map(locals(_)) private def computeEdges(parts: PartitionedBlock): Map[StateId, List[StateId]] = val edges = mutable.ListBuffer.empty[(StateId, StateId)] def findEdges(uid: StateId, b: Block) = new BlockTraverser: override def applyBlock(b: Block): Unit = b match case StateTransition(uid2) => edges.addOne((uid, uid2)) case _ => super.applyBlock(b) applyBlock(b) for (uid, blk) <- parts.states do findEdges(uid, blk.blk) edges.groupBy(_._1).map: case uid -> ids => uid -> ids.map: case (a, b) => b .toList .distinct // Denotes whether a block transitions to another state only on the outer level, // i.e. should return false iff there is a state transition within an if, label, etc. // A precondition is that the state corresponding to the input block has an out-degree // of 1. This means if a state transition cannot be found on the outer level, there // must be a state transition within another construct and should return false. @tailrec private def isSimpleTransition(b: Block): Bool = b match case StateTransition(uid) => true case b: NonBlockTail => isSimpleTransition(b.rest) case _: BlockTail => false // Given a directed graph, computes the "straight line" segments of the graph, i.e. partitions it // into segments such that the out-degree of all elements in each segment is 1, except // for the last element. Note that the partitioning is not necessarily unique and this does // not necessarily produce a "maximal" partitioning. (I actually suspect that producing a // maximal partitioning is NP-hard...) // // I do have some ideas to improve this though, but those can be done later. private def computeStraightLines(entry: StateId, edges: Map[StateId, List[StateId]]): List[List[StateId]] = val visited = mutable.HashSet.empty[StateId] val ret = mutable.ListBuffer.empty[List[StateId]] // Algorithm: Perform a DFS and accumulate the current straight-line segment as we visit nodes. // Once we reach a node that has an out degree of != 1, we end the current straight line segment. def dfs(state: StateId, acc: List[StateId]): Unit = var curAcc = acc def concludeSegment = ret.addOne(curAcc) curAcc = List.empty if !visited.contains(state) then // Not yet visited: Add this node to the current segment. curAcc = state :: curAcc visited.add(state) edges.get(state) match case Some(nexts) => // If this state has an out degree of != 1, then end the current segment. if nexts.size != 1 then concludeSegment for n <- nexts do dfs(n, curAcc) case None => concludeSegment // If this state was visited from a node u with an out-degree of 1, but this state // has already been previously visited, then we must conclude the current segment, // ending at the node u. else if !curAcc.isEmpty then concludeSegment dfs(entry, List.empty) ret.sortBy(x => x.headOption.getOrElse(BigInt(-1))).toList val stackSafetyMap: mutable.Map[FnOrCls, (Int, Block)] = mutable.HashMap.empty private def lifterReport(using Line, FileName)(msgs: Ls[Message -> Opt[Loc]])(using Name) = if opt.softLifterError then WarningReport(msgs, source = Diagnostic.Source.Compilation) else InternalError(msgs, source = Diagnostic.Source.Compilation) /** * The actual translation: * 1. rewrite handler blocks in terms of classes and functions (directly during Lowering) * 2. class lifter * 3. state machine transformation of all functions (HandlerLowering, this class) */ private def translateBlock(blk: Block, h: HandlerCtx, scopedVars: collection.Set[Local]): Block = given HandlerCtx = h def translateFunLike(fun: FunDefn, funcPath: Path, thisPath: Option[Path], debugNme: Str) = val scopedVars = fun.body match case Scoped(syms, body) => syms case _ => Set() val varList = scopedVars.toList.sortBy(_.uid) val debugInfo = Value.Lit(Tree.StrLit(debugNme)).asArg :: varList.zipWithIndex.filter(_._1.isInstanceOf[VarSymbol]) .flatMap: (sym, idx) => List(intLit(idx), Value.Lit(Tree.StrLit(sym.nme))) .map(_.asArg) val debugInfoSym = freshTmp(s"$debugNme$$debugInfo") // TODO: properly support spread argument by calculating the correct length. val rtArgLists = intLit(fun.params.length) :: fun.params.flatMap: pl => intLit(pl.params.length) :: pl.params.map(_.sym.asPath) val newCtx = HandlerCtx.FunctionLike(FunctionCtx(funcPath, thisPath, ResumeInfo(rtArgLists, varList, L(fun.sym)), DebugInfo(debugNme, if opt.debug then debugInfoSym.asPath else unit), thisPath.isDefined && fun.params.isEmpty)) val bod2 = translateBlock(fun.body, newCtx, scopedVars) val fun2 = if fun.body is bod2 then fun else FunDefn(fun.owner, fun.sym, fun.dSym, fun.params, bod2)(fun.configOverride, fun.annotations) (debugInfoSym, debugInfo, fun2) // transform inner function/class and effect handler intrinsics to the runtime functions. val preTransform = new BlockTransformer(SymbolSubst.Id): override def applyResult(r: Result)(k: Result => Block): Block = r match case Call(Value.Ref(sym, _), args) if sym is Elaborator.ctx.builtins.runtime.suspend => k(Call(paths.mkEffectPath, args)(true, true, false)) case Call(Value.Ref(sym, _), args) if sym is Elaborator.ctx.builtins.runtime.handle_suspension => k(Call(paths.enterHandleBlockPath, args)(true, true, false)) case _ => super.applyResult(r)(k) override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case fun: FunDefn => if !h.allowDefn then raise(lifterReport(msg"Unexpected nested function: lambdas may not function correctly." -> fun.sym.toLoc :: Nil)) val (debugInfoSym, debugInfo, fun2) = translateFunLike(fun, Value.Ref(fun.sym, S(fun.dSym)), N, fun.sym.nme) if opt.debug then Scoped(Set.single(debugInfoSym), Assign(debugInfoSym, Tuple(false, debugInfo), k(fun2))) else k(fun2) case defn @ ClsLikeDefn(owner, isym, sym, ctorSym, kind, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, companion, bufferable) => if !h.allowDefn then raise(lifterReport(msg"Unexpected nested class: lambdas may not function correctly." -> isym.toLoc :: Nil)) val debugInfos = mutable.ArrayBuffer.empty[(Local, List[Arg])] val newMtds = methods.map: f => val (debugInfoSym, debugInfo, fun2) = translateFunLike(f, Value.Ref(isym).sel(new Tree.Ident(f.sym.nme), f.dSym), S(Value.Ref(isym)), s"${sym.nme}#${f.sym.nme}") debugInfos += debugInfoSym -> debugInfo fun2 val companion2 = companion.map: bod => val newMtds = bod.methods.map: f => val (debugInfoSym, debugInfo, fun2) = translateFunLike(f, Value.Ref(bod.isym).sel(new Tree.Ident(f.sym.nme), f.dSym), S(Value.Ref(bod.isym)), s"${sym.nme}.${f.sym.nme}") debugInfos += debugInfoSym -> debugInfo fun2 // We cannot use this bc there is no subblock transform... // val newCtor = translateTrivialOrTopLevel(bod.ctor) // TODO: Companion's ctor is more well behaved so it is possible to handle it // However, JSBuilder inserts extra statements between preCtor and ctor and it's not possible to replicate the exact behavior // without many special handling. val newCtor = if opt.doNotInstrumentTopLevelModCtor && !h.innerDefIsTrulyNested then bod.ctor else translateCtorLike(bod.ctor, bod.isym.asPath, true) tl.log(s"companion name: ${bod.isym.nme}") ClsLikeBody(bod.isym, newMtds, bod.privateFields, bod.publicFields, newCtor, bod.annotations) val c2 = ClsLikeDefn(owner, isym, sym, ctorSym, kind, paramsOpt, auxParams, parentPath, newMtds, privateFields, publicFields, translateCtorLike(preCtor, isym.asPath, false), translateCtorLike(ctor, isym.asPath, false), companion2, bufferable)(defn.configOverride, defn.annotations) if opt.debug then Scoped(debugInfos.map(_._1).toSet, debugInfos.foldRight(k(c2)): (elem, blk) => Assign(elem._1, Tuple(false, elem._2), blk)) else k(c2) case _ => super.applyDefn(defn)(k) val b = preTransform.applyBlock(blk) if h.inCtor then return translateIllegalEffectCtx(b, Call(paths.illegalEffectPath, (Value.Lit(Tree.StrLit("in a constructor")).asArg :: Nil) ne_:: Nil)(true, true, false)) if h.inTopLevel then return translateIllegalEffectCtx(b, Call(paths.topLevelEffectPath, (Value.Lit(Tree.BoolLit(opt.debug)).asArg :: Nil) ne_:: Nil)(true, false, false)) val ctx = h.asInstanceOf[HandlerCtx.FunctionLike].ctx if ctx.inGetter then return translateIllegalEffectCtx(b, Call(paths.illegalEffectPath, (Value.Lit(Tree.StrLit("in a getter")).asArg :: Nil) ne_:: Nil)(true, false, false)) given FunctionCtx = ctx val parts = partitionBlock(b) stackSafetyMap += ctx.resumeInfo.currentStackSafetySym -> ( 1, ctx.doUnwind(ctx.resumeInfo.currentStackSafetySym.fold(_.toLoc, _.toLoc).fold(unit)(locToStr(_)), -1, Nil)(using paths) ) if parts.states.size <= 1 && !parts.containsError then return b val vars = if opt.debug then ctx.resumeInfo.currentLocals else computeRestoreList(parts) val pcVar = freshTmp("pc") val mainLoopLbl = freshLabel("main") val edges = computeEdges(parts) val straightLines = computeStraightLines(parts.entry, edges) val segmentTailTransform = new BlockTransformerShallow(SymbolSubst.Id): override def applyBlock(b: Block) = b match case StateTransition(uid) => Assign(pcVar, Value.Lit(Tree.IntLit(uid)), Continue(mainLoopLbl)) case Unwind(uid, loc) => ctx.doUnwind(loc, uid, vars)(using paths) case _ => super.applyBlock(b) // Note: `line` has the last state as the head, and the first state at the end def straightLineToArms(line: List[StateId]): Block => Block = def transformState(state: StateId) = val blk = parts.states(state) // If the state transition does not appear in tail position on the outer level, // we must wrap the transformed state in a label, and jump to that label when // encountering a state transition val isSimple = isSimpleTransition(blk.blk) lazy val lblSym = LabelSymbol(N, "brk" + state.toString()) val nextState = edges(state).head val transform = new BlockTransformerShallow(SymbolSubst.Id): override def applyBlock(b: Block) = b match case StateTransition(uid) => assert(uid === nextState) if isSimple then Assign(pcVar, Value.Lit(Tree.IntLit(uid)), End()) else Break(lblSym) case Unwind(uid, loc) => ctx.doUnwind(loc, uid, vars)(using paths) case _ => super.applyBlock(b) val transformed = transform.applyBlock(blk.blk) if isSimple then transformed else Label( lblSym, false, transformed, Assign(pcVar, Value.Lit(Tree.IntLit(nextState)), End()) ) line match case head :: next => val headTransformed = segmentTailTransform.applyBlock(parts.states(head).blk) val initial: Block => Block = blk => Match( Value.Ref(pcVar), Case.Lit(Tree.IntLit(head)) -> headTransformed :: Nil, N, blk ) next.foldLeft(initial): // Applying this function to a block b will result in b appearing in the tail // of the sequence of match blocks case (acc, uid) => val transformed = transformState(uid) blk => Match( Value.Ref(pcVar), Case.Lit(Tree.IntLit(uid)) -> transformed :: Nil, N, acc(blk) ) case Nil => id val mainLoop = if parts.states.size <= 1 then segmentTailTransform.applyBlock(parts.states.head._2.blk) else val matches = straightLines.map(straightLineToArms).foldLeft[Block](End()): case (acc, f) => f(acc) Label(mainLoopLbl, true, matches, End()) val getSavedTmp = freshTmp("saveOffset") def getSaved(off: BigInt): (Block => Block, Path) = if off == 0 then return (id, DynSelect(paths.runtimePath.selSN("resumeArr"), paths.runtimePath.selSN("resumeIdx"), true)) val addOne = Assign(getSavedTmp, Call(State.builtinOpsMap("+").asPath, (paths.runtimePath.selSN("resumeIdx").asArg :: intLit(off).asArg :: Nil) ne_:: Nil)(false, false, false), _) (addOne, DynSelect(paths.runtimePath.selSN("resumeArr"), getSavedTmp.asPath, true)) val resumeArrIndexed = DynSelect(paths.runtimePath.selSN("resumeArr"), getSavedTmp.asPath, true) val plus = State.builtinOpsMap("+").asPath val preRestore = blockBuilder .assign(pcVar, paths.resumePc) .scopedVars(Set(getSavedTmp)) val restoreVars = vars.zipWithIndex.foldLeft(preRestore): case (builder, (local, idx)) => builder .assign(getSavedTmp, if idx == 0 then paths.resumeIdx else Call(plus, (getSavedTmp.asPath.asArg :: intLit(1).asArg :: Nil) ne_:: Nil)(false, false, false)) .assign(local, resumeArrIndexed) Scoped( scopedVars ++ Set(pcVar), Match( paths.resumePc, Case.Lit(Tree.IntLit(-1)) -> Assign(pcVar, intLit(parts.entry), End()) :: Nil, S(restoreVars .assignFieldN(paths.runtimePath, new Tree.Ident("resumePc"), Value.Lit(Tree.IntLit(-1))).end), mainLoop)) private def translateCtorLike(b: Block, thisPath: Path, isModCtor: Bool)(using h: HandlerCtx): Block = translateBlock(b, if isModCtor then HandlerCtx.ModCtor(h.innerDefIsTrulyNested) else HandlerCtx.Ctor, Set.empty) private def translateIllegalEffectCtx(b: Block, onEffect: Call)(using HandlerCtx): Block = def effectCheck(l: Local, r: Result, rst: Block): Block = blockBuilder .assign(l, r) .ifthen( paths.curEffect, Case.Lit(Tree.UnitLit(true)), End(), S(Assign(l, onEffect, End()))) .rest(rst) val topLevelTransform = new BlockTransformerShallow(SymbolSubst.Id): override def applyBlock(b: Block) = b match case Assign(lhs, EffectfulResult(r), rest) => // Optimization to reuse lhs instead of fresh local effectCheck(lhs, r, applyBlock(rest)) case _ => super.applyBlock(b) override def applyResult(r: Result)(k: Result => Block) = r match case EffectfulResult(r) => // Fallback case, this may lead to unnecessary assignments if it is assign-like val l = freshTmp() Scoped(Set(l), effectCheck(l, r, k(Value.Ref(l)))) case _ => super.applyResult(r)(k) topLevelTransform.applyBlock(b) def translateTopLevel(b: Block): (Block, StackSafetyMap) = val preTransformed = new ScopeFlattener().applyBlock(b) val ctx = HandlerCtx.TopLevel val transformed = translateBlock(preTransformed, ctx, Set.empty) val blk = blockBuilder .assign(State.noSymbol, Call(paths.resetEffects, Nil ne_:: Nil)(true, false, false)) .rest(transformed) (blk, stackSafetyMap) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* import hkmc2.semantics.* import semantics.Elaborator.State import hkmc2.syntax.Tree object LambdaRewriter: def desugar(b: Block)(using State) = val transformer = new BlockTransformer(SymbolSubst.Id): override def applyResult(r: Result)(k: Result => Block): Block = r match case lam: Lambda => val sym = BlockMemberSymbol("lambda", Nil, nameIsMeaningful = false) val Lambda(params, body) = super.applyLam(lam) val lamDefn = FunDefn.withFreshSymbol(N, sym, params :: Nil, body)(N, annotations = Nil) Scoped(Set.single(sym), Define(lamDefn, k(lamDefn.asPath))) case _ => super.applyResult(r)(k) override def applyBlock(b: Block): Block = b match case Assign(lhs, Lambda(params, body), rest) if !lhs.isInstanceOf[TempSymbol] => val newSym = BlockMemberSymbol(lhs.nme, Nil, nameIsMeaningful = true // TODO: lhs.nme is not always meaningful ) val defn = FunDefn.withFreshSymbol(N, newSym, params :: Nil, applyBlock(body))(N, annotations = Nil) val blk = blockBuilder .define(defn) .assign(lhs, defn.asPath) .rest(applyBlock(rest)) Scoped(Set.single(newSym), blk) case _ => super.applyBlock(b) transformer.applyBlock(b) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import utils.* import syntax.{Keyword, SpreadKind} import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.Message.* import hkmc2.ScopeData.* import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.ListBuffer object Lifter: /** * Describes the free variables of a function that have been accessed by its nested definitions. * @param vars The free variables that are accessed by nested classes/functions. * @param reqCapture The free variables that must be captured using a heap-allocated object. */ case class FreeVars(vars: Set[Local], reqCapture: Set[Local]): def ++(that: FreeVars) = FreeVars(vars ++ that.vars, reqCapture ++ that.reqCapture) object FreeVars: val empty = FreeVars(Set.empty, Set.empty) extension (l: List[Lazy[Defn] | Defn]) def gatherUsed: List[Defn] = l.collect: case l: Lazy[?] if !l.isEmpty => l.force_! case d: Defn => d /** * Describes previously defined locals and definitions which could possibly be accessed or mutated by particular definition. * Here, a "previously defined" local or definition means it is accessible to the particular definition (which we call `d`), * but is not defined *by* `d`. * * @param accessed Previously defined locals which could possibly be accessed or mutated. * @param mutated Such locals which could also be mutated by this definition. * @param refdDefns Previously defined definitions which could possibly be used by this definition. */ case class AccessInfo( accessed: Set[Local], mutated: Set[Local], refdDefns: Set[ScopedInfo] ): def ++(that: AccessInfo) = AccessInfo( accessed ++ that.accessed, mutated ++ that.mutated, refdDefns ++ that.refdDefns ) def withoutLocals(locals: Set[Local]) = AccessInfo( accessed -- locals, mutated -- locals, refdDefns ) def intersectLocals(locals: Set[Local]) = AccessInfo( accessed.intersect(locals), mutated.intersect(locals), refdDefns ) def addAccess(l: Local) = copy(accessed = accessed + l) def addMutated(l: Local) = copy(accessed = accessed + l, mutated = mutated + l) def addRefdScopedObj(l: ScopedInfo) = copy(refdDefns = refdDefns + l) object AccessInfo: val empty = AccessInfo(Set.empty, Set.empty, Set.empty) object RefOfBms: def unapply(p: Path): Opt[(BlockMemberSymbol, Opt[DefinitionSymbol[?]], Bool)] = p match case Value.Ref(l: BlockMemberSymbol, disamb) => S((l, disamb, false)) case s @ Select(_, _) => s.symbol match case Some(value) => value.asBlkMember.map((_, S(value), true)) case _ => N case _ => N def modOrObj(d: Defn) = d match case c: ClsLikeDefn => (c.companion.isDefined) || (c.k is syntax.Obj) case _ => false /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. */ class Lifter(topLevelBlk: Block)(using State, Raise, Config): // TODO: implement tracing debug system import Lifter.* extension (l: Local) def asLocalPath: LocalPath = LocalPath.Sym(l) def asDefnRef: DefnRef = DefnRef.Sym(l) enum LocalPath: case Sym(l: Local) case BmsRef(l: BlockMemberSymbol, d: DefinitionSymbol[?]) case InCapture(capturePath: Path, field: TermSymbol) def read(using ctx: LifterCtxNew): Path = this match case Sym(l) => l.asPath case BmsRef(l, d) => Value.Ref(l, S(d)) case InCapture(path, field) => Select(path, field.id)(S(field)) def asArg(using ctx: LifterCtxNew) = read.asArg def assign(value: Result, rest: Block)(using ctx: LifterCtxNew): Block = this match case Sym(l) => Assign(l, value, rest) case BmsRef(l, d) => lastWords("Tried to assign to a BlockMemberSymbol") case InCapture(path, field) => AssignField(path, field.id, value, rest)(S(field)) enum DefnRef: case Sym(l: Local) case InScope(l: BlockMemberSymbol, d: DefinitionSymbol[?]) case Field(isym: InnerSymbol, l: BlockMemberSymbol, d: DefinitionSymbol[?]) def read(using ctx: LifterCtxNew): Path = this match case Sym(l) => l.asPath case InScope(l, d) => Value.Ref(l, S(d)) case Field(isym, l, d) => Select(ctx.symbolsMap(isym).read, Tree.Ident(l.nme))(S(d)) def asArg(using ctx: LifterCtxNew) = read.asArg case class FunSyms[T <: DefinitionSymbol[?]](b: BlockMemberSymbol, d: T): def asPath = Value.Ref(b, S(d)) object FunSyms: def fromFun(b: BlockMemberSymbol, owner: Opt[InnerSymbol] = N) = FunSyms(b, TermSymbol.fromFunBms(b, owner)) type ClsLikeSym = DefinitionSymbol[? <: ClassDef | ModuleOrObjectDef] type ClsSym = DefinitionSymbol[? <: ClassLikeDef] type ModuleOrObjSym = DefinitionSymbol[? <: ModuleOrObjectDef] case class LifterMetadata( unliftable: Set[ClsSym | ModuleOrObjSym], ): def ++(that: LifterMetadata) = LifterMetadata(unliftable ++ that.unliftable) object LifterMetadata: def empty = LifterMetadata(Set.empty) // s is a top-level definition // returns (ignored classes, modules) private def createMetadata(s: ScopeNode): LifterMetadata = var ignored: Set[ClsSym | ModuleOrObjSym] = Set.empty val nestedScopes: Set[ScopedInfo] = s.allChildNodes.map(_.obj.toInfo).toSet - s.obj.toInfo // hack: ClassLikeSymbol does not extend DefinitionSymbol directly, so we must // use a map to convert val moduleObjs: List[ScopedObject.Companion | ScopedObject.Class] = s.allChildNodes.collect: case s @ ScopeNode(obj = o: ScopedObject.Companion) if !s.inModOrTopLevel => o case s @ ScopeNode(obj = o: ScopedObject.Class) if !s.inModOrTopLevel && o.isObj => o for m <- moduleObjs do m match case c: ScopedObject.Class => ignored += c.cls.isym raise(WarningReport( msg"Objects are not yet lifted." -> c.cls.isym.toLoc :: Nil, N, Diagnostic.Source.Compilation )) case m: ScopedObject.Companion => ignored += m.compDefn.isym ignored += m.clsBody.isym raise(WarningReport( msg"Modules are not yet lifted." -> m.clsBody.isym.toLoc :: Nil, N, Diagnostic.Source.Compilation )) var inheritanceTree: Set[(ClsSym, ClsSym)] = Set.empty // search for unliftable classes and build the extends graph new BlockTraverser: this.applyScopedObject(s.obj) override def applyCase(cse: Case): Unit = cse match case Case.Cls(cls: (ClassSymbol | ModuleOrObjectSymbol), _) => if nestedScopes.contains(cls) && !ignored.contains(cls) && !data.getNode(cls).inModOrTopLevel then // don't generate a warning if it's already ignored raise(WarningReport( msg"Cannot yet lift class/module `${cls.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += cls case _ => () override def applyResult(r: Result): Unit = r match // do not search the ref to the class case Instantiate(mut, RefOfBms(_, S(d), _), argss) => argss.flatten.foreach(applyArg) // for class constructors case Call(RefOfBms(_, S(d), _), argss) => argss.flatten.foreach(applyArg) case _ => super.applyResult(r) override def applyDefn(defn: Defn): Unit = defn match case defn: FunDefn => applyFunDefn(defn) case ValDefn(tsym, sym, rhs) => tsym.owner.foreach(_.traverse) sym.traverse applyPath(rhs) case ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) => own.foreach(_.traverse) isym.traverse sym.traverse // Check if `extends` is a complex expression, i.e. not just extending a class. // If it's just a class, add it to an graph where edges are class extensions. // If B extends A, then A -> B is an edge parentPath match case None => () case Some(RefOfBms(_, S(s: (ClassSymbol | ModuleOrObjectSymbol)), _)) => if nestedScopes.contains(s) then inheritanceTree += (s -> isym) case _ if !ignored.contains(isym) => raise(WarningReport( msg"Cannot yet lift definition `${sym.nme}` as it extends an expression." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += isym case _ => () paramsOpt.foreach(applyParamList) auxParams.foreach(applyParamList) methods.foreach(applyFunDefn) privateFields.foreach(_.traverse) publicFields.foreach: f => f._1.traverse; f._2.traverse applyBlock(preCtor) applyBlock(ctor) mod.foreach(applyCompanionModule) def isFun(d: Defn) = d match case _: FunDefn => true case _ => false override def applyValue(v: Value): Unit = v match case RefOfBms(_, S(l), _) if nestedScopes.contains(l) => data.getNode(l).obj match case c: ScopedObject.Class if c.isObj => () // Parameterized class constructors used as naked references are constructor function // references, not first-class class uses. They can be lifted using a curried wrapper. case c: ScopedObject.ClassCtor => () case c: ScopedObject.Class => if !c.node.get.inModOrTopLevel then raise(WarningReport( msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += c.cls.isym case _ => super.applyValue(v) case _ => super.applyValue(v) // analyze the extends graph val extendsEdges = inheritanceTree.groupBy(_._1).map: case (a, bs) => a -> bs.map(_._2) .toMap var newUnliftable: Set[ClsSym] = Set.empty // dfs starting from unliftable classes def dfs(s: ClsSym): Unit = for edges <- extendsEdges.get(s) b <- edges if !newUnliftable.contains(b) && !ignored.contains(b) do raise(WarningReport( msg"Cannot yet lift definition `${b.nme}` as it extends an unliftable class." -> N :: Nil, N, Diagnostic.Source.Compilation )) newUnliftable += b dfs(b) for case s: ClsLikeSym <- ignored do dfs(s) LifterMetadata(ignored ++ newUnliftable) // This rewrites code so that it's valid when lifted to the top level. // This way, no piece of code must be traversed by a BlockRewriter more than once. // Remark: This is why so much prior analysis is needed and is the main source of complexity in the lifter. class BlockRewriter(using ctx: LifterCtxNew) extends ScopeRewriter: // Closure symbols that point to an initialized closure in this scope var activeClosures: Set[Local] = Set.empty // Map from block member symbols to initialized closures val closureMap: MutMap[BlockMemberSymbol, Local] = MutMap.empty val extraLocals: MutSet[Local] = MutSet.empty def rewrite(b: Block) = val ret = applyBlock(b) Scoped(extraLocals, ret) // Replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, // it also directly rewrites Results (Calls and Instantiates). // Since first-class classes can't be lifted, this is where class // instantiations are rewritten. // // Does *not* rewrite references to non-lifted BMS symbols. // // References to methods and unlifted classes nested inside classes/modules are // always rewritten using `this.defnName` (when accessed internally) or `object.defnName`. def rewriteBms(b: Block) = // BMS's that need to be created val syms: LinkedHashMap[FunSyms[?], Local] = LinkedHashMap.empty val extraLocals: MutSet[Local] = MutSet.empty val walker = new BlockDataTransformer(SymbolSubst.Id): // only scan within the block. don't traverse def resolveDefnRef(l: BlockMemberSymbol, d: DefinitionSymbol[?], r: RewrittenScope[?]) = ctx.defnsMap.get(d) match case Some(defnRef) => S(defnRef.read) case None => r.obj match case c: ScopedObject.Class if c.isObj => ctx.symbolsMap.get(c.cls.isym).map(_.read) case c: ScopedObject.Companion => ctx.symbolsMap.get(c.clsBody.isym).map(_.read) case _ => N override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version case c @ Call(RefOfBms(l, S(d), _), argss) => ctx.rewrittenScopes.get(d) match case N => super.applyResult(r)(k) // external call, or have not yet traversed that function case S(r) => applyArgss(argss): newArgss => def join2: Block = resolveDefnRef(l, d, r) match case Some(value) => k(c.copy(fun = value, argss = newArgss.ne_!)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall).withLoc(c.toLoc)) case None => super.applyPath(c.fun): fun2 => if (fun2 is c.fun) && (argss is newArgss) then k(c) else k(c.copy(fun = fun2, argss = newArgss.ne_!)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall).withLoc(c.toLoc)) r match // function call case f: LiftedFunc => k(f.rewriteCall(c, newArgss)) // ctor call (without using `new`) case ctor: RewrittenClassCtor => ctor.getRewrittenCls match case cls: LiftedClass => cls.rewriteCall(c, newArgss)(k) case _ => join2 case _ => join2 case inst @ Instantiate(mut, RefOfBms(l, S(d), _), argss) => applyArgss(argss): newArgss => def join = if argss is newArgss then inst else inst.copy(argss = newArgss).withLoc(inst.toLoc) ctx.rewrittenScopes.get(d) match case N => k(join) case S(c: LiftedClass) => c.rewriteInstantiate(inst, newArgss)(k) case S(r) => resolveDefnRef(l, d, r) match case Some(value) => k(Instantiate(inst.mut, value, newArgss).withLoc(inst.toLoc)) case None => k(join) case _ => super.applyResult(r)(k) // extract the call override def applyPath(p: Path)(k: Path => Block): Block = p match case r @ RefOfBms(l, S(d), isSel) => ctx.rewrittenScopes.get(d) match case S(f: LiftedFunc) => if f.isTrivial then k(r) else val newSym = closureMap.get(l) match case None => val newSym = TempSymbol(N, l.nme + "$here") extraLocals.add(newSym) syms.addOne(FunSyms(l, d) -> newSym) // add to `syms`: this closure will be initialized in `applyBlock` closureMap.addOne(l -> newSym) // add to `closureMap`: `newSym` refers to the closure and can be used later newSym // symbol exists, and is initialized case Some(value) if activeClosures.contains(value) => value // symbol exists, needs initialization case Some(value) => syms.addOne(FunSyms(l, d) -> value) value k(Value.Ref(newSym, N)) // Naked reference to a parameterized class constructor (used as a first-class function). // Replace with a partially applied curried C$ wrapper. case S(ctor: RewrittenClassCtor) if !isSel => ctor.getRewrittenCls match case cls: LiftedClass if !cls.isTrivial => val newSym = closureMap.get(l) match case None => val newSym = TempSymbol(N, l.nme + "$here") extraLocals.add(newSym) syms.addOne(FunSyms(l, d) -> newSym) closureMap.addOne(l -> newSym) newSym case Some(value) if activeClosures.contains(value) => value case Some(value) => syms.addOne(FunSyms(l, d) -> value) value k(Value.Ref(newSym, N)) case _ => resolveDefnRef(l, d, ctor) match case Some(value) => k(value) case None => super.applyPath(p)(k) // Other naked references to BlockMemberSymbols. // // For now, do not immediately rewrite selections if they are not referencing // a lifted function, and instead rewrite `qual`. This is so that, when we reference // a nested object or class using a selection `A.B`, we just rewrite the reference to `A` // instead of trying to rewrite the whole reference to `B`. The variable analyzer is // written so that a reference to `A` is available (in the case that `A` is a module or object), // as a passed parameter if needed. // // Once we properly support lifting objects, which involves putting the object instance in // a new public field belonging to its owner, we will need to replace the selection's // disambiguation with that public field's symbol. case S(r) if !isSel => resolveDefnRef(l, d, r) match case Some(value) => k(value) case None => super.applyPath(p)(k) case _ => super.applyPath(p)(k) case _ => super.applyPath(p)(k) (walker.applyBlock(b), syms.toList, extraLocals) end rewriteBms def applySubBlockAndReset(b: Block): Block = val curActive = activeClosures val ret = applySubBlock(b) activeClosures = curActive ret override def applyBlock(b: Block): Block = // extract references to BlockMemberSymbols in the block which now may // need to be enriched with aux parameters val (rewritten, syms, extras) = rewriteBms(b) extraLocals.addAll(extras) val pre = syms.foldLeft(blockBuilder): case (blk, (funSym, local)) => ctx.liftedScopes.get(funSym.d) match case Some(l: LiftedFunc) => blk.assign(local, l.rewriteRef) case _ => // ClassCtor reference: look up the rewritten class ctor to get the LiftedClass ctx.rewrittenScopes(funSym.d) match case ctor: RewrittenClassCtor => ctor.getRewrittenCls match case cls: LiftedClass => blk.assign(local, cls.rewriteCtorRef) case _ => die case _ => die // Rewrite the rest val remaining = rewritten match // We create closures once the first time we see them, then re-use them later. // We store already-created closures in a set in the BlockRewriter class. // This set needs to be reset after processing an if-else branch or while loop, // since closures nested inside each branch may not be re-used elsewhere. case Match(scrut, arms, dflt, rst) => applyPath(scrut): scrut2 => applyListOf( arms, (tup, k) => val (cse, blk) = tup val blk2 = applySubBlockAndReset(blk) applyCase(cse): cse2 => if (cse2 is cse) && (blk is blk2) then k(tup) else k(cse2 -> blk2) ): arms2 => val dflt2 = dflt.mapConserve(applySubBlockAndReset) val rst2 = applySubBlock(rst) if (scrut2 is scrut) && (arms2 is arms) && (dflt2 is dflt) && (rst2 is rst) then rewritten else Match(scrut2, arms2, dflt2, rst2) case Label(lbl, false, bod, rst) => val lbl2 = lbl.subst val bod2 = applySubBlockAndReset(bod) val rst2 = applySubBlock(rst) if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then rewritten else Label(lbl2, false, bod2, rst2) case TryBlock(sub, fin, rst) => val sub2 = applySubBlockAndReset(sub) val fin2 = applySubBlockAndReset(fin) val rst2 = applySubBlock(rst) if (sub2 is sub) && (fin2 is fin) && (rst2 is rst) then rewritten else TryBlock(sub2, fin2, rst2) // Assignment to variables case Assign(lhs, rhs, rest) => ctx.symbolsMap.get(lhs) match case Some(path) => applyResult(rhs): rhs2 => path.assign(rhs2, applySubBlock(rest)) case _ => super.applyBlock(rewritten) // rewrite object definitions, assigning to the saved symbol case Define(d @ ClsLikeDefn(k = syntax.Obj), rest: Block) => ctx.liftedScopes.get(d.isym) match case Some(l: LiftedClass) if l.obj.isObj => ctx.symbolsMap(l.cls.isym).assign(l.instObject, applySubBlock(rest)) case _ => super.applyBlock(rewritten) case _ => super.applyBlock(rewritten) pre.rest(remaining) override def applyPath(p: Path)(k: Path => Block): Block = p match // This rewrites naked references to locals, case Value.Ref(l, _) => ctx.symbolsMap.get(l) match case Some(value) => k(value.read) case _ => super.applyPath(p)(k) case _ => super.applyPath(p)(k) case class LifterResult[+T](liftedDefn: T, extraDefns: List[Lazy[Defn] | Defn]) case class LifterCtxNew( liftedScopes: MutMap[LiftedSym, LiftedScope[?]] = MutMap.empty, rewrittenScopes: MutMap[ScopedInfo, RewrittenScope[?]] = MutMap.empty, var symbolsMap: Map[Local, LocalPath] = Map.empty, var defnsMap: Map[DefinitionSymbol[?], DefnRef] = Map.empty, var capturesMap: Map[ScopedInfo, Path] = Map.empty ) /** * Creates a capture class for a function consisting of its mutable (and possibly immutable) local variables. * @param f The function to create the capture class for. * @param ctx The lifter context. Determines which variables will be captured. * @return The tuple (defn, varsMap), where `defn` is the capture class's definition, and * `varsMap` maps the function's locals to the corresponding `VarSymbol` (for the class parameters) in the correct order. */ def createCaptureCls(s: ScopedObject) : (ClsLikeDefn, List[(Symbol, TermSymbol)]) = val nme = "Capture$" + s.nme val clsSym = ClassSymbol( Tree.DummyTypeDef(syntax.Cls), Tree.Ident(nme) ) val cap = usedVars.reqdCaptures(s.toInfo) val fresh = FreshInt() val sortedVars: Array[(ctorSyms: (local: Local, vs: VarSymbol), param: Param, valDefn: ValDefn)] = cap.toArray.sortBy(_.uid).map: sym => val id = fresh.make val nme = sym.nme + "$" + id val ident = new Tree.Ident(nme) val varSym = VarSymbol(ident) val fldSym = BlockMemberSymbol(nme, Nil) val tSym = TermSymbol(syntax.MutVal, S(clsSym), ident) val p = Param(FldFlags.empty.copy(isVal = true), varSym, N, Modulefulness.none) varSym.decl = S(p) // * Currently this is only accessed to create the class' toString method val vd = ValDefn( tSym, fldSym, Value.Ref(varSym) )(N, Nil) (sym -> varSym, p, vd) val defn = ClsLikeDefn( None, clsSym, BlockMemberSymbol(nme, Nil), S(TermSymbol(syntax.Fun, S(clsSym), clsSym.id)), syntax.Cls, N, PlainParamList(sortedVars.iterator.map(_.param).toList) :: Nil, None, Nil, Nil, Nil, End(), sortedVars.iterator.foldLeft[Block](End()): case (acc, (_, _, vd)) => Define(vd, acc), N, N, )(N, Nil) (defn, sortedVars.iterator.map(x => (x.ctorSyms.local, x.valDefn.tsym)).toList) class ScopeRewriter(using ctx: LifterCtxNew) extends BlockTransformerShallow(SymbolSubst.Id): val extraDefns: ListBuffer[Defn] = ListBuffer.empty def applyRewrittenScope[T](r: RewrittenScope[T]): T = val LifterResult(rewritten, defns) = liftNestedScopes(r) extraDefns ++= defns.gatherUsed rewritten override def applyBlock(b: Block): Block = b match case s: Scoped => val uid = data.getUID(s) applyRewrittenScope(ctx.rewrittenScopes(uid)) match case b: Block => b case _ => die case l: Label if l.loop => val node = data.getNode(l.label) val blk = applyRewrittenScope(ctx.rewrittenScopes(l.label)) match case b: Block => b case _ => die l.copy(body = blk) case Define(defn, rest) => val dsym = defn match case f: FunDefn => f.dSym case v: ValDefn => v.tsym case c: ClsLikeDefn => c.isym ctx.liftedScopes.get(dsym) match case Some(_) => applySubBlock(rest) case None => super.applyBlock(b) case _ => super.applyBlock(b) override def applyFunDefn(fun: FunDefn) = applyRewrittenScope(ctx.rewrittenScopes(fun.dSym)) match case f: FunDefn => f case _ => die override def applyDefn(defn: Defn)(k: Defn => Block) = defn match case f: FunDefn => k(applyFunDefn(f)) case c: ClsLikeDefn => val newCls = applyRewrittenScope(ctx.rewrittenScopes(c.isym)) match case c: ClsLikeDefn => c case _ => die val newComp = c.companion.map(comp => applyRewrittenScope(ctx.rewrittenScopes(comp.isym))) match case Some(c: ClsLikeBody) => S(c) case Some(_) => die case None => N k(newCls.copy(companion = newComp)(newCls.configOverride, newCls.annotations)) case _ => super.applyDefn(defn)(k) /** * Represents a scoped object that will be rewritten to reference the lifted version of objects and variables. */ sealed abstract class RewrittenScope[T](val obj: TScopedObject[T]): val node = obj.node.get protected final val thisCapturedLocals = usedVars.reqdCaptures(obj.toInfo) val hasCapture = !thisCapturedLocals.isEmpty // These are lazy, because we don't necessarily need a captrue private final lazy val captureInfo: (ClsLikeDefn, List[(Local, TermSymbol)]) = createCaptureCls(obj) lazy val captureClass = captureInfo._1 lazy val captureMap = captureInfo._2.toMap lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] lazy val capturePath: Path protected def rewriteImpl: LifterResult[T] protected final def addExtraSyms(b: Block, captureSym: Local, objSyms: Iterable[Local], define: Bool): Block = if hasCapture then val undef = Value.Lit(Tree.UnitLit(false)).asArg val inst = Instantiate( true, Value.Ref(captureClass.sym, S(captureClass.isym)), captureInfo._2.map( (sym, _) => sym.asPath.asArg) :: Nil ) val assign = Assign(captureSym, inst, b) if define then Scoped( Set(captureSym) ++ objSyms, assign ) else assign else if define then Scoped(objSyms.toSet, b) else b /** * Rewrites the contents of this scoped object to reference the lifted versions of variables. * * @return The rewritten scoped object, plus any extra scoped definitions arising from lifting the nested scoped objects. */ final def rewrite = if hasCapture then val LifterResult(defn, extra) = rewriteImpl LifterResult(defn, captureClass :: extra) else rewriteImpl /** The path to access locals defined by this object. The primary purpose of this is to rewrite accesses * to locals that have been moved to a capture. */ protected final def pathsFromThisObj: Map[Local, LocalPath] = // Remove child BlockMemberSymbols; we will use their definition symbols instead // Locals introduced by this object val fromThisObj = node.localsWithoutBms .map: s => s -> s.asLocalPath .toMap // Locals introduced by this object that are inside this object's capture val fromCap = thisCapturedLocals .map: s => val tSym = captureMap(s) s -> LocalPath.InCapture(capturePath, tSym) .toMap // Inner symbols of nested modules and objects val isyms = node.children .collect: case ScopeNode(obj = c: ScopedObject.Companion) => val s: Local = c.clsBody.isym s -> LocalPath.BmsRef(c.bsym, c.clsBody.isym) case ScopeNode(obj = c: ScopedObject.Class) if c.isObj => c.cls.isym -> (liftedObjsMap.get(c.cls.isym) match case Some(value) => value // lifted case None => LocalPath.BmsRef(c.bsym, c.cls.isym) // not lifted ) .toMap // Note: the order here is important, as fromCap must override keys from // fromThisObj. isyms ++ fromThisObj ++ fromCap lazy val capturePaths = if thisCapturedLocals.isEmpty then Map.empty else Map(obj.toInfo -> capturePath) // BMS refs from ignored defns (including child defns of modules) // Note that we map the DefinitionSymbol to the disambiguated BMS. protected val defnPathsFromThisObj: Map[DefinitionSymbol[?], DefnRef] = node.children.filter: case s @ ScopeNode(obj = r: ScopedObject.Class) if r.isObj => false case _ => true .collect: case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => val path = r.owner match case Some(isym) => DefnRef.Field(isym, r.bsym, r.sym) case None => DefnRef.InScope(r.bsym, r.sym) r.sym -> path .toMap lazy val defnPaths: Map[DefinitionSymbol[?], DefnRef] = defnPathsFromThisObj lazy val symbolsMap: Map[Local, LocalPath] = pathsFromThisObj /** Represents a scoped object that is to be rewritten and lifted. */ sealed abstract class LiftedScope[T <: Defn](override val obj: ScopedObject.Liftable[T])(using ctx: LifterCtxNew) extends RewrittenScope[T](obj): private val AccessInfo(accessed, _, refdScopes) = usedVars.accessMap(obj.toInfo) private val AccessInfo(_, _, allRefdScopes) = usedVars.accessMapWithIgnored(obj.toInfo) private val refdDSyms = refdScopes.collect: case d: LiftedSym => d .toSet /** Symbols that this object will lose access to once lifted, and therefore must receive * as a parameter. Does not include neighbouring objects that this definition may lose * access to. Those are in a separate list. * * Includes symbols introduced by modules and objects, which could be introduced when * accessing their member functions. */ final val reqSymbols = accessed private val (reqPassedSymbols, captures) = reqSymbols .partitionMap: s => usedVars.capturesMap.get(s) match case Some(info) => R((s, info)) case None => L(s) /** Locals that are directly passed to this object, i.e. not via a capture. */ final val passedSyms: Set[Local] = reqPassedSymbols /** Maps locals to the scope where they were defined. */ final val capturesOrigin: Map[Local, ScopedInfo] = captures.toMap /** Locals that are inside captures. */ final val inCaptureSyms: Set[Local] = captures.map(_._1) /** Scopes whose captures this object requires. */ final val reqCaptures: Set[ScopedInfo] = captures.map(_._2) /** * Neighbouring objects that this definition may lose access to * once lifted, referenced by their *definition symbol* (not BMS). */ final val reqDefns = node.reqCaptureObjs .map(_.sym) .toSet.intersect(refdDSyms) /** Maps directly passed locals to the path representing that local within this object. */ protected val passedSymsMap: Map[Local, LocalPath] /** Maps scopes to the path representing their captures within this object. */ protected val capSymsMap: Map[ScopedInfo, Path] /** Maps definition symbols to the path representing that definition. */ protected val passedDefnsMap: Map[DefinitionSymbol[?], DefnRef] protected lazy val capturesOrdered: List[ScopedInfo] = reqCaptures.toList.sorted protected final lazy val passedSymsOrdered: List[Local] = reqPassedSymbols.toList.sortBy(_.uid) protected final lazy val reqDefnsOrdered: List[DefinitionSymbol[?]] = reqDefns.toList.sortBy(_.uid) override lazy val capturePaths: Map[ScopedInfo, Path] = if thisCapturedLocals.isEmpty then capSymsMap else capSymsMap + (obj.toInfo -> capturePath) // Note: we have to make this lazy because Scala's type system is unsound and // lets you access the above two fields before they are initialized // (since this constructor runs before the child classes' constructors) /** Maps symbols to the path representing that local within this object. * Includes locals defined by this object's parents, and this object's own defined locals. */ override lazy val symbolsMap: Map[Local, LocalPath] = val fromParents = reqSymbols .map: s => passedSymsMap.get(s) match // The symbol is passed directly case Some(value) => s -> value // The symbol is passed in a capture case None => val fromScope = capturesOrigin(s) val capSym = capSymsMap(fromScope) val tSym = ctx.rewrittenScopes(fromScope).captureMap(s) s -> LocalPath.InCapture(capSym, tSym) .toMap fromParents ++ pathsFromThisObj override lazy val defnPaths: Map[DefinitionSymbol[?], DefnRef] = val fromParents = reqDefns .map: s => s -> passedDefnsMap(s) .toMap defnPathsFromThisObj ++ fromParents final def formatArgs: List[Arg] = val defnsArgs = reqDefnsOrdered.map(d => ctx.defnsMap(d).asArg) val captureArgs = capturesOrdered.map(c => ctx.capturesMap(c).asArg) val localArgs = passedSymsOrdered.map(l => ctx.symbolsMap(l).asArg) defnsArgs ::: captureArgs ::: localArgs final lazy val liftedFromStagedModule: Bool = node.allAncestors.exists: case ScopeNode(ScopedObject.Companion(comp, _), _, _) => comp.isStaged case _ => false /* MIXINS */ /** * A rewritten scope with a generic VarSymbol capture symbol. */ sealed trait GenericRewrittenScope[T] extends RewrittenScope[T]: lazy val captureSym = VarSymbol(Tree.Ident(obj.nme + "$cap")) override lazy val capturePath = captureSym.asPath protected val liftedObjsOrdered: List[InnerSymbol] = node.liftedObjSyms.toList.sortBy(_.uid) protected val liftedObjsSyms: Map[InnerSymbol, VarSymbol] = liftedObjsOrdered.map: s => s -> VarSymbol(Tree.Ident(s.nme + "$")) .toMap override lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] = liftedObjsSyms.map: case k -> v => k -> v.asLocalPath protected def addExtraSyms(b: Block): Block = addExtraSyms(b, captureSym, liftedObjsSyms.values, true) /** * A rewritten scope with a TermSymbol capture symbol. */ sealed trait ClsLikeRewrittenScope[T](sym: InnerSymbol) extends RewrittenScope[T]: lazy val captureSym = TermSymbol(syntax.ImmutVal, S(sym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath = captureSym.asPath protected val liftedObjsOrdered: List[InnerSymbol] = node.liftedObjSyms.toList.sortBy(_.uid) protected val liftedObjsSyms: Map[InnerSymbol, TermSymbol] = liftedObjsOrdered.map: s => s -> TermSymbol(syntax.ImmutVal, S(sym), Tree.Ident(s.nme + "$")) .toMap override lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] = liftedObjsSyms.map: case k -> v => k -> v.asLocalPath protected def appendCaptureField(privFields: List[TermSymbol]) = if hasCapture then captureSym :: privFields else privFields protected def rewriteMethods(node: ScopeNode, methods: List[FunDefn])(using ctx: LifterCtxNew) = val mtds = node.children .map: c => ctx.rewrittenScopes(c.obj.toInfo) .collect: case r: RewrittenFunc if r.obj.isMethod.isDefined => r val (liftedMtds, extras) = mtds.map(liftNestedScopes).unzip(using l => (l.liftedDefn, l.extraDefns)) LifterResult(liftedMtds, extras.flatten) // some helpers private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) private def dupParams(plist: List[Param]): List[Param] = plist.map(dupParam) private def dupParamList(plist: ParamList): ParamList = plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam)) /* CONCRETE IMPLS */ class RewrittenScopedBlock(override val obj: ScopedObject.ScopedBlock)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: override def rewriteImpl: LifterResult[Block] = val rewriter = new BlockRewriter // Remove symbols belonging to lifted scopes val liftedChildSyms = node.allChildNodes.collect: case s @ ScopeNode(obj = l: ScopedObject.Liftable[?]) if s.isLifted => l.defn.sym val (syms, rewritten) = (obj.block.syms.toSet -- liftedChildSyms, rewriter.rewrite(obj.block.body)) val withCapture = addExtraSyms(rewritten) LifterResult(Scoped(syms, withCapture), rewriter.extraDefns.toList) class RewrittenLoop(override val obj: ScopedObject.Loop)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: override def rewriteImpl: LifterResult[Block] = val rewriter = new BlockRewriter val rewritten = rewriter.rewrite(obj.body) val withCapture = addExtraSyms(rewritten) LifterResult(withCapture, rewriter.extraDefns.toList) class RewrittenFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends RewrittenScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: override def rewriteImpl: LifterResult[FunDefn] = val rewriter = new BlockRewriter val rewritten = rewriter.rewrite(obj.fun.body) val withCapture = addExtraSyms(rewritten) LifterResult(obj.fun.copy(body = withCapture)(obj.fun.configOverride, obj.fun.annotations), rewriter.extraDefns.toList) class RewrittenClassCtor(override val obj: ScopedObject.ClassCtor)(using ctx: LifterCtxNew) extends RewrittenScope[Unit](obj): override lazy val capturePath: Path = lastWords("tried to create a capture class for a class ctor") override lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] = lastWords("tried to create obj syms for a class ctor") override protected def rewriteImpl: LifterResult[Unit] = LifterResult((), Nil) // dummy def getRewrittenCls = ctx.rewrittenScopes(obj.cls.isym) class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends RewrittenScope[ClsLikeDefn](obj) with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath override def rewriteImpl: LifterResult[ClsLikeDefn] = val rewriterCtor = new BlockRewriter val rewriterPreCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) val ctorWithCap = addExtraSyms(rewrittenCtor, captureSym, Nil, false) val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( ctor = ctorWithCap, preCtor = rewrittenPrector, privateFields = appendCaptureField(liftedObjsOrdered.map(liftedObjsSyms) ::: obj.cls.privateFields), methods = newMtds, )(obj.cls.configOverride, obj.cls.annotations) LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) class RewrittenCompanion(override val obj: ScopedObject.Companion)(using ctx: LifterCtxNew) extends RewrittenScope[ClsLikeBody](obj) with ClsLikeRewrittenScope[ClsLikeBody](obj.clsBody.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.clsBody.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath override def rewriteImpl: LifterResult[ClsLikeBody] = val rewriterCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.clsBody.ctor) val ctorWithCap = addExtraSyms(rewrittenCtor, captureSym, Nil, false) val LifterResult(newMtds, extras) = rewriteMethods(node, obj.clsBody.methods) val newComp = obj.clsBody.copy( ctor = ctorWithCap, privateFields = appendCaptureField(liftedObjsOrdered.map(liftedObjsSyms) ::: obj.clsBody.privateFields), methods = newMtds ) LifterResult(newComp, rewriterCtor.extraDefns.toList ::: extras) class LiftedFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends LiftedScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: private val passedSymsMap_ : Map[Local, VarSymbol] = passedSymsOrdered.map: s => s -> VarSymbol(Tree.Ident(s.nme)) .toMap private val capSymsMap_ : Map[ScopedInfo, VarSymbol] = capturesOrdered.map: i => val nme = data.getNode(i).obj.nme i -> VarSymbol(Tree.Ident(nme + "$cap")) .toMap private val defnSymsMap_ : Map[DefinitionSymbol[?], VarSymbol] = reqDefnsOrdered.sortBy(_.uid).map: i => val nme = data.getNode(i).obj.nme i -> VarSymbol(Tree.Ident(nme + "$")) .toMap override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.asLocalPath).toMap override protected val capSymsMap = capSymsMap_.view.mapValues(_.asPath).toMap override protected val passedDefnsMap = defnSymsMap_.view.mapValues(_.asDefnRef).toMap val auxParams: List[Param] = (reqDefnsOrdered.map(defnSymsMap_) ::: capturesOrdered.map(capSymsMap_) ::: passedSymsOrdered.map(passedSymsMap_)) .map: s => val decl = Param(FldFlags.empty.copy(isVal = false), s, N, Modulefulness.none) s.decl = S(decl) decl // Whether this can be lifted without the need to pass extra parameters. val isTrivial = auxParams.isEmpty val fun = obj.fun val (mainSym, mainDsym) = (fun.sym, fun.dSym) val auxSym = BlockMemberSymbol(fun.sym.nme + "$", Nil, fun.sym.nameIsMeaningful) val auxDsym = TermSymbol.fromFunBms(auxSym, fun.owner) // Definition with the auxiliary parameters merged into the first parameter list. private def mkFlattenedDefn: LifterResult[FunDefn] = val newPlists = fun.params match case head :: next => head.copy(params = auxParams ::: head.params) :: next case Nil => PlainParamList(auxParams) :: Nil val rewriter = new BlockRewriter val newBod = rewriter.rewrite(fun.body) val withCapture = addExtraSyms(newBod) val newDefn = fun.copy(owner = N, sym = mainSym, dSym = mainDsym, params = newPlists, body = withCapture)( fun.configOverride, if liftedFromStagedModule && !fun.isStaged then Annot.Modifier(Keyword.`staged`) :: fun.annotations else fun.annotations) LifterResult(newDefn, rewriter.extraDefns.toList) // Definition with the auxiliary parameters merged into the second parameter list. private def mkAuxDefn: FunDefn = val newPList = PlainParamList(dupParams(auxParams)) val (newPlists, syms, restSym) = fun.params match case head :: _ => val duped = dupParamList(head) ( newPList :: duped :: Nil, newPList.params.map(_.sym) ::: duped.params.map(_.sym), duped.restParam.map(_.sym)) case Nil => lastWords("tried to make an aux defn for a function with no parameter list") val args = restSym match case Some(value) => val tail = Arg(S(SpreadKind.Eager), value.asPath) :: Nil syms.foldLeft(tail): case (acc, sym) => Arg(N, sym.asPath) :: acc case None => syms.map(s => Arg(N, s.asPath)) val call = Call(Value.Ref(fun.sym, S(fun.dSym)), args ne_:: Nil)(true, true, false) val bod = Return(call, false) FunDefn( N, auxSym, auxDsym, newPlists, bod )(N, fun.annotations) private val aux = Lazy[Defn](mkAuxDefn) def rewriteCall(c: Call, argss: NELs[List[Arg]])(using ctx: LifterCtxNew): Call = if isTrivial then if argss is c.argss then c else c.copy(argss = argss)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall).withLocOf(c) else Call( Value.Ref(mainSym, S(mainDsym)), (formatArgs ::: argss.head) ne_:: argss.tail )( isMlsFun = true, mayRaiseEffects = c.mayRaiseEffects, explicitTailCall = c.explicitTailCall ).withLoc(c.toLoc) def rewriteRef(using ctx: LifterCtxNew): Call = if isTrivial then lastWords("tried to rewrite a ref to a trivial function") aux.force // forces computation Call( Value.Ref(auxSym, S(auxDsym)), formatArgs ne_:: Nil )( isMlsFun = true, mayRaiseEffects = false, explicitTailCall = false ) def rewriteImpl: LifterResult[FunDefn] = val LifterResult(lifted, extra) = mkFlattenedDefn if isTrivial then LifterResult(lifted, extra) else LifterResult(lifted, aux :: extra) class LiftedClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends LiftedScope[ClsLikeDefn](obj) with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath private val passedSymsMap_ : Map[Local, (vs: VarSymbol, ts: TermSymbol)] = passedSymsOrdered.map: s => s -> ( VarSymbol(Tree.Ident(s.nme)), TermSymbol(syntax.LetBind, S(obj.cls.isym), Tree.Ident(s.nme)) ) .toMap private val capSymsMap_ : Map[ScopedInfo, (vs: VarSymbol, ts: TermSymbol)] = capturesOrdered.map: i => val nme = data.getNode(i).obj.nme + "$cap" i -> ( VarSymbol(Tree.Ident(nme)), TermSymbol(syntax.LetBind, S(obj.cls.isym), Tree.Ident(nme)) ) .toMap private val defnSymsMap_ : Map[DefinitionSymbol[?], (vs: VarSymbol, ts: TermSymbol)] = reqDefnsOrdered.map: i => i -> ( VarSymbol(Tree.Ident(i.nme + "$")), TermSymbol(syntax.LetBind, S(obj.cls.isym), Tree.Ident(i.nme + "$")) ) .toMap private lazy val extraPrivSyms: List[TermSymbol] = liftedObjsOrdered.map(liftedObjsSyms) ::: reqDefnsOrdered.map(defnSymsMap_(_).ts) ::: capturesOrdered.map(capSymsMap_(_).ts) ::: passedSymsOrdered.map(passedSymsMap_(_).ts) override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.ts.asLocalPath).toMap override protected val capSymsMap = capSymsMap_.view.mapValues(_.ts.asPath).toMap override protected val passedDefnsMap = defnSymsMap_.view.mapValues(_.ts.asDefnRef).toMap val auxParams: List[Param] = (reqDefnsOrdered.map(x => defnSymsMap_(x).vs) ::: capturesOrdered.map(x => capSymsMap_(x).vs) ::: passedSymsOrdered.map(x => passedSymsMap_(x).vs)) .map(Param.simple(_)) val auxParamList = PlainParamList(auxParams) // Whether this can be lifted without the need to pass extra parameters. val isTrivial = auxParams.isEmpty val cls = obj.cls val flattenedSym = BlockMemberSymbol(obj.cls.sym.nme + "$", Nil, true) val flattenedDSym = TermSymbol.fromFunBms(flattenedSym, N) // Contains *all* parameters, and applies them all at once in a single `Instantiate` def mkFlattenedDefn: FunDefn = // Symbols for the aux parameter list val auxSyms = auxParams.map(p => VarSymbol(Tree.Ident(p.sym.nme))) val auxParamListLocal = PlainParamList(auxSyms.map(Param.simple(_))) val dupedClsAuxParams = cls.auxParams.map(dupParamList(_)) val dupedMainOpt = cls.paramsOpt.map(dupParamList(_)) val clsParamLists = dupedMainOpt match case Some(dupedMain) => dupedMain :: dupedClsAuxParams case None => dupedClsAuxParams // Contains aux param list val allParamLists = auxParamListLocal :: clsParamLists // Uses the symbols from pl1. def applyPlToPl(pl1: ParamList, pl2: ParamList): List[Arg] = (pl1.restParam, pl2.restParam) match case (S(rp), S(_)) => pl1.params.foldRight(Arg(S(SpreadKind.Eager), rp.sym.asPath) :: Nil)(_.sym.asPath.asArg :: _) case (N, N) => pl1.paramSyms.map(_.asPath.asArg) case _ => die // If class has a main param list, the aux list comes after it inline def appliedMainAndAuxArgs(rest: List[List[Arg]]): List[List[Arg]] = (dupedMainOpt, cls.paramsOpt) match case (S(dupedMain), S(clsParams)) => applyPlToPl(dupedMain, clsParams) :: applyPlToPl(auxParamListLocal, auxParamList) :: rest case (N, N) => applyPlToPl(auxParamListLocal, auxParamList) :: rest case _ => die val appliedClsAuxArgs = (dupedClsAuxParams zip cls.auxParams).map(applyPlToPl) // main :: aux :: clsAuxArgs // or aux :: clsAuxArgs val argsList = appliedMainAndAuxArgs(appliedClsAuxArgs) val ref = Value.Ref(obj.cls.sym, S(obj.cls.isym)) val inst = Instantiate(false, ref, argsList) val bod = Return(inst, false) FunDefn(N, flattenedSym, flattenedDSym, allParamLists, bod)(N, annotations = Nil) private val flat = Lazy[Defn](mkFlattenedDefn) def instObject = Instantiate(false, Value.Ref(cls.sym, S(cls.isym)), formatArgs :: Nil) // Rewrite a naked reference to a parameterized class constructor. // Returns a Call to the curried C$ wrapper partially applied with formatArgs. def rewriteCtorRef: Call = if isTrivial then lastWords("tried to rewrite a ref to a trivial class ctor") flat.force Call( Value.Ref(flattenedSym, S(flattenedDSym)), formatArgs ne_:: Nil )( isMlsFun = true, mayRaiseEffects = false, explicitTailCall = false ) def rewriteInstantiate(inst: Instantiate, argss: List[List[Arg]])(k: Result => Block): Block = if obj.isObj then lastWords("tried to rewrite instantiate for an object") val path = Value.Ref(cls.sym, S(cls.isym)) if isTrivial then if (inst.cls === path) && (inst.argss is argss) then k(inst) else k(inst.copy(cls = path, argss = argss).withLocOf(inst)) else if cls.paramsOpt.isEmpty && cls.auxParams.isEmpty then // Paramless class: lifter args go directly into the Instantiate constructor k(Instantiate(inst.mut, path, (formatArgs ::: argss.head) :: argss.tail).withLoc(inst.toLoc)) else // Parameterized class: use Instantiate with original args + lifter args inserted after the first list k(Instantiate(inst.mut, path, argss.head :: formatArgs :: argss.tail).withLoc(inst.toLoc)) def rewriteCall(c: Call, argss: NELs[List[Arg]])(k: Result => Block)(using ctx: LifterCtxNew): Block = if obj.isObj then lastWords("tried to rewrite instantiate for an object") val path = Value.Ref(cls.sym, S(cls.isym)) if isTrivial then if c.argss is argss then k(c) else k(c.copy(argss = argss)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall).withLocOf(c)) else if cls.paramsOpt.isEmpty && cls.auxParams.isEmpty then // Paramless class: unreachable lastWords("Call to paramless class") else // Parameterized class: Same as Instantiate case k(Instantiate(false, path, argss.head :: formatArgs :: argss.tail).withLoc(c.toLoc)) def rewriteImpl: LifterResult[ClsLikeDefn] = val rewriterCtor = new BlockRewriter val rewriterPreCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) val ctorWithCap = addExtraSyms(rewrittenCtor, captureSym, Nil, false) // Assign passed locals and captures val ctorWithPassed = passedSymsOrdered.foldRight(ctorWithCap): case (sym, acc) => val (vs, ts) = passedSymsMap_(sym) Assign(ts, vs.asPath, acc) val ctorWithCaps = capturesOrdered.foldRight(ctorWithPassed): case (sym, acc) => val (vs, ts) = capSymsMap_(sym) Assign(ts, vs.asPath, acc) val ctorWithDefns = reqDefnsOrdered.foldRight(ctorWithCaps): case (sym, acc) => val (vs, ts) = defnSymsMap_(sym) Assign(ts, vs.asPath, acc) val newAuxList = if isTrivial then cls.auxParams else auxParamList :: cls.auxParams val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( owner = N, k = syntax.Cls, // turn objects into classes ctor = ctorWithDefns, preCtor = rewrittenPrector, privateFields = appendCaptureField(extraPrivSyms ::: obj.cls.privateFields), methods = newMtds, auxParams = newAuxList )(obj.cls.configOverride, if liftedFromStagedModule && !obj.cls.isStaged then Annot.Modifier(Keyword.`staged`) :: obj.cls.annotations else obj.cls.annotations) val extrasDefns = rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras LifterResult(newCls, flat :: extrasDefns) private def createRewritten[T](s: TScopeNode[T])(using ctx: LifterCtxNew): RewrittenScope[T] = s.obj match case _: ScopedObject.Top => lastWords("tried to rewrite the top-level scope") case o: ScopedObject.Class => if s.isLifted then LiftedClass(o) else RewrittenClass(o) case o: ScopedObject.Companion => RewrittenCompanion(o) case o: ScopedObject.ClassCtor => RewrittenClassCtor(o) case o: ScopedObject.Func => if s.isLifted then LiftedFunc(o) else RewrittenFunc(o) case o: ScopedObject.Loop => RewrittenLoop(o) case o: ScopedObject.ScopedBlock => RewrittenScopedBlock(o) // Note: we must write this as a definition here to have tighter types private def rewriteScope[T <: Defn](l: LiftedScope[T])(using ctx: LifterCtxNew) = val LifterResult[T](d1, d2) = liftNestedScopes[T](l) (d1, d2) /** * Lifts scopes nested within `s`, and then rewrites `s`. * * @param s The scope to be rewritten. * @param r The rewritten scope associated with `s`. * @param ctx The lifter context. * @return The rewritten scope with the additional definitions. */ private def liftNestedScopesImpl[T](scope: RewrittenScope[T])(using ctx: LifterCtxNew): LifterResult[T] = val node = scope.node // Add the symbols map of the current scope // Note: this will be reset to the original value in liftNestedScopes ctx.symbolsMap ++= scope.symbolsMap ctx.capturesMap ++= scope.capturePaths ctx.defnsMap ++= scope.defnPaths val rewrittenScopes = node.children.map(createRewritten) // The scopes in `lifted` will be rewritten right now // The scopes in `ignored` will be rewritten in-place when traversing the block val (lifted, ignored) = rewrittenScopes.partitionMap: case s: LiftedScope[?] => L(s) case s => R(s) for r <- rewrittenScopes do ctx.rewrittenScopes.put(r.obj.toInfo, r) for l <- lifted do ctx.liftedScopes.put(l.obj.sym, l) val LifterResult(rewrittenObj, extraDefns) = scope.rewrite val (res1, res2) = lifted.map(rewriteScope).unzip val defns = res1 ++ res2.flatten ++ extraDefns LifterResult(rewrittenObj, defns) def liftNestedScopes[T](r: RewrittenScope[T])(using ctx: LifterCtxNew): LifterResult[T] = val curSyms = ctx.symbolsMap val curCaptures = ctx.capturesMap val curDefns = ctx.defnsMap if r.node.isLifted then ctx.symbolsMap = Map.empty ctx.capturesMap = Map.empty ctx.defnsMap = Map.empty val ret = liftNestedScopesImpl(r) ctx.symbolsMap = curSyms ctx.capturesMap = curCaptures ctx.defnsMap = curDefns ret // entry point given ignoredScopes: IgnoredScopes = IgnoredScopes(N) val data = ScopeData(topLevelBlk) val metadata = data.root.children.foldLeft(LifterMetadata.empty)(_ ++ createMetadata(_)) def asDSym(s: ClsSym | ModuleOrObjSym): DefinitionSymbol[?] = s val ignored: Set[ScopedInfo] = metadata.unliftable.map(asDSym) ignoredScopes.ignored = S(ignored) val usedVars = UsedVarAnalyzer(topLevelBlk, data) def transform = given ctx: LifterCtxNew = new LifterCtxNew val root = data.root val children = root.children children.foreach: c => ctx.rewrittenScopes.put(c.obj.toInfo, createRewritten(c)) val topLevelRewriter = new ScopeRewriter val (syms, top) = root.obj.contents match case Scoped(syms, body) => (syms.toSet, body) case b => (Set.empty, b) val transformed = topLevelRewriter.applyBlock(top) val newSyms = syms ++ topLevelRewriter.extraDefns.map(_.sym) val withDefns = topLevelRewriter.extraDefns.foldLeft(transformed): case (acc, d) => Define(d, acc) Scoped(newSyms, withDefns) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala ================================================ package hkmc2 package codegen import scala.language.implicitConversions import scala.annotation.tailrec import os.{Path as AbsPath, RelPath} import sourcecode.Line import mlscript.utils.*, shorthands.* import utils.* import hkmc2.Message.MessageContext import codegen.ReflectionInstrumenter import semantics.*, ucs.FlatPattern import hkmc2.{semantics => sem} import semantics.{Term => st} import semantics.Term.{Throw => _, *} import semantics.Elaborator.{State, Ctx, ctx} import syntax.{Literal, Tree, SpreadKind} import hkmc2.syntax.{Fun, Keyword} abstract class TailOp extends (Result => Block) object Ret extends TailOp: def apply(r: Result): Block = Return(r, implct = false) object ImplctRet extends TailOp: def apply(r: Result): Block = r match case Value.Lit(Tree.UnitLit(false)) => End() case _ => Return(r, implct = true) object Thrw extends TailOp: def apply(r: Result): Block = Throw(r) class LoweringCtx( initMap: Map[Local, Value], // No longer in meaningful use and could be removed if we don't find a use for it val mayRet: Bool, // For rewriting while loop into tail recursive function, represent whether an explicit return is legal in the current block private val definedSymsDuringLowering: collection.mutable.Set[Symbol] // used to create Scoped blocks ): val map = initMap def collectScopedSym(s: Symbol) = definedSymsDuringLowering.add(s) def collectScopedSyms(s: Symbol*) = definedSymsDuringLowering.addAll(s) def registerTempSymbol(trm: Option[Term], dbgNme: Str = "tmp")(using State) = val tmp = new TempSymbol(trm, dbgNme) definedSymsDuringLowering.add(tmp) tmp def getCollectedSym: collection.Set[Symbol] = definedSymsDuringLowering /* def +(kv: (Local, Value)): Subst = kv match case (ns: NamedSymbol, Value.Ref(ts: TempSymbol)) => ts.nameHints += ns.name case _ => Subst(map + kv) */ def apply(v: Value): Value = v match case Value.Ref(l, _) => map.getOrElse(l, v) case _ => v object LoweringCtx: def loweringCtx(using sub: LoweringCtx): LoweringCtx = sub val empty = LoweringCtx(Map.empty, mayRet = false, collection.mutable.Set.empty) def nestFunc(using sub: LoweringCtx): LoweringCtx = LoweringCtx(sub.map, mayRet = true, sub.definedSymsDuringLowering) def nestScoped(using sub: LoweringCtx): LoweringCtx = LoweringCtx(sub.map, sub.mayRet, collection.mutable.Set.empty) end LoweringCtx import LoweringCtx.loweringCtx object Lowering: def compError: Block = Throw(Value.Lit(Tree.StrLit("This code cannot be run as its compilation yielded an error."))) def fail(err: ErrorReport)(using Raise): Block = raise(err) compError import Lowering.* class Lowering()(using Config, TL, Raise, State, Ctx): extension (t: Term) def instantiated = t match case r: Resolvable => tl.trace[Term](s"Expanding term ${r}", post = t => s"~> ${t}"): r.expanded case t => t val lowerHandlers: Bool = config.effectHandlers.isDefined val lift: Bool = config.liftDefns.isDefined private lazy val wasmBinaryIntrinsicMap: Map[Str, Str] = Map( "+" -> "plus_impl", "-" -> "minus_impl", "*" -> "times_impl", "/" -> "div_impl", "%" -> "mod_impl", "===" -> "eq_impl", "!==" -> "neq_impl", "<" -> "lt_impl", "<=" -> "le_impl", ">" -> "gt_impl", ">=" -> "ge_impl" ) private lazy val wasmUnaryIntrinsicMap: Map[Str, Str] = Map( "-" -> "neg_impl", "+" -> "pos_impl", "!" -> "not_impl" ) private def wasmIntrinsicPath(sym: BuiltinSymbol, unary: Bool): Opt[Path] = if config.target is CompilationTarget.Wasm then val map = if unary then wasmUnaryIntrinsicMap else wasmBinaryIntrinsicMap map.get(sym.nme).map(name => Value.Ref(State.wasmSymbol).selN(Tree.Ident(name))) else N private lazy val wasmIntrinsicSymbols: Set[BlockMemberSymbol] = Set( ctx.builtins.wasm.plus_impl, ctx.builtins.wasm.minus_impl, ctx.builtins.wasm.times_impl, ctx.builtins.wasm.div_impl, ctx.builtins.wasm.mod_impl, ctx.builtins.wasm.eq_impl, ctx.builtins.wasm.neq_impl, ctx.builtins.wasm.lt_impl, ctx.builtins.wasm.le_impl, ctx.builtins.wasm.gt_impl, ctx.builtins.wasm.ge_impl, ctx.builtins.wasm.neg_impl, ctx.builtins.wasm.pos_impl, ctx.builtins.wasm.not_impl ) lazy val unreachableFn = Select(Value.Ref(State.runtimeSymbol), Tree.Ident("unreachable"))(S(State.unreachableSymbol)) def unit: Path = Select(Value.Ref(State.runtimeSymbol), Tree.Ident("Unit"))(S(State.unitSymbol)) // type Rcd = (mut: Bool, args: List[RcdArg]) // * Better, but Scala's patmat exhaustiveness chokes on it type Rcd = (Bool, List[RcdArg]) def returnedTerm(t: st)(using LoweringCtx): Block = term(t)(Ret)(using LoweringCtx.nestFunc) def parentConstructor(cls: Term, args: Ls[Term])(using LoweringCtx) = if args.length > 1 then raise: ErrorReport( msg"Extending a class with multiple parameter lists is not supported" -> Loc(cls :: args) :: Nil, source = Diagnostic.Source.Compilation ) lowerSuperCtorCall( Value.Ref(State.builtinOpsMap("super")), isMlsFun = true, isTailCall = false, args.headOption, N, // TODO: location? )(c => Return(c, implct = true)) // * Used to work around Scala's @tailrec annotation for those few calls that are not in tail position. final def term_nonTail(t: st, inStmtPos: Bool = false)(k: Result => Block)(using LoweringCtx): Block = term(t, inStmtPos = inStmtPos)(k) @tailrec final def splitBlock(stats: Ls[Statement], imps: Ls[Import], funs: Ls[TermDefinition], rest: Ls[Statement]) : (Ls[Import], Ls[TermDefinition], Ls[Statement]) = stats match case (imp: Import) :: stats => splitBlock(stats, imp :: imps, funs, rest) case (fun: TermDefinition) :: stats if fun.k is syntax.Fun => splitBlock(stats, imps, fun :: funs, rest) case stat :: stats => splitBlock(stats, imps, funs, stat :: rest) case Nil => (imps.reverse, funs.reverse, rest.reverse) def block(stats: Ls[Statement], res: Rcd \/ Term, inStmtPos: Bool = false) (k: Result => Block)(using LoweringCtx): Block = // TODO we should also isolate and reorder classes by inheritance topological sort val (imps, funs, rest) = splitBlock(stats, Nil, Nil, Nil) def blockImpl(stats: Ls[Statement], res: Rcd \/ Term)(using LoweringCtx): Block = stats match case (t: sem.Term) :: stats => term(t, inStmtPos = true)(Assign.discard(_, blockImpl(stats, res))) case Nil => res match case R(res) => term(res, inStmtPos = inStmtPos)(k) case L((mut, flds)) => k(Record(mut, flds.reverse)) case RcdSpread(bod) :: stats => res match case R(_) => wat("RcdField in non-Rcd context", res) case L((mut, flds)) => subTerm(bod): l => blockImpl(stats, L((mut, RcdArg(N, l) :: flds))) case RcdField(lhs, rhs) :: stats => res match case R(_) => wat("RcdField in non-Rcd context", res) case L((mut, flds)) => subTerm(lhs): l => subTerm_nonTail(rhs): r => blockImpl(stats, L((mut, RcdArg(S(l), r) :: flds))) case (decl @ LetDecl(sym, annotations)) :: stats => reportAnnotations(decl, annotations) if sym.asTrm.forall(_.owner.isEmpty) then loweringCtx.collectScopedSym(sym) blockImpl(stats, res) case DefineVar(sym, rhs) :: stats => term(rhs): r => Assign(sym, r, blockImpl(stats, res)) case (_: SetConfig) :: stats => // Config changes are handled at the program level; skip during block lowering blockImpl(stats, res) case (imp: Import) :: stats => raise(ErrorReport( msg"Imports must be at the top level" -> imp.toLoc :: Nil, source = Diagnostic.Source.Compilation)) blockImpl(stats, res) case (d: Declaration) :: stats => d match case td: TermDefinition => reportAnnotations(td, td.extraAnnotations) if td.owner.isEmpty && td.hasDeclareModifier.isEmpty then loweringCtx.collectScopedSym(td.sym) td.body match case N => // abstract declarations have no lowering blockImpl(stats, res) case S(bod) => td.k match case knd: syntax.Val => assert(td.params.isEmpty) val cfgOverride = td.extraAnnotations.collectFirst: case Annot.Config(modify) => modify(config) subTerm_nonTail(bod)(r => // Assign(td.sym, r, // term(st.Blk(stats, res))(k))) Define(ValDefn(td.tsym, td.sym, r)(cfgOverride, td.annotations), blockImpl(stats, res)))(using LoweringCtx.nestFunc) case syntax.Fun => val (paramLists, bodyBlock) = setupFunctionOrByNameDef(td.params, bod, S(td.sym.nme)) val cfgOverride = td.extraAnnotations.collectFirst: case Annot.Config(modify) => modify(config) Define(FunDefn(td.owner, td.sym, td.tsym, paramLists, bodyBlock)(cfgOverride, td.annotations), blockImpl(stats, res)) case syntax.Ins => // Implicit instances are not parameterized for now. assert(td.params.isEmpty) val cfgOverride = td.extraAnnotations.collectFirst: case Annot.Config(modify) => modify(config) subTerm(bod)(r => Define(ValDefn(td.tsym, td.sym, r)(cfgOverride, td.annotations), blockImpl(stats, res))) case syntax.LetBind | syntax.HandlerBind => fail: ErrorReport( msg"Unexpected declaration kind '${td.k.str}' in lowering" -> td.toLoc :: Nil, source = Diagnostic.Source.Compilation) case cls: ClassLikeDef if cls.sym.defn.exists(_.hasDeclareModifier.isDefined) => // * Declarations have no lowering blockImpl(stats, res) case cls: ClassDef if cls.moduleCompanion.isDefined => // * Class definitions are pure, but their companions might not be, // * as they may contain static initialization code; // * therefore, we lower classes at the point where the companion is defined, // * if it is defined, rather than at the point where the class is defined. reportAnnotations(cls, cls.extraAnnotations) blockImpl(stats, res) case _defn: ClassLikeDef => if _defn.owner.isEmpty then loweringCtx.collectScopedSym(_defn.bsym) val defn = _defn match case cls: ClassDef => cls case mod: ModuleOrObjectDef if mod.kind is syntax.Mod => // * Currently, both objects and modules are represented as `ModuleOrObjectDef`s mod.classCompanion match case S(comp) => comp.defn.getOrElse(wat("Module companion without definition", mod.companion)) case N => val stagedAnnots = mod.annotations.collect { case Annot.Modifier(Keyword.`staged`) => Annot.Modifier(Keyword.`staged`) } ClassDef.Plain(mod.owner, syntax.Cls, new ClassSymbol(Tree.DummyTypeDef(syntax.Cls), mod.sym.id), mod.bsym, Nil, N, ObjBody(Blk(Nil, UnitVal())), S(mod.sym), stagedAnnots, ) case _ => _defn reportAnnotations(defn, defn.extraAnnotations) val bufferableAnnots = defn.annotations.flatMap: case Annot.Trm(trm: SynthSel) => if trm.sym.contains(ctx.builtins.annotations.buffered) then S(false) else if trm.sym.contains(ctx.builtins.annotations.bufferable) then S(true) else N case _ => N if bufferableAnnots.length > 1 then raise(ErrorReport( msg"Only one of bufferable annotation is allowed." -> defn.toLoc :: Nil, source = Diagnostic.Source.Compilation )) if bufferableAnnots.length >= 1 then if defn.companion.isDefined then raise(ErrorReport( msg"No companion class is allowed with @buffered or @bufferable." -> defn.toLoc :: Nil, source = Diagnostic.Source.Compilation )) val bufferable = bufferableAnnots.headOption val (mtds, publicFlds, privateFlds, ctor) = defn match case pd: PatternDef => // Compile the pattern definition into `unapply` and `unapplyStringPrefix` // methods using the `SplitCompiler`, which transliterate the pattern into // UCS splits that backtrack without any optimizations. val compiler = new ups.SplitCompiler val methods = compiler.compilePattern(pd) // We only need `owner`, `sym`, `params` and `body` val mtds = methods.map: case (sym, params, split) => val paramLists = params :: Nil val bodyBlock = inScopedBlock(ucs.Normalization(this)(split)(Ret)) FunDefn.withFreshSymbol(N, sym, paramLists, bodyBlock)(configOverride = N, annotations = Nil) // The return type is intended to be consistent with `gatherMembers` (mtds, Nil, Nil, End()) case _ => gatherMembers(defn.body) val mod = defn.companion match case S(sym) => sym.defn match case S(mod: ModuleOrObjectDef) => reportAnnotations(mod, mod.extraAnnotations) mod.ext match case S(ext) => fail: ErrorReport( msg"Modules cannot have an extension clause." -> ext.toLoc :: Nil, source = Diagnostic.Source.Compilation ) case N => val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(mod.body) S(ClsLikeBody(mod.sym, mtds, privateFlds, publicFlds, ctor, mod.annotations)) case _ => N case _ => N defn.ext match case N => val cfgOverride = defn.extraAnnotations.collectFirst: case Annot.Config(modify) => modify(config) Define( ClsLikeDefn(defn.owner, defn.sym, defn.bsym, defn.ctorSym, defn.kind, defn.paramsOpt, defn.auxParams, N, mtds, privateFlds, publicFlds, End(), ctor, mod, bufferable, )(cfgOverride, defn.annotations), blockImpl(stats, res)) case S(ext) => assert(k isnt syntax.Mod) // modules can't extend things and can't have super calls val cfgOverride = defn.extraAnnotations.collectFirst: case Annot.Config(modify) => modify(config) subTerm(ext.cls): clsp => val pctor = inScopedBlock(parentConstructor(ext.cls, ext.args)) Define( ClsLikeDefn( defn.owner, defn.sym, defn.bsym, defn.ctorSym, defn.kind, defn.paramsOpt, defn.auxParams, S(clsp), mtds, privateFlds, publicFlds, pctor, ctor, mod, bufferable, )(cfgOverride, defn.annotations), blockImpl(stats, res) ) case td: TypeDef => // * Type definitions are erased blockImpl(stats, res) blockImpl(imps ::: funs ::: rest, res) // * Lowers the `super(...)` call we get from the `extends C(...)` syntax def lowerSuperCtorCall(fr: Path, isMlsFun: Bool, isTailCall: Bool, arg: Opt[Term], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = arg match case S(arg) => lowerArgs(arg)(as => k(Call(fr, as ne_:: Nil)(isMlsFun, true, isTailCall).withLoc(loc))) case N => // * No arguments to a super ctor means a nullary call, e.g., `extends C` means `extends C()` k(Call(fr, Nil ne_:: Nil)(isMlsFun, true, isTailCall).withLoc(loc)) /** Lower a call with multiple argument lists into `Call` nodes, * trying to group as many as possible into a single one * when they correspond to parameter lists of the same callee. */ def lowerMultiCall(fr: Path, isMlsFun: Bool, isTailCall: Bool, args: Ls[Term], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = def zipArgs(remainingParamss: Ls[ParamList], remainingArgss: Ls[Term], acc: Ls[Ls[Arg]]): Block = (remainingParamss, remainingArgss) match case (ps :: remainingParams, args :: remainingArgs) => lowerArgs(args)(as => zipArgs(remainingParams, remainingArgs, as :: acc)) case (Nil, Nil) => k(Call(fr, acc.reverse.ne_!)(isMlsFun, true, isTailCall).withLoc(loc)) case (Nil, args :: remainingArgss) => acc.reverse match case Nil => lowerRemainingCalls(fr, args, remainingArgss, isTailCall, loc)(k) case acc: NELs[Ls[Arg]] => val tmp = loweringCtx.registerTempSymbol(N, "baseCall") val call = Call(fr, acc)(isMlsFun, true, isTailCall).withLoc(loc) Assign(tmp, call, lowerRemainingCalls(Value.Ref(tmp), args, remainingArgss, isTailCall, loc)(k)) case (_ :: _, Nil) => k(Call(fr, acc.reverse.ne_!)(isMlsFun, true, isTailCall).withLoc(loc)) fr.targetSymbol match case S(fs: TermSymbol) => fs.defn match case S(td: TermDefinition) => zipArgs(td.params, args, Nil) case _ => zipArgs(Nil, args, Nil) case _ => zipArgs(Nil, args, Nil) def lowerRemainingCalls(base: Path, args: Term, remainingArgss: Ls[Term], isTailCall: Bool, loc: Opt[Loc]) (k: Result => Block)(using LoweringCtx): Block = lowerArgs(args): as => val call = Call(base, as ne_:: Nil)(isMlsFun = false, true, isTailCall).withLoc(loc) remainingArgss match case Nil => k(call) case args :: remainingArgss => val tmp = loweringCtx.registerTempSymbol(N, "callPrefix") Assign(tmp, call, lowerRemainingCalls(Value.Ref(tmp), args, remainingArgss, isTailCall, loc)(k)) /** Lower an instantiation with multiple argument lists into `Instantiate` and `Call` nodes, * trying to group as many as possible into a single `Instantiate` * when they correspond to constructor parameter lists of the same class. * If fewer argument lists are provided than constructor parameter lists, eta-expands * the missing ones with fresh lambdas (avoiding reliance on mutable JS class curry semantics). */ def lowerMultiInstantiate(mut: Bool, cls: Path, args: Ls[Term])(k: Result => Block)(using LoweringCtx): Block = // Nullary instantiations are represented with one empty argument list, matching existing `Instantiate` usage. def buildInstantiate(argss: Ls[Ls[Arg]]): Instantiate = Instantiate(mut, cls, if argss.isEmpty then Nil :: Nil else argss) // * Zip constructor param lists with argument lists, accumulating lowered args. // * Consumes one argument list per constructor param list; when all ctor params are // * consumed but extra args remain, falls back to `Call` nodes on the result. // * When args run out before ctor params are consumed, eta-expands the remaining ones. def zipArgs(remainingCtorParamss: Ls[ParamList], remainingArgss: Ls[Term], acc: Ls[Ls[Arg]]): Block = (remainingCtorParamss, remainingArgss) match case (ps :: remainingParams, args :: remainingArgs) => lowerArgs(args)(as => zipArgs(remainingParams, remainingArgs, as :: acc)) case (Nil, Nil) => k(buildInstantiate(acc.reverse)) case (Nil, args :: remainingArgss) => val tmp = loweringCtx.registerTempSymbol(N, "baseInst") Assign(tmp, buildInstantiate(acc.reverse), lowerRemainingCalls(Value.Ref(tmp), args, remainingArgss, isTailCall = false, N)(k)) case (remainingParamss, Nil) => // * Eta-expand missing argument lists by creating lambdas for each remaining param list. // * This makes partial `new C(args...)` explicit instead of relying on the JS class curry. def etaExpand(remainingParamss: Ls[ParamList], accArgss: Ls[Ls[Arg]]): Result = remainingParamss match case Nil => buildInstantiate(accArgss) case ps :: rest => val freshSyms = ps.params.map(p => new VarSymbol(new Tree.Ident(p.sym.nme))) softTODO(ps.restParam.isEmpty, "Eta expanding rest parameters in constructor definitions is not yet supported") val freshParams = (ps.params zip freshSyms).map((p, s) => Param(p.flags, s, N, p.modulefulness)) val freshParamList = ParamList(ps.flags, freshParams, N) val freshArgs = freshSyms.map(s => Arg(N, Value.Ref(s))) Lambda(freshParamList, Return(etaExpand(rest, accArgss :+ freshArgs), implct = false)) k(etaExpand(remainingParamss, acc.reverse)) // * Resolve the class definition to get the constructor param lists. // * The class path typically resolves to a TermSymbol (the constructor function), // * so we also look up the owner InnerSymbol via TermDefinition#owner. // * Note: apparently, this can also be accessed through TermDefinition#companionClass // * (what Copilot initially used), which is weird. val ctorParamLists: Ls[ParamList] = cls.targetSymbol.flatMap: sym => sym.asClsOrMod.flatMap(_.defn) orElse sym.asTrm.flatMap(_.owner).flatMap(_.asDefnSym.defn) .fold(Nil: Ls[ParamList]): clsDef => clsDef.paramsOpt.toList ::: clsDef.auxParams if ctorParamLists.isEmpty then // * Need to specially handle no-param classes args match case Nil => k(buildInstantiate(Nil)) case args :: remainingArgss => lowerArgs(args): as => remainingArgss match case Nil => k(buildInstantiate(as :: Nil)) case remainingArgss => val tmp = loweringCtx.registerTempSymbol(N, "baseInst") Assign(tmp, buildInstantiate(as :: Nil), lowerRemainingCalls(Value.Ref(tmp), remainingArgss.head, remainingArgss.tail, isTailCall = false, N)(k)) else zipArgs(ctorParamLists, args, Nil) def lowerArgs(arg: Term)(k: Ls[Arg] => Block)(using LoweringCtx): Block = arg match case Tup(fs) => if fs.exists(e => e match case Spd(SpreadKind.Lazy, _) => true // is lazy spread case _ => false) then raise(ErrorReport( msg"Lazy spreads are not supported in call arguments" -> arg.toLoc :: Nil, S(arg), source = Diagnostic.Source.Compilation)) args(fs)(as => k(as)) case _ => // Application arguments that are not tuples represent spreads, as in `f(...arg)` subTerm_nonTail(arg): ar => k(Arg(spread = S(SpreadKind.Eager), ar) :: Nil) def warnPureExprInStmtPos(loc: Opt[Loc], extraInfo: => Opt[Any] = N)(using Line): Unit = raise: WarningReport(msg"Pure expression in statement position" -> loc :: Nil, extraInfo, source = Diagnostic.Source.Compilation) def ref(ref: st.Ref, annots: List[Annot], disamb: Opt[DefinitionSymbol[?]], inStmtPos: Bool)(k: Result => Block)(using LoweringCtx): Block = def warnStmt = if inStmtPos then warnPureExprInStmtPos(ref.toLoc, S(ref)) val sym = ref.sym sym match case ctx.builtins.source.bms | ctx.builtins.js.bms | ctx.builtins.wasm.bms | ctx.builtins.debug.bms | ctx.builtins.annotations.bms => return fail: ErrorReport( msg"Module '${sym.nme}' is virtual (i.e., \"compiler fiction\"); cannot be used directly" -> ref.toLoc :: Nil, S(ref), source = Diagnostic.Source.Compilation) case sym if sym.asCls.exists(ctx.builtins.virtualClasses) => return fail: ErrorReport( msg"Symbol '${sym.nme}' is virtual (i.e., \"compiler fiction\"); cannot be used as a term" -> ref.toLoc :: Nil, S(ref), source = Diagnostic.Source.Compilation) case _ => () sym match case sym: BuiltinSymbol => warnStmt if sym.binary then val t1 = new Tree.Ident("arg1") val t2 = new Tree.Ident("arg2") val p1 = Param(FldFlags.empty, VarSymbol(t1), N, Modulefulness.none) val p2 = Param(FldFlags.empty, VarSymbol(t2), N, Modulefulness.none) val ps = PlainParamList(p1 :: p2 :: Nil) val bod = st.App(ref, st.Tup(List(st.Ref(p1.sym)(t1, 666, N).resolve, st.Ref(p2.sym)(t2, 666, N).resolve)) (Tree.Tup(Nil // FIXME should not be required (using dummy value) )))( Tree.App(Tree.Empty(), Tree.Empty()), // FIXME should not be required (using dummy value) N, FlowSymbol(sym.nme) ).resolve val (paramLists, bodyBlock) = setupFunctionDef(ps :: Nil, bod, S(sym.nme)) tl.log(s"Ref builtin $sym") assert(paramLists.length === 1) return k(Lambda(paramLists.head, bodyBlock).withLocOf(ref)) if sym.unary then val t1 = new Tree.Ident("arg") val p1 = Param(FldFlags.empty, VarSymbol(t1), N, Modulefulness.none) val ps = PlainParamList(p1 :: Nil) val bod = st.App(ref, st.Tup(List(st.Ref(p1.sym)(t1, 666, N).resolve)) (Tree.Tup(Nil // FIXME should not be required (using dummy value) )))( Tree.App(Tree.Empty(), Tree.Empty()), // FIXME should not be required (using dummy value) N, FlowSymbol(sym.nme) ).resolve val (paramLists, bodyBlock) = setupFunctionDef(ps :: Nil, bod, S(sym.nme)) tl.log(s"Ref builtin $sym") assert(paramLists.length === 1) return k(Lambda(paramLists.head, bodyBlock).withLocOf(ref)) case bs: BlockMemberSymbol => disamb.flatMap(_.defn) match case S(d) if d.hasDeclareModifier.isDefined => val sel = SynthSel(State.globalThisSymbol.ref().resolve, ref.tree)(S(bs), FlowSymbol.synthSel(ref.tree.name), N, N).withLocOf(ref).resolve return disamb.fold(term(sel)(k))(d => term(st.Resolved(sel, d)(N))(k)) // * Note: the alternative below, which might seem more appealing, // * works but does not instrument the selection to check for `undefined`! // return k(Value.Ref(State.globalThisSymbol).sel(ref.tree, bs).withLocOf(ref)) case S(td: TermDefinition) if td.k is syntax.Fun => // * Local functions with no parameter lists are getters // * and are lowered to functions with an empty parameter list // * (non-local functions are compiled into getter methods selected on some prefix) if td.params.isEmpty then return k(Call( Value.Ref(bs, disamb).withLocOf(ref), Nil ne_:: Nil )(isMlsFun = true, true, annots.contains(Annot.TailCall))) case S(_) => () case N => () // TODO panic here; can only lower refs to elab'd symbols case _ => () warnStmt k(loweringCtx(Value.Ref(sym, disamb).withLocOf(ref))) @tailrec final def term(t: st, inStmtPos: Bool = false)(k: Result => Block)(using LoweringCtx): Block = tl.log(s"Lowering.term ${t.showDbg.truncate(100, "[...]")}${ if inStmtPos then " (in stmt)" else ""}${ t.resolvedSym.fold("")(" – symbol " + _)}") def warnStmt = if inStmtPos then warnPureExprInStmtPos(t.toLoc, S(t)) @tailrec def extractAnnots(t: st, acc: List[Annot]): (List[Annot], st) = t match case st.Annotated(annot, trm) => extractAnnots(trm.instantiated, annot :: acc) case _ => (acc, t) val insted = t.instantiated val (annots, trm) = extractAnnots(insted, Nil) reportAnnotations(trm, annots) trm match case st.UnitVal() => k(unit) case st.Lit(lit) => if lit =/= Tree.UnitLit(false) then warnStmt k(Value.Lit(lit)) case st.Ret(res) => returnedTerm(res) case st.Throw(res) => term(res)(Thrw) case st.Asc(lhs, rhs) => term(lhs, inStmtPos = inStmtPos)(k) case st.Tup(fs) => args(fs)(args => k(Tuple(mut = false, args))) case Mut(st.Tup(fs)) => args(fs)(args => k(Tuple(mut = true, args))) case st.CtxTup(fs) => // * This case is currently triggered for code such as `f(using 42)` args(fs)(args => k(Tuple(mut = false, args))) case t @ st.Ref(sym) => ref(t, annots, N, inStmtPos = inStmtPos)(k) case st.Resolved(t @ st.Ref(bsym), sym) => ref(t, annots, S(sym), inStmtPos = inStmtPos)(k) case st.App(ref @ Ref(sym: BuiltinSymbol), arg) => arg match case st.Tup(Nil) => if !sym.nullary then raise: ErrorReport( msg"Expected arguments for builtin operator '${sym.nme}'" -> t.toLoc :: Nil, S(arg), source = Diagnostic.Source.Compilation) k(Value.Ref(sym).withLocOf(ref)) case st.Tup(Fld(FldFlags.benign(), arg, N) :: Nil) => if !sym.unary then raise: ErrorReport( msg"Builtin '${sym.nme}' is not a unary operator" -> t.toLoc :: Nil, S(arg), source = Diagnostic.Source.Compilation) subTerm(arg): ar => val target = wasmIntrinsicPath(sym, unary = true) .getOrElse(Value.Ref(sym).withLocOf(ref)) k(Call(target, (Arg(N, ar) :: Nil) ne_:: Nil)(true, false, false)) case st.Tup(Fld(FldFlags.benign(), arg1, N) :: Fld(FldFlags.benign(), arg2, N) :: Nil) => if !sym.binary then raise: ErrorReport( msg"Builtin '${sym.nme}' is not a binary operator" -> t.toLoc :: Nil, S(arg), source = Diagnostic.Source.Compilation) subTerm(arg1): ar1 => def mkBooleanMatch(isAnd: Bool): Block = val posLit = Tree.BoolLit(isAnd) val negLit = Tree.BoolLit(!isAnd) if k.isInstanceOf[TailOp] then Match( ar1, (Case.Lit(posLit) -> term_nonTail(arg2)(k)) :: Nil, S(k(Value.Lit(negLit))), Unreachable("tail operation in branches"), ) else val ts = loweringCtx.registerTempSymbol(N) Match( ar1, (Case.Lit(posLit) -> term_nonTail(arg2)(Assign(ts, _, End()))) :: Nil, S(Assign(ts, Value.Lit(negLit), End())), k(Value.Ref(ts)), ) sym match case State.andSymbol => mkBooleanMatch(true) case State.orSymbol => mkBooleanMatch(false) case _ => subTerm_nonTail(arg2): ar2 => val target = wasmIntrinsicPath(sym, unary = false) .getOrElse(Value.Ref(sym).withLocOf(ref)) k(Call(target, (Arg(N, ar1) :: Arg(N, ar2) :: Nil) ne_:: Nil)(true, false, false)) case _ => fail: ErrorReport( msg"Unexpected arguments for builtin symbol '${sym.nme}'" -> arg.toLoc :: Nil, S(arg), source = Diagnostic.Source.Compilation) case st.TyApp(f, ts) => term(f)(k) // * Type arguments are erased case st.App(f, arg) => // Collect chains of App nodes: `f(a)(b)(c)` → (f, [a, b, c]) // This allows lowering curried calls as a single `Call` with multiple arg lists. @tailrec def collectAppChain(expr: st, args: Ls[Term]): (st, Ls[Term]) = expr.instantiated match case st.App(inner, innerArg) => collectAppChain(inner, innerArg :: args) case st.TyApp(inner, _) => collectAppChain(inner, args) // type args are erased case expr => (expr, args) val (baseF, allArgs) = collectAppChain(f, arg :: Nil) val isMlsFun = baseF.resolvedSym.fold(baseF.isInstanceOf[st.Lam]): case _: sem.BuiltinSymbol => true case sym: sem.BlockMemberSymbol => sym.trmImplTree.fold(sym.clsTree.isDefined)(_.k is syntax.Fun) case sym: sem.TermSymbol => (sym.k is syntax.Fun) && sym.defn.forall(!_.hasDeclareModifier.isDefined) // Do not perform safety check on `MatchSuccess` and `MatchFailure`. case sym => (sym is State.matchSuccessClsSymbol) || (sym is State.matchFailureClsSymbol) def conclude(fr: Path) = lowerMultiCall(fr, isMlsFun, annots.contains(Annot.TailCall), allArgs, t.toLoc)(k) // * We have to instantiate `f` again because, if `f` is a Sel, the `term` // * function is not called again with f. See below `Sel` and `SelProj` cases. // * Note: now the instantiation is done by `collectAppChain`. val instantiated = baseF val instantiatedResolvedBms = instantiated.resolvedSym.flatMap(_.asBlkMember) instantiated match case t if instantiatedResolvedBms.exists(_ is ctx.builtins.js.bitand) => conclude(Value.Ref(State.runtimeSymbol).selN(Tree.Ident("bitand"))) case t if instantiatedResolvedBms.exists(_ is ctx.builtins.js.bitnot) => conclude(Value.Ref(State.runtimeSymbol).selN(Tree.Ident("bitnot"))) case t if instantiatedResolvedBms.exists(_ is ctx.builtins.js.bitor) => conclude(Value.Ref(State.runtimeSymbol).selN(Tree.Ident("bitor"))) case t if instantiatedResolvedBms.exists(_ is ctx.builtins.js.shl) => conclude(Value.Ref(State.runtimeSymbol).selN(Tree.Ident("shl"))) case t if instantiatedResolvedBms.exists(_ is ctx.builtins.js.try_catch) => conclude(Value.Ref(State.runtimeSymbol).selN(Tree.Ident("try_catch"))) case t if t.resolvedSym.exists { case sym: BlockMemberSymbol => wasmIntrinsicSymbols.contains(sym) case _ => false } => val sym = t.resolvedSym.get.asInstanceOf[BlockMemberSymbol] conclude(Value.Ref(State.wasmSymbol).selN(Tree.Ident(sym.nme))) case t if instantiatedResolvedBms.exists(_ is ctx.builtins.debug.printStack) => if !config.effectHandlers.exists(_.debug) then return fail: ErrorReport( msg"Debugging functions are not enabled" -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation) conclude(Value.Ref(State.runtimeSymbol).selSN("raisePrintStackEffect").withLocOf(baseF)) case t if instantiatedResolvedBms.exists(_ is ctx.builtins.scope.locally) => // scope.locally only applies to the innermost call; extra args are applied on top if allArgs.length > 1 then subTerm(baseF)(conclude) else arg match case Tup(Fld(FldFlags.benign(), body, N) :: Nil) => LoweringCtx.nestScoped.givenIn: val res = block(Nil, R(body))(k) val scopedSyms = loweringCtx.getCollectedSym // Put the Scoped in the rest, so that the returned result can be found correctly Scoped(scopedSyms, res) case _ => return fail: ErrorReport( msg"Unsupported form for scope.locally." -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation) // * Due to whacky JS semantics, we need to make sure that selections leading to a call // * are preserved in the call and not moved to a temporary variable. case sel @ Sel(prefix, nme) => subTerm(prefix): p => conclude(Select(p, nme)(N).withLocOf(sel)) case Resolved(sel @ Sel(prefix, nme), sym) => subTerm(prefix): p => conclude(Select(p, nme)(S(sym)).withLocOf(sel)) case sel @ SelProj(prefix, _, nme) => subTerm(prefix): p => conclude(Select(p, nme)(N).withLocOf(sel)) case Resolved(sel @ SelProj(prefix, _, nme), sym) => subTerm(prefix): p => conclude(Select(p, nme)(S(sym)).withLocOf(sel)) case _ => subTerm(baseF)(conclude) case h @ Handle(lhs, rhs, as, cls, defs, bod) => if !lowerHandlers then return fail: ErrorReport( msg"Effect handlers are not enabled" -> h.toLoc :: Nil, source = Diagnostic.Source.Compilation) val handlers = defs.map { case HandlerTermDefinition(resumeSym, td) => td.body match case None => raise(ErrorReport(msg"Handler function definitions cannot be empty" -> td.toLoc :: Nil)) N case Some(bod) => reportAnnotations(td, td.annotations) val (paramLists, bodyBlock) = setupFunctionDef(td.params, bod, S(td.sym.nme)) S(Handler(td.sym, resumeSym, paramLists, bodyBlock)) }.collect{ case Some(v) => v } loweringCtx.collectScopedSym(lhs) val resSym = loweringCtx.registerTempSymbol(S(t)) subTerm(rhs): par => subTerms(as): asr => HandleBlock(lhs, resSym, par, asr, cls, handlers, inScopedBlock(returnedTerm(bod)), k(Value.Ref(resSym))) case st.Blk(sts, res) => block(sts, R(res), inStmtPos = inStmtPos)(k) case Assgn(lhs, rhs) => lhs match case Ref(sym) => subTerm(rhs): r => Assign(sym, r, k(unit)) case sel @ SynthSel(prefix, nme) => subTerm(prefix): p => subTerm_nonTail(rhs): r => AssignField(p, nme, r, k(unit))(sel.sym) case sel @ Sel(prefix, nme) => subTerm(prefix): p => subTerm_nonTail(rhs): r => AssignField(p, nme, r, k(unit))(sel.sym) case sel @ DynSel(prefix, fld, ai) => subTerm(prefix): p => subTerm_nonTail(fld): f => subTerm_nonTail(rhs): r => AssignDynField(p, f, ai, r, k(unit)) case sel @ SelProj(prefix, _, proj) => subTerm(prefix): p => subTerm_nonTail(rhs): r => AssignField(p, proj, r, k(unit))(sel.sym) case _ => fail: ErrorReport( msg"Unexpected left-hand side in assignment (${lhs.describe})" -> lhs.toLoc :: Nil, S(lhs), source = Diagnostic.Source.Compilation) case st.Lam(params, body) => warnStmt val (paramLists, bodyBlock) = setupFunctionDef(params :: Nil, body, N) if k.isInstanceOf[TailOp] || bodyBlock.size <= 5 then k(Lambda(paramLists.head, bodyBlock)) else val lamSym = new BlockMemberSymbol("lambda", Nil, false) loweringCtx.collectScopedSym(lamSym) val lamDef = FunDefn.withFreshSymbol(N, lamSym, paramLists, bodyBlock)(configOverride = N, annotations = Nil) Define( lamDef, k(lamDef.asPath)) case iftrm: st.IfLike => ucs.Normalization(this)(iftrm)(k) case iftrm: st.SynthIf => ucs.Normalization(this)(iftrm)(k) case sel @ Sel(prefix, nme) => setupSelection(prefix, nme, N)(k) case Resolved(sel @ Sel(prefix, nme), sym) => setupSelection(prefix, nme, S(sym))(k) case sel @ SynthSel(prefix, nme) => // * Not using `setupSelection` as these selections are not meant to be sanity-checked subTerm(prefix): p => k(Select(p, nme)(sel.sym.collect: case s: DefinitionSymbol[?] => s )) case Resolved(sel @ SynthSel(prefix, nme), sym) => // * Not using `setupSelection` as these selections are not meant to be sanity-checked subTerm(prefix): p => k(Select(p, nme)(S(sym))) case DynSel(prefix, fld, ai) => subTerm(prefix): p => subTerm_nonTail(fld): f => k(DynSelect(p, f, ai)) case nw @ (_: New | _: DynNew | Mut(_: New | _: DynNew)) => val (mut, cls, as, rft) = nw match case New(c, a, r) => (false, c, a, r) case Mut(New(c, a, r)) => (true, c, a, r) case DynNew(c, a) => (false, c, a, N) case Mut(DynNew(c, a)) => (true, c, a, N) case _ => spuriousWarning subTerm(cls): sr => rft match case N => lowerMultiInstantiate(mut, sr, as)(k) case S((isym, rft)) => val sym = new BlockMemberSymbol(isym.name, Nil) loweringCtx.collectScopedSym(sym) val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(rft) val pctor = parentConstructor(cls, as) val clsDef = ClsLikeDefn(N, isym, sym, N, syntax.Cls, N, Nil, S(sr), mtds, privateFlds, publicFlds, pctor, ctor, N, N)(N, Nil) val inner = new New(sym.ref().resolved(isym), Nil, N)(N) Define(clsDef, term_nonTail(if mut then Mut(inner) else inner)(k)) case Try(sub, finallyDo) => val l = loweringCtx.registerTempSymbol(S(sub)) TryBlock( subTerm_nonTail(sub)(p => Assign(l, p, End())), subTerm_nonTail(finallyDo)(_ => End()), k(Value.Ref(l)) ) case Quoted(body) => quote(body)(k) // * InvalML-specific cases: t.Cls#field and mutable operations case sp @ SelProj(prefix, _, proj) => setupSelection(prefix, proj, N)(k) case Resolved(sp @ SelProj(prefix, _, proj), sym) => setupSelection(prefix, proj, S(sym))(k) case Region(reg, body) => loweringCtx.collectScopedSym(reg) Assign(reg, Instantiate(mut = true, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Region"))(N), Nil :: Nil), term_nonTail(body)(k)) case RegRef(reg, value) => plainArgs(reg :: value :: Nil): args => k(Instantiate(mut = true, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Ref"))(N), args :: Nil)) case Drop(ref) => subTerm(ref): _ => k(unit) case Deref(ref) => subTerm(ref): r => k(Select(r, Tree.Ident("value"))(N)) case SetRef(lhs, rhs) => subTerm(lhs): ref => subTerm_nonTail(rhs): value => AssignField(ref, Tree.Ident("value"), value, k(value))(N) case Mut(Rcd(mut, stats)) => // TODO: warn when in statement position // * Note: I don't think this is supposed to happen... block(stats, L(mut -> Nil))(k) case Rcd(mut, stats) => // TODO: warn when in statement position block(stats, L(mut -> Nil))(k) case Missing => fail: ErrorReport( msg"Cannot compile ${t.describe} term that was not elaborated (maybe elaboration was one in 'lightweight' mode?)" -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation) case _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted | _: LeadingDotSel => fail: ErrorReport( msg"Unexpected term form in expression position (${t.describe})" -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation) case Error => compError // case _ => // subTerm(t)(k) def setupTerm(name: Str, args: Ls[Path])(k: Result => Block)(using LoweringCtx): Block = k(Instantiate(mut = false, Value.Ref(State.termSymbol).selSN(name), args.map(_.asArg) :: Nil)) def setupQuotedKeyword(kw: Str): Path = Value.Ref(State.termSymbol).selSN("Keyword").selSN(kw) def setupSymbol(symbol: Local)(k: Result => Block)(using LoweringCtx): Block = k(Instantiate(mut = false, Value.Ref(State.termSymbol).selSN("Symbol"), (Value.Lit(Tree.StrLit(symbol.nme)).asArg :: Nil) :: Nil)) def quotePattern(p: FlatPattern)(k: Result => Block)(using LoweringCtx): Block = p match case FlatPattern.Lit(lit) => setupTerm("LitPattern", Value.Lit(lit) :: Nil)(k) case _ => // TODO fail: ErrorReport( msg"Unsupported quasiquote pattern type" -> p.toLoc :: Nil, source = Diagnostic.Source.Compilation ) def quoteSplit(split: Split)(k: Result => Block)(using LoweringCtx): Block = split match case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => quote(scrutinee): r1 => val l1, l2, l3, l4, l5 = loweringCtx.registerTempSymbol(N) blockBuilder.assign(l1, r1) .chain(b => quotePattern(pattern)(r2 => Assign(l2, r2, b))) .chain(b => quoteSplit(continuation)(r3 => Assign(l3, r3, b))) .chain(b => setupTerm("Branch", (l1 :: l2 :: l3 :: Nil).map(s => Value.Ref(s)))(r4 => Assign(l4, r4, b))) .chain(b => quoteSplit(tail)(r5 => Assign(l5, r5, b))) .rest(setupTerm("Cons", (l4 :: l5 :: Nil).map(s => Value.Ref(s)))(k)) case Split.Let(sym, term, tail) => setupSymbol(sym): r1 => loweringCtx.collectScopedSym(sym) val l1, l2, l3 = loweringCtx.registerTempSymbol(N) blockBuilder.assign(l1, r1) .chain(b => setupTerm("Ref", Value.Ref(l1) :: Nil)(r => Assign(sym, r, b))) .chain(b => quote(term)(r2 => Assign(l2, r2, b))) .chain(b => quoteSplit(tail)(r3 => Assign(l3, r3, b))) .rest(setupTerm("Let", (l1 :: l2 :: l3 :: Nil).map(s => Value.Ref(s)))(k)) case Split.Else(default) => quote(default): r => val l = loweringCtx.registerTempSymbol(N) Assign(l, r, setupTerm("Else", Value.Ref(l) :: Nil)(k)) case Split.End => setupTerm("End", Nil)(k) lazy val setupFilename: Path = val state = summon[State] Value.Ref(state.importSymbol).selSN("meta").selSN("url") def quote(t: st)(k: Result => Block)(using LoweringCtx): Block = t match case Lit(lit) => setupTerm("Lit", Value.Lit(lit) :: Nil)(k) case Ref(sym) if Elaborator.binaryOps.contains(sym.nme) => // builtin symbols val l = loweringCtx.registerTempSymbol(N) setupTerm("Builtin", Value.Lit(Tree.StrLit(sym.nme)) :: Nil)(k) case Resolved(Ref(sym), disamb) => k(Value.Ref(sym, S(disamb))) case Ref(sym) => k(Value.Ref(sym, N)) case SynthSel(Ref(sym: ModuleOrObjectSymbol), name) => // Local cross-stage references setupSymbol(sym): r1 => val l1, l2 = loweringCtx.registerTempSymbol(N) Assign(l1, r1, setupTerm("CSRef", Value.Ref(l1) :: setupFilename :: Value.Lit(syntax.Tree.UnitLit(false)) :: Nil)(r2 => Assign(l2, r2, setupTerm("Sel", Value.Ref(l2) :: Value.Lit(syntax.Tree.StrLit(name.name)) :: Nil)(k)) )) case SynthSel(Ref(sym: BlockMemberSymbol), name) => // Multi-file cross-stage references if config.qqEnabled then fail: ErrorReport( msg"Cross-stage reference to ${sym.nme}.${name.name} is only allowed in compiled files due to the lack of `import.meta` in REPL mode." -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation ) else (t.toLoc, sym.toLoc) match case (S(Loc(_, _, Origin(base, _, _))), S(Loc(_, _, Origin(filename, _, _)))) => setupSymbol(sym): r1 => val l1, l2 = loweringCtx.registerTempSymbol(N) val basePath = base.up val targetPath = filename val relPath = targetPath.relativeTo(basePath).map(_.toString).getOrElse(targetPath.toString) Assign(l1, r1, setupTerm("CSRef", Value.Ref(l1) :: setupFilename :: Value.Lit(syntax.Tree.StrLit(relPath)) :: Nil)(r2 => Assign(l2, r2, setupTerm("Sel", Value.Ref(l2) :: Value.Lit(syntax.Tree.StrLit(name.name)) :: Nil)(k)) )) case _ => fail: ErrorReport( msg"Cannot refer to imported module ${sym.nme} due to the lack of path." -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation ) case Lam(params, body) => def rec(ps: Ls[LocalSymbol & NamedSymbol], ds: Ls[Path])(k: Result => Block)(using LoweringCtx): Block = ps match case Nil => quote(body): r => val l = loweringCtx.registerTempSymbol(N) val arr = loweringCtx.registerTempSymbol(N, "arr") Assign( arr, Tuple(mut = false, ds.reverse.map(_.asArg)), Assign(l, r, setupTerm("Lam", Value.Ref(arr) :: Value.Ref(l) :: Nil)(k))) case sym :: rest => loweringCtx.collectScopedSym(sym) setupSymbol(sym): r => val l = loweringCtx.registerTempSymbol(N) Assign(l, r, setupTerm("Ref", Value.Ref(l) :: Nil): r1 => Assign(sym, r1, rec(rest, Value.Ref(l) :: ds)(k))) rec(params.params.map(_.sym), Nil)(k) // TODO: restParam? case App(lhs, Tup(rhs)) => quote(lhs): r1 => def rec(es: Ls[Elem], xs: Ls[Path])(k: Result => Block): Block = es match case Nil => val arrSym = loweringCtx.registerTempSymbol(N, "arr") Assign( arrSym, Tuple(mut = false, xs.reverse.map(_.asArg)), setupTerm("Tup", Value.Ref(arrSym) :: Nil): r2 => val l1 = loweringCtx.registerTempSymbol(N) val l2 = loweringCtx.registerTempSymbol(N) Assign(l1, r1, Assign(l2, r2, setupTerm("App", Value.Ref(l1) :: Value.Ref(l2) :: Nil)(k))) ) case Fld(_, t, _) :: rest => quote(t): r2 => val l = loweringCtx.registerTempSymbol(N) Assign(l, r2, rec(rest, Value.Ref(l) :: xs)(k)) case Spd(eager, term) :: rest => fail: ErrorReport( msg"Unsupported spread in quasiquote application" -> term.toLoc :: Nil, source = Diagnostic.Source.Compilation ) rec(rhs, Nil)(k) case Blk(LetDecl(sym, _) :: DefineVar(sym2, rhs) :: Nil, res) => // Let bindings require(sym2 is sym) loweringCtx.collectScopedSyms(sym) setupSymbol(sym){r1 => val l1, l2, l3, l4, l5 = loweringCtx.registerTempSymbol(N) val arrSym = loweringCtx.registerTempSymbol(N, "arr") blockBuilder.assign(l1, r1) .chain(b => setupTerm("Ref", Value.Ref(l1) :: Nil)(r => Assign(sym, r, b))) .chain(b => quote(rhs)(r2 => Assign(l2, r2, b))) .chain(b => quote(res)(r3 => Assign(l3, r3, b))) .chain(b => setupTerm("LetDecl", Value.Ref(l1) :: Nil)(r4 => Assign(l4, r4, b))) .chain(b => setupTerm("DefineVar", Value.Ref(l1) :: Value.Ref(l2) :: Nil)(r5 => Assign(l5, r5, b))) .assign(arrSym, Tuple(mut = false, (l4 :: l5 :: Nil).map(s => Value.Ref(s).asArg))) .rest(setupTerm("Blk", Value.Ref(arrSym) :: Value.Ref(l3) :: Nil)(k)) } case IfLike(_, IfLikeForm.ReturningIf, split) => quoteSplit(split.getExpandedSplit): r => val l = loweringCtx.registerTempSymbol(N) Assign(l, r, setupTerm("IfLike", setupQuotedKeyword("If") :: Value.Ref(l) :: Nil)(k)) case Unquoted(body) => term(body)(k) case _ => fail: ErrorReport( msg"Unsupported quasiquote type ${t.describe}" -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation ) def gatherMembers(clsBody: ObjBody)(using LoweringCtx) : (Ls[FunDefn], Ls[BlockMemberSymbol -> TermSymbol], Ls[TermSymbol], Block) = val mtds = clsBody.methods .flatMap: td => td.body.map: bod => val (paramLists, bodyBlock) = setupFunctionDef(td.params, bod, S(td.sym.nme)) reportAnnotations(td, td.extraAnnotations) val cfgOverride = td.extraAnnotations.collectFirst: case Annot.Config(modify) => modify(config) FunDefn(td.owner, td.sym, td.tsym, paramLists, bodyBlock)(cfgOverride, td.annotations) val publicFlds = clsBody.publicFlds.map(f => f.sym -> f.tsym) val privateFlds = clsBody.nonMethods.collect: case decl @ LetDecl(sym: TermSymbol, annotations) => reportAnnotations(decl, annotations) sym val ctor = inScopedBlock: term_nonTail(Blk(clsBody.nonMethods, clsBody.blk.res), inStmtPos = true)(Assign.discard(_, End())) (mtds, publicFlds, privateFlds, ctor) def args(elems: Ls[Elem])(k: Ls[Arg] => Block)(using LoweringCtx): Block = val as = elems.map: case sem.Fld(sem.FldFlags.benign(), value, N) => R(N -> value) case sem.Fld(sem.FldFlags.benign(), idx, S(rhs)) => L(idx -> rhs) case arg @ sem.Fld(flags, value, asc) => TODO(s"Other argument forms: $arg") case spd: Spd => R(S(spd.k) -> spd.term) // * The straightforward way to lower arguments creates too much recursion depth // * and makes Lowering stack overflow when lowering functions with lots of arguments. /* def rec(as: Ls[Bool -> st], asr: Ls[Arg]): Block = as match case Nil => k(Call(fr, asr.reverse)(isMlsFun, true)) case (spd, a) :: as => subTerm_nonTail(a): ar => rec(as, Arg(spd, ar) :: asr) rec(as, Nil) */ var asr: Ls[Arg] = Nil var fsr: Ls[RcdArg] = Nil def rec(as: Ls[(Term -> Term) \/ (Opt[SpreadKind] -> st)]): Block = as match case Nil => End() case R((spd, a)) :: as => subTerm_nonTail(a): ensureOnce: (ar: Path) => asr ::= Arg(spd, ar) rec(as) case L((idx, t)) :: as => subTerm_nonTail(idx): ir => subTerm_nonTail(t): ensureOnce: (tr: Path) => fsr ::= RcdArg(S(ir), tr) rec(as) val b = rec(as) if fsr.isEmpty then Begin(b, k(asr.reverse)) else val rcdSym = loweringCtx.registerTempSymbol(N, "rcd") Begin( b, Assign( rcdSym, Record(mut = false, fsr.reverse), k((Arg(N, Value.Ref(rcdSym)) :: asr).reverse))) inline def plainArgs(ts: Ls[st])(k: Ls[Arg] => Block)(using LoweringCtx): Block = subTerms(ts)(asr => k(asr.map(Arg(N, _)))) inline def subTerms(ts: Ls[st])(k: Ls[Path] => Block)(using LoweringCtx): Block = // @tailrec // TODO def rec(as: Ls[st], asr: Ls[Path]): Block = as match case Nil => k(asr.reverse) case a :: as => subTerm_nonTail(a): ar => rec(as, ar :: asr) rec(ts, Nil) def subTerm_nonTail(t: st, inStmtPos: Bool = false)(k: Path => Block)(using LoweringCtx): Block = subTerm(t, inStmtPos = inStmtPos)(k) inline def subTerm(t: st, inStmtPos: Bool = false)(k: Path => Block)(using LoweringCtx): Block = term(t, inStmtPos = inStmtPos): case v: Value => k(v) case p: Path => k(p) case Lambda(params, body) => val lamSym = BlockMemberSymbol("lambda", Nil, false) loweringCtx.collectScopedSym(lamSym) val lamDef = FunDefn.withFreshSymbol(N, lamSym, params :: Nil, body)(configOverride = N, annotations = Nil) Define(lamDef, k(lamDef.asPath)) case r => val l = loweringCtx.registerTempSymbol(N) Assign(l, r, k(l |> Value.Ref.apply)) def program(main: st.Blk): Program = val (imps, funs, rest) = splitBlock(main.stats, Nil, Nil, Nil) val blk = inScopedBlock(using LoweringCtx.empty): block(funs ::: rest, R(main.res))(ImplctRet) val desug = LambdaRewriter.desugar(blk) val deforested = val outterTl = tl config.deforest match case None => desug case Some(dCfg) => /* // * For some weird reason (Scala bug?), // * the version below leads to a stack overflows during its initialization given TraceLogger with override def doTrace: Bool = dCfg.debug override def emitDbg(str: Str): Unit = outterTl.emitDbg(s"deforest > $str") */ (new TraceLogger: override def doTrace: Bool = dCfg.debug override def emitDbg(str: Str): Unit = outterTl.emitDbg(s"deforest > $str") ).givenIn: deforest.Deforest(Program(imps.map(imp => imp.sym -> imp.str), desug)).main val handlerPaths = new HandlerPaths val shouldFlattenScopes = config.effectHandlers.isDefined val scopeFlattened = if shouldFlattenScopes then ScopeFlattener().applyBlock(deforested) else deforested val lifted = if lift then Lifter(scopeFlattened).transform else scopeFlattened val (withHandlers2, stackSafetyInfo) = config.effectHandlers.fold((lifted, Map.empty)): opt => HandlerLowering(handlerPaths, opt).translateTopLevel(lifted) val stackSafe = config.stackSafety match case N => withHandlers2 case S(sts) => StackSafeTransform(sts.stackLimit, handlerPaths, stackSafetyInfo).transformTopLevel(withHandlers2) val flattened = stackSafe.flattened val bufferable = BufferableTransform().transform(flattened) // * TODO[Anto]: Can we remove MergeMatchArmTransformer? Seems no longer necessary val merged = MergeMatchArmTransformer.applyBlock(bufferable) val funcToCls = if config.funcToCls then Lifter(FirstClassFunctionTransformer().transform(merged)).transform else merged val staged = ReflectionInstrumenter(using summon).apply(funcToCls) val res = if config.tailRecOpt then TailRecOpt().transform(staged) else staged Program( imps.map(imp => imp.sym -> imp.str), res ) def setupSelection(prefix: Term, nme: Tree.Ident, disamb: Opt[DefinitionSymbol[?]])(k: Result => Block)(using LoweringCtx): Block = subTerm(prefix): p => k(Select(p, nme)(disamb)) final def setupFunctionOrByNameDef(paramLists: List[ParamList], bodyTerm: Term, name: Option[Str]) (using LoweringCtx): (List[ParamList], Block) = val physicalParams = paramLists match case Nil => ParamList(ParamListFlags.empty, Nil, N) :: Nil case ps => ps setupFunctionDef(physicalParams, bodyTerm, name) def inScopedBlock(using LoweringCtx)(mkBlock: LoweringCtx ?=> Block): Block = LoweringCtx.nestScoped.givenIn: val body = mkBlock val scopedSyms = loweringCtx.getCollectedSym Scoped(scopedSyms, body) def setupFunctionDef(paramLists: List[ParamList], bodyTerm: Term, name: Option[Str]) (using LoweringCtx): (List[ParamList], Block) = val scopedBody = inScopedBlock(returnedTerm(bodyTerm)) (paramLists, scopedBody) // TODO: reduce duplication between the two `reportAnnotations` def reportAnnotations(target: Statement, annotations: Ls[Annot]): Unit = def warn(annot: Annot, msg: Opt[Message] = N) = raise: msg match case S(value) => WarningReport(value -> annot.toLoc :: Nil) case N => WarningReport(msg"This annotation has no effect." -> annot.toLoc :: Nil) annotations.foreach: case Annot.Untyped => () case a @ Annot.TailRec => target match case TermDefinition(body = S(bod), k = syntax.Fun) => () case TermDefinition(k = syntax.Fun) => warn(a, S(msg"Only functions with a body may be marked as @tailrec.")) case _ => warn(a) case Annot.Modifier(syntax.Keyword.`public` | syntax.Keyword.`private`) => () case Annot.Modifier(syntax.Keyword("staged")) => () case _: Annot.Config => () // Config annotations are handled during FunDefn creation case annot => warn(annot) def reportAnnotations(receiver: Term, annotations: Ls[Annot]): Unit = def warn(annot: Annot, msg: Opt[Message] = N) = val message = msg match case N => msg"This annotation is not supported on ${receiver.describe} terms." case S(value) => value raise: WarningReport( msg"This annotation has no effect." -> annot.toLoc :: message -> receiver.toLoc :: Nil) annotations.foreach: case Annot.Untyped => () case a @ Annot.TailCall => receiver match case st.App(Ref(_: BuiltinSymbol), _) => warn(a, S(msg"The @tailcall annotation has no effect on calls to built-in symbols.")) case st.App(_, _) => () case st.Resolved(_, defnSym) => defnSym.defn match case S(td: TermDefinition) if (td.k is syntax.Fun) && td.params.isEmpty => () case _ => warn(a) case _ => warn(a) case annot => warn(annot) trait LoweringSelSanityChecks(using Config, TL, Raise, State) extends Lowering: private val instrument: Bool = config.sanityChecks.isDefined override def setupSelection(prefix: st, nme: Tree.Ident, disamb: Opt[DefinitionSymbol[?]])(k: Result => Block)(using LoweringCtx): Block = if !instrument // || disamb.exists(_.defn.exists(_.hasDeclareModifier.isEmpty)) // * This checks `declare` members, which is normally unwanted || disamb.isDefined // * ^ We assume that resolved selections are well-behaved (will not yield undefined or debind a method) then super.setupSelection(prefix, nme, disamb)(k) else subTerm(prefix): p => val selRes = loweringCtx.registerTempSymbol(N, "selRes") // * We are careful to access `x.f` before `x.f$__checkNotMethod` in case `x` is, eg, `undefined` and // * the access should throw an error like `TypeError: Cannot read property 'f' of undefined`. blockBuilder .assign(selRes, Select(p, nme)(disamb)) .assign(State.noSymbol, Select(p, Tree.Ident(nme.name+"$__checkNotMethod"))(N)) .ifthen(selRes.asPath, Case.Lit(syntax.Tree.UnitLit(false)), Throw(Instantiate(mut = false, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(N), (Value.Lit(syntax.Tree.StrLit(s"Access to required field '${nme.name}' yielded 'undefined'")).asArg :: Nil) :: Nil)) ) .rest(k(selRes.asPath)) trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) extends Lowering: private def selFromGlobalThis(path: Str*): Path = path.foldLeft[Path](Value.Ref(State.globalThisSymbol)): (qual, name) => Select(qual, Tree.Ident(name))(N) private def assignStmts(stmts: (Local, Result)*)(rest: Block) = stmts.foldRight(rest): case ((sym, res), acc) => Assign(sym, res, acc) private def pureCall(fn: Path, args: Ls[Arg]): Call = Call(fn, args ne_:: Nil)(true, false, false) extension (k: Block => Block) def |>: (b: Block): Block = k(b) private val traceLogFn = Value.Ref(State.runtimeSymbol).selSN("TraceLogger").selSN("log") private val traceLogIndentFn = Value.Ref(State.runtimeSymbol).selSN("TraceLogger").selSN("indent") private val traceLogResetFn = Value.Ref(State.runtimeSymbol).selSN("TraceLogger").selSN("resetIndent") private val strConcatFn = selFromGlobalThis("String", "prototype", "concat", "call") private val inspectFn = selFromGlobalThis("util", "inspect") override def setupFunctionDef(paramLists: List[ParamList], bodyTerm: st, name: Option[Str]) (using LoweringCtx): (List[ParamList], Block) = if instrument then val (ps, bod) = handleMultipleParamLists(paramLists, bodyTerm) val instrumentedBody = setupFunctionBody(ps, bod, name) (ps :: Nil, instrumentedBody) else super.setupFunctionDef(paramLists, bodyTerm, name) def handleMultipleParamLists(paramLists: List[ParamList], bod: Term) = def go(paramLists: List[ParamList], bod: Term): (ParamList, Term) = paramLists match case Nil => ??? case h :: Nil => (h, bod) case h :: t => go(t, Term.Lam(h, bod)) go(paramLists.reverse, bod) def setupFunctionBody(params: ParamList, bod: Term, name: Option[Str])(using LoweringCtx): Block = inScopedBlock: val enterMsgSym = loweringCtx.registerTempSymbol(N, dbgNme = "traceLogEnterMsg") val prevIndentLvlSym = loweringCtx.registerTempSymbol(N, dbgNme = "traceLogPrevIndent") val resSym = loweringCtx.registerTempSymbol(N, dbgNme = "traceLogRes") val retMsgSym = loweringCtx.registerTempSymbol(N, dbgNme = "traceLogRetMsg") val psInspectedSyms = params.params.map(p => loweringCtx.registerTempSymbol(N, dbgNme = s"traceLogParam_${p.sym.nme}") -> p.sym) val resInspectedSym = loweringCtx.registerTempSymbol(N, dbgNme = "traceLogResInspected") val psSymArgs = psInspectedSyms.zipWithIndex.foldRight[Ls[Arg]](Arg(N, Value.Lit(Tree.StrLit(")"))) :: Nil): case (((s, p), i), acc) => if i == psInspectedSyms.length - 1 then Arg(N, Value.Ref(s)) :: acc else Arg(N, Value.Ref(s)) :: Arg(N, Value.Lit(Tree.StrLit(", "))) :: acc val tmp1, tmp2, tmp3 = loweringCtx.registerTempSymbol(N) assignStmts(psInspectedSyms.map: (pInspectedSym, pSym) => pInspectedSym -> pureCall(inspectFn, Arg(N, Value.Ref(pSym)) :: Nil) *) |>: assignStmts( enterMsgSym -> pureCall( strConcatFn, Arg(N, Value.Lit(Tree.StrLit(s"CALL ${name.getOrElse("[arrow function]")}("))) :: psSymArgs ), tmp1 -> pureCall(traceLogFn, Arg(N, Value.Ref(enterMsgSym)) :: Nil), prevIndentLvlSym -> pureCall(traceLogIndentFn, Nil) ) |>: term(bod)(r => assignStmts( resSym -> r, resInspectedSym -> pureCall(inspectFn, Arg(N, Value.Ref(resSym)) :: Nil), retMsgSym -> pureCall( strConcatFn, Arg(N, Value.Lit(Tree.StrLit("=> "))) :: Arg(N, Value.Ref(resInspectedSym)) :: Nil ), tmp2 -> pureCall(traceLogResetFn, Arg(N, Value.Ref(prevIndentLvlSym)) :: Nil), tmp3 -> pureCall(traceLogFn, Arg(N, Value.Ref(retMsgSym)) :: Nil) ) |>: Ret(Value.Ref(resSym)) ) object TrivialStatementsAndMatch: def unapply(b: Block): Opt[(Opt[Block => Block], Match)] = def handleAssignAndMatch( assign: Block => Block, m: Match, k: Opt[Block => Block] ): Some[(Some[Block => Block], Match)] = def newK(r: Block): Block = val newR = k.getOrElse(identity: Block => Block)(r) assign(newR) S(S(newK), m) b match case m: Match => S(N, m) case Assign(lhs, rhs: Path, TrivialStatementsAndMatch(k, m)) => handleAssignAndMatch(r => Assign(lhs, rhs, r), m, k) case a@AssignField(lhs, nme, rhs: Path, TrivialStatementsAndMatch(k, m)) => handleAssignAndMatch(r => AssignField(lhs, nme, rhs, r)(a.symbol), m, k) case AssignDynField(lhs, fld, arrayIdx, rhs: Path, TrivialStatementsAndMatch(k, m)) => handleAssignAndMatch(r => AssignDynField(lhs, fld, arrayIdx, rhs, r), m, k) case Define(defn, TrivialStatementsAndMatch(k, m)) => handleAssignAndMatch(r => Define(defn, r), m, k) case _ => N object MergeMatchArmTransformer extends BlockTransformer(SymbolSubst.Id): override def applyBlock(b: Block): Block = super.applyBlock(b) match case m @ Match(scrut, arms, Some(dflt), rest) => dflt match case TrivialStatementsAndMatch(k, Match(scrutRewritten, armsRewritten, dfltRewritten, restRewritten)) if (scrutRewritten === scrut) && (restRewritten.size * armsRewritten.length) < 10 => val newArms = restRewritten match case _: End => armsRewritten case _ => armsRewritten.map: case (cse, body) => cse -> Begin(body, restRewritten) k.getOrElse(identity[Block]): Match(scrut, arms ::: newArms, dfltRewritten.fold(restRewritten)(Begin(_, restRewritten)) |> some, rest) case _ => m case b => b ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala ================================================ package hkmc2.codegen import scala.collection.mutable.{Map => MutMap} import mlscript.utils._, shorthands._ import hkmc2._ import hkmc2.Message.MessageContext import hkmc2.document._ import hkmc2.semantics._ import hkmc2.syntax._ import hkmc2.semantics.Elaborator.State import hkmc2.utils.Scope import hkmc2.utils.Scope.scope import hkmc2.document.Document.{braced, bracedbk} /** `SymbolPrinter` is used for printing symbols that are not locally bound, so that they are consistent * with the debug-printed names shown in other parts of the compiler, such as showAsTreee. */ class Printer(using Raise, ShowCfg, SymbolPrinter, Config): val showPurity = false // true def print(l: Local)(using Scope): Document = // * Symbols that are not local symbols in scope should be printed using their dbgName // * – these will appear like `x¹²` and will be globally unique. scope.lookup(l) match case S(str) => str case N => summon[SymbolPrinter].printSymbol(l) def print(blk: Block)(using Scope): Document = blk match case Match(scrut, arms, dflt, rest) => def case_doc(c: Case) = c match case Case.Lit(lit) => doc"${lit.idStr}" case Case.Cls(cls, path) => doc"${print(cls)}" case Case.Tup(len, inf) => doc"Array($len${if inf then "+" else ""})" case Case.Field(name, safe) => doc"${if safe then "" else "Object "}{ ${name.name} }" val docCases = arms .map{ case (c, b) => doc"${case_doc(c)} => #{ # ${print(b)} #} " } .mkDocument(sep = doc" # ") val docDefault = dflt.fold(doc"")(e => doc" # else #{ # ${print(e)} #} ") doc"match ${print(scrut)} #{ # ${docCases}$docDefault #} # ${print(rest)}" case Return(res, implct) => if implct then print(res) else doc"return ${print(res)}" case Throw(exc) => doc"throw ${print(exc)}" case Label(label, loop, body, rest) => val l2 = scope.allocateOrGetName(label) // * ^ if we print the same block with a top-level label more than once, using `scope.allocateName` will crash... doc"${if loop then "loop" else "block"} $l2: #{ # ${print(body)} #} # ${print(rest)}" case Break(label) => doc"break ${print(label)}" case Continue(label) => doc"continue ${print(label)}" case Begin(sub, rest) => doc"begin #{ # ${print(sub)}; #} # ${print(rest)}" case TryBlock(sub, finallyDo, rest) => doc"try #{ # ${print(sub)} # #} finally # #{ ${print(finallyDo)}; # #} ${print(rest)}" case Assign(_: NoSymbol, rhs, rest) => doc"do ${print(rhs)}; # ${print(rest)}" case Assign(lhs, rhs, rest) => doc"set ${print(lhs)} = ${print(rhs)}; # ${print(rest)}" case AssignField(lhs, nme, rhs, rest) => doc"set ${print(lhs)}.${nme.name} = ${print(rhs)}; # ${print(rest)}" case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => doc"set ${print(lhs)}${if arrayIdx then "." else "!"}${print(fld)} = ${print(rhs)}; # ${print(rest)}" case Define(defn, rest) => doc"define ${print(defn.sym)} as ${print(defn)}; # ${print(rest)}" case Scoped(syms, body) => scope.nest.givenIn: import hkmc2.given_Ordering_Uid // Not sure why needed... val names = syms.toList.sortBy(_.uid).map(s => scope.allocateName(s)) doc"let ${names.mkDocument(", ")}; # ${print(body)}" case End(msg) if msg.nonEmpty && config.commentGeneratedCode => doc"end /* ${msg} */" case End(_) => doc"end" case Unreachable(msg) => doc"unreachable /* ${msg} */" case _ => TODO(blk) def print( privateFields: Ls[TermSymbol], publicFields: Ls[(BlockMemberSymbol, TermSymbol)], methods: Ls[FunDefn], auxParams: Ls[ParamList], preCtor: Opt[Block], ctor: Block, ctorSym: Opt[TermSymbol], )(using Scope): Document = val privFields = privateFields.map(x => doc"private val ${print(x)};").mkDocument(sep = doc" # ") val pubFields = publicFields.map(x => doc"val ${print(x._1)};").mkDocument(sep = doc" # ") val docPrivFlds = if privateFields.isEmpty then doc"" else doc" # ${privFields}" val docPubFlds = if publicFields.isEmpty then doc"" else doc" # ${pubFields}" val docPreCtor = preCtor match case Some(End(_)) => doc"" case Some(value) => print(value) :: doc"; # " case None => doc"" val docCtor = ctor match case End(_) => doc"" case _ => doc" # constructor${ctorSym.fold(doc"")(doc" " :: print(_))}${printParamLists(auxParams)} ${ bracedbk(docPreCtor :: print(ctor))}" val mtds = methods.map(m => doc"method ${print(m.sym)} = " :: print(m)).mkDocument(sep = doc" # ") val docMethods = if methods.isEmpty then doc"" else doc" # ${mtds}" if publicFields.isEmpty && privateFields.isEmpty && methods.isEmpty && preCtor.forall(_.isEmpty) && ctor.isEmpty then doc"" else doc" " :: braced(doc"${docPrivFlds}${docPubFlds}${docCtor}${docMethods}") def printParamLists(paramss: Ls[ParamList])(using Scope): Document = paramss .map: pl => val allParams = pl.params.map(x => scope.allocateName(x.sym)) ++ pl.restParam.map(x => "..." + scope.allocateName(x.sym)) allParams.mkDocument("(", ", ", ")") .mkDocument("") def print(defn: Defn)(using Scope): Document = defn match case fun @ FunDefn(own, sym, dSym, paramss, body) => scope.nest.givenIn: val docParams = printParamLists(paramss) val docBody = print(body) val docStaged = if fun.isStaged then doc"staged " else doc"" doc"${docStaged}fun ${print(dSym)}${docParams} ${bracedbk(docBody)}" case ValDefn(tsym, sym, rhs) => doc"val ${print(tsym)} = ${print(rhs)}" case cls @ ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) => scope.nest.givenIn: val ctorParams = printParamLists(paramsOpt.toList) val docStaged = if cls.isStaged then doc"staged " else doc"" val docBody = print(privateFields, publicFields, methods, auxParams, S(preCtor), ctor, ctorSym) val clsType = k.str val docCls = doc"${docStaged}${clsType} ${print(isym)}${ctorParams}${docBody}" val docModule = mod match case Some(mod) => val docStaged = if mod.isStaged then doc"staged " else doc"" val docBody = print(mod.privateFields, mod.publicFields, mod.methods, Nil, N, mod.ctor, N) doc" # ${docStaged}module ${print(mod.isym)}${docBody}" case None => doc"" doc"${docCls}${docModule}" private def showSymbol(name: Str, sym: Opt[DefinitionSymbol[?]]): Document = sym.fold(doc"${name}﹖")(sym => if summon[ShowCfg].debug then doc"‹${sym.toString}›" else summon[SymbolPrinter].printSymbol(sym)) def print(arg: Arg)(using Scope): Document = val doc = print(arg.value) if arg.spread.nonEmpty then doc"...${doc}" else doc def print(value: Value)(using Scope): Document = value match case Value.Ref(l: InnerSymbol, N) => doc"${print(l)}.this" case Value.Ref(l, N) => print(l) case Value.Ref(l, disamb) => showSymbol(l.nme, disamb) case Value.This(sym) => doc"this" case Value.Lit(lit) => doc"${lit.idStr}" def print(path: Path)(using Scope): Document = path match case sel @ Select(qual, name) => val docQual = print(qual) doc"${docQual}.${showSymbol(name.name, sel.symbol)}" case DynSelect(qual, fld, arrayIdx) => doc"${print(qual)}${if arrayIdx then "." else "!"}${print(fld)}" case x: Value => print(x) // case _ => TODO(path) def print(result: Result)(using Scope): Document = (if !showPurity || result.isPure then "" else "!") :: result.match case Call(fun, argss) => val chainedArgs = argss.map(args => doc"(${args.map(print).mkDocument(", ")})").mkDocument("") doc"${print(fun)}${chainedArgs}" case Instantiate(mut, cls, argss) => val chainedArgs = argss.map(args => doc"(${args.map(print).mkDocument(", ")})").mkDocument("") doc"new ${if mut then "mut " else ""}${print(cls)}${chainedArgs}" case Lambda(params, body) => scope.nest.givenIn: val allParams = params.params.map(x => scope.allocateName(x.sym)) ++ params.restParam.map(x => "..." + scope.allocateName(x.sym)) val docParams = allParams.mkDocument("(", ", ", ")") doc"$docParams => ${bracedbk(print(body))}" case Tuple(mut, elems) => val docElems = elems.map(x => print(x)).mkDocument(", ") doc"${if mut then "mut " else ""}[${docElems}]" case Record(mut, args) => doc"${if mut then "mut " else ""}{ ${ args.map(x => x.idx.fold(doc"...")(p => print(p) :: ": ") :: print(x.value)).mkDocument(", ") } }" case x: Path => print(x) def print(imports: Ls[Local -> Str])(using Scope): Document = imports.map: (local, path) => val docLocal = scope.allocateName(local) doc"import ${docLocal}; # " .mkDocument() def print(prog: Program)(using Scope): Document = doc"${print(prog.imports)}${print(prog.main)}" def worksheet(prog: Program)(using Scope): Document = doc"${print(prog.imports)}${ prog.main match case Scoped(syms, body) => // * The top-level Scoped block in a worksheet contains symbols that are actually // * still visible in the following blocks; // * therefore, we want to avoid printing them with fresh names but use their `dbgName`s instead. scope.nest.givenIn: import hkmc2.given_Ordering_Uid // Not sure why needed... val names = syms.toList.sortBy(_.uid).map: case s: TempSymbol => scope.allocateName(s) case s => summon[SymbolPrinter].printSymbol(s) doc"let ${names.mkString(", ")}; # ${print(body)}" case m => print(m) }" ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala ================================================ package hkmc2 package codegen import utils.* import hkmc2.Message.MessageContext import scala.collection.mutable.HashMap import scala.util.chaining.* import mlscript.utils.*, shorthands.* import semantics.* import semantics.Elaborator.{State, Ctx, ctx} import syntax.{Keyword, Literal, Tree} // it should be possible to cache some common constructions (End, Option) into the context // this avoids having to rebuild the same shapes everytime they are needed // transform Block to Block IR so that it can be instrumented in mlscript class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(new SymbolSubst()): // recover `defn` for when `sym.defn` is `None`, when the definition was generated by other compiler passes val defnMap = HashMap[Symbol, ClsLikeDefn]() type ArgWrappable = Path | Symbol type Context = HashMap[Path, Path] // TODO: there could be a fresh scope per function body, instead of a single one for the entire program val scope = Scope.empty(Scope.Cfg.default) def asArg(x: ArgWrappable): Arg = x match case p: Path => p.asArg case l: Symbol => l.asPath.asArg // null and undefined are missing def toValue(lit: Str | Int | BigDecimal | Bool): Value = val l = lit match case i: Int => Tree.IntLit(i) case b: Bool => Tree.BoolLit(b) case s: Str => Tree.StrLit(s) case n: BigDecimal => Tree.DecLit(n) Value.Lit(l) extension [A, B](ls: Ls[(A => B) => B]) def collectApply(f: Ls[A] => B): B = // defer applying k while prepending new elements to the list ls.foldRight((_: Ls[A] => B)(Nil))((headCont, tailCont) => k => headCont: head => tailCont: tail => k(head :: tail) )(f) // helpers for constructing Block def assign(res: Result, symName: Str = "tmp")(k: Path => Block): Block = // TODO: skip assignment if res: Path? val sym = new TempSymbol(N, symName) Scoped(Set(sym), Assign(sym, res, k(sym.asPath))) def tuple(elems: Ls[ArgWrappable], symName: Str = "tmp")(k: Path => Block): Block = assign(Tuple(false, elems.map(asArg)), symName)(k) // isMlsFun is probably always true? def call(fun: Path, args: Ls[ArgWrappable], isMlsFun: Bool = true, symName: Str = "tmp")(k: Path => Block): Block = assign(Call(fun, args.map(asArg) ne_:: Nil)(isMlsFun, false, false), symName)(k) // helpers for instrumenting Block def blockMod(name: Str) = summon[State].blockSymbol.asPath.selSN(name) def optionMod(name: Str) = summon[State].optionSymbol.asPath.selSN(name) def blockCtor(name: Str, args: Ls[ArgWrappable], symName: Str = "tmp")(k: Path => Block): Block = call(blockMod(name), args, true, symName)(k) def optionSome(arg: ArgWrappable, symName: Str = "tmp")(k: Path => Block): Block = call(optionMod("Some"), Ls(arg), true, symName)(k) def optionNone(symName: Str = "tmp")(k: Path => Block): Block = assign(optionMod("None"), symName)(k) def blockCall(name: Str, args: Ls[ArgWrappable], symName: Str = "tmp")(k: Path => Block): Block = call(blockMod(name), args, symName = symName)(k) // linking functions defined in MLscipt def fnPrintCode(p: Path)(k: Block): Block = // discard result, we only care about side effect blockCall("printCode", Ls(p))(_ => k) def fnConcat(p1: Path, p2: Path, symName: String = "concat")(k: Path => Block): Block = blockCall("concat", Ls(p1, p2), symName)(k) // transformation helpers // if sym is ClassSymbol, we may need pOpt to link to the path pointing to the value of the symbol def transformSymbol(sym: Symbol, pOpt: Option[Path] = N, symName: Str = "sym")(k: Path => Block): Block = sym match case t: TermSymbol if t.defn.exists(_.sym.asClsOrMod.isDefined) => transformSymbol(t.defn.get.sym.asClsOrMod.get, pOpt, symName)(k) // retain names to built-in functions or function definitions case t: TermSymbol if t.defn.exists(_.k == syntax.Fun) => blockCtor("Symbol", Ls(toValue(sym.nme)), symName)(k) case clsSym: ClassSymbol if ctx.builtins.virtualClasses(clsSym) => blockCtor("VirtualClassSymbol", Ls(toValue(sym.nme)), symName)(k) case baseSym: BaseTypeSymbol => val name = scope.allocateOrGetName(sym) val (owner, bsym, paramsOpt, auxParams) = (baseSym.defn, defnMap.get(baseSym)) match case (S(defn), _) => (defn.owner, defn.bsym, defn.paramsOpt, defn.auxParams) case (_, S(defn: ClsLikeDefn)) => (defn.owner, defn.sym, defn.paramsOpt, defn.auxParams) // FIXME: hack to patch in staging for returning the object Unit. case _ if baseSym == State.unitSymbol => (N, baseSym, N, Nil) case _ => raise(ErrorReport(msg"Unable to infer parameters from symbol in staged module, which are necessary to reconstruct class instances: ${sym.toString()}" -> sym.toLoc :: Nil)) return End() val path: ArgWrappable = pOpt.getOrElse(owner match case S(owner) => owner.asPath.selSN(sym.nme) case N => bsym) baseSym match case _: ClassSymbol => transformParamsOpt(paramsOpt): paramsOpt => auxParams.map(ps => transformParamList(ps)).collectApply: auxParams => tuple(auxParams): auxParams => blockCtor("ConcreteClassSymbol", Ls(toValue(name), path, paramsOpt, auxParams), symName)(k) case _: ModuleOrObjectSymbol => blockCtor("ModuleSymbol", Ls(toValue(name), path), symName)(k) case _: NoSymbol => blockCtor("NoSymbol", Nil, symName)(k) case _: TempSymbol | _: VarSymbol => val name = scope.allocateOrGetName(sym) blockCtor("Symbol", Ls(toValue(name)), symName)(k) // preserve names to builtin symbols case _: BuiltinSymbol => blockCtor("Symbol", Ls(toValue(sym.nme)), symName)(k) // FIXME: there may be more types of symbols that need to be renamed during staging case _ => blockCtor("Symbol", Ls(toValue(sym.nme)), symName)(k) def transformOption[A](xOpt: Opt[A], f: A => (Path => Block) => Block)(k: Path => Block): Block = xOpt match case S(x) => f(x)(optionSome(_)(k)) case N => optionNone()(k) // instrumentation rules def ruleEnd(symName: String = "end")(k: Path => Block): Block = blockCtor("End", Ls(), symName)(k) def ruleBranches(x: Path, p: Path, arms: Ls[Case -> Block], dflt: Opt[Block], symName: String = "branches")(using Context)(k: (Path, Context) => Block): Block = def applyRuleBranch(cse: Case, block: Block)(f: Path => Context => Block)(ctx: Context): Block = transformCase(cse): cse => transformBlock(block)(using ctx.clone() += p -> x): (y, ctx) => blockCtor("Arm", Ls(cse, y)): cde => f(cde)(ctx.clone() -= p) (arms.map(applyRuleBranch).collectApply(_: Ls[Path] => Context => Block)(summon)): arms => ctx => tuple(arms): arms => ruleEnd(): e => // TODO: use transformOption here def dfltStaged(k: (Path, Context) => Block) = dflt match case S(dflt) => transformBlock(dflt)(using ctx.clone() += p -> x): (dflt, ctx) => optionSome(dflt)(k(_, ctx.clone() -= p)) case N => optionNone()(k(_, ctx)) dfltStaged: (dflt, ctx) => blockCtor("Match", Ls(x, arms, dflt, e), symName)(k(_, ctx)) // transformations of Block def transformPath(p: Path)(using ctx: Context)(k: Path => Block): Block = // rulePath ctx.get(p).map(k).getOrElse: p match case Value.Ref(l, disamb) => transformSymbol(disamb.getOrElse(l)): sym => blockCtor("ValueRef", Ls(sym), "var")(k) case l: Value.Lit => blockCtor("ValueLit", Ls(l), "lit")(k) case s @ Select(p, Tree.Ident(name)) => transformPath(p): x => val sym = s.symbol.map(transformSymbol(_, S(s))) .getOrElse(blockCtor("Symbol", Ls(toValue(name)))) sym: sym => blockCtor("Select", Ls(x, sym), "sel")(k) case DynSelect(qual, fld, arrayIdx) => transformPath(qual): x => transformPath(fld): y => blockCtor("DynSelect", Ls(x, y, toValue(arrayIdx)), "dynsel")(k) case _: Value.This => raise(ErrorReport(msg"Value.This not supported in staged module." -> p.toLoc :: Nil)) End() def transformResult(r: Result)(using Context)(k: Path => Block): Block = r match case p: Path => transformPath(p)(k) case Tuple(mut, elems) => assert(!mut, "mutable tuple not supported") transformArgs(elems): xs => tuple(xs.map(_._1)): codes => blockCtor("Tuple", Ls(codes), "tup")(k) case Instantiate(mut, cls, argss) => assert(!mut, "mutable instantiation not supported") argss match case Nil => raise(ErrorReport(msg"Instantiate with no argument lists not supported in staged module." -> r.toLoc :: Nil)) End() case args :: Nil => transformArgs(args): xs => transformPath(cls): cls => tuple(xs.map(_._1)): codes => blockCtor("Instantiate", Ls(cls, codes), "inst")(k) case args :: restArgss => raise(ErrorReport(msg"Instantiate with multiple argument lists not supported in staged module." -> r.toLoc :: Nil)) End() // desugar Runtime.Tuple.get into Select case Call(fun, Ls(Arg(_, scrut), Arg(_, Value.Lit(Tree.IntLit(idx)))) :: _) if fun == State.runtimeSymbol.asPath.selSN("Tuple").selSN("get") => transformPath(Select(scrut, Tree.Ident(idx.toString()))(N))(k) case Call(fun, argss) => val stagedFunPath = fun match case s @ Select(qual, Tree.Ident(name)) => s.symbol.flatMap({ case t: TermSymbol => t.owner.flatMap({ case sym: DefinitionSymbol[?] => sym.defn.flatMap(_.hasStagedModifier.map(_ => Select(qual, Tree.Ident(name + "_gen"))(N) )) }) case _ => N }) case _ => N argss match case args :: Nil => val newFun = stagedFunPath.getOrElse(fun) transformPath(newFun): fun => transformArgs(args): args => tuple(args.map(_._1)): tup => blockCtor("Call", Ls(fun, tup), "app")(k) case args :: restArgss => raise(ErrorReport(msg"Call with multiple argument lists not supported in staged module." -> r.toLoc :: Nil)) End() case _ => raise(ErrorReport(msg"Other Results not supported in staged module: ${r.getClass.toString()}" -> r.toLoc :: Nil)) End() def transformArg(a: Arg)(using Context)(k: ((Path, Bool)) => Block): Block = val Arg(spread, value) = a if spread.isDefined then raise(ErrorReport(msg"Spread parameters are not supported in staged module: ${a.toString()}" -> N :: Nil)) End() else transformPath(value): value => blockCtor("Arg", Ls(value)): cde => k(cde, spread.isDefined) def transformArgs(args: Ls[Arg])(using Context)(k: Ls[(Path, Bool)] => Block): Block = args.map(transformArg).collectApply(k) def transformParamList(ps: ParamList)(k: Path => Block) = ps.params.map(p => transformSymbol(p.sym)).collectApply(tuple(_)(k)) def transformParamsOpt(pOpt: Opt[ParamList])(k: Path => Block) = transformOption(pOpt, transformParamList)(k) def transformCase(cse: Case)(using Context)(k: Path => Block): Block = cse match case Case.Lit(lit) => blockCtor("Lit", Ls(Value.Lit(lit)))(k) case Case.Cls(cls, path) => transformSymbol(cls): cls => transformPath(path): path => blockCtor("Cls", Ls(cls, path))(k) case Case.Tup(len, true) => raise(ErrorReport(msg"Spread parameters are not supported in staged module: ${cse.toString()}" -> N :: Nil)) End() case Case.Tup(len, false) => blockCtor("Tup", Ls(toValue(len)))(k) case Case.Field(name, safe) => raise(ErrorReport(msg"Case.Field not supported in staged module." -> name.toLoc :: Nil)) End() def transformBlock(b: Block)(using Context)(k: Path => Block): Block = transformBlock(b)((p, _) => k(p)) def transformBlock(b: Block)(using ctx: Context)(k: (Path, Context) => Block): Block = b match case Return(res, implct) => transformResult(res): x => blockCtor("Return", Ls(x, toValue(implct)), "return")(k(_, ctx)) case Assign(x, r, b) => transformResult(r): y => transformSymbol(x): xSym => blockCtor("ValueRef", Ls(xSym)): xStaged => (Assign(x, xStaged, _)): given Context = ctx.clone() += x.asPath -> xStaged transformBlock(b): (z, ctx) => blockCtor("Assign", Ls(xSym, y, z), "assign")(k(_, ctx)) case Define(cls: ClsLikeDefn, rest) => assert(cls.companion.isEmpty, "nested module not supported") transformBlock(rest): p => transformSymbol(cls.isym): c => // staging the methods within the module cls.methods.map(transformFunDefn).collectApply: methods => tuple(methods): methods => optionNone(): none => // TODO: handle companion object blockCtor("ClsLikeDefn", Ls(c, methods, none)): cls => blockCtor("Define", Ls(cls, p))(k(_, ctx)) case Define(v: ValDefn, rest) => // TODO: only allow ValDefn inside ctors transformBlock(rest): p => transformOption(v.tsym.owner, transformSymbol(_)): owner => transformSymbol(v.sym): sym => transformPath(v.rhs): rhs => blockCtor("ValDefn", Ls(owner, sym, rhs)): v => blockCtor("Define", Ls(v, p))(k(_, ctx)) case End(_) => ruleEnd()(k(_, ctx)) case Match(p, ks, dflt, rest) => transformPath(p): x => ruleBranches(x, p, ks, dflt): (stagedMatch, ctx) => transformBlock(rest)(using ctx): (z, ctx) => fnConcat(stagedMatch, z, "match")(k(_, ctx)) case Begin(sub, rest) => // TODO: This is untested as there is no test case that generates the Begin block yet transformBlock(sub): (sub, ctx) => transformBlock(rest)(using ctx): (rest, ctx) => fnConcat(sub, rest)(k(_, ctx)) case Scoped(syms, body) => syms.toList.sortBy(_.uid).map(transformSymbol(_)).collectApply: symsStaged => tuple(symsStaged): tup => transformBlock(body): (body, ctx) => blockCtor("Scoped", Ls(tup, body))(b => Scoped(syms, k(b, ctx))) case Define(_: FunDefn, _) => raise(ErrorReport(msg"Nested function definitions are not supported in staged modules. Try enabling :ftc." -> N :: Nil)) End() case _: Label | _: Break => raise(ErrorReport(msg"Other Blocks not supported in staged module: ${b.getClass.toString()}." -> N :: Nil)) End() case _ => raise(ErrorReport(msg"Other Blocks not supported in staged module: ${b.getClass.toString()}" -> N :: Nil)) End() def transformFunDefn(f: FunDefn)(using Context)(k: Path => Block): Block = transformBlock(f.body): body => if f.params.length != 1 then raise(ErrorReport(msg":ftc must be enabled to desugar functions with multiple parameter lists." -> f.sym.toLoc :: Nil)) // maintain parameter names in instrumented code f.params.map( _.params.map(p => blockCtor("Symbol", Ls(toValue(p.sym.nme)))).collectApply ).collectApply: paramListSyms => blockCtor("Symbol", Ls(toValue(f.sym.nme))): sym => paramListSyms.map(tuple(_)).collectApply: tups => tuple(tups): tup => blockCtor("FunDefn", Ls(sym, tup, body, toValue(true)))(k) def applyFunDefnInner(f: FunDefn): (FunDefn, Block => Block) = val genSymName = f.sym.nme + "_instr" val genSym = BlockMemberSymbol(genSymName, Nil, false) val sym = f.owner.get.asPath.selSN(genSymName) // turn into fundefn val dSym = TermSymbol(f.dSym.k, f.dSym.owner, Tree.Ident(f.sym.nme + "_instr")) val argSyms = f.params.flatMap(_.params).map(_.sym) val newBody = Scoped(Set(argSyms*), transformFunDefn(f)(using new HashMap)(Return(_, false))) // TODO: remove it. only for test val debug = (k: Block) => call(sym, Nil)(fnPrintCode(_)(k)) val newFun = f.copy(sym = genSym, dSym = dSym, params = Ls(PlainParamList(Nil)), body = newBody)(f.configOverride, f.annotations) (newFun, debug) override def applyBlock(b: Block): Block = super.applyBlock(b) match // find modules with staged annotation case Define(c: ClsLikeDefn, rest) if c.companion.exists(_.isStaged) => val sym = c.sym.subst val companion = c.companion.get val (stagedMethods, debugPrintCode) = companion.methods .map(applyFunDefnInner) .unzip val ctor = FunDefn.withFreshSymbol(S(companion.isym), BlockMemberSymbol("ctor$", Nil), Ls(PlainParamList(Nil)), companion.ctor)(N, Nil) val (stagedCtor, ctorPrint) = applyFunDefnInner(ctor) val unit = State.runtimeSymbol.asPath.selSN("Unit") val debugBlock = (ctorPrint :: debugPrintCode).foldRight((Return(unit, true): Block))(_(_)) def debugCont(rest: Block) = Begin(debugBlock, rest) // add generator functions for classes within the constructor val genCls = new BlockTransformer(SymbolSubst.Id): override def applyBlock(b: Block): Block = super.applyBlock(b) match case Define(c: ClsLikeDefn, rest) if c.companion.isEmpty => val (stagedMethods, debugPrintCode) = c.methods .map(applyFunDefnInner) .unzip val newModule = c.copy(methods = c.methods ++ stagedMethods)(c.configOverride, c.annotations.filter: case Annot.Modifier(Keyword.`staged`) => false case _ => true) Define(newModule, rest) case b => b val newCtor = genCls.applyBlock(companion.ctor) val newCompanion = companion.copy( methods = stagedCtor :: companion.methods ++ stagedMethods, ctor = Begin(newCtor, debugCont(End())), ) val newModule = c.copy(sym = sym, companion = S(newCompanion))(c.configOverride, c.annotations.filter: case Annot.Modifier(Keyword.`staged`) => false case _ => true) Define(newModule, rest) case b => b def mkDefnMap(b: Block): Unit = val transformer = new BlockTraverser: override def applyDefn(defn: Defn) = defn match case c: ClsLikeDefn => defnMap.addOne(c.isym, c) super.applyDefn(defn) case _ => super.applyDefn(defn) transformer.applyBlock(b) def apply(b: Block) = mkDefnMap(b) applyBlock(b) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.ScopeData.* import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt import java.util.IdentityHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet object ScopeData: opaque type ScopeUID = Int class FreshUID: private val underlying = FreshInt() def make: ScopeUID = underlying.make class ScopeFinder(fresh: FreshUID, ignoredClasses: Set[DefinitionSymbol[?] & InnerSymbol]) extends BlockTraverserShallow: var objs: List[ScopedObject] = Nil override def applyBlock(b: Block): Unit = b match case s: Scoped => val id = fresh.make objs ::= ScopedObject.ScopedBlock(id, s) case l: Label if l.loop => objs ::= ScopedObject.Loop(l.label, l.body) applySubBlock(l.rest) case _ => super.applyBlock(b) override def applyFunDefn(fun: FunDefn): Unit = objs ::= ScopedObject.Func(fun, N) override def applyDefn(defn: Defn): Unit = defn match case f: FunDefn => applyFunDefn(f) case c: ClsLikeDefn => if !ignoredClasses.contains(c.isym) then objs ::= ScopedObject.Class(c, c.k === syntax.Obj) c.ctorSym match case Some(value) => objs ::= ScopedObject.ClassCtor(c) case None => () c.companion.map: comp => objs ::= ScopedObject.Companion(comp, c) case _ => super.applyDefn(defn) type ScopedInfo = DefinitionSymbol[?] | LabelSymbol | ScopeUID | Unit given Ordering[ScopedInfo] with def compare(x: ScopedInfo, y: ScopedInfo): Int = (x, y) match case (a: Symbol, b: Symbol) => Ordering[Uid[Symbol]].compare(a.uid, b.uid) case (_: Symbol, _) => -1 case (_, _: Symbol) => 1 case ((), ()) => 0 case ((), _) => -1 case (_, ()) => 1 case (a: Int, b: Int) => a compare b // ScopeUID is Int inside ScopeData // ScopeData requires the set of ignored scopes to compute certain things, but // the lifter requires the scope tree to generate the metadata. To solve this, // we generate the scope tree then populate the metadata later. case class IgnoredScopes(var ignored: Opt[Set[ScopedInfo]]) type ScopedObject = ScopedObject.ScopedObject[?] type TScopedObject[T] = ScopedObject.ScopedObject[T] type LiftedSym = DefinitionSymbol[?] extension (d: DefinitionSymbol[?]) def asBmsRef = Value.Ref(d.asBlkMember.get, S(d)) enum MethodKind: case ClsMethod case ObjMethod case ModMethod // These cannot be hashed object ScopedObject: // T: The actual contents of the scoped object sealed abstract class ScopedObject[T]: var node: Opt[TScopeNode[T]] = N lazy val toInfo: ScopedInfo = this match case Top(_) => () case Class(cls, _) => cls.isym case Companion(clsBody, compDefn) => clsBody.isym case ClassCtor(cls) => cls.ctorSym.get case Func(fun, _) => fun.dSym case ScopedBlock(uid, block) => uid case Loop(sym, _) => sym // note: not unique lazy val nme = this match case Top(b) => "top" case Class(cls, _) => cls.isym.nme case Companion(clsBody, compDefn) => clsBody.isym.nme + "_mod" case ClassCtor(cls) => cls.isym.nme // should be unused case Func(fun, isMethod) => fun.dSym.nme case Loop(sym, block) => "loop$" + sym.uid.toString() case ScopedBlock(uid, block) => "scope" + uid // Locals defined by a scoped object. lazy val definedLocals: Set[Local] = this match // we want definedLocals for the top level scope to be empty, because otherwise, // the lifter may try to capture those locals. case Top(b) => Set.empty case Class(cls, _) => // Public fields are not included, as they are accessed using // a field selection rather than directly using the BlockMemberSymbol. val paramsSet: Set[Local] = cls.paramsOpt match case Some(value) => value.params.map(_.sym).toSet case None => Set.empty val auxSet: Set[Local] = cls.auxParams.flatMap: p => p.params.map(_.sym) .toSet paramsSet ++ auxSet ++ cls.privateFields + cls.isym case Companion(clsBody, compDefn) => clsBody.privateFields.toSet + clsBody.isym case _: ClassCtor => Set.empty case Func(fun, _) => fun.params.flatMap: p => p.restParam.map(_.sym) ++ p.params.map(_.sym) .toSet case ScopedBlock(_, block) => block.syms.toSet case _: Loop => Set.empty def contents: T = this match case Top(b) => b case Class(cls, _) => cls case Companion(clsBody, compDefn) => clsBody case ClassCtor(cls) => () case Func(fun, _) => fun case ScopedBlock(_, block) => block case Loop(_, blk) => blk // Scoped nodes which may be referenced using a symbol. sealed abstract class Referencable[T] extends TScopedObject[T]: def sym: LiftedSym = this match case Class(cls, _) => cls.isym case Companion(clsBody, compDefn) => clsBody.isym case Func(fun, isMethod) => fun.dSym case ClassCtor(cls) => cls.ctorSym.get def bsym: BlockMemberSymbol = this match case Class(cls, _) => cls.sym case Companion(clsBody, compDefn) => compDefn.sym case Func(fun, isMethod) => fun.sym case ClassCtor(cls) => cls.sym def owner: Opt[InnerSymbol] = this match case Class(cls, _) => cls.owner case Companion(clsBody, compDefn) => compDefn.owner case ClassCtor(cls) => cls.owner case Func(fun, isMethod) => fun.owner // Scoped nodes which could possibly be lifted to the top level. sealed abstract class Liftable[T <: Defn] extends Referencable[T]: val defn: T // The top-level scope. case class Top(b: Block) extends ScopedObject[Block] // b may be a scoped block, in which case, its variables represent the top-level variables. case class Class(cls: ClsLikeDefn, isObj: Bool) extends Liftable[ClsLikeDefn]: val defn = cls case class Companion(clsBody: ClsLikeBody, compDefn: ClsLikeDefn) extends Referencable[ClsLikeBody] // We model it like this: the ctor is just another function in the same scope as the class and initializes the corresponding class case class ClassCtor(cls: ClsLikeDefn) extends Referencable[Unit] // isMethod: // N = not a method // S(false) = module method // S(true) = class or object method case class Func(fun: FunDefn, isMethod: Opt[MethodKind]) extends Liftable[FunDefn]: val defn = fun // The purpose of `Loop` is to enforce the rule that the control flow remains linear when we enter // a scoped block. case class Loop(sym: LabelSymbol, body: Block) extends ScopedObject[Block] case class ScopedBlock(uid: ScopeUID, block: Scoped) extends ScopedObject[Block] extension (traverser: BlockTraverser) def applyScopedObject(obj: ScopedObject) = extension (s: Symbol) def traverse = traverser.applySymbol(s) obj match case ScopedObject.Top(b) => traverser.applyBlock(b) case ScopedObject.Class(ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable), _) => // do not traverse the companion -- it is a separate kind of scoped object // and will therefore be traversed separately own.foreach(_.traverse) isym.traverse sym.traverse ctorSym.foreach(_.traverse) paramsOpt.foreach(traverser.applyParamList) auxParams.foreach(traverser.applyParamList) parentPath.foreach(traverser.applyPath) methods.foreach(traverser.applyFunDefn) privateFields.foreach(_.traverse) publicFields.foreach: f => f._1.traverse; f._2.traverse traverser.applySubBlock(preCtor) traverser.applySubBlock(ctor) case ScopedObject.Companion(comp, cls) => traverser.applyCompanionModule(comp) case ScopedObject.Func(fun, isMethod) => traverser.applyFunDefn(fun) case ScopedObject.ScopedBlock(uid, block) => traverser.applyBlock(block) case ScopedObject.ClassCtor(c) => () case ScopedObject.Loop(_, b) => traverser.applyBlock(b) // A simple tree data structure representing the nesting relation of definitions and scopes. class NestedScopeTree(val root: TScopeNode[Block]): val nodesMap: Map[ScopedInfo, ScopeNode] = root.allChildNodes.map(n => n.obj.toInfo -> n).toMap type ScopeNode = ScopeNode.ScopeNode[?] type TScopeNode[T] = ScopeNode.ScopeNode[T] object ScopeNode: case class ScopeNode[T](obj: TScopedObject[T], var ancestor: Opt[ScopeNode[?]], children: List[ScopeNode[?]])(using ignoredScopes: IgnoredScopes): lazy val allAncestors: List[ScopeNode[?]] = ancestor match case Some(value) => this :: value.allAncestors case None => this :: Nil lazy val parentsSet = allAncestors.map(_.obj.toInfo).toSet def inSubtree(root: ScopedInfo) = parentsSet.contains(root) // note: includes itself lazy val allChildNodes: List[ScopeNode[?]] = this :: children.flatMap(_.allChildNodes) lazy val allChildren: List[ScopedObject] = allChildNodes.map(_.obj) // does not include variables introduced by itself lazy val existingVars: Set[Local] = ancestor match case Some(value) => value.existingVars ++ value.obj.definedLocals ++ value.nestedModObjSyms case None => Set.empty lazy val inModOrTopLevel: Bool = ancestor match case Some(par) => par.obj match case _: ScopedObject.Companion => true case _: ScopedObject.Top => true case _ => false case None => true // Scoped blocks include the BlockMemberSymbols of their nested definitions. This removes them. lazy val localsWithoutBms: Set[Local] = obj match case s: ScopedObject.ScopedBlock => val rmv = children.collect: case c @ ScopeNode(obj = s: ScopedObject.Referencable[?]) => s.bsym obj.definedLocals -- rmv case _ => obj.definedLocals lazy val nestedModObjSyms: Set[InnerSymbol] = children.collect: case ScopeNode(obj = c: ScopedObject.Class) if c.isObj => c.cls.isym case ScopeNode(obj = c: ScopedObject.Companion) => c.clsBody.isym .toSet lazy val liftedObjSyms: Set[InnerSymbol] = children.collect: case n @ ScopeNode(obj = c: ScopedObject.Class) if c.isObj && n.isLifted => c.cls.isym .toSet lazy val inScopeISyms: Set[Local] = val parVals = ancestor match case Some(value) => value.inScopeISyms case None => Set.empty obj match case c: ScopedObject.Class => parVals + c.cls.isym case c: ScopedObject.Companion => parVals + c.clsBody.isym case _ => parVals /** * Partitions the nodes of the scope tree into two lists `as` and `bs`, where: * - `bs` contains the the highest children of the curent node such that `f(b)` is `true` for `b` in `bs`, and * - `as` contains the parents of all nodes in `bs`. * * @param f The predicicate used to partition the tree. * @return The partitioned tree. */ def partitionTree(f: ScopeNode[?] => Bool): (List[ScopeNode[?]], List[ScopeNode[?]]) = val (as, bs) = children.foldLeft((List.empty[ScopeNode[?]], List.empty[ScopeNode[?]])): case ((l1, l2), child) => if f(child) then (l1, child :: l2) else val (l1_, l2_) = child.partitionTree(f) (l1_ ::: l1, l2_ ::: l2) (this :: as, bs) /** * Partitions the nodes of the scope tree into two lists `as` and `bs`, where: * - `bs` contains the the highest children of the curent node such that `f(b.obj)` is `true` for `b` in `bs`, and * - `as` contains the parents of all nodes in `bs`. * * @param f The predicicate used to partition the tree. * @return The partitioned tree. */ def partitionTree2(f: ScopedObject => Bool): (List[ScopeNode[?]], List[ScopeNode[?]]) = partitionTree(x => f(x.obj)) // All of the following must not be called until ignoredScopes is populated with the relevant data. lazy val isLifted: Bool = def impl: Bool = val ignored = ignoredScopes.ignored match case Some(value) => value case None => lastWords("isLifted accessed before the set of ignored scopes was set") // check if the owner is a module obj match case r: ScopedObject.Referencable[?] => r.owner match case Some(value) => if value.asMod.isDefined then return false case _ => () case _ => () ancestor.map(_.obj) match case Some(_: ScopedObject.Companion) => false // there is no need to lift objects nested inside a module case _ => obj match case _ if ignored.contains(obj.toInfo) => false case _ if inModOrTopLevel => false case ScopedObject.Func(isMethod = S(_)) => false case c: ScopedObject.Class if c.isObj => false case _: ScopedObject.Loop | _: ScopedObject.ClassCtor | _: ScopedObject.ScopedBlock | _: ScopedObject.Companion => false case _ => true impl // Finds the first ancestor that is a lifted object, i.e. a non-ignored definition, or the top level lazy val firstLiftedAncestor: ScopedObject = if !isLifted then ancestor match case Some(value) => value.firstLiftedAncestor case None => obj match case _: ScopedObject.Top => obj case _ => lastWords("unreachable") else obj // When a node is lifted, some neighbouring ignored definitions may become out of scope. This computes // the list of these definitions, and they could be passed to this node as a parameter once lifted. private lazy val reqCaptureObjsImpl: List[ScopedObject.Referencable[?]] = obj match case _: ScopedObject.Top => List.empty case _ => // All unlifted neighbour nodes ::: ancestor's reqCaptureObjsImpl val initial = ancestor.get.children .filter: case ScopeNode(obj = f: ScopedObject.Func) if f.isMethod.isDefined => false case _ => true .collect: case c @ ScopeNode(obj = t: ScopedObject.Referencable[?]) if !c.isLifted && !inModOrTopLevel && !(t is obj) => t initial ::: ancestor.get.reqCaptureObjsImpl lazy val reqCaptureObjs: List[ScopedObject.Referencable[?]] = obj match case _: ScopedObject.Top => List.empty case _ => if isLifted then reqCaptureObjsImpl else ancestor.get.reqCaptureObjsImpl def dSymUnapply(data: ScopeData, v: DefinitionSymbol[?] | Option[DefinitionSymbol[?]]) = v match case Some(d) if data.contains(d) => S(d) case d: DefinitionSymbol[?] if data.contains(d) => S(d) case _ => None class ScopeData(b: Block)(using State, IgnoredScopes): import ScopeData.* def contains(s: ScopedInfo) = scopeTree.nodesMap.contains(s) def getNode(x: ScopedInfo): ScopeNode = scopeTree.nodesMap(x) def getNode(defn: ClsLikeDefn): ScopeNode = getNode(defn.isym) def getNode(companion: ClsLikeBody): ScopeNode = getNode(companion.isym) def getNode(defn: FunDefn): ScopeNode = getNode(defn.dSym) def getUID(blk: Scoped): ScopeUID = if scopedMap.containsKey(blk) then scopedMap.get(blk) else lastWords("getUID: key not found") def getNode(blk: Scoped): ScopeNode = getNode(getUID(blk)) // From the input block or definition, traverses until a function, class or new scoped block is found and appends them. // Classes with owners are ignored, as they could be defined within a scoped block within the constructor. // We instead add all these classes when we encounter them. def scopeFinder = new ScopeFinder(fresh, classesWithOwner.keySet) // entry point private val fresh = FreshUID() private val (classesWithOwner, classesMap) = val mp1: MutMap[DefinitionSymbol[?] & InnerSymbol, InnerSymbol] = MutMap.empty val mp2: MutMap[InnerSymbol, ClsLikeDefn] = MutMap.empty new BlockTraverser: applyBlock(b) override def applyDefn(defn: Defn): Unit = defn match case c @ ClsLikeDefn(owner = S(own)) => mp1.put(c.isym, own) mp2.put(c.isym, c) c.companion.foreach: comp => mp1.put(comp.isym, own) super.applyDefn(c) case _ => super.applyDefn(defn) (mp1.toMap, mp2.toMap) private val ownersInv: Map[InnerSymbol, List[DefinitionSymbol[?] & InnerSymbol]] = classesWithOwner .toList.groupBy(_._2) .map: case k -> v => k -> v.map(_._1) .toMap val scopeTree = NestedScopeTree(makeScopeTreeRec(ScopedObject.Top(b))) val root = scopeTree.root val allBms = root.allChildren.collect: case s: ScopedObject.Referencable[?] => s.bsym private val scopedMap: IdentityHashMap[Scoped, ScopeUID] = new IdentityHashMap for case ScopeNode(obj = ScopedObject.ScopedBlock(uid, blk)) <- scopeTree.root.allChildNodes do scopedMap.put(blk, uid) def makeScopeTreeRec[T](obj: TScopedObject[T]): TScopeNode[T] = // An annoying thing with class ctors: // Sometimes, nested classes/objects appear inside the top-level scoped block of class/module ctor, // despite being a child of the class, but we want these to be a direct child of the class/module. val finder = scopeFinder obj match case ScopedObject.Top(s: Scoped) => finder.applyBlock(s.body) case ScopedObject.Top(b) => finder.applyBlock(b) case ScopedObject.Class(cls, _) => finder.applyBlock(cls.preCtor) finder.applyBlock(cls.ctor) case ScopedObject.Companion(comp, cls) => finder.applyBlock(comp.ctor) case ScopedObject.Func(fun, _) => finder.applyBlock(fun.body) case ScopedObject.ScopedBlock(_, block) => finder.applyBlock(block.body) case ScopedObject.ClassCtor(c) => () case ScopedObject.Loop(_, b) => finder.applyBlock(b) val mtdObjs = obj match case ScopedObject.Class(cls, _) => val k = if cls.k is syntax.Obj then MethodKind.ObjMethod else MethodKind.ClsMethod cls.methods.map(ScopedObject.Func(_, S(k))) case ScopedObject.Companion(comp, cls) => comp.methods.map(ScopedObject.Func(_, S(MethodKind.ModMethod))) case _ => Nil val isym = obj match case c: ScopedObject.Class => S(c.cls.isym) case c: ScopedObject.Companion => S(c.clsBody.isym) case _ => N val ownedClasses = isym.flatMap(ownersInv.get(_)) match case S(syms) => syms.map(d => classesMap.get(d)).collect: case S(c) => c case N => Nil val ownedClassObjs = ownedClasses.flatMap: c => var objs: List[ScopedObject] = List.empty objs ::= ScopedObject.Class(c, c.k === syntax.Obj) c.ctorSym match case Some(value) => objs ::= ScopedObject.ClassCtor(c) case None => () c.companion.map: comp => objs ::= ScopedObject.Companion(comp, c) objs val children = (ownedClassObjs ::: mtdObjs ::: finder.objs).map(makeScopeTreeRec) val retNode = ScopeNode.ScopeNode(obj, N, children) obj.node = S(retNode) for c <- children do c.ancestor = S(retNode) retNode ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeFlattener.scala ================================================ package hkmc2 package codegen import scala.collection.mutable import mlscript.utils.*, shorthands.* import hkmc2.utils.* import semantics.* /** - For function bodies, fuse all shallowly-nested scopes into one top-level one, * because certain passes, such as the handler lowering, rely on knowing all the local * variables of each function. * - Assert the absence of Label(loop = true) blocks, * because loops should be rewritten to functions first, * otherwise we cannot fuse scopes correctly. */ class ScopeFlattener extends BlockTransformer(new SymbolSubst): override def applyBlock(b: Block): Block = b match case Label(_, loop, _, _) => assert(!loop, "loops should be rewritten to functions before scope flattening") super.applyBlock(b) case _ => super.applyBlock(b) private var scopedSymForCurrentFun: Opt[mutable.Set[Symbol]] = N override def applyFunBodyLikeBlock(b: Block): Block = val prevScopedSymForCurrentFun = scopedSymForCurrentFun val resBlk = b match case Scoped(syms, body) => val tmp = mutable.Set.from(syms) scopedSymForCurrentFun = S(tmp) val newBody = applySubBlock(body) if (newBody is body) && tmp.sizeCompare(syms) === 0 then b else Scoped(tmp, newBody) case _ => val tmp = mutable.Set.empty[Symbol] scopedSymForCurrentFun = S(tmp) val newBlk = applySubBlock(b) Scoped(tmp, newBlk) scopedSymForCurrentFun = prevScopedSymForCurrentFun resBlk override def applyScopedBlock(b: Block): Block = b match case Scoped(syms, body) => scopedSymForCurrentFun match case N => super.applyScopedBlock(b) case S(scopedForCurrentFun) => scopedForCurrentFun.addAll(syms) super.applySubBlock(body) case _ => super.applySubBlock(b) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/SpecializedSwitch.scala ================================================ package hkmc2 package codegen import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.syntax.{Literal} import scala.annotation.tailrec /** * Represents a switch case. * @param l The case's literal value. * @param b The case body. */ enum SwitchCase(val litValue: Literal, val body: Block): /** * A switch case that requires an explicit `break` to be inserted by the codegen. * @param l The case's literal value. * @param b The case body. */ case ExplicitBreak(l: Literal, b: Block) extends SwitchCase(l, b) /** * A switch case that is abortive and does not explicitly require a `break`. * @param l The case's literal value. * @param b The case body. */ case Abortive(l: Literal, b: Block) extends SwitchCase(l, b) /** * A switch case that falls through to the subsequent case. * @param l The case's literal value. * @param b The case body. * @param next The literal value of the next case. */ case Fallthrough(l: Literal, b: Block, next: Literal) extends SwitchCase(l, b) private enum MatchType: case MFallthrough(value: Literal, body: Block, next: Literal) case MAbortive(arms: List[Literal -> Block]) case MCases(arms: List[Literal -> Block]) /* * We specialize chains of match statements of the following form: * * M1 M2 ... Mn * * where each Mi are match statements that match on a common scrutinee `x`, only have literal patterns, * and have an empty or no default case, except for Mn. We define three types of such match statements * (which are mostly unrelated to switch case types in the enum `SwitchCase`): * - MFallthrough(next): Has only one branch, and if the branch executes to completion, then `next` is assigned * to `x`. * - MAbortive: All branches are abortive (and thus exits the scope that the match chain is defined in). * - MCases: Is not an MFallthrough or an MAbortive (but still matches on `x` and only has literals patterns). * * For this chain to be specialized, for each adjacent pair Mi and M(i+1), one of the following hold: * * - Mi = MFallthrough(_, _, v), and the first case of M(i+1) matches v. * - Mi = MAbortive. * * Note that this means Mi = MCases only if i = n. * * Furthermore, if M(n-1) is an MAbortive, then the last statement may have a non-empty default case and it will be * compiled into `default: body`. * * - MFallthrough is translated into SwitchCase.Fallthrough. * - MAbortive is translated into SwitchCase.Abortive. * - MCases is translated into a list of SwitchCase.ExplicitBreak. */ private object PostCondRes: val empty = PostCondRes(false, false, Map.empty) // isImpure: indicates whether the result was computed for a possibly impure expression. All analysis results // that could possibly come before this result must be discarded. // isAbortive: indicates whether the result was computed for an abortive block. When merging results from // different branches, the results for this must be discarded. private case class PostCondRes(isImpure: Bool, isAbortive: Bool, varsMap: Map[Local, Literal]): def markImpure = copy(isImpure = true) // Combines postconditions from different branches. private def combinePostConds(p1: PostCondRes, p2: PostCondRes) = if p1.isAbortive && p2.isAbortive then PostCondRes(false, false, Map.empty) else if p1.isAbortive then PostCondRes(p2.isImpure, false, p2.varsMap) // We don't care about whether p1 is impure if it is abortive. else if p2.isAbortive then PostCondRes(p1.isImpure, false, p1.varsMap) // Same as above else // If a variable was set to a different value in either branch, or was only set in one branch, do not include // Otherwise, we can combine them val combined = p1.varsMap.collect: case k -> v1 if p2.varsMap.contains(k) && p2.varsMap(k) == v1 => k -> v1 PostCondRes(p1.isImpure || p2.isImpure, false, combined) extension (r: PostCondRes) // For combining postconditions derived in different branches def ++(r2: PostCondRes): PostCondRes = combinePostConds(r, r2) // For combining sequences of postconditions def >=>(r2: PostCondRes): PostCondRes = if r.isAbortive then r else if r2.isImpure then r2 else PostCondRes(r.isImpure, r2.isAbortive, r.varsMap ++ r2.varsMap) def +(v: Local -> Literal): PostCondRes = PostCondRes(r.isImpure, r.isAbortive, r.varsMap + v) // Analyzes postconditions for a block. Namely, determines variables that are // definitely set to a certain literal. // // Note that impure computations will lead to `isImpure` being set to true. This means // all analysis *before* the impure computation will be discarded. // // The intended semantics of this are, assuming the block finishes execution, i.e. // it does not break to a label that wraps the block or returns, then the // postconditions hold. Otherwise, the results are invalid, which does not matter, // because they will be irrelevant in that case anyway. private object PostCondAnalysisImpl extends CachedAnalysis[Block, PostCondRes]: private def res(lhs: Opt[Local], rhs: Result, rest: Block) = if rhs.isPure then lhs match case Some(lhs) => rhs match case Value.Lit(lit) => PostCondRes(false, false, Map(lhs -> lit)) >=> analyze(rest) case _ => analyze(rest) case None => analyze(rest) else analyze(rest).markImpure override def analyzeUncached(b: Block): PostCondRes = b match case Label(label, loop, body, rest) => /* We do not traverse into the label body. * If we do, then there are cases like this: * * label l1: * label l2: * body * set x = 1 * end * end * * and it is very difficult to tell whether we should have the postcondition x = 2 in the outer loop, * because `body` could break out of `l1` or `l2`. * * Not traversing into labels also makes `Block.isAbortive` more useful, as we always know that * if a block is abortive, then it aborts out of the "initial" block that `analyze` was called on. */ val restRes = analyze(rest) restRes.copy(isImpure = restRes.isImpure || analyze(body).isImpure) case Match(scrut, arms, dflt, rest) => arms.foldLeft(dflt.map(analyze).getOrElse(PostCondRes.empty)): case (acc, (_, blk)) => acc ++ analyze(blk) >=> analyze(rest) case Scoped(syms, body) => analyze(body) case Begin(sub, rest) => analyze(sub) >=> analyze(rest) case TryBlock(sub, finallyDo, rest) => analyze(sub) >=> analyze(finallyDo) >=> analyze(rest) case Assign(lhs, rhs, rest) => res(S(lhs), rhs, rest) case AssignField(path, _, rhs, rest) => res(N, rhs, rest) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => res(N, rhs, rest) case Define(defn, rest) => defn match case v: ValDefn => res(S(v.sym), v.rhs, rest) case c: ClsLikeDefn => analyze(rest).markImpure // TODO: refine for object and module ctors case f: FunDefn => analyze(rest) case b: BlockTail => PostCondRes.empty.copy(isAbortive = b.isAbortive) private object PostCondAnalysis extends CachedAnalysis[Block, Map[Local, Literal]]: override def analyzeUncached(b: Block): Map[Symbol, Literal] = PostCondAnalysisImpl.analyze(b).varsMap // Matches List[Case.Lit -> Block] private object LitCases: def unapply(arms: List[Case -> Block]) = arms.foldLeft[Opt[List[Literal -> Block]]](S(Nil)): case (S(acc), Case.Lit(litVal) -> b) => S((litVal -> b) :: acc) case _ => N private case class MatchChain(scrut: Value.Ref, cases: List[MatchType], dflt: Opt[Block], rest: Block) // Helper that determines whether a default branch is empty private def isEmptyDflt(dflt: Opt[Block]) = dflt match case S(x) if x.isEmpty => true case N => true case _ => false // Extracts a valid match chain beginning at a block. @tailrec private def findMatchChainRec( b: Block, scrutRef: Value.Ref, acc: List[MatchType] ): MatchChain = object TailAssign: def unapply(b: Block) = if b.isAbortive then N else PostCondAnalysis.analyze(b).get(scrutRef.l) match case S(value) => S(value) case N => N // Whether the current match may have a non-empty default case. // It is allowed iff the previous case was a break, or if this is the only case. val isDfltCaseAllowed = acc.headOption match case Some(_: MatchType.MAbortive) => true case None => true case _ => false // This block does not include the match chain inline def fail = MatchChain(scrutRef, acc, N, b) inline def success(res: MatchType, m: Match, dflt: Opt[Block], dfltEmpty: Bool, rest: Block) = if !dfltEmpty then MatchChain(scrutRef, res :: acc, dflt, rest) else findMatchChainRec(rest, scrutRef, res :: acc) inline def join: MatchChain = b match case m: Match => val dfltEmpty = isEmptyDflt(m.dflt) // Classify the current match statement. m match // MFallthrough case Match( `scrutRef`, // * The scrutinee is a ref and is the same as the one before. Case.Lit(curVal) -> (b @ TailAssign(nextVal)) :: Nil, // * There is only one case matching a literal // and it assigns the scrut to a literal. default, restBlk ) => if !isDfltCaseAllowed && !dfltEmpty then fail // Default branch not allowed else val res = MatchType.MFallthrough(curVal, b, nextVal) success(res, m, m.dflt, dfltEmpty, restBlk) // MAbortive or MCases case Match(`scrutRef`, LitCases(arms), default, restBlk) => // MAbortive if arms.forall(_._2.isAbortive) then // If both default and restBlk are not End(), and default blocks are not allowed, then fail // Otherwise, take the non-end one as the next block val restEmpty = restBlk.isEmpty val res = MatchType.MAbortive(arms) if !dfltEmpty && !restEmpty then if !isDfltCaseAllowed then fail else success(res, m, m.dflt, dfltEmpty, restBlk) else if restEmpty then success(res, m, N, true, m.dflt.getOrElse(End())) else success(res, m, m.dflt, dfltEmpty, restBlk) // MCases else if !isDfltCaseAllowed && !dfltEmpty then fail // Default branch not allowed else val res = MatchType.MCases(arms) success(res, m, m.dflt, dfltEmpty, restBlk) case _ => fail case _ => fail // Get the first case's value (in case the previous match is MFallthrough). val curVal = b match case m: Match => m.arms.headOption.collect: case Case.Lit(lit) -> _ => lit case _ => N // Check for the valid cases: // - The previous match is a fallthrough that sets the scrut to curVal. // - The previous match is abortive. acc.headOption match case S(MatchType.MFallthrough(next = expectedVal)) if curVal.map(_ == expectedVal).getOrElse(true) => join // OK case S(_: MatchType.MAbortive) | N => join // OK case S(_) => fail private case class SwitchLike(scrut: Value.Ref, cases: List[SwitchCase], dflt: Opt[Block], rest: Block) // Converts a match chain to a switch. private def matchChainToSwitch(m: MatchChain): SwitchLike = def mpArms(arms: List[(Literal, Block)]) = arms.map: case (l, b) => if b.isAbortive then SwitchCase.Abortive(l, b) else SwitchCase.ExplicitBreak(l, b) val cases = m.cases.flatMap: case MatchType.MFallthrough(value, body, next) => SwitchCase.Fallthrough(value, body, next) :: Nil case MatchType.MAbortive(arms) => mpArms(arms) case MatchType.MCases(arms) => mpArms(arms) SwitchLike(m.scrut, cases, m.dflt, m.rest) object SpecializedSwitch: def unapply(b: Block) = b match case m @ Match(scrut = r @ Value.Ref(l, _)) => val chain = findMatchChainRec(m, r, Nil) val SwitchLike(scrut, cases, dflt, rest) = matchChainToSwitch(chain) if cases.size < 2 then N else S((scrut, cases.reverse, dflt, rest)) case _ => N ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* import hkmc2.semantics.Elaborator.State import hkmc2.semantics.* import hkmc2.syntax.Tree import hkmc2.codegen.HandlerLowering.FnOrCls class StackSafeTransform(depthLimit: Int, paths: HandlerPaths, stackSafetyMap: StackSafetyMap)(using State, Config): private val STACK_DEPTH_IDENT: Tree.Ident = Tree.Ident("stackDepth") private val runtimePath: Path = State.runtimeSymbol.asPath private val checkDepthPath: Path = runtimePath.selN(Tree.Ident("checkDepth")) private val runStackSafePath: Path = runtimePath.selN(Tree.Ident("runStackSafe")) private val stackDepthPath: Path = runtimePath.selN(STACK_DEPTH_IDENT) private def intLit(n: BigInt) = Value.Lit(Tree.IntLit(n)) private def op(op: String, a: Path, b: Path) = Call(State.builtinOpsMap(op).asPath, (a.asArg :: b.asArg :: Nil) ne_:: Nil)(true, false, false) // Increases the stack depth, assigns the call to a value, then decreases the stack depth // then binds that value to a desired block def extractRes(res: Result, isTailCall: Bool, f: Result => Block, sym: Symbol, curDepth: => Symbol): Block = if isTailCall then Return(res, false) else blockBuilder .assign(sym, res) .assignFieldN(runtimePath, STACK_DEPTH_IDENT, curDepth.asPath) .rest(f(sym.asPath)) def wrapStackSafe(body: Block, resSym: Local, rest: Block) = val bodSym = BlockMemberSymbol("‹stack safe body›", Nil, false) val bodFun = FunDefn.withFreshSymbol(N, bodSym, ParamList(ParamListFlags.empty, Nil, N) :: Nil, body)(configOverride = N, annotations = Nil) Scoped(Set.single(bodSym), Define(bodFun, Assign(resSym, Call(runStackSafePath, (intLit(depthLimit).asArg :: bodSym.asPath.asArg :: Nil) ne_:: Nil)(true, true, false), rest)) ) def extractResTopLevel(res: Result, isTailCall: Bool, f: Result => Block, sym: Symbol, curDepth: => Symbol) = val resSym = sym wrapStackSafe(Ret(res), resSym, f(resSym.asPath)) // Rewrites anything that can contain a Call to increase the stack depth def transform(b: Block, curDepth: => Symbol, isTopLevel: Bool = false): Block = def usesStack(r: Result) = r match case Call(Value.Ref(_: BuiltinSymbol, _), _) => false case c: Call if !c.mayRaiseEffects => false // a call can only trigger a stack delay if it can raise effects case _: Call | _: Instantiate => true case _ => false val extract = if isTopLevel then extractResTopLevel else extractRes val transform = new BlockTransformer(SymbolSubst.Id): override def applyFunDefn(fun: FunDefn): FunDefn = rewriteFn(fun) override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case defn: ClsLikeDefn => k(rewriteCls(defn, isTopLevel)) case _: FunDefn | _: ValDefn => super.applyDefn(defn)(k) override def applyBlock(b: Block): Block = b match case Return(res, implct) if usesStack(res) => val tmp = TempSymbol(N, "res") super.applyResult(res): res => Scoped(Set.single(tmp), extract(res, true, Return(_, implct), tmp, curDepth)) // Optimization to avoid generation of unnecessary variables case Assign(lhs, r, rest) => if usesStack(r) then super.applyResult(r): r => extract(r, false, _ => applyBlock(rest), lhs, curDepth) else super.applyBlock(b) case _ => super.applyBlock(b) override def applyHandler(hdr: Handler): Handler = lastWords("HandleBlock in stack safe transformation") override def applyResult(r: Result)(k: Result => Block): Block = if usesStack(r) then val tmp = TempSymbol(N, "res") Scoped(Set.single(tmp), extract(r, false, k, tmp, curDepth)) else super.applyResult(r)(k) override def applyLam(lam: Lambda): Lambda = lastWords("Lambda in stack safe transformation") transform.applyBlock(b) def isTrivial(b: Block): Boolean = var trivial = true new BlockTraverserShallow: applyBlock(b) override def applyResult(r: Result): Unit = r match case Call(Value.Ref(_: BuiltinSymbol, _), _) => () case _: Call | _: Instantiate => trivial = false case _ => () trivial def rewriteCls(defn: ClsLikeDefn, isTopLevel: Bool): ClsLikeDefn = defn.parentPath match case Some(value) if value eq paths.contClsPath => defn case _ => val ClsLikeDefn(owner, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) = defn ClsLikeDefn( owner, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods.map(rewriteFn), privateFields, publicFields, preCtor, ctor, mod.map(rewriteObjBody(_, isTopLevel)), bufferable, )(defn.configOverride, defn.annotations) def rewriteObjBody(defn: ClsLikeBody, isTopLevel: Bool): ClsLikeBody = ClsLikeBody( defn.isym, defn.methods.map(rewriteFn), defn.privateFields, defn.publicFields, if isTopLevel then if config.effectHandlers.exists(_.doNotInstrumentTopLevelModCtor) then defn.ctor else transformTopLevel(defn.ctor) else rewriteBlk(defn.ctor, R(defn.isym)), defn.annotations, ) // fnOrCls points us to the doUnwind function def rewriteBlk(blk: Block, fnOrCls: FnOrCls) = (stackSafetyMap.get(fnOrCls), isTrivial(blk)) match case (S((increment, doUnwindBlk)), false) => var usedDepth = false lazy val curDepth = usedDepth = true TempSymbol(None, "curDepth") val newBody = transform(blk, curDepth) val resSym = TempSymbol(None, "stackDelayRes") val addStackSafeEffect = blk => blockBuilder .assignFieldN(runtimePath, STACK_DEPTH_IDENT, op("+", stackDepthPath, intLit(increment))) .staticif(usedDepth, _.assignScoped(curDepth, stackDepthPath)) .assignScoped(resSym, Call(checkDepthPath, Nil ne_:: Nil)(true, true, false)) .ifthen( paths.curEffect, Case.Lit(Tree.UnitLit(true)), End(), S(doUnwindBlk) ) .rest(blk) addStackSafeEffect(newBody) case _ => blk def rewriteFn(defn: FunDefn) = FunDefn(defn.owner, defn.sym, defn.dSym, defn.params, rewriteBlk(defn.body, L(defn.sym)))(defn.configOverride, defn.annotations) def transformTopLevel(b: Block) = transform(b, TempSymbol(N), true) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/SymbolRefresher.scala ================================================ package hkmc2 package codegen import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer} import mlscript.utils.*, shorthands.* import hkmc2.utils.* import semantics.* import semantics.Elaborator.State class SymbolRefresher(existingMapping: Map[Symbol, Symbol])(using State) extends BlockTransformer(SymbolSubst.Id): val mapping = MutMap.from(existingMapping) // Stack of cleanup frames, one per scoped block. ClsLikeDefn / applyObjBody add // class-internal symbols to the top frame; the enclosing applyScopedBlock pops // and removes them from `mapping` when its body finishes. Initialized with a // bottom frame so callers that hand us a non-Scoped entry block still have a // valid `cleanupStack.head` to add to (the bottom frame is never popped). private var toRemoveSymbols: List[MutSet[Symbol]] = MutSet.empty[Symbol] :: Nil override def applyScopedBlock(b: Block): Block = toRemoveSymbols = MutSet.empty[Symbol] :: toRemoveSymbols val res = b match case Scoped(syms, body) => val newSyms = MutSet.empty[Symbol] val oldSyms = MutSet.empty[Symbol] for s <- syms.toList.sortBy(_.uid) do assert(!mapping.isDefinedAt(s), s"already defined: $s") val newS = s match case tmpSym: TempSymbol => new TempSymbol(N, tmpSym.nme) case bms: BlockMemberSymbol => val newBms = new BlockMemberSymbol(bms.nme, Nil, bms.nameIsMeaningful) newBms.tsym = bms.tsym.map: t => val newOwner: Opt[InnerSymbol] = t.owner.map: o => existingMapping.get(o) match case Some(inner: InnerSymbol) => inner case _ => o val nt = new TermSymbol(t.k, newOwner, t.id) mapping(t) = nt oldSyms.add(t) nt newBms case varSym: VarSymbol => new VarSymbol(varSym.id) case _ => lastWords(s"unexpected symbol kind: $s") mapping(s) = newS oldSyms.add(s) newSyms.add(newS) val r = Scoped(newSyms, applyBlock(body)) for s <- oldSyms do mapping.remove(s) r case _ => super.applyScopedBlock(b) val hd = toRemoveSymbols.head toRemoveSymbols = toRemoveSymbols.tail hd.foreach(mapping.remove) res override def applyBlock(b: Block): Block = b match case Assign(lhs, rhs, rest) => applyResult(rhs): newRhs => val newLhs = mapping.getOrElse(lhs, lhs) val newRest = applyBlock(rest) if (newLhs is lhs) && (newRhs is rhs) && (newRest is rest) then b else Assign(newLhs, newRhs, newRest) case Label(label, loop, body, rest) => assert(!mapping.isDefinedAt(label)) val newLabel = new LabelSymbol(label.trm, label.nme) mapping(label) = newLabel val newBody = applyBlock(body) mapping.remove(label) val newRest = applyBlock(rest) Label(newLabel, loop, newBody, newRest) case Break(label) => Break(mapping.getOrElse(label, label).asInstanceOf[LabelSymbol]) case Continue(label) => Continue(mapping.getOrElse(label, label).asInstanceOf[LabelSymbol]) case _ => super.applyBlock(b) override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case fun: FunDefn => assert(fun.owner.isEmpty) // because fun sym is not treated as a free var, we refresh here var newlyCreated = false val (sym2, dSym2) = mapping.get(fun.sym) match case Some(s: BlockMemberSymbol) => (s, s.tsym.get) case None => newlyCreated = true val newBms = new BlockMemberSymbol(fun.sym.nme, fun.sym.trees, fun.sym.nameIsMeaningful) val newDsym = fun.sym.tsym.map: tsym => assert(tsym.owner.isEmpty) new TermSymbol(tsym.k, N, tsym.id) newBms.tsym = S(newDsym.get) mapping(fun.sym) = newBms (newBms, newDsym.get) case _ => die val oldParamSyms = Buffer.empty[VarSymbol] val params2 = fun.params.map: case ParamList(flags, params, restParam) => def handleSingleParam(p: Param) = val Param(flags, sym, sign, modulefulness) = p oldParamSyms.append(sym) val newSym = new VarSymbol(sym.id) assert(!mapping.isDefinedAt(sym)) mapping(sym) = newSym Param(flags, newSym, sign, modulefulness) val params2 = params.map(handleSingleParam) val rest2 = restParam.map(handleSingleParam) ParamList(flags, params2, rest2) val body2 = applyFunBodyLikeBlock(fun.body) for s <- oldParamSyms do mapping.remove(s) if newlyCreated then Scoped(Set.single(sym2), k(FunDefn(N, sym2, dSym2, params2, body2)(fun.configOverride, fun.annotations))) else k(FunDefn(N, sym2, dSym2, params2, body2)(fun.configOverride, fun.annotations)) case defn @ ValDefn(tsym, sym, rhs) => val (tsym2, sym2) = mapping.get(sym) match case None => val newBms = new BlockMemberSymbol(sym.nme, sym.trees, sym.nameIsMeaningful) val newTsym = new TermSymbol(tsym.k, tsym.owner, tsym.id) newBms.tsym = S(newTsym) (newTsym, newBms) case S(bms: BlockMemberSymbol) => (bms.tsym.get, bms) case _ => die applyPath(rhs): rhs2 => k(ValDefn(tsym2, sym2, rhs2)(defn.configOverride, defn.annotations)) case defn: ClsLikeDefn => val hd = toRemoveSymbols.head val oldIsym = defn.isym assert(!mapping.isDefinedAt(oldIsym), s"isym already in mapping: $oldIsym") val newIsym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol = oldIsym match case c: ClassSymbol => new ClassSymbol(c.tree, c.id) case m: ModuleOrObjectSymbol => new ModuleOrObjectSymbol(m.tree, m.id) case p: PatternSymbol => new PatternSymbol(p.id, p.params, p.body) case _ => lastWords(s"unexpected isym kind: $oldIsym") mapping(oldIsym) = newIsym hd += oldIsym var newlyCreatedSym = false val newSym: BlockMemberSymbol = mapping.get(defn.sym) match case Some(bms: BlockMemberSymbol) => bms case None => newlyCreatedSym = true val nb = new BlockMemberSymbol(defn.sym.nme, Nil, defn.sym.nameIsMeaningful) mapping(defn.sym) = nb nb case _ => die val newCtorSym: Opt[TermSymbol] = defn.ctorSym.map: cs => assert(!mapping.isDefinedAt(cs)) val ncs = new TermSymbol(cs.k, S(newIsym), cs.id) mapping(cs) = ncs hd += cs ncs val newOwn: Opt[InnerSymbol] = defn.owner.map: o => mapping.get(o) match case Some(inner: InnerSymbol) => inner case _ => o def freshenParamList(pl: ParamList): ParamList = def handleParam(p: Param) = val ns = new VarSymbol(p.sym.id) assert(!mapping.isDefinedAt(p.sym)) mapping(p.sym) = ns hd += p.sym Param(p.flags, ns, p.sign, p.modulefulness) ParamList(pl.flags, pl.params.map(handleParam), pl.restParam.map(handleParam)) val newParamsOpt = defn.paramsOpt.map(freshenParamList) val newAuxParams = defn.auxParams.map(freshenParamList) val newPrivateFields = freshenPrivateFields(defn.privateFields, newIsym) val newPublicFields = freshenPublicFields(defn.publicFields, newIsym) val newMethods = freshenMethods(defn.methods, oldIsym, newIsym) val newPreCtor = applyFunBodyLikeBlock(defn.preCtor) val newCtor = applyFunBodyLikeBlock(defn.ctor) val newMod = defn.companion.map(applyObjBody) def buildResult(newParentPath: Opt[Path]): Block = val newDefn = ClsLikeDefn(newOwn, newIsym, newSym, newCtorSym, defn.k, newParamsOpt, newAuxParams, newParentPath, newMethods, newPrivateFields, newPublicFields, newPreCtor, newCtor, newMod, defn.bufferable)(defn.configOverride, defn.annotations) if newlyCreatedSym then Scoped(Set.single(newSym), k(newDefn)) else k(newDefn) defn.parentPath match case Some(pp) => applyPath(pp): pp2 => buildResult(if pp2 is pp then defn.parentPath else Some(pp2)) case None => buildResult(None) override def applyPath(p: Path)(k: Path => Block): Block = p match case p @ Select(qual, name) => applyPath(qual): qual2 => val sym2 = p.symbol.map: s => mapping.get(s) match case Some(ds: DefinitionSymbol[?]) => ds case _ => s k(if (qual2 is qual) && (sym2 is p.symbol) then p else Select(qual2, name)(sym2).withLocOf(p)) case _ => super.applyPath(p)(k) override def applyValue(v: Value)(k: Value => Block): Block = v match case Value.Ref(l, x) => mapping.get(l) match case None => super.applyValue(v)(k) case Some(newBms: BlockMemberSymbol) => val newDisamb = x match case Some(oldDisamb) => mapping.get(oldDisamb) match case Some(nd: DefinitionSymbol[?]) => Some(nd) case _ => newBms.tsym.orElse(x) case None => newBms.tsym k(Value.Ref(newBms, newDisamb)) case Some(newSym) => k(Value.Ref(newSym, N)) case Value.This(sym) => mapping.get(sym) match case Some(inner: InnerSymbol) => k(Value.This(inner).withLocOf(v)) case _ => super.applyValue(v)(k) case _ => super.applyValue(v)(k) private def freshenPrivateFields( fields: Ls[TermSymbol], ownerIsym: InnerSymbol ): Ls[TermSymbol] = fields.map: ts => assert(!mapping.isDefinedAt(ts)) val nts = new TermSymbol(ts.k, S(ownerIsym), ts.id) mapping(ts) = nts toRemoveSymbols.head += ts nts private def freshenPublicFields( fields: Ls[BlockMemberSymbol -> TermSymbol], ownerIsym: InnerSymbol ): Ls[BlockMemberSymbol -> TermSymbol] = fields.map: case (bms, ts) => assert(!mapping.isDefinedAt(bms)) assert(!mapping.isDefinedAt(ts)) val nbms = new BlockMemberSymbol(bms.nme, Nil, bms.nameIsMeaningful) val nts = new TermSymbol(ts.k, S(ownerIsym), ts.id) nbms.tsym = S(nts) mapping(bms) = nbms mapping(ts) = nts toRemoveSymbols.head.addAll(Seq(bms, ts)) nbms -> nts private def freshenMethods( methods: Ls[FunDefn], oldIsym: InnerSymbol, newIsym: InnerSymbol ): Ls[FunDefn] = val methodsAndNewSyms = methods.map: m => assert(m.owner.contains(oldIsym), s"method owner mismatch: ${m.owner} vs S($oldIsym)") assert(!mapping.isDefinedAt(m.sym)) assert(!mapping.isDefinedAt(m.dSym)) val newMsym = new BlockMemberSymbol(m.sym.nme, Nil, m.sym.nameIsMeaningful) val newDsym = new TermSymbol(m.dSym.k, S(newIsym), m.dSym.id) newMsym.tsym = S(newDsym) mapping(m.sym) = newMsym mapping(m.dSym) = newDsym toRemoveSymbols.head.addAll(Seq(m.sym, m.dSym)) (m, newMsym, newDsym) methodsAndNewSyms.map: (m, newMsym, newDsym) => val methodParamOlds = MutSet.empty[VarSymbol] val newParams = m.params.map: pl => def handleParam(p: Param) = val ns = new VarSymbol(p.sym.id) assert(!mapping.isDefinedAt(p.sym)) mapping(p.sym) = ns methodParamOlds += p.sym Param(p.flags, ns, p.sign, p.modulefulness) ParamList(pl.flags, pl.params.map(handleParam), pl.restParam.map(handleParam)) val newBody = applyFunBodyLikeBlock(m.body) methodParamOlds.foreach(mapping.remove) FunDefn(S(newIsym), newMsym, newDsym, newParams, newBody)(m.configOverride, m.annotations) override def applyObjBody(defn: ClsLikeBody): ClsLikeBody = val hd = toRemoveSymbols.head val oldIsym = defn.isym assert(!mapping.isDefinedAt(oldIsym), s"companion isym already in mapping: $oldIsym") val newIsym: DefinitionSymbol[? <: ModuleOrObjectDef] & InnerSymbol = oldIsym match case m: ModuleOrObjectSymbol => new ModuleOrObjectSymbol(m.tree, m.id) case _ => lastWords(s"unexpected companion isym kind: $oldIsym") mapping(oldIsym) = newIsym hd += oldIsym val newPrivateFields = freshenPrivateFields(defn.privateFields, newIsym) val newPublicFields = freshenPublicFields(defn.publicFields, newIsym) val newMethods = freshenMethods(defn.methods, oldIsym, newIsym) val newCtor = applyFunBodyLikeBlock(defn.ctor) ClsLikeBody(newIsym, newMethods, newPrivateFields, newPublicFields, newCtor, defn.annotations) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.Message.* import hkmc2.semantics.Elaborator.State import hkmc2.syntax.{Tree, SpreadKind} import hkmc2.ScopeData.* import hkmc2.Lifter.AccessInfo import scala.collection.mutable.ArrayBuffer import java.lang.instrument.ClassDefinition /* DOCUMENTATION OF SEMANTICS OF @tailcall and @tailrec @tailcall: Used to annotate specific function calls. Calls annotated with @tailcall must be tail calls. These calls must be optimized to not consume additional stack space. If such an optimization is not possible, then the compiler will report an error. @tailrec: Used to annotate functions. When this annotation is used on a function, say `@tailrec fun foo()`, the compiler will ensure no sequence of statically known recursive calls back to foo() consumes stack space, i.e. they are all tail calls. For example, @tailrec fun foo() = bar() foo() fun bar() = bar() bar() is valid. However, @tailrec fun foo() = bar() fun bar() = foo() bar() is invalid. If we swap the position of foo() and bar() in the body of bar, i.e. @tailrec fun foo() = bar() fun bar() = bar() foo() it is still invalid, since the following sequence of calls from foo to foo would incur extra stack space: foo -> bar (tail call) -> bar (not a tail call) -> foo (tail call) Equivalently, if fun foo() is annotated with @tailrec, let S be the largest strongly connected component in the call-graph of the program that contains foo. Then an error will be thrown unless all edges (calls) connecting the nodes of the strongly connected component are tail calls. */ // This optimization assumes the lifter has been run. class TailRecOpt(using State, TL, Raise): type AccessMap = Map[ScopedInfo, AccessInfo] object CallToFun: def unapply(c: Call): Opt[TermSymbol] = c match case Call(fun = Value.Ref(b, S(r: TermSymbol))) => S(r) case Call(fun = s: Select) => s.symbol match case Some(r: TermSymbol) => S(r) case _ => N case _ => N object TailCallShape: def unapply(b: Block): Opt[(TermSymbol, Call)] = b match case Return(c @ CallToFun(r), _) => S((r, c)) case Assign(a, c @ CallToFun(r), Return(Value.Ref(b, _), _)) if a === b => S((r, c)) case _ => N enum CallEdge: val f1: TermSymbol val f2: TermSymbol val call: Call case TailCall(f1: TermSymbol, f2: TermSymbol)(val call: Call) case NormalCall(f1: TermSymbol, f2: TermSymbol)(val call: Call) class CallFinder(f: FunDefn)(using data: (ScopeData, AccessMap)) extends BlockTraverserShallow: val scopeData = data._1 var edges: List[CallEdge] = Nil def find = edges = Nil applyBlock(f.body) edges def getFun(d: TermSymbol) = if scopeData.contains(d) then scopeData.getNode(d) match case ScopeNode(obj = ScopedObject.Func(f, _)) => S(f) case _ => N else N override def applyBlock(b: Block): Unit = b match case TailCallShape(r, c) => getFun(r) match case Some(value) => if c.argss.size != value.params.size then if c.explicitTailCall then raise(ErrorReport(msg"Only fully applied calls may be marked @tailcall." -> c.toLoc :: Nil)) else edges ::= CallEdge.TailCall(f.dSym, r)(c) case None => if c.explicitTailCall then raise(ErrorReport(msg"Only functions in this compilation unit may be marked @tailcall." -> c.toLoc :: Nil)) case Return(c: Call, _) => if c.explicitTailCall then raise(ErrorReport(msg"Only direct calls in tail position may be marked @tailcall." -> c.toLoc :: Nil)) case _ => super.applyBlock(b) override def applyResult(r: Result): Unit = r match case c: Call => if c.explicitTailCall then raise(ErrorReport(msg"This call is not in tail position." -> c.toLoc :: Nil)) c match case CallToFun(r) => edges ::= CallEdge.NormalCall(f.dSym, r)(c) case _ => case _ => super.applyResult(r) def buildCallGraph(fs: List[FunDefn])(using (ScopeData, AccessMap)): List[CallEdge] = fs.flatMap(f => CallFinder(f).find) case class SccOfCalls(funs: List[FunDefn], calls: List[CallEdge]) def partFns(fs: List[FunDefn])(using (ScopeData, AccessMap)): List[SccOfCalls] = val defnSyms = fs.map(_.dSym) val tsToDefn = fs.map(f => f.dSym -> f).toMap // Only care about calls to functions in the same scope // Note that the results may differ if the lifter has been run. val cg = buildCallGraph(fs).filter: c => val cond = defnSyms.contains(c.f1) && defnSyms.contains(c.f2) c.match case c: CallEdge.TailCall if c.call.explicitTailCall && !cond => raise(ErrorReport( msg"This tail call exits the current scope and is not optimized." -> c.call.toLoc :: Nil)) case _ => cond val cgTup = cg.map(c => (c.f1, c.f2)) val sccs = algorithms.sccsWithInfo(cgTup, defnSyms) // partition the call graph val sccMap = sccs.sccs.flatMap: case (id, scc) => scc.map(f => f -> id) val cgLabelled = cg .groupBy: c => val s1 = sccMap(c.f1) val s2 = sccMap(c.f2) if s1 =/= s2 && c.call.explicitTailCall then raise(ErrorReport( msg"This call is not optimized as it does not directly recurse through its parent function." -> c.call.toLoc :: Nil)) -1 else s1 .filter: (id, _) => id =/= -1 sccs.sccs.toList.map: v => val (id, tss) = v val cgs = cgLabelled.get(id) match case Some(value) => value case None => Nil SccOfCalls(tss.map(tsToDefn), cgs) def maxInt[T](items: List[T], f: T => Int): Int = items.foldLeft(0): case (l, item) => val x = f(item) if x > l then x else l def getParamSyms(f: FunDefn) = f.params.flatMap: case ParamList(_, params, S(rest)) => params.map(_.sym).appended(rest.sym) case p => p.params.map(_.sym) def paramsLen(f: FunDefn): Int = f.params.foldLeft(0): (acc, head) => acc + (if head.restParam.isDefined then 1 + head.params.length else head.params.length) // Success: This arg list was successfully transformed. The args may be blindly assigned to the // parameters in order. // ForceSpread: This arg list may be rewritten, but contains spread parameters for which must use a tuple // to correctly extract the arguments correctly. private enum CallArgsResult: case Success(res: List[Result]) case ForceSpread // Tries to rewrite an application of an arg list to a param list without needing to forcibly // unspread the arguments. private def rewriteArgsList(f: FunDefn, paramList: ParamList, args: List[Arg]): CallArgsResult = val (headArgs, restArgs) = paramList.restParam match case Some(value) => args.splitAt(paramList.params.length) case None => (args, Nil) var hasSpread = false // If any args have a spre val hd = for a <- headArgs yield a.spread match case Some(SpreadKind.Eager) => hasSpread = true a.value case Some(SpreadKind.Lazy) => lastWords("Lazys pread in arguments") case _ => a.value if hasSpread then return CallArgsResult.ForceSpread if paramList.restParam.isDefined then val rest = restArgs match case Arg(S(SpreadKind.Eager), value) :: Nil => value case _ => Tuple(true, restArgs) CallArgsResult.Success(hd.appended(rest)) else CallArgsResult.Success(hd) def optScc(scc: SccOfCalls, owner: Opt[InnerSymbol])(using accessInfo: (ScopeData, AccessMap)): (Opt[FunDefn], List[FunDefn]) = // sort the functions so the order is more predictable val funs = scc.funs.sortBy(f => f.dSym.uid) val fSyms = funs.map(_.dSym).toSet val funsMap = funs.map: f => f.dSym -> f .toMap // remove calls which don't flow into this scc val calls = scc.calls.filter(c => fSyms.contains(c.f2)) val nonTailCallsLs = calls.collect: case c: CallEdge.NormalCall => c.f2 -> c.call val nonTailCalls = nonTailCallsLs.toMap if nonTailCallsLs.sizeCompare(calls) === 0 then for f <- funs if f.forceTailRec do raise(WarningReport(msg"This function does not directly self-recurse, but is marked @tailrec." -> f.dSym.toLoc :: Nil)) return (N, funs) if !nonTailCalls.isEmpty then for f <- funs if f.forceTailRec do val reportLoc = nonTailCalls.get(f.dSym) match // always display a call to f, if possible case Some(value) => value.toLoc case None => nonTailCalls.head._2.toLoc raise(ErrorReport( msg"This function is not tail recursive." -> f.dSym.toLoc :: msg"It could self-recurse through this call, which is not a tail call." -> reportLoc :: Nil )) val maxParamLen = maxInt(funs, paramsLen) val paramSyms = if funs.length === 1 then (getParamSyms(funs.head)) else for i <- 0 until maxParamLen yield VarSymbol(Tree.Ident("param" + i)) .toList val paramSymsArr = ArrayBuffer.from(paramSyms) // Function -> param -> param symbol in the rewritten function val paramSymsMap: Map[TermSymbol, Map[VarSymbol, VarSymbol]] = funs.map: f => val flattenedSyms = f.params.flatMap(_.paramSyms) val mp = flattenedSyms.zipWithIndex.map: case (l, i) => l -> paramSymsArr(i) .toMap f.dSym -> mp .toMap val dSymIds = funs.map(_.dSym).zipWithIndex.toMap val dSymToDefn = funs.map(f => f.dSym -> f).toMap val bms = if funs.size === 1 then funs.head.sym else BlockMemberSymbol(funs.map(_.sym.nme).mkString("_"), Nil, true) val dSym = if funs.size === 1 then funs.head.dSym else TermSymbol(syntax.Fun, owner, Tree.Ident(bms.nme)) val loopSym = LabelSymbol(N, "loopLabel") val curIdSym = VarSymbol(Tree.Ident("id")) class FunRewriter(f: FunDefn) extends BlockTransformerShallow(SymbolSubst.Id): val params = getParamSyms(f) val paramsSet = f.params.toSet val paramsIdxes = params.zipWithIndex.toMap // Certain variables must be re-defined in the tailrec loop // if they are captured by nested definitions or lambdas. Otherwise, // when they are mutated by a tailrec call, the nested definitions // would capture the mutated variable rather than the one defined // in the original call. See https://github.com/hkust-taco/mlscript/issues/415 val copiedParams: Set[VarSymbol] = // scopeData: A class that wraps a tree describing the scoping relation in the IR. Each node is // an object that introduces a scope, which could be a scoped block, function, class, etc. // A node's children represent that scope's nested scopes, functions, classes, etc. // accessMap: Maps scopes to the variables that they could access, either directly or by entering // another scope. For functions, that would be the variables they could access. val (scopeData, accessMap) = accessInfo val node = scopeData.getNode(f) // Finds the immediate child functions/classes of the the function `f`. val (_, childDefNodes) = node.partitionTree2: case _: ScopedObject.Class => true case _: ScopedObject.Func => true case _ => false childDefNodes.iterator.map(_.obj) .collect: case r: ScopedObject.Referencable[?] => r.sym // Obtains the definition symbol of the nested class/function. .flatMap(s => accessMap(s).accessed) // All local variables that each nested class/function could access. .collect: case x: VarSymbol => x .filter(params.toSet) .toSet val copiedParamSyms = copiedParams.map: x => x -> VarSymbol(x.id) .toMap val subst = new SymbolSubst: override def mapVarSym(l: VarSymbol): VarSymbol = copiedParamSyms.getOrElse( l, paramsIdxes.get(l) match case Some(idx) => paramSymsArr(idx) case _ => l ) val symRewriter = new BlockTransformer(subst) override def applyBlock(b: Block): Block = b match // Note: the args in `c` have already been rewritten to point to the symbols in `paramSyms`. case TailCallShape(calleeSym, c) => dSymIds.get(calleeSym) match case None => super.applyBlock(b) case Some(id) => val callee = dSymToDefn(calleeSym) val calleeParamsMap = paramSymsMap(callee.dSym) // We require the call to be fully applied. if c.argss.size != callee.params.size then super.applyBlock(b) else // The code used to continute the loop. val cont = if scc.funs.size === 1 then Continue(loopSym) else Assign(curIdSym, Value.Lit(Tree.IntLit(dSymIds(calleeSym))), Continue(loopSym)) // In some cases, we could have assignments like this: // param0 = whatever // param1 = // which means param1's value is incorrect. // We should thus assign the params to temporary symbols // if they are needed for a subsequent assignment. var assignedSyms: Map[VarSymbol, Lazy[TempSymbol]] = paramSyms.map: sym => sym -> Lazy(TempSymbol(N, sym.nme + "_tmp")) // Use `Lazy` to avoid generating useless symbols .toMap var requiredTmps: Set[(VarSymbol, TempSymbol)] = Set.empty // Rewrites references to mutated parameters to point to a temp variable instead, and // reports that the parameter's value must be assigned to that temp variable // before assigning to the parameters of this arg list. val paramRewriter = new BlockDataTransformer(SymbolSubst.Id): override def applyValue(v: Value)(k: Value => Block): Block = v match // `assignedSyms` contains all of the param symbols that have been assigned to // before the current assignment, and thus references to them must be rewritten // to point to a temporary variable. case Value.Ref(l: VarSymbol, disamb) => assignedSyms.get(l) match case S(v) => val tmpSym = v.force_! // Adding this to `requiredTmps` will make sure we set the temporary variable // to the current variable at the start of the rewritten call. requiredTmps += (l, tmpSym) k(Value.Ref(tmpSym, disamb)) case _ => super.applyValue(v)(k) case _ => super.applyValue(v)(k) val argListResults = callee.params.zip(c.argss).map: case (params, args) => (params, params.paramSyms.map(calleeParamsMap), args, rewriteArgsList(callee, params, args)) // Prevent stupid assignments like `set a = a` val selfAssigns = argListResults.flatMap: (_, thisParamSyms, args, argsRes) => argsRes match case CallArgsResult.Success(res) => thisParamSyms.zip(res).collect: case (sym1, Value.Ref(sym2, _)) if sym1 === sym2 => sym1 case CallArgsResult.ForceSpread => List.empty assignedSyms --= selfAssigns // Algorithm: Apply the args from right to left, but have the resulting assignment order // be left to right. // // When applying each arg, keep track of the parameters that must be assigned to be a // temporary variable. Also, remove each assigned parameter from `assignedSyms` after assigning // them, so that assignments coming before them will not mistakenly add the param syms from // future assignments to `requiredTmps`. val assignments: Block = argListResults.foldRight(cont): case ((ogParamList, thisParamSyms, ogArgs, argRes), rest) => argRes match case CallArgsResult.Success(argVals) => // Remove symbols from assignedSyms as we encounter them. // Note that foldRight will call the function right to left thisParamSyms.zip(argVals).foldRight[Block](rest): (v, acc) => val (sym, res) = v assignedSyms -= sym // Rewrite the result with symbols pointing to the temporary variables as described above. // Note that we already rewrote the result with symbols pointing to the merged function parameters // in `rewrite`. Also note that `paramRewriter` will add all encountered rewritten variables // to `requiredTmps`. val ret = paramRewriter.applyResult(res)(Assign(sym, _, acc)) match case Assign(sym, Value.Ref(sym1, _), rest) if sym === sym1 => rest // avoid useless assignments case x => x ret case CallArgsResult.ForceSpread => // Forcibly spread the args in an array. // Assume the lengths are correct val paramList = ogParamList.params val restParam = ogParamList.restParam val tupleSym = TempSymbol(N, "argList") // We can safely remove all of the symbols from this parameter list from `assignedSyms` at this stage, // because the RHS of every parameter will be computed when spreading them in the tuple, which happens // before any of the param symbols are assigned to. assignedSyms --= thisParamSyms paramRewriter.applyArgs(ogArgs): newArgs => val tupleRes = Tuple(false, newArgs) // Main args def mainArgs(rest: List[Path]) = (0 until paramList.size).toList.foldRight(rest): case (n, acc) => DynSelect(tupleSym.asPath, Value.Lit(Tree.IntLit(n)), true) :: acc // If the rest param exists, append a slice val (initialBlk: (Block => Block), pathList: List[Path]) = if restParam.isDefined then val sliceResSym = TempSymbol(N, "sliceRes") // runtime.Tuple.slice(tupleSym, paramList.length, 0) val sliceRes = Call( State.runtimeSymbol.asPath .sel(Tree.Ident("Tuple"), State.tupleSymbol) .sel(Tree.Ident("slice"), State.tupleSliceSymbol), (tupleSym.asPath.asArg :: Value.Lit(Tree.IntLit(paramList.length)).asArg :: Value.Lit(Tree.IntLit(0)).asArg :: Nil) ne_:: Nil )(true, false, false) val blk = blockBuilder .assignScoped(tupleSym, tupleRes) .assignScoped(sliceResSym, sliceRes) (blk, mainArgs(sliceResSym.asPath :: Nil)) else (blockBuilder.assignScoped(tupleSym, tupleRes), mainArgs(Nil)) end val val paramAssignments = (thisParamSyms zip pathList).foldRight[Block](rest): case ((sym, path), restBlk) => Assign(sym, path, restBlk) initialBlk.rest(paramAssignments) end assignments // Bind the tmps Scoped( requiredTmps.values.toSet, requiredTmps.toList.foldRight(assignments): case ((v, l), acc) => Assign(l, Value.Ref(v), acc)) // Not a tail call case _ => super.applyBlock(b) def rewrite(b: Block): Block = // Rewrite the result with symbols pointing to the merged function parameters and possibly the copied parameters (see `copiedParams`). val blk = applyBlock(symRewriter.applyBlock(b)) val withCopied = copiedParamSyms.toArray.sortBy(_._1.uid).foldRight(blk): case ((ogParam, copiedParam), accBlk) => Assign(copiedParam, paramSymsArr(paramsIdxes(ogParam)).asPath, accBlk) Scoped(copiedParamSyms.map(_._2).toSet, withCopied) val arms = funs.map: f => Case.Lit(Tree.IntLit(dSymIds(f.dSym))) -> FunRewriter(f).rewrite(f.body) val switch = if arms.length === 1 then arms.head._2 else Match(curIdSym.asPath, arms, N, End()) val loop = Label(loopSym, true, switch, End()) val sel = owner match case Some(value) => Select(Value.Ref(value, N), Tree.Ident(bms.nme))(S(dSym)) case None => Value.Ref(bms, S(dSym)) val rewrittenFuns = if funs.size === 1 then Nil else funs.map: f => val paramArgs = getParamSyms(f).map(_.asPath.asArg) val args = Value.Lit(Tree.IntLit(dSymIds(f.dSym))).asArg :: paramArgs ::: List.fill(maxParamLen - paramArgs.length)(Value.Lit(Tree.UnitLit(false)).asArg) val newBod = Return( Call(sel, args ne_:: Nil)(true, false, false), false ) FunDefn(f.owner, f.sym, f.dSym, f.params, newBod)(N, f.annotations) val params = val initial = paramSyms.map(Param.simple(_)) if funs.length === 1 then initial else Param.simple(curIdSym) :: initial val loopDefn = FunDefn( owner, bms, dSym, PlainParamList(params) :: Nil, loop)(N, annotations = Nil) // Q: maybe should be Private? if funs.size === 1 then val f = funs.head if f.params.length > 1 then // When a function has multiple param lists, TailRecOpt flattens them into a single // param list for the internal loop. We need a wrapper function that preserves the // original multi-param-list interface and delegates to the flattened internal loop. val loopBms = BlockMemberSymbol(bms.nme + "$tailrec", Nil, true) val loopDSym = TermSymbol(syntax.Fun, owner, Tree.Ident(loopBms.nme)) val internalLoopDefn = FunDefn( owner, loopBms, loopDSym, PlainParamList(params) :: Nil, loop)(N, annotations = Annot.Private :: Nil) val paramArgs = getParamSyms(f).map(_.asPath.asArg) val internalSel = owner match case Some(value) => Select(Value.Ref(value, N), Tree.Ident(loopBms.nme))(S(loopDSym)) case None => Value.Ref(loopBms, S(loopDSym)) val wrapperBod = Return( Call(internalSel, paramArgs ne_:: Nil)(true, false, false), false ) val wrapperDefn = FunDefn(f.owner, f.sym, f.dSym, f.params, wrapperBod)( f.configOverride, annotations = f.annotations) (S(internalLoopDefn), wrapperDefn :: Nil) else (N, loopDefn :: Nil) else (S(loopDefn), rewrittenFuns) def optFunctions(fs: List[FunDefn], owner: Opt[InnerSymbol])(using (ScopeData, AccessMap)) = val (newFsOpt, fsOpt) = partFns(fs).map(optScc(_, owner)).foldLeft[(List[FunDefn], List[FunDefn])](Nil, Nil): case ((newFns, fns), (newFnOpt, fns_)) => newFnOpt match case Some(value) => (value :: newFns, fns_ ::: fns) case None => (newFns, fns_ ::: fns) // preserve the order of function defns val fMap = fsOpt.map(f => (f.dSym, f)).toMap val fsRet = fs.map(f => fMap(f.dSym)) (newFsOpt, fsRet) def reportClassesTailrec(c: ClsLikeDefn) = new BlockTraverserShallow(): for f <- c.methods do applyBlock(f.body) if f.forceTailRec then raise(ErrorReport(msg"Class methods may not yet be marked @tailrec." -> f.dSym.toLoc :: Nil)) override def applyResult(r: Result): Unit = r match case c: Call if c.explicitTailCall => raise(ErrorReport(msg"Calls from class methods cannot yet be marked @tailcall." -> c.toLoc :: Nil)) case _ => super.applyResult(r) def optFunctionsFlat(fs: List[FunDefn], owner: Opt[InnerSymbol])(using (ScopeData, AccessMap)) = val (a, b) = optFunctions(fs, owner) a ::: b def optClasses(cs: List[ClsLikeDefn])(using (ScopeData, AccessMap)) = cs.map: c => // Class methods cannot yet be optimized as they cannot yet be marked final. if c.k is syntax.Cls then reportClassesTailrec(c) val companion = c.companion.map: comp => val cMtds = optFunctionsFlat(comp.methods, S(comp.isym)) comp.copy(methods = cMtds) c.copy(companion = companion)(c.configOverride, c.annotations) else val mtds = optFunctionsFlat(c.methods, S(c.isym)) val companion = c.companion.map: comp => val cMtds = optFunctionsFlat(comp.methods, S(comp.isym)) comp.copy(methods = cMtds) c.copy(methods = mtds, companion = companion)(c.configOverride, c.annotations) def transform(b: Block) = /* To avoid `x` being overridden in the following when the lifter is not run: * * let lam * fun f(x) = * set lam = () => x * f(x + 1) * * we need to do some analysis on what nested functions use what variables. We * re-use the analysis from the lifter to do this. */ given (ScopeData, AccessMap) = // IgnoredScoes can be an empty set, since that information is only relevant for lifting given IgnoredScopes = IgnoredScopes(S(Set.empty)) val scopeData = ScopeData(b) val analyzer = new UsedVarAnalyzer(b, scopeData) (scopeData, analyzer.accessMapWithIgnored) val defns = b.gatherDefns() val (funs, clses) = defns.partitionMap: case f: FunDefn => L(f) case c: ClsLikeDefn => R(c) case _ => die // unreachable as floatOutDefns only floats out FunDefns and ClsLikeDefns // Filter out functions that have a @config annotation disabling tailRecOpt val (tailRecFuns, _) = funs.partition: f => f.configOverride.forall(_.tailRecOpt) val (optFNew, optF) = optFunctions(tailRecFuns, N) val optC = optClasses(clses) val fMap = optF.map(f => f.dSym -> f).toMap // Scala needs this annotation to type check for some reason val cMap: Map[DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, ClsLikeDefn] = optC.map(c => c.isym -> c).toMap // replace them in place val transformer = new BlockTransformerShallow(SymbolSubst.Id): override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case f: FunDefn => fMap.get(f.dSym) match case Some(value) => k(value) case None => k(f) case c: ClsLikeDefn => cMap.get(c.isym) match case Some(value) => k(value) case None => k(c) case _ => super.applyDefn(defn)(k) val result = Scoped( optFNew.map(_.sym).toSet, optFNew.foldLeft(transformer.applyBlock(b)): case (acc, f) => Define(f, acc)) // Report @tailrec on functions that weren't processed by the optimization above, // e.g. nested functions or functions with @config(tailRecOpt: false). // Class/module methods are handled separately by optClasses and are skipped here. val tailRecFunSyms = tailRecFuns.map(_.dSym).toSet new BlockTraverser: override def applyFunDefn(fun: FunDefn): Unit = if fun.forceTailRec && !tailRecFunSyms.contains(fun.dSym) then raise(ErrorReport( msg"This @tailrec function was not processed by the tail-call optimizer." -> fun.dSym.toLoc :: Nil)) super.applyFunDefn(fun) override def applyDefn(defn: Defn): Unit = defn match case _: ClsLikeDefn => () case _ => super.applyDefn(defn) .applyBlock(result) result ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.Message.* import hkmc2.semantics.Elaborator.State import hkmc2.ScopeData.* import hkmc2.Lifter.* import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.Buffer object UsedVarAnalyzer: case class MutAccessInfo( accessed: MutSet[Local], mutated: MutSet[Local], refdDefns: MutSet[ScopedInfo] ): def toIMut = AccessInfo(accessed.toSet, mutated.toSet, refdDefns.toSet) object MutAccessInfo: def empty = MutAccessInfo( MutSet.empty, MutSet.empty, MutSet.empty ) /** * Analyzes which variables have been used and mutated by which functions. * Also finds which variables can be passed to a capture class without a heap * allocation (during class lifting) despite being mutable. * * Assumes the input trees have no lambdas. */ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State): import UsedVarAnalyzer.* object SDSym: def unapply(v: DefinitionSymbol[?] | Option[DefinitionSymbol[?]]) = dSymUnapply(scopeData, v) private def isObj(s: ScopeNode) = s.obj match case c: ScopedObject.Class if c.isObj => true case _ => false // Finds the locals that this block accesses/mutates, and the definitions which it could use. private def blkAccessesShallow(b: Block): AccessInfo = var accessed: MutAccessInfo = MutAccessInfo.empty new BlockTraverserShallow: applyBlock(b) override def applyBlock(b: Block): Unit = b match case s: Scoped => accessed.refdDefns.add(scopeData.getUID(s)) case Assign(lhs, rhs, rest) => accessed.accessed.add(lhs) accessed.mutated.add(lhs) applyResult(rhs) applyBlock(rest) case l: Label if l.loop => accessed.refdDefns.add(l.label) case d: Define => d.defn match case v: ValDefn => applyDefn(v) applySubBlock(d.rest) case c @ ClsLikeDefn(k = syntax.Obj) => accessed.refdDefns.add(c.isym) case _ => applySubBlock(d.rest) case _ => super.applyBlock(b) override def applyPath(p: Path): Unit = p match case Value.Ref(_: BuiltinSymbol, _) => super.applyPath(p) case RefOfBms(_, SDSym(dSym), _) => val node = scopeData.getNode(dSym) node.obj match // Here, we add an edge to a definition, even if it is the result of a field selection, if it is: // - Lifted, but not an object // - Is a module method // - Is a ctor of a lifted class // Otherwise, we ignore the disambiguated symbol and traverse into the the selection's path. Once // we reach the "base" reference to an object, then we add a reference to that as required. case ScopedObject.Func(isMethod = S(MethodKind.ModMethod)) => accessed.refdDefns.add(node.obj.toInfo) super.applyPath(p) case ScopedObject.Func(isMethod = N) => accessed.refdDefns.add(node.obj.toInfo) super.applyPath(p) case _ if node.isLifted && !isObj(node) => accessed.refdDefns.add(node.obj.toInfo) case ScopedObject.ClassCtor(cls) if scopeData.getNode(cls).isLifted => accessed.refdDefns.add(node.obj.toInfo) case _ => p match case _: Value.Ref => node.obj match case c: ScopedObject.Class if c.isObj => accessed.accessed.add(c.cls.isym) case r: ScopedObject.Referencable[?] if !node.isLifted => accessed.refdDefns.add(r.toInfo) case _: ScopedObject.Class | _: ScopedObject.ClassCtor | _: ScopedObject.Companion => accessed.refdDefns.add(node.obj.toInfo) case _ => () case _ => super.applyPath(p) case Value.Ref(l, _) => accessed.accessed.add(l) case _ => super.applyPath(p) accessed.toIMut /** * Finds the variables belonging to a parent scope which this scoped object could possibly * access or mutate, excluding mutations through calls to other functions and mutations * of their own variables. Also finds the other scoped objects that this definition may enter. * * @param obj The scoped object to search through. * @return The variables which this definition could possibly mutate. */ private def findAccessesShallow(obj: ScopedObject): AccessInfo = val accessed = obj match case ScopedObject.Top(b) => b match case s: Scoped => blkAccessesShallow(s.body) case _ => blkAccessesShallow(b) case ScopedObject.Func(f, _) => blkAccessesShallow(f.body) case ScopedObject.Class(c, _) => // We must assume that classes may access all their methods. // When the class symbol is referenced once, that symbol may be used in // arbitrary ways, which includes calling any of this class's methods. val res = blkAccessesShallow(c.preCtor) ++ blkAccessesShallow(c.ctor) res.copy(refdDefns = res.refdDefns ++ c.methods.map(_.dSym)) case ScopedObject.ClassCtor(cls) => // Recall that we interpret the ctor as just another function in the same scope // as the corresponding class, and initializes the class. AccessInfo.empty.addRefdScopedObj(scopeData.getNode(cls).obj.toInfo) case ScopedObject.ScopedBlock(uid, b) => blkAccessesShallow(b.body) case ScopedObject.Companion(c, _) => // There likely won't be nested companion classes in the future, but for now, // just assume they may access all their methods val res = blkAccessesShallow(c.ctor) res.copy(refdDefns = res.refdDefns ++ c.methods.map(_.dSym)) case ScopedObject.Loop(_, b) => blkAccessesShallow(b) // Variables introduced by this scoped object do not belong to a parent scope, so // we remove them accessed.withoutLocals(obj.definedLocals) private def combineInfos(m1: Map[ScopedInfo, AccessInfo], m2: Map[ScopedInfo, AccessInfo]): Map[ScopedInfo, AccessInfo] = if m2.size < m1.size then combineInfos(m2, m1) else m1.foldLeft(m2): case (acc, info -> accesses) => m2.get(info) match case Some(value) => acc + (info -> (accesses ++ value)) case None => acc + (info -> accesses) // Find: // - Map 1: // - Variables that each scoped object has accessed, either through itself or a nested scoped object. // - Variables that each scoped object has mutated, either through itself or a nested scoped object. // - Scoped objects that each object accesses, either through itself or a nested scoped object. // - Map 2: // - Variables that each scoped object has accessed, either through itself or a *lifted* scoped object. // - Variables that each scoped object has mutated, either through itself or a lifted nested scoped object. // - Scoped objects that each object accesses, either through itself or a lifted nested scoped object. // // The former includes ignored objects, and is used to do the readers/writers analysis. The latter is used to determine // whether we actually need to allocate a capture for the object. In particular, we never need to allocate a capture // for a variable if only nested scopes mutate it. // // Note that it is possible for a lifted scoped object to be reached by traversing through an ignored object. // // Also observe that if a node is not accesed from any of its children, then we can re-use the result of its parent's analysis. private def findAccesses(s: ScopeNode): (Map[ScopedInfo, AccessInfo], Map[ScopedInfo, AccessInfo]) = // Note: these include `s` val children = s.allChildren val childInfo = children.map(_.toInfo).toSet // Traverses the node's children, and stops when a child that is accessed by one of its children is found. // The analysis will be performed on *all* of the traversed nodes simultaneously. // We will later recurse on the children of all these nodes. val (nodes, nexts) = s.partitionTree(x => accessedByChild(x.obj.toInfo)) val allLocals = nodes.flatMap(node => node.obj.definedLocals).toSet val accessInfo = children.map: obj => val a @ AccessInfo(accessed, mutated, refdDefns) = shallowAccesses(obj.toInfo) obj.toInfo -> AccessInfo( accessed = accessed.intersect(allLocals), mutated = mutated.intersect(allLocals), refdDefns = refdDefns.intersect(childInfo) ) val accessInfoMap = accessInfo.toMap val edges: Set[(ScopedInfo, ScopedInfo)] = for (src, AccessInfo(_, _, refd)) <- accessInfo r <- refd // remove self-edges: they do not affect this analysis if src =/= r // very important: we only care about edges that flow into the subtree rooted at `s` if childInfo.contains(r) && r =/= s.obj.toInfo yield src -> r .toSet // (sccs, sccEdges) forms a directed acyclic graph (DAG) val algorithms.SccsInfo(sccs, sccEdges, inDegs, outDegs) = algorithms.sccsWithInfo(edges, childInfo) val rootInfo = s.obj.toInfo val (rootId, rootElems) = sccs.find: case (id, elems) => elems.contains(rootInfo) .get if rootElems.size != 1 then lastWords("SCC containing root had a degree other than 1.") // With respect to the current scoped object `s`, we may "ignore" one of its children `c` if and only if // it is ignored (not lifted), and `s` is in the subtree rooted at the first lifted parent of `c`. We // "ignore" `c` in the sense that it does not need to capture `s`'s scoped object's variables, nor does // it require the current scoped object to create a capture class for its accessed variables. def isIgnored(c: ScopedInfo) = s.inSubtree(scopeData.getNode(c).firstLiftedAncestor.toInfo) // All objects in the same scc must have at least the same accesses as each other def go(includeIgnored: Bool) = val base = for (id, scc) <- sccs yield // If all objects in this SCC are ignored, then we treat it as if it does not access anything, // unless we explicitly want to count ignored items (for the readers-mutators analysis) if !includeIgnored && scc.forall(isIgnored) then id -> AccessInfo.empty else id -> scc.foldLeft(AccessInfo.empty): case (acc, sym) => acc ++ accessInfoMap(sym) // dp on DAG val dp: MutMap[Int, AccessInfo] = MutMap.empty def sccAccessInfo(scc: Int): AccessInfo = dp.get(scc) match case Some(value) => value case None => val ret = sccEdges(scc).foldLeft(base(scc)): case (acc, nextScc) => acc ++ sccAccessInfo(nextScc) dp.addOne(scc -> ret) ret for (id, scc) <- sccs sym <- scc yield sym -> sccAccessInfo(id).withoutLocals(scopeData.getNode(sym).obj.definedLocals) // Remove locals that are not yet defined def removeUnused(m: Map[ScopedInfo, AccessInfo]) = m.map: case k -> v => val node = scopeData.getNode(k) k -> v.intersectLocals(node.existingVars) val (m1, m2) = (removeUnused(go(true)), removeUnused(go(false))) val subCases = nexts.map(findAccesses) subCases.foldLeft((m1, m2)): case ((acc1, acc2), (new1, new2)) => (combineInfos(acc1, new1), combineInfos(acc2, new2)) private def reqdCaptureLocals(s: ScopeNode): Map[ScopedInfo, Set[Local]] = val blk = s.obj match case ScopedObject.Top(b) => lastWords("reqdCaptureLocals called on top block") case ScopedObject.ClassCtor(cls) => return Map.empty + (s.obj.toInfo -> Set.empty) case ScopedObject.Class(cls, _) => Begin(cls.preCtor, cls.ctor) case ScopedObject.Companion(comp, _) => comp.ctor case ScopedObject.Func(fun, _) => fun.body case ScopedObject.ScopedBlock(uid, block) => block case ScopedObject.Loop(sym, block) => block val (nodes, nexts) = s.partitionTree2: case obj: (ScopedObject.ScopedBlock | ScopedObject.Loop) => false case _ => true val locals = nodes.flatMap(_.obj.definedLocals).toSet val cap = reqdCaptureLocalsBlk(blk, nexts.toList, s.obj.definedLocals, locals) // In a class, all variables that are mutated by a child scope and accessed by a lifted class must be captured val additional = s.obj match case _: ScopedObject.Companion | _: ScopedObject.Class => val (a, b) = s.children.map: c => val acc = accessMap(c.obj.toInfo) val accAll = accessMapWithIgnored(c.obj.toInfo) (accAll.mutated, acc.accessed) .unzip a.flatten.toSet.intersect(b.flatten.toSet) case _ => Set.empty val newCap = cap ++ additional val cur: Map[ScopedInfo, Set[Local]] = nodes.map: n => n.obj.toInfo -> newCap.intersect(n.obj.definedLocals) .toMap nexts.foldLeft(cur): case (mp, acc) => mp ++ reqdCaptureLocals(acc) // readers-mutators analysis private def reqdCaptureLocalsBlk(b: Block, nextNodes: List[ScopeNode], startingVars: Set[Local], thisVars: Set[Local]): Set[Local] = val scopeInfos: Map[ScopedInfo, ScopeNode] = nextNodes.map(node => node.obj.toInfo -> node).toMap case class CaptureInfo(reqCapture: Set[Local], hasReader: Set[Local], hasMutator: Set[Local], mutated: Set[Local]) // linearVars denotes the variables defined inside the scopes up to the nearest loop or the top level block. // If a loop modifies a non-linear variable and then one of its nested definitions accesses it, we must put put // that variable in a capture. def go(b: Block, reqCapture_ : Set[Local], hasReader_ : Set[Local], hasMutator_ : Set[Local], mutated_ : Set[Local])(using linearVars: Set[Local]): CaptureInfo = var reqCapture = reqCapture_ var hasReader = hasReader_ var hasMutator = hasMutator_ // note: the meaning of `mutated` is a bit strange: it basically means variables which are currently not linear that have been mutated // if a variable is in this set but is linear, then it's ignored var mutated = mutated_ inline def merge(c: CaptureInfo) = reqCapture ++= c.reqCapture hasReader ++= c.hasReader hasMutator ++= c.hasMutator mutated ++= c.mutated def rec(blk: Block)(using linearVars: Set[Local]) = go(blk, reqCapture, hasReader, hasMutator, mutated_) new BlockTraverserShallow: applyBlock(b) override def applyBlock(b: Block): Unit = b match // Note that we traverse directly into scoped blocks without using handleCalledScope case s: Scoped => rec(s.body)(using linearVars = linearVars ++ s.syms) |> merge case l: Label if l.loop => rec(l.body)(using linearVars = Set.empty) |> merge applySubBlock(l.rest) case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs if !linearVars.contains(lhs) then mutated += lhs applySubBlock(rest) case Define(c @ ClsLikeDefn(k = syntax.Obj), rest) => handleCalledScope(c.isym) applySubBlock(rest) case Match(scrut, arms, dflt, rest) => applyPath(scrut) val infos = arms.map: case (_, arm) => rec(arm) val dfltInfo = dflt.map: case arm => rec(arm) infos.foreach(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive dfltInfo.foreach(merge) applySubBlock(rest) case Begin(sub, rest) => rec(sub) |> merge applySubBlock(rest) case TryBlock(sub, finallyDo, rest) => // sub and finallyDo could be executed sequentially, so we must merge rec(sub) |> merge rec(finallyDo) |> merge applySubBlock(rest) case Return(res, false) => applyResult(res) hasReader = Set.empty hasMutator = Set.empty case _ => super.applyBlock(b) def handleCalledScope(called: ScopedInfo): Unit = scopeInfos.get(called) match case None => () case Some(node) => node.obj match // ignore method calls to class or object methods case ScopedObject.Func(_, S(MethodKind.ClsMethod | MethodKind.ObjMethod)) => return case _ => () val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(called) val muts = muted.intersect(thisVars) val reads = accessed.intersect(thisVars) -- muts val refdExcl = refd.filter: sym => scopeData.getNode(sym).obj match case s: ScopedObject.ScopedBlock => false case ScopedObject.Func(_, S(MethodKind.ClsMethod | MethodKind.ObjMethod)) => false case _ => true // This not a naked reference. If it's a ref to a class, this can only ever create once instance // so the "one writer" rule applies. // However, if the control flow is not linear, we are forced to add all the mutated variables for l <- muts do if hasReader.contains(l) || hasMutator.contains(l) || !linearVars.contains(l) then reqCapture += l hasReader += l hasMutator += l mutated += l for l <- reads do if hasMutator.contains(l) then reqCapture += l if mutated.contains(l) && !linearVars.contains(l) then reqCapture += l hasReader += l // if this defn calls another defn that creates a class or has a naked reference to a // function, we must capture the latter's mutated variables in a capture, as arbitrarily // many mutators could be created from it for sym <- refdExcl l <- accessMapWithIgnored(sym).mutated do reqCapture += l hasMutator += l def handleScopeRef(s: ScopedInfo) = scopeInfos.get(s) match case None => // super.applyPath(p) case Some(defn) => val isModOrObj = defn.obj match case c: ScopedObject.Companion => true case c: ScopedObject.Class => c.isObj case _ => false if isModOrObj then () //super.applyPath(p) else val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(s) val muts = muted.intersect(thisVars) val reads = accessed.intersect(thisVars) -- muts // this is a naked reference, we assume things it mutates always needs a capture for l <- muts do reqCapture += l hasMutator += l for l <- reads do if hasMutator.contains(l) then reqCapture += l if mutated.contains(l) && !linearVars.contains(l) then reqCapture += l hasReader += l // if this defn calls another defn that creates a class or has a naked reference to a // function, we must capture the latter's mutated variables in a capture, as arbitrarily // many mutators could be created from it for sym <- refd l <- accessMapWithIgnored(sym).mutated do reqCapture += l hasMutator += l override def applyResult(r: Result): Unit = r match case Call(RefOfBms(_, SDSym(d), _), argss) => argss.foreach(_.foreach(super.applyArg(_))) val numArgLists = scopeData.getNode(d).obj match case ScopedObject.Func(fun, _) => fun.params.size.min(1) case ScopedObject.Class(c, false) => c.paramsOpt.map(_.params.size).getOrElse(0).min(1) case ScopedObject.ClassCtor(c) => c.paramsOpt.map(_.params.size).getOrElse(0).min(1) case _ => die // Partial call; the resulting object requiring access to the scope may linger if numArgLists != argss.size then handleScopeRef(d) // Fully applied, we can treat it as a call else handleCalledScope(d) case Instantiate(mut, RefOfBms(_, SDSym(d), _), argss) => argss.foreach(_.foreach(super.applyArg(_))) handleCalledScope(d) case _ => super.applyResult(r) override def applyPath(p: Path): Unit = p match case RefOfBms(_, SDSym(d), _) => handleScopeRef(d) case Value.Ref(l, _) => if hasMutator.contains(l) then reqCapture += (l) case _ => super.applyPath(p) override def applyDefn(defn: Defn): Unit = defn match case c: ClsLikeDefn if modOrObj(c) => handleCalledScope(c.isym) super.applyDefn(defn) case _ => super.applyDefn(defn) CaptureInfo(reqCapture, hasReader, hasMutator, mutated) val reqCapture = go(b, Set.empty, Set.empty, Set.empty, Set.empty)(using linearVars = startingVars).reqCapture reqCapture.intersect(thisVars) // entry point val shallowAccesses: Map[ScopedInfo, AccessInfo] = scopeData.scopeTree.root.allChildren.map(obj => obj.toInfo -> findAccessesShallow(obj)).toMap // Optimization: Find all nodes which are accessed by their children // See the comment for findAccesses private val allEdges = for (src, accesses) <- shallowAccesses refd <- accesses.refdDefns if src =/= refd yield (src, refd) private val accessedByChild = allEdges .groupBy(_._2) // group by edge destination .map: case (_: Unit) -> _ => () -> false case d -> edges => val par = scopeData.getNode(d).ancestor.get.obj.toInfo d -> edges.exists: case a -> b => a =/= par .collect: case d -> true => d .toSet // Searching from the root makes no sense. We instead start searching from each scope nested in the top-level private val (m1, m2) = scopeData.scopeTree.root.children.map(findAccesses).unzip val accessMapWithIgnored = m1.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) val accessMap = m2.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) // We make these lazy, because not all users of UsedVarAnalyzer need this analysis. For now, only the lifter needs it. lazy val reqdCaptures: Map[ScopedInfo, Set[Local]] = scopeData.root.children.foldLeft(Map.empty): case (acc, node) => acc ++ reqdCaptureLocals(node) // For local inside a capture, finds the node to which this local belongs. lazy val capturesMap = for case (info -> reqCap) <- reqdCaptures s <- reqCap yield s -> info ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/cpp/Ast.scala ================================================ package hkmc2 package codegen.cpp import mlscript._ import mlscript.utils._ import mlscript.utils.shorthands._ import scala.language.implicitConversions import document._ private def raw(x: String): Document = doc"$x" given Conversion[String, Document] = x => doc"$x" enum Specifier: case Extern case Static case Inline def toDocument = this match case Extern => "extern" case Static => "static" case Inline => "inline" override def toString: Str = toDocument object Type: def toDocuments(args: Ls[Type], sep: Document, extraTypename: Bool = false): Document = args.map(_.toDocument(extraTypename)).mkDocument(sep) def toDocuments(args: Ls[(Str, Type)], sep: Document): Document = args.map(x => doc"${x._2.toDocument()} ${x._1}").mkDocument(sep) enum Type: case Prim(name: Str) case Ptr(inner: Type) case Ref(inner: Type) case Array(inner: Type, size: Opt[Int]) case FuncPtr(ret: Type, args: List[Type]) case Struct(name: Str) case Enum(name: Str) case Template(name: Str, args: List[Type]) case Var(name: Str) case Qualifier(inner: Type, qual: Str) def toDocument(extraTypename: Bool = false): Document = def aux(x: Type): Document = x match case Prim(name) => name case Ptr(inner) => doc"${aux(inner)}*" case Ref(inner) => doc"${aux(inner)}&" case Array(inner, size) => doc"${aux(inner)}[${size.fold("")(x => x.toString)}]" case FuncPtr(ret, args) => doc"${aux(ret)}(${Type.toDocuments(args, sep = ", ")})" case Struct(name) => doc"struct $name" case Enum(name) => doc"enum $name" case Template(name, args) => doc"$name<${Type.toDocuments(args, sep = ", ")}>" case Var(name) => name case Qualifier(inner, qual) => doc"${aux(inner)} $qual" aux(this) override def toString: Str = toDocument().toString object Stmt: def toDocuments(decl: Ls[Decl], stmts: Ls[Stmt]): Document = (decl.map(_.toDocument) ++ stmts.map(_.toDocument)).mkDocument(doc" # ") enum Stmt: case AutoBind(lhs: Ls[Str], rhs: Expr) case Assign(lhs: Str, rhs: Expr) case Return(expr: Expr) case If(cond: Expr, thenStmt: Stmt, elseStmt: Opt[Stmt]) case While(cond: Expr, body: Stmt) case For(init: Stmt, cond: Expr, update: Stmt, body: Stmt) case ExprStmt(expr: Expr) case Break case Continue case Block(decl: Ls[Decl], stmts: Ls[Stmt]) case Switch(expr: Expr, cases: Ls[(Expr, Stmt)]) case Raw(stmt: Str) def toDocument: Document = def aux(x: Stmt): Document = x match case AutoBind(lhs, rhs) => lhs match case Nil => rhs.toDocument case x :: Nil => doc"auto $x = ${rhs.toDocument};" case _ => doc"auto [${lhs.mkDocument(", ")}] = ${rhs.toDocument};" case Assign(lhs, rhs) => doc"$lhs = ${rhs.toDocument};" case Return(expr) => doc"return ${expr.toDocument};" case If(cond, thenStmt, elseStmt) => doc"if (${cond.toDocument}) ${thenStmt.toDocument}${elseStmt.fold(doc" ")(x => doc" else ${x.toDocument}")}" case While(cond, body) => doc"while (${cond.toDocument}) ${body.toDocument}" case For(init, cond, update, body) => doc"for (${init.toDocument}; ${cond.toDocument}; ${update.toDocument}) ${body.toDocument}" case ExprStmt(expr) => doc"${expr.toDocument};" case Break => "break;" case Continue => "continue;" case Block(decl, stmts) => doc"{ #{ # ${Stmt.toDocuments(decl, stmts)} #} # }" case Switch(expr, cases) => val docCases = cases.map { case (cond, stmt) => doc"case ${cond.toDocument}: ${stmt.toDocument}" }.mkDocument(doc" # ") doc"switch (${expr.toDocument}) { #{ # ${docCases} #} # }" case Raw(stmt) => stmt aux(this) object Expr: def toDocuments(args: Ls[Expr], sep: Document): Document = args.map(_.toDocument).mkDocument(sep) enum Expr: case Var(name: Str) case IntLit(value: BigInt) case DoubleLit(value: Double) case StrLit(value: Str) case CharLit(value: Char) case Call(func: Expr, args: Ls[Expr]) case Member(expr: Expr, member: Str) case Index(expr: Expr, index: Expr) case Unary(op: Str, expr: Expr) case Binary(op: Str, lhs: Expr, rhs: Expr) case Initializer(exprs: Ls[Expr]) def toDocument: Document = def aux(x: Expr): Document = x match case Var(name) => name case IntLit(value) => value.toString case DoubleLit(value) => value.toString case StrLit(value) => value.escaped case CharLit(value) => value.toInt.toString case Call(func, args) => doc"${func.toDocument}(${Expr.toDocuments(args, sep = ", ")})" case Member(expr, member) => doc"${expr.toDocument}->$member" case Index(expr, index) => doc"${expr.toDocument}[${index.toDocument}]" case Unary(op, expr) => doc"($op${expr.toDocument})" case Binary(op, lhs, rhs) => doc"(${lhs.toDocument} $op ${rhs.toDocument})" case Initializer(exprs) => doc"{${Expr.toDocuments(exprs, sep = ", ")}}" aux(this) case class CompilationUnit(includes: Ls[Str], decls: Ls[Decl], defs: Ls[Def]): def toDocument: Document = (includes.map(raw) ++ decls.map(_.toDocument) ++ defs.map(_.toDocument)).mkDocument(doc" # ") def toDocumentWithoutHidden: Document = val hiddenNames: Set[Str] = Set() defs.filterNot { case Def.StructDef(name, _, _, _, _) => hiddenNames.contains(name.stripPrefix("_mls_")) case _ => false }.map(_.toDocument).mkDocument(doc" # ") enum Decl: case StructDecl(name: Str) case EnumDecl(name: Str) case FuncDecl(ret: Type, name: Str, args: Ls[Type], isOverride: Bool, isVirtual: Bool) case VarDecl(name: Str, typ: Type) def toDocument: Document = def aux(x: Decl): Document = x match case StructDecl(name) => doc"struct $name;" case EnumDecl(name) => doc"enum $name;" case FuncDecl(ret, name, args, or, virt) => val docVirt = (if virt then doc"virtual " else doc"") val docSpecRet = ret.toDocument() val docArgs = Type.toDocuments(args, sep = ", ") val docOverride = if or then doc" override" else doc"" doc"$docVirt$docSpecRet $name($docArgs)$docOverride;" case VarDecl(name, typ) => doc"${typ.toDocument()} $name;" aux(this) enum Def: case StructDef(name: Str, fields: Ls[(Str, Type)], inherit: Opt[Ls[Str]], methods: Ls[Def], methodsDecl: Ls[Decl]) case EnumDef(name: Str, fields: Ls[(Str, Opt[Int])]) case FuncDef(specret: Type, name: Str, args: Ls[(Str, Type)], body: Stmt.Block, isOverride: Bool, isVirtual: Bool, in_scope: Opt[Str]) case VarDef(typ: Type, name: Str, init: Opt[Expr]) case RawDef(raw: Str) def toDocument: Document = def aux(x: Def): Document = x match case StructDef(name, fields, inherit, defs, decls) => val docFirst = doc"struct $name${inherit.fold(doc"")(x => doc": public ${x.mkDocument(doc", ")}")} {" val docFields = fields.map { case (name, typ) => doc"${typ.toDocument()} $name;" }.mkDocument(doc" # ") val docDefs = defs.map(_.toDocument).mkDocument(doc" # ") val docDecls = decls.map(_.toDocument).mkDocument(doc" # ") val docLast = "};" doc"$docFirst #{ # $docFields # $docDefs # $docDecls #} # $docLast" case EnumDef(name, fields) => val docFirst = doc"enum $name {" val docFields = fields.map { case (name, value) => value.fold(s"$name")(x => s"$name = $x") }.mkDocument(doc" # ") val docLast = "};" doc"$docFirst #{ # $docFields #} # $docLast" case FuncDef(specret, name, args, body, or, virt, scope) => val docVirt = (if virt then doc"virtual " else doc"") val docSpecRet = specret.toDocument() val docArgs = Type.toDocuments(args, sep = ", ") val docOverride = if or then doc" override" else doc"" val docBody = body.toDocument val docScope = scope.fold(doc"")(x => doc"$x::") doc"$docVirt$docSpecRet $docScope$name($docArgs)$docOverride ${body.toDocument}" case VarDef(typ, name, init) => val docTyp = typ.toDocument() val docInit = init.fold(raw(""))(x => doc" = ${x.toDocument}") doc"$docTyp $name$docInit;" case RawDef(x) => x aux(this) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/cpp/CodeGen.scala ================================================ package hkmc2 package codegen package cpp import mlscript.utils._ import mlscript.utils.shorthands._ import scala.collection.mutable.ListBuffer import llir.{Expr => IExpr, _} import utils.{Scope, TraceLogger} import Scope.scope import semantics._ class CppCodeGen(builtinClassSymbols: Set[Local], tl: TraceLogger): import tl.{trace, log, logs} def mapName(name: Str): Str = "_mls_" + Scope.replaceInvalidCharacters(name) def mapClsLikeName(sym: Local)(using Raise, Scope): Str = if builtinClassSymbols.contains(sym) then sym.nme |> mapName else allocIfNew(sym) def directName(sym: Local): Str = sym.nme |> mapName val mlsValType = Type.Prim("_mlsValue") val mlsUnitValue = Expr.Call(Expr.Var("_mlsValue::create<_mls_Unit>"), Ls()); val mlsRetValue = "_mls_retval" def mlsRetValType(n: Int) = if n === 1 then mlsValType else Type.Template("std::tuple", Ls.fill(n)(mlsValType)) def mlsRetValueDecl(n: Int) = if n === 1 then Decl.VarDecl(mlsRetValue, mlsValType) else Decl.VarDecl(mlsRetValue, mlsRetValType(n)) val mlsMainName = "_mlsMain" val mlsPrelude = "#include \"mlsprelude.h\"" val mlsPreludeImpl = "#include \"mlsprelude.cpp\"" val builtinClassSymbolNames = Set("Callable", "Lazy") def mlsIsInternalClass(sym: Local) = builtinClassSymbolNames.contains(sym.nme) val mlsObject = "_mlsObject" val mlsBuiltin = "builtin" val mlsEntryPoint = s"int main() { return _mlsLargeStack(_mlsMainWrapper); }"; def mlsCallEntry(s: Str) = s"_mlsValue _mlsMain() { return $s(); }" def mlsIntLit(x: BigInt) = Expr.Call(Expr.Var("_mlsValue::fromIntLit"), Ls(Expr.IntLit(x))) def mlsStrLit(x: Str) = Expr.Call(Expr.Var("_mlsValue::create<_mls_Str>"), Ls(Expr.StrLit(x))) def mlsDecLit(x: BigDecimal) = Expr.Call(Expr.Var("_mlsValue::create<_mls_Float>"), Ls(Expr.DoubleLit(x.toDouble))) def mlsCharLit(x: Char) = Expr.Call(Expr.Var("_mlsValue::fromIntLit"), Ls(Expr.CharLit(x))) def mlsNewValue(cls: Str, args: Ls[Expr]) = Expr.Call(Expr.Var(s"_mlsValue::create<$cls>"), args) def mlsIsValueOf(cls: Str, scrut: Expr) = Expr.Call(Expr.Var(s"_mlsValue::isValueOf<$cls>"), Ls(scrut)) def mlsIsBoolLit(scrut: Expr, lit: hkmc2.syntax.Tree.BoolLit) = Expr.Call(Expr.Var("_mlsValue::isIntLit"), Ls(scrut, Expr.IntLit(if lit.value then 1 else 0))) def mlsIsIntLit(scrut: Expr, lit: hkmc2.syntax.Tree.IntLit) = Expr.Call(Expr.Var("_mlsValue::isIntLit"), Ls(scrut, Expr.IntLit(lit.value))) def mlsDebugPrint(x: Expr) = Expr.Call(Expr.Var("_mlsValue::print"), Ls(x)) def mlsTupleValue(init: Ls[Expr]) = Expr.Call(Expr.Var("std::make_tuple"), init) def mlsAs(name: Str, cls: Str) = Expr.Var(s"_mlsValue::as<$cls>($name)") def mlsAsUnchecked(name: Str, cls: Str) = Expr.Var(s"_mlsValue::cast<$cls>($name)") def mlsObjectNameMethod(name: Str) = s"constexpr static inline const char *typeName = \"${name}\";" def mlsTypeTag() = s"constexpr static inline uint32_t typeTag = nextTypeTag();" def mlsTypeTag(n: Int) = s"constexpr static inline uint32_t typeTag = $n;" def mlsCommonCreateMethod(cls: Str, fields: Ls[Str], id: Int) = val parameters = fields.map{x => s"_mlsValue $x"}.mkString(", ") val fieldsAssignment = fields.map{x => s"_mlsVal->$x = $x; "}.mkString s"static _mlsValue create($parameters) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) $cls; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; $fieldsAssignment return _mlsValue(_mlsVal); }" def mlsCommonPrintMethod(fields: Ls[Str]) = if fields.isEmpty then s"virtual void print() const override { std::printf(\"%s\", typeName); }" else val fieldsPrint = fields.map{x => s"this->$x.print(); "}.mkString("std::printf(\", \"); ") s"virtual void print() const override { std::printf(\"%s\", typeName); std::printf(\"(\"); $fieldsPrint std::printf(\")\"); }" def mlsCommonDestructorMethod(cls: Str, fields: Ls[Str]) = val fieldsDeletion = fields.map{x => s"_mlsValue::destroy(this->$x); "}.mkString s"virtual void destroy() override { $fieldsDeletion operator delete (this, std::align_val_t(_mlsAlignment)); }" def mlsThrowNonExhaustiveMatch = Stmt.Raw("_mlsNonExhaustiveMatch();"); def mlsCall(fn: Str, args: Ls[Expr]) = Expr.Call(Expr.Var("_mlsCall"), Expr.Var(fn) :: args) def mlsMethodCall(cls: Local, method: Str, args: Ls[Expr])(using Raise, Scope) = Expr.Call(Expr.Member(Expr.Call(Expr.Var(s"_mlsMethodCall<${cls |> mapClsLikeName}>"), Ls(args.head)), method), args.tail) def mlsThisCall(cls: Local, method: Str, args: Ls[Expr])(using Raise, Scope) = Expr.Call(Expr.Member(Expr.Var("this"), method), args) def mlsFnWrapperName(fn: Str) = s"_mlsFn_$fn" def mlsFnCreateMethod(fn: Str) = s"static _mlsValue create() { static _mlsFn_$fn mlsFn alignas(_mlsAlignment); mlsFn.refCount = stickyRefCount; mlsFn.tag = typeTag; return _mlsValue(&mlsFn); }" def mlsNeverValue(n: Int) = if (n <= 1) then Expr.Call(Expr.Var(s"_mlsValue::never"), Ls()) else Expr.Call(Expr.Var(s"_mlsValue::never<$n>"), Ls()) val mlsThis = Expr.Var("_mlsValue(this, _mlsValue::inc_ref_tag{})") // first construct a value, then incRef() case class Ctx( fieldCtx: Set[Local], ) def getVar(l: Local)(using Raise, Scope): String = l match case ts: hkmc2.semantics.TermSymbol => ts.owner match case S(owner) => scope.lookup_!(ts, N) case N => scope.lookup_!(ts, N) case ts: hkmc2.semantics.InnerSymbol => scope.lookup_!(ts, N) case _ => scope.lookup_!(l, N) def allocIfNew(l: Local)(using Raise, Scope): Str = trace[Str](s"allocIfNew $l begin", r => s"allocIfNew $l end -> $r"): if scope.lookup(l).isDefined then getVar(l) |> mapName else scope.allocateName(l) |> mapName def codegenClassInfo(using Ctx, Raise, Scope)(cls: ClassInfo) = trace[(Opt[Def], Decl, Ls[Def])](s"codegenClassInfo ${cls.symbol} begin"): val fields = cls.fields.map{x => (x |> directName, mlsValType)} cls.fields.foreach(x => scope.allocateName(x)) val parents = if cls.parents.nonEmpty then cls.parents.toList.map(mapClsLikeName) else mlsObject :: Nil val decl = Decl.StructDecl(cls.symbol |> mapClsLikeName) if mlsIsInternalClass(cls.symbol) then (None, decl, Ls.empty) else val methods = cls.methods.map: case (name, defn) => val (cdef, decl) = codegenDefn(using Ctx(summon[Ctx].fieldCtx ++ cls.fields))(defn) val cdef2 = cdef match case x: Def.FuncDef if builtinApply.contains(defn.name.nme) => x.copy(name = defn.name |> directName, in_scope = Some(cls.symbol |> mapClsLikeName)) case x: Def.FuncDef => x.copy(in_scope = Some(cls.symbol |> mapClsLikeName)) case _ => throw new Exception(s"codegenClassInfo: unexpected def $cdef") val decl2 = decl match case x: Decl.FuncDecl if builtinApply.contains(defn.name.nme) => x.copy(isVirtual = true, name = defn.name |> directName) case x: Decl.FuncDecl => x.copy(isVirtual = true) case _ => throw new Exception(s"codegenClassInfo: unexpected decl $decl") log(s"codegenClassInfo: ${cls.symbol} method ${defn.name} $decl2") (cdef2, decl2) val theDef = Def.StructDef( cls.symbol |> mapClsLikeName, fields, if parents.nonEmpty then Some(parents) else None, Ls(Def.RawDef(mlsObjectNameMethod(cls.symbol.nme)), Def.RawDef(mlsTypeTag()), Def.RawDef(mlsCommonPrintMethod(cls.fields.map(directName))), Def.RawDef(mlsCommonDestructorMethod(cls.symbol |> mapClsLikeName, cls.fields.map(directName))), Def.RawDef(mlsCommonCreateMethod(cls.symbol |> mapClsLikeName, cls.fields.map(directName), cls.id))), methods.iterator.map(_._2).toList ) (S(theDef), decl, methods.iterator.map(_._1).toList) def toExpr(texpr: TrivialExpr, reifyUnit: Bool = false)(using Ctx, Raise, Scope): Opt[Expr] = texpr match case IExpr.Ref(name) if summon[Ctx].fieldCtx.contains(name) => S(Expr.Var(name |> directName)) case IExpr.Ref(name: BuiltinSymbol) if name.nme === "" => S(mlsThis) case IExpr.Ref(name) => S(Expr.Var(name |> allocIfNew)) case IExpr.Literal(hkmc2.syntax.Tree.BoolLit(x)) => S(mlsIntLit(if x then 1 else 0)) case IExpr.Literal(hkmc2.syntax.Tree.IntLit(x)) => S(mlsIntLit(x)) case IExpr.Literal(hkmc2.syntax.Tree.DecLit(x)) => S(mlsDecLit(x)) case IExpr.Literal(hkmc2.syntax.Tree.StrLit(x)) => S(mlsStrLit(x)) case IExpr.Literal(hkmc2.syntax.Tree.UnitLit(_)) => if reifyUnit then S(mlsUnitValue) else None def toExpr(texpr: TrivialExpr)(using Ctx, Raise, Scope): Expr = texpr match case IExpr.Ref(name) if summon[Ctx].fieldCtx.contains(name) => Expr.Var(name |> directName) case IExpr.Ref(name: BuiltinSymbol) if name.nme === "" => mlsThis case IExpr.Ref(name) => Expr.Var(name |> allocIfNew) case IExpr.Literal(hkmc2.syntax.Tree.BoolLit(x)) => mlsIntLit(if x then 1 else 0) case IExpr.Literal(hkmc2.syntax.Tree.IntLit(x)) => mlsIntLit(x) case IExpr.Literal(hkmc2.syntax.Tree.DecLit(x)) => mlsDecLit(x) case IExpr.Literal(hkmc2.syntax.Tree.StrLit(x)) => mlsStrLit(x) case IExpr.Literal(hkmc2.syntax.Tree.UnitLit(_)) => mlsUnitValue def wrapMultiValues(exprs: Ls[TrivialExpr])(using Ctx, Raise, Scope): Expr = exprs match case x :: Nil => toExpr(x, reifyUnit = true).get case _ => val init = exprs.map{x => toExpr(x)} mlsTupleValue(init) def codegenCaseWithIfs(scrut: TrivialExpr, cases: Ls[(Pat, Node)], default: Opt[Node], storeInto: Str)(using decls: Ls[Decl], stmts: Ls[Stmt])(using Ctx, Raise, Scope): (Ls[Decl], Ls[Stmt]) = val scrut2 = toExpr(scrut) val init: Stmt = default.fold(mlsThrowNonExhaustiveMatch)(x => { val (decls2, stmts2) = codegen(x, storeInto)(using Ls.empty, Ls.empty[Stmt]) Stmt.Block(decls2, stmts2) }) val stmt = cases.foldRight(S(init)) { case ((Pat.Class(cls), arm), nextarm) => val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) val stmt = Stmt.If(mlsIsValueOf(cls |> mapClsLikeName, scrut2), Stmt.Block(decls2, stmts2), nextarm) S(stmt) case ((Pat.Lit(i @ hkmc2.syntax.Tree.IntLit(_)), arm), nextarm) => val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) val stmt = Stmt.If(mlsIsIntLit(scrut2, i), Stmt.Block(decls2, stmts2), nextarm) S(stmt) case ((Pat.Lit(i @ hkmc2.syntax.Tree.BoolLit(_)), arm), nextarm) => val (decls2, stmts2) = codegen(arm, storeInto)(using Ls.empty, Ls.empty[Stmt]) val stmt = Stmt.If(mlsIsBoolLit(scrut2, i), Stmt.Block(decls2, stmts2), nextarm) S(stmt) case _ => TODO("codegenCaseWithIfs doesn't support these patterns currently") } (decls, stmt.fold(stmts)(x => stmts :+ x)) def codegenJumpWithCall(func: Local, args: Ls[TrivialExpr], storeInto: Opt[Str])(using decls: Ls[Decl], stmts: Ls[Stmt])(using Ctx, Raise, Scope): (Ls[Decl], Ls[Stmt]) = val call = Expr.Call(Expr.Var(func |> allocIfNew), args.map(toExpr)) val stmts2 = stmts ++ Ls(storeInto.fold(Stmt.Return(call))(x => Stmt.Assign(x, call))) (decls, stmts2) def codegenOps(op: BuiltinSymbol, args: Ls[TrivialExpr])(using Ctx, Raise, Scope) = trace[Expr](s"codegenOps $op begin"): var op2 = op.nme if op2 === "===" then op2 = "==" else if op2 === "!===" then op2 = "!=" if op.binary && args.length === 2 then Expr.Binary(op2, toExpr(args(0)), toExpr(args(1))) else if op.unary && args.length === 1 then Expr.Unary(op2, toExpr(args(0))) else TODO(s"codegenOps ${op.nme} ${args.size} ${op.binary} ${op.unary} ${args.map(_.show).mkString(", ")}") def codegen(expr: IExpr)(using Ctx, Raise, Scope): Expr = expr match case x @ (IExpr.Ref(_) | IExpr.Literal(_)) => toExpr(x, reifyUnit = true).get case IExpr.CtorApp(cls, args) => mlsNewValue(cls |> mapClsLikeName, args.map(toExpr)) case IExpr.Select(name, cls, field) if field.forall(_.isDigit) => Expr.Member(mlsAsUnchecked(name |> allocIfNew, cls |> mapClsLikeName), s"field${field}" |> mapName) case IExpr.Select(name, cls, field) => Expr.Member(mlsAsUnchecked(name |> allocIfNew, cls |> mapClsLikeName), field |> mapName) case IExpr.BasicOp(name, args) => codegenOps(name, args) case IExpr.AssignField(assignee, cls, field, value) => TODO("codegen assign field") def codegenBuiltin(names: Ls[Local], builtin: Str, args: Ls[TrivialExpr])(using Ctx, Raise, Scope): Ls[Stmt] = builtin match case "error" => Ls(Stmt.Raw("throw std::runtime_error(\"Error\");"), Stmt.AutoBind(names.map(allocIfNew), mlsNeverValue(names.size))) case _ => Ls(Stmt.AutoBind(names.map(allocIfNew), Expr.Call(Expr.Var("_mls_builtin_" + builtin), args.map(toExpr)))) lazy val builtinApply = Set( "apply0", "apply1", "apply2", "apply3", "apply4", "apply5", "apply6", "apply7", "apply8", "apply9", ) def codegen(body: Node, storeInto: Str)(using decls: Ls[Decl], stmts: Ls[Stmt])(using Ctx, Raise, Scope): (Ls[Decl], Ls[Stmt]) = trace[(Ls[Decl], Ls[Stmt])](s"codegen $body begin"): body match case Node.Result(res) => val expr = wrapMultiValues(res) val stmts2 = stmts ++ Ls(Stmt.Assign(storeInto, expr)) (decls, stmts2) case Node.Jump(defn, args) => codegenJumpWithCall(defn, args, S(storeInto)) case Node.Panic(msg) => (decls, stmts :+ Stmt.Raw(s"throw std::runtime_error(\"$msg\");")) case Node.LetExpr(name, expr, body) => val stmts2 = stmts ++ Ls(Stmt.AutoBind(Ls(name |> allocIfNew), codegen(expr))) codegen(body, storeInto)(using decls, stmts2) case Node.LetCall(names, bin: BuiltinSymbol, IExpr.Literal(syntax.Tree.StrLit(s)) :: tail, body) if bin.nme === "" => val stmts2 = stmts ++ codegenBuiltin(names, s.replace("\"", ""), tail) codegen(body, storeInto)(using decls, stmts2) case Node.LetMethodCall(names, cls, method, IExpr.Ref(bin: BuiltinSymbol) :: args, body) if bin.nme === "" => val call = mlsThisCall(cls, method |> directName, args.map(toExpr)) val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(allocIfNew), call)) codegen(body, storeInto)(using decls, stmts2) case Node.LetMethodCall(names, cls, method, args, body) if builtinApply.contains(method.nme) => val call = mlsMethodCall(cls, method |> directName, args.map(toExpr)) val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(allocIfNew), call)) codegen(body, storeInto)(using decls, stmts2) case Node.LetMethodCall(names, cls, method, args, body) => val call = mlsMethodCall(cls, method |> allocIfNew, args.map(toExpr)) val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(allocIfNew), call)) codegen(body, storeInto)(using decls, stmts2) case Node.LetCall(names, defn, args, body) => val call = Expr.Call(Expr.Var(defn |> allocIfNew), args.map(toExpr)) val stmts2 = stmts ++ Ls(Stmt.AutoBind(names.map(allocIfNew), call)) codegen(body, storeInto)(using decls, stmts2) case Node.Case(scrut, cases, default) => codegenCaseWithIfs(scrut, cases, default, storeInto) def codegenDefn(using Ctx, Raise, Scope)(defn: Func): (Def, Decl) = defn match case Func(id, name, params, resultNum, body) => val decls = Ls(mlsRetValueDecl(resultNum)) val stmts = Ls.empty[Stmt] val (decls2, stmts2) = codegen(body, mlsRetValue)(using decls, stmts) val stmtsWithReturn = stmts2 :+ Stmt.Return(Expr.Var(mlsRetValue)) val theDef = Def.FuncDef(mlsRetValType(resultNum), name |> allocIfNew, params.map(x => (x |> allocIfNew, mlsValType)), Stmt.Block(decls2, stmtsWithReturn), false, false, None) val decl = Decl.FuncDecl(mlsRetValType(resultNum), name |> allocIfNew, params.map(x => mlsValType), false, false) (theDef, decl) // Topological sort of classes based on inheritance relationships def sortClasses(prog: Program)(using Raise, Scope): Ls[ClassInfo] = var depgraph = prog.classes.map(x => (x.symbol, x.parents)).toMap ++ builtinClassSymbols.map(x => (x, List.empty[Symbol])) log(s"depgraph: $depgraph") var degree = depgraph.view.mapValues(_.size).toMap def removeNode(node: Symbol) = degree -= node depgraph -= node depgraph = depgraph.view.mapValues(_.filter(_ =/= node)).toMap degree = depgraph.view.mapValues(_.size).toMap val sorted = ListBuffer.empty[ClassInfo] given Ordering[Local] with def compare(x: Local, y: Local): Int = x.nme.compareTo(y.nme) var work = degree.filter(_._2 === 0).keys.toSortedSet while work.nonEmpty do val node = work.head work -= node prog.classes.find(x => (x.symbol) === node).foreach(sorted.addOne) removeNode(node) val next = degree.filter(_._2 === 0).keys.toSortedSet work ++= next if depgraph.nonEmpty then val cycle = depgraph.keys.mkString(", ") throw new Exception(s"Cycle detected in class hierarchy: $cycle") sorted.toList def codegen(prog: Program)(using Raise, Scope): CompilationUnit = val sortedClasses = sortClasses(prog) val fieldCtx = Set.empty[Local] given Ctx = Ctx(fieldCtx) val (defs, decls, methodsDef) = sortedClasses.map(codegenClassInfo).unzip3 val (defs2, decls2) = prog.defs.map(codegenDefn).unzip CompilationUnit(Ls(mlsPrelude), decls ++ decls2, defs.flatten ++ defs2 ++ methodsDef.flatten :+ Def.RawDef(mlsCallEntry(prog.entry |> allocIfNew)) :+ Def.RawDef(mlsEntryPoint)) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/cpp/CompilerHost.scala ================================================ package hkmc2.codegen.cpp import mlscript._ import mlscript.utils.shorthands._ import scala.collection.mutable.ListBuffer final class CppCompilerHost(val auxPath: Str, val output: Str => Unit): import scala.sys.process._ private def ifAnyCppCompilerExists(): Boolean = Seq("g++", "--version").! == 0 || Seq("clang++", "--version").! == 0 private def isMakeExists(): Boolean = import scala.sys.process._ Seq("make", "--version").! == 0 val ready = ifAnyCppCompilerExists() && isMakeExists() def compileAndRun(src: Str): Unit = if !ready then return val srcPath = os.temp(contents = src, suffix = ".cpp") val binPath = os.temp(suffix = ".mls.out") var stdout = ListBuffer[Str]() var stderr = ListBuffer[Str]() val buildLogger = ProcessLogger(stdout :+= _, stderr :+= _) val buildResult = Seq("make", "-B", "-C", auxPath, "auto", s"SRC=$srcPath", s"DST=$binPath") ! buildLogger if buildResult != 0 then output("Compilation failed: ") for line <- stdout do output(line) for line <- stderr do output(line) return stdout.clear() stderr.clear() val runCmd = Seq(binPath.toString) val runResult = runCmd ! buildLogger if runResult != 0 then output("Execution failed: ") for line <- stdout do output(line) for line <- stderr do output(line) return output("Execution succeeded: ") for line <- stdout do output(line) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala ================================================ package hkmc2 package codegen package deforest import utils.* import mlscript.utils.*, shorthands.* import semantics.* import scala.collection.mutable.{Set as MutSet, Map as MutMap, LinkedHashMap} import hkmc2.codegen.flowAnalysis.* sealed abstract class FinalDest case class FinalDestMatch(dtor: CtorDtorId, sels: Set[CtorDtorId]) extends FinalDest case class FinalDestSel(dtors: Set[CtorDtorId], field: SelField) extends FinalDest class DeforestFusionSolver(val constraintSolver: FlowConstraintSolver): given preAnalyzer: FlowPreAnalyzer = constraintSolver.preAnalyzer given fState: FlowAnalysis.State = constraintSolver.fState given eState: Elaborator.State = constraintSolver.eState given tl: TraceLogger = constraintSolver.tl private def selAndDtorIsSameConsumer(dtor: CtorDtorId, sels: Iterable[CtorDtorId]): Boolean = sels.forall: case CtorDtorId(selExpr, instId) => instId == dtor.instId && preAnalyzer.res.getEnclosingMatchesForSel(selExpr).exists(_._1 == dtor.exprId) && selExpr.getResult.matches: case TrackableSelect(from, _, _) => from === dtor.exprId.getResult val finalCtorDests = LinkedHashMap.empty[CtorDtorId, FinalDest] val finalDtorSrcs = LinkedHashMap.empty[CtorDtorId, Set[CtorDtorId]] val fusingCtorInfo = MutMap.empty[CtorDtorId, ConcreteProducer] val fusingDtorInfo = MutMap.empty[CtorDtorId, ConcreteConsumer] locally { def mergeDests(dests: Set[ConcreteConsumer | NoCons.type]): Opt[FinalDest] = def selsSelectingTheSameSymbol(sels: Set[FieldSel]) = sels.map(s => s.field).size == 1 if dests.contains(NoCons) then N else val (dtors, sels) = dests.partitionMap: case d: Dtor => Left(d) case fs: FieldSel => Right(fs) case _ => die if dtors.size == 0 && selsSelectingTheSameSymbol(sels) then S(FinalDestSel( sels.map(_.toCtorDtorId), sels.head.field )) else if dtors.size != 1 then N else val dtor = dtors.head if selAndDtorIsSameConsumer( dtor.toCtorDtorId, sels.map(s => s.toCtorDtorId) ) then S(FinalDestMatch( CtorDtorId(dtor.scrutExprId, dtor.instantiationId.get), sels.map: s => CtorDtorId(s.exprId, s.instantiationId.get) )) else N end mergeDests val prodRoots = for (ctor, dests) <- constraintSolver.ctorDests if mergeDests(dests).isEmpty yield ctor val consRoots = for (dtor, srcs) <- constraintSolver.dtorSrcs if srcs.contains(NoProd) yield dtor val result = FlowWebComputation[ConcreteProducer, ConcreteConsumer]( p => constraintSolver.ctorDests(p).collect: case c: ConcreteConsumer => c, c => constraintSolver.dtorSrcs(c).collect: case p: ConcreteProducer => p, prodRoots, consRoots, ) val toRemoveCtor = result.markedProducers val toRemoveDtor = result.markedConsumers for (ctor, dests) <- constraintSolver.ctorDests if !toRemoveCtor(ctor) do finalCtorDests(ctor.toCtorDtorId) = mergeDests(dests).get fusingCtorInfo(ctor.toCtorDtorId) = ctor for (dtor, srcs) <- constraintSolver.dtorSrcs if !toRemoveDtor(dtor) do finalDtorSrcs(dtor.toCtorDtorId) = // srcs are always ConcreteProducers after constraint solving srcs.map(_.asInstanceOf[ConcreteProducer].toCtorDtorId) fusingDtorInfo(dtor.toCtorDtorId) = dtor } tl.log(">>> fusing >>>") for case (c, dest) <- finalCtorDests do tl.log(s"${c.pp} ->") dest match case FinalDestMatch(dtor, sels) => tl.log(s"\t${dtor.pp}") for s <- sels.toSeq.sortBy(_.exprId) do tl.log(s"\t${s.pp}") case FinalDestSel(dtors, field) => tl.log(s"\t${field}") tl.log("<<< fusing <<<") end DeforestFusionSolver object Deforest: def apply(p: Program)(using cfg: Config, tl: TL, raise: Raise, eState: Elaborator.State, ): Program = // TODO: handle see through imported modules val flowAnalysisRes = FlowAnalysis(p, mono = cfg.deforest.exists(_.mono)) val solver = new DeforestFusionSolver(flowAnalysisRes) if solver.finalCtorDests.isEmpty && solver.finalDtorSrcs.isEmpty then p else val rewrite = new DeforestRewriter(solver) rewrite() ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala ================================================ package hkmc2 package codegen package deforest import utils.* import mlscript.utils.*, shorthands.* import semantics.* import syntax.Tree import scala.collection.mutable.{Set as MutSet, Map as MutMap, LinkedHashMap, Buffer} import hkmc2.syntax.Fun import hkmc2.codegen.flowAnalysis.* class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): def apply(): Program = if newBody is pre.pgrm.main then pre.pgrm else Program(pre.pgrm.imports, newBody) val collector = solver.constraintSolver.collector given tl: TraceLogger = solver.tl given fState: FlowAnalysis.State = solver.fState given eState: Elaborator.State = solver.eState given pre: FlowPreAnalyzer = solver.preAnalyzer type MatchOrLabelId = ResultId | LabelSymbol type BranchId = CtorDtorId -> Opt[CtorCls] type LabelId = LabelSymbol -> InstantiationId type RestFunId = CtorDtorId | LabelId extension (restFunId: RestFunId) def withoutInstId: MatchOrLabelId = restFunId match case CtorDtorId(exprId, instId) => exprId case l: LabelId => l._1 extension (restFunId: RestFunId) def getInstId = restFunId match case CtorDtorId(exprId, instId) => instId case l: LabelId => l._2 extension (matchOrLabelId: MatchOrLabelId) def withInstId(instId: InstantiationId): RestFunId = matchOrLabelId match case l: LabelSymbol => l -> instId case scrutId => CtorDtorId(scrutId.asInstanceOf[ResultId], instId) extension (vs: Ls[VarSymbol]) def asParamList: ParamList = ParamList(ParamListFlags.empty, vs.map(Param.simple), N) extension (c: CtorCls) def ctorClsName: String = c match case cls: ClassLikeSymbol => cls.nme case n: Int => s"tup$n" extension (f: SelField) def fieldName: String = f match case tSym: TermSymbol => tSym.nme case n: Int => n.toString private def getParentLabelOrMatchesAndRestBefore( matchOrLabelId: MatchOrLabelId ): (Iterator[Label | Match], Block) = val ctx = matchOrLabelId match case label: LabelSymbol => pre.res.labelSymToCtxOfLabel(label) case dtorId: ResultId => pre.res.matchScrutToCtxOfMatch(dtorId) val simpleRest = matchOrLabelId match case label: LabelSymbol => pre.res.labelSymToLabelBlk(label).rest case dtorId: ResultId => pre.res.matchScrutToMatchBlock(dtorId).rest def it = ctx.iterator .takeWhile: case _: (pre.InCtx.Fn | pre.InCtx.ModCtor | pre.InCtx.Cls | pre.InCtx.ClsPreCtor | pre.InCtx.ClsCtor | pre.InCtx.TopLvl) => false case _ => true .collect: case pre.InCtx.LblBody(l) => l case pre.InCtx.MtchBody(m, _) => m case pre.InCtx.BegnBody(b) => b val blockUntilParent = it .takeWhile(_.isInstanceOf[Begin]) .collect: case b: Begin => b.rest .foldLeft(simpleRest)(Begin.apply) val parents = it .collect: case l: Label => l case m: Match => m .asInstanceOf[Iterator[Label | Match]] parents -> blockUntilParent private val _symSubst = SymbolSubst.Id val newPolyFnSyms = LinkedHashMap.empty[InstantiationId, Map[TermSymbol, (BlockMemberSymbol, TermSymbol)]] val branchSelSyms = MutMap.empty[CtorDtorId, VarSymbol] // branch fun params for fields (which share the same symbol in `branchSelSyms`) val branchFunParamFieldSyms = MutMap.empty[BranchId, Ls[VarSymbol]] val ctorWhichBranch = MutMap.empty[CtorDtorId, BranchId] // Symbols of branch functions // the content of those functions should be // `; return match_rest(...)` val branchFunSyms = LinkedHashMap.empty[BranchId, (BlockMemberSymbol, TermSymbol)] // Symbols of rest functions for relevant matches or labels. // 1) Matches that will be fused or // 2) Matches or Labels that properly nest other fusing matches // should get their "rest"s extracted as functions, // and the content of those functions should be // `; return parent_rest(...)` val restFunSyms = LinkedHashMap.empty[RestFunId, (BlockMemberSymbol, TermSymbol)] // original bodies of a branch val branchOriginalBodies = MutMap.empty[ResultId -> Opt[CtorCls], Block] // original rest function bodies and their parent matches (if any) val restOriginalBodiesAndParentRest = MutMap.empty[MatchOrLabelId, Block -> Opt[MatchOrLabelId]] // compute new symbols locally { def mkNewPolyFnSyms(path: List[ResultId], refedFun: ResultId): Unit = val groupFuns = collector.funToSccGroups(refedFun.getReferredFun.get) newPolyFnSyms.getOrElseUpdate( path, groupFuns .map: f => val name = path.mkFunName + s"$$${f.nme}" f -> ( new BlockMemberSymbol(name, Nil, true), new TermSymbol(Fun, N, Tree.Ident(name))) .toMap) end mkNewPolyFnSyms for case (ctor, finalDest) <- solver.finalCtorDests do finalDest match case FinalDestSel(dtors, field) => // create poly fun syms val instIds = (dtors + ctor).toList.sortBy(_.exprId).map(_.instId) for ctorInstId <- instIds case path@(pathTo :+ refedFun) <- ctorInstId.inits // skip synthesized instIds — those rewrite in-place if !collector.synthesizedInstIdToFunSym.contains(path) do mkNewPolyFnSyms(path, refedFun) case FinalDestMatch(dest, sels) => val ctorInfo = solver.fusingCtorInfo(ctor) // create poly fun syms for ctorInstId <- List(ctor.instId, dest.instId) case path@(pathTo :+ refedFun) <- ctorInstId.inits // skip synthesized instIds — those rewrite in-place if !collector.synthesizedInstIdToFunSym.contains(path) do mkNewPolyFnSyms(path, refedFun) // create branch sel syms val fieldSym = MutMap.empty[SelField, VarSymbol] for sel <- sels.toList.sortBy(_._1) do branchSelSyms.getOrElseUpdate( sel, locally: val selInfo = solver.fusingDtorInfo(sel).asInstanceOf[FieldSel] val clsNme = selInfo.selectsFrom.ctorClsName fieldSym.getOrElseUpdate( selInfo.field, new VarSymbol(Tree.Ident(s"${clsNme}_${selInfo.field.fieldName}"))) ) // ctor dest branch function computations val matchBlk = pre.res.matchScrutToMatchBlock(dest._1) val (whichBranch, whichBranchPreBody) = val tmp = val ctorCls = ctorInfo.ctor matchBlk.arms .find: (cse, _) => cse match case Case.Cls(cls, path) => cls === ctorCls case Case.Tup(len, inf) => len === ctorCls case _ => die .map(b => ctorCls -> b._2) tmp.map(_._1) -> tmp.fold(matchBlk.dflt.get)(_._2) val destBranchId: BranchId = dest -> whichBranch // identify the dest branchid for a ctor ctorWhichBranch(ctor) = destBranchId // compute the function symbols for branch funs branchFunSyms.getOrElseUpdate( destBranchId, locally: val branchName = whichBranch.fold("_dflt")(c => s"_${c.ctorClsName}") val scrutName = dest._1.getReferredSym.nme val branchFnNme = s"${dest.instId.mkFunName}$$$scrutName$branchName" new BlockMemberSymbol(branchFnNme, Nil, true) -> new TermSymbol(Fun, N, Tree.Ident(branchFnNme)) ) // compute the function parameters corresponding to ctor fields of branch funs branchFunParamFieldSyms.getOrElseUpdate( destBranchId, locally: val completeArgs: Ls[SelField] = ctorInfo.args.unzip._1 val selsInfos: Map[SelField, CtorDtorId] = sels .iterator .map: sel => solver.fusingDtorInfo(sel).asInstanceOf[FieldSel].field -> sel .toMap completeArgs.map: selField => selsInfos.get(selField) match case Some(selId) => branchSelSyms(selId) case None => VarSymbol(Tree.Ident(s"_${selField.fieldName}")) ) val (parents, _) = getParentLabelOrMatchesAndRestBefore(dest.exprId) for needRest <- Iterator.single(pre.res.matchScrutToMatchBlock(dest._1)) ++ parents do val (matchOrLabelId, nme) = needRest match case Match(scrut, arms, dflt, rest) => scrut.uid -> scrut.uid.getReferredSym.nme case Label(label, loop, body, rest) => label -> label.nme val restFunId = matchOrLabelId.withInstId(dest.instId) restFunSyms.getOrElseUpdate( restFunId, locally: val restFunName = dest.instId.mkFunName + s"$$${nme}_rest" new BlockMemberSymbol(restFunName, Nil, true) -> new TermSymbol(Fun, N, Tree.Ident(restFunName)) ) val (ps, restBeforeParent) = getParentLabelOrMatchesAndRestBefore(matchOrLabelId) restOriginalBodiesAndParentRest.getOrElseUpdate( matchOrLabelId, restBeforeParent -> ps.nextOption().map: case Match(scrut, arms, dflt, rest) => scrut.uid case Label(label, loop, body, rest) => label ) // compute the complete deforestable branch body of a fusing match branchOriginalBodies.getOrElseUpdate( dest._1 -> whichBranch, whichBranchPreBody ) } // compute free vars after we know new symbols val dtorBranchFnFvs = MutMap.empty[CtorDtorId, Ls[Symbol]] val restFnFvs = MutMap.empty[RestFunId, Ls[Symbol]] locally { val allBranchesOfDtor = branchFunSyms.keys.groupBy(_._1) extension (b: Block) // ctx should be the branch fun parameters corresponding to ctor fields def deforestFreeVars( ctx: collection.Set[Symbol], instId: InstantiationId ): collection.Set[Symbol] = val traverser = new FreeVarTraverser(ctx, instId) traverser.applyBlock(b) traverser.freeVars class FreeVarTraverser(ctx: collection.Set[Symbol], instId: InstantiationId) extends BlockTraverser: extension (resId: ResultId) def toCtorDtorId = CtorDtorId(resId, instId) val inCtx = MutSet.from[Symbol]: ctx ++ newPolyFnSyms.values.flatMap(_.values.unzip._1) ++ branchFunSyms.values.unzip._1 ++ eState.builtinOpsMap.values ++ (eState.globalThisSymbol :: eState.runtimeSymbol :: eState.noSymbol :: Nil) ++ locally: pre.pgrm.main match case Scoped(syms, _) => syms case _ => Nil val freeVars = MutSet.empty[Symbol] private def handleTrackableSel(s: Result) = val toBeSubstSymbol = branchSelSyms(s.uid.toCtorDtorId) if !inCtx(toBeSubstSymbol) then freeVars.add(toBeSubstSymbol) override def applyValue(v: Value): Unit = v match case Value.Ref(l, disamb) if !inCtx(l) && l.asClsLike.isEmpty => freeVars.add(l) case _ => super.applyValue(v) override def applyResult(r: Result): Unit = r match case s@TrackableSelect(_, _, _) if branchSelSyms.isDefinedAt(s.uid.toCtorDtorId) => handleTrackableSel(s) case Lambda(params, body) => for p <- params.allParams do inCtx.add(p.sym) applyBlock(body) for p <- params.allParams do inCtx.remove(p.sym) case _ => super.applyResult(r) override def applyPath(p: Path): Unit = p match case s@TrackableSelect(_, _, _) if branchSelSyms.isDefinedAt(s.uid.toCtorDtorId) => handleTrackableSel(s) case _ => super.applyPath(p) override def applyBlock(b: Block): Unit = b match case m: Match if solver.finalDtorSrcs.isDefinedAt(m.scrut.uid.toCtorDtorId) => for fv <- fvsForDtor(m.scrut.uid.toCtorDtorId) if !inCtx(fv) do freeVars.add(fv) super.applyPath(m.scrut) case Assign(lhs, rhs, rest) => if !inCtx(lhs) then freeVars.add(lhs) applyResult(rhs) applyBlock(rest) case Scoped(syms, body) => for s <- syms do inCtx.add(s) applyBlock(body) for s <- syms do inCtx.remove(s) case _ => super.applyBlock(b) override def applyDefn(defn: Defn): Unit = defn match case fDef: FunDefn => inCtx.add(fDef.sym) for p <- fDef.params.flatMap(_.allParams) do inCtx.add(p.sym) applyBlock(fDef.body) for p <- fDef.params.flatMap(_.allParams) do inCtx.remove(p.sym) case cDef: ClsLikeDefn => inCtx.add(cDef.sym) val ps = (cDef.auxParams ++ cDef.paramsOpt).flatMap(_.params).map(_.sym) for p <- ps do inCtx.add(p) super.applyDefn(cDef) for p <- ps do inCtx.remove(p) case vDef: ValDefn => inCtx.add(vDef.sym) super.applyDefn(defn) end FreeVarTraverser def fvsForDtor(dtorId: CtorDtorId): Ls[Symbol] = dtorBranchFnFvs.get(dtorId) match case Some(fvs) => fvs case None => val fvsOfBranches = allBranchesOfDtor(dtorId) .flatMap: branchId => branchOriginalBodies(branchId._1._1 -> branchId._2) .deforestFreeVars( branchFunParamFieldSyms(branchId).toSet, branchId._1._2) .toSortedSet(using Ordering.by[Symbol, Uid[Symbol]](_.uid)) .toList val fvsOfRest = fvsForRest(dtorId) val fvs = fvsOfBranches ++ fvsOfRest dtorBranchFnFvs(dtorId) = fvs fvs def fvsForRest(restFunId: RestFunId): Ls[Symbol] = restFnFvs.get(restFunId) match case Some(fvs) => fvs case None => val instId = restFunId.getInstId val (restBody, parentRest) = restOriginalBodiesAndParentRest(restFunId.withoutInstId) val fvOfRestBody = restBody.deforestFreeVars(Set.empty, restFunId.getInstId).toList.sortBy(_.uid) val fvOfRestParent = parentRest.fold(List.empty)(pr => fvsForRest(pr.withInstId(instId))) val fvs = fvOfRestBody ++ fvOfRestParent restFnFvs(restFunId) = fvs fvs allBranchesOfDtor.keysIterator.foreach(fvsForDtor) } // compute new program body val newBody = extension (a: Symbol) def toValueRef = a match case bms: BlockMemberSymbol => Value.Ref(bms, bms.tsym) case _ => Value.Ref(a, N) def mkReturnCall(target: (BlockMemberSymbol, TermSymbol), args: Ls[Symbol]): Block = Return(Call( Value.Ref(target._1, S(target._2)), args.map(a => Arg(N, a.toValueRef)) ne_:: Nil )(true, false, false), false) class Rewriter(instId: InstantiationId) extends BlockTransformer(_symSubst): extension (resId: ResultId) def toCtorDtorId = CtorDtorId(resId, instId) private def ctorLamFvs(ctorId: CtorDtorId): Ls[VarSymbol] = // only for ctors that are fused with a match val dtorId = solver.finalCtorDests(ctorId).asInstanceOf[FinalDestMatch].dtor dtorBranchFnFvs(dtorId).map(s => new VarSymbol(Tree.Ident(s"fv_ctorLam_${s.nme}"))) private def newRefId(refId: ResultId, refSym: TermSymbol) = instId match case Nil => refId :: Nil case pathTo :+ called => val lastRefedSymbol = called.getReferredFun.get val funToSccRepMap = collector.funToSccRep (funToSccRepMap(lastRefedSymbol), funToSccRepMap(refSym)) match case (Some(a), Some(b)) if a is b => instId case _ => instId :+ refId case _ => die override def applyResult(r: Result)(k: Result => Block): Block = r match case s@TrackableSelect(from, _, _) => if branchSelSyms.isDefinedAt(s.uid.toCtorDtorId) then k(Value.Ref(branchSelSyms(s.uid.toCtorDtorId))) else if solver.finalDtorSrcs.contains(s.uid.toCtorDtorId) then applyPath(from)(k) else super.applyResult(r)(k) case ctor@CtorCall(cls, args) => def mkCtorFieldSyms(ctorDtorId: CtorDtorId): Ls[TempSymbol] = val ctorInfo = solver.fusingCtorInfo(ctorDtorId) val clsNme = ctorInfo.ctor.ctorClsName ctorInfo.args.unzip._1.map: f => new TempSymbol(N, s"${clsNme}_${f.fieldName}") end mkCtorFieldSyms solver.finalCtorDests.get(ctor.uid.toCtorDtorId) match case None => super.applyResult(ctor)(k) case Some(FinalDestSel(_, field)) => val ctorInfo = solver.fusingCtorInfo(ctor.uid.toCtorDtorId) val idx = ctorInfo.args.unzip._1.indexOf(field) val fieldSyms = mkCtorFieldSyms(ctor.uid.toCtorDtorId) args.zip(fieldSyms).foldRight(k(Value.Ref(fieldSyms(idx)))): case (Arg(N, a) -> s, rest) => applyPath(a): fusedField => Scoped(Set.single(s), Assign(s, fusedField, rest)) case _ => TODO("spread args are not supported") case Some(_: FinalDestMatch) => val fieldSyms = mkCtorFieldSyms(ctor.uid.toCtorDtorId) val (branchBms, branchTermSym) = branchFunSyms(ctorWhichBranch(ctor.uid.toCtorDtorId)) val ctorLamParams = ctorLamFvs(ctor.uid.toCtorDtorId) val callBranchFun = Lambda( ctorLamParams.asParamList, mkReturnCall((branchBms, branchTermSym), ctorLamParams ++ fieldSyms)) args.zip(fieldSyms).foldRight(k(callBranchFun)): case (Arg(N, a) -> fieldSym, rest) => applyPath(a): fusedField => Scoped(Set.single(fieldSym), Assign(fieldSym, fusedField, rest)) case _ => TODO("spread args are not supported") case _ => super.applyResult(r)(k) override def applyPath(p: Path)(k: Path => Block): Block = p match case ref@FunRef(f) if newPolyFnSyms.isDefinedAt(newRefId(ref.uid, f)) => val (bms, tSym) = newPolyFnSyms(newRefId(ref.uid, f))(f) k(Value.Ref(bms, S(tSym))) case ctor@CtorCall(_, args) if solver.finalCtorDests.isDefinedAt(ctor.uid.toCtorDtorId) => assert(args.isEmpty) val (branchBms, branchTermSym) = branchFunSyms(ctorWhichBranch(ctor.uid.toCtorDtorId)) val ctorLamParams = ctorLamFvs(ctor.uid.toCtorDtorId) val lambdaSym = new TempSymbol(N, "deforest$lam") Scoped( Set.single(lambdaSym), Assign( lambdaSym, Lambda( ctorLamParams.asParamList, mkReturnCall((branchBms, branchTermSym), ctorLamParams)), k(Value.Ref(lambdaSym, N))) ) case s@TrackableSelect(from, _, _) => if branchSelSyms.isDefinedAt(s.uid.toCtorDtorId) then k(Value.Ref(branchSelSyms(s.uid.toCtorDtorId))) else if solver.finalDtorSrcs.contains(s.uid.toCtorDtorId) then applyPath(from)(k) else super.applyPath(s)(k) case _ => super.applyPath(p)(k) override def applyBlock(b: Block): Block = b match case m@Match(scrut, _, _, _) if solver.finalDtorSrcs.isDefinedAt(scrut.uid.toCtorDtorId) => val callWithFvs = dtorBranchFnFvs(scrut.uid.toCtorDtorId) applyPath(scrut): newScrut => Return( Call(newScrut, callWithFvs.map(s => Arg(N, s.toValueRef)) ne_:: Nil)(true, false, false), false) case Break(label) => val labelRestFunId = label.withInstId(instId) restFunSyms.get(labelRestFunId) match case None => super.applyBlock(b) case Some(labelRestFunSym) => val labelRestFunFvs = restFnFvs(labelRestFunId) mkReturnCall(labelRestFunSym, labelRestFunFvs) case Return(res, true) => super.applyBlock(Return(res, false)) case _ => super.applyBlock(b) end Rewriter class RefreshSymbol(existingMapping: Map[Symbol, Symbol]) extends SymbolRefresher(existingMapping): override def applyScopedBlock(b: Block): Block = b match case Scoped(syms, body) => syms.foreach: sym => sym match case bms: BlockMemberSymbol => assert(bms.tsym.forall(_.owner.isEmpty)) case _ => case _ => super.applyScopedBlock(b) override def applyBlock(b: Block): Block = b match case Label(label, loop, body, rest) => assert(!loop) case Continue(label) => TODO("unsupported `continue` instruction during rewriting") case _ => super.applyBlock(b) override def applyValue(v: Value)(k: Value => Block): Block = v match case Value.Ref(l, x) => pre.res.modSymToBms.get(l) match case Some(bms) => k(Value.Ref(bms, l.asMod)) case None => super.applyValue(v)(k) case _ => super.applyValue(v)(k) end RefreshSymbol val newPolyFuns = for (instId, funSymMap) <- newPolyFnSyms (referringFun, (bms, tSym)) <- funSymMap.toList.sortBy(_._1.uid) yield val fDefn = pre.res.funSymToFunDefn(referringFun) val transformedBody = new Rewriter(instId).applyBlock(fDefn.body) // refresh other local symbols: for funs, we can check existing scoped blocks and // there is no need to add scoped blocks, because function bodies now already are scoped val refreshParamMap = MutMap.empty[VarSymbol, VarSymbol] val refreshedParams = fDefn.params.map: pl => ParamList( pl.flags, pl.params.map: p => val newSym = new VarSymbol(Tree.Ident(p.sym.name)) refreshParamMap(p.sym) = newSym Param(p.flags, newSym, p.sign, p.modulefulness), pl.restParam) val bodyWithCorrectSymbols = new RefreshSymbol(refreshParamMap.toMap).applyBlock(transformedBody) FunDefn( N, bms, tSym, refreshedParams, bodyWithCorrectSymbols)(N, fDefn.annotations) end newPolyFuns val newBranchFuns = for (branchId@(dtorId, whichBranch), (bms, tSym)) <- branchFunSyms yield val instId = dtorId.getInstId val ogBody = branchOriginalBodies(dtorId.exprId -> whichBranch) val restFunSym = restFunSyms(dtorId) val restFunArgs = restFnFvs(dtorId) val actualBody = Begin( new Rewriter(instId).applyBlock(ogBody), mkReturnCall(restFunSym, restFunArgs)) val refreshedFvSymbols = dtorBranchFnFvs(branchId._1).map(s => s -> new VarSymbol(Tree.Ident(s"fv_${s.nme}"))) val bodyWithCorrectSymbols = new RefreshSymbol(refreshedFvSymbols.toMap).applyBlock(actualBody) FunDefn(N, bms, tSym, (refreshedFvSymbols.unzip._2 ++ branchFunParamFieldSyms(branchId)).asParamList :: Nil, bodyWithCorrectSymbols )(N, annotations = Nil) end newBranchFuns val newRestFuns = for (restFunId, (bms, tsym)) <- restFunSyms yield val instId = restFunId.getInstId val (ogBody, parent) = restOriginalBodiesAndParentRest(restFunId.withoutInstId) val transformedOgBody = new Rewriter(instId).applyBlock(ogBody) val actualBody = parent match case Some(parentRestId) => val parentRestFunId = parentRestId.withInstId(instId) val parentFunSym = restFunSyms(parentRestFunId) val parentFunFvs = restFnFvs(parentRestFunId) Begin( transformedOgBody, mkReturnCall(parentFunSym, parentFunFvs)) case None => Begin(transformedOgBody, Return(Value.Lit(Tree.UnitLit(true)), false)) val refreshedFvSymbols = restFnFvs(restFunId).map(s => s -> new VarSymbol(Tree.Ident(s"fv_${s.nme}"))) val bodyWithCorrectSymbols = new RefreshSymbol(refreshedFvSymbols.toMap).applyBlock(actualBody) FunDefn(N, bms, tsym, refreshedFvSymbols.unzip._2.asParamList :: Nil, bodyWithCorrectSymbols)(N, annotations = Nil) end newRestFuns val inplaceRewrittenFunBodies = Map.from[TermSymbol, Block]: for (selfInstId, funSym) <- collector.synthesizedInstIdToFunSym yield val fDefn = pre.res.funSymToFunDefn(funSym) funSym -> new Rewriter(selfInstId).applyBlock(fDefn.body) val newMainBody = object mainRewriter extends Rewriter(Nil): override def applyFunDefn(fun: FunDefn): FunDefn = inplaceRewrittenFunBodies.get(fun.dSym) match case Some(rewrittenBody) => FunDefn(fun.owner, fun.sym, fun.dSym, fun.params, rewrittenBody)(fun.configOverride, fun.annotations) case None => super.applyFunDefn(fun) object implicitRetPass extends BlockTransformerShallow(_symSubst): override def applyBlock(b: Block): Block = b match case Return(res, false) => Return(res, true) case _ => super.applyBlock(b) Scoped( Set.from(newPolyFuns.map(_.sym) ++ newBranchFuns.map(_.sym) ++ newRestFuns.map(_.sym)), implicitRetPass.applyBlock(mainRewriter.applyBlock(pre.pgrm.main))) (newPolyFuns ++ newBranchFuns ++ newRestFuns).foldRight(newMainBody): (fdef, rest) => Define(fdef, rest) end newBody end DeforestRewriter ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala ================================================ package hkmc2 package codegen package flowAnalysis import scala.jdk.CollectionConverters.MapHasAsScala import utils.* import mlscript.utils.*, shorthands.* import semantics.* import syntax.Tree import scala.collection.mutable import scala.collection.mutable.{Set as MutSet, Map as MutMap, LinkedHashMap, LinkedHashSet} object FlowAnalysis: class State: val resultToResultId = new java.util.IdentityHashMap[Result, Uid[Result]].asScala val resultIdToResult = mutable.Map.empty[Uid[Result], Result] object ResultUidState extends Uid.Result.State extension (instId: InstantiationId) def mkFunName(using Elaborator.State): String = instId .map: i => s"${i.getReferredFun.get.name}_$i" .mkString("_") extension (resultId: ResultId) def getResult = resultIdToResult(resultId) def getReferredSym: Symbol = resultId.getResult match case Value.Ref(s, _) => s case e => lastWords(s"assumption failed: $e is not a Value.Ref") def getReferredFun(using Elaborator.State): Option[TermSymbol] = resultId.getResult match case FunRef(f) => Some(f) case _ => None extension (r: Result) def uid = resultToResultId.get(r) match case None => val id = ResultUidState.nextUid resultIdToResult(id) = r resultToResultId(r) = id id case Some(id) => id def apply(pgrm: Program, mono: Bool)(using TraceLogger, Elaborator.State, Raise) = given State = new State val pre = new FlowPreAnalyzer(pgrm) val constrCol = new FlowConstraintsCollector(pre, mono) new FlowConstraintSolver(constrCol) end FlowAnalysis type ResultId = Uid[Result] type InstantiationId = Ls[ResultId] type CtorCls = ClassLikeSymbol | Int type SelField = TermSymbol | Int type FunId = (funSym: Symbol, whichParamList: Int) | ResultId object RefLike: private def classCtorSymbol(sym: Symbol)(using Elaborator.State): Opt[ClassSymbol | ModuleOrObjectSymbol] = sym.asObj orElse sym.asTrm.flatMap: tSym => for cls <- tSym.owner.flatMap(_.asCls) clsDef <- cls.defn ctorSym <- clsDef.ctorSym if ctorSym is tSym yield cls def unapply(p: Value.Ref | Select)(using Elaborator.State): Opt[Symbol] = p match case Value.Ref(l, disamb) => val sym = disamb.getOrElse(l) classCtorSymbol(sym) orElse S(sym) case s: Select => s.symbol.flatMap: selSym => classCtorSymbol(selSym) orElse locally: for selTermSym <- selSym.asTrm owner <- selTermSym.owner _ <- owner.asMod yield selTermSym object TrackableFieldSelect: def unapply(s: Select): Opt[Path -> (field: TermSymbol, owner: ClassSymbol)] = s.symbol match case S(sSym) if sSym.asTrm.exists(_.decl.exists(_.isInstanceOf[Param])) => val tSym = sSym.asTrm.get for owner <- tSym.owner cls <- owner.asCls if cls.tree.clsParams.size === 1 yield s.qual -> (tSym, cls) case _ => N object PossibleTrackableTupleSelect: def unapply(s: Result)(using eState: Elaborator.State): Opt[Value.Ref -> Int] = s match case Call( Select(Select(Value.Ref(runtimeSym, N), Tree.Ident("Tuple")), Tree.Ident("get")), (Arg(N, ref@Value.Ref(scrut, N)) :: Arg(N, Value.Lit(Tree.IntLit(n))) :: Nil) :: Nil ) if runtimeSym is eState.runtimeSymbol => S(ref -> n.toInt) case _ => N object TrackableSelect: def unapply(s: Result)(using pre: FlowPreAnalyzer, eState: Elaborator.State): Opt[(from: Path, field: SelField, owner: CtorCls)] = given fState: FlowAnalysis.State = pre.fState s match case sel@PossibleTrackableTupleSelect((ref@Value.Ref(scrut, N)) -> ith) => pre.res.getEnclosingMatchesForSel(sel.uid).find(_._1.getReferredSym is scrut).flatMap: case (_, Some(tupSize: Int)) => S(ref, ith, tupSize) case _ => N case TrackableFieldSelect(qual, field -> owner) => Some((qual, field, owner)) case _ => N object CtorRef: def unapply(s: Path)(using Elaborator.State): Option[ClassSymbol | ModuleOrObjectSymbol] = s match case RefLike(s) => s.asCls orElse s.asObj case _ => None object CtorCall: def unapply(r: Result)(using Elaborator.State): Option[(ClassSymbol | ModuleOrObjectSymbol | Int) -> Ls[Arg]] = r match case Instantiate(_, CtorRef(ctor), argss) => Some(ctor -> argss.flatten) case Call(CtorRef(ctor), argss) => Some(ctor -> argss.flatten) case CtorRef(ctor) if ctor.asObj.isDefined => Some(ctor -> Nil) case Tuple(_, args) => Some(args.size, args) case _ => None object FunRef: def unapply(s: Path)(using Elaborator.State): Option[TermSymbol] = s match case RefLike(tSym: TermSymbol) if tSym.k is syntax.Fun => Some(tSym) case _ => None type StratVarId = Uid[StratVar] class StratVarState(val uid: StratVarId, val name: Str, val generatedForFun: Opt[TermSymbol]): lazy val asProdStrat = ProdVar(this) lazy val asConsStrat = ConsVar(this) override def toString(): String = s"${if name.isEmpty() then "$stratvar" else name}@${uid}@$generatedForFun" object StratVarState: def freshVar(nme: String)(using vuid: Uid.StratVar.State): StratVarState = val newId = vuid.nextUid StratVarState(newId, nme, N) def freshVar(nme: String, generatedForFun: TermSymbol)(using vuid: Uid.StratVar.State): StratVarState = val newId = vuid.nextUid StratVarState(newId, s"${nme}_for_${generatedForFun.nme}", S(generatedForFun)) def freshVar(nme: String, forFunOpt: Opt[TermSymbol])(using vuid: Uid.StratVar.State): StratVarState = forFunOpt match case None => freshVar(nme) case Some(forFun) => freshVar(nme, forFun) trait StratVar(s: StratVarState): this: ProdVar | ConsVar => def asProdStrat = s.asProdStrat def asConsStrat = s.asConsStrat def uid = s.uid sealed abstract class ProdStrat case class ProdVar(s: StratVarState) extends ProdStrat with StratVar(s) class ProdFun( val funId: FunId, val instantiationId: Opt[InstantiationId] )( val params: Ls[ConsStrat], val restParam: Opt[ConsStrat], val res: ProdStrat ) extends ProdStrat: override def toString(): String = s"(${params.map(_.toString()).mkString(", ")}) -> ${res.toString()}" case object NoProd extends ProdStrat class Ctor( val exprId: ResultId, val instantiationId: Opt[InstantiationId] )( val ctor: CtorCls, val args: Ls[SelField -> ProdStrat] ) extends ProdStrat with ToCtorDtorId(exprId, instantiationId): override def toString(): String = s"$ctor(${args.map(_.toString()).mkString(", ")})" sealed abstract class ConsStrat case class ConsVar(s: StratVarState) extends ConsStrat with StratVar(s) class ConsFun( val exprId: ResultId, val instantiationId: Opt[InstantiationId] )( val params: Ls[ProdStrat], val res: ConsStrat ) extends ConsStrat: override def toString(): String = s"(${params.map(_.toString()).mkString(", ")}) -> ${res.toString()}" case object NoCons extends ConsStrat class FieldSel( val exprId: ResultId, val instantiationId: Opt[InstantiationId] )( val field: SelField, val selectsFrom: CtorCls, val consVar: ConsVar ) extends ConsStrat with ToCtorDtorId(exprId, instantiationId) class Dtor( val scrutExprId: ResultId, val instantiationId: Opt[InstantiationId] ) extends ConsStrat with ToCtorDtorId(scrutExprId, instantiationId) sealed trait ToCtorDtorId(exprId: ResultId, instId: Opt[InstantiationId]): def toCtorDtorId = CtorDtorId(exprId, instId.get) case class CtorDtorId(exprId: ResultId, instId: InstantiationId): def pp(using FlowAnalysis.State) = s"${exprId.getResult}" type ConcreteProducer = Ctor type ConcreteConsumer = Dtor | FieldSel class ProdStratScheme(val s: StratVarState, val constraints: Ls[ProdStrat -> ConsStrat]) class FlowPreAnalyzer(val pgrm: Program)(using val tl: TraceLogger, val eState: Elaborator.State, val fState: FlowAnalysis.State, val raise: Raise ) extends BlockTraverser: given stratVarUidState: Uid.StratVar.State = new Uid.StratVar.State import StratVarState.freshVar ctxTracker.inTopLvl: applyBlock(pgrm.main) object res: val primitiveStratVar = StratVarState.freshVar("unknown") val rootFunDefns = LinkedHashSet.empty[FunDefn] val funSymToFunDefn = MutMap.empty[TermSymbol, FunDefn] val matchScrutToMatchBlock = MutMap.empty[ResultId, Match] val labelSymToLabelBlk = MutMap.empty[Symbol, Label] val matchScrutToCtxOfMatch = MutMap.empty[ResultId, Ls[InCtx]] val labelSymToCtxOfLabel = MutMap.empty[Symbol, Ls[InCtx]] val selToCtxOfSel = MutMap.empty[ResultId, Ls[InCtx]] val modSymToBms = MutMap.empty[Symbol, BlockMemberSymbol] val generatedProdVars = MutMap.empty[Symbol, StratVarState] lazy val rootFunSyms: collection.Set[TermSymbol] = rootFunDefns.map(_.dSym).toSet def getEnclosingMatchesForSel(selExprId: ResultId) = selToCtxOfSel.get(selExprId).fold(Iterator.empty): _.iterator .collect: case InCtx.MtchBody(m, cse) => m.scrut.uid -> cse end res enum InCtx: case TopLvl() case Mod(mod: ClsLikeBody) case ModCtor(mod: ClsLikeBody) case Cls(cls: ClsLikeDefn) case ClsPreCtor(cls: ClsLikeDefn) case ClsCtor(cls: ClsLikeDefn) case Fn(f: FunDefn) case LblBody(l: Label) case MtchBody(m: Match, cse: Opt[CtorCls]) case BegnBody(b: Begin) case Scped(s: Scoped) private object ctxTracker: private var ctx: Ls[InCtx] = Nil private def isTopLvlLikeFunCtx(ctx0: Ls[InCtx]): Boolean = ctx0.forall: case InCtx.TopLvl() => true case InCtx.Mod(_) => true case InCtx.ModCtor(_) => true case InCtx.BegnBody(_) => true case InCtx.Scped(_) => true case _ => false def getAllCtx = ctx def isTopLvlLikeModuleCtx: Boolean = ctx.forall: case InCtx.TopLvl() => true case InCtx.BegnBody(_) => true case InCtx.Scped(_) => true case _ => false def registerStratVar(sym: Symbol, nme: String): Unit = val currentRootFun = ctx.tails.collectFirst: case InCtx.Fn(fun) :: tl if isTopLvlLikeFunCtx(tl) => fun.dSym res.generatedProdVars.getOrElseUpdate(sym, freshVar(nme, currentRootFun)) private inline def withCtx(newCtx: InCtx)(inline body: => Any)(after: => Unit = ()): Unit = ctx = newCtx :: ctx body ctx = ctx.tail after inline def inMod(mod: ClsLikeBody)(inline body: => Any) = withCtx(InCtx.Mod(mod))(body)() inline def inFun(fun: FunDefn)(inline body: => Any) = withCtx(InCtx.Fn(fun))(body): res.funSymToFunDefn(fun.dSym) = fun if isTopLvlLikeFunCtx(ctx) then res.rootFunDefns.add(fun) inline def inLabelBody(label: Label)(inline body: => Any) = withCtx(InCtx.LblBody(label))(body): res.labelSymToLabelBlk.addOne(label.label -> label) res.labelSymToCtxOfLabel.addOne(label.label -> ctx) inline def inMatchBody(m: Match, cse: Opt[CtorCls])(inline body: => Any) = withCtx(InCtx.MtchBody(m, cse))(body): res.matchScrutToMatchBlock.addOne(m.scrut.uid -> m) res.matchScrutToCtxOfMatch.addOne(m.scrut.uid -> ctx) inline def inBeginBody(begin: Begin)(inline body: => Any) = withCtx(InCtx.BegnBody(begin))(body)() inline def inScoped(scpd: Scoped)(inline body: => Any) = withCtx(InCtx.Scped(scpd))(body)() inline def inTopLvl(inline body: => Any) = assert(ctx.isEmpty) withCtx(InCtx.TopLvl())(body): assert(ctx.isEmpty) inline def inModCtor(mod: ClsLikeBody)(inline body: => Any) = assert(ctx.head.matches{ case _: InCtx.Mod => true }) withCtx(InCtx.ModCtor(mod))(body): assert(ctx.head.matches{ case _: InCtx.Mod => true }) inline def inCls(cls: ClsLikeDefn)(inline body: => Any) = withCtx(InCtx.Cls(cls))(body)() inline def inClsPreCtor(cls: ClsLikeDefn)(inline body: => Any) = withCtx(InCtx.ClsPreCtor(cls))(body)() inline def inClsCtor(cls: ClsLikeDefn)(inline body: => Any) = withCtx(InCtx.ClsCtor(cls))(body)() end ctxTracker override def applyBlock(b: Block): Unit = b match case scpd@Scoped(syms, body) => for s <- syms do s match case s: BlockMemberSymbol => () case _ => ctxTracker.registerStratVar(s, s.nme) ctxTracker.inScoped(scpd): applyBlock(body) case m@Match(scrut, arms, dflt, rest) => applyPath(scrut) for (cse, body) <- arms do val cseCls = cse match case Case.Cls(cls, _) => S(cls) case Case.Tup(n, false) => S(n) case _ => N ctxTracker.inMatchBody(m, cseCls): applyBlock(body) for dft <- dflt do ctxTracker.inMatchBody(m, N): applyBlock(dft) applyBlock(rest) case Return(res, implct) => applyResult(res) case lbl@Label(label, loop, body, rest) => ctxTracker.inLabelBody(lbl): applyBlock(body) applyBlock(rest) case bgn@Begin(sub, rest) => ctxTracker.inBeginBody(bgn): applyBlock(sub) applyBlock(rest) case Assign(lhs, rhs, rest) => applyResult(rhs) applyBlock(rest) case Define(defn, rest) => applyDefn(defn) applyBlock(rest) case Throw(exc) => applyResult(exc) case Break(label) => () case Continue(label) => () case TryBlock(sub, finallyDo, rest) => applyBlock(sub) applyBlock(finallyDo) applyBlock(rest) case AssignField(lhs, nme, rhs, rest) => applyPath(lhs) applyResult(rhs) applyBlock(rest) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => applyPath(lhs) applyPath(fld) applyResult(rhs) applyBlock(rest) case End(_) => () case Unreachable(_) => () override def applyResult(r: Result): Unit = r match case tupSel@PossibleTrackableTupleSelect(_, _) => res.selToCtxOfSel.addOne(tupSel.uid -> ctxTracker.getAllCtx) case Call(fun, argss) => applyPath(fun) argss.foreach(_.foreach(applyArg)) case Instantiate(mut, cls, argss) => applyPath(cls) argss.foreach(_.foreach(applyArg)) case l: Lambda => applyLam(l) case Tuple(mut, elems) => elems.foreach(applyArg) case Record(_, fields) => fields.foreach: case RcdArg(idx, value) => idx.foreach(applyPath); applyPath(value) case p: Path => applyPath(p) override def applyPath(p: Path): Unit = p match case DynSelect(qual, fld, arrayIdx) => applyPath(qual); applyPath(fld) case p@TrackableFieldSelect(qual, _ -> _) => res.selToCtxOfSel.addOne(p.uid -> ctxTracker.getAllCtx) applyPath(qual) case p: Select => super.applyPath(p) case v: Value => applyValue(v) override def applyValue(v: Value): Unit = v match case Value.Ref(l, disamb) => () case Value.This(sym) => () case Value.Lit(lit) => () override def applyFunDefn(fun: FunDefn): Unit = ctxTracker.inFun(fun): ctxTracker.registerStratVar(fun.dSym, fun.sym.nme) fun.params.foreach(applyParamList) applyBlock(fun.body) override def applyValDefn(defn: ValDefn): Unit = ctxTracker.registerStratVar(defn.tsym, defn.tsym.nme) applyPath(defn.rhs) override def applyParamList(pl: ParamList): Unit = pl.params.foreach(p => ctxTracker.registerStratVar(p.sym, p.sym.nme)) pl.restParam.foreach(p => ctxTracker.registerStratVar(p.sym, p.sym.nme)) override def applyArg(arg: Arg): Unit = applyPath(arg.value) override def applyDefn(defn: Defn): Unit = defn match case defn: FunDefn => applyFunDefn(defn) case defn: ValDefn => applyValDefn(defn) case cls@ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) => ctxTracker.inCls(cls): paramsOpt.foreach(applyParamList) auxParams.foreach(applyParamList) privateFields.foreach(tsym => ctxTracker.registerStratVar(tsym, tsym.nme)) publicFields.foreach: (_, tsym) => ctxTracker.registerStratVar(tsym, tsym.nme) methods.foreach(applyFunDefn) ctxTracker.inClsPreCtor(cls): applyBlock(preCtor) ctxTracker.inClsCtor(cls): applyBlock(ctor) for b: ClsLikeBody <- mod do if ctxTracker.isTopLvlLikeModuleCtx then res.modSymToBms(b.isym) = sym ctxTracker.inMod(b): b.privateFields.foreach(tsym => ctxTracker.registerStratVar(tsym, tsym.nme)) b.publicFields.foreach: (_, tsym) => ctxTracker.registerStratVar(tsym, tsym.nme) b.methods.foreach(applyFunDefn) ctxTracker.inModCtor(b): applyBlock(b.ctor) override def applyCompanionModule(b: ClsLikeBody): Unit = lastWords("handled inline in `applyDefn`") end FlowPreAnalyzer class FlowConstraintsCollector(val preAnalyzer: FlowPreAnalyzer, val mono: Bool): given FlowPreAnalyzer = preAnalyzer given Uid.StratVar.State = preAnalyzer.stratVarUidState given Raise = preAnalyzer.raise given fState: FlowAnalysis.State = preAnalyzer.fState given eState: Elaborator.State = preAnalyzer.eState given tl: TraceLogger = preAnalyzer.tl import StratVarState.freshVar private class ConstraintsCollector(val forFunGroup: Opt[TermSymbol]): var constraints = Ls.empty[ProdStrat -> ConsStrat] val instId: Opt[InstantiationId] = forFunGroup.fold(S(Nil))(_ => N) def constrain(p: ProdStrat, c: ConsStrat) = constraints ::= p -> c def constrain(cs: Iterable[ProdStrat -> ConsStrat]) = constraints :::= cs.toList private val globalCollector = new ConstraintsCollector(N) def allConstraints = globalCollector.constraints val funToSccGroups = MutMap.empty[TermSymbol, Ls[TermSymbol]] def funToSccRep(tSym: TermSymbol): Option[TermSymbol] = funToSccGroups.get(tSym).map(_.head) // for fusing strictly internal parts of functions val synthesizedInstIdToFunSym = LinkedHashMap.empty[InstantiationId, TermSymbol] private val generatedProdVars: collection.Map[Symbol, StratVarState] = preAnalyzer.res.generatedProdVars.withDefaultValue(preAnalyzer.res.primitiveStratVar) locally { val funsToProdStratScheme = MutMap.empty[TermSymbol, ProdStratScheme] if !mono then // compute scc val sccInOrder: Ls[Ls[TermSymbol]] = import algorithms.partitionScc var edges = Ls.empty[(TermSymbol, TermSymbol)] for f <- preAnalyzer.res.rootFunDefns do object CollectAllReferredFun extends BlockTraverser: override def applyPath(p: Path) = p match case FunRef(callee) => if preAnalyzer.res.rootFunDefns.exists(_.dSym is callee) then edges = (f.dSym -> callee) :: edges case _ => () CollectAllReferredFun.applyBlock(f.body) partitionScc( edges, preAnalyzer.res.rootFunDefns.map(_.dSym) ).reverse end sccInOrder for group <- sccInOrder f <- group do funToSccGroups(f) = group // compute strat scheme for each scc group for groupedFuns <- sccInOrder do (new ConstraintsCollector(Some(funToSccRep(groupedFuns.head).get))).givenIn: cc ?=> for funSym <- groupedFuns do val fun = preAnalyzer.res.funSymToFunDefn(funSym) val thisFunVar = generatedProdVars(fun.dSym) val funProdStrat = processHandleableFun( s"${funSym.nme}_res", fun.params, fun.body, (fun.dSym, -1)) cc.constrain(funProdStrat, thisFunVar.asConsStrat) for funSym <- groupedFuns do funsToProdStratScheme(funSym) = ProdStratScheme(generatedProdVars(funSym), cc.constraints) end if // collect constraints from the top-level block globalCollector.givenIn: cc ?=> cc.constrain(preAnalyzer.res.primitiveStratVar.asProdStrat, NoCons) cc.constrain(NoProd, preAnalyzer.res.primitiveStratVar.asConsStrat) processBlock(preAnalyzer.pgrm.main)(using cc, NoCons) if mono then for fun <- preAnalyzer.res.rootFunDefns if fun.visibility is Visibility.Public do cc.constrain(generatedProdVars(fun.dSym).asProdStrat, NoCons) else for fun <- preAnalyzer.res.rootFunDefns do val funSym = fun.dSym val pScheme = funsToProdStratScheme(funSym) val synthesizedRefUid = Value.Ref(preAnalyzer.res.funSymToFunDefn(funSym).sym, S(funSym)).uid val selfProd = pScheme.instantiate(synthesizedRefUid, funSym) cc.constrain(selfProd, NoCons) val selfInstId = synthesizedRefUid :: Nil synthesizedInstIdToFunSym(selfInstId) = funSym // ========================= extension (pScheme: ProdStratScheme) def instantiate( referSite: ResultId, referringTo: TermSymbol )(using cc: ConstraintsCollector): ProdVar = val groupRep: TermSymbol = funToSccRep(referringTo).get val stratVarMap = MutMap.empty[StratVarState, StratVarState] def updateInstantiationId(instId: Opt[InstantiationId]) = S(instId.fold(referSite :: Nil)(referSite :: _)) def duplicateVarState(s: StratVarState) = if s.generatedForFun.fold(false): forFun => funToSccRep(forFun).fold(false)(_ is groupRep) then stratVarMap.getOrElseUpdate(s, freshVar(s.name, cc.forFunGroup)) else s def duplicateProdStrat(s: ProdStrat): ProdStrat = s match case ProdVar(s) => duplicateVarState(s).asProdStrat case p: ProdFun => new ProdFun(p.funId, updateInstantiationId(p.instantiationId))( p.params.map(duplicateConsStrat), p.restParam.map(duplicateConsStrat), duplicateProdStrat(p.res)) case NoProd => NoProd case c: Ctor => new Ctor(c.exprId, updateInstantiationId(c.instantiationId))( c.ctor, c.args.map((a, b) => a -> duplicateProdStrat(b))) def duplicateConsStrat(c: ConsStrat): ConsStrat = c match case ConsVar(s) => duplicateVarState(s).asConsStrat case c: ConsFun => new ConsFun(c.exprId, updateInstantiationId(c.instantiationId))( c.params.map(duplicateProdStrat), duplicateConsStrat(c.res)) case NoCons => NoCons case fSel: FieldSel => new FieldSel(fSel.exprId, updateInstantiationId(fSel.instantiationId))( fSel.field, fSel.selectsFrom, duplicateVarState(fSel.consVar.s).asConsStrat) case dtor: Dtor => new Dtor(dtor.scrutExprId, updateInstantiationId(dtor.instantiationId)) val newProd = duplicateVarState(pScheme.s).asProdStrat pScheme.constraints.foreach: (p, c) => cc.constrain(duplicateProdStrat(p), duplicateConsStrat(c)) newProd enum OpaqueBoundary: case Input, Output, Both extension (v: StratVarState) def constrainOpaque(boundary: OpaqueBoundary)(using cc: ConstraintsCollector): Unit = boundary match case OpaqueBoundary.Input => cc.constrain(NoProd, v.asConsStrat) case OpaqueBoundary.Output => cc.constrain(v.asProdStrat, NoCons) case OpaqueBoundary.Both => cc.constrain(NoProd, v.asConsStrat) cc.constrain(v.asProdStrat, NoCons) def mkFunProdStrat( params: Ls[ParamList], rootFunId: FunId, res: ProdStrat )(using cc: ConstraintsCollector): ProdStrat = def paramListFunId(whichParamList: Int): FunId = rootFunId match case (sym: Symbol, _) => (sym, whichParamList) case lambdaExprId: ResultId => assert(whichParamList == 0) lambdaExprId params.zipWithIndex.foldRight[ProdStrat](res): case ((ps, whichParamList), acc) => new ProdFun(paramListFunId(whichParamList), cc.instId)( ps.params.map(p => generatedProdVars(p.sym).asConsStrat), ps.restParam.map(p => generatedProdVars(p.sym).asConsStrat), acc) def processHandleableFun( resName: String, params: Ls[ParamList], body: Block, rootFunId: FunId )(using cc: ConstraintsCollector): ProdStrat = val res = freshVar(resName, cc.forFunGroup) params.foreach: _.restParam.foreach: p => generatedProdVars(p.sym).constrainOpaque(OpaqueBoundary.Both) val funProdStrat = mkFunProdStrat(params, rootFunId, res.asProdStrat) processBlock(body)(using cc, res.asConsStrat) funProdStrat def constrainOpaqueResult(r: Result)(using cc: ConstraintsCollector): Unit = cc.constrain(processResult(r), NoCons) def processHandleableFunDefn(fun: FunDefn)(using cc: ConstraintsCollector): Unit = if mono || !preAnalyzer.res.rootFunSyms(fun.dSym) then val funProdStrat = processHandleableFun( s"${fun.dSym.nme}_res", fun.params, fun.body, (fun.dSym, -1)) cc.constrain(funProdStrat, generatedProdVars(fun.dSym).asConsStrat) def processClsLikeDefn(cls: ClsLikeDefn)(using cc: ConstraintsCollector): Unit = cls.privateFields.foreach(sym => generatedProdVars(sym).constrainOpaque(OpaqueBoundary.Both)) cls.publicFields.foreach: (_, tsym) => generatedProdVars(tsym).constrainOpaque(OpaqueBoundary.Both) cls.methods.foreach: fun => processBlock(fun.body)(using cc, NoCons) processBlock(cls.preCtor)(using cc, NoCons) processBlock(cls.ctor)(using cc, NoCons) cls.companion.foreach: mod => mod.privateFields.foreach(sym => generatedProdVars(sym).constrainOpaque(OpaqueBoundary.Both)) mod.publicFields.foreach: (_, tsym) => generatedProdVars(tsym).constrainOpaque(OpaqueBoundary.Both) mod.methods.foreach: fun => processHandleableFunDefn(fun) processBlock(mod.ctor)(using cc, NoCons) def processBlock(b: Block)(using cc: ConstraintsCollector, blkRes: ConsStrat): Unit = val instId = cc.instId b match case Return(res, implct) => cc.constrain(processResult(res), blkRes) case Throw(exc) => constrainOpaqueResult(exc) case Match(scrut, arms, dflt, rest) => val scrutStrat = processResult(scrut) cc.constrain(scrutStrat, new Dtor(scrut.uid, instId)) (arms.map(_._2) ++ dflt).foreach(processBlock) processBlock(rest) case Label(l, loop, body, rest) => processBlock(body) processBlock(rest) case Break(label) => () case Continue(label) => () case Scoped(syms, body) => processBlock(body) case Begin(sub, rest) => processBlock(sub) processBlock(rest) case Assign(lhs, rhs, rest) => val rhsStrat = processResult(rhs) lhs.match case _: NoSymbol => () case _ => cc.constrain(rhsStrat, generatedProdVars(lhs).asConsStrat) processBlock(rest) case TryBlock(sub, finallyDo, rest) => processBlock(sub) processBlock(finallyDo) processBlock(rest) case AssignField(lhs, nme, rhs, rest) => constrainOpaqueResult(lhs) constrainOpaqueResult(rhs) processBlock(rest) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => constrainOpaqueResult(lhs) constrainOpaqueResult(fld) constrainOpaqueResult(rhs) processBlock(rest) case Define(defn, rest) => defn match case ValDefn(tsym, sym, rhs) => val rhsStrat = processResult(rhs) cc.constrain(rhsStrat, generatedProdVars(tsym).asConsStrat) case fun: FunDefn => processHandleableFunDefn(fun) case cls: ClsLikeDefn => processClsLikeDefn(cls) processBlock(rest) case End(msg) => () case Unreachable(_) => () def processResult(r: Result)(using cc: ConstraintsCollector): ProdStrat = val instId = cc.instId def handleCallLike(callExprId: ResultId, f: Path, args: List[Arg]): ProdStrat = val fStrat = processResult(f) val argsStrat = args.map(a => processResult(a.value)) if args.exists(_.spread.isDefined) then cc.constrain(fStrat, NoCons) argsStrat.foreach(arg => cc.constrain(arg, NoCons)) NoProd else val callRes = freshVar("call_res", cc.forFunGroup) cc.constrain(fStrat, new ConsFun(callExprId, instId)(argsStrat, callRes.asConsStrat)) callRes.asProdStrat r match case sel@TrackableSelect(from, field, owner) => val fromStrat = processResult(from) val selRes = freshVar("sel_res", cc.forFunGroup) cc.constrain( fromStrat, new FieldSel(sel.uid, instId)(field, owner, selRes.asConsStrat)) selRes.asProdStrat case c@CtorCall(ctor, args) if args.forall(_.spread.isEmpty) => val argsStrat = args.map: case Arg(_, a) => processResult(a) ctor match case cls: ClassSymbol => cls.tree.clsParams.size match case 1 => val clsParams = cls.tree.clsParams.head // TODO: properly check the parameter lists, which may change after passes like lifting // softTODO(argsStrat.size === clsParams.size, s"mismatched ctor arg and cls param sizes") new Ctor(c.uid, instId)(ctor, clsParams.zip(argsStrat)) case _ => // - the size of 0 means we don't know the cls param symbols, // so we constrain args with NoCons and this CtorCall gives NoProd // - if size > 1, we cannot handle multiple parameter class flow now, // constrain args with NoCons and this CtorCall gives NoProd for a <- argsStrat do cc.constrain(a, NoCons) NoProd case _: ModuleOrObjectSymbol => new Ctor(c.uid, instId)(ctor, Nil) case tupSize: Int => new Ctor(c.uid, instId)(tupSize, (0 until tupSize).zip(argsStrat).toList) case c@CtorCall(_, args) => args.foreach(arg => cc.constrain(processResult(arg.value), NoCons)) NoProd case c@Call(fun, argss) => argss match case args :: Nil => handleCallLike(c.uid, fun, args) case args :: rest => // For multi-arg-list calls, handle the first arg list normally for // dead-param-elim, then constrain subsequent arg lists opaquely. // We cannot reuse c.uid for subsequent ConsFuns because the // DeadParamElim rewriter only rewrites the first arg list, // and sharing the same exprId would cause conflicting eliminable sets. val firstResult = handleCallLike(c.uid, fun, args) cc.constrain(firstResult, NoCons) rest.foreach: nextArgs => nextArgs.foreach(a => cc.constrain(processResult(a.value), NoCons)) NoProd case Nil => handleCallLike(c.uid, fun, Nil) case i@Instantiate(_, cls, argss) => handleCallLike(i.uid, cls, argss.flatten) case lam@Lambda(ps, body) => processHandleableFun("lam_res", ps :: Nil, body, lam.uid) case _: Tuple => lastWords("should be handled in CtorCall") case Record(_, fields) => fields.foreach: case RcdArg(idx, value) => idx.foreach(p => cc.constrain(processResult(p), NoCons)) cc.constrain(processResult(value), NoCons) NoProd case p: Path => p match case CtorRef(ctor) => NoProd case refSite@FunRef(f) => funsToProdStratScheme.get(f) match case Some(fScheme) => fScheme.instantiate(refSite.uid, f) case None => generatedProdVars(f).asProdStrat case refLk@RefLike(sym) => refLk match case Select(p, _) => cc.constrain(processResult(p), NoCons) case _ => () generatedProdVars(sym).asProdStrat case _: Value.Ref => lastWords("already handled in `RefLike` case") case Select(qual, name) => cc.constrain(processResult(qual), NoCons) NoProd case DynSelect(qual, fld, arrayIdx) => cc.constrain(processResult(qual), NoCons) cc.constrain(processResult(fld), NoCons) NoProd case Value.This(sym) => NoProd case Value.Lit(lit) => NoProd } end FlowConstraintsCollector class FlowConstraintSolver(val collector: FlowConstraintsCollector): given tl: TraceLogger = collector.tl given fState: FlowAnalysis.State = collector.fState given eState: Elaborator.State = collector.eState given preAnalyzer: FlowPreAnalyzer = collector.preAnalyzer val ctorDests = LinkedHashMap.empty[ConcreteProducer, Set[ConcreteConsumer | NoCons.type]].withDefaultValue(Set.empty) val dtorSrcs = LinkedHashMap.empty[ConcreteConsumer, Set[ConcreteProducer | NoProd.type]].withDefaultValue(Set.empty) val funDests = LinkedHashMap.empty[ProdFun, Set[ConsFun | NoCons.type]].withDefaultValue(Set.empty) val funSrcs = LinkedHashMap.empty[ConsFun, Set[ProdFun | NoProd.type]].withDefaultValue(Set.empty) val upperBounds = MutMap.empty[StratVarId, Ls[ConsStrat]].withDefaultValue(Nil) val lowerBounds = MutMap.empty[StratVarId, Ls[ProdStrat]].withDefaultValue(Nil) locally { val cache = MutSet.empty[ProdStrat -> ConsStrat] def hasConcreteInstantiationId(prodOrCons: ProdStrat | ConsStrat): Boolean = prodOrCons match case p: Ctor => p.instantiationId.isDefined case p: ProdFun => p.instantiationId.isDefined case c: Dtor => c.instantiationId.isDefined case c: FieldSel => c.instantiationId.isDefined case c: ConsFun => c.instantiationId.isDefined case _ => true def recordFlow[P, C]( dests: collection.mutable.Map[P, Set[C | NoCons.type]], srcs: collection.mutable.Map[C, Set[P | NoProd.type]] )(producer: P, consumer: C): Unit = dests(producer) = dests(producer) + consumer srcs(consumer) = srcs(consumer) + producer def recordNoCons[P, C]( dests: collection.mutable.Map[P, Set[C | NoCons.type]] )(producer: P): Unit = dests(producer) = dests(producer) + NoCons def recordNoProd[P, C]( srcs: collection.mutable.Map[C, Set[P | NoProd.type]] )(consumer: C): Unit = srcs(consumer) = srcs(consumer) + NoProd def handle(constraint: ProdStrat -> ConsStrat): Unit = if cache.add(constraint) then assert: val (prod, cons) = constraint hasConcreteInstantiationId(prod) && hasConcreteInstantiationId(cons) constraint match case (c: Ctor, d: Dtor) => recordFlow(ctorDests, dtorSrcs)(c, d) case (c: Ctor, d: FieldSel) => if d.selectsFrom === c.ctor then recordFlow(ctorDests, dtorSrcs)(c, d) handle( c.args.find(_._1 is d.field).get._2, d.consVar) case (c: Ctor, NoCons) => recordNoCons(ctorDests)(c) for (_, argProd) <- c.args do handle(argProd, NoCons) case (p: ProdFun, c: ConsFun) => recordFlow(funDests, funSrcs)(p, c) c.params.take(p.params.size).lazyZip(p.params).foreach((argC, argP) => handle(argC -> argP)) for restCons <- p.restParam arg <- c.params.drop(p.params.size) do handle(arg, restCons) handle(p.res, c.res) case (p: ProdFun, NoCons) => recordNoCons(funDests)(p) for a <- p.params do handle(NoProd, a) p.restParam.foreach(r => handle(NoProd, r)) handle(p.res, NoCons) case (NoProd, d: Dtor) => recordNoProd(dtorSrcs)(d) case (NoProd, sel: FieldSel) => recordNoProd(dtorSrcs)(sel) handle(NoProd, sel.consVar) case (NoProd, c: ConsFun) => recordNoProd(funSrcs)(c) for a <- c.params do handle(a, NoCons) handle(NoProd, c.res) case (p: ProdVar, c: ConsVar) => upperBounds(p.s.uid) ::= c for l <- lowerBounds(p.s.uid) do handle(l, c) lowerBounds(c.s.uid) ::= p for u <- upperBounds(c.s.uid) do handle(p, u) case (p: ProdVar, c) => upperBounds(p.s.uid) ::= c for l <- lowerBounds(p.s.uid) do handle(l, c) case (p, c: ConsVar) => lowerBounds(c.s.uid) ::= p for u <- upperBounds(c.s.uid) do handle(p, u) case _ => () // ignore other cases end handle for c <- collector.allConstraints do handle(c) } end FlowConstraintSolver ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowWebComputation.scala ================================================ package hkmc2 package codegen package flowAnalysis import scala.collection.mutable.{Set as MutSet} object FlowWebComputation: case class Result[P, C](markedProducers: collection.Set[P], markedConsumers: collection.Set[C]) def apply[P, C]( dests: P => IterableOnce[C], srcs: C => IterableOnce[P], producerRoots: IterableOnce[P], consumerRoots: IterableOnce[C], ): Result[P, C] = val markedProducers = MutSet.empty[P] val markedConsumers = MutSet.empty[C] def markProducer(p: P): Unit = if markedProducers.add(p) then dests(p).iterator.foreach(markConsumer) def markConsumer(c: C): Unit = if markedConsumers.add(c) then srcs(c).iterator.foreach(markProducer) producerRoots.iterator.foreach(markProducer) consumerRoots.iterator.foreach(markConsumer) Result(markedProducers, markedConsumers) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala ================================================ package hkmc2 package codegen package js import mlscript.utils.*, shorthands.* import utils.* import document.* import document.Document.{braced, bracketed} import hkmc2.Message.MessageContext import hkmc2.syntax.{Tree, MutVal, ImmutVal, SpreadKind} import hkmc2.semantics.* import Elaborator.{State, Ctx} import hkmc2.codegen.Lambda import Scope.scope import hkmc2.syntax.Tree.UnitLit import hkmc2.semantics.Elaborator.ctx import hkmc2.syntax.Tree.{IntLit, StrLit} import scala.annotation.tailrec // TODO factor some logic for other codegen backends abstract class CodeBuilder: type Context class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: import JSBuilder.* def checkMLsCalls: Bool = false def checkSelections: Bool = false def freezeDefinitions: Bool = false val builtinOpsBase: Ls[Str] = Ls( "+", "-", "*", "/", "%", "==", "!=", "<", "<=", ">", ">=", "===", "&&", "||") val builtinOpsMap: Map[Str, Str] = ( builtinOpsBase.map(op => op -> op).toMap + (";" -> ",") ) val needsParens: Set[Str] = Set(",") val freeze = if !config.noFreeze then "globalThis.Object.freeze" else "" lazy val freezeDefns = if freezeDefinitions && !config.noFreeze then "globalThis.Object.freeze" else "" // TODO use this to avoid parens when we generate recomposed expressions later enum Context: case TopLevel case SelectionPrefix case Argument case Operand(prec: Int) def mkErr(errMsg: Message)(using Raise, Scope): Document = doc"throw globalThis.Error(${result(Value.Lit(syntax.Tree.StrLit(errMsg.show)))})" def errExpr(errMsg: Message)(using Raise, Scope): Document = raise(ErrorReport(errMsg -> N :: Nil, source = Diagnostic.Source.Compilation)) doc"(()=>{${mkErr(errMsg)}})()" def errStmt(errMsg: Message)(using Raise, Scope): Document = raise(ErrorReport(errMsg -> N :: Nil, source = Diagnostic.Source.Compilation)) doc" # ${mkErr(errMsg)};" def getVar(l: Local, loc: Opt[Loc])(using Raise, Scope): Document = l match case ts: semantics.TermSymbol => ts.owner match case S(owner) => doc"${getVar(owner, loc)}${ if (ts.k is syntax.LetBind) && !owner.isInstanceOf[semantics.TopLevelSymbol] then ".#" + owner.privatesScope.lookup_!(ts, loc) else fieldSelect(ts.id.name) }" case N => scope.lookup_!(ts, loc) case ts: semantics.ModuleOrObjectSymbol if ts.asMod.isDefined => // FIXME: currently, objects have a ModuleSymbol... // * Module self-references use the module name itself instead of `this` scope.lookup_!(ts, loc) case ts: semantics.InnerSymbol => scope.findThis_!(ts) case _ => scope.lookup_!(l, loc) def runtimeVar(using Raise, Scope): Document = getVar(State.runtimeSymbol, N) def argument(a: Arg)(using Raise, Scope): Document = val spd = a.spread match case S(SpreadKind.Eager) => doc"..." case S(SpreadKind.Lazy) => doc"$runtimeVar.Tuple.split, " case N => doc"" doc"${spd}${result(a.value)}" def operand(a: Arg)(using Raise, Scope): Document = if a.spread.nonEmpty then die else subexpression(a.value) def subexpression(r: Result)(using Raise, Scope): Document = r match case _: Lambda => doc"(${result(r)})" case _ => result(r) def fieldSelect(s: Str): Document = escapeField(s, ".") def escapeField(s: Str, defaultPrefix: Str): Document = if JSBuilder.isValidFieldName(s) then doc"$defaultPrefix$s" else s.toIntOption match case S(index) => doc"[$index]" case N => doc"[${JSBuilder.makeStringLiteral(s)}]" def result(r: Result)(using Raise, Scope): Document = r match case Value.This(sym) => scope.findThis_!(sym) case Value.Lit(Tree.StrLit(value)) => makeStringLiteral(value) case Value.Lit(lit) => lit.idStr case Value.Ref(l: BuiltinSymbol, _) => if l.nullary then l.nme else errExpr(msg"Illegal reference to builtin symbol '${l.nme}'") case Value.Ref(l, disamb) => l match case l: BlockMemberSymbol if disamb.exists(_.shouldBeLifted) => doc"${getVar(l, l.toLoc)}.class" case _ => getVar(l, r.toLoc) case Call(Value.Ref(l: BuiltinSymbol, _), (lhs :: rhs :: Nil) :: Nil) if !l.functionLike => if l.binary then val res = doc"${operand(lhs)} ${l.nme} ${operand(rhs)}" if needsParens(l.nme) then doc"(${res})" else res else errExpr(msg"Cannot call non-binary builtin symbol '${l.nme}'") case Call(Value.Ref(l: BuiltinSymbol, _), (rhs :: Nil) :: Nil) if !l.functionLike => if l.unary then val res = doc"${l.nme} ${operand(rhs)}" if needsParens(l.nme) then doc"(${res})" else res else errExpr(msg"Cannot call non-unary builtin symbol '${l.nme}'") case Call(Value.Ref(l: BuiltinSymbol, _), args :: Nil) => if l.functionLike then val argsDoc = args.map(argument).mkDocument(", ") doc"${l.nme}(${argsDoc})" else errExpr(msg"Illegal arity for builtin symbol '${l.nme}'") case Call(s @ Select(_, Elaborator.ctx.builtins.BuiltInOpIdent(jsOp)), (lhs :: rhs :: Nil) :: Nil) => val res = doc"${operand(lhs)} ${jsOp} ${operand(rhs)}" if needsParens(jsOp) then doc"(${res})" else res case c @ Call(fun, argss) => val base = subexpression(fun) val calls = argss.foldLeft(base): (acc, args) => val argsDoc = args.map(argument).mkDocument(", ") doc"${acc}(${argsDoc})" if c.isMlsFun then if checkMLsCalls then doc"$runtimeVar.checkCall(${calls})" else doc"${calls}" else doc"$runtimeVar.safeCall(${calls})" case Lambda(ps, bod) => scope.nest givenIn: val (params, bodyDoc) = setupFunction(none, ps, bod, isLambda = true) doc"($params) => ${ braced(bodyDoc) }" case s @ Select(qual, id) => val dotClass = s.symbol match case S(ds) if ds.shouldBeLifted => doc".class" case _ => doc"" val name = id.name doc"${result(qual)}${ if isValidFieldName(name) then doc".$name" else name.toIntOption match case S(index) => doc"[$index]" case N => doc"[${makeStringLiteral(name)}]" }${dotClass}" case DynSelect(qual, fld, ai) => if ai then doc"${result(qual)}.at(${result(fld)})" else doc"${result(qual)}[${result(fld)}]" case Instantiate(mut, cls, argss) => val calls = argss.foldLeft(result(cls)): (acc, args) => doc"${acc}(${args.map(argument).mkDocument(", ")})" val inner = doc"new $calls" if mut then inner else doc"$freeze(${inner})" case Tuple(mut, es) if es.isEmpty => if mut then "[]" else doc"$freeze([])" case Tuple(mut, es) => val inner = val lazyConcat = es.exists(!_.spread.fold(true)(_.isEager)) if lazyConcat then doc"$runtimeVar.Tuple.lazyConcat(${es.map(argument).mkDocument(doc", ")})" else bracketed("[", "]", insertBreak = true): es.map(argument).mkDocument(doc", # ") if mut then inner else doc"$freeze(${inner})" case Record(mut, Nil) => if mut then "{}" else doc"$freeze({})" case Record(mut, flds) => val inner = bracketed(pre = "{", post = "}", insertBreak = true): flds.map: case RcdArg(S(Value.Lit(IntLit(idx))), v) => doc"${idx.toString}: ${result(v)}" case RcdArg(S(Value.Lit(StrLit(idx))), v) => doc"${if isValidIdentifier(idx) then idx else s"\"$idx\""}: ${result(v)}" case RcdArg(S(idx), v) => doc"[${result(idx)}]: ${result(v)}" case RcdArg(N, v) => doc"...${result(v)}" .mkDocument(doc", # ") if mut then inner else doc"$freeze(${inner})" /** * Matches the following kind of if statement, where ai are ints: * * ``` * if scrut is a1 do * body1 * set scrut = a2 * if scrut is a2 do * body2 * set scrut = a3 * if scrut is an do * bodyn * ``` * * The intention is that this can be compiled efficiently into a switch statement: * * ```js * switch (scrut) { * case a1: * body1 * scrut = a2; * case a2: * body2 * scrut = a3; * ... * case an: * bodyn * } * ``` * Note that `scrut` is guaranteed to not change between `set scrut = ai` and `if scrut is ai`, * because the JS event loop waits until the entire call stack is cleared before running any other * code. Hence, this transformation is safe. */ object IfIntChain: @tailrec private def lastBlkAssign(b: Block): Opt[Assign] = b match case a @ Assign(lhs, rhs, End(_)) => S(a) case b: NonBlockTail => lastBlkAssign(b.rest) case _: BlockTail => N @tailrec private def unapplyImpl( b: Block, acc: List[(BigInt, Block)], scrut: Opt[Value.Ref], curVal: Opt[BigInt] ): Opt[(Value.Ref, List[(BigInt, Block)], Block)] = val scrutSym = scrut.map(_.l) b match case Match( scrut_ @ Value.Ref(scrutSym_, _), // The scrutinee is a ref. (Case.Lit(Tree.IntLit(curVal_)), b) :: Nil, // There is only one case matching an int literal. S(End(_)) | N, rest // Default case exists and does nothing. ) if scrutSym.map(_ === scrutSym_).getOrElse(true) // The scrutinee is the same as the one before. && curVal.map(_ === curVal_).getOrElse(true) // The matched int literal is one previously set. => lastBlkAssign(b) match // the one branch ends by assigning `nextInt` to `scrutSym` case S(Assign(`scrutSym_`, Value.Lit(Tree.IntLit(nextInt)), _)) => unapplyImpl(rest, (curVal_, b) :: acc, S(scrut_), S(nextInt)) case _ => S((scrut_, (curVal_, b) :: acc, rest)) case _ => scrut match case Some(value) => S((value, acc, b)) case None => N def unapply(b: Block): Opt[(scrut: Value.Ref, cases: List[(BigInt, Block)], rest: Block)] = unapplyImpl(b, Nil, N, N) match case Some(value) if value._2.length > 1 => S(value) case _ => N def returningTerm(t: Block, endSemi: Bool)(using Raise, Scope): Document = def mkSemi = if endSemi then ";" else "" t match case Assign(l, r, rst) if l is State.noSymbol => doc" # ${result(r)};${returningTerm(rst, endSemi)}" case Assign(l, r, rst) => doc" # ${getVar(l, l.toLoc // TODO: improve location )} = ${result(r)};${returningTerm(rst, endSemi)}" case AssignField(p, n, r, rst) => doc" # ${result(p)}${fieldSelect(n.name)} = ${result(r)};${returningTerm(rst, endSemi)}" case AssignDynField(p, f, ai, r, rst) => doc" # ${result(p)}[${result(f)}] = ${result(r)};${returningTerm(rst, endSemi)}" case Define(defn, rst) => def mkThis(sym: InnerSymbol): Document = result(Value.This(sym)) val resJS = defn match case ValDefn(tsym, sym, p) => val sym = defn.sym // * Currently we allow `val` outside of object/module scopes, // * in which case it has no owner and is just a glorified local variable rather than a field. tsym.owner match case N => doc"${getVar(sym, sym.toLoc)} = ${result(p)};${returningTerm(rst, endSemi)}" case S(owner) => val thisDoc = mkThis(owner) val nme = sym.nme owner match case mod: ModuleOrObjectSymbol if (mod.tree.k is syntax.Mod) && (nme == "name" || nme == "length") => // * JavaScript class constructors have built-in non-writable `name` and `length` properties. // * Use Object.defineProperty to override them in module/class static contexts. doc"Object.defineProperty(${thisDoc}, ${nme.escaped}, { configurable: true, enumerable: true, writable: true, value: ${result(p)} });${returningTerm(rst, endSemi)}" case _ => doc"${thisDoc}${fieldSelect(nme)} = ${result(p)};${returningTerm(rst, endSemi)}" case defn: (FunDefn | ClsLikeDefn) => val outerScope = scope val (thisProxy, res) = scope.nestRebindThis( // * Either this is an InnerSymbol or this is a Fun, // * and we need to rebind `this` to None to shadow it. defn.innerSym.collectFirst{ case s: InnerSymbol => s }): defn match case FunDefn(params = Nil) => lastWords("cannot generate function with no parameter list") case FunDefn(own, sym, dSym, ps :: pss, bod) => val result = pss.foldRight(bod): case (ps, block) => Return(Lambda(ps, block), false) val displayName = if sym.nameIsMeaningful then S(dSym.name) else N // * We may need to set up the function in a nested scope in one case below, so this is marked as lazy. lazy val (params, bodyDoc) = setupFunction(displayName, ps, result, isLambda = false) val symName = sym.nme // * If the name is a valid JavaScript identifier, use it in the generated function code. if sym.nameIsMeaningful && isValidIdentifier(symName) then val varName = getVar(sym, dSym.toLoc) scope.reverseLookup(sym.nme) match // * Maybe the function's internal name was already bound in scope; // * in that case, we need to forward it to a different variable to avoid unintended capture. case S(otherSym) if (otherSym isnt sym) && bod.freeVars.contains(otherSym) => scope.nest.givenIn: val externalName = scope.allocateName(otherSym, prefix = "proxy$", shadow = true) val (params, bodyDoc) = setupFunction(displayName, ps, result, isLambda = false) doc"const $externalName = $symName; ${ varName} = function $symName($params) ${ braced(bodyDoc) };" case _ => doc"${varName} = function ${sym.nme}($params) ${ braced(bodyDoc) };" else // * In JS, `let x = (0, function (args) {...})` makes the function anonymous; // * otherwise, using `let x = function (args) {...}` would name the function `x`, // * which is not meaningful, here. doc"${getVar(sym, dSym.toLoc)} = (undefined, function ($params) ${ braced(bodyDoc) });" case ClsLikeDefn(ownr, isym, sym, ctorSym, kind, paramsOpt, auxParams, par, mtds, privFlds, pubFlds, preCtor, ctor, modo, bufferable) => val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) val ctorAuxParams = auxParams.map(ps => ps.params.map(p => p.sym -> scope.allocateName(p.sym))) def mkMethods(mtds: Ls[FunDefn], mtdPrefix: Str)(using Scope): Document = mtds.map: case td @ FunDefn(params = ps :: pss, body = bod) => val result = pss.foldRight(bod): case (ps, block) => Return(Lambda(ps, block), false) val (params, bodyDoc) = scope.nest.givenIn: setupFunction(S(td.sym.nme), ps, result, isLambda = false) doc" # $mtdPrefix${td.sym.nme}($params) ${ braced(bodyDoc) }" case td @ FunDefn(params = Nil, body = bod) => doc" # ${mtdPrefix}get ${td.sym.nme}() ${ braced(body(bod, endSemi = true)) }" .mkDocument(" ") def mkPrivs(pubFlds: Ls[BlockMemberSymbol -> TermSymbol], privFlds: Ls[TermSymbol], mtdPrefix: Str, isym: InnerSymbol)(using Scope): Document = // * Note: the non-mut-val parts of `pubFlds` are not used because in JS, fields are not declared val mutPubFields = pubFlds.collect: case (_, sym) if sym.k is MutVal => sym -> TermSymbol( syntax.LetBind, S(isym), Tree.Ident(sym.nme)) val allPrivFlds = privFlds ++ mutPubFields.map(_._2) val privDecls = allPrivFlds.map: fld => val nme = isym.privatesScope.allocateOrGetName(fld) doc" # $mtdPrefix#$nme;" val accessors = mutPubFields.flatMap: (valSym, letSym) => doc" # ${mtdPrefix}get ${escapeField(valSym.name, "") }() { return ${getVar(letSym, letSym.toLoc)}; }" :: doc" # ${mtdPrefix}set ${escapeField(valSym.name, "") }(value) { ${getVar(letSym, letSym.toLoc)} = value; }" :: Nil (privDecls ::: accessors).mkDocument(doc"") val modDoc = modo match case N => doc"" case S(mod) => val (thisProxy, res) = outerScope.nestRebindThis(S(mod.isym)): val mtdPrefix = "static " val privs = mkPrivs(mod.publicFields, mod.privateFields, mtdPrefix, mod.isym) val ctorCode = if mod.ctor.isEmpty then doc"" else doc" # static " :: braced: body(mod.ctor, endSemi = true) privs :: ctorCode :: { mkMethods(mod.methods, mtdPrefix) } // * Note that `thisProxy` might be defined at this point, // * if the module accesses the self-reference of an outer definition. // * But in that case, we'll already be creating a proxy through the `nestRebindThis` call above, // * and that proxy will return the same symbol, so we don't need to bind it here. res val mtdPrefix = "" val privs = mkPrivs(pubFlds, privFlds, mtdPrefix, isym) val isSingleton = (kind is syntax.Obj) || (kind is syntax.Pat) val (singletonInit, singletonFreeze) = if isSingleton then val fz = doc" # $freeze(this);" ownr match case S(owner) => (doc" # ${result(Value.Ref(owner, N))}.${sym.nme} = this;", fz) case N => (doc" # ${getVar(sym, sym.toLoc)} = this;", fz) else (doc"", doc"") val ctorCode = scope.nest.givenIn: val preCtorCode = nonNestedScoped(preCtor)(bd => block(bd, true)) doc"$preCtorCode$singletonInit${nonNestedScoped(ctor)(bd => block(bd, endSemi = true))}${ kind match case syntax.Obj => doc" # ${defineProperty(doc"this", "class", doc"${scope.lookup_!(isym, isym.toLoc)}")};" case _ => "" }$singletonFreeze" // * If there are no ctor params, pop one param list off the aux params val (newCtorAuxParams, initialCtorParams) = paramsOpt match case None => ctorAuxParams match case head :: next => (next, head) case Nil => (ctorAuxParams, Nil) case Some(_) => (ctorAuxParams, ctorParams) val ctorAux = if newCtorAuxParams.isEmpty then ctorCode else val pss = newCtorAuxParams.map(_.map(_._2)) val newCtorCode = doc"$ctorCode # return this;" val ctorBraced = doc"${ braced(newCtorCode) }" val funBod = pss.foldRight(ctorBraced): case (psDoc, doc) => doc"(${psDoc.mkDocument(", ")}) => $doc" doc" # return $funBod" val ctorBod = {{ val extraPath = if paramsOpt.isDefined then ".class" else "" doc" # static " :: braced: val v = getVar(isym, isym.toLoc) if isSingleton then doc" # new $v" else ownr match case S(owner) => doc" # ${result(Value.Ref(owner, N))}.${sym.nme}$extraPath = $v" case N => doc" # ${getVar(sym, sym.toLoc)}$extraPath = $v" }} :: ( if ctorAux.isEmpty then doc"" else doc" # constructor(${initialCtorParams.unzip._2.mkDocument(", ")}) " :: braced(ctorAux) ) val clsJS = doc"class ${scope.lookup_!(isym, isym.toLoc)}${ par.map(p => doc" extends ${ result(p) }").getOrElse("") } " :: braced: ctorBod :: modDoc :: privs :: { // * Create "debound method" checkers as accessors if checkSelections then mtds .flatMap: case td @ FunDefn(params = ps :: pss, body = bod) => S: doc" # get ${td.sym.nme}$$__checkNotMethod() { ${ runtimeVar }.deboundMethod(${makeStringLiteral(td.sym.nme)}, ${ makeStringLiteral(sym.nme) }); }" case _ => N .mkDocument(" ") else doc"" } :: { mkMethods(mtds, mtdPrefix) } :: { // * If this class has a `toString` implementation, then delegate // * `prettyPrint` to `toString`. if mtds.exists(_.sym.nme == "toString") then doc""" # [${ getVar(State.prettyPrintSymbol, N) }]() { return this.toString(); }""" // * Call the `render` function in the default `toString` method. else doc" # ${mtdPrefix}toString() { return $runtimeVar.render(this); }" } :: { doc""" # static [${getVar(State.definitionMetadataSymbol, N)}] = [${ kind.desc.escaped}, ${sym.nme.escaped}${ if (kind is syntax.Cls) && paramsOpt.isDefined then doc", [${ctorParams.map { (p, _) => p.decl match case S(Param(flags = FldFlags(isVal = true))) => doc"${p.name.escaped}" case S(_) | N => doc"null" }.mkDocument(", ")}]" else doc"" }]; """ } if isSingleton then ownr match case S(owner) => assert((kind is syntax.Pat) || paramsOpt.isEmpty) doc"$freezeDefns(${clsJS});" case N => doc"$freezeDefns(${clsJS});" else val paramsAll = paramsOpt match case None => auxParams case Some(value) => value :: auxParams val fun = paramsAll match case ps_ :: pss_ if paramsOpt.isDefined => outerScope.nest.givenIn: val (ps, _) = setupFunction(some(sym.nme), ps_, End(), isLambda = false) val pss = pss_.map(setupFunction(N, _, End(), isLambda = false)._1) val paramsDoc = pss.foldLeft(doc"($ps)"): case (doc, ps) => doc"${doc}(${ps})" val inner = doc"new ${sym.nme}.class$paramsDoc" val bod = braced(doc" # return $freeze($inner);") val funBod = pss.foldRight(bod): case (psDoc, doc_) => doc"($psDoc) => $doc_" val funBodRet = if pss.isEmpty then funBod else braced(doc" # return $funBod") val nme = if isValidIdentifier(sym.nme) then sym.nme else "" S(doc"function $nme($ps) ${ funBodRet }") case _ => N ownr match case S(owner) => val ths = mkThis(owner) fun match case S(f) => doc"${ths}.${sym.nme} = ${f}; # $freezeDefns($clsJS);" case N => doc"$freezeDefns(${clsJS});" case N => fun match case S(f) => doc"${getVar(sym, sym.toLoc)} = ${f}; # $freezeDefns($clsJS);" case N => doc"$freezeDefns(${clsJS});" thisProxy match case S(proxy) if !scope.thisProxyDefined => scope.thisProxyDefined = true doc"const $proxy = this; # $res${returningTerm(rst, endSemi)}" case _ => doc"$res${returningTerm(rst, endSemi)}" doc" # $resJS" case Return(Value.Lit(UnitLit(false)), false) => doc" # return${mkSemi}" case Return(res, true) => doc" # ${result(res)}${mkSemi}" case Return(res, false) => doc" # return ${result(res)}${mkSemi}" case Match(scrut, Nil, els, rest) => val e = els match case S(el) => nonBracedScoped(el)(bod => returningTerm(bod, endSemi = true)) case N => doc"" e :: returningTerm(rest, endSemi) case Match(scrut, (Case.Lit(lit), End(msg)) :: Nil, S(el), rest) => val sd = result(scrut) val e = braced(nonBracedScoped(el)(res => returningTerm(res, endSemi = false))) doc" # if ($sd !== ${lit.idStr}) $e" :: returningTerm(rest, endSemi) case SpecializedSwitch(scrut, cases, dflt, rest) => val switchBod = cases.foldLeft(doc""): (acc, arm) => val needsBreak = arm.isInstanceOf[SwitchCase.ExplicitBreak] acc :: doc" # case ${result(Value.Lit(arm.litValue))}: #{ ${ // * Note: we use `block` here so that Scoped nodes will create proper brace sections, // * necessary since `case` clauses do not create a new scope, // * so something like `switch (x) { case 1: let y = 1; break; case 2: let y = 2 }` is ill-formed! block(arm.body, endSemi = true) }${if needsBreak then doc" # break;" else ""} #} " val bodWithDflt = doc"${switchBod}${dflt match case Some(bd) => doc" # default: #{ ${nonBracedScoped(bd)(bd => returningTerm(bd, endSemi = true))} #} " case None => doc"" }" doc" # switch (${result(scrut)}) { #{ ${bodWithDflt} #} # }" :: returningTerm(rest, endSemi) case Match(scrut, arms @ hd :: tl, els, rest) => val sd = result(scrut) def cond(cse: Case) = cse match case Case.Lit(lit) => doc"$sd === ${lit.idStr}" case Case.Cls(cls, pth) => cls match // case _: semantics.ModuleSymbol => doc"=== ${result(pth)}" // [invariant:0] If the class represented by `cls` does not exist at // runtime, then `pth` is a dummy value and should be discarded. case Elaborator.ctx.builtins.Str => doc"typeof $sd === 'string'" case Elaborator.ctx.builtins.Num => doc"typeof $sd === 'number'" case Elaborator.ctx.builtins.Bool => doc"typeof $sd === 'boolean'" case Elaborator.ctx.builtins.Int => doc"globalThis.Number.isInteger($sd)" case Elaborator.ctx.builtins.BigInt => doc"typeof $sd === 'bigint'" case Elaborator.ctx.builtins.Symbol.module => doc"typeof $sd === 'symbol'" case Elaborator.ctx.builtins.TypedArray => doc"globalThis.ArrayBuffer.isView($sd) && !($sd instanceof globalThis.DataView)" case _: ModuleOrObjectSymbol => doc"$sd instanceof ${result(pth)}.class" case _ => doc"$sd instanceof ${result(pth)}" case Case.Tup(len, inf) => doc"$runtimeVar.Tuple.isArrayLike($sd) && $sd.length ${if inf then ">=" else "==="} ${len}" case Case.Field(name = n, safe = false) => doc"""typeof $sd === "object" && $sd !== null && "${n.name}" in $sd""" case Case.Field(name = n, safe = true) => doc""""${n.name}" in $sd""" val h = doc" # if (${ cond(hd._1) }) ${ braced(nonBracedScoped(hd._2)(res => returningTerm(res, endSemi = false))) }" val t = tl.foldLeft(h)((acc, arm) => acc :: doc" else if (${ cond(arm._1) }) ${ braced(nonBracedScoped(arm._2)(res => returningTerm(res, endSemi = false))) }") val e = els match case S(End(_)) => doc"" case S(el) if arms.forall(_._2.isAbortive) => // * We print the `else` branch outside, after the `if` when all arms are abortive. // * This typically results in slightly more concise code. // * Not sure it's necessarily a good idea, though. (Does it affect the performance of the generated code?) returningTerm(el, endSemi = true) case S(el) => doc" else ${ braced(nonBracedScoped(el)(res => returningTerm(res, endSemi = false))) }" case N => doc"" t :: e :: returningTerm(rest, endSemi) case Begin(sub, thn) => doc"${returningTerm(sub, endSemi = true)}${returningTerm(thn, endSemi)}" case End(msg) if config.commentGeneratedCode && msg.nonEmpty => doc" # /* $msg */" case End(_) => doc"" case Unreachable(msg) if config.commentGeneratedCode => if msg.isEmpty then doc" # /* Unreachable */" else doc" # /* Unreachable: $msg */" case Unreachable(_) => doc"" case Throw(res) => doc" # throw ${result(res)}${mkSemi}" case Break(lbl) => doc" # break ${getVar(lbl, lbl.toLoc)}${mkSemi}" case Continue(lbl) => doc" # continue ${getVar(lbl, lbl.toLoc)}${mkSemi}" case Label(lbl, loop, bod, rst) => scope.allocateName(lbl) // [fixme:0] TODO check scope and allocate local variables here (see: https://github.com/hkust-taco/mlscript/pull/293#issuecomment-2792229849) doc" # ${getVar(lbl, lbl.toLoc)}:${if loop then doc" while (true)" else ""} " :: braced { nonBracedScoped(bod)(bd => returningTerm(bd, endSemi = true)) :: (if loop && !bod.isAbortive then doc" # break;" else doc"") } :: returningTerm(rst, endSemi) case TryBlock(sub, fin, rst) => doc" # try ${ braced(returningTerm(sub, endSemi = false)) } finally ${ braced(returningTerm(fin, endSemi = false)) } # ${ returningTerm(rst, endSemi).stripBreaks}" // Only nested scopes in unusual positions are handled here. case Scoped(syms, body) => doc" # " :: braced: scope.nest.givenIn: blockPreamble(syms.view.filter(body.freeVars)) :: returningTerm(body, endSemi = endSemi) // case _ => ??? /** We want to first reserve the names of all defined classes, object, and modules, * as these will be used as the internal names for these things, which may differ from the external name. * For instance, `class Foo() { ... Foo ... }` will essentially translate to * `Foo1 = function Foo() { return new Foo1.class }; Foo1.class = class Foo { ... Foo1 ... }`. * Here, we prefer the `class Foo` part to bear the original `Foo` name (and not, say, `Foo1` or `Foo2`), * as it will be visible at JS runtime. * Also note it is crucial here that the inner reference can access the outer definition `Foo1` and not `Foo` * – Foo refers to the inner class in generated code and not to the parameterized Foo class of the source. * For modules, we do turn any `this` reference into a reference to the corresponding generated class, * since modules represent static members and since we want them to avoid the problem of JS method debinding. * That means we must generate unique inner names, at least in the case of modules; * for instance, consider that `module M with { val x = 1; module M with { val y = x } }` * should generate something like `M2 = class M { static x = 1; static M1 = class M1 { static y = M.x } }`, * where it is crucial that the inner module's inner name M1 not clash with the outer module's inner name M. * We do not reserve the names of functions, as we currently just use the source name as the inner name, * since any unintentional capture will have no consequence. * For example, consider that `fun foo() = foo()` may generate something like * `foo = function foo() { return foo(); }` * or, if there was already a `foo` defined in some outer scope, * `foo1 = function foo() { return foo1(); }` * but the result has the same semantics. * */ def reserveNames(p: Program)(using Scope, Raise): Unit = def go(blk: Block): Unit = tl.trace(s"avoidNames ${blk.toString.take(100)}..."): blk match case Define(defn, rest) => defn match case d: ClsLikeDefn => val nme = scope.allocateName(d.isym) d.companion match case N => () case S(comp) => // * This is a bit messy. // * If there is a companion module, we need to map its inner symbol to the same name // * as the class' inner symbol, since in the end they are consolidated into a single definition. scope.addToBindings(comp.isym, nme, shadow = false) case _ => //scope.allocateName(defn.sym) defn.subBlocks.foreach(go) go(rest) case _ => blk.subBlocks.foreach(go) go(p.main) def program(p: Program, exprt: Opt[BlockMemberSymbol], wd: io.Path)(using Raise, Scope): Document = scope.allocateName(State.definitionMetadataSymbol) scope.allocateName(State.prettyPrintSymbol) doc"""const ${getVar(State.definitionMetadataSymbol, N)} = globalThis.Symbol.for("mlscript.definitionMetadata");""" :/: doc"""const ${getVar(State.prettyPrintSymbol, N)} = globalThis.Symbol.for("mlscript.prettyPrint");""" :/: programBody(p, exprt, wd) def programBody(p: Program, exprt: Opt[BlockMemberSymbol], wd: io.Path)(using Raise, Scope): Document = reserveNames(p) // Allocate names for imported modules. p.imports.foreach: i => i._1 -> scope.allocateName(i._1) // Generate import statements. val imps = p.imports.map: i => val path = i._2 val relPath = if path.startsWith("/") then "./" + io.Path(path).relativeTo(wd).map(_.toString).getOrElse(path) else path doc"""import ${getVar(i._1, N)} from "${relPath}";""" imps.mkDocument(doc" # ") :/: nonNestedScoped(p.main)(block(_, endSemi = false)).stripBreaks :: locally: exprt match case S(sym) => doc"\nlet ${sym.nme} = ${scope.lookup_!(sym, sym.toLoc)}; export default ${sym.nme};\n" case N => doc"" def worksheet(p: Program)(using Raise, Scope): (Document, Document) = reserveNames(p) lazy val imps = p.imports.map: i => doc"""${getVar(i._1, N)} = await import("${i._2.toString}").then(m => m.default ?? m);""" p.main match case Scoped(syms, body) => val fvs = body.freeVars blockPreamble(p.imports.map(_._1) ++ syms.view.filter(s => !s.isInstanceOf[TempSymbol] // ^ VarSymbols and TermSymbols should be kept as their value will be acessed and printed by the worksheet || fvs(s))) -> (imps.mkDocument(doc" # ") :/: block(body, endSemi = false).stripBreaks) case body => blockPreamble(p.imports.map(_._1)) -> (imps.mkDocument(doc" # ") :/: returningTerm(body, endSemi = false).stripBreaks) def genLetDecls(vars: Iterator[(Symbol, Str)]): Document = if vars.isEmpty then doc"" else doc" # let " :: vars.map: (_, nme) => nme .toList.mkDocument(", ") :: doc";" def blockPreamble(ss: Iterable[Symbol])(using Raise, Scope): Document = val vars = ss.toArray.sortBy(_.uid).iterator.map: l => whenValidatingIR: if scope.lookup(l).isDefined then // * It is invalid to shadow symbols in the IR raise: WarningReport(msg"var ${l.toString()} in scoped is already allocated" -> N :: Nil) l -> scope.allocateName(l) genLetDecls(vars) /** Specially handle top-level Scoped node: output the bindings, but do not add another pair of braces */ def nonBracedScoped(blk: Block)(k: Scope ?=> Block => Document)(using Raise, Scope): Document = blk match case Scoped(syms, body) => scope.nest.givenIn: blockPreamble(syms.view.filter(body.freeVars)) :: k(body) case _ => k(blk) /** Like `nonBracedScoped`, but not not create a nested scope – useful in fringe JS scenarios */ def nonNestedScoped(blk: Block)(k: Block => Document)(using Raise, Scope): Document = blk match case Scoped(syms, body) => blockPreamble(syms.view.filter(body.freeVars)) :: k(body) case _ => k(blk) def block(t: Block, endSemi: Bool)(using Raise, Scope): Document = returningTerm(t, endSemi) def body(t: Block, endSemi: Bool)(using Raise, Scope): Document = nonBracedScoped(t)(bd => block(bd, endSemi)) def defineProperty(target: Document, prop: Str, value: Document, enumerable: Bool = false): Document = doc"Object.defineProperty(${target}, ${prop.escaped}, ${ bracketed("{", "}", insertBreak = true): (if enumerable then doc"enumerable: true, # " else doc"") :: doc"value: ${value}" })" def setupFunction(name: Option[Str], params: ParamList, body: Block, isLambda: Bool) (using Raise, Scope): (Document, Document) = val paramsList = params.params.map(p => scope.allocateName(p.sym)) .++(params.restParam.map(p => "..." + scope.allocateName(p.sym))) .mkDocument(", ") (paramsList, this.body(body, endSemi = false)) object JSBuilder: def isValidIdentifier(s: Str): Bool = identifierPattern.matches(s) && !keywords.contains(s) // in this case, a keyword can be used as a field name // e.g. `something.class` is valid def isValidFieldName(s: Str): Bool = identifierPattern.matches(s) val keywords: Set[Str] = Set( // Reserved keywords as of ECMAScript 2015 "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof", "new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "yield", // The following are reserved as future keywords by the ECMAScript specification. // They have no special functionality at present, but they might at some future time, // so they cannot be used as identifiers. These are always reserved: "enum", // The following are only reserved when they are found in strict mode code: "abstract", "boolean", "byte", "char", "double", "final", "float", "goto", "implements", "int", "long", "native", "package", "protected", "short", "static", "synchronized", "throws", "transient", "volatile", // not a keyword, but cannot be declared as identifier in strict mode "arguments", "eval", ) def makeStringLiteral(s: Str): Str = s"\"${escapeStringCharacters(s)}\"" def escapeStringCharacters(s: Str): Str = s.map[Str] { case '"' => "\\\"" case '\\' => "\\\\" case '\b' => "\\b" case '\f' => "\\f" case '\n' => "\\n" case '\r' => "\\r" case '\t' => "\\t" case c => if 0 < c && c <= 255 && !c.isControl then c.toString else f"\\u${c.toInt}%04X" }.mkString extension (dsym: DefinitionSymbol[?]) def shouldBeLifted: Bool = val bsym = dsym.asBlkMember ( (dsym.asTrm orElse bsym.flatMap(_.asTrm)).isDefined || (dsym.asCls orElse bsym.flatMap(_.asCls)).flatMap(_.defn).exists(_.paramsOpt.isDefined) ) && (dsym.asModOrObj orElse dsym.asCls).isDefined end JSBuilder trait JSBuilderArgNumSanityChecks(using TL, Config, Elaborator.State) extends JSBuilder: private val doInstrument = config.sanityChecks.isDefined private val init = true def instrument: Bool = require(init, "trait body is not yet initialized") doInstrument override def checkMLsCalls: Bool = instrument override def checkSelections: Bool = instrument override def freezeDefinitions: Bool = instrument val functionParamVarargSymbol = semantics.TempSymbol(N, "args") override def setupFunction(name: Option[Str], params: ParamList, body: Block, isLambda: Bool)(using Raise, Scope): (Document, Document) = // * We used to instrument `fun f(x, y) = x + y` into something like // * `function f(...args) { runtime.checkArgs("f", 2, true, args.length); let x = args[0]; let y = args[1]; x + y }` // * which was very verbose, in addition to possibly making things quite inefficient. // * Now, we no longer instrument lambdas (which affects extra parameter lists), // * and we instead use the JS builtin `arguments` array to get the number of received arguments, as in // * `function f(x, y) { runtime.checkArgs("f", 2, true, arguments.length); x + y }` // * The idea is that later on, we'll add a runtime type sanity check as well anyway, // * which will check arguments against the erased parameter type, // * including checking they are not `undefined`, which should achieve most of the benefit. /* if instrument then val paramsList = params.params.map(p => scope.allocateName(p.sym)) val paramRest = params.restParam.map(p => scope.allocateName(p.sym)) val paramsStr = scope.allocateName(functionParamVarargSymbol, shadow = true) val functionName = JSBuilder.makeStringLiteral(name.fold("")(n => s"${JSBuilder.escapeStringCharacters(n)}")) val checkArgsNum = doc"\n$runtimeVar.checkArgs($functionName, ${params.paramCountLB}, ${params.paramCountUB.toString}, $paramsStr.length);" val paramsAssign = paramsList.zipWithIndex.map{(nme, i) => doc"\nlet ${nme} = ${paramsStr}[$i];"}.mkDocument("") val restAssign = paramRest match case N => doc"" case S(p) => doc"\nlet $p = $runtimeVar.Tuple.slice($paramsStr, ${params.paramCountLB}, 0);" (doc"...$paramsStr", doc"$checkArgsNum$paramsAssign$restAssign${this.body(body, endSemi = false)}") */ if instrument && !isLambda then val functionName = JSBuilder.makeStringLiteral(name.fold("")(n => s"${JSBuilder.escapeStringCharacters(n)}")) val checkArgsNum = doc"\n$runtimeVar.checkArgs($functionName, ${params.paramCountLB}, ${params.paramCountUB.toString}, arguments.length);" val paramsList = params.params.map(p => scope.allocateName(p.sym)) .++(params.restParam.map(p => "..." + scope.allocateName(p.sym))) .mkDocument(", ") (paramsList, doc"$checkArgsNum${this.body(body, endSemi = false)}") else super.setupFunction(name, params, body, isLambda = isLambda) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Analysis.scala ================================================ package hkmc2 package codegen package llir import scala.annotation.tailrec import scala.collection.immutable.* import scala.collection.mutable.ListBuffer import scala.collection.mutable.{HashMap => MutHMap} import scala.collection.mutable.{HashSet => MutHSet, Set => MutSet} import mlscript._ import mlscript.utils._ import mlscript.utils.shorthands._ import syntax.Tree.UnitLit import semantics.{BuiltinSymbol, InnerSymbol} class UsefulnessAnalysis(verbose: Bool = false): import Expr._ import Node._ def log(x: Any) = if verbose then println(x) val uses = MutHMap[(Local, Int), Int]() val defs = MutHMap[Local, Int]() private def addDef(x: Local) = defs.update(x, defs.getOrElse(x, 0) + 1) private def addUse(x: Local) = val def_count = defs.get(x) match case None => throw Exception(s"Use of undefined variable $x") case Some(value) => value val key = (x, defs(x)) uses.update(key, uses.getOrElse(key, 0) + 1) private def f(x: TrivialExpr): Unit = x match case Ref(name) => addUse(name) case _ => () private def f(x: Expr): Unit = x match case Ref(name) => addUse(name) case Literal(lit) => case CtorApp(name, args) => args.foreach(f) case Select(name, cls, field) => addUse(name) case BasicOp(name, args) => args.foreach(f) case AssignField(assignee, _, _, value) => addUse(assignee) f(value) private def f(x: Node): Unit = x match case Result(res) => res.foreach(f) case Jump(defn, args) => args.foreach(f) case Case(scrut, cases, default) => scrut match case Ref(name) => addUse(name) case _ => () cases.foreach { case (cls, body) => f(body) }; default.foreach(f) case LetMethodCall(names, cls, method, args, body) => addUse(method); args.foreach(f); names.foreach(addDef); f(body) case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) case LetCall(names, defn, args, body) => args.foreach(f); names.foreach(addDef); f(body) def run(x: Func) = x.params.foreach(addDef) f(x.body) uses.toMap class FreeVarAnalysis(ctx: Local => Func): import Expr._ import Node._ private val visited = MutHSet[Local]() private def f(using defined: Set[Local])(defn: Func, fv: Set[Local]): Set[Local] = val defined2 = defn.params.foldLeft(defined)((acc, param) => acc + param) f(using defined2)(defn.body, fv) private def f(using defined: Set[Local])(expr: Expr, fv: Set[Local]): Set[Local] = expr match case Ref(name) => if defined.contains(name) then fv else fv + name case Literal(lit) => fv case CtorApp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Select(name, cls, field) => if defined.contains(name) then fv else fv + name case BasicOp(name, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case AssignField(assignee, _, _, value) => f(using defined)( value.toExpr, if defined.contains(assignee) then fv + assignee else fv ) private def f(using defined: Set[Local])(node: Node, fv: Set[Local]): Set[Local] = node match case Result(res) => res.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Jump(defn, args) => args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) case Case(scrut, cases, default) => val fv2 = scrut match case Ref(name) => if defined.contains(name) then fv else fv + name case _ => fv val fv3 = cases.foldLeft(fv2): case (acc, (cls, body)) => f(using defined)(body, acc) default match case Some(body) => f(using defined)(body, fv3) case None => fv3 case Panic(msg) => fv case LetMethodCall(resultNames, cls, method, args, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name) f(using defined2)(body, fv2) case LetExpr(name, expr, body) => val fv2 = f(using defined)(expr, fv) val defined2 = defined + name f(using defined2)(body, fv2) case LetCall(resultNames, defn, args, body) => var fv2 = args.foldLeft(fv)((acc, arg) => f(using defined)(arg.toExpr, acc)) val defined2 = resultNames.foldLeft(defined)((acc, name) => acc + name) f(using defined2)(body, fv2) def run(node: Node) = f(using Set.empty)(node, Set.empty) def run_with(node: Node, defined: Set[Local]) = f(using defined)(node, Set.empty) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala ================================================ package hkmc2 package codegen package llir import scala.collection.mutable.ListBuffer import scala.collection.mutable.{HashMap => MutMap} import mlscript.utils.* import mlscript.utils.shorthands.* import utils.* import document.* import Message.MessageContext import Scope.scope import syntax.Tree import semantics.* import codegen.llir.{ Program => LlirProgram, Node, Func } import codegen.Program import cpp.Expr.StrLit private def bErrStop(msg: Message)(using Raise) = raise(ErrorReport(msg -> N :: Nil, source = Diagnostic.Source.Compilation)) throw LowLevelIRError("stopped") final case class FuncInfo(paramsSize: Int) final case class BuiltinSymbols( var callableSym: Opt[Local] = None, var thisSym: Opt[Local] = None, var builtinSym: Opt[Local] = None, fieldSym: MutMap[Int, VarSymbol] = MutMap.empty, applySym: MutMap[Int, BlockMemberSymbol] = MutMap.empty, tupleSym: MutMap[Int, DefinitionSymbol[? <: ClassLikeDef]] = MutMap.empty, runtimeSym: Opt[TempSymbol] = None, ): def hiddenClasses = callableSym.toSet final case class Ctx( def_acc: ListBuffer[Func], class_acc: ListBuffer[ClassInfo], symbol_ctx: Map[Local, Local] = Map.empty, fn_ctx: Map[Local, FuncInfo] = Map.empty, // is a known function class_ctx: Map[DefinitionSymbol[? <: ClassLikeDef], ClassInfo] = Map.empty, class_sym_ctx: Map[BlockMemberSymbol, DefinitionSymbol[? <: ClassLikeDef]] = Map.empty, flow_ctx: Map[Path, Local] = Map.empty, isTopLevel: Bool = true, method_class: Opt[DefinitionSymbol[? <: ClassLikeDef]] = None, builtinSym: BuiltinSymbols = BuiltinSymbols() ): def addFuncName(n: Local, paramsSize: Int) = copy(fn_ctx = fn_ctx + (n -> FuncInfo(paramsSize))) def findFuncName(n: Local)(using Raise) = fn_ctx.get(n) match case None => bErrStop(msg"Function name not found: ${n.toString()}") case Some(value) => value def addClassInfo(n: DefinitionSymbol[? <: ClassLikeDef], bsym: BlockMemberSymbol, m: ClassInfo) = copy(class_ctx = class_ctx + (n -> m), class_sym_ctx = class_sym_ctx + (bsym -> n)) def addName(n: Local, m: Local) = copy(symbol_ctx = symbol_ctx + (n -> m)) def findName(n: Local)(using Raise) = symbol_ctx.get(n) match case None => bErrStop(msg"Name not found: ${n.toString}") case Some(value) => value def findClassInfo(n: DefinitionSymbol[? <: ClassLikeDef])(using Raise) = class_ctx.get(n) match case None => bErrStop(msg"Class not found: ${n.toString}") case Some(value) => value def addKnownClass(n: Path, m: Local) = copy(flow_ctx = flow_ctx + (n -> m)) def setClass(c: DefinitionSymbol[? <: ClassLikeDef]) = copy(method_class = Some(c)) def nonTopLevel = copy(isTopLevel = false) object Ctx: def empty(using Elaborator.State) = Ctx(ListBuffer.empty, ListBuffer.empty, builtinSym = BuiltinSymbols( runtimeSym = Some(Elaborator.State.runtimeSymbol) )) final class LlirBuilder(using Elaborator.State)(tl: TraceLogger, uid: FreshInt): import tl.{trace, log, logs} given TraceLogger = tl def er = Expr.Ref def nr = Node.Result def sr(x: Local) = er(x) def nsr(xs: Ls[Local]) = xs.map(er(_)) private def allocIfNew(l: Local)(using Raise, Scope): String = trace[Str](s"allocIfNew begin: $l", x => s"allocIfNew end: $x"): if scope.lookup(l).isDefined then getVar_!(l) else scope.allocateName(l) private def getVar_!(l: Local)(using Raise, Scope): String = trace[Str](s"getVar_! begin", x => s"getVar_! end: $x"): l match case ts: semantics.TermSymbol => ts.owner match case S(owner) => ts.id.name case N => ts.id.name case ts: semantics.BlockMemberSymbol => // this means it's a locally-defined member ts.nme case ts: semantics.InnerSymbol => scope.findThis_!(ts) case _ => scope.lookup_!(l, N) private def symMap(s: Local)(using ctx: Ctx)(using Raise, Scope) = ctx.findName(s) private def newTemp = TempSymbol(N, "x") private def newNamedTemp(name: Str) = TempSymbol(N, name) private def newNamedBlockMem(name: Str) = BlockMemberSymbol(name, Nil) private def newNamed(name: Str) = VarSymbol(Tree.Ident(name)) private def newClassSym(name: Str) = ClassSymbol(Tree.TypeDef(hkmc2.syntax.Cls, Tree.Empty(), N), Tree.Ident(name)) private def newTupleSym(len: Int) = ClassSymbol(Tree.TypeDef(hkmc2.syntax.Cls, Tree.Empty(), N), Tree.Ident(s"Tuple$len")) private def newVarSym(name: Str) = VarSymbol(Tree.Ident(name)) private def newFunSym(name: Str) = BlockMemberSymbol(name, Nil) private def newBuiltinSym(name: Str) = BuiltinSymbol(name, false, false, false, false) private def builtinField(n: Int)(using Ctx) = summon[Ctx].builtinSym.fieldSym.getOrElseUpdate(n, newVarSym(s"field$n")) private def builtinApply(n: Int)(using Ctx) = summon[Ctx].builtinSym.applySym.getOrElseUpdate(n, newFunSym(s"apply$n")) private def builtinTuple(n: Int)(using Ctx) = summon[Ctx].builtinSym.tupleSym.getOrElseUpdate(n, newTupleSym(n)) private def builtinCallable(using ctx: Ctx) : Local = ctx.builtinSym.callableSym match case None => val sym = newBuiltinSym("Callable") ctx.builtinSym.callableSym = Some(sym); sym case Some(value) => value private def builtinThis(using ctx: Ctx) : Local = ctx.builtinSym.thisSym match case None => val sym = newBuiltinSym("") ctx.builtinSym.thisSym = Some(sym); sym case Some(value) => value private def builtin(using ctx: Ctx) : Local = ctx.builtinSym.builtinSym match case None => val sym = newBuiltinSym("") ctx.builtinSym.builtinSym = Some(sym); sym case Some(value) => value private def bBind(name: Opt[Local], e: Result, body: Block)(k: TrivialExpr => Ctx ?=> Node)(ct: Block)(using ctx: Ctx)(using Raise, Scope): Node = trace[Node](s"bBind begin: $name", x => s"bBind end: ${x.show}"): bResult(e): case r: Expr.Ref => given Ctx = ctx.addName(name.getOrElse(newTemp), r.sym) log(s"bBind ref: $name -> $r") bBlock(body)(k)(ct) case l: Expr.Literal => val v = newTemp given Ctx = ctx.addName(name.getOrElse(newTemp), v) log(s"bBind lit: $name -> $v") Node.LetExpr(v, l, bBlock(body)(k)(ct)) private def bArgs(e: Ls[Arg])(k: Ls[TrivialExpr] => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope): Node = trace[Node](s"bArgs begin", x => s"bArgs end: ${x.show}"): e match case Nil => k(Nil) case Arg(spread, x) :: xs => bPath(x): case r: TrivialExpr => bArgs(xs): case rs: Ls[TrivialExpr] => k(r :: rs) private def bPaths(e: Ls[Path])(k: Ls[TrivialExpr] => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope): Node = trace[Node](s"bArgs begin", x => s"bArgs end: ${x.show}"): e match case Nil => k(Nil) case x :: xs => bPath(x): case r: TrivialExpr => bPaths(xs): case rs: Ls[TrivialExpr] => k(r :: rs) private def bNestedFunDef(e: FunDefn)(k: TrivialExpr => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope): Node = val FunDefn(_own, sym, dSym, params, body) = e // generate it as a single named lambda expression that may be self-recursing if params.length === 0 then bErrStop(msg"Function without arguments not supported: ${params.length.toString}") else val fstParams = params.head val wrappedLambda = params.tail.foldRight(body)((params, acc) => Return(Lambda(params, acc), false)) bLam(Lambda(fstParams, wrappedLambda), S(sym.nme), S(sym))(k)(using ctx) private def bFunDef(e: FunDefn)(using ctx: Ctx)(using Raise, Scope): Func = trace[Func](s"bFunDef begin: ${e.sym}", x => s"bFunDef end: ${x.show}"): val FunDefn(_own, sym, dSym, params, body) = e assert(ctx.isTopLevel) if params.length === 0 then bErrStop(msg"Function without arguments not supported: ${params.length.toString}") else val paramsList = params.head.params val ctx2 = paramsList.foldLeft(ctx)((acc, x) => acc.addName(x.sym, x.sym)).nonTopLevel val pl = paramsList.map(_.sym) val wrappedLambda = params.tail.foldRight(body)((params, acc) => Return(Lambda(params, acc), false)) Func( uid.make, sym, params = pl, resultNum = 1, body = bBlockWithEndCont(wrappedLambda)(x => Node.Result(Ls(x)))(using ctx2) ) private def bMethodDef(e: FunDefn)(using ctx: Ctx)(using Raise, Scope): Func = trace[Func](s"bFunDef begin: ${e.sym}", x => s"bFunDef end: ${x.show}"): val FunDefn(_own, sym, dSym, params, body) = e if !ctx.isTopLevel then bErrStop(msg"Non top-level definition ${sym.nme} not supported") else if params.length === 0 then bErrStop(msg"Function without arguments not supported: ${params.length.toString}") else val paramsList = params.head.params val ctx2 = paramsList.foldLeft(ctx)((acc, x) => acc.addName(x.sym, x.sym)).nonTopLevel val pl = paramsList.map(_.sym) val wrappedLambda = params.tail.foldRight(body)((params, acc) => Return(Lambda(params, acc), false)) Func( uid.make, sym, params = pl, resultNum = 1, body = bBlockWithEndCont(wrappedLambda)(x => Node.Result(Ls(x)))(using ctx2) ) private def bClsLikeDef(e: ClsLikeDefn)(using ctx: Ctx)(using Raise, Scope): ClassInfo = trace[ClassInfo](s"bClsLikeDef begin", x => s"bClsLikeDef end: ${x.show}"): val ClsLikeDefn( _own, isym, _sym, _ctorSym, kind, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable) = e if !ctx.isTopLevel then bErrStop(msg"Non top-level definition ${isym.toString()} not supported") else val clsParams = paramsOpt.fold(Nil)(_.paramSyms) given Ctx = ctx.setClass(isym) val funcs = methods.map(bMethodDef) def parentFromPath(p: Path): Ls[Local] = p match case Value.Ref(l, disamb) => fromMemToClass(l.orElseDisamb(disamb)) :: Nil case _ => bErrStop(msg"Unsupported parent path ${p.toString()}") ClassInfo( uid.make, isym, clsParams, parentSym.fold(Nil)(parentFromPath), funcs.map(f => f.name -> f).toMap, ) private def bLam(lam: Lambda, nameHint: Opt[Str], recName: Opt[Local])(k: TrivialExpr => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope) : Node = trace[Node](s"bLam begin", x => s"bLam end: ${x.show}"): val Lambda(params, body) = lam // Generate an auxiliary class inheriting from Callable val freeVars = lam.freeVarsLLIR -- body.definedVars -- recName.iterator -- ctx.fn_ctx.keySet log(s"Defined vars: ${body.definedVars}") log(s"Match free vars: ${lam.freeVarsLLIR -- body.definedVars} ${ctx.fn_ctx.keySet} ${params.params.map(p => p.sym)}") log(s"Lot: $lam") val name = newClassSym(s"Lambda${nameHint.fold("")(x => "_" + x)}") val freeVarsList = freeVars.toList val args = freeVarsList.map(symMap) // args may have the same name (with different uid) // it's not allowed when generating the names of fields in the backend val clsParams = args.zipWithIndex.map: case (arg, i) => newVarSym(s"lam_arg$i") val applyParams = params.params // add the parameters of lambda expression to the context val ctx2 = applyParams.foldLeft(ctx)((acc, x) => acc.addName(x.sym, x.sym)) // add the free variables (class parameters) to the context val ctx3 = clsParams.iterator.zip(freeVarsList).foldLeft(ctx2): case (acc, (param, arg)) => acc.addName(arg, param) val ctx4 = recName.fold(ctx3)(x => ctx3.addName(x, builtinThis)).nonTopLevel val pl = applyParams.map(_.sym) val method = Func( uid.make, builtinApply(params.params.length), params = pl, resultNum = 1, body = bBlockWithEndCont(body)(x => Node.Result(Ls(x)))(using ctx4) ) ctx.class_acc += ClassInfo( uid.make, name, clsParams, builtinCallable :: Nil, Map(method.name -> method), ) val v: Local = newTemp val new_ctx = recName.fold(ctx)(x => ctx.addName(x, v)) Node.LetExpr(v, Expr.CtorApp(name, args.map(sr)), k(v |> sr)(using new_ctx)) private def bValue(v: Value)(k: TrivialExpr => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope) : Node = trace[Node](s"bValue { $v } begin", x => s"bValue end: ${x.show}"): v match case Value.Ref(l: TermSymbol, _) if l.owner.nonEmpty => k(l |> sr) case Value.Ref(sym, disamb) if sym.nme.isCapitalized => val v: Local = newTemp Node.LetExpr(v, Expr.CtorApp(fromMemToClass(sym.orElseDisamb(disamb)), Ls()), k(v |> sr)) case Value.Ref(l, _) => ctx.fn_ctx.get(l) match case Some(f) => val tempSymbols = (0 until f.paramsSize).map(x => newNamed("arg")) val paramsList = PlainParamList( (0 until f.paramsSize).zip(tempSymbols).map((_n, sym) => Param(FldFlags.empty, sym, N, Modulefulness.none)).toList) val app = Call(v, tempSymbols.map(x => Arg(N, Value.Ref(x))).toList ne_:: Nil)(true, false, false) bLam(Lambda(paramsList, Return(app, false)), S(l.nme), N)(k) case None => k(ctx.findName(l) |> sr) case Value.This(sym) => bErrStop(msg"Unsupported value: This") case Value.Lit(lit) => k(Expr.Literal(lit)) private def getClassOfField(p: DefinitionSymbol[?])(using ctx: Ctx)(using Raise, Scope): Local = trace[Local](s"bClassOfField { $p } begin", x => s"bClassOfField end: $x"): p match case ts: TermSymbol => ts.owner.get case ms: MemberSymbol => ms.defn match case Some(d: ClassLikeDef) => d.owner.get case Some(d: TermDefinition) => d.owner.get case Some(value) => bErrStop(msg"Member symbol without class definition ${value.toString}") case None => bErrStop(msg"Member symbol without definition ${ms.toString}") private def fromMemToClass(m: Symbol)(using ctx: Ctx)(using Raise, Scope): DefinitionSymbol[? <: ClassLikeDef] = trace[DefinitionSymbol[? <: ClassLikeDef]](s"bFromMemToClass $m", x => s"bFromMemToClass end: $x"): m match case ms: DefinitionSymbol[?] => ms.defn match case Some(d: TermDefinition) => val companion = d.companionClass.getOrElse(bErrStop(msg"Term definition without companion ${d.toString}")) fromMemToClass(companion) case Some(d: ClassLikeDef) => d.sym.asClsLike.getOrElse(bErrStop(msg"Class definition without symbol")) case Some(value) => bErrStop(msg"Member symbol without class definition ${value.toString}") case None => bErrStop(msg"Member symbol without definition ${ms.toString}") case _ => bErrStop(msg"Unsupported symbol kind ${m.toString}") private def bPath(p: Path)(k: TrivialExpr => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope) : Node = trace[Node](s"bPath { $p } begin", x => s"bPath end: ${x.show}"): p match case s @ Select(Value.Ref(sym, _), Tree.Ident("Unit")) if sym is ctx.builtinSym.runtimeSym.get => bPath(Value.Lit(Tree.UnitLit(false)))(k) case s @ Select(Value.Ref(cls: ClassSymbol, _), name) if ctx.method_class.contains(cls) => s.symbol match case None => ctx.flow_ctx.get(p) match case Some(cls) => k(cls |> sr) case None => bErrStop(msg"Unsupported selection by users") case Some(s) => k(s |> sr) case s @ DynSelect(qual, fld, arrayIdx) => bErrStop(msg"Unsupported dynamic selection") case s @ Select(qual, name) => log(s"bPath Select: $qual.$name with ${s.symbol}") s.symbol match case None => ctx.flow_ctx.get(qual) match case Some(cls) => bPath(qual): case q: Expr.Ref => val v: Local = newTemp val field = name.name Node.LetExpr(v, Expr.Select(q.sym, cls, field), k(v |> sr)) case q: Expr.Literal => bErrStop(msg"Unsupported select on literal") case None => log(s"${ctx.flow_ctx}") bErrStop(msg"Unsupported selection by users") case Some(value) => bPath(qual): case q: Expr.Ref => val v: Local = newTemp val cls = getClassOfField(s.symbol.get) val field = name.name Node.LetExpr(v, Expr.Select(q.sym, cls, field), k(v |> sr)) case q: Expr.Literal => bErrStop(msg"Unsupported select on literal") case x: Value => bValue(x)(k) private def bResult(r: Result)(k: TrivialExpr => Ctx ?=> Node)(using ctx: Ctx)(using Raise, Scope) : Node = trace[Node](s"bResult begin", x => s"bResult end: ${x.show}"): r match case Call(_, argss) if argss.sizeIs > 1 => bErrStop(msg"Calls with multiple argument lists are not yet supported in LLIR") case Call(Value.Ref(sym: BuiltinSymbol, _), argss) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetExpr(v, Expr.BasicOp(sym, args), k(v |> sr)) case Call(Value.Ref(sym, S(disamb)), argss) if disamb.defn.exists(defn => defn match case cls: ClassLikeDef => true case trm: TermDefinition => trm.companionClass.isDefined case _ => false ) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetExpr(v, Expr.CtorApp(fromMemToClass(disamb), args), k(v |> sr)) case Call(Value.Ref(sym: DefinitionSymbol[?], _), argss) if sym.defn.exists(defn => defn match case cls: ClassLikeDef => true case trm: TermDefinition => trm.companionClass.isDefined case _ => false ) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetExpr(v, Expr.CtorApp(fromMemToClass(sym), args), k(v |> sr)) case Call(s @ Value.Ref(sym, _), argss) => val v: Local = newTemp ctx.fn_ctx.get(sym) match case Some(f) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => Node.LetCall(Ls(v), sym, args, k(v |> sr)) case None => bPath(s): case f: TrivialExpr => bArgs(argss.flatten): case args: Ls[TrivialExpr] => Node.LetMethodCall(Ls(v), builtinCallable, builtinApply(args.length), f :: args, k(v |> sr)) case Call(Select(Value.Ref(_: TopLevelSymbol, _), Tree.Ident("builtin")), argss) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetCall(Ls(v), builtin, args, k(v |> sr)) case Call(Select(Select(Value.Ref(_: TopLevelSymbol, _), Tree.Ident("console")), Tree.Ident("log")), argss) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetCall(Ls(v), builtin, Expr.Literal(Tree.StrLit("println")) :: args, k(v |> sr)) case Call(Select(Select(Value.Ref(_: TopLevelSymbol, _), Tree.Ident("Math")), Tree.Ident(mathPrimitive)), argss) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetCall(Ls(v), builtin, Expr.Literal(Tree.StrLit(mathPrimitive)) :: args, k(v |> sr)) case Call(s @ Select(r @ Value.Ref(sym, _), Tree.Ident(fld)), argss) if s.symbol.isDefined => bPath(r): case r => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp log(s"Method Call Select: $r.$fld with ${s.symbol}") Node.LetMethodCall(Ls(v), getClassOfField(s.symbol.get), s.symbol.get, r :: args, k(v |> sr)) case Call(_, _) => bErrStop(msg"Unsupported kind of Call ${r.toString()}") case Instantiate( false, Value.Ref(sym, S(disamb: (ClassSymbol | ModuleOrObjectSymbol))), argss) => bArgs(argss.flatten): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetExpr(v, Expr.CtorApp(fromMemToClass(disamb), args), k(v |> sr)) case Instantiate(_, cls, argss) => bErrStop(msg"Unsupported kind of Instantiate") case lam @ Lambda(params, body) => bLam(lam, N, N)(k) case Tuple(false, elems) => bArgs(elems): case args: Ls[TrivialExpr] => val v: Local = newTemp Node.LetExpr(v, Expr.CtorApp(builtinTuple(elems.length), args), k(v |> sr)) case Record(mut, fields) => bErrStop(msg"Unsupported value: Rcd") case x: Path => bPath(x)(k) private def bBlockWithEndCont(blk: Block)(k: TrivialExpr => Ctx ?=> Node)(using Ctx)(using Raise, Scope) : Node = bBlock(blk)(k)(End("")) private def bBlock(blk: Block)(k: TrivialExpr => Ctx ?=> Node)(ct: Block)(using ctx: Ctx)(using Raise, Scope) : Node = trace[Node](s"bBlock begin", x => s"bBlock end: ${x.show}"): blk match case Match(scrut, arms, dflt, rest) => bPath(scrut): case e: TrivialExpr => val nextCont = Begin(rest, ct) val jp: BlockMemberSymbol = newNamedBlockMem("j") val fvset = nextCont.freeVarsLLIR -- nextCont.definedVars -- ctx.fn_ctx.keySet val fvs1 = fvset.toList log(s"Match free vars: $fvset ${nextCont.freeVarsLLIR -- nextCont.definedVars} $fvs1") val new_ctx = fvs1.foldLeft(ctx)((acc, x) => acc.addName(x, x)) val fvs = fvs1.map(new_ctx.findName(_)) def cont(x: TrivialExpr)(using ctx: Ctx) = Node.Jump( jp, fvs1.map(x => ctx.findName(x)).map(sr) ) val casesList: Ls[(Pat, Node)] = arms.map: case (Case.Lit(lit), body) => (Pat.Lit(lit), bBlock(body)(cont)(nextCont)(using ctx)) case (Case.Cls(cls, _), body) => (Pat.Class(cls), bBlock(body)(cont)(nextCont)(using ctx)) case (Case.Tup(len, inf), body) => val ctx2 = ctx.addKnownClass(scrut, builtinTuple(len)) (Pat.Class(builtinTuple(len)), bBlock(body)(cont)(nextCont)(using ctx2)) val defaultCase = dflt.map(bBlock(_)(cont)(nextCont)(using ctx)) val jpdef = Func( uid.make, jp, params = fvs, resultNum = 1, bBlock(rest)(k)(ct)(using new_ctx), ) summon[Ctx].def_acc += jpdef Node.Case(e, casesList, defaultCase) case Return(res, implct) => bResult(res)(x => Node.Result(Ls(x))) case Throw(Instantiate(false, Select(Value.Ref(_, _), ident), Ls(Arg(N, Value.Lit(Tree.StrLit(e)))) :: Nil)) if ident.name === "Error" => Node.Panic(e) case Label(label, loop, body, rest) => TODO("Label not supported") case Break(label) => TODO("Break not supported") case Continue(label) => TODO("Continue not supported") case Begin(sub, rest) => // re-associate rest blocks to correctly handle the continuation sub match case _: BlockTail => val definedVars = sub.definedVars definedVars.foreach(allocIfNew) bBlock(sub)(x => bBlock(rest)(k)(ct))(Begin(rest, ct)) case Assign(lhs, rhs, rest2) => bBlock(Assign(lhs, rhs, Begin(rest2, rest)))(k)(ct) case Begin(sub, rest2) => bBlock(Begin(sub, Begin(rest2, rest)))(k)(ct) case Define(defn, rest2) => bBlock(Define(defn, Begin(rest2, rest)))(k)(ct) case Match(scrut, arms, dflt, rest2) => bBlock(Match(scrut, arms, dflt, Begin(rest2, rest)))(k)(ct) case _ => TODO(s"Other non-tail sub components of Begin not supported $sub") case TryBlock(sub, finallyDo, rest) => TODO("TryBlock not supported") case Assign(lhs, rhs, rest) => bBind(S(lhs), rhs, rest)(k)(ct) case AssignField(lhs, nme, rhs, rest) => TODO("AssignField not supported") case Define(fd: FunDefn, rest) => if ctx.isTopLevel then val f = bFunDef(fd) ctx.def_acc += f bBlock(rest)(k)(ct) else bNestedFunDef(fd): case r: TrivialExpr => bBlock(rest)(k)(ct) case Define(_: ClsLikeDefn, rest) => bBlock(rest)(k)(ct) case End(msg) => k(Expr.Literal(Tree.UnitLit(false))) case Scoped(_, body) => bBlock(body)(k)(ct) case _: Block => val docBlock = blk.showAsTree bErrStop(msg"Unsupported block: $docBlock") def registerClasses(b: Block)(using ctx: Ctx)(using Raise, Scope): Ctx = b match case Define(cd @ ClsLikeDefn(_own, isym, sym, ctorSym, kind, _paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor, mod, bufferable), rest) => if !auxParams.isEmpty then bErrStop(msg"The class ${sym.nme} has auxiliary parameters, which are not yet supported") val c = bClsLikeDef(cd) ctx.class_acc += c val new_ctx = ctx.addClassInfo(isym, sym, c) log(s"Define class: ${isym.toString()} -> ${ctx}") registerClasses(rest)(using new_ctx) case _ => b.subBlocks.foldLeft(ctx)((ctx, rest) => registerClasses(rest)(using ctx)) def registerBuiltinClasses(using ctx: Ctx)(using Raise, Scope): Ctx = ctx.builtinSym.tupleSym.foldLeft(ctx): case (ctx, (len, sym)) => val c = ClassInfo(uid.make, sym, (0 until len).map(x => builtinField(x)).toList, Nil, Map.empty) ctx.class_acc += c ctx.addClassInfo(sym, BlockMemberSymbol(sym.nme, Nil), c) def registerFunctions(b: Block)(using ctx: Ctx)(using Raise, Scope): Ctx = var ctx2 = ctx new BlockTraverser: applyBlock(b) override def applyBlock(b: Block): Unit = b match case Match(scrut, arms, dflt, rest) => applyBlock(rest) case Return(res, implct) => case Throw(exc) => case Label(label, loop, body, rest) => applyBlock(rest) case Break(label) => case Continue(label) => case Begin(sub, rest) => applyBlock(rest) case TryBlock(sub, finallyDo, rest) => applyBlock(rest) case Assign(lhs, rhs, rest) => applyBlock(rest) case AssignField(_, _, _, rest) => applyBlock(rest) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => applyBlock(rest) case Define(defn, rest) => applyDefn(defn); applyBlock(rest) case Scoped(_, body) => applyBlock(body) case End(msg) => override def applyDefn(defn: Defn): Unit = defn match case f: FunDefn => applyFunDefn(f) case _ => () override def applyFunDefn(fun: FunDefn): Unit = val FunDefn(_own, sym, dSym, params, body) = fun if params.length === 0 then bErrStop(msg"Function without arguments not supported: ${params.length.toString}") ctx2 = ctx2.addFuncName(sym, params.head.params.length) log(s"Define function: ${sym.nme} -> ${ctx2}") ctx2 def bProg(e: Program)(using Raise, Scope, Ctx): (LlirProgram, Ctx) = var ctx = summon[Ctx] // * Classes may be defined after other things such as functions, // * especially now that the elaborator moves all functions to the top of the block. ctx = registerClasses(e.main)(using ctx) ctx = registerFunctions(e.main)(using ctx) log(s"Classes: ${ctx.class_ctx}") val entryBody = bBlockWithEndCont(e.main)(x => Node.Result(Ls(x)))(using ctx) val entryFunc = Func( uid.make, newFunSym("entry"), params = Ls.empty, resultNum = 1, body = entryBody ) ctx.def_acc += entryFunc ctx = registerBuiltinClasses(using ctx) val prog = LlirProgram(ctx.class_acc.toSet, ctx.def_acc.toSet, entryFunc.name) ctx.class_acc.clear() ctx.def_acc.clear() (prog, ctx) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Fresh.scala ================================================ package hkmc2.codegen.llir final class FreshInt: private var counter = 0 def make: Int = { val tmp = counter counter += 1 tmp } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Interp.scala ================================================ package hkmc2 package codegen package llir import mlscript.* import mlscript.utils.* import scala.collection.immutable.* import scala.collection.mutable.ListBuffer import shorthands.* import scala.util.boundary, boundary.break import syntax.Tree import hkmc2.utils.TraceLogger import semantics.BuiltinSymbol enum Stuck: case StuckExpr(expr: Expr, msg: Str) case StuckNode(node: Node, msg: Str) override def toString: String = this match case StuckExpr(expr, msg) => s"StuckExpr(${expr.show}, $msg)" case StuckNode(node, msg) => s"StuckNode(${node.show}, $msg)" final case class InterpreterError(message: String) extends Exception(message) class Interpreter(tl: TraceLogger): import tl.{trace, log, logs} import Stuck._ private case class Configuration( var context: Ctx ) private type Result[T] = Either[Stuck, T] private enum Value: case Class(cls: ClassInfo, var fields: Ls[Value]) case Literal(lit: hkmc2.syntax.Literal) override def toString: String = import hkmc2.syntax.Tree.* this match case Class(cls, fields) => s"${cls.symbol.nme}(${fields.mkString(",")})" case Literal(IntLit(lit)) => lit.toString case Literal(BoolLit(lit)) => lit.toString case Literal(DecLit(lit)) => lit.toString case Literal(StrLit(lit)) => lit.toString case Literal(UnitLit(isNullNotUndefined)) => if isNullNotUndefined then "null" else "undefined" private final case class Ctx( bindingCtx: Map[Local, Value], classCtx: Map[Local, ClassInfo], funcCtx: Map[Local, Func], thisVal: Opt[Value], ) import Node._ import Expr._ private def getTrue(using ctx: Ctx) = Value.Literal(hkmc2.syntax.Tree.BoolLit(true)) private def getFalse(using ctx: Ctx) = Value.Literal(hkmc2.syntax.Tree.BoolLit(false)) private def eval(op: Str, x1: Value, x2: Value)(using ctx: Ctx): Opt[Value] = import Value.{Literal => Li} import hkmc2.syntax.Tree.* (op, x1, x2) match case ("+", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x + y))) case ("-", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x - y))) case ("*", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x * y))) case ("/", Li(IntLit(x)), Li(IntLit(y))) => S(Li(IntLit(x / y))) case ("&&", Li(BoolLit(x)), Li(BoolLit(y))) => S(if x && y then getTrue else getFalse) case ("||", Li(BoolLit(x)), Li(BoolLit(y))) => S(if x || y then getTrue else getFalse) case ("==", Li(IntLit(x)), Li(IntLit(y))) => S(if x === y then getTrue else getFalse) case ("===", Li(IntLit(x)), Li(IntLit(y))) => S(if x === y then getTrue else getFalse) case ("!=", Li(IntLit(x)), Li(IntLit(y))) => S(if x != y then getTrue else getFalse) case ("<=", Li(IntLit(x)), Li(IntLit(y))) => S(if x <= y then getTrue else getFalse) case (">=", Li(IntLit(x)), Li(IntLit(y))) => S(if x >= y then getTrue else getFalse) case (">", Li(IntLit(x)), Li(IntLit(y))) => S(if x > y then getTrue else getFalse) case ("<", Li(IntLit(x)), Li(IntLit(y))) => S(if x < y then getTrue else getFalse) case _ => N private def evalArgs(using ctx: Ctx)(exprs: Ls[TrivialExpr]): Result[Ls[Value]] = var values = ListBuffer.empty[Value] var stuck: Opt[Stuck] = None exprs foreach { expr => stuck match case None => eval_t(expr) match case L(x) => stuck = Some(x) case R(x) => values += x case _ => () } stuck.toLeft(values.toList) private def eval_t(expr: TrivialExpr)(using ctx: Ctx): Result[Value] = expr match case Ref(x: BuiltinSymbol) => x.nme match case "" => ctx.thisVal.toRight(StuckExpr(expr.toExpr, s"undefined this value")) case _ => L(StuckExpr(expr.toExpr, s"undefined builtin ${x.nme}")) case Ref(x) => ctx.bindingCtx.get(x).toRight(StuckExpr(expr.toExpr, s"undefined variable $x")) case Literal(x) => R(Value.Literal(x)) private def eval(expr: Expr)(using ctx: Ctx): Result[Value] = expr match case x: TrivialExpr => eval_t(x) case CtorApp(cls, args) => for xs <- evalArgs(args) cls <- ctx.classCtx.get(cls).toRight(StuckExpr(expr, s"undefined class ${cls.nme}")) yield Value.Class(cls, xs) case Select(name, cls, field) if field.forall(_.isDigit) => val nth = field.toInt ctx.bindingCtx.get(name).toRight(StuckExpr(expr, s"undefined variable $name")).flatMap { case Value.Class(cls2, xs) if cls === cls2.symbol => xs.lift(nth) match case Some(x) => R(x) case None => L(StuckExpr(expr, s"unable to find selected field $field")) case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) case x => L(StuckExpr(expr, s"unexpected value $x")) } case Select(name, cls, field) => ctx.bindingCtx.get(name).toRight(StuckExpr(expr, s"undefined variable $name")).flatMap { case Value.Class(cls2, xs) if cls === cls2.symbol => xs.zip(cls2.fields).find{_._2.nme === field} match case Some((x, _)) => R(x) case None => L(StuckExpr(expr, s"unable to find selected field $field")) case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) case x => L(StuckExpr(expr, s"unexpected value $x")) } case BasicOp(name, args) => boundary: evalArgs(args).flatMap( xs => name.nme match case "+" | "-" | "*" | "/" | "==" | "===" | "!=" | "<=" | ">=" | "<" | ">" => if xs.length < 2 then break: L(StuckExpr(expr, s"not enough arguments for basic operation $name")) else eval(name.nme, xs.head, xs.tail.head).toRight(StuckExpr(expr, s"unable to evaluate basic operation")) case _ => L(StuckExpr(expr, s"unexpected basic operation $name"))) case AssignField(assignee, cls, field, value) => for x <- eval_t(Ref(assignee): TrivialExpr) y <- eval_t(value) res <- x match case obj @ Value.Class(cls2, xs) if cls === cls2 => xs.zip(cls2.fields).find{_._2.nme === field} match case Some((_, _)) => obj.fields = xs.map(x => if x === obj then y else x) // Ideally, we should return a unit value here, but here we return the assignee value for simplicity. R(obj) case None => L(StuckExpr(expr, s"unable to find selected field $field")) case Value.Class(cls2, xs) => L(StuckExpr(expr, s"unexpected class $cls2")) case x => L(StuckExpr(expr, s"unexpected value $x")) yield res private def eval(node: Node)(using ctx: Ctx): Result[Ls[Value]] = node match case Result(res) => evalArgs(res) case Jump(func, args) => for xs <- evalArgs(args) func <- ctx.funcCtx.get(func).toRight(StuckNode(node, s"undefined function ${func.nme}")) ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ func.params.zip(xs)) res <- eval(func.body)(using ctx1) yield res case Case(scrut, cases, default) => eval_t(scrut) flatMap { case Value.Class(cls, fields) => cases.find { case (Pat.Class(cls2), _) => cls.symbol === cls2 case _ => false } match { case Some((_, x)) => eval(x) case None => default match case S(x) => eval(x) case N => L(StuckNode(node, s"can not find the matched case, ${cls.symbol} expected")) } case Value.Literal(lit) => cases.find { case (Pat.Lit(lit2), _) => lit === lit2 case _ => false } match { case Some((_, x)) => eval(x) case None => default match case S(x) => eval(x) case N => L(StuckNode(node, s"can not find the matched case, $lit expected")) } } case LetExpr(name, expr, body) => for x <- eval(expr) ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx + (name -> x)) res <- eval(body)(using ctx1) yield res case LetMethodCall(names, cls, method, args, body) => def lookup_method(cls: ClassInfo, method: Local): Option[Func] = // The methods with the same name in a subclass will override the method in the superclass. // But they have different symbols for the method definition. // So, we don't directly use the method symbol to find the method. // Instead, we fallback to use the method name. cls.methods.find(_._1.nme === method.nme).map(_._2) for ys <- evalArgs(args).flatMap { case (ths @ Value.Class(cls2, xs)) :: args => lookup_method(cls2, method).toRight(StuckNode(node, s"undefined method ${method.nme}")).flatMap { method => val ctx1 = ctx.copy( bindingCtx = ctx.bindingCtx ++ cls2.fields.zip(xs) ++ method.params.zip(args), thisVal = S(ths) ) eval(method.body)(using ctx1) } case _ => L(StuckNode(node, s"not enough arguments for method call, or the first argument is not a class")) } ctx2 = ctx.copy(bindingCtx = ctx.bindingCtx ++ names.zip(ys)) res <- eval(body)(using ctx2) yield res case LetCall(names, func, args, body) => for xs <- evalArgs(args) func <- ctx.funcCtx.get(func).toRight(StuckNode(node, s"undefined function ${func.nme}")) ctx1 = ctx.copy(bindingCtx = ctx.bindingCtx ++ func.params.zip(xs)) ys <- eval(func.body)(using ctx1) ctx2 = ctx.copy(bindingCtx = ctx.bindingCtx ++ names.zip(ys)) res <- eval(body)(using ctx2) yield res case Panic(msg) => L(StuckNode(node, msg)) private def f(prog: Program): Ls[Value] = val Program(classes, defs, entry) = prog given Ctx = Ctx( bindingCtx = Map.empty, classCtx = classes.map(cls => (cls.symbol, cls)).toMap, funcCtx = defs.map(func => (func.name, func)).toMap, thisVal = None, ) val entryFunc = summon[Ctx].funcCtx.getOrElse(entry, throw InterpreterError("Entry doesn't exist")) assert(entryFunc.params.isEmpty, "Entry function should not have parameters") eval(entryFunc.body) match case R(x) => x case L(x) => throw InterpreterError("Stuck evaluation: " + x.toString) def interpret(prog: Program): Str = val node = f(prog) node.map(_.toString).mkString(",") ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Llir.scala ================================================ package hkmc2 package codegen.llir import mlscript._ import mlscript.utils._ import mlscript.utils.shorthands._ import syntax._ import Message.MessageContext import document._ import codegen._ import util.Sorting import collection.immutable.SortedSet import language.implicitConversions import collection.mutable.{Map as MutMap, Set as MutSet, HashMap, ListBuffer} import hkmc2.semantics._ private def raw(x: String): Document = doc"$x" final case class LowLevelIRError(message: String) extends Exception(message) val hiddenPrefixes = Set("Tuple") def defaultHidden(x: Str): Bool = hiddenPrefixes.exists(x.startsWith) case class Program( classes: Set[ClassInfo], defs: Set[Func], entry: Local, ): def show = LlirDebugPrinter.mkDocument(this).toString implicit object ClassInfoOrdering extends Ordering[ClassInfo] { def compare(a: ClassInfo, b: ClassInfo) = a.id.compare(b.id) } case class ClassInfo( id: Int, symbol: DefinitionSymbol[? <: ClassLikeDef], fields: Ls[VarSymbol], parents: Ls[Local], methods: Map[Local, Func], ): override def hashCode: Int = id def show = LlirDebugPrinter.mkDocument(this).toString class FuncRef(var func: Local): def name: String = func.nme override def equals(o: Any): Bool = o match { case o: FuncRef => o.name === this.name case _ => false } implicit object FuncOrdering extends Ordering[Func] { def compare(a: Func, b: Func) = a.id.compare(b.id) } case class Func( id: Int, name: BlockMemberSymbol, params: Ls[Local], resultNum: Int, body: Node ): var recBoundary: Opt[Int] = None override def hashCode: Int = id def show = LlirDebugPrinter.mkDocument(this).toString sealed trait TrivialExpr: import Expr._ def toExpr: Expr = this match { case x: Expr => x } def foldRef(f: Local => TrivialExpr): TrivialExpr = this match case Ref(sym) => f(sym) case _ => this def iterRef(f: Local => Unit): Unit = this match case Ref(sym) => f(sym) case _ => () def show: String enum Expr: case Ref(sym: Local) extends Expr, TrivialExpr case Literal(lit: hkmc2.syntax.Literal) extends Expr, TrivialExpr case CtorApp(cls: DefinitionSymbol[? <: ClassLikeDef], args: Ls[TrivialExpr]) case Select(name: Local, cls: Local, field: Str) case BasicOp(name: BuiltinSymbol, args: Ls[TrivialExpr]) case AssignField(assignee: Local, cls: Local, field: Str, value: TrivialExpr) def show = LlirDebugPrinter.mkDocument(this).toString enum Pat: case Lit(lit: hkmc2.syntax.Literal) case Class(cls: Local) enum Node: // Terminal forms: case Result(res: Ls[TrivialExpr]) case Jump(func: Local, args: Ls[TrivialExpr]) case Case(scrutinee: TrivialExpr, cases: Ls[(Pat, Node)], default: Opt[Node]) case Panic(msg: Str) // Intermediate forms: case LetExpr(name: Local, expr: Expr, body: Node) case LetMethodCall(names: Ls[Local], cls: Local, method: Local, args: Ls[TrivialExpr], body: Node) case LetCall(names: Ls[Local], func: Local, args: Ls[TrivialExpr], body: Node) def show = LlirDebugPrinter.mkDocument(this).toString abstract class LlirPrinting: import hkmc2.utils.* import hkmc2.semantics.Elaborator.State def mkDocument(local: Local): Document def mkDocument(lit: Literal): Document = doc"${lit.idStr}" def mkDocument(texpr: TrivialExpr): Document = texpr match case Expr.Ref(sym) => mkDocument(sym) case Expr.Literal(lit) => mkDocument(lit) def mkDocument(expr: Expr): Document = expr match case Expr.Ref(sym) => doc"${mkDocument(sym)}" case Expr.Literal(lit) => doc"${lit.idStr}" case Expr.CtorApp(cls, args) => doc"${mkDocument(cls)}(${args.map(mkDocument).mkDocument(",")})" case Expr.Select(name, cls, field) => doc"${mkDocument(name)}.<${mkDocument(cls)}:$field>" case Expr.BasicOp(sym, args) => doc"${sym.nme}(${args.map(mkDocument).mkDocument(",")})" case Expr.AssignField(assignee, clsInfo, fieldName, value) => doc"${mkDocument(assignee)}.${fieldName} := ${mkDocument(value)}" def mkDocument(node: Node): Document = node match case Node.Result(res) => doc"${res.map(mkDocument).mkDocument(",")}" case Node.Jump(func, args) => doc"jump ${mkDocument(func)}(${args.map(mkDocument).mkDocument(",")})" case Node.Case(scrutinee, cases, default) => val docFirst = doc"case ${mkDocument(scrutinee)} of" val docCases = cases.map { case (pat, node) => doc"${pat.toString} => #{ # ${mkDocument(node)} #} " }.mkDocument(doc" # ") default match case N => doc"$docFirst #{ # $docCases #} " case S(dc) => val docDeft = doc"_ => #{ # ${mkDocument(dc)} #} " doc"$docFirst #{ # $docCases # $docDeft #} " case Node.Panic(msg) => doc"panic ${s"\"$msg\""}" case Node.LetExpr(x, expr, body) => doc"let ${mkDocument(x)} = ${mkDocument(expr)} in # ${mkDocument(body)}" case Node.LetMethodCall(xs, cls, method, args, body) => doc"let ${xs.map(mkDocument).mkDocument(",")} = ${mkDocument(cls)}.${method.nme}(${args.map(mkDocument).mkDocument(",")}) in # ${mkDocument(body)}" case Node.LetCall(xs, func, args, body) => doc"let* (${xs.map(mkDocument).mkDocument(",")}) = ${mkDocument(func)}(${args.map(mkDocument).mkDocument(",")}) in # ${mkDocument(body)}" def mkDocument(defn: Func): Document = def docParams(params: Ls[Local]): Document = params.map(mkDocument).mkDocument("(", ",", ")") given Conversion[String, Document] = raw val docFirst = doc"def ${mkDocument(defn.name)}${docParams(defn.params)} =" val docBody = mkDocument(defn.body) doc"$docFirst #{ # $docBody #} " def mkDocument(cls: ClassInfo): Document = given Conversion[String, Document] = raw val ext = if cls.parents.isEmpty then doc"" else " extends " :: cls.parents.map(mkDocument).mkDocument(", ") val docFirst = doc"class ${mkDocument(cls.symbol)}(${cls.fields.map(_.nme).mkDocument(",")})$ext" if cls.methods.isEmpty then doc"$docFirst" else val docMethods = cls.methods.map { (_, func) => mkDocument(func) }.toList.mkDocument(doc" # ") doc"$docFirst { #{ # $docMethods #} # }" def mkDocument(prog: Program, hide: Str => Bool = defaultHidden): Document = given Conversion[String, Document] = raw val t1 = prog.classes.iterator.filterNot(c => hide(c.symbol.nme)).toArray val t2 = prog.defs.toArray Sorting.quickSort(t1) Sorting.quickSort(t2) val docClasses = t1.filterNot(c => hide(c.symbol.nme)).map(mkDocument).toList.mkDocument(doc" # ") val docDefs = t2.map(mkDocument).toList.mkDocument(doc" # ") val docMain = doc"entry = ${mkDocument(prog.entry)}" doc" #{ $docClasses\n$docDefs\n$docMain #} " class LlirPrinter(using Raise, hkmc2.utils.Scope) extends LlirPrinting: import hkmc2.utils.* import hkmc2.semantics.Elaborator.State import Scope.scope def getVar(l: Local): String = l match case ts: hkmc2.semantics.TermSymbol => ts.owner match case S(owner) => scope.lookup_!(ts, N) case N => scope.lookup_!(ts, N) case ts: hkmc2.semantics.InnerSymbol => scope.lookup_!(ts, N) case _ => scope.lookup_!(l, N) def allocIfNew(l: Local): String = scope.lookup(l) match case S(_) => getVar(l) case N => scope.allocateName(l) override def mkDocument(local: Local): Document = allocIfNew(local) object LlirDebugPrinter extends LlirPrinting: import hkmc2.utils.* def docSymWithUid(sym: Local): Document = doc"${sym.nme}$$${sym.uid.toString()}" override def mkDocument(local: Local): Document = docSymWithUid(local) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Ctx.scala ================================================ package hkmc2 package codegen package wasm package text import mlscript.utils.*, shorthands.* import hkmc2.utils.* import document.* import document.Document import semantics.{ BlockMemberSymbol, Elaborator, InnerSymbol, LabelSymbol, ModuleOrObjectSymbol, ParamList, Symbol, TempSymbol, }, Elaborator.State import text.Param as WasmParam import Instructions.* import scala.collection.immutable.ListMap import scala.collection.mutable.{ArrayBuffer as ArrayBuf, Map as MutMap, LinkedHashSet} import scala.reflect.ClassTag /** Metadata for a REPL binding that can be imported by later Wasm modules. */ sealed trait SessionBinding: /** Returns the deduplication key for this binding. */ def bindingKey: Str /** Returns the symbols that should resolve to this binding. */ def bindingSyms: Seq[Local] /** Returns the export name if this binding is re-exported. */ def exportNameOpt: Opt[Str] = N object SessionBinding: val ReplModuleName: Str = "repl" /** Metadata for an exported function that later Wasm REPL modules can import. * * @param sym * The source symbol associated with the exported function. * @param moduleName * The Wasm module name used by the import. * @param exportName * The exported function name within `moduleName`. * @param funcType * The Wasm function type expected by the import. */ final case class SessionFunc( sym: BlockMemberSymbol, wrapId: Opt[Str] -> Opt[Str], moduleName: Str, exportName: Str, funcType: FunctionType, ) extends SessionBinding: def bindingKey: Str = s"func:$moduleName:$exportName" def bindingSyms: Seq[Local] = sym :: Nil override def exportNameOpt: Opt[Str] = S(exportName) /** Metadata for an exported global that later Wasm REPL modules can import. * * @param sym * The source symbol associated with the exported global. * @param moduleName * The Wasm module name used by the import. * @param exportName * The exported global name within `moduleName`. * @param globalType * The Wasm global type expected by the import. */ final case class SessionGlobal( sym: Symbol, wrapId: Opt[Str] -> Opt[Str], moduleName: Str, exportName: Str, globalType: GlobalType, ) extends SessionBinding: def bindingKey: Str = s"global:$moduleName:$exportName" def bindingSyms: Seq[Local] = sym :: Nil override def exportNameOpt: Opt[Str] = S(exportName) /** Metadata for a class type made visible to later Wasm REPL modules. * * @param sym * The block member symbol of the class. * @param typeInfo * The Wasm type information that must be recreated in importing modules. * @param runtimeTags * The class' runtime tag together with descendant class tags. * @param aliasSyms * Additional symbols that should resolve to this class binding. */ final case class SessionClass( sym: BlockMemberSymbol, wrapId: Opt[Str] -> Opt[Str], compType: CompType, objectTag: Opt[Int], runtimeTags: LinkedHashSet[Int], aliasSyms: Seq[Local] = Nil, ) extends SessionBinding: def bindingKey: Str = s"class:${sym.uid}" def bindingSyms: Seq[Local] = sym +: aliasSyms /** Metadata for a singleton object's backing global made visible to later Wasm REPL modules. * * @param blockSym * The block member symbol of the singleton object. * @param objectSym * Optional object/module symbol that should also resolve to this singleton binding. * @param moduleName * The Wasm module name used by the import. * @param exportName * The exported global name within `moduleName`. * @param globalTy * The Wasm reference type of the singleton backing global. */ final case class SessionSingleton( blockSym: BlockMemberSymbol, objectSym: Opt[ModuleOrObjectSymbol], wrapId: Opt[Str] -> Opt[Str], moduleName: Str, exportName: Str, globalTy: RefType, ) extends SessionBinding: def bindingKey: Str = s"singleton:$moduleName:$exportName" def bindingSyms: Seq[Local] = blockSym +: objectSym.toSeq override def exportNameOpt: Opt[Str] = S(exportName) /** The emitted Wasm module together with REPL/session export metadata. * * @param wat * The generated WAT for the module. * @param entryName * The name of the module entry function. * @param systemMemMinPages * The minimum number of imported system-memory pages required by the module. * @param sessionExports * Session bindings exported by this module for use by later REPL blocks. */ final case class CompiledWasmModule( wat: Document, entryName: Str, systemMemMinPages: Int, sessionExports: Seq[SessionBinding], ) /** Context used while collecting REPL/session exports for a single Wasm module. * * @param symbolsToExport * The symbols from the current module that should be recorded as session exports. * @param collectedBindings * The session bindings accumulated while compiling the current module. */ final class SessionExportCtx( val symbolsToExport: Set[Local], val collectedBindings: ArrayBuf[SessionBinding], ): def shouldExport(sym: Local): Bool = symbolsToExport(sym) def emit(binding: SessionBinding): Unit = collectedBindings += binding def freshCollector(): SessionExportCtx = SessionExportCtx(symbolsToExport, ArrayBuf.empty) /** A Wasm function and its associated information. * * Each instance of [[FuncInfo]] represents a single function definition in a WebAssembly module. * * @param sym * The source [[Symbol]] which this function is generated from. * @param wrapId * An pair of optional strings for adding a prefix and suffix to the generated identifier of this function. * @param typeUse * [[TypeUse]] of the function's type in the module's type section. * @param params * [[Seq]] of parameter local variables and their names. * @param resultTypes * The result types of the function. * @param locals * [[Seq]] of local variables (excluding parameters) and their names. * @param body * The expression of the function body. * @param exportName * Optional export name. */ class FuncInfo( val sym: BlockMemberSymbol | TempSymbol, val typeUse: TypeUse, val params: Seq[Local -> SymIdx], val resultTypes: Seq[Result], val locals: Seq[Local -> SymIdx], val body: Expr, val exportName: Opt[Str], val wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the function. */ val id = SymIdx(summon[Ctx].funcScp.allocateOrGetNameWrapped(sym, wrapId)) /** Returns the type of this function as a [[SignatureType]]. */ def getSignatureType: SignatureType = SignatureType( params = params.map((_, paramIdx) => WasmParam(paramIdx, RefType.anyref)), results = resultTypes, ) def toWat: Document = doc"""(func ${id.toWat}${ exportName.fold(doc""): e => doc""" (export "$e")""" } ${typeUse.toWat}${ getSignatureType.toWat.surroundUnlessEmpty(doc" ") } #{ ${ locals.map: p => doc"(local ${p._2.toWat} ${RefType.anyref.toWat})" .mkDocument(doc" # ").surroundUnlessEmpty(doc" # ") } # ${body.toWat} #} )""" end FuncInfo /** A Wasm global and its associated information. * * Each instance of [[GlobalInfo]] represents a single global definition in a WebAssembly module. * * @param globalType * The type of the global. * @param init * The initializer expression for the global. * @param exportName * Optional export name. * @param sym * The source [[Symbol]] which this global is generated from. * @param wrapId * An pair of optional strings for adding a prefix and suffix to the generated identifier of this global. */ class GlobalInfo( val globalType: GlobalType, val init: Expr, val exportName: Opt[Str], val sym: Symbol, val wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the global. */ val id: SymIdx = SymIdx(summon[Ctx].globalScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"""(global ${id.toWat}${ exportName.fold(doc""): name => doc""" (export "$name")""" } ${globalType.toWat} ${init.toWat})""" end GlobalInfo /** A WebAssembly memory and its associated information. * * Each instance of [[MemInfo]] represents a single memory definition in a WebAssembly module. * * @param sym * The source [[TempSymbol]] which this memory is generated from. * @param memType * The type of the memory. * @param wrapId * An pair of optional strings for adding a prefix and suffix to the generated identifier of this memory. */ class MemInfo(val sym: TempSymbol, val memType: MemType, val wrapId: Opt[Str] -> Opt[Str] = N -> N)(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the global. */ val id: SymIdx = SymIdx(summon[Ctx].memoryScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"(memory ${id.toWat} ${memType.toWat})" end MemInfo /** A Wasm type and its associated information. * * Each instance of [[TypeInfo]] represents a single type definition in a WebAssembly module. * * @param sym * The source [[Symbol]] which this type is generated from. * @param wrapId * An pair of optional strings for adding a prefix and suffix to the generated identifier of this type. * @param compType * The composite type this type definition represents. * @param objectTag * An optional object tag number associated with this type. */ final class TypeInfo( val sym: BlockMemberSymbol | TempSymbol, val compType: CompType, val objectTag: Opt[Int], val wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the type. */ val id = SymIdx(summon[Ctx].typeScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"(type ${id.toWat} ${compType.toWat})" /** A WebAssembly exception tag declaration. * * In Wasm, a `tag` names an exception kind and points to a function type that describes the payload values carried by * `throw tag ...` and extracted by matching `catch tag ...`. * * @param typeUse * The function type referenced by this tag. * @param sym * The source [[Symbol]] which this tag is generated from. */ class TagInfo(val typeUse: TypeUse, val sym: Symbol, val wrapId: Opt[Str] -> Opt[Str] = N -> N)(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the tag. */ val id: SymIdx = SymIdx(summon[Ctx].tagScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"""(tag ${id.toWat} (export "${id.id}") ${typeUse.toWat})""" end TagInfo enum WasmIntrinsicType: case TupleArray(mutable: Bool) /** Class containing identifiers of labels to jump to when breaking or continuing from a control flow structure. * * @param breakLabel * The identifier of the label to jump to for exiting the control flow structure, e.g. for `break` statements. * @param continueLabel * The identifier of the label to jump to for continuing the control flow structure, e.g. for `continue` statements * in loops. This is `None` for non-loop control flow structures. */ case class LabelTarget(breakLabel: Str, continueLabel: Opt[Str]) object FunctionCtx: def funcCtx(using funcCtx: FunctionCtx): FunctionCtx = funcCtx /** Context for tracking control flow jump targets. * * @param scp * [[Scope]] for generating WAT identifiers of labels in this control flow context. * @param breakLabel * The label to jump to for exiting this control flow context, e.g. for `break` statements. * @param continueLabel * The label to jump to for continuing this control flow context, e.g. for `continue` statements in loops. This is * `None` for non-loop contexts. */ private case class ControlFlowCtx(scp: Scope, breakLabel: LabelSymbol, continueLabel: Opt[LabelSymbol]) /** Context associated with codegen for a Wasm function. * * @param _params * The parameters of this function. * @param thisSym * The implicit `this` parameter symbol if this function is generated from a non-static method, or `N` otherwise. */ class FunctionCtx(_params: Ls[ParamList], thisSym: Opt[InnerSymbol])(using Raise, State): /** [[Scope]] for generating WAT identifiers of locals. */ private[text] val localScp = Scope.empty(Scope.Cfg.default) /** The parameter of this function, represented by a tuple of the symbol representing the parameter and its symbolic * identifier. */ val params: Seq[Local -> SymIdx] = if _params.length > 1 then lastWords("Multiple parameter lists are not yet supported") val thisParam = thisSym.map: dis => dis -> SymIdx(localScp.addToBindings(dis, "this", shadow = false)) thisParam.toSeq ++ _params.flatMap(_.paramSyms).map(p => p -> SymIdx(localScp.allocateName(p))) private val _locals = ArrayBuf.empty[Local] private var labels = ListMap.empty[LabelSymbol, FunctionCtx.ControlFlowCtx] /** Adds a Wasm local into this context. * * @param customName * An optional name for the local variable. If provided, the local will be emitted with the given name instead of * an auto-generated one. */ def addLocal(local: Local, customName: Opt[Str] = N): LocalIdx = customName match case S(name) => localScp.addToBindings(local, name, shadow = false) case N => localScp.allocateName(local) _locals += local LocalIdx(SymIdx(localScp.lookup_!(local, N))) /** Looks up the given `sym` in this function context, returning its [[LocalIdx]] if it exists. */ def lookupLocal(sym: Local): Opt[LocalIdx] = localScp.lookup(sym).map(idx => LocalIdx(SymIdx(idx))) /** Similar to [[lookupLocal]], but throws an exception if `sym` is not in this context. */ def lookupLocal_!(sym: Local, loc: Opt[Loc]): LocalIdx = LocalIdx(SymIdx(localScp.lookup_!(sym, loc))) /** The locals of this function, represented by a tuple of the symbol representing the parameter and its symbolic * identifier. */ def locals: Seq[Local -> SymIdx] = _locals.map(l => l -> SymIdx(localScp.lookup_!(l, N))).toSeq /** Pushes a label target for the dynamic extent of `body` and pops it afterwards. * * The `body` function is given a [[LabelTarget]] containing the `break` and `continue` labels corresponding to * `label`. * * @param hasContinueLabel * Indicates whether a `continue` label should be generated for this control flow context, e.g. for loops. */ def withLabel[T](label: LabelSymbol, hasContinueLabel: Bool)(body: LabelTarget => T): T = val ctrlFlowCtx = FunctionCtx.ControlFlowCtx( scp = labels.lastOption.fold(Scope.empty(Scope.Cfg.default))(_._2.scp.nest), breakLabel = label, continueLabel = if hasContinueLabel then S(LabelSymbol(N, s"${label.nme}_cont")) else N, ) labels += label -> ctrlFlowCtx val res = body: LabelTarget( breakLabel = ctrlFlowCtx.scp.allocateName(label), continueLabel = ctrlFlowCtx.continueLabel.map(cl => ctrlFlowCtx.scp.allocateName(cl)), ) labels = labels.init res /** Looks up the nearest in-scope target for `label`. */ def lookupLabel(label: LabelSymbol): Opt[LabelTarget] = labels.lastOption.flatMap: (_, ctrlFlowCtx) => ctrlFlowCtx.scp.lookup(label).map: labelId => LabelTarget( breakLabel = labelId, continueLabel = labels(label).continueLabel.map(cl => labels.last._2.scp.lookup_!(cl, N)), ) end FunctionCtx /** Generates a function body, providing an instance of [[FunctionCtx]] for parameter and locals tracking. * * Returns the result of the `mkBody` function along with the [[FunctionCtx]]. */ def genFuncBody[T]( params: Ls[ParamList], thisSym: Opt[InnerSymbol], )(mkBody: FunctionCtx ?=> T)(using Raise, State): T -> FunctionCtx = val funcCtx = FunctionCtx(params, thisSym) val result = mkBody(using funcCtx) result -> funcCtx object Ctx: case class SingletonInfo( globalName: Str, globalTy: RefType, ) val binaryOps: Map[Str, (Expr, Expr) => Expr] = Map( "plus_impl" -> i32.add, "minus_impl" -> i32.sub, "times_impl" -> i32.mul, "div_impl" -> i32.div_s, "mod_impl" -> i32.rem_s, "eq_impl" -> i32.eq, "neq_impl" -> i32.ne, "lt_impl" -> i32.lt_s, "le_impl" -> i32.le_s, "gt_impl" -> i32.gt_s, "ge_impl" -> i32.ge_s, ) val unaryOps: Map[Str, Expr => Expr] = Map( "neg_impl" -> (value => i32.sub(i32.const(0), value)), "pos_impl" -> identity, "not_impl" -> i32.eqz, ) val wasmIntrinsicArities: Map[Str, Int] = (binaryOps.keys.map(_ -> 2) ++ unaryOps.keys.map(_ -> 1)).toMap val wasmIntrinsicNameSet: Set[Str] = wasmIntrinsicArities.keySet def empty(using State): Ctx = Ctx() def ctx(using ctx: Ctx): Ctx = ctx extension (ref: CtxIdx | Symbol) private def prettyString: Str = ref match case idx: CtxIdx => s"type index `${idx.toWat.mkString()}`" case sym: Symbol => s"symbol `${sym.toString}`" end Ctx /** Context for [[WatBuilder]]. */ class Ctx(using State) extends ToWat: import Ctx.prettyString /** [[Scope]] for generating WAT identifiers of types. */ private[text] val typeScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all type definitions in the module mapped by their symbolic identifiers. */ private var types = ListMap.empty[SymIdx, TypeInfo] /** [[MutMap]] containing type symbols mapped to their corresponding [[TypeInfo]] instance. */ private val namedTypes = MutMap.empty[BlockMemberSymbol, TypeInfo] /** [[Scope]] for generating WAT identifiers of data segments. */ private[text] val dataSegmentScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all data segments in the module. */ private var dataSegments = ListMap.empty[SymIdx, DataSegment] /** [[Scope]] for generating WAT identifiers of element segments. */ private[text] val elemSegmentScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all element segments in the module. */ private var elemSegments = ListMap.empty[SymIdx, ElemSegment] /** [[Scope]] for generating WAT identifiers of functions. */ private[text] val funcScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all function definitions and imports in the module mapped by their symbolic identifiers. */ private var funcs = ListMap.empty[SymIdx, FuncInfo | Import[ExternType.Func]] /** [[MutMap]] containing function symbols mapped to the corresponding [[FuncInfo]] or [[Import]] instance. */ private val namedFuncs = MutMap.empty[Symbol, FuncInfo | Import[ExternType.Func]] /** [[Scope]] for generating WAT identifiers of memories. */ private[text] val memoryScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all memory definitions and imports in the module mapped by their symbolic identifiers. */ private var memories = ListMap.empty[SymIdx, MemInfo | Import[ExternType.Mem]] /** [[Scope]] for generating WAT identifiers of tags. */ private[text] val tagScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all tag definitions in the module. */ private var tags = ListMap.empty[SymIdx, TagInfo] /** [[Scope]] for generating WAT identifiers of globals. */ private[text] val globalScp = Scope.empty(Scope.Cfg.default) /** [[ListMap]] containing all global definitions and imports in the module. */ private var globals = ListMap.empty[SymIdx, GlobalInfo | Import[ExternType.Global]] /** [[MutMap]] containing global symbols mapped to their corresponding [[GlobalInfo]] or [[Import]] instance. */ private val namedGlobals = MutMap.empty[Symbol, GlobalInfo | Import[ExternType.Global]] private var startFunc = N: Opt[FuncIdx] /** Counter for generating object tags. */ private var objectTagNum = 0 private val wasmIntrinsicFuncs = MutMap.empty[Str, FuncIdx] private val wasmIntrinsicTypes = MutMap.empty[WasmIntrinsicType, TypeIdx] private val wasmIntrinsicTags = MutMap.empty[Str, TagIdx] private val cachedMemoryImport = MutMap.empty[(Str, Str), SymIdx] private val cachedFunctionImports = MutMap.empty[(Str, Str), FuncIdx] private val cachedGlobalImports = MutMap.empty[(Str, Str), GlobalIdx] private val singletonByBms = MutMap.empty[BlockMemberSymbol, Ctx.SingletonInfo] private val singletonByIsym = MutMap.empty[ModuleOrObjectSymbol, Ctx.SingletonInfo] private val singletonInitActions = ArrayBuf.empty[Expr] private val runtimeClassTags = MutMap.empty[BlockMemberSymbol, LinkedHashSet[Int]] private def imports: Seq[Import[?]] = val importedFuncs = funcs.collect: case (_, imp: Import[ExternType.Func]) => imp val importedGlobals = globals.collect: case (_, imp: Import[ExternType.Global]) => imp val importedMems = memories.collect: case (_, imp: Import[ExternType.Mem]) => imp (importedFuncs ++ importedGlobals ++ importedMems).toSeq private def globalExternType(globalEntry: GlobalInfo | Import[ExternType.Global])(using Ctx, Raise, ): ExternType.Global = globalEntry match case globalInfo: GlobalInfo => ExternType.Global(globalInfo.globalType, globalInfo.sym) case globalImport: Import[ExternType.Global] => globalImport.externType /** Returns a new number to be used as an object tag. */ def getFreshObjectTag(): Int = val tag = objectTagNum objectTagNum += 1 tag /** Adds a type into this context. */ def addType(typeInfo: TypeInfo): TypeIdx = val id = typeInfo.id types += (id -> typeInfo) typeInfo.sym match case bms: BlockMemberSymbol => namedTypes(bms) = typeInfo case _ => TypeIdx(id) /** Returns the [[TypeIdx]] of the given `typeref`. */ def getType(typeref: TypeIdx | BlockMemberSymbol): Opt[TypeIdx] = typeref match case typeidx: TypeIdx => S(typeidx) case sym: BlockMemberSymbol => getTypeInfo(typeref).map(ti => TypeIdx(ti.id)) /** Same as [[getType]] but throws an exception when the `typeref` is not found. */ def getType_!(typeref: TypeIdx | BlockMemberSymbol): TypeIdx = getType(typeref).getOrElse: lastWords(s"Missing type definition for ${typeref.prettyString}") /** Returns the [[TypeInfo]] instance associated with the given `typeref`. */ def getTypeInfo(typeref: TypeIdx | BlockMemberSymbol): Opt[TypeInfo] = typeref match case TypeIdx(idx @ SymIdx(nme)) => types.get(idx) case sym: BlockMemberSymbol => namedTypes.get(sym) /** Same as [[getTypeInfo]] but throws an exception when the `typeref` is not found. */ def getTypeInfo_!(typeref: TypeIdx | BlockMemberSymbol): TypeInfo = getTypeInfo(typeref).getOrElse: lastWords(s"Missing type definition for ${typeref.prettyString}") /** Records the class' runtime tag together with descendant class tags for `sym`. */ def registerRuntimeClassTags(sym: BlockMemberSymbol, tags: LinkedHashSet[Int]): Unit = runtimeClassTags(sym) = tags /** Returns the class' runtime tag together with descendant class tags for `sym`. */ def getAllRuntimeTags(sym: BlockMemberSymbol): Opt[LinkedHashSet[Int]] = runtimeClassTags.get(sym) /** Adds a function import into this context. * * Returns the function index in the global function index space. */ def addFunctionImport(funcImport: Import[ExternType.Func]): FuncIdx = val id = funcImport.externType.id funcs = funcs + (id -> funcImport) namedFuncs(funcImport.externType.sym) = funcImport FuncIdx(id) /** Returns the cached function import for (`module`, `name`), creating it with `createImport` if needed. */ def getOrCreateFunctionImport( module: Str, name: Str, )(createImport: => Import[ExternType.Func]): FuncIdx = cachedFunctionImports.getOrElseUpdate((module, name), addFunctionImport(createImport)) /** Adds a global import into this context. * * Returns the global index in the global index space. */ def addGlobalImport(globalImport: Import[ExternType.Global]): GlobalIdx = val id = globalImport.externType.id globals = globals + (id -> globalImport) namedGlobals(globalImport.externType.sym) = globalImport GlobalIdx(id) /** Returns the cached global import for (`module`, `name`), creating it with `createImport` if needed. */ def getOrCreateGlobalImport( module: Str, name: Str, )(createImport: => Import[ExternType.Global]): GlobalIdx = cachedGlobalImports.getOrElseUpdate((module, name), addGlobalImport(createImport)) /** Adds or updates a memory import. If the import already exists, its minimum pages are increased to at least * `minPages`. */ def ensureMemoryImport(module: Str, name: Str, minPages: Int)(using Ctx, Raise): Unit = val key = module -> name cachedMemoryImport.get(key) match case S(idx) => val existing = memories(idx) match case imp: Import[ExternType.Mem] => imp case _ => lastWords( s"Expected an existing memory import \"$module\".\"$name\" for `${idx.toWat}`, got a definition instead.", ) val newMin = existing.externType.memType.lim.min max minPages if newMin > existing.externType.memType.lim.min then memories = memories + (idx -> Import( module, name, ExternType.Mem( MemType(existing.externType.memType.lim.copy(min = minPages)), sym = existing.externType.sym, wrapId = existing.externType.wrapId, ), )) case N => val id = SymIdx(name) memories = memories + (id -> Import(module, name, ExternType.Mem(MemType(Limits(minPages)), sym = TempSymbol(N, name)))) cachedMemoryImport(key) = SymIdx(name) end match end ensureMemoryImport /** Returns the memory import information for the given (`module`, `name`) tuple if present. */ def getMemoryImport(module: Str, name: Str): Opt[ExternType.Mem] = memories.collectFirst: case (_, imp @ Import(`module`, `name`, mem: ExternType.Mem)) => mem /** Adds a data segment into this context. */ def addDataSegment(seg: DataSegment): Unit = dataSegments = dataSegments + (seg.id -> seg) /** Adds a tag into this context. */ def addTag(tagInfo: TagInfo): TagIdx = val id = tagInfo.id tags = tags + (id -> tagInfo) TagIdx(id) /** Adds a function into this context. */ def addFunc(funcInfo: FuncInfo)(using Ctx, Raise): FuncIdx = val id = funcInfo.id funcs = funcs + (id -> funcInfo) funcInfo.sym match case bms: BlockMemberSymbol => namedFuncs(bms) = funcInfo case _ => val idx = FuncIdx(funcInfo.id) val refType = RefType(funcInfo.typeUse.typeIdx, nullable = false) elemSegments = elemSegments + (id -> ElemSegment.Declare(refType -> Seq(ref.func(idx, refType)), funcInfo.sym, funcInfo.wrapId)) idx /** Returns the [[FuncIdx]] of the given `funcref`. */ def getFunc(funcref: FuncIdx | Symbol): Opt[FuncIdx] = funcref match case funcidx: FuncIdx => S(funcidx) case sym: Symbol => namedFuncs.get(sym).map: funcInfo => funcInfo match case fi: FuncInfo => FuncIdx(fi.id) case imp: Import[ExternType.Func] => FuncIdx(imp.externType.id) /** Same as [[getFunc]] but throws an exception when the `funcref` is not found. */ def getFunc_!(funcref: FuncIdx | Symbol): FuncIdx = getFunc(funcref).getOrElse: lastWords(s"Missing function definition for ${funcref.prettyString}") private def getFuncEntry(funcref: FuncIdx | Symbol): Opt[FuncInfo | Import[ExternType.Func]] = funcref match case FuncIdx(idx @ SymIdx(_)) => funcs.get(idx) case funcref: Symbol => namedFuncs.get(funcref) /** Returns the [[FuncInfo]] instance associated with the given `funcref`. */ def getFuncInfo(funcref: FuncIdx | Symbol): Opt[FuncInfo] = getFuncEntry(funcref).collect: case funcInfo: FuncInfo => funcInfo /** Same as [[getFuncInfo]] but throws an exception when the `funcref` is not found. */ def getFuncInfo_!(funcref: FuncIdx | Symbol): FuncInfo = getFuncInfo(funcref).getOrElse: lastWords(s"Missing function definition for ${funcref.prettyString}") /** Returns the type use associated with the given `funcref`, whether it is a definition or an import. */ def getFuncTypeUse(funcref: FuncIdx | Symbol): Opt[TypeUse] = getFuncEntry(funcref).map: case funcInfo: FuncInfo => funcInfo.typeUse case funcImport: Import[ExternType.Func] => funcImport.externType.typeUse /** Same as [[getFuncTypeUse]] but throws an exception when the `funcref` is not found. */ def getFuncTypeUse_!(funcref: FuncIdx | Symbol): TypeUse = getFuncTypeUse(funcref).getOrElse: lastWords(s"Missing function definition for ${funcref.prettyString}") /** Returns the [[GlobalIdx]] of the given `globalref`. */ def getGlobal(globalref: GlobalIdx | Symbol)(using Ctx, Raise): Opt[GlobalIdx] = globalref match case globalidx: GlobalIdx => S(globalidx) case sym: Symbol => namedGlobals.get(sym).map: globalEntry => GlobalIdx(globalExternType(globalEntry).id) /** Same as [[getGlobal]] but throws an exception when the `globalref` is not found. */ def getGlobal_!(globalref: GlobalIdx | Symbol)(using Ctx, Raise): GlobalIdx = getGlobal(globalref).getOrElse: lastWords(s"Missing global definition for ${globalref.prettyString}") private def getGlobalEntry(globalref: GlobalIdx | Symbol): Opt[GlobalInfo | Import[ExternType.Global]] = globalref match case GlobalIdx(idx @ SymIdx(_)) => globals.get(idx) case sym: Symbol => namedGlobals.get(sym) /** Returns the global extern metadata associated with the given `globalref`. */ def getGlobalType(globalref: GlobalIdx | Symbol)(using Ctx, Raise): Opt[ExternType.Global] = getGlobalEntry(globalref).map(globalExternType) /** Same as [[getGlobalType]] but throws an exception when the `globalref` is not found. */ def getGlobalType_!(globalref: GlobalIdx | Symbol)(using Ctx, Raise): ExternType.Global = getGlobalType(globalref).getOrElse: lastWords(s"Missing global definition for ${globalref.prettyString}") /** Returns the [[GlobalInfo]] instance associated with the given `globalref` when it is a definition. */ def getGlobalInfo(globalref: GlobalIdx | Symbol): Opt[GlobalInfo] = getGlobalEntry(globalref).collect: case globalInfo: GlobalInfo => globalInfo /** Same as [[getGlobalInfo]] but throws an exception when the `globalref` is not found. */ def getGlobalInfo_!(globalref: GlobalIdx | Symbol): GlobalInfo = getGlobalInfo(globalref).getOrElse: lastWords(s"Missing global definition for ${globalref.prettyString}") /** Adds a new variable into the global variable scope. */ def addGlobal(globalInfo: GlobalInfo): GlobalIdx = val id = globalInfo.id globals = globals + (id -> globalInfo) namedGlobals(globalInfo.sym) = globalInfo GlobalIdx(id) /** Adds a [[Seq]] of variables into the global variable scope. */ def addGlobals(globalDefs: Seq[GlobalInfo]): Seq[GlobalIdx] = globalDefs.map(addGlobal) /** Checks whether the global variable scope contains the variable `sym`. */ def containsGlobal(sym: Symbol): Bool = namedGlobals.contains(sym) /** Returns all globals in this context. */ def getGlobals: Seq[Symbol] = namedGlobals.keys.toSeq /** Checks whether singleton metadata has been registered for class symbol `sym`. */ def containsSingleton(sym: BlockMemberSymbol): Bool = singletonByBms.contains(sym) /** Returns singleton metadata for `sym` when it resolves to either the block-member symbol or module/object symbol * used during singleton registration. */ def getSingletonInfo(sym: Local): Opt[Ctx.SingletonInfo] = sym match case bms: BlockMemberSymbol => singletonByBms.get(bms) case isym: ModuleOrObjectSymbol => singletonByIsym.get(isym) case _ => N /** Registers singleton metadata under both its block-member symbol and optional module/object symbol alias. */ def registerSingleton( bms: BlockMemberSymbol, isym: Opt[ModuleOrObjectSymbol], info: Ctx.SingletonInfo, ): Unit = singletonByBms(bms) = info isym.foreach(singletonByIsym(_) = info) /** Appends one eager singleton initialization action for synthesized module start code. */ def addSingletonInitAction(action: Expr): Unit = singletonInitActions += action /** Returns the singleton initialization actions in deterministic insertion order. */ def getSingletonInitActions: Seq[Expr] = singletonInitActions.toSeq /** Returns the runtime class tag for `sym`. */ def getRuntimeClassTag(sym: BlockMemberSymbol): Opt[Int] = getAllRuntimeTags(sym).flatMap(_.headOption) /** Same as [[getRuntimeClassTag]] but throws if no runtime tag is known. */ def getRuntimeClassTag_!(sym: BlockMemberSymbol): Int = getRuntimeClassTag(sym).getOrElse: lastWords(s"Missing runtime class tag for `${sym.toString}`") /** Configures the module start function. */ def setStartFunc(funcIdx: FuncIdx): Unit = startFunc = S(funcIdx) /** Returns the cached [[FuncIdx]] for the intrinsic named `name`, creating it with `createIntrinsic` if it does not * yet exist in this context. */ def getOrCreateWasmIntrinsic(name: Str, createIntrinsic: => FuncIdx): FuncIdx = wasmIntrinsicFuncs.getOrElseUpdate(name, createIntrinsic) /** Returns the cached [[TypeIdx]] for the intrinsic type `key`, creating it with `createType` if it does not yet * exist in this context. */ def getOrCreateWasmIntrinsicType(key: WasmIntrinsicType)(createType: => TypeIdx): TypeIdx = wasmIntrinsicTypes.getOrElseUpdate(key, createType) /** Returns the cached [[TagIdx]] for the intrinsic tag named `name`, creating it if absent. */ def getOrCreateWasmIntrinsicTag(name: Str, createTag: => TagIdx): TagIdx = wasmIntrinsicTags.getOrElseUpdate(name, createTag) def toWat: Document = val definedGlobals = globals.valuesIterator.collect: case globalInfo: GlobalInfo => globalInfo.toWat val memDefns = memories.valuesIterator.collect: case memInfo: MemInfo => memInfo.toWat val funcDefns = funcs.valuesIterator.collect: case funcInfo: FuncInfo => funcInfo.toWat doc"(module #{ # ${ ( types.valuesIterator.map(_.toWat) ++ imports.iterator.map(_.toWat) ++ tags.valuesIterator.map(_.toWat) ++ definedGlobals ++ memDefns ++ funcDefns ++ dataSegments.valuesIterator.map(_.toWat) ++ elemSegments.valuesIterator.map(_.toWat) ++ startFunc.iterator.map(funcIdx => doc"(start ${funcIdx.toWat})") ).toSeq.mkDocument(doc" # ") } #} )" end toWat end Ctx ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Instructions.scala ================================================ package hkmc2 package codegen.wasm.text import mlscript.utils.*, shorthands.* import document.* object Instructions: /** Creates a `block` instruction. */ def block( label: Opt[Str], children: Seq[Expr], resultTypes: Seq[Result], ): FoldedInstr = val labelWat = label.map(lbl => doc"$$$lbl") FoldedInstr( mnemonic = "block", instrargs = labelWat.toSeq ++ resultTypes, stackargs = children, resultTypes = resultTypes.map(_.valtype), ) /** Creates a `loop` instruction. */ def loop( label: Opt[Str], children: Seq[Expr], resultTypes: Seq[Result], ): FoldedInstr = val labelWat = label.map(lbl => doc"$$$lbl") FoldedInstr( mnemonic = "loop", instrargs = labelWat.toSeq ++ resultTypes, stackargs = children, resultTypes = resultTypes.map(_.valtype), ) /** Creates an `if` instruction. */ def `if`( condition: Expr, ifTrue: Expr, ifFalse: Opt[Expr], resultTypes: Seq[Result], ): FoldedInstr = val thenInstr = FoldedInstr( mnemonic = "then", instrargs = Seq.empty, stackargs = Seq(ifTrue), resultTypes = ifTrue.resultTypes, ) val elseInstr = ifFalse.map: elseExpr => FoldedInstr( mnemonic = "else", instrargs = Seq.empty, stackargs = Seq(elseExpr), resultTypes = elseExpr.resultTypes, ) FoldedInstr( mnemonic = "if", instrargs = resultTypes, stackargs = Seq(condition, thenInstr) ++ elseInstr.toSeq, resultTypes = resultTypes.map(_.valtype), ) end `if` /** Creates a `call` instruction. */ def call( funcidx: FuncIdx, operands: Seq[Expr], returnTypes: Seq[Result], ): FoldedInstr = FoldedInstr( mnemonic = "call", instrargs = Seq(funcidx.toWat), stackargs = operands, resultTypes = returnTypes.map(_.valtype), ) /** Creates a `call_ref` instruction. */ def call_ref( target: Expr, operands: Seq[Expr], typeIdx: TypeIdx, funcType: FunctionType, ): FoldedInstr = FoldedInstr( mnemonic = "call_ref", instrargs = Seq(typeIdx.toWat), stackargs = operands :+ target, resultTypes = funcType.sigType.results.map(_.valtype), ) /** Creates a `nop` instruction. */ def nop: FoldedInstr = FoldedInstr( mnemonic = "nop", instrargs = Seq.empty, stackargs = Seq.empty, resultType = N, ) /** Creates a `drop` instruction. */ def drop(value: Expr): FoldedInstr = FoldedInstr( mnemonic = "drop", instrargs = Seq.empty, stackargs = Seq(value), resultType = N, ) /** Creates a `return` instruction with an optional return value. */ def `return`(value: Opt[Expr]): FoldedInstr = FoldedInstr( mnemonic = "return", instrargs = Seq.empty, stackargs = value.toSeq, resultTypes = value.fold(Seq.empty)(_.resultTypes), ) /** Creates a `throw` instruction. */ def `throw`(tag: TagIdx, operands: Seq[Expr]): FoldedInstr = FoldedInstr( mnemonic = "throw", instrargs = Seq(tag.toWat), stackargs = operands, resultType = S(UnreachableType), ) /** Creates an `unreachable` instruction. */ def unreachable: FoldedInstr = FoldedInstr( mnemonic = "unreachable", instrargs = Seq.empty, stackargs = Seq.empty, resultType = S(UnreachableType), ) /** Creates a `br` (branch) instruction. */ def br(label: Str): FoldedInstr = FoldedInstr( mnemonic = "br", instrargs = Seq(doc"$$$label"), stackargs = Seq.empty, resultType = S(UnreachableType), ) object i32: /** Creates an `i32.const` instruction. */ def const(value: Int): FoldedInstr = FoldedInstr( mnemonic = "i32.const", instrargs = Seq(doc"$value"), stackargs = Seq.empty, resultType = S(I32Type), ) /** Creates an `i32.add` instruction. */ def add(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.add", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.add` instruction. */ def eq(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.eq", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S( (lhs.resultType, rhs.resultType) match case (UnreachableType, _) | (_, UnreachableType) => UnreachableType case _ => I32Type, ), ) /** Creates an `i32.ge_u` instruction (greater than or equal, unsigned). */ def ge_u(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.ge_u", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.and` instruction. */ def and(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.and", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.or` instruction. */ def or(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.or", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.sub` instruction. */ def sub(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.sub", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.mul` instruction. */ def mul(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.mul", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.div_s` instruction. */ def div_s(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.div_s", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.rem_s` instruction. */ def rem_s(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.rem_s", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.ne` instruction. */ def ne(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.ne", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.lt_s` instruction. */ def lt_s(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.lt_s", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.le_s` instruction. */ def le_s(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.le_s", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.gt_s` instruction. */ def gt_s(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.gt_s", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.ge_s` instruction. */ def ge_s(lhs: Expr, rhs: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.ge_s", instrargs = Seq.empty, stackargs = Seq(lhs, rhs), resultType = S(I32Type), ) /** Creates an `i32.eqz` instruction. */ def eqz(value: Expr): FoldedInstr = FoldedInstr( mnemonic = "i32.eqz", instrargs = Seq.empty, stackargs = Seq(value), resultType = S(I32Type), ) end i32 object array: /** Creates an `array.len` instruction. */ def len(arrayRef: Expr): FoldedInstr = FoldedInstr( mnemonic = "array.len", instrargs = Seq.empty, stackargs = Seq(arrayRef), resultType = S(I32Type), ) /** Creates an `array.new_fixed` instruction. */ def new_fixed(arrayType: TypeIdx, items: Seq[Expr]): FoldedInstr = FoldedInstr( mnemonic = "array.new_fixed", instrargs = Seq(arrayType.toWat, doc"${items.length}"), stackargs = items, resultType = S(RefType(arrayType, nullable = false)), ) /** Creates an `array.get` instruction. */ def get(arrayType: TypeIdx, arrayRef: Expr, index: Expr, elemType: Type): FoldedInstr = FoldedInstr( mnemonic = "array.get", instrargs = Seq(arrayType.toWat), stackargs = Seq(arrayRef, index), resultType = S(elemType), ) /** Creates an `array.set` instruction. */ def set(arrayType: TypeIdx, arrayRef: Expr, index: Expr, value: Expr): FoldedInstr = FoldedInstr( mnemonic = "array.set", instrargs = Seq(arrayType.toWat), stackargs = Seq(arrayRef, index, value), resultType = N, ) end array object ref: /** Creates a `ref.null` instruction. */ def `null`(heapType: HeapType): FoldedInstr = FoldedInstr( mnemonic = "ref.null", instrargs = Seq(heapType.toWat), stackargs = Seq.empty, resultType = S(RefType(heapType, nullable = true)), ) /** Creates a `ref.is_null` instruction. */ def is_null(value: Expr): FoldedInstr = FoldedInstr( mnemonic = "ref.is_null", instrargs = Seq.empty, stackargs = Seq(value), resultType = S(I32Type), ) /** Creates a `ref.func` instruction. */ def func(idx: FuncIdx, ty: RefType): FoldedInstr = FoldedInstr( mnemonic = "ref.func", instrargs = Seq(idx.toWat), stackargs = Seq.empty, resultType = S(ty), ) /** Creates a `ref.i31` instruction. */ def i31(value: Expr): FoldedInstr = FoldedInstr( mnemonic = "ref.i31", instrargs = Seq.empty, stackargs = Seq(value), resultType = S(RefType.i31ref), ) /** Creates a `ref.test` instruction. */ def test(value: Expr, castType: RefType): FoldedInstr = FoldedInstr( mnemonic = "ref.test", instrargs = Seq(castType.toWat), stackargs = Seq(value), resultType = S(I32Type), ) /** Creates a `ref.cast` instruction. */ def cast(value: Expr, castType: RefType): FoldedInstr = FoldedInstr( mnemonic = "ref.cast", instrargs = Seq(castType.toWat), stackargs = Seq(value), resultType = S(castType), ) end ref object i31: def get(i31: Expr, signed: Bool): FoldedInstr = FoldedInstr( mnemonic = s"i31.get_${if signed then 's' else 'u'}", instrargs = Seq.empty, stackargs = Seq(i31), resultType = S(I32Type), ) /** Creates an `i31.get_s` instruction. */ def get_s(i31: Expr): FoldedInstr = get(i31, true) end i31 object local: /** Creates a `local.get` instruction. */ def get(index: LocalIdx, ty: Type): FoldedInstr = FoldedInstr( mnemonic = "local.get", instrargs = Seq(index), stackargs = Seq.empty, resultType = S(ty), ) /** Creates a `local.tee` instruction. */ def tee(index: LocalIdx, value: Expr): FoldedInstr = FoldedInstr( mnemonic = "local.tee", instrargs = Seq(index), stackargs = Seq(value), resultTypes = value.resultTypes, ) /** Creates a `local.set` instruction. */ def set(index: LocalIdx, value: Expr): FoldedInstr = FoldedInstr( mnemonic = "local.set", instrargs = Seq(index), stackargs = Seq(value), resultType = N, ) end local object global: /** Creates a `global.get` instruction. */ def get(index: GlobalIdx, ty: Type): FoldedInstr = FoldedInstr( mnemonic = "global.get", instrargs = Seq(index), stackargs = Seq.empty, resultType = S(ty), ) /** Creates a `global.set` instruction. */ def set(index: GlobalIdx, value: Expr): FoldedInstr = FoldedInstr( mnemonic = "global.set", instrargs = Seq(index), stackargs = Seq(value), resultType = N, ) end global object struct: /** Creates a `struct.new_default` instruction. */ def new_default(ty: TypeIdx): FoldedInstr = FoldedInstr( mnemonic = "struct.new_default", instrargs = Seq(ty.toWat), stackargs = Seq.empty, resultType = S(RefType(ty, nullable = false)), ) /** Creates a `struct.set` instruction. */ def set(index: FieldIdx, ref: Expr, value: FoldedInstr): FoldedInstr = FoldedInstr( mnemonic = "struct.set", instrargs = Seq(ref.resultType_!.asInstanceOf[RefType].heapType, index), stackargs = Seq(ref, value), resultType = N, ) /** Creates a `struct.get` instruction. */ def get(index: FieldIdx, ref: Expr, ty: Type): FoldedInstr = FoldedInstr( mnemonic = "struct.get", instrargs = Seq(ref.resultType_!.asInstanceOf[RefType].heapType, index), stackargs = Seq(ref), resultType = S(ty), ) end struct end Instructions ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/Wasm.scala ================================================ package hkmc2 package codegen package wasm package text import mlscript.utils.*, shorthands.* import document.* import semantics.{DefinitionSymbol, Elaborator, Symbol, TempSymbol}, Elaborator.State import utils.Scope import scala.collection.Map extension (doc: Document) /** Surrounds a document by the given `prefix` and `suffix`, unless the document is empty. */ private def surroundUnlessEmpty( prefix: => Document = Document.empty, postfix: => Document = Document.empty, ): Document = doc.optionUnless(_.isEmpty).fold(doc): doc => doc"$prefix$doc$postfix" extension (scp: Scope) /** Convenience function for [[Scope.allocateOrGetName]] with an optional prefix and suffix. */ private[text] def allocateOrGetNameWrapped(sym: Local, wrapId: Opt[Str] -> Opt[Str])(using Raise): Str = val prefix = wrapId._1.fold("")(prefix => s"${prefix}_") wrapId._2 match case S(suffix) => scp.lookup(sym).getOrElse: scp.addToBindings(sym, s"$prefix${sym.nme}_$suffix", shadow = false) case N => scp.allocateOrGetName(sym, prefix) /** Trait indicating a WAT representation is available. */ trait ToWat: /** Converts this object into a WAT representation. */ def toWat: Document /** Abstract base class for all Wasm types. */ sealed abstract class Type extends ToWat: /** Attempts to convert this type to a [[[ValType]]]. */ def asValType: Opt[ValType] = this match case ty: ValType => S(ty) case _ => N /** Same as [[[asValType]]], except throws an exception if this type is not a `ValType`. */ def asValType_! : ValType = asValType.getOrElse: lastWords(s"asValType_! called on non-ValType: `$toWat` (${getClass.getName})") private case object I32Type extends Type: def toWat: Document = doc"i32" private case object I64Type extends Type: def toWat: Document = doc"i64" private case object F32Type extends Type: def toWat: Document = doc"f32" private case object F64Type extends Type: def toWat: Document = doc"f64" private case object V128Type extends Type: def toWat: Document = doc"v128" private case object UnreachableType extends Type: def toWat: Document = throw UnsupportedOperationException( s"${toString} is a compiler-internal type and cannot be converted to WAT", ) type NumType = I32Type.type | I64Type.type | F32Type.type | F64Type.type type VecType = V128Type.type object RefType: def anyref: RefType = RefType(HeapType.Any, nullable = true) def i31ref: RefType = RefType(HeapType.I31, nullable = true) def funcref: RefType = RefType(HeapType.Func, nullable = true) /** Wasm type representing a reference to a [[HeapType]]. */ case class RefType(heapType: HeapType, nullable: Bool) extends Type: def toWat: Document = doc"(ref${if nullable then " null" else ""} ${heapType.toWat})" object HeapType: case object Func extends ToWat: def toWat: Document = doc"func" case object Ext extends ToWat: def toWat: Document = doc"extern" case object Any extends ToWat: def toWat: Document = doc"any" case object Eq extends ToWat: def toWat: Document = doc"eq" case object I31 extends ToWat: def toWat: Document = doc"i31" case object Struct extends ToWat: def toWat: Document = doc"struct" case object Array extends ToWat: def toWat: Document = doc"array" case object None extends ToWat: def toWat: Document = doc"none" case object NoExt extends ToWat: def toWat: Document = doc"noextern" case object NoFunc extends ToWat: def toWat: Document = doc"nofunc" end HeapType type ValType = NumType | VecType | RefType /** A Wasm parameter clause. Appears in function signatures. */ case class Param(id: SymIdx, valtype: ValType) extends ToWat: def toWat: Document = doc"(param ${id.toWat} ${valtype.toWat})" /** A Wasm result clause. Appears in function signatures and some instructions. */ case class Result(valtype: ValType) extends ToWat: def toWat: Document = doc"(result ${valtype.toWat})" /** A type representing a function signature. * * Function signatures differ from [[FunctionType]] in that they do not include the `func` keyword. */ case class SignatureType(params: Seq[Param], results: Seq[Result]) extends ToWat: def toWat: Document = (params.map(_.toWat) ++ results.map(_.toWat)).mkDocument(doc" ") object FunctionType: def apply(params: Seq[Param], results: Seq[Result]): FunctionType = new FunctionType(SignatureType(params, results)) /** A type representing a function type. */ case class FunctionType(sigType: SignatureType) extends ToWat: def toWat: Document = doc"(func${sigType.toWat.surroundUnlessEmpty(doc" ")})" /** A type representing a struct field. */ case class Field(ty: ValType, mutable: Bool, id: Str) extends ToWat: def toWat: Document = doc"(field $$$id ${if mutable then doc"(mut ${ty.toWat})" else ty.toWat})" /** A type representing a structure type. */ case class StructType( fields: Seq[DefinitionSymbol[?] -> Field], parents: Seq[TypeIdx] = Seq.empty, isFinal: Bool = false, ) extends ToWat: lazy val fieldsBySym: Map[DefinitionSymbol[?], Field] = fields.toMap def toWat: Document = val structWat = doc"(struct${fields.map(_._2.toWat).mkDocument(doc" ").surroundUnlessEmpty(doc" ")})" if parents.isEmpty && isFinal then structWat else doc"(sub${ if isFinal then doc" final" else doc"" }${ parents.map(_.toWat).mkDocument(doc" ").surroundUnlessEmpty(doc" ") } $structWat)" /** A type representing an array type. */ case class ArrayType(elemType: ValType, mutable: Bool) extends ToWat: def toWat: Document = val elemDoc = if mutable then doc"(mut ${elemType.toWat})" else elemType.toWat doc"(array ${elemDoc})" /** A composite type. */ type CompType = StructType | FunctionType | ArrayType type AbsHeapType = HeapType.Func.type | HeapType.Ext.type | HeapType.Any.type | HeapType.Eq.type | HeapType.I31.type | HeapType.Struct.type | HeapType.Array.type | HeapType.None.type | HeapType.NoExt.type | HeapType.NoFunc.type type HeapType = AbsHeapType | TypeIdx case class TypeUse(typeIdx: TypeIdx) extends ToWat: def toWat: Document = doc"(type ${typeIdx.toWat})" sealed abstract class Index extends ToWat /** A symbolic identifier. */ case class SymIdx(val id: Str) extends Index: def toWat: Document = doc"$$$id" /** An index that is bound to an index space. */ sealed abstract class CtxIdx(idx: Index) extends ToWat: def toWat: Document = idx.toWat /** An index bound to the ''types'' index space. */ case class TypeIdx(idx: Index) extends CtxIdx(idx) /** An index bound to the ''global'' index space. */ case class GlobalIdx(idx: Index) extends CtxIdx(idx) /** An index bound to the ''memory'' index space. */ case class MemIdx(idx: Index) extends CtxIdx(idx) /** An index bound to the ''funcs'' index space. */ case class FuncIdx(idx: Index) extends CtxIdx(idx) /** An index bound to the ''locals'' index space. */ case class LocalIdx(idx: Index) extends CtxIdx(idx) /** An index bound to the ''fields'' index space. */ case class FieldIdx(idx: Index) extends CtxIdx(idx) /** An index bound to the ''tags'' index space. */ case class TagIdx(idx: Index) extends CtxIdx(idx) /** An import entry. */ case class Import[ET <: ExternType](module: Str, name: Str, externType: ET) extends ToWat: def toWat: Document = doc"""(import "$module" "$name" ${externType.toWat})""" /** The address type of a memory type. */ enum AddrType extends ToWat: case i32 case i64 def toWat: Document = this match case AddrType.i32 => doc"i32" case AddrType.i64 => doc"i64" /** The size range of resizeable storage. */ case class Limits(min: Int, max: Opt[Int] = N) extends ToWat: def toWat: Document = doc"$min${max.fold(doc"")(max => doc" $max")}" /** A linear memory entry. */ case class MemType(lim: Limits, addrType: AddrType = AddrType.i32) extends ToWat: def toWat: Document = doc"${addrType.optionIf(_ != AddrType.i32).fold(doc"")(at => doc"${at.toWat} ")}${lim.toWat}" /** A global type. */ case class GlobalType(valType: ValType, mutable: Bool) extends ToWat: def toWat: Document = if mutable then doc"(mut ${valType.toWat})" else valType.toWat object ExternType: /** An linear memory entry that is externally addressable. */ case class Mem(memType: MemType, override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N)(using Ctx, Raise) extends ExternType(sym): val id: SymIdx = SymIdx(summon[Ctx].memoryScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"""(memory ${id.toWat} ${memType.toWat})""" /** An function entry that is externally addressable. */ case class Func(typeUse: TypeUse, override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N)(using Ctx, Raise) extends ExternType(sym): val id: SymIdx = SymIdx(summon[Ctx].funcScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"""(func ${id.toWat} ${typeUse.toWat})""" /** A global entry that is externally addressable. */ case class Global( globalType: GlobalType, override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ExternType(sym): val id: SymIdx = SymIdx(summon[Ctx].globalScp.allocateOrGetNameWrapped(sym, wrapId)) def toWat: Document = doc"""(global ${id.toWat} ${globalType.toWat})""" end ExternType sealed abstract class ExternType(val sym: Symbol) extends ToWat: /** Symbolic identifier for the extern declaration. */ val id: SymIdx /** A memory import entry. */ @deprecated("Use `Import` with `ExternType.Mem` instead.") case class MemoryImport(module: Str, name: Str, id: SymIdx, minPages: Int) extends ToWat: def toWat: Document = doc"""(import "$module" "$name" (memory ${id.toWat} $minPages))""" /** A function import entry. */ @deprecated("Use `Import` with `ExternType.Func` instead.") case class FuncImport(module: Str, name: Str, id: SymIdx, typeIdx: TypeIdx) extends ToWat: def toWat: Document = doc"""(import "$module" "$name" (func ${id.toWat} (type ${typeIdx.toWat})))""" /** A memory use entry. */ case class MemUse(memidx: MemIdx) extends ToWat: def toWat: Document = doc"(memory ${memidx.toWat})" object DataSegment: /** A passive data segment, which is not associated with any memory and must be explicitly loaded with `memory.init`. */ case class Passive(bytes: Seq[Str], override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N)(using Ctx, Raise) extends DataSegment(bytes, sym, wrapId): def toWat: Document = doc"(data ${id.toWat}${bytes.map(s => s"\"$s\"").mkDocument(doc" ").surroundUnlessEmpty(doc" ")})" /** An active data segment, which is automatically copied into a memory given by `memuse` and `offset`. */ case class Active( offset: Expr, bytes: Seq[Str], memuse: Opt[MemUse], override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends DataSegment(bytes, sym, wrapId): def toWat: Document = doc"(data ${id.toWat}${ memuse.fold(doc"")(memuse => doc" ${memuse.toWat}") } ${offset.toWat}${ bytes.map(s => s"\"$s\"").mkDocument(doc" ").surroundUnlessEmpty(doc" ") })" end DataSegment /** A data segment entry. */ sealed abstract class DataSegment(bytes: Seq[Str], val sym: Symbol, wrapId: Opt[Str] -> Opt[Str])(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the data segment. */ val id = SymIdx(summon[Ctx].dataSegmentScp.allocateOrGetNameWrapped(sym, wrapId)) object ElemSegment: /** A passive element segment, which is not associated with any table and must be explicitly initialized with * `table.init`. */ case class Passive( override val elemlist: RefType -> Seq[Expr], override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ElemSegment(elemlist, sym, wrapId): def toWat: Document = doc"(elem ${id.toWat} ${abbrevElemList})" /** An active element segment, which is automatically copied into a table given by `offset. */ case class Active( offset: Expr, override val elemlist: RefType -> Seq[Expr], // TODO(Derppening): Add `tableuse` here if/when we support multiple tables. override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ElemSegment(elemlist, sym, wrapId): def toWat: Document = doc"(elem ${id.toWat} ${offset.toWat} ${abbrevElemList})" /** A declarative element segment, which is used to forward declare references present in the code (such as using * `ref.func`). */ case class Declare( override val elemlist: RefType -> Seq[Expr], override val sym: Symbol, wrapId: Opt[Str] -> Opt[Str] = N -> N, )(using Ctx, Raise) extends ElemSegment(elemlist, sym, wrapId): def toWat: Document = doc"(elem ${id.toWat} declare ${abbrevElemList})" end ElemSegment /** An element segment entry. */ sealed abstract class ElemSegment( val elemlist: RefType -> Seq[Expr], val sym: Symbol, wrapId: Opt[Str] -> Opt[Str], )(using Ctx, Raise) extends ToWat: /** Symbolic identifier for the element segment. */ val id = SymIdx(summon[Ctx].elemSegmentScp.allocateOrGetNameWrapped(sym, wrapId)) /** Applies abbreviations on the `elemlist` if a simpler replacement is available. */ protected def abbrevElemList: Document = if elemlist._2.forall(_.mnemonic == "ref.func") then doc"func${ elemlist._2.map: e => e.instrargs.head match case a: ToWat => a.toWat case a: Document => a .mkDocument(doc" ").surroundUnlessEmpty(doc" ") }" else doc"${elemlist._1.toWat}${elemlist._2.map(_.toWat).mkDocument(doc" ").surroundUnlessEmpty(doc" ")}" end ElemSegment /** An abstraction over a generic WebAssembly instructions. */ sealed abstract class Instruction extends ToWat: /** The mnemonic of the instruction, e.g. "i32.add". */ val mnemonic: String /** The arguments to the instruction. Note that this only includes arguments that are directly part of the * instruction, not the stack arguments. * * For example, for `i32.add` this would be empty, but for `i32.const 42`, this would be `Seq(doc"42")`. */ val instrargs: Seq[ToWat | Document] object FoldedInstr: def apply( mnemonic: Str, instrargs: Seq[ToWat | Document], stackargs: Seq[FoldedInstr], resultType: Opt[Type], ): FoldedInstr = new FoldedInstr(mnemonic, instrargs, stackargs, resultType.toSeq) /** A WebAssembly folded instruction. * * @param stackargs * The stack arguments of the instruction. */ case class FoldedInstr( mnemonic: Str, instrargs: Seq[ToWat | Document], stackargs: Seq[Expr], resultTypes: Seq[Type], ) extends Instruction: /** Returns the result type of this instruction if this instruction only has 0-1 result values. */ def resultType: Opt[Type] = resultTypes match case Seq() => N case ty :: Seq() => S(ty) case _ => lastWords(s"resultType_! called on instruction with multi-value result type: $this") /** Returns the singular result type of this instruction, otherwise throws an exception. */ def resultType_! : Type = resultType.getOrElse: lastWords(s"resultType_! called on instruction with a non-unique result type: $this") def toWat: Document = doc"($mnemonic${ instrargs.map: a => a match case a: ToWat => a.toWat case a: Document => a .mkDocument(doc" ").surroundUnlessEmpty(doc" ") } #{ ${ stackargs.map(_.toWat).mkDocument(doc" # ").surroundUnlessEmpty(doc" # ") } #} )" end FoldedInstr /** A WebAssembly expression, comprised of one or more instructions that generate a result value. */ type Expr = FoldedInstr ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala ================================================ package hkmc2 package codegen package wasm package text import mlscript.utils.*, shorthands.* import hkmc2.utils.* import document.* import document.Document import js.CodeBuilder import semantics.*, Elaborator.State import syntax.Tree.{BoolLit, IntLit, StrLit, Ident} import text.{Import as WasmImport, Param as WasmParam} import Message.MessageContext import scala.collection.mutable.{ArrayBuffer as ArrayBuf, LinkedHashMap, LinkedHashSet, Queue} import scala.util.boundary, boundary.break import sourcecode.Line extension (instr: FoldedInstr) /** Returns the mneomic prefix of this instruction. * * For example, for `local.get` it returns `Some("local")`, and for `nop` it returns `None`. */ private def mnemonicPrefix: Opt[Str] = instr.mnemonic.split('.').optionUnless(_.size == 1).map(_.head) object WatBuilder: /** The maximum number of characters taken to be part of the identifier asscoiated with string constants. */ val StringConstantIdentMaxLength = 16 object ExternIntrinsics: val SystemModule = "system" val SystemMemoryImportName = "mem" val StringFromUtf16ImportName = "mlx_str_from_utf16" val WasmPageSizeBytes = 65536 class WatBuilder(using TraceLogger, State) extends CodeBuilder: import Ctx.ctx import Ctx.{SingletonInfo, binaryOps, unaryOps, wasmIntrinsicArities, wasmIntrinsicNameSet} import FunctionCtx.funcCtx import Instructions.{block as blockInstr, loop as loopInstr, *} import WatBuilder.ExternIntrinsics type Context = Ctx private val baseObjectSym: BlockMemberSymbol = BlockMemberSymbol("Object", Nil) private val tagFieldSym: TermSymbol = TermSymbol(syntax.MutVal, owner = N, Ident("$tag")) private case class StringLitInfo(offset: Int, byteLen: Int, watBytes: Str) private val stringLits: LinkedHashMap[Str, StringLitInfo] = LinkedHashMap.empty private val initFuncSyms: LinkedHashMap[BlockMemberSymbol, BlockMemberSymbol] = LinkedHashMap.empty private var nextStringDataOffset: Int = 0 private def baseObjectTypeIdx(using Ctx): TypeIdx = ctx.getType_!(baseObjectSym) private def baseObjectStruct(using Ctx): StructType = ctx.getTypeInfo_!(baseObjectSym).compType match case struct: StructType => struct case other => lastWords(s"Base Object type must be a struct, found ${other.toWat.mkString()}") private def baseObjectRefType(nullable: Bool)(using Ctx): RefType = RefType(baseObjectTypeIdx, nullable = nullable) /** True if this top-level class can be declared as a Wasm struct type. */ private def isSupportedTopLevelClass(defn: ClsLikeDefn): Bool = defn.owner.isEmpty && ((defn.k is syntax.Cls) || (defn.k is syntax.Obj)) && defn.auxParams.isEmpty && (!(defn.k is syntax.Obj) || defn.parentPath.isEmpty) && (!(defn.k is syntax.Obj) || defn.methods.isEmpty) && defn.companion.isEmpty /** Returns singleton metadata when `sym` resolves to a registered singleton object. */ private def singletonInfoFor(sym: Local)(using Ctx): Opt[SingletonInfo] = ctx.getSingletonInfo(sym) /** Loads the singleton object reference from its backing mutable global. */ private def singletonGlobalGet(info: SingletonInfo): Expr = global.get(GlobalIdx(SymIdx(info.globalName)), info.globalTy) /** The runtime representation of Unit as a singleton object. */ private lazy val syntheticUnitDefn: ClsLikeDefn = ClsLikeDefn( owner = N, isym = State.unitSymbol, sym = State.unitBlockMemberSymbol, ctorSym = N, k = syntax.Obj, paramsOpt = N, auxParams = Nil, parentPath = N, methods = Nil, privateFields = Nil, publicFields = Nil, preCtor = End(""), ctor = End(""), companion = N, bufferable = N, )(N, Nil) /** Registers the synthetic `Unit` singleton. */ private def RegisterUnitSingleton()(using Ctx, FunctionCtx, Raise, SessionExportCtx): Unit = val unitDefn = syntheticUnitDefn val singletonOwner = unitDefn.isym match case mos: ModuleOrObjectSymbol => S(mos) case _ => N if ctx.containsSingleton(unitDefn.sym) then return if ctx.getType(unitDefn.sym).isEmpty then predeclareClassType(unitDefn) predeclareClassInit(unitDefn) predeclareClassConstructor(unitDefn) returningTerm(Define(unitDefn, End(""))) val typeInfo = ctx.getTypeInfo_!(unitDefn.sym) val singletonInfo = ctx.getSingletonInfo(unitDefn.sym) getOrElse: lastWords("Missing singleton metadata for synthetic Unit object") // Record session metadata for the synthetic Unit singleton. summon[SessionExportCtx].emit(SessionClass( sym = unitDefn.sym, wrapId = typeInfo.wrapId, compType = typeInfo.compType, objectTag = typeInfo.objectTag, runtimeTags = ctx.getAllRuntimeTags(unitDefn.sym) getOrElse: LinkedHashSet(ctx.getRuntimeClassTag_!(unitDefn.sym)) , aliasSyms = singletonOwner.toSeq, )) summon[SessionExportCtx].emit(SessionSingleton( blockSym = unitDefn.sym, wrapId = N -> N, objectSym = singletonOwner, moduleName = SessionBinding.ReplModuleName, exportName = singletonInfo.globalName, globalTy = singletonInfo.globalTy, )) end RegisterUnitSingleton /** Registers eager singleton runtime state by creating its global and start-init action. */ private def registerSingletonInit( clsLikeDefn: ClsLikeDefn, typeref: TypeIdx, )(using Ctx, Raise): Unit = if ctx.containsSingleton(clsLikeDefn.sym) then return val globalSym = BlockMemberSymbol(s"${clsLikeDefn.sym.nme}$$inst", Nil, nameIsMeaningful = false) val globalTy = RefType(typeref, nullable = true) val globalInfo = GlobalInfo( globalType = GlobalType(globalTy, mutable = true), init = ref.`null`(typeref), exportName = S(globalSym.nme), sym = globalSym, ) val globalIdx = ctx.addGlobal(globalInfo) val singletonOwner = clsLikeDefn.isym match case mos: ModuleOrObjectSymbol => S(mos) case _ => N val info = SingletonInfo(globalInfo.id.id, globalTy) ctx.registerSingleton(clsLikeDefn.sym, singletonOwner, info) val ctorCall = call( funcidx = ctx.getFunc_!(clsLikeDefn.sym), operands = Seq.empty, returnTypes = Seq(Result(RefType.anyref)), ) ctx.addSingletonInitAction(global.set(globalIdx, ref.cast(ctorCall, globalTy))) end registerSingletonInit /** Collects only top-level class definitions in `block`. */ private def collectTopLevelClassDefns(block: Block): List[ClsLikeDefn] = val acc = ArrayBuf.empty[ClsLikeDefn] new BlockTraverserShallow: applyBlock(block) override def applyBlock(b: Block): Unit = b match case Match(_, _, _, rst) => applySubBlock(rst) case Label(_, _, _, rst) => applySubBlock(rst) case TryBlock(_, _, rst) => applySubBlock(rst) case _ => super.applyBlock(b) override def applyDefn(defn: Defn): Unit = defn match case clsLikeDefn: ClsLikeDefn => clsLikeDefn.optionIf(isSupportedTopLevelClass).foreach(acc += _) case _ => () acc.toList /** Resolves the parent symbol for a top-level class definition, if present. */ private def resolveParentSym(defn: ClsLikeDefn)(using Raise): Opt[BlockMemberSymbol] = def unsupportedParent(): Opt[BlockMemberSymbol] = raise(ErrorReport( msg"Wasm inheritance ordering only supports direct resolved parent class references." -> defn.parentPath.flatMap(_.toLoc) :: Nil, extraInfo = S(defn.showAsTree), source = Diagnostic.Source.Compilation, )) N defn.parentPath match case N => N case S(Value.Ref(sym, _)) => sym.asCls.flatMap(_.asBlkMember).orElse(unsupportedParent()) case S(sel: Select) => sel.symbol.flatMap(_.asCls).flatMap(_.asBlkMember).orElse(unsupportedParent()) case S(_) => unsupportedParent() /** Orders top-level classes using a Kahn topological sort. */ private def sortTopLevelClasses(defns: List[ClsLikeDefn])(using Raise): List[ClsLikeDefn] = val defnsBySym = defns.iterator.map(defn => defn.sym -> defn).toMap val childrenBySym = LinkedHashMap.empty[BlockMemberSymbol, ArrayBuf[BlockMemberSymbol]] val indegrees = LinkedHashMap.empty[BlockMemberSymbol, Int] defns.foreach: defn => childrenBySym(defn.sym) = ArrayBuf.empty indegrees(defn.sym) = 0 defns.foreach: defn => if defn.parentPath.nonEmpty then val parentSym = resolveParentSym(defn).getOrElse(lastWords("unreachable")) if defnsBySym.contains(parentSym) then childrenBySym(parentSym) += defn.sym indegrees(defn.sym) += 1 else raise(ErrorReport( msg"Wasm inheritance ordering requires parent classes to be supported top-level classes." -> defn.parentPath.flatMap(_.toLoc) :: Nil, extraInfo = S(s"${defn.sym.nme} extends ${parentSym.nme}"), source = Diagnostic.Source.Compilation, )) val zeroIndegree = Queue.from: defns.iterator.collect: case defn if indegrees(defn.sym) == 0 => defn.sym val ordered = ArrayBuf.empty[ClsLikeDefn] while zeroIndegree.nonEmpty do val sym = zeroIndegree.dequeue() ordered += defnsBySym(sym) childrenBySym(sym).foreach: childSym => indegrees(childSym) -= 1 if indegrees(childSym) == 0 then zeroIndegree.enqueue(childSym) if ordered.size != defns.size then raise(ErrorReport( msg"Inheritance cycles are not supported." -> defns.flatMap(_.sym.toLoc).headOption :: Nil, extraInfo = S( defns.iterator .filter(defn => indegrees(defn.sym) > 0) .map(_.sym.nme) .mkString(", "), ), source = Diagnostic.Source.Compilation, )) ordered.toList end sortTopLevelClasses /** Declares one supported top-level class type for early wasm registration. */ private def predeclareClassType(defn: ClsLikeDefn)(using Ctx, Raise): Unit = val parentTypeIdx = if defn.parentPath.isEmpty then baseObjectTypeIdx else ctx.getType_!( resolveParentSym(defn) getOrElse: lastWords(s"Expected resolved parent class symbol when predeclaring ${defn.sym.nme}"), ) val inheritedFields = ctx.getTypeInfo_!(parentTypeIdx).compType match case struct: StructType => struct.fields case other => lastWords(s"Parent type must be a struct, found ${other.toWat.mkString()}") val classFields = (defn.publicFields.map(_._2) ++ defn.privateFields) .map: f => f -> Field(RefType.anyref, mutable = true, id = f.nme) val allFields = inheritedFields ++ classFields val runtimeTag = ctx.getFreshObjectTag() ctx.addType(TypeInfo( sym = defn.sym, compType = StructType(fields = allFields, parents = Seq(parentTypeIdx)), objectTag = S(runtimeTag), )) ctx.registerRuntimeClassTags(defn.sym, LinkedHashSet(runtimeTag)) end predeclareClassType /** Records the runtime tag accepted by each class pattern: the class's own tag and descendant tags. */ private def predeclareClassTags( orderedDefns: List[ClsLikeDefn], )(using Ctx, Raise): Unit = val childrenBySym = LinkedHashMap.empty[BlockMemberSymbol, ArrayBuf[BlockMemberSymbol]] orderedDefns.foreach: defn => childrenBySym(defn.sym) = ArrayBuf.empty orderedDefns.foreach: defn => resolveParentSym(defn).foreach: parentSym => childrenBySym(parentSym) += defn.sym orderedDefns.reverseIterator.foreach: defn => val ownTag = ctx.getTypeInfo_!(defn.sym).objectTag getOrElse: lastWords(s"Expected class ${defn.sym} to have an object tag") val childTags = childrenBySym(defn.sym).flatMap: childSym => ctx.getAllRuntimeTags(childSym).getOrElse(lastWords("unreachable")) ctx.registerRuntimeClassTags(defn.sym, LinkedHashSet(ownTag) ++ childTags) /** Declares the shared Wasm function type used by a class-associated function placeholder. */ private def declareClassFuncType( defn: ClsLikeDefn, suffix: Str, params: Seq[Local -> SymIdx], )(using Ctx, Raise): TypeIdx = ctx.addType(TypeInfo( sym = TempSymbol(N, defn.sym.nme), FunctionType( params = params.map(p => WasmParam(p._2, RefType.anyref)), results = Seq(Result(RefType.anyref)), ), objectTag = N, wrapId = N -> S(suffix), )) /** Returns the symbol used to predeclare and later overwrite a class init function. */ private def initFuncSym(sym: BlockMemberSymbol): BlockMemberSymbol = initFuncSyms.getOrElseUpdate(sym, BlockMemberSymbol("init", Nil, nameIsMeaningful = false)) /** Registers a placeholder class-associated function so later lowering can overwrite it. */ private def predeclareClassFunc( defn: ClsLikeDefn, suffix: Str, params: Seq[Local -> SymIdx], sym: BlockMemberSymbol, exportName: Opt[Str], )(using Ctx, Raise): Unit = val funcTy = declareClassFuncType(defn, suffix, params) ctx.addFunc(new FuncInfo( sym, wrapId = if sym.asClsOrMod.isDefined then (N -> S("ctor")) else (S(defn.sym.nme) -> N), typeUse = TypeUse(funcTy), params = params, resultTypes = Seq(Result(RefType.anyref)), locals = Seq.empty, body = ref.`null`(ctx.getType_!(defn.sym)), exportName = exportName, )) /** Declares one top-level class init function. */ private def predeclareClassInit(defn: ClsLikeDefn)(using Ctx, Raise): Unit = val initParams = (defn.isym -> SymIdx("this")) +: defn.paramsOpt.fold(Nil): ps => ps.params.map: p => p.sym -> SymIdx(p.sym.nme) predeclareClassFunc(defn, "init", initParams, initFuncSym(defn.sym), N) /** Declares one top-level class constructor. */ private def predeclareClassConstructor(defn: ClsLikeDefn)(using Ctx, Raise): Unit = val ctorParams = defn.paramsOpt.fold(Nil): ps => ps.params.map: p => p.sym -> SymIdx(p.sym.nme) val ctorExportName = defn.sym .optionIf: sym => !(defn.k is syntax.Obj) && sym.nameIsMeaningful .map(_.nme) predeclareClassFunc(defn, "ctor", ctorParams, defn.sym, ctorExportName) /** Collects the symbols that should live in mutable globals so later REPL blocks can import them. * * TODO: replace this structural scan with an explicit "session-visible bindings" set from lowering once that * information is available directly in the IR. */ private def collectSessionGlobalSymbols( b: Block, sessionExportCtx: SessionExportCtx, ): Set[Symbol] = def restOf(block: Block): Opt[Block] = block match case Define(_, rst) => S(rst) case Assign(_, _, rst) => S(rst) case AssignField(_, _, _, rst) => S(rst) case AssignDynField(_, _, _, _, rst) => S(rst) case Match(_, _, _, rst) => S(rst) case TryBlock(_, _, rst) => S(rst) case Label(_, _, _, rst) => S(rst) case _ => N def recur(block: Block): Set[Symbol] = block match case Scoped(_, body) => recur(body) case Begin(sub, rst) => recur(sub) ++ recur(rst) case Define(ValDefn(_, sym, _), rst) if sessionExportCtx.shouldExport(sym) => recur(rst) + sym case Define(_, rst) => recur(rst) case Assign(sym: Symbol, _, rst) if sessionExportCtx.shouldExport(sym) => recur(rst) + sym case _: BlockTail => Set.empty case block => restOf(block).fold(Set.empty)(recur) recur(b) end collectSessionGlobalSymbols /** Declares a mutable exported global for a REPL-visible binding produced by the current block. */ private def registerSessionGlobal( sym: Symbol, )(using Ctx, Raise, SessionExportCtx): Unit = if ctx.containsGlobal(sym) then return val exportName = sym.nme val globalInfo = GlobalInfo( globalType = GlobalType(RefType.anyref, mutable = true), init = ref.`null`(HeapType.Any), exportName = S(exportName), sym, ) ctx.addGlobal(globalInfo) summon[SessionExportCtx].emit(SessionGlobal( sym = sym, wrapId = globalInfo.wrapId, moduleName = SessionBinding.ReplModuleName, exportName = exportName, globalType = GlobalType(RefType.anyref, mutable = true), )) end registerSessionGlobal /** Registers imported REPL bindings into the current module before codegen starts. */ private def registerSessionImports( sessionImports: Seq[SessionBinding], )(using Ctx, Raise): Unit = sessionImports.foreach: case cls: SessionClass => ctx.addType(TypeInfo( sym = cls.sym, wrapId = cls.wrapId, compType = cls.compType, objectTag = cls.objectTag, )) ctx.registerRuntimeClassTags(cls.sym, cls.runtimeTags) case _ => sessionImports.foreach: case func: SessionFunc => // If the function symbol comes from a class or module, generate a TempSymbol to avoid symbol collision with // the class/module itself val funcTySym = TempSymbol(N, func.sym.nme) val typeIdx = ctx.addType(TypeInfo(sym = funcTySym, wrapId = func.wrapId, compType = func.funcType, objectTag = N)) ctx.addFunctionImport(WasmImport( func.moduleName, func.exportName, ExternType.Func(TypeUse(typeIdx), func.sym, wrapId = func.wrapId), )) case glob: SessionGlobal => ctx.addGlobalImport(WasmImport( glob.moduleName, glob.exportName, ExternType.Global(glob.globalType, glob.sym, glob.wrapId), )) case singleton: SessionSingleton => val globalExtern = ExternType.Global( GlobalType(singleton.globalTy, mutable = true), singleton.blockSym, singleton.wrapId, ) ctx.addGlobalImport(WasmImport( singleton.moduleName, singleton.exportName, globalExtern, )) ctx.registerSingleton( singleton.blockSym, singleton.objectSym, SingletonInfo(globalExtern.id.id, singleton.globalTy), ) case _: SessionClass => end registerSessionImports /** Declares one top-level class method. */ private def predeclareMethod(methodDefn: FunDefn, ownerCls: ClsLikeDefn)(using Ctx, Raise): Unit = val methodParams = (ownerCls.isym -> SymIdx("this")) +: methodDefn.params.headOption.fold(Nil): ps => ps.params.map: p => p.sym -> SymIdx(p.sym.nme) predeclareClassFunc(ownerCls, methodDefn.sym.nme, methodParams, methodDefn.sym, N) /** Declares placeholders for all methods on one top-level class. */ private def predeclareClassMethods(defn: ClsLikeDefn)(using Ctx, Raise): Unit = defn.methods.foreach: case methodDefn @ FunDefn(_, _, _, Nil | _ :: Nil, _) => predeclareMethod(methodDefn, defn) case FunDefn(_, sym, _, _ :: _ :: _, _) => raise(ErrorReport( msg"WatBuilder::predeclareClassMethods for ClsLikeDefn(...) with `multi-parameter-list method` not implemented yet" -> sym.toLoc :: Nil, source = Diagnostic.Source.Compilation, )) case _ => () /** Gets (and caches) the exception tag used for MLX `throw`. */ private def exnTagIdx(using Ctx, Raise): TagIdx = val sym = TempSymbol(N, "mlx_exn") ctx.getOrCreateWasmIntrinsicTag( "mlx_exn", ctx.addTag(TagInfo( typeUse = TypeUse(ctx.addType(TypeInfo( sym, FunctionType(params = Seq(WasmParam(SymIdx("ex"), RefType.anyref)), results = Seq.empty), objectTag = S(ctx.getFreshObjectTag()), ))), sym = sym, )), ) /** Returns (and caches) string literal data metadata, allocating data-segment space on first use. */ private def internStringLiteral(value: Str): StringLitInfo = stringLits.getOrElseUpdate( value, if value.isEmpty then StringLitInfo(offset = 0, byteLen = 0, watBytes = "") else val sb = new StringBuilder(value.length * 6) value.foreach: ch => val codeUnit = ch.toInt sb.append(f"\\${codeUnit & 0xff}%02x") sb.append(f"\\${(codeUnit >>> 8) & 0xff}%02x") val watBytes = sb.toString val offset = (nextStringDataOffset + 1) & ~1 val byteLen = value.length * 2 nextStringDataOffset = offset + byteLen StringLitInfo(offset = offset, byteLen = byteLen, watBytes = watBytes), ) /** Ensures imports required for string materialization exist and returns the constructor function. */ private def getOrLoadStrCtorFunction(using Ctx, Raise): FuncIdx = val minBytes = nextStringDataOffset val pageSize = ExternIntrinsics.WasmPageSizeBytes val minPages = if minBytes <= 0 then 0 else (minBytes + pageSize - 1) / pageSize ctx.ensureMemoryImport( ExternIntrinsics.SystemModule, ExternIntrinsics.SystemMemoryImportName, minPages, ) ctx.getOrCreateFunctionImport( module = ExternIntrinsics.SystemModule, name = ExternIntrinsics.StringFromUtf16ImportName, ): val importTySym = TempSymbol(N, ExternIntrinsics.StringFromUtf16ImportName) val importTy = ctx.addType(TypeInfo( sym = importTySym, compType = FunctionType( params = Seq(WasmParam(SymIdx("glob_offset"), RefType.anyref), WasmParam(SymIdx("len"), RefType.anyref)), results = Seq(Result(RefType.anyref)), ), objectTag = N, )) WasmImport( module = ExternIntrinsics.SystemModule, name = ExternIntrinsics.StringFromUtf16ImportName, externType = ExternType.Func( typeUse = TypeUse(importTy), sym = importTySym, wrapId = N -> N, ), ) end getOrLoadStrCtorFunction /** Gets (and caches) the Wasm GC array type used for tuples (`mut` selects mutability). */ private def tupleArrayType(mut: Bool)(using Ctx, Raise): TypeIdx = ctx.getOrCreateWasmIntrinsicType(WasmIntrinsicType.TupleArray(mutable = mut)): val suffix = if mut then "Mut" else "" val sym = BlockMemberSymbol(s"TupleArray$suffix", Nil) ctx.addType(TypeInfo( sym, ArrayType(elemType = RefType.anyref, mutable = mut), objectTag = N, )) /** Allocates a fresh temp local (typed `anyref`) and returns its `LocalIdx`. */ private def mkTempLocal(base: Str)(using Ctx, FunctionCtx, Raise): LocalIdx = funcCtx.addLocal(TempSymbol(N, base)) /** Binds constructor self (`thisSym`) to the Wasm local name `this` in the current function context. */ private def bindCtorThis(thisSym: Local)(using Ctx, FunctionCtx, Raise): LocalIdx = funcCtx.addLocal(thisSym, S("this")) /** Compiles a class init body under its own Wasm-local frame with explicit `this`. */ private def setupInitLocals( clsLikeDefn: ClsLikeDefn, )(using Ctx, Raise, SessionExportCtx): (Expr, FunctionCtx) = genFuncBody(clsLikeDefn.paramsOpt.toList, thisSym = S(clsLikeDefn.isym)): val thisVar = funcCtx.lookupLocal_!(clsLikeDefn.isym, N) val preCtorWat = compilePreCtor(clsLikeDefn, thisVar) val ctorWat = block(clsLikeDefn.ctor) blockInstr( label = N, children = Seq( preCtorWat, ctorWat, `return`(S(local.get(thisVar, RefType.anyref))), ), resultTypes = Seq(Result(RefType.anyref)), ) /** Lowers an inherited pre-constructor by preserving its setup code and rewriting the final `super(...)` into * `Parent_init(this, ...)`. */ private def compilePreCtor( clsLikeDefn: ClsLikeDefn, thisVar: LocalIdx, )(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = def withRest(block: NonBlockTail, rest: Block): Block = block match case Scoped(syms, _) => Scoped(syms, rest) case Begin(sub, _) => Begin(sub, rest) case TryBlock(sub, finallyDo, _) => TryBlock(sub, finallyDo, rest) case Assign(lhs, rhs, _) => Assign(lhs, rhs, rest) case af @ AssignField(lhs, nme, rhs, _) => AssignField(lhs, nme, rhs, rest)(af.symbol) case AssignDynField(lhs, fld, arrayIdx, rhs, _) => AssignDynField(lhs, fld, arrayIdx, rhs, rest) case Define(defn, _) => Define(defn, rest) case Match(scrut, arms, dflt, _) => Match(scrut, arms, dflt, rest) case Label(label, loop, body, _) => Label(label, loop, body, rest) def splitSuperTail(block: Block): Opt[Block -> Ls[Arg]] = block match case End(_) => N case Return(Call(Value.Ref(bs: BuiltinSymbol, _), argss), true) if bs eq State.builtinOpsMap("super") => S(End("") -> argss.flatten) case b: NonBlockTail => splitSuperTail(b.rest).map: (prefix, args) => withRest(b, prefix) -> args case _ => N clsLikeDefn.preCtor match case End(_) => nop case _ => splitSuperTail(clsLikeDefn.preCtor) match case S((prefixBlock, args)) => val prefixWat = block(prefixBlock) resolveParentSym(clsLikeDefn) match case S(parentSym) => val parentInitFunc = initFuncSym(parentSym) val superCall = call( funcidx = ctx.getFunc_!(parentInitFunc), operands = local.get(thisVar, RefType.anyref) +: args.map(argument), returnTypes = Seq(Result(RefType.anyref)), ) blockInstr( label = N, children = Seq(asStatement(prefixWat), drop(superCall)), resultTypes = Seq.empty, ) case N => nop case N => raise(ErrorReport( msg"Wasm preCtor lowering only supports lowered super(...) shapes." -> clsLikeDefn.sym.toLoc :: Nil, extraInfo = S(clsLikeDefn.preCtor.showAsTree), source = Diagnostic.Source.Compilation, )) nop end match end compilePreCtor /** Converts expression result types to WAT result clauses, dropping unreachable types. */ private def resultClauses(expr: Expr): Seq[Result] = if expr.resultTypes.exists(_ is UnreachableType) then Seq.empty else expr.resultTypes.map(ty => Result(ty.asValType_!)) /** Normalizes the exported `entry` body so it always returns single result. */ private def normalizeEntryExpr( expr: Expr, isAbortive: Bool, )(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = if expr.resultTypes.isEmpty && !isAbortive then blockInstr( label = N, children = Seq(expr, result(Value.Ref(State.unitSymbol))), resultTypes = Seq(Result(RefType.anyref)), ) else expr /** Validates an IntLit value fits signed 32-bit and delegates codegen to `onValid`. */ private def withValidIntLit( value: BigInt, loc: Opt[Loc], )(onValid: Int => Expr)(using Ctx, Raise, Line): Expr = if value.isValidInt then onValid(value.toInt) else errExpr( Ls(msg"WatBuilder::IntLit lowering with value outside signed 32-bit range not implemented yet" -> loc), extraInfo = S(value.toString), ) /** Emits a tuple element load that works for both mutable and immutable tuple arrays. */ private def tupleArrayGet(tupleExpr: Expr, idxBuilder: Expr => Expr)(using Ctx, FunctionCtx, Raise): Expr = val elemType = RefType.anyref val mutArrayType = tupleArrayType(true) val immArrayType = tupleArrayType(false) val tupleTmp = mkTempLocal("tuple") val tupleIsMutable = ref.test(local.tee(tupleTmp, tupleExpr), RefType(mutArrayType, nullable = true)) val tupleValue = local.get(tupleTmp, RefType.anyref) val mutableBranch = val tupleRef = ref.cast(tupleValue, RefType(mutArrayType, nullable = false)) array.get(mutArrayType, tupleRef, idxBuilder(tupleRef), elemType) val immutableBranch = val tupleRef = ref.cast(tupleValue, RefType(immArrayType, nullable = false)) array.get(immArrayType, tupleRef, idxBuilder(tupleRef), elemType) `if`( condition = tupleIsMutable, ifTrue = mutableBranch, ifFalse = S(immutableBranch), resultTypes = Seq(Result(elemType.asValType_!)), ) /** Builds an i32 index for tuple indexing (supports negative indices; caches non-literals). */ private def compileTupleIndex( fld: Path, loc: Opt[Loc], errCtx: Str, errExtra: => Str, )(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr => Expr = fld match case Value.Lit(IntLit(value)) if value.isValidInt => val idx = value.toInt tupleRef => if idx >= 0 then i32.const(idx) else i32.add(array.len(tupleRef), i32.const(idx)) case _ => val rawIdx = result(fld) val idxI32 = rawIdx.resultType match case S(I32Type) => rawIdx case S(RefType(HeapType.I31, _)) => i31.get(rawIdx, signed = true) case S(RefType(HeapType.Any, _)) => val casted = ref.cast(rawIdx, RefType.i31ref) i31.get(casted, signed = true) case ty => return (_: Expr) => errExpr( msg"$errCtx expects an integer index but found ${ty.fold("(none)")(_.toWat.mkString())}" -> loc :: Nil, extraInfo = S(errExtra), ) val idxTmp = mkTempLocal("idx") tupleRef => val storeIdx = local.set(idxTmp, ref.i31(idxI32)) def idxVal: Expr = i31.get(ref.cast(local.get(idxTmp, RefType.anyref), RefType.i31ref), signed = true) val normalizedIdx = `if`( condition = i32.lt_s(idxVal, i32.const(0)), ifTrue = i32.add(idxVal, array.len(tupleRef)), ifFalse = S(idxVal), resultTypes = Seq(Result(I32Type)), ) blockInstr( label = N, children = Seq(storeIdx, normalizedIdx), resultTypes = Seq(Result(I32Type)), ) /** Raises a [[WarningReport]] with the given `warnMsgs` and `extraInfo`, and emits the `defaultValue` instruction. */ def warnExpr( warnMsgs: Ls[Message -> Opt[Loc]], extraInfo: Opt[Any] = N, )(defaultValue: => FoldedInstr = unreachable)(using Ctx, Raise)(using Line): Expr = raise(WarningReport(warnMsgs, source = Diagnostic.Source.Compilation, extraInfo = extraInfo)) defaultValue /** Raises an [[ErrorReport]] with the given `warnMsgs` and `extraInfo`, and emits an `unreachable` instruction. */ def errExpr( errMsgs: Ls[Message -> Opt[Loc]], extraInfo: => Opt[Any] = N, )(using Ctx, Raise)(using Line): Expr = raise(ErrorReport(errMsgs, source = Diagnostic.Source.Compilation, extraInfo = extraInfo)) unreachable def getVar(l: Local, loc: Opt[Loc])(using Ctx, FunctionCtx, Raise): Expr = singletonInfoFor(l) match case S(info) => singletonGlobalGet(info) case N => l match case ts: semantics.TermSymbol => errExpr( Ls(msg"WatBuilder::getVar for TermSymbol not implemented yet" -> ts.toLoc), extraInfo = S(ts.toString), ) case ts: semantics.ModuleOrObjectSymbol if ts.asMod.isDefined => errExpr( Ls( msg"WatBuilder::getVar for ModuleOrObjectSymbol (`ts.asMod.isDefined`) not implemented yet" -> ts.toLoc, ), extraInfo = S(ts.toString), ) case ts: semantics.InnerSymbol => funcCtx.lookupLocal(ts) match case S(localIdx) => local.get(localIdx, RefType.anyref) case N => errExpr( Ls( msg"WatBuilder::getVar for InnerSymbol `${ts.toString}` (symbol not in top-level scope) not implemented yet" -> ts.toLoc, ), extraInfo = S( s"Locals: ${(funcCtx.params ++ funcCtx.locals).toString}\nGlobals: ${ctx.getGlobals.toString}", ), ) case l => funcCtx.lookupLocal(l) match case S(localIdx) => local.get(localIdx, RefType.anyref) case N if ctx.containsGlobal(l) => global.get(ctx.getGlobal_!(l), ctx.getGlobalType_!(l).globalType.valType) case _ => errExpr( Ls( msg"Cannot find variable `${l.toString}` (${l.getClass.getSimpleName}) in local or global scope." -> l.toLoc, ), extraInfo = S( s"Locals: ${(funcCtx.params ++ funcCtx.locals).toString}\nGlobals: ${ctx.getGlobals.toString}", ), ) end getVar def argument(a: Arg)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = if a.spread.nonEmpty then errExpr( Ls(msg"WatBackend::argument for spread expression not implemented yet" -> a.value.toLoc), extraInfo = S(a.showAsTree), ) else result(a.value) def operand(a: Arg)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = if a.spread.nonEmpty then die else subexpression(a.value) def subexpression(r: codegen.Result)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = r match case r: Lambda => errExpr( Ls(msg"WatBuilder::subexpression for Lambda not implemented yet" -> r.toLoc), extraInfo = S(r.showAsTree), ) case r => result(r) /** Returns the owning class symbol for a resolved field/member symbol, when available. */ private def fieldOwner(sym: Symbol): Opt[BlockMemberSymbol] = sym match case ts: TermSymbol => ts.owner.flatMap(_.asBlkMember) case ms: MemberSymbol => ms.asTrm.flatMap(_.owner.flatMap(_.asBlkMember)) case _ => N def fieldSelect(thisSym: BlockMemberSymbol, sym: DefinitionSymbol[?])(using Ctx, Raise): FieldIdx = val structInfo = ctx.getTypeInfo_!(thisSym) val symToField = structInfo.compType match case ty: StructType => ty.fieldsBySym case _ => lastWords(s"Cannot select field from non-struct type: ${structInfo.compType.toWat.mkString()}") val fieldIdx = symToField.get(sym).fold(lastWords( s"Missing field `${sym.toString}` in struct `${thisSym.toString}` with type `${structInfo.toWat.mkString()}`", )): field => field.id FieldIdx(SymIdx(fieldIdx)) end fieldSelect /** Resolves `sym` to a predeclared class method symbol, if any. */ private def predeclaredClassMethodSym(sym: DefinitionSymbol[?])(using Ctx): Opt[BlockMemberSymbol] = sym.asBlkMember.filter: methodSym => methodSym.asTrm.exists(_.owner.exists(_.asCls.isDefined)) && ctx.getFunc(methodSym).nonEmpty def result(r: codegen.Result)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = r match case Value.This(sym) => // TODO(Derppening): Add type tracking and refinement for locals, remove the `ref.cast` ref.cast( local.get(funcCtx.lookupLocal_!(sym, sym.toLoc), RefType.anyref), RefType( sym.asBlkMember.fold(baseObjectTypeIdx)(ctx.getType_!(_)), nullable = false, ), ) case Value.Lit(BoolLit(value)) => ref.i31(i32.const(if value then 1 else 0)) case Value.Lit(IntLit(value)) => withValidIntLit(value, r.toLoc)(intVal => ref.i31(i32.const(intVal))) case Value.Lit(StrLit(value)) => val lit = internStringLiteral(value) val stringCtor = getOrLoadStrCtorFunction call( funcidx = stringCtor, operands = Seq(ref.i31(i32.const(lit.offset)), ref.i31(i32.const(lit.byteLen))), returnTypes = Seq(Result(RefType.anyref)), ) case Value.Ref(l, disamb) => if (l is State.unitSymbol) || disamb.contains(State.unitSymbol) then RegisterUnitSingleton() singletonInfoFor(l) match case S(info) => singletonGlobalGet(info) case N => if disamb.exists(_.isInstanceOf[ClassSymbol]) then errExpr: Ls(msg"Plain class references are not supported in Wasm; instantiate the class instead." -> r.toLoc) else ctx.getFunc(l) match case S(funcIdx) => ref.func(funcIdx, RefType(ctx.getFuncTypeUse_!(l).typeIdx, nullable = false)) case N => getVar(l, r.toLoc) case Call(Value.Ref(l: BuiltinSymbol, _), lhs :: rhs :: Nil) if !l.functionLike => if l.binary then errExpr( Ls( msg"WatBuilder::result encountered builtin '${ l.nme }' which should be lowered to an intrinsic function" -> r.toLoc, ), extraInfo = S(r.toString), ) else errExpr(Ls(msg"Cannot call non-binary builtin symbol '${l.nme}'" -> r.toLoc)) case Call(sel @ Select(qual, _), argss) if sel.symbol.flatMap(predeclaredClassMethodSym).nonEmpty => if argss.length > 1 then return errExpr( Ls(msg"WatBuilder::result for Call(...) with multiple argument lists is not supported yet" -> r.toLoc), extraInfo = S(r.toString), ) val methodSym = sel.symbol.flatMap(predeclaredClassMethodSym).get call( funcidx = ctx.getFunc_!(methodSym), operands = result(qual) +: argss.flatten.map(argument), returnTypes = Seq(Result(RefType.anyref)), ) case c @ Call(fun, argss) => if argss.length > 1 then return errExpr( Ls(msg"WatBuilder::result for Call(...) with multiple argument lists is not supported yet" -> c.toLoc), extraInfo = S(c.toString), ) val args = argss.flatten wasmIntrinsicName(fun) match case S(intrName) => val expectedArity = wasmIntrinsicArities(intrName) if expectedArity =/= args.length then return errExpr( Ls(msg"Wasm intrinsic '$intrName' called with incorrect arity (${args.length})" -> c.toLoc), extraInfo = S(c.toString), ) val funcIdx = getIntrinsic(intrName) call( funcidx = funcIdx, operands = args.map(argument), returnTypes = Seq(Result(RefType.anyref)), ) case N => fun match case Value.Ref(l, _) => val base = fun match case Value.Ref(l, _) => ctx.getFunc(l) case _ => N val baseFuncIdx = base match case S(idx) => idx case N => return errExpr( Ls(msg"Expected static function reference in Call(...) expression" -> fun.toLoc), extraInfo = S(fun.toString), ) val baseTypeInfo = ctx.getTypeInfo_!(ctx.getFuncTypeUse_!(baseFuncIdx).typeIdx) val wasmArgs = args.map(argument) call( funcidx = baseFuncIdx, operands = wasmArgs.toSeq, returnTypes = baseTypeInfo.compType.asInstanceOf[FunctionType].sigType.results, ) case _ => val base = subexpression(fun) if base.resultTypes.exists(_ is UnreachableType) then return base val wasmArgs = args.map(argument) val baseTypeIdx = base.resultType match case S(RefType(idx: TypeIdx, _)) => idx case ty => return errExpr( Ls(msg"Expected WAT of `fun` expression in Call(...) to have a `(ref )` type" -> r.toLoc), extraInfo = S( s"Block IR: `${ fun.toString }`\nCompiled WAT: `${ base.toWat.mkString() }`\n... which has type `${ ty.fold("(none)")(_.toWat.mkString()) }`", ), ) val baseTypeInfo = ctx.getTypeInfo_!(baseTypeIdx) call_ref( target = base, operands = wasmArgs.toSeq, typeIdx = baseTypeIdx, funcType = baseTypeInfo.compType.asInstanceOf[FunctionType], ) case sel @ Select(qual, id) => sel.symbol match case S(selObj: ModuleOrObjectSymbol) => if selObj is State.unitSymbol then RegisterUnitSingleton() singletonInfoFor(selObj) match case S(info) => singletonGlobalGet(info) case N => errExpr( Ls(msg"WatBuilder::result for object selection `${id.name}` not implemented yet" -> sel.toLoc), extraInfo = S(sel), ) case S(selSym) if predeclaredClassMethodSym(selSym).nonEmpty => val methodSym = predeclaredClassMethodSym(selSym).get methodSym.asTrm.flatMap(_.defn) match case S(defn: TermDefinition) if defn.params.isEmpty => call( funcidx = ctx.getFunc_!(methodSym), operands = Seq(result(qual)), returnTypes = Seq(Result(RefType.anyref)), ) case _ => errExpr( Ls( msg"`${methodSym.toString}` is neither a field access nor a callable method" -> sel.toLoc, ), extraInfo = S(sel), ) case S(selSym: MemberSymbol) => val qualRes = result(qual) val ownerInfo = fieldOwner(selSym) val selCls = fieldOwner(selSym) getOrElse: lastWords( s"Expected resolved class for Select(...) expression to be a BlockMemberSymbol, but got ${ownerInfo.fold("(none)")( _.toString, )}", ) val fieldidx = fieldSelect(selCls, selSym) struct.get( fieldidx, ref = ref.cast(qualRes, RefType(ctx.getType_!(selCls), nullable = false)), ty = RefType.anyref, ) case N => errExpr( Ls( msg"WatBuilder::result for field selection without a resolved symbol is not implemented (field `${ id.name }`). Use `_.[_]` for index-based accesses." -> sel.toLoc, ), extraInfo = S(sel), ) case dyn @ DynSelect(qual, fld, arrayIdx) => val qualRes = result(qual) if arrayIdx then val idxBuilder = compileTupleIndex( fld = fld, loc = fld.toLoc, errCtx = "WatBuilder::result for array-style dynamic selections", errExtra = dyn.toString, ) tupleArrayGet(qualRes, idxBuilder) else errExpr( Ls(msg"WatBuilder::result for dynamic field selections is not implemented yet" -> dyn.toLoc), extraInfo = S(dyn), ) case Instantiate(_, cls, argss) => if argss.length > 1 then return errExpr( Ls(msg"WatBuilder::result for Instantiate(...) with multiple argument lists is not supported yet" -> r.toLoc), extraInfo = S(r.toString), ) val as = argss.flatten cls match // TODO: Implement proper lowering for Errors with unit payloads. case Select(Value.Ref(sym, _), id) if (sym eq State.globalThisSymbol) && id.name == "Error" => return as.headOption match case S(arg) => arg.value match case Value.Lit(BoolLit(value)) => ref.i31(i32.const(if value then 1 else 0)) case Value.Lit(IntLit(value)) => withValidIntLit(value, arg.value.toLoc)(intVal => ref.i31(i32.const(intVal))) case Value.Lit(StrLit(_)) => result(arg.value) case unsupported => warnExpr( msg"WatBuilder::result for Instantiate(...) of `globalThis.Error(...)` with payload `${ unsupported.toString }` not implemented yet" -> unsupported.toLoc :: Nil, extraInfo = S(unsupported.toString), ): ref.i31(i32.const(0)) case N => ref.i31(i32.const(0)) case _ => () end match val ctorClsSymOpt = cls match case ref: Value.Ref => ref.disamb case sel: Select => sel.symbol case cls => return errExpr( Ls( msg"WatBuilder::result for Instantiate(...) where `cls` is not a Ref(...) or Select(...) path not implemented yet " -> cls.toLoc, ), extraInfo = S(s"Block IR of `cls` expression: ${cls.toString}"), ) val ctorClsSym = ctorClsSymOpt match case S(sym) => sym case N => return errExpr( Ls(msg"Class path for an Instantiate(...) expression must be resolved" -> cls.toLoc), extraInfo = S(s"Block IR of `cls` expression: ${cls.toString}"), ) val ctorClsBlkSym = ctorClsSym.asBlkMember match case S(sym) => sym case N => lastWords( s"Expected resolved class for an Instantiate(...) expression to be a BlockMemberSymbol, but got ${ ctorClsSym.getClass.getName }", ) val ctorFuncIdx = ctx.getFunc(ctorClsBlkSym) match case S(idx) => idx case N => lastWords(s"Missing constructor definition for class ${ctorClsBlkSym.toString}") call(funcidx = ctorFuncIdx, as.map(argument), Seq(Result(RefType.anyref))) case Tuple(mut, elems) => val tupleValues = elems.map(argument) array.new_fixed(tupleArrayType(mut), tupleValues) case r => errExpr( Ls(msg"WatBackend::result for ${r.getClass.getSimpleName} expression not implemented yet" -> r.toLoc), extraInfo = S(s"Block IR: `${r.toString}`"), ) end result /** Returns the intrinsic name if `path` refers to a builtin under `wasm`, or `N` otherwise. */ private def wasmIntrinsicName(path: Path): Opt[Str] = path match case Select(Value.Ref(sym, _), ident) if (sym eq State.wasmSymbol) && wasmIntrinsicNameSet.contains(ident.name) => S(ident.name) case _ => N /** Gets (or creates) the intrinsic function implementing the wasm operator `name`. */ private def getIntrinsic(name: Str)(using Ctx, Raise): FuncIdx = ctx.getOrCreateWasmIntrinsic(name, importIntrinsic(name)) private def importIntrinsic(name: Str)(using Ctx, Raise): FuncIdx = val typeIdx = declareIntrinsicType(name) ctx.addFunctionImport(WasmImport( ExternIntrinsics.SystemModule, name, ExternType.Func(TypeUse(typeIdx), TempSymbol(N, name), wrapId = N -> N), )) /** Creates the intrinsic definition for `name`. */ private def createIntrinsic(name: Str, exportName: Opt[Str])(using Ctx, Raise): FuncIdx = if binaryOps.contains(name) then createBinaryInt31Func(name, binaryOps(name), exportName) else if unaryOps.contains(name) then createUnaryInt31Func(name, unaryOps(name), exportName) else lastWords(s"Unsupported wasm intrinsic '$name'") private def intrinsicParamSuffixes(name: Str): Seq[Str] = if binaryOps.contains(name) then Seq("lhs", "rhs") else Seq("arg") private def declareIntrinsicType(name: Str)(using Ctx, Raise): TypeIdx = ctx.addType(TypeInfo( sym = TempSymbol(N, name), compType = FunctionType( params = intrinsicParamSuffixes(name).map(nme => WasmParam(SymIdx(nme), RefType.anyref)), results = Seq(Result(RefType.anyref)), ), objectTag = N, )) /** Creates a binary Int31 intrinsic with two parameters and body built from `op`. */ private def createBinaryInt31Func( name: Str, op: (Expr, Expr) => Expr, exportName: Opt[Str], )(using Ctx, Raise): FuncIdx = val params = mkIntrinsicParams(name, Seq("lhs", "rhs")) val lhsName = params.head._2 val rhsName = params(1)._2 val body = binaryInt31Body(LocalIdx(lhsName), LocalIdx(rhsName), op) createIntrinsicFunc(name, params, body, exportName) /** Creates a unary Int31 intrinsic with a single parameter and body built from `op`. */ private def createUnaryInt31Func( name: Str, op: Expr => Expr, exportName: Opt[Str], )(using Ctx, Raise): FuncIdx = val params = mkIntrinsicParams(name, Seq("arg")) val argName = params.head._2 val body = unaryInt31Body(LocalIdx(argName), op) createIntrinsicFunc(name, params, body, exportName) /** Allocates the Wasm type and function definition for an intrinsic with the given signature. */ private def createIntrinsicFunc( name: Str, params: Seq[TempSymbol -> SymIdx], body: Expr, exportName: Opt[Str], )(using Ctx, Raise): FuncIdx = val funcTy = declareIntrinsicType(name) val funcInfo = FuncInfo( sym = TempSymbol(N, name), typeUse = TypeUse(funcTy), params = params, locals = Seq.empty, body = body, resultTypes = Seq(Result(RefType.anyref)), exportName = exportName, ) ctx.addFunc(funcInfo) def intrinsicSupportModule()(using Raise): Document = val ctx = Ctx.empty given Ctx = ctx wasmIntrinsicNameSet.toSeq.sorted.foreach: name => createIntrinsic(name, S(name)) ctx.toWat /** Builds the body for an Int31 binary operator. */ private def binaryInt31Body( lhsIdx: LocalIdx, rhsIdx: LocalIdx, op: (Expr, Expr) => Expr, )(using Ctx): Expr = val cond = i32.and( ref.test(getLocalAnyref(lhsIdx), RefType.i31ref), ref.test(getLocalAnyref(rhsIdx), RefType.i31ref), ) val i31Op = ref.i31(op(getI32FromAnyref(lhsIdx), getI32FromAnyref(rhsIdx))) `if`( condition = cond, ifTrue = i31Op, ifFalse = S(unreachable), resultTypes = Seq(Result(RefType.anyref)), ) /** Builds the body for an Int31 unary operator. */ private def unaryInt31Body(paramIdx: LocalIdx, op: Expr => Expr)(using Ctx): Expr = val cond = ref.test(getLocalAnyref(paramIdx), RefType.i31ref) val i31Op = ref.i31(op(getI32FromAnyref(paramIdx))) `if`( condition = cond, ifTrue = i31Op, ifFalse = S(unreachable), resultTypes = Seq(Result(RefType.anyref)), ) /** Creates parameters for an intrinsic. */ private def mkIntrinsicParams(name: Str, suffixes: Seq[Str]): Seq[TempSymbol -> SymIdx] = suffixes.map: suffix => val sym = TempSymbol(N, suffix) sym -> SymIdx(suffix) /** Loads the local `name` as an `anyref`. */ private def getLocalAnyref(idx: LocalIdx): Expr = local.get(idx, RefType.anyref) /** Extracts the signed i32 value from the Int31 stored in the local `name`. */ private def getI32FromAnyref(idx: LocalIdx): Expr = i31.get(ref.cast(getLocalAnyref(idx), RefType.i31ref), true) extension (expr: Expr) private def isControlTransfer: Bool = expr.resultType.contains(UnreachableType) || expr.mnemonic == "return" private def asStatement(expr: Expr): Expr = if expr.isControlTransfer then expr else expr.resultType match case S(_) => drop(expr) case N => expr def returningTerm(t: Block)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = t match case Assign(l, r, rst) if l is State.noSymbol => val rExpr = result(r) val evalExpr = rExpr.resultType match case S(_) => drop(rExpr) case N => rExpr val rstBlk = returningTerm(rst) blockInstr( label = N, children = Seq(evalExpr, rstBlk), resultTypes = rstBlk.resultTypes.map(r => Result(r.asValType_!)), ) case Assign(l, r, rst) => val lExpr = getVar(l, l.toLoc) val rExpr = result(r) val assignExpr = lExpr.mnemonicPrefix match case S("global") => global.set(lExpr.instrargs(0).asInstanceOf[GlobalIdx], rExpr) case S("local") => local.set(lExpr.instrargs(0).asInstanceOf[LocalIdx], rExpr) case _ => lastWords( s"Expected `global.*` or `local.*` when compiling instruction for `$l`, but got ${lExpr.mnemonic}", ) val rstBlk = returningTerm(rst) blockInstr( label = N, children = Seq(assignExpr, rstBlk), resultTypes = resultClauses(rstBlk), ) case assign @ AssignField(lhs, nme, rhs, rst) => val lhsExpr = result(lhs) val rhsExpr = result(rhs) val assignInstr = assign.symbol match case S(selSym) => selSym.asTrm match case S(fieldSym) => val selOwner = fieldSym.owner getOrElse lastWords(s"Expected resolved AssignField(...) expression `$fieldSym` to have an owner") val selCls = selOwner.asBlkMember getOrElse lastWords( s"Expected resolved class for AssignField(...) expression to be a BlockMemberSymbol, but got $selOwner (${ selOwner.getClass.getName })", ) val fieldidx = fieldSelect(selCls, fieldSym) val objRef = ref.cast(lhsExpr, RefType(ctx.getType_!(selCls), nullable = false)) struct.set(fieldidx, objRef, rhsExpr) case N => lastWords( s"Expected resolved AssignField(...) expression to be a TermSymbol, but got $selSym (${ selSym.getClass.getName })", ) case N => errExpr( Ls( msg"WatBuilder::returningTerm for AssignField(...) without a resolved symbol is not implemented (field `${ nme.name }`). Use `_.[_]` for index-based accesses." -> nme.toLoc, ), extraInfo = S(assign), ) val rstBlk = returningTerm(rst) blockInstr( label = N, children = Seq(assignInstr, rstBlk), resultTypes = resultClauses(rstBlk), ) case assign @ AssignDynField(lhs, fld, arrayIdx, rhs, rst) => val lhsExpr = result(lhs) val rhsExpr = result(rhs) val assignInstr = if arrayIdx then val tupleArrayType = this.tupleArrayType(mut = true) val tupleRef = ref.cast(lhsExpr, RefType(tupleArrayType, nullable = false)) val idxBuilder = compileTupleIndex( fld = fld, loc = fld.toLoc, errCtx = "WatBuilder::returningTerm for AssignDynField(...)", errExtra = assign.toString, ) val idxExpr = idxBuilder(tupleRef) array.set(tupleArrayType, tupleRef, idxExpr, rhsExpr) else errExpr( Ls(msg"WatBuilder::returningTerm for AssignDynField(...) where `arrayIdx = false` is not implemented yet" -> lhs.toLoc), extraInfo = S(assign), ) val rstBlk = returningTerm(rst) blockInstr( label = N, children = Seq(assignInstr, rstBlk), resultTypes = resultClauses(rstBlk), ) case Define(defn, rst) => def mkThis(sym: InnerSymbol): Expr = result(Value.This(sym)) defn match case ValDefn(tsym, sym, p) => // * Currently we allow `val` outside of object/module scopes, // * in which case it has no owner and is just a glorified local variable rather than a field tsym.owner match case N => val symExpr = getVar(sym, sym.toLoc) val defineExpr = symExpr.mnemonicPrefix match case S("global") => global.set(symExpr.instrargs(0).asInstanceOf[GlobalIdx], result(p)) case S("local") => local.set(symExpr.instrargs(0).asInstanceOf[LocalIdx], result(p)) case _ => lastWords( s"Expected `global.*` or `local.*` when compiling definition for `$sym`, but got ${symExpr.mnemonic}", ) val rstWat = returningTerm(rst) blockInstr( label = N, children = Seq( defineExpr, rstWat, ), resultTypes = rstWat.resultTypes.map(r => Result(r.asValType_!)), ) case S(owner) => val ownerBlkMem = owner.asBlkMember.get val rstWat = returningTerm(rst) blockInstr( label = N, children = Seq( struct.set( index = fieldSelect(ownerBlkMem, tsym), ref = mkThis(owner), value = result(p), ), rstWat, ), resultTypes = resultClauses(rstWat), ) case defn: (FunDefn | ClsLikeDefn) => val res = boundary: defn match case FunDefn(params = Nil) => lastWords("cannot generate function with no parameter list") case fd @ FunDefn(own, sym, dSym, ps :: pss, bod) => if own.nonEmpty then break(errExpr( Ls( msg"WatBuilder::returningTerm for Define(...) with `owner.nonEmpty` not implemented yet" -> defn.sym.toLoc, ), extraInfo = S(defn.showAsTree), )) val result = pss.foldRight(bod): case (ps, block) => Return(Lambda(ps, block), false) val (bodyWat, fnCtx) = setupFunction(N, ps, result) if sym.nameIsMeaningful then val funcTy = ctx.addType( TypeInfo( sym = TempSymbol(N, sym.nme), FunctionType( params = fnCtx.params.map(p => WasmParam(p._2, RefType.anyref)), results = Seq.fill(bodyWat.resultTypes.length)(Result(RefType.anyref)), ), objectTag = N, ), ) val funcInfo = FuncInfo( sym, typeUse = TypeUse(funcTy), params = ps.params.zip(fnCtx.params.map(_._2)).map((p, idx) => p.sym -> idx), resultTypes = Seq.fill(bodyWat.resultTypes.length)(Result(RefType.anyref)), locals = fnCtx.locals, body = bodyWat, exportName = sym.optionIf(_.nameIsMeaningful).map(_.nme), ) ctx.addFunc(funcInfo) if summon[SessionExportCtx].shouldExport(defn.sym) then summon[SessionExportCtx].emit(SessionFunc( sym = defn.sym, wrapId = funcInfo.wrapId, moduleName = SessionBinding.ReplModuleName, exportName = sym.nme, funcType = FunctionType(funcInfo.getSignatureType), )) nop else errExpr( Ls( msg"WatBuilder::returningTerm for FunDefn(...) where `!sym.nameIsMeaningful` not implemented yet" -> defn.sym.toLoc, ), extraInfo = S(defn.showAsTree), ) end if case clsLikeDefn: ClsLikeDefn => // Guard against unsupported features def errUnimplExpr(cond: Str): Nothing = break(errExpr( Ls( msg"WatBuilder::returningTerm for ClsLikeDefn(...) where `$cond` not implemented yet" -> clsLikeDefn.sym.toLoc, ), extraInfo = S(defn.showAsTree), )) val isSingletonObj = clsLikeDefn.k is syntax.Obj if clsLikeDefn.owner.nonEmpty then break(errUnimplExpr("owner.nonEmpty")) if !(clsLikeDefn.k is syntax.Cls) && !isSingletonObj then break(errUnimplExpr("unsupported ClsLikeDefn kind")) if isSingletonObj && clsLikeDefn.paramsOpt.nonEmpty then break(errUnimplExpr("paramsOpt.nonEmpty for object")) if clsLikeDefn.auxParams.nonEmpty then break(errUnimplExpr("auxParams.nonEmpty")) if isSingletonObj && clsLikeDefn.parentPath.nonEmpty then break(errUnimplExpr("parentPath.nonEmpty for object")) if isSingletonObj && clsLikeDefn.methods.nonEmpty then break(errUnimplExpr("methods.nonEmpty for object")) if clsLikeDefn.companion.isDefined then break(errUnimplExpr("companion.isDefined")) val ctorAuxParams = clsLikeDefn.auxParams.map: ps => ps.params.map: p => p -> errUnimplExpr("auxParams.nonEmpty") // Use the symbolic type reference (e.g. `$Foo`) in emitted WAT for readability. // Numeric indices are only needed for `$tag` values. val typeref = ctx.getType_!(clsLikeDefn.sym) val typeinfo = ctx.getTypeInfo_!(typeref) val (initWat, initFnCtx) = setupInitLocals(clsLikeDefn) // * If there are no ctor params, pop one param list off the aux params val newCtorAuxParams = clsLikeDefn.paramsOpt match case None => ctorAuxParams match case head :: next => next case Nil => ctorAuxParams case Some(_) => ctorAuxParams val tagValue = typeinfo.objectTag getOrElse: lastWords(s"Expected class ${clsLikeDefn.sym} to have an object tag") val initFuncRef = initFuncSym(clsLikeDefn.sym) val (ctorCode, ctorFnCtx) = genFuncBody(clsLikeDefn.paramsOpt.toList, thisSym = N): val thisVar = bindCtorThis(clsLikeDefn.isym) val initCall = call( funcidx = ctx.getFunc_!(initFuncRef), operands = local.get(thisVar, RefType.anyref) +: funcCtx.params.map((_, nme) => getLocalAnyref(LocalIdx(nme))), returnTypes = Seq(Result(RefType.anyref)), ) blockInstr( label = N, Seq( local.set(thisVar, struct.new_default(typeref)), struct.set( FieldIdx(SymIdx(typeinfo.compType.asInstanceOf[StructType].fields(0)._2.id)), ref.cast( local.get(thisVar, RefType.anyref), RefType(typeref, nullable = false), ), i32.const(tagValue), ), drop(initCall), `return`(S(local.get(thisVar, RefType(typeref, nullable = false)))), ), resultTypes = Seq(Result(RefType.anyref)), ) val ctorAux = if newCtorAuxParams.isEmpty then ctorCode else break(errUnimplExpr("newCtorAuxParams.nonEmpty")) val predeclaredInit = ctx.getFuncInfo_!(initFuncRef) ctx.addFunc(FuncInfo( sym = initFuncRef, wrapId = S(clsLikeDefn.sym.nme) -> N, typeUse = predeclaredInit.typeUse, params = initFnCtx.params, resultTypes = initWat.resultTypes.map(ty => Result(ty.asValType_!)), locals = initFnCtx.locals, body = initWat, exportName = predeclaredInit.exportName, )) val predeclaredCtor = ctx.getFuncInfo_!(clsLikeDefn.sym) val ctorFuncInfo = FuncInfo( sym = clsLikeDefn.sym, wrapId = S(clsLikeDefn.sym.nme) -> N, typeUse = predeclaredCtor.typeUse, params = ctorFnCtx.params, resultTypes = ctorAux.resultTypes.map(ty => Result(ty.asValType_!)), locals = ctorFnCtx.locals, body = ctorAux, exportName = predeclaredCtor.exportName, ) ctx.addFunc(ctorFuncInfo) def overwriteMethod( sym: BlockMemberSymbol, ps: ParamList, bod: Block, ): Unit = val (bodyWat, fnCtx) = setupFunction(S(clsLikeDefn.isym), ps, bod) val predeclaredMethod = ctx.getFuncInfo_!(sym) ctx.addFunc(FuncInfo( sym, wrapId = S(clsLikeDefn.sym.nme) -> N, typeUse = predeclaredMethod.typeUse, params = fnCtx.params, resultTypes = Seq.fill(bodyWat.resultTypes.length)(Result(RefType.anyref)), locals = fnCtx.locals, body = bodyWat, exportName = predeclaredMethod.exportName, )) clsLikeDefn.methods.foreach: case FunDefn(_, sym, _, Nil, bod) => overwriteMethod(sym, PlainParamList(Nil), bod) case FunDefn(_, sym, _, ps :: Nil, bod) => overwriteMethod(sym, ps, bod) case methodDefn => lastWords( s"Class method `$methodDefn` with multiple parameter lists should be rejected in predeclaration pass", ) if summon[SessionExportCtx].shouldExport(clsLikeDefn.sym) then summon[SessionExportCtx].emit(SessionClass( sym = clsLikeDefn.sym, wrapId = typeinfo.wrapId, compType = typeinfo.compType, objectTag = typeinfo.objectTag, runtimeTags = ctx.getAllRuntimeTags(clsLikeDefn.sym).getOrElse(LinkedHashSet(tagValue)), aliasSyms = clsLikeDefn.isym match case mos: ModuleOrObjectSymbol => mos :: Nil case _ => Nil, )) if !isSingletonObj && clsLikeDefn.sym.nameIsMeaningful then summon[SessionExportCtx].emit(SessionFunc( sym = clsLikeDefn.sym, wrapId = ctorFuncInfo.wrapId, moduleName = SessionBinding.ReplModuleName, exportName = clsLikeDefn.sym.nme, funcType = FunctionType( SignatureType( params = ctorFnCtx.params.map(p => WasmParam(p._2, RefType.anyref)), results = Seq(Result(RefType.anyref)), ), ), )) end if if isSingletonObj then registerSingletonInit(clsLikeDefn, typeref) if summon[SessionExportCtx].shouldExport(clsLikeDefn.sym) then ctx.getSingletonInfo(clsLikeDefn.sym).foreach: info => val singletonOwner = clsLikeDefn.isym match case mos: ModuleOrObjectSymbol => S(mos) case _ => N summon[SessionExportCtx].emit(SessionSingleton( blockSym = clsLikeDefn.sym, wrapId = typeinfo.wrapId, objectSym = singletonOwner, moduleName = SessionBinding.ReplModuleName, exportName = info.globalName, globalTy = info.globalTy, )) nop case defn => errExpr( Ls(msg"WatBuilder::returningTerm for Define(...) not implemented yet" -> defn.sym.toLoc), extraInfo = S(defn.showAsTree), ) end match val rstBlk = returningTerm(rst) blockInstr( label = N, children = Seq(res, rstBlk), resultTypes = resultClauses(rstBlk), ) end match case Return(res, true) => val resWat = result(res) resWat.resultType match case S(refTy: RefType) => refTy.heapType match case HeapType.Func => errExpr(Ls(msg"Returning function instances is not supported" -> res.toLoc)) case typeidx: TypeIdx if ctx.getTypeInfo_!(typeidx).compType.isInstanceOf[FunctionType] => errExpr(Ls(msg"Returning function instances is not supported" -> res.toLoc)) case _ => () case _ => () resWat case Return(res, false) => val resWat = result(res) resWat.resultType match case S(refTy: RefType) => refTy.heapType match case HeapType.Func => errExpr(Ls(msg"Returning function instances is not supported" -> res.toLoc)) case typeidx: TypeIdx if ctx.getTypeInfo_!(typeidx).compType.isInstanceOf[FunctionType] => errExpr(Ls(msg"Returning function instances is not supported" -> res.toLoc)) case _ => () case _ => () `return`(S(resWat)) case Scoped(syms, body) => blockPreamble(syms) returningTerm(body) case Break(label) => funcCtx.lookupLabel(label) match case S(target) => br(target.breakLabel) case N => errExpr( Ls( msg"WatBuilder::returningTerm for Break(...) to unknown label `${label.nme}`" -> label.toLoc, ), extraInfo = S(t.showAsTree), ) case Continue(label) => funcCtx.lookupLabel(label) match case S(target) => target.continueLabel match case S(continueLabel) => br(continueLabel) case N => errExpr( Ls( msg"WatBuilder::returningTerm for Continue(...) to non-loop label `${label.nme}`" -> label.toLoc, ), extraInfo = S(t.showAsTree), ) case N => errExpr( Ls( msg"WatBuilder::returningTerm for Continue(...) to unknown label `${label.nme}`" -> label.toLoc, ), extraInfo = S(t.showAsTree), ) case Label(label, loop, body, rst) => val labeledRegion = funcCtx.withLabel(label, hasContinueLabel = loop): case LabelTarget(breakLabel, continueLabel) => val bodyExpr = returningTerm(body) val bodyStmt = asStatement(bodyExpr) if loop then blockInstr( label = S(breakLabel), children = Seq( loopInstr( label = continueLabel, children = Seq(bodyStmt), resultTypes = Seq.empty, ), ), resultTypes = Seq.empty, ) else blockInstr( label = S(breakLabel), children = Seq(bodyStmt), resultTypes = Seq.empty, ) val rstExpr = returningTerm(rst) val rstResultTypes = rstExpr.resultTypes.flatMap(ty => ty.asValType.map(Result(_))) blockInstr( label = N, children = Seq(labeledRegion, rstExpr), resultTypes = rstResultTypes, ) case Match(scrut, arms, dflt, rst) => val tailMode = rst.isInstanceOf[End] val matchResLocal = if tailMode then S(mkTempLocal("matchRes")) else N def getScrutExpr: Expr = result(scrut) def assignTailResult(target: LocalIdx, expr: Expr): Expr = if expr.isControlTransfer then expr else expr.resultType match case S(_) => local.set(target, expr) case N => blockInstr( label = N, children = Seq( expr, local.set(target, result(Value.Ref(State.unitSymbol))), ), resultTypes = Seq.empty, ) def lowerMatchBody(expr: Expr): Expr = matchResLocal match case S(localIdx) => assignTailResult(localIdx, expr) case N => asStatement(expr) val matchResInitExpr = matchResLocal.map: localIdx => local.set(localIdx, ref.`null`(HeapType.Any)) // Compile each match arm val matchBlock = funcCtx.withLabel(LabelSymbol(N, "match"), hasContinueLabel = false): case LabelTarget(matchLabel, _) => boundary: val armExprs = arms.zipWithIndex.flatMap: (caseAndBody, armIdx) => val (cse, body) = caseAndBody cse match case Case.Lit(lit) => val testExpr: FoldedInstr = lit match case BoolLit(value) => val scrutAsI31 = ref.cast(getScrutExpr, RefType.i31ref) val scrutValue = i31.get(scrutAsI31, signed = true) i32.eq(scrutValue, i32.const(if value then 1 else 0)) case IntLit(value) => val scrutAsI31 = ref.cast(getScrutExpr, RefType.i31ref) val scrutValue = i31.get(scrutAsI31, signed = true) i32.eq(scrutValue, withValidIntLit(value, lit.toLoc)(i32.const)) case _ => break(errExpr(Ls(msg"Pattern matching for unit literals not implemented yet" -> lit.toLoc))) val bodyExpr = returningTerm(body) val armBodyExpr = lowerMatchBody(bodyExpr) funcCtx.withLabel(LabelSymbol(N, "arm"), hasContinueLabel = false): case LabelTarget(armLabel, _) => S(`if`( condition = testExpr, ifTrue = blockInstr( label = S(armLabel), children = Seq(armBodyExpr, br(matchLabel)), resultTypes = Seq.empty, ), ifFalse = N, resultTypes = Seq.empty, )) case Case.Cls(cls, _) => val clsBlkMemberSym = cls.asBlkMember getOrElse: break(errExpr( Ls(msg"Could not resolve BlockMemberSymbol for class pattern" -> cls.toLoc), extraInfo = S(s"ClassLikeSymbol: ${cls.toString}"), )) val clsTypeIdx = ctx.getType_!(clsBlkMemberSym) val typeinfo = ctx.getTypeInfo_!(clsTypeIdx) val expectedTag = typeinfo.objectTag getOrElse: lastWords(s"Expected class $clsBlkMemberSym to have an object tag") // TODO (https://github.com/orgs/hkust-taco/projects/14/views/1?pane=issue&itemId=174476970): // replace with RTTI ancestry checks once each object carries runtime type information. val matchTags = ctx.getAllRuntimeTags(clsBlkMemberSym).getOrElse(LinkedHashSet(expectedTag)) val scrutExpr = getScrutExpr val isStructCompatible = ref.test(scrutExpr, baseObjectRefType(nullable = true)) val bodyExpr = returningTerm(body) val armBodyExpr = lowerMatchBody(bodyExpr) // Safe to cast and extract tag since ref.test passed val scrutAsObject = ref.cast(scrutExpr, baseObjectRefType(nullable = false)) val scrutTag = struct.get( FieldIdx(SymIdx(typeinfo.compType.asInstanceOf[StructType].fields(0)._2.id)), scrutAsObject, I32Type, ) val tagMatches = matchTags.toList match case tag :: Nil => i32.eq(scrutTag, i32.const(tag)) case tag :: rest => rest.foldLeft[Expr](i32.eq(scrutTag, i32.const(tag))): (acc, candidateTag) => i32.or(acc, i32.eq(scrutTag, i32.const(candidateTag))) case Nil => lastWords(s"Expected class $clsBlkMemberSym to have at least one accepted runtime tag") funcCtx.withLabel(LabelSymbol(N, "arm"), hasContinueLabel = false): case LabelTarget(armLabel, _) => S(`if`( condition = isStructCompatible, ifTrue = `if`( condition = tagMatches, ifTrue = blockInstr( label = S(armLabel), children = Seq(armBodyExpr, br(matchLabel)), resultTypes = Seq.empty, ), ifFalse = N, resultTypes = Seq.empty, ), ifFalse = N, resultTypes = Seq.empty, )) case Case.Tup(len, inf) => val arrayRefType = RefType(HeapType.Array, nullable = true) val isArrayTest = ref.test(getScrutExpr, arrayRefType) // Length check val scrutArray = ref.cast(getScrutExpr, arrayRefType) val arrayLength = array.len(scrutArray) val lengthTest = if inf then i32.ge_u(arrayLength, i32.const(len)) else i32.eq(arrayLength, i32.const(len)) val testExpr = i32.and(isArrayTest, lengthTest) val bodyExpr = returningTerm(body) val armBodyExpr = lowerMatchBody(bodyExpr) funcCtx.withLabel(LabelSymbol(N, "arm"), hasContinueLabel = false): case LabelTarget(armLabel, _) => S(`if`( condition = testExpr, ifTrue = blockInstr( label = S(armLabel), children = Seq(armBodyExpr, br(matchLabel)), resultTypes = Seq.empty, ), ifFalse = N, resultTypes = Seq.empty, )) case _ => break(errExpr( Ls( msg"WatBuilder::returningTerm for Match(...) with case `${cse.toString}` not implemented yet" -> N, ), extraInfo = S(cse.toString), )) end match val defaultExpr = val rawDefaultExpr = dflt match case S(defaultBody) => returningTerm(defaultBody) case N => nop lowerMatchBody(rawDefaultExpr) // Generate the match block blockInstr( label = S(matchLabel), children = matchResInitExpr.toSeq ++ armExprs :+ defaultExpr, resultTypes = Seq.empty, ) if tailMode then blockInstr( label = N, children = Seq( matchBlock, local.get(matchResLocal.get, RefType.anyref), ), resultTypes = Seq(Result(RefType.anyref)), ) else val rstExpr = returningTerm(rst) blockInstr( label = N, children = Seq(matchBlock, rstExpr), resultTypes = rstExpr.resultTypes.flatMap(ty => ty.asValType.map(Result(_))), ) // * Try/finally lowering is intentionally rejected for now: the previous implementation required `exnref` support // * which can only be enabled with the `--experimental-wasm-exnref` flag. // * Later, it will be implemented using intrinsic function. case TryBlock(sub, _, _) => errExpr( Ls(msg"WatBuilder::returningTerm for TryBlock(...) not implemented yet" -> N), extraInfo = S(sub.showAsTree), ) case Throw(res) => val excWat = result(res) `throw`(exnTagIdx, Seq(excWat)) case End(_) => nop case t => errExpr( Ls(msg"WatBuilder::returningTerm for ${t.getClass.getSimpleName} block not implemented yet" -> N), extraInfo = S(t.showAsTree), ) end match end returningTerm def program( p: Program, exprt: Opt[BlockMemberSymbol], wd: io.Path, sessionImports: Seq[SessionBinding], preservedSessionSymbols: Set[Local], )(using Raise): CompiledWasmModule = for imprt <- p.imports do raise( ErrorReport( msg"Import of symbol `${imprt._2}` not implemented yet" -> imprt._1.toLoc :: Nil, extraInfo = S(imprt), source = Diagnostic.Source.Compilation, ), ) exprt.foreach: exprt => raise( ErrorReport( msg"Export of symbol `${exprt.nme}` not implemented yet" -> exprt.toLoc :: Nil, extraInfo = S(exprt), source = Diagnostic.Source.Compilation, ), ) val sessionExportCtx = SessionExportCtx( symbolsToExport = preservedSessionSymbols, collectedBindings = ArrayBuf.empty, ) given SessionExportCtx = sessionExportCtx val ctx = Ctx.empty given Ctx = ctx def systemMemMinPages: Int = ctx.getMemoryImport( ExternIntrinsics.SystemModule, ExternIntrinsics.SystemMemoryImportName, ).fold(0)(_.memType.lim.min) def compiledModule(entryName: Str): CompiledWasmModule = CompiledWasmModule(ctx.toWat, entryName, systemMemMinPages, sessionExportCtx.collectedBindings.toSeq) // Create base Object struct with tag field that all other structs will inherit ctx.addType(TypeInfo( sym = baseObjectSym, StructType(Seq(tagFieldSym -> Field(I32Type, mutable = true, id = "$tag"))), objectTag = S(ctx.getFreshObjectTag() ensuring (_ == 0)), )) registerSessionImports(sessionImports) collectSessionGlobalSymbols( p.main, sessionExportCtx, ).toSeq.sortBy(_.uid).foreach: sym => registerSessionGlobal(sym) boundary[CompiledWasmModule]: val outerRaise = summon[Raise] // Early registration scheme: collect supported top-level classes from main block, // order by inheritance, predeclare struct types, init functions, and constructors. locally: given Raise = diag => outerRaise(diag) diag match case _: ErrorReport => break(compiledModule("entry")) case _ => () val ordered = sortTopLevelClasses(collectTopLevelClassDefns(p.main)) ordered.foreach(predeclareClassType) predeclareClassTags(ordered) ordered.foreach(predeclareClassInit) ordered.foreach(predeclareClassConstructor) ordered.foreach(predeclareClassMethods) // Compile the entry function under a dedicated local scope so that any temp locals introduced // during codegen (e.g., via `local.tee`) are declared in the entry function. val (entryFnExpr, entryFnCtx) = genFuncBody(Nil, thisSym = N): val rawEntryFnExpr = block(p.main) normalizeEntryExpr(rawEntryFnExpr, p.main.isAbortive) val entrySym = BlockMemberSymbol("entry", Nil) val entryFnTy = ctx.addType(TypeInfo( sym = TempSymbol(N, entrySym.nme), FunctionType(params = Seq.empty, results = Seq(Result(RefType.anyref))), objectTag = N, )) val entryFnInfo = FuncInfo( sym = entrySym, typeUse = TypeUse(entryFnTy), params = Seq.empty, resultTypes = Seq(Result(RefType.anyref)), locals = entryFnCtx.locals, body = entryFnExpr, exportName = S(entrySym.nme), ) if stringLits.nonEmpty then stringLits.foreach: (s, lit) => if lit.byteLen > 0 then ctx.addDataSegment(DataSegment.Active( offset = i32.const(lit.offset), bytes = Seq(lit.watBytes), memuse = N, sym = TempSymbol(N, s.take(WatBuilder.StringConstantIdentMaxLength)), )) val singletonInitActions = ctx.getSingletonInitActions if singletonInitActions.nonEmpty then val initTy = ctx.addType(TypeInfo( sym = TempSymbol(N, "start"), FunctionType(params = Seq.empty, results = Seq.empty), objectTag = N, )) val initBody = blockInstr( label = N, children = singletonInitActions.toSeq, resultTypes = Seq.empty, ) val initFn = ctx.addFunc(FuncInfo( sym = TempSymbol(N, "start"), typeUse = TypeUse(initTy), params = Seq.empty, resultTypes = Seq.empty, locals = Seq.empty, body = initBody, exportName = N, )) ctx.setStartFunc(initFn) end if ctx.addFunc(entryFnInfo) compiledModule(entrySym.nme) end program def blockPreamble(ss: Iterable[Symbol])(using Ctx, FunctionCtx, Raise): Unit = ss.toArray.sortBy(_.uid).toSeq.filter: sym => !ctx.containsGlobal(sym) && ctx.getFunc(sym).isEmpty .foreach: sym => funcCtx.addLocal(sym) def nonNestedScoped( blk: Block, )(k: Block => Expr)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = blk match case Scoped(syms, body) => blockPreamble(syms.view.filter(body.freeVars)) k(body) case _ => k(blk) def block(t: Block)(using Ctx, FunctionCtx, Raise, SessionExportCtx): Expr = nonNestedScoped(t)(returningTerm) def setupFunction( thisParam: Opt[InnerSymbol], params: ParamList, body: Block, )(using Ctx, Raise, SessionExportCtx): (Expr, FunctionCtx) = genFuncBody(params :: Nil, thisSym = thisParam): block(body) end WatBuilder ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/ConstraintSolver.scala ================================================ package hkmc2 package invalml import scala.collection.mutable import semantics.* import Message.MessageContext import mlscript.utils.*, shorthands.* import utils.* import utils.Scope type Cache = mutable.HashSet[(Type, Type)] type ExtrudeCache = mutable.HashMap[(Uid[InfVar], Bool), InfVar] case class CCtx(cache: Cache, parents: Ls[(Type, Type)], origin: Term, exp: Opt[GeneralType])(using Scope): def err(using Raise, InvalCtx) = raise(ErrorReport( msg"Type error in ${origin.describe}${exp match case S(ty) => msg" with expected type ${ty.show}" case N => msg"" }" -> origin.toLoc :: parents.reverse.map(p => msg"because: cannot constrain ${p._1.show} <: ${p._2.show}" -> N ) )) def nest(sub: (Type, Type)): CCtx = copy(cache = cache += sub, parents = parents match case `sub` :: _ => parents case _ => sub :: parents ) object CCtx: inline def init(origin: Term, exp: Opt[GeneralType])(using Scope) = CCtx(mutable.HashSet.empty, Nil, origin, exp) def cctx(using CCtx): CCtx = summon class ConstraintSolver(infVarState: InfVarUid.State, elState: Elaborator.State, tl: TraceLogger): import tl.{trace, log} import hkmc2.invalml.NormalForm.* private def freshXVar(lvl: Int, sym: Symbol, hint: Str): InfVar = InfVar(lvl, infVarState.nextUid, new VarState(), false)(InstSymbol(sym)(using elState), hint) def extrude(ty: Type)(using lvl: Int, pol: Bool, cache: ExtrudeCache, invalctx: InvalCtx, cctx: CCtx, tl: TL): Type = trace[Type](s"Extruding[${printPol(pol)}] ${ty.showDbg}", r => s"~> ${r.showDbg}"): if ty.lvl <= lvl then ty else ty.toBasic/*TODO improve extrude directly*/ match case ClassLikeType(sym, targs) => ClassLikeType(sym, targs.map { case Wildcard(in, out) => Wildcard(extrude(in)(using lvl, !pol), extrude(out)) case t: Type => Wildcard(extrude(t)(using lvl, !pol), extrude(t)) }) case v @ InfVar(_, uid, state, true) => // * skolem cache.getOrElse(uid -> pol, { val nv = freshXVar(lvl, v.sym, v.hint) cache += uid -> pol -> nv if pol then constrainImpl(state.upperBounds.foldLeft[Type](Top)(_ & _), nv) else constrainImpl(nv, state.lowerBounds.foldLeft[Type](Bot)(_ | _)) nv }) case v @ InfVar(_, uid, _, false) => cache.getOrElse(uid -> pol, { val nv = freshXVar(lvl, v.sym, v.hint) cache += uid -> pol -> nv if pol then v.state.upperBounds ::= nv nv.state.lowerBounds = v.state.lowerBounds.map(extrude) // * propagate else v.state.lowerBounds ::= nv nv.state.upperBounds = v.state.upperBounds.map(extrude) // * propagate nv }) case ft @ FunType(args, ret, eff) => FunType(args.map(arg => extrude(arg)(using lvl, !pol)), extrude(ret), extrude(eff)) case ComposedType(lhs, rhs, p) => Type.mkComposedType(extrude(lhs), extrude(rhs), p) case NegType(ty) => Type.mkNegType(extrude(ty)(using lvl, !pol)) case Top | Bot => ty private def constrainConj(conj: Conj)(using InvalCtx, CCtx, TL): Unit = trace(s"Constraining ${conj.showDbg}"): conj match case Conj(i, u, (v, pol) :: tail) => var rest = Conj(i, u, tail) if v.isSkolem then constrainConj(rest) else val bd = if v.lvl >= rest.lvl then rest else extrude(rest)(using v.lvl, true, mutable.HashMap.empty) if pol then val nc = Type.mkNegType(bd).toDnf // always cache the normal form to avoid unexpected cache misses log(s"New bound: ${v.showDbg} <: ${nc.showDbg}") cctx.nest(v.toDnf -> nc) givenIn: v.state.upperBounds ::= nc v.state.lowerBounds.foreach(lb => constrainImpl(lb, nc)) else val c = bd.toDnf // always cache the normal form to avoid unexpected cache misses log(s"New bound: ${v.showDbg} :> ${c.showDbg}") cctx.nest(c -> v.toDnf) givenIn: v.state.lowerBounds ::= c v.state.upperBounds.foreach(ub => constrainImpl(c, ub)) case Conj(i, u, Nil) => (conj.i, conj.u) match case (_, Union(N, Nil)) => // raise(ErrorReport(msg"Cannot solve ${conj.i.toString()} ∧ ¬⊥" -> N :: Nil)) cctx.err case (Inter(S(ClassLikeType(cls1, targs1))), Union(f, ClassLikeType(cls2, targs2) :: rest)) => if cls1.uid === cls2.uid then targs1.zip(targs2).foreach: (ta1, ta2) => constrainArgs(ta1, ta2) else constrainConj(Conj(conj.i, Union(f, rest), Nil)) case (int: Inter, Union(f, _ :: rest)) => constrainConj(Conj(int, Union(f, rest), Nil)) case (Inter(S(FunType(args1, ret1, eff1))), Union(S(FunType(args2, ret2, eff2)), Nil)) => if args1.length =/= args2.length then // raise(ErrorReport(msg"Cannot constrain ${conj.i.toString()} <: ${conj.u.toString()}" -> N :: Nil)) cctx.err else args1.zip(args2).foreach { case (a1, a2) => constrainImpl(a2, a1) } constrainImpl(ret1, ret2) constrainImpl(eff1, eff2) case _ => // raise(ErrorReport(msg"Cannot solve ${conj.i.toString()} <: ${conj.u.toString()}" -> N :: Nil)) cctx.err private def constrainDNF(disj: Disj)(using InvalCtx, CCtx, TL): Unit = disj.conjs.foreach(constrainConj(_)) private def constrainArgs(lhs: TypeArg, rhs: TypeArg)(using InvalCtx, CCtx, TL): Unit = constrainImpl(rhs.negPart, lhs.negPart) constrainImpl(lhs.posPart, rhs.posPart) private def inlineSkolemBounds(ty: Type, pol: Bool)(using cache: Set[Uid[InfVar]]): Type = ty.toBasic match case v @ InfVar(_, uid, state, skolem) if skolem && !cache(uid) => given Set[Uid[InfVar]] = cache + uid inlineSkolemBounds(if pol then state.upperBounds.foldLeft[Type](v)(_ & _) else state.lowerBounds.foldLeft[Type](v)(_ | _), pol) case ComposedType(lhs, rhs, p) => ComposedType(inlineSkolemBounds(lhs, pol), inlineSkolemBounds(rhs, pol), p) case NegType(ty) => NegType(inlineSkolemBounds(ty, !pol)) case _: ClassLikeType | _: FunType | _: InfVar | Top | Bot => ty private def constrainImpl(lhs: Type, rhs: Type)(using InvalCtx, CCtx, TL): Unit = val p = lhs.toDnf -> rhs.toDnf if cctx.cache(p) then log(s"Cached!") else trace(s"CONSTRAINT ${lhs.showDbg} <: ${rhs.showDbg}"): cctx.nest(p) givenIn: val ty = dnf(inlineSkolemBounds(lhs & rhs.!, true)(using Set.empty)) constrainDNF(ty) def constrain(lhs: Type, rhs: Type)(using InvalCtx, CCtx, TL): Unit = constrainImpl(lhs, rhs) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala ================================================ package hkmc2 package invalml import scala.collection.mutable.{HashSet, HashMap, ListBuffer} import scala.annotation.tailrec import mlscript.utils.*, shorthands.* import utils.* import Message.MessageContext import semantics.*, Term.*, ucs.FlatPattern import Elaborator.Ctx import syntax.* import Tree.* import utils.Scope object InfVarUid extends Uid.Handler[InfVar] final case class InvalCtx( raise: Raise, ctx: Ctx, parent: Option[InvalCtx], lvl: Int, env: HashMap[Uid[Symbol], GeneralType], outRegAcc: Type, symbolCache: HashMap[Str, TypeSymbol], ): def +=(p: Symbol -> GeneralType): Unit = env += p._1.uid -> p._2 def get(sym: Symbol): Option[GeneralType] = env.get(sym.uid) orElse parent.dlof(_.get(sym))(None) def getCls(name: Str): TypeSymbol = symbolCache.getOrElseUpdate(name, ctx.get(name).get.symbol.get.asTpe.get) def &=(p: (Symbol, Type, InfVar)): Unit = env += p._1.uid -> InvalCtx.varTy(p._2, p._3)(using this) def nest: InvalCtx = copy(parent = Some(this), env = HashMap.empty) def nextLevel: InvalCtx = copy(parent = Some(this), lvl = lvl + 1, env = HashMap.empty) def nestReg(reg: InfVar): InvalCtx = copy(parent = Some(this), lvl = lvl + 1, env = HashMap.empty, outRegAcc = outRegAcc | reg) def nestWithOuter(outer: InfVar): InvalCtx = copy(parent = Some(this), lvl = lvl + 1, env = HashMap.empty, outRegAcc = outRegAcc | outer) def getRegEnv: Type = outRegAcc def invalctx(using ctx: InvalCtx): InvalCtx = ctx given (using ctx: InvalCtx): Raise = ctx.raise object InvalCtx: def unitTy(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Unit"), Nil) def intTy(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Int"), Nil) def numTy(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Num"), Nil) def strTy(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Str"), Nil) def boolTy(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Bool"), Nil) def errTy(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Error"), Nil) private def codeBaseTy(ct: TypeArg, cr: TypeArg, isVar: TypeArg)(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("CodeBase"), ct :: cr :: isVar :: Nil) def codeTy(ct: Type, cr: Type)(using ctx: InvalCtx): Type = codeBaseTy(Wildcard.out(ct), Wildcard.out(cr), Wildcard.out(Top)) def varTy(ct: Type, cr: Type)(using ctx: InvalCtx): Type = codeBaseTy(ct, Wildcard(cr, cr), Wildcard.out(Bot)) def regionTy(sk: Type)(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Region"), Wildcard.out(sk) :: Nil) def refTy(ct: Type, sk: Type)(using ctx: InvalCtx): Type = ClassLikeType(ctx.getCls("Ref"), Wildcard(ct, ct) :: Wildcard.out(sk) :: Nil) def init(raise: Raise)(using Elaborator.State, Elaborator.Ctx): InvalCtx = new InvalCtx(raise, summon, None, 1, HashMap.empty, Bot, HashMap.empty) val builtinOps = Elaborator.binaryOps ++ Elaborator.unaryOps ++ Elaborator.aliasOps.keySet end InvalCtx class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): import tl.{trace, log} private val infVarState = new InfVarUid.State() private val solver = new ConstraintSolver(infVarState, elState, tl) // A temporary solution for ADT matching exhausive checking // `adtCtors` maps IDs of ADTs to their constructors' IDs // `adtParent` maps constructors' IDs to the ADT class symbol they belong to // `typeNames` maintains all type names // since we need to reject all non-variable patterns in ADT match for now private val adtCtors = HashMap.empty[Uid[Symbol], ListBuffer[Uid[Symbol]]] private val adtParent = HashMap.empty[Uid[Symbol], Symbol] private val typeNames = HashSet.empty[Str] private def freshSkolem(sym: Symbol, hint: Str = "")(using ctx: InvalCtx): InfVar = InfVar(ctx.lvl, infVarState.nextUid, new VarState(), true)(sym, hint) private def freshVar(sym: Symbol, hint: Str = "")(using ctx: InvalCtx): InfVar = InfVar(ctx.lvl, infVarState.nextUid, new VarState(), false)(sym, hint) private def freshWildcard(sym: Symbol)(using ctx: InvalCtx) = val in = freshVar(sym, "") val out = freshVar(sym, "") // in.state.upperBounds ::= out // * Not needed for soundness; complicates inferred types Wildcard(in, out) private def freshReg(sym: Symbol)(using ctx: InvalCtx) = val state = new VarState() state.upperBounds = ctx.getRegEnv.! :: Nil InfVar(ctx.lvl + 1, infVarState.nextUid, state, true)(sym, "") private def freshOuter(sym: Symbol)(using ctx: InvalCtx): InfVar = InfVar(ctx.lvl + 1, infVarState.nextUid, new VarState(), true)(sym, "") private def freshEnv(sym: Symbol)(using ctx: InvalCtx): InfVar = val state = new VarState() state.upperBounds = ctx.getRegEnv :: Nil state.lowerBounds = ctx.getRegEnv :: Nil InfVar(ctx.lvl, infVarState.nextUid, state, false)(sym, "") private def error(msg: Ls[Message -> Opt[Loc]], extraInfo: => Opt[Any] = N)(using InvalCtx) = raise(ErrorReport(msg, extraInfo = extraInfo)) Bot // TODO: error type? private def addADTCtor(pSym: Symbol, cSym: Symbol) = if !adtCtors.keySet(pSym.uid) then adtCtors += pSym.uid -> ListBuffer(cSym.uid) else adtCtors(pSym.uid) += cSym.uid adtParent += cSym.uid -> pSym typeNames.add(pSym.nme) typeNames.add(cSym.nme) private def typeAndSubstType (ty: Term, pol: Bool)(using map: Map[Uid[Symbol], TypeArg])(using ctx: InvalCtx, cctx: CCtx) : GeneralType = trace[GeneralType](s"${ctx.lvl}. Typing type ${ty.showDbg}", r => s"~> ${r.showDbg}"): def mono(ty: Term, pol: Bool): Type = monoOrErr(typeAndSubstType(ty, pol), ty) ty match case Ref(sym: LocalSymbol) => log(s"Type lookup: ${sym.nme} ${sym.uid} ${map.keySet}") map.get(sym.uid) match case Some(Wildcard(in, out)) => if pol then out else in case Some(ty: Type) => ty case N => ctx.get(sym) match case Some(ty) => ty case _ => error(msg"Variable not found: ${sym.nme}" -> ty.toLoc :: Nil) case FunTy(Term.Tup(params), ret, eff) => PolyFunType(params.map { case Fld(_, p, _) => typeAndSubstType(p, !pol) }, typeAndSubstType(ret, pol), eff.map(e => typeAndSubstType(e, pol) match { case t: Type => t case _ => error(msg"Effect cannot be polymorphic." -> ty.toLoc :: Nil) }).getOrElse(Bot)) case f @ Term.Forall(tvs, outer, body) => val outVar = freshOuter(outer.getOrElse(new TempSymbol(S(f), "outer")))(using ctx) val nestCtx = ctx.nestWithOuter(outVar) outer.foreach(sym => nestCtx += sym -> outVar) given InvalCtx = nestCtx genPolyType(tvs, outVar, typeAndSubstType(body, pol)) case Term.TyApp(cls, targs) => // log(s"Type application: ${cls.nme} with ${targs}") cls.symbol.flatMap(_.asTpe) match case S(tpeSym) => if tpeSym.nme === "Any" then Top // FIXME hygiene else if tpeSym.nme === "Nothing" then Bot // FIXME hygiene else val defn = tpeSym.defn.get if targs.length != defn.tparams.length then error(msg"Type arguments do not match class definition" -> ty.toLoc :: Nil) val ts = defn.tparams.lazyZip(targs).map: (tp, t) => t match case Term.WildcardTy(in, out) => Wildcard( in.map(t => mono(t, !pol)).getOrElse(Bot), out.map(t => mono(t, pol)).getOrElse(Top) ) case _ => val ta = mono(t, pol) tp.vce match case S(false) => Wildcard.in(ta) case S(true) => Wildcard.out(ta) case N => ta ClassLikeType(tpeSym, ts) case N => error(msg"Not a valid class: ${cls.describe}" -> cls.toLoc :: Nil) case Neg(rhs) => mono(rhs, !pol).! case CompType(lhs, rhs, pol) => Type.mkComposedType(typeMonoType(lhs), typeMonoType(rhs), pol) case UnitVal() => InvalCtx.unitTy case _ => ty.symbol.flatMap(_.asTpe) match case S(cls: (ClassSymbol | TypeAliasSymbol)) => typeAndSubstType(Term.TyApp(ty, Nil)(N), pol) case N => error(msg"Invalid type" -> ty.toLoc :: Nil, S(ty)) // TODO private def genPolyType(tvs: Ls[QuantVar], outer: InfVar, body: => GeneralType)(using ctx: InvalCtx, cctx: CCtx) = val bds = tvs.map: case qv @ QuantVar(sym, ub, lb) => val tv = freshVar(sym) ctx += sym -> tv // TODO: a type var symbol may be better... tv -> qv bds.foreach: case (tv, QuantVar(_, ub, lb)) => ub.foreach(ub => tv.state.upperBounds ::= typeMonoType(ub)) lb.foreach(lb => tv.state.lowerBounds ::= typeMonoType(lb)) val lbty = tv.state.lowerBounds.foldLeft[Type](Bot)(_ | _) val ubty = tv.state.upperBounds.foldLeft[Type](Top)(_ & _) constrain(lbty, ubty) PolyType(bds.map(_._1), S(outer), body) private def typeMonoType(ty: Term)(using ctx: InvalCtx, cctx: CCtx): Type = monoOrErr(typeType(ty), ty) private def typeType(ty: Term)(using ctx: InvalCtx, cctx: CCtx): GeneralType = typeAndSubstType(ty, pol = true)(using Map.empty) private def instantiate(ty: PolyType)(using ctx: InvalCtx): GeneralType = ty.instantiate(infVarState.nextUid, freshEnv(new TempSymbol(N, "env")), ctx.lvl)(tl) private def extrude(ty: GeneralType)(using ctx: InvalCtx, pol: Bool, cctx: CCtx): GeneralType = ty match case ty: Type => solver.extrude(ty)(using ctx.lvl, pol, HashMap.empty) case PolyType(tvs, outer, body) => PolyType(tvs, outer, extrude(body)) case pf @ PolyFunType(args, ret, eff) => PolyFunType(args.map(extrude(_)(using ctx, !pol)), extrude(ret), solver.extrude(eff)(using ctx.lvl, pol, HashMap.empty)) private def constrain(lhs: Type, rhs: Type)(using ctx: InvalCtx, cctx: CCtx): Unit = solver.constrain(lhs, rhs) private def typeCode(code: Term)(using ctx: InvalCtx, scope: Scope): (Type, Type, Type) = given CCtx = CCtx.init(code, N) code match case UnitVal() => (Top, Bot, Bot) case Lit(lit) => ((lit match // TODO dedup with other `case Lit(lit)` case _: IntLit => InvalCtx.intTy case _: DecLit => InvalCtx.numTy case _: StrLit => InvalCtx.strTy case _: UnitLit => InvalCtx.unitTy case _: BoolLit => InvalCtx.boolTy), Bot, Bot) case Ref(sym: Symbol) if sym.nme === "error" => (Bot, Bot, Bot) case Ref(sym: Symbol) if InvalCtx.builtinOps(sym.nme) => ctx.get(sym) match case S(ty) => (tryMkMono(ty, code), Bot, Bot) case N => (error(msg"Cannot quote operator ${sym.nme}" -> code.toLoc :: Nil), Bot, Bot) case f @ Lam(PlainParamList(params), body) => val nestCtx = ctx.nextLevel given InvalCtx = nestCtx val bds = params.map: case Param(sym = sym) => val tv = freshVar(sym) val sk = freshSkolem(sym) nestCtx &= (sym, tv, sk) (tv, sk) val (bodyTy, ctxTy, eff) = typeCode(body) val res = freshVar(new TempSymbol(S(f), "ctx"))(using ctx) constrain(ctxTy, bds.foldLeft[Type](res)((res, bd) => res | bd._2)) (FunType(bds.map(_._1), bodyTy, Bot), res, eff) case app @ Term.App(lhs, Term.Tup(rhs)) => val (lhsTy, lhsCtx, lhsEff) = typeCode(lhs) val (rhsTy, rhsCtx, rhsEff) = rhs.foldLeft[(Ls[Type], Type, Type)]((Nil, Bot, Bot)): case (res, p: Fld) => val (ty, ctx, eff) = typeCode(p.term) (ty :: res._1, res._2 | ctx, res._3 | eff) val resTy = freshVar(new TempSymbol(S(app), "app")) constrain(lhsTy, FunType(rhsTy.reverse, resTy, Bot)) // TODO: right (resTy, lhsCtx | rhsCtx, lhsEff | rhsEff) case sel @ Term.SynthSel(Term.Ref(_: TopLevelSymbol), _) if sel.symbol.isDefined => val (opTy, eff) = typeCheck(Ref(sel.symbol.get)(sel.nme, 666, N)) // FIXME 666 (tryMkMono(opTy, sel), Bot, eff) case unq @ Term.Unquoted(body) => val (ty, eff) = typeCheck(body) val tv = freshVar(new TempSymbol(S(unq), "cde")) val cr = freshVar(new TempSymbol(S(unq), "ctx")) constrain(tryMkMono(ty, body), InvalCtx.codeTy(tv, cr)) (tv, cr, eff) case blk @ Term.Blk(LetDecl(sym, _) :: DefineVar(sym2, rhs) :: Nil, body) if sym2 is sym => // TODO: more than one!! val (rhsTy, rhsCtx, rhsEff) = typeCode(rhs)(using ctx) val nestCtx = ctx.nextLevel given InvalCtx = nestCtx val sk = freshSkolem(sym) nestCtx &= (sym, rhsTy, sk) val (bodyTy, bodyCtx, bodyEff) = typeCode(body) val res = freshVar(new TempSymbol(S(blk), "ctx"))(using ctx) constrain(bodyCtx, sk | res) (bodyTy, rhsCtx | res, rhsEff | bodyEff) case Term.IfLike(_, IfLikeForm.ReturningIf, SimpleSplit.IfThenElse(cond, cons, alts)) => val (condTy, condCtx, condEff) = typeCode(cond) val (consTy, consCtx, consEff) = typeCode(cons) val (altsTy, altsCtx, altsEff) = typeCode(alts) constrain(condTy, InvalCtx.boolTy) (consTy | altsTy, condCtx | consCtx | altsCtx, condEff | consEff | altsEff) case _ => (error(msg"Cannot quote ${code.toString}" -> code.toLoc :: Nil), Bot, Bot) private def typeFunDef(sym: Symbol, lam: Term, sig: Opt[Term])(using ctx: InvalCtx, cctx: CCtx, scope: Scope) = lam match case Term.Lam(params, body) => sig match case S(sig) => val sigTy = typeType(sig)(using ctx) ctx += sym -> sigTy ascribe(lam, sigTy) () case N => val outer = freshOuter(new TempSymbol(S(lam), "outer"))(using ctx) given InvalCtx = ctx.nestWithOuter(outer) val funTyV = freshVar(sym) ctx += sym -> funTyV // for recursive functions val (res, _) = typeCheck(lam) val funTy = tryMkMono(res, lam) given CCtx = CCtx.init(lam, N) constrain(funTy, funTyV)(using ctx) ctx += sym -> PolyType.generalize(funTy, S(outer), ctx.lvl + 1) case _ => error(msg"Function definition shape not yet supported for ${sym.nme}" -> lam.toLoc :: Nil) // Check if a given matching expression is matching on an ADT. // An `if` expression can only matching on one ADT and patterns can only carry variables so far. // It is a temporary solution to ADTs. private def isADTMatch(split: Split)(using InvalCtx) = def rec(split: Split, acc: Either[Opt[Symbol], Unit]): Bool = split match case Split.Cons(Branch(_, pattern, _), alts) => pattern match case FlatPattern.ClassLike(_, sym, _, _) if adtParent.keySet(sym.uid) => acc match case L(N) => rec(alts, L(S(sym))) case L(S(other)) if adtParent.get(other.uid).exists(p => p.uid == adtParent(sym.uid).uid) => rec(alts, L(S(sym))) case R(_) => error(msg"Mixing ADT pattern matching and general matching is not supported yet." -> split.toLoc :: Nil) false case _ => acc match case L(S(_)) => error(msg"Mixing ADT pattern matching and general matching is not supported yet." -> split.toLoc :: Nil) false case _ => rec(alts, R(())) case Split.Let(_, _, tail) => rec(tail, acc) case _ => acc match case L(S(sym)) => true case _ => false rec(split, L(N)) // Type check ADT matching, which also returns mentioned constructors for exhaustive checking. // No GADT reasoning. // It is a temporary solution to ADTs. private def typeADTMatch (split: Split, sign: Opt[GeneralType])(using ctx: InvalCtx)(using CCtx, Scope) : (GeneralType, Type, Ls[Symbol], Bool) = split match case Split.Cons(Branch(scrutinee, pattern, cons), alts) => val (scrutineeTy, scrutineeEff) = typeCheck(scrutinee) val map = HashMap[Uid[Symbol], TypeArg]() pattern match case FlatPattern.ClassLike(_, sym, paramsOpt, _) => val clsTy = adtParent.get(sym.uid).flatMap(_.asCls.flatMap(_.defn)) match case S(cls) => ClassLikeType(cls.sym, cls.tparams.map(_ => freshWildcard(sym))) case _ => error(msg"Cannot match ${scrutinee.toString} as ${sym.toString}" -> split.toLoc :: Nil) Bot constrain(tryMkMono(scrutineeTy, scrutinee), clsTy) val (paramList, tps, isGeneric, ext) = sym.asCls.flatMap(_.defn) match case S(clsDef) => val isGeneric = clsDef.annotations.exists { case Annot.Modifier(syntax.Keyword.data) => true case _ => false } val ext = clsDef.ext match case S(Term.New(p: Term, _, _)) => p case _ => Term.Error (clsDef.paramsOpt.map(p => p.params).getOrElse(Nil), clsDef.tparams, isGeneric, ext) case N => error(msg"${sym.toString} is not a valid constructor." -> split.toLoc :: Nil) (Nil, Nil, false, Term.Error) val params = paramsOpt.getOrElse(Nil) if params.length != paramList.length then error(msg"${sym.toString} is not a valid constructor." -> split.toLoc :: Nil) (Bot, Bot, sym :: Nil, false) else val nestCtx = if isGeneric then ctx.nextLevel else ctx.nest tps.foreach { case TyParam(_, _, targ) => val ty = if isGeneric then freshVar(targ)(using nestCtx) else freshWildcard(targ)(using nestCtx) map += targ.uid -> ty } if !isGeneric then // no GADT reasoning so far constrain(clsTy, tryMkMono(typeAndSubstType(ext, true)(using map.toMap), scrutinee)) params.iterator.zip(paramList).foreach: case (p, Param(_, _, S(ty), _)) => nestCtx += p._1 -> typeAndSubstType(ty, true)(using map.toMap) val (consTy, consEff) = typeAllSplits(cons, sign)(using nestCtx) val (altsTy, altsEff, altCases, fallback) = typeADTMatch(alts, sign) val allEff = scrutineeEff | (consEff | altsEff) (sign.getOrElse(tryMkMono(consTy, cons) | tryMkMono(altsTy, alts)), allEff, sym :: altCases, fallback) case Split.Let(name, term, tail) => val nestCtx = ctx.nest given InvalCtx = nestCtx val (termTy, termEff) = typeCheck(term) nestCtx += name -> termTy val (tailTy, tailEff, cases, fallback) = typeADTMatch(tail, sign)(using nestCtx) (tailTy, termEff | tailEff, cases, fallback) case Split.Else(alts) => sign match case S(sign) => val (ty, res) = ascribe(alts, sign) (ty, res, Nil, true) case _ => val (ty, res) = typeCheck(alts) (ty, res, Nil, true) case Split.End => (Bot, Bot, Nil, false) private def typeSplit (split: Split, sign: Opt[GeneralType])(using ctx: InvalCtx)(using CCtx, Scope) : (GeneralType, Type) = split match case Split.Cons(Branch(scrutinee, pattern, cons), alts) => val (scrutineeTy, scrutineeEff) = typeCheck(scrutinee) val nestCtx1 = ctx.nest val nestCtx2 = ctx.nest val patTy = pattern match case pat: FlatPattern.ClassLike => pat.constructor.symbol.flatMap(_.asCls) match case S(sym) => val (clsTy, tv, emptyTy) = sym.defn.map(sym -> _) match case S((sym, cls)) => (ClassLikeType(sym, cls.tparams.map(_ => freshWildcard(sym))), (freshVar(new TempSymbol(S(scrutinee), "scrut"))), ClassLikeType(sym, cls.tparams.map(_ => Wildcard.empty))) case _ => error(msg"Cannot match ${scrutinee.toString} as ${sym.toString}" -> split.toLoc :: Nil) (Bot, Bot, Bot) scrutinee match // * refine case Ref(sym: LocalSymbol) => nestCtx1 += sym -> clsTy nestCtx2 += sym -> tv case _ => () // TODO: refine all variables holding this value? clsTy | (tv & Type.mkNegType(emptyTy)) case N => error(msg"Not a valid class: ${pat.constructor.describe}" -> pat.constructor.toLoc :: Nil) Bot case FlatPattern.Lit(lit) => lit match case _: Tree.BoolLit => InvalCtx.boolTy case _: Tree.IntLit => InvalCtx.intTy case _: Tree.DecLit => InvalCtx.numTy case _: Tree.StrLit => InvalCtx.strTy case _: Tree.UnitLit => InvalCtx.unitTy constrain(tryMkMono(scrutineeTy, scrutinee), patTy) val (consTy, consEff) = typeSplit(cons, sign)(using nestCtx1) val (altsTy, altsEff) = typeSplit(alts, sign)(using nestCtx2) val allEff = scrutineeEff | (consEff | altsEff) (sign.getOrElse(tryMkMono(consTy, cons) | tryMkMono(altsTy, alts)), allEff) case Split.Let(name, term, tail) => val nestCtx = ctx.nest given InvalCtx = nestCtx val (termTy, termEff) = typeCheck(term) nestCtx += name -> termTy val (tailTy, tailEff) = typeSplit(tail, sign)(using nestCtx) (tailTy, termEff | tailEff) case Split.Else(alts) => sign match case S(sign) => ascribe(alts, sign) case _ => typeCheck(alts) case Split.End => (Bot, Bot) private def typeAllSplits (split: Split, sign: Opt[GeneralType])(using ctx: InvalCtx)(using CCtx, Scope) : (GeneralType, Type) = if isADTMatch(split) then val (res, eff, cases, fallback) = typeADTMatch(split, sign) if !fallback then cases match // A primitive exhaustive check case c :: rest => // previous check already guarantees that all cases belong to the same ADT. adtParent.get(c.uid).flatMap(p => adtCtors.get(p.uid)) match case S(ctors) => val dist = cases.map(_.uid).distinct if dist.length < cases.length then error(msg"Duplicate match branches." -> split.toLoc :: Nil) if dist.length != ctors.length then error(msg"Expect ${ctors.length.toString()} cases, but ${dist.length.toString()} got." -> split.toLoc :: Nil) case N => error(msg"Unknown ADT constructor ${c.nme}" -> split.toLoc :: Nil) case Nil => ??? // impossible (res, eff) else typeSplit(split, sign) // * Note: currently, the returned type is not used or useful, but it could be in the future private def ascribe(lhs: Term, rhs: GeneralType)(using ctx: InvalCtx, scope: Scope): (GeneralType, Type) = trace[(GeneralType, Type)](s"${ctx.lvl}. Ascribing ${lhs.showDbg} : ${rhs.showDbg}", res => s"! ${res._2.showDbg}"): given CCtx = CCtx.init(lhs, S(rhs)) (lhs, rhs) match case (Term.Lam(PlainParamList(params), body), ft @ PolyFunType(args, ret, eff)) => // * annoted functions if params.length != args.length then (error(msg"Cannot type this ${lhs.describe} as ${rhs.show}" -> lhs.toLoc :: Nil), Bot) else val nestCtx = ctx.nest val argsTy = params.zip(args).map: case (Param(sym = sym), ty) => nestCtx += sym -> ty ty given InvalCtx = nestCtx val (_, effTy) = ascribe(body, ret) constrain(effTy, eff) (ft, Bot) case (Term.Lam(params, body), ft @ FunType(args, ret, eff)) => ascribe(lhs, PolyFunType(args, ret, eff)) case (term, pt @ PolyType(_, outer, _)) => // * generalize val nextCtx = outer match case S(outer) => ctx.nestWithOuter(outer) case N => ctx.nextLevel given InvalCtx = nextCtx constrain(ascribe(term, skolemize(pt))._2, Bot) // * never generalize terms with effects (pt, Bot) case (Term.IfLike(_, IfLikeForm.ReturningIf, split), ty) => // * propagate typeAllSplits(split.getExpandedSplit, S(ty)) case (Term.Asc(term, ty), rhs) => ascribe(term, typeType(ty)) ascribe(term, rhs) case _ => val (lhsTy, eff) = typeCheck(lhs) rhs match case pf: PolyFunType if pf.isPoly => (error(msg"Cannot type non-function term ${lhs.toString} as ${rhs.show}" -> lhs.toLoc :: Nil), Bot) case _ => constrain(tryMkMono(lhsTy, lhs), monoOrErr(rhs, lhs)) (rhs, eff) // TODO: t -> loc when toLoc is implemented private def app(lhs: (GeneralType, Type), rhs: Ls[Elem], t: Term) (using ctx: InvalCtx)(using CCtx, Scope) : (GeneralType, Type) = lhs match case (PolyFunType(params, ret, eff), lhsEff) => // * if the function type is known, we can directly use it if params.length != rhs.length then (error(msg"Incorrect number of arguments" -> t.toLoc :: Nil), Bot) else var resEff: Type = lhsEff | eff rhs.lazyZip(params).foreach: case (f: Fld, t) => val (ty, ef) = ascribe(f.term, t) resEff |= ef (ret, resEff) case (FunType(params, ret, eff), lhsEff) => app((PolyFunType(params, ret, eff), lhsEff), rhs, t) case (ty: PolyType, eff) => app((instantiate(ty), eff), rhs, t) case (funTy, lhsEff) => val (argTy, argEff) = rhs.flatMap: case f: Fld => val (ty, eff) = typeCheck(f.term) Left(ty) :: Right(eff) :: Nil .partitionMap(x => x) val effVar = freshVar(new TempSymbol(S(t), "eff")) val retVar = freshVar(new TempSymbol(S(t), "app")) constrain(tryMkMono(funTy, t), FunType(argTy.map((tryMkMono(_, t))), retVar, effVar)) (retVar, argEff.foldLeft[Type](effVar | lhsEff)((res, e) => res | e)) private def skolemize(ty: PolyType)(using ctx: InvalCtx) = ty.skolemize(infVarState.nextUid, ctx.lvl)(tl) // TODO: implement toLoc private def monoOrErr(ty: GeneralType, sc: Located)(using InvalCtx) = ty.monoOr(error(msg"General type is not allowed here." -> sc.toLoc :: Nil)) // * Try to instantiate the given type if it is forall quantified private def tryMkMono(ty: GeneralType, sc: Located)(using InvalCtx, Scope): Type = ty match case pt: PolyType => tryMkMono(instantiate(pt), sc) case ft: PolyFunType => ft.monoOr(error(msg"Expected a monomorphic type or an instantiable type here, but ${ty.show} found" -> sc.toLoc :: Nil)) case ty: Type => ty private def createADTCtor(clsDef: ClassDef, resTy: Term)(using ctx: InvalCtx, scope: Scope, cctx: CCtx) = val nestCtx = ctx.nextLevel given InvalCtx = nestCtx val map = HashMap[Uid[Symbol], TypeArg]() val isGeneric = clsDef.annotations.exists { case Annot.Modifier(syntax.Keyword.data) => true case _ => false } val targs = clsDef.tparams.map { case TyParam(_, vce, targ) => val ty = vce match case S(v) => val tv = freshVar(targ) if v then Wildcard.out(tv) else Wildcard.in(tv) case _ => if isGeneric then freshVar(targ) else freshWildcard(targ) map += targ.uid -> ty ty } addADTCtor(clsDef.ext.flatMap(n => n.cls.symbol).getOrElse(???), clsDef.sym) clsDef match case clsDef: ClassDef.Plain => ctx += clsDef.bsym -> typeAndSubstType(resTy, true)(using map.toMap) case clsDef: ClassDef.Parameterized => if clsDef.tparams.isEmpty then ctx += clsDef.bsym -> PolyFunType(clsDef.params.params.map { case Param(_, _, S(ty), _) => typeType(ty) case p => error(msg"Invalid ADT parameter." -> p.toLoc :: Nil) Bot }, typeAndSubstType(resTy, true)(using map.toMap), Bot) else ctx += clsDef.bsym -> PolyType(targs.flatMap { case Wildcard(in: InfVar, out: InfVar) => in :: out :: Nil case Wildcard(in: InfVar, _) => in :: Nil case Wildcard(_, out: InfVar) => out :: Nil case v: InfVar => v :: Nil }, N, PolyFunType(clsDef.params.params.map { case Param(_, _, S(ty), _) => typeAndSubstType(ty, true)(using map.toMap) case p => error(msg"Invalid ADT parameter." -> p.toLoc :: Nil) Bot }, typeAndSubstType(resTy, true)(using map.toMap), Bot)) private def typeCheck(t: Term)(using ctx: InvalCtx, scope: Scope): (GeneralType, Type) = trace[(GeneralType, Type)](s"${ctx.lvl}. Typing ${t.showDbg}", res => s": (${res._1.showDbg}, ${res._2.showDbg})"): given CCtx = CCtx.init(t, N) t match case Term.Annotated(Annot.Untyped, _) => (Bot, Bot) case sel @ Term.SynthSel(Ref(_: TopLevelSymbol), nme) if sel.symbol.isDefined => typeCheck(Ref(sel.symbol.get)(sel.nme, 666, N)) // FIXME 666 case Ref(sym) => ctx.get(sym) match case Some(ty) => (ty, Bot) case _ => // (error(msg"Variable not found: ${sym.nme} (${sym.toString} @ ${sym.uid.toString})" (error(msg"Variable not found: ${sym.nme}" -> t.toLoc :: Nil), Bot) case Blk(stats, res) => val effBuff = ListBuffer.empty[Type] def goStats(stats: Ls[Statement]): Unit = stats match case Nil => () case (term: Term) :: stats => effBuff += typeCheck(term)._2 goStats(stats) case LetDecl(sym, _) :: DefineVar(sym2, rhs) :: stats => require(sym2 is sym) val (rhsTy, eff) = typeCheck(rhs) effBuff += eff ctx += sym -> rhsTy goStats(stats) case (td @ TermDefinition(k = Fun, params = ps :: Nil, sign = sig, body = S(body))) :: stats => typeFunDef(td.sym, Term.Lam(ps, body), sig) goStats(stats) case (td @ TermDefinition(k = Fun, params = Nil, sign = sig, body = S(body))) :: stats => typeFunDef(td.sym, body, sig) // * may be a case expressions goStats(stats) case (td1 @ TermDefinition(k = Fun, sign = S(sig), body = None)) :: (td2 @ TermDefinition(k = Fun, body = S(body))) :: stats if td1.sym === td2.sym => goStats(td2 :: stats) // * avoid type check signatures twice case (td @ TermDefinition(k = Fun, sign = S(sig), body = None)) :: stats => ctx += td.sym -> typeType(sig) goStats(stats) case (clsDef: ClassDef) :: stats => typeNames.add(clsDef.sym.nme) clsDef.ext match case S(Term.New(ty, _, N)) => createADTCtor(clsDef, ty) case _ => () goStats(stats) case (modDef: ModuleOrObjectDef) :: stats => typeNames.add(modDef.sym.nme) goStats(stats) case Import(sym, str, pth) :: stats => goStats(stats) // TODO: case stat :: _ => TODO(stat) goStats(stats) val (ty, eff) = typeCheck(res) (ty, effBuff.foldLeft(eff)((res, e) => res | e)) case UnitVal() => (InvalCtx.unitTy, Bot) case Lit(lit) => ((lit match case _: IntLit => InvalCtx.intTy case _: DecLit => InvalCtx.numTy case _: StrLit => InvalCtx.strTy case _: UnitLit => InvalCtx.unitTy case _: BoolLit => InvalCtx.boolTy), Bot) case Lam(PlainParamList(params), body) => val nestCtx = ctx.nest given InvalCtx = nestCtx val tvs = params.map: case Param(_, sym, sign, _) => val ty = sign.map(s => typeType(s)(using nestCtx)).getOrElse(freshVar(sym)) nestCtx += sym -> ty ty val (bodyTy, eff) = typeCheck(body) (PolyFunType(tvs, bodyTy, eff), Bot) case Term.SelProj(term, cls, field) => val (ty, eff) = typeCheck(term) cls.symbol.flatMap(_.asCls.flatMap(sym => sym.defn.map(sym -> _))) match case S(clsSym -> clsDfn) => val map = HashMap[Uid[Symbol], TypeArg]() val targs = clsDfn.tparams.map { case TyParam(_, _, targ) => val ty = freshWildcard(targ) map += targ.uid -> ty ty } constrain(tryMkMono(ty, term), ClassLikeType(clsSym, targs)) require(clsDfn.paramsOpt.forall(_.restParam.isEmpty)) (clsDfn.paramsOpt.fold(Nil)(_.params).map { case Param(_, sym, sign, _) => if sym.nme === field.name then sign else N }.filter(_.isDefined)) match case S(res) :: Nil => (typeAndSubstType(res, pol = true)(using map.toMap), eff) case _ => (error(msg"${field.name} is not a valid member in class ${clsSym.nme}" -> t.toLoc :: Nil), Bot) case N => (error(msg"Not a valid class: ${cls.describe}" -> cls.toLoc :: Nil), Bot) case t @ Term.App(lhs, Term.Tup(rhs)) => val (funTy, lhsEff) = typeCheck(lhs) app((funTy, lhsEff), rhs, t) case Term.New(cls, args, N) => cls.symbol.flatMap(_.asCls.flatMap(_.defn)) match case S(clsDfn: ClassDef.Parameterized) => require(clsDfn.paramsOpt.forall(_.restParam.isEmpty)) val argsList = args match case Nil => Nil case Term.Tup(elems) :: Nil => elems.map: case PlainFld(term) => term case _ => ??? case _ => ??? if argsList.length != clsDfn.params.params.length then (error(msg"The number of parameters is incorrect" -> t.toLoc :: Nil), Bot) else val map = HashMap[Uid[Symbol], TypeArg]() val targs = clsDfn.tparams.map { case TyParam(_, S(_), targ) => val ty = freshVar(targ) map += targ.uid -> ty ty case TyParam(_, N, targ) => // val ty = freshWildcard // FIXME probably not correct val ty = freshVar(targ) map += targ.uid -> ty ty } val effBuff = ListBuffer.empty[Type] require(clsDfn.paramsOpt.forall(_.restParam.isEmpty)) argsList.iterator.zip(clsDfn.params.params).foreach { case (arg, Param(sign = S(sign))) => val (ty, eff) = ascribe(arg, typeAndSubstType(sign, pol = true)(using map.toMap)) effBuff += eff case _ => ??? } (ClassLikeType(clsDfn.sym, targs), effBuff.foldLeft[Type](Bot)((res, e) => res | e)) case S(clsDfn: ClassDef.Plain) => ??? // TODO case N => (error(msg"Not a valid class: ${cls.describe}" -> cls.toLoc :: Nil), Bot) case Term.Asc(term, ty) => val res = typeType(ty)(using ctx) ascribe(term, res) case Term.IfLike(_, IfLikeForm.ReturningIf, split) => typeAllSplits(split.getExpandedSplit, N) case reg @ Term.Region(sym, body) => val sk = freshReg(sym)(using ctx) val nestCtx = ctx.nestReg(sk) given InvalCtx = nestCtx nestCtx += sym -> InvalCtx.regionTy(sk) val (res, eff) = typeCheck(body) val tv = freshVar(new TempSymbol(S(reg), "eff"))(using ctx) constrain(eff, tv | sk) (extrude(res)(using ctx, true), tv) case Term.RegRef(reg, value) => val (regTy, regEff) = typeCheck(reg) val (valTy, valEff) = typeCheck(value) val sk = freshVar(new TempSymbol(S(reg), "reg")) constrain(tryMkMono(regTy, reg), InvalCtx.regionTy(sk)) (InvalCtx.refTy(tryMkMono(valTy, value), sk), sk | (regEff | valEff)) case Term.SetRef(lhs, rhs) => val (lhsTy, lhsEff) = typeCheck(lhs) val (rhsTy, rhsEff) = typeCheck(rhs) val sk = freshVar(new TempSymbol(S(lhs), "reg")) constrain(tryMkMono(lhsTy, lhs), InvalCtx.refTy(tryMkMono(rhsTy, rhs), sk)) (tryMkMono(rhsTy, rhs), sk | (lhsEff | rhsEff)) case Term.Deref(ref) => val (refTy, refEff) = typeCheck(ref) val sk = freshVar(new TempSymbol(S(ref), "reg")) val ctnt = freshVar(new TempSymbol(S(ref), "ref")) constrain(tryMkMono(refTy, ref), InvalCtx.refTy(ctnt, sk)) (ctnt, sk | refEff) case Term.Quoted(body) => val nestCtx = ctx.nest given InvalCtx = nestCtx val (ty, ctxTy, eff) = typeCode(body) (InvalCtx.codeTy(ty, ctxTy), eff) case _: Term.Unquoted => (error(msg"Unquote should nest in quasiquote" -> t.toLoc :: Nil), Bot) case Throw(e) => val (ty, eff) = typeCheck(e) constrain(tryMkMono(ty, e), InvalCtx.errTy) (Bot, eff) case Term.Error => (Bot, Bot) // TODO: error type? case _ => (error(msg"Term shape not yet supported by InvalML: ${t.toString}" -> t.toLoc :: Nil), Bot) def typePurely(t: Term)(using InvalCtx, Scope): GeneralType = val (ty, eff) = typeCheck(t) given CCtx = CCtx.init(t, N) constrain(eff, Bot) ty ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/NormalForm.scala ================================================ package hkmc2 package invalml import scala.annotation.tailrec import semantics.* import Message.MessageContext import mlscript.utils.*, shorthands.* import utils.* import utils.Scope final case class Disj(conjs: Ls[Conj]) extends NormalForm with CachedBasicType: def isBot: Bool = conjs.isEmpty def mkBasic: BasicType = BasicType.union(conjs.map(_.toBasic)) def toDnf(using TL): Disj = this override def show(using Scope, InvalCtx, Raise): Str = if conjs.isEmpty then "⊥" else conjs.map(_.show).mkString(" ∨ ") override def showDbg: Str = if conjs.isEmpty then "D()" else s"D( ${conjs.map(_.showDbg).mkString(" || ")} )" object Disj: val bot: Disj = Disj(Nil) val top: Disj = Disj(Conj.empty :: Nil) sealed abstract case class Conj(i: Inter, u: Union, vars: Ls[(InfVar, Bool)]) extends NormalForm with CachedBasicType: def merge(that: Conj)(using TL): Option[Conj] = tl.traceNot[Option[Conj]](s"merge ${this.showDbg} and ${that.showDbg}", r => s"= ${r.map(_.showDbg)}"): val Conj(i1, u1, vars1) = this val Conj(i2, u2, vars2) = that i1.merge(i2) match case S(i) => val u = u1.merge(u2) val vars = (vars1 ++ vars2).sortWith { case ((InfVar(_, uid1, _, _), _), (InfVar(_, uid2, _, _), _)) => uid1 <= uid2 }.foldLeft[Opt[Ls[(InfVar, Bool)]]](S(Nil))((res, p) => (res, p) match { // * None -> bot case (N, _) => N case (S(Nil), p) => S(p :: Nil) case (S((lhs @ InfVar(v, uid1, s, k), p1) :: tail), (InfVar(_, uid2, _, _), p2)) if uid1 === uid2 => if p1 === p2 then S((InfVar(v, uid1, s, k)(lhs.sym, lhs.hint), p1) :: tail) else N case (S(head :: tail), p) => S(p :: head :: tail) }) vars match case S(vars) => S(Conj(i, u, vars)) case _ => N case N => N def mkBasic: BasicType = BasicType.inter(i.toBasic :: NegType(u.toBasic) :: vars.map{ case (tv, true) => tv case (tv, false) => NegType(tv) }) def toDnf(using TL): Disj = Disj(this :: Nil) override def show(using Scope, InvalCtx, Raise): Str = val s = ((i :: Nil).filterNot(_.isTop).map(_.show) ::: (u :: Nil).filterNot(_.isBot).map("¬{"+_.show+"}") ::: vars.map: case (tv, true) => tv.show case (tv, false) => "¬" + tv.show ).mkString(" ∧ ") if s.isEmpty then "⊤" else s override def showDbg: Str = val s = ((i :: Nil).filterNot(_.isTop).map(_.showDbg) ::: (u :: Nil).filterNot(_.isBot).map("~{"+_.showDbg+"}") ::: vars.map: case (tv, true) => tv.showDbg case (tv, false) => "~" + tv.showDbg ).mkString(" && ") if s.isEmpty then "⊤" else s object Conj: // * Conj objects cannot be created with `new` except in this file. // * This is because we want to sort the vars in the apply function. def apply(i: Inter, u: Union, vars: Ls[(InfVar, Bool)]) = new Conj(i, u, vars.sortWith { case ((v1 @ InfVar(lv1, _, _, sk1), _), (v2 @ InfVar(lv2, _, _, sk2), _)) => !(sk1 || !sk2 && lv1 <= lv2) }){} lazy val empty: Conj = Conj(Inter.empty, Union.empty, Nil) def mkVar(v: InfVar, pol: Bool) = Conj(Inter.empty, Union.empty, (v, pol) :: Nil) def mkInter(inter: ClassLikeType | FunType) = Conj(Inter(S(inter)), Union.empty, Nil) def mkUnion(union: ClassLikeType | FunType) = Conj(Inter.empty, union match { case cls: ClassLikeType => Union(N, cls :: Nil) case fun: FunType => Union(S(fun), Nil) }, Nil) // * Some(ClassType) -> C[in D_i out D_i], Some(FunType) -> D_1 ->{D_2} D_3, None -> Top final case class Inter(v: Opt[ClassLikeType | FunType]) extends NormalForm: def isTop: Bool = v.isEmpty def merge(other: Inter): Option[Inter] = (v, other.v) match case (S(ClassLikeType(cls1, targs1)), S(ClassLikeType(cls2, targs2))) if cls1.uid === cls2.uid => S(Inter(S(ClassLikeType(cls1, targs1.lazyZip(targs2).map(_ & _))))) case (S(_: ClassLikeType), S(_: ClassLikeType)) => N case (S(FunType(a1, r1, e1)), S(FunType(a2, r2, e2))) => S(Inter(S(FunType(a1.lazyZip(a2).map(_ | _), r1 & r2, e1 & e2)))) case (S(v), N) => S(Inter(S(v))) case (N, v) => S(Inter(v)) case _ => N def toBasic: BasicType = v.getOrElse(Top) def toDnf(using TL): Disj = Disj(Conj(this, Union(N, Nil), Nil) :: Nil) override def show(using Scope, InvalCtx, Raise): Str = toBasic.show override def showDbg: Str = toBasic.showDbg object Inter: lazy val empty: Inter = Inter(N) // * fun: Some(FunType) -> D_1 ->{D_2} D_3, None -> bot final case class Union(fun: Opt[FunType], cls: Ls[ClassLikeType]) extends NormalForm with CachedBasicType: def isBot = fun.isEmpty && cls.isEmpty def toType = fun.getOrElse(Bot) | cls.foldLeft[Type](Bot)(_ | _) def merge(other: Union): Union = Union((fun, other.fun) match { case (S(FunType(a1, r1, e1)), S(FunType(a2, r2, e2))) => S(FunType(a1.lazyZip(a2).map(_ & _), r1 | r2, e1 | e2)) case (S(f), N) => S(f) case (N, S(f)) => S(f) case (N, N) => N }, (cls ++ other.cls).sortWith { // * Merge the same classes case (cls1, cls2) => cls1.name.uid <= cls2.name.uid }.foldLeft[Ls[ClassLikeType]](Nil)((res, cls) => (res, cls) match { case (Nil, cls) => cls :: Nil case (ClassLikeType(cls1, targs1) :: tail, ClassLikeType(cls2, targs2)) if cls1.uid === cls2.uid => ClassLikeType(cls1, targs1.lazyZip(targs2).map(_ | _)) :: tail case (head :: tail, cls) => cls :: head :: tail })) def mkBasic: BasicType = BasicType.union(fun.toList ::: cls) def toDnf(using TL): Disj = NormalForm.neg(this) override def show(using Scope, InvalCtx, Raise): Str = toType.show override def showDbg: Str = toType.showDbg object Union: val empty: Union = Union(N, Nil) sealed abstract class NormalForm extends TypeExt: def toBasic: BasicType lazy val lvl: Int = toBasic.lvl // TODO improve: avoid inefficient use of toBasic def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = toBasic.subst def show(using Scope, InvalCtx, Raise): Str def showDbg: Str object NormalForm: def inter(lhs: Disj, rhs: Disj)(using TL): Disj = tl.traceNot[Disj](s"inter ${lhs.showDbg} and ${rhs.showDbg}", r => s"= ${r.showDbg}"): if lhs.isBot || rhs.isBot then Disj.bot else Disj(lhs.conjs.flatMap(lhs => rhs.conjs.flatMap(rhs => lhs.merge(rhs) match { case S(conj) => conj :: Nil case N => Nil }))) def union(lhs: Disj, rhs: Disj): Disj = Disj(lhs.conjs ++ rhs.conjs) def neg(ty: Type)(using TL): Disj = tl.traceNot[Disj](s"~DNF ${ty.showDbg} ${ty.getClass} ${ty.toBasic.showDbg}", r => s"= ${r.showDbg}"): ty match case u: Union => Disj(Conj(Inter(N), u, Nil) :: Nil) case _ => ty.toBasic match case Top => Disj.bot case Bot => Disj.top case v: InfVar => Disj(Conj.mkVar(v, false) :: Nil) case ct: ClassLikeType => Disj(Conj.mkUnion(ct) :: Nil) case ft: FunType => Disj(Conj.mkUnion(ft) :: Nil) case ComposedType(lhs, rhs, pol) => if pol then inter(neg(lhs), neg(rhs)) else union(neg(lhs), neg(rhs)) case NegType(ty) => dnf(ty) def dnf(ty: Type)(using TL): Disj = tl.traceNot[Disj](s"DNF ${ty.showDbg} ${ty.getClass}", r => s"= ${r.showDbg}"): ty match case d: Disj => d case c: Conj => Disj(c :: Nil) case i: Inter => Disj(Conj(i, Union(N, Nil), Nil) :: Nil) case _ => ty.toBasic match case Top => Disj.top case Bot => Disj.bot case v: InfVar => Disj(Conj.mkVar(v, true) :: Nil) case ct: ClassLikeType => Disj(Conj.mkInter(ct.toNorm) :: Nil) case ft: FunType => Disj(Conj.mkInter(ft.toNorm) :: Nil) case ComposedType(lhs, rhs, pol) => if pol then union(dnf(lhs), dnf(rhs)) else inter(dnf(lhs), dnf(rhs)) case NegType(ty) => neg(ty) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/PrettyPrinter.scala ================================================ package hkmc2 package invalml import scala.collection.mutable.{Set => MutSet, ListBuffer} import utils.Scope class PrettyPrinter(output: String => Unit)(using Scope, InvalCtx): def print(ty: GeneralType)(using Raise): Unit = ty.show match case "()" => case tyStr => output(s"Type: ${tyStr}") val bounds = PrettyPrinter.collectBounds(ty).distinct if !bounds.isEmpty then output("Where:") bounds.foreach { case (lhs, rhs) => output(s" ${lhs.show} <: ${rhs.show}") } object PrettyPrinter: def apply(output: String => Unit)(using Scope, InvalCtx): PrettyPrinter = new PrettyPrinter(output) type Bound = (Type, Type) // * Type <: Type private def collectBounds(ty: GeneralType): List[Bound] = val res = ListBuffer[Bound]() val cache = MutSet[Uid[InfVar]]() object CollectBounds extends TypeTraverser: override def apply(pol: Boolean)(ty: GeneralType): Unit = ty match case v @ InfVar(_, uid, state, _) => if cache.add(uid) then res ++= state.lowerBounds.map: bd => apply(true)(bd) (bd, v) res ++= state.upperBounds.map: bd => apply(false)(bd) (v, bd) super.apply(pol)(ty) case _ => super.apply(pol)(ty) CollectBounds(true)(ty) res.toList ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/TypeSimplifier.scala ================================================ package hkmc2.invalml import scala.collection.mutable.{Map => MutMap, Set => MutSet, LinkedHashMap, LinkedHashSet} import scala.collection.immutable.{SortedMap, SortedSet} import scala.util.chaining._ import mlscript.utils.*, shorthands.* import hkmc2.utils.* import Type.* final def printPol(pol: Bool): Str = pol match { case true => "+" case false => "-" } class TypeSimplifier(tl: TraceLogger): import tl.{trace, log} def apply(pol: Bool, lvl: Int)(ty: GeneralType): GeneralType = ty |> simplifyStructure(pol, lvl) |> simplifyBounds(pol, lvl) |> simplifyForall def simplifyStructure(pol: Bool, lvl: Int)(ty: GeneralType): GeneralType = val done = MutSet.empty[InfVar] def goTV(tv: InfVar): InfVar = if done.add(tv) then tv.state.lowerBounds = tv.state.lowerBounds.map(goType) tv.state.upperBounds = tv.state.upperBounds.map(goType) tv def goType(ty: Type): Type = trace[Type](s"simplifyStructure(${ty.showDbg})", r => s"= ${r.showDbg}"): ty match case tv: InfVar => goTV(tv) case _ => ty.map(goType).simp def go(ty: GeneralType): GeneralType = ty match case ty: Type => goType(ty) case pt: PolyType => pt.tvs.foreach(goTV) pt.map(go) case pf: PolyFunType => pf.map(go) go(ty) def simplifyBounds(pol: Bool, lvl: Int)(ty: GeneralType): GeneralType = type IV = InfVar object Analysis extends TypeTraverser: val posVars: MutSet[IV] = MutSet.empty val negVars: MutSet[IV] = MutSet.empty val recVars: MutSet[IV] = MutSet.empty val occsNum: MutMap[IV, Int] = MutMap.empty[IV, Int] var curPath: Ls[IV] = Nil var pastPathsSet: MutSet[IV] = MutSet.empty val outerPol: MutMap[IV, Bool] = MutMap.empty[IV, Bool] // outer polarity before entering next level val varSubst: MutMap[IV, IV] = MutMap.empty val traversedTVs: MutSet[IV] = MutSet.empty def getRepr(tv: IV): IV = varSubst.get(tv) match { case S(tv2) => varSubst.get(tv2) match { case S(tv3) => varSubst += tv -> tv3 // varSubst += tv2 -> tv3 getRepr(tv3) case N => tv2 } case N => tv } override def apply(pol: Bool)(ty: GeneralType): Unit = trace(s"Analyse[${printPol(pol)}] ${ty.showDbg} [${curPath.reverseIterator.mkString(" ~> ")}]"): ty match case ty if ty.lvl <= lvl => log(s"Level is < $lvl") case tv: IV if { occsNum(tv) = occsNum.getOrElse(tv, 0) + 1; false } => case tv: IV => if varSubst.contains(tv) then return log(s"Already subst'd") // * If the IV was set to be substituted, it means it's been found recursive and we don't need to traverse it again var continue = true // if (!traversedTVs.contains(tv)) { if curPath.exists(_ is tv) then // TODO opt traversedTVs += tv val recPrefix = curPath.takeWhile(_ isnt tv) log(s"UNIFYING ${tv.showDbg} with ${recPrefix.mkString(", ")}") recPrefix.foreach: tv2 => if tv2 isnt tv then traversedTVs += tv2 var tvRepr = getRepr(tv2) assert(traversedTVs(tvRepr)) if tvRepr isnt tv then // assert(!varSubst.contains(tvRepr)) if tv.lvl === tvRepr.lvl /* && tvRepr.nameHint.isEmpty */ && !varSubst.contains(tvRepr) then { varSubst += tvRepr -> tv } else if tv.lvl > tvRepr.lvl then varSubst += tv -> tvRepr else if tvRepr.lvl > lvl && !varSubst.contains(tvRepr) then varSubst += tvRepr -> tv continue = false // TODO else?? if traversedTVs.contains(tv) then log(s"Now already traversed ${tv.showDbg}") else if pastPathsSet.contains(tv) then log(s"REC ${tv.showDbg}") recVars += tv continue = false if continue then // log(s">>> $curPath") val oldPath = curPath curPath ::= tv // * If tv is forall-qualified in a negative position, we need to **flip** the polarity // * e.g., ([A] -> A -> Int) -> ([A] -> A -> Int) // * Both `[A] -> A -> Int` should be simplified to the same type // * The first `[A] -> A -> Int` is in a negative position // * but the argument type `A` should be treated as negative instead of positive if !outerPol.get(tv).getOrElse(true) then if !pol then posVars += tv else negVars += tv else if pol then posVars += tv else negVars += tv // log(s">>>> $curPath") // traversingTVs += tv // traversedTVs += tv super.apply(pol)(ty) // traversingTVs -= tv curPath = oldPath case pt @ PolyType(tvs, outer, _) => // Avoid simplify outer variables to Top unexpectedly outer.foreach(outer => { posVars += outer negVars += outer }) val oldPath = curPath pastPathsSet ++= oldPath curPath = Nil outerPol ++= (tvs.map(v => v -> pol)) super.apply(pol)(pt) outerPol --= tvs curPath = oldPath pastPathsSet --= oldPath () case _ => val oldPath = curPath pastPathsSet ++= oldPath curPath = Nil super.apply(pol)(ty) curPath = oldPath pastPathsSet --= oldPath () trace(s"Simplifying type ${ty.showDbg}"): Analysis(pol)(ty) log("Unif-pre: " + Analysis.varSubst) Analysis.varSubst.valuesIterator.foreach { Analysis.getRepr(_) } // log("Unif-pst: " + Analysis.varSubst) log("Occ#: " + Analysis.occsNum.toSortedMap) log("Pos: " + Analysis.posVars.toSortedSet) log("Neg: " + Analysis.negVars.toSortedSet) log("Rec: " + Analysis.recVars.toSortedSet) log("Unif: " + Analysis.varSubst.toSortedMap) val cache: MutMap[IV, Type] = MutMap.empty val traversed: MutSet[IV] = MutSet.empty val transformed: MutMap[IV, Type] = MutMap.empty def subst(ty: GeneralType): GeneralType = trace[GeneralType](s"subst(${ty.showDbg})", r => s"= ${r.showDbg}"): ty match case ty if ty.lvl <= lvl => ty // TODO NOPE case _tv: IV => val tv = Analysis.getRepr(_tv) log(s"Repr: ${tv.showDbg}") transformed.getOrElseUpdate(tv, { if Analysis.recVars.contains(tv) then log(s"It's recursive!") transformed += tv -> tv else transformed += tv -> // TypeBounds(TopType, BotType)(noProv) // TODO improve? creates lots of junk... Top // FIXME arbitrary // TODO rm self-cycles val newLBs = tv.state.lowerBounds.map(subst(_).monoOr(???)) val newUBs = tv.state.upperBounds.map(subst(_).monoOr(???)) tv.state.lowerBounds = newLBs tv.state.upperBounds = newUBs val isPos = Analysis.posVars.contains(tv) val isNeg = Analysis.negVars.contains(tv) // if (isPos && !isNeg && (Analysis.occsNum(tv) === 1 && {newLBs match { case (tv: IV) :: Nil => true; case _ => false }} || newLBs.forall(_.isSmall))) { if isPos && !isNeg && ({newLBs match { case (tv: IV) :: Nil => true; case _ => false }} || newLBs.forall(_ => true)) then { // if (isPos && !isNeg && ({newLBs match { case (tv: IV) :: Nil => true; case _ => false }})) { newLBs.foldLeft(Bot: Type)(_ | _) } else // if (isNeg && !isPos && (Analysis.occsNum(tv) === 1 && {newUBs match { case (tv: IV) :: Nil => true; case _ => false }} || newUBs.forall(_.isSmall))) { if isNeg && !isPos && ({newUBs match { case (tv: IV) :: Nil => true; case _ => false }} || newUBs.forall(_ => true)) then // if (isNeg && !isPos && ({newUBs match { case (tv: IV) :: Nil => true; case _ => false }})) { newUBs.foldLeft(Top: Type)(_ & _) else // tv.lowerBounds = newLBs // tv.upperBounds = newUBs tv }) case ty: Type => ty.map(subst(_).monoOr(???)) case pt: PolyType => pt.map(subst) case pf: PolyFunType => pf.map(subst) subst(ty) def simplifyForall(ty: GeneralType): GeneralType = ty match case PolyType(tvs, outer, body) => val newBody = simplifyForall(body) val visited = PolyType.collectTVs(newBody) val newTvs = tvs.filter(visited) val newOuter = outer.filter(visited) if newTvs.isEmpty && newOuter.isEmpty then newBody else PolyType(newTvs, newOuter, newBody) case PolyFunType(args, ret, eff) => PolyFunType(args.map(arg => simplifyForall(arg)), simplifyForall(ret), eff) case _ => ty ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/TypeTraverser.scala ================================================ package hkmc2.invalml import mlscript.utils.*, shorthands.* import Type.* class TypeTraverser: def apply(pol: Bool)(ty: GeneralType): Unit = ty match case Top | Bot => case NegType(ty) => apply(!pol)(ty) case FunType(args, ret, eff) => args.foreach(apply(!pol)) apply(pol)(ret) apply(pol)(eff) case ClassLikeType(name, targs) => targs.foreach: case Wildcard(in, out) => apply(!pol)(in) apply(pol)(out) case ty: Type => apply(pol)(ty) apply(!pol)(ty) case InfVar(vlvl, uid, state, _) => if pol then state.lowerBounds.foreach(apply(true)) else state.upperBounds.foreach(apply(false)) case ComposedType(lhs, rhs, _) => apply(pol)(lhs) apply(pol)(rhs) case PolyType(tv, outer, body) => apply(pol)(body) case PolyFunType(args, ret, eff) => args.foreach(apply(!pol)) apply(pol)(ret) apply(pol)(eff) case ty: Type => apply(pol)(ty.toBasic) // TODO improve impl for all ctors ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/invalml/types.scala ================================================ package hkmc2 package invalml import mlscript.utils.*, shorthands.* import syntax.* import semantics.*, semantics.Term.* import utils.* import scala.collection.mutable.{Set => MutSet} import utils.Scope, Scope.scope import Elaborator.State // * General types include mono types (i.e., Type), forall quantified type, and poly function types sealed abstract class GeneralType: // * Is this type polymorphic lazy val isPoly: Bool // * Polymorphic level def lvl: Int // * Return itself if it is actually monomorphic. // * Otherwise, evaluate fallback def monoOr(fallback: => Type): Type // * The map function should not change the shape! protected type ThisType <: GeneralType def map(f: ThisType => ThisType): ThisType def subst(using map: Map[Uid[InfVar], InfVar]): ThisType def show(using Scope, InvalCtx, Raise): Str def showDbg: Str // * Types that can be used as class type arguments sealed trait TypeArg: def lvl: Int def mapArg(f: Type => Type): TypeArg def show(using Scope, InvalCtx, Raise): Str def showDbg: Str def & (that: TypeArg): TypeArg = (this, that) match case (Wildcard(in1, out1), Wildcard(in2, out2)) => Wildcard(in1 | in2, out1 & out2) case (ty: Type, Wildcard(in2, out2)) => Wildcard(ty | in2, ty & out2) case (Wildcard(in1, out1), ty: Type) => Wildcard(in1 | ty, out1 & ty) case (ty1: Type, ty2: Type) => ty1 & ty2 def | (that: TypeArg): TypeArg = (this, that) match case (Wildcard(in1, out1), Wildcard(in2, out2)) => Wildcard(in1 & in2, out1 | out2) case (ty: Type, Wildcard(in2, out2)) => Wildcard(ty & in2, ty | out2) case (Wildcard(in1, out1), ty: Type) => Wildcard(in1 & ty, out1 | ty) case (ty1: Type, ty2: Type) => ty1 | ty2 def posPart: Type = this match case Wildcard(_, out) => out case ty: Type => ty def negPart: Type = this match case Wildcard(in, _) => in case ty: Type => ty case class Wildcard(in: Type, out: Type) extends TypeArg { def mapArg(f: Type => Type): Wildcard = Wildcard(f(in), f(out)) override def show(using Scope, InvalCtx, Raise): Str = in match case `out` => in.show case Bot => out match case Top => "?" case _ => s"out ${out.show}" case _ => out match case Top => s"in ${in.show}" case _ => s"in ${in.show} out ${out.show}" override def showDbg: Str = s"in ${in.showDbg} out ${out.showDbg}" override lazy val lvl: Int = in.lvl.max(out.lvl) } object Wildcard: def in(ty: Type): Wildcard = Wildcard(ty, Top) def out(ty: Type): Wildcard = Wildcard(Bot, ty) def empty: Wildcard = Wildcard(Bot, Top) trait CachedBasicType extends Type: override lazy val toBasic: BasicType = mkBasic def mkBasic: BasicType abstract class TypeExt extends Type: override def hashCode: Int = toBasic.hashCode sealed abstract class Type extends GeneralType with TypeArg: override protected type ThisType = Type def toBasic: BasicType def toDnf(using TL): Disj // * Remove redundant Top/Bot. // * e.g., Top & Int === Int lazy val simp: Type = this lazy val isPoly: Bool = false override def map(f: Type => Type): Type = toBasic.mapBasic(f) // TODO improve: map on all type constructors def mapArg(f: Type => Type): Type = f(this) def monoOr(fallback: => Type): Type = this def & (that: Type): Type = this match case Top => that case Bot => Bot case _ => that match case Top => this case Bot => Bot case ComposedType(l, r, false) => this & l & r case _ => if this === that then this else ComposedType(this, that, false) def | (that: Type): Type = this match case Top => Top case Bot => that case _ => that match case Top => Top case Bot => this case ComposedType(l, r, true) => this | l | r case _ => if this === that then this else ComposedType(this, that, true) def ! : Type = this match case Top => Bot case Bot => Top case NegType(res) => res case ComposedType(l, r, true) => l.! & r.! case ComposedType(l, r, false) => l.! | r.! case _ => NegType(this) protected[invalml] def paren(using Scope, InvalCtx, Raise): Str = toBasic match case _: InfVar | _: ClassLikeType | _: NegType | Top | Bot => show case _: ComposedType | _: FunType => s"($show)" protected[invalml] def parenDbg: Str = toBasic match case _: InfVar | _: ClassLikeType | _: NegType | Top | Bot => showDbg case _: ComposedType | _: FunType => s"($showDbg)" sealed abstract class BasicType extends Type: lazy val lvl: Int = this match case ClassLikeType(name, targs) => targs.map(_.lvl).maxOption.getOrElse(0) case InfVar(lvl, _, _, _) => lvl case FunType(args, ret, eff) => (ret :: eff :: args).map(_.lvl).max case ComposedType(lhs, rhs, _) => lhs.lvl.max(rhs.lvl) case NegType(ty) => ty.lvl case Top | Bot => 0 def mapBasic(f: Type => Type): Type = this match case ClassLikeType(name, targs) => ClassLikeType(name, targs.map(_.mapArg(f))) case FunType(args, ret, eff) => FunType(args.map(f), f(ret), f(eff)) case ComposedType(lhs, rhs, pol) => Type.mkComposedType(f(lhs), f(rhs), pol) case NegType(ty) => Type.mkNegType(f(ty)) case Top | Bot | _: InfVar => this override def show(using Scope, InvalCtx, Raise): Str = def printEff(eff: Type) = eff match case Bot => "" // case ty if ty == allocSkolem => "" case _ => s"{${eff.show}}" this match case ClassLikeType(sym, Nil) if sym is invalctx.getCls("Unit") => "()" case ClassLikeType(name, targs) => if targs.isEmpty then s"${name.nme}" else s"${name.nme}[${targs.map(_.show).mkString(", ")}]" case v @ InfVar(lvl, uid, _, isSkolem) => val name = scope.lookup(v.sym).getOrElse(scope.allocateName(v.sym, v.hint)) if isSkolem then name else s"'${name}" case FunType(arg :: Nil, ret, eff) => s"${arg.paren} ->${printEff(eff)} ${ret.paren}" case FunType(args, ret, eff) => s"(${args.map(_.show).mkString(", ")}) ->${printEff(eff)} ${ret.paren}" case ComposedType(lhs, rhs, pol) => s"${lhs.paren} ${if pol then "∨" else "∧"} ${rhs.paren}" case NegType(ty) => s"¬${ty.paren}" case Top => "⊤" case Bot => "⊥" override def showDbg: Str = this match case ClassLikeType(name, targs) => if targs.isEmpty then s"${name.nme}" else s"${name.nme}[${targs.map(_.showDbg).mkString(", ")}]" case v @ InfVar(lvl, uid, _, isSkolem) => val name = if v.hint.isEmpty then s"${v.sym.nme}" else s"${v.sym.nme}(${v.hint})" if isSkolem then s"${name}${uid}_${lvl}" else s"'${name}${uid}_${lvl}" case FunType(arg :: Nil, ret, eff) => s"${arg.parenDbg} ->{${eff.showDbg}} ${ret.parenDbg}" case FunType(args, ret, eff) => s"(${args.map(_.showDbg).mkString(", ")}) ->{${eff.showDbg}} ${ret.parenDbg}" case ComposedType(lhs, rhs, pol) => s"${lhs.parenDbg} ${if pol then "∨" else "∧"} ${rhs.parenDbg}" case NegType(ty) => s"¬${ty.parenDbg}" case Top => "⊤" case Bot => "⊥" def toBasic: BasicType = this var _dnf: Disj = null def withDnf(d: Disj): this.type = // TODO: remove? assert(_dnf eq null) _dnf = d this def toDnf(using TL) = if _dnf eq null then val d = NormalForm.dnf(this) _dnf = d d else _dnf trait CachedNorm[A <: AnyRef]: def mkNorm(using TL): A private var _norm: A = null.asInstanceOf final def hasNorm: Bool = !(_norm eq null) def withNorm(d: A): this.type = // TODO: remove? assert(!hasNorm) _norm = d this def toNorm(using TL) = if _norm eq null then val d = mkNorm _norm = d d else _norm object BasicType: // TOOD dedup def union(tys: Ls[BasicType]): BasicType = tys match case Nil => Bot case ty :: Nil => ty case ty :: tys => ComposedType(ty, union(tys), true) def inter(tys: Ls[BasicType]): BasicType = tys match case Nil => Bot case ty :: Nil => ty case ty :: tys => ComposedType(ty, inter(tys), false) case class ClassLikeType(name: TypeSymbol | ModuleOrObjectSymbol, targs: Ls[TypeArg]) extends BasicType with CachedNorm[ClassLikeType]: def mkNorm(using TL): ClassLikeType = ClassLikeType(name, targs.map: case ty: Type => ty.toDnf case Wildcard(i, o) => Wildcard(i.toDnf, o.toDnf) ) override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = ClassLikeType(name, targs.map { case Wildcard(in, out) => Wildcard(in.subst, out.subst) case ty: Type => ty.subst }) final case class InfVar(vlvl: Int, uid: Uid[InfVar], state: VarState, isSkolem: Bool)(val sym: Symbol, val hint: Str) extends BasicType: override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = map.get(uid).getOrElse(this) given Ordering[InfVar] = Ordering.by(_.uid) case class FunType(args: Ls[Type], ret: Type, eff: Type) extends BasicType with CachedNorm[FunType]: def mkNorm(using TL): FunType = FunType(args.map(_.toDnf), ret.toDnf, eff.toDnf) override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = FunType(args.map(_.subst), ret.subst, eff.subst) case class ComposedType(lhs: Type, rhs: Type, pol: Bool) extends BasicType: // * Positive -> union override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = Type.mkComposedType(lhs.subst, rhs.subst, pol) override lazy val simp: Type = Type.mkComposedType(lhs.simp, rhs.simp, pol) final case class NegType(ty: Type) extends BasicType: override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = NegType(ty.subst) override lazy val simp: Type = ty.simp.! object Top extends BasicType: override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = Top object Bot extends BasicType: override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = Bot object Type: def mkComposedType(lhs: Type, rhs: Type, pol: Bool): Type = if pol then lhs | rhs else lhs & rhs def mkNegType(ty: Type): Type = ty.! // * Poly types can not be used as type arguments case class PolyType(tvs: Ls[InfVar], outer: Opt[InfVar], body: GeneralType) extends GeneralType: override protected type ThisType = GeneralType override lazy val isPoly: Bool = true override lazy val lvl: Int = (body :: tvs).map(_.lvl).max override def show(using Scope, InvalCtx, Raise): Str = given Scope = scope.nest val lst = (outer match { case S(outer) => val op = outer.show (if op === "outer" then op else s"outer $op") :: Nil case N => Nil }) ++ tvs.map(_.show) s"[${lst.mkString(", ")}] -> ${body.show}" override def showDbg: Str = s"[${(outer.toList ++ tvs).map(_.showDbg).mkString(", ")}] -> ${body.showDbg}" override def monoOr(fallback: => Type): Type = fallback override def map(f: GeneralType => GeneralType): PolyType = PolyType(tvs, outer, f(body)) override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = PolyType(tvs.map { case v @ InfVar(lvl, uid, state, skolem) => val newSt = new VarState() newSt.lowerBounds = state.lowerBounds.map(_.subst) newSt.upperBounds = state.upperBounds.map(_.subst) InfVar(lvl, uid, newSt, skolem)(v.sym, v.hint) }, outer, body.subst) // * outer should have no bound! // * This function will only return the body after substitution // * and \dom(map) should cover all tvs. // * This function is dedicated to `skolemize` and `instantiate`. private def substAndGetBody(using map: Map[Uid[InfVar], InfVar]): GeneralType = tvs.foreach: case InfVar(lvl, uid, state, skolem) => val v = map(uid) v.state.lowerBounds = state.lowerBounds.map(_.subst) v.state.upperBounds = state.upperBounds.map(_.subst) body.subst def skolemize(nextUid: => Uid[InfVar], lvl: Int)(tl: TL) = // * Note that by this point, the state is supposed to be frozen/treated as immutable // * `outer` is already skolemized when it is declared val map = tvs.map(v => val sk = InfVar(lvl, nextUid, new VarState(), true)(v.sym, v.hint) tl.log(s"skolemize ${v.showDbg} ~> ${sk.showDbg}") v.uid -> sk ).toMap substAndGetBody(using map) def instantiate(nextUid: => Uid[InfVar], env: InfVar, lvl: Int)(tl: TL)(using State): GeneralType = val map = (outer.map(_.uid -> env).toList ++ tvs.map(v => val nv = InfVar(lvl, nextUid, new VarState(), false)(new InstSymbol(v.sym), v.hint) tl.log(s"instantiate ${v.showDbg} ~> ${nv.showDbg}") v.uid -> nv )).toMap substAndGetBody(using map) object PolyType: def collectTVs(ty: GeneralType): Set[InfVar] = val visited = MutSet.empty[InfVar] object CollectTVs extends TypeTraverser: override def apply(pol: Boolean)(ty: GeneralType): Unit = ty match case v @ InfVar(_, _, state, _) => if visited.add(v) then state.lowerBounds.foreach: bd => apply(true)(bd) state.upperBounds.foreach: bd => apply(false)(bd) super.apply(pol)(ty) case _ => super.apply(pol)(ty) CollectTVs(true)(ty) visited.toSet def generalize(ty: GeneralType, outer: Opt[InfVar], lvl: Int): PolyType = PolyType(collectTVs(ty).filter(v => v.lvl == lvl && outer.map(_.uid != v.uid).getOrElse(true)).toList.sorted, outer, ty) // * Functions that accept/return a polymorphic type. // * Note that effects are always monomorphic // * Poly types can not be used as type arguments case class PolyFunType(args: Ls[GeneralType], ret: GeneralType, eff: Type) extends GeneralType: override protected type ThisType = GeneralType lazy val isPoly: Bool = (ret :: args).exists(_.isPoly) lazy val lvl: Int = (ret :: eff :: args).map(_.lvl).max override def show(using Scope, InvalCtx, Raise): Str = s"(${args.map(_.show).mkString(", ")}) ->{${eff.show}} ${ret.show}" override def showDbg: Str = s"(${args.map(_.showDbg).mkString(", ")}) ->{${eff.showDbg}} ${ret.showDbg}" private lazy val mono: Opt[FunType] = if isPoly then N else Some(FunType(args.map { case t: Type => t case pf: PolyFunType => pf.mono.get case _ => ??? // * Impossible }, ret match { case t: Type => t case pf: PolyFunType => pf.mono.get case _ => ??? // * Impossible }, eff)) override def monoOr(fallback: => Type): Type = mono.getOrElse(fallback) override def map(f: GeneralType => GeneralType): PolyFunType = PolyFunType(args.map(f), f(ret), f(eff).monoOr(???)) // * Must be mono override def subst(using map: Map[Uid[InfVar], InfVar]): ThisType = PolyFunType(args.map(_.subst), ret.subst, eff.subst) class VarState: var lowerBounds: Ls[Type] = Nil var upperBounds: Ls[Type] = Nil ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/io/FileSystem.scala ================================================ package hkmc2 package io import mlscript.utils._, shorthands._ /** * Abstract file system operations. * * These are file system operations that can be directly called by the compiler. * More high-level file system operations, such as getting all files under a * folder or recursively walking through a specified path, should be handled by * the caller of the compiler. */ trait FileSystem: /** Read entire file as string. */ def read(path: Path): String /** Write string to file, overwriting if exists. */ def write(path: Path, content: String): Unit /** Check if a file exists at the given path. */ def exists(path: Path): Bool /** * Get the last changed timestamp of the file at the given path. The meaning * of the timestamp is platform-dependent. The caller should ensure that the * file exists before calling this method. */ def getLastChangedTimestamp(path: Path): Long object FileSystem: /** Get the platform default file system by delegating to the platform. */ def default: FileSystem = PlatformFileSystem.default class FileNotFoundException(path: Path) extends Exception: override def getMessage(): String = s"File not found: ${path.toString}" ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/io/Path.scala ================================================ package hkmc2 package io import mlscript.utils._, shorthands._ /** * Cross-platform path abstraction. * Represents a file system path without performing any I/O. */ abstract class Path: /** Convert to platform-specific string representation */ def toString: String /** Get the last segment of the path (file or directory name) */ def last: String /** Get the base name without extension */ def baseName: String /** Get the file extension (without dot) */ def ext: String /** Navigate to parent directory */ def up: Path /** Join with a relative path */ def /(relPath: RelPath): Path /** Join with a single path fragment */ def /(fragment: Str): Path /** Calculate relative path from this to target */ def relativeTo(base: Path): Opt[RelPath] /** Get segments as a list */ def segments: Ls[String] /** Check if this is an absolute path */ def isAbsolute: Bool object Path: /** Create path from string - delegates to platform-specific implementation */ def apply(str: String): Path = PathFactory.fromString(str) /** Platform-specific path separator */ def separator: String = PathFactory.separator /** * Represents a relative path */ abstract class RelPath: def toString: String def segments: Ls[String] def /(other: RelPath): RelPath object RelPath: /** Create relative path from string - delegates to platform-specific implementation */ def apply(str: String): RelPath = PathFactory.relPathFromString(str) /** Represents parent directory (..) */ val up: RelPath = PathFactory.relPathUp ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/package.scala ================================================ package hkmc2 import sourcecode.{Line, FileName} import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.Message.MessageContext extension [A](x: A) infix inline def givenIn[R](inline k: A ?=> R) = k(using x) infix inline def ne_::(xs: Ls[A]): NELs[A] = new ::(x, xs) extension [A](xs: Ls[A]) def ne_! : NELs[A] = xs match case Nil => throw new IllegalArgumentException("Cannot convert an empty list to a non-empty list.") case xs: NELs[A] => xs inline def ne_? : Opt[NELs[A]] = xs match case Nil => N case xs: NELs[A] => S(xs) // * Valid identifiers for the members of module and class-like definitions // * Importantly, these are the same as valid JavaScript identifiers, // * so we do not check them in JS code-generation. val identifierPattern: scala.util.matching.Regex = "^[A-Za-z_$][A-Za-z0-9_$]*$".r def softAssert(cond: Boolean, msg: => Str = "")(using Line, FileName, Raise): Unit = if !cond then raise: InternalError( msg"Compiler reached an unexpected state at '${summon[FileName].value}:${summon[Line].value}'${ if msg === "" then "" else s": $msg"}" -> N :: msg"The compilation result may be incorrect." -> N :: msg"This is a compiler bug; please report it to the maintainers." -> N :: Nil) def softTODO(cond: Boolean, msg: => Str = "")(using Line, FileName, Raise): Unit = if !cond then raise: InternalError( msg"Compiler reached an unsupported state${ if msg === "" then "" else s": $msg"}" -> N :: msg"The compilation result may be incorrect." -> N :: msg"This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers." -> N :: Nil) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala ================================================ package hkmc2 package semantics import mlscript.utils.*, shorthands.* import syntax.Tree import syntax.Tree.* import hkmc2.syntax.{PossiblyAnnotated, TypeOrTermDef} trait BlockImpl(using Elaborator.State): self: Block => val desugStmts = def desug(stmts: Ls[Tree]): Ls[Tree] = stmts match case syntax.Desugared(PossiblyAnnotated(anns, Assert(kw, cond, N, els))) :: stmts => PossiblyAnnotated(anns, Assert(kw, cond, S(Block(stmts)), els)) :: Nil case syntax.Desugared(PossiblyAnnotated(anns, td: TypeDef)) :: stmts => val ctors = td.withPart.toList.flatMap: case Block(sts) => sts.flatMap: case Constructor(Block(ctors)) => ctors case _ => Nil case _ => Nil // A temporary solution for ADTs, which desugars ADTs to normal class definitions. // This will be removed after we truly support ADTs correctly. // TODO: No raise contextual variable. Only `Error` nodes are returned if there is an error. lazy val (headId, headPs) = td.baseHead match case id: Ident => (id, Nil) case App(id: Ident, TyTup(ps)) => (id, ps) // Temporarily use `data` annotation to distinguish the following ctors: // - Ctor(...) // - Ctor[...](...) extends ADT[...] // since the former will be desugared to `class Ctor[...](...) extends ADT[...]`, // where `Ctor[...]` and `ADT[...]` share the same type parameter list def getPossibleGenericAnns(decl: Tree, anns: Ls[Tree]) = decl match case InfixApp(_, Keywrd(syntax.Keyword.`extends`), _) => Keywrd(syntax.Keyword.data) :: anns case _ => anns // Generate `extends` suffix if it is not provided by users // Also check if the number of type parameters is correct def genExt(decl: Tree) = decl match case InfixApp(_, Keywrd(syntax.Keyword.`extends`), ext) => ext match case _: Ident if headPs.isEmpty => ext case App(id: Ident, TyTup(ps)) if id.name == headId.name && ps.length == headPs.length => ext case _ => Error() case _ => headPs match case Nil => headId case ps => App(headId, TyTup(ps.map { case m @ Modified(Keywrd(syntax.Keyword.`in`) | Keywrd(syntax.Keyword.`out`), _) => m case t => Tup(Tree.Modified(Keywrd(syntax.Keyword.`in`), t) :: Tree.Modified(Keywrd(syntax.Keyword.`out`), t) :: Nil) })) // Insert type parameters for constructors. // e.g. `class Foo[T] with constructor Bar(x: T)` will be desugared to // `class Bar[T](x: T) extends Foo[T]` // Otherwise, the elaborator will complain `T` is not defined. def genCtorHead(decl: Tree) = decl match case InfixApp(decl, Keywrd(syntax.Keyword.`extends`), _) => decl // check will be applied in genExt case App(_: Ident, tup: TyTup) => Error() case App(id: Ident, ps: Tup) => App(App(id, TyTup(headPs)), ps) case id: Ident => App(id, TyTup(headPs)) case _ => Error() // Only fields modified by `val` can be extracted by pattern matching. // For ADTs, all fields can be extracted, but we don't want to add `val`s manumally. def insertVal(decl: Tree): Tree = decl match case id: Ident => id case App(f, Tup(ps)) => App(f, Tup(ps.map(p => TermDef(syntax.ImmutVal, p, N)))) case App(f, tup: TyTup) => App(insertVal(f), tup) case _ => Error() PossiblyAnnotated(anns, td) :: ( ctors.map(head => PossiblyAnnotated(getPossibleGenericAnns(head, anns), TypeDef(syntax.Cls, td.name match case L(_) => head case R(name) => InfixApp(insertVal(genCtorHead(head)), Keywrd(syntax.Keyword.`extends`), genExt(head)) , N))) ) ::: desug(stmts) case stmt :: stmts => stmt.desugared match case PossiblyAnnotated(anns, h @ Hndl(body = N)) => PossiblyAnnotated(anns, h.copy(body = S(Block(stmts)))) :: Nil case stmt => stmt :: desug(stmts) case Nil => Nil desug(stmts) val definedSymbols: Array[Str -> BlockMemberSymbol] = desugStmts .flatMap: case PossiblyAnnotated(_, td: syntax.TypeOrTermDef) => td.name match case L(_) => Nil case R(id) => id.name -> R(td) :: ( td.symbName match case S(R(sid)) => id.name -> L(sid.name) :: Nil case _ => Nil ) case _ => Nil .groupMap(_._1)(_._2).flatMap: case (nme, snmes_tds) => val (symNmes, tds) = snmes_tds.partitionMap(identity) val sym = new BlockMemberSymbol(nme, tds) nme -> sym :: symNmes.map(_ -> sym) .toArray.sortBy(_._1) end BlockImpl ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala ================================================ package hkmc2 package semantics import scala.collection.mutable import scala.annotation.tailrec import scala.language.implicitConversions import mlscript.utils.*, shorthands.* import utils.TraceLogger import syntax.* import Tree.* import BracketKind.* import Term.{ Blk, Rcd } import hkmc2.Message.MessageContext import Keyword.{`let`, `set`} import hkmc2.utils.Scope object Elaborator: val binaryOps = Set( ",", // * Not currently used directly; but `;` (below) maps to it "+", "-", "*", "/", "%", "==", "!=", "<", "<=", ">", ">=", "===", "!==", "&&", "||") val unaryOps = Set("-", "+", "!", "~", "typeof") val anyOps = Set("super") val builtins = binaryOps ++ unaryOps ++ anyOps val aliasOps = Map( ";" -> ",", "+." -> "+", "-." -> "-", "*." -> "*", "/." -> "/") private val builtinBinOps = aliasOps ++ (binaryOps.map: op => op -> op).toMap val reservedNames = binaryOps.toSet ++ aliasOps.keySet + "NaN" + "Infinity" // TODO: rename to ScopeKind? enum OuterCtx: case Function(returnHandlerSymbol: TempSymbol) case InnerScope(innerSymbol: InnerSymbol) case LocalScope(nameHint: Str) case LambdaOrHandlerBlock case NonReturnContext def showDbg: Str = this match case Function(sym) => s"fun:${sym.nme}" case InnerScope(inner) => inner.toString case LocalScope(hint) => hint case LambdaOrHandlerBlock => "LambdaOrHandlerBlock" case NonReturnContext => "NonReturnContext" def inner: Opt[InnerSymbol] = this match case InnerScope(inner) => S(inner) case _ => N enum ReturnHandler: case Required(handler: TempSymbol) case Direct case NotInFunction case Forbidden /** Context used to keep track of underscores representing lambda shorthands, eg in `_ + 1`. */ // TODO later: use TempSymbol instead of VarSymbol? (currently, trying that creates lot of problems) class UnderCtx(val unders: Opt[mutable.ArrayBuffer[VarSymbol]]) case class Ctx( outer: OuterCtx, parent: Opt[Ctx], env: Map[Str, Ctx.Elem], mode: Mode, ): override def toString: Str = s"${parent.fold("")(_.toString+"/")}${outer.showDbg}" lazy val scope: SrcScope = SrcScope(outer, parent.map(_.scope)) def +(local: Str -> Symbol): Ctx = copy(outer, env = env + local.mapSecond(Ctx.RefElem(_))) def ++(locals: IterableOnce[Str -> Symbol]): Ctx = copy(outer, env = env ++ locals.mapValues(Ctx.RefElem(_))) def elem_++(locals: IterableOnce[Str -> Ctx.Elem]): Ctx = copy(outer, env = env ++ locals.iterator.filter: kv => // * Imports should not shadow symbols defined in the same scope; // * but they should be allowed to shadow previous imports. env.get(kv._1).forall(_.isImport)) def withMembers(members: Iterable[Str -> MemberSymbol]): Ctx = copy(env = env ++ members.map: case (nme, sym) => val elem = outer.inner match case S(outer) => Ctx.SelElem(outer, sym.nme, S(sym), isImport = false) case N => Ctx.RefElem(sym) nme -> elem ) def nest(outerCtx: OuterCtx): Ctx = Ctx(outerCtx, Some(this), Map.empty, mode) def nestLocal(nameHint: Str): Ctx = nest(OuterCtx.LocalScope(nameHint)) def nestInner(inner: InnerSymbol): Ctx = nest(OuterCtx.InnerScope(inner)) def get(name: Str): Opt[Ctx.Elem] = env.get(name).orElse(parent.flatMap(_.get(name))) def getOuter: Opt[InnerSymbol] = outer.inner.orElse(parent.flatMap(_.getOuter)) def getNonLocalRetHandler: Opt[TempSymbol] = outer match case OuterCtx.Function(sym) => S(sym) case _ => parent.flatMap(_.getNonLocalRetHandler) def getRetHandler: ReturnHandler = outer match case OuterCtx.Function(sym) => ReturnHandler.Direct case _: (OuterCtx.LambdaOrHandlerBlock.type | OuterCtx.InnerScope) => getNonLocalRetHandler.fold(ReturnHandler.NotInFunction)(ReturnHandler.Required(_)) case OuterCtx.NonReturnContext => ReturnHandler.Forbidden case _: OuterCtx.LocalScope => parent.fold(ReturnHandler.NotInFunction)(_.getRetHandler) // * Invariant: We expect that the top-level context only contain hard-coded symbols like `globalThis` // * and that built-in symbols like Int and Str be imported into another nested context on top of it. // * It should not be possible to shadow these built-in symbols, so user code should always be compiled // * in further nested contexts. lazy val preludeCtx: Ctx = parent match case N => lastWords("Cannot find prelude context.") case S(par) => if par.parent.isEmpty then this else par.preludeCtx // * Method `getBuiltin` is used to look up built-in symbols in the context of builtin symbols. def getBuiltin(nme: Str): Opt[Ctx.Elem] = preludeCtx.env.get(nme) lazy val builtins: Ctx#MkBuiltins = preludeCtx.MkBuiltins private object MkBuiltins extends MkBuiltins class MkBuiltins: assert(Ctx.this is preludeCtx) private def assumeBuiltin(nme: Str): Symbol = getBuiltin(nme) .getOrElse(throw new NoSuchElementException(s"builtin $nme not in ${parent.map(_.env.keySet)}")) .symbol.getOrElse(throw new NoSuchElementException(s"builtin symbol $nme")) private def assumeBuiltinTpe(nme: Str): TypeSymbol = assumeBuiltin(nme).asTpe.getOrElse(throw new NoSuchElementException( s"builtin type symbol $nme")) private def assumeBuiltinCls(nme: Str): ClassSymbol = assumeBuiltin(nme).asCls.getOrElse(throw new NoSuchElementException( s"builtin class symbol $nme")) private def assumeBuiltinObj(nme: Str): ModuleOrObjectSymbol = assumeBuiltin(nme).asObj.getOrElse(throw new NoSuchElementException( s"builtin object symbol $nme")) private def assumeBuiltinMod(nme: Str): ModuleOrObjectSymbol = assumeBuiltin(nme).asMod.getOrElse(throw new NoSuchElementException( s"builtin module symbol $nme")) val Int = assumeBuiltinCls("Int") // TODO(Derppening): Can we move the Int31 builtin in the wasm module? val Int31 = assumeBuiltinCls("Int31") val Num = assumeBuiltinCls("Num") val Str = assumeBuiltinCls("Str") val BigInt = assumeBuiltinCls("BigInt") val Function = assumeBuiltinCls("Function") val Error = assumeBuiltinCls("Error") val Bool = assumeBuiltinCls("Bool") val Object = assumeBuiltinCls("Object") val Array = assumeBuiltinCls("Array") val TypedArray = assumeBuiltinCls("TypedArray") val untyped = assumeBuiltinTpe("untyped") val tailrec = assumeBuiltinTpe("tailrec") val tailcall = assumeBuiltinTpe("tailcall") // println(s"Builtins: $Int, $Num, $Str, $untyped") class VirtualModule(val module: ModuleOrObjectSymbol): val bms = getBuiltin(module.nme) match case S(Ctx.RefElem(bms: BlockMemberSymbol)) => bms case huh => wat(huh) protected def assumeObject(nme: Str): BlockMemberSymbol = module.tree.definedSymbols.get(nme).getOrElse: throw new NoSuchElementException( s"builtin module symbol source.$nme") object Symbol extends VirtualModule(assumeBuiltinObj("Symbol")): val `for` = assumeObject("for") val iterator = assumeObject("iterator") object source extends VirtualModule(assumeBuiltinMod("source")): val line = assumeObject("line") val name = assumeObject("name") val file = assumeObject("file") object js extends VirtualModule(assumeBuiltinMod("js")): val bitand = assumeObject("bitand") val bitnot = assumeObject("bitnot") val bitor = assumeObject("bitor") val shl = assumeObject("shl") val try_catch = assumeObject("try_catch") object wasm extends VirtualModule(assumeBuiltinMod("wasm")): val plus_impl = assumeObject("plus_impl") val minus_impl = assumeObject("minus_impl") val times_impl = assumeObject("times_impl") val div_impl = assumeObject("div_impl") val mod_impl = assumeObject("mod_impl") val eq_impl = assumeObject("eq_impl") val neq_impl = assumeObject("neq_impl") val lt_impl = assumeObject("lt_impl") val le_impl = assumeObject("le_impl") val gt_impl = assumeObject("gt_impl") val ge_impl = assumeObject("ge_impl") val neg_impl = assumeObject("neg_impl") val pos_impl = assumeObject("pos_impl") val not_impl = assumeObject("not_impl") object debug extends VirtualModule(assumeBuiltinMod("debug")): val printStack = assumeObject("printStack") object annotations extends VirtualModule(assumeBuiltinMod("annotations")): val compile = assumeObject("compile") val buffered = assumeObject("buffered") val bufferable = assumeObject("bufferable") object scope extends VirtualModule(assumeBuiltinMod("scope")): val locally = assumeObject("locally") object runtime extends VirtualModule(assumeBuiltinMod("runtime")): val suspend = assumeObject("suspend") val handle_suspension = assumeObject("handle_suspension") def getBuiltinOp(op: Str): Opt[Str] = if getBuiltin(op).isDefined then builtinBinOps.get(op) else N object BuiltInOpIdent: def unapply(id: Ident): Opt[Str] = getBuiltinOp(id.name) /** Classes that do not use `instanceof` in pattern matching. */ val virtualClasses = Set(Int, Num, Str, Bool, TypedArray) object Ctx: abstract class Elem: def nme: Str def ref(id: Ident)(using Elaborator.State, Ctx): Resolvable def symbol: Opt[Symbol] def isImport: Bool final case class RefElem(sym: Symbol) extends Elem: val nme = sym.nme def ref(id: Ident)(using Elaborator.State, Ctx): Resolvable = // * Note: due to symbolic ops, we may have `id.name =/= nme`; // * e.g., we can have `id.name = "|>"` and `nme = "pipe"`. Term.Ref(sym)(id, 666, N) // FIXME: 666 is a temporary placeholder def symbol = S(sym) def isImport: Bool = false final case class SelElem(base: Elem, nme: Str, symOpt: Opt[MemberSymbol], isImport: Bool) extends Elem: def ref(id: Ident)(using Elaborator.State, Ctx): Resolvable = // * Same remark as in RefElem#ref Term.SynthSel(base.ref(Ident(base.nme)), new Ident(nme).withLocOf(id))(symOpt, FlowSymbol.synthSel(nme), N, S(summon)) def symbol = symOpt given Conversion[Symbol, Elem] = RefElem(_) val empty: Ctx = Ctx(OuterCtx.LocalScope("top-level"), N, Map.empty, Mode.Full) enum Mode: case Full case Light type Ctxl[A] = Ctx ?=> Cfg[A] transparent inline def ctx(using Ctx): Ctx = summon class State: val suid = new Uid.Symbol.State given State = this val globalThisSymbol = TopLevelSymbol("globalThis") val unitSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Obj), Ident("Unit")) // Stable symbol for the synthetic Wasm Unit singleton val unitBlockMemberSymbol = BlockMemberSymbol("Unit", Nil) val loopEndSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Obj), Ident("LoopEnd")) val tupleSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Mod), Ident("Tuple")) val strSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Mod), Ident("Str")) // In JavaScript, `import` can be used for getting current file path, as `import.meta` val importSymbol = new VarSymbol(Ident("import")) val noSymbol = NoSymbol() val runtimeSymbol = TempSymbol(N, "runtime") val definitionMetadataSymbol = TempSymbol(N, "definitionMetadata") val prettyPrintSymbol = TempSymbol(N, "prettyPrint") val termSymbol = TempSymbol(N, "Term") val blockSymbol = TempSymbol(N, "Block") val optionSymbol = TempSymbol(N, "option") val wasmSymbol = TempSymbol(N, "wasm") val nonLocalRetHandlerTrm = val id = new Ident("NonLocalReturn") val sym = ClassSymbol(DummyTypeDef(syntax.Cls), id) val bsym = BlockMemberSymbol("ret", Nil, true) val defn = ClassDef(N, syntax.Cls, sym, bsym, N, Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(UnitLit(false)))), Nil, N) sym.defn = S(defn) Term.SynthSel(runtimeSymbol.ref(), id)(S(sym), FlowSymbol.synthSel(id.name), N, N) val nonLocalRet = val id = new Ident("ret") BlockMemberSymbol(id.name, Nil, true) val unreachableSymbol = TermSymbol(syntax.ImmutVal, N, new Ident("unreachable")) val tupleGetSymbol = createFunSymbolInMod("get", "xs" :: "i" :: Nil, tupleSymbol) val tupleSliceSymbol = createFunSymbolInMod("slice", "xs" :: "i" :: "j" :: Nil, tupleSymbol) val tupleLazySliceSymbol = createFunSymbolInMod("lazySlice", "xs" :: "i" :: "j" :: Nil, tupleSymbol) val strStartsWithSymbol = createFunSymbolInMod("startsWith", "string" :: "prefix" :: Nil, strSymbol) val strGetSymbol = createFunSymbolInMod("get", "string" :: "i" :: Nil, strSymbol) val strTakeSymbol = createFunSymbolInMod("take", "string" :: "n" :: Nil, strSymbol) val strLeaveSymbol = createFunSymbolInMod("leave", "string" :: "n" :: Nil, strSymbol) val (matchSuccessClsSymbol, matchSuccessTrmSymbol) = val id = new Ident("MatchSuccess") val td = TypeDef(syntax.Cls, App(id, Tup(Ident("output") :: Ident("bindings") :: Nil)), N) val cs = ClassSymbol(td, id) val ts = TermSymbol(syntax.Fun, N, id) val flag = FldFlags.empty.copy(isVal = true) val ps = PlainParamList( Param(flag, VarSymbol(Ident("output")), N, Modulefulness(N)(false)) :: Param(flag, VarSymbol(Ident("bindings")), N, Modulefulness(N)(false)) :: Nil) val ctsym = TermSymbol(Fun, S(cs), cs.id) cs.defn = S(ClassDef.Parameterized(N, syntax.Cls, cs, BlockMemberSymbol(cs.name, Nil), S(ctsym), Nil, ps, Nil, N, ObjBody(Blk(Nil, Term.Lit(UnitLit(false)))), N, Nil)) cs -> ts val (matchFailureClsSymbol, matchFailureTrmSymbol) = val id = new Ident("MatchFailure") val td = DummyTypeDef(syntax.Cls) val cs = ClassSymbol(td, id) val ts = TermSymbol(syntax.Fun, N, id) val flag = FldFlags.empty.copy(isVal = true) val ps = PlainParamList(Param(flag, VarSymbol(Ident("errors")), N, Modulefulness(N)(false)) :: Nil) val ctsym = TermSymbol(Fun, S(cs), cs.id) cs.defn = S(ClassDef.Parameterized(N, syntax.Cls, cs, BlockMemberSymbol(cs.name, td :: Nil), S(ctsym), Nil, ps, Nil, N, ObjBody(Blk(Nil, Term.Lit(UnitLit(false)))), N, Nil)) cs -> ts val builtinOpsMap = val baseBuiltins = builtins.map: op => op -> BuiltinSymbol(op, binary = binaryOps(op), unary = unaryOps(op), nullary = false, functionLike = anyOps(op)) .toMap baseBuiltins ++ aliasOps.map: case (alias, base) => alias -> baseBuiltins(base) val andSymbol = builtinOpsMap("&&") val orSymbol = builtinOpsMap("||") def init(using State): Ctx = Ctx.empty.copy(env = Map( "globalThis" -> globalThisSymbol, )) def dbg: Bool = false def dbgRefNum(num: Int): Str = if dbg then s"#$num" else "" def dbgUid(uid: Uid[Symbol]): Str = if dbg then s"‹$uid›" else "" // ^ we do not display the uid by default to avoid polluting diff-test outputs // Create a term symbol for a function defined in the given module private def createFunSymbolInMod(name: Str, paramNames: List[Str], mod: ModuleOrObjectSymbol) = val sym = TermSymbol(syntax.Fun, N, Ident(name)) val bsym = BlockMemberSymbol(name, Nil, true) val ps = PlainParamList(paramNames.map(s => Param.simple(VarSymbol(Ident(s))))) sym.defn = S(TermDefinition(syntax.Fun, bsym, sym, ps :: Nil, N, N, N, TermDefFlags(true), Modulefulness(S(mod))(false), Nil, N)) sym transparent inline def State(using state: State): State = state end Elaborator import Elaborator.* class Elaborator(val tl: TraceLogger, val wd: io.Path, val prelude: Ctx) (using val raise: Raise, val state: State, val cctx: CompilerCtx, val config: Config) extends Importer with ucs.SplitElaborator: import tl.* given TraceLogger = tl lazy val illegalMemberNameTail = msg"Member names must start with a letter or underscore, followed by letters, digits, or underscores." -> N :: Nil def mkLetBinding(kw: Tree.Keywrd[?], sym: LocalSymbol, rhs: Term, annotations: Ls[Annot]): Ls[Statement] = LetDecl(sym, annotations).mkLocWith(kw, sym) :: DefineVar(sym, rhs) :: Nil def resolveField(srcTree: Tree, base: Opt[Symbol], nme: Ident): Opt[MemberSymbol] = base match case S(psym: BlockMemberSymbol) => psym.modOrObjTree match case S(cls) => cls.definedSymbols.get(nme.name) match case s @ S(clsSym) => s case N => raise(ErrorReport(msg"${cls.k.desc.capitalize} '${cls.symbol.nme }' does not contain member '${nme.name}'" -> srcTree.toLoc :: Nil)) S(ErrorSymbol(nme.name, srcTree)) case N => N case _ => N def annot(tree: Tree): Ctxl[Opt[Annot]] = tree match case Keywrd(kw @ ( Keyword.`abstract` | Keyword.`declare` | Keyword.`data` | Keyword.`staged` | Keyword.`public` | Keyword.`private` )) => S(Annot.Modifier(kw)) case App(Ident("config"), Tup(args)) => val modify = ConfigParser.parseOverrides(args) S(Annot.Config(modify)) case _ => term(tree) match case Term.Error => N case trm => trm.symbol match case S(sym) => sym.asTpe match case S(ctx.builtins.untyped) => return S(Annot.Untyped) case S(ctx.builtins.tailcall) => return S(Annot.TailCall) case S(ctx.builtins.tailrec) => return S(Annot.TailRec) case _ => () case _ => () S(Annot.Trm(trm)) def term(tree: Tree): Ctxl[Term] = trace[Term](s"Elab term ${tree.showDbg}", r => s"~> $r"): val unders = mutable.ArrayBuffer.empty[VarSymbol] given UnderCtx = new UnderCtx(S(unders)) val st = subterm(tree, inAppPrefix = false, inTyAppPrefix = false) val params = unders.iterator.map: sym => Param(FldFlags.empty, sym, N, Modulefulness.none) .toList if params.isEmpty then st else Term.Lam(PlainParamList(params), st) def subterm(tree: Tree, inAppPrefix: Bool = false, inTyAppPrefix: Bool = false): Ctxl[UnderCtx ?=> Term] = trace[Term](s"Elab subterm ${tree.showDbg}", r => s"~> $r"): tree.desugared match case Trm(term) => term case unt @ Unt() => unit.withLocOf(unt) case Bra(k, e) => k match case Round => case Curly => case _ => raise(ErrorReport(msg"Unsupported ${k.name} in this position" -> tree.toLoc :: Nil)) term(e) // * not `subterm` as `e` could be a lambda shorthand case b: Block => ctx.nestLocal("‹block›").givenIn: block(b, hasResult = true)._1 match case Term.Blk(Nil, res) => res case res => res case lit: Literal => Term.Lit(lit) case d: Def => subterm(Block(d :: Unt() :: Nil)) case LetLike(kw @ Keywrd(`let`), lhs, rhso, S(bod)) => subterm(Block(LetLike(kw, lhs, rhso, N) :: bod :: Nil)) case LetLike(kw @ Keywrd(`let`), lhs, rhso, N) => raise(ErrorReport( msg"Expected a body for let bindings in expression position" -> tree.toLoc :: Nil)) block(LetLike(kw, lhs, rhso, N) :: Nil, hasResult = true)._1 case LetLike(Keywrd(`set`), lhs, S(rhs), N) => Term.Assgn(subterm(lhs), subterm(rhs)) case LetLike(Keywrd(`set`), lhs, N, N) => raise(ErrorReport( msg"Expected a right-hand side for this assignment" -> tree.toLoc :: Nil)) Term.Error case LetLike(Keywrd(`set`), lhs, S(rhs), S(bod)) => // * Backtracking assignment if config.effectHandlers.isDefined then raise(ErrorReport( msg"Backtracking assignment is not supported with effect handlers enabled" -> tree.toLoc :: Nil)) Term.Error else val lt = subterm(lhs) val sym = TempSymbol(S(lt), "old") Blk( LetDecl(sym, Nil) :: DefineVar(sym, lt) :: Nil, Term.Try(Blk( Term.Assgn(lt, subterm(rhs)) :: Nil, subterm(bod), ), Term.Assgn(lt, sym.ref()))) case (hd @ Hndl(id: Ident, c, Block(sts_), S(bod))) => ctx.nest(OuterCtx.LambdaOrHandlerBlock).givenIn: val sym = fieldOrVarSym(HandlerBind, id) log(s"Processing `handle` statement $id (${sym}) ${ctx.outer}") val derivedClsSym = ClassSymbol(Tree.DummyTypeDef(syntax.Cls), Tree.Ident(s"Handler$$${id.name}$$")) derivedClsSym.defn = S(ClassDef( N, syntax.Cls, derivedClsSym, BlockMemberSymbol(derivedClsSym.name, Nil), N, Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(Tree.UnitLit(false)))), Nil, N)) val elabed = ctx.nestInner(derivedClsSym).givenIn: block(sts_, hasResult = false)._1 elabed.res match case Term.Lit(UnitLit(false)) => case trm => raise(WarningReport(msg"Terms in handler block do nothing" -> trm.toLoc :: Nil)) val tds = elabed.stats.map { case td @ TermDefinition(Fun, sym, tsym, params, tparams, sign, body, flags, mf, annotations, comp) => params.reverse match case ParamList(_, value :: Nil, _) :: newParams => if newParams.isEmpty then raise(ErrorReport(msg"Handler function cannot be a getter" -> td.toLoc :: Nil)) val newTd = TermDefinition(Fun, sym, tsym, newParams.reverse, tparams, sign, body, flags, mf, annotations, comp) S(HandlerTermDefinition(value.sym, newTd)) case _ => raise(ErrorReport(msg"Handler function is missing resumption parameter" -> td.toLoc :: Nil)) None case st => raise(ErrorReport(msg"Only function definitions are allowed in handler blocks" -> st.toLoc :: Nil)) None }.collect { case Some(x) => x } val (cp, p) = c match case App(c, Tup(params)) => (subterm(c), params.map(subterm(_))) case c => (subterm(c), Nil) (ctx + (id.name -> sym)).givenIn: Term.Handle(sym, cp, p, derivedClsSym, tds, subterm(bod)) case h: Hndl => raise(ErrorReport( msg"Unsupported handle binding shape" -> h.toLoc :: Nil)) Term.Error case id @ Ident("this") => ctx.getOuter match case S(sym) => sym.ref(id) case N => raise: ErrorReport(msg"Cannot use 'this' outside of an object scope" -> tree.toLoc :: Nil) Term.Error case id @ Ident("|" | "&") => raise: ErrorReport(msg"Unexpected use of special operator '${id.name}'" -> id.toLoc :: Nil) Term.Error case id @ Ident(name) => ident(id).getOrElse: raise(ErrorReport(msg"Name not found: $name" -> id.toLoc :: Nil)) Term.Error case TyApp(lhs, targs) => Term.TyApp(subterm(lhs, inTyAppPrefix = true), targs.map { case Modified(Keywrd(Keyword.`in`), arg) => Term.WildcardTy(S(subterm(arg)), N) case Modified(Keywrd(Keyword.`out`), arg) => Term.WildcardTy(N, S(subterm(arg))) case Tup(Modified(Keywrd(Keyword.`in`), arg1) :: Modified(Keywrd(Keyword.`out`), arg2) :: Nil) => Term.WildcardTy(S(subterm(arg1)), S(subterm(arg2))) case arg => subterm(arg) })(N).withLocOf(tree) case InfixApp(TyTup(tvs), Keywrd(Keyword.`->`), body) => val boundVars = mutable.HashMap.empty[Str, VarSymbol] def genSym(id: Tree.Ident) = val sym = VarSymbol(id) sym.decl = S(TyParam(FldFlags.empty, N, sym)) // TODO vce boundVars += id.name -> sym sym val syms = (tvs.collect: case id: Tree.Ident => (genSym(id), N, N) case InfixApp(id: Tree.Ident, Keywrd(Keyword.`extends`), ub) => (genSym(id), S(ub), N) case InfixApp(id: Tree.Ident, Keywrd(Keyword.`restricts`), lb) => (genSym(id), N, S(lb)) case InfixApp(InfixApp(id: Tree.Ident, Keywrd(Keyword.`extends`), ub), Keywrd(Keyword.`restricts`), lb) => (genSym(id), S(ub), S(lb)) ) val outer = (tvs.collect: case Outer(S(name: Tree.Ident)) => genSym(name) case Outer(N) => genSym(Tree.Ident("outer")) ) match case ot :: Nil => S(ot) case _ :: rest => raise(ErrorReport(msg"Only one outer variable can be bound." -> tree.toLoc :: Nil)) N case Nil => N if syms.length + outer.count(_ => true) =/= tvs.length then raise(ErrorReport(msg"Illegal forall annotation." -> tree.toLoc :: Nil)) Term.Error else given Ctx = ctx ++ boundVars val bds = syms.map: case (sym, ub, lb) => QuantVar(sym, ub.map(ub => subterm(ub)), lb.map(lb => subterm(lb))) Term.Forall(bds, outer, subterm(body)) case InfixApp(lhs, Keywrd(Keyword.`->`), Effectful(eff, rhs)) => Term.FunTy(subterm(lhs), subterm(rhs), S(subterm(eff))) case InfixApp(lhs, Keywrd(Keyword.`->`), rhs) => Term.FunTy(subterm(lhs), subterm(rhs), N) case InfixApp(lhs, Keywrd(Keyword.`=>`), rhs) => lhs match case Tup(_) => ctx.nest(OuterCtx.LambdaOrHandlerBlock).givenIn: val (syms, nestCtx) = funParams(lhs) Term.Lam(syms, term(rhs)(using nestCtx)) case TyTup(tys) => val constraints = tys.flatMap(maybeConstraint) val body = term(rhs) Term.Constrained(constraints, body) case InfixApp(lhs, Keywrd(Keyword.`as`), rhs) => Term.Asc(subterm(lhs), subterm(rhs)) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) => block(tree :: Nil, hasResult = false)._1 case PrefixApp(kw @ Keywrd(Keyword.`not`), rhs) => Term.App(State.builtinOpsMap("!").ref(new Ident("not").withLocOf(kw)), Term.Tup( PlainFld(subterm(rhs, inAppPrefix = true)) :: Nil)(DummyTup))(DummyApp, N, FlowSymbol("not-app")) case tree @ InfixApp(lhs, Keywrd(Keyword.`is` | Keyword.`and` | Keyword.`or`), rhs) => Term.IfLike(Keyword.`if`, IfLikeForm.ReturningIf, shorthandSplit(tree)) case InfixApp(Sel(pre, idn: Ident), Keywrd(Keyword.`#`), idp: Ident) => val c = subterm(idn) val f = c.symbol.flatMap(_.asCls) match case S(cls: ClassSymbol) => cls.tree.allSymbols.get(idp.name) match case S(fld: MemberSymbol) => S(fld) case _ => raise(ErrorReport(msg"Class '${cls.nme}' does not contain member '${idp.name}'." -> idp.toLoc :: Nil)) N case _ => raise(ErrorReport(msg"Identifier `${idn.name}` does not name a known class symbol." -> idn.toLoc :: Nil)) N Term.SelProj(subterm(pre), c, idp)(f, FlowSymbol.selProj(idp.name), N, S(summon)) case InfixApp(lhs, kw, rhs) => raise: ErrorReport(msg"Unexpected infix use of keyword '${kw.name}' here" -> tree.toLoc :: Nil) Term.Error case OpApp(lhs, Ident("|"), rhs :: Nil) => Term.CompType(subterm(lhs), subterm(rhs), true) case OpApp(lhs, Ident("&"), rhs :: Nil) => Term.CompType(subterm(lhs), subterm(rhs), false) case OpApp(lhs, Ident(":="),rhs :: Nil) => Term.SetRef(subterm(lhs), subterm(rhs)) case App(Ident("!"), Tup(rhs :: Nil)) => Term.Deref(subterm(rhs)) case App(Ident("~"), Tup(rhs :: Nil)) => Term.Neg(subterm(rhs)) case App(Ident("|" | "&"), Tup(rhs :: Nil)) => subterm(rhs) case tree @ OpSplit(lhs, rhss) => val tree = rhss.foldLeft(lhs): case (acc, rhs) => rhs.splitOn(acc) subterm(tree) case tree @ App(lhs, rhs) => val sym = FlowSymbol.app() val lt = subterm(lhs, inAppPrefix = true) val rt = subterm(rhs) Term.App(lt, rt)(tree, N, sym) case tree @ OpApp(lhs, op, rhss) => val sym = FlowSymbol.app() val lt = subterm(lhs, inAppPrefix = true) val ot = subterm(op, inAppPrefix = true) val rts = rhss.map(r => PlainFld(subterm(r))) Term.App(ot, Term.Tup(PlainFld(lt) :: rts)(DummyTup))( DummyApp, N, sym) case SynthSel(pre, nme) => val preTrm = subterm(pre) val sym = resolveField(nme, preTrm.symbol, nme) Term.SynthSel(preTrm, nme)(sym, FlowSymbol.synthSel(nme.name), N, S(summon)).withLocOf(tree) case Sel(Empty(), nme) => Term.LeadingDotSel(nme)(S(summon)).withLocOf(tree) case Sel(pre, nme) => val preTrm = subterm(pre) val sym = resolveField(nme, preTrm.symbol, nme) sym match // * Enforcing [invariant:1] case S(ms: BlockMemberSymbol) // FIXME[Harry]: move the check to resolver because preTrm's symbol may not be resolved yet. if // * If we're selecting a parameterized class method without applying it, an error should be reported. // * Note that module methods are fine to select without applying, since they don't use `this`. !inAppPrefix && ms.isParameterizedMethod && !preTrm.symbol.exists(_.existsModuleful) => raise: ErrorReport( msg"[debinding error] Method '${nme.name}' cannot be accessed without being called." -> nme.toLoc :: Nil) case S(_) | N => () if sym.contains(ctx.builtins.source.line) then val loc = tree.toLoc.getOrElse(???) val (line, _, _) = loc.origin.fph.getLineColAt(loc.spanStart) Term.Lit(IntLit(loc.origin.startLineNum + line)) else if sym.contains(ctx.builtins.source.name) then Term.Lit(StrLit(ctx.getOuter.map(_.nme).getOrElse(""))) else if sym.contains(ctx.builtins.source.file) then val loc = tree.toLoc.getOrElse(???) Term.Lit(StrLit(loc.origin.fileName.toString)) else Term.Sel(preTrm, nme)(sym, FlowSymbol.sel(nme.name), N, S(summon)) case MemberProj(ct, nme) => val c = subterm(ct) val f = c.symbol.flatMap(_.asCls) match case S(cls: ClassSymbol) => cls.tree.allSymbols.get(nme.name) match case S(fld: MemberSymbol) => S(fld) case _ => raise(ErrorReport(msg"Class '${cls.nme}' does not contain member '${nme.name}'." -> nme.toLoc :: Nil)) N case _ => raise: ErrorReport: msg"${ct.describe.capitalize} is not a known class." -> ct.toLoc :: msg"Note: any expression of the form `‹expression›::‹identifier›` is a member projection;" -> N :: msg" add a space before ‹identifier› to make it an operator application." -> N :: Nil N val self = VarSymbol(Ident("self")) val args = VarSymbol(Ident("args")) val ps = ParamList(ParamListFlags.empty, Param(FldFlags.empty, self, N, Modulefulness.none) :: Nil, S: Param(FldFlags.empty, args, N, Modulefulness.none) ) val rs = FlowSymbol.app() Term.Lam(ps, Term.App(Term.SelProj(self.ref(), c, nme)(f, FlowSymbol.selProj(nme.name), N, S(summon)), args.ref())( App(nme, Tup(Nil)) // FIXME , N, rs) ) case tree @ Tup(TermDef(Ins, f, N) :: fs) => Term.CtxTup((f :: fs).map(fld(_)))(tree) case Modified(kw @ Keywrd(Keyword.`mut`), tree @ Tup(fields)) => Term.Mut(Term.Tup(fields.map(fld(_)))(tree)).mkLocWith(kw) case tree @ Tup(fields) => Term.Tup(fields.map(fld(_)))(tree) case DynamicNew(Apps(c, args)) => val (mut, c2) = c match case Modified(Keywrd(Keyword.`mut`), c) => (true, c) case c => (false, c) val base = new Term.DynNew(subterm(c2, inAppPrefix = inAppPrefix), args.map(subterm(_))).withLocOf(tree) if mut then Term.Mut(base) else base // case New(c, rfto) => // assert(rfto.isEmpty) // Term.New(cls(subterm(c), inAppPrefix = inAppPrefix), params.map(subterm(_)), bodo).withLocOf(tree) case ProperNew(body, rfto) => // TODO handle Under lazy val bodo = rfto.map: rft => val clsSym = new ClassSymbol(DummyTypeDef(syntax.Cls), Ident("$anon")) ctx.nestInner(clsSym).givenIn: clsSym -> // TODO integrate context inherited from cls // TODO make context with var symbols for class parameters ObjBody(block(rft, hasResult = false)._1) body match case S(Apps(c, args)) => val (mut, c2) = c match case Modified(Keywrd(Keyword.`mut`), c) => (true, c) case c => (false, c) val inner = new Term.New( subterm(c2), // * Note: we'll catch bad `new` targets during type checking args.map(subterm(_)), bodo )(N).withLocOf(tree) if mut then Term.Mut(inner) else inner case N => Term.New(State.globalThisSymbol.ref().sel(Ident("Object"), S(ctx.builtins.Object)), Nil, bodo)(N).withLocOf(tree) // case _ => // raise(ErrorReport(msg"Illegal new expression." -> tree.toLoc :: Nil)) case tree: IfLike => split(tree) case Assert(kw, rhs, thno, els) => val (fl, ln) = kw.toLoc match case S(loc) => val org = loc.origin (org.fileName.relativeTo(config.baseDir).getOrElse(org.fileName).toString, (org.startLineNum + org.fph.getLineColAt(loc.spanStart)._1).toString) case N => ("‹unknown›", "‹unknown›") val elsPart = els.fold(PrefixApp(Keywrd(Keyword.`else`), Tree.Trm( State.runtimeSymbol.ref().selNoSym("assertFail") .app(Term.Lit(StrLit(fl)), Term.Lit(StrLit(ln))) )))(PrefixApp.apply.tupled) subterm: IfLike(new Keywrd(Keyword.`if`).withLocOf(kw), Block( InfixApp(rhs, new Keywrd(Keyword.`then`), thno.getOrElse(Unt())) :: elsPart :: Nil)) case Quoted(body) => Term.Quoted(subterm(body)) case Unquoted(body) => Term.Unquoted(subterm(body)) case tree @ Case(kw, _) => val scrut = VarSymbol(Ident("caseScrut")) val body = caseSplit(scrut, tree) val params = Param(FldFlags.empty, scrut, N, Modulefulness.none) :: Nil Term.Lam(PlainParamList(params), body).mkLocWith(kw) case PrefixApp(kw @ Keywrd(Keyword.`return`), body) => ctx.getRetHandler match case ReturnHandler.Required(sym) => log(s"Non-local return: $sym") if config.effectHandlers.isEmpty then raise: ErrorReport(msg"Non-local return statements are only supported with effect handlers enabled." -> tree.toLoc :: Nil) Term.Error else val rs = FlowSymbol.app() val retMtdTree = new Ident("ret") val argTree = new Tup(body :: Nil) val dummyIdent = new Ident("return").withLocOf(kw) Term.App( Term.Sel(sym.ref(dummyIdent), retMtdTree)(S(state.nonLocalRet), FlowSymbol.sel(dummyIdent.name), N, S(summon)), Term.Tup(PlainFld(subterm(body)) :: Nil)(argTree) )(App(Sel(dummyIdent, retMtdTree), argTree), N, rs) case ReturnHandler.NotInFunction => raise: ErrorReport(msg"Return statements are not allowed outside of functions." -> tree.toLoc :: Nil) Term.Error case ReturnHandler.Direct => Term.Ret(subterm(body)) case ReturnHandler.Forbidden => raise: ErrorReport(msg"Return statements are not allowed in this context." -> tree.toLoc :: Nil) Term.Error case PrefixApp(kw @ Keywrd(Keyword.`throw`), body) => Term.Throw(subterm(body)).mkLocWith(kw) case PrefixApp(kw @ Keywrd(Keyword.`do`), body) => Blk(subterm(body) :: Nil, unit).mkLocWith(kw) case PrefixApp(kw @ Keywrd(Keyword.`drop`), body) => Term.Drop(subterm(body)).mkLocWith(kw) case Region(id: Ident, body) => val sym = VarSymbol(id) given Ctx = ctx + (id.name -> sym) Term.Region(sym, subterm(body)) case RegRef(reg, value) => Term.RegRef(subterm(reg), subterm(value)) case Outer(S(_)) => raise(ErrorReport(msg"Illegal outer binding." -> tree.toLoc :: Nil)) Term.Error case Outer(N) => ctx.get("outer") match case S(sym) => sym.ref(Ident("outer")) case N => raise(ErrorReport(msg"Illegal outer reference." -> tree.toLoc :: Nil)) Term.Error case Empty() => raise(ErrorReport(msg"A term was expected in this position, but no term was found." -> tree.toLoc :: Nil)) Term.Error case Error() => Term.Error case TermDef(k, nme, rhs) => raise(ErrorReport(msg"Illegal definition in term position." -> tree.toLoc :: Nil)) Term.Error case TypeDef(k, head, rhs) => raise(ErrorReport(msg"Illegal type declaration in term position." -> tree.toLoc :: Nil)) Term.Error case Modified(Keywrd(Keyword.`mut`), body: Block) => blockOrRcd(body, hasResult = true) match case (Blk(Nil, Term.UnitVal()), ctx) => Rcd(mut = true, Nil).withLocOf(body) case (blk: Blk, ctx) => raise(ErrorReport(msg"Expected a record after 'mut' keyword; found a block" -> blk.toLoc :: Nil)) blk case (rcd: Rcd, ctx) => rcd.copy(mut = true).withLocOf(rcd) case Modified(kw, body) => raise(ErrorReport(msg"Illegal position for '${kw.name}' modifier." -> kw.toLoc :: Nil)) subterm(body) case PrefixApp(kw, body) => raise(ErrorReport(msg"Illegal position for prefix keyword '${kw.name}'." -> kw.toLoc :: Nil)) subterm(body) case Jux(lhs, rhs) => def go(acc: Term, trees: Ls[Tree]): Term = trees match case Nil => acc // * FIXME this `f.name.head.isLetter` test is a big hack... // * TODO would be better to keep the fixity of applications part of the Tree repr. case (ap @ App(f: Ident, tup @ Tup(lhs :: args))) :: trees if !f.name.head.isLetter => val res = go(acc, lhs :: Nil) val sym = FlowSymbol.app() val fl = Fld(FldFlags.empty, res, N) val app = Term.App(subterm(f, inAppPrefix = true), Term.Tup( fl :: args.map(fld))(tup))(ap, N, sym) go(app, trees) case (ap @ App(f, tup @ Tup(args))) :: trees => val sym = FlowSymbol.app() go(Term.App(subterm(f, inAppPrefix = true), Term.Tup(Fld(FldFlags.empty, acc, N) :: args.map(fld))(tup) )(ap, N, sym), trees) case Block(sts) :: trees => go(acc, sts ::: trees) case tree :: trees => raise(ErrorReport(msg"Illegal juxtaposition right-hand side (${tree.describe})." -> tree.toLoc :: Nil)) go(acc, trees) go(subterm(lhs), rhs :: Nil) case Open(op) => raise(ErrorReport(msg"Illegal position for 'open' statement." -> tree.toLoc :: Nil)) Term.Error case OpenIn(op, body) => subterm(Block(Open(op) :: body :: Nil), inAppPrefix) case DynAccess(obj, rhs) => rhs match case Bra(bk @ (Round | Square), fld) => Term.DynSel(subterm(obj), subterm(fld), bk is Square) case fld: Literal => Term.DynSel(subterm(obj), subterm(fld), false) case id: Ident => Term.DynSel(subterm(obj), Term.Lit(StrLit(id.name)).withLocOf(id), false) case _ => raise(ErrorReport(msg"Illegal dynamic field access selector (${rhs.describe})." -> tree.toLoc :: Nil)) Term.Error case Spread(kw, body) => raise(ErrorReport(msg"Illegal position for '${kw.name}' spread operator." -> kw.toLoc :: Nil)) Term.Error case und: Under => summon[UnderCtx].unders match case N => raise(ErrorReport(msg"Illegal position for '_' placeholder." -> tree.toLoc :: Nil)) Term.Error case S(unds) => val sym = VarSymbol(Ident("_" + unds.size)) unds += sym sym.ref() case Annotated(lhs, rhs) => annot(lhs).fold(subterm(rhs))(ann => Term.Annotated(ann, subterm(rhs))) case Keywrd(kw) => raise(ErrorReport(msg"Unexpected keyword '${kw.name}' in this position." -> tree.toLoc :: Nil)) Term.Error case Constructor(delc) => raise(ErrorReport(msg"Unsupported constructor in this position." -> tree.toLoc :: Nil)) Term.Error // case _ => // ??? def arg(tree: Tree)(using UnderCtx): Ctxl[Term] = tree match case u: Under => subterm(tree) // Note: currently `f(a, _, c)` is treated the same as `f of a, _, c` case _ => term(tree) def fld(tree: Tree)(using UnderCtx): Ctxl[Elem] = tree match case InfixApp(id: Ident, Keywrd(Keyword.`:`), rhs) => Fld(FldFlags.empty, Term.Lit(StrLit(id.name).withLocOf(id)), S(arg(rhs))) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) => Fld(FldFlags.empty, term(lhs), S(arg(rhs))) case Spread(Keywrd(Keyword.`..`), S(trm)) => Spd(SpreadKind.Lazy, arg(trm)) case Spread(Keywrd(Keyword.`...`), S(trm)) => Spd(SpreadKind.Eager, arg(trm)) case _ => val t = arg(tree) var flags = FldFlags.empty Fld(flags, t, N) def unit: Term.UnitVal = Term.UnitVal() def block(sts: Ls[Tree], hasResult: Bool)(using UnderCtx): Ctxl[(Blk, Ctx)] = block(new Block(sts), hasResult) def block(blk: Block, hasResult: Bool)(using UnderCtx): Ctxl[(Blk, Ctx)] = blockOrRcd(blk, hasResult) match case (blk: Blk, ctx) => (blk, ctx) case (rcd: Rcd, ctx) => (Blk(Nil, rcd), ctx) val supportedOverloadings: Set[(OuterKind, OuterKind)] = Set( Cls -> Mod, Obj -> Mod, Als -> Mod, ) val notYetSupportedOverloadings: Set[(OuterKind, OuterKind)] = Set( Fun -> Cls, Fun -> Mod, Pat -> Mod, ImmutVal -> Mod, MutVal -> Mod, ) // * Some blocks do not have a meaningful result, // * e.g., constructor blocks or top-level blocks (in MLscript files and diff-tests); // * for these, elaborate with `hasResult = false`, which uses `undefined` as the result // * when there is no other result available. This is fine since the value is never used. // * These useless trailing `undefined`s are then removed by `Lowering`. def blockOrRcd(blk: Block, hasResult: Bool)(using UnderCtx) : Ctxl[(Blk | Rcd, Ctx)] = trace[(Blk | Rcd, Ctx)]( pre = s"Elab block ${blk.desugStmts.toString.truncate(100, "[...]")} ${ctx.outer}", r => s"~> ${r._1}" ): val members = blk.definedSymbols.toMap val newSignatureTrees = mutable.Map.empty[Str, Tree] // * Store trees of signatures // * Check for double/incompatible definitions and declarations blk.definedSymbols.foreach: (name, sym) => if sym.nme === name then // * This is not true when `name` is the symbolic name of a member sym.trees.foreach: td => td.symbName match case S(R(id)) => val mem = members.getOrElse(id.name, die) if mem isnt sym then raise: ErrorReport: msg"Symbolic name '${id.name}' of ${ td.name.fold(_ => "this definition", id => "definition '" + id.name + "'") } is already used" -> td.toLoc :: msg"by sibling member '${mem.nme}'" -> mem.toLoc :: Nil case _ => () val defns = sym.trees.collect: case td: TermDef if td.rhs.isDefined && td.name.exists(_.name === name) => td case td: TypeDef if td.name.exists(_.name === name) => td if defns.sizeCompare(1) > 0 then val groups = defns.groupMapReduce(_.k)(_ :: Nil)(_ ::: _) val sortedGroups = groups.toArray.sortBy(_._1) sortedGroups.iterator.foreach: (k, group) => if group.size > 1 then raise(ErrorReport(msg"Multiple definitions of symbol '$name'" -> N :: group.map(msg"defined here" -> _.toLoc))) val mainDefn = group.head // * Safe since these `groupMapReduce` groups cannot be empty log(s"Processing overloadings for '$name'") defns.iterator.foreach: defn => if defn.k > k then if !supportedOverloadings(k -> defn.k) then raise: ErrorReport: if notYetSupportedOverloadings(k -> defn.k) then msg"Not yet supported: overloading of ${k.desc} '$name'" -> mainDefn.toLoc :: msg"with ${defn.k.desc} of the same name" -> defn.toLoc :: Nil else msg"Illegal overloading of ${k.desc} '$name'" -> mainDefn.toLoc :: msg"with ${defn.k.desc} of the same name" -> defn.toLoc :: Nil val decls = sym.trees.collect: case td: TermDef if td.rhs.isEmpty => td if decls.length > 1 then raise(ErrorReport(msg"Multiple declarations of symbol '$name'" -> N :: decls.map(msg"declared here" -> _.toLoc))) val sig = decls.collectFirst: case td if td.annotatedResultType.isDefined && td.paramLists.isEmpty => td.annotatedResultType.get sig.foreach: sig => newSignatureTrees += name -> sig // TODO extract this into a separate method // * @param funs: // * While elaborating a block, we move all function definitions to the top (similar to JS function semantics) @tailrec def go(sts: Ls[Tree], annotations: Ls[Annot], acc: Ls[Statement]): Ctxl[(Blk | Rcd, Ctx)] = /** Call this function when the following term cannot be annotated. */ def reportUnusedAnnotations: Unit = if annotations.nonEmpty then raise: WarningReport: msg"This annotation has no effect" -> (annotations.foldLeft[Opt[Loc]](N): case (acc, ann) => acc match case N => ann.toLoc case S(loc) => S(loc ++ ann.toLoc) ) :: (sts.headOption match case N => msg"A target term is expected at the end of block" -> blk.toLoc.map(_.right) case S(head) => msg"Annotations are not supported on ${head.describe} terms." -> head.toLoc ) :: Nil sts match case Nil => reportUnusedAnnotations (mkBlk(acc, N, hasResult), ctx) case Constructor(Block(ctors)) :: sts => // TODO properly handle (it currently desugars to sibling classes) go(sts, annotations, acc) case Open(bod) :: sts => reportUnusedAnnotations bod match case Jux(bse, Block(sts)) => some(bse -> some(sts)) // * There could be other shapes of open statements... case bse: Ident => some(bse -> N) case _ => raise(ErrorReport(msg"Illegal 'open' statement shape." -> bod.toLoc :: Nil)) N match case N => go(sts, annotations, acc) case S((base, importedTrees)) => base match case baseId: Ident => ctx.get(baseId.name) match case S(baseElem) => val importedNames = importedTrees match case N => // "wilcard" open baseElem.symbol match case S(sym: BlockMemberSymbol) if sym.modOrObjTree.isDefined => sym.modOrObjTree.get.definedSymbols.map: case (nme, sym) => nme -> Ctx.SelElem(baseElem, sym.nme, S(sym), isImport = true) case _ => raise(ErrorReport(msg"Wildcard 'open' not supported for this kind of symbol." -> baseId.toLoc :: Nil)) Nil case S(sts) => sts.flatMap: case id: Ident => if ctx.env.contains(id.name) then raise(WarningReport(msg"Imported name '${id.name}' is shadowed by a name already defined in the same scope" -> id.toLoc :: Nil)) val sym = resolveField(id, baseElem.symbol, id) val e = Ctx.SelElem(baseElem, id.name, sym, isImport = true) id.name -> e :: Nil case t => raise(ErrorReport(msg"Illegal 'open' statement element." -> t.toLoc :: Nil)) Nil (ctx elem_++ importedNames).givenIn: go(sts, Nil, acc) case N => raise(ErrorReport(msg"Name not found: ${baseId.name}" -> baseId.toLoc :: Nil)) go(sts, Nil, acc) case _ => raise(ErrorReport(msg"Illegal 'open' statement base." -> base.toLoc :: Nil)) go(sts, Nil, acc) case (m @ PrefixApp(Keywrd(Keyword.`import`), arg)) :: sts => reportUnusedAnnotations val pathAndAlias: Opt[(Tree, Opt[Ident])] = arg match case InfixApp(pathArg, Keywrd(Keyword.`as`), alias: Ident) => S((pathArg, S(alias))) case InfixApp(pathArg, Keywrd(Keyword.`as`), Error()) => N case InfixApp(_, Keywrd(Keyword.`as`), badAlias) => raise(ErrorReport( msg"Expected identifier after 'as' in import statement" -> badAlias.toLoc :: Nil)) N case pathArg => S((pathArg, N)) val (newCtx, newAcc) = pathAndAlias match case S((StrLit(path), alias)) => val stmt = importPath(path, alias).withLocOf(m) (ctx + (stmt.sym.nme -> stmt.sym), stmt :: acc) case S((pathArg, _)) => raise(ErrorReport( msg"Expected string literal after 'import' keyword" -> pathArg.toLoc :: Nil)) (ctx, acc) case N => // errors have been reported above. (ctx, acc) newCtx.givenIn: go(sts, Nil, newAcc) case Spread(Keywrd(Keyword.`...`), S(body)) :: sts => reportUnusedAnnotations go(sts, Nil, RcdSpread(term(body)) :: acc) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) :: sts => var newCtx = ctx val (rlhs, rhs_t) = rhs match case _: Under => (lhs, subterm(rhs)) case _ => lhs match case Apps(base, tups) => val rrhs = tups.foldRight(rhs): InfixApp(_, Keywrd(Keyword.`=>`), _) (base, term(rrhs)) val newAcc = rlhs match case id: Ident => val sym = new VarSymbol(id) newCtx += id.name -> sym RcdField(Term.Lit(StrLit(id.name)).withLocOf(id), sym.ref(id)) :: DefineVar(sym, rhs_t) :: LetDecl(sym, annotations) :: acc case lit: Literal => reportUnusedAnnotations RcdField(Term.Lit(lit).withLocOf(lit), rhs_t) :: acc case Bra(Round, inner) => reportUnusedAnnotations RcdField(term(inner), rhs_t) :: acc case _ => raise(ErrorReport(msg"Unexpected record key shape." -> rlhs.toLoc :: Nil)) RcdField(Term.Error, rhs_t) :: acc newCtx.givenIn: go(sts, Nil, newAcc) case (hd @ LetLike(kw @ Keywrd(`let`), Apps(id: Ident, tups), rhso, N)) :: sts if tups.isEmpty || id.name.headOption.exists(_.isLower) => val sym = fieldOrVarSym(LetBind, id) log(s"Processing `let` statement $id (${sym}) ${ctx.outer}") members.get(id.name).foreach: s => raise(ErrorReport(msg"Name '${id.name}' is already used" -> hd.toLoc :: msg"by a member declared in the same block" -> s.toLoc :: Nil)) val newAcc = rhso match case S(rhs) => val rrhs = tups.foldRight(rhs): InfixApp(_, Keywrd(Keyword.`=>`), _) mkLetBinding(kw, sym, term(rrhs), annotations) reverse_::: acc case N => if tups.nonEmpty then raise(ErrorReport(msg"Expected a right-hand side for let bindings with parameters" -> hd.toLoc :: Nil)) LetDecl(sym, annotations).mkLocWith(kw) :: acc (ctx + (id.name -> sym)) givenIn: go(sts, Nil, newAcc) case (tree @ LetLike(Keywrd(`let`), lhs, _, N)) :: sts => raise(ErrorReport(msg"Unsupported let binding shape" -> tree.toLoc :: Nil)) go(sts, Nil, Term.Error :: acc) case Def(lhs, rhs) :: sts => reportUnusedAnnotations lhs match case id: Ident => val r = term(rhs) ctx.get(id.name) match case S(elem) => elem.symbol match case S(sym: LocalSymbol) => go(sts, Nil, DefineVar(sym, r) :: acc) case N => // TODO lookup in members? inherited/refined stuff? raise(ErrorReport(msg"Name not found: ${id.name}" -> id.toLoc :: Nil)) go(sts, Nil, Term.Error :: acc) case App(base, args) => go(Def(base, InfixApp(args, Keywrd(Keyword.`=>`), rhs)) :: sts, Nil, acc) case _ => raise(ErrorReport(msg"Unrecognized definitional assignment left-hand side: ${lhs.describe}" -> lhs.toLoc :: Nil)) // TODO BE go(sts, Nil, Term.Error :: acc) case (td @ TermDef(k, nme, rhs)) :: sts => log(s"Processing term definition $nme") td.symbName match case S(L(d)) => raise(d) case _ => () td.name match case R(id) => val sym = members.getOrElse(id.name, die) val owner = ctx.outer.inner if owner.isDefined && !identifierPattern.matches(id.name) then raise: ErrorReport: msg"Illegal member ${k.desc} name: '${id.name}'" -> nme.toLoc :: illegalMemberNameTail return go(sts, Nil, acc) val isMethod = owner.exists(_.isInstanceOf[ClassSymbol]) val tdf = ctx.nest(OuterCtx.NonReturnContext).givenIn: newCtx ?=> // * Add type parameters to context val (tps, newCtx1) = td.typeParams match case S(t) => val (tps, ctx) = typeParams(t) (S(tps), ctx) case N => (N, ctx) // * Add parameters to context var newCtx = newCtx1 val pss = td.paramLists.map: ps => val (res, newCtx2) = funParams(ps)(using newCtx) newCtx = newCtx2 res // * Elaborate signature val st = td.annotatedResultType.orElse(newSignatureTrees.get(id.name)) val s = st.map: // unwrap possible module modifier // e.g, `fun f: module M` // ^^^^^^ case TypeDef(Mod, st, N) => term(st)(using newCtx) case st => term(st)(using newCtx) val body: Opt[Term] = rhs match case N => N case _ if ctx.mode is Mode.Light => S(Term.Missing) case S(rhs) => S: val nonLocalRetHandler = TempSymbol(N, s"nonLocalRetHandler$$${id.name}") newCtx.nest(OuterCtx.Function(nonLocalRetHandler)).givenIn: newCtx ?=> val b = term(rhs)(using newCtx) if nonLocalRetHandler.directRefs.isEmpty then b else val clsSym = ClassSymbol(DummyTypeDef(Cls), Ident("‹non-local return effect›")) val valueSym = VarSymbol(Ident("value")) val resumeSym = VarSymbol(Ident("resume")) val mtdSym = BlockMemberSymbol("ret", Nil, true) val tsym = TermSymbol(Fun, N, Ident("ret")) val td = TermDefinition( Fun, mtdSym, tsym, PlainParamList(Param(FldFlags.empty, valueSym, N, Modulefulness.none) :: Nil) :: Nil, N, N, S(valueSym.ref(Ident("value"))), TermDefFlags.empty, Modulefulness.none, Nil, N) tsym.defn = S(td) mtdSym.tsym = S(tsym) val htd = HandlerTermDefinition(resumeSym, td) Term.Handle(nonLocalRetHandler, state.nonLocalRetHandlerTrm, Nil, clsSym, htd :: Nil, b) val r = FlowSymbol(s"‹result of ${sym}›") val mfn = st match // st.isModified(Mod) indicates if the function marks // its result as "module". e.g, `fun f: module M` // ^^^^^^ case S(st) if st.isModified(Mod) => Modulefulness.ofSign(s)(true) case _ => Modulefulness.none val tsym = TermSymbol(k, owner, id) // TODO? val tdf = TermDefinition(k, sym, tsym, pss, tps, s, body, TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N).withLocOf(td) tsym.defn = S(tdf) sym.tsym = S(tsym) tdf go(sts, Nil, tdf :: acc) case L(d) => reportUnusedAnnotations raise(d) go(sts, Nil, acc) case (td @ TypeDef(k, head, rhs)) :: sts => val owner = ctx.outer.inner assert((k is Als) || (k is Cls) || (k is Mod) || (k is Obj) || (k is Pat), k) val body = td.withPart td.symbName match case S(L(d)) => raise(d) case _ => () val nme = td.name match case R(id) => id case L(d) => raise(d) return go(sts, Nil, acc) if owner.isDefined && !identifierPattern.matches(nme.name) then raise: ErrorReport: msg"Illegal member ${k.desc} name: '${nme.name}'" -> nme.toLoc :: illegalMemberNameTail return go(sts, Nil, acc) val sym = members.getOrElse(nme.name, lastWords(s"Symbol not found: ${nme.name}")) val outerCtx = ctx var newCtx = S(td.symbol).collectFirst: case s: InnerSymbol => s .fold(ctx.nest(OuterCtx.NonReturnContext))(ctx.nestInner(_)) val tps = td.typeParams match case S(ts) => ts.tys.flatMap: targ => val (id, vce) = targ match case id: Ident => (id, N) case Modified(Keywrd(Keyword.`in`), id: Ident) => (id, S(false)) case Modified(Keywrd(Keyword.`out`), id: Ident) => (id, S(true)) val vs = VarSymbol(id) val res = TyParam(FldFlags.empty, vce, vs) vs.decl = S(res) res :: Nil case N => Nil newCtx ++= tps.map(tp => tp.sym.name -> tp.sym) // TODO: correct ++? val isDataClass = annotations.exists: case Annot.Modifier(Keyword.`data`) => true case _ => false val pss = td.paramLists.map: ps => val (res, newCtx2) = given Ctx = newCtx params(ps, isDataClass, k is Pat) newCtx = newCtx2 res def withFields(using Ctx)(fn: (Ctx) ?=> (Term.Blk, Ctx)): (Term.Blk, Ctx) = softAssert(pss.sizeCompare(td.clsParams) === 0, s"mismatched parameter list numbers ${pss} vs ${td.clsParams}") val fields: Ls[Statement] = pss.zip(td.clsParams).flatMap: (ps, cps) => // TODO: handle this gracefully (could be caused by erroneous input code) softTODO(ps.params.sizeCompare(cps) === 0, s"mismatched param list lengths ${ps.params} vs ${cps}") ps.params.zip(cps).flatMap: (p, cp) => // For class-like types, "desugar" the parameters into additional class fields. val owner = td.symbol match // Any MemberSymbol should be an InnerSymbol, except for TypeAliasSymbol, // but type aliases should not call this function. case s: InnerSymbol => S(s) case _: TypeAliasSymbol => die if p.flags.isVal || isDataClass then val k = if p.flags.mut then MutVal else ImmutVal val fsym = BlockMemberSymbol(p.sym.nme, Nil) val tsym = cp cp.decl = S(p) val fdef = TermDefinition( k, fsym, tsym, Nil, N, N, S(p.sym.ref()), TermDefFlags.empty.copy(isMethod = (k is Cls)), p.modulefulness, Nil, N, ).withLocOf(p) assert(p.fldSym.isEmpty) p.fldSym = S(fsym) fsym.tsym = S(tsym) tsym.defn = S(fdef) fdef :: Nil else val psym = TermSymbol(LetBind, owner, p.sym.id) val decl = LetDecl(psym, Nil) val defn = DefineVar(psym, p.sym.ref()) p.fldSym = S(psym) decl :: defn :: Nil val ctxWithFields = val valParams = fields.collect: case f: TermDefinition => f.sym.nme -> f.sym val params = fields.collect: case (f: LetDecl) => f.sym.nme -> f.sym ctx.withMembers(valParams) ++ params val (blk, c) = fn(using ctxWithFields) val blkWithFields: Blk = blk.copy(stats = fields ::: blk.stats) ObjBody.extractMembers(blkWithFields) match case R(_) => (blkWithFields, c) case L(errs) => errs.foreach(raise) (blk, c) def mkBody(using Ctx) = withFields: body match case N | S(Error()) => (new Blk(Nil, Term.Lit(UnitLit(false))), ctx) case S(b: Block) => block(b, hasResult = false) case S(t) => raise(ErrorReport( msg"Illegal body of ${k.desc} definition (should be a block; found ${t.describe})." -> t.toLoc :: Nil)) (new Blk(Nil, Term.Lit(UnitLit(false))), ctx) val defn = k match case Als => val alsSym = td.symbol.asInstanceOf[TypeAliasSymbol] // TODO improve `asInstanceOf` // newCtx.nest(S(alsSym)).givenIn: newCtx.nestLocal.givenIn: assert(pss.isEmpty) assert(body.isEmpty) val d = given Ctx = newCtx semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_)), N, annotations) alsSym.defn = S(d) d case Pat => val patSym = td.symbol.asInstanceOf[PatternSymbol] // TODO improve `asInstanceOf` newCtx.givenIn: if pss.length > 1 then raise: ErrorReport: msg"Multiple parameter lists are not supported for this definition." -> td.toLoc :: Nil // Pattern definition should not have a body like class definition. assert(body.isEmpty) val ps = pss.headOption val allParams = ps.fold(Nil): _.params.flatMap: // Only `pat` flag is `true`. case p @ Param(flags = FldFlags(false, false, true, false)) => S(p) // All flags are `false`. case p @ Param(flags = FldFlags(false, false, false, false)) => S(p) case Param(flags, sym, _, _) => raise(ErrorReport(msg"Unexpected pattern parameter ${sym.name} with modifiers: ${flags.show}" -> sym.toLoc :: Nil)) N // The following iteration filters out: // 1. pattern parameters, e.g., `T` in `pattern Nullable(pattern T) = ...`; // 2. extraction bindings, e.g., `value` in `pattern Middle(value) = ...`; and // 3. the rest are reported as invalid parameters. val (patternParams, extractionParams) = allParams.partition(_.flags.pat) log(s"`${patSym.nme}`'s pattern parameters: ${patternParams.mkString("[", ", ", "]")}") log(s"`${patSym.nme}`'s extraction parameters: ${extractionParams.mkString("[", ", ", "]")}") // Empty pattern body is considered as wildcard patterns. val rhs = td.rhs.getOrElse: raise(ErrorReport(msg"Pattern definitions must have a body." -> td.toLoc :: Nil)) Tree.Under() // Elaborate the pattern body with the pattern parameters. val pat = pattern(rhs)(using ctx ++ patternParams.iterator.map(p => p.sym.name -> p.sym)) // Report all invalid variables we found in the top-level pattern. pat.variables.report // Note that the remaining variables have not been bound to any // `VarSymbol` yet. Thus, we need to pair them with the extraction // parameters. We only report warnings for unbound variables // because they are harmless. Variables used in guard conditions // (from `where` clauses) are not considered useless. val guardedNames = pat.varNamesUsedInGuards pat.variables.varMap.foreach: (name, aliases) => extractionParams.find(_.sym.name == name) match case S(param) => aliases.foreach(_.symbol = param.sym) case N if !guardedNames.contains(name) => raise(WarningReport(msg"Unused pattern binding: $name." -> aliases.head.toLoc :: Nil)) case _ => () scoped("ucs:ups")(log(s"elaborated pattern body: ${pat.showDbg}")) scoped("ucs:ups:tree")(log(s"elaborated pattern body: ${pat.showAsTree}")) // `paramsOpt` is set to `N` because we don't want parameters to // appear in the generated class's constructor. val pd = PatternDef(owner, patSym, sym, tps, allParams, patternParams, extractionParams, pat, annotations) patSym.defn = S(pd) pd case k: (Mod.type | Obj.type) => val modSym = td.symbol.asInstanceOf[ModuleOrObjectSymbol] // TODO: improve `asInstanceOf` newCtx.givenIn: trace(s"Processing module/object definition $nme"): val comp = sym.asCls match case comp @ S(_) => assert(sym.asAls.isEmpty) comp case N => sym.asAls log(s"Companion: ${comp}") val md = val (bod, c) = mkBody ModuleOrObjectDef(owner, modSym, sym, tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)(outerCtx.scope) modSym.defn = S(md) md case Cls => val clsSym = td.symbol.asInstanceOf[ClassSymbol] // TODO: improve `asInstanceOf` newCtx.givenIn: trace(s"Processing class definition $nme"): val comp = sym.asMod log(s"Companion: ${comp}") val tsym = if pss.nonEmpty then val ctsym = TermSymbol(Fun, S(clsSym), clsSym.id) val ctdef = TermDefinition( Fun, sym, ctsym, pss, S(tps.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))), S(clsSym.ref()), N, TermDefFlags.empty, Modulefulness.none, annotations.collect: case a @ Annot.Modifier(Keyword.`declare`) => a , S(clsSym), ) ctsym.defn = S(ctdef) sym.tsym = S(ctsym) S(ctsym) else N val cd = val (bod, c) = mkBody ClassDef(owner, Cls, clsSym, sym, tsym, tps, pss, newOf(td), ObjBody(bod), annotations, comp) clsSym.defn = S(cd) cd go(sts, Nil, defn :: acc) case Annotated(annotation, target) :: sts => go(target :: sts, annotations ++ annot(annotation), acc) // * With tight right precedence, `#config(args)` is parsed as `App(Directive(config, Tup()), Tup(args))`. // * Reconstruct as `Directive(config, Tup(args))` and re-process. case App(Directive(prefix, _), args) :: sts => go(Directive(prefix, args) :: sts, annotations, acc) case Directive(Ident("config"), Tup(args)) :: sts => reportUnusedAnnotations val modify = ConfigParser.parseOverrides(args) go(sts, Nil, SetConfig(modify) :: acc) case Directive(Ident(name), _) :: sts => raise(ErrorReport( msg"Unknown directive '#${name}'" -> sts.headOption.flatMap(_.toLoc) :: Nil, source = Diagnostic.Source.Compilation)) go(sts, annotations, acc) case (dir @ Directive(prefix, _)) :: sts => raise(ErrorReport( msg"Expected a directive name after '#', but found ${prefix.describe}" -> prefix.toLoc :: Nil, source = Diagnostic.Source.Compilation)) go(sts, annotations, acc) case (st: Tree) :: sts => // TODO reject plain term statements? Currently, `(1, 2)` is allowed to elaborate (tho it should be rejected in type checking later) val res = annotations.foldLeft(term(st)): case (acc, ann) => Term.Annotated(ann, acc) sts match case Nil => (mkBlk(acc, S(res), hasResult), ctx) case _ => go(sts, Nil, res :: acc) end go ctx.withMembers(members).givenIn: go(blk.desugStmts, Nil, Nil) def mkBlk(acc: Ls[Statement], res: Opt[Term], hasResult: Bool): Blk | Rcd = // TODO forbid certain kinds of terms in records val isRcd = acc.exists: case _: (RcdField | RcdSpread) => true case _ => false if isRcd then Term.Rcd(mut = false, (res.toList ::: acc).reverse) else Blk(acc.reverse, res.getOrElse: if hasResult then unit else Term.Lit(UnitLit(false)) ) def newOf(td: TypeDef): Ctxl[Opt[Term.New]] = td.extension match case S(ext) => S(term(ProperNew(S(ext), N))) case N => N match case S(n: Term.New) => S(n) case S(trm) => raise: ErrorReport: msg"Unexpected shape of extension clause: ${trm.describe}" -> trm.toLoc :: Nil N case N => N def fieldOrVarSym(k: TermDefKind, id: Ident)(using Ctx): TermSymbol | VarSymbol = if ctx.outer.inner.isDefined then TermSymbol(k, ctx.outer.inner, id) else VarSymbol(id) def param(t: Tree, inUsing: Bool, inDataClass: Bool): Ctxl[Diagnostic \/ (Param, Opt[SpreadKind])] = t.desugared.asParam(inUsing).map: case pt @ ParamTree(flags, id, sign, spd, modifiers) => log(s"Elaborating ParamTree: ${pt}") val flg = flags.copy(isVal = flags.isVal || inDataClass) val sym = VarSymbol(id) val sig = sign.map(term(_)) val p = Param(flg, sym, sig, Modulefulness.ofSign(sig)(Mod in modifiers)) sym.decl = S(p) (p, spd) def funParams(t: Tree): Ctxl[(ParamList, Ctx)] = val ps_ctx = params(t, inDataClass = false, inPattern = false) def checkFlags(p: Param): Unit = if p.flags.isVal || p.flags.mut then raise(ErrorReport(msg"Illegal function parameter modifiers: ${p.flags.show}" -> p.sym.toLoc :: Nil)) ps_ctx._1.params.foreach(checkFlags) ps_ctx._1.restParam.foreach(checkFlags) ps_ctx /** Elaborate a subtyping constraint. */ def constraint(lhs: Tree, op: "<:<" | ">:>", rhs: Tree): Ctxl[SubConstraint] = val l = term(lhs) val r = term(rhs) val dir = op match case "<:<" => SubDir.Sub case ">:>" => SubDir.Sup SubConstraint(l, r, dir) /** Elaborate a subtyping constraint that may be malformed. */ def maybeConstraint(t: Tree): Ctxl[Option[SubConstraint]] = t match case OpApp(lhs, Ident(op : ("<:<" | ">:>")), rhs :: Nil) => S(constraint(lhs, op, rhs)) case _ => raise(ErrorReport(msg"Illegal constraint syntax." -> t.toLoc :: Nil)) N /** Elaborate a parameter list of a term or a definition. * @param inDataClass Whether the parameter list belongs to a data class. * @param inPattern Whether the parameter list belongs to a pattern definition. * If `inPattern` is `true`, only parameters with `pat` flag * will be added to the context. */ def params(t: Tree, inDataClass: Bool, inPattern: Bool): Ctxl[(ParamList, Ctx)] = t match case Tup(ps) => def go(ps: Ls[Tree], acc: Ls[Param], ctx: Ctx, flags: ParamListFlags): (ParamList, Ctx) = ps match case Nil => (ParamList(flags, acc.reverse, N).withLocOf(t), ctx) case hd :: tl => val isCtxParam = hd.isModified(Ins) val inUsing = flags.ctx || isCtxParam param(hd, inUsing, inDataClass)(using ctx) match case R((p, spd)) => if isCtxParam && acc.nonEmpty then raise(ErrorReport(msg"Keyword `using` must occur before all parameters." -> hd.toLoc :: Nil)) val newCtx = if !inPattern || p.flags.pat then ctx + (p.sym.name -> p.sym) else ctx val newFlags = flags.copy(ctx = inUsing) spd match case S(spd) => if spd is SpreadKind.Lazy then raise(ErrorReport(msg"Lazy spread parameters not allowed." -> hd.toLoc :: Nil)) if tl.isEmpty then (ParamList(flags, acc.reverse, S(p)).withLocOf(t), newCtx) else raise(ErrorReport(msg"Spread parameters must be the last in the parameter list." -> hd.toLoc :: Nil)) go(tl, p :: acc, newCtx, newFlags) case N => go(tl, p :: acc, newCtx, newFlags) case L(d) => raise(d); go(tl, acc, ctx, flags) go(ps, Nil, ctx, ParamListFlags.empty) def ident(id: Ident)(using Ctx): Ctxl[Opt[Term]] = ctx.get(id.name) match case S(elem) => S(elem.ref(id)) case N => state.builtinOpsMap.get(id.name) match case S(bi) => S(bi.ref(id)) case N => N def pattern(t: Tree): Ctxl[Pattern] = import ucs.{Ctor, unapply, error}, ucs.extractors.*, Keyword.*, Pattern.*, InvalidReason.* given TraceLogger = tl /** String range bounds must be single characters. */ def isInvalidStringBounds(lo: StrLit, hi: StrLit)(using Raise): Bool = val ds = collection.mutable.Buffer.empty[(Message, Option[Loc])] if lo.value.length =/= 1 then ds += msg"The lower bound of character ranges must be a single character." -> lo.toLoc if hi.value.length =/= 1 then ds += msg"The upper bound of character ranges must be a single character." -> hi.toLoc if ds.nonEmpty then error(ds.toSeq*) ds.nonEmpty /** Resolve an identifier. We need to perform a very preliminary check to * determine whether this identifier refers to a pattern, a class, an * object, or creates a new binding. * * FIXME: This routine is insufficient to look up definitions defined * later in the program. */ def ident(id: Ident)(using Ctx): Ctxl[Opt[Term]] = scoped("ucs:pattern:resolution"): log(s"resolve ${id}") ctx.get(id.name) match case S(elem) => log("has elem!") elem.symbol.flatMap: case vs: VarSymbol => vs.decl match case S(d) if d.isPatternConstructor => S(elem.ref(id)) case S(_) | N => N case sym: Symbol => sym.asCls.orElse(sym.asObj).orElse(sym.asPat) match case S(_) => S(elem.ref(id)) case N => N case N => state.builtinOpsMap.get(id.name) match case S(bi) => S(bi.ref(id)) case N => N /** Elaborate arrow patterns like `p => t`. Meanwhile, report all invalid * variables we found in `p`. */ def arrow(lhs: Tree, rhs: Tree): Ctxl[Pattern] = val pattern = go(lhs) // The symbol allocated here will be bound in `split` to the values // destructed from the scrutinee. val variables = pattern.variables.allocate // The `VarSymbol` created here is used as the parameters of the // lambda expression generated by extraction (such as `p => t`). // To avoid duplicating `t`, we generate a lambda function before the // branch starts, which will be called if the `split` succeeds. // - `contextEntries` will be added to the context // - `correspondence` is mapping from symbols representing variables // in the pattern to symbols for parameters to be used in the `term`. val (contextEntries, correspondence) = variables.iterator.map: case (name, symbol) => // We create a symbol specifically for `Param` for each variable to // avoid redundantly redeclaring symbols in `Scope` during code // generation, which triggers the assertion in `Scope.addToBindings`. val parameterSymbol = VarSymbol(new Ident(symbol.name)) (name -> parameterSymbol, symbol -> parameterSymbol) .toList.unzip pattern.variables.report // Report all invalid variables we found in `pattern`. Transform(pattern, correspondence, term(rhs)(using ctx ++ contextEntries)) /** Elaborate tuple patterns like `[p1, p2, ...ps, pn]`. */ def tuple(ts: Ls[Tree]): Ctxl[Pattern.Tuple] = // We are accumulating two components: the leading patterns, the spread // part including the trailing patterns. val z = (Ls[Pattern](), N: Opt[(SpreadKind, Pattern, Ls[Pattern])]) val (leading, spread) = ts.foldLeft(z): case (acc @ (_, S(_)), t: Spread) => // Found two `...p`s in the same tuple pattern. Report an error. raise(ErrorReport(msg"Multiple spread patterns are not supported." -> t.toLoc :: Nil)) acc // Do not modify the accumulator and skip this `Spread`. case ((leading, N), Spread(ellipsis, S(t))) => // Found `...p`, elaborate `p` and assign it to the spread pattern. (leading, S(SpreadKind.fromKw(ellipsis), go(t), Nil)) case ((leading, N), Spread(ellipsis, N)) => // Found `...` (no following patterns), which means the spread part // will not be further matched. Set the spread pattern to `Wildcard`. (leading, S(SpreadKind.fromKw(ellipsis), Wildcard(), Nil)) case ((leading, N), t) => // Found a tuple field while the spread pattern is not set. Add the // elaborated pattern to the leading patterns. (go(t) :: leading, N) case ((leading, S((spreadKind, spread, trailing))), t) => // Found a tuple field while the spread pattern has been set. Add the // elaborated pattern to the trailing patterns. (leading, S((spreadKind, spread, go(t) :: trailing))) Tuple(leading.reverse, spread) /** Elaborate record patterns like `(a: p1, b: p2, ...pn)`. */ def record(ps: Ls[Tree]): Ctxl[Pattern.Record] = val entries = ps.iterator.map(_.desugared).foldLeft(List[(Ident, Pattern)]()): case (acc, InfixApp(id: Ident, Keywrd(Keyword.`:`), p)) => (id, go(p)) :: acc case (acc, InfixApp(key: StrLit, Keywrd(Keyword.`:`), p)) => ((Ident(key.value): Ident).withLocOf(key), go(p)) :: acc case (acc, Pun(false, p)) => (p, Variable(p)) :: acc case (acc, t) => raise(ErrorReport(msg"Unexpected record property pattern." -> t.toLoc :: Nil)) acc Record(entries.reverse) /** Elaborate a pattern argument. */ def arg(t: Tree): Ctxl[Pattern \/ Pattern] = t match case TypeDef(syntax.Pat, body, N) => L(go(body)) case _ => R(go(t)) def go(t: Tree): Ctxl[Pattern] = trace[Pattern](s"Elab pattern ${t.showDbg}", r => s"~> $r"): t match // Annotated patterns like `@compile P`. case Tree.Annotated(annotation, target) => go(target).annotate(term(annotation), t.toLoc) // Brackets. case Bra(BracketKind.Round | BracketKind.Curly, t) => go(t) // Tuple patterns like `[p1, p2, ...ps, pn]`. case TyTup(ps) => tuple(ps) case Tup(ps) => tuple(ps) case t: syntax.Literal => Literal(t) // Negation patterns: `~p` case App(Ident("~"), Tup(p :: Nil)) => Negation(go(p)) // Negative integer and decimal literals. case app @ App(Ident("-"), Tup(IntLit(n) :: Nil)) => Literal(IntLit(-n).withLocOf(app)) case app @ App(Ident("-"), Tup(DecLit(n) :: Nil)) => Literal(DecLit(-n).withLocOf(app)) // Union and intersection patterns: `p | q` and `p & q` case App(Ident(op @ ("|" | "&")), Tup(rhs :: Nil)) => go(rhs) // unary uses of `|` and `&` are no-ops case OpApp(lhs, Ident(op @ ("|" | "&")), rhs :: Nil) => Composition(op === "|", go(lhs), go(rhs)) // Constructor patterns with pattern arguments and arguments. case App(ctor: Ctor, Tup(argTrees)) => Constructor(term(ctor), S(argTrees.map(go(_)))) // `[p1, p2, ...ps, pn] => term`: All patterns are in the `TyTup`. case (lhs: TyTup) `=>` rhs => arrow(lhs, rhs) // `pattern => term`: Note that `pattern` is wrapped in a `Tup`. case Tup(lhs) `=>` rhs => lhs match case p @ Pun(false, _) :: Nil => record(p) case p @ InfixApp(_: Ident, Keywrd(Keyword.`:`), _) :: Nil => record(p) case lhs :: Nil => arrow(lhs, rhs) case _ :: _ | Nil => ??? // TODO: this case reached by, eg, `pattern p = () => Unit` case p as q => q match // `p as id` is elaborated into alias if `id` is not a constructor. case id: Ident => ident(id) match case S(target) if target.symbol.exists(_.isInstanceOf[VarSymbol]) => // If the target is a variable, we should shadow it. This check is // probably insufficient as there are more cases. go(p) binds id case S(target) => Chain(go(p), Constructor(target, N)) case N => go(p) binds id // Fallback to alias. // `p as q` where `q` is not an identifier is elaborated into chain. case _: Tree => Chain(go(p), go(q)) case p where t => val q = go(p) Guarded(q, term(t)(using ctx ++ q.variables.allocate)) case Under() => Pattern.Wildcard().withLocOf(t) // Singleton blocks like `{1}`. case Block(p :: Nil) => go(p) // Record patterns like `(a: p1, b: p2, ...pn)`. case Block(ps) => record(ps) // A single pun pattern is a record pattern. case p @ Pun(false, _) => record(p :: Nil) // A single record field is a record pattern. case p @ InfixApp(_, Keywrd(Keyword.`:`), _) => record(p :: Nil) // Range patterns. We can also desugar them into disjunctions of all the // literals in the range. case (lower: StrLit) to (incl, upper: StrLit) => if isInvalidStringBounds(lower, upper) then Pattern.Wildcard() else Pattern.Range(lower, upper, incl) case (lower: IntLit) to (incl, upper: IntLit) => Pattern.Range(lower, upper, incl) case (lower: DecLit) to (incl, upper: DecLit) => Pattern.Range(lower, upper, incl) case (lower: syntax.Literal) to (_, upper: syntax.Literal) => raise(ErrorReport(msg"The upper and lower bounds of range patterns should be literals of the same type." -> t.toLoc :: Nil)) Pattern.Wildcard() // String concatenation patterns: `p ~ q`. Currently, not supported by the // pattern compilation. We elaborate them to keep the consistency with the // pattern translation. case OpApp(lhs, Ident("~"), rhs :: Nil) => Pattern.Concatenation(go(lhs), go(rhs)) // Constructor patterns can be written in the infix form. case OpApp(lhs, op, rhs :: Nil) => Pattern.Constructor(term(op), S(Ls(go(lhs), go(rhs)))) // Constructor patterns without arguments case id @ Ident(name) if name.isUncapitalized => Variable(id) case id @ Ident(name) => ident(id) match case S(target) => Constructor(target, N) case N => raise: ErrorReport(msg"Pattern name not found: ${id.name}." -> id.toLoc :: Nil) Pattern.Wildcard() case sel: (SynthSel | Sel) => Constructor(term(sel), N) case _: Tree => raise(ErrorReport(msg"Unrecognized pattern (${t.describe})." -> t.toLoc :: Nil)) Pattern.Wildcard() go(t) def typeParams(t: Tree): Ctxl[(Ls[Param], Ctx)] = t match case TyTup(ps) => val vs = ps.flatMap: case id: Ident => val sym = VarSymbol(id) sym.decl = S(TyParam(FldFlags.empty, N, sym)) Param(FldFlags.empty, sym, N, Modulefulness.none) :: Nil case t => raise(ErrorReport(msg"Unsupported type parameter ${t.describe}" -> t.toLoc :: Nil)) Nil (vs, ctx ++ vs.map(p => p.sym.name -> p.sym)) def importFrom(sts: Block): Ctxl[(Blk, Ctx)] = given UnderCtx = new UnderCtx(N) val (res, newCtx) = block(sts, hasResult = false) // TODO handle name clashes (res, newCtx) def topLevel(sts: Block): Ctxl[(Blk, Ctx)] = given UnderCtx = new UnderCtx(N) val (res, ctx) = block(sts, hasResult = false) computeVariances(res) (res, ctx) def computeVariances(s: Statement): Unit = val trav = VarianceTraverser() def go(s: Statement): Unit = s match case TermDefinition(k, sym, tsym, pss, _, sign, body, r, _, _, _) => pss.foreach(ps => ps.params.foreach(trav.traverseType(S(false)))) sign.foreach(trav.traverseType(S(true))) body match case S(b) => go(b) case N => case ClassDef(sym, tps, pso, body) => pso.foreach: ps => ps.foreach: p => p.sign.foreach(trav.traverseType(S(true))) body.blk.stats.foreach(go) // s.subStatements.foreach(go) case _ => s.subStatements.foreach(go) while trav.changed do trav.changed = false go(s) class VarianceTraverser(var changed: Bool = true) extends Traverser: override def traverseType(pol: Pol)(trm: Term): Unit = trm match case Term.TyApp(lhs, targs) => lhs.symbol.flatMap(sym => sym.asTpe) match case S(sym: ClassSymbol) => sym.defn match case S(td: ClassDef) => td.tparams.zip(targs).foreach: case (tp, targ) => if !tp.isContravariant then traverseType(pol)(targ) if !tp.isCovariant then traverseType(pol.!)(targ) case N => // TODO(sym->sym.uid) case S(sym: ModuleOrObjectSymbol) => sym.defn match case S(td: ModuleOrObjectDef) => td.tparams.zip(targs).foreach: case (tp, targ) => if !tp.isContravariant then traverseType(pol)(targ) if !tp.isCovariant then traverseType(pol.!)(targ) case N => // TODO(sym->sym.uid) case S(sym: TypeAliasSymbol) => // TODO dedup with above... sym.defn match case S(td: semantics.TypeDef) => td.tparams.zip(targs).foreach: case (tp, targ) => if !tp.isContravariant then traverseType(pol)(targ) if !tp.isCovariant then traverseType(pol.!)(targ) case N => TODO(sym->sym.uid) // case S(sym) => ??? case N => log(s"No symbol found $lhs ${lhs.symbol}") // ??? () // TODO case Term.Ref(sym: VarSymbol) => sym.decl match case S(ty: TyParam) => if pol =/= S(true) && ty.isCovariant then changed = true ty.isCovariant = false if pol =/= S(false) && ty.isContravariant then changed = true ty.isContravariant = false // case _ => ??? case N => lastWords(s"VarSymbol ${sym.name} has no declaration") case _ => super.traverseType(pol)(trm) abstract class Traverser: def traverseType(pol: Pol)(trm: Term): Unit = trm match case Term.Lit(_) | Term.UnitVal() | Term.Error => case Term.TyApp(lhs, targs) => // lhs.resolveSymbol // targs.foreach(traverseType(pol)) ??? case r: Term.Ref => case Term.FunTy(l, r, e) => traverseType(pol.!)(l) traverseType(pol)(r) e.foreach(e => traverseType(pol)(e)) case Term.Forall(_, _, body) => traverseType(pol)(body) case Term.WildcardTy(in, out) => in.foreach(t => traverseType(pol.!)(t)) out.foreach(t => traverseType(pol)(t)) case Term.CompType(lhs, rhs, _) => () // TODO: case Term.SynthSel(bse, nme) => traverseType(pol)(bse) // FIXME: probably wrong for what we want to do case Term.Tup(fields) => // fields.foreach(f => traverseType(pol)(f.value)) fields.foreach(traverseType(pol)) // case _ => ??? case Term.Neg(ty) => traverseType(pol.!)(ty) case _ => // TODO def traverseType(pol: Pol)(f: Elem): Unit = f match case f: Fld => traverseType(pol)(f.term) f.asc.foreach(traverseType(pol)) def traverseType(pol: Pol)(f: Param): Unit = f.sign.foreach(traverseType(pol)) end Elaborator type Pol = Opt[Bool] extension (p: Pol) def ! : Pol = p.map(!_) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala ================================================ package hkmc2 package semantics import scala.collection.mutable import scala.annotation.tailrec import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.Message.MessageContext import hkmc2.io import utils.TraceLogger import Elaborator.* import hkmc2.syntax.LetBind class Importer: self: Elaborator => import tl.* def importPath(path: Str, alias: Opt[syntax.Tree.Ident])(using cfg: Config): Import = // log(s"pwd: ${os.pwd}") // log(s"wd: ${wd}") val file = if path.startsWith("/") then io.Path(path) else wd / io.RelPath(path) val nme = file.baseName val id = alias.getOrElse(new syntax.Tree.Ident(nme)) // TODO loc lazy val sym = TermSymbol(LetBind, N, id) if path.startsWith(".") || path.startsWith("/") then // leave alone imports like "fs" log(s"importing $file") val nme = file.baseName val id = new syntax.Tree.Ident(nme) // TODO loc file.ext match case "mjs" | "js" => Import(sym, file.toString, file) case "mls" if { !cctx.beingCompiled.contains(file) `||`: raise: ErrorReport: msg"Circular imports of `mls` files are not yet supported" -> N :: (cctx.allFilesBeingImported :+ file).map(f => msg" importing ${f.toString}" -> N) false } => val importedSym = tl.trace(s">>> Importing $file"): given TL = tl val artifact = cctx.getElaboratedBlock(file, prelude) artifact.tree.definedSymbols.find(_._1 === nme) match case Some(nme -> imsym) => imsym case None => lastWords(s"File $file does not define a symbol named $nme") val sym = alias.fold(importedSym): alias => val res = BlockMemberSymbol(alias.name, importedSym.trees, importedSym.nameIsMeaningful) res.tsym = importedSym.tsym res val jsFile = file.up / io.RelPath(file.baseName + ".mjs") Import(sym, jsFile.toString, jsFile) case _ => if file.ext =/= "mls" then raise: ErrorReport(msg"Unsupported file extension: ${file.ext}" -> N :: Nil) Import(sym, path, file) else Import(sym, path, file) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala ================================================ package hkmc2 package semantics import mlscript.utils.*, shorthands.* import collection.immutable.HashMap, collection.mutable.Buffer import syntax.{Keyword, SpreadKind, Tree}, Tree.{Ident, StrLit} import Elaborator.State, Message.MessageContext, ucs.error import scala.annotation.tailrec, util.chaining.* import utils.{TraceLogger, tl} object Pattern: /** The reason why a variable obtained from `Pattern.variables` is invalid. */ enum InvalidReason: /** The variable shadowed another variable. * @param previous The identifiers shadowed by the current aliases. */ case Duplicated(previous: Ls[Ident]) /** The variable presents in one side of disjunction, but not the other. */ case Inconsistent(disjunction: Pattern.Composition, missingOnTheLeft: Bool) /** The variable is bound in a `Negation` pattern. */ case Negated(negation: Pattern.Negation) /** Higher-order pattern arguments contain free variables. For example, `x` * in `pattern MaybeInt = Nullable(pattern Int as x)` is invalid. Because * `MaybeInt` does not guarantee that `Int as x` is used in `Nullable`. */ case Escaped(pattern: Pattern) import InvalidReason.* /** A set of variables that present in a pattern. * * These variables are represented by `Tree.Ident` because not every identifier * can represent a meaningful binding. This set is used in the computed property * on the `Pattern` tree. We only create `VarSymbol` for these variables at the * root node or when elaborating the term of a `Transform` pattern. * * @param varMap A map from variable names to their valid aliases. Multiple * aliases are allowed when they are in different disjunctions. * @param invalidVars A list of variables and the reason why they are invalid. */ final case class Variables( varMap: HashMap[Str, Ls[Pattern.Alias]], invalidVars: Ls[(Pattern.Alias, InvalidReason)] ): /** Allocate symbols for all variables. */ def allocate(using State, TraceLogger): Seq[(Str, VarSymbol)] = varMap.iterator.map: (name, aliases) => // We need to assign the same symbol to the same name. But I realize // that the following cases will break this constraint: `(x where x > 0) // | (x where x < 0)`. In both alternatives, `x` needs to be allocated // in advanced, but at the level of the disjunction, `x` needs to be // assigned to the same variable. This represents an edge case which // needs to be fixed later. val symbols = aliases.iterator.flatMap(_.symbolOption).toSet // TODO: The above edge case would fail the following assertion. assert(symbols.size <= 1) // If no symbol had been created before, create a new symbol now. val symbol = symbols.headOption.getOrElse(VarSymbol(Ident(name))) aliases.foreach: alias => // For guarded patterns (`p where t`), the variables in `p` have to be // allocated before `t` is elaborated. In that case, we don't need to // allocate the variables in `p` again when the variables of pattern // containing `p` are allocated. if alias.symbolOption.isEmpty then alias.symbol = symbol (name, symbol) .toSeq /** Get all symbols for the variables. */ def symbols: Ls[VarSymbol] = varMap.iterator.flatMap(_._2.head.symbolOption).toList /** Add a single variable to the variable set. */ def +(alias: Pattern.Alias): Variables = varMap.get(alias.id.name) match // Add the alias to the variable set if the name has not been used. case N => Variables(varMap + (alias.id.name -> Ls(alias)), invalidVars) case S(oldAliases) => Variables( varMap.updated(alias.id.name, Ls(alias)), // Append the invalid reason: `alias` shadows `oldAliases`. invalidVars :+ (alias -> Duplicated(oldAliases.map(_.id)))) /** Union two variable sets. If the latter contains a variable that is * already present in the former, the variable is considered duplicated. */ def ++(that: Variables): Variables = val duplicated: Buffer[(Alias, InvalidReason)] = Buffer.empty Variables( varMap.merged(that.varMap): case ((name, previous), (_, aliases)) => duplicated ++= aliases.map(_ -> Duplicated(previous.map(_.id))) (name, aliases), invalidVars ::: that.invalidVars ::: duplicated.toList) /** For debugging purpose only. */ def display: Str = varMap.iterator.map: case (key, aliases) => key + " -> " + aliases.iterator.map(_.id.name).mkString(", ") .mkString("{", "; ", "}") /** Intersect two variable sets and move variables that are only present in * one side to the invalid variables. This method considers `this` as the * left side, and `that` as the right side. */ def intersect(that: Variables, pattern: Pattern.Composition): Variables = // Check if two variable sets are the same. val notInThat = varMap.removedAll(that.varMap.keys) val notInThis = that.varMap.removedAll(varMap.keys) Variables( // Merge two sets and remove variables that only present in one side. varMap.merged(that.varMap): case ((name, left), (_, right)) => (name, left ::: right) .removedAll(Iterable.concat(notInThat.keys, notInThis.keys)), // Add variables that only present in one side to the invalid variables. invalidVars ::: notInThis.iterator.flatMap(_._2.map(_ -> Inconsistent(pattern, true))).toList ::: notInThat.iterator.flatMap(_._2.map(_ -> Inconsistent(pattern, false))).toList) /** Mark all variables in this set as invalid. */ def invalidated(reason: InvalidReason): Variables = Variables(HashMap.empty, invalidVars appendedAll varMap.iterator.flatMap(_._2.map(_ -> reason))) /** Report all invalid variables. */ def report(using Raise): Unit = invalidVars.foreach: case (Alias(_, id), Duplicated(previous)) => raise(ErrorReport( msg"Duplicated pattern variable." -> id.toLoc :: msg"The previous definition ${if previous.size === 1 then "is" else "are"} as follows." -> previous.head.toLoc :: previous.tail.map(msg"" -> _.toLoc))) case (Alias(_, id), Inconsistent(disjunction, missingOnTheLeft)) => error( msg"Found an inconsistent variable in disjunction patterns." -> id.toLoc, msg"The variable is missing from this sub-pattern." -> ( if missingOnTheLeft then disjunction.left else disjunction.right ).toLoc) case (Alias(pattern, id), Negated(negation)) => error( pattern match case Wildcard() => msg"This variable cannot be accessed." -> id.toLoc case _: Pattern => msg"This pattern cannot be bound." -> pattern.toLoc, msg"Because the pattern it belongs to is negated." -> negation.toLoc) object Variables: lazy val empty: Variables = Variables(HashMap.empty, Nil) extension (patterns: IterableOnce[Pattern]) def variables: Variables = patterns.iterator.foldLeft(Variables.empty): case (vars, pattern) => vars ++ pattern.variables /** A shorthand for creating a variable pattern. */ def Variable = Pattern.Wildcard() binds (_: Ident) trait ConstructorImpl: self: Pattern.Constructor => /** Get the resolved symbol of the target term. */ def symbol: Opt[Symbol] = self.target.resolvedSym /** Expect the `symbol` to be set. */ def symbol_! : Symbol = symbol.getOrElse: lastWords(s"target term `${self.target}` does not resolve to a symbol") /** Add a mutable field to the `Alias` pattern to store the symbol for the * variable. Note that NOT every `Alias` pattern has a symbol. */ trait AliasImpl: self: Pattern.Alias => private var _symbol: Opt[VarSymbol] = N /** Directly set the symbol for the variable. This should be called in the * elaborator when elaborating the non-`Transform` top-level pattern. */ def symbol_=(symbol: VarSymbol): Unit = _symbol = S(symbol) def symbolOption: Opt[VarSymbol] = _symbol def symbol: VarSymbol = _symbol.getOrElse: lastWords(s"no symbol was assigned to variable `${id.name}` at ${id.toLoc}") import Pattern.*, InvalidReason.* /** An inductive data type for patterns. */ enum Pattern extends AutoLocated: /** A pattern that matches a constructor and its arguments. * @param target The term representing the constructor. * @param arguments `None` if the pattern does not have a parameter list. The * patterns that are used to destruct the constructor's arguments. */ case Constructor( target: Term, arguments: Opt[Ls[Pattern]] ) extends Pattern with ConstructorImpl /** A pattern that is the composition of two patterns. * @param polarity `true` if the pattern is a disjunction, `false` if it is * a conjunction. */ case Composition(polarity: Bool, left: Pattern, right: Pattern) /** A pattern that is the negation of another pattern. We don't allow negation * patterns to be nested. They can only be used as the top-level pattern. * Also, what's the point of binding the inner pattern? */ case Negation(pattern: Pattern) /** A pattern that matches any value. It is syntically denoted by `_`. */ case Wildcard() /** A pattern that matches the given literal. */ case Literal(literal: syntax.Literal) /** A pattern that matches a range of values. */ case Range(lower: syntax.Literal, upper: syntax.Literal, rightInclusive: Bool) /** A pattern that matches the concatenation of two string patterns. The * concatenation of non-string patterns results in a never-match pattern. */ case Concatenation(left: Pattern, right: Pattern) /** A pattern that matches a tuple. At most one `spread` pattern is allowed. * When the `spread` pattern is absent, sub-patterns should be placed in the * `leading` field. * @param leading matches a fixed number of leading elements in the tuple. * @param spread matches a variable number of elements in the tuple plus * the trailing elements. */ case Tuple( leading: Ls[Pattern], spread: Opt[(SpreadKind, Pattern, Ls[Pattern])], ) /** A pattern that matches a record consisting of a list of fields. Note that * the fields are not ordered semantically. */ case Record(fields: Ls[(Ident, Pattern)]) /** Chain one pattern to another. The pattern matches the input value against * the `first` pattern, and then matches its output against the `second` * pattern. It fails when either of the patterns fails. */ case Chain(first: Pattern, second: Pattern) /** A pattern that matches the same value as another pattern, but bind the * matched value to a new variable. This is the only class that holds the * symbol for the variable. */ case Alias(pattern: Pattern, id: Ident) extends Pattern with AliasImpl /** A pattern that matches the same value as other pattern, with an additional * function applied to the bound variables in the pattern. * * @param pattern The pattern to be matched against. * @param parameters A map from the variable symbols to parameter symbols. * @param transform The term that should be applied to the extracted values. */ case Transform(pattern: Pattern, parameters: Ls[(VarSymbol, VarSymbol)], transform: Term) /** If the term is `Error`, we add `Opt[Loc]` to the list instead. */ case Annotated(pattern: Pattern, annotations: Vector[Opt[Loc] \/ Term]) /** A pattern that comes with an extra condition. It works in a way similar to * `and` in split. */ case Guarded(pattern: Pattern, guard: Term) infix def binds(id: Ident): Pattern.Alias = Pattern.Alias(this, id) /** Annotate the pattern using the given term. If the term is `Error`, then * use the location of the original tree for error reporting. */ inline def annotate(annotation: Term, treeLoc: Opt[Loc]): Pattern.Annotated = val elem = if annotation is Term.Error then L(treeLoc) else R(annotation) this match case Annotated(pattern, annotations) => Annotated(pattern, annotations :+ elem) case _ => Annotated(this, Vector.single(elem)) inline def withGuard(guard: Term) = Pattern.Guarded(this, guard) /** Collect all variables in the pattern. Meanwhile, list invalid variables, * which will be reported when constructing symbols for variables. We use a * map because we want to replace variables. */ lazy val variables: Variables = this match case Constructor(_, arguments) => arguments.fold(Variables.empty)(_.variables) case Composition(false, left, right) => left.variables ++ right.variables case union @ Composition(true, left, right) => left.variables.intersect(right.variables, union) // If we only allow negation patterns to be used as the top-level pattern // and the arguments represent the diagnostic information of match failure, // then we can bind the variables in the pattern to the arguments. // case Negation(Constructor(_, arguments)) => arguments.variables // Otherwise, negation patterns should not bind any variables. case negation @ Negation(pattern) => pattern.variables.invalidated(Negated(negation)) case _: (Wildcard | Literal | Range | Transform) => Variables.empty case Concatenation(left, right) => left.variables ++ right.variables case Tuple(leading, spread) => leading.variables ++ spread.fold(Variables.empty): case (_, middle, trailing) => middle.variables ++ trailing.variables case Record(fields) => fields.iterator.map(_._2).variables case alias @ Alias(pattern, _) => pattern.variables + alias case Chain(first, second) => first.variables ++ second.variables case Annotated(pattern, _) => pattern.variables case Guarded(pattern, _) => pattern.variables /** Collect the names of pattern variables that are actually referenced * as free variables inside guard terms of `Guarded` patterns. Only * variables whose names appear free (not locally re-bound) in the guard * are included, so that truly unused pattern bindings (e.g., * `[x] where true`) and shadowed bindings (e.g., * `[x, y] where (let x = ..., x)`) still trigger warnings. */ lazy val varNamesUsedInGuards: Set[Str] = this match case Guarded(pattern, guard) => val boundNames = pattern.variables.varMap.keySet val referencedNames = guard.freeVars (boundNames & referencedNames) ++ pattern.varNamesUsedInGuards case _ => children.iterator.collect: case p: Pattern => p.varNamesUsedInGuards .foldLeft(Set.empty[Str])(_ ++ _) def children: Vector[Located] = this match case Constructor(target, arguments) => target +: arguments.fold(Vector.empty)(_.toVector) case Composition(polarity, left, right) => Vector.double(left, right) case Negation(pattern) => Vector.single(pattern) case Wildcard() => Vector.empty case Literal(literal) => Vector.single(literal) case Range(lower, upper, rightInclusive) => Vector.double(lower, upper) case Concatenation(left, right) => Vector.double(left, right) case Tuple(leading, spread) => leading.toVector ++ spread.fold(Vector.empty): case (_, middle, trailing) => middle +: trailing.toVector case Record(fields) => fields.iterator.flatMap: case (name, pattern) => name +: pattern.children .toVector case Chain(first, second) => Vector.double(first, second) case Alias(pattern, alias) => Vector.double(pattern, alias) case Transform(pattern, _, transform) => Vector.double(pattern, transform) case Annotated(pattern, annotations) => pattern +: annotations.iterator.collect { case R(term) => term }.toVector case Guarded(pattern, guard) => pattern.children :+ guard def subTerms: Vector[Term] = this match case Constructor(target, arguments) => target +: Vector.concat(arguments.fold(Vector.empty)(_.iterator.flatMap(_.subTerms).toVector)) case Composition(_, left, right) => left.subTerms ++ right.subTerms case Negation(pattern) => pattern.subTerms case _: (Wildcard | Literal | Range) => Vector.empty case Concatenation(left, right) => left.subTerms ++ right.subTerms case Tuple(leading, spread) => leading.iterator.flatMap(_.subTerms).toVector ++ spread.fold(Vector.empty): case (_, middle, trailing) => middle.subTerms ++ trailing.iterator.flatMap(_.subTerms).toVector case Record(fields) => fields.iterator.flatMap(_._2.subTerms).toVector case Chain(first, second) => first.subTerms ++ second.subTerms case Alias(pattern, _) => pattern.subTerms case Transform(pattern, _, transform) => pattern.subTerms :+ transform case Annotated(pattern, annotations) => pattern.subTerms ++ annotations.iterator.collect { case R(term) => term }.toList case Guarded(pattern, guard) => pattern.subTerms :+ guard def describe: Str = this match case Constructor(_, _) => "constructor" case Composition(true, _, _) => "disjunction" case Composition(false, _, _) => "conjunction" case Negation(_) => "negation" case Wildcard() => "wildcard" case Literal(_) => "literal" case Range(_, _, _) => "range" case Concatenation(_, _) => "concatenation" case Tuple(_, _) => "tuple" case Record(_) => "record" case Chain(_, _) => "chain" case Alias(_, _) => "alias" case Transform(_, _, _) => "transform" case Annotated(_, _) => "annotated pattern" case Guarded(_, _) => "guarded pattern" private def showDbgWithPar(using DebugPrinter): Str = val addPar = this match case _: (Constructor | Wildcard | Literal | Tuple | Record | Negation | Annotated) => false case Alias(Wildcard(), _) => false case _: (Alias | Composition | Transform | Range | Concatenation | Chain | Guarded) => true if addPar then s"(${showDbg})" else showDbg def showDbg(using DebugPrinter): Str = this match case Constructor(target, arguments) => target.symbol.fold(target.showDbg)(_.nme) + arguments.fold(""): args => s"(${args.map(_.showDbg).mkString(", ")})" case Composition(true, left, right) => s"${left.showDbg} ∨ ${right.showDbg}" case Composition(false, left, right) => s"${left.showDbg} ∧ ${right.showDbg}" case Negation(pattern) => s"¬${pattern.showDbgWithPar}" case Wildcard() => "_" case Literal(literal) => literal.idStr case Range(lower, upper, rightInclusive) => s"${lower.idStr} ${if rightInclusive then "to" else "until"} ${upper.idStr}" case Concatenation(left, right) => s"${left.showDbg} ~ ${right.showDbg}" case Tuple(leading, spread) => (leading.iterator.map(_.showDbg) ++ spread.fold(Iterator.empty): case (spreadKind, middle, trailing) => Iterator.single(spreadKind.str + middle.showDbg) ++ trailing.iterator.map(_.showDbg)).mkString("[", ", ", "]") case Record(fields) => s"{${fields.map((k, v) => s"${k.name}: ${v.showDbg}").mkString(", ")}}" case Chain(first, second) => s"${first.showDbgWithPar} as ${second.showDbgWithPar}" case Alias(Wildcard(), alias) => alias.name case Alias(pattern, alias) => s"${pattern.showDbgWithPar} as ${alias.name}" case Transform(pattern, _, transform) => s"${pattern.showDbgWithPar} => ${transform.showDbg}" case Annotated(pattern, annotations) => annotations.iterator.map: case L(errorLoc) => "error" case R(term) => term.showDbg .mkString("@", " @", " ") + pattern.showDbgWithPar case Guarded(pattern, guard) => pattern.showDbg + " where " + guard.showDbg ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala ================================================ package hkmc2 package semantics import mlscript.utils.*, shorthands.* import utils.TraceLogger import syntax.Tree import syntax.Tree.{DummyTup, DummyApp} import syntax.{Fun, Ins, Mod, ImmutVal, MutVal, SpreadKind} import syntax.Keyword.{`if`} import Elaborator.State import Resolvable.* import typing.Type import semantics.ucs.FlatPattern import Message.MessageContext import scala.annotation.tailrec object Resolver: /** * An "implicit" or "instance" context, as opposed to the one in * Elaborator. * * Essentially, the resolver does the following: * * 1. If it sees an application with contextual arguments to be * resolved, it resolves the contextual arguments from the instance * environment. If the type of the contextual arguments involve * type parameters, it resolves the actual types from the type * environment. * * 2. If it sees an instance definition, it adds the instance to the * instance environment. * * 3. If it sees an application with contextual arguments to be * resolved and type arguments specified, it resolves the * contextual arguments from the instance environment, and adds the * type arguments to the type environment. * * @param parent the parent context, if any * @param iEnv the instance environment, mapping types to instances * @param tEnv the type environment, mapping type parameters to * concrete types */ case class ICtx( parent: Opt[ICtx], iEnv: Map[Type, Ls[(Type, ICtx.Instance)]], tEnv: Map[VarSymbol, Type] ): def +(typ: Type, ins: Symbol): ICtx = typ match case Type.NotImplemented => lastWords("Cannot add instance with a not-yet implemented type.") case Type.Error => this case _ => copy(iEnv = iEnv + (typ.key -> ((typ -> ICtx.Instance(ins)) :: iEnv.getOrElse(typ.key, Nil))) ) def withTypeArg(param: VarSymbol, arg: Type): ICtx = copy(tEnv = tEnv + (param -> arg)) extension (t: Type) private def key: Type = t match case Type.Ref(base, _) => Type.Ref(base, Nil) case _ => t extension (lhs: Type) private def =:= (rhs: Type): Bool = lhs <:< rhs && rhs <:< lhs private def <:< (rhs: Type): Bool = (lhs, rhs) match case (_, Type.Top) => true // If LHS/RHS is unspecified (not substituted into a concrete type), // we consider LHS/RHS is always a subtype of LHS/RHS. case (lhs: Type.Ref, rhs @ Type.Ref(_: VarSymbol, Nil)) => true case (lhs @ Type.Ref(_: VarSymbol, Nil), rhs: Type.Ref) => true case (lhs: Type.Ref, rhs: Type.Ref) => lhs.sym === rhs.sym // TODO: subtyping for Ref && (lhs.args.sizeCompare(rhs.args.length) === 0) && (lhs.args zip rhs.args).forall((a, b) => a.lb =:= b.lb && a.ub =:= b.ub) // suppose invariant case (lhs: Type.Fun, rhs: Type.Fun) => rhs.args <:< lhs.args && lhs.ret <:< rhs.ret && (lhs.eff, rhs.eff).match case (N, N) => true case (S(le), S(re)) => le <:< re // covariant case _ => false case (lhs: Type.Neg, rhs) => ??? case (lhs, rhs: Type.Neg) => ??? case (lhs, rhs: Type.Union) => lhs <:< rhs.lhs || lhs <:< rhs.rhs case (lhs, rhs: Type.Inter) => lhs <:< rhs.lhs && lhs <:< rhs.rhs case (lhs, rhs) => lhs === rhs def query(q: Type): Ls[Message -> Opt[Loc]] \/ ICtx.Instance = q match case Type.NotImplemented => lastWords("Cannot resolve instance for a not-yet implemented type.") case q @ Type.Ref(sym: VarSymbol, args) if !tEnv.contains(sym) => L: msg"Illegal query for an unspecified type variable ${q.show}." -> N :: Nil case q => q.subst: case ty @ Type.Ref(sym: VarSymbol, Nil) => tEnv.getOrElse(sym, ty) case ty @ Type.Ref(sym: VarSymbol, args) => lastWords(s"Unsupported substitution with type arguments") .into: qq => iEnv.getOrElse(qq.key, Nil) .find: (ty, _instance) => ty <:< qq .map: (_ty, instance) => instance .toRight: msg"Missing instance: Expected: ${showTy(q)}; Available: ${showEnv}" -> N :: Nil def showEnv: Str = iEnv.values .flatMap(_.map((typ, instance) => s"${typ.show}")) .toList .distinct .mkStringOr(", ", els = "‹none available›") def showTy(ty: Type): Str = ty match case Type.Ref(sym: VarSymbol, Nil) => s"${tEnv.get(sym).map(_.show).getOrElse("‹unspecified›")} (type parameter ${ty.show})" case Type.Ref(sym: VarSymbol, args) => s"${tEnv.get(sym).map(_.show).getOrElse("‹unspecified›")}${args.map(_.show).mkString("[", ", ", "]")} (type parameter ${ty.show})" case _ => s"${ty.show}" enum Expect: case Module(reason: Opt[Message]) case NonModule(reason: Opt[Message]) case Class(reason: Opt[Message]) case Selectable(reason: Opt[Message]) case PatternConstructor(reason: Opt[Message]) case Any def message: Ls[Message -> Opt[Loc]] = this match case Expect.Module(msg) => msg.toList.map(_ -> N) case Expect.NonModule(msg) => msg.toList.map(_ -> N) case Expect.Class(msg) => msg.toList.map(_ -> N) case Expect.Selectable(msg) => msg.toList.map(_ -> N) case Expect.PatternConstructor(msg) => msg.toList.map(_ -> N) case Expect.Any => Nil def `module` = isInstanceOf[Module] def `class` = isInstanceOf[Class] def nonModule = isInstanceOf[NonModule] override def toString: Str = this match case _: Expect.Module => "Module" case _: Expect.NonModule => "NonModule" case _: Expect.Class => "Class" case _: Expect.Selectable => "Selectable" case _: Expect.PatternConstructor => "PatternConstructor" case Expect.Any => "Any" object Expect: def fromModulefulness(mf: Modulefulness): Expect = if mf.modified then Expect.Module(N) else Expect.NonModule(N) object ICtx: case class Instance(sym: Symbol) val empty = ICtx(N, Map.empty, Map.empty) def ictx(using ICtx) = summon[ICtx] end Resolver /** * Resolver for the module system. * * Resolver mainly resolves the terms, definitions and symbols that are * not yet ready during the elaboration. In addition, it also performs * some checks on the modulefulness of the terms. * * Here are some notes that I used for reasoning about the design. * * ### Static Members vs. Class Members * * An important feature in MLscript is that the program should be * compilable without type checking, and the user experience of doing * so should be good and not error prone. (See: * https://github.com/hkust-taco/mlscript-design-docs/blob/main/wiki/module-methods.md) * A consequence of this design is that the definition of a method may * not always be present at compilation-time, because the type * information of the object is absent. * * In contrast, for a function, the definition is always known at the * call sites at compilation time. This is important because most * call-site features require the definition to be known. For example, * by-name, lazy and implicit parameters all require the definition so * that the compiler can perform the correct elaboration and * resolution. * * ### Module Methods & Modulefulness Check * * In addition to functions, module methods also always have the * definition present at compilation-time. We will now start to refer * to the module methods as "functions". * * The definition of module functions are always present because of * some restrictions on modules. Only declarations that have a * signature and marked as `module` may bind to a module value. For * example, * * ```mls * val v: module M * fun f(module m: M): module M * ``` * * This preserves the module definition even if the module is bound to * other names. In order to maintain the correctness of this system, * the resolver performs the extra checks to ensure that the definition * of a module is never lost during re-binding. * * 1. Only declarations that have a signature and marked as `module` * may bind to a module value. * 2. Only declarations of static members may bind to a module value. * * If a term evaluates to a module value (e.g., an application to a * function returning a module, a reference to a module variable), it * is said to be moduleful. All moduleful terms must only occur at * certain locations in the program. For example, a moduleful term must * not occur as a scrutinee because it may be re-bound to a different * name, during when the module definition is lost. * * ### Symbol Resolution * * The resolver also resolves extra symbols that are not resolved * during the elaboration. Consider the following example: * * ```mls * val m: module M = M.v.v * module M with * val v: module M = M * ``` * * At the time of elaboration on `M.v.v`, the elaborator hasn't * elaborated the module `M` yet, so it merely knows that `M` is a * module and `v` is a member of `M`. It doesn't know that `M.v` is a * module, so it cannot resolve `M.v.v` to the correct member symbol. * Therefore, resolution of such symbols is deferred to the resolver. */ class Resolver(tl: TraceLogger) (using raise: Raise, state: State, ctx: Elaborator.Ctx, cfg: Config): import tl.* import Resolver.* import Expect.* import ModuleChecker.* given TraceLogger = tl /** * Traverse a block and resolve any resolvable sub-terms. This is * usually the entry point for the resolver. */ def traverseBlock(blk: Term.Blk)(using ICtx): ICtx = trace(s"Traversing block: $blk"): traverseStmts(blk.stats).givenIn: traverse(blk.res, expect = Any) ictx @tailrec final def traverseStmts(stmts: Ls[Statement])(using ICtx): ICtx = stmts match case Nil => ictx case stmt :: rest => log(s"Traversing statement: $stmt") val newICtx = stmt match case tdf: Definition => traverseDefn(tdf) case t: Term => traverse(t, expect = Any) ictx // Default Case. e.g., import statements, let bindings. case _ => stmt.subTerms.foreach(traverse(_, expect = NonModule(N))) ictx traverseStmts(rest)(using newICtx) /** * Traverse a term: traverse the sub-terms, resolve the term, and * check the modulefulness of the term. */ def traverse(t: Term, expect: Expect)(using ictx: ICtx): Unit = trace(s"Traversing term: $t (expect = ${expect})"): t match case blk: Term.Blk => traverseBlock(blk) case Term.Rcd(mut, stats) => traverseStmts(stats) case t: Term.IfLike => def simpleSplit(s: SimpleSplit): Unit = s match case SimpleSplit.Cons(head: SimpleSplit.Head.Match, tail) => traverse(head.scrutinee, expect = NonModule(N)) traversePattern(head.pattern) simpleSplit(head.consequent) simpleSplit(tail) case SimpleSplit.Cons(SimpleSplit.Head.Let(sym, term), tail) => traverse(term, expect = NonModule(N)) simpleSplit(tail) case SimpleSplit.Else(default) => traverse(default, expect = Any) case SimpleSplit.End => simpleSplit(t.split) case Term.Handle(lhs, rhs, args, derivedClsSym, defs, body) => traverse(rhs, expect = Class(S("The 'handle' keyword requires a statically known class."))) args.foreach(traverse(_, expect = NonModule(N))) defs.foreach(d => traverseDefn(d.td)) traverse(body, expect = NonModule(N)) case t: Term.Lam => t.params.foreach(traverseParam) traverse(t.body, expect = NonModule(N)) case t: Resolvable => resolve(t, prefer = expect, inAppPrefix = false, inTyPrefix = false, inCtxPrefix = false) case _ => t.subTerms.foreach(traverse(_, expect = NonModule(N))) /* Post Traversal - Checks */ // * The `module checks` checks the term's all possibly overloaded // * definitions / declarations. It raise errors only if the term // * can only be interpreted as a module. // TODO: currently it checks for only overloaded statically // known classes, it might require checking other definitions also // later. if !cfg.noModuleCheck then val evalsToModule = ModuleChecker.evalsToModule(t, prefer = expect) val evalsToStaticClass = ModuleChecker.isStaticClass(t) log(s"Checking ${t}: expect ${expect}, evalsToModule = ${evalsToModule}, evalsToStaticClass = ${evalsToStaticClass}") if expect.`module` && !evalsToModule then raise(ErrorReport(msg"Expected a module; found non-moduleful ${t.describe}." -> t.toLoc :: expect.message)) if expect.nonModule && evalsToModule && !evalsToStaticClass then raise(ErrorReport(msg"Unexpected moduleful ${t.describe}." -> t.toLoc :: expect.message)) if expect.`class` && !evalsToStaticClass then raise(ErrorReport(msg"Expected a statically known class; found ${t.describe}." -> t.toLoc :: expect.message)) end traverse def traverseDefn(defn: Definition)(using ICtx): ICtx = trace(s"Resolving definition: $defn"): def traverseTermDef(tdf: TermDefinition) = val TermDefinition(_k, _sym, _tsym, pss, tps, sign, body, TermDefFlags(isMethod), modulefulness, annotations, comp ) = tdf /** * Add the contextual parameters in pss to the ICtx so that they * can later be referred (be resolved to) in the body of the term * definition. */ def withCtxParams(using ICtx): ICtx = pss .filter(_.flags.ctx) .foldLeft(ictx): (ictx, ps) => ps.params.foldLeft(ictx): (ictx, p) => p.sign match case S(sign) => ictx + (resolveSign(sign, expect = Any), p.sym) case N => // The type signature should be present because of the syntax of contextual parameter. lastWords(s"No type signature for contextual parameter ${defn.showDbg} at ${defn.toLoc}") if isMethod && modulefulness.isModuleful then raise(ErrorReport(msg"${tdf.k.desc.capitalize} returning modules should not be a class member." -> defn.toLoc :: Nil)) pss.foreach(_.allParams.foreach(traverseParam(_))) tps.getOrElse(Nil).flatMap(_.subTerms).foreach(traverse(_, expect = NonModule(N))) sign.foreach(traverseSign(_, expect = if modulefulness.modified then Module(S(msg"${tdf.k.desc.capitalize} marked as returning a 'module' must have a module return type.")) else NonModule(S(msg"${tdf.k.desc.capitalize} must be marked as returning a 'module' in order to have a module return type.")) )) body.foreach(traverse(_, expect = if modulefulness.modified then Module(S(msg"${tdf.k.desc.capitalize} marked as returning a 'module' but not returning a module.")) else NonModule(S(msg"${tdf.k.desc.capitalize} must be marked as returning a 'module' in order to return a module.")) )(using withCtxParams)) annotations.flatMap(_.subTerms).foreach(traverse(_, expect = NonModule(N))) def traverseClassLikeDef(cld: ClassLikeDef) = /** * Add the contextual parameters in the class-like definition to * the ICtx so that they can later be referred (be resolved to) in * the body of the class-like definition. */ def withCtxParams(using ICtx): ICtx = (cld.paramsOpt.toList.iterator ++ cld.auxParams) .filter(_.flags.ctx) .foldLeft(ictx): (ictx, ps) => ps.params.foldLeft(ictx): (ictx, p) => p.sign match case S(sign) => val sym = p.fldSym.getOrElse(die) ictx + (resolveSign(sign, expect = Any), sym) case N => // The type signature should be present because of the syntax of contextual parameter. lastWords(s"No type signature for contextual parameter ${defn.showDbg} at ${defn.toLoc}") cld.paramsOpt.foreach(_.allParams.foreach(traverseParam(_))) cld.annotations.flatMap(_.subTerms).foreach(traverse(_, expect = NonModule(N))) cld.ext.foreach(traverse(_, expect = NonModule(N))) traverseBlock(cld.body.blk)(using withCtxParams) defn match // Case: instance definition. Add the instance to the context. case defn @ TermDefinition(k = Ins, sym = sym, flags = TermDefFlags(isMethod), sign = sign) => log(s"Resolving instance definition ${defn.showDbg}") traverseTermDef(defn) sign match case N => // By the syntax of instance defintiion, the type signature should be present. lastWords(s"No type signature for instance definition ${defn.showDbg} at ${defn.toLoc}") case S(sign) => ictx + (resolveSign(sign, expect = Any), sym) // Case: Fun/Val definition. case defn @ TermDefinition(k = Fun | ImmutVal | MutVal) => log(s"Resolving ${defn.k.desc} definition $defn") traverseTermDef(defn) ictx // Case: Class-like definition. Add the contextual parameters to the // context and use the context to traverse through the body. // Traverse through other subterms with original context. case defn: ClassLikeDef => log(s"Resolving ${defn.kind.desc} definition $defn") defn match case defn: PatternDef => // For pattern definitions, we need to traverse through the pattern body. traversePattern(defn.pattern) case _: ClassLikeDef => () traverseClassLikeDef(defn) ictx // Case: other definition forms. Just traverse through the sub-terms. case t => t.subTerms.foreach(traverse(_, expect = NonModule(N))) ictx def traversePattern(p: Pattern)(using ICtx): Unit = p match case _: (Pattern.Wildcard | Pattern.Literal | Pattern.Range) => () case Pattern.Constructor(target, patternArguments) => traverse(target, expect = PatternConstructor(N)) patternArguments.foreach(_.foreach(traversePattern(_))) case Pattern.Composition(_, left, right) => traversePattern(left) traversePattern(right) case Pattern.Negation(pattern) => traversePattern(pattern) case Pattern.Concatenation(left, right) => traversePattern(left) traversePattern(right) case Pattern.Tuple(leading, spread) => leading.foreach(traversePattern(_)) spread.foreach: (_, spd, trailing) => traversePattern(spd) trailing.foreach(traversePattern(_)) case Pattern.Record(fields) => fields.map((id, p) => traversePattern(p)) case Pattern.Chain(first, second) => traversePattern(first) traversePattern(second) case Pattern.Alias(pattern, _) => traversePattern(pattern) case Pattern.Transform(pattern, _, transform) => traversePattern(pattern) traverse(transform, expect = NonModule(N)) case Pattern.Annotated(pattern, annotations) => traversePattern(pattern) annotations.map(_.toOption).flatten.foreach(traverse(_, expect = NonModule(N))) case Pattern.Guarded(pattern, guard) => traversePattern(pattern) traverse(guard, expect = NonModule(N)) /** * Resolve a resolvable term. This involves: * 1. Resolving the sub-terms of the term. Because resolving a term * may require the sub-terms to be resolved first. * 3. Resolving the (new) symbol of the term. If a term is moduleful, * the symbol should be some module symbol. The implicit arguments * resolved in the previous step should also be used to resolve * the symbol of the term. * 2. Resolving the implicit arguments of the term. This may change * the semantic of the term, so it has to done before the symbol * resolution. * * @param inAppPrefix if true, the currently resolving term is the * prefix of an App. The eta-expansion should only happens on the * outer-most App term, rather than on the in-between App terms * (otherwise the expansion might be redundant). * * @param inCtxPrefix if true, the currently resolving term is the * prefix of an App where the implicit arguments are explicitly * specified, e.g., `f(using 42)`. The implicit arguments should be * resolved on the the App `f(using 42)`, but not on the base of the * App `f`. * @param inTyPrefix if true, the currently resolving term is the * prefix of an TyApp, e.g., `f[Int]`. The implicit arguments should * be resolved on the the TyApp `f[Int]`, but not on the base of the * TyApp `f`. */ def resolve(t: Resolvable, prefer: Expect, inAppPrefix: Bool, inCtxPrefix: Bool, inTyPrefix: Bool)(using ICtx): (Opt[CallableDefinition], ICtx) = trace[(Opt[CallableDefinition], ICtx)]( s"Resolving resolvable term: ${t}, (prefer = ${prefer}, inAppPrefix = ${inAppPrefix}, inCtxPrefix = ${inCtxPrefix}, inTyPrefix = ${inTyPrefix})", _ => s"~> ${t.expanded} (sym = ${t.resolvedSym}, typ = ${t.resolvedTyp})" ): // Resolve the sub-resolvable-terms of the term. val (defn, newICtx1) = t match case _: Term.Resolved => lastWords(s"Term ${t} is already resolved.") // Note: the arguments of the App are traversed later because the // definition is required. case Term.App(lhs: Resolvable, args) => val result = args match case t @ Term.CtxTup(_) => resolve(lhs, prefer = Any, inAppPrefix = true, inCtxPrefix = true, inTyPrefix = inTyPrefix) case _ => resolve(lhs, prefer = Any, inAppPrefix = true, inCtxPrefix = inCtxPrefix, inTyPrefix = inTyPrefix) resolveSymbol(t, prefer = prefer, sign = false) resolveType(t, prefer = prefer) result case Term.App(lhs, _) => traverse(lhs, expect = Any) (t.callableDefn, ictx) case Term.TyApp(lhs: Resolvable, targs) => resolve(lhs, prefer = prefer, inAppPrefix = inAppPrefix, inCtxPrefix = inCtxPrefix, inTyPrefix = true) targs.foreach(traverseSign(_, expect = Any)) resolveSymbol(t, prefer = prefer, sign = false) resolveType(t, prefer = prefer) (t.callableDefn, ictx) case Term.TyApp(lhs, targs) => traverse(lhs, expect = Any) targs.foreach(traverse(_, expect = Any)) (t.callableDefn, ictx) case AnySel(pre: Resolvable, id, cls) => resolve(pre, prefer = Selectable(N), inAppPrefix = false, inCtxPrefix = false, inTyPrefix = false) cls.foreach: case cls: Resolvable => resolve(cls, prefer = Class(N), inAppPrefix = false, inCtxPrefix = false, inTyPrefix = false) case cls => traverse(cls, expect = Class(N)) resolveSymbol(t, prefer = prefer, sign = false) resolveType(t, prefer = prefer) (t.callableDefn, ictx) case AnySel(pre, id, cls) => traverse(pre, expect = Selectable(N)) cls.foreach(traverse(_, expect = Class(N))) (t.callableDefn, ictx) case Term.New(cls, args, rft) => // Term.New has only a type, but does not have a symbol. traverse(cls, expect = Class(S("The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation."))) args.foreach(traverse(_, expect = NonModule(N))) resolveType(t, prefer = prefer) (t.callableDefn, ictx) case Term.LeadingDotSel(nme) => (N, ictx) case Term.Ref(_: BlockMemberSymbol) => resolveSymbol(t, prefer = prefer, sign = false) resolveType(t, prefer = prefer) (t.callableDefn, ictx) case Term.Ref(_) => resolveSymbol(t, prefer = prefer, sign = false) resolveType(t, prefer = prefer) (N, ictx) t.expandedResolvableIn: t => log(s"Resolving resolvable term ${t} with sym = ${t.resolvedSym}, typ = ${t.resolvedTyp}") // Fill the context with possibly the type arguments information. val newICtx2 = newICtx1.givenIn: defn match case S(defn) => val tparams: Opt[Ls[Param]] = defn.tparams val targs: Opt[Ls[Term]] = t match case Term.TyApp(lhs, targs) => S(targs) case _ => N val newICtx = (tparams, targs) match case (S(tparams), S(targs)) => if tparams.length != targs.length then raise(ErrorReport(msg"Expected ${tparams.length.toString()} type arguments, " + msg"got ${targs.length.toString()}" -> t.toLoc :: Nil)) (tparams zip targs).foldLeft(ictx): case (ictx, (tparam, targ)) => val sym = tparam.sym val typ = resolveSign(targ, expect = Any) log(s"Resolving App with type arg ${sym} = $typ") ictx.withTypeArg(sym, typ) case (S(tparams), N) => tparams.foldLeft(ictx): case (ictx, tparam) => log(s"Resolving App with type arg ${tparam.sym} unspecified") ictx case (N, _) => ictx newICtx case N => ictx // Resolve the implicit arguments. newICtx2.givenIn: // Create a new term definition for App terms. The new term // definition is for handling partial applications. // // For example: In `fun f(a)(b) = 42`, f's definition should // indicate that it accepts two argument lists; f(42)'s definition // should indicate that it accepts one argument list; f(42)(43)'s // definition should indicate that it accepts zero argument lists. // // Currently, only parameters are processed for these new term // definitions. The type parameters and result type are kept // as-is. In the future we may take them into consideration as // well. val newDefn: Opt[CallableDefinition] = t match case Term.App(lhs, as) => defn match case S(defn @ CallableDefinition(params = ps :: pss)) => val (argCountUB, argCountLB) = as match // Tup: regular arguments case tup: (Term.Tup | Term.CtxTup) => val fields = tup match case Term.Tup(fs) => fs case Term.CtxTup(fs) => fs ( !fields.exists(_.isInstanceOf[Spd]), ( fields.count: case Fld(asc = S(_)) => false case _: Fld => true case _: Spd => false ) + ( fields.collectFirst: case Fld(asc = S(_)) => 1 .getOrElse(0) ) ) // Other: spread arguments case _ => (false, 0) (ps.paramCountUB, argCountUB) match case (true, true) => if ps.paramCountLB != argCountLB then raise(ErrorReport(msg"Expected ${ps.paramCountLB.toString()} arguments, " + msg"got ${argCountLB.toString()}" -> as.toLoc :: Nil)) case (true, false) => if ps.paramCountLB < argCountLB then raise(ErrorReport(msg"Expected ${ps.paramCountLB.toString()} arguments, " + msg"got at least ${argCountLB.toString()}" -> as.toLoc :: Nil)) case (false, true) => if ps.paramCountLB > argCountLB then raise(ErrorReport(msg"Expected at least ${ps.paramCountLB.toString()} arguments, " + msg"got ${argCountLB.toString()}" -> as.toLoc :: Nil)) case (false, false) => () /** * Zip (pair) a list of parameter and a list of arguments. * * If there are some spread parameters, we are not able to * pair all the parameters and arguments statically. We will * try to pair as many as possible. */ @tailrec def zip(ps: Ls[Param], as: Ls[Elem], recordArgs: Ls[Fld], beforeSpread: Bool): Ls[Fld] = (ps, as) match // The spread argument takes all the remaining arguments. case (ps, (a: Spd) :: as) => traverse(a.term, expect = NonModule(N)) zip(ps, as, recordArgs, false) case (ps, a :: as) if !beforeSpread => a.subTerms.foreach(traverse(_, expect = NonModule(N))) zip(ps, as, recordArgs, false) // Pair the parameter and the argument. case (p :: ps, (a @ Fld(asc = N)) :: as) => traverse(a.term, // note: we accept regular arguments for module parameters expect = if p.modulefulness.isModuleful then Any else NonModule(S(msg"Module argument passed to a non-module parameter.")) ) zip(ps, as, recordArgs, true) // Record Arguments. They are pushed to the last parameter. case (_, (a @ Fld(asc = S(_))) :: as) => zip(ps, as, a :: recordArgs, true) // If there are more parameters, there must be a spread // argument, or some record arguments before. case (p :: ps, Nil) => recordArgs.reverse // If there are more arguments, all of them go to `restParam`. case (Nil, a :: as) => a.subTerms.foreach(traverse(_, expect = NonModule(N))) zip(Nil, as, recordArgs, beforeSpread) case (Nil, Nil) => recordArgs.reverse end zip // Application arguments that are not tuples represent spreads, as in `f(...arg)` val args = as match case Term.Tup(args) => args case Term.CtxTup(args) => args case spd => Spd(SpreadKind.Eager, spd) :: Nil // The lhs of the App is already traversed by the recursive // `traverse` or `resolve` at the beginning. val recordArgs = zip(ps.params, args, Nil, true) recordArgs.foreach: _.subTerms.foreach(traverse(_, expect = NonModule(N))) S(defn.copy(params = pss)) case _ => traverse(as, expect = NonModule(N)) N case _ => defn // Resolve the implicit arguments. newDefn match case S(defn) if !inCtxPrefix && !inTyPrefix => /** * Resolve all possible implicit arguments and perform eta-expansion. * * @return (1) A lambda accepting a term, applying the implicit * arguments and performing eta-expansion on the term, and * return the result. (2) The residual parameter lists that are not * consumed by this resolution. */ def expand(pss: Ls[ParamList], lam: Term => Term, bod: Term => Term): (Term => Term, Ls[ParamList]) = pss match // The current parameter list is not a using clause, and // there are more using clauses in later parameter lists, // so perform eta-expansion. case (ps @ ParamList( flags = ParamListFlags(ctx = false) )) :: pss if !inAppPrefix && pss.exists(_.flags.ctx) => val as = ps.params.map(p => Fld(p.flags, p.sym.ref().dontResolve, N)) // val as = ps.params.map(p => Fld(p.flags, p.sym.ref().resolve, N)) val newLam = (t: Term) => lam(Term.Lam(ps, t)) val newBod = (t: Term) => Term.App(bod(t), Term.Tup(as)(DummyTup))(DummyApp, N, FlowSymbol("implicit app")).dontResolve expand(pss, newLam, newBod) // The current parameter list is a using clause, so resolve // implicit arguments from the context. case (ps @ ParamList( flags = ParamListFlags(ctx = true) )) :: pss => val as = ps.params.map(resolveArg(_)(t)) val newBod = (t: Term) => Term.App(bod(t), Term.Tup(as)(DummyTup))(DummyApp, N, FlowSymbol("implicit app")).dontResolve expand(pss, lam, newBod) case _ => ((t: Term) => lam(bod(t)), pss) val (expansionFn, pss) = expand(defn.params, identity, identity) if defn.params.length =/= pss.length then val expansion = expansionFn(t.duplicate) t.expand(S(expansion)) expansion match // * expansion may change the semantics, thus symbol is also changed case r: Resolvable => resolveSymbol(r, prefer = prefer, sign = false) resolveType(r, prefer = prefer) case _ => () (S(defn.copy(params = pss)), ictx) case S(defn) => t.dontResolve (S(defn), ictx) case _ => t.dontResolve (N, ictx) def disambSym(expect: Expect, sign: Bool)(bms: BlockMemberSymbol): Opt[DefinitionSymbol[?]] = expect match // If it is expecting a class or module specifically, cast the symbol. case _: Module => bms.asMod case _: Class => bms.asCls case _: Selectable => bms.asModOrObj orElse bms.asTrm // If it is expecting a generic symbol, use asPrinciple for the "default interpretation". case _: (Any.type | NonModule) if sign => bms.asTpe case _: (Any.type | NonModule) => bms.asPrincipal case _: PatternConstructor => bms.asCls orElse bms.asObj orElse bms.asAls orElse bms.asPat orElse bms.asMod orElse bms.asTrm /** * Resolve the symbol for a resolvable term, which was not resolved by * the elaborator due to unready definitions. After the symbol * resolution, the term is expanded into the same term, * * In particular, for Sel and SynthSel terms, it checks the definition * of the prefix (note that the prefix should be resolved before this * term is resolved), and if prefix is moduleful, it resolves the * symbol for the member to be selected. * * Then, for all terms including Sel and SynthSel, it unwraps the * parameters as an App, even for a generalized "App" that has no * parameter list (in order to handle implicit applications), and * resolves the symbol for the result of this App. * * This also expands the LHS `Foo` of a selection to `Foo.class` if * the selection is selecting a static member from a lifted module. */ def resolveSymbol(t: Resolvable, prefer: Expect, sign: Bool)(using ictx: ICtx): Unit = trace[Unit]( s"Resolving symbol for term: ${t} (prefer = ${prefer})", _ => s"-> (sym = ${t.resolvedSym}, typ = ${t.resolvedTyp})" ): // If the term has an expansion already, it is likely that there is // an internal error because otherwise we should resolve the symbol // of the expansion instead. /* if t.hasExpansion then lastWords: s"resolveSymbol: term ${t} already has an expansion ~> ${t.expanded}, " + s"thus the resolver cannot resolve its symbol" */ // * We can't perform the check because of UCS and handler reusing terms. t.expandedResolvableIn: t => t match // The symbol resolution already failed in the elaborator. We will // not try to resolve it again in the resolver. case _ if t.symbol.exists(_.isInstanceOf[ErrorSymbol]) => () case t @ Term.Ref(bsym: BlockMemberSymbol) => log(s"Resolving symbol for reference ${t} (bsym = ${bsym})") val sym = disambSym(prefer, sign = sign)(bsym) sym.foreach: sym => t.expand(S((t.duplicate.resolved(sym)))) resolveType(t, prefer = prefer) log(s"Resolved symbol for ${t}: ${sym}") case t @ AnySel(lhs: Resolvable, id, cls) => lhs.expandedResolvableIn: lhs => log(s"Resolving symbol for selection ${t}, defn = ${lhs.defn}") val clsDefn = cls.flatMap: sym => sym.resolvedSym.flatMap(_.asCls).map: clsSym => clsSym.defn.getOrElse(die) // Singleton Definitions lhs.singletonDefn.foreach: defn => val fsym = defn.body.members.get(id.name) fsym match case S(bms: BlockMemberSymbol) => log(s"Resolving symbol for ${t}, defn = ${lhs.defn}") disambSym(prefer, sign)(bms) match case S(ds) => t.expand(S(t.withSym(bms).resolved(ds))) case N => log(s"Unable to disambiguate ${bms}") t.expand(S(t.withSym(bms))) log(s"Resolved symbol for ${t}: ${bms}") case N => t.expand(S(t.withSym(ErrorSymbol(id.name, Tree.Dummy)))) raise: ErrorReport( msg"${defn.kind.desc.capitalize} '${defn.sym.nme}' " + msg"does not contain member '${id.name}'" -> t.toLoc :: Nil, extraInfo = S(defn)) // Class-Like Definitions (clsDefn orElse lhs.typDefn).foreach: case defn: ClassLikeDef => // A reference to a member within the class is elaborated into a SynthSel, e.g., // class C with // fun f = 42 // f // The `f` in the body is elaborated into SynthSel(class:Foo, 'f'). val fsym = defn.body.members.get(id.name) fsym match case S(bms: BlockMemberSymbol) => log(s"Resolving symbol for ${t}, defn = ${lhs.defn}") disambSym(prefer, sign)(bms) match case S(ds) => t.expand(S(t.withSym(bms).resolved(ds))) case N => log(s"Unable to disambiguate ${bms}") t.expand(S(t.withSym(bms))) log(s"Resolved symbol for ${t}: ${bms}") case N => // TODO @Harry: Appropriately resolve all selections on classes. // - MLscript programs selecting JS members without properly defining them. // - Inherited members. case defn => log(s"Unsupported selection from definition: ${defn}") case t @ Term.New(cls, _, N) => // cls is already resolved, so the symbol should be present; // otherwise, there is already an error raised. cls.resolvedSym match case S(clsSym: ClassSymbol) => t.expand(S(t.duplicate.resolved(clsSym))) case S(sym) => lastWords(s"Expected a class symbol; found ${sym} for term ${t}.") case N => case _ => /** * Resolve the type for a resolvable term representing an expression * (as opposed to a term representing a type, i.e., signature). The * resolvable term should should already be symbol-resolved by * `resolveSymbol`. After this type resolution, the resolvable term is * expanded into a copy of the term with the `typ` field set to the * resolved type. */ def resolveType(t: Resolvable, prefer: Expect)(using ictx: ICtx): Unit = t.expandedResolvableIn: t => trace[Unit]( s"Resolving the type for term: ${t} (prefer = ${prefer}, sym = ${t.resolvedSym})", _ => s"-> typ = ${t.resolvedTyp}" ): def disambSym(bms: BlockMemberSymbol): Opt[DefinitionSymbol[?]] = prefer match case _: Module => bms.asMod case _: Class => bms.asCls case _: Selectable => bms.asModOrObj orElse bms.asTrm case _: (Any.type | NonModule) => bms.asPrincipal t match case Term.New(cls, _, N) => cls.resolvedSym match case S(clsSym: ClassSymbol) => val ty = Type.Ref(clsSym, Nil) t.expand(S(t.withTyp(ty))) case _ => // cls is already resolved, so the symbol should be present; // otherwise, there is already an error raised. () case t @ Apps(base: Resolvable, ass) => val decl = base.resolvedSym match case S(bms: BlockMemberSymbol) => val disambBms = disambSym(bms) log(s"Disambiguate ${bms} (${bms.asTrm}) into ${disambBms} (defn = ${disambBms.map(_.defn)}), preferring ${prefer}") disambBms match case S(disambBms) => disambBms.defn case N => bms.asPrincipal.flatMap(_.defn) case S(bls: BlockLocalSymbol) => bls.decl case S(ds: DefinitionSymbol[?]) => ds.defn case _ => N log(s"Declaration: ${decl}") decl match case S(defn: TermDefinition) if defn.params.length === ass.length => val ty = defn.sign.map(resolveSign(_, expect = Expect.fromModulefulness(defn.modulefulness))) ty.foreach(ty => t.expand(S(t.withTyp(ty)))) case S(defn: Param) if ass.isEmpty => val ty = defn.sign.map(resolveSign(_, expect = Expect.fromModulefulness(defn.modulefulness))) ty.foreach(ty => t.expand(S(t.withTyp(ty)))) case S(defn: ModuleOrObjectDef) if ass.isEmpty => val ty = Type.Ref(defn.sym, Nil) t.expand(S(t.withTyp(ty))) case S(defn: ClassDef) => base match case Term.Ref(inner: InnerSymbol) => val ty = Type.Ref(defn.sym, Nil) t.expand(S(t.withTyp(ty))) case _ => case _ => case _ => end resolveType def resolveArg(p: Param)(lhs: Term)(using ictx: ICtx): Elem = log(s"Resolving implicit argument, expecting a ${p.sign}") p.sign match case S(sign) => // FIXME[Harry]: Don't re-resolve signatures for every argument! val ty = resolveSign(sign, expect = if p.modulefulness.modified then Module(N) else NonModule(N)) log(s"Resolving implicit argument, expecting a ${ty.show}") ictx.query(ty) match case R(i) => log(s"Resolved ${p.sign} with instance ${i}") val ref = i.sym.ref() traverse(ref, // note: we accept regular arguments for module parameters expect = if p.modulefulness.isModuleful then Any else NonModule(S(msg"Module argument passed to a non-module parameter.")), ) Fld(p.flags, ref, N) case L(msgs) => raise(ErrorReport( msg"Cannot query instance of type ${ictx.showTy(ty)} for call: " -> lhs.toLoc :: msg"Required by contextual parameter declaration: " -> p.toLoc :: msgs)) Fld(FldFlags.empty, Term.Error, N) case N => // By the syntax of contextual parameter, // the type signature should be present. lastWords(s"No type signature for contextual parameter ${p.showDbg} at ${p.toLoc}") def traverseParam(p: Param)(using ictx: ICtx): Unit = log(s"Resolving parameter ${p.showDbg}") val ty = p.sign.map(sign => resolveSign(sign, expect = if p.modulefulness.modified then Module(N) else NonModule(N))) p.signType = ty if p.modulefulness.modified then if p.sign.isEmpty then raise(ErrorReport(msg"Module parameter must have explicit type." -> p.sym.toLoc :: Nil)) p.sign.foreach: traverseSign(_, expect = if p.modulefulness.modified then Module(S(msg"Module parameter must have a module type.")) else NonModule(S(msg"Non-module parameter must have a non-module type.")), ) /** * Traverse through a term that is expected to be a type. In this * process, it reports any non-type terms, checks if the term * satisfies the expectation, resolves the symbol of the term, and * checks the arity of type params/args. * * @param inAppPrefix if true, the currently traversing term is the * prefix of an TyApp. * @param expect the expectation on the type. See [[Expect]] for * details. */ def traverseSign(t: Term, expect: Expect, inAppPrefix: Bool = false)(using ictx: ICtx): Unit = trace(s"Traversing type ${t}, expecting ${expect}"): // * Traverse through the sub-terms. t match case Term.Ref(_) => case Term.Lit(_) => case Term.Tup(_) => t.subTerms.foreach(traverse(_, expect = NonModule(N))) case Term.UnitVal() => // Literals with operators. e.g., -42 case Term.App(Term.Ref(_: BuiltinSymbol), Term.Tup(Fld(term = Term.Lit(_)) :: Nil)) => // Selection. The prefix should be a term, rather than a type, that // can be selected from. This should not be a selection projection. case AnySel(base, _, N) => base.subTerms.foreach(traverse(_, expect = Any)) // Type Application. Traverse the type constructor and arguments, // respectively. case Term.TyApp(con, targs) => traverseSign(con, expect = expect, inAppPrefix = true) targs.foreach(traverseSign(_, expect = Expect.NonModule(S("Type arguments should be non-moduleful types.")))) // Complex type: Function type, Wildcard type, Composed type, // Negation type, Forall type, case t: (Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup) => t.subTerms.foreach(traverseSign(_, expect = Expect.NonModule(N))) // t is not a type. case _ => raise(ErrorReport(msg"Expected a type, got ${t.describe}" -> t.toLoc :: Nil)) return // * Resolve the symbol and type of the term. val typ = t match case t: Resolvable => resolveSymbol(t, prefer = expect, sign = true) val typ = resolveSign(t, expect = expect) t.expandedResolvableIn(_.withTyp(typ)) typ case _ => val typ = resolveSign(t, expect = expect) typ // * Check if the term satisfies the expectation. // * Check the arity of type params/args. typ match case Type.Ref(sym: TypeSymbol, targs) if !inAppPrefix => sym.defn.flatMap(CallableDefinition.fromDefn(_)).foreach: case CallableDefinition(tparams = tparams) => val tparamsNum = tparams.map(_.length) val targsNum = targs.length if tparamsNum.getOrElse(0) =/= targsNum then val tparamsMsg = tparamsNum.getOrElse("no").toString val targsMsg = if targsNum === 0 then "none" else targsNum.toString raise: ErrorReport: msg"Expected ${tparamsMsg} type arguments, " + msg"got ${targsMsg}" -> t.toLoc :: Nil case Type.Ref(sym: VarSymbol, targs) if !inAppPrefix => // TODO: check arity? case _ => /** * Given a symbol-resolved term that represents a type, resolve the * type that it represents. */ def resolveSign(t: Term, expect: Expect): Type = def raiseError(sym: Opt[Symbol] = N) = val defnMsg = sym match case S(sym: DefinitionSymbol[?]) => sym.defn match case S(defn: TermDefinition) => s" denoting ${defn.k.desc} '${defn.sym.nme}'" case S(defn: ClassLikeDef) => s" denoting ${defn.kind.desc} '${defn.sym.nme}'" case _ => "" case _ => "" expect match case expect: Module => raise: ErrorReport(msg"Expected a module type; found ${t.describe}${defnMsg}." -> t.toLoc :: expect.message) case expect: Class => raise: ErrorReport(msg"Expected a statically resolvable class; found ${t.describe}${defnMsg}." -> t.toLoc :: expect.message) case expect: NonModule => raise: ErrorReport(msg"Expected a non-module type; found ${t.describe}${defnMsg}." -> t.toLoc :: expect.message) case _ => raise: ErrorReport(msg"Expected a type, got ${t.describe}${defnMsg}" -> t.toLoc :: Nil) Type.Error t match // If the term is a type application, e.g., T[A, ...], resolve the // type constructor and arguments respectively. case Term.TyApp(con, args) => resolveSign(con, expect = expect) match case Type.Ref(sym, Nil) => Type.Ref(sym, args.map(resolveSign(_, expect = Any))) case _ => raise(ErrorReport(msg"Expected a type constructor, got ${t.describe}" -> t.toLoc :: Nil)) Type.Error case Term.Lit(_) => if expect.module then raiseError() else Type.NotImplemented // TODO: Support Lit case Term.UnitVal() => if expect.module then raiseError() else Type.NotImplemented // TODO: Support UnitVal case Term.App(Term.Ref(_: BuiltinSymbol), Term.Tup(Fld(term = Term.Lit(_)) :: Nil)) => if expect.module then raiseError() else Type.NotImplemented // TODO: Support Lit with operator case _: (Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup | Term.Lit) => if expect.module then raiseError() else Type.NotImplemented // TODO: Support complex types // Otherwise, resolve the term directly. case _ => t.resolvedSym match // A VarSymbol is probably a type parameter. case S(sym: VarSymbol) if ModuleChecker.isTypeParam(sym) => if expect.module then raiseError(S(sym)) else Type.Ref(sym, Nil) case S(tsym) => val dtsym = expect match case expect: Module => tsym.asMod.getOrElse(raiseError(S(tsym))) case expect: Class => tsym.asCls.getOrElse(raiseError(S(tsym))) case expect: NonModule => tsym.asNonModTpe.getOrElse(raiseError(S(tsym))) case _ => tsym.asTpe.getOrElse(raiseError(S(tsym))) dtsym match case dtsym: TypeSymbol => Type.Ref(dtsym, Nil) case _ => Type.Error case N => raise(ErrorReport(msg"Expected a type, got a non-type ${t.describe}" -> t.toLoc :: Nil)) Type.Error end Resolver object ModuleChecker: extension (s: Split) private def results: Ls[Term] = def go(s: Split): Ls[Term] = s match case Split.Cons(head, tail) => go(head.continuation) ::: go(tail) case Split.Let(_, _, tail) => go(tail) case Split.Else(term) => term :: Nil case Split.End => Nil go(s) /** Checks if a symbol is of a type parameter. */ def isTypeParam(sym: VarSymbol): Bool = sym.decl .exists(_.isInstanceOf[TyParam]) import Resolver.Expect /** Checks the term's modulefulness. */ def evalsToModule(t: Term, prefer: Expect): Bool = def checkDecl(decl: Declaration): Bool = decl match /* Type Declaration / Defintiions */ // A type is not moduleful if it is not a module. (obvious!) case ModuleOrObjectDef(kind = Mod) => true // Objects use ModuleDef but is not moduleful. case _: TypeLikeDef => false /* Term Declarationis / Definitions */ case defn: TermDefinition => defn.modulefulness.isModuleful case defn: Param => defn.modulefulness.isModuleful // A type param is not a module because it is a type. case defn: TyParam => false def checkSym(sym: Symbol): Bool = sym match case sym: BuiltinSymbol => false case sym: BlockLocalSymbol => sym.decl.exists(checkDecl) case sym: MemberSymbol => prefer match case Expect.Module(_) => sym.existsModuleful case _ => !sym.existsNonModuleful case sym => lastWords(s"unsupported symbol type ${sym.getClass} (${sym})") t match case Term.Blk(_, res) => evalsToModule(res, prefer = prefer) case Term.IfLike(`if`, IfLikeForm.ReturningIf, split) => split.results.exists(evalsToModule(_, prefer = prefer)) case t: Resolvable => t.resolvedTyp match case S(ty) => ty.symbol.map(checkSym(_)).getOrElse(false) case N => false case _ => false def isStaticClass(t: Term): Bool = t.resolvedSym.exists(_.asCls.isDefined) end ModuleChecker ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/SimpleSplit.scala ================================================ package hkmc2 package semantics import mlscript.utils.*, shorthands.*, syntax.*, Tree.{BoolLit, Keywrd} import Keyword.{`do`, `else`, `then`}, utils.TL, Elaborator.{Ctx, State} /** * Similar to `Split` but contains nested patterns (i.e., class `Pattern`). */ enum SimpleSplit extends AutoLocated with ProductWithTail: import SimpleSplit.Head case Cons(branch: SimpleSplit.Head, tail: SimpleSplit) /** * The end of splits with a default term. * * @param default The default term. * @param kw The keyword of `then` or `else`. It is `None` if the split is * the topmost one. */ case Else(default: Term)(val kw: Opt[Keywrd[`else`.type | `then`.type | `do`.type]]) case End inline def ~:(head: SimpleSplit.Head): Cons = Cons(head, this) def ~~:(front: SimpleSplit): SimpleSplit = front match case Cons(head, tail) => Cons(head, tail ~~: this) case els: Else => els case End => this protected def children: Vector[Located] = this match case Cons(branch, tail) => Vector.double(branch, tail) case els @ Else(default) => els.kw match case N => Vector.single(default) case S(kw) => Vector.double(kw, default) case End => Vector.empty def subTerms: Vector[Term] = this match case Cons(branch, tail) => branch.subTerms.toVector ++ tail.subTerms case Else(default) => Vector.single(default) case End => Vector.empty /** Free variable names, accounting for pattern bindings in match heads * and let bindings in let heads. */ lazy val freeVars: Set[Str] = this match case Cons(head, tail) => head match case Head.Match(scrutinee, pattern, consequent) => val patVarNames = pattern.variables.varMap.keySet scrutinee.freeVars ++ pattern.subTerms.iterator.flatMap(_.freeVars) ++ (consequent.freeVars -- patVarNames) ++ tail.freeVars case Head.Let(binding, term) => term.freeVars ++ (tail.freeVars - binding.nme) case Else(default) => default.freeVars case End => Set.empty def showDbg(using DebugPrinter): Str = this match case Cons(branch, tail) => s"${branch.showDbg}; ${tail.showDbg}" case Else(default) => s"else ${default.showDbg}" case End => "" def prettyPrint(kw: Keyword.SplitLike)(using DebugPrinter): Str = SimpleSplit.prettyPrint(this, kw) /** Get the results of all branches. */ def results: Ls[Term] = def go(acc: Ls[Term], split: SimpleSplit): Ls[Term] = split match case Cons(_: Head.Let, tail) => go(acc, tail) case Cons(Head.Match(_, _, consequent), alternative) => go(go(acc, consequent), alternative) case Else(default) => default :: acc case End => acc go(Nil, this).reverse /** This field is designed to be compatible with bbML. */ private var _expandedSplit: Opt[Split] = N /** */ def getExpandedSplit(using TL, Ctx, State, Raise): Split = _expandedSplit.getOrElse: val split = Split.from(this) _expandedSplit = S(split) split def mkClone(using State): SimpleSplit = this match case Cons(head, tail) => Cons(head match case Head.Match(scrutinee, pattern, consequent) => Head.Match(scrutinee.mkClone.asInstanceOf, pattern, consequent.mkClone) // TODO: clone `pattern`? case Head.Let(binding, term) => Head.Let(binding, term.mkClone) , tail.mkClone) case e @ Else(default) => Else(default.mkClone)(e.kw) case End => End end SimpleSplit object SimpleSplit: object IfThenElse: def unapply(split: SimpleSplit): Opt[(Term, Term, Term)] = split match case Cons( Head.Let(binding, condition), Cons(Head.Match( scrutinee, Pattern.Literal(BoolLit(true)), Else(consequent)), Else(alternative)) ) if scrutinee.sym === binding => S((condition, consequent, alternative)) case _ => N /** Represents a single branch of a simple split. */ enum Head extends AutoLocated: case Match(scrutinee: Term.Ref, pattern: Pattern, consequent: SimpleSplit) case Let(binding: BlockLocalSymbol, term: Term) def subTerms: Vector[Term] = this match case Match(scrutinee, pattern, consequent) => scrutinee +: (pattern.subTerms ++ consequent.subTerms) case Let(_, term) => Vector.single(term) def showDbg(using DebugPrinter): Str = this match case Match(scrutinee, pattern, consequent) => val consequentStr = consequent match case Cons(_, _) => s"and ${consequent.showDbg}" case Else(default) => s"then ${default.showDbg}" case End => "then {}" s"${scrutinee.showDbg} is ${pattern.showDbg} ${consequentStr}" case Let(binding, term) => s"let ${binding.nme} = ${term.showDbg}" protected def children: Vector[Located] = this match case Match(scrutinee, pattern, consequent) => Vector.triple(scrutinee, pattern, consequent) case Let(binding, term) => Vector.double(binding, term) private[semantics] object prettyPrint: /** Represents lines with indentations. */ type Lines = Ls[(Int, Str)] extension (lines: Lines) /** Increase the indentation of all lines by one. */ def indent: Lines = lines.map: case (n, line) => (n + 1, line) /** Make a multi-line string. */ def toIndentedString: Str = lines.iterator.map: case (n, line) => " " * n + line .mkString("\n") extension (prefix: String) /** * If the first line does not have indentation and the remaining lines are * indented, prepend the given string to the first line. Otherwise, prepend * the given string to the first line and indent all remaining lines. * * When you want to amend the title of lines, you should use this function. */ def #:(lines: Lines): Lines = lines match case all @ ((0, line) :: lines) if lines.forall(_._1 > 0) => if prefix.isEmpty then all else (0, s"$prefix $line") :: lines case lines => (0, prefix) :: lines.indent inline def apply(s: SimpleSplit, kw: Keyword.SplitLike)(using DebugPrinter): Str = showSplit(kw.name, s) /** Show a split as a list of lines. * @param isFirst whether this is the first and frontmost branch * @param isTopLevel whether this is the top-level split */ private[semantics] def split(s: SimpleSplit, isFirst: Bool, isTopLevel: Bool)(using DebugPrinter): Lines = s match case SimpleSplit.Cons(head: Head.Match, tail) => (branch(head, isTopLevel) match case (n, line) :: tail => (n, line) :: tail case Nil => Nil ) ::: split(tail, false, isTopLevel) case SimpleSplit.Cons(Head.Let(nme, rhs), tail) => (0, s"let $nme = ${rhs.showDbg}") :: split(tail, false, true) case SimpleSplit.Else(t) => (if isFirst && !isTopLevel then "" else "else") #: term(t) case SimpleSplit.End => Nil private def term(t: Statement)(using DebugPrinter): Lines = (0, t.showDbg) :: Nil private def branch(b: Head.Match, isTopLevel: Bool)(using DebugPrinter): Lines = val Head.Match(scrutinee, pattern, consequent) = b val lines = split(consequent, true, false) val prefix = s"${scrutinee.sym} is ${pattern.showDbg}" consequent match case SimpleSplit.Else(_) => (prefix + " then") #: lines case _ => (prefix + " and") #: lines private def showSplit(prefix: Str, s: SimpleSplit)(using DebugPrinter): Str = val lines = split(s, true, true) (if prefix.isEmpty then lines else prefix #: lines).toIndentedString end prettyPrint ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Split.scala ================================================ package hkmc2 package semantics import mlscript.utils.*, shorthands.* import syntax.*, Elaborator.State, ucs.FlatPattern final case class Branch(scrutinee: Term.Ref, pattern: FlatPattern, continuation: Split) extends AutoLocated: def mkClone(using State): Branch = val scrutineeClone = new Term.Ref(scrutinee.sym) (Tree.Ident(scrutinee.tree.name), scrutinee.refNum, scrutinee.typ) Branch(scrutineeClone, pattern.mkClone, continuation.mkClone) override def children: Vector[Located] = Vector.triple(scrutinee, pattern, continuation) def showDbg(using DebugPrinter): String = s"${scrutinee.sym.nme} is ${pattern.showDbg} -> { ${continuation.showDbg} }" object Branch: def apply(scrutinee: Term.Ref, continuation: Split): Branch = Branch(scrutinee, FlatPattern.Lit(Tree.BoolLit(true)), continuation) enum Split extends AutoLocated with ProductWithTail: case Cons(head: Branch, tail: Split) case Let(sym: BlockLocalSymbol, term: Term, tail: Split) case Else(default: Term) case End inline def ~:(head: Branch): Split = Split.Cons(head, this) def mkClone(using State): Split = this match case Cons(head, tail) => Cons(head.mkClone, tail.mkClone) case Let(sym, term, tail) => Let(sym, term.mkClone, tail.mkClone) case Else(default) => Else(default.mkClone) case End => End /** Used to indicate whether the `Split` was duplicated during desugaring or * normalization. */ def duplicated: Bool = false private var _duplicated: Bool = false def setDuplicated: this.type = if this != End then _duplicated = true this def duplicate: Split = (this match case Cons(head, tail) => Cons(head, tail.duplicate) case Let(name, term, tail) => Let(name, term, tail.duplicate) case Else(default) => Else(default) case End => End).setDuplicated lazy val isFull: Bool = this match case Split.Cons(_, tail) => tail.isFull case Split.Let(_, _, tail) => tail.isFull case Split.Else(_) => true case Split.End => false lazy val isEmpty: Bool = this match case Split.Let(_, _, tail) => tail.isEmpty case Split.Else(_) | Split.Cons(_, _) => false case Split.End => true final override def children: Vector[Located] = this match case Split.Cons(head, tail) => Vector.double(head, tail) case Split.Let(name, term, tail) => Vector.triple(name, term, tail) case Split.Else(default) => Vector.single(default) case Split.End => Vector.empty def subTerms: Vector[Term] = this match case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => scrutinee +: (pattern.subTerms ++ continuation.subTerms ++ tail.subTerms) case Split.Let(_, term, tail) => term +: tail.subTerms case Split.Else(term) => Vector.single(term) case Split.End => Vector.empty /** Free variable names, accounting for let bindings. */ lazy val freeVars: Set[Str] = this match case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => scrutinee.freeVars ++ pattern.subTerms.iterator.flatMap(_.freeVars) ++ continuation.freeVars ++ tail.freeVars case Split.Let(sym, term, tail) => term.freeVars ++ (tail.freeVars - sym.nme) case Split.Else(term) => term.freeVars case Split.End => Set.empty final def showDbg(using DebugPrinter): String = this match case Split.Cons(head, tail) => s"${head.showDbg}; ${tail.showDbg}" case Split.Let(name, term, tail) => s"let ${name} = ${term.showDbg}; ${tail.showDbg}" case Split.Else(default) => s"else ${default.showDbg}" case Split.End => "" final override def withLoc(loco: Option[Loc]): this.type = super.withLoc: this match // `Split.Nil` must not have a location. This prevents sharing locations, // which causes the assertion of distinctness of origins to fail. case Split.End => N case _: Split.Else => N // FIXME: @Luyu pls clean up this mess case _ => loco var isFallback: Bool = false def prettyPrint(using DebugPrinter): Str = Split.prettyPrint(this) end Split extension (split: Split) def ~~:(fallback: Split): Split = if fallback == Split.End || split.isFull then split else split match case Split.Cons(head, tail) => Split.Cons(head, tail ~~: fallback) case Split.Let(name, term, tail) => Split.Let(name, term, tail ~~: fallback) case Split.Else(_) => lastWords("impossible since split is not full") case Split.End => fallback object Split: def default(term: Term): Split = Split.Else(term) import SimpleSplit as SS import Elaborator.{Ctx, State} import utils.{tl, TL} import collection.mutable.{Map as MutMap} import ups.SplitCompiler, SplitCompiler.{MakeConsequent, Scrut, SymbolScrut} import Term.Ref def from(rootSplit: SS)(using tl: TL)(using Ctx, Raise, State): Split = val compiler = new SplitCompiler() val scrutCache = MutMap.empty[Ref, Scrut] def go(split: SS): Split = split match case SS.Cons(branch, tail) => branch match case SS.Head.Match(ref, pattern, consequent) => lazy val alternative = go(tail) val makeConsequent: MakeConsequent = (output, bindings) => bindings.iterator.foldLeft(go(consequent)): case (innerSplit, (symbol, mkTerm)) => Let(symbol, mkTerm(), innerSplit) compiler.makeMatchSplit (scrutCache.getOrElseUpdate(ref, Scrut.from(ref)), pattern, false) (makeConsequent, alternative) case SS.Head.Let(binding, term) => Let(binding, term, go(tail)) case SS.Else(default) => Else(default) case SS.End => End go(rootSplit) private object prettyPrint: /** Represents lines with indentations. */ type Lines = Ls[(Int, Str)] extension (lines: Lines) /** Increase the indentation of all lines by one. */ def indent: Lines = lines.map: case (n, line) => (n + 1, line) /** Make a multi-line string. */ def toIndentedString: Str = lines.iterator.map: case (n, line) => " " * n + line .mkString("\n") extension (prefix: String) /** * If the first line does not have indentation and the remaining lines are * indented, prepend the given string to the first line. Otherwise, prepend * the given string to the first line and indent all remaining lines. * * When you want to amend the title of lines, you should use this function. */ def #:(lines: Lines): Lines = lines match case all @ ((0, line) :: lines) if lines.forall(_._1 > 0) => if prefix.isEmpty then all else (0, s"$prefix $line") :: lines case lines => (0, prefix) :: lines.indent inline def apply(s: Split)(using DebugPrinter): Str = showSplit("‹if|while›", s) private def showSplit(prefix: Str, s: Split)(using DebugPrinter): Str = /** Show a split as a list of lines. * @param isFirst whether this is the first and frontmost branch * @param isTopLevel whether this is the top-level split */ def split(s: Split, isFirst: Bool, isTopLevel: Bool): Lines = val lines = s match case Split.Cons(head, tail) => (branch(head, isTopLevel) match case (n, line) :: tail => (n, line) :: tail case Nil => Nil ) ::: split(tail, false, isTopLevel) case Split.Let(nme, rhs, tail) => (0, s"let $nme = ${rhs.showDbg}") :: split(tail, false, true) case Split.Else(t) => (if isFirst && !isTopLevel then "" else "else") #: term(t) case Split.End => Nil if s.duplicated then lines.map: case (n, line) if !line.endsWith("// duplicated") => (n, s"$line // duplicated") case other => other else lines def term(t: Statement): Lines = t match case Term.Blk(stmts, term) => stmts.iterator.concat(Iterator.single(term)).flatMap: case DefineVar(sym, Term.IfLike(kw, IfLikeForm.ReturningIf, splt)) => s"$sym = ${kw.name}" #: SimpleSplit.prettyPrint.split(splt, true, true) case DefineVar(sym, Term.SynthIf(splt)) => s"$sym = if" #: split(splt, true, true) case stmt => (0, stmt.showDbg) :: Nil .toList case t: Statement => (0, t.showDbg) :: Nil def branch(b: Branch, isTopLevel: Bool): Lines = val Branch(scrutinee, pattern, consequent) = b val lines = split(consequent, true, false) val prefix = s"${scrutinee.sym} is ${pattern.showDbg}" consequent match case Split.Else(_) => (prefix + " then") #: lines case _ => (prefix + " and") #: lines val lines = split(s, true, true) (if prefix.isEmpty then lines else prefix #: lines).toIndentedString end prettyPrint ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala ================================================ package hkmc2 package semantics import scala.collection.mutable import scala.collection.mutable.{Set => MutSet} import mlscript.utils.*, shorthands.* import syntax.* import hkmc2.utils.* import Elaborator.State import Tree.Ident import hkmc2.utils.SymbolSubst abstract class Symbol(using State) extends Located: def nme: Str def getState: State = summon val uid: Uid[Symbol] = State.suid.nextUid def showPlainName(using scp: Scope): hkmc2.document.Document = import hkmc2.document.* scp.allocateOrGetName(this)(using throw _) def showName(using scp: Scope, cfg: ShowCfg)(using Raise): Str = cfg.shownSymbols += this import hkmc2.document.* val name = nme if cfg.showFlowSymbols then s"$name${scp.allocateOrGetName(this).stripPrefix(name)}" else name def prefix: Str = "" def showPrefix(using Scope, ShowCfg, Raise): Str = prefix def showFullName(using Scope, ShowCfg, Raise): Str = showPrefix + showName + State.dbgUid(uid) override def toString: Str = prefix + nme + State.dbgUid(uid) val directRefs: mutable.Buffer[Term.Ref] = mutable.Buffer.empty def ref(id: Tree.Ident = Tree.Ident("") // FIXME hack ): Term.Ref = val res = new Term.Ref(this)(id, directRefs.size, N) directRefs += res res def refsNumber: Int = directRefs.size def existsNonModuleful: Bool = this match case mod: ModuleOrObjectSymbol => !(mod.tree.k is Mod) case mem: BlockMemberSymbol => // Some block member symbols do not correspond to a definition // Tree, e.g., val definitions of a data class. So, it is supposed // that if there is no tree, then it is not moduleful (because // modules do have a tree). mem.trees.isEmpty || mem.trees.exists: case t @ Tree.TypeDef(k = Mod) => false case _ => true case _ => true def existsModuleful: Bool = this match case mod: ModuleOrObjectSymbol => (mod.tree.k is Mod) case mem: BlockMemberSymbol => mem.trees.exists: case t @ Tree.TypeDef(k = Mod) => true case _ => false case _ => false def hasTrmDef: Bool = this match case trm: TermSymbol => true case mem: BlockMemberSymbol => mem.trmTree.nonEmpty case _ => false def asCls: Opt[ClassSymbol] = this match case cls: ClassSymbol => S(cls) case mem: BlockMemberSymbol => mem.clsTree.flatMap(_.symbol.asCls) case _ => N def asModOrObj: Opt[ModuleOrObjectSymbol] = this match case mod: ModuleOrObjectSymbol => S(mod) case mem: BlockMemberSymbol => mem.modOrObjTree.flatMap(_.symbol.asModOrObj) case _ => N def asMod: Opt[ModuleOrObjectSymbol] = asModOrObj.filter(_.tree.k is Mod) def asObj: Opt[ModuleOrObjectSymbol] = asModOrObj.filter(_.tree.k is Obj) def asClsOrMod: Opt[ClassSymbol | ModuleOrObjectSymbol] = asCls orElse asModOrObj // * This is a hack for that `TermDef` currently doesn't have a symbol. // * So, the symbol is from the `TermDefinition`. def asTrm: Opt[TermSymbol] = this match case trm: TermSymbol => S(trm) case mem: BlockMemberSymbol => mem.tsym case _ => N def asPat: Opt[PatternSymbol] = this match case pat: PatternSymbol => S(pat) case mem: BlockMemberSymbol => mem.patTree.flatMap(_.symbol.asPat) case _ => N def asAls: Opt[TypeAliasSymbol] = this match case cls: TypeAliasSymbol => S(cls) case mem: BlockMemberSymbol => mem.alsTree.flatMap(_.symbol.asAls) case _ => N def asClsLike: Opt[ClassSymbol | ModuleOrObjectSymbol | PatternSymbol] = (asCls: Opt[ClassSymbol | ModuleOrObjectSymbol | PatternSymbol]) orElse asModOrObj orElse asPat def asTpe: Opt[TypeSymbol] = asCls .orElse[TypeSymbol](asObj) .orElse[TypeSymbol](asAls) .orElse[TypeSymbol](asMod) def asNonModTpe: Opt[TypeSymbol] = asCls .orElse[TypeSymbol](asObj) .orElse[TypeSymbol](asAls) def asBlkMember: Opt[BlockMemberSymbol] = this match case mem: BlockMemberSymbol => S(mem) case mem: DefinitionSymbol[?] => mem.defn match case S(defn: TypeLikeDef) => S(defn.bsym) case S(defn: TermDefinition) => S(defn.bsym) case N => N case _ => N /** Get the symbol corresponding to the "representative" of a set of overloaded definitions, * or the sole definition, if it is not overloaded. * We should consider the ordering terms > classes/objects/types > modules, for this purpose. */ def asPrincipal: Opt[TermSymbol | ClassSymbol | ModuleOrObjectSymbol | TypeAliasSymbol | PatternSymbol] = val asTrm: Opt[TermSymbol | ClassSymbol | ModuleOrObjectSymbol | TypeAliasSymbol | PatternSymbol] = this.asTrm asTrm orElse asCls orElse asObj orElse asAls orElse asPat orElse asMod def orElseDisamb(disamb: Opt[DefinitionSymbol[?]]): Symbol = (this, disamb) match case (bms: BlockMemberSymbol, S(disamb)) => disamb case (bms: BlockMemberSymbol, N) => lastWords(s"Cannot disambiguate overloaded member symbol ${bms.nme}: no disambiguation provided") case (sym, N) => sym case (sym, S(_)) => lastWords(s"Cannot disambiguate non-BlockMember symbol ${sym.nme}: disambiguation provided") override def equals(x: Any): Bool = this is x override def hashCode: Int = uid.hashCode def subst(using SymbolSubst): Symbol end Symbol // * Used, eg, as the Assign receiver of intermediate computations whose result is not used final class NoSymbol(using State) extends Symbol: def nme: Str = "‹no symbol›" def toLoc: Option[Loc] = N def subst(using s: SymbolSubst): NoSymbol = this class FlowSymbol(label: Str)(using State) extends Symbol: def nme: Str = label def toLoc: Option[Loc] = N // TODO track source trees of flows import flow.* val outFlows: mutable.Buffer[FlowSymbol] = mutable.Buffer.empty val consumers: mutable.Buffer[Consumer] = mutable.Buffer.empty val producers: mutable.Buffer[ConcreteProd] = mutable.Buffer.empty def showDbg: Str = label + s"‹$uid›" def subst(using s: SymbolSubst): FlowSymbol = s.mapFlowSym(this) object FlowSymbol: def app()(using State) = // FlowSymbol("‹app-res›") // FlowSymbol("@") FlowSymbol("app") def sel(nme: Str)(using State) = FlowSymbol(s"⋅$nme") def synthSel(nme: Str)(using State) = FlowSymbol(s"(⋅)$nme") def selProj(nme: Str)(using State) = FlowSymbol(s"#⋅$nme") def lds(nme: Str)(using State) = FlowSymbol(s"Ɛ⋅$nme") end FlowSymbol sealed trait LocalSymbol extends Symbol: def subst(using s: SymbolSubst): LocalSymbol sealed trait NamedSymbol extends Symbol: def name: Str def id: Ident def subst(using s: SymbolSubst): NamedSymbol class LabelSymbol(val trm: Opt[Term], name: Str = "lbl")(using State) extends LocalSymbol: def nme = name def subst(using s: SymbolSubst): LabelSymbol = s.mapLabelSym(this) def toLoc = trm.flatMap(_.toLoc) override def prefix: Str = "label:" abstract class BlockLocalSymbol(name: Str)(using State) extends FlowSymbol(name): self: LocalSymbol => // * using `with LocalSymbol` in the `extends` clause makes Scala think there's a bad override var decl: Opt[Declaration] = N class TempSymbol(val trm: Opt[Term], dbgNme: Str = "tmp")(using State) extends BlockLocalSymbol(dbgNme) with LocalSymbol: // val nameHints: MutSet[Str] = MutSet.empty // * May be useful later? override def toLoc: Option[Loc] = trm.flatMap(_.toLoc) override def prefix: Str = "tmp:" override def subst(using s: SymbolSubst): TempSymbol = s.mapTempSym(this) // * When instantiating forall-qualified TVs, we need to duplicate the information // * for pretty-printing, but each instantiation should be different from each other // * i.e., UID should be different class InstSymbol(val origin: Symbol)(using State) extends LocalSymbol: override def nme: Str = origin.nme override def toLoc: Option[Loc] = origin.toLoc def subst(using sub: SymbolSubst): InstSymbol = sub.mapInstSym(this) class VarSymbol(val id: Ident)(using State) extends BlockLocalSymbol(id.name) with NamedSymbol with LocalSymbol: val name: Str = id.name override def toLoc: Opt[Loc] = id.toLoc // override def toString: Str = s"$name@$uid" override def subst(using s: SymbolSubst): VarSymbol = s.mapVarSym(this) class BuiltinSymbol (val nme: Str, val binary: Bool, val unary: Bool, val nullary: Bool, val functionLike: Bool)(using State) extends Symbol: def toLoc: Option[Loc] = N override def prefix: Str = "builtin:" def subst(using sub: SymbolSubst): BuiltinSymbol = sub.mapBuiltInSym(this) def isPure: Bool = true // * For now, all builtins are pure // * A basic approximation of builtin operator types lazy val signature : semantics.flow.Producer = import typing.Type import typing.Type.* val binaryType : Type = Fun(args = Tup.mk(Top, Top), ret = Top, eff = N) val unaryType : Type = Fun(args = Tup.mk(Top), ret = Top, eff = N) val nullaryType : Type = Top val typ = (binary, unary, nullary) match case (true, true, true) => Union(binaryType, Union(unaryType, nullaryType)) case (true, true, _) => Union(binaryType, unaryType) case (true, _, true) => Union(binaryType, nullaryType) case (_, true, true) => Union(unaryType, nullaryType) case (true, _, _) => binaryType case (_, true, _) => unaryType case (_, _, true) => nullaryType case _ => Bot semantics.flow.Producer.Typ(typ) /** This is the outside-facing symbol associated to a possibly-overloaded * definition living in a block – e.g., a module or class. * `nameIsMeaningful` is `true` when the name comes from the user's source code; * it is false when the name is a default given by the compiler, such as "lambda" when lifting lambdas. */ class BlockMemberSymbol(val nme: Str, val trees: Ls[TypeOrTermDef], val nameIsMeaningful: Bool = true)(using State) extends MemberSymbol: // * This is a hack for that `TermDef` currently doesn't have a symbol. var tsym: Opt[TermSymbol] = N def toLoc: Option[Loc] = Loc(trees) def describe: Str = trees match case td :: Nil => td.describe case _ => trees.iterator.map(_.describe).mkString("overloaded ", ", ", "symbol") def clsTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if t.k is Cls => t def modOrObjTree: Opt[Tree.TypeDef] = modTree orElse objTree def objTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if (t.k is Obj) => t def modTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if (t.k is Mod) => t def alsTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if t.k is Als => t def patTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if t.k is Pat => t def trmTree: Opt[Tree.TermDef] = trees.collectFirst: case t: Tree.TermDef /* if t.k is */ => t def trmImplTree: Opt[Tree.TermDef] = trees.collectFirst: case t: Tree.TermDef if t.rhs.isDefined => t def isParameterizedMethod: Bool = trmTree.exists(_.isParameterizedMethod) override def prefix: Str = "member:" def subst(using sub: SymbolSubst): BlockMemberSymbol = sub.mapBlockMemberSym(this) // * The flow of this symbol, when interpreted as a term (assuming no disambiguation) lazy val flow: FlowSymbol = FlowSymbol(s"flow:$nme")(using getState) end BlockMemberSymbol sealed abstract class MemberSymbol(using State) extends Symbol: def nme: Str def subst(using SymbolSubst): MemberSymbol class TermSymbol(val k: TermDefKind, val owner: Opt[InnerSymbol], val id: Tree.Ident)(using State) extends MemberSymbol with DefinitionSymbol[TermDefinition] with LocalSymbol with NamedSymbol: def nme: Str = id.name def name: Str = nme def toLoc: Option[Loc] = id.toLoc override def prefix: Str = s"term:${owner.map(o => s"${o.nme}/").getOrElse("")}" override def showPrefix(using Scope, ShowCfg, Raise): Str = "term:" + owner.map(_.showName + "/").getOrElse("") def subst(using sub: SymbolSubst): TermSymbol = sub.mapTermSym(this) object TermSymbol: def fromFunBms(b: BlockMemberSymbol, owner: Opt[InnerSymbol])(using State) = TermSymbol(syntax.Fun, owner, Tree.Ident(b.nme)) sealed trait CtorSymbol extends Symbol: def nme: Str def subst(using sub: SymbolSubst): CtorSymbol = sub.mapCtorSym(this) case class Extr(isTop: Bool)(using State) extends CtorSymbol: def nme: Str = if isTop then "Top" else "Bot" def toLoc: Option[Loc] = N override def toString: Str = nme sealed abstract case class LitSymbol(lit: Literal)(using State) extends CtorSymbol: def nme: Str = lit.idStr def toLoc: Option[Loc] = lit.toLoc override def prefix: Str = "lit:" object LitSymbol: val cache: mutable.Map[Literal, LitSymbol] = mutable.Map.empty def apply(lit: Literal)(using State): LitSymbol = cache.getOrElseUpdate(lit, new LitSymbol(lit){}) /** A TypeSymbol that is not an alias. */ type BaseTypeSymbol = ClassSymbol | ModuleOrObjectSymbol type TypeSymbol = BaseTypeSymbol | TypeAliasSymbol /** * ErrorSymbol is a placeholder symbol denoting error (during symbol * resolution in the elaborator / resolver). This helps prevent the * same error from throwing multiple times. */ case class ErrorSymbol(val nme: Str, tree: Tree)(using State) extends MemberSymbol: override def toLoc: Option[Loc] = tree.toLoc override def subst(using sub: SymbolSubst): ErrorSymbol = sub.mapErrorSym(this) override def prefix: Str = "error:" sealed trait ClassLikeSymbol extends IdentifiedSymbol: self: MemberSymbol & DefinitionSymbol[? <: ClassDef | ModuleOrObjectDef] => val tree: Tree.TypeDef def subst(using sub: SymbolSubst): ClassLikeSymbol /** * A symbol for entities with a definition. * * This is different from `MemberSymbol` because `BlockMemberSymbol` extends `MemberSymbol`, and its * definition is ambiguous in the sense that a `BlockMemberSymbol` corresponds to multiple * overloaded definitions. In contrast, a `DefinitionSymbol` corresponds to only one specific * definition. */ sealed trait DefinitionSymbol[Defn <: Definition] extends Symbol: this: MemberSymbol => var defn: Opt[Defn] = N var decl: Opt[Declaration] = N // NOTE: currently only assigned for class params and only used by deforestation; may want to just remove it once deforestation is improved def bms: Opt[BlockMemberSymbol] = defn.map(_.bsym) /** Whether we know it's pure when selected (eg getters are not always pure). */ def isPure: Bool = this match case _: ModuleOrObjectSymbol => true case _ => defn.exists: case d: ClassDef => true case TermDefinition(k = _: syntax.ValLike) => true case TermDefinition(k = syntax.Fun, params = _ :: _) => true // References to functions are only guaranteed to be pure when the functions have parameter lists case _ => false def subst(using sub: SymbolSubst): DefinitionSymbol[Defn] def asMemSym: MemberSymbol = this end DefinitionSymbol /** This is the symbol associated to specific definitions. * One overloaded `BlockMemberSymbol` may correspond to multiple `InnerSymbol`s * A `Ref(_: InnerSymbol)` represents a `this`-like reference to the current object. */ // TODO prevent from appearing in Ref sealed trait InnerSymbol(using State) extends Symbol: // Ideally, InnerSymbol should extend DefinitionSymbol, but that requires us to specify the type // parameter to all occurrences of InnerSymbol. So, we use a self-type annotation instead to // ensure that any implementation of InnerSymbol is also a DefinitionSymbol. self: DefinitionSymbol[? <: ClassLikeDef] => val privatesScope: Scope = Scope.empty(Scope.Cfg.default) // * Scope for private members of this symbol val thisProxy: TempSymbol = TempSymbol(N, s"this$$$nme") def subst(using SymbolSubst): InnerSymbol def asDefnSym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol = this match case d: DefinitionSymbol[? <: ClassLikeDef] => d trait IdentifiedSymbol extends Symbol: val id: Tree.Ident class ClassSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State) extends MemberSymbol with ClassLikeSymbol with CtorSymbol with DefinitionSymbol[ClassDef] with InnerSymbol with NamedSymbol: def name: Str = nme def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of classe here override def prefix: Str = "class:" /** Compute the arity. */ def arity: Int = tree.paramLists.headOption.fold(0)(_.fields.length) override def subst(using sub: SymbolSubst): ClassSymbol = sub.mapClsSym(this) class ModuleOrObjectSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State) extends MemberSymbol with ClassLikeSymbol with CtorSymbol with DefinitionSymbol[ModuleOrObjectDef] with InnerSymbol with NamedSymbol: def name: Str = nme def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of module here override def prefix: Str = if tree.k is Obj then "object:" else "module:" override def subst(using sub: SymbolSubst): ModuleOrObjectSymbol = sub.mapModuleSym(this) class TypeAliasSymbol(val id: Tree.Ident)(using State) extends MemberSymbol with DefinitionSymbol[TypeDef]: def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of type alias here override def prefix: Str = "type:" def subst(using sub: SymbolSubst): TypeAliasSymbol = sub.mapTypeAliasSym(this) class PatternSymbol(val id: Tree.Ident, val params: Opt[Tree.Tup], val body: Tree)(using State) extends MemberSymbol with CtorSymbol with DefinitionSymbol[PatternDef] with InnerSymbol: def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of pattern here override def prefix: Str = "pattern:" override def subst(using sub: SymbolSubst): PatternSymbol = sub.mapPatSym(this) class TopLevelSymbol(blockNme: Str)(using State) extends MemberSymbol with DefinitionSymbol[ModuleOrObjectDef] with InnerSymbol: def nme = blockNme def toLoc: Option[Loc] = N override def prefix: Str = "globalThis:" def subst(using sub: SymbolSubst): TopLevelSymbol = sub.mapTopLevelSym(this) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/SymbolPrinter.scala ================================================ package hkmc2 package semantics import mlscript.utils.*, shorthands.* import syntax.* import hkmc2.utils.* class SymbolPrinter(dbgScp: Scope) extends DebugPrinter: override def printPlain(v: Any): Str = v match case sym: Symbol => sym.showFullName(using dbgScp, semantics.ShowCfg.internal, throw _) case _ => super.printPlain(v) def printSymbol(sym: Symbol)(using Raise, ShowCfg): Str = sym.showName(using dbgScp, summon) end SymbolPrinter ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala ================================================ package hkmc2 package semantics import scala.collection.mutable.{Buffer, Set as MutSet} import mlscript.utils.*, shorthands.* import syntax.* import hkmc2.utils.Scope import hkmc2.utils.Scope.scope import hkmc2.document.* import hkmc2.document.Document.* import Elaborator.State import hkmc2.typing.Type import hkmc2.semantics.Elaborator.{Ctx, ctx} import hkmc2.Message.MessageContext final case class QuantVar(sym: VarSymbol, ub: Opt[Term], lb: Opt[Term]) enum Annot extends AutoLocated: case Untyped case Modifier(mod: Keyword) case Trm(trm: Term) case TailRec case TailCall case Config(modify: hkmc2.Config => hkmc2.Config) def symbol: Opt[Symbol] = this match case Trm(trm) => trm.symbol case _ => N def subTerms: Vector[Term] = this match case Trm(trm) => Vector.single(trm) case _: Modifier | Untyped | TailRec | TailCall | _: Config => Vector.empty def children: Vector[Located] = this match case Trm(trm) => Vector.single(trm) case _: Modifier | Untyped | TailRec | TailCall | _: Config => Vector.empty def show(using Scope, ShowCfg, Raise): Document = this match case Untyped => doc"‹untyped›" case Modifier(mod) => doc"@${mod.name}" case Trm(trm) => doc"@${trm.show}" case Config(_) => doc"@config(...)" def mkClone(using State): Annot = this match case Untyped => Untyped case Modifier(mod) => Modifier(mod) case Trm(trm) => Trm(trm.mkClone) case TailRec => TailRec case TailCall => TailCall case c: Config => c object Annot: val Private = Modifier(Keyword.`private`) end Annot type AnySelTerm = AnySel & Resolvable sealed trait AnySel extends ResolvableImpl: self: Term.Sel | Term.SynthSel | Term.SelProj => val sym: Opt[MemberSymbol] val typ: Opt[Type] val nme: Tree.Ident val resSym: FlowSymbol val prefix: Term val originalCtx: Opt[SrcScope] var resolvedTargets: Ls[flow.SelectionTarget] = Nil // * filled during flow analysis var isErroneous: Bool = false // * to avoid reporting follow-on errors after a flow/resolution error end AnySel object AnySel: def unapply(t: AnySelTerm): S[(Term, Tree.Ident, Opt[Term])] = t match case Term.Sel(lhs, id) => S((lhs, id, N)) case Term.SynthSel(lhs, id) => S((lhs, id, N)) case Term.SelProj(lhs, cls, proj) => S((lhs, proj, S(cls))) end AnySel type Resolvable = Term & ResolvableImpl sealed trait ResolvableImpl: this: Term => import Resolvable.CallableDefinition /** * The expanded form of the term, if it exists. * * - If it is None, the term hasn't yet expanded. * - If it is Some of None, the term has expanded to itself. * - If it is Some of Some, the term has expanded to something else. */ private[semantics] var expansion: Opt[Opt[Term]] = N def duplicate(using State): this.type = this.match case t: Term.Resolved => t.copy()(t.typ) case t: Term.Ref => t.copy()(t.tree, t.refNum, t.typ) case t: Term.App => t.copy()(t.tree, t.typ, t.resSym) case t: Term.TyApp => t.copy()(t.typ) case t: Term.Sel => t.copy()(t.sym, t.resSym, t.typ, t.originalCtx) case t: Term.SynthSel => t.copy()(t.sym, t.resSym, t.typ, t.originalCtx) case t: Term.LeadingDotSel => t.copy()(t.originalCtx) case t: Term.SelProj => t.copy()(t.sym, t.resSym, t.typ, t.originalCtx) case t: Term.New => t.copy()(t.typ) .withLocOf(this) .asInstanceOf def withSym(sym: MemberSymbol): this.type = this.match case t: Term.Sel => t.copy()(S(sym), t.resSym, t.typ, t.originalCtx) case t: Term.SynthSel => t.copy()(S(sym), t.resSym, t.typ, t.originalCtx) case t: Term.SelProj => t.copy()(S(sym), t.resSym, t.typ, t.originalCtx) .withLocOf(this) .asInstanceOf def withTyp(typ: Type)(using DebugPrinter): this.type = this.match case t: Term.Resolved => t.copy()(S(typ)) case t: Term.Ref => t.copy()(t.tree, t.refNum, S(typ)) case t: Term.App => t.copy()(t.tree, S(typ), t.resSym) case t: Term.TyApp => t.copy()(S(typ)) case t: Term.Sel => t.copy()(t.sym, t.resSym, S(typ), t.originalCtx) case t: Term.SynthSel => t.copy()(t.sym, t.resSym, S(typ), t.originalCtx) case _: Term.LeadingDotSel => lastWords(s"Cannot attach a type to leading dot selection: ${this.showDbg}") case t: Term.SelProj => t.copy()(t.sym, t.resSym, S(typ), t.originalCtx) case t: Term.New => t.copy()(S(typ)) .withLocOf(this) .asInstanceOf def expandedIn[T](in: Term => T): T = in(expanded) def expandedResolvableIn[T](in: Resolvable => T)(using DebugPrinter): T = expanded match case r: Resolvable => in(r) case t => lastWords(s"Expected a resolvable term, but got ${t.showDbg}.") /** * Expanding a term to another, which can be later retrieved by the * `instantiate` method. * * If a term is expanding to a term that contains itself, the * `instantiate` method goes into an infinite loop and the expansion * never ends. Thus, the term itself should be duplicated by the * `duplicate` method if it appears in its own expansion. * * This method is only supposed to be called by Resolver. */ private[semantics] def expand(expansion: Opt[Term]): this.type = // `expansion.isDefined`: Ideally, if a term is already expanded, // one should look at the term's expansion and expand it again. This // check is to prevent one from mistakenly overriding an expansion // without looking at the instantiation. // // `expansion.get =/= newExpansion`: Waiting for @Luyu to revamp the // desugaring stage so that no same term occurs in different places. if this.expansion.isDefined && this.expansion.get =/= expansion then lastWords(s"Cannot expand the term ${this} multiple times (to different expansions ${expansion.get}).") this.expansion = S(expansion) this def resolve: this.type = expand(N) def dontResolve: this.type = this // TODO rm /** * A helper function to create a resolved term for this term. */ def resolved(sym: DefinitionSymbol[?]): Term.Resolved = Term.Resolved(this, sym)(typ = resolvedTyp) def hasExpansion = expansion.isDefined def defn: Opt[Definition] = resolvedSym match case S(sym: BlockMemberSymbol) => N case S(sym: DefinitionSymbol[?]) => sym.defn case _ => N def typDefn = resolvedTyp match case S(typ) => typ.symbol match case S(sym: TypeSymbol) => sym.defn case _ => N case N => N def callableDefn: Opt[CallableDefinition] = defn.flatMap: CallableDefinition.fromDefn(_) def singletonDefn: Opt[ModuleOrObjectDef] = typDefn match case S(td: ModuleOrObjectDef) => S(td) case _ => N object Resolvable: case class CallableDefinition( sym: BlockMemberSymbol, params: Ls[ParamList], tparams: Opt[Ls[Param]], sign: Opt[Term], flags: TermDefFlags, modulefulness: Modulefulness, defn: Definition ) object CallableDefinition: def fromDefn(defn: Definition): Opt[CallableDefinition] = defn match case defn: TermDefinition => S(CallableDefinition( defn.sym, defn.params, defn.tparams, defn.sign, defn.flags, defn.modulefulness, defn, )) case defn: TypeDef => S(CallableDefinition( defn.bsym, Nil, if defn.tparams.isEmpty then N else S(defn.tparams.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))), defn.rhs, TermDefFlags.empty, // TODO: handle class-like definitions with flags Modulefulness.none, // TODO: handle modulefulness for class-like definitions defn, )) case defn: ClassLikeDef => S(CallableDefinition( defn.bsym, defn.paramsOpt.toList ::: defn.auxParams, if defn.tparams.isEmpty then N else S(defn.tparams.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))), N, // TODO: handle class-like definitions with signatures TermDefFlags.empty, // TODO: handle class-like definitions with flags Modulefulness.none, // TODO: handle modulefulness for class-like definitions defn, )) trait LeadingDotSelImpl(using State): self: Term.LeadingDotSel => val resSym: FlowSymbol = FlowSymbol.lds(self.nme.name) var resolvedTargets: Ls[flow.SelectionTarget.CompanionMember] = Nil // * filled during flow analysis case class SrcScope(outer: Elaborator.OuterCtx, parent: Opt[SrcScope]): /** Computes the outermost scope from which the current scope can still be accessed. * For instance, from [scp2] here, [scp1] is the outermost accessible base: * [scp0] * fun foo = * [scp1] * module Foo with * module Bar with * [scp2] * and the path is Foo :: Bar :: Nil. * [scp0] cannot access [scp2] because there is a function (Function outer) on the way. * The same would happen for: * [scp0] * if ... then // LocalScope also blocks access to [scp1] and [scp2] * [scp1] * module Foo with * module Bar with * [scp2] */ lazy val outermostAcessibleBase: (SrcScope, Ls[InnerSymbol]) = import Elaborator.OuterCtx.* outer match case InnerScope(inner) => parent match case N => (this, inner :: Nil) case S(par) => val (base, path) = par.outermostAcessibleBase (base, inner :: path) case _: (Function | LocalScope) | LambdaOrHandlerBlock | NonReturnContext => (this, Nil) object SrcScope: given s: Ctx => SrcScope = summon[Ctx].scope enum Term extends Statement: case Error case UnitVal() case Missing // Placeholder terms that were not elaborated due to the "lightweight" elaboration mode `Mode.Light` case Lit(lit: Literal) /** A term that wraps another term, indicating that the symbol of the inner term is resolved. * This is mainly used to disambiguate overloaded definitions. */ case Resolved(t: Term, sym: DefinitionSymbol[?]) (val typ: Opt[Type]) extends Term, ResolvableImpl case Ref(sym: Symbol) (val tree: Tree.Ident, val refNum: Int, val typ: Opt[Type]) extends Term, ResolvableImpl case App(lhs: Term, rhs: Term) (val tree: Tree.App, val typ: Opt[Type], val resSym: FlowSymbol) extends Term, ResolvableImpl case TyApp(lhs: Term, targs: Ls[Term]) (val typ: Opt[Type]) extends Term, ResolvableImpl case Sel(prefix: Term, nme: Tree.Ident) (val sym: Opt[MemberSymbol], val resSym: FlowSymbol, val typ: Opt[Type], val originalCtx: Opt[SrcScope]) extends Term, AnySel case SynthSel(prefix: Term, nme: Tree.Ident) (val sym: Opt[MemberSymbol], val resSym: FlowSymbol, val typ: Opt[Type], val originalCtx: Opt[SrcScope]) extends Term, AnySel case SelProj(prefix: Term, cls: Term, nme: Tree.Ident) (val sym: Opt[MemberSymbol], val resSym: FlowSymbol, val typ: Opt[Type], val originalCtx: Opt[SrcScope]) extends Term, AnySel case DynSel(prefix: Term, fld: Term, arrayIdx: Bool) case Tup(fields: Ls[Elem])(val tree: Tree.Tup) case Mut(underlying: Tup | Rcd | New | DynNew) case CtxTup(fields: Ls[Elem])(val tree: Tree.Tup) case IfLike(kw: Keyword.SplitLike, form: IfLikeForm, split: SimpleSplit) /** `If` expressions synthesized by the pattern compiler. It should only be * created and used in `Lowering`. One must make sure that all terms in the * split are correctly resolved. In the future, we might look for a way to * remove `SynthIf` by generating IR `Match` blocks directly. */ case SynthIf(split: Split) case Lam(params: ParamList, body: Term) case FunTy(lhs: Term, rhs: Term, eff: Opt[Term]) case Forall(tvs: Ls[QuantVar], outer: Opt[VarSymbol], body: Term) case Constrained(constraints: Ls[SubConstraint], body: Term) case WildcardTy(in: Opt[Term], out: Opt[Term]) case Blk(stats: Ls[Statement], res: Term) extends Term, BlkImpl case Rcd(mut: Bool, stats: Ls[Statement]) case Quoted(body: Term) case Unquoted(body: Term) case New(cls: Term, args: Ls[Term], rft: Opt[ClassSymbol -> ObjBody]) (val typ: Opt[Type]) extends Term, ResolvableImpl case DynNew(cls: Term, args: Ls[Term]) case Asc(term: Term, ty: Term) case CompType(lhs: Term, rhs: Term, pol: Bool) case Neg(rhs: Term) case Region(name: VarSymbol, body: Term) case RegRef(reg: Term, value: Term) case Assgn(lhs: Term, rhs: Term) case Drop(trm: Term) case Deref(ref: Term) case SetRef(ref: Term, value: Term) case Ret(result: Term) case Throw(result: Term) case Try(body: Term, finallyDo: Term) case Annotated(annot: Annot, target: Term) case Handle(lhs: LocalSymbol, rhs: Term, args: List[Term], derivedClsSym: ClassSymbol, defs: Ls[HandlerTermDefinition], body: Term) case LeadingDotSel(nme: Tree.Ident)( val originalCtx: Opt[SrcScope] ) (using State) extends Term, ResolvableImpl, LeadingDotSelImpl def expanded: Term = this match case t: Resolvable => t.expansion match case S(S(t)) => t.expanded case S(N) => this case N => this case _ => this /** * This field equals `S(lds)` if the term is a chain of selections * and applications that originates with a leading-dot selection, * namely `lds`. Otherwise this field equals `N`. * It is evaluated during flow analysis to constrain the LDS with * the type of the whole term. */ lazy val ldsRoot: Opt[LeadingDotSel] = this match case Sel(prefix, nme) => prefix.ldsRoot case App(lhs, rhs) => lhs.ldsRoot case sel: LeadingDotSel => S(sel) case _ => N /** * The prelinminary symbol for the term that is resolved during * elaboration. */ lazy val symbol: Opt[Symbol] = this match case res: Resolved => S(res.sym) case Ref(sym) => S(sym) case sel: Sel => sel.sym case sel: SynthSel => sel.sym case sel: SelProj => sel.sym case TyApp(lhs = lhs) => lhs.symbol case _ => N /** * The symbol representing the evaluation result of the term. This * symbol is resolved during the resolution stage. */ def resolvedSym: Opt[Symbol] = expanded match case res: Resolved => S(res.sym) case ref: Ref => ref.symbol case sel: Sel => sel.sym case sel: SynthSel => sel.sym case sel: SelProj => sel.sym case app: TyApp => app.lhs.resolvedSym case _ => N def resolvedTyp: Opt[Type] = expanded match case res: Resolved => res.typ case ref: Ref => ref.typ case app: App => app.typ case app: TyApp => app.typ case sel: Sel => sel.typ case sel: SynthSel => sel.typ case nu: New => nu.typ case _ => N /** The set of free variable names in this term. * Note: it is wrong to compute this based on strings, rather than symbols, * but strings are good enough for now. This is currently only used to detect useless pattern variables. * (These definitions were generated as part of a slop PR.) */ lazy val freeVars: Set[Str] = this match case Ref(sym) => Set.single(sym.nme) case Lam(params, body) => val paramNames = params.allParams.iterator.map(_.sym.nme).toSet body.freeVars -- paramNames case Blk(stats, res) => Term.blkFreeVars(stats, res.freeVars) case IfLike(_, _, split) => split.freeVars case SynthIf(split) => split.freeVars case Region(name, body) => body.freeVars - name.nme case Handle(lhs, rhs, args, _, defs, body) => val rhsFree = rhs.freeVars val argsFree = args.iterator.flatMap(_.freeVars).toSet val defsFree = defs.iterator.flatMap(d => Term.termDefFreeVars(d.td)).toSet val bodyFree = body.freeVars - lhs.nme rhsFree ++ argsFree ++ defsFree ++ bodyFree case Forall(_, _, body) => body.freeVars case Error | Missing | _: Lit | _: UnitVal | _: LeadingDotSel => Set.empty case _ => subTerms.iterator.flatMap(_.freeVars).toSet def sel(id: Tree.Ident, sym: Opt[MemberSymbol])(using State, Elaborator.Ctx): Sel = Sel(this, id)(sym, FlowSymbol.sel(id.name), N, S(summon)) def selNoSym(nme: Str, synth: Bool = false)(using State, Elaborator.Ctx): Sel | SynthSel = val id = new Tree.Ident(nme) if synth then SynthSel(this, id)(N, FlowSymbol.synthSel(nme), N, S(summon)) else sel(id, N) def app(args: Term*)(using State) = App(this, Tup(args.toList.map(PlainFld(_)))(Tree.DummyTup)) (Tree.App(Tree.Dummy, Tree.Dummy), N, FlowSymbol("")) override def mkClone(using State): Term = val that = this match case Error => Error case UnitVal() => UnitVal() case Missing => Missing case Lit(Tree.StrLit(value)) => Lit(Tree.StrLit(value)) case Lit(Tree.IntLit(value)) => Lit(Tree.IntLit(value)) case Lit(Tree.DecLit(value)) => Lit(Tree.DecLit(value)) case Lit(Tree.BoolLit(value)) => Lit(Tree.BoolLit(value)) case Lit(Tree.UnitLit(value)) => Lit(Tree.UnitLit(value)) case term @ Resolved(t, sym) => Resolved(t.mkClone, sym)(term.typ) case term @ Ref(sym) => Ref(sym)(Tree.Ident(term.tree.name), term.refNum, term.typ) case term @ App(lhs, rhs) => App(lhs.mkClone, rhs.mkClone)(term.tree, term.typ, term.resSym) case term @ TyApp(lhs, targs) => TyApp(lhs.mkClone, targs.map(_.mkClone))(term.typ) case term @ Sel(prefix, nme) => Sel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx) case term @ SynthSel(prefix, nme) => SynthSel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx) case DynSel(prefix, fld, arrayIdx) => DynSel(prefix.mkClone, fld.mkClone, arrayIdx) case term @ Tup(fields) => Tup(fields.map { case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) case s: Spd => s.copy(term = s.term.mkClone) })(term.tree) case Mut(underlying) => Mut(underlying.mkClone.asInstanceOf[Tup | Rcd | New | DynNew]) case term @ CtxTup(fields) => CtxTup(fields.map { case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) case s: Spd => s.copy(term = s.term.mkClone) })(term.tree) case IfLike(kw, form, split) => IfLike(kw, form, split.mkClone) case SynthIf(split) => SynthIf(split.mkClone) case Lam(params, body) => Lam(params, body.mkClone) case FunTy(lhs, rhs, eff) => FunTy(lhs.mkClone, rhs.mkClone, eff.map(_.mkClone)) case Forall(tvs, outer, body) => Forall(tvs, outer, body.mkClone) case Constrained(constraints, body) => Constrained(constraints, body.mkClone) case WildcardTy(in, out) => WildcardTy(in.map(_.mkClone), out.map(_.mkClone)) case blk: Blk => blk.mkBlkClone case Rcd(mut, stats) => Rcd(mut, stats.map(_.mkClone)) case Quoted(body) => Quoted(body.mkClone) case Unquoted(body) => Unquoted(body.mkClone) case term @ New(cls, args, rft) => New(cls.mkClone, args.map(_.mkClone), rft.map { case (cs, ob) => cs -> ObjBody(ob.blk.mkBlkClone) })(term.typ) case DynNew(cls, args) => DynNew(cls.mkClone, args.map(_.mkClone)) case term @ SelProj(prefix, cls, proj) => SelProj(prefix.mkClone, cls.mkClone, Tree.Ident(proj.name))(term.sym, term.resSym, term.typ, term.originalCtx) case Asc(term, ty) => Asc(term.mkClone, ty.mkClone) case CompType(lhs, rhs, pol) => CompType(lhs.mkClone, rhs.mkClone, pol) case Neg(rhs) => Neg(rhs.mkClone) case Region(name, body) => Region(name, body.mkClone) case RegRef(reg, value) => RegRef(reg.mkClone, value.mkClone) case Assgn(lhs, rhs) => Assgn(lhs.mkClone, rhs.mkClone) case Drop(trm) => Drop(trm.mkClone) case Deref(ref) => Deref(ref.mkClone) case SetRef(ref, value) => SetRef(ref.mkClone, value.mkClone) case Ret(result) => Ret(result.mkClone) case Throw(result) => Throw(result.mkClone) case Try(body, finallyDo) => Try(body.mkClone, finallyDo.mkClone) case Annotated(annot, target) => Annotated(annot, target.mkClone) case Handle(lhs, rhs, args, derivedClsSym, defs, body) => Handle(lhs, rhs.mkClone, args.map(_.mkClone), derivedClsSym, defs, body.mkClone) (this, that) match case (self: Resolvable, that: Resolvable) if self.expansion.isDefined => that.expand(self.expansion.get.map(_.mkClone)) case _ => that end Term object Term: /** Compute the free variable names of a block given its statements and the * free vars of its result term. Processes statements right-to-left so that * bindings introduced by `LetDecl`, `TermDefinition`, class definitions, * etc. correctly shadow uses in subsequent statements. */ private[semantics] def blkFreeVars(stats: Ls[Statement], resFree: Set[Str]): Set[Str] = val blockLevelBinders = Buffer.empty[Str] stats.foreach: case td: TermDefinition => blockLevelBinders += td.sym.nme case cls: ClassLikeDef => blockLevelBinders += cls.sym.nme case td: TypeDef => blockLevelBinders += td.bsym.nme case _: (Import | SetConfig | RcdField | RcdSpread | Term | LetDecl | DefineVar) => () (stats.foldRight(resFree): (stat, acc) => stat match case LetDecl(sym, annotations) => (acc - sym.nme) ++ annotations.iterator.flatMap(_.subTerms).flatMap(_.freeVars) case DefineVar(sym, rhs) => acc + sym.nme ++ rhs.freeVars case t: Term => acc ++ t.freeVars case _: (Import | SetConfig | RcdField | RcdSpread | TermDefinition | ClassLikeDef | TypeDef) => acc ++ stat.subTerms.iterator.flatMap(_.freeVars) ) -- blockLevelBinders /** Free vars of a TermDefinition body, with its own params subtracted. */ private[semantics] def termDefFreeVars(td: TermDefinition): Set[Str] = val paramNames = td.params.iterator.flatMap(_.allParams).map(_.sym.nme).toSet val bodyFree = td.body.iterator.flatMap(_.freeVars).toSet val signFree = td.sign.iterator.flatMap(_.freeVars).toSet (bodyFree ++ signFree) -- paramNames /** Free vars of a ClassLikeDef body, with constructor params subtracted. */ private def classLikeDefFreeVars(cls: ClassLikeDef): Set[Str] = val paramNames = cls.paramsOpt.iterator.flatMap(_.allParams).map(_.sym.nme).toSet val bodyFree = cls.body.blk.freeVars val extFree = cls.ext.iterator.flatMap(_.freeVars).toSet (bodyFree ++ extFree) -- paramNames end Term import Term.* extension (self: Blk) def mapRes(f: Term => Term) = Blk(self.stats, f(self.res)) case class ShowCfg( showExpansionMappings: Bool, showFlowSymbols: Bool, debug: Bool, ): // * Rather ugly way of collecting shown symbols during show operations val shownSymbols: MutSet[Symbol] = MutSet.empty end ShowCfg object ShowCfg: // * For use when displaying things for internal use (not for end users) val internal = ShowCfg( showFlowSymbols = true, showExpansionMappings = false, debug = false, ) end ShowCfg sealed trait Statement extends AutoLocated, ProductWithExtraInfo: def mkClone(using State): Statement = this match case t: Term => lastWords(s"overridden implementation") case d: Definition => ??? case imp: Import => Import(imp.sym, imp.str, imp.file) case LetDecl(sym, annotations) => LetDecl(sym, annotations.map(_.mkClone)) case RcdField(field, rhs) => RcdField(field.mkClone, rhs.mkClone) case RcdSpread(rcd) => RcdSpread(rcd.mkClone) case DefineVar(sym, rhs) => DefineVar(sym, rhs.mkClone) case sc: SetConfig => sc def describe: Str = val desc = this match case Error => "‹error›" case UnitVal() => "unit value" case Lit(lit) => lit.describeLit case Ref(sym) => "reference" case App(lhs, rhs) => "application" case TyApp(lhs, targs) => "type application" case Sel(pre, nme) => "selection" case SynthSel(pre, nme) => "selection" case DynSel(o, f, _) => "dynamic selection" case Tup(fields) => "tuple literal" case CtxTup(fields) => "contextual tuple literal" case IfLike(_, IfLikeForm.ReturningIf, body) => "`if` expression" case IfLike(_, IfLikeForm.ImperativeIf, body) => "`if` statement" case IfLike(_, IfLikeForm.While, body) => "`while` statement" case SynthIf(split) => "synthetic `if` expression" case Lam(params, body) => "function literal" case FunTy(lhs, rhs, eff) => "function type" case Forall(tvs, outer, body) => "universal quantification" case Constrained(constraints, body) => "constrained type" case WildcardTy(in, out) => "wildcard type" case Blk(stats, res) => "block" case Quoted(term) => "quoted term" case Unquoted(term) => "unquoted term" case New(cls, args, rft) => "object creation" case SelProj(pre, cls, proj) => "field selection" case Asc(term, ty) => "type ascription" case CompType(lhs, rhs, pol) => "composed type" case Neg(rhs) => "negation type" case Region(name, body) => "region expression" case RegRef(reg, value) => "reference creation" case Assgn(lhs, rhs) => "assignment" case SetRef(ref, value) => "mutable reference assignment" case Drop(ref) => "drop" case Deref(ref) => "dereference" case Throw(e) => "throw" case Annotated(annotation, target) => "annotation" case Ret(res) => "return" case Try(body, finallyDo) => "try expression" case Missing => "missing" case LeadingDotSel(name) => "leading dot selection" case Resolved(t, sym) => t.describe case s => TODO(s) this match case self: Resolvable => self.resolvedTyp match case S(typ) => s"${desc} of type ${typ.show}" case N => desc case _ => desc def extraInfo(using DebugPrinter): Str = this match case r: Resolvable if r.resolvedSym.isDefined || r.resolvedTyp.isDefined => ( r.resolvedSym.map(s => s"sym=${s.showAsPlain}") :: r.resolvedTyp.map(s => s"typ=${s.showDbg}") :: Nil ).flatten.mkString(",") case r: SelProj => r.symbol.mkString case _ => "" def subStatements: Vector[Statement] = this match case Blk(stats, res) => stats.toVector :+ res case _ => subTerms def subTerms: Vector[Term] = this match case Error | Missing | _: Lit | _: Ref | _: UnitVal => Vector.empty case Resolved(t, sym) => Vector.single(t) case App(lhs, rhs) => Vector.double(lhs, rhs) case RcdField(lhs, rhs) => Vector.double(lhs, rhs) case RcdSpread(bod) => Vector.single(bod) case FunTy(lhs, rhs, eff) => Vector.double(lhs, rhs) ++ eff.toVector case TyApp(pre, tarsg) => pre +: tarsg.toVector case Sel(pre, _) => Vector.single(pre) case SynthSel(pre, _) => Vector.single(pre) case DynSel(o, f, _) => Vector.double(o, f) case Tup(fields) => fields.flatMap(_.subTerms).toVector case Mut(und) => Vector.single(und) case CtxTup(fields) => fields.flatMap(_.subTerms).toVector case IfLike(_, _, split) => split.subTerms case SynthIf(split) => split.subTerms case Lam(params, body) => params.allParams.iterator.flatMap(_.sign).toVector :+ body case Blk(stats, res) => stats.flatMap(_.subTerms).toVector :+ res case Rcd(mut, stats) => stats.flatMap(_.subTerms).toVector case Quoted(term) => Vector.single(term) case Unquoted(term) => Vector.single(term) case New(cls, args, rft) => (cls +: args.toVector) ++ rft.toVector.flatMap(_._2.blk.subTerms) case DynNew(cls, args) => cls +: args.toVector case SelProj(pre, cls, _) => Vector.double(pre, cls) case Asc(term, ty) => Vector.double(term, ty) case Ret(res) => Vector.single(res) case Throw(res) => Vector.single(res) case Forall(_, _, body) => Vector.single(body) case Constrained(constraints, body) => constraints.flatMap(_.subTerms).toVector :+ body case WildcardTy(in, out) => in.toVector ++ out.toVector case CompType(lhs, rhs, _) => Vector.double(lhs, rhs) case LetDecl(sym, annotations) => annotations.flatMap(_.subTerms).toVector case DefineVar(sym, rhs) => Vector.single(rhs) case Region(_, body) => Vector.single(body) case RegRef(reg, value) => Vector.double(reg, value) case Assgn(lhs, rhs) => Vector.double(lhs, rhs) case SetRef(lhs, rhs) => Vector.double(lhs, rhs) case Drop(term) => Vector.single(term) case Deref(term) => Vector.single(term) case TermDefinition(_, _, _, pss, tps, sign, body, _, _, annotations, _) => pss.toVector.flatMap(_.subTerms) ++ tps.getOrElse(Nil).flatMap(_.subTerms).toVector ++ sign.toVector ++ body.toVector ++ annotations.flatMap(_.subTerms).toVector case cls: ClassDef => (cls.paramsOpt.toVector.flatMap(_.subTerms) :+ cls.body.blk) ++ cls.annotations.flatMap(_.subTerms).toVector case mod: ModuleOrObjectDef => ( mod.paramsOpt.toVector.flatMap(_.subTerms) :+ mod.body.blk) ++ mod.annotations.flatMap(_.subTerms).toVector case td: TypeDef => td.rhs.toVector ++ td.annotations.flatMap(_.subTerms).toVector case pat: PatternDef => (pat.paramsOpt.toVector.flatMap(_.subTerms) :+ pat.body.blk) ++ pat.annotations.flatMap(_.subTerms).toVector case Import(sym, str, pth) => Vector.empty case Try(body, finallyDo) => Vector.single(body) ++ Vector.single(finallyDo) case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => (rhs +: args.toVector) ++ defs.flatMap(_.td.subTerms).toVector :+ bod case Neg(e) => Vector.single(e) case Annotated(ann, target) => ann.subTerms ++ Vector.single(target) case LeadingDotSel(nme) => Vector.empty case SetConfig(_) => Vector.empty // private def treeOrSubterms(t: Tree, t: Term): Ls[Located] = t match private def treeOrSubterms(t: Tree): Vector[Located] = t match case Tree.DummyApp | Tree.DummyTup => subTerms case _ => Vector.single(t) protected def children: Vector[Located] = this match case t: Lit => Vector.single(t.lit.asTree) case t: Ref => treeOrSubterms(t.tree) case t: Tup => treeOrSubterms(t.tree) case l: Lam => Vector.double(l.params, l.body) case t: App => treeOrSubterms(t.tree) case IfLike(_, _, split) => Vector.single(split) case SynthIf(split) => Vector.single(split) case SynthSel(pre, nme) => Vector.double(pre, nme) case Sel(pre, nme) => Vector.double(pre, nme) case SelProj(prefix, cls, proj) => Vector.triple(prefix, cls, proj) case _ => subTerms // TODO more precise (include located things that aren't terms) def show(using Scope, ShowCfg, Raise): Document = def res: Document = this match case lit: Lit => lit.lit.idStr case r: Ref => r.sym match case _: BuiltinSymbol => r.sym.nme case _ => r.sym.showName case sel: Sel => if summon[ShowCfg].showFlowSymbols then doc"${sel.prefix.show}.${sel.sym.fold(doc"${sel.nme.name}‹?›")(_.showName)}" else doc"${sel.prefix.show}.${sel.nme.name}" case sel: SynthSel => if summon[ShowCfg].showFlowSymbols then doc"⟨${sel.prefix.show}.⟩${sel.sym.fold(doc"${sel.nme.name}‹?›")(_.showName)}" else doc"${sel.prefix.show}.${sel.nme.name}" case Resolved(trm, sym) => trm.show case app: App => doc"${app.lhs.show}${app.rhs.showAsParams}${ if summon[ShowCfg].showFlowSymbols then summon[ShowCfg].shownSymbols.add(app.resSym) "‹" :: app.resSym.showPlainName :: "›" else "" }" case lam: Lam => doc"${lam.params.show} => ${lam.body.show}" case nw: New => doc"new ${nw.cls.show}${nw.args.map(_.showAsParams).mkDocument()}${ nw.rft.fold(doc"")(doc" with " :: _._2.blk.show)}" case tup: Tup => bracketed("[", "]", insertBreak = true): tup.fields.map(_.show).mkDocument(doc", # ") case blk: Blk => braced: doc" # " :: (blk.stats ::: blk.res.match case Lit(Tree.UnitLit(false)) => Nil case res => res :: Nil ).map(_.show).mkDocument(doc", # ") case ld: LetDecl => (ld.annotations.map(_.show) ::: doc"let ${ld.sym.showName}" :: Nil).mkDocument() case df: DefineVar => doc"${df.sym.showName} = ${df.rhs.show}" case td: TermDefinition => td.annotations.map(_.show).mkDocument() :: doc"${td.k.str} ${td.sym.showName}" :: (if td.tparams.isEmpty then doc"" else doc"[${td.tparams.get.map(_.sym.showName).mkDocument(", ")}]") :: td.params.map(_.show).mkDocument() :: td.sign.fold(doc"")(s => doc": ${s.show}") :: (if summon[ShowCfg].showFlowSymbols then doc" ‹${td.bsym.flow.showName}›" else doc"") :: td.body.fold(doc"")(b => doc" = ${b.show}") case cld: ClassLikeDef => cld.annotations.map(_.show).mkDocument() :: doc"${cld.kind.str} ${cld.sym.nme}" :: (if cld.tparams.isEmpty then doc"" else doc"[${cld.tparams.map(_.sym.showName).mkDocument(", ")}]") :: cld.paramsOpt.map(_.show).toList.mkDocument() :: cld.auxParams.map(_.show).mkDocument() :: doc" ${cld.body.blk.show}" case imp: Import => doc"import ${"\""}.../${imp.file.last}${"\""} as ${imp.sym.showName}" case LeadingDotSel(name) => doc"_?_.${name.name}" case Error => doc"‹error›" case _ => doc"TODO[show:${getClass.getSimpleName}](${toString})" this match case t: Resolvable => t.expansion match case S(S(exp)) => val rhs = exp.show(using summon, summon[ShowCfg].copy(showExpansionMappings = false)) if summon[ShowCfg].showExpansionMappings then if exp === t then rhs // ^ Some expansions only modify meta-data, such as types and symbols; // we don't print them for conciseness else res :: doc"{ ~> " :: rhs :: doc" }" else exp.show case _ => res case _ => res def size: Int = this match case Lit(Tree.StrLit(str)) => str.size / 4 + 1 case _ => subTerms.iterator.map(_.size).sum + 1 def showDbg(using DebugPrinter): Str = this match case r: Ref => r.sym.showAsPlain case r: Resolved => s"${r.showPlain}‹${r.sym}›" case trm: Term => // s"$showPlain‹${trm.symbol.getOrElse("")}›" s"$showPlain${trm.symbol.fold("")("‹"+_+"›")}" case _ => showPlain def showAsParams(using Scope, ShowCfg, Raise): Document = this match case tup: Tup => doc"(${tup.fields.map(_.show).mkDocument(", ")})" case _ => doc"(...$show)" def showDbgAsParams(using DebugPrinter): Str = this match case tup: Tup => s"(${tup.fields.map(_.showDbg).mkString(", ")})" case _ => s"(...$showDbg)" def showPlain(using DebugPrinter): Str = this match case Term.UnitVal() => "()" case Lit(lit) => lit.idStr case Resolved(t, sym) => t.showPlain case r @ Ref(symbol) => symbol.toString + symbol.getState.dbgRefNum(r.refNum) case App(lhs, rhs) => s"${lhs.showDbg}${rhs.showDbgAsParams}" case RcdField(lhs, rhs) => s"${lhs.showDbg}: ${rhs.showDbg}" case RcdSpread(bod) => s"...${bod.showDbg}" case FunTy(lhs: Tup, rhs, eff) => s"${lhs.fields.map(_.showDbg).mkString(", ")} ->${ eff.map(e => s"{${e.showDbg}}").getOrElse("")} ${rhs.showDbg}" case FunTy(lhs, rhs, eff) => s"(...${lhs.showDbg}) ->${eff.map(e => s"{${e.showDbg}}").getOrElse("")} ${rhs.showDbg}" case TyApp(lhs, targs) => s"${lhs.showDbg}[${targs.mkString(", ")}]" case Forall(tvs, outer, body) => s"forall ${tvs.mkString(", ")}${outer.map(v => s", outer $v").mkString}: ${body.toString}" case Constrained(constraints, body) => s"[${constraints.map(_.showDbg).mkString(", ")}] => ${body.showDbg}" case WildcardTy(in, out) => s"in ${in.map(_.toString).getOrElse("⊥")} out ${out.map(_.toString).getOrElse("⊤")}" case Sel(pre, nme) => s"${pre.showDbg}.${nme.name}" case SynthSel(pre, nme) => s"(${pre.showDbg}.)${nme.name}" case DynSel(pre, fld, _) => s"${pre.showDbg}[${fld.showDbg}]" case IfLike(kw, _, split) => s"${kw.name} { ${split.showDbg} }" case SynthIf(split) => s"if { ${split.showDbg} }" case Lam(params, body) => s"λ${params.showDbg}. ${body.showDbg}" case Blk(stats, res) => (stats.map(_.showDbg + "; ") :+ (res match { case Lit(Tree.UnitLit(false)) => "" case x => x.showDbg + " " })) .mkString("{ ", "", "}") case Rcd(mut, stats) => (if mut then "mut " else "") + stats.map(_.showDbg + "; ").mkString("{ ", "", "}") case Quoted(term) => s"""code"${term.showDbg}"""" case Unquoted(term) => s"$${${term.showDbg}}" case New(cls, args, rft) => s"new ${cls.showDbg}${args.map(_.showDbgAsParams).mkString}${rft.fold("")(r => s"{ ${r._2.blk.showDbg} }")}" case DynNew(cls, args) => s"new! ${cls.showDbg}${args.map(_.showDbgAsParams).mkString}" case SelProj(pre, cls, proj) => s"${pre.showDbg}.${cls.showDbg}#${proj.name}" case Asc(term, ty) => s"${term.toString}: ${ty.toString}" case LetDecl(sym, _) => s"let ${sym}" case DefineVar(sym, rhs) => s"${sym} = ${rhs.showDbg}" case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => s"handle ${lhs} = ${rhs}(${args.mkString(", ")}) ${defs} in ${bod}" case Region(name, body) => s"region ${name.nme} in ${body.showDbg}" case RegRef(reg, value) => s"(${reg.showDbg}).ref ${value.showDbg}" case Assgn(lhs, rhs) => s"${lhs.showDbg} := ${rhs.showDbg}" case SetRef(lhs, rhs) => s"${lhs.showDbg} := ${rhs.showDbg}" case Drop(term) => s"drop $term" case Deref(term) => s"!$term" case Neg(ty) => s"~${ty.showDbg}" case CompType(lhs, rhs, pol) => s"${lhs.showDbg} ${if pol then "|" else "&"} ${rhs.showDbg}" case Error => "" case Tup(fields) => fields.map(_.showDbg).mkString("[", ", ", "]") case Mut(und) => s"mut ${und.showDbg}" case CtxTup(fields) => fields.map(_.showDbg).mkString("‹using›[", ", ", "]") case TermDefinition(k, sym, tsym, pss, tps, sign, body, flags, _, _, _) => s"${flags.showDbg}${k.str} ${sym}${ tps.map(_.map(_.showDbg)).mkStringOr(", ", "[", "]") }${ pss.map(_.showDbg).mkString("") }${sign.fold("")(": "+_.showDbg)}${ body match case S(x) => " = " + x.showDbg case N => "" }" case cls: ClassLikeDef => s"${cls.kind} ${cls.sym.nme}${ cls.tparams.map(_.showDbg).mkStringOr(", ", "[", "]")}${ cls.paramsOpt.fold("")(_.toString)} ${cls.body}" case Import(sym, str, file) => s"import $str from ${file}" case Annotated(ann, target) => s"@${ann} ${target.showDbg}" case Throw(res) => s"throw ${res.showDbg}" case Try(body, finallyDo) => s"try ${body.showDbg} finally ${finallyDo.showDbg}" case Ret(res) => s"return ${res.showDbg}" case TypeDef(sym, _, tparams, rhs, _, _) => s"type ${sym}${tparams.mkStringOr(", ", "[", "]")} = ${rhs.fold("")(x => x.showDbg)}" case Missing => "missing" case LeadingDotSel(nme) => s"_?_.${nme.name}" final case class LetDecl(sym: LocalSymbol, annotations: Ls[Annot]) extends Statement final case class RcdField(field: Term, rhs: Term) extends Statement final case class RcdSpread(rcd: Term) extends Statement final case class DefineVar(sym: LocalSymbol, rhs: Term) extends Statement /** A global configuration change directive (`#config(...)`). * Records a function that modifies the current compiler configuration. */ final case class SetConfig(modify: hkmc2.Config => hkmc2.Config) extends Statement enum Visibility: case Public, Private /** * isMethod: if the term is a method (as opposed to a function) */ final case class TermDefFlags(isMethod: Bool): def showDbg: Str = val flags = Buffer.empty[String] if isMethod then flags += "method " flags.mkString override def toString: String = "‹" + showDbg + "›" object TermDefFlags { val empty: TermDefFlags = TermDefFlags(false) } /** * A case class representing the modulefulness of a declaration. * * @param msym if None, the declaration is non-moduful; if Some of * symbol, the declaration is moduleful and the symbol is the symbol of * the module's type */ final case class Modulefulness(msym: Opt[ModuleOrObjectSymbol])(val modified: Bool): def isModuleful: Bool = msym.isDefined /** * Return Some of the module definition if it is moduleful; None * otherwise. */ def mdef: Opt[ModuleOrObjectDef] = msym match case S(msym) => msym.defn match case S(defn: ModuleOrObjectDef) => S(defn) case N => lastWords(s"no definition for module symbol $msym") case N => N object Modulefulness: def ofSign(sign: Option[Term])(modified: Bool) = sign.flatMap(_.symbol) match case S(sym: BlockMemberSymbol) if modified => sym.asMod match case S(sym) => Modulefulness(S(sym))(modified) case N => Modulefulness(N)(modified) case _ => Modulefulness(N)(modified) val none = Modulefulness(N)(false) final case class TermDefinition( k: TermDefKind, // * The only reason we store it here in addition to tsym.k is for refining patmats sym: BlockMemberSymbol, tsym: TermSymbol, params: Ls[ParamList], tparams: Opt[Ls[Param]], sign: Opt[Term], body: Opt[Term], flags: TermDefFlags, modulefulness: Modulefulness, annotations: Ls[Annot], companion: Opt[CompanionSymbol], ) extends CompanionValue: require(k is tsym.k) def bsym: BlockMemberSymbol = sym val owner = tsym.owner def visibility: Visibility = annotations.collectFirst: case Annot.Modifier(Keyword.`private`) => Visibility.Private case Annot.Modifier(Keyword.`public`) => Visibility.Public .getOrElse(Visibility.Public) def extraAnnotations: Ls[Annot] = annotations.filter: case Annot.Modifier(Keyword.`declare` | Keyword.`abstract`) => false case _ => true def companionClass: Opt[ClassSymbol] = companion match case S(sym: ClassSymbol) if sym.defn.isDefined => S(sym) case _ => N final case class HandlerTermDefinition( resumeSym: VarSymbol, td: TermDefinition ) object ObjBody: def extractMembers(blk: Term.Blk): Ls[ErrorReport] \/ Map[Str, BlockMemberSymbol] = val (errs, mems) = blk.stats.collect: case td: TermDefinition => td.sym -> td case td: ClassLikeDef => td.bsym -> td case td: TypeDef => td.bsym -> td .groupBy(_._1.nme) .partitionMap: (nme, syms) => if syms.map(_._1).distinct.tail.nonEmpty then L: (msg"Duplicate definition of member named '${nme}'." -> N) :: syms.map(_._2).map(msg"Defined at: " -> _.toLoc) else R: nme -> syms.head._1 if errs.nonEmpty then L(errs.map(ErrorReport(_)).toList) else R(mems.toMap) case class ObjBody(blk: Term.Blk): lazy val members: Map[Str, BlockMemberSymbol] = ObjBody.extractMembers(blk) match case L(errs) => lastWords: errs.map(_.mainMsg).mkString("\n") case R(mems) => mems lazy val (methods, nonMethods) = blk.stats.partitionMap: case td: TermDefinition if td.k is syntax.Fun => L(td) case s => R(s) lazy val publicFlds: Ls[TermDefinition] = nonMethods.collect: case td: TermDefinition if td.k.isInstanceOf[syntax.Val] => td // override def toString: String = statmts.mkString("{ ", "; ", " }") // override def toString: String = blk.showDbg end ObjBody /** `sym` is a `MemberSymbol` when the import is made by the user and can be referred to by name, * in which case it is a `BlockMemberSymbol` when importing files explicitly * and a `TermSymbol` when the import is made implicitly by the compiler (eg, importing "Predef"). * Note that the `file` Path may not represent a real file; eg when importing "fs". */ case class Import(sym: TempSymbol | MemberSymbol, str: Str, file: io.Path) extends Statement sealed abstract class Declaration: val sym: Symbol /** Whether this declares a class, a pattern, an object, or a pattern * parameter. Only these can be in patterns constructor position. */ def isPatternConstructor: Bool = this match case _: (TermDefinition | TypeDef | TyParam) => false case d: ModuleOrObjectDef => d.kind isnt Mod case _: (PatternDef | ClassDef) => true case p: Param => p.flags.pat sealed abstract class Definition extends Declaration, Statement: val annotations: Ls[Annot] def bsym: BlockMemberSymbol def hasDeclareModifier: Opt[Annot.Modifier] = annotations.collectFirst: case mod @ Annot.Modifier(Keyword.`declare`) => mod def hasStagedModifier: Opt[Annot.Modifier] = annotations.collectFirst: case mod @ Annot.Modifier(Keyword.`staged`) => mod sealed trait CompanionValue extends Definition type CompanionSymbol = ModuleOrObjectSymbol | TypeAliasSymbol | ClassSymbol type ClassCompanionSymbol = ModuleOrObjectSymbol | TypeAliasSymbol type ModuleCompanionSymbol = TypeAliasSymbol | ClassSymbol sealed abstract class TypeLikeDef extends Definition: val bsym: BlockMemberSymbol val tparams: Ls[TyParam] val annotations: Ls[Annot] sealed abstract class ClassLikeDef extends TypeLikeDef: val owner: Opt[InnerSymbol] val kind: ClsLikeKind val sym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol val bsym: BlockMemberSymbol val ctorSym: Opt[TermSymbol] val tparams: Ls[TyParam] val paramsOpt: Opt[ParamList] val auxParams: Ls[ParamList] val ext: Opt[New] val body: ObjBody val companion: Opt[CompanionSymbol] val annotations: Ls[Annot] def classCompanion = companion match case S(sym: ClassSymbol) => S(sym) case _ => N def moduleCompanion = companion match case S(sym: ModuleOrObjectSymbol) => S(sym) case _ => N def extraAnnotations(using Ctx): Ls[Annot] = annotations.filter: case Annot.Modifier(Keyword.`declare` | Keyword.`abstract` | Keyword.`data`) => false case Annot.Trm(trm: SynthSel) if (kind is Cls) && (trm.sym.contains(ctx.builtins.annotations.bufferable) || trm.sym.contains(ctx.builtins.annotations.buffered)) => false case _ => true case class ModuleOrObjectDef( owner: Opt[InnerSymbol], sym: ModuleOrObjectSymbol, bsym: BlockMemberSymbol, tparams: Ls[TyParam], paramsOpt: Opt[ParamList], auxParams: Ls[ParamList], ext: Opt[New], kind: ClsLikeKind, body: ObjBody, companion: Opt[ModuleCompanionSymbol], annotations: Ls[Annot], )( val path: SrcScope ) extends ClassLikeDef, CompanionValue: val ctorSym: Option[TermSymbol] = N case class PatternDef( owner: Opt[InnerSymbol], sym: PatternSymbol, bsym: BlockMemberSymbol, tparams: Ls[TyParam], /** All parameters. */ parameters: Ls[Param], /** The pattern parameters, for example, `T` in * `pattern Nullable(pattern T) = null | T`. */ patternParams: Ls[Param], /** The extraction parameters, for example, `x` in * `pattern PairLike(x, y) = [x, y] | Pair(x, y)`. */ extractionParams: Ls[Param], /** The elaborated pattern on the right-hand side, for example, * `[x, y] | Pair(x, y)` in `pattern PairLike(x, y) = [x, y] | Pair(x, y)`. */ pattern: Pattern, annotations: Ls[Annot], ) extends ClassLikeDef: self => val kind: ClsLikeKind = Pat val ext: Opt[New] = N /** Each pattern definition should contain two methods: `unapply` and * `unapplyStringPrefix`, which are generated in `Lowering`. Hence, there * is no need to make `body` a parameter. */ val body: ObjBody = ObjBody(Blk(Nil, Term.Lit(syntax.Tree.UnitLit(false)))) /** Pattern definitions do not need parameter lists. */ val paramsOpt: Opt[ParamList] = N val auxParams: Ls[ParamList] = Nil val companion: Opt[CompanionSymbol] = N // TODO support val ctorSym: Option[TermSymbol] = N sealed abstract class ClassDef extends ClassLikeDef: val kind: ClsLikeKind val sym: ClassSymbol val bsym: BlockMemberSymbol val ctorSym: Opt[TermSymbol] val tparams: Ls[TyParam] val paramsOpt: Opt[ParamList] val auxParams: Ls[ParamList] val body: ObjBody val companion: Opt[ClassCompanionSymbol] val annotations: Ls[Annot] def isData: Opt[Annot.Modifier] = annotations.collectFirst: case mod @ Annot.Modifier(Keyword.`data`) => mod override def extraAnnotations(using Ctx): Ls[Annot] = super.extraAnnotations.filter: case Annot.Modifier(Keyword.`data`) => false case _ => true object ClassDef: def apply( owner: Opt[InnerSymbol], kind: ClsLikeKind, sym: InnerSymbol, bsym: BlockMemberSymbol, ctorSym: Opt[TermSymbol], tparams: Ls[TyParam], params: Ls[ParamList], ext: Opt[New], body: ObjBody, annotations: Ls[Annot], comp: Opt[ClassCompanionSymbol], ): ClassDef = params match case ps :: pss => Parameterized(owner, kind, sym.asInstanceOf// TODO: improve , bsym, S(ctorSym.getOrElse(lastWords("Parameterized classes should have a ctor symbol."))) , tparams, ps, pss, ext, body, comp, annotations) case Nil => Plain(owner, kind, sym.asInstanceOf// TODO: improve , bsym , tparams, ext, body, comp, annotations) def unapply(cls: ClassDef): Opt[(ClassSymbol, Ls[TyParam], Opt[ParamList], ObjBody)] = S((cls.sym, cls.tparams, cls.paramsOpt, cls.body)) case class Parameterized( owner: Opt[InnerSymbol], kind: ClsLikeKind, sym: ClassSymbol, bsym: BlockMemberSymbol, ctorSym: S[TermSymbol], tparams: Ls[TyParam], params: ParamList, auxParams: Ls[ParamList], ext: Opt[New], body: ObjBody, companion: Opt[ClassCompanionSymbol], annotations: Ls[Annot], ) extends ClassDef: val paramsOpt: Opt[ParamList] = S(params) case class Plain( owner: Opt[InnerSymbol], kind: ClsLikeKind, sym: ClassSymbol, bsym: BlockMemberSymbol, tparams: Ls[TyParam], ext: Opt[New], body: ObjBody, companion: Opt[ClassCompanionSymbol], annotations: Ls[Annot] ) extends ClassDef: val paramsOpt: Opt[ParamList] = N val auxParams: List[ParamList] = Nil val ctorSym: Opt[TermSymbol] = N end ClassDef case class TypeDef( sym: TypeAliasSymbol, bsym: BlockMemberSymbol, tparams: Ls[TyParam], rhs: Opt[Term], companion: Opt[CompanionValue], annotations: Ls[Annot], ) extends TypeLikeDef // TODO Store optional source locations for the flags instead of booleans final case class FldFlags(mut: Bool, spec: Bool, pat: Bool, isVal: Bool): def show: Str = val flags = Buffer.empty[String] if mut then flags += "mut" if spec then flags += "spec" if pat then flags += "pattern" if isVal then flags += "val" flags.mkString(" ") override def toString: String = "‹" + show + "›" object FldFlags: val empty: FldFlags = FldFlags(false, false, false, false) object benign: // * Some flags like `mut` and `module` are "benign" in the sense that they don't affect code-gen def unapply(flags: FldFlags): Bool = !flags.spec enum IfLikeForm: case ReturningIf, ImperativeIf, While def isImperative: Bool = this match case ReturningIf => false case ImperativeIf | While => true sealed abstract class Elem: def subTerms: Ls[Term] = this match case Fld(_, term, asc) => term :: asc.toList case Spd(_, term) => term :: Nil def show(using Scope, ShowCfg, Raise): Document def showDbg(using DebugPrinter): Str object Elem: given Conversion[Term, Elem] = PlainFld(_) final case class Fld(flags: FldFlags, term: Term, asc: Opt[Term]) extends Elem, FldImpl object PlainFld: def apply(term: Term) = Fld(FldFlags.empty, term, N) def unapply(fld: Fld): Opt[Term] = S(fld.term) final case class Spd(k: SpreadKind, term: Term) extends Elem: def show(using Scope, ShowCfg, Raise): Document = k.str :: term.show def showDbg(using DebugPrinter): Str = k.str + term.showDbg final case class TyParam(flags: FldFlags, vce: Opt[Bool], sym: VarSymbol) extends Declaration: // * For variance analysis var isCovariant: Bool = true var isContravariant: Bool = true def showDbg: Str = // (if isContravariant then "in " else "") + // (if isCovariant then "out " else "") + (if isCovariant then if isContravariant then "" else "out " else if isContravariant then "in " else "in out ") + flags.show + sym object Param: def simple(sym: VarSymbol) = Param(FldFlags.empty, sym, N, Modulefulness.none) final case class Param(flags: FldFlags, sym: VarSymbol, sign: Opt[Term], modulefulness: Modulefulness) extends Declaration, AutoLocated: var fldSym: Opt[MemberSymbol] = N val flow: FlowSymbol = sym // * This field is filled in during flow analysis; // * it is not meant to be maintained afterwards (so it does not need to be copied around). var signType: Opt[Type] = N def withSignTypeOf(p: Param): this.type = signType = p.signType this def subTerms: Ls[Term] = sign.toList override protected def children: Vector[Located] = sym +: sign.toVector def show(using Scope, ShowCfg, Raise): Document = doc"${flags.show}${sym.showName}${sign.fold(doc"")(": " :: _.show)}" def showDbg(using DebugPrinter): Str = flags.show + sym + sign.fold("")(": " + _.showDbg) final case class ParamList(flags: ParamListFlags, params: Ls[Param], restParam: Opt[Param]) extends AutoLocated: override protected def children: Vector[Located] = params.toVector ++ restParam def foreach(f: Param => Unit): Unit = (params.iterator ++ restParam).foreach(f) def paramCountLB: Int = params.length def paramCountUB: Bool = restParam.isEmpty lazy val paramSyms = params.map(_.sym) ++ restParam.map(_.sym) lazy val allParams = params ++ restParam.toList def subTerms: Ls[Term] = params.flatMap(_.subTerms) ++ restParam.toList.flatMap(_.subTerms) def show(using Scope, ShowCfg, Raise): Document = flags.show :: doc"(" :: ( params.map(_.show) ::: restParam.map(p => doc"...${p.show}").toList ).mkDocument(", ") :: doc")" def showDbg(using DebugPrinter): Str = flags.showDbg + (params.map(_.showDbg) ++ restParam.toList.map("..." + _.showDbg)).mkString("(", ", ", ")") object PlainParamList: def apply(params: Ls[Param]) = ParamList(ParamListFlags.empty, params, N) def unapply(pl: ParamList): Opt[Ls[Param]] = pl match case ParamList(ParamListFlags.empty, params, N) => S(params) case _ => N final case class ParamListFlags(ctx: Bool): def show: Str = (if ctx then "ctx " else "") def showDbg: Str = (if ctx then "ctx " else "") override def toString: String = "‹" + showDbg + "›" /** A subtyping direction. */ enum SubDir: case Sub, Sup def showDbg: Str = this match case Sub => "<:" case Sup => ":>" /** A subtyping constraint. */ final case class SubConstraint(lhs: Term, rhs: Term, dir: SubDir) extends AutoLocated: override protected def children: Vector[Located] = Vector(lhs, rhs) def showDbg(using DebugPrinter): Str = s"${lhs.showDbg} ${dir.showDbg} ${rhs.showDbg}" def subTerms: Ls[Term] = List(lhs, rhs) object ParamListFlags: val empty = ParamListFlags(false) trait FldImpl extends AutoLocated: self: Fld => def children: Vector[Located] = self.term +: self.asc.toVector def show(using Scope, ShowCfg, Raise): Document = flags.show :: self.term.show def showDbg(using DebugPrinter): Str = flags.show + self.term.showDbg def describe: Str = (if self.flags.spec then "specialized " else "") + (if self.flags.mut then "mutable " else "") + self.term.describe /** * Unwrapper that unwraps a term until it is no longer an App. */ object Apps: def unapply(t: Term): S[(Term, Ls[Term])] = t match case Term.App(Apps(base, args), arg) => S(base, args :+ arg) case t => S(t, Nil) trait BlkImpl: this: Blk => def mkBlkClone(using State): Blk = Blk(stats.map(_.mkClone), res.mkClone) def showTopLevel(using Scope, ShowCfg, Raise): Document = (stats ::: (res match case Lit(Tree.UnitLit(false)) => Nil case res => res :: Nil)).map(_.show).mkDocument(doc", # ") ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/flow/Constraint.scala ================================================ package hkmc2 package semantics package flow import scala.collection.mutable import mlscript.utils.*, shorthands.* import hkmc2.utils.Scope import hkmc2.utils.Scope.scope import hkmc2.document.* import typing.* import semantics.* import hkmc2.syntax.SpreadKind import hkmc2.syntax.Tree.{UnitLit, Ident} import hkmc2.semantics.AnySelTerm case class Constraint(lhs: Producer, rhs: Consumer): def showDbg(using DebugPrinter): Str = s"${lhs.showDbg} <: ${rhs.showDbg}" enum Producer: case Flow(sym: FlowSymbol) case Fun(lhs: Consumer, rhs: Producer, captures: Ls[(Producer, Consumer)]) case Tup(elems: Ls[Opt[SpreadKind] -> Producer]) case Ctor(sym: CtorSymbol, args: List[Producer])(val trm: Term) extends Producer, CtorImpl case LeadingDotSel(trm: Term.LeadingDotSel) case Typ(typ: Type) case Unknown(s: Statement) // `s` is just for error reporting/debugging purposes def toLoc: Opt[Loc] = this match case self: Ctor => self.trm.toLoc case self: LeadingDotSel => self.trm.toLoc case Unknown(t) => t.toLoc case _ => None def show(using Scope, Raise): Document = this match case Flow(sym) => scope.allocateOrGetName(sym) case Fun(lhs, rhs, caps) => doc"(${lhs.showAsParams} -> ${rhs.show})" case tup: Tup => Document.bracketed("[", "]")(showTupElems(tup)) case Ctor(LitSymbol(UnitLit(false)), Nil) => "()" case Ctor(sym, args) => doc"${sym.nme}${args.map(_.showAsParams).mkDocument()}" case LeadingDotSel(trm) => given ShowCfg = ShowCfg.internal doc"_?_.${trm.show}" case Typ(typ) => doc"type ${typ.show}" case Unknown(t) => given ShowCfg = ShowCfg.internal doc"¿${t.show}?" def showAsParams(using Scope, Raise): Document = this match case tup: Tup => "(" :: showTupElems(tup) :: doc")" case _ => Document.text(s"...$show") private def showTupElems(tup: Tup)(using Scope, Raise): Document = tup.elems.map: case (None, c) => c.show case (Some(spd), c) => spd.str :: " " :: c.show .mkDocument(", ") def showDbg(using DebugPrinter): Str = this match case Flow(sym) => sym.showDbg case Fun(lhs, rhs, caps) => s"(${lhs.showDbgAsParams} -> ${rhs.showDbg})" case Ctor(LitSymbol(UnitLit(false)), Nil) => "()" case Ctor(sym, Nil) => sym.nme case Tup(args) => s"[${args.map((spd, a) => spd.fold("")(_.str) + a.showDbg).mkString(", ")}]" case Ctor(sym, args) => s"${sym.nme}${args.map(_.showDbgAsParams).mkString}" case sel @ LeadingDotSel(trm) => trm.showDbg case Typ(typ) => s"type ${typ.showDbg}" case Unknown(t) => s"¿${t.showDbg}?" def showDbgAsParams(using DebugPrinter): Str = this match case Tup(args) => args.map: case (None, c) => c.showDbg case (Some(spd), c) => s"${spd.str} ${c.showDbg}" .mkString("(", ", ", ")") case _ => s"(...$showDbg)" end Producer enum Consumer: case Flow(sym: FlowSymbol) case Fun(lhs: Producer, rhs: Consumer) case Tup(init: Ls[Consumer], rest: Opt[(SpreadKind, Consumer, Ls[Consumer])]) case Ctor(sym: CtorSymbol, args: List[Consumer]) case Sel(nme: Ident, res: Consumer)(val trm: AnySelTerm) case Typ(typ: Type) def show(using Scope, Raise): Document = this match case Flow(sym) => scope.allocateOrGetName(sym) case Fun(lhs, rhs) => doc"(${lhs.showAsParams} -> ${rhs.show})" case tup: Tup => Document.bracketed("[", "]")(showTupElems(tup)) case Ctor(sym, args) => doc"${sym.nme}${args.map(_.showAsParams).mkDocument()}" case Sel(nme, res) => doc"{${nme.name}: ${res.show}}" case Typ(typ) => doc"type ${typ.show}" def showAsParams(using Scope, Raise): Document = this match case tup: Tup => "(" :: showTupElems(tup) :: doc")" case _ => doc"(...$show)" private def showTupElems(tup: Tup)(using Scope, Raise): Document = ( tup.init.iterator.map(_.show) ++ tup.rest.iterator.flatMap: case (spd, c, post) => (spd.str :: c.show) :: post.map(_.show) ).toSeq.mkDocument(", ") def showDbg(using DebugPrinter): Str = this match case Flow(sym) => sym.showDbg case Fun(lhs, rhs) => s"(${lhs.showDbgAsParams} -> ${rhs.showDbg})" case Ctor(sym, Nil) => sym.nme case tup: Tup => "[" + showDbgTupElems(tup) + "]" case Ctor(sym, args) => s"${sym.nme}(${args.map(_.showDbg).mkString(", ")})" case Sel(id, res) => s"{${id.name}: ${res.showDbg}}" case Typ(typ) => s"type ${typ.showDbg}" private def showDbgTupElems(tup: Tup)(using DebugPrinter): Str = ( tup.init.iterator.map(_.showDbg) ++ tup.rest.iterator.flatMap: case (spd, c, post) => s"${spd.str}${c.showDbg}" :: post.map(_.showDbg) ).mkString(", ") def showDbgAsParams(using DebugPrinter): Str = this match case tup: Tup => "(" + showDbgTupElems(tup) + ")" case _ => s"(...$showDbg)" end Consumer trait CtorImpl: self: Producer.Ctor => end CtorImpl ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala ================================================ package hkmc2 package semantics package flow import scala.collection.mutable import mlscript.utils.*, shorthands.* import utils.TraceLogger import Message.MessageContext import semantics.*, semantics.Term.*, semantics.AnySelTerm import typing.* import syntax.SpreadKind import syntax.Tree import Elaborator.{State, Ctx, ctx} import Producer as P import Consumer as C type FlowPoint = FlowSymbol | VarSymbol type Path = Vector[FlowPoint] type ProdCtor = Producer.Ctor | Producer.Fun | Producer.Typ | Producer.Tup | Producer.LeadingDotSel case class ConcreteProd(path: Path, ctor: ProdCtor) enum SelectionTarget: case ObjectMember(sym: MemberSymbol) case CompanionMember(comp: Term, sym: MemberSymbol) /** This is a very sketchy exploration/proof of concept of flow analysis * that can be used to help compile programs in a type-directed way without having to perform full type inference. */ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): import tl.* val MAX_FUEL = 1000 def typeBody(b: ObjBody): Unit = typeProd(b.blk) def typeProd(t: Term, insideSelAppChain: Boolean = false): Producer = typeProdImpl(t.expanded, insideSelAppChain) def typeProdImpl(t: Term, insideSelAppChain: Boolean): Producer = trace[P](s"Typing producer: ${t.showDbg}", post = res => s": ${res.showDbg}"): def constrain(lhs: P, rhs: C): Unit = collectedConstraints += ((src = t, c = Constraint(lhs, rhs))) def checkLDS(sub: Term)(res: Producer => Producer): Producer = t.ldsRoot match case S(lds) if !insideSelAppChain => val sym = FlowSymbol("bind") log(s"Constraining leading dot selection ${lds.showDbg} at the top level") constrain(P.LeadingDotSel(lds), C.Flow(sym)) constrain(res(typeProd(sub, insideSelAppChain = true)), C.Flow(sym)) P.Flow(sym) case _ => res(typeProd(sub, insideSelAppChain = insideSelAppChain)) t match case Resolved(trm, sym) => val _ = typeProd(trm) // * Discard the base producer because it's already resolved and `sym` is the real one sym match case cls: ClassSymbol => P.Ctor(cls, Nil)(t) case cls: ModuleOrObjectSymbol => P.Ctor(cls, Nil)(t) case ts: TermSymbol => getFlowSymOrType(ts.bms.get) case Ref(sym) => sym match case sym: VarSymbol => P.Flow(sym) case cls: ClassSymbol => P.Ctor(cls, Nil)(t) case cls: ModuleOrObjectSymbol => P.Ctor(cls, Nil)(t) case ts: TermSymbol => die case bs: BuiltinSymbol => bs.signature case bms: BlockMemberSymbol => if bms.asTrm.isEmpty then raise: ErrorReport: msg"Cannot use non-term member ${bms.nme} in term position" -> t.toLoc :: Nil getFlowSymOrType(bms) case _: Symbol => log(s"/!\\ Unhandled symbol type: ${sym} (${sym.getClass.getSimpleName}) /!\\") P.Unknown(t) case Blk(stats, res) => stats.foreach: case stmt: LetDecl => () case stmt: DefineVar => val rhs = typeProd(stmt.rhs) stmt.sym match case sym: FlowSymbol => constrain(rhs, C.Flow(sym)) case t: TermDefinition => val sign_ty = t.sign.map(typeType(_)) // TODO use sign_ty val ps = t.params.map(typeParamList) t.body.foreach: bod => val bod_ty = typeProd(bod) val fun_ty = ps.foldRight(bod_ty): (pl, acc) => P.Fun(C.Tup(pl, N), acc, Nil) constrain(fun_ty, C.Flow(t.sym.flow)) case t: Term => typeProd(t) case cd: ClassDef => typeBody(cd.body) val prod = cd.paramsOpt match case S(ps) => ps.restParam match case S(_) => ??? case N => P.Fun( C.Tup(ps.params.map(typeParam), N), P.Ctor(cd.sym, Nil // FIXME: Nil )( Term.Missing // FIXME ), Nil, ) case N => P.Unknown(cd) log(s"Class member type: ${prod.showDbg}") constrain(prod, C.Flow(cd.bsym.flow)) case md: ModuleOrObjectDef => // TODO log(s"Module: ${md.path}") typeBody(md.body) case _: Import => // TODO? case stt => log(s"/!\\ Unhandled statement: ${stt} /!\\") P.Unknown(stt) typeProd(res) case Lit(lit) => P.Ctor(LitSymbol(lit), Nil)(t) case sel @ LeadingDotSel(nme) => leadingDotSelsToExpand += sel log(s"Leading dot selection ${sel.showDbg}") val sym = sel.resSym constrain(P.LeadingDotSel(sel), C.Flow(sym)) P.Flow(sym) case sel @ AnySel(pre, nme, cls) => log(s"Selection ${sel.showDbg} ${sel.typ}") checkLDS(pre): pre_t => sel.resolvedSym match case S(sym: BlockMemberSymbol) => log(s"RES ${sym.nme} in ${sel.showDbg}") getFlowSymOrType(sym) case S(sym) => selsToExpand += sel log(s"Unhandled symbol reference ${sym.nme} in ${sel.showDbg}") P.Unknown(sel) case N => selsToExpand += sel val sym = sel.resSym constrain(pre_t, C.Sel(nme, C.Flow(sym))(sel)) P.Flow(sym) case nw @ New(cls, args, rft) => rft match case N => cls.resolvedSym.flatMap(_.asCls) match case N => log(s"Unresolved or invalid class symbol in ${cls.showDbg}") P.Unknown(nw) case S(sym) => sym match case sym: ClassSymbol => val args_t = args.map(typeProd(_, insideSelAppChain = insideSelAppChain)) P.Ctor(sym, args_t)(t) case app @ App(lhs, rhs) => checkLDS(lhs): pre_t => val sym = app.resSym val c = C.Fun(typeProd(rhs), C.Flow(sym)) constrain(pre_t, c) P.Flow(sym) case Lam(pl, bod) => val ps = typeParamList(pl) val pl_t = C.Tup(ps, N) val bod_t = typeProd(bod) P.Fun(pl_t, bod_t, Nil) case ft: FunTy => P.Typ(typeType(ft)) case Tup(fields) => P.Tup(fields.map: case f: Fld => N -> typeProd(f.term)) case Error => P.Ctor(Extr(false), Nil)(t) // case _ => P.Flow(FlowSymbol("TODO")) /* def getType(t: Term): Type = t.resolvedTyp.getOrElse: raise: ErrorReport: msg"Cannot use this ${t.describe} as a type, as it could not be resolved" -> t.toLoc :: Nil Type.Error */ def typeParam(p: Param): C = trace[C](s"Typing param: ${p.showDbg}", post = res => s": ${res.showDbg}"): p.signType match case S(typ) => p.flow.producers += ConcreteProd(Vector.empty, P.Typ(typ)) C.Typ(typ) case N => C.Flow(p.flow) def typeParamList(ps: ParamList): Ls[C] = if ps.restParam.nonEmpty then ??? // TODO ps.params.map(typeParam) def typeType(t: Term): Type = trace[Type](s"Typing consumer: ${t.showDbg}", post = res => s": ${res.showDbg}"): t match case Ref(sym: VarSymbol) => Type.Ref(sym, Nil) // unparameterized type variable case Ref(cls: ClassSymbol) => Type.Ref(cls, Nil) case Ref(ts: BlockMemberSymbol) => Type.Ref(ts.asTpe.getOrElse(TODO(t)), Nil) case Tup(fields) => Type.Tup: fields.map: case f: Fld => typeType(f.term) case Spd(eager, term) => (???, typeType(term)) case FunTy(lhs, rhs, _) => Type.Fun(typeType(lhs), typeType(rhs), N) case _ => TODO(t) val collectedConstraints: mutable.Stack[(src: Term, c: Constraint)] = mutable.Stack.empty val selsToExpand: mutable.Buffer[AnySelTerm] = mutable.Buffer.empty val leadingDotSelsToExpand: mutable.Buffer[LeadingDotSel] = mutable.Buffer.empty def expandTerms() = import SelectionTarget.* selsToExpand.foreach: sel => log(s"Resolved targets for ${sel.showDbg}: ${sel.resolvedTargets.mkString(", ")}") assert(sel.expansion.isEmpty) sel.resolvedTargets match case ObjectMember(sym) :: Nil => assert(sel.sym.isEmpty) sel.expansion = S(S(sel.withSym(sym))) case CompanionMember(comp, sym) :: Nil => val base = Sel(comp, Tree.Ident(sym.nme))(S(sym), FlowSymbol.sel(sym.nme), N, N) val app = App(base, Tup(sel.prefix :: Nil)(Tree.DummyTup))(Tree.DummyApp, N, FlowSymbol.app()) log(s"Expansion: ${app.showDbg}") sel.expansion = S(S(app)) case Nil => // FIXME: actually allow that in dead code (use floodfill constraints from exported members to detect) if !sel.isErroneous then raise: ErrorReport: msg"Cannot resolve selection" -> sel.toLoc :: Nil // * An error should alsoready be reported in this case case targets => raise: ErrorReport: msg"Ambiguous selection with multiple apparent targets" -> sel.toLoc :: targets.map: case ObjectMember(sym) => msg"object member ${sym.nme}" -> sym.toLoc case CompanionMember(_, sym) => msg"companion member ${sym.nme}" -> sym.toLoc leadingDotSelsToExpand.foreach: sel => log(s"Resolved targets for ${sel.showDbg}: ${sel.resolvedTargets.mkString(", ")}") assert(sel.expansion.isEmpty) sel.resolvedTargets match case CompanionMember(comp, sym) :: Nil => val base = Sel(comp, Tree.Ident(sym.nme))(S(sym), FlowSymbol.sel(sym.nme), N, N) log(s"Leading dot expansion: ${base.showDbg}") sel.expansion = S(S(base)) case Nil => // FIXME: actually allow that in dead code (use floodfill constraints from exported members to detect) sel.expansion = S(S(Error)) raise: ErrorReport: msg"Cannot resolve leading dot selection" -> sel.toLoc :: Nil case targets => sel.expansion = S(S(Error)); raise: ErrorReport: msg"Ambiguous selection with multiple apparent targets:" -> sel.toLoc :: targets.map: case CompanionMember(_, sym) => msg"companion member ${sym.nme}" -> sym.toLoc def getCompanionMember(name: Str, oc: Opt[SrcScope], sym: Symbol): Opt[(Term, BlockMemberSymbol)] = sym match case ms: ModuleOrObjectSymbol => ms.defn.flatMap: d => d.body.members.get(name) match case S(memb: BlockMemberSymbol) => oc .flatMap(findAccessPath(_, d.path, ms)) .map((_, memb)) case _ => N case cs: ClassSymbol => cs.defn .flatMap(_.moduleCompanion) .flatMap(x => x.defn.map((x, _))) .flatMap: (comp, d) => d.body.members.get(name) match case S(memb: BlockMemberSymbol) => oc .flatMap(findAccessPath(_, d.path, comp)) .map((_, memb)) case _ => N case _ => N // * This is a rather hacky way to check whether we're looking at a BMS from the same compilation unit/file, // * in which case we should get access to its internal flow symbol, or whether it's from another file, // * in which case we shouldn't, as flow analysis is supposed to be local // * (if not, we'd notably get race conditions and inconsistencies in compilation). // * ASSUMPTION: This relies on the fact that each file is always elaborated in a distinct State instance. def getFlowSymOrType(bms: BlockMemberSymbol): P = if bms.getState is summon[State] then P.Flow(bms.flow) else P.Typ(Type.Top) // TODO: should get a frozen inferred type view of the internally inferred flow def solveConstraints(): Unit = var fuel = MAX_FUEL val toSolve: mutable.Stack[Constraint] = mutable.Stack.empty val inCache: mutable.Set[FlowSymbol -> C] = mutable.Set.empty val outCache: mutable.Set[P -> FlowSymbol] = mutable.Set.empty while fuel > 0 && collectedConstraints.nonEmpty do val (trm, cc) = collectedConstraints.pop() toSolve.push(cc) trace(s"Handling constraint: ${cc.showDbg} (from ${trm.showDbg})"): while fuel > 0 && toSolve.nonEmpty do fuel -= 1 val c = toSolve.pop() def dig(lhs: P, rhs: C, path: Path): Unit = log(s"Solving: ${lhs.showDbg} <: ${rhs.showDbg} (${lhs.getClass.getSimpleName}, ${rhs.getClass.getSimpleName})") (lhs, rhs) match case (P.Flow(sym), rhs) if inCache.contains(sym -> rhs) => log(s"In (in) cache!") case (lhs, C.Flow(sym)) if outCache.contains(lhs -> sym) => log(s"In (out) cache!") case (P.Flow(sym), C.Flow(sym2)) => log(s"New flow $sym ~> $sym2") sym.outFlows += sym2 sym.producers.foreach(cp => dig(cp.ctor, rhs, cp.path ++ path)) case (lhs: ProdCtor, C.Flow(sym)) => log(s"New flow $lhs ~> $sym") sym.producers += ConcreteProd(path, lhs) sym.consumers.foreach: c => dig(lhs, c, path) case (P.Flow(sym), rhs) => log(s"New flow $sym ~> $rhs") sym.consumers += rhs sym.producers.foreach: cp => dig(cp.ctor, rhs, cp.path ++ path) sym.outFlows.foreach: fs => dig(P.Flow(fs), rhs, fs +: path) case (P.Fun(pl, pr, _), C.Fun(cl, cr)) => dig(cl, pl, path) // FIXME path dig(pr, cr, path) // FIXME path case (P.Ctor(sym1, args1), C.Ctor(sym2, args2)) if (sym1 is sym2) && args1.size === args2.size // TODO generalize => args1.zip(args2).foreach: (a1, a2) => dig(a1, a2, path) // FIXME path case (P.Tup(args), C.Tup(ini, rst)) => def zip(args: Ls[Opt[SpreadKind] -> P], cons: Ls[C], rst: Opt[(SpreadKind, C, Ls[C])], path: Path): Unit = (args, cons) match case (Nil, Nil) => () case ((N, a1) :: args, c1 :: cons) => dig(a1, c1, path) // FIXME path zip(args, cons, rst, path) case ((S(spd), a1) :: args, Nil) => ??? case ((N, a1) :: args, Nil) => // extra producers can be matched by spread in consumer rst match case S((spd, a2, post)) => ??? case N => raise(ErrorReport( msg"Tuple arity mismatch: too many elements on the producer side" -> trm.toLoc :: Nil)) zip(args, ini, rst, path) case (sel @ P.LeadingDotSel(trm), rhs) => rhs match case C.Typ(Type.Ref(sym, _)) => log(s"Examining ${sym} for leading dot selection resolution") getCompanionMember(trm.nme.name, trm.originalCtx, sym) match case S((path, memb)) => sel.trm.resolvedTargets ::= SelectionTarget.CompanionMember(path, memb) log(s"Found member ${memb}") toSolve.push(Constraint(getFlowSymOrType(memb), C.Flow(trm.resSym))) case _ => log(s"Could not find member ${trm.nme.name} in ${sym}") case _ => log("Unhandled RHS for leading dot selections") case (lhs, sel: C.Sel) => lhs match case P.Typ(Type.Ref(sym: ClassSymbol, targs)) => if targs.nonEmpty then TODO(targs) toSolve.push(Constraint(P.Ctor(sym, Nil)(Term.Missing), sel)) case P.Ctor(sym: ClassSymbol, args) => val d = sym.defn.getOrElse(die) d.body.members.get(sel.nme.name) match case S(memb: BlockMemberSymbol) => sel.trm.resolvedTargets ::= SelectionTarget.ObjectMember(memb) log(s"Found immediate member ${memb}") toSolve.push(Constraint(getFlowSymOrType(memb), sel.res)) case S(memb) => TODO(memb) case N => d.moduleCompanion match case S(comp) => val cd = comp.defn.getOrElse(die) cd.body.members.get(sel.nme.name) match case S(memb) => log(s"Found companion member ${memb}") sel.trm.originalCtx match case S(oc) => val patho = findAccessPath(oc, cd.path, comp) log(s"Access path: ${patho}") patho match case S(path) => sel.trm.resolvedTargets ::= SelectionTarget.CompanionMember(path, memb) val newlhs = memb match case memb: BlockMemberSymbol => getFlowSymOrType(memb) case _ => TODO(memb) toSolve.push(Constraint(newlhs, C.Fun(P.Tup((N, lhs) :: Nil), sel.res))) case N => raise: sel.trm.isErroneous = true ErrorReport: msg"Cannot access companion ${comp.name} from the context of this selection" -> sel.trm.toLoc :: Nil case N => ??? case N => ??? case N => raise: sel.trm.isErroneous = true ErrorReport( // TODO construct proper error message msg"Field ${sel.nme.name} is not a member of ${d.kind.desc} ${d.sym.name}" -> trm.toLoc :: Nil) case _ => raise: sel.trm.isErroneous = true ErrorReport( // TODO construct proper error message msg"Unresolved selection:" -> sel.trm.toLoc :: msg"Type `${lhs.showDbg}` does not contain member '${sel.nme.name}'" -> lhs.toLoc :: Nil) case _ => log(s"/!\\ Unhandled constraint /!\\") end dig dig(c.lhs, c.rhs, Vector.empty) if fuel === 0 then raise(ErrorReport( msg"Could not solve all constraints within $MAX_FUEL iterations." -> N :: Nil)) def findAccessPath(src: SrcScope, dst: SrcScope, moduleSym: ModuleOrObjectSymbol): Opt[Term] = log(s"outermostAcessibleBase ${dst.outermostAcessibleBase}") val (outermostBase, outermostPath) = dst.outermostAcessibleBase var cur = src while cur isnt outermostBase do cur.parent match case N => return N case S(p) => cur = p assert(cur is outermostBase) (moduleSym :: outermostPath).reverse match case Nil => die case sym :: syms => S: syms.foldLeft(sym.asInstanceOf[DefinitionSymbol[?]]//FIXME .bms.getOrElse(die).ref(): Term): (a, b) => Sel(a, Tree.Ident(b.nme))(S(b.asInstanceOf[DefinitionSymbol[?]]//FIXME .bms.getOrElse(die)), FlowSymbol.sel(b.nme), N, N) import hkmc2.document.* import utils.Scope def showFlows(using Scope, ShowCfg): Document = val syms = summon[ShowCfg].shownSymbols.toIndexedSeq.sortBy(_.uid) doc" #{ # ${ syms.collect: case sym: FlowSymbol => ( if sym.producers.isEmpty then Nil else doc"${sym.showName} <~ ${ sym.producers.toSeq.map(_.ctor.show).mkDocument(doc" ")}" :: Nil ) ::: ( if sym.consumers.isEmpty then Nil else doc"${sym.showName} ~> ${ sym.consumers.toSeq.map(_.show).mkDocument(doc" ")}" :: Nil ) ::: ( if sym.outFlows.isEmpty then Nil else doc"${sym.showName} -> ${ sym.outFlows.toSeq.map(_.showName).mkDocument(doc" ")}" :: Nil ) ::: Nil .flatten.mkDocument(doc" # ") } #} " end FlowAnalysis ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/FlatPattern.scala ================================================ package hkmc2 package semantics package ucs import mlscript.utils.*, shorthands.* import syntax.*, Tree.Ident, Elaborator.State /** * Flattened patterns used in splits for `Normalization` and `Lowering`. All * cases of patterns declared hereby can be matched in constant time. * Non-trivial patterns (e.g., unions, intersections, * transformations, etc) have been compiled to `Split`. */ enum FlatPattern extends AutoLocated: case Lit(literal: Literal) /** * To match against a class or an object. * * @param constructor The term representing the class or the object. * @param symbol The symbol resolved from `constructor`. * @param arguments The sub-scrutinees and their locations. This field is * `None` when no argument list is provided, i.e., `x is Some`. * @param refined Whether the type of the scrutinee can be further refined by * other patterns in nested splits. It is not used currently. * @param tree The tree which this pattern is elaborated from. This is only * used for error reporting and should not be copied in `mkClone`. */ case ClassLike( val constructor: Term, val symbol: ClassSymbol | ModuleOrObjectSymbol, val arguments: Opt[Ls[(BlockLocalSymbol, Opt[Loc])]], var refined: Bool )(val tree: Tree) case Tuple(size: Int, inf: Bool) case Record(entries: List[(Ident -> BlockLocalSymbol)]) def mkClone(using State): FlatPattern = this match case Lit(literal) => Lit(literal) case pattern @ ClassLike(constructor, symbol, arguments, refined) => ClassLike(constructor.mkClone, symbol, arguments, refined)(Tree.Dummy) case Tuple(size, inf) => Tuple(size, inf) case Record(entries) => Record(entries) def subTerms: Vector[Term] = this match case p: ClassLike => Vector(p.constructor) case _: (Lit | Tuple | Record) => Vector.empty def children: Vector[Located] = this match case Lit(literal) => Vector(literal) case ClassLike(ctor, symbol, scruts, _) => Vector(ctor) ++ scruts.fold(Vector.empty)(_.map(_._1)) case Tuple(fields, _) => Vector.empty case Record(entries) => entries.iterator.flatMap: case (nme, als) => Vector(nme, als) .toVector def showDbg(using DebugPrinter): Str = (this match case Lit(literal) => literal.idStr case ClassLike(ctor, symbol, args, rfd) => def showCtor(ctor: Term): Str = ctor match // This prints the symbol name without `refNum` and "member:" prefix. case Term.Ref(sym: BlockMemberSymbol) => sym.nme // This prints the symbol without `refNum`. case Term.Ref(sym) => sym.toString case Term.Sel(p, i) => s"${showCtor(p)}.${i.name}" case Term.SynthSel(p, i) => s"${showCtor(p)}.${i.name}" case _ => ctor.showDbg (if rfd then "refined " else "") + showCtor(ctor) + args.fold("")(_.iterator.map(_._1.nme).mkString("(", ", ", ")")) case Tuple(size, inf) => "[]" + (if inf then ">=" else "=") + size case Record(Nil) => "{}" case Record(entries) => entries.iterator.map(_.name + ": " + _).mkString("{ ", ", ", " }")) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala ================================================ package hkmc2 package semantics package ucs import mlscript.utils.*, shorthands.* import syntax.{Literal, Tree, Keyword}, utils.* import Message.MessageContext import Elaborator.{Ctx, State, ctx} import codegen.Lowering import collection.mutable.{Map as MutMap} class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) extends TermSynthesizer: import Normalization.*, Mode.* import tl.* def reportUnreachableCase[T <: Located](unreachable: Located, subsumedBy: T, when: Bool = true): T = if when then warn( msg"this case is unreachable" -> unreachable.toLoc, msg"because it is subsumed by the branch" -> subsumedBy.toLoc) subsumedBy extension (these: Split) def markAsFallback: Split = these.isFallback = true these def clearFallback: Split = these.isFallback = false these def ++(those: Split): Split = if these.isFull then log("tail is discarded") these else (these match case Split.Cons(head, tail) => Split.Cons(head, tail ++ those) case Split.Let(name, term, tail) => Split.Let(name, term, tail ++ those) case Split.Else(_) /* impossible */ | Split.End => those) extension (lhs: FlatPattern) /** Checks if two patterns are the same. */ def =:=(rhs: FlatPattern): Bool = (lhs, rhs) match case (lhs: FlatPattern.ClassLike, rhs: FlatPattern.ClassLike) => lhs.constructor.symbol === rhs.constructor.symbol case (FlatPattern.Lit(l1), FlatPattern.Lit(l2)) => l1 === l2 case (FlatPattern.Tuple(n1, b1), FlatPattern.Tuple(n2, b2)) => n1 === n2 && b1 === b2 case (FlatPattern.Record(ls1), FlatPattern.Record(ls2)) => ls1.lazyZip(ls2).forall: case ((fieldName1, p1), (fieldName2, p2)) => fieldName1 === fieldName2 && p1 === p2 case (_: FlatPattern.ClassLike, _) | (_: FlatPattern.Lit, _) | (_: FlatPattern.Tuple, _) | (_: FlatPattern.Record, _) => false /** Checks if `lhs` can be subsumed under `rhs`. */ def <:<(rhs: FlatPattern): Bool = compareCasePattern(lhs, rhs) /** If the pattern is a class-like pattern, override its `refined` flag. */ def markAsRefined: Unit = lhs match case lhs: FlatPattern.ClassLike => lhs.refined = true case _ => () extension (lhs: FlatPattern.Record) /** reduces the record pattern `lhs` assuming we have matched `rhs`. * It removes field matches that may now be unnecessary */ infix def assuming(rhs: FlatPattern): FlatPattern.Record = rhs match case FlatPattern.Record(rhsEntries) => val filteredEntries = lhs.entries.filter: (fieldName1, _) => rhsEntries.forall { (fieldName2, _) => !(fieldName1 === fieldName2)} FlatPattern.Record(filteredEntries) case rhs: FlatPattern.ClassLike => rhs.constructor.symbol.flatMap(_.asCls) match case S(cls: ClassSymbol) => cls.defn match case S(ClassDef.Parameterized(params = paramList)) => val filteredEntries = lhs.entries.filter: (fieldName1, _) => paramList.params.forall { (param:Param) => !(fieldName1 === param.sym.id)} FlatPattern.Record(filteredEntries) case S(_) | N => lhs case S(_) | N => lhs case _ => lhs inline def apply(split: Split): Split = normalize(split)(using VarSet()) /** * Normalize core abstract syntax to MLscript syntax. * * @param split the split to normalize * @return the normalized term */ private def normalize(split: Split)(using vs: VarSet): Split = trace( pre = s"normalize <<< ${split.prettyPrint}", post = (res: Split) => "normalize >>> " + res.prettyPrint, ): normalizeImpl(split) def normalizeImpl(split: Split)(using vs: VarSet): Split = split match case Split.Cons(Branch(scrutinee, pattern, consequent), alternative) => log(s"MATCH: ${scrutinee.showDbg} is ${pattern.showDbg}") val whenTrue = normalize(specialize(consequent ++ alternative.duplicate, +, scrutinee, pattern)) val whenFalse = normalizeImpl(specialize(alternative, -, scrutinee, pattern).clearFallback) Branch(scrutinee, pattern, whenTrue) ~: whenFalse case Split.Let(v, _, tail) if vs has v => log(s"LET: SKIP already declared scrutinee $v") normalizeImpl(tail) case Split.Let(v, rhs, tail) => log(s"LET: $v") Split.Let(v, rhs, normalizeImpl(tail)(using vs + v)) case split @ Split.Else(default) => log(s"DFLT: ${default.showDbg}") split case Split.End => Split.End /** * Specialize `split` with the assumption that `scrutinee` matches `pattern`. * * In mode `+` (positive), keeps branches consistent with the assumption: * - Case 1.1.1: Same pattern (`=:=`) → merge continuation and tail via alias bindings. * - Case 1.1.2: Branch pattern is more specific (`thatPattern <:< pattern`) → keep as-is, * mark the specializing pattern as refined, and recurse into the tail so remaining * branches on the same scrutinee are simplified with the known assumption. * - Case 1.1.3: Branch is a fallback → skip to tail. * - Case 1.1.4: Branch is a record → simplify fields already matched by the assumption. * - Case 1.1.5: Specializing pattern is more specific (`pattern <:< thatPattern`) → keep as-is * (the branch always matches when the assumption holds). * - Case 1.1.6: Patterns are unrelated — if provably disjoint (e.g., different literals, * sibling classes under single inheritance), skip; otherwise keep the branch to support * conjunction patterns like `A & B`. * * In mode `-` (negative), removes branches that the assumption makes unreachable: * - Case 1.2.1: Branch pattern equals or is subsumed by the assumption → remove. * - Case 1.2.2: Unrelated → keep, recurse into tail. * * Case 2: Different scrutinee → recurse into both continuation and tail. */ private def specialize( split: Split, mode: Mode, scrutinee: Term.Ref, pattern: FlatPattern )(using VarSet): Split = trace( pre = s"S$mode <<< ${scrutinee.showDbg} is ${pattern.showDbg} : ${split.prettyPrint}", post = (r: Split) => s"S$mode >>> ${r.prettyPrint}" ): def rec(split: Split)(using mode: Mode, vs: VarSet): Split = split match case Split.End => log("CASE Nil"); split case Split.Else(_) => log("CASE Else"); split case split @ Split.Let(sym, _, tail) => log(s"CASE Let ${sym}") split.copy(tail = rec(tail)) case split @ Split.Cons(head @ Branch(thatScrutinee, thatPattern, continuation), tail) => log(s"CASE Cons ${head.showDbg}") if scrutinee === thatScrutinee then mode match case + => log(s"Case 1.1: $scrutinee === $thatScrutinee") if thatPattern =:= pattern then log(s"Case 1.1.1: $pattern =:= $thatPattern") aliasBindings(pattern, thatPattern)(rec(continuation) ++ rec(tail)) else if thatPattern <:< pattern then log(s"Case 1.1.2: $pattern <:< $thatPattern") pattern.markAsRefined; split.copy(tail = rec(tail)) else if split.isFallback then log(s"Case 1.1.3: $pattern is unrelated with $thatPattern") rec(tail) else thatPattern match case thatPattern: FlatPattern.Record => log(s"Case 1.1.4: $thatPattern is a record") // we can use information if pattern is itself a record, or if it is a constructor with arguments val simplifiedRecord = thatPattern assuming pattern if simplifiedRecord.entries.isEmpty then tail else Split.Cons(Branch(thatScrutinee, simplifiedRecord, continuation), tail) case _ => if pattern <:< thatPattern then // TODO: the warning will be useful when we have inheritance information // raiseDesugaringWarning( // msg"the pattern always matches" -> thatPattern.toLoc, // msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, // msg"which is a subtype of ${thatPattern.toString}" -> (pattern match { // case Pattern.Class(cls, _, _) => cls.toLoc // case _ => thatPattern.toLoc // })) log(s"case 1.1.5: $pattern <:< $thatPattern") split else if areProvablyDisjoint(pattern, thatPattern) then log(s"Case 1.1.6: $pattern and $thatPattern are provably disjoint") rec(tail) else // When patterns are not provably disjoint, we cannot assume // the scrutinee can't match both (e.g., conjunction patterns // like `A & B`). Keep the branch. log(s"Case 1.1.6: $pattern and $thatPattern are not provably disjoint") head.copy(continuation = rec(continuation)) ~: rec(tail) case - => log(s"Case 1.2: $scrutinee === $thatScrutinee") if thatPattern =:= pattern || thatPattern <:< pattern then log(s"Case 1.2.1: $pattern =:= (or <:<) $thatPattern") rec(tail) else log(s"Case 1.2.2: $pattern are unrelated to $thatPattern") split.copy(tail = rec(tail)) else log(s"Case 2: $scrutinee =/= $thatScrutinee") head.copy(continuation = rec(continuation)) ~: rec(tail) end rec rec(split)(using mode, summon) private def aliasBindings(p: FlatPattern, q: FlatPattern): Split => Split = (p, q) match case (FlatPattern.ClassLike(_, _, S(ss1), _), FlatPattern.ClassLike(_, _, S(ss2), _)) => ss1.iterator.zip(ss2.iterator).foldLeft(identity[Split]): case (acc, (l, r)) if l._1 === r._1 => acc case (acc, (l, r)) => innermost => Split.Let(r._1, l._1.safeRef, acc(innermost)) case (_, _) => identity import codegen.*, lowering.{term_nonTail, subTerm_nonTail, unreachableFn} /** Collect terms that appear in multiple `Split.Else` branches. We will share * the corresponding blocks to avoid code duplication. */ private def createLabelsForDuplicatedBranches(split: Split)(using config: Config): Labels = val counts: MutMap[Term, (order: Int, count: Int)] = MutMap.empty var fallThroughCount = 0 def rec(s: Split): Unit = s match case Split.End => fallThroughCount += 1 case Split.Else(els) => counts.updateWith(els): case S((n, count)) => S((n, count + 1)) case N => S((counts.size + 1, 1)) case Split.Let(_, _, tail) => rec(tail) case Split.Cons(Branch(_, _, cons), tail) => rec(cons); rec(tail) rec(split) val consequents = config.patMatConsequentSharingThreshold match case S(threshold) => counts.iterator.filter(kv => kv._2.count > 1 && kv._2.count * kv._1.size > threshold).toSeq.sortBy(_._2.order).zipWithIndex.map: case ((term, _), i) => (term, LabelSymbol(S(term), s"split_${i + 1}$$")) .toList case N => Nil val matchError = if fallThroughCount > 1 then S(LabelSymbol(N, s"split_default$$")) else N Labels(consequents, matchError) private def lowerSplit (split: Split, cont: Result => Block) (using labels: Labels, form: IfLikeForm) (using LoweringCtx) : Block = split match case Split.Let(sym, trm, tl) => LoweringCtx.loweringCtx.collectScopedSym(sym) term_nonTail(trm): r => Assign(sym, r, lowerSplit(tl, cont)) case Split.Cons(Branch(scrut, pat, tail), restSplit) => subTerm_nonTail(scrut): sr => tl.log(s"Binding scrut $scrut to $sr (${summon[LoweringCtx].map})") def mkMatch(cse: Case -> Block) = Match(sr, cse :: Nil, S(lowerSplit(restSplit, cont)), End() ) pat match case FlatPattern.Lit(lit) => mkMatch(Case.Lit(lit) -> lowerSplit(tail, cont)) case FlatPattern.ClassLike(ctor, symbol, argsOpt, _refined) => for args <- argsOpt; (arg, _) <- args do LoweringCtx.loweringCtx.collectScopedSym(arg) /** Make a continuation that creates the match. */ def k(ctorSym: ClassLikeSymbol, clsParams: Ls[TermSymbol])(st: Path): Block = val args = argsOpt.map(_.map(_._1)).getOrElse(Nil) // Normalization should reject cases where the user provides // more sub-patterns than there are actual class parameters. assert(argsOpt.isEmpty || args.length <= clsParams.length, (argsOpt, clsParams)) def mkArgs(args: Ls[TermSymbol -> BlockLocalSymbol])(using LoweringCtx): Case -> Block = args match case Nil => Case.Cls(ctorSym, st) -> lowerSplit(tail, cont) case (param, arg) :: args => val (cse, blk) = mkArgs(args) (cse, Assign(arg, Select(sr, new Tree.Ident(param.id.name).withLocOf(arg))(S(param)), blk)) mkMatch(mkArgs(clsParams.iterator.zip(args).toList)) symbol match case cls: ClassSymbol if ctx.builtins.virtualClasses contains cls => // [invariant:0] Some classes (e.g., `Int`) from `Prelude` do // not exist at runtime. If we do lowering on `trm`, backends // (e.g., `JSBuilder`) will not be able to handle the corresponding selections. // In this case the second parameter of `Case.Cls` will not be used. // So we do not elaborate `ctor` when the `cls` is virtual // and use it `Predef.unreachable` here. k(cls, Nil)(unreachableFn) case cls: ClassSymbol => subTerm_nonTail(ctor)(k(cls, cls.tree.clsParams.headOption.getOrElse(Nil) // FIXME? case when there are only aux parameter lists )) case mod: ModuleOrObjectSymbol => subTerm_nonTail(ctor)(k(mod, Nil)) case FlatPattern.Tuple(len, inf) => mkMatch(Case.Tup(len, inf) -> lowerSplit(tail, cont)) case FlatPattern.Record(entries) => for (_, s) <- entries do LoweringCtx.loweringCtx.collectScopedSym(s) val objectSym = ctx.builtins.Object mkMatch( // checking that we have an object Case.Cls(objectSym, Value.Ref(BuiltinSymbol(objectSym.nme, false, false, true, false))), entries.foldRight(lowerSplit(tail, cont)): case ((fieldName, fieldSymbol), blk) => mkMatch( Case.Field(fieldName, safe = true), // we know we have an object, no need to check again Assign(fieldSymbol, Select(sr, fieldName)(N), blk) ) ) case Split.Else(els) => labels.get(els) match case S(label) => Break(label) case N => term_nonTail(els, inStmtPos = form.isImperative)(cont) case Split.End => // * See comment [comment:1] above if form is IfLikeForm.While then End() else labels.matchError.fold(throwMatchErrorBlock)(Break(_)) /** * Make a block that throws the match error. We might add the information of * match failure in the future. */ private def throwMatchErrorBlock = Throw(Instantiate(mut = false, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(S(ctx.builtins.Error)), (Value.Lit(syntax.Tree.StrLit("match error")).asArg :: Nil) :: Nil)) // TODO add failed-match scrutinee info import syntax.Keyword.{`if`, `while`} def apply(t: Term.IfLike)(k: Result => Block)(using config: Config)(using LoweringCtx): Block = val newSplit = t.split.getExpandedSplit scoped("ucs:desugared"): log(s"Split with nested patterns:\n${t.split.prettyPrint(t.kw)}") log(s"Expanded split with flattened patterns:\n${newSplit.prettyPrint}") this(newSplit, t.form, S(t), k) def apply(t: Term.SynthIf)(k: Result => Block)(using Config, LoweringCtx): Block = this(t.split, IfLikeForm.ReturningIf, S(t), k) def apply(split: Split)(k: Result => Block)(using Config, LoweringCtx): Block = this(split, IfLikeForm.ReturningIf, N, k) private def apply(inputSplit: Split, form: IfLikeForm, t: Opt[Term], k: Result => Block)(using cfg: Config, outerCtx: LoweringCtx) = // if it's `while`, we always make sure that loop bodies are proper nested scoped // see https://github.com/hkust-taco/mlscript/pull/356#discussion_r2588412258 val useNestedScoped = form is IfLikeForm.While (if useNestedScoped then LoweringCtx.nestScoped else outerCtx).givenIn: var usesResTmp = false // The symbol of the temporary variable for the result of the `if`-like term. // It will be created in one of the following situations. // 1. The continuation `k` is not a tail operation. // 2. There are shared consequents in the `if`-like term. // 3. The term is a `while` and the result is used. lazy val l = usesResTmp = true val res = new TempSymbol(t) outerCtx.collectScopedSym(res) res // The symbol for the loop label if the term is a `while`. lazy val loopLabel = new LabelSymbol(t) lazy val f = val res = new BlockMemberSymbol("while", Nil, false) outerCtx.collectScopedSym(res) res lazy val tSym = TermSymbol.fromFunBms(f, N) val normalized = tl.scoped("ucs:normalize"): normalize(inputSplit)(using VarSet()) tl.scoped("ucs:normalized"): tl.log(s"Normalized:\n${normalized.prettyPrint}") // Collect consequents that are shared in more than one branch. given labels: Labels = createLabelsForDuplicatedBranches(normalized) lazy val rootBreakLabel = new LabelSymbol(N, "split_root$") lazy val breakRoot = if (k is Ret) || (k is Thrw) then k else (r: Result) => Assign(l, r, Break(rootBreakLabel)) lazy val assignResult = (r: Result) => form match case IfLikeForm.ReturningIf => if (k is Ret) || (k is Thrw) then k(r) else Assign(l, r, End()) case IfLikeForm.ImperativeIf => Assign.discard(r, End()) case IfLikeForm.While => Assign(State.noSymbol, r, loopCont) // NOTE: `shouldRewriteWhile` is not the same as `config.rewriteWhileLoops` // as shouldRewriteWhile is always true when effect handler lowering is on lazy val loopCont = if config.shouldRewriteWhile then Return(Call(Value.Ref(f, S(tSym)), Nil ne_:: Nil)(true, true, false), false) else Continue(loopLabel) val cont = form match case IfLikeForm.While => // * Note that if the term is a `while`, the continuation `cont` corresponds to // * what happens after each specified branch terminates, // * ie, continuation to the next loop iteration. (r: Result) => Assign.discard(r, loopCont) case IfLikeForm.ImperativeIf => (r: Result) => Assign.discard(r, End()) case IfLikeForm.ReturningIf => if labels.isEmpty then if k.isInstanceOf[TailOp] then // If there are no shared consequents and the continuation is a tail // operation, we can call it directly. k else // Otherwise, if the continuation is not a tail operation, we should // save the result in a temporary variable and call the continuation // in the end. assignResult else // When there are shared consequents, we are forced to save the result // in the temporary variable nevertheless. Note that `cont` only gets // called for non-shared consequents, so we should break to the end of // the entire split after the assignment. breakRoot // When we are not rewriting while loops to tail-recursive functions, // whether we need a `Break` to exit the loop at the end of the main block // depends on whether there are shared consequents, // as shared consequents will be lifted out of the main block and added as separate labelled blocks // to jump to, so we cannot simply fall through the end of the main block to exit the loop. // Note that when there is a `default` branch and we're in a loop, // the semantics of that default branch is to always `continue` the loop, // so we don't need to break out of the loop at the end of the main block as well. val needsBreakToExitLoop = (form is IfLikeForm.While) && !config.shouldRewriteWhile && labels.consequents.nonEmpty //&& labels.default.isEmpty // The main block contains the lowered split, where each shared consequent // is replaced with a `Break` to the corresponding label. val mainBlock = val innermostBlock = given IfLikeForm = form if needsBreakToExitLoop then Begin(lowerSplit(normalized, cont), Break(rootBreakLabel)) else lowerSplit(normalized, cont) // Wrap the main block in a labelled block for each shared consequent. The // `rest` of each `Label` is the lowered consequent plus a `Break` to the // end of the entire `if` term. Otherwise, it will fall through to the outer // consequent, which is the wrong semantics. val innerBlock: Block = labels.consequents match case Nil => innermostBlock case all @ (head :: tail) => def wrap(consequents: Ls[(Term, LabelSymbol)]): Block = consequents.foldRight(innermostBlock): case ((term, label), innerBlock) => Label(label, false, innerBlock, if form is IfLikeForm.While then term_nonTail(term)(r => Assign.discard(r, loopCont)) else term_nonTail(term)(breakRoot)) // There is no need to generate `break` for the outermost split // if we're not generating an additional matchError block at the end. if labels.matchError.isEmpty then Label(head._2, false, wrap(tail), term_nonTail(head._1)(assignResult)) else wrap(all) if form is IfLikeForm.While then if needsBreakToExitLoop then Begin(innerBlock, Break(rootBreakLabel)) else innerBlock else labels.matchError match case S(label) => Label(label, false, innerBlock, throwMatchErrorBlock) case N => innerBlock // If there are shared consequents, we need a wrap the entire block in a // `Label` so that `Break`s in the shared consequents can jump to the end. val body = Scoped( if useNestedScoped then LoweringCtx.loweringCtx.getCollectedSym else Set.empty, if labels.isEmpty && !needsBreakToExitLoop then mainBlock else Label(rootBreakLabel, false, mainBlock, End())) // Embed the `body` into `Label` if the term is a `while`. lazy val rest = if usesResTmp then k(Value.Ref(l)) else k(lowering.unit) val block = if form === IfLikeForm.While then // NOTE: `shouldRewriteWhile` is not the same as `config.rewriteWhileLoops` // as shouldRewriteWhile is always true when effect handler lowering is on if config.shouldRewriteWhile then val loopResult = TempSymbol(N) val isReturned = TempSymbol(N) outerCtx.collectScopedSym(loopResult) outerCtx.collectScopedSym(isReturned) val loopEnd: Path = Select(Value.Ref(State.runtimeSymbol), Tree.Ident("LoopEnd"))(S(State.loopEndSymbol)) val blk = blockBuilder .define(FunDefn(N, f, tSym, PlainParamList(Nil) :: Nil, Begin(body, Return(loopEnd, false)))(configOverride = N, annotations = Nil)) .assign(loopResult, Call(Value.Ref(f, S(tSym)), Nil ne_:: Nil)(true, true, false)) if summon[LoweringCtx].mayRet then blk .assign(isReturned, Call(Value.Ref(State.builtinOpsMap("!==")), (loopResult.asPath.asArg :: loopEnd.asArg :: Nil) ne_:: Nil)(true, false, false)) .ifthen(Value.Ref(isReturned), Case.Lit(Tree.BoolLit(true)), Return(Value.Ref(loopResult), false), N ) .rest(rest) else blk.rest(rest) else Begin(Label(loopLabel, true, body, End()), rest) else if labels.isEmpty && k.isInstanceOf[TailOp] && !form.isImperative // * ^ Generated imperative `if` branches do not always yield a value, so if we removed this, // * we would sometimes return `undefined`. // * (This could be improved; currently, we fail to preserve the tail call in `fun f() = if false do f()`.) then body else Begin(body, rest) scoped("ucs:lowered"): log(s"Lowered:\n${block.showAsTree}") block end Normalization object Normalization: /** This contains the labels for duplicated consequents and the default * branch which throws match errors. */ private class Labels(val consequents: Ls[(Term, LabelSymbol)], val matchError: Opt[LabelSymbol]): private val map = consequents.toMap inline def isEmpty: Bool = consequents.isEmpty && matchError.isEmpty inline def get(term: Term): Opt[LabelSymbol] = map.get(term) /** * Subtyping relations used in normalization and coverage checking. */ def compareCasePattern(lhs: FlatPattern, rhs: FlatPattern)(using ctx: Elaborator.Ctx): Bool = import FlatPattern.*, ctx.builtins as blt (lhs, rhs) match // `Object` is the supertype of all (non-virtual) classes and modules. case (ClassLike(_, cs: ClassSymbol, _, _), ClassLike(symbol = blt.`Object`)) if !ctx.builtins.virtualClasses.contains(cs) => true // Class and module are subtypes of `Object`. case (ClassLike(_, cs: ModuleOrObjectSymbol, _, _), ClassLike(symbol = blt.`Object`)) => true case (Tuple(n1, false), Tuple(n2, false)) if n1 === n2 => true case (Tuple(n1, _), Tuple(n2, true)) if n2 <= n1 => true // Note: We don't make Int31 compatible with Num, since Int31 needs to know how it should be // sign-extended in order to convert into a Num. case (ClassLike(symbol = blt.`Int`), ClassLike(symbol = blt.`Num`)) => true // TODO(Derppening): Do we limit IntLit to (1 << 31) - 1 for `Int31`? case (Lit(Tree.IntLit(_)), ClassLike(symbol = blt.`Int` | blt.`Int31` | blt.`Num`)) => true case (Lit(Tree.StrLit(_)), ClassLike(symbol = blt.`Str`)) => true case (Lit(Tree.DecLit(_)), ClassLike(symbol = blt.`Num`)) => true case (Lit(Tree.BoolLit(_)), ClassLike(symbol = blt.`Bool`)) => true case (Record(entries1), Record(entries2)) => entries1.forall { (fieldName1, _) => entries2.exists { (fieldName2, _) => fieldName1 === fieldName2 } } case (Record(entries), rhs: ClassLike) => val clsParams = rhs.constructor.symbol.flatMap(_.asCls) match case S(symbol) => symbol.defn match case S(ClassDef.Parameterized(params = paramList)) => paramList.params case S(_) | N => Nil case (S(_) | N) => Nil entries.forall { (fieldName, _) => clsParams.exists { case Param(flags = FldFlags(isVal = isVal), sym = sym) => isVal && fieldName === sym.id }} // Check user-defined class hierarchy via extends clauses. case (ClassLike(_, lhsSym, _, _), ClassLike(_, rhsSym, _, _)) => isSubclassOf(lhsSym, rhsSym) case (_: FlatPattern, _: FlatPattern) => false /** * Check if two patterns are provably disjoint, i.e., no value can match both. * This is used to safely eliminate branches during specialization. * Returns `true` for clear-cut cases (e.g., different literals, * incompatible tuple sizes, sibling classes under single inheritance). */ def areProvablyDisjoint(lhs: FlatPattern, rhs: FlatPattern)(using ctx: Elaborator.Ctx): Bool = import FlatPattern.* (lhs, rhs) match case (Lit(l1), Lit(l2)) => !(l1 === l2) case (Tuple(n1, false), Tuple(n2, false)) => n1 =/= n2 case (Tuple(n1, true), Tuple(n2, false)) => n2 < n1 case (Tuple(n1, false), Tuple(n2, true)) => n1 < n2 case (Lit(_), _: ClassLike) => !compareCasePattern(lhs, rhs) case (_: ClassLike, Lit(_)) => !compareCasePattern(rhs, lhs) case (Lit(_), Tuple(_, _)) | (Tuple(_, _), Lit(_)) => true case (Record(_), Lit(_)) | (Lit(_), Record(_)) => true case (Record(_), Tuple(_, _)) | (Tuple(_, _), Record(_)) => true // Under the single-inheritance restriction, two classes where neither is a // subclass of the other are provably disjoint. When we add matchable // class-like things with multiple inheritance (e.g., interfaces), this check // will need to be refined. case (ClassLike(_, lhsSym, _, _), ClassLike(_, rhsSym, _, _)) => !isSubclassOf(lhsSym, rhsSym) && !isSubclassOf(rhsSym, lhsSym) case _ => false /** Get the parent class-like symbol from the extends clause of a class or module. */ private def getParentClassLikeSymbol(sym: ClassSymbol | ModuleOrObjectSymbol) : Opt[ClassSymbol | ModuleOrObjectSymbol] = val ext: Opt[Term.New] = sym match case cls: ClassSymbol => cls.defn.flatMap(_.ext) case mod: ModuleOrObjectSymbol => mod.defn.flatMap(_.ext) ext.flatMap(nw => nw.cls.symbol.flatMap(_.asClsOrMod)) /** Check if `child` is a subclass of `parent` by traversing the class hierarchy. * Uses a visited set to avoid infinite loops in case of cyclic inheritance. */ private def isSubclassOf( child: ClassSymbol | ModuleOrObjectSymbol, parent: ClassSymbol | ModuleOrObjectSymbol ): Bool = def go(sym: ClassSymbol | ModuleOrObjectSymbol, visited: Set[ClassSymbol | ModuleOrObjectSymbol]): Bool = !visited.contains(sym) && (getParentClassLikeSymbol(sym) match case S(parentSym) => parentSym === parent || go(parentSym, visited + sym) case N => false) go(child, Set.empty) final case class VarSet(declared: Set[BlockLocalSymbol]): def +(nme: BlockLocalSymbol): VarSet = copy(declared + nme) infix def has(nme: BlockLocalSymbol): Bool = declared.contains(nme) def showDbg: Str = declared.iterator.mkString("{", ", ", "}") object VarSet: def apply(): VarSet = VarSet(Set()) /** Specialization mode */ enum Mode: case + case - ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/SplitElaborator.scala ================================================ package hkmc2 package semantics package ucs import mlscript.utils.*, shorthands.* import syntax.{Keyword, Tree}, Tree.* import Keyword.{`and`, `do`, `else`, `if`, `case`, `while`, `is`, `let`, `or`, `then`} import Elaborator.{Ctx, Ctxl, UnderCtx, ctx}, SimpleSplit.* import Message.MessageContext import collection.mutable.SortedSet import utils.TL import scala.annotation.tailrec object SplitElaborator: /** A scrutinee is a function that returns a reference to the symbol. */ type Reference = () => Term.Ref type Connective = `do`.type | `then`.type import SplitElaborator.* trait SplitElaborator: self: Elaborator => import tl.* private given TL = tl private given Ordering[Loc] = Ordering.by(l => (l.spanStart, l.spanEnd)) /** Keep track of the locations where `do` and `then` are used as connectives. */ private var kwLocSets = (SortedSet.empty[Loc], SortedSet.empty[Loc]) private def reportInconsistentConnectives(kw: Keywrd[Keyword.SplitLike]): Unit = (kwLocSets._1.headOption, kwLocSets._2.headOption) match case (Some(doLoc), Some(thenLoc)) => raise(ErrorReport( msg"Mixed use of `do` and `then` in the `${kw.kw.name}` expression." -> kw.toLoc :: msg"Keyword `then` is used here." -> S(thenLoc) :: msg"Keyword `do` is used here." -> S(doLoc) :: Nil )) case _ => () private def topmostDefault: SimpleSplit = if kwLocSets._1.nonEmpty then Else(Term.UnitVal())(N) else End private object `~>`: infix def unapply(tree: Tree): Opt[(Tree, Tree \/ (Keywrd[Connective], Tree))] = tree match case InfixApp(lhs, Keywrd(`and`), rhs) => S((lhs, L(rhs))) case InfixApp(lhs, kwTree @ Keywrd(kw: `then`.type), rhs) => kwLocSets._2 ++= kwTree.toLoc S((lhs, R((new Keywrd(kw).withLocOf(kwTree), rhs)))) case InfixApp(lhs, kwTree @ Keywrd(kw: `do`.type), rhs) => kwLocSets._1 ++= kwTree.toLoc S((lhs, R((new Keywrd(kw).withLocOf(kwTree), rhs)))) case _ => N private def withScopedConnectives (kw: Keywrd[Keyword.SplitLike])(evaluate: => SimpleSplit): (IfLikeForm, SimpleSplit) = val savedKwLocSets = kwLocSets kwLocSets = (SortedSet.empty, SortedSet.empty) val split = evaluate val (result, form) = kw.kw match case `if` | `case` => (split ~~: topmostDefault, if kwLocSets._1.nonEmpty then IfLikeForm.ImperativeIf else IfLikeForm.ReturningIf) case `while` => (split, IfLikeForm.While) reportInconsistentConnectives(kw) kwLocSets = savedKwLocSets (form, result) /** Transform trees into a UCS split. */ protected def split(t: IfLike): Ctxl[Term.IfLike] = val (form, split) = withScopedConnectives(t.kw): t.split match case block: Block => termSplit(block.desugStmts, identity) case other: Tree => termSplit(Ls(other), identity) new Term.IfLike(t.kw.kw, form, split).withLocOf(t) /** Elaborate `case` expressions */ protected def caseSplit(scrut: VarSymbol, tree: Case): Ctxl[Term.IfLike] = val (form, split) = withScopedConnectives(tree.kw): patternBranch(() => scrut.ref(), tree.branches, identity) new Term.IfLike(tree.kw.kw, form, split).withLocOf(tree) /** Elaborate shorthand expressions. */ protected def shorthandSplit(tree: Tree)(using UnderCtx): Ctxl[SimpleSplit] = val affirmative = Else(Term.Lit(BoolLit(true)))(N) val negative = Else(Term.Lit(BoolLit(false)))(N) val (scrutinee, pattern) :: matches = disaggregate(tree) subterm(scrutinee).reference: scrutinee => lazy val innerSplit: Ctxl[SimpleSplit] = expandMatches(matches)(affirmative) pattern match case Block(Nil) => val recordPattern = Pattern.Record(Nil).withLocOf(pattern) Head.Match(scrutinee(), recordPattern, innerSplit) ~: negative case Block(trees) => trees.foldRight(negative): case (pattern, alternative) => Head.Match(scrutinee(), self.pattern(pattern), innerSplit) ~: alternative case _ => val firstPattern = self.pattern(pattern) firstPattern.variables.report (ctx ++ firstPattern.variables.allocate).givenIn: Head.Match(scrutinee(), firstPattern, innerSplit) ~: negative /** Desugar a list of trees as a term split. The returned function takes a * function, which takes a `Ctx` and returns a `SimpleSplit` representing * the _alternative_ split, and returns a `SimpleSplit` representing the * split of the given trees. */ private def termSplit(ts: Ls[Tree], mk: Term => Term): Ctxl[SimpleSplit] = val (_, splits) = ts.foldLeft((ctx, Ls[SimpleSplit]())): case ((curCtx, splits), t) => termBranch(t, mk)(using curCtx).mapSecond(_ :: splits) concatenate(splits) /** Concatenate a sequence of splits and report warning for splits that come * after a split which ends with an `else` branch. */ private def concatenate(splits: Ls[SimpleSplit]): SimpleSplit = // The first element is a list of branches. The second element is // - `N` if no `else` branch has been found; or // - `S((default, unreachables))` if `default` is the first `else` branch // in the split and all splits thereafter will be added to `unreachables`. val z: (Ls[Head], Opt[(Else, Ls[SimpleSplit])]) = (Nil, N) val (reachables, elseRest) = splits.reverseIterator.foldLeft(z): // This is the case when we haven't found an `else` branch yet. case ((branches, N), split) => @tailrec def go(acc: Ls[Head], split: SimpleSplit): (Ls[Head], Opt[Else]) = split match case Cons(branch, tail) => go(branch :: acc, tail) case els: Else => (acc, S(els)) case End => (acc, N) go(branches, split).mapSecond(_.map(_ -> (Nil: Ls[SimpleSplit]))) case ((branches, S((default, unreachables))), split) => (branches, S((default, split :: unreachables))) // Report unreachables splits. elseRest match case S((default, unreachables)) => val messages = unreachables.reverseIterator.map: split => msg"This branch is unreachable." -> split.toLoc .toList if messages.nonEmpty then raise(WarningReport((msg"This catch-all clause makes the following branches unreachable." -> default.toLoc :: messages))) case N => () // Reconstruct the split from the reachable `heads`. reachables.foldLeft(elseRest.fold(SimpleSplit.End)(_._1)): case (innerSplit, branch) => branch ~: innerSplit /** Handle the common cases of branches in splits. */ private def branch(using Ctx): Cfg[PartialFunction[Tree, (Ctx, SimpleSplit)]] = // Interleaved-`let` bindings like `{ x is A then 0; let x = 1; ... }`. case LetLike(Keywrd(`let`), ident: Ident, S(rhsTree), N) => val symbol = VarSymbol(ident) val head = Head.Let(symbol, term(rhsTree)) ((ctx + (ident.name -> symbol)), head ~: End) // Interleaved-`do` statements like `{ x is A then 0; do log(1); ... }`. case PrefixApp(Keywrd(`do`), rhsTree) => (ctx, Head.Let(TempSymbol(N, "unused"), term(rhsTree)) ~: End) // Although the `else`-clause marks the end of the split, we cannot // stop and still have to elaborate the remaining trees. case PrefixApp(kwTree @ Keywrd(`else`), elseTree) => (ctx, Else(term(elseTree))(S(new Keywrd(`else`).withLocOf(kwTree)))) private def expandMatches(matchesTree: Ls[TT])(consequent: Ctxl[SimpleSplit]): Ctxl[SimpleSplit] = val z = (ctx, Ls[(Term, Pattern)]()) // Elaborate the term and the pattern in each match. val (innerCtx, matches) = matchesTree.foldLeft(z): case ((curCtx, matches), (scrutineeTree, patternTree)) => val scrutinee = term(scrutineeTree)(using curCtx) val pattern = self.pattern(patternTree)(using curCtx) pattern.variables.report val resCtx = curCtx ++ pattern.variables.allocate (resCtx, (scrutinee, pattern) :: matches) // As `matches` is reversed, we should process it from the left. val split = matches.foldLeft(consequent(using innerCtx)): case (innerSplit, (scrutinee, pattern)) => scrutinee.reference: scrutineeRef => Head.Match(scrutineeRef(), pattern, innerSplit) ~: End split private def termBranch(t: Tree, mk: Term => Term): Ctxl[(Ctx, SimpleSplit)] = branch.appOrElse(t): case block: Block => (ctx, termSplit(block.desugStmts, mk)) case lhs is rhs => (ctx, mk(term(lhs)).reference(patternBranch(_, rhs, identity))) // Several matches followed by `and`, `do`, or `then`. case matchesTree ~> consequent => val (coda, patternTree) :: matches = disaggregate(matchesTree) def innerSplit(using ctx: Ctx) = expandMatches(matches): consequent match case L(tree) => termSplit(Ls(tree), identity) case R((kw, tree)) => Else(term(tree))(S(kw)) val split = coda match case Under() => innerSplit case coda => mk(term(coda)).reference: scrutinee => val pattern = self.pattern(patternTree) val innerCtx = ctx ++ pattern.variables.allocate Head.Match(scrutinee(), pattern, innerSplit(using innerCtx)) ~: End (ctx, split) // Handle splits on binary operators. case OpApp(lhs, ident: Ident, rhss) => val op = term(ident) val split = term(lhs).reference: lhs => val mk2 = (rhs: Term) => val args = Term.Tup(PlainFld(lhs()) :: PlainFld(rhs) :: Nil)(DummyTup) Term.App(op, args)(Tree.DummyApp, N, FlowSymbol("‹operator-split›")) termSplit(rhss, mk2 andThen mk) (ctx, split) case OpSplit(lhs, rhss) => val split = mk(term(lhs)).reference: lhs => val (_, splits) = rhss.foldLeft((ctx, Ls[SimpleSplit]())): case ((curCtx, splits), t) => operatorBranch(lhs, t)(using curCtx).mapSecond(_ :: splits) concatenate(splits) (ctx, split) // Unrecognized term split. case _ => error(msg"Unrecognized term split (${t.describe})" -> t.toLoc) (ctx, End) private def operatorBranch(scrutinee: Reference, rhs: Tree): Ctxl[(Ctx, SimpleSplit)] = branch.appOrElse(rhs): rhsTree => termBranch(rhsTree.splitOn(Trm(scrutinee())), identity) private def patternBranch(scrutinee: Reference, t: Tree, mk: Tree => Tree): Ctxl[SimpleSplit] = t match case block: Block => val (_, splits) = block.desugStmts.foldLeft((ctx, Ls[SimpleSplit]())): case ((curCtx, splits), t) => branch(using curCtx).lift(t).getOrElse: (curCtx, patternBranch(scrutinee, t, mk)(using curCtx)) .mapSecond(_ :: splits) concatenate(splits) case App(ctor: Ctor, Tup(rhss)) => val nl = (t: Tree) => mk(App(ctor, Tup(t :: Nil))) patternBranch(scrutinee, Block(rhss), nl) case Annotated(annotation, target) => patternBranch(scrutinee, target, Annotated(annotation, _) |> mk) case patternAndMatches ~> consequentTree => val (firstPatternTree, _) :: matches = disaggregate(patternAndMatches) val firstPattern = self.pattern(mk(firstPatternTree)) firstPattern.variables.report (ctx ++ firstPattern.variables.allocate).givenIn: val split = expandMatches(matches): consequentTree match case L(tree) => termSplit(Ls(tree), identity) case R((kw, tree)) => Else(term(tree))(S(kw)) Head.Match(scrutinee(), firstPattern, split) ~: End case _ => error(msg"Unrecognized pattern split (${t.describe})." -> t.toLoc) Else(Term.Error)(N).withLocOf(t) // To inspect the source of errors. extension (term: Term) private inline def reference(continuation: Reference => SimpleSplit): SimpleSplit = term match // If the term is already a reference, we can re-reference its symbol. case Term.Ref(symbol) => continuation(() => symbol.ref()) // Otherwise, we need to create a temporary symbol holding the term. case term: Term => val symbol = TempSymbol(N, "scrut") Head.Let(symbol, term) ~: continuation(() => symbol.ref()) private type TT = (Tree, Tree) /** Decompose a `Tree` of conjunct matches. The tree is from the same line in * the source code and followed by a `then`, or `and` with a continued line. * A formal definition of the conjunction is: * * ```bnf * conjunction ::= conjunction `and` conjunction # conjunction * | term `is` pattern # pattern matching * | term # Boolean condition * ``` * * Each match is represented by a pair of a _coda_ and a _pattern_ that is * yet to be elaborated. For boolean conditions, the pattern is a `BoolLit`. * * This function does not invoke elaboration and the implementation utilizes * functional lists to avoid calling the `reverse` method on the output, * which returns type `List[T]` instead of `::[T]`. See paper _A Novel * Representation of Lists and Its Application to the Function_ for details. * * @param tree the tree to desugar * @return a non-empty list of scrutinee and pattern pairs represented in * type `::[T]` (instead of `List[T]`) so that the head element * can be retrieved in a type-safe manner */ private def disaggregate(tree: Tree): ::[TT] = def go(tree: Tree, acc: TT => ::[TT]): () => ::[TT] = tree match case lhs `and` rhs => go(lhs, ::(_, go(rhs, acc)())) case lhs `or` rhs => error(msg"Logical `or` is not yet supported." -> tree.toLoc) go(lhs, ::(_, go(rhs, acc)())) // FIXME: this is currently copy-pasted from the `and` case case scrut `is` pat => () => acc((scrut, pat)) case test => () => acc((test, Tree.BoolLit(true))) go(tree, ::(_, Nil))() ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala ================================================ package hkmc2 package semantics package ucs import mlscript.utils.*, shorthands.* import syntax.Tree, Tree.*, Elaborator.{Ctx, State, ctx} /** This trait includes some helpers for synthesizing `Term`s which look like * they have already been processed by the `Resolver`. Its methods should only * be called in stages after the `Resolver`. Currently, its derived classes are * `Normalization`, `Compiler`, and `SplitCompiler`. */ trait TermSynthesizer(using State): protected final def sel(p: Term, k: Ident): Term.SynthSel = (Term.SynthSel(p, k)(N, FlowSymbol.synthSel(k.name), N, N): Term.SynthSel).resolve protected final def sel(p: Term, k: Ident, s: MemberSymbol): Term.SynthSel = (Term.SynthSel(p, k)(S(s), FlowSymbol.synthSel(k.name), N, N): Term.SynthSel).resolve protected final def sel(p: Term, k: Str): Term.SynthSel = sel(p, Ident(k): Ident) protected final def sel(p: Term, k: Str, s: MemberSymbol): Term.SynthSel = sel(p, Ident(k): Ident, s) protected final def int(i: Int) = Term.Lit(IntLit(BigInt(i))) protected final def str(s: Str) = Term.Lit(StrLit(s)) protected final def `null` = Term.Lit(UnitLit(true)) protected final def fld(t: Term) = Fld(FldFlags.empty, t, N) protected final def tup(xs: List[Term]): Term.Tup = Term.Tup(xs.iterator.map(fld).toList)(DummyTup) protected final def tup(xs: Fld*): Term.Tup = Term.Tup(xs.toList)(DummyTup) protected final def app(l: Term, r: Term, label: Str): Term.App = app(l, r, FlowSymbol(label)) protected final def app(l: Term, r: Term, s: FlowSymbol): Term.App = (Term.App(l, r)(App(Dummy, Dummy), N, s): Term.App).resolve protected final def `new`(cls: Term, args: Ls[Term], label: Str): Term.New = Term.New(cls, args, N)(N) protected final def rcd(fields: RcdField*): Term.Rcd = Term.Rcd(false, fields.toList) protected final def splitLet(sym: BlockLocalSymbol, term: Term)(inner: Split): Split = Split.Let(sym, term, inner) protected final def param = Param(FldFlags.empty, _, N, Modulefulness.none) protected final def paramList(params: Param*) = PlainParamList(params.toList) private lazy val runtimeRef: Term.Ref = State.runtimeSymbol.ref().resolve /** Make a term that looks like `runtime.MatchSuccess` with its symbol. */ protected lazy val matchSuccessClass = sel(runtimeRef, "MatchSuccess", State.matchSuccessClsSymbol).resolved(State.matchSuccessClsSymbol) /** Make a pattern that looks like `runtime.MatchSuccess.class`. */ protected def matchSuccessPattern(parametersOpt: Opt[Ls[BlockLocalSymbol]]): FlatPattern.ClassLike = val parameters = parametersOpt.map(_.map(_ -> N)) FlatPattern.ClassLike(matchSuccessClass, State.matchSuccessClsSymbol, parameters, false)(Tree.Dummy) /** Make a term that looks like `runtime.MatchFailure` with its symbol. */ protected lazy val matchFailureClass = sel(runtimeRef, "MatchFailure", State.matchFailureClsSymbol).resolved(State.matchFailureClsSymbol) /** Make a pattern that looks like `runtime.MatchFailure.class`. */ protected def matchFailurePattern(parametersOpt: Opt[Ls[BlockLocalSymbol]]): FlatPattern.ClassLike = val parameters = parametersOpt.map(_.map(_ -> N)) FlatPattern.ClassLike(matchFailureClass, State.matchFailureClsSymbol, parameters, false)(Tree.Dummy) protected lazy val tupleSlice = sel(sel(runtimeRef, "Tuple"), "slice", State.tupleSliceSymbol) protected lazy val tupleLazySlice = sel(sel(runtimeRef, "Tuple"), "lazySlice", State.tupleLazySliceSymbol) protected lazy val tupleGet = sel(sel(runtimeRef, "Tuple"), "get", State.tupleGetSymbol) protected lazy val stringStartsWith = sel(sel(runtimeRef, "Str"), "startsWith", State.strStartsWithSymbol) protected lazy val stringGet = sel(sel(runtimeRef, "Str"), "get", State.strGetSymbol) protected lazy val stringTake = sel(sel(runtimeRef, "Str"), "take", State.strTakeSymbol) protected lazy val stringLeave = sel(sel(runtimeRef, "Str"), "leave", State.strLeaveSymbol) /** Make a term that looks like `runtime.Tuple.get(t, i)`. */ protected final def callTupleGet(t: Term, i: Int, label: Str): Term = callTupleGet(t, i, FlowSymbol(label)) /** Make a term that looks like `runtime.Tuple.slice(t, i)`. */ protected final def callTupleGet(t: Term, i: Int, s: FlowSymbol): Term = app(tupleGet, tup(fld(t), fld(int(i))), s) /** Make a term that looks like `runtime.Tuple.slice(t, i, j)`. */ protected final def callTupleSlice(t: Term, i: Int, j: Int, label: Str): Term = app(tupleSlice, tup(fld(t), fld(int(i)), fld(int(j))), label) /** Make a term that looks like `runtime.Str.startsWith(t, p)`. */ protected final def callStringStartsWith(t: Term.Ref, p: Term, label: Str) = app(stringStartsWith, tup(fld(t), fld(p)), label) /** Make a term that looks like `runtime.Str.get(t, i)`. */ protected final def callStringGet(t: Term.Ref, i: Int, label: Str) = app(stringGet, tup(fld(t), fld(int(i))), label) /** Make a term that looks like `runtime.Str.drop(t, n)`. */ protected final def callStringTake(t: Term.Ref, n: Int, label: Str) = app(stringTake, tup(fld(t), fld(int(n))), label) /** Make a term that looks like `runtime.Str.drop(t, n)`. */ protected final def callStringDrop(t: Term.Ref, n: Int, label: Str) = app(stringLeave, tup(fld(t), fld(int(n))), label) protected final def tempLet(dbgName: Str, term: Term)(inner: TempSymbol => Split): Split = val s = TempSymbol(N, dbgName) Split.Let(s, term, inner(s)) protected final def plainTest(cond: Term, dbgName: Str = "cond")(inner: => Split): Split = val s = TempSymbol(N, dbgName) Split.Let(s, cond, Branch(s.safeRef, inner) ~: Split.End) protected final def makeBindings(fields: Ls[RcdField | RcdSpread]): Term = if fields.isEmpty then `null` else Term.Rcd(false, fields) protected final def makeMatchSuccess(output: Term) = `new`(matchSuccessClass, tup(fld(output), fld(`null`)) :: Nil, "result of `MatchSuccess`") protected final def makeMatchSuccess(output: Term, bindings: Term) = `new`(matchSuccessClass, tup(fld(output), fld(bindings)) :: Nil, "result of `MatchSuccess`") protected final def makeMatchSuccess(output: Term, fields: Ls[RcdField | RcdSpread]) = `new`(matchSuccessClass, tup(fld(output), fld(makeBindings(fields))) :: Nil, "result of `MatchSuccess`") protected final def makeMatchFailure(errors: Term = Term.Lit(UnitLit(true))) = `new`(matchFailureClass, tup(fld(errors)) :: Nil, "result of `MatchFailure`") /** Make a `Branch` that calls `Pattern` symbols' `unapply` functions. */ def makeLocalPatternBranch( scrut: => Term.Ref, localPatternSymbol: BlockLocalSymbol, inner: => Split, )(fallback: Split): Split = val call = app(localPatternSymbol.safeRef, tup(fld(scrut)), s"result of ${localPatternSymbol.nme}") tempLet("matchSuccess", call): resultSymbol => Branch(resultSymbol.safeRef, matchSuccessPattern(N), inner) ~: fallback protected final def makeTupleBranch( scrut: => Term.Ref, subScrutinees: Ls[BlockLocalSymbol], consequent: => Split, alternative: Split ): Split = Branch(scrut, FlatPattern.Tuple(subScrutinees.size, false), subScrutinees.iterator.zipWithIndex.foldRight(consequent): case ((arg, index), innerSplit) => val label = s"the $index-th element of the match result" Split.Let(arg, callTupleGet(scrut, index, label), innerSplit) ) ~: alternative protected final def makeTupleBranch( scrut: => Term.Ref, leading: Ls[BlockLocalSymbol], spread: BlockLocalSymbol, trailing: Ls[BlockLocalSymbol], consequent: => Split, alternative: Split ): Split = val split0 = trailing.iterator.zipWithIndex.foldRight(consequent): case ((arg, index), innerSplit) => val reverseIndex = trailing.size - index val label = s"the last $reverseIndex-th element of the tuple" Split.Let(arg, callTupleGet(scrut, -reverseIndex, label), innerSplit) val split1 = Split.Let(spread, callTupleSlice( scrut, leading.size, trailing.size, "the middle part of the tuple"), split0) val split2 = leading.iterator.zipWithIndex.foldRight(split1): case ((arg, index), innerSplit) => val label = s"the first ${index + 1}-th element of the tuple" Split.Let(arg, callTupleGet(scrut, index, label), innerSplit) Branch(scrut, FlatPattern.Tuple(leading.size + trailing.size, true), split2) ~: alternative ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/package.scala ================================================ package hkmc2 package semantics import sourcecode.{FileName, Line, Name} import syntax.{Keyword, Tree}, Tree.{Ident, InfixApp, Keywrd, Sel, SynthSel} import mlscript.utils.*, shorthands.* package object ucs: def error(using Line, FileName, Name, Raise)(msgs: (Message, Option[Loc])*): Unit = raise(ErrorReport(msgs.toList)) def warn(using Line, FileName, Name, Raise)(msgs: (Message, Option[Loc])*): Unit = raise(WarningReport(msgs.toList)) def bug(using Line, FileName, Name, Raise)(msgs: (Message, Option[Loc])*): Unit = raise(InternalError(msgs.toList)) extension (symbol: Symbol) /** Create a `Ref` that does not have any implicit arguments. We need this * function because we generate a lot of `Ref`s after implicit resolution. * Writing `.resolve` is too verbose. */ def safeRef: Term.Ref = symbol.ref().resolve extension (op: Keyword.Infix) infix def unapply(tree: Tree): Opt[(Tree, Tree)] = tree match case InfixApp(lhs, Keywrd(`op`), rhs) => S((lhs, rhs)) case _ => N type Ctor = SynthSel | Sel | Ident /** A helper extractor for matching the tree of `x | y`. */ object extractors: import Tree.OpApp /** A helper extractor for matching the tree of `x ..= y` and `x ..< y`. * The Boolean value indicates whether the range is inclusive. */ object to: infix def unapply(tree: Tree): Opt[(Tree, (Bool, Tree))] = tree match case OpApp(lhs, Ident("..="), rhs :: Nil) => S(lhs, (true, rhs)) case OpApp(lhs, Ident("..<"), rhs :: Nil) => S(lhs, (false, rhs)) case _ => N end ucs ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala ================================================ package hkmc2 package semantics package ups import mlscript.utils.*, shorthands.* import syntax.{Keyword, LetBind, Tree}, Tree.{BoolLit, DecLit, Ident, IntLit, StrLit, UnitLit} import Term.{Blk, Rcd, Ref, SynthIf, SynthSel} import Pattern.{Instantiation, Head} import Elaborator.{Ctx, State, ctx}, utils.TL import ucs.{TermSynthesizer, FlatPattern, safeRef} import Message.MessageContext, ucs.error import collection.mutable.{Queue, Map as MutMap}, collection.immutable.{Set, Map} import scala.annotation.tailrec /** The compiler for pattern definitions. It compiles instantiated patterns into * a few matcher functions. Each matcher function matches a set of patterns * and returns a record that contains the results of each pattern. */ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends TermSynthesizer: import Compiler.*, tl.* import Pattern.* /** A previously-computed matcher result for one field of the current * multi-matcher. The runtime representation is shape-dependent: * singleton-label matchers return the label's value directly, while * multi-label matchers return a record keyed by label field names. */ private final case class MatcherResult(symbol: VarSymbol, labels: Set[Label]): private def result: Term = sel(symbol.safeRef, "result") def input: Term = sel(symbol.safeRef, "input") /** Read the result for one label from this matcher result, abstracting over * the singleton direct-return optimization. */ def select(label: Label): Term = if labels.size is 1 then result else sel(result, label.asFieldName) /** Produce the default failure value for this matcher result with the same * shape that a successful submatcher call would have produced. */ def default(using ResultMode): Term = val result = labels.toList match case label :: Nil => emptyMatchResult("empty") case labels => Rcd(false, labels.map: label => RcdField(str(label.asFieldName), emptyMatchResult("empty"))) rcd( RcdField(str("input"), `null`), RcdField(str("result"), result) ) private def bool(value: Bool): Term = Term.Lit(BoolLit(value)) private def isMatchOnly(using mode: ResultMode): Bool = mode is ResultMode.MatchOnly private def emptyMatchResult(reason: Str)(using mode: ResultMode): Term = if isMatchOnly then bool(false) else makeMatchFailure(str(reason)) private def nullifyEmptyBindings(bindings: Term): Term = bindings match case Rcd(false, Nil) => `null` case bindings => bindings extension (label: Label) /** This decides the the field name of each label in the match record. */ def asFieldName: Str = s"p_$label" extension (pattern: Pat) /** Get or create a label for the pattern. */ def label: Label = labelMap.getOrElseUpdate(pattern, labelMap.size) extension (field: Ident | Int) def showDbg: Str = field match case id: Ident => id.name case index: Int => s"p_$index" /** Convert the field name to an `Ident`. */ def asIdent: Ident = field match case id: Ident => new Ident(id.name) case index: Int => Ident(index.toString) extension (head: Head) /** Create a flat pattern that can be used in the UCS expressions. */ def toFlatPattern: FlatPattern = head match case lit: syntax.Literal => FlatPattern.Lit(lit) case sym: (ClassSymbol | ModuleOrObjectSymbol) => val constructor = reference(sym, head.toLoc).getOrElse(Term.Error) FlatPattern.ClassLike(constructor, sym, N, false)(Tree.Dummy) def showDbg: Str = head match case lit: syntax.Literal => lit.idStr case sym: ClassLikeSymbol => sym.nme extension (patterns: Set[(Label, ExPat)]) /** Specialize a set of patterns. Also display them in the debug log. */ def specializeSet(head: Opt[Head]): Set[(Label, SpPat)] = log(s"Specialized patterns of ${head.fold("the case without tags")(_.showDbg)}:") patterns.map: (label, pattern) => val spec = pattern.specialize(head) val simp = spec.simplify log(s"• Label $label") log(s" ‣ Expanded: ${spec.showDbg}") log(s" ‣ Simplified: ${simp.showDbg}") (label, simp) val labelMap: MutMap[Pat, Label] = MutMap() val multiMatchers: MutMap[Set[Label], BlockLocalSymbol] = MutMap.empty /** The built multi-matcher functions. */ val implementations: MutMap[BlockLocalSymbol, (ParamList, Term)] = MutMap.empty val buildQueue: Queue[(BlockLocalSymbol, Set[Pat])] = Queue.empty /** Build a matcher function that matches a single pattern. This function * should be applied to the pattern that is considered as the entry point.*/ def buildMatcher(pattern: Pat, resultMode: ResultMode): (BlockLocalSymbol, Ls[Implementation]) = scoped("ucs:compiler"): given ResultMode = resultMode val entryPointSymbol = buildMultiMatcher(Set(pattern)) while buildQueue.nonEmpty do val (symbol, patterns) = buildQueue.dequeue() implementations += (symbol -> buildMultiMatcherBody(patterns)) entryPointSymbol -> implementations.iterator.map: case (symbol, (paramList, term)) => (symbol, paramList, term) .toList /** Build a multi-matcher function and returns the local symbol that we can * use to call it. The built function definition is stored in the map * `implementations`. */ def buildMultiMatcher(patterns: Set[Pat])(using ResultMode): BlockLocalSymbol = // Get or create the label for each pattern. Multi-matchers are identified // by the set of labels (orders are not important). val labels = patterns.map(_.label) multiMatchers.get(labels).getOrElse: val f = TempSymbol(N, makeMultiMatcherName(patterns)) multiMatchers += (labels -> f) buildQueue enqueue (f -> patterns) f // Return the symbol of the built function. /** Build the body of a multi-matcher function. The memoization is done by * `buildMultiMatcher`. */ def buildMultiMatcherBody(patterns: Set[Pat])(using ResultMode): (ParamList, Term) = trace( pre = s"buildMultiMatcherBody: ${ patterns.iterator.map: pattern => s"${pattern.showDbg} => ${pattern.label}" .mkString("{", ", ", "}")}" ): val expandedPatterns = patterns.map(p => (p.label, p.expand(Set.empty))) val heads = expandedPatterns.flatMap((_, p) => p.heads).toList // This is the parameter of the current multi-matcher. val scrutinee = VarSymbol(Ident("input")) // Assemble branches for constructors and literals. val branches = heads.map: head => // Weird. Removing type annotations caused type errors. val specialized = expandedPatterns.specializeSet(S(head)) val consequent = Split.Else(multiMatcherBranch(specialized, scrutinee)) Branch(scrutinee.safeRef, head.toFlatPattern, consequent) // Assemble the default branch. val default = // Weird. Removing type annotations caused type errors. val specialized = expandedPatterns.specializeSet(N) Split.Else(multiMatcherBranch(specialized, scrutinee)) // Make a split that tries all branches in order. val topmostSplit = branches.foldRight(default)(_ ~: _) val bodyTerm = SynthIf(topmostSplit) log(s"Multi-matcher body:\n${topmostSplit.prettyPrint}") (paramList(param(scrutinee)), bodyTerm) def multiMatcherBranch( patterns: Set[(Label, SpPat)], scrutinee: BlockLocalSymbol )(using ResultMode): Blk = trace( pre = s"multiMatcherBranch: scrutinee = ${scrutinee} | patterns = ${ patterns.iterator.map: (label, pattern) => s"${pattern.showDbg} => ${label}" .mkString("{", ", ", "}")}" ): val fields = patterns.flatMap((_, p) => p.fields) log(s"fields: ${fields.iterator.map(_.showDbg).mkString("{", ", ", "}")}") val subPatternsByField = Map.from(fields.map: field => field -> patterns.flatMap((_, p) => p.collectSubPatterns(field))) val subScrutinees = Map.from(subPatternsByField.map: (field, subPatterns) => field -> MatcherResult(VarSymbol(field.asIdent), subPatterns.map(_.label))) // Let bindings that bind the sub-scrutinee to the result of each matcher. val bindings = subPatternsByField.iterator.flatMap: (field, subPatterns) => val subScrutinee = subScrutinees(field) log(s"subPattern for field ${field.showDbg}: ${ subPatterns.iterator.map(_.showDbg).mkString("{", ", ", "}")}") val subMatcherSymbol = buildMultiMatcher(subPatterns) val conditional = // Check the presence of the field, and call the matcher if it exists. val fieldIdent: Ident = field.asIdent val fieldSymbol = TempSymbol(N, fieldIdent.name) val fieldTest = FlatPattern.Record((fieldIdent -> fieldSymbol) :: Nil) val consequent = Split.Else: val resultTerm = app(subMatcherSymbol.safeRef, tup(fld(fieldSymbol.safeRef)), "result") rcd( RcdField(str("input"), fieldSymbol.safeRef), RcdField(str("result"), resultTerm) ) val branch = Branch(scrutinee.safeRef, fieldTest, consequent) SynthIf(branch ~: Split.Else(subScrutinee.default)) LetDecl(subScrutinee.symbol, Nil) :: DefineVar(subScrutinee.symbol, conditional) :: Nil .toList // For each pattern, we compile a split and bind the result to a variable. // The variable will be a field of the output record. val z = (Nil: Ls[Statement], Nil: Ls[(Label, Term)]) val (tests, resultTerms) = patterns.iterator.foldLeft(z): case ((stmts, results), (label, pattern)) => val symbol = TempSymbol(N, label.asFieldName + "$") val makeSplit = completePattern(pattern, scrutinee, subScrutinees, Nil) val split = makeSplit( // There is no topmost transform here, so we emit the direct success // value: `MatchSuccess` in full mode, `true` in match-only mode. makeConsequent = (outputSymbol, bindings) => Split.Else: if isMatchOnly then bool(true) else makeMatchSuccess(outputSymbol.use, nullifyEmptyBindings(bindings.use)), alternative = Split.Else(emptyMatchResult("topmost"))) val test = SynthIf(split) (DefineVar(symbol, test) :: LetDecl(symbol, Nil) :: stmts, (label, symbol.safeRef) :: results) // Materialize the matcher's final return value. Singleton matchers return // their only field directly; multi-label matchers still return a record. val resultTerm = resultTerms.reverse match case (_, term) :: Nil => term case terms => Rcd(false, terms.map: (label, term) => RcdField(str(label.asFieldName), term)) // Lastly, we return the matcher result, directly for singleton matchers // and as a record otherwise. Blk(bindings ::: tests.reverse, resultTerm) import Pattern.* /** Represent things that can be used as expressions in consequents. */ type Usable = BlockLocalSymbol | Term extension (usable: Usable) def use: Term = usable match case symbol: BlockLocalSymbol => symbol.safeRef case term: Term => term /** The bindings can be a `Term` or a `TempSymbol`. */ type MakeConsequent = (output: Usable, bindings: Usable) => Split /** A function that makes a split in matcher functions. The first argument * is the function that makes the innermost split. The second argument is the * alternative split. Note that the alternative is not a fallback. */ type MakeSplit = (makeConsequent: MakeConsequent, alternative: Split) => Split /** Create the innermost `Else` split based on whether we have a transform * term or not. * @param output The default output of the pattern. It will not be evaluated * if there is a transform term. */ private def completeMatchSuccess( transformOpt: Opt[TempSymbol], output: => Term, bindings: => Term ): Split = transformOpt match // If no transform is provided, we just return the current scrutinee and // the bindings through `MatchSuccess`. case N => Split.Else: makeMatchSuccess(output, nullifyEmptyBindings(bindings)) case S(transform) => val resultSymbol = TempSymbol(N, "transformResult") val bindingsTerm = nullifyEmptyBindings(bindings) val transformTerm = app(transform.safeRef, tup(fld(bindingsTerm)), "the transform's result") Split.Let(resultSymbol, transformTerm, Split.Else( makeMatchSuccess(resultSymbol.safeRef))) private def completePattern( pattern: SpPat, scrutinee: BlockLocalSymbol, subScrutinees: Map[Ident | Int, MatcherResult], aliases: Ls[VarSymbol] )(using ResultMode): MakeSplit = trace(pre = s"completePattern: ${pattern.showDbg}"): pattern match case MatchedClassLike(sym, fields) if isMatchOnly => val acceptAll: MakeSplit = (makeConsequent, _) => makeConsequent(scrutinee, rcd()) fields.iterator.foldRight(acceptAll): case ((field, pattern), makeInnerSplit) => val label = pattern.label val target = subScrutinees(field).select(label) val resultSymbol = TempSymbol(N, s"result$label$$") (makeConsequent, alternative) => Split.Let(resultSymbol, target, Branch(resultSymbol.safeRef, makeInnerSplit(makeConsequent, Split.End)) ~: alternative) case MatchedClassLike(sym, fields) => val rebuildNeeded = fields.iterator.exists((_, pattern) => !pattern.preservesOriginalScrutinee) val constructor = sym match case symbol: (ClassSymbol | ModuleOrObjectSymbol) => reference(symbol, symbol.toLoc).getOrElse(Term.Error) val makeMakeSplit = fields.iterator.foldRight( (fields: Ls[(Ident, Term)], bindingsSymbols: Ls[TempSymbol]) => ((makeConsequent, alternative) => log(s"current bindings: " + aliases.map(_.name).mkString("{", ", ", "}")) val currentBindings = aliases.map: alias => RcdField(str(alias.name), scrutinee.safeRef) val bindings = bindingsSymbols match case Nil => makeBindings(currentBindings) case bindingsSymbol :: Nil => if currentBindings.isEmpty then bindingsSymbol.safeRef else makeBindings(RcdSpread(bindingsSymbol.safeRef) :: currentBindings) case _ => val spreads = bindingsSymbols.reverseIterator.map: _.safeRef |> RcdSpread.apply .toList makeBindings(spreads ::: currentBindings) val output = if rebuildNeeded then `new`( constructor, tup(fields.reverseIterator.map(_._2 |> fld).toSeq*) :: Nil, s"rebuilt ${sym.nme}" ) else scrutinee.safeRef makeConsequent(output, bindings) ): MakeSplit ): case ((field, pattern), makeInnerSplit) => val label = pattern.label val target = subScrutinees(field).select(label) val resultSymbol = TempSymbol(N, s"result$label$$") val outputSymbol = TempSymbol(N, s"output$label$$") val fieldAliases = pattern.aliases val fieldBindingsSymbol = TempSymbol(N, "fieldBindings") val fieldBindingsTerm = makeBindings(fieldAliases.map: alias => RcdField(str(alias.name), outputSymbol.safeRef)) val fieldOutput = if rebuildNeeded && !pattern.preservesOriginalScrutinee then outputSymbol.safeRef else subScrutinees(field).input (outputFields: Ls[(Ident, Term)], bindingsSymbols: Ls[TempSymbol]) => ((makeConsequent, alternative) => val bindingsSymbol = TempSymbol(N, "bindings") val accumulatedBindings = val withFieldAliases = if fieldAliases.isEmpty then bindingsSymbols else fieldBindingsSymbol :: bindingsSymbols if pattern.symbols.isEmpty then withFieldAliases else bindingsSymbol :: withFieldAliases val innerSplit = makeInnerSplit((field, fieldOutput) :: outputFields, accumulatedBindings) (makeConsequent, Split.End) Split.Let(resultSymbol, target, Branch( scrutinee = resultSymbol.safeRef, pattern = matchSuccessPattern(S(outputSymbol :: bindingsSymbol :: Nil)), continuation = if fieldAliases.isEmpty then innerSplit else Split.Let(fieldBindingsSymbol, fieldBindingsTerm, innerSplit) ) ~: alternative)): MakeSplit makeMakeSplit(Nil, Nil) case Record(fields) if isMatchOnly => val acceptAll: MakeSplit = (makeConsequent, _) => makeConsequent(scrutinee, rcd()) fields.iterator.foldRight(acceptAll): case ((field, pattern), makeInnerSplit) => val label = pattern.label val target = subScrutinees(field).select(label) val resultSymbol = TempSymbol(N, s"result$label$$") (makeConsequent, alternative) => Split.Let(resultSymbol, target, Branch(resultSymbol.safeRef, makeInnerSplit(makeConsequent, Split.End)) ~: alternative) case Record(fields) => // Input: a record pattern made of several fields. // Output: a record, each field of which is the output of the // corresponding field's pattern. // Construct a conjunction of tests for each field. val makeMakeSplit = fields.iterator.foldRight( (fields: Ls[RcdField], bindingsSymbols: Ls[TempSymbol]) => ((makeConsequent, alternative) => log(s"current bindings: " + aliases.map(_.name).mkString("{", ", ", "}")) // The current pattern's bindings. val currentBindings = aliases.map: alias => RcdField(str(alias.name), scrutinee.safeRef) // Here, we need to merge the current bindings with previously // accumulated bindings. Some special cases here are to make the // generated code more concise and efficient. val bindings = bindingsSymbols match case Nil => makeBindings(currentBindings) case bindingsSymbol :: Nil => if currentBindings.isEmpty then bindingsSymbol.safeRef else makeBindings(RcdSpread(bindingsSymbol.safeRef) :: currentBindings) case _ => // Spread the previously accumulated bindings. val spreads = bindingsSymbols.reverseIterator.map: _.safeRef |> RcdSpread.apply .toList // Append the current bindings to the spreads. makeBindings(spreads ::: currentBindings) makeConsequent(Rcd(false, fields.reverse), bindings) ): MakeSplit ): case ((field, pattern), makeInnerSplit) => val label = pattern.label val target = subScrutinees(field).select(label) // This is the symbol for `MatchSuccess`. val resultSymbol = TempSymbol(N, s"result$label$$") // This is the symbol for the output of the pattern. val outputSymbol = TempSymbol(N, s"output$label$$") val outputField = RcdField(str(field.name), outputSymbol.safeRef) // This is the bindings of the current field. val fieldAliases = pattern.aliases val fieldBindingsSymbol = TempSymbol(N, "fieldBindings") val fieldBindingsTerm = makeBindings(fieldAliases.map: alias => RcdField(str(alias.name), outputSymbol.safeRef)) (outputFields: Ls[RcdField], bindingsSymbols: Ls[TempSymbol]) => ((makeConsequent, alternative) => val bindingsSymbol = TempSymbol(N, "bindings") val accumulatedBindings = val withFieldAliases = if fieldAliases.isEmpty then bindingsSymbols else fieldBindingsSymbol :: bindingsSymbols if pattern.symbols.isEmpty then withFieldAliases else bindingsSymbol :: withFieldAliases val innerSplit = makeInnerSplit(outputField :: outputFields, accumulatedBindings) (makeConsequent, Split.End) Split.Let(resultSymbol, target, Branch( scrutinee = resultSymbol.safeRef, pattern = matchSuccessPattern(S(outputSymbol :: bindingsSymbol :: Nil)), continuation = if fieldAliases.isEmpty then innerSplit else Split.Let(fieldBindingsSymbol, fieldBindingsTerm, innerSplit) ) ~: alternative)): MakeSplit makeMakeSplit(Nil, Nil) case Tuple(leading, spread) => (_, _) => // TODO: Think about how to handle the spread pattern. error(msg"Tuple patterns are not supported yet." -> pattern.toLoc) Split.Else(emptyMatchResult("unsupported tuple pattern")) // The wildcard case always succeeds. Thus, the `alternative` is not used. case Or(Nil) => (makeConsequent, _) => if isMatchOnly then makeConsequent(scrutinee, rcd()) else val bindings = aliases.map: alias => RcdField(str(alias.name), scrutinee.safeRef) makeConsequent(scrutinee, makeBindings(bindings)) // The never case should always fail. case And(Nil) => (_, _) => Split.Else(emptyMatchResult("never")) // The disjunction case should check the result from each pattern in order. case Or(patterns) => // Make those functions first so that symbols are allocated top-down. val functions = patterns.map: completePattern(_, scrutinee, subScrutinees, aliases) (makeConsequent, alternative) => functions.foldRight(alternative): case (makeSplit, innerSplit) => makeSplit(makeConsequent, innerSplit) // The conjunction case should check all results from patterns and only // return `MatchSuccess` if all patterns succeed. case And(patterns) if isMatchOnly => val functions = patterns.map: completePattern(_, scrutinee, subScrutinees, Nil) val acceptAll: MakeSplit = (makeConsequent, _) => makeConsequent(scrutinee, rcd()) functions.foldRight(acceptAll): case (makeSplit, makeInnerSplit) => (makeConsequent, alternative) => makeSplit( makeConsequent = (_output, _bindings) => makeInnerSplit(makeConsequent, Split.End), alternative = alternative) case And(patterns) => val functions = patterns.map: pattern => pattern -> completePattern(pattern, scrutinee, subScrutinees, aliases) val makeMakeSplit = functions.foldRight( (allOutputs: Ls[Usable], allBindings: Ls[Usable]) => ( (makeConsequent, alternative) => // Here, we need to make a tuple of all output values of patterns. // Then we merge the bindings of all patterns. val outputSymbol = TempSymbol(N, "combinedOutput") val outputTerm = tup(allOutputs.reverseIterator.map(_.use |> fld).toSeq*) val bindingsSymbol = TempSymbol(N, "combinedBindings") // I think the bindings do not need to be reversed. val bindingsTerm = makeBindings(allBindings.map: binding => RcdSpread(binding.use)) splitLet(outputSymbol, outputTerm) <|: splitLet(bindingsSymbol, bindingsTerm) <|: makeConsequent(outputSymbol, bindingsSymbol) ): MakeSplit ): case ((pattern, makeSplit), makeInnerMakeSplit) => (accOutputs: Ls[Usable], accBindings: Ls[Usable]) => ( (makeConsequent, alternative) => makeSplit( // Get the output and bindings of the current pattern. makeConsequent = (output, bindings) => val nextBindings = if aliases.nonEmpty || pattern.symbols.nonEmpty then bindings :: accBindings else accBindings makeInnerMakeSplit(output :: accOutputs, nextBindings) (makeConsequent, Split.End), alternative = alternative) ): MakeSplit makeMakeSplit(Nil, Nil) case Not(pattern) => (_, _) => // TODO: Think about how to handle negation patterns. error(msg"Negation patterns are not supported yet." -> pattern.toLoc) Split.Else(emptyMatchResult("unsupported negation pattern")) case Rename(pattern, name) => completePattern(pattern, scrutinee, subScrutinees, if isMatchOnly then Nil else name :: aliases) case Extract(pattern, correspondence, term) => if isMatchOnly then completePattern(pattern, scrutinee, subScrutinees, Nil) else // The symbol representing the transform function, which should be // declared at the outermost level. val transformSymbol = TempSymbol(N, "transform") // The transform function takes a single record as the argument. val bindingsSymbol = VarSymbol(Ident("args")) val params = paramList(param(bindingsSymbol)) // Because we pass the extracted values using recoreds. We need to bind // each property to its corresponding variable which is accessible from // then `term`. val letBindings = pattern.symbols.flatMap: symbol => val termSymbol = correspondence(symbol) LetDecl(termSymbol, Nil) :: DefineVar(termSymbol, sel(bindingsSymbol.safeRef, termSymbol.name)) :: Nil val makeSplit = completePattern(pattern, scrutinee, subScrutinees, Nil) (makeConsequent, alternative) => Split.Let( sym = transformSymbol, term = Term.Lam(params, Blk(letBindings, term.mkClone)), tail = makeSplit( // The `outputSymbol` is the output of `pattern`. // vvvvvvvvvvvv makeConsequent = (outputSymbol, bindings) => // Apply the transform to the bindings. val transformTerm = app(transformSymbol.safeRef, tup(fld(bindings.use)), "the transform's result") // Bind the transformation result to a new output symbol. val resultSymbol = TempSymbol(N, "transformResult") // Don't forget that current pattern may also have aliases which are // available in some outer transform patterns. val currentBindingsSymbol = TempSymbol(N, "bindings") val currentBindings = makeBindings(aliases.map: alias => RcdField(str(alias.name), resultSymbol.safeRef)) Split.Let(resultSymbol, transformTerm, Split.Let(currentBindingsSymbol, currentBindings, makeConsequent(resultSymbol, currentBindingsSymbol))), alternative = alternative)) /** Make a human-readable name for patterns that only have class patterns. If * the patterns are too complicated, we just use the counter. */ def makeMultiMatcherName(patterns: Set[Pat]): Str = "matcher" + patterns.iterator.mapOption(_.shortName()).fold(multiMatchers.size.toString): _.reverse.mkString("__", "_", "") + "$" object Compiler: type Label = Int enum ResultMode: case Full, MatchOnly /** A multi-matcher implementation. */ type Implementation = (BlockLocalSymbol, ParamList, Term) /** Perform a reverse lookup for a term that references a symbol in the * current context. */ def reference(symbol: ClassSymbol | ModuleOrObjectSymbol | PatternSymbol, loc: Opt[Loc])(using tl: TL)(using Ctx, State): Opt[Term] = /** To make `Lowering` happy about the terms. */ def fillImplicitArgs(term: Term): Term = term match case ref: Ref => ref.resolve case sel: SynthSel => fillImplicitArgs(sel.prefix) sel.resolve case _: Term => term type ClassLikeDefnSymbol = ClassSymbol | ModuleOrObjectSymbol | PatternSymbol def classLikeCandidates(symbol: Symbol): Iterator[ClassLikeDefnSymbol] = symbol match case symbol: ClassLikeDefnSymbol => Iterator.single(symbol) case member: BlockMemberSymbol => (member.clsTree.iterator.map(_.symbol.asClsLike) ++ member.modOrObjTree.iterator.map(_.symbol.asClsLike) ++ member.patTree.iterator.map(_.symbol.asClsLike)) .flatten case _ => Iterator.empty def memberReference( ownerRef: Term, key: Str, member: Symbol ): Opt[Term] = classLikeCandidates(member).collectFirst: case candidate if candidate is symbol => val memberSymbol = candidate.defn.get.bsym SynthSel(ownerRef, new Ident(key).withLoc(loc))(S(memberSymbol), FlowSymbol.synthSel(key), N, S(summon)) .resolved(candidate) def ownerMemberReference(owner: ClassSymbol | ModuleOrObjectSymbol): Opt[Term] = val ownerRef = owner.defn.get.bsym.ref() owner.tree.definedSymbols.iterator.map: case (key, member) => memberReference(ownerRef, key, member) .firstSome def findSymbol(elem: Ctx.Elem): Opt[Term] = val direct = elem.symbol.iterator.flatMap(classLikeCandidates).collectFirst: case candidate if candidate is symbol => elem.ref(new Ident(candidate.nme)).withLoc(loc).resolved(candidate) direct.orElse: elem.symbol.iterator.collectFirst: case owner: (ClassSymbol | ModuleOrObjectSymbol) => ownerMemberReference(owner) .flatten def ownerChainReference(symbol: ClassLikeDefnSymbol): Opt[Term] = def mkSelect(prefix: Term, member: BlockMemberSymbol, target: ClassLikeDefnSymbol): Term = SynthSel(prefix, new Ident(member.nme).withLoc(loc))(S(member), FlowSymbol.synthSel(member.nme), N, S(summon)) .resolved(target) def go(symbol: ClassLikeDefnSymbol): Term = val defn = symbol.defn.getOrElse(lastWords(s"Missing definition for symbol `${symbol.nme}`.")) defn.owner match case S(owner: ClassLikeDefnSymbol) => mkSelect(go(owner), defn.bsym, symbol) case S(owner) => mkSelect(owner.ref().resolve, defn.bsym, symbol) case N => defn.bsym.ref(new Ident(defn.bsym.nme).withLoc(loc)).resolved(symbol) symbol.defn.map(_ => go(symbol)) @tailrec def go(ctx: Ctx): Opt[Term] = val fromEnv = ctx.env.values.iterator.map(findSymbol).firstSome val fromOuter = ctx.outer.inner.collectFirst: case owner: (ClassSymbol | ModuleOrObjectSymbol) => ownerMemberReference(owner) .flatten (fromEnv orElse fromOuter) match case S(term) => S(fillImplicitArgs(term)) case N => ctx.parent match case N => N case S(parent) => go(parent) go(ctx).orElse(ownerChainReference(symbol)) import Pattern.* extension [X](xs: Iterator[X]) /** Apply a function to each element of the iterator. If any of the calls * returns `None`, then the whole function returns `None`. */ def mapOption[Y](f: X => Opt[Y]): Opt[Ls[Y]] = xs.foldLeft(S(Nil): Opt[Ls[Y]]): case (S(acc), x) => f(x) match case S(y) => S(y :: acc) case N => N case (N, _) => N /** Canadian Syllabics Pa */ val FAKE_LEFT_ANGLE = "ᐸ" /** Canadian Syllabics Na */ val FAKE_RIGHT_ANGLE = "ᐳ" // I learned the characters above from Go's trick: // https://news.ycombinator.com/item?id=14276891 // and I introduced more symbols by following the trick. /** Katakana-Hiragana Prolonged Sound Mark */ val FAKE_MINUS = "ー" /** Lisu Letter Ta */ val FAKE_TOP = "ꓔ" /** Lisu Letter Tha */ val FAKE_BOTTOM = "ꓕ" /** Lisu Letter Tone Na Po */ val FAKE_COMMA = "ꓹ" /** Modifier Letter Left Half Ring */ val FAKE_LEFT_PAREN = "ʿ" /** Modifier Letter Right Half Ring */ val FAKE_RIGHT_PAREN = "ʾ" /** Latin Letter Lateral Click */ val FAKE_BAR = "ǁ" extension (pattern: Pat) /** Make a human-readable short name using Unicode letters, which are * allowed in in identifiers in the generated code, for patterns. */ def shortName(prec: Int = 0): Opt[Str] = pattern match case Literal(IntLit(n)) => S((if n < 0 then FAKE_MINUS else "") + n.toString) case Literal(StrLit(s)) => S(s"str${s.length}") case Literal(UnitLit(true)) => S("null") case Literal(UnitLit(false)) => S("undefined") case ClassLike(sym, arguments) => arguments.fold(S(sym.nme)): arguments => arguments.iterator.mapOption: case (_, pat) => pat.shortName(0) .map(_.reverse.mkString(sym.nme + FAKE_LEFT_PAREN, FAKE_COMMA, FAKE_RIGHT_PAREN)) case MatchedClassLike(sym, entries) => entries.iterator.mapOption: case (_, pat) => pat.shortName(0) .map(_.reverse.mkString(sym.nme + FAKE_LEFT_PAREN, FAKE_COMMA, FAKE_RIGHT_PAREN)) case Synonym(Instantiation(symbol, patterns)) => if patterns.isEmpty then S(symbol.nme) else patterns.iterator.mapOption(_.shortName(0)).map: _.reverse.mkString(symbol.nme + FAKE_LEFT_ANGLE, FAKE_COMMA, FAKE_RIGHT_ANGLE) case Or(Nil) => S(FAKE_BOTTOM) case Or(patterns) => patterns.iterator.mapOption(_.shortName(1)).map: _.reverse.mkString( if prec > 0 then FAKE_LEFT_PAREN else "", FAKE_BAR, if prec > 0 then FAKE_RIGHT_PAREN else "") case And(Nil) => S(FAKE_TOP) case _ => N ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Context.scala ================================================ package hkmc2 package semantics package ups import mlscript.utils.*, shorthands.* import Pattern.{Instantiation, Never} import Message.MessageContext, ucs.bug import sourcecode.{FileName, Line, Name} /** Before pattern compilation, we monomorphize higher-order patterns into * first-order patterns. `Context` records the correspondence between each * `Pattern.Instantiation` and the resulting `Pat` after monomorphization, * to avoid redundant work. */ class Context(val definitions: Map[Pattern.Instantiation, Pat]): def get(instantiation: Instantiation)(using Raise): Pat = definitions.get(instantiation) match case Some(pattern) => pattern case None => bug(msg"The pattern is not instantiated." -> instantiation.toLoc) Never object Context: extension (instantiation: Instantiation) /** Get the body of the instantiated pattern definition. */ def body(using Context, Raise): Pat = summon[Context].get(instantiation) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala ================================================ package hkmc2 package semantics package ups import mlscript.utils.*, shorthands.* import Elaborator.{Ctx, State, ctx} import Message.MessageContext, ucs.{error, warn} import semantics.Pattern as SP, Pattern.* import syntax.Tree, Tree.{StrLit, Ident, IntLit} import collection.mutable.{Map as MutMap, Queue as MutQueue} import utils.TL import scala.collection.immutable.SeqMap object Instantiator: type Subst = Map[VarSymbol, Pat] class Instantiator(using tl: TL)(using Ctx, State, Raise): import tl.* /** The map contains all instantiated patterns. The key is a pattern symbol * paired with fully instantiated patterns as arguments. The value is the * pattern symbol's definition with all parameters have been substituted. * If the value is `None`, the pattern has not been instantiated yet. */ val progress: MutMap[Instantiation, Opt[Pat]] = MutMap.empty /** We instantiate patterns in a breadth-first manner. The queue contains all * patterns that need to be instantiated. */ val worklist: MutQueue[Instantiation] = MutQueue.empty /** Instantiate an anonymous pattern. */ def apply(pattern: SP): (Pat, Context) = scoped("ucs:instantiation"): val entryPoint = instantiate(pattern)(using Map.empty) (entryPoint, runInstantiationLoop) /** Run the loop to recursively instantiate needed patterns. */ private def runInstantiationLoop: Context = while worklist.nonEmpty do val instantiation = worklist.dequeue() val defn = instantiation.symbol.defn.get // Check if the number of pattern parameters and pattern arguments match. if defn.patternParams.size != instantiation.arguments.size then val parameterCount = "pattern parameter" countBy defn.patternParams.size val argumentCount = "pattern argument" countBy instantiation.arguments.size error(msg"Pattern `${instantiation.symbol.nme}` has $parameterCount." -> Loc(defn.patternParams), msg"But $argumentCount were provided." -> instantiation.toLoc) // The entire pattern will not be instantiated. Use never instead. progress += (instantiation -> S(Pattern.Never)) else val subst = defn.patternParams.iterator.map(_.sym).zip(instantiation.arguments).toMap log("Instantiating " + instantiation.showDbg) val instantiated = instantiate(defn.pattern)(using subst) log(s"Instantiated ${instantiation.showDbg}\n" + s"arity = ${instantiation.symbol.defn.get.patternParams.size}\n" + s"arguments = ${instantiation.arguments.iterator.map(_.showDbg).mkString(", ")}\n" + s"instantiated = ${instantiated.showDbg}") // Add the instantiated pattern to the progress map. progress += (instantiation -> S(instantiated)) // Finally, return the synonym representing the entry point pattern and all // instantiated pattern definitions. val definitions = progress.view.mapValues: _.getOrElse(lastWords("The pattern is expected to be instantiated.")) .toMap new Context(definitions) /** Add the instantiation to the queue if it has not been instantiated yet. */ def schedule(instantiation: Instantiation): Instantiation = if !progress.contains(instantiation) then progress += (instantiation -> N) worklist.enqueue(instantiation) else log(s"Already instantiated ${instantiation.showDbg}") progress(instantiation) instantiation /** Instantiate the given pattern with a substitution map. */ def instantiate(pattern: SP)(using subst: Map[VarSymbol, Pat]): Pat = pattern match case SP.Constructor(target, arguments) => target.symbol match // Look up the corresponding pattern from the substitution. case S(symbol: VarSymbol) => subst(symbol) // Recursively instantiate the arguments of constructor patterns. case S(symbol) => symbol.asClsLike match case S(symbol: ClassSymbol) => val keyedArguments = symbol.defn.get.paramsOpt match case S(ParamList(_, params, _)) => arguments match case S(arguments) => if params.size != arguments.size then error(msg"Class `${symbol.nme}` has ${params.size} parameters." -> Loc(params), msg"But ${arguments.size} arguments were provided." -> Loc(arguments)) S(params.iterator.zip(arguments).flatMap: case (param, argument) if param.flags.isVal => // The names are not from the source and are retrieved from // parameters in class definitions. Therefore, no `Loc` // should be attached. S(new Ident(param.sym.id.name) -> instantiate(argument)) case (param, argument) => error(msg"Parameter `${param.sym.nme}` is not accessible." -> param.toLoc) N .to(SeqMap)) case N => N // The class has parameters but no arguments are provided. case N => arguments match case N => N // No arguments are provided. case S(arguments) => error(msg"Class `${symbol.nme}` has no parameters." -> Loc(arguments)) N ClassLike(symbol, keyedArguments) case S(symbol: ModuleOrObjectSymbol) => arguments match case N => ClassLike(symbol, N) case S(arguments) => error( msg"`${symbol.nme}` is a module, thus it cannot have arguments." -> Loc(arguments)) ClassLike(symbol, N) case S(symbol: PatternSymbol) => // TODO(after we defined the semantics of pattern parameters): We need // to partition the arguments into pattern arguments and extraction // arguments here. val patternArguments = arguments.getOrElse(Nil).map(instantiate(_)) val instantiation = Instantiation(symbol, patternArguments)(pattern.toLoc) Synonym(schedule(instantiation)) case SP.Composition(true, left, right) => instantiate(left) or instantiate(right) case SP.Composition(false, left, right) => instantiate(left) and instantiate(right) case SP.Negation(pattern) => Not(instantiate(pattern)) case SP.Wildcard() => Wildcard case SP.Literal(literal) => Literal(literal) case SP.Range(lower, upper, rightInclusive) => // Currently, we expand the range pattern into a list of literals. After // the `where` clause or chain patterns are implemented, we could directly // expand the range pattern into a range test. (lower, upper) match case (StrLit(lower), StrLit(upper)) => Or((lower.head to upper.head).map(c => Literal(StrLit(c.toString))).toList) case (IntLit(lower), IntLit(upper)) => Or((lower to upper).map(i => Literal(IntLit(i))).toList) case _ => error(msg"Range patterns are not supported in pattern compilation." -> pattern.toLoc) Never case SP.Concatenation(left, right) => error(msg"String concatenation is not supported in pattern compilation." -> pattern.toLoc) Never case SP.Tuple(leading, spread) => val instantiatedSpread = spread.map: case (spreadKind, middle, trailing) => (spreadKind, instantiate(middle), trailing.map(instantiate(_))) Tuple(leading.map(instantiate(_)), instantiatedSpread) case SP.Record(fields) => Record: fields.iterator.map((id, pattern) => (id, instantiate(pattern))).to(SeqMap) case SP.Chain(first, second) => error(msg"Pattern chaining is not supported in pattern compilation." -> pattern.toLoc) Never case alias @ SP.Alias(pattern, id) => alias.symbolOption match case N => instantiate(pattern) case S(symbol) => Rename(instantiate(pattern), symbol) // Pattern arguments are accessible throughout the pattern. case SP.Transform(pattern, parameters, transform) => Extract(instantiate(pattern), parameters.toMap, transform) case SP.Annotated(pattern, annotations) => // Currently, we only support `@compile` annotation, so here we only // check whether this annotation exists, and report an error for all // other annotations. val shouldCompile = annotations.foldLeft(true): (acc, termOrLoc) => val res = termOrLoc match case R(term) => term.resolvedSym match case S(symbol) if symbol.asBlkMember.exists(_ is ctx.builtins.annotations.compile) => N case S(_) | N => S(term.toLoc) case L(loc) => S(loc) res match case S(loc) => warn(msg"This annotation is not supported here." -> loc, msg"Note: Patterns only support the `@compile` annotation." -> pattern.toLoc) acc case N => true instantiate(pattern) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala ================================================ package hkmc2 package semantics package ups import mlscript.utils.*, shorthands.* import semantics.Pattern as SP import syntax.{Tree, SpreadKind}, Tree.{Ident, StrLit} import Message.MessageContext import ucs.{error, warn} import Context.* import hkmc2.semantics.ups.Pattern.NonCompositional import scala.collection.immutable.SeqMap private[ups] object Kind: sealed trait Specialized extends Expanded sealed trait Expanded extends Complete sealed trait Complete /** This class `Pattern` with its case classes are the internal representation * of patterns used in pattern compilation. The most important difference * between these and those in the `semantics` package are as follows. * * 1. All symbols in this abstract data type are resolved, including bindings * used in pattern transformations. Ill-formed bindings have been rejected * during the elaboration stage. * 2. Higher-order patterns have been instantiated. Diverging patterns have * been rejected during instantiation. Therefore, all symbols in this abstract * data type are guaranteed to be first-order. */ sealed abstract class Pattern[+K <: Kind.Complete] extends AutoLocated: import Pattern.* infix def and[L >: K <: Kind.Complete](that: Pattern[L]): Pattern[L] = this match case And(patterns) => that match case And(patterns2) => And(patterns ++ patterns2) case _ => And[L](patterns appended that) case _ => that match case And(patterns) => And(this :: patterns) case _ => And[L](this :: that :: Nil) infix def or[L >: K <: Kind.Complete](that: Pattern[L]): Pattern[L] = this match case Or(patterns) => that match case Or(patterns2) => Or(patterns ++ patterns2) case _ => Or[L](patterns appended that) case _ => that match case Or(patterns) => Or[L](this :: patterns) case _ => Or[L](this :: that :: Nil) // TODO: Associate with locations. protected def children: Vector[Located] = this match case Literal(lit) => Vector.single(lit) case ClassLike(sym, arguments) => arguments.fold(Vector.empty): _.map((id, p) => p).toVector case MatchedClassLike(sym, entries) => entries.values.toVector case Record(entries) => entries.values.toVector case Tuple(leading, spread) => leading.toVector ++ spread.fold(Vector.empty): case (_, middle, trailing) => middle +: trailing.toVector case And(patterns) => patterns.toVector case Or(patterns) => patterns.toVector case Not(pattern) => Vector.single(pattern) case Rename(pattern, name) => Vector.single(pattern) case Extract(pattern, _, term) => Vector.double(pattern, term) case Synonym(pattern) => pattern.symbol +: pattern.arguments.toVector lazy val symbols: Ls[VarSymbol] = this match case Literal(lit) => Nil case ClassLike(sym, arguments) => arguments.fold(Nil)(_.values.flatMap(_.symbols).toList) case MatchedClassLike(sym, entries) => entries.values.flatMap(_.symbols).toList case Record(entries) => entries.values.flatMap(_.symbols).toList case Tuple(leading, spread) => leading.flatMap(_.symbols) ::: spread.fold(Nil): case (_, middle, trailing) => middle.symbols ::: trailing.flatMap(_.symbols) case Synonym(_) => Nil case And(patterns) => patterns.flatMap(_.symbols) case Or(patterns) => // We have checked that bindings are consistent in the elaboration stage. // Inconsistent bindings are not associated with symbols. patterns.find(_ != Never).fold(Nil)(_.symbols) case Not(_) => Nil case Rename(pattern, name) => name :: pattern.symbols case Extract(_, _, _) => Nil /** Checks whether a successful match preserves the original scrutinee as the * produced output. * * This property is context-sensitive because `Synonym` nodes must look * through instantiated pattern definitions. Cycles are treated as * preserving so the traversal stays well-founded on recursive patterns. */ def preservesOriginalScrutinee(using Context, Raise): Bool = def loop(pattern: Pat, visiting: Set[Instantiation]): Bool = pattern match case Literal(_) => true case ClassLike(_, arguments) => arguments.fold(true)(_.valuesIterator.forall(loop(_, visiting))) case MatchedClassLike(_, entries) => entries.valuesIterator.forall(loop(_, visiting)) case Record(entries) => entries.valuesIterator.forall(loop(_, visiting)) case Tuple(leading, spread) => leading.forall(loop(_, visiting)) && spread.forall: (_, middle, trailing) => loop(middle, visiting) && trailing.forall(loop(_, visiting)) case And(patterns) => patterns.forall(loop(_, visiting)) case Or(patterns) => patterns.forall(loop(_, visiting)) case Not(_) => true case Rename(pattern, _) => loop(pattern, visiting) case Extract(_, _, _) => false case Synonym(instantiation) => if visiting contains instantiation then true else loop(instantiation.body, visiting + instantiation) loop(this, Set.empty) /** Apply a partial function to every node in the pattern tree. Replace each * node with the result of the partial function. If the partial function is * not defined at a node, the node is left unchanged. * @param f The partial function to apply to each node. */ def map[L <: Kind.Complete](f: NonCompositional[K] => Pattern[L]): Pattern[L] = this match case p: NonCompositional[K] => f(p) case And(patterns) => And[L](patterns.map(_.map(f))) case Or(patterns) => Or[L](patterns.map(_.map(f))) case Not(pattern) => Not[L](pattern.map(f)) case Rename(pattern, name) => Rename[L](pattern.map(f), name) case Extract(pattern, correspondence, term) => Extract[L](pattern.map(f), correspondence, term) /** A simplified reduce for ``Pattern``. * It is designed to be used when we want to * compute a single value, starting from the leaves. * It silently goes through ``Not``, ``Extract``, ``Rename`` and ``Synonym``. * The function must provide all the other base cases, and how * a list is merged (for ``And`` and ``Or`` nodes) */ def reduce[A](merge: List[A] => A)(f: PartialFunction[Pattern[K], A]): A = this match case p: NonCompositional[K] => if f.isDefinedAt(p) then f(p) else merge(Nil) case And(patterns) => merge(patterns.map(_.reduce(merge)(f))) case Or(patterns) => merge(patterns.map(_.reduce(merge)(f))) case Not(pattern) => pattern.reduce(merge)(f) case Rename(pattern, _) => pattern.reduce(merge)(f) case Extract(pattern, _, _) => pattern.reduce(merge)(f) def heads: Set[Head] = reduce[Set[Head]](_.toSet.flatten): case Literal(lit) => Set(lit) case ClassLike(sym, _) => Set(sym) def fields: Set[Ident | Int] = reduce[Set[Ident | Int]](_.toSet.flatten): case MatchedClassLike(_, entries) => entries.keys.toSet case Record(entries) => entries.keys.toSet case Tuple(leading, spread) => val n = leading.size + spread.fold(0)(_._3.size) val subfields = Range(0, n).toSet[Ident | Int] // if this is strict, then a condition is imposed on field n if spread.isEmpty then subfields + n else subfields def collectSubPatterns(field: Ident | Int): Set[Pat] = field match case id: Ident => this.reduce[Set[Pat]](_.toSet.flatten): // TODO: raise a warning case ClassLike(sym, arguments) => /* TODO:raise a warning */ arguments match case None => Set() case Some(arguments) => arguments.find((id1, _) => id1 === id).map((_, p) => p).toSet case MatchedClassLike(_, entries) => entries.get(id).toSet case Record(entries) => entries.get(id).toSet case Tuple(leading, spread) => Set() case n: Int => this.reduce[Set[Pat]](_.toSet.flatten): case Tuple(leading, S(_, _, trailing)) => trailing.lift(n).toSet /** Simplify the pattern by removing `Never` patterns. */ def simplify: Pattern[K] = this match case _: Literal => this case ClassLike(sym, arguments) => ClassLike(sym, arguments.map(_.map((id, p) => (id, p.simplify)))) case MatchedClassLike(sym, entries) => val simplified = entries.map((id, p) => (id, p.simplify)) if simplified.exists((_, p) => p === Never) then Never else MatchedClassLike(sym, simplified) case Record(entries) => val simplify = entries.map((id, p) => (id, p.simplify)) if simplify.exists((_, p) => p === Never) then Never else Record(simplify) case Tuple(leading, N) => val simplified = leading.map(_.simplify) if simplified contains Never then Never else Tuple(simplified, N) case Tuple(leading, S((spreadKind, middle, trailing))) => val leading2 = leading.map(_.simplify) val middle2 = middle.simplify val trailing2 = trailing.map(_.simplify) if leading2.contains(Never) || middle2 === Never || trailing2.contains(Never) then Never else Tuple(leading2, S((spreadKind, middle2, trailing2))) case And(patterns) => // TODO: Complete the simplification logic here. val simplified = patterns.foldRight(Nil: Ls[Pattern[K]]): case (p, acc) => p.simplify match case `Wildcard` => acc match case `Wildcard` :: _ => acc // One consecutive wildcard is enough. case acc => Wildcard :: acc case simplified => simplified :: acc if simplified contains Never then Never else simplified.foldSingleton(And.apply)(identity) case Or(patterns) => patterns.foldRight(Nil: Ls[Pattern[K]]): case (p, acc) => p.simplify match case Never => acc match // The list should be a list of non-`Never` patterns or a singleton // list of `Never` pattern. case Nil => Never :: Nil case Never :: Nil | _ => acc // Should we discard the accumulated patterns? // case `Wildcard` => Wildcard :: Nil // Discard the following patterns. case `Wildcard` => acc match case `Never` :: Nil => Wildcard :: Nil case `Wildcard` :: _ => acc // One consecutive wildcard is enough. case acc => Wildcard :: acc case pat => acc match case Nil | Never :: Nil => pat :: Nil case _ => pat :: acc .foldSingleton(Or.apply)(identity) case Not(pattern) => Not(pattern.simplify) case Rename(pattern, name) => Rename(pattern.simplify, name) case Extract(pattern, correspondence, term) => pattern.simplify match case `Never` => Never // Lift up `Never`. Let the caller reduce it. case simplified => Extract(simplified, correspondence, term) case Synonym(sym) => this /** Expand the pattern by replacing any top-level synonym with its body. * @return the expanded pattern named "ExPat" in the paper. */ def expand(alreadyExpanded: Set[Instantiation])(using Context, Raise): ExPat = map: case Synonym(instantiation) => if alreadyExpanded contains instantiation then error(msg"Expanding this pattern leads to an infinite loop." -> instantiation.toLoc) Never // Infinite recursive patterns are considered as never patterns. else // Otherwise, we expand the instantiated pattern definition's body. instantiation.body.expand(alreadyExpanded + instantiation) // I don't know how to make the GADT type checking work here. // case pattern: NonCompositional[K] => pattern // Noop. // case pattern: ExPat => pattern // Noop. case pattern: ClassLike => pattern case pattern: Record => pattern case pattern: Tuple => pattern case pattern: Literal => pattern /** Unwrap `Rename` patterns until we reach a non-`Rename` pattern. Collect * the symbols on the way. Maybe we can make `Rename` a property of each * pattern. */ def aliases: Ls[VarSymbol] = this match case Rename(pattern, name) => name :: pattern.aliases case _: Pattern[K] => Nil def showDbg(using DebugPrinter): Str = this match case Literal(lit) => lit.idStr case ClassLike(sym, arguments) => val argumentsText = arguments.fold(""): _.iterator.map((id, p) => s"${id.name}: ${p.showDbg}").mkString(" { ", ", ", " }") s"${sym.nme}$argumentsText" case MatchedClassLike(sym, entries) => val argumentsText = entries.iterator.map: case (id, p) => s"${id.name}: ${p.showDbg}" .mkString(" { ", ", ", " }") s"${sym.nme}$argumentsText" case Record(entries) => if entries.isEmpty then "{}" else entries.iterator.map: case (id, p) => s"${id.name}: ${p.showDbg}" .mkString("{ ", ", ", " }") case Tuple(leading, N) => leading.iterator.map(_.showDbg).mkString("[", ", ", "]") case Tuple(leading, S(spreadKind, spread, trailing)) => val leadingItems = leading.iterator.map(_.showDbg) val spreadItem = Iterator.single(spreadKind.str + spread.showDbg) val trailingItems = trailing.iterator.map(_.showDbg) (leadingItems ++ spreadItem ++ trailingItems).mkString("[", ", ", "]") case And(Nil) => "⊤" case And(pattern :: Nil) => pattern.showDbg case And(patterns) => patterns.map(_.showDbg).mkString("(", " ∧ ", ")") case Or(Nil) => "⊥" case Or(pattern :: Nil) => pattern.showDbg case Or(patterns) => patterns.map(_.showDbg).mkString("(", " ∨ ", ")") case Not(pattern) => s"!${pattern.showDbg}" case Rename(Or(Nil), name) => name.name case Rename(pattern, name) => s"${pattern.showDbg} as $name" case Extract(pattern, _, term) => s"${pattern.showDbg} => ${term.showDbg}" case Synonym(sym) => sym.showDbg object Pattern: sealed trait NonCompositional[+K <: Kind.Complete] extends Pattern[K] final case class Literal(lit: syntax.Literal) extends NonCompositional[Kind.Expanded] /** Represents a constructor or a class, possibly with arguments. * @param sym the class symbol corresponding to the class or the constructor * @param arguments is None if no arguments were provided (as * in ``Foo``, and is Some if there were arguments provided, even if * it is an empty argument list, e.g. ``Bar()``) * * the list contains the patterns in the order with which they were given * along with the corresponding identifier, even if it was not present before. * e.g. if we have a class defintion ``class L = Nil | Cons(hd: Int, tl: L)`` * then the pattern ``Cons(foo, bar)`` is expected to be * ``ClassLike(sym=Cons, arguments=Some(List((hd, foo), tl, bar)))`` */ final case class ClassLike( sym: ClassLikeSymbol, arguments: Opt[SeqMap[Ident, Pat]] ) extends NonCompositional[Kind.Expanded] /** Represents a constructor pattern after its head has already been matched * by specialization. * * We cannot reuse `ClassLike` here because that would still mean “a class * head that must participate in head-based specialization”, which is no * longer true at this stage. We also cannot collapse it to `Record`, * because matching the remaining fields may still need to rebuild an output * value of the original constructor, so the constructor symbol must be * preserved. * * In other words, this node denotes the intermediate state where the class * identity is already known, but the field subpatterns still need to be * matched and may determine whether the final output reuses the original * scrutinee or rebuilds a fresh instance of the same class. */ final case class MatchedClassLike( sym: ClassLikeSymbol, entries: SeqMap[Ident, Pat] ) extends NonCompositional[Kind.Specialized] final case class Record( entries: SeqMap[Ident, Pat] ) extends NonCompositional[Kind.Specialized] /** @param entries is the ordered list of patterns, from left to right * @param strict is true if we matchexactly these fields, and no more, * it is false, if we are allowed to have more fields. * * Tuple([p0, ..., p(n-1)], true) <~> {0:p0, ..., n-1: p(n-1)} & Not({n:_}) * Tuple([p0, ..., p(n-1)], false) <~> {0:p0, ..., n-1: p(n-1)} */ final case class Tuple( leading: List[Pat], spread: Opt[(SpreadKind, Pat, List[Pat])], ) extends NonCompositional[Kind.Specialized] /** Represents a pattern synonym. * @param sym the pattern symbol corresponding */ final case class Synonym(pattern: Instantiation) extends NonCompositional[Kind.Complete] // * Pattern kinds propagate through the following cases. final case class And[+K <: Kind.Complete](patterns: List[Pattern[K]]) extends Pattern[K] final case class Or[+K <: Kind.Complete](patterns: List[Pattern[K]]) extends Pattern[K] final case class Not[+K <: Kind.Complete](pattern: Pattern[K]) extends Pattern[K] final case class Rename[+K <: Kind.Complete](pattern: Pattern[K], name: VarSymbol) extends Pattern[K] /** * Similar to `Pattern.Transform`. * * @param pattern The pattern to be matched against. * @param correspondence The correspondence between symbols in the pattern * and symbols in the transformation term. * @param term The term to be applied to the extracted values. */ final case class Extract[+K <: Kind.Complete]( pattern: Pattern[K], correspondence: Map[VarSymbol, VarSymbol], term: Term ) extends Pattern[K] val Wildcard = Or(Nil) val Never = And(Nil) type Head = syntax.Literal | ClassLikeSymbol /** A representation of fully instantiated first-order patterns. */ final case class Instantiation( symbol: PatternSymbol, arguments: Ls[Pat] )(val toLoc: Opt[Loc]) extends Located: def showDbg(using DebugPrinter): Str = val argumentsText = if arguments.isEmpty then "" else arguments.iterator.map(_.showDbg).mkString("[", ", ", "]") s"${symbol.nme}$argumentsText" type Pat = Pattern[Kind.Complete] type ExPat = Pattern[Kind.Expanded] type SpPat = Pattern[Kind.Specialized] import Pattern.* extension (pattern: ExPat) /** Modifies the pattern under the assumption that the scrutinee matches the * given literal. We decide to not care about literals with fields. (For * example, wrapped primitives with properties in JavaScript.) */ def specialize(lit: syntax.Literal): SpPat = pattern.map: case Literal(`lit`) => Wildcard case _: (Literal | ClassLike) => Never case pattern: (Record | Tuple) => pattern /** Modifies the pattern under the assumption that the scrutinee matches the * given class. */ def specialize(symbol: ClassLikeSymbol): SpPat = pattern.map: case Literal(_) => Never /** Note that `ClassLike` corresponds the syntax sugar of class patterns * intersected with record patterns. So, we need to leave the arguments * part unchanged. If there's no arguments, we return `Wildcard`. */ case ClassLike(`symbol`, arguments) => arguments.fold(Wildcard)(MatchedClassLike(symbol, _)) case ClassLike(_, _) => Never case pattern: (MatchedClassLike | Record | Tuple) => pattern /** Modifies the pattern under the assumption that the scrutinee matches the * given literal or class. */ def specialize(head: Option[Head]): SpPat = head match case Some(h: syntax.Literal) => pattern.specialize(h) case Some(h: ClassLikeSymbol) => pattern.specialize(h) case None => pattern.map: case _: (Literal | ClassLike) => Never case pattern: (Record | Tuple) => pattern ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala ================================================ package hkmc2 package semantics package ups import mlscript.utils.*, shorthands.* import Message.MessageContext import ucs.{TermSynthesizer, FlatPattern, error, warn, safeRef}, ucs.extractors.* import syntax.{Fun, Keyword, Tree}, Tree.{Ident, StrLit}, Keyword.{`as`, `=>`} import collection.mutable.{Buffer, HashMap}, collection.immutable.SeqMap import Elaborator.{Ctx, State, ctx}, utils.TL import semantics.Pattern as SP // "SP" is short for "semantic patterns" import Term.Ref import ups.Compiler.ResultMode object SplitCompiler: /** A class that can generate `Ref` to the scrutinee. It also comes with a few * mutable maps to reuse sub-scrutinees. Memoization of sub-scrutinees help * the normalization to merge let bindings from different branches. */ sealed abstract class Scrut: private val subScrutinees: HashMap[ClassSymbol | PatternSymbol, Buffer[SymbolScrut[?]]] = HashMap.empty private val fields: HashMap[Ident, SymbolScrut[?]] = HashMap.empty private val tupleLead: HashMap[Int, SymbolScrut[?]] = HashMap.empty private val tupleLast: HashMap[Int, SymbolScrut[?]] = HashMap.empty def apply(): Term.Ref def getSubScrutinee(cs: ClassSymbol | PatternSymbol)(i: Int)(using State): SymbolScrut[?] = val scrutinees = subScrutinees.getOrElseUpdate(cs, Buffer.empty) while scrutinees.size <= i do scrutinees += TempSymbol(N, s"arg$$${cs.nme}$$${scrutinees.size}$$").toScrut scrutinees(i) def getTupleLeadSubScrutinee(index: Int)(using State): SymbolScrut[?] = tupleLead.getOrElseUpdate(index, TempSymbol(N, s"element$index$$").toScrut) def getTupleLastSubScrutinee(index: Int)(using State): SymbolScrut[?] = tupleLast.getOrElseUpdate(index, TempSymbol(N, s"lastElement$index$$").toScrut) def getFieldScrutinee(fieldName: Ident)(using State): SymbolScrut[?] = fields.getOrElseUpdate(fieldName, TempSymbol(N, s"field_${fieldName.name}$$").toScrut) object Scrut: def from(ref: Term.Ref): RefScrut = RefScrut(() => ref) class RefScrut(make: () => Term.Ref) extends Scrut: def apply(): Ref = make() class SymbolScrut[SymbolType <: BlockLocalSymbol](val symbol: SymbolType) extends Scrut: def apply(): Ref = symbol.ref() extension [SymbolType <: BlockLocalSymbol](symbol: SymbolType) def toScrut: SymbolScrut[SymbolType] = SymbolScrut(symbol) type BindingMap = SeqMap[VarSymbol, Scrut] type SplitSequel[Output] = (makeConsequent: (Output, BindingMap) => Split, alternative: Split) => Split extension [Output](sequel: SplitSequel[Output]) def map[Result](f: Output => Result): SplitSequel[Result] = (makeConsequent, alternative) => sequel((output, bindings) => makeConsequent(f(output), bindings), alternative) type MakeConsequent = (output: Scrut, bindings: BindingMap) => Split /** The continuation function returned by `makeMatchSplit`. * Note that `alternative` does not serve as the fallback split. It means the * next split we should try when the current split fails. * * Note: I realized that it would make more sense to let `output` be a term. * Because in some cases, the output is not used in the consequent split. * For example, the output of the `negation` pattern is discarded. * * The `bindings` parameter is used to denote the variables that are bound * during the matching process. The key is the pattern's variable symbol and * the value is the local symbol that represents the matched values. */ type MakeSplit = (makeConsequent: MakeConsequent, alternative: Split) => Split object RejectSplit extends ((MakeConsequent, Split) => Split): def apply(makeConsequent: MakeConsequent, alternative: Split): Split = alternative extension (first: MakeSplit) def | (second: MakeSplit): MakeSplit = (makeConsequent, alternative) => first(makeConsequent, second(makeConsequent, alternative)) def & (second: MakeSplit): SplitSequel[(Scrut, Scrut)] = (makeConsequent, alternative) => first( (firstOutput, firstBindings) => second( (secondOutput, secondBindings) => makeConsequent( (firstOutput, secondOutput), firstBindings ++ secondBindings // Do we need to duplicate the bindings? ), alternative), alternative) type MakePrefixConsequent = (consumedOutput: Scrut, remainingOutput: Scrut, bindings: BindingMap) => Split /** The continuation function returned by `makeStringPrefixMatchSplit`. It * represents functions making splits that match any given string's prefix. */ type MakePrefixSplit = (makeConsequent: MakePrefixConsequent, alternative: Split) => Split object RejectPrefixSplit extends ((MakePrefixConsequent, Split) => Split): def apply(makeConsequent: MakePrefixConsequent, alternative: Split): Split = alternative extension (makePrefixSplit: MakePrefixSplit) /** Build another `MakePrefixSplit` if the function does not reject. */ transparent inline def whenAccept(derivedFunction: => MakePrefixSplit): MakePrefixSplit = if makePrefixSplit is RejectPrefixSplit then RejectPrefixSplit else derivedFunction /** A lazily created scrutinee. */ class LazyScrut(nameHint: Opt[Str] = N)(using State) extends Scrut: private var _hasBeenUsed = false private lazy val symbol = _hasBeenUsed = true TempSymbol(N, nameHint.getOrElse("output")) def apply(): Ref = symbol.safeRef def toList: Ls[TempSymbol] = if _hasBeenUsed then symbol :: Nil else Nil def toLet(term: => Term, tail: Split): Split = if _hasBeenUsed then Split.Let(symbol, term, tail) else tail import SplitCompiler.* /** This class compiles a pattern to a split that matches the pattern. */ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesizer: import tl.*, SP.* private lazy val lteq = State.builtinOpsMap("<=") private lazy val lt = State.builtinOpsMap("<") private lazy val add = State.builtinOpsMap("+") private def carriesExtractionSlots(context: Context): Bool = context.definitions.keysIterator.exists: _.symbol.defn.exists(_.extractionParams.nonEmpty) /** Conservatively checks whether a source-level pattern preserves its own * scrutinee as the produced output. */ private def preservesOriginalScrutinee(pattern: SP): Bool = def loop( pattern: SP, parameters: Map[VarSymbol, Bool], visiting: Set[PatternSymbol] ): Bool = pattern match case Constructor(target, arguments) => target.resolvedSym match case S(symbol: VarSymbol) => arguments.isEmpty && parameters.getOrElse(symbol, false) case symbolOption => symbolOption.flatMap(_.asClsLike) match case S(_: ClassSymbol | _: ModuleOrObjectSymbol) => arguments.forall(_.forall(loop(_, parameters, visiting))) case S(symbol: PatternSymbol) => symbol.defn match case N => false case S(defn) => if visiting contains symbol then true else val allArguments = arguments.getOrElse(Nil) if allArguments.length < defn.patternParams.length then false else val patternArguments = allArguments.take(defn.patternParams.length) val parameterBindings = defn.patternParams.iterator.map(_.sym).zip( patternArguments.iterator.map(loop(_, parameters, visiting)) ).toMap loop(defn.pattern, parameterBindings, visiting + symbol) case N => false case Composition(_, left, right) => loop(left, parameters, visiting) && loop(right, parameters, visiting) case Negation(_) => true case Wildcard() | Literal(_) | Range(_, _, _) => true case Concatenation(left, right) => loop(left, parameters, visiting) && loop(right, parameters, visiting) case Tuple(leading, spread) => leading.forall(loop(_, parameters, visiting)) && spread.forall: (_, middle, trailing) => loop(middle, parameters, visiting) && trailing.forall(loop(_, parameters, visiting)) case Record(fields) => fields.iterator.forall((_, pattern) => loop(pattern, parameters, visiting)) case Chain(first, second) => loop(first, parameters, visiting) && loop(second, parameters, visiting) case Alias(pattern, _) => loop(pattern, parameters, visiting) case Transform(_, _, _) => false case Annotated(pattern, _) => loop(pattern, parameters, visiting) case Guarded(pattern, _) => loop(pattern, parameters, visiting) loop(pattern, Map.empty, Set.empty) private def explicitlyDiscardsOutput(pattern: SP): Bool = pattern match case Alias(_, id) if id.name == "_" => true case Chain(_, Wildcard()) => true case Annotated(pattern, _) => explicitlyDiscardsOutput(pattern) case _ => false private def warnOnDiscardedExtractionOutputs( patternSymbol: PatternSymbol, extractionMatches: Opt[Ls[SP]] ): Unit = extractionMatches.foreach(_.foreach: subPattern => if !explicitlyDiscardsOutput(subPattern) && !preservesOriginalScrutinee(subPattern) then warn( msg"This extraction argument's transformation result is discarded by pattern `${patternSymbol.nme}`." -> subPattern.toLoc, msg"Write `... as _` to discard it explicitly." -> N )) private def warnOnDiscardedExtractionOutputs(pattern: SP): Unit = pattern match case Constructor(target, arguments) => target.resolvedSym.flatMap(_.asPat).foreach: patternSymbol => patternSymbol.defn.foreach: defn => val (_, extractionMatches, _) = matchParametersWithArguments(defn, arguments, reportErrors = false) warnOnDiscardedExtractionOutputs(patternSymbol, extractionMatches) arguments.foreach(_.foreach(warnOnDiscardedExtractionOutputs)) case Composition(_, left, right) => warnOnDiscardedExtractionOutputs(left) warnOnDiscardedExtractionOutputs(right) case Negation(pattern) => warnOnDiscardedExtractionOutputs(pattern) case Concatenation(left, right) => warnOnDiscardedExtractionOutputs(left) warnOnDiscardedExtractionOutputs(right) case Tuple(leading, spread) => leading.foreach(warnOnDiscardedExtractionOutputs) spread.foreach: (_, middle, trailing) => warnOnDiscardedExtractionOutputs(middle) trailing.foreach(warnOnDiscardedExtractionOutputs) case Record(fields) => fields.iterator.foreach((_, pattern) => warnOnDiscardedExtractionOutputs(pattern)) case Chain(first, second) => warnOnDiscardedExtractionOutputs(first) warnOnDiscardedExtractionOutputs(second) case Alias(pattern, _) => warnOnDiscardedExtractionOutputs(pattern) case Transform(pattern, _, _) => warnOnDiscardedExtractionOutputs(pattern) case Annotated(pattern, _) => warnOnDiscardedExtractionOutputs(pattern) case Guarded(pattern, _) => warnOnDiscardedExtractionOutputs(pattern) case Wildcard() | Literal(_) | Range(_, _, _) => () private def hasExplicitExtractionMatches(scrutinee: Scrut, pattern: SP): Bool = pattern match case Constructor(target, arguments) => target.resolvedSym.flatMap(_.asPat).exists: patternSymbol => val defn = patternSymbol.defn.getOrElse: lastWords(s"Pattern `${patternSymbol.nme}` has not been elaborated.") val (_, extractionMatches, _) = matchParametersWithArguments(defn, arguments, reportErrors = false) extractionMatches.exists(_.nonEmpty) case _ => false private def makeRangeTest(scrut: Scrut, lo: syntax.Literal, hi: syntax.Literal, rightInclusive: Bool, innerSplit: Split) = def scrutFld = fld(scrut()) val test1 = app(lteq.safeRef, tup(fld(Term.Lit(lo)), scrutFld), "isGreaterThanLower") val upperOp = if rightInclusive then lteq else lt val test2 = app(upperOp.safeRef, tup(scrutFld, fld(Term.Lit(hi))), "isLessThanUpper") plainTest(test1, "isGreaterThanLower")(plainTest(test2, "isLessThanUpper")(innerSplit)) extension (patterns: Ls[SP]) /** * Folds over sub-patterns to create a consequent making function with * accumulated sub-scrutinees. This function processes a list of patterns by * creating a consequent making function that applies consequent making * function of each sub-pattern from right to left. * * Mental model example: for patterns `[p1, p2, p3]` with sub-scrutinees * `[s1, s2, s3]`, this function creates: * ``` * s1 is p1 and s2 is p2 and s3 is p3 and * {| z(outerOutput + bindings1 + bindings2 + bindings3) |} * ``` * * @param z The initial consequent making function to start the fold with. * @param getSubScrutinee To get a scrutinee for a given pattern index. * @return A tuple containing: * - A list of `(BlockLocalSymbol, Option[Loc])` pairs for each * sub-scrutinee and its location. * - A `MakeConsequent` function that creates the nested match split. */ def folded(z: MakeConsequent)(getSubScrutinee: Int => SymbolScrut[?]) : (Ls[(BlockLocalSymbol, Opt[Loc])], MakeConsequent) = patterns.iterator.zipWithIndex.foldRight((Nil, z)): case ((subPattern, index), (accSubScrutinees, makeInnerSplit)) => val subScrutinee = getSubScrutinee(index) val makeThisSplit: MakeConsequent = (outerOutput, outerBindings) => makeMatchSplit(subScrutinee, subPattern, false)( // Note that the individual pattern's output is ignored because // in real world it's hard to synthesized a valid object if the // pattern carries some transformation. (_elementOutput, elementBindings) => makeInnerSplit( outerOutput, // TODO: Combine `outerOutput` and `elementOutput` outerBindings ++ elementBindings), Split.End) ((subScrutinee.symbol -> subPattern.toLoc) :: accSubScrutinees, makeThisSplit) /** Same as the other overload, but for `MakePrefixConsequent`. */ def folded(z: MakePrefixConsequent)(getSubScrutinee: Int => SymbolScrut[?]) : (Ls[(BlockLocalSymbol, Opt[Loc])], MakePrefixConsequent) = patterns.iterator.zipWithIndex.foldRight((Nil, z)): case ((subPattern, index), (accSubScrutinees, makeInnerSplit)) => val subScrutinee = getSubScrutinee(index) val makeThisSplit: MakePrefixConsequent = (outerOutput, outerRemaining, outerBindings) => makeStringPrefixMatchSplit(subScrutinee, subPattern)( // Note that the individual pattern's output is ignored because // in real world it's hard to synthesized a valid object if the // pattern carries some transformation. (_elementOutput, _elementRemaining, elementBindings) => makeInnerSplit( outerOutput, // TODO: Combine `outerOutput` and `elementOutput` outerRemaining, // TODO: Combine `outerRemaining` and `elementRemaining` outerBindings ++ elementBindings), Split.End) ((subScrutinee.symbol -> subPattern.toLoc) :: accSubScrutinees, makeThisSplit) def makePatternBindings(using State): Ls[(TempSymbol, SP)] = patterns.iterator.zipWithIndex.map: case (pattern, index) => new TempSymbol(N, s"patternArgument${index}$$") -> pattern .toList /** * Check whether the number of parameters in class-like patterns matches the * number in their definition, and whether each parameter is accessible. * * @param scrutinee the scrutinee * @param classTerm the term that resolves to the class * @param classSymbol the symbol for the class to be matched * @param arguments sub-patterns */ private def makeMatchClassSplit( scrutinee: Scrut, classTerm: Term, classSymbol: ClassSymbol, arguments: Opt[Ls[SP]], outputNeeded: Bool ): MakeSplit = // Obtain the `classHead` used for error reporting and the parameter list // from the class definitions. val (classHead, paramsOpt) = classSymbol.defn match case N => lastWords(s"Class ${classSymbol.name} does not have a definition") // Use the constructor pattern's location for error reporting. case S(cd) => new Tree.Ident(classSymbol.name).withLoc(classTerm.toLoc) -> cd.paramsOpt // Check if the number of arguments matches the number of parameters. val allArguments = arguments.getOrElse(Nil) val rebuildNeeded = outputNeeded && allArguments.exists(arg => !preservesOriginalScrutinee(arg)) def reportInaccessibleParameter(param: Param, arg: SP): Unit = val inaccessible = msg"because the corresponding parameter `${param.sym.name}` is not publicly accessible" -> param.sym.toLoc if rebuildNeeded then error( msg"This pattern cannot be matched" -> arg.toLoc, inaccessible, msg"because rebuilding the matched `${classSymbol.name}` value requires reading every constructor argument" -> classHead.toLoc, msg"Suggestion: mark this parameter with `val` so it becomes accessible" -> N ) else error( msg"This pattern cannot be matched" -> arg.toLoc, inaccessible, msg"Suggestion: use a wildcard pattern `_` in this position" -> N, msg"Suggestion: mark this parameter with `val` so it becomes accessible" -> N ) val successful = paramsOpt match case S(paramList) => arguments match case S(args) => // Check the number of parameters is correct. if args.size != paramList.params.size then val loc = Loc(args) orElse classTerm.toLoc error: if paramList.params.isEmpty then msg"The constructor does not take any arguments but found ${ "argument" countBy args.size}." -> loc else msg"Expected ${"argument" countBy paramList.params.size }, but found ${if args.size < paramList.params.size then "only " else "" }${"argument" countBy args.size}." -> loc // Check the fields are accessible. paramList.params.iterator.zip(args).map: case (param @ Param(flags, _, _, _), Wildcard()) if !flags.isVal && !rebuildNeeded => true case (param @ Param(flags, _, _, _), arg) if !flags.isVal => reportInaccessibleParameter(param, arg) false case _ => true // If the number of arguments are more than the number of parameters, // or one of parameters is incessible, we cannot make the branch. .foldLeft(args.size <= paramList.params.size)(_ && _) case N => arguments match case S(args) => error(msg"class ${classSymbol.name} does not have parameters" -> classHead.toLoc, msg"but the pattern has ${"sub-pattern" countBy args.size}" -> Loc(args)) false case N => true // No parameters, no arguments. This is fine. case N => arguments match // No parameter lists. Check if scruts are empty. case S(Nil) => error(msg"Class ${classSymbol.name} does not have a parameter list" -> classTerm.toLoc) true case S(args) => error(msg"Class ${classSymbol.name} does not have a parameter list" -> classTerm.toLoc, msg"but the pattern has ${"sub-pattern" countBy args.size}" -> Loc(args)) false case N => true if successful then (makeConsequent, alternative) => val argumentInfos = allArguments.iterator.zipWithIndex.map: case (argument, index) => val subScrutinee = scrutinee.getSubScrutinee(classSymbol)(index) (argument, subScrutinee, preservesOriginalScrutinee(argument)) .toList val destructuringArguments = arguments.map(_.iterator.zipWithIndex.map: case (argument, index) => (scrutinee.getSubScrutinee(classSymbol)(index).symbol, argument.toLoc) .toList) def buildConsequent( infos: Ls[(SP, SymbolScrut[?], Bool)], rebuiltArguments: Ls[Scrut], bindings: BindingMap ): Split = infos match case Nil => if rebuildNeeded then val rebuiltOutput = new LazyScrut(S(s"${classSymbol.name}Output")) val rebuiltTerm = `new`( classTerm, tup(rebuiltArguments.reverseIterator.map(scrut => scrut() |> fld).toSeq*) :: Nil, s"rebuilt ${classSymbol.name}" ) rebuiltOutput.toLet(rebuiltTerm, makeConsequent(rebuiltOutput, bindings)) else makeConsequent(scrutinee, bindings) case (argument, subScrutinee, preserves) :: rest => val childOutputNeeded = rebuildNeeded && !preserves makeMatchSplit(subScrutinee, argument, childOutputNeeded)( (argumentOutput, argumentBindings) => val nextArguments = if rebuildNeeded then (if preserves then subScrutinee else argumentOutput) :: rebuiltArguments else rebuiltArguments buildConsequent(rest, nextArguments, bindings ++ argumentBindings), Split.End) val pattern = FlatPattern.ClassLike(classTerm, classSymbol, destructuringArguments, false)(Tree.Dummy) Branch(scrutinee(), pattern, buildConsequent(argumentInfos, Nil, SeqMap.empty)) ~: alternative else RejectSplit /** * Make a split that matches the given object. Meanwhile, check whether the * object pattern has an argument list. * * @param scrutinee the scrutinee * @param objectSymbol the symbol for the object * @param arguments objects do not have parameters, thus the arguments are * only used for error checking */ private def makeMatchObjectSplit( patternLoc: Opt[Loc], scrutinee: Scrut, objectTerm: Term, objectSymbol: ModuleOrObjectSymbol, arguments: Opt[Ls[SP]] ): MakeSplit = val shouldReject = arguments match case S(Nil) => // The object pattern comes with an unnecessary parameter list, but it // can still be compiled. error(msg"`${objectSymbol.name}` is an object." -> objectSymbol.id.toLoc, msg"Its pattern cannot have an argument list." -> patternLoc) false case S(_ :: _) => // One or more parameters are provided, thus we cannot compile the // following consequent split. error(msg"`${objectSymbol.name}` is an object." -> objectSymbol.id.toLoc, msg"Its pattern cannot have arguments." -> patternLoc) true case N => false if shouldReject then RejectSplit else (makeConsequent, alternative) => val outputSymbol = new LazyScrut() val consequent = makeConsequent(outputSymbol, SeqMap.empty) val pattern = FlatPattern.ClassLike(objectTerm, objectSymbol, N, false)(Tree.Dummy) Branch(scrutinee(), pattern, outputSymbol.toLet(scrutinee(), consequent)) ~: alternative /** * When a scrutinee is matched with a defined pattern `P`, this method checks * whether the arguments provided by the user (both pattern arguments and * extraction parameters) match the parameters in the definition of `P`. This * part of the logic is used by both efficiently compiled patterns and * naively compiled patterns to ensure the consistency of diagnostics. * * @param defn the pattern's definition * @param arguments all arguments provided by the user * @return Return a triple. The first element is the pattern arguments * received when `P` is parameterized. The second element is the * sub-patterns used to match the value returned after `P` is applied. * The third element indicates whether expanding this branch should * be abandoned due to invalid arguments. */ private def matchParametersWithArguments( defn: PatternDef, arguments: Opt[Ls[SP]], reportErrors: Bool = true )(using Raise): (Ls[SP], Opt[Ls[SP]], Bool) = val argumentCount = arguments.fold(0)(_.length) val patternParameterCount = defn.patternParams.length val loc = Loc(arguments.getOrElse(Nil)) if argumentCount < patternParameterCount then // Since pattern parameters are required, if the total number of arguments // is less than this number, the pattern matching is definitely incorrect. if reportErrors then error: msg"Expected ${"pattern argument" countBy patternParameterCount }, but found only ${"pattern argument" countBy argumentCount}." -> loc (Nil, N, true) else arguments match // Now that the number of pattern parameters can definitely be satisfied, // what we need is to properly pair the parameters with the arguments. case N => (Nil, N, false) // This implies `patternParameterCount` is 0. case S(arguments) if argumentCount === patternParameterCount => // All the arguments satisfy the pattern arguments, with none left over. (arguments, N, false) case S(arguments) => val extractionParameterCount = defn.extractionParams.size val extractionArgumentCount = argumentCount - patternParameterCount if extractionArgumentCount === extractionParameterCount then // Match arguments and parameters pairwisely. val (patternArguments, extractionSubPatterns) = arguments.iterator.zip(defn.parameters).toList.partitionMap: case (patternArgument, Param(FldFlags(pat = true), _, _, _)) => L(patternArgument) case (subPattern, _) => R(subPattern) (patternArguments, S(extractionSubPatterns), false) else arguments match // When the pattern does not have any parameters, the user can provide // an argument as a shorthand of matching the output. For example, // `x is P(Q) === x is P as Q` when `P`` is not parameterized. case outputPattern :: Nil if defn.parameters.isEmpty => (Nil, S(outputPattern :: Nil), false) case _ :: _ | Nil => if reportErrors then error: msg"Expected ${"extraction argument" countBy extractionParameterCount }, but found ${if extractionArgumentCount < extractionParameterCount then "only " else "" }${"argument" countBy extractionArgumentCount}." -> loc (Nil, N, true) /** * Create a split that naively compiles and then binds the pattern arguments. * * @param patternArguments the pattern arguments with their symbols * @param split the inner split to be enclosed */ def buildPatternArguments( patternArguments: List[(BlockLocalSymbol, SP)], split: Split ): Split = patternArguments.foldRight(split): case ((symbol, pattern), innerSplit) => Split.Let(symbol, compileAnonymousPattern(Nil, Nil, pattern), innerSplit) /** * Make a split that matches the given pattern in the naive way. Note that * the efficient pattern compilation without backtracking is handled in the * case of `Annotated(_, _)`. * * @param scrutinee the scrutinee * @param patternTerm the term that resolved to the pattern * @param patternSymbol the symbol for the pattern * @param arguments pattern arguments and extraction arguments */ private def makeMatchPatternSplit( scrutinee: Scrut, patternTerm: Term, patternSymbol: PatternSymbol, arguments: Opt[Ls[SP]] ): MakeSplit = val defn = patternSymbol.defn.getOrElse: lastWords(s"Pattern `${patternSymbol.nme}` has not been elaborated.") val (patternArguments, extractionMatches, shouldReject) = matchParametersWithArguments(defn, arguments) warnOnDiscardedExtractionOutputs(patternSymbol, extractionMatches) if shouldReject then RejectSplit else (makeConsequent, alternative) => val (extractionArguments, makeConsequentForSubPatterns) = val z = (N: Opt[Ls[(BlockLocalSymbol, Opt[Loc])]], makeConsequent) extractionMatches.fold(z): _.folded(makeConsequent)(scrutinee.getSubScrutinee(patternSymbol)).mapFirst(S(_)) // First, we need to prepare the objects that performs the naive matching // of pattern arguments which will be passed to the pattern. val patternBindings = patternArguments.makePatternBindings val wrapPatternArgumentDefinitions = buildPatternArguments(patternBindings, _) // Then, we can call the `unapply` method. val unapplyArguments = patternBindings.map(_._1.safeRef |> fld) :+ fld(scrutinee()) val unapplyCall = app(sel(patternTerm, "unapply").resolve, tup(unapplyArguments*), s"result of unapply") val unapplyResult = TempSymbol(N, "unapplyResult") val wrapUnapply = Split.Let(unapplyResult, unapplyCall, _) // Then, we need to destruct the produced `MatchSuccess`. val outputSymbol = TempSymbol(N, "output").toScrut // TODO: We can use `LazyScrut` for this, but the match result pattern's parameters requires a symbol. val bindingsSymbol = TempSymbol(N, "bindings") // TODO: This can be automatically removed when no transformation is used. val wrapDestruction = (consequent: Split) => val pattern = matchSuccessPattern(S(outputSymbol.symbol :: bindingsSymbol :: Nil)) Branch(unapplyResult.safeRef, pattern, consequent) ~: alternative // Check the number of extraction arguments. val wrapExtractionArguments = extractionArguments match case N | S(Nil) => identity[Split] case S((single, _) :: Nil) => Split.Let(single, outputSymbol(), _) case S(extractionArguments) => (split: Split) => makeTupleBranch(outputSymbol(), extractionArguments.map(_._1), split, Split.End) // Finally, the inner split that does the work. val consequent = makeConsequentForSubPatterns(outputSymbol, SeqMap.empty) // Here we go! wrapPatternArgumentDefinitions(wrapUnapply(wrapDestruction(wrapExtractionArguments(consequent)))) /** Report errors when pattern parameters are used incorrectly. */ private def validatePatternParameter[A]( parameterTerm: Term, parameterSymbol: VarSymbol, arguments: Opt[Ls[SP]], patternLoc: Opt[Loc], default: A )(body: => A)(using Raise): A = parameterSymbol.decl match case S(param @ Param(flags = FldFlags(pat = true))) => arguments match case S(list) => // The object pattern comes with an unnecessary parameter list, but it // can still be compiled. error(msg"`${parameterSymbol.name}` is a pattern parameter." -> param.toLoc, msg"It cannot be applied${if list.isEmpty then "" else " to any arguments"}." -> patternLoc) default case N => body case S(_) | N => error(msg"Cannot use this ${parameterTerm.describe} as a pattern" -> parameterTerm.toLoc) default private def makeMatchPatternParameterSplit( scrutinee: Scrut, parameterTerm: Term, parameterSymbol: VarSymbol, arguments: Opt[Ls[SP]], patternLoc: Opt[Loc] ): MakeSplit = validatePatternParameter[MakeSplit]( parameterTerm, parameterSymbol, arguments, patternLoc, RejectSplit ): (makeConsequent, alternative) => val unapplyTerm = sel(parameterTerm, "unapply").resolve val unapplyCall = app(unapplyTerm, tup(fld(scrutinee())), s"result of unapply") tempLet(s"matchSuccess_${parameterSymbol.name}", unapplyCall): matchSuccessSymbol => val outputSymbol = TempSymbol(N, "output").toScrut val bindingsSymbol = TempSymbol(N, "bindings").toScrut val pattern = matchSuccessPattern(S(Ls(outputSymbol.symbol, bindingsSymbol.symbol))) val consequent = makeConsequent(outputSymbol, SeqMap.empty) Branch(matchSuccessSymbol.safeRef, pattern, consequent) ~: alternative def makeMatchSplit(scrutinee: Scrut, pattern: SP): MakeSplit = makeMatchSplit(scrutinee, pattern, true) /** Make a UCS split that matches the entire scrutinee against the pattern. * Since each pattern has an output, the split is responsible for creating * a binding that holds the output value and pass it to the continuation * function that makes the consequent split. */ def makeMatchSplit(scrutinee: Scrut, pattern: SP, outputNeeded: Bool): MakeSplit = pattern match case Constructor(target, arguments) => target.resolvedSym match case S(symbol: VarSymbol) => makeMatchPatternParameterSplit(scrutinee, target, symbol, arguments, pattern.toLoc) case symbolOption => symbolOption.flatMap(_.asClsLike) match case S(classSymbol: ClassSymbol) => makeMatchClassSplit(scrutinee, target, classSymbol, arguments, outputNeeded) case S(objectSymbol: ModuleOrObjectSymbol) => makeMatchObjectSplit(pattern.toLoc, scrutinee, target, objectSymbol, arguments) case S(patternSymbol: PatternSymbol) => makeMatchPatternSplit(scrutinee, target, patternSymbol, arguments) case N => error(msg"Cannot use this ${target.describe} as a pattern." -> target.toLoc) RejectSplit case Composition(true, left, right) => makeMatchSplit(scrutinee, left, outputNeeded) | makeMatchSplit(scrutinee, right, outputNeeded) case Composition(false, left, right) => (makeConsequent, alternative) => if outputNeeded then makeMatchSplit(scrutinee, left, true)( (leftOutput, leftBindings) => makeMatchSplit(scrutinee, right, true)( (rightOutput, rightBindings) => val outputScrut = new LazyScrut() outputScrut.toLet( tup(leftOutput() |> fld, rightOutput() |> fld), makeConsequent(outputScrut, leftBindings ++ rightBindings) ~~: alternative), alternative), alternative) else makeMatchSplit(scrutinee, left, false)( (_leftOutput, leftBindings) => makeMatchSplit(scrutinee, right, false)( (_rightOutput, rightBindings) => makeConsequent(scrutinee, leftBindings ++ rightBindings) ~~: alternative, alternative), alternative) case Negation(pattern) => (makeConsequent, alternative) => // Currently, the negation pattern produces the original value. In the // future, we would include diagnostic information about why the pattern // failed. Note that this feature requires the `alternative` parameter // to be a function that takes a diagnostic information generation // function. val outputSymbol = new LazyScrut() makeMatchSplit(scrutinee, pattern, false)( (_output, _bindings) => alternative, // The output and bindings are discarded. // The place where the diagnostic information should be stored. outputSymbol.toLet(scrutinee(), makeConsequent(outputSymbol, SeqMap.empty) ~~: alternative) ) // Note that we might duplicate the alternative split here. case Wildcard() => (makeConsequent, alternative) => makeConsequent(scrutinee, SeqMap.empty) ~~: alternative case Literal(literal) => (makeConsequent, alternative) => Branch(scrutinee(), FlatPattern.Lit(literal), makeConsequent(scrutinee, SeqMap.empty)) ~: alternative case Range(lower, upper, rightInclusive) => (makeConsequent, alternative) => makeRangeTest(scrutinee, lower, upper, rightInclusive, makeConsequent(scrutinee, SeqMap.empty)) ~~: alternative case Concatenation(left, right) => (makeConsequent, alternative) => makeStringPrefixMatchSplit(scrutinee, left)( (consumedOutput, remainingOutput, bindingsFromConsumed) => makeMatchSplit(remainingOutput, right, outputNeeded)( (postfixOutput, bindingsFromRemaining) => if outputNeeded then val combinedOutput = new LazyScrut(S("concatenatedOutput")) val combinedTerm = app( add.ref(), tup(fld(consumedOutput()), fld(postfixOutput())), "concatenated string output" ) combinedOutput.toLet( combinedTerm, makeConsequent(combinedOutput, bindingsFromConsumed ++ bindingsFromRemaining) ) ~~: alternative else makeConsequent(scrutinee, bindingsFromConsumed ++ bindingsFromRemaining) ~~: alternative, alternative), alternative ) case Tuple(elements, N) => (makeConsequent, alternative) => // Fixed-length tuple patterns are similar to constructor patterns. val (subScrutinees, makeChainedConsequent) = elements.folded(makeConsequent)(scrutinee.getTupleLeadSubScrutinee) val consequent = makeChainedConsequent(scrutinee, SeqMap.empty) makeTupleBranch(scrutinee(), subScrutinees.map(_._1), consequent, alternative) case Tuple(leading, S((_, spread, trailing))) => (makeConsequent, alternative) => val trailingSize = trailing.size val (trailSubScrutinees, makeConsequent0) = trailing.folded(makeConsequent): index => scrutinee.getTupleLastSubScrutinee(trailingSize - index) val spreadSubScrutinee = TempSymbol(N, "middleElements") val makeConsequent1: MakeConsequent = (outerOutput, outerBindings) => makeMatchSplit(spreadSubScrutinee.toScrut, spread, false)( (spreadOutput, spreadBindings) => makeConsequent0( spreadOutput, // TODO: Combine `outerOutput` and `spreadOutput` outerBindings ++ spreadBindings), Split.End) val (leadingSubScrutinees, makeConsequent2) = leading.folded(makeConsequent1): index => scrutinee.getTupleLeadSubScrutinee(index) makeTupleBranch( scrutinee(), leadingSubScrutinees.map(_._1), spreadSubScrutinee, trailSubScrutinees.map(_._1), makeConsequent2(scrutinee, SeqMap.empty), alternative) case Record(fields) => (makeConsequent, alternative) => // This case is similar to the `Constructor` case. val z = (Nil: Ls[(Ident, BlockLocalSymbol)], makeConsequent) // TODO: Deduplicate the code with the `Constructor` case. val (entries, makeChainedConsequent) = fields.iterator.zipWithIndex.foldRight(z): case (((key, pattern), index), (fields, makeInnerSplit)) => val subScrutinee = scrutinee.getFieldScrutinee(key) val makeThisSplit: MakeConsequent = (outerOutput, outerBindings) => makeMatchSplit(subScrutinee, pattern, false)( (fieldOutput, fieldBindings) => makeInnerSplit( fieldOutput, // TODO: Combine `outerOutput` and `fieldOutput` outerBindings ++ fieldBindings), alternative) // We fill in the alternative here. This is // different from the `Constructor` case. ((key, subScrutinee.symbol) :: fields, makeThisSplit) // END TODO val consequent = makeChainedConsequent(scrutinee, SeqMap.empty) Branch(scrutinee(), FlatPattern.Record(entries), consequent) ~: alternative case Chain(first, second) => (makeConsequent, alternative) => makeMatchSplit(scrutinee, first, true)( (firstOutput, firstBindings) => makeMatchSplit(firstOutput, second, outputNeeded)( (secondOutput, secondBindings) => makeConsequent(secondOutput, firstBindings ++ secondBindings), alternative), alternative) case alias @ Alias(pattern, id) => alias.symbolOption match // Ignore those who don't have symbols. `Elaborator` should have // reported errors. case N => makeMatchSplit(scrutinee, pattern, true) case S(symbol) => (makeConsequent, alternative) => makeMatchSplit(scrutinee, pattern, true)( (output, bindings) => makeConsequent(output, bindings + (symbol -> output)), alternative) case Transform(pattern, parameters, transform) => if !outputNeeded then makeMatchSplit(scrutinee, pattern, false) else // We should first create a local function that transforms the captured // values. So far, `pattern`'s variables should be bound to symbols. // Thus, we can make a parameter list from the symbols. Then, we make // a lambda term from the parameter list and the transform term. Because // `pattern` might be translated to many branches, making a lambda term // in advance reduces code duplication. val symbols = pattern.variables.symbols val params = parameters.map: case (_, parameterSymbol) => Param(FldFlags.empty, parameterSymbol, N, Modulefulness.none) val lambdaSymbol = new TempSymbol(N, "transform") // Next, we need to elaborate the pattern into a split. Note that // `makeMatchSplit` returns a function that takes a split as the // consequence. `makeMatchSplit` also takes a list of symbols so that // it needs to make sure that those bindings are available in the // consequence split. (makeConsequent, alternative) => Split.Let( sym = lambdaSymbol, term = Term.Lam(PlainParamList(params), transform.mkClone), // Declare the lambda function at the outermost level. Even if there // are multiple disjunctions in the consequent, we will not need to // repeat the `transform` term. tail = makeMatchSplit(scrutinee, pattern, true)( // Note that the output is not used. Semantically, the `transform` // term can only access the matched values by bindings. (_output, bindings) => val arguments = symbols.iterator.map(bindings).map(_() |> fld).toSeq val resultTerm = app(lambdaSymbol.safeRef, tup(arguments*), "the transform's result") val resultSymbol = TempSymbol(N, "transformResult") Split.Let(resultSymbol, resultTerm, makeConsequent(resultSymbol.toScrut, SeqMap.empty)), alternative)) case Annotated(pattern, annotations) => // Currently, we only support `@compile` annotation, so here we only // check whether this annotation exists, and report an error for all // other annotations. val shouldCompile = annotations.foldLeft(true): (acc, termOrLoc) => val res = termOrLoc match case R(term) => term.resolvedSym match case S(symbol) if symbol.asBlkMember.exists(_ is ctx.builtins.annotations.compile) => N case S(_) | N => S(term.toLoc) case L(loc) => S(loc) res match case S(loc) => warn(msg"This annotation is not supported here." -> loc, msg"Note: Patterns only support the `@compile` annotation." -> pattern.toLoc) acc case N => true if shouldCompile then compilePattern(scrutinee, pattern, outputNeeded) else makeMatchSplit(scrutinee, pattern, outputNeeded) case Guarded(pattern, guard) => (makeConsequent, alternative) => makeMatchSplit(scrutinee, pattern, true)( (output, bindings) => val guardSymbol = TempSymbol(N, "guardResult") val branch = Branch(guardSymbol.ref(), makeConsequent(output, bindings)) val innermost = Split.Let(guardSymbol, guard, branch ~: Split.End) // The creation of bindings here is repeated with the creation of // bindings during desugaring. We can add a piece of information // to the `bindings` map. See tests in `where.mls`. bindings.iterator.foldLeft(innermost): case (innerSplit, (symbol, mkTerm)) => Split.Let(symbol, mkTerm(), innerSplit), alternative) private def makeMatchPrefixPatternSplit( scrutinee: Scrut, patternTerm: Term, patternSymbol: PatternSymbol, arguments: Opt[Ls[SP]] )(using Raise): MakePrefixSplit = val defn = patternSymbol.defn.getOrElse(die) val (patternArguments, extractionMatches, shouldReject) = matchParametersWithArguments(defn, arguments) warnOnDiscardedExtractionOutputs(patternSymbol, extractionMatches) if shouldReject then RejectPrefixSplit else (makeConsequent, alternative) => val outputSymbol = TempSymbol(N, "output").toScrut val remainingSymbol = TempSymbol(N, "remaining").toScrut val (extractionArguments, makeConsequentForArguments) = extractionMatches.fold((N, makeConsequent)): _.folded(makeConsequent)(scrutinee.getSubScrutinee(patternSymbol)).mapFirst(S(_)) val consequent = makeConsequentForArguments(outputSymbol, remainingSymbol, SeqMap.empty) val patternBindings = patternArguments.makePatternBindings val unapplyCall = val method = "unapplyStringPrefix" val args = tup(patternBindings.map(_._1.safeRef) :+ scrutinee()) app(sel(patternTerm, method), args, s"result of $method") val split = tempLet("unapplyResult", unapplyCall): matchSuccessSymbol => val outputPairSymbol = TempSymbol(N, "outputPair") val bindingsSymbol = TempSymbol(N, "bindings") Branch( matchSuccessSymbol.safeRef, matchSuccessPattern(S(outputPairSymbol :: bindingsSymbol :: Nil)), // Bind the `remaining` variable to the second element of the output // of `matchSuccess`. Split.Let(outputSymbol.symbol, callTupleGet(outputPairSymbol.safeRef, 0, "prefix"), Split.Let(remainingSymbol.symbol, callTupleGet(outputPairSymbol.safeRef, 1, "postfix"), consequent)) ) ~: alternative buildPatternArguments(patternBindings, split) private def makeMatchPrefixPatternParameterSplit( scrutinee: Scrut, parameterTerm: Term, parameterSymbol: VarSymbol, arguments: Opt[Ls[SP]], patternLoc: Opt[Loc] )(using Raise): MakePrefixSplit = validatePatternParameter[MakePrefixSplit]( parameterTerm, parameterSymbol, arguments, patternLoc, RejectPrefixSplit ): (makeConsequent, alternative) => val unapplyTerm = sel(parameterTerm, "unapplyStringPrefix").resolve val unapplyCall = app(unapplyTerm, tup(fld(scrutinee())), s"result of unapply") tempLet(s"matchSuccess_${parameterSymbol.name}", unapplyCall): matchSuccessSymbol => // Destruct the `MatchSuccess` produced by the `unapply` method. val outputPairSymbol = TempSymbol(N, "outputPair").toScrut val bindingsSymbol = TempSymbol(N, "bindings").toScrut val pattern = matchSuccessPattern(S(Ls(outputPairSymbol.symbol, bindingsSymbol.symbol))) // Destruct the first field of the `MatchSuccess` as a pair. val outputSymbol = TempSymbol(N, "output").toScrut // Denotes the pattern's output. val remainingSymbol = TempSymbol(N, "remaining").toScrut // Denotes the remaining value. // Assemble the `Split`s in order from inside to outside. val consequent1 = makeConsequent(outputSymbol, remainingSymbol, SeqMap.empty) val consequent2 = makeTupleBranch(outputPairSymbol(), Ls(outputSymbol.symbol, remainingSymbol.symbol), consequent1, Split.End) Branch(matchSuccessSymbol.safeRef, pattern, consequent2) ~: alternative /** Construct a UCS split to match the prefix of the given scrutinee. * * @return The return value is a function that builds the split. */ protected def makeStringPrefixMatchSplit( scrutinee: Scrut, pattern: SP, )(using Raise): MakePrefixSplit = pattern match case Constructor(target, arguments) => target.resolvedSym match // The case when the target refers to a pattern parameter. case S(symbol: VarSymbol) => makeMatchPrefixPatternParameterSplit(scrutinee, target, symbol, arguments, pattern.toLoc) case symbolOption => symbolOption.flatMap(_.asClsLike) match case S(symbol: PatternSymbol) => makeMatchPrefixPatternSplit(scrutinee, target, symbol, arguments) // We accept the string class as a valid string pattern as it literally // means all strings. case S(symbol: ClassSymbol) if symbol is ctx.builtins.Str => arguments match case S(args) if args.nonEmpty => error( msg"`${symbol.name}` does not take any arguments." -> target.toLoc, msg"But the pattern has ${"sub-pattern" countBy args.size}." -> Loc(args) ) RejectPrefixSplit case _ => (makeConsequent, alternative) => val nonEmptySymbol = TempSymbol(N, "nonEmpty") val nonEmptyTerm = app( this.lt.safeRef, tup(fld(int(0)), fld(sel(scrutinee(), "length"))), "string is not empty" ) val outputSymbol = TempSymbol(N, "stringHead") val outputTerm = callStringGet(scrutinee(), 0, "head") val remainsSymbol = TempSymbol(N, "stringTail") val remainsTerm = callStringDrop(scrutinee(), 1, "tail") Split.Let(nonEmptySymbol, nonEmptyTerm, Branch(nonEmptySymbol.safeRef, Split.Let(outputSymbol, outputTerm, Split.Let(remainsSymbol, remainsTerm, makeConsequent(outputSymbol.toScrut, remainsSymbol.toScrut, SeqMap.empty))) ) ~: alternative) case S(_: ModuleOrObjectSymbol) | S(_: ClassSymbol) | N => RejectPrefixSplit case Composition(true, left, right) => val makeLeft = makeStringPrefixMatchSplit(scrutinee, left) val makeRight = makeStringPrefixMatchSplit(scrutinee, right) (makeConsequent, alternative) => makeLeft(makeConsequent, makeRight(makeConsequent, alternative)) case Composition(false, left, right) => (makeConsequent, alternative) => // This case is different, as the left pattern should be matched in prefix // mode, but the `right` pattern should be matched in full mode. If the // `right` pattern fails, we should check if `left` can match a different // prefix and retry `right`. // TODO: Implement the correct backtracking behavior. makeStringPrefixMatchSplit(scrutinee, left)( (leftOutput, leftRemains, leftBindings) => makeMatchSplit(scrutinee, right)( (rightOutput, rightBindings) => val productSymbol = TempSymbol(N, "product") val productTerm = tup(leftOutput() |> fld, rightOutput() |> fld) Split.Let(productSymbol, productTerm, makeConsequent( productSymbol.toScrut, leftRemains, leftBindings ++ rightBindings)), alternative), alternative) case Negation(pattern) => // This case is tricky. The question is how many of characters should be // left to the continuation? For example, to match string "match is over" // against pattern `~"game" ~ " is over"`. The first step is to match // pattern `"game"` as a prefix of the input. After we found it doesn't // not match, how many characters should we consume in this step? From a // global perspective, we know that we should consume the prefix // `"match is "``, but with backtracking, we have to try every // combinations before we can make a conclusion. RejectPrefixSplit case Wildcard() => (makeConsequent, alternative) => // Because the wildcard pattern always matches, we can match the entire // string and returns an empty string as the remaining value. val emptyStringSymbol = TempSymbol(N, "emptyString") makeConsequent(scrutinee, emptyStringSymbol.toScrut, SeqMap.empty) Branch(scrutinee(), FlatPattern.ClassLike(ctx.builtins.Str.safeRef, ctx.builtins.Str, N, false)(Tree.Dummy), Split.Let(emptyStringSymbol, str(""), makeConsequent(scrutinee, emptyStringSymbol.toScrut, SeqMap.empty)) ) ~: alternative case Literal(prefix: StrLit) => (makeConsequent, alternative) => // Check if the scrutinee is the same as the literal. If so, we return // an empty string as the remaining value. val isLeadingSymbol = TempSymbol(N, "isLeading") val isLeadingTerm = callStringStartsWith( scrutinee(), Term.Lit(prefix), "the result of startsWith") val outputSymbol = TempSymbol(N, "consumed") val outputTerm = callStringTake(scrutinee(), prefix.value.length, "the consumed part of input") val remainsSymbol = TempSymbol(N, "remains") val remainsTerm = callStringDrop(scrutinee(), prefix.value.length, "the remaining input") Split.Let(isLeadingSymbol, isLeadingTerm, Branch(isLeadingSymbol.safeRef, Split.Let(outputSymbol, outputTerm, Split.Let(remainsSymbol, remainsTerm, makeConsequent(outputSymbol.toScrut, remainsSymbol.toScrut, SeqMap.empty))) ) ~: alternative) // Non-string literal patterns are directly discarded. case Literal(_) => RejectPrefixSplit case Range(lower: StrLit, upper: StrLit, rightInclusive) => (makeConsequent, alternative) => // Check if the string is not empty. Then val stringHeadSymbol = TempSymbol(N, "stringHead") val stringTailSymbol = TempSymbol(N, "stringTail") val nonEmptySymbol = TempSymbol(N, "nonEmpty") val nonEmptyTerm = app(this.lt.safeRef, tup(fld(int(0)), fld(sel(scrutinee(), "length"))), "string is not empty") Split.Let(nonEmptySymbol, nonEmptyTerm, // `0 < string.length` Branch(nonEmptySymbol.safeRef, Split.Let(stringHeadSymbol, callStringGet(scrutinee(), 0, "head"), Split.Let(stringTailSymbol, callStringDrop(scrutinee(), 1, "tail"), makeRangeTest(stringHeadSymbol.toScrut, lower, upper, rightInclusive, makeConsequent(stringHeadSymbol.toScrut, stringTailSymbol.toScrut, SeqMap.empty)))) ) ~: alternative) // Other range patterns cannot be string prefixes. case Range(_, _, _) => RejectPrefixSplit case Concatenation(left, right) => (makeConsequent, alternative) => makeStringPrefixMatchSplit(scrutinee, left)( (leftConsumedOutput, leftRemainingOutput, leftBindings) => makeStringPrefixMatchSplit(leftRemainingOutput, right)( (rightConsumedOutput, rightRemainingOutput, rightBindings) => val combinedOutputSymbol = LazyScrut(S("combinedOutput")) combinedOutputSymbol.toLet( app(add.ref(), tup(fld(leftConsumedOutput()), fld(rightConsumedOutput())), "combined output"), makeConsequent(combinedOutputSymbol, rightRemainingOutput, leftBindings ++ rightBindings)), alternative), alternative) // Tuples and records cannot be string prefixes. case Tuple(_, _) => RejectPrefixSplit case Record(_) => RejectPrefixSplit case Chain(first, second) => (makeConsequent, alternative) => // This case is different because the first pattern might haven // non-string output. So, we should apply `makeMatchSplit` to the second // pattern, and finally pass the remains from the first pattern to the // continuation. makeStringPrefixMatchSplit(scrutinee, first)( (firstOutput, firstRemains, firstBindings) => makeMatchSplit(firstOutput, second)( (secondOutput, secondBindings) => makeConsequent(secondOutput, firstRemains, firstBindings ++ secondBindings), alternative), alternative) case alias @ Alias(pattern, id) => alias.symbolOption match // Ignore those who don't have symbols. `Elaborator` should have // reported errors. case N => makeStringPrefixMatchSplit(scrutinee, pattern) case S(symbol) => val make = makeStringPrefixMatchSplit(scrutinee, pattern) make.whenAccept: (makeConsequent, alternative) => make( (output, remains, bindings) => makeConsequent(output, remains, bindings + (symbol -> output)), alternative) case Transform(pattern, parameters, transform) => val make = makeStringPrefixMatchSplit(scrutinee, pattern) make.whenAccept: // Declare the lambda function at the outermost level. Even if there are // multiple disjunctions in the consequent, we will not need to repeat // the `transform` term. val symbols = pattern.variables.symbols val params = parameters.map: case (_, parameterSymbol) => Param(FldFlags.empty, parameterSymbol, N, Modulefulness.none) val lambdaSymbol = new TempSymbol(N, "transform") (makeConsequent, alternative) => Split.Let( sym = lambdaSymbol, term = Term.Lam(PlainParamList(params), transform), tail = make( // Note that the output is not used. Semantically, the `transform` // term can only access the matched values by bindings. (_output, remains, bindings) => val arguments = symbols.iterator.map(bindings).map(_() |> fld).toSeq val resultTerm = app(lambdaSymbol.safeRef, tup(arguments*), "the transform's result") val resultSymbol = TempSymbol(N, "transformResult") Split.Let(resultSymbol, resultTerm, makeConsequent(resultSymbol.toScrut, remains, SeqMap.empty)), alternative)) case Guarded(pattern, guard) => val make = makeStringPrefixMatchSplit(scrutinee, pattern) make.whenAccept: (makeConsequent, alternative) => make( (output, remains, bindings) => val guardSymbol = TempSymbol(N, "guardResult") val branch = Branch(guardSymbol.ref(), makeConsequent(output, remains, bindings)) val innermost = Split.Let(guardSymbol, guard, branch ~: Split.End) // The creation of bindings here is repeated with the creation of // bindings during desugaring. We can add a piece of information // to the `bindings` map. See tests in `where.mls`. bindings.iterator.foldLeft(innermost): case (innerSplit, (symbol, mkTerm)) => Split.Let(symbol, mkTerm(), innerSplit), alternative) case Annotated(pattern, annotations) => // Currently, we only support `@compile` annotation, so here we only // check whether this annotation exists, and report an error for all // other annotations. val shouldCompile = annotations.foldLeft(true): (acc, termOrLoc) => val res = termOrLoc match case R(term) => term.resolvedSym match case S(symbol) if symbol is ctx.builtins.annotations.compile => N case S(_) | N => S(term.toLoc) case L(loc) => S(loc) res match case S(loc) => warn(msg"This annotation is not supported here." -> loc, msg"Note: Patterns only support the `@compile` annotation." -> pattern.toLoc) acc case N => true if shouldCompile then error: msg"String patterns are not yet supported by efficient compilation." -> pattern.toLoc makeStringPrefixMatchSplit(scrutinee, pattern) def compilePattern(scrutinee: Scrut, pattern: SP): MakeSplit = compilePattern(scrutinee, pattern, true) /** This method handles the efficient and non-backtracking pattern compilation. * Note that we still have not supported accessing pattern parameters in the * naive pattern declaration in the efficient pattern compilation. */ def compilePattern(scrutinee: Scrut, pattern: SP, outputNeeded: Bool): MakeSplit = (makeConsequent, alternative) => scoped("ucs:ups:compilation"): warnOnDiscardedExtractionOutputs(pattern) // Instantiate the pattern and all patterns used in it. val instantiator = new Instantiator val (synonym, context) = instantiator(pattern) // Decide whether the compiled matcher needs to create `MatchSuccess` or can // represent success and failure using Boolean values. // // We must stay in `Full` mode when // - the pattern explicitly matches extraction arguments, or when any // reachable instantiated pattern definition has extraction slots; // - otherwise, if the use site needs an output, we only need `Full` // mode when a successful match does have transformations. // // When the instantiated pattern is transform-free and thus preserves the // original scrutinee as its output, `MatchOnly` is used and the caller can // obtain the scrutinee directly. val canReuseScrutineeOutput = synonym.preservesOriginalScrutinee(using context, summon[Raise]) val resultMode = if hasExplicitExtractionMatches(scrutinee, pattern) || carriesExtractionSlots(context) then ResultMode.Full else if outputNeeded && !canReuseScrutineeOutput then ResultMode.Full else ResultMode.MatchOnly // Initiate the compilation. val compiler = new Compiler(using context) val (matcherSymbol, implementations) = compiler.buildMatcher(synonym, resultMode) val innermostSplit = resultMode match case ResultMode.MatchOnly => val resultSymbol = TempSymbol(N, "matchSuccess") val resultTerm = app(matcherSymbol.safeRef, tup(fld(scrutinee())), "result of matcher function") Split.Let(resultSymbol, resultTerm, Branch(resultSymbol.safeRef, makeConsequent(scrutinee, SeqMap.empty)) ~: alternative) case ResultMode.Full => // 1. Bind the call result to a variable. val recordSymbol = TempSymbol(N, "matchRecord") val recordTerm = app(matcherSymbol.safeRef, tup(fld(scrutinee())), "result of matcher function") val f1 = Split.Let(recordSymbol, recordTerm, _) // 2. Check if the direct result is a `MatchSuccess` and bind the output. val outputSymbol = TempSymbol(N, "patternOutput") val bindingsSymbol = TempSymbol(N, "bindings") // TODO: This is useless. val consequent = makeConsequent(outputSymbol.toScrut, SeqMap.empty) val pattern = matchSuccessPattern(S(outputSymbol :: bindingsSymbol :: Nil)) val branch = Branch(recordSymbol.safeRef, pattern, consequent) f1(branch ~: alternative) implementations.iterator.foldRight(innermostSplit): case ((symbol, paramList, term), innerSplit) => log(term.showDbg) Split.Let(symbol, Term.Lam(paramList, term), innerSplit) /** Make a term like `MatchFailure(null)`. We will synthesize detailed * error messages and pass them to the function. */ private def failure: Split = Split.Else(makeMatchFailure()) /** * Create a method from the given UCS splits. The function has a parameter * list that contains the pattern parameters and a parameter that represents * the input value. * * @param name the method's name * @param patternParameters all pattern parameters * @param scrut the symbol of the input value * @param topmost the topmost split */ private def makeMethod( name: Str, patternParameters: List[Param], scrut: VarSymbol, topmost: Split ): (BlockMemberSymbol, ParamList, Split) = val sym = BlockMemberSymbol(name, Nil) // Pattern parameters are passed as objects. val patternInputs = patternParameters.map(_.copy(flags = FldFlags.empty)) // The last parameter is the scrutinee. val scrutParam = Param(FldFlags.empty, scrut, N, Modulefulness.none) val ps = PlainParamList(patternInputs :+ scrutParam) (sym, ps, topmost) /** Translate a list of extractor/matching functions for the given pattern. * There are currently two functions: `unapply` and `unapplyStringPrefix`. * * - `unapply` is used for matching the entire scrutinee. It returns the * captured/extracted values. * - `unapplyStringPrefix` is used for matching the string prefix of the * scrutinee. It returns the remaining string and the captured/extracted * values. If the given tree does not represent a string pattern, this * function will not be generated. * * @param pattern We will eventually generate methods from the omnipotent * `Pattern` class. Now the new `pattern` parameter and the * old `body` parameter are mixed. */ def compilePattern(pd: PatternDef): Ls[(BlockMemberSymbol, ParamList, Split)] = trace( pre = s"compilePattern <<< ${pd.showDbg}", post = (blk: Ls[(BlockMemberSymbol, ParamList, Split)]) => s"compilePattern >>> $blk" ): val unapply = scoped("ucs:translation"): val inputSymbol = VarSymbol(Ident("input")) val topmost = makeMatchSplit(inputSymbol.toScrut, pd.pattern, true)( makeConsequent = (output, bindings) => def getBinding(p: Param) = bindings.get(p.sym).fold(Term.Error)(_()) pd.extractionParams match case Nil => // If the pattern doesn't have any extraction parameters, we take // the entire output as the match result. Split.Else(makeMatchSuccess(output())) case sole :: Nil => // If there is only one extraction parameter, we don't make a tuple. Split.Else(makeMatchSuccess(getBinding(sole))) case ps => // Otherwise, `bindings` records which symbol each extraction // parameter is actually represented by. Here, we extract them and // put them into a tuple, then return it. Split.Else(makeMatchSuccess(tup(ps.map(getBinding)))), alternative = failure ) pd.extractionParams.map(_.sym) log(s"Translated `unapply`: ${topmost.prettyPrint}") makeMethod("unapply", pd.patternParams, inputSymbol, topmost) // TODO: Use `pd.extractionParams`. val unapplyStringPrefix = scoped("ucs:cp"): // We don't report errors here because they have been already reported in // the translation of `unapply` function. given Raise = Function.const(()) val inputSymbol = VarSymbol(Ident("input")) val topmost = makeStringPrefixMatchSplit(inputSymbol.toScrut, pd.pattern) ((consumedOutput, remainingOutput, bindings) => Split.Else: makeMatchSuccess(tup(fld(consumedOutput()), fld(remainingOutput()))), failure) log(s"Translated `unapplyStringPrefix`: ${topmost.prettyPrint}") makeMethod("unapplyStringPrefix", pd.patternParams, inputSymbol, topmost) unapply :: unapplyStringPrefix :: Nil /** Generate the record statements of `unapply` methods that can be used in * objects for anonymous patterns. */ private def makeUnapplyRecordStatements( name: Str, patternParameters: List[Param], scrut: VarSymbol, topmost: Split ): Ls[Statement] = val fieldSymbol = TempSymbol(N, name) val decl = LetDecl(fieldSymbol, Nil) val param = Param(FldFlags.empty, scrut, N, Modulefulness.none) val paramList = PlainParamList(param :: Nil) val lambda = Term.Lam(paramList, Term.SynthIf(topmost)) val defineVar = DefineVar(fieldSymbol, lambda) val field = RcdField(str(name), fieldSymbol.safeRef) decl :: defineVar :: field :: Nil /** Translate an anonymous pattern. They are usually pattern arguments. */ private def compileAnonymousPattern(patternParams: Ls[Param], params: Ls[Param], pattern: SP): Term = trace( pre = s"compileAnonymousPattern <<< $pattern", post = (blk: Term) => s"compileAnonymousPattern >>> $blk" ): // If the `target` refers to a pattern symbol, we can reference the pattern. val term = pattern match case Constructor(target, N) => target.symbol.flatMap(_.asPat).flatMap(Compiler.reference(_, target.toLoc)) case _ => N term.getOrElse: val unapply = scoped("ucs:translation"): val inputSymbol = VarSymbol(Ident("input")) val topmost = makeMatchSplit(inputSymbol.toScrut, pattern, true) ((output, bindings) => Split.Else(makeMatchSuccess(output())), failure) log(s"Translated `unapply`: ${topmost.prettyPrint}") makeUnapplyRecordStatements("unapply", patternParams, inputSymbol, topmost) val unapplyStringPrefix = scoped("ucs:cp"): // We don't report errors here because they have been already reported in // the translation of `unapply` function. given Raise = Function.const(()) val inputSymbol = VarSymbol(Ident("input")) val topmost = makeStringPrefixMatchSplit(inputSymbol.toScrut, pattern) ((consumedOutput, remainingOutput, bindings) => Split.Else: makeMatchSuccess(tup(fld(consumedOutput()), fld(remainingOutput()))), failure) log(s"Translated `unapplyStringPrefix`: ${topmost.prettyPrint}") makeUnapplyRecordStatements("unapplyStringPrefix", patternParams, inputSymbol, topmost) Term.Rcd(false, unapply ::: unapplyStringPrefix) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala ================================================ package hkmc2 package syntax import collection.mutable import mlscript.utils.*, shorthands.* class Keyword( val name: String, val leftPrec: Opt[Int], val rightPrec: Opt[Int], /** If the operator can be used infix, can it be done on a newline (with no indent)? For instance, if `via` has `canStartInfixOnNewLine`, then one can write: foo via f via g But `is` does not have `canStartInfixOnNewLine` so that if x is A then foo is B then bar does not parse as if x { is A then foo { is B then ... } } Note: Currently, this just fails to parse. We should probably rather make `is` a normal operator like `+`; then it would work. */ val canStartInfixOnNewLine: Bool = true ): Keyword.all += name -> this def assumeLeftPrec: Int = leftPrec.getOrElse(lastWords(s"$this does not have left precedence")) def assumeRightPrec: Int = rightPrec.getOrElse(lastWords(s"$this does not have right precedence")) def leftPrecOrMin: Int = leftPrec.getOrElse(Int.MinValue) def rightPrecOrMin: Int = rightPrec.getOrElse(Int.MinValue) def rightPrecOrMax: Int = rightPrec.getOrElse(Int.MaxValue) override def toString: Str = s"keyword '$name'" object Keyword: def unapply(kw: Keyword): Opt[Str] = S(kw.name) val all: mutable.Map[Str, Keyword] = mutable.Map.empty // val Let = Keyword("let", 0, 0) // val Let = Keyword("let", 0, 0) private var _curPrec = 2 private def curPrec: S[Int] = S(_curPrec) private def nextPrec: S[Int] = _curPrec += 1 S(_curPrec) val `class` = Keyword("class", N, N) val `extends` = Keyword("extends", nextPrec, curPrec) val `restricts` = Keyword("restricts", curPrec, curPrec) val `with` = Keyword("with", curPrec, curPrec) val `val` = Keyword("val", N, curPrec) val eqPrec = nextPrec val ascPrec = nextPrec // * `x => x : T` should parsed as `x => (x : T)` val `=` = Keyword("=", eqPrec, eqPrec) val `:` = Keyword(":", ascPrec, eqPrec) val `..` = Keyword("..", N, N) val `...` = Keyword("...", N, N) // val `;` = Keyword(";", ascPrec, eqPrec) val `if` = Keyword("if", N, nextPrec) val `while` = Keyword("while", N, curPrec) val `assert` = Keyword("assert", N, curPrec) type `assert` = `assert`.type val `case` = Keyword("case", N, curPrec) val thenPrec = nextPrec val `then` = Keyword("then", thenPrec, eqPrec) val `do` = Keyword("do", thenPrec, eqPrec) val `drop` = Keyword("drop", thenPrec, eqPrec) val `else` = Keyword("else", N, eqPrec) type `else` = `else`.type val `return` = Keyword("return", N, curPrec) val `throw` = Keyword("throw", N, curPrec) val `import` = Keyword("import", N, curPrec) val `fun` = Keyword("fun", N, N) // val `val` = Keyword("val", N, N) val `var` = Keyword("var", N, N) val `where` = Keyword("where", nextPrec, curPrec) val `of` = Keyword("of", N, N) // * Note that `of` is parsed specially, so its precedence is not listed here val `or` = Keyword("or", nextPrec, curPrec) val `and` = Keyword("and", nextPrec, nextPrec) val `not` = Keyword("not", nextPrec, nextPrec) val `is` = Keyword("is", nextPrec, curPrec, canStartInfixOnNewLine = false) val `as` = Keyword("as", nextPrec, curPrec) // val `let` = Keyword("let", nextPrec, curPrec) val `let` = Keyword("let", N, N) val `handle` = Keyword("handle", N, N) val `region` = Keyword("region", N, N) val `rec` = Keyword("rec", N, N) val `in` = Keyword("in", curPrec, curPrec) val `out` = Keyword("out", N, curPrec) val `set` = Keyword("set", N, curPrec) val `declare` = Keyword("declare", N, N) val `data` = Keyword("data", N, N) val `trait` = Keyword("trait", N, N) val `mixin` = Keyword("mixin", N, N) val `interface` = Keyword("interface", N, N) val `override` = Keyword("override", N, N) val `super` = Keyword("super", N, N) // val `namespace` = Keyword("namespace", N, N) val `using` = Keyword("using", N, N) val `module` = Keyword("module", N, N) val `object` = Keyword("object", N, N) val `open` = Keyword("open", N, curPrec) val `type` = Keyword("type", N, N) val `forall` = Keyword("forall", N, N) val `exists` = Keyword("exists", N, N) val `null` = Keyword("null", N, N) val `undefined` = Keyword("undefined", N, N) val `abstract` = Keyword("abstract", N, N) val `constructor` = Keyword("constructor", N, N) val `virtual` = Keyword("virtual", N, N) val `staged` = Keyword("staged", N, N) val `true` = Keyword("true", N, N) val `false` = Keyword("false", N, N) val `public` = Keyword("public", N, N) val `private` = Keyword("private", N, N) val `this` = Keyword("this", N, N) val `outer` = Keyword("outer", N, N) val `pattern` = Keyword("pattern", N, N) val `->` = Keyword("->", nextPrec, eqPrec) val maxPrec = curPrec // * The lambda operator is special: // * it should associate very strongly on the left and very loosely on the right // * so that we can write things like `f() |> x => x is 0` ie `(f()) |> (x => (x is 0))` // * Currently, the precedence of normal operators starts at the maximum precedence of keywords, // * so we need to start the precedence of `=>` to account for that. val `=>` = Keyword("=>", S(maxPrec.get + charPrecList.length), eqPrec) // * `new` is a strange keyword: // * it has a very high precedence that sits between that of selection and that of application. // * Indeed, `new Foo().bar` should parse as `(new Foo()).bar`, not `new (Foo().bar)`, // * but `new Foo.Bar` should parse as `new (Foo.Bar)`. val newRightPrec = S(maxPrec.get + charPrecList.length - 1) // * ^ maxPrec.get + charPrecList.length is the precedence of selection val `new` = Keyword("new", N, newRightPrec) val `new!` = Keyword("new!", N, newRightPrec) val `mut` = Keyword("mut", N, newRightPrec) // * `#` is both a prefix keyword (for directives like `#config(...)`) // * and an infix operator (for disambiguation like `Lazy#get()`). // * It has very high left precedence (like selection) when used as infix. // * The right precedence is set to the same level (very tight) so that infix `#` // * only picks up the immediately following identifier, e.g., `arr.Cls#d.f()` // * parses as `App(Sel(InfixApp(Sel(arr, Cls), #, d), f), ())`. // * In prefix position, this means `#config` only gets the name; `(args)` is // * consumed by `exprCont` and the elaborator reconstructs the directive. // * `canStartInfixOnNewLine = false` prevents it from being parsed as infix // * when it appears on a new line after an expression. val hashSelPrec = S(maxPrec.get + charPrecList.length) val `#` = Keyword("#", hashSelPrec, hashSelPrec, canStartInfixOnNewLine = false) val __ = Keyword("_", N, N) val modifiers = Set( `abstract`, mut, virtual, `override`, declare, public, `private`) type Prefix = `do`.type | `drop`.type | `not`.type | `new!`.type | `else`.type | `return`.type | `throw`.type | `import`.type type Infix = `is`.type | `:`.type | `->`.type | `=>`.type | `extends`.type | `restricts`.type | `as`.type | `do`.type | `where`.type | `with`.type | `and`.type | `or`.type | `then`.type | `else`.type | `#`.type type InfixSplittable = `is`.type | `:`.type | `->`.type | `=>`.type | `extends`.type | `restricts`.type | `as`.type | `do`.type | `where`.type | `with`.type | `of`.type type Ellipsis = `...`.type | `..`.type type IfLike = `if`.type | `while`.type type SplitLike = IfLike | `case`.type type LetLike = `let`.type | `set`.type type Modifier = `in`.type | `out`.type | `mut`.type | `abstract`.type | `declare`.type | `data`.type | `virtual`.type | `override`.type | `public`.type | `private`.type | `staged`.type ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax/Lexer.scala ================================================ package hkmc2 package syntax import scala.annotation.tailrec import mlscript._ import utils._, shorthands._ import Message.MessageContext import Diagnostic.Source.{Lexing, Parsing} import Lexer._ import Tree.{IntLit, DecLit, StrLit} class Lexer(origin: Origin, dbg: Bool)(using raise: Raise): val bytes: Array[Char] = origin.fph.blockStr.toArray private val length = bytes.length type State = Int private val isOpChar = Set( '!', '#', '%', '&', '*', '+', '-', '/', ':', '<', '=', '>', '?', '@', '\\', '^', '|', '~', '.', // ',', // ';' ) def isIdentFirstChar(c: Char): Bool = c.isLetter || c === '_' || c === '\'' def isIdentChar(c: Char): Bool = isIdentFirstChar(c) || isDigit(c) || c === '\'' def isHexDigit(c: Char): Bool = isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') def isOctDigit(c: Char): Bool = c >= '0' && c <= '7' def isBinDigit(c: Char): Bool = c === '0' || c === '1' def isDigit(c: Char): Bool = c >= '0' && c <= '9' def matches(i: Int, syntax: Str, start: Int): Bool = if start < syntax.length && i + start < length && bytes(i + start) === syntax(start) then matches(i, syntax, start + 1) else start >= syntax.length /* // TODO remove (unused) private val isNonStickyKeywordChar = Set( ',', ':', ';', ) */ private val isSymKeyword = Set( // "->", "=>", "=", ":", ";", // ",", "#", "`" // ".", // "<", // ">", ) @tailrec final def takeWhile(i: Int, cur: Ls[Char] = Nil)(pred: Char => Bool): (Str, Int) = if i < length && pred(bytes(i)) then takeWhile(i + 1, bytes(i) :: cur)(pred) else (cur.reverseIterator.mkString, i) final def num(i: Int): (Literal, Int) = def test(i: Int, p: Char => Bool): Bool = i < length && p(bytes(i)) def zero: IntLit = IntLit(BigInt(0)) /** Take a sequence of digits interleaved with underscores. */ def takeDigits(i: Int, pred: Char => Bool): (Opt[Str], Int) = @tailrec def rec(i: Int, acc: Ls[Char], firstSep: Bool, lastSep: Bool): (Str, Bool, Bool, Int) = if i < length then val c = bytes(i) if pred(c) then rec(i + 1, c :: acc, firstSep, false) else if c === '_' then rec(i + 1, acc, acc.isEmpty, true) else (acc.reverseIterator.mkString, firstSep, lastSep, i) else (acc.reverseIterator.mkString, firstSep, lastSep, i) val (str, firstSep, lastSep, j) = rec(i, Nil, false, false) if firstSep then raise(WarningReport( msg"Leading separator is not allowed" -> S(loc(i - 1, i)) :: Nil, source = Lexing)) if lastSep then raise(WarningReport( msg"Trailing separator is not allowed" -> S(loc(j - 1, j)) :: Nil, source = Lexing)) (if str.isEmpty then N else S(str), j) /** Take an integer and coverts to `BigInt`. Also checks if it is empty. */ def integer(i: Int, radix: Int, desc: Str, pred: Char => Bool): (IntLit, Int) = takeDigits(i, pred) match case (N, j) => raise(ErrorReport(msg"Expected at least one $desc digit" -> S(loc(i, i + 2)) :: Nil, source = Lexing)) (zero, j) case (S(str), j) => (IntLit(BigInt(str, radix)), j) def isDecimalStart(ch: Char) = ch === '.' || ch === 'e' || ch === 'E' /** Take a fraction part with an optional exponent part. Call at periods. */ def decimal(i: Int, integral: Str): (DecLit, Int) = lazy val msgPrefix = "Expected at least one digit after the " val (fraction, j) = if test(i, _ === '.') then takeDigits(i + 1, isDigit) match case (N, j) => // TODO should parse a selection here; as in `1.toString()` raise(ErrorReport(msg"$msgPrefix decimal point" -> S(loc(i + 1, i + 2)) :: Nil, source = Lexing)) ("", j) case (S(digits), j) => ("." + digits, j) else ("", i) val (exponent, k) = if test(j, ch => ch === 'e' || ch === 'E') then val (sign, k) = if test(j + 1, ch => ch === '+' || ch === '-') then (bytes(j + 1), j + 2) else ('+', j + 1) takeDigits(k, isDigit) match case (N, l) => raise(ErrorReport(msg"$msgPrefix exponent sign" -> S(loc(l - 1, l)) :: Nil, source = Lexing)) ("", l) case (S(digits), l) => ("E" + sign + digits, l) else ("", j) (DecLit(BigDecimal(integral + fraction + exponent)), k) if i < length then bytes(i) match case '0' if i + 1 < length => bytes(i + 1) match case 'x' => integer(i + 2, 16, "hexadecimal", isHexDigit) case 'o' => integer(i + 2, 8, "octal", isOctDigit) case 'b' => integer(i + 2, 2, "binary", isBinDigit) case '.' | 'E' | 'e' => decimal(i + 1, "0") case _ => integer(i, 10, "decimal", isDigit) case '0' => (zero, i + 1) case _ => takeDigits(i, isDigit) match case (N, j) => raise(ErrorReport(msg"Expected a numeric literal" -> S(loc(i, i + 1)) :: Nil, source = Lexing)) (zero, i) case (S(integral), j) => if j < length && isDecimalStart(bytes(j)) then decimal(j, integral) else (IntLit(BigInt(integral)), j) else raise(ErrorReport(msg"Expected a numeric literal instead of end of input" -> S(loc(i, i + 1)) :: Nil, source = Lexing)) (zero, i) // * Check the end of a string (either single quotation or triple quotation) final def closeStr(i: Int, isTriple: Bool): Int = if !isTriple && bytes.lift(i) === Some('"') then i + 1 else if isTriple && matches(i, "\"\"\"", 0) then i + 3 else raise(ErrorReport(msg"unclosed quotation mark" -> S(loc(i, i + 1)) :: Nil, source = Lexing)) i @tailrec final def str(i: Int, escapeMode: Bool, cur: Ls[Char] = Nil)(using triple: Bool): (Str, Int) = if escapeMode then if i < length then bytes(i) match case '\\' => str(i + 1, false, '\\' :: cur) case '"' => str(i + 1, false, '"' :: cur) case 'n' => str(i + 1, false, '\n' :: cur) case 't' => str(i + 1, false, '\t' :: cur) case 'r' => str(i + 1, false, '\r' :: cur) case 'b' => str(i + 1, false, '\b' :: cur) case 'f' => str(i + 1, false, '\f' :: cur) case 'u' => /** * This code handles two types of Unicode escape sequences: * * + Traditional Unicode escape: "\uXXXX" * - Consists of the characters '\' and 'u' followed by exactly * four hexadecimal digits. * - Example: "\u0041" represents the character 'A'. * + Unicode code point escape: "\u{XXXXXX}" * - Starts with "\u{" and ends with "}", allowing between 1 and * 6 hexadecimal digits in between. * - Example: "\u{1F600}" represents the grinning face emoji. * * In both cases, the scanned code point is validated to ensure * that it falls within the allowed Unicode range (0x0 to 0x10FFFF). * If any errors occur during scanning or conversion, such as * invalid characters, missing digits, or code points out of range, * a warning is raised with a precise location. */ @tailrec def scanHexDigits(idx: Int, maxDigits: Int, value: Int, count: Int): (Int, Int, Int) = if idx < length && isHexDigit(bytes(idx)) then if count < maxDigits then scanHexDigits(idx + 1, maxDigits, (value << 4) + Character.digit(bytes(idx), 16), count + 1) else scanHexDigits(idx + 1, maxDigits, value, count + 1) else (idx, value, count) if i + 1 < length && bytes(i + 1) == '{' then // Scan up to 6 hex digits after the opening brace. val (nextIdx, acc, count) = scanHexDigits(i + 2, 6, 0, 0) val result = if count == 0 then raise(WarningReport(msg"Expected at least one hexadecimal digit in Unicode escape sequence" -> S(loc(i + 1, nextIdx)) :: Nil, source = Lexing)) cur else if count > 6 then raise(WarningReport(msg"Too many hexadecimal digits in Unicode escape sequence" -> S(loc(nextIdx - (count - 6), nextIdx)) :: Nil, source = Lexing)) cur else if acc > 0x10FFFF then raise(WarningReport(msg"Unicode code point out of range: 0x${acc.toHexString}" -> S(loc(i + 2, nextIdx)) :: Nil, source = Lexing)) cur else Character.toChars(acc).reverseIterator.toList ::: cur // Close the brace. val finalIdx = if nextIdx >= length || bytes(nextIdx) != '}' then raise(WarningReport(msg"Unterminated Unicode escape sequence: missing '}'" -> S(loc(nextIdx, nextIdx)) :: Nil, source = Lexing)) nextIdx else nextIdx + 1 str(finalIdx, false, result) else // Process the traditional 4-digit Unicode escape (\uXXXX). val (nextIdx, acc, count) = scanHexDigits(i + 1, 4, 0, 0) if count =/= 4 then raise(WarningReport(msg"Invalid Unicode escape sequence: expected 4 hexadecimal digits but got ${count.toString}" -> S(loc(i + 1, nextIdx)) :: Nil, source = Lexing)) str(nextIdx, false, cur) else str(nextIdx, false, acc.toChar :: cur) case ch => raise(WarningReport(msg"Found invalid escape character" -> S(loc(i, i + 1)) :: Nil, source = Lexing)) str(i + 1, false, ch :: cur) else raise(ErrorReport(msg"Expected an escape character" -> S(loc(i, i + 1)) :: Nil, source = Lexing)) (cur.reverseIterator.mkString, i) else if triple then if i < length then bytes(i) match case '"' => if matches(i, "\"\"\"", 0) && !matches(i + 1, "\"\"\"", 0) then // Find the last """ (cur.reverseIterator.mkString, i) else str(i + 1, false, '"' :: cur) case ch => str(i + 1, false, ch :: cur) else (cur.reverseIterator.mkString, i) else if i < length then bytes(i) match case '\\' => str(i + 1, true, cur) case '"' | '\n' => (cur.reverseIterator.mkString, i) case ch => str(i + 1, false, ch :: cur) else (cur.reverseIterator.mkString, i) def loc(start: Int, end: Int): Loc = Loc(start, end, origin) def mkSymIdent(nme: Str) = nme match case ".." => SUSPENSION(false) case "..." => SUSPENSION(true) case _ => IDENT(nme, true) @tailrec final def lex(i: Int, ind: Ls[Int], acc: Ls[TokLoc]) (using qqList: Ls[BracketKind]): Ls[TokLoc] = if i >= length then acc.reverse else val c = bytes(i) def pe(msg: Message): Unit = raise(ErrorReport(msg -> S(loc(i, i + 1)) :: Nil, source = Lexing)) def isQuasiquoteOpening(i: Int): Bool = matches(i, BracketKind.Quasiquote.beg, 0) def isQuasiquoteTripleOpening(i: Int): Bool = matches(i, BracketKind.QuasiquoteTriple.beg, 0) def isUnquoteOpening(i: Int): Bool = matches(i, BracketKind.Unquote.beg, 0) def isQuasiquoteTripleClosing(i: Int): Bool = matches(i, BracketKind.QuasiquoteTriple.end, 0) inline def go(j: Int, tok: Token)(using qqList: Ls[BracketKind]) = lex(j, ind, (tok, loc(i, j)) :: acc) c match case ' ' => val (_, j) = takeWhile(i)(_ === ' ') go(j, SPACE) case ',' => val j = i + 1 go(j, COMMA) case '`' => go(i + 1, QUOTE) case 'c' if isQuasiquoteOpening(i) || isQuasiquoteTripleOpening(i) => val isTripleQuoteQQ = isQuasiquoteTripleOpening(i) val bracket_kind = if isTripleQuoteQQ then BracketKind.QuasiquoteTriple else BracketKind.Quasiquote val len = bracket_kind.beg.length go(i + len, OPEN_BRACKET(bracket_kind))(using bracket_kind :: qqList) case '$' if isUnquoteOpening(i) => go(i + 2, OPEN_BRACKET(BracketKind.Unquote)) case '$' if i + 1 < length && isIdentFirstChar(bytes(i + 1)) => val (n, j) = takeWhile(i + 1)(isIdentChar) go(j, BRACKETS(BracketKind.Unquote, ( IDENT(n, false), loc(i + 1, j) ) :: Nil)(loc(i, j))) case 'i' if i + 2 < length && bytes(i + 1) === 'd' && bytes(i + 2) === '"' => val (n, j) = takeWhile(i + 3)(isIdentChar) go(j + 1, if bytes(j) === '"' && !n.isEmpty() then ESC_IDENT(n) else { pe(msg"unexpected identifier escape"); ERROR } ) case ';' => val j = i + 1 // lex(j, ind, next(j, SEMI)) go(j, IDENT(";", true)) case '"' => val (isTripleQQ, cons) = qqList match case h :: t => (h === BracketKind.QuasiquoteTriple, t) case Nil => (false, Nil) if isTripleQQ && isQuasiquoteTripleClosing(i) then val length = BracketKind.QuasiquoteTriple.end.length go(i + length, CLOSE_BRACKET(BracketKind.QuasiquoteTriple))(using cons) else if !isTripleQQ && qqList.nonEmpty then go(i + 1, CLOSE_BRACKET(BracketKind.Quasiquote))(using cons) else val isTriple = matches(i, "\"\"\"", 0) val j = i + (if isTriple then 3 else 1) val (chars, k) = str(j, false)(using isTriple) val k2 = closeStr(k, isTriple) go(k2, LITVAL(StrLit(chars))) case '/' if bytes.lift(i + 1).contains('/') => val j = i + 2 val (txt, k) = takeWhile(j)(c => c =/= '\n') go(k, COMMENT(txt)) case '/' if bytes.lift(i + 1).contains('*') => // multiple-line comment val j = i + 2 var prev1 = '/'; var prev2 = '*' val (txt, k) = takeWhile(j)(c => { val res = prev1 =/= '*' || prev2 =/= '/' prev1 = prev2; prev2 = c res }) go(k, COMMENT(txt.dropRight(2))) case BracketKind(Left(k)) => go(i + 1, OPEN_BRACKET(k)) case BracketKind(Right(k)) => go(i + 1, CLOSE_BRACKET(k)) case '\n' => val j = i + 1 val (space, k) = takeWhile(j)(c => c === ' ' || c === '\n') val nextInd = space.reverseIterator.takeWhile(_ =/= '\n').size if ind.headOption.forall(_ < nextInd) && nextInd > 0 then lex(k, nextInd :: ind, (INDENT, loc(j, k)) :: acc) else val newIndBase = ind.dropWhile(_ > nextInd) val droppedNum = ind.size - newIndBase.size val hasNewIndent = newIndBase.headOption.forall(_ < nextInd) && nextInd > 0 val newInd = if hasNewIndent then nextInd :: newIndBase else newIndBase if dbg then println("dbg: " + bytes.drop(i).take(10).map(escapeChar).mkString+"...") println((ind, nextInd, newIndBase, droppedNum, hasNewIndent, newInd)) lex(k, newInd, if droppedNum > 0 then { if hasNewIndent then (INDENT, loc(j, k)) else (NEWLINE, loc(i, k)) } :: List.fill(droppedNum)((DEINDENT, loc(j-1, k))) ::: acc else (NEWLINE, loc(i, k)) :: acc ) case _ if isIdentFirstChar(c) => val (n, j) = takeWhile(i)(isIdentChar) go(j, IDENT(n, false)) case _ if isOpChar(c) => val (n, j) = takeWhile(i)(isOpChar) if n === "." && j >= length then go(j, PERIOD) // * There is no other character to parse after `.` else if (n === "." || n === "!") && j < length then inline def mkSelect(str: Str) = SELECT(str, n === "!") val nc = bytes(j) if isIdentFirstChar(nc) then val (name, k) = takeWhile(j)(isIdentChar) go(k, mkSelect(name)) else if // The first character is '0' and the next character is not a digit (nc === '0' && !(j + 1 < length && isDigit(bytes(j + 1)))) || ('0' < nc && nc <= '9') // The first character is a digit other than '0' then val (name, k) = takeWhile(j)(isDigit) go(k, mkSelect(name)) else go(j, if n === "." then PERIOD else // * Eventually we should also forbid `!` as an identifier, but it's currently used by BbML/InvalML mkSymIdent(n) ) else go(j, mkSymIdent(n)) case _ if isDigit(c) => val (lit, j) = num(i) go(j, LITVAL(lit)) case _ => pe(msg"unexpected character '${escapeChar(c)}'") go(i + 1, ERROR) def escapeChar(ch: Char): String = ch match case '\b' => "\\b" case '\t' => "\\t" case '\n' => "\\n" case '\f' => "\\f" case '\r' => "\\r" case '"' => "\\\"" case '\'' => "\\\'" case '\\' => "\\\\" case _ => if ch.isControl then "\\0" + Integer.toOctalString(ch.toInt) else String.valueOf(ch) lazy val tokens: Ls[Token -> Loc] = lex(0, Nil, Nil)(using Nil) /** Converts the lexed tokens into structured tokens. */ lazy val bracketedTokens: Ls[Stroken -> Loc] = import BracketKind._ def go( toks: Ls[Token -> Loc], canStartAngles: Bool, stack: Ls[BracketKind -> Loc -> Ls[Stroken -> Loc]], swallowedInd: Int, // * Number of previous indentations that were not closed by deindents but by closing other brackets acc: Ls[Stroken -> Loc], ): Ls[Stroken -> Loc] = toks match case (SUSPENSION(true), l0) :: Nil => // * This is an ugly special-case to handle things like `module M with ...` // * where there is no actual body after the `...`. // * It can't be handled in the parser because this is only valid at the top-level, // * not within brackets, as in `(arg0, ...) => blah`. go(OPEN_BRACKET(Indent) -> l0 :: LITVAL(Tree.UnitLit(false)) -> l0 :: Nil, false, stack, swallowedInd, acc) case (QUOTE, l0) :: (IDENT("<", true), l1) :: rest => go(rest, false, stack, swallowedInd, (IDENT("<", true), l1) :: (QUOTE, l0) :: acc) case (QUOTE, l0) :: (IDENT(">", true), l1) :: rest => go(rest, false, stack, swallowedInd, (IDENT(">", true), l1) :: (QUOTE, l0) :: acc) case (OPEN_BRACKET(k), l0) :: rest => go(rest, false, k -> l0 -> acc :: stack, swallowedInd, Nil) case (NEWLINE, l1) :: rest if swallowedInd > 0 => go((OPEN_BRACKET(Indent), l1) :: rest, false, stack, swallowedInd - 1, acc) case (CLOSE_BRACKET(Indent), l1) :: rest if swallowedInd > 0 => go(rest, false, stack, swallowedInd - 1, acc) case (CLOSE_BRACKET(k1), l1) :: rest => stack match case ((k0 @ Indent, l0), oldAcc) :: oldStack if k1 =/= Indent => // * Sometimes, open/close parentheses are interleaved with indent/deindent; eg in // * module P with // * ( // * 2) // * 1 // * which results in token stream `|module| |P| |with|→|(|→|2|)|←|↵|1|`. // * So we temporarily swallow indentations until we reach a NL or deindent. go(toks, false, oldStack, swallowedInd + 1, BRACKETS(k0, acc.reverse)(l0.right ++ l1.left) -> (l0 ++ l1) :: oldAcc) case ((Indent, loc), oldAcc) :: stack if k1 === Indent && acc.forall { case (SPACE | NEWLINE, _) => true; case _ => false } => // * Ignore empty indented blocks: go(rest, false, stack, swallowedInd, oldAcc) case ((k0, l0), oldAcc) :: stack => if k0 =/= k1 && !(k0 === Unquote && k1 === Curly) then raise(ErrorReport(msg"Mistmatched closing ${k1.name}" -> S(l1) :: msg"does not correspond to opening ${k0.name}" -> S(l0) :: Nil, source = Parsing)) val accr = acc match // * This is to flatten nested brackets like `BRACES(INDENT(acc))` // * ie to make: // * foo { // * a // * b // * c // * } // * parse the same as: // * foo {a, b, c} case (BRACKETS(Indent, acc), _) :: Nil if k0 is Curly => acc case (NEWLINE, _) :: (BRACKETS(Indent, acc), _) :: Nil if k0 is Curly => acc case _ => acc.reverse val accr2 = accr.dropWhile(_._1 === SPACE) go(rest, true, stack, swallowedInd, BRACKETS(k0, accr2)(l0.right ++ l1.left) -> (l0 ++ l1) :: oldAcc) case Nil => raise(ErrorReport(msg"Unexpected closing ${k1.name}" -> S(l1) :: Nil, source = Parsing)) go(rest, false, stack, swallowedInd, acc) case (INDENT, loc) :: rest => go(OPEN_BRACKET(Indent) -> loc :: rest, false, stack, swallowedInd, acc) case (DEINDENT, loc) :: rest => go(CLOSE_BRACKET(Indent) -> loc :: rest, false, stack, swallowedInd, acc) case (IDENT("<", true), loc) :: rest if canStartAngles => go(OPEN_BRACKET(Angle) -> loc :: rest, false, stack, swallowedInd, acc) case (IDENT(">", true), loc) :: rest if canStartAngles && (stack match { case ((Angle, _), _) :: _ => true case _ => false }) => go(CLOSE_BRACKET(Angle) -> loc :: rest, false, stack, swallowedInd, acc) case (IDENT(id, true), loc) :: rest if (canStartAngles && id.forall(_ == '>') && id.length > 1 && (stack match { case ((Angle, _), _) :: _ => true case _ => false })) => // split `>>` to `>` and `>` so that code like `A>` can be parsed correctly go((CLOSE_BRACKET(Angle) -> loc.left) :: (IDENT(id.drop(1), true) -> loc) :: rest, false, stack, swallowedInd, acc) case ((tk @ IDENT(">", true), loc)) :: rest if canStartAngles => raise(WarningReport( msg"This looks like an angle bracket, but it does not close any angle bracket section" -> S(loc) :: msg"Add spaces around it if you intended to use `<` as an operator" -> N :: Nil, source = Parsing)) go(rest, false, stack,swallowedInd, tk -> loc :: acc) case (tk: Stroken, loc) :: rest => go(rest, tk match { case SPACE | NEWLINE => false case _ => true }, stack, swallowedInd, tk -> loc :: acc) case Nil => stack match case ((Indent, loc), oldAcc) :: _ => go(CLOSE_BRACKET(Indent) -> loc/*FIXME not proper loc...*/ :: Nil, false, stack, swallowedInd, acc) case ((k, l0), oldAcc) :: stack => raise(ErrorReport(msg"Unmatched opening ${k.name}" -> S(l0) :: ( if k === Angle then msg"Note that `<` without spaces around it is considered as an angle bracket and not as an operator" -> N :: Nil else Nil ), source = Parsing)) (oldAcc ::: acc).reverse case Nil => acc.reverse go(tokens, false, Nil, 0, Nil) object Lexer: type TokLoc = (Token, Loc) val keywords: Set[Str] = Set( "if", "then", "else", "case", "fun", "val", "var", // "is", // "as", "of", // "and", // "or", "let", "rec", "in", // "any", // "all", "mut", "set", "do", "while", "declare", "class", "trait", "mixin", "interface", "extends", "override", "super", "new", "namespace", "module", "type", "where", "forall", "exists", "in", "out", "null", "undefined", "abstract", "constructor", "virtual", "staged" ) private val SEP = "┊" def printToken(tl: TokLoc): Str = tl match case (SPACE, _) => " " case (COMMA, _) => "`,`" case (PERIOD, _) => "`.`" case (NEWLINE, _) => "↵" case (INDENT, _) => "→" case (DEINDENT, _) => "←" case (ERROR, _) => "" case (QUOTE, _) => "`" case (LITVAL(lv), _) => lv.idStr // case (KEYWRD(name: String), _) => "#" + name case (IDENT(name, symbolic), _) => name case (SELECT(name, false), _) => "." + name case (SELECT(name, true), _) => "!" + name case (OPEN_BRACKET(k), _) => k.beg case (CLOSE_BRACKET(k), _) => k.end case (BRACKETS(k, contents), _) => k.beg + "⟨" + printTokens(contents) + "⟩" + k.end case (COMMENT(text), _) => "/*" + text + "*/" case (SUSPENSION(true), _) => "..." case (SUSPENSION(false), _) => ".." case (ESC_IDENT(name), _) => name def printTokens(ts: Ls[TokLoc]): Str = ts.iterator.map(printToken).mkString(SEP, SEP, SEP) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala ================================================ package hkmc2 package syntax import sourcecode.{Name, Line} import mlscript.utils.*, shorthands.* import hkmc2.Message._ import BracketKind._ import syntax.Tree.Keywrd import semantics.Elaborator.State // * TODO: add lookahead to Expr as a PartialFunction[Ls[Token], Bool] enum Alt[+A]: case Kw[Rest, +Res](kw: Keyword)(val rest: ParseRule[Rest])(val k: (Keywrd[kw.type], Rest) => Res) extends Alt[Res] case Expr[Rest, +Res](rest: ParseRule[Rest])(val k: (Tree, Rest) => Res) extends Alt[Res] case Blk[Rest, +Res](rest: ParseRule[Rest])(val k: (Tree, Rest) => Res) extends Alt[Res] case End()(val a: () => A) def map[B](f: A => B): Alt[B] = this match case k: Kw[rest, A] => Kw(k.kw)(k.rest)((kw, rest) => f(k.k(kw, rest))) case e: Expr[rest, A] => Expr(e.rest)((tree, rest) => f(e.k(tree, rest))) case e: End[?] => End()(() => f(e.a())) case b: Blk[rest, A] => Blk(b.rest)((tree, rest) => f(b.k(tree, rest))) def end[A](a: => A): Alt[A] = Alt.End()(() => a) def discard[A]: (A, Unit) => A = { case (a, _) => a } def extendLoc[T <: Tree]: (Keywrd[?], T) => T = { case (k, a) => a.mkLocWith(k) } def discardKw[Rest](kw: Keyword)(rest: ParseRule[Rest]): Alt[Rest] = Alt.Kw(kw)(rest)((_, rest) => rest) def keepKw[Rest](kw: Keyword)(rest: ParseRule[Rest]): Alt[Keywrd[kw.type] -> Rest] = Alt.Kw(kw)(rest)((k, rest) => k -> rest) class ParseRule[+A](val name: Str, val omitAltsStr: Bool = false)(val alts: Alt[A]*): def map[B](f: A => B): ParseRule[B] = ParseRule(name)(alts.map(_.map(f))*) override def toString: Str = s"$name ::= " + alts.mkString(" | ") lazy val emptyAlt = alts.collectFirst { case e: Alt.End[?] => e.a } lazy val kwAlts = alts.collect { case alt: Alt.Kw[rst, A] => alt.kw.name -> alt }.toMap def getKwAlt(k: Keyword, loc: Opt[Loc]) = kwAlts.get(k.name).map: kwAlt => kwAlt.rest.map(a => kwAlt.k(new Keywrd(k.asInstanceOf[kwAlt.kw.type]).withLoc(loc), a)) lazy val exprAlt = alts.collectFirst { case alt: Alt.Expr[rst, A] => alt } lazy val blkAlt = alts.collectFirst { case alt: Alt.Blk[rst, A] => alt } def mkAfterStr: Str = if omitAltsStr then "in this position" else s"after $name" def whatComesAfter: Str = if omitAltsStr then name else alts.map: case Alt.Kw(kw) => s"'${kw.name}' keyword" case Alt.Expr(rest) => "expression" case Alt.Blk(rest) => "block" case Alt.End() => "end of input" .toList match case Nil => "nothing at all" case str :: Nil => str case str1 :: str2 :: Nil => s"$str1 or $str2" case strs => strs.init.mkString(", ") + ", or " + strs.last end ParseRule class ParseRules(using State): import Keyword.* import Alt.* import Tree.* val standaloneExpr = Expr(ParseRule("expression")(end(())))((l, _: Unit) => l) def prefixed(kw: Keyword.Prefix): Alt[Tree] = prefixed(kw, standaloneExpr) def prefixed(kw: Keyword.Prefix, body: Alt[Tree]) = Kw(kw)(ParseRule(s"prefix keyword '${kw.name}'")(body))((k, r) => Tree.PrefixApp(k, r)) def modified(kw: Keyword.Modifier): Alt[Tree] = modified(kw, standaloneExpr) def modified(kw: Keyword.Modifier, body: Alt[Tree]) = Kw(kw)(ParseRule(s"modifier keyword '${kw.name}'")(body)): case (k: Keywrd[Keyword.Modifier], r) => Tree.Modified(k, r) def exprOrBlk[Rest, Res](body: ParseRule[Rest])(k: (Tree, Rest) => Res): List[Alt[Res]] = Expr(body)(k) :: Blk(body)(k) :: Nil def standaloneExprOrBlk[Rest, Res]: List[Alt[Tree]] = standaloneExpr :: Blk(ParseRule("block")(end(())))((l, _: Unit) => l) :: Nil val typeDeclTemplate: Alt[Opt[Tree]] = end(N) /* // * What we had before we allowed parsing juxtapositions def termDefBody(k: TermDefKind): ParseRule[Tree] = ParseRule(s"'${k.str}' binding keyword")( Expr( ParseRule(s"'${k.str}' binding head")( Expr( ParseRule(s"'${k.str}' binding name part")( funBody(k).map(b => (b, N)), funSign(k), ) ) { case (sym, (sign, rhs)) => (S(sym), sign, rhs) }, funBody(k).map(b => (N, N, b)), funSign(k).map(sb => (N, sb._1, sb._2)), ) ) { case (lhs, (N, sign, rhs)) => TermDef(k, N, S(lhs), sign, rhs) case (lhs, (sym, sign, rhs)) => TermDef(k, S(lhs), sym, sign, rhs) } ) */ def termDefBody(k: TermDefKind): ParseRule[Tree] = ParseRule(s"'${k.str}' binding keyword")( Expr( ParseRule(s"'${k.str}' binding head")( funBody(k), end(N), ) ) { case (lhs, rhs) => TermDef(k, lhs, rhs) } ) def typeDeclBody(k: TypeDefKind): ParseRule[TypeDef] = ParseRule("type declaration keyword"): Expr( ParseRule("type declaration head"): end(()) ): case (head, ()) => TypeDef(k, head, N) def letLike(kw: Keyword.LetLike) = keepKw(kw)( ParseRule(s"'${kw.name}' binding keyword")( Expr( ParseRule(s"'${kw.name}' binding head")( discardKw(`=`): ParseRule(s"'${kw.name}' binding equals sign")( exprOrBlk( ParseRule(s"'${kw.name}' binding right-hand side")( discardKw(`in`): ParseRule(s"'${kw.name}' binding `in` clause")( exprOrBlk( ParseRule(s"'${kw.name}' binding body"){end{()}} ){ (body, _: Unit) => S(body) }* ), end(N) ) ) { (rhs, body) => (S(rhs), body) }* ), discardKw(`in`): ParseRule(s"'${kw.name}' binding `in` clause")( exprOrBlk( ParseRule(s"'${kw.name}' binding body")(end(())) ){ (body, _: Unit) => N -> S(body) }* ) , end(N -> N) ) ) { case (lhs, (rhs, body)) => (lhs, rhs, body) } ) ).map { case (kw, (lhs, rhs, body)) => LetLike(kw, lhs, rhs, body) } def ifLike(kw: Keyword.IfLike): Alt[Tree] = Kw(kw)( ParseRule(s"'${kw.name}' keyword")( Expr( ParseRule(s"'${kw.name}' expression")( end(N), Kw(`else`)( ParseRule(s"`else` keyword")( exprOrBlk(ParseRule(s"`else` expression")(end(()))): discard * ) ) { case (elsKw, default) => S((elsKw, default)) } ) ): case (split, S((elsKw, default))) => val clause = PrefixApp(elsKw, default) val items = split match case Block(stmts) => stmts.appended(clause) case _ => split :: clause :: Nil Block(items) case (split, N) => split , Blk( ParseRule(s"'${kw.name}' block")(end(())) )(discard) ) ) { case (kw, body) => IfLike(kw, body) } def typeAliasLike(kw: Keyword, kind: TypeDefKind): Alt[TypeDef] = keepKw(kw): ParseRule(s"${kind.desc} declaration"): Expr( ParseRule(s"${kind.desc} head")( discardKw(`=`): ParseRule(s"${kind.desc} declaration equals sign")( exprOrBlk( ParseRule(s"${kind.desc} declaration right-hand side")( end(()) ) ) { case (rhs, ()) => S(rhs) }*), end(N), ) ) { (lhs, rhs) => TypeDef(kind, lhs, rhs) } .map { case (kw, t) => t.mkLocWith(kw) } val prefixRules: ParseRule[Tree] = ParseRule("start of expression", omitAltsStr = true)( letLike(`let`), letLike(`set`), keepKw(`handle`): ParseRule("'handle' binding keyword"): Expr( ParseRule("'handle' binding head"): discardKw(`=`): ParseRule("'handle' binding equals sign"): Expr( ParseRule("'handle' binding class name"): discardKw(`with`): ParseRule("type declaration body")( Blk( ParseRule("type declaration block")( discardKw(`in`): ParseRule(s"'handle' binding `in` clause")( exprOrBlk(ParseRule(s"'handle' binding body")(end(())))((body, _: Unit) => S(body))* ), end(N)) ) { case (res, t) => (S(res), t) } ) ) { case (rhs, (S(defs), body)) => (rhs, defs, body) } ) { case (lhs, (rhs, defs, body)) => Hndl(lhs, rhs, defs, body) } .map { case (kw, h) => h.mkLocWith(kw) } , keepKw(`new`): val withRefinement = discardKw(`with`)( ParseRule("'new' body")( Blk(ParseRule("'new' expression")(end(()))) { case (res: Block // FIXME: can it be something else? , ()) => S(res) } ) ) ParseRule("`new` keyword")( ( withRefinement.map(rfto => LexicalNew(N, rfto)) :: exprOrBlk(ParseRule("`new` expression")( withRefinement, end(N), ))((body, rfto) => LexicalNew(S(body), rfto)) )* ) .map { case (kw, nu) => nu.mkLocWith(kw) } , Kw(`in`)( ParseRule("modifier keyword `in`"): Expr( ParseRule("`in` expression")( Kw(`out`)(ParseRule(s"modifier keyword `out`")(standaloneExpr)): case (kw, s) => S(Tree.Modified(kw, s)), end(N), ) ) { case (lhs, S(rhs)) => Tup(lhs :: rhs :: Nil) case (lhs, N) => lhs } ) { case (kw, Tup(lhs :: rhs :: Nil)) => Tup(Modified(kw, lhs) :: rhs :: Nil) case (kw, lhs) => Modified(kw, lhs) }, ifLike(`if`), ifLike(`while`), Kw(`assert`)( ParseRule(s"'assert' keyword")( exprOrBlk( ParseRule(s"'assert' expression")( end(N), Kw(`else`)( ParseRule(s"`else` keyword")( exprOrBlk(ParseRule(s"`else` expression")(end(()))): discard * ) ) { case (elsKw, default) => S((elsKw, default)) } ) )(_ -> _)* ) ): case (kw, (rhs, els)) => Assert(kw, rhs, N, els) , Kw(`else`)( ParseRule("`else` clause")( Expr(ParseRule("`else` expression")(end(())))(discard), Blk(ParseRule("`else` expression")(end(())))(discard) ) ) { case (kwrd, tree) => PrefixApp(kwrd, tree) }, keepKw(`case`): ParseRule("`case` keyword")( exprOrBlk(ParseRule("`case` branches")(end(())))((body, _: Unit) => body)* ) .map { case (kw, body) => Case(kw, body) } , keepKw(`region`): ParseRule("`region` keyword"): Expr( ParseRule("`region` declaration"): discardKw(`in`): ParseRule("`in` keyword")( Expr(ParseRule("'region' expression")(end(())))((body, _: Unit) => body), Blk(ParseRule("'region' block")(end(())))((body, _: Unit) => body) ) ) { case (name, body) => Region(name, body) } .map { case (kw, r) => r.mkLocWith(kw) } , keepKw(`outer`): ParseRule("outer binding operator")( Expr( ParseRule("`outer` binding name")(end(())) ){ (body, _: Unit) => Outer(S(body)) }, end(Outer(N)) ) .map { case (kw, o) => o.mkLocWith(kw) } , keepKw(`constructor`): ParseRule("constructor keyword"): Blk( ParseRule(s"constructor block")(end(())) ) { case (body, _) => Constructor(body) } .map { case (kw, c) => c.mkLocWith(kw) } , Kw(`fun`)(termDefBody(Fun))(extendLoc), Kw(`val`)(termDefBody(ImmutVal))(extendLoc), Kw(`using`)(termDefBody(Ins))(extendLoc), typeAliasLike(`type`, Als), typeAliasLike(`pattern`, Pat), Kw(`class`)(typeDeclBody(Cls))(extendLoc), Kw(`trait`)(typeDeclBody(Trt))(extendLoc), Kw(`module`)(typeDeclBody(Mod))(extendLoc), Kw(`object`)(typeDeclBody(Obj))(extendLoc), keepKw(`open`): ParseRule("'open' keyword")( exprOrBlk(ParseRule("'open' declaration")(end(()))){ case (body, _) => Open(body)}*) .map { case (kw, o) => o.mkLocWith(kw) } , modified(`abstract`, keepKw(`class`)(typeDeclBody(Cls)).map { case (kw, c) => c.mkLocWith(kw) }), Kw(`mut`)(ParseRule(s"'mut' keyword")(standaloneExprOrBlk*)) { case (kw, body) => Tree.Modified(kw, body) }, Kw(`do`)( ParseRule(s"'do' keyword")( exprOrBlk(ParseRule(s"'do' body")(end(()))): discard *) ) { case (kw, body) => Tree.PrefixApp(kw, body) }, Kw(`return`)( ParseRule(s"'return' keyword")( // * The Block alternative is important, otherwise // * > return // * > print("returning...") // * > x // * is terated as a keyword stutter: { return print("returning..."); return x } end(Unt()) :: exprOrBlk(ParseRule(s"'return' body")(end(()))): discard *) ) { case (kw, body) => Tree.PrefixApp(kw, body) }, prefixed(`drop`), prefixed(`not`), prefixed(`new!`), prefixed(`throw`), prefixed(`import`), modified(`virtual`), modified(`override`), modified(`declare`), modified(`data`), modified(`public`), modified(`private`), modified(`out`), modified(`staged`), singleKw(`true`)(BoolLit(true)), singleKw(`false`)(BoolLit(false)), singleKw(`undefined`)(UnitLit(false)), singleKw(`null`)(UnitLit(true)), singleKw(`this`)(Ident("this")), singleKw(Keyword.__)(Under()), Kw(`#`)( ParseRule(s"'#' directive keyword")( Expr(ParseRule(s"'#' directive body")(end(()))){ case (body, ()) => body } ) ) { case (kw, body) => body match case App(prefix, args) => Directive(prefix, args).mkLocWith(kw) case _ => Directive(body, Tup(Nil)).mkLocWith(kw) }, standaloneExpr, ) def singleKw[T <: Tree](kw: Keyword)(v: => T): Alt[T] = Kw(kw)(ParseRule(s"'${kw.name}' keyword")(end(v)))((kwrd, v) => v.withLocOf(kwrd)) val prefixRulesAllowIndentedBlock: ParseRule[Tree] = ParseRule(prefixRules.name, prefixRules.omitAltsStr)(prefixRules.alts :+ (Blk( ParseRule("block"): end(()) ) { case (res, ()) => res })*) /* def funSign(k: TermDefKind): Alt[(S[Tree], Opt[Tree])] = Kw(`:`): ParseRule(s"'${k.str}' binding colon"): Expr( ParseRule(s"'${k.str}' binding signature")( funBody(k), end(N), ) ) { case (sign, rhs) => (S(sign), rhs) } */ def funBody(k: TermDefKind): Alt[S[Tree]] = discardKw(`=`): ParseRule(s"'${k.str}' binding equals sign")( Expr( ParseRule(s"'${k.str}' binding right-hand side")(end(())) ) { case (rhs, ()) => S(rhs) } , Blk( ParseRule(s"'${k.str}' binding block")(end(())) ) { case (rhs, _) => S(rhs) } ) def keywordThenTree[A, K <: Keyword](kw: K)(k: (Keywrd[kw.type], Tree) => A): Alt[A] = keepKw(kw): ParseRule(s"'${kw}' operator")( Expr(ParseRule(s"'${kw}' operator right-hand side")(end(()))): case (tree, ()) => tree // * Interestingly, this does not seem to change anything: // exprOrBlk(ParseRule(s"'${kw}' operator right-hand side")(End(())))(k)* ) .map(k.tupled) def makeInfixRule[K <: Infix](kw: K): Alt[Tree => Tree] = keywordThenTree(kw): case (kw, rhs) => lhs => InfixApp(lhs, kw, rhs) val infixRules: ParseRule[Tree => Tree] = ParseRule("continuation of expression")( makeInfixRule(`and`), makeInfixRule(`or`), makeInfixRule(`is`), makeInfixRule(`as`), makeInfixRule(`then`), makeInfixRule(`:`), makeInfixRule(`extends`), makeInfixRule(`restricts`), makeInfixRule(`do`), makeInfixRule(`where`), makeInfixRule(`with`), makeInfixRule(`#`), ) end ParseRules ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala ================================================ package hkmc2 package syntax import scala.util.boundary import sourcecode.{Name, Line} import mlscript.utils.*, shorthands.* import hkmc2.Message._ import BracketKind._ import Tree.* import Parser.* import scala.annotation.tailrec import Keyword.`let` import hkmc2.syntax.Keyword.Ellipsis import semantics.Elaborator.State val charPrecList: List[Str] = List( "", // `of` rhs ",", // ^ for keywords ";", // "=", // higher than || means `a == 1 || b` parses surprisingly "@", ":", "|", "&", "=", // "/ \\", "^", // "= !", "!", "< >", "+ -", "* / %", "~", "", // Precedence of prefix operators "", // Precedence of application "", // Precedence of `new` // ".", ". \\", ) object Parser: type TokLoc = (Stroken, Loc) type LTL = Ls[TokLoc] private val MinPrec = 0 private val NoElsePrec = MinPrec + 1 def verbose = // true false private val precOf: Map[Char,Int] = charPrecList.zipWithIndex.flatMap { case (cs, i) => cs.filterNot(_ === ' ').map(_ -> (i + Keyword.maxPrec.get)) }.toMap.withDefaultValue(Int.MaxValue) // * Note: keywords without a specified right precedence are now assumed to have right precedence `CommaPrecNext` // private val CommaPrec = prec(',') private val CommaPrec = 0 private val CommaPrecNext = CommaPrec + 1 private val CommaPrecNext2 = CommaPrecNext + 1 private val SelPrec = precOf('.') private val AppPrec = SelPrec - 1 private val PrefixOpsPrec = AppPrec - 1 // * Annotation body precedence: above all keyword infix ops (like `as`, `is`) // * but below character-based operators (like `+`, `;`). private val AnnotBodyPrec = Keyword.maxPrec.get + 1 final def opCharPrec(opChar: Char): Int = precOf(opChar) final def opPrec(opStr: Str): (Int, Int) = opStr match { case "+." | "-." | "*." => (precOf(opStr.head), precOf(opStr.head)) case _ if opStr.exists(_.isLetter) => (Keyword.maxPrec.get, Keyword.maxPrec.get) case _ => val r = opStr.last (precOf(opStr.head), precOf(r) - (if r === ',' || r === ':' then 1 else 0)) } val prefixOps: Set[Str] = Set("!", "+", "-", "~", "@", "|", "&") type Indent_Curly = Curly.type | Indent.type type NEWLINE_COMMA = NEWLINE.type | COMMA.type object KEYWORD: def unapply(t: IDENT): Opt[Keyword] = t match case IDENT(nme, sym) => Keyword.all.get(nme) // case IDENT(nme, sym) => Keyword.all.get(nme).map(_.name) // case IDENT(nme, sym) if Keyword.all.contains(nme) => S(nme) object OP: def unapply(t: IDENT): Opt[Str] = t match case IDENT(nme, true) if !Keyword.all.contains(nme) => S(nme) case _ => N object ALPHA: def unapply(t: Token): Opt[Str] = t match case IDENT(nme, false) if !Keyword.all.contains(nme) => S(nme) case _ => N object NOISE: def get(ts: Ls[TokLoc]): Ls[TokLoc] = ts match case (SPACE, _) :: rest => get(rest) case (COMMENT(_), _) :: rest => get(rest) case (NEWLINE, _) :: (COMMENT(_), _) :: rest => get(rest) case (BRACKETS(Indent, NOISE(Nil)), _) :: rest => get(rest) case _ => ts def unapply(ts: Ls[TokLoc]): S[Ls[TokLoc]] = S(get(ts)) extension (loc: Loc) def showStart: String = loc.origin.fph.getLineColAt(loc.spanStart) match case (ln, _, col) => s"Ln $ln Col $col" def showEnd: String = loc.origin.fph.getLineColAt(loc.spanStart) match case (ln, _, col) => s"Ln $ln Col $col" extension (trees: Ls[Tree]) /** Note that the innermost annotation is the leftmost. */ def annotate(tree: Tree): Tree = trees.foldLeft(tree): case (target, annotation) => Annotated(annotation, target) end Parser import Parser._ abstract class Parser( origin: Origin, tokens: Ls[TokLoc], rules: ParseRules, raiseFun: Diagnostic => Unit, val dbg: Bool, // fallbackLoc: Opt[Loc], description: Str = "input", )(using State): outer => import rules.* object PrefixRule: def unapply(t: IDENT): Opt[(Keyword, ParseRule[Tree])] = t match // * the Loc of this Keywrd is added at the call site case KEYWORD(kw) => prefixRules.getKwAlt(kw, N).map: subRule => kw -> subRule case _ => N protected def doPrintDbg(msg: => Str): Unit protected def printDbg(msg: => Any): Unit = doPrintDbg("│ " * this.indent + msg) protected var indent = 0 private var _cur: Ls[TokLoc] = preprocessTokens(tokens) private def preprocessTokens(tokens: Ls[TokLoc]): Ls[TokLoc] = tokens match case (IDENT("new", false), l1) :: (IDENT("!", true), l2) :: rest => (IDENT("new!", false), l1 ++ l2) :: preprocessTokens(rest) // * Remove empty indented sections case (BRACKETS(Indent, toks), _) :: rest if toks.forall: case (NEWLINE | SPACE, _) => true case _ => false => preprocessTokens(rest) // * Expands end-of-line suspensions that introduce implied indentation, // * skipping NOISE tokens between `...` and NEWLINE (eg `... // hello\n body`) // * Note: using `NEWLINE_COMMA` instead of `NEWLINE` causes misparsing of things like `fun foo(..., ...)` case (SUSPENSION(true), l0) :: NOISE((NEWLINE, l1) :: rest) => val outerLoc = l0.left ++ rest.lastOption.map(_._2.right) val innerLoc = l1.right ++ rest.lastOption.map(_._2.left) BRACKETS(Indent, preprocessTokens(rest))(innerLoc) -> outerLoc :: Nil case tl :: rest => val rest2 = preprocessTokens(rest) if rest2 is rest then tokens else tl :: rest2 case Nil => tokens private def wrap[R](args: => Any)(using l: Line, n: Name)(mkRes: => R): R = printDbg(s"@ ${n.value}${args match { case it: Iterable[_] => it.mkString("(", ",", ")") case _: Product => args case _ => s"($args)" }} [at syntax/Parser.scala:${l.value}]") val res = try indent += 1 mkRes finally indent -= 1 printDbg(s"= $res") res final def rec(tokens: Ls[Stroken -> Loc], fallbackLoc: Opt[Loc], description: Str): Parser = new Parser(origin, tokens, rules, raiseFun, dbg // , fallbackLoc, description ): def doPrintDbg(msg: => Str): Unit = outer.printDbg("> " + msg) def resetCur(newCur: Ls[TokLoc]): Unit = _cur = newCur // _modifiersCache = ModifierSet.empty private lazy val lastLoc = tokens.lastOption.map(_._2.right)//.orElse(fallbackLoc) private def summarizeCur = Lexer.printTokens(_cur.take(5)) + (if _cur.sizeIs > 5 then "..." else "") private def cur(implicit l: Line, n: Name) = if dbg then printDbg(s"? ${n.value}\t\tinspects ${summarizeCur} [at syntax/Parser.scala:${l.value}]") while !_cur.isEmpty && (_cur.head._1 match { case COMMENT(_) => true case _ => false }) do consume _cur private def cur_=(using l: Line, n: Name)(newCur: Ls[TokLoc]) = if dbg then printDbg(s"! ${n.value}\t\tresets ${Lexer.printTokens(newCur)} [at syntax/Parser.scala:${l.value}]") _cur = newCur final def consume(implicit l: Line, n: Name): Unit = if dbg then printDbg(s"! ${n.value}\t\tconsumes ${Lexer.printTokens(_cur.take(1))} [at syntax/Parser.scala:${l.value}]") resetCur(_cur.tailOption.getOrElse(Nil)) // FIXME throw error if empty? private def yeetSpaces(using Line, Name): Ls[TokLoc] = _cur = NOISE.get(_cur) _cur // final def raise(mkDiag: => Diagnostic)(implicit fe: FoundErr = false): Unit = // if (!foundErr) raiseFun(mkDiag) final def raise(mkDiag: => Diagnostic): Unit = raiseFun(mkDiag) private def errExpr = Tree.Error().withLoc(cur.headOption.fold(lastLoc)(_._2 |> some)) private def empty = Tree.Empty().withLoc(cur.headOption.fold(lastLoc)(_._2.left |> some)) final def err(msgs: Ls[Message -> Opt[Loc]])(implicit l: Line, n: Name): Unit = printDbg(s"Error [at syntax/Parser.scala:${l.value}]") raise(ErrorReport(msgs, source = Diagnostic.Source.Parsing)) final def parseAll[R](parser: => R): R = val res = parser cur match case c @ (tk, tkl) :: _ => val (relevantToken, rl) = c.dropWhile(_._1 === SPACE).headOption.getOrElse(tk, tkl) err(msg"Expected end of input; found ${relevantToken.describe} instead" -> S(rl) :: Nil) case Nil => () res final def concludeWith[R](using l: Line)(f: this.type => R): R = wrap(())(concludeWithImpl(f)) final def concludeWithImpl[R](f: this.type => R): R = val res = f(this) cur.dropWhile(tk => (tk._1 === SPACE || tk._1 === NEWLINE) && { consume; true }) match case c @ (tk, tkl) :: _ => val (relevantToken, rl) = c.dropWhile(_._1 === SPACE).headOption.getOrElse(tk, tkl) err(msg"Unexpected ${relevantToken.describe} here" -> S(rl) :: Nil) case Nil => () printDbg(s"Concluded with $res") res final def continueWith[R](f: this.type => R): (R, Ls[TokLoc]) = val res = f(this) val rest = cur.dropWhile(tk => (tk._1 === SPACE || tk._1 === NEWLINE) && { consume; true }) printDbg(s"Continued with $res, $rest") (res, rest) final def maybeIndented[R](f: (Parser, Bool) => R): R = yeetSpaces match case (_: NEWLINE_COMMA, l0) :: _ => consume while yeetSpaces.headOption.exists(_._1 === NEWLINE) do consume cur match case Nil => case (tok, loc) :: _ => raise(WarningReport( msg"This ${tok.describe} should be indented" -> S(loc) :: msg"since it is a continuation of the new line here" -> S(l0) :: Nil)) maybeIndented(f) // case (br @ BRACKETS(Indent | Curly, toks), _) :: _ => // * Note: not accepting Curly braces here, so as to allow things like `foo({X}, Y)` to parse case (br @ BRACKETS(Indent, toks), _) :: _ => consume rec(toks, S(br.innerLoc), br.describe).concludeWith(f(_, true)) case _ => f(this, false) final def blockMaybeIndented: Ls[Tree] = maybeIndented((p, i) => p.block(allowNewlines = i)) /* Note on annotation parsing: There is currently an inconsistency when parsing annotations that start blocks, where the annotatee is parsed with no precedence (because this is done by `block`), so that `@Test 2 as Int` at the top level parses as `@Test (2 as Int)`; but within a subexpression, it is parsed with precendence AnnotBodyPrec, so that, eg, `x is @compile P() as y` parses correctly... */ def annot(id: Ident): Tree = val pre = exprCont(id, SelPrec, allowNewlines = false, gobbleSpaces = false) cur match case (br @ BRACKETS(Round, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockMaybeIndented) App(pre, Tup(as).withLoc(S(loc))) case _ => pre def block(allowNewlines: Bool)(using Line): Ls[Tree] = blockOf(prefixRules, Nil, allowNewlines) def blockOf(rule: ParseRule[Tree], annotations: Ls[Tree], allowNewlines: Bool)(using Line): Ls[Tree] = wrap(rule.name, s"allowNewlines = $allowNewlines")(blockOfImpl(rule, annotations, allowNewlines)) def blockOfImpl(rule: ParseRule[Tree], annotations: Ls[Tree], allowNewlines: Bool): Ls[Tree] = def blockContOf(rule: ParseRule[Tree], annotations: Ls[Tree] = Nil): Ls[Tree] = yeetSpaces match case (COMMA, _) :: _ => consume; blockOf(rule, annotations, allowNewlines) case (NEWLINE, _) :: _ if allowNewlines => consume; blockOf(rule, annotations, allowNewlines) case _ => Nil cur match case Nil => Nil case (NEWLINE, _) :: _ if allowNewlines => consume; blockOf(rule, annotations, allowNewlines) case (COMMA, _) :: _ => consume; blockOf(rule, annotations, allowNewlines) case (SPACE, _) :: _ => consume; blockOf(rule, annotations, allowNewlines) case (br @ BRACKETS(Indent, toks), _) :: _ => // * Handle indented blocks appearing after comma continuations // * (e.g., in multi-line function calls like `tuple(1,\n 2)`) consume rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockOf(rule, annotations, true)) ++ blockContOf(rule) case (IDENT("@", _), l0) :: (IDENT(id, false), l1) :: _ => consume consume val a = annot(new Ident(id).withLoc(S(l0 ++ l1))) yeetSpaces match case Nil => err( msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}" -> a.toLoc.map(_.right) :: msg"found a lone annotation instead" -> a.toLoc :: Nil ) errExpr :: Nil case _ => blockOf(rule, a :: annotations, allowNewlines = allowNewlines) case (tok @ (id: IDENT), loc) :: _ if id.name =/= ":" => Keyword.all.get(id.name) match case S(kw) => consume rule.getKwAlt(kw, S(loc)) match case S(subRule) => yeetSpaces match case (tok @ BRACKETS(_: Indent_Curly, toks), loc) :: _ if subRule.blkAlt.isEmpty => consume val blk = rec(toks, S(tok.innerLoc), tok.describe).concludeWith(_.blockOf(subRule, Nil, allowNewlines)) // FIXME allowNewlines? if blk.isEmpty then err(msg"Expected ${subRule.whatComesAfter} ${subRule.mkAfterStr}; found end of block instead" -> S(loc) :: Nil) errExpr blk.map(annotations.annotate) ::: blockContOf(rule) case _ => val p = kw.rightPrec.getOrElse(CommaPrecNext) val res = parseRule(p, subRule, allowNewlines = allowNewlines).getOrElse(errExpr) annotations.annotate(exprCont(res, CommaPrecNext, allowNewlines = allowNewlines)) :: blockContOf(rule) case N => // TODO dedup this common-looking logic: rule.exprAlt match case S(exprAlt) => yeetSpaces match case (tok @ BRACKETS(_: Indent_Curly, toks), loc) :: _ => consume prefixRules.getKwAlt(kw, S(loc)) match case S(subRule) if subRule.blkAlt.isEmpty => rec(toks, S(tok.innerLoc), tok.describe).concludeWith { p => p.blockOf(subRule.map(e => parseRule(CommaPrecNext, exprAlt.rest, allowNewlines = allowNewlines).map(res => exprAlt.k(e, res)).getOrElse(errExpr)), annotations, allowNewlines) } ++ blockContOf(rule) case _ => TODO(cur) case _ => prefixRules.getKwAlt(kw, S(loc)) match case S(subRule) => val e = exprCont(parseRule(CommaPrecNext, subRule, allowNewlines = allowNewlines) .getOrElse(errExpr), CommaPrecNext, allowNewlines = allowNewlines) annotations.annotate(parseRule(CommaPrecNext, exprAlt.rest, allowNewlines = allowNewlines).map(res => exprAlt.k(e, res)).getOrElse(errExpr)) :: blockContOf(rule) case N => // TODO dedup? err(msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil) annotations.annotate(errExpr) :: blockContOf(rule) case N => err(msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil) annotations.annotate(errExpr) :: blockContOf(rule) case N => val lhs = tryParseExp(CommaPrecNext, tok, loc, rule, allowNewlines = allowNewlines).getOrElse(errExpr) cur match case (KEYWORD(kw @ (Keyword.`=`)), l0) :: _ /* if kw.leftPrecOrMin > prec */ => consume val rhs = expr(CommaPrecNext, allowNewlines = allowNewlines) Def(lhs, rhs) :: blockContOf(rule) case _ => annotations.annotate(lhs) :: blockContOf(rule) case (tok, loc) :: _ => annotations.annotate( tryParseExp(CommaPrecNext, tok, loc, rule, allowNewlines = allowNewlines).getOrElse(errExpr) ) :: blockContOf(rule) private def tryParseExp[A](prec: Int, tok: Token, loc: Loc, rule: ParseRule[A], allowNewlines: Bool): Opt[A] = rule.exprAlt match case S(exprAlt) => val e = simpleExpr(prec, allowNewlines = allowNewlines) if verbose then printDbg("$ proceed with rule: " + exprAlt) parseRule(prec, exprAlt.rest, allowNewlines = allowNewlines).map(res => exprAlt.k(e, res)) case N => rule.emptyAlt match case S(res) => S(res()) case N => err(msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil) N /** A result of None means there was an error (already reported) and nothing could be parsed. */ def parseRule[A](prec: Int, rule: ParseRule[A], allowNewlines: Bool)(using Line): Opt[A] = wrap(prec, rule, s"allowNewlines = $allowNewlines")(parseRuleImpl(prec, rule, allowNewlines = allowNewlines)) def parseRuleImpl[A](prec: Int, rule: ParseRule[A], allowNewlines: Bool): Opt[A] = def tryEmpty(tok: Token, loc: Loc) = rule.emptyAlt match case S(res) => S(res()) case N => consume err(msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(loc) :: Nil) N yeetSpaces match // case (tok @ (id: IDENT), loc) :: _ if Keyword.all.get(id.name).exists(_.leftPrecOrMin < prec) => // printDbg(s"Precedence of $id < $prec") // // TODO dedup with "nil" case below? // rule.emptyAlt match // case S(res) => // S(res) // case N => // err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found end of phrase instead" -> S(loc.left) :: Nil) // N case (tok @ (id: IDENT), loc) :: _ => Keyword.all.get(id.name) match case S(Keyword.`:`) | N => // encountering `:` should lead to parsing an expr (likely a pun) tryParseExp(prec, tok, loc, rule, allowNewlines = allowNewlines) case S(kw) => rule.getKwAlt(kw, S(loc)) match case S(subRule) => if verbose then printDbg(s"$$ proceed with rule: ${subRule.name}") consume yeetSpaces match case (tok @ BRACKETS(_: Indent_Curly, toks), loc) :: _ if subRule.blkAlt.isEmpty => consume rec(toks, S(tok.innerLoc), tok.describe) .concludeWith(_.parseRule(kw.rightPrec.getOrElse(CommaPrecNext), subRule, allowNewlines = true)) case _ => parseRule(kw.rightPrec.getOrElse(CommaPrecNext), subRule, allowNewlines = allowNewlines) case N => if verbose then printDbg(s"$$ cannot find a rule starting with: ${id.name}") rule.exprAlt match case S(exprAlt) => consume prefixRules.getKwAlt(kw, S(loc)) match case S(subRule) => val e = yeetSpaces match case (br @ BRACKETS(_: Indent_Curly, toks), _brLoc) :: _ if subRule.blkAlt.isEmpty && (subRule.kwAlts.nonEmpty || subRule.exprAlt.isDefined) => // * Enter this indented block to parse the continuation of a prefix keyword, // * but only when the subrule doesn't already have its own block handler (`blkAlt.isEmpty`) // * and has keyword or expression alternatives to parse (`kwAlts.nonEmpty || exprAlt.isDefined`). // * This skips block entry for keywords registered via `singleKw` (e.g., `true`, `false`, // * `undefined`, `null`, `this`) whose continuation rule is end-only — they produce a // * `ParseRule(end(v))` with no `kwAlts`, no `exprAlt`, and no `blkAlt`. // * Without this guard, `if true` followed by an indented `then`/`else` would consume the // * block trying to parse `true`'s empty continuation, losing the `then`/`else` tokens. consume val blk = rec(toks, S(br.innerLoc), br.describe) .concludeWith(_.blockOf(subRule, Nil, allowNewlines = true)) val tree = blk match case Nil => err(msg"Expected ${subRule.whatComesAfter} ${subRule.mkAfterStr}; found empty block instead" -> S(_brLoc) :: Nil) errExpr case single :: Nil => single case multiple => Block(multiple) exprCont(tree, prec, allowNewlines = allowNewlines) case _ => exprCont( parseRule(kw.rightPrecOrMin, subRule, allowNewlines = allowNewlines) .getOrElse(errExpr), prec, allowNewlines = allowNewlines) parseRule(prec, exprAlt.rest, allowNewlines = allowNewlines).map(res => exprAlt.k(e, res)) case N => tryEmpty(tok, loc) case N => tryEmpty(tok, loc) case (tok @ (_: NEWLINE_COMMA), l0) :: (id: IDENT, l1) :: _ if allowNewlines && rule.kwAlts.contains(id.name) => consume parseRule(prec, rule, allowNewlines = allowNewlines) case (tok @ (_: NEWLINE_COMMA), l0) :: _ => // TODO(cur) rule.emptyAlt match case S(res) => S(res()) case N => //err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> lastLoc :: Nil) err(msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> S(l0) :: Nil) N case (br @ BRACKETS(_: Indent_Curly, toks), loc) :: _ => // rule.blkAlt match // case S(res) => S(res) // case N => // err((msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found ${tok.describe} instead" -> lastLoc :: Nil) // N if verbose then printDbg("$ found an indented" + (toks match case (_, loc) :: tail => val lastLoc = tail.lastOption.map(_._2).getOrElse(loc) s" block from ${loc.showStart} to ${lastLoc.showEnd}" case Nil => "empty block")) rule.blkAlt match case S(exprAlt) => consume if verbose then printDbg("$ found blockAlt; proceed with block") val e = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.block(allowNewlines = true)) |> Tree.Block.apply parseRule(prec, exprAlt.rest, allowNewlines = true).map(res => exprAlt.k(e, res)) case N => if verbose then printDbg("$ no blockAlt; proceed with rule") val continue = toks.headOption match case S(LITVAL(_) -> _) => true case S(IDENT(nme, sym) -> _) => Keyword.all.contains(nme) && ( rule.kwAlts.contains(nme) || prefixRules.kwAlts.contains(nme) && rule.exprAlt.nonEmpty ) || rule.exprAlt.nonEmpty case _ => false if continue then consume rec(toks, S(br.innerLoc), br.describe).concludeWith(_.parseRule(prec, rule, allowNewlines = false)) else tryEmpty(br, loc) case (tok, loc) :: _ => if verbose then printDbg("$ treat as an expression: " + tok.describe) tryParseExp(prec, tok, loc, rule, allowNewlines = true /* Making this false somehow makes the following not parse: ``` if (print("Hello World"), false) then 0(0) else 1 ``` and this parse incorrectly (as a kw stutter): ``` mut let x = 1 in x ``` */) // TODO(tok) case Nil => rule.emptyAlt match case S(res) => S(res()) case N => err(msg"Expected ${rule.whatComesAfter} ${rule.mkAfterStr}; found end of input instead" -> lastLoc :: Nil) N // TODO refactor? This is only used for quotes, which should parse like normal code final def bindings(acc: Ls[Tree -> Tree]): Ls[Tree -> Tree] = cur match { case (SPACE, _) :: _ => consume bindings(acc) case (_: NEWLINE_COMMA, _) :: _ => // TODO: | ... acc.reverse case (IDENT(nme, sym), l0) :: _ => consume yeetSpaces match case (IDENT("=", _), l1) :: _ => consume case (tk, l1) :: _ => err(msg"Expected `=` after ${nme}; found ${tk.toString} instead" -> S(l1) :: Nil) case _ => die val rhs = simpleExprImpl(0, allowNewlines = true) val v = Tree.Ident(nme).withLoc(S(l0)) cur match { case (COMMA, l1) :: _ => consume bindings((v -> rhs) :: acc) case _ => ((v -> rhs) :: acc).reverse } case _ => Nil } def expr(prec: Int, allowNewlines: Bool)(using Line): Tree = val res = parseRule(prec, prefixRulesAllowIndentedBlock, allowNewlines = allowNewlines ).getOrElse(errExpr) // * a `None` result means an alread-reported error exprCont(res, prec, allowNewlines = allowNewlines) def simpleExpr(prec: Int, allowNewlines: Bool)(using Line): Tree = wrap(prec)(simpleExprImpl(prec, allowNewlines = allowNewlines)) def simpleExprImpl(prec: Int, allowNewlines: Bool): Tree = yeetSpaces match case (IDENT("=", _), l0) :: (IDENT(nme, false), l1) :: rest => consume consume Pun(true, new Ident(nme).withLoc(S(l1))) case (IDENT(":", _), l0) :: (IDENT(nme, false), l1) :: rest => consume consume Pun(false, new Ident(nme).withLoc(S(l1))) case (IDENT("@", _), l0) :: (IDENT(id, false), l1) :: _ => consume consume val a = annot(new Ident(id).withLoc(S(l0 ++ l1))) exprCont( Annotated(a, simpleExpr(AnnotBodyPrec, allowNewlines = allowNewlines)), prec, allowNewlines = allowNewlines) case (ESC_IDENT(name), loc) :: _ => consume val id = Tree.Ident(name).withLoc(S(loc)) exprCont(id, prec, allowNewlines = true) case (IDENT(nme, sym), loc) :: _ => Keyword.all.get(nme) match case S(kw) => // * Expressions starting with keywords should be handled in parseRule // * I guess this case is not really supposed to ever be reached (?) err(msg"Unexpected ${kw.toString} in this position" -> S(loc) :: Nil) errExpr case N => consume val id = Tree.Ident(nme).withLoc(S(loc)) if prefixOps.contains(nme) then yeetSpaces match case Nil => id case _ => val newPrec = if nme === "!" then // Special case: bang operator currently used in BbML PrefixOpsPrec else opCharPrec(nme.head) val rhs = expr(newPrec, allowNewlines = allowNewlines) exprCont(App(id, PlainTup(rhs)), prec, allowNewlines = allowNewlines) else exprCont(id, prec, allowNewlines = allowNewlines) case (LITVAL(lit), loc) :: _ => consume exprCont(lit.asTree.withLoc(S(loc)), prec, allowNewlines = allowNewlines) case (br @ BRACKETS(bk @ (Round | Curly | Square), toks), loc) :: _ => consume val ps = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockMaybeIndented) yeetSpaces match case (QUOTE, l) :: (KEYWORD(kw @ (Keyword.`=>` | Keyword.`->`)), l0) :: _ => consume consume val rhs = effectfulRhs(kw.rightPrecOrMin, allowNewlines = allowNewlines) val lhs = bk match case Round => Tup(ps) case Curly => Bra(Curly, Block(ps)) case Square => TyTup(ps) exprCont( Quoted(InfixApp(lhs, new Keywrd(kw).withLoc(S(l0)), Unquoted(rhs)).withLoc(S(loc))).withLoc(S(l ++ loc)), prec, allowNewlines = allowNewlines) case (KEYWORD(kw @ (Keyword.`=>` | Keyword.`->`)), l0) :: _ if kw.leftPrecOrMin > prec => consume val rhs = effectfulRhs(kw.rightPrecOrMin, allowNewlines = allowNewlines) val lhs = bk match case Round => Tup(ps) case Curly => ??? case Square => TyTup(ps) val res = InfixApp(lhs.withLoc(S(loc)), new Keywrd(kw).withLoc(S(l0)), rhs) exprCont(res, prec, allowNewlines = allowNewlines) case _ => val sts = ps val res = bk match case Square => Tup(sts).withLoc(S(loc)) case Round => sts match case Nil => Unt().withLoc(S(loc)) case e :: Nil => Bra(Round, e).withLoc(S(loc)) case es => Bra(Round, Block(es).withLoc(S(loc))) case Curly => Bra(Curly, Block(ps)) exprCont(res, prec, allowNewlines = allowNewlines) case (QUOTE, loc) :: _ => consume cur match { case (IDENT("let", _), l0) :: _ => consume val bs = bindings(Nil) val body = yeetSpaces match case (QUOTE, l1) :: (IDENT("in", _), l2) :: _ => consume consume simpleExpr(0, allowNewlines = false) case (tk, loc) :: _ => err(msg"Expected an expression; found ${tk.toString} instead" -> S(loc) :: Nil) errExpr case Nil => err(msg"Expected '`in'; found end of input instead" -> lastLoc :: Nil) errExpr bs.foldRight(body) { case ((v, r), acc) => Quoted(LetLike(new Keywrd(`let`).withLoc(S(l0)), v, S(Unquoted(r)), S(Unquoted(acc)))) } case (IDENT("if", _), l0) :: _ => consume val term = simpleExprImpl(prec, allowNewlines = false) yeetSpaces match case (IDENT("else", _), l1) :: _ => consume val ele = simpleExprImpl(prec, allowNewlines = false) term match case InfixApp(lhs, kw @ Keywrd(Keyword.`then`), rhs) => Quoted(IfLike(new Keywrd(Keyword.`if`).withLoc(S(l0)), Block( InfixApp(Unquoted(lhs), kw, Unquoted(rhs)) :: PrefixApp(new Keywrd(Keyword.`else`).withLoc(S(l1)), Unquoted(ele)) :: Nil ))) case tk => err(msg"Expected '`in'; found ${tk.toString} instead" -> tk.toLoc :: Nil) errExpr case (tk, loc) :: _ => err(msg"Expected 'else'; found ${tk.toString} instead" -> S(loc) :: Nil) errExpr case Nil => err(msg"Expected 'else'; found end of input instead" -> lastLoc :: Nil) errExpr case (IDENT(nme, sym), loc) :: _ => consume val res = if nme === "true" then Tree.BoolLit(true) else if nme === "false" then Tree.BoolLit(false) else Tree.Ident(nme) exprCont(Tree.Quoted(res.withLoc(S(loc))), prec, allowNewlines = false) case (LITVAL(lit), l0) :: _ => consume exprCont(Tree.Quoted(lit.asTree.withLoc(S(l0))), prec, allowNewlines = false) case _ => unsupportedQuote(S(loc)) } case (BRACKETS(_: Indent_Curly, _), loc) :: _ => err(msg"Expected an expression; found block instead" -> lastLoc :: Nil) errExpr case (SUSPENSION(dotDotDot), loc) :: _ => consume val bod = yeetSpaces match case Nil | (COMMA, _) :: _ => N case _ => S(expr(prec, allowNewlines = allowNewlines)) val kw = if dotDotDot then new Keywrd(Keyword.`...`) else new Keywrd(Keyword.`..`) Spread(kw.withLoc(S(loc)), bod) // case (NEWLINE, loc) :: _ => // this seems to never be reached // raise(WarningReport(msg"???" -> S(loc) :: Nil)) // consume // simpleExprImpl(prec) case (SELECT(name = nme, dynamic = false), loc) :: _ => consume exprCont(Tree.Sel(Tree.Empty(), new Ident(nme).withLoc(S(loc))), prec, allowNewlines = false) // TODO: use a new tree ctor case (tok, loc) :: _ => err(msg"Expected an expression; found ${tok.describe} instead" -> S(loc) :: Nil) errExpr case Nil => err(msg"Expected an expression; found end of input instead" -> lastLoc :: Nil) errExpr private def unsupportedQuote(loc: Opt[Loc]) = { err(msg"This quote syntax is not supported yet" -> loc :: Nil) errExpr } def effectfulRhs(prec: Int, allowNewlines: Bool)(using Line): Tree = yeetSpaces match case (br @ BRACKETS(Curly, toks), loc) :: _ => consume val eff = rec(toks, S(loc), "effect type").concludeWith(_.expr(0, allowNewlines = allowNewlines)) Effectful(eff, expr(prec, allowNewlines = allowNewlines)) case _ => expr(prec, allowNewlines = allowNewlines) // case _ => Block.mk(blockMaybeIndented) def split(using Line): Ls[Tree] = wrap("")(splitItem(Nil).reverse) @tailrec final private def splitItem(acc: Ls[Tree]): Ls[Tree] = val item = wrap(s"index = ${acc.size + 1}"): cur match // `true | false | Tree` case Nil => false case (_: NEWLINE_COMMA | SPACE, _) :: _ => consume; true case (KEYWORD(kw), loc) :: _ if kw isnt Keyword.__ => prefixRules.getKwAlt(kw, S(loc)) match case S(subRule) => consume parseRule(CommaPrecNext, subRule, allowNewlines = false).getOrElse(errExpr) case N => expr(0, allowNewlines = false) case _ => expr(0, allowNewlines = false) item match case true => splitItem(acc) // continue case false => printDbg(s"! end of split"); acc // break case e: Tree => // needs further inspection yeetSpaces match case (_: NEWLINE_COMMA, _) :: _ => consume; splitItem(e :: acc) case _ => printDbg(s"! end of split"); e :: acc /** Parse an operator split (block of lines starting by an operator). * TODO: parse let bindings */ def opSplit(lhs: Tree, splittingOpLoc: Loc, prec: Int)(using Line): Tree = wrap((lhs, splittingOpLoc, prec))(opSplitImpl(lhs, splittingOpLoc, prec, Nil)) def opSplitImpl(lhs: Tree, splittingOpLoc: Loc, prec: Int, acc: Ls[Tree]): Tree = val (newAcc, e) = yeetSpaces match case (PrefixRule(kw, rule), loc) :: _ => consume val e = parseRule(kw.rightPrecOrMin, rule.map(_.withLoc(S(loc))), allowNewlines = true).getOrElse(errExpr) (e :: acc, S(e)) case _ => exprCont(SplitPoint(), prec, allowNewlines = false) match case SplitPoint() => // * Note: nothing was parsed! // * Kludge to accommodate the kludge for `then` below (acc, N) case e => (e :: acc, S(e)) yeetSpaces match case Nil => OpSplit(lhs, newAcc.reverse) case (_: NEWLINE_COMMA, l0) :: _ => consume opSplitImpl(lhs, splittingOpLoc, prec, newAcc) case (SELECT(nme, dyn), l0) :: rest => assert(SelPrec <= prec) ??? // TODO? case (IDENT("then", false), l0) :: rest if e.isDefined => // * Kludge – when we have a proper generally splittable syntax, this will be handled by construction consume val rhs = expr(Keyword.`then`.rightPrecOrMin, allowNewlines = false) e match case N => die case S(e) => opSplitImpl(lhs, splittingOpLoc, prec, InfixApp(e, new Keywrd(Keyword.`then`).withLoc(S(l0)), rhs) :: acc) case (IDENT(op, true), l0) :: rest => assert(opPrec(op)._1 <= prec) if rest.collectFirst{ case (_: NEWLINE_COMMA | IDENT("then", false), _) => }.isEmpty // TODO dedup then OpSplit(lhs, acc.reverse) else err( msg"Operator cannot be used inside this operator split" -> S(l0) :: msg"as it has lower precedence than the splitting operator here" -> S(splittingOpLoc) :: Nil) val rhs = expr(opPrec(op)._2, allowNewlines = false) opSplitImpl(OpApp(lhs, Ident(op).withLoc(S(l0)), rhs :: Nil), splittingOpLoc, prec, newAcc) case (tok, loc) :: _ => // TODO indented op block err(msg"Unexpected ${tok.describe} in this operator split inner position" -> S(loc):: msg"Note: the operator split starts here" -> S(splittingOpLoc) :: Nil) OpSplit(lhs, acc reverse_::: errExpr :: Nil) /** `gobbleSpaces` is currently only used to prevent `@foo (2 + 2)` from parsing as `@foo(2 + 2) ...` */ final def exprCont(acc: Tree, prec: Int, allowNewlines: Bool, gobbleSpaces: Bool = true)(using Line): Tree = wrap(prec, s"`$acc`", allowNewlines)(exprContImpl(acc, prec, allowNewlines, gobbleSpaces)) final def exprContImpl(acc: Tree, prec: Int, allowNewlines: Bool, gobbleSpaces: Bool): Tree = cur match case (SPACE, l0) :: _ if gobbleSpaces => consume acc match // TODO: [CY] looks fishy. a better way? [LP] indeed; ref should be a prefix keyword! case Sel(reg, Ident("ref")) => RegRef(reg, simpleExprImpl(0, allowNewlines = false)) case _ => exprCont(acc, prec, allowNewlines = allowNewlines) case (QUOTE, l) :: _ => cur match { case _ :: (KEYWORD(kw @ (Keyword.`=>` | Keyword.`->`)), l0) :: _ if kw.leftPrecOrMin > prec => consume consume val rhs = effectfulRhs(kw.rightPrecOrMin, allowNewlines = allowNewlines) exprCont(Quoted(InfixApp(PlainTup(acc), new Keywrd(kw).withLoc(S(l0)), Unquoted(rhs))), prec, allowNewlines = allowNewlines) case _ :: (br @ BRACKETS(Round, toks), loc) :: _ => consume consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockMaybeIndented).map(t => Unquoted(t)) val res = App(Unquoted(acc), Tup(as).withLoc(S(loc))) exprCont(Quoted(res), prec, allowNewlines = allowNewlines) case _ :: (OP(opStr), l0) :: _ => if opPrec(opStr)._1 > prec then { consume consume val v = Ident(opStr).withLoc(S(l0)) yeetSpaces match { case (_: NEWLINE_COMMA, l0) :: _ => consume case _ => } val rhs = expr(opPrec(opStr)._2, allowNewlines = allowNewlines) exprCont(opStr match { case "with" => unsupportedQuote(S(l0)) case _ => Quoted(App(v, PlainTup(Unquoted(acc), Unquoted(rhs)))) }, prec, allowNewlines = allowNewlines) } else acc case _ :: (KEYWORD(Keyword("in")), _) :: _ => acc case _ => consume unsupportedQuote(acc.toLoc) acc } case (COMMA, l0) :: _ if prec === 0 => consume err(msg"Unexpected comma in this position" -> S(l0) :: Nil) acc /* case (KEYWORD(opStr @ "=>"), l0) :: (_: NEWLINE_COMMA, l1) :: _ if opPrec(opStr)._1 > prec => consume val rhs = Blk(typingUnit.entities) R(Lam(PlainTup(acc), rhs)) */ // case (KEYWORD(kw @ (Keyword.`=`)), l0) :: _ if kw.leftPrecOrMin > prec => // consume // ??? case (KEYWORD(kw @ (Keyword.`=>` | Keyword.`->`)), l0) :: _ if kw.leftPrecOrMin > prec => consume val rhs = effectfulRhs(kw.rightPrecOrMin, allowNewlines = allowNewlines) val res = acc match case _ => InfixApp(PlainTup(acc), new Keywrd(kw).withLoc(S(l0)), rhs) exprCont(res, prec, allowNewlines = allowNewlines) case (IDENT("!", _), l0) :: (br @ BRACKETS(bk, toks), l1) :: _ => consume consume val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0, allowNewlines = true)) exprCont(DynAccess(acc, Bra(bk, inner)), prec, allowNewlines = allowNewlines) // TODO: these should eventually no longer be treated as dynamic: case (PERIOD, l0) :: (br @ BRACKETS(bk @ (Round | Square), toks), l1) :: _ => consume consume val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0, allowNewlines = true)) exprCont(DynAccess(acc, Bra(bk, inner)).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) case (PERIOD, l0) :: (br @ BRACKETS(Curly, toks), l1) :: _ => consume consume val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockMaybeIndented) exprCont(OpenIn(acc, Block(inner)), prec, allowNewlines = allowNewlines) case (PERIOD, l0) :: (LITVAL(lit: (Tree & Literal)), l1) :: _ => consume consume exprCont(DynAccess(acc, lit.withLoc(S(l1))).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) /* case (PERIOD, l0) :: (br @ BRACKETS(Square, toks), l1) :: _ => consume consume val idx = rec(toks, S(br.innerLoc), br.describe) .concludeWith(_.simpleExpr(0, allowSpace = true)) val newAcc = Subs(acc, idx).withLoc(S(l0 ++ l1 ++ idx.toLoc)) exprCont(newAcc, prec, allowNewlines) */ // * Parse operator splits case (br @ BRACKETS(_: Indent_Curly, toks @ ((tok @ (IDENT(_, true) | SELECT(_, _) | KEYWORD(_: Keyword.InfixSplittable)), l0) :: _)), loc) :: _ if tok.match { case KEYWORD(Keyword.`of`) => AppPrec case KEYWORD(kw) => kw.leftPrecOrMin case id: IDENT => opPrec(id.name)._1 case sel: SELECT => SelPrec } > prec => consume if toks.collectFirst{ case (_: NEWLINE_COMMA, _) => }.isEmpty then // * If the indented block doens't have any newlines or commas, // * this is not truly a split, and we can parse it as a normal expression continuation. cur = toks ::: cur exprCont(acc, prec, allowNewlines = allowNewlines) else val r = rec(toks, S(loc), "operator block") val res = r.opSplit(acc, l0, prec) r.yeetSpaces match case Nil => exprCont(res, prec, allowNewlines = allowNewlines) case toks => cur = toks ::: cur exprCont(res, prec, allowNewlines = allowNewlines) // TODO // case (NEWLINE, _) :: (SELECT(nme), _) :: _ // => // * Parse newline-operators, eg `2 + 2\n*\n2 + 2` <=> `(2 + 2) * (2 + 2)` // TODO also allow uses of SELECT case (_: NEWLINE_COMMA, _) :: (OP(opStr), l0) :: rest if allowNewlines && prec <= NoElsePrec // (Q: why doesn't MinPrec work?) && NOISE.get(rest).nonEmpty // * Don't treat as infix if there are no tokens for the RHS (eg `()\n???`) && (!prefixOps.contains(opStr) || rest.match case (_: NEWLINE_COMMA, _) :: _ | (SPACE, _) :: _ | (BRACKETS(_: Indent_Curly, _), _) :: _ | Nil => true case _ => false ) => consume consume val v = Ident(opStr).withLoc(S(l0)) val rhsPrec = yeetSpaces match case (_: NEWLINE_COMMA, l0) :: _ => consume CommaPrecNext2 // for chained nl ops: left assoc case _ => opPrec(opStr)._2 val rhs = expr(rhsPrec, allowNewlines = allowNewlines) exprCont(OpApp(acc, v, rhs :: Nil), prec, allowNewlines = allowNewlines) case (OP("::"), l0) :: (IDENT(id, false), l1) :: _ => consume consume exprCont(MemberProj(acc, new Ident(id).withLoc(S(l1))).withLoc(S(l0 ++ l1)), prec, allowNewlines = allowNewlines) case (OP(opStr), l0) :: _ if /* isInfix(opStr) && */ opPrec(opStr)._1 > prec => consume val v = Ident(opStr).withLoc(S(l0)) yeetSpaces match { case (_: NEWLINE_COMMA, l0) :: _ => consume case _ => } printDbg(s"! found operator `$opStr` with prec ${opPrec(opStr)}") yeetSpaces match case (BRACKETS(_: Indent_Curly, toks), l0) :: _ => consume // rec(toks, S(br.innerLoc), br.describe).concludeWith(f(_, true)) val rhs = rec(toks, S(l0), "operator split").concludeWith(_.split) exprCont(OpApp(acc, v, Block(rhs).withLoc(S(l0)) :: Nil), prec, allowNewlines = allowNewlines) case _ => // val rhs = simpleExpr(opPrec(opStr)._2) val rhs = expr(opPrec(opStr)._2, allowNewlines = allowNewlines) exprCont(opStr match { case "with" => rhs match { // TODO? // case rhs: Rcd => // With(acc, rhs)//.withLocOf(term) // case Bra(true, rhs: Rcd) => // With(acc, rhs)//.withLocOf(term) case _ => err(msg"record literal expected here; found ${rhs.describe}" -> rhs.toLoc :: Nil) acc } case _ => OpApp(acc, v, rhs :: Nil) }, prec, allowNewlines = allowNewlines) case (SELECT(name, dyn), l0) :: _ if SelPrec >= prec => consume val tree = if dyn then DynAccess(acc, new Ident(name).withLoc(S(l0))) else Sel(acc, new Ident(name).withLoc(S(l0))) exprCont(tree, prec, allowNewlines = allowNewlines) case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockMaybeIndented) // val res = TyApp(acc, as.map(_.mapSecond.to)) val res = TyApp(acc, as).withLoc(acc.toLoc.fold(some(loc))(_ ++ loc |> some)) exprCont(res, prec, allowNewlines = allowNewlines) /* /*case (br @ BRACKETS(Square, toks), loc) :: _ => // * Currently unreachable because we match Square brackets as tparams consume val idx = rec(toks, S(br.innerLoc), "subscript").concludeWith(_.simpleExpr(0)) val res = Subs(acc, idx.withLoc(S(loc))) exprCont(res, prec, allowNewlines)*/ */ case (br @ BRACKETS(Round, toks), loc) :: _ if prec <= AppPrec => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockMaybeIndented) val res = App(acc, Tup(as).withLoc(S(loc))) exprCont(res, prec, allowNewlines = allowNewlines) case (KEYWORD(Keyword.`of`), _) :: _ if prec <= AppPrec => consume val as = blockMaybeIndented val res = App(acc, Tup(as)) exprCont(res, prec, allowNewlines = allowNewlines) case (_: NEWLINE_COMMA, _) :: (KEYWORD(kw), _) :: _ if kw.canStartInfixOnNewLine && kw.leftPrecOrMin > prec && infixRules.kwAlts.contains(kw.name) && (kw isnt Keyword.`do`) // This is to avoid the following case: // ``` // 0 then "null" // do console.log("non-null") // ``` // Otherwise, `do` will be parsed as an infix operator => consume exprCont(acc, prec, allowNewlines = allowNewlines) case (br @ BRACKETS(bk @ (_: Indent_Curly), toks @ ((KEYWORD(kw), _) :: _)), loc) :: _ if kw.leftPrecOrMin > prec && infixRules.kwAlts.contains(kw.name) => consume val (res, rest) = rec(toks, S(br.innerLoc), br.describe).continueWith: _.exprCont(acc, prec, allowNewlines = true) rest match case (_, l) :: _ => printDbg(s"!! REDUCING BRACKET") cur = (NEWLINE, l.left) :: rest ::: cur case _ => exprCont(res, prec, allowNewlines = allowNewlines) case (KEYWORD(kw), l0) :: _ if kw.leftPrecOrMin > prec => if verbose then printDbg(s"$$ found keyword: ${kw.name} (${kw.leftPrecOrMin})") infixRules.getKwAlt(kw, S(l0)) match case S(subRule) => consume if verbose then printDbg(s"$$ proceed with rule: ${subRule.name}") subRule.exprAlt match case S(exprAlt) => if verbose then printDbg("$ parsing the right-hand side") val rhs = expr(kw.rightPrecOrMin, allowNewlines = allowNewlines) parseRule(kw.rightPrecOrMin, exprAlt.rest, allowNewlines = allowNewlines).map: rest => exprCont(exprAlt.k(rhs, rest)(acc), prec, allowNewlines = allowNewlines) // FIXME prec?? .getOrElse(errExpr) case N => // TODO other alts...? err(msg"Expected ${subRule.whatComesAfter} ${subRule.mkAfterStr}; found ${kw.name} instead" -> S(l0) :: Nil) acc case _ => acc case _ => exprJux(acc, prec, allowNewlines = false) final def exprJux(acc: Tree, prec: Int, allowNewlines: Bool)(using Line): Tree = wrap(prec, s"`$acc`", allowNewlines)(exprJuxImpl(acc, prec, allowNewlines)) final def exprJuxImpl(acc: Tree, prec: Int, allowNewlines: Bool): Tree = cur match case (_: NEWLINE_COMMA, _) :: _ if allowNewlines => consume exprJux(acc, prec, allowNewlines = allowNewlines) case (IDENT(id, false), _) :: _ if prec < AppPrec && !Keyword.all.contains(id) => val res = exprCont(Jux(acc, expr(AppPrec, allowNewlines = allowNewlines)), prec, allowNewlines = allowNewlines) exprJux(res, prec, allowNewlines = allowNewlines) case (br @ BRACKETS(_: Indent_Curly, toks), l0) :: _ if prec < AppPrec && (toks.headOption match case S((IDENT(nme, sym), _)) => !sym && !Keyword.all.contains(nme) case _ => true ) => consume val res = rec(toks, S(br.innerLoc), br.describe).concludeWith: _.block(allowNewlines = true) exprCont(Jux(acc, Block(res).withLoc(S(l0))), prec, allowNewlines = true) case (tok, _) :: _ => printDbg(s"stops at ${tok.toString}") acc case Nil => printDbg(s"stops at the end of input") acc ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax/Token.scala ================================================ package hkmc2 package syntax import mlscript.utils._, shorthands._ /** Type of general Tokens */ sealed abstract class Token: def describe: Str = this match case SPACE => "space" case COMMA => "comma" case PERIOD => "period" case NEWLINE => "new line" case INDENT => "indentation" case DEINDENT => "deindentation" case ERROR => "error" case QUOTE => "quote" case LITVAL(value) => "literal" // case KEYWRD(name) => // if name.headOption.exists(_.isLetter) then s"'$name' keyword" else s"'$name'" case IDENT(name, symbolic) => if Keyword.all.contains(name) then s"'$name' keyword" else if symbolic then "operator" else "identifier" case SELECT(name, dyn) => (if dyn then "dynamic" else "static") + " selector" case OPEN_BRACKET(k) => s"opening ${k.name}" case CLOSE_BRACKET(k) => s"closing ${k.name}" case BRACKETS(BracketKind.Indent, contents) => s"indented block" case BRACKETS(k, contents) => s"${k.name} section" case COMMENT(text) => "comment" case SUSPENSION(true) => "'...' ellipsis" case SUSPENSION(false) => "'..' ellipsis" case ESC_IDENT(_) => "identifier" /** Type of 'Structured Tokens' aka 'Strokens', * which use a `BRACKETS` construct instead of `OPEN_BRACKET`/`CLOSE_BRACKET` and `INDENT`/`DEINDENT` */ sealed trait Stroken extends Token case object SPACE extends Token with Stroken case object COMMA extends Token with Stroken case object PERIOD extends Token with Stroken case object NEWLINE extends Token with Stroken // TODO rm case object INDENT extends Token case object DEINDENT extends Token case object ERROR extends Token with Stroken case object QUOTE extends Token with Stroken final case class LITVAL(value: Literal) extends Token with Stroken // final case class KEYWRD(name: String) extends Token with Stroken final case class IDENT(name: String, symbolic: Bool) extends Token with Stroken final case class SELECT(name: String, dynamic: Bool) extends Token with Stroken final case class OPEN_BRACKET(k: BracketKind) extends Token final case class CLOSE_BRACKET(k: BracketKind) extends Token final case class BRACKETS(k: BracketKind, contents: Ls[Stroken -> Loc])(val innerLoc: Loc) extends Token with Stroken final case class COMMENT(text: String) extends Token with Stroken final case class SUSPENSION(dotDotDot: Bool) extends Token with Stroken final case class ESC_IDENT(name: String) extends Token with Stroken sealed abstract class BracketKind: import BracketKind._ lazy val (beg, end) = this match case Round => "(" -> ")" case Curly => "{" -> "}" case Square => "[" -> "]" case Angle => "‹" -> "›" case Indent => "→" -> "←" case Quasiquote => "code\"" -> "\"" case QuasiquoteTriple => "code\"\"\"" -> "\"\"\"" case Unquote => "${" -> "}" def name: Str = this match case Round => "parenthesis" case Curly => "curly brace" case Square => "square bracket" case Angle => "angle bracket" case Indent => "indentation" case Quasiquote => "quasiquote" case QuasiquoteTriple => "quasiquote triple" case Unquote => "unquote" object BracketKind: case object Round extends BracketKind case object Curly extends BracketKind case object Square extends BracketKind case object Angle extends BracketKind case object Indent extends BracketKind case object Quasiquote extends BracketKind case object QuasiquoteTriple extends BracketKind case object Unquote extends BracketKind def unapply(c: Char): Opt[Either[BracketKind, BracketKind]] = c `|>?` : case '(' => Left(Round) case ')' => Right(Round) case '{' => Left(Curly) case '}' => Right(Curly) case '[' => Left(Square) case ']' => Right(Square) case '‹' => Left(Angle) case '›' => Right(Angle) ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala ================================================ package hkmc2 package syntax import scala.annotation.tailrec import scala.collection.mutable import sourcecode.Line import mlscript.utils.*, shorthands.* import hkmc2.utils.* import hkmc2.Message.MessageContext import semantics.{FldFlags, TermDefFlags, Modulefulness} import semantics.Elaborator.State import Tree._ sealed trait Literal extends AutoLocated: this: Tree => def asTree: Tree = this val idStr: Str = this match case IntLit(value) => value.toString case DecLit(value) => value.toString case StrLit(value) => value.iterator.map: // TODO dedup logic with `JSBuilder.makeStringLiteral`? case '\b' => "\\b" case '\t' => "\\t" case '\n' => "\\n" case '\r' => "\\r" case '\f' => "\\f" case '"' => "\\\"" case '\\' => "\\\\" case c if c.isControl => f"\\u${c.toInt}%04x" case c => c.toString .mkString("\"", "", "\"") case UnitLit(value) => if value then "null" else "undefined" case BoolLit(value) => value.toString def describeLit: Str = this.match case _: IntLit => "integer" case _: DecLit => "decimal" case _: StrLit => "string" case UnitLit(isNull) => if isNull then "null" else "undefined" case _: BoolLit => "boolean" + " literal" // def children: List[Located] = Nil enum SpreadKind: case Eager, Lazy def isEager: Bool = this match case Eager => true case Lazy => false def str: Str = this match case Eager => "..." case Lazy => ".." object SpreadKind: def fromKw(kw: Keywrd[Keyword.Ellipsis]) = kw.kw match case Keyword.`..` => SpreadKind.Lazy case Keyword.`...` => SpreadKind.Eager enum Tree extends AutoLocated: case Empty() case Error() case Dummy // TODO change the places where this is used case Under() case Unt() case Ident(name: Str) case Pun(eql: Bool, id: Ident) // `=ident` (eql) or `:ident` (!eql) case Keywrd[+K <: Keyword & Singleton](kw: K) case IntLit(value: BigInt) extends Tree with Literal case DecLit(value: BigDecimal) extends Tree with Literal case StrLit(value: Str) extends Tree with Literal case UnitLit(isNullNotUndefined: Bool) extends Tree with Literal case BoolLit(value: Bool) extends Tree with Literal case Bra(k: BracketKind, inner: Tree) case Block(stmts: Ls[Tree])(using State) extends Tree with semantics.BlockImpl case LetLike(kw: Keywrd[Keyword.LetLike], lhs: Tree, rhs: Opt[Tree], body: Opt[Tree]) case Hndl(lhs: Tree, cls: Tree, defs: Tree, body: Opt[Tree]) case Def(lhs: Tree, rhs: Tree) case TermDef(k: TermDefKind, head: Tree, rhs: Opt[Tree]) extends Tree with TermDefImpl case TypeDef(k: TypeDefKind, head: Tree, rhs: Opt[Tree])(using State) extends Tree with TypeDefImpl case Open(opened: Tree) case OpenIn(opened: Tree, body: Tree) case DynAccess(obj: Tree, fld: Tree) case Modified(modifier: Keywrd[Keyword.Modifier], body: Tree) case Quoted(body: Tree) case Unquoted(body: Tree) case Tup(fields: Ls[Tree]) case TyTup(tys: Ls[Tree]) case App(lhs: Tree, rhs: Tree) case OpApp(lhs: Tree, op: Tree, rhss: Ls[Tree]) case Jux(lhs: Tree, rhs: Tree) case SynthSel(prefix: Tree, name: Ident) case Sel(prefix: Tree, name: Ident) case MemberProj(cls: Tree, name: Ident) case PrefixApp(kw: Keywrd[Keyword.Prefix], rhs: Tree) case InfixApp(lhs: Tree, kw: Keywrd[Keyword.Infix], rhs: Tree) case LexicalNew(body: Opt[Tree], rft: Opt[Block]) // * New as it is parsed, with its weird precedence – eg (new C)(123) case ProperNew(body: Opt[Tree], rft: Opt[Block]) // * A desugared version of New that sets it right – eg new(C(123)) case DynamicNew(cls: Tree) // * Dynamic version – eg new! C(123) case IfLike(kw: Keywrd[Keyword.IfLike], split: Tree) case Assert(kw: Keywrd[Keyword.`assert`], cond: Tree, thn: Opt[Tree], els: Opt[Keywrd[Keyword.`else`] -> Tree]) case SplitPoint() case OpSplit(lhs: Tree, ops_rhss: Ls[Tree]) // * the rhss trees are expressions rooted in `SplitPoint`s case Case(kw: Keywrd[Keyword.`case`.type], branches: Tree) case Region(name: Tree, body: Tree) case RegRef(reg: Tree, value: Tree) case Effectful(eff: Tree, body: Tree) case Outer(name: Opt[Tree]) case Spread(kw: Keywrd[Keyword.Ellipsis], body: Opt[Tree]) case Annotated(annotation: Tree, target: Tree) case Directive(prefix: Tree, body: Tree) case Constructor(decl: Tree) /** Represents a term that has already been elaborated. When desugaring * operator splits in the UCS, the `lhs` of `OpSplit` has already been elaborated * into a `Term`, so we need to embed `Term` into `Tree` using `Trm`. */ case Trm(term: semantics.Term) def splitOn(acc: Tree): Tree = this match case SplitPoint() => acc case Sel(pre, id) => Sel(pre.splitOn(acc), id) case App(lhs, rhs) => App(lhs.splitOn(acc), rhs) case OpApp(lhs, op, rhss) => OpApp(lhs.splitOn(acc), op, rhss) case OpSplit(lhs, rhss) => OpSplit(lhs.splitOn(acc), rhss) case InfixApp(lhs, kw, rhs) => InfixApp(lhs.splitOn(acc), kw, rhs) case _: (Ident | Literal | Error) => acc case _ => die def children: Vector[Located] = this match case _: Empty | _: Error | _: Ident | _: Literal | _: Under | _: Unt => Vector.empty case Pun(_, e) => Vector.single(e) case Bra(_, e) => Vector.single(e) case Block(stmts) => stmts.toVector case LetLike(kw, lhs, rhs, body) => lhs +: (rhs.toVector ++ body.toVector) case Hndl(lhs, rhs, defs, body) => body match case Some(value) => lhs +: rhs +: defs +: value +: Vector.empty case None => lhs +: rhs +: defs +: Vector.empty case TypeDef(k, head, rhs) => head +: rhs.toVector case Modified(_, body) => Vector.single(body) case Quoted(body) => Vector.single(body) case Unquoted(body) => Vector.single(body) case Tup(fields) => fields.toVector case App(lhs, rhs) => Vector.double(lhs, rhs) case OpApp(lhs, op, rhss) => lhs +: op +: rhss.toVector case Jux(lhs, rhs) => Vector.double(lhs, rhs) case PrefixApp(kw, rhs) => Vector.double(kw, rhs) case InfixApp(lhs, kw, rhs) => Vector.triple(lhs, kw, rhs) case TermDef(k, head, rhs) => head +: rhs.toVector case LexicalNew(body, rft) => body.toVector ++ rft.toVector case ProperNew(body, rft) => body.toVector ++ rft.toVector case DynamicNew(body) => Vector.single(body) case IfLike(_, split) => Vector.single(split) case Assert(_, cond, thn, els) => cond +: (thn.toVector ++ els.toList.map(_._2)) case Case(_, bs) => Vector.single(bs) case Region(name, body) => Vector.double(name, body) case RegRef(reg, value) => Vector.double(reg, value) case Effectful(eff, body) => Vector.double(eff, body) case Outer(name) => name.toVector case TyTup(tys) => tys.toVector case Sel(prefix, name) => Vector.double(prefix, name) case SynthSel(prefix, name) => Vector.single(prefix) case DynAccess(prefix, fld) => Vector.double(prefix, fld) case Open(bod) => Vector.single(bod) case OpenIn(opened, body) => Vector.double(opened, body) case Def(lhs, rhs) => Vector.double(lhs, rhs) case Spread(kw, body) => Vector.single(kw) ++ body.toVector case Annotated(annotation, target) => Vector.double(annotation, target) case Directive(prefix, body) => Vector.double(prefix, body) case Constructor(decl) => Vector.single(decl) case MemberProj(cls, name) => Vector.single(cls) case Keywrd(kw) => Vector.empty case Dummy => Vector.empty case OpSplit(lhs, ops_rhss) => lhs +: ops_rhss.toVector case SplitPoint() => Vector.empty case Trm(trm) => Vector.single(trm) def describe: Str = this match case Empty() => "empty" case Error() => "‹erroneous syntax›" case Under() => "underscore" case Ident(name) => "identifier" case IntLit(value) => "integer literal" case DecLit(value) => "decimal literal" case StrLit(value) => "string literal" case BoolLit(value) => s"$value literal" case UnitLit(value) => if value then "null" else "undefined" case Unt() => "unit" case Bra(k, _) => k.name + " section" case Block(stmts) => "block" case LetLike(kw, lhs, rhs, body) => kw.name case TermDef(k, alphaName, rhs) => "term definition" case TypeDef(k, head, rhs) => "type definition" case Modified(kw, body) => s"'${kw.name}'-modified ${body.describe}" case Quoted(body) => "quoted" case Unquoted(body) => "unquoted" case Tup(fields) => "tuple" case TyTup(tys) => "type tuple" case App(lhs, rhs) => "application" case OpApp(lhs, op, rhss) => "operator application" case Jux(lhs, rhs) => "juxtaposition" case Sel(prefix, name) => "selection" case SynthSel(prefix, name) => "synthetic selection" case DynAccess(prefix, name) => "dynamic field access" case PrefixApp(kw, body) => s"prefix operator '${kw.name}'" case InfixApp(lhs, kw, rhs) => s"infix operator '${kw.name}'" case LexicalNew(body, _) => "new" case ProperNew(body, _) => "new" case DynamicNew(body) => "dynamic new" case IfLike(Keywrd(Keyword.`if`), split) => "if expression" case IfLike(Keywrd(Keyword.`while`), split) => "while expression" case Case(_, branches) => "case" case Region(name, body) => "region" case RegRef(reg, value) => "region reference" case Effectful(eff, body) => "effectful" case Outer(_) => "outer binding" case Hndl(_, _, _, _) => "handle" case Def(lhs, rhs) => "defining assignment" case Spread(_, _) => "spread" case Annotated(_, _) => "annotated" case Directive(_, _) => "directive" case Open(_) => "open" case Constructor(_) => "constructor" case MemberProj(_, _) => "member projection" case Keywrd(kw) => s"'${kw.name}' keyword" case Dummy => "‹dummy›" case Trm(t) => t.describe + " term" case Pun(eql, id) => "pun" case SplitPoint() => "split point" case OpSplit(lhs, ops_rhss) => "operator split" case OpenIn(opened, body) => "open-in" def deparenthesized: Tree = this match case Bra(BracketKind.Round, inner) => inner.deparenthesized case _ => this def showDbg: Str = toString // TODO lazy val desugared: Tree = this match case Ident(name) if name.startsWith("'") => StrLit(name.drop(1)).withLocOf(this) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) => InfixApp(lhs.desugared, Keywrd(Keyword.`:`), rhs.desugared) case Sel(pre, nme) if nme.name.startsWith("'") => DynAccess(pre.desugared, StrLit(nme.name.drop(1)).withLocOf(nme)).withLocOf(this) case Pun(false, id) => InfixApp(id, Keywrd(Keyword.`:`), id) // TODO generalize to pattern-let and rm this special case case LetLike(kw, und @ Under(), r, b) => LetLike(kw, Ident("_").withLocOf(und), r, b) case PossiblyAnnotated(anns, m: Modified) => PossiblyAnnotated(anns, m match case Modified(kw @ Keywrd(Keyword.`declare`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`data`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`abstract`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`staged`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`public`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`private`), s) => Annotated(kw, s.desugared) case Modified(kw @ Keywrd(Keyword.`mut`), TermDef(ImmutVal, anme, rhs)) => TermDef(MutVal, anme, rhs).withLocOf(this).desugared case _ => m ) case PossiblyAnnotated(anns, LetLike(letLike, OpApp(lhs, f @ Ident(nme), rhss), N, bodo)) if nme.endsWith("=") => // TODO only do this if the lhs is non-expansive/a valid assignment receiver? PossiblyAnnotated(anns, LetLike(letLike, lhs, S(OpApp(lhs, Ident(nme.init), rhss)), bodo).withLocOf(this).desugared) case Apps(PrefixApp(Keywrd(Keyword.`new!`), cls), argss) => DynamicNew(Apps(cls, argss)).withLocOf(this) case Apps(LexicalNew(S(body), N), argss) => ProperNew(S(Apps(body, argss)), N).withLocOf(this) case LexicalNew(bodo, rfto) => ProperNew(bodo, rfto).withLocOf(this) case InfixApp(Desugared(ProperNew(bodo, N)), Keywrd(Keyword.`with`), rhs: Block) => ProperNew(bodo, S(rhs)).withLocOf(this) case _ => this /** * Parse a tree as a parameter. * @param inUsing whether the parameter is in a `using` parameter list */ def asParam(inUsing: Bool): Diagnostic \/ ParamTree = @tailrec def go(t: Tree, flags: FldFlags, modifiers: Set[DeclKind]): Diagnostic \/ ParamTree = t match // * Base Cases. // fun f(_) case und: Under => R(ParamTree(flags, new Ident("_").withLocOf(und), N, N, modifiers)) // fun f(a) case id: Ident if !inUsing => R(ParamTree(flags, id, N, N, modifiers)) // fun f(a: A) case InfixApp(id: Ident, Keywrd(Keyword.`:`), sign) => R(ParamTree(flags, id, S(sign), N, modifiers)) // fun f(..a) | fun f(...a) case SpreadParam(id, spd) => R(ParamTree(flags, id, N, S(spd), modifiers)) // * Unwrapping Cases // fun f(module <...>) case TypeDef(Mod, inner, N) => go(inner, flags, modifiers + Mod) // fun f(pattern <...>) case TypeDef(Pat, inner, N) => go(inner, flags.copy(pat = true), modifiers + Pat) // class C(val <...>) case TermDef(ImmutVal, inner, _) => go(inner, flags.copy(isVal = true), modifiers + ImmutVal) // class C(mut val <...>) case TermDef(MutVal, inner, _) => go(inner, flags.copy(isVal = true, mut = true), modifiers + MutVal) // fun f(using <...>) case TermDef(Ins, inner, N) => go(inner, flags, modifiers + Ins) // * Base Case (for `using` clause) // fun f(using A) case ty: Tree if inUsing => // In contextual parameter lists, a single Tree as parameter is // understood as a type for unnamed contextual parameters, as // opposed to that an identifier is understood as the identifier // for a regular parameter list. R(ParamTree(flags, Ident(""), S(ty), N, modifiers)) // * Default Case case _ => L: ErrorReport: msg"Expected a valid parameter, found ${this.describe}" -> this.toLoc :: Nil go(this, flags = FldFlags.empty, modifiers = Set.empty) def isModified(modifier: Keyword | DeclKind): Bool = this match case td @ Tree.TypeDef(m, head, N) => (td.extension.isEmpty && td.withPart.isEmpty && m == modifier) || head.isModified(modifier) case td @ Tree.TermDef(m, head, N) => (td.extension.isEmpty && td.withPart.isEmpty && m == modifier) || head.isModified(modifier) case Modified(Keywrd(m), body) => (modifier is m) || body.isModified(modifier) case _ => false object Tree: val DummyApp: App = App(Dummy, Dummy) // TODO change the places where this is used val DummyTup: Tup = Tup(Dummy :: Nil) def DummyTypeDef(k: TypeDefKind)(using State): TypeDef = Tree.TypeDef(k, Tree.Dummy, N) object Block: def mk(stmts: Ls[Tree])(using State): Tree = stmts match case Nil => UnitLit(false) case e :: Nil => e case es => Block(es) object TyApp: def apply(lhs: Tree, targs: Ls[Tree]): App = App(lhs, TyTup(targs)) def unapply(t: App): Opt[(Tree, Ls[Tree])] = t match case App(lhs, TyTup(targs)) => S(lhs, targs) case _ => N extension [T <: Keyword & Singleton](kw: Tree.Keywrd[T]) def name = kw.kw.name /** * A parameter yet to be elaborated, which is different from * semantics.Param. It merely contains the information directly * extracted from the syntax tree. */ case class ParamTree( flags: FldFlags, ident: Ident, sign: Opt[Tree], spd: Opt[SpreadKind], modifiers: Set[DeclKind] ) object SpreadParam: def unapply(t: Tree): Opt[(Ident, SpreadKind)] = t match // fun f(..a) // fun f(...a) case Spread(kw, S(id: Ident)) => S(id, SpreadKind.fromKw(kw)) // fun f(.._) // fun f(..._) case Spread(kw, S(und: Under)) => S(new Ident("_").withLocOf(und), SpreadKind.fromKw(kw)) // fun f(..) // fun f(...) case Spread(kw, N) => S(new Ident("_").withLocOf(kw), SpreadKind.fromKw(kw)) case _ => N object Desugared: def unapply(t: Tree): S[Tree] = S(t.desugared) object PlainTup: def apply(fields: Tree*): Tree = Tup(fields.toList) object Apps: def apply(t: Tree, as: Ls[Tup]): Tree = as match case Nil => t case arg :: args => Apps(App(t, arg), args) def unapply(t: Tree): S[(Tree, Ls[Tup])] = t match case App(Apps(base, args), arg: Tup) => S(base, args :+ arg) case t => S(t, Nil) object PossiblyAnnotated: def apply(anns: Ls[Tree], t: Tree): Tree = anns.foldRight(t)(Annotated(_, _)) def unapply(t: Tree): Opt[(Ls[Tree], Tree)] = t match case Annotated(q, PossiblyAnnotated(qs, target)) => S(q :: qs, target) case other => S((Nil, other)) object PossiblyParenthesized: def unapply(t: Tree): S[Tree] = t match case Bra(BracketKind.Round, inner) => S(inner) case _ => S(t) sealed abstract class OuterKind(val desc: Str)(using line: Line) extends Ordered[OuterKind]: val ordinal: Int = line.value // YOLO assert(!OuterKind.kinds.contains(ordinal)) OuterKind.kinds += (ordinal -> this) def compare(that: OuterKind): Int = this.ordinal - that.ordinal object OuterKind: private var counter = 0 private val kinds = mutable.Map.empty[Int, OuterKind] // Please don't put any of these on the same line... case object BlockKind extends OuterKind("block") sealed abstract class DeclKind(val str: Str, desc: Str)(using Line) extends OuterKind(desc) sealed abstract class TermDefKind(str: Str, desc: Str)(using Line) extends DeclKind(str, desc) sealed abstract class ValLike(str: Str, desc: Str)(using Line) extends TermDefKind(str, desc) sealed abstract class Val(str: Str, desc: Str)(using Line) extends ValLike(str, desc) case object ImmutVal extends Val("val", "value") case object MutVal extends Val("mut val", "mutable value") case object LetBind extends ValLike("let", "let binding") case object HandlerBind extends TermDefKind("handler", "handler binding") case object Fun extends TermDefKind("fun", "function") case object Ins extends TermDefKind("using", "implicit instance") sealed abstract class TypeDefKind(str: Str, desc: Str)(using Line) extends DeclKind(str, desc) sealed trait ObjDefKind sealed trait ClsLikeKind extends ObjDefKind: val str: Str val desc: Str case object Cls extends TypeDefKind("class", "class") with ClsLikeKind case object Trt extends TypeDefKind("trait", "trait") with ObjDefKind case object Mxn extends TypeDefKind("mixin", "mixin") with ObjDefKind case object Als extends TypeDefKind("type", "type alias") with ObjDefKind case object Pat extends TypeDefKind("pattern", "pattern") with ClsLikeKind case object Obj extends TypeDefKind("object", "object") with ClsLikeKind case object Mod extends TypeDefKind("module", "module") with ClsLikeKind trait TermDefImpl extends TypeOrTermDef: this: TermDef => def isParameterizedMethod: Bool = (k is Fun) && paramLists.length > 0 trait TypeOrTermDef extends Located: this: TypeDef | TermDef => def describe: Str def k: DeclKind def head: Tree type MaybeIdent = Diagnostic \/ Ident lazy val (symbName, name, paramLists, typeParams, annotatedResultType) : (Opt[MaybeIdent], MaybeIdent, Ls[Tup], Opt[TyTup], Opt[Tree]) = val k = this match case td: TypeDef => td.k case td: TermDef => td.k def rec(t: Tree, symbName: Opt[MaybeIdent], annot: Opt[Tree]): (Opt[MaybeIdent], MaybeIdent, Ls[Tup], Opt[TyTup], Opt[Tree]) = t match // use Foo as foo = ... case InfixApp(typ, Keywrd(Keyword.`as`), id: Ident) if k == Ins => (S(R(id)), R(id), Nil, N, S(typ)) // use Foo = ... case typ if k == Ins => val name = typ.showDbg val id: Ident = Ident(s"instance$$$name") (S(R(id)), R(id), Nil, N, S(typ)) case InfixApp(tree, Keywrd(Keyword.`:`), ann) => rec(tree, symbName, S(ann)) // fun f // fun f(n1: Int) // fun f(n1: Int)(nn: Int) case Apps(PossiblyParenthesized(id: Ident), paramLists) => (symbName, R(id), paramLists, N, annot) // fun f[T] // fun f[T](n1: Int) // fun f[T](n1: Int)(nn: Int) case Apps(App(PossiblyParenthesized(id: Ident), typeParams: TyTup), paramLists) => (symbName, R(id), paramLists, S(typeParams), annot) case Jux(id: Ident, rhs) => val err = L: ErrorReport: msg"Invalid ${k.desc} definition head: unexpected ${rhs.describe} in this position" -> rhs.toLoc :: Nil (S(err), R(id), Nil, N, annot) case Jux(lhs, rhs) => // happens in `fun (op) nme` form val sn = lhs match case Bra(BracketKind.Round, id: Ident) => require(symbName.isEmpty) // TODO R(id) case Bra(BracketKind.Round, lhs) => L: ErrorReport: msg"This ${lhs.describe} is not a valid symbolic name" -> lhs.toLoc :: Nil case tree => L: ErrorReport: msg"Invalid ${k.desc} definition head: unexpected ${lhs.describe} in this position" -> lhs.toLoc :: Nil rec(rhs, S(sn), annot) case _ => (N, L(ErrorReport( msg"Expected a valid ${k.desc} definition head; found ${t.describe} instead" -> t.toLoc :: Nil)), Nil, N, annot) rec(baseHead, N, N) val (baseHead, extension, withPart) = head match case InfixApp(InfixApp(base, Keywrd(Keyword.`extends`), ext), Keywrd(Keyword.`with`), wp) => (base, S(ext), S(wp)) case InfixApp(base, Keywrd(Keyword.`with`), wp) => (base, N, S(wp)) case InfixApp(base, Keywrd(Keyword.`extends`), ext) => (base, S(ext), N) case h => (h, N, N) end TypeOrTermDef trait TypeDefImpl(using State) extends TypeOrTermDef: this: TypeDef => import semantics.* lazy val symbol: DefinitionSymbol[? <: TypeLikeDef] = k match case Cls => ClassSymbol(this, name.getOrElse(Ident("‹error›"))) case Mod | Obj => ModuleOrObjectSymbol(this, name.getOrElse(Ident("‹error›"))) case Als => TypeAliasSymbol(name.getOrElse(Ident("‹error›"))) case Pat => PatternSymbol( name.getOrElse(Ident("‹error›")), paramLists.headOption, rhs.getOrElse(Empty())) case Trt | Mxn => ??? lazy val definedSymbols: Map[Str, BlockMemberSymbol] = // val fromParams = // val fromTypeParams = withPart match case S(blk: Block) => blk.definedSymbols.toMap case _ => Map.empty lazy val clsParams: Ls[Ls[TermSymbol]] = this.paramLists.map: tup => val pts = tup.fields val inUsing = pts.headOption.exists(_.isModified(Ins)) pts.flatMap(_.desugared.asParam(inUsing = inUsing).toOption).map: // case ParamTree(spd = S(_)) => lastWords("spreads are not allowed in class parameters") // TODO: properly report this in Elaborator case pt @ ParamTree(ident = id) => val k = if pt.flags.mut then MutVal else ImmutVal TermSymbol(k, symbol.asClsLike, id) .toList lazy val allSymbols = definedSymbols ++ clsParams.flatten.map(s => s.nme -> s).toMap ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/syntax.scala ================================================ package hkmc2 import scala.util.boundary import mlscript.utils._, shorthands._ import math.Ordered.orderingToOrdered type Raise = Diagnostic => Unit def raise(using r: Raise): Raise = r trait TypeLike: def showIn(prec: Int)(using ShowCtx): Str = ??? lazy val typeVarsList: List[TypeVar] = this match case uv: TypeVar => uv :: Nil // case Recursive(n, b) => n :: b.typeVarsList // case _ => childrenTypes.flatMap(_.typeVarsList) trait NullaryType extends TypeLike trait Located: def toLoc: Opt[Loc] trait AutoLocated extends Located: protected def children: Vector[Located] private var loc: Opt[Loc] = N def withLoc(s: Int, e: Int, ori: Origin): this.type = withLoc(S(Loc(s, e, ori))) def withLoc(loco: Opt[Loc]): this.type = require(loc.isEmpty, s"'$this' already has a location: $loc") loc = loco this def withLocOf(that: Located): this.type = withLoc(that.toLoc) def mkLocWith(otherChildren: Located*): this.type = mkLoc(otherChildren ++ children) this private def mkLoc(allChildren: IterableOnce[Located]) = boundary: if loc.isEmpty then def subLocs = allChildren.iterator.flatMap(_.toLoc.iterator) val spanStart = subLocs.map(_.spanStart).minOption.getOrElse(boundary.break(N)) val spanEnd = subLocs.map(_.spanEnd).maxOption.getOrElse(boundary.break(N)) val origins = subLocs.map(_.origin).toList.distinct assert(origins.size === 1, (origins, this)) val res = S(Loc(spanStart, spanEnd, origins.head)) val _ = withLoc(res) res else loc def toLoc: Opt[Loc] = mkLoc(children) def withoutLoc: this.type = loc = N this private[hkmc2] def getLoc: Opt[Loc] = ??? /** If the identifier is an integer, we can still have a string name used as a name hint. */ final case class TypeVar(val identifier_name: EitherOrBoth[Int, Str]) extends NullaryType with TypeVarImpl: val identifier: Int \/ Str = identifier_name.reduce(left)(right)((x, y) => x) def nameHint: Opt[Str] = identifier_name.second override def toString: Str = identifier.fold("α" + _, identity) trait TypeVarImpl extends Ordered[TypeVar] { self: TypeVar => def name: Opt[Str] = identifier.toOption.orElse(nameHint) def compare(that: TypeVar): Int = (this.identifier.fold((_, ""), (0, _))) compare (that.identifier.fold((_, ""), (0, _))) } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/typing/Type.scala ================================================ package hkmc2 package typing import mlscript.utils.*, shorthands.* import syntax.SpreadKind import semantics.{TypeSymbol, VarSymbol} sealed trait TypeArg: def subst(f: PartialFunction[Type.Ref, Type]): this.type def show: Str def showDbg(using DebugPrinter): Str def ub = this match case Wildcard(_, out) => out case ty: Type => ty def lb = this match case Wildcard(in, _) => in case ty: Type => ty end TypeArg object Type: object Tup: def mk(xs: (Type | (SpreadKind, Type))*): Type = Type.Tup(xs.toList) enum Type extends TypeArg: case Error case Top case Bot case Ref(sym: TypeSymbol | VarSymbol, args: Ls[TypeArg]) case Fun(args: Type, ret: Type, eff: Opt[Type]) case Tup(fields: Ls[Type | (SpreadKind, Type)]) case Neg(t: Type) case Union(lhs: Type, rhs: Type) case Inter(lhs: Type, rhs: Type) // A placeholder for types that are not yet implemented. case NotImplemented override def show: Str = this match case Error => "‹error›" case Top => "⊤" case Bot => "⊥" case Ref(sym, Nil) => sym.nme case Ref(sym, args) => s"${sym.nme}[${args.map(_.show).mkString(", ")}]" case Fun(args, ret, eff) => val effStr = eff.map(e => s" ! ${e.show}").getOrElse("") s"(${args.show}) -> ${ret.show}$effStr" // TODO improve case Neg(t) => s"¬${t.show}" case Union(l, r) => s"(${l.show} ∨ ${r.show})" case Inter(l, r) => s"(${l.show} ∧ ${r.show})" case NotImplemented => "‹not implemented›" case Tup(fields) => val fieldStrs = fields.map: case ty: Type => ty.show case (spd, ty) => s"${spd.str}${ty.show}" s"[${fieldStrs.mkString(", ")}]" override def showDbg(using DebugPrinter): Str = this match case Error => "‹error›" case Top => "⊤" case Bot => "⊥" case Ref(sym, Nil) => s"${sym.showAsPlain}" case Ref(sym, args) => s"${sym.showAsPlain}[${args.map(_.showDbg).mkString(", ")}]" case Fun(args, ret, eff) => val effStr = eff.map(e => s" ! ${e.showDbg}").getOrElse("") s"(${args.showDbg}) -> ${ret.showDbg}$effStr" // TODO improve case Neg(t) => s"¬${t.showDbg}" case Union(l, r) => s"(${l.showDbg} ∨ ${r.showDbg})" case Inter(l, r) => s"(${l.showDbg} ∧ ${r.showDbg})" case NotImplemented => "‹not implemented›" case Tup(fields) => val fieldStrs = fields.map: case ty: Type => ty.showDbg case (spd, ty) => s"${spd.str}${ty.showDbg}" s"[${fieldStrs.mkString(", ")}]" override def subst(f: PartialFunction[Ref, Type]): this.type = this.match case Error => Error case Top => Top case Bot => Bot case ref: Ref if f.isDefinedAt(ref) => f(ref) case ref: Ref => Ref(ref.sym, ref.args.map(_.subst(f))) case Fun(args, ret, eff) => Fun(args.subst(f), ret.subst(f), eff.map(_.subst(f))) case Neg(t) => Neg(t.subst(f)) case Union(l, r) => Union(l.subst(f), r.subst(f)) case Inter(l, r) => Inter(l.subst(f), r.subst(f)) case NotImplemented => NotImplemented case Tup(fields) => Tup(fields.map: case ty: Type => ty.subst(f) case (spd, ty) => (spd, ty.subst(f)) ) .asInstanceOf[this.type] def symbol: Opt[TypeSymbol | VarSymbol] = this match case Ref(sym, _) => S(sym) case _ => N end Type case class Wildcard(in: Type, out: Type) extends TypeArg: override def subst(f: PartialFunction[Type.Ref, Type]): this.type = Wildcard(in.subst(f), out.subst(f)).asInstanceOf override def show: Str = s"? <: ${out.show} >: ${in.show}" override def showDbg(using DebugPrinter): Str = s"? <: ${out.showDbg} >: ${in.showDbg}" end Wildcard ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/ReplHost.scala ================================================ package hkmc2 import java.io.{BufferedWriter, BufferedReader, InputStreamReader, OutputStreamWriter} import mlscript.utils.*, shorthands.* import hkmc2.utils.* /** * A helper class to manipulate an interactive Node.js process. */ class ReplHost(rootPath: Str)(using TL) { private val builder = new java.lang.ProcessBuilder() // `--interactive` always enters the REPL even if stdin is not a terminal. builder.command("node", "--interactive") private val proc = builder.start() private val stdin = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream)) private val stdout = new BufferedReader(new InputStreamReader(proc.getInputStream)) private val stderr = new BufferedReader(new InputStreamReader(proc.getErrorStream)) // Skip the welcome message. collectUntilPrompt() execute("console.info = console.error") execute(s"process.chdir('$rootPath')") /** * This function simply collects output from Node.js until meeting `"\n> "`. * It is useful to skip the welcome message and collect REPL reply from * interactive Node.js. It also filters out syntax errors. * * @return when there are syntax errors, returns `Error` where `syntax` is * `true`; otherwise, returns the result */ private def collectUntilPrompt(): ReplHost.Error | Str = { val buffer = new StringBuilder() while !buffer.endsWith("\n> ") do { val c = stdout.read() if c === -1 then lastWords(s"ReplHost could not read more from NodeJS stdout.") buffer.append(c.toChar) } // Remove the trailing `"\n> "` buffer.delete(buffer.length - 3, buffer.length) val reply = buffer.toString() // tl.log(s"REPL> Collected (raw):\n${reply}") val res = reply.linesIterator.find(_.startsWith(ReplHost.syntaxErrorHead)) match { case None => reply.linesIterator.find(_.startsWith(ReplHost.uncaughtErrorHead)) match { case None => reply case Some(uncaughtErrorLine) => { val message = uncaughtErrorLine.substring(ReplHost.uncaughtErrorHead.length) ReplHost.Error(false, message, reply.take(reply.indexOf(uncaughtErrorLine)).trim()) } } case Some(syntaxErrorLine) => val message = syntaxErrorLine.substring(ReplHost.syntaxErrorHead.length) ReplHost.Error(true, message, reply.take(reply.indexOf(syntaxErrorLine)).trim()) } tl.log(s"REPL> Collected:\n${res}") res } private def consumeStderr(): String = { val buffer = new StringBuilder() while stderr.ready() do buffer.append(stderr.read().toChar) buffer.toString() } /** * Parse query results from collected output from Node.js. * * - If there is a line begining with `"Uncaught SyntaxError: "`, * return the syntax error indicated in that line. * - If character `0x200B` presents in the output, * return the trimmed error message. * - Otherwise, returns the result string. */ private def parseQueryResult(): ReplHost.Error | Str = val parsed = collectUntilPrompt() match case reply: Str => // Find error boundaries. val begin = reply.indexOf(0x200b) val end = reply.lastIndexOf(0x200b) // If there is an error, returns `ReplHost.Error`. if begin >= 0 && end >= 0 then // `console.log` inserts a space between every two arguments, // so + 1 and - 1 is necessary to get correct length. ReplHost.Error(false, reply.substring(begin + 1, end), reply.takeWhile(_ != 0x200b).trim()) else reply case error: ReplHost.Error => error tl.log(s"REPL> Parsed:\n${parsed}") parsed /** * Send code to Node.js process. * * @param code the code to execute */ private def send(code: Str): Unit = tl.log(s"REPL> Sending: ${code}") stdin.write(if code.endsWith("\n") then code else code + "\n") stdin.flush() def query(prelude: Str, code: Str, showStackTrace: Bool): (ReplHost.Reply, Str) = // Wrap the code with `try`-`catch` block. val wrapped = s"${prelude}try { $code; undefined } catch (e) { console.log('\\u200B' + ${if showStackTrace then "(e.stack ?? e)" else "e"} + '\\u200B'); }" // Send the code send(wrapped) (parseQueryResult() match case output: Str => ReplHost.Result(output) case error: ReplHost.Error => error , consumeStderr()) /** * Execute class and function declarations. * * @param code the code to execute * @return */ def execute(code: Str): ReplHost.Reply = { send(code) collectUntilPrompt() match case res: Str => ReplHost.Result(res) case error: ReplHost.Error => error } /** * Kills the Node.js process. */ def terminate(): Unit = proc.destroy() } object ReplHost { /** * The syntax error beginning text from Node.js. */ private val syntaxErrorHead = "Uncaught SyntaxError: " val uncaughtErrorHead = "Uncaught " /** * The base class of all kinds of REPL replies. */ sealed abstract class Reply { /** * Maps the successful part. Like `Option[T].map`. * * @param f the function over * @return */ def map(f: Str => Reply): Reply } /** * Represents a successful reply from Node.js. * * @param content the reply content, i.e. the final result */ final case class Result(content: Str) extends Reply { override def map(f: Str => Reply): Reply = f(content) override def toString(): Str = s"[success] $content" } /** * If the query is `Empty`, we will receive this. */ object Empty extends Reply { override def map(f: Str => Reply): Reply = this override def toString(): Str = "[empty]" } /** * If the query is `Unexecuted`, we will receive this. * @param message the error message */ final case class Unexecuted(message: Str) extends Reply { override def map(f: Str => Reply): Reply = this override def toString(): Str = s"[unexecuted] $message" } /** * If the `ReplHost` captured errors, it will response with `Error`. * @param syntax if `true`, this is a syntax error; otherwise, this is a * runtime error * @param message the error message */ final case class Error(syntax: Bool, message: Str, otherOutputs: Str) extends Reply { override def map(f: Str => Reply): Reply = this override def toString(): Str = if syntax then s"[syntax error] $message" else s"[runtime error] $message" } } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/ReportFormatter.scala ================================================ package hkmc2 import collection.mutable import mlscript.utils.*, shorthands.* /** * Formats diagnostic reports using box drawing characters and/or ANSI colors. * * @param output The output function (e.g., `System.out.println`). * @param colorize Whether to colorize the output using ANSI colors. * @param wrap Optionally wraps each `Raise` call, e.g., with `synchronized`. */ class ReportFormatter( output: Str => Unit, val colorize: Bool, val wrap: Opt[(=> Unit) => Unit] = N ): val MaxLineCount = 5 /** Output main text. */ private def text(str: Str) = output(if colorize then fansi.Color.Red(str).toString else str) /** Output title text. */ private def title(str: Str) = output(if colorize then fansi.Color.LightRed(str).toString else str) val badLines = mutable.Buffer.empty[Int] /** Create a `Raise` dedicated to reporting diagnostics for `file`. */ def mkRaise(file: io.Path): Raise = // This shows the parent directory of a file and its name. val relPath = file.relativeTo(file.up.up).map(_.toString).getOrElse(file.toString) d => def mk = title(s"/!!!\\ Error in $relPath /!!!\\") apply(0, d :: Nil, showRelativeLineNums = false) wrap.fold(mk)(_(mk)) /** Report errors and warnings. */ def apply(blockLineNum: Int, diags: Ls[Diagnostic], showRelativeLineNums: Bool): Unit = diags.foreach { diag => val sctx = Message.mkCtx(diag.allMsgs.iterator.map(_._1), "?") val onlyOneLine = diag.allMsgs.size =:= 1 && diag.allMsgs.head._2.isEmpty val headStr = val headChar = if onlyOneLine then '═' else '╔' diag match case ErrorReport(msg, loco, mkei, src) => src match case Diagnostic.Source.Lexing => s"$headChar══[LEXICAL ERROR] " case Diagnostic.Source.Parsing => s"$headChar══[PARSE ERROR] " case Diagnostic.Source.Compilation => s"$headChar══[COMPILATION ERROR] " case Diagnostic.Source.Runtime => s"$headChar══[RUNTIME ERROR] " case Diagnostic.Source.Typing => s"$headChar══[TYPE ERROR] " case WarningReport(msg, loco, mkei, src) => s"$headChar══[WARNING] " case InternalError(msg, loco, mkei, src) => s"$headChar══[INTERNAL ERROR] " val lastMsgNum = diag.allMsgs.size - 1 var globalLineNum = blockLineNum diag.allMsgs.zipWithIndex.foreach { case ((msg, loco), msgNum) => val isLastMsg = msgNum =:= lastMsgNum val msgStr = msg.showIn(using sctx) if msgNum =:= 0 then text(headStr + msgStr) else if loco.isEmpty && diag.allMsgs.size =:= 1 then if !onlyOneLine then text("╙──") else text(s"${if isLastMsg && loco.isEmpty then "╙──" else "╟──"} ${msgStr}") loco.foreach { loc => val (startLineNum, startLineStr, startLineCol) = loc.origin.fph.getLineColAt(loc.spanStart) badLines += startLineNum if globalLineNum =:= 0 then globalLineNum += startLineNum - 1 val (endLineNum, endLineStr, endLineCol) = loc.origin.fph.getLineColAt(loc.spanEnd) var l = startLineNum var c = startLineCol var lineCount = 0 while l <= endLineNum && lineCount < MaxLineCount do val isLastLineOfLoc = l === endLineNum || lineCount === MaxLineCount - 1 lineCount += 1 val globalLineNum = loc.origin.startLineNum + l - 1 val relativeLineNum = globalLineNum - blockLineNum + 1 val shownLineNum = if showRelativeLineNums && relativeLineNum > 0 then s"l.+$relativeLineNum" else "l." + globalLineNum val prepre = "║ " val pre = s"$shownLineNum: " val curLine = if lineCount < MaxLineCount then loc.origin.fph.lines(l - 1) else "... (more lines omitted) ..." text(prepre + pre + "\t" + curLine) val tickBuilder = new StringBuilder() tickBuilder ++= ( (if isLastMsg && isLastLineOfLoc then "╙──" else prepre) + " " * pre.length + "\t" + " " * (c - 1)) val lastCol = if l =:= endLineNum then endLineCol else curLine.length + 1 while c < lastCol do { tickBuilder += ('^'); c += 1 } if c =:= startLineCol then tickBuilder += ('^') text(tickBuilder.toString) c = 1 l += 1 } } if diag.allMsgs.isEmpty then text("╙──") // if (!mode.fixme) { // if (!allowTypeErrors // && !mode.expectTypeErrors && diag.isInstanceOf[ErrorReport] && diag.source =:= Diagnostic.Typing) // { text("TEST CASE FAILURE: There was an unexpected type error"); failures += globalLineNum } // if (!allowParseErrors // && !mode.expectParseErrors && diag.isInstanceOf[ErrorReport] && (diag.source =:= Diagnostic.Lexing || diag.source =:= Diagnostic.Parsing)) // { text("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } // if (!allowTypeErrors && !allowParseErrors // && !mode.expectWarnings && diag.isInstanceOf[WarningReport]) // { text("TEST CASE FAILURE: There was an unexpected warning"); failures += globalLineNum } // } () } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/Scope.scala ================================================ package hkmc2 package utils import scala.collection.mutable.{Map => MutMap, Set => MutSet} import sourcecode.{Name, Line, FileName} import mlscript.utils.*, shorthands.* import utils.* import hkmc2.Message.MessageContext import Scope.* import hkmc2.semantics.InnerSymbol import hkmc2.semantics.VarSymbol import hkmc2.semantics.Elaborator import hkmc2.semantics.TopLevelSymbol import semantics.Elaborator.State import hkmc2.codegen.Local import hkmc2.codegen.js.JSBuilder /** When `curThis` is N, it means this scope does not rebind `this`. * When `curThis` is Some(None), it means the scope rebinds `this` * to something unknown, following JavaScript's inane `this` handling in `function`s. * When `curThis` is Some(Some(sym)), it means the scope rebinds `this` * to an inner symbol (e.g., class or module). * Note: I made `Scope` a case class just so that it can benefit from `printAsTree`. */ case class Scope (val parentOrCfg: Cfg \/ Scope, val curThis: Opt[Opt[InnerSymbol]], private val bindings: MutMap[Local, Str]) (using State): lazy val parent: Opt[Scope] = parentOrCfg.toOption lazy val cfg: Cfg = parentOrCfg.fold(identity, _.cfg) private val existingNames = MutMap.empty[Str, Local] private var thisProxyAccessed = false lazy val thisProxy = curThis match case N | S(N) => die case S(S(State.globalThisSymbol)) => "globalThis" case S(S(thisSym)) => thisProxyAccessed = true given Raise = throw _ allocateName(thisSym.thisProxy) /** Whether the code generator has produced a binding for `thisProxy` yet. */ var thisProxyDefined: Bool = false private def thisError(thisSym: InnerSymbol)(using Raise): Str = raise: InternalError(msg"`this` not in scope: ${thisSym.toString}" -> N :: Nil, extraInfo = Some(this), source = Diagnostic.Source.Compilation) "‹MISSING_THIS›" def addToBindings(symbol: Local, name: String, shadow: Bool)(using Raise, Line, Name, FileName) = val fullName = if !shadow && lookup(symbol).nonEmpty then raise: InternalError(msg"`${symbol.toString}` is already in scope" -> symbol.toLoc :: Nil, extraInfo = Some(this)) name + "‹BAD_SHADOW›" else name bindings += symbol -> fullName existingNames += fullName -> symbol fullName def getBindings: Iterator[(Local, String)] = bindings.iterator def findThis_!(thisSym: InnerSymbol)(using Raise): Str = // println(s"findThis_! $thisSym") def getParent = parent.fold( if thisSym.isInstanceOf[TopLevelSymbol] // * TopLevelSymbol scopes are special and not nested in codegen `Scope`s // * to avoid needlessly generating new variable names in separate blocks. then "this" else thisError(thisSym) ) curThis match case S(S(sym: TopLevelSymbol)) if sym === State.globalThisSymbol => // `this` at the top level evaluates to `undefined` in strict mode. // We need this because we generate some code that uses `this`/`globalThis`, // for example the symbol loading code. "globalThis" case S(S(`thisSym`)) => "this" // no need to qualify `this` case S(_) => getParent(_.findThisProxy_!(thisSym)) case N => getParent(_.findThis_!(thisSym)) def findThisProxy_!(thisSym: InnerSymbol)(using Raise): Str = // println(s"findThisProxy_! $thisSym") if thisSym.isInstanceOf[TopLevelSymbol] then "globalThis" else curThis match case S(S(`thisSym`)) => thisProxy case _ => parent.fold(thisError(thisSym))(_.findThisProxy_!(thisSym)) def nest: Scope = Scope(R(this), N, MutMap.empty) def getThisScope: Opt[Scope] = curThis.fold(parent.flatMap(_.getThisScope))(_ => S(this)) def getOuterThisScope: Opt[Scope] = parent.flatMap(_.getThisScope) def nestRebindThis[R](thisSym: Opt[InnerSymbol])(k: Scope ?=> R): (Opt[Str], R) = val nested = Scope(R(this), S(thisSym), MutMap.empty) val res = k(using nested) getOuterThisScope match case N => (N, res) case S(outer) => (if outer.thisProxyAccessed then S(outer.thisProxy) else N, res) def inScope(name: Str): Bool = existingNames.contains(name) || parent.exists(_.inScope(name)) def reverseLookup(name: Str): Opt[Local] = existingNames.get(name).orElse(parent.flatMap(_.reverseLookup(name))) def lookup(l: Local): Opt[Str] = // curThis.filter(_ is l).map(_ => thisProxy) orElse bindings.get(l).orElse(parent.flatMap(_.lookup(l))) def lookup_!(l: Local, loc: Opt[Loc])(using Raise): Str = lookup(l).getOrElse: // * Prevent long-winded error messages which quote the entire definition. val extraLoc = l match case sym: semantics.BlockMemberSymbol => sym.trees.collectFirst: case t: syntax.Tree.TypeDef => t.head.toLoc .flatten.orElse(l.toLoc) case other => other.toLoc raise(ErrorReport(msg"No definition found in scope for member '${l.nme}'" -> loc :: (if extraLoc.isEmpty then Nil else msg"which references the symbol introduced here" -> extraLoc :: Nil), extraInfo = Some(l -> l.getClass -> this), source = Diagnostic.Source.Compilation)) l.nme // * Note: it is sound for an existing name to have been allocated with a different prefix (which is only cosmetic) def allocateOrGetName(l: Local, prefix: Str = "")(using Raise): Str = lookup(l).getOrElse(allocateName(l, prefix = prefix)) def allocateName(l: Local, prefix: Str = "", shadow: Bool = false)(using Raise, Line, Name, FileName): Str = val c = cfg // * May be useful later? /* val base: Str = l match case tmp: semantics.TempSymbol if tmp.nameHints.sizeCompare(1) =/= 0 => prefix + tmp.nameHints.head case _ => if l.nme.isEmpty && prefix.isEmpty then "tmp" else prefix + l.nme */ val base = if l.nme.isEmpty && prefix.isEmpty then c.defaultName else prefix + l.nme val realBase = if cfg.escapeChars then Scope.replaceInvalidCharacters(base) else base val name = // Try just realBase. if !c.includeZero && !inScope(realBase) && !JSBuilder.keywords.contains(realBase) && realBase.nonEmpty then realBase else // Try realBase with an integer. ((if c.includeZero then 0 else 1) to Int.MaxValue).iterator .map: i => val idx = if c.useSuperscripts then i.toString.map: case '0' => '⁰' case '1' => '¹' case '2' => '²' case '3' => '³' case '4' => '⁴' case '5' => '⁵' case '6' => '⁶' case '7' => '⁷' case '8' => '⁸' case '9' => '⁹' case _ => die else i.toString s"$realBase$idx" .filterNot(inScope).next() val fullName = addToBindings(l, name, shadow = shadow) fullName object Scope: case class Cfg(escapeChars: Bool, useSuperscripts: Bool, includeZero: Bool, defaultName: Str) object Cfg: val default = Cfg(escapeChars = true, useSuperscripts = false, includeZero = false, defaultName = "tmp") end Cfg def scope(using scp: Scope): Scope = scp def empty(cfg: Cfg)(using State): Scope = Scope(L(cfg), S(S(State.globalThisSymbol)), MutMap.empty) def replaceInvalidCharacters(str: Str): Str = str.iterator.map: case c if c.isLetter || c.isDigit => c // case '\'' => "$tick" case '$' => "$" case '_' => "_" case _ => "$_" .mkString end Scope ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/SymbolSubst.scala ================================================ package hkmc2.utils import hkmc2.semantics.* class SymbolSubst: def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = s def mapFlowSym(s: FlowSymbol): FlowSymbol = s def mapTempSym(s: TempSymbol): TempSymbol = s def mapVarSym(s: VarSymbol): VarSymbol = s def mapInstSym(s: InstSymbol): InstSymbol = s def mapBuiltInSym(s: BuiltinSymbol): BuiltinSymbol = s def mapTermSym(s: TermSymbol): TermSymbol = s def mapCtorSym(s: CtorSymbol): CtorSymbol = s def mapClsSym(s: ClassSymbol): ClassSymbol = s def mapModuleSym(s: ModuleOrObjectSymbol): ModuleOrObjectSymbol = s def mapTypeAliasSym(s: TypeAliasSymbol): TypeAliasSymbol = s def mapPatSym(s: PatternSymbol): PatternSymbol = s def mapTopLevelSym(s: TopLevelSymbol): TopLevelSymbol = s def mapErrorSym(s: ErrorSymbol): ErrorSymbol = s def mapLabelSym(s: LabelSymbol): LabelSymbol = s object SymbolSubst: object Id extends SymbolSubst ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/document/DocBuilder.scala ================================================ package hkmc2 package document import mlscript.utils.*, shorthands.* import utils.* import scala.collection._ import Document._ /** * Use a [[DocBuilder]] to build [[Document]]s for large files. It works similarly to a [[StringBuilder]]. */ case class DocBuilder(NEST_COUNT: Int = DEFAULT_NEST_COUNT) { protected val nestedDocs = mutable.Stack(Document.empty: Document) protected def thisret[T](x: T): this.type = this /** Appends a document to the builder */ def +=(d: Document) = thisret { nestedDocs push (nestedDocs.pop() :: d) } /** Append a document with a conditional break (breaks in a group are only rendered if the group does not fit on one line) */ def +=\(d: Document) = thisret { this += (d :: break) } /** Append a document with an unconditional break (always insert newline) */ def +=\\(d: Document) = thisret { this += (d :: forceBreak) } /** Inserts an unconditional break */ def newLine = this += forceBreak /** Indents all documents appended during the execution of `f` */ def nest(f: => Unit) = thisret { nestedDocs push empty f this += Document.nest(nestedDocs.pop(), NEST_COUNT) } /** * Groups all documents appended during the execution of `f` (grouped documents will have all their conditional * linebreaks break together or not break at all) */ def grouped(f: => Document) = thisret { this += group(f) } /** Inserts curly braces around documents appended by `f`, in Scala style */ def braces(f: => Unit) = thisret { this +=\ "{" nest { f } this +=\ "}" } /** Inserts curly braces around documents appended by the function passed in argument, after appending document `pre` */ def bracesAfter(pre: Document) = { this += pre :: " " braces } /** Inserts a line comment */ def comment(text: String) = this +=\\ ("// " + text) /** Converts the current builder to a proper [[Document]] */ def toDoc = { require(nestedDocs.size == 1); nestedDocs.head } } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/document/Document.scala ================================================ package hkmc2 package document import mlscript.utils.*, shorthands.* import utils.* /* adapted from advanced compiler course project */ import java.io.{ StringWriter, Writer } import scala.annotation.tailrec /** * A basic pretty-printing library, based on Lindig's strict version * of Wadler's adaptation of Hughes' pretty-printer. * * @author Michel Schinz * @author Lionel Parreaux */ sealed abstract class Document { def +(that: Str) = this :: DocText(that) def +(that: Document) = this :: that def ::(that: Document): Document = (this, that) match case (DocNil, _) => that case (_, DocNil) => this case _ => DocCons(that, this) def :/:(hd: Document): Document = hd :: DocBreak(force = false) :: this def :\\:(hd: Document): Document = hd :: DocBreak(force = true) :: this def stripBreaks: Document = this match case DocBreak(false) => DocNil case DocCons(DocText(""), t) => t.stripBreaks case DocCons(h, t) => val res = h.stripBreaks if res is h then this else res :: t case _ => this def isEmpty: Bool = this match case DocNil => true case DocText("") => true case DocGroup(doc) => doc.isEmpty case DocCons(hd, tl) => hd.isEmpty && tl.isEmpty case _ => false /** * Format this document on `writer` and try to set line * breaks so that the result fits within `width` columns. */ def format(width: Int, writer: Writer): Unit = type FmtState = (Int, Boolean, Document) // indent, doBreak, doc @tailrec def fits(w: Int, docs: List[Document]): Boolean = docs match case _ if w < 0 => false case Nil => true case DocNil :: z => fits(w, z) case DocCons(h, t) :: z => fits(w, h :: t :: z) // List prepend here, not doc case DocText(t) :: z => fits(w - t.length, z) case DocNest(ii, d) :: z => fits(w, d :: z) case DocBreak(true) :: z => false case DocBreak(false) :: z => fits(w - 1, z) case DocGroup(d) :: z => fits(w, d :: z) def spaces(n: Int): Unit = var rem = n while rem >= 16 do { writer.write(" "); rem -= 16 } if rem >= 8 then { writer.write(" "); rem -= 8 } if rem >= 4 then { writer.write(" "); rem -= 4 } if rem >= 2 then { writer.write(" "); rem -= 2 } if rem === 1 then { writer.write(" ") } @tailrec def fmt(charsIntoLine: Int, state: List[FmtState]): Unit = def write(indent: Int, text: String): Int = val written = ( if charsIntoLine === 0 then spaces(indent) indent else charsIntoLine ) + text.length writer.write(text) written state match case Nil => () case (_, _, DocNil) :: z => fmt(charsIntoLine, z) case (i, b, DocCons(h, t)) :: z => fmt(charsIntoLine, (i, b, h) :: (i, b, t) :: z) case (i, _, DocText(t)) :: z => fmt(write(i, t), z) case (i, b, DocNest(ii, d)) :: z => fmt(charsIntoLine, (i + ii, b, d) :: z) case (i, doBreak, DocBreak(force)) :: z if doBreak || force => write(i, "\n") fmt(0, z) case (i, false, DocBreak(false)) :: z => fmt(write(i, " "), z) case (i, _b, DocGroup(d)) :: z => val fitsFlat = fits(width - charsIntoLine, d :: z.map(_._3)) fmt(charsIntoLine, (i, !fitsFlat, d) :: z) case _ => () fmt(0, (0, false, DocGroup(this)) :: Nil) end format def mkString(columns: Int = Int.MaxValue): Str = val w = new StringWriter() format(columns, w) w.toString } object Document { /** The empty document */ def empty: Document = DocNil /** A break, which will either be turned into a space or a line break */ def break: Document = DocBreak(false) /** An unconditional break */ def forceBreak: Document = DocBreak(true) /** A document consisting of some text literal; escapes \n characters, unlike DocText */ def text(s: String): Document = val docs = s.split("\\n", -1).map(DocText(_)) // \n needs not be escaped here because it's a single-character string... docs.toSeq.mkDocument(forceBreak) /** * A group, whose components will either be printed with all breaks * rendered as spaces, or with all breaks rendered as line breaks. */ def group(d: Document): Document = DocGroup(d) /** A nested document, which will be indented as specified. */ def nest(doc: Document, indent: Int = DEFAULT_NEST_COUNT): Document = DocNest(indent, doc) val DEFAULT_NEST_COUNT = 2 def bracketed(pre: Str, post: Str, insertBreak: Bool = false)(d: Document): Document = if d.isEmpty then pre + post else group(doc"$pre #{ ${if insertBreak then break :: d else d} #} # $post") def braced(d: Document): Document = bracketed("{", "}")(d) def bracedbk(d: Document): Document = bracketed("{", "}", insertBreak = true)(d) } private case object DocNil extends Document private case class DocBreak(force: Boolean) extends Document private case class DocText(txt: String) extends Document { if txt contains '\n' then System.err.println(s"Warning: DocText should not contain \\n characters; use DocBreak instead:\n\t$txt") } private case class DocGroup(doc: Document) extends Document private case class DocNest(indent: Int, doc: Document) extends Document private case class DocCons(hd: Document, tl: Document) extends Document ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/document/DocumentContext.scala ================================================ package hkmc2 package document import collection.immutable.ArraySeq import collection.immutable.ArraySeq.unsafeWrapArray import util.matching.Regex import mlscript.utils.*, shorthands.* import utils.* import Document._ /** * Doc formatter using string interpolation. * Markers " #{ ", " #} " (including the space character) are used to express nesting * Marker " # " expresses document break, as well as "\n". * * For example: * {{{ * doc"class A { #{ # $decl #} # }" * }}} * instead of: * {{{ * "class A {" :: Document.nest(2, DocBreak :: decl) :/: "}" * }}} */ object DocumentContext: private val NestRegex = " #\\{ ".r private val UnNestRegex = " #\\} ".r private val BeginGroupRegex = "\\\\\\{".r private val EndGroupRegex = "\\\\\\}".r private val DocBreakRegex = " # ".r private val ForceDocBreakRegex = """\\n""".r case object Nest; type Nest = Nest.type case object UnNest; type UnNest = UnNest.type case object BeginGroup; type BeginGroup = BeginGroup.type case object EndGroup; type EndGroup = EndGroup.type case object Insert; type Insert = Insert.type case class RawDocText(s: String) // avoids the "\n chars" warning of DocText import DocumentContext.* class DocumentContext(ctx: StringContext) { object doc { def apply(docs: Document*): Document = type DocsMarkers = Document | Nest | UnNest | BeginGroup | EndGroup | RawDocText type DocsMarkersInsert = DocsMarkers | Insert assert(ctx.parts.size == docs.size + 1 && ctx.parts.size > 0) def interleave(docs: Seq[DocsMarkersInsert], interleaved: DocsMarkersInsert) = docs.head :: (docs.iterator.drop(1).map { List(interleaved, _) }.flatten.toList: Ls[DocsMarkersInsert]) def splitOn(mark: Regex, interleaved: DocsMarkersInsert) = (ds: Ls[DocsMarkersInsert]) => ds.flatMap: case RawDocText(str) => interleave(unsafeWrapArray(mark.pattern.split(str, -1)).map(RawDocText(_)), interleaved) case d: DocsMarkersInsert => Seq(d) // Makes a sequence of the parts separated with Nest, UnNest and Insert (for positions where docs are to be inserted) val parts = ( splitOn(NestRegex, Nest) andThen splitOn(UnNestRegex, UnNest) andThen splitOn(BeginGroupRegex, BeginGroup) andThen splitOn(EndGroupRegex, EndGroup) andThen splitOn(DocBreakRegex, DocBreak(false)) andThen splitOn(ForceDocBreakRegex, DocBreak(true)) // interpolated strings don't get special chars replaced (we escape \n for the regex) )(interleave(ctx.parts.map(RawDocText(_)), Insert)).map: case RawDocText(s) => text(s) // 'text' escapes \n chars case d => d val diter = docs.iterator val allDocs = parts.map: case Insert => diter.next() case d: DocsMarkers => d // Processes the sequence, replacing Nest..UnNest pairs by a nest(..) call // and BeginGroup..EndGroup pairs by a group(..) call var curDocs = allDocs def process(acc: Document, outer: Insert | Nest | BeginGroup): Document = curDocs match case Nil => outer match case Insert => acc case Nest => throw IllegalArgumentException("Unmatched opening nest marker") case BeginGroup => throw IllegalArgumentException("Unmatched opening group marker") case doc :: rest => curDocs = rest doc match case Nest => process(acc :: nest(process(empty, Nest)), outer) case UnNest => outer match case Nest => acc case _ => throw IllegalArgumentException("Closing nest marker closes nothing") case BeginGroup => process(acc :: group(process(empty, BeginGroup)), outer) case EndGroup => outer match case BeginGroup => acc case _ => throw IllegalArgumentException("Closing group marker closes nothing") case doc: Document => process(acc :: doc, outer) case RawDocText(str) => process(acc :: text(str), outer) end process process(empty, Insert) } } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/document/Liftable.scala ================================================ package hkmc2 package document import mlscript.utils.*, shorthands.* import utils.* import scala.annotation.implicitNotFound /** * Describes how to lift objects using the `doc` string interpolation. */ @implicitNotFound("Cannot find document.Liftable implementation for type ${T}") trait Liftable[-T] { def apply(value: T): Document } object Liftable { def apply[T](f: T => Document): Liftable[T] = new Liftable[T] { def apply(value: T): Document = f(value) } } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/document/package.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import utils.* import scala.language.implicitConversions /** * A basic pretty-printing library that handles indentation and conditional line-breaking. * * The most convenient way to build a Document is with a combination of `doc"..."` string interpolation and use of [[DocBuilder]]. * To use `doc` string interpolation, you have to import `document._`. * */ package object document { import Document._ implicit def toDocumentContext(ctx: StringContext): DocumentContext = new DocumentContext(ctx) implicit def toDocument[T: Liftable](x: T): Document = implicitly[Liftable[T]].apply(x) implicit val docLift: Liftable[Document] = Liftable[Document](identity) implicit val intLift: Liftable[Int] = Liftable[Int](_.toString) implicit val strLift: Liftable[String] = Liftable[String](text) // /** Lifting document lists with line breaks in between is almost never what the user wants, and leads to errors */ // implicit def seqLift[T: Liftable]: Liftable[Seq[T]] = Liftable[Seq[T]] { ls => // val lift = implicitly[Liftable[T]].apply _ // (ls map lift).mkDocument() // } implicit class LiftableSeqOps[A: Liftable](docs: Seq[A]) { def mkDocument(pre: Document, sep: Document, post: Document): Document = docs filter (_ != empty) match { case d +: ds => pre :: ((d: Document) +: ds.map(sep :: _)).foldLeft(empty)(_ :: _) :: post case _ => pre :: post } def mkDocument(sep: Document = empty): Document = mkDocument(empty, sep, empty) } } ================================================ FILE: hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala ================================================ package hkmc2 import scala.util.chaining.scalaUtilChainingOps import mlscript.utils.*, shorthands.* given utils.TraceLogger => DebugPrinter = summon[utils.TraceLogger].debugPrinter extension (s: Str) def escaped: Str = s.iterator.flatMap: case '\b' => "\\b" case '\t' => "\\t" case '\n' => "\\n" case '\r' => "\\r" case '\f' => "\\f" case '"' => "\\\"" case '\\' => "\\\\" case c if c.isControl => f"\\u${c.toInt}%04x" case c => c.toString .mkString("\"", "", "\"") abstract class OnceFunction[-A, +B] extends (A => B): private var called = false final def apply(x: A): B = if called then throw new IllegalStateException("This function can only be called once.") called = true applyBody(x) def applyBody(x: A): B inline def ensureOnce[A, B](f: OnceFunction[A, B]): f.type = f import hkmc2.semantics.{TermDefFlags, FldFlags, ParamListFlags, Resolvable} import scala.collection.mutable.Buffer trait ProductWithTail extends Product trait ProductWithExtraInfo extends Product: def extraInfo(using DebugPrinter): Str extension (t: Product) def showAsTree(using ts: DebugPrinter): Str = ts.printProduct(false, t) extension (t: Any) def showAsPlain(using ts: DebugPrinter): Str = ts.printPlain(t) class DebugPrinter: given DebugPrinter = this def preProcess(t: Product): Product = t def postProcess(t: Product): Str = "" def printPlain(v: Any): Str = v.toString def printProduct(inTailPos: Bool, t: Product): Str = def aux(v: Any, inTailPos: Bool = false): Str = v match case Some(v) => "S of " + aux(v) case None => "N" case Nil => "Nil" case xs: List[_] => "Ls of \n" + xs.iterator.map(aux(_)).mkString("\n").indent(" ") case xs: Vector[_] => "Vector of \n" + xs.iterator.map(aux(_)).mkString("\n").indent(" ") case s: Str => s.escaped case TermDefFlags(isMethod) => val flags = Buffer.empty[Str] if isMethod then flags += "method" flags.mkString("(", ", ", ")") case FldFlags(mut, spec, pat, value) => val flags = Buffer.empty[Str] if mut then flags += "mut" if spec then flags += "spec" if pat then flags += "pat" if value then flags += "val" flags.mkString("(", ", ", ")") case ParamListFlags(ctx) => val flags = Buffer.empty[Str] if ctx then flags += "ctx" flags.mkString("(", ", ", ")") case Loc(start, end, origin) => val (sl, _, sc) = origin.fph.getLineColAt(start) val (el, _, ec) = origin.fph.getLineColAt(end) s"Loc at :$sl:$sc-$el:$ec" case codegen.Scoped(syms, body) => val symsStr = "{" + syms.toArray.sortBy(_.uid).map(_.showAsPlain).mkString(", ") + "}" s"Scoped(syms = $symsStr): \n" + s"body = ${printProduct(false, body)}".indent(" ") case t: Product => printProduct(inTailPos, t) case v => printPlain(v) val tt = preProcess(t) val postfix = postProcess(tt) val midfix = tt match case t: ProductWithExtraInfo => t.extraInfo match case "" => "" case str => "{" + str + "}" case _ => "" val prefix = tt.productPrefix + midfix + postfix tt.productArity match case 0 => prefix case 1 => prefix + " of " + aux(tt.productElement(0)) case a => var args = tt.productIterator.zipWithIndex.filterNot(DebugPrinter.emptyValues contains _._1).map: case (v, i) => tt.productElementName(i) + " = " + aux(v, tt.isInstanceOf[ProductWithTail] && i === a - 1) // val emptyArgs = args.isEmpty // val argsStr = if emptyArgs then "‹empty›" else args.mkString("\n") // prefix + locally: // if inTailPos then ": \\\n" + argsStr // else ":\n" + argsStr.indent(" ") if args.isEmpty then prefix + ": ‹empty›" else val argsStr = args.mkString("\n") prefix + locally: if inTailPos then ": \\\n" + argsStr else ":\n" + argsStr.indent(" ") end DebugPrinter object DebugPrinter: val emptyValues: Set[Any] = Set( None, Nil, Vector.empty, semantics.PlainParamList(Nil), ParamListFlags.empty, TermDefFlags.empty, FldFlags.empty, codegen.End(""), ) end DebugPrinter extension [A](self: Opt[A]) def mapConserve[B](f: A => A): Opt[A] = self match case S(v) => val v2 = f(v) if v2 is v then self else S(v2) case N => N extension [A](ls: Ls[A]) /** Apply a special function for lists with one element. */ def foldSingleton[B](no: Ls[A] => B)(yes: A => B): B = ls match case x :: Nil => yes(x) case Nil | _ :: _ => no(ls) extension (n: Int) /** Converts a number to its English word representation. */ def spelled: Str = n match case 0 => "zero" case 1 => "one" case 2 => "two" case 3 => "three" case 4 => "four" case 5 => "five" case 6 => "six" case 7 => "seven" case 8 => "eight" case 9 => "nine" case _ => n.toString object English: // These lists are not complete (nor are they intended to be). Please add // necessary words as needed. val irregularPlurals = Map("datum" -> "data", "index" -> "indices") val ves = Set("proof") val o = Set("zero") extension (str: Str) /** Converts a singular noun to its plural form using English pluralization * rules. The rules should be updated as needed. */ def pluralize: Str = English.irregularPlurals.getOrElse(str, if str.isEmpty then str else str(str.size - 1) match // -s, -sh, -ch, -x, -z -> +es; e.g., bus -> buses, box -> boxes case 's' | 'x' | 'z' => str + "es" // -sh, -ch -> +es; e.g., brush -> brushes, church -> churches case 'h' if str.size > 1 && (str(str.size - 2) === 's' || str(str.size - 2) === 'c') => str + "es" // -o -> +es (with some exceptions); e.g., potato -> potatoes case 'o' if !English.o.contains(str) => str + "es" // -[^aeiou]y -> -ies; e.g., city -> cities, but not toy -> toys case 'y' if str.size > 1 => str(str.size - 2) match case 'a' | 'e' | 'i' | 'o' | 'u' => str + "s" // Vowel before 'y' case _ => str.dropRight(1) + "ies" // Consonant before 'y' // -f -> -ves (with exceptions); e.g., leaf -> leaves case 'f' if !English.ves.contains(str) => str.dropRight(1) + "ves" // -fe -> -ves; e.g., knife -> knives case 'e' if str.size > 1 && str(str.size - 2) === 'f' => str.dropRight(2) + "ves" // Default case: just add 's' case _ => str + "s") /** Formats a number and a noun as a human-readable string. */ infix def countBy(n: Int): Str = s"${n.spelled} ${if n === 1 then str else str.toLowerCase.pluralize}" ================================================ FILE: hkmc2/shared/src/main/scala/utils/EitherOrBoth.scala ================================================ package mlscript.utils import shorthands.* enum EitherOrBoth[+A, +B]: case First[+A](value: A) extends EitherOrBoth[A, Nothing] case Second[+B](value: B) extends EitherOrBoth[Nothing, B] case Both[+A, +B](firstValue: A, secondValue: B) extends EitherOrBoth[A, B] def first: Opt[A] = this `|>?` : case First(v) => v case Both(v, _) => v def second: Opt[B] = this `|>?` : case Second(v) => v case Both(_, v) => v def options: (Opt[A], Opt[B]) = (first, second) def fold[R](f: A => R)(g: B => R)(h: (A, B) => R): R = this match case First(v) => f(v) case Second(v) => g(v) case Both(a, b) => h(a, b) def reduce[R](f: A => R)(g: B => R)(h: (R, R) => R): R = fold(f)(g)((x, y) => h(f(x), g(y))) def merge[R](f: (A | B) => R)(h: (R, R) => R): R = reduce(f)(f)(h) end EitherOrBoth export EitherOrBoth.{ First, Second, Both } ================================================ FILE: hkmc2/shared/src/main/scala/utils/FastParseHelpers.scala ================================================ package hkmc2 import scala.collection.mutable.ArrayBuffer import mlscript.utils._, shorthands._ class FastParseHelpers(val blockStr: Str, val lines: collection.IndexedSeq[Str]): def this(lines: IndexedSeq[Str]) = this(lines.mkString("\n"), lines) def this(blockStr: Str) = this(blockStr, blockStr.splitSane('\n')) // this line-parsing logic was copied from fastparse internals: // val lineNumberLookup: Array[Int] = fastparse.internal.Util.lineNumberLookup(blockStr) val lineNumberLookup: Array[Int] = mkLineNumberLookup(blockStr) def mkLineNumberLookup(data: String): Array[Int] = val lineStarts = new ArrayBuffer[Int]() var i = 0 var col = 1 var cr = false var prev: Character = null while i < data.length do val char = data(i) if char == '\r' then if prev != '\n' && col == 1 then lineStarts.append(i) col = 1 cr = true else if char == '\n' then if prev != '\r' && col == 1 then lineStarts.append(i) col = 1 cr = false else if col == 1 then lineStarts.append(i) col += 1 cr = false prev = char i += 1 if col == 1 then lineStarts.append(i) lineStarts.toArray def getLineColAt(index: Int): (Int, String, Int) = val lineNum = lineNumberLookup.indexWhere(_ > index) match case -1 => lineNumberLookup.length case n => math.max(1, n) val idx = lineNum.min(lines.length) - 1 val colNum = index - lineNumberLookup(idx) + 1 val lineStr = lines(idx) (lineNum, lineStr, colNum) ================================================ FILE: hkmc2/shared/src/main/scala/utils/TraceLogger.scala ================================================ package hkmc2 package utils import mlscript.utils.*, shorthands.* type TL = TraceLogger def tl(using TL): TL = summon abstract class TraceLogger(using val debugPrinter: DebugPrinter): def doTrace: Bool = true protected val noPostTrace: Any => Str = _ => "" protected var indent = 0 def trace[T](pre: => Str, post: T => Str = noPostTrace)(thunk: => T): T = { log(pre) enter() val res = try thunk finally exit() if post isnt noPostTrace then log(post(res)) res } inline def traceNot[T](pre: => Str, post: T => Str = noPostTrace)(thunk: => T): T = thunk inline def enter() = indent += 1 inline def exit() = indent -= 1 protected[hkmc2] def emitDbg(str: Str): Unit = scala.Predef.println(str) inline def log(msg: => Any): Unit = log(msg, noIndent = false) def logs(msgs: => Any*): Unit = if doTrace then msgs.foreach(log(_)) def log(msg: => Any, noIndent: Bool = false): Unit = if doTrace then emitDbg(if noIndent then msg.toString else "| " * indent + msg.toString.indentNewLines("| " * indent + "> ")) protected var scope: Opt[Str] = N def scoped[T](flag: Str)(thunk: => T): T = var oldScope = scope scope = S(flag) try thunk finally scope = oldScope ================================================ FILE: hkmc2/shared/src/test/js/MyClass.mjs ================================================ export default class MyClass { constructor(name) { this.name = name; } greet() { console.log(`Hello from ${this.name}!`); } } ================================================ FILE: hkmc2/shared/src/test/js/Test.mjs ================================================ export function greet() { console.log('Hello from Test.mjs!'); } ================================================ FILE: hkmc2/shared/src/test/mlscript/ChangedTests.cmd ================================================ :tests ================================================ FILE: hkmc2/shared/src/test/mlscript/ElabScratch.mls ================================================ :js // :de // :sjs // :pt // :elt :global :d ================================================ FILE: hkmc2/shared/src/test/mlscript/HkScratch.mls ================================================ :js // :de // :sjs // :pt // :elt :global // :d // :todo ================================================ FILE: hkmc2/shared/src/test/mlscript/OverloadedModulesInSignatures.mls ================================================ :js class ClsMod with val whoami = "class" module ClsMod with val whoami = "module" type TypMod = Int module TypMod with val whoami = "module" :fixme fun TrmMod = 42 module TrmMod with val whoami = "module" //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'TrmMod' //│ ║ l.14: fun TrmMod = 42 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── with module of the same name //│ ║ l.15: module TrmMod with //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.16: val whoami = "module" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ fun f: ClsMod = 42 :rt f //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:f⁰,typ=class:ClsMod⁰}: //│ t = Ref{sym=member:f¹} of member:f¹ //│ sym = term:f⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 fun f: module ClsMod = ClsMod :rt f //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:f²,typ=module:ClsMod¹}: //│ t = Ref{sym=member:f³} of member:f³ //│ sym = term:f² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class ClsMod { whoami: "module" } fun f: TypMod = 42 :rt f //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:f⁴,typ=type:TypMod⁰}: //│ t = Ref{sym=member:f⁵} of member:f⁵ //│ sym = term:f⁴ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 fun f: module TypMod = TypMod :rt f //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:f⁶,typ=module:TypMod¹}: //│ t = Ref{sym=member:f⁷} of member:f⁷ //│ sym = term:f⁶ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class TypMod { whoami: "module" } :e fun f: TrmMod = 42 //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference of type TrmMod denoting module 'TrmMod'. //│ ║ l.75: fun f: TrmMod = 42 //│ ║ ^^^^^^ //│ ╙── Function must be marked as returning a 'module' in order to have a module return type. :sjs fun f = TrmMod //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f5; f5 = function f() { return TrmMod1() }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :fixme :expect 42 f //│ ═══[RUNTIME ERROR] TypeError: Class constructor TrmMod cannot be invoked without 'new' //│ ═══[RUNTIME ERROR] Expected: '42', got: 'undefined' :sjs fun f: module TrmMod = TrmMod //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; f6 = function f() { return TrmMod1.class }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun assertModule(module m: ClsMod): module ClsMod = m assertModule(ClsMod).whoami //│ = "module" assertModule(TypMod).whoami //│ = "module" :fixme assertModule(TrmMod).whoami //│ ═══[RUNTIME ERROR] TypeError: Class constructor TrmMod cannot be invoked without 'new' fun assertNonModule(m) = m // FIXME: should not resolve to module's `.whoami`, hence should be an error :breakme assertNonModule(ClsMod).whoami //│ = "module" // FIXME: should not resolve to module's `.whoami`, hence should be an error :breakme assertNonModule(TypMod).whoami //│ = "module" // FIXME: should not resolve to module's `.whoami`, hence should be an error // FIXME: Should module-check as the term module (or probably we will remove module-checks before that) :breakme :fixme assertNonModule(TrmMod).whoami //│ ═══[RUNTIME ERROR] TypeError: Class constructor TrmMod cannot be invoked without 'new' type Foo[A] = Int module Foo :e fun f(x: Foo): Foo = x //│ ╔══[COMPILATION ERROR] Expected 1 type arguments, got none //│ ║ l.136: fun f(x: Foo): Foo = x //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Expected 1 type arguments, got none //│ ║ l.136: fun f(x: Foo): Foo = x //│ ╙── ^^^ fun f(x: Foo[Int]): Foo[Int] = x fun f(module x: Foo): module Foo = x :e fun f(module x: Foo[Int]): module Foo[Int] = x //│ ╔══[COMPILATION ERROR] Expected no type arguments, got 1 //│ ║ l.149: fun f(module x: Foo[Int]): module Foo[Int] = x //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expected no type arguments, got 1 //│ ║ l.149: fun f(module x: Foo[Int]): module Foo[Int] = x //│ ╙── ^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/AccountingTest.mls ================================================ :js import "../../mlscript-compile/apps/Accounting.mls" val acc = new Accounting //│ acc = Accounting { //│ warnings: [], //│ Project: fun Project { class: class Project }, //│ Line: fun Line { class: class Line }, //│ lines: [], //│ Report: fun Report { class: class Report } //│ } val proj = acc.Project("P1") //│ proj = Project("P1") val l1 = acc.mkLine("L1", proj, 200_000, true) val l2 = acc.mkLine("L2", proj, 1_000_000, true) //│ l1 = Line("L1", Project("P1"), 200000, true) //│ l2 = Line("L2", Project("P1"), 1000000, true) :... //│ ———————————————————————————————————————————————————————————————————————————————— acc.process of "out/apps/AccountingTest.md", report => ... report.snapShot of "Initial" l1.expense(33_880.87) l2.expense(666_666) // Luyu's juicy post-PQE salary report.snapShot of "Y1" l1.expense(10_000) l2.expense(200_000) report.snapShot of "Y2" l1.expense(10_000) l2.expense(130_000) report.snapShot of "Y3" l1.mustBeEmpty() l2.mustBeEmpty() //│ > Report written to out/apps/AccountingTest.md //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/CSVTest.mls ================================================ :js import "../../mlscript-compile/apps/CSV.mls" let csv = CSV(undefined) //│ csv = CSV(",") csv.strDelimiter //│ = "," csv.objPattern //│ = /(\,|\r?\n|\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^"\,\r\n]*))/gi csv.toArrays("a,b,c") //│ = [["a", "b", "c"]] csv.toArrays("a,b,c\n1,2,3\n4,5,6") //│ = [["a", "b", "c"], ["1", "2", "3"], ["4", "5", "6"]] csv.toArrays("a,b,c\n\n1,2,3\n4,5,6\n") //│ = [["a", "b", "c"], [""], ["1", "2", "3"], ["4", "5", "6"], [""]] csv.toArrays(",") //│ = [[""]] csv.toArrays(",,") //│ = [["", ""]] csv.toArrays("1,\"2\"") //│ = [["1", "2"]] csv.toArrays("1,\"2,3\",4") //│ = [["1", "2,3", "4"]] csv.toArrays("1,\"\",4") //│ = [["1", "", "4"]] csv.toArrays("1,\",4") //│ = [["1", "", "4"]] csv.toArrays("1,\",4\"") //│ = [["1", ",4"]] // We would probably rather expect "A\\\"B", here csv.toArrays("1,\"A\\\"B\",4").0.1 //│ = "A\\" ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/IterTest.mls ================================================ :js import "../../mlscript-compile/Iter.mls" import "../../mlscript-compile/Stack.mls" import "../../mlscript-compile/MutMap.mls" open Iter open Stack { Cons, Nil } [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] filtering of x => x % 2 is 0 mapping of x => x * 2 toArray() //│ = [4, 8, 12, 16, 20] ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10] filtering of x => x % 2 is 0 folded of "[", (x, y) => if x is "[" then x + y else x + ", " + y) + "]" //│ = "[2, 4, 6, 8, 10]" [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] filtering of x => x % 2 is 1 rightFolded of Nil, Cons //│ = Cons(1, Cons(3, Cons(5, Cons(7, Cons(9, Nil))))) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] filtering of x => x % 2 is 0 reduced of (x, y) => x + y //│ = 30 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] filtering of x => x % 2 is 0 folded of 1, * //│ = 3840 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] leaving(2) taking(5) toArray() //│ = [3, 4, 5, 6, 7] ["foo", "bar", "baz"] zippingWithIndex() toArray() //│ = [["foo", 0], ["bar", 1], ["baz", 2]] ["foo", "bar", "baz"] zippingWithIndex() mapping of case [x, y] then [y, x] toArray() //│ = [[0, "foo"], [1, "bar"], [2, "baz"]] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] joined(" <> ") //│ = "1 <> 2 <> 3 <> 4 <> 5 <> 6 <> 7 <> 8 <> 9 <> 10" ["foo", "bar", "", "", "baz"] flattening() toArray() //│ = ["f", "o", "o", "b", "a", "r", "b", "a", "z"] ["", "foo", "", "bar", "", "", "baz"] mapping of str => Array.from(str).reverse() flattening() toArray() //│ = ["o", "o", "f", "r", "a", "b", "z", "a", "b"] ["", "", "", "", ""] flattening() toArray() //│ = [] ["foo", "bar", [], ["qax"], "", "baz"] flattening() toArray() //│ = ["f", "o", "o", "b", "a", "r", "qax", "b", "a", "z"] [1, 3, 5, 7, 9] mapping of x => x * 2 appended of [2, 4, 6, 8, 10] mapping of x => x / 2 toArray() //│ = [1, 3, 5, 7, 9, 1, 2, 3, 4, 5] [7, 8, 9, 10] filtering of x => x % 2 is 0 mapping of x => x * 2 appended of [] toArray() //│ = [16, 20] [["foo", 1]] MutMap.toMap() mapping of case [k, v] then [k + "!", v * 2] appended of MutMap.toMap([["bar", 2]]) toArray() //│ = [["foo!", 2], ["bar", 2]] [1, 2, 3, 4, 5, 6] Iter.some of x => x % 3 == 0 //│ = true [1, 2, 3, 4, 5, 6] Iter.some of x => x % 9 == 0 //│ = false [1, 2, 3, 4, 5, 6] Iter.every of x => x % 2 == 0 //│ = false [1, 2, 3, 4, 5, 6] Iter.mapping of x => x * 2 Iter.every of x => x % 2 == 0 //│ = true [1, 2, -3, 4, 5, 6] Iter.mapping of x => () => print(x + " was accessed"); x Iter.some of get => get() < 0 //│ > 1 was accessed //│ > 2 was accessed //│ > -3 was accessed //│ = true [1, 2, 3, -4, 5, 6] Iter.mapping of x => () => print(x + " was accessed"); x Iter.every of get => get() > 0 //│ > 1 was accessed //│ > 2 was accessed //│ > 3 was accessed //│ > -4 was accessed //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/.gitignore ================================================ rule.html ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/CamlLightTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/Test.mls" open Test { example } // ____ _ __ ___ _ // / ___|___ | | ___ _ __ \ \ / / |__ ___ ___| | // | | / _ \| |/ _ \| '__| \ \ /\ / /| '_ \ / _ \/ _ \ | // | |__| (_) | | (_) | | \ V V / | | | | __/ __/ | // \____\___/|_|\___/|_| \_/\_/ |_| |_|\___|\___|_| // // sources/examples/colwheel/colwheel.ml // ===================================== // Note: We do not support or patterns for now, so I expanded the or patterns. example of """ let max = 255;; let nround x y = (2*x+y)/(2*y);; let rgb_of_hsb h s v = let h = h*6 in let i = h/max*max in let f = h-i in let m = v*(max-s)/max and n = v*(max-s*f/max)/max and k = v*(max-s*(max-f)/max)/max in graphics__rgb (nround (max*( match i/max with | 0 -> v | 6 -> v | 1 -> n | 2 -> m | 3 -> m | 4 -> k | _ -> v )) max) (nround (max*( match i/max with | 0 -> k | 6 -> k | 1 -> v | 2 -> v | 3 -> n | 4 -> m | _ -> m )) max) (nround (max*( match i/max with | 0 -> m | 6 -> m | 1 -> m | 2 -> k | 3 -> v | 4 -> v | _ -> n )) max) ;; let pi180 = 3.141592654 /. 180.0;; let wheel s v r = for theta = 0 to 23 do set_color (rgb_of_hsb (theta * max / 24) s v); fill_arc (size_x()/2) (size_y()/2) r r (theta * 15) (theta * 15 + 15) done ;; let wheels v = for r = 8 downto 1 do wheel (r * max / 8) v (r * (size_y()/20)) done ;; let main() = open_graph ""; let (msg_w, msg_h) = text_size "Press 'q' to quit R=999 G=999 B=999" in try wheels max; set_color foreground; moveto 0 0; draw_string "Press 'q' to quit"; while true do let e = wait_next_event [Button_down; Key_pressed] in if e.keypressed then begin match e.key with | "0" -> clear_graph(); wheels ((int_of_char e.key - 48) * max / 9) | "q" -> raise Exit | _ -> () end else if e.button then begin let c = point_color e.mouse_x e.mouse_y in let r = c lsr 16 and g = (c lsr 8) land 255 and b = c land 255 in set_color background; fill_rect 0 0 msg_w msg_h; set_color foreground; moveto 0 0; draw_string ("Press 'q' to quit R=" ^ string_of_int r ^ " G=" ^ string_of_int g ^ " B=" ^ string_of_int b) end done with Exit -> close_graph() ;; if sys__interactive then () else begin main(); exit 0 end;; """ //│ > «let max = 255» //│ > «let nround x y = 2 * x + y / 2 * y» //│ > «let rgb_of_hsb h s v = let h = h * 6 in let i = h / max * max in let f = h - i in let m = v * (max - s) / max and n = v * (max - s * f / max) / max and k = v * (max - s * (max - f) / max) / max in graphics__rgb (nround (max * (match i / max with 0 -> v | 6 -> v | 1 -> n | 2 -> m | 3 -> m | 4 -> k | _ -> v)) max) (nround (max * (match i / max with 0 -> k | 6 -> k | 1 -> v | 2 -> v | 3 -> n | 4 -> m | _ -> m)) max) (nround (max * (match i / max with 0 -> m | 6 -> m | 1 -> m | 2 -> k | 3 -> v | 4 -> v | _ -> n)) max)» //│ > «let pi180 = 3.141592654 /. 180.0» //│ > «let wheel s v r = for theta = 0 to 23 do set_color (rgb_of_hsb (theta * max / 24) s v); fill_arc (size_x () / 2) (size_y () / 2) r r (theta * 15) (theta * 15 + 15) done» //│ > «let wheels v = for r = 8 to 1 do wheel (r * max / 8) v (r * (size_y () / 20)) done» //│ > «let main () = open_graph ""; let (msg_w, msg_h) = text_size "Press…" in try wheels max; set_color foreground; moveto 0 0; draw_string "Press…"; while true do let e = wait_next_event [Button_down; Key_pressed] in if e.keypressed then match e.key with "0" -> clear_graph (); wheels ((int_of_char e.key - 48) * max / 9) | "q" -> raise Exit | _ -> () then if e.button then let c = point_color e.mouse_x e.mouse_y in let r = c lsr 16 and g = c lsr 8 land 255 and b = c land 255 in set_color background; fill_rect 0 0 msg_w msg_h; set_color foreground; moveto 0 0; draw_string ("Press…" ^ string_of_int r ^ " G=" ^ string_of_int g ^ " B=" ^ string_of_int b) then None done» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «if sys__interactive then () then main (); exit 0» // _ _ _ // | | | | __ _ _ __ ___ (_) // | |_| |/ _` | '_ \ / _ \| | // | _ | (_| | | | | (_) | | // |_| |_|\__,_|_| |_|\___/|_| // // sources/examples/hanoi/hanoi.ml // =============================== // The original example is too long and would cause the lexer to overflow, // so we will temporarily split it into three parts for parsing. example of """ let spaces n = make_string n " ";; let disk size = let right_half = make_string size ">" and left_half = make_string size "<" in left_half ^ "|" ^ right_half;; let disk_number n largest_disk_size = let white_part = spaces (largest_disk_size + 1 - n) in white_part ^ (disk n) ^ white_part;; let peg_base largest_disk_size = let half = make_string largest_disk_size "_" in " " ^ half ^ "|" ^ half ^ " ";; let rec peg largest_disk_size = function | (0, []) -> [] | (0, head::rest) -> disk_number head largest_disk_size :: peg largest_disk_size (0, rest) | (offset, lst) -> disk_number 0 largest_disk_size :: peg largest_disk_size (offset-1, lst);; let rec join_lines l1 l2 l3 = match (l1, l2, l3) with | ([], [], []) -> [] | (t1::r1, t2::r2, t3::r3) -> (t1 ^ t2 ^ t3) :: join_lines r1 r2 r3 | _ -> failwith "join_lines";; let print_line line = print_string line; print_newline();; let print_game num_disks start middle end_ = let drawing = join_lines (peg num_disks start) (peg num_disks middle) (peg num_disks end_) in do_list print_line drawing; let b = peg_base num_disks in print_line (b ^ b ^ b);; let add_disk disk (offset, disks) = (offset - 1, disk::disks);; let top = function | (offset, top :: rest) -> top | (offset, []) -> failwith "top: empty peg";; let remove_top = function | (offset, top :: rest) -> (offset + 1, rest) | (offset, []) -> failwith "remove_top: empty peg";; let move (name_start, peg_start) (name_finish, peg_finish) = print_line ("I move a disk from " ^ name_start ^ " to " ^ name_finish); let moved_disk = top !peg_start in peg_start := remove_top !peg_start; peg_finish := add_disk moved_disk !peg_finish;; let empty_peg num_disks = (num_disks, []);; let full_peg num_disks = let rec list_of_disks i = if i <= num_disks then i :: list_of_disks (i+1) else [] in (0, list_of_disks 1);; let game num_disks = let left = ref (full_peg num_disks) and middle = ref (empty_peg num_disks) and right = ref (empty_peg num_disks) in let rec hanoi height start_ intermediate destination = if height > 0 then begin hanoi (height - 1) start_ destination intermediate; move start_ destination; print_game num_disks !left !middle !right; hanoi (height - 1) intermediate start_ destination end in print_line "I name the pegs A, B, and C."; print_line "Starting position:"; print_game num_disks !left !middle !right; hanoi num_disks ("A", left) ("B", middle) ("C", right);; if sys__interactive then () else begin game (int_of_string (sys__command_line.(1))); exit 0 end;; """ //│ > «let spaces n = make_string n " "» //│ > «let disk size = let right_half = make_string size ">" and left_half = make_string size "<" in left_half ^ "|" ^ right_half» //│ > «let disk_number n largest_disk_size = let white_part = spaces (largest_disk_size + 1 - n) in white_part ^ disk n ^ white_part» //│ > «let peg_base largest_disk_size = let half = make_string largest_disk_size "_" in " " ^ half ^ "|" ^ half ^ " "» //│ > «let peg largest_disk_size = function (0, []) -> [] | (0, head :: rest) -> disk_number head largest_disk_size :: peg largest_disk_size (0, rest) | (offset, lst) -> disk_number 0 largest_disk_size :: peg largest_disk_size (offset - 1, lst)» //│ > «let join_lines l1 l2 l3 = match (l1, l2, l3) with ([], [], []) -> [] | (t1 :: r1, t2 :: r2, t3 :: r3) -> t1 ^ t2 ^ t3 :: join_lines r1 r2 r3 | _ -> failwith "join_…"» //│ > «let print_line line = print_string line; print_newline ()» //│ > «let print_game num_disks start middle end_ = let drawing = join_lines (peg num_disks start) (peg num_disks middle) (peg num_disks end_) in do_list print_line drawing; let b = peg_base num_disks in print_line (b ^ b ^ b)» //│ > «let add_disk disk (offset, disks) = (offset - 1, disk :: disks)» //│ > «let top = function (offset, top :: rest) -> top | (offset, []) -> failwith "top: …"» //│ > «let remove_top = function (offset, top :: rest) -> (offset + 1, rest) | (offset, []) -> failwith "remov…"» //│ > «let move (name_start, peg_start) (name_finish, peg_finish) = print_line ("I mov…" ^ name_start ^ " to " ^ name_finish); let moved_disk = top ! peg_start in peg_start := remove_top ! peg_start; peg_finish := add_disk moved_disk ! peg_finish» //│ > «let empty_peg num_disks = (num_disks, [])» //│ > «let full_peg num_disks = let list_of_disks i = if i <= num_disks then i :: list_of_disks (i + 1) then [] in (0, list_of_disks 1)» //│ > «let game num_disks = let left = ref (full_peg num_disks) and middle = ref (empty_peg num_disks) and right = ref (empty_peg num_disks) in let hanoi height start_ intermediate destination = if height > 0 then hanoi (height - 1) start_ destination intermediate; move start_ destination; print_game num_disks ! left ! middle ! right; hanoi (height - 1) intermediate start_ destination then None in print_line "I nam…"; print_line "Start…"; print_game num_disks ! left ! middle ! right; hanoi num_disks ("A", left) ("B", middle) ("C", right)» //│ > «if sys__interactive then () then game (int_of_string sys__command_line . 1); exit 0» ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/DirectiveTest.mls ================================================ :js import "../../../mlscript-compile/MutMap.mls" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/apps/parsing/Test.mls" import "../../../mlscript-compile/apps/parsing/Rules.mls" import "../../../mlscript-compile/apps/parsing/Keywords.mls" open Test { example } example of """ #open "hello";; let f x = x + 1;; let g x = x * 2;; """ //│ > «#open "hello"» //│ > «let f x = x + 1» //│ > «let g x = x * 2» example of """ #newKeyword ("hello", None, None) #newKeyword ("world", Some 34, None) """ //│ > print of Keywords.extended |> MutMap.valuesIterator |. Iter.joined("\n") //│ > Keyword(`hello`, N/A, N/A) //│ > Keyword(`world`, 34, N/A) example of """ #newCategory "lol" """ //│ > example of """ #extendCategory "lol", [ keyword("hello"), "lol" ], foo #extendCategory "lol", [ keyword("world") ], bar #extendCategory "term", [ "lol" ], baz """ //│ > Rules.getRuleByKind("lol").display print() //│ > ::= //│ > | "hello" //│ > | "world" Rules.getRuleByKind("term").display print() //│ > ::= //│ > | "let" ["rec"] ["in" ] //│ > | "fun" "->" //│ > | "match" "with" ["|"] "->" ["|" ] //│ > | "function" ["|"] "->" ["|" ] //│ > | "if" "then" ["else" ] //│ > | "while" "do" "done" //│ > | "for" "=" ("to" | "downto") "do" "done" //│ > | "(" ")" //│ > | "[" "]" //│ > | "{" "}" //│ > | "begin" "end" //│ > | ("," | ";" | "<-" | "==" | "*" | "." ("(" ")" | ) | ":" | ) //│ > | "hello" //│ > | "world" example of """ hello world """ //│ > «{}» //│ > «⚠» //│ > «⚠» example of """ #newKeyword ("assert", None, None) #newCategory "assert" #extendCategory "assert", [ keyword "assert", "term" ], do_assert assert 0 """ //│ > «⚠» //│ > «⚠» example of """ #newKeyword ("hello", Some 3, Some 3) #newKeyword ("goodbye", None, None) #newCategory("greeting") #extendCategory("greeting", [ keyword("hello"), "term", "greeting" ], foo) #extendCategory("greeting", [ keyword("goodbye") ], bar) #extendCategory("decl", [ "greeting" ], baz) hello "Rob" hello "Bob" goodbye #diagram "" """ //│ > «baz (foo "Rob" (foo "Bob" bar))» //│ > «#diagram ""» Rules.getRuleByKind("term").display print() //│ > ::= //│ > | "let" ["rec"] ["in" ] //│ > | "fun" "->" //│ > | "match" "with" ["|"] "->" ["|" ] //│ > | "function" ["|"] "->" ["|" ] //│ > | "if" "then" ["else" ] //│ > | "while" "do" "done" //│ > | "for" "=" ("to" | "downto") "do" "done" //│ > | "(" ")" //│ > | "[" "]" //│ > | "{" "}" //│ > | "begin" "end" //│ > | ("," | ";" | "<-" | "==" | "*" | "." ("(" ")" | ) | ":" | ) //│ > | "hello" //│ > | "world" ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/LeftRecursion.mls ================================================ :js import "../../../mlscript-compile/MutMap.mls" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/apps/parsing/Test.mls" import "../../../mlscript-compile/apps/parsing/Rules.mls" import "../../../mlscript-compile/apps/parsing/Keywords.mls" open Test { example } example of """ #newKeyword ("count", Some 5, Some 5) #newKeyword ("base", None, None) #newKeyword ("incr", None, None) """ //│ > print of Keywords.extended |> MutMap.valuesIterator |. Iter.joined("\n") //│ > Keyword(`count`, 5, 5) //│ > Keyword(`base`, N/A, N/A) //│ > Keyword(`incr`, N/A, N/A) example of """ #newCategory "counting" """ //│ > // ::= "incr" | "base" example of """ #extendCategory "counting", [ "counting", keyword("incr") ], succ #extendCategory "counting", [ keyword("base") ], zero #extendCategory "term", [ keyword("count"), "counting" ], result """ //│ > Rules.getRuleByKind("counting").display print() //│ > ::= //│ > | "incr" //│ > | "base" example of """count base""" //│ > «result zero» example of """count base incr incr""" //│ > «result (succ (succ zero))» Rules.getRuleByKind("term").display print() //│ > ::= //│ > | "let" ["rec"] ["in" ] //│ > | "fun" "->" //│ > | "match" "with" ["|"] "->" ["|" ] //│ > | "function" ["|"] "->" ["|" ] //│ > | "if" "then" ["else" ] //│ > | "while" "do" "done" //│ > | "for" "=" ("to" | "downto") "do" "done" //│ > | "(" ")" //│ > | "[" "]" //│ > | "{" "}" //│ > | "begin" "end" //│ > | ("," | ";" | "<-" | "==" | "*" | "." ("(" ")" | ) | ":" | ) //│ > | "count" ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/LexerTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/Lexer.mls" import "../../../mlscript-compile/apps/parsing/Token.mls" import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" fun lines(...strs) = strs.join("\n") open Lexer { lex } open Stack fun printLexed(text) = print of lex(text, false) Iter.fromStack() Iter.mapping of Token.display Iter.joined of "\n" printLexed of "0 1 0xDEADCAFE 0b1101 0o755 3.1415926535" //│ > integer "0" at 1:1-1:2 //│ > space at 1:2-1:3 //│ > integer "1" at 1:3-1:4 //│ > space at 1:4-1:6 //│ > integer "0xDEADCAFE" at 1:1-1:7 //│ > space at 1:16-1:17 //│ > integer "0b1101" at 1:12-1:18 //│ > space at 1:23-1:24 //│ > integer "0o755" at 1:20-1:25 //│ > space at 1:29-1:34 //│ > decimal "3.1415926535" at 1:23-1:35 // Good strings. printLexed of """ "hello, world" "\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64" "\u0048\u0065\u006C\u006C\u006F\u002C\u0020\u0077\u006F\u0072\u006C\u0064" "\u{48}\u{65}\u{6C}\u{6C}\u{6F}\u{2C}\u{20}\u{77}\u{6F}\u{72}\u{6C}\u{64}" "\u{1F600}" // 😀 "\u{1F601}" // 😁 "\u{1F602}" // 😂 "\u{1F603}" // 😃 "\u{1F604}" // 😄 "\n\r\t\b" """ //│ > space at 1:1-2:1 //│ > string "hello, world" at 2:2-2:15 //│ > space at 2:15-3:1 //│ > string "Hello, world" at 3:2-3:51 //│ > space at 3:51-4:1 //│ > string "Hello, world" at 4:2-4:75 //│ > space at 4:75-5:1 //│ > string "Hello, world" at 5:2-5:75 //│ > space at 5:75-6:1 //│ > string "😀" at 6:2-6:12 //│ > space at 6:12-6:13 //│ > comment at 6:14-6:18 //│ > space at 6:18-7:1 //│ > string "😁" at 7:2-7:12 //│ > space at 7:12-7:13 //│ > comment at 7:14-7:18 //│ > space at 7:18-8:1 //│ > string "😂" at 8:2-8:12 //│ > space at 8:12-8:13 //│ > comment at 8:14-8:18 //│ > space at 8:18-9:1 //│ > string "😃" at 9:2-9:12 //│ > space at 9:12-9:13 //│ > comment at 9:14-9:18 //│ > space at 9:18-10:1 //│ > string "😄" at 10:2-10:12 //│ > space at 10:12-10:13 //│ > comment at 10:14-10:18 //│ > space at 10:18-11:1 //│ > string "\n\r\t\b" at 11:2-11:11 //│ > space at 11:11-12:1 // Ill-formed Strings // ================== printLexed of """ "hello, world. """ //│ > space at 1:1-2:1 //│ > string "hello, world. " at 2:2-2:18 printLexed of """ "hello, \world""" //│ > space at 1:1-2:1 //│ > string "hello, world" at 2:2-2:15 printLexed of """let v' = let a, b = (1, 2) in a + b""" //│ > identifier `let` at 1:1-1:4 //│ > space at 1:4-1:5 //│ > identifier `v'` at 1:5-1:7 //│ > space at 1:7-1:8 //│ > identifier `=` at 1:8-1:9 //│ > space at 1:9-2:3 //│ > identifier `let` at 2:3-2:6 //│ > space at 2:6-2:7 //│ > identifier `a` at 2:7-2:8 //│ > identifier `,` at 2:8-2:9 //│ > space at 2:9-2:10 //│ > identifier `b` at 2:10-2:11 //│ > space at 2:11-2:12 //│ > identifier `=` at 2:12-2:13 //│ > space at 2:13-2:14 //│ > identifier `(` at 2:13-2:14 //│ > integer "1" at 2:15-2:16 //│ > identifier `,` at 2:16-2:17 //│ > space at 2:17-2:18 //│ > integer "2" at 2:18-2:19 //│ > identifier `)` at 2:18-2:19 //│ > space at 2:20-2:21 //│ > identifier `in` at 2:21-2:23 //│ > space at 2:23-2:24 //│ > identifier `a` at 2:24-2:25 //│ > space at 2:25-2:26 //│ > identifier `+` at 2:26-2:27 //│ > space at 2:27-2:28 //│ > identifier `b` at 2:28-2:29 printLexed of lines of "let rec factorial x = // a simple factorial function" " if x <= 1 then 1 else x * factorial (x - 1)" "" "let bool_of_string = function" " | \"true\" -> true" " | \"false\" -> false" " | _ -> raise (Invalid_argument \"bool_of_string\")" //│ > identifier `let` at 1:1-1:4 //│ > space at 1:4-1:5 //│ > identifier `rec` at 1:5-1:8 //│ > space at 1:8-1:9 //│ > identifier `factorial` at 1:9-1:18 //│ > space at 1:18-1:19 //│ > identifier `x` at 1:19-1:20 //│ > space at 1:20-1:21 //│ > identifier `=` at 1:21-1:22 //│ > space at 1:22-1:23 //│ > comment at 1:24-1:53 //│ > space at 1:53-2:3 //│ > identifier `if` at 2:3-2:5 //│ > space at 2:5-2:6 //│ > identifier `x` at 2:6-2:7 //│ > space at 2:7-2:8 //│ > identifier `<=` at 2:8-2:10 //│ > space at 2:10-2:11 //│ > integer "1" at 2:11-2:12 //│ > space at 2:12-2:13 //│ > identifier `then` at 2:13-2:17 //│ > space at 2:17-2:18 //│ > integer "1" at 2:18-2:19 //│ > space at 2:19-2:20 //│ > identifier `else` at 2:20-2:24 //│ > space at 2:24-2:25 //│ > identifier `x` at 2:25-2:26 //│ > space at 2:26-2:27 //│ > identifier `*` at 2:27-2:28 //│ > space at 2:28-2:29 //│ > identifier `factorial` at 2:29-2:38 //│ > space at 2:38-2:39 //│ > identifier `(` at 2:38-2:39 //│ > identifier `x` at 2:40-2:41 //│ > space at 2:41-2:42 //│ > identifier `-` at 2:42-2:43 //│ > space at 2:43-2:44 //│ > integer "1" at 2:44-2:45 //│ > identifier `)` at 2:44-2:45 //│ > space at 2:46-4:1 //│ > identifier `let` at 4:1-4:4 //│ > space at 4:4-4:5 //│ > identifier `bool_of_string` at 4:5-4:19 //│ > space at 4:19-4:20 //│ > identifier `=` at 4:20-4:21 //│ > space at 4:21-4:22 //│ > identifier `function` at 4:22-4:30 //│ > space at 4:30-5:3 //│ > identifier `|` at 5:3-5:4 //│ > space at 5:4-5:5 //│ > string "true" at 5:6-5:11 //│ > space at 5:11-5:12 //│ > identifier `->` at 5:12-5:14 //│ > space at 5:14-5:15 //│ > boolean "true" at 5:15-5:19 //│ > space at 5:19-6:3 //│ > identifier `|` at 6:3-6:4 //│ > space at 6:4-6:5 //│ > string "false" at 6:6-6:12 //│ > space at 6:12-6:13 //│ > identifier `->` at 6:13-6:15 //│ > space at 6:15-6:16 //│ > boolean "false" at 6:16-6:21 //│ > space at 6:21-7:3 //│ > identifier `|` at 7:3-7:4 //│ > space at 7:4-7:5 //│ > identifier `_` at 7:5-7:6 //│ > space at 7:6-7:7 //│ > identifier `->` at 7:7-7:9 //│ > space at 7:9-7:10 //│ > identifier `raise` at 7:10-7:15 //│ > space at 7:15-7:16 //│ > identifier `(` at 7:15-7:16 //│ > identifier `Invalid_argument` at 7:17-7:33 //│ > space at 7:33-7:34 //│ > string "bool_of_string" at 7:35-7:50 //│ > identifier `)` at 7:49-7:50 // It turns out that OCaml identifiers' apostrophes are not limited to the end. printLexed of "a'b" printLexed of "'a" //│ > identifier `a'b` at 1:1-1:4 //│ > identifier `'a` at 1:1-1:1 printLexed of "[< ] < .. ... >" //│ > identifier `[` at 1:1-1:1 //│ > identifier `<` at 1:2-1:3 //│ > space at 1:3-1:4 //│ > identifier `]` at 1:3-1:4 //│ > space at 1:5-1:6 //│ > identifier `<` at 1:6-1:7 //│ > space at 1:7-1:8 //│ > identifier `..` at 1:8-1:10 //│ > space at 1:10-1:11 //│ > identifier `...` at 1:11-1:14 //│ > space at 1:14-1:15 //│ > identifier `>` at 1:15-1:16 printLexed of "`a `b _" //│ > identifier ``a` at 1:1-1:1 //│ > space at 1:3-1:4 //│ > identifier ``b` at 1:2-1:4 //│ > space at 1:6-1:7 //│ > identifier `_` at 1:7-1:8 :e // It would be decent if we allow pattern parameters in functions. fun take(pattern P, idx: Str, acc: Str) = while idx < str.length and str.charAt(idx) is ch and ch is P then set idx = idx + 1 set acc = acc + ch else [acc, idx] //│ ╔══[COMPILATION ERROR] Name not found: str //│ ║ l.238: while idx < str.length and str.charAt(idx) is ch and ch is P //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: str //│ ║ l.238: while idx < str.length and str.charAt(idx) is ch and ch is P //│ ╙── ^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/LoopExpressions.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/Test.mls" open Test { example } example of """ :tree let x = 0 in while x < 10 do print_int x; print_newline (); x <- x + 1 done """ //│ > LetIn: //│ > bindings = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "x" //│ > rhs = Literal#Integer of "0" //│ > body = Some of While: //│ > condition = App: //│ > callee = Ident of "<" //│ > argument = Stack of //│ > Ident of "x" //│ > Literal#Integer of "10" //│ > body = Sequence of Stack of //│ > App: //│ > callee = Ident of "print_int" //│ > argument = Ident of "x" //│ > App: //│ > callee = Ident of "print_newline" //│ > argument = Tuple of Nil //│ > Infix: //│ > op = Keyword(`<-`, 9, 9) //│ > lhs = Ident of "x" //│ > rhs = App: //│ > callee = Ident of "+" //│ > argument = Stack of //│ > Ident of "x" //│ > Literal#Integer of "1" //│ > «let x = 0 in while x < 10 do print_int x; print_newline (); x <- x + 1 done» example of """ :tree for i = 0 to 10 do print i done """ //│ > For: //│ > head = Ident of "i" //│ > start = Literal#Integer of "0" //│ > end = Literal#Integer of "10" //│ > body = App: //│ > callee = Ident of "print" //│ > argument = Ident of "i" //│ > «for i = 0 to 10 do print i done» ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/ParseRuleVisualizerTest.mls ================================================ :js :silent import "../../../mlscript-compile/apps/parsing/Parser.mls" import "../../../mlscript-compile/apps/parsing/ParseRuleVisualizer.mls" import "../../../mlscript-compile/apps/parsing/Rules.mls" import "../../../mlscript-compile/apps/parsing/vendors/railroad/railroad.mjs" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/XML.mls" import "path" open ParseRuleVisualizer { render } open Rules { termRule, declRule, typeRule } open XML { html, tag, elem, style } // Check if the given path exists. fun ensurePath(pathString) = if fs.statSync(pathString).isDirectory() is false do throw Error("The output directory does not exist.") pathString // `process.cwd()` is different in `hkmc2AllTests / test` and // `~hkmc2DiffTests / Test / run`. This is a workaround to make the path. fun findProjectRoot = let base = process.cwd() let fragments = base.split(path.sep) if fragments.at(-1) is "shared" and fragments.at(-2) is "hkmc2" then base else base + path.sep + "hkmc2" + path.sep + "shared" fun outputPath = ensurePath of path.resolve of findProjectRoot, "src", "test", "mlscript", "apps", "parsing" fun filePath(...fragments) = path.join(outputPath, ...fragments) fun libraryPath = ensurePath of path.resolve of findProjectRoot, "src", "test", "mlscript-compile", "apps", "parsing", "vendors", "railroad" fun loadRailroadStylesheet = fs.readFileSync of path.join(libraryPath, "railroad.css"), "utf-8" fun toHTML(...elements) = html(lang: "en") of elem("head") of tag("meta") of charset: "UTF-8" tag("meta") of name: "viewport" content: "width=device-width, initial-scale=1.0" tag("link") of href: "https://cdn.jsdelivr.net/npm/typeface-inconsolata@1.1.13/index.min.css" rel: "stylesheet" elem("title") of "Parse Rules" elem("body", style(padding: "1em")) of elem("div", style of display: "flex" "flex-direction": "column" gap: "1em" ) of ...elements elem("style") of "body{margin:0;font-family:system-ui,sans-serif;font-size:16px}" "figure{margin:0;display:flex;flex-direction:column;gap:0.5em}" "figcaption{font-style:italic;font-weight:600}" "figure:target figcaption{color:#007bff}" "figure:target figcaption::before{font-style:normal;content:'⯈ '}" "figure:target figcaption::after{font-style:normal;content:' ⯇'}" loadRailroadStylesheet fun toElements(diagrams) = diagrams Iter.mapping of case [caption, svg] then elem("figure", id: caption) of elem("figcaption") of caption svg Iter.toArray() fs.writeFileSync of filePath of "rule.html" toHTML of elem("h1") of "Parse Rules" elem("h2") of "Terms" ...toElements of render(railroad, "term", termRule) elem("h2") of "Types" ...toElements of render(railroad, "type", typeRule) elem("h2") of "Definitions" ...toElements of render(railroad, "definition", declRule) "utf-8" ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/ParserErrorTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/Test.mls" open Test { example } example of ":tree ((x) ==> x" //│ > Error of "unexpected end of input" //│ > «⚠» example of ":tree ((x) ==> x))" //│ > App: //│ > callee = Ident of "==>" //│ > argument = Stack of //│ > Ident of "x" //│ > Ident of "x" //│ > Error of "unexpected token Identifier(\")\", true)" //│ > «x ==> x» //│ > «⚠» example of """ type a = b and b = c """ //│ > «type a = b and b = c» example of """ :tree type test = Bar | Test | Foo and test' = Bar' | Test' | Foo' """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "test" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Ident of "Bar" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Ident of "Test" //│ > rhs = Ident of "Foo" //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "test'" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Ident of "Bar'" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Ident of "Test'" //│ > rhs = Ident of "Foo'" //│ > «type test = Bar | Test | Foo and test' = Bar' | Test' | Foo'» example of """ :tree -2 * 3 ~2 * 3 """ //│ > App: //│ > callee = Ident of "*" //│ > argument = Stack of //│ > App: //│ > callee = Ident of "*" //│ > argument = Stack of //│ > App: //│ > callee = Ident of "-" //│ > argument = Literal#Integer of "2" //│ > App: //│ > callee = Ident of "~" //│ > argument = Stack of //│ > Literal#Integer of "3" //│ > Literal#Integer of "2" //│ > Literal#Integer of "3" //│ > «(-2) * 3 ~ 2 * 3» example of """ :tree let foo = -2 * 3 2 * 3 """ //│ > LetIn: //│ > bindings = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "foo" //│ > rhs = App: //│ > callee = Ident of "*" //│ > argument = Stack of //│ > App: //│ > callee = Ident of "*" //│ > argument = Stack of //│ > App: //│ > callee = Ident of "-" //│ > argument = Literal#Integer of "2" //│ > App: //│ > callee = Literal#Integer of "3" //│ > argument = Literal#Integer of "2" //│ > Literal#Integer of "3" //│ > body = None //│ > «let foo = (-2) * 3 2 * 3» // TODO should fail example of """ => xx """ //│ > «=>xx» example of """ :tree id fun x -> x """ //│ > Ident of "id" //│ > Error of "unexpected token Identifier(\"fun\", false)" //│ > Error of "unexpected token Identifier(\"x\", false)" //│ > Error of "unexpected token Identifier(\"->\", true)" //│ > Error of "unexpected token Identifier(\"x\", false)" //│ > «id» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» example of """ :tree id if true then 1 else 0 """ //│ > Ident of "id" //│ > Error of "unexpected token Identifier(\"if\", false)" //│ > Error of "unexpected token Literal(Boolean, \"true\")" //│ > Error of "unexpected token Identifier(\"then\", false)" //│ > Error of "unexpected token Literal(Integer, \"1\")" //│ > Error of "unexpected token Identifier(\"else\", false)" //│ > Error of "unexpected token Literal(Integer, \"0\")" //│ > «id» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» example of """ type;; """ //│ > «⚠» example of """ function;; """ //│ > «⚠» example of """ let foo_bar = function;; """ //│ > «⚠» example of """ let let = 0;; """ //│ > «⚠» ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/ParserTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/Test.mls" open Test { example } example of "(x) => x" //│ > «x => x» example of "let x: int = 0" //│ > «let x : int = 0» example of "let x: int -> int = function | 0 -> 1 | x -> x" //│ > «let x : int -> int = function 0 -> 1 | x -> x» example of """ fun x y -> x + y """ //│ > «fun x y -> x + y» example of """ fun true false -> false | false true -> true """ //│ > «fun true false -> false» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» //│ > «⚠» example of """ id (fun x y -> x + y) """ //│ > «id (fun x y -> x + y)» example of """ :tree let x: int -> int -> int = fun x y -> x + y """ //│ > LetIn: //│ > bindings = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Infix: //│ > op = Keyword(`:`, 4, 3) //│ > lhs = Ident of "x" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Ident of "int" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Ident of "int" //│ > rhs = Ident of "int" //│ > rhs = Lambda: //│ > params = Stack of //│ > App: //│ > callee = Ident of "x" //│ > argument = Ident of "y" //│ > body = App: //│ > callee = Ident of "+" //│ > argument = Stack of //│ > Ident of "x" //│ > Ident of "y" //│ > body = None //│ > «let x : int -> int -> int = fun x y -> x + y» example of "(1 + 2) * 3" //│ > «(1 + 2) * 3» example of "1 + 2 ** 5 * 4" //│ > «1 + 2 ** 5 * 4» example of "let zero = 0 in zero" //│ > «let zero = 0 in zero» example of """ let one = 1 in let zero = 0 in one + zero """ //│ > «let one = 1 in let zero = 0 in one + zero» example of """ f x y """ //│ > «f x y» example of """ let pi = 4.0 *. atan 1.0 """ //│ > «let pi = 4.0 *. atan 1.0» example of "let square x = x *. x" //│ > «let square x = x *. x» example of "square (sin pi) +. square (cos pi)" //│ > «square (sin pi) +. square (cos pi)» example of "(1 < 2) = false" //│ > «1 < 2» //│ > «⚠» //│ > «⚠» example of "let cat s1 s2 = s1 ^ \" \" ^ s2" //│ > «let cat s1 s2 = s1 ^ " " ^ s2» example of "fun x -> x" //│ > «fun x -> x» example of """ let compose f g = fun x -> f (g x) in let cos2 = compose square cos """ //│ > «let compose f g = fun x -> f (g x) in let cos2 = compose square cos» example of "type sign = Positive | Negative" //│ > «type sign = Positive | Negative» example of "true" //│ > «true» example of """ match x with true -> 1 | false -> 0 """ //│ > «match x with true -> 1 | false -> 0» example of """ function (_, 0) -> "both zero" """ //│ > «function (_, 0) -> "both …"» example of """ function (0, 0) -> "both zero" | (0, _) -> "first only zero" | (_, 0) -> "second only zero" | (_, _) -> "neither zero" """ //│ > «function (0, 0) -> "both …" | (0, _) -> "first…" | (_, 0) -> "secon…" | (_, _) -> "neith…"» example of "if f x then y else z" //│ > «if f x then y then z» example of "id (if f x then y else z)" //│ > «id (if f x then y then z)» example of "if f x then if g x then z else w else if h x then y else w" //│ > «if f x then if g x then z then w then if h x then y then w» example of "1, 2, 3" //│ > «(1, 2, 3)» example of "1; 2; 3" //│ > «1; 2; 3» example of """ 1, 2; 3, 4; 5 """ //│ > «(1, 2); (3, 4); 5» // > The expressions `( expr )` and `begin expr end` have the same value as `expr`. // > The two constructs are semantically equivalent, but it is good style to use // > `begin … end` inside control structures. example of """ begin print_string "hello"; print_string "world" end """ //│ > «print_string "hello"; print_string "world"» example of "a == b" //│ > «a == b» // TODO: comment on this syntax design smell in the paper example of """ if a == b then a else b """ //│ > «if a == b then a then b» example of """ let f a b = if a == b then print_endline "Equal" else begin print_string "Not Equal: "; print_int a; print_string " and "; print_int b; print_newline () end """ //│ > «let f a b = if a == b then print_endline "Equal" then print_string "Not E…"; print_int a; print_string " and "; print_int b; print_newline ()» example of """ let foo = y; x """ //│ > «let foo = y; x» example of """ let translate p dx dy = p.x <- p.x +. dx; p.y <- p.y +. dy """ //│ > «let translate p dx dy = p.x <- p.x +. dx; p.y <- p.y +. dy» example of """:tree f x.y """ //│ > App: //│ > callee = Ident of "f" //│ > argument = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "x" //│ > rhs = Ident of "y" //│ > «f x.y» example of """:tree f x.y.z x.y """ //│ > App: //│ > callee = App: //│ > callee = Ident of "f" //│ > argument = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "x" //│ > rhs = Ident of "y" //│ > rhs = Ident of "z" //│ > argument = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "x" //│ > rhs = Ident of "y" //│ > «f x.y.z x.y» example of """:tree f x.y.z x.y """ //│ > App: //│ > callee = App: //│ > callee = Ident of "f" //│ > argument = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "x" //│ > rhs = Ident of "y" //│ > rhs = Ident of "z" //│ > argument = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "x" //│ > rhs = Ident of "y" //│ > «f x.y.z x.y» example of "()" //│ > «()» example of """ match x with | 0 -> 1 """ //│ > «match x with 0 -> 1» example of """ let fullname title first second = match title with | Some t -> t ^ " " ^ first ^ " " ^ second | None -> first ^ " " ^ second """ //│ > «let fullname title first second = match title with Some t -> t ^ " " ^ first ^ " " ^ second | None -> first ^ " " ^ second» example of """ let a = if x then 1 else 0 in let b = 0 """ //│ > «let a = if x then 1 then 0 in let b = 0» example of """ let rec factorial x = if x <= 1 then 1 else x * factorial (x - 1) and sum l = match l with | [] -> 0 | h :: t -> h + sum t in factorial 5 + sum [1; 2; 3] """ //│ > «let factorial x = if x <= 1 then 1 then x * factorial (x - 1) and sum l = match l with [] -> 0 | h :: t -> h + sum t in factorial 5 + sum [1; 2; 3]» example of "begin end" //│ > «» example of """ let v' = let a, b = (1, 2) in a + b in v' """ //│ > «let v' = let (a, b) = (1, 2) in a + b in v'» example of """ let a = 0 and b = 1 in a + b """ //│ > «let a = 0 and b = 1 in a + b» example of """ let rec even = function 0 -> true | n -> odd (n - 1) and odd = function 0 -> false | n -> even (n - 1) in even 1000 """ //│ > «let even = function 0 -> true | n -> odd (n - 1) and odd = function 0 -> false | n -> even (n - 1) in even 1000» example of """ let rec even = function 0 -> true | n -> odd (n - 1) and odd = function 0 -> false | n -> even (n - 1) """ //│ > «let even = function 0 -> true | n -> odd (n - 1) and odd = function 0 -> false | n -> even (n - 1)» example of """ let a = 0 let b = 0 let f x = x + 1 let g x = f (f x) """ //│ > «let a = 0» //│ > «let b = 0» //│ > «let f x = x + 1» //│ > «let g x = f (f x)» example of """ :tree -2 * 3 ;; ~2 * 3 let x = !true """ //│ > App: //│ > callee = Ident of "*" //│ > argument = Stack of //│ > App: //│ > callee = Ident of "-" //│ > argument = Literal#Integer of "2" //│ > Literal#Integer of "3" //│ > App: //│ > callee = Ident of "*" //│ > argument = Stack of //│ > App: //│ > callee = Ident of "~" //│ > argument = Literal#Integer of "2" //│ > Literal#Integer of "3" //│ > LetIn: //│ > bindings = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "x" //│ > rhs = App: //│ > callee = Ident of "!" //│ > argument = Literal#Boolean of "true" //│ > body = None //│ > «(-2) * 3» //│ > «~2 * 3» //│ > «let x = !true» example of """ let f x = x ;; 1 + 2 * 3 """ //│ > «let f x = x» //│ > «1 + 2 * 3» example of """ :tree x.y <- 0 """ //│ > Infix: //│ > op = Keyword(`<-`, 9, 9) //│ > lhs = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "x" //│ > rhs = Ident of "y" //│ > rhs = Literal#Integer of "0" //│ > «x.y <- 0» example of """ :tree f expr .( expr ) <- expr """ //│ > Infix: //│ > op = Keyword(`<-`, 9, 9) //│ > lhs = App: //│ > callee = Ident of "f" //│ > argument = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "expr" //│ > rhs = Ident of "expr" //│ > rhs = Ident of "expr" //│ > «f expr.expr <- expr» example of """ :tree f (expr .( expr ) <- expr) """ //│ > App: //│ > callee = Ident of "f" //│ > argument = Infix: //│ > op = Keyword(`<-`, 9, 9) //│ > lhs = Infix: //│ > op = Keyword(`.`, 26, 26) //│ > lhs = Ident of "expr" //│ > rhs = Ident of "expr" //│ > rhs = Ident of "expr" //│ > «f (expr.expr <- expr)» example of """ a:; """ //│ > «a :;» example of """ let sub = (-);; """ //│ > «let sub = -» example of """ :tree f -1 """ //│ > App: //│ > callee = Ident of "-" //│ > argument = Stack of //│ > Ident of "f" //│ > Literal#Integer of "1" //│ > «f - 1» example of """ :tree f :: -1 """ //│ > App: //│ > callee = Ident of "::" //│ > argument = Stack of //│ > Ident of "f" //│ > App: //│ > callee = Ident of "-" //│ > argument = Literal#Integer of "1" //│ > «f :: -1» let x = 1 //│ x = 1 mkStr of "A", x is Str //│ // Standard Error: //│ Assertion failed //│ //│ = "Afalse" ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/PrattParsingTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/PrattParsing.mls" import "../../../mlscript-compile/apps/parsing/Expr.mls" import "../../../mlscript-compile/apps/parsing/TokenHelpers.mls" import "../../../mlscript-compile/apps/parsing/Lexer.mls" open Lexer { lex } open PrattParsing { parse } fun example(input) = let tokens = lex of input, false print of "Tokens: " + TokenHelpers.panorama(tokens) let expr = parse of tokens print of "Parsed: " + expr.toString() print of "Pretty-printed: " + expr Expr.prettyPrint() example of "x + y - 3 * (a / b) ** 2" //│ > Tokens: ┃x│␠│+│␠│y│␠│-│␠│3│␠│*│␠│(│a│␠│/│␠│b│)│␠│**│␠│2┃ //│ > Parsed: Inf("-", Inf("+", Var("x"), Var("y")), Inf("*", Lit(3), Inf("**", Inf("/", Var("a"), Var("b")), Lit(2)))) //│ > Pretty-printed: x + y - 3 * (a / b) ** 2 example of "((m - 2) / n) ** 3 + 5 * x" //│ > Tokens: ┃(│(│m│␠│-│␠│2│)│␠│/│␠│n│)│␠│**│␠│3│␠│+│␠│5│␠│*│␠│x┃ //│ > Parsed: Inf("+", Inf("**", Inf("/", Inf("-", Var("m"), Lit(2)), Var("n")), Lit(3)), Inf("*", Lit(5), Var("x"))) //│ > Pretty-printed: ((m - 2) / n) ** 3 + 5 * x example of "(a ** 2) - (b / 3) * (c + d)" //│ > Tokens: ┃(│a│␠│**│␠│2│)│␠│-│␠│(│b│␠│/│␠│3│)│␠│*│␠│(│c│␠│+│␠│d│)┃ //│ > Parsed: Inf("-", Inf("**", Var("a"), Lit(2)), Inf("*", Inf("/", Var("b"), Lit(3)), Inf("+", Var("c"), Var("d")))) //│ > Pretty-printed: a ** 2 - b / 3 * (c + d) example of "x * y / (a - b) + c ** 4" //│ > Tokens: ┃x│␠│*│␠│y│␠│/│␠│(│a│␠│-│␠│b│)│␠│+│␠│c│␠│**│␠│4┃ //│ > Parsed: Inf("+", Inf("/", Inf("*", Var("x"), Var("y")), Inf("-", Var("a"), Var("b"))), Inf("**", Var("c"), Lit(4))) //│ > Pretty-printed: x * y / (a - b) + c ** 4 example of "2 + 3 - 4 * 5 / (x ** 2)" //│ > Tokens: ┃2│␠│+│␠│3│␠│-│␠│4│␠│*│␠│5│␠│/│␠│(│x│␠│**│␠│2│)┃ //│ > Parsed: Inf("-", Inf("+", Lit(2), Lit(3)), Inf("/", Inf("*", Lit(4), Lit(5)), Inf("**", Var("x"), Lit(2)))) //│ > Pretty-printed: 2 + 3 - 4 * 5 / x ** 2 example of "(x - y) ** (z + 3 / w) * 2" //│ > Tokens: ┃(│x│␠│-│␠│y│)│␠│**│␠│(│z│␠│+│␠│3│␠│/│␠│w│)│␠│*│␠│2┃ //│ > Parsed: Inf("*", Inf("**", Inf("-", Var("x"), Var("y")), Inf("+", Var("z"), Inf("/", Lit(3), Var("w")))), Lit(2)) //│ > Pretty-printed: (x - y) ** (z + 3 / w) * 2 example of "((a + b) ** 2) / (c - d) * e - f" //│ > Tokens: ┃(│(│a│␠│+│␠│b│)│␠│**│␠│2│)│␠│/│␠│(│c│␠│-│␠│d│)│␠│*│␠│e│␠│-│␠│f┃ //│ > Parsed: Inf("-", Inf("*", Inf("/", Inf("**", Inf("+", Var("a"), Var("b")), Lit(2)), Inf("-", Var("c"), Var("d"))), Var("e")), Var("f")) //│ > Pretty-printed: (a + b) ** 2 / (c - d) * e - f example of "8 / (2 + (3 * x) - y) ** 2" //│ > Tokens: ┃8│␠│/│␠│(│2│␠│+│␠│(│3│␠│*│␠│x│)│␠│-│␠│y│)│␠│**│␠│2┃ //│ > Parsed: Inf("/", Lit(8), Inf("**", Inf("-", Inf("+", Lit(2), Inf("*", Lit(3), Var("x"))), Var("y")), Lit(2))) //│ > Pretty-printed: 8 / (2 + 3 * x - y) ** 2 example of "((p - q) * (r + s)) / (t ** (u - 1))" //│ > Tokens: ┃(│(│p│␠│-│␠│q│)│␠│*│␠│(│r│␠│+│␠│s│)│)│␠│/│␠│(│t│␠│**│␠│(│u│␠│-│␠│1│)│)┃ //│ > Parsed: Inf("/", Inf("*", Inf("-", Var("p"), Var("q")), Inf("+", Var("r"), Var("s"))), Inf("**", Var("t"), Inf("-", Var("u"), Lit(1)))) //│ > Pretty-printed: (p - q) * (r + s) / t ** (u - 1) example of "1 + 2 * 3 - 4 / (5 ** (6 - x))" //│ > Tokens: ┃1│␠│+│␠│2│␠│*│␠│3│␠│-│␠│4│␠│/│␠│(│5│␠│**│␠│(│6│␠│-│␠│x│)│)┃ //│ > Parsed: Inf("-", Inf("+", Lit(1), Inf("*", Lit(2), Lit(3))), Inf("/", Lit(4), Inf("**", Lit(5), Inf("-", Lit(6), Var("x"))))) //│ > Pretty-printed: 1 + 2 * 3 - 4 / 5 ** (6 - x) example of "5 ** 6 ** 7 ** 8" //│ > Tokens: ┃5│␠│**│␠│6│␠│**│␠│7│␠│**│␠│8┃ //│ > Parsed: Inf("**", Lit(5), Inf("**", Lit(6), Inf("**", Lit(7), Lit(8)))) //│ > Pretty-printed: 5 ** 6 ** 7 ** 8 example of "(a ** 2) ** 5 ** e" //│ > Tokens: ┃(│a│␠│**│␠│2│)│␠│**│␠│5│␠│**│␠│e┃ //│ > Parsed: Inf("**", Inf("**", Var("a"), Lit(2)), Inf("**", Lit(5), Var("e"))) //│ > Pretty-printed: (a ** 2) ** 5 ** e ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/RecursiveDescentTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/RecursiveDescent.mls" import "../../../mlscript-compile/apps/parsing/BasicExpr.mls" import "../../../mlscript-compile/apps/parsing/TokenHelpers.mls" import "../../../mlscript-compile/apps/parsing/Lexer.mls" open Lexer { lex } open RecursiveDescent { parse } open BasicExpr { Expr } fun example(input) = let tokens = lex of input, false print of "Tokens: " + TokenHelpers.panorama(tokens) let expr = parse of tokens print of "Parsed: " + expr.toString() print of "Pretty-printed: " + expr BasicExpr.prettyPrint() example of "1 + 2 * 3" //│ > Tokens: ┃1│␠│+│␠│2│␠│*│␠│3┃ //│ > Parsed: Add(Lit(1), Mul(Lit(2), Lit(3))) //│ > Pretty-printed: 1 + 2 * 3 example of "3 * x + 1" //│ > Tokens: ┃3│␠│*│␠│x│␠│+│␠│1┃ //│ > Parsed: Add(Mul(Lit(3), Var("x")), Lit(1)) //│ > Pretty-printed: 3 * x + 1 example of "1 + (1 + 1)" //│ > Tokens: ┃1│␠│+│␠│(│1│␠│+│␠│1│)┃ //│ > Parsed: Add(Lit(1), Add(Lit(1), Lit(1))) //│ > Pretty-printed: 1 + 1 + 1 example of "1 + (1 + 1" //│ > Tokens: ┃1│␠│+│␠│(│1│␠│+│␠│1┃ //│ > Parsed: Add(Lit(1), Err(Some(Add(Lit(1), Lit(1))), "Expected token ), but found end of input")) //│ > Pretty-printed: 1 + { 1 + 1 | "Expected token ), but found end of input" } example of "1 + 1 +" //│ > Tokens: ┃1│␠│+│␠│1│␠│+┃ //│ > Parsed: Add(Add(Lit(1), Lit(1)), Err(None, "Unexpected end of input")) //│ > Pretty-printed: 1 + 1 + { "Unexpected end of input" } example of "x + 5" //│ > Tokens: ┃x│␠│+│␠│5┃ //│ > Parsed: Add(Var("x"), Lit(5)) //│ > Pretty-printed: x + 5 example of "3 * x + 2" //│ > Tokens: ┃3│␠│*│␠│x│␠│+│␠│2┃ //│ > Parsed: Add(Mul(Lit(3), Var("x")), Lit(2)) //│ > Pretty-printed: 3 * x + 2 example of "(x + 1) * (y + 4)" //│ > Tokens: ┃(│x│␠│+│␠│1│)│␠│*│␠│(│y│␠│+│␠│4│)┃ //│ > Parsed: Mul(Add(Var("x"), Lit(1)), Add(Var("y"), Lit(4))) //│ > Pretty-printed: (x + 1) * (y + 4) example of "2 + (3 * y)" //│ > Tokens: ┃2│␠│+│␠│(│3│␠│*│␠│y│)┃ //│ > Parsed: Add(Lit(2), Mul(Lit(3), Var("y"))) //│ > Pretty-printed: 2 + 3 * y example of "(x + 2) + (3 * y)" //│ > Tokens: ┃(│x│␠│+│␠│2│)│␠│+│␠│(│3│␠│*│␠│y│)┃ //│ > Parsed: Add(Add(Var("x"), Lit(2)), Mul(Lit(3), Var("y"))) //│ > Pretty-printed: x + 2 + 3 * y example of "3 * (x + y) + 7" //│ > Tokens: ┃3│␠│*│␠│(│x│␠│+│␠│y│)│␠│+│␠│7┃ //│ > Parsed: Add(Mul(Lit(3), Add(Var("x"), Var("y"))), Lit(7)) //│ > Pretty-printed: 3 * (x + y) + 7 // Unbalanced parentheses example of "((x + y)" //│ > Tokens: ┃(│(│x│␠│+│␠│y│)┃ //│ > Parsed: Err(Some(Add(Var("x"), Var("y"))), "Expected token ), but found end of input") //│ > Pretty-printed: { x + y | "Expected token ), but found end of input" } // Missing operand between '+' and '*' example of "x + * y" //│ > Tokens: ┃x│␠│+│␠│*│␠│y┃ //│ > Parsed: Add(Var("x"), Mul(Err(None, "Unexpected token *"), Var("y"))) //│ > Pretty-printed: x + { "Unexpected token *" } * y // Another unbalanced parenthesis example of "3 + (y" //│ > Tokens: ┃3│␠│+│␠│(│y┃ //│ > Parsed: Add(Lit(3), Err(Some(Var("y")), "Expected token ), but found end of input")) //│ > Pretty-printed: 3 + { y | "Expected token ), but found end of input" } // Unbalanced parenthesis example of "3 + 7)" //│ > Tokens: ┃3│␠│+│␠│7│)┃ //│ > Parsed: Err(Some(Add(Lit(3), Lit(7))), "Expect end of input, but found )") //│ > Pretty-printed: { 3 + 7 | "Expect end of input, but found )" } // Operator '+' missing a second operand example of "(x +) + y" //│ > Tokens: ┃(│x│␠│+│)│␠│+│␠│y┃ //│ > Parsed: Add(Add(Var("x"), Err(None, "Unexpected token )")), Var("y")) //│ > Pretty-printed: x + { "Unexpected token )" } + y // Expression starting with an operator '*' example of "* x + 5" //│ > Tokens: ┃*│␠│x│␠│+│␠│5┃ //│ > Parsed: Add(Mul(Err(None, "Unexpected token *"), Var("x")), Lit(5)) //│ > Pretty-printed: { "Unexpected token *" } * x + 5 // Double operator '+' without an operand in between example of "x + + 5" //│ > Tokens: ┃x│␠│+│␠│+│␠│5┃ //│ > Parsed: Add(Add(Var("x"), Err(None, "Unexpected token +")), Lit(5)) //│ > Pretty-printed: x + { "Unexpected token +" } + 5 // Missing operator between 'x' and 'y' example of "x y" //│ > Tokens: ┃x│␠│y┃ //│ > Parsed: Err(Some(Var("x")), "Expect end of input, but found y") //│ > Pretty-printed: { x | "Expect end of input, but found y" } // Empty parentheses with no expression example of "()" //│ > Tokens: ┃(│)┃ //│ > Parsed: Err(None, "Unexpected token )") //│ > Pretty-printed: { "Unexpected token )" } // Operator '*' at the end with no following operand example of "3 + x *" //│ > Tokens: ┃3│␠│+│␠│x│␠│*┃ //│ > Parsed: Add(Lit(3), Mul(Var("x"), Err(None, "Unexpected end of input"))) //│ > Pretty-printed: 3 + x * { "Unexpected end of input" } ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/RulesTest.mls ================================================ :js import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/MutMap.mls" import "../../../mlscript-compile/apps/parsing/Keywords.mls" import "../../../mlscript-compile/apps/parsing/Rules.mls" import "../../../mlscript-compile/apps/parsing/Test.mls" open Keywords { opPrec, opPrecOpt } Keywords.maxKeywordPrec //│ = 11 Keywords.maxOperatorPrec //│ = 26 opPrec("|>") //│ = [14, 20] opPrec("<:") //│ = [20, 12] opPrec("::") //│ = [13, 12] opPrec of "+" //│ = [21, 21] opPrecOpt of "==" //│ = Some([16, 16]) print of Keywords.all |> MutMap.valuesIterator |. Iter.joined("\n") //│ > Keyword(`;;`, 0, 0) //│ > Keyword(`class`, N/A, 0) //│ > Keyword(`;`, 1, 0) //│ > Keyword(`,`, 2, 1) //│ > Keyword(`=`, 3, 3) //│ > Keyword(`and`, N/A, 4) //│ > Keyword(`|`, N/A, N/A) //│ > Keyword(`->`, 5, 3) //│ > Keyword(`:`, 4, 3) //│ > Keyword(`match`, 6, 6) //│ > Keyword(`while`, 7, 7) //│ > Keyword(`for`, 8, 8) //│ > Keyword(`to`, N/A, N/A) //│ > Keyword(`downto`, N/A, N/A) //│ > Keyword(`do`, N/A, N/A) //│ > Keyword(`done`, N/A, N/A) //│ > Keyword(`of`, N/A, N/A) //│ > Keyword(`with`, N/A, 8) //│ > Keyword(`case`, N/A, 8) //│ > Keyword(`if`, 10, 9) //│ > Keyword(`<-`, 9, 9) //│ > Keyword(`then`, 9, 9) //│ > Keyword(`else`, 9, 9) //│ > Keyword(`let`, 3, 1) //│ > Keyword(`in`, 9, 9) //│ > Keyword(`true`, N/A, N/A) //│ > Keyword(`false`, N/A, N/A) //│ > Keyword(`as`, 11, 11) //│ > Keyword(`fun`, 11, 5) //│ > Keyword(`function`, 11, 3) //│ > Keyword(`type`, 11, N/A) //│ > Keyword(`exception`, 11, N/A) //│ > Keyword(`rec`, 11, 3) //│ > Keyword(`#`, N/A, N/A) //│ > Keyword(`.`, 26, 26) //│ > Keyword(`*`, 22, 22) //│ > Keyword(`==`, 16, 16) //│ > Keyword(`(`, 27, 0) //│ > Keyword(`)`, 0, N/A) //│ > Keyword(`[`, 27, 0) //│ > Keyword(`]`, 0, N/A) //│ > Keyword(`{`, 27, 0) //│ > Keyword(`}`, 0, N/A) //│ > Keyword(`begin`, 27, 0) //│ > Keyword(`end`, 0, N/A) print of Rules.termRule.display //│ > ::= //│ > | "let" ["rec"] ["in" ] //│ > | "fun" "->" //│ > | "match" "with" ["|"] "->" ["|" ] //│ > | "function" ["|"] "->" ["|" ] //│ > | "if" "then" ["else" ] //│ > | "while" "do" "done" //│ > | "for" "=" ("to" | "downto") "do" "done" //│ > | "(" ")" //│ > | "[" "]" //│ > | "{" "}" //│ > | "begin" "end" //│ > | ("," | ";" | "<-" | "==" | "*" | "." ("(" ")" | ) | ":" | ) print of Rules.typeRule.display //│ > ::= //│ > | "(" ( ")" | ")" []) //│ > | ("->" | "*" | ) print of Rules.declRule.display //│ > ::= //│ > | "let" ["rec"] //│ > | "type" //│ > | "exception" //│ > | "#" Rules.syntaxKinds Iter.mapping of case [name, rule] then rule.display Iter.joined("\n") print() //│ > ::= "=" ["and" ] //│ > ::= "->" ["|" ] //│ > ::= //│ > ::= "->" ["|" ] //│ > ::= "," [] //│ > ::= ["of" ] //│ > ::= ["|" ] //│ > ::= ["and" ] //│ > ::= //│ > | "=" ( | "{" "}") //│ > | "==" //│ > ::= ":" //│ > ::= [";" ] //│ > ::= ["|" ] //│ > ::= //│ > ::= //│ > | //│ > | "(" ")" //│ > ::= "," //│ > ::= //│ > | "let" ["rec"] ["in" ] //│ > | "fun" "->" //│ > | "match" "with" ["|"] "->" ["|" ] //│ > | "function" ["|"] "->" ["|" ] //│ > | "if" "then" ["else" ] //│ > | "while" "do" "done" //│ > | "for" "=" ("to" | "downto") "do" "done" //│ > | "(" ")" //│ > | "[" "]" //│ > | "{" "}" //│ > | "begin" "end" //│ > | ("," | ";" | "<-" | "==" | "*" | "." ("(" ")" | ) | ":" | ) //│ > ::= //│ > | "(" ( ")" | ")" []) //│ > | ("->" | "*" | ) //│ > ::= //│ > | "let" ["rec"] //│ > | "type" //│ > | "exception" //│ > | "#" ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing/TypeTest.mls ================================================ :js import "../../../mlscript-compile/apps/parsing/Test.mls" open Test { example } example of """ type 'a foo == 'a list """ //│ > «type foo ('a) == list 'a» // (A, List[A]) example of """ type 'a foo == 'a * 'a list """ //│ > «type foo ('a) == 'a * list 'a» // (A, List[A], List[A]) example of """ :tree type 'a foo == 'a * 'a list * 'a list """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`==`, 16, 16) //│ > lhs = App: //│ > callee = Ident of "foo" //│ > argument = Tuple of Stack of //│ > Ident of "'a" //│ > rhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = Ident of "'a" //│ > rhs = App: //│ > callee = Ident of "list" //│ > argument = Ident of "'a" //│ > rhs = App: //│ > callee = Ident of "list" //│ > argument = Ident of "'a" //│ > «type foo ('a) == 'a * list 'a * list 'a» example of """ type ('a, 'b) foo == 'a * 'b list -> 'b """ //│ > «type foo ('a, 'b) == 'a * list 'b -> 'b» example of """ type b == p * q * (r * s) """ //│ > «type b == p * q * r * s» example of """ type b == (p * q) type f == int """ //│ > «type b == p * q» //│ > «type f == int» example of """ type a == b type b == p * q * (r * s) type f == int -> int """ //│ > «type a == b» //│ > «type b == p * q * r * s» //│ > «type f == int -> int» example of """ :tree type f == a -> b -> c type g == (a -> b) -> c """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`==`, 16, 16) //│ > lhs = Ident of "f" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Ident of "a" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Ident of "b" //│ > rhs = Ident of "c" //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`==`, 16, 16) //│ > lhs = Ident of "g" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Ident of "a" //│ > rhs = Ident of "b" //│ > rhs = Ident of "c" //│ > «type f == a -> b -> c» //│ > «type g == a -> b -> c» example of """ :tree type f == int * string -> float type g == int * (string -> float) """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`==`, 16, 16) //│ > lhs = Ident of "f" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = Ident of "int" //│ > rhs = Ident of "string" //│ > rhs = Ident of "float" //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`==`, 16, 16) //│ > lhs = Ident of "g" //│ > rhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = Ident of "int" //│ > rhs = Infix: //│ > op = Keyword(`->`, 5, 3) //│ > lhs = Ident of "string" //│ > rhs = Ident of "float" //│ > «type f == int * string -> float» //│ > «type g == int * string -> float» example of """ type 'a f == 'a * 'a """ //│ > «type f ('a) == 'a * 'a» example of """ type foo = Bar | Qux let double n = n * 2 """ //│ > «type foo = Bar | Qux» //│ > «let double n = n * 2» example of """ type foo = Bar of int | Qux of int * int """ //│ > «type foo = Bar of int | Qux of int * int» example of """ :tree type 'a option = Some of 'a | None """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = App: //│ > callee = Ident of "option" //│ > argument = Tuple of Stack of //│ > Ident of "'a" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Infix: //│ > op = Keyword(`of`, N/A, N/A) //│ > lhs = Ident of "Some" //│ > rhs = Ident of "'a" //│ > rhs = Ident of "None" //│ > «type option ('a) = Some of 'a | None» example of """ :tree type 'a tree = Leaf | Node of 'a tree * 'a * 'a tree """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = App: //│ > callee = Ident of "tree" //│ > argument = Tuple of Stack of //│ > Ident of "'a" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Ident of "Leaf" //│ > rhs = Infix: //│ > op = Keyword(`of`, N/A, N/A) //│ > lhs = Ident of "Node" //│ > rhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = App: //│ > callee = Ident of "tree" //│ > argument = Ident of "'a" //│ > rhs = Ident of "'a" //│ > rhs = App: //│ > callee = Ident of "tree" //│ > argument = Ident of "'a" //│ > «type tree ('a) = Leaf | Node of tree 'a * 'a * tree 'a» example of """ type ('a, 'b) twist == a list * b list """ //│ > «type twist ('a, 'b) == list a * list b» // example of """ // type qux = Qux of { q : int; u: int; x :int } // """ // example of """ // type colour = // | Red | Green | Blue | Yellow | Black | White // | RGB of {r : int; g : int; b : int} // type 'a tree = Lf | Br of 'a * 'a tree * 'a;; // """ example of """ :tree type t = {decoration : string; substance : t'} and t' = Int of int | List of t list """ //│ > Define: //│ > kind = Type //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "t" //│ > rhs = Bracketed#Curly of Sequence of Stack of //│ > Infix: //│ > op = Keyword(`:`, 4, 3) //│ > lhs = Ident of "decoration" //│ > rhs = Ident of "string" //│ > Infix: //│ > op = Keyword(`:`, 4, 3) //│ > lhs = Ident of "substance" //│ > rhs = Ident of "t'" //│ > Infix: //│ > op = Keyword(`=`, 3, 3) //│ > lhs = Ident of "t'" //│ > rhs = Infix: //│ > op = Keyword(`|`, N/A, N/A) //│ > lhs = Infix: //│ > op = Keyword(`of`, N/A, N/A) //│ > lhs = Ident of "Int" //│ > rhs = Ident of "int" //│ > rhs = Infix: //│ > op = Keyword(`of`, N/A, N/A) //│ > lhs = Ident of "List" //│ > rhs = App: //│ > callee = Ident of "list" //│ > argument = Ident of "t" //│ > «type t = {decoration : string; substance : t'} and t' = Int of int | List of list t» example of """ type int_to_int == int -> int """ //│ > «type int_to_int == int -> int» example of """ :tree exception E of int * string """ //│ > Define: //│ > kind = Exception //│ > items = Stack of //│ > Infix: //│ > op = Keyword(`of`, N/A, N/A) //│ > lhs = Ident of "E" //│ > rhs = Infix: //│ > op = Keyword(`*`, 22, 22) //│ > lhs = Ident of "int" //│ > rhs = Ident of "string" //│ > «exception E of int * string» example of """ type 'a bst = E | N of 'a * 'a bst * 'a bst let rec insert x = function | E -> N (x, E, E) | N (y, l, r) -> if x < y then N (y, insert x l, r) else N (y, l, insert x r) let rec min_elt = function | E -> failwith "min_elt called on empty tree" | N (x, E, _) -> x | N (_, l, _) -> min_elt l let rec remove x = function | E -> E | N (y, l, r) -> if x < y then N (y, remove x l, r) else if x > y then N (y, l, remove x r) else match (l, r) with | (E, _) -> r | (_, E) -> l | _ -> let m = min_elt r in N (m, l, remove m r) """ //│ > «type bst ('a) = E | N of 'a * bst 'a * bst 'a» //│ > «let insert x = function E -> N (x, E, E) | N (y, l, r) -> if x < y then N (y, insert x l, r) then N (y, l, insert x r)» //│ > «let min_elt = function E -> failwith "min_e…" | N (x, E, _) -> x | N (_, l, _) -> min_elt l» //│ > «let remove x = function E -> E | N (y, l, r) -> if x < y then N (y, remove x l, r) then if x > y then N (y, l, remove x r) then match (l, r) with (E, _) -> r | (_, E) -> l | _ -> let m = min_elt r in N (m, l, remove m r)» ================================================ FILE: hkmc2/shared/src/test/mlscript/apps/parsing-web-demo/ExamplesTest.mls ================================================ :js import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/MutMap.mls" import "../../../mlscript-compile/apps/parsing/Test.mls" import "../../../mlscript-compile/apps/parsing-web-demo/Examples.mls" :silent val examples = (Examples.examples |> MutMap.values).sort of (a, b) => a.name.localeCompare(b.name) examples Iter.each of case entry then print of "Testing example: " + entry.name Test.example of entry.source //│ > Testing example: Extensible Syntax //│ > «baz (foo "Rob" (foo "Bob" bar))» //│ > «#diagram ""» //│ > Testing example: Hanoi from Caml Light //│ > «let spaces n = make_string n " "» //│ > «let disk size = let right_half = make_string size ">" and left_half = make_string size "<" in left_half ^ "|" ^ right_half» //│ > «let disk_number n largest_disk_size = let white_part = spaces (largest_disk_size + 1 - n) in white_part ^ disk n ^ white_part» //│ > «let peg_base largest_disk_size = let half = make_string largest_disk_size "_" in " " ^ half ^ "|" ^ half ^ " "» //│ > «let peg largest_disk_size = function (0, []) -> [] | (0, head :: rest) -> disk_number head largest_disk_size :: peg largest_disk_size (0, rest) | (offset, lst) -> disk_number 0 largest_disk_size :: peg largest_disk_size (offset - 1, lst)» //│ > «let join_lines l1 l2 l3 = match (l1, l2, l3) with ([], [], []) -> [] | (t1 :: r1, t2 :: r2, t3 :: r3) -> t1 ^ t2 ^ t3 :: join_lines r1 r2 r3 | _ -> failwith "join_…"» ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls ================================================ :js :todo [a, b] => [b, a] //│ ╔══[COMPILATION ERROR] Illegal constraint syntax. //│ ║ l.5: [a, b] => [b, a] //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Illegal constraint syntax. //│ ║ l.5: [a, b] => [b, a] //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: b //│ ║ l.5: [a, b] => [b, a] //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.5: [a, b] => [b, a] //│ ╙── ^ //│ /!!!\ Uncaught error: scala.MatchError: Constrained(List(),Tup(List(Fld(‹›,Error,None), Fld(‹›,Error,None)))) (of class hkmc2.semantics.Term$Constrained) fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple //│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple //│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: c //│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: b //│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: d //│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/ExplicitLabels.mls ================================================ :js :ignore // TODO: would be nice to be able to define our own labels with the following syntax let x = 0 //│ x = 0 do label: if x == 0 do label.break 42 print("Not zero!") x //│ ╔══[PARSE ERROR] Unexpected literal here //│ ║ l.12: if x == 0 do label.break 42 //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Name not found: label //│ ║ l.12: if x == 0 do label.break 42 //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let i = 10 in do label1: if i <= 0 do label1.break () print("Still > 0:", i) set i -= 1 label2: // this is implicitly nested in label1 label1.continue //│ ╔══[COMPILATION ERROR] Name not found: label1 //│ ║ l.26: if i <= 0 do label1.break () //│ ╙── ^^^^^^ //│ > Still > 0: 10 //│ ═══[RUNTIME ERROR] Error: Access to required field 'continue' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/ForLLM.mls ================================================ :js // Instruction for agent: for each test case you solve, move it to its own test file outside of the `backlog` folder // ——— ——— ——— // Bug: `pattern do body` inside `is` expression is not handled. // The root cause is at the parser level: in `Some of [process, rest] do "hi"`, // the `of` keyword greedily includes `do "hi"` inside the constructor argument, // producing `App(Some, Tup([InfixApp(Tup([process, rest]), do, "hi")]))`. // In `if`/`case`/`while` splits, the parser treats `do` as a split boundary // at the branch level, so it correctly separates pattern from consequent. But // in standalone `is` expressions, the block lines are parsed as regular // expressions, so `do` is parsed as an infix operator inside the constructor's // argument. Fixing this would require the parser to switch to a split-like // parsing mode for `is` RHS blocks, which is a significant refactoring. class Some(val v) :fixme Some(1) is Some of [process, rest] do "hi" //│ ╔══[COMPILATION ERROR] Unrecognized pattern (infix operator 'do'). //│ ║ l.22: Some of [process, rest] do "hi" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ = true // ——— ——— ——— ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/Lifter.mls ================================================ :js :lift :ignore // The following are problems with lifting classes inside other definitions. // We use the optional `symbol` parameter of `Select` to detect references to the // BlockMemberSymbol. But when this symbol is not present, there is no way to properly // detect it. // // The following could instead be lifted as: // // class B$(y)(a) with // fun getB() = a.x + y // class A(x) with // fun B(y) = B$(y)(this) // set B.class = B // so that `new a.B(n)` still works // fun getA() = this.B(2).getB() // A(1).getA() // // where B must be marked as final. :expect 3 data class A(x) with data class B(y) with fun getB() = x + y fun getA() = // NOTE: The Term.Resolved PR made it so `this.B` gets resolved correctly. // Explicitly binding `t = this` reproduces the previous error let t = this t.B(2).getB() A(1).getA() //│ ═══[RUNTIME ERROR] TypeError: t.B is not a function //│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' // This is due to the order of classes after lifting class Test fun hello() = class Test2 extends Test new Test2 //│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null // This is due to subclasses not calling `super` with the required locals. The analysis to // determine which locals are required is not in place yet. :expect 2 fun test(x) = class A with fun get = x class B() extends A B().get test(2) //│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' //│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' // This is due to `super` not being called with multiple parameter lists. :expect "B()" fun test(x) = class A() with fun get = x class B() extends A() with fun bar = x B() test(0).toString() //│ ═══[RUNTIME ERROR] Expected: '"B()"', got: '"(x1) => { this.#x = x1; return this; }"' //│ = "(x1) => { this.#x = x1; return this; }" // In fact, the correct way (using the current "constructor returns a lambda" hack) is this: // return (params) => { // let ret = super(parentMainParams)(parentAuxParams); // // ctor // return ret; // } /// The following are related to first-class classes, instance checks, and private fields. /// :expect 1 fun f(used1, unused1) = fun g(g_arg) = let used3 = 2 fun h = used3 used1 + h let unused2 = 2 class Test(a) with fun get() = used1 let foo = Test foo(unused1) f(1, 2).get() //│ = 1 :todo :w fun foo1(x, n) = class C() let res = if x is C then "Y" else "N" if n > 0 then res + foo1(C(), n - 1) else "" //│ ═══[WARNING] Cannot yet lift class/module `C` as it is used in an instance check. fun foo2() = class C() C :w fun foo3() = class C fun f = new C C //│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :expect "NN" foo1(0, 2) //│ = "NN" // Since B extends A and A is not lifted, B cannot access A's BlockMemberSymbol. We can't lift B. :todo fun test(x) = class A with fun get = x class B() extends A 0 is A B().get test(2) //│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. //│ ═══[WARNING] Cannot yet lift definition `B` as it extends an unliftable class. //│ = 2 /// The following are related to modules and objects. /// :todo fun foo(x, y) = module M with val test = 2 fun foo() = set y = 2 x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.130: module M with //│ ╙── ^ :expect 14 foo(10, 0) //│ = 14 fun foo(x, y) = module M with fun foo() = set y = 2 x + y fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.145: module M with //│ ╙── ^ :expect 12 foo(10, 0) //│ = 12 data class A(x) with module M with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.161: module M with //│ ╙── ^ :expect 2 A(2).getA() //│ = 2 // TODO: Foo needs to be put in a mutable capture. Also, we need to pass the Foo instance itself into Foo fun foo(x) = object Foo with fun self1 = this fun self2 = Foo class Bar extends Foo (new Bar).self2 foo(2) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type Foo. //│ ║ l.177: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.174: object Foo with //│ ╙── ^^^ //│ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. //│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null // `h` is lifted out, but then cannot access the BlockMemberSymbol M. :fixme :noInline :w fun f = module M with fun g = fun h = M.g h M.g //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.195: module M with //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' //│ ║ l.197: fun h = M.g //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.195: module M with //│ ║ ^^^^^^ //│ ║ l.196: fun g = //│ ║ ^^^^^^^^^^^ //│ ║ l.197: fun h = M.g //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.198: h //│ ╙── ^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/NewWith.mls ================================================ :js :pe :re new //│ ╔══[PARSE ERROR] Expected 'with' keyword, expression, or block after `new` keyword; found end of input instead //│ ║ l.6: new //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. new Object //│ = {} :pe :re new with //│ ╔══[PARSE ERROR] Expected block after 'new' body; found end of input instead //│ ║ l.17: new with //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe :re new Object with //│ ╔══[PARSE ERROR] Expected block after 'new' body; found end of input instead //│ ║ l.25: new Object with //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe :re new Object with 1 //│ ╔══[PARSE ERROR] Expected block after 'new' body; found literal instead //│ ║ l.33: new Object with 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.33: new Object with 1 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e (new Object) with 1 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'with' here //│ ║ l.43: (new Object) with 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // TODO don't show as `$anon` new Object with { val x = 1 } //│ = $anon { x: 1 } :e new Object with { val x = 1 } with { val y = 1 } //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'with' here //│ ║ l.54: new Object with { val x = 1 } with { val y = 1 } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let obj = new Object with val x = 12 //│ obj = $anon { x: 12 } obj.x //│ = 12 let n = new with val x = 12 //│ n = $anon { x: 12 } n.x //│ = 12 class Foo(x) with fun foo = x + 1 print("Foo", x, foo) new Foo(123) with { fun foo = 27 } //│ > Foo 123 27 //│ = $anon :e :re new (Foo(123)) with { fun foo = 27 } //│ ╔══[COMPILATION ERROR] Expected a statically known class; found application of type Foo. //│ ║ l.89: new (Foo(123)) with { fun foo = 27 } //│ ║ ^^^^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ > Foo 123 124 //│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null :e new! (Foo(123)) with { fun foo = 27 } //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'with' here //│ ║ l.98: new! (Foo(123)) with { fun foo = 27 } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re new {Foo(123)} with { fun foo = 27 } //│ ╔══[COMPILATION ERROR] Expected a statically known class; found application of type Foo. //│ ║ l.106: new {Foo(123)} with { fun foo = 27 } //│ ║ ^^^^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ > Foo 123 124 //│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null new Foo(123) with // fun foo = super.foo + 1 // TODO (super) fun foo = 27 print("Bar", foo) //│ > Foo 123 27 //│ > Bar 27 //│ = $anon :e new Foo(123) with 1 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'with' here //│ ║ l.123: new Foo(123) with 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls ================================================ :js // TODO `fun` does not return anything; the returned value should be the result of `bar + 1` fun foo = bar + 1 fun bar = 1 foo fun foo = print(bar) fun bar = 1 foo //│ > 1 // TODO maybe warn that this can be confusing: one might think that `bar` is the one defined above fun bar = 0 fun foo = print(bar) print("...") fun bar = 1 :sjs foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ foo2() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > ... ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/OfStatements.mls ================================================ :js :todo print of let x = 1 x + 1 //│ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position //│ ║ l.6: let x = 1 //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.7: x + 1 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. print of open Predef let x = 1 x + 1 //│ ╔══[COMPILATION ERROR] Illegal position for 'open' statement. //│ ║ l.17: open Predef //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position //│ ║ l.18: let x = 1 //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.19: x + 1 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls ================================================ :js :ignore // ——— ——— ——— // Too hard for Claude // FIXME: Printing of [..[], "a"] //│ = [a] // It should print the same as: ["a"] //│ = ["a"] // ——— ——— ——— :breakme // TODO instrument Predef :re id(1, 2) //│ ╔══[COMPILATION ERROR] Expected 1 arguments, got 2 //│ ║ l.20: id(1, 2) //│ ╙── ^^^^^^ //│ = 1 // ——— ——— ——— :breakme // TODO confusing formatting should not be allowed :e let x = if true then 1 else 0 //│ x = 1 // ——— ——— ——— Infinity //│ = Infinity :sjs val Infinity = 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Infinity; Infinity = 1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Identifier 'Infinity' has already been declared //│ Infinity = Infinity :sjs Infinity //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Infinity //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Infinity module Test with val Infinity = 1 Test.Infinity //│ = 1 // ——— ——— ——— // TODO forbid declarations inside local blocks :breakme :e fun main() = declare fun foo: Int () // TODO forbid unimplemented signatures inside local blocks :breakme :e fun main() = fun bar: Int () // ——— ——— ——— // * Objects should not be allowed to have parameters... // * Or these parameters should have default values. :sjs object Cls(val x) with fun huh = x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Cls1; //│ (class Cls { //│ static { //│ new this //│ } //│ constructor(x1) { //│ Cls1 = this; //│ this.x = x1; //│ Object.defineProperty(this, "class", { //│ value: Cls //│ }); //│ globalThis.Object.freeze(this); //│ } //│ get huh() { //│ return this.x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["object", "Cls"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :e Cls.x //│ ╔══[COMPILATION ERROR] Object 'Cls' does not contain member 'x' //│ ║ l.108: Cls.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' // * Note: the access is not checked because the selection is resolved // * (to an MLscript definition, which is not supposed to return `undefined`). print(Cls.huh) //│ > undefined let c = new Cls(123) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type Cls. //│ ║ l.119: let c = new Cls(123) //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: Cls1 is not a constructor //│ c = undefined c.x //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') // ——— ——— ——— :breakme // TODO: warn against using `undefined` :w fun oops = undefined :re oops //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. // ——— ——— ——— class C :sjs class D extends id(C) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. //│ ║ l.145: class D extends id(C) //│ ║ ^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let D1; //│ (class D extends Predef.id { //│ static { //│ D1 = this //│ } //│ constructor() { //│ super(C1); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "D"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Class extends value id(x) { //│ return x //│ } is not a constructor or null // ——— ——— ——— let x = 0 //│ x = 0 // This somehow parses as `set (x += 1; ())` :fixme set x += 1; () //│ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment //│ ║ l.174: set x += 1; () //│ ╙── ^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e set (x += 1; ()) //│ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment //│ ║ l.181: set (x += 1; ()) //│ ╙── ^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. (set x += 1); () :fixme [x, set x += 1; x] //│ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment //│ ║ l.190: [x, set x += 1; x] //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // ——— ——— ——— import "../../mlscript-compile/Stack.mls" // The parser rejects this, but it should be allowed. open Stack { ::, Nil } //│ ╔══[COMPILATION ERROR] Illegal 'open' statement shape. //│ ║ l.201: open Stack { ::, Nil } //│ ╙── ^^^^^^^^^^^^^^^ // Instead, if we parenthesize the operator, it is rejected by the elaborator. open Stack { (::), Nil } //│ ╔══[COMPILATION ERROR] Illegal 'open' statement element. //│ ║ l.207: open Stack { (::), Nil } //│ ╙── ^^^^ open Stack { Nil, :: } :sjs 1 :: Nil //│ ╔══[COMPILATION ERROR] Module 'Stack' does not contain member '::' //│ ║ l.215: 1 :: Nil //│ ╙── ^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(Stack["::"](1, Stack.Nil)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Stack.:: is not a function // ——— ——— ——— data class Foo(x, y) // TODO missing sanity check :re id(Foo)(1) //│ = Foo(1, undefined) // ——— ——— ——— data class FooSpd(...args) //│ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: mismatched param list lengths List() vs List(term:FooSpd/args) //│ ╟── The compilation result may be incorrect. //│ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. FooSpd(1, 2, 3).args //│ ═══[RUNTIME ERROR] Error: Access to required field 'args' yielded 'undefined' :todo if FooSpd(1, 2, 3) is FooSpd(...args) then args //│ ╔══[COMPILATION ERROR] Unrecognized pattern (spread). //│ ║ l.244: if FooSpd(1, 2, 3) is FooSpd(...args) then args //│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: args //│ ║ l.244: if FooSpd(1, 2, 3) is FooSpd(...args) then args //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] The constructor does not take any arguments but found one argument. //│ ║ l.244: if FooSpd(1, 2, 3) is FooSpd(...args) then args //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] //│ ╔══[COMPILATION ERROR] The constructor does not take any arguments but found three arguments. //│ ║ l.256: if FooSpd(1, 2, 3) is FooSpd(a, b, c) then [a, b, c] //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error if FooSpd(1, 2, 3) is FooSpd(arg) then arg //│ ╔══[COMPILATION ERROR] The constructor does not take any arguments but found one argument. //│ ║ l.262: if FooSpd(1, 2, 3) is FooSpd(arg) then arg //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] Error: match error // ——— ——— ——— // To add to `mlscript/basics/LetBindings.mls` fun foo() = let bar(x: Str): Str = x + x bar("test") //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.272: let bar(x: Str): Str = x + x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: bar //│ ║ l.273: bar("test") //│ ╙── ^^^ // ——— ——— ——— :breakme module Foo(x) :fixme // proper compilation error :sjs module Foo(x) with print(x) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //│ ╟── which references the symbol introduced here //│ ║ l.288: module Foo(x) with //│ ╙── ^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo5; //│ (class Foo4 { //│ static { //│ Foo5 = this //│ } //│ static #x; //│ static { //│ Foo4.#x = x; //│ Predef.print(Foo4.#x); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 :fixme // proper compilation error :sjs module Foo(val x) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //│ ╟── which references the symbol introduced here //│ ║ l.313: module Foo(val x) //│ ╙── ^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo7; //│ (class Foo6 { //│ static { //│ Foo7 = this //│ } //│ static { //│ this.x = x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // ——— ——— ——— // TODO support syntax? data class Foo(mut x) //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found 'mut'-modified identifier //│ ║ l.335: data class Foo(mut x) //│ ╙── ^ // ——— ——— ——— // FIXME should report an error fun Foo class Foo // ——— ——— ——— // * TODO: improve errors (don't cascade meaningless errors) :e class Id(name: UnboundName) //│ ╔══[COMPILATION ERROR] Name not found: UnboundName //│ ║ l.350: class Id(name: UnboundName) //│ ╙── ^^^^^^^^^^^ //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› // ——— ——— ——— ================================================ FILE: hkmc2/shared/src/test/mlscript/backlog/UCS.mls ================================================ :js :ignore // ——— ——— ——— // * Example confusing parses that should be fixed when we fix the parsing of operator splits if 1 + 0 == 0 then "X" == 1 then "A" //│ = "A" if 1 + 1 * 2 == 4 then "X" * 2 == 1 then "A" |> id is r then r //│ ╔══[PARSE ERROR] Operator cannot be used inside this operator split //│ ║ l.15: * 2 == 4 then "X" //│ ║ ^^ //│ ╟── as it has lower precedence than the splitting operator here //│ ║ l.15: * 2 == 4 then "X" //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected literal in this operator split inner position //│ ║ l.15: * 2 == 4 then "X" //│ ║ ^ //│ ╟── Note: the operator split starts here //│ ║ l.15: * 2 == 4 then "X" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.15: * 2 == 4 then "X" //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unrecognized term split (integer literal) //│ ║ l.15: * 2 == 4 then "X" //│ ╙── ^ //│ ═══[COMPILATION ERROR] Unrecognized term split (reference term) //│ ═══[RUNTIME ERROR] Error: match error // ——— ——— ——— class Some(val v) object None // :ucs normalized // Uncomment the line above to see the explosion: ~1200 lines of code. // If the number of branches continues to increase, the time duration of // compilation will noticeably slow down. fun parse(text: Str): [Int, Str] = fun char(idx: Int) = if idx < text.length then Some of text.charAt of idx else None // Some helper parsing utilities. fun a(idx: Int): [Int, Str] = ??? fun b(idx: Int): [Int, Str] = ??? fun c(idx: Int): [Int, Str] = ??? fun d(idx: Int): [Int, Str] = ??? fun e(idx: Int): [Int, Str] = ??? fun f(idx: Int): [Int, Str] = ??? fun g(idx: Int): [Int, Str] = ??? // Note: Use `is` instead `==` can mitigate the explosion. Because the // specialization knows those literal string patterns are disjoint. fun launch(idx: Int) = if char(idx) is Some(ch) and ch == "a" and a(idx + 1) is [idx', res] then ??? "b" and b(idx + 1) is [idx', res'] then ??? "c" and c(idx + 1) is [idx', res''] then ??? "d" and d(idx + 1) is [idx', res'''] then ??? "e" and e(idx + 1) is [idx', res''''] then ??? "f" and f(idx + 1) is [idx', res'''''] then ??? "g" and g(idx + 1) is [idx', res'''''''] then ??? else [idx, ""] launch(0) // ——— ——— ——— data class Tuple(a, b, c) let foo = case Tuple(...elements) then elements //│ ╔══[COMPILATION ERROR] Unrecognized pattern (spread). //│ ║ l.83: Tuple(...elements) then elements //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: elements //│ ║ l.83: Tuple(...elements) then elements //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expected three arguments, but found only one argument. //│ ║ l.83: Tuple(...elements) then elements //│ ╙── ^^^^^ //│ foo = fun foo foo(Tuple(1, 2, 3)) //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // ——— ——— ——— module Foo with class Bar Baz fun foo(x) = if x is Foo. Bar then "Bar" Foo then "Foo" //│ ╔══[PARSE ERROR] Expected end of input; found period instead //│ ║ l.105: fun foo(x) = if x is Foo. //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (identifier). //│ ║ l.105: fun foo(x) = if x is Foo. //│ ╙── ^^^ fun foo(x) = if x is Foo .Bar then "Bar" .Foo then "Foo" //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (operator split). //│ ║ l.115: fun foo(x) = if x is Foo //│ ║ ^^^ //│ ║ l.116: .Bar then "Bar" //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.117: .Foo then "Foo" //│ ╙── ^^^^^^^^^^^^^^^^^ // ——— ——— ——— ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ADTs.mls ================================================ :js :ignore // * Standard syntax of ADTs (TODO) class Expr constructor Lit(n: Int) constructor Add(lhs: Expr, rhs: Expr) //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead //│ ║ l.8: constructor Lit(n: Int) //│ ╙── ^^ class Expr[A] with constructor Lit(n: Int) { A = Int } Add(lhs: Expr[Int], rhs: Expr[Int]) { A = Int } //│ ═══[COMPILATION ERROR] Expected a valid class definition head; found ‹erroneous syntax› instead //│ ═══[COMPILATION ERROR] Expected a valid class definition head; found ‹erroneous syntax› instead // * one can also go the TypeScript way data class Lit(n: Int) data class Add(lhs: Expr, rhs: Expr) type Expr = Lit | Add // * Which could get a "blessed" shorthand syntax as well enum Expr = | Lit(n: Int) | Add(lhs: Expr, rhs: Expr) //│ ╔══[COMPILATION ERROR] Unrecognized definitional assignment left-hand side: juxtaposition //│ ║ l.31: enum Expr = //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/AppOp.mls ================================================ :js id @ 123 //│ = 123 id@123 //│ = 123 fun inc(n) = n + 1 inc @ 1 //│ = 2 :re inc @ inc @ 1 //│ ═══[RUNTIME ERROR] TypeError: f is not a function inc @ (inc @ 1) //│ = 3 inc of inc @ 1 //│ = 3 inc @ inc of 1 //│ = 3 fun add(l)(r) = l + r add @ 1 @ 2 //│ = 3 add@1@2 //│ = 3 @ //│ = fun apply :sjs :fixme @(id, 123) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Predef.apply(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: f is not a function (@)(id, 123) //│ = 123 [tuple of 1, 2, 3] //│ = [[1, 2, 3]] [tuple @ 1, 2, 3] //│ = [[1], 2, 3] fun test(f) = f(42) // :dp test @ x => x + 1 //│ = 43 test @ (x) => x + 1 //│ = 43 test @ (_ + 1) //│ = 43 test @ {_ + 1} //│ = 43 test @ _ + 1 //│ = 43 // * Note: this lambda shorthand includes the whole term, as `_ + 1` is not delimited/parenthesized test @ _ + 1 //│ = fun pipeInto of 1, inc //│ = 2 :re pipeInto @ 1, inc //│ ═══[RUNTIME ERROR] TypeError: f is not a function pipeInto(1, _) @ inc //│ = fun (pipeInto(1, _)) @ inc //│ = 2 (pipeInto of 1, _) @ inc //│ = 2 1 pipeInto(inc) //│ = 2 // * Indeed should not work... // * application shorthand needs an actual application on the right-hand side :e :re 1 pipeInto @ inc //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). //│ ║ l.108: 1 pipeInto @ inc //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] TypeError: f is not a function (1 pipeInto(_)) @ inc //│ = 2 // * Another argument for using `.` rather than juxtaposition 1 \pipeInto @ inc //│ = 2 // * This is a bit surprising... parses as `(inc @ (1 . pipeInto)) @ inc` :re inc @ 1 \pipeInto @ inc //│ ═══[RUNTIME ERROR] TypeError: f is not a function (inc @ 1) \pipeInto @ inc //│ = 3 // * Now `of` has the same left-binding power as normal application 1 \pipeInto of inc //│ = 2 (1 \pipeInto) of inc //│ = 2 let n = inc of 42 //│ n = 43 n \pipeInto @ inc //│ = 44 n \pipeInto @ inc \pipeInto @ inc //│ = 45 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Assert.mls ================================================ :js let xs = [1, 2, 3] //│ xs = [1, 2, 3] :sjs assert true //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; //│ scrut = true; //│ if (scrut === true) { //│ runtime.Unit //│ } else { runtime.safeCall(runtime.assertFail("Assert.mls", "9")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :re assert false //│ ═══[RUNTIME ERROR] Error: Assertion failed (Assert.mls:19) assert xs is Array // * FIXME: Error-prone!! :sjs :fixme assert(true); 123 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut2; //│ scrut2 = (true , 123); //│ if (scrut2 === true) { //│ runtime.Unit //│ } else { runtime.safeCall(runtime.assertFail("Assert.mls", "28")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Assertion failed (Assert.mls:28) // * FIXME: the `else` is parsed as part of the `assert`! :sjs :fixme if 1 is 2 then assert true else 3 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut3, scrut4; //│ scrut3 = 1; //│ if (scrut3 === 2) { //│ scrut4 = true; //│ if (scrut4 === true) { //│ runtime.Unit //│ } else { 3 } //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error assert true else 0 1 //│ = 1 assert false else 0 1 //│ = 0 assert xs is [1, ...ys] else [] ys //│ = [2, 3] assert xs is [2, ...ys] else [] ys //│ = [] (assert true else 2) + 3 //│ = "()3" (assert true else 2, 1) + 3 //│ = 4 (assert false else 2, 1) + 3 //│ = 5 :re (assert false else throw Error("whoops")) + 3 //│ ═══[RUNTIME ERROR] Error: whoops fun test(arg) = assert arg is [l, r] else arg [l + r] test([2, 3]) //│ = [5] test([1]) //│ = [1] import "../../mlscript-compile/Option.mls" open Option fun sumOpts(arg1, arg2) = assert arg1 is Some(arg1) and arg2 is Some(arg2) else None print("Both args are Some!") Some(arg1 + arg2) sumOpts(Some(2), Some(3)) //│ > Both args are Some! //│ = Some(5) sumOpts(Some(2), None) //│ = None sumOpts(None, Some(3)) //│ = None sumOpts(None, None) //│ = None ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadClasses.mls ================================================ // TODO should make better error messages! :e class Foo { log("hi") } //│ ╔══[COMPILATION ERROR] Invalid class definition head: unexpected block in this position //│ ║ l.6: class Foo { log("hi") } //│ ╙── ^^^^^^^^^^^^^ :e class Foo { 1 } //│ ╔══[COMPILATION ERROR] Invalid class definition head: unexpected block in this position //│ ║ l.12: class Foo { 1 } //│ ╙── ^^^^^ // * Note that these work: :w class Foo with { 1 } //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.21: class Foo with { 1 } //│ ╙── ^ :w class Foo with 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.28: 1 //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadDefs.mls ================================================ :js val (++) x = 0 //│ ++ = 0 //│ x = 0 x //│ = 0 ++ //│ = 0 :sjs val ++ = 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let $_$_; $_$_ = 0; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ++ = 0 :sjs ++ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ $_$_ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :e val ++ y = 1 //│ ╔══[COMPILATION ERROR] Invalid value definition head: unexpected identifier in this position //│ ║ l.31: val ++ y = 1 //│ ╙── ^ //│ ++ = 1 :e y //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.38: y //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ++ //│ = 1 :e fun ++ z = 0 //│ ╔══[COMPILATION ERROR] Invalid function definition head: unexpected identifier in this position //│ ║ l.49: fun ++ z = 0 //│ ╙── ^ :sjs ++ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ $_$_2() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :e fun (class Lol) foo = 0 //│ ╔══[COMPILATION ERROR] This type definition is not a valid symbolic name //│ ║ l.63: fun (class Lol) foo = 0 //│ ╙── ^^^^^^^^^ foo //│ = 0 :e Lol //│ ╔══[COMPILATION ERROR] Name not found: Lol //│ ║ l.72: Lol //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls ================================================ :js :sjs :e 1::x //│ ╔══[COMPILATION ERROR] Integer literal is not a known class. //│ ║ l.6: 1::x //│ ║ ^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. //│ ║ l.6: 1::x //│ ╙── ^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; //│ lambda = (undefined, function (self, ...args) { return runtime.safeCall(self.x(...args)) }); //│ lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :ssjs :e :re 1::x() //│ ╔══[COMPILATION ERROR] Integer literal is not a known class. //│ ║ l.25: 1::x() //│ ║ ^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. //│ ║ l.25: 1::x() //│ ╙── ^ //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let lambda1; //│ lambda1 = (undefined, function (self, ...args) { //│ runtime.checkArgs("", 1, false, arguments.length); //│ return runtime.safeCall(self.x(...args)) //│ }); //│ block$res2 = runtime.safeCall(lambda1()); //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 fun (::) f(a, b) = [a, b] let x = 1 //│ x = 1 "A" :: x //│ = ["A", 1] "A":: x //│ = ["A", 1] :e "A"::x //│ ╔══[COMPILATION ERROR] String literal is not a known class. //│ ║ l.58: "A"::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ╔══[COMPILATION ERROR] Expected a statically known class; found string literal. //│ ║ l.58: "A"::x //│ ╙── ^^^ //│ = fun :e "A" ::x //│ ╔══[COMPILATION ERROR] String literal is not a known class. //│ ║ l.70: "A" ::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ╔══[COMPILATION ERROR] Expected a statically known class; found string literal. //│ ║ l.70: "A" ::x //│ ╙── ^^^ //│ = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadModuleUses.mls ================================================ :js module M with fun mtd() = 42 :e module Example with fun foo(): module null = M //│ ╔══[COMPILATION ERROR] Expected a module type; found null literal. //│ ║ l.9: fun foo(): module null = M //│ ║ ^^^^ //│ ╙── Function marked as returning a 'module' must have a module return type. module Example with fun foo(): module M = M :e let m = Example.foo() //│ ╔══[COMPILATION ERROR] Unexpected moduleful application of type M. //│ ║ l.19: let m = Example.foo() //│ ╙── ^^^^^^^^^^^^^ //│ m = class M :breakme // FIXME: should be an error :e id(m).mtd() //│ = 42 Example.foo() //│ = class M :e id(Example.foo()) //│ ╔══[COMPILATION ERROR] Unexpected moduleful application of type M. //│ ║ l.35: id(Example.foo()) //│ ║ ^^^^^^^^^^^^^ //│ ╙── Module argument passed to a non-module parameter. //│ = class M Example.foo().mtd() //│ = 42 :e Example.foo() + 1 //│ ╔══[COMPILATION ERROR] Unexpected moduleful application of type M. //│ ║ l.46: Example.foo() + 1 //│ ╙── ^^^^^^^^^^^^^ //│ = "class M { static { M1 = this } static mtd() { runtime.checkArgs(\"mtd\", 0, true, arguments.length); return 42 } toString() { return runtime.render(this); } static [definitionMetadata] = [\"class\", \"M\"]; }1" :e M + 1 //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.53: M + 1 //│ ╙── ^ //│ = "class M { static { M1 = this } static mtd() { runtime.checkArgs(\"mtd\", 0, true, arguments.length); return 42 } toString() { return runtime.render(this); } static [definitionMetadata] = [\"class\", \"M\"]; }1" :e M |> id //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.60: M |> id //│ ║ ^ //│ ╙── Module argument passed to a non-module parameter. //│ = class M :e id <| M //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.68: id <| M //│ ║ ^ //│ ╙── Module argument passed to a non-module parameter. //│ = class M fun (+) lol(a, b) = [a, b] :e M + 1 //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.78: M + 1 //│ ║ ^ //│ ╙── Module argument passed to a non-module parameter. //│ = [class M, 1] :e let m = M //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.86: let m = M //│ ╙── ^ //│ m = class M :e module A with fun f() = 2 module B with val a: module A = A print(B.a) //│ ╔══[COMPILATION ERROR] Unexpected moduleful selection of type A. //│ ║ l.98: print(B.a) //│ ╙── ^^^ //│ > class A :e :re id(module 1) //│ ╔══[COMPILATION ERROR] Illegal type declaration in term position. //│ ║ l.107: id(module 1) //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadModules.mls ================================================ :js class Foo :ge module M extends Foo //│ ╔══[COMPILATION ERROR] Modules cannot have an extension clause. //│ ║ l.7: module M extends Foo //│ ╙── ^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadMut.mls ================================================ :js :e mut "oops" //│ ╔══[COMPILATION ERROR] Illegal position for 'mut' modifier. //│ ║ l.5: mut "oops" //│ ╙── ^^^ //│ = "oops" :e mut a: 1 //│ ╔══[COMPILATION ERROR] Unexpected record key shape. //│ ║ l.12: mut a: 1 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e mut {"oops"} //│ ╔══[COMPILATION ERROR] Expected a record after 'mut' keyword; found a block //│ ║ l.19: mut {"oops"} //│ ╙── ^^^^^^ //│ = "oops" ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadOverloading.mls ================================================ :js :e class Foo class Foo //│ ╔══[COMPILATION ERROR] Multiple definitions of symbol 'Foo' //│ ╟── defined here //│ ║ l.5: class Foo //│ ║ ^^^^^^^^^ //│ ╟── defined here //│ ║ l.6: class Foo //│ ╙── ^^^^^^^^^ :e class Foo object Foo //│ ╔══[COMPILATION ERROR] Illegal overloading of class 'Foo' //│ ║ l.16: class Foo //│ ║ ^^^^^^^^^ //│ ╟── with object of the same name //│ ║ l.17: object Foo //│ ╙── ^^^^^^^^^^ :e object Foo class Foo //│ ╔══[COMPILATION ERROR] Illegal overloading of class 'Foo' //│ ║ l.27: class Foo //│ ║ ^^^^^^^^^ //│ ╟── with object of the same name //│ ║ l.26: object Foo //│ ╙── ^^^^^^^^^^ :e fun Foo = 1 object Foo //│ ╔══[COMPILATION ERROR] Illegal overloading of function 'Foo' //│ ║ l.36: fun Foo = 1 //│ ║ ^^^^^^^^^^^ //│ ╟── with object of the same name //│ ║ l.37: object Foo //│ ╙── ^^^^^^^^^^ // * Note that this does not report an error at the moment: fun Foo: Int object Foo :e fun Foo = 1 object Foo class Foo //│ ╔══[COMPILATION ERROR] Illegal overloading of function 'Foo' //│ ║ l.50: fun Foo = 1 //│ ║ ^^^^^^^^^^^ //│ ╟── with object of the same name //│ ║ l.51: object Foo //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' //│ ║ l.50: fun Foo = 1 //│ ║ ^^^^^^^^^^^ //│ ╟── with class of the same name //│ ║ l.52: class Foo //│ ╙── ^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Illegal overloading of class 'Foo' //│ ║ l.52: class Foo //│ ║ ^^^^^^^^^ //│ ╟── with object of the same name //│ ║ l.51: object Foo //│ ╙── ^^^^^^^^^^ // TODO: support :e class Foo fun Foo = 1 //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' //│ ║ l.76: fun Foo = 1 //│ ║ ^^^^^^^^^^^ //│ ╟── with class of the same name //│ ║ l.75: class Foo //│ ╙── ^^^^^^^^^ // TODO: support :e fun Foo = 1 module Foo //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' //│ ║ l.86: fun Foo = 1 //│ ║ ^^^^^^^^^^^ //│ ╟── with module of the same name //│ ║ l.87: module Foo //│ ╙── ^^^^^^^^^^ // TODO: support :e val Foo = 1 module Foo //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of value 'Foo' //│ ║ l.97: val Foo = 1 //│ ║ ^^^^^^^^^^^ //│ ╟── with module of the same name //│ ║ l.98: module Foo //│ ╙── ^^^^^^^^^^ //│ Foo = class Foo :e fun (**) f1(a, b) = "[" + a + " * " + b + "]" fun (**) f2(a, b) = "(" + a + " + " + b + ")" //│ ╔══[COMPILATION ERROR] Symbolic name '**' of definition 'f1' is already used //│ ║ l.109: fun (**) f1(a, b) = "[" + a + " * " + b + "]" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── by sibling member 'f2' //│ ║ l.110: fun (**) f2(a, b) = "(" + a + " + " + b + ")" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1 ** 2 //│ = "(1 + 2)" :e fun (**) t() = 0 fun (++) t() = 1 //│ ╔══[COMPILATION ERROR] Multiple definitions of symbol 't' //│ ╟── defined here //│ ║ l.122: fun (**) t() = 0 //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── defined here //│ ║ l.123: fun (++) t() = 1 //│ ╙── ^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadParams.mls ================================================ :js :e (val x) => x //│ ╔══[COMPILATION ERROR] Illegal function parameter modifiers: val //│ ║ l.5: (val x) => x //│ ╙── ^ //│ = fun :e (mut x) => x //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found 'mut'-modified identifier //│ ║ l.12: (mut x) => x //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.12: (mut x) => x //│ ╙── ^ //│ = fun :e (mut val x) => x //│ ╔══[COMPILATION ERROR] Illegal function parameter modifiers: mut val //│ ║ l.22: (mut val x) => x //│ ╙── ^ //│ = fun :e fun f(val x) = x //│ ╔══[COMPILATION ERROR] Illegal function parameter modifiers: val //│ ║ l.29: fun f(val x) = x //│ ╙── ^ :e (...val x) => x //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found spread //│ ║ l.35: (...val x) => x //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.35: (...val x) => x //│ ╙── ^ //│ = fun // TODO: prevent duplicate parameter names? :sjs (x, x) => x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda4; lambda4 = (undefined, function (x, x1) { return x1 }); lambda4 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :e (...x, ...y) => x //│ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. //│ ║ l.53: (...x, ...y) => x //│ ╙── ^^^^ //│ = fun :e (..., ...y) => y //│ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. //│ ║ l.60: (..., ...y) => y //│ ╙── ^^^ //│ = fun :e class Foo(class C) //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found type definition //│ ║ l.68: class Foo(class C) //│ ╙── ^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls ================================================ :e module M with fun f(foo: Int, using bar: Int) //│ ╔══[COMPILATION ERROR] Keyword `using` must occur before all parameters. //│ ║ l.4: fun f(foo: Int, using bar: Int) //│ ╙── ^^^^^^^^^^^^^^ module M with fun f(using Int) :e M.f //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.13: M.f //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.10: fun f(using Int) //│ ║ ^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› :todo // support literal type using 42 = 42 use[42] //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Cannot add instance with a not-yet implemented type. val someInt = 42 :e using someInt = 42 //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. //│ ║ l.30: using someInt = 42 //│ ║ ^^^^^^^ //│ ╙── Implicit instance must be marked as returning a 'module' in order to have a module return type. //│ ╔══[COMPILATION ERROR] Expected a type, got reference //│ ║ l.30: using someInt = 42 //│ ╙── ^^^^^^^ :e using someInt: Int = 42 //│ ╔══[COMPILATION ERROR] Expected a type, got block //│ ║ l.40: using someInt: Int = 42 //│ ╙── ^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expected a type, got a non-type block //│ ║ l.40: using someInt: Int = 42 //│ ╙── ^^^^^^^^^^^^ module M with fun bar[A, B](a: A, b: B) = a + b :e M.bar[](1, 2) //│ ╔══[COMPILATION ERROR] Expected 2 type arguments, got 0 //│ ║ l.52: M.bar[](1, 2) //│ ╙── ^^^^^^^ :e M.bar[Int](1, 2) //│ ╔══[COMPILATION ERROR] Expected 2 type arguments, got 1 //│ ║ l.58: M.bar[Int](1, 2) //│ ╙── ^^^^^^^^^^ using Int = 42 module M with fun foo()(using a: Int) // This should fail because M.foo() returns a function that requires special handling :breakme :e M.foo() :e fun f[T](using t: T) = t f //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter T) for call: //│ ║ l.75: f //│ ║ ^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.74: fun f[T](using t: T) = t //│ ║ ^^^^ //│ ╙── Illegal query for an unspecified type variable T. :e fun f(using t: Int = 42) = t //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found term definition //│ ║ l.85: fun f(using t: Int = 42) = t //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: t //│ ║ l.85: fun f(using t: Int = 42) = t //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/BlockArgs.mls ================================================ :todo fun foo(x){f} = f(x) //│ ╔══[COMPILATION ERROR] Expected a valid function definition head; found block instead //│ ║ l.4: fun foo(x){f} = //│ ╙── ^^^ foo(1){x => x} //│ ╔══[COMPILATION ERROR] Name not found: foo //│ ║ l.10: foo(1){x => x} //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (infix operator '=>'). //│ ║ l.10: foo(1){x => x} //│ ╙── ^^^^^^ foo(1) x => x //│ ╔══[COMPILATION ERROR] Name not found: foo //│ ║ l.18: foo(1) //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (infix operator '=>'). //│ ║ l.19: x => x //│ ╙── ^^^^^^ foo(1) { x => x } //│ ╔══[COMPILATION ERROR] Name not found: foo //│ ║ l.27: foo(1) { x => x } //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (infix operator '=>'). //│ ║ l.27: foo(1) { x => x } //│ ╙── ^^^^^^ foo(1) with x => x //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'with' here //│ ║ l.35: foo(1) with x => x //│ ╙── ^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ByNameTailPosition.mls ================================================ :js // `baz()` should be a call in tail position (like `print(1)`), not bound to a variable :sir fun baz = print(1) fun bar() = baz //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let bar⁰, baz⁰; //│ define baz⁰ as fun baz¹() { //│ return Predef⁰.print⁰(1) //│ }; //│ define bar⁰ as fun bar¹() { return baz¹() }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CallSyntaxes.mls ================================================ :js 123 \print() //│ > 123 123 \tuple() //│ = [123] tuple(123, 456) //│ = [123, 456] 123 \tuple(456) //│ = [123, 456] 123 \tuple of 456 //│ = [123, 456] 123 \tuple @ 456 //│ = [123, 456] 123 |> (tuple(_, 456)) //│ = [123, 456] // * Maybe later support this syntax? :pe :re 123 |> tuple(., 456) //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.29: 123 |> tuple(., 456) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.29: 123 |> tuple(., 456) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Classes.mls ================================================ :js class Foo class Foo() class Foo(x: Int) class Foo(x: Int, y: Str) class Foo(x: Int) with val y = x :fixme class Foo(x: Int) { val y = x } //│ ╔══[PARSE ERROR] Expected end of input; found curly brace section instead //│ ║ l.17: class Foo(x: Int) { val y = x } //│ ╙── ^^^^^^^^^^^^^ :fixme class Foo(x: Int) val y = x //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead //│ ║ l.24: val y = x //│ ╙── ^^ :sjs :fixme :e class Foo(x: Int) { log("Hello!") } //│ ╔══[COMPILATION ERROR] Expected a valid class definition head; found block instead //│ ║ l.32: class Foo(x: Int) { log("Hello!") } //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // *** *** *** :e class Foo(42) //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.45: class Foo(42) //│ ╙── ^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CompanionFunctions.mls ================================================ :js :fixme // TODO: support // :sjs fun Foo(x) = new Foo(x, 0) data class Foo(x, y) //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' //│ ║ l.6: fun Foo(x) = new Foo(x, 0) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── with class of the same name //│ ║ l.7: data class Foo(x, y) //│ ╙── ^^^^^^^^^^^^^^^ :fixme Foo(123) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.16: Foo(123) //│ ╙── ^^^^^ //│ = Foo(123, undefined) ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CompanionModules_Classes.mls ================================================ :js class C() module C with val empty = 1 :rt C() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=class:C⁰}: //│ lhs = Resolved{sym=term:C⁰/C¹}: //│ t = Ref{sym=member:C²} of member:C² //│ sym = term:C⁰/C¹ //│ rhs = Tup of Nil //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = C() :sir let f = new C //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰, f¹; define f¹ as fun f²() { return new C⁰() }; set f⁰ = f²; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f f() //│ = C() :rt new C() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = New{typ=class:C⁰}: //│ cls = Resolved{sym=class:C⁰}: //│ t = Ref{sym=member:C²} of member:C² //│ sym = class:C⁰ //│ args = Ls of //│ Tup of Nil //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = C() :rt C.empty //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:C³/empty⁰}: //│ t = Sel{sym=member:empty¹}: //│ prefix = Resolved{sym=module:C³,typ=module:C³}: //│ t = Ref{sym=member:C²} of member:C² //│ sym = module:C³ //│ nme = Ident of "empty" //│ sym = term:C³/empty⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :breakme // TODO: should be an error, down the line, when we resolve all selections statically :e :re id(C).empty //│ ═══[RUNTIME ERROR] Error: Access to required field 'empty' yielded 'undefined' class C module C with val empty = 1 :rt C.empty //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:C⁴/empty²}: //│ t = Sel{sym=member:empty³}: //│ prefix = Resolved{sym=module:C⁴,typ=module:C⁴}: //│ t = Ref{sym=member:C⁵} of member:C⁵ //│ sym = module:C⁴ //│ nme = Ident of "empty" //│ sym = term:C⁴/empty² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 class Test with fun hi = Test.ans module Test with val ans = 42 fun test = new Test Test.test.hi //│ = 42 mut val state = 10 //│ state = 10 // * The order of evaluation of modules matters; // * they should not be moved to their companion classes (the opposite should happen) :sjs class Foo() with // the class definition is moved to the location of its companion fun foo = state set state += 1 module Foo with val res = Foo().foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1, tmp1; //│ tmp1 = state + 1; //│ state = tmp1; //│ Foo1 = function Foo() { //│ return globalThis.Object.freeze(new Foo.class()); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ static { //│ let tmp2; //│ tmp2 = Foo1(); //│ this.res = tmp2.foo; //│ } //│ get foo() { //│ return state; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", []]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 11 Foo.res //│ = 11 fun bar = using Int = 42 Foo.foo mut val x = 1 print(x) module Foo with set x += 1 fun foo(using Int) = use[Int] print(x) bar //│ > 1 //│ > 2 //│ = 42 //│ x = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CompanionModules_Functions.mls ================================================ :js :fixme // TODO: support // :sjs fun foo(x) = x + 1 module foo with val zero = foo(0) //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'foo' //│ ║ l.6: fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── with module of the same name //│ ║ l.7: module foo with //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.8: val zero = foo(0) //│ ╙── ^^^^^^^^^^^^^^^^^^^ :fixme foo(123) //│ ═══[RUNTIME ERROR] TypeError: Class constructor foo cannot be invoked without 'new' ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CompanionModules_Patterns.mls ================================================ :js :fixme pattern Foo = 1 module Foo //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of pattern 'Foo' //│ ║ l.5: pattern Foo = 1 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── with module of the same name //│ ║ l.6: module Foo //│ ╙── ^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CompanionModules_Types.mls ================================================ :js type Foo = Int module Foo with val default: Foo = 123 Foo.default //│ = 123 data class Cons[A](head: A, tail: List[A]) object Nil type List[A] = Cons[A] module List with fun map[A, B](f: A -> B, xs: List[A]): List[B] = if xs is Nil then Nil Cons(head, tail) then Cons(f(head), map(f, tail)) ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CyclicModuleForwarders.mls ================================================ :js fun f: module M2 = M1.List module M1 with fun List: module M2 = M2.List module M2 with fun List: module M1 = M1.List :breakme // TODO: init error :e f //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/CyclicValues.mls ================================================ :js data class Foo(x) let foo = new mut Foo(null) //│ foo = Foo(null) set foo.x = foo foo //│ = Foo(ref'1) as ref'1 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/DataClass.mls ================================================ :js data class Foo(x: Int) Foo(1).x //│ = 1 // TODO: support? :todo data Foo(x: Int) //│ ╔══[WARNING] This annotation has no effect. //│ ╟── This annotation is not supported on application of type Foo terms. //│ ║ l.12: data Foo(x: Int) //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term //│ ║ l.12: data Foo(x: Int) //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Declare.mls ================================================ :js :sjs declare fun foo: Int //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // :re :sjs foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.foo //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— declare fun p: Int // :re p fun q: Int fun q = 1 q //│ = 1 declare module q with { val x } :re q.x //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') declare class C module M with declare class D // * FIXME LP: @Harry why is this not resolved? :rt C //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=class:C⁰}: //│ t = Ref{sym=member:C¹} of member:C¹ //│ sym = class:C⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * whereas this is: M.D //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=class:D⁰}: //│ t = Sel{sym=member:D¹}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "D" //│ sym = class:D⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :fixme // FIXME: Should report a proper error about lack of declared C companion module C.oops //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'C' //│ ║ l.66: C.oops //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.37: declare class C //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'C' //│ ║ l.66: C.oops //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.37: declare class C //│ ╙── ^ //│ ═══[RUNTIME ERROR] ReferenceError: C is not defined :re M.D.oops //│ ═══[RUNTIME ERROR] Error: Access to required field 'D' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/DisruptiveComments.mls ================================================ :js "a" + // hi "b" //│ = "ab" "a" + // hi "b" //│ = "ab" "a" + // hi "b" //│ = "ab" + 1 + 2 //│ = 3 + 1 // oops + 2 //│ = 3 object Example with // whoops val a = this assert Example.a is Example pattern Bin = | 0 // oops | 1 assert 1 is Bin ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Drop.mls ================================================ :js :sjs drop id(2 + 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = 2 + 2; Predef.id(tmp); runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sir :sjs drop { a: 0, b: 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.Unit //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let a⁰, b⁰, tmp, tmp1; //│ set a⁰ = 0; //│ set tmp = { "a": a⁰ }; //│ set b⁰ = 1; //│ set tmp1 = { "b": b⁰ }; //│ runtime⁰.Unit⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs let discard = drop _ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let discard, discard1; discard1 = function discard(_0) { return runtime.Unit }; discard = discard1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ discard = fun discard discard of { a: 0, b: 1 } // * Notice the difference in how these are parsed: :pt drop id id do id id //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ PrefixApp: //│ kw = Keywrd of keyword 'drop' //│ rhs = Ident of "id" //│ PrefixApp: //│ kw = Keywrd of keyword 'drop' //│ rhs = Ident of "id" //│ PrefixApp: //│ kw = Keywrd of keyword 'do' //│ rhs = Block of Ls of //│ Ident of "id" //│ Ident of "id" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls ================================================ :js let xs = mut [1, 2, 3] //│ xs = [1, 2, 3] :sjs xs.(0) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ xs[0] //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :sjs set xs.(0) = 4 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ xs[0] = 4; runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— xs.(0) //│ = 4 set xs.[0] += 1 set xs.[0] = xs.[0] + 1 xs.[0] //│ = 6 set xs.("lol") = "wat" xs //│ = [6, 2, 3] class Foo(val x) let foo = Foo(123) //│ foo = Foo(123) foo.("x") //│ = 123 // TODO sanitize :breakme :re foo.("y") // TODO sanitize :breakme :re foo.(0) :re foo.[0] //│ ═══[RUNTIME ERROR] TypeError: foo.at is not a function // * Array indexing syntax is only for numeric indices – this should throw :re foo.["x"] //│ ═══[RUNTIME ERROR] TypeError: foo.at is not a function class DynCtor() with set this.("dynField") = 42 DynCtor().dynField //│ = 42 new DynCtor().dynField //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/DynamicInstantiation.mls ================================================ :js class C :sjs new! C //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new C1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = C let c = C //│ c = class C new! c //│ = C let f = new! _ //│ f = fun f f(c) //│ = C // * Same in JS: // * > new id(C) // * Uncaught TypeError: id is not a constructor // * > new (id(C)) // * C {} :sjs :pt :re new! id(C) //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ App: //│ lhs = PrefixApp: //│ kw = Keywrd of keyword 'new!' //│ rhs = Ident of "id" //│ rhs = Tup of Ls of //│ Ident of "C" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new Predef.id(C1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Predef.id is not a constructor // * Ok (as in JS) new! (id(C)) //│ = C // * We should reject static new on an unknown class. :e :re new id(C) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. //│ ║ l.53: new id(C) //│ ║ ^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: Predef.id is not a constructor :e new (id(C)) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found application. //│ ║ l.61: new (id(C)) //│ ║ ^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ = C data class C(x, y) // Note: mismatched number of arguments and class parameters // (dead param elimination cannot yet handle this and used to throw a softTODO here) // :todo let f = new! C() //│ f = C(undefined, undefined) let f = new! C //│ f = fun f f(1, 2) //│ = C(1, 2) let f = x => new! (id(C))(x, x) //│ f = fun f f(1) //│ = C(1, 1) new! C(1, 2) //│ = C(1, 2) let o = new! mut Object //│ o = {} set o.x = 1 o.x //│ = 1 set o.x += 1 in o.x //│ = 2 o.x //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/DynamicSelection.mls ================================================ :js let r = a: 1 value: 2 //│ r = {a: 1, value: 2} :pt r!a //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ DynAccess: //│ obj = Ident of "r" //│ fld = Ident of "a" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 r!("a") //│ = 1 r !a //│ = 1 :e :fixme r! a //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.26: r! a //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator //│ ║ l.26: r! a //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :fixme r ! a //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.37: r ! a //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator //│ ║ l.37: r ! a //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * Note: artifact from BbML/InvalML :sjs !(r) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ r.value //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 let t = [1, 2, 3] //│ t = [1, 2, 3] t!0 //│ = 1 t!("0") //│ = 1 t!(0) //│ = 1 t![0] //│ = 1 t.1 //│ = 2 t.01 //│ = 2 :w :fixme // * Strange that this is treated as a statement !(1,2) //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.79: !(1,2) //│ ╙── ^ //│ > try { block$res15 = 2.value; undefined; undefined } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ > ^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Invalid or unexpected token :ge (!)(1,2) //│ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator //│ ║ l.88: (!)(1,2) //│ ╙── ^^^^^^^^ //│ ═══[COMPILATION ERROR] Cannot call non-binary builtin symbol '!' //│ ═══[RUNTIME ERROR] Error: Cannot call non-binary builtin symbol '!' :sjs (!) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; lambda = (undefined, function (arg) { return ! arg }); lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :fixme !1 //│ ╔══[PARSE ERROR] Expected an expression; found dynamic selector instead //│ ║ l.103: !1 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found dynamic selector instead //│ ║ l.103: !1 //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ExplicitSelections.mls ================================================ :js class Foo() with val x = 1 fun f() = x + 1 Foo().x //│ = 1 Foo().Foo#x //│ = 1 Foo().f() //│ = 2 Foo().Foo#f() //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/FunDefs.mls ================================================ fun foo = 1 foo fun (+) plus: Int fun (+) : Int 1 + 2 fun f = g fun g = f fun (++)(x, y) = 1 1 ++ 2 fun (**) pow = 1 1 ** 2 fun (:::) appendAll[A](a, b): A = a + b fun (>>) foo(a, b) = a * b 2 >> 2 :e fun ~ concat(a, b) = a + b //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found application //│ ║ l.37: fun ~ concat(a, b) = a + b //│ ╙── ^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.37: fun ~ concat(a, b) = a + b //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: b //│ ║ l.37: fun ~ concat(a, b) = a + b //│ ╙── ^ // * Note the strange parsing :pt :todo // better error? fun not(b) = !b //│ ╔══[PARSE ERROR] Expected an expression; found dynamic selector instead //│ ║ l.51: fun not(b) = !b //│ ╙── ^^ //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ DynAccess: //│ obj = TermDef: //│ k = Fun //│ head = PrefixApp: //│ kw = Keywrd of keyword 'not' //│ rhs = Bra: //│ k = Round //│ inner = Ident of "b" //│ rhs = S of Error //│ fld = Ident of "b" //│ ╔══[COMPILATION ERROR] Illegal definition in term position. //│ ║ l.51: fun not(b) = !b //│ ╙── ^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/FunnyRecordKeys.mls ================================================ :js :global :sjs { a: 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a; a = 1; globalThis.Object.freeze({ a: a }) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {a: 1} { "a": 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze({ a: 1 }) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {a: 1} :sjs { " ": 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze({ " ": 1 }) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {" ": 1} :sjs { 0: 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze({ 0: 1 }) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {"0": 1} :sjs { (0): 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze({ 0: 1 }) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {"0": 1} :sjs { (id(0)): 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = Predef.id(0); globalThis.Object.freeze({ [tmp]: 1 }) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {"0": 1} ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/GenericClasses.mls ================================================ class Foo[A] class Foo[A]() class Foo[A](x: A) class Foo[A](x: A -> Int) class Foo[A](x: Foo[A]) class Foo[A](x: Foo[A] -> A) class Foo[A](x: Bar[A]) class Bar[B](x: Foo[Int], y: B -> Int) class Foo[A](x: Bar[A]) class Baz[C](x: C) class Bar[B](x: Foo[Int], y: Baz[B] -> Int) class Foo[A] with fun foo: A -> A :e class Foo[A](x: Foo[A, A]) //│ ╔══[COMPILATION ERROR] Expected 1 type arguments, got 2 //│ ║ l.30: class Foo[A](x: Foo[A, A]) //│ ╙── ^^^^^^^^^ :e class Foo[A](x: Bar[A]) class Bar(x: Foo[Int]) //│ ╔══[COMPILATION ERROR] Expected no type arguments, got 1 //│ ║ l.36: class Foo[A](x: Bar[A]) //│ ╙── ^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Getters.mls ================================================ :js :ignore get foo = 123 //│ ╔══[COMPILATION ERROR] Unrecognized definitional assignment left-hand side: juxtaposition //│ ║ l.6: get foo = 123 //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * These two should be equivalent and should *not* be getters: fun f = x => x fun f(x) = x ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/IdentEscape.mls ================================================ let id"of" = 42 fun id"fun"(x: Int) = x + 1 id"fun"(id"of") let id"if" = 1 id"if" + 1 module id"object" with object id"class" class id"module"(x: Int) :pe id"" //│ ╔══[LEXICAL ERROR] unexpected identifier escape //│ ║ l.14: id"" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found error instead //│ ║ l.14: id"" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found error instead //│ ║ l.14: id"" //│ ╙── ^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/IfThenNewline.mls ================================================ :js // Regression test: `if true` with `then` on a new indented line should work // the same as `if true then` on the same line. // Works (same line) if true then 5 else 1 //│ = 5 // Previously broken (then on next line) if true then 5 else 1 //│ = 5 // Also test false if false then 5 else 1 //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Indentation.mls ================================================ :js tuple( ) //│ = [] tuple( ) tuple() //│ = [[]] tuple( ) tuple() //│ = [[]] tuple( id(2 +2)) tuple() //│ = [[4]] tuple( id(2 +2)) tuple() //│ = [[4]] tuple( id(2 +2)) tuple() //│ = [] tuple( id(2 +2)) tuple() tuple() //│ = [[[4]]] // 🤔 :e tuple( id(2 +2)) tuple() tuple() tuple() //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (juxtaposition). //│ ║ l.47: tuple() //│ ║ ^^^^^^^ //│ ║ l.48: tuple() //│ ║ ^^^^^^^^^^^ //│ ║ l.49: tuple() //│ ╙── ^^ //│ = [[4]] module A with { } :p tuple( ) //│ ┊tuple┊(⟨┊↵┊⟩)┊ //│ Parsed: //│ App(Ident(tuple),Tup(List())) //│ = [] :w tuple( 1 ) //│ ╔══[WARNING] This literal should be indented //│ ║ l.72: 1 //│ ║ ^ //│ ╟── since it is a continuation of the new line here //│ ║ l.71: tuple( //│ ║ ^ //│ ║ l.72: 1 //│ ╙── //│ = [1] tuple( ) //│ = [] print( 2) 1 //│ > 2 //│ = 1 id( 1) //│ = 1 id( id)(1) //│ = 1 :p id( 2) print() //│ ┊id┊(⟨┊→⟨┊2┊⟩←┊⟩)┊→⟨┊print┊(⟨┊┊⟩)┊⟩←┊ //│ Parsed: //│ Jux(App(Ident(id),Tup(List(IntLit(2)))),Block(List(App(Ident(print),Tup(List()))))) //│ > 2 id(0 passTo(id))(2) //│ = 0 :p id( 2 ) print() //│ ┊id┊(⟨┊→⟨┊2┊⟩←┊↵┊⟩)┊→⟨┊print┊(⟨┊┊⟩)┊⟩←┊ //│ Parsed: //│ Jux(App(Ident(id),Tup(List(IntLit(2)))),Block(List(App(Ident(print),Tup(List()))))) //│ > 2 :p id( id )(1) //│ ┊id┊(⟨┊→⟨┊id┊⟩←┊↵┊⟩)┊(⟨┊1┊⟩)┊ //│ Parsed: //│ App(App(Ident(id),Tup(List(Ident(id)))),Tup(List(IntLit(1)))) //│ = 1 :e :p id 1 //│ ┊id┊→⟨┊1┊⟩←┊ //│ Parsed: //│ Jux(Ident(id),Block(List(IntLit(1)))) //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (integer literal). //│ ║ l.137: 1 //│ ╙── ^ //│ = fun id :e :re :p id(0 id)(1) //│ ┊id┊(⟨┊0┊→⟨┊id┊⟩←┊⟩)┊(⟨┊1┊⟩)┊ //│ Parsed: //│ App(App(Ident(id),Tup(List(Jux(IntLit(0),Block(List(Ident(id))))))),Tup(List(IntLit(1)))) //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). //│ ║ l.150: id)(1) //│ ╙── ^^ //│ ═══[RUNTIME ERROR] TypeError: baseCall3 is not a function fun test2() = fun funny = (case 0 then 1) funny test2()(0) //│ = 1 :pt module P with print( 2) val x = 1 P.x //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TypeDef: //│ k = Mod //│ head = InfixApp: //│ lhs = Ident of "P" //│ kw = Keywrd of keyword 'with' //│ rhs = Block of Ls of //│ App: //│ lhs = Ident of "print" //│ rhs = Tup of Ls of //│ IntLit of 2 //│ TermDef: //│ k = ImmutVal //│ head = Ident of "x" //│ rhs = S of IntLit of 1 //│ Sel: //│ prefix = Ident of "P" //│ name = Ident of "x" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 //│ = 1 module P with ( ) module TraceLogger module P with ( ) module TraceLogger module P with ... fun checkArgs(expected) = ("Function" + expected) module TraceLogger P.TraceLogger //│ = class TraceLogger ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Inheritance.mls ================================================ :js class Foo with val x = 10 fun foo = x + 1 object Bar extends Foo :todo // recognize fields that are inherited from superclasses [Bar.x, Bar.foo] //│ ╔══[COMPILATION ERROR] Object 'Bar' does not contain member 'x' //│ ║ l.12: [Bar.x, Bar.foo] //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Object 'Bar' does not contain member 'foo' //│ ║ l.12: [Bar.x, Bar.foo] //│ ╙── ^^^^ //│ = [10, 11] let b = id(Bar) in [b.x, b.foo] //│ = [10, 11] Bar is Foo //│ = true Bar is String //│ = false // :pt // :elt // :de data class Bar(y) extends Foo with fun bar = y + 1 let b = Bar(42) //│ b = Bar(42) b is Foo //│ = true b is String //│ = false [b.x, b.foo] //│ = [10, 11] :sjs data class Baz(z) extends Bar(z * 1) with fun baz = this.bar * 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Baz1; //│ Baz1 = function Baz(z) { //│ return globalThis.Object.freeze(new Baz.class(z)); //│ }; //│ (class Baz extends Bar3.class { //│ static { //│ Baz1.class = this //│ } //│ constructor(z) { //│ let tmp; //│ tmp = z * 1; //│ super(tmp); //│ this.z = z; //│ } //│ get baz() { //│ return this.bar * 2; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Baz", ["z"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let b = Baz(42) //│ b = Baz(42) b is Foo //│ = true b is String //│ = false [b.x, b.foo] //│ = [10, 11] abstract class Option[out T]: Some[T] | None object None extends Option[Nothing] data class Some[out T](x: T) extends Option[T] [Some(123) is Option, None is Option, Some(123) is String, None is String] //│ = [true, true, false, false] data class Barr(x: Int)(y: Int) with fun f = x + y :todo data class Bazz(z: Int) extends Bar(z % 10)(z / 10) with fun g = z //│ ╔══[COMPILATION ERROR] Extending a class with multiple parameter lists is not supported //│ ║ l.101: data class Bazz(z: Int) extends Bar(z % 10)(z / 10) with //│ ╙── ^^^^^^^^^^^^^^^^^^^ :todo Bazz(42).f //│ ═══[RUNTIME ERROR] Error: Access to required field 'f' yielded 'undefined' :todo new Barr(40)(2) with fun f = 43 //│ ╔══[COMPILATION ERROR] Extending a class with multiple parameter lists is not supported //│ ║ l.112: new Barr(40)(2) with //│ ╙── ^^^^^^^^^^^ //│ = fun module M with class A // * Check that the access to `M.A` is not sanity-checked, as it is resolved. :sjs class B extends M.A //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let B1; //│ (class B extends M1.A { //│ static { //│ B1 = this //│ } //│ constructor() { //│ super(); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "B"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls ================================================ :js :expect [[1, 2], 3] if [1, ..[2, 3]] is [..a, b] then [a, b] //│ = [[1, 2], 3] :expect [[1, 2], 3] if [1, ...[2, 3]] is [..a, b] then [a, b] //│ = [[1, 2], 3] :expect [[1, 2], 3] if [1, ..[2, 3]] is [..a, b] then [a, b] //│ = [[1, 2], 3] :sjs fun buildPalindrome = case 0 then [] n then [n, ..buildPalindrome(n - 1), n] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let buildPalindrome; //│ buildPalindrome = function buildPalindrome() { //│ let lambda; //│ lambda = (undefined, function (caseScrut) { //│ let n, tmp3, tmp4, tmp5; //│ if (caseScrut === 0) { //│ return globalThis.Object.freeze([]) //│ } //│ n = caseScrut; //│ tmp3 = buildPalindrome(); //│ tmp4 = n - 1; //│ tmp5 = runtime.safeCall(tmp3(tmp4)); //│ return globalThis.Object.freeze(runtime.Tuple.lazyConcat(n, runtime.Tuple.split, tmp5, n)); //│ }); //│ return lambda //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :silent val arr = buildPalindrome(5) :expect [5, 4, 3, 2, 1, 1, 2, 3, 4, 5] arr //│ = [5, 4, 3, 2, 1, 1, 2, 3, 4, 5] :sjs fun sum2 = case [] then 0 [x, ..xs, y] then x + y + sum2(xs) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let sum2; //│ sum2 = function sum2() { //│ let lambda; //│ lambda = (undefined, function (caseScrut) { //│ let x, xs, y, lastElement1$3, middleElements3, element0$, tmp5, tmp6, tmp7; //│ if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length === 0) { //│ return 0 //│ } else if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length >= 2) { //│ element0$ = runtime.Tuple.get(caseScrut, 0); //│ middleElements3 = runtime.Tuple.slice(caseScrut, 1, 1); //│ lastElement1$3 = runtime.Tuple.get(caseScrut, -1); //│ y = lastElement1$3; //│ xs = middleElements3; //│ x = element0$; //│ tmp5 = x + y; //│ tmp6 = sum2(); //│ tmp7 = runtime.safeCall(tmp6(xs)); //│ return tmp5 + tmp7 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ return lambda //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 30 sum2(arr) //│ = 30 :e fun f(..xs) = xs //│ ╔══[COMPILATION ERROR] Lazy spread parameters not allowed. //│ ║ l.81: fun f(..xs) = xs //│ ╙── ^^^^ :ge sum2(..arr) //│ ╔══[COMPILATION ERROR] Lazy spreads are not supported in call arguments //│ ║ l.87: sum2(..arr) //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 2 fun map2(f) = case [] then [] [x, ..xs, y] then [f(x), ..map2(f)(xs), f(y)] :expect [10, 8, 6, 4, 2, 2, 4, 6, 8, 10] map2(x => x * 2)(arr) //│ = [10, 8, 6, 4, 2, 2, 4, 6, 8, 10] ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/LetBindings.mls ================================================ :js let x = 1 //│ x = 1 let x = 1 in x //│ = 1 let x = 1 let y = 2 x + y //│ = 3 //│ x = 1 //│ y = 2 x //│ = 1 let z = 3 x + y + z //│ = 6 //│ z = 3 let x = print("computing x") 123 //│ > computing x //│ x = 123 let x = 0 //│ x = 0 let x += 1 in... x //│ = 1 let x += 1 in... print of x x //│ > 1 //│ = 1 let x += 1 in... print(x) x //│ > 1 //│ = 1 let x += 1 in print(x) x //│ > 1 //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/LiteralSelection.mls ================================================ :js let arr = [1, 2, 3] //│ arr = [1, 2, 3] arr.1 //│ = 2 arr."1" //│ = 2 // Equivalent: arr.'1 //│ = 2 {a: 123}.'a //│ = 123 {'a: 123}.'a //│ = 123 {"a": 123}.'a //│ = 123 let obj = a: 1 //│ obj = {a: 1} obj.a //│ = 1 obj .a //│ = 1 obj .a //│ = 1 :pe obj. a //│ ╔══[PARSE ERROR] Expected end of input; found period instead //│ ║ l.42: obj. a //│ ╙── ^ //│ = {a: 1} obj.("a") //│ = 1 obj .("a") //│ = 1 :fixme obj .("a") //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.56: .("a") //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.56: .("a") //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (‹erroneous syntax›). //│ ║ l.56: .("a") //│ ╙── ^ //│ = {a: 1} :pe obj. ("a") //│ ╔══[PARSE ERROR] Expected end of input; found period instead //│ ║ l.69: obj. ("a") //│ ╙── ^ //│ = {a: 1} obj."a" //│ = 1 obj ."a" //│ = 1 :fixme obj ."a" //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.83: ."a" //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.83: ."a" //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (‹erroneous syntax›). //│ ║ l.83: ."a" //│ ╙── ^ //│ = {a: 1} :pe obj. "a" //│ ╔══[PARSE ERROR] Expected end of input; found period instead //│ ║ l.96: obj. "a" //│ ╙── ^ //│ = {a: 1} let obj = " ": 1 "¯\\_(ツ)_/¯": 2 //│ obj = {" ": 1, "¯_(ツ)_/¯": 2} obj." " //│ = 1 :fixme :expect 2 obj."¯\\_(ツ)_/¯" //│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' :fixme "¯\_(ツ)_/¯" :fixme let obj = "¯\_(ツ)_/¯": 2 //│ ╔══[WARNING] Found invalid escape character //│ ║ l.117: "¯\_(ツ)_/¯" //│ ╙── ^ //│ ╔══[WARNING] Found invalid escape character //│ ║ l.119: let obj = "¯\_(ツ)_/¯": 2 //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: fixme //│ ║ l.118: :fixme //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'obj' //│ ╟── which references the symbol introduced here //│ ║ l.119: let obj = "¯\_(ツ)_/¯": 2 //│ ╙── ^^^ //│ obj = {a: 1} :fixme obj."¯\_(ツ)_/¯" //│ ╔══[WARNING] Found invalid escape character //│ ║ l.136: obj."¯\_(ツ)_/¯" //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'obj' //│ ║ l.136: obj."¯\_(ツ)_/¯" //│ ║ ^^^ //│ ╟── which references the symbol introduced here //│ ║ l.119: let obj = "¯\_(ツ)_/¯": 2 //│ ╙── ^^^ :fixme :expect ¯\\_(ツ)_/¯ "¯\\_(ツ)_/¯" //│ ═══[RUNTIME ERROR] Expected: '¯\\_(ツ)_/¯', got: '"¯\\_(ツ)_/¯"' //│ = "¯\\_(ツ)_/¯" ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Literals.mls ================================================ :js 1 //│ = 1 () (1) //│ = 1 (1,) //│ = 1 :w (1, 2) //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.16: (1, 2) //│ ╙── ^ //│ = 2 :w (1, 2,) //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.23: (1, 2,) //│ ╙── ^ //│ = 2 [] //│ = [] [1] //│ = [1] [1,] //│ = [1] [1, 2] //│ = [1, 2] [1, 2,] //│ = [1, 2] null //│ = null undefined 0.5 //│ = 0.5 :todo NaN //│ ╔══[COMPILATION ERROR] Name not found: NaN //│ ║ l.55: NaN //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. "" //│ = "" "AAA" //│ = "AAA" 'AAA //│ = "AAA" 'AAA' //│ = "AAA'" :e 'AA BB' //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). //│ ║ l.75: 'AA BB' //│ ╙── ^^^ //│ = "AA" "\u0068\u0065\u006c\u006c\u006f" //│ = "hello" "\u{a}" //│ = "\n" "\ud83d\ude00" //│ = "😀" "\u{1F600}" //│ = "😀" "\u{1F600}" == "\ud83d\ude00" //│ = true :w "\uFE&&" //│ ╔══[WARNING] Invalid Unicode escape sequence: expected 4 hexadecimal digits but got 2 //│ ║ l.98: "\uFE&&" //│ ╙── ^^ //│ = "&&" :w "\u{}" //│ ╔══[WARNING] Expected at least one hexadecimal digit in Unicode escape sequence //│ ║ l.105: "\u{}" //│ ╙── ^ //│ = "" :w "\u{ffffffff}" //│ ╔══[WARNING] Too many hexadecimal digits in Unicode escape sequence //│ ║ l.112: "\u{ffffffff}" //│ ╙── ^^ //│ = "" :w "\u{efefef}" //│ ╔══[WARNING] Unicode code point out of range: 0xefefef //│ ║ l.119: "\u{efefef}" //│ ╙── ^^^^^^ //│ = "" :w "\u{55" //│ ╔══[WARNING] Unterminated Unicode escape sequence: missing '}' //│ ║ l.126: "\u{55" //│ ╙── ^ //│ = "U" :w "\u{41****" //│ ╔══[WARNING] Unterminated Unicode escape sequence: missing '}' //│ ║ l.133: "\u{41****" //│ ╙── ^ //│ = "A****" print of fold(+) of "\u{250c}\u{2500}\u{252c}\u{2510}" "\u{2554}\u{2550}\u{2566}\u{2557}" "\u{2553}\u{2500}\u{2565}\u{2556}" "\u{2552}\u{2550}\u{2564}\u{2555}" "\u000A" "\u{2502}\u{20}\u{2502}\u{2502}" "\u{2551}\u{20}\u{2551}\u{2551}" "\u{2551}\u{20}\u{2551}\u{2551}" "\u{2502}\u{20}\u{2502}\u{2502}" "\u000A" "\u{251c}\u{2500}\u{253c}\u{2524}" "\u{2560}\u{2550}\u{256c}\u{2563}" "\u{255f}\u{2500}\u{256b}\u{2562}" "\u{255e}\u{2550}\u{256a}\u{2561}" "\u000A" "\u{2514}\u{2500}\u{2534}\u{2518}" "\u{255a}\u{2550}\u{2569}\u{255d}" "\u{2559}\u{2500}\u{2568}\u{255c}" "\u{2558}\u{2550}\u{2567}\u{255b}" //│ > ┌─┬┐╔═╦╗╓─╥╖╒═╤╕ //│ > │ ││║ ║║║ ║║│ ││ //│ > ├─┼┤╠═╬╣╟─╫╢╞═╪╡ //│ > └─┴┘╚═╩╝╙─╨╜╘═╧╛ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/LocalVal.mls ================================================ :js fun foo() = val x = 1 x + 1 foo() //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls ================================================ :js module M with data class Foo(x) with fun m() = this.x + 1 fun n(y) = this.x + y let foo = M.Foo(123) //│ foo = Foo(123) foo.m() //│ = 124 foo.n(1) //│ = 124 foo M.Foo::m() //│ = 124 foo M.Foo::n(2) //│ = 125 M.Foo::n(foo, 2) //│ = 125 M.Foo ::n(foo, 2) //│ = 125 M.Foo ::n (foo, 2) //│ = 125 :e M.Foo:: n(foo, 2) //│ ╔══[COMPILATION ERROR] Name not found: :: //│ ║ l.37: M.Foo:: n(foo, 2) //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Name not found: n //│ ║ l.37: M.Foo:: n(foo, 2) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs let m = M.Foo::m //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let m, m1; m1 = function m(self5, ...args5) { return self5.m(...args5) }; m = m1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ m = fun m m(foo) //│ = 124 foo m() //│ = 124 M.{foo Foo::m()} //│ = 124 M.{foo.Foo#m()} //│ = 124 do open M print of foo M.Foo::m() //│ > 124 do open M print of foo.Foo#m() //│ > 124 // * This one still succeeds at runtime because the JS backend discards the class info :e foo.Foo#m() //│ ╔══[COMPILATION ERROR] Name not found: Foo //│ ║ l.79: foo.Foo#m() //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Identifier `Foo` does not name a known class symbol. //│ ║ l.79: foo.Foo#m() //│ ╙── ^^^^ //│ ═══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ = 124 :e :re foo.M.Foo::m() //│ ╔══[COMPILATION ERROR] Selection is not a known class. //│ ║ l.91: foo.M.Foo::m() //│ ║ ^^^^^^^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 :e :re foo.M.Foo#m() //│ ╔══[COMPILATION ERROR] Name not found: Foo //│ ║ l.101: foo.M.Foo#m() //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Identifier `Foo` does not name a known class symbol. //│ ║ l.101: foo.M.Foo#m() //│ ╙── ^^^^ //│ ═══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ ═══[RUNTIME ERROR] Error: Access to required field 'M' yielded 'undefined' :e Foo::m //│ ╔══[COMPILATION ERROR] Name not found: Foo //│ ║ l.112: Foo::m //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Identifier is not a known class. //│ ║ l.112: Foo::m //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ═══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ = fun :e :sjs :noInline Foo::n(foo, 2) //│ ╔══[COMPILATION ERROR] Name not found: Foo //│ ║ l.127: Foo::n(foo, 2) //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Identifier is not a known class. //│ ║ l.127: Foo::n(foo, 2) //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ ═══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda9; //│ lambda9 = (undefined, function (self7, ...args7) { //│ return runtime.safeCall(self7.n(...args7)) //│ }); //│ runtime.safeCall(lambda9(foo, 2)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 125 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls ================================================ :js let xs = [0, 1, 2] //│ xs = [0, 1, 2] xs \print() //│ > [0, 1, 2] :re xs.reverse() //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property '0' of object '[object Array]' xs //│ = [0, 1, 2] let xs = mut [...xs] //│ xs = [0, 1, 2] let f = xs \print //│ f = fun f() //│ > [0, 1, 2] xs.reverse() //│ = [2, 1, 0] :re xs.map(x => x * 2) //│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 xs.map((x, i, a) => x * 2) //│ = [4, 2, 0] xs.map((x, ...) => x * 2) //│ = [4, 2, 0] xs.map((x, ..._) => x * 2) //│ = [4, 2, 0] fun map(xs, f) = xs.map((x, ...bs) => f(x)) xs \map(x => x * 2) \print() //│ > [4, 2, 0] // * Note: the RHS of `\` is a self-contained block, thus implicitly parenthesized. :sjs :e :re xs \ map(x => x * 2) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.52: map(x => x * 2) //│ ╙── ^^^^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda5, tmp1; //│ lambda5 = (undefined, function (x) { return x * 2 }); //│ tmp1 = map(lambda5); //│ Predef.passTo(xs1, tmp1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1 :e :re xs\ map(x => x * 2) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.67: map(x => x * 2) //│ ╙── ^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 'map' expected 2 arguments but got 1 :pt xs \ map(f) //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ App: //│ lhs = OpApp: //│ lhs = Ident of "xs" //│ op = Ident of "\\" //│ rhss = Ls of //│ Ident of "map" //│ rhs = Tup of Ls of //│ Ident of "f" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > [2, 1, 0] 2 //│ > [2, 1, 0] 1 //│ > [2, 1, 0] 0 //│ = [(), (), ()] xs \map(x => x * 2) //│ = [4, 2, 0] xs \map(x => x * 2) \print() //│ > [4, 2, 0] fun f = print(0) f //│ > 0 f //│ > 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ModuleMethods.mls ================================================ module M module MM[T] // Parameters :e fun f(m: M) //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. //│ ║ l.9: fun f(m: M) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference of type M denoting module 'M'. //│ ║ l.9: fun f(m: M) //│ ║ ^ //│ ╙── Non-module parameter must have a non-module type. fun f(m) :e f(M) //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.21: f(M) //│ ║ ^ //│ ╙── Module argument passed to a non-module parameter. f(42) :e fun f(module m) //│ ╔══[COMPILATION ERROR] Module parameter must have explicit type. //│ ║ l.30: fun f(module m) //│ ╙── ^ fun f(module m: M) f(42) f(M) :e fun f[T](module m: T) //│ ╔══[COMPILATION ERROR] Expected a module type; found reference. //│ ║ l.42: fun f[T](module m: T) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Expected a module type; found reference. //│ ║ l.42: fun f[T](module m: T) //│ ║ ^ //│ ╙── Module parameter must have a module type. fun f[T](module m: MM[T]) f[Int](42) f[Int](MM[Int]) // Results fun assertM(module m: M) fun assertNonM(m) fun assertMM[T](module m: MM[T]) fun assertNonMM[T](m) :e fun f() = M //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.68: fun f() = M //│ ║ ^ //│ ╙── Function must be marked as returning a 'module' in order to return a module. :e fun f(): M = M //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference of type M denoting module 'M'. //│ ║ l.75: fun f(): M = M //│ ║ ^ //│ ╙── Function must be marked as returning a 'module' in order to have a module return type. //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.75: fun f(): M = M //│ ║ ^ //│ ╙── Function must be marked as returning a 'module' in order to return a module. fun f(): module M = M :e assertNonM(f()) //│ ╔══[COMPILATION ERROR] Unexpected moduleful application of type M. //│ ║ l.88: assertNonM(f()) //│ ║ ^^^ //│ ╙── Module argument passed to a non-module parameter. assertM(f()) :e fun f4[T](): module T = M //│ ╔══[COMPILATION ERROR] Expected a module type; found reference. //│ ║ l.97: fun f4[T](): module T = M //│ ║ ^ //│ ╙── Function marked as returning a 'module' must have a module return type. fun f[T](): module MM[T] = MM[T] :e assertNonM(f[Int]()) //│ ╔══[COMPILATION ERROR] Unexpected moduleful application of type MM[T]. //│ ║ l.106: assertNonM(f[Int]()) //│ ║ ^^^^^^^^ //│ ╙── Module argument passed to a non-module parameter. assertM(f[Int]()) :todo :e fun f3() = return M //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.117: fun f3() = return M //│ ╙── ^ :todo :e fun f3() = (() => M)() //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.124: fun f3() = (() => M)() //│ ╙── ^ // * [test:T4] :todo :e :effectHandlers :lift fun f3() = (() => return M)() //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.134: fun f3() = (() => return M)() //│ ╙── ^ // Parameters Modulefulness :e fun f(module m: M) = m //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.144: fun f(module m: M) = m //│ ║ ^ //│ ╙── Function must be marked as returning a 'module' in order to return a module. fun f(module m: M): module M = m // Val Bindings :e val v = M //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.157: val v = M //│ ║ ^ //│ ╙── Value must be marked as returning a 'module' in order to return a module. val v: module M = M // *Module* Methods module N with fun foo(): module M = M fun bar(module m: M): module M = foo() N.bar(M) // Methods / Fields :e class C with fun foo: module M = M val bar: module M = M //│ ╔══[COMPILATION ERROR] Function returning modules should not be a class member. //│ ║ l.179: fun foo: module M = M //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Value returning modules should not be a class member. //│ ║ l.180: val bar: module M = M //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Modules.mls ================================================ // :flow module Foo :pe module Foo with //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.7: module Foo with //│ ╙── ^ :e module Foo with val x = 1 //│ ╔══[COMPILATION ERROR] Illegal body of module definition (should be a block; found term definition). //│ ║ l.13: module Foo with val x = 1 //│ ╙── ^^^^^^^^^ :e Foo.x //│ ╔══[COMPILATION ERROR] Module 'Foo' does not contain member 'x' //│ ║ l.19: Foo.x //│ ╙── ^^ module Foo with val x = 1 Foo Foo.x ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls ================================================ :js data class Foo(x, y)(z) Foo //│ = fun Foo { class: class Foo } Foo(1, 2) //│ = fun Foo(1, 2)(3) //│ = Foo(1, 2) :re :sir Foo(1, 2)(3)(4) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let baseCall; set baseCall = Foo⁰(1, 2)(3); baseCall(4) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: baseCall is not a function new Foo(1, 2) //│ = fun new Foo(1, 2)(3) //│ = Foo(1, 2) :re :sir new Foo(1, 2)(3)(4) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let baseInst; set baseInst = new Foo¹(1, 2)(3); baseInst(4) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: baseInst is not a function :sir new Foo(1, 2)(3) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ new Foo¹(1, 2)(3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo(1, 2) :sir new Foo(1, 2) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁰; define lambda⁰ as fun lambda¹(z) { return new Foo¹(1, 2)(z) }; lambda¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :todo data class Foo with constructor(x, y) //│ ╔══[PARSE ERROR] Expected block after constructor keyword; found parenthesis section instead //│ ║ l.56: constructor(x, y) //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.56: constructor(x, y) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.56: constructor(x, y) //│ ╙── ^ :todo data class Foo with constructor(x, y)(z) //│ ╔══[PARSE ERROR] Expected block after constructor keyword; found parenthesis section instead //│ ║ l.69: constructor(x, y)(z) //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.69: constructor(x, y)(z) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.69: constructor(x, y)(z) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: z //│ ║ l.69: constructor(x, y)(z) //│ ╙── ^ // class Foo(using z: Int) // * is to be understood as class Foo0 fun Foo(using z: Int) = new mut Foo0 !> res => set res.z = z using Int = 123 Foo //│ = Foo0 { z: 123 } // class Foo(x, y)(using z) // * is to be understood as class Foo0 fun Foo(x, y)(using z: Int) = new mut Foo0 !> res => set res.x = x res.y = y res.z = z Foo(1, 2) //│ = Foo0 { x: 1, y: 2, z: 123 } :sjs let f = Foo // should compile to `(x, y) => Foo(x, y)(use[Int])` //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f, f1; f1 = function f(x, y) { return Foo7(x, y)(instance$Ident$_Int$_) }; f = f1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f // Eta-expansion happens here. See EtaExpansion.mls. f(1, 2) //│ = Foo0 { x: 1, y: 2, z: 123 } ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls ================================================ :js :global :sjs fun f(n1: Int): Int = n1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; f = function f(n1) { return n1 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(42) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f(42) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // TODO compile this to // function f(n1) { return (n2) => f$(n1, n2); } // function f$(n1, n2) { let tmp; tmp = 10 * n1; return tmp + n2; } fun f(n1: Int)(n2: Int): Int = (10 * n1 + n2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; f1 = function f(n1) { return (n2) => { let tmp; tmp = 10 * n1; return tmp + n2 } }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // TODO compile this to // this.f$(4, 2) f(4)(2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f1(4)(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 fun f(n1: Int)(n2: Int)(n3: Int): Int = 10 * (10 * n1 + n2) + n3 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2; //│ f2 = function f(n1) { //│ return (n2) => { //│ return (n3) => { //│ let tmp, tmp1, tmp2; //│ tmp = 10 * n1; //│ tmp1 = tmp + n2; //│ tmp2 = 10 * tmp1; //│ return tmp2 + n3 //│ } //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(4)(2)(0) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f2(4)(2)(0) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 420 fun f(n1: Int)(n2: Int)(n3: Int)(n4: Int): Int = 10 * (10 * (10 * n1 + n2) + n3) + n4 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f3; //│ f3 = function f(n1) { //│ return (n2) => { //│ return (n3) => { //│ return (n4) => { //│ let tmp, tmp1, tmp2, tmp3, tmp4; //│ tmp = 10 * n1; //│ tmp1 = tmp + n2; //│ tmp2 = 10 * tmp1; //│ tmp3 = tmp2 + n3; //│ tmp4 = 10 * tmp3; //│ return tmp4 + n4 //│ } //│ } //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(3)(0)(3)(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f3(3)(0)(3)(1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3031 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MultilineExpressions.mls ================================================ :js 2 * 2 + 1 //│ = 5 2 * 2 + 1 //│ = 6 2 * 2 + 1 //│ = 6 fun (**) f1(a, b) = "[" + a + " * " + b + "]" fun (++) f2(a, b) = "(" + a + " + " + b + ")" 1 ** 2 ++ 3 //│ = "([1 * 2] + 3)" 1 ** 2 ++ 3 //│ = "([1 * 2] + 3)" 1 ** 2 ++ 3 ++ 4 //│ = "(([1 * 2] + 3) + 4)" 1 ++ 2 ** 3 ** 4 //│ = "(1 + [[2 * 3] * 4])" 1 ++ 2 ** 3 ** 4 //│ = "(1 + [[2 * 3] * 4])" // TODO warn or error on such operator blocks 1 ++ 2 ** 3 ++ 4 //│ = "((1 + [2 * 3]) + 4)" 1 ++ 2 ** 3 ++ 4 //│ = "((1 + [2 * 3]) + 4)" 1 ++ 2 ** 3 ** 4 //│ = "(1 + [[2 * 3] * 4])" // FIXME problematic elaboration? or not? :pt // :elt :sjs :re if 1 + 2 * 3 then 0 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "+" //│ rhss = Ls of //│ OpApp: //│ lhs = IntLit of 2 //│ op = Ident of "*" //│ rhss = Ls of //│ IntLit of 3 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut, tmp17; //│ tmp17 = 2 * 3; //│ scrut = 1 + tmp17; //│ if (scrut === true) { //│ 0 //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error :pt :re if 1 * 2 + 3 then 0 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = OpApp: //│ lhs = OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "*" //│ rhss = Ls of //│ IntLit of 2 //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 3 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 0 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error // TODO perhaps we should just require parenthesizing (1 + 2) here :pe :e if 1 + 2 * 3 then 0 + 4 then 1 //│ ╔══[PARSE ERROR] Operator cannot be used inside this operator split //│ ║ l.119: + 4 then 1 //│ ║ ^ //│ ╟── as it has lower precedence than the splitting operator here //│ ║ l.118: * 3 then 0 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in this operator split inner position //│ ║ l.119: + 4 then 1 //│ ║ ^^^^ //│ ╟── Note: the operator split starts here //│ ║ l.118: * 3 then 0 //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.117: if 1 + 2 //│ ║ ^ //│ ║ l.118: * 3 then 0 //│ ║ ^^^^^^^^^^^^ //│ ║ l.119: + 4 then 1 //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pt :re if (1 + 2) * 3 then 0 + 4 then 1 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = OpSplit: //│ lhs = Bra: //│ k = Round //│ inner = OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 2 //│ ops_rhss = Ls of //│ InfixApp: //│ lhs = OpApp: //│ lhs = SplitPoint //│ op = Ident of "*" //│ rhss = Ls of //│ IntLit of 3 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 0 //│ InfixApp: //│ lhs = OpApp: //│ lhs = SplitPoint //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 4 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error if (1 + 2) * 3 === 8 then 0 + 4 === 7 then 1 //│ = 1 if 1 + 0 == 0 then "X" == 1 then "A" //│ = "A" if (1 + 1) * 2 == 4 then "X" * 2 == 1 then "A" |> id is r then r //│ = "X" 1 ++ 2 //│ = "(1 + 2)" // TODO reject – hard to read; should be a postfix op // But then what about versions with many indented lines? -> require putting the op at line starts 1 ++ 2 //│ = "(1 + 2)" // * Note that the argument to the first `++` is a self-contained block, thus implicitly parenthesized. 1 ++ 2 ++ 3 //│ = "(1 + (2 + 3))" 1 ++ 2 ++ 3 //│ = "((1 + 2) + 3)" 1 ++ 2 ** 3 //│ = "(1 + [2 * 3])" // * Note that the argument to `**` is a self-contained block, thus implicitly parenthesized. 1 ** 2 ++ 3 //│ = "[1 * (2 + 3)]" 1 ++ 2 ++ 3 ++ 4 //│ = "(1 + ((2 + 3) + 4))" ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MutArr.mls ================================================ :js :sjs let t = [0, 1, 2] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let t; t = globalThis.Object.freeze([ 0, 1, 2 ]); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ t = [0, 1, 2] :re t.push(4) //│ ═══[RUNTIME ERROR] TypeError: Cannot add property 3, object is not extensible // Has no effect, as t is frozen set t.[1] = 5 t //│ = [0, 1, 2] :sjs let t = mut [0, 1, 2] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let t1; t1 = [ 0, 1, 2 ]; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ t = [0, 1, 2] t.push(4) //│ = 4 set t.[1] = 5 t //│ = [0, 5, 2, 4] ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MutCls.mls ================================================ :js class C(val x) let c = C(1) //│ c = C(1) set c.x = 2 c //│ = C(1) class Foo(val x)(val y) let f = Foo(1)(2) //│ f = Foo(1) f.y //│ = 2 set f.y = 3 f.y //│ = 2 class Foo with fun foo() = 1 fun bar = 2 let f = new mut Foo //│ f = Foo set f.foo = () => 3 f.foo() //│ = 3 set f.bar = 3 f.bar //│ = 2 let f = new Foo //│ f = Foo set f.foo = () => 3 f.foo() //│ = 1 set f.bar = 3 f.bar //│ = 2 // * TODO: later, we might want to distinguish freezing from sealing; // * `new` would freeze and `new mut` would solely seal; `new open` would do neither. // let o = new open Object ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MutRcd.mls ================================================ :js let r = {a: 0} //│ r = {a: 0} set r.a = 1 r.foo = 1 :expect 0 r.a //│ = 0 :re r.foo //│ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' let r = mut {a: 1} //│ r = {a: 1} set r.a = 1 r.foo = 1 :expect 1 r.a //│ = 1 :expect 1 r.foo //│ = 1 let r = mut {} //│ r = {} set r.foo = 1 :expect 1 r.foo //│ = 1 :sjs let r = {} //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let r3; r3 = runtime.Unit; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ r = () :re r.x //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' set r.foo = 1 :re r.foo //│ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' :re ().foo //│ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' let s = {} //│ s = () set s.foo = 2 :re r.foo //│ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' :e mut() //│ ╔══[COMPILATION ERROR] Illegal position for 'mut' modifier. //│ ║ l.78: mut() //│ ╙── ^^^ let r = mut x: 1 //│ r = {x: 1} Object.freeze(r) //│ = {x: 1} set r.x = 2 :expect 1 r.x //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/MutVal.mls ================================================ :js mut val cached: Int = 1 //│ cached = 1 :sjs set cached = 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ cached = 2; runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— cached //│ = 2 object O with val x = 1 let y = 1 fun gy = y fun sy = set y += 1 set O.x = 2 O.x //│ = 1 O.sy O.gy //│ = 2 :soir class Foo(x, val y, mut val z) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let Foo⁰; //│ define Foo⁰ as class Foo²(x, y, z) { //│ private val x⁰; //│ val y⁰; //│ val z⁰; //│ constructor Foo¹ { define y⁰ as val y¹ = y; define z⁰ as val z¹ = z; end } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :soir class Foo(x, val y, mut val z) with print(x) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let Foo³; //│ define Foo³ as class Foo⁵(x, y, z) { //│ private val x¹; //│ val y²; //│ val z²; //│ constructor Foo⁴ { //│ set x¹ = x; //│ define y² as val y³ = y; //│ define z² as val z³ = z; //│ do Predef⁰.print⁰(x¹); //│ end //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let f = Foo(1, 2, 3) //│ > 1 //│ f = Foo(_, 2, 3) f.z //│ = 3 set f.z = 0 f.z //│ = 0 data class Foo(mut val x) let f = Foo(1) //│ f = Foo(1) set f.x = 2 f.x //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/NamedArgs.mls ================================================ :js // * Named tuple [a: 0] //│ = [{a: 0}] [1, 2, a: "a", 3, b: "b", 4, 5] //│ = [1, 2, 3, 4, 5, {a: "a", b: "b"}] let r = [4, 5, c: "c"] //│ r = [4, 5, {c: "c"}] [1, 2, a: "a", 3, b: "b", ...r] //│ = [1, 2, 3, 4, 5, {c: "c"}, {a: "a", b: "b"}] let aa = 123 //│ aa = 123 [aa: 0] //│ = [{aa: 0}] [aa: aa] //│ = [{aa: 123}] // * Named arguments // :de :elt id(a: 0) //│ —————————————| Elaborated tree |———————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App: //│ lhs = SynthSel{sym=member:id⁰}: //│ prefix = Ref{sym=member:Predef⁰} of member:Predef⁰ //│ nme = Ident of "id" //│ rhs = Tup of Ls of //│ Fld: //│ term = Lit of StrLit of "a" //│ asc = S of Lit of IntLit of 0 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = {a: 0} id of a: 0 //│ = {a: 0} print of foo: 1 bar: "..." //│ > {foo: 1, bar: "..."} print of "Hello" foo: 1 bar: "..." "World" //│ > Hello World {foo: 1, bar: "..."} ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/NestedBlocks.mls ================================================ :js // * Checking that a `val` in a nested block is not treated as a member of the enclosing object object Foo with if true then val x = 0 :e :re Foo.x //│ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x' //│ ║ l.12: Foo.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' module Oops with if true then fun f = 1 () // * We used to forget to reset the owner to None in sub-blocks like this one :e :re object Oops with if true then val fakeField = 1 print(fakeField) print(this.fakeField) // shouldn't work! //│ ╔══[COMPILATION ERROR] Object 'Oops' does not contain member 'fakeField' //│ ║ l.31: print(this.fakeField) // shouldn't work! //│ ╙── ^^^^^^^^^^^^^^ //│ > 1 //│ ═══[RUNTIME ERROR] Error: Access to required field 'fakeField' yielded 'undefined' :e :re Oops.fakeField //│ ╔══[COMPILATION ERROR] Object 'Oops' does not contain member 'fakeField' //│ ║ l.40: Oops.fakeField //│ ╙── ^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'fakeField' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/New.mls ================================================ :js class Foo new Foo //│ = Foo new Foo() //│ = Foo new Foo //│ = Foo :e :w new Foo Foo //│ ╔══[COMPILATION ERROR] Expected a statically known class; found block. //│ ║ l.20: Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.19: Foo //│ ╙── ^^^ //│ = Foo // * Interesting... it tries to call `Foo()` and instantiate the resulting class value... :e :re new Foo() //│ ╔══[COMPILATION ERROR] Expected a statically known class; found application. //│ ║ l.34: Foo() //│ ║ ^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: Class constructor Foo cannot be invoked without 'new' :e new Foo () //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (unit). //│ ║ l.44: () //│ ╙── ^^ //│ = Foo :e :w :re new Foo () //│ ╔══[COMPILATION ERROR] Expected a statically known class; found block. //│ ║ l.55: () //│ ║ ^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.54: Foo //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor :e new Foo () //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (unit). //│ ║ l.67: () //│ ╙── ^^ //│ = Foo let xs = [1,2,3] //│ xs = [1, 2, 3] data class Foo(a,b,c) new Foo(...xs) //│ = Foo(1, 2, 3) new Foo(...[1, 2]) //│ = Foo(1, 2, undefined) new Foo(...[1, 2, 3, 4]) //│ = Foo(1, 2, 3) ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/NewMut.mls ================================================ :js class Foo with val x = 0 fun change = set x += 1 :sjs let f = new Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; f = globalThis.Object.freeze(new Foo1()); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = Foo { x: 0 } set f.x = 1 f.y = 1 :expect 0 f.x //│ = 0 :re f.change //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'x' of object '[object Object]' :expect 0 f.x //│ = 0 :re f.y //│ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' :sjs // :elt let f = new mut Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; f1 = new Foo1(); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = Foo { x: 0 } set f.x = 1 f.y = 1 f.change :expect 2 f.x //│ = 2 :expect 1 f.y //│ = 1 :e let f = mut new Foo //│ ╔══[COMPILATION ERROR] Illegal position for 'mut' modifier. //│ ║ l.62: let f = mut new Foo //│ ╙── ^^^ //│ f = Foo { x: 0 } :e :re new mut Foo.x //│ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. //│ ║ l.70: new mut Foo.x //│ ║ ^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' let f = new mut Foo() //│ f = Foo { x: 0 } set f.x = 1 f.y = 1 f.change :expect 2 f.x //│ = 2 :expect 1 f.y //│ = 1 new mut Foo().x //│ = 0 new Foo().x //│ = 0 class Foo(val x) let f = new Foo(0) //│ f = Foo(0) set f.x = 1 :expect 0 f.x //│ = 0 let f = new mut Foo(0) //│ f = Foo(0) set f.x = 1 :expect 1 f.x //│ = 1 let f = Foo(0) //│ f = Foo(0) set f.x = 1 f.y = 1 :expect 0 f.x //│ = 0 :re f.y //│ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' class Foo(mut val x) with mut val y = 0 let f = new Foo(0) //│ f = Foo(0) set f.x = 1 f.y = 1 // * Note that `mut val`s are backed by an internally mutable private field. :expect 1 f.x //│ = 1 :expect 1 f.y //│ = 1 let f = new Object with val x = 12 //│ f = $anon { x: 12 } set f.x = 1 f.y = 1 f.x //│ = 12 :re f.y //│ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' let f = new mut Object with val x = 12 //│ f = $anon { x: 12 } set f.x = 1 f.y = 1 f.x //│ = 1 f.y //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/NewlineOperatorEnd.mls ================================================ :js // * Operators like ??? on a new line at end of block should be standalone expressions, not newline-infix fun parse(tokens) = () ??? fun parse(tokens) = () ??? // oops ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/NewlineOps.mls ================================================ :js // * Operator continuations on a new line include the whole previous expression as LHS operand :p 2 + 2 //│ ┊2┊↵┊+┊↵┊2┊ //│ Parsed: //│ OpApp(IntLit(2),Ident(+),List(IntLit(2))) //│ = 4 2 + 2 * 2 //│ = 8 2 + 2 * 2 //│ = 8 // * Notice the difference 2 + 2 * 2 //│ = 6 // * Generally, operators on the *same indentation level* are sequenced – applied in order 2 + 2 * 2 //│ = 8 // * Somehow this also parses... 2 + 2 * 2 //│ = 6 2 + 2 \print() //│ > 4 2 + 2 \print() //│ > 2 //│ = "2()" let xs = [1, 2, 3] let ys = [4, 5, 6] fun len(xs) = xs.length //│ xs = [1, 2, 3] //│ ys = [4, 5, 6] xs |> len + xs |> len |> print //│ > 6 :w :pt let r = 10 in r -1 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ LetLike: //│ kw = Keywrd of keyword 'let' //│ lhs = Ident of "r" //│ rhs = S of IntLit of 10 //│ body = S of Ident of "r" //│ App: //│ lhs = Ident of "-" //│ rhs = Tup of Ls of //│ IntLit of 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.74: let r = 10 in r //│ ╙── ^ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = -1 let r = 10 in id(r) -1 //│ = -1 let r = 10 -1 //│ = -1 //│ r = 10 let r = id(id)(10) -1 //│ = -1 //│ r = 10 :pt let r = id(10) !(r) //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ LetLike: //│ kw = Keywrd of keyword 'let' //│ lhs = Ident of "r" //│ rhs = S of App: //│ lhs = Ident of "id" //│ rhs = Tup of Ls of //│ IntLit of 10 //│ App: //│ lhs = Ident of "!" //│ rhs = Tup of Ls of //│ Bra: //│ k = Round //│ inner = Ident of "r" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ r = 10 // fun solution(bd, c, n) = // if true then // let a = 1 // a // else ??? if 0 === 1 then 2 else 3 //│ = 3 let x = 1 //│ x = 1 let x = 1 //│ x = 1 let x = 1 //│ x = 1 2 + 1 * 2 //│ = 6 2 |> (a: _) .a //│ = 2 :re 2 |> (a: _) .a //│ ═══[RUNTIME ERROR] Error: Access to required field 'a' yielded 'undefined' :ge :re 2 |> (a: _) .a //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (leading dot selection) //│ ║ l.173: .a //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // :dp 2 * 1 + 2 //│ = 4 // :dp 2 * 1 + 2 //│ = 4 2 * 1 + 2 + 2 //│ = 6 2 * 1 + 2 + 2 //│ = 6 // :dp 2 {+ 1} * 2 //│ = 4 {2 + 1} * 2 //│ = 6 {2 { + 1 }} * 2 //│ = 6 2 { * 1 { + 2 } } //│ = 4 // :dp 2 + 1 * 2 //│ = 4 2 + 1 * 2 * 2 //│ = 6 2 + 1 * 2 * 2 //│ = 8 2 + 1 |> print //│ > 3 2 + 1 + 1 |> print //│ > 4 2 + 1 |> print //│ > 3 2 + 1 + 1 |> print //│ > 4 2 + 1 |> print //│ > 3 let incr = _ + 1 //│ incr = fun incr // :dp 2 + 1 + 1 |> print //│ > 4 2 + 1 + 1 |> incr |> print //│ > 5 2 + 1 + 1 |> incr |> print //│ > 5 2 + 1 + 1 |> incr |> print //│ > 5 // Uh... 2 + 1 !> print + 1 |> incr !> print |> print //│ > 3 //│ > 5 //│ > 5 {1} + {1} //│ = 2 {111} + {222} + {333} //│ = 666 2 * 3 //│ = 6 :pe :w 2, * 3 //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.319: 2, * 3 //│ ╙── ^ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.319: 2, * 3 //│ ╙── ^ //│ = fun 2,* 3 //│ = 6 2 ,* 3 //│ = 6 2 + 2 * 3 //│ = 12 2 + 2 ,* 3 //│ = 12 2 + 2 ,* 2 + 2 //│ = 10 2 + 2 ,*, 2 + 2 //│ = 16 module Arr with val len = _.length let xs = [1, 2] ys = [3, 4, 5] //│ xs = [1, 2] //│ ys = [3, 4, 5] xs |> Arr.len + ys |> Arr.len //│ = 5 xs |> Arr.len,+ ys |> Arr.len //│ = 5 xs |> Arr.len +, ys |> Arr.len //│ = 5 xs |> Arr.len ,+, ys |> Arr.len //│ = 5 :pe :re xs |> Arr.len ,+ ys |> Arr.len //│ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead //│ ║ l.375: ,+ //│ ║ ^ //│ ║ l.376: ys |> Arr.len //│ ╙── //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. xs |> Arr.len ,+ 1 //│ = 1 :pe :re xs |> Arr.len +, ys |> Arr.len //│ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead //│ ║ l.391: +, //│ ║ ^ //│ ║ l.392: ys |> Arr.len //│ ╙── //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. xs |> Arr.len +, ys |> Arr.len //│ = 5 // :pe xs |> Arr.len is 2 ,+, ys |> Arr.len //│ = 4 xs |> Arr.len is 2 ,+, ys |> Arr.len is 3 //│ = 2 // :pe xs |> Arr.len is 2 ,+ ys |> Arr.len //│ = 4 class Foo(x) new Foo(1) is Foo //│ = true :fixme // ? new Foo(1) ,is Foo //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'is' keyword instead //│ ║ l.425: new Foo(1) ,is Foo //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead //│ ║ l.425: new Foo(1) ,is Foo //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme // ? new Foo(1) is Foo //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'is' keyword instead //│ ║ l.436: is Foo //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead //│ ║ l.436: is Foo //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/NewlineSels.mls ================================================ :js let rcd = a: b: c: 1 //│ rcd = {a: {b: {c: 1}}} rcd.a.b.c //│ = 1 rcd .a //│ = {b: {c: 1}} rcd .a .b //│ = {c: 1} rcd .a.b //│ = {c: 1} rcd .a .b .c //│ = 1 rcd .a.b.c //│ = 1 rcd .a.b .c //│ = 1 :todo rcd .a //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.39: rcd //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (leading dot selection) //│ ║ l.40: .a //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let rcd = a: x => b: y => c: x + y //│ rcd = {a: fun a} rcd.a(1).b(2).c //│ = 3 rcd .a(1) .b(2) .c //│ = 3 rcd .a(1).b(2) .c //│ = 3 rcd .a(1) .b(2).c //│ = 3 rcd .a(1) .b(2) .c + 1 //│ = 4 rcd .a(1) !> print .b(2) !> print .c !> print + 1 !> print + 1 //│ > {b: fun b} //│ > {c: 3} //│ > 3 //│ > 4 //│ = 5 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ObjectExtensions.mls ================================================ :js object Foo :fixme // Support? new Foo //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type Foo. //│ ║ l.7: new Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: Foo1 is not a constructor :re new! Foo //│ ═══[RUNTIME ERROR] TypeError: Foo1 is not a constructor :fixme // Support? new! Foo.class //│ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'class' //│ ║ l.19: new! Foo.class //│ ╙── ^^^^^^ //│ = Foo new! Foo!class //│ = Foo :fixme // Support? object Bar extends Foo //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type Foo. //│ ║ l.29: object Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null :e :re object Bar extends Foo!class //│ ╔══[COMPILATION ERROR] Expected a statically known class; found dynamic selection. //│ ║ l.38: object Bar extends Foo!class //│ ║ ^^^^^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: Cannot redefine property: class :fixme // Support? object Bar extends! Foo!class //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. //│ ║ l.46: object Bar extends! Foo!class //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/OfLambdaArgs.mls ================================================ :js fun id(x) = x fun foo(f, g) = f(g(1)) foo(id(x => 1), id(y => 2)) //│ = 1 foo of (id of x => 1), id of y => 2 //│ = 1 :e :re foo(id of x => 1, id of y => 2) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.16: foo(id of x => 1, id of y => 2) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expected 1 arguments, got 2 //│ ║ l.16: foo(id of x => 1, id of y => 2) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 'id' expected 1 argument but got 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls ================================================ :js 2 + 2 //│ = 4 2 + 2 + 2 //│ = 6 2 + 1 * 2 //│ = 6 2 + 1 * 2 //│ = 4 // * Note: indented newlines starting with an operator are always treated as continuations // * of the previous line, without any implicit grouping of the indented parts, // * explaining the result below. // * I originally tried to make such indented operators implicitly group subexpressions, // * but it turned out to be just too strange and confusing; the simple rule above seems better. 2 + 1 * 2 //│ = 4 2 + 2 //│ = 4 1 + 2 * 3 //│ = 7 1 + 2 * 2 * 2 //│ = 9 1 + 2 * 3 + 1 //│ = 8 2 * 3 + 1 //│ = 8 2 * 3 + 1 //│ = 8 2 * 3 + 1 //│ = 7 // :pe 2 * 3 + 1 //│ = 7 1 + 1 * 2 //│ = 3 1 + 1 * 2 //│ = 4 // Parses as: 1 + (1 * 3) + 1 1 + 1 * 3 + 1 //│ = 5 // :dp 1 + 2 * 3 + 1 //│ = 8 :pe 1 + 2 * 2 + 1 * 3 //│ ╔══[PARSE ERROR] Unexpected indented block in this operator split inner position //│ ║ l.95: + 1 //│ ║ ^^^^^^^ //│ ║ l.96: * 3 //│ ║ ^^ //│ ╟── Note: the operator split starts here //│ ║ l.94: * 2 //│ ╙── ^ //│ = 12 :pe 1 + 2 * 2 + 1 * 3 //│ ╔══[PARSE ERROR] Operator cannot be used inside this operator split //│ ║ l.109: * 2 + //│ ║ ^ //│ ╟── as it has lower precedence than the splitting operator here //│ ║ l.109: * 2 + //│ ╙── ^ //│ = 19 :pe 1 + 2 * 2 * 3 + 1 //│ ╔══[PARSE ERROR] Unexpected indented block in this operator split inner position //│ ║ l.124: + 1 //│ ║ ^^^^ //│ ╟── Note: the operator split starts here //│ ║ l.122: * 2 //│ ╙── ^ //│ = 6 1 + 2 {* 3 {+ 1}} //│ = 8 1 + 2 * 3 + 1 //│ = 8 :pe 1 + 2 * 3 + 1 * 3 //│ ╔══[PARSE ERROR] Operator cannot be used inside this operator split //│ ║ l.143: * 3 + 1 //│ ║ ^ //│ ╟── as it has lower precedence than the splitting operator here //│ ║ l.143: * 3 + 1 //│ ╙── ^ //│ = 28 // :pe 1 + 2 * 3 * 3 + 1 //│ = 8 1 + 2 + 3 * 3 + 1 //│ = 19 :w 1 > 1 0 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.167: 1 //│ ╙── ^ //│ = true let f = x => x * 2 //│ f = fun f 2 + 1 |> f //│ = 6 f of 1 //│ = 2 f |> id of 1 //│ = 2 fun f(x)(y) = x + y f of 1 of 2 //│ = 3 f |> id of 1 of 2 //│ = 3 fun f(a)(b) = [a, b] fun g(a, b) = [a, b] f of g of 1 2 of 3 //│ = [[1, 2], 3] (f of g of 1 2 ) of 3 //│ = [[1, 2], 3] fun f(x) = x + 1 f(123) //│ = 124 fun f(x) = x * 2 + 1 f(123) //│ = 247 :pt fun f(x) = x + 1 * 2 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TermDef: //│ k = Fun //│ head = App: //│ lhs = Ident of "f" //│ rhs = Tup of Ls of //│ Ident of "x" //│ rhs = S of OpSplit: //│ lhs = Ident of "x" //│ ops_rhss = Ls of //│ OpApp: //│ lhs = SplitPoint //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 1 //│ OpApp: //│ lhs = SplitPoint //│ op = Ident of "*" //│ rhss = Ls of //│ IntLit of 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(123) //│ = 248 :pt fun f(x) = x + 1 * 2 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TermDef: //│ k = Fun //│ head = App: //│ lhs = Ident of "f" //│ rhs = Tup of Ls of //│ Ident of "x" //│ rhs = S of OpApp: //│ lhs = Ident of "x" //│ op = Ident of "+" //│ rhss = Ls of //│ OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "*" //│ rhss = Ls of //│ IntLit of 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(123) //│ = 125 fun f(x) = x { + 1, * 2 } f(3) //│ = 8 :pt fun f(x) = if x == 0 then "a" > 1 then "b" //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TermDef: //│ k = Fun //│ head = App: //│ lhs = Ident of "f" //│ rhs = Tup of Ls of //│ Ident of "x" //│ rhs = S of IfLike: //│ kw = Keywrd of keyword 'if' //│ split = OpSplit: //│ lhs = Ident of "x" //│ ops_rhss = Ls of //│ InfixApp: //│ lhs = OpApp: //│ lhs = SplitPoint //│ op = Ident of "==" //│ rhss = Ls of //│ IntLit of 0 //│ kw = Keywrd of keyword 'then' //│ rhs = StrLit of "a" //│ InfixApp: //│ lhs = OpApp: //│ lhs = SplitPoint //│ op = Ident of ">" //│ rhss = Ls of //│ IntLit of 1 //│ kw = Keywrd of keyword 'then' //│ rhs = StrLit of "b" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(0) //│ = "a" :re f(1) //│ ═══[RUNTIME ERROR] Error: match error f(2) //│ = "b" fun f(x) = if x > 0 then "a" is 0 then "b" f(0) //│ = "b" f(1) //│ = "a" // :pt :todo // TODO: juxtaposition splits? :e fun f(x) = if x foo(A) then a bar(B) then b //│ ╔══[COMPILATION ERROR] Unrecognized term split (juxtaposition) //│ ║ l.361: fun f(x) = if x //│ ║ ^ //│ ║ l.362: foo(A) then a //│ ╙── ^^ if 0 is 0 then "a" //│ = "a" if 0 is 0 then "a" is 1 then "b" //│ = "a" x => if x is 0 then "a" //│ = fun x => if x is 0 then "a" is 1 then "b" //│ = fun fun f(x) = if x is 0 then "a" is 1 then "b" f(0) //│ = "a" f(1) //│ = "b" :pt id of id of 1 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ App: //│ lhs = Ident of "id" //│ rhs = Tup of Ls of //│ App: //│ lhs = Ident of "id" //│ rhs = Tup of Ls of //│ IntLit of 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // :dp f of 1 //│ = "b" ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/OpDivPrecedence.mls ================================================ :js // Division should have the same precedence as multiplication :expect 101 100 + 100 / 100 //│ = 101 :expect 300 100 * 3 + 0 //│ = 300 :expect 300 0 + 100 * 3 //│ = 300 :expect 50 100 / 2 + 0 //│ = 50 :expect 50 0 + 100 / 2 //│ = 50 :expect 50 200 / 2 / 2 //│ = 50 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/OpenIn.mls ================================================ :js import "../../mlscript-compile/StrOps.mls" StrOps.{~} //│ = fun concat2 StrOps.{~}("a", "b") //│ = "ab" :e StrOps.{~}("a", "b", "c") //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 3 //│ ║ l.13: StrOps.{~}("a", "b", "c") //│ ╙── ^^^^^^^^^^^^^^^ //│ = "ab" StrOps.{"a" ~ "b"} //│ = "ab" StrOps.{"a" ~ from(123)} //│ = "a123" // Lexed as Str .~ :pe :e StrOps.~ //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.29: StrOps.~ //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: .~ //│ ║ l.29: StrOps.~ //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type StrOps. //│ ║ l.29: StrOps.~ //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/OutParams.mls ================================================ :js :todo // fun typeTerm(_, set ctx) = case // IntLit then // let ctx = .... // Int // App(f, a) then // let f_t = typeTerm(f, let ctx) // let a_t = typeTerm(a, let ctx) // resTy.... fun typeTerm(_, set ctx) = print(ctx) ctx = 123 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found set //│ ║ l.14: fun typeTerm(_, set ctx) = //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: ctx //│ ║ l.15: print(ctx) //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: ctx //│ ║ l.16: ctx = 123 //│ ╙── ^^^ fun typeTerm(_, let ctx) = ctx = 123 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found let //│ ║ l.27: fun typeTerm(_, let ctx) = //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: ctx //│ ║ l.28: ctx = 123 //│ ╙── ^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Overloading.mls ================================================ :js :global // :d :todo fun Foo() = 1 module Foo with { val x = 1 } //│ ╔══[COMPILATION ERROR] Not yet supported: overloading of function 'Foo' //│ ║ l.8: fun Foo() = 1 //│ ║ ^^^^^^^^^^^^^ //│ ╟── with module of the same name //│ ║ l.9: module Foo with { val x = 1 } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Foo //│ = class Foo { x: 1 } :todo Foo() //│ ═══[RUNTIME ERROR] TypeError: Class constructor Foo cannot be invoked without 'new' ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/PartialApps.mls ================================================ :js :pe :re . //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.6: . //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found period instead //│ ║ l.6: . //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe :re . // //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.17: . // //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found period instead //│ ║ l.17: . // //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme id(.) //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.28: id(.) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.28: id(.) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun incr = _ + 1 fun add = _ + _ passTo(2, incr)() //│ = 3 // TODO disallow :breakme passTo(2, incr(_))() //│ = 3 passTo(2, incr @ _)() //│ = 3 :fixme passTo(2, incr(.) @ _)() //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.53: passTo(2, incr(.) @ _)() //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.53: passTo(2, incr(.) @ _)() //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :breakme passTo(2, (incr(_)) @ _)() //│ = 3 :breakme passTo(2, add(_, 2))() //│ = 4 :fixme passTo(2, add(., 2))() //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.71: passTo(2, add(., 2))() //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.71: passTo(2, add(., 2))() //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :breakme passTo(1, incr(_) + _)(2) //│ = 4 :breakme passTo(1, (incr(_)) @ _ + _)(2) //│ = 4 passTo(1, (add(_, 1)) @ _ + _)(2) //│ = 4 :fixme passTo(1, incr(.) @ _ + _)(2) //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.92: passTo(1, incr(.) @ _ + _)(2) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.92: passTo(1, incr(.) @ _ + _)(2) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme passTo(1, add(., 1) @ _ + _)(2) //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.102: passTo(1, add(., 1) @ _ + _)(2) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.102: passTo(1, add(., 1) @ _ + _)(2) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :breakme :sjs let f = add(_, 1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f, f1; //│ f1 = function f(_0) { let tmp1; tmp1 = add(); return runtime.safeCall(tmp1(_0, 1)) }; //│ f = f1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f :fixme let f = add(., 1) //│ ╔══[PARSE ERROR] Expected an expression; found period instead //│ ║ l.123: let f = add(., 1) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here //│ ║ l.123: let f = add(., 1) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ f = undefined :fixme f(1) //│ ═══[RUNTIME ERROR] TypeError: f2 is not a function let f = add of _, 1 //│ f = fun f f(1) //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/PrefixOps.mls ================================================ :js import "../../mlscript-compile/Stack.mls" open Stack 0 * + 1 + 2 //│ = 0 x => + 1 + 2 //│ = fun | 1 //│ = 1 & 1 //│ = 1 let res = | 1 //│ res = 1 | 2 + 2 * | 3 + 3 //│ = 24 2 + 2 * 3 + 3 //│ = 24 2 + 2 * 3 + 3 //│ = 11 | 2 + 2 * | 3 + 3 //│ = 14 2 + 2 * | 3 + 3 //│ = 14 :pt 1 + | 2 + 3 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "+" //│ rhss = Ls of //│ App: //│ lhs = Ident of "|" //│ rhs = Tup of Ls of //│ OpApp: //│ lhs = IntLit of 2 //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 3 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 :fixme // ? | 1 | 2 //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composed type) //│ ║ l.70: | 1 //│ ║ ^ //│ ║ l.71: | 2 //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. pattern Bin = | 0 0 is Bin //│ = true 1 is Bin //│ = false pattern Bin = | 0 | 1 0 is Bin //│ = true 1 is Bin //│ = true 2 is Bin //│ = false pattern Bin = | 0 => false | 1 => true Bin.unapply(0) //│ = MatchSuccess(false, null) Bin.unapply(1) //│ = MatchSuccess(true, null) Bin.unapply(2) //│ = MatchFailure(null) // * Note: let x = + //│ x = fun x x(2, 3) //│ = 5 :e let x = | //│ ╔══[COMPILATION ERROR] Unexpected use of special operator '|' //│ ║ l.127: let x = | //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ x = undefined :e let x = (|) //│ ╔══[COMPILATION ERROR] Unexpected use of special operator '|' //│ ║ l.135: let x = (|) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ x = undefined :e & //│ ╔══[COMPILATION ERROR] Unexpected use of special operator '&' //│ ║ l.143: & //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun (|) test = 1 :e let x = | //│ ╔══[COMPILATION ERROR] Unexpected use of special operator '|' //│ ║ l.153: let x = | //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ x = undefined ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Puns.mls ================================================ :js let a = 1 //│ a = 1 (a: 1) //│ = {a: 1} (a: a) //│ = {a: 1} (:a) //│ = {a: 1} {:a} //│ = {a: 1} fun foo(x) = :x fun foo(x) = (:x) foo(1) //│ = {x: 1} fun foo(x) = :x y: x + 1 foo(1) //│ = {x: 1, y: 2} let r = :a b: 2 //│ r = {a: 1, b: 2} if r is { a: x } then x //│ = 1 if r is (a: x) then x //│ = 1 :e if r is a: x then x //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator ':'). //│ ║ l.48: a: x then x //│ ╙── ^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. if 1 is (((1))) then true //│ = true if 1 is {1} then true //│ = true :js let r2 = {a:2} if r2 is {a:0} then false 0 then true else 4 //│ = 4 //│ r2 = {a: 2} :js let foo = case (a: 0) then false (a: 1) then true (:a) then a //│ foo = fun foo(r) //│ = true foo(a: 2) //│ = 2 foo(:a) //│ = true :todo // would mean `val a = ‹the old a›`? val =a //│ ╔══[PARSE ERROR] Expected expression after 'val' binding keyword; found '=' keyword instead //│ ║ l.90: val =a //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe :re let =a //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found '=' keyword instead //│ ║ l.98: let =a //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. import "../../mlscript-compile/LazyArray.mls" // * Should probably warn that `dropLeftRight` has no effect // * (this requires checking it's not an effectful getter!) LazyArray.{ dropLeftRight, concat } //│ = fun concat LazyArray.{ :dropLeftRight, :concat } //│ = {dropLeftRight: fun dropLeftRight, concat: fun concat} ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls ================================================ :js :w do 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.5: do 1 //│ ╙── ^ :w if true do 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.11: if true do 1 //│ ╙── ^ :w if true do () else 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.17: if true do () else 1 //│ ╙── ^ if true then () else 1 :w x => x, 0 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.26: x => x, 0 //│ ╙── ^^^^^^ //│ = 0 :w () => 1 2 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.33: () => 1 //│ ╙── ^^^^^^^ //│ = 2 :w case x then x, 0 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.41: case x then x, 0 //│ ╙── ^^^^^^^^^^^^^ //│ = 0 :w case x then x case x then x //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.48: case x then x //│ ╙── ^^^^^^^^^^^^^ //│ = fun :w fun foo(x) = x x //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.58: x //│ ╙── ^ :w fun foo() = val x = 1 x x //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.67: x //│ ╙── ^ :w fun foo() = let x = 1 x x //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.76: x //│ ╙── ^ // * Note that uses of `;` currently do not warn... :sjs 1; id(2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = Predef.id(2); (1 , tmp) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Records.mls ================================================ :js // * Note that this currently denotes unit, not an empty record... // * (we could make them the same thing) {} let r = {} //│ r = () {1} //│ = 1 :w {1, 2} //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.17: {1, 2} //│ ╙── ^ //│ = 2 {foo: 1} //│ = {foo: 1} let rcd = { foo: 1, bar: "..." } //│ rcd = {foo: 1, bar: "..."} [rcd.foo, rcd.bar] //│ = [1, "..."] typeof(rcd) //│ = "object" // * Should we allow this syntax? let rcd = (foo: 1, bar: "...") //│ rcd = {foo: 1, bar: "..."} [rcd.foo, rcd.bar] //│ = [1, "..."] let rcd = ( foo: 1 bar: "..." ) //│ rcd = {foo: 1, bar: "..."} let rcd = foo: 1 bar: "..." //│ rcd = {foo: 1, bar: "..."} [rcd.foo, rcd.bar] //│ = [1, "..."] print <| foo: 1 bar: "..." //│ > {foo: 1, bar: "..."} // * But not this syntax... :e :fixme let rcd = foo = 1 bar = "..." //│ ╔══[COMPILATION ERROR] Name not found: foo //│ ║ l.72: foo = 1 //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: bar //│ ║ l.73: bar = //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ rcd = undefined // * How about this syntax? :todo let rcd = new with foo = 1 bar = "..." //│ ╔══[COMPILATION ERROR] Name not found: foo //│ ║ l.89: foo = 1 //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: bar //│ ║ l.90: bar = //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ rcd = undefined id of (x: 1) //│ = {x: 1} id of { x: 1 } //│ = {x: 1} id of ({ x: 1 }) //│ = {x: 1} id of ({ x: 1, y: 2 }) //│ = {x: 1, y: 2} id of (x: 1, y: 2) //│ = {x: 1, y: 2} id of { x: 1, y: 2 } //│ = {x: 1, y: 2} let m0 = {get(k): k + 1} //│ m0 = {get: fun get} m0.get(41) //│ = 42 let m0 = {("get")(k): k + 1} //│ m0 = {get: fun} m0.get(41) //│ = 42 :e {get(k): _} //│ ╔══[COMPILATION ERROR] Unexpected record key shape. //│ ║ l.137: {get(k): _} //│ ╙── ^^^^^^ //│ = fun :silent import "../../mlscript-compile/Iter.mls" let graphics = new Set of tuple of "circle", "ellipse", "image", "line", "path" "polygon", "polyline", "rect", "text", "use" fun showProps(x, y, props) = Object.entries(props) Iter.mapping of case ["x", x'] then "x: " + (x + x') ["y", y'] then "y: " + (y + y') [k, v] then k + ": " + v Iter.joined(", ") fun output(i, s) = print of " ".repeat(i) + s fun t(kind, props) = if graphics.has(kind) then (args) => output of args.i, kind + " " + showProps(args.x, args.y, props) else (...children) => (args) => output of args.i, kind + " " + showProps(args.x, args.y, props) + " with" children Iter.each of child => child of x: args.x + props.x y: args.y + props.y i: args.i + 1 let display = t("group", x: 20, y: 20) of t("circle", x: 30, y: 30, r: 42) t("rect", x: 40, y: 40, width: 80, height: 90) t("ellipse", x: 50, y: 50, rx: 40, ry: 60) //│ display = fun display(x: 11, y: 22, i: 2) //│ > group x: 31, y: 42 with //│ > circle x: 61, y: 72, r: 42 //│ > rect x: 71, y: 82, width: 80, height: 90 //│ > ellipse x: 81, y: 92, rx: 40, ry: 60 let display = t("group", x: 0, y: 0) of t("circle", x: 10, y: 10, r: 42) //│ display = fun display(x: 88, y: 99, i: 0) //│ > group x: 88, y: 99 with //│ > circle x: 98, y: 109, r: 42 :todo t("group") //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.192: t("group") //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 't' expected 2 arguments but got 1 :todo t("group") //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.199: t("group") //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 't' expected 2 arguments but got 1 let empty = Object.create of null //│ empty = {} t("group", empty) //│ = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/RefinedClasses.mls ================================================ :todo class Foo { } //│ ╔══[COMPILATION ERROR] Invalid class definition head: unexpected block in this position //│ ║ l.4: class Foo { } //│ ╙── ^^^ :todo class Foo() { val x = 1 } //│ ╔══[PARSE ERROR] Expected end of input; found curly brace section instead //│ ║ l.10: class Foo() { //│ ║ ^ //│ ║ l.11: val x = 1 //│ ║ ^^^^^^^^^^^ //│ ║ l.12: } //│ ╙── ^ :todo class Foo() val x = 1 //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead //│ ║ l.23: val x = 1 //│ ╙── ^^ :todo class Bar { val x: Int } //│ ╔══[PARSE ERROR] Expected end of input; found curly brace section instead //│ ║ l.29: class Bar { val x: Int } //│ ╙── ^^^^^^^^^^^^^^ :todo class Foo() extends Bar { x = 1 } with val y = this.x //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (defining assignment). //│ ║ l.35: class Foo() extends Bar { x = 1 } with //│ ╙── ^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Return.mls ================================================ :js fun f = print("reachable") return print("unreachable") f //│ > reachable fun foo(x) = if x do print("true") return 42 print("false") 0 foo(true) //│ > true //│ = 42 foo(false) //│ > false //│ = 0 fun foo(x) = return print("returning...") x foo(123) //│ > returning... //│ = 123 :e return "whoops" //│ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. //│ ║ l.40: return "whoops" //│ ╙── ^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e x => return "whoops" //│ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. //│ ║ l.47: x => return "whoops" //│ ╙── ^^^^^^^^^^^^^^^ //│ = fun :e fun foo() = x => return "whoops" //│ ╔══[COMPILATION ERROR] Non-local return statements are only supported with effect handlers enabled. //│ ║ l.55: x => return "whoops" //│ ╙── ^^^^^^^^^^^^^^^ :e fun foo(x: return "whoops") = 1 //│ ╔══[COMPILATION ERROR] Return statements are not allowed in this context. //│ ║ l.61: fun foo(x: return "whoops") = 1 //│ ╙── ^^^^^^^^^^^^^^^ //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› :e fun foo() = fun bar(x: return "whoops") = 1 bar(123) //│ ╔══[COMPILATION ERROR] Return statements are not allowed in this context. //│ ║ l.70: fun bar(x: return "whoops") = 1 //│ ╙── ^^^^^^^^^^^^^^^ //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/SetIndented.mls ================================================ :js // Same-line set with member selection parses fine :e set this.pc = 0 set (this).pc = 0 //│ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope //│ ║ l.5: set this.pc = 0 //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope //│ ║ l.7: (this).pc = 0 //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // Indented set with member selection should also parse fine :e set this.pc = 0 //│ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope //│ ║ l.19: this.pc = 0 //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls ================================================ :js :sjs 1 && 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (1 === true) { 2 } else { false } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = false fun loud(x) = print(x); x loudTrue = loud(true) loudFalse = loud(false) true && loudTrue //│ > true //│ = true true && loudFalse //│ > false //│ = false false && loudTrue //│ = false :sjs print(1 && loudTrue) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; //│ if (1 === true) { tmp = loudTrue(); Predef.print(tmp) } else { tmp = false; Predef.print(tmp) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > false :sjs 1 || 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (1 === false) { 2 } else { true } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true print(1 || 2) //│ > true 1 || loudTrue //│ = true 1 || loudFalse //│ = true true || loudTrue //│ = true 123 || loud(42) //│ = true 0 || loud(42) //│ = true 123 && loud(42) //│ = false :sjs fold(||)(0, false, 42, 123) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda, callPrefix; //│ lambda = (undefined, function (arg1, arg2) { //│ if (arg1 === false) { //│ return arg2 //│ } //│ return true; //│ }); //│ callPrefix = runtime.safeCall(Predef.fold(lambda)); //│ runtime.safeCall(callPrefix(0, false, 42, 123)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true print of print("A") && print("B") //│ > A //│ > false fun (&&) foo(x, y) = print(x) print(y) x and y 1 && 2 //│ > 1 //│ > 2 //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/StrTest.mls ================================================ :js import "../../mlscript-compile/StrOps.mls" open StrOps :sjs "a" is Str //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; scrut = "a"; if (typeof scrut === 'string') { true } else { false } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true concat of "a", "b", "c" //│ = "abc" concat of "a" "b" "c" //│ = "abc" // TODO (~) should be sanitized :e (~)("a", "b", "c") //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 3 //│ ║ l.29: (~)("a", "b", "c") //│ ╙── ^^^^^^^^^^^^^^^ //│ = "ab" :e (~)("a") //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.37: (~)("a") //│ ╙── ^^^^^ //│ = "aundefined" :ge ~"a" //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) //│ ║ l.44: ~"a" //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge ~("a", "b") //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) //│ ║ l.52: ~("a", "b") //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe :ge ~ of "a", "b" //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'of' keyword instead //│ ║ l.61: ~ of "a", "b" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.61: ~ of "a", "b" //│ ╙── ^^^ //│ ═══[COMPILATION ERROR] Unexpected term form in expression position (negation type) //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. (~) of "a", "b" //│ = "ab" fun test(...xs) = concat of ...xs, "!" test("a", "b", "c") //│ = "abc!" "a" StrOps.concat2("b") //│ = "ab" "a" StrOps.concat2 of "b" //│ = "ab" ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/SuspensionComments.mls ================================================ :js // * Suspension with end-of-line comment should properly expand the continuation body mkStr of ... // hello "oops" //│ = "oops" mkStr of ... // hello "oops" //│ = "oops" mkStr of ... // hello "oops" //│ = "oops" mkStr of ... // hello // hello "oops" //│ = "oops" ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ThenRecord.mls ================================================ :js //* Fixed: `if true then success: 42` should work like `if true then (success: 42)` if true then (success: 42) //│ = {success: 42} if true then success: 42 //│ = {success: 42} if true then success: 42 //│ = {success: 42} if true then success: 42 else failure: "oops" //│ = {success: 42} if false then success: 42 else failure: "oops" //│ = {failure: "oops"} ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/Underscores.mls ================================================ :js :sjs _ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; lambda = (undefined, function (_0) { return _0 }); lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun (_) //│ = fun :e y: _ //│ ╔══[COMPILATION ERROR] Illegal position for '_' placeholder. //│ ║ l.15: y: _ //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ y = undefined (y: _) //│ = fun :w _, _ //│ ═══[WARNING] Pure expression in statement position //│ = fun let inc = _ + 1 //│ inc = fun inc inc(2) //│ = 3 :sjs _ + _ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda4; lambda4 = (undefined, function (_0, _1) { return _0 + _1 }); lambda4 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun let f = _ + _ //│ f = fun f f(2, 3) //│ = 5 import "../../mlscript-compile/Iter.mls" open Iter filtering of [], _ => 1 //│ = Iterable(_) print(_) //│ = fun :sjs let test = _.f(0, _, 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test, test1; //│ test1 = function test(_0, _1) { return runtime.safeCall(_0.f(0, _1, 2)) }; //│ test = test1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ test = fun test :re test(f: print)(1) //│ ═══[RUNTIME ERROR] Error: Function 'test' expected 2 arguments but got 1 test((f: print), 1) //│ > 0 1 2 test({f: print}, 1) //│ > 0 1 2 {f: print} //│ = {f: fun print} ({f: print}) //│ = {f: fun print} [{f: print}] //│ = [{f: fun print}] [{f: print}, 1] //│ = [{f: fun print}, 1] print(inc(_)) //│ > fun :sjs _ is Int //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda8; //│ lambda8 = (undefined, function (_0) { //│ if (globalThis.Number.isInteger(_0)) { return true } //│ return false; //│ }); //│ lambda8 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun let f = _ is Int [f(1), f("1")] //│ = [true, false] //│ f = fun f [1, "a", 3, true] filtering(_ is Int) toArray() //│ = [1, 3] 42 is _ //│ = true let x_to = x: _ //│ x_to = fun x_to x_to(42) //│ = {x: 42} let mkObj = (x: _, y: _, z: 3) //│ mkObj = fun mkObj mkObj(1, 2) //│ = {x: 1, y: 2, z: 3} // * Note: this is parsed as `{let mkObj = x: _}, y: _, z: 3` // * TODO: improve or at least raise a warning :e :ge :re let mkObj = x: _, y: _, z: 3 //│ ╔══[COMPILATION ERROR] Illegal position for '_' placeholder. //│ ║ l.140: let mkObj = x: _, y: _, z: 3 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ mkObj = fun mkObj //│ y = undefined //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'z' //│ ╟── which references the symbol introduced here //│ ║ l.140: let mkObj = x: _, y: _, z: 3 //│ ╙── ^ //│ ═══[RUNTIME ERROR] ReferenceError: z is not defined // mkObj(1, 2) let mkObj = x: _, y: _, z: 3 //│ mkObj = fun mkObj mkObj(1, 2) //│ = {x: 1, y: 2, z: 3} let obj = f: _ + 1 //│ obj = {f: fun f} obj.f(123) //│ = 124 {f: _ + 1}.f(123) //│ = 124 let f = _ => 1 //│ f = fun f f(2) //│ = 1 let f = x => _ //│ f = fun f f(2)(3) //│ = 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/ValMemberSymbols.mls ================================================ :js :noSanityCheck :sir class A(val x) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A⁰; define A⁰ as class A²(x) { val x⁰; constructor A¹ { define x⁰ as val x¹ = x; end } }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let a = A(1) //│ a = A(1) // * Currently, we don't get a symbol here, yet :sir a.x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ a⁰.x﹖ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // * Check that the symbol here is the one we expect: `x¹`, from `define x⁰ as val x¹` above :sir a.A#x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ a⁰.x¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // * Idem :sir A::x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁰; define lambda⁰ as fun lambda¹(self, ...args) { return self.x¹(...args) }; lambda¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // * Idem :sir case A(x) then x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda²; //│ define lambda² as fun lambda³(caseScrut) { //│ let x, arg$A$0$; //│ match caseScrut //│ A² => //│ set arg$A$0$ = caseScrut.x¹; //│ set x = arg$A$0$; //│ return x //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ lambda³ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :sir class A()(val x) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A³; define A³ as class A⁵() { val x²; constructor A⁴(x) { define x² as val x³ = x; end } }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let a = A()(1) //│ a = A() :sir a.x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ a¹.x﹖ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :sir a.A#x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ a¹.x³ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :sir A::x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁴; define lambda⁴ as fun lambda⁵(self, ...args) { return self.x³(...args) }; lambda⁵ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // * Note: no symbol info here, yet :sir case A as a then a.x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁶; //│ define lambda⁶ as fun lambda⁷(caseScrut) { //│ let a; //│ match caseScrut //│ A⁵ => //│ set a = caseScrut; //│ return a.x﹖ //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ lambda⁷ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // :ssjs :sir :lift // TODO: remove this flag when the lifter problem is resolved (see: the end of ClassInFun.mls) :deadParamElim off fun foo(x, y) = class A(val u) with { val v = y } let a = new A(x) [a, a.A#u, a.A#v] //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A⁶, foo⁰; //│ define A⁶ as class A⁸(u) { //│ private val y⁰; //│ val u⁰; //│ val v⁰; //│ constructor A⁷(y) { //│ set y⁰ = y; //│ define u⁰ as val u¹ = u; //│ define v⁰ as val v¹ = y⁰; //│ end //│ } //│ }; //│ define foo⁰ as fun foo¹(x, y) { let a; set a = new A⁸(x)(y); return [a, a.u¹, a.v¹] }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect [A(1), 1, 2] foo(1, 2) //│ = [A(1), 1, 2] // TODO: when it's supported in the surface language, also test classes with only aux param lists, as in // class A with // constructor(val x) // if new A(1) is // A { x } then ... ================================================ FILE: hkmc2/shared/src/test/mlscript/basics/WeirdCalls.mls ================================================ :js // TODO: should report that such `print` reference does not do anything in statement position print ( "A" ) //│ = "A" :fixme print ( "A" ) //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (parenthesis section). //│ ║ l.13: ( //│ ║ ^ //│ ║ l.14: "A" //│ ║ ^^^^^^^ //│ ║ l.15: ) //│ ╙── ^^^ //│ = fun print :w print of ( "A" ) //│ ╔══[WARNING] This parenthesis section should be indented //│ ║ l.27: ( //│ ║ ^ //│ ║ l.28: "A" //│ ║ ^^^^^ //│ ║ l.29: ) //│ ║ ^ //│ ╟── since it is a continuation of the new line here //│ ║ l.26: print of //│ ║ ^ //│ ║ l.27: ( //│ ╙── //│ > A :w print of ("A") //│ ╔══[WARNING] This parenthesis section should be indented //│ ║ l.46: ("A") //│ ║ ^^^^^ //│ ╟── since it is a continuation of the new line here //│ ║ l.45: print of //│ ║ ^ //│ ║ l.46: ("A") //│ ╙── //│ > A print of ( "A" ) //│ > A :pe print of ( "A" ) //│ ╔══[PARSE ERROR] Mistmatched closing indentation //│ ║ l.66: "A" //│ ║ ^ //│ ║ l.67: ) //│ ║ //│ ╟── does not correspond to opening parenthesis //│ ║ l.65: ( //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected closing parenthesis //│ ║ l.67: ) //│ ╙── ^ //│ > A print of ( "A" ) //│ > A print( "A" ) //│ > A ================================================ FILE: hkmc2/shared/src/test/mlscript/block-staging/Functions.mls ================================================ :js :staging :fixme // TODO: fix IR rebinding issue (each symbol should be bound at most once) :checkIR staged module Expressions with fun lit() = let x = 42 x //│ ╔══[INTERNAL ERROR] [BlockChecker] Invalid IR: symbol x⁰ is bound more than once //│ ║ l.8: let x = 42 //│ ╙── ^ //│ > fun ctor_() = () //│ > fun lit() = //│ > let {x} //│ > x = 42 //│ > x let x = [1, 2, 3] staged module Expressions with fun lit() = 1 fun assign() = let x = 42 let y = x y fun tup1() = [1, 2] fun tup2() = [1, x] fun dynsel() = [1].(0) fun match1() = if 9 is Bool then 1 8 then 2 Int then 3 9 then 4 else 0 fun match2() = if x is [] then 1 [1, 2] then 2 [a, _] then 3 else 0 //│ > fun ctor_() = () //│ > fun lit() = 1 //│ > fun assign() = //│ > let {x, y} //│ > x = 42 //│ > y = x //│ > y //│ > fun tup1() = [1, 2] //│ > fun tup2() = [1, x1] //│ > fun dynsel() = //│ > let {tmp} //│ > tmp = [1] //│ > tmp.(0) //│ > fun match1() = //│ > let {scrut} //│ > scrut = 9 //│ > if scrut is //│ > Bool then 1 //│ > 8 then 2 //│ > Int then 3 //│ > else 0 //│ > fun match2() = //│ > let {a, element1_, element0_} //│ > if x1 is //│ > [] then 1 //│ > [_, _] then //│ > element0_ = x1.0 //│ > element1_ = x1.1 //│ > if element0_ is //│ > 1 then //│ > if element1_ is //│ > 2 then 2 //│ > else //│ > a = element0_ //│ > 3 //│ > else //│ > a = element0_ //│ > 3 //│ > else 0 //│ x = [1, 2, 3] class Outside(a) staged module ClassInstrumentation with class Inside(a, b) class NoArg fun inst1() = new Outside(1) fun inst2() = new NoArg fun app1() = Outside(1) fun app2() = Inside(1, 2) //│ > fun ctor_() = //│ > class Inside(a1, b) //│ > class NoArg //│ > fun inst1() = new Outside(1) //│ > fun inst2() = new ClassInstrumentation.NoArg //│ > fun app1() = Outside(1) //│ > fun app2() = ClassInstrumentation.Inside(1, 2) module Nonstaged with fun f() = 1 staged module Staged with fun f() = 1 staged module CallSubst with fun call() = 1 + 1 Nonstaged.f() Staged.f() //│ > fun ctor_() = () //│ > fun f() = 1 //│ > fun ctor_() = () //│ > fun call() = //│ > Nonstaged.f() //│ > Staged.f_gen() :ftc staged module Arguments with fun f(x) = x = 1 x fun g(x)(y, z)() = z //│ > fun ctor_() = () //│ > fun f(x) = //│ > x = 1 //│ > x //│ > fun g(x) = //│ > let {tmp} //│ > tmp = new Function_ //│ > tmp // :e staged module BadArguments with fun f() = Arguments.g(1)(2, 3) //│ > fun ctor_() = () //│ > fun f() = //│ > let {callPrefix} //│ > callPrefix = Arguments.g_gen(1) //│ > callPrefix(2, 3) staged module OtherBlocks with fun scope() = scope.locally of ( let a = 1 a ) fun breakAndLabel() = if 1 is 2 then 0 3 then 0 else 0 //│ > fun ctor_() = () //│ > fun scope() = //│ > let {a} //│ > a = 1 //│ > OtherBlocks.scope.locally(a) //│ > fun breakAndLabel() = //│ > let {scrut} //│ > scrut = 1 //│ > if scrut is //│ > 2 then 0 //│ > 3 then 0 //│ > else 0 staged module ClassDefs with class A //│ > fun ctor_() = //│ > class A staged module ValClass with class A(val a) //│ > fun ctor_() = //│ > class A(a) staged module ClassFunctions with class InnerClass() with fun f() = 1 + Arguments.f(1) fun g() = f() //│ > fun ctor_() = //│ > class InnerClass() with //│ > fun f() = //│ > let {tmp} //│ > tmp = Arguments.f_gen(1) //│ > 1 + tmp //│ > fun g() = InnerClass.f() staged module RetUnit with fun f() = () //│ > fun ctor_() = () //│ > fun f() = () // name collision class A() staged module A with fun f() = 1 //│ > fun ctor_() = () //│ > fun f() = 1 // nested module module A with staged module B with fun f() = 1 //│ > fun ctor_() = () //│ > fun f() = 1 // FIXME: for g to be defined in the next stage, we need to also print the other ClsLikeDefn/FunDefn nodes when printing the next stage :ftc staged module NestedFunDefn with fun f() = fun g() = 1 g() //│ > fun ctor_() = () //│ > fun f() = g() :todo staged module LabelBreak with fun f() = let x = 1 while x == 1 do set x = x + 1 fun g() = if 1 is 0 then 2 1 then 2 else 2 //│ ═══[COMPILATION ERROR] Other Blocks not supported in staged module: class hkmc2.codegen.Label. //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //│ ║ l.217: let x = 1 //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.217: let x = 1 //│ ╙── ^ //│ > fun ctor_() = () //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. :e class C(val a) staged module A with let x = C(1) fun f() = set x.a = 0 fun g() = {1 : 2} //│ ═══[COMPILATION ERROR] Other Blocks not supported in staged module: class hkmc2.codegen.AssignField //│ ╔══[COMPILATION ERROR] Other Results not supported in staged module: class hkmc2.codegen.Record //│ ║ l.239: fun g() = {1 : 2} //│ ╙── ^ //│ > fun ctor_() = //│ > x = C(1) //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. :todo staged module Spread with fun f() = if [1, ..[1, 2]] is [1, ...x] then x else 0 //│ ═══[COMPILATION ERROR] Spread parameters are not supported in staged module: Arg(Some(Lazy),Ref(tmp:tmp,None)) //│ ═══[COMPILATION ERROR] No definition found in scope for member 'tmp' //│ > fun ctor_() = () //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. ================================================ FILE: hkmc2/shared/src/test/mlscript/block-staging/Nested.mls ================================================ :js :ftc :sir :staging //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ import Predef; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— staged fun direct() = 1 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let direct⁰; define direct⁰ as staged fun direct¹() { return 1 }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— staged module LiftedNested with fun f() = fun g() = 1 g() //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g⁰, LiftedNested⁰; //│ define g⁰ as staged fun g¹() { //│ return 1 //│ }; //│ define LiftedNested⁰ as class LiftedNested¹ //│ staged module LiftedNested² { //│ constructor { //│ let tmp, tmp1, tmp2, tmp3; //│ set tmp2 = LiftedNested².this.ctor$_instr﹖(); //│ set tmp3 = Block⁰.printCode﹖(tmp2); //│ set tmp = LiftedNested².this.f_instr﹖(); //│ set tmp1 = Block⁰.printCode﹖(tmp); //│ runtime⁰.Unit﹖ //│ } //│ method ctor$_instr⁰ = fun ctor$_instr¹() { //│ let end, tmp, tmp1, tmp2, tmp3; //│ set end = Block⁰.End﹖(); //│ set tmp = Block⁰.Symbol﹖("ctor$"); //│ set tmp1 = []; //│ set tmp2 = [tmp1]; //│ set tmp3 = Block⁰.FunDefn﹖(tmp, tmp2, end, true); //│ return tmp3 //│ } //│ method f⁰ = fun f¹() { //│ return g¹() //│ } //│ method f_instr⁰ = fun f_instr¹() { //│ let sym, var1, tmp, app, return1, tmp1, tmp2, tmp3, tmp4; //│ set sym = Block⁰.Symbol﹖("g"); //│ set var1 = Block⁰.ValueRef﹖(sym); //│ set tmp = []; //│ set app = Block⁰.Call﹖(var1, tmp); //│ set return1 = Block⁰.Return﹖(app, false); //│ set tmp1 = Block⁰.Symbol﹖("f"); //│ set tmp2 = []; //│ set tmp3 = [tmp2]; //│ set tmp4 = Block⁰.FunDefn﹖(tmp1, tmp3, return1, true); //│ return tmp4 //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > fun ctor_() = () //│ > fun f() = g() staged module LiftedLambda with fun foo(x) = y => x + y //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let LiftedLambda⁰, lambda⁰, Function$⁰; //│ define lambda⁰ as staged fun lambda¹(x, y) { //│ return +⁰(x, y) //│ }; //│ define Function$⁰ as staged class Function$¹ { //│ private val x⁰; //│ constructor(x) { //│ super⁰(); //│ set x⁰ = x; //│ end //│ } //│ method call⁰ = fun call¹(y) { //│ return lambda¹(x⁰, y) //│ } //│ }; //│ define LiftedLambda⁰ as class LiftedLambda¹ //│ staged module LiftedLambda² { //│ constructor { //│ let tmp, tmp1, tmp2, tmp3; //│ set tmp2 = LiftedLambda².this.ctor$_instr﹖(); //│ set tmp3 = Block⁰.printCode﹖(tmp2); //│ set tmp = LiftedLambda².this.foo_instr﹖(); //│ set tmp1 = Block⁰.printCode﹖(tmp); //│ runtime⁰.Unit﹖ //│ } //│ method ctor$_instr² = fun ctor$_instr³() { //│ let end, tmp, tmp1, tmp2, tmp3; //│ set end = Block⁰.End﹖(); //│ set tmp = Block⁰.Symbol﹖("ctor$"); //│ set tmp1 = []; //│ set tmp2 = [tmp1]; //│ set tmp3 = Block⁰.FunDefn﹖(tmp, tmp2, end, true); //│ return tmp3 //│ } //│ method foo⁰ = fun foo¹(x) { //│ let tmp; //│ set tmp = new Function$¹(x); //│ return tmp //│ } //│ method foo_instr⁰ = fun foo_instr¹() { //│ let x, tmp, sym, tmp1, sym1, var1, tmp2, tmp3, sym2, tmp4, tmp5, sym3, var2, tmp6, inst, sym4, tmp7, return1, assign, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; //│ set sym = Block⁰.Symbol﹖("tmp"); //│ set tmp1 = [sym]; //│ set sym1 = Block⁰.Symbol﹖("x"); //│ set var1 = Block⁰.ValueRef﹖(sym1); //│ set tmp2 = Block⁰.Arg﹖(var1); //│ set tmp3 = option⁰.None﹖; //│ set sym2 = Block⁰.Symbol﹖("x1"); //│ set tmp4 = [sym2]; //│ set tmp5 = [tmp4]; //│ set sym3 = Block⁰.ConcreteClassSymbol﹖("Function$", Function$⁰, tmp3, tmp5); //│ set var2 = Block⁰.ValueRef﹖(sym3); //│ set tmp6 = [tmp2]; //│ set inst = Block⁰.Instantiate﹖(var2, tmp6); //│ set sym4 = Block⁰.Symbol﹖("tmp"); //│ set tmp7 = Block⁰.ValueRef﹖(sym4); //│ set tmp = tmp7; //│ set return1 = Block⁰.Return﹖(tmp7, false); //│ set assign = Block⁰.Assign﹖(sym4, inst, return1); //│ set tmp8 = Block⁰.Scoped﹖(tmp1, assign); //│ set tmp9 = Block⁰.Symbol﹖("x"); //│ set tmp10 = Block⁰.Symbol﹖("foo"); //│ set tmp11 = [tmp9]; //│ set tmp12 = [tmp11]; //│ set tmp13 = Block⁰.FunDefn﹖(tmp10, tmp12, tmp8, true); //│ return tmp13 //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > fun ctor_() = () //│ > fun foo(x) = //│ > let {tmp} //│ > tmp = new Function_(x) //│ > tmp staged module LiftedMultParams with fun foo(x)(y) = x + y //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let LiftedMultParams⁰, lambda$⁰, Function$²; //│ define lambda$⁰ as staged fun lambda$¹(x, y) { //│ return +⁰(x, y) //│ }; //│ define Function$² as staged class Function$³ { //│ private val x¹; //│ constructor(x) { //│ super⁰(); //│ set x¹ = x; //│ end //│ } //│ method call² = fun call³(y) { //│ return lambda$¹(x¹, y) //│ } //│ }; //│ define LiftedMultParams⁰ as class LiftedMultParams¹ //│ staged module LiftedMultParams² { //│ constructor { //│ let tmp, tmp1, tmp2, tmp3; //│ set tmp2 = LiftedMultParams².this.ctor$_instr﹖(); //│ set tmp3 = Block⁰.printCode﹖(tmp2); //│ set tmp = LiftedMultParams².this.foo_instr﹖(); //│ set tmp1 = Block⁰.printCode﹖(tmp); //│ runtime⁰.Unit﹖ //│ } //│ method ctor$_instr⁴ = fun ctor$_instr⁵() { //│ let end, tmp, tmp1, tmp2, tmp3; //│ set end = Block⁰.End﹖(); //│ set tmp = Block⁰.Symbol﹖("ctor$"); //│ set tmp1 = []; //│ set tmp2 = [tmp1]; //│ set tmp3 = Block⁰.FunDefn﹖(tmp, tmp2, end, true); //│ return tmp3 //│ } //│ method foo² = fun foo³(x) { //│ let tmp; //│ set tmp = new Function$³(x); //│ return tmp //│ } //│ method foo_instr² = fun foo_instr³() { //│ let x, tmp, sym, tmp1, sym1, var1, tmp2, tmp3, sym2, tmp4, tmp5, sym3, var2, tmp6, inst, sym4, tmp7, return1, assign, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; //│ set sym = Block⁰.Symbol﹖("tmp"); //│ set tmp1 = [sym]; //│ set sym1 = Block⁰.Symbol﹖("x"); //│ set var1 = Block⁰.ValueRef﹖(sym1); //│ set tmp2 = Block⁰.Arg﹖(var1); //│ set tmp3 = option⁰.None﹖; //│ set sym2 = Block⁰.Symbol﹖("x1"); //│ set tmp4 = [sym2]; //│ set tmp5 = [tmp4]; //│ set sym3 = Block⁰.ConcreteClassSymbol﹖("Function$", Function$², tmp3, tmp5); //│ set var2 = Block⁰.ValueRef﹖(sym3); //│ set tmp6 = [tmp2]; //│ set inst = Block⁰.Instantiate﹖(var2, tmp6); //│ set sym4 = Block⁰.Symbol﹖("tmp"); //│ set tmp7 = Block⁰.ValueRef﹖(sym4); //│ set tmp = tmp7; //│ set return1 = Block⁰.Return﹖(tmp7, false); //│ set assign = Block⁰.Assign﹖(sym4, inst, return1); //│ set tmp8 = Block⁰.Scoped﹖(tmp1, assign); //│ set tmp9 = Block⁰.Symbol﹖("x"); //│ set tmp10 = Block⁰.Symbol﹖("foo"); //│ set tmp11 = [tmp9]; //│ set tmp12 = [tmp11]; //│ set tmp13 = Block⁰.FunDefn﹖(tmp10, tmp12, tmp8, true); //│ return tmp13 //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > fun ctor_() = () //│ > fun foo(x) = //│ > let {tmp} //│ > tmp = new Function_(x) //│ > tmp ================================================ FILE: hkmc2/shared/src/test/mlscript/block-staging/PrintCode.mls ================================================ :staging :js import "../../mlscript-compile/Block.mls" import "../../mlscript-compile/Option.mls" open Block open Option printCode(FunDefn(Symbol("f"), [[Symbol("x")]], Return(ValueLit(1), false))) //│ > fun f(x) = 1 printCode(ValueLit(true)) printCode(ValueLit(null)) printCode(ValueLit(undefined)) printCode(ValueLit("c")) printCode(ValueLit("string")) //│ > true //│ > null //│ > undefined //│ > "c" //│ > "string" printCode(Symbol("internal$")) //│ > internal_ printCode(Select(ValueRef(Symbol("p")), Symbol("x"))) printCode(DynSelect(ValueRef(Symbol("p")), ValueRef(Symbol("field")), false)) //│ > p.x //│ > p.(field) :silent class A(val x)(y) class B let aSym = ConcreteClassSymbol("A", A, Some([Symbol("x")]), [[Symbol("y")]]) let bSym = ConcreteClassSymbol("B", B, None, []) printCode(Call(ValueRef(Symbol("f")), [Arg(ValueLit(3))])) printCode(Instantiate(ValueRef(aSym), [Arg(ValueLit(0))])) printCode(Tuple([Arg(ValueRef(Symbol("x"))), Arg(ValueRef(Symbol("y")))])) //│ > f(3) //│ > new A(0) //│ > [x, y] :silent let f = FunDefn(Symbol("f"), [[Symbol("x")]], Return(ValueLit(1), false)) printCode(f) printCode(ClsLikeDefn(aSym, [f], None)) printCode(ClsLikeDefn(bSym, [], None)) //│ > fun f(x) = 1 //│ > class A(x) with //│ > fun f(x) = 1 //│ > class B printCode(Scoped([Symbol("x"), Symbol("y")], Assign(Symbol("x"), ValueLit(4.2), Return(ValueRef(Symbol("x")), true)))) //│ > let {x, y} //│ > x = 4.2 //│ > x printCode(Define(ValDefn(Some(aSym), Symbol("x"), ValueLit(1)), End())) printCode(Define(ValDefn(None, Symbol("a"), ValueLit(1)), End())) //│ > val x = 1 //│ > val a = 1 printCode(Match(ValueRef(Symbol("x")), [Arm(Lit(1), Return(ValueLit(2), false)), Arm(Cls(aSym, ValueRef(aSym)), Return(ValueLit(3), false))], Some(Return(ValueRef(Symbol("x")), false)), End())) //│ > if x is //│ > 1 then 2 //│ > A then 3 //│ > else x printCode(Scoped([Symbol("y")], Match( ValueLit(1), [Arm(Lit(1), Return(ValueLit(1), false))], Some(Return(ValueLit(1), false)), Return(ValueRef(Symbol("y")), false)))) //│ > let {y} //│ > if 1 is //│ > 1 then 1 //│ > else 1 //│ > y printCode(Scoped([Symbol("y")], Match( ValueLit(1), [Arm(Lit(1), Assign(Symbol("y"), ValueLit(1), Return(ValueRef(Symbol("y")), false)))], Some(Assign(Symbol("y"), ValueLit(2), Return(ValueRef(Symbol("y")), false))), End()))) //│ > let {y} //│ > if 1 is //│ > 1 then //│ > y = 1 //│ > y //│ > else //│ > y = 2 //│ > y printCode(Scoped([Symbol("y")], Match( ValueLit(1), [Arm(Lit(1), Match( ValueLit(2), [Arm(Lit(2), Assign(Symbol("y"), ValueLit(1), Return(ValueRef(Symbol("y")), false)))], Some(Assign(Symbol("y"), ValueLit(2), Return(ValueRef(Symbol("y")), false))), End() ))], Some(Assign(Symbol("y"), ValueLit(3), Return(ValueRef(Symbol("y")), false))), End()))) //│ > let {y} //│ > if 1 is //│ > 1 then //│ > if 2 is //│ > 2 then //│ > y = 1 //│ > y //│ > else //│ > y = 2 //│ > y //│ > else //│ > y = 3 //│ > y ================================================ FILE: hkmc2/shared/src/test/mlscript/block-staging/StageSymbols.mls ================================================ :js :staging staged module M with class C with fun f() = 1 fun g() = f() //│ > fun ctor_() = //│ > class C with //│ > fun f() = 1 //│ > fun g() = C.f() staged module A with fun f() = 1 module B with fun f() = 1 class C(val a, b)(c)(d) staged module D with class E fun f() = A.f() B.f() fun matching() = 1 is Int 1 is C //│ > fun ctor_() = () //│ > fun f() = 1 //│ > fun ctor_() = //│ > class E //│ > fun f() = //│ > A.f_gen() //│ > B.f() //│ > fun matching() = //│ > let {scrut, scrut1, tmp} //│ > scrut = 1 //│ > if scrut is //│ > Int then //│ > tmp = true //│ > else //│ > tmp = false //│ > scrut1 = 1 //│ > if scrut1 is //│ > C then true //│ > else false module A with module B with class C(a) staged module M with class E fun f() = D.E A.B.C A.B.C(1) E //│ > fun ctor_() = //│ > class E //│ > fun f() = //│ > A.B.C(1) //│ > M.E :e staged module M with fun g()() = 1 //│ ╔══[COMPILATION ERROR] :ftc must be enabled to desugar functions with multiple parameter lists. //│ ║ l.64: fun g()() = 1 //│ ╙── ^^^^^^^^^^^^^ //│ > fun ctor_() = () //│ > fun g()() = 1 :ftc staged module M with fun f() = x => x fun g()() = 1 //│ > fun ctor_() = () //│ > fun f() = //│ > let {tmp} //│ > tmp = new Function_ //│ > tmp //│ > fun g() = //│ > let {tmp1} //│ > tmp1 = new Function_1 //│ > tmp1 // TODO: these need to be printed, somehow: :noModuleCheck let x = M."f_instr"().body.rest.rhs.cls.l.value let y = M."g_instr"().body.rest.rhs.cls.l.value //│ x = class Function$ //│ y = class Function$ (new x).call(2) //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/block-staging/SymbolRenaming.mls ================================================ :js :staging staged module TempVariable with fun f() = let x let x x //│ > fun ctor_() = () //│ > fun f() = //│ > let {x, x1} //│ > x1 staged module DupeFunctionName with fun f() = 1 //│ > fun ctor_() = () //│ > fun f() = 1 class C(val x) staged module Selection with fun f(x) = x.call() C(1).x is Bool //│ > fun ctor_() = () //│ > fun f(x) = //│ > let {scrut, tmp} //│ > x.call() //│ > tmp = C(1) //│ > scrut = tmp.x //│ > if scrut is //│ > Bool then true //│ > else false staged module DupeClass with class C(val y) fun f() = C(1).y //│ > fun ctor_() = //│ > class C(y) //│ > fun f() = //│ > let {tmp} //│ > tmp = DupeClass.C(1) //│ > tmp.y ================================================ FILE: hkmc2/shared/src/test/mlscript/block-staging/Syntax.mls ================================================ :pt staged module A //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ Modified: //│ modifier = Keywrd of keyword 'staged' //│ body = TypeDef: //│ k = Mod //│ head = Ident of "A" // TODO: reject these annotations? staged object Foo staged class Foo staged fun f() = 0 :js :sir :staging staged module A //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A⁰; //│ define A⁰ as class A¹ //│ staged module A² { //│ constructor { //│ let tmp, tmp1; //│ set tmp = A².this.ctor$_instr﹖(); //│ set tmp1 = Block⁰.printCode﹖(tmp); //│ runtime⁰.Unit﹖ //│ } //│ method ctor$_instr⁰ = fun ctor$_instr¹() { //│ let end, tmp, tmp1, tmp2, tmp3; //│ set end = Block⁰.End﹖(); //│ set tmp = Block⁰.Symbol﹖("ctor$"); //│ set tmp1 = []; //│ set tmp2 = [tmp1]; //│ set tmp3 = Block⁰.FunDefn﹖(tmp, tmp2, end, true); //│ return tmp3 //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > fun ctor_() = () ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Arrays.mls ================================================ :js let empty = [] //│ empty = [] :re empty.0 //│ ═══[RUNTIME ERROR] Error: Access to required field '0' yielded 'undefined' :sjs let single = [1] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let single; single = globalThis.Object.freeze([ 1 ]); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ single = [1] single.0 //│ = 1 :sjs val single = [1] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let single1, tmp; tmp = globalThis.Object.freeze([ 1 ]); single1 = tmp; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ single = [1] single.0 //│ = 1 val pair = [1, 2] //│ pair = [1, 2] pair.0 //│ = 1 pair.1 //│ = 2 val triple = [1, 2, 3] //│ triple = [1, 2, 3] triple.2 //│ = 3 :re triple.3 //│ ═══[RUNTIME ERROR] Error: Access to required field '3' yielded 'undefined' :re triple.4 //│ ═══[RUNTIME ERROR] Error: Access to required field '4' yielded 'undefined' // * Array hacking (taken from Anto's original port of nofib, which was slightly deranged) set Array.prototype.toString = globalThis.eval("(function() { return '(' + Array.prototype.join.call(this, ',') + ')'; })") [1, 2, 3] //│ = [1, 2, 3] new Array(4) //│ = [<4 empty items>] // TODO also sanity-check these accesses :breakme :re empty.(0) :breakme :re empty.[0] empty.["oops"] ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls ================================================ :js :w @config(tailRecOpt: false) "hi" //│ ╔══[WARNING] This annotation has no effect. //│ ╟── This annotation is not supported on string literal terms. //│ ║ l.5: @config(tailRecOpt: false) "hi" //│ ╙── ^^^^ //│ = "hi" // TODO: report useless `tailRecOpt` config annotation on non-function term :breakme @config(tailRecOpt: false) val x = 1 //│ x = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadFunctions.mls ================================================ :js :fixme fun (let) oops = 1 //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead //│ ║ l.5: fun (let) oops = 1 //│ ╙── ^ //│ ╔══[COMPILATION ERROR] This ‹erroneous syntax› is not a valid symbolic name //│ ║ l.5: fun (let) oops = 1 //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadImport.mls ================================================ :js :e import 123 //│ ╔══[COMPILATION ERROR] Expected string literal after 'import' keyword //│ ║ l.5: import 123 //│ ╙── ^^^ // * Produces lots of output // import "fs" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadInit.mls ================================================ :js // Note: // This used to fail. // It now works, but allowing this to work requires nontrivial compilation // in a way that makes objects problematic to lift, in the lifter. // So, we might just want to drop objects altogether // and just use `val`s with more obvious initialization semantics (?) :sjs object Bar with val x = 1 module Baz with val a = Bar.x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Bar1; //│ (class Bar { //│ static { //│ new this //│ } //│ constructor() { //│ Bar1 = this; //│ this.x = 1; //│ const this$Bar = this; //│ (class Baz { //│ static { //│ this$Bar.Baz = this //│ } //│ static { //│ this.a = Bar1.x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Baz"]; //│ }); //│ Object.defineProperty(this, "class", { //│ value: Bar //│ }); //│ globalThis.Object.freeze(this); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["object", "Bar"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Modules don't have this initialization problem because they set their bindings // * as the first thing in their static ctors. // * Objects can't really do so because their (non-static) ctors could be called from child classes. :sjs module Bar with val x = 1 module Baz with val a = Bar.x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Bar3; //│ (class Bar2 { //│ static { //│ Bar3 = this //│ } //│ static { //│ this.x = 1; //│ (class Baz1 { //│ static { //│ Bar2.Baz = this //│ } //│ static { //│ this.a = Bar3.x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Baz"]; //│ }); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Bar"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadNew.mls ================================================ :js :pe :re new //│ ╔══[PARSE ERROR] Expected 'with' keyword, expression, or block after `new` keyword; found end of input instead //│ ║ l.6: new //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re new 2 //│ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. //│ ║ l.14: new 2 //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor :e :re new 2 + 2 //│ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. //│ ║ l.23: new 2 + 2 //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor :sjs :re new! 2 + 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp1; tmp1 = globalThis.Object.freeze(new 2()); tmp1 + 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor :e :re new() //│ ╔══[COMPILATION ERROR] Expected a statically known class; found unit value. //│ ║ l.40: new() //│ ║ ^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor :re new!() //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor :e :re new {} //│ ╔══[COMPILATION ERROR] Expected a statically known class; found unit value. //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor :pe new! {} //│ ╔══[PARSE ERROR] Expected expression after prefix keyword 'new!'; found end of block instead //│ ║ l.59: new! {} //│ ╙── ^^ // * Note that thanks to the parser "feature" for keyword-block stutter // * this used to parse as `((new x) = 1)`; // * it no longer does, since I make `new` accept `exprOrBlk` :e :pt new { x = 1 } //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ LexicalNew: //│ body = S of Block of Ls of //│ Def: //│ lhs = Ident of "x" //│ rhs = IntLit of 1 //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.70: new { x = 1 } //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Expected a statically known class; found block. //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e new! { x = 1 } //│ ╔══[COMPILATION ERROR] Unrecognized definitional assignment left-hand side: prefix operator 'new!' //│ ║ l.86: new! { x = 1 } //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e let xs = new Oopsie //│ ╔══[COMPILATION ERROR] Name not found: Oopsie //│ ║ l.94: let xs = new Oopsie //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ xs = undefined ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadOpen.mls ================================================ :js module Foo with val x = 1 :e (open Foo { x }) //│ ╔══[COMPILATION ERROR] Illegal position for 'open' statement. //│ ║ l.9: (open Foo { x }) //│ ╙── ^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e (((open Foo { x }))) //│ ╔══[COMPILATION ERROR] Illegal position for 'open' statement. //│ ║ l.16: (((open Foo { x }))) //│ ╙── ^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e (open Foo { x }) + 1 //│ ╔══[COMPILATION ERROR] Illegal position for 'open' statement. //│ ║ l.23: (open Foo { x }) + 1 //│ ╙── ^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e open Foo { y } //│ ╔══[COMPILATION ERROR] Module 'Foo' does not contain member 'y' //│ ║ l.30: open Foo { y } //│ ╙── ^ :sjs y //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo1.y //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— val Oops = "oops" //│ Oops = "oops" :e open Oops //│ ╔══[COMPILATION ERROR] Wildcard 'open' not supported for this kind of symbol. //│ ║ l.46: open Oops //│ ╙── ^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadSpreads.mls ================================================ :js :e ..., 1 //│ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. //│ ║ l.5: ..., 1 //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e fun f = ..., 1 //│ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. //│ ║ l.12: fun f = ..., 1 //│ ╙── ^^^ //│ = 1 :re [0, ...1] //│ ═══[RUNTIME ERROR] TypeError: 1 is not iterable :e fun foo(a, ...b, c) = [a, ...b, c] //│ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. //│ ║ l.25: fun foo(a, ...b, c) = [a, ...b, c] //│ ╙── ^^^^ :e fun foo(a, ...b, c) = [a, ...b] //│ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. //│ ║ l.31: fun foo(a, ...b, c) = [a, ...b] //│ ╙── ^^^^ :re foo(1, 2, 3) //│ ═══[RUNTIME ERROR] TypeError: b is not iterable fun foo(a, ...b) = [a, ...b] :e :re foo() //│ ╔══[COMPILATION ERROR] Expected at least 1 arguments, got 0 //│ ║ l.45: foo() //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Function 'foo' expected at least 1 argument but got 0 :e fun foo(1) = 2 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.53: fun foo(1) = 2 //│ ╙── ^ :e fun foo(...1) = 2 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found spread //│ ║ l.59: fun foo(...1) = 2 //│ ╙── ^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadThis.mls ================================================ :js globalThis.clearInterval //│ = fun clearInterval class A :re if 0 is A then 1 //│ ═══[RUNTIME ERROR] Error: match error // * Unhygienic binding... // * TODO: prevent rebinding of `globalThis` :re val globalThis = "oops" //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') :re globalThis.clearInterval //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') :re if 0 is A then 1 //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadValInit.mls ================================================ :js // TODO raise initialization error at CT and RT // TODO show undefined-valued defined bindings val a = b val b = 1 //│ a = undefined //│ b = 1 val a = () //│ a = () a ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BadWhile.mls ================================================ :js :e :sjs while {} do() //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'do' here //│ ║ l.6: while {} do() //│ ╙── ^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ throw "This code cannot be run as its compilation yielded an error." //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * Warn about pure expressions in statement position in `while` branches :w fun f() = let i = 10 while i > 0 do set i -= 1 i //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.22: i //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls ================================================ :js :global :sjs 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :lot :w 1 2 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.15: 1 //│ ╙── ^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 2 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.15: 1 //│ ╙── ^ //│ —————————————| Lowered IR Tree |———————————————————————————————————————————————————————————————————— //│ Program: //│ main = Return: //│ res = Lit of IntLit of 2 //│ implct = true //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 print("Hi") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Predef.print("Hi") //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hi :lot print("Hi") 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Predef.print("Hi"); 2 //│ —————————————| Lowered IR Tree |———————————————————————————————————————————————————————————————————— //│ Program: //│ main = Assign: //│ lhs = ‹no symbol›⁰ //│ rhs = Call: //│ fun = Select{sym=term:Predef⁰/print⁰}: //│ qual = Ref{disamb=module:Predef⁰}: //│ l = member:Predef¹ //│ disamb = S of module:Predef⁰ //│ name = Ident of "print" //│ argss = Ls of //│ Ls of //│ Arg: //│ value = Lit of StrLit of "Hi" //│ rest = Return: \ //│ res = Lit of IntLit of 2 //│ implct = true //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hi //│ = 2 :re 2(2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(2(2)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: 2 is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Bindings.mls ================================================ :js // :sjs // :elt // :lot let x = 1 x + 1 //│ = 2 //│ x = 1 x //│ = 1 let x = 2 //│ x = 2 x //│ = 2 let x = 1 let y = x x + y //│ = 2 //│ x = 1 //│ y = 1 let x = let y = 2 in y + 1 x * 2 //│ = 6 //│ x = 3 let x = let x = 2 in x + 1 x * 2 //│ = 6 //│ x = 3 let x = 1 //│ x = 1 fun incr(n) = n + 1 fun (|>) pipe(x, f) = f(x) let x = x |> incr //│ x = 2 let x |>= incr //│ x = 3 :e let 2 //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.55: let 2 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e let x += 1 = 0 //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.62: let x += 1 = 0 //│ ╙── ^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e let x += 1 = 0 in x //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.69: let x += 1 = 0 in x //│ ╙── ^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e (let x = 1) //│ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position //│ ║ l.77: (let x = 1) //│ ╙── ^^^^^ :e (let x = 1) + 1 //│ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position //│ ║ l.83: (let x = 1) + 1 //│ ╙── ^^^^^ //│ = "()1" :e (let f(x) = x) + 1 //│ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position //│ ║ l.90: (let f(x) = x) + 1 //│ ╙── ^^^^^^^^ //│ = "()1" (let f(x) = x + 1 in f(42)) + 1 //│ = 44 let x = 1 in console.log(x); x //│ > 1 //│ = 1 let x = 1 in console.log(x) console.log(x) //│ > 1 //│ > 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls ================================================ :js :sir //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ import Predef; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— #config(commentGeneratedCode: true) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— 2 + 2 + 2 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let tmp; set tmp = +⁰(2, 2); +⁰(tmp, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 :lot let x = 1 x + 1 //│ —————————————| Lowered IR Tree |———————————————————————————————————————————————————————————————————— //│ Program: //│ main = Scoped(syms = {x⁰}): //│ body = Assign: //│ lhs = x⁰ //│ rhs = Lit of IntLit of 1 //│ rest = Return: \ //│ res = Call: //│ fun = Ref: //│ l = builtin:+⁰ //│ argss = Ls of //│ Ls of //│ Arg: //│ value = Ref: //│ l = x⁰ //│ Arg: //│ value = Lit of IntLit of 1 //│ implct = true //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x⁰; set x⁰ = 1; +⁰(x⁰, 1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 //│ x = 1 x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ x⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 let x = 1 x + 1 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x¹; set x¹ = 1; +⁰(x¹, 1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 //│ x = 1 // * Notice that the IR has two symbols: the BlockMemberSymbol x² but also the more specific TermSymbol x³. // * This is because x² could be overloaded and contain other definitions, such as a type definition. // * In the end, the JS just uses one variable. :sjs val x = 1 x + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x2; x2 = 1; x2 + 1 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x²; define x² as val x³ = 1; +⁰(x³, 1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 //│ x = 1 case [] then print("empty") [x, ...xs] then print("non-empty", x) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁰; //│ define lambda⁰ as fun lambda¹(caseScrut) { //│ let x, xs, middleElements, element0$; //│ match caseScrut //│ Array(0) => //│ return Predef⁰.print⁰("empty") //│ Array(1+) => //│ set element0$ = runtime⁰.Tuple﹖.get⁰(caseScrut, 0); //│ set middleElements = runtime⁰.Tuple﹖.slice⁰(caseScrut, 1, 0); //│ set xs = middleElements; //│ set x = element0$; //│ return Predef⁰.print⁰("non-empty", x) //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ lambda¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun fun f(x) = let y = 1 in x + y //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰; define f⁰ as fun f¹(x) { let y; set y = 1; return +⁰(x, y) }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun f(x) = let y = 1 in x + y //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f²; define f² as fun f³(x) { let y; set y = 1; return +⁰(x, y) }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun incr(n) = n + 1 fun (|>) pipe(x, f) = f(x) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let incr⁰, pipe⁰; //│ define incr⁰ as fun incr¹(n) { //│ return +⁰(n, 1) //│ }; //│ define pipe⁰ as fun pipe¹(x, f) { return f(x) }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let x = 1 let x = if x == 0 then 1 else 0 let x = x + 1 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x⁴, x⁵, scrut, x⁶, tmp; //│ set x⁴ = 1; //│ set scrut = Predef⁰.equals⁰(x⁴, 0); //│ match scrut //│ true => //│ set tmp = 1; //│ end //│ else //│ set tmp = 0; //│ end //│ set x⁵ = tmp; //│ set x⁶ = +⁰(x⁵, 1); //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ x = 1 class C(x) C(123) new C(123) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let C⁰; //│ define C⁰ as class C²(x) { //│ private val x⁷; //│ constructor C¹ { set x⁷ = x; end } //│ }; //│ do C¹(123); //│ new C²(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = C(_) module A with val x = 2 let y = 3 class C(x) with fun f(x) = 2 fun g(x) = 3 A.x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A⁰; //│ define A⁰ as class A¹ //│ module A² { //│ private val y⁰; //│ val x⁸; //│ constructor { //│ define x⁸ as val x⁹ = 2; //│ set y⁰ = 3; //│ define C³ as class C⁵(x) { //│ private val x¹⁰; //│ constructor C⁴ { //│ set x¹⁰ = x; //│ end //│ } //│ method f⁴ = fun f⁵(x1) { return 2 } //│ }; //│ end //│ } //│ method g⁰ = fun g¹(x) { return 3 } //│ }; //│ A².x⁹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 module A with val x = 3 A.x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A³; //│ define A³ as class A⁴ //│ module A⁵ { val x¹¹; constructor { define x¹¹ as val x¹² = 3; end } }; //│ A⁵.x¹² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 class A class B(x) extends A with val y = 2 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A⁶, B⁰; //│ define A⁶ as class A⁷; //│ define B⁰ as class B²(x) { //│ private val x¹³; //│ val y¹; //│ constructor B¹ { super⁰(); set x¹³ = x; define y¹ as val y² = 2; end } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— pattern P = B //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let P⁰; //│ define P⁰ as pattern P¹ { //│ method unapply⁰ = fun unapply¹(input) { //│ match input //│ B² => //│ return new runtime⁰.MatchSuccess⁰(input, null) //│ else //│ return new runtime⁰.MatchFailure⁰(null) //│ end //│ } //│ method unapplyStringPrefix⁰ = fun unapplyStringPrefix¹(input) { //│ return new runtime⁰.MatchFailure⁰(null) //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— object A with fun f = 42 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let A⁸; define A⁸ as object A⁹ { method f⁶ = fun f⁷ { return 42 } }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let x = [1, 2, 3] x.0 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x¹⁴, selRes; //│ set x¹⁴ = [1, 2, 3]; //│ set selRes = x¹⁴.0﹖; //│ do x¹⁴.0$__checkNotMethod﹖; //│ match selRes //│ undefined => //│ throw new globalThis⁰.Error﹖("Access to required field '0' yielded 'undefined'") //│ else //│ selRes //│ end /* (Unreachable:) rest of abortive match */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 //│ x = [1, 2, 3] let y = { x: 2 } y!x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let y³, x¹⁵; set x¹⁵ = 2; set y³ = { "x": x¹⁵ }; y³!"x" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 //│ y = {x: 2} fun foo = class A new A //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo⁰; define foo⁰ as fun foo¹() { let A; define A as class A¹⁰; return new A¹⁰() }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls ================================================ :js :sjs 2 + 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 2 + 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 4 :sjs 2 +. 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 2 + 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 4 :sjs +2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ + 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 + //│ = fun // * A bit confusing... but at least there's a warning! :w +(2, 3) //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.30: +(2, 3) //│ ╙── ^ //│ = 3 *(2, 3) //│ = 6 :ge *(1) //│ ╔══[COMPILATION ERROR] Builtin '*' is not a unary operator //│ ║ l.40: *(1) //│ ╙── ^^^^ //│ ═══[COMPILATION ERROR] Cannot call non-unary builtin symbol '*' //│ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' (+) //│ = fun (+)(2, 3) //│ = 5 (*)(2, 3) //│ = 6 :ge (*)(2) //│ ╔══[COMPILATION ERROR] Builtin '*' is not a unary operator //│ ║ l.57: (*)(2) //│ ╙── ^^^^^^ //│ ═══[COMPILATION ERROR] Cannot call non-unary builtin symbol '*' //│ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' id(+) //│ = fun id(+)(1, 2) //│ = 3 (+ 1) //│ = 1 (+)(1) //│ = 1 :sjs :re id(+)(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda4, baseCall1; //│ lambda4 = (undefined, function (arg1, arg2) { //│ return arg1 + arg2 //│ }); //│ baseCall1 = Predef.id(lambda4); //│ runtime.safeCall(baseCall1(1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 fun (+) lol(a, b) = [a, b] :sjs 1 + 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ lol(1, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [1, 2] :sjs id(~)(2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda5, baseCall2; //│ lambda5 = (undefined, function (arg) { //│ return ~ arg //│ }); //│ baseCall2 = Predef.id(lambda5); //│ runtime.safeCall(baseCall2(2)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = -3 2 |> ~ //│ = -3 typeof //│ = fun typeof(1) //│ = "number" typeof("a") //│ = "string" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/BuiltinSymbols.mls ================================================ :js // * Prevent explicit accesses to Unit, Int etc. from Prelude or elaborate them :ge Int //│ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term //│ ║ l.7: Int //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge Str //│ ╔══[COMPILATION ERROR] Symbol 'Str' is virtual (i.e., "compiler fiction"); cannot be used as a term //│ ║ l.14: Str //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge Bool //│ ╔══[COMPILATION ERROR] Symbol 'Bool' is virtual (i.e., "compiler fiction"); cannot be used as a term //│ ║ l.21: Bool //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :soir (case Bool then 1)(true) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let lambda⁰, caseScrut⁰, inlinedVal; //│ set caseScrut⁰ = true; //│ match caseScrut⁰ //│ Bool⁰ => //│ set inlinedVal = 1; //│ inlinedVal //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls ================================================ :js data class Some[out A](value: A) object None :sjs fun test(x) = if (if x is Some(v) then Some(v + 1) None then None ) is Some(v) then print(v) None then print("none") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test; //│ test = function test(x) { //│ let v, scrut, v1, arg$Some$0$, arg$Some$0$1, tmp, tmp1; //│ if (x instanceof Some1.class) { //│ arg$Some$0$1 = x.value; //│ v = arg$Some$0$1; //│ tmp = v + 1; //│ tmp1 = Some1(tmp); //│ } else if (x instanceof None1.class) { //│ tmp1 = None1; //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ scrut = tmp1; //│ if (scrut instanceof Some1.class) { //│ arg$Some$0$ = scrut.value; //│ v1 = arg$Some$0$; //│ return Predef.print(v1) //│ } else if (scrut instanceof None1.class) { //│ return Predef.print("none") //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls ================================================ :js case x then x //│ = fun :sjs case { x then x } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; lambda1 = (undefined, function (caseScrut) { let x; x = caseScrut; return x }); lambda1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :sjs x => if x is 0 then true //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda2; //│ lambda2 = (undefined, function (x) { //│ if (x === 0) { //│ return true //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ lambda2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :sjs case 0 then true //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda3; //│ lambda3 = (undefined, function (caseScrut) { //│ if (caseScrut === 0) { //│ return true //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ lambda3 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // * Note: `case`/`do` works analogously to `if`/`do` :sjs case 0 do print(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda4; //│ lambda4 = (undefined, function (caseScrut) { //│ if (caseScrut === 0) { Predef.print(1); return runtime.Unit } //│ return runtime.Unit; //│ }); //│ lambda4 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun (case x then x) of 1 //│ = 1 1 |> case x then x //│ = 1 1 |> id of case x then x //│ = 1 [1] |> case [x] then x //│ = 1 1 |> (2 |> case x then case y then [x, y]) |> case [a, b] then [b, a] //│ = [1, 2] :todo // TODO: support this braceless syntax? case [x] then x, [] then 0 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.75: case [x] then x, [] then 0 //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.75: case [x] then x, [] then 0 //│ ╙── ^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs case 0 then true _ then false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda11; //│ lambda11 = (undefined, function (caseScrut1) { //│ if (caseScrut1 === 0) { return true } //│ return false; //│ }); //│ lambda11 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun data class Some(value) object None :sjs val isDefined = case Some then true None then false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let isDefined, lambda12; //│ lambda12 = (undefined, function (caseScrut1) { //│ if (caseScrut1 instanceof Some1.class) { //│ return true //│ } else if (caseScrut1 instanceof None1.class) { //│ return false //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ isDefined = lambda12; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ isDefined = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls ================================================ :js :sjs data class Outer(a, b) with data class Inner(c) with fun i1(d) = [b, c, d] print(a) print(c) print(i1(a)) fun o1(c) = Inner(c) fun o2(c, d) = Inner(c).i1(d) val i = Inner(a) print(i.c) print(i.i1(1)) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Outer1; //│ Outer1 = function Outer(a, b) { //│ return globalThis.Object.freeze(new Outer.class(a, b)); //│ }; //│ (class Outer { //│ static { //│ Outer1.class = this //│ } //│ constructor(a, b) { //│ let tmp, tmp1; //│ this.a = a; //│ this.b = b; //│ const this$Outer = this; //│ this.Inner = function Inner(c) { //│ return globalThis.Object.freeze(new Inner.class(c)); //│ }; //│ (class Inner { //│ static { //│ this$Outer.Inner.class = this //│ } //│ constructor(c) { //│ let tmp2; //│ this.c = c; //│ Predef.print(this$Outer.a); //│ Predef.print(this.c); //│ tmp2 = this.i1(this$Outer.a); //│ Predef.print(tmp2); //│ } //│ i1(d) { //│ return globalThis.Object.freeze([ //│ this$Outer.b, //│ this.c, //│ d //│ ]) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Inner", ["c"]]; //│ }); //│ tmp = this.Inner(this.a); //│ this.i = tmp; //│ Predef.print(this.i.c); //│ tmp1 = runtime.safeCall(this.i.i1(1)); //│ Predef.print(tmp1); //│ } //│ o1(c) { //│ return this.Inner(c) //│ } //│ o2(c, d) { //│ let tmp; //│ tmp = this.Inner(c); //│ return tmp.i1(d) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Outer", ["a", "b"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— val o = Outer(100, 200) //│ > 100 //│ > 100 //│ > [200, 100, 100] //│ > 100 //│ > [200, 100, 1] //│ o = Outer(100, 200) o.o1(30) //│ > 100 //│ > 30 //│ > [200, 30, 100] //│ = Inner(30) o.o2(30, 40) //│ > 100 //│ > 30 //│ > [200, 30, 100] //│ = [200, 30, 40] o.i //│ = Inner(100) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls ================================================ :js :sjs fun test(a) = class C with { val x = a } new C //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test; //│ test = function test(a) { //│ let C1; //│ (class C { //│ static { //│ C1 = this //│ } //│ constructor() { //│ this.x = a; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C"]; //│ }); //│ return globalThis.Object.freeze(new C1()) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— test(12) //│ = C { x: 12 } fun test(x) = data class Foo(x) Foo(x) test(3) //│ = Foo(3) :sjs fun test(x) = data class Foo(a, b) Foo(x, x + 1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test2; //│ test2 = function test(x) { //│ let Foo2, tmp; //│ Foo2 = function Foo(a, b) { //│ return globalThis.Object.freeze(new Foo.class(a, b)); //│ }; //│ (class Foo1 { //│ static { //│ Foo2.class = this //│ } //│ constructor(a, b) { //│ this.a = a; //│ this.b = b; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["a", "b"]]; //│ }); //│ tmp = x + 1; //│ return Foo2(x, tmp) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— test(123) //│ = Foo(123, 124) // * Forgot to pass the arg: :e :re test() //│ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 //│ ║ l.71: test() //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Function 'test' expected 1 argument but got 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls ================================================ :js data class Some[out A](value: A) object None :sjs if Some(0) is Some(x) then x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut, x, arg$Some$0$; //│ scrut = Some1(0); //│ if (scrut instanceof Some1.class) { //│ arg$Some$0$ = scrut.value; //│ x = arg$Some$0$; //│ x //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 let s = Some(0) //│ s = Some(0) :sjs if s is Some(x) then x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x1, arg$Some$0$1; //│ if (s instanceof Some1.class) { //│ arg$Some$0$1 = s.value; //│ x1 = arg$Some$0$1; //│ x1 //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 if s is Some(x) then x None then 1 //│ = 0 :sir :soir :re if s is Some(1) then 0 None then 1 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let arg$Some$0$, tmp; //│ block split_root$: //│ block split_default$: //│ match s⁰ //│ Some⁰ => //│ set arg$Some$0$ = s⁰.value⁰; //│ match arg$Some$0$ //│ 1 => //│ set tmp = 0; //│ break split_root$ //│ else //│ break split_default$ //│ end //│ None⁰ => //│ set tmp = 1; //│ break split_root$ //│ else //│ break split_default$ //│ end //│ throw new globalThis⁰.Error⁰("match error") //│ tmp //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let arg$Some$0$, tmp; //│ match s⁰ //│ Some⁰ => //│ set arg$Some$0$ = s⁰.value⁰; //│ match arg$Some$0$ //│ 1 => //│ begin //│ set tmp = 0; //│ end; //│ tmp //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ None⁰ => //│ set tmp = 1; //│ tmp //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error :expect 0 if s is Some then s.value //│ = 0 :sjs x => if x is Some(x) then x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; //│ lambda = (undefined, function (x3) { //│ let x4, arg$Some$0$4; //│ if (x3 instanceof Some1.class) { //│ arg$Some$0$4 = x3.value; //│ x4 = arg$Some$0$4; //│ return x4 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun data class C(a) fun f(x) = if x is C(a) then a else 0 f(C(27)) //│ = 27 fun f(x) = if x is C then 1 else 0 f(C(27)) //│ = 1 object M with data class D(a) val d = D(27) M.d //│ = D(27) fun f(x) = if x is M.D then 1 else 0 M.D.class //│ = class D M is M.D //│ = false f(M.d) //│ = 1 f(M.D(27)) //│ = 1 fun f(x) = if x is M.D(a) then a else 0 f(M.d) //│ = 27 f(M.D(27)) //│ = 27 // TODO dedup branches duplicated by UCS normalization :sjs fun f(x) = if x is Some(x) and x > 0 then 42 None then "ok" else print("oops") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f4; //│ f4 = function f(x3) { //│ let x4, scrut1, arg$Some$0$4; //│ if (x3 instanceof Some1.class) { //│ arg$Some$0$4 = x3.value; //│ x4 = arg$Some$0$4; //│ scrut1 = x4 > 0; //│ if (scrut1 === true) { //│ return 42 //│ } //│ return Predef.print("oops"); //│ } else if (x3 instanceof None1.class) { return "ok" } //│ return Predef.print("oops"); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(Some(0)) //│ > oops f(Some(1)) //│ = 42 f(None) //│ = "ok" f(0) //│ > oops data class Pair[out A, out B](fst: A, snd: B) :sjs fun f(x) = if x is Some(u) then u Pair(a, b) then a + b //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f5; //│ f5 = function f(x3) { //│ let u, a, b, arg$Pair$0$, arg$Pair$1$, arg$Some$0$4; //│ if (x3 instanceof Some1.class) { //│ arg$Some$0$4 = x3.value; //│ u = arg$Some$0$4; //│ return u //│ } else if (x3 instanceof Pair1.class) { //│ arg$Pair$0$ = x3.fst; //│ arg$Pair$1$ = x3.snd; //│ b = arg$Pair$1$; //│ a = arg$Pair$0$; //│ return a + b //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(Some(123)) //│ = 123 f(Pair(10, 5)) //│ = 15 // UCS term not in tail position :sjs fun f(x) = print of if x is Some(0) then "0" None then "ok" else "oops" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; //│ f6 = function f(x3) { //│ let arg$Some$0$4, tmp9; //│ if (x3 instanceof Some1.class) { //│ arg$Some$0$4 = x3.value; //│ if (arg$Some$0$4 === 0) { //│ tmp9 = "0"; //│ return Predef.print(tmp9) //│ } //│ tmp9 = "oops"; //│ return Predef.print(tmp9); //│ } else if (x3 instanceof None1.class) { //│ tmp9 = "ok"; //│ return Predef.print(tmp9) //│ } //│ tmp9 = "oops"; //│ return Predef.print(tmp9); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(Some(0)) //│ > 0 f(Some(1)) //│ > oops f(None) //│ > ok f(0) //│ > oops ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Classes.mls ================================================ :js :global :sjs data class Foo(arguments) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo(arguments1) { //│ return globalThis.Object.freeze(new Foo.class(arguments1)); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ constructor(arguments1) { //│ this.arguments = arguments1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["arguments"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— data class Foo(eval) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo3; //│ Foo3 = function Foo(eval1) { //│ return globalThis.Object.freeze(new Foo.class(eval1)); //│ }; //│ (class Foo2 { //│ static { //│ Foo3.class = this //│ } //│ constructor(eval1) { //│ this.eval = eval1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["eval"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— data class Foo(static) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo5; //│ Foo5 = function Foo(static1) { //│ return globalThis.Object.freeze(new Foo.class(static1)); //│ }; //│ (class Foo4 { //│ static { //│ Foo5.class = this //│ } //│ constructor(static1) { //│ this.static = static1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["static"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— data class Foo(v') //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo7; //│ Foo7 = function Foo(v$_) { //│ return globalThis.Object.freeze(new Foo.class(v$_)); //│ }; //│ (class Foo6 { //│ static { //│ Foo7.class = this //│ } //│ constructor(v$_) { //│ this["v'"] = v$_; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["v'"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/CodegenScratch.mls ================================================ :js :global :sjs :sir ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Comma.mls ================================================ :js :global :sjs fun f() = console.log("ok"), 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; f = function f() { runtime.safeCall(globalThis.console.log("ok")); return 42 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun f() = { console.log("ok"), 42 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; f1 = function f() { runtime.safeCall(globalThis.console.log("ok")); return 42 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun f() = console.log("ok"), 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2; f2 = function f() { return runtime.safeCall(globalThis.console.log("ok")) }; 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ConfigDirective.mls ================================================ :js // Test 1: #config global directive - test_before has tailRecOpt (default) :sir fun test_before(n, acc) = if n == 0 then acc else test_before(n - 1, n + acc) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let test_before⁰; //│ define test_before⁰ as fun test_before¹(n, acc) { //│ loop loopLabel: //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return acc //│ else //│ set tmp = -⁰(n, 1); //│ set tmp1 = +⁰(n, acc); //│ set n = tmp; //│ set acc = tmp1; //│ continue loopLabel //│ end //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— #config(tailRecOpt: false) // Test 2: After #config(tailRecOpt: false), functions should NOT be tail-rec optimized :sir fun test_after(n, acc) = if n == 0 then acc else test_after(n - 1, n + acc) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let test_after⁰; //│ define test_after⁰ as fun test_after¹(n, acc) { //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return acc //│ else //│ set tmp = -⁰(n, 1); //│ set tmp1 = +⁰(n, acc); //│ return test_after¹(tmp, tmp1) //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Test 3: Re-enable tail recursion optimization #config(tailRecOpt: true) :sir fun test_reenabled(n, acc) = if n == 0 then acc else test_reenabled(n - 1, n + acc) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let test_reenabled⁰; //│ define test_reenabled⁰ as fun test_reenabled¹(n, acc) { //│ loop loopLabel: //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return acc //│ else //│ set tmp = -⁰(n, 1); //│ set tmp1 = +⁰(n, acc); //│ set n = tmp; //│ set acc = tmp1; //│ continue loopLabel //│ end //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Test 4: @config local annotation - disable tailRecOpt for one function only :sir @config(tailRecOpt: false) fun foo(n, acc) = if n == 0 then acc else foo(n - 1, n + acc) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo⁰; //│ define foo⁰ as fun foo¹(n, acc) { //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return acc //│ else //│ set tmp = -⁰(n, 1); //│ set tmp1 = +⁰(n, acc); //│ return foo¹(tmp, tmp1) //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Test 5: bar (no annotation) should still have tailRecOpt :sir fun bar(n, acc) = if n == 0 then acc else bar(n - 1, n + acc) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let bar⁰; //│ define bar⁰ as fun bar¹(n, acc) { //│ loop loopLabel: //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return acc //│ else //│ set tmp = -⁰(n, 1); //│ set tmp1 = +⁰(n, acc); //│ set n = tmp; //│ set acc = tmp1; //│ continue loopLabel //│ end //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Make sure effect handlers with stack safety can be enabled in one go: // * Here, handlers are not enabled :sir fun foo(x) = { x.call(), print(42) } //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo²; define foo² as fun foo³(x) { do x.call﹖(); return Predef⁰.print⁰(42) }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— #config(effectHandlers: Some(EffectHandlers())) handle h = Object with {} print(h) //│ //│ > Handler$h$ #config(effectHandlers: Some(EffectHandlers(stackSafety: Some(StackSafety(100))))) #config(liftDefns: Some(LiftDefns)) // * Needed by effect handlers instrumentation // * Now, they are fun foo(x) = { x.call(), print(42) } handle h = Object with fun call()(resume) = "Aborted!" foo(h) //│ = "Aborted!" print(42) #config(liftDefns: None) //│ //│ > 42 #config(deadParamElim: Some(DeadParamElim(debug: true, mono: true)), inlining: None) private fun deadParamConfig(a) = 0 deadParamConfig(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun deadParamConfig#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 #config(deadParamElim: None) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< #config(deforest: Some(Deforest(mono: false))) #config(effectHandlers: Some(EffectHandlers(stackSafety: None))) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< data class Pair(a, b) fun fstConfig(p) = if p is Pair(x, y) then x fstConfig(Pair(1, 2)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(a)) //│ deforest > Select(Ref(p,None),Ident(b)) //│ deforest > <<< fusing <<< //│ = 1 #config(deforest: None) fun fstConfig(p) = if p is Pair(x, y) then x fstConfig(Pair(1, 2)) //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ConsoleLog.mls ================================================ :js :global :sjs :silent declare val console //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— console.log("a") console.log("b") 123 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(globalThis.console.log("a")); runtime.safeCall(globalThis.console.log("b")); 123 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > a //│ > b //│ = 123 let l = console.log l(123) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let l; l = globalThis.console.log; runtime.safeCall(l(123)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 123 //│ l = fun log 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 console.log("a") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(globalThis.console.log("a")) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > a let x = console.log("a") 123 console.log("b") let y = x + 1 console.log("c") y * 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x, y; //│ runtime.safeCall(globalThis.console.log("a")); //│ x = 123; //│ runtime.safeCall(globalThis.console.log("b")); //│ y = x + 1; //│ runtime.safeCall(globalThis.console.log("c")); //│ y * 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > a //│ > b //│ > c //│ = 248 //│ x = 123 //│ y = 124 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls ================================================ :js // * The code-gen of curried classes is currently a bit crazy and unnecessary. // * It was intended to make interop better, but it turned out to be a surprising semantic footgun, // * as partially-applied class constructors will share the same "this" object, resulting in unexpected mutations! // * TODO: Compile differently and forbid the use of curried classes as first-class values. :sjs class A(val x)(val y) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let A1; //│ A1 = function A(x) { //│ return (y) => { //│ return globalThis.Object.freeze(new A.class(x)(y)); //│ } //│ }; //│ (class A { //│ static { //│ A1.class = this //│ } //│ constructor(x) { //│ return (y) => { //│ this.x = x; //│ this.y = y; //│ return this; //│ } //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let w = new A(1) //│ w = fun w let h = w(2) h.y //│ = 2 //│ h = A(1) :expect 3 let h2 = w(3) h2.y //│ = 3 //│ h2 = A(1) // * This used to return 3, but we now make sure instantiations are always saturated :expect 2 h.y //│ = 2 // * First-class class value; FIXME: should be forbidden let a = A.class //│ a = class A // * Dynamic instantiation let w = new! a(1) //│ w = fun let h = w(2) h.y //│ = 2 //│ h = A(1) :expect 3 let h2 = w(3) h2.y //│ = 3 //│ h2 = A(1) :re // * Bad bad bad! :expect 2 h.y //│ ═══[RUNTIME ERROR] Expected: '2', got: '3' //│ = 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls ================================================ :js :soir fun foo(x)(y) = z => x + y + z foo(1)(2)(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let foo⁰, baseCall; //│ define foo⁰ as fun foo¹(x)(y) { //│ let lambda; //│ define lambda as fun lambda⁰(z) { //│ let tmp; //│ set tmp = +⁰(x, y); //│ return +⁰(tmp, z) //│ }; //│ return lambda⁰ //│ }; //│ set baseCall = foo¹(1)(2); //│ baseCall(3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 :sir :soir :lift fun foo(x)(y) = z => x + y + z foo(1)(2)(3) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo², baseCall, lambda¹, lambda$⁰; //│ define lambda$⁰ as fun lambda$¹(x, y)(z) { //│ return lambda²(x, y, z) //│ }; //│ define lambda¹ as fun lambda²(x, y, z) { //│ let tmp; //│ set tmp = +⁰(x, y); //│ return +⁰(tmp, z) //│ }; //│ define foo² as fun foo³(x)(y) { //│ let lambda$here; //│ set lambda$here = lambda$¹(x, y); //│ return lambda$here //│ }; //│ set baseCall = foo³(1)(2); //│ baseCall(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let foo², baseCall, lambda¹, lambda$⁰; //│ define lambda$⁰ as fun lambda$¹(x, y)(z) { //│ let x1, y1, z1, inlinedVal, tmp; //│ set x1 = x; //│ set y1 = y; //│ set z1 = z; //│ set tmp = +⁰(x1, y1); //│ set inlinedVal = +⁰(tmp, z1); //│ return inlinedVal //│ }; //│ define foo² as fun foo³(x)(y) { //│ let lambda$here; //│ set lambda$here = lambda$¹(x, y); //│ return lambda$here //│ }; //│ set baseCall = foo³(1)(2); //│ baseCall(3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 fun loudly(x) = print("hi", x) x fun foo(x)(y) = print("hello") z => print("world") a => print(x, y, z, a) // * Notice that the side effects should happen in the correct order :sir foo(loudly(1))(loudly(2))(loudly(3))(loudly(4)) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let tmp, tmp1, baseCall, tmp2, callPrefix, tmp3; //│ set tmp = loudly⁰(1); //│ set tmp1 = loudly⁰(2); //│ set baseCall = foo⁴(tmp)(tmp1); //│ set tmp2 = loudly⁰(3); //│ set callPrefix = baseCall(tmp2); //│ set tmp3 = loudly⁰(4); //│ callPrefix(tmp3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi 1 //│ > hi 2 //│ > hello //│ > hi 3 //│ > world //│ > hi 4 //│ > 1 2 3 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls ================================================ :js :global :ssjs let x //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let x; //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ x = undefined print(x) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res2 = runtime.checkCall(Predef.print(x)); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > undefined x = 1 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ x = 1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— x //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res4 = x; undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // TODO forbid redefining a let x = 2 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ x = 2; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— x //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res6 = x; undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 let y = 1 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let y; //│ y = 1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ y = 1 :e z = 1 //│ ╔══[COMPILATION ERROR] Name not found: z //│ ║ l.52: z = 1 //│ ╙── ^ //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ throw "This code cannot be run as its compilation yielded an error." //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun f() = 1 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f; //│ f = function f() { //│ runtime.checkArgs("f", 0, true, arguments.length); //│ return 1 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res10 = f; undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun f let f f(x) = x + 1 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f1, f2; //│ f2 = function f(x1) { //│ runtime.checkArgs("f", 1, true, arguments.length); //│ return x1 + 1 //│ }; //│ f1 = f2; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f f(1) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res12 = runtime.safeCall(f1(1)); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 let foo foo = 0 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let foo; //│ foo = 0; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = 0 :fixme let foo if true then foo = 0 else foo = 1 //│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead //│ ║ l.108: then foo = 0 //│ ╙── ^ //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let foo1, scrut; //│ scrut = true; //│ if (scrut === true) { //│ block$res14 = undefined; //│ undefined //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = undefined let foo if true then foo = 0 else foo = 1 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let foo2, scrut1; //│ scrut1 = true; //│ if (scrut1 === true) { //│ foo2 = 0; //│ block$res15 = runtime.Unit; //│ undefined //│ } else { foo2 = 1; block$res15 = runtime.Unit; undefined } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = 0 fun f() = foo = 42 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f3; //│ f3 = function f() { //│ runtime.checkArgs("f", 0, true, arguments.length); //│ foo2 = 42; //│ return runtime.Unit //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f() //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res17 = runtime.checkCall(f3()); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— foo //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res18 = foo2; undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 :fixme fun f() = foo = 0 //│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead //│ ║ l.165: fun f() = foo = 0 //│ ╙── ^ //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f4; //│ f4 = function f() { //│ runtime.checkArgs("f", 0, true, arguments.length); //│ return foo2 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Do.mls ================================================ :js :elt do () //│ —————————————| Elaborated tree |———————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Blk: //│ stats = Ls of //│ UnitVal //│ res = UnitVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— do console.log("ok") //│ > ok :w do 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.18: do 1 //│ ╙── ^ do let hello = 1 () :re print(globalThis.hello) //│ ═══[RUNTIME ERROR] Error: Access to required field 'hello' yielded 'undefined' :ucs desugared val f = case 0 then "null" do console.log("non-null") 1 then "unit" let res = "other" do print(res) _ then res //│ Split with nested patterns: //│ > case //│ > caseScrut is 0 then "null" //│ > let tmp:unused = member:console⁰.log‹member:log›("non-null") //│ > caseScrut is 1 then "unit" //│ > let res = "other" //│ > let tmp:unused = (member:Predef⁰.)print‹member:print›(res⁰) //│ > caseScrut is _ then res⁰ //│ Expanded split with flattened patterns: //│ > ‹if|while› //│ > caseScrut is 0 then "null" //│ > let tmp:unused = member:console⁰.log‹member:log›("non-null") //│ > caseScrut is 1 then "unit" //│ > let res = "other" //│ > let tmp:unused = (member:Predef⁰.)print‹member:print›(res⁰) //│ > else res⁰ //│ f = fun f(0) //│ = "null" f(1) //│ > non-null //│ = "unit" f(2) //│ > non-null //│ > other //│ = "other" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls ================================================ :js fun f() = print("--- pre ---") return 1 print("--- post ---") f() //│ > --- pre --- //│ = 1 :sjs fun f(x) = if x < 0 do print("whoops") return 0 x + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; //│ f1 = function f(x) { //│ let scrut; //│ scrut = x < 0; //│ if (scrut === true) { Predef.print("whoops"); return 0 } //│ return x + 1; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(1) //│ = 2 f(-1) //│ > whoops //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ElseLess.mls ================================================ :js data class Foo(a) :todo // Treat else-less single-branch UCS as implicitly returning () in default case if Foo(0) is Foo(x) and x > 0 then print("ok") //│ ═══[RUNTIME ERROR] Error: match error let x = 1 //│ x = 1 :todo if x > 0 and x < 0 then print("wat") //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls ================================================ :js data class Foo(val x) with val y = x // TODO add symbol to this Sel :elt Foo(42).x //│ —————————————| Elaborated tree |———————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Sel: //│ prefix = App: //│ lhs = Ref{sym=member:Foo⁰} of member:Foo⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Lit of IntLit of 42 //│ nme = Ident of "x" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 let foo = Foo(42) //│ foo = Foo(42) // TODO add symbol to this Sel :elt foo.x //│ —————————————| Elaborated tree |———————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Sel: //│ prefix = Ref{sym=foo⁰} of foo⁰ //│ nme = Ident of "x" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // TODO add symbol to this Sel :elt foo.y //│ —————————————| Elaborated tree |———————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Sel: //│ prefix = Ref{sym=foo⁰} of foo⁰ //│ nme = Ident of "y" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // * Notice that the `.x` selection has a symbol, propagated to the lowered tree, as expected :elt :sir case Foo(a) then a //│ —————————————| Elaborated tree |———————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Lam: //│ params = ParamList: //│ params = Ls of //│ Param: //│ sym = caseScrut⁰ //│ modulefulness = Modulefulness of N //│ body = IfLike: //│ kw = keyword 'case' //│ form = ReturningIf //│ split = Cons: //│ branch = Match: //│ scrutinee = Ref{sym=caseScrut⁰} of caseScrut⁰ //│ pattern = Constructor: //│ target = Ref{sym=member:Foo⁰} of member:Foo⁰ //│ arguments = S of Ls of //│ Alias: //│ pattern = Wildcard //│ id = Ident of "a" //│ consequent = Else of Ref{sym=a⁰} of a⁰ //│ tail = End //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁰; //│ define lambda⁰ as fun lambda¹(caseScrut) { //│ let a, arg$Foo$0$; //│ match caseScrut //│ Foo¹ => //│ set arg$Foo$0$ = caseScrut.x⁰; //│ set a = arg$Foo$0$; //│ return a //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ lambda¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // TODO support: // data class Foo(x) // Foo(123).0 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls ================================================ :js :ftc :sir fun aux(f) = f(1) + f(10) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let aux⁰; //│ define aux⁰ as fun aux¹(f) { //│ let tmp, tmp1; //│ set tmp = f.call﹖(1); //│ set tmp1 = f.call﹖(10); //│ return +⁰(tmp, tmp1) //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sir x => x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda⁰, Function$⁰, tmp; //│ define lambda⁰ as fun lambda¹(x) { //│ return x //│ }; //│ define Function$⁰ as class Function$¹ { //│ method call⁰ = fun call¹(x) { return lambda¹(x) } //│ }; //│ set tmp = new Function$¹(); //│ tmp //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class anonymous :fixme // TODO: fix IR rebinding issue (each symbol should be bound at most once) :checkIR :noInline x => x //│ ╔══[INTERNAL ERROR] [BlockChecker] Invalid IR: symbol x⁰ is bound more than once //│ ║ l.37: x => x //│ ╙── ^ //│ = class anonymous fun f(x, y, b) = aux(z => x + z) * aux(z => if b then y + z else y - z) :expect 121 f(0, 0, true) //│ = 121 :sir (x => x)(42) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda²; define lambda² as fun lambda³(x) { return x }; lambda³(42) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 let x = 1 in (y => x + y)(0) //│ = 1 :sir fun foo(x) = x + 1 fun bar(f) = f(0) bar(foo) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let bar⁰, foo⁰, Function$², tmp; //│ define foo⁰ as fun foo¹(x) { //│ return +⁰(x, 1) //│ }; //│ define bar⁰ as fun bar¹(f) { //│ return f.call﹖(0) //│ }; //│ define Function$² as class Function$³ { //│ method call² = fun call³(x) { return foo¹(x) } //│ }; //│ set tmp = new Function$³(); //│ bar¹(tmp) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :expect 1 bar(foo) //│ = 1 fun foo(x) = x + 1 fun bar(f) = f(0) let f = foo in bar(f) //│ = 1 :ge bar([foo].0) bar([foo, x => x].1) //│ ╔══[COMPILATION ERROR] Cannot determine if 0 is a function. //│ ║ l.96: bar([foo].0) //│ ╙── ^^ //│ ═══[COMPILATION ERROR] Cannot determine if 0$__checkNotMethod is a function. //│ ═══[COMPILATION ERROR] Cannot determine if Error is a function. //│ ╔══[COMPILATION ERROR] Cannot determine if 1 is a function. //│ ║ l.97: bar([foo, x => x].1) //│ ╙── ^^ //│ ═══[COMPILATION ERROR] Cannot determine if 1$__checkNotMethod is a function. //│ ═══[COMPILATION ERROR] Cannot determine if Error is a function. //│ = 0 let i = 1 bar([foo, x => x].(i)) //│ = 0 //│ i = 1 // We have to reject this form, since in the following test case, the process is undecidable :ge [foo, x => x].(i)(0) //│ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. //│ ║ l.119: [foo, x => x].(i)(0) //│ ╙── ^ class Foo() with val f = x => x val g = x => x + 1 fun h(x) = x - 1 :ge let foo = Foo() foo.("f")(0) foo.("h")(1) //│ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. //│ ║ l.132: foo.("f")(0) //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. //│ ║ l.133: foo.("h")(1) //│ ╙── ^^^^^^^^ //│ = 0 //│ foo = Foo() class Foo(x) with fun foo(f) = f(x) :expect 0 :sir Foo(1).foo(x => x - 1) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let tmp, lambda⁴, Function$⁴, tmp1; //│ set tmp = Foo⁰(1); //│ define lambda⁴ as fun lambda⁵(x) { //│ return -⁰(x, 1) //│ }; //│ define Function$⁴ as class Function$⁵ { //│ method call⁴ = fun call⁵(x) { return lambda⁵(x) } //│ }; //│ set tmp1 = new Function$⁵(); //│ tmp.foo²(tmp1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :expect 1 :sir fun foo() = x => x foo()(1) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo³, baseCall, lambda⁶, Function$⁶; //│ define lambda⁶ as fun lambda⁷(x) { //│ return x //│ }; //│ define Function$⁶ as class Function$⁷ { //│ method call⁶ = fun call⁷(x) { //│ return lambda⁷(x) //│ } //│ }; //│ define foo³ as fun foo⁴() { //│ let tmp; //│ set tmp = new Function$⁷(); //│ return tmp //│ }; //│ set baseCall = foo⁴(); //│ baseCall.call﹖(1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 class Bar() with fun bar() = x => x :expect 42 Bar().bar()(42) //│ = 42 class Baz(b) with fun baz() = x => x + b :expect 42 Baz(42).baz()(0) //│ = 42 module Foo with fun aux(f) = f(1) + f(10) fun f(x, y, b) = aux(z => x + z) * aux(z => if b then y + z else y - z) :expect 121 Foo.f(0, 0, true) //│ = 121 :sir module Foo with class Bar(y) with fun bar(z) = x => x + y + z //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let Foo¹, lambda⁸, Function$⁸; //│ define lambda⁸ as fun lambda⁹(y, z, x) { //│ let tmp; //│ set tmp = +⁰(x, y); //│ return +⁰(tmp, z) //│ }; //│ define Function$⁸ as class Function$⁹ { //│ private val y⁰; //│ private val z⁰; //│ constructor(y, z) { //│ super⁰(); //│ set y⁰ = y; //│ set z⁰ = z; //│ end //│ } //│ method call⁸ = fun call⁹(x) { //│ return lambda⁹(y⁰, z⁰, x) //│ } //│ }; //│ define Foo¹ as class Foo² //│ module Foo³ { //│ constructor { //│ define Bar⁰ as class Bar²(y) { //│ private val y¹; //│ constructor Bar¹ { //│ set y¹ = y; //│ end //│ } //│ method bar² = fun bar³(z) { //│ let tmp; //│ set tmp = new Function$⁹(y¹, z); //│ return tmp //│ } //│ }; //│ end //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 3 Foo.Bar(1).bar(1)(1) //│ = 3 let x = 1 //│ x = 1 :sir y => x + y //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lambda¹⁰, Function$¹⁰, tmp; //│ define lambda¹⁰ as fun lambda¹¹(y) { //│ return +⁰(x¹, y) //│ }; //│ define Function$¹⁰ as class Function$¹¹ { //│ method call¹⁰ = fun call¹¹(y) { return lambda¹¹(y) } //│ }; //│ set tmp = new Function$¹¹(); //│ tmp //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class anonymous :expect 2 (y => x + y)(1) //│ = 2 fun foo(x) = x + 1 fun bar(x) = foo(x) + 1 class A(val x) A(42) //│ = A(42) A(x => x).x(1) //│ = 1 fun foo(x) = x + 1 fun bar(x) = foo(x) + 1 A(foo).x(0) //│ = 1 module Foo with fun foo(x) = x + 1 fun foo(f) = f(0) :expect 1 foo(Foo.foo) //│ = 1 module Foo with module Bar1 with fun bar(x) = x + 1 module Bar2 with fun bar(x) = x - 1 fun foo(f) = f(0) foo(Foo.Bar1.bar) //│ = 1 foo(Foo.Bar2.bar) //│ = -1 module Foo with val x = 1 fun bar() = 0 fun foo(y) = y + 1 foo(Foo.x) //│ = 2 class Foo(val x) with fun f(y) = y + 1 Foo(0).f(1) //│ = 2 Foo(0).x //│ = 0 Foo(x => x).x(1) //│ = 1 let foo = Foo(1) foo.Foo#x //│ = 1 //│ foo = Foo(1) :ge foo.f(0) //│ ╔══[COMPILATION ERROR] Cannot determine if f is a function object. //│ ║ l.375: foo.f(0) //│ ╙── ^^^^^ //│ = 1 foo.Foo#f(0) //│ = 1 let foo = Foo(x => x + 1) //│ foo = Foo(class anonymous) foo.Foo#f(0) //│ = 1 :ge foo.x(0) //│ ╔══[COMPILATION ERROR] Cannot determine if x is a function object. //│ ║ l.393: foo.x(0) //│ ╙── ^^^^^ foo.Foo#x(0) //│ = 1 class Bar() with val x = 0 val bar = x => x + 1 Bar().bar(1) //│ = 2 Bar().x //│ = 0 let bar = Bar() bar.Bar#bar(1) //│ = 2 //│ bar = Bar() bar.Bar#x //│ = 0 let bar = Bar().bar in bar(1) //│ = 2 module Foo with val f = x => x Foo.f(1) //│ = 1 module Bar with fun h(x) = x + 1 fun w(y) = y(0) fun f(x) = w(h) + x Bar.f(0) //│ = 1 module Foo with fun f(x) = x + 1 module Bar with fun g1(h) = h(0) fun g2(x) = g1(Foo.f) + x Foo.Bar.g2(1) //│ = 2 let f = x => y => x + y //│ f = class anonymous :expect 3 f(1)(2) //│ = 3 :sir fun foo(x)(y) = x + y //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo⁵, lambda$⁰, Function$¹²; //│ define lambda$⁰ as fun lambda$¹(x, y) { //│ return +⁰(x, y) //│ }; //│ define Function$¹² as class Function$¹³ { //│ private val x²; //│ constructor(x) { //│ super⁰(); //│ set x² = x; //│ end //│ } //│ method call¹² = fun call¹³(y) { //│ return lambda$¹(x², y) //│ } //│ }; //│ define foo⁵ as fun foo⁶(x) { let tmp; set tmp = new Function$¹³(x); return tmp }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 1 let bar = foo(1) in bar(0) //│ = 1 fun foo(x)(y)(z) = x + y + z :expect 2 let bar = foo(1) in let baz = bar(0) in baz(1) //│ = 2 fun foo(x) = fun bar(y) = x + y bar(x * 2) foo(1) //│ = 3 fun foo(f) = f("abc") // FIXME: should output "abc" foo(print) //│ > :fixme :sir :expect ["abc"] foo(tuple) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let Function$¹⁴, tmp; //│ define Function$¹⁴ as class Function$¹⁵ { //│ method call¹⁴ = fun call¹⁵(...xs) { //│ return Predef⁰.tuple⁰() //│ } //│ }; //│ set tmp = new Function$¹⁵(); //│ foo⁷(tmp) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Expected: '["abc"]', got: '[]' //│ = [] if true is true then print("true") //│ > true if 0 is Bool then 1 Str then 1 0 then 0 else 2 //│ = 0 class Foo(val x) if Foo(1) is Foo(x) then x else 0 //│ = 1 if [1, 2, 3] is [x, y] then x + y [x, y, z] then x + y + z [x, ...y] then x else 0 //│ = 6 class Foo(val x) let foo = Foo(1) in foo.Foo#x //│ = 1 let foo = new Foo(1) in foo.Foo#x //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Formatting.mls ================================================ :js :sjs //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ import Predef from "./../../mlscript-compile/Predef.mjs"; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let discard = drop _ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let discard, discard1; discard1 = function discard(_0) { return runtime.Unit }; discard = discard1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ discard = fun discard // * Same code, but the latter occurrence is broken because of the slightly wider gensym'd idents discard of { a: 0, b: 11111 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a, b, tmp; //│ a = 0; //│ b = 11111; //│ tmp = globalThis.Object.freeze({ a: a, b: b }); //│ runtime.safeCall(discard(tmp)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of { a: 0, b: 11111 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a1, b1, tmp1; //│ a1 = 0; //│ b1 = 11111; //│ tmp1 = globalThis.Object.freeze({ a: a1, b: b1 }); //│ runtime.safeCall(discard(tmp1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of { aaaa: 1 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let aaaa, tmp2; //│ aaaa = 1; //│ tmp2 = globalThis.Object.freeze({ aaaa: aaaa }); //│ runtime.safeCall(discard(tmp2)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of { aaaaaaaaa: 1, bbbb: 2 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let aaaaaaaaa, bbbb, tmp3; //│ aaaaaaaaa = 1; //│ bbbb = 2; //│ tmp3 = globalThis.Object.freeze({ //│ aaaaaaaaa: aaaaaaaaa, //│ bbbb: bbbb //│ }); //│ runtime.safeCall(discard(tmp3)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of { aaaaaaaaa: 1, bbbbbbbb: 2, ccccccccc: 3, dddddddddd: 4 } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let aaaaaaaaa1, bbbbbbbb, ccccccccc, dddddddddd, tmp4; //│ aaaaaaaaa1 = 1; //│ bbbbbbbb = 2; //│ ccccccccc = 3; //│ dddddddddd = 4; //│ tmp4 = globalThis.Object.freeze({ //│ aaaaaaaaa: aaaaaaaaa1, //│ bbbbbbbb: bbbbbbbb, //│ ccccccccc: ccccccccc, //│ dddddddddd: dddddddddd //│ }); //│ runtime.safeCall(discard(tmp4)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of [ "aaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb" ] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp5; //│ tmp5 = globalThis.Object.freeze([ //│ "aaaaaaaaaaaaaaa", //│ "bbbbbbbbbbbbbbbb" //│ ]); //│ runtime.safeCall(discard(tmp5)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of [ "aaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb", "ccccccccccccccccc", "dddddddddddddddddd" ] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp6; //│ tmp6 = globalThis.Object.freeze([ //│ "aaaaaaaaaaaaaaa", //│ "bbbbbbbbbbbbbbbb", //│ "ccccccccccccccccc", //│ "dddddddddddddddddd" //│ ]); //│ runtime.safeCall(discard(tmp6)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— discard of [[ "aaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb", "ccccccccccccccccc", "dddddddddddddddddd" ]] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp7, tmp8; //│ tmp7 = globalThis.Object.freeze([ //│ "aaaaaaaaaaaaaaa", //│ "bbbbbbbbbbbbbbbb", //│ "ccccccccccccccccc", //│ "dddddddddddddddddd" //│ ]); //│ tmp8 = globalThis.Object.freeze([ tmp7 ]); //│ runtime.safeCall(discard(tmp8)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls ================================================ :js fun test() = data class C(a) with { fun f() = a } C(0).f() test() //│ = 0 :sjs fun test(a) = data class Inner(b) with print(a) fun f(c) = [a, b, c] fun g(d) = fun h(e) = [a, b, d, e] h(d) Inner(42) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test1; //│ test1 = function test(a) { //│ let Inner1; //│ Inner1 = function Inner(b) { //│ return globalThis.Object.freeze(new Inner.class(b)); //│ }; //│ (class Inner { //│ static { //│ Inner1.class = this //│ } //│ constructor(b) { //│ this.b = b; //│ Predef.print(a); //│ } //│ f(c) { //│ return globalThis.Object.freeze([ //│ a, //│ this.b, //│ c //│ ]) //│ } //│ g(d) { //│ let e, inlinedVal; //│ e = d; //│ inlinedVal = globalThis.Object.freeze([ //│ a, //│ this.b, //│ d, //│ e //│ ]); //│ return inlinedVal //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Inner", ["b"]]; //│ }); //│ return Inner1(42) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let i = test(100) //│ > 100 //│ i = Inner(42) i.f(200) //│ = [100, 42, 200] i.g(200) //│ = [100, 42, 200, 200] :sjs fun test(a) = data class C1(b) with print of [a, b] data class C2(b) with print of [a, b] [C1(1), C2(2)] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test2; //│ test2 = function test(a) { //│ let C11, C21, tmp, tmp1; //│ C11 = function C1(b) { //│ return globalThis.Object.freeze(new C1.class(b)); //│ }; //│ (class C1 { //│ static { //│ C11.class = this //│ } //│ constructor(b) { //│ let tmp2; //│ this.b = b; //│ tmp2 = globalThis.Object.freeze([ //│ a, //│ this.b //│ ]); //│ Predef.print(tmp2); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C1", ["b"]]; //│ }); //│ C21 = function C2(b) { //│ return globalThis.Object.freeze(new C2.class(b)); //│ }; //│ (class C2 { //│ static { //│ C21.class = this //│ } //│ constructor(b) { //│ let tmp2; //│ this.b = b; //│ tmp2 = globalThis.Object.freeze([ //│ a, //│ this.b //│ ]); //│ Predef.print(tmp2); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C2", ["b"]]; //│ }); //│ tmp = C11(1); //│ tmp1 = C21(2); //│ return globalThis.Object.freeze([ tmp, tmp1 ]) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— test(123) //│ > [123, 1] //│ > [123, 2] //│ = [C1(1), C2(2)] // Inlining is disabled to test this proxy :sjs :noInline data class Foo(a) with fun foo() = fun bar() = a fun baz() = a bar() baz() foo() Foo(123) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo(a) { //│ return globalThis.Object.freeze(new Foo.class(a)); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ constructor(a) { //│ this.a = a; //│ this.foo(); //│ } //│ foo() { //│ let bar, baz; //│ const this$Foo = this; //│ bar = function bar() { //│ return this$Foo.a //│ }; //│ baz = function baz() { //│ return this$Foo.a //│ }; //│ bar(); //│ return baz() //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["a"]]; //│ }); //│ Foo1(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo(123) :sjs data class Bar(x) with fun foo()() = fun bar() = x bar() foo()() Bar(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Bar1; //│ Bar1 = function Bar(x) { //│ return globalThis.Object.freeze(new Bar.class(x)); //│ }; //│ (class Bar { //│ static { //│ Bar1.class = this //│ } //│ constructor(x) { //│ this.x = x; //│ this.foo()(); //│ } //│ foo() { //│ return () => { //│ let inlinedVal; //│ inlinedVal = this.x; //│ return inlinedVal //│ } //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Bar", ["x"]]; //│ }); //│ Bar1(1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Bar(1) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Functions.mls ================================================ :js fun foo() = 1 + 2 foo //│ = fun foo foo() //│ = 3 fun foo(x) = x + 1 foo(123) //│ = 124 fun foo(x) = x + 1 + 2 foo(123) //│ = 126 fun foo() = bar() fun bar() = 42 bar() //│ = 42 foo() //│ = 42 fun foo() = bar() let r = foo() fun bar() = 42 r //│ = 42 //│ r = 42 // * This fails to properly initialize, as expected :re :noInline fun foo() = bar() let r = foo() fun bar() = r //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. //│ r = undefined // * Similar :re object Outer with fun foo() = bar() val r = foo() fun bar() = r Outer.r //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. fun outerfun(x) = let r = x + 1 fun nested(y) = r + x + y print(nested(x)) nested let f = outerfun(100) f(200) //│ > 301 //│ = 401 //│ f = fun nested // f(200) outerfun(100) //│ > 301 //│ = fun nested outerfun(100)(200) //│ > 301 //│ = 401 // * Notice this strange JS phenomenon: // * functions leak from the `try` scope, although things like classes do not... :showRepl fun test1(x) = test2(x) fun test2(y) = y + 1 //│ REPL> Sending: block$res18 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: let test2, test1;try { test1 = function test1(x) { runtime.checkArgs("test1", 1, true, arguments.length); let y, inlinedVal; y = x; inlinedVal = y + 1; return inlinedVal }; test2 = function test2(y) { runtime.checkArgs("test2", 1, true, arguments.length); return y + 1 };; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: //│ > undefined //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res18)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ > undefined //│ REPL> Parsed: //│ > undefined //│ > undefined test1(1) //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls ================================================ :js :sjs val x = 2 fun foo() = x + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo, x; foo = function foo() { return x + 1 }; x = 2; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ x = 2 :sjs class Test with print(foo()) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Test1; //│ (class Test { //│ static { //│ Test1 = this //│ } //│ constructor() { //│ let tmp; //│ tmp = foo(); //│ Predef.print(tmp); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Test"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— new Test //│ > 3 //│ = Test ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/FunnyOpen.mls ================================================ :js module Mod with fun printt(x) = console.log(x) fun (|>>) pipee(x, f) = f(x) :ctx open Mod //│ Env: //│ pipee -> SelElem(RefElem(member:Mod),pipee,Some(member:pipee),true) //│ printt -> SelElem(RefElem(member:Mod),printt,Some(member:printt),true) //│ |>> -> SelElem(RefElem(member:Mod),pipee,Some(member:pipee),true) pipee //│ = fun pipee (|>>) //│ = fun pipee |>> //│ = fun pipee :pe :e Mod.|>> //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.26: Mod.|>> //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: .|>> //│ ║ l.26: Mod.|>> //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type Mod. //│ ║ l.26: Mod.|>> //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. 1 |>> x => printt(x) //│ > 1 :pe :e open Mod { |>> } //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.45: open Mod { |>> } //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Illegal 'open' statement shape. //│ ║ l.45: open Mod { |>> } //│ ╙── ^^^^^^^^^^^ :e open Mod { (|>>) } //│ ╔══[COMPILATION ERROR] Illegal 'open' statement element. //│ ║ l.55: open Mod { (|>>) } //│ ╙── ^^^^^ 12 |>> print //│ > 12 open Mod { printt, |>> } :fixme 12 |>> printt //│ ╔══[COMPILATION ERROR] Module 'Mod' does not contain member '|>>' //│ ║ l.67: 12 |>> printt //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] TypeError: Mod1.|>> is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Getters.mls ================================================ :js :noInline :sjs fun t = 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let t; t = function t() { return 42 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 42 :sjs t //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ t() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 fun main() = let n = 0 fun foo = print("ok") set n += 1 n foo + foo :expect 3 main() //│ > ok //│ > ok //│ = 3 :sjs fun test() = fun whoops = print("ok") 42 whoops + whoops //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test; //│ test = function test() { //│ let whoops, tmp, tmp1; //│ whoops = function whoops() { //│ Predef.print("ok"); //│ return 42 //│ }; //│ tmp = whoops(); //│ tmp1 = whoops(); //│ return tmp + tmp1 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— test() //│ > ok //│ > ok //│ = 84 :sjs module T with fun t() = 1 fun p = 2 val a = t val b = this.t val c = p val d = this.p //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let T1; //│ (class T { //│ static { //│ T1 = this //│ } //│ static { //│ this.a = T.t; //│ this.b = T.t; //│ this.c = T.p; //│ this.d = T.p; //│ } //│ static t() { //│ return 1 //│ } //│ static get p() { //│ return 2; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "T"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— T.a //│ = fun t T.b //│ = fun t T.c //│ = 2 T.d //│ = 2 :sjs module M with fun t = 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let M1; //│ (class M { //│ static { //│ M1 = this //│ } //│ static get t() { //│ return 0; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs M.t //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ M1.t //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :sjs fun test() = fun whoops = 42 whoops //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test1; //│ test1 = function test() { let whoops; whoops = function whoops() { return 42 }; return whoops() }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :re globalThis.whoops //│ ═══[RUNTIME ERROR] Error: Access to required field 'whoops' yielded 'undefined' test() //│ = 42 :re globalThis.whoops //│ ═══[RUNTIME ERROR] Error: Access to required field 'whoops' yielded 'undefined' :sjs fun bar() = fun baz() = 42 baz //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let bar; bar = function bar() { let baz; baz = function baz() { return 42 }; return baz }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun baz() = fun w = 1 fun z = 2 (x, y) => x + y + w + z //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let baz; //│ baz = function baz() { //│ let w, z, lambda; //│ w = function w() { //│ return 1 //│ }; //│ z = function z() { //│ return 2 //│ }; //│ lambda = (undefined, function (x, y) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = x + y; //│ tmp1 = w(); //│ tmp2 = tmp + tmp1; //│ tmp3 = z(); //│ return tmp2 + tmp3 //│ }); //│ return lambda //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— baz()(3, 4) //│ = 10 :sjs fun a() = fun b = 1 fun c() = fun d = 2 b + d c //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a; //│ a = function a() { //│ let b, c; //│ b = function b() { //│ return 1 //│ }; //│ c = function c() { //│ let d, tmp, tmp1; //│ d = function d() { return 2 }; //│ tmp = b(); //│ tmp1 = d(); //│ return tmp + tmp1 //│ }; //│ return c //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— a()() //│ = 3 :sjs fun b() = fun c = 1 fun d() = fun c = 2 c d //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let b; //│ b = function b() { //│ let d; //│ d = function d() { let c; c = function c() { return 2 }; return c() }; //│ return d //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— b()() //│ = 2 :sjs fun c() = fun f = 1 fun d = fun e = 1 e + f d //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let c; //│ c = function c() { //│ let d, f; //│ f = function f() { //│ return 1 //│ }; //│ d = function d() { //│ let e, tmp, tmp1; //│ e = function e() { return 1 }; //│ tmp = e(); //│ tmp1 = f(); //│ return tmp + tmp1 //│ }; //│ return d() //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— c() //│ = 2 :sjs data class Foo(x) with fun oops = x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo(x) { //│ return globalThis.Object.freeze(new Foo.class(x)); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ constructor(x) { //│ this.x = x; //│ } //│ get oops() { //│ return this.x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun foo(f) = f.oops foo(Foo(42)) //│ = 42 fun foo(f) = f.oops data class Foo(x) with fun oops = x foo(Foo(42)) //│ = 42 :re foo(Foo(42))() //│ ═══[RUNTIME ERROR] TypeError: baseCall3 is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls ================================================ :js :silent let g = globalThis // FIXME prevent rebinding of this name :re let globalThis = "oops" //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') :re globalThis //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') // * This one uses `this.Error` so it's fine :sjs :re if false then 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; //│ scrut = false; //│ if (scrut === true) { //│ 0 //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') // * This one uses `globalThis.Error` // * Notice the failed exception throw :sjs :re fun foo() = if false then 0 foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo; //│ foo = function foo() { //│ let scrut1; //│ scrut1 = false; //│ if (scrut1 === true) { //│ return 0 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ foo() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls ================================================ :js // * Note how we avoid the name clash between the internal name `Test` of the class // * and the external name `Test1` of its singleton instance. :sjs object Test with val x = 12 fun foo() = print(Test) Test.x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Test1; //│ (class Test { //│ static { //│ new this //│ } //│ constructor() { //│ Test1 = this; //│ this.x = 12; //│ Object.defineProperty(this, "class", { //│ value: Test //│ }); //│ globalThis.Object.freeze(this); //│ } //│ foo() { //│ Predef.print(Test1); //│ return Test1.x //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["object", "Test"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Test //│ = Test { x: 12, class: object Test } print(Test) //│ > Test { x: 12, class: object Test } :sjs Test.foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Test1.foo() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Test { x: 12, class: object Test } //│ = 12 :sjs val Test = "oops" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Test2; Test2 = "oops"; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ Test = "oops" :re Test.foo() //│ ═══[RUNTIME ERROR] TypeError: Test2.foo is not a function :sjs let x = 1 let f = () => x let x = 2 f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x, f, x1, f1; x = 1; f1 = function f() { return x }; f = f1; x1 = 2; runtime.safeCall(f()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 //│ f = fun f //│ x = 2 :ssjs :e module Test with val x = 1 let x = 2 //│ ╔══[COMPILATION ERROR] Name 'x' is already used //│ ║ l.78: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block //│ ║ l.77: val x = 1 //│ ╙── ^^^^^^^^^ //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let Test4; //│ globalThis.Object.freeze(class Test3 { //│ static { //│ Test4 = this //│ } //│ static #x; //│ static { //│ this.x = 1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Test"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Test.x //│ = 1 module Test with let x = 1 let f = () => x let x = 2 print(f()) //│ > 1 :sjs data class Cls(x) with let x += 1 fun foo = x let x *= 2 fun bar = x print(this.x, x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Cls1; //│ Cls1 = function Cls(x2) { //│ return globalThis.Object.freeze(new Cls.class(x2)); //│ }; //│ (class Cls { //│ static { //│ Cls1.class = this //│ } //│ constructor(x2) { //│ this.x = x2; //│ this.#x = this.x + 1; //│ this.#x1 = this.#x * 2; //│ Predef.print(this.x, this.#x1); //│ } //│ #x; //│ #x1; //│ get foo() { //│ return this.#x; //│ } //│ get bar() { //│ return this.#x1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Cls", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let cls = Cls(10) //│ > 10 22 //│ cls = Cls(10) :expect 10 cls.x //│ = 10 :expect 11 cls.foo //│ = 11 :expect 22 cls.bar //│ = 22 module M fun foo(): module M = if false then module A A else module A A foo() //│ = class A :sjs module Whoops with val v: module Whoops = this fun f() = "Hello" module Whoops with val w: module Whoops = this fun g() = f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Whoops2; //│ (class Whoops { //│ static { //│ Whoops2 = this //│ } //│ static { //│ this.v = Whoops; //│ (class Whoops1 { //│ static { //│ Whoops.Whoops = this //│ } //│ static { //│ this.w = Whoops1; //│ } //│ static g() { //│ return Whoops.f() //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Whoops"]; //│ }); //│ } //│ static f() { //│ return "Hello" //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Whoops"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Whoops.f() //│ = "Hello" Whoops.Whoops //│ = class Whoops { w: ref'1 } as ref'1 Whoops.Whoops.g() //│ = "Hello" :e Runtime //│ ╔══[COMPILATION ERROR] Name not found: Runtime //│ ║ l.225: Runtime //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs module AA with module BB with val y = 2 fun test = let BB = "oops" y //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let AA1; //│ (class AA { //│ static { //│ AA1 = this //│ } //│ static { //│ (class BB { //│ static { //│ AA.BB = this //│ } //│ static { //│ this.y = 2; //│ } //│ static get test() { //│ return BB.y; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "BB"]; //│ }); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "AA"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls ================================================ :js // :elt if true then 1 else 0 //│ = 1 :sjs let f = x => if x then print("ok") else print("ko") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f, f1; //│ f1 = function f(x) { //│ if (x === true) { return Predef.print("ok") } //│ return Predef.print("ko"); //│ }; //│ f = f1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f f(true) //│ > ok f(false) //│ > ko :sjs let f = x => print((if x then "ok" else "ko") + "!") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2, lambda; //│ lambda = (undefined, function (x) { //│ let tmp, tmp1; //│ if (x === true) { //│ tmp = "ok"; //│ } else { tmp = "ko"; } //│ tmp1 = tmp + "!"; //│ return Predef.print(tmp1) //│ }); //│ f2 = lambda; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun :sjs let f = x => print((if x and x then "ok" else "ko") + "!") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f3, lambda1; //│ lambda1 = (undefined, function (x) { //│ let tmp, tmp1; //│ if (x === true) { //│ tmp = "ok"; //│ } else { tmp = "ko"; } //│ tmp1 = tmp + "!"; //│ return Predef.print(tmp1) //│ }); //│ f3 = lambda1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun // --- TODO: What we want --- // this.f = (x) => { // let tmp, tmp1, flag; // if (x) { // if (x) { // tmp = "ok"; // } else { // flag = 1; // } // } else { // flag = 1; // } // if (flag === 1) { // tmp = "ko"; // } // tmp1 = tmp + "!"; // return print(tmp1) // }; // undefined // f = [Function (anonymous)] f(true) //│ > ok! f(false) //│ > ko! x => if x > 0 then print("Hi") else print("Bye") //│ = fun x => print(if true then "Hi" else "Bye") //│ = fun x => print(if x + 1 > 0 then "Hi" else "Bye") //│ = fun x => let str = (+)(if x + 1 > 0 then "Hello" else "Bye", "World") print(str) //│ = fun fun f(x, y) = if x then y f(true, 42) //│ = 42 :re f(false, 42) //│ ═══[RUNTIME ERROR] Error: match error fun f(x, y, z) = if x then y else z f(true, 0, 1) //│ = 0 fun f(x, y, z) = if x then y else z f(true, 1, 0) //│ = 1 f(false, 1, 0) //│ = 0 if true then 100 //│ = 100 if true then 100 else 0 //│ = 100 if true then 100 else fun f(x) = x f(3) //│ = 100 if true then fun f(x) = x f(3) else 100 //│ = 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImperativeConditionals.mls ================================================ :js :ignore let x = -1 //│ x = -1 if x < 0 do set x = 0 fun f(x) = if x < 0 return 0 Math.sqrt(x) //│ ╔══[PARSE ERROR] Unexpected 'return' keyword here //│ ║ l.12: if x < 0 return 0 //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized term split (integer literal) //│ ║ l.12: if x < 0 return 0 //│ ╙── ^ fun hasZeroElement(xs) = while xs is Cons(0, tl) return true Cons(hd, tl) do set xs = tl Nil return false //│ ╔══[PARSE ERROR] Unexpected 'return' keyword here //│ ║ l.24: Cons(0, tl) return true //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (integer literal). //│ ║ l.24: Cons(0, tl) return true //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (identifier). //│ ║ l.24: Cons(0, tl) return true //│ ╙── ^^ //│ ╔══[WARNING] This catch-all clause makes the following branches unreachable. //│ ║ l.24: Cons(0, tl) return true //│ ║ ^ //│ ╟── This branch is unreachable. //│ ║ l.24: Cons(0, tl) return true //│ ╙── ^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportAlias.mls ================================================ :js import "../../mlscript-compile/Example.mls" as AnotherExample :e Example.inc(0) //│ ╔══[COMPILATION ERROR] Name not found: Example //│ ║ l.7: Example.inc(0) //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 1 AnotherExample.inc(0) //│ = 1 :silent import "../../mlscript-compile/Example.mjs" as JSExample :e Example.inc(0) //│ ╔══[COMPILATION ERROR] Name not found: Example //│ ║ l.22: Example.inc(0) //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 1 JSExample.inc(0) //│ = 1 :pe import "../../mlscript-compile/Example.mls" as //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.34: import "../../mlscript-compile/Example.mls" as //│ ╙── ^ :e import "../../mlscript-compile/Example.mls" as "Examle" //│ ╔══[COMPILATION ERROR] Expected identifier after 'as' in import statement //│ ║ l.40: import "../../mlscript-compile/Example.mls" as "Examle" //│ ╙── ^^^^^^^^ import "../../mlscript-compile/Example.mls" :expect 1 Example.inc(0) //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportConflicts.mls ================================================ :js module Option1 with { fun Some1 = 1 } open Option1 fun Some1 = "111" Some1 //│ = "111" module Option2 with { fun Some2 = 2 } // * Note that wildcard imports do not import names that are already defined in the same scope fun Some2 = "222" open Option2 Some2 //│ = "222" module Option3 with { fun Some3 = 3 } :w fun Some3 = "333" open Option3 { Some3 } Some3 //│ ╔══[WARNING] Imported name 'Some3' is shadowed by a name already defined in the same scope //│ ║ l.25: open Option3 { Some3 } //│ ╙── ^^^^^ //│ = "333" module Option4 with { fun Some4 = 4 } fun Some4 = "444" fun bar = open Option4 { Some4 } Some4 bar //│ = 4 module Option5 with { fun Some5 = 5 } fun Some5 = "555" fun bar = open Option5 Some5 bar //│ = 5 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportExample.mls ================================================ :js import "../../mlscript-compile/Example.mls" Example.inc(123) //│ = 124 Example.funnySlash(Example.inc, 123) //│ = 124 Example.pubFun //│ = "hi" :e :re Example.privFun //│ ╔══[COMPILATION ERROR] Module 'Example' does not contain member 'privFun' //│ ║ l.20: Example.privFun //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'privFun' yielded 'undefined' :re Example.assertFail() //│ ═══[RUNTIME ERROR] Error: Assertion failed (mlscript-compile/Example.mls:27) let n = 42 //│ n = 42 :sjs n / 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ n / 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 21 // * Builtin operators can be shadowed/rebound open Example :sjs inc / 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Example.funnySlash(Example.inc, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 :sjs :re n / 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Example.funnySlash(n, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: f is not a function funnySlash(inc, 123) //│ = 124 inc / 123 //│ = 124 test("hello") //│ = "str" test / (inc / 123) //│ = "int" // Note: `/` is left-associative, so `test / inc / 123` parses as `(test / inc) / 123`, // which errors because `funnySlash(test, inc)` does not return a valid function. :re test / inc / 123 //│ ═══[RUNTIME ERROR] TypeError: f is not a function :re test / inc / 123 * 0.5 //│ ═══[RUNTIME ERROR] TypeError: f is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportJSClass.mls ================================================ :js import "../../js/MyClass.mjs" //│ MyClass = class MyClass MyClass //│ = class MyClass new! MyClass("Bob") //│ = MyClass { name: "Bob" } let C = MyClass //│ C = class MyClass let c = C //│ c = class MyClass new! c("Bob") //│ = MyClass { name: "Bob" } :re globalThis.MyClass //│ ═══[RUNTIME ERROR] Error: Access to required field 'MyClass' yielded 'undefined' :ge fun foo() = import "../../js/MyClass.mjs" () //│ ╔══[COMPILATION ERROR] Imports must be at the top level //│ ║ l.30: import "../../js/MyClass.mjs" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportJSModule.mls ================================================ :js import "../../js/Test.mjs" //│ Test = {greet: fun greet} Test //│ = {greet: fun greet} Test.greet() //│ > Hello from Test.mjs! ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls ================================================ :js import "../../mlscript-compile/Option.mls" open Option { isDefined } Option.None isDefined() //│ = false Option.Some(1) isDefined() //│ = true open Option Some, None isDefined :sjs None isDefined() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Option.isDefined(Option.None) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = false case Some(x) then x //│ = fun case { Some(x) then x } //│ = fun Some(1) isDefined() //│ = true Some(1) isDefined() //│ = true :sjs (new Some(1)) isDefined() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp3; tmp3 = globalThis.Object.freeze(new Option.Some.class(1)); Option.isDefined(tmp3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true new Some(1) isDefined() //│ = true new Some(1) isDefined() //│ = true open Option { Some, None, isDefined } (new Some(1)) isDefined() //│ = true None isDefined() //│ = false None == Option.None //│ = true :e :re Option.oops //│ ╔══[COMPILATION ERROR] Module 'Option' does not contain member 'oops' //│ ║ l.69: Option.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' :e open Option { oops } //│ ╔══[COMPILATION ERROR] Module 'Option' does not contain member 'oops' //│ ║ l.76: open Option { oops } //│ ╙── ^^^^ oops :e :re Option.None.oops //│ ╔══[COMPILATION ERROR] Object 'None' does not contain member 'oops' //│ ║ l.85: Option.None.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls ================================================ :js import "../../mlscript-compile/Option.mjs" //│ Option = class Option { //│ Some: fun Some { class: class Some }, //│ None: None, //│ Both: fun Both { class: class Both }, //│ unsafe: class unsafe //│ } :e Option.isDefined(new Option.Some(1)) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found selection. //│ ║ l.14: Option.isDefined(new Option.Some(1)) //│ ║ ^^^^^^^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ = true Option.isDefined(new! Option.Some(1)) //│ = true Option.isDefined(new! Option.Some.class(1)) //│ = true Option.isDefined(Option.None) //│ = false (new! Option.Some(1)) Option.isDefined() //│ = true Option.None Option.isDefined() //│ = false Option.None //│ = None new! Option.Some(1) //│ = Some(1) new! Option.Both(1, 2) //│ = Both(1, 2) Option.None //│ = None // * This used to misbehave due to inadequately handling the weird precedence of `new` new! Option.Some(1) //│ = Some(1) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls ================================================ :js module M with fun (~) concat(a, b) = a + b open M :sjs fun foo() = "a" ~ "b" ~ "c" foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo; //│ foo = function foo() { let tmp; tmp = M1.concat("a", "b"); return M1.concat(tmp, "c") }; //│ foo() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "abc" let name = "_" //│ name = "_" let display(balance) = balance //│ display = fun display "b" ~ display("-") //│ = "b-" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls ================================================ :js :noInline :sjs (x => x + 1 + 1 + 1 + 1 + 1)(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; //│ lambda = (undefined, function (x) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = x + 1; //│ tmp1 = tmp + 1; //│ tmp2 = tmp1 + 1; //│ tmp3 = tmp2 + 1; //│ return tmp3 + 1 //│ }); //│ runtime.safeCall(lambda(1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 :sjs (x => x + 1 + 1 + 1 + 1 + 1 + 1)(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; //│ lambda1 = (undefined, function (x) { //│ let tmp, tmp1, tmp2, tmp3, tmp4; //│ tmp = x + 1; //│ tmp1 = tmp + 1; //│ tmp2 = tmp1 + 1; //│ tmp3 = tmp2 + 1; //│ tmp4 = tmp3 + 1; //│ return tmp4 + 1 //│ }); //│ runtime.safeCall(lambda1(1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 7 :sjs (x => x) + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda2; lambda2 = (undefined, function (x) { return x }); lambda2 + 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "function (x) { runtime.checkArgs(\"\", 1, true, arguments.length); return x }1" :sjs 1 + (x => x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda3; lambda3 = (undefined, function (x) { return x }); 1 + lambda3 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "1function (x) { runtime.checkArgs(\"\", 1, true, arguments.length); return x }" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/InlineMultiArgLists.mls ================================================ :js // * TODO: take `private` into accoutn and inline these single-use functions regardless of threshold // Test: Curried function with two parameter lists. Properly fully inlined. :soir private fun add(x)(y) = x + y add(1)(2) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let add⁰, x⁰, y⁰, inlinedVal; //│ define add⁰ as fun add¹(x)(y) { //│ return +⁰(x, y) //│ }; //│ set x⁰ = 1; //│ set y⁰ = 2; //│ set inlinedVal = +⁰(x⁰, y⁰); //│ inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 // Test: Function returning a lambda, called with 2 arg lists at use site. // We properly inline the outer function, but not yet the inner lambda. :inlineThreshold 100 :soir private fun mkAdder(x) = (y) => x + y mkAdder(10)(20) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let mkAdder⁰, baseCall, x¹, inlinedVal, lambda⁰; //│ define mkAdder⁰ as fun mkAdder¹(x) { //│ let lambda; //│ define lambda as fun lambda¹(y) { //│ return +⁰(x, y) //│ }; //│ return lambda¹ //│ }; //│ set x¹ = 10; //│ define lambda⁰ as fun lambda²(y) { //│ return +⁰(x¹, y) //│ }; //│ set inlinedVal = lambda²; //│ set baseCall = inlinedVal; //│ baseCall(20) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 30 // Test: Partial application — call with fewer arg lists than param lists :soir private fun add2(x)(y) = x + y let f = add2(10) f(5) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let add2⁰, f⁰; define add2⁰ as fun add2¹(x)(y) { return +⁰(x, y) }; set f⁰ = add2¹(10); f⁰(5) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 15 //│ f = fun // Test: Three parameter lists — all properly inlined :inlineThreshold 100 :soir private fun triple(x)(y)(z) = x + y + z triple(1)(2)(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let triple⁰, x², y¹, z⁰, inlinedVal, tmp; //│ define triple⁰ as fun triple¹(x)(y)(z) { //│ let tmp1; //│ set tmp1 = +⁰(x, y); //│ return +⁰(tmp1, z) //│ }; //│ set x² = 1; //│ set y¹ = 2; //│ set z⁰ = 3; //│ set tmp = +⁰(x², y¹); //│ set inlinedVal = +⁰(tmp, z⁰); //│ inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Inliner.mls ================================================ :js :soir fun f() = 0 f() //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰, inlinedVal; define f⁰ as fun f¹() { return 0 }; set inlinedVal = 0; inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir fun f(x) = x f(100) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f², x⁰, inlinedVal; //│ define f² as fun f³(x) { return x }; //│ set x⁰ = 100; //│ set inlinedVal = x⁰; //│ inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 100 :soir fun f() = let done(x) = x done //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁴; //│ define f⁴ as fun f⁵() { //│ let done, done1; //│ define done1 as fun done⁰(x) { return x }; //│ set done = done⁰; //│ return done //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * TODO: support spread arguments like this one :soir :inlineThreshold 10 fun f(a, b, c) = a + b + c print(f(1, 2, 3)) print(f(1, 2, ...[1])) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁶, tmp, tmp1, tmp2, a⁰, b⁰, c⁰, inlinedVal, tmp3; //│ define f⁶ as fun f⁷(a, b, c) { //│ let tmp4; //│ set tmp4 = +⁰(a, b); //│ return +⁰(tmp4, c) //│ }; //│ set a⁰ = 1; //│ set b⁰ = 2; //│ set c⁰ = 3; //│ set tmp3 = +⁰(a⁰, b⁰); //│ set inlinedVal = +⁰(tmp3, c⁰); //│ set tmp = inlinedVal; //│ do Predef⁰.print⁰(tmp); //│ set tmp1 = [1]; //│ set tmp2 = f⁷(1, 2, ...tmp1); //│ Predef⁰.print⁰(tmp2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 6 //│ > 4 :soir :inlineThreshold 10 fun f(a, ...b) = print(a) print(b) f(1, ...[2]) f(1, ...[2], ...[3]) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁸, tmp, tmp1, tmp2; //│ define f⁸ as fun f⁹(a, ...b) { //│ do Predef⁰.print⁰(a); //│ return Predef⁰.print⁰(b) //│ }; //│ set tmp = [2]; //│ do f⁹(1, ...tmp); //│ set tmp1 = [2]; //│ set tmp2 = [3]; //│ f⁹(1, ...tmp1, ...tmp2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > [2] //│ > 1 //│ > [2, 3] // All inlined :soir :inlineThreshold 10 fun f(a, b, ...c) = print(a + b) print(c) f(1, 2) f(1, 2, 3) f(1, 2, 3, 4) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹⁰, a¹, b¹, c¹, a², b², c², a³, b³, c³, inlinedVal, tmp, tmp1, tmp2; //│ define f¹⁰ as fun f¹¹(a, b, ...c) { //│ let tmp3; //│ set tmp3 = +⁰(a, b); //│ do Predef⁰.print⁰(tmp3); //│ return Predef⁰.print⁰(c) //│ }; //│ set a¹ = 1; //│ set b¹ = 2; //│ set c¹ = mut []; //│ set tmp2 = +⁰(a¹, b¹); //│ do Predef⁰.print⁰(tmp2); //│ do Predef⁰.print⁰(c¹); //│ set a² = 1; //│ set b² = 2; //│ set c² = mut [3]; //│ set tmp1 = +⁰(a², b²); //│ do Predef⁰.print⁰(tmp1); //│ do Predef⁰.print⁰(c²); //│ set a³ = 1; //│ set b³ = 2; //│ set c³ = mut [3, 4]; //│ set tmp = +⁰(a³, b³); //│ do Predef⁰.print⁰(tmp); //│ set inlinedVal = Predef⁰.print⁰(c³); //│ inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 3 //│ > [] //│ > 3 //│ > [3] //│ > 3 //│ > [3, 4] // only inline perfect match when spread is used :sjs :inlineThreshold 10 fun f(a, b, ...c) = print(a + b) print(c) f(1, ...[2, 3]) f(1, 2, ...[3]) f(1, 2, 3, ...[4]) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6, tmp10, tmp11, tmp12; //│ f6 = function f(a4, b4, ...c4) { //│ let tmp13; //│ tmp13 = a4 + b4; //│ Predef.print(tmp13); //│ return Predef.print(c4) //│ }; //│ tmp10 = globalThis.Object.freeze([ //│ 2, //│ 3 //│ ]); //│ f6(1, ...tmp10); //│ tmp11 = globalThis.Object.freeze([ //│ 3 //│ ]); //│ f6(1, 2, ...tmp11); //│ tmp12 = globalThis.Object.freeze([ 4 ]); //│ f6(1, 2, 3, ...tmp12) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 3 //│ > [3] //│ > 3 //│ > [3] //│ > 3 //│ > [3, 4] // Multiple return points :sjs :expect true fun f() = fun g(x) = if x is true do return true return false g(true) f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f7; //│ f7 = function f() { //│ let x1, inlinedVal4; //│ x1 = true; //│ if (x1 === true) { //│ inlinedVal4 = true; //│ return inlinedVal4 //│ } //│ inlinedVal4 = false; //│ return inlinedVal4; //│ }; //│ f7() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true // Methods (not implemented currently) // NOTE: only final (ie, non-virtual) method may be inlined, as they could be overriden :sjs :soir :inlineThreshold 10 module A with fun f() = 1 fun g() = A.f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let A1; //│ (class A { //│ static { //│ A1 = this //│ } //│ static f() { //│ return 1 //│ } //│ static g() { //│ return A1.f() //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let A⁰; //│ define A⁰ as class A¹ //│ module A² { method f¹² = fun f¹³() { return 1 } method g⁰ = fun g¹() { return A².f¹³() } }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Inlining limits :soir :inlineThreshold 10 fun f() = if false do f() + 1 f() //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹⁴; //│ define f¹⁴ as fun f¹⁵() { //│ let scrut; //│ set scrut = false; //│ match scrut //│ true => //│ do f¹⁵(); //│ return runtime⁰.Unit⁰ //│ else //│ return runtime⁰.Unit⁰ //│ end //│ }; //│ f¹⁵() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Pathological case of inlining :soir fun g() = fun f() = f() f() //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let g²; define g² as fun g³() { let f; define f as fun f¹⁶() { return f¹⁶() }; return f¹⁶() }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/InnerNameHygiene.mls ================================================ :js :noInline // * Functions fun f(x) = x :sjs fun f(x) = x fun f1(x) = f(x) + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1, f11; //│ f1 = function f(x) { //│ return x //│ }; //│ const proxy$f = f1; f11 = function f1(x) { let tmp; tmp = proxy$f(x); return tmp + 1 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 42 f1(41) //│ = 42 :sjs fun g(x) = g(x) + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let g; g = function g(x) { let tmp; tmp = g(x); return tmp + 1 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun ff(x) = 0 :sjs fun ff(x) = x fun g() = fun ff1(x) = ff(x) ff1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let ff1, g1; //│ ff1 = function ff(x) { //│ return x //│ }; //│ g1 = function g() { //│ let ff11; //│ const proxy$ff = ff1; ff11 = function ff1(x) { return proxy$ff(x) }; //│ return ff11 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 42 g()(42) //│ = 42 :sjs fun h(x) = x fun g() = fun h1(x) = h(x) + h1(x) h1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let g2, h; //│ h = function h(x) { //│ return x //│ }; //│ g2 = function g() { //│ let h1; //│ h1 = function h1(x) { let tmp, tmp1; tmp = h(x); tmp1 = h1(x); return tmp + tmp1 }; //│ return h1 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Classes :sjs class C with class C with new C //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let C2; //│ (class C { //│ static { //│ C2 = this //│ } //│ constructor() { //│ const this$C = this; //│ (class C1 { //│ static { //│ this$C.C = this //│ } //│ constructor() { //│ globalThis.Object.freeze(new this$C.C()); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C"]; //│ }); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs class CC with fun foo = class CC2 with new CC2 new CC2 new CC //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let CC1; //│ (class CC { //│ static { //│ CC1 = this //│ } //│ constructor() { //│ globalThis.Object.freeze(new CC1()); //│ } //│ get foo() { //│ let CC21; //│ (class CC2 { //│ static { //│ CC21 = this //│ } //│ constructor() { //│ globalThis.Object.freeze(new CC21()); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "CC2"]; //│ }); //│ return globalThis.Object.freeze(new CC21()); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "CC"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— class F module M with object F1 with val res = new F :expect true M.F1.res is F //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/InterleavedRecords.mls ================================================ :js // * Record fields can be interleaved with other statements. // * Also, later record fields can refer to prior ones. let rcd = a: 0 print(a) let y = a + 1 b: y //│ > 0 //│ rcd = {a: 0, b: 1} let rcd = a: 1 print(a) b: a + 1 let tmp = a + b c: tmp print("Done!") //│ > 1 //│ > Done! //│ rcd = {a: 1, b: 2, c: 3} fun foo(x) = x: x + 1 y: x let r = foo(123) in [r.x, r.y] //│ = [124, 124] // * Yaml! let rcd = a: 0 b: y: 1 z: 2 c: b.z //│ rcd = {a: 0, b: {y: 1, z: 2}, c: 2} [rcd.b.y, rcd.c] //│ = [1, 2] // * Imperative Yaml! let config(useModules) = ext: "js" if useModules do set ext = "m" + ext base: "main" full: base + "." + ext //│ config = fun config(true) //│ = {ext: "mjs", base: "main", full: "main.mjs"} config(false) //│ = {ext: "js", base: "main", full: "main.js"} // * When any statement is a record field, the whole block is a record. :w let rcd = print("Hello!") a: 0 42 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.72: 42 //│ ╙── ^^ //│ > Hello! //│ rcd = {a: 0} ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Juxtapositions.mls ================================================ :js 42 print() //│ > 42 print(42) //│ > 42 42 console.log() //│ > 42 fun inc(x) = x + 1 42 inc() //│ = 43 42 inc() inc() //│ = 44 42 inc() inc() print() //│ > 44 inc(42) inc() print() //│ > 44 fun add(x, y) = x + y 42 add(2) //│ = 44 42 add(2) inc() //│ = 45 :e :re :w 42 add(2) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.41: add(2) //│ ╙── ^^^ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.40: 42 //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Function 'add' expected 2 arguments but got 1 42 add(2) + 1 //│ = 45 42 add(2) * 1 add(1) //│ = 88 42 add(2) //│ = 44 42 add(2) inc() //│ = 45 42 { inc() } //│ = 43 42 { inc() } inc() //│ = 44 42 { add(2) } inc() //│ = 45 42 { add(2) } { inc() } //│ = 45 42 { add(2), inc() } //│ = 45 :e 42 { add(2) inc() } //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (juxtaposition). //│ ║ l.85: 42 { add(2) inc() } //│ ╙── ^^^^^^^^^^^^ //│ = 42 :e 42 { add(2) { inc() } } //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (juxtaposition). //│ ║ l.92: 42 { add(2) { inc() } } //│ ╙── ^^^^^^^^^^^^^^^^ //│ = 42 2 * 2 inc() //│ = 6 :pt inc(2) * 2 inc() //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp: //│ lhs = App: //│ lhs = Ident of "inc" //│ rhs = Tup of Ls of //│ IntLit of 2 //│ op = Ident of "*" //│ rhss = Ls of //│ Jux: //│ lhs = IntLit of 2 //│ rhs = Block of Ls of //│ App: //│ lhs = Ident of "inc" //│ rhs = Tup of Nil //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 9 // :pt // :elt // :sjs :todo // ? do we really want to support this 42 inc() * 1 add(2) //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). //│ ║ l.127: inc() * 1 //│ ║ ^^^^^^^^^ //│ ║ l.128: add(2) //│ ╙── ^^^^ //│ = 42 :e 42 inc() add(2) //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (juxtaposition). //│ ║ l.138: inc() //│ ║ ^^^^^ //│ ║ l.139: add(2) //│ ╙── ^^^^ //│ = 42 // :dp :todo // ? do we really want to support this 42 inc() + 1 //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). //│ ║ l.151: inc() + 1 //│ ╙── ^^^^^^^^^ //│ = 42 :todo // ? do we really want to support this 42 add(2) + 1 inc() //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). //│ ║ l.159: add(2) + 1 //│ ╙── ^^^^^^^^^^ //│ = 43 :todo // ? do we really want to support this 42 add(2) inc() + 1 //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (operator application). //│ ║ l.169: inc() + 1 //│ ╙── ^^^^^^^^^ //│ = 44 2 * 2 inc() inc() //│ = 8 (2 * 2) inc() inc() //│ = 6 (2 * 2 inc()) inc() //│ = 7 (2 * 2 inc()) inc() //│ = 7 (2 * 2 inc() ) inc() //│ = 7 :e :re (2 * 2 inc()) inc() //│ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 //│ ║ l.204: inc() //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Function 'inc' expected 1 argument but got 0 :e :re (2 * 2 inc() ) inc() //│ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 //│ ║ l.215: inc() //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Function 'inc' expected 1 argument but got 0 42 console.log() "Done." console.log() //│ > 42 //│ > Done. :e 42 console.log //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (selection). //│ ║ l.232: 42 console.log //│ ╙── ^^^^^^^^^^^ //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls ================================================ :js :global :sjs x => let y = x y //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; lambda = (undefined, function (x) { let y; y = x; return y }); lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun (acc, _) => acc //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; lambda1 = (undefined, function (acc, _) { return acc }); lambda1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Lazy.mls ================================================ :js data class Lazy[out A](init: () -> A) with mut val cached: A | () = () fun get: A // fun eq(that: Lazy[A]) = this.cached == that.cached // TODO `this` ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/LetPatterns.mls ================================================ :js data class Some[out A](value: A) :todo let Some(x) = Some(42) //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.7: let Some(x) = Some(42) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. object None let N = None //│ N = None :todo let (None) = N //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.20: let (None) = N //│ ╙── ^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. data class Foo(a) :todo let Foo(x) = Foo(1) in print(x) //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.30: let Foo(x) = Foo(1) in print(x) //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.30: let Foo(x) = Foo(1) in print(x) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :todo let Foo(x) and x > 0 = Foo(1) in print("ok") //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.40: let Foo(x) and x > 0 = Foo(1) in print("ok") //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls ================================================ :js #config(commentGeneratedCode: true) object A object B object C object D object E let a = A let b = B //│ a = A //│ b = B :sjs if a is A then 1 B then 2 C then 3 D then 4 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (a instanceof A1.class) { //│ 1 //│ } else if (a instanceof B1.class) { //│ 2 //│ } else if (a instanceof C1.class) { //│ 3 //│ } else if (a instanceof D1.class) { //│ 4 //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // manually written `else if`s branches on the same scrut is also merged :sjs if a is A then 1 else if a is B then 2 C then 3 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (a instanceof A1.class) { //│ 1 //│ } else if (a instanceof B1.class) { //│ 2 //│ } else if (a instanceof C1.class) { //│ 3 //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :sjs if a is A and b is A then 1 B then 2 B then 2 C then 3 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; //│ if (a instanceof A1.class) { //│ if (b instanceof A1.class) { //│ tmp = 1; //│ tmp //│ } else if (b instanceof B1.class) { //│ tmp = 2; //│ tmp //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ } else if (a instanceof B1.class) { //│ tmp = 2; //│ tmp //│ } else if (a instanceof C1.class) { //│ tmp = 3; //│ tmp //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 // The match arms here are still merged because // `let tmp = 3` is moved out so that it does not get into the way // and `print("done")` is duplicated and moved into match arm bodies :sjs let x = if a is A then 1 B then 2 C then 3 else let tmp = 3 if a is D then if a is A then 1 + tmp B then 2 + tmp E then 5 print("done") print(x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x, tmp1; //│ if (a instanceof A1.class) { //│ tmp1 = 1; //│ } else if (a instanceof B1.class) { //│ tmp1 = 2; //│ } else if (a instanceof C1.class) { //│ tmp1 = 3; //│ } else if (a instanceof D1.class) { //│ if (a instanceof A1.class) {} else if (a instanceof B1.class) {} else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ tmp1 = Predef.print("done"); //│ } else if (a instanceof E1.class) { //│ tmp1 = Predef.print("done"); //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ x = tmp1; //│ Predef.print(x) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ x = 1 // The match arms here are still merged because // `let tmp = 2` is moved out so that it does not get into the way :sjs if a is A then 1 let tmp = 2 B then 2 + tmp //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp2; //│ tmp2 = 2; //│ if (a instanceof A1.class) { //│ 1 //│ } else if (a instanceof B1.class) { //│ 2 + tmp2 //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 fun printAndId(x) = print(x) x // `let tmp = printAndId(3)` is not moved out // because the rhs is not trivial :sjs if a is A then 1 let tmp = printAndId(3) B then 2 + tmp //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp3; //│ if (a instanceof A1.class) { //│ 1 //│ } else { //│ tmp3 = printAndId(3); //│ if (a instanceof B1.class) { //│ 2 + tmp3 //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // The match arms here are still merged because // `print(x)` is duplicated and moved into match arm bodies :sjs if a is A then 1 else let x = if a is B then 2 C then 3 print(x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x1, tmp4; //│ if (a instanceof A1.class) { //│ 1 //│ } else if (a instanceof B1.class) { //│ tmp4 = 2; //│ x1 = tmp4; //│ Predef.print(x1) //│ } else if (a instanceof C1.class) { //│ tmp4 = 3; //│ x1 = tmp4; //│ Predef.print(x1) //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // The match arms are not merged because // `print(x); print(x + 1); print(x + 2)` is // not moved into match arm bodies for being too big :sjs if a is B then 1 else let x = if a is A then 2 C then 3 print(x) print(x + 1) print(x + 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x2, tmp5, tmp6, tmp7; //│ if (a instanceof B1.class) { //│ 1 //│ } else { //│ if (a instanceof A1.class) { //│ tmp5 = 2; //│ } else if (a instanceof C1.class) { //│ tmp5 = 3; //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ x2 = tmp5; //│ Predef.print(x2); //│ tmp6 = x2 + 1; //│ Predef.print(tmp6); //│ tmp7 = x2 + 2; //│ Predef.print(tmp7) //│ } //│ /* Rest moved to non-abortive branch(es) */ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 //│ > 3 //│ > 4 :soir fun f(x) = let acc = "" if x is 5 then return acc else if x is 8 then print("8") else print("?") print("hi") acc //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰; //│ define f⁰ as fun f¹(x) { //│ let acc; //│ set acc = ""; //│ match x //│ 5 => //│ return acc //│ 8 => //│ do Predef⁰.print⁰("8"); //│ do Predef⁰.print⁰("hi"); //│ return acc //│ else //│ do Predef⁰.print⁰("?"); //│ do Predef⁰.print⁰("hi"); //│ return acc //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(5) //│ = "" f(6) //│ > ? //│ > hi //│ = "" f(8) //│ > 8 //│ > hi //│ = "" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Misc.mls ================================================ :js 1 as Int //│ = 1 1as Int //│ = 1 (1)as(Int) //│ = 1 :ge // Shouldn't allow using `Int` in term position 1 : Int //│ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term //│ ║ l.14: 1 : Int //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sjs 1 + 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 1 + 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 set (mut []).0 = 1 let arr = mut [] set arr.0 = 1 //│ arr = [1] fun (+!) bar = tuple 1 +! 2 //│ = [1, 2] bar of 1, 2 //│ = [1, 2] bar(1, 2) //│ = [1, 2] 1 \bar(2) //│ = [1, 2] (1 \bar)(2) //│ = [1, 2] (1 \bar)(2, , 3) //│ = [1, 2, 3] (1 \bar)( 2 3 ) //│ = [1, 2, 3] ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ModuleMatching.mls ================================================ :js object Foo fun test(x) = x is Foo test(Foo) //│ = true fun foo() = object Foo Foo is Foo foo() //│ = true fun foo() = class Foo (new Foo) is Foo foo() //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ModuleMethods.mls ================================================ :js module Example with val a = 456 fun f(x) = [x, a] :ssjs Example.f(123) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res2 = runtime.checkCall(Example1.f(123)); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [123, 456] :ssjs let s = Example.f //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let s; //│ s = Example1.f; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ s = fun f s(123) //│ = [123, 456] :e Example |>. s(123) //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type Example. //│ ║ l.27: Example |>. s(123) //│ ║ ^^^^^^^ //│ ╙── Module argument passed to a non-module parameter. //│ = [123, 456] :sjs module Test with let s = 1 print(s) fun foo() = print(s) fun bar() = foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Test1; //│ (class Test { //│ static { //│ Test1 = this //│ } //│ static #s; //│ static { //│ Test.#s = 1; //│ Predef.print(Test.#s); //│ } //│ static foo() { //│ return Predef.print(Test.#s) //│ } //│ static bar() { //│ return Test.foo() //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Test"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 let f = Test.foo //│ f = fun foo // :fixme f() //│ > 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Modules.mls ================================================ :js :sjs module None //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let None1; //│ (class None { //│ static { //│ None1 = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "None"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs None //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ None1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class None :sjs :re None() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(None1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Class constructor None cannot be invoked without 'new' :e new None //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type None. //│ ║ l.33: new None //│ ║ ^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ = None :sjs :e new! None //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type None. //│ ║ l.42: new! None //│ ╙── ^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new None1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = None :sjs module M with class C class D() val x = 1 val y = x + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let M1; //│ (class M { //│ static { //│ M1 = this //│ } //│ static { //│ let tmp; //│ (class C { //│ static { //│ M.C = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C"]; //│ }); //│ this.D = function D() { //│ return globalThis.Object.freeze(new D.class()); //│ }; //│ (class D { //│ static { //│ M.D.class = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "D", []]; //│ }); //│ this.x = 1; //│ tmp = M.x + 1; //│ this.y = tmp; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— M.C //│ = class C new M.C //│ = C M.D() //│ = D() M.x //│ = 1 M.y //│ = 2 :e :re M.oops //│ ╔══[COMPILATION ERROR] Module 'M' does not contain member 'oops' //│ ║ l.109: M.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' // Note: this used to fail, but we now set the module's own binding as the first thing in the static ctor :sjs module M with val m: module M = M //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let M3; //│ (class M2 { //│ static { //│ M3 = this //│ } //│ static { //│ this.m = M3; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs M.m //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ M3.m //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class M { m: ref'1 } as ref'1 module M() // * Peculiarity (should not be a problem): notice that we have `BB.y`, and not `AA.BB.y` :sjs module AA with val x = 1 module BB with val y = 2 [x, y] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let AA1; //│ (class AA { //│ static { //│ AA1 = this //│ } //│ static { //│ this.x = 1; //│ (class BB { //│ static { //│ AA.BB = this //│ } //│ static { //│ this.y = 2; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "BB"]; //│ }); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "AA"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/NestedClasses.mls ================================================ :js module Foo with data class C(x) Foo.C(123).x //│ = 123 (new Foo.C(123)).x //│ = 123 let c = Foo.C //│ c = fun C { class: class C } c(123) //│ = C(123) :e new c(123) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. //│ ║ l.20: new c(123) //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ = C(123) new! c(123) //│ = C(123) object D extends Foo.C(123) with print(this!x) print(id(this).x) //│ > 123 //│ > 123 :e object D extends Foo.C(123) with print(this.x) //│ ╔══[COMPILATION ERROR] Object 'D' does not contain member 'x' //│ ║ l.38: print(this.x) //│ ╙── ^^^^^^ //│ > 123 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls ================================================ :js // * NOTE: usually, Scoped blocks are flattened by the IR smart constructors // * Flattened :sir :sjs scope.locally of 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 42 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 :sir let x = 42 in x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x⁰; set x⁰ = 42; x⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // * Flattened :sir scope.locally of let x = 42 in x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x¹; set x¹ = 42; x¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // :sjs scope.locally of ( let x = 42 x ) //│ = 42 :sir // :sjs scope.locally of scope.locally of ( let x = 42 x ) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x²; set x² = 42; x² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 :e x //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.58: x //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun foo(x) = let z = 1 scope.locally of let y = 1 in x + y + z foo(1) //│ = 3 // * Not flattened: in conditional arms :sjs fun f() = if true then scope.locally of ( let a = 4 a + 4 ) else let b = 5 b + 9 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; //│ f = function f() { //│ let scrut, b; //│ scrut = true; //│ if (scrut === true) { let a; a = 4; return a + 4 } //│ b = 5; //│ return b + 9; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f() //│ = 8 // * Idem :sjs fun f() = if false then let a = 4 a + 4 else scope.locally of ( let b = 5 b + 9 ) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; //│ f1 = function f() { //│ let scrut, a; //│ scrut = false; //│ if (scrut === true) { a = 4; return a + 4 } //│ { let b; b = 5; return b + 9; } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f() //│ = 14 fun g(x, y, z) = 3 + 4 * if x then 0 y then 0 z then scope.locally of ( let a = 1 (_ + a) ) fun foo(x, y) = let z = [1, 2, 3] if not x > z.0 do print("rua") let m = x + y scope.locally of ( let w1 = z.1 + m let w2 = z.2 - m if w1 == w2 do print("ruarua") ) :ge scope.locally() //│ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. //│ ║ l.154: scope.locally() //│ ╙── ^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge scope.locally of (let x = 1 in x), (let y = 2 in y) //│ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. //│ ║ l.162: scope.locally of (let x = 1 in x), (let y = 2 in y) //│ ╙── ^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :ge scope.locally of (let x = 1 in x), (let y = 2 in x) //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.171: scope.locally of (let x = 1 in x), (let y = 2 in x) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. //│ ║ l.171: scope.locally of (let x = 1 in x), (let y = 2 in x) //│ ╙── ^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let z = 114 scope.locally of ( let y = 514 y ) //│ = 514 //│ z = 114 // :sjs let z = 2 scope.locally of ( let y = () => 1 y ) //│ = fun y //│ z = 2 // NOTE: `z2` (bms) should be declared in the same place as `z3` :sjs fun f(x) = if x then let y = 2 scope.locally of ( let z = () => 3 z ) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2; //│ f2 = function f(x4) { //│ if (x4 === true) { //│ let z2, z3; //│ z3 = function z() { //│ return 3 //│ }; //│ z2 = z3; //│ return z2 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :lift scope.locally of scope.locally of ( fun h() = fun f() = g() fun g() = f() 4 3 ) //│ = 3 // :sjs let x = 3 () => x //│ = fun //│ x = 3 :sjs fun f(x) = if x then () => 4 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f3; //│ f3 = function f(x5) { //│ if (x5 === true) { //│ let lambda1; //│ lambda1 = (undefined, function () { //│ return 4 //│ }); //│ return lambda1 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/NestedTypes.mls ================================================ :js module M with type T = Int fun f[A] = (x: A) => x f[M.T](42) //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/NoFreeze.mls ================================================ :js :ssjs class Foo(val x) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo(x) { //│ return globalThis.Object.freeze(new Foo.class(x)); //│ }; //│ globalThis.Object.freeze(class Foo { //│ static { //│ Foo1.class = this //│ } //│ constructor(x) { //│ this.x = x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :ssjs :noFreeze class Foo(val x) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let Foo3; //│ Foo3 = function Foo(x) { //│ return (new Foo.class(x)); //│ }; //│ (class Foo2 { //│ static { //│ Foo3.class = this //│ } //│ constructor(x) { //│ this.x = x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :ssjs let foo = new Foo(1) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let foo; //│ foo = globalThis.Object.freeze(new Foo3.class(1)); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = Foo(1) :ssjs :noFreeze let foo = new Foo(1) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let foo1; //│ foo1 = (new Foo3.class(1)); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = Foo(1) set foo.x = 42 :expect 42 foo.x //│ = 42 import "../../mlscript-compile/NoFreeze.mls" let foo = NoFreeze.foo() //│ foo = Foo(0) set foo.x = 42 :expect 42 foo.x //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/NoModuleCheck.mls ================================================ :js module M with fun foo(x) = x + 1 :e M."foo"(1) //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.8: M."foo"(1) //│ ╙── ^ //│ = 2 :noModuleCheck M."foo"(1) //│ = 2 :e fun foo(): module M = let m = M in m //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type M. //│ ║ l.22: let m = M in m //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Expected a module; found non-moduleful block. //│ ║ l.22: let m = M in m //│ ║ ^^^^^^ //│ ╙── Function marked as returning a 'module' but not returning a module. :noModuleCheck fun foo(): module M = let m = M in m foo().foo(1) //│ = 2 class Bar(val x) :e let bar = Bar in new bar(1) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. //│ ║ l.43: let bar = Bar in new bar(1) //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ = Bar(1) :noModuleCheck let bar = Bar in new bar(1) //│ = Bar(1) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ObjectInit.mls ================================================ :js fun f = print(A) object A with f // A is not fully initialized yet but can still be accessed val x = 1 //│ > A {} // * By contrast: fun f = print(A) class Acls with f // A is not accessible at all here val x = 1 val A = new Acls //│ > undefined //│ A = Acls { x: 1 } // * We still need an initialization checker to catch things like this: fun f = print(A.x) object A with f val x = 1 //│ > undefined fun f = print(A, A.x) object A with f val x = 1 f //│ > A5 {} undefined //│ > A5 { x: 1 } 1 f //│ > A { x: 1, class: object A } 1 object A with print(B) object B //│ > undefined ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ObjectMethodDebinding.mls ================================================ :js object Test with fun bar = 1 fun foo() = bar + 1 :e let f = Test.foo //│ ╔══[COMPILATION ERROR] [debinding error] Method 'foo' cannot be accessed without being called. //│ ║ l.9: let f = Test.foo //│ ╙── ^^^^ //│ f = fun foo // TODO make this a type error // :e :re let f = id(Test).foo //│ ═══[RUNTIME ERROR] Error: [debinding error] Method 'foo' of class 'Test' was accessed without being called. //│ f = undefined // * Note that dynamic member accesses are not checked against debinding: let f = Test.("foo") //│ f = fun foo :re f() //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'bar') // * I tried compiling selections to essentially the following, // * but it doesn't work since many things in JS are "functions" // * (and notably may have fields, which are not preserved by `bind`): let g = if f is Function then f.bind(Test) else f //│ g = fun bound foo g() //│ = 2 // * TODO we should probably compile the above as: object Test with fun bar = 1 fun foo() = Test.bar + 1 let f = Test.("foo") //│ f = fun foo f() //│ = 2 // * Which also works well no matter where the object is defined: fun test() = object Test with fun bar = 1 fun foo() = Test.bar + 1 [Test] let f = test().0.("foo") //│ f = fun foo f() //│ = 2 // * However, it would disable open recursion upon object inheritance (eg `class C extends Test with ...`). // * But it would be better to require open recursion to be explicitly indicated by `virtual` anyway. ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Open.mls ================================================ :js module Foo with val x = 1 val y = 2 val z = 3 :sjs open Foo { x } //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— open Foo { x, y } x + y //│ = 3 :e z //│ ╔══[COMPILATION ERROR] Name not found: z //│ ║ l.21: z //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e open Foo { a } //│ ╔══[COMPILATION ERROR] Module 'Foo' does not contain member 'a' //│ ║ l.28: open Foo { a } //│ ╙── ^ open Bar { y, Baz } open Baz { b } module Bar with val x = 1 val y = 2 module Baz with val a = 4 val b = 5 open Baz { a } fun f() = [a, b, y] val str = "oops" //│ str = "oops" open str { length, oops } length //│ = 4 oops === () //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/OpenJS.mls ================================================ :js open console { log } open Math { pow } log(123 pow(2)) //│ > 15129 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/OpenWildcard.mls ================================================ :js import "../../mlscript-compile/Option.mls" module OtherModule with val None = 123 :sjs open Option //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs None isDefined() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Option.isDefined(Option.None) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = false Some(1) isDefined() //│ = true None === Option.None //│ = true fun none() = None open OtherModule None === Option.None //│ = false none() === Option.None //│ = true open Option fun none() = None console.log(None) open OtherModule console.log(None) none() //│ > None {} //│ > 123 //│ = None // * `open` statements are hygienic and are not affected by redefinitions :sjs val Option = "Oops" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Option1; Option1 = "Oops"; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ Option = "Oops" :sjs Some(123) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Option.Some(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Some(123) module Option with val None = 123 :sjs open Option //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs Some //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Option.Some //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun Some { class: class Some } :sjs None //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Option3.None //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls ================================================ :js data class Some(value) object None :sjs fun isDefined(x) = if x is Some then true None then false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let isDefined; //│ isDefined = function isDefined(x) { //│ if (x instanceof Some1.class) { //│ return true //│ } else if (x instanceof None1.class) { //│ return false //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— isDefined(Some(1)) //│ = true isDefined(None) //│ = false :sjs val isDefined = case Some(_) then true None then false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let isDefined1, lambda; //│ lambda = (undefined, function (caseScrut) { //│ if (caseScrut instanceof Some1.class) { //│ return true //│ } else if (caseScrut instanceof None1.class) { //│ return false //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ isDefined1 = lambda; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ isDefined = fun isDefined(Some(1)) //│ = true isDefined(None) //│ = false val isDefined = x => if x is Some(_) then true None then false //│ isDefined = fun isDefined(Some(1)) //│ = true isDefined(None) //│ = false module Foo with data class Other(v) val isOther = x => if x is Foo.Other(_) then true None then false //│ isOther = fun fun keepIfGreaterThan(x, y) = if x > y then Some(x) else None 1 keepIfGreaterThan(0) isDefined() //│ = true 1 keepIfGreaterThan(0) isDefined() console.log() //│ > true 1 \ keepIfGreaterThan(0) //│ = Some(1) 1 \ keepIfGreaterThan(0) \ isDefined() \ console.log() //│ > true 1 |> (keepIfGreaterThan(_, 0)) |> (isDefined(_)) //│ = true 1 |> {keepIfGreaterThan(_, 0)} |> {isDefined(_)} //│ = true 1 |> keepIfGreaterThan(_, 0) |> isDefined(_) //│ = true let isDefined2 = _ is Some //│ isDefined2 = fun isDefined2 Some(1) isDefined2() //│ = true Some(1) |> isDefined2 //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls ================================================ :js :global :sjs data class Foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo() { //│ return globalThis.Object.freeze(new Foo.class()); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", []]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun Foo { class: class Foo } Foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo1() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo() Foo.class //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo1.class //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class Foo data class Foo(a) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo3; //│ Foo3 = function Foo(a) { //│ return globalThis.Object.freeze(new Foo.class(a)); //│ }; //│ (class Foo2 { //│ static { //│ Foo3.class = this //│ } //│ constructor(a) { //│ this.a = a; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["a"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo3 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun Foo { class: class Foo } Foo(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo3(1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo(1) Foo(1).a //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = Foo3(1); tmp.a //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 fun foo(y) = Foo(y) foo(27) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo, y, inlinedVal; //│ foo = function foo(y1) { return Foo3(y1) }; //│ y = 27; //│ inlinedVal = Foo3(y); //│ inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo(27) data class Foo(a, b) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo5; //│ Foo5 = function Foo(a, b) { //│ return globalThis.Object.freeze(new Foo.class(a, b)); //│ }; //│ (class Foo4 { //│ static { //│ Foo5.class = this //│ } //│ constructor(a, b) { //│ this.a = a; //│ this.b = b; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["a", "b"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let foo = Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo1; foo1 = Foo5; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = fun Foo { class: class Foo } let f = foo(1, 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; f = runtime.safeCall(foo1(1, 2)); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = Foo(1, 2) let f = new! foo(1, 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; f1 = globalThis.Object.freeze(new foo1(1, 2)); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = Foo(1, 2) f.a //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f1.a //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 f.b //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f1.b //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 let f = Foo(1, 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2; f2 = Foo5(1, 2); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = Foo(1, 2) f.a //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f2.a //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 f.b //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ f2.b //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 Foo(print(1), print(2)) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp1, tmp2; tmp1 = Predef.print(1); tmp2 = Predef.print(2); Foo5(tmp1, tmp2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > 2 //│ = Foo((), ()) data class Inner(c) with fun i1(d) = c + d print(c) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Inner1; //│ Inner1 = function Inner(c) { //│ return globalThis.Object.freeze(new Inner.class(c)); //│ }; //│ (class Inner { //│ static { //│ Inner1.class = this //│ } //│ constructor(c) { //│ this.c = c; //│ Predef.print(this.c); //│ } //│ i1(d) { //│ return this.c + d //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Inner", ["c"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let i = new Inner(100) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let i; i = globalThis.Object.freeze(new Inner1.class(100)); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 100 //│ i = Inner(100) i.i1(20) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(i.i1(20)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 120 // * This is strange, but does not need to be an error class Foo(x, val y, z, val z, z) with let x = 1 let y += 1 print("x = " + x) print("y = " + y) print("this.y = " + this.y) print("z = " + z) print("this.z = " + this.z) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo7; //│ Foo7 = function Foo(x, y1, z, z1, z2) { //│ return globalThis.Object.freeze(new Foo.class(x, y1, z, z1, z2)); //│ }; //│ (class Foo6 { //│ static { //│ Foo7.class = this //│ } //│ constructor(x, y1, z, z1, z2) { //│ let tmp3, tmp4, tmp5, tmp6, tmp7; //│ this.y = y1; //│ this.z = z1; //│ this.#z1 = z2; //│ this.#x1 = 1; //│ this.#y = this.y + 1; //│ tmp3 = "x = " + this.#x1; //│ Predef.print(tmp3); //│ tmp4 = "y = " + this.#y; //│ Predef.print(tmp4); //│ tmp5 = "this.y = " + this.y; //│ Predef.print(tmp5); //│ tmp6 = "z = " + this.#z1; //│ Predef.print(tmp6); //│ tmp7 = "this.z = " + this.z; //│ Predef.print(tmp7); //│ } //│ #x; //│ #z; //│ #z1; //│ #x1; //│ #y; //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", [null, "y", null, "z", null]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Foo(10, 20, 30, 40, 50) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo7(10, 20, 30, 40, 50) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > x = 1 //│ > y = 21 //│ > this.y = 20 //│ > z = 50 //│ > this.z = 40 //│ = Foo(_, 20, _, 40, _) :e class Foo(val z, val z) //│ ╔══[COMPILATION ERROR] Duplicate definition of member named 'z'. //│ ╟── Defined at: //│ ║ l.262: class Foo(val z, val z) //│ ║ ^ //│ ╟── Defined at: //│ ║ l.262: class Foo(val z, val z) //│ ╙── ^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo9; //│ Foo9 = function Foo(z, z1) { //│ return globalThis.Object.freeze(new Foo.class(z, z1)); //│ }; //│ (class Foo8 { //│ static { //│ Foo9.class = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["z", "z"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Foo(1, 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo9(1, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo(undefined, undefined) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls ================================================ :js fun foo(x, y, z) = [x, y, z] let f = foo(1, _, 3) //│ f = fun f f(2) //│ = [1, 2, 3] :sjs let f = foo(1, _, _) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2, f3; f3 = function f(_0, _1) { return foo(1, _0, _1) }; f2 = f3; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f f(2, 3) //│ = [1, 2, 3] let g = f(_, 3) //│ g = fun g g(2) //│ = [1, 2, 3] foo(_, _, 3) of 1, 2 //│ = fun {foo(_, _, 3)} of 1, 2 //│ = [1, 2, 3] {foo(_, _, 3)}(1, 2) //│ = [1, 2, 3] fun foo2(x, y, z)(a, b) = [x, y, z, a, b] {foo2(_, _, 3)(4, _)}(1, 2, 5) //│ = [1, 2, 3, 4, 5] // :sjs let f = [_] f(27) //│ = [27] //│ f = fun f // :sjs let f = [_ + 1] f.0(27) //│ = 28 //│ f = [fun] let f = foo(..._) f([1, 2, 3]) //│ = [1, 2, 3] //│ f = fun f let f = [..._] f([1, 2, 3]) //│ = [1, 2, 3] //│ f = fun f // :sjs let f = [..._] f([]) //│ = [] //│ f = fun f let h = _ - 2 //│ h = fun h h(1) //│ = -1 :sjs let i = _(0, 1, _) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let i, i1; i1 = function i(_03, _13) { return runtime.safeCall(_03(0, 1, _13)) }; i = i1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ i = fun i i(print, 2) //│ > 0 1 2 :re i(print)(2) //│ ═══[RUNTIME ERROR] Error: Function 'i' expected 2 arguments but got 1 // :re i((x, y, z) => x + y + z, 2) //│ = 3 // :re i((...args) => [...args], _)(2) //│ = fun 123 |> (_ + 1) //│ = 124 // * Note: missing parens 123 |> _ + 1 //│ = fun 123 |> ((_ + 1) >> (_ * 2)) //│ = 248 :re 123 |> (_ + 1 >> _ * 2) //│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 (_ + 1 + _ * 2) of 123, 456 //│ = 1036 // * Note: lack of parens let f = _ * 2 + 1 |> print in f(3) //│ > 7 123 |> (_ + 1) |> (_ * 2) //│ = 248 (_ * 2) <| (_ + 1) <| 123 //│ = 248 (_ + 1) >> (_ * 2) <| 123 //│ = 248 let j = _.x in j(x: 123) //│ = 123 let j = _.x(123) in j(x: print) //│ > 123 let j = _.x(1, _) in j((x: tuple), 0) //│ = [1, 0] // ie let j = (obj, arg) => obj.x(1, arg) in j((x: tuple), 0) //│ = [1, 0] // TODO: really support this? let j = _.x.y(1, _) in j((x: y: tuple), 0) //│ = [1, 0] data class C(a, b, c) 2 |> C(1, _, 3) |> print //│ = fun 2 |> (C(1, _, 3)) |> print //│ > C(1, 2, 3) 2 |> {C(1, _, 3)} |> print //│ > C(1, 2, 3) 2 |> {C(1, _, 3)} !> print //│ > C(1, 2, 3) //│ = C(1, 2, 3) {2 |> {C(1, _, 3)}} !> print //│ > C(1, 2, 3) //│ = C(1, 2, 3) 2 |> {C(1, _, 3)} !> {print(_)} |> {print(_)} //│ > C(1, 2, 3) //│ > C(1, 2, 3) (2 |> (new C(1, _, 3))) |> print //│ > C(1, 2, 3) 2 |> (new C(1, _, 3)) |> print //│ > C(1, 2, 3) (_ - _) of 1, 2 //│ = -1 // * Note: missing parens _ - _ of 1, 2 //│ = fun (_ - 2) <| 1 //│ = -1 1 |> (_ - 2) //│ = -1 1 |> (_ - 2) //│ = -1 :pe |> 1 //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.202: |> 1 //│ ╙── ^ //│ = fun pipeInto :e :re |> (1) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.210: |> (1) //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] TypeError: f is not a function |> (1, _ - 2) //│ = -1 1 |> (_ - 2) //│ = -1 1 |> _ - 2 //│ = -1 :sjs 1 \ (_ - 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda39; lambda39 = (undefined, function (_05) { return _05 - 2 }); Predef.passTo(1, lambda39) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :sjs 1 \ (_ - 2)() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda40; //│ lambda40 = (undefined, function (_05) { return _05 - 2 }); //│ Predef.passTo(1, lambda40)() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = -1 1 \ (_ - _)(2) //│ = -1 // `_` here is interpreted as a UCS default case... :sjs :w let f = if _ then 1 else 0 //│ ╔══[WARNING] This catch-all clause makes the following branches unreachable. //│ ║ l.250: let f = if _ then 1 else 0 //│ ║ ^^^^^^ //│ ╟── This branch is unreachable. //│ ║ l.250: let f = if _ then 1 else 0 //│ ╙── ^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f15, tmp20; tmp20 = 1; f15 = tmp20; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = 1 :sjs fun f(x) = if x == 0 then 1 _ then 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f16; //│ f16 = function f(x3) { //│ let scrut; //│ scrut = Predef.equals(x3, 0); //│ if (scrut === true) { return 1 } //│ return 2; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls ================================================ :js :global :sjs class Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ (class Foo { //│ static { //│ Foo1 = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Foo is Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (Foo1 instanceof Foo1) { true } else { false } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = false (new Foo) is Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; //│ scrut = globalThis.Object.freeze(new Foo1()); //│ if (scrut instanceof Foo1) { true } else { false } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true new Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new Foo1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo new Foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new Foo1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Foo1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class Foo :re Foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(Foo1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Class constructor Foo cannot be invoked without 'new' data class Foo with { print("hi") } print("ok") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo3; //│ (class Foo2 { //│ static { //│ Foo3 = this //│ } //│ constructor() { //│ Predef.print("hi"); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ Predef.print("ok") //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > ok fun test() = data class Foo with { print("hi") } print("ok") Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test; //│ test = function test() { //│ let Foo5; //│ (class Foo4 { //│ static { //│ Foo5 = this //│ } //│ constructor() { //│ Predef.print("hi"); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ Predef.print("ok"); //│ return Foo5 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let t = test() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let t; t = test(); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > ok //│ t = class Foo :e new t //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. //│ ║ l.108: new t //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new t()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = Foo new! t //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new t()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = Foo :e new t() //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. //│ ║ l.127: new t() //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new t()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = Foo new! t() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new t()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = Foo class Foo with val x = 1 let y = x + 1 fun z() = y + x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo6; //│ (class Foo5 { //│ static { //│ Foo6 = this //│ } //│ constructor() { //│ this.x = 1; //│ this.#y = this.x + 1; //│ } //│ #y; //│ z() { //│ return this.#y + this.x //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— class Foo with val x1 = 1 val x2 = 2 let y1 = 3 let y2 = 4 fun z1() = 5 fun z2() = 6 print("hello") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo8; //│ (class Foo7 { //│ static { //│ Foo8 = this //│ } //│ constructor() { //│ this.x1 = 1; //│ this.x2 = 2; //│ Predef.print("hello"); //│ } //│ #y1; //│ #y2; //│ z1() { //│ return 5 //│ } //│ z2() { //│ return 6 //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— class Foo with val x = 1 fun foo(y) = x + y fun bar(z) = foo(z) + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo10; //│ (class Foo9 { //│ static { //│ Foo10 = this //│ } //│ constructor() { //│ this.x = 1; //│ } //│ foo(y) { //│ return this.x + y //│ } //│ bar(z) { //│ let tmp; //│ tmp = this.foo(z); //│ return tmp + 1 //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let a = new Foo print(a.x) print(a.foo(1)) print(a.bar(1)) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a, tmp, tmp1; //│ a = globalThis.Object.freeze(new Foo10()); //│ Predef.print(a.x); //│ tmp = runtime.safeCall(a.foo(1)); //│ Predef.print(tmp); //│ tmp1 = runtime.safeCall(a.bar(1)); //│ Predef.print(tmp1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > 2 //│ > 3 //│ a = Foo { x: 1 } :e class Foo with val x = 1 val x = 2 //│ ╔══[COMPILATION ERROR] Multiple definitions of symbol 'x' //│ ╟── defined here //│ ║ l.251: val x = 1 //│ ║ ^^^^^^^^^ //│ ╟── defined here //│ ║ l.252: val x = 2 //│ ╙── ^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo12; //│ (class Foo11 { //│ static { //│ Foo12 = this //│ } //│ constructor() { //│ this.x = 1; //│ this.x = 2; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :e class Foo with val x = 1 let x = 2 //│ ╔══[COMPILATION ERROR] Name 'x' is already used //│ ║ l.278: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block //│ ║ l.277: val x = 1 //│ ╙── ^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo14; //│ (class Foo13 { //│ static { //│ Foo14 = this //│ } //│ constructor() { //│ this.x = 1; //│ } //│ #x; //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // ——— TODO ——— :e class Foo with val x = 1 //│ ╔══[COMPILATION ERROR] Illegal body of class definition (should be a block; found term definition). //│ ║ l.308: class Foo with val x = 1 //│ ╙── ^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo16; //│ (class Foo15 { //│ static { //│ Foo16 = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls ================================================ :js not(true) //│ = false not(false) //│ = true print(12) //│ > 12 12 |> print(_) //│ > 12 :sjs 12 |> print //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Predef.pipeInto(12, Predef.print) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 12 12 |> x => print(x) //│ > 12 12 |> (x => print(x)) //│ > 12 12 |> (x => console.log(String(x))) //│ > 12 42 id() id() print() //│ > 42 Predef.print(123) //│ > 123 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Primes.mls ================================================ :js let x = 1 //│ x = 1 :sjs let x' = 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x$_; x$_ = 2; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ x' = 2 x' //│ = 2 :e object Foo with val x' = 0 //│ ╔══[COMPILATION ERROR] Illegal member value name: 'x'' //│ ║ l.20: val x' = 0 //│ ║ ^^ //│ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e :re Foo.x' //│ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x'' //│ ║ l.28: Foo.x' //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x'' yielded 'undefined' Foo.("x'") :e object Foo with mut val x' = 0 //│ ╔══[COMPILATION ERROR] Illegal member mutable value name: 'x'' //│ ║ l.38: mut val x' = 0 //│ ║ ^^ //│ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e :re set Foo.x' += 1 //│ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x'' //│ ║ l.46: set Foo.x' += 1 //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x'' //│ ║ l.46: set Foo.x' += 1 //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x'' yielded 'undefined' Foo.("x'") :e class Foo with fun f' = 1 //│ ╔══[COMPILATION ERROR] Illegal member function name: 'f'' //│ ║ l.60: fun f' = 1 //│ ║ ^^ //│ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e class Foo with class F' //│ ╔══[COMPILATION ERROR] Illegal member class name: 'F'' //│ ║ l.68: class F' //│ ║ ^^ //│ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e class Foo with object F' //│ ╔══[COMPILATION ERROR] Illegal member object name: 'F'' //│ ║ l.76: object F' //│ ║ ^^ //│ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. :e class Foo with module F' //│ ╔══[COMPILATION ERROR] Illegal member module name: 'F'' //│ ║ l.84: module F' //│ ║ ^^ //│ ╙── Member names must start with a letter or underscore, followed by letters, digits, or underscores. class Foo with let f' = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Pwd.mls ================================================ :js :sjs let folderName1 = process.env.PWD.split("/").pop() in let folderName2 = process.cwd().split("/").pop() in folderName2 === folderName1 || folderName2 === "shared" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let folderName1, folderName2, tmp, tmp1, tmp2, tmp3; //│ tmp = runtime.safeCall(globalThis.process.env.PWD.split("/")); //│ folderName1 = runtime.safeCall(tmp.pop()); //│ tmp1 = runtime.safeCall(globalThis.process.cwd()); //│ tmp2 = runtime.safeCall(tmp1.split("/")); //│ folderName2 = runtime.safeCall(tmp2.pop()); //│ tmp3 = folderName2 === folderName1; //│ if (tmp3 === false) { folderName2 === "shared" } else { true } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/QQImport.mls ================================================ :js :qq meta.print //│ = fun print meta.print(`42) //│ > 42 meta.print(`+) //│ > + meta.print(`1 `+ `2) //│ > (+)(1, 2) meta.print(x `=> x) //│ > (x_0) => //│ > x_0 meta.print((x, y) `=> x `+ y) //│ > (x_0, y_0) => //│ > (+)(x_0, y_0) meta.print(`let x = `42 `in x) //│ > let x_0 //│ > x_0 = 42 //│ > x_0 meta.print(`if `true then `true else `false) //│ > if //│ > let scrut_0 = true //│ > scrut_0 is true then true //│ > else false meta.print(x `=> `if x `=== `0.0 then `1.0 else x) //│ > (x_0) => //│ > if //│ > let scrut_0 = (===)(x_0, 0) //│ > scrut_0 is true then 1 //│ > else x_0 import "../../mlscript-compile/QuoteExample2.mls" QuoteExample2.codegen() import "../../mlscript-compile/quotes/QuoteFoo.mls" QuoteFoo.res //│ = 2 import "../../mlscript-compile/quotes/QuoteInc.mls" QuoteInc.res(42) //│ = 43 import "../../mlscript-compile/CSP.mls" meta.codegen(CSP.foo(), "./hkmc2/shared/src/test/mlscript-compile/quotes/CSPFoo.mls") import "../../mlscript-compile/quotes/CSPFoo.mls" CSPFoo.res //│ = 124 meta.codegen(CSP.bar(), "./hkmc2/shared/src/test/mlscript-compile/quotes/CSPBar.mls") import "../../mlscript-compile/quotes/CSPBar.mls" CSPBar.res //│ = 1 meta.codegen(CSP.baz(), "./hkmc2/shared/src/test/mlscript-compile/quotes/CSPBaz.mls") import "../../mlscript-compile/quotes/CSPBaz.mls" CSPBaz.res //│ = 42 QuoteExample2.genCubic() import "../../mlscript-compile/quotes/Cubic.mls" let cubic = Cubic.res cubic(8) //│ = 512 //│ cubic = fun QuoteExample2.genGib12() import "../../mlscript-compile/quotes/Gib12.mls" let gib12 = Gib12.res gib12(0, 1) //│ = 144 //│ gib12 = fun QuoteExample2.genSafeDiv() import "../../mlscript-compile/quotes/SafeDiv.mls" let safeDiv = SafeDiv.res //│ safeDiv = fun safeDiv(10, 3, 0) //│ = 3.3333333333333335 safeDiv(10, 0, -1) //│ = -1 QuoteExample2.genOpened() import "../../mlscript-compile/quotes/Opened.mls" Opened.res //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Quasiquotes.mls ================================================ :js :qq `42 //│ = Lit(42) `3.14 //│ = Lit(3.14) `true //│ = Lit(true) `"abc" //│ = Lit("abc") `+ //│ = Builtin("+") `1 `+ `2 //│ = App(Builtin("+"), Tup([Lit(1), Lit(2)])) x `=> x //│ = Lam([Symbol("x")], Ref(Symbol("x"))) :sjs x `=> print(x) x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x1, tmp6, tmp7, arr2; //│ tmp6 = globalThis.Object.freeze(new Term.Symbol("x")); //│ x1 = globalThis.Object.freeze(new Term.Ref(tmp6)); //│ Predef.print(x1); //│ arr2 = globalThis.Object.freeze([ //│ tmp6 //│ ]); //│ tmp7 = x1; //│ globalThis.Object.freeze(new Term.Lam(arr2, tmp7)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Ref(Symbol("x")) //│ = Lam([Symbol("x")], Ref(Symbol("x"))) let f = x `=> x `+ `1 //│ f = Lam([Symbol("x")], App(Builtin("+"), Tup([Ref(Symbol("x")), Lit(1)]))) f`(`0) //│ = App( //│ Lam([Symbol("x")], App(Builtin("+"), Tup([Ref(Symbol("x")), Lit(1)]))), //│ Tup([Lit(0)]) //│ ) `let x = `42 `in x //│ = Blk( //│ [LetDecl(Symbol("x")), DefineVar(Symbol("x"), Lit(42))], //│ Ref(Symbol("x")) //│ ) `let x = `42 `in print(x); x //│ > Ref(Symbol("x")) //│ = Blk( //│ [LetDecl(Symbol("x")), DefineVar(Symbol("x"), Lit(42))], //│ Ref(Symbol("x")) //│ ) :sjs `if `true then `true else `false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38; //│ tmp28 = globalThis.Object.freeze(new Term.Symbol("scrut")); //│ scrut = globalThis.Object.freeze(new Term.Ref(tmp28)); //│ tmp29 = globalThis.Object.freeze(new Term.Lit(true)); //│ tmp32 = scrut; //│ tmp33 = globalThis.Object.freeze(new Term.LitPattern(true)); //│ tmp38 = globalThis.Object.freeze(new Term.Lit(true)); //│ tmp34 = globalThis.Object.freeze(new Term.Else(tmp38)); //│ tmp35 = globalThis.Object.freeze(new Term.Branch(tmp32, tmp33, tmp34)); //│ tmp37 = globalThis.Object.freeze(new Term.Lit(false)); //│ tmp36 = globalThis.Object.freeze(new Term.Else(tmp37)); //│ tmp30 = globalThis.Object.freeze(new Term.Cons(tmp35, tmp36)); //│ tmp31 = globalThis.Object.freeze(new Term.Let(tmp28, tmp29, tmp30)); //│ globalThis.Object.freeze(new Term.IfLike(Term.Keyword.If, tmp31)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = IfLike( //│ If, //│ Let( //│ Symbol("scrut"), //│ Lit(true), //│ Cons( //│ Branch(Ref(Symbol("scrut")), LitPattern(true), Else(Lit(true))), //│ Else(Lit(false)) //│ ) //│ ) //│ ) x `=> `if x `=== `0.0 then `1.0 else x //│ = Lam( //│ [Symbol("x")], //│ IfLike( //│ If, //│ Let( //│ Symbol("scrut"), //│ App(Builtin("==="), Tup([Ref(Symbol("x")), Lit(0)])), //│ Cons( //│ Branch(Ref(Symbol("scrut")), LitPattern(true), Else(Lit(1))), //│ Else(Ref(Symbol("x"))) //│ ) //│ ) //│ ) //│ ) :fixme // TODO: error doesn't make much sense (see https://github.com/hkust-taco/mlscript/pull/368#discussion_r2635074722) x `=> `if x `== `0.0 then `1.0 else x //│ ╔══[COMPILATION ERROR] Cross-stage reference to Predef.equals is only allowed in compiled files due to the lack of `import.meta` in REPL mode. //│ ║ l.126: x `=> `if x `== `0.0 then `1.0 else x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // We cannot quote a symbol from another file here due to the lack of `import.meta` in REPL mode. :ge `id`(`42) //│ ╔══[COMPILATION ERROR] Cross-stage reference to Predef.id is only allowed in compiled files due to the lack of `import.meta` in REPL mode. //│ ║ l.134: `id`(`42) //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun id(x) = x `id`(`42) //│ = App(fun id, Tup([Lit(42)])) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls ================================================ :js // * Note that this is not currently treated as tail-recursive (for dumb reasons) :sjs fun foo() = if false do foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo; //│ foo = function foo() { //│ let scrut; //│ scrut = false; //│ if (scrut === true) { foo(); return runtime.Unit } //│ return runtime.Unit; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * ...but this is (so it's tailrec-optimized) :sjs fun foo() = if false do return foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo1; //│ foo1 = function foo() { //│ loopLabel: while (true) { //│ let scrut; //│ scrut = false; //│ if (scrut === true) { continue loopLabel } //│ return runtime.Unit; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun foo() = foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo2; foo2 = function foo() { loopLabel: while (true) { continue loopLabel; } }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs data class Foo(x) with class Bar with val y = x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo(x) { //│ return globalThis.Object.freeze(new Foo.class(x)); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ constructor(x) { //│ this.x = x; //│ const this$Foo = this; //│ (class Bar { //│ static { //│ this$Foo.Bar = this //│ } //│ constructor() { //│ this.y = this$Foo.x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Bar"]; //│ }); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun foo() = fun bar() = bar() bar() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo3; foo3 = function foo() { let bar; bar = function bar() { return bar() }; return bar() }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs let f = 1 do fun f = f print(() => f) () //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f, lambda; //│ f = 1; //│ lambda = (undefined, function () { //│ loopLabel: while (true) { continue loopLabel; } //│ }); //│ Predef.print(lambda); //│ runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > fun //│ f = 1 :sjs :e let foo = 1 fun foo(x) = foo //│ ╔══[COMPILATION ERROR] Name 'foo' is already used //│ ║ l.100: let foo = 1 //│ ║ ^^^^^^^ //│ ╟── by a member declared in the same block //│ ║ l.101: fun foo(x) = foo //│ ╙── ^^^^^^^^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo4; foo4 = 1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = 1 :sjs :re foo(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(foo4(1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: foo4 is not a function fun f(x) = if x is 1 and x > 0 then return print("huh") ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls ================================================ :js fun foo() = let x = 1 let f = () => x fun g() = x let x = 2 f() g() foo() //│ = 1 let x = 1 let f = () => x //│ f = fun f //│ x = 1 f() //│ = 1 let x = 2 //│ x = 2 f() //│ = 1 // * Notice that we pick a different private field name for each `x` object Foo with let x = 1 let f = () => x fun g() = x print(f()) let x = 2 print(x) print(f()) print(g()) //│ > 1 //│ > 2 //│ > 1 //│ > 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/RecordSpreads.mls ================================================ :js let rcd = a: 0 b: 1 //│ rcd = {a: 0, b: 1} ...rcd //│ = {a: 0, b: 1} :e (...rcd) //│ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. //│ ║ l.13: (...rcd) //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ...rcd c: 123 //│ = {a: 0, b: 1, c: 123} //│ c = 123 let rcd = a: 0 ...(c: 2, d: 3) b: 1 //│ rcd = {a: 0, c: 2, d: 3, b: 1} ...rcd ...rcd //│ = {a: 0, c: 2, d: 3, b: 1} ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Repl.mls ================================================ :js fun res val res: Int :showRepl fun res() = 1 //│ REPL> Sending: block$res3 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: let res2;try { res2 = function res() { runtime.checkArgs("res", 0, true, arguments.length); return 1 };; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: //│ > undefined //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res3)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ > undefined //│ REPL> Parsed: //│ > undefined //│ > undefined :showRepl 2 + 2 //│ REPL> Sending: block$res4 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: try { block$res4 = 2 + 2; undefined; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: //│ > undefined //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res4)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > 4 //│ > undefined //│ REPL> Parsed: //│ > 4 //│ > undefined //│ = 4 :showRepl res //│ REPL> Sending: block$res5 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: try { block$res5 = res2; undefined; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: //│ > undefined //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res5)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > fun res //│ > undefined //│ REPL> Parsed: //│ > fun res //│ > undefined //│ = fun res :showRepl let x = 1, print(x), x //│ REPL> Sending: block$res6 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: let x;try { x = 1; runtime.checkCall(Predef.print(x)); block$res6 = x; undefined; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > 1 //│ > undefined //│ REPL> Parsed: //│ > 1 //│ > undefined //│ > 1 //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res6)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > 1 //│ > undefined //│ REPL> Parsed: //│ > 1 //│ > undefined //│ = 1 //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(x)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > 1 //│ > undefined //│ REPL> Parsed: //│ > 1 //│ > undefined //│ x = 1 ["***", "***", "***", "***", "***", "***", "***", "***", "***", "***", "***", "***", "***", "***", "***"] //│ = [ //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***", //│ "***" //│ ] ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/RuntimeUsage.mls ================================================ :js () print(()) //│ > () print(print("hello!")) //│ > hello! //│ > () // * One can import the Runtime module explicitly import "../../mlscript-compile/Runtime.mls" Runtime.printRaw(1) //│ > 1 print(Runtime.Unit) //│ > () open Runtime print(Unit) //│ > () print of safeCall(123) //│ > 123 print of safeCall(null) //│ > null print of safeCall(undefined) //│ > () print of safeCall(()) //│ > () print of checkCall(123) //│ > 123 print of checkCall(null) //│ > null :re print of checkCall(undefined) //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. print of Tuple.slice([1, 2, 3, 4], 1, -1) //│ > [2, 3, 4] print of Tuple.get([1, 2, 3, 4], 0) //│ > 1 print of Tuple.get([1, 2, 3, 4], -1) //│ > 4 :re print of Tuple.get([1, 2, 3, 4], 5) //│ ═══[RUNTIME ERROR] RangeError: Tuple.get: index out of bounds print of Str.leave("hello, world", 2) //│ > llo, world print of Str.leave("hello, world", -2) //│ > ld print of Str.get("hello, world", 2) //│ > l :re print of Str.get("hello, world", 22) //│ ═══[RUNTIME ERROR] RangeError: Str.get: index out of bounds print of Str.get("hello, world", -1) //│ > d print of Str.startsWith("hello, world", "hello") //│ > true print of Str.startsWith("hello, world", "") //│ > true print of Str.startsWith("hello, world", "farewell") //│ > false stackLimit //│ = 0 :ssjs set stackLimit = 1 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ Runtime.stackLimit = 1; block$res29 = runtime.Unit; undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— stackLimit //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls ================================================ :js // * In this test, we use `id` to wrap functions so that compile-time arity checks get bypassed. :ssjs fun f(x, y) = x + y f(2, 3) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f, x, y, inlinedVal; //│ f = function f(x1, y1) { //│ runtime.checkArgs("f", 2, true, arguments.length); //│ return x1 + y1 //│ }; //│ x = 2; //│ y = 3; //│ inlinedVal = x + y; //│ block$res1 = inlinedVal; //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 5 :ssjs let f = (x, y) => x + y f(2, 3) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f1, f2; //│ f2 = function f(x1, y1) { //│ runtime.checkArgs("f", 2, true, arguments.length); //│ return x1 + y1 //│ }; //│ f1 = f2; //│ block$res2 = runtime.safeCall(f1(2, 3)); //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 5 //│ f = fun f :re f(2) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 :re f(2, 3, 4) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 3 :ssjs :expect NaN :noSanityCheck fun f2(x, y) = x + y id(f2)(2) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f21, baseCall; //│ f21 = function f2(x1, y1) { //│ return x1 + y1 //│ }; //│ baseCall = Predef.id(f21); //│ block$res5 = runtime.safeCall(baseCall(2)); //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = NaN :ssjs :re id(f)(2) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let baseCall1; //│ baseCall1 = runtime.checkCall(Predef.id(f1)); block$res6 = runtime.safeCall(baseCall1(2)); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 :ssjs // :re // Argument numbers are no longer checked for lambdas, including extra parameter lists fun f(x)(y, z) = x + y + z id(f)(3)(4) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let f3, baseCall2, callPrefix; //│ f3 = function f(x1) { //│ runtime.checkArgs("f", 1, true, arguments.length); //│ return (y1, z) => { //│ let tmp; //│ tmp = x1 + y1; //│ return tmp + z //│ } //│ }; //│ baseCall2 = runtime.checkCall(Predef.id(f3)); //│ callPrefix = runtime.safeCall(baseCall2(3)); //│ block$res7 = runtime.safeCall(callPrefix(4)); //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = NaN // :ssjs :expect NaN :noSanityCheck let f = (x, y) => x + y in f(2) //│ = NaN // :ssjs :re let f = (x, y) => x + y f(2) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 //│ f = fun f // :ssjs :expect NaN :noSanityCheck data class Cls(x, y) with fun f(z, p) = x + y + z + p id(Cls(1, 2)).f(3) //│ = NaN // :ssjs :re data class Cls(x, y) with fun f(z, p) = x + y + z + p id(Cls(1, 2)).f(3) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 :ssjs :expect NaN // :re // Argument numbers are no longer checked for lambdas, including extra parameter lists data class Cls(x, y) with fun f(z, p)(q, s) = x + y + z + p + q + s id(Cls(1, 2)).f(3, 4)(5) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let Cls5, tmp4, tmp5, callPrefix1; //│ Cls5 = function Cls(x1, y1) { //│ return globalThis.Object.freeze(new Cls.class(x1, y1)); //│ }; //│ globalThis.Object.freeze(class Cls4 { //│ static { //│ Cls5.class = this //│ } //│ constructor(x1, y1) { //│ this.x = x1; //│ this.y = y1; //│ } //│ get f$__checkNotMethod() { runtime.deboundMethod("f", "Cls"); } //│ f(z, p) { //│ runtime.checkArgs("f", 2, true, arguments.length); //│ return (q, s) => { //│ let tmp6, tmp7, tmp8, tmp9; //│ tmp6 = this.x + this.y; //│ tmp7 = tmp6 + z; //│ tmp8 = tmp7 + p; //│ tmp9 = tmp8 + q; //│ return tmp9 + s //│ } //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Cls", ["x", "y"]]; //│ }); //│ tmp4 = runtime.checkCall(Cls5(1, 2)); //│ tmp5 = runtime.checkCall(Predef.id(tmp4)); //│ callPrefix1 = runtime.safeCall(tmp5.f(3, 4)); //│ block$res12 = runtime.safeCall(callPrefix1(5)); //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = NaN // :ssjs console.log(1) //│ > 1 // :ssjs :re globalThis.x //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' // :ssjs :re globalThis.x() //│ ═══[RUNTIME ERROR] TypeError: globalThis.x is not a function module M with data class A(x) with fun f(y) = x + y fun g(x, y) = x + y :ssjs :re if M.A(1).y is x and x == 1 then x else 0 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let scrut, x1, scrut1, tmp6, selRes1; //│ tmp6 = runtime.checkCall(M1.A(1)); //│ selRes1 = tmp6.y; //│ tmp6.y$__checkNotMethod; //│ if (selRes1 === undefined) { //│ throw globalThis.Object.freeze(new globalThis.Error("Access to required field 'y' yielded 'undefined'")) //│ } //│ scrut = selRes1; //│ x1 = scrut; //│ scrut1 = runtime.checkCall(Predef.equals(x1, 1)); //│ if (scrut1 === true) { block$res17 = x1; undefined } else { block$res17 = 0; undefined } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' // :ssjs :re M.A(1).y console.log() //│ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' // :ssjs :noSanityCheck M.A(1).y console.log() //│ > undefined :noSanityCheck :expect false M.A(2).y > 1 //│ = false :re M.A(2).y > 1 //│ ═══[RUNTIME ERROR] Error: Access to required field 'y' yielded 'undefined' // :ssjs :re M.A(1).g(0) //│ ═══[RUNTIME ERROR] TypeError: tmp11.g is not a function // :ssjs M.A(1).f(0) //│ = 1 :ssjs M.g(1, 2) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ block$res24 = runtime.checkCall(M1.g(1, 2)); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 val outFilePath = "out/test.md" //│ outFilePath = "out/test.md" :sjs fun w(txt) = fs.writeFileSync(outFilePath, txt) // () //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let w; //│ w = function w(txt) { return runtime.safeCall(globalThis.fs.writeFileSync(outFilePath, txt)) }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— w("whoops") ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ScopedBlocks.mls ================================================ :js let x = let y = 1 + 3 * 4 y + 4 //│ x = 17 fun f() = if true then let a = 4 a + 4 else let b = 5 b + 9 // TODO: improve later: `let a` can be moved to the branch body // NOTE: `tmp1` and `tmp2` should indeed be declared in the top level of the function body :sjs fun g(x, y, z) = 3 + 4 * if x then 0 y then 0 z then let a = 1 (_ + a) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let g; //│ g = function g(x1, y1, z) { //│ let a, tmp1, tmp2; //│ if (x1 === true) { //│ tmp1 = 0; //│ } else { //│ if (y1 === true) { //│ tmp1 = 0; //│ } else { //│ if (z === true) { //│ let lambda; //│ a = 1; //│ lambda = (undefined, function (_0) { //│ return _0 + a //│ }); //│ tmp1 = lambda; //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ } //│ } //│ tmp2 = 4 * tmp1; //│ return 3 + tmp2 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— module M data class A(a, b) data class B(a, b) data object Nil fun f() = if x is A(a, b) and a > b then true B(c, d) then false fun f() = if A(1, Nil) is A(1, Nil) then 3 fun f(x) = x = while x is A(a, _) then a() B(a, _) then a() else x() x fun f() = let y = x + 1 while y is A(z, a) and false and true do set y = z + 1 else y = a fun f(x, y) = while x && y do f(0, 0) + 4 class A with val x = 1 + 2 let state = 0 //│ state = 0 class Foo() with // the class definition is moved to the location of its companion fun foo() = state set state += 1 module Foo with val res = Foo().foo() :sjs type I = Int //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— class A(x) with val y = x module M with data class A(x, y) // NOTE: while bodies are not rewritten to functions by default // and are `Scoped` :sjs fun f(x) = while x do let y = x print(x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; //│ f6 = function f(x1) { //│ lbl: while (true) { //│ if (x1 === true) { Predef.print(x1); continue lbl } //│ break; //│ } //│ return runtime.Unit //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // `:rewriteWhile` turns while loop into functions :sjs :rewriteWhile fun f(x) = while x do let y = x print(x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f7; //│ f7 = function f(x1) { //│ let while1, tmp2, tmp3; //│ while1 = (undefined, function () { //│ if (x1 === true) { //│ Predef.print(x1); //│ return while1() //│ } //│ return runtime.LoopEnd; //│ }); //│ tmp2 = while1(); //│ tmp3 = tmp2 !== runtime.LoopEnd; //│ if (tmp3 === true) { return tmp2 } //│ return runtime.Unit; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— data class Bar(x) // NOTE: `tmp`s in pCtor and ctor shouldn't have name clashes with each other :sjs data class Baz(z) extends Bar(z * 1) with val k = 1 + 2 + 3 fun baz = this.bar * 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Baz1; //│ Baz1 = function Baz(z) { //│ return globalThis.Object.freeze(new Baz.class(z)); //│ }; //│ (class Baz extends Bar1.class { //│ static { //│ Baz1.class = this //│ } //│ constructor(z) { //│ let tmp2; //│ tmp2 = z * 1; //│ super(tmp2); //│ let tmp3, tmp4; //│ this.z = z; //│ tmp3 = 1 + 2; //│ tmp4 = tmp3 + 3; //│ this.k = tmp4; //│ } //│ get baz() { //│ return this.bar * 2; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Baz", ["z"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls ================================================ :js :lift :effectHandlers fun start(x, f) = while x do f(() => x) // if `z` and `g` is defined in a nested scoped block // and `g` is floated out to the top level, `z` in `g` gets unbound. // so currently nested `if`s do not get nested `Scoped` blocks :sjs fun f(x) = if x do if x do let z = 3 fun g() = z //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; //│ runtime.resetEffects(); //│ f = function f(x) { if (x === true) { return runtime.Unit } return runtime.Unit; }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— abstract class Effect with fun perform() fun f(x) = if x then if x then let z = 4 handle h = Effect with fun perform()(k) = print(x) k(x + z) print(h.perform()) else 0 fun f(x) = while x do while x do let z = 4 handle h = Effect with fun perform()(k) = print(x) k(x + z) print(h.perform()) 0 // The class body generated by the handler is in the // top level of the function, and it refers to `g`. // So there would be problems if the declartion of `g` // is nested inside the first `if`. :expect 2 fun foo(h) = h.perform() if true then if true then fun g() = 2 g() else fun g() = 3 g() handle h = Effect with fun perform()(k) = k(()) foo(h) //│ = 2 fun f(x) = while x do while x do let z = 3 fun g() = z g() fun start(x, f) = while x do let z = 2 fun g() = z g() // NOTE: `z` should be in the function top level when `:effectHandlers` to be properly captured by the floated-out `g`. :sjs fun f(x) = if x do scope.locally of ( let z = 3 fun g() = z g() ) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f4; //│ runtime.resetEffects(); //│ f4 = function f(x) { //│ let z, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ main: while (true) { //│ switch (pc) { //│ case 2: //│ runtime.resumeValue; //│ pc = 1; //│ case 1: //│ return runtime.Unit; //│ case 0: //│ if (x === true) { //│ let z1, inlinedVal; //│ z = 3; //│ z1 = z; //│ inlinedVal = z1; //│ runtime.resumeValue = inlinedVal; //│ if (runtime.curEffect === null) { //│ pc = 2; //│ continue main //│ } //│ return runtime.unwind(f4, 2, "ScopedBlocksAndHandlers.mls:103:7", null, null, 1, 1, x, 0); //│ } //│ pc = 1; //│ continue main; //│ } //│ break; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Note: degenerate case :sjs fun f(x) = if x do scope.locally of ( let z = 3 fun g() = z ) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f5; //│ runtime.resetEffects(); //│ f5 = function f(x) { if (x === true) { return runtime.Unit } return runtime.Unit; }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— class C(x) with if x do scope.locally of ( let z = 3 fun g() = z ) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Scoping.mls ================================================ :js :expect 4 fun f(y) = let x = 0 class A() with fun g() = set x += 1 set y += 1 fun h() = set x += 1 set y += 1 A().g() h() x + y f(0) //│ = 4 :expect 0 fun f() = let x = 0 class A() with let x = 2 fun g() = set x += 1 A().g() x f() //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SelfReferences.mls ================================================ :js :sjs module Foo with val self: module Foo = Foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ (class Foo { //│ static { //│ Foo1 = this //│ } //│ static { //│ this.self = Foo1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Foo.self //│ = class Foo { self: ref'1 } as ref'1 module Foo with val self: module Foo = this Foo.self //│ = class Foo { self: ref'1 } as ref'1 :e module Foo with val self = this //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type Foo. //│ ║ l.34: val self = this //│ ║ ^^^^ //│ ╙── Value must be marked as returning a 'module' in order to return a module. Foo.self //│ = class Foo { self: ref'1 } as ref'1 object Foo with val self = this Foo.self //│ = Foo { self: ref'1, class: object Foo } as ref'1 module Foo with val foo = 1 print(Foo.foo) //│ > 1 // * This used not to work, and it could be argued it should be an initialization error :sjs object Foo with val self = id(Foo) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo11; //│ (class Foo10 { //│ static { //│ new this //│ } //│ constructor() { //│ Foo11 = this; //│ let tmp; //│ tmp = Predef.id(Foo11); //│ this.self = tmp; //│ Object.defineProperty(this, "class", { //│ value: Foo10 //│ }); //│ globalThis.Object.freeze(this); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["object", "Foo"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * This used not to work, and it could be argued it should be an initialization error // * (leaking a partially-initialized module) object Foo with val self = id(Foo) val lateInit = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SetIn.mls ================================================ :js let x = 0 //│ x = 0 :sjs let x += 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x1; x1 = x + 1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ x = 1 x //│ = 1 :sjs set x = 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ x1 = 0; runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs set x += 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = x1 + 1; x1 = tmp; runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— x //│ = 1 :sjs set x += 1 in print(x) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let old, tmp1, tmp2, tmp3; //│ old = x1; //│ try { tmp2 = x1 + 1; x1 = tmp2; tmp3 = Predef.print(x1); tmp1 = tmp3; } finally { x1 = old; } //│ tmp1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 x //│ = 1 fun example() = let x = 0 let get_x() = x let x += 1 print(x) print(get_x()) example() //│ > 1 //│ > 0 fun example() = let x = 0 let get_x() = x set x += 1 print(x) print(get_x()) example() //│ > 1 //│ > 1 :sjs fun example() = let x = 0 let get_x() = x set x += 1 in print(x); print(get_x()) print(x) print(get_x()) example() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let example2; //│ example2 = function example() { //│ let x2, get_x, old1, tmp4, tmp5, tmp6, get_x1; //│ x2 = 0; //│ get_x1 = function get_x() { //│ return x2 //│ }; //│ get_x = get_x1; //│ old1 = x2; //│ try { //│ tmp4 = x2 + 1; //│ x2 = tmp4; //│ Predef.print(x2); //│ tmp5 = runtime.safeCall(get_x()); //│ Predef.print(tmp5); //│ } finally { //│ x2 = old1; //│ } //│ Predef.print(x2); //│ tmp6 = runtime.safeCall(get_x()); //│ return Predef.print(tmp6) //│ }; //│ example2() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > 1 //│ > 0 //│ > 0 :sjs fun example() = let x = 0 let get_x() = x let y = set x += 1 in print(x); x print(x) print(get_x()) y example() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let example3; //│ example3 = function example() { //│ let x2, get_x, y, old1, tmp4, tmp5, tmp6, tmp7, tmp8, get_x1; //│ x2 = 0; //│ get_x1 = function get_x() { //│ return x2 //│ }; //│ get_x = get_x1; //│ old1 = x2; //│ try { //│ tmp5 = x2 + 1; //│ x2 = tmp5; //│ tmp6 = Predef.print(x2); //│ tmp7 = (tmp6 , x2); //│ tmp4 = tmp7; //│ } finally { //│ x2 = old1; //│ } //│ y = tmp4; //│ Predef.print(x2); //│ tmp8 = runtime.safeCall(get_x()); //│ Predef.print(tmp8); //│ return y //│ }; //│ example3() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > 0 //│ > 0 //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SetStmt.mls ================================================ :js let g = [0] //│ g = [0] set g = g.concat of [1] set g |>= (_.concat of [2]) g //│ = [0, 1, 2] set g.0 += 1 set g.0 = g.0 + 1 g //│ = [2, 1, 2] set g.0 |>= Math.sign set g.2 |>= Math.pow(_, 5) //│ = fun g //│ = [1, 1, 2] ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ShortCircuitReturn.mls ================================================ :js // Regression test: && and || should use Match blocks instead of short_and/short_or function calls. // This ensures that `return` inside && works correctly. :soir fun test = true && return 42 43 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let test⁰; define test⁰ as fun test¹() { match true true => return 42 else return 43 end }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 42 test //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SimplePatMat.mls ================================================ :js data class Some[out A](value: A) :todo let Some(x) = Some(42) //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.8: let Some(x) = Some(42) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SingletonInit.mls ================================================ :js object A with val x = 1 object B with print(A, "|", B) print(x, y) val y = 2 print(A, "|", B) print(x, y) //│ > A { x: 1, B: B {} } | B {} //│ > 1 undefined //│ > A { x: 1, B: B, y: 2 } | B //│ > 1 2 print(A, "|", A.B) //│ > A { x: 1, B: B, y: 2, class: object A } | B :w module A with val x = 1 module B with print(x, y) A val y = 2 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.25: A //│ ╙── ^ //│ > 1 undefined ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SpecialJSNames.mls ================================================ :js // * JavaScript classes have built-in non-writable `name` and `length` properties, // * which can't be set directly by a normal assignment. // * Notice: :re :noModuleCheck module M with set this."name" = "oops" //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'name' of function 'class M { static { M1 = this } static { M["name"] = "oops"; } toString() { return runtime.ren...... }' // * So we use `Object.defineProperty` instead: :sjs module M with val name = "Student" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let M3; //│ (class M2 { //│ static { //│ M3 = this //│ } //│ static { //│ Object.defineProperty(this, "name", { configurable: true, enumerable: true, writable: true, value: "Student" }); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Non-static definitions should be unaffected: :sjs class C with val name = "Student" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let C1; //│ (class C { //│ static { //│ C1 = this //│ } //│ constructor() { //│ this.name = "Student"; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Spreads.mls ================================================ :js let a = [1, 2, 3] //│ a = [1, 2, 3] let b = [0, ...a] //│ b = [0, 1, 2, 3] fun foo(w, x, y, z) = [w, x, y, z] foo(...b) //│ = [0, 1, 2, 3] foo(0, ...a) //│ = [0, 1, 2, 3] :sjs foo(1, ...[2, 3], 4) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = globalThis.Object.freeze([ 2, 3 ]); foo(1, ...tmp, 4) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [1, 2, 3, 4] :re foo(...a) //│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 4 arguments but got 3 let f = (...xs) => xs //│ f = fun f f(a) //│ = [[1, 2, 3]] f(1, 2, 3) //│ = [1, 2, 3] f(...a) //│ = [1, 2, 3] fun foo(...xs) = xs foo() //│ = [] foo(1, 2, 3) //│ = [1, 2, 3] foo(0, ...a) //│ = [0, 1, 2, 3] :todo :expect [1, 2, 3] data class A(...r) let x = new A(1, 2, 3) x.r //│ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: mismatched param list lengths List() vs List(term:A/r) //│ ╟── The compilation result may be incorrect. //│ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. //│ ═══[RUNTIME ERROR] Error: Access to required field 'r' yielded 'undefined' //│ ═══[RUNTIME ERROR] Expected: '[1, 2, 3]', got: 'undefined' //│ x = A(undefined) :todo :expect [1, 2, 3] data class A(...r) with fun getR = r new A(1, 2, 3).getR //│ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: mismatched param list lengths List() vs List(term:A/r) //│ ╟── The compilation result may be incorrect. //│ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. //│ ═══[RUNTIME ERROR] ReferenceError: r is not defined //│ ═══[RUNTIME ERROR] Expected: '[1, 2, 3]', got: 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/SwitchSpecialization.mls ================================================ :js :sjs fun f(x) = if x is 0 then "a" else if x is 1 then "b" else if x is 2 then "c" else if x is 3 then "d" else "e" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; //│ f = function f(x) { //│ switch (x) { //│ case 0: //│ return "a"; //│ case 1: //│ return "b"; //│ case 2: //│ return "c"; //│ case 3: //│ return "d"; //│ } //│ return "e" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "b" f(1) //│ = "b" // Chains of if statements in the following form, which is essentially a jump table, // can be converted into a switch statement. // (This can be used to very efficiently compile resumable functions, i.e. for effect handlers and co-routines.) :sjs fun f(x) = let acc = "" if x is 0 do set acc += "a" set x = 1 if x is 1 do set acc += "b" set x = 2 if x is 2 do set acc += "c" set x = 3 if x is 3 do set acc += "d" set x = 4 if x is 4 do set acc += "e" [acc, x] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; //│ f1 = function f(x) { //│ let acc, tmp, tmp1, tmp2, tmp3, tmp4; //│ acc = ""; //│ switch (x) { //│ case 0: //│ tmp = acc + "a"; //│ acc = tmp; //│ x = 1; //│ case 1: //│ tmp1 = acc + "b"; //│ acc = tmp1; //│ x = 2; //│ case 2: //│ tmp2 = acc + "c"; //│ acc = tmp2; //│ x = 3; //│ case 3: //│ tmp3 = acc + "d"; //│ acc = tmp3; //│ x = 4; //│ case 4: //│ tmp4 = acc + "e"; //│ acc = tmp4; //│ return globalThis.Object.freeze([ acc, x ]); //│ } //│ return globalThis.Object.freeze([ acc, x ]) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect ["cde", 4] f(2) //│ = ["cde", 4] // See `SpecializedSwitch` for details on what is specialized here // Note: It would also specialize if `return` were replaced with `break` or `continue`, but it isn't // yet possible to explicitly write in the source language :sjs fun f(x) = let acc = "" if x is 0 do set acc += "a" set x = 1 if x is 1 do set acc += "b" set x = 2 if x is 2 do set acc += "c" return acc if x is 3 do set acc += "A" set x = 4 if x is 4 do set acc += "B" set x = 5 if x is 5 do set acc += "C" return acc if x is 6 then "EndA" 7 then "EndB" 8 then "EndC" else "EndD" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2; //│ f2 = function f(x) { //│ let acc, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; //│ acc = ""; //│ switch (x) { //│ case 0: //│ tmp = acc + "a"; //│ acc = tmp; //│ x = 1; //│ case 1: //│ tmp1 = acc + "b"; //│ acc = tmp1; //│ x = 2; //│ case 2: //│ tmp2 = acc + "c"; //│ acc = tmp2; //│ return acc; //│ case 3: //│ tmp3 = acc + "A"; //│ acc = tmp3; //│ x = 4; //│ case 4: //│ tmp4 = acc + "B"; //│ acc = tmp4; //│ x = 5; //│ case 5: //│ tmp5 = acc + "C"; //│ acc = tmp5; //│ return acc; //│ case 6: //│ return "EndA"; //│ case 7: //│ return "EndB"; //│ case 8: //│ return "EndC"; //│ } //│ return "EndD" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "bc" f(1) //│ = "bc" :expect "c" f(2) //│ = "c" :expect "ABC" f(3) //│ = "ABC" :expect "EndA" f(6) //│ = "EndA" :expect "EndC" f(8) //│ = "EndC" :expect "EndD" f(-1) //│ = "EndD" :sjs fun f(x) = let acc = "" if x is 0 do set acc += "M" set x = 1 if x is 1 do return acc + "1" 2 do return acc + "2" "fail" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f3; //│ f3 = function f(x) { //│ let acc, tmp1; //│ acc = ""; //│ switch (x) { //│ case 0: //│ tmp1 = acc + "M"; //│ acc = tmp1; //│ x = 1; //│ case 1: //│ return acc + "1"; //│ case 2: //│ return acc + "2"; //│ } //│ return "fail" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "M1" f(0) //│ = "M1" :expect "fail" f(4) //│ = "fail" :expect "2" f(2) //│ = "2" // Do not specialize all into one switch :sjs fun f(x) = let acc = "" if x is 0 do set acc += "M" set x = 1 if x is 1 do return acc + "1" 2 do return acc + "2" else do return acc + "3" "fail" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f4; //│ f4 = function f(x) { //│ let acc, tmp1; //│ acc = ""; //│ switch (x) { //│ case 0: //│ tmp1 = acc + "M"; //│ acc = tmp1; //│ x = 1; //│ case 1: //│ return acc + "1"; //│ case 2: //│ return acc + "2"; //│ } //│ return acc + "3" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "3" f(4) //│ = "3" // More tricky fallthrough cases // // Do not optimize :sjs fun f(x, y) = fun bad() = set x = 1000 if x is 0 do set x = 1 if y == 0 then bad() else set x = 1 if x is 1 do return "x = 1" return "x != 1" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f5; //│ f5 = function f(x, y) { //│ let scrut; //│ if (x === 0) { //│ x = 1; //│ scrut = Predef.equals(y, 0); //│ if (scrut === true) { //│ x = 1000; //│ } else { x = 1; } //│ } //│ if (x === 1) { return "x = 1" } //│ return "x != 1"; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "x != 1" f(0, 0) //│ = "x != 1" :expect "x = 1" f(0, 1) //│ = "x = 1" // Optimize :sjs fun f(x, y) = fun bad() = set x = 1000 if x is 0 do set x = 1 if y == 0 then bad() set x = 1 else set x = 1 if x is 1 do return "x = 1" return "x != 1" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; //│ f6 = function f(x, y) { //│ let scrut; //│ switch (x) { //│ case 0: //│ x = 1; //│ scrut = Predef.equals(y, 0); //│ if (scrut === true) { //│ x = 1000; //│ x = 1; //│ } else { x = 1; } //│ case 1: //│ return "x = 1"; //│ } //│ return "x != 1" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "x = 1" f(0, 0) //│ = "x = 1" :expect "x = 1" f(1, 0) //│ = "x = 1" ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/TailRecFormerFailure.mls ================================================ :js import "../../mlscript-compile/Stack.mls" open Stack // Note: tail-rec optimized :sir fun go(x) = if x is Cons(_, _) then "Z" n then go([]) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let go⁰; //│ define go⁰ as fun go¹(x) { //│ loop loopLabel: //│ let n, arg$Cons$0$, arg$Cons$1$, tmp; //│ match x //│ Cons⁰ => //│ set arg$Cons$0$ = x.head⁰; //│ set arg$Cons$1$ = x.tail⁰; //│ return "Z" //│ else //│ set n = x; //│ set tmp = []; //│ set x = tmp; //│ continue loopLabel //│ end //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Note: tail-rec optimized :sir fun go(x) = if x is Cons(_, Nil) then "Z" n then go([]) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let go²; //│ define go² as fun go³(x) { //│ loop loopLabel: //│ let n, arg$Cons$0$, arg$Cons$1$, tmp, tmp1; //│ match x //│ Cons⁰ => //│ set arg$Cons$0$ = x.head⁰; //│ set arg$Cons$1$ = x.tail⁰; //│ match arg$Cons$1$ //│ Nil⁰ => //│ return "Z" //│ else //│ set n = x; //│ set tmp = []; //│ set x = tmp; //│ continue loopLabel //│ end //│ else //│ set n = x; //│ set tmp1 = []; //│ set x = tmp1; //│ continue loopLabel //│ end //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Note: Used not to be tail-rec optimized; now fixed :sir :patMatConsequentSharingThreshold 1 // ^ The default value allowed duplicating "Z" in the above version fun go(x) = if x is Cons(_, Nil) then "Z" n then go([]) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let go⁴; //│ define go⁴ as fun go⁵(x) { //│ loop loopLabel: //│ let n, arg$Cons$0$, arg$Cons$1$, tmp; //│ block split_root$: //│ block split_1$: //│ match x //│ Cons⁰ => //│ set arg$Cons$0$ = x.head⁰; //│ set arg$Cons$1$ = x.tail⁰; //│ match arg$Cons$1$ //│ Nil⁰ => //│ return "Z" //│ else //│ set n = x; //│ break split_1$ //│ end //│ else //│ set n = x; //│ break split_1$ //│ end //│ set tmp = []; //│ set x = tmp; //│ continue loopLabel //│ return runtime⁰.Unit⁰ //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/This.mls ================================================ :js // TODO add syntax for this-taking detached methods :sjs :e fun test(x) = [this.a, x] //│ ╔══[COMPILATION ERROR] Cannot use 'this' outside of an object scope //│ ║ l.8: [this.a, x] //│ ╙── ^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test; //│ test = function test(x) { throw "This code cannot be run as its compilation yielded an error." }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun test(x) = [globalThis.a, x] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test1; test1 = function test(x) { return globalThis.Object.freeze([ globalThis.a, x ]) }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :re test(123) //│ ═══[RUNTIME ERROR] Error: Access to required field 'a' yielded 'undefined' val a = 1 //│ a = 1 :re test(123) //│ ═══[RUNTIME ERROR] Error: Access to required field 'a' yielded 'undefined' set globalThis.a = 2 test(123) //│ = [2, 123] :sjs module Test with val a = 3 fun test1(x) = test(x) fun test2(x) = [this.a, x] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Test1; //│ (class Test { //│ static { //│ Test1 = this //│ } //│ static { //│ this.a = 3; //│ } //│ static test1(x) { //│ return test1(x) //│ } //│ static test2(x) { //│ return globalThis.Object.freeze([ //│ Test.a, //│ x //│ ]) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Test"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— Test.test1(123) //│ = [2, 123] Test.test2(123) //│ = [3, 123] // * Note: a module that induces a `this`-proxy // :sjs class A(x) with module M with print(x) A(42) //│ > 42 //│ = A(_) ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls ================================================ :js object Example with val a = 1 fun f(x) = [x, a] Example.f(2) //│ = [2, 1] let oops = Example.("f") //│ oops = fun f // * JavaScript nonsense :re oops(2) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'a') oops.call(Example, 2) //│ = [2, 1] // * We could use that syntax as a shorthand: fun (.!.) call(receiver, f)(arg) = f.call(receiver, arg) // * Notice how it parses with the correct precedence: :pt :sjs Example .!. oops(2) //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ App: //│ lhs = OpApp: //│ lhs = Ident of "Example" //│ op = Ident of ".!." //│ rhss = Ls of //│ Ident of "oops" //│ rhs = Tup of Ls of //│ IntLit of 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ call(Example1, oops)(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [2, 1] Example.!. oops(2) //│ = [2, 1] Example .!.oops(2) //│ = [2, 1] id(Example) .!. oops(2) //│ = [2, 1] :e data class Example2(val a) with val a = 1 fun f(inc) = Example2(a + inc) //│ ╔══[COMPILATION ERROR] Duplicate definition of member named 'a'. //│ ╟── Defined at: //│ ║ l.58: data class Example2(val a) with //│ ║ ^ //│ ╟── Defined at: //│ ║ l.59: val a = 1 //│ ╙── ^^^^^^^^^ let oops2 = Example2(0).("f") //│ oops2 = fun f Example2(1) .!. oops2(2) .!. oops2(2) //│ = Example2(1) Example2(1).!. oops2(2).!. oops2(2) //│ = Example2(1) (new Example2(1)) .!. oops2(2) //│ = Example2(1) new Example2(1) .!. oops2(2) //│ = Example2(1) new Example2(1) .!. oops2(2) .!. oops2(2) //│ = Example2(1) // * Alternative syntax – precedence is now inappropriate fun (.>) call(receiver, f)(arg) = f.call(receiver, arg) :pt :sjs :re Example .> oops(2) //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp: //│ lhs = Ident of "Example" //│ op = Ident of ".>" //│ rhss = Ls of //│ App: //│ lhs = Ident of "oops" //│ rhs = Tup of Ls of //│ IntLit of 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp10; tmp10 = runtime.safeCall(oops(2)); call1(Example1, tmp10) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'a') // * Note: :re oops(2) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'a') // * Note: (Example .> oops)(2) //│ = [2, 1] // * Another alternative syntax – currently adopted in Predef fun (|>.) call(receiver, f)(arg) = f.call(receiver, arg) Example |>. oops(2) //│ = [2, 1] Example2(1) |>. oops2(2) |>. oops2(2) //│ = Example2(1) (Example2(1) |>. oops2(2) |>. oops2(2) ).a //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls ================================================ :js class Example with val a = 456 fun f(x) = [x, a] val ex = new Example //│ ex = Example { a: 456 } let s = ex.("f") //│ s = fun f :breakme :e s(123) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'a') :sjs ex |>. s(123) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Predef.call(ex, s)(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [123, 456] // * Modules on the other hand, do not suffer from method debinding, // * because they use static references and avoid `this` altogether. module Example with val a = 456 fun f(x) = [x, a] fun g(x) = [x, a, f(x)] let f = Example.f g = Example.g //│ f = fun f //│ g = fun g f(123) //│ = [123, 456] g(123) //│ = [123, 456, [123, 456]] :e Example |>. g(123) //│ ╔══[COMPILATION ERROR] Unexpected moduleful reference of type Example. //│ ║ l.48: Example |>. g(123) //│ ║ ^^^^^^^ //│ ╙── Module argument passed to a non-module parameter. //│ = [123, 456, [123, 456]] // * Passing `ex` as `this` has no effect ex |>. g(123) //│ = [123, 456, [123, 456]] ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/Throw.mls ================================================ :js :re throw Error("e") //│ ═══[RUNTIME ERROR] Error: e :re fun f(x, y) = throw Error("e") f(2, 2) //│ ═══[RUNTIME ERROR] Error: e :re fun f(x, y) = if x == y then x + y x == (y + 1) then throw Error("not equal") f(3, 2) //│ ═══[RUNTIME ERROR] Error: not equal :re f(2, 3) //│ ═══[RUNTIME ERROR] Error: match error :re :sjs fun f(x) = let y = throw Error("e") return y f(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f2; //│ f2 = function f(x) { //│ throw runtime.safeCall(globalThis.Error("e")) //│ }; //│ throw runtime.safeCall(globalThis.Error("e")) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: e :re :sjs fun f(x) = throw (if x then Error("x") else Error("y")) f(false) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f3; //│ f3 = function f(x) { //│ if (x === true) { //│ throw runtime.safeCall(globalThis.Error("x")) //│ } //│ throw runtime.safeCall(globalThis.Error("y")); //│ }; //│ f3(false) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: y :re :sjs if false then throw 0 throw 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; //│ scrut = false; //│ if (scrut === true) { throw 0 } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error import "../../mlscript-compile/Runtime.mls" fun foo(e) = Runtime.try_catch of () => throw e case Str as o then "caught " + o else print("caught something else") throw e // rethrow the original error foo("hello") //│ = "caught hello" :re foo(42) //│ > caught something else //│ ═══[RUNTIME ERROR] 42 :re foo(Error("hi")) //│ > caught something else //│ ═══[RUNTIME ERROR] Error: hi ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls ================================================ :js :traceJS :noSanityCheck :sjs fun fib(a) = if a <= 1 then a else fib(a - 1) + fib(a - 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let fib; //│ fib = function fib(a) { //│ let scrut, tmp, tmp1, tmp2, tmp3; //│ scrut = a <= 1; //│ if (scrut === true) { //│ return a //│ } //│ tmp = a - 1; //│ tmp1 = fib(tmp); //│ tmp2 = a - 2; //│ tmp3 = fib(tmp2); //│ return tmp1 + tmp3; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun f(x) = g(x) fun g(x) = h(x) fun h(x) = fib(x) f(5) //│ > CALL f(5) //│ > | CALL g(5) //│ > | | CALL h(5) //│ > | | | CALL fib(5) //│ > | | | | CALL fib(4) //│ > | | | | | CALL fib(3) //│ > | | | | | | CALL fib(2) //│ > | | | | | | | CALL fib(1) //│ > | | | | | | | => 1 //│ > | | | | | | | CALL fib(0) //│ > | | | | | | | => 0 //│ > | | | | | | => 1 //│ > | | | | | | CALL fib(1) //│ > | | | | | | => 1 //│ > | | | | | => 2 //│ > | | | | | CALL fib(2) //│ > | | | | | | CALL fib(1) //│ > | | | | | | => 1 //│ > | | | | | | CALL fib(0) //│ > | | | | | | => 0 //│ > | | | | | => 1 //│ > | | | | => 3 //│ > | | | | CALL fib(3) //│ > | | | | | CALL fib(2) //│ > | | | | | | CALL fib(1) //│ > | | | | | | => 1 //│ > | | | | | | CALL fib(0) //│ > | | | | | | => 0 //│ > | | | | | => 1 //│ > | | | | | CALL fib(1) //│ > | | | | | => 1 //│ > | | | | => 2 //│ > | | | => 5 //│ > | | => 5 //│ > | => 5 //│ > => 5 //│ = 5 abstract class E: S | N data class S(x) with fun toString() = "S(" + x + "): " + toInt(succ(x)) class N() with fun toString() = "N: 0" fun succ(x) = S(x) fun toInt(x) = if x is N then 0 S(n) then 1 + toInt(n) (S(S(N()))).toString() //│ > CALL toString() //│ > | CALL toString() //│ > | | CALL toString() //│ > | | => 'N: 0' //│ > | | CALL succ(N {}) //│ > | | => S { x: N {} } //│ > | | CALL toInt(S { x: N {} }) //│ > | | | CALL toInt(N {}) //│ > | | | => 0 //│ > | | => 1 //│ > | => 'S(N: 0): 1' //│ > | CALL succ(S { x: N {} }) //│ > | => S { x: S { x: N {} } } //│ > | CALL toInt(S { x: S { x: N {} } }) //│ > | | CALL toInt(S { x: N {} }) //│ > | | | CALL toInt(N {}) //│ > | | | => 0 //│ > | | => 1 //│ > | => 2 //│ > => 'S(S(N: 0): 1): 2' //│ = "S(S(N: 0): 1): 2" data class Cls(a, b, c, d, e, f) fun id(y) = if y == 0 then Cls("aaaa", "bbbb", "cccc", "dddd", "eeee", "ffff") else id(y - 1) 0 id(2) //│ > CALL id(2) //│ > | CALL id(1) //│ > | | CALL id(0) //│ > | | => Cls { //│ > a: 'aaaa', //│ > b: 'bbbb', //│ > c: 'cccc', //│ > d: 'dddd', //│ > e: 'eeee', //│ > f: 'ffff' //│ > } //│ > | => 0 //│ > => 0 //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls ================================================ :js fun plus(x, y) = x + y :traceJS :e fun g(z) = if z < -1 then g(z + 1) z == -1 then 42 z == 0 then plus(0) z > 0 then g(z - 1) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.11: z == 0 then plus(0) //│ ╙── ^^^ fun f(x, y)(z) = x + y + g(z) :traceJS fun h(x) = f(1,2)(x) :traceJS :re h(3) //│ > CALL h(3) //│ > | CALL g(3) //│ > | | CALL g(2) //│ > | | | CALL g(1) //│ > | | | | CALL g(0) //│ ═══[RUNTIME ERROR] Error: Function 'plus' expected 2 arguments but got 1 :traceJS h(-2) //│ > CALL h(-2) //│ > | CALL g(-2) //│ > | | CALL g(-1) //│ > | | => 42 //│ > | => 42 //│ > => 45 //│ = 45 :traceJS f(1,1)(-2) //│ > CALL g(-2) //│ > | CALL g(-1) //│ > | => 42 //│ > => 42 //│ = 44 :traceJS :re f(1,2)(3) //│ > CALL g(3) //│ > | CALL g(2) //│ > | | CALL g(1) //│ > | | | CALL g(0) //│ ═══[RUNTIME ERROR] Error: Function 'plus' expected 2 arguments but got 1 :showRepl :traceJS f(1,2)(-3) //│ REPL> Sending: runtime.TraceLogger.enabled = true; runtime.TraceLogger.resetIndent(0) //│ REPL> Collected: //│ > Unit {} //│ REPL> Sending: block$res9 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: let tmp2;try { tmp2 = - 3; block$res9 = runtime.checkCall(f(1, 2)(tmp2)); undefined; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > CALL g(-3) //│ > | CALL g(-2) //│ > | | CALL g(-1) //│ > | | => 42 //│ > | => 42 //│ > => 42 //│ > undefined //│ REPL> Parsed: //│ > CALL g(-3) //│ > | CALL g(-2) //│ > | | CALL g(-1) //│ > | | => 42 //│ > | => 42 //│ > => 42 //│ > undefined //│ > CALL g(-3) //│ > | CALL g(-2) //│ > | | CALL g(-1) //│ > | | => 42 //│ > | => 42 //│ > => 42 //│ REPL> Sending: runtime.TraceLogger.enabled = false //│ REPL> Collected: //│ > false //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res9)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > 45 //│ > undefined //│ REPL> Parsed: //│ > 45 //│ > undefined //│ = 45 :showRepl f(1,2)(-4) //│ REPL> Sending: block$res10 = undefined //│ REPL> Collected: //│ > undefined //│ REPL> Sending: let tmp3;try { tmp3 = - 4; block$res10 = runtime.checkCall(f(1, 2)(tmp3)); undefined; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: //│ > undefined //│ REPL> Sending: try { runtime.checkCall(runtime.printRaw(block$res10)); undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > 45 //│ > undefined //│ REPL> Parsed: //│ > 45 //│ > undefined //│ = 45 ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/UnitValue.mls ================================================ :js fun foo = return () foo print of foo //│ > () fun foo = return foo print of foo //│ > () :breakme // warn about useless binding :w fun foo() = let x = 1 print of foo() //│ > () :breakme // warn about useless binding :w fun foo() = val x = 1 print of foo() //│ > () :sjs print of globalThis.console.log("Hello, world!") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp4; tmp4 = runtime.safeCall(globalThis.console.log("Hello, world!")); Predef.print(tmp4) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hello, world! //│ > () data class Box(value) fun foo() = if false do () print of Box(foo()).value //│ > () :w fun foo() = while false do 0 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.58: while false do 0 //│ ╙── ^ print of Box(foo()).value //│ > () :sjs fun foo() = {} //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo6; foo6 = function foo() { return runtime.Unit }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— print of Box(foo()).value //│ > () :sjs fun foo(x) = set x = 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo7; foo7 = function foo(x) { x = 1; return runtime.Unit }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— print of Box(foo(1)).value //│ > () :e fun f = let x = 1 //│ ╔══[COMPILATION ERROR] Expected a body for let bindings in expression position //│ ║ l.89: fun f = let x = 1 //│ ╙── ^^^^^ print of f //│ > () ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/While.mls ================================================ :js while false do ??? :ssjs while false then () //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ lbl1: while (true) { //│ let scrut; //│ scrut = false; //│ if (scrut === true) { continue lbl1 } //│ break; //│ } //│ block$res2 = runtime.Unit; //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— () => while true then () //│ = fun :w () => while true then 0 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.24: while true then 0 //│ ╙── ^ //│ = fun let x = true //│ x = true // TODO: forbid uses of keywords `then` as well as `else` in `while` // :re // Note: this used to throw a match error while x then set x = false let x = true //│ x = true while x do set x = false let x = true let y = false //│ x = true //│ y = false :sjs fun f = while x then print("Hello World") set x = false else return 42 // TODO: support `break` f //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; //│ f = function f() { //│ lbl4: while (true) { //│ if (x2 === true) { Predef.print("Hello World"); x2 = false; continue lbl4 } //│ return 42; //│ } //│ }; //│ f() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hello World //│ = 42 fun f = while (print("Hello World"), false) then 0(0) else return 1 f //│ > Hello World //│ = 1 // * Returning inside while loop :expect 42 fun f() = while true do return 42 f() //│ = 42 let i = 0 //│ i = 0 fun f = while i < 3 then set i = i + 1 else return i f //│ = 3 let i = 0 fun f = while print("checking"); i < 3 then print("not yet"); set i = i + 1 else return print("ok") f //│ > checking //│ > not yet //│ > checking //│ > not yet //│ > checking //│ > not yet //│ > checking //│ > ok //│ i = 3 // :sjs // * Note: infinite loop! The `let` should be outside the loop () => while let i = 0 i < 10 do set i += 1 //│ = fun fun f = let i = 0 in while i < 10 then set i += 1 else return i f //│ = 10 let i = 0 in while let _ = print(i) i < 3 do set i += 1 //│ > 0 //│ > 1 //│ > 2 //│ > 3 let i = 0 in while do print(i) i < 3 do set i += 1 //│ > 0 //│ > 1 //│ > 2 //│ > 3 // * Note that the semantics of UCS-while is quite subtle. // * Currently, we only treat the *top-level* `else` as terminating the loop; // * indeed, other `else` branches are currently indistinguishable from `then` branches // * in normalized UCS form. // * Consider: :ucs normalized if x and y then 0 else 1 // this one will not terminate the loop, if we're in a `while` else 42 //│ Normalized: //│ > ‹if|while› //│ > x is true and //│ > y is true then 0 //│ > else 1 //│ > else 42 //│ = 42 :ucs normalized if x and y then 0 _ then 1 else 42 //│ Normalized: //│ > ‹if|while› //│ > x is true and //│ > y is true then 0 //│ > else 1 //│ > else 42 //│ = 42 // TODO use // import "../../mlscript-compile/Stack.mls" data class Cons(hd, tl) :sjs fun f(ls) = while ls is Cons(h, tl) then set ls = tl print(h) // else print("Done!") print("Done!") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; //│ f6 = function f(ls) { //│ lbl6: while (true) { //│ let tl, h, arg$Cons$0$, arg$Cons$1$; //│ if (ls instanceof Cons1.class) { //│ arg$Cons$0$ = ls.hd; //│ arg$Cons$1$ = ls.tl; //│ tl = arg$Cons$1$; //│ h = arg$Cons$0$; //│ ls = tl; //│ Predef.print(h); //│ continue lbl6 //│ } //│ break; //│ } //│ return Predef.print("Done!") //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— f(0) //│ > Done! f(Cons(1, 0)) //│ > 1 //│ > Done! f(Cons(1, Cons(2, Cons(3, 0)))) //│ > 1 //│ > 2 //│ > 3 //│ > Done! fun f(ls) = while do print(ls) ls is Cons(h, tl) do set ls = tl f(Cons(1, Cons(2, Cons(3, 0)))) //│ > Cons(1, Cons(2, Cons(3, 0))) //│ > Cons(2, Cons(3, 0)) //│ > Cons(3, 0) //│ > 0 while false do class A new A let x = 1 //│ x = 1 :sjs while x is {} do() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ lbl7: while (true) { if (x3 instanceof Object) { continue lbl7 } break; } runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Mix of local mutable variable, while loop and lambdas :expect 2 fun f() = let cnt = 2 savedLambda = null while cnt > 0 do let local = cnt if cnt == 2 do // This lambda should always return 2, // since local is 2 and not reassigned in the while loop savedLambda = () => local set cnt -= 1 // Calls the saved lambda savedLambda() f() //│ = 2 class Lazy[out A](f: () -> A) with mut val cached: A | () = () fun get = if cached === () do set cached = f() cached :expect [1, 2, 3] :lift :rewriteWhile let arr = [1, 2, 3] let output = mut [] let i = 0 while i < arr.length do let elem = arr.[i] output.push(new Lazy(() => elem)) set i += 1 [output.[0].get, output.[1].get, output.[2].get] //│ = [1, 2, 3] //│ arr = [1, 2, 3] //│ i = 3 //│ output = [Lazy(_), Lazy(_), Lazy(_)] :expect [1, 2, 3] :lift let arr = [1, 2, 3] let output = mut [] let i = 0 while i < arr.length do let elem = arr.[i] output.push(new Lazy(() => elem)) set i += 1 [output.[0].get, output.[1].get, output.[2].get] //│ = [1, 2, 3] //│ arr = [1, 2, 3] //│ i = 3 //│ output = [Lazy(_), Lazy(_), Lazy(_)] // ——— FIXME: ——— :fixme while print("Hello World"); false then 0(0) else 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.317: then 0(0) //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized term split (false literal) //│ ║ l.316: while print("Hello World"); false //│ ╙── ^^^^^ //│ > Hello World :fixme while { print("Hello World"), false } then 0(0) else 1 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.328: while { print("Hello World"), false } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.329: then 0(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. //│ ║ l.330: else 1 //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme while print("Hello World") false then 0(0) else 1 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.343: print("Hello World") //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.344: false //│ ║ ^^^^^^^^^ //│ ║ l.345: then 0(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. //│ ║ l.346: else 1 //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // * https://github.com/hkust-taco/mlscript/issues/401 // :sjs :rewriteWhile fun f = while false do () print(f) //│ > () ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/WhileDefaults.mls ================================================ :js // * Note that the following :sjs () => while false do print(1) _ do print(2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; //│ lambda = (undefined, function () { //│ lbl: while (true) { //│ let scrut; //│ scrut = false; //│ if (scrut === true) { //│ Predef.print(1); //│ continue lbl //│ } //│ Predef.print(2); //│ continue lbl; //│ } //│ }); //│ lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // * ...is currently interpreted the same as the loop below () => while false do print(1) else print(2) //│ = fun // * ...and so is this one () => while false do print(1) true do print(2) //│ = fun // * This semantics for `else` branches is confusing, // * as the semantics of `while ... else ...` is notably not aligned with Python's own `while`/`else` form // * Some legitimate uses of `else` branches in `while`: // * This is a valid, but silly use (could have inserted the `else` part _after_ the `while`!) fun f = let x = 1 in while x is 1 do print("`x` was `1`") set x = 0 else print("stopping at the `else`") return x f //│ > `x` was `1` //│ > stopping at the `else` //│ = 0 // * Less silly: we still want to see what's in scope // * (here, the result of the `expensive` computation) // * upon stopping the loop fun f = let x = 5 in while let expensive = x - 1 - 1 - 1 expensive < 0 do set x += 2 else print("the result of the expensive upon stopping the loop computation was", expensive) return f //│ > the result of the expensive upon stopping the loop computation was 2 // * An `else` not at the top level of the `while` let x = 3 in while x > 0 and false do ??? else set x -= 1 print("inner `else` split reached") //│ > inner `else` split reached //│ > inner `else` split reached //│ > inner `else` split reached // * (A weird variation of the above:) let x = 3 in while x > 0 and else set x -= 1 print("inner `else` split reached") //│ > inner `else` split reached //│ > inner `else` split reached //│ > inner `else` split reached // * Similar: we might want to use the extracted value of `x` upon stopping the loop fun f(xs) = while xs is [y, ..ys] and y > 0 do print("still positive:", y) set xs = ys else return print("no longer positive (" + y + "); stopping...") print("all were positive!") f of [3, 1, 2, -1, 0, -2] //│ > still positive: 3 //│ > still positive: 1 //│ > still positive: 2 //│ > no longer positive (-1); stopping... f of [3, 1, 2, 5] //│ > still positive: 3 //│ > still positive: 1 //│ > still positive: 2 //│ > still positive: 5 //│ > all were positive! ================================================ FILE: hkmc2/shared/src/test/mlscript/codegen/i382.mls ================================================ :js // https://github.com/hkust-taco/mlscript/issues/382 // "Full example to demonstrate that changing else branch affect the loop behaviour" // * These are infinite loops, now... () => let x = 1 while x === 1 do set x = 2 2 do set x = 3 print("valid") else set x = 2 print("not inf loop") //│ = fun () => let x = 1 while x === 1 do set x = 2 2 do set x = 3 print("valid") else set x = 3 print("not inf loop") //│ = fun // * Variation: let x = 1 while x === 1 do set x += 1 2 do set x = 3 print("valid") 3 do set x += 1 //│ > valid //│ x = 4 // * Same, rewritten as a tail-recursive function: :rewriteWhile let x = 1 while x === 1 do set x = 2 2 do set x = 3 print("valid") 3 do set x += 1 //│ > valid //│ x = 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/CascadingTypeClasses.mls ================================================ :js class Foo[A] class Bar[B] class Baz fun foo[A](using Foo[A], A) = 42 using Foo[Baz] = new Foo // FIXME: error message refers to `A` :e :re foo[Baz] //│ ╔══[COMPILATION ERROR] Cannot query instance of type Baz (type parameter A) for call: //│ ║ l.18: foo[Baz] //│ ║ ^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.9: fun foo[A](using Foo[A], A) = 42 //│ ║ ^ //│ ╙── Missing instance: Expected: Baz (type parameter A); Available: Foo[Baz] //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.30: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.9: fun foo[A](using Foo[A], A) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. using Baz = new Baz :todo foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.44: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.9: fun foo[A](using Foo[A], A) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun foo[A](using Foo[A])(using A) = 42 :todo foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.58: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.55: fun foo[A](using Foo[A])(using A) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun foo[A](using A)(using Foo[A]) = 42 :todo foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.72: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. using Foo[Bar[Baz]] = new Foo :e :re foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.87: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. using Bar[Baz] = new Bar :e :re foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.101: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. using Bar[Int] = new Bar :e :re foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.115: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. using Bar[Baz] = new Bar :todo foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter A) for call: //│ ║ l.128: foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/ClassCtxParams.mls ================================================ :js using Int = 42 class Foo(using val n: Int) :expect Foo(42) Foo //│ = Foo(42) :expect 42 Foo.n //│ = 42 class Foo(using n: Int) with fun foo = n :expect Foo(_) Foo //│ = Foo(_) :expect 42 Foo.foo //│ = 42 class Bar(using val s: Str) :e Bar //│ ╔══[COMPILATION ERROR] Cannot query instance of type Str for call: //│ ║ l.33: Bar //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.30: class Bar(using val s: Str) //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Str; Available: Int //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. class Bar(using s: Str) :e Bar //│ ╔══[COMPILATION ERROR] Cannot query instance of type Str for call: //│ ║ l.46: Bar //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.43: class Bar(using s: Str) //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Str; Available: Int //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun summon(using i: Int) = i class Foo(using Int) with fun foo = use[Int] fun bar = summon using Int = 99 :expect Foo(_) Foo //│ = Foo(_) :expect 99 Foo.foo //│ = 99 :expect 99 Foo.bar //│ = 99 class Foo(using Int)(using Str) with fun f = use[Int] fun g = use[Str] :e Foo //│ ╔══[COMPILATION ERROR] Cannot query instance of type Str for call: //│ ║ l.84: Foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.79: class Foo(using Int)(using Str) with //│ ║ ^^^ //│ ╙── Missing instance: Expected: Str; Available: Int //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // FIXME: the compiler thinks T is a member of Foo! // FIXME: the compiler fails to compile the field projection class T :sjs class Foo(using T) with print(T) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo9; //│ Foo9 = function Foo(tmp4) { //│ return globalThis.Object.freeze(new Foo.class(tmp4)); //│ }; //│ (class Foo8 { //│ static { //│ Foo9.class = this //│ } //│ constructor(tmp4) { //│ Predef.print(T1); //│ } //│ #tmp; //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", [null]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :todo :sjs x => x.Foo#S //│ ╔══[COMPILATION ERROR] Class 'Foo' does not contain member 'S'. //│ ║ l.123: x => x.Foo#S //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Cannot query instance of type T for call: //│ ║ l.123: x => x.Foo#S //│ ║ ^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.101: class Foo(using T) with //│ ║ ^ //│ ╙── Missing instance: Expected: T; Available: Int //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; lambda = (undefined, function (x) { return x.S }); lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls ================================================ :js using Int = 42 fun foo()(using Int) = [use[Int]] fun bar(x)(using Int) = [x, use[Int]] fun baz(x)(using Int)(y)(using Num) = [x, use[Int], y, use[Num]] :e baz //│ ╔══[COMPILATION ERROR] Cannot query instance of type Num for call: //│ ║ l.13: baz //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.9: fun baz(x)(using Int)(y)(using Num) = [x, use[Int], y, use[Num]] //│ ║ ^^^ //│ ╙── Missing instance: Expected: Num; Available: Int //│ = fun using Num = 0.42 // foo // should be expanded to // () => foo() // and then resolved to // () => foo()( 42) :sir let f1 = foo //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f1⁰, f1¹; define f1¹ as fun f1²() { return foo⁰()(instance$Ident(Int)⁰) }; set f1⁰ = f1²; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f1 = fun f1 :expect [42] f1() //│ = [42] // bar // should be expanded to // (x) => bar(x) // and then resolved to // (x) => bar(x)( 42) :sir let f2 = bar //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f2⁰, f2¹; define f2¹ as fun f2²(x) { return bar⁰(x)(instance$Ident(Int)⁰) }; set f2⁰ = f2²; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f2 = fun f2 :expect [0.42, 42] f2(0.42) //│ = [0.42, 42] // baz // should be expanded to // (x) => baz(x) // and then resolved to // (x) => baz(x)( 42) :sir let f3 = baz //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f3⁰, f3¹; //│ define f3¹ as fun f3²(x) { //│ let lambda; //│ define lambda as fun lambda⁰(y) { //│ return baz⁰(x)(instance$Ident(Int)⁰)(y)(instance$Ident(Num)⁰) //│ }; //│ return lambda⁰ //│ }; //│ set f3⁰ = f3²; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f3 = fun f3 :expect [43, 42, 44, 0.42] f3(43)(44) //│ = [43, 42, 44, 0.42] // This should not expand :sir foo() //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ foo⁰()(instance$Ident(Int)⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [42] // This should not expand :sir bar(0.42) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ bar⁰(0.42)(instance$Ident(Int)⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [0.42, 42] // :fixme // TODO: fix IR rebinding issue (each symbol should be bound at most once) // :checkIR :sir module Test with fun test(j)(using Int) = 0 fun main(using Int) = test //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let Test⁰; //│ define Test⁰ as class Test¹ //│ module Test² { //│ method test⁰ = fun test¹(j)(tmp) { //│ return 0 //│ } //│ method main⁰ = fun main¹(tmp) { //│ let lambda; //│ define lambda as fun lambda¹(j) { return Test².this.test¹(j)(tmp) }; //│ return lambda¹ //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/ExplicitlySpec.mls ================================================ :js fun f(using i: Int): Int = i :expect 42 f(using 42) //│ = 42 fun f(using i: Int, j: Num): Num = i + j :e :re f(using 42) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.14: f(using 42) //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 :expect 45.14 f(using 42, 3.14) //│ = 45.14 fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k :e :re f(using 32) //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.29: f(using 32) //│ ║ ^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.25: fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.29: f(using 32) //│ ║ ^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.25: fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re f(using 32)(using 8) //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.48: f(using 32)(using 8) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.25: fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 42 f(using 32)(using 8)(using 2) //│ = 42 fun f(using i: Int)(j: Int)(using k: Int)(l: Num): Int = i + j + k + l :e :expect fun f(using 32) //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.67: f(using 32) //│ ║ ^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.63: fun f(using i: Int)(j: Int)(using k: Int)(l: Num): Int = i + j + k + l //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ = fun :e :re f(using 32)(8) //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.79: f(using 32)(8) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.63: fun f(using i: Int)(j: Int)(using k: Int)(l: Num): Int = i + j + k + l //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect fun f(using 32)(8)(using 2) //│ = fun :expect 45.14 f(using 32)(8)(using 2)(3.14) //│ = 45.14 ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/ForwardTypeClassUses.mls ================================================ :js using Str = "ok" fun bar = M.foo() module M with fun foo()(using s: Str) = s :expect "ok" bar //│ = "ok" fun bar = M.getN.foo() module M with fun getN: module N = N module N with fun foo()(using Str) = 1 :expect 1 bar //│ = 1 :e :re fun test = M.mtd() module M with fun mtd()(using n: Int) = n module Example with fun foo(): module M = M test //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.27: fun test = M.mtd() //│ ║ ^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.29: fun mtd()(using n: Int) = n //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: Str //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re fun test = Example.foo().mtd() module M with fun mtd()(using n: Int) = n module Example with fun foo(): module M = M test //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.45: fun test = Example.foo().mtd() //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.47: fun mtd()(using n: Int) = n //│ ║ ^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: Str //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. using Int = 42 fun test = Example.foo().mtd() module M with fun mtd()(using n: Int) = n module Example with fun foo(): module M = M :expect 42 test //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls ================================================ fun f: Any -> Any :e g(5) //│ ╔══[COMPILATION ERROR] Name not found: g //│ ║ l.6: g(5) //│ ╙── ^ :js :ge :re f(1) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' //│ ║ l.14: f(1) //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.3: fun f: Any -> Any //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] ReferenceError: f is not defined // no errors if we don't generate code f(1) fun g(x) = f(x) :js :ge :re g(0) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'g' //│ ║ l.31: g(0) //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.26: fun g(x) = f(x) //│ ╙── ^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] ReferenceError: g is not defined :js :ge fun g(x) = f(x) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' //│ ║ l.42: fun g(x) = f(x) //│ ║ ^ //│ ╟── which references the symbol introduced here //│ ║ l.3: fun f: Any -> Any //│ ╙── ^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls ================================================ :js :fixme let x: Int //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.6: let x: Int //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. let x //│ x = undefined x val x // `x` is declared, just never initilized, // so there shouldn't be a compilation error // of "`x` not found in scope" x val x: Int // `x` is declared, just never initilized, // so there shouldn't be a compilation error // of "`x` not found in scope" x fun p: Int :re p //│ ═══[RUNTIME ERROR] TypeError: p is not a function // :ctx fun (++) test: (Int, Int) -> Int :re test(1, 2) //│ ═══[RUNTIME ERROR] TypeError: test is not a function :sjs :re 1 ++ 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp1; tmp1 = test(); runtime.safeCall(tmp1(1, 1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: test is not a function declare class C :re new C //│ ═══[RUNTIME ERROR] TypeError: globalThis.C is not a constructor ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/Summon.mls ================================================ :js using Int = 42 using Str = "42" :expect 42 use[Int] //│ = 42 :expect "42" use[Str] //│ = "42" :e :re use[Num] //│ ╔══[COMPILATION ERROR] Cannot query instance of type Num (type parameter T) for call: //│ ║ l.17: use[Num] //│ ║ ^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.126: fun use[T](using instance: T) = instance //│ ║ ^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Num (type parameter T); Available: Int, Str //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 42 let x = use[Int] x //│ = 42 //│ x = 42 :todo using Int -> Int = id use[Int -> Int](123) //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Cannot add instance with a not-yet implemented type. :expect -42 fun f(using Int) = use[Int] using Int = -42 f //│ = -42 :expect "42" fun f(using Int) = use[Str] using Str = "-42" f //│ = "42" :e fun f(using Int) = use[Num] //│ ╔══[COMPILATION ERROR] Cannot query instance of type Num (type parameter T) for call: //│ ║ l.53: fun f(using Int) = use[Num] //│ ║ ^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.126: fun use[T](using instance: T) = instance //│ ║ ^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Num (type parameter T); Available: Int, Str data class Some[T](value: T) :expect 42 fun f[T](using Some[T]) = use[Some[T]].value using Some[Int] = Some(42) f //│ = 42 :e :re use{[Some[Int]].value} //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (selection). //│ ║ l.74: use{[Some[Int]].value} //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter T) for call: //│ ║ l.74: use{[Some[Int]].value} //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.126: fun use[T](using instance: T) = instance //│ ║ ^^^^^^^^^^^ //│ ╙── Illegal query for an unspecified type variable T. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :re use([Some[Int]].value) //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter T) for call: //│ ║ l.89: use([Some[Int]].value) //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.126: fun use[T](using instance: T) = instance //│ ║ ^^^^^^^^^^^ //│ ╙── Illegal query for an unspecified type variable T. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :expect 42 fun f[T](using T) = use[T] using Int = 42 f[Int] //│ = 42 :e :re f //│ ╔══[COMPILATION ERROR] Cannot query instance of type ‹unspecified› (type parameter T) for call: //│ ║ l.108: f //│ ║ ^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.101: fun f[T](using T) = use[T] //│ ║ ^ //│ ╙── Illegal query for an unspecified type variable T. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/SymbolResolution.mls ================================================ module M with val foo: module M = M fun bar: module M = M fun baz(): module M = M fun that: module N = N module N with fun that: module M = M module R with fun foo()(): module N = N fun bar[T](using Int)(using Num)(using Str): module M = M fun foo[T](using Int)(using Num)(using Str): module M = M using Int = 42 using Num = 4.2 using Str = "42" :global :rt M.foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:M⁰/foo⁰,typ=module:M⁰}: //│ t = Sel{sym=member:foo¹}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "foo" //│ sym = term:M⁰/foo⁰ M.bar //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:M⁰/bar⁰,typ=module:M⁰}: //│ t = Sel{sym=member:bar¹}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "bar" //│ sym = term:M⁰/bar⁰ M.baz //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:M⁰/baz⁰}: //│ t = Sel{sym=member:baz¹}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "baz" //│ sym = term:M⁰/baz⁰ M.baz() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:M⁰}: //│ lhs = Resolved{sym=term:M⁰/baz⁰}: //│ t = Sel{sym=member:baz¹}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "baz" //│ sym = term:M⁰/baz⁰ //│ rhs = Tup of Nil M.foo.bar.baz() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:M⁰}: //│ lhs = Resolved{sym=term:M⁰/baz⁰}: //│ t = Sel{sym=member:baz¹}: //│ prefix = Resolved{sym=term:M⁰/bar⁰,typ=module:M⁰}: //│ t = Sel{sym=member:bar¹}: //│ prefix = Resolved{sym=term:M⁰/foo⁰,typ=module:M⁰}: //│ t = Sel{sym=member:foo¹}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "foo" //│ sym = term:M⁰/foo⁰ //│ nme = Ident of "bar" //│ sym = term:M⁰/bar⁰ //│ nme = Ident of "baz" //│ sym = term:M⁰/baz⁰ //│ rhs = Tup of Nil M.that.that //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:N⁰/that⁰,typ=module:M⁰}: //│ t = Sel{sym=member:that¹}: //│ prefix = Resolved{sym=term:M⁰/that²,typ=module:N⁰}: //│ t = Sel{sym=member:that³}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "that" //│ sym = term:M⁰/that² //│ nme = Ident of "that" //│ sym = term:N⁰/that⁰ M.that.that.that.that.that.that //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:N⁰/that⁰,typ=module:M⁰}: //│ t = Sel{sym=member:that¹}: //│ prefix = Resolved{sym=term:M⁰/that²,typ=module:N⁰}: //│ t = Sel{sym=member:that³}: //│ prefix = Resolved{sym=term:N⁰/that⁰,typ=module:M⁰}: //│ t = Sel{sym=member:that¹}: //│ prefix = Resolved{sym=term:M⁰/that²,typ=module:N⁰}: //│ t = Sel{sym=member:that³}: //│ prefix = Resolved{sym=term:N⁰/that⁰,typ=module:M⁰}: //│ t = Sel{sym=member:that¹}: //│ prefix = Resolved{sym=term:M⁰/that²,typ=module:N⁰}: //│ t = Sel{sym=member:that³}: //│ prefix = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ nme = Ident of "that" //│ sym = term:M⁰/that² //│ nme = Ident of "that" //│ sym = term:N⁰/that⁰ //│ nme = Ident of "that" //│ sym = term:M⁰/that² //│ nme = Ident of "that" //│ sym = term:N⁰/that⁰ //│ nme = Ident of "that" //│ sym = term:M⁰/that² //│ nme = Ident of "that" //│ sym = term:N⁰/that⁰ R.foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:R⁰/foo²}: //│ t = Sel{sym=member:foo³}: //│ prefix = Resolved{sym=module:R⁰,typ=module:R⁰}: //│ t = Ref{sym=member:R¹} of member:R¹ //│ sym = module:R⁰ //│ nme = Ident of "foo" //│ sym = term:R⁰/foo² R.foo() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App: //│ lhs = Resolved{sym=term:R⁰/foo²}: //│ t = Sel{sym=member:foo³}: //│ prefix = Resolved{sym=module:R⁰,typ=module:R⁰}: //│ t = Ref{sym=member:R¹} of member:R¹ //│ sym = module:R⁰ //│ nme = Ident of "foo" //│ sym = term:R⁰/foo² //│ rhs = Tup of Nil R.foo()() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:N⁰}: //│ lhs = App: //│ lhs = Resolved{sym=term:R⁰/foo²}: //│ t = Sel{sym=member:foo³}: //│ prefix = Resolved{sym=module:R⁰,typ=module:R⁰}: //│ t = Ref{sym=member:R¹} of member:R¹ //│ sym = module:R⁰ //│ nme = Ident of "foo" //│ sym = term:R⁰/foo² //│ rhs = Tup of Nil //│ rhs = Tup of Nil R.foo()()() //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App: //│ lhs = App{typ=module:N⁰}: //│ lhs = App: //│ lhs = Resolved{sym=term:R⁰/foo²}: //│ t = Sel{sym=member:foo³}: //│ prefix = Resolved{sym=module:R⁰,typ=module:R⁰}: //│ t = Ref{sym=member:R¹} of member:R¹ //│ sym = module:R⁰ //│ nme = Ident of "foo" //│ sym = term:R⁰/foo² //│ rhs = Tup of Nil //│ rhs = Tup of Nil //│ rhs = Tup of Nil // the symbol of this Sel should be M because of the implicit application R.bar //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:M⁰}: //│ lhs = App: //│ lhs = App: //│ lhs = Resolved{sym=term:R⁰/bar²}: //│ t = Sel{sym=member:bar³}: //│ prefix = Resolved{sym=module:R⁰,typ=module:R⁰}: //│ t = Ref{sym=member:R¹} of member:R¹ //│ sym = module:R⁰ //│ nme = Ident of "bar" //│ sym = term:R⁰/bar² //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Int)⁰,typ=class:Int⁰}: //│ t = Ref{sym=member:instance$Ident(Int)¹} of member:instance$Ident(Int)¹ //│ sym = term:instance$Ident(Int)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Num)⁰,typ=class:Num⁰}: //│ t = Ref{sym=member:instance$Ident(Num)¹} of member:instance$Ident(Num)¹ //│ sym = term:instance$Ident(Num)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Str)⁰,typ=class:Str⁰}: //│ t = Ref{sym=member:instance$Ident(Str)¹} of member:instance$Ident(Str)¹ //│ sym = term:instance$Ident(Str)⁰ // the symbol of this Ref should be M because of the implicit application foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:M⁰}: //│ lhs = App: //│ lhs = App: //│ lhs = Resolved{sym=term:foo⁴}: //│ t = Ref{sym=member:foo⁵} of member:foo⁵ //│ sym = term:foo⁴ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Int)⁰,typ=class:Int⁰}: //│ t = Ref{sym=member:instance$Ident(Int)¹} of member:instance$Ident(Int)¹ //│ sym = term:instance$Ident(Int)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Num)⁰,typ=class:Num⁰}: //│ t = Ref{sym=member:instance$Ident(Num)¹} of member:instance$Ident(Num)¹ //│ sym = term:instance$Ident(Num)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Str)⁰,typ=class:Str⁰}: //│ t = Ref{sym=member:instance$Ident(Str)¹} of member:instance$Ident(Str)¹ //│ sym = term:instance$Ident(Str)⁰ // the symbol of this TyApp should be M because of the implicit application // note that the resolved symbol for implicit application is on TyApp instead of on Sel R.bar[Int] //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:M⁰}: //│ lhs = App: //│ lhs = App: //│ lhs = TyApp{sym=term:R⁰/bar²}: //│ lhs = Resolved{sym=term:R⁰/bar²}: //│ t = Sel{sym=member:bar³}: //│ prefix = Resolved{sym=module:R⁰,typ=module:R⁰}: //│ t = Ref{sym=member:R¹} of member:R¹ //│ sym = module:R⁰ //│ nme = Ident of "bar" //│ sym = term:R⁰/bar² //│ targs = Ls of //│ Resolved{sym=class:Int⁰}: //│ t = Ref{sym=member:Int¹} of member:Int¹ //│ sym = class:Int⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Int)⁰,typ=class:Int⁰}: //│ t = Ref{sym=member:instance$Ident(Int)¹} of member:instance$Ident(Int)¹ //│ sym = term:instance$Ident(Int)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Num)⁰,typ=class:Num⁰}: //│ t = Ref{sym=member:instance$Ident(Num)¹} of member:instance$Ident(Num)¹ //│ sym = term:instance$Ident(Num)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Str)⁰,typ=class:Str⁰}: //│ t = Ref{sym=member:instance$Ident(Str)¹} of member:instance$Ident(Str)¹ //│ sym = term:instance$Ident(Str)⁰ // the symbol of this TyApp should be M because of the implicit application // note that the resolved symbol for implicit application is on TyApp instead of on Ref foo[Int] //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = App{typ=module:M⁰}: //│ lhs = App: //│ lhs = App: //│ lhs = TyApp{sym=term:foo⁴}: //│ lhs = Resolved{sym=term:foo⁴}: //│ t = Ref{sym=member:foo⁵} of member:foo⁵ //│ sym = term:foo⁴ //│ targs = Ls of //│ Resolved{sym=class:Int⁰}: //│ t = Ref{sym=member:Int¹} of member:Int¹ //│ sym = class:Int⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Int)⁰,typ=class:Int⁰}: //│ t = Ref{sym=member:instance$Ident(Int)¹} of member:instance$Ident(Int)¹ //│ sym = term:instance$Ident(Int)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Num)⁰,typ=class:Num⁰}: //│ t = Ref{sym=member:instance$Ident(Num)¹} of member:instance$Ident(Num)¹ //│ sym = term:instance$Ident(Num)⁰ //│ rhs = Tup of Ls of //│ Fld: //│ term = Resolved{sym=term:instance$Ident(Str)⁰,typ=class:Str⁰}: //│ t = Ref{sym=member:instance$Ident(Str)¹} of member:instance$Ident(Str)¹ //│ sym = term:instance$Ident(Str)⁰ ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/TrickyResolution.mls ================================================ :js class Foo[A] class Bar[B] fun foo[A](using Foo[A], Bar[A]) fun foo[A](using f: Foo[A], b: Bar[A]): A using Foo[Str] = new Foo using Foo[Int] = new Foo using Bar[Int] = new Bar using Bar[Str] = new Bar // * Probably picks A = Int due to the Foo constraint, picking up the latest-defined Foo instance, // * and thus picks the Bar[Int] instance :fixme foo //│ ═══[RUNTIME ERROR] TypeError: foo1 is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/TypeClasses.mls ================================================ :js // Syntaxes using Int = 42 using Int as someInt = 42 module M with fun f(using Int) = 42 module M with fun f(using foo: Int) = 42 module M with fun f(using foo: Int, bar: Int) = 42 module M with fun f(using foo: Int)(bar: Int) = 42 module M with fun f(foo: Int)(using bar: Int) = 42 module M with fun f(using foo: Int)(using bar: Int) = 42 module M with fun f(using foo: Int, bar: Int) = 42 module M with fun f(using Int, Int) = 42 module M with fun foo(using Int, arg: Str) = 42 module M with fun foo(using arg: Str, Int) = 42 // Basic Resolution abstract class Foo[T] with fun foo(): T class IntFoo extends Foo[Int] with fun foo(): Int = 42 class StrFoo extends Foo[Str] with fun foo(): Str = "42" module M with fun foo(using someInt: Int): Int = someInt fun strFoo(using someFoo: Foo[Str]): Str = someFoo.foo() fun intFoo(using someFoo: Foo[Int]): Int = someFoo.foo() fun tFoo[T](using someFoo: Foo[T]): T = someFoo.foo() using Int as i = 24 using Foo[Int] = new IntFoo() using Foo[Str] = new StrFoo() // should resolve to foo(i) :expect 24 M.foo //│ = 24 // should resolve to intFoo(new IntFoo()) :expect 42 M.intFoo //│ = 42 // should resolve to strFoo(new StrFoo()) :expect "42" M.strFoo //│ = "42" // should resolve to 100 + intFoo(new IntFoo()) :expect 142 100 + M.intFoo //│ = 142 // should be able to resolve in function body from outer scope :expect 42 fun f: Int = M.intFoo f //│ = 42 // should be able to resolve in function body from parameters :expect 3.14 module N with fun f(using someNum: Num): Num = someNum fun g(using Num): Num = N.f using Num = 3.14 N.g //│ = 3.14 // Parameterized Type Resolution // should resolve to tFoo(new StrFoo()) :expect "42" M.tFoo //│ = "42" // should resolve to tFoo(new IntFoo()) :expect 42 M.tFoo[Int] //│ = 42 abstract class Bar[A, B] with fun bar(): Str class IntStrBar extends Bar[Int, Str] with fun bar(): Str = "IntStr" class StrIntBar extends Bar[Str, Int] with fun bar(): Str = "StrInt" module M with fun tBar1[T](using someFoo: Bar[Int, T]): Str = someFoo.bar() fun tBar2[T](using someFoo: Bar[Str, T]): Str = someFoo.bar() fun tBar3[A, B](using someFoo: Bar[A, B]): Str = someFoo.bar() using Bar[Int, Str] = new IntStrBar() using Bar[Str, Int] = new StrIntBar() // should resolve to tFoo1(new IntStrBar()) :expect "IntStr" M.tBar1 //│ = "IntStr" // should resolve to tFoo2(new StrIntBar()) :expect "StrInt" M.tBar2 //│ = "StrInt" // should resolve to tFoo3(new StrIntBar()) :expect "StrInt" M.tBar3 //│ = "StrInt" // should resolve to tFoo3(new IntStrBar()) :expect "IntStr" M.tBar3[Int, Str] //│ = "IntStr" // Monoid Example abstract class Monoid[T] with fun combine(a: T, b: T): T fun empty: T object IntAddMonoid extends Monoid[Int] with fun combine(a: Int, b: Int): Int = a + b fun empty: Int = 0 object IntMulMonoid extends Monoid[Int] with fun combine(a: Int, b: Int): Int = a * b fun empty: Int = 1 module M with fun foldInt(x1: Int, x2: Int, x3: Int)(using m: Monoid[Int]): Int = m.combine(x1, m.combine(x2, m.combine(x3, m.empty))) fun fold[T](x1: T, x2: T, x3: T)(using m: Monoid[T]): T = m.combine(x1, m.combine(x2, m.combine(x3, m.empty))) :expect 9 using Monoid[Int] = IntAddMonoid M.foldInt(2, 3, 4) //│ = 9 :expect 24 using Monoid[Int] = IntMulMonoid M.foldInt(2, 3, 4) //│ = 24 :expect 6 using Monoid[Int] = IntAddMonoid M.fold(1, 2, 3) //│ = 6 // Generalized Module Methods using Int = 42 :expect 42 fun foo()(using i: Int) = i foo() //│ = 42 :expect 42 fun main() = fun foo()(using i: Int) = i foo() main() //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/ctx/TypeResolution.mls ================================================ class Foo with val foo: Foo = new Foo :rt new Foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = New{typ=class:Foo⁰}: //│ cls = Resolved{sym=class:Foo⁰}: //│ t = Ref{sym=member:Foo¹} of member:Foo¹ //│ sym = class:Foo⁰ // * TODO: dynamic new-expression should also be resolvable for its type :rt new! Foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = DynNew: //│ cls = Resolved{sym=class:Foo⁰}: //│ t = Ref{sym=member:Foo¹} of member:Foo¹ //│ sym = class:Foo⁰ val foo: Foo = new Foo :rt foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:foo⁰,typ=class:Foo⁰}: //│ t = Ref{sym=member:foo¹} of member:foo¹ //│ sym = term:foo⁰ :rt foo.foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:Foo⁰/foo²,typ=class:Foo⁰}: //│ t = Sel{sym=member:foo³}: //│ prefix = Resolved{sym=term:foo⁰,typ=class:Foo⁰}: //│ t = Ref{sym=member:foo¹} of member:foo¹ //│ sym = term:foo⁰ //│ nme = Ident of "foo" //│ sym = term:Foo⁰/foo² class Foo2[A, B] with val foo: Foo2[Int, Str] = new Foo2 :rt new Foo2 //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = New{typ=class:Foo2⁰}: //│ cls = Resolved{sym=class:Foo2⁰}: //│ t = Ref{sym=member:Foo2¹} of member:Foo2¹ //│ sym = class:Foo2⁰ // * TODO: dynamic new-expression should also be resolvable for its type :rt new! Foo2 //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = DynNew: //│ cls = Resolved{sym=class:Foo2⁰}: //│ t = Ref{sym=member:Foo2¹} of member:Foo2¹ //│ sym = class:Foo2⁰ val foo: Foo2[Str, Int] = new Foo2 :rt foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:foo⁴,typ=class:Foo2⁰[class:Str⁰, class:Int⁰]}: //│ t = Ref{sym=member:foo⁵} of member:foo⁵ //│ sym = term:foo⁴ :rt foo.foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:Foo2⁰/foo⁶,typ=class:Foo2⁰[class:Int⁰, class:Str⁰]}: //│ t = Sel{sym=member:foo⁷}: //│ prefix = Resolved{sym=term:foo⁴,typ=class:Foo2⁰[class:Str⁰, class:Int⁰]}: //│ t = Ref{sym=member:foo⁵} of member:foo⁵ //│ sym = term:foo⁴ //│ nme = Ident of "foo" //│ sym = term:Foo2⁰/foo⁶ :rt fun f(x: Foo) = x //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ stats = Ls of //│ TermDefinition: //│ k = Fun //│ sym = member:f⁰ //│ tsym = term:f¹ //│ params = Ls of //│ ParamList: //│ params = Ls of //│ Param: //│ sym = x⁰ //│ sign = S of Resolved{sym=class:Foo⁰}: //│ t = Ref{sym=member:Foo¹} of member:Foo¹ //│ sym = class:Foo⁰ //│ modulefulness = Modulefulness of N //│ body = S of Ref{sym=x⁰,typ=class:Foo⁰} of x⁰ //│ modulefulness = Modulefulness of N //│ res = Lit of UnitLit of false module M object O :rt M //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ :rt O //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=object:O⁰,typ=object:O⁰}: //│ t = Ref{sym=member:O¹} of member:O¹ //│ sym = object:O⁰ val m: module M = M val o: O = O :rt m //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:m⁰,typ=module:M⁰}: //│ t = Ref{sym=member:m¹} of member:m¹ //│ sym = term:m⁰ :rt o //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ res = Resolved{sym=term:o⁰,typ=object:O⁰}: //│ t = Ref{sym=member:o¹} of member:o¹ //│ sym = term:o⁰ :rt fun f(module m: M): module M = m //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ stats = Ls of //│ TermDefinition: //│ k = Fun //│ sym = member:f² //│ tsym = term:f³ //│ params = Ls of //│ ParamList: //│ params = Ls of //│ Param: //│ sym = m² //│ sign = S of Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ modulefulness = Modulefulness of S of module:M⁰ //│ sign = S of Resolved{sym=module:M⁰,typ=module:M⁰}: //│ t = Ref{sym=member:M¹} of member:M¹ //│ sym = module:M⁰ //│ body = S of Ref{sym=m²,typ=module:M⁰} of m² //│ modulefulness = Modulefulness of S of module:M⁰ //│ res = Lit of UnitLit of false :rt fun f(o: O) = o //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ stats = Ls of //│ TermDefinition: //│ k = Fun //│ sym = member:f⁴ //│ tsym = term:f⁵ //│ params = Ls of //│ ParamList: //│ params = Ls of //│ Param: //│ sym = o² //│ sign = S of Resolved{sym=object:O⁰,typ=object:O⁰}: //│ t = Ref{sym=member:O¹} of member:O¹ //│ sym = object:O⁰ //│ modulefulness = Modulefulness of N //│ body = S of Ref{sym=o²,typ=object:O⁰} of o² //│ modulefulness = Modulefulness of N //│ res = Lit of UnitLit of false // The implicit `this` (class:Foo) in the SynthSel(class:Foo, "foo") should be resolved correctly. :rt class Foo with val foo = 42 foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ stats = Ls of //│ Plain: //│ kind = Cls //│ sym = class:Foo² //│ bsym = member:Foo³ //│ body = ObjBody of Blk: //│ stats = Ls of //│ TermDefinition: //│ k = ImmutVal //│ sym = member:foo⁸ //│ tsym = term:Foo²/foo⁹ //│ body = S of Lit of IntLit of 42 //│ flags = (method) //│ modulefulness = Modulefulness of N //│ res = Resolved{sym=term:Foo²/foo⁹}: //│ t = SynthSel{sym=member:foo⁸}: //│ prefix = Ref{sym=class:Foo²,typ=class:Foo²} of class:Foo² //│ nme = Ident of "foo" //│ sym = term:Foo²/foo⁹ //│ res = Lit of UnitLit of false // The explicit `this` (class:Foo) in the Sel(class:Foo, "foo") should be resolved correctly. :rt class Foo with val foo = 42 this.foo //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk: //│ stats = Ls of //│ Plain: //│ kind = Cls //│ sym = class:Foo⁴ //│ bsym = member:Foo⁵ //│ body = ObjBody of Blk: //│ stats = Ls of //│ TermDefinition: //│ k = ImmutVal //│ sym = member:foo¹⁰ //│ tsym = term:Foo⁴/foo¹¹ //│ body = S of Lit of IntLit of 42 //│ flags = (method) //│ modulefulness = Modulefulness of N //│ res = Resolved{sym=term:Foo⁴/foo¹¹}: //│ t = Sel{sym=member:foo¹⁰}: //│ prefix = Ref{sym=class:Foo⁴,typ=class:Foo⁴} of class:Foo⁴ //│ nme = Ident of "foo" //│ sym = term:Foo⁴/foo¹¹ //│ res = Lit of UnitLit of false ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/basic.mls ================================================ :js :noInline :deadParamElim debug :soir private fun f(a) = 0 f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰; define f⁰ as fun f¹() { return 0 }; f¹() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir private fun f(a, b) = 0 f(1, 2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f²; define f² as fun f³() { return 0 }; f³() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir private fun f(a)(b) = 0 f(1)(2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁴; define f⁴ as fun f⁵()(b) { return 0 }; f⁵()(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir private fun f(a) = a f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁶; define f⁶ as fun f⁷(a) { return a }; f⁷(42) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 :soir private fun f(a, b) = a + b f(1, 2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁸; define f⁸ as fun f⁹(a, b) { return +⁰(a, b) }; f⁹(1, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 :soir private fun f(a, b) = a f(1, 2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹⁰; define f¹⁰ as fun f¹¹(a) { return a }; f¹¹(1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :soir private fun f(a, b) = b f(1, 2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹²; define f¹² as fun f¹³(b) { return b }; f¹³(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 :soir private fun f(a)(b) = a f(1)(2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹⁴; define f¹⁴ as fun f¹⁵(a)(b) { return a }; f¹⁵(1)(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :soir private fun f(a)(b) = b f(1)(2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹⁶; define f¹⁶ as fun f¹⁷()(b) { return b }; f¹⁷()(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 :soir private fun f(a) = 0 f(1) f(2) f(3) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f¹⁸; define f¹⁸ as fun f¹⁹() { return 0 }; do f¹⁹(); do f¹⁹(); f¹⁹() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir private fun g() = fun f(a) = 3 f(4) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let g⁰; define g⁰ as fun g¹() { let f; define f as fun f²⁰() { return 3 }; return f²⁰() }; end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :soir class Foo(x) with fun bar(a) = x let foo = Foo(1) foo.bar(99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let Foo⁰, foo⁰; //│ define Foo⁰ as class Foo²(x) { //│ private val x⁰; //│ constructor Foo¹ { //│ set x⁰ = x; //│ end //│ } //│ method bar⁰ = fun bar¹(a) { return x⁰ } //│ }; //│ set foo⁰ = Foo¹(1); //│ foo⁰.bar﹖(99) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 //│ foo = Foo(_) class Foo(x) with fun bar(a) = a let foo = Foo(1) foo.bar(99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 99 //│ foo = Foo(_) :expect 1 class Foo(x) with fun get() = x Foo(1).get() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :expect 1 data class Pair(a, b) private fun fst(p) = if p is Pair(a, b) then a fst(Pair(1, 2)) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :expect 2 data class Pair(a, b) private fun snd(p) = if p is Pair(a, b) then b snd(Pair(1, 2)) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 2 :expect 43 private fun g(x) = x + 1 private fun f(a) = g(a) f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 43 :expect 42 private fun f(a) = let x = a x f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 42 :expect 1 private fun apply(f) = f(0) private fun inc(x) = x + 1 apply(inc) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :expect 1 data class Pair(a, b) private fun f(p) = p.Pair#a f(Pair(1, 2)) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 private fun f(a) = g(a) private fun g(x) = 3 f(1) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > prodfun g#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 3 :deadParamElim debug poly fun f(a) = g(a) fun g(x) = 3 f(1) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ f@3 -> eliminable: {0} //│ dead-param-elim > prodfun g#0 @ f@3.g@1 -> eliminable: {0} //│ dead-param-elim > prodfun g#0 @ f@4.g@1 -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 3 f(2) g(1) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 3 :deadParamElim debug mono :expect [3, 4] private fun f(x) = fun g(a, b, ...rest) = print(x) rest g(1, 2, 3, 4) f(2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun g#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ > 2 //│ = [3, 4] ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/clash.mls ================================================ :js :noInline :deadParamElim debug // g has dead param `b`, but f has live param `a` :expect 0 private fun f(a) = a private fun g(b) = 0 private fun test(x)(y) = if false then x else y private fun m() = test(f)(g)(5) m() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :expect 3 private fun f(a)(b) = 0 private fun g(a)(b) = a + b private fun main(x)(y) = if false then x else y private fun m1() = main(f)(g)(1)(2) m1() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 3 :expect 5 private fun f(a) = a private fun g(b) = 0 let h = if true then f else g h(5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 5 //│ h = fun f // f, g, h all flow to the same call site :expect 0 private fun f(a) = a private fun g(b) = 0 private fun h(c) = 0 private fun pick(x)(y)(z) = if false then x else if false then y else z private fun m() = pick(f)(g)(h)(5) m() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :expect 1 private fun f(a)(b) = 0 private fun g(a)(b) = a private fun choose(x)(y) = if false then x else y private fun m() = choose(f)(g)(1)(2) m() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :expect 1 private fun f(a, b) = 0 private fun g(a, b) = a private fun choose(x)(y) = if false then x else y private fun m() = choose(f)(g)(1, 2) m() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :soir [1,2,3].map((x, i, a) => x + 1) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let tmp, lambda⁰; //│ set tmp = [1, 2, 3]; //│ define lambda⁰ as fun lambda¹(x, i, a) { return +⁰(x, 1) }; //│ tmp.map﹖(lambda¹) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [2, 3, 4] :expect [4, 5] private fun f(a, b, ...rest) = rest private fun g(a, b, c, ...rest) = rest (if false then f else g)(1,2,3,4,5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0, 1} //│ dead-param-elim > prodfun g#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = [4, 5] :expect [3, 4, 5] private fun f(a, b, ...rest) = rest private fun g(a, b, c, ...rest) = rest (if true then f else g)(1,2,3,4,5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0, 1} //│ dead-param-elim > prodfun g#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = [3, 4, 5] :expect [3, 4, 5] public fun f(a, b, ...rest) = rest public fun g(a, b, c, ...rest) = rest (if true then f else g)(1,2,3,4,5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = [3, 4, 5] :expect [4, 5] (if false then f else g)(1,2,3,4,5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = [4, 5] :expect [3, 4, 5] (if true then f else g)(1,2,3,4,5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = [3, 4, 5] ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/class-in-fun.mls ================================================ :js :deadParamElim debug :lift :soir :expect 1 :noInline private fun f(used, unused) = class C with fun get() = used new C f(1, 2).get() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let C⁰, f⁰, tmp; //│ define C⁰ as class C¹ { //│ private val used⁰; //│ constructor(used) { //│ set used⁰ = used; //│ end //│ } //│ method get⁰ = fun get¹() { //│ return used⁰ //│ } //│ }; //│ define f⁰ as fun f¹(used) { return new C¹(used) }; //│ set tmp = f¹(1); //│ tmp.get﹖() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :js :lift private fun foo(y) = class M with fun bar() = set y = 2 (new M).bar() foo(10) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/config-flags.mls ================================================ :js :noInline // DPE is on by default, no debug output :soir private fun noFlag(a) = 0 noFlag(42) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let noFlag⁰; define noFlag⁰ as fun noFlag¹() { return 0 }; noFlag¹() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :deadParamElim private fun bareFlag(a) = 0 bareFlag(42) //│ = 0 :deadParamElim mono debug private fun monoDebug(a) = 0 monoDebug(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun monoDebug#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :deadParamElim debug mono :soir private fun debugMono(a) = 0 debugMono(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun debugMono#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let debugMono⁰; define debugMono⁰ as fun debugMono¹() { return 0 }; debugMono¹() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :deadParamElim mono poly private fun conflictTest(a) = 0 conflictTest(42) //│ /!!!\ ':deadParamElim' flags 'mono' and 'poly' conflict //│ = 0 // :deadParamElim off — disable DPE, dead param should NOT be eliminated :deadParamElim off :soir private fun dpeOff(a) = 0 dpeOff(42) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let dpeOff⁰; define dpeOff⁰ as fun dpeOff¹(a) { return 0 }; dpeOff¹(42) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :deadParamElim foo private fun unknownFlag(a) = 0 unknownFlag(42) //│ /!!!\ Unknown ':deadParamElim' flags: foo //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/dead-ref.mls ================================================ :js :noInline :deadParamElim debug :soir private fun f(a) = a = 3 g(a) private fun g(a) = 2 f(undefined) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > prodfun g#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰, g⁰; define f⁰ as fun f¹() { return g¹() }; define g⁰ as fun g¹() { return 2 }; f¹() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 data class Box(x) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< :soir private fun f(a) = Box(a) if f(42) is Box(v) then 0 //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f², scrut; //│ define f² as fun f³() { //│ return Box⁰(undefined) //│ }; //│ set scrut = f³(); //│ match scrut //│ Box¹ => //│ 0 //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir private fun f(a) = let x = a 0 f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁴; define f⁴ as fun f⁵() { return 0 }; f⁵() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls ================================================ :js :deadParamElim debug :expect 0 let f = (a) => 0 f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 //│ f = fun f :expect 42 let f = (a) => a f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 42 //│ f = fun f :noInline :expect 0 private fun apply(f, x) = f(x) apply((a) => 0, 42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun apply#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :expect 43 private fun apply(f, x) = f(x) apply((a) => a + 1, 42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 43 private fun f(a) = a let g = (b) => 0 private fun pick(x)(y) = if false then x else y pick(f)(g)(5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 //│ g = fun g fun f(a) = 0 let g = (b) => 0 private fun pick(x)(y) = if false then x else y pick(f)(g)(5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 //│ g = fun g :expect 42 private fun f(a) = let g = () => a g() f(42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 42 // two `apply`, one has dead param, another does not :noInline private fun apply(f, x) = f(x) private fun main(f1, f2) = apply(f1, 42) + apply(f2, 0) private fun main1() = main((a) => 0, (a) => a + 1) main1() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 // two `apply`, one has dead param, another does not; // but if `apply` is inlined, we can eliminate the `a` in `(a) => 0` private fun apply(f, x) = f(x) private fun main(f1, f2) = apply(f1, 42) + apply(f2, 0) private fun main1() = main((a) => 0, (a) => a + 1) main1() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun lambda#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :deadParamElim debug poly :noInline private fun apply(f, x) = f(x) private fun main(f1, f2) = apply(f1, 42) + apply(f2, 0) private fun main1() = main((a) => 0, (a) => a + 1) main1() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun apply#0 @ main1@10.main@8.apply@2 -> eliminable: {1} //│ dead-param-elim > prodfun apply#0 @ main1@13.main@8.apply@2 -> eliminable: {1} //│ dead-param-elim > prodfun lambda#0 @ main1@10 -> eliminable: {0} //│ dead-param-elim > prodfun lambda#0 @ main1@13 -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :deadParamElim debug mono :expect 0 private fun f = (a) => 0 f(3) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun lambda#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls ================================================ :js :deadParamElim debug module M with private fun f(a) = 0 private fun g(b) = b M.f(1) M.g(2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 2 :expect 3 module M with val x = 3 private fun f(a) = x private fun main() = M.f(42) M.main() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 3 :expect 42 module M with private fun f(a) = a private fun main() = M.f(42) M.main() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 42 module M with private fun f(a) = a private fun g(b) = 0 private fun pick(x)(y) = if false then x else y private fun main() = M.pick(M.f)(M.g)(5) M.main() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :expect 125 module M with data class Foo(x) with private fun m() = this.x + 1 private fun n(y) = this.x + y let foo = M.Foo(123) foo M.Foo::n(2) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 125 //│ foo = Foo(123) ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/multiArgLists.mls ================================================ :js :noInline // Test: Dead parameter elimination with multiple parameter lists :deadParamElim :soir private fun f(a)(b) = a f(42)(99) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰; define f⁰ as fun f¹(a)(b) { return a }; f¹(42)(99) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // Test: Both params dead across two parameter lists :deadParamElim :soir private fun g(x)(y) = 0 g(1)(2) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let g⁰; define g⁰ as fun g¹()(y) { return 0 }; g¹()(2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 // Test: Dead param in second list only :deadParamElim :soir private fun h(x)(y) = x h(5)(10) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let h⁰; define h⁰ as fun h¹(x)(y) { return x }; h¹(5)(10) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 5 // Test: Dead param in first list only :deadParamElim :soir private fun j(x)(y) = y j(5)(10) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let j⁰; define j⁰ as fun j¹()(y) { return y }; j¹()(10) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 10 // Test: Three parameter lists, middle one dead :deadParamElim :soir private fun k(a)(b)(c) = a + c k(1)(2)(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let k⁰; define k⁰ as fun k¹(a)(b)(c) { return +⁰(a, c) }; k¹(1)(2)(3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls ================================================ :js :deadParamElim debug :expect 0 :soir :noTailRec private fun countdown(n, unused) = if n == 0 then 0 else countdown(n - 1, unused) countdown(5, 42) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun countdown#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let countdown⁰; //│ define countdown⁰ as fun countdown¹(n) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return countdown¹(tmp) //│ end //│ }; //│ countdown¹(5) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir private fun countdown(n) = if n == 0 then 0 else countdown(n - 1) countdown(5) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let countdown²; //│ define countdown² as fun countdown³(n) { //│ loop loopLabel: //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ set n = tmp; //│ continue loopLabel //│ end //│ unreachable /* Rest of abortive labelled block */ //│ }; //│ countdown³(5) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 private fun isEven(n) = if n == 0 then true else isOdd(n - 1) private fun isOdd(n) = if n == 0 then false else isEven(n - 1) isEven(10) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = true :soir :noTailRec private fun ping(n, unused) = if n == 0 then 0 else pong(n - 1, unused) private fun pong(n, unused) = if n == 0 then 0 else ping(n - 1, unused) ping(5, 99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun ping#0 @ -> eliminable: {1} //│ dead-param-elim > prodfun pong#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let ping⁰, pong⁰; //│ define ping⁰ as fun ping¹(n) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return pong¹(tmp) //│ end //│ }; //│ define pong⁰ as fun pong¹(n) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return ping¹(tmp) //│ end //│ }; //│ ping¹(5) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :soir :noTailRec fun ping(n, unused) = if n == 0 then 0 else pong(n - 1, unused) fun pong(n, unused) = if n == 0 then 0 else ping(n - 1, unused) ping(5, 99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let ping², pong²; //│ define ping² as fun ping³(n, unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return pong³(tmp, unused) //│ end //│ }; //│ define pong² as fun pong³(n, unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return ping³(tmp, unused) //│ end //│ }; //│ ping³(5, 99) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :expect 0 ping(5, 0) pong(5, 0) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :soir :noTailRec private fun ping(n)(unused) = if n == 0 then 0 else pong(n - 1)(unused) private fun pong(n)(unused) = if n == 0 then 0 else ping(n - 1)(unused) ping(5)(99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let ping⁴, pong⁴; //│ define ping⁴ as fun ping⁵(n)(unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return pong⁵(tmp)(unused) //│ end //│ }; //│ define pong⁴ as fun pong⁵(n)(unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return ping⁵(tmp)(unused) //│ end //│ }; //│ ping⁵(5)(99) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :noTailRec fun ping(n)(unused) = if n == 0 then 0 else pong(n - 1)(unused) fun pong(n)(unused) = if n == 0 then 0 else ping(n - 1)(unused) ping(5)(99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :expect 0 ping(5)(0) pong(5)(0) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 :soir :noTailRec module M with fun ping(n, unused) = if n == 0 then 0 else pong(n - 1, unused) private fun pong(n, unused) = if n == 0 then 0 else M.ping(n - 1, unused) class Test(a) with fun f() = pong(3, 123) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun pong#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let Test⁰, pong⁶, M⁰; //│ define pong⁶ as fun pong⁷(n) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return M¹.ping⁶(tmp, undefined) //│ end //│ }; //│ define M⁰ as class M² //│ module M¹ { //│ method ping⁷ = fun ping⁶(n, unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ set tmp = -⁰(n, 1); //│ return pong⁷(tmp) //│ end //│ } //│ }; //│ define Test⁰ as class Test¹(a) { private val a⁰; method f⁰ = fun f¹() { return pong⁷(3) } }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 0 Test(2).f() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls ================================================ :js :deadParamElim debug poly :checkIR :expect 1 fun f(used, unused) = class C with fun get() = used new C f(1, 2).get() //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun f#0 @ f@1 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 :checkIR fun foo(y) = class M with fun bar() = set y = 2 (new M).bar() foo(10) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun foo#0 @ foo@3 -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< :checkIR :expect 10 fun g(x, dead) = class Pair with fun first() = x fun second() = x + x let p = new Pair p.first() g(10, 99) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun g#0 @ g@3 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 10 :checkIR :expect 13 fun h(base, unused) = class Adder with fun add(n) = base + n new Adder h(10, 0).add(3) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun h#0 @ h@2 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 13 :checkIR :expect 42 :soir fun wrapper(val1, dead) = class A with fun makeB() = class B with fun getVal() = val1 new B (new A).makeB().getVal() wrapper(42, 0) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun wrapper#0 @ wrapper@3 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let wrapper⁰, wrapper_3$wrapper⁰; //│ define wrapper_3$wrapper⁰ as fun wrapper_3$wrapper¹(val1) { //│ let A, tmp, tmp1; //│ define A as class A⁰ { //│ method makeB⁰ = fun makeB¹() { //│ let B; //│ define B as class B⁰ { //│ method getVal⁰ = fun getVal¹() { //│ return val1 //│ } //│ }; //│ return new B⁰() //│ } //│ }; //│ set tmp = new A⁰(); //│ set tmp1 = tmp.makeB¹(); //│ return tmp1.getVal﹖() //│ }; //│ define wrapper⁰ as fun wrapper¹(val1, dead) { //│ let A, tmp, tmp1; //│ define A as class A¹ { //│ method makeB² = fun makeB³() { //│ let B; //│ define B as class B¹ { //│ method getVal² = fun getVal³() { //│ return val1 //│ } //│ }; //│ return new B¹() //│ } //│ }; //│ set tmp = new A¹(); //│ set tmp1 = tmp.makeB³(); //│ return tmp1.getVal﹖() //│ }; //│ wrapper_3$wrapper¹(42) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 :checkIR :expect 30 fun multi(a, b, dead) = class Calc with fun sum() = a + b fun prod() = a * b (new Calc).prod() multi(5, 6, 999) //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun multi#0 @ multi@4 -> eliminable: {2} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 30 ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/todos.mls ================================================ ================================================ FILE: hkmc2/shared/src/test/mlscript/dead-param-elim/ups.mls ================================================ :js :deadParamElim debug :ucs ups pattern Middle = ([x] => x) | ([x, ...(Middle as m), y] => m) //│ elaborated pattern body: [x] => x⁰ ∨ [x, ...Middle as m, y] => m⁰ //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > prodfun lambda#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< if [1,2,3,4,5] is Middle as x then x //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 3 :re if [1,2,3,4] is Middle as x then x //│ dead-param-elim > >>> dead-param-elim results >>> //│ dead-param-elim > <<< dead-param-elim results <<< //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/decls/Prelude.mls ================================================ declare type Any declare type Anything declare type Nothing declare type untyped declare type tailrec declare type tailcall declare class Object declare module Object with fun create freeze assign entries prototype fromEntries getPrototypeOf defineProperty getOwnPropertyDescriptor getOwnPropertyDescriptors declare class JSON declare module JSON with fun stringify declare class Number declare module Number with val MIN_VALUE MAX_VALUE MIN_SAFE_INTEGER MAX_SAFE_INTEGER NEGATIVE_INFINITY POSITIVE_INFINITY declare class BigInt declare module BigInt declare class Function declare module Function declare class String declare module String with fun fromCharCode fromCodePoint raw declare class RegExp declare module RegExp declare class Set[V] declare module Set declare class Map[K, V] declare module Map declare class WeakSet[V] declare module WeakSet declare class WeakMap[K, V] declare module WeakMap declare class Error//(info) // TODO: handle JS classes that can be instantiated without `new` specially in codegen. declare class TypeError//(info) // TODO: handle JS classes that can be instantiated without `new` specially in codegen. declare class RangeError//(info) // TODO: handle JS classes that can be instantiated without `new` specially in codegen. declare class Date declare module Date declare class ArrayBuffer declare module ArrayBuffer declare class TypedArray declare module TypedArray declare class Int8Array declare module Int8Array declare class Uint8Array declare module Uint8Array declare class Uint8ClampedArray declare module Uint8ClampedArray declare class Int16Array declare module Int16Array declare class Uint16Array declare module Uint16Array declare class Int32Array declare module Int32Array declare class Uint32Array declare module Uint32Array declare class Float16Array declare module Float16Array declare class Float32Array declare module Float32Array declare class Float64Array declare module Float64Array declare class BigInt64Array declare module BigInt64Array declare class BigUint64Array declare module BigUint64Array // MLscript-specific types declare class Bool declare class Int declare class Num declare class Str declare class Class // The `Array` class/function is a footgun: // > Array() // [] // > Array(1) // [ <1 empty item> ] // > Array(1, 2) // [ 1, 2 ] // We used to declare it as a class taking exactly one argument here to avoid that footgun: // declare class Array[T](val length: Int): Array[T] // but this made instance checks wrongly use `Array.class`; // TODO: handle Array and other JS classes that can be instantiated without `new` specially in codegen. declare class Array[T] declare module Array with fun from concat isArray prototype declare object Symbol with // The `TermDef` needs `rhs` to be defined to be recognized as `isMLsFun`. // Otherwise, it would be wrapped in `runtime.safeCall`, which accesses the // uninitialized `runtime` in the `Rendering` module. fun for: Str -> Any = () val iterator: Any // MLwasm-specific types declare class Int31 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math declare module Math with declare val E: Num LN10: Num LN2: Num LOG10E: Num LOG2E: Num PI: Num SQRT1_2: Num SQRT2: Num fun abs: Num -> Num acos: Num -> Num acosh: Num -> Num asin: Num -> Num asinh: Num -> Num atan: Num -> Num atan2: (Num, Num) -> Num atanh: Num -> Num cbrt: Num -> Num ceil: Num -> Num clz32: Num -> Int cos: Num -> Num cosh: Num -> Num exp: Num -> Num expm1: Num -> Num floor: Num -> Num f16round: Num -> Num fround: Num -> Num hypot(...values): Num imul: (Num, Num) -> Int log: Num -> Num log10: Num -> Num log1p: Num -> Num log2: Num -> Num max(...values): Num min(...values): Num pow: (Num, Num) -> Num random: () -> Num round: Num -> Num sign: Num -> (-1 | 0 | 1) sin: Num -> Num sinh: Num -> Num sqrt: Num -> Num tan: Num -> Num tanh: Num -> Num trunc: Num -> Num declare module Reflect with fun get // set // TODO: handle keyword-named members has ownKeys getPrototypeOf apply construct declare module console with declare fun log debug info warn error // assert clear count countReset dir dirxml group groupCollapsed groupEnd table time timeEnd trace declare val process // TODO make `module` declare val fs // TODO make `module` declare val Infinity // Wasm support declare class Promise declare val Promise declare object WebAssembly with declare object Instance declare class Memory declare object Module with fun exports imports fun instantiate validate // declare fun typeof: (Any) -> Str declare fun parseInt(str: Str, radix: Int): Int declare fun parseFloat(str: Str): Num declare module source with object line name file declare module js with fun bitand bitnot bitor shl try_catch declare module wasm with fun plus_impl minus_impl times_impl div_impl mod_impl eq_impl neq_impl lt_impl le_impl gt_impl ge_impl neg_impl pos_impl not_impl declare module debug with fun printStack declare module annotations with object compile object buffered object bufferable declare module scope with fun locally declare module runtime with fun suspend handle_suspension // HTML DOM API definitions. // Move them to a separate Prelude file when there are enough of them. declare val document declare val customElements declare class HTMLElement ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/append.mls ================================================ :js :deforest object Nil data class (::) Cons(h, t) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :expect Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) fun append1(xs1, ys) = if xs1 is h :: t then h :: append1(t, ys) Nil then ys fun append2(xs2, ys) = if xs2 is h :: t then h :: append2(t, ys) Nil then ys fun appendThree(xs, ys, zs) = append1(append2(xs, ys), zs) appendThree of id(1 :: 2 :: Nil) id(3 :: 4 :: Nil) id(5 :: 6 :: Nil) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) // maybe the fusion target for the previous program :expect Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) fun appendReified(ys, zs) = if ys is h :: t then h :: appendReified(t, zs) Nil then zs fun append1(ys, zs) = ys(zs) fun append2(xs, ys) = if xs is h :: t then zs => h :: append1(append2(t, ys), zs) // normal fusion Nil then zs => appendReified(ys, zs) // reified fun test(xs, ys, zs) = append1(append2(xs, ys), zs) test of id(1 :: 2 :: Nil) id(3 :: 4 :: Nil) id(5 :: 6 :: Nil) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) :expect Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) fun idList(l) = if l is h :: t then h :: idList(t) Nil then Nil fun append(xs, ys) = if xs is h :: t then h :: append(t, ys) Nil then idList(ys) fun appendThree(xs, ys, zs) = append(append(xs, ys), zs) appendThree of id(1 :: 2 :: Nil) id(3 :: 4 :: Nil) id(5 :: 6 :: Nil) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(h,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(h,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(h,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(h,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) :expect Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) fun append(xs, ys) = if xs is h :: t then h :: append(t, ys) Nil then idList(ys) fun concat(lss) = if lss is hh :: tt then append(hh, concat(tt)) Nil then Nil concat of id of (1 :: 2 :: Nil) :: (3 :: 4 :: Nil) :: (5 :: 6 :: Nil) :: Nil //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/basic.mls ================================================ :js :deforest data class X(a) data class Y(b) data class Z(c) data object O data class A(x) data class B(x) data object C data object D data object Nil data class (::) Cons(h, t) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :sir let lsit = [2, 1] let useless = if lsit is [a, b] then 0 //│ deforest > >>> fusing >>> //│ deforest > Tuple(false,List(Arg(None,Lit(IntLit(2))), Arg(None,Lit(IntLit(1))))) -> //│ deforest > Ref(lsit,None) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(lsit,None)), Arg(None,Lit(IntLit(0)))))) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(lsit,None)), Arg(None,Lit(IntLit(1)))))) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let lsit⁰, useless⁰, a⁰, b⁰, element1$, element0$, tmp, $lsit_tup2⁰, $lsit_rest⁰, tup2_0, tup2_1; //│ define $lsit_tup2⁰ as fun $lsit_tup2¹(tup2_01, tup2_11) { //│ set element0$ = tup2_01; //│ set element1$ = tup2_11; //│ set b⁰ = element1$; //│ set a⁰ = element0$; //│ set tmp = 0; //│ return $lsit_rest¹() //│ }; //│ define $lsit_rest⁰ as fun $lsit_rest¹() { //│ set useless⁰ = tmp; //│ return null //│ }; //│ set tup2_0 = 2; //│ set tup2_1 = 1; //│ set lsit⁰ = () => { return $lsit_tup2¹(tup2_0, tup2_1) }; //│ lsit⁰() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = null //│ lsit = fun lsit //│ useless = 0 :expect O fun p() = X(O) fun c(x) = if x is X(x) then x c(p()) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Ref(member:O,Some(object:O)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > <<< fusing <<< //│ = O :sir :expect O fun p() = X(Y(O)) fun f(x) = if x is X(Y(y)) then y f(p()) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Ref(member:Y,Some(term:Y/Y)),List(List(Arg(None,Ref(member:O,Some(object:O)))))) -> //│ deforest > Ref(tmp:arg$X$0$,None) //│ deforest > Select(Ref(tmp:arg$X$0$,None),Ident(b)) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let p⁰, f⁰, tmp, p_10$p⁰, f_12$f⁰, f_12$x_X⁰, f_12$x_rest⁰, f_12$split_default$_rest⁰, f_12$split_root$_rest⁰, f_12$arg$X$0$_Y⁰, f_12$arg$X$0$_rest⁰; //│ define p_10$p⁰ as fun p_10$p¹() { //│ let tmp1, Y_b, X_a; //│ set Y_b = O⁰; //│ set tmp1 = (fv_ctorLam_y, fv_ctorLam_arg$Y$0$) => { //│ return f_12$arg$X$0$_Y¹(fv_ctorLam_y, fv_ctorLam_arg$Y$0$, Y_b) //│ }; //│ set X_a = tmp1; //│ return (fv_ctorLam_y, fv_ctorLam_arg$X$0$, fv_ctorLam_arg$Y$0$) => { //│ return f_12$x_X¹(fv_ctorLam_y, fv_ctorLam_arg$X$0$, fv_ctorLam_arg$Y$0$, X_a) //│ } //│ }; //│ define f_12$f⁰ as fun f_12$f¹(x) { //│ let y, arg$X$0$, arg$Y$0$; //│ block split_root$: //│ block split_default$: //│ return x(y, arg$X$0$, arg$Y$0$) //│ throw new globalThis⁰.Error⁰("match error") //│ return runtime⁰.Unit⁰ //│ }; //│ define f_12$x_X⁰ as fun f_12$x_X¹(fv_y, fv_arg$X$0$, fv_arg$Y$0$, X_a) { //│ set fv_arg$X$0$ = X_a; //│ return fv_arg$X$0$(fv_y, fv_arg$Y$0$) //│ }; //│ define f_12$arg$X$0$_Y⁰ as fun f_12$arg$X$0$_Y¹(fv_y, fv_arg$Y$0$, Y_b) { //│ set fv_arg$Y$0$ = Y_b; //│ set fv_y = fv_arg$Y$0$; //│ return fv_y //│ }; //│ define f_12$x_rest⁰ as fun f_12$x_rest¹() { //│ return f_12$split_default$_rest¹() //│ }; //│ define f_12$split_default$_rest⁰ as fun f_12$split_default$_rest¹() { //│ throw new globalThis⁰.Error⁰("match error") //│ }; //│ define f_12$split_root$_rest⁰ as fun f_12$split_root$_rest¹() { //│ return runtime⁰.Unit⁰ //│ }; //│ define f_12$arg$X$0$_rest⁰ as fun f_12$arg$X$0$_rest¹() { //│ return f_12$x_rest¹() //│ }; //│ define p⁰ as fun p¹() { //│ let tmp1; //│ set tmp1 = Y⁰(O⁰); //│ return X⁰(tmp1) //│ }; //│ define f⁰ as fun f¹(x) { //│ let y, arg$X$0$, arg$Y$0$; //│ block split_root$: //│ block split_default$: //│ match x //│ X¹ => //│ set arg$X$0$ = x.a¹; //│ match arg$X$0$ //│ Y¹ => //│ set arg$Y$0$ = arg$X$0$.b¹; //│ set y = arg$Y$0$; //│ return y //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ throw new globalThis⁰.Error⁰("match error") //│ return runtime⁰.Unit⁰ //│ }; //│ set tmp = p_10$p¹(); //│ f_12$f¹(tmp) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = O :expect 14 fun p() = X(Y(O)) fun f(x, res) = if x is X(a) then let tmp = 12 if a is Y(y) then res + tmp f(p(), 2) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Ref(member:Y,Some(term:Y/Y)),List(List(Arg(None,Ref(member:O,Some(object:O)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(b)) //│ deforest > <<< fusing <<< //│ = 14 :expect 16 fun f(x, y, res) = if x is X(a) then let tmp = 12 if y is Y(b) then let _d = () => y res + tmp + a f(X(1), Y(2), 3) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Y,Some(term:Y/Y)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(b)) //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > <<< fusing <<< //│ = 16 fun c(x, d) = if x is X(a) then c(X(a), d) () => c(X(3), 2) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Ref(a,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Ref(a,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Ref(member:X,Some(term:X/X)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > <<< fusing <<< //│ = fun lambda_8$lambda :expect 2 fun a() = O fun b() = a() fun c() = b() fun p(x) = if x is O then 2 p(c()) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:O,Some(object:O)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 2 :sir :expect 3 if [1, 2] is [a, b] then a + b //│ deforest > >>> fusing >>> //│ deforest > Tuple(false,List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(2))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(tmp:scrut,None)), Arg(None,Lit(IntLit(0)))))) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(tmp:scrut,None)), Arg(None,Lit(IntLit(1)))))) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let scrut, a², b², element1$, element0$, $scrut_tup2⁰, $scrut_rest⁰, tup2_0, tup2_1; //│ define $scrut_tup2⁰ as fun $scrut_tup2¹(tup2_01, tup2_11) { //│ set element0$ = tup2_01; //│ set element1$ = tup2_11; //│ set b² = element1$; //│ set a² = element0$; //│ return +⁰(a², b²) //│ }; //│ define $scrut_rest⁰ as fun $scrut_rest¹() { //│ return null //│ }; //│ set tup2_0 = 1; //│ set tup2_1 = 2; //│ set scrut = () => { return $scrut_tup2¹(tup2_0, tup2_1) }; //│ scrut() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 :expect 3 fun f(x) = if x is [a,b] then a + b f([1,2]) //│ deforest > >>> fusing >>> //│ deforest > Tuple(false,List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(2))))) -> //│ deforest > Ref(x,None) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(x,None)), Arg(None,Lit(IntLit(0)))))) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(x,None)), Arg(None,Lit(IntLit(1)))))) //│ deforest > <<< fusing <<< //│ = 3 :expect 2 fun f(x) = if x is O then 2 f(id(O)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 2 :expect 0 let x = O fun f() = if x is O then 0 f() //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 0 //│ x = O :expect 1 fun f(x) = if x is A(x) then x f(A(1)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 1 :expect 2 fun f(x) = if x is A(a) then x.A#x + a f(A(1)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 2 :expect 4 fun f(x) = if x is A(x) then 2 f(A(2)) + f(A(3)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 4 :expect B(4) fun localFuse(x) = if A(x) is A(y) then B(y) if localFuse(3) is B(a) then a localFuse(4) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:B,Some(term:B/B)),List(List(Arg(None,Ref(y,None))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = B(4) :expect 3 fun g(x) = if x is A(a) then a fun f(x) = x.A#x let o = A(1) g(o) + f(o) + o.A#x //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 3 //│ o = A(1) :expect 1 let x = if A(0) is A then 1 let f() = x f() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > <<< fusing <<< //│ = 1 //│ f = fun f //│ x = 1 :expect B(0) fun inner(y, z) = if y is B then z fun dtor(x) = if x is A then inner(B(4), x.A#x) dtor(A(B(0))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:B,Some(term:B/B)),List(List(Arg(None,Lit(IntLit(4)))))) -> //│ deforest > Ref(y,None) //│ deforest > Call(Ref(member:B,Some(term:B/B)),List(List(Arg(None,Lit(IntLit(4)))))) -> //│ deforest > Ref(y,None) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = B(0) ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/clashes.mls ================================================ :js :deforest data class A(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< data class (::) Cons(h, t) object Nil //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :expect 10 fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is h :: t then h Nil then 2 fun f2(ls) = if ls is h :: t then h + 1 Nil then 3 fun badWrap(n) = let x = to(n) f1(x) + f2(x) x f1(badWrap(4)) + f2(badWrap(5)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 10 :expect 9 fun p(x) = A(x) let one = p(1) let two = p(2) fun f1(p) = if p is A(a) then a + 1 fun f2(p) = if p is A(b) then b + 2 f1(one) + f2(one) + f2(two) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 9 //│ one = A(1) //│ two = fun inlinedVal1 :expect 5 fun p(a) = a fun f1(a1) = if a1 is A(x1) then x1 fun f2(a2) = if a2 is A(x2) then x2 + 1 let x = A(1) let y = A(2) let res1 = p(if true then x else y) // both x and y flow into this call-site result let res2 = p(x) // x flows into this call-site result let res3 = p(y) // y flows into this call-site result // f1(res1) + f2(res1) + f1(res2) + f2(res3) f1(res1) + f1(res2) + f2(res3) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 5 //│ res1 = A(1) //│ res2 = A(1) //│ res3 = A(2) //│ x = A(1) //│ y = A(2) :expect 4 fun f1(a1) = if a1 is A(x1) then x1 fun f2(a2) = if a2 is A(x2) then x2 + 1 fun p(x, clashedCtor) = if true then A(x) else clashedCtor let clashed = A(3) f1(p(1, clashed)) + f2(p(2, clashed)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 4 //│ clashed = A(3) ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/cyclic.mls ================================================ :js data class A(x) data class B(x) data object C data object D data class (::) Cons(h, t) object Nil object None data class Some(x) :expect 0 fun f(x) = if x is A(_) then f(A(2)) else 0 f(Nil) //│ = 0 :expect 0 fun f(x) = let res = if x is A(x) then x if res > 0 then f(A(res - 1)) else 0 f(A(4)) //│ = 0 :expect 0 fun f(x, b) = if x is Nil then if b then f(Nil, false) else 0 f(Nil, false) //│ = 0 :deforest fun f(a) = if a is C then C else D (x => 0)(f(f(C))) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:D,Some(object:D)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(a,None) //│ deforest > <<< fusing <<< //│ = 0 :deforest :expect Some(2) fun maxOpt(ls) = if ls is Nil then None h :: t then if maxOpt(t) is None then Some(h) Some(m) then if m > h then Some(m) else Some(h) maxOpt(id(1 :: 2 :: Nil)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Some(2) :deforest :sir :expect Some(2) fun modSome(x, m) = if x is Some(n) and n >= m then modSome(Some(n - m), m) else Some(n) None then None modSome(Some(17), 3) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Some,Some(term:Some/Some)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:Some,Some(term:Some/Some)),List(List(Arg(None,Lit(IntLit(17)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let modSome⁰, tmp, modSome_12$modSome⁰, modSome_12$x_Some⁰, modSome_12$x_rest⁰, Some_x; //│ define modSome_12$modSome⁰ as fun modSome_12$modSome¹(x, m) { //│ let n, scrut, arg$Some$0$, tmp1, tmp2; //│ return x(m, n, scrut, arg$Some$0$, tmp1, tmp2) //│ }; //│ define modSome_12$x_Some⁰ as fun modSome_12$x_Some¹(fv_m, fv_n, fv_scrut, fv_arg$Some$0$, fv_tmp, fv_tmp1, Some_x1) { //│ set fv_arg$Some$0$ = Some_x1; //│ set fv_n = fv_arg$Some$0$; //│ set fv_scrut = >=⁰(fv_n, fv_m); //│ match fv_scrut //│ true => //│ let Some_x2; //│ set fv_tmp = -⁰(fv_n, fv_m); //│ set Some_x2 = fv_tmp; //│ set fv_tmp1 = (fv_ctorLam_m, fv_ctorLam_n, fv_ctorLam_scrut, fv_ctorLam_arg$Some$0$, fv_ctorLam_tmp, fv_ctorLam_tmp1) => { //│ return modSome_12$x_Some¹(fv_ctorLam_m, fv_ctorLam_n, fv_ctorLam_scrut, fv_ctorLam_arg$Some$0$, fv_ctorLam_tmp, fv_ctorLam_tmp1, Some_x2) //│ }; //│ return modSome_12$modSome¹(fv_tmp1, fv_m) //│ else //│ return Some⁰(fv_n) //│ end //│ }; //│ define modSome_12$x_rest⁰ as fun modSome_12$x_rest¹() { //│ return null //│ }; //│ define modSome⁰ as fun modSome¹(x, m) { //│ loop loopLabel: //│ let n, scrut, arg$Some$0$, tmp1, tmp2; //│ match x //│ Some¹ => //│ set arg$Some$0$ = x.x⁰; //│ set n = arg$Some$0$; //│ set scrut = >=⁰(n, m); //│ match scrut //│ true => //│ set tmp1 = -⁰(n, m); //│ set tmp2 = Some⁰(tmp1); //│ set x = tmp2; //│ continue loopLabel //│ else //│ return Some⁰(n) //│ end //│ None⁰ => //│ return None⁰ //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ end //│ }; //│ set Some_x = 17; //│ set tmp = (fv_ctorLam_m, fv_ctorLam_n, fv_ctorLam_scrut, fv_ctorLam_arg$Some$0$, fv_ctorLam_tmp, fv_ctorLam_tmp1) => { //│ return modSome_12$x_Some¹(fv_ctorLam_m, fv_ctorLam_n, fv_ctorLam_scrut, fv_ctorLam_arg$Some$0$, fv_ctorLam_tmp, fv_ctorLam_tmp1, Some_x) //│ }; //│ modSome_12$modSome¹(tmp, 3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Some(2) :deforest :expect Some(3) fun maxOpt2(ls, x) = if ls is Nil then x h :: t and x is None then maxOpt2(t, Some(h)) Some(m) and h > m then maxOpt2(t, Some(h)) else maxOpt2(t, Some(m)) maxOpt2(id(1 :: 2 :: 3 :: Nil), None) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Some(3) :deforest :expect Nil fun zipWith(xsys, f) = if xsys is [hx :: tx, hy :: ty] then f(hx, hy) :: zipWith([tx, ty], f) [Nil, Nil] then Nil zipWith([id(Nil), id(Nil)], (x, y) => x) //│ deforest > >>> fusing >>> //│ deforest > Tuple(false,List(Arg(None,Ref(tx,None)), Arg(None,Ref(ty,None)))) -> //│ deforest > Ref(xsys,None) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(xsys,None)), Arg(None,Lit(IntLit(0)))))) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(xsys,None)), Arg(None,Lit(IntLit(1)))))) //│ deforest > Tuple(false,List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None)))) -> //│ deforest > Ref(xsys,None) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(xsys,None)), Arg(None,Lit(IntLit(0)))))) //│ deforest > Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(xsys,None)), Arg(None,Lit(IntLit(1)))))) //│ deforest > <<< fusing <<< //│ = Nil ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/determinism.mls ================================================ :js data class A(a) data class B(a, b, c, d, e) :deforest :sir :expect 0 if B(1,2,3,4,5) is B(a,b,c,d,e) then 0 //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:B,Some(term:B/B)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(2))), Arg(None,Lit(IntLit(3))), Arg(None,Lit(IntLit(4))), Arg(None,Lit(IntLit(5)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(a)) //│ deforest > Select(Ref(tmp:scrut,None),Ident(b)) //│ deforest > Select(Ref(tmp:scrut,None),Ident(c)) //│ deforest > Select(Ref(tmp:scrut,None),Ident(d)) //│ deforest > Select(Ref(tmp:scrut,None),Ident(e)) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let scrut, e⁰, a⁰, b⁰, c⁰, d⁰, arg$B$0$, arg$B$1$, arg$B$2$, arg$B$3$, arg$B$4$, $scrut_B⁰, $scrut_rest⁰, B_a, B_b, B_c, B_d, B_e; //│ define $scrut_B⁰ as fun $scrut_B¹(B_a1, B_b1, B_c1, B_d1, B_e1) { //│ set arg$B$0$ = B_a1; //│ set arg$B$1$ = B_b1; //│ set arg$B$2$ = B_c1; //│ set arg$B$3$ = B_d1; //│ set arg$B$4$ = B_e1; //│ set e⁰ = arg$B$4$; //│ set d⁰ = arg$B$3$; //│ set c⁰ = arg$B$2$; //│ set b⁰ = arg$B$1$; //│ set a⁰ = arg$B$0$; //│ return 0 //│ }; //│ define $scrut_rest⁰ as fun $scrut_rest¹() { //│ return null //│ }; //│ set B_a = 1; //│ set B_b = 2; //│ set B_c = 3; //│ set B_d = 4; //│ set B_e = 5; //│ set scrut = () => { return $scrut_B¹(B_a, B_b, B_c, B_d, B_e) }; //│ scrut() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :sir :deforest :expect 1 fun c2(x) = if x is A(A(A(A(A(A(a)))))) then a c2 of A(A(A(A(A(A(1)))))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp:arg$A$0$,None) //│ deforest > Select(Ref(tmp:arg$A$0$,None),Ident(a)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp:arg$A$0$,None) //│ deforest > Select(Ref(tmp:arg$A$0$,None),Ident(a)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp:arg$A$0$,None) //│ deforest > Select(Ref(tmp:arg$A$0$,None),Ident(a)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp:arg$A$0$,None) //│ deforest > Select(Ref(tmp:arg$A$0$,None),Ident(a)) //│ deforest > Call(Ref(member:A,Some(term:A/A)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:arg$A$0$,None) //│ deforest > Select(Ref(tmp:arg$A$0$,None),Ident(a)) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let c2⁰, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, c2_21$c2⁰, c2_21$x_A⁰, c2_21$x_rest⁰, c2_21$split_default$_rest⁰, c2_21$split_root$_rest⁰, c2_21$arg$A$0$_A⁰, c2_21$arg$A$0$_rest⁰, c2_21$arg$A$0$_A¹, c2_21$arg$A$0$_rest¹, c2_21$arg$A$0$_A², c2_21$arg$A$0$_rest², c2_21$arg$A$0$_A³, c2_21$arg$A$0$_rest³, c2_21$arg$A$0$_A⁴, c2_21$arg$A$0$_rest⁴, A_a, A_a1, A_a2, A_a3, A_a4, A_a5; //│ define c2_21$c2⁰ as fun c2_21$c2¹(x) { //│ let a, arg$A$0$, arg$A$0$1, arg$A$0$2, arg$A$0$3, arg$A$0$4, arg$A$0$5; //│ block split_root$: //│ block split_default$: //│ return x(a, arg$A$0$, arg$A$0$1, arg$A$0$2, arg$A$0$3, arg$A$0$4, arg$A$0$5) //│ throw new globalThis⁰.Error⁰("match error") //│ return runtime⁰.Unit⁰ //│ }; //│ define c2_21$x_A⁰ as fun c2_21$x_A¹(fv_a, fv_arg$A$0$, fv_arg$A$0$1, fv_arg$A$0$2, fv_arg$A$0$3, fv_arg$A$0$4, fv_arg$A$0$5, A_a6) { //│ set fv_arg$A$0$ = A_a6; //│ return fv_arg$A$0$(fv_a, fv_arg$A$0$1, fv_arg$A$0$2, fv_arg$A$0$3, fv_arg$A$0$4, fv_arg$A$0$5) //│ }; //│ define c2_21$arg$A$0$_A⁰ as fun c2_21$arg$A$0$_A⁵(fv_a, fv_arg$A$0$, fv_arg$A$0$1, fv_arg$A$0$2, fv_arg$A$0$3, fv_arg$A$0$4, A_a6) { //│ set fv_arg$A$0$ = A_a6; //│ return fv_arg$A$0$(fv_a, fv_arg$A$0$1, fv_arg$A$0$2, fv_arg$A$0$3, fv_arg$A$0$4) //│ }; //│ define c2_21$arg$A$0$_A¹ as fun c2_21$arg$A$0$_A⁶(fv_a, fv_arg$A$0$, fv_arg$A$0$1, fv_arg$A$0$2, fv_arg$A$0$3, A_a6) { //│ set fv_arg$A$0$ = A_a6; //│ return fv_arg$A$0$(fv_a, fv_arg$A$0$1, fv_arg$A$0$2, fv_arg$A$0$3) //│ }; //│ define c2_21$arg$A$0$_A² as fun c2_21$arg$A$0$_A⁷(fv_a, fv_arg$A$0$, fv_arg$A$0$1, fv_arg$A$0$2, A_a6) { //│ set fv_arg$A$0$ = A_a6; //│ return fv_arg$A$0$(fv_a, fv_arg$A$0$1, fv_arg$A$0$2) //│ }; //│ define c2_21$arg$A$0$_A³ as fun c2_21$arg$A$0$_A⁸(fv_a, fv_arg$A$0$, fv_arg$A$0$1, A_a6) { //│ set fv_arg$A$0$ = A_a6; //│ return fv_arg$A$0$(fv_a, fv_arg$A$0$1) //│ }; //│ define c2_21$arg$A$0$_A⁴ as fun c2_21$arg$A$0$_A⁹(fv_a, fv_arg$A$0$, A_a6) { //│ set fv_arg$A$0$ = A_a6; //│ set fv_a = fv_arg$A$0$; //│ return fv_a //│ }; //│ define c2_21$x_rest⁰ as fun c2_21$x_rest¹() { //│ return c2_21$split_default$_rest¹() //│ }; //│ define c2_21$split_default$_rest⁰ as fun c2_21$split_default$_rest¹() { //│ throw new globalThis⁰.Error⁰("match error") //│ }; //│ define c2_21$split_root$_rest⁰ as fun c2_21$split_root$_rest¹() { //│ return runtime⁰.Unit⁰ //│ }; //│ define c2_21$arg$A$0$_rest⁰ as fun c2_21$arg$A$0$_rest⁵() { //│ return c2_21$x_rest¹() //│ }; //│ define c2_21$arg$A$0$_rest¹ as fun c2_21$arg$A$0$_rest⁶() { //│ return c2_21$arg$A$0$_rest⁵() //│ }; //│ define c2_21$arg$A$0$_rest² as fun c2_21$arg$A$0$_rest⁷() { //│ return c2_21$arg$A$0$_rest⁶() //│ }; //│ define c2_21$arg$A$0$_rest³ as fun c2_21$arg$A$0$_rest⁸() { //│ return c2_21$arg$A$0$_rest⁷() //│ }; //│ define c2_21$arg$A$0$_rest⁴ as fun c2_21$arg$A$0$_rest⁹() { //│ return c2_21$arg$A$0$_rest⁸() //│ }; //│ define c2⁰ as fun c2¹(x) { //│ let a, arg$A$0$, arg$A$0$1, arg$A$0$2, arg$A$0$3, arg$A$0$4, arg$A$0$5; //│ block split_root$: //│ block split_default$: //│ match x //│ A⁰ => //│ set arg$A$0$ = x.a¹; //│ match arg$A$0$ //│ A⁰ => //│ set arg$A$0$1 = arg$A$0$.a¹; //│ match arg$A$0$1 //│ A⁰ => //│ set arg$A$0$2 = arg$A$0$1.a¹; //│ match arg$A$0$2 //│ A⁰ => //│ set arg$A$0$3 = arg$A$0$2.a¹; //│ match arg$A$0$3 //│ A⁰ => //│ set arg$A$0$4 = arg$A$0$3.a¹; //│ match arg$A$0$4 //│ A⁰ => //│ set arg$A$0$5 = arg$A$0$4.a¹; //│ set a = arg$A$0$5; //│ return a //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ throw new globalThis⁰.Error⁰("match error") //│ return runtime⁰.Unit⁰ //│ }; //│ set A_a = 1; //│ set tmp = (fv_ctorLam_a, fv_ctorLam_arg$A$0$) => { //│ return c2_21$arg$A$0$_A⁹(fv_ctorLam_a, fv_ctorLam_arg$A$0$, A_a) //│ }; //│ set A_a1 = tmp; //│ set tmp1 = (fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1) => { //│ return c2_21$arg$A$0$_A⁸(fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, A_a1) //│ }; //│ set A_a2 = tmp1; //│ set tmp2 = (fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2) => { //│ return c2_21$arg$A$0$_A⁷(fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, A_a2) //│ }; //│ set A_a3 = tmp2; //│ set tmp3 = (fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, fv_ctorLam_arg$A$0$3) => { //│ return c2_21$arg$A$0$_A⁶(fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, fv_ctorLam_arg$A$0$3, A_a3) //│ }; //│ set A_a4 = tmp3; //│ set tmp4 = (fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, fv_ctorLam_arg$A$0$3, fv_ctorLam_arg$A$0$4) => { //│ return c2_21$arg$A$0$_A⁵(fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, fv_ctorLam_arg$A$0$3, fv_ctorLam_arg$A$0$4, A_a4) //│ }; //│ set A_a5 = tmp4; //│ set tmp5 = (fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, fv_ctorLam_arg$A$0$3, fv_ctorLam_arg$A$0$4, fv_ctorLam_arg$A$0$5) => { //│ return c2_21$x_A¹(fv_ctorLam_a, fv_ctorLam_arg$A$0$, fv_ctorLam_arg$A$0$1, fv_ctorLam_arg$A$0$2, fv_ctorLam_arg$A$0$3, fv_ctorLam_arg$A$0$4, fv_ctorLam_arg$A$0$5, A_a5) //│ }; //│ c2_21$c2¹(tmp5) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/dropLast.mls ================================================ :js data class (::) Cons(h, t) data object Nil fun lseq(l1, l2) = if l1 is Nil and l2 is Nil then true l1 is x :: xs and l2 is y :: ys and x == y then lseq(xs, ys) else false :deforest fun dl(ls) = if ls is Nil then Nil x :: Nil then Nil x :: xs then x :: dl(xs) fun fix(x) = let res = dl(x) if lseq(res, x) then res else fix(res) fix //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = fun fix :deforest fun dl(ls) = if ls is Nil then Nil x :: Nil then Nil x :: xs then x :: dl(xs) fun fix(x, fuel) = let res = dl(x) if fuel == 0 then res else fix(res, fuel - 1) fix //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = fun fix :deforest fun dl(ls) = if ls is Nil then Nil x :: xs then dlHelper(x, xs) fun dlHelper(prev, rest) = if rest is Nil then Nil h :: t then prev :: dlHelper(h, t) fun fix(x, fuel) = let res = dl(x) if fuel == 0 then res else fix(res, fuel - 1) fix //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = fun fix :deforest :expect Nil fun dlHelper(prev, rest) = if rest is Nil then Nil h :: t then prev :: dlHelper(h, t) fun fix(x, fuel) = let res = dlHelper(-1, x) if fuel == 0 then res else fix(res, fuel - 1) id(fix(Nil, 1000)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Nil :deforest :expect Nil fun dlHelper(prev, rest) = if rest is Nil then Nil h :: t then prev :: dlHelper(h, t) fun fix(x) = if x is Nil then Nil x :: xs then fix(dlHelper(x, xs)) fix(2 :: 3 :: Nil) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Nil :deforest :expect Nil fun f(x) = if x is Nil then Nil fun loop(l, n) = if n > 0 then loop(f(l), n - 1) else l id(loop(Nil, 2)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Nil ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/floatoutSafety.mls ================================================ :js data class (::) Cons(h, t) data object Nil data class Many(x) data class Once(x) fun expensive(x) = x // we don't want to fuse `p = enumFrom(3)`, // because the `expensive` computation would be duplicated // btw, should `p` be considered as being in the correct position here? :deforest fun sum(x) = if x is h :: t then h + sum(t) Nil then 0 fun enumFrom(a) = if a > 0 then expensive(a) :: enumFrom(a - 1) else Nil fun main() = let p = enumFrom(3) let f = () => sum(p) f() + f() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(h)) //│ deforest > Select(Ref(x,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< // `Many` cannot be fused because: // - it's not one-shot // - it has a clash :deforest :expect 13 fun consumer(f, g, p) = f(p) + g(p) consumer of case Many(_) then 3 case Many(Once(v)) then v Many(Once(10)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Once,Some(term:Once/Once)),List(List(Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(tmp:arg$Many$0$,None) //│ deforest > Select(Ref(tmp:arg$Many$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 13 // we don't actually want to fuse it, because the logic of // the creation of the list may be expensive :deforest :expect 12 fun enumFrom(a) = if a > 0 then expensive(a) :: enumFrom(a - 1) else Nil fun sum(x) = if x is h :: t then h + sum(t) Nil then 0 fun consume(f, ls) = f(ls) + f(ls) consume(sum, enumFrom(3)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(h)) //│ deforest > Select(Ref(x,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 12 data class Outter(x) data class Inner(x) // nested ctors may or may not be fusible. it depends on what the consumer // does with the corresponding field. The outer and inner ctors can differ in: // - strategy clash (outer has none, but inner does) // - linearity (outer is one-shot, but inner is not) // - tail-modulo-fusion position // these causes can also interact (between different levels of ctors). :deforest :expect 22 fun prod(x) = Outter(Inner(x)) fun cons(f, p) = if p is Outter(i) then f(i) + f(i) cons of case Inner(a) then a + 1 prod(10) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Outter,Some(term:Outter/Outter)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(x)) //│ deforest > Call(Ref(member:Inner,Some(term:Inner/Inner)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(caseScrut,None) //│ deforest > Select(Ref(caseScrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 22 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/imperative.mls ================================================ :js :deforest object A object B // object C // class AA(aa) // class BB(bb) // object None // class Some(value) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< // * Not fused: two `x is A` consumers let x = if true then A else B fun foo(x) = if x is A do print(123) if x is A then 1 B then 2 //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ x = A // * We could make it work. But it's a special case that's probably not very important let x = if true then A else B fun foo(x) = if x is A do print(123) if x is B do print(456) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ x = A :expect 1 fun foo(k, x) = if x === 0 do k(A) k of if x > 0 then A else B fun bar(v) = if v is A then 1 B then 2 foo(bar, 123) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(v,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(v,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(v,None) //│ deforest > <<< fusing <<< //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls ================================================ :js :deforest object Nil data class (::) Cons(h, t) data class Pair(a, b) object A object B //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :expect Cons(Pair(1, 3), Cons(Pair(2, 4), Cons(Pair(3, 5), Nil))) fun zip(xs_zip, ys_zip) = if xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun enumFromTo(a, b) = if a < (b + 1) then a :: enumFromTo(a + 1, b) else Nil fun lscomp1(ls) = if ls is Pair(x, y1) :: t then fun lscomp2(ls2) = if ls2 is Pair(y2, z) :: t2 and y1 == y2 then Pair(x, z) :: lscomp2(t2) else lscomp2(t2) else lscomp1(t) lscomp2(zip(enumFromTo(x, x + 2), enumFromTo(y1, y1 + 1))) else Nil lscomp1(zip(enumFromTo(1, 3), enumFromTo(2, 4))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls2,None) //│ deforest > Select(Ref(ls2,None),Ident(h)) //│ deforest > Select(Ref(ls2,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(y,None))))) -> //│ deforest > Ref(tmp:arg$Cons$0$,None) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(a)) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls2,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls2,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls2,None) //│ deforest > Select(Ref(ls2,None),Ident(h)) //│ deforest > Select(Ref(ls2,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(y,None))))) -> //│ deforest > Ref(tmp:arg$Cons$0$,None) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(a)) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls2,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls2,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(y,None))))) -> //│ deforest > Ref(tmp:arg$Cons$0$,None) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(a)) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(Pair(1, 3), Cons(Pair(2, 4), Cons(Pair(3, 5), Nil))) :expect Cons(Pair(5, 1), Cons(Pair(5, 2), Cons(Pair(5, 5), Nil))) fun append(xs, ys) = if xs is h :: t then h :: append(t, ys) Nil then ys fun concatMap(f, ls) = if ls is h :: t then append(f(h), concatMap(f, t)) Nil then Nil fun f1(a1) = if a1 is Pair(a, b) then fun f2(a2) = if a2 is Pair(c, d) then Pair(a, c) :: Nil else Nil concatMap(f2, Pair(1, 3) :: Pair(2, 3) :: Pair(a, b) :: Nil) else Nil concatMap(f1, Pair(5, 10) :: Nil) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(a)) //│ deforest > Select(Ref(a2,None),Ident(b)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(a)) //│ deforest > Select(Ref(a2,None),Ident(b)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(b,None))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(a)) //│ deforest > Select(Ref(a2,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(a)) //│ deforest > Select(Ref(a2,None),Ident(b)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(a)) //│ deforest > Select(Ref(a2,None),Ident(b)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(b,None))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(a)) //│ deforest > Select(Ref(a2,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(h,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(5))), Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(a1,None) //│ deforest > Select(Ref(a1,None),Ident(a)) //│ deforest > Select(Ref(a1,None),Ident(b)) //│ deforest > <<< fusing <<< //│ = Cons(Pair(5, 1), Cons(Pair(5, 2), Cons(Pair(5, 5), Nil))) :expect Cons(Pair(1, 3), Cons(Pair(1, 4), Nil)) fun lscomp1(ls1, ls2) = if ls1 is h1 :: t1 then lscomp2(ls2, h1, t1) Nil then Nil fun lscomp2(ls2, h1, t1) = if ls2 is h2 :: t2 then Pair(h1, h2) :: lscomp2(t2, h1, t1) Nil then lscomp1(t1, ls2) lscomp1(1 :: 2 :: Nil, 3 :: 4 :: Nil) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls2,None) //│ deforest > Select(Ref(ls2,None),Ident(h)) //│ deforest > Select(Ref(ls2,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(4))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls2,None) //│ deforest > Select(Ref(ls2,None),Ident(h)) //│ deforest > Select(Ref(ls2,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls2,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls1,None) //│ deforest > Select(Ref(ls1,None),Ident(h)) //│ deforest > Select(Ref(ls1,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls1,None) //│ deforest > Select(Ref(ls1,None),Ident(h)) //│ deforest > Select(Ref(ls1,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls1,None) //│ deforest > <<< fusing <<< //│ = Cons(Pair(1, 3), Cons(Pair(1, 4), Nil)) :expect Cons(2, Cons(3, Cons(4, Nil))) fun lscomp(ls) = if ls is h :: t then (h + 1) :: lscomp(t) Nil then Nil if A is A then lscomp(1 :: 2 :: 3 :: Nil) B then Nil //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > <<< fusing <<< //│ = Cons(2, Cons(3, Cons(4, Nil))) :lift :deforest fun h() = fun lscomp1(ls) = if ls is Nil then Nil x :: xs then fun lscomp2(ls) = if ls is Nil then lscomp1(xs) y :: ys then [x, y] :: lscomp2(ys) lscomp2(1 :: 2 :: 3 :: Nil) lscomp1(1 :: 2 :: 3 :: Nil) h() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = Cons( //│ [1, 1], //│ Cons( //│ [1, 2], //│ Cons( //│ [1, 3], //│ Cons( //│ [2, 1], //│ Cons( //│ [2, 2], //│ Cons([2, 3], Cons([3, 1], Cons([3, 2], Cons([3, 3], Nil)))) //│ ) //│ ) //│ ) //│ ) //│ ) ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/module.mls ================================================ :js :deforest :expect 123 module M with data class XX(a) data class YY(b) data class ZZ(c) data object OO fun h() = 123 fun p() = XX(YY(OO)) fun f(x) = if x is XX(YY(y)) then h() M.f(M.p()) //│ deforest > >>> fusing >>> //│ deforest > Call(Select(Ref(module:M,None),Ident(XX)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(a)) //│ deforest > Call(Select(Ref(module:M,None),Ident(YY)),List(List(Arg(None,Select(Ref(module:M,None),Ident(OO)))))) -> //│ deforest > Ref(tmp:arg$XX$0$,None) //│ deforest > Select(Ref(tmp:arg$XX$0$,None),Ident(b)) //│ deforest > <<< fusing <<< //│ = 123 :deforest :expect 5056 module DeforestList with data object Nil data class (::) Cons(h, t) fun sum(ls) = if ls is Nil then 0 h :: t then h + sum(t) fun enum(n) = if n > 0 then n :: enum(n - 1) else Nil fun main() = DeforestList.sum(DeforestList.enum(3)) val res = DeforestList.sum(DeforestList.enum(100)) DeforestList.res + DeforestList.main() //│ deforest > >>> fusing >>> //│ deforest > Call(Select(Ref(module:DeforestList,None),Ident(Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Select(Ref(module:DeforestList,None),Ident(Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Select(Ref(module:DeforestList,None),Ident(Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Select(Ref(module:DeforestList,None),Ident(Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(module:DeforestList,None),Ident(Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Select(Ref(module:DeforestList,None),Ident(Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 5056 :deforest :expect 5056 module DeforestList with data object Nil data class (::) Cons(h, t) fun sum(ls) = fun helper(ls, acc) = if ls is Nil then acc h :: t then helper(t, acc + h) helper(ls, 0) fun enum(n) = if n > 0 then n :: enum(n - 1) else Nil fun main() = DeforestList.sum(DeforestList.enum(3)) val res = DeforestList.sum(DeforestList.enum(100)) DeforestList.res + DeforestList.main() //│ deforest > >>> fusing >>> //│ deforest > Call(Select(Ref(module:DeforestList,None),Ident(Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Select(Ref(module:DeforestList,None),Ident(Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Select(Ref(module:DeforestList,None),Ident(Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Select(Ref(module:DeforestList,None),Ident(Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(module:DeforestList,None),Ident(Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Select(Ref(module:DeforestList,None),Ident(Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 5056 :deforest :expect 3 module MMM with data object A val y = 3 val x = MMM.f() fun f() = if A is A then y MMM.f() //│ deforest > >>> fusing >>> //│ deforest > Select(Ref(module:MMM,None),Ident(A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(module:MMM,None),Ident(A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(module:MMM,None),Ident(A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > <<< fusing <<< //│ = 3 :deforest :expect X module M with data object X data object Y val x = X val y = Y fun f(k) = if k is X then if y is Y then x M.f(M.X) //│ deforest > >>> fusing >>> //│ deforest > Select(Ref(member:M,Some(module:M)),Ident(X)) -> //│ deforest > Ref(k,None) //│ deforest > <<< fusing <<< //│ = X :deforest :expect [D, A] data object A data class B(b) data object C data object D module M with fun f(x) = if M.g(false) is A and x < 1 then B(0) else M.f(x - 1) C then D fun g(b) = if b then if M.f(0) is B then A D then A else C [M.f(3), M.g(true)] //│ deforest > >>> fusing >>> //│ deforest > Ref(member:D,Some(object:D)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:B,Some(term:B/B)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:D,Some(object:D)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:B,Some(term:B/B)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > <<< fusing <<< //│ = [D, A] ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/multiArgLists.mls ================================================ :js :deforest // Test: Deforestation with function of multiple parameter lists object A data class Pair(x, y) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< fun test(a)(b) = if a is Pair(x, y) then x + y + b test(Pair(1, 2))(3) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > Select(Ref(a,None),Ident(y)) //│ deforest > <<< fusing <<< //│ = 6 // Test: Deforestation with nested constructor calls and multiple param lists data class Box(v) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< fun unbox(b)(offset) = if b is Box(v) then v + offset unbox(Box(10))(5) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Box,Some(term:Box/Box)),List(List(Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(b,None) //│ deforest > Select(Ref(b,None),Ident(v)) //│ deforest > <<< fusing <<< //│ = 15 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/nestedMatch.mls ================================================ :js :deforest object A object B object C data class AA(x) data class BB(x) data class CC(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< object Nil data class (::) Cons(h, t) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< object None data class Some(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :expect 37 fun f(x, y) = let t = if x is AA(a) then let m = if a is AA(x) then x m + 9 BB(b) then b t + y fun g(x) = if x is AA then 0 let a = AA(AA(3)) f(a, 2) + g(a) + f(BB(3), 2) + f(AA(AA(4)), 5) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(4)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 37 //│ a = AA(fun tmp) :expect 30 fun f(x, y) = let n = if x is AA(a) then if a is AA(m) then m n + 2 + y f(AA(AA(3)), 9) + f(AA(AA(4)), 10) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(4)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 30 :expect 5 fun f(x) = let n = if x is AA(a) then if a is AA(m) then m n + 2 f(AA(AA(3))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 5 :expect "A3A3" fun f(x) = let t = if x is AA(AA(a)) then a t + 3 f(AA(AA(A))) + f(AA(AA(A))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = "A3A3" :expect 0 fun c2(x) = if x is AA(AA(a)) then a c2(AA(AA(0))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 0 :expect 3 fun f(a) = if a is AA(BB(B)) then 3 f(AA(BB(B))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(member:B,Some(object:B)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(tmp:arg$BB$0$,None) //│ deforest > <<< fusing <<< //│ = 3 :expect 10 fun test(x, y, z, i) = if x is AA(a) then let m = if y is AA(a1) then a1 + i let n = if z is BB(a2) then a2 - i m + n test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(z,None) //│ deforest > Select(Ref(z,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(z,None) //│ deforest > Select(Ref(z,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 10 // only the match in the middle is fused :expect "ccf2f3cc" fun f1(x) = if x is AA(x1) then if x1 is BB(x2) then if x2 is CC(x3) then x3 fun f2(x) = if x is AA then "f2" fun f3(x) = if x is CC then "f3" + x.CC#x fun aa(x) = AA(x) let cc = CC("cc") f1(aa(BB(cc))) + f2(aa(BB(CC(0)))) + f3(cc) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(cc,None))))) -> //│ deforest > Ref(x1,None) //│ deforest > Select(Ref(x1,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = "ccf2f3cc" //│ cc = CC("cc") :expect 4 fun c(x, y) = if x is AA then let t = if y is A then 2 t + x.AA#x let y = A c(AA(2), y) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(y,None) //│ deforest > <<< fusing <<< //│ = 4 //│ y = fun y7 :expect 9 fun c(x, y) = if x is AA then let t = if y is A then 2 + x.AA#x t + x.AA#x fun c2(y) = if y is A then 3 let y = A c(AA(2), y) + c2(y) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 9 //│ y = A // need to include computations of `rest` from more than one levels of parent matches :expect "105035" fun test(x) = let t = if x is AA(AA(AA(a))) then a AA then "3" t + "5" fun f(a) = if a is AA(AA) then "0" let p = AA(AA(AA("10"))) test(p) + f(p) + test(AA(A)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(StrLit(10)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = "105035" //│ p = AA(AA(fun tmp49)) :expect 13 fun f(x, y) = let tmp = BB(y + 1) if tmp is BB then let m = if x is AA then if AA(2) is AA(yf) then yf if AA(3) is AA then m + tmp.BB#x let aa = AA(3) f(aa, 3) + f(aa, 4) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp,None) //│ deforest > Select(Ref(tmp,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp,None) //│ deforest > Select(Ref(tmp,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp,None) //│ deforest > Select(Ref(tmp,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > <<< fusing <<< //│ = 13 //│ aa = AA(3) :expect 16 fun test(n, p, q) = n + if AA(0) is AA then let k = if p is AA(x) then x if q is AA(y) then k + y test(3, AA(2), AA(3)) + test(3, AA(2), AA(3)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(q,None) //│ deforest > Select(Ref(q,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(q,None) //│ deforest > Select(Ref(q,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 16 :expect 13 fun test(n, p, q, a) = let o if a is A then let k if p is AA(x) then k = x if q is AA(y) then o = k + y o + n let a = A fun c(a) = if a is A then 0 test(1, AA(2), AA(3), a) + test(2, AA(2), AA(3), a) + c(a) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(q,None) //│ deforest > Select(Ref(q,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(q,None) //│ deforest > Select(Ref(q,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 13 //│ a = A :expect 18 fun test(x, y, z, i) = let k = if x is AA(a) then let m = if y is AA(a1) then a1 + i let n = if z is BB(a2) then a2 - i m + n k + i test(AA(1), AA(2), BB(3), 4) + test(AA(1), AA(2), BB(3), 4) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(z,None) //│ deforest > Select(Ref(z,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(z,None) //│ deforest > Select(Ref(z,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 18 :expect 10 fun test(x, y, z, i) = let k = if x is AA(a) then let m = if y is AA(a1) then a1 + i BB(b2) then b2 - i let n = if z is BB(a2) then a2 - i m + n k + i test(AA(1), AA(2), BB(3), 4) + test(AA(1), BB(2), BB(3), 4) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(z,None) //│ deforest > Select(Ref(z,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(z,None) //│ deforest > Select(Ref(z,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 10 :expect 12 fun test(x) = let n = if x is AA(BB(b)) then b AA(CC(c)) then c else 0 n + 3 fun c(x) = if x is AA then 0 fun p(x) = AA(x) test(p(BB(3))) + test(p(CC(3))) + c(p(A)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:CC,Some(term:CC/CC)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 12 :expect 41 fun f(x, y, z, k) = let tmp = if z then BB(y + 1) else BB(y - 1) tmp.BB#x + if tmp is BB then let m = if x is AA then if AA(2) is AA(yf) then yf if AA(3) is AA then m + k f(AA(3), 3, true, 10) + f(AA(5), 4, false, 20) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(5)))))) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 41 :expect 15 fun test(x) = let t = if x is AA(AA(AA(a))) then a t + 5 fun f(a) = if a is AA(AA) then 0 let p = AA(AA(AA(10))) test(p) + f(p) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 15 //│ p = AA(AA(fun tmp114)) :expect "()()" fun test(a, b) = let x if a is A then print("A") B then x = 1 if b is A then x = 2 B then print("B") print(x) test(B, B) + test(A, A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(b,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(b,None) //│ deforest > <<< fusing <<< //│ > B //│ > 1 //│ > A //│ > 2 //│ = "()()" // this misalign is fine :deforest :expect 20 fun prodDouble(x) = if x is 0 then Nil n then n :: n :: prodDouble(n - 1) fun sum(ls) = if ls is Nil then 0 h :: t then h + sum(t) sum(prodDouble(4)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = 20 // this misalign prevents fusion (consumer destructures 2 elements per step, producer emits 1) :deforest :expect 10 fun prod(x) = if x is 0 then Nil n then n :: prod(n - 1) fun cons(ls) = if ls is h1 :: h2 :: t then h1 + h2 + cons(t) _ then 0 cons(prod(4)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 10 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/recursive.mls ================================================ :js :deforest object Nil data class (::) Cons(h, t) object None data class Some(v) object A object B data class T(n, l, r) object L //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< // :sir fun map(f, ls_map) = if ls_map is h :: t then f(h) :: map(f, t) Nil then Nil let c = Cons(1, Nil) map(x => x + 1, c) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls_map,None) //│ deforest > Select(Ref(ls_map,None),Ident(h)) //│ deforest > Select(Ref(ls_map,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map,None) //│ deforest > <<< fusing <<< //│ = Cons(2, Nil) //│ c = fun c // :sir fun map(f, ls_map) = if ls_map is h :: t then f(h) :: map(f, t) Nil then Nil fun c(a) = Cons(a, Nil) map(x => x + 1, c(1)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls_map,None) //│ deforest > Select(Ref(ls_map,None),Ident(h)) //│ deforest > Select(Ref(ls_map,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map,None) //│ deforest > <<< fusing <<< //│ = Cons(2, Nil) :expect Cons(2, Cons(3, Nil)) fun map(ls, f) = if ls is Nil then Nil h :: t then f(h) :: map(t, f) map(1 :: 2 :: Nil, x => x + 1) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = Cons(2, Cons(3, Nil)) :expect 6 fun sum(ls) = if ls is Nil then 0 h :: t then h + sum(t) fun enum(n) = if n > 0 then n :: enum(n - 1) else Nil sum(enum(3)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 6 :expect Cons(4, Cons(6, Nil)) fun map(ls, f) = if ls is Nil then Nil h :: t then f(h) :: map(t, f) map(map(id(1 :: 2 :: Nil), x => x + 1), x => x * 2) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = Cons(4, Cons(6, Nil)) :expect Cons(7, Cons(5, Cons(3, Cons(1, Nil)))) fun map(f, ls) = if ls is Nil then Nil h :: t then f(h) :: map(f, t) fun enum(a) = if a < 0 then Nil else a :: enum(a - 1) map(x => x + 1, map(x => x * 2, id(enum(3)))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = Cons(7, Cons(5, Cons(3, Cons(1, Nil)))) :expect 2 fun f(x) = if x == 0 then Nil else x :: g(x - 1) fun g(y) = if y == 0 then Nil else y :: f(y - 1) fun cons(a) = if a is Nil then 0 h :: t then h + cons(t) fun cons1(a1) = if a1 is Nil then 1 h :: t then h + cons1(t) cons(f(0)) + cons1(f(1)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(a1,None) //│ deforest > Select(Ref(a1,None),Ident(h)) //│ deforest > Select(Ref(a1,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(y,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(a1,None) //│ deforest > Select(Ref(a1,None),Ident(h)) //│ deforest > Select(Ref(a1,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(a1,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(a1,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(h)) //│ deforest > Select(Ref(a,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(y,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(h)) //│ deforest > Select(Ref(a,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(a,None) //│ deforest > <<< fusing <<< //│ = 2 :expect 12 fun to(n) = if n > 0 then let m = n - 1 n :: to(m) else Nil fun f1(ls1) = if ls1 is h :: t then h + f1(t) Nil then 2 f1(to(4)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls1,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls1,None) //│ deforest > Select(Ref(ls1,None),Ident(h)) //│ deforest > Select(Ref(ls1,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 12 :expect Cons(4, Cons(6, Nil)) fun map(ls, f) = if ls is Nil then Nil h :: t then f(h) :: map(t, f) fun succ(x) = x + 1 fun double(x) = x * 2 map(map(1 :: 2 :: Nil, succ), double) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = Cons(4, Cons(6, Nil)) :expect 10 fun to(n) = if n > 0 then n :: to(n - 1) else Nil fun f1(ls) = if ls is h :: t then h Nil then 2 fun f2(ls) = if ls is h :: t then h + 1 Nil then 3 fun wrap(n) = to(n) f1(wrap(4)) + f2(wrap(5)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = 10 :expect 51 fun to(n, acc) = if n == 0 then acc else to(n - 1, n :: acc) fun f1(ls, acc) = if ls is Nil then acc h :: t then f1(t, acc + h) fun f2(ls, acc) = if ls is Nil then acc h :: t then f2(t, acc + h + 1) f1(to(4, Nil), 0) + f2(to(5, Nil), 0) + f1(to(6, Nil), 0) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(acc,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(acc,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(acc,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 51 :expect Cons(12, Cons(18, Nil)) fun map(ls, f) = if ls is Nil then Nil h :: t then f(h) :: map(t, f) fun succ(x) = x + 1 fun double(x) = x * 2 fun triple(x) = x * 3 map(map(map(1 :: 2 :: Nil, succ), double), triple) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = Cons(12, Cons(18, Nil)) :expect Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun map(f, ls_map) = if ls_map is h :: t then f(h) :: map(f, t) Nil then Nil fun map1(f, ls_map1) = if ls_map1 is h :: t then f(h) :: map1(f, t) Nil then Nil fun incr(x) = x + 1 fun double(x) = x * 2 map1(incr, map(double, id(mk(5)))) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map1,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls_map1,None) //│ deforest > Select(Ref(ls_map1,None),Ident(h)) //│ deforest > Select(Ref(ls_map1,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(11, Cons(9, Cons(7, Cons(5, Cons(3, Cons(1, Nil)))))) :expect Cons(5, Cons(6, Cons(7, Nil))) fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun map(ls) = if ls is Nil then Nil h :: t then (h + 4) :: map(t) map(enumFromTo(1, 4)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(5, Cons(6, Cons(7, Nil))) :expect 45 fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun sum(ls) = if ls is Nil then 0 h :: t then h + sum(t) sum(enumFromTo(1,10)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 45 :expect Cons(5, Cons(6, Cons(7, Nil))) fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun map(f, ls) = if ls is Nil then Nil h :: t then f(h) :: map(f, t) map(x => x + 4, enumFromTo(1, 4)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(5, Cons(6, Cons(7, Nil))) :expect Cons(5, Cons(6, Cons(7, Nil))) fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun map(f, ls) = (if ls is Nil then f => Nil h :: t then f => f(h) :: map(f, t) )(f) map(x => x + 4, enumFromTo(1, 4)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(5, Cons(6, Cons(7, Nil))) :expect 45 fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun sum(ls, a) = if ls is Nil then a h :: t then sum(t, h + a) sum(enumFromTo(1, 10), 0) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls,None) //│ deforest > Select(Ref(ls,None),Ident(h)) //│ deforest > Select(Ref(ls,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 45 :expect Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun incr(x) = x + 1 fun map(f, xs_map) = if xs_map is Nil then Nil x :: xs then f(x) :: map(f, xs) fun rev(xs_rev, acc) = if xs_rev is x :: xs then rev(xs, x :: acc) Nil then acc map(incr, rev(id(mk(5)), Nil)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_map,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(acc,None))))) -> //│ deforest > Ref(xs_map,None) //│ deforest > Select(Ref(xs_map,None),Ident(h)) //│ deforest > Select(Ref(xs_map,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Nil)))))) :expect Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) fun mk(n) = if n < 0 then Nil else n :: mk(n - 1) fun incr(x) = x + 1 fun map(f, xs_map) = if xs_map is Nil then Nil x :: xs then f(x) :: map(f, xs) fun rev(xs_rev, acc) = if xs_rev is x :: xs then rev(xs, x :: acc) Nil then acc rev(map(incr, id(mk(3))), Nil) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_rev,None) //│ deforest > Select(Ref(xs_rev,None),Ident(h)) //│ deforest > Select(Ref(xs_rev,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_rev,None) //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) data class Pair(a, b) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< fun pair_up(xs) = if xs is x :: xss then if xss is y :: xs then Pair(x, y) :: pair_up(xs) else Nil else Nil fun mk(n) = if n > 0 then (n - 1) :: n :: (n + 1) :: mk(n - 1) else Nil pair_up(mk(4)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Cons( //│ Pair(3, 4), //│ Cons( //│ Pair(5, 2), //│ Cons( //│ Pair(3, 4), //│ Cons(Pair(1, 2), Cons(Pair(3, 0), Cons(Pair(1, 2), Nil))) //│ ) //│ ) //│ ) :expect 20 fun mk(n) = if n > 0 then n :: mk(n - 1) else Nil fun mk2d(n) = if n > 0 then mk(n) :: mk2d(n - 1) else Nil fun append(xs, ys) = if xs is Nil then ys x :: xs then x :: append(xs, ys) fun sum(ls_sum) = if ls_sum is Nil then 0 x :: xs then x + sum(xs) fun flatten(ls_flatten) = if ls_flatten is Nil then Nil x :: xs then append(x, flatten(xs)) sum(flatten(id(mk2d(4)))) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_sum,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls_sum,None) //│ deforest > Select(Ref(ls_sum,None),Ident(h)) //│ deforest > Select(Ref(ls_sum,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 20 :expect 2 fun count(c, xs) = if xs is h :: t then count(c + 1, t) Nil then c fun rev(a, ys) = if ys is h :: t then rev(h :: a, t) Nil then a count(0, rev(Nil, 1 :: 2 :: Nil)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(h,None)), Arg(None,Ref(a,None))))) -> //│ deforest > Ref(xs,None) //│ deforest > Select(Ref(xs,None),Ident(h)) //│ deforest > Select(Ref(xs,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ys,None) //│ deforest > Select(Ref(ys,None),Ident(h)) //│ deforest > Select(Ref(ys,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ys,None) //│ deforest > Select(Ref(ys,None),Ident(h)) //│ deforest > Select(Ref(ys,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys,None) //│ deforest > <<< fusing <<< //│ = 2 :expect Some(2) fun last(ls) = if ls is h :: t and t is Nil then Some(h) _ :: _ then last(t) Nil then None let p = 1 :: 2 :: Nil last(p) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Some(2) //│ p = Cons(1, Cons(2, Nil)) :expect Cons(1, Cons(0, Nil)) fun map(f, xs_map) = if xs_map is Nil then Nil x :: xs then f(x) :: map(f, xs) map(x => if x is A then 1 else 0, A :: B :: Nil) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(member:A,Some(object:A))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_map,None) //│ deforest > Select(Ref(xs_map,None),Ident(h)) //│ deforest > Select(Ref(xs_map,None),Ident(t)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(member:B,Some(object:B))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(xs_map,None) //│ deforest > Select(Ref(xs_map,None),Ident(h)) //│ deforest > Select(Ref(xs_map,None),Ident(t)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_map,None) //│ deforest > <<< fusing <<< //│ = Cons(1, Cons(0, Nil)) :expect T(0, T(1, L, L), T(0, L, L)) fun c(x) = if x is T(n, l, r) then T of if n is A then 0 B then 1 c(l) c(r) L then L c(T(A, T(B, L, L), T(A, L, L))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:T,Some(term:T/T)),List(List(Arg(None,Ref(member:A,Some(object:A))), Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(n)) //│ deforest > Select(Ref(x,None),Ident(l)) //│ deforest > Select(Ref(x,None),Ident(r)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(n,None) //│ deforest > Call(Ref(member:T,Some(term:T/T)),List(List(Arg(None,Ref(member:A,Some(object:A))), Arg(None,Ref(member:L,Some(object:L))), Arg(None,Ref(member:L,Some(object:L)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(n)) //│ deforest > Select(Ref(x,None),Ident(l)) //│ deforest > Select(Ref(x,None),Ident(r)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(n,None) //│ deforest > Ref(member:L,Some(object:L)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:L,Some(object:L)) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:T,Some(term:T/T)),List(List(Arg(None,Ref(member:B,Some(object:B))), Arg(None,Ref(member:L,Some(object:L))), Arg(None,Ref(member:L,Some(object:L)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(n)) //│ deforest > Select(Ref(x,None),Ident(l)) //│ deforest > Select(Ref(x,None),Ident(r)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(n,None) //│ deforest > Ref(member:L,Some(object:L)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:L,Some(object:L)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = T(0, T(1, L, L), T(0, L, L)) :expect 4 fun max(ms, m) = if ms is h :: t and h > m then max(t, h) else max(t, m) Nil then m max(3 :: 2 :: 4 :: 1 :: 0 :: Nil, 0) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ms,None) //│ deforest > Select(Ref(ms,None),Ident(h)) //│ deforest > Select(Ref(ms,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ms,None) //│ deforest > Select(Ref(ms,None),Ident(h)) //│ deforest > Select(Ref(ms,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(4))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ms,None) //│ deforest > Select(Ref(ms,None),Ident(h)) //│ deforest > Select(Ref(ms,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ms,None) //│ deforest > Select(Ref(ms,None),Ident(h)) //│ deforest > Select(Ref(ms,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(0))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(ms,None) //│ deforest > Select(Ref(ms,None),Ident(h)) //│ deforest > Select(Ref(ms,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ms,None) //│ deforest > <<< fusing <<< //│ = 4 :expect Some(4) fun filter(ls, f) = if ls is h :: t and f(h) then h :: filter(t, f) else filter(t, f) Nil then Nil fun last(ls_last) = fun go(a, ls_go) = if ls_go is Nil then a h :: t then go(h, t) if ls_last is h :: t then Some(go(h, t)) Nil then None fun lastFilter(ls, f) = last(filter(ls, f)) lastFilter(id(1 :: 2 :: 3 :: 4 :: 5 :: Nil), x => (x % 2 == 0)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = Some(4) // should prevent processing a function body more than once :deforest fun g() = f() fun f() = g() + g() [f, g] //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = [fun f, fun g] // should prevent processing a function body more than once :deforest fun h1() = g() fun h2() = g() fun g() = f() fun f() = h1() + h2() [h1, h2, g, f] //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = [fun h1, fun h2, fun g, fun f] // should prevent processing a function body more than once :deforest fun h1() = g() fun h2() = g() + h1() fun g() = f() fun f() = h1() + h2() [h1, h2, g, f] //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = [fun h1, fun h2, fun g, fun f] // should prevent processing a function body more than once :deforest fun main() = if false then f() + g() else 0 fun f() = if false then h() else 0 fun h() = if false then f() + main() else 0 fun g() = if false then h() + main() else 0 main //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = fun main :deforest fun f() = if B is B then h1() + h2() fun h1() = g1() fun g1() = f() fun h2() = g2() fun g2() = f() f //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(member:B,Some(object:B)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(member:B,Some(object:B)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(member:B,Some(object:B)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(member:B,Some(object:B)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(member:B,Some(object:B)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(member:B,Some(object:B)) //│ deforest > <<< fusing <<< //│ = fun f_9$f :deforest fun h1() = g() fun h2() = g() + h2() fun g() = f() fun f() = h1() + h2() [h1, h2, g, f] //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = [fun h1, fun h2, fun g, fun f] :deforest fun g() = f() fun f() = g() [g, f] //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = [fun g, fun f] :deforest fun g() = 0 fun f() = f() + g() [f, g] //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = [fun f, fun g] :deforest fun h(x) = if x is A then 0 else f() fun g1() = h(B) fun g2() = h(B) fun g3() = h(B) fun g4() = h(B) fun f() = [g1(), g2(), g3(), g4(), h(B)] f //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = fun f_17$f :deforest fun h(x) = if x is A then 0 else f() fun g1() = h(B) fun g2() = h(B) fun f() = [g1(), g2()] f //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = fun f_9$f :deforest :lift fun f() = fun h(x) = if x is A then 0 else f() [() => h(B), () => h(B), () => h(B), () => h(B), h(B)] f //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = fun f_13$f ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/relaxedPrograms.mls ================================================ :js :deforest :expect 5 data class AA(x) class Store(n) with fun get() = if AA(n) is AA(x) then x + n fun foo(x) = if x is AA(x) then x + n val store = Store(1) store.foo(AA(4)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(term:Store/n,None))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 5 //│ store = Store(_) :deforest :expect 3 fun classMethodSibling() = if AA(1) is AA(x) then x + store.get() classMethodSibling() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 3 :deforest let r = a: 0 value: 1 if AA(1) is AA(x) then x + r!a //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 1 //│ r = {a: 0, value: 1} :expect 2 :deforest if AA(1) is AA(x) then let xs = [0, 1] x + xs!1 //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 2 // TODO: rewriting for nested classes :deforest :fixme fun nestedClass() = class Local() with fun get() = 0 Local().get() if AA(1) is AA(x) then x nestedClass() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ /!!!\ Uncaught error: java.lang.AssertionError: assertion failed // TODO: rewriting for loops :deforest :fixme fun loopSibling() = while false do if AA(1) is AA(x) then print(x) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ /!!!\ Uncaught error: scala.NotImplementedError: unsupported `continue` instruction during rewriting (of class String) // TODO: branch body capturing mutable vars :fixme :deforest :expect 5 class C(n) with let _n = n fun get() = if AA(1) is AA(x) then let res = x + _n _n = _n + 1 res let c = C(1) c.get() + c.get() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ ═══[RUNTIME ERROR] Expected: '5', got: '4' //│ = 4 //│ c = C(_) // TODO: private members aren't visible outside :fixme :deforest module M with let _private = 3 fun f() = if AA(1) is AA(a) then a + _private M.f() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ > let M1, f_7$scrut_AA, f_6$f, f_6$scrut_AA, inlinedVal1, scrut3, AA_x3;try { globalThis.Object.freeze(class M { static { M1 = this } static #_private; static { M.#_private = 3; } static f() { runtime.checkArgs("f", 0, true, arguments.length); let scrut4, AA_x4; AA_x4 = 1; scrut4 = (fv_ctorLam__private, fv_ctorLam_a, fv_ctorLam_arg$AA$0$) => { let fv__private, fv_a, fv_arg$AA$0$, AA_x5, inlinedVal2; fv__private = fv_ctorLam__private; fv_a = fv_ctorLam_a; fv_arg$AA$0$ = fv_ctorLam_arg$AA$0$; AA_x5 = AA_x4; fv_arg$AA$0$ = AA_x5; fv_a = fv_arg$AA$0$; inlinedVal2 = fv_a + fv__private; return inlinedVal2 }; return runtime.checkCall(scrut4(M.#_private, undefined, undefined)) } toString() { return runtime.render(this); } static [definitionMetadata] = ["class", "M"]; }); AA_x3 = 1; scrut3 = (fv_ctorLam__private, fv_ctorLam_a, fv_ctorLam_arg$AA$0$) => { let fv__private, fv_a, fv_arg$AA$0$, AA_x4, inlinedVal2; fv__private = fv_ctorLam__private; fv_a = fv_ctorLam_a; fv_arg$AA$0$ = fv_ctorLam_arg$AA$0$; AA_x4 = AA_x3; fv_arg$AA$0$ = AA_x4; fv_a = fv_arg$AA$0$; inlinedVal2 = fv_a + fv__private; return inlinedVal2 }; inlinedVal1 = runtime.checkCall(scrut3(M.#_private, undefined, undefined)); block$res6 = inlinedVal1; undefined; undefined } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Private field '#_private' must be declared in an enclosing class // TODO: rewrite selecting from nested modules :fixme :deforest module M with module MM with fun g() = 5 fun f() = if AA(1) is AA(a) then a + g() M.MM.f() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ ═══[RUNTIME ERROR] ReferenceError: MM is not defined ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/selectionsInNestedMatch.mls ================================================ :js :deforest object A object B object C data class AA(x) data class BB(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< object Nil data class Cons(h, t) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< object None data class Some(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :expect 2 fun c(x) = if x is AA then if A is A then x.AA#x c(AA(2)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 2 :expect 2 fun c(x, y) = if x is AA then if y is A then x.AA#x c(AA(2), A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(y,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 2 :expect 2 fun c(x, y) = if x is AA then if y is A then x.AA#x fun p() = AA(2) c(p(), A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(y,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 2 :expect 3 fun c(x, y) = if x is AA then let a = if y is A then 1 a + x.AA#x fun p() = AA(2) c(p(), A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(y,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 3 :expect 42 fun f(x) = if x is AA(AA(a)) then g(a) fun g(x) = if x is AA(b) then f(b) A then 42 f(AA(AA(AA(AA(AA(A)))))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 42 :expect A fun f(x) = if x is AA(AA(a)) then a f(AA(AA(A))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = A :expect A fun f(x) = if x is AA(AA(a)) then a fun p() = AA(AA(A)) f(p()) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = A :expect 12 fun c(x, y) = let t = if x is AA then if A is A then x.AA#x t + y c(AA(2), 10) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > <<< fusing <<< //│ = 12 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/simple.mls ================================================ :js :deforest object A object B object C object D data class AA(aa) data class BB(bb) data class AAA(x, y) data class BBB(x, y) data class CCC(c) object None data class Some(value) data object Nil data class (::) Cons(h, t) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< // * Note that this test produces dead code (non-empty rest after an abortive block prefix), // * but that code is now renoved, as can be checked by comparing `:sir` and `:soir` outputs. // * FIXME: At the time of writing, there is currently still some (non-dead) duplication of the big addition, though. // :sir // :soir fun test(x) = let y = if x is AA(AA(v)) then v y + 1 + 2 + 3 + 4 test(AA(AA(10))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 20 :expect 1 let x = if true then A else B if x is A then 1 B then 2 //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 1 //│ x = fun tmp2 :expect A let x = if true then AA(A) else BB(B) if x is AA(x) then x BB(x) then x //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(member:B,Some(object:B)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = A //│ x = fun tmp3 :expect 1 fun f(a) = if a is A then 1 B then 2 let x = if true then AA(A) else BB(B) if x is AA(x) then f(x) BB(x) then f(x) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(member:B,Some(object:B)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(a,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(a,None) //│ deforest > <<< fusing <<< //│ = 1 //│ x = fun tmp4 // `x.x` is successfully fused :expect 1 fun f1(a) = if a is A then 1 B then 2 C then 3 fun f2(a) = if a is A then 4 B then 5 C then 6 let x = if true then AA(A) else BB(B) if x is AA then f1(x.AA#aa) BB then f2(x.BB#bb) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(member:B,Some(object:B)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(a,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:A,Some(object:A)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(a,None) //│ deforest > <<< fusing <<< //│ = 1 //│ x = fun tmp5 :expect 11 fun g(x) = if true then AA(11) else BB(22) fun c(x) = if x is AA(x) then x BB(x) then x c(g(true)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(22)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(11)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 11 // multiple match, no fusion :expect 2 let x = B if x is A then 1 B then 3 if x is B then 2 //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 2 //│ x = B :expect 2 let x = A let y = B if x is A then 1 if y is B then 2 //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(y,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 2 //│ x = fun x11 //│ y = fun y :expect 3 fun c(a) = let x = if a is A then 1 B then 2 print(x) x c(A) + c(B) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(a,None) //│ deforest > <<< fusing <<< //│ > 1 //│ > 2 //│ = 3 :expect 5 fun c(x) = let t = x.AA#aa let n = if x is AA then 2 n + t c(AA(3)) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 5 :expect 3 fun f(a, b) = if a is A then if b is B then 3 f(A, B) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(b,None) //│ deforest > <<< fusing <<< //│ = 3 :expect 0 fun f(x) = if x is Some then if x.Some#value > 1 then f(Some(x.Some#value - 1)) else 0 f(Some(2)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Some,Some(term:Some/Some)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(value)) //│ deforest > Select(Ref(x,None),Ident(value)) //│ deforest > Call(Ref(member:Some,Some(term:Some/Some)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(value)) //│ deforest > Select(Ref(x,None),Ident(value)) //│ deforest > <<< fusing <<< //│ = 0 :expect 2 let x = A let y = B if x is A then 1 if y is B then 2 //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(y,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 2 //│ x = fun x13 //│ y = fun y1 :expect 21 fun f(x, y) = let a = if x is AAA(n, m) then y + n - m BBB(n, m) then m + 1 - n a + 3 f(AAA(1, 3), 1) + f(BBB(2, 3), 2) + f(AAA(3, 2), 4) + f(BBB(4, 6), 0) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BBB,Some(term:BBB/BBB)),List(List(Arg(None,Lit(IntLit(4))), Arg(None,Lit(IntLit(6)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > Call(Ref(member:BBB,Some(term:BBB/BBB)),List(List(Arg(None,Lit(IntLit(2))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > <<< fusing <<< //│ = 21 :expect 2 fun c1(x) = if x is AA then if A is A then x.AA#aa fun c2(x) = if x is AA then x.AA#aa fun p(a) = c1(a) + c2(a) p(AA(1)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > <<< fusing <<< //│ = 2 :expect 2 fun c(x, y) = if x is AA(a) then if y is A then a c(AA(2), A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(y,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 2 :expect 24 fun f(x, y) = let a = if x is AAA(n, m) then y + n - m CCC(n) then n + 1 a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) + f(CCC(4), 0) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:CCC,Some(term:CCC/CCC)),List(List(Arg(None,Lit(IntLit(4)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(c)) //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > Call(Ref(member:CCC,Some(term:CCC/CCC)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(c)) //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > <<< fusing <<< //│ = 24 :expect 22 fun f(x, y) = let a = if x is AAA then y + x.AAA#y CCC(n) then n + 1 a + 3 f(AAA(1, 3), 1) + f(CCC(2), 2) + f(AAA(3, 2), 4) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(3))), Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > Call(Ref(member:CCC,Some(term:CCC/CCC)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(c)) //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > <<< fusing <<< //│ = 22 :expect 0 fun c(x, m) = if x is AA(n) then n + 1 else m c(BB(3), 0) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 0 :expect 1 fun f(x) = if x is AA(b) then g(b) fun g(b) = if b is AA(a) then a + 1 f(AA(AA(0))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(b,None) //│ deforest > Select(Ref(b,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 1 :expect 3 fun f(x) = x.AA#aa.AA#aa f(AA(AA(3))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > term:AA/aa //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > term:AA/aa //│ deforest > <<< fusing <<< //│ = 3 :expect 103 fun inner(x, y) = let t = if x is AA(a) then a + y BB(b) then b - y t * y fun outer1(x, y, z) = let t = if x is AA(a) then inner(a, y) BB(b) then inner(b, y) t + z fun outer2(x, y, z) = let t = if x is AA(a) then inner(a, z) BB(b) then inner(b, y) t + y let p = AA(AA(3)) outer1(p, 1, 2) + outer2(p, 3, 4) + inner(AA(5), 6) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(5)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 103 //│ p = AA(AA(3)) :expect 1 fun mapHead(f, x) = if x is AAA(h, t) then f(h) mapHead(x => x, AAA(1, AAA(2, None))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AAA,Some(term:AAA/AAA)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Select(Ref(x,None),Ident(y)) //│ deforest > <<< fusing <<< //│ = 1 :expect 5 let k if A is A then k = 3 k + 2 //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > <<< fusing <<< //│ = 5 //│ k = 3 :expect 4 fun test(a) = if a is AA then 1 else 2 test(B) + test(C) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(a,None) //│ deforest > <<< fusing <<< //│ = 4 // technically ill-typed, so `AA(3)` (which flows to `z`) is not fused :expect "2AA(3)NaN" fun test(x, y, z) = if x is AA(a) then let m = if y is AA(a1) then a1 + z let n = if z is AA(a2) then a2 - z m + n test(AA(1), AA(2), AA(3)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(y,None) //│ deforest > Select(Ref(y,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = "2AA(3)NaN" :expect 141 fun test(x) = let t = id of if x is AA(CCC(a)) then a BB(CCC(a)) then a t(123).AA#aa + 5 * 4 - 3 + 2 - 1 let p = if true then AA(CCC(AA)) else BB(CCC(AA)) test(p) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Call(Ref(member:CCC,Some(term:CCC/CCC)),List(List(Arg(None,Ref(member:AA,Some(term:AA/AA)))))) -> //│ deforest > Ref(tmp:arg$BB$0$,None) //│ deforest > Select(Ref(tmp:arg$BB$0$,None),Ident(c)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Call(Ref(member:CCC,Some(term:CCC/CCC)),List(List(Arg(None,Ref(member:AA,Some(term:AA/AA)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(c)) //│ deforest > <<< fusing <<< //│ = 141 //│ p = fun tmp60 :expect 141 fun test(x, y) = let t = if y is CCC then if x is AA(a) then a BB(a) then a t(123).CCC#c + 5 * 4 - 3 + 2 - 1 let p = if true then AA(CCC) else BB(CCC) test(p, id(CCC(123))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Ref(member:CCC,Some(term:CCC/CCC)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(member:CCC,Some(term:CCC/CCC)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 141 //│ p = fun tmp62 :sir :expect 2 fun f(x) = if x is A then 1 f(id(A)) + f(A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰, tmp, tmp1, tmp2, f_7$f⁰, f_7$x_A⁰, f_7$x_rest⁰, deforest$lam; //│ define f_7$f⁰ as fun f_7$f¹(x) { //│ return x() //│ }; //│ define f_7$x_A⁰ as fun f_7$x_A¹() { //│ return 1 //│ }; //│ define f_7$x_rest⁰ as fun f_7$x_rest¹() { //│ return null //│ }; //│ define f⁰ as fun f¹(x) { //│ match x //│ A⁰ => //│ return 1 //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ set tmp = Predef⁰.id⁰(A⁰); //│ set tmp1 = f¹(tmp); //│ set deforest$lam = () => { return f_7$x_A¹() }; //│ set tmp2 = f_7$f¹(deforest$lam); //│ +⁰(tmp1, tmp2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 :expect 0 fun f(x) = if x is A then 0 B then 1 fun g(x) = if x is AA then A BB then B f(g(AA(1))) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 0 :expect 1 fun f(a, b) = if a is A and b is B then 1 else 0 f(A, B) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(a,None) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(b,None) //│ deforest > <<< fusing <<< //│ = 1 :expect 6 fun app(f) = f(1) fun identity(x) = x fun c1(x1) = if x1 is AA(i) then i fun c2(x) = if x is AA(i) then i A then 0 c1(app(AA)) + c1(AA(2)) + c2(AA(3)) + c2(A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x1,None) //│ deforest > Select(Ref(x1,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 6 :re fun p(x) = if x then new AA(2) else BB(3) fun c(x) = if x is AA(a) then a BB(b) then b else throw (if A is A then Error("e1") else Error("e2")) print(c(p(true)) + c(p(false))) c(B) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Instantiate(false,Ref(member:AA,Some(class:AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(member:A,Some(object:A)) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(bb)) //│ deforest > Instantiate(false,Ref(member:AA,Some(class:AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ > 5 //│ ═══[RUNTIME ERROR] Error: e1 :expect 3 fun f(a, b) = if a is AA(x) then x + b.BB#bb f(AA(1), BB(2)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > term:BB/bb //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 3 :deforest :expect 9 fun g(k, x) = if k is A then x.AA#aa + x.AA#aa + x.AA#aa g(A, AA(3)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(k,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > term:AA/aa //│ deforest > <<< fusing <<< //│ = 9 // multi parameter list :expect 103 fun inner(x)(y) = let t = if x is AA(a) then a + y BB(b) then b - y t * y fun outer1(x)(y, z) = let t = if x is AA(a) then inner(a)(y) BB(b) then inner(b)(y) t + z fun outer2(x, y)(z) = let t = if x is AA(a) then inner(a)(z) BB(b) then inner(b)(y) t + y let p = AA(AA(3)) outer1(p)(1, 2) + outer2(p, 3)(4) + inner(AA(5))(6) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(5)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 103 //│ p = AA(AA(3)) // `x.AA#aa` is used both as the scrutinee of the inner match // and as the base of a field selection (`x.AA#aa.AA#aa`), // giving it two consumer destinations, so the inner match is not fused :expect 3 fun f(x) = if x is AA then if x.AA#aa is AA then x.AA#aa.AA#aa f(AA(AA(3))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 3 :expect 3 fun id(x) = x fun f1(a1) = if a1 is AA then 1 fun f2(a2) = if a2 is AA then 2 f1(id(AA(1))) + f2(id(AA(2))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(a2,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(a1,None) //│ deforest > <<< fusing <<< //│ = 3 :expect 0 fun c1(x) = if x is h :: t then if t is Nil then 0 c1(1 :: Nil) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Lit(IntLit(1))), Arg(None,Ref(member:Nil,Some(object:Nil)))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(h)) //│ deforest > Select(Ref(x,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(t,None) //│ deforest > <<< fusing <<< //│ = 0 :expect 0 fun f(x) = if x is AA then g1(x.AA#aa) BB then g2(x.BB#bb) fun g1(y1) = if y1 is AA then 0 fun g2(y2) = if y2 is AA then 1 fun apply(f) = let inner = AA(0) f(AA(inner)) apply(f) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(inner,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(y1,None) //│ deforest > <<< fusing <<< //│ = 0 :expect 3 fun p(x) = AA(x) fun wrap1(x) = p(x) fun wrap(x) = wrap1(x) fun f1(a1) = if a1 is AA then 1 fun f2(a2) = if a2 is AA then 2 f1(wrap(1)) + f2(wrap(2)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a2,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a1,None) //│ deforest > <<< fusing <<< //│ = 3 :expect 0 fun p1(x) = p2(x) fun p2(x) = if x == 0 then AA(x) else p1(x - 1) fun c(a) = if a is AA(x) then x c(p1(3)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 0 :expect [D, AA(1)] fun f(x) = if g(false) is AA and x < 1 then BB(0) else f(x - 1) C then D fun g(b) = if b then if f(0) is BB then AA(0) D then AA(1) else C [f(3), g(true)] //│ deforest > >>> fusing >>> //│ deforest > Ref(member:D,Some(object:D)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:D,Some(object:D)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:BB,Some(term:BB/BB)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Ref(member:C,Some(object:C)) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(0)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > <<< fusing <<< //│ = [D, AA(1)] // `y` should not be considered as a free var for the branch :expect 4 fun f(ls) = if ls is [] then let y = 4 fun h(z) = if z > 0 then h(z - 1) else y h(3) f([]) //│ deforest > >>> fusing >>> //│ deforest > Tuple(false,List()) -> //│ deforest > Ref(ls,None) //│ deforest > <<< fusing <<< //│ = 4 :expect 0 fun f(x, b) = if x is Nil then if b then f(Nil, false) else 0 f(Nil, false) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 0 :expect 4 fun test() = let p = AA(1) let a = if p is AA(x) then x let b = if p is AA(x) then x a + b test() + test() //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 4 :expect 3 fun p(d) = AA(3) fun c1(x) = if x is AA then 1 fun c2(x) = if x is AA then 2 c1(p(1)) + c2(p(2)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(3)))))) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 3 :expect 15 fun p(x) = AA(x) fun f1(a1) = if a1 is AA(aa) then aa fun f2(a2) = if a2 is AA(aa) then aa fun f3(a3) = if a3 is AA(aa) then aa fun f4(a4) = if a4 is AA(aa) then aa fun f5(a5) = if a5 is AA(aa) then aa f1(p(1)) + f2(p(2)) + f3(p(3)) + f4(p(4)) + f5(p(5)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a5,None) //│ deforest > Select(Ref(a5,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a4,None) //│ deforest > Select(Ref(a4,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a3,None) //│ deforest > Select(Ref(a3,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a1,None) //│ deforest > Select(Ref(a1,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 15 // calls to `to` should be able to be duplicated :expect 88 fun to(n) = if n > 0 then let m = n - 1 n :: to(m) else Nil fun f1(ls1) = if ls1 is h :: t then h + f1(t) Nil then 2 fun f2(ls2) = if ls2 is h :: t then h + f2(t) Nil then 3 fun f3(ls3) = if ls3 is h :: t then h + f3(t) Nil then 4 fun f4(ls4) = if ls4 is h :: t then h + f4(t) Nil then 5 f1(to(4)) + f2(to(5)) + f3(to(6)) + f4(to(7)) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls4,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls4,None) //│ deforest > Select(Ref(ls4,None),Ident(h)) //│ deforest > Select(Ref(ls4,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls3,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls3,None) //│ deforest > Select(Ref(ls3,None),Ident(h)) //│ deforest > Select(Ref(ls3,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls2,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls2,None) //│ deforest > Select(Ref(ls2,None),Ident(h)) //│ deforest > Select(Ref(ls2,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls1,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(n,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls1,None) //│ deforest > Select(Ref(ls1,None),Ident(h)) //│ deforest > Select(Ref(ls1,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = 88 :expect 8 fun p(x) = AA(x) fun g1(b) = if b then p(1) else p(2) fun wrap(b) = g1(b) fun g2(b) = if b then p(3) else p(4) fun f1(p) = if p is AA(a) then a + 1 fun f2(p) = if p is AA(b) then b + 2 f1(wrap(true)) + f2(g2(false)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 8 :expect 10 fun p(x) = AA(x) let one = p(1) let two = p(2) fun f1(p) = if p is AA(a) then a + 1 fun f2(p) = if p is AA(b) then b + 2 fun f3(p) = if p is AA(c) then c + 3 f1(one) + f2(one) + f3(two) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(p,None) //│ deforest > Select(Ref(p,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 10 //│ one = AA(1) //│ two = fun inlinedVal91 :expect 4 fun id(x) = x fun f1(a) = if a is AA(i) then i fun f2(a) = if a is AA(i) then i + 1 f1(id(AA(1))) + f2(id(AA(2))) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(2)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(a,None) //│ deforest > Select(Ref(a,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 4 :expect 6 fun p(x) = AA(x) fun f1(a1) = if a1 is AA(a) then a + 1 fun f2(a2) = if a2 is AA(a) then a + 2 fun wrap(a) = a f1(wrap(p(2))) + f2(p(1)) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a2,None) //│ deforest > Select(Ref(a2,None),Ident(aa)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(x,None))))) -> //│ deforest > Ref(a1,None) //│ deforest > Select(Ref(a1,None),Ident(aa)) //│ deforest > <<< fusing <<< //│ = 6 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/todos.mls ================================================ :js :deforest object A object B object C data class AA(x) data class BB(x) data class CC(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< object Nil data class Cons(h, t) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< object None data class Some(x) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< // TODO: many dead code contain duplicated `t + 5 * 4 - 3 + 2 - 1`. // `t + 5 * 4 - 3 + 2 - 1` is the "rest" of a fusing match; // Deforestation makes programs early return at fusing matches, // and after deforestation, the "rest" of fusing matches would appear both // in "rest function"s and after the early return (and becomes dead code). // A dead code elimination pass that detects code after early return would solve this problem. :sir :expect 78 fun test(x) = let t = if x is AA(AA(AA(a))) then a else 4 t + 5 * 4 - 3 + 2 - 1 fun f(a) = if a is AA(AA) then 0 let p = AA(AA(AA(10))) test(p) + f(p) + test(AA(AA(AA(10)))) + test(B) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:B,Some(object:B)) -> //│ deforest > Ref(x,None) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(x,None) //│ deforest > Select(Ref(x,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > Call(Ref(member:AA,Some(term:AA/AA)),List(List(Arg(None,Lit(IntLit(10)))))) -> //│ deforest > Ref(tmp:arg$AA$0$,None) //│ deforest > Select(Ref(tmp:arg$AA$0$,None),Ident(x)) //│ deforest > <<< fusing <<< //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let test⁰, f⁰, p⁰, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, test_31$test⁰, test_31$x_dflt⁰, test_31$x_rest⁰, test_28$test⁰, test_28$x_AA⁰, test_28$x_rest⁰, test_28$arg$AA$0$_AA⁰, test_28$arg$AA$0$_rest⁰, test_28$arg$AA$0$_AA¹, test_28$arg$AA$0$_rest¹, test_20$test⁰, test_20$arg$AA$0$_AA⁰, test_20$arg$AA$0$_rest⁰, test_20$arg$AA$0$_rest¹, test_20$x_rest⁰, AA_x, AA_x1, AA_x2, AA_x3, deforest$lam; //│ define test_31$test⁰ as fun test_31$test¹(x) { //│ let t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15; //│ return x(tmp11, t, tmp11, tmp12, tmp13, tmp14, tmp15) //│ }; //│ define test_28$test⁰ as fun test_28$test¹(x) { //│ let t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15; //│ return x(t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15, t, tmp11, tmp12, tmp13, tmp14, tmp15) //│ }; //│ define test_20$test⁰ as fun test_20$test¹(x) { //│ let t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15; //│ match x //│ AA⁰ => //│ set arg$AA$0$ = x.x⁰; //│ match arg$AA$0$ //│ AA⁰ => //│ set arg$AA$0$1 = arg$AA$0$.x⁰; //│ return arg$AA$0$1(a, arg$AA$0$2, tmp11, t, tmp11, tmp12, tmp13, tmp14, tmp15) //│ else //│ set tmp11 = 4; //│ end //│ end //│ else //│ set tmp11 = 4; //│ end //│ set t = tmp11; //│ set tmp12 = *⁰(5, 4); //│ set tmp13 = +⁰(t, tmp12); //│ set tmp14 = -⁰(tmp13, 3); //│ set tmp15 = +⁰(tmp14, 2); //│ return -⁰(tmp15, 1) //│ }; //│ define test_31$x_dflt⁰ as fun test_31$x_dflt¹(fv_tmp, fv_t, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_tmp5) { //│ set fv_tmp1 = 4; //│ return test_31$x_rest¹(fv_t, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_tmp5) //│ }; //│ define test_28$x_AA⁰ as fun test_28$x_AA¹(fv_t, fv_a, fv_arg$AA$0$, fv_arg$AA$0$1, fv_arg$AA$0$2, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_t1, fv_tmp5, fv_tmp6, fv_tmp7, fv_tmp8, fv_tmp9, AA_x4) { //│ set fv_arg$AA$0$ = AA_x4; //│ return fv_arg$AA$0$(fv_t1, fv_a, fv_arg$AA$0$1, fv_arg$AA$0$2, fv_tmp5, fv_tmp6, fv_tmp7, fv_tmp8, fv_tmp9, fv_t1, fv_tmp5, fv_tmp6, fv_tmp7, fv_tmp8, fv_tmp9) //│ }; //│ define test_28$arg$AA$0$_AA⁰ as fun test_28$arg$AA$0$_AA²(fv_t, fv_a, fv_arg$AA$0$, fv_arg$AA$0$1, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_t1, fv_tmp5, fv_tmp6, fv_tmp7, fv_tmp8, fv_tmp9, AA_x4) { //│ set fv_arg$AA$0$ = AA_x4; //│ return fv_arg$AA$0$(fv_a, fv_arg$AA$0$1, fv_tmp5, fv_t1, fv_tmp5, fv_tmp6, fv_tmp7, fv_tmp8, fv_tmp9) //│ }; //│ define test_28$arg$AA$0$_AA¹ as fun test_28$arg$AA$0$_AA³(fv_a, fv_arg$AA$0$, fv_tmp, fv_t, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_tmp5, AA_x4) { //│ set fv_arg$AA$0$ = AA_x4; //│ set fv_a = fv_arg$AA$0$; //│ set fv_tmp1 = fv_a; //│ return test_28$arg$AA$0$_rest²(fv_t, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_tmp5) //│ }; //│ define test_20$arg$AA$0$_AA⁰ as fun test_20$arg$AA$0$_AA¹(fv_a, fv_arg$AA$0$, fv_tmp, fv_t, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_tmp5, AA_x4) { //│ set fv_arg$AA$0$ = AA_x4; //│ set fv_a = fv_arg$AA$0$; //│ set fv_tmp1 = fv_a; //│ return test_20$arg$AA$0$_rest²(fv_t, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4, fv_tmp5) //│ }; //│ define test_31$x_rest⁰ as fun test_31$x_rest¹(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ set fv_t = fv_tmp; //│ set fv_tmp1 = *⁰(5, 4); //│ set fv_tmp2 = +⁰(fv_t, fv_tmp1); //│ set fv_tmp3 = -⁰(fv_tmp2, 3); //│ set fv_tmp4 = +⁰(fv_tmp3, 2); //│ return -⁰(fv_tmp4, 1) //│ }; //│ define test_28$x_rest⁰ as fun test_28$x_rest¹(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ set fv_t = fv_tmp; //│ set fv_tmp1 = *⁰(5, 4); //│ set fv_tmp2 = +⁰(fv_t, fv_tmp1); //│ set fv_tmp3 = -⁰(fv_tmp2, 3); //│ set fv_tmp4 = +⁰(fv_tmp3, 2); //│ return -⁰(fv_tmp4, 1) //│ }; //│ define test_28$arg$AA$0$_rest⁰ as fun test_28$arg$AA$0$_rest³(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ return test_28$x_rest¹(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) //│ }; //│ define test_28$arg$AA$0$_rest¹ as fun test_28$arg$AA$0$_rest²(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ return test_28$arg$AA$0$_rest³(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) //│ }; //│ define test_20$arg$AA$0$_rest⁰ as fun test_20$arg$AA$0$_rest²(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ return test_20$arg$AA$0$_rest³(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) //│ }; //│ define test_20$arg$AA$0$_rest¹ as fun test_20$arg$AA$0$_rest³(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ return test_20$x_rest¹(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) //│ }; //│ define test_20$x_rest⁰ as fun test_20$x_rest¹(fv_t, fv_tmp, fv_tmp1, fv_tmp2, fv_tmp3, fv_tmp4) { //│ set fv_t = fv_tmp; //│ set fv_tmp1 = *⁰(5, 4); //│ set fv_tmp2 = +⁰(fv_t, fv_tmp1); //│ set fv_tmp3 = -⁰(fv_tmp2, 3); //│ set fv_tmp4 = +⁰(fv_tmp3, 2); //│ return -⁰(fv_tmp4, 1) //│ }; //│ define test⁰ as fun test¹(x) { //│ let t, a, arg$AA$0$, arg$AA$0$1, arg$AA$0$2, tmp11, tmp12, tmp13, tmp14, tmp15; //│ match x //│ AA⁰ => //│ set arg$AA$0$ = x.x⁰; //│ match arg$AA$0$ //│ AA⁰ => //│ set arg$AA$0$1 = arg$AA$0$.x⁰; //│ match arg$AA$0$1 //│ AA⁰ => //│ set arg$AA$0$2 = arg$AA$0$1.x⁰; //│ set a = arg$AA$0$2; //│ set tmp11 = a; //│ end //│ else //│ set tmp11 = 4; //│ end //│ end //│ else //│ set tmp11 = 4; //│ end //│ end //│ else //│ set tmp11 = 4; //│ end //│ set t = tmp11; //│ set tmp12 = *⁰(5, 4); //│ set tmp13 = +⁰(t, tmp12); //│ set tmp14 = -⁰(tmp13, 3); //│ set tmp15 = +⁰(tmp14, 2); //│ return -⁰(tmp15, 1) //│ }; //│ define f⁰ as fun f¹(a) { //│ let arg$AA$0$; //│ block split_root$: //│ block split_default$: //│ match a //│ AA⁰ => //│ set arg$AA$0$ = a.x⁰; //│ match arg$AA$0$ //│ AA⁰ => //│ return 0 //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ throw new globalThis⁰.Error⁰("match error") //│ return runtime⁰.Unit⁰ //│ }; //│ set AA_x = 10; //│ set tmp = (fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_tmp, fv_ctorLam_t, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_tmp5) => { //│ return test_20$arg$AA$0$_AA¹(fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_tmp, fv_ctorLam_t, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_tmp5, AA_x) //│ }; //│ set tmp1 = AA¹(tmp); //│ set p⁰ = AA¹(tmp1); //│ set tmp2 = test_20$test¹(p⁰); //│ set tmp3 = f¹(p⁰); //│ set tmp4 = +⁰(tmp2, tmp3); //│ set AA_x1 = 10; //│ set tmp5 = (fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_tmp, fv_ctorLam_t, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_tmp5) => { //│ return test_28$arg$AA$0$_AA³(fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_tmp, fv_ctorLam_t, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_tmp5, AA_x1) //│ }; //│ set AA_x2 = tmp5; //│ set tmp6 = (fv_ctorLam_t, fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_arg$AA$0$1, fv_ctorLam_tmp, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_t1, fv_ctorLam_tmp5, fv_ctorLam_tmp6, fv_ctorLam_tmp7, fv_ctorLam_tmp8, fv_ctorLam_tmp9) => { //│ return test_28$arg$AA$0$_AA²(fv_ctorLam_t, fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_arg$AA$0$1, fv_ctorLam_tmp, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_t1, fv_ctorLam_tmp5, fv_ctorLam_tmp6, fv_ctorLam_tmp7, fv_ctorLam_tmp8, fv_ctorLam_tmp9, AA_x2) //│ }; //│ set AA_x3 = tmp6; //│ set tmp7 = (fv_ctorLam_t, fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_arg$AA$0$1, fv_ctorLam_arg$AA$0$2, fv_ctorLam_tmp, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_t1, fv_ctorLam_tmp5, fv_ctorLam_tmp6, fv_ctorLam_tmp7, fv_ctorLam_tmp8, fv_ctorLam_tmp9) => { //│ return test_28$x_AA¹(fv_ctorLam_t, fv_ctorLam_a, fv_ctorLam_arg$AA$0$, fv_ctorLam_arg$AA$0$1, fv_ctorLam_arg$AA$0$2, fv_ctorLam_tmp, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_t1, fv_ctorLam_tmp5, fv_ctorLam_tmp6, fv_ctorLam_tmp7, fv_ctorLam_tmp8, fv_ctorLam_tmp9, AA_x3) //│ }; //│ set tmp8 = test_28$test¹(tmp7); //│ set tmp9 = +⁰(tmp4, tmp8); //│ set deforest$lam = (fv_ctorLam_tmp, fv_ctorLam_t, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_tmp5) => { //│ return test_31$x_dflt¹(fv_ctorLam_tmp, fv_ctorLam_t, fv_ctorLam_tmp1, fv_ctorLam_tmp2, fv_ctorLam_tmp3, fv_ctorLam_tmp4, fv_ctorLam_tmp5) //│ }; //│ set tmp10 = test_31$test¹(deforest$lam); //│ +⁰(tmp9, tmp10) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 78 //│ p = AA(AA(fun tmp)) // TODO: rewriting does not handle properly classes nested in functions yet :todo :expect Local(2) data class Global(x) fun test() = data class Local(x) if Global(1) is Global(x) then Local(x + 1) test() //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Global,Some(term:Global/Global)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > Call(Ref(member:Global,Some(term:Global/Global)),List(List(Arg(None,Lit(IntLit(1)))))) -> //│ deforest > Ref(tmp:scrut,None) //│ deforest > Select(Ref(tmp:scrut,None),Ident(x)) //│ deforest > <<< fusing <<< //│ /!!!\ Uncaught error: java.lang.AssertionError: assertion failed // TODO: fusion opportunity is lost // because there are two consumers due to two call sites of `inner`s. :expect 0 fun inner(x) = if x is A then 0 else 1 fun test() = let x = A inner(x) + inner(x) test() //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< //│ = 0 // TODO: duplicated instantiations of the consumer `f` :expect 2 fun f(x) = if x is A then 1 f(A) + f(A) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > Ref(member:A,Some(object:A)) -> //│ deforest > Ref(x,None) //│ deforest > <<< fusing <<< //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/deforest/zipunzip.mls ================================================ :js :deforest object Nil data class (::) Cons(h, t) data class Pair(a, b) //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< :expect Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) fun zip(xs_zip, ys_zip) = if xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun unzip(ls_unzip) = if ls_unzip is Pair(a, b) :: t and unzip(t) is Pair(atail, btail) then Pair(a :: atail, b :: btail) Nil then Pair(Nil, Nil) fun enumFromTo(a, b) = if a < b then a :: enumFromTo(a + 1, b) else Nil fun testUnzipZip(n) = unzip(zip(id(enumFromTo(1, n)), id(enumFromTo(2, n + 3)))) testUnzipZip(3) //│ deforest > >>> fusing >>> //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls_unzip,None) //│ deforest > Select(Ref(ls_unzip,None),Ident(h)) //│ deforest > Select(Ref(ls_unzip,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(y,None))))) -> //│ deforest > Ref(tmp:arg$Cons$0$,None) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(a)) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_unzip,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_unzip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ls_unzip,None) //│ deforest > Select(Ref(ls_unzip,None),Ident(h)) //│ deforest > Select(Ref(ls_unzip,None),Ident(t)) //│ deforest > Call(Ref(member:Pair,Some(term:Pair/Pair)),List(List(Arg(None,Ref(x,None)), Arg(None,Ref(y,None))))) -> //│ deforest > Ref(tmp:arg$Cons$0$,None) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(a)) //│ deforest > Select(Ref(tmp:arg$Cons$0$,None),Ident(b)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_unzip,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_unzip,None) //│ deforest > <<< fusing <<< //│ = Pair(Cons(1, Cons(2, Nil)), Cons(2, Cons(3, Nil))) :expect Cons(Pair(3, 4), Cons(Pair(2, 3), Cons(Pair(1, 2), Nil))) fun zip(xs_zip, ys_zip) = if xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun unzip(ls_unzip) = if ls_unzip is Pair(a, b) :: t and unzip(t) is Pair(atail, btail) then Pair(a :: atail, b :: btail) Nil then Pair(Nil, Nil) fun makeZippedList(n) = if n > 0 then Pair(n, n + 1) :: makeZippedList(n - 1) else Nil fun testZipUnzip(n) = if unzip(id(makeZippedList(n))) is Pair(xs, ys) then zip(xs, ys) testZipUnzip(3) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(atail,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(b,None)), Arg(None,Ref(btail,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(atail,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(b,None)), Arg(None,Ref(btail,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > <<< fusing <<< //│ = Cons(Pair(3, 4), Cons(Pair(2, 3), Cons(Pair(1, 2), Nil))) fun zip(xs_zip, ys_zip) = if xs_zip is x :: xt and ys_zip is y :: yt then Pair(x, y) :: zip(xt, yt) else Nil fun unzip(ls_unzip) = if ls_unzip is Pair(a, b) :: t and unzip(t) is Pair(atail, btail) then Pair(a :: atail, b :: btail) Nil then Pair(Nil, Nil) fun map(f, ls_map) = if ls_map is h :: t then f(h) :: map(f, t) Nil then Nil fun makeZippedList(n) = if n > 0 then Cons(Pair(n, n + 1), makeZippedList(n - 1)) else Nil fun testZipMapBothUnzip(n) = if unzip(id(makeZippedList(n))) is Pair(xs, ys) then zip( map(x => x + 1, xs), map(x => x * x, ys) ) testZipMapBothUnzip(4) //│ deforest > >>> fusing >>> //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(atail,None))))) -> //│ deforest > Ref(ls_map,None) //│ deforest > Select(Ref(ls_map,None),Ident(h)) //│ deforest > Select(Ref(ls_map,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(b,None)), Arg(None,Ref(btail,None))))) -> //│ deforest > Ref(ls_map,None) //│ deforest > Select(Ref(ls_map,None),Ident(h)) //│ deforest > Select(Ref(ls_map,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(a,None)), Arg(None,Ref(atail,None))))) -> //│ deforest > Ref(ls_map,None) //│ deforest > Select(Ref(ls_map,None),Ident(h)) //│ deforest > Select(Ref(ls_map,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ls_map,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(b,None)), Arg(None,Ref(btail,None))))) -> //│ deforest > Ref(ls_map,None) //│ deforest > Select(Ref(ls_map,None),Ident(h)) //│ deforest > Select(Ref(ls_map,None),Ident(t)) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Select(Ref(xs_zip,None),Ident(h)) //│ deforest > Select(Ref(xs_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(xs_zip,None) //│ deforest > Call(Ref(member:Cons,Some(term:Cons/Cons)),List(List(Arg(None,Ref(tmp:tmp,None)), Arg(None,Ref(tmp:tmp,None))))) -> //│ deforest > Ref(ys_zip,None) //│ deforest > Select(Ref(ys_zip,None),Ident(h)) //│ deforest > Select(Ref(ys_zip,None),Ident(t)) //│ deforest > Ref(member:Nil,Some(object:Nil)) -> //│ deforest > Ref(ys_zip,None) //│ deforest > <<< fusing <<< //│ = Cons( //│ Pair(5, 25), //│ Cons(Pair(4, 16), Cons(Pair(3, 9), Cons(Pair(2, 4), Nil))) //│ ) ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/BadFlows.mls ================================================ :js :flow :global :sf :e :ge Int //│ ╔══[COMPILATION ERROR] Cannot use non-term member Int in term position //│ ║ l.10: Int //│ ╙── ^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ Int⁰{ ~> Int⁰ } //│ where //│ //│ ╔══[COMPILATION ERROR] Symbol 'Int' is virtual (i.e., "compiler fiction"); cannot be used as a term //│ ║ l.10: Int //│ ╙── ^^^ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. class C //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class C { //│ //│ } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :e C //│ ╔══[COMPILATION ERROR] Cannot use non-term member C in term position //│ ║ l.35: C //│ ╙── ^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ C⁰{ ~> C⁰ } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = class C ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls ================================================ :js :flow // :df // :todo // :d :global :sf 2 //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ 2 //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 x => x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ (x⁰) => x⁰ //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun 2 + 2 //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ +(2, 2)‹app⁰› //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 4 class Foo with val m = 1 //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class Foo { //│ val m⁰ ‹flow:m⁰› = 1 //│ } //│ where //│ flow:m⁰ <~ 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :e fun test(x) = x.m //│ ╔══[COMPILATION ERROR] Cannot resolve selection //│ ║ l.48: fun test(x) = x.m //│ ╙── ^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun test⁰(x¹) ‹flow:test⁰› = x¹.m‹?› //│ where //│ x¹ ~> {m: ⋅m⁰} //│ flow:test⁰ <~ ((x¹) -> ⋅m⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // :de // :df fun test(x: Foo) = x.m //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun test¹(x²: Foo⁰{ ~> Foo⁰ }) ‹flow:test¹› = x².m‹?›{ ~> x².m⁰ } //│ where //│ x² <~ type Foo //│ flow:test¹ <~ ((type Foo) -> flow:m⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let f = (x: Foo) => x.m //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let f⁰, //│ f⁰ = (x³: Foo⁰{ ~> Foo⁰ }) => x³.m‹?›{ ~> x³.m⁰ } //│ where //│ f⁰ <~ ((type Foo) -> flow:m⁰) //│ x³ <~ type Foo //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f let a = [1, 2, 3] //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let a⁰, //│ a⁰ = [ //│ 1, //│ 2, //│ 3 //│ ] //│ where //│ a⁰ <~ [1, 2, 3 ] //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ a = [1, 2, 3] let f = (x, y) => x + y //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let f¹, //│ f¹ = (x⁴, y⁰) => +(x⁴, y⁰)‹app¹› //│ where //│ f¹ <~ ((x⁴, y⁰) -> app¹) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f f(1, 2) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f¹(1, 2)‹app²› //│ where //│ f¹ <~ ((x⁴, y⁰) -> app¹) //│ f¹ ~> ((1, 2) -> app²) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 :fixme f(...a) //│ /!!!\ Uncaught error: scala.MatchError: Spd(Eager,Ref(a)) (of class hkmc2.semantics.Spd) fun przd[A, B](x: A) = x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun przd⁰[A⁰, B⁰](x⁵: A⁰) ‹flow:przd⁰› = x⁵ //│ where //│ x⁵ <~ type A //│ flow:przd⁰ <~ ((type A) -> x⁵) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/Identity.mls ================================================ :flow :global :sf fun id(x) = x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun id⁰(x⁰) ‹flow:id⁰› = x⁰ //│ where //│ flow:id⁰ <~ ((x⁰) -> x⁰) id(1) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ id⁰{ ~> id⁰ }(1)‹app⁰› //│ where //│ app⁰ <~ 1 id(true) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ id⁰{ ~> id⁰ }(true)‹app¹› //│ where //│ app¹ <~ 1 true fun foo(x) = x + x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun foo⁰(x¹) ‹flow:foo⁰› = +(x¹, x¹)‹app²› //│ where //│ flow:foo⁰ <~ ((x¹) -> app²) (x, x) => x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ (x², x³) => x³ //│ where //│ (x, y) => x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ (x⁴, y⁰) => x⁴ //│ where //│ ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/LeadingDotAccesses.mls ================================================ :js :flow class A with fun b = new A fun c = (x) => new A module A with fun a = new A fun f(x : A) = x :sf f(.a) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰{ ~> f⁰ }(_?_.a{ ~> A⁰.a⁰ })‹app⁰› //│ where //│ app⁰ <~ type A //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = A :sf f(.a.b) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰{ ~> f⁰ }(A⁰.a⁰.b⁰)‹app¹› //│ where //│ app¹ <~ type A //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = A :sf let z = .a f(z) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let z⁰, //│ z⁰ = _?_.a{ ~> A⁰.a⁰ }, //│ f⁰{ ~> f⁰ }(z⁰)‹app²› //│ where //│ z⁰ <~ _?_.A⁰.a⁰ //│ z⁰ ~> type A //│ app² <~ type A //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = A //│ z = A :sf let z = .a.b.c(3) f(z).c(4).b //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let z¹, //│ z¹ = A⁰.a⁰.b⁰.c⁰(3)‹app³›, //│ f⁰(z¹)‹app⁴›.c⁰(4)‹app⁵›.b⁰ //│ where //│ z¹ <~ _?_.A⁰.a⁰ //│ z¹ ~> type A //│ app³ <~ A //│ app³ -> bind⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = A //│ z = A :sf f(.a.c(1).b.c(4)) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰{ ~> f⁰ }(A⁰.a⁰.c⁰(1)‹app⁶›.b⁰.c⁰(4)‹app⁷›)‹app⁸› //│ where //│ app⁸ <~ type A //│ app⁷ <~ A //│ app⁷ -> bind¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = A class C module C with fun a = new C :sf :e fun g(x : C) = x let d = .a f(d) g(d) //│ ╔══[COMPILATION ERROR] Ambiguous selection with multiple apparent targets: //│ ║ l.78: let d = .a //│ ║ ^^ //│ ╟── companion member a //│ ║ l.73: fun a = new C //│ ║ ^^^^^^^^^^^^^ //│ ╟── companion member a //│ ║ l.8: fun a = new A //│ ╙── ^^^^^^^^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun g⁰(x⁰: C⁰{ ~> C⁰ }) ‹flow:g⁰› = x⁰, //│ let d⁰, //│ d⁰ = _?_.a{ ~> ‹error› }, //│ f⁰{ ~> f⁰ }(d⁰)‹app⁹›, //│ g⁰{ ~> g⁰ }(d⁰)‹app¹⁰› //│ where //│ x⁰ <~ type C //│ x⁰ -> app¹⁰ //│ d⁰ <~ _?_.‹error› //│ d⁰ ~> type A type C //│ app⁹ <~ type A //│ app¹⁰ <~ type C //│ flow:g⁰ <~ ((type C) -> x⁰) //│ flow:g⁰ ~> ((d⁰) -> app¹⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ d = undefined :sf f(.a) g(.a) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰{ ~> f⁰ }(_?_.a{ ~> A⁰.a⁰ })‹app¹¹›, //│ g⁰{ ~> g⁰ }(_?_.a{ ~> C⁰.a¹ })‹app¹²› //│ where //│ app¹¹ <~ type A //│ app¹² <~ type C //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = C :sf :re :e f(.w) //│ ╔══[COMPILATION ERROR] Cannot resolve leading dot selection //│ ║ l.124: f(.w) //│ ╙── ^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰{ ~> f⁰ }(_?_.w{ ~> ‹error› })‹app¹³› //│ where //│ app¹³ <~ type A //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :sf :e .b(new A) //│ ╔══[COMPILATION ERROR] Cannot resolve leading dot selection //│ ║ l.137: .b(new A) //│ ╙── ^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ _?_.b{ ~> ‹error› }(new A⁰)‹app¹⁴› //│ where //│ app¹⁴ -> bind² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/SelExpansion.mls ================================================ :js :flow // :df :global :sf class Foo with val a = 123 module Foo with fun foo(x: Foo) = x.a //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class Foo { //│ val a⁰ ‹flow:a⁰› = 123 //│ }, //│ module Foo { //│ fun foo⁰(x⁰: Foo⁰{ ~> Foo⁰ }) ‹flow:foo⁰› = x⁰.a‹?›{ ~> x⁰.a⁰ } //│ } //│ where //│ x⁰ <~ type Foo //│ flow:a⁰ <~ 123 //│ flow:foo⁰ <~ ((type Foo) -> flow:a⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let f = new Foo() //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let f⁰, //│ f⁰ = new Foo⁰() //│ where //│ f⁰ <~ Foo() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = Foo { a: 123 } f.a //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰.a⁰ //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 :e :re f.a.b //│ ╔══[COMPILATION ERROR] Unresolved selection: //│ ║ l.45: f.a.b //│ ║ ^^^^^ //│ ╟── Type `123` does not contain member 'b' //│ ║ l.10: val a = 123 //│ ╙── ^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰.a⁰.b‹?› //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'b' yielded 'undefined' :sjs f.foo //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ f⁰.foo‹?›{ ~> Foo⁰.foo⁰(f⁰)‹app⁰› } //│ where //│ f⁰ <~ Foo() //│ f⁰ ~> {a: ⋅a⁰} {a: ⋅a¹} {foo: ⋅foo⁰} //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ runtime.safeCall(Foo1.foo(f)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 fun id(x) = x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun id⁰(x¹) ‹flow:id⁰› = x¹ //│ where //│ flow:id⁰ <~ ((x¹) -> x¹) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— id(f).foo //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ id⁰{ ~> id⁰ }(f⁰)‹app¹›.foo‹?›{ ~> Foo⁰.foo⁰(id⁰(f⁰)‹app¹›)‹app²› } //│ where //│ f⁰ <~ Foo() //│ f⁰ ~> {a: ⋅a⁰} {a: ⋅a¹} {foo: ⋅foo⁰} //│ f⁰ -> x¹ //│ app¹ <~ Foo() //│ app¹ ~> {foo: ⋅foo¹} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 let id(x) = x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let id¹, //│ id¹ = (x²) => x² //│ where //│ id¹ <~ ((x²) -> x²) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ id = fun id id(f).foo //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ id¹(f⁰)‹app³›.foo‹?›{ ~> Foo⁰.foo⁰(id¹(f⁰)‹app³›)‹app⁴› } //│ where //│ f⁰ <~ Foo() //│ f⁰ ~> {a: ⋅a⁰} {a: ⋅a¹} {foo: ⋅foo⁰} //│ f⁰ -> x¹ x² //│ id¹ <~ ((x²) -> x²) //│ id¹ ~> ((f⁰) -> app³) //│ app³ <~ Foo() //│ app³ ~> {foo: ⋅foo²} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 fun id(x) = x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun id²(x³) ‹flow:id¹› = x³ //│ where //│ flow:id¹ <~ ((x³) -> x³) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— id(0) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ id²{ ~> id² }(0)‹app⁵› //│ where //│ app⁵ <~ 0 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 // * Note the flow confusion due to lack of polymorphism: :e id(f).foo //│ ╔══[COMPILATION ERROR] Unresolved selection: //│ ║ l.133: id(f).foo //│ ║ ^^^^^^^^^ //│ ╟── Type `0` does not contain member 'foo' //│ ║ l.123: id(0) //│ ╙── ^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ id²{ ~> id² }(f⁰)‹app⁶›.foo‹?›{ ~> Foo⁰.foo⁰(id²(f⁰)‹app⁶›)‹app⁷› } //│ where //│ f⁰ <~ Foo() //│ f⁰ ~> {a: ⋅a⁰} {a: ⋅a¹} {foo: ⋅foo⁰} //│ f⁰ -> x¹ x² x³ //│ app⁶ <~ 0 Foo() //│ app⁶ ~> {foo: ⋅foo³} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 :e fun test(g) = g.foo //│ ╔══[COMPILATION ERROR] Cannot resolve selection //│ ║ l.153: fun test(g) = g.foo //│ ╙── ^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun test⁰(g⁰) ‹flow:test⁰› = g⁰.foo‹?› //│ where //│ g⁰ ~> {foo: ⋅foo⁴} //│ flow:test⁰ <~ ((g⁰) -> ⋅foo⁴) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :re test(f) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ test⁰{ ~> test⁰ }(f⁰)‹app⁸› //│ where //│ f⁰ <~ Foo() //│ f⁰ ~> {a: ⋅a⁰} {a: ⋅a¹} {foo: ⋅foo⁰} //│ f⁰ -> x¹ x² x³ g⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'foo' yielded 'undefined' fun test(g) = g.foo test(f) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ fun test¹(g¹) ‹flow:test¹› = g¹.foo‹?›{ ~> Foo⁰.foo⁰(g¹)‹app⁹› }, //│ test¹{ ~> test¹ }(f⁰)‹app¹⁰› //│ where //│ f⁰ <~ Foo() //│ f⁰ ~> {a: ⋅a⁰} {a: ⋅a¹} {foo: ⋅foo⁰} //│ f⁰ -> x¹ x² x³ g⁰ g¹ //│ g¹ <~ Foo() //│ g¹ ~> {foo: ⋅foo⁵} //│ flow:test¹ <~ ((g¹) -> ⋅foo⁵) //│ flow:test¹ ~> ((f⁰) -> app¹⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 module AA with module BB with class CC with val x: Int = 1 module CC with fun getX(self: CC) = self.x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ module AA { //│ module BB { //│ class CC { //│ val x⁴: Int⁰{ ~> Int⁰ } ‹flow:x⁰› = 1 //│ }, //│ module CC { //│ fun getX⁰(self⁰: ⟨BB⁰.⟩CC⁰) ‹flow:getX⁰› = self⁰.x‹?›{ ~> self⁰.x⁴ } //│ } //│ } //│ } //│ where //│ self⁰ <~ type CC //│ flow:x⁰ <~ 1 //│ flow:getX⁰ <~ ((type CC) -> flow:x⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs new AA.BB.CC().x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ new AA⁰.BB¹.CC⁰().x‹?›{ ~> new AA⁰.BB¹.CC⁰().x⁴ } //│ where //│ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp3; tmp3 = globalThis.Object.freeze(new AA1.BB.CC()); tmp3.x //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :sjs new AA.BB.CC().getX //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ new AA⁰.BB¹.CC⁰().getX‹?›{ ~> AA⁰.BB¹.CC⁰.getX⁰(new AA⁰.BB¹.CC⁰())‹app¹¹› } //│ where //│ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp4; tmp4 = globalThis.Object.freeze(new AA1.BB.CC()); runtime.safeCall(AA1.BB.CC.getX(tmp4)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 let foo = class A module A with fun test(a) = 1 new A //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let foo¹, //│ foo¹ = { //│ class A { //│ //│ }, //│ module A { //│ fun test²(a¹) ‹flow:test²› = 1 //│ }, //│ new A⁰ //│ } //│ where //│ foo¹ <~ A //│ flow:test² <~ ((a¹) -> 1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = A :e :re foo.test //│ ╔══[COMPILATION ERROR] Cannot access companion A from the context of this selection //│ ║ l.262: foo.test //│ ╙── ^^^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ foo¹.test‹?› //│ where //│ foo¹ <~ A //│ foo¹ ~> {test: ⋅test⁰} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'test' yielded 'undefined' let foo = class A module A with fun test(a) = new A (new A).test //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let foo², //│ foo² = { //│ class A { //│ //│ }, //│ module A { //│ fun test³(a²) ‹flow:test³› = new A¹ //│ }, //│ new A¹.test‹?›{ ~> A¹.test³(new A¹)‹app¹²› } //│ } //│ where //│ foo² <~ A //│ a² <~ A //│ flow:test³ <~ ((a²) -> A) //│ flow:test³ ~> ((A) -> ⋅test¹) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ foo = A :e :re foo.test //│ ╔══[COMPILATION ERROR] Cannot access companion A from the context of this selection //│ ║ l.301: foo.test //│ ╙── ^^^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ foo².test‹?› //│ where //│ foo² <~ A //│ foo² ~> {test: ⋅test²} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'test' yielded 'undefined' class CC(val x: Int) module CC with fun getX(self: CC) = self.x //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class CC(valx⁵: Int⁰{ ~> Int⁰ }) { //│ val x⁶ ‹flow:x¹› = x⁵ //│ }, //│ module CC { //│ fun getX¹(self¹: CC¹{ ~> CC¹ }) ‹flow:getX¹› = self¹.x‹?›{ ~> self¹.x⁶ } //│ } //│ where //│ x⁵ <~ type Int //│ x⁵ -> flow:x¹ //│ self¹ <~ type CC //│ flow:x¹ <~ type Int //│ flow:getX¹ <~ ((type CC) -> flow:x¹) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :fixme // TODO: handle lifted class defs: the JS is missing the `.class` access to get the static member `CC2.class.getX` CC(123).getX //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ CC¹(123)‹app¹³›.getX‹?›{ ~> CC¹.getX¹(CC¹(123)‹app¹³›)‹app¹⁴› } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: CC2.getX is not a function :breakme // TODO: warn when this happens (accidental infinite recursion) class CC module CC with fun oops(x: CC) = x.oops //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class CC { //│ //│ }, //│ module CC { //│ fun oops⁰(x⁷: CC²{ ~> CC² }) ‹flow:oops⁰› = x⁷.oops‹?›{ ~> CC².oops⁰(x⁷)‹app¹⁵› } //│ } //│ where //│ x⁷ <~ type CC //│ x⁷ ~> {oops: ⋅oops⁰} //│ flow:oops⁰ <~ ((type CC) -> ⋅oops⁰) //│ flow:oops⁰ ~> ((CC) -> ⋅oops⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :re new CC().oops //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ new CC²().oops‹?›{ ~> CC².oops⁰(new CC²())‹app¹⁶› } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded class CC with val okay = 123 module CC with fun okay(x: CC) = x.okay //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class CC { //│ val okay⁰ ‹flow:okay⁰› = 123 //│ }, //│ module CC { //│ fun okay¹(x⁸: CC³{ ~> CC³ }) ‹flow:okay¹› = x⁸.okay‹?›{ ~> x⁸.okay⁰ } //│ } //│ where //│ x⁸ <~ type CC //│ flow:okay⁰ <~ 123 //│ flow:okay¹ <~ ((type CC) -> flow:okay⁰) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— new CC().okay //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ new CC³().okay‹?›{ ~> new CC³().okay⁰ } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 module Example with class Test(val field: Int) module Test with fun next(self: Test) = Test(self.field + 1) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ module Example { //│ class Test(valfield⁰: Int⁰{ ~> Int⁰ }) { //│ val field¹ ‹flow:field⁰› = field⁰ //│ }, //│ module Test { //│ fun next⁰(self²: ⟨Example⁰.⟩Test⁰) ‹flow:next⁰› = ⟨Example⁰.⟩Test⁰(+(self².field¹, 1)‹app¹⁷›)‹app¹⁸› //│ } //│ } //│ where //│ field⁰ <~ type Int //│ field⁰ -> flow:field⁰ //│ self² <~ type Test //│ flow:field⁰ <~ type Int //│ flow:next⁰ <~ ((type Test) -> app¹⁸) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let e = new Example.Test(123) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let e⁰, //│ e⁰ = new Example¹.Test⁰(123) //│ where //│ e⁰ <~ Test(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ e = Test(123) :fixme // Same as test case above (missing `.class` access) e.next //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ e⁰.next‹?›{ ~> Example¹.Test⁰.next⁰(e⁰)‹app¹⁹› } //│ where //│ e⁰ <~ Test(123) //│ e⁰ ~> {next: ⋅next⁰} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: selRes7.next is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/SelExpansionImport.mls ================================================ :js :flow // :df :global :sf import "../../mlscript-compile/Example.mls" //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ import ".../Example.mjs" as Example⁰ //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let e = new Example.Test(123) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let e⁰, //│ e⁰ = new Example⁰.Test⁰(123) //│ where //│ e⁰ <~ Test(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ e = Test(123) e.field //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ e⁰.field⁰ //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 // TODO: support :e :re e.next //│ ╔══[COMPILATION ERROR] Cannot access companion Test from the context of this selection //│ ║ l.37: e.next //│ ╙── ^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ e⁰.next‹?› //│ where //│ e⁰ <~ Test(123) //│ e⁰ ~> {field: ⋅field⁰} {next: ⋅next⁰} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'next' yielded 'undefined' open Example //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let e = new Test(123) //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let e¹, //│ e¹ = new ⟨Example⁰.⟩Test⁰(123) //│ where //│ e¹ <~ Test(123) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ e = Test(123) // TODO: support :e :re e.next //│ ╔══[COMPILATION ERROR] Cannot access companion Test from the context of this selection //│ ║ l.69: e.next //│ ╙── ^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ e¹.next‹?› //│ where //│ e¹ <~ Test(123) //│ e¹ ~> {next: ⋅next¹} //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'next' yielded 'undefined' // * TODO: better error! :e let e = Example.Test(123) //│ ╔══[COMPILATION ERROR] Cannot use non-term member Example in term position //│ ║ l.85: let e = Example.Test(123) //│ ╙── ^^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let e², //│ e² = Example⁰.Test⁰(123)‹app⁰› //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ e = Test(123) // * TODO: better error! (with location) :e let e = Test(123) //│ ═══[COMPILATION ERROR] Cannot use non-term member Example in term position //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ let e³, //│ e³ = ⟨Example⁰.⟩Test⁰(123)‹app¹› //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ e = Test(123) :fixme // FIXME: error is probably due to some `bms.asTrm` weirdness (guess: due to lightweight elab?) Example.t //│ ╔══[COMPILATION ERROR] Cannot use non-term member Example in term position //│ ║ l.111: Example.t //│ ╙── ^^^^^^^ //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Test(123) // * Note that here, it's important that we don't get the flow symbol attached to BlockMemberSymbol `t` // * (which should be internal to Example.mls, otherwise we'd mutate it nondeterministically across different imports), // * but rather some inferred Type view of it, which we can then use to resolve the selection. :fixme // TODO: should get an inferred Type view of the field Example.t.field //│ ╔══[COMPILATION ERROR] Cannot use non-term member Example in term position //│ ║ l.126: Example.t.field //│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Unresolved selection: //│ ║ l.126: Example.t.field //│ ║ ^^^^^^^^^^^^^^^ //│ ╙── Type `type ⊤` does not contain member 'field' //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ }.field‹?› //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 :fixme // Same Example.t.next //│ ╔══[COMPILATION ERROR] Cannot use non-term member Example in term position //│ ║ l.142: Example.t.next //│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Unresolved selection: //│ ║ l.142: Example.t.next //│ ║ ^^^^^^^^^^^^^^ //│ ╙── Type `type ⊤` does not contain member 'next' //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ Example⁰{ ~> Example⁰ }.t⁰{ ~> Example⁰.t⁰ }.next‹?› //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Access to required field 'next' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/flows/TypeFlows.mls ================================================ :js :flow :global :sf class Foo[A] //│ —————————————————| Flowed |————————————————————————————————————————————————————————————————————————— //│ class Foo[A⁰] { //│ //│ } //│ where //│ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :fixme fun f(x: Foo[Int]) = x.foo //│ /!!!\ Uncaught error: scala.NotImplementedError: List(Ref(class:Int,List())) (of class $colon$colon) :fixme fun f[A](x: Foo[A]) = x.foo //│ /!!!\ Uncaught error: scala.NotImplementedError: List(Ref(A,List())) (of class $colon$colon) ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls ================================================ :js :effectHandlers :lift :re handle h = Object with {} h.oops //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' handle h = Object with {} val x = 1 handle h = Object with {} val x = 1 x + 1 //│ = 2 :e x //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.20: x //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // Getter // :expect 3 :e handle h = Object with fun lol(k) = k(()) h.lol //│ ╔══[COMPILATION ERROR] Handler function cannot be a getter //│ ║ l.31: fun lol(k) = k(()) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Effect Handler$h$6 is raised in a getter fun lol(k) = k(1) h.lol + 2 //│ ╔══[COMPILATION ERROR] Name not found: h //│ ║ l.38: h.lol + 2 //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re handle h = Object with fun lol()(k) = k(1) class A with fun f = h.lol() new A().f + 2 //│ ═══[RUNTIME ERROR] Error: Effect Handler$h$8 is raised in a getter handle h = Object with fun lol()(k) = k(42) h.lol() val x = 1 object Foo with handle h = Object with fun lol()(k) = k(42) val x = h.lol() x + 1 :e :re Foo.x //│ ╔══[COMPILATION ERROR] Object 'Foo' does not contain member 'x' //│ ║ l.66: Foo.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/Debugging.mls ================================================ :js :effectHandlers debug :lift :re raiseUnhandledEffect() //│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect //│ at tail position :sjs fun f() = let i = 0 let j = 100 let k = 2000 if i == 0 do print of raiseUnhandledEffect() j / i //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f, f$debugInfo; //│ runtime.resetEffects(); //│ f$debugInfo = globalThis.Object.freeze([ //│ "f", //│ 0, //│ "i", //│ 1, //│ "j", //│ 2, //│ "k" //│ ]); //│ f = function f() { //│ let i, j, k, scrut, tmp1, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ let saveOffset; //│ pc = runtime.resumePc; //│ saveOffset = runtime.resumeIdx; //│ i = runtime.resumeArr.at(saveOffset); //│ saveOffset = saveOffset + 1; //│ j = runtime.resumeArr.at(saveOffset); //│ saveOffset = saveOffset + 1; //│ k = runtime.resumeArr.at(saveOffset); //│ saveOffset = saveOffset + 1; //│ scrut = runtime.resumeArr.at(saveOffset); //│ saveOffset = saveOffset + 1; //│ tmp1 = runtime.resumeArr.at(saveOffset); //│ runtime.resumePc = -1; //│ } //│ main: while (true) { //│ switch (pc) { //│ case 0: //│ i = 0; //│ j = 100; //│ k = 2000; //│ runtime.resumeValue = Predef.equals(i, 0); //│ if (runtime.curEffect === null) { //│ pc = 5; //│ } else { //│ return runtime.unwind(f, 5, "Debugging.mls:17:6", f$debugInfo, null, 1, 0, 5, i, j, k, scrut, tmp1) //│ } //│ case 5: //│ scrut = runtime.resumeValue; //│ if (scrut === true) { //│ runtime.resumeValue = Predef.raiseUnhandledEffect(); //│ if (runtime.curEffect === null) { //│ pc = 4; //│ continue main //│ } //│ return runtime.unwind(f, 4, "Debugging.mls:18:14", f$debugInfo, null, 1, 0, 5, i, j, k, scrut, tmp1); //│ } //│ pc = 1; //│ continue main; //│ case 4: //│ tmp1 = runtime.resumeValue; //│ pc = 3; //│ case 3: //│ runtime.resumeValue = Predef.print(tmp1); //│ if (runtime.curEffect === null) { //│ pc = 2; //│ } else { //│ return runtime.unwind(f, 2, "Debugging.mls:18:5", f$debugInfo, null, 1, 0, 5, i, j, k, scrut, tmp1) //│ } //│ case 2: //│ runtime.resumeValue; //│ pc = 1; //│ case 1: //│ return j / i; //│ } //│ break; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :re f() //│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect //│ at f (Debugging.mls:18:14) :re fun lambda_test(f) = f() 200 lambda_test(() => raiseUnhandledEffect() 100) //│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect //│ at lambda (Debugging.mls:106:3) //│ at lambda_test (Debugging.mls:103:3) import "../../mlscript-compile/Runtime.mls" :silent let res = Runtime.try(f) res.reified.contTrace.next.getLocals //│ = [LocalVarInfo("i", 0), LocalVarInfo("j", 100), LocalVarInfo("k", 2000)] Runtime.debugEff(res.reified) //│ > Debug EffectSig: //│ > handler: FatalEffect //│ > handlerFun: null //│ > resumed: false //│ > //│ > FunctionContFrame(pc=4, last) -> (null) //│ > :expect Infinity res.resumeWith(42) //│ > 42 //│ = Infinity class Debugger() with fun break(payload) module Test with fun test(j)(using dbg: Debugger) = let i = 0 let k = 2000 if i == 0 do set i = dbg.break("whoops") j / i fun main()(using Debugger) = test(12) + test(34) let res = handle h = Debugger with fun break(payload)(resume) = resume using Debugger = h Runtime.try(() => Test.main()) //│ res = EffectHandle(_) :re res.raise() //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ //│ at Test.test (Debugging.mls:145:15) //│ at Test.main (Debugging.mls:148:5) set res = res.resumeWith(42) :re res.raise() //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ //│ at Test.test (Debugging.mls:145:15) //│ at Test.main (Debugging.mls:148:16) :expect 0.33676533676533676 res.resumeWith(666) //│ = 0.33676533676533676 let i = 100 fun f() = // Currently debug.getLocals is not supported. // print(debug.getLocals()) let j = 200 debug.printStack(true) set j = 300 debug.printStack(false) debug.printStack(true) // print(debug.getLocals()) //│ i = 100 f() //│ > Stack Trace: //│ > at f (Debugging.mls:180:3) with locals: j=200 //│ > Stack Trace: //│ > at f (Debugging.mls:182:3) //│ > Stack Trace: //│ > at tail position ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls ================================================ :js :effectHandlers :lift class PrintEffect with fun p(s: String): () fun aux(s: String): () :e // :sjs handle h = PrintEffect with fun p(x)(k) = print(x) k(()) fun aux(x)(k) = h.p(x) k(()) h.p(x) h.aux(3) //│ ╔══[COMPILATION ERROR] Name not found: h //│ ║ l.16: h.p(x) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: h //│ ║ l.18: h.p(x) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. handle h1 = PrintEffect with fun p(x)(k) = print(x) k(()) fun aux(x)(k) = k(()) handle h2 = PrintEffect with fun p(x)(k) = print(x) k(()) fun aux(x)(k) = h1.p(x) k(()) h1.p(x) h1.p(4) h2.aux(3) h1.p(4) //│ > 4 //│ > 3 //│ > 4 //│ > 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/Effects.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(arg: Str): Str handle h = Effect with fun perform()(k) = "b" :expect 42 handle h = Effect with fun perform()(k) = k(42) h.perform() //│ = 42 :expect "b" handle h = Effect with fun perform(arg)(k) = "b" h.perform("k") //│ = "b" :expect "k" handle h = Effect with fun perform(arg)(k) = arg h.perform("k") //│ = "k" :expect "k" handle h = Effect with fun perform(arg)(k) = k(arg) h.perform("k") //│ = "k" :expect "hi" fun foo(e) = e.perform("hi") handle h = Effect with fun perform(arg)(k) = print(arg) k(arg) foo(h) //│ > hi //│ = "hi" fun foo(e) = e.perform("hi") :expect "Hello World!" handle h = Effect with fun perform(arg)(k) = print(arg) k(arg) (() => h.perform("Hello") + " World!")() //│ > Hello //│ = "Hello World!" :expect "54321" :sjs let result = "" handle h = Effect with fun perform(arg)(k) = let v = k(arg) result = result + arg v in h.perform("1") h.perform("2") h.perform("3") h.perform("4") h.perform("5") result //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let result, h7, handleBlock$7, Handler$h$perform7, Handler$h$15, Handler$h$perform$4; //│ runtime.resetEffects(); //│ Handler$h$perform$4 = function Handler$h$perform$(arg) { //│ return (k) => { //│ return Handler$h$perform7(arg, k) //│ } //│ }; //│ Handler$h$perform7 = function Handler$h$perform(arg, k) { //│ let v, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ runtime.resumeValue = runtime.safeCall(k(arg)); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(Handler$h$perform7, 1, "Effects.mls:63:13", null, null, 1, 2, arg, k, 0) //│ } //│ case 1: //│ v = runtime.resumeValue; //│ result = result + arg; //│ return v; //│ } //│ }; //│ result = ""; //│ (class Handler$h$14 extends Effect1 { //│ static { //│ Handler$h$15 = this //│ } //│ constructor() { //│ let tmp7; //│ tmp7 = super(); //│ if (runtime.curEffect === null) { //│ tmp7 //│ } else { //│ tmp7 = runtime.illegalEffect("in a constructor"); //│ tmp7 //│ } //│ } //│ perform(arg) { //│ let Handler$h$perform$here; //│ Handler$h$perform$here = Handler$h$perform$4(arg); //│ return runtime.mkEffect(this, Handler$h$perform$here) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Handler$h$"]; //│ }); //│ h7 = new Handler$h$15(); //│ handleBlock$7 = (undefined, function () { //│ let pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ runtime.resumeValue = runtime.safeCall(h7.perform("1")); //│ if (runtime.curEffect === null) { //│ pc = 4; //│ } else { //│ return runtime.unwind(handleBlock$7, 4, "Effects.mls:67:3", null, null, 1, 0, 0) //│ } //│ case 4: //│ runtime.resumeValue; //│ runtime.resumeValue = runtime.safeCall(h7.perform("2")); //│ if (runtime.curEffect === null) { //│ pc = 3; //│ } else { //│ return runtime.unwind(handleBlock$7, 3, "Effects.mls:68:3", null, null, 1, 0, 0) //│ } //│ case 3: //│ runtime.resumeValue; //│ runtime.resumeValue = runtime.safeCall(h7.perform("3")); //│ if (runtime.curEffect === null) { //│ pc = 2; //│ } else { //│ return runtime.unwind(handleBlock$7, 2, "Effects.mls:69:3", null, null, 1, 0, 0) //│ } //│ case 2: //│ runtime.resumeValue; //│ runtime.resumeValue = runtime.safeCall(h7.perform("4")); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(handleBlock$7, 1, "Effects.mls:70:3", null, null, 1, 0, 0) //│ } //│ case 1: //│ runtime.resumeValue; //│ return runtime.safeCall(h7.perform("5")); //│ } //│ }); //│ runtime.enterHandleBlock(h7, handleBlock$7); //│ if (runtime.curEffect === null) { result } else { runtime.topLevelEffect(false); result } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "54321" //│ result = "54321" :expect "b" handle h = Effect with fun perform(arg)(k) = print(arg) "b" h.perform("t") //│ > t //│ = "b" :expect 3 handle h = Effect with fun perform(arg)(k) = print(arg) let r = k(true) print(arg) r if false || h.perform("hello") then 3 else 0 //│ > hello //│ > hello //│ = 3 :expect "Hello World!" fun foo(h) = print("Entering foo") let result = h.perform("Hello") h.perform("?") print("Exiting foo") result handle h = Effect with fun perform(arg)(k) = print("handler called") let result = k(arg) print("handler finished") result print("mainbody start") let result = foo(h) + " World!" print("mainbody end") result //│ > mainbody start //│ > Entering foo //│ > handler called //│ > handler called //│ > Exiting foo //│ > mainbody end //│ > handler finished //│ > handler finished //│ = "Hello World!" :expect "Hello World!" fun foo(h) = print("Entering foo") let result = h.perform("Hello") h.perform("Oops!") print("Exiting foo") result fun bar(h) = print("Entering bar") let result = foo(h) + " World" print("Exiting bar") result fun foobar(h) = print("Entering foobar") let result = bar(h) + "!" print("Exiting foobar") result handle h = Effect with fun perform(arg)(k) = print("handler called") let result = k(arg) print("handler finished") result print("mainbody start") let result = foobar(h) print("mainbody end") result //│ > mainbody start //│ > Entering foobar //│ > Entering bar //│ > Entering foo //│ > handler called //│ > handler called //│ > Exiting foo //│ > Exiting bar //│ > Exiting foobar //│ > mainbody end //│ > handler finished //│ > handler finished //│ = "Hello World!" // Float out definitions handle h = Effect with fun perform(arg)(k) = arg if true do fun f() = 3 print(f()) //│ > 3 module A with data class Effect(x) with fun test() = x fun perform(arg) handle h = A.Effect(3) with fun perform()(k) = 0 h.perform() //│ = 0 fun f(perform) = handle h = Effect with fun perform(arg)(k) = arg perform() f(() => 3) //│ = 3 abstract class Cell with fun getVal(): Int fun setVal(Int): () let x = 0 handle h = Cell with fun getVal()(k) = k(x) fun setVal(value)(k) = x = value k(()) print(h.getVal()) h.setVal(1) print(h.getVal()) //│ > 0 //│ > 1 //│ x = 1 abstract class Eff with fun get(): Int :expect 120 fun fact(e, factvalue) = if factvalue != 0 then factvalue * fact(e, factvalue-1) else e.get() handle h = Eff with fun get()(k) = k(1) fact(h, 5) //│ = 120 abstract class StackDelay with fun raise(): () // stack safe recursion :expect 5050 handle h = StackDelay with fun raise()(k) = // console.trace("Stack unwinded!") k(10) fun sum(depth, x) = let new_depth = if depth > 70 then // console.trace("Too deep, heapifying the stack") h.raise() else depth + 1 if x != 0 then x + sum(new_depth, x - 1) else 0 sum(0, 100) //│ = 5050 // stack safe recursion :expect 450015000 handle h = StackDelay with fun raise()(k) = k(10) fun sum(depth, x) = let new_depth = if depth > 1000 then h.raise() else depth + 1 if x != 0 then x + sum(new_depth, x - 1) else 0 sum(0, 30000) //│ = 450015000 :re fun sum(x) = if x != 0 then x + sum(x - 1) else 0 sum(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :expect 2 fun foo(h) = h.perform() if true then fun g() = 2 g() else fun g() = 3 g() handle h = Eff with fun perform()(k) = k(()) foo(h) //│ = 2 :expect 3 fun foo(h) = h.perform() if false then fun g() = 2 g() else fun g() = 3 g() handle h = Eff with fun perform()(k) = k(()) foo(h) //│ = 3 :expect 123 fun foo(h) = h.perform() fun A() = 1 let a = if true then class A() with fun f() = 2 A().f() else class A() with fun f() = 3 A().f() let b = if false then class A() with fun f() = 2 A().f() else class A() with fun f() = 3 A().f() A() * 100 + a * 10 + b handle h = Eff with fun perform()(k) = k(()) foo(h) //│ = 123 :fixme :expect 123 fun foo(h) = h.perform() fun A() = 1 let a = if true then module A with fun f() = 2 A.f() else module A with fun f() = 3 A.f() let b = if false then module A with fun f() = 2 A.f() else module A with fun f() = 3 A.f() A() * 100 + a * 10 + b handle h = Eff with fun perform()(k) = k(()) foo(h) //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.440: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.436: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.432: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.428: module A with //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.428: module A with //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.432: module A with //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.436: module A with //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.440: module A with //│ ╙── ^ //│ = 123 // Access superclass fields handle h1 = Object with fun perf()(k) = k(2) class A with val x = 2 handle h2 = A with fun perf()(k) = k(this.x) h2.perf() //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls ================================================ :js :effectHandlers :lift module M // * Notice that the two module definitions are lifted to the top of the block. :ssjs :fixme fun foo(h): module M = if false then module A A else module A A //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.15: module A //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.12: module A //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.12: module A //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.15: module A //│ ╙── ^ //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let foo; //│ runtime.checkCall(runtime.resetEffects()); //│ foo = function foo(h) { //│ runtime.checkArgs("foo", 1, true, arguments.length); //│ let A2, A3, scrut; //│ scrut = false; //│ if (scrut === true) { //│ globalThis.Object.freeze(class A { //│ static { //│ A2 = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); //│ return A2 //│ } //│ globalThis.Object.freeze(class A1 { //│ static { //│ A3 = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); //│ return A3; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(arg: Str): Str :sjs data class Lol(h) with print(h.perform("k")) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Lol1; //│ runtime.resetEffects(); //│ Lol1 = function Lol(h) { //│ return globalThis.Object.freeze(new Lol.class(h)); //│ }; //│ (class Lol { //│ static { //│ Lol1.class = this //│ } //│ constructor(h) { //│ let tmp; //│ this.h = h; //│ tmp = runtime.safeCall(this.h.perform("k")); //│ if (runtime.curEffect !== null) { //│ tmp = runtime.illegalEffect("in a constructor"); //│ } //│ Predef.print(tmp); //│ if (runtime.curEffect !== null) { //│ runtime.illegalEffect("in a constructor"); //│ } //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Lol", ["h"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * This is a weird use of effect handlers where an effect is raised during the construction of a handler. // * The most natural semantics (following what normal constructors currently do) // * is for the handler to not yet be constructed but to be constructed and executed normally after resumption. // * See full discussion at: https://github.com/hkust-taco/mlscript/pull/268#discussion_r1938523085 :fixme handle h = Effect with fun perform(arg)(k) = print(arg) k() abstract class Eff with fun test() h.perform("x") handle k = Eff with fun test()(k) = k() k.test() print("test") //│ ═══[RUNTIME ERROR] Error: Effect Handler$h$ is raised in a constructor :fixme let oops = handle h = Effect with fun perform(arg)(k) = print(arg) "b" Lol(h) //│ ═══[RUNTIME ERROR] Error: Effect Handler$h$2 is raised in a constructor //│ oops = undefined :fixme :expect "b" oops //│ ═══[RUNTIME ERROR] Expected: '"b"', got: 'undefined' :fixme let oops = handle h = Effect with fun perform(arg)(k) = print(arg) "b" new Lol(h) //│ ═══[RUNTIME ERROR] Error: Effect Handler$h$4 is raised in a constructor //│ oops = undefined :fixme :expect "b" oops //│ ═══[RUNTIME ERROR] Expected: '"b"', got: 'undefined' :fixme let oops = handle h = Effect with fun perform(arg)(k) = print(arg) k("b") Lol(h) //│ ═══[RUNTIME ERROR] Error: Effect Handler$h$6 is raised in a constructor //│ oops = undefined :fixme oops.h //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'h') let obj = mut {s: undefined} //│ obj = {s: undefined} handle h = Effect with fun perform(arg)(k) = k() print(arg) set obj.s = h.perform("k") //│ > k fun f() = () module A with f() () // defeats tail call optimization let obj = new Object with fun foo() = print("Hello") foo() //│ > Hello //│ obj = $anon ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/EffectsInMethods.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(arg: Str): Str class Test with fun p(h, x) = h.perform("A", x) + h.perform("B", x + 1) let t = new Test //│ t = Test handle h = Effect with fun perform(arg, x)(k) = print(arg) k(x * 2) [t.p(h, 2), t.p(h, 3)] //│ > A //│ > B //│ > A //│ > B //│ = [10, 14] ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/Fallthrough.mls ================================================ :js :effectHandlers :lift fun g() = 0 :sjs fun f() = let x if 0 is 0 do set x = g() + g() set x = g() set x = g() set x = g() set x = g() return x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; //│ runtime.resetEffects(); //│ f = function f() { //│ let x, scrut, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ let saveOffset; //│ pc = runtime.resumePc; //│ saveOffset = runtime.resumeIdx; //│ tmp = runtime.resumeArr.at(saveOffset); //│ runtime.resumePc = -1; //│ } //│ main: while (true) { //│ switch (pc) { //│ case 7: //│ tmp = runtime.resumeValue; //│ runtime.resumeValue = g(); //│ if (runtime.curEffect === null) { //│ pc = 6; //│ } else { //│ return runtime.unwind(f, 6, "Fallthrough.mls:11:19", null, null, 1, 0, 1, tmp) //│ } //│ case 6: //│ tmp1 = runtime.resumeValue; //│ tmp2 = tmp + tmp1; //│ x = tmp2; //│ pc = 5; //│ case 5: //│ runtime.resumeValue = g(); //│ if (runtime.curEffect === null) { //│ pc = 4; //│ } else { //│ return runtime.unwind(f, 4, "Fallthrough.mls:12:11", null, null, 1, 0, 1, tmp) //│ } //│ case 4: //│ tmp3 = runtime.resumeValue; //│ x = tmp3; //│ runtime.resumeValue = g(); //│ if (runtime.curEffect === null) { //│ pc = 3; //│ } else { //│ return runtime.unwind(f, 3, "Fallthrough.mls:13:11", null, null, 1, 0, 1, tmp) //│ } //│ case 3: //│ tmp4 = runtime.resumeValue; //│ x = tmp4; //│ runtime.resumeValue = g(); //│ if (runtime.curEffect === null) { //│ pc = 2; //│ } else { //│ return runtime.unwind(f, 2, "Fallthrough.mls:14:11", null, null, 1, 0, 1, tmp) //│ } //│ case 2: //│ tmp5 = runtime.resumeValue; //│ x = tmp5; //│ runtime.resumeValue = g(); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(f, 1, "Fallthrough.mls:15:11", null, null, 1, 0, 1, tmp) //│ } //│ case 1: //│ tmp6 = runtime.resumeValue; //│ x = tmp6; //│ return x; //│ case 0: //│ scrut = 0; //│ if (scrut === 0) { //│ runtime.resumeValue = g(); //│ if (runtime.curEffect === null) { //│ pc = 7; //│ continue main //│ } //│ return runtime.unwind(f, 7, "Fallthrough.mls:11:13", null, null, 1, 0, 1, tmp); //│ } //│ pc = 5; //│ continue main; //│ } //│ break; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/GeneratorStack.mls ================================================ :js :effectHandlers :lift import "../../mlscript-compile/Stack.mls" open Stack class Generator with fun produce(result: Stack[Int]): () fun (++) concat(l1, l2) = if l1 is Nil then l2 Cons(h, t) then Cons(h, t ++ l2) fun (:+) append(l, e) = if l is Nil then e :: Nil Cons(h, t) then h :: t :+ e fun length(l) = if l is Nil then 0 Cons(_, t) then 1 + length(t) fun permutations_impl(gen, l1, l2) = if l2 is Nil and l1 is Nil then gen.produce(Nil) else () Cons(f, t) then handle gen2 = Generator with fun produce(result)(resume) = gen.produce(f :: result) resume(()) permutations_impl(gen2, Nil, l1 ++ t) permutations_impl(gen, l1 :+ f, t) fun permutations(gen, l) = permutations_impl(gen, Nil, l) let result = handle gen = Generator with fun produce(result)(resume) = print(result) let r = resume(()) result :: r permutations(gen, 1 :: 2 :: 3 :: 4 :: Nil), Nil in length(result) //│ > Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) //│ > Cons(1, Cons(2, Cons(4, Cons(3, Nil)))) //│ > Cons(1, Cons(3, Cons(2, Cons(4, Nil)))) //│ > Cons(1, Cons(3, Cons(4, Cons(2, Nil)))) //│ > Cons(1, Cons(4, Cons(2, Cons(3, Nil)))) //│ > Cons(1, Cons(4, Cons(3, Cons(2, Nil)))) //│ > Cons(2, Cons(1, Cons(3, Cons(4, Nil)))) //│ > Cons(2, Cons(1, Cons(4, Cons(3, Nil)))) //│ > Cons(2, Cons(3, Cons(1, Cons(4, Nil)))) //│ > Cons(2, Cons(3, Cons(4, Cons(1, Nil)))) //│ > Cons(2, Cons(4, Cons(1, Cons(3, Nil)))) //│ > Cons(2, Cons(4, Cons(3, Cons(1, Nil)))) //│ > Cons(3, Cons(1, Cons(2, Cons(4, Nil)))) //│ > Cons(3, Cons(1, Cons(4, Cons(2, Nil)))) //│ > Cons(3, Cons(2, Cons(1, Cons(4, Nil)))) //│ > Cons(3, Cons(2, Cons(4, Cons(1, Nil)))) //│ > Cons(3, Cons(4, Cons(1, Cons(2, Nil)))) //│ > Cons(3, Cons(4, Cons(2, Cons(1, Nil)))) //│ > Cons(4, Cons(1, Cons(2, Cons(3, Nil)))) //│ > Cons(4, Cons(1, Cons(3, Cons(2, Nil)))) //│ > Cons(4, Cons(2, Cons(1, Cons(3, Nil)))) //│ > Cons(4, Cons(2, Cons(3, Cons(1, Nil)))) //│ > Cons(4, Cons(3, Cons(1, Cons(2, Nil)))) //│ > Cons(4, Cons(3, Cons(2, Cons(1, Nil)))) //│ = 24 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/Generators.mls ================================================ :js :effectHandlers :lift abstract class Generator with fun produce(result): () fun permutations_impl(gen, l1, l2) = if l2 is [f, ...t] do handle genWithPrefix = Generator with fun produce(result)(resume) = result.unshift(f) gen.produce(result) resume(()) permutations_impl(genWithPrefix, mut [], l1.concat(t)) l1.push(f) permutations_impl(gen, l1, t) [] and l1 is [] do gen.produce(mut []) fun permutations(gen, l) = permutations_impl(gen, mut [], l) let res = mut [] handle gen = Generator with fun produce(result)(resume) = res.push(result) resume(()) in permutations(gen, [1, 2, 3, 4]) //│ res = [ //│ [1, 2, 3, 4], //│ [1, 2, 4, 3], //│ [1, 3, 2, 4], //│ [1, 3, 4, 2], //│ [1, 4, 2, 3], //│ [1, 4, 3, 2], //│ [2, 1, 3, 4], //│ [2, 1, 4, 3], //│ [2, 3, 1, 4], //│ [2, 3, 4, 1], //│ [2, 4, 1, 3], //│ [2, 4, 3, 1], //│ [3, 1, 2, 4], //│ [3, 1, 4, 2], //│ [3, 2, 1, 4], //│ [3, 2, 4, 1], //│ [3, 4, 1, 2], //│ [3, 4, 2, 1], //│ [4, 1, 2, 3], //│ [4, 1, 3, 2], //│ [4, 2, 1, 3], //│ [4, 2, 3, 1], //│ [4, 3, 1, 2], //│ [4, 3, 2, 1] //│ ] fun permutations_foreach(l, f) = handle gen = Generator with fun produce(result)(resume) = f(result) resume(()) permutations(gen, l) permutations_foreach([1, 2, 3], print) //│ > [1, 2, 3] //│ > [1, 3, 2] //│ > [2, 1, 3] //│ > [2, 3, 1] //│ > [3, 1, 2] //│ > [3, 2, 1] fun permutations_impl(gen, l1, l2) = if l2 is [f, ...t] do handle genWithPrefix = Generator with fun produce(result)(resume) = gen.produce([f, ...result]) let x = resume(()) x permutations_impl(genWithPrefix, mut [], l1.concat(t)) l1.push(f) permutations_impl(gen, l1, t) [] and l1 is [] do gen.produce([]) fun permutations(gen, l) = permutations_impl(gen, mut [], l) let res = mut [] handle gen = Generator with fun produce(result)(resume) = res.push(result) let x = resume(()) x in permutations(gen, [1, 2, 3]) //│ res = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/HandlerBlockBindings.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(arg: Str): Str // non-resumptive handlers handle h = Effect with fun perform(arg)(k) = "A" h.perform("B") let x = 123 //│ = "A" :e x //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.19: x //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. handle h = Effect with fun perform(arg)(k) = print(arg) let x = 123 x + 1 //│ = 124 :e x //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.34: x //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. handle h = Effect with fun perform(arg)(k) = print(arg) val y = 123 :e y //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.47: y //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/HandlerReset.mls ================================================ :js :effectHandlers :lift import "../../mlscript-compile/Runtime.mls" set Runtime.resumePc = 100 :expect -1 Runtime.resumePc //│ = -1 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(arg: Str): Str // * Note that the `val x` binding is in the handler's own body and not a field of the object... // * It could not possibly be, because the handler might not resume the rest of the object's constructor, // * so `x` might never get initialized even though the object might be returned. object Lol with handle h = Effect with fun perform(arg)(k) = print(arg) "B" print(h.perform("A")) val x = 123 //│ > A :e :re Lol.x //│ ╔══[COMPILATION ERROR] Object 'Lol' does not contain member 'x' //│ ║ l.25: Lol.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' object Lol with handle h = Effect with fun perform(arg)(k) = print(arg) k("B") print(h.perform("A")) val x = 123 //│ > A //│ > B :e :re Lol.x //│ ╔══[COMPILATION ERROR] Object 'Lol' does not contain member 'x' //│ ║ l.44: Lol.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' data class Lol() with handle h = Effect with fun perform(arg)(k) = print(arg) "B" print(h.perform("A")) val x = 123 // * Note: does NOT return a partially initialized object let oops = Lol() //│ > A //│ oops = Lol() :re oops.x //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/HandlersInMethods.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(arg: Str): Str class Test with fun p(x) = handle h = Effect with fun perform(arg, x)(k) = print(arg) k(x * 2) h.perform("A", x) + h.perform("B", x + 1) let t = new Test //│ t = Test [t.p(2), t.p(3)] //│ > A //│ > B //│ > A //│ > B //│ = [10, 14] ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls ================================================ :js :effectHandlers :lift abstract class Effect ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/LeakingEffects.mls ================================================ :js :effectHandlers :lift abstract class Effect[A] with fun perform(arg: A): A let xs = mut [] count = 0 fun main() = handle g = Effect with fun perform(arg1)(k1) = print("B " + arg1) xs.push(k1) print(g.perform("a")) print("—") print(g.perform("b")) print("—") print(g.perform("c")) main() while do print(xs) let f = xs.pop() f !== () then set count += 1 f(count) print("?") //│ > B a //│ > [fun] //│ > 1 //│ > — //│ > B b //│ > ? //│ > [fun] //│ > 2 //│ > — //│ > B c //│ > ? //│ > [fun] //│ > 3 //│ > ? //│ > [] //│ count = 3 //│ xs = [] // * Notes: (mut []).pop() === undefined //│ = false (mut []).pop() === null //│ = false (mut []).pop() === () //│ = true // g is non-resumptive, which means g.perform("a") does not resume // hence it should exit the entire handle block with the return value of xs.push(k1) :expect 1 handle h = Effect with fun perform(arg)(k) = print("A " + arg) let xs = mut [] count = 0 handle g = Effect with fun perform(arg1)(k1) = print("B " + arg + " " + arg1) xs.push(k1) k(g) while xs.pop() is null then print("Done.") f then set count += 1 f(count) let g = h.perform("hi") print(g.perform("a")) print("—") print(g.perform("b")) print("—") print(g.perform("c")) // set g = h.perform("hola") // g.perform("adios") //│ > A hi //│ > B hi a //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/ManualEffectBinding.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun perform(): () let h1 = null handle h = Effect with fun perform()(k) = print("perform") k() print("perform-end") set h1 = h //│ h1 = Handler$h$ :re h1.perform() //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ enterHandleBlock(h1, () => h1.perform()) //│ > perform //│ > perform-end let h2 = null handle h = Effect with fun perform()(k) = print("perform2") k() print("perform-end2") set h2 = h //│ h2 = Handler$h$ // This causes the scenario where next is null but nextHandler is not null in the runtime enterHandleBlock(h1, () => enterHandleBlock(h2, () => h1.perform() h2.perform() h1.perform() )) //│ > perform //│ > perform2 //│ > perform //│ > perform-end2 //│ > perform-end //│ > perform-end ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls ================================================ :js :effectHandlers :lift // * This file demonstrates the handler-based mechanism of our stack safety implementation // * by manually applying the transformation of a recursive factorial function // * defined through the Z combinator. // * It is notably interesting in that it demonstrates the ability to preserve tail calls. // * The original function can be found in `hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls` fun test(n, stackLimit) = let stackDepth = 0 let stackOffset = 0 abstract class StackDelay with fun perform() handle h = StackDelay with fun perform()(resume) = let curOffset = stackOffset set stackOffset = stackDepth console.log("resuming at offset:", curOffset) let tmp = resume() console.log("finished at offset:", curOffset) set stackOffset = curOffset tmp fun selfApp(f) = if stackDepth - stackOffset >= stackLimit do h.perform() set stackDepth += 1 f(f) fun mkrec(g) = if stackDepth - stackOffset >= stackLimit do h.perform() fun a(self) = if stackDepth - stackOffset >= stackLimit do h.perform() fun b(y) = if stackDepth - stackOffset >= stackLimit do h.perform() let tmp = stackDepth set stackDepth += 1 let res = selfApp(self) set stackDepth = tmp set stackDepth += 1 res(y) set stackDepth += 1 g(b) set stackDepth += 1 selfApp(a) let fact = fun a(self) = if stackDepth - stackOffset >= stackLimit do h.perform() fun b(x) = if stackDepth - stackOffset >= stackLimit do h.perform() if x == 0 then 1 else console.log(stackDepth, stackOffset) let tmp = stackDepth set stackDepth += 1 let prev = self(x - 1) set stackDepth = tmp console.log("resumed:", x) x * prev b mkrec(a) set stackDepth = 1 let ans = fact(n) set stackDepth = 0 ans :expect 3628800 test(10, 100) //│ > 1 0 //│ > 3 0 //│ > 5 0 //│ > 7 0 //│ > 9 0 //│ > 11 0 //│ > 13 0 //│ > 15 0 //│ > 17 0 //│ > 19 0 //│ > resumed: 1 //│ > resumed: 2 //│ > resumed: 3 //│ > resumed: 4 //│ > resumed: 5 //│ > resumed: 6 //│ > resumed: 7 //│ > resumed: 8 //│ > resumed: 9 //│ > resumed: 10 //│ = 3628800 :expect 3628800 test(10, 5) //│ > 1 0 //│ > resuming at offset: 0 //│ > 3 5 //│ > 5 5 //│ > 7 5 //│ > resuming at offset: 5 //│ > 9 10 //│ > 11 10 //│ > resuming at offset: 10 //│ > 13 15 //│ > 15 15 //│ > 17 15 //│ > resuming at offset: 15 //│ > 19 20 //│ > resumed: 1 //│ > resumed: 2 //│ > resumed: 3 //│ > resumed: 4 //│ > resumed: 5 //│ > resumed: 6 //│ > resumed: 7 //│ > resumed: 8 //│ > resumed: 9 //│ > resumed: 10 //│ > finished at offset: 15 //│ > finished at offset: 10 //│ > finished at offset: 5 //│ > finished at offset: 0 //│ = 3628800 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/MultiResumption.mls ================================================ :js :effectHandlers :lift class Logger with fun info(s: Str): () :re handle h = Logger with fun info(s)(k) = print(s) k() k() h.info("a") //│ > a //│ ═══[RUNTIME ERROR] Error: Multiple resumption handle h = Logger with fun info(s)(k) = print(s) k() k() 123 //│ = 123 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/NestedFun.mls ================================================ :js :effectHandlers :lift fun foo(f) = let y = 1 fun bar() = f() set y += 1 let lam = () => y bar() set y += 1 lam() :expect 3 handle h = Object with fun f()(k) = k(()) foo(() => h.f()) //│ = 3 fun foo() = let y = 1 fun bar() = set y += 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls ================================================ :js :effectHandlers :lift let id = 0 //│ id = 0 // FIXME: if the following 2 blocks are together, lifter causes class ordering issue class MaybeStop with fun f(x: Bool): () fun handleEffects(g) = handle h1 = MaybeStop with fun f(x)(k) = if x then print("h1 stop") else set id += 1 let cur = id print("h1 start " + String(cur)) let result = k(x) print("h1 end " + String(cur)) result handle h2 = MaybeStop with fun f(x)(k) = if x then print("h2 stop") else set id += 1 let cur = id print("h2 start " + String(cur)) let result = k(x) print("h2 end " + String(cur)) result g(h1, h2) fun f(h1, h2) = h1.f(false) h2.f(false) h1.f(false) h2.f(false) h1.f(false) h2.f(false) handleEffects(f) //│ > h1 start 1 //│ > h2 start 2 //│ > h1 start 3 //│ > h2 start 4 //│ > h1 start 5 //│ > h2 start 6 //│ > h2 end 6 //│ > h2 end 4 //│ > h2 end 2 //│ > h1 end 5 //│ > h1 end 3 //│ > h1 end 1 //│ = false fun f(h1, h2) = h1.f(false) h2.f(false) h1.f(false) h2.f(true) h1.f(false) h2.f(false) handleEffects(f) //│ > h1 start 7 //│ > h2 start 8 //│ > h1 start 9 //│ > h2 stop //│ > h2 end 8 //│ > h1 end 9 //│ > h1 end 7 fun f(h1, h2) = h1.f(false) h2.f(false) h1.f(true) h2.f(false) h1.f(false) h2.f(false) handleEffects(f) //│ > h1 start 10 //│ > h2 start 11 //│ > h1 stop //│ > h1 end 10 class Eff with fun perform(): () data class Box(n) let box = new mut Box(0) //│ box = Box(0) :expect 5120 fun f(h, box, n) = if n <= 1 then h.perform() else handle h2 = Eff with fun perform()(k) = set box.n = box.n + 1 h.perform() k() f(h2, box, n - 1) f(h2, box, n - 1) handle h = Eff with fun perform()(k) = set box.n = box.n + 1 k() f(h, box, 10) box.n //│ = 5120 handle h1 = Eff with fun perform()(k) = print("h1") let x = k() print("h1 end") x handle h2 = Eff with fun perform()(k) = print("h2") h1.perform() let x = k() print("h2 end") x h2.perform() h2.perform() h2.perform() //│ > h2 //│ > h1 //│ > h2 //│ > h1 //│ > h2 //│ > h1 //│ > h2 end //│ > h2 end //│ > h2 end //│ > h1 end //│ > h1 end //│ > h1 end handle h1 = Eff with fun perform()(k) = print("h1") k() handle h2 = Eff with fun perform()(k) = print("h2") h1.perform() k() handle h3 = Eff with fun perform()(k) = print("h3") h2.perform() k() h1.perform() h3.perform() //│ > h1 //│ > h3 //│ > h2 //│ > h1 let d = 0 //│ d = 0 :expect 11 handle h1 = Eff with fun perform()(k) = set d = d + 1 k() let i = 0 h1.perform() while i < 10 do handle h2 = Eff with fun perform()(k) = set d = d + 1 k() h2.perform() set i = i + 1 d //│ = 11 abstract class Eff2 with fun f1(): () fun f2(): () // Effect performed at end of handler, which trigger a special branch to handle handle h1 = Eff2 with fun f1()(k) = print("h1.f1 pre") k() print("h1.f1 post") fun f2()(k) = k() handle h2 = Eff2 with fun f1()(k) = print("h2.f1") k() fun f2()(k) = print("h2.f2 pre") k() print("h2.f2 post") h1.f1() h2.f2() h2.f1() //│ > h2.f2 pre //│ > h2.f1 //│ > h2.f2 post //│ > h1.f1 pre //│ > h1.f1 post ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/NoHandler.mls ================================================ :js abstract class Effect with fun perform(arg: Str): Str :ge handle h = Effect with fun perform(arg)(k) = k(arg) h.perform("k") //│ ╔══[COMPILATION ERROR] Effect handlers are not enabled //│ ║ l.7: handle h = Effect with //│ ║ ^^^^^^^^^^^ //│ ║ l.8: fun perform(arg)(k) = k(arg) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.9: h.perform("k") //│ ╙── ^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/NoStackSafety.mls ================================================ :js // sanity check :expect 5050 fun sum(n) = if n === 0 then 0 else n + sum(n - 1) sum(100) //│ = 5050 // stack-overflows without :stackSafe :re fun sum(n) = if n === 0 then 0 else n + sum(n - 1) sum(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :re fun foo() = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) f(10000) foo() //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :sjs :stackSafe 42 fun hi(n) = n hi(0) //│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let hi, n, inlinedVal; hi = function hi(n1) { return n1 }; n = 0; inlinedVal = n; inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :stackSafe 42 hi(0) //│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls ================================================ :js :effectHandlers :lift fun f() = (() => return 100)() print("Bad") f() //│ = 100 fun foo(x) = let xs = [() => return x + 1] xs.0() foo(123) //│ = 124 :expect 123 fun f() = let lam = () => return 123 fun g() = let lam2 = () => return 200 lam() lam2() 100 print("Ok") g() print("Bad") 100 f() //│ > Ok //│ = 123 :expect 100 fun f() = let lam = () => return 123 fun g() = let lam2 = () => return 200 lam2() lam() 100 print("Ok") print(g()) print("Continue") 100 f() //│ > Ok //│ > 200 //│ > Continue //│ = 100 :re fun f() = () => return 3 f()() //│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_12 :re fun f(): () -> Int = () => return () => 3 f()() //│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_14 :e return 100 //│ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. //│ ║ l.68: return 100 //│ ╙── ^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e if true do while false do let f = () => return 100 //│ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. //│ ║ l.77: let f = () => return 100 //│ ╙── ^^^^^^^^^^ :e fun f() = type A = return "lol" //│ ╔══[COMPILATION ERROR] Return statements are not allowed in this context. //│ ║ l.84: type A = return "lol" //│ ╙── ^^^^^^^^^^^^ :expect 100 fun f() = if true do let f = null let done = false while not(done) do set f = () => return 100 done = true f() print("Bad") f() //│ = 100 // TODO(inliner): something is wrong here // broken due to handlers currently not handling `object` properly :fixme :expect 100 fun f() = object A with return 100 4 f() //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.109: object A with //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.109: object A with //│ ╙── ^ //│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_18 is raised in a constructor //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' // ctor cannot raise effect, so error is expected. :fixme :expect 100 fun f() = class A with return 100 new A f() //│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_20 is raised in a constructor //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' :fixme :expect 100 fun f() = object A with (() => return 100)() 4 f() //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.136: object A with //│ ╙── ^ //│ ╔══[INTERNAL ERROR] Unexpected nested class: lambdas may not function correctly. //│ ║ l.136: object A with //│ ╙── ^ //│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_22 is raised in a constructor //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' :fixme :expect 100 fun f() = class A with (() => return 100)() new A 4 f() //│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_24 is raised in a constructor //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls ================================================ :js :effectHandlers :lift abstract class Effect[A] with fun perform(arg: A): A handle h1 = Effect with fun perform(arg)(k) = print("performing " + arg) [k("ok")] h1.perform("hi") h1.perform("hello") //│ > performing hi //│ > performing hello //│ = [["ok"]] :e h1 //│ ╔══[COMPILATION ERROR] Name not found: h1 //│ ║ l.20: h1 //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e h1.perform("oops") //│ ╔══[COMPILATION ERROR] Name not found: h1 //│ ║ l.27: h1.perform("oops") //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re handle h = Object with fun write(x)(k) = if x < 10 do write(10) [x, k(())] h.write(1) h.write(2) //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ handle h = Object with fun write(x)(k) = if x < 10 do print(enterHandleBlock(this, () => write(10))) [x, k(())] h.write(1) h.write(2) //│ > [10, ()] //│ > [10, ()] //│ = [1, [2, ()]] handle h = Object with fun write(x)(k) = enterHandleBlock of this, () => if x < 10 do write(10) [x, k(())] h.write(1) h.write(2) //│ = [10, [1, [10, [2, ()]]]] // deep handler :todo handle h2 = Effect with fun perform(arg)(k) = print("performing " + arg) k of if arg > 0 then h2.perform(arg - 1) + " " + arg else "0" [ print("–––"); h2.perform(2) print("–––"); h2.perform(3) ] //│ ╔══[COMPILATION ERROR] Name not found: h2 //│ ║ l.67: then h2.perform(arg - 1) + " " + arg //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. //│ ║ l.68: else "0" //│ ╙── ^^^^ //│ > ––– //│ > performing 2 //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // passing additional handlers to the handler body let res = handle h = Effect with fun perform(arg)(k) = print("A " + arg) handle g = Effect with fun perform(arg1)(k1) = print("B " + arg + " " + arg1) ["‹", k1(arg), arg1, "›"] [k(g)] let g = h.perform("hi") g.perform("bye") g.perform("friend") set g = h.perform("hola") g.perform("adios") g.perform("amigos") in res.toString() //│ > A hi //│ > B hi bye //│ > B hi friend //│ > A hola //│ > B hola adios //│ > B hola amigos //│ = "‹,‹,‹,‹,hola,amigos,›,adios,›,friend,›,bye,›" // This is equivalent to the above, with all calls to h.perform expanded let res = print("A " + "hi") handle g = Effect with fun perform(arg1)(k1) = print("B " + "hi" + " " + arg1) ["‹", k1("hi"), arg1, "›"] [(() => g.perform("bye") g.perform("friend") print("A " + "hola") handle g2 = Effect with fun perform(arg1)(k1) = print("B " + "hola" + " " + arg1) ["‹", k1("hola"), arg1, "›"] [(() => g2.perform("adios") g2.perform("amigos") )()] )()] in res.toString() //│ > A hi //│ > B hi bye //│ > B hi friend //│ > A hola //│ > B hola adios //│ > B hola amigos //│ = "‹,‹,‹,‹,hola,amigos,›,adios,›,friend,›,bye,›" :sjs let str = "" if true do handle h1 = Effect with fun perform(arg)(k) = set str += "A" k(arg) set str += "A" handle h2 = Effect with fun perform(arg)(k) = set str += str + "B" k(arg) set str += str + "B" h2.perform(()) h1.perform(()) str //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let str, scrut, h11, handleBlock$9, Handler$h2$perform1, Handler$h2$3, handleBlock$10, Handler$h1$perform1, Handler$h1$3, handleBlock$$2, Handler$h2$perform$1, Handler$h1$perform$1; //│ runtime.resetEffects(); //│ Handler$h1$perform$1 = function Handler$h1$perform$(arg) { //│ return (k) => { //│ return Handler$h1$perform1(arg, k) //│ } //│ }; //│ Handler$h1$perform1 = function Handler$h1$perform(arg, k) { //│ let tmp10, tmp11, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ tmp10 = str + "A"; //│ str = tmp10; //│ runtime.resumeValue = runtime.safeCall(k(arg)); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(Handler$h1$perform1, 1, "RecursiveHandlers.mls:147:7", null, null, 1, 2, arg, k, 0) //│ } //│ case 1: //│ runtime.resumeValue; //│ tmp11 = str + "A"; //│ str = tmp11; //│ return runtime.Unit; //│ } //│ }; //│ Handler$h2$perform$1 = function Handler$h2$perform$(arg) { //│ return (k) => { //│ return Handler$h2$perform1(arg, k) //│ } //│ }; //│ Handler$h2$perform1 = function Handler$h2$perform(arg, k) { //│ let tmp10, tmp11, tmp12, tmp13, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ tmp10 = str + "B"; //│ tmp11 = str + tmp10; //│ str = tmp11; //│ runtime.resumeValue = runtime.safeCall(k(arg)); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(Handler$h2$perform1, 1, "RecursiveHandlers.mls:152:7", null, null, 1, 2, arg, k, 0) //│ } //│ case 1: //│ runtime.resumeValue; //│ tmp12 = str + "B"; //│ tmp13 = str + tmp12; //│ str = tmp13; //│ return runtime.Unit; //│ } //│ }; //│ handleBlock$$2 = (undefined, function (h22) { //│ return () => { //│ return handleBlock$9(h22) //│ } //│ }); //│ (class Handler$h2$2 extends Effect1 { //│ static { //│ Handler$h2$3 = this //│ } //│ constructor() { //│ let tmp10; //│ tmp10 = super(); //│ if (runtime.curEffect === null) { //│ tmp10 //│ } else { //│ tmp10 = runtime.illegalEffect("in a constructor"); //│ tmp10 //│ } //│ } //│ perform(arg) { //│ let Handler$h2$perform$here; //│ Handler$h2$perform$here = Handler$h2$perform$1(arg); //│ return runtime.mkEffect(this, Handler$h2$perform$here) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Handler$h2$"]; //│ }); //│ handleBlock$9 = (undefined, function (h22) { //│ let pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ runtime.resumeValue = runtime.safeCall(h22.perform(runtime.Unit)); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(handleBlock$9, 1, "RecursiveHandlers.mls:154:3", null, null, 1, 1, h22, 0) //│ } //│ case 1: //│ runtime.resumeValue; //│ return runtime.safeCall(h11.perform(runtime.Unit)); //│ } //│ }); //│ (class Handler$h1$2 extends Effect1 { //│ static { //│ Handler$h1$3 = this //│ } //│ constructor() { //│ let tmp10; //│ tmp10 = super(); //│ if (runtime.curEffect === null) { //│ tmp10 //│ } else { //│ tmp10 = runtime.illegalEffect("in a constructor"); //│ tmp10 //│ } //│ } //│ perform(arg) { //│ let Handler$h1$perform$here; //│ Handler$h1$perform$here = Handler$h1$perform$1(arg); //│ return runtime.mkEffect(this, Handler$h1$perform$here) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Handler$h1$"]; //│ }); //│ handleBlock$10 = (undefined, function () { //│ let h22, tmp10, handleBlock$$here, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ h22 = new Handler$h2$3(); //│ handleBlock$$here = handleBlock$$2(h22); //│ runtime.resumeValue = runtime.enterHandleBlock(h22, handleBlock$$here); //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(handleBlock$10, 1, null, null, null, 1, 0, 0) //│ } //│ case 1: //│ tmp10 = runtime.resumeValue; //│ return tmp10; //│ } //│ }); //│ str = ""; //│ scrut = true; //│ if (scrut === true) { //│ h11 = new Handler$h1$3(); //│ runtime.enterHandleBlock(h11, handleBlock$10); //│ if (runtime.curEffect === null) { str } else { runtime.topLevelEffect(false); str } //│ } else { str } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "BABABA" //│ str = "BABABA" ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls ================================================ :js :effectHandlers :lift abstract class Effect with fun f(): () // `return` within handle blocks is implemented as a non-local return fun f() = handle h = Effect with fun foo()(r) = let m = r(()) print("Bye!") // This never executes because `r` returns from `f` m h.foo() return 3 // Note: changing this to just `3` leads to `print("Bye!")` executing f() //│ = 3 fun f() = handle h = Effect with fun foo()(r) = let m = r() print("Bye!") m in h.foo() return 3 10 :expect 3 f() //│ = 3 :e let l = () => handle h = Effect with fun foo()(r) = r(()) return 3 4 //│ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. //│ ║ l.40: return 3 //│ ╙── ^^^^^^^^ //│ l = fun l :re l() //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e handle h1 = Effect with fun foo()(r) = r(()) let l = () => handle h2 = Effect with fun bar()(r) = r(()) h1.foo() return 3 4 l() //│ ╔══[COMPILATION ERROR] Return statements are not allowed outside of functions. //│ ║ l.58: return 3 //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/SavedVars.mls ================================================ :js :effectHandlers :lift // This acts as a function that may throw an effect fun f() = () // A constant function to block any potential optimization. fun always_true() = true // Observe that no local variables will be saved, as they are not needed. :sjs fun g() = let a = if always_true() then f() 1 else 2 a //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let g; //│ runtime.resetEffects(); //│ g = function g() { //│ let a, scrut, tmp, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ main: while (true) { //│ switch (pc) { //│ case 0: //│ runtime.resumeValue = always_true(); //│ if (runtime.curEffect === null) { //│ pc = 3; //│ } else { //│ return runtime.unwind(g, 3, "SavedVars.mls:17:8", null, null, 1, 0, 0) //│ } //│ case 3: //│ scrut = runtime.resumeValue; //│ if (scrut === true) { //│ runtime.resumeValue = f(); //│ if (runtime.curEffect === null) { //│ pc = 2; //│ continue main //│ } //│ return runtime.unwind(g, 2, "SavedVars.mls:18:7", null, null, 1, 0, 0); //│ } //│ tmp = 2; //│ pc = 1; //│ continue main; //│ case 2: //│ runtime.resumeValue; //│ tmp = 1; //│ pc = 1; //│ if (pc === 1) { a = tmp; return a } //│ break; //│ case 1: //│ a = tmp; //│ return a; //│ } //│ break; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/SetInHandlers.mls ================================================ :js // * This feature relies on `finally` :sjs let x = 1 set x += 1 in print(x) x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x, old, tmp; //│ x = 1; //│ old = x; //│ try { tmp = x + 1; x = tmp; Predef.print(x); } finally { x = old; } //│ x //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 //│ = 1 //│ x = 1 // * But `finally` is not yet supported by effect handlers :todo :effectHandlers :lift let x = 1 handle h = Object with fun test()(k) = print("SUSPENDING") let res = k(42) print("DONE") res set x += 1 in print("init") print(h.test()) print(x) print("end") x //│ ╔══[COMPILATION ERROR] Backtracking assignment is not supported with effect handlers enabled //│ ║ l.33: set x += 1 in //│ ║ ^^^^^^^^^ //│ ║ l.34: print("init") //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.35: print(h.test()) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.36: print(x) //│ ║ ^^^^^^^^^^ //│ ║ l.37: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ x = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls ================================================ :js :effectHandlers :lift :noTailRec // preserve tail calls // MUST see "return hi(tmp1)" in the output :stackSafe 6 :expect 0 :sjs fun hi(n) = if n === 0 then 0 else hi(n - 1) hi(0) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let hi, tmp, $_stack$_safe$_body$_; //│ runtime.resetEffects(); //│ hi = function hi(n) { //│ runtime.stackDepth = runtime.stackDepth + 1; //│ runtime.checkDepth(); //│ if (runtime.curEffect === null) { //│ let scrut, tmp1; //│ scrut = n === 0; //│ if (scrut === true) { //│ return 0 //│ } //│ { //│ tmp1 = n - 1; //│ return hi(tmp1); //│ } //│ } //│ return runtime.unwind(hi, -1, "StackSafety.mls:12:1", null, null, 1, 1, n, 0); //│ }; //│ $_stack$_safe$_body$_ = (undefined, function () { //│ return hi(0) //│ }); //│ tmp = runtime.runStackSafe(6, $_stack$_safe$_body$_); //│ if (runtime.curEffect === null) { tmp } else { tmp = runtime.topLevelEffect(false); tmp } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :stackSafe :expect 50005000 :sjs fun sum(n) = if n === 0 then 0 else n + sum(n - 1) sum(10000) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let sum, tmp1, $_stack$_safe$_body$_1; //│ runtime.resetEffects(); //│ sum = function sum(n) { //│ let curDepth; //│ runtime.stackDepth = runtime.stackDepth + 1; //│ curDepth = runtime.stackDepth; //│ runtime.checkDepth(); //│ if (runtime.curEffect === null) { //│ let scrut, tmp2, tmp3, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; //│ } else { //│ pc = runtime.resumePc; //│ runtime.resumePc = -1; //│ } //│ switch (pc) { //│ case 0: //│ scrut = n === 0; //│ if (scrut === true) { //│ return 0 //│ } //│ { //│ let res; //│ tmp2 = n - 1; //│ res = sum(tmp2); //│ runtime.stackDepth = curDepth; //│ runtime.resumeValue = res; //│ if (runtime.curEffect === null) { //│ pc = 1; //│ } else { //│ return runtime.unwind(sum, 1, "StackSafety.mls:49:9", null, null, 1, 1, n, 0) //│ } //│ } //│ case 1: //│ tmp3 = runtime.resumeValue; //│ return n + tmp3; //│ } //│ } else { //│ return runtime.unwind(sum, -1, "StackSafety.mls:46:1", null, null, 1, 1, n, 0) //│ } //│ }; //│ $_stack$_safe$_body$_1 = (undefined, function () { //│ return sum(10000) //│ }); //│ tmp1 = runtime.runStackSafe(1000, $_stack$_safe$_body$_1); //│ if (runtime.curEffect === null) { tmp1 } else { tmp1 = runtime.topLevelEffect(false); tmp1 } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 50005000 :stackSafe mut val ctr = 0 fun dummy(x) = x fun foo(f) = if ctr > 10000 then 0 else set ctr += 1 dummy(f(f)) foo(foo) //│ = 0 //│ ctr = 10001 :stackSafe :expect 50005000 val foo = val f = n => if n <= 0 then 0 else n + f(n-1) f(10000) foo //│ = 50005000 //│ foo = 50005000 :stackSafe 100 :expect 50005000 val foo = val f = n => if n <= 0 then 0 else n + f(n-1) f(10000) foo //│ = 50005000 //│ foo = 50005000 abstract class Eff with fun perform(a): () // functions and lambdas inside handlers :stackSafe 100 :expect 50005000 fun foo(h) = h.perform() handle h = Eff with fun perform()(resume) = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) foo(h) //│ = 50005000 // function call and defn inside handler :stackSafe 100 :expect 50005000 handle h = Eff with fun perform()(resume) = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) in fun foo(h) = h.perform() foo(h) //│ = 50005000 :stackSafe 100 :expect 50005000 module A with val f = n => if n <= 0 then 0 else n + f(n-1) val x = f(10000) A.x //│ = 50005000 :re fun foo(h) = h.perform(2) handle h = Eff with fun perform(a)(resume) = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) foo(h) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :stackSafe :sjs fun max(a, b) = if a < b then b else a //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let max; //│ runtime.resetEffects(); //│ max = function max(a, b) { let scrut; scrut = a < b; if (scrut === true) { return b } return a; }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :stackSafe :expect 100010000 fun sum(n) = if n === 0 then 0 else n + sum(n - 1) fun bad() = sum(10000) + sum(10000) bad() //│ = 100010000 :stackSafe 100 :expect 0 let depth = 0 handle h = Object with fun whoops()(k) = if depth >= 50000 then 0 else set depth += 1 enterHandleBlock(this, () => whoops()) h.whoops() //│ = 0 //│ depth = 50000 // TODO: the stack depth is not accurately tracked in the runtime functions // for now this works, but it's not very safe :stackSafe :expect 10000 fun dummy = () fun f(h) = let n = 0 while n < 10000 do h.perform() set n += 1 n handle h = Object with fun perform()(k) = k() f(h) //│ = 10000 :stackSafe fun trivial() = () fun f(n) = trivial() if n >= 0 do f(n-1) trivial() f(10000) abstract class Eff with fun raise() :re fun shift_stack(n, f) = if n >= 0 then shift_stack(n - 1, f) else f() fun handler_stack(n) = handle h = Eff with fun raise()(k) = shift_stack(100, k) h.raise() if n >= 0 do handler_stack(n - 1) "ok" handler_stack(100) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded // This test was failing non-deterministically due to handler state not being properly reset. // The flakiness was due to the stack overflow in the previous block not always interrupting the code in the same place, // sometimes leaving the effect handlers runtime in an inconsistent state. :silent let N = 100 M = 100 :stackSafe fun shift_stack(n, f) = if n >= 0 then shift_stack(n - 1, f) else f() fun handler_stack(n) = handle h = Eff with fun raise()(k) = shift_stack(N, k) h.raise() if n >= 0 do handler_stack(n - 1) "ok" handler_stack(M) //│ = "ok" ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/TailCallOptimization.mls ================================================ :js :effectHandlers :lift abstract class StackDelay with fun perform(): () :expect 3 handle h = StackDelay with fun perform()(resume) = resume() fun foo(x) = h.perform() x + 4 fun bar(y) = h.perform() // tail call foo(y + 2) fun foobar(z) = bar(z + 1) // stuff after tail call is linked differently 3 foobar(0) //│ = 3 // Check that tail call optimization is applied in presence of effect handlers: there should be no function calls in the body. :sjs :expect 0 :stackSafe :sjs fun hi(n) = if n === 0 then 0 else hi(n - 1) hi(0) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let hi, tmp1, $_stack$_safe$_body$_; //│ runtime.resetEffects(); //│ hi = function hi(n) { //│ loopLabel: while (true) { //│ runtime.stackDepth = runtime.stackDepth + 1; //│ runtime.checkDepth(); //│ if (runtime.curEffect === null) { //│ let scrut, tmp2; //│ scrut = n === 0; //│ if (scrut === true) { //│ return 0 //│ } //│ { //│ tmp2 = n - 1; //│ n = tmp2; //│ continue loopLabel; //│ } //│ } //│ return runtime.unwind(hi, -1, "TailCallOptimization.mls:31:1", null, null, 1, 1, n, 0); //│ } //│ }; //│ $_stack$_safe$_body$_ = (undefined, function () { //│ return hi(0) //│ }); //│ tmp1 = runtime.runStackSafe(1000, $_stack$_safe$_body$_); //│ if (runtime.curEffect === null) { tmp1 } else { tmp1 = runtime.topLevelEffect(false); tmp1 } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/UserThreads.mls ================================================ :js :effectHandlers :lift abstract class ThreadEffect with val tasks = mut [] fun start(thread: () -> ()): () fun fork(thread: () -> ()): () fun yld(): () class Lock(val locked) with fun lock(th: ThreadEffect) = while locked do th.yld() set locked = true fun unlock() = set locked = false fun f(h, x) = print("f " + x) h.yld() print("f " + x) h.yld() print("f " + x) fun main(h) = print("main start") h.fork(() => f(h, 0)) h.fork(() => f(h, 1)) h.fork(() => f(h, 2)) print("main end") // LIFO handle h = ThreadEffect with fun fork(thread)(k) = this.tasks.unshift(thread) k(()) fun yld()(k) = this.tasks.push(() => k(())) enterHandleBlock(this, () => this.tasks.shift()()) fun start(main)(k) = this.tasks.push(() => main(this)) while this.tasks.length != 0 do enterHandleBlock(this, () => this.tasks.shift()()) k(()) in h.start(main) //│ > main start //│ > main end //│ > f 2 //│ > f 1 //│ > f 0 //│ > f 2 //│ > f 1 //│ > f 0 //│ > f 2 //│ > f 1 //│ > f 0 // FIFO handle h = ThreadEffect with fun fork(thread)(k) = this.tasks.push(thread) k(()) fun yld()(k) = this.tasks.push(() => k(())) enterHandleBlock(this, () => this.tasks.shift()()) fun start(main)(k) = this.tasks.push(() => main(this)) while this.tasks.length != 0 do enterHandleBlock(this, () => this.tasks.shift()()) k(()) in h.start(main) //│ > main start //│ > main end //│ > f 0 //│ > f 1 //│ > f 2 //│ > f 0 //│ > f 1 //│ > f 2 //│ > f 0 //│ > f 1 //│ > f 2 // Adapted from: https://ocaml.org/manual/5.3/effects.html abstract class ThreadEffect with fun fork(thread: ThreadEffect -> ()): () fun yld(): () val tasks = mut [] //│ tasks = [] fun f(x)(h) = print("f " + x) h.yld() print("f " + x) h.yld() print("f " + x) fun main(h) = print("main start") h.fork(f(0)) h.fork(f(1)) h.fork(f(2)) print("main end") fun dequeue() = if tasks.length != 0 do tasks.shift()(()) fun start(thread: ThreadEffect -> ()) = handle h = ThreadEffect with fun fork(new_thread)(k) = tasks.push(k) start(new_thread) fun yld()(k) = tasks.push(k) dequeue() in thread(h) dequeue() start(main) //│ > main start //│ > f 0 //│ > f 1 //│ > f 0 //│ > f 2 //│ > f 1 //│ > f 0 //│ > main end //│ > f 2 //│ > f 1 //│ > f 2 import "../../mlscript-compile/Option.mls" open Option abstract class ThreadEffect with fun fork(thread: ThreadEffect -> ()): () fun yld(): () fun xchg(x: Int): Int val tasks = mut [] fun enqueue(k, x) = tasks.push(() => k(x)) fun dequeue() = if tasks.length != 0 do tasks.shift()() //│ tasks = [] let exchanger = None //│ exchanger = None fun start(thread: ThreadEffect -> ()) = handle h = ThreadEffect with fun fork(new_thread)(k) = enqueue(k, ()) start(new_thread) fun yld()(k) = enqueue(k, ()) dequeue() fun xchg(x)(k) = if exchanger is Some([y, k1]) then set exchanger = None enqueue(k1, x) k(y) None then set exchanger = Some([x, k]) dequeue() in thread(h) dequeue() fun main(h) = h.fork(h => print("[t1] Sending 0") let v = h.xchg(0) print("[t1] received " + v.toString())) h.fork(h => print("[t2] Sending 1") let v = h.xchg(1) print("[t2] received " + v.toString())) start(main) //│ > [t1] Sending 0 //│ > [t2] Sending 1 //│ > [t2] received 0 //│ > [t1] received 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls ================================================ :js :effectHandlers debug :lift class ThreadEffect with // task queue val tasks = mut [] fun drain() = while tasks.length != 0 do tasks.pop()() fun fork(thread: () -> ()): () fun yld(): () fun f(h, x)() = print("f " + x) h.yld() print("f " + x) h.yld() print("f " + x) fun main(h) = print("main start") h.fork(f(h, 0)) h.fork(f(h, 1)) h.fork(f(h, 2)) print("main end") // LIFO :re handle h = ThreadEffect with fun fork(thread)(k) = this.tasks.push(thread) k() this.drain() fun yld()(k) = this.tasks.unshift(k) this.drain() in main(h) //│ > main start //│ > main end //│ > f 2 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ //│ at f (UserThreadsSafe.mls:18:3) //│ at while (UserThreadsSafe.mls:11:7) //│ at ThreadEffect#drain (pc=1) //│ at Handler$h$fork (UserThreadsSafe.mls:36:5) //│ at Handler$h$fork (UserThreadsSafe.mls:36:5) // FIFO :re handle h = ThreadEffect with fun fork(thread)(k) = this.tasks.unshift(thread) k() this.drain() fun yld()(k) = this.tasks.unshift(k) this.drain() in main(h) //│ > main start //│ > main end //│ > f 0 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$2 //│ at f (UserThreadsSafe.mls:18:3) //│ at while (UserThreadsSafe.mls:11:7) //│ at ThreadEffect#drain (pc=1) //│ at Handler$h$fork (UserThreadsSafe.mls:59:5) //│ at Handler$h$fork (UserThreadsSafe.mls:59:5) ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls ================================================ :js :effectHandlers debug :lift class ThreadEffect with fun fork(thread: () -> ()): () fun yld(): () fun f(h, x)() = print("f " + x) h.yld() print("f " + x) h.yld() print("f " + x) fun main(h) = print("main start") h.fork(f(h, 0)) h.fork(f(h, 1)) h.fork(f(h, 2)) print("main end") // task queue let tasks = mut [] fun drain() = while tasks.length != 0 do tasks.pop()() //│ tasks = [] // LIFO :re handle h = ThreadEffect with fun fork(thread)(k) = tasks.push(thread) k() drain() fun yld()(k) = tasks.unshift(k) drain() in main(h) //│ > main start //│ > main end //│ > f 2 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ //│ at f (UserThreadsUnsafe.mls:13:3) //│ at while (UserThreadsUnsafe.mls:30:5) //│ at drain (pc=1) //│ at Handler$h$fork (UserThreadsUnsafe.mls:39:5) //│ at Handler$h$fork (UserThreadsUnsafe.mls:39:5) // FIFO :re handle h = ThreadEffect with fun fork(thread)(k) = tasks.unshift(thread) k() drain() fun yld()(k) = tasks.unshift(k) drain() in main(h) //│ > main start //│ > main end //│ > f 1 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ //│ at f (UserThreadsUnsafe.mls:13:3) //│ at while (UserThreadsUnsafe.mls:30:5) //│ at drain (pc=1) //│ at Handler$h$fork (UserThreadsUnsafe.mls:62:5) //│ at Handler$h$fork (UserThreadsUnsafe.mls:62:5) ================================================ FILE: hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls ================================================ :js :effectHandlers :lift :stackSafe fun selfApp(f) = f(f) fun mkrec(g) = selfApp of self => g of y => self(self)(y) let fact = mkrec of self => x => if x == 0 then 1 else self(x - 1) * x //│ fact = fun fact(3) //│ = 6 fact(10) //│ = 3628800 :re :stackSafe off fact(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :stackSafe fun mkrec(g) = selfApp of self => g of y => selfApp(self)(y) // * FIXME: Why doesn't the following work when using Predef function `(==) equals`? let fact = mkrec of self => x => if x === 0 then 1 else self(x - 1) * x //│ fact = fun :stackSafe fact(10000) //│ = Infinity // * Without `:stackSafe`, gives `RangeError: Maximum call stack size exceeded` :re :stackSafe off fact(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :stackSafe fact(10000) //│ = Infinity :re :stackSafe off fact(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :stackSafe 6 set selfApp = f => f(f) :expect 3628800 fact(10) //│ = 3628800 :stackSafe 6 set selfApp = f => f(f) fact(1000) //│ = Infinity // simplified version without lambdas for easier debugging :stackSafe 6 fun mkrec(g) = fun a(self) = fun b(y) = selfApp(self)(y) g(b) selfApp(a) :stackSafe 6 import "../../mlscript-compile/Runtime.mls" let fact = fun a(self) = fun b(x) = if x == 0 then 1 else let prev = self(x - 1) console.log("resumed:", x) x * prev b mkrec(a) //│ fact = fun :expect 3628800 :stackSafe 6 fact(10) //│ > resumed: 1 //│ > resumed: 2 //│ > resumed: 3 //│ > resumed: 4 //│ > resumed: 5 //│ > resumed: 6 //│ > resumed: 7 //│ > resumed: 8 //│ > resumed: 9 //│ > resumed: 10 //│ = 3628800 ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Arrays.mls ================================================ :js let arr = [true, false] //│ arr = [true, false] // * Array methods annoyingly supply extra arguments, such as the index and the array itself :re arr.map(_ === false) //│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 :sjs arr.map((e, ...) => e === false) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; //│ lambda1 = (undefined, function (e, ..._) { return e === false }); //│ runtime.safeCall(arr.map(lambda1)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [false, true] :sjs arr.map((_, ...) => 1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda2; //│ lambda2 = (undefined, function (_, ..._1) { return 1 }); //│ runtime.safeCall(arr.map(lambda2)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [1, 1] :e arr.map((..., ...) => 1) //│ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. //│ ║ l.32: arr.map((..., ...) => 1) //│ ╙── ^^^ //│ = [1, 1] :e :re arr.map((..., e) => e) //│ ╔══[COMPILATION ERROR] Spread parameters must be the last in the parameter list. //│ ║ l.40: arr.map((..., e) => e) //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 3 arr.map(pass1(_ === false)) //│ = [false, true] [1,3,2].map((x, _, _) => x + 1).sort() //│ = [2, 3, 4] [1,3,2].map((x, _, _) => x + 1) .sort() //│ = [2, 3, 4] Array //│ = class Array Array(1) //│ = [<1 empty items>] :breakme // TODO better report an error, here :e Array() //│ = [] :breakme // TODO better report an error, here :e Array(1, 2, 3) //│ = [1, 2, 3] if arr is Array then "ok" //│ = "ok" :fixme // TODO handle? if arr is Array(n) then n //│ ╔══[COMPILATION ERROR] Class Array does not have a parameter list //│ ║ l.79: if arr is Array(n) then n //│ ║ ^^^^^ //│ ╟── but the pattern has one sub-pattern //│ ║ l.79: if arr is Array(n) then n //│ ╙── ^ //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/CtorBypass.mls ================================================ :js class A with print("ok") val n = 123 Object.freeze(this) let a = new A //│ > ok //│ a = A { n: 123 } a.n //│ = 123 A.prototype //│ = {} // * This can be used for deserialization let b = Object.create(A.prototype) //│ b = A :sjs b is A //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (b instanceof A1) { true } else { false } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true :re b.n //│ ═══[RUNTIME ERROR] Error: Access to required field 'n' yielded 'undefined' set b.n = 0 b.n //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Functions.mls ================================================ :js (x => x).constructor == Function //│ = true fun foo(x) = x foo.constructor == Function //│ = true foo.name //│ = "foo" foo.constructor.name //│ = "Function" typeof(foo) //│ = "function" typeof(foo.constructor) //│ = "function" ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/JSCollections.mls ================================================ :js let xs = new Set of [0, 1, 2] //│ xs = Set(3) {0, 1, 2} let xs = new Set of tuple of 0, 1, 2 //│ xs = Set(3) {0, 1, 2} xs.size //│ = 3 xs.add(3) //│ = Set(4) {0, 1, 2, 3} xs.has(1) //│ = true xs.has(3) //│ = true xs.size //│ = 4 let xs = new Map([[1, "a"],[2, "b"]]) //│ xs = Map(2) {1 => "a", 2 => "b"} xs.size //│ = 2 xs.get(1) //│ = "a" xs.set(3, "c") //│ = Map(3) {1 => "a", 2 => "b", 3 => "c"} xs.size //│ = 3 xs.get(3) //│ = "c" xs.set(1, "_") //│ = Map(3) {1 => "_", 2 => "b", 3 => "c"} xs.size //│ = 3 xs.get(1) //│ = "_" ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/JSDerp.mls ================================================ :js Object.create(()) is Object //│ = true Object.create(new Number()) is Object //│ = true Object.create(null) is Object //│ = false Object(0) is Object //│ = true (new Object(0)) is Object //│ = true Number(0) is Object //│ = false Number(0) is Number //│ = false (new Number(0)) is Object //│ = true (new Number(0)) is Number //│ = true let arr = [[1], [1, 1], [2, 1], [1, 2, 1, 1]] //│ arr = [[1], [1, 1], [2, 1], [1, 2, 1, 1]] arr.toString() //│ = "1,1,1,2,1,1,2,1,1" set Array.prototype.toString = globalThis.eval("(function() { return '(' + Array.prototype.join.call(this, ',') + ')'; })") arr.toString() //│ = "((1),(1,1),(2,1),(1,2,1,1))" ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Null.mls ================================================ :js null == undefined //│ = false null === undefined //│ = false null === null //│ = true console.log(null is null) //│ > true Object.create(null).("toString") is undefined //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Number.mls ================================================ :js globalThis.Number("0.50") //│ = 0.5 let num = _ globalThis.Number() //│ num = fun num num("0.50") //│ = 0.5 ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Objects.mls ================================================ :js let rcd = { x: 0, y: undefined, z: null} //│ rcd = {x: 0, y: undefined, z: null} rcd.constructor //│ = class Object typeof(rcd.constructor) //│ = "function" Object.entries of rcd //│ = [["x", 0], ["y", undefined], ["z", null]] class Foo(val x, val y) with let z = 2 val a = 3 fun f = 4 fun g() = 5 let foo = Foo(1,2) //│ foo = Foo(1, 2) Object.entries of Foo //│ = [["class", class Foo]] Object.entries of foo //│ = [["x", 1], ["y", 2], ["a", 3]] Foo.name //│ = "Foo" Foo.class.name //│ = "Foo" foo.constructor //│ = class Foo // ! will be changed by minification foo.constructor.name //│ = "Foo" typeof(foo) //│ = "object" typeof(foo.constructor) //│ = "function" ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Symbols.mls ================================================ :js :expect false Symbol("123") === Symbol("123") //│ = false :expect true Symbol.for("123") === Symbol.for("123") //│ = true :expect false Symbol.for("123") === Symbol("123") //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/interop/Virtualization.mls ================================================ :js // * `try`/`catch` js.try_catch(() => 123, id) //│ = 123 js.try_catch(() => 123(), id) //│ = TypeError: 123 is not a function js.try_catch of () => 123() case { TypeError then 666, e then throw e } //│ = 666 :re js.try_catch of () => throw Error("whoops") case TypeError then 666 //│ ═══[RUNTIME ERROR] Error: match error :re js.try_catch of () => throw Error("whoops") case { TypeError then 666, e then throw e } //│ ═══[RUNTIME ERROR] Error: whoops ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLAuthorResponse.mls ================================================ :invalml fun wield: [R] -> (Region[out R]) ->{R} () fun freeze: [R, E extends ~R, T] -> (Region[out R], () ->{~R & E} T) ->{R | E} T fun (;) seq(_, res) = res fun rand: () -> Bool fun print: Any -> () // ### Example 1 fun foo1(r1, r2) = freeze(r1, () => print("ok")) wield(r2) region r in foo1(r, r) region r in region s in foo1(r, s) region r0 in fun foo1(r1, r2) = freeze(r1, () => wield(r0)) wield(r2) region r in foo1(r, r) region r in region s in foo1(r, s) fun foo2(r1, r2) = freeze(r1, () => wield(r2)) wield(r2) :e region r in foo2(r, r) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type 'r2 //│ ║ l.51: foo2(r, r) //│ ║ ^ //│ ╟── because: cannot constrain Region[in ⊥ out r] <: 'r2 //│ ╟── because: cannot constrain Region[in ⊥ out r] <: Region[in ⊥ out 'R] //│ ╟── because: cannot constrain r <: 'R //│ ╟── because: cannot constrain r <: 'E //│ ╟── because: cannot constrain r <: ¬'R1 //│ ╟── because: cannot constrain 'R1 <: ¬r //│ ╙── because: cannot constrain r <: ¬r region r in region s in foo2(r, s) // ### Example 2 fun foo: [outer, R extends outer] -> Region[R] ->{outer} () fun foo(r1) = region r2 in freeze(r2, () => wield(r1) ) foo //│ Type: [outer, 'R] -> (Region['R]) ->{outer} () //│ Where: //│ 'R <: outer fun foo': [outer] -> Region[outer] ->{outer} () fun foo'(r) = foo(r) fun foo: [outer] -> Region[outer] ->{outer} () fun foo(r1) = region r2 in freeze(r2, () => wield(r1) ) foo //│ Type: [outer] -> (Region[outer]) ->{outer} () fun foo: [outer, R extends outer] -> Region[R] ->{outer} () fun foo'(r) = foo(r) // ### Example 3 fun foo(r1, r2, f) = let exec = freeze(r1, () => freeze(r2, () => f(r1, r2))) let r = exec() !(r) + 1 region r in region s in foo(r, s, (x, y) => if rand() then print("Chose x"); () => x.ref 0 else print("Chose y"); () => y.ref 1) //│ Type: Int // TODO: simplify this type more! foo //│ Type: ['r1, 'r2, 'R, 'E, 'R1, 'eff, 'reg, 'ref] -> ('r1, 'r2, ('r1, 'r2) ->{(('E ∧ ¬'R) ∧ ¬'R1) ∧ ¬'R1} (() ->{'eff} Ref['ref, out 'reg])) ->{(('reg ∨ 'R) ∨ 'E) ∨ 'eff} Int //│ Where: //│ 'r1 <: Region[out 'R] //│ 'E <: ¬'R //│ 'R1 <: 'E //│ 'R1 <: ¬'R //│ 'r2 <: Region[out 'R1] //│ 'ref <: Int // *** NOT CURRENTLY USED IN RESPONSE *** fun wield: [R] -> (Region[out R]) ->{R} () fun freeze: [R, E extends ~R, T] -> (Region[out R], () ->{~R | E} T) ->{E} T fun foo(r1, r2, f) = let r = freeze(r1, () => freeze(r2, () => f(r1, r2))) wield(r) region r in foo(r, r, (x, y) => if rand() then x else y) region r in region s in foo(r, s, (x, y) => if rand() then x else y) // *** NOTES *** // Doesn't type check because r.ref has effect r fun foo(r1, r2, f) = let r = freeze(r1, () => freeze(r2, () => f(r1, r2))) r := !(r) + 1 :e region r in region s in foo(r, s, (x, y) => (if rand() then x.ref 0 else y.ref 0)) //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type 'f //│ ║ l.160: foo(r, s, (x, y) => (if rand() then x.ref 0 else y.ref 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain ('x, 'y) ->{'reg ∨ 'reg1} (Ref[Int, in ⊥ out 'reg] ∨ Ref[Int, in ⊥ out 'reg1]) <: 'f //│ ╟── because: cannot constrain ('x, 'y) ->{'reg ∨ 'reg1} (Ref[Int, in ⊥ out 'reg] ∨ Ref[Int, in ⊥ out 'reg1]) <: ('r1, 'r2) ->{'eff} ('app) //│ ╟── because: cannot constrain 'reg ∨ 'reg1 <: 'eff //│ ╟── because: cannot constrain 'reg1 <: 'eff //│ ╟── because: cannot constrain s ∧ ¬r <: 'eff //│ ╟── because: cannot constrain s ∧ ¬r <: 'E ∨ ¬'R //│ ╟── because: cannot constrain 'R ∧ s ∧ ¬r <: 'E //│ ╟── because: cannot constrain 'R ∧ s ∧ ¬r <: ¬'R //│ ╟── because: cannot constrain 'R <: ¬s ∨ r //│ ╙── because: cannot constrain s ∧ ¬r <: ¬s ∨ r //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLBasicADTs.mls ================================================ :global :invalml class IntList with constructor Nil Cons(x: Int, xs: IntList) Nil //│ Type: IntList Cons(1, Cons(2, Cons(3, Nil))) //│ Type: IntList :e class Foo with constructor Bar(x) //│ ╔══[COMPILATION ERROR] Invalid ADT parameter. //│ ║ l.20: Bar(x) //│ ╙── ^ :e Nil(1) //│ ╔══[COMPILATION ERROR] Type error in application //│ ║ l.27: Nil(1) //│ ║ ^^^^^^ //│ ╙── because: cannot constrain IntList <: (Int) ->{'eff} ('app) //│ Type: ⊥ :e Nil() //│ ╔══[COMPILATION ERROR] Type error in application //│ ║ l.35: Nil() //│ ║ ^^^^^ //│ ╙── because: cannot constrain IntList <: () ->{'eff} ('app) //│ Type: ⊥ :e Cons("1", Nil) //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.44: Cons("1", Nil) //│ ║ ^^^ //│ ╙── because: cannot constrain Str <: Int //│ Type: IntList class Option[T] with constructor None Some(x: T) None //│ Type: Option[in ⊤ out ⊥] Some(42) //│ Type: Option[in ⊤ out Int] Some(false) //│ Type: Option[in ⊤ out Bool] class Fun[T, S] with constructor Pure(f: T -> S) Pure(x => x + 1) //│ Type: Fun[in Int out ⊥, in ⊤ out Int] class Value[T] with constructor IntVal(x: Int) extends Value[out Int] BoolVal(x: Bool) extends Value[out Bool] IntVal(42) //│ Type: Value[out Int] BoolVal(false) //│ Type: Value[out Bool] :e IntVal("1") //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.89: IntVal("1") //│ ║ ^^^ //│ ╙── because: cannot constrain Str <: Int //│ Type: Value[out Int] class Expr[T] with constructor Lit[S](x: S) extends Expr[out S] Var[S](nme: Str) extends Expr[out S] Add(lhs: Expr[out Int], rhs: Expr[out Int]) extends Expr[out Int] Lam[U, V](f: Expr[out U] -> Expr[out V]) extends Expr[out U -> V] App[U, V](f: Expr[out U -> V], a: Expr[out U]) extends Expr[out V] let a = Lit(42) a //│ Type: Expr[out Int] let f = Lam(x => Add(x, Lit(1))) f //│ Type: Expr[out Int -> Int] :e Lam(42) //│ ╔══[COMPILATION ERROR] Type error in integer literal with expected type (Expr[out 'U]) ->{⊥} Expr[out 'V] //│ ║ l.113: Lam(42) //│ ║ ^^ //│ ╙── because: cannot constrain Int <: (Expr[in ⊥ out 'U]) ->{⊥} (Expr[in ⊥ out 'V]) //│ Type: Expr[out ⊤ -> ⊥] App(f, a) //│ Type: Expr[out Int] let b = Lit(true) b //│ Type: Expr[out Bool] :e App(f, b) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type Expr[out 'U] //│ ║ l.129: App(f, b) //│ ║ ^ //│ ╟── because: cannot constrain Expr[in ⊥ out 'S] <: Expr[in ⊥ out 'U] //│ ╟── because: cannot constrain 'S <: 'U //│ ╟── because: cannot constrain 'S <: 'U1 //│ ╟── because: cannot constrain Bool <: 'U1 //│ ╙── because: cannot constrain Bool <: Int //│ Type: Expr[out Int] fun sum(xs) = if xs is Nil then 0 Cons(x, xs) then x + sum(xs) sum //│ Type: IntList -> Int sum(Cons(1, Cons(2, Cons(3, Cons(4, Nil))))) //│ Type: Int :e fun badSum(xs) = if xs is Cons(x, xs) then x + badSum(xs) //│ ╔══[COMPILATION ERROR] Expect 2 cases, but 1 got. //│ ║ l.156: Cons(x, xs) then x + badSum(xs) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e fun badSum(xs) = if xs is Cons(x, xs) then x + badSum(xs) Cons(x, xs) then x + badSum(xs) //│ ╔══[COMPILATION ERROR] Duplicate match branches. //│ ║ l.164: Cons(x, xs) then x + badSum(xs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.165: Cons(x, xs) then x + badSum(xs) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expect 2 cases, but 1 got. //│ ║ l.164: Cons(x, xs) then x + badSum(xs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.165: Cons(x, xs) then x + badSum(xs) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fun getOrElse(m, d) = if m is Some(x) then x None then d getOrElse //│ Type: ['d, 'Some] -> (Option[?] ∧ Option[out 'Some], 'd) -> ('Some ∨ 'd) getOrElse(Some(42), 0) //│ Type: Int getOrElse(None, false) //│ Type: Bool getOrElse(Some(42), "42") //│ Type: Int ∨ Str if IntVal(1) is IntVal(x) then 1 BoolVal(x) then 0 //│ Type: Int fun toBool(v) = if v is IntVal(x) then x > 0 BoolVal(x) then x toBool //│ Type: Value[?] -> Bool toBool(IntVal(1)) //│ Type: Bool class MyMap[K, V] with constructor Empty Pair(k: K, v: V, rest: MyMap[out K, out V]) fun newMap(k, v) = Pair(k, v, Empty) newMap //│ Type: ['k, 'v] -> ('k, 'v) -> MyMap[in ⊤ out 'k, in ⊤ out 'v] fun insert(m, k, v) = Pair(k, v, m) insert //│ Type: ['k, 'v, 'K, 'V] -> (MyMap[out 'K, out 'V], 'k, 'v) -> MyMap[in ⊤ out 'K, in ⊤ out 'V] //│ Where: //│ 'k <: 'K //│ 'v <: 'V fun getOrElse: [K, V] -> (MyMap[out K, out V], K, V) -> V fun getOrElse(m, k, d) = if m is Empty then d Pair(k', v, r) then (if k == k' then v else getOrElse(r, k, d)) let incMap1 = newMap(1, 2) incMap1 //│ Type: MyMap[in ⊤ out Int, in ⊤ out Int] let incMap2 = insert(incMap1, 2, 3) incMap2 //│ Type: MyMap[in ⊤ out Int, in ⊤ out Int] getOrElse(incMap2, 3, 4) //│ Type: Int fun toString: Any -> Str fun concat: (Str, Str) -> Str fun print: [T] -> Expr[out T] -> Str fun print(e) = if e is Lit(x) then toString(x) Var(nme) then nme Add(lhs, rhs) then concat(print(lhs), concat(" + ", print(rhs))) Lam(f) then let v = Var("arg") in concat(print(v), concat(" => ", print(f(v)))) App(f, a) then concat(print(f), concat(" ", print(a))) let p = Lam(x => Add(x, Lit(1))) p //│ Type: Expr[out Int -> Int] let a = App(p, Lit(2)) a //│ Type: Expr[out Int] print(p) print(a) //│ Type: Str :e fun foo(x) = if x is Some(x) then 1 None then 0 42 then 42 //│ ╔══[COMPILATION ERROR] Mixing ADT pattern matching and general matching is not supported yet. //│ ║ l.280: 42 then 42 //│ ╙── ^^^^^^^^^^ //│ ═══[COMPILATION ERROR] Variable not found: arg$Some$0$ fun foo(x) = if x is Some(x) then x else 0 class List[out T] with constructor Nil Cons(x: T, xs: List[T]) Cons(42, Nil) //│ Type: List[out Int] fun foo: [T] -> T -> List[T] foo //│ Type: ['T] -> ('T) ->{⊥} List[out 'T] class Bar[in T] with constructor B(f: T -> Int) B(x => x + 1) //│ Type: Bar[in Int] fun bar: [T] -> (T, Bar[T]) -> Int bar //│ Type: ['T] -> ('T, Bar[in 'T]) ->{⊥} Int class Option[out T] with constructor None Some(x: T) fun headOpt(xs) = if xs is Nil then None Cons(x, xs) then Some(x) headOpt //│ Type: ['Cons] -> (List[out 'Cons] ∧ List[?]) -> (Option[⊥] ∨ Option[out 'Cons]) headOpt(Nil) //│ Type: Option[⊥] headOpt(Cons(42, Nil)) //│ Type: Option[⊥] ∨ Option[out Int] fun getOrElse(v, d) = if v is Some(x) then x else d getOrElse //│ Type: ['d, 'Some] -> (Option[out 'Some], 'd) -> ('Some ∨ 'd) getOrElse(headOpt(Nil), "foo") //│ Type: Str getOrElse(headOpt(Cons(42, Nil)), 1) //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLBasics.mls ================================================ :invalml 123 //│ Type: Int 3.14 //│ Type: Num false //│ Type: Bool "invalml" //│ Type: Str () fun id(x) = x id //│ Type: ['x] -> 'x -> 'x fun inc(x) = x + 1 fun mul(x, y) = x * y x => x //│ Type: ('x) ->{⊥} 'x + //│ Type: (Int, Int) ->{⊥} Int (x, y) => x + y //│ Type: (Int, Int) ->{⊥} Int ((x, y) => x + y)(40, 2) //│ Type: Int :e 114 + "514" //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.39: 114 + "514" //│ ║ ^^^^^ //│ ╙── because: cannot constrain Str <: Int //│ Type: Int inc(42) //│ Type: Int let x = 42 in x //│ Type: Int let x = 1 in let y = 2 in x + y //│ Type: Int :e let x = 0 in x + "1" //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.57: let x = 0 in x + "1" //│ ║ ^^^ //│ ╙── because: cannot constrain Str <: Int //│ Type: Int data class Foo(x: Int) :e new Nothingness(0) //│ ╔══[COMPILATION ERROR] Name not found: Nothingness //│ ║ l.67: new Nothingness(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[COMPILATION ERROR] Not a valid class: ‹error› //│ Type: ⊥ :e new 42 //│ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. //│ ║ l.77: new 42 //│ ║ ^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ╔══[COMPILATION ERROR] Not a valid class: integer literal //│ ║ l.77: new 42 //│ ╙── ^^ //│ Type: ⊥ new Foo(42) //│ Type: Foo data class Point(x: Num, y: Num, z: Num) new Point(0.0, 0.0, 0.0) //│ Type: Point :e new Foo("1!5!") //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.99: new Foo("1!5!") //│ ║ ^^^^^^ //│ ╙── because: cannot constrain Str <: Int //│ Type: Foo data class Some[A](value: A) new Some(true) //│ Type: Some['A] //│ Where: //│ Bool <: 'A new Some(42) //│ Type: Some['A] //│ Where: //│ Int <: 'A let p = new Point(1.0, 0.0, 0.0) in p.Point#x //│ Type: Num let t = new Some(true) in t.Some#value //│ Type: Bool :fixme :pe 42.Some#value //│ ╔══[LEXICAL ERROR] Expected at least one digit after the decimal point //│ ║ l.127: 42.Some#value //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (infix operator '#'). //│ ║ l.127: 42.Some#value //│ ╙── ^^^^^^^^^^ //│ Type: Num data class Printer[T](f: T -> Str) fun foofoo(x) = let t = x + 1 in "foo" new Printer(foofoo) //│ Type: Printer['T] //│ Where: //│ 'T <: Int let ip = new Printer(foofoo) in ip.Printer#f(42) //│ Type: Str :e let ip = new Printer(foofoo) in ip.Printer#f("42") //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type 'T //│ ║ l.151: let ip = new Printer(foofoo) in ip.Printer#f("42") //│ ║ ^^^^ //│ ╟── because: cannot constrain Str <: 'T //│ ╟── because: cannot constrain Str <: 'T //│ ╙── because: cannot constrain Str <: Int //│ Type: Str data class TFun[T](f: T -> T) fun inc(x) = x + 1 new TFun(inc) //│ Type: TFun['T] //│ Where: //│ Int <: 'T //│ 'T <: Int let tf = new TFun(inc) in tf.TFun#f(1) //│ Type: Int :e let tf = new TFun(inc) in tf.TFun#f("1") //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type 'T //│ ║ l.174: let tf = new TFun(inc) in tf.TFun#f("1") //│ ║ ^^^ //│ ╟── because: cannot constrain Str <: 'T //│ ╟── because: cannot constrain Str <: 'T //│ ╙── because: cannot constrain Str <: Int //│ Type: Str ∨ Int data class Pair[A, B](fst: A, snd: B) (new Pair(42, true)).Pair#fst //│ Type: Int (new Pair(42, true)).Pair#snd //│ Type: Bool new Pair(id, id) //│ Type: Pair['A, 'B] //│ Where: //│ 'x -> 'x <: 'A //│ 'x1 -> 'x1 <: 'B new Pair(x => x, x => x) //│ Type: Pair['A, 'B] //│ Where: //│ 'x -> 'x <: 'A //│ 'x1 -> 'x1 <: 'B if 1 < 2 then 1 else 0 //│ Type: Int if false then 1 else "1" //│ Type: Int ∨ Str if 1 is Int then 1 else 0 //│ Type: Int if 1 is Foo then 0 else 1 //│ Type: Int fun test(x) = if x is Int then x + 1 else 0 test //│ Type: (¬Int ∨ Int) -> Int test(1) //│ Type: Int test("1") //│ Type: Int fun fact(n) = if n > 1 then n * fact(n - 1) else 1 fact //│ Type: Int -> Int fact(1) //│ Type: Int fun fact2 = x => fact2(1) fun fact2 = x => if x is n then fact2(n - 1) fun fact2(x) = if x is n then fact2(n - 1) fun fact2 = case 0 then 1 n then n * fact2(n - 1) fact2 //│ Type: Int -> Int fact2(0) //│ Type: Int data class Foo[A](x: A -> A) new Foo(x => x) //│ Type: Foo['A] fun f(g) = new Foo(g) f //│ Type: ['A] -> ('A -> 'A) -> Foo['A] f(x => x).Foo#x //│ Type: ('A) ->{⊥} 'A g => (new Foo(g)).Foo#x //│ Type: ('A -> 'A) ->{⊥} ('A) ->{⊥} 'A throw new Error("oops") //│ Type: ⊥ :e throw 42 //│ ╔══[COMPILATION ERROR] Type error in throw //│ ║ l.286: throw 42 //│ ║ ^^^^^^^^ //│ ╙── because: cannot constrain Int <: Error //│ Type: ⊥ ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLBorrowing.mls ================================================ :invalml class Reg[Rg, Br] fun letreg: [E, Res] -> ([Rg] -> Reg[Rg, E] ->{E | Rg} Res) ->{E} Res fun use_: [Rg, Br] -> Reg[Rg, Br] ->{Rg} Int class MutVec[A, Rg, Br] fun mkVec: [A, Rg, Br] -> Reg[Rg, Br] -> MutVec[A, Rg, Br] fun clear: [A, Rg, Br] -> MutVec[A, Rg, Br] ->{Rg} Int class Iter[A, Rg] fun iterate: [A, Rg, Br, Res] -> MutVec[A, Rg, Br] -> ([L] -> Iter[A, Br | L] ->{Br | L} Res) ->{Br} Res fun integers: [Rg, Br, Res] -> Reg[Rg, Br] ->{Rg} (Iter[Int, Br] ->{Br} Res) ->{Br} Res fun next: [A, Br] -> Iter[A, Br] ->{Br} A letreg(r => r) //│ Type: Reg[?, 'E] //│ Where: //│ 'E <: ⊥ letreg of r => let b = mkVec(r) clear(b) iterate(b) of it => next(it) 123 clear(b) r //│ Type: Reg[?, 'E] //│ Where: //│ 'E <: ⊥ // * Non-lexical borrowing pattern encoded with a thunk :w letreg of r => let b = mkVec(r) clear(b) let k = iterate(b) of it => next(it) 123 if next(it) > 0 then () => 0 else () => clear(b) k() //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.51: 123 //│ ╙── ^^^ //│ Type: Int :w :e letreg of r => let b = mkVec(r) clear(b) let k = iterate(b) of it => next(it) 123 () => next(it) k() //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.66: 123 //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.61: letreg of r => //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.62: let b = mkVec(r) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.63: clear(b) //│ ║ ^^^^^^^^^^ //│ ║ l.64: let k = iterate(b) of it => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.65: ... (more lines omitted) ... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'E <: ⊥ //│ ╟── because: cannot constrain ¬'Rg <: ⊥ //│ ╟── because: cannot constrain ⊤ <: 'Rg //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: ⊥ :e letreg of r => let b = mkVec(r) clear(b) iterate(b) of it => next(it) clear(b) 123 clear(b) r //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.90: letreg of r => //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.91: let b = mkVec(r) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.92: clear(b) //│ ║ ^^^^^^^^^^ //│ ║ l.93: iterate(b) of it => //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.94: ... (more lines omitted) ... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'E <: ⊥ //│ ╟── because: cannot constrain 'Rg <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Reg[?, 'E] //│ Where: //│ ⊤ <: 'E //│ 'E <: ⊥ letreg of r => use_(r) integers(r) of it => next(it) use_(r) r //│ Type: Reg[?, 'E] //│ Where: //│ 'E <: ⊥ :e letreg of r => use_(r) integers(r) of it => use_(r) next(it) use_(r) r //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.130: letreg of r => //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.131: use_(r) //│ ║ ^^^^^^^^^ //│ ║ l.132: integers(r) of it => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.133: use_(r) //│ ║ ^^^^^^^^^^^ //│ ║ l.134: ... (more lines omitted) ... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'E <: ⊥ //│ ╟── because: cannot constrain 'Rg <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Reg[?, 'E] //│ Where: //│ ⊤ <: 'E //│ 'E <: ⊥ letreg of r0 => letreg of r1 => integers(r1) of it => use_(r0) next(it) use_(r1) r1 //│ Type: Reg[?, in 'E] //│ Where: //│ 'E <: ⊥ // * Can leak the iterator :w fun iterate_unsafe: [A, Rg, Br, Res] -> MutVec[A, Rg, Br] -> (Iter[A, Br] ->{Br} Res) ->{Br} Res // * Eg: letreg of r => let b = mkVec(r) clear(b) let k = iterate_unsafe(b) of it => next(it) 123 () => next(it) clear(b) k() //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.177: 123 //│ ╙── ^^^ //│ Type: ⊥ ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLBorrowing2.mls ================================================ :invalml class Reg[Rg, Br] fun letreg: [E, Res] -> ([Rg] -> Reg[Rg, E] ->{E | Rg} Res) ->{E} Res fun subreg: [E, Rg, Br, Res] -> Reg[Rg, Br] -> ([Rg2] -> Reg[Rg2 & ~Rg, E] ->{E | Rg2} Res) ->{E} Res fun read: [Rg, Br] -> Reg[Rg, Br] ->{Br} Int fun write: [Rg, Br] -> Reg[Rg, Br] ->{Rg} Int fun borrow: [Rg, Br, Res] -> Reg[Rg, Br] ->{Rg} (() ->{Br} Res) ->{Br} Res letreg of r => read(r) borrow(r) of () => read(r) write(r) //│ Type: Int :e letreg of r => read(r) borrow(r) of () => write(r) read(r) write(r) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.24: letreg of r => //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.25: read(r) //│ ║ ^^^^^^^^^ //│ ║ l.26: borrow(r) of () => //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.27: write(r) //│ ║ ^^^^^^^^^^^^ //│ ║ l.28: ... (more lines omitted) ... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'E <: ⊥ //│ ╟── because: cannot constrain 'Rg <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int fun borrow: [Rg, Br, Res] -> Reg[Rg, Br] ->{Rg} (() ->{Br | ~Rg} Res) ->{Br} Res letreg of r => read(r) borrow(r) of () => read(r) write(r) //│ Type: Int :e letreg of r => read(r) borrow(r) of () => write(r) read(r) write(r) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.58: letreg of r => //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.59: read(r) //│ ║ ^^^^^^^^^ //│ ║ l.60: borrow(r) of () => //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.61: write(r) //│ ║ ^^^^^^^^^^^^ //│ ║ l.62: ... (more lines omitted) ... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'E <: ⊥ //│ ╟── because: cannot constrain 'Rg <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int // * TODO... ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLBounds.mls ================================================ :invalml (x => x + 1) as [A extends Int] -> A -> Int //│ Type: (Int) ->{⊥} Int (x => x) as [A restricts Int] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A //│ Where: //│ Int <: 'A (x => x - 1) as [A extends Int restricts Int] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A //│ Where: //│ Int <: 'A //│ 'A <: Int fun iid: [A extends Int] -> A -> A fun iid(x) = x iid //│ Type: ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'A <: Int :e iid("42") //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type 'A //│ ║ l.29: iid("42") //│ ║ ^^^^ //│ ╟── because: cannot constrain Str <: 'A //│ ╙── because: cannot constrain Str <: Int //│ Type: Str iid(42) //│ Type: Int class Foo[A] fun foo: [A extends Foo[in Nothing out Any] restricts Foo[in Num]] -> A -> A foo //│ Type: ['A] -> ('A) ->{⊥} 'A //│ Where: //│ Foo[in Num] <: 'A //│ 'A <: Foo[?] fun bar: Foo[in Num out Int] foo(bar) //│ Type: Foo[in Num out Int] ∨ Foo[in Num] :e fun badfoo: [A extends Str restricts Int] -> A -> A //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.56: fun badfoo: [A extends Str restricts Int] -> A -> A //│ ║ ^^^^^^ //│ ╙── because: cannot constrain Int <: Str fun baz: [A extends B, B extends A] -> A -> B baz //│ Type: (⊤) ->{⊥} ⊤ fun bazbaz: [A extends Int] -> A -> [B extends A] -> B bazbaz //│ Type: (Int) ->{⊥} ⊥ fun foofun: [A extends Int -> Int restricts Any -> Int] -> A -> Int -> Int foofun //│ Type: (Int -> Int) ->{⊥} (Int) ->{⊥} Int foofun(x => x + 1)(42) //│ Type: Int fun bazbaz: [A extends Int] -> A -> ([B extends A -> A restricts A -> A] -> B) -> A bazbaz //│ Type: ['A] -> ('A) ->{⊥} ('A -> 'A) ->{⊥} 'A //│ Where: //│ 'A <: Int bazbaz(42)(x => x + 1) //│ Type: Int fun cc: [A extends B -> B restricts B -> B, B extends A -> A restricts A -> A] -> A -> B -> Bool cc //│ Type: ['A, 'B] -> ('A) ->{⊥} ('B) ->{⊥} Bool //│ Where: //│ 'A -> 'A <: 'B //│ 'B <: 'A -> 'A //│ 'B -> 'B <: 'A //│ 'A <: 'B -> 'B fun w: Any -> Nothing cc(w)(w) //│ Type: Bool fun h: [C] -> ([A extends Int] -> A -> ([B extends A -> A restricts A -> A] -> B) -> A) -> C -> Int :e bazbaz as [A extends Int] -> A -> ([B extends A -> A restricts A -> A] -> B) -> A //│ ╔══[COMPILATION ERROR] Cannot type non-function term Ref(member:bazbaz) as (A) ->{⊥} ([outer, 'B] -> 'B) ->{⊥} A //│ ║ l.104: bazbaz as [A extends Int] -> A -> ([B extends A -> A restricts A -> A] -> B) -> A //│ ╙── ^^^^^^ //│ Type: ['A] -> ('A) ->{⊥} ('A -> 'A) ->{⊥} 'A //│ Where: //│ 'A <: Int (x => f => bazbaz(x)(f)) as [A extends Int] -> A -> ([B extends A -> A restricts A -> A] -> B) -> A //│ Type: ['A] -> ('A) ->{⊥} ('A -> 'A) ->{⊥} 'A //│ Where: //│ 'A <: Int h(x => f => bazbaz(x)(f))(42) //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLCheck.mls ================================================ :invalml fun add: (Int, Int) -> Int fun add(x, y) = x + y // FIXME locations :e fun foo: Int -> Int fun foo: Int -> Int //│ ╔══[COMPILATION ERROR] Multiple declarations of symbol 'foo' //│ ╟── declared here //│ ║ l.9: fun foo: Int -> Int //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── declared here //│ ║ l.10: fun foo: Int -> Int //│ ╙── ^^^^^^^^^^^^^^^^^^^ :e fun id(x) = x fun id(y) = y //│ ╔══[COMPILATION ERROR] Multiple definitions of symbol 'id' //│ ╟── defined here //│ ║ l.20: fun id(x) = x //│ ║ ^^^^^^^^^^^^^ //│ ╟── defined here //│ ║ l.21: fun id(y) = y //│ ╙── ^^^^^^^^^^^^^ :e fun bar: Str -> Str fun bar(x) = let t = x + 1 in "aaa" //│ ╔══[COMPILATION ERROR] Type error in reference with expected type Int //│ ║ l.32: fun bar(x) = let t = x + 1 in "aaa" //│ ║ ^ //│ ╙── because: cannot constrain Str <: Int :e fun baz: Int -> Int fun baz(x) = "bbb" //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.40: fun baz(x) = "bbb" //│ ║ ^^^^^ //│ ╙── because: cannot constrain Str <: Int add(1, 2) //│ Type: Int add(1, 2) as Int //│ Type: Int :e add(0, 0) as Str //│ ╔══[COMPILATION ERROR] Type error in application of type ‹not implemented› with expected type Str //│ ║ l.54: add(0, 0) as Str //│ ║ ^^^^^^^^^ //│ ╙── because: cannot constrain Int <: Str //│ Type: Str :e fun errAdd(x: Int) = x + "1" //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type Int //│ ║ l.62: fun errAdd(x: Int) = x + "1" //│ ║ ^^^ //│ ╙── because: cannot constrain Str <: Int fun high: ([A] -> A -> A) -> Int fun high(f) = f(42) high //│ Type: (['A] -> ('A) ->{⊥} 'A) ->{⊥} Int high((x => x) as [A] -> A -> A) //│ Type: Int high(x => x) //│ Type: Int :e high(x => x + 1) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type Int //│ ║ l.82: high(x => x + 1) //│ ║ ^ //│ ╙── because: cannot constrain A <: Int //│ ╔══[COMPILATION ERROR] Type error in application with expected type A //│ ║ l.82: high(x => x + 1) //│ ║ ^^^^^ //│ ╙── because: cannot constrain Int <: A //│ Type: Int (let a = 0 in x => x) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A (if false then x => x else y => y) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A fun baz: Int -> (([A] -> A -> A), Int) -> Int fun baz(z) = ((f, x) => f(x)) fun baz: Int -> (([A] -> A -> A), Int) -> Int fun baz(z) = ((f, x) => f(x)) as (([A] -> A -> A), Int) -> Int :e baz as Int -> (([A] -> A -> A), Int) -> Int //│ ╔══[COMPILATION ERROR] Cannot type non-function term Ref(member:baz) as (Int) ->{⊥} ([outer, 'A] -> ('A) ->{⊥} 'A, Int) ->{⊥} Int //│ ║ l.110: baz as Int -> (([A] -> A -> A), Int) -> Int //│ ╙── ^^^ //│ Type: ⊥ baz(42) //│ Type: (['A] -> ('A) ->{⊥} 'A, Int) ->{⊥} Int :e baz(42) as (([A] -> A -> A), Int) -> Int //│ ╔══[COMPILATION ERROR] Cannot type non-function term App(Ref(member:baz),Tup(List(Fld(‹›,Lit(IntLit(42)),None)))) as ([outer, 'A] -> ('A) ->{⊥} 'A, Int) ->{⊥} Int //│ ║ l.121: baz(42) as (([A] -> A -> A), Int) -> Int //│ ╙── ^^^^^^^ //│ Type: ⊥ (z => (f, x) => baz(z)(f, x)) as Int -> (([A] -> A -> A), Int) -> Int //│ Type: (Int) ->{⊥} (['A] -> ('A) ->{⊥} 'A, Int) ->{⊥} Int fun id: [A] -> A -> A fun id(x) = x id as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A (id as [A] -> A -> A) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A 42 as Int | Num //│ Type: Int ∨ Num 42 as [A] -> Int //│ Type: Int 42 as [A] -> Int | Num //│ Type: Int ∨ Num fun foo: Int -> Int foo //│ Type: (Int) ->{⊥} Int foo //│ Type: (Int) ->{⊥} Int fun foo: [A] -> (A, ~A) -> A foo //│ Type: ['A] -> ('A, ¬'A) ->{⊥} 'A :e foo(42, 1) //│ ╔══[COMPILATION ERROR] Type error in integer literal with expected type ¬'A //│ ║ l.166: foo(42, 1) //│ ║ ^ //│ ╟── because: cannot constrain Int <: ¬'A //│ ╟── because: cannot constrain 'A <: ¬{Int} //│ ╙── because: cannot constrain Int <: ¬{Int} //│ Type: Int foo(42, false) //│ Type: Int fun f: ([A] -> A -> Int) -> ([A] -> A -> Int) f //│ Type: ((⊤) ->{⊥} Int) ->{⊥} (⊤) ->{⊥} Int fun f: ([A] -> ([B] -> A | B -> B)) -> ([A] -> ([B] -> A | B -> B)) f //│ Type: (['B] -> (⊤) ->{⊥} 'B) ->{⊥} ['B] -> (⊤) ->{⊥} 'B fun f: ([A] -> ([B] -> A & B -> B)) -> ([A] -> ([B] -> A & B -> B)) f //│ Type: (['B] -> ('B) ->{⊥} 'B) ->{⊥} ['B] -> ('B) ->{⊥} 'B fun f: ([A] -> ([B] -> A -> B & A)) -> ([A] -> ([B] -> A -> B | A)) f //│ Type: (['A] -> ('A) ->{⊥} ⊥) ->{⊥} ['A] -> ('A) ->{⊥} 'A fun f: ([A] -> ([B] -> A | B -> B) -> A) -> ([A] -> ([B] -> A | B -> B) -> A) f //│ Type: ((['B] -> ('B) ->{⊥} 'B) ->{⊥} ⊥) ->{⊥} (['B] -> ('B) ->{⊥} 'B) ->{⊥} ⊥ fun f: ([A] -> ([B] -> A | B -> B | A) -> A) -> ([A] -> ([B] -> A | B -> B | A) -> A) f //│ Type: (['A] -> (['B] -> ('A ∨ 'B) ->{⊥} 'B ∨ 'A) ->{⊥} 'A) ->{⊥} ['A] -> (['B] -> ('A ∨ 'B) ->{⊥} 'B ∨ 'A) ->{⊥} 'A fun f: [A] -> [B] -> ([C] -> A | B -> B | C -> A | C) -> B f //│ Type: ['A] -> (['C] -> ('A) ->{⊥} ('C) ->{⊥} 'A ∨ 'C) ->{⊥} ⊥ fun f: [A, B] -> ([C] -> A | B -> B | C -> A | B | C) -> B f //│ Type: ['A, 'B] -> (['C] -> ('A ∨ 'B) ->{⊥} ('B ∨ 'C) ->{⊥} ('A ∨ 'B) ∨ 'C) ->{⊥} 'B ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls ================================================ :js :global :invalml :sjs 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 //│ Type: Int :sjs 1 + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 1 + 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 //│ Type: Int :sjs 1.0 +. 2.14 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 1.0 + 2.14 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3.14 //│ Type: Num :sjs let x = 1 in x + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x; x = 1; x + 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 //│ Type: Int :sjs "abc" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ "abc" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "abc" //│ Type: Str :sjs false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ false //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = false //│ Type: Bool :sjs (x => x) as [T] -> T -> T //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; lambda1 = (undefined, function (x1) { return x1 }); lambda1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun //│ Type: ['T] -> ('T) ->{⊥} 'T :sjs data class Foo(x: Int) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let Foo1; //│ Foo1 = function Foo(x1) { //│ return globalThis.Object.freeze(new Foo.class(x1)); //│ }; //│ (class Foo { //│ static { //│ Foo1.class = this //│ } //│ constructor(x1) { //│ this.x = x1; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["x"]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs new Foo(42) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ globalThis.Object.freeze(new Foo1.class(42)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Foo(42) //│ Type: Foo :sjs let foo = new Foo(42) in foo.Foo#x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo; foo = globalThis.Object.freeze(new Foo1.class(42)); foo.x //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 //│ Type: Int :sjs fun inc(x) = x + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let inc; inc = function inc(x1) { return x1 + 1 }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs inc(41) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ inc(41) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 //│ Type: Int :sjs if 1 == 2 then 0 else 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; scrut = 1 == 2; if (scrut === true) { 0 } else { 42 } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 //│ Type: Int :sjs if 1 is Int then 1 else 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut1; scrut1 = 1; if (globalThis.Number.isInteger(scrut1)) { 1 } else { 0 } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 //│ Type: Int data class Foo() :sjs let foo = new Foo() if foo is Foo then 1 else 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo1; //│ foo1 = globalThis.Object.freeze(new Foo3.class()); //│ if (foo1 instanceof Foo3.class) { 1 } else { 0 } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 //│ foo = Foo() //│ Type: Int :sjs fun pow(x) = case 0 then 1 n then x * pow(x)(n-1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let pow; //│ pow = function pow(x1) { //│ let lambda2; //│ lambda2 = (undefined, function (caseScrut) { //│ let n, baseCall, tmp, tmp1; //│ if (caseScrut === 0) { //│ return 1 //│ } //│ n = caseScrut; //│ baseCall = pow(x1); //│ tmp = n - 1; //│ tmp1 = runtime.safeCall(baseCall(tmp)); //│ return x1 * tmp1; //│ }); //│ return lambda2 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun nott = case true then false false then true //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let nott; //│ nott = function nott() { //│ let lambda2; //│ lambda2 = (undefined, function (caseScrut) { //│ switch (caseScrut) { //│ case true: //│ return false; //│ case false: //│ return true; //│ } //│ throw globalThis.Object.freeze(new globalThis.Error.class("match error")) //│ }); //│ return lambda2 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect true :sjs nott of false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = nott(); runtime.safeCall(tmp(false)) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true //│ Type: Bool :sjs fun fact = case 0 then 1 n then n * fact(n - 1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let fact; //│ fact = function fact() { //│ let lambda2; //│ lambda2 = (undefined, function (caseScrut) { //│ let n, tmp1, tmp2, tmp3; //│ if (caseScrut === 0) { //│ return 1 //│ } //│ n = caseScrut; //│ tmp1 = fact(); //│ tmp2 = n - 1; //│ tmp3 = runtime.safeCall(tmp1(tmp2)); //│ return n * tmp3; //│ }); //│ return lambda2 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 6 fact(3) //│ = 6 //│ Type: Int :sjs region x in 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ new globalThis.Region(); 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 //│ Type: Int :sjs region x in x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x1; x1 = new globalThis.Region(); x1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Region //│ Type: Region[?] :sjs region x in x.ref 42 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x2; x2 = new globalThis.Region(); new globalThis.Ref(x2, 42) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = Ref(Region, 42) //│ Type: Ref[Int, ?] :sjs region x in let y = x.ref 42 in !(y) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x3, y; x3 = new globalThis.Region(); y = new globalThis.Ref(x3, 42); y.value //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 //│ Type: Int :sjs region x in let y = x.ref 42 in y := 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let x4, y1; x4 = new globalThis.Region(); y1 = new globalThis.Ref(x4, 42); y1.value = 0; 0 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 //│ Type: Int data class LsAlg[A, E](nil:() -> E, cons: (A, E) -> E) fun nil(x) = x.LsAlg#nil() fun cons(x, y, z) = x.LsAlg#cons(y, z) data class Nil() data class Cons[A, B](val x: A,val y: B) fun mk() = new LsAlg(() => new Nil, (x, y) => new Cons(x, y)) // fun xs: [E] -> LsAlg[in Int, E] -> E fun xs(x) = x cons(1, x nil()) fun ys: [E] -> LsAlg[in Nothing, E] -> E fun ys(x) = x nil() fun zs: [E] -> LsAlg[in Int | E, E] -> E fun zs(x) = x cons(xs(x), x cons(ys(x),x nil())) mk() zs() //│ = Cons(Cons(1, fun), Cons(fun, fun)) //│ Type: Nil ∨ Cons['A, 'B] //│ Where: //│ ('E ∨ Cons['A, 'B]) ∨ Nil <: 'B //│ Nil <: 'E //│ Cons['A, 'B] <: 'E //│ 'E ∨ Int <: 'A ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLCyclicExtrude.mls ================================================ :invalml let f = (x => (x as [A] -> A -> A)) in f //│ Type: (⊤ -> ⊥) ->{⊥} ['A] -> ('A) ->{⊥} 'A let f = ((x => x) as [A] -> A -> A) in f //│ Type: ['A] -> ('A) ->{⊥} 'A let foo = f => (x => f(x(x)) as [A] -> A -> A) in foo //│ Type: ('app -> (⊤ -> ⊥)) ->{⊥} ('x -> 'app) ->{⊥} ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'x <: 'x -> 'app f => (let g = x => x(x) in f(g(g))) as [A] -> A -> A //│ Type: (⊥ -> (⊤ -> ⊥)) ->{⊥} ['A] -> ('A) ->{⊥} 'A :e f => (let g = x => f(x(x)) in g) as [A] -> A -> A //│ ╔══[COMPILATION ERROR] Type error in block with expected type (A) ->{⊥} A //│ ║ l.20: f => (let g = x => f(x(x)) in g) as [A] -> A -> A //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain ('x) ->{'eff ∨ 'eff1} ('app) <: (A) ->{⊥} (A) //│ ╟── because: cannot constrain A <: 'x //│ ╙── because: cannot constrain A <: ('x) ->{'eff1} ('app1) //│ Type: (⊥ -> ⊥) ->{⊥} ['A] -> ('A) ->{⊥} 'A f => (x => f(x(x)) as [A] -> A -> A) //│ Type: ('app -> (⊤ -> ⊥)) ->{⊥} ('x -> 'app) ->{⊥} ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'x <: 'x -> 'app let foo = f => (f(x => x(x)) as [A] -> A -> A) in foo //│ Type: ((('x ->{'eff} 'app) ->{'eff} 'app) -> (⊤ -> ⊥)) ->{⊥} ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'x <: 'x ->{'eff} 'app let foo(f) = (f(x => x(x)) as [A] -> A -> A) in foo //│ Type: ((('x ->{'eff} 'app) ->{'eff} 'app) -> (⊤ -> ⊥)) ->{⊥} ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'x <: 'x ->{'eff} 'app :todo fun foo(f) = (f(x => x(x)) as [A] -> A -> A) //│ ╔══[COMPILATION ERROR] Expected a monomorphic type or an instantiable type here, but ('f) ->{⊥} [outer, 'A] -> ('A) ->{⊥} 'A found //│ ║ l.45: fun foo(f) = (f(x => x(x)) as [A] -> A -> A) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ f => (let g = x => x(x) in let tmp = g(g) in f(g)) as [A] -> A -> A //│ Type: (((('x ∨ ('x ->{'eff} 'app)) -> 'app) ->{'eff} 'app) -> (⊤ -> ⊥)) ->{⊥} ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'x <: ('x ∨ ('x ->{'eff} 'app)) -> 'app ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLDP.mls ================================================ :js :global :invalml declare class Array[T] fun mkArray: [T] -> () -> Array[T] fun mkArray() = @untyped globalThis.Array() fun mkArrayWith: [T] -> (T, Int) -> Array[T] fun mkArrayWith(iv, size) = @untyped globalThis.Array(size).fill(iv) fun at: [T] -> (Array[T], Int) -> T fun at(xs, i) = @untyped xs.at(i) fun splice: [T] -> (Array[T], Int, T) -> () fun splice(xs, i, v) = @untyped xs.splice(i, 1, v); () class Option[A] with constructor Some(x: A) None() class ArrayList[T, out R](val d: Array[T]) class Iter[T, out R](val arr: Array[T], val i: Ref[Int, out R], val step: Int, val end: Int) class Array2D[T, out R](val d: Array[Array[T]]) class Interviewee with constructor Itv(score: Int, salary: Int) fun debug: Any -> () fun debug(s) = @untyped print(s) fun (;) seq(_, res) = res fun toString: Any -> Str fun toString(x) = @untyped x.toString() fun concat: (Str, Str) -> Str fun concat(x, y) = @untyped x + y fun println: Str -> () fun println(s) = @untyped print(s) fun empty: [A, R] -> Region[out R] ->{R} ArrayList[A, R] fun empty(r) = new ArrayList(mkArray()) fun push: [A, R] -> (ArrayList[A, R], A) ->{R} () fun push(arr, e) = @untyped arr.ArrayList#d.push(e); () fun len: [A, R] -> (ArrayList[A, R]) ->{R} Int fun len(arr) = @untyped arr.ArrayList#d.length fun iter: [Res, R, E extends ~R, T] -> (ArrayList[T, R], [S] -> Iter[T, S] ->{S | E} Res) ->{E | R} Res fun iter(arr, f) = region r in f(new Iter(arr.ArrayList#d, (r.ref 0), 1, len(arr))) fun revIter: [Res, R, E extends ~R, T] -> (ArrayList[T, R], [S] -> Iter[T, S] ->{S | E} Res) ->{E | R} Res fun revIter(arr, f) = region r in f(new Iter(arr.ArrayList#d, (r.ref (len(arr) - 1)), 0 - 1, 0 - 1)) :sjs fun next: [T, S] -> Iter[T, S] ->{S} Option[T] fun next(it) = let i = !(it.Iter#i) if i == it.Iter#end then None() else let res = Some(at of it.Iter#arr, i) it.Iter#i := i + it.Iter#step res //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let next; //│ next = function next(it) { //│ let i, scrut, res, tmp, tmp1; //│ i = it.i.value; //│ scrut = i == it.end; //│ if (scrut === true) { //│ return None1() //│ } //│ tmp = at(it.arr, i); //│ res = Some1(tmp); //│ tmp1 = i + it.step; //│ it.i.value = tmp1; //│ return res; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun whileDo: [R] -> (() ->{R} Bool) ->{R} () fun whileDo(f) = if f() then whileDo(f) else () fun init: [A, R] -> (Region[out R], Int, Int, A) ->{R} Array2D[A, R] fun init(r, d1, d2, iv) = let res = new Array2D(mkArrayWith(mkArrayWith(iv, d2), d1)) region r in let i = r.ref 0 whileDo of () => if !(i) == d1 then false else splice(res.Array2D#d, !(i), mkArrayWith(iv, d2)) i := !(i) + 1 true res fun update: [A, R] -> (Array2D[A, R], Int, Int, A) ->{R} () fun update(arr, d1, d2, v) = splice(at(arr.Array2D#d, d1), d2, v) fun get: [A, R] -> (Array2D[A, R], Int, Int) ->{R} A fun get(arr, d1, d2) = at(at(arr.Array2D#d, d1), d2) fun max: (Int, Int) -> Int fun max(x, y) = if x > y then x else y fun format(it) = if it is Itv(score, salary) then concat("interviewee score: ", concat(toString(score), concat(", salary: ", toString(salary)))) fun printAll(arr) = iter of arr, it => whileDo of () => if next(it) is Some(x) then println(format(x)); false None then false // fun select: [outer, R1 extends outer, R2 extends ~R1] -> (ArrayList[Interviewee, R1], Int, ArrayList[Interviewee, R2]) ->{R1 | R2} Int fun select(interviewees, budget, results) = region r in let size = len(interviewees), let i = r.ref 1 let dp = init(r, size + 1, budget + 1, 0) iter of interviewees, it => whileDo of () => if next(it) is Some(itv) then if itv is Itv(score, salary) then let j = r.ref 0 whileDo of () => if !(j) < salary then update(dp, !(i), !(j), get(dp, !(i) - 1, !(j))) else let p = get(dp, !(i) - 1, !(j) - salary), let np = get(dp, !(i) - 1, !(j)) update(dp, !(i), !(j), max of np, p + score) j := !(j) + 1; !(j) <= budget i := !(i) + 1 true None then false i := size let rest = r.ref budget revIter of interviewees, it => whileDo of () => if next(it) is Some(itv) then if itv is Itv(score, salary) then if get(dp, !(i), !(rest)) == get(dp, !(i) - 1, !(rest) - salary) + score then push(results, itv); rest := !(rest) - salary else () i := !(i) - 1 true None then false get(dp, size, budget) select //│ = fun select //│ Type: [outer, 'A, 'R, 'T, 'R1, 'T1, 'T2, 'R2, 'A1, 'R3] -> ((ArrayList[in 'T1 out 'T2, out 'R2] ∧ ArrayList[in 'T out Interviewee ∧ 'T, out 'R1]) ∧ ArrayList['A, out 'R], Int, ArrayList['A1, out 'R3]) ->{(('R2 ∨ 'R3) ∨ 'R1) ∨ 'R} Int //│ Where: //│ 'T <: Interviewee //│ 'T <: 'T1 //│ 'R <: outer //│ 'R1 <: outer //│ 'T <: 'A //│ 'R2 <: ¬'R region r in let interviewees = empty(r) push(interviewees, Itv(20, 3)) push(interviewees, Itv(50, 1)) push(interviewees, Itv(30, 1)) println("all interviewees:") printAll(interviewees) region r2 in let results = empty(r2) let m = select(interviewees, 4, results) println("candidates:") printAll(results) m //│ > all interviewees: //│ > interviewee score: 20, salary: 3 //│ > candidates: //│ > interviewee score: 30, salary: 1 //│ = 80 //│ Type: Int region r in let interviewees = empty(r) push(interviewees, Itv(100, 71)) push(interviewees, Itv(1, 69)) push(interviewees, Itv(2, 1)) println("all interviewees:") printAll(interviewees) region r2 in let results = empty(r2) let m = select(interviewees, 70, results) println("candidates:") printAll(results) m //│ > all interviewees: //│ > interviewee score: 100, salary: 71 //│ > candidates: //│ > interviewee score: 2, salary: 1 //│ = 3 //│ Type: Int region r in let interviewees = empty(r) push(interviewees, Itv(40, 10)) push(interviewees, Itv(60, 20)) push(interviewees, Itv(120, 30)) push(interviewees, Itv(70, 20)) println("all interviewees:") printAll(interviewees) region r2 in let results = empty(r2) let m = select(interviewees, 60, results) println("candidates:") printAll(results) m //│ > all interviewees: //│ > interviewee score: 40, salary: 10 //│ > candidates: //│ > interviewee score: 70, salary: 20 //│ = 230 //│ Type: Int :e region r in let interviewees = empty(r) push(interviewees, Itv(20, 3)) push(interviewees, Itv(50, 1)) push(interviewees, Itv(30, 1)) let results = empty(r) let m = select(interviewees, 4, results) println("candidates:") printAll(results) m // //│ ╔══[ERROR] Type error in reference with expected type 'results // //│ ║ l.110: let m = select(interviewees, 4000, results) // //│ ║ ^^^^^^^ // //│ ╟── because: cannot constrain ArrayList['A, in ⊥ out 'R] <: 'results // //│ ╟── because: cannot constrain ArrayList['A, in ⊥ out 'R] <: ArrayList['A1, in ⊥ out 'R1] // //│ ╟── because: cannot constrain 'R <: 'R1 // //│ ╟── because: cannot constrain 'R <: ¬'R2 // //│ ╟── because: cannot constrain 'R2 <: ¬'R // //│ ╟── because: cannot constrain 'R3 <: ¬'R // //│ ╟── because: cannot constrain 'R <: ¬'R3 // //│ ╟── because: cannot constrain r <: ¬'R3 // //│ ╟── because: cannot constrain 'R3 <: ¬r // //│ ╙── because: cannot constrain r <: ¬r // //│ Type: Int //│ > candidates: //│ > interviewee score: 30, salary: 1 //│ = 80 //│ ╔══[COMPILATION ERROR] Type error in reference with expected type 'results //│ ║ l.247: let m = select(interviewees, 4, results) //│ ║ ^^^^^^^ //│ ╟── because: cannot constrain ArrayList['A, in ⊥ out 'R] <: 'results //│ ╟── because: cannot constrain ArrayList['A, in ⊥ out 'R] <: ArrayList['A1, in ⊥ out 'R1] //│ ╟── because: cannot constrain 'R <: 'R1 //│ ╟── because: cannot constrain 'R <: ¬'R2 //│ ╟── because: cannot constrain 'R2 <: ¬'R //│ ╟── because: cannot constrain 'R3 <: ¬'R //│ ╟── because: cannot constrain 'R <: ¬'R3 //│ ╟── because: cannot constrain r <: ¬'R3 //│ ╟── because: cannot constrain 'R3 <: ¬r //│ ╙── because: cannot constrain r <: ¬r //│ Type: Int fun wrongSelect(interviewees, budget, results) = region r in let size = len(interviewees), let i = r.ref 1 let dp = init(r, size + 1, budget + 1, 0) iter of interviewees, it => whileDo of () => if next(it) is Some(itv) then if itv is Itv(score, salary) then let j = r.ref 0 whileDo of () => if !(j) < salary then update(dp, !(i), !(j), get(dp, !(i) - 1, !(j))) else let p = get(dp, !(i) - 1, !(j) - salary), let np = get(dp, !(i) - 1, !(j)) update(dp, !(i), !(j), max of np, p + score) j := !(j) + 1; !(j) <= budget i := !(i) + 1 true None then false i := size let rest = r.ref budget revIter of interviewees, it => whileDo of () => if next(it) is Some(itv) then if itv is Itv(score, salary) then if get(dp, !(i), !(rest)) == get(dp, !(i) - 1, !(rest) - salary) + score then push(interviewees, itv); rest := !(rest) - salary else () i := !(i) - 1 true None then false get(dp, size, budget) :e region r in let interviewees = empty(r) push(interviewees, Itv(20, 3000)) push(interviewees, Itv(50, 1000)) push(interviewees, Itv(30, 1000)) region r2 in let results = empty(r2) let m = wrongSelect(interviewees, 4000, results) println("candidates:") printAll(results) m //│ > candidates: //│ = 80 //│ ╔══[COMPILATION ERROR] Type error in reference with expected type 'interviewees //│ ║ l.324: let m = wrongSelect(interviewees, 4000, results) //│ ║ ^^^^^^^^^^^^ //│ ╟── because: cannot constrain ArrayList['A, in ⊥ out 'R] <: 'interviewees //│ ╟── because: cannot constrain ArrayList['A, in ⊥ out 'R] <: ArrayList[in 'T out 'T1, in ⊥ out 'R1] //│ ╟── because: cannot constrain 'R <: 'R1 //│ ╟── because: cannot constrain 'R <: ¬'R //│ ╟── because: cannot constrain 'R <: ⊥ //│ ╙── because: cannot constrain r <: ⊥ //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLDisjoint.mls ================================================ :invalml data class Pair[P, Q](fst: P, snd: Q) fun fork: [A, B extends ~A, T1, T2] -> (Any ->{A} T1, Any ->{B} T2) ->{A | B} Pair[out T1, out T2] fork //│ Type: ['A, 'B, 'T1, 'T2] -> ((⊤) ->{'A} 'T1, (⊤) ->{'B} 'T2) ->{'A ∨ 'B} Pair[out 'T1, out 'T2] //│ Where: //│ 'B <: ¬'A fun foo: Any -> Int fun bar: Any -> Str fork(foo, bar) //│ Type: Pair[out Int, out Str] :e region x in fork((_ => x.ref 1), (_ => x.ref 2)) //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type (⊤) ->{'B} 'T2 //│ ║ l.24: fork((_ => x.ref 1), (_ => x.ref 2)) //│ ║ ^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg <: 'B //│ ╟── because: cannot constrain x <: 'B //│ ╟── because: cannot constrain x <: ¬'A //│ ╟── because: cannot constrain 'A <: ¬x //│ ╙── because: cannot constrain x <: ¬x //│ Type: Pair[out Ref[Int, ?], out Ref[Int, ?]] region x in region y in fork((_ => x.ref 1), (_ => y.ref 2)) //│ Type: Pair[out Ref[Int, ?], out Ref[Int, ?]] let naive_helper = r1 => // cannot infer the outer variable! region r2 in fork((_ => r1.ref 1), (_ => r2.ref 2)) naive_helper //│ Type: (Region[out 'reg]) ->{'reg} Pair[out Ref[Int, out 'reg], out Ref[Int, ?]] //│ Where: //│ 'reg <: ⊥ fun f: [S, R, R extends ~S] -> (Region[out S], Region[out R]) ->{S | R} Int :e region r1 in let g = (r => region r2 in f(r, r2)) in region r3 in g(r3) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type 'r //│ ║ l.57: region r3 in g(r3) //│ ║ ^^ //│ ╟── because: cannot constrain Region[in ⊥ out r3] <: 'r //│ ╟── because: cannot constrain Region[in ⊥ out 'r31] <: 'r //│ ╟── because: cannot constrain Region[in ⊥ out 'r31] <: Region[in ⊥ out 'S] //│ ╟── because: cannot constrain 'r31 <: 'S //│ ╟── because: cannot constrain ¬r1 <: 'S //│ ╟── because: cannot constrain ¬r1 <: ¬'r2 ∨ r1 //│ ╟── because: cannot constrain 'r2 <: r1 //│ ╙── because: cannot constrain ¬r1 <: r1 //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.56: let g = (r => region r2 in f(r, r2)) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.57: region r3 in g(r3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'eff <: ⊥ //│ ╟── because: cannot constrain ¬'r11 <: ⊥ //│ ╟── because: cannot constrain ⊤ <: 'r11 //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int fun g(r) = region r2 in f(r, r2) region r1 in region r3 in g(r3) //│ Type: Int :e region x in naive_helper(x) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type 'r1 //│ ║ l.90: naive_helper(x) //│ ║ ^ //│ ╟── because: cannot constrain Region[in ⊥ out x] <: 'r1 //│ ╟── because: cannot constrain Region[in ⊥ out 'x1] <: 'r1 //│ ╟── because: cannot constrain Region[in ⊥ out 'x1] <: Region[in ⊥ out 'reg] //│ ╟── because: cannot constrain 'x1 <: 'reg //│ ╟── because: cannot constrain ⊤ <: 'reg //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.90: naive_helper(x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'eff <: ⊥ //│ ╟── because: cannot constrain ¬'x2 ∧ 'eff1 <: ⊥ //│ ╟── because: cannot constrain 'eff1 <: 'x2 //│ ╟── because: cannot constrain 'eff1 <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Pair[out Ref[Int, ?], out Ref[Int, ?]] fun helper(r1) = region r2 in fork((_ => r1.ref 1), (_ => r2.ref 2)) helper //│ Type: [outer, 'reg] -> Region[out 'reg] ->{'reg} Pair[out Ref[Int, out 'reg], out Ref[Int, out ¬outer]] //│ Where: //│ 'reg <: outer region x in helper(x) //│ Type: Pair[out Ref[Int, ?], out Ref[Int, ?]] region x in (region y in let t = helper(y) in 42) as [A] -> Int //│ Type: Int region x in region y in let t = helper(x) in 42 //│ Type: Int :e region x in (region y in let t = helper(x) in 42) as [A] -> Int //│ ╔══[COMPILATION ERROR] Type error in region expression with expected type [outer, 'A] -> Int //│ ║ l.139: (region y in let t = helper(x) in 42) as [A] -> Int //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'eff <: ⊥ //│ ╟── because: cannot constrain ¬'y ∧ x <: ⊥ //│ ╟── because: cannot constrain x <: 'y //│ ╙── because: cannot constrain x <: ⊥ //│ Type: Int fun anno: [outer A] -> Int ->{A} Int fun anno2: [outer] -> Int ->{outer} Int :e fun badanno: outer //│ ╔══[COMPILATION ERROR] Illegal outer reference. //│ ║ l.157: fun badanno: outer //│ ╙── ^^^^^ //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› //│ ═══[COMPILATION ERROR] Invalid type :e fun badanno2: [outer A, outer B] -> Int ->{A | B} Int //│ ╔══[COMPILATION ERROR] Only one outer variable can be bound. //│ ║ l.165: fun badanno2: [outer A, outer B] -> Int ->{A | B} Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Illegal forall annotation. //│ ║ l.165: fun badanno2: [outer A, outer B] -> Int ->{A | B} Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› //│ ═══[COMPILATION ERROR] Invalid type fun annohelper: [outer, T extends outer] -> Region[out T] ->{T} Pair[out Ref[Int, out T], out Ref[Int, out ~outer]] fun annohelper(r1) = region r2 in fork((_ => r1.ref 1), (_ => r2.ref 2)) annohelper //│ Type: [outer, 'T] -> (Region[out 'T]) ->{'T} Pair[out Ref[Int, out 'T], out Ref[Int, out ¬outer]] //│ Where: //│ 'T <: outer region x in annohelper(x) //│ Type: Pair[out Ref[Int, ?], out Ref[Int, ?]] fun annohelper: [outer, T] -> Region[out T & outer] ->{T & outer} Pair[out Ref[Int, out T & outer], out Ref[Int, out ~outer]] fun annohelper(r1) = region r2 in fork((_ => r1.ref 1), (_ => r2.ref 2)) annohelper //│ Type: [outer, 'T] -> (Region[out 'T ∧ outer]) ->{'T ∧ outer} Pair[out Ref[Int, out 'T ∧ outer], out Ref[Int, out ¬outer]] region x in annohelper(x) //│ Type: Pair[out Ref[Int, out 'env], out Ref[Int, ?]] //│ Where: //│ ⊤ <: 'env // Cannot type check since foo: 'foo <: Region[T] ->{'eff} 'app // Annotation is required for recursive calls :e fun foo(r1) = region r2 in fork((_ => r1.ref 1), (_ => r2.ref 2)) foo(r2) //│ ╔══[COMPILATION ERROR] Type error in function literal //│ ║ l.209: fun foo(r1) = //│ ║ ^^^^^^ //│ ║ l.210: region r2 in //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.211: fork((_ => r1.ref 1), (_ => r2.ref 2)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.212: foo(r2) //│ ║ ^^^^^^^^^^^ //│ ╟── because: cannot constrain ('r1) ->{'eff} ('app) <: 'foo //│ ╟── because: cannot constrain ('r1) ->{'eff} ('app) <: (Region[in ⊥ out 'r2]) ->{'eff1} ('app1) //│ ╟── because: cannot constrain Region[in ⊥ out 'r2] <: 'r1 //│ ╟── because: cannot constrain Region[in ⊥ out 'r2] <: Region[in ⊥ out 'reg] //│ ╟── because: cannot constrain 'r2 <: 'reg //│ ╟── because: cannot constrain ¬outer <: 'reg //│ ╟── because: cannot constrain ¬outer <: ¬'r21 ∨ outer //│ ╟── because: cannot constrain 'r21 <: outer //│ ╙── because: cannot constrain ¬outer <: outer fun foo: [outer S, T extends S] -> Region[out T] ->{T} Nothing fun foo(r1) = region r2 in foo(r2) foo //│ Type: [outer S, 'T] -> (Region[out 'T]) ->{'T} ⊥ //│ Where: //│ 'T <: S fun foo: [outer To, T extends To] -> Region[out T] ->{T} ([outer So, S extends So] -> Region[S]->{S} Pair[out Ref[Int, out S], out Ref[Int, out Any]]) fun foo(r1) = r3 => region r4 in fork((_ => r3.ref 3), (_ => r4.ref 4)) fun bar: [outer S, T extends S] -> Region[out T] ->{T} Int bar as [outer Q, P extends Q] -> Region[P] ->{P} Int //│ Type: [outer Q, 'P] -> (Region['P]) ->{'P} Int //│ Where: //│ 'P <: Q fun foo: [outer To, T extends To] -> Region[out T] ->{T} ([outer So, S extends So] -> Region[out S]->{S} Int) fun foo(r1) = bar foo //│ Type: [outer To, 'T] -> (Region[out 'T]) ->{'T} [outer So, 'S] -> (Region[out 'S]) ->{'S} Int //│ Where: //│ 'T <: To //│ 'S <: So fun borrow: [S, T, E extends ~S] -> Region[out S] -> ([P] -> Region[out P] ->{E | P} T) ->{E} T fun foo(f) = region r in let x = r.ref 0 f(n => x := n) borrow(r) of it => foo(f) foo //│ Type: [outer, 'n, 'eff] -> (('n ->{¬outer} ('n ∨ Int)) ->{'eff} ⊤) ->{'eff} ⊥ //│ Where: //│ 'n <: Int //│ 'eff <: outer ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLEffectAnnots.mls ================================================ :invalml fun foo: Int ->{Int} Int fun foo: Int ->{Int} Int fun foo: Int -> Int ->{Int} Int fun foo: Int ->{Int} Int -> Int fun foo: Int ->{Int} Int ->{Int} Int fun foo: [A] -> Int ->{A} Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLErrors.mls ================================================ :invalml :e 2(2) //│ ╔══[COMPILATION ERROR] Type error in application //│ ║ l.5: 2(2) //│ ║ ^^^^ //│ ╙── because: cannot constrain Int <: (Int) ->{'eff} ('app) //│ Type: ⊥ (x => x(0))(1) //│ ╔══[COMPILATION ERROR] Type error in integer literal with expected type 'x //│ ║ l.12: (x => x(0))(1) //│ ║ ^ //│ ╟── because: cannot constrain Int <: 'x //│ ╙── because: cannot constrain Int <: (Int) ->{'eff} ('app) //│ Type: ⊥ (1).Foo#a() //│ ╔══[COMPILATION ERROR] Name not found: Foo //│ ║ l.20: (1).Foo#a() //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Identifier `Foo` does not name a known class symbol. //│ ║ l.20: (1).Foo#a() //│ ╙── ^^^^ //│ ═══[COMPILATION ERROR] Expected a statically known class; found ‹error›. //│ ═══[COMPILATION ERROR] Not a valid class: ‹error› //│ Type: ⊥ fun Oops() = 1 Oops().Oops#a() //│ ╔══[COMPILATION ERROR] Identifier `Oops` does not name a known class symbol. //│ ║ l.33: Oops().Oops#a() //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Not a valid class: reference //│ ║ l.33: Oops().Oops#a() //│ ╙── ^^^^^ //│ Type: ⊥ data class Oops2() (new Oops2()).Oops2#a() //│ ╔══[COMPILATION ERROR] Class 'Oops2' does not contain member 'a'. //│ ║ l.43: (new Oops2()).Oops2#a() //│ ╙── ^ //│ ╔══[COMPILATION ERROR] a is not a valid member in class Oops2 //│ ║ l.43: (new Oops2()).Oops2#a() //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ Type: ⊥ fun inc(x) = x + 1 inc("oops") //│ ╔══[COMPILATION ERROR] Type error in string literal with expected type 'x //│ ║ l.55: inc("oops") //│ ║ ^^^^^^ //│ ╟── because: cannot constrain Str <: 'x //│ ╙── because: cannot constrain Str <: Int //│ Type: Int fun inc(x) = x + 1 inc as Int //│ ╔══[COMPILATION ERROR] Type error in reference with expected type Int //│ ║ l.64: inc as Int //│ ║ ^^^ //│ ╙── because: cannot constrain ('x) ->{⊥} (Int) <: Int //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLExtrude.mls ================================================ :invalml fun f(y) = let local = ((x => y(x) + 1) as [A] -> A -> Int) in y // * the parameter type of y is extruded. f //│ Type: ['y] -> 'y -> 'y //│ Where: //│ 'y <: ⊤ -> Int fun foo: [A] -> A -> Int fun foo(x) = 0 foo //│ Type: (⊤) ->{⊥} Int f(foo) //│ Type: ⊤ -> Int fun g: ([A] -> A -> Int) -> ([A] -> A -> Int) fun g(y) = let local = ((x => y(x) + 1) as ([A] -> A -> Int)) in y g //│ Type: ((⊤) ->{⊥} Int) ->{⊥} (⊤) ->{⊥} Int g(foo) //│ Type: (⊤) ->{⊥} Int :e y `=> (let t = run(x `=> x `+ y) in y) //│ ╔══[COMPILATION ERROR] Type error in quoted term with expected type CodeBase[out 'T, ⊥, ?] //│ ║ l.32: y `=> (let t = run(x `=> x `+ y) in y) //│ ║ ^^^^^^^^^^^^ //│ ╟── because: cannot constrain CodeBase[in ⊥ out ('x) ->{⊥} ('cde), in ⊥ out 'ctx, in ⊥ out ⊤] <: CodeBase[in ⊥ out 'T, ⊥, in ⊥ out ⊤] //│ ╟── because: cannot constrain 'ctx <: ⊥ //│ ╟── because: cannot constrain ¬'x1 ∧ y <: ⊥ //│ ╟── because: cannot constrain y <: 'x1 //│ ╙── because: cannot constrain y <: ⊥ //│ Type: CodeBase[out 'y -> 'y, ⊥, ?] //│ Where: //│ 'y <: Int data class C[A](m: A, n: A -> Int) fun f: [A] -> ([B] -> (C[out B] & A) -> B) -> A -> Int f //│ Type: ['A] -> (['B] -> (C[out 'B] ∧ 'A) ->{⊥} 'B) ->{⊥} ('A) ->{⊥} Int fun g: [D] -> C[in Int out D] -> D g //│ Type: ['D] -> (C[in Int out 'D]) ->{⊥} 'D f(g) //│ Type: (¬C[?] ∨ C[in Int out ⊥]) ->{⊥} Int fun foo: C[in Int out Nothing] foo //│ Type: C[in Int out ⊥] fun bar: C[in Int out Int] bar //│ Type: C[Int] f(g)(foo) //│ Type: Int :fixme // ??? f(g)(bar) //│ ╔══[COMPILATION ERROR] Type error in reference of type C[‹not implemented›] with expected type 'A //│ ║ l.72: f(g)(bar) //│ ║ ^^^ //│ ╟── because: cannot constrain C[Int] <: 'A //│ ╟── because: cannot constrain C[Int] <: ¬{C[in ⊥ out ¬⊥ ∧ 'B]} ∨ C[in Int out 'D] //│ ╟── because: cannot constrain Int ∧ 'B <: 'D //│ ╟── because: cannot constrain Int ∧ 'B <: 'B1 //│ ╟── because: cannot constrain Int ∧ 'B <: ⊥ //│ ╟── because: cannot constrain 'B <: ¬{Int} //│ ╙── because: cannot constrain ⊤ <: ¬{Int} //│ Type: Int fun k(x) = fun f(y) = x(y) f(0) + 0 k //│ Type: ['eff] -> (Int ->{'eff} Int) ->{'eff} Int :e k(_ => "") //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type 'x //│ ║ l.92: k(_ => "") //│ ║ ^^^^^^^ //│ ╟── because: cannot constrain ('_) ->{⊥} (Str) <: 'x //│ ╟── because: cannot constrain ('_) ->{⊥} (Str) <: (Int) ->{'eff} (Int) //│ ╙── because: cannot constrain Str <: Int //│ Type: Int k(_ => 42) //│ Type: Int fun k(x) = fun f: [S, T] -> S -> T fun f(y) = x(y) f(0) + 0 k //│ Type: (⊤ -> ⊥) -> Int :e k(_ => "") //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type 'x //│ ║ l.114: k(_ => "") //│ ║ ^^^^^^^ //│ ╟── because: cannot constrain ('_) ->{⊥} (Str) <: 'x //│ ╟── because: cannot constrain ('_) ->{⊥} (Str) <: (⊤) ->{⊥} ⊥ //│ ╙── because: cannot constrain Str <: ⊥ //│ Type: Int :e k(_ => 42) //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type 'x //│ ║ l.124: k(_ => 42) //│ ║ ^^^^^^^ //│ ╟── because: cannot constrain ('_) ->{⊥} (Int) <: 'x //│ ╟── because: cannot constrain ('_) ->{⊥} (Int) <: (⊤) ->{⊥} ⊥ //│ ╙── because: cannot constrain Int <: ⊥ //│ Type: Int fun k(x) = let f = y => x(y) f(0) + 0 k //│ Type: ['eff] -> (Int ->{'eff} Int) ->{'eff} Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLFunGenFix.mls ================================================ :global :invalml class Reg[out Rg, out AccI] class BaseEff fun basereg: Reg[Any, BaseEff] fun subreg: [E, Rg, AccI, Res] -> Reg[Rg, AccI] -> ([Rg2] -> Reg[Rg2 & AccI, AccI & ~Rg2] ->{E | Rg2} Res) ->{E} Res fun write: [Rg, AccI] -> Reg[Rg, AccI] ->{Rg} Int subreg(basereg) of r0 => fun helper(a) = write(a) helper(r0) //│ Type: Int fun helper(a) = write(a) helper //│ Type: ['Rg] -> Reg[out 'Rg, ?] ->{'Rg} Int subreg(basereg) of r0 => helper(r0) //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLFuns.mls ================================================ :invalml fun f(x, y) = x(y) f //│ Type: ['y, 'eff, 'app] -> ('y ->{'eff} 'app, 'y) ->{'eff} 'app f((x => x), 42) //│ Type: Int f(x => x, 42) //│ Type: Int fun id: [A] -> A -> A id as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A id(id) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A data class Nil() data class Cons[A, B](val car: A, val cdr: B) data class Ls[A](prim: [R] -> (() -> R, (A, Ls[A]) -> R) -> R) fun nil() = new Ls((n, _) => n()) fun cons(p, q) = new Ls((n, r) => r(p, q)) fun from(x) = x.Ls#prim(() => new Nil, (x, y) => new Cons(x, from(y))) fun to(x) = if x is Nil then nil() Cons then cons(x.Cons#car, to(x.Cons#cdr)) let foo = nil() from() foo //│ Type: Cons['A, 'B] ∨ Nil //│ Where: //│ ⊥ <: 'A //│ Cons['A, 'B] <: 'R //│ Nil <: 'R //│ 'R <: 'B foo to() //│ Type: Ls['A] ∨ Ls['A1] //│ Where: //│ ⊥ <: 'A //│ 'A <: 'A1 //│ ⊥ <: 'A1 //│ 'A1 <: 'A fun (;) seq(_, res) = res fun foo: [R1, R2] -> (Region[R1], Region[R2], Int ->{R1 & ~R2} (), Int ->{R2 & ~R1} ()) ->{R1 | R2} () fun foo(r1, r2, f, g) = f(123); g(456) foo //│ Type: ['R1, 'R2] -> (Region['R1], Region['R2], (Int) ->{'R1 ∧ ¬'R2} (), (Int) ->{'R2 ∧ ¬'R1} ()) ->{'R1 ∨ 'R2} () :e fun foo[outer, R <: outer](r1: Region[R]) = region r2 in 42 //│ ╔══[COMPILATION ERROR] Unsupported type parameter outer binding //│ ║ l.67: fun foo[outer, R <: outer](r1: Region[R]) = //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Unsupported type parameter operator application //│ ║ l.67: fun foo[outer, R <: outer](r1: Region[R]) = //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: R //│ ║ l.67: fun foo[outer, R <: outer](r1: Region[R]) = //│ ╙── ^ //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Invalid type ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLGPCE.mls ================================================ :global :invalml fun power: [C] -> CodeBase[out Num, out C, out Any] -> Int -> CodeBase[out Num, out C, out Any] fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) power //│ Type: ['C] -> (CodeBase[out Num, out 'C, ?]) ->{⊥} (Int) ->{⊥} CodeBase[out Num, out 'C, ?] fun id: [A] -> A -> A fun id(x) = x run(x `=> id(x) `* x) //│ Type: Int -> Int fun assertNotZero: [C] -> CodeBase[out Num, out C, out Any] -> CodeBase[out Num, out C, out Any] fun assertNotZero(x) = `if (x `== `0.0) then `error else x let checkedDiv = x `=> y `=> x `/. (assertNotZero(y)) run(checkedDiv) //│ Type: Num -> (Num -> Num) fun show: [T] -> CodeBase[out T, out Any, out Any] -> Str = s => "debug" show //│ Type: (CodeBase[?, ?, ?]) ->{⊥} Str fun inc(dbg) = x `=> let c = x `+ `1 in let t = dbg(c) in c inc //│ Type: ['eff] -> (CodeBase[out Int, ?, ?] ->{'eff} ⊤) ->{'eff} CodeBase[out Int -> Int, ⊥, ?] inc(c => log(show(c))) //│ Type: CodeBase[out Int -> Int, ⊥, ?] fun body: [T, C] -> (CodeBase[out Int, out T, out Any], CodeBase[out Int, out C, out Any]) -> Int -> CodeBase[out Int, out T | C, out Any] fun body(x, y) = case 0 then x 1 then y n then body(y, x `+ y)(n - 1) fun gib_naive(n) = (x, y) `=> body(x, y)(n) let gn5 = run(gib_naive(5)) fun bind(rhs, k) = `let x = rhs `in k(x) bind //│ Type: ['cde, 'ctx, 'eff, 'cde1, 'ctx1] -> (CodeBase[out 'cde, out 'ctx, ?], CodeBase['cde, ?, ⊥] ->{'eff} CodeBase[out 'cde1, out 'ctx1, ?]) ->{'eff} CodeBase[out 'cde1, out 'ctx ∨ 'ctx1, ?] :e fun body: [G] -> (CodeBase[out Int, out G, out Any], CodeBase[out Int, out G, out Any]) -> Int -> CodeBase[out Int, out G, out Any] fun body(x, y) = case 0 then x 1 then y n then bind of x `+ y, (z => body(y, z)(n - 1)) //│ ╔══[COMPILATION ERROR] Type error in application with expected type CodeBase[out Int, out G, ?] //│ ║ l.64: n then bind of x `+ y, (z => body(y, z)(n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain CodeBase[in ⊥ out 'cde, in ⊥ out 'ctx ∨ 'ctx1, in ⊥ out ⊤] <: CodeBase[in ⊥ out Int, in ⊥ out G, in ⊥ out ⊤] //│ ╟── because: cannot constrain 'ctx ∨ 'ctx1 <: G //│ ╟── because: cannot constrain 'ctx1 <: G //│ ╟── because: cannot constrain 'ctx2 <: G //│ ╙── because: cannot constrain ⊤ <: G fun bind: [G] -> (CodeBase[out Int, out G, out Any], [C] -> CodeBase[out Int, out C, out Any] -> CodeBase[out Int, out C | G, out Any]) -> CodeBase[out Int, out G, out Any] fun bind(rhs, k) = `let x = rhs `in k(x) bind //│ Type: ['G] -> (CodeBase[out Int, out 'G, ?], ['C] -> (CodeBase[out Int, out 'C, ?]) ->{⊥} CodeBase[out Int, out 'C ∨ 'G, ?]) ->{⊥} CodeBase[out Int, out 'G, ?] fun body: [G] -> (CodeBase[out Int, out G, out Any], CodeBase[out Int, out G, out Any]) -> Int -> CodeBase[out Int, out G, out Any] fun body(x, y) = case 0 then x 1 then y n then bind of x `+ y, (z => body(y, z)(n - 1)) body //│ Type: ['G] -> (CodeBase[out Int, out 'G, ?], CodeBase[out Int, out 'G, ?]) ->{⊥} (Int) ->{⊥} CodeBase[out Int, out 'G, ?] fun gib(n) = (x, y) `=> body(x, y)(n) let g5 = run(gib(5)) g5 //│ Type: (Int, Int) -> Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLGetters.mls ================================================ :js :noSanityCheck // * For some reason, these cause problems :global :invalml :todo fun test1() = fun sayHi = print("Hi") sayHi, sayHi, sayHi //│ ╔══[COMPILATION ERROR] Function definition shape not yet supported for sayHi //│ ║ l.10: fun sayHi = print("Hi") //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Variable not found: sayHi //│ ║ l.11: sayHi, sayHi, sayHi //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Variable not found: sayHi //│ ║ l.11: sayHi, sayHi, sayHi //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Variable not found: sayHi //│ ║ l.11: sayHi, sayHi, sayHi //│ ╙── ^^^^^ test1() //│ > Hi //│ > Hi //│ > Hi //│ Type: ⊥ :todo // it feels like this should work fun test2() = fun funny = case 0 then 0 n then funny(n - 1) + 1 funny //│ ╔══[COMPILATION ERROR] Expected a monomorphic type or an instantiable type here, but () ->{⊥} [outer, 'caseScrut, 'eff] -> 'caseScrut ->{'eff} Int found //│ ║ l.33: fun test2() = //│ ║ ^^^^ //│ ║ l.34: fun funny = case //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.35: 0 then 0 //│ ║ ^^^^^^^^^^^^ //│ ║ l.36: n then funny(n - 1) + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.37: ... (more lines omitted) ... //│ ╙── ^^^^^^^ fun test2() = fun funny = case 0 then 0 n then funny(n - 1) + 1 funny(_) test2 //│ = fun test2 //│ Type: () -> (Int -> Int) :sjs test2() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ test21() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun //│ Type: Int -> Int :ssjs test2()(5) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let baseCall; //│ baseCall = test21(); block$res8 = runtime.safeCall(baseCall(5)); undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 5 //│ Type: Int // * Notice the mistake here; we warn that the first case in statement position is useless! :w :e :sjs fun test2() = fun funny = case 0 then 0 case n then funny(n - 1) + 1 funny //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.84: case 0 then 0 //│ ╙── ^^^^^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test22; //│ test22 = function test2() { //│ let funny; //│ funny = function funny() { //│ let lambda1; //│ lambda1 = (undefined, function (caseScrut) { //│ let n, tmp, tmp1, tmp2; //│ n = caseScrut; //│ tmp = funny(); //│ tmp1 = n - 1; //│ tmp2 = runtime.safeCall(tmp(tmp1)); //│ return tmp2 + 1 //│ }); //│ return lambda1 //│ }; //│ return funny() //│ }; //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.84: case 0 then 0 //│ ╙── ^^^^^^^^^^^^^ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ╔══[COMPILATION ERROR] Function definition shape not yet supported for funny //│ ║ l.84: case 0 then 0 //│ ║ ^^^^^^^^ //│ ║ l.85: case n then funny(n - 1) + 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Variable not found: funny //│ ║ l.86: funny //│ ╙── ^^^^^ :todo fun test3 = print("Hi") //│ ╔══[COMPILATION ERROR] Function definition shape not yet supported for test3 //│ ║ l.124: print("Hi") //│ ╙── ^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLId.mls ================================================ :invalml fun id(x) = x id(1) //│ Type: Int id //│ Type: ['x] -> 'x -> 'x id(true) //│ Type: Bool id //│ Type: ['x] -> 'x -> 'x fun id(x) = x id as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A (y => y) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A fun id(x) = x (y => id(y)) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLLetRegEncoding.mls ================================================ :invalml fun letreg: [E,Res] -> ([R] -> Region[out R] ->{E | R} Res) ->{E} Res letreg //│ Type: ['E, 'Res] -> (['R] -> (Region[out 'R]) ->{'E ∨ 'R} 'Res) ->{'E} 'Res letreg(r => r) //│ Type: Region[?] :e letreg(r => r).ref 1 //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.14: letreg(r => r).ref 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'E <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'R <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Ref[Int, ?] letreg(r => r.ref 1) //│ Type: Ref[Int, ?] letreg(r => !(r.ref 1)) //│ Type: Int :e !(letreg(r => r.ref 1)) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.31: !(letreg(r => r.ref 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'E <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'reg1 <: ⊥ //│ ╟── because: cannot constrain 'R <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int letreg of r => let x = r.ref 1 x := !(x) + 1 !(x) //│ Type: Int let f = letreg(r => arg => r.ref arg) f //│ Type: 'arg ->{⊤} Ref['arg, ?] :e letreg(r => arg => r.ref arg)(0) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.54: letreg(r => arg => r.ref arg)(0) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'eff ∨ 'E <: ⊥ //│ ╟── because: cannot constrain 'eff <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'R <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Ref['arg, ?] //│ Where: //│ Int <: 'arg // * An incorrect one, just for testing the error: fun letreg: [E,Res] -> ([R] -> Region[out R] -> Res) ->{E} Res :e letreg(r => r.ref 1) //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type (Region[out R]) ->{⊥} 'Res //│ ║ l.74: letreg(r => r.ref 1) //│ ║ ^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╙── because: cannot constrain R <: ⊥ //│ Type: Ref[Int, ?] :e letreg(r => !(r.ref 1)) //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type (Region[out R]) ->{⊥} 'Res //│ ║ l.83: letreg(r => !(r.ref 1)) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'reg1 <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'reg1 <: ⊥ //│ ╙── because: cannot constrain R <: ⊥ //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLList.mls ================================================ :invalml data class List[A](inspect: [E, Res] -> (() ->{E} Res, (A, List[A]) ->{E} Res) ->{E} Res) fun map: [A, B, E] -> List[out A] -> (A ->{E} B) ->{E} List[out B] // * Dummy implementation fun mapi: [A, E] -> List[out A] -> ((Int, A) ->{E} A) ->{E} List[out A] fun mapi = s => f => region r in map(s) of x => f(0, x) fun mapi: [A, E] -> List[out A] -> ((Int, A) ->{E} A) ->{E} List[out A] fun mapi = s => f => region r in let i = r.ref 0 map(s) of x => i := !(i) + 1 f(!(i), x) // * Example usage fun mkList: [A] -> A -> List[out A] fun head: [A] -> List[out A] -> A region r in let sum = r.ref 0 let s1 = mkList of !(sum) let s2 = mapi(s1) of (x, i) => x * i !(sum) + head(s2) //│ Type: Int // * Should be an error. This definition would not be referentially transparent. // * The error message needs improvement, though. :e fun mapi: [A, E] -> List[out A] -> ((Int, A) ->{E} A) ->{E} List[out A] fun mapi = s => region r in let i = r.ref 0 f => map(s) of x => i := !(i) + 1 f(!(i), x) //│ ╔══[COMPILATION ERROR] Type error in region expression with expected type ((Int, A) ->{E} A) ->{E} List[out A] //│ ║ l.43: let i = r.ref 0 //│ ║ ^^^^^^^ //│ ║ l.44: f => map(s) of x => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.45: i := !(i) + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: f(!(i), x) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain ('f) ->{'E1} (List[in ⊥ out 'B]) <: ((Int, A) ->{E} (A)) ->{E} (List[in ⊥ out A]) //│ ╟── because: cannot constrain 'E1 <: E //│ ╟── because: cannot constrain 'reg <: E //│ ╟── because: cannot constrain 'r ∧ ¬outer <: E //│ ╟── because: cannot constrain 'r <: E ∨ outer //│ ╙── because: cannot constrain ¬outer <: E ∨ outer ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLOption.mls ================================================ :invalml data class Option[out A](inspect: [E, Res] -> (() ->{E} Res, A ->{E} Res) ->{E} Res) opt => opt.Option#inspect //│ Type: (Option[out 'A]) ->{⊥} ['E, 'Res] -> (() ->{'E} 'Res, ('A) ->{'E} 'Res) ->{'E} 'Res opt => opt.Option#inspect( () => 42 n => n + 1 ) //│ Type: (Option[out Int]) ->{⊥} Int opt => opt.Option#inspect of () => 42, n => n + 1 //│ Type: (Option[out Int]) ->{⊥} Int opt => opt.Option#inspect of () => 42 n => n + 1 //│ Type: (Option[out Int]) ->{⊥} Int opt => opt as Option[out Int] //│ Type: (Option[out Int]) ->{⊥} Option[out Int] opt => (opt as Option[Int]) as Option[Int | Str] //│ Type: (Option[out Int ∨ Str] ∧ Option[out Int]) ->{⊥} Option[out Int ∨ Str] ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLPoly.mls ================================================ :invalml 42 as [A] -> Int //│ Type: Int :e 42 as [A] -> Str //│ ╔══[COMPILATION ERROR] Type error in integer literal with expected type Str //│ ║ l.8: 42 as [A] -> Str //│ ║ ^^ //│ ╙── because: cannot constrain Int <: Str //│ Type: Str fun id: [A] -> A -> A fun id(x) = x id //│ Type: ['A] -> ('A) ->{⊥} 'A :e (x => x + 1) as [A] -> A -> A //│ ╔══[COMPILATION ERROR] Type error in reference with expected type Int //│ ║ l.22: (x => x + 1) as [A] -> A -> A //│ ║ ^ //│ ╙── because: cannot constrain A <: Int //│ ╔══[COMPILATION ERROR] Type error in application with expected type A //│ ║ l.22: (x => x + 1) as [A] -> A -> A //│ ║ ^^^^^ //│ ╙── because: cannot constrain Int <: A //│ Type: ['A] -> ('A) ->{⊥} 'A (x => x) as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A id as [A] -> A -> A //│ Type: ['A] -> ('A) ->{⊥} 'A id as Int -> Int //│ Type: (Int) ->{⊥} Int fun myInc(inc: Int -> Int, x: Int) = inc(x) myInc(id, 0) //│ Type: Int let t = 42 in ((x => x) as [A] -> A -> A) //│ Type: ['A] -> ('A) ->{⊥} 'A id(42) //│ Type: Int id("abc") //│ Type: Str data class Pair[A, B](a: A, b: B) new Pair(42, true) //│ Type: Pair['A, 'B] //│ Where: //│ Int <: 'A //│ Bool <: 'B fun swap: [A, B] -> Pair[out A, out B] -> Pair[out B, out A] fun swap(p) = new Pair(p.Pair#b, p.Pair#a) swap //│ Type: ['A, 'B] -> (Pair[out 'A, out 'B]) ->{⊥} Pair[out 'B, out 'A] let t = new Pair(42, true) in swap(t) //│ Type: Pair[out Bool, out Int] let t = new Pair("114", "514") in swap(t) //│ Type: Pair[out Str, out Str] let id = ((x => x) as [A] -> A -> A) in new Pair(id(42), id("42")) //│ Type: Pair['A, 'B] //│ Where: //│ Int <: 'A //│ Str <: 'B fun foo: ([A] -> A -> A) -> Int fun foo(x) = 42 foo //│ Type: (['A] -> ('A) ->{⊥} 'A) ->{⊥} Int foo(id) //│ Type: Int foo(x => x) //│ Type: Int data class Foo(foo: [A] -> A -> A) new Foo(id) //│ Type: Foo new Foo(x => x) //│ Type: Foo let foo = new Foo(id) in foo.Foo#foo(42) //│ Type: Int data class Bar[A](x: A, f: [B] -> B -> B) new Bar(0, id) //│ Type: Bar['A] //│ Where: //│ Int <: 'A let bar = new Bar(0, id) in bar.Bar#f(bar.Bar#x) //│ Type: Int data class Some[A](value: A) new Some((x => x) as [A] -> A -> A) //│ Type: Some['A] //│ Where: //│ 'x -> 'x <: 'A let s = new Some((x => x) as [A] -> A -> A) in let t = s.Some#value(42) in s.Some#value(false) //│ Type: Bool ∨ Int fun gen: Int -> [A] -> A -> A fun gen(x) = let t = x + 1 in ((y => y) as [A] -> A -> A) gen //│ Type: (Int) ->{⊥} ['A] -> ('A) ->{⊥} 'A gen(42) //│ Type: ['A] -> ('A) ->{⊥} 'A :e fun cnt: Some[out [A] -> A -> A] -> Int fun cnt(x) = 42 //│ ╔══[COMPILATION ERROR] General type is not allowed here. //│ ║ l.144: fun cnt: Some[out [A] -> A -> A] -> Int //│ ╙── ^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLPrelude.mls ================================================ :ignore // ^ Ignore the silly warnings below, which are due to `untyped` not being treated as THE `untyped` builtin declare class Any declare class Nothing declare class Object declare class untyped declare class tailrec declare class tailcall declare class Unit declare class Bool declare class Int declare class Num declare class TypedArray declare class BigInt declare class Int31 fun String: Any -> Str data class CodeBase[T, C, S] Region[T] Ref[T, S](reg: Region[T], value: S) @untyped set globalThis.CodeBase = CodeBase globalThis.Region = Region globalThis.Ref = (r, v) => new mut Ref(r, v) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.32: @untyped //│ ║ ^^^^^^^^ //│ ╟── This annotation is not supported on assignment terms. //│ ║ l.34: globalThis.CodeBase = CodeBase //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.32: @untyped //│ ║ ^^^^^^^^ //│ ╟── This annotation is not supported on assignment terms. //│ ║ l.35: globalThis.Region = Region //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.32: @untyped //│ ║ ^^^^^^^^ //│ ╟── This annotation is not supported on assignment terms. //│ ║ l.36: globalThis.Ref = (r, v) => new mut Ref(r, v) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ declare class Function declare class Str(length: Int, concat: Str -> Str) declare class Error(msg: Str) declare module source with object line: Int name: Str file: Str declare class Array[T](val length: Int): Array[T] declare object Symbol with // The `TermDef` needs `rhs` to be defined to be recognized as `isMLsFun`. // Otherwise, it would be wrapped in `runtime.safeCall`, which accesses the // uninitialized `runtime` in the `Rendering` module. fun for: Str -> Any = () val iterator: Any declare module js with fun bitand bitnot bitor shl try_catch declare module wasm with fun plus_impl minus_impl times_impl div_impl mod_impl eq_impl neq_impl lt_impl le_impl gt_impl ge_impl neg_impl pos_impl not_impl declare module debug with fun printStack declare module annotations with object compile object buffered object bufferable declare module scope with fun locally declare module runtime with fun suspend handle_suspension declare fun run: [T] -> CodeBase[out T, out Nothing, out Any] -> T declare fun log: Str -> Any declare fun error: Nothing declare fun (+): (Int, Int) -> Int declare fun (-): (Int, Int) -> Int declare fun (*): (Int, Int) -> Int declare fun (/): (Int, Int) -> Num declare fun (+.): (Num, Num) -> Num declare fun (-.): (Num, Num) -> Num declare fun (*.): (Num, Num) -> Num declare fun (/.): (Num, Num) -> Num declare fun (<): (Int, Int) -> Bool declare fun (>): (Int, Int) -> Bool declare fun (<=): (Int, Int) -> Bool declare fun (>=): (Int, Int) -> Bool declare fun (==): [T] -> (T, T) -> Bool declare fun (!=): [T] -> (T, T) -> Bool declare fun (===): [T] -> (T, T) -> Bool declare fun (!==): [T] -> (T, T) -> Bool declare fun (&&): (Bool, Bool) -> Bool declare fun (||): (Bool, Bool) -> Bool fun print(x) = @untyped globalThis.console.log(x) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.146: fun print(x) = @untyped globalThis.console.log(x) //│ ║ ^^^^^^^^ //│ ╟── This annotation is not supported on application terms. //│ ║ l.146: fun print(x) = @untyped globalThis.console.log(x) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLQQ.mls ================================================ :invalml `42 //│ Type: CodeBase[out Int, ⊥, ?] :p `false //│ ┊`┊false┊ //│ Parsed: //│ Quoted(BoolLit(false)) //│ Type: CodeBase[out Bool, ⊥, ?] `"rua" //│ Type: CodeBase[out Str, ⊥, ?] `3.14 //│ Type: CodeBase[out Num, ⊥, ?] x `=> x //│ Type: CodeBase[out 'x -> 'x, ⊥, ?] x `=> `42 //│ Type: CodeBase[out ⊤ -> Int, ⊥, ?] :e x `=> 42 //│ ╔══[COMPILATION ERROR] Type error in unquoted term //│ ║ l.28: x `=> 42 //│ ║ ^^ //│ ╙── because: cannot constrain Int <: CodeBase[in ⊥ out 'cde, in ⊥ out 'ctx, in ⊥ out ⊤] //│ Type: CodeBase[out ⊤ -> ⊥, ⊥, ?] f `=> x `=> f`(x) //│ Type: CodeBase[out ('x -> 'app) -> ('x -> 'app), ⊥, ?] x `=> y `=> x `+ y //│ Type: CodeBase[out Int -> (Int -> Int), ⊥, ?] (x, y) `=> x `+ y //│ Type: CodeBase[out (Int, Int) -> Int, ⊥, ?] (x, y, z) `=> x `+ y `+ z //│ Type: CodeBase[out (Int, Int, Int) -> Int, ⊥, ?] f `=> x `=> y `=> f`(x, y) //│ Type: CodeBase[out (('x, 'y) -> 'app) -> ('x -> ('y -> 'app)), ⊥, ?] `let x = `42 `in x //│ Type: CodeBase[out Int, ⊥, ?] :e `let x = 42 `in x //│ ╔══[COMPILATION ERROR] Type error in unquoted term //│ ║ l.60: `let x = 42 `in x //│ ║ ^^ //│ ╙── because: cannot constrain Int <: CodeBase[in ⊥ out 'cde, in ⊥ out 'ctx, in ⊥ out ⊤] //│ Type: CodeBase[⊥, ⊥, ?] :e `let x = `0 `in 1 //│ ╔══[COMPILATION ERROR] Type error in unquoted term //│ ║ l.68: `let x = `0 `in 1 //│ ║ ^ //│ ╙── because: cannot constrain Int <: CodeBase[in ⊥ out 'cde, in ⊥ out 'ctx, in ⊥ out ⊤] //│ Type: CodeBase[⊥, ⊥, ?] `if `true then `true else `false //│ Type: CodeBase[out Bool, ⊥, ?] x `=> `if x `=== `0.0 then `1.0 else x //│ Type: CodeBase[out 'x -> ('x ∨ Num), ⊥, ?] run(`1) //│ Type: Int :e run(1) //│ ╔══[COMPILATION ERROR] Type error in integer literal with expected type CodeBase[out 'T, ⊥, ?] //│ ║ l.90: run(1) //│ ║ ^ //│ ╙── because: cannot constrain Int <: CodeBase[in ⊥ out 'T, ⊥, in ⊥ out ⊤] //│ Type: ⊥ :e x `=> run(x) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type CodeBase[out 'T, ⊥, ?] //│ ║ l.98: x `=> run(x) //│ ║ ^ //│ ╟── because: cannot constrain CodeBase['x, x, ⊥] <: CodeBase[in ⊥ out 'T, ⊥, in ⊥ out ⊤] //│ ╙── because: cannot constrain x <: ⊥ //│ Type: CodeBase[out CodeBase[out 'cde, ?, ?] -> 'cde, out 'ctx, ?] :e `let x = `42 `in run(x) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type CodeBase[out 'T, ⊥, ?] //│ ║ l.107: `let x = `42 `in run(x) //│ ║ ^ //│ ╟── because: cannot constrain CodeBase['cde, x, ⊥] <: CodeBase[in ⊥ out 'T, ⊥, in ⊥ out ⊤] //│ ╙── because: cannot constrain x <: ⊥ //│ ╔══[COMPILATION ERROR] Type error in unquoted term //│ ║ l.107: `let x = `42 `in run(x) //│ ║ ^^^^^^ //│ ╟── because: cannot constrain 'T <: CodeBase[in ⊥ out 'cde1, in ⊥ out 'ctx, in ⊥ out ⊤] //│ ╟── because: cannot constrain 'cde <: CodeBase[in ⊥ out 'cde1, in ⊥ out 'ctx, in ⊥ out ⊤] //│ ╟── because: cannot constrain 'cde <: CodeBase[in ⊥ out 'cde2, in ⊥ out 'ctx1, in ⊥ out ⊤] //│ ╙── because: cannot constrain Int <: CodeBase[in ⊥ out 'cde2, in ⊥ out 'ctx1, in ⊥ out ⊤] //│ Type: CodeBase[⊥, ⊥, ?] ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLRec.mls ================================================ :invalml :fixme // parsing fun f x = f //│ ╔══[COMPILATION ERROR] Invalid function definition head: unexpected identifier in this position //│ ║ l.5: fun f x = f //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Function definition shape not yet supported for f //│ ║ l.5: fun f x = f //│ ╙── ^ :e f //│ ╔══[COMPILATION ERROR] Variable not found: f //│ ║ l.14: f //│ ╙── ^ //│ Type: ⊥ :e x //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.21: x //│ ╙── ^ //│ Type: ⊥ fun f(x) = f f //│ Type: ['f] -> ⊤ -> (⊤ -> 'f) //│ Where: //│ ⊤ -> 'f <: 'f fun f(x) = f(x) f //│ Type: ⊤ -> ⊥ :todo fun f(x) = f(x.a) //│ ╔══[COMPILATION ERROR] Term shape not yet supported by InvalML: Sel(Ref(x),Ident(a)) //│ ║ l.38: fun f(x) = f(x.a) //│ ╙── ^^^ data class Foo[A](a: A) :todo proper error Foo(123) //│ ╔══[COMPILATION ERROR] Variable not found: Foo //│ ║ l.47: Foo(123) //│ ╙── ^^^ //│ Type: ⊥ new Foo(123) //│ Type: Foo['A] //│ Where: //│ Int <: 'A :todo proper error fun f(x) = f(Foo.a(x)) //│ ╔══[COMPILATION ERROR] Term shape not yet supported by InvalML: Sel(Ref(member:Foo),Ident(a)) //│ ║ l.59: fun f(x) = f(Foo.a(x)) //│ ╙── ^^^^^ fun f(x) = f(x.Foo#a) f //│ Type: ['x] -> Foo[out 'x] -> ⊥ //│ Where: //│ 'x <: Foo[out 'x] ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLRef.mls ================================================ :invalml region x in 42 //│ Type: Int region x in x //│ Type: Region[?] region x in x.ref 42 //│ Type: Ref[Int, ?] let r = region x in x.ref 42 r //│ Type: Ref[Int, ?] :e let r = region x in x.ref 42 !(r) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.19: let r = region x in x.ref 42 //│ ║ ^^^^^^^^ //│ ║ l.20: !(r) //│ ║ ^^^ //│ ╟── because: cannot constrain 'reg ∨ 'eff <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'reg1 <: ⊥ //│ ╟── because: cannot constrain 'x <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int fun mkRef() = region x in x.ref 42 mkRef //│ Type: [outer] -> () -> Ref[Int, out ¬outer] :e let t = region x in x in t.ref 42 //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.38: let t = region x in x in t.ref 42 //│ ║ ^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'eff <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'x <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Ref[Int, ?] region x in let r = x.ref 42 in r := 0 //│ Type: Int :e let t = region x in x.ref 42 in t := 0 //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.55: x.ref 42 //│ ║ ^^^^^^^^ //│ ║ l.56: in t := 0 //│ ║ ^^^^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'eff <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'reg1 <: ⊥ //│ ╟── because: cannot constrain 'x <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int region x in let r = x.ref 42 in !(r) //│ Type: Int :e let t = region x in x.ref 42 in !(t) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.76: x.ref 42 //│ ║ ^^^^^^^^ //│ ║ l.77: in !(t) //│ ║ ^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'eff <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'reg1 <: ⊥ //│ ╟── because: cannot constrain 'x <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Int region x in let r = x.ref 42 in let t = r := 0 in !(r) //│ Type: Int fun rid: [A] -> A -> A fun rid(x) = let t = (region y in 42) in x fun rid: [A] -> A -> A fun rid(x) = let t = (region y in 42) in x :e region x in (let dz = x.ref 42 in 42) as [A] -> Int //│ ╔══[COMPILATION ERROR] Type error in block with expected type [outer, 'A] -> Int //│ ║ l.105: (let dz = x.ref 42 in 42) as [A] -> Int //│ ║ ^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╙── because: cannot constrain x <: ⊥ //│ Type: Int :e let t = region x in y => x.ref y in t(42) //│ ╔══[COMPILATION ERROR] Type error in block //│ ║ l.117: y => x.ref y //│ ║ ^^^^^^^^^^^^ //│ ║ l.118: in t(42) //│ ║ ^^^^^^^^ //│ ╟── because: cannot constrain 'reg ∨ 'eff <: ⊥ //│ ╟── because: cannot constrain 'reg <: ⊥ //│ ╟── because: cannot constrain 'x <: ⊥ //│ ╙── because: cannot constrain ⊤ <: ⊥ //│ Type: Ref[in 'y out 'y ∨ Int, ?] fun foo: [A] -> Int ->{A} Int fun foo(x) = region y in x + 1 region x in x.ref ((x => x) as [A] -> A -> A) //│ Type: Ref['A -> 'A, ?] fun foo: [A extends Int] -> A -> A fun foo(x) = x foo //│ Type: ['A] -> ('A) ->{⊥} 'A //│ Where: //│ 'A <: Int region x in x.ref foo //│ Type: Ref[in 'A -> ('A ∧ Int) out ('A ∧ Int) -> 'A, ?] fun bar: ([A] -> A -> A) -> Int fun bar(f) = f(42) bar //│ Type: (['A] -> ('A) ->{⊥} 'A) ->{⊥} Int :e region x in x.ref bar //│ ╔══[COMPILATION ERROR] Expected a monomorphic type or an instantiable type here, but ([outer, 'A] -> ('A) ->{⊥} 'A) ->{⊥} Int found //│ ║ l.158: region x in x.ref bar //│ ╙── ^^^ //│ Type: Ref[⊥, ?] ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLScratch.mls ================================================ :invalml // :d let i = 1 let i = 1 i //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLSelfApp.mls ================================================ :invalml (x => x(x)) (x => x(x)) //│ Type: ⊥ fun f(x) = x(x) f //│ Type: ['x, 'eff, 'app] -> ('x ->{'eff} 'app) ->{'eff} 'app //│ Where: //│ 'x <: 'x ->{'eff} 'app f(f) //│ Type: ⊥ ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLSeq.mls ================================================ :invalml data class Seq[A, E](next: () ->{E} A) fun map: [A, B, E] -> Seq[out A, out E] -> (A ->{E} B) -> Seq[out B, out E] // * Note: equivalent since Seq is covariant: // fun map: [A, B, E, F] -> Seq[out A, out E] -> (A ->{F} B) -> Seq[out B, out E | F] // * Forces the elements of the sequence to be evaluated and caches them fun force_cache: [A, B, E] -> Seq[out A, out E] ->{E} Seq[out B, Nothing] // * Dummy implementation fun mapi: [A, E] -> Seq[out A, out E] -> ((Int, A) ->{E} A) ->{Nothing} Seq[out A, out E] fun mapi = s => f => region r in map(s) of x => f(0, x) // * Should be an error. This definition would not be referentially transparent. // * The error message needs improvement, though. :e fun mapi: [A, E] -> Seq[out A, out E] -> ((Int, A) ->{E} A) ->{Nothing} Seq[out A, out E] fun mapi = s => f => region r in let i = r.ref 0 map(s) of x => i := !(i) + 1 f(!(i), x) //│ ╔══[COMPILATION ERROR] Type error in region expression with expected type Seq[out A, out E] //│ ║ l.28: let i = r.ref 0 //│ ║ ^^^^^^^ //│ ║ l.29: map(s) of x => //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.30: i := !(i) + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.31: f(!(i), x) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain Seq[in ⊥ out 'B, in ⊥ out 'E1] <: Seq[in ⊥ out A, in ⊥ out E] //│ ╟── because: cannot constrain 'E1 <: E //│ ╟── because: cannot constrain 'reg <: E //│ ╟── because: cannot constrain 'r ∧ ¬outer <: E //│ ╟── because: cannot constrain 'r <: E ∨ outer //│ ╙── because: cannot constrain ¬outer <: E ∨ outer // * Notice the inferred type, which produces an unusable Sequence (of effect `?` ie `Any`) fun mapi = s => f => region r in let i = r.ref 0 map(s) of x => i := !(i) + 1 f(!(i), x) mapi //│ Type: [outer, 'A, 'E, 'app, 'eff] -> Seq[out 'A, out 'E] -> (((Int, 'A) ->{'eff} 'app) -> Seq[out 'app, out (¬outer ∨ 'eff) ∨ 'E]) // * This version is correct as it keeps the mutation encapsulated within the region fun mapi_force: [A, E] -> Seq[out A, out E] -> ((Int, A) ->{E} A) ->{E} Seq[out A, Nothing] fun mapi_force = s => f => region r in let i = r.ref 0 force_cache of map(s) of x => i := !(i) + 1 f(!(i), x) // * An alternative version that takes an existing region in parameter fun mapi: [A, R, E] -> (Seq[out A, out E], Region[out R]) -> ((Int, A) ->{E} A) ->{R} Seq[out A, out E | R] fun mapi = (s, r) => f => let i = r.ref 0 map(s) of x => i := !(i) + 1 f(!(i), x) // * Simpler; should be equivalent when Region is covariant fun mapi: [A, E] -> (Seq[out A, out E], Region[out E]) -> ((Int, A) ->{E} A) ->{E} Seq[out A, out E] fun mapi = (s, r) => f => let i = r.ref 0 map(s) of x => i := !(i) + 1 f(!(i), x) // * Example usage fun mkSeq: [A, E] -> (() ->{E} A) ->{E} Seq[out A, out E] fun head: [A, E] -> Seq[out A, out E] ->{E} A region r in let sum = r.ref 0 let s1 = mkSeq of () => sum := !(sum) + 1, !(sum) let s2 = mapi(s1, r) of (x, i) => x * i !(sum) + head(s2) //│ Type: Int fun r: Ref[Int, Nothing] r := 1 !(r) //│ Type: Int :e r := 1 ! r // because of the space, this is a NL-op //│ ╔══[COMPILATION ERROR] Builtin '!' is not a binary operator //│ ║ l.106: r := 1 //│ ║ ^^^^^^ //│ ║ l.107: ! r // because of the space, this is a NL-op //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Variable not found: ! //│ ║ l.107: ! r // because of the space, this is a NL-op //│ ╙── ^ //│ Type: ⊥ r := 1, // `,` to avoid parsing `! r` on next line as continuation of expression ! r //│ Type: Int ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLSyntax.mls ================================================ :invalml :parseOnly data class Foo //│ Parsed: //│ Modified(Keywrd(keyword 'data'),TypeDef(Cls,Ident(Foo),None)) data class Bar(x: Int) //│ Parsed: //│ Modified(Keywrd(keyword 'data'),TypeDef(Cls,App(Ident(Bar),Tup(List(InfixApp(Ident(x),Keywrd(keyword ':'),Ident(Int))))),None)) data class Bar2(x: Int, y: Int) //│ Parsed: //│ Modified(Keywrd(keyword 'data'),TypeDef(Cls,App(Ident(Bar2),Tup(List(InfixApp(Ident(x),Keywrd(keyword ':'),Ident(Int)), InfixApp(Ident(y),Keywrd(keyword ':'),Ident(Int))))),None)) data class Baz[A] //│ Parsed: //│ Modified(Keywrd(keyword 'data'),TypeDef(Cls,App(Ident(Baz),TyTup(List(Ident(A)))),None)) data class BazBaz[A](f: A -> A) //│ Parsed: //│ Modified(Keywrd(keyword 'data'),TypeDef(Cls,App(App(Ident(BazBaz),TyTup(List(Ident(A)))),Tup(List(InfixApp(Ident(f),Keywrd(keyword ':'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A)))))),None)) Baz[Int] //│ Parsed: //│ App(Ident(Baz),TyTup(List(Ident(Int)))) Baz[in Int] //│ Parsed: //│ App(Ident(Baz),TyTup(List(Modified(Keywrd(keyword 'in'),Ident(Int))))) Baz[out Int] //│ Parsed: //│ App(Ident(Baz),TyTup(List(Modified(Keywrd(keyword 'out'),Ident(Int))))) Baz[in Int out Int] //│ Parsed: //│ App(Ident(Baz),TyTup(List(Tup(List(Modified(Keywrd(keyword 'in'),Ident(Int)), Modified(Keywrd(keyword 'out'),Ident(Int))))))) t.Bar#x //│ Parsed: //│ InfixApp(Sel(Ident(t),Ident(Bar)),Keywrd(keyword '#'),Ident(x)) foo.Bar#baz(42) //│ Parsed: //│ App(InfixApp(Sel(Ident(foo),Ident(Bar)),Keywrd(keyword '#'),Ident(baz)),Tup(List(IntLit(42)))) foo(42).Bar#baz(42) //│ Parsed: //│ App(InfixApp(Sel(App(Ident(foo),Tup(List(IntLit(42)))),Ident(Bar)),Keywrd(keyword '#'),Ident(baz)),Tup(List(IntLit(42)))) let x = 42 in x + 0 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(42)),Some(OpApp(Ident(x),Ident(+),List(IntLit(0))))) fun id(x) = x //│ Parsed: //│ TermDef(Fun,App(Ident(id),Tup(List(Ident(x)))),Some(Ident(x))) x => x + 1 //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),OpApp(Ident(x),Ident(+),List(IntLit(1)))) true and true //│ Parsed: //│ InfixApp(BoolLit(true),Keywrd(keyword 'and'),BoolLit(true)) false: Bool //│ Parsed: //│ InfixApp(BoolLit(false),Keywrd(keyword ':'),Ident(Bool)) f of false //│ Parsed: //│ App(Ident(f),Tup(List(BoolLit(false)))) let bar = new Bar(42) //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(bar),Some(App(LexicalNew(Some(Ident(Bar)),None),Tup(List(IntLit(42))))),None) let bar = new Bar2(1, 1) //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(bar),Some(App(LexicalNew(Some(Ident(Bar2)),None),Tup(List(IntLit(1), IntLit(1))))),None) new Bar(0) //│ Parsed: //│ App(LexicalNew(Some(Ident(Bar)),None),Tup(List(IntLit(0)))) new Bar2(114, 514) //│ Parsed: //│ App(LexicalNew(Some(Ident(Bar2)),None),Tup(List(IntLit(114), IntLit(514)))) fun inc: Int -> Int //│ Parsed: //│ TermDef(Fun,InfixApp(Ident(inc),Keywrd(keyword ':'),InfixApp(Tup(List(Ident(Int))),Keywrd(keyword '->'),Ident(Int))),None) [A] -> A -> A //│ Parsed: //│ InfixApp(TyTup(List(Ident(A))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A))) fun id: [A] -> A -> A //│ Parsed: //│ TermDef(Fun,InfixApp(Ident(id),Keywrd(keyword ':'),InfixApp(TyTup(List(Ident(A))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A)))),None) [A] => (x: A) => x //│ Parsed: //│ InfixApp(TyTup(List(Ident(A))),Keywrd(keyword '=>'),InfixApp(Tup(List(InfixApp(Ident(x),Keywrd(keyword ':'),Ident(A)))),Keywrd(keyword '=>'),Ident(x))) [A, B] -> A -> B //│ Parsed: //│ InfixApp(TyTup(List(Ident(A), Ident(B))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(B))) [A, B, C] -> (A, B) -> C //│ Parsed: //│ InfixApp(TyTup(List(Ident(A), Ident(B), Ident(C))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A), Ident(B))),Keywrd(keyword '->'),Ident(C))) ([A] -> A -> A) -> Int //│ Parsed: //│ InfixApp(Tup(List(InfixApp(TyTup(List(Ident(A))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A))))),Keywrd(keyword '->'),Ident(Int)) x => if x == 0 then 1 else x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(OpApp(Ident(x),Ident(==),List(IntLit(0))),Keywrd(keyword 'then'),IntLit(1)), PrefixApp(Keywrd(keyword 'else'),Ident(x)))))) if 1 < 2 then 1 else 0 //│ Parsed: //│ IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(OpApp(IntLit(1),Ident(<),List(IntLit(2))),Keywrd(keyword 'then'),IntLit(1)), PrefixApp(Keywrd(keyword 'else'),IntLit(0))))) if false then 0 else 42 //│ Parsed: //│ IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(BoolLit(false),Keywrd(keyword 'then'),IntLit(0)), PrefixApp(Keywrd(keyword 'else'),IntLit(42))))) if 24 then false else true //│ Parsed: //│ IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(IntLit(24),Keywrd(keyword 'then'),BoolLit(false)), PrefixApp(Keywrd(keyword 'else'),BoolLit(true))))) if x then true else false //│ Parsed: //│ IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(Ident(x),Keywrd(keyword 'then'),BoolLit(true)), PrefixApp(Keywrd(keyword 'else'),BoolLit(false))))) if 1 is Int then 1 else 0 //│ Parsed: //│ IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(InfixApp(IntLit(1),Keywrd(keyword 'is'),Ident(Int)),Keywrd(keyword 'then'),IntLit(1)), PrefixApp(Keywrd(keyword 'else'),IntLit(0))))) fun fact = case 0 then 1 n then n * fact(n - 1) //│ Parsed: //│ TermDef(Fun,Ident(fact),Some(Case(Keywrd(keyword 'case'),Block(List(InfixApp(IntLit(0),Keywrd(keyword 'then'),IntLit(1)), InfixApp(Ident(n),Keywrd(keyword 'then'),OpApp(Ident(n),Ident(*),List(App(Ident(fact),Tup(List(OpApp(Ident(n),Ident(-),List(IntLit(1)))))))))))))) `42 //│ Parsed: //│ Quoted(IntLit(42)) x `=> x //│ Parsed: //│ Quoted(InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Unquoted(Ident(x)))) (x, y) `=> x `+ y //│ Parsed: //│ Quoted(InfixApp(Tup(List(Ident(x), Ident(y))),Keywrd(keyword '=>'),Unquoted(Quoted(App(Ident(+),Tup(List(Unquoted(Ident(x)), Unquoted(Ident(y))))))))) (x, y, z) `=> x `+ y `+ z //│ Parsed: //│ Quoted(InfixApp(Tup(List(Ident(x), Ident(y), Ident(z))),Keywrd(keyword '=>'),Unquoted(Quoted(App(Ident(+),Tup(List(Unquoted(Quoted(App(Ident(+),Tup(List(Unquoted(Ident(x)), Unquoted(Ident(y))))))), Unquoted(Ident(z))))))))) `1 `+ `1 //│ Parsed: //│ Quoted(App(Ident(+),Tup(List(Unquoted(Quoted(IntLit(1))), Unquoted(Quoted(IntLit(1))))))) f`(x) //│ Parsed: //│ Quoted(App(Unquoted(Ident(f)),Tup(List(Unquoted(Ident(x)))))) g`(`1, `2) //│ Parsed: //│ Quoted(App(Unquoted(Ident(g)),Tup(List(Unquoted(Quoted(IntLit(1))), Unquoted(Quoted(IntLit(2))))))) `let x = `42 `in x //│ Parsed: //│ Quoted(LetLike(Keywrd(keyword 'let'),Ident(x),Some(Unquoted(Quoted(IntLit(42)))),Some(Unquoted(Ident(x))))) `if x `== `0.0 then `1.0 else x //│ Parsed: //│ Quoted(IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(Unquoted(Quoted(App(Ident(==),Tup(List(Unquoted(Ident(x)), Unquoted(Quoted(DecLit(0.0)))))))),Keywrd(keyword 'then'),Unquoted(Quoted(DecLit(1.0)))), PrefixApp(Keywrd(keyword 'else'),Unquoted(Ident(x))))))) x `=> if 0 == 0 then x else `0 //│ Parsed: //│ Quoted(InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Unquoted(IfLike(Keywrd(keyword 'if'),Block(List(InfixApp(OpApp(IntLit(0),Ident(==),List(IntLit(0))),Keywrd(keyword 'then'),Ident(x)), PrefixApp(Keywrd(keyword 'else'),Quoted(IntLit(0))))))))) region x in 42 //│ Parsed: //│ Region(Ident(x),IntLit(42)) region x in x.ref 42 //│ Parsed: //│ Region(Ident(x),Block(List(RegRef(Ident(x),IntLit(42))))) region x in let r = x.ref 42 in let t = r := 0 in !(r) //│ Parsed: //│ Region(Ident(x),Block(List(LetLike(Keywrd(keyword 'let'),Ident(r),Some(RegRef(Ident(x),IntLit(42))),Some(LetLike(Keywrd(keyword 'let'),Ident(t),Some(OpApp(Ident(r),Ident(:=),List(IntLit(0)))),Some(App(Ident(!),Tup(List(Bra(Round,Ident(r)))))))))))) !(r) + 1 //│ Parsed: //│ OpApp(App(Ident(!),Tup(List(Bra(Round,Ident(r))))),Ident(+),List(IntLit(1))) Int ->{Any} Int //│ Parsed: //│ InfixApp(Tup(List(Ident(Int))),Keywrd(keyword '->'),Effectful(Ident(Any),Ident(Int))) [A] -> Str ->{A} Str //│ Parsed: //│ InfixApp(TyTup(List(Ident(A))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(Str))),Keywrd(keyword '->'),Effectful(Ident(A),Ident(Str)))) A | B //│ Parsed: //│ OpApp(Ident(A),Ident(|),List(Ident(B))) [A extends Int] -> A -> A //│ Parsed: //│ InfixApp(TyTup(List(InfixApp(Ident(A),Keywrd(keyword 'extends'),Ident(Int)))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A))) [A restricts Int] -> A -> A //│ Parsed: //│ InfixApp(TyTup(List(InfixApp(Ident(A),Keywrd(keyword 'restricts'),Ident(Int)))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A))) [A extends Int restricts Int] -> A -> A //│ Parsed: //│ InfixApp(TyTup(List(InfixApp(InfixApp(Ident(A),Keywrd(keyword 'extends'),Ident(Int)),Keywrd(keyword 'restricts'),Ident(Int)))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(A))) [A extends Int, B restricts Int] -> A -> B //│ Parsed: //│ InfixApp(TyTup(List(InfixApp(Ident(A),Keywrd(keyword 'extends'),Ident(Int)), InfixApp(Ident(B),Keywrd(keyword 'restricts'),Ident(Int)))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(B))) [A extends Int restricts Int, B extends Int restricts Int] -> A -> B //│ Parsed: //│ InfixApp(TyTup(List(InfixApp(InfixApp(Ident(A),Keywrd(keyword 'extends'),Ident(Int)),Keywrd(keyword 'restricts'),Ident(Int)), InfixApp(InfixApp(Ident(B),Keywrd(keyword 'extends'),Ident(Int)),Keywrd(keyword 'restricts'),Ident(Int)))),Keywrd(keyword '->'),InfixApp(Tup(List(Ident(A))),Keywrd(keyword '->'),Ident(B))) ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLTODOs.mls ================================================ :invalml :todo let id: [A] -> A -> A = x => x //│ ╔══[COMPILATION ERROR] Unsupported let binding shape //│ ║ l.5: let id: [A] -> A -> A = x => x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ :todo fun id[A](x: A) = x //│ ╔══[COMPILATION ERROR] Variable not found: A //│ ║ l.11: fun id[A](x: A) = x //│ ╙── ^ :fixme fun id: [A] -> A -> A => x = x //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.17: fun id: [A] -> A -> A => x = x //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.17: fun id: [A] -> A -> A => x = x //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Expected a type, got function literal //│ ║ l.17: fun id: [A] -> A -> A => x = x //│ ╙── ^ //│ ═══[COMPILATION ERROR] Function definition shape not yet supported for id ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLUntyped.mls ================================================ :invalml :js @untyped 1 //│ = 1 //│ Type: ⊥ :re (@untyped 1)(2) //│ ═══[RUNTIME ERROR] TypeError: 1 is not a function //│ Type: ⊥ @untyped fun f: Int @untyped declare fun f: Int @untyped @untyped declare fun f: Int @untyped fun f() = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLUsefulExtrusion.mls ================================================ :invalml fun (;) seq(_, res) = res fun wield: [R] -> (Region[out R]) ->{R} () fun freeze: [R, E extends ~R, T] -> (Region[out R], () ->{~R & E} T) ->{R | E} T freeze //│ Type: ['R, 'E, 'T] -> (Region[out 'R], () ->{¬'R ∧ 'E} 'T) ->{'R ∨ 'E} 'T //│ Where: //│ 'E <: ¬'R fun foo(f) = region r in freeze(r, () => f(32)) foo //│ Type: [outer, 'app, 'eff] -> (Int ->{'eff} 'app) ->{'eff} 'app //│ Where: //│ 'eff <: outer fun foo(f) = region r in freeze(r, f) 123 foo //│ Type: [outer, 'E] -> (() ->{'E ∧ outer} ⊤) ->{'E} Int //│ Where: //│ 'E <: outer fun foo(r1, r2, f, g) = freeze(r1, () => f(123)); freeze(r2, () => g(456)) foo //│ Type: ['res, 'R, 'E, 'R1, 'E1] -> (Region[out 'R], Region[out 'R1], Int ->{'E ∧ ¬'R} ⊤, Int ->{'E1 ∧ ¬'R1} 'res) ->{(('R ∨ 'E) ∨ 'R1) ∨ 'E1} 'res //│ Where: //│ 'E <: ¬'R //│ 'E1 <: ¬'R1 fun freeze: [E, R, S, T] -> (Region[out R], () ->{~R & E} T) ->{R | E} T freeze //│ Type: ['E, 'R, 'T] -> (Region[out 'R], () ->{¬'R ∧ 'E} 'T) ->{'R ∨ 'E} 'T fun foo(f) = region r in freeze(r, () => f(32)) foo //│ Type: [outer, 'app, 'eff] -> (Int ->{'eff} 'app) ->{'eff} 'app //│ Where: //│ 'eff <: outer fun foo(f) = region r in freeze(r, f) 123 foo //│ Type: [outer, 'E] -> (() ->{'E ∧ outer} ⊤) ->{'E} Int fun foo: [R1, R2] -> (Region[out R1], Region[out R2], Int ->{R1 & ~R2} (), Int ->{R2 & ~R1} ()) ->{R1 | R2} () fun foo(r1, r2, f, g) = f(123); g(456) fun foo(r1, r2, f, g) = freeze(r1, () => f(123)); freeze(r2, () => g(456)) foo //│ Type: ['res, 'E, 'R, 'E1, 'R1] -> (Region[out 'R], Region[out 'R1], Int ->{'E ∧ ¬'R} ⊤, Int ->{'E1 ∧ ¬'R1} 'res) ->{(('R ∨ 'E) ∨ 'R1) ∨ 'E1} 'res fun foo(r1, r2, f, g) = freeze(r1, () => freeze(r2, () => f(r2); g(r1))) foo //│ Type: ['r1, 'r2, 'E, 'R, 'T, 'R1] -> ('r1, 'r2, 'r2 ->{('E ∧ ¬'R) ∧ ¬'R1} ⊤, 'r1 ->{('E ∧ ¬'R) ∧ ¬'R1} 'T) ->{'R ∨ 'E} 'T //│ Where: //│ 'r1 <: Region[out 'R] //│ 'R1 <: 'E //│ 'R1 <: ¬'R //│ 'r2 <: Region[out 'R1] :e region r in foo(r, r, wield, wield) //│ ╔══[COMPILATION ERROR] Type error in reference with expected type 'r2 //│ ║ l.98: foo(r, r, wield, wield) //│ ║ ^ //│ ╟── because: cannot constrain Region[in ⊥ out r] <: 'r2 //│ ╟── because: cannot constrain Region[in ⊥ out r] <: Region[in ⊥ out 'R] //│ ╟── because: cannot constrain r <: 'R //│ ╟── because: cannot constrain r <: ¬'R1 //│ ╟── because: cannot constrain 'R1 <: ¬r //│ ╙── because: cannot constrain r <: ¬r //│ ╔══[COMPILATION ERROR] Type error in reference of type ‹not implemented› with expected type 'f //│ ║ l.98: foo(r, r, wield, wield) //│ ║ ^^^^^ //│ ╟── because: cannot constrain (Region[in ⊥ out 'R2]) ->{'R2} (()) <: 'f //│ ╟── because: cannot constrain (Region[in ⊥ out 'R2]) ->{'R2} (()) <: ('r2) ->{¬'R ∧ ¬'R1 ∧ 'E} (⊤) //│ ╟── because: cannot constrain 'R2 <: ¬'R ∧ ¬'R1 ∧ 'E //│ ╟── because: cannot constrain 'R2 <: ¬'R1 //│ ╟── because: cannot constrain r <: ¬'R1 //│ ╟── because: cannot constrain 'R1 <: ¬r //│ ╙── because: cannot constrain r <: ¬r //│ ╔══[COMPILATION ERROR] Type error in reference of type ‹not implemented› with expected type 'g //│ ║ l.98: foo(r, r, wield, wield) //│ ║ ^^^^^ //│ ╟── because: cannot constrain (Region[in ⊥ out 'R3]) ->{'R3} (()) <: 'g //│ ╟── because: cannot constrain (Region[in ⊥ out 'R3]) ->{'R3} (()) <: ('r1) ->{¬'R ∧ ¬'R1 ∧ 'E} ('T) //│ ╟── because: cannot constrain 'R3 <: ¬'R ∧ ¬'R1 ∧ 'E //│ ╟── because: cannot constrain 'R3 <: ¬'R1 //│ ╟── because: cannot constrain r <: ¬'R1 //│ ╟── because: cannot constrain 'R1 <: ¬r //│ ╙── because: cannot constrain r <: ¬r :e region r in region s in foo(r, s, wield, wield) //│ ╔══[COMPILATION ERROR] Type error in reference of type ‹not implemented› with expected type 'f //│ ║ l.132: foo(r, s, wield, wield) //│ ║ ^^^^^ //│ ╟── because: cannot constrain (Region[in ⊥ out 'R]) ->{'R} (()) <: 'f //│ ╟── because: cannot constrain (Region[in ⊥ out 'R]) ->{'R} (()) <: ('r2) ->{¬'R1 ∧ ¬'R2 ∧ 'E} (⊤) //│ ╟── because: cannot constrain 'R <: ¬'R1 ∧ ¬'R2 ∧ 'E //│ ╟── because: cannot constrain 'R <: ¬'R1 //│ ╟── because: cannot constrain s ∧ ¬r <: ¬'R1 //│ ╟── because: cannot constrain 'R1 <: ¬s ∨ r //│ ╙── because: cannot constrain s ∧ ¬r <: ¬s ∨ r //│ ╔══[COMPILATION ERROR] Type error in reference of type ‹not implemented› with expected type 'g //│ ║ l.132: foo(r, s, wield, wield) //│ ║ ^^^^^ //│ ╟── because: cannot constrain (Region[in ⊥ out 'R3]) ->{'R3} (()) <: 'g //│ ╟── because: cannot constrain (Region[in ⊥ out 'R3]) ->{'R3} (()) <: ('r1) ->{¬'R1 ∧ ¬'R2 ∧ 'E} ('T) //│ ╟── because: cannot constrain 'R3 <: ¬'R1 ∧ ¬'R2 ∧ 'E //│ ╟── because: cannot constrain 'R3 <: ¬'R2 //│ ╟── because: cannot constrain r <: ¬'R2 //│ ╟── because: cannot constrain 'R2 <: ¬r //│ ╙── because: cannot constrain r <: ¬r region r in region s in foo(r, s, x => x, y => y) //│ Type: Region[?] :fixme fun foo[outer, R <: outer](r1: Region[R]) = region r2 in freeze(r2, () => wield(r1) ) //│ ╔══[COMPILATION ERROR] Unsupported type parameter outer binding //│ ║ l.162: fun foo[outer, R <: outer](r1: Region[R]) = //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Unsupported type parameter operator application //│ ║ l.162: fun foo[outer, R <: outer](r1: Region[R]) = //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: R //│ ║ l.162: fun foo[outer, R <: outer](r1: Region[R]) = //│ ╙── ^ //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Expected a type, got a non-type ‹error› //│ ═══[COMPILATION ERROR] Invalid type :fixme fun foo: [outer, R <: outer] -> Region[R] -> () //│ ╔══[COMPILATION ERROR] Illegal forall annotation. //│ ║ l.183: fun foo: [outer, R <: outer] -> Region[R] -> () //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[COMPILATION ERROR] Expected a type, got ‹error› //│ ═══[COMPILATION ERROR] Invalid type // * A function that executes its argument `f` in a frozen local region; // * we show that this `f` can itself in turn freeze its own region from the call site fun foo(f) = region r in freeze of r, () => f() foo //│ Type: [outer, 'app, 'eff] -> (() ->{'eff} 'app) ->{'eff} 'app //│ Where: //│ 'eff <: outer region s in foo(() => wield(s)) region s in foo of () => wield(s) freeze(s, () => 42) //│ Type: Int :e region s in foo of () => freeze(s, () => wield(s)) //│ ╔══[COMPILATION ERROR] Type error in function literal with expected type () ->{¬'R ∧ 'E} 'T //│ ║ l.217: freeze(s, () => wield(s)) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── because: cannot constrain 'R1 <: ¬'R ∧ 'E //│ ╟── because: cannot constrain 'R1 <: ¬'R //│ ╟── because: cannot constrain s <: ¬'R //│ ╟── because: cannot constrain 'R <: ¬s //│ ╙── because: cannot constrain s <: ¬s region s in foo of () => region t in wield(s) freeze(s, () => wield(t)) ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/InvalMLVariance.mls ================================================ :invalml class Foo[out A] (x: Foo[Int]) => (x as Foo[Int | Str]) //│ Type: (Foo[out Int]) ->{⊥} Foo[out Int ∨ Str] class Foo[in A] (x: Foo[Int]) => (x as Foo[Int & Str]) //│ Type: (Foo[in Int]) ->{⊥} Foo[in Int ∧ Str] ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/DynamicProgramming.mls ================================================ :global :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— class Option[A] with constructor Some(x: A) None class ArrayList[T, out R] class Iter[T, out R] class Array2D[T, out R] class Interviewee with constructor Itv(score: Int, salary: Int) fun (;) seq(_, res) = res fun toString: Any -> Str fun concat: (Str, Str) -> Str fun println: Str -> () fun empty: [A, R] -> Region[out R] ->{R} ArrayList[A, out R] push: [A, R] -> (ArrayList[A, out R], A) ->{R} () iter: [Res, R, E extends ~R, T] -> (ArrayList[T, out R], [S] -> Iter[T, S] ->{S | E} Res) ->{E | R} Res revIter: [outer, Res, R, E extends ~R, T] -> (ArrayList[T, out R], [S extends ~outer] -> Iter[T, S] ->{S | E} Res) ->{E | R} Res next: [T, S] -> Iter[T, S] ->{S} Option[T] len: [A, R] -> (ArrayList[A, out R]) ->{R} Int whileDo: [R] -> (() ->{R} Bool) ->{R} () init: [A, R] -> (Region[out R], Int, Int, A) ->{R} Array2D[A, R] update: [A, R] -> (Array2D[A, out R], Int, Int, A) ->{R} () get: [A, R] -> (Array2D[A, out R], Int, Int) ->{R} A max: (Int, Int) -> Int fun format(it) = if it is Itv(score, salary) then concat("interviewee, score: ", concat(toString(score), concat("salary", toString(salary)))) fun printAll(arr) = iter of arr, it => whileDo of () => if next(it) is Some(x) then println(format(x)); true None then false // fun select: [outer, R1 extends outer, R2 extends ~R1] -> (ArrayList[Interviewee, R1], Int, ArrayList[Interviewee, R2]) ->{R1 | R2} Int fun select(interviewees, budget, results) = region r in let size = len(interviewees), let i = r.ref 1 let dp = init(r, size + 1, budget + 1, 0) iter of interviewees, it => whileDo of () => if next(it) is Some(itv) then if itv is Itv(score, salary) then let j = r.ref 0 whileDo of () => if !(j) < salary then update(dp, !(i), !(j), get(dp, !(i) - 1, !(j))) else let p = get(dp, !(i) - 1, !(j) - salary), let np = get(dp, !(i) - 1, !(j)) update(dp, !(i), !(j), max of np, p + score) j := !(j) + 1; !(j) <= budget i := !(i) + 1 true None then false i := size let rest = r.ref budget revIter of interviewees, it => whileDo of () => if next(it) is Some(itv) then if itv is Itv(score, salary) then if get(dp, !(i), !(rest)) == get(dp, !(i) - 1, !(rest) - salary) + score then push(results, itv); rest := !(rest) - salary else () i := !(i) - 1 true None then false get(dp, size, budget) // region r in // let interviewees = empty(r) // push(interviewees, Itv(20, 3000)) // push(interviewees, Itv(50, 1000)) // push(interviewees, Itv(30, 1000)) // let results = empty(r) // let m = select(interviewees, 4000, results) // printAll(results) // m // fun wrongSelect(interviewees, budget, results) = // region r in // let size = len(interviewees), let i = r.ref 1 // let dp = init(r, size + 1, budget + 1, 0) // iter of interviewees, it => // whileDo of () => // if next(it) is // Some(itv) then if itv is Itv(score, salary) then // let j = r.ref 0 // whileDo of () => // if !j < salary then update(dp, !i, !j, get(dp, !i - 1, !j)) // else // let p = get(dp, !i - 1, !j - salary), let np = get(dp, !i - 1, !j) // update(dp, !i, !j, max of np, p + score) // j := !j + 1; !j <= budget // i := !i + 1 // true // None then false // i := size // let rest = r.ref budget // revIter of interviewees, it => // whileDo of () => // if next(it) is // Some(itv) then if itv is Itv(score, salary) then // if get(dp, !i, !rest) == get(dp, !i - 1, !rest - salary) + score // do push(interviewees, itv); rest := !rest - salary // i := !i - 1 // true // None then false // get(dp, size, budget) // region r in // let interviewees = empty(r) // push(interviewees, Itv(20, 3000)) // push(interviewees, Itv(50, 1000)) // push(interviewees, Itv(30, 1000)) // region r2 in // let results = empty(r2) // let m = wrongSelect(interviewees, 4000, results) // printAll(results) // m region r in let interviewees = empty(r) push(interviewees, Itv(40, 10)) push(interviewees, Itv(60, 20)) push(interviewees, Itv(120, 30)) push(interviewees, Itv(70, 20)) println("all interviewees:") printAll(interviewees) region r2 in let results = empty(r2) let m = select(interviewees, 60, results) println("candidates:") printAll(results) m //│ Type: Int //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/ExamplesInResponse.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This file contains examples in the author response. A few typos therein are corrected. // Some utility functions: fun wield: [R] -> (Region[out R]) ->{R} () fun freeze: [R, E extends ~R, T] -> (Region[out R], () ->{~R & E} T) ->{R | E} T // ^ Fixed a typo in the author response (`{~R | E}` should have been `{~R & E}`) fun (;) seq(_, res) = res fun rand: () -> Bool fun print: Any -> () // ### Example 1 // Passing the same region for both arguments fundamentally does not work approaches like Rust and Capability Calculus. fun foo1(r1, r2) = freeze(r1, () => print("ok")) wield(r2) // InvalML allows r1 and r2 to alias as long as ‹body› does not use r2; for instance: region r in foo1(r, r) // Capability Calculus and related systems would require r2 to be distinct from r1. // Note – this also works: region r in region s in foo1(r, s) // Here is a slightly less trivial examples that still type checks seamlessly in InvalML: region r0 in fun foo1(r1, r2) = freeze(r1, () => wield(r0)) wield(r2) region r in foo1(r, r) region r in region s in foo1(r, s) // On the other hand, given fun foo2(r1, r2) = freeze(r1, () => wield(r2)) wield(r2) // we rightfully reject // region r in // foo2(r, r) // but still accept region r in region s in foo2(r, s) // ### Example 2 fun foo3: [outer, R extends outer] -> Region[out R] ->{outer} () fun foo3(r1) = region r2 in freeze(r2, () => wield(r1) ) // ^ Fixed typos in the author response (it used the paper's syntax instead of the implementation's syntax) // In fact, this formally equivalent signature also works: fun foo3': [outer] -> Region[out outer] ->{outer} () fun foo3'(r1) = foo3(r1) // ### Example 3 fun foo4(r1, r2, f) = let exec = freeze(r1, () => freeze(r2, () => f(r1, r2))) let r = exec() !(r) + 1 region r in region s in foo4(r, s, (x, y) => if rand() then print("Chose x"); () => x.ref 0 else print("Chose y"); () => y.ref 1) // ### Example 4 fun foo5(f) = region r in freeze(r, () => f(32)) fun freeze2: [R, E extends ~R, T] -> (Region[out R], () ->{~R & E} T) ->{R | E} T fun foo6(f) = region r in freeze(r, () => f(32)) foo6 //│ Type: [outer, 'app, 'eff] -> (Int ->{'eff} 'app) ->{'eff} 'app //│ Where: //│ 'eff <: outer //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/ExamplesInThePaper.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This file contains examples in the paper class ArrayList[A, R] class List[A] class Iter[T, R] fun mkArrayList : [R, T] -> (Region[out R]) ->{R} ArrayList[T, R] fun add : [R, T] -> (ArrayList[T, R], T) ->{R} () fun clear : [R, T] -> ArrayList[T, R] ->{R} () fun foreach : [E, R, T] -> (Iter[T, R], T ->{E} ()) -> {R | E} () fun iter : [Res, R, E extends ~R, T] -> (ArrayList[T, R], [I] -> Iter[T, I] ->{I | E} Res) ->{E | R} Res fun map: [T, S, E] -> (List[out T], T ->{E} S) ->{E} List[out S] fun println: Any -> () fun (;) seq(_, res) = res region r in // This is used to delimit the scope of mutation let xs = mkArrayList(r) // Creates a new mutable list in r egion r add(xs, "1"); add(xs, "2"); add(xs, "3"); iter(xs, it => foreach(it , e => println(e))) // region r in // This is used to delimit the scope of mutation // let xs = mkArrayList(r) // Creates a new mutable list in r egion r // add(xs, "1"); add(xs, "2"); add(xs, "3"); // iter(xs, it => foreach(it , e => println (e); clear (xs))) fun mapi: [A, B, E] -> (List[out A], (Int, A) ->{E} B) ->{E} List[out B] fun mapi(xs, f) = region r in let index = r.ref 0 in map(xs, x => let res = f(!(index), x) in index := !(index) + 1; res) fun f: [R1, R2 extends ~R1] -> (Region[out R1], Region[out R2]) ->{R1 | R2} Int // region r1 in // let g = (r => region r2 in f(r, r2)) in (region r3 in g(r3)) region r1 in fun g: [outer, R extends outer] -> Region[out R] ->{R} Int fun g(r) = region r2 in f(r, r2) region r3 in g(r3) region r in let a1 = mkArrayList(r) in add(a1, 12); add(a1, 34); iter of a1, it1 => region s in let a2 = mkArrayList(s) foreach of it1, v1 => add(a2 , v1) iter of a2, it2 => foreach of it2 , v2 => println(v2) clear(a2) // region r in // let a = mkArrayList(r) in add(a, 12); add(a, 34); // iter of a, it => // foreach of it, v => println(v); clear(a) //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/Exception.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This files includes the exception handling extension of InvalML. class Exc[T] fun (;) seq(_, res) = res fun raise: [T, P extends Exc[T]] -> (P, T) ->{P} Nothing fun hdle: [Res, E, T] -> ([P extends Exc[T]] -> (P) ->{E | P} Res, T ->{E} Res) ->{E} Res fun print: Str -> Unit fun noExc: [E extends ~Exc[in Nothing out Any], Res] -> (() ->{E} Res) ->{E} Res hdle(e => raise(e, "oops!"), msg => print(msg)) // Error! The payload type is incorrect. // hdle(e => raise(e, 42), msg => print(msg)) fun div(x, y) = hdle(e => if y is 0 then raise(e, "div-by-zero!") else x / y, msg => print(msg); 0) hdle(e1 => hdle(e2 => raise(e1, "oops!"), msg => print(msg)), msg => print(msg)) hdle(e1 => hdle(e2 => raise(e2, "oops!"), msg => print(msg)), msg => print(msg)) hdle(e1 => hdle(e2 => raise(e1, "oops!"), msg => raise(e1, msg)), msg => print(msg)) noExc(() => 42) noExc(() => hdle(e => raise(e, "oops!"), msg => print(msg))) // Error! No exception is allowed. // hdle(e => noExc(() => raise(e, "oops!")), msg => print(msg)) //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/README.md ================================================ # Web-Demo ## Syntax Most syntax can be found in the paper. We list the changed and non-mentioned syntax in this documentation. ### ADT Declarations We use the following syntax to declare ADTs: ```fs class List[T] with constructor Nil Cons(x: T, xs: List[out T]) ``` which is equivalent to the syntax used in the paper: ```scala enum List[T]: case Nil case Cons(x: T, xs: List[out T]) ``` ### Functions Keyword `case` is used to create a function that pattern matches on the unique parameter. ```fs fun fact = case 1 then 1 n then n * fact(n - 1) ``` which is equivalent to ```fs fun fact(x) = if x is 1 then 1 n then n * fact(n - 1) ``` Keyword `of` is used for function application to avoid unnecessary parentheses. ```fs fun add(x, y, z) = x + y + z add of 1, 2, 3 ``` which is equivalent to ```fs fun add(x, y, z) = x + y + z add(1, 2, 3) ``` ### Type Annotations Type annotations are written in the following syntax: ```fs [T] -> ... // equivalent to ∀ T. ... [T extends S] -> ... // equivalent to ∀ T {T ≤ S}. ... [T restricts S] -> ... // equivalent to ∀ T {S ≤ T}. ... T | S // equivalent to T ∨ S T & S // equivalent to T ∧ S ~T // equivalent to ¬T ``` ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/SimpleConstraintSolver.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * In this file, we implement a simple constraint solver, originally presented in https://doi.org/10.1145/3410225 // * We only implement int type, function types, and type variables for simplicity // *** infrastructures fun nt(b) = if b then false else true fun (;) seq(_, res) = res fun id(x) = x fun println: Any -> () fun (~) concat: (Str, Str) -> Str fun toString: Any -> Str fun (===) streq: (Str, Str) -> Bool fun error() = error() class PairOf[out A, out B] with constructor Pair(fst: A, snd: B) class Option[out A] with constructor None() Some(value: A) class List[out A] with constructor Nil() Cons(head: A, tail: List[A]) fun fold(x, xs, f) = if xs is Nil() then x Cons(y, ys) then fold(f(x, y), ys, f) fun map(xs, f) = if xs is Nil() then Nil() Cons(x, xs) then Cons(f(x), map(xs, f)) fun each(xs, f) = if xs is Nil() then () Cons(x, xs) then f(x); each(xs, f) fun find(xs, f) = if xs is Nil() then None() Cons(x, xs) then if f(x) then Some(x) else find(xs, f) class ArrayList[T, out R] class Iter[T, out R] class HashMap[K, V, out R] class MapIter[T, out R] // fun empty: [A, R] -> Region[R] ->{R} ArrayList[out A, out R] // TODO investigate: why does this break things? fun empty: [A, R] -> Region[out R] ->{R} ArrayList[A, out R] fun clear: [A, R] -> (ArrayList[A, out R]) ->{R} () fun push: [A, R] -> (ArrayList[A, R], A) ->{R} () fun len: [A, R] -> (ArrayList[A, R]) ->{R} Int fun iter: [Res, R, E extends ~R, T] -> (ArrayList[T, R], [S] -> Iter[T, S] ->{S | E} Res) ->{E | R} Res fun next: [T, S] -> Iter[T, S] ->{S} Option[T] fun whileDo: [R] -> (() ->{R} Bool) ->{R} () fun foreach: [E, R, T] -> (Iter[T, R], T ->{E} ()) ->{R | E} () fun max(x, y) = if x < y then y else x fun mkHashMap: [R, K, V] -> (Region[out R], K -> Str) ->{R} HashMap[K, V, R] fun getOrUpdate: [R, K, V, E] -> (HashMap[K, V, R], K, () ->{E} V) ->{E | R} V fun hasOrUpdate: [R, K, V, E] -> (HashMap[K, V, R], K, () ->{E} V) ->{E | R} () fun iterMap: [Res, R, E extends ~R, K, V] -> (HashMap[K, V, R], [S] -> MapIter[V, S] ->{S | E} Res) ->{E | R} Res fun nextVal: [T, S] -> MapIter[T, S] ->{S} Option[T] fun hasKey: [K, V, R] -> (HashMap[K, V, R], K) ->{R} Bool fun add: [K, V, R] -> (HashMap[K, V, R], K, V) ->{R} () fun values: [E, R, T] -> (MapIter[T, R], T ->{E} ()) ->{R | E} () // *** simple constraint solver // Unif contains a type variable's id, lower bounds, and upper bounds. class Type[out R] with constructor IntType() FunctionType(lhs: Type[R], rhs: Type[R]) RecordType(fields: List[PairOf[Str, Type[R]]]) TypeVariable(id: Str, level: Int, lowerBounds: ArrayList[Type[R], R], upperBounds: ArrayList[Type[R], R]) fun isSimpl(ty) = if ty is FunctionType(_, _) then false else true fun ty2Str(ty) = if ty is IntType() then "Int" FunctionType(lhs, rhs) then let ls = if isSimpl(lhs) then ty2Str(lhs) else "(" ~ ty2Str(lhs) ~ ")" ls ~ " -> " ~ ty2Str(rhs) RecordType(fields) then "{ " ~ fold("", fields, (s, p) => if p is Pair(n, t) then s ~ n ~ ": " ~ ty2Str(t) ~ ", ") ~ "}" TypeVariable(name, level, _, _) then name ~ "_" ~ toString(level) // fun levelOf: [R] -> Type[R] -> Int fun levelOf(ty) = if ty is IntType() then 0 FunctionType(lhs, rhs) then max(levelOf(lhs), levelOf(rhs)) RecordType(fields) then fold(0, fields, (r, p) => if p is Pair(_, t) then max(r, levelOf(t))) TypeVariable(_, level, _, _) then level fun report(lhs, rhs) = println("Cannot constrain " ~ ty2Str(lhs) ~ " <: " ~ ty2Str(rhs) ~ "!") fun extrude: [outer, R extends outer] -> (Type[R], Bool, Int, (Str, Int) ->{R} Type[R], HashMap[PairOf[Type[R], PairOf[Int, Bool]], Type[R], R]) ->{R} Type[R] fun extrude(ty, pol, lvl, freshVar, cache) = getOrUpdate of cache, Pair(ty, Pair(lvl, pol)), () => if levelOf(ty) <= lvl then ty else if ty is IntType() then ty FunctionType(lhs, rhs) then FunctionType(extrude(lhs, nt(pol), lvl, freshVar, cache), extrude(rhs, pol, lvl, freshVar, cache)) RecordType(fields) then RecordType(map(fields, p => if p is Pair(name, ty) then Pair(name, extrude(ty, pol, lvl, freshVar, cache)))) TypeVariable(name, level, lb, ub) then let nc = freshVar(name ~ "'" ~ (if pol then "+" else "-"), lvl) if pol then push(ub, nc) let nlb = if nc is TypeVariable(_, _, lb, _) then lb else error() // impossible region r in let nbd = empty(r) iter of lb, it => foreach(it, b => push(nbd, b)) iter of nbd, it => foreach(it, b => push(nlb, extrude(b, pol, lvl, freshVar, cache))) else push(lb, nc) let nub = if nc is TypeVariable(_, _, _, ub) then ub else error() // impossible region r in let nbd = empty(r) iter of ub, it => foreach(it, b => push(nbd, b)) iter of nbd, it => foreach(it, b => push(nub, extrude(b, pol, lvl, freshVar, cache))) nc // fun solve: [outer, R extends outer] -> (List[PairOf[Type[R], Type[R]]], (Str, Int) ->{R} Type[R], HashMap[PairOf[Type[R], Type[R]], Any, R], () ->{R} HashMap[PairOf[Type[R], PairOf[Int, Bool]], Type[R], R]) ->{R} () fun solve(constraints, freshVar, cache, genExtrCache) = if constraints is Nil() then () Cons(c, cs) then if c is Pair(lhs, rhs) then hasOrUpdate of cache, c, () => if lhs is IntType() then if rhs is IntType() then solve(cs, freshVar, cache, genExtrCache) TypeVariable(name, level, lb, ub) then push(lb, lhs) region r in let ncs = r.ref cs iter(ub, it => foreach(it, b => ncs := Cons(Pair(lhs, b), !(ncs)); ())) solve(!(ncs), freshVar, cache, genExtrCache) else report(lhs, rhs) FunctionType(arg, res) then if rhs is FunctionType(arg', res') then solve(Cons(Pair(arg', arg), Cons(Pair(res, res'), cs)), freshVar, cache, genExtrCache) TypeVariable(name, level, lb, ub) then if levelOf(lhs) <= level then push(lb, lhs) region r in let ncs = r.ref cs iter(ub, it => foreach(it, b => ncs := Cons(Pair(lhs, b), !(ncs)); ())) solve(!(ncs), freshVar, cache, genExtrCache) else let lhs' = extrude(lhs, true, level, freshVar, genExtrCache()) solve(Cons(Pair(lhs', rhs), cs), freshVar, cache, genExtrCache) else report(lhs, rhs) RecordType(flds) then if rhs is RecordType(flds') then each(flds', p' => if p' is Pair(n', t') then if find(flds, p => if p is Pair(n, t) then n === n') is Some(p) then if p is Pair(n, t) then solve(Cons(Pair(t, t'), cs), freshVar, cache, genExtrCache) None() then println("Missing field " ~ n' ~ " in " ~ ty2Str(lhs)) ) TypeVariable(name, level, lb, ub) then if levelOf(lhs) <= level then push(lb, lhs) region r in let ncs = r.ref cs iter(ub, it => foreach(it, b => ncs := Cons(Pair(lhs, b), !(ncs)); ())) solve(!(ncs), freshVar, cache, genExtrCache) else let lhs' = extrude(lhs, true, level, freshVar, genExtrCache()) solve(Cons(Pair(lhs', rhs), cs), freshVar, cache, genExtrCache) else report(lhs, rhs) TypeVariable(name, level, lb, ub) then if levelOf(rhs) <= level then push(ub, rhs) region r in let ncs = r.ref cs iter(lb, it => foreach(it, b => ncs := Cons(Pair(b, rhs), !(ncs)); ())) solve(!(ncs), freshVar, cache, genExtrCache) else let rhs' = extrude(rhs, false, level, freshVar, genExtrCache()) solve(Cons(Pair(lhs, rhs'), cs), freshVar, cache, genExtrCache) fun freshVar(r, ctx, name, lvl) = if (nt of hasKey(ctx, name)) then add(ctx, name, 1) TypeVariable(name, lvl, empty(r), empty(r)) else region r2 in let i = r2.ref 0 let res = r2.ref None() whileDo of () => let nn = name ~ toString(!(i)) if (nt of hasKey(ctx, nn)) then res := Some(TypeVariable(nn, lvl, empty(r), empty(r))) add(ctx, nn, 1) false else i := !(i) + 1 true if !(res) is Some(v) then v else error() fun genHash(r) = mkHashMap(r, p => if p is Pair(x, y) then ty2Str(x) ~ " <: " ~ ty2Str(y)) fun genExtrHash(r) = () => mkHashMap(r, t => if t is Pair(ty, p) then if p is Pair(lvl, pol) then ty2Str(ty) ~ (if pol then "+_" else "-_") ~ toString(lvl)) fun printBounds(tv) = if tv is TypeVariable(name, level, lb, ub) then iter of lb, it => foreach of it, b => println(" " ~ ty2Str(b) ~ " <: " ~ ty2Str(tv)) iter of ub, it => foreach of it, b => println(" " ~ ty2Str(tv) ~ " <: " ~ ty2Str(b)) else () // fun printRes: [outer, R extends outer] -> (Type[R], Type[R]) ->{R} () fun printRes(lhs, rhs) = println(ty2Str(lhs) ~ " <: " ~ ty2Str(rhs)) region r in let tvs = mkHashMap(r, s => s) // fun go: [outer, R extends outer, S extends outer] -> (Type[R], HashMap[Str, Type[R], S]) ->{R | S} () fun go(t, tvs) = if t is IntType() then () FunctionType(lhs, rhs) then go(lhs, tvs); go(rhs, tvs) RecordType(fields) then each(fields, p => if p is Pair(_, t) then go(t, tvs)) TypeVariable(name, level, lb, ub) then getOrUpdate of tvs, name ~ toString(level), () => region r2 in let tmp = empty(r2) iter of lb, it => foreach(it, ty => push(tmp, ty)) iter of ub, it => foreach(it, ty => push(tmp, ty)) t () go(lhs, tvs); go(rhs, tvs) println("where: ") iterMap of tvs, it => values of it, printBounds // *** error example // if we call the `solve` function during the iteration, the `solve` function // can only handle the cases where no allocation is required (i.e., no type variables) // fun TODO() = TODO() // fun wrongSolve(constraints, freshVar, cache, genExtrCache) = if constraints is // Nil() then () // Cons(c, cs) then if c is // Pair(lhs, rhs) then hasOrUpdate of cache, c, () => // if lhs is // IntType() then TODO() // FunctionType(arg, res) then TODO() // RecordType(flds) then TODO() // TypeVariable(name, level, lb, ub) then // if levelOf(rhs) <= level then // push(ub, rhs) // iter(lb, it => foreach(it, b => solve(Cons(Pair(b, rhs), Nil()), freshVar, cache, genExtrCache))) // solve(cs, freshVar, cache, genExtrCache) // else // let rhs' = extrude(rhs, false, level, freshVar, genExtrCache()) // solve(Cons(Pair(lhs, rhs'), cs), freshVar, cache, genExtrCache) // The error' version of `solve` function can only be used for constraints without type variables // wrongSolve(Cons(Pair(IntType, IntType), Nil())) // Calling the `wrongSolve` function to solve constraints involving type variables will lead to type errors // region r in // let ctx = mkHashMap(r, s => s) // let a = freshVar(r, ctx, "a", 1) // let b = freshVar(r, ctx, "a", 1) // let lhs = a // let rhs = b // wrongSolve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) // printRes(lhs, rhs) // *** examples region r in let ctx = mkHashMap(r, s => s) let lhs = FunctionType(IntType(), IntType()) let rhs = IntType() solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) region r in let ctx = mkHashMap(r, s => s) let lhs = FunctionType(IntType(), IntType()) let a = freshVar(r, ctx, "a", 1) let rhs = FunctionType(a, a) solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) region r in let ctx = mkHashMap(r, s => s) let lhs = RecordType(Cons(Pair("a", IntType()), Cons(Pair("b", FunctionType(IntType(), IntType())), Nil()))) let a = freshVar(r, ctx, "a", 1) let b = freshVar(r, ctx, "b", 1) let rhs = RecordType(Cons(Pair("a", a), Cons(Pair("b", b), Nil()))) solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) region r in let ctx = mkHashMap(r, s => s) let a = freshVar(r, ctx, "a", 1) let b = freshVar(r, ctx, "b", 1) let lhs = a let rhs = b solve(Cons(Pair(lhs, rhs), Cons(Pair(rhs, lhs), Nil())), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) printRes(rhs, lhs) region r in let ctx = mkHashMap(r, s => s) let lhs = RecordType(Cons(Pair("a", IntType()), Cons(Pair("b", FunctionType(IntType(), IntType())), Nil()))) let b = freshVar(r, ctx, "b", 1) let rhs = RecordType(Cons(Pair("b", b), Nil())) solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) region r in let ctx = mkHashMap(r, s => s) let a = freshVar(r, ctx, "a", 1) let b = freshVar(r, ctx, "b", 1) let lhs = FunctionType(IntType(), a) let rhs = FunctionType(IntType(), b) if a is TypeVariable(_, _, _, ub) then push(ub, lhs) else error() // impossible if b is TypeVariable(_, _, lb, _) then push(lb, rhs) else error() // impossible solve(Cons(Pair(a, b), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(a, b) region r in let ctx = mkHashMap(r, s => s) let a = freshVar(r, ctx, "a", 1) let lhs = a let rhs = FunctionType(IntType(), IntType()) if a is TypeVariable(_, _, lb, ub) then push(lb, IntType()) else error() // impossible solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) region r in let ctx = mkHashMap(r, s => s) let a = freshVar(r, ctx, "a", 1) let b = freshVar(r, ctx, "b", 2) let lhs = FunctionType(IntType(), a) let rhs = FunctionType(IntType(), b) solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) region r in let ctx = mkHashMap(r, s => s) let a = freshVar(r, ctx, "a", 1) let b = freshVar(r, ctx, "a", 1) let lhs = a let rhs = b solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) region r in let ctx = mkHashMap(r, s => s) let a = freshVar(r, ctx, "a", 1) let b = freshVar(r, ctx, "b", 2) let lhs = a let rhs = FunctionType(b, FunctionType(b, b)) solve(Cons(Pair(lhs, rhs), Nil()), (n, l) => freshVar(r, ctx, n, l), genHash(r), genExtrHash(r)) printRes(lhs, rhs) //│ ———————————————————————————————————————————————————————————————————————————————— // ––––––––– TO IMPROVE LATER ––––––––– fun test(x) = case IntType() then () FunctionType(l1, r1) then () RecordType(f) then () TypeVariable(id, lvl, lbs, ubs) then clear(ubs) // FIXME: why is this not simplified more? (((Type[out 'TypeVariable] ∧ Type[?]) ∧ Type[?]) ∧ Type[?]) test //│ Type: ['TypeVariable] -> ⊤ -> ((((Type[out 'TypeVariable] ∧ Type[?]) ∧ Type[?]) ∧ Type[?]) ->{'TypeVariable} ()) ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/StackMM.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This files includes the stack-based memory management extension of InvalML. class Stack[S, R] class StackRef[T, R] fun (;) seq(_, res) = res fun allocStack: [E, Res] -> ([S, R] -> Stack[S, R] ->{S | R | E} Res) ->{E} Res fun alloc: [S, R, A] -> (Stack[S, R], A) ->{S} StackRef[A, R] fun read: [R, A] -> StackRef[A, R] ->{R} A fun write: [R, A] -> (StackRef[A, R], A) ->{R} () fun push: [Res, S, R, E extends ~S] -> (Stack[S, R], [U] -> Stack[U, R] -> {U | R | E} Res) ->{E | R | S} Res allocStack of s => let r1 = alloc(s, 42) let r2 = alloc(s, 0) write(r1, 1) read(r2) allocStack of s1 => let r1 = alloc(s1, 42) let r2 = alloc(s1, 0) write(r1, 1) read(r2) push of s1, s2 => let r3 = alloc(s2, 0) in read(r3) // Error! After pusing `s1`, one can not allocate references on it. // allocStack of s1 => // let r1 = alloc(s1, 42) // let r2 = alloc(s1, 0) // write(r1, 1) // read(r2) // push of s1, s2 => // let r3 = alloc(s1, 0) in read(r3) //│ Type: Int //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/Staging.mls ================================================ :global :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This files includes the metaprogramming extension of InvalML. // Type `CodeBase[T, R, S]`: // T: type of quoted expression // R: union of free variables' skolems // S: can be either top or bot. `CodeBase[T, R, bot]` is equivalent to `Var[T, R]`; `CodeBase[T, R, top]` is equivalent to `Code[T, R]`. fun power: [C] -> CodeBase[out Num, out C, out Any] -> Int -> CodeBase[out Num, out C, out Any] fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) fun id: [A] -> A -> A fun id(x) = x run(x `=> id(x) `* x) fun assertNotZero: [C] -> CodeBase[out Num, out C, out Any] -> CodeBase[out Num, out C, out Any] fun assertNotZero(x) = `if (x `== `0.0) then `error else x let checkedDiv = x `=> y `=> x `/. (assertNotZero(y)) run(checkedDiv) fun show: [T] -> CodeBase[out T, out Any, out Any] -> Str = s => "debug" fun inc(dbg) = x `=> let c = x `+ `1 in let t = dbg(c) in c inc(c => log(show(c))) fun body_naive: [T, C] -> (CodeBase[out Int, out T, out Any], CodeBase[out Int, out C, out Any]) -> Int -> CodeBase[out Int, out T | C, out Any] fun body_naive(x, y) = case 0 then x 1 then y n then body_naive(y, x `+ y)(n - 1) fun gib_naive(n) = (x, y) `=> body_naive(x, y)(n) let gn5 = run(gib_naive(5)) // Wrong version! There is an unexpected extrusion in `bind`, making `bind` unusable. // fun bind(rhs, k) = `let x = rhs `in k(x) // fun body: [G] -> (CodeBase[out Int, out G, out Any], CodeBase[out Int, out G, out Any]) -> Int -> CodeBase[out Int, out G, out Any] // fun body(x, y) = case // 0 then x // 1 then y // n then bind of x `+ y, (z => body(y, z)(n - 1)) fun bind: [G] -> (CodeBase[out Int, out G, out Any], [C] -> CodeBase[out Int, out C, out Any] -> CodeBase[out Int, out C | G, out Any]) -> CodeBase[out Int, out G, out Any] fun bind(rhs, k) = `let x = rhs `in k(x) fun body: [G] -> (CodeBase[out Int, out G, out Any], CodeBase[out Int, out G, out Any]) -> Int -> CodeBase[out Int, out G, out Any] fun body(x, y) = case 0 then x 1 then y n then bind of x `+ y, (z => body(y, z)(n - 1)) fun gib(n) = (x, y) `=> body(x, y)(n) let g5 = run(gib(5)) g5 //│ Type: (Int, Int) -> Int //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/flix/GUI.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This file includes the type checking implementation for the GUI example // * adapted from https://doi.org/10.5281/zenodo.7990289 class Block class IO class Label class Button // * So far, our system does not support impure programs. // * We explicitly insert this handle function at **the top level** to allow primitive effects like IO and Block fun doPrimitiveEffects: [Res, E] -> (() ->{E | IO | Block} Res) ->{E} Res fun sleep: (Int) ->{Block} () fun mkLabel: (Str) ->{IO} Label fun mkButton: (Str) ->{IO} Button fun setText: (Str, Label) ->{IO} () // The callback function should not block the execution // so `E` has an upper bound `~Block` fun addActionListener: [T, E extends ~Block] -> (() ->{E} (), Button) ->{E | T} () // examples doPrimitiveEffects of () => sleep(42) doPrimitiveEffects of () => mkLabel("Hello, World!") doPrimitiveEffects of () => let label = mkLabel("Hello, World!") sleep(42) setText("Goodbye, World!", label) // ok! `setText` will not block the execution doPrimitiveEffects of () => let label = mkLabel("label") let button = mkButton("button") addActionListener(() => setText("clicked!", label), button) // error! `sleep` will block the execution and the compiler reject the following program // doPrimitiveEffects of () => // let label = mkLabel("label") // let button = mkButton("button") // addActionListener(() => sleep(1), button) //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/flix/Interpreter.mls ================================================ :global :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This file includes the implementation for an interpreter and a compiler for a small toy programming language. // * Adapted from https://doi.org/10.5281/zenodo.7990289 class List[T] with constructor Nil Cons(x: T, xs: List[out T]) fun concat: [T] -> (List[out T], List[out T]) -> List[out T] fun (;) seq(_, res) = res fun error: Nothing // * Arithmetical Expression class AExp with constructor Cst(v: Int) Plus(lhs: AExp, rhs: AExp) Minus(lhs: AExp, rhs: AExp) Times(lhs: AExp, rhs: AExp) IfThenElse(cond: BExp, cons: AExp, alts: AExp) // * Boolean Expression class BExp with constructor True False Not(v: BExp) Conj(lhs: BExp, rhs: BExp) Disj(lhs: BExp, rhs: BExp) Eq(lhs: AExp, rhs: AExp) Neq(lhs: AExp, rhs: AExp) // * Evaliation functions fun evalBExp: BExp -> Bool fun evalAExp(e) = if e is Cst(v) then v Plus(e1, e2) then evalAExp(e1) + evalAExp(e2) Minus(e1, e2) then evalAExp(e1) - evalAExp(e2) Times(e1, e2) then evalAExp(e1) * evalAExp(e2) IfThenElse(cond, e1, e2) then if evalBExp(cond) then evalAExp(e1) else evalAExp(e2) fun evalBExp(e) = if e is True then true False then false Not(e) then evalBExp(e) is false Conj(e1, e2) then evalBExp(e1) && evalBExp(e2) Disj(e1, e2) then evalBExp(e1) || evalBExp(e2) Eq(e1, e2) then evalAExp(e1) == evalAExp(e2) Neq(e1, e2) then evalAExp(e1) != evalAExp(e2) // * Examples evalAExp(Cst(42)) evalAExp(Plus(Cst(42), Cst(21))) evalAExp(Minus(Cst(42), Cst(21))) evalAExp(IfThenElse(True, Cst(1), Cst(2))) evalAExp(IfThenElse(Neq(Cst(1), Cst(2)), Cst(42), Cst(21))) evalBExp(True) evalBExp(Not(True)) evalBExp(Conj(True, False)) evalBExp(Disj(True, False)) evalBExp(Neq(Cst(1), Cst(2))) // * Instruction class Inst with constructor Push(v: Int) Add Sub Mul Neg And Or Cmp Branch(cons: List[out Inst], alts: List[out Inst]) // * Compilation functions fun compileBExp: BExp -> List[out Inst] fun compileAExp(e) = if e is Cst(v) then Cons(Push(v), Nil) Plus(e1, e2) then let is1 = compileAExp(e1) in let is2 = compileAExp(e2) in concat(concat(is2, is1), Cons(Add, Nil)) Minus(e1, e2) then let is1 = compileAExp(e1) in let is2 = compileAExp(e2) in concat(concat(is2, is1), Cons(Sub, Nil)) Times(e1, e2) then let is1 = compileAExp(e1) in let is2 = compileAExp(e2) in concat(concat(is2, is1), Cons(Mul, Nil)) IfThenElse(e1, e2, e3) then let is1 = compileBExp(e1) in let is2 = compileAExp(e2) in let is3 = compileAExp(e3) in concat(is1, Cons(Branch(is2, is3), Nil)) fun compileBExp(e) = if e is True then Cons(Push(1), Nil) False then Cons(Push(0), Nil) Not(e) then concat(compileBExp(e), Cons(Neg, Nil)) Conj(e1, e2) then let is1 = compileBExp(e1) in let is2 = compileBExp(e2) in concat(concat(is2, is1), Cons(And, Nil)) Disj(e1, e2) then let is1 = compileBExp(e1) in let is2 = compileBExp(e2) in concat(concat(is2, is1), Cons(Or, Nil)) Eq(e1, e2) then let is1 = compileAExp(e1) in let is2 = compileAExp(e2) in concat(concat(is2, is1), Cons(Cmp, Nil)) Neq(e1, e2) then let is1 = compileAExp(e1) in let is2 = compileAExp(e2) in concat(concat(is2, is1), Cons(Neg, Cons(Cmp, Nil))) // * Examples compileAExp(Cst(42)) compileAExp(Plus(Cst(42), Cst(21))) compileAExp(Minus(Cst(42), Cst(21))) compileAExp(IfThenElse(True, Cst(1), Cst(2))) compileAExp(IfThenElse(Neq(Cst(1), Cst(2)), Cst(42), Cst(21))) compileBExp(True) compileBExp(Not(True)) compileBExp(Conj(True, False)) compileBExp(Disj(True, False)) compileBExp(Neq(Cst(1), Cst(2))) // So far, we have not supported nested patterns. // This helper function is for the following pattern matching: // `if lst is Cons(x, Cons(y, ys)) then f(x, y, ys) else g()` fun matchTwo(lst, f, g) = if lst is Nil then g() Cons(x, xs) then if xs is Nil then g() Cons(y, ys) then f(x, y, ys) // * Instruction evaluation fun evalInst(insts, stack) = if insts is Nil then if stack is Cons(x, xs) then if xs is Nil then x Cons(_, _) then error Nil then error Cons(inst, rest) then if inst is Push(i) then evalInst(rest, Cons(i, stack)) Add then matchTwo(stack, (x, y, r) => evalInst(rest, Cons(x + y, r)), () => error) Sub then matchTwo(stack, (x, y, r) => evalInst(rest, Cons(x - y, r)), () => error) Mul then matchTwo(stack, (x, y, r) => evalInst(rest, Cons(x * y, r)), () => error) Neg then if stack is Nil then error Cons(x, xs) then evalInst(rest, Cons(if x == 0 then 1 else 0, xs)) And then matchTwo(stack, (x, y, r) => evalInst(rest, Cons(if x != 0 && y != 0 then 1 else 0, r)), () => error) Or then matchTwo(stack, (x, y, r) => evalInst(rest, Cons(if x != 0 || y != 0 then 1 else 0, r)), () => error) Cmp then matchTwo(stack, (x, y, r) => evalInst(rest, Cons(if x == y then 1 else 0, r)), () => error) Branch(is1, is2) then if stack is Nil then error Cons(x, xs) then if x != 0 then evalInst(is1, xs) else evalInst(is2, xs) // * Examples evalInst(compileAExp(Cst(42)), Nil) evalInst(compileAExp(Plus(Cst(42), Cst(21))), Nil) evalInst(compileAExp(Minus(Cst(42), Cst(21))), Nil) evalInst(compileAExp(IfThenElse(True, Cst(1), Cst(2))), Nil) evalInst(compileAExp(IfThenElse(Neq(Cst(1), Cst(2)), Cst(42), Cst(21))), Nil) evalInst(compileAExp(IfThenElse(Eq(Cst(1), Cst(2)), Cst(42), Times(Cst(21), Cst(82)))), Nil) //│ Type: Int //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/invalml/web-demos/reml/MergeSort.mls ================================================ :invalml :... //│ ———————————————————————————————————————————————————————————————————————————————— // * This file includes the implementation for merge sorting, // * executed sequentially or in parallel. // * Adapted from https://doi.org/10.5281/zenodo.8425443 // *** infrastructures declare class Array[T, R] class Pair[T, S] with constructor Pr(fst: T, snd: S) fun mkArray: [T, R] -> Region[out R] ->{R} Array[T, out R] push: [T, R] -> (Array[in T, out R], T) ->{R} () unshift: [T, R] -> (Array[in T, out R], T) ->{R} () map: [T, S, R, E extends ~R] -> (Array[out T, out R], T ->{E} S) ->{E | R} Array[S, out R] foreach: [T, R, E extends ~R] -> (Array[out T, out R], T ->{E} ()) ->{E | R} () len: [R] -> Array[in Nothing out Any, out R] ->{R} Int at: [T, R] -> (Array[out T, out R], Int) ->{R} T slice: [T, R] -> (Array[out T, out R], Int) ->{R} Array[out T, out R] concat: [T, R1, R2, R3] -> (Region[out R3], Array[in T, out R1], Array[out T, out R2]) ->{R1 | R2 | R3} Array[T, out R3] // Matching on js array is not supported yet. // This helper function is equivalent to `if xs is Cons(x, xs) then fh(x, xs) else fn(xs)` fun matchOne(xs, fn, fh) = if len(xs) is 0 then fn(xs) else fh(at(xs, 0), slice(xs, 1)) // Matching on js array is not supported yet. // This helper function is equivalent to // `if xs is Cons(x, Cons(y, ys)) then f2(x, y, ys) else if xs is Cons(x, xs) then f1(x, xs) else fn(xs)` // fun matchTwo: [T, R, E1, E2, E3, S] -> (Array[out T, out R], Array[out T, out R] ->{E1} S, T ->{E2} S, (T, T, Array[out T, out R]) ->{E3} S) ->{E1 | E2 | E3 | R} S fun matchTwo(xs, fn, f1, f2) = if len(xs) is 0 then fn(xs) else if len(xs) is 1 then f1(at(xs, 0)) else f2(at(xs, 0), at(xs, 1), slice(xs, 2)) fun (;) seq(_, res) = res fun println: Any -> () fun forkJoin: [T, S, P, E1, E2 extends ~E1, E3] -> (() ->{E1} T, () ->{E2} S, (T, S) ->{E3} P) ->{E1 | E2 | E3} P // *** pmsort implementation // Split the given array into two sub-arrays. // Region r1 and r2 indicate where the two sub-arrays should be stored. fun split(xs, r1, r2) = fun rs(xs, ys, zs) = matchTwo of xs, _ => Pr(ys, zs), x => push(ys, x); Pr(ys, zs), (x1, x2, r) => push(ys, x1); push(zs, x2); rs(r, ys, zs) rs(xs, mkArray(r1), mkArray(r2)) // Merge arr1 and arr2. The result is stored in region r. // fun merge: [T, R1, R2, R] -> (Array[out T, out R1], Array[out T, out R2], Region[R]) ->{R1 | R2 | R} Array[T, out R] fun merge(arr1, arr2, r) = fun recm(xs, ys, acc) = matchOne of xs, _ => concat(r, acc, ys), (x, rx) => matchOne of ys, _ => concat(r, acc, xs), (y, ry) => if x < y then push(acc, x); recm(rx, ys, acc) else push(acc, y); recm(xs, ry, acc) recm(arr1, arr2, mkArray(r)) // Merge sort in single thread. fun smsort(xs, r) = matchTwo of xs, _ => mkArray(r), x => let res = mkArray(r) in push(res, x); res, (_, _, _) => let p = split(xs, r, r) in if p is Pr(fst, snd) then merge(smsort(fst, r), smsort(snd, r), r) // Wrong version! Sub-arrays cannot be stored in the same region! // fun pmsort: [R1, R2] -> (Array[out Int, out R1], Region[R2]) ->{R1 | R2} Array[Int, out R2] // fun pmsort(xs, r) = // matchTwo of xs, _ => mkArray(r), x => let res = mkArray(r) in push(res, x); res, (_, _, _) => // let p = split(xs, r, r) in // if p is Pr(fst, snd) then // forkJoin(_ => pmsort(fst, r), pmsort(snd, r), (r1, r2) => merge(r1, r2, r)) // Merge sort in parallel. fun pmsort: [R1, R2] -> (Array[out Int, out R1], Region[out R2]) ->{R1 | R2} Array[Int, out R2] fun pmsort(xs, r) = matchTwo of xs, _ => mkArray(r), x => let res = mkArray(r) in push(res, x); res, (_, _, _) => region r1 in region r2 in let p = split(xs, r1, r2) in if p is Pr(fst, snd) then forkJoin(() => pmsort(fst, r1), () => pmsort(snd, r2), (rx, ry) => merge(rx, ry, r)) // *** simple tests region r in let arr = mkArray(r) push(arr, 10) push(arr, 23) push(arr, 13) push(arr, 4) smsort(arr, r) region r in let arr = mkArray(r) push(arr, 10) push(arr, 23) push(arr, 13) push(arr, 4) pmsort(arr, r) //│ Type: Array[Int, ?] //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls ================================================ :lift :js :noInline // :deadParamElim off // used to be `off` because lifter changes the parameter lists and DPE used to look at the original ones // This test was previously used to demonstrate immutable captures names are not properly discriminated, // but handler no longer occur before lifting so this now yield the correct result. :effectHandlers :expect 3 abstract class Effect with fun perform() handle h = Effect with fun perform()(k) = k() fun f() = h.perform() 1 f() + f() + f() //│ = 3 :expect 1 fun f(x) = class Test() with fun get() = x Test() f(1).get() //│ = 1 :expect 1 fun f(used1, unused1) = fun g(g_arg) = let used3 = 2 fun h = used3 used1 + h let unused2 = 2 data class Test(a) with fun get() = used1 Test(unused1) f(1, 2).get() //│ = 1 :expect 1 fun f(used1, unused1) = fun g(g_arg) = let used3 = 2 fun h = used3 used1 + h let unused2 = 2 class Test(a) with fun get() = used1 new Test(unused1) f(1, 2).get() //│ = 1 :expect 1 fun f(used1, unused1) = fun g(g_arg) = let used3 = 2 fun h = used3 used1 + h let unused2 = 2 class Test with fun get() = used1 new Test f(1, 2).get() //│ = 1 :expect 2 fun f(x) = class A() with fun f() = set x = 2 A().f() x f(1) //│ = 2 :sir fun f(x) = class C with fun y = x new C //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let C⁰, f⁰; //│ define C⁰ as class C¹ { //│ private val x⁰; //│ constructor(x) { //│ set x⁰ = x; //│ end //│ } //│ method y⁰ = fun y¹ { return x⁰ } //│ }; //│ define f⁰ as fun f¹(x) { return new C¹(x) }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // only w should be in a capture :expect 10111 :sir fun f() = let x = 1 let y = 10 let z = 10 let w = 1000 class Good() with fun foo() = set z = 100 x + y + z + w class Bad() with fun foo() = set w = 10000 Bad().foo() Good() f().foo() //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let Bad⁰, Good⁰, f², tmp, Capture$scope0⁰; //│ define Capture$scope0⁰ as class Capture$scope0² { //│ constructor Capture$scope0¹(z$0, w$1) { //│ define w$1⁰ as val w$1¹ = w$1; //│ define z$0⁰ as val z$0¹ = z$0; //│ end //│ } //│ }; //│ define Good⁰ as class Good²() { //│ private val scope0$cap⁰; //│ private val x¹; //│ private val y²; //│ constructor Good¹(scope0$cap, x, y) { //│ set scope0$cap⁰ = scope0$cap; //│ set x¹ = x; //│ set y² = y; //│ end //│ } //│ method foo⁰ = fun foo¹() { //│ let tmp1, tmp2; //│ set scope0$cap⁰.z$0 = 100; //│ set tmp1 = +⁰(x¹, y²); //│ set tmp2 = +⁰(tmp1, scope0$cap⁰.z$0¹); //│ return +⁰(tmp2, scope0$cap⁰.w$1¹) //│ } //│ }; //│ define Bad⁰ as class Bad²() { //│ private val scope0$cap¹; //│ constructor Bad¹(scope0$cap) { //│ set scope0$cap¹ = scope0$cap; //│ end //│ } //│ method foo² = fun foo³() { //│ set scope0$cap¹.w$1 = 10000; //│ return runtime⁰.Unit⁰ //│ } //│ }; //│ define f² as fun f³() { //│ let x, y, z, w, tmp1, scope0$cap; //│ set scope0$cap = new mut Capture$scope0²(z, w); //│ set x = 1; //│ set y = 10; //│ set scope0$cap.z$0 = 10; //│ set scope0$cap.w$1 = 1000; //│ set tmp1 = new Bad²()(scope0$cap); //│ do tmp1.foo³(); //│ return new Good²()(scope0$cap, x, y) //│ }; //│ set tmp = f³(); //│ tmp.foo﹖() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 10111 // handler :stackSafe 6 :effectHandlers fun sum(n) = if n == 0 then 0 else n + sum(n - 1) sum(100) //│ = 5050 // instance checks fun f() = data class Test(a) with fun get() = a fun foo() = Test foo()(0) f().get() //│ = 0 // Parameterized class constructor used as a first-class function :expect 43 fun foo2(x) = class C(y) with fun get = x + y [C, set x += 1] let res = foo2(40) res.0(2).get //│ = 43 //│ res = [fun, ()] :lift :w :expect 2 fun f(x) = class Test with fun get() = fun h() = x h() let foo = Test foo (new! (f(2))).get() //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 2 :expect 2 fun test() = class A with fun get = 2 class B() extends A B().get test() //│ = 2 // Note: semicolon required for auxparam before `return this;` // :sir fun test(f) = class A(x) with f(x) A(1) // Check that the extra params are not printed :expect "A(2)" fun f(x) = data class A(y) with fun hi = x + y A(2).toString() f(0) //│ = "A(2)" :todo fun f(x) = class A(...r) with fun a = r.push_back(x) fun b = r new A(1, 2, 3) let res = f(0) res.a res.b //│ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: mismatched param list lengths List() vs List(term:A/r) //│ ╟── The compilation result may be incorrect. //│ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. //│ ═══[RUNTIME ERROR] ReferenceError: r is not defined //│ res = A(_) // *** Mutability *** // * Trivial case :sir :noSanityCheck fun main() = class C(val a) let c = new mut C(123) set c.a = 0 set c.b = "hi" let d = new C(456) set d.a = 0 let e = C(789) set e.a = 0 [c.a, c.b, d.a, e.a] //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let C², main⁰; //│ define C² as class C⁴(a) { //│ val a⁰; //│ constructor C³ { //│ define a⁰ as val a¹ = a; //│ end //│ } //│ }; //│ define main⁰ as fun main¹() { //│ let c, d, e; //│ set c = new mut C⁴(123); //│ set c.a = 0; //│ set c.b = "hi"; //│ set d = new C⁴(456); //│ set d.a = 0; //│ set e = C³(789); //│ set e.a = 0; //│ return [c.a﹖, c.b﹖, d.a﹖, e.a﹖] //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Notice that the lifted class above behaves the same as the equivalent top-level class below: class C(val a) let c = new mut C(123) set c.a = 0 set c.b = "hi" let d = new C(456) set d.a = 0 let e = C(789) set e.a = 0 //│ c = C(0) //│ d = C(456) //│ e = C(789) :expect [0, "hi", 456, 789] [c.a, c.b, d.a, e.a] //│ = [0, "hi", 456, 789] // * Nontrivial case :sir :noSanityCheck fun main(x) = class C(val a) with fun get = x + a let c = new mut C(123) set c.a = 0 set c.b = "hi" let d = new C(456) set d.a = 0 let e = C(789) set e.a = 0 [c.a, c.b, d.a, e.a, c.get, d.get, e.get] //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let C⁵, main²; //│ define C⁵ as class C⁷(a) { //│ private val x²; //│ val a²; //│ constructor C⁶(x) { //│ set x² = x; //│ define a² as val a³ = a; //│ end //│ } //│ method get⁰ = fun get¹ { //│ return +⁰(x², C⁷.this.a³) //│ } //│ }; //│ define main² as fun main³(x) { //│ let c, d, e; //│ set c = new mut C⁷(123)(x); //│ set c.a = 0; //│ set c.b = "hi"; //│ set d = new C⁷(456)(x); //│ set d.a = 0; //│ set e = new C⁷(789)(x); //│ set e.a = 0; //│ return [c.a﹖, c.b﹖, d.a﹖, e.a﹖, c.get﹖, d.get﹖, e.get﹖] //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect [0, "hi", 456, 789, 42, 498, 831] main(42) //│ = [0, "hi", 456, 789, 42, 498, 831] // * The lifted class above should behave the same as the equivalent top-level class below: let x = id(42) class C(val a) with fun get = x + a //│ x = 42 let c = new mut C(123) set c.a = 0 set c.b = "hi" let d = new C(456) set d.a = 0 let e = C(789) set e.a = 0 //│ c = C(0) //│ d = C(456) //│ e = C(789) :expect [0, "hi", 456, 789, 42, 498, 831] [c.a, c.b, d.a, e.a, c.get, d.get, e.get] //│ = [0, "hi", 456, 789, 42, 498, 831] // This used to raise a softTODO in DPE // The error is because the type tree for classes store parameter list arity information, // but the lifter modifies this information, and the equality is enforced by the middle-end // flow analysis, which is used by dead param elimination. It would be a pain to change all the // class symbols because they could be nested in other symbols at an arbitrary depth. // :fixme :deadParamElim fun f(x) = class A(y) with fun get = x + y new A(2) ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls ================================================ :lift :js :w fun foo(x) = class C(y) with fun get = [x, y] module C with val empty = C(123) C.empty //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.9: module C with //│ ╙── ^ foo(10).get //│ = [10, 123] :w fun foo(x) = class C with fun get = x module C with val empty = new C C.empty //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.23: module C with //│ ╙── ^ foo(10).get //│ = 10 ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls ================================================ :lift :js :w :sir fun f(x) = class A with fun get = x module A fun g = new A //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.10: module A //│ ╙── ^ //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g⁰, f⁰; //│ define g⁰ as fun g¹(A$) { //│ return new A$() //│ }; //│ define f⁰ as fun f¹(x) { //│ let A; //│ define A as class A⁰ { //│ method get⁰ = fun get¹ { return x } //│ } //│ module A¹; //│ return runtime⁰.Unit⁰ //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/CurriedClassInFun.mls ================================================ :lift :js :expect 2 fun f(x) = class A()() with fun newA() = A()() fun foo() = set x += 1 x fun getX() = x A()() let a = f(0) a.foo() let b = a.newA() b.foo() a.getX() //│ = 2 //│ a = A() //│ b = A() // * Notice how the capture parameter `f$cap` is inserted before the aux param `z` but after the main param `y` :soir fun f(x) = class A(val y)(val z) with fun newA() = new A(y + 1)(z + 1) fun foo() = set x += 1 x fun getX() = x [new A(x + 1)(x + 2), A, A(x + 1), A(x + 1)(x + 2)] //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let A⁰, f⁰, Capture$f⁰, A$⁰; //│ define A$⁰ as fun A$¹(f$cap)(y)(z) { //│ return new A¹(y)(f$cap)(z) //│ }; //│ define A⁰ as class A¹(y) { //│ private val f$cap⁰; //│ val y⁰; //│ val z⁰; //│ constructor A²(f$cap)(z) { //│ set f$cap⁰ = f$cap; //│ define y⁰ as val y¹ = y; //│ define z⁰ as val z¹ = z; //│ end //│ } //│ method newA⁰ = fun newA¹() { //│ let tmp, tmp1; //│ set tmp = +⁰(A¹.this.y¹, 1); //│ set tmp1 = +⁰(A¹.this.z¹, 1); //│ return new A¹(tmp)(f$cap⁰)(tmp1) //│ } //│ method foo⁰ = fun foo¹() { //│ let tmp; //│ set tmp = +⁰(f$cap⁰.x$0⁰, 1); //│ set f$cap⁰.x$0 = tmp; //│ return f$cap⁰.x$0⁰ //│ } //│ method getX⁰ = fun getX¹() { //│ return f$cap⁰.x$0⁰ //│ } //│ }; //│ define Capture$f⁰ as class Capture$f² { //│ constructor Capture$f¹(x$0) { //│ define x$0¹ as val x$0⁰ = x$0; //│ end //│ } //│ }; //│ define f⁰ as fun f¹(x) { //│ let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, f$cap, A$here; //│ set f$cap = new mut Capture$f²(x); //│ set tmp = +⁰(f$cap.x$0⁰, 1); //│ set tmp1 = +⁰(f$cap.x$0⁰, 2); //│ set tmp2 = new A¹(tmp)(f$cap)(tmp1); //│ set tmp3 = +⁰(f$cap.x$0⁰, 1); //│ set tmp4 = new A¹(tmp3)(f$cap); //│ set tmp5 = +⁰(f$cap.x$0⁰, 1); //│ set tmp6 = +⁰(f$cap.x$0⁰, 2); //│ set tmp7 = new A¹(tmp5)(f$cap)(tmp6); //│ set A$here = A$¹(f$cap); //│ return [tmp2, A$here, tmp4, tmp7] //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let res = f(0) //│ res = [A(1), fun, fun, A(1)] :expect [2, 2] let a = res.0 a.foo() let b = a.newA() b.foo() [a.getX(), b.getX()] //│ = [2, 2] //│ a = A(1) //│ b = A(2) res.1(10)(20).foo() //│ = 3 let a1 = res.2(20) //│ a1 = A(1) a1.foo() //│ = 4 :expect 20 a1.z //│ = 20 let a2 = res.2(30) //│ a2 = A(1) a2.z //│ = 30 :fixme :expect 20 a1.z //│ ═══[RUNTIME ERROR] Expected: '20', got: '30' //│ = 30 a.getX() //│ = 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls ================================================ :js :lift // NOTE: due to conflict between flow analysis and lifter :deadParamElim off :sir data class A(x) with data class B(y) with fun getB() = x + y fun getA() = B(2).getB() //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let B⁰, A⁰; //│ define B⁰ as class B²(y) { //│ private val A¹; //│ val y⁰; //│ constructor B¹(A) { //│ set A¹ = A; //│ define y⁰ as val y¹ = y; //│ end //│ } //│ method getB⁰ = fun getB¹() { //│ return +⁰(A¹.x⁰, B².this.y¹) //│ } //│ }; //│ define A⁰ as class A³(x) { //│ val x¹; //│ constructor A² { //│ define x¹ as val x⁰ = x; //│ end //│ } //│ method getA⁰ = fun getA¹() { let tmp; set tmp = new B²(2)(A³.this); return tmp.getB¹() } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 3 A(1).getA() //│ = 3 data class A1(x) with data class B(y) with fun getB() = x + y fun getA() = (new B(2)).getB() :expect 3 (new A1(1)).getA() //│ = 3 :sir class A with val x = fun g() = 2 g (new A).x() //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g⁰, A⁴, tmp; //│ define g⁰ as fun g¹() { //│ return 2 //│ }; //│ define A⁴ as class A⁵ { //│ val x²; //│ constructor { define x² as val x³ = g¹; end } //│ }; //│ set tmp = new A⁵(); //│ tmp.x³() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 2 :expect 2 class A(a) with let x = 2 fun f() = fun g() = x g() A(2).f() //│ = 2 class A with let x = 2 val bruh = () => x fun f = fun g() = set x += 2 this g let a = new A a.f() a.f() a.bruh() //│ = 6 //│ a = A { A$cap: Capture$A { x$0: 6 }, bruh: fun } ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls ================================================ :js :lift fun f() = 3 :effectHandlers module A with data class Test with f() val a = 1 :effectHandlers class Test with f() val a = 1 class Eff :effectHandlers :expect 7 handle h = Eff with fun perform(x)(k) = k(x) + 1 let x = 5 h.perform(x) + 1 //│ = 7 ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls ================================================ :lift :js fun foo = let x fun bar = x :expect 12 fun foo = let x fun bar = x x = 12 bar foo //│ = 12 :expect 5 fun f(used1, unused1) = let used2 = unused1 fun g(g_arg) = let used3 = 2 used1 + used2 let unused2 = 2 g(used2) + unused2 f(1, 2) //│ = 5 :expect 9 fun f(used1, unused1) = let used2 = unused1 fun g(g_arg)(g_arg2) = let used3 = 2 used1 + used2 + g_arg + g_arg2 let unused2 = 2 g(used2)(used2) + unused2 f(1, 2) //│ = 9 :expect 9 fun f(used1, unused1) = let used2 = unused1 fun g(g_arg)(g_arg2) = let used3 = 2 used1 + used2 + g_arg + g_arg2 let unused2 = 2 let foo = g foo(used2)(used2) + unused2 f(1, 2) //│ = 9 :expect 5 :sir fun f(used1, unused1) = let used2 = unused1 fun g(g_arg) = let used3 = 2 used1 + used2 let unused2 = 2 let foo = g foo(used2) + unused2 f(1, 2) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g⁰, f⁰, g$⁰; //│ define g$⁰ as fun g$¹(used1, used2)(g_arg) { //│ return g¹(used1, used2, g_arg) //│ }; //│ define g⁰ as fun g¹(used1, used2, g_arg) { //│ let used3; //│ set used3 = 2; //│ return +⁰(used1, used2) //│ }; //│ define f⁰ as fun f¹(used1, unused1) { //│ let used2, unused2, foo, tmp, g$here; //│ set used2 = unused1; //│ set unused2 = 2; //│ set g$here = g$¹(used1, used2); //│ set foo = g$here; //│ set tmp = foo(used2); //│ return +⁰(tmp, unused2) //│ }; //│ f¹(1, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 5 // preserve order :sir fun f(a1, a2, a3, a4, a5, a6) = fun g = a1 + a2 + a3 + a4 + a5 + a6 g f(1,2,3,4,5,6) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g², f²; //│ define g² as fun g³(a1, a2, a3, a4, a5, a6) { //│ let tmp, tmp1, tmp2, tmp3; //│ set tmp = +⁰(a1, a2); //│ set tmp1 = +⁰(tmp, a3); //│ set tmp2 = +⁰(tmp1, a4); //│ set tmp3 = +⁰(tmp2, a5); //│ return +⁰(tmp3, a6) //│ }; //│ define f² as fun f³(a1, a2, a3, a4, a5, a6) { //│ return g³(a1, a2, a3, a4, a5, a6) //│ }; //│ f³(1, 2, 3, 4, 5, 6) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 21 :expect "01" data class Tuple(a, b) fun f() = let a = 0 fun g() = set a = 1 fun h() = a Tuple(g, h) let ret = f() let f1 = ret.a let f2 = ret.b let x = f2().toString() f1() let y = f2().toString() x + y //│ = "01" //│ f1 = fun //│ f2 = fun //│ ret = Tuple(fun, fun) //│ x = "0" //│ y = "1" data class Tuple(a, b) fun f(used1) = fun g1(used2) = fun h() = set used1 = 10 set used2 = 100 fun i() = used1 + used2 Tuple(h, i) fun g22() = used1 Tuple(g1(10), g22) :expect "11110110" let ret = f(1) let gRet = ret.a let g2 = ret.b let hFun = gRet.a let iFun = gRet.b let a = iFun() let b = g2() hFun() let c = g2() let d = iFun() a.toString() + b + c + d //│ = "11110110" //│ a = 11 //│ b = 1 //│ c = 10 //│ d = 110 //│ g2 = fun //│ gRet = Tuple(fun, fun) //│ hFun = fun //│ iFun = fun //│ ret = Tuple(Tuple(fun, fun), fun) // some variables mutated, some not :sir :expect 7 fun f(unused, immutable, mutated) = fun g() = set mutated = 2 immutable + mutated fun h() = mutated let a = g() a + h() + unused f(1, 2, 1000) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g⁴, h⁰, f⁴, Capture$f⁰; //│ define g⁴ as fun g⁵(f$cap, immutable) { //│ set f$cap.mutated$0 = 2; //│ return +⁰(immutable, f$cap.mutated$0⁰) //│ }; //│ define h⁰ as fun h¹(f$cap) { //│ return f$cap.mutated$0⁰ //│ }; //│ define Capture$f⁰ as class Capture$f² { //│ constructor Capture$f¹(mutated$0) { //│ define mutated$0¹ as val mutated$0⁰ = mutated$0; //│ end //│ } //│ }; //│ define f⁴ as fun f⁵(unused, immutable, mutated) { //│ let a, tmp, tmp1, f$cap; //│ set f$cap = new mut Capture$f²(mutated); //│ set a = g⁵(f$cap, immutable); //│ set tmp = h¹(f$cap); //│ set tmp1 = +⁰(a, tmp); //│ return +⁰(tmp1, unused) //│ }; //│ f⁵(1, 2, 1000) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 7 // if used as a higher order function, pass closures :expect 2 fun f() = let x = g let y = 2 fun g() = y x() f() //│ = 2 :expect 2 fun f() = let y = 1 fun g() = y let x = g set y = 2 x() f() //│ = 2 :expect 2 fun f(arg) = fun g(n) = if n <= 0 then arg else h(n-1) fun h(n) = if n <= 0 then arg else g(n-1) h(5) f(2) //│ = 2 :expect 1 fun f() = let x = 1 fun g() = x fun f() = g() f() f() //│ = 1 fun f() = 3 fun g() = fun h() = 3 h() g() //│ = 3 :sir fun g() = let y = 0 fun f(x) = let k = 4 set y = 2 // set x = 3 // just a dummy function to force class generation fun h() = set k = 5 set x = 4 x + y x f g()(1) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let h², f⁶, g⁶, baseCall, Capture$scope0⁰, f$⁰; //│ define Capture$scope0⁰ as class Capture$scope0² { //│ constructor Capture$scope0¹(y$0) { //│ define y$0⁰ as val y$0¹ = y$0; //│ end //│ } //│ }; //│ define h² as fun h³(scope0$cap, x, k) { //│ set k = 5; //│ set x = 4; //│ return +⁰(x, scope0$cap.y$0¹) //│ }; //│ define f$⁰ as fun f$¹(scope0$cap)(x) { //│ return f⁷(scope0$cap, x) //│ }; //│ define f⁶ as fun f⁷(scope0$cap, x) { //│ let k; //│ set k = 4; //│ set scope0$cap.y$0 = 2; //│ return x //│ }; //│ define g⁶ as fun g⁷() { //│ let y, scope0$cap, f$here; //│ set scope0$cap = new mut Capture$scope0²(y); //│ set scope0$cap.y$0 = 0; //│ set f$here = f$¹(scope0$cap); //│ return f$here //│ }; //│ set baseCall = g⁷(); //│ baseCall(1) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :expect 2 fun f() = let b = 0 fun g() = set b += 1 b g let ret = f() ret() ret() //│ = 2 //│ ret = fun :expect 6 fun f(x, y, z) = fun g() = set x = 1 h() x + y + z fun h() = set y = 2 i() fun i() = set z = 3 g f(0, 0, 0)() //│ = 6 fun f(x, cond) = set x = 1 set x = 2 if cond then () => x else set x = 3 () => x :expect 1 fun f() = let x = 1 fun g() = 1 fun f() = set x = 1 f() x f() //│ = 1 let n = 0 //│ n = 0 fun h() = 3 fun f() = fun g() = set n = n + 1 g() f() fun f(es, vs) = let v' = es class A with val fld = v' new A // f should not have a capture :expect 1 :sir fun f(x) = fun g() = set x += 1 x if true do return g() set x += 1 x f(0) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g⁸, f⁸; //│ define g⁸ as fun g⁹(x) { //│ let tmp; //│ set tmp = +⁰(x, 1); //│ set x = tmp; //│ return x //│ }; //│ define f⁸ as fun f⁹(x) { //│ let scrut, tmp; //│ set scrut = true; //│ match scrut //│ true => //│ return g⁹(x) //│ else //│ set tmp = +⁰(x, 1); //│ set x = tmp; //│ return x //│ end //│ }; //│ f⁹(0) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // try to avoid creating multiple closures fun f(x) = fun g() = x [g, g] :sir fun f(x) = let y fun g() = y if x < 0 then set y = 1 [g, g] else set y = 2 [g, g] //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g¹⁰, f¹⁰, g$²; //│ define g$² as fun g$³(y)() { //│ return g¹¹(y) //│ }; //│ define g¹⁰ as fun g¹¹(y) { //│ return y //│ }; //│ define f¹⁰ as fun f¹¹(x) { //│ let y, scrut, g$here; //│ set scrut = <⁰(x, 0); //│ match scrut //│ true => //│ set y = 1; //│ set g$here = g$³(y); //│ return [g$here, g$here] //│ else //│ set y = 2; //│ set g$here = g$³(y); //│ return [g$here, g$here] //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sir fun f(x) = fun g() = x let a = g set x += 1 [a, g] //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let g¹², f¹², Capture$f³, g$⁴; //│ define g$⁴ as fun g$⁵(f$cap)() { //│ return g¹³(f$cap) //│ }; //│ define g¹² as fun g¹³(f$cap) { //│ return f$cap.x$0⁰ //│ }; //│ define Capture$f³ as class Capture$f⁵ { //│ constructor Capture$f⁴(x$0) { //│ define x$0¹ as val x$0⁰ = x$0; //│ end //│ } //│ }; //│ define f¹² as fun f¹³(x) { //│ let a, tmp, f$cap, g$here; //│ set f$cap = new mut Capture$f⁵(x); //│ set g$here = g$⁵(f$cap); //│ set a = g$here; //│ set tmp = +⁰(f$cap.x$0⁰, 1); //│ set f$cap.x$0 = tmp; //│ set g$here = g$⁵(f$cap); //│ return [a, g$here] //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Can be avoided in `h` by passing the initialized closure, but this is more complicated // Note that automatically rewriting `fun h() = g` to `fun h() = a` would resolve this, // but it requires some more analysis fun f(x) = fun g() = x let a = g fun h() = g [a, h()] // Requires analysis of all branches fun f(x) = fun g() = x if true then g else g g // Spread arguments :expect [1] fun f(x) = fun g(...rest) = print(x) rest let a = g a(1) f(2) //│ > 2 //│ = [1] fun f(...args) = () => print("test") f(...args) f(100)()()() //│ > test //│ > test //│ > test //│ = fun data class Some(x) // This test generates a function that is not defined directly within the scoped block introducing its symbol, // i.e. it is defined another scope nested within the scoped block. This checks that that the function symbol // is removed properly from that socpe. let _refChoice = () => if 0 is Some(0) then let process''(pairRes) = if pairRes is [restRes] then 0 true then let process''() = 0 _refChoice //│ = fun //│ _refChoice = fun :expect 2 fun f = fun g(x) = x() fun h() = 2 g(h) f //│ = 2 :expect 2 fun f = class A(x) with fun foo = x() fun h() = 2 new A(h) f.foo //│ = 2 :expect 2 fun f = class A(x) with fun foo = x() fun h() = 2 A(h) f.foo //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls ================================================ :lift :js :expect 6 data class A(a) with fun f(b) = fun g(c) = set b = 2 a + b + c g A(1).f(1)(3) //│ = 6 ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/Imports.mls ================================================ :js :lift import "../../mlscript-compile/Option.mls" :sjs module A with fun f(x) = x is Option.Some //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let A1; //│ (class A { //│ static { //│ A1 = this //│ } //│ static f(x) { //│ if (x instanceof Option.Some.class) { //│ return true //│ } //│ return false; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/Loops.mls ================================================ :js :lift :ignore // FIXME: Lifting should not require scope flattening (which doesn't handle loops) let fs = mut [] //│ fs = [] fun foo() = let x = 1 while x < 5 do set x += 1 fs.push of () => x :expect 5 foo() fs.0() //│ = 5 let fs = mut [] //│ fs = [] fun foo() = let i = 1 while i < 5 do let x = i set i += 1 fs.push of () => x // * Note that this works with while loop rewriting // * See [fixme:0] for cause of the issue :expect 1 foo() fs.0() //│ = 1 :sir fun foo() = let x = 1 while true do set x += 1 return () => x //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo⁰, lambda⁰, Capture$scope0⁰, lambda$⁰; //│ define lambda$⁰ as fun lambda$¹(scope0$cap)() { //│ return lambda¹(scope0$cap) //│ }; //│ define lambda⁰ as fun lambda¹(scope0$cap) { //│ return scope0$cap.x$0⁰ //│ }; //│ define Capture$scope0⁰ as class Capture$scope0² { //│ constructor Capture$scope0¹(x$0) { //│ define x$0¹ as val x$0⁰ = x$0; //│ end //│ } //│ }; //│ define foo⁰ as fun foo¹() { //│ let x, scope0$cap; //│ set scope0$cap = new mut Capture$scope0²(x); //│ set scope0$cap.x$0 = 1; //│ loop lbl: //│ let scrut, tmp; //│ set scrut = true; //│ match scrut //│ true => //│ let lambda$here; //│ set tmp = +⁰(scope0$cap.x$0⁰, 1); //│ set scope0$cap.x$0 = tmp; //│ set lambda$here = lambda$¹(scope0$cap); //│ return lambda$here //│ else //│ end //│ end //│ return runtime⁰.Unit⁰ //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 2 foo()() //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls ================================================ :lift :js // NOTE: due to conflict between flow analysis and lifter :deadParamElim off // * Lifting an object may require it to gain some mutable fields :todo :w fun foo(x, y) = object M with val test = 2 fun foo() = set y = 2 x + y + test M.foo() //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.12: object M with //│ ╙── ^ :expect 14 foo(10, 0) //│ = 14 // * This works the same with classes. // * Note that we currently generate accessors, which is not ideal; // * When the captures are not accessed from the outside, we should be able to drop these. :sir fun foo(y) = class M with fun foo() = set y = 2 (new M).foo() foo(10) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let M⁰, foo⁰; //│ define M⁰ as class M¹ { //│ private val y⁰; //│ constructor(y) { //│ set y⁰ = y; //│ end //│ } //│ method foo¹ = fun foo²() { //│ set y⁰ = 2; //│ return runtime⁰.Unit⁰ //│ } //│ }; //│ define foo⁰ as fun foo³(y) { let tmp; set tmp = new M¹(y); return tmp.foo²() }; //│ foo³(10) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * `new mut` (or lack thereof) should be preserved by the lifter fun foo(y) = class C with fun foo() = set y = 2 [new C, new mut C] let cc = foo(10) c0 = cc.0 c1 = cc.1 //│ c0 = C //│ c1 = C //│ cc = [C, C] :re set c0.x = 1 c0.x //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' set c1.x = 1 c1.x //│ = 1 // * Objects should remain non-`new mut` :todo :w fun foo(y) = object O with fun foo() = set y = 2 O //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.85: object O with //│ ╙── ^ let o = foo(10) //│ o = O :re set o.x = 1 o.x //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' :todo :w :sir fun foo(x, y) = object M with fun foo2() = set y = 2 x + y fun foo3 = M.foo2() foo3 //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.105: object M with //│ ╙── ^ //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let foo3⁰, foo⁴; //│ define foo3⁰ as fun foo3¹(M) { //│ return M.foo2⁰() //│ }; //│ define foo⁴ as fun foo⁵(x, y) { //│ let M; //│ define M as object M² { //│ method foo2¹ = fun foo2⁰() { set y = 2; return +⁰(x, y) } //│ }; //│ return foo3¹(M²) //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 12 foo(10, 0) //│ = 12 :todo :w data class A(x) with object M with fun getB() = x fun getA() = fun bar = M.getB() bar //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.136: object M with //│ ╙── ^ :expect 2 A(2).getA() //│ = 2 // Classes and functions in modules // :expect 2 module M with val x = 2 class A() with fun get = x val hi = A() M.hi.get //│ = 2 :expect 2 module M with val x = 2 fun f() = fun g() = x g M.f()() //│ = 2 :expect 2 object M with val x = 2 class A() with fun get = x val hi = A() M.hi.get //│ = 2 :expect 2 object M with val x = 2 fun f() = fun g() = x g M.f()() //│ = 2 // unlifted objects :lift :w :expect 2 fun f = object A with fun a = 2 0 is A fun foo = A foo f.a //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.192: object A with //│ ╙── ^ //│ = 2 :sir module M with class A() with fun get = M.x + x val x = if A() is A then 2 else 3 M.A().get //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let M³, tmp; //│ define M³ as class M⁴ //│ module M⁵ { //│ val x⁰; //│ constructor { //│ let scrut, tmp1; //│ define A⁰ as class A¹() { //│ method get⁰ = fun get¹ { //│ return +⁰(M⁵.this.x¹, M⁵.this.x¹) //│ } //│ }; //│ set scrut = M⁵.this.A²(); //│ match scrut //│ A¹ => //│ set tmp1 = 2; //│ end //│ else //│ set tmp1 = 3; //│ end //│ define x⁰ as val x¹ = tmp1; //│ end //│ } //│ }; //│ set tmp = M⁵.A²(); //│ tmp.get¹ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 4 module M with val x = 2 class A with fun get = x class B() extends A M.B().get //│ = 2 /// Top Level Modules /// // newA should reference the class symbol // newA_B should reference the BlockMemberSymbol // note: no warning because A is not lifted :lift module M with data class A(x) with class B() with fun get = x fun newA_B(y) = A(y) fun newA = A(42) fun newB = B() 0 is A M.A(2).newB.newA_B(3).newB.get //│ = 3 module M with class A object B extends A :todo :w :lift fun foo(x) = object O with fun bar = x val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.272: object O with //│ ╙── ^ foo(123) //│ = [true, [true, false], true] module A with fun g = 1 fun f = () => g f() module A with val a = (() => 1)() // This is related to the above, but it will look into the ignored bms map module A with val x = fun f() = 1 f() module A with val x = fun f() = 1 fun g() = fun h() = f() h() f() + g() A.x //│ = 2 // private fields in modules module A with let x = 1 fun mtd = fun nested() = x nested() A.mtd //│ = 1 // nested objects :todo :w fun f = object A with object B with object C A.B.C f //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.320: object A with //│ ╙── ^ //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.321: object B with //│ ╙── ^ //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.322: object C //│ ╙── ^ //│ = C :todo :w fun f = module A with object B with object C A.B.C f //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.339: module A with //│ ╙── ^ //│ ╔══[WARNING] Objects are not yet lifted. //│ ║ l.341: object C //│ ╙── ^ //│ = C :todo :w fun f = module A with module B with object C A.B.C f //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.355: module A with //│ ╙── ^ //│ = C module X with object A A module X with object A fun f = fun g = A g f ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/Mutation.mls ================================================ :lift :js let xs = mut [] //│ xs = [] :sir fun foo() = let x = 1 fun bar() = x xs.push(bar) set x = 2 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let bar⁰, foo⁰, Capture$scope0⁰, bar$⁰; //│ define Capture$scope0⁰ as class Capture$scope0² { //│ constructor Capture$scope0¹(x$0) { //│ define x$0⁰ as val x$0¹ = x$0; //│ end //│ } //│ }; //│ define bar$⁰ as fun bar$¹(scope0$cap)() { //│ return bar¹(scope0$cap) //│ }; //│ define bar⁰ as fun bar¹(scope0$cap) { //│ return scope0$cap.x$0¹ //│ }; //│ define foo⁰ as fun foo¹() { //│ let x, scope0$cap, bar$here; //│ set scope0$cap = new mut Capture$scope0²(x); //│ set scope0$cap.x$0 = 1; //│ set bar$here = bar$¹(scope0$cap); //│ do xs⁰.push﹖(bar$here); //│ set scope0$cap.x$0 = 2; //│ return runtime⁰.Unit⁰ //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect 2 foo() xs.0() //│ = 2 let xs = mut [] //│ xs = [] // :sir fun foo() = let x fun bar() = x xs.push(bar) x = 2 :expect 2 foo() xs.0() //│ = 2 fun foo() = let x fun bar() = x = 2 [bar, () => x] :expect 2 let b_x = foo() b_x.0() b_x.1() //│ = 2 //│ b_x = [fun, fun] fun foo() = let x = undefined fun bar() = x = 2 [bar, () => x] :expect 2 let b_x = foo() b_x.0() b_x.1() //│ = 2 //│ b_x = [fun, fun] :sir fun foo() = fun bar() = let x x bar //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let bar², foo²; //│ define bar² as fun bar³() { let x; return x }; //│ define foo² as fun foo³() { return bar³ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls ================================================ :lift :js // TODO: Improve warning messages :w fun foo() = pattern P = Int x => x is P //│ ═══[WARNING] Cannot yet lift class `P` as it is used as a first-class class. ================================================ FILE: hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls ================================================ :js :lift :noTailRec // * FIXME: Why doesn't the following work when using Predef function `(==) equals`? // sanity check :expect 5050 fun sum(n) = if n === 0 then 0 else n + sum(n - 1) sum(100) //│ = 5050 // preserve tail calls // MUST see "return hi(tmp1)" in the output :stackSafe 6 :effectHandlers :expect 0 :sir fun hi(n) = if n === 0 then 0 else hi(n - 1) hi(0) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let hi⁰, tmp, ‹stack safe body›⁰; //│ do runtime⁰.resetEffects﹖(); //│ define hi⁰ as fun hi¹(n) { //│ let stackDelayRes; //│ set runtime⁰.stackDepth = +⁰(runtime⁰.stackDepth﹖, 1); //│ set stackDelayRes = runtime⁰.checkDepth﹖(); //│ match runtime⁰.curEffect﹖ //│ null => //│ let scrut, tmp1; //│ set scrut = ===⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ let res; //│ set tmp1 = -⁰(n, 1); //│ return hi¹(tmp1) //│ end //│ else //│ return runtime⁰.unwind﹖(hi¹, -1, "StackSafetyLift.mls:22:1", null, null, 1, 1, n, 0) //│ end //│ }; //│ define ‹stack safe body›⁰ as fun ‹stack safe body›¹() { //│ return hi¹(0) //│ }; //│ set tmp = runtime⁰.runStackSafe﹖(6, ‹stack safe body›⁰); //│ match runtime⁰.curEffect﹖ //│ null => //│ tmp //│ else //│ set tmp = runtime⁰.topLevelEffect﹖(false); //│ tmp //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :sir :stackSafe :effectHandlers :expect 50005000 fun sum(n) = if n === 0 then 0 else n + sum(n - 1) sum(10000) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let sum⁰, tmp, ‹stack safe body›²; //│ do runtime⁰.resetEffects﹖(); //│ define sum⁰ as fun sum¹(n) { //│ let curDepth, stackDelayRes; //│ set runtime⁰.stackDepth = +⁰(runtime⁰.stackDepth﹖, 1); //│ set curDepth = runtime⁰.stackDepth﹖; //│ set stackDelayRes = runtime⁰.checkDepth﹖(); //│ match runtime⁰.curEffect﹖ //│ null => //│ let scrut, tmp1, tmp2, pc; //│ match runtime⁰.resumePc﹖ //│ -1 => //│ set pc = 0; //│ end //│ else //│ let saveOffset; //│ set pc = runtime⁰.resumePc﹖; //│ set runtime⁰.resumePc = -1; //│ end //│ loop main: //│ match pc //│ 0 => //│ block brk0: //│ set scrut = ===⁰(n, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ let res; //│ set tmp1 = -⁰(n, 1); //│ set res = sum¹(tmp1); //│ set runtime⁰.stackDepth = curDepth; //│ set runtime⁰.resumeValue = res; //│ match runtime⁰.curEffect﹖ //│ null => //│ break brk0 //│ else //│ let res1; //│ return runtime⁰.unwind﹖(sum¹, 1, "StackSafetyLift.mls:70:9", null, null, 1, 1, n, 0) //│ end //│ end //│ set pc = 1; //│ end //│ match pc //│ 1 => //│ set tmp2 = runtime⁰.resumeValue﹖; //│ return +⁰(n, tmp2) //│ else //│ end //│ end //│ end //│ else //│ return runtime⁰.unwind﹖(sum¹, -1, "StackSafetyLift.mls:67:1", null, null, 1, 1, n, 0) //│ end //│ }; //│ define ‹stack safe body›² as fun ‹stack safe body›³() { //│ return sum¹(10000) //│ }; //│ set tmp = runtime⁰.runStackSafe﹖(1000, ‹stack safe body›²); //│ match runtime⁰.curEffect﹖ //│ null => //│ tmp //│ else //│ set tmp = runtime⁰.topLevelEffect﹖(false); //│ tmp //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 50005000 // stack-overflows without :stackSafe :re fun sum(n) = if n === 0 then 0 else n + sum(n - 1) sum(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :effectHandlers :stackSafe 6 mut val ctr = 0 fun dummy(x) = x fun foo(f) = if ctr > 10000 then 0 else set ctr += 1 dummy(f(f)) foo(foo) //│ = 0 //│ ctr = 10001 :stackSafe :effectHandlers :expect 50005000 val foo = val f = n => if n <= 0 then 0 else n + f(n-1) f(10000) foo //│ = 50005000 //│ foo = 50005000 :re fun foo() = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) f(10000) foo() //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded abstract class Eff with fun perform(a): () // functions and lambdas inside handlers :effectHandlers :stackSafe 6 :expect 50005000 fun foo(h) = h.perform() handle h = Eff with fun perform()(resume) = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) foo(h) //│ = 50005000 // function call and defn inside handler :effectHandlers :stackSafe 6 :expect 50005000 handle h = Eff with fun perform()(resume) = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) in fun foo(h) = h.perform() foo(h) //│ = 50005000 :re :effectHandlers fun foo(h) = h.perform(2) handle h = Eff with fun perform(a)(resume) = let f = () set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) foo(h) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded :effectHandlers :stackSafe :sir fun max(a, b) = if a < b then b else a //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let max⁰; //│ do runtime⁰.resetEffects﹖(); //│ define max⁰ as fun max¹(a, b) { //│ let scrut; //│ set scrut = <⁰(a, b); //│ match scrut //│ true => //│ return b //│ else //│ return a //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sir :stackSafe 42 fun hi(n) = n hi(0) //│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let hi²; define hi² as fun hi³(n) { return n }; hi³(0) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 :stackSafe 42 hi(0) //│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set //│ = 0 :stackSafe :effectHandlers :expect 100010000 fun sum(n) = if n === 0 then 0 else n + sum(n - 1) fun bad() = sum(10000) + sum(10000) bad() //│ = 100010000 ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/BadPrograms.mls ================================================ :llir :cpp :ge fun oops(a) = class A with fun m = a let x = 1 //│ ═══[COMPILATION ERROR] Function without arguments not supported: 0 //│ Stopped due to an error during the Llir generation :ge let x = "oops" x.m //│ ═══[COMPILATION ERROR] Unsupported selection by users //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/BasicCpp.mls ================================================ :js :llir :cpp fun foo(a) = let x = undefined if a > 0 do x = 1 x + 1 :showWholeCpp fun bar(x) = x + 1 foo(1) //│ = 2 //│ //│ WholeProgramCpp: //│ #include "mlsprelude.h" //│ _mlsValue _mls_entry2(); //│ _mlsValue _mls_foo(_mlsValue); //│ _mlsValue _mls_entry(); //│ _mlsValue _mls_j(); //│ _mlsValue _mls_entry1(); //│ _mlsValue _mls_bar(_mlsValue); //│ _mlsValue _mls_bar(_mlsValue _mls_x6) { //│ _mlsValue _mls_retval; //│ auto _mls_x5 = (_mls_x6 + _mlsValue::fromIntLit(1)); //│ _mls_retval = _mls_x5; //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry2() { //│ _mlsValue _mls_retval; //│ auto _mls_x7 = _mls_foo(_mlsValue::fromIntLit(1)); //│ _mls_retval = _mls_x7; //│ return _mls_retval; //│ } //│ _mlsValue _mls_j() { //│ _mlsValue _mls_retval; //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry() { //│ _mlsValue _mls_retval; //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mls_foo(_mlsValue _mls_a) { //│ _mlsValue _mls_retval; //│ auto _mls_x = _mlsValue::create<_mls_Unit>(); //│ auto _mls_x1 = (_mls_a > _mlsValue::fromIntLit(0)); //│ if (_mlsValue::isIntLit(_mls_x1, 1)) { //│ auto _mls_x3 = _mlsValue::fromIntLit(1); //│ auto _mls_x4 = (_mls_x3 + _mlsValue::fromIntLit(1)); //│ _mls_retval = _mls_x4; //│ } else { //│ auto _mls_x2 = (_mls_x + _mlsValue::fromIntLit(1)); //│ _mls_retval = _mls_x2; //│ } //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry1() { //│ _mlsValue _mls_retval; //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mlsMain() { return _mls_entry2(); } //│ int main() { return _mlsLargeStack(_mlsMainWrapper); } ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/BasisLLIR.mls ================================================ :js :llir :cpp :noTailRec // The LLIR lowering does not yet support Label blocks // This file contains all tests for LLIR in the original MLscript compiler. :intl data class Pair[A, B](x: A, y: B) fun mktup2(x, y) = mktup(x, y) fun mktup(x, y) = Pair(x, y) fun foo() = mktup2(1, 2) foo() //│ = Pair(1, 2) //│ //│ Interpreted: //│ Pair(1,2) :intl data class Pair[A, B](x: A, y: B) fun foo(pair) = if pair is Pair(x, y) then Pair(x, y) fun bar() = foo(Pair(1, 2)) bar() //│ = Pair(1, 2) //│ //│ Interpreted: //│ Pair(1,2) :intl data class Pair[A, B](x: A, y: B) fun foo(pair) = if pair is Pair(x, y) then Pair(x, y) fun bar() = foo(Pair(1, 2)) bar() //│ = Pair(1, 2) //│ //│ Interpreted: //│ Pair(1,2) :intl data class Pair[A, B](x: A, y: B) fun silly(pair) = let x = 0 let n = if pair is Pair(x1, x2) then if pair is Pair (x3, x4) then x3 + 1 n + 1 fun foo() = let a = Pair(0, 1) let b = silly(a) b foo() //│ = 2 //│ //│ Interpreted: //│ 2 :intl data class Pair[A, B](x: A, y: B) fun inc_fst(pair) = let c = 2 if pair is Pair(x1, x2) then x1 + c fun foo() = let a = Pair(0, 1) let b = inc_fst(a) b foo() //│ = 2 //│ //│ Interpreted: //│ 2 :intl data class Pair[A, B](x: A, y: B) fun inc_fst(pair) = let x = 0 if pair is Pair(x1, x2) then x2 + 1 fun foo() = let b = inc_fst(Pair(0, 1)) b foo() //│ = 2 //│ //│ Interpreted: //│ 2 :intl abstract class Either[out A, out B]: Left[A, B] | Right[A, B] data class Left[out A, out B](x: A) extends Either[A, B] data class Right[out A, out B](y: B) extends Either[A, B] fun foo(a, b) = let t = if a is Left(x) then Left(x + 1) Right(y) then Right(b) if t is Left(x) then x Right(y) then y fun bar() = foo(Right(2), 2) bar() //│ = 2 //│ //│ Interpreted: //│ 2 :intl abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun foo() = bar(S(O)) fun bar(x) = baz(x) fun baz(x) = if x is S(s) then s O then x foo() //│ = O //│ //│ Interpreted: //│ O() :intl data class A(x, y, z) data class B(m, n) fun complex_foo(t) = let r = if t is A(x, y, z) then x + y * z B(m, n) then m - n let s = B(1, 2) let u = if s is A(x, y, z) then 3 B(m, n) then 4 r + u fun bar() = complex_foo(A(6, 7, 8)) complex_foo(B(9, 10)) bar() //│ = 3 //│ //│ Interpreted: //│ 3 :intl data class A(w, x) data class B(y) data class C(z) fun complex_foo(t) = let a = 1 + 2 let b = 1 * 2 let x = if t is A(x, y) then y B(x) then B(x + b) C(x) then C(0) let z = A(5, x) let v = B(6) let y = if x is A(x, y) then let m = x + a + b if y is A(x, y) then x B(x) then m C(x) then 0 B(x) then 2 C(x) then 3 if z is A(x, y) then x B(x) then 4 C(x) then if v is A(x, y) then x B(x) then 7 C(x) then 8 fun bar() = complex_foo(A(10, A(9, B(10)))) bar() //│ = 5 //│ //│ Interpreted: //│ 5 :intl fun fib(n) = if n < 2 then n else fib(n-1) + fib(n-2) fib(20) //│ = 6765 //│ //│ Interpreted: //│ 6765 :intl fun odd(x) = if x === 0 then false else even(x-1) fun even(x) = if x === 0 then true else odd(x-1) fun foo() = odd(10) foo() //│ = false //│ //│ Interpreted: //│ false :intl abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option fun nott(x) = if x then false else true fun foo(x) = if x then None else Some(foo(nott(x))) fun main() = foo(false) main() //│ = Some(None) //│ //│ Interpreted: //│ Some(None()) :intl abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option fun fromSome(s) = if s is Some(x) then x abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun aaa() = let m = 1 let n = 2 let p = 3 let q = 4 m + n - p + q fun bbb() = let x = aaa() x * 100 + 4 fun nott(x) = if x then false else true fun foo(x) = if x then None else Some(foo(nott(x))) fun main() = let x = foo(false) if x is None then aaa() Some(b1) then bbb() main() //│ = 404 //│ //│ Interpreted: //│ 404 :intl abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun odd(x) = if x is O then false S(s) then even(s) fun even(x) = if x is O then true S(s) then odd(s) fun foo() = odd(S(S(S(O)))) foo() //│ = true //│ //│ Interpreted: //│ true :intl abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun odd(x) = if x is O then false S(s) then even(s) fun even(x) = if x is O then true S(s) then odd(s) fun mk(n) = if n > 0 then S(mk(n - 1)) else O fun foo() = odd(mk(10)) foo() //│ = false //│ //│ Interpreted: //│ false :intl abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun odd(x) = if x is O then false S(s) then even(s) fun even(x) = if x is O then true S(s) then odd(s) fun mk(n) = if n > 0 then S(mk(n - 1)) else O fun foo() = odd(S(S(mk(10)))) foo() //│ = false //│ //│ Interpreted: //│ false :intl abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun odd(x) = if x is O then false S(s) then even(s) fun even(x) = if x is O then true S(s) then odd(s) fun foo() = odd(if 10 > 0 then S(O) else O) fun bar() = if 10 > 0 then odd(S(O)) else odd(O) fun main() = foo() bar() main() //│ = true //│ //│ Interpreted: //│ true :intl abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List fun head_opt(l) = if l is Nil then None Cons(h, t) then Some(h) fun is_none(o) = if o is None then true Some(x) then false fun is_empty(l) = is_none(head_opt(l)) fun main() = is_empty(Cons(1, Cons(2, Nil))) main() //│ = false //│ //│ Interpreted: //│ false :intl abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List fun mk_list(n) = if n === 0 then Nil else Cons(n, mk_list(n - 1)) fun head_opt(l) = if l is Nil then None Cons(h, t) then Some(h) fun is_none(o) = if o is None then true Some(x) then false fun is_empty(l) = is_none(head_opt(l)) fun main() = is_empty(mk_list(10)) main() //│ = false //│ //│ Interpreted: //│ false :intl abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List fun mk_list(n) = if n === 0 then Nil else Cons(n, mk_list(n - 1)) fun last_opt(l) = if l is Nil then None Cons(h, t) then if t is Nil then Some(h) Cons(h2, t2) then last_opt(t) fun main() = last_opt(mk_list(10)) main() //│ = Some(1) //│ //│ Interpreted: //│ Some(1) :intl abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option fun is_some(o) = if o is Some(x) then true None then false fun e0(w) = w + 8 + 9 + 10 fun e1(a, c) = a + 1 + 2 + 3 + 4 fun e3(c) = let m = 4 let n = 5 let p = 6 let q = 7 if c then m + n + p + q else m + n - p + q fun e2(x) = x + 12 + 13 + 14 fun f(x) = let c1 = is_some(x) let z = e3(c1) let w = if x is Some(a) then e1(a, z) None then e2(z) e0(w) fun main() = f(Some(2)) + f(None) main() //│ = 115 //│ //│ Interpreted: //│ 115 :intl abstract class Nat: S[Nat] | O data class S(s: Nat) extends Nat object O extends Nat fun pred(n) = if n is S(p) then p O then O fun plus(n1, n2) = if n1 is O then n2 S(p) then S(plus(p, n2)) fun fib(n) = if n is O then S(O) S(p) then if p is O then S(O) S(q) then plus(fib(p), fib(q)) fun to_int(n) = if n is O then 0 S(p) then 1 + to_int(p) fun to_nat(n) = if n === 0 then O else S(to_nat(n - 1)) fun main() = to_int(fib(to_nat(10))) main() //│ = 89 //│ //│ Interpreted: //│ 89 ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/Classes.mls ================================================ :llir :cpp :noTailRec // The LLIR lowering does not yet support Label blocks :intl abstract class Callable object FnLike1 extends Callable with fun apply1(x) = x * 2 fun apply(f, x) = f(x) fun main() = let mul2 = FnLike1 apply(mul2, 3) main() //│ //│ Interpreted: //│ 6 :intl :sllir class Base() with fun get() = 1 class Derived() extends Base with fun get() = 2 fun main() = let d = Derived() d.Base#get() * d.Derived#get() main() //│ LLIR: //│ class Base() { //│ def get() = //│ 1 //│ } //│ class Derived() extends Base { //│ def get1() = //│ 2 //│ } //│ def main() = //│ let x = Derived() in //│ let x1 = Base.get(x) in //│ let x2 = Derived.get(x) in //│ let x3 = *(x1,x2) in //│ x3 //│ def entry() = //│ let* (x4) = main() in //│ x4 //│ entry = entry //│ //│ Interpreted: //│ 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/ControlFlow.mls ================================================ :js :llir :cpp :noTailRec // The LLIR lowering does not yet support Label blocks :sllir :intl fun f1() = let x = 1 let x = 2 x f1() //│ = 2 //│ LLIR: //│ //│ def f1() = //│ let x = 1 in //│ let x1 = 2 in //│ x1 //│ def entry() = //│ let* (x2) = f1() in //│ x2 //│ entry = entry //│ //│ Interpreted: //│ 2 :sllir :intl fun f2() = let x = 0 if x === 1 then 2 else 3 f2() //│ = 3 //│ LLIR: //│ //│ def f2() = //│ let x = 0 in //│ let x1 = ===(x,1) in //│ case x1 of //│ Lit(BoolLit(true)) => //│ 2 //│ _ => //│ 3 //│ def j() = //│ undefined //│ def entry() = //│ let* (x2) = f2() in //│ x2 //│ entry = entry //│ //│ Interpreted: //│ 3 :sllir fun f3() = let x1 = 0 let x2 = 1 if true then x1 else x2 f3() //│ = 0 //│ LLIR: //│ //│ def f3() = //│ let x = 0 in //│ let x1 = 1 in //│ let x2 = true in //│ case x2 of //│ Lit(BoolLit(true)) => //│ x //│ _ => //│ x1 //│ def j() = //│ undefined //│ def entry() = //│ let* (x3) = f3() in //│ x3 //│ entry = entry :sllir :intl fun f4() = let x = 0 let x = if x === 1 then 2 else 3 x f4() //│ = 3 //│ LLIR: //│ //│ def f4() = //│ let x = 0 in //│ let x1 = ===(x,1) in //│ case x1 of //│ Lit(BoolLit(true)) => //│ let x2 = 2 in //│ jump j(x2) //│ _ => //│ let x3 = 3 in //│ jump j(x3) //│ def j(tmp) = //│ tmp //│ def entry() = //│ let* (x4) = f4() in //│ x4 //│ entry = entry //│ //│ Interpreted: //│ 3 :sllir :intl fun f5() = let x = 0 let x = if x === 1 then 2 else 3 let x = if x === 2 then 4 else 5 x f5() //│ = 5 //│ LLIR: //│ //│ def f5() = //│ let x = 0 in //│ let x1 = ===(x,1) in //│ case x1 of //│ Lit(BoolLit(true)) => //│ let x2 = 2 in //│ jump j(x2) //│ _ => //│ let x3 = 3 in //│ jump j(x3) //│ def j(tmp) = //│ let x4 = ===(tmp,2) in //│ case x4 of //│ Lit(BoolLit(true)) => //│ let x5 = 4 in //│ jump j1(x5) //│ _ => //│ let x6 = 5 in //│ jump j1(x6) //│ def j1(tmp1) = //│ tmp1 //│ def entry() = //│ let* (x7) = f5() in //│ x7 //│ entry = entry //│ //│ Interpreted: //│ 5 // :fixme // wat :sllir fun test() = if true do test() //│ LLIR: //│ //│ def test() = //│ let x = true in //│ case x of //│ Lit(BoolLit(true)) => //│ let* (x1) = test() in //│ undefined //│ _ => //│ undefined //│ def j() = //│ undefined //│ def entry() = //│ undefined //│ entry = entry :sllir fun test() = (if true then test()) + 1 //│ LLIR: //│ //│ def test() = //│ let x = true in //│ case x of //│ Lit(BoolLit(true)) => //│ let* (x1) = test() in //│ let x2 = +(x1,1) in //│ x2 //│ _ => //│ panic "match error" //│ def j() = //│ undefined //│ def entry() = //│ undefined //│ entry = entry :sllir :intl fun f() = let x = 10 if true do set x += 1 x f() //│ = 11 //│ LLIR: //│ //│ def f() = //│ let x = 10 in //│ let x1 = true in //│ case x1 of //│ Lit(BoolLit(true)) => //│ let x2 = +(x,1) in //│ x2 //│ _ => //│ x //│ def j() = //│ undefined //│ def entry() = //│ let* (x3) = f() in //│ x3 //│ entry = entry //│ //│ Interpreted: //│ 11 :sllir :intl data class A(x) data class B(y) fun f(a) = let t = if a is A(_) then 1 B(_) then 2 t f(A(1)) //│ = 1 //│ LLIR: //│ class A(x) //│ class B(y) //│ def f(a) = //│ case a of //│ Class(class:A) => //│ let x = a. in //│ let x1 = 1 in //│ jump j(x1) //│ Class(class:B) => //│ let x2 = a. in //│ let x3 = 2 in //│ jump j(x3) //│ _ => //│ panic "match error" //│ def j(tmp) = //│ tmp //│ def entry() = //│ let x4 = A(1) in //│ let* (x5) = f(x4) in //│ x5 //│ entry = entry //│ //│ Interpreted: //│ 1 :sllir :intl :fixme // Started failing since split deduplication was introduced. data class A(x) data class B(y) fun f(a) = let t = if a is A(_) then if a is A(1) then 1 B(_) then 2 B(_) then 3 t f(A(1)) //│ = 1 //│ /!!!\ Uncaught error: scala.NotImplementedError: Label not supported (of class String) ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/Ctor.mls ================================================ :js :llir :cpp object None fun testCtor1() = None :e :ge fun testCtor2() = new None //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference of type None. //│ ║ l.10: fun testCtor2() = new None //│ ║ ^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[COMPILATION ERROR] Unsupported kind of Instantiate //│ Stopped due to an error during the Llir generation class A(x) fun testCtor1() = A(1) fun testCtor2() = new A(1) ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/HigherOrder.mls ================================================ :js :llir :cpp :intl :noTailRec // The LLIR lowering does not yet support Label blocks //│ //│ Interpreted: //│ undefined // :ge :sllir fun add(x) = y => x + y fun add_curried(x)(y) = x + y add(1)(2) //│ = 3 //│ LLIR: //│ class Lambda_lambda(lam_arg0) extends Callable { //│ def apply1(y) = //│ let x = +(lam_arg0,y) in //│ x //│ } //│ class Lambda(lam_arg0) extends Callable { //│ def apply1(y1) = //│ let x1 = +(lam_arg01,y1) in //│ x1 //│ } //│ def add(x2) = //│ let x3 = Lambda_lambda(x2) in //│ x3 //│ def add_curried(x4) = //│ let x5 = Lambda(x4) in //│ x5 //│ def entry() = //│ let* (x6) = add(1) in //│ let x7 = Callable.apply1(x6,2) in //│ x7 //│ entry = entry //│ //│ Interpreted: //│ 3 // :ge fun add4(a, b) = (c, d) => a + b + c + d fun add4_curried(a, b)(c, d) = a + b + c + d add4(1, 2)(3, 4) //│ = 10 //│ //│ Interpreted: //│ 10 // :ge fun add(a, b) = a + b fun dummy() = add dummy()(1, 2) //│ = 3 //│ //│ Interpreted: //│ 3 abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List fun map(f, l) = if l is Cons(h, t) then Cons(f(h), map(f, t)) Nil then Nil fun inc(x) = x + 1 fun main() = map(x => inc(x), 1 :: 2 :: Nil) map(inc, 3 :: 4 :: Nil) main() //│ = Cons(4, Cons(5, Nil)) //│ //│ Interpreted: //│ Cons(4,Cons(5,Nil())) abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List fun nott(c) = if c then false else true fun filter(f, ls) = if ls is Nil then Nil h :: t and f(h) then h :: filter(f, t) else filter(f, t) fun nubBy(eq, ls) = if ls is Nil then Nil h :: t then h :: nubBy(eq, filter(y => nott(eq(h, y)), t)) nubBy((x, y) => x === y, 1 :: 2 :: 3 :: 3 :: Nil) //│ = Cons(1, Cons(2, Cons(3, Nil))) //│ //│ Interpreted: //│ Cons(1,Cons(2,Cons(3,Nil()))) :intl fun f(x) = fun self_rec(x) = if x === 0 then 0 else x + self_rec(x - 1) self_rec(x) f(3) //│ = 6 //│ //│ Interpreted: //│ 6 fun f(x) = fun even(x) = fun odd(x) = if x === 0 then true else if x === 1 then false else even(x - 1) if x === 0 then true else if x === 1 then false else odd(x - 1) even(x) f(3) //│ = false //│ //│ Interpreted: //│ false ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/Lazy.mls ================================================ :llir // This should ideally be a declaration. // Now it is a built-in class specially handled in the C++ backend, // with related logic defined in `mlsprelude.h`. abstract class Lazy[out A](init: () -> A) with fun get: A fun lazy(x) = Lazy(x) fun force(x) = if x is Lazy then x.Lazy#get() type LazyList[out T] = Lazy[LzList[T]] abstract class LzList[out T]: LzCons[T] | LzNil data class LzCons[out T](head: T, tail: LazyList[T]) extends LzList[T] object LzNil extends LzList :sllir :scpp fun side_effect() = console.log("executed") 1 fun main() = let x = lazy(() => side_effect()) let y = force(x) let y1 = force(x) // force again, but should not execute side_effect again () main() //│ LLIR: //│ class Lambda_lambda() extends Callable { //│ def apply0() = //│ let* (x) = side_effect() in //│ x //│ } //│ def side_effect() = //│ let* (x1) = $_builtin$_("println","executed") in //│ 1 //│ def main() = //│ let x2 = Lambda_lambda() in //│ let* (x3) = lazy(x2) in //│ let* (x4) = force(x3) in //│ let* (x5) = force(x3) in //│ undefined //│ def entry() = //│ let* (x6) = main() in //│ x6 //│ entry = entry //│ //│ Cpp: //│ #include "mlsprelude.h" //│ struct _mls_Lambda_lambda; //│ _mlsValue _mls_side_effect(); //│ _mlsValue _mls_main(); //│ _mlsValue _mls_entry(); //│ struct _mls_Lambda_lambda: public _mls_Callable { //│ //│ constexpr static inline const char *typeName = "Lambda_lambda"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); } //│ virtual void destroy() override { operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Lambda_lambda; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } //│ virtual _mlsValue _mls_apply0(); //│ }; //│ _mlsValue _mls_side_effect() { //│ _mlsValue _mls_retval; //│ auto _mls_x1 = _mls_builtin_println(_mlsValue::create<_mls_Str>("executed")); //│ _mls_retval = _mlsValue::fromIntLit(1); //│ return _mls_retval; //│ } //│ _mlsValue _mls_main() { //│ _mlsValue _mls_retval; //│ auto _mls_x2 = _mlsValue::create<_mls_Lambda_lambda>(); //│ auto _mls_x3 = _mls_lazy(_mls_x2); //│ auto _mls_x4 = _mls_force(_mls_x3); //│ auto _mls_x5 = _mls_force(_mls_x3); //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry() { //│ _mlsValue _mls_retval; //│ auto _mls_x6 = _mls_main(); //│ _mls_retval = _mls_x6; //│ return _mls_retval; //│ } //│ _mlsValue _mls_Lambda_lambda::_mls_apply0() { //│ _mlsValue _mls_retval; //│ auto _mls_x = _mls_side_effect(); //│ _mls_retval = _mls_x; //│ return _mls_retval; //│ } //│ _mlsValue _mlsMain() { return _mls_entry(); } //│ int main() { return _mlsLargeStack(_mlsMainWrapper); } ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/LazyCycle.mls ================================================ :llir // This should ideally be a declaration. // Now it is a built-in class specially handled in the C++ backend, // with related logic defined in `mlsprelude.h`. abstract class Lazy[out A](init: () -> A) with fun get: A fun lazy(x) = Lazy(x) fun force(x) = if x is Lazy then x.Lazy#get() type LazyList[out T] = Lazy[LzList[T]] abstract class LzList[out T]: LzCons[T] | LzNil data class LzCons[out T](head: T, tail: LazyList[T]) extends LzList[T] object LzNil extends LzList :sllir :showWholeCpp fun llist(x) = fun f(x) = lazy(() => LzCons(x, f(x + 1))) f(x) llist(1) //│ LLIR: //│ class Lambda_lambda(lam_arg0,lam_arg1) extends Callable { //│ def apply0() = //│ let x = +(lam_arg0,1) in //│ let x1 = Callable.apply1(lam_arg1,x) in //│ let x2 = LzCons(lam_arg0,x1) in //│ x2 //│ } //│ class Lambda_f() extends Callable { //│ def apply1(x3) = //│ let x4 = Lambda_lambda(x3,$_this$_) in //│ let* (x5) = lazy(x4) in //│ x5 //│ } //│ def llist(x6) = //│ let x7 = Lambda_f() in //│ let x8 = Callable.apply1(x7,x6) in //│ x8 //│ def entry() = //│ let* (x9) = llist(1) in //│ x9 //│ entry = entry //│ //│ WholeProgramCpp: //│ #include "mlsprelude.h" //│ struct _mls_Lambda_f; //│ struct _mls_Lambda_lambda; //│ struct _mls_Lazy; //│ struct _mls_LzList; //│ struct _mls_LzCons; //│ struct _mls_LzNil; //│ _mlsValue _mls_entry2(); //│ _mlsValue _mls_entry(); //│ _mlsValue _mls_llist(_mlsValue); //│ _mlsValue _mls_force(_mlsValue); //│ _mlsValue _mls_lazy(_mlsValue); //│ _mlsValue _mls_j(); //│ _mlsValue _mls_entry1(); //│ struct _mls_Lambda_f: public _mls_Callable { //│ //│ constexpr static inline const char *typeName = "Lambda_f"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); } //│ virtual void destroy() override { operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Lambda_f; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } //│ virtual _mlsValue _mls_apply1(_mlsValue); //│ }; //│ struct _mls_Lambda_lambda: public _mls_Callable { //│ _mlsValue _mls_lam_arg0; //│ _mlsValue _mls_lam_arg1; //│ constexpr static inline const char *typeName = "Lambda_lambda"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); std::printf("("); this->_mls_lam_arg0.print(); std::printf(", "); this->_mls_lam_arg1.print(); std::printf(")"); } //│ virtual void destroy() override { _mlsValue::destroy(this->_mls_lam_arg0); _mlsValue::destroy(this->_mls_lam_arg1); operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create(_mlsValue _mls_lam_arg0, _mlsValue _mls_lam_arg1) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Lambda_lambda; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->_mls_lam_arg0 = _mls_lam_arg0; _mlsVal->_mls_lam_arg1 = _mls_lam_arg1; return _mlsValue(_mlsVal); } //│ virtual _mlsValue _mls_apply0(); //│ }; //│ struct _mls_LzList: public _mlsObject { //│ //│ constexpr static inline const char *typeName = "LzList"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); } //│ virtual void destroy() override { operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_LzList; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } //│ //│ }; //│ struct _mls_LzCons: public _mls_LzList { //│ _mlsValue _mls_head; //│ _mlsValue _mls_tail; //│ constexpr static inline const char *typeName = "LzCons"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); std::printf("("); this->_mls_head.print(); std::printf(", "); this->_mls_tail.print(); std::printf(")"); } //│ virtual void destroy() override { _mlsValue::destroy(this->_mls_head); _mlsValue::destroy(this->_mls_tail); operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create(_mlsValue _mls_head, _mlsValue _mls_tail) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_LzCons; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->_mls_head = _mls_head; _mlsVal->_mls_tail = _mls_tail; return _mlsValue(_mlsVal); } //│ //│ }; //│ struct _mls_LzNil: public _mls_LzList { //│ //│ constexpr static inline const char *typeName = "LzNil"; //│ constexpr static inline uint32_t typeTag = nextTypeTag(); //│ virtual void print() const override { std::printf("%s", typeName); } //│ virtual void destroy() override { operator delete (this, std::align_val_t(_mlsAlignment)); } //│ static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_LzNil; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } //│ //│ }; //│ _mlsValue _mls_j() { //│ _mlsValue _mls_retval; //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry() { //│ _mlsValue _mls_retval; //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mls_lazy(_mlsValue _mls_x7) { //│ _mlsValue _mls_retval; //│ auto _mls_x6 = _mlsValue::create<_mls_Lazy>(_mls_x7); //│ _mls_retval = _mls_x6; //│ return _mls_retval; //│ } //│ _mlsValue _mls_llist(_mlsValue _mls_x12) { //│ _mlsValue _mls_retval; //│ auto _mls_x11 = _mlsValue::create<_mls_Lambda_f>(); //│ auto _mls_x13 = _mlsMethodCall<_mls_Callable>(_mls_x11)->_mls_apply1(_mls_x12); //│ _mls_retval = _mls_x13; //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry1() { //│ _mlsValue _mls_retval; //│ auto _mls_x8 = _mls_llist(_mlsValue::fromIntLit(1)); //│ _mls_retval = _mls_x8; //│ return _mls_retval; //│ } //│ _mlsValue _mls_entry2() { //│ _mlsValue _mls_retval; //│ _mls_retval = _mlsValue::create<_mls_Unit>(); //│ return _mls_retval; //│ } //│ _mlsValue _mls_force(_mlsValue _mls_x9) { //│ _mlsValue _mls_retval; //│ if (_mlsValue::isValueOf<_mls_Lazy>(_mls_x9)) { //│ auto _mls_x10 = _mlsMethodCall<_mls_Lazy>(_mls_x9)->_mls_get(); //│ _mls_retval = _mls_x10; //│ } else { //│ throw std::runtime_error("match error"); //│ } //│ return _mls_retval; //│ } //│ _mlsValue _mls_Lambda_f::_mls_apply1(_mlsValue _mls_x1) { //│ _mlsValue _mls_retval; //│ auto _mls_x = _mlsValue::create<_mls_Lambda_lambda>(_mls_x1, _mlsValue(this, _mlsValue::inc_ref_tag{})); //│ auto _mls_x2 = _mls_lazy(_mls_x); //│ _mls_retval = _mls_x2; //│ return _mls_retval; //│ } //│ _mlsValue _mls_Lambda_lambda::_mls_apply0() { //│ _mlsValue _mls_retval; //│ auto _mls_x3 = (_mls_lam_arg0 + _mlsValue::fromIntLit(1)); //│ auto _mls_x4 = _mlsMethodCall<_mls_Callable>(_mls_lam_arg1)->_mls_apply1(_mls_x3); //│ auto _mls_x5 = _mlsValue::create<_mls_LzCons>(_mls_lam_arg0, _mls_x4); //│ _mls_retval = _mls_x5; //│ return _mls_retval; //│ } //│ _mlsValue _mlsMain() { return _mls_entry1(); } //│ int main() { return _mlsLargeStack(_mlsMainWrapper); } ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/LlirScratch.mls ================================================ :js :llir :global :d ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/Method.mls ================================================ :js :llir :cpp :sllir class A(m) with fun f() = m fun main() = let a = A(1) a.A#f() main() //│ = 1 //│ LLIR: //│ class A(m) { //│ def f() = //│ m //│ } //│ def main() = //│ let x = A(1) in //│ let x1 = A.f(x) in //│ x1 //│ def entry() = //│ let* (x2) = main() in //│ x2 //│ entry = entry ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/Split.mls ================================================ :llir :todo // Started failing after the addition of lazy spreads (#318) :intl abstract class Iter[T] object Done data class Yield(n) data class Map(f, it) fun done(it) = if it is Done then true else false fun map(f, it) = Map(f, it) fun dec(n) = Yield(n) fun next(it) = if it is Yield(n) then if n == 0 then [0, Done] else [n, Yield(n - 1)] Map(f, it) then if next(it) is [n, it] then if done(it) then [f(n), Done] else [f(n), Map(f, it)] fun fold(acc, it) = if next(it) is [n, it] then if done(it) then acc else fold(n + acc, it) fun map_sum(f, n) = let it = map(f, dec(n)) in fold(0, it) map_sum(x => x, 200) //│ ═══[COMPILATION ERROR] Unsupported kind of Call Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(tmp:scrut,None)), Arg(None,Lit(IntLit(0)))))) //│ Stopped due to an error during the Llir generation :intl abstract class Iter[T] object Done data class Yield(n) data class Map(f, it) fun done(it) = if it is Done then true else false fun map(f, it) = Map(f, it) fun dec(n) = Yield(n) fun next(it) = if it is Yield(n) then if n == 0 then [0, Done] else [n, Yield(n - 1)] Map(f, it) then if next(it) is [n, it] then if done(it) then [f(n), Done] else [f(n), Map(f, it)] fun fold(acc, iter) = if iter is Yield(n) then if n == 0 then acc else fold(n + acc, Yield(n - 1)) Map(f, it) then if next(it) is [n, it] then if it is Done then acc else fold(f(n) + acc, Map(f, it)) fun map_sum(f, n) = let it = map(f, dec(n)) in fold(0, it) map_sum(x => x, 200) //│ ═══[COMPILATION ERROR] Unsupported kind of Call Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(tmp:scrut,None)), Arg(None,Lit(IntLit(0)))))) //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/Tuple.mls ================================================ :js :llir :cpp :todo // Started failing after the addition of lazy spreads (#318) :intl :sllir :scpp fun mkTup(x, y) = [x, y] fun fst(t) = if t is [x, y] then x mkTup(1, 2) //│ = [1, 2] //│ ═══[COMPILATION ERROR] Unsupported kind of Call Call(Select(Select(Ref(tmp:runtime,None),Ident(Tuple)),Ident(get)),List(List(Arg(None,Ref(t,None)), Arg(None,Lit(IntLit(0)))))) //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/nofib/NofibPrelude.mls ================================================ // Alternative from the standard operator fun nott(c) = if c then false else true type Char = String abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option // This should ideally be a declaration. // Now it is a built-in class specially handled in the C++ backend, // with related logic defined in `mlsprelude.h`. abstract class Lazy[out A](init: () -> A) with fun get() fun lazy(x) = Lazy(x) fun force(x) = if x is Lazy then x.Lazy#get() fun fromSome(s) = if s is Some(x) then x abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List fun ltList(xs, ys, lt, gt) = if xs is Nil and ys is Nil then false else true x :: xs and ys is Nil then false y :: ys and lt(x, y) then true gt(x, y) then false else ltList(xs, ys, lt, gt) type LazyList[out T] = Lazy[LzList[T]] abstract class LzList[out T]: LzCons[T] | LzNil data class LzCons[out T](head: T, tail: LazyList[T]) extends LzList[T] object LzNil extends LzList fun ltTup2(t1, t2, lt1, gt1, lt2) = if t1 is [a, b] and t2 is [c, d] and lt1(a, c) then true gt1(a, c) then false else lt2(b, d) fun eqTup2(t1, t2) = if t1 is [a, b] and t2 is [c, d] then a == c and b == d fun compose(f, g) = x => f(g(x)) fun snd(x) = if x is [f, s] then s fun fst(x) = if x is [f, s] then f fun until(p, f, i) = if p(i) then i else until(p, f, f(i)) fun flip(f, x, y) = f(y)(x) fun power(a, n) = Math.pow(a, n) fun intDiv(a, b) = globalThis.builtin("floor_div", a, b) fun intQuot(a, b) = globalThis.builtin("trunc_div", a, b) fun intMod(a, b) = globalThis.builtin("floor_mod", a, b) fun intRem(a, b) = globalThis.builtin("trunc_mod", a, b) fun quotRem(a, b) = [intQuot(a, b), intRem(a, b)] fun divMod(a, b) = [intDiv(a, b), intMod(a, b)] fun head(l) = if l is h :: t then h fun tail(l) = if l is h :: t then t fun while_(p, f, x) = if p(x) then while_(p, f, f(x)) else x fun reverse(l) = fun r(l', l) = if l is x :: xs then r(x :: l', xs) else l' r(Nil, l) fun map(f, xs) = if xs is x :: xs then f(x) :: map(f, xs) Nil then Nil fun listLen(ls) = fun l(ls, a) = if ls is Nil then a h :: t then l(t, a + 1) l(ls, 0) fun listEq(xs, ys) = if xs is Nil and ys is Nil then true xs is hx :: tx and ys is hy :: ty and (hx == hy) then listEq(tx, ty) else false fun listEqBy(f, a, b) = if a is Nil and b is Nil then true x :: xs and b is y :: ys then f(x, y) and listEqBy(f, xs, ys) else false fun listNeq(xs, ys) = if xs is Nil and ys is Nil then false xs is hx :: tx and ys is hy :: ty and (hx == hy) then listNeq(tx, ty) else true fun enumFromTo(a, b) = if a <= b then a :: enumFromTo(a + 1, b) else Nil fun enumFromThenTo(a, t, b) = if a <= b then a :: enumFromThenTo(t, 2 * t - a, b) else Nil fun leave(n, ls) = if ls is Nil then Nil h :: t and n <= 0 then ls else leave(n - 1, t) fun take(n, ls) = if ls is Nil then Nil h :: t and n <= 0 then Nil else h :: take(n - 1, t) fun splitAt(n, ls) = [take(n, ls), leave(n, ls)] fun zip(xs, ys) = if xs is x :: xs and ys is y :: ys then [x, y] :: zip(xs, ys) else Nil fun inList(x, ls) = if ls is h :: t and x === h then true else inList(x, t) Nil then false fun notElem(x, ls) = nott(inList(x, ls)) fun (+:) append(xs, ys) = if xs is Nil then ys x :: xs then x :: append(xs, ys) fun concat(ls) = if ls is Nil then Nil x :: xs then append(x, concat(xs)) fun filter(f, ls) = if ls is Nil then Nil h :: t and f(h) then h :: filter(f, t) else filter(f, t) fun filterCurried(f)(ls) = if ls is Nil then Nil h :: t and f(h) then h :: filter(f, t) else filter(f, t) fun all(p, ls) = if ls is Nil then true h :: t and p(h) then all(p, t) else false fun orList(ls) = if ls is Nil then false h :: t and h then true else orList(t) fun leaveWhile(f, ls) = if ls is Nil then Nil h :: t and f(h) then leaveWhile(f, t) else h :: t fun foldl(f, a, xs) = if xs is Nil then a h :: t then foldl(f, f(a, h), t) fun scanl(f, q, ls) = if ls is Nil then q :: Nil x :: xs then q :: scanl(f, f(q, x), xs) fun scanr(f, q, ls) = if ls is Nil then q :: Nil x :: xs and scanr(f, q, xs) is q :: t then f(x, q) :: q :: t fun foldr(f, z, xs) = if xs is Nil then z h :: t then f(h, foldr(f, z, t)) fun foldl1(f, ls) = if ls is x :: xs then foldl(f, x, xs) fun foldr1(f, ls) = if ls is x :: Nil then x x :: xs then f(x, foldr1(f, xs)) fun maximum(xs) = foldl1((x, y) => if x > y then x else y, xs) fun nubBy(eq, ls) = if ls is Nil then Nil h :: t then h :: nubBy(eq, filter(y => nott(eq(h, y)), t)) fun zipWith(f, xss, yss) = if xss is x :: xs and yss is y :: ys then f(x, y) :: zipWith(f, xs, ys) else Nil fun deleteBy(eq, x, ys) = if ys is Nil then Nil y :: ys and eq(x, y) then ys else y :: deleteBy(eq, x, ys) fun unionBy(eq, xs, ys) = append(xs, foldl((acc, y) => deleteBy(eq, y, acc), nubBy(eq, ys), xs)) fun union(xs, ys) = unionBy((x, y) => x == y, xs, ys) fun atIndex(i, ls) = if ls is h :: t and i == 0 then h else atIndex(i - 1, t) fun sum(xs) = fun go(xs, a) = if xs is Nil then a h :: t then go(t, a + h) go(xs, 0) fun null_(ls) = if ls is Nil then true else false fun replicate(n, x) = if n == 0 then Nil else x :: replicate(n - 1, x) fun unzip(l) = fun f(l, a, b) = if l is Nil then [reverse(a), reverse(b)] [x, y] :: t then f(t, x :: a, y :: b) f(l, Nil, Nil) fun zip3(xs, ys, zs) = if xs is x :: xs and ys is y :: ys and zs is z :: zs then [x, y, z] :: zip3(xs, ys, zs) else Nil fun transpose(xss) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is hd :: tl then [hd, tl] :: lscomp(t) else lscomp(t) fun combine(y, h, ys, t) = (y :: h) :: transpose(ys :: t) if xss is Nil then Nil Nil :: xss then transpose(xss) (x :: xs) :: xss and unzip(lscomp(xss)) is [hds, tls] then combine(x, hds, xs, tls) fun break_(p, ls) = if ls is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] break_(p, xs) is [ys, zs] then [x :: ys, zs] fun flatMap(f, ls) = if ls is Nil then Nil h :: t then append(f(h), flatMap(f, t)) // ===================== fun map_lz(f, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(f(h), map_lz(f, t)) fun filter_lz(p, ls) = Lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) and p(h) then LzCons(h, filter_lz(p, t)) else force(filter_lz(p, t)) fun nubBy_lz(eq, ls) = Lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(h, nubBy_lz(eq, filter_lz(y => nott(eq(h, y)), t))) fun nub_lz(ls) = nubBy_lz((x, y) => x == y, ls) fun take_lz(n, ls) = if n > 0 and force(ls) is LzNil then Nil LzCons(h, t) then h :: take_lz(n - 1, t) else Nil fun take_lz_lz(n, ls) = lazy of () => if n > 0 and force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(h, take_lz_lz(n - 1, t)) else LzNil fun leave_lz(n, ls) = if n <= 0 then ls force(ls) is LzNil then lazy of () => LzNil LzCons(h, t) then leave_lz(n - 1, t) fun splitAt_lz(n, ls) = [take_lz(n, ls), leave_lz(n, ls)] fun zip_lz_nl(xs, ys) = if force(xs) is LzCons(x, xs) and ys is y :: ys then [x, y] :: zip_lz_nl(xs, ys) else Nil fun zip_lz_lz(xs, ys) = if force(xs) is LzCons(x, xs) and force(ys) is LzCons(y, ys) then lazy of () => LzCons([x, y], zip_lz_lz(xs, ys)) else lazy of () => LzNil fun zipWith_lz_lz(f, xss, yss) = lazy of () => if force(xss) is LzCons(x, xs) and (force(yss)) is LzCons(y, ys) then LzCons(f(x, y), zipWith_lz_lz(f, xs, ys)) else LzNil fun zipWith_lz_nl(f, xss, yss) = if force(xss) is LzCons(x, xs) and yss is y :: ys then f(x, y) :: zipWith_lz_nl(f, xs, ys) else Nil fun iterate(f, x) = lazy of () => LzCons(x, iterate(f, f(x))) fun append_nl_lz(xs, ys) = if xs is Nil then ys h :: t then lazy of () => LzCons(h, append_nl_lz(t, ys)) fun append_lz_lz(xs, ys) = lazy of () => if force(xs) is LzNil then force(ys) LzCons(h, t) then LzCons(h, append_lz_lz(t, ys)) fun replicate_lz(n, x) = if n == 0 then lazy of () => LzNil else lazy of () => LzCons(x, replicate_lz(n - 1, x)) fun enumFrom(a) = lazy of () => LzCons(a, enumFrom(a + 1)) fun head_lz(ls) = if force(ls) is LzCons(h, t) then h fun repeat(x) = lazy of () => LzCons(x, repeat(x)) // ===================== fun stringOfInt(x) = globalThis.builtin("int2str", x) fun stringOfFloat(x) = globalThis.builtin("float2str", x) fun stringConcat(x, y) = globalThis.builtin("str_concat", x, y) fun stringListConcat(ls) = if ls is Nil then "" h :: t then stringConcat(h, stringListConcat(t)) fun print(x) = globalThis.builtin("println", x) fun abs(x) = globalThis.Math.abs(x) fun floatOfInt(x) = globalThis.builtin("int2float", x) // fun max(a, b) = Math.min(a, b) // fun min(a, b) = Math.max(a, b) // fun abs(x) = Math.abs(x) // fun sqrt(x) = Math.sqrt(x) // fun tan(x) = Math.tan(x) // fun sin(x) = Math.sin(x) // fun cos(x) = Math.cos(x) // fun round(x) = Math.round(x) ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/nofib/atom.mls ================================================ :llir :todo // Started failing after the addition of lazy spreads (#318) // The errors are nondeterministic, producing useless diffs, so I disabled the whole file for now :exit ==================================================================================================== :import NofibPrelude.mls //│ Imported 104 member(s) //│ Error: hkmc2.ErrorReport: Unsupported kind of Call Call(Select(Select(Ref($runtime),Ident(Tuple)),Ident(get)),List(Arg(None,Ref(t1)), Arg(None,Lit(IntLit(0))))) //│ Stopped due to an error during the Llir generation data class State(position: List[Num], velocity: List[Num]) fun dotPlus(fs, gs) = if fs is Nil then gs gs is Nil then fs fs is f :: fs and gs is g :: gs then (f + g) :: dotPlus(fs, gs) fun dotMult(fs, gs) = if fs is f :: fs and gs is g :: gs then (f * g) :: dotMult(fs, gs) else Nil fun scalarMut(c, fs) = if fs is Nil then Nil f :: fs then (c * f) :: scalarMut(c, fs) fun testforce(k, ss) = lazy of () => if force(ss) is LzCons(State(pos, vel), atoms) then LzCons(dotMult(scalarMut(-1.0, k), pos), testforce(k, atoms)) //│ ═══[COMPILATION ERROR] Name not found: member:force //│ Stopped due to an error during the Llir generation fun show(s) = fun lscomp(ls) = if ls is Nil then Nil component :: t then Cons(stringConcat(stringOfFloat(component), "\t"), lscomp(t)) if s is State(pos, vel) then stringListConcat of lscomp(pos) //│ ═══[COMPILATION ERROR] Name not found: member:stringConcat //│ Stopped due to an error during the Llir generation fun propagate(dt, aforce, state) = if state is State(pos, vel) then State(dotPlus(pos, scalarMut(dt, vel)), dotPlus(vel, scalarMut(dt, aforce))) fun runExperiment(law, dt, param, init) = lazy of () => let stream = runExperiment(law, dt, param, init) LzCons(init, zipWith_lz_lz((x, y) => propagate(dt, x, y), law(param, stream), stream)) //│ ═══[COMPILATION ERROR] Name not found: member:zipWith_lz_lz //│ Stopped due to an error during the Llir generation fun testAtom_nofib(n) = fun lscomp(ls) = if ls is Nil then Nil state :: t then stringConcat(show(state), "\n") :: lscomp(t) stringListConcat of lscomp(take_lz(n, runExperiment(testforce, 0.02, 1.0 :: Nil, State(1.0 :: Nil, 0.0 :: Nil)))) //│ ═══[COMPILATION ERROR] Name not found: member:show //│ Stopped due to an error during the Llir generation testAtom_nofib(20) //│ ═══[COMPILATION ERROR] Name not found: member:testAtom_nofib //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/nofib/awards.mls ================================================ :llir :todo // Started failing after the addition of lazy spreads (#318) // The errors are nondeterministic, producing useless diffs, so I disabled the whole file for now :exit ==================================================================================================== :import NofibPrelude.mls //│ Imported 104 member(s) //│ Error: hkmc2.ErrorReport: Unsupported kind of Call Call(Select(Select(Ref($runtime),Ident(Tuple)),Ident(get)),List(Arg(None,Ref(t1)), Arg(None,Lit(IntLit(0))))) //│ Stopped due to an error during the Llir generation fun delete_(xs, e) = deleteBy((x, y) => x == y, e, xs) //│ ═══[COMPILATION ERROR] Name not found: member:deleteBy //│ Stopped due to an error during the Llir generation fun listDiff(a, ls) = foldl(delete_, a, ls) //│ ═══[COMPILATION ERROR] Name not found: member:foldl //│ Stopped due to an error during the Llir generation :... //│ ———————————————————————————————————————————————————————————————————————————————— fun qsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) fun qpart(le, x, ys, rlt, rge, r) = if ys is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) else qpart(le, x, ys, y :: rlt, rge, r) fun rqsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) fun rqpart(le, x, yss, rle, rgt, r) = if yss is Nil then qsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) else rqpart(le, x, ys, rle, y :: rgt, r) //│ ———————————————————————————————————————————————————————————————————————————————— fun sort(l) = qsort((a, b) => ltTup2(a, b, (a, b) => a < b, (a, b) => a > b, (a, b) => ltList(a, b, (a, b) => a < b, (a, b) => a > b)), l, Nil) //│ ═══[COMPILATION ERROR] Name not found: member:ltList //│ Stopped due to an error during the Llir generation fun perms(m, nns) = if nns is Nil then Nil m == 1 then map(x => x :: Nil, nns) nns is n :: ns then map(x => n :: x, perms(m-1, ns)) +: perms(m, ns) //│ ═══[COMPILATION ERROR] Name not found: member:map //│ Stopped due to an error during the Llir generation fun atleast(threshold, sumscores) = filter(case { [sum_, p] then sum_ >= threshold }, sumscores) //│ ═══[COMPILATION ERROR] Name not found: $runtime //│ Stopped due to an error during the Llir generation fun award(name_threshold, sumscores) = if name_threshold is [name, threshold] then map(ps => [name, ps], sort(atleast(threshold, sumscores))) //│ ═══[COMPILATION ERROR] Unsupported kind of Call Call(Select(Select(Ref($runtime),Ident(Tuple)),Ident(get)),List(Arg(None,Ref(name_threshold)), Arg(None,Lit(IntLit(0))))) //│ Stopped due to an error during the Llir generation fun awards(scores) = let sumscores = map(p => [sum(p), p], perms(3, scores)) award(["Gold", 70], sumscores) +: award(["Silver", 60], sumscores) +: award(["Bronze", 50], sumscores) //│ ═══[COMPILATION ERROR] Name not found: member:perms //│ Stopped due to an error during the Llir generation fun findawards(scores) = if awards(scores) is Nil then Nil head_ :: tail_ and head_ is [award, [sum_, perm]] then [award, [sum_, perm]] :: findawards(listDiff(scores, perm)) //│ ═══[COMPILATION ERROR] Name not found: member:awards //│ Stopped due to an error during the Llir generation fun findallawards(competitors) = map(case { [name, scores] then [name, findawards(scores)] }, competitors) //│ ═══[COMPILATION ERROR] Name not found: $runtime //│ Stopped due to an error during the Llir generation fun competitors(i) = ["Simon", (35 :: 27 :: 40 :: i :: 34 :: 21 :: Nil)] :: ["Hans", (23 :: 19 :: 45 :: i :: 17 :: 10 :: 5 :: 8 :: 14 :: Nil)] :: ["Phil", (1 :: 18 :: i :: 20 :: 21 :: 19 :: 34 :: 8 :: 16 :: 21 :: Nil)] :: ["Kevin", (9 :: 23 :: 17 :: 54 :: i :: 41 :: 9 :: 18 :: 14 :: Nil)] :: Nil fun testAwards_nofib(n) = map(x => print(findallawards(competitors(intMod(x, 100)))), enumFromTo(1, n)) //│ ═══[COMPILATION ERROR] Name not found: member:findallawards //│ Stopped due to an error during the Llir generation testAwards_nofib(100) //│ ═══[COMPILATION ERROR] Name not found: member:testAwards_nofib //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/nofib/constraints.mls ================================================ :llir :todo // Started failing after the addition of lazy spreads (#318) // The errors are nondeterministic, producing useless diffs, so I disabled the whole file for now :exit ==================================================================================================== :import NofibPrelude.mls //│ Imported 104 member(s) //│ Error: hkmc2.ErrorReport: Unsupported kind of Call Call(Select(Select(Ref($runtime),Ident(Tuple)),Ident(get)),List(Arg(None,Ref(t1)), Arg(None,Lit(IntLit(0))))) //│ Stopped due to an error during the Llir generation // -- Figure 1. CSPs in Haskell. data class Assign(varr: Int, value: Int) data class CSP(vars: Int, vals: Int, rel: Int) :... //│ ———————————————————————————————————————————————————————————————————————————————— fun qsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) fun qpart(le, x, ls, rlt, rge, r) = if ls is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) else qpart(le, x, ys, y :: rlt, rge, r) fun rqsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) fun rqpart(le, x, ls, rle, rgt, r) = if ls is Nil then rqsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) else rqpart(le, x, ys, rle, y :: rgt, r) //│ ———————————————————————————————————————————————————————————————————————————————— fun level(a) = if a is Assign(v, _) then v fun value(a) = if a is Assign(_, v) then v fun maxLevel(ls) = if ls is Nil then 0 Assign(v, _) :: t then v fun complete(csp, s) = if csp is CSP(v, _, _) then maxLevel(s) == v fun generate(csp) = fun g(vals, var_) = fun lscomp1(ls) = if ls is Nil then Nil val_ :: t1 then fun lscomp2(ls) = if ls is Nil then lscomp1(t1) st :: t2 then (Assign(var_, val_) :: st) :: lscomp2(t2) lscomp2(g(vals, var_ - 1)) if var_ == 0 then Nil :: Nil else lscomp1(enumFromTo(1, vals)) if csp is CSP(vars, vals, rel) then g(vals, vars) //│ ═══[COMPILATION ERROR] Name not found: member:enumFromTo //│ Stopped due to an error during the Llir generation fun inconsistencies(csp, as_) = if csp is CSP(vars, vals, rel) then fun lscomp1(ls) = if ls is Nil then Nil a :: t1 then fun lscomp2(ls) = if ls is Nil then lscomp1(t1) b :: t2 and a > b and not(rel(a, b)) then [level(a), (b)] :: lscomp2(t2) else lscomp2(t2) lscomp2(reverse(as_)) lscomp1(as_) //│ ═══[COMPILATION ERROR] Name not found: member:not //│ Stopped due to an error during the Llir generation fun consistent(csp)(x) = null_(inconsistencies(csp, x)) //│ ═══[COMPILATION ERROR] Name not found: member:inconsistencies //│ Stopped due to an error during the Llir generation fun test(csp, ls) = filter(consistent(csp), ls) //│ ═══[COMPILATION ERROR] Name not found: member:consistent //│ Stopped due to an error during the Llir generation fun solver(csp) = test(csp, generate(csp)) //│ ═══[COMPILATION ERROR] Name not found: member:generate //│ Stopped due to an error during the Llir generation fun safe(as1, as2) = if as1 is Assign(i, m) and as2 is Assign(j, n) then not(m == n) and not(abs(i - j) == abs(m - n)) //│ ═══[COMPILATION ERROR] Name not found: member:not //│ Stopped due to an error during the Llir generation fun queens(n) = CSP(n, n, safe) //│ ═══[COMPILATION ERROR] Name not found: member:safe //│ Stopped due to an error during the Llir generation // -- Figure 2. Trees in Haskell. data class Node[out T](lab: T, children: List[Node[T]]) fun label(n) = if n is Node(l, _) then l fun mapTree(f, n) = if n is Node(l, c) then Node(f(l), map((x => mapTree(f, x)), c)) //│ ═══[COMPILATION ERROR] Name not found: member:map //│ Stopped due to an error during the Llir generation fun foldTree(f, n) = if n is Node(l, c) then f(l, map((x => foldTree(f, x)), c)) //│ ═══[COMPILATION ERROR] Name not found: member:map //│ Stopped due to an error during the Llir generation fun filterTree(p, t) = fun f1(a, cs) = Node(a, filter(x => p(label(x)), cs)) foldTree(f1, t) //│ ═══[COMPILATION ERROR] Name not found: member:filter //│ Stopped due to an error during the Llir generation fun prune(p, t) = filterTree(x => not(p(x)), t) //│ ═══[COMPILATION ERROR] Name not found: member:not //│ Stopped due to an error during the Llir generation fun leaves(t) = if t is Node(leaf, Nil) then leaf :: Nil Node(_, cs) then concat(map(leaves, cs)) //│ ═══[COMPILATION ERROR] Name not found: member:map //│ Stopped due to an error during the Llir generation fun initTree(f, x) = Node(x, map(y => initTree(f, y), f(x))) //│ ═══[COMPILATION ERROR] Name not found: member:map //│ Stopped due to an error during the Llir generation // -- Figure 3. Simple backtracking solver for CSPs. fun mkTree(csp) = if csp is CSP(vars, vals, rel) then fun next(ss) = if maxLevel(ss) < vars then fun lscomp1(ls) = if ls is Nil then Nil j :: t1 then (Assign(maxLevel(ss) + 1, j) :: ss) :: lscomp1(t1) lscomp1(enumFromTo(1, vals)) else Nil initTree(next, Nil) //│ ═══[COMPILATION ERROR] Name not found: member:enumFromTo //│ Stopped due to an error during the Llir generation fun earliestInconsistency(csp, aas) = if csp is CSP(vars, vals, rel) and aas is Nil then None a :: as_ and filter(x => not(rel(a, x)), reverse(as_)) is Nil then None b :: _ then Some([level(a), level(b)]) //│ ═══[COMPILATION ERROR] Name not found: member:reverse //│ Stopped due to an error during the Llir generation fun labelInconsistencies(csp, t) = fun f2(s) = [s, earliestInconsistency(csp, s)] mapTree(f2, t) //│ ═══[COMPILATION ERROR] Name not found: member:earliestInconsistency //│ Stopped due to an error during the Llir generation fun btsolver0(csp) = filter of x => complete(csp, x) leaves of mapTree of fst prune of x => not(snd(x) is None) labelInconsistencies(csp, mkTree(csp)) //│ ═══[COMPILATION ERROR] Name not found: member:snd //│ Stopped due to an error during the Llir generation // -- Figure 6. Conflict-directed solving of CSPs. abstract class ConflictSet: Known | Unknown data class Known(vs: List[Int]) extends ConflictSet object Unknown extends ConflictSet fun knownConflict(c) = if c is Known(a :: as_) then true else false fun knownSolution(c) = if c is Known(Nil) then true else false fun checkComplete(csp, s) = if complete(csp, s) then Known(Nil) else Unknown fun search(labeler, csp) = map of fst filter of x => knownSolution(snd(x)) leaves of prune of x => knownConflict(snd(x)) labeler(csp, mkTree(csp)) //│ ═══[COMPILATION ERROR] Name not found: member:mkTree //│ Stopped due to an error during the Llir generation fun bt(csp, t) = fun f3(s) = [s, (if earliestInconsistency(csp, s) is Some([a, b]) then Known(a :: b :: Nil) else checkComplete(csp, s))] mapTree(f3, t) //│ ═══[COMPILATION ERROR] Name not found: $runtime //│ Stopped due to an error during the Llir generation // -- Figure 8. Backmarking. fun emptyTable(csp) = if csp is CSP(vars, vals, rel) then fun lscomp1(ls) = if ls is Nil then Nil n :: t1 then fun lscomp2(ls) = if ls is Nil then Nil m :: t2 then Unknown :: lscomp2(t2) lscomp2(enumFromTo(1, vals)) :: lscomp1(t1) Nil :: lscomp1(enumFromTo(1, vars)) //│ ═══[COMPILATION ERROR] Name not found: member:enumFromTo //│ Stopped due to an error during the Llir generation fun fillTable(s, csp, tbl) = if s is Nil then tbl Assign(var_, val_) :: as_ and csp is CSP(vars, vals, rel) then fun f4(cs, varval) = if varval is [varr, vall] and cs is Unknown and not(rel(Assign(var_, val_), Assign(varr, vall))) then Known(var_ :: varr :: Nil) else cs fun lscomp1(ls) = if ls is Nil then Nil varrr :: t1 then fun lscomp2(ls) = if ls is Nil then Nil valll :: t2 then [varrr, valll] :: lscomp2(t2) lscomp2(enumFromTo(1, vals)) :: lscomp1(t1) zipWith((x, y) => zipWith(f4, x, y), tbl, lscomp1(enumFromTo(var_ + 1, vars))) //│ ═══[COMPILATION ERROR] Name not found: member:not //│ Stopped due to an error during the Llir generation fun lookupCache(csp, t) = fun f5(csp, tp) = if tp is [Nil, tbl] then [[Nil, Unknown], tbl] [a :: as_, tbl] then let tableEntry = atIndex(value(a) - 1, head(tbl)) let cs = if tableEntry is Unknown then checkComplete(csp, a :: as_) else tableEntry [[a :: as_, cs], tbl] mapTree(x => f5(csp, x), t) //│ ═══[COMPILATION ERROR] Name not found: member:head //│ Stopped due to an error during the Llir generation fun cacheChecks(csp, tbl, n) = if n is Node(s, cs) then Node([s, tbl], map(x => cacheChecks(csp, fillTable(s, csp, tail(tbl)), x), cs)) //│ ═══[COMPILATION ERROR] Name not found: member:fillTable //│ Stopped due to an error during the Llir generation fun bm(csp, t) = mapTree(fst, lookupCache(csp, cacheChecks(csp, emptyTable(csp), t))) //│ ═══[COMPILATION ERROR] Name not found: member:emptyTable //│ Stopped due to an error during the Llir generation // -- Figure 10. Conflict-directed backjumping. fun combine(ls, acc) = if ls is Nil then acc [s, Known(cs)] :: css and notElem(maxLevel(s), cs) then cs else combine(css, union(cs, acc)) //│ ═══[COMPILATION ERROR] Unsupported kind of Call Call(Select(Select(Ref($runtime),Ident(Tuple)),Ident(get)),List(Arg(None,Ref($param0)), Arg(None,Lit(IntLit(0))))) //│ Stopped due to an error during the Llir generation fun bj_(csp, t) = fun f7(tp2, chs) = if tp2 is [a, Known(cs)] then Node([a, Known(cs)], chs) [a, Unknown] and let cs_ = Known(combine(map(label, chs), Nil)) knownConflict(cs_) then Node([a, cs_], Nil) else Node([a, cs_], chs) foldTree(f7, t) //│ ═══[COMPILATION ERROR] Name not found: $runtime //│ Stopped due to an error during the Llir generation fun bj(csp, t) = fun f6(tp2, chs) = if tp2 is [a, Known(cs)] then Node([a, Known(cs)], chs) [a, Unknown] then Node([a, Known(combine(map(label, chs), Nil))], chs) foldTree(f6, t) //│ ═══[COMPILATION ERROR] Name not found: $runtime //│ Stopped due to an error during the Llir generation fun bjbt(csp, t) = bj(csp, bt(csp, t)) //│ ═══[COMPILATION ERROR] Name not found: member:bt //│ Stopped due to an error during the Llir generation fun bjbt_(csp, t) = bj_(csp, bt(csp, t)) //│ ═══[COMPILATION ERROR] Name not found: member:bt //│ Stopped due to an error during the Llir generation // -- Figure 11. Forward checking. fun collect(ls) = if ls is Nil then Nil Known(cs) :: css then union(cs, collect(css)) //│ ═══[COMPILATION ERROR] Name not found: member:union //│ Stopped due to an error during the Llir generation fun domainWipeout(csp, t) = if csp is CSP(vars, vals, rel) then fun f8(tp2) = if tp2 is [[as_, cs], tbl] then let wipedDomains = fun lscomp1(ls) = if ls is Nil then Nil vs :: t1 and all(knownConflict, vs) then vs :: lscomp1(t1) else lscomp1(t1) lscomp1(tbl) let cs_ = if null_(wipedDomains) then cs else Known(collect(head(wipedDomains))) [as_, cs_] mapTree(f8, t) //│ ═══[COMPILATION ERROR] Name not found: member:head //│ Stopped due to an error during the Llir generation fun fc(csp, t) = domainWipeout(csp, lookupCache(csp, cacheChecks(csp, emptyTable(csp), t))) //│ ═══[COMPILATION ERROR] Name not found: member:emptyTable //│ Stopped due to an error during the Llir generation fun try_(n, algorithm) = listLen(search(algorithm, queens(n))) //│ ═══[COMPILATION ERROR] Name not found: member:queens //│ Stopped due to an error during the Llir generation fun testConstraints_nofib(n) = map(x => try_(n, x), bt :: bm :: bjbt :: bjbt_ :: fc :: Nil) //│ ═══[COMPILATION ERROR] Name not found: member:fc //│ Stopped due to an error during the Llir generation print(testConstraints_nofib(6)) //│ ═══[COMPILATION ERROR] Name not found: member:testConstraints_nofib //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/nofib/scc.mls ================================================ :llir :todo // Started failing after the addition of lazy spreads (#318) :import NofibPrelude.mls //│ Imported 104 member(s) //│ /!!!\ Uncaught error: scala.NotImplementedError: Label not supported (of class String) fun dfs(r, vsns, xs) = if vsns is [vs, ns] and xs is Nil then [vs, ns] x :: xs and inList(x, vs) then dfs(r, [vs, ns], xs) dfs(r, [x :: vs, Nil], r(x)) is [vs', ns'] then dfs(r, [vs', (x :: ns') +: ns], xs) //│ /!!!\ Uncaught error: scala.NotImplementedError: Label not supported (of class String) fun stronglyConnComp(es, vs) = fun swap(a) = if a is [f, s] then [s, f] fun new_range(xys, w) = if xys is Nil then Nil [x, y] :: xys and x == w then y :: new_range(xys, w) else new_range(xys, w) fun span_tree(r, vsns, xs) = if vsns is [vs, ns] and xs is Nil then [vs, ns] x :: xs and inList(x, vs) then span_tree(r, [vs, ns], xs) dfs(r, [x :: vs, Nil], r(x)) is [vs', ns'] then span_tree(r, [vs', (x :: ns') :: ns], xs) snd of span_tree of x => new_range(map(swap, es), x) [Nil, Nil] snd of dfs of x => new_range(es, x) [Nil, Nil] vs //│ ═══[COMPILATION ERROR] Name not found: tmp:runtime //│ Stopped due to an error during the Llir generation fun testScc_nofib(d) = let a = 1 let b = 2 let c = 3 let d = 4 let f = 5 let g = 6 let h = 7 let vertices = a :: b :: c :: d :: f :: g :: h :: Nil let edges = [b, a] :: [c, b] :: [c, d] :: [c, h] :: [d, c] :: [f, a] :: [f, g] :: [f, h] :: [g, f] :: [h, g] :: Nil stronglyConnComp(edges, vertices) //│ ═══[COMPILATION ERROR] Name not found: member:stronglyConnComp //│ Stopped due to an error during the Llir generation print(testScc_nofib(0)) //│ ═══[COMPILATION ERROR] Name not found: member:testScc_nofib //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/llir/nofib/secretary.mls ================================================ :llir :todo // Started failing after the addition of lazy spreads (#318) // The errors are nondeterministic, producing useless diffs, so I disabled the whole file for now :exit ==================================================================================================== :import NofibPrelude.mls //│ Imported 104 member(s) //│ Error: hkmc2.ErrorReport: Unsupported kind of Call Call(Select(Select(Ref($runtime),Ident(Tuple)),Ident(get)),List(Arg(None,Ref(t1)), Arg(None,Lit(IntLit(0))))) //│ Stopped due to an error during the Llir generation fun infRand(m, s) = fun f(x) = lazy(() => LzCons((intMod(x, m) + 1), f(intMod((97 * x + 11), power(2, 7))))) f(s) //│ ═══[COMPILATION ERROR] Name not found: member:lazy //│ Stopped due to an error during the Llir generation fun simulate(n, m, proc) = fun lscomp(ls) = if ls is Nil then Nil seed :: t then proc(infRand(m, seed)) :: lscomp(t) floatOfInt(listLen(filter(x => x, lscomp(enumFromTo(1, n))))) / floatOfInt(n) //│ ═══[COMPILATION ERROR] Name not found: member:infRand //│ Stopped due to an error during the Llir generation fun sim(n, k) = fun proc(rs) = let xs = take_lz(100, nub_lz(rs)) let best = 100 let bestk = maximum(take(k, xs)) let afterk = dropWhile(x => x < bestk, drop(k, xs)) listEq(best :: Nil, take(1, afterk)) simulate(n, 100, proc) //│ ═══[COMPILATION ERROR] Name not found: member:take_lz //│ Stopped due to an error during the Llir generation fun testSecretary_nofib(n) = fun listcomp(ls) = if ls is Nil then Nil h :: t then sim(n, h) :: listcomp(t) listcomp(enumFromTo(35, 39)) //│ ═══[COMPILATION ERROR] Name not found: member:sim //│ Stopped due to an error during the Llir generation print(testSecretary_nofib(50)) //│ ═══[COMPILATION ERROR] Name not found: member:testSecretary_nofib //│ Stopped due to an error during the Llir generation ================================================ FILE: hkmc2/shared/src/test/mlscript/meta/BlockDiffTesting.mls ================================================ :js :... //│ ———————————————————————————————————————————————————————————————————————————————— fun foo() = bar() fun bar() = if false then foo() else () foo() //│ ———————————————————————————————————————————————————————————————————————————————— 42 //│ = 42 :... //│ ———————————————————————————————————————————————————————————————————————————————— print of "Starting..." print of "Hello, world!" print of "Done." //│ > Starting... //│ > Hello, world! //│ > Done. //│ ———————————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/meta/CompilerFiction.mls ================================================ :js :ge source //│ ╔══[COMPILATION ERROR] Module 'source' is virtual (i.e., "compiler fiction"); cannot be used directly //│ ║ l.5: source //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge js //│ ╔══[COMPILATION ERROR] Module 'js' is virtual (i.e., "compiler fiction"); cannot be used directly //│ ║ l.13: js //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e :ge js.oops //│ ╔══[COMPILATION ERROR] Module 'js' does not contain member 'oops' //│ ║ l.21: js.oops //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Module 'js' is virtual (i.e., "compiler fiction"); cannot be used directly //│ ║ l.21: js.oops //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge js.try_catch //│ ╔══[COMPILATION ERROR] Module 'js' is virtual (i.e., "compiler fiction"); cannot be used directly //│ ║ l.31: js.try_catch //│ ╙── ^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re js.try_catch() //│ ═══[RUNTIME ERROR] TypeError: onError is not a function :re js.try_catch(0) //│ ═══[RUNTIME ERROR] TypeError: onError is not a function js.try_catch(0, id) //│ = TypeError: computation is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/meta/ImportedTest.mls ================================================ fun hello() = "Hello!" ================================================ FILE: hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls ================================================ :js :import ImportedTest.mls //│ Imported 1 member(s) :sjs hello() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ hello() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "Hello!" fun hello() = "Hello?" :sjs hello() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ hello1() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "Hello?" ================================================ FILE: hkmc2/shared/src/test/mlscript/meta/LocDebugging.mls ================================================ :js :loc :loc :pt :rt 111 + 222 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp loc[1:1-1:10]: //│ lhs = IntLit loc[1:1-1:4] of 111 //│ op = Ident loc[1:5-1:6] of "+" //│ rhss = Ls of //│ IntLit loc[1:7-1:10] of 222 //│ ——————————————| Resolved tree |————————————————————————————————————————————————————————————————————— //│ Blk loc[1:1-1:10]: //│ res = App loc[1:1-1:10]: //│ lhs = Ref{sym=builtin:+⁰} loc[1:5-1:6] of builtin:+⁰ //│ rhs = Tup loc[1:1-1:10] of Ls of //│ Fld loc[1:1-1:4]: //│ term = Lit loc[1:1-1:4] of IntLit loc[1:1-1:4] of 111 //│ Fld loc[1:7-1:10]: //│ term = Lit loc[1:7-1:10] of IntLit loc[1:7-1:10] of 222 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 333 ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/ansi.mls ================================================ :js import "../../mlscript-compile/nofib/ansi.mls" ansi.main() //│ = "LE[5;17HESC[7mDemonstration programESC[0mE[5;48HVersion 1.0E[7;17HThis program illustrates a simple approachE[8;17Hto screen-based interactive programs usingE[9;17Hthe Hugs functional programming system.E[11;17HPlease press any key to continue ...E[15;17HPlease enter your name: E[15;41H__________________E[15;41HesttesttestE[18;31HHello esttesttest!E[23;1HI'm waiting..." ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/atom.mls ================================================ :js import "../../mlscript-compile/nofib/atom.mls" atom.main() //│ = "1\t\n1\t\n0.9996\t\n0.9988\t\n0.9976001600000001\t\n0.9960008\t\n0.994002399936\t\n0.991605599552\t\n0.9888111982080257\t\n0.9856201546242305\t\n0.982033586561152\t\n0.978052770436224\t\n0.9736791408766714\t\n0.9689142902089444\t\n0.9637599678848666\t\n0.9582180798447053\t\n0.95229068781739\t\n0.9459800085581369\t\n0.9392884130237569\t\n0.9322184254859536\t\n" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/awards.mls ================================================ :js import "../../mlscript-compile/nofib/awards.mls" awards.main() //│ = [[["Simon", [["Gold", [70, [35,1,34]]],["Gold", [88, [27,40,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]]]]],[["Simon", [["Gold", [71, [35,2,34]]],["Gold", [88, [27,40,21]]]]],["Hans", [["Gold", [70, [23,45,2]]],["Bronze", [50, [19,17,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [54,2,14]]],["Gold", [73, [9,23,41]]]]]],[["Simon", [["Gold", [70, [27,40,3]]],["Gold", [90, [35,34,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [71, [54,3,14]]],["Gold", [73, [9,23,41]]]]]],[["Simon", [["Gold", [71, [27,40,4]]],["Gold", [90, [35,34,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]]]]],[["Simon", [["Gold", [72, [27,40,5]]],["Gold", [90, [35,34,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]]]]],[["Simon", [["Gold", [73, [27,40,6]]],["Gold", [90, [35,34,21]]]]],["Hans", [["Gold", [70, [19,45,6]]],["Bronze", [50, [23,17,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [23,6,41]]],["Gold", [72, [9,54,9]]]]]],[["Simon", [["Gold", [74, [27,40,7]]],["Gold", [90, [35,34,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [9,54,7]]],["Gold", [72, [17,41,14]]],["Bronze", [50, [23,9,18]]]]]],[["Simon", [["Gold", [70, [35,27,8]]],["Gold", [95, [40,34,21]]]]],["Hans", [["Gold", [70, [45,8,17]]],["Bronze", [50, [23,19,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [71, [9,54,8]]],["Gold", [72, [17,41,14]]],["Bronze", [50, [23,9,18]]]]]],[["Simon", [["Gold", [70, [27,9,34]]],["Gold", [96, [35,40,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [51, [23,19,9]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Bronze", [50, [23,9,18]]]]]],[["Simon", [["Gold", [71, [27,10,34]]],["Gold", [96, [35,40,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Bronze", [51, [23,10,18]]]]]],[["Simon", [["Gold", [72, [27,11,34]]],["Gold", [96, [35,40,21]]]]],["Hans", [["Gold", [70, [45,11,14]]],["Bronze", [50, [23,17,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [11,41,18]]],["Gold", [72, [9,54,9]]],["Bronze", [54, [23,17,14]]]]]],[["Simon", [["Gold", [73, [27,12,34]]],["Gold", [96, [35,40,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [17,12,41]]],["Gold", [72, [9,54,9]]],["Bronze", [55, [23,18,14]]]]]],[["Simon", [["Gold", [74, [27,13,34]]],["Gold", [96, [35,40,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [50, [23,13,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [71, [17,13,41]]],["Gold", [72, [9,54,9]]],["Bronze", [55, [23,18,14]]]]]],[["Simon", [["Gold", [70, [35,14,21]]],["Gold", [101, [27,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [51, [23,14,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,14,41]]],["Bronze", [55, [23,18,14]]]]]],[["Simon", [["Gold", [70, [15,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,15,10]]],["Bronze", [50, [19,17,14]]]]],["Phil", [["Gold", [70, [15,21,34]]],["Silver", [60, [20,19,21]]]]],["Kevin", [["Gold", [70, [15,41,14]]],["Gold", [72, [9,54,9]]],["Bronze", [58, [23,17,18]]]]]],[["Simon", [["Gold", [71, [16,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [16,20,34]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [71, [16,41,14]]],["Gold", [72, [9,54,9]]],["Bronze", [58, [23,17,18]]]]]],[["Simon", [["Gold", [72, [17,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [50, [19,17,14]]]]],["Phil", [["Gold", [70, [17,19,34]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Bronze", [58, [23,17,18]]]]]],[["Simon", [["Gold", [73, [18,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [60, [23,19,18]]]]],["Phil", [["Gold", [70, [18,18,34]]],["Silver", [60, [20,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Bronze", [59, [23,18,18]]]]]],[["Simon", [["Gold", [74, [19,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [61, [23,19,19]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [60, [23,19,18]]]]]],[["Simon", [["Gold", [75, [20,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [62, [23,19,20]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [9,20,41]]],["Gold", [77, [54,9,14]]],["Bronze", [58, [23,17,18]]]]]],[["Simon", [["Gold", [76, [21,34,21]]],["Gold", [102, [35,27,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [63, [23,19,21]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [71, [9,21,41]]],["Gold", [77, [54,9,14]]],["Bronze", [58, [23,17,18]]]]]],[["Simon", [["Gold", [70, [27,22,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [64, [23,19,22]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,22,41]]],["Gold", [77, [54,9,14]]],["Bronze", [58, [23,17,18]]]]]],[["Simon", [["Gold", [71, [27,23,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [60, [23,23,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]],["Bronze", [50, [23,19,8]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [64, [23,23,18]]]]]],[["Simon", [["Gold", [72, [27,24,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [61, [23,24,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]],["Bronze", [51, [24,19,8]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [65, [23,24,18]]]]]],[["Simon", [["Gold", [73, [27,25,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [62, [23,25,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]],["Bronze", [52, [25,19,8]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [66, [23,25,18]]]]]],[["Simon", [["Gold", [74, [27,26,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [63, [23,26,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]],["Bronze", [53, [26,19,8]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [67, [23,26,18]]]]]],[["Simon", [["Gold", [75, [27,27,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Silver", [60, [19,27,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]],["Bronze", [54, [27,19,8]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [68, [23,27,18]]]]]],[["Simon", [["Gold", [76, [27,28,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [23,19,28]]],["Gold", [70, [45,17,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [70, [28,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Silver", [69, [23,28,18]]]]]],[["Simon", [["Gold", [77, [27,29,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [71, [23,19,29]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [71, [29,21,21]]]]],["Kevin", [["Gold", [70, [23,29,18]]],["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]]]]],[["Simon", [["Gold", [78, [27,30,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [23,30,17]]],["Gold", [72, [19,45,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [70, [30,19,21]]]]],["Kevin", [["Gold", [70, [23,17,30]]],["Gold", [72, [9,54,9]]],["Gold", [73, [41,18,14]]]]]],[["Simon", [["Gold", [79, [27,31,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [23,19,31]]]]],["Phil", [["Gold", [70, [18,31,21]]],["Gold", [70, [20,34,16]]]]],["Kevin", [["Gold", [71, [23,17,31]]],["Gold", [72, [9,54,9]]],["Gold", [73, [41,18,14]]]]]],[["Simon", [["Gold", [80, [27,32,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [74, [23,19,32]]]]],["Phil", [["Gold", [70, [18,32,20]]],["Gold", [71, [21,34,16]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [73, [23,32,18]]]]]],[["Simon", [["Gold", [81, [27,33,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [23,33,14]]],["Gold", [70, [45,17,8]]]]],["Phil", [["Gold", [70, [18,33,19]]],["Gold", [70, [20,34,16]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [70, [23,33,14]]],["Gold", [72, [9,54,9]]],["Gold", [76, [17,41,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [109, [35,40,34]]]]],["Hans", [["Gold", [70, [19,34,17]]],["Gold", [73, [23,45,5]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [71, [18,19,34]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [71, [23,34,14]]],["Gold", [72, [9,54,9]]],["Gold", [76, [17,41,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [110, [35,40,35]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [72, [23,35,14]]]]],["Phil", [["Gold", [70, [1,35,34]]],["Silver", [60, [18,21,21]]],["Bronze", [55, [20,19,16]]]]],["Kevin", [["Gold", [70, [17,35,18]]],["Gold", [72, [9,54,9]]],["Gold", [78, [23,41,14]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [111, [35,40,36]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [23,36,14]]]]],["Phil", [["Gold", [70, [18,36,16]]],["Gold", [73, [20,19,34]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [71, [17,36,18]]],["Gold", [72, [9,54,9]]],["Gold", [78, [23,41,14]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [112, [35,40,37]]]]],["Hans", [["Gold", [70, [19,37,14]]],["Gold", [70, [45,17,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [74, [18,37,19]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,37,18]]],["Gold", [78, [23,41,14]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [113, [35,40,38]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [71, [19,38,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [75, [18,38,19]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [70, [9,23,38]]],["Gold", [72, [17,41,14]]],["Gold", [81, [54,9,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [114, [35,40,39]]]]],["Hans", [["Gold", [70, [23,39,8]]],["Gold", [72, [45,17,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [76, [18,39,19]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [70, [17,39,14]]],["Gold", [72, [9,54,9]]],["Gold", [82, [23,41,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [115, [35,40,40]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [19,40,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [77, [18,40,19]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [71, [17,40,14]]],["Gold", [72, [9,54,9]]],["Gold", [82, [23,41,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [116, [35,40,41]]]]],["Hans", [["Gold", [70, [19,41,10]]],["Gold", [70, [45,17,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [70, [41,8,21]]],["Bronze", [58, [18,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [82, [23,41,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [117, [35,40,42]]]]],["Hans", [["Gold", [70, [23,42,5]]],["Gold", [70, [45,17,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [71, [42,8,21]]],["Bronze", [58, [18,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [83, [23,42,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [118, [35,40,43]]]]],["Hans", [["Gold", [70, [19,43,8]]],["Gold", [72, [45,17,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [70, [43,19,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [70, [9,43,18]]],["Gold", [72, [17,41,14]]],["Gold", [86, [23,54,9]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [119, [35,40,44]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [72, [23,44,5]]]]],["Phil", [["Gold", [70, [18,44,8]]],["Gold", [70, [20,34,16]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [70, [9,17,44]]],["Gold", [73, [23,41,9]]],["Gold", [86, [54,18,14]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [120, [35,40,45]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [23,45,5]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [71, [18,45,8]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [71, [9,17,45]]],["Gold", [73, [23,41,9]]],["Gold", [86, [54,18,14]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [121, [35,40,46]]]]],["Hans", [["Gold", [70, [19,46,5]]],["Gold", [70, [45,17,8]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [72, [18,46,8]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,17,46]]],["Gold", [73, [23,41,9]]],["Gold", [86, [54,18,14]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [122, [35,40,47]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [71, [19,47,5]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [73, [18,47,8]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [70, [9,47,14]]],["Gold", [73, [23,41,9]]],["Gold", [89, [17,54,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [123, [35,40,48]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [72, [19,48,5]]]]],["Phil", [["Gold", [70, [1,48,21]]],["Gold", [70, [20,34,16]]],["Bronze", [58, [18,19,21]]]]],["Kevin", [["Gold", [71, [9,48,14]]],["Gold", [73, [23,41,9]]],["Gold", [89, [17,54,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [124, [35,40,49]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [19,49,5]]]]],["Phil", [["Gold", [70, [1,49,20]]],["Gold", [71, [18,19,34]]],["Bronze", [50, [21,8,21]]]]],["Kevin", [["Gold", [72, [9,49,14]]],["Gold", [73, [23,41,9]]],["Gold", [89, [17,54,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [125, [35,40,50]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [74, [19,50,5]]]]],["Phil", [["Gold", [70, [1,50,19]]],["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [91, [23,50,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [126, [35,40,51]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [70, [51,5,14]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [1,18,51]]],["Gold", [70, [20,34,16]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [92, [23,51,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [127, [35,40,52]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [71, [52,5,14]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [71, [1,18,52]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [70, [9,52,9]]],["Gold", [72, [17,41,14]]],["Gold", [95, [23,54,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [128, [35,40,53]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [72, [53,5,14]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [1,53,16]]],["Gold", [71, [18,19,34]]],["Silver", [62, [20,21,21]]]]],["Kevin", [["Gold", [71, [9,53,9]]],["Gold", [72, [17,41,14]]],["Gold", [95, [23,54,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [129, [35,40,54]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [54,5,14]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [73, [1,18,54]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [95, [23,54,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [130, [35,40,55]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [70, [55,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [74, [1,18,55]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [96, [23,55,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [131, [35,40,56]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [71, [56,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [75, [1,18,56]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [97, [23,56,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [132, [35,40,57]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [72, [57,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [76, [1,18,57]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [98, [23,57,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [133, [35,40,58]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [73, [58,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [77, [1,18,58]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [99, [23,58,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [134, [35,40,59]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [74, [59,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [78, [1,18,59]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [100, [23,59,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [135, [35,40,60]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [75, [60,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [79, [1,18,60]]],["Silver", [61, [21,19,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [101, [23,60,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [136, [35,40,61]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [76, [61,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [1,61,8]]],["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [102, [23,61,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [137, [35,40,62]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [77, [62,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [71, [1,62,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [103, [23,62,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [138, [35,40,63]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [78, [63,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [72, [1,63,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [104, [23,63,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [139, [35,40,64]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [79, [64,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [73, [1,64,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [105, [23,64,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [140, [35,40,65]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [80, [65,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [74, [1,65,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [106, [23,65,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [141, [35,40,66]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [81, [66,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [75, [1,66,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [107, [23,66,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [142, [35,40,67]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [82, [67,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [76, [1,67,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [108, [23,67,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [143, [35,40,68]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [83, [68,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [77, [1,68,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [109, [23,68,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [144, [35,40,69]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [84, [69,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [78, [1,69,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [110, [23,69,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [145, [35,40,70]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [85, [70,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [79, [1,70,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [111, [23,70,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [146, [35,40,71]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [86, [71,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [80, [1,71,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [112, [23,71,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [147, [35,40,72]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [87, [72,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [81, [1,72,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [113, [23,72,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [148, [35,40,73]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [88, [73,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [82, [1,73,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [114, [23,73,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [149, [35,40,74]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [89, [74,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [83, [1,74,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [115, [23,74,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [150, [35,40,75]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [90, [75,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [84, [1,75,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [116, [23,75,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [151, [35,40,76]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [91, [76,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [85, [1,76,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [117, [23,76,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [152, [35,40,77]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [92, [77,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [86, [1,77,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [118, [23,77,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [153, [35,40,78]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [93, [78,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [87, [1,78,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [119, [23,78,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [154, [35,40,79]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [94, [79,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [88, [1,79,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [120, [23,79,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [155, [35,40,80]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [95, [80,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [89, [1,80,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [121, [23,80,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [156, [35,40,81]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [96, [81,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [90, [1,81,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [122, [23,81,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [157, [35,40,82]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [97, [82,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [91, [1,82,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [123, [23,82,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [158, [35,40,83]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [98, [83,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [92, [1,83,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [124, [23,83,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [159, [35,40,84]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [99, [84,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [93, [1,84,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [125, [23,84,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [160, [35,40,85]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [100, [85,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [94, [1,85,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [126, [23,85,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [161, [35,40,86]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [101, [86,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [95, [1,86,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [127, [23,86,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [162, [35,40,87]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [102, [87,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [96, [1,87,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [128, [23,87,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [163, [35,40,88]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [103, [88,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [97, [1,88,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [129, [23,88,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [164, [35,40,89]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [104, [89,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [98, [1,89,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [130, [23,89,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [165, [35,40,90]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [105, [90,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [99, [1,90,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [131, [23,90,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [166, [35,40,91]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [106, [91,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [100, [1,91,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [132, [23,91,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [167, [35,40,92]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [107, [92,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [101, [1,92,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [133, [23,92,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [168, [35,40,93]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [108, [93,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [102, [1,93,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [134, [23,93,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [169, [35,40,94]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [109, [94,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [103, [1,94,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [135, [23,94,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [170, [35,40,95]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [110, [95,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [104, [1,95,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [136, [23,95,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [171, [35,40,96]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [111, [96,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [105, [1,96,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [137, [23,96,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [172, [35,40,97]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [112, [97,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [106, [1,97,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [138, [23,97,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [173, [35,40,98]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [113, [98,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [107, [1,98,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [139, [23,98,18]]]]]],[["Simon", [["Gold", [82, [27,34,21]]],["Gold", [174, [35,40,99]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Gold", [114, [99,10,5]]],["Bronze", [56, [23,19,14]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Gold", [108, [1,99,8]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [72, [9,54,9]]],["Gold", [72, [17,41,14]]],["Gold", [140, [23,99,18]]]]]],[["Simon", [["Gold", [74, [40,0,34]]],["Gold", [83, [35,27,21]]]]],["Hans", [["Gold", [70, [45,17,8]]],["Bronze", [52, [23,19,10]]]]],["Phil", [["Gold", [70, [20,34,16]]],["Silver", [60, [18,21,21]]]]],["Kevin", [["Gold", [71, [17,54,0]]],["Gold", [73, [9,23,41]]]]]]] ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/banner.mls ================================================ :js import "../../mlscript-compile/nofib/banner.mls" print(banner.main()) //│ > //│ > IIIII SSSS TTTTT H H IIIII SSSS N N OOO TTTTT A GGGG RRRR EEEEE A TTTTT BBBB A N N N N EEEEE RRRR ??? //│ > I S T H H I S NN N O O T A A G R R E A A T B B A A NN N NN N E R R ? ? //│ > I SSS T HHHHH I SSS N N N O O T AAAAA G GG RRRR EEEEE AAAAA T BBBB AAAAA N N N N N N EEEEE RRRR ? //│ > I S T H H I S N NN O O T A A G G R R E A A T B B A A N NN N NN E R R ? //│ > IIIII SSSS T H H IIIII SSSS N N OOO T A A GGG R R EEEEE A A T BBBB A A N N N N EEEEE R R . //│ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/boyer.mls ================================================ :js import "../../mlscript-compile/nofib/boyer.mls" boyer.main() //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/boyer2.mls ================================================ :js import "../../mlscript-compile/nofib/boyer2.mls" boyer2.main() //│ = "The term is a tautology" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/calendar.mls ================================================ :js import "../../mlscript-compile/nofib/calendar.mls" print(calendar.main()) //│ > 1993 //│ > //│ > January February March //│ > Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa //│ > 1 2 1 2 3 4 5 6 1 2 3 4 5 6 //│ > 3 4 5 6 7 8 9 7 8 9 10 11 12 13 7 8 9 10 11 12 13 //│ > 10 11 12 13 14 15 16 14 15 16 17 18 19 20 14 15 16 17 18 19 20 //│ > 17 18 19 20 21 22 23 21 22 23 24 25 26 27 21 22 23 24 25 26 27 //│ > 24 25 26 27 28 29 30 28 28 29 30 31 //│ > 31 //│ > //│ > April May June //│ > Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa //│ > 1 2 3 1 1 2 3 4 5 //│ > 4 5 6 7 8 9 10 2 3 4 5 6 7 8 6 7 8 9 10 11 12 //│ > 11 12 13 14 15 16 17 9 10 11 12 13 14 15 13 14 15 16 17 18 19 //│ > 18 19 20 21 22 23 24 16 17 18 19 20 21 22 20 21 22 23 24 25 26 //│ > 25 26 27 28 29 30 23 24 25 26 27 28 29 27 28 29 30 //│ > 30 31 //│ > //│ > July August September //│ > Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa //│ > 1 2 3 1 2 3 4 5 6 7 1 2 3 4 //│ > 4 5 6 7 8 9 10 8 9 10 11 12 13 14 5 6 7 8 9 10 11 //│ > 11 12 13 14 15 16 17 15 16 17 18 19 20 21 12 13 14 15 16 17 18 //│ > 18 19 20 21 22 23 24 22 23 24 25 26 27 28 19 20 21 22 23 24 25 //│ > 25 26 27 28 29 30 31 29 30 31 26 27 28 29 30 //│ > //│ > //│ > October November December //│ > Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa //│ > 1 2 1 2 3 4 5 6 1 2 3 4 //│ > 3 4 5 6 7 8 9 7 8 9 10 11 12 13 5 6 7 8 9 10 11 //│ > 10 11 12 13 14 15 16 14 15 16 17 18 19 20 12 13 14 15 16 17 18 //│ > 17 18 19 20 21 22 23 21 22 23 24 25 26 27 19 20 21 22 23 24 25 //│ > 24 25 26 27 28 29 30 28 29 30 26 27 28 29 30 31 //│ > 31 //│ > //│ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/cichelli.mls ================================================ :js import "../../mlscript-compile/nofib/cichelli.mls" cichelli.main() //│ = "YesIts(113994, [[\"w\", 16],[\"r\", 17],[\"o\", 11],[\"m\", 17],[\"l\", 11],[\"x\", 2],[\"n\", 11],[\"i\", 4],[\"f\", 10],[\"h\", 3],[\"g\", 0],[\"t\", 5],[\"d\", 0],[\"a\", 0],[\"s\", 2],[\"c\", 0],[\"e\", 1]])" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/circsim.mls ================================================ :js import "../../mlscript-compile/nofib/circsim.mls" circsim.main() //│ = "[[F,F,F,F,F,F,F,F],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T],[T,T,T,T,T,T,T,T]]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/clausify.mls ================================================ :js import "../../mlscript-compile/nofib/clausify.mls" clausify.main() //│ = "a <=\na <=\na <=\na <=\na <=\na <=\na <=\na <=\na <=\na <=\n" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/constraints.mls ================================================ :js import "../../mlscript-compile/nofib/constraints.mls" constraints.main() //│ = "[4,4,4,4,4]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/cryptarithm1.mls ================================================ :js :re import "../../mlscript-compile/nofib/cryptarithm1.mls" cryptarithm1.main() //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls ================================================ :js import "../../mlscript-compile/nofib/cryptarithm2.mls" cryptarithm2.main() //│ = "[\"W\",\" \",\"=\",\">\",\" \",\"3\",\"\\n\",\"H\",\" \",\"=\",\">\",\" \",\"9\",\"\\n\",\"N\",\" \",\"=\",\">\",\" \",\"8\",\"\\n\",\"I\",\" \",\"=\",\">\",\" \",\"4\",\"\\n\",\"L\",\" \",\"=\",\">\",\" \",\"7\",\"\\n\",\"R\",\" \",\"=\",\">\",\" \",\"2\",\"\\n\",\"V\",\" \",\"=\",\">\",\" \",\"6\",\"\\n\",\"T\",\" \",\"=\",\">\",\" \",\"1\",\"\\n\",\"E\",\" \",\"=\",\">\",\" \",\"0\",\"\\n\",\"Y\",\" \",\"=\",\">\",\" \",\"5\",\"\\n\"]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/cse.mls ================================================ :js import "../../mlscript-compile/nofib/cse.mls" cse.main() //│ = "[[[[0, \"a\", []]]],[[[0, \"a\", []]],[[0, \"+\", [2,2]],[2, \"a\", []]]],[[[0, \"a\", []]],[[0, \"+\", [2,2]],[2, \"a\", []]],[[0, \"+\", [4,4]],[4, \"*\", [5,6]],[5, \"a\", []],[6, \"b\", []]]],[[[0, \"a\", []]],[[0, \"+\", [2,2]],[2, \"a\", []]],[[0, \"+\", [4,4]],[4, \"*\", [5,6]],[5, \"a\", []],[6, \"b\", []]],[[0, \"+\", [1,6]],[1, \"*\", [6,5]],[5, \"c\", []],[6, \"+\", [7,8]],[7, \"a\", []],[8, \"b\", []]]],[[[0, \"a\", []]],[[0, \"+\", [2,2]],[2, \"a\", []]],[[0, \"+\", [4,4]],[4, \"*\", [5,6]],[5, \"a\", []],[6, \"b\", []]],[[0, \"+\", [1,6]],[1, \"*\", [6,5]],[5, \"c\", []],[6, \"+\", [7,8]],[7, \"a\", []],[8, \"b\", []]],[[0, \"X\", [21,20,19,18,17]],[17, \"+\", [18,25]],[18, \"+\", [19,24]],[19, \"+\", [20,23]],[20, \"+\", [21,22]],[21, \"0\", []],[22, \"a\", []],[23, \"b\", []],[24, \"c\", []],[25, \"d\", []]]],[]]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/eliza.mls ================================================ :js import "../../mlscript-compile/nofib/eliza.mls" eliza.main() //│ = "[\"I'm not sure I understand you fully.\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\nHow long have you been not?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\nHow long have you been not?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\nHow long have you been not?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\nHow long have you been not?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\nHow long have you been not?\\n\\n\",\"I'm not sure I understand you fully.\\n\\nWe were discussing you --not me.\\n\\nWhy do you ask?\\n\\nOh, do?\\n\\nWhat does that suggest to you?\\n\\nDo computers worry you?\\n\\nYou're not really talking about me, are you?\\n\\nI see.\\n\\nCan you elaborate on that?\\n\\nWhy do you ask?\\n\\nDid you come to me because you are asking questions, not me?\\n\\nAre you saying no just to be negative?\\n\\nYou seem quite positive.\\n\\nHow long have you been not?\\n\\n\",\"\"]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/fish.mls ================================================ :js // This is caused by the implementation of `+:` in `NofibPrelude.mls`. Replacing // `+:` with a non-recursive implementation can fix the stack overflow error. // However, it will cause the file size to increase by ~390 KiB. :re import "../../mlscript-compile/nofib/fish.mls" fish.main() //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/gcd.mls ================================================ :js import "../../mlscript-compile/nofib/gcd.mls" gcd.main() //│ = 5021 ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/input/1500.1 ================================================ 11596 5940 7041 3954 16850 19194 10798 3867 10582 22284 4 1625 21592 5 24168 25739 9819 3262 12274 19602 15970 20213 8 9093 13180 15097 26473 21207 15290 21927 342 21747 5726 9156 12 7172 1504 5693 22552 9331 1734 25347 24559 20698 25218 25680 25424 25613 356 17 17 10111 24559 4698 12539 12734 24006 865 73 3185 9328 2912 6016 12214 18883 16299 17439 25687 15482 20301 24 12760 11924 25 23900 17711 7354 11615 2862 21974 21523 28 20161 14961 11016 8632 11501 13515 1075 17678 2762 31 11903 14949 4157 18133 14620 25164 10820 16951 4023 1530 11790 35 22374 14412 12089 26644 10965 20032 12133 22326 20603 13547 14386 26591 23567 17933 3245 10384 2345 25201 25892 4290 42 5554 14342 5403 9475 10260 4521 18977 25744 45 19352 16483 12678 7803 2415 18458 13727 9949 4632 21152 11601 23964 7588 1818 10842 4415 10294 17518 6662 16220 2188 601 23645 53 19437 3750 54 4438 8594 26951 17058 56 7960 24584 26620 26417 22684 13719 23719 26922 15771 59 9699 5756 1185 20876 12536 24949 18189 23630 62 26990 3535 13082 6650 2992 15784 1989 20916 5452 10476 16338 10122 14194 627 24819 6507 22585 21604 9209 23512 69 12264 3286 17142 13742 9759 71 16514 8005 15480 17672 11364 3041 16609 10543 74 847 25294 75 14286 23092 76 8876 1269 744 9805 8998 25043 21278 25791 79 15071 205 25600 8248 6324 81 7833 2722 8127 7538 7926 13043 22099 15396 6889 18172 22408 85 4288 11323 15774 15062 23746 15943 26770 26920 1488 22240 19412 26921 1233 12314 16543 8079 12051 11307 13347 21801 92 25780 16853 6056 10576 17462 94 9539 10730 5754 24407 22645 8936 17213 11785 26884 21153 6490 98 25266 7974 17502 22182 16644 7673 2721 101 13960 13168 4507 25843 8563 4295 20210 5610 25328 1360 17925 18252 25633 26684 9023 24826 106 6198 4126 10278 13921 108 20313 14741 4648 2600 18715 110 15811 21050 6079 16175 3160 7629 13376 2713 12892 23556 9642 20383 19226 21387 3347 13174 23593 116 23241 24973 17437 12920 25131 19942 8118 8370 25783 3543 17456 15813 8072 1948 24801 7332 4122 12167 20231 123 123 123 11996 124 15089 5112 285 18080 19942 24523 23011 7218 4271 24746 1152 24869 18253 20249 129 25081 18807 21186 23386 494 131 9862 25716 921 5644 20904 584 26837 10419 7635 12739 15098 362 17943 4368 24717 23037 4545 5985 14073 9687 26911 22127 4110 10947 6270 12377 321 6457 21237 8701 1021 19059 142 8115 10682 9242 26226 18485 11045 10653 3652 25500 6700 7418 20895 21394 3283 21998 7027 17897 22705 2857 11077 19645 1085 26990 635 7203 18527 12255 20783 16608 4605 10856 8017 153 15332 17858 3871 25810 25643 16230 26790 19849 156 4076 24296 25376 704 19030 26219 19027 106 4898 19079 6909 14728 5128 25564 161 18817 6143 162 17106 14070 23171 18206 5708 19777 16020 23509 2816 592 6966 166 26478 18247 9682 22407 5568 168 21088 3545 169 26276 23466 16247 10399 18046 10507 10414 20220 18457 10689 7192 18600 14557 12059 4499 10310 23106 6055 15138 4544 7357 1696 23849 10652 24780 15327 10071 19543 10107 22595 10694 5457 5273 25457 1613 181 15101 16819 182 24011 10719 22655 663 24613 10864 19837 7673 17844 20564 2586 6551 18634 18854 13507 12286 5636 188 15793 21128 17904 10272 26691 7358 190 18818 24010 21954 168 5437 16957 3217 7036 20945 25983 18234 21642 23819 195 542 796 19164 913 197 6368 3877 16494 16867 23726 23538 4530 5234 2856 22120 21709 25441 13193 7369 17903 202 12226 16219 1323 21982 9017 13937 569 10216 3245 23957 13118 3763 26030 17962 25951 16335 17080 208 10461 21324 23308 15500 23639 5618 9015 11315 3470 26222 12297 212 21225 1469 7429 6037 21203 12027 18619 20002 25703 26839 7541 216 22269 12804 26820 556 5970 10343 24391 5150 219 19339 4876 20908 4873 11648 5413 21277 14275 222 3027 1970 15631 24698 26448 8984 24869 23932 225 225 15063 22615 3887 20166 13619 26803 23132 17076 18740 21133 24712 7613 14787 230 4206 13898 20610 12738 26064 14256 14320 5980 25817 22500 3375 5591 1951 23142 16654 11091 15289 8852 10041 5061 6381 1808 15022 14363 6430 18690 4530 10138 1072 13525 1933 10785 21812 11260 402 11351 3815 243 243 22131 969 7644 23921 2960 7968 16973 21051 17187 24838 15650 19239 13938 23525 25245 2037 14540 8593 10257 6751 13002 3967 8379 8387 20758 17145 19921 11284 3917 8360 20424 630 254 21758 16682 23594 10970 21813 256 14893 12401 257 1681 20495 258 17543 5870 23499 14995 25977 18188 21308 15365 19853 20509 23323 262 24846 13439 19954 13815 11120 22917 16976 15548 26404 22164 26786 8266 12399 17278 267 15171 19769 25177 20116 26301 6616 9256 2150 7411 5134 13287 24527 482 20576 9501 4973 16516 273 777 15471 274 18250 8763 14851 6587 14044 17625 3724 15229 21072 23480 11243 7934 22259 9090 8394 22378 10733 20741 17656 3724 15148 721 13274 282 24618 5470 21003 15918 22297 11329 23321 6448 20216 22680 15275 14275 3451 14783 18538 9567 6325 14768 25597 17353 289 3500 21490 290 7591 19619 24859 6142 18580 292 1425 16021 22200 20616 2283 6323 18187 3058 2786 3922 312 17117 25557 793 14737 7812 7159 15719 298 17190 18614 21971 21004 300 3052 25093 25885 15736 6219 18867 9563 9839 13503 26186 9885 7080 10605 16780 20284 17241 25482 3375 25151 14019 10758 20078 3257 23988 9545 18581 7480 26728 25635 8147 16174 8826 311 5114 6493 2568 17109 14180 24025 24089 23546 2282 10178 20142 13182 6894 23353 9260 9444 16909 19765 17192 20638 318 15678 12191 4138 13167 22669 6781 6413 10385 2636 10793 11527 18271 12362 11350 26947 25582 16297 25801 11529 7053 4389 21557 2958 2067 6286 4546 10754 16058 1904 17597 7813 17193 329 1033 12682 26130 12426 2534 23214 9259 9425 26148 19068 7901 333 1256 19614 25430 7390 12978 13615 25879 12237 10021 24261 8905 21865 26988 8242 3730 13647 2926 419 13427 12993 17105 12244 10200 14744 20272 1734 2603 16483 1922 26983 3367 25357 18149 5016 18745 25585 25865 1143 17362 13386 4590 4430 21718 4289 348 20161 8461 349 1832 9550 14662 3478 8791 7898 14015 23045 15861 1157 23620 353 9500 5639 13231 18127 13262 26235 15395 2209 356 26569 21168 25064 680 9814 19683 14891 17383 22930 682 13616 9405 5472 2289 18497 15044 8490 362 18327 25086 14134 1075 9060 11500 3825 24661 16136 13629 16947 15955 22502 7727 26415 25319 6757 3365 13632 14692 15436 19148 25151 9218 23391 3787 371 4347 17665 10497 18321 16669 9616 24536 26571 19299 10563 26930 4719 23794 2424 20501 20997 24849 7308 8081 21295 8530 7535 23747 9990 3518 12657 4945 12548 9693 23704 11336 22659 2971 3886 24015 4218 16978 6120 10104 384 7124 21236 26764 3111 386 16239 10987 387 20782 24361 14401 25449 9888 389 2189 22310 1526 18102 19543 12698 18255 7573 25373 19957 17361 20873 6153 26914 3447 6183 25462 8166 8374 21457 9177 2036 3240 3480 17288 2350 398 22150 12858 399 11626 13725 6733 12845 21044 6260 24977 12250 2146 10482 7867 403 23334 2908 14948 23193 19853 22864 11845 5574 11499 14446 8186 407 12791 15560 2997 13429 18089 18692 2233 3754 24770 21178 25259 18419 11827 17601 1244 14177 20240 25504 20728 4739 17022 2851 20503 4895 23175 10269 14797 6317 3692 417 26772 8383 24258 3863 24219 294 5254 4993 1244 8756 7485 421 13013 23422 12155 6931 26826 24354 25410 25320 9168 15528 2321 18244 20681 13658 22727 24970 5419 24051 20875 7233 26036 849 6813 23232 12832 8371 10451 2019 9674 18770 7442 9544 21672 18677 9633 11308 14916 12522 2799 9506 19118 19518 12030 19129 26721 18508 17112 7917 1973 7454 3654 17667 4778 19970 7050 3285 8309 440 6785 12732 15409 23602 11055 25818 20326 15566 26294 25585 21361 10852 184 6557 3344 22030 22963 9430 11226 466 14495 15685 16533 24469 449 2620 8233 890 16287 23815 18214 24875 6494 9604 23884 23433 7824 453 936 8846 21262 4062 12959 7714 455 85 4365 3525 16353 14585 21609 4143 458 3703 3531 16715 19694 2164 6348 5308 9277 6133 15512 462 7667 3603 2071 3786 6354 16344 24285 18733 16641 8833 12348 362 5338 8562 20691 467 16219 2793 10769 6681 23072 7280 22744 24875 4302 24878 14594 13826 2970 8485 629 25397 10409 21596 22225 7882 24703 26306 24163 25734 25526 14465 476 22377 18392 22544 20552 2086 10987 12126 24786 8242 14767 4101 9245 25056 20705 11409 16708 2370 482 9402 20318 17883 21206 5385 1297 17948 17541 14901 20224 7371 486 5699 7 3455 25895 5317 16093 20485 21980 489 2484 3410 2455 26650 4163 12955 25014 10497 18217 14316 15221 8496 18752 1915 494 17958 23687 7127 23023 13928 5685 3221 11161 21657 16353 18687 21431 12714 6534 15491 19470 20089 7225 22729 24608 12080 6493 4022 22134 24547 26191 503 1415 25669 13109 26376 23649 505 18665 4071 506 17890 26851 4979 21134 52 465 8900 288 13784 9125 17435 638 8334 25058 22383 23882 26989 1157 512 26121 23857 9881 24711 26663 19031 13094 17499 22835 24868 11500 26932 21717 8304 10240 3659 4547 26299 1607 13359 9295 8952 26581 8504 12921 12756 4585 9015 10943 19474 15438 24486 8395 24052 10265 25217 17789 525 8416 12942 17926 21755 2842 1722 8586 21709 18672 14061 5732 22193 22692 4503 26127 20031 1758 5715 15214 6673 3193 9268 16285 19216 10789 10886 2275 16494 16738 14202 6298 11013 24997 17528 8676 11068 25777 14178 15151 711 22691 23963 16518 17617 7708 21076 3509 541 8501 20254 542 2651 17050 18250 823 613 544 2101 1825 3769 14052 14911 14751 12994 1963 6059 4310 548 13561 8636 5749 5200 24048 8139 8067 3502 8863 6034 16023 25448 9448 8928 2220 2508 553 10130 22687 11210 7150 24006 214 18073 11633 4988 22192 4989 16384 17475 2747 21198 9167 19226 18863 7637 15624 5301 15753 561 26121 5138 562 19935 18499 16662 10331 2785 564 7793 23013 15136 18845 566 9086 14819 16631 25034 1522 12144 3949 11976 12972 11612 11561 7047 21546 199 20926 1086 15059 7596 17273 20084 8264 1085 6909 26827 8982 24739 24338 26 13335 6040 9688 4016 8985 1020 9577 22666 25490 15431 8051 20846 5979 17881 13761 4172 20989 3416 3216 1366 4619 25486 17919 583 8855 25565 9304 965 585 585 19777 19711 8351 14639 5187 5470 5371 23012 14809 7268 24013 7648 10341 11182 17267 4923 21895 25394 1378 19461 592 12328 22076 593 22465 15602 6738 20631 17795 595 13091 9641 16796 596 16432 2120 8205 11779 4462 26902 11354 599 13226 21069 12045 3560 15492 289 25876 19250 602 3711 1358 19062 24758 24641 604 16764 9216 2613 8240 758 22171 12027 20591 6039 20010 749 16272 19477 17788 4817 19940 23706 2151 14026 24555 2494 15374 8489 8804 21913 22821 9093 22992 14462 24515 11099 25114 21514 10378 17400 16965 13064 12249 20777 1564 3970 26535 17618 619 15795 17803 21244 4905 9217 2061 3784 24648 23470 1787 16262 21522 6146 11458 13104 23933 19757 23676 21961 15388 911 14362 13271 21795 16438 4587 2388 628 17884 19565 20872 9504 16931 3707 10587 4247 21583 1127 19912 19472 10864 18729 4305 17532 5866 11066 1562 11894 4638 9235 13420 9345 11513 7568 19109 637 16339 22331 18339 22959 639 2186 6429 15141 13696 21809 26417 4217 12922 18178 26047 20867 26699 7411 19076 6236 26625 24373 645 13789 23366 18083 14198 7711 24335 9306 2616 5168 25821 4348 15204 19497 7386 23319 8722 20251 12475 3126 4505 17449 11841 14253 1141 10048 12363 12158 16814 10202 8978 17394 1605 24229 656 921 7868 23153 12394 22074 17642 10515 24006 659 6084 21329 24921 22136 661 23157 13035 19347 1238 11191 21314 10927 1024 805 6933 18833 10545 19628 14583 15623 22871 3230 14315 23062 15649 8156 6980 10701 669 8157 1339 670 12715 17490 2314 802 8949 17557 8805 9281 7388 10737 8042 26823 11530 6950 16643 8891 292 22916 23084 18245 19029 9117 17310 2547 11094 19799 22178 19466 10360 18021 22197 22148 24244 26436 7322 15306 10410 16894 683 22739 2745 7692 11889 5832 14509 7704 2622 15654 22310 4815 12498 16066 21205 688 1197 15332 15785 13372 10743 6303 1679 9067 20779 3363 11585 9556 20873 14749 10152 16000 15395 21886 14811 10231 14834 7287 18976 13512 4312 7713 20628 26404 24655 17802 22106 8542 25758 20891 19185 10276 12617 25637 7256 4752 8851 14190 25011 13242 703 2034 4240 17984 4101 22089 23156 24876 18047 8071 23106 13374 10275 18099 16764 15580 11396 5776 709 24752 16094 1563 14011 25882 12706 17967 4128 26432 18048 9884 10796 1556 15786 714 5858 16939 1342 18286 16713 6097 16564 3920 21264 3077 6995 718 18118 22207 719 9767 25520 11581 720 10833 4172 8129 18791 722 9535 8526 22054 8011 13476 8196 16305 4077 5733 8920 22830 13566 26062 15071 9263 16658 10672 16357 12016 21065 20180 1329 1778 986 16415 18531 2643 11734 11436 1828 12329 733 733 8192 4758 734 16771 19063 23914 4394 15469 856 17373 4057 2236 16364 5871 738 19103 2702 20019 5419 8420 9585 8372 389 16613 1589 13062 25923 555 21407 4135 13471 10600 744 17117 22388 6241 12241 23586 746 8567 17942 4574 23419 19508 22409 12897 18725 20109 1853 24270 750 4358 16967 14170 2954 200 10872 15549 5108 11916 17609 21871 18263 5530 278 17915 20614 19065 756 22044 10680 3901 18077 12627 1862 10715 22186 7751 19023 24693 21973 18448 3268 14801 4553 9226 22666 25087 21646 17899 17035 22505 764 19324 13224 712 20472 15347 19779 12595 1906 767 1010 24893 22432 9317 15169 5436 25676 1079 4658 16642 678 20835 6243 11412 19324 14772 13216 9517 20069 9750 3350 17467 4802 17002 4495 18120 22685 22576 19668 18932 10001 22938 11362 21199 11318 23459 10243 25948 24684 1577 14629 1621 19469 25238 6387 24131 24162 17082 11895 6360 7645 7064 9300 532 13132 19231 20706 26274 22563 26115 17358 17868 25545 21628 19669 12152 7512 13318 7774 11190 15847 6154 23866 15021 24808 9037 20769 14292 5412 9450 951 7135 9275 16446 7414 19785 6121 11508 17661 5216 5901 2838 22899 20483 9671 16842 9591 13528 248 13224 18196 25569 19396 10266 24927 21727 26734 20691 22979 10281 22204 11276 12296 10656 8301 26483 14979 10787 9751 807 10914 5093 8968 11613 13708 24001 4481 20594 1087 16410 22542 9078 15435 18860 26953 23100 25997 20984 26952 6390 987 142 26642 1295 22018 4597 717 23312 4236 22537 11052 24183 975 10143 8475 819 21643 26073 8713 3084 14760 9765 7384 9502 17219 238 11874 16242 5258 23800 832 4429 10364 22313 7652 26663 18615 23730 11995 6627 18846 2556 23876 17833 19152 8093 10656 347 17115 25763 11098 21034 2346 20245 19200 15765 25889 13044 12996 3183 17674 12282 7110 13187 20611 23569 836 18092 1696 3477 8789 16870 747 26974 16119 2831 18210 7605 13664 12232 19089 5964 6473 19258 12687 1186 20854 8390 26590 3169 18532 24788 26893 845 2760 26142 4291 4086 24994 6895 18498 9256 24749 9837 2268 2353 9292 12402 4159 17703 6790 7182 19966 15804 3529 14028 23104 11381 7973 3630 4859 11694 6351 21727 9354 13293 1013 16917 4916 857 25356 23975 858 858 12158 859 26598 12961 10337 3684 15864 861 3304 20707 14587 4211 20714 863 23770 12781 24080 6557 6308 15428 16937 63 17255 24826 19326 867 2939 19980 24860 2049 7832 20701 9933 5070 8195 12782 22346 871 20354 26749 12181 15213 18145 18353 12212 16535 9791 17903 21443 20758 12686 612 7457 19905 4773 9744 19880 1203 13467 16830 12594 13866 17522 23264 19096 26181 15889 14345 8340 19634 26967 3103 23163 6678 24539 24316 8044 18257 7952 2416 23605 3446 17054 6502 7911 8895 9426 13533 888 6309 13780 889 3964 15186 890 20674 19446 11630 2779 15913 18556 8489 8800 21280 11344 12851 894 6958 4823 895 5106 4821 10760 20648 11457 9164 460 17314 898 17007 12147 20331 13318 10196 24065 17041 19861 901 12824 94 24606 3787 14130 12687 7607 14653 14232 10925 13905 19980 12017 8466 21543 26738 13731 5654 8654 873 3220 25308 10448 14472 1992 20123 19019 23139 18210 21970 2103 6117 12141 14789 16700 8652 9612 21610 914 19311 931 67 10222 21436 3921 7108 3328 25560 288 14427 12342 21630 18751 13631 2986 5328 20560 21853 21804 5900 3148 11114 17935 18719 2214 2219 26219 8332 18753 97 3653 13669 12256 26166 5051 9846 13786 503 2327 18680 17648 8376 22617 26260 3452 14367 15498 8279 13934 931 6635 22332 20932 20057 1869 18896 20920 16051 6086 5750 9394 13450 10530 12277 13213 10573 24561 11457 7601 22655 22975 18578 21755 6398 2862 900 4708 13796 26029 14832 4013 25059 26107 17126 9658 1890 24314 4592 22413 9600 2340 11684 3292 12951 24010 946 24766 23150 6222 23020 11041 22929 15008 15824 1997 18371 11598 26782 5231 13962 13135 21325 19573 5949 16108 18676 24044 3639 5311 15855 8083 4299 10158 1580 21345 4036 18333 957 17341 22835 958 22731 24730 14546 1450 18493 680 26301 10996 26929 22849 20482 3570 6927 11094 17339 24502 2628 9401 9873 23768 21293 20288 9843 8294 9923 3786 18146 17887 18224 21757 16053 25345 26393 12057 14471 18034 19183 6550 23939 3459 11681 8865 6649 26424 14045 19016 19531 22 18443 5431 25391 7359 10797 6685 3720 15569 761 5401 24666 3106 14466 8019 20078 3030 21985 20012 9169 9568 18397 20352 8214 19718 19483 25679 18479 19343 13821 5949 20544 13932 16804 1460 22767 14479 2847 19150 987 12518 8012 12449 16017 24285 2277 9893 18163 16358 14427 25722 26618 20554 24112 13621 11072 20140 993 25652 4903 994 26202 26278 995 2422 9372 700 15585 25400 5176 17968 2643 21355 9915 6298 10967 3346 5728 10981 2000 6708 21369 10449 4026 26090 20986 13278 21094 12747 20260 12865 12377 18432 8720 22928 21515 2086 5091 24866 23250 24530 26573 16517 11349 11908 12353 14220 17103 551 16370 8755 13718 3523 22732 3297 4972 17869 856 21 11963 26326 9755 19495 7135 13295 1016 9701 9965 15513 1017 20193 13394 25127 11919 23542 15323 11883 7172 1020 16052 11757 456 8645 14291 24835 23723 12735 1023 10818 10549 2333 11200 10025 20257 793 18778 1026 22938 3254 1027 3867 13532 19985 1028 11157 15909 14368 7907 23414 6955 5647 20074 18786 5757 19277 26469 3780 5636 1988 18439 23911 19090 23782 14454 2438 23305 19385 23140 24232 1037 17125 14539 21227 23907 12015 2546 6255 13912 9293 2941 9481 8564 23585 13594 7418 343 4555 15787 3699 9460 18345 8025 10685 15368 26845 1046 2934 20939 20007 911 16255 12045 14325 8512 2577 7172 26985 22930 1690 7770 1694 12390 6091 26737 1052 12281 12893 12360 18176 6094 16139 7299 23231 1866 22130 9392 18824 24776 10241 1057 10721 21778 2735 26743 4838 19814 8051 15740 14745 14489 14925 15845 4189 379 25614 21251 18842 21007 5663 21720 15109 21093 22385 1065 16172 13479 22031 19583 16774 5819 21078 2977 18233 881 4517 7901 18653 7347 13494 11630 3119 12082 23695 8568 18877 15000 15841 14268 300 21858 223 19783 17571 7523 9611 4740 2609 11364 23925 7600 3128 23342 24395 3227 7111 15978 12058 23893 8045 19101 11225 10372 14281 3546 7751 5866 22750 20374 6302 7756 24964 2121 1717 424 16549 2019 1086 18235 2359 9351 12186 16984 26589 16656 1396 11817 6652 19591 5082 1090 9651 974 14950 2604 24057 20649 25757 25112 11357 20550 8691 1611 14423 13202 24690 10829 10621 12645 15444 5732 5316 16786 5922 17119 10190 1099 18734 2660 18585 17268 21608 19928 7192 10843 13430 11582 25890 1103 21650 3237 17080 22320 21681 5980 4513 23151 23479 1106 11358 3782 26403 12164 1108 18348 17872 13776 17237 14166 25150 12539 8887 14687 458 18504 10624 1112 21617 23092 21593 17239 15071 6503 16422 11707 17150 16921 18764 2564 9165 25872 15656 5310 7683 17107 16759 22178 20871 24837 168 24221 1121 21380 10196 10919 11263 21615 19411 25742 24862 9737 26108 14993 1709 13405 6461 6222 21267 23531 1930 6690 19511 15485 1792 309 2804 5812 21452 25994 23994 1727 10099 24070 2363 7849 1132 24657 3904 25672 11024 15467 13315 1395 10647 24919 12890 6669 5301 6472 17676 812 20396 20687 4562 18130 2771 16646 15035 22828 3332 5825 13077 1893 11085 1094 18867 13779 10287 19082 21714 11549 14525 5240 12593 18860 21708 14095 1146 25674 6963 16094 22830 17052 1148 13593 15109 23384 885 2774 21067 21406 3599 14391 19687 18752 9165 9669 20625 5081 17452 22498 26079 23327 3838 20867 3582 21929 21881 20281 20341 149 5088 25062 24203 21547 15170 10626 3095 14205 1160 2773 26044 17940 11673 18530 20823 7850 11187 23107 25019 10500 7409 13180 17757 21712 7933 7094 16006 17734 1959 6130 10002 11376 7360 6373 18777 1169 24588 21018 1170 8631 20371 23406 22723 24705 19865 26449 13685 2704 2173 11459 20654 11942 26322 15618 3895 17592 21885 8797 4633 13897 3996 13087 16895 15146 17534 1675 17438 13985 4700 24452 3816 16824 10045 13190 2046 17982 11442 20991 5418 3256 1184 5096 22481 2905 18884 9282 9551 23927 18638 21627 7350 20580 13961 18385 25845 17056 23069 10899 17539 9115 23554 19146 4623 24085 8565 21576 20305 1193 12788 25255 24823 8023 19462 16691 3054 4764 16561 21505 12480 4552 7664 13099 1198 24662 8578 1370 8343 22077 11040 22976 14844 7505 25537 1202 6986 3943 6254 1203 10238 23884 26500 20884 18589 5381 16896 13870 14926 966 24458 24647 21439 8480 23336 22997 10401 14604 19300 4407 20615 17626 16283 1211 7166 17417 9553 732 21776 5573 24664 11070 1214 8998 15127 1167 11754 20469 14456 17480 19716 6601 1041 1351 18938 3610 7270 19571 8315 2233 13204 16076 21749 23440 1389 13574 9523 22211 20570 14586 14714 3013 14104 20253 2188 1225 13596 23663 1226 26807 13214 24531 4846 26769 1228 1065 248 1229 10949 11011 20803 15123 22295 25346 3039 5789 18493 10304 13577 2225 18577 12706 1234 17695 11083 9054 26787 20372 12473 20617 11944 3128 13989 7147 19086 13102 1239 25535 13327 8224 11840 21333 13009 16049 14337 2223 18610 11951 17054 542 12763 23177 8324 5681 22781 19213 6693 7342 19662 20811 25874 5687 17802 22741 18477 685 18209 1249 12596 14703 12562 26959 1310 4467 8734 26924 18545 14292 549 12576 9349 7995 22502 12998 17375 24074 18714 11117 25120 88 22788 16716 4740 22839 18759 26207 4582 15726 7014 19860 12188 3425 21261 5120 10005 23643 1262 23019 8570 13687 24178 14160 11597 17928 4124 16908 12729 15951 25063 22418 19174 1267 13731 5276 1892 25812 23773 25368 23848 19 1270 19491 14066 25362 20202 3781 1272 5424 1273 1273 15596 3962 1274 13482 5907 10683 16670 11332 1369 17428 10365 21976 18773 26246 1278 22899 16770 25146 21703 20064 13725 10989 22457 15513 2785 13447 8578 14266 1883 15459 15422 2049 1689 2193 22813 20112 16941 9470 22635 12302 15767 23170 24095 23293 4864 14768 7009 25316 25193 13727 9082 914 24507 13062 67 2356 20145 13740 25285 2957 973 1243 5467 11371 11775 1855 6378 7872 9544 4869 16428 22772 18937 21074 1298 13346 25987 1011 24907 16985 19537 17508 9861 14861 25053 6051 11286 13462 22815 4839 9002 8709 7669 20264 19532 20644 513 15407 647 14863 22299 1307 10646 15036 18881 9353 25360 22096 20637 18971 1310 8486 13794 16786 4922 20528 25488 957 6073 753 14089 79 8162 18546 614 3222 16758 4500 1121 4113 15320 1317 1317 24739 6331 13075 17282 5066 24359 2848 4232 8088 20985 1201 4329 16570 17815 13983 16110 15523 2622 4241 10316 9889 5496 24688 1013 1326 1326 17003 5455 25866 25738 22853 22616 2328 10404 15684 9868 7170 12858 9223 14899 18398 16763 18393 3465 5372 9373 29 7693 5014 23454 13118 20311 16159 26842 19592 1336 23701 5180 1337 969 879 5879 7218 26486 1339 19142 16785 1340 22964 14397 6613 26640 17659 17894 9230 12991 21335 10906 8192 22101 15333 1364 9257 17489 17527 21306 114 24206 26142 4558 24460 13364 23657 2901 9669 16280 190 10675 5755 12679 1351 4602 4784 9424 8965 11657 1353 6716 7026 26623 15602 13635 5395 19443 26732 1356 3745 12120 1016 24488 2203 24990 19763 22274 17039 15415 19109 12344 15912 8444 13929 3068 17106 1362 10767 13990 24198 26414 9028 26580 14833 21653 8432 19800 851 14606 9838 9210 26882 22159 2461 7037 23680 5356 19697 1369 14050 8234 8111 18422 22606 14987 18060 7588 20820 16805 17112 21336 7654 622 20246 16215 22183 17442 1376 10989 1376 14092 21876 13348 13991 8306 26007 7502 26003 14766 20924 1380 7137 6149 26645 8757 7414 2918 25574 10402 18495 22330 18301 1384 22613 13561 26993 17956 1386 24802 24234 203 18734 1870 10124 15124 10036 25245 4405 5973 5395 8262 963 4431 1391 13047 17653 1392 9728 6756 2241 13617 14167 14930 1394 24102 16387 4662 11572 3801 18844 26600 16517 20045 22275 646 24595 15042 7327 21759 13944 19728 2072 19588 1401 24105 14338 15730 22327 14859 17763 9646 11052 9380 81 15789 976 12365 12203 23523 16662 5306 1407 17594 17157 26400 22160 13241 26097 1201 19151 9154 22042 534 19910 21590 26577 1276 16772 15341 6781 13237 2206 1414 2910 7959 3282 13146 15181 7232 17277 9292 4044 5300 19706 1298 22514 3142 1859 18070 22324 8676 21937 24664 25640 23912 7483 4398 1422 19215 19442 22818 19672 1424 4592 3369 817 21604 4223 17770 6866 8286 19022 21427 6185 22697 23484 26304 3973 21085 16318 10974 25171 12535 15786 14623 1432 6269 12461 15964 652 1353 11183 17378 14039 18294 1435 17411 4732 1436 11900 9200 9981 15368 2899 25483 9363 17591 13978 7506 26549 7672 26717 7441 12180 21633 15002 20578 7231 22 18606 23947 22953 1028 3900 5448 26168 24120 21355 3083 18819 4167 8986 6703 2925 7856 944 9780 13556 9284 25439 1450 4082 9758 1451 16878 9953 6452 1852 1453 5696 20024 14918 22638 14667 18983 9527 6327 4160 8589 7317 281 21460 16308 25634 8138 20767 14078 9086 11835 21673 11809 21001 13981 24424 16496 9451 4955 11118 20175 8618 5455 22736 2405 8989 22321 26300 17889 23914 23426 24575 20598 12774 3742 5065 23257 18201 1165 16760 21224 17779 1470 10915 8714 25079 6458 20365 1213 6760 948 3641 12012 22247 22367 450 14659 20038 2574 25116 12433 23121 18648 11232 9688 24926 14275 12934 12919 22375 10439 11064 9760 12096 7708 19396 545 18506 6575 14578 17838 14414 3211 22129 21844 17169 13408 24120 20021 6843 10166 2475 9087 6338 15679 8381 8597 1080 13596 14105 25076 26247 1490 7626 2190 12494 19427 15505 6025 13724 19909 8480 5416 7403 1219 20294 6570 1759 4474 5368 11080 15808 12281 1497 26401 20895 18287 7647 2403 16566 6934 17393 1500 14217 ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/input/1500.2 ================================================ 43064 1 21209 44843 14863 2407 39682 291 10043 50208 396 68 25033 24360 19976 41154 4358 1731 31035 24738 16050 46865 8 26168 27629 26932 21433 29595 26167 19498 36570 8766 1438 44629 8420 16721 28956 13800 25296 24314 7755 19062 30867 7362 14207 49913 18920 20981 45141 22748 18308 34563 21431 14783 24986 13070 699 33176 5713 6257 34953 21 17701 40775 10795 20726 31414 8143 12026 49548 1365 17968 40013 19172 4572 39851 22970 16378 30631 27 11310 45144 12761 26740 38236 3816 14008 37951 8107 4675 36006 4514 6490 25697 32 5597 38789 20817 18700 42123 22351 1290 27442 4819 23470 47765 21377 10356 32076 18456 19453 47015 6403 16894 45891 1767 24383 25553 10421 1560 41437 9204 19292 42947 25986 4802 47602 1043 20899 24776 25492 22913 31708 26205 13728 33626 15158 10259 41326 674 20983 27252 8144 26504 44912 15884 5516 42662 50 22599 42967 1763 25507 50989 8953 17716 36553 20760 14432 37375 15902 11123 24310 55 8359 44300 1040 3341 34144 57 1457 44835 14575 8151 40874 5502 17587 39077 20177 5364 29745 11744 22965 24362 1947 11206 41606 25450 4047 46284 14248 14112 47077 12273 8561 47491 21922 20914 46663 2526 363 48664 36 2065 26660 2533 11869 30119 3483 26819 37702 8695 23074 41793 19808 10816 33397 20305 23444 41043 15903 17359 31511 7027 21878 40720 19844 1540 26892 16101 22437 24679 13366 8507 39643 2818 47 31529 11968 13853 28325 1852 15545 39667 20962 2807 38762 6470 20950 31016 16604 26508 43185 24501 5048 34935 18366 16398 48355 18506 22199 45673 12405 7653 39901 5281 13337 51099 3719 351 50394 6270 15038 33157 9940 9081 39844 23189 20072 50871 9998 5990 32667 4106 5530 32060 22085 14821 40597 5865 3972 46939 98 3151 34991 3107 18875 29224 19457 16892 37425 5589 17024 46183 26307 24707 33491 10431 11914 30084 8296 10600 24781 4569 16841 32595 26370 11410 46095 2174 26579 47632 2257 7505 29465 3925 24144 39263 26323 2054 35766 10783 879 39740 24968 11880 31792 10009 16204 51155 12543 21530 37314 22030 11798 43392 15849 8569 36585 24728 14661 42594 118 20110 40406 119 14383 40644 120 14421 24744 20369 9 25038 21946 15359 36399 23102 13798 48197 124 8252 49361 26696 8909 25079 94 2142 35470 127 11255 50972 9037 6400 48581 4988 20380 26363 9815 4679 46338 131 18859 37624 1521 4900 27961 133 3248 24359 134 8203 28251 9511 25727 41788 136 20517 36589 24817 12116 47411 138 23823 29482 6574 26691 37872 15649 16324 43105 3789 7976 35943 12859 9342 36174 11567 8914 32324 6320 20968 30784 16428 15081 35595 22986 21367 34434 4827 18614 39792 2553 19441 27484 5544 15269 44666 8691 15611 38651 175 7231 27193 18360 20717 28197 17780 13921 27054 154 18058 46242 11086 23731 29525 26036 2369 43593 12053 26573 46255 14227 8011 30438 16583 21335 25068 3952 19264 46360 17849 7361 24462 5738 1346 42959 10686 22118 32893 5745 7721 31249 26048 10173 29159 12067 7259 33603 4786 1050 36265 8357 26357 36736 23172 7105 46579 12554 367 49330 13915 6843 39637 5161 15876 44364 20365 2501 24346 7014 8310 51206 6695 11922 33004 12733 19192 48365 19681 737 26222 23055 12842 24479 18198 23747 44653 10604 25577 24745 18680 12672 32066 11515 25710 49707 3170 14295 33249 1557 3752 40216 185 1049 43398 26759 407 37274 187 10398 41280 11601 2897 27161 22576 10680 28434 23814 26086 45030 9415 14407 32217 10216 5221 49264 23308 26620 40486 423 1103 27866 4294 19675 38760 18041 15484 29052 197 26608 38218 198 20083 51078 26906 13023 28593 25728 18920 48888 12345 2625 44683 14114 8503 34735 203 7923 49064 9825 19361 41929 24877 53 33711 8227 25347 40859 21175 13583 40196 6896 19013 30517 2980 6729 37270 20386 25666 36511 22870 19518 39213 20252 9185 41121 26216 6085 25855 6243 7022 28382 19394 7287 49305 6328 888 35672 8393 12812 42790 24103 5583 24519 16998 7203 45413 572 220 26969 22973 15701 30431 4235 21539 39563 111 17450 31481 6360 9424 31685 225 5964 44859 2914 6999 44759 2595 2587 41941 25081 13977 27129 20336 3176 51271 10358 6998 36267 25042 23647 33649 19832 24085 31656 15508 3820 26539 322 20010 48274 9787 3742 25525 16529 19857 44529 237 18749 41874 19342 19187 27131 20498 127 37980 240 23981 44925 21452 22857 25238 11914 25178 33266 243 2339 39424 8393 9033 45052 12029 2952 46487 7875 15379 34630 16866 7450 38276 26208 26840 30477 1196 20177 46235 25522 250 45594 25558 4331 31389 15593 26377 42828 15392 8621 37106 16499 25619 37251 2335 10567 26473 16469 26757 44213 257 3289 42563 12863 13706 44626 12062 18798 49477 16217 16172 28169 261 26197 43559 262 5691 34942 13879 11898 26156 264 23325 49168 11148 8441 24523 1610 4338 39890 4723 22339 27621 4169 13788 36956 18565 1677 36951 3686 22019 34966 271 130 36852 3552 3885 39744 26140 15900 47523 22410 7839 36082 5046 11291 50861 228 5273 48292 13928 8605 38103 17166 4854 44686 14266 23 33188 26256 12352 26901 20924 12484 28195 9055 18647 35698 5627 21187 35912 23596 17572 26164 18312 13200 44258 8926 214 24747 7199 287 24977 3296 4928 42376 25284 11401 26115 290 17023 31746 21723 10158 43109 673 26257 39380 293 9976 45975 14227 10891 26934 295 20199 32401 7173 20992 48757 23724 17564 31654 1378 4447 43503 14171 10414 28456 17641 1268 30257 1421 17944 50175 15270 16019 29963 23954 15642 25452 25088 15936 39736 305 5580 31134 12106 25791 42207 307 307 43680 26700 25257 50161 309 12309 42831 8475 22214 26819 311 20311 24612 5616 117 35024 4268 3009 29966 26207 15599 34770 10294 2099 31333 5673 5121 48577 19128 23117 27903 8014 446 29198 6359 17818 37740 320 12008 42968 6524 6529 36915 322 9258 47879 20886 9283 33213 5628 7417 40833 3045 10493 50018 326 14646 31774 21607 10079 45044 328 15909 39456 24084 12924 40926 8391 3810 48079 235 3286 47880 332 23716 50313 19909 8253 43434 21459 16326 34163 17906 26762 49252 11789 25200 35720 16737 13964 41470 11162 2394 24639 8755 24867 49181 6697 25516 38009 24517 7357 42815 18531 20179 43190 26223 24215 34073 26408 1608 40064 9444 12020 45955 346 3162 39655 11099 5286 32392 348 20689 33337 18584 10973 48831 350 1259 25350 7410 22946 39140 3608 17925 31984 5953 12425 45646 11970 6850 40802 25819 26494 25797 6292 2084 43764 6000 5568 41351 2654 3526 50126 20242 9911 36404 20808 17965 46968 23977 361 50694 13183 3967 45951 10771 10875 24317 364 8708 26012 18760 1000 50106 6683 12155 25347 12071 4314 41388 3957 160 31693 17033 22329 39430 23778 14394 50071 20566 22155 44400 8044 18204 35145 20933 21024 48554 25459 1811 37622 375 7823 27860 3885 2312 50045 377 14852 48947 378 23626 51247 11459 3478 39189 7873 15897 31041 15997 4904 50671 7230 2387 51262 9615 10506 48961 384 2437 39048 23129 20052 30507 9519 17322 39311 14846 14142 33888 15636 17121 48220 9168 1453 40970 17830 8771 43819 17399 11455 44041 18176 22464 46741 6537 1345 40174 14490 9618 40615 14710 5643 44165 24345 5673 50628 4024 18656 38031 12798 5934 38587 399 9183 27945 14701 4741 43325 1140 9969 30987 8498 12170 46807 5499 15150 29920 16852 2545 42308 20360 16997 48607 21803 13918 41830 24151 16274 44332 9120 21525 33608 3017 7740 27387 8279 25154 27191 21054 5118 30368 4708 15633 37036 15680 18872 44386 13435 25062 44907 18314 8266 41169 1184 22008 47605 21532 21012 49595 20135 13602 34407 16027 283 44461 19409 1868 49340 12389 23229 38314 3731 26715 33379 17938 6487 47441 11837 9485 34512 22673 21417 37950 8407 20570 50815 9963 19334 32864 10041 14833 39753 3885 1917 43818 18027 18563 27763 730 12738 46228 22197 7029 32784 7940 5916 32971 6127 23650 42026 2019 886 26312 6476 14393 33620 10501 20376 26530 23659 19942 50086 15634 23330 51124 440 20397 50776 7937 14396 44507 13466 11495 39826 11046 2323 28264 668 7796 25449 5552 22432 28170 9443 4907 36358 447 19799 45468 8573 448 44501 4012 23897 50326 14375 13714 37994 26646 11014 24437 15388 14188 42364 22568 2416 25799 13427 2979 48046 17210 12903 38049 14184 4240 27160 21905 19265 25230 458 8055 33151 17286 8747 37952 19121 21665 31553 17632 10072 26746 5699 14227 50182 463 3778 39985 17000 21997 24765 4329 7076 43947 19319 3159 25066 10323 15411 44080 1300 6497 45964 24008 21173 48839 11979 2563 37046 14418 17719 36812 24464 14688 31045 473 4001 44438 15807 7418 32863 923 2958 24853 24308 1249 50332 3344 7664 29503 7638 14587 47979 22298 8498 39681 3013 2013 34744 22028 20908 40347 18879 13527 29151 19926 26339 33048 19361 25412 25476 2237 12557 36050 7134 3459 40742 487 2626 28033 12509 3128 45608 2937 7236 33446 18959 2970 49554 26502 3507 31968 609 6617 43060 493 19797 36722 1795 20094 50078 2807 666 26340 10600 11264 27992 497 3209 24798 6631 26103 29039 1406 1854 41853 2369 8220 28492 26752 20469 50850 19147 6067 25846 1938 5330 49713 7597 8040 25469 9817 4412 45542 1866 17362 44871 21030 15670 30077 508 7484 25236 14024 23861 33034 6043 25366 38926 26903 22666 35673 5613 24373 37229 25801 7673 33854 5991 12258 33378 515 25862 39272 25276 17860 43665 2056 21517 39466 25187 22510 49502 18791 25938 24820 1216 1664 48600 12233 8628 43443 17199 19335 39287 12987 16899 50821 20 16124 36513 8424 25565 47258 21334 2782 49022 8815 10482 32116 23197 72 38765 529 23228 34078 11490 562 50935 531 2283 36936 11588 12689 43484 15957 21688 24834 870 20854 41382 11738 16938 47604 10173 4293 47280 20756 16145 33547 11279 21482 42834 20094 12307 29797 24756 16665 40577 272 5557 27178 24387 22699 35518 17655 3231 38132 25136 7784 29384 23428 18708 43366 21007 8719 39983 10646 8646 49733 21561 7553 48324 2408 3325 33418 347 11499 31830 6927 13074 30196 8197 21613 35976 6937 5377 50299 15754 8738 24490 8222 3462 45765 3545 11585 24401 12352 17773 35682 5963 19451 46115 5106 362 39841 2069 32 43917 4372 23972 44915 562 21455 35831 3603 2310 36093 22105 2137 26132 2624 25232 44079 16371 486 36710 9823 13626 44356 15565 26888 36077 22860 9873 26190 20487 1391 35431 2830 22126 27005 16345 5948 47372 573 11536 29722 574 1150 42987 16618 24426 43780 1813 23112 46120 577 8449 46299 578 22847 50138 18350 12278 46877 22185 20921 40649 2192 7741 32639 9819 13107 37203 19831 23530 34292 18829 13696 40165 585 18908 46526 6119 25474 26274 24107 15446 28816 21033 22657 36593 26093 9013 28074 18723 14555 45451 5127 7647 29372 20848 26904 33632 12937 24033 29342 3466 5831 40823 18899 26555 36573 596 9225 50964 10733 1813 36807 17302 25878 30123 599 599 43217 12837 24981 42920 1969 8892 49878 20911 20922 40847 603 4659 42752 24425 5609 30260 16376 3600 34794 9206 19467 40531 12175 23711 29868 18696 1469 30125 10697 21305 47366 4626 25290 43826 611 115 45381 6284 25721 47537 11197 25661 40226 24019 25203 25206 4954 16570 24332 8733 15880 30608 12649 4036 26614 9122 10431 49783 24790 18995 42645 620 21625 24473 7749 4213 50834 7686 23870 27926 12794 24143 32009 21312 22576 39981 25748 19356 35755 8826 21399 27871 14774 11371 39573 6092 628 30497 1589 10821 32050 6227 21779 38622 5162 15402 40825 23288 3957 38736 16164 7004 31571 22578 24594 41999 2238 5526 37461 8441 13793 45233 23208 2920 44946 3174 26510 35219 16639 18770 50556 19661 301 41848 23985 4337 29227 5751 25250 48602 643 8563 35232 19633 6081 28193 9040 18437 36178 566 1299 43251 26191 55 48748 648 22400 48373 649 13177 26835 21471 15674 37658 13934 26022 32048 652 18876 27588 23672 8544 46823 6174 15331 27286 655 20746 46180 1328 25181 43328 22729 8177 47566 2778 10047 48655 18998 25891 27352 660 22209 46713 661 16245 47874 10230 4699 35982 11474 20695 27513 15376 15989 40264 12668 18052 33107 22498 5783 25671 19854 8107 32032 21897 14788 32908 24840 11709 39519 7987 9302 24782 4698 10626 36913 15821 3656 24328 14921 7561 38534 9903 26394 37370 22806 4230 44357 676 5460 41460 13184 20533 27967 678 26763 24579 4290 8679 24772 11789 5336 44800 980 956 36950 6375 21290 36690 16755 22038 40928 24849 1081 26561 5829 18533 43711 686 11787 32686 3031 16146 24825 23128 16901 44104 689 16276 46742 12751 2506 28223 2230 22486 33605 20449 25132 43476 693 693 37074 15715 6750 39318 22618 6330 44241 4685 19968 38373 9233 22196 31150 19367 1591 42802 699 17702 34445 16300 19273 37748 701 5477 33922 1019 24558 48326 8850 20823 40625 5157 20448 36789 15052 20260 28086 24095 20194 50114 20595 26643 33992 20764 5449 34884 25069 3173 25623 1915 3891 40646 7306 21018 29249 12325 20856 29296 6817 15625 29091 12378 8394 44218 18299 17131 31317 16612 26772 47721 717 20392 40415 12694 11414 28587 10730 13218 30612 11821 26021 45853 16265 4116 49515 11295 18642 37823 4326 22822 34597 7481 7297 50337 1264 24912 24386 11971 10331 24486 8106 14423 29836 10016 24608 35277 729 13457 44299 431 3290 44586 16235 11923 33976 7641 3529 35868 733 6885 28327 18606 17558 38126 18426 19311 37508 8904 736 38421 5993 17625 48742 26335 10103 50410 21107 26766 32213 15313 25969 45676 12517 24653 29274 14646 15230 25099 1871 17106 49969 1909 2973 27192 24753 6481 46571 3367 14074 36447 23566 21843 28965 18105 6273 25148 15269 5032 40599 9286 14342 27214 21874 1954 37620 26685 14493 47845 4953 3089 48131 6650 8015 24634 12070 755 41328 16833 7617 33185 13285 12813 27746 9587 14123 39654 759 8255 43305 760 19685 34680 5580 17297 49483 13714 8698 49903 21686 2811 42045 20633 9953 32497 765 5069 27343 20435 12963 49198 17775 17954 48004 23757 56 47008 3060 529 48390 23799 13311 28522 470 3763 36440 19060 17748 26292 6981 16789 49807 10635 3323 42595 7847 11535 47185 14336 5301 42581 21244 17196 26486 14802 21127 37063 12758 12190 37669 8668 16924 26316 13264 12448 47719 534 24414 38155 12623 10514 37049 24269 5317 47360 7604 785 28507 19498 16031 25087 19595 12326 46341 5172 788 48476 23309 26309 32890 5451 2102 46947 14695 23495 50409 3813 2717 37149 3065 16724 50307 19199 12210 30319 13667 6902 26909 4049 4713 26657 5245 13784 35935 15307 15758 40814 10351 18842 48553 3893 6384 44637 8129 6772 36270 6994 18687 50826 21507 11262 43605 948 5745 37116 1509 14485 41346 2491 25302 50731 25727 19351 37356 18584 26448 32976 19572 22929 44454 4450 10466 25874 811 3979 49872 25100 22276 48193 21824 23597 34263 4275 11174 25710 24274 22351 26740 8197 15808 38613 16732 11492 44147 2703 5359 27458 14054 23526 35208 17097 16785 38004 16008 20568 45202 7854 24099 26318 20802 21682 32356 824 26013 37768 11609 25684 38814 826 826 25127 1347 24214 28117 241 11860 29916 5712 4813 27135 9342 4195 38731 8930 1967 26393 12789 9149 43789 500 19401 29867 20810 24066 38199 9763 587 33384 24161 897 48916 5920 13544 25138 838 13659 26158 26687 24727 41009 19824 4496 35813 13060 1284 47627 24962 9559 45658 26854 11534 31768 844 15500 37705 3624 1709 35714 846 1718 37243 4714 21882 36033 15069 11112 25568 21628 23076 47227 17639 26058 39234 15363 7574 50901 852 15825 49260 24968 16520 44562 1995 9910 28142 4298 4703 34625 22197 8557 43149 4740 26476 28990 21591 858 38015 7883 17054 47789 1508 25956 24452 8400 6880 47319 862 5915 51283 26639 17703 28345 25013 14960 37616 1977 10460 29302 12151 9719 39946 7462 3571 27408 19945 19201 26932 3741 25309 28895 15043 8699 24699 2386 17767 42260 872 872 38056 9969 4729 25934 21447 25559 49671 10926 3558 50781 12724 14476 35292 877 9901 36778 11995 9838 47235 12746 15314 33977 880 4920 47576 9548 16004 29387 14527 12274 32215 1390 6534 37208 18921 14001 49156 3544 23880 44298 886 26011 24526 11026 22418 46345 1597 13808 29917 889 10292 45315 1994 9018 25546 17470 15267 40917 7417 15945 25801 12312 16509 47087 26243 9467 27243 895 1986 38377 8253 23104 36336 18417 26212 44811 2791 2218 24431 6998 26267 47317 900 13729 37892 901 12648 25202 8179 9243 51211 21826 24826 36849 15093 18965 48021 15052 20817 31062 11658 18722 24930 13771 107 51240 22044 1604 38585 20165 13085 33210 23019 94 28827 22010 3978 49804 21048 26621 45704 5780 15289 33923 23842 12834 24474 12206 203 39192 25252 8164 36009 12064 15448 47975 6907 22318 35707 5127 3458 46585 13344 2557 40096 11745 22313 36091 24607 3074 41711 13630 17179 47493 22660 25121 24516 20224 9581 43751 14267 13678 34203 3226 3135 37385 928 24869 25408 10980 14105 31430 12175 22546 24450 366 17347 49797 22929 4564 40404 5229 16832 50783 9774 9611 40502 12426 22346 48356 936 26184 46933 1153 16604 31779 16898 21154 43242 2499 22603 45621 7516 19684 29364 24744 14845 36506 14787 7534 50118 6159 239 32764 7656 16093 50560 4489 16225 47782 13151 7370 30258 24494 17454 37565 5425 12625 41580 4904 24917 29530 26883 7766 45643 9562 1722 32428 9448 13109 45320 20460 21260 29531 17058 10463 27775 24350 15459 50621 4913 21324 41148 7032 22336 26207 20366 21219 49710 14351 1463 25260 26901 19856 33789 4228 25404 47254 18314 15410 35602 963 16982 27709 22004 457 30521 15976 26133 36159 2891 26526 48139 16679 9034 25268 18400 14325 25872 23732 12212 26790 970 10826 33018 24067 26547 34725 18033 977 43609 997 16400 32122 16374 14358 25614 22394 13671 34497 7056 3816 34480 8444 16249 29670 25338 15527 33762 15102 22542 31949 4993 20905 38116 11104 22944 25591 23110 21211 26334 8402 11503 51001 25392 18064 48653 16332 24356 39491 17391 23570 28351 17998 11875 36512 15985 6236 40601 8645 16941 31143 14603 10427 33678 991 1714 32673 15541 22112 29760 13500 12353 29747 4122 15727 45554 9347 995 32152 11668 19097 28817 25168 10765 35055 23902 11294 48038 14719 4130 34689 7021 23480 37432 18577 1548 47462 6111 21898 33002 25547 16571 30304 9609 5060 32121 6869 20384 41327 1006 15339 33275 1007 4690 32481 25461 1008 46104 1009 9313 50667 10551 10855 43199 1011 25699 48232 988 21729 41724 7192 21221 27959 14374 9011 41814 22671 20383 45556 25205 19176 39157 20380 20657 50654 23055 6047 26626 1019 8542 28333 5588 21953 47868 15509 13392 35778 1022 2326 39179 21383 439 33884 4072 19317 35557 3020 18457 32979 7218 10402 31418 14342 20734 46560 18140 22788 28260 20813 11712 30202 18059 8926 48726 21274 9479 50921 21488 21016 33432 1033 25561 42995 1034 25058 47218 7430 25550 25688 6604 23220 45985 14216 24744 44154 20798 21963 45523 24735 23058 29905 1040 24709 47696 19220 22212 25342 14007 12482 50223 1787 24470 34221 9521 14252 50532 16032 6448 28250 23043 15814 25147 9967 24751 34564 1048 24200 39525 17273 7425 26390 20871 20490 26322 4806 15942 27856 1633 3545 26276 7584 11861 37815 21766 6798 42670 15642 26378 32372 24181 1496 33957 8289 14644 29118 10514 17567 30250 16091 16987 38232 3569 1060 29521 13733 4349 32818 24539 14131 27510 18402 12362 36489 1064 13045 48320 15804 24641 49243 9031 11375 43287 26715 20830 41093 21681 12449 44905 1376 10109 50266 25179 8214 35142 2554 19423 33801 22453 16080 48957 22737 9164 40707 12834 6215 49114 23254 3075 37653 1076 11684 44868 12864 18493 29562 6878 10955 31467 1079 11639 38569 21669 9288 35757 23012 9761 41203 18218 9023 29858 25926 4267 29013 1084 14361 24844 24045 18309 40482 571 20806 34830 22370 18682 47097 1560 26453 41021 17137 13297 41467 1834 6679 29831 10859 18283 47109 13636 2873 32844 3248 752 45895 1094 15795 43038 11415 19807 40873 8101 13216 45192 1097 436 29611 11295 16207 28575 24582 7742 39389 25553 10545 40532 13864 20133 26778 22342 15470 34694 11722 466 27100 22968 1733 34192 21985 7268 25854 7495 1106 32930 15182 9270 28880 745 21172 30868 19024 21821 37402 4374 5587 30262 26570 20794 39569 19085 20149 29261 1113 11020 25699 14503 17162 41626 1115 302 29056 1876 24849 28980 12461 19413 45066 18542 3067 44355 24658 4066 37604 20757 13312 44384 18065 16705 46846 10735 15138 25834 2358 13003 31856 10657 2556 48372 24888 3304 36295 2806 17059 27395 4850 24242 32569 14125 23157 49496 8188 18404 30350 5426 9810 29498 21110 3382 43584 8729 2937 50089 1133 3277 44703 1134 12987 33515 25058 10338 29628 1368 3072 25437 17724 9100 39723 7511 20970 46159 13203 24323 25720 26772 25860 38444 9285 12096 30074 17795 23627 39907 16354 13871 29164 13912 13384 27608 132 16049 26827 1146 19223 39122 3214 8838 25448 1148 17004 25836 5328 3360 47146 17070 9547 34390 24375 4186 48028 3397 20125 25616 22577 3628 32182 11610 6698 37226 5782 6819 40632 24937 11340 32396 3768 5765 25607 1158 24363 40339 15463 18503 41465 1160 19885 46928 22737 20892 27779 11834 6775 45879 14326 15675 47192 14908 9001 48020 25760 12549 27850 6131 15659 27830 1167 7898 39969 1168 7933 36928 19737 19377 28411 999 14106 41546 24790 10926 34485 11276 23057 25473 4576 25520 37791 23574 13179 26926 7394 554 33956 14520 4592 35696 12252 12561 37931 21479 1178 27138 2062 9867 45533 7721 1116 50081 15008 1181 41487 26219 21846 32262 9831 25690 37361 17325 25072 30912 20276 9441 35387 18114 15762 41378 16579 1187 27013 11620 17953 25489 24384 18149 43146 8931 26686 44974 11274 24546 26868 1192 9016 48245 17801 8532 36555 1354 22890 41986 22574 8835 37941 12628 4313 30289 25741 18109 41495 1198 1198 40987 11543 20250 32892 5136 22064 47752 11817 10300 51014 8319 6687 40074 1203 19003 47304 23596 24841 37532 8997 13357 27346 10931 23438 44558 511 58 48705 13837 19509 38088 18588 2172 31963 22015 3442 50482 10059 686 39381 23089 22524 43100 6349 23613 36431 5502 1214 24875 22594 20586 32601 17336 8853 29312 21385 5241 29203 1218 16143 31042 21638 12411 38301 2148 8169 45361 1896 10269 48722 1222 17107 28531 6487 9879 28884 24544 9568 41661 17841 12225 25779 21162 18135 28738 14790 16198 35224 17745 9812 47356 23973 20861 28850 3939 9747 42614 7778 12327 38396 9445 25848 49389 20916 19604 48238 18834 16546 32135 9046 1014 31773 1628 14257 46268 1237 22085 29327 1238 18886 49062 23687 17938 27097 1240 21557 30141 23212 15249 25542 1242 9287 28191 13870 14515 24621 14593 10537 33068 3080 14296 47986 26971 15299 42318 17130 3226 35201 25437 7429 50248 18324 14108 33235 19231 17082 32423 1251 26419 39560 2873 4932 45372 24344 26165 44743 1955 10246 36550 1255 10538 32148 1256 20485 36896 26348 1473 29742 14898 18071 49690 9966 8195 40501 1260 5020 24865 1101 18160 33410 20846 21310 26843 23458 20962 47681 16365 1264 43965 1265 19668 38422 25463 7826 50607 1814 23406 36600 23444 18217 43916 19984 16656 33946 19798 1758 47574 8919 13034 40116 1272 14952 36565 732 9457 51030 1274 6834 35506 10222 4115 32341 14673 24220 49793 20712 21869 37215 6675 339 35422 1279 25138 25972 6512 13525 48680 1281 10796 34627 16799 10519 40834 22230 20451 49421 7108 21228 45692 4541 25549 51239 15822 17790 47667 1287 25274 37593 9424 26037 45861 21916 13396 28035 6855 23138 28727 5539 9003 41925 19697 1884 24708 736 4061 37346 3323 26307 28675 24154 3714 31684 19608 17856 44216 5740 24972 41046 22407 12359 44026 18739 5422 25093 3033 15161 51220 1845 17333 28626 1302 11963 28915 17335 20714 50601 5613 5029 32400 12116 16996 50963 19186 2682 39127 18382 20163 24981 8385 15700 30652 1309 8808 48367 13563 19214 37467 9954 18602 28380 20621 6957 50032 3665 8225 39446 19450 10903 47303 20035 4979 40488 4153 188 48692 24720 965 30314 23750 475 34886 4799 6362 40444 14248 24773 25621 1377 12889 47630 6103 1943 34218 2158 20075 40448 15809 22236 37913 12173 1821 43082 1326 12502 27430 20090 1498 50812 1976 2869 32349 11657 2196 32563 1330 4978 25631 18782 24395 48896 1332 12436 48868 26693 3920 44559 7158 12483 50174 10490 21658 40033 1336 21565 47837 18833 16780 41934 18954 6207 43415 9822 6867 32336 4356 19156 50697 16160 8472 33919 2134 11838 48163 15919 10978 44692 3869 1792 28837 24697 4505 34499 14850 3191 44234 20894 23270 47221 22289 11977 30220 4021 128 39594 24702 5667 43102 22215 14167 26700 7333 11208 44981 6689 9881 44027 26031 26439 33810 21219 11859 28725 1356 17780 42633 23856 13184 46714 22883 12235 30190 5191 4698 25129 5181 26864 50085 17697 425 33862 15991 22535 32119 4731 20606 48325 10289 16060 46212 9296 640 45610 830 14915 40630 11199 11394 49313 23336 3301 42776 14009 5276 38326 5967 17402 37799 9947 8094 38768 20945 9268 35369 11888 18136 27858 1374 24651 38195 1375 25263 41569 18672 8928 36232 21988 22900 41803 6962 22279 25655 379 18795 25680 1908 23225 41137 6632 168 37783 26662 6283 30614 26711 14399 29993 24320 3397 25357 10204 19964 33691 12954 24346 47751 20323 4486 41496 1428 21628 41236 64 10200 26770 1467 7411 39030 10042 19226 47457 25888 13661 24304 617 12644 36483 9375 20183 50551 1395 22987 41368 8692 10988 43900 16413 23037 29783 16723 1398 38107 4650 16994 43196 19917 7637 48277 20457 17449 48643 24567 13890 38474 16814 8219 45752 10793 15561 28633 2800 26637 47242 2987 803 26718 11943 11663 44700 12160 13533 33336 1409 16577 27790 25999 6135 44194 10606 22494 49125 8489 15748 40292 3893 17760 45170 1414 14214 46638 9079 3983 35057 19693 22445 50704 16097 26516 44358 16447 22527 37082 6043 11382 39136 17121 11609 43113 11736 4445 40698 3203 22067 27758 1423 16682 46988 22885 9869 42197 7356 676 29075 26975 359 37807 1427 19790 25728 12860 7081 50228 2024 13336 41175 9219 1051 46214 1431 21978 28788 21293 2944 45069 1433 2412 31846 1434 383 32458 15238 10363 25237 12108 20489 48969 17389 10029 44231 3142 25803 37163 9407 26506 48993 11045 22621 43848 2113 15924 46886 16335 20674 45943 1443 16406 32149 15305 14676 35340 6888 6005 35743 23430 12678 40179 21858 21423 33508 11037 9632 38656 21468 20601 34710 10343 18706 42386 1758 13227 43397 8521 10977 27476 1453 12021 28887 18886 20051 25451 1455 1711 41761 17637 18512 39608 24265 6401 35358 8218 24023 34706 15470 20979 28952 22153 21681 25780 2781 13152 36671 9582 8971 51150 15866 522 44404 17408 26957 29776 1465 6337 45587 7546 19418 30562 1211 22955 45781 18516 26697 29313 23008 17301 37431 1470 8043 25731 24007 7658 25457 5149 21701 27208 22225 2092 29627 22015 1095 29210 2702 9211 50349 5572 11065 32425 14853 280 46879 25046 5195 30803 8903 4178 49284 10288 2597 28648 20097 1908 43067 1482 11967 44530 1363 26731 30165 16540 1124 45337 13248 2864 29223 8798 7915 25766 1487 11362 26436 2448 10941 35253 18369 6225 25275 3274 25607 46239 14467 17339 50992 18868 23884 39884 23576 1528 37386 1494 23027 49158 1495 9423 47756 6501 3517 37773 12905 24425 36891 14439 8159 48922 3366 26966 39405 5828 22497 ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/input/Main.hs ================================================ module Main where import Sort import Control.Monad (replicateM_) import Data.List (intersperse) import System.Environment (getArgs) import NofibUtils (hash) main = do (n:_) <- getArgs replicateM_ (read n) $ do (_:s:_) <- getArgs f <- readFile s print (hash (mangle f)) mangle :: String{-input to sort-} -> String{-output-} mangle inpt = (unlines . sort . lines) inpt where sort = foldr (.) id (intersperse reverse sorts) sorts = [ heapSort , insertSort , mergeSort , quickSort , quickSort2 , quickerSort , treeSort , treeSort2 ] ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/input/heathcote3.prob ================================================ -- G. Heathcote -- Reading Observer, 1904 -- `A charming little three-mover constructed with -- the art that conceals art.' (Phillips) - - - - - - - - - - - - - - - - - - - - - - - - - - - - P - - - - - - - p - k b - P n - K - - - - P - - - - - - - q - - - - - - White to play and mate in 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/input/rsa.faststdin ================================================ module Rsa (encrypt, decrypt, makeKeys) where encrypt, decrypt :: Integer -> Integer -> String -> String encrypt n e = unlines . map (show . power e n . code) . collect (size n) decrypt n d = concat . map (decode . power d n . read) . lines -------- Converting between Strings and Integers ----------- code :: String -> Integer code = foldl accum 0 where accum x y = (128 * x) + fromIntegral (fromEnum y) ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/integer.mls ================================================ :js import "../../mlscript-compile/nofib/integer.mls" integer.main() //│ = "[true,false,false,false,false,false,true,true,false,false,false,false,true,true,true,false,false,false,true,true,true,true,false,false,true,true,true,true,true,false,true,true,true,true,true,true]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/knights.mls ================================================ :js import "../../mlscript-compile/nofib/knights.mls" print(knights.main()) //│ > //│ > Knights tour with 0 backtracking moves //│ > 1 34 3 18 41 32 13 16 //│ > 4 19 64 33 14 17 44 31 //│ > 35 2 37 40 63 42 15 12 //│ > 20 5 56 47 38 45 30 43 //│ > 55 36 39 62 57 48 11 26 //│ > 6 21 52 49 46 27 60 29 //│ > 51 54 23 8 61 58 25 10 //│ > 22 7 50 53 24 9 28 59 //│ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/lambda.mls ================================================ :js import "../../mlscript-compile/nofib/lambda.mls" lambda.main() //│ = "[\"C\",\"o\",\"n\",\" \",\"3\",\"2\",\"4\",\"0\"],[\"3\",\"2\",\"4\",\"0\",\" \",\" \",\"[\",\"]\"]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/last-piece.mls ================================================ :js import "../../mlscript-compile/nofib/lastpiece.mls" print of lastpiece.main() //│ > Success! //│ > accchhff //│ > aacnghhf //│ > innnglhf //│ > inmggllf //│ > iimgelkk //│ > dimeelkb //│ > dmmejkkb //│ > ddjjjjbb //│ > //│ > Success! //│ > acccjjjj //│ > aacffjgb //│ > hhddfggb //│ > lhhdfgbb //│ > llhdfgnm //│ > lkkennnm //│ > lkeeniim //│ > kkeiiimm //│ > //│ > Success! //│ > ammmmccc //│ > aaiimnch //│ > iiinnnhh //│ > gggnlhhj //│ > fkgglljj //│ > fkkklbdj //│ > feeklbdj //│ > ffeebbdd //│ > //│ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/lcss.mls ================================================ :js import "../../mlscript-compile/nofib/lcss.mls" lcss.main() //│ = "[30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/life.mls ================================================ :js import "../../mlscript-compile/nofib/life.mls" life.main() //│ = 468 ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/mandel.mls ================================================ :js import "../../mlscript-compile/nofib/mandel.mls" mandel.main() //│ = Pixmap( //│ 25, //│ 25, //│ 75, //│ [[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[3, 72, 72],[5, 70, 70],[5, 70, 70],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[3, 72, 72],[4, 71, 71],[14, 61, 61],[55, 20, 20],[4, 71, 71],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[3, 72, 72],[6, 69, 69],[6, 69, 69],[59, 16, 16],[75, 0, 0],[7, 68, 68],[5, 70, 70],[4, 71, 71],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[4, 71, 71],[4, 71, 71],[5, 70, 70],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[39, 36, 36],[23, 52, 52],[3, 72, 72],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[2, 73, 73],[2, 73, 73],[3, 72, 72],[4, 71, 71],[6, 69, 69],[6, 69, 69],[6, 69, 69],[9, 66, 66],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[8, 67, 67],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[3, 72, 72],[3, 72, 72],[4, 71, 71],[6, 69, 69],[19, 56, 56],[75, 0, 0],[24, 51, 51],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[24, 51, 51],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[3, 72, 72],[5, 70, 70],[6, 69, 69],[18, 57, 57],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[6, 69, 69],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[3, 72, 72],[5, 70, 70],[6, 69, 69],[18, 57, 57],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[6, 69, 69],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[3, 72, 72],[3, 72, 72],[4, 71, 71],[6, 69, 69],[19, 56, 56],[75, 0, 0],[24, 51, 51],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[24, 51, 51],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[2, 73, 73],[2, 73, 73],[3, 72, 72],[4, 71, 71],[6, 69, 69],[6, 69, 69],[6, 69, 69],[9, 66, 66],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[8, 67, 67],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[4, 71, 71],[4, 71, 71],[5, 70, 70],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[75, 0, 0],[39, 36, 36],[23, 52, 52],[3, 72, 72],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[3, 72, 72],[6, 69, 69],[6, 69, 69],[59, 16, 16],[75, 0, 0],[7, 68, 68],[5, 70, 70],[4, 71, 71],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[3, 72, 72],[4, 71, 71],[14, 61, 61],[55, 20, 20],[4, 71, 71],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[3, 72, 72],[3, 72, 72],[5, 70, 70],[5, 70, 70],[3, 72, 72],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[2, 73, 73],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[1, 74, 74],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75],[0, 75, 75]] //│ ) ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/mandel2.mls ================================================ :js import "../../mlscript-compile/nofib/mandel2.mls" mandel2.main() //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/mate.mls ================================================ :js import "../../mlscript-compile/nofib/mate.mls" print(mate.main()) //│ > - - - - - - - - //│ > - - - - - - - - //│ > - - - - - - - - //│ > - - - - P - - - //│ > - - - - p - k b //│ > - P n - K - - - //│ > - P - - - - - - //│ > - q - - - - - - //│ > //│ > White to move and mate in 3 //│ > //│ > 1. N/QB3-QR2, //│ > if K-Q5; 2. B/KR4-KB2, K-QB5; 3. Q/QN1-KB1++ //│ > if K-Q7; 2. K-KB3, P/QN6xN/QR7; 3. B/KR4-K1++ //│ > if K-K7; 2. N/QR2-QN4, //│ > if K-Q7; 3. Q/QN1-K1++ //│ > if K-K6; 3. Q/QN1-Q3++ //│ > if P/QN6xN/QR7; 2. Q/QN1-QB2, ...; 3. B/KR4-KB2++ //│ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/minimax.mls ================================================ :js import "../../mlscript-compile/nofib/minimax.mls" print(minimax.main()) //│ > OXO //│ > XWin //│ > X|O| //│ > ------ //│ > |X| //│ > ------ //│ > | | //│ > //│ > XWin //│ > X|O|O //│ > ------ //│ > |X| //│ > ------ //│ > | | //│ > //│ > XWin //│ > X|O|O //│ > ------ //│ > X|X| //│ > ------ //│ > | | //│ > //│ > XWin //│ > X|O|O //│ > ------ //│ > X|X|O //│ > ------ //│ > | | //│ > //│ > XWin //│ > X|O|O //│ > ------ //│ > X|X|O //│ > ------ //│ > X| | //│ > //│ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/para.mls ================================================ :js import "../../mlscript-compile/nofib/para.mls" para.main() //│ = "In the constructive programming community it is commonplace to\nsee formal developments of textbook algorithms. In the algorithm\ndesign community, on the other hand, it may be well known that\nthe textbook solution to a problem is not the most efficient\npossible. However, in presenting the more efficient solution, the\nalgorithm designer will usually omit some of the implementation\ndetails, this creating an algorithm gap between the abstract\nalgorithm and its concrete implementation. This is in contrast\nto the formal development, which usually presents the complete\nconcrete implementation of the less efficient solution.\n" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/power.mls ================================================ :js import "../../mlscript-compile/nofib/power.mls" power.main() //│ = "[0,1,1,2,5,14,42,132,429,1430,4862,16796,58786,208012]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/pretty.mls ================================================ :js import "../../mlscript-compile/nofib/pretty.mls" pretty.main() //│ = "-42@This is a string\n, \nThis is the label\n\txxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx\n" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/primetest.mls ================================================ :js import "../../mlscript-compile/nofib/primetest.mls" primetest.main() //│ = "[\"Composite\",\"Composite\",\"Probably prime\",\"Composite\",\"Composite\"]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/puzzle.mls ================================================ :js :re // nodejs stack size 8192 can handle this import "../../mlscript-compile/nofib/puzzle.mls" puzzle.main() //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded // // │ > Solution 1 // // │ > Time: 0 // // │ > ---------------------------------------- // // │ > Bono | | // // │ > The Edge | | // // │ > Larry | | // // │ > Adam | | // // │ > ---------------------------------------- // // │ > Time: 2 // // │ > ---------------------------------------- // // │ > Bono | | // // │ > The Edge | | // // │ > | | Larry // // │ > | | Adam // // │ > ---------------------------------------- // // │ > Time: 3 // // │ > ---------------------------------------- // // │ > Bono | | // // │ > The Edge | | // // │ > | | Larry // // │ > Adam | | // // │ > ---------------------------------------- // // │ > Time: 13 // // │ > ---------------------------------------- // // │ > | | Bono // // │ > | | The Edge // // │ > | | Larry // // │ > Adam | | // // │ > ---------------------------------------- // // │ > Time: 15 // // │ > ---------------------------------------- // // │ > | | Bono // // │ > | | The Edge // // │ > Larry | | // // │ > Adam | | // // │ > ---------------------------------------- // // │ > Time: 17 // // │ > ---------------------------------------- // // │ > | | Bono // // │ > | | The Edge // // │ > | | Larry // // │ > | | Adam // // │ > ---------------------------------------- // // │ > Solution 2 // // │ > Time: 0 // // │ > ---------------------------------------- // // │ > Bono | | // // │ > The Edge | | // // │ > Larry | | // // │ > Adam | | // // │ > ---------------------------------------- // // │ > Time: 2 // // │ > ---------------------------------------- // // │ > Bono | | // // │ > The Edge | | // // │ > | | Larry // // │ > | | Adam // // │ > ---------------------------------------- // // │ > Time: 4 // // │ > ---------------------------------------- // // │ > Bono | | // // │ > The Edge | | // // │ > Larry | | // // │ > | | Adam // // │ > ---------------------------------------- // // │ > Time: 14 // // │ > ---------------------------------------- // // │ > | | Bono // // │ > | | The Edge // // │ > Larry | | // // │ > | | Adam // // │ > ---------------------------------------- // // │ > Time: 15 // // │ > ---------------------------------------- // // │ > | | Bono // // │ > | | The Edge // // │ > Larry | | // // │ > Adam | | // // │ > ---------------------------------------- // // │ > Time: 17 // // │ > ---------------------------------------- // // │ > | | Bono // // │ > | | The Edge // // │ > | | Larry // // │ > | | Adam // // │ > ---------------------------------------- // // │ > ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/rsa.mls ================================================ :js import "../../mlscript-compile/nofib/rsa.mls" rsa.main() //│ = 27333540773077035891319648583115426924319682304972207061138531859687685497972324052329603672575247712822051033469833084249533708345668063349995687590898179232348381885068093916042332376874471111951277359240086024689478630903872483533692492045159120070581960228683268092981264305946420008131657132188411691046890656423813430758797867405414254427252889799598265252801351427969270327221859277538276931835433884202770274573975497479910055419604950116998779489424711382329456954759006285308055617249211518517251626537584144819963887585424355716160948313809816237911911714261525254960504461968887058634176692781918761529885191647640353988451378487593523928746420834635489374763284035135024554501992592243054651704669361294874639088141090295124111150498567814209355019358035109087855352831799613561922371560229756096541243234407756387955273981612894548796096568181979612805473591487774278882312329731123289196221054300160041623910405907021509065754120986913156062146884015988300349866352421512547114991188706014109057361892879580430212109312752658248376273202302160796406164353595904770530340704402942462200108055227350082862003023696909347848580847463903352486983305887517122394068078234691266091519485573727824252527733372590392886198860292659649377855405883044150450559660371076194186374359910112808222171852893169058046390627297042238730809765588373104750n ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/scc.mls ================================================ :js import "../../mlscript-compile/nofib/scc.mls" scc.main() //│ = "[[1],[2],[7,5,6],[3,4]]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/secretary.mls ================================================ :js import "../../mlscript-compile/nofib/secretary.mls" secretary.main() //│ = "[0.3,0.3,0.3,0.34,0.36]" ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/sorting.mls ================================================ :js import "../../mlscript-compile/nofib/sorting.mls" sorting.main() //│ = 51324117188054929115411819706576305711291550658570113274291768822777898552447837804420193403034280334581113235670099168108057310001444698188883902000871912629747889286942274533143362191347715770581074120684028066661549409447053566864805982796651442411825424058139343253169753750307013113236996387277789074623630137288987589869641913268610508598505040964906263227437054452571059796961267985287441539342423613634888118731387934522320394294636575704912348025851038478882488022787377510287490741895262233989080994241592338442788051616303082788441666485156091668196999668892850523930511918729736856089560984406499736606084171798216360770991023233180481976810767432750226966921480421914178641779179040473344962763594357880599983085435482161632405644389584578524102239884780307588440751057343783694537559222924711205140877849398042544407611747227163056408231621727687968160470282720794027470616537695447289808133239223198869n ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/sphere.mls ================================================ :js import "../../mlscript-compile/nofib/sphere.mls" sphere.main() //│ = 5303517076988315313332949801059915491159227238911114869988477691048042454660240028235263286419219487056215941279053460510402913638191134284712332734906453769177119866479549825710564164275378353165954211251783583776613466486364346857260327927988942302708582821755390617347892889203137614326710267631036346563969292879841889783895830486185094255970382302038580666396338511666737651046972647664365680026395064916507788381669683616268208254545568013323075147569343527502482992453415977736934966646039451380029114116189124196276320031764844380624699920031305131672697387138852897706575918870327168611841752330951914241998117147850328297747558099955601889252104175852174057287066122859365466714696330034330699419079661953966001855819730466637347813009964893758393398194456345159379187006376036876209312080525632387380670119254556148852520127502599030787981579016179740021934386773631075580149533017386555553650112461351624483964765154488441338857870911043358714412530793575995251653967501366725439356901586292297118595437194582661104059821332957540366106419721198265755243343224842512131366898546251377194773492369372328808207397107344802194547364640358333075609512226775541628703229778371160920064859614452908773471314342954366183109912759985134547433205776906834992238207194263830836275456360444786309396200054415810563334317160702817096977297265083167419253864506146719603442916146256166128051227049046678660445048488558024838465181996990391375870148258607829684558969016638984826787347541150003498463027973936442463090071589323955227296902474986841480537918878837478902992973202305608637036765245465738479448312n ================================================ FILE: hkmc2/shared/src/test/mlscript/nofib/treejoin.mls ================================================ :js :re // nodejs stack size 8192 can handle this import "../../mlscript-compile/nofib/treejoin.mls" treejoin.main() //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded // //│ = 'Node(26790, Node(25449, Node(24977, Node(24359, Node(24314, Leaf(24314, (9658,1890,24314,24314,7755)), Leaf(24359, (17282,5066,24359,24359,134))), Node(24452, Leaf(24452, (13985,4700,24452,24452,8400)), Node(24776, Leaf(24776, (9392,18824,24776,24776,25492)), Leaf(24977, (21044,6260,24977,24977,3296))))), Node(25347, Node(25087, Leaf(25087, (9226,22666,25087,25087,19595)), Leaf(25347, (9331,1734,25347,25347,12071))), Leaf(25449, (24361,14401,25449,25449,5552)))), Node(26770, Node(26207, Node(25780, Node(25457, Leaf(25457, (5457,5273,25457,25457,5149)), Leaf(25780, (21801,92,25780,25780,2781))), Leaf(26207, (22839,18759,26207,26207,20366))), Node(26436, Node(26274, Node(26222, Leaf(26222, (11315,3470,26222,26222,23055)), Leaf(26274, (19231,20706,26274,26274,24107))), Node(26276, Leaf(26276, (3545,169,26276,26276,7584)), Leaf(26436, (22148,24244,26436,26436,2448)))), Node(26473, Leaf(26473, (13180,15097,26473,26473,16469)), Leaf(26770, (23746,15943,26770,26770,1467))))), Leaf(26790, (25643,16230,26790,26790,970)))), Leaf(26932, (24868,11500,26932,26932,3741)))' ================================================ FILE: hkmc2/shared/src/test/mlscript/objbuf/Basics.mls ================================================ :js import "../../mlscript-compile/ObjectBuffer.mls" open ObjectBuffer open annotations @buffered class A(x) with fun f(y) = x + y @bufferable class A2(x) with fun f(y) = x + y :ge @buffered @buffered class B1(x) //│ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. //│ ║ l.17: @buffered //│ ║ ^^^^^^^^^ //│ ║ l.18: @buffered //│ ╙── ^^^^^^^^^ :ge @buffered @bufferable class B2(x) //│ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. //│ ║ l.27: @buffered //│ ║ ^^^^^^^^^ //│ ║ l.28: @bufferable //│ ╙── ^^^^^^^^^^^ :w @bufferable module B3 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.37: @bufferable //│ ╙── ^^^^^^^^^^^ :w @bufferable object B3 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.44: @bufferable //│ ╙── ^^^^^^^^^^^ let buf = new DefaultObjectBuffer(10) //│ buf = DefaultObjectBuffer(_) let obj1 = buf.mkNew(A)(3) //│ obj1 = 0 let obj2 = buf.mkNew(A)(5) //│ obj2 = 2 A.f(buf, obj1)(1) //│ = 4 A.f(buf, obj2)(1) //│ = 6 buf.del(A, obj1) let obj3 = buf.mkNew(A)(10) //│ obj3 = 0 // UAF A.f(buf, obj1)(1) //│ = 11 A.f(buf, obj3)(1) //│ = 11 @bufferable class Oops with fun hi = print("hi") let obj1 = buf.mkNew(Oops) //│ obj1 = 0 Oops.hi(buf, obj2) //│ > hi let obj2 = buf.mkNew(A)(3) //│ obj2 = 4 A.f(buf, obj2)(1) //│ = 4 buf.del(Oops, obj1) :expect 4 A.f(buf, obj2)(1) //│ = 4 @buffered class A(x) with val z = x fun f(y) = x + y + z ================================================ FILE: hkmc2/shared/src/test/mlscript/objbuf/Mutation.mls ================================================ :js import "../../mlscript-compile/ObjectBuffer.mls" open ObjectBuffer open annotations :fixme // TODO: fix IR rebinding issue (each symbol should be bound at most once) :checkIR @bufferable class A() with let z = 0 set z += 1 //│ ═══[INTERNAL ERROR] [BlockChecker] Invalid IR: symbol tmp:tmp⁰ is bound more than once :sjs @bufferable class A(x) with mut val z = x fun f(y) = set x += 1 set z += 2 x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let A3; //│ A3 = function A(x) { //│ return globalThis.Object.freeze(new A.class(x)); //│ }; //│ (class A2 { //│ static { //│ A3.class = this //│ } //│ constructor(x) { //│ this.#x = x; //│ this.z = this.#x; //│ } //│ static { //│ this.size = 2; //│ } //│ static ctor(buf, idx) { //│ return (x) => { //│ let idx1, idx2, idx3; //│ idx3 = idx + 0; //│ buf.buf[idx3] = x; //│ idx1 = idx + 0; //│ idx2 = idx + 1; //│ buf.buf[idx2] = buf.buf.at(idx1); //│ return idx //│ } //│ } //│ static f(buf, idx) { //│ return (y) => { //│ let tmp, tmp1, idx1, idx2, idx3, idx4, idx5; //│ idx1 = idx + 0; //│ tmp = buf.buf.at(idx1) + 1; //│ idx5 = idx + 0; //│ buf.buf[idx5] = tmp; //│ idx2 = idx + 1; //│ tmp1 = buf.buf.at(idx2) + 2; //│ idx4 = idx + 1; //│ buf.buf[idx4] = tmp1; //│ idx3 = idx + 0; //│ return buf.buf.at(idx3) //│ } //│ } //│ #x; //│ #z; //│ get z() { return this.#z; } //│ set z(value) { this.#z = value; } //│ f(y) { //│ let tmp, tmp1; //│ tmp = this.#x + 1; //│ this.#x = tmp; //│ tmp1 = this.z + 2; //│ this.z = tmp1; //│ return this.#x //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A", [null]]; //│ }); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— let buf = new DefaultObjectBuffer(10) //│ buf = DefaultObjectBuffer(_) let obj1 = buf.mkNew(A.class)(3) //│ obj1 = 0 let obj2 = buf.mkNew(A.class)(5) //│ obj2 = 2 A.class.f(buf, obj1)(1) //│ = 4 A.class.f(buf, obj1)(1) //│ = 5 A.class.f(buf, obj2)(1) //│ = 6 buf.del(A.class, obj1) let obj3 = buf.mkNew(A.class)(10) //│ obj3 = 0 // UAF A.class.f(buf, obj1)(1) //│ = 11 A.class.f(buf, obj3)(1) //│ = 12 @bufferable class A(x) with mut val z = x fun f(y) = set z += 1 z ================================================ FILE: hkmc2/shared/src/test/mlscript/objbuf/ObjectBufferAllocator.mls ================================================ :js import "../../mlscript-compile/ObjectBuffer.mls" open ObjectBuffer let buf = new DefaultObjectBuffer(10) //│ buf = DefaultObjectBuffer(_) buf.alloc(10) //│ = 0 buf.alloc(1000) //│ = 16 buf.alloc(1) //│ = 1016 buf.free(16, 1000) buf.alloc(1) //│ = 16 buf.alloc(1) //│ = 18 ================================================ FILE: hkmc2/shared/src/test/mlscript/opt/AbortivePrefix.mls ================================================ :js // * Note: we properly removed the call to `???` :sjs fun f = if true then return 1 else return 2 (???) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; f = function f() { let scrut; scrut = true; if (scrut === true) { return 1 } return 2; }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Note: we now remove the call to `???` because // * the previous block is abortive, which cannot be seen syntactically on the fly // * due to the use of an intervening label, but can be simplified by BlockSimplifier. :sir :soir fun f = if true and false then return() (???) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰; //│ define f⁰ as fun f¹() { //│ let scrut, scrut1; //│ block split_root$: //│ block split_default$: //│ set scrut = true; //│ match scrut //│ true => //│ set scrut1 = false; //│ match scrut1 //│ true => //│ return runtime⁰.Unit⁰ //│ else //│ break split_default$ //│ end //│ else //│ break split_default$ //│ end //│ throw new globalThis⁰.Error⁰("match error") //│ return Predef⁰.notImplementedError⁰ //│ }; //│ end //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰; //│ define f⁰ as fun f¹() { //│ let scrut, scrut1; //│ set scrut = true; //│ match scrut //│ true => //│ set scrut1 = false; //│ match scrut1 //│ true => //│ return runtime⁰.Unit⁰ //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sir :soir fun f = if true and false then return "1" else return "2 long long long long long long long long long long long long long long" (???) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f²; //│ define f² as fun f³() { //│ let scrut, scrut1; //│ block split_root$: //│ block split_1$: //│ set scrut = true; //│ match scrut //│ true => //│ set scrut1 = false; //│ match scrut1 //│ true => //│ return "1" //│ else //│ break split_1$ //│ end //│ else //│ break split_1$ //│ end //│ return "2 long long long long long long long long long long long long long long" //│ return Predef⁰.notImplementedError⁰ //│ }; //│ end //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f²; //│ define f² as fun f³() { //│ let scrut, scrut1; //│ set scrut = true; //│ match scrut //│ true => //│ set scrut1 = false; //│ match scrut1 //│ true => //│ return "1" //│ else //│ return "2 long long long long long long long long long long long long long long" //│ end //│ else //│ return "2 long long long long long long long long long long long long long long" //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls ================================================ :js // * Remove useless objects :soir :sjs fun f() = object A 42 f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f, inlinedVal; f = function f() { return 42 }; inlinedVal = 42; inlinedVal //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f⁰, inlinedVal; define f⁰ as fun f¹() { return 42 }; set inlinedVal = 42; inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 42 // * Do not remove objects with non-trivial constructors :soir :sjs fun f() = object A with print("Hello") 42 f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f1; //│ f1 = function f() { //│ let A1; //│ (class A { //│ static { //│ new this //│ } //│ constructor() { //│ A1 = this; //│ Predef.print("Hello"); //│ Object.defineProperty(this, "class", { //│ value: A //│ }); //│ globalThis.Object.freeze(this); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["object", "A"]; //│ }); //│ return 42 //│ }; //│ f1() //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f²; //│ define f² as fun f³() { //│ let A; //│ define A as object A⁰ { constructor { do Predef⁰.print⁰("Hello"); end } }; //│ return 42 //│ }; //│ f³() //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hello //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/opt/DeadSelRemoval.mls ================================================ :js // * It's important that we preserve the call to `???` :re :soir while true do ??? //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ loop lbl: //│ let scrut; //│ set scrut = true; //│ match scrut //│ true => //│ do Predef⁰.notImplementedError⁰; //│ continue lbl //│ else //│ end //│ end //│ runtime⁰.Unit⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Not implemented :sir :soir do val x = 2 + 2 val y = print(2) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x⁰, y⁰, tmp, tmp1; //│ set tmp = +⁰(2, 2); //│ define x⁰ as val x¹ = tmp; //│ set tmp1 = Predef⁰.print⁰(2); //│ define y⁰ as val y¹ = tmp1; //│ runtime⁰.Unit⁰ //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Predef⁰.print⁰(2); runtime⁰.Unit⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 :sir :soir scope.locally of ( val x = 1 val y = print(2) 123 ) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let x², y², tmp; //│ define x² as val x³ = 1; //│ set tmp = Predef⁰.print⁰(2); //│ define y² as val y³ = tmp; //│ 123 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Predef⁰.print⁰(2); 123 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 //│ = 123 // :soir module Foo with mut val x = 1 fun y = set x += 1 print(x) Foo.x //│ = 1 // * The first selection is pure and can be removed :soir Foo.x, Foo.x //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ Foo⁰.x⁴ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // * TODO: also optimize :soir Foo.x; Foo.x //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ ,⁰(Foo⁰.x⁴, Foo⁰.x⁴) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // * Cannot remove the first selection! :soir :expect 2 Foo.y, Foo.x //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Foo⁰.y⁴; Foo⁰.x⁴ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 //│ = 2 :soir :expect 3 Foo.y; Foo.x //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ ,⁰(Foo⁰.y⁴, Foo⁰.x⁴) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 3 //│ = 3 :soir let a = Foo.x in Foo.x //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ Foo⁰.x⁴ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 :expect 4 :soir let a = Foo.y in Foo.x //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Foo⁰.y⁴; Foo⁰.x⁴ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 4 //│ = 4 :sir :soir ( val a = print("hi") 42 ) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let a⁰, tmp; set tmp = Predef⁰.print⁰("hi"); define a⁰ as val a¹ = tmp; 42 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Predef⁰.print⁰("hi"); 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = 42 :soir :ssjs ( val a = Foo.y 42 ) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let a²; define a² as val a³ = Foo⁰.y⁴; 42 //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let a; //│ a = Foo1.y; block$res13 = 42; undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 5 //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/opt/DeadStatRemoval.mls ================================================ :js :soir if 1 === 2 do 2 + 2 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ runtime⁰.Unit⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :soir if 1 == 2 do 2 + 2 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Predef⁰.equals⁰(1, 2); runtime⁰.Unit⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :soir do val x = 2 + 2 val y = print(2) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ do Predef⁰.print⁰(2); runtime⁰.Unit⁰ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/opt/DeadVarRemoval.mls ================================================ :js :soir let x = 2 + 2 in 1 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :soir fun main = let x = 2 + 2 1 main //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let main⁰, inlinedVal; define main⁰ as fun main¹() { return 1 }; set inlinedVal = 1; inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Apply.mls ================================================ :parseOnly f() //│ Parsed: //│ App(Ident(f),Tup(List())) f(1) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1)))) f(1, 2, 3) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) f()() //│ Parsed: //│ App(App(Ident(f),Tup(List())),Tup(List())) f(1)(2) //│ Parsed: //│ App(App(Ident(f),Tup(List(IntLit(1)))),Tup(List(IntLit(2)))) f( 1 ) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1)))) f( 1, ) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1)))) f( 1, 2, 3 ) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) f( 1, 2, 3, ) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) f( 1 2 3 ) //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Atoms.mls ================================================ :parseOnly foo //│ Parsed: //│ Ident(foo) foo bar //│ Parsed: //│ Ident(foo) //│ Ident(bar) 123 //│ Parsed: //│ IntLit(123) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Block.mls ================================================ :parseOnly a //│ Parsed: //│ Ident(a) a, //│ Parsed: //│ Ident(a) a, b //│ Parsed: //│ Ident(a) //│ Ident(b) a, b, c //│ Parsed: //│ Ident(a) //│ Ident(b) //│ Ident(c) class A, res //│ Parsed: //│ TypeDef(Cls,Ident(A),None) //│ Ident(res) foo of a, b //│ Parsed: //│ App(Ident(foo),Tup(List(Ident(a), Ident(b)))) foo of a b //│ Parsed: //│ App(Ident(foo),Tup(List(Ident(a), Ident(b)))) x => x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Ident(x)) x => x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Block(List(Ident(x)))) x => foo x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Block(List(Ident(foo), Ident(x)))) let a = 1, 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),None) //│ IntLit(2) let a = (1, 2) //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(Bra(Round,Block(List(IntLit(1), IntLit(2))))),None) let a = { 1, 2 } //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(Block(List(IntLit(1), IntLit(2)))),None) let a = 1 in 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(IntLit(2))) let a = 1 in 2, 3 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(IntLit(2))) //│ IntLit(3) let a = 1 in (2, 3) //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(Bra(Round,Block(List(IntLit(2), IntLit(3)))))) let a = 1 in { 2, 3 } //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(Block(List(IntLit(2), IntLit(3))))) foo a //│ Parsed: //│ Jux(Ident(foo),Ident(a)) foo a //│ Parsed: //│ Jux(Ident(foo),Block(List(Ident(a)))) foo a b //│ Parsed: //│ Jux(Ident(foo),Block(List(Ident(a), Ident(b)))) foo a, b //│ Parsed: //│ Jux(Ident(foo),Block(List(Ident(a), Ident(b)))) // --- ERROR CASES --- :global :pe ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/BoolOps.mls ================================================ :parseOnly a and b //│ Parsed: //│ InfixApp(Ident(a),Keywrd(keyword 'and'),Ident(b)) a and b and c //│ Parsed: //│ InfixApp(InfixApp(Ident(a),Keywrd(keyword 'and'),Ident(b)),Keywrd(keyword 'and'),Ident(c)) a and b or x and d //│ Parsed: //│ InfixApp(InfixApp(Ident(a),Keywrd(keyword 'and'),Ident(b)),Keywrd(keyword 'or'),InfixApp(Ident(x),Keywrd(keyword 'and'),Ident(d))) a or b and x or d //│ Parsed: //│ InfixApp(InfixApp(Ident(a),Keywrd(keyword 'or'),InfixApp(Ident(b),Keywrd(keyword 'and'),Ident(x))),Keywrd(keyword 'or'),Ident(d)) (a and b) or (x and d) //│ Parsed: //│ InfixApp(Bra(Round,InfixApp(Ident(a),Keywrd(keyword 'and'),Ident(b))),Keywrd(keyword 'or'),Bra(Round,InfixApp(Ident(x),Keywrd(keyword 'and'),Ident(d)))) a and (b or x) and d //│ Parsed: //│ InfixApp(InfixApp(Ident(a),Keywrd(keyword 'and'),Bra(Round,InfixApp(Ident(b),Keywrd(keyword 'or'),Ident(x)))),Keywrd(keyword 'and'),Ident(d)) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Class.mls ================================================ :parseOnly :pe class //│ ╔══[PARSE ERROR] Expected expression after type declaration keyword; found end of input instead //│ ║ l.5: class //│ ╙── ^ //│ Parsed: //│ Error() class Foo //│ Parsed: //│ TypeDef(Cls,Ident(Foo),None) :pe class Foo extends //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.17: class Foo extends //│ ╙── ^ //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Error()),None) class Foo extends Bar //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),None) :pe class Foo extends Bar with //│ ╔══[PARSE ERROR] Expected start of expression in this position; found end of input instead //│ ║ l.29: class Foo extends Bar with //│ ╙── ^ //│ Parsed: //│ TypeDef(Cls,InfixApp(InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),Keywrd(keyword 'with'),Error()),None) class Foo extends Bar with val x //│ Parsed: //│ TypeDef(Cls,InfixApp(InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),Keywrd(keyword 'with'),TermDef(ImmutVal,Ident(x),None)),None) class Foo extends Bar with val x: Int //│ Parsed: //│ TypeDef(Cls,InfixApp(InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),Keywrd(keyword 'with'),Block(List(TermDef(ImmutVal,InfixApp(Ident(x),Keywrd(keyword ':'),Ident(Int)),None)))),None) class Foo with val x: Int //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'with'),Block(List(TermDef(ImmutVal,InfixApp(Ident(x),Keywrd(keyword ':'),Ident(Int)),None)))),None) :pe with //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'with' keyword instead //│ ║ l.52: with //│ ╙── ^^^^ //│ Parsed: //│ Error() class Foo //│ Parsed: //│ TypeDef(Cls,Ident(Foo),None) class Foo Bar //│ Parsed: //│ TypeDef(Cls,Ident(Foo),None) //│ TypeDef(Cls,Ident(Bar),None) class Foo extends Bar Bar with x //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),None) //│ TypeDef(Cls,InfixApp(Ident(Bar),Keywrd(keyword 'with'),Block(List(Ident(x)))),None) :pe class Foo //│ ╔══[PARSE ERROR] Expected expression after type declaration keyword; found new line instead //│ ║ l.82: class //│ ║ ^ //│ ║ l.83: Foo //│ ╙── //│ Parsed: //│ Error() //│ Ident(Foo) :fixme class Foo extends Bar //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'extends' keyword instead //│ ║ l.96: extends Bar //│ ╙── ^^^^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead //│ ║ l.96: extends Bar //│ ╙── ^^^ //│ Parsed: //│ TypeDef(Cls,Ident(Foo),None) //│ Error() class Foo extends Bar //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),None) class Foo extends Bar Bar extends Baz //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),None) //│ TypeDef(Cls,InfixApp(Ident(Bar),Keywrd(keyword 'extends'),Ident(Baz)),None) class Foo extends Bar //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Ident(Bar)),None) class Foo extends Bar //│ Parsed: //│ TypeDef(Cls,InfixApp(Ident(Foo),Keywrd(keyword 'extends'),Block(List(Ident(Bar)))),None) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/ConstrainedTypes.mls ================================================ :parseOnly [] => Int //│ Parsed: //│ InfixApp(TyTup(List()),Keywrd(keyword '=>'),Ident(Int)) [Bot <:< Top] => Int //│ Parsed: //│ InfixApp(TyTup(List(OpApp(Ident(Bot),Ident(<:<),List(Ident(Top))))),Keywrd(keyword '=>'),Ident(Int)) [(Int & Str) <:< (Int | Str)] => Int //│ Parsed: //│ InfixApp(TyTup(List(OpApp(Bra(Round,OpApp(Ident(Int),Ident(&),List(Ident(Str)))),Ident(<:<),List(Bra(Round,OpApp(Ident(Int),Ident(|),List(Ident(Str)))))))),Keywrd(keyword '=>'),Ident(Int)) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Extends.mls ================================================ :ctx :noCodeGen // Plain extension class Base class Derived extends Base //│ Env: //│ Base -> RefElem(member:Base) //│ Derived -> RefElem(member:Derived) // Extension with parameters abstract class Box data class IntBox(val value: Int) extends Box data class StrBox(val value: Str) extends Box //│ Env: //│ Box -> RefElem(member:Box) //│ IntBox -> RefElem(member:IntBox) //│ StrBox -> RefElem(member:StrBox) // Extension with symbolic name abstract class Ring module End extends Ring data class (++) Link(left: Ring, right: Ring) extends Ring //│ Env: //│ ++ -> RefElem(member:Link) //│ End -> RefElem(member:End) //│ Link -> RefElem(member:Link) //│ Ring -> RefElem(member:Ring) // Extension with type parameters abstract class Option[T]: (Some[T] | None) data class Some[T](val value: T) extends Option[T] module None extends Option[Nothing] //│ Env: //│ None -> RefElem(member:None) //│ Option -> RefElem(member:Option) //│ Some -> RefElem(member:Some) // Extension with type parameters and symbolic name abstract class List[T]: (Cons[T] | Nil) data class (::) Cons[T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[Nothing] //│ Env: //│ :: -> RefElem(member:Cons) //│ Cons -> RefElem(member:Cons) //│ List -> RefElem(member:List) //│ Nil -> RefElem(member:Nil) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Handler.mls ================================================ :noCodeGen :pe handle //│ ╔══[PARSE ERROR] Expected expression after 'handle' binding keyword; found end of input instead //│ ║ l.4: handle //│ ╙── ^ :pe handle h //│ ╔══[PARSE ERROR] Expected '=' keyword after 'handle' binding head; found end of input instead //│ ║ l.10: handle h //│ ╙── ^ :pe handle h = Eff //│ ╔══[PARSE ERROR] Expected 'with' keyword after 'handle' binding class name; found end of input instead //│ ║ l.16: handle h = Eff //│ ╙── ^ :parseOnly fun foo(x) = 1 handle h = Eff with fun f()(r) = r(0) fun g(a)(r) = r(1) //│ Parsed: //│ TermDef(Fun,App(Ident(foo),Tup(List(Ident(x)))),Some(IntLit(1))) //│ Hndl(Ident(h),Ident(Eff),Block(List(TermDef(Fun,App(App(Ident(f),Tup(List())),Tup(List(Ident(r)))),Some(App(Ident(r),Tup(List(IntLit(0)))))), TermDef(Fun,App(App(Ident(g),Tup(List(Ident(a)))),Tup(List(Ident(r)))),Some(App(Ident(r),Tup(List(IntLit(1)))))))),None) :e handle 1 = 1 with fun f()(r) = r(0) in foo(h) //│ ╔══[COMPILATION ERROR] Unsupported handle binding shape //│ ║ l.32: handle 1 = 1 with //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.33: fun f()(r) = r(0) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.34: in //│ ║ ^^ //│ ║ l.35: foo(h) //│ ╙── ^^^^^^^^ abstract class Eff fun foo(h) = 0 :el handle h = Eff with fun f()(r) = r(0) in foo(h) //│ Elab: { handle h = Ref(member:Eff)() List(HandlerTermDefinition(r,TermDefinition(Fun,member:f,term:Handler$h$/f,List(ParamList(‹›,List(),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹method ›,Modulefulness(None),List(),None))) in App(Ref(member:foo),Tup(List(Fld(‹›,Ref(h),None)))) } :e ( handle h = Eff with fun f()(r) = r(0) fun g(a)()(r) = r(1) ) + 1 //│ ╔══[COMPILATION ERROR] Unsupported handle binding shape //│ ║ l.58: handle h = Eff with //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.59: fun f()(r) = r(0) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.60: fun g(a)()(r) = r(1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ module Mod with abstract class Eff(x) :el handle h = Mod.Eff(3) with fun f()(r) = r(0) fun g(a)()()(r) = r(1) foo(h) //│ Elab: { handle h = Sel(Ref(member:Mod),Ident(Eff))(Lit(IntLit(3))) List(HandlerTermDefinition(r,TermDefinition(Fun,member:f,term:Handler$h$/f,List(ParamList(‹›,List(),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹method ›,Modulefulness(None),List(),None)), HandlerTermDefinition(r,TermDefinition(Fun,member:g,term:Handler$h$/g,List(ParamList(‹›,List(Param(‹›,a,None,Modulefulness(None))),None), ParamList(‹›,List(),None), ParamList(‹›,List(),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹method ›,Modulefulness(None),List(),None))) in App(Ref(member:foo),Tup(List(Fld(‹›,Ref(h),None)))) } :e handle h = Eff with fun f()(r) = r(0) fun g(a)(r) = r(1) val x = 24 class Test with fun x() = x foo(h) //│ ╔══[COMPILATION ERROR] Only function definitions are allowed in handler blocks //│ ║ l.84: val x = 24 //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Only function definitions are allowed in handler blocks //│ ║ l.86: fun x() = x //│ ╙── ^ :e handle h = Eff with fun f()(r) = r(0) fun g(a)(r) = handle e = Eff with fun f = r(0) fun g() = r(0) fun h()() = r(1) fun h2()(a, b) = r(1) foo(h) foo(h) //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter //│ ║ l.100: fun f = r(0) //│ ╙── ^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter //│ ║ l.101: fun g() = r(0) //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter //│ ║ l.102: fun h()() = r(1) //│ ╙── ^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter //│ ║ l.103: fun h2()(a, b) = r(1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: h //│ ║ l.104: foo(h) //│ ╙── ^ :w :el handle h = Eff with fun f()(r) = r(0) fun g(a)(r) = r(1) 12345 foo(h) //│ ╔══[WARNING] Terms in handler block do nothing //│ ║ l.127: 12345 //│ ╙── ^^^^^ //│ Elab: { handle h = Ref(member:Eff)() List(HandlerTermDefinition(r,TermDefinition(Fun,member:f,term:Handler$h$/f,List(ParamList(‹›,List(),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹method ›,Modulefulness(None),List(),None)), HandlerTermDefinition(r,TermDefinition(Fun,member:g,term:Handler$h$/g,List(ParamList(‹›,List(Param(‹›,a,None,Modulefulness(None))),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹method ›,Modulefulness(None),List(),None))) in App(Ref(member:foo),Tup(List(Fld(‹›,Ref(h),None)))) } ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Indent.mls ================================================ :parseOnly x //│ Parsed: //│ Ident(x) x y //│ Parsed: //│ Jux(Ident(x),Block(List(Ident(y)))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Let.mls ================================================ :parseOnly :pe let //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead //│ ║ l.5: let //│ ╙── ^ //│ Parsed: //│ Error() let x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),None,None) :pe let x = //│ ╔══[PARSE ERROR] Expected expression or block after 'let' binding equals sign; found end of input instead //│ ║ l.17: let x = //│ ╙── ^ //│ Parsed: //│ Error() let x = y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(Ident(y)),None) let x = y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(Block(List(Ident(y)))),None) :pe let x = y in //│ ╔══[PARSE ERROR] Expected expression or block after 'let' binding `in` clause; found end of input instead //│ ║ l.34: let x = y in //│ ╙── ^ //│ Parsed: //│ Error() let x = y in z //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(Ident(y)),Some(Ident(z))) :pe let x = y class //│ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead //│ ║ l.46: let x = y class //│ ╙── ^^^^^ //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(Ident(y)),None) let x = 1 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) // * Note: this used to parse as an operator application... let x = 1 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) // * Note: this used to parse as an operator application... :pe let x = 1 //│ ╔══[PARSE ERROR] Expected expression or block after 'let' binding equals sign; found new line instead //│ ║ l.67: = //│ ║ ^ //│ ║ l.68: 1 //│ ╙── //│ Parsed: //│ Error() //│ IntLit(1) :pe let {} //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of block instead //│ ║ l.80: let {} //│ ╙── ^^ //│ Parsed: let () //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Unt(),None,None) :pe let // a //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead //│ ║ l.91: let // a //│ ╙── ^ //│ Parsed: //│ Error() :pe let // a //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead //│ ║ l.100: // a //│ ╙── ^ //│ Parsed: //│ Error() :pe let // a 1 //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found new line instead //│ ║ l.109: // a //│ ║ ^ //│ ║ l.110: 1 //│ ╙── //│ Parsed: //│ Error() //│ IntLit(1) let x = y foo //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(Ident(y)),None) //│ Ident(foo) let x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),None,None) let x y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),None,None) //│ LetLike(Keywrd(keyword 'let'),Ident(y),None,None) let x, y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),None,None) //│ LetLike(Keywrd(keyword 'let'),Ident(y),None,None) let x = 1 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) let x = 1 in x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x))) let x = 1 in x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x))) let x = 1 in x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x))) let x = 1 in x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x))) // Awkward... parses `in x` as a separate statement let x = 1 in x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) //│ Modified(Keywrd(keyword 'in'),Ident(x)) :pe let x = 1 in x //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found new line instead //│ ║ l.182: let //│ ║ ^ //│ ║ l.183: x = 1 //│ ╙── //│ Parsed: //│ Error() //│ Def(Ident(x),IntLit(1)) //│ Modified(Keywrd(keyword 'in'),Ident(x)) let a in b //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),None,Some(Ident(b))) let a = 1 in b //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(Ident(b))) :pe let with //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found 'with' keyword instead //│ ║ l.207: with //│ ╙── ^^^^ //│ Parsed: //│ Error() let x = 1 in x //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Block(List(Ident(x))))) let x = 1 in x y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Block(List(Ident(x), Ident(y))))) x => let x = 1 in x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x)))) x => let x = 1 in x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Block(List(Ident(x)))))) x => let x = 1 in x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x)))) x => let x = 1 in x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x)))))) x => let x = 1 x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None), Ident(x)))) x => let x = 1 x //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(Jux(IntLit(1),Block(List(Ident(x))))),None)) :pe if //│ ╔══[PARSE ERROR] Expected expression or block after 'if' keyword; found end of input instead //│ ║ l.259: if //│ ╙── ^ //│ Parsed: //│ Error() ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/LetLet.mls ================================================ :parseOnly :pe let let //│ ╔══[PARSE ERROR] Expected expression after 'let' binding keyword; found end of input instead //│ ║ l.5: let let //│ ╙── ^ //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Error(),None,None) :pe let x = 1 let y = 2 //│ ╔══[PARSE ERROR] Expected end of input; found 'let' keyword instead //│ ║ l.13: let x = 1 let y = 2 //│ ╙── ^^^ //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) let x = 1 in let y = 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),None))) let x = 1 in let y = 2 in let z = 3 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),Some(LetLike(Keywrd(keyword 'let'),Ident(z),Some(IntLit(3)),None))))) let x = 1 let y = 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) //│ LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),None) let x y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),None,None) //│ LetLike(Keywrd(keyword 'let'),Ident(y),None,None) let x = 1 y = 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None) //│ LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),None) let x = 1 in x y = 2 in y //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x))) //│ LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),Some(Ident(y))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Modifiers.mls ================================================ :parseOnly mut let x = 1 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None)) mut mut let x = 1 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Modified(Keywrd(keyword 'mut'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/MultiLineCall.mls ================================================ :js // Multi-line function call with comma continuation should parse correctly tuple(1, 2) //│ = [1, 2] tuple of 1, 2 //│ = [1, 2] tuple of 1, 2, 3 //│ = [1, 2, 3] tuple of 1, 2, 3 //│ = [1, 2, 3] tuple of 1, 2 3 //│ = [1, 2, 3] :e tuple of 1 2 3 //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (integer literal). //│ ║ l.35: 2 //│ ╙── ^ //│ = [1, 3] ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Mut.mls ================================================ :parseOnly :pe mut //│ ╔══[PARSE ERROR] Expected expression or block after 'mut' keyword; found end of input instead //│ ║ l.5: mut //│ ╙── ^ //│ Parsed: //│ Error() mut x //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Ident(x)) mut x y //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(Ident(x), Ident(y)))) mut let x = 1 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None)) mut let x = 1 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None)))) mut let x = 1 in x //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x)))))) mut let x = 1 let y = 2 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None), LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),None)))) mut let x = 1 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None)))) mut let x = 1 let y = 1 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None), LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(1)),None)))) mut let x = 1 y = 2 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None), LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),None)))) mut let x = 1 in x //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x)))))) mut let x = 1 in x let y = 2 in y //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),Some(Ident(x))), LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),Some(Ident(y)))))) // Note that this parses as a `mut` modifier on a block of two let bindings, which is a bit weird mut let x = 1 y = 2 //│ Parsed: //│ Modified(Keywrd(keyword 'mut'),Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(1)),None), LetLike(Keywrd(keyword 'let'),Ident(y),Some(IntLit(2)),None)))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Of.mls ================================================ :parseOnly f of () //│ Parsed: //│ App(Ident(f),Tup(List(Unt()))) f of 1 //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1)))) f of (1) //│ Parsed: //│ App(Ident(f),Tup(List(Bra(Round,IntLit(1))))) f of 1, 2 //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2)))) f of 1, 2, 3 //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) f of (1, 2, 3) //│ Parsed: //│ App(Ident(f),Tup(List(Bra(Round,Block(List(IntLit(1), IntLit(2), IntLit(3))))))) f of g(1) //│ Parsed: //│ App(Ident(f),Tup(List(App(Ident(g),Tup(List(IntLit(1))))))) f of g of 1 //│ Parsed: //│ App(Ident(f),Tup(List(App(Ident(g),Tup(List(IntLit(1))))))) (f of g) of 1 //│ Parsed: //│ App(Bra(Round,App(Ident(f),Tup(List(Ident(g))))),Tup(List(IntLit(1)))) (f of g)(1) //│ Parsed: //│ App(Bra(Round,App(Ident(f),Tup(List(Ident(g))))),Tup(List(IntLit(1)))) f of (g)(1) //│ Parsed: //│ App(Ident(f),Tup(List(App(Bra(Round,Ident(g)),Tup(List(IntLit(1))))))) f of 1, 2, 3 //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) f of 1 2 3 //│ Parsed: //│ App(Ident(f),Tup(List(IntLit(1), IntLit(2), IntLit(3)))) :pe log of 1 //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'of' keyword instead //│ ║ l.66: of 1 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.66: of 1 //│ ╙── ^ //│ Parsed: //│ Ident(log) //│ Error() :w print of 123 //│ ╔══[WARNING] This literal should be indented //│ ║ l.79: 123 //│ ║ ^^^ //│ ╟── since it is a continuation of the new line here //│ ║ l.78: print of //│ ║ ^ //│ ║ l.79: 123 //│ ╙── //│ Parsed: //│ App(Ident(print),Tup(List(IntLit(123)))) :w print of 1 2 //│ ╔══[WARNING] This literal should be indented //│ ║ l.93: 1 //│ ║ ^ //│ ╟── since it is a continuation of the new line here //│ ║ l.92: print of //│ ║ ^ //│ ║ l.93: 1 //│ ╙── //│ Parsed: //│ App(Ident(print),Tup(List(IntLit(1)))) //│ IntLit(2) print of... 1 2 //│ Parsed: //│ App(Ident(print),Tup(List(IntLit(1), IntLit(2)))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Operators.mls ================================================ :parseOnly + //│ Parsed: //│ Ident(+) +1 //│ Parsed: //│ App(Ident(+),Tup(List(IntLit(1)))) 1 + 2 //│ Parsed: //│ OpApp(IntLit(1),Ident(+),List(IntLit(2))) 1 + +2 //│ Parsed: //│ OpApp(IntLit(1),Ident(+),List(App(Ident(+),Tup(List(IntLit(2)))))) +1 + +2 //│ Parsed: //│ OpApp(App(Ident(+),Tup(List(IntLit(1)))),Ident(+),List(App(Ident(+),Tup(List(IntLit(2)))))) + + 1 + + + 2 //│ Parsed: //│ OpApp(App(Ident(+),Tup(List(App(Ident(+),Tup(List(IntLit(1))))))),Ident(+),List(App(Ident(+),Tup(List(App(Ident(+),Tup(List(IntLit(2))))))))) 1 + 2 * 3 //│ Parsed: //│ OpApp(IntLit(1),Ident(+),List(OpApp(IntLit(2),Ident(*),List(IntLit(3))))) 1 + 2 * 3 - 4 //│ Parsed: //│ OpApp(OpApp(IntLit(1),Ident(+),List(OpApp(IntLit(2),Ident(*),List(IntLit(3))))),Ident(-),List(IntLit(4))) 1 => 2 //│ Parsed: //│ InfixApp(Tup(List(IntLit(1))),Keywrd(keyword '=>'),IntLit(2)) 1 => 2 //│ Parsed: //│ InfixApp(Tup(List(IntLit(1))),Keywrd(keyword '=>'),Block(List(IntLit(2)))) x => 1 2 //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Block(List(IntLit(1), IntLit(2)))) x => x : t //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),InfixApp(Ident(x),Keywrd(keyword ':'),Ident(t))) x => x : t //│ Parsed: //│ InfixApp(Tup(List(Ident(x))),Keywrd(keyword '=>'),Block(List(InfixApp(Ident(x),Keywrd(keyword ':'),Ident(t))))) 1 + 2 //│ Parsed: //│ OpApp(IntLit(1),Ident(+),List(Block(List(IntLit(2))))) :pt 1 + 2 + 3 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp: //│ lhs = OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 2 //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 3 1 + let x = 2 class A x 42 //│ Parsed: //│ OpApp(IntLit(1),Ident(+),List(Block(List(LetLike(Keywrd(keyword 'let'),Ident(x),Some(IntLit(2)),None), TypeDef(Cls,Ident(A),None), Ident(x), IntLit(42))))) :pe 1 + 2, 3 //│ ╔══[PARSE ERROR] Unexpected comma in this position //│ ║ l.92: 2, //│ ╙── ^ //│ Parsed: //│ OpApp(IntLit(1),Ident(+),List(Block(List(IntLit(2), IntLit(3))))) a === "a" || b === "b" //│ Parsed: //│ OpApp(OpApp(Ident(a),Ident(===),List(StrLit(a))),Ident(||),List(OpApp(Ident(b),Ident(===),List(StrLit(b))))) A is B where C is D //│ Parsed: //│ InfixApp(InfixApp(Ident(A),Keywrd(keyword 'is'),Ident(B)),Keywrd(keyword 'where'),InfixApp(Ident(C),Keywrd(keyword 'is'),Ident(D))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls ================================================ :js +2 //│ = 2 2 + +3 //│ = 5 2 + -3 //│ = -1 // * `+` is a prefix op; needs a space if we want to use it as a binary op in new-line position :w :sjs 1 +2 +3 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.16: 1 //│ ╙── ^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ + 3 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.16: 1 //│ ╙── ^ //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 :sjs 1 + 2 + 3 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp2; tmp2 = 1 + 2; tmp2 + 3 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 1 * 2 * 3 //│ = 6 1 *2 *3 //│ = 6 :sjs + //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; lambda = (undefined, function (arg1, arg2) { return arg1 + arg2 }); lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun * //│ = fun :w 1 * //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.61: 1 //│ ╙── ^ //│ = fun fun (??) foo(x, y) = x + y ?? //│ = fun foo (??) //│ = fun foo ??(1, 2) //│ = 3 ?? of 1, 2 //│ = 3 1 ??(2) //│ = 3 1 ?? 2 //│ = 3 // `??` is not in the list of prefix operators... :pe ?? 1 //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.91: ?? 1 //│ ╙── ^ //│ = fun foo 1 ?? 2 //│ = 3 1 ?? 2 //│ = 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Semis.mls ================================================ :parseOnly a; b //│ Parsed: //│ OpApp(Ident(a),Ident(;),List(Ident(b))) a; b; c //│ Parsed: //│ OpApp(OpApp(Ident(a),Ident(;),List(Ident(b))),Ident(;),List(Ident(c))) let a = 1; 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(OpApp(IntLit(1),Ident(;),List(IntLit(2)))),None) let a = (1; 2) //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(Bra(Round,OpApp(IntLit(1),Ident(;),List(IntLit(2))))),None) let a = { 1; 2 } //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(Block(List(OpApp(IntLit(1),Ident(;),List(IntLit(2)))))),None) let a = 1 in 2 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(IntLit(2))) let a = 1 in 2; 3 //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(OpApp(IntLit(2),Ident(;),List(IntLit(3))))) let a = 1 in (2; 3) //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(Bra(Round,OpApp(IntLit(2),Ident(;),List(IntLit(3)))))) let a = 1 in { 2; 3 } //│ Parsed: //│ LetLike(Keywrd(keyword 'let'),Ident(a),Some(IntLit(1)),Some(Block(List(OpApp(IntLit(2),Ident(;),List(IntLit(3))))))) 1; id(2) //│ Parsed: //│ OpApp(IntLit(1),Ident(;),List(App(Ident(id),Tup(List(IntLit(2)))))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/SetCurlyBraces.mls ================================================ :js let { x = 0, y = 0 } //│ x = 0 //│ y = 0 // Works: if false then 0 else set { x = 1, y = 1 } // Also works (set { ... } on same line as else): if false then 0 else set { x = 1, y = 1 } x //│ = 1 y //│ = 1 // Reset and test single set set { x = 42 } x //│ = 42 :pe set { } //│ ╔══[PARSE ERROR] Expected expression after 'set' binding keyword; found end of block instead //│ ║ l.30: set { } //│ ╙── ^^^ :pe :re if false then 0 else set { } //│ ╔══[PARSE ERROR] Expected expression after 'set' binding keyword; found empty block instead //│ ║ l.38: else set { } //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. if false then 0 else set x = 2 y = 2 x //│ = 2 y //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Suspension.mls ================================================ :js print of ... 123 //│ > 123 print of... 123 //│ > 123 print of 123 //│ > 123 // * Interesting quirk: print of ... [1, 2] //│ > [1, 2] print of ... [1, 2] //│ > 1 2 if true do... if true do... print(1) //│ > 1 module Option with ... module Option with ... val a = 1 module Option with ... // val a = 1 // :pt module A with module B with ... val a = 1 val b = 2 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TypeDef: //│ k = Mod //│ head = InfixApp: //│ lhs = Ident of "A" //│ kw = Keywrd of keyword 'with' //│ rhs = Block of Ls of //│ TypeDef: //│ k = Mod //│ head = InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'with' //│ rhs = Block of Ls of //│ TermDef: //│ k = ImmutVal //│ head = Ident of "a" //│ rhs = S of IntLit of 1 //│ TermDef: //│ k = ImmutVal //│ head = Ident of "b" //│ rhs = S of IntLit of 2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun test(x) = if x then print("ok") else... print("ko") let foo = 1 //│ foo = 1 :w x => if false then 0 else... 1 let bar = 1 //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.86: x => //│ ║ ^^^^ //│ ║ l.87: if false then 0 else... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.88: 1 //│ ╙── ^^^ //│ bar = 1 bar //│ = 1 :w :e x => if false then 0 else... 1 let foo = 1 //│ ╔══[COMPILATION ERROR] Illegal position for '...' spread operator. //│ ║ l.105: x => if false then 0 else... //│ ╙── ^^^ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.105: x => if false then 0 else... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.106: 1 //│ ╙── ^^^ //│ foo = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Then.mls ================================================ :parseOnly 1 then 2 //│ Parsed: //│ InfixApp(IntLit(1),Keywrd(keyword 'then'),IntLit(2)) 1 then 2 then 3 //│ Parsed: //│ InfixApp(IntLit(1),Keywrd(keyword 'then'),InfixApp(IntLit(2),Keywrd(keyword 'then'),IntLit(3))) 1 and 2 then 3 //│ Parsed: //│ InfixApp(InfixApp(IntLit(1),Keywrd(keyword 'and'),IntLit(2)),Keywrd(keyword 'then'),IntLit(3)) 1 and 2 and 3 then 4 //│ Parsed: //│ InfixApp(InfixApp(InfixApp(IntLit(1),Keywrd(keyword 'and'),IntLit(2)),Keywrd(keyword 'and'),IntLit(3)),Keywrd(keyword 'then'),IntLit(4)) 1 then 2 and 3 //│ Parsed: //│ InfixApp(IntLit(1),Keywrd(keyword 'then'),InfixApp(IntLit(2),Keywrd(keyword 'and'),IntLit(3))) ================================================ FILE: hkmc2/shared/src/test/mlscript/parser/Val.mls ================================================ :parseOnly // :pe val 1 //│ Parsed: //│ TermDef(ImmutVal,IntLit(1),None) // :pe val x //│ Parsed: //│ TermDef(ImmutVal,Ident(x),None) val x = 1 //│ Parsed: //│ TermDef(ImmutVal,Ident(x),Some(IntLit(1))) // :pe val x //│ Parsed: //│ TermDef(ImmutVal,Ident(x),None) val x = 1 //│ Parsed: //│ TermDef(ImmutVal,Ident(x),Some(IntLit(1))) val x = 1 y = 2 z = 3 //│ Parsed: //│ TermDef(ImmutVal,Ident(x),Some(IntLit(1))) //│ TermDef(ImmutVal,Ident(y),Some(IntLit(2))) //│ TermDef(ImmutVal,Ident(z),Some(IntLit(3))) ================================================ FILE: hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls ================================================ :js import "../../mlscript-compile/FingerTreeList.mls" import "../../mlscript-compile/LazyFingerTree.mls" open FingerTreeList let xs = FingerTreeList.mk(1,2,3) //│ xs = [1, 2, 3] let ys = concat(xs, xs) concat(xs) //│ ys = [1, 2, 3, 1, 2, 3, 1, 2, 3] ys |> ith(-1) //│ = None ys |> ith(0) //│ = Some(1) ys |> ith(1) //│ = Some(2) ys |> ith(2) //│ = Some(3) ys |> ith(3) //│ = Some(1) ys |> ith(4) //│ = Some(2) ys |> ith(5) //│ = Some(3) ys |> ith(6) //│ = Some(1) ys |> ith(7) //│ = Some(2) ys |> ith(8) //│ = Some(3) ys |> ith(9) //│ = None let ys = (ys |> popFront).rest //│ ys = [2, 3, 1, 2, 3, 1, 2, 3] let ys = (ys |> popBack).rest //│ ys = [2, 3, 1, 2, 3, 1, 2] let ys = (ys |> popBack).rest //│ ys = [2, 3, 1, 2, 3, 1] let ys = (ys |> popBack).rest //│ ys = [2, 3, 1, 2, 3] let ys = (ys |> popFront).rest //│ ys = [3, 1, 2, 3] let ys = (ys |> popFront).rest //│ ys = [1, 2, 3] let ys = (ys |> popFront).rest //│ ys = [2, 3] let ys = xs ++ xs ++ xs //│ ys = [1, 2, 3, 1, 2, 3, 1, 2, 3] ys |> dropLeftRight(3, 3) //│ = [1, 2, 3] let ys = 1 +: 2 +: 3 +: ys :+ 1 :+ 2 :+ 3 //│ ys = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3] :expect true LazyFingerTree.equals(ys, [...ys]) //│ = true :expect true (ys ++ ys).length == ys.length + ys.length //│ = true :silent let zs = ys ++ ys :expect true zs FingerTreeList.isFingerTree() //│ = true :expect true [...zs] Array.isArray() //│ = true fun mkRange(start, end) = if start < end then mkRange(start + 1, end) :+ start else mk() fun repeat(xs, n) = if n == 0 then mk() else xs ++ repeat(xs, n - 1) :expect true repeat(xs, 512).length == xs.length * 512 //│ = true let xs = FingerTreeList.mk(1, 2, 3) //│ xs = [1, 2, 3] :expect [1, [2, 3]] :sjs if xs is [a, ...ls] then [a, ls] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let ls, a, middleElements, element0$; //│ if (runtime.Tuple.isArrayLike(xs1) && xs1.length >= 1) { //│ element0$ = runtime.Tuple.get(xs1, 0); //│ middleElements = runtime.Tuple.slice(xs1, 1, 0); //│ ls = middleElements; //│ a = element0$; //│ globalThis.Object.freeze([ //│ a, //│ ls //│ ]) //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = [1, [2, 3]] fun addUntilN(xs, n) = if n <= 0 then xs else addUntilN([n] ++ xs, n - 1) :silent let zs = addUntilN(mk(), 1000) :expect 1000 zs.length //│ = 1000 fun popUntilEmpty(lft, acc) = if lft is [a, ...ls] then popUntilEmpty(ls, [..acc, a]) [] then acc :sjs fun popByIndex(start, end, acc, lft) = if start >= end then acc else popByIndex(start + 1, end, [...acc, lft.at(start)], lft) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let popByIndex; //│ popByIndex = function popByIndex(start, end, acc, lft) { //│ loopLabel: while (true) { //│ let scrut, tmp34, tmp35, tmp36; //│ scrut = start >= end; //│ if (scrut === true) { //│ return acc //│ } //│ tmp34 = start + 1; //│ tmp35 = runtime.safeCall(lft.at(start)); //│ tmp36 = globalThis.Object.freeze([ //│ ...acc, //│ tmp35 //│ ]); //│ start = tmp34; //│ acc = tmp36; //│ continue loopLabel; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :silent let zs1 = popUntilEmpty(zs, []) :silent let zs2 = popByIndex(0, zs.length, [], zs) zs1 |> isFingerTree //│ = false zs2 |> isFingerTree //│ = false zs |> isFingerTree //│ = true :expect true LazyFingerTree.equals(zs1, zs) //│ = true :expect true LazyFingerTree.equals(zs2, zs) //│ = true (if FingerTreeList.mk(1,2,3) is [a, ...ls, b] then ls) |> FingerTreeList.isFingerTree //│ = true :expect 1 zs.at(0) //│ = 1 :expect 1 zs.[0] //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/std/IterTest.mls ================================================ :js import "../../mlscript-compile/Iter.mls" let arr = [ "0" "1" "2" "3" ] //│ arr = ["0", "1", "2", "3"] {Iter.joined(_, "")} of arr //│ = "0123" arr Iter.joined("") //│ = "0123" arr \ Iter.mapping(_ + "!") \ Iter.toArray() //│ = ["0!", "1!", "2!", "3!"] arr |> Iter.withMap of _ + "!" |> Iter.withFilter of _ != "2!" |> Iter.toArray //│ = ["0!", "1!", "3!"] ["abc", "01234"] |> Iter.withMap of _.toUpperCase() |> Iter.flattening |> Iter.withFilter of _ != "0" |> Iter.toArray //│ = ["A", "B", "C", "1", "2", "3", "4"] mkStr of "0" "1" "2" "3" //│ = "0123" mkStr of "0" 1 //│ // Standard Error: //│ Assertion failed //│ //│ = "01" fun xs = [1, 2, 3] Iter.mapping(_ + "!" !> print) xs //│ = Iterable(_) xs.toArray() //│ > 1! //│ > 2! //│ > 3! //│ = ["1!", "2!", "3!"] xs.toArray() //│ > 1! //│ > 2! //│ > 3! //│ = ["1!", "2!", "3!"] let ys = xs //│ ys = Iterable(_) ys.toArray() //│ > 1! //│ > 2! //│ > 3! //│ = ["1!", "2!", "3!"] set ys.toArray().[0] = "??" //│ > 1! //│ > 2! //│ > 3! ys.toArray() //│ > 1! //│ > 2! //│ > 3! //│ = ["1!", "2!", "3!"] ================================================ FILE: hkmc2/shared/src/test/mlscript/std/LazyArrayTest.mls ================================================ :js import "../../mlscript-compile/LazyArray.mls" import "../../mlscript-compile/MutMap.mls" open LazyArray // Splices :silent val a = concat([0]) :silent let b = a.concat([1,2,3,4]) :expect [0, 1, 2, 3, 4] [...b] //│ = [0, 1, 2, 3, 4] :expect [0, 1, 2, 3, 4] b.materialize() //│ = [0, 1, 2, 3, 4] // Views :silent let a = mk(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) dropLeftRight(2, 3)() dropLeftRight(1, 1)() :expect 4 a.at(0) //│ = 4 :expect 4 a.at(-3) //│ = 4 :expect 5 a.at(1) //│ = 5 :expect 5 a.at(-2) //│ = 5 :expect 6 a.at(2) //│ = 6 :expect 6 a.at(-1) //│ = 6 :expect [4, 5, 6] a //│ = [4, 5, 6] :expect [4, 5, 6] [...a] //│ = [4, 5, 6] :expect [4, 5, 6] a.materialize() //│ = [4, 5, 6] :re a.at(-4) //│ ═══[RUNTIME ERROR] RangeError: View.at: Index out of bounds :re a.at(3) //│ ═══[RUNTIME ERROR] RangeError: View.at: Index out of bounds // View and Splices that represent the same array should be equal :silent let xs = mk(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) dropLeftRight (1, 1)() :silent let ys = concat([1, 2, 3], [4, 5, 6], [7, 8, 9, 10]) :expect true equals([...xs], [...ys]) //│ = true :expect true equals(xs, ys) //│ = true :expect true equals(xs.materialize(), ys.materialize()) //│ = true // Mixing Views and Splices :silent let ys1 = mk(0).concat([1, 2, 3, 4], [5, 6, 7], [8, 9]) :expect [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [...ys1] //│ = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] :expect 10 ys1.length //│ = 10 :silent let ys2 = LazyArray.dropLeftRight(2, 2)(ys1) :expect [2, 3, 4, 5, 6, 7] ys2 //│ = [2, 3, 4, 5, 6, 7] :silent let ys2 = LazyArray.dropLeftRight(0, 2)(ys2) :expect [2, 3, 4, 5] ys2 //│ = [2, 3, 4, 5] // concat and __concat should do similar things :silent let w1 = __concat(1, __split, xs, __split, ys1, __split, ys1) :silent let w2 = concat([1], xs, ys1, ys1) :expect true w1.length == w2.length //│ = true :expect true equals([...w1], [...w2]) //│ = true :expect true equals(w1, w2) //│ = true :expect true equals(w1.materialize(), w2.materialize()) //│ = true // object concat and slice :silent let o1 = mk(1, 2, 3, 4, 5).slice(1, 4) :expect [2, 3, 4] o1 //│ = [2, 3, 4] :expect [2, 3, 4] [...o1] //│ = [2, 3, 4] :expect [2, 3, 4] o1.materialize() //│ = [2, 3, 4] let o2 = o1.concat([5, 6, 7]) //│ o2 = [2, 3, 4, 5, 6, 7] :silent let o3 = concat([1, 2, 3, 4]) :silent let o4 = o3.concat([5, 6, 7], [8]) let o5 = o4.slice(1, 7) //│ o5 = [2, 3, 4, 5, 6, 7] :expect true equals(o2, o5) //│ = true :expect true equals([...o2], [...o5]) //│ = true :expect true equals(o2.materialize(), o5.materialize()) //│ = true // slice indices let r = [1,2,3,4,5,6,7,8,9,10] let v = mk(1,2,3,4,5,6,7,8,9,10) let s = concat([1,2,3], [4,5,6], [7,8,9,10]) //│ r = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //│ s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //│ v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] :expect [4, 5] // both positive r.slice(3, 5) //│ = [4, 5] v.slice(3, 5) //│ = [4, 5] s.slice(3, 5) //│ = [4, 5] dropLeftRight(3, 5)(r) //│ = [4, 5] // both negative r.slice(-7, -5) //│ = [4, 5] v.slice(-7, -5) //│ = [4, 5] s.slice(-7, -5) //│ = [4, 5] // beg positive, fin negative r.slice(3, -5) //│ = [4, 5] v.slice(3, -5) //│ = [4, 5] s.slice(3, -5) //│ = [4, 5] // beg negative, fin positive r.slice(-7, 5) //│ = [4, 5] v.slice(-7, 5) //│ = [4, 5] s.slice(-7, 5) //│ = [4, 5] :expect [] // both positive, beg > fin r.slice(5, 3) //│ = [] v.slice(5, 3) //│ = [] s.slice(5, 3) //│ = [] // both negative, beg > fin r.slice(-5, -7) //│ = [] v.slice(-5, -7) //│ = [] s.slice(-5, -7) //│ = [] // beg > arr length r.slice(10, 12) //│ = [] v.slice(10, 12) //│ = [] s.slice(10, 12) //│ = [] // beg = fin = 0 r.slice(0, 0) //│ = [] v.slice(0, 0) //│ = [] s.slice(0, 0) //│ = [] // LazyArray on non-arrays dropLeftRight(1, 1)("hello") //│ = [e, l, l] val mm = MutMap.empty //│ mm = MutMap(Map(0) {}) mm |> MutMap.insert of "a", 1 :re val uint8Arr = new Uint8Array([1, 2, 3, 4, 5]) //│ ═══[RUNTIME ERROR] TypeError: Cannot freeze array buffer views with elements //│ uint8Arr = undefined val uint8Arr = new mut Uint8Array([1, 2, 3, 4, 5]) //│ uint8Arr = Uint8Array { "0": 1, "1": 2, "2": 3, "3": 4, "4": 5 } dropLeftRight(1, 1)(uint8Arr) //│ = [2, 3, 4] :re concat([1, 2, 3], "hello", uint8Arr, mm) //│ ═══[RUNTIME ERROR] Error: Expected an Array, got: MutMap(Map(1) {"a" => 1}) :re dropLeftRight(0, 0)(mm) //│ ═══[RUNTIME ERROR] Error: Expected an Array, got: MutMap(Map(1) {"a" => 1}) ================================================ FILE: hkmc2/shared/src/test/mlscript/std/LazyFingerTreeTest.mls ================================================ :js import "../../mlscript-compile/LazyFingerTree.mls" import "../../mlscript-compile/FingerTreeList.mls" import "../../mlscript-compile/MutMap.mls" open LazyFingerTree // Splices :silent val a = concat(FingerTreeList.mk(0)) :silent let b = a.concat([1, 2, 3, 4]) :expect [0, 1, 2, 3, 4] [...b] //│ = [0, 1, 2, 3, 4] :expect [0, 1, 2, 3, 4] b.materialize() //│ = [0, 1, 2, 3, 4] // Views :silent let a = mk(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) dropLeftRight(2, 3)() dropLeftRight(1, 1)() :expect 4 a.at(0) //│ = 4 :expect 4 a.at(-3) //│ = 4 :expect 5 a.at(1) //│ = 5 :expect 5 a.at(-2) //│ = 5 :expect 6 a.at(2) //│ = 6 :expect 6 a.at(-1) //│ = 6 :expect [4, 5, 6] a //│ = [4, 5, 6] :expect [4, 5, 6] [...a] //│ = [4, 5, 6] :re a.at(-4) //│ ═══[RUNTIME ERROR] RangeError: View.at: Index out of bounds :re a.at(3) //│ ═══[RUNTIME ERROR] RangeError: View.at: Index out of bounds // View and Splices that represent the same array should be equal :silent let xs = mk(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) dropLeftRight(1, 1)() :silent let ys = concat([1, 2, 3], [4, 5, 6], [7, 8, 9, 10]) :expect true equals([...xs], [...ys]) //│ = true :expect true equals(xs, ys) //│ = true :expect true equals(xs.materialize(), ys.materialize()) //│ = true // Mixing Views and Splices :silent let ys1 = concat(mk(0), [1, 2, 3, 4], [5, 6, 7], [8, 9]) :expect [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [...ys1] //│ = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] :expect 10 ys1.length //│ = 10 :silent let ys2 = LazyFingerTree.dropLeftRight(2, 2)(ys1) :expect [2, 3, 4, 5, 6, 7] ys2 //│ = [2, 3, 4, 5, 6, 7] :silent let ys2 = LazyFingerTree.dropLeftRight(0, 2)(ys2) :expect [2, 3, 4, 5] ys2 //│ = [2, 3, 4, 5] // concat and __concat should do similar things :silent let w1 = __concat(1, __split, xs, __split, ys1, __split, ys1) :silent let w2 = concat([1], xs, ys1, ys1) :expect true w1.length == w2.length //│ = true :expect true equals([...w1], [...w2]) //│ = true :expect true equals(w1, w2) //│ = true :expect true equals(w1.materialize(), w2.materialize()) //│ = true // object concat and slice :silent let o1 = mk(1, 2, 3, 4, 5).slice(1, 4) :expect [2, 3, 4] o1 //│ = [2, 3, 4] :expect [2, 3, 4] [...o1] //│ = [2, 3, 4] let o2 = o1.concat([5, 6, 7]) //│ o2 = [2, 3, 4, 5, 6, 7] :silent let o3 = concat([1, 2, 3, 4]) :silent let o4 = o3.concat([5, 6, 7], [8]) let o5 = o4.slice(1, 7) //│ o5 = [2, 3, 4, 5, 6, 7] :expect true equals(o2, o5) //│ = true :expect true equals([...o2], [...o5]) //│ = true :expect true equals(o2.materialize(), o5) //│ = true // slice indices let r = [1,2,3,4,5,6,7,8,9,10] let v = mk(1,2,3,4,5,6,7,8,9,10) let s = concat([1,2,3], [4,5,6], [7,8,9,10]) //│ r = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //│ s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //│ v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] :expect [4, 5] // both positive r.slice(3, 5) //│ = [4, 5] v.slice(3, 5) //│ = [4, 5] s.slice(3, 5) //│ = [4, 5] dropLeftRight(3, 5)(r) //│ = [4, 5] // both negative r.slice(-7, -5) //│ = [4, 5] v.slice(-7, -5) //│ = [4, 5] s.slice(-7, -5) //│ = [4, 5] // beg positive, fin negative r.slice(3, -5) //│ = [4, 5] v.slice(3, -5) //│ = [4, 5] s.slice(3, -5) //│ = [4, 5] // beg negative, fin positive r.slice(-7, 5) //│ = [4, 5] v.slice(-7, 5) //│ = [4, 5] s.slice(-7, 5) //│ = [4, 5] :expect [] // both positive, beg > fin r.slice(5, 3) //│ = [] v.slice(5, 3) //│ = [] s.slice(5, 3) //│ = [] // both negative, beg > fin r.slice(-5, -7) //│ = [] v.slice(-5, -7) //│ = [] s.slice(-5, -7) //│ = [] // beg > arr length r.slice(10, 12) //│ = [] v.slice(10, 12) //│ = [] s.slice(10, 12) //│ = [] // beg = fin = 0 r.slice(0, 0) //│ = [] v.slice(0, 0) //│ = [] s.slice(0, 0) //│ = [] ================================================ FILE: hkmc2/shared/src/test/mlscript/std/PredefTest.mls ================================================ :js id(123) //│ = 123 :e :re id() //│ ╔══[COMPILATION ERROR] Expected 1 arguments, got 0 //│ ║ l.11: id() //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. print(1, 2, 3) //│ > 1 2 3 1 print() //│ > 1 1 \ print() //│ > 1 1 \ (print(_))() //│ > 1 {1 \ print(_)}(2) //│ > 1 2 1 \ print() //│ > 1 2 + 2 |> print //│ > 4 2 + 2 |> (_ * 2) //│ = 8 2 + 2 .> (_ * 2) //│ = 6 print <| 2 + 2 //│ > 4 (_ * 2) <| 2 + 2 |> (_ + 1) //│ = 9 2 + 2 !> print //│ > 4 //│ = 4 print 4 //│ = 4 print (_ + 1) //│ > 4 //│ = 5 (_ + 1) <| 2 + 2 !> print //│ > 4 //│ = 5 fun f(x)(a) = x + a 123 |> f of 1 //│ = 124 123 |> f <| 1 //│ = 124 fun f(x)(y)(a) = x + y + a 123 |> f(1) of 2 //│ = 126 tuple passing(1, 2, 3) <| 4 //│ = [1, 2, 3, 4] passing(tuple, 1, 2, 3) of 4, 5, 6 //│ = [1, 2, 3, 4, 5, 6] (tuple passing(1, 2, 3)) of 4, 5, 6 //│ = [1, 2, 3, 4, 5, 6] :re tuple passing(1, 2, 3) of 4, 5, 6 //│ ═══[RUNTIME ERROR] TypeError: f.bind is not a function :re ??("oops") //│ ═══[RUNTIME ERROR] Error: Not implemented: oops :re :sjs ??? //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Predef.notImplementedError //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: Not implemented :re ??? + 1 //│ ═══[RUNTIME ERROR] Error: Not implemented :re 1 + ??? //│ ═══[RUNTIME ERROR] Error: Not implemented Symbols //│ = Symbols { //│ prettyPrint: Symbol("mlscript.prettyPrint"), //│ definitionMetadata: Symbol("mlscript.definitionMetadata"), //│ class: object Symbols //│ } // * Function composition is associative let f = ((_ + 1) >> (_ * 2)) >> (_ - 1) g = (_ + 1) >> ((_ * 2) >> (_ - 1)) //│ f = fun //│ g = fun [f(123), g(123)] //│ = [247, 247] // *** Folding *** fold(+)(0, 1, 2, 3) //│ = 6 fold(-)(0, 1, 2, 3) //│ = -6 fold(print)(0, 1, 2, 3) //│ > 0 1 //│ > () 2 //│ > () 3 fold(tuple)(0, 1, 2, 3) //│ = [[[0, 1], 2], 3] fold(+) of 0 1 2 3 //│ = 6 :breakme :re :e fold(+)() fold(tuple) of "A" + "B" "C" //│ = ["AB", "C"] foldr(+)(0, 1, 2, 3) //│ = 6 foldr(-)(0, 1, 2, 3) //│ = -2 foldr(tuple)(0, 1, 2, 3) //│ = [0, [1, [2, 3]]] :todo foldr(+)() //│ ╔══[COMPILATION ERROR] Expected at least 1 arguments, got 0 //│ ║ l.185: foldr(+)() //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. // An alternative implementation of foldr fun foldr(f)(first, ...rest) = let len = rest.length if len == 0 then first else... let init = rest.at(len - 1) i = len - 2 while i >= 0 do set init = f(rest.at(i), init) i -= 1 f(first, init) foldr(+)(0, 1, 2, 3) //│ = 6 foldr(-)(0, 1, 2, 3) //│ = -2 fold(+) of "0" "1" "2" "3" //│ = "0123" import "../../mlscript-compile/Iter.mls" Iter.joined(_, "") of [ "0" "1" "2" "3" ] //│ = fun mkStr of "0" "1" "2" "3" //│ = "0123" mkStr of "0" 1 //│ // Standard Error: //│ Assertion failed //│ //│ = "01" // loose equality is disabled null == undefined //│ = false undefined == null //│ = false undefined === null //│ = false [] == false //│ = false "0" == false //│ = false 1 == true //│ = false // * Arrays are compared elementwise [1, 2, 3] == [1, 2, 3] //│ = true // non-data classes unchanged :silent class B(val a, b) let x = B(1, 2) let y = B(1, 4) x == y //│ = false // data classes are equal from arguments :silent data class A(a, b, c) module M with data class A(a, b, c) let x = A(1, 2, 3) let y = A(1, 2, 3) x == y //│ = true x != y //│ = false x == A(2, 3, 4) //│ = false x == M.A(1, 2, 3) //│ = false A([x], 2, 3) == A([y], 2, 3) //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/std/PrettyPrintTest.mls ================================================ :js import "../../mlscript-compile/Rendering.mls" open Rendering // We set `breakLength` to 74. With the `print` function and diff test comments // added, we can use an 80-character-wide ruler to check whether line breaks // work correctly. fun show(value) = let breakLength = 74 let rendered = render of value, indent: 2, breakLength: breakLength print of rendered .split("\n") .map of (line, _, _) => if line.length > breakLength then line else line + " ".repeat(breakLength - line.length) + "┊" .join("\n") show of [1, 2, 3, 4] //│ > [1, 2, 3, 4] ┊ :silent let shortText = "Hello, world!" let longText = "This is a very long line. ".repeat(4).trim() // Long strings can exceed the `breakLength` limit. show of longText //│ > "This is a very long line. This is a very long line. This is a very long line. This is a very long line." show of [1, longText, 2, 3, 4, 5] //│ > [ ┊ //│ > 1, ┊ //│ > "This is a very long line. This is a very long line. This is a very long line. This is a very long line.", //│ > 2, ┊ //│ > 3, ┊ //│ > 4, ┊ //│ > 5 ┊ //│ > ] ┊ show of x: 0, y: 0 //│ > {x: 0, y: 0} ┊ show of x: 0, y: 0, z: longText //│ > { ┊ //│ > x: 0, ┊ //│ > y: 0, ┊ //│ > z: "This is a very long line. This is a very long line. This is a very long line. This is a very long line." //│ > } ┊ show of x: 0, y: 0, z: [shortText, 0, 0] //│ > {x: 0, y: 0, z: ["Hello, world!", 0, 0]} ┊ show of x: 0, y: 0, z: [longText, 0, 0] //│ > { ┊ //│ > x: 0, ┊ //│ > y: 0, ┊ //│ > z: [ ┊ //│ > "This is a very long line. This is a very long line. This is a very long line. This is a very long line.", //│ > 0, ┊ //│ > 0 ┊ //│ > ] ┊ //│ > } ┊ show of x: 0, y: [shortText, shortText, shortText], z: [longText, 0, 0] //│ > { ┊ //│ > x: 0, ┊ //│ > y: ["Hello, world!", "Hello, world!", "Hello, world!"], ┊ //│ > z: [ ┊ //│ > "This is a very long line. This is a very long line. This is a very long line. This is a very long line.", //│ > 0, ┊ //│ > 0 ┊ //│ > ] ┊ //│ > } ┊ // It should break `y` but keep `x` and `z` inline. show of x: (a: "A", b: "B", c: "C") y: [shortText, shortText, shortText, shortText, shortText] z: [1, 2, 3, 4] //│ > { ┊ //│ > x: {a: "A", b: "B", c: "C"}, ┊ //│ > y: [ ┊ //│ > "Hello, world!", ┊ //│ > "Hello, world!", ┊ //│ > "Hello, world!", ┊ //│ > "Hello, world!", ┊ //│ > "Hello, world!" ┊ //│ > ], ┊ //│ > z: [1, 2, 3, 4] ┊ //│ > } ┊ show of Array(10) //│ > [<10 empty items>] ┊ show of [-100, ...Array(5), BigInt("10000000")] //│ > [-100, undefined, undefined, undefined, undefined, undefined, 10000000n] ┊ show of [-100, ...Array(5), BigInt("100000000")] //│ > [-100, undefined, undefined, undefined, undefined, undefined, 100000000n] ┊ show of ["hello", ...Array(7), "world"] //│ > [ ┊ //│ > "hello", ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > "world" ┊ //│ > ] ┊ show of Object.assign of () => 42 x: 0 y: [...Array(8)] z: 512 //│ > fun { ┊ //│ > x: 0, ┊ //│ > y: [ ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined, ┊ //│ > undefined ┊ //│ > ], ┊ //│ > z: 512 ┊ //│ > } ┊ show of x: new Set([1, 2, 3, 4]), y: new Set([5, 6, 7, 8]) //│ > {x: Set(4) {1, 2, 3, 4}, y: Set(4) {5, 6, 7, 8}} ┊ show of x: new Set([longText]) //│ > { ┊ //│ > x: Set(1) { ┊ //│ > "This is a very long line. This is a very long line. This is a very long line. This is a very long line." //│ > } ┊ //│ > } ┊ :silent let testSet = new Set([shortText, (), Symbol.for("you"), 1, 2, 3, 4, 5, 6, 7]) show of x: testSet //│ > {x: Set(10) {"Hello, world!", (), Symbol("you"), 1, 2, 3, 4, 5, 6, 7}} ┊ testSet.add(8); show of x: testSet //│ > {x: Set(11) {"Hello, world!", (), Symbol("you"), 1, 2, 3, 4, 5, 6, 7, 8}} ┊ testSet.add(9); show of x: testSet //│ > { ┊ //│ > x: Set(12) { ┊ //│ > "Hello, world!", ┊ //│ > (), ┊ //│ > Symbol("you"), ┊ //│ > 1, ┊ //│ > 2, ┊ //│ > 3, ┊ //│ > 4, ┊ //│ > 5, ┊ //│ > 6, ┊ //│ > 7, ┊ //│ > 8, ┊ //│ > 9 ┊ //│ > } ┊ //│ > } ┊ show of new Map([[1, 2], [3, 4]]) //│ > Map(2) {1 => 2, 3 => 4} ┊ show of new Map([[longText, shortText]]) //│ > Map(1) { ┊ //│ > "This is a very long line. This is a very long line. This is a very long line. This is a very long line." => "Hello, world!" //│ > } ┊ show of new Map([[{ x: shortText }, shortText]]) //│ > Map(1) {{x: "Hello, world!"} => "Hello, world!"} ┊ show of new Map([[{ x: longText }, shortText]]) //│ > Map(1) { ┊ //│ > { ┊ //│ > x: "This is a very long line. This is a very long line. This is a very long line. This is a very long line." //│ > } => "Hello, world!" ┊ //│ > } ┊ :silent let testKey = mut { x: 0, y: 0, z: 0 } let testMap = new Map([[testKey, testKey]]) show of testMap //│ > Map(1) {{x: 0, y: 0, z: 0} => {x: 0, y: 0, z: 0}} ┊ set testKey.w = 0, show of testMap //│ > Map(1) {{x: 0, y: 0, z: 0, w: 0} => {x: 0, y: 0, z: 0, w: 0}} ┊ set testKey.a = 42, show of testMap //│ > Map(1) {{x: 0, y: 0, z: 0, w: 0, a: 42} => {x: 0, y: 0, z: 0, w: 0, a: 42}} // TODO set testKey.b = 404, show of testMap //│ > Map(1) { ┊ //│ > {x: 0, y: 0, z: 0, w: 0, a: 42, b: 404} => {x: 0, y: 0, z: 0, w: 0, a: 42, b: 404} //│ > } ┊ :silent let testKey = mut { a: 1, b: a * 10, c: b * 10, d: c * 10, e: d * 10 } let testMap = new Map([[testKey, shortText]]) show of testMap //│ > Map(1) {{a: 1, b: 10, c: 100, d: 1000, e: 10000} => "Hello, world!"} ┊ set testKey.x = 42, show of testMap //│ > Map(1) {{a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42} => "Hello, world!"} set testKey.y = 100, show of testMap //│ > Map(1) { ┊ //│ > {a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42, y: 100} => "Hello, world!" //│ > } ┊ set testKey.z = shortText, show of testMap //│ > Map(1) { ┊ //│ > {a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42, y: 100, z: "Hello, world!"} => "Hello, world!" //│ > } ┊ testMap.set(testKey, text: shortText), show of testMap //│ > Map(1) { ┊ //│ > {a: 1, b: 10, c: 100, d: 1000, e: 10000, x: 42, y: 100, z: "Hello, world!"} => {text: "Hello, world!"} //│ > } ┊ :silent let fakeAddress = street: "123 Main St" city: "Springfield" state: "IL" zip: "62704" country: "USA" show of new Set of [fakeAddress] //│ > Set(1) { ┊ //│ > { ┊ //│ > street: "123 Main St", ┊ //│ > city: "Springfield", ┊ //│ > state: "IL", ┊ //│ > zip: "62704", ┊ //│ > country: "USA" ┊ //│ > } ┊ //│ > } ┊ show of firstName: "John" lastName: "Doe" // This object should cause its parent object to be split into multiple lines, // but it should be printed on a single line itself. company: name: "Acme Corp." industry: "Manufacturing" age: 42 email: "john.doe@acme.com" phone: "+1 555-1234-888" address: fakeAddress isActive: true roles: ["admin", "user"] metadata: created: "2024-01-01" updated: "2024-06-01" //│ > { ┊ //│ > firstName: "John", ┊ //│ > lastName: "Doe", ┊ //│ > company: {name: "Acme Corp.", industry: "Manufacturing"}, ┊ //│ > age: 42, ┊ //│ > email: "john.doe@acme.com", ┊ //│ > phone: "+1 555-1234-888", ┊ //│ > address: { ┊ //│ > street: "123 Main St", ┊ //│ > city: "Springfield", ┊ //│ > state: "IL", ┊ //│ > zip: "62704", ┊ //│ > country: "USA" ┊ //│ > }, ┊ //│ > isActive: true, ┊ //│ > roles: ["admin", "user"], ┊ //│ > metadata: {created: "2024-01-01", updated: "2024-06-01"} ┊ //│ > } ┊ show of level1: level2: level3: level4: level5: level6: level7: level8: level9: level10: "deep value" //│ > { ┊ //│ > level1: { ┊ //│ > level2: { ┊ //│ > level3: { ┊ //│ > level4: { ┊ //│ > level5: { ┊ //│ > level6: {level7: {level8: {level9: {level10: "deep value"}}}} ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ :silent let nested = [[[[[[[[[[[[[[[[[[[[[[[[[[[["hkhkhkhkhkhkhkhk"]]]]]]]]]]]]]]]]]]]]]]]]]]]] show of nested //│ > [[[[[[[[[[[[[[[[[[[[[[[[[[[["hkhkhkhkhkhkhkhk"]]]]]]]]]]]]]]]]]]]]]]]]]]]]┊ show of [nested] //│ > [ ┊ //│ > [ ┊ //│ > [[[[[[[[[[[[[[[[[[[[[[[[[[["hkhkhkhkhkhkhkhk"]]]]]]]]]]]]]]]]]]]]]]]]]]] //│ > ] ┊ //│ > ] ┊ show of [[[[nested]]]] //│ > [ ┊ //│ > [ ┊ //│ > [ ┊ //│ > [ ┊ //│ > [ ┊ //│ > [ ┊ //│ > [ ┊ //│ > [ ┊ //│ > [[[[[[[[[[[[[[[[[[[[[[[["hkhkhkhkhkhkhkhk"]]]]]]]]]]]]]]]]]]]]]]]] //│ > ] ┊ //│ > ] ┊ //│ > ] ┊ //│ > ] ┊ //│ > ] ┊ //│ > ] ┊ //│ > ] ┊ //│ > ] ┊ :silent let nested = a: b: c: d: e: f: g: "This property name is very long.": 0 show of nested //│ > {a: {b: {c: {d: {e: {f: {g: {"This property name is very long.": 0}}}}}}}}┊ show of x: y: nested //│ > { ┊ //│ > x: { ┊ //│ > y: { ┊ //│ > a: { ┊ //│ > b: { ┊ //│ > c: {d: {e: {f: {g: {"This property name is very long.": 0}}}}} ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ show of x: y: z: nested //│ > { ┊ //│ > x: { ┊ //│ > y: { ┊ //│ > z: { ┊ //│ > a: { ┊ //│ > b: { ┊ //│ > c: {d: {e: {f: {g: {"This property name is very long.": 0}}}}}┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ show of u: v: w: i: j: k: nested x: y: z: [nested, nested] //│ > { ┊ //│ > u: { ┊ //│ > v: { ┊ //│ > w: { ┊ //│ > i: { ┊ //│ > j: { ┊ //│ > k: { ┊ //│ > a: { ┊ //│ > b: { ┊ //│ > c: { ┊ //│ > d: { ┊ //│ > e: {f: {g: {"This property name is very long.": 0}}}┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > }, ┊ //│ > x: { ┊ //│ > y: { ┊ //│ > z: [ ┊ //│ > { ┊ //│ > a: { ┊ //│ > b: { ┊ //│ > c: { ┊ //│ > d: { ┊ //│ > e: { ┊ //│ > f: {g: {"This property name is very long.": 0}} ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > }, ┊ //│ > { ┊ //│ > a: { ┊ //│ > b: { ┊ //│ > c: { ┊ //│ > d: { ┊ //│ > e: { ┊ //│ > f: {g: {"This property name is very long.": 0}} ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > ] ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ //│ > } ┊ class Hola with fun toString() = "Hola.\nMi amigo.\n\u00bfQu\u00e9 tal?\nHasta luego." // Customized `toString` should be able to trigger breaking lines. show of x: y: new Hola //│ > { ┊ //│ > x: { ┊ //│ > y: Hola. \ ┊ //│ > Mi amigo. \ ┊ //│ > ¿Qué tal? \ ┊ //│ > Hasta luego. ┊ //│ > } ┊ //│ > } ┊ class Hello with fun toString() = "Hi there. How's going?" show of x: y: z: new Hello //│ > {x: {y: {z: Hi there. How's going?}}} ┊ // ___ _ _ // / _ \ _ __ | |_(_) ___ _ __ ___ // | | | | '_ \| __| |/ _ \| '_ \/ __| // | |_| | |_) | |_| | (_) | | | \__ \ // \___/| .__/ \__|_|\___/|_| |_|___/ // |_| :silent let testObject = x: x: x: x: x: 42 y: shortText render of testObject //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, breakLength: null //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, breakLength: undefined //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, breakLength: () //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, breakLength: "bad options" //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, breakLength: -10 //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, indent: null //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, indent: undefined //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, indent: () //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, indent: "bad options" //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, indent: -10 //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, padding: true //│ = "{ x: { x: { x: { x: { x: 42 } } } }, y: \"Hello, world!\" }" render of testObject, padding: null //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, padding: undefined //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, padding: () //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" render of testObject, padding: "bad value" //│ = "{x: {x: {x: {x: {x: 42}}}}, y: \"Hello, world!\"}" // * Strangely, the first line is not broken, but the rest is... @Luyu whyyyy [ [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1] [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1] [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1] ] //│ = [ //│ [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1], //│ [ //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1 //│ ], //│ [ //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1, //│ 2, //│ 3, //│ 1 //│ ] //│ ] ================================================ FILE: hkmc2/shared/src/test/mlscript/std/RecordTest.mls ================================================ :js import "../../mlscript-compile/Record.mls" let src = k0: 0 k1: 1 k2: 2 k3: 3 k4: 4 //│ src = {k0: 0, k1: 1, k2: 2, k3: 3, k4: 4} src Record.steal("k1", "k2", "k3") //│ = {k1: 1, k2: 2, k3: 3} let dst = k0: "0" ...src Record.steal("k1", "k2", "k3") k4: "4" //│ dst = {k0: "0", k1: 1, k2: 2, k3: 3, k4: "4"} // * Alternative 'cute' API import "../../mlscript-compile/Iter.mls" fun steal(...fields_from) = let from = fields_from.at(-1).from let fields = fields_from.slice(0, -1) let rcd = new mut Object fields Iter.each of f => set rcd.(f) = from.(f) rcd let src = k0: 0 k1: 1 k2: 2 k3: 3 k4: 4 //│ src = {k0: 0, k1: 1, k2: 2, k3: 3, k4: 4} steal("k1", "k2", "k3", from: src) //│ = {k1: 1, k2: 2, k3: 3} let dst = k0: "0" ...steal("k1", "k2", "k3", from: src) k4: "4" //│ dst = {k0: "0", k1: 1, k2: 2, k3: 3, k4: "4"} ================================================ FILE: hkmc2/shared/src/test/mlscript/std/RenderingTest.mls ================================================ :js render(()) //│ = "()" render(null) //│ = "null" render(undefined) //│ = "undefined" render(123) //│ = "123" render("123") //│ = "\"123\"" render("12\n3") //│ = "\"12\\n3\"" render("12\\n3") //│ = "\"12\\\\n3\"" render([]) //│ = "[]" render([1]) //│ = "[1]" render([1, 2]) //│ = "[1, 2]" render([1,2,3]) //│ = "[1, 2, 3]" render([1,2,3,4]) //│ = "[1, 2, 3, 4]" render(1) //│ = "1" render(Object.create(null)) //│ = "{}" render(x => x) //│ = "fun" class Foo render(Foo) //│ = "class Foo" render(new Foo) //│ = "Foo" interleave(0) of 1, 2, 3 //│ = [1, 0, 2, 0, 3] let arg = [1, 2] //│ arg = [1, 2] map(render)(...arg) //│ = ["1", "2"] fold(+)("[", ...interleave("|")(...map(render)(...arg)), "]") //│ = "[1|2]" interleave(", ") of 1, 2 //│ = [1, ", ", 2] data class Foo(val xs) render(Foo([1, 2, 3])) //│ = "Foo([1, 2, 3])" render([Foo([1]), Foo([2])]) //│ = "[Foo([1]), Foo([2])]" renderAsStr("1") //│ = "1" renderAsStr(1) //│ = "1" let xs = [1, 2] //│ xs = [1, 2] map(renderAsStr)(xs) //│ = ["[1, 2]"] map(renderAsStr(_))(xs) //│ = ["[1, 2]"] map(_ + 1)(...xs) //│ = [2, 3] renderAsStr of new RegExp("test") //│ = "/test/" renderAsStr of Symbol.for("mlscript") //│ = "Symbol(\"mlscript\")" renderAsStr of BigInt("2" + "0".repeat(30)) //│ = "2000000000000000000000000000000n" renderAsStr of new Set of [1, 2, 3] //│ = "Set(3) {1, 2, 3}" renderAsStr of new Map of [] //│ = "Map(0) {}" renderAsStr of new Map of [[1, 2], [3, 4]] //│ = "Map(2) {1 => 2, 3 => 4}" renderAsStr of new Map of [[Foo, 2], [Symbol, 1]] //│ = "Map(2) {fun Foo { class: class Foo } => 2, class Symbol => 1}" renderAsStr of new Map([[{ a: 0 }, { b: 0 }]]) //│ = "Map(1) {{a: 0} => {b: 0}}" renderAsStr of new Error("hello") //│ = "Error: hello" renderAsStr of new TypeError("hello") //│ = "TypeError: hello" renderAsStr of Object.create(null) //│ = "{}" // `Object`'s prototype has `null` as the prototype. renderAsStr of Object.prototype //│ = "{}" renderAsStr of hello: "world" //│ = "{hello: \"world\"}" renderAsStr of Object.assign(Object.create(null), hello: "world") //│ = "{hello: \"world\"}" renderAsStr of new WeakMap //│ = "WeakMap { }" renderAsStr of new WeakSet //│ = "WeakSet { }" // _____ _ _ // | ___| _ _ __ ___| |_(_) ___ _ __ ___ // | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __| // | _|| |_| | | | | (__| |_| | (_) | | | \__ \ // |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ // // ============================================= class Foo renderAsStr of Foo //│ = "class Foo" set Foo.bar = (direction: "vertical") renderAsStr of Foo //│ = "class Foo" class Bar extends Foo renderAsStr of Bar //│ = "class Bar" renderAsStr of new Bar //│ = "Bar" fun test() = () renderAsStr of test //│ = "fun test" renderAsStr of [42, 100, test] //│ = "[42, 100, fun test]" set test.test = test renderAsStr of test //│ = "fun test { test: ref'1 } as ref'1" let greetings = () => "hello" //│ greetings = fun greetings renderAsStr of greetings //│ = "fun greetings" renderAsStr of x => x * 2 //│ = "fun" renderAsStr of Object.assign(x => x * 2, { a: 2 }) //│ = "fun { a: 2 }" renderAsStr of globalThis.setImmediate //│ = "fun setImmediate" // ____ _ _ // / ___(_)_ __ ___ _ _| | __ _ _ __ // | | | | '__/ __| | | | |/ _` | '__| // | |___| | | | (__| |_| | | (_| | | // \____|_|_| \___|\__,_|_|\__,_|_| // // ===================================== // Test: simple circular reference. let a = mut x: 0 y: 1 //│ a = {x: 0, y: 1} set a.x = a a //│ = {x: ref'1, y: 1} as ref'1 // Test: an object is referenced multiple times in a circular way. let tree = left: mut left: null right: null right: mut left: null right: null //│ tree = {left: {left: null, right: null}, right: {left: null, right: null}} set tree.left.left = tree set tree.right.left = tree tree //│ = {left: {left: ref'1, right: null}, right: {left: ref'1, right: null}} as ref'1 // Test: nested circular reference. let b = mut [a, a] //│ b = [{x: ref'1, y: 1} as ref'1, {x: ref'2, y: 1} as ref'2] // If the same circular object appears multiple times, it should be rendered as // the same reference in single-line mode. render(b) //│ = "[{x: ref'1, y: 1} as ref'1, {x: ref'1, y: 1} as ref'1]" b.push(b) //│ = 3 b //│ = [{x: ref'1, y: 1} as ref'1, {x: ref'2, y: 1} as ref'2, ref'3] as ref'3 let functionWithProperties = Object.assign(x => x, "kind": "Callable") //│ functionWithProperties = fun { kind: "Callable" } functionWithProperties //│ = fun { kind: "Callable" } functionWithProperties.kind //│ = "Callable" // ___ _ _ _ // / _ \| |__ (_) ___ ___| |_ // | | | | '_ \| |/ _ \/ __| __| // | |_| | |_) | | __/ (__| |_ // \___/|_.__// |\___|\___|\__| // |__/ // ============================= let something = mut { x: 0 } //│ something = {x: 0} Object.defineProperty(something, "y", enumerable: true, get: () => 42) //│ = {x: 0, y: [Getter]} Object.defineProperty of something, "z" enumerable: true get: () => 42 "set": (value) => print("The user tries to set the value to " + value) //│ = {x: 0, y: [Getter], z: [Getter/Setter]} renderAsStr of "\u6d4b\u8bd5": "Test" "\u4f60\u597d": "Hello" //│ = "{测试: \"Test\", 你好: \"Hello\"}" renderAsStr of "a property name with spaces": true //│ = "{\"a property name with spaces\": true}" // ____ _ // / ___| | __ _ ___ ___ ___ ___ // | | | |/ _` / __/ __|/ _ \/ __| // | |___| | (_| \__ \__ \ __/\__ \ // \____|_|\__,_|___/___/\___||___/ // // ================================= object Singleton renderAsStr of Singleton //│ = "Singleton" class Phantom // Parameter-less classes should be rendered as `class`. renderAsStr of Phantom //│ = "class Phantom" data class Point(x: Num, y: Num) // While classes with parameters should be rendered as `fun`. renderAsStr of Point //│ = "fun Point { class: class Point }" renderAsStr of Point.class //│ = "class Point" renderAsStr of Point(1, 2) //│ = "Point(1, 2)" renderAsStr of Point(1, 2) //│ = "Point(1, 2)" class Bar(x: Int, val a: Int) renderAsStr of Bar(123, 789) //│ = "Bar(_, 789)" import "../../mlscript-compile/Stack.mls" open Stack renderAsStr of 1 :: 2 :: 3 :: 4 //│ = "Cons(1, Cons(2, Cons(3, 4)))" class Origin with val x = 0 val y = 0 renderAsStr of new Origin //│ = "Origin { x: 0, y: 0 }" class Point(x: Num, y: Num) with val x = x val y = y // Note that we do not display members defined in the body. // Some classes define lots of members, so doing so could be problematic. renderAsStr of new Point(0, 1) //│ = "Point(_, _)" class SelfRef with val a = this val b = 1 renderAsStr of new SelfRef //│ = "SelfRef { a: ref'1, b: 1 } as ref'1" renderAsStr of new Object with val x = 0 fun y = 0 //│ = "$anon { x: 0 }" abstract class Shape data class Rectangle(x: Num, y: Num, w: Num, h: Num) renderAsStr of Shape //│ = "class Shape" renderAsStr of new Shape with val x = 0 //│ = "$anon { x: 0 }" renderAsStr of Rectangle //│ = "fun Rectangle { class: class Rectangle }" renderAsStr of Rectangle(0, 0, 5, 5) //│ = "Rectangle(0, 0, 5, 5)" pattern Null = null renderAsStr of Null //│ = "pattern Null" // FIXME? :re renderAsStr of Null.class //│ ═══[RUNTIME ERROR] Error: Access to required field 'class' yielded 'undefined' declare class Iterator class RangeIterator(val start: Int, val end: Int) extends Iterator with let current = start fun next() = if current < end then let result = (done: false, value: current) set current += 1 result else (done: true, value: undefined) renderAsStr of RangeIterator(0, 1) //│ = "RangeIterator(0, 1)" renderAsStr of RangeIterator(0, 5).toArray() //│ = "[0, 1, 2, 3, 4]" // __ __ _ // | \/ (_)___ ___ // | |\/| | / __|/ __| // | | | | \__ \ (__ // |_| |_|_|___/\___| // // =================== renderAsStr of Object //│ = "class Object" renderAsStr of String //│ = "class String" renderAsStr of Symbol //│ = "class Symbol" renderAsStr of BigInt //│ = "class BigInt" renderAsStr of globalThis.FinalizationRegistry //│ = "class FinalizationRegistry" let asyncFun = globalThis.eval("async () => {}") //│ asyncFun = fun renderAsStr of asyncFun //│ = "fun" renderAsStr of Object.getPrototypeOf(asyncFun).constructor //│ = "class AsyncFunction" renderAsStr of renderAsStr //│ = "fun renderAsStr" renderAsStr of JSON //│ = "{}" renderAsStr of new RegExp("") //│ = "/(?:)/" renderAsStr of globalThis.Iterator //│ = "class Iterator" // * Customization // =================== class Test Test //│ = class Test new Test //│ = Test set Test.prototype.(Symbols.prettyPrint) = () => "Test pretty print!" Test //│ = class Test new Test //│ = Test pretty print! ================================================ FILE: hkmc2/shared/src/test/mlscript/std/StackTests.mls ================================================ :js import "../../mlscript-compile/Stack.mls" open Stack let s1 = 1 :: 2 :: 3 :: Nil s2 = "a" :: "b" :: "c" :: Nil s3 = true :: false :: Nil //│ s1 = Cons(1, Cons(2, Cons(3, Nil))) //│ s2 = Cons("a", Cons("b", Cons("c", Nil))) //│ s3 = Cons(true, Cons(false, Nil)) // *** Concat *** // * Note that concatenation internally uses mutation, but it freezes the result let s12 = s1 ::: s2 //│ s12 = Cons(1, Cons(2, Cons(3, Cons("a", Cons("b", Cons("c", Nil)))))) // * Should not have any effect set s12.tail = Nil :expect false s12.tail isEmpty() //│ = false // *** Zipping *** zip of Nil //│ = Nil zip of s1 //│ = Cons([1], Cons([2], Cons([3], Nil))) zip of s1, s2 //│ = Cons([1, "a"], Cons([2, "b"], Cons([3, "c"], Nil))) zip of s1, s3 //│ = Cons([1, true], Cons([2, false], Cons([3], Nil))) s1 zip(s2) //│ = Cons([1, "a"], Cons([2, "b"], Cons([3, "c"], Nil))) print of ... s1 zip of s2, s3 //│ > Cons([1, "a", true], Cons([2, "b", false], Cons([3, "c"], Nil))) ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/AbusiveSuspensions.mls ================================================ :js data class Ok[A](value: A) data class Error[A](value: A) type Result[A, B] = Ok[A] | Error[B] fun foo[A, B](expr1, expr2, expr3): Result[A, B] = if expr1 is... Error(b) then b Ok(a) then... if expr2 is... Error(d) then d Ok(c) then... expr3 foo(Ok(1), Ok(2), 3) //│ = 3 foo(Ok(1), Error(2), 3) //│ = 2 :pe :e fun foo(expr1) = if expr1 is Error(b) then b Ok(a) then... a //│ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead //│ ║ l.28: if expr1 is //│ ║ ^ //│ ║ l.29: Error(b) then b //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (‹erroneous syntax›). //│ ║ l.28: if expr1 is //│ ║ ^ //│ ║ l.29: Error(b) then b //│ ╙── ^^ //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.29: Error(b) then b //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.30: Ok(a) then... //│ ║ ^^^^^^^^^^^^^ //│ ║ l.31: a //│ ╙── ^^^ fun foo(expr1) = if expr1 is Error(b) then b is Ok(a) then... a :todo fun foo(expr1) = if expr1 is Error(b) then b is Ok(a) then... a //│ ╔══[PARSE ERROR] Expected start of expression in this position; found 'is' keyword instead //│ ║ l.60: is Error(b) then b //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected identifier here //│ ║ l.60: is Error(b) then b //│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized term split (identifier) //│ ║ l.59: if expr1 //│ ╙── ^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/BackslashApps.mls ================================================ :js import "../../mlscript-compile/Iter.mls" fun inc(x) = x + 1 1 \inc() //│ = 2 1\inc() //│ = 2 1 \inc() \inc() //│ = 3 1 \inc()\inc() //│ = 3 42 \inc() \inc() //│ = 44 [1, 2, 3] \Iter.mapping(inc) \Iter.toStack() //│ = Cons(2, Cons(3, Cons(4, Nil))) [1, 2, 3] \Iter.mapping(inc) \Iter.toStack() //│ = Cons(2, Cons(3, Cons(4, Nil))) [1, 2, 3] \Iter.mapping @ x => x + 1 \Iter.toStack() //│ = Cons(2, Cons(3, Cons(4, Nil))) [1, 2, 3] \Iter.mapping @ _ + 1 \Iter.toStack() //│ = Cons(2, Cons(3, Cons(4, Nil))) ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/BasicIfs.mls ================================================ :js if true then 1 //│ = 1 if true then let x = 1 1 else 2 //│ = 1 if true then if true then let x = 1 1 else 2 //│ = 1 if 0 is 0 then 1 //│ = 1 if 0 is 0 then 1 1 then 1 //│ = 1 if 0 is 0 and true then 1 //│ = 1 if 0 is 0 and false then let x = 1 1 0 and true then let x = 1 1 //│ = 1 let foo = if true then "" else "" //│ foo = "" if 0 is 0 then 1 //│ = 1 if 0 is 0 then let hl = 1 hl //│ = 1 let s = "" //│ s = "" if s is "" then "" //│ = "" if s is "" then "" "" then "" //│ = "" (id is "") //│ = false (if s is "" then "") //│ = "" (if s is "" then "" ) //│ = "" (if s is "" then "" ) + "]" //│ = "]" (if "" is "" then "" nme then " " + nme ) //│ = "" if "" is "" then "" //│ = "" (if "" is id then "" ) //│ = "" (if "" is "" then "" ) //│ = "" ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls ================================================ :js let x = 0 y = 0 //│ x = 0 //│ y = 0 set x += 1 y += 2 [x, y] //│ = [1, 2] :p abstract class Foo(a) Bar(b) //│ ┊abstract┊→⟨┊class┊→⟨┊Foo┊(⟨┊a┊⟩)┊↵┊Bar┊(⟨┊b┊⟩)┊⟩←┊⟩←┊ //│ Parsed: //│ Modified(Keywrd(keyword 'abstract'),TypeDef(Cls,App(Ident(Foo),Tup(List(Ident(a)))),None)) //│ Modified(Keywrd(keyword 'abstract'),TypeDef(Cls,App(Ident(Bar),Tup(List(Ident(b)))),None)) :todo abstract class Foo(a) Bar(b) //│ ╔══[PARSE ERROR] Unexpected identifier here //│ ║ l.31: Bar(b) //│ ╙── ^^^ abstract class Foo object A extends Foo B extends Foo // * Would be cool if something like this was supported: :pe object \$ extends Foo A B //│ ╔══[LEXICAL ERROR] unexpected character '$' //│ ║ l.45: object \$ extends Foo //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found error instead //│ ║ l.45: object \$ extends Foo //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/TypesInTerms.mls ================================================ :js :ge 1 | 2 //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composed type) //│ ║ l.5: 1 | 2 //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge print("A") & print("B") //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composed type) //│ ║ l.12: print("A") & print("B") //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :ge ~false //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (negation type) //│ ║ l.19: ~false //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/WeirdBrackets.mls ================================================ :js {( 123 )} //│ = 123 {({( a: 1, b: 2 )})} //│ = {a: 1, b: 2} :e \{ Int } //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). //│ ║ l.12: \{ Int } //│ ╙── ^^^ //│ = fun passTo :e \(0) //│ ╔══[COMPILATION ERROR] Expected 2 arguments, got 1 //│ ║ l.19: \(0) //│ ╙── ^^^ //│ = fun fun (\) huh(x) = x \(0) //│ = 0 :e // Writing record patterns in this way is strange. In the previous `Desugarer`, // the logic from two methods happened to work together and supported this // syntax. In the new `Desugarer`, we chose not to support this syntax. fun f(x) = if x is {( Int then 1, Bool then 2 )} //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (parenthesis section). //│ ║ l.34: fun f(x) = if x is {( Int then 1, Bool then 2 )} //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ // * Potential alternative syntaxes for UCS splits :e fun f(x) = if x is ( Int then 1, Bool then 2 ) //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (parenthesis section). //│ ║ l.43: fun f(x) = if x is ( Int then 1, Bool then 2 ) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e fun f(x) = if x is \{ Int then 1, Bool then 2 } //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (juxtaposition). //│ ║ l.49: fun f(x) = if x is \{ Int then 1, Bool then 2 } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e fun f(x) = if x is \( Int then 1, Bool then 2 ) //│ ╔══[COMPILATION ERROR] Cannot use this reference as a pattern. //│ ║ l.55: fun f(x) = if x is \( Int then 1, Bool then 2 ) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Cannot use this reference as a pattern. //│ ║ l.55: fun f(x) = if x is \( Int then 1, Bool then 2 ) //│ ╙── ^ class Box[T](val content: T) :ucs desugared fun interesting(x) = if x is Box( Int then 1, Bool then 2 ) //│ Split with nested patterns: //│ > if //│ > x is Box(Int) then 1 //│ > x is Box(Bool) then 2 //│ Expanded split with flattened patterns: //│ > ‹if|while› //│ > x is Box(arg$Box$0$) and tmp:arg$Box$0$ is Int then 1 //│ > x is Box(arg$Box$0$) and tmp:arg$Box$0$ is Bool then 2 interesting of Box(42) //│ = 1 interesting of Box(true) //│ = 2 :re interesting of Box("str") //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/annotations/AnnotationPrecedence.mls ================================================ :js // The `@compile` annotation should annotate `A(0)`, not `A(0) as y`. // The precedence of annotations should be tight enough to not include `as`. pattern A(pattern B) = B fun foo(x) = if x is @annotations.compile A(0) as y then y fun foo(x) = if x is (@annotations.compile A(0)) as y then y // :pt // :re // if 1 is (@annotations.compile A(0)) as y then y :pe :w :e fun foo(x) = if x is @annotations.compile (A(0) as y) then y //│ ╔══[PARSE ERROR] Unexpected keyword 'then' in this position //│ ║ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (‹erroneous syntax›). //│ ║ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //│ ╙── ^ //│ ╔══[WARNING] This annotation is not supported here. //│ ║ l.21: fun foo(x) = if x is @annotations.compile (A(0) as y) then y //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: Patterns only support the `@compile` annotation. :e fun foo(x) = if x is @annotations.compile {A(0) as y} then y //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' //│ ║ l.40: fun foo(x) = if x is @annotations.compile {A(0) as y} then y //│ ╙── ^ :pe // parses as `@annotations.compile(A(0) as y) ‹missing annot body›` :e fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //│ ╔══[PARSE ERROR] Expected start of expression in this position //│ ║ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //│ ║ ^ //│ ╟── found a lone annotation instead //│ ║ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (‹erroneous syntax›). //│ ║ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.47: fun foo(x) = if x is (@annotations.compile (A(0) as y)) then y //│ ╙── ^ :pe // parses as `@annotations.compile(A(0) as y) ‹missing annot body›` :e fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //│ ╔══[PARSE ERROR] Expected start of expression in this position //│ ║ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //│ ║ ^ //│ ╟── found a lone annotation instead //│ ║ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (‹erroneous syntax›). //│ ║ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.63: fun foo(x) = if x is (@annotations.compile(A(0) as y)) then y //│ ╙── ^ :e fun foo(x) = if x is (@annotations.compile {A(0) as y}) then y //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' //│ ║ l.78: fun foo(x) = if x is (@annotations.compile {A(0) as y}) then y //│ ╙── ^ :fixme fun foo(x) = if x is (@annotations.compile A(0) as y) then y //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' //│ ║ l.84: fun foo(x) = if x is (@annotations.compile A(0) as y) then y //│ ╙── ^ // Using a dummy annotation to test that annotations gobble type Test :w @Test 1 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.95: @Test 1 //│ ║ ^^^^^ //│ ╟── This annotation is not supported on integer literal terms. //│ ║ l.95: @Test 1 //│ ╙── ^ //│ = 1 :w @Test 2 + 1 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.105: @Test 2 + 1 //│ ║ ^^^^^ //│ ╟── This annotation is not supported on application terms. //│ ║ l.105: @Test 2 + 1 //│ ╙── ^^^^^ //│ = 3 :w @Test 2 as Int //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.115: @Test 2 as Int //│ ║ ^^^^^ //│ ╟── This annotation is not supported on type ascription terms. //│ ║ l.115: @Test 2 as Int //│ ╙── ^^^^^^^^ //│ = 2 :w @Test id(2) as Int //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.125: @Test id(2) as Int //│ ║ ^^^^^ //│ ╟── This annotation is not supported on type ascription terms. //│ ║ l.125: @Test id(2) as Int //│ ╙── ^^^^^^^^^^^^ //│ = 2 :pe :re (@Test) //│ ╔══[PARSE ERROR] Expected start of expression in this position //│ ║ l.136: (@Test) //│ ║ ^ //│ ╟── found a lone annotation instead //│ ║ l.136: (@Test) //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :pe :re print(@Test) //│ ╔══[PARSE ERROR] Expected start of expression in this position //│ ║ l.147: print(@Test) //│ ║ ^ //│ ╟── found a lone annotation instead //│ ║ l.147: print(@Test) //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls ================================================ :js object tailrec tailrec //│ = tailrec :w @tailrec fun fact_n(n, acc) = if n == 0 then acc else fact_n(n - 1, n * acc) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.9: @tailrec fun fact_n(n, acc) = //│ ╙── ^^^^^^^^ :w fun fact(n) = @tailrec fun go(n, acc) = if n == 0 then acc else go(n - 1, n * acc) go(n, 1) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.17: @tailrec fun go(n, acc) = //│ ╙── ^^^^^^^^ data class Freezed(degree: Num) :w @Freezed(-273.15) class AbsoluteZero //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.27: @Freezed(-273.15) class AbsoluteZero //│ ╙── ^^^^^^^^^^^^^^^^^ :w @Freezed(-18) class Beverage(name: Str) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.33: @Freezed(-18) class Beverage(name: Str) //│ ╙── ^^^^^^^^^^^^^ :w @Freezed(-4) let drink = Beverage("Coke") //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.39: @Freezed(-4) let drink = Beverage("Coke") //│ ╙── ^^^^^^^^^^^^ //│ drink = Beverage(_) module Foo with data class Bar(qax: Str) :w @Foo.Bar("baz") class Qux //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.49: @Foo.Bar("baz") class Qux //│ ╙── ^^^^^^^^^^^^^^^ // * Not an annotation, but an applicationo f operator `@`... :pe :re @42 class Qux //│ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead //│ ║ l.57: @42 class Qux //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] TypeError: f is not a function :pe :re @(1 + 2) class Qux //│ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead //│ ║ l.65: @(1 + 2) class Qux //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] TypeError: f is not a function :pe :re @1 module Qux //│ ╔══[PARSE ERROR] Expected end of input; found 'module' keyword instead //│ ║ l.73: @1 module Qux //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] TypeError: f is not a function object inline :w // All functions are annotated with @inline. @inline fun min(x, y) = if x < y then x else y max(x, y) = if x > y then x else y //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.83: @inline //│ ╙── ^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.83: @inline //│ ╙── ^^^^^^^ :w @inline let abs(x) = if x < 0 then -x else x clamp(x, lo, hi) = min(max(x, lo), hi) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.95: @inline let //│ ╙── ^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.95: @inline let //│ ╙── ^^^^^^^ //│ abs = fun abs //│ clamp = fun clamp :w // Only the first variable is annotated with @inline. let @inline success = 0 failure = 1 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.110: @inline success = 0 //│ ╙── ^^^^^^^ //│ failure = 1 //│ success = 0 // Multiple annotations // ==================== :w @inline @tailrec fun fib(n) = if n < 2 then n else fib(n - 1) + fib(n - 2) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.122: @inline @tailrec fun fib(n) = //│ ╙── ^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.122: @inline @tailrec fun fib(n) = //│ ╙── ^^^^^^^^ object internal :w @internal object shared thread_local transient volatile //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.134: @internal object //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.134: @internal object //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.134: @internal object //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.134: @internal object //│ ╙── ^^^^^^^^^ :w @volatile let @shared counter = 0 @transient @thread_local cache = () @volatile @transient empty = "" normal = () //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.153: @volatile let //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.154: @shared counter = 0 //│ ╙── ^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.153: @volatile let //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.155: @transient @thread_local cache = () //│ ╙── ^^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.155: @transient @thread_local cache = () //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.153: @volatile let //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.156: @volatile @transient empty = "" //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.156: @volatile @transient empty = "" //│ ╙── ^^^^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.153: @volatile let //│ ╙── ^^^^^^^^^ //│ cache = () //│ counter = 0 //│ empty = "" //│ normal = () ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/annotations/Pattern.mls ================================================ :js open annotations { compile } class Box() // Now the `@compile` annotation applies to all patterns, while before it could // only be used for pattern symbols. Now it expands and compiles the following // pattern. fun foo(x) = if x is @compile Box then "yes" else "no" object Singleton fun foo(x) = if x is @compile Singleton then "yes" else "no" fun foo(x) = if x is @compile 42 then "yes" else "no" object do_not_compile :w fun foo(x) = if x is @do_not_compile Box then "yes" else "no" //│ ╔══[WARNING] This annotation is not supported here. //│ ║ l.23: fun foo(x) = if x is @do_not_compile Box then "yes" else "no" //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: Patterns only support the `@compile` annotation. //│ ║ l.23: fun foo(x) = if x is @do_not_compile Box then "yes" else "no" //│ ╙── ^^^ :w :e fun foo(x) = if x is @name_not_found Box then "yes" else "no" //│ ╔══[COMPILATION ERROR] Name not found: name_not_found //│ ║ l.33: fun foo(x) = if x is @name_not_found Box then "yes" else "no" //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[WARNING] This annotation is not supported here. //│ ║ l.33: fun foo(x) = if x is @name_not_found Box then "yes" else "no" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: Patterns only support the `@compile` annotation. //│ ║ l.33: fun foo(x) = if x is @name_not_found Box then "yes" else "no" //│ ╙── ^^^ pattern Box' = Box fun foo(x) = if x is @compile Box' then "yes" else "no" :w fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" //│ ╔══[WARNING] This annotation is not supported here. //│ ║ l.49: fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: Patterns only support the `@compile` annotation. //│ ║ l.49: fun foo(x) = if x is @do_not_compile Box' then "yes" else "no" //│ ╙── ^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/syntax/annotations/Unsupported.mls ================================================ :js // The annotations for the following expressions are not supported for now. object debug import "../../../mlscript-compile/StrOps.mls" :w @debug open StrOps //│ ╔══[WARNING] This annotation has no effect //│ ║ l.9: @debug //│ ║ ^^^^^^ //│ ╟── Annotations are not supported on open terms. //│ ║ l.10: open StrOps //│ ╙── ^^^^^^^^^^^ :w @debug 0 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.19: @debug 0 //│ ║ ^^^^^^ //│ ╟── This annotation is not supported on integer literal terms. //│ ║ l.19: @debug 0 //│ ╙── ^ //│ = 0 :w @debug 1 + 2 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.29: @debug 1 + 2 //│ ║ ^^^^^^ //│ ╟── This annotation is not supported on application terms. //│ ║ l.29: @debug 1 + 2 //│ ╙── ^^^^^ //│ = 3 :w (@debug 1 + 2) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.39: (@debug 1 + 2) //│ ║ ^^^^^^ //│ ╟── This annotation is not supported on application terms. //│ ║ l.39: (@debug 1 + 2) //│ ╙── ^^^^^ //│ = 3 :w (@debug 1) + 2 //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.49: (@debug 1) + 2 //│ ║ ^^^^^^ //│ ╟── This annotation is not supported on integer literal terms. //│ ║ l.49: (@debug 1) + 2 //│ ╙── ^ //│ = 3 :w (1 + @debug 2) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.59: (1 + @debug 2) //│ ║ ^^^^^^ //│ ╟── This annotation is not supported on integer literal terms. //│ ║ l.59: (1 + @debug 2) //│ ╙── ^ //│ = 3 data class Log(msg: Str) :w id(@Log 5) //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.71: id(@Log 5) //│ ║ ^^^^ //│ ╟── This annotation is not supported on integer literal terms. //│ ║ l.71: id(@Log 5) //│ ╙── ^ //│ = 5 :pe :re @1 + 2 class Qux //│ ╔══[PARSE ERROR] Expected end of input; found 'class' keyword instead //│ ║ l.82: @1 + 2 class Qux //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] TypeError: f is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/tailrec/Annots.mls ================================================ :js :w @tailrec fun f //│ ═══[WARNING] Only functions with a body may be marked as @tailrec. :w @tailrec class A //│ ═══[WARNING] This annotation has no effect. :w @tailrec 2 + 2 //│ ╔══[WARNING] This annotation has no effect. //│ ╟── This annotation is not supported on application terms. //│ ║ l.14: @tailrec 2 + 2 //│ ╙── ^^^^^ //│ = 4 @tailrec fun f = g fun g = f :w @tailcall fun g = 2 //│ ═══[WARNING] This annotation has no effect. fun test = @tailcall test :w let f = 0 fun test = @tailcall f //│ ╔══[WARNING] This annotation has no effect. //│ ╟── This annotation is not supported on reference terms. //│ ║ l.36: @tailcall f //│ ╙── ^ //│ f = 0 :todo class A with @tailrec fun f() = g() fun g() = f() //│ ╔══[COMPILATION ERROR] Class methods may not yet be marked @tailrec. //│ ║ l.46: fun f() = g() //│ ╙── ^ :w class A with @tailcall fun f = 2 //│ ═══[WARNING] This annotation has no effect. :todo class A module A with @tailrec fun f = 2 //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.62: fun f = 2 //│ ╙── ^ :w class A module A with @tailcall fun f = 2 //│ ═══[WARNING] This annotation has no effect. :w :effectHandlers :lift handle h = Object with @tailcall fun f()(r) = 2 //│ ═══[WARNING] This annotation has no effect. :w fun test = @tailcall 1 + 2 //│ ╔══[WARNING] This annotation has no effect. //│ ╟── The @tailcall annotation has no effect on calls to built-in symbols. //│ ║ l.85: @tailcall 1 + 2 //│ ╙── ^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/tailrec/Errors.mls ================================================ :js :e fun f(x) = @tailcall f(x) f(x) //│ ╔══[COMPILATION ERROR] This call is not in tail position. //│ ║ l.5: @tailcall f(x) //│ ╙── ^^^^ :e fun f(x) = 2 fun g(x) = @tailcall f(x) //│ ╔══[COMPILATION ERROR] This call is not optimized as it does not directly recurse through its parent function. //│ ║ l.13: fun g(x) = @tailcall f(x) //│ ╙── ^^^^ :e @tailrec fun f(x) = g(x) fun g(x) = f(x); f(x) //│ ╔══[COMPILATION ERROR] This function is not tail recursive. //│ ║ l.19: @tailrec fun f(x) = //│ ║ ^ //│ ╟── It could self-recurse through this call, which is not a tail call. //│ ║ l.21: fun g(x) = f(x); f(x) //│ ╙── ^^^^ :e @tailrec fun f(x) = g(x) fun g(x) = f(x) h(x) fun h(x) = g(x) //│ ╔══[COMPILATION ERROR] This function is not tail recursive. //│ ║ l.30: @tailrec fun f(x) = //│ ║ ^ //│ ╟── It could self-recurse through this call, which is not a tail call. //│ ║ l.33: f(x) //│ ╙── ^^^^ :e @tailrec fun f(x) = g(x) fun g(x) = h(x) f(x) fun h(x) = f(x) //│ ╔══[COMPILATION ERROR] This function is not tail recursive. //│ ║ l.45: @tailrec fun f(x) = //│ ║ ^ //│ ╟── It could self-recurse through this call, which is not a tail call. //│ ║ l.48: h(x) //│ ╙── ^^^^ :e module A with fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) @tailcall f(x - 1) //│ ╔══[COMPILATION ERROR] This call is not in tail position. //│ ║ l.63: @tailcall f(x - 1) //│ ╙── ^^^^^^^^ :w @tailrec fun f = 2 //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.71: fun f = 2 //│ ╙── ^ :w module A with @tailrec fun f() = 2 //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.79: fun f() = 2 //│ ╙── ^ :fixme // TODO: support @tailrec fun foo() = Foo.bar() module Foo with fun bar() = foo() //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.86: fun foo() = Foo.bar() //│ ╙── ^^^ :e fun f(x) = @tailrec fun g() = g() g() //│ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. //│ ║ l.96: fun g() = g() //│ ╙── ^ :e fun scan(idx, acc) = @tailrec fun go(idx, tok) = scan(idx, tok) go(idx, acc) //│ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. //│ ║ l.105: fun go(idx, tok) = scan(idx, tok) //│ ╙── ^^ :e :w @tailrec fun scan(idx, acc) = @tailrec fun go(idx, tok) = scan(idx, tok) go(idx, acc) //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.114: fun scan(idx, acc) = //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. //│ ║ l.116: fun go(idx, tok) = scan(idx, tok) //│ ╙── ^^ :e @tailrec @config(tailRecOpt: false) fun foo() = foo() //│ ╔══[COMPILATION ERROR] This @tailrec function was not processed by the tail-call optimizer. //│ ║ l.128: fun foo() = foo() //│ ╙── ^^^ :e fun f = @tailcall id(2) //│ ╔══[COMPILATION ERROR] Only functions in this compilation unit may be marked @tailcall. //│ ║ l.135: @tailcall id(2) //│ ╙── ^^^^^ :e fun f(x)(y) = @tailcall f(x) //│ ╔══[COMPILATION ERROR] Only fully applied calls may be marked @tailcall. //│ ║ l.142: @tailcall f(x) //│ ╙── ^^^^ :e fun f(x)() = @tailcall g(x)(0) fun g(x)(y) = @tailcall f(x) //│ ╔══[COMPILATION ERROR] Only fully applied calls may be marked @tailcall. //│ ║ l.151: @tailcall f(x) //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] This call is not optimized as it does not directly recurse through its parent function. //│ ║ l.149: @tailcall g(x)(0) //│ ╙── ^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/tailrec/MultiArgLists.mls ================================================ :js // Test: Tail-recursive function with single parameter list works correctly :expect 5050 fun sum(n, acc) = if n == 0 then acc else sum(n - 1, n + acc) sum(100, 0) //│ = 5050 // Test: Curried function with multiple parameter lists — properly lowered as // a multi-param-list function, so the self-recursive tail call is recognized // and optimized by TailRecOpt. :expect 500500 :sir @tailrec fun loop(x)(y) = if x == 0 then y else loop(x - 1)(y + x) loop(1000)(0) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let loop⁰, loop$tailrec⁰; //│ define loop$tailrec⁰ as fun loop$tailrec¹(x, y) { //│ loop loopLabel: //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(x, 0); //│ match scrut //│ true => //│ return y //│ else //│ set tmp = -⁰(x, 1); //│ set tmp1 = +⁰(y, x); //│ set x = tmp; //│ set y = tmp1; //│ continue loopLabel //│ end //│ end //│ }; //│ define loop⁰ as fun loop¹(x)(y) { return loop$tailrec¹(x, y) }; //│ loop¹(1000)(0) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 500500 // Test: Function with multiple parameter lists, non-annotated (should still optimize to a loop) :sir :expect 100 fun count(start)(stop) = if start == stop then start else count(start + 1)(stop) count(0)(100) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let count⁰, count$tailrec⁰; //│ define count$tailrec⁰ as fun count$tailrec¹(start, stop) { //│ loop loopLabel: //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(start, stop); //│ match scrut //│ true => //│ return start //│ else //│ set tmp = +⁰(start, 1); //│ set start = tmp; //│ continue loopLabel //│ end //│ end //│ }; //│ define count⁰ as fun count¹(start)(stop) { return count$tailrec¹(start, stop) }; //│ count¹(0)(100) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 100 // Test: Mutually recursive functions with multiple parameter lists :sir fun even(n)(dummy) = if n == 0 then [true, dummy] else odd(n - 1)(dummy + 1) fun odd(n)(dummy) = if n == 0 then [false, dummy] else even(n - 1)(dummy + 1) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let even⁰, odd⁰, even_odd⁰; //│ define even_odd⁰ as fun even_odd¹(id, param0, param1) { //│ loop loopLabel: //│ match id //│ 0 => //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(param0, 0); //│ match scrut //│ true => //│ return [true, param1] //│ else //│ set tmp = -⁰(param0, 1); //│ set tmp1 = +⁰(param1, 1); //│ set param0 = tmp; //│ set param1 = tmp1; //│ set id = 1; //│ continue loopLabel //│ end //│ 1 => //│ let scrut, tmp, tmp1; //│ set scrut = Predef⁰.equals⁰(param0, 0); //│ match scrut //│ true => //│ return [false, param1] //│ else //│ set tmp = -⁰(param0, 1); //│ set tmp1 = +⁰(param1, 1); //│ set param0 = tmp; //│ set param1 = tmp1; //│ set id = 0; //│ continue loopLabel //│ end //│ else //│ end //│ end //│ end //│ }; //│ define even⁰ as fun even¹(n)(dummy) { //│ return even_odd¹(0, n, dummy) //│ }; //│ define odd⁰ as fun odd¹(n)(dummy) { return even_odd¹(1, n, dummy) }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect [true, 10] even(10)(0) //│ = [true, 10] :expect [false, 9] even(9)(0) //│ = [false, 9] // Spreads // This is the same as a test in `TailRecOpt.mls` but with some parameters separated and changed. :sir fun f(n)(a, b, c, d, ...e)(m) = if n > 0 then @tailcall g(n)(m + 1) else [e, m] fun g(n)(m) = @tailcall f(n - 1)(...[1, 2], 3, ...[3, 4], ...[5, n], 7, n)(m) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰, g⁰, f_g⁰; //│ define f_g⁰ as fun f_g¹(id, param0, param1, param2, param3, param4, param5, param6) { //│ loop loopLabel: //│ match id //│ 0 => //│ let scrut, tmp; //│ set scrut = >⁰(param0, 0); //│ match scrut //│ true => //│ set tmp = +⁰(param6, 1); //│ set param1 = tmp; //│ set id = 1; //│ continue loopLabel //│ else //│ return [param5, param6] //│ end //│ 1 => //│ let tmp, tmp1, tmp2, tmp3, param1_tmp, argList, param0_tmp, sliceRes; //│ set tmp = -⁰(param0, 1); //│ set tmp1 = [1, 2]; //│ set tmp2 = [3, 4]; //│ set tmp3 = [5, param0]; //│ set param1_tmp = param1; //│ set param0_tmp = param0; //│ set param0 = tmp; //│ set argList = [...tmp1, 3, ...tmp2, ...tmp3, 7, param0_tmp]; //│ set sliceRes = runtime⁰.Tuple⁰.slice⁰(argList, 4, 0); //│ set param1 = argList.0; //│ set param2 = argList.1; //│ set param3 = argList.2; //│ set param4 = argList.3; //│ set param5 = sliceRes; //│ set param6 = param1_tmp; //│ set id = 0; //│ continue loopLabel //│ else //│ end //│ end //│ end //│ }; //│ define f⁰ as fun f¹(n)(a, b, c, d, ...e)(m) { //│ return f_g¹(0, n, a, b, c, d, e, m) //│ }; //│ define g⁰ as fun g¹(n)(m) { //│ return f_g¹(1, n, m, undefined, undefined, undefined, undefined, undefined) //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect [[4, 5, 1, 7, 1], 9] g(10)(0) //│ = [[4, 5, 1, 7, 1], 9] ================================================ FILE: hkmc2/shared/src/test/mlscript/tailrec/Simple.mls ================================================ :js :sjs @tailrec fun f = f //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; f = function f() { loopLabel: while (true) { continue loopLabel; } }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :fixme module Foo with @tailrec fun bar = bar //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.14: fun bar = bar //│ ╙── ^^^ @tailrec fun f() = f() module Foo with @tailrec fun bar() = bar() fun f(x) = f(x + 1) :sjs fun f(x) = f(f(x) + 1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f3; //│ f3 = function f(x) { //│ loopLabel: while (true) { //│ let tmp, tmp1; //│ tmp = f3(x); //│ tmp1 = tmp + 1; //│ x = tmp1; //│ continue loopLabel; //│ } //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— module A with fun f(x) = @tailcall f(f(x) + 1) :fixme // TODO: support module A with @tailrec fun f(x) = B.g(x + 1) module B with fun g(x) = A.f(x * 2) //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. //│ ║ l.50: fun f(x) = B.g(x + 1) //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls ================================================ :js :expect 200010000 fun sum_impl(n, acc) = if n == 0 then acc else sum_impl(n - 1, n + acc) fun sum(n) = sum_impl(n, 0) sum(20000) //│ = 200010000 :sir :expect 50000 fun g(a, b, c, d) = f(a, b, c + d) fun f(a, b, c) = if a > 0 then g(a - 1, b, c, 1) b > 0 then g(a, b - 1, c, 2) else c f(10000, 20000, 0) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰, g⁰, g_f⁰; //│ define g_f⁰ as fun g_f¹(id, param0, param1, param2, param3) { //│ loop loopLabel: //│ match id //│ 0 => //│ let tmp; //│ set tmp = +⁰(param2, param3); //│ set param2 = tmp; //│ set id = 1; //│ continue loopLabel //│ 1 => //│ let scrut, scrut1, tmp, tmp1; //│ set scrut = >⁰(param0, 0); //│ match scrut //│ true => //│ set tmp = -⁰(param0, 1); //│ set param0 = tmp; //│ set param3 = 1; //│ set id = 0; //│ continue loopLabel //│ else //│ set scrut1 = >⁰(param1, 0); //│ match scrut1 //│ true => //│ set tmp1 = -⁰(param1, 1); //│ set param1 = tmp1; //│ set param3 = 2; //│ set id = 0; //│ continue loopLabel //│ else //│ return param2 //│ end //│ end //│ else //│ end //│ end //│ end //│ }; //│ define g⁰ as fun g¹(a, b, c, d) { //│ return g_f¹(0, a, b, c, d) //│ }; //│ define f⁰ as fun f¹(a, b, c) { return g_f¹(1, a, b, c, undefined) }; //│ f¹(10000, 20000, 0) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 50000 :sjs :expect 200010000 module A with fun sum_impl(n, acc) = if n == 0 then acc else @tailcall sum_impl(n - 1, n + acc) fun sum(n) = sum_impl(n, 0) A.sum(20000) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let A1; //│ (class A { //│ static { //│ A1 = this //│ } //│ static sum_impl(n1, acc) { //│ loopLabel: while (true) { //│ let scrut, tmp, tmp1; //│ scrut = Predef.equals(n1, 0); //│ if (scrut === true) { //│ return acc //│ } //│ tmp = n1 - 1; //│ tmp1 = n1 + acc; //│ n1 = tmp; //│ acc = tmp1; //│ continue loopLabel; //│ } //│ } //│ static sum(n1) { //│ return A.sum_impl(n1, 0) //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); //│ A1.sum(20000) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 200010000 :silent :lift :rewriteWhile :expect 200010000 val x = mut [] let n = 20000 let i = 0 while i < n do x.push of () => i set i += 1 fun sumOf(x, idx, acc) = if idx == 0 then acc else sumOf(x, idx - 1, acc + x![idx]()) sumOf(x, n - 1, 0) // Check that spreads are compiled correctly fun f(x, ...z) = if x < 0 then 0 else g(x, 0, x, x) fun g(x, y, ...z) = if x < 0 then 0 else f(x - 1, 0, ...[x, x, x]) f(100) //│ = 0 fun f(n, x, y, z) = if n > 0 then @tailcall f(n - 1, ...[1, 2], 3) else y :expect 2 f(10, 0, 0, 0) //│ = 2 :sir fun f(n, a, b, c, d, ...e) = if n > 0 then @tailcall g(n - 1) else e fun g(n) = @tailcall f(n, ...[1, 2], 3, ...[3, 4], ...[5, n], 7, n) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f², g², f_g⁰; //│ define f_g⁰ as fun f_g¹(id, param0, param1, param2, param3, param4, param5) { //│ loop loopLabel: //│ match id //│ 0 => //│ let scrut, tmp; //│ set scrut = >⁰(param0, 0); //│ match scrut //│ true => //│ set tmp = -⁰(param0, 1); //│ set param0 = tmp; //│ set id = 1; //│ continue loopLabel //│ else //│ return param5 //│ end //│ 1 => //│ let tmp, tmp1, tmp2, argList, sliceRes; //│ set tmp = [1, 2]; //│ set tmp1 = [3, 4]; //│ set tmp2 = [5, param0]; //│ set argList = [param0, ...tmp, 3, ...tmp1, ...tmp2, 7, param0]; //│ set sliceRes = runtime⁰.Tuple⁰.slice⁰(argList, 5, 0); //│ set param0 = argList.0; //│ set param1 = argList.1; //│ set param2 = argList.2; //│ set param3 = argList.3; //│ set param4 = argList.4; //│ set param5 = sliceRes; //│ set id = 0; //│ continue loopLabel //│ else //│ end //│ end //│ end //│ }; //│ define f² as fun f³(n, a, b, c, d, ...e) { //│ return f_g¹(0, n, a, b, c, d, e) //│ }; //│ define g² as fun g³(n) { //│ return f_g¹(1, n, undefined, undefined, undefined, undefined, undefined) //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect [4, 5, 0, 7, 0] g(10) //│ = [4, 5, 0, 7, 0] :e :lift fun f(x) = fun g() = @tailcall f(x) @tailcall f(x) @tailcall g() //│ ╔══[COMPILATION ERROR] This call is not in tail position. //│ ║ l.195: @tailcall f(x) //│ ╙── ^^^^ :lift fun f(x) = fun g() = @tailcall f(x) @tailcall g() // Ensure y is set using x_tmp and x is assigned to x_tmp :sir fun f(x, y) = if x == 0 then 0 else f(x - 1, x) //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁴; //│ define f⁴ as fun f⁵(x, y) { //│ loop loopLabel: //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(x, 0); //│ match scrut //│ true => //│ return 0 //│ else //│ let x_tmp; //│ set tmp = -⁰(x, 1); //│ set x_tmp = x; //│ set x = tmp; //│ set y = x_tmp; //│ continue loopLabel //│ end //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // Make sure variables accessed by nested functions or classes are re-defined in a new scope in the while loop :expect 2 let lam = () => 0 fun foo(x) = assert x < 5 else x if x == 2 do set lam = () => x foo(x + 1) foo(0) lam() //│ = 2 //│ lam = fun // Mutrec case :expect 2 let lam = () => 0 fun foo1(x) = assert x < 5 else x if x == 2 do set lam = () => x foo2(x + 1) fun foo2(x) = assert x < 5 else x if x == 2 do set lam = () => x foo1(x + 1) foo1(0) lam() //│ = 2 //│ lam = fun // Classes :expect 2 let a fun foo(x) = data class A(x) assert x < 5 else x if x == 2 do set a = A(x) foo(x + 1) foo(0) a.x //│ = 2 //│ a = A(2) // Functions inside module definitions are lifted to the top level. This means they cannot yet be optimized. :lift :fixme module A with fun f(x) = fun g(x) = if x < 0 then 0 else @tailcall f(x) @tailcall g(x - 1) A.f(10000) //│ ╔══[COMPILATION ERROR] This tail call exits the current scope and is not optimized. //│ ║ l.283: fun g(x) = if x < 0 then 0 else @tailcall f(x) //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] This tail call exits the current scope and is not optimized. //│ ║ l.284: @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded // These calls are represented as field selections and don't yet have the explicitTailCall parameter. :breakme :e module A with fun f = g fun g = @tailcall f f :expect 0 module A with fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) A.f(10) //│ = 0 :todo :expect 0 class A with @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) (new A).f(10) //│ ╔══[COMPILATION ERROR] Calls from class methods cannot yet be marked @tailcall. //│ ║ l.313: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Class methods may not yet be marked @tailrec. //│ ║ l.313: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Calls from class methods cannot yet be marked @tailcall. //│ ║ l.314: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) //│ ╙── ^^^^^^^^ //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/AndNewline.mls ================================================ :js // Bug fix: `and` on its own line in `if` condition should work if true and true do print("ok") //│ > ok // `and` at end of line (always worked): if true and true do print("ok2") //│ > ok2 // Same-line version (always worked): if true and true do print("ok3") //│ > ok3 // false-and case if true and false do print("should not print") ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/BinarySearchTree.mls ================================================ :js import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/Stack.mls" open Option { Some, None } open Stack fun (~~>) toBe(x, y) = if x === y then () else throw Error(String(x) + " is not " + String(y)) fun (?) max(x, y) = if x > y then x else y fun abs(x) = if x < 0 then -x else x fun (??) getOrElse = Option.getOrElse abstract class Tree[out A] data class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] object Empty extends Tree fun single(v) = Node(v, Empty, Empty) fun show[A](t: Tree[A]): Str = if t is Node(v, l, r) then "(" + show(l) + " " + String(v) + " " + show(r) + ")" Empty then "•" show(Empty) show(Node(0, Empty, Empty)) show(Node(1, Node(0, Empty, Empty), Empty)) show(Node(1, Node(0, Empty, Empty), Node(2, Empty, Empty))) //│ = "((• 0 •) 1 (• 2 •))" fun insert(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) v > v' then Node(v', l, insert(r, v)) _ then t Empty then Node(v, Empty, Empty) fun insert'(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) > v' then Node(v', l, insert(r, v)) else t Empty then Node(v, Empty, Empty) insert(Empty, 0) |> show //│ = "(• 0 •)" insert'(Empty, 0) |> show //│ = "(• 0 •)" insert(Node(0, Empty, Empty), 0) |> show //│ = "(• 0 •)" insert'(Node(0, Empty, Empty), 0) |> show //│ = "(• 0 •)" insert(Node(1, Empty, Empty), 0) |> show //│ = "((• 0 •) 1 •)" insert'(Node(1, Empty, Empty), 0) |> show //│ = "((• 0 •) 1 •)" insert(Node(1, Node(0, Empty, Empty), Empty), 0) |> show //│ = "((• 0 •) 1 •)" insert'(Node(1, Node(0, Empty, Empty), Empty), 0) |> show //│ = "((• 0 •) 1 •)" insert(Node(1, Node(0, Empty, Empty), Empty), 2) |> show //│ = "((• 0 •) 1 (• 2 •))" insert'(Node(1, Node(0, Empty, Empty), Empty), 2) |> show //│ = "((• 0 •) 1 (• 2 •))" fun fromList(l) = fun fromList'(t, xs) = if xs is Cons(x, xs') then fromList'(insert(t, x), xs') Nil then t fromList'(Empty, l) fromList(1 :: 2 :: 3 :: 4 :: Nil) |> show //│ = "(• 1 (• 2 (• 3 (• 4 •))))" fromList(2 :: 1 :: 4 :: 3 :: Nil) |> show //│ = "((• 1 •) 2 ((• 3 •) 4 •))" fromList(4 :: 3 :: 2 :: 1 :: Nil) |> show //│ = "((((• 1 •) 2 •) 3 •) 4 •)" let example1 = fromList(1 :: 3 :: 2 :: 4 :: Nil) //│ example1 = Node(1, Empty, Node(3, Node(2, Empty, Empty), Node(4, Empty, Empty))) example1 |> show //│ = "(• 1 ((• 2 •) 3 (• 4 •)))" fun contains(t, v) = if t is Node(v', l, r) and v < v' then contains(l, v) v > v' then contains(r, v) _ then true Empty then false // Writing tests like this is very interesting. contains(Empty, 0) ~~> false contains(Node(0, Empty, Empty), 0) ~~> true contains(Node(1, Empty, Empty), 0) ~~> false fun minValue(t) = if t is Empty then None Node(v, Empty, _) then Some(v) Node(_, l, _) then minValue(l) minValue(Empty) ?? "not found" //│ = "not found" minValue(Node(0, Empty, Empty)) ?? "not found" //│ = 0 minValue(example1) ?? "not found" //│ = 1 fun maxValue(t) = if t is Empty then None Node(v, _, Empty) then Some(v) Node(_, _, r) then maxValue(r) maxValue(Empty) ?? "not found" //│ = "not found" maxValue(Node(0, Empty, Empty)) ?? "not found" //│ = 0 maxValue(example1) ?? "not found" //│ = 4 fun lowerBound(t, v) = if t is Node(v', l, r) and v < v' then lowerBound(l, v) v > v' then Some(lowerBound(r, v) ?? v') else Some(v') Empty then None lowerBound(Empty, 0) ?? "not found" //│ = "not found" lowerBound(Node(0, Empty, Empty), 0) ?? "not found" //│ = 0 lowerBound(Node(1, Empty, Empty), 0) ?? "not found" //│ = "not found" lowerBound(Node(-1, Empty, Empty), 0) ?? "not found" //│ = -1 lowerBound(example1, 0) ?? "not found" //│ = "not found" lowerBound(example1, 1) ?? "not found" //│ = 1 lowerBound(example1, 2) ?? "not found" //│ = 2 lowerBound(example1, 3) ?? "not found" //│ = 3 lowerBound(example1, 4) ?? "not found" //│ = 4 lowerBound(example1, 5) ?? "not found" //│ = 4 let example2 = fromList(1 :: 5 :: 42 :: 10 :: 23 :: 59 :: 81 :: Nil) //│ example2 = Node( //│ 1, //│ Empty, //│ Node( //│ 5, //│ Empty, //│ Node( //│ 42, //│ Node(10, Empty, Node(23, Empty, Empty)), //│ Node(59, Empty, Node(81, Empty, Empty)) //│ ) //│ ) //│ ) lowerBound(example2, 0) ?? "not found" //│ = "not found" lowerBound(example2, 25) ?? "not found" //│ = 23 lowerBound(example2, 99) ?? "not found" //│ = 81 lowerBound(example2, 7) ?? "not found" //│ = 5 lowerBound(example2, 32) ?? "not found" //│ = 23 lowerBound(example2, 41) ?? "not found" //│ = 23 fun upperBound(t, v) = if t is Node(v', l, r) and v < v' then Some(upperBound(l, v) ?? v') v > v' then upperBound(r, v) _ then Some(v') Empty then None upperBound(example2, 0) ?? "not found" //│ = 1 upperBound(example2, 25) ?? "not found" //│ = 42 upperBound(example2, 99) ?? "not found" //│ = "not found" upperBound(example2, 7) ?? "not found" //│ = 10 upperBound(example2, 32) ?? "not found" //│ = 42 upperBound(example2, 41) ?? "not found" //│ = 42 fun remove(t, v) = if t is Node(v', l, r) and v < v' then Node(v', remove(l, v), r) v > v' then Node(v', l, remove(r, v)) minValue(r) is None then l Some(v'') then Node(v'', l, remove(r, v'')) Empty then Empty remove(Empty, 0) |> show //│ = "•" remove(Node(0, Empty, Empty), 0) |> show //│ = "•" remove(Node(1, Empty, Empty), 0) |> show //│ = "(• 1 •)" remove(Node(1, Node(0, Empty, Empty), Empty), 0) |> show //│ = "(• 1 •)" remove(Node(1, Empty, Node(2, Empty, Empty)), 2) |> show //│ = "(• 1 •)" remove(Node(1, Node(0, Empty, Empty), Node(2, Empty, Empty)), 1) |> show //│ = "((• 0 •) 2 •)" example1 |> show //│ = "(• 1 ((• 2 •) 3 (• 4 •)))" remove(example1, 0) |> show //│ = "(• 1 ((• 2 •) 3 (• 4 •)))" remove(example1, 1) |> show //│ = "(• 2 (• 3 (• 4 •)))" remove(example1, 2) |> show //│ = "(• 1 (• 3 (• 4 •)))" remove(example1, 3) |> show //│ = "(• 1 ((• 2 •) 4 •))" remove(example1, 4) |> show //│ = "(• 1 ((• 2 •) 3 •))" fun extractMin(t) = if t is Node(v, Empty, r) then [Some(v), r] Node(v, l, r) and extractMin(l) is [m, l'] then [m, Node(v, l', r)] Empty then [None, Empty] extractMin(example1).0 ?? "not found" //│ = 1 extractMin(example1).1 |> show //│ = "((• 2 •) 3 (• 4 •))" fun merge(l, r) = if extractMin(r) is [None, _] then l [Some(m), r'] then Node(m, l, r') merge(Empty, Empty) |> show //│ = "•" merge(Empty, Node(0, Empty, Empty)) |> show //│ = "(• 0 •)" merge(Node(0, Empty, Empty), Empty) |> show //│ = "(• 0 •)" merge(Node(0, Empty, Empty), Node(1, Empty, Empty)) |> show //│ = "((• 0 •) 1 •)" merge(Node(0, Empty, Empty), Node(2, Node(1, Empty, Empty), Empty)) |> show //│ = "((• 0 •) 1 (• 2 •))" fun removeGte(t, v) = if t is Node(v', l, r) and v < v' then removeGte(l, v) v > v' then Node(v', l, removeGte(r, v)) _ then l // lucky case Empty then Empty removeGte(Empty, 0) |> show //│ = "•" removeGte(example1, 0) |> show //│ = "•" removeGte(example1, 1) |> show //│ = "•" removeGte(example1, 2) |> show //│ = "(• 1 •)" removeGte(example1, 3) |> show //│ = "(• 1 (• 2 •))" removeGte(example1, 4) |> show //│ = "(• 1 ((• 2 •) 3 •))" removeGte(example1, 5) |> show //│ = "(• 1 ((• 2 •) 3 (• 4 •)))" example2 |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))" removeGte(example2, 10) |> show //│ = "(• 1 (• 5 •))" removeGte(example2, 22) |> show //│ = "(• 1 (• 5 (• 10 •)))" removeGte(example2, 23) |> show //│ = "(• 1 (• 5 (• 10 •)))" removeGte(example2, 24) |> show //│ = "(• 1 (• 5 (• 10 (• 23 •))))" removeGte(example2, 70) |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 •))))" removeGte(example2, 99) |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))" fun removeLt(t, v) = if t is Node(v', l, r) and v' < v then removeLt(r, v) else Node(v', removeLt(l, v), r) Empty then Empty example2 |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))" removeLt(example2, 10) |> show //│ = "((• 10 (• 23 •)) 42 (• 59 (• 81 •)))" removeLt(example2, 22) |> show //│ = "((• 23 •) 42 (• 59 (• 81 •)))" removeLt(example2, 23) |> show //│ = "((• 23 •) 42 (• 59 (• 81 •)))" removeLt(example2, 24) |> show //│ = "(• 42 (• 59 (• 81 •)))" removeLt(example2, 70) |> show //│ = "(• 81 •)" removeLt(example2, 99) |> show //│ = "•" // Remove elements from `begin` until `end`. fun removeRange(t, begin, end) = if t is Node(v, l, r) and begin > v then Node(v, l, removeRange(r, begin, end)) end <= v then Node(v, removeRange(l, begin, end), r) _ then merge(removeGte(l, begin), removeLt(r, end)) Empty then Empty example2 |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))" removeRange(example2, -10000, 10000) |> show //│ = "•" removeRange(example2, 1, 82) |> show //│ = "•" removeRange(example2, 1, 50) |> show //│ = "(• 59 (• 81 •))" removeRange(example2, 50, 81) |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 81 •))))" removeRange(example2, 20, 60) |> show //│ = "(• 1 (• 5 ((• 10 •) 81 •)))" removeRange(example2, 20, 24) |> show //│ = "(• 1 (• 5 ((• 10 •) 42 (• 59 (• 81 •)))))" removeRange(example2, 59, 60) |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 81 •))))" removeRange(example2, 59, 59) |> show //│ = "(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))" fun size(t) = if t is Node(_, l, r) then 1 + size(l) + size(r) Empty then 0 size(Empty) ~~> 0 size(Node(0, Empty, Empty)) ~~> 1 size(example1) ~~> 4 size(example2) ~~> 7 fun inverse(t) = if t is Node(v, l, r) then Node(v, inverse(r), inverse(l)) Empty then Empty inverse(Empty) |> show //│ = "•" inverse(Node(0, Empty, Empty)) |> show //│ = "(• 0 •)" inverse(example1) |> show //│ = "(((• 4 •) 3 (• 2 •)) 1 •)" inverse(example2) |> show //│ = "(((((• 81 •) 59 •) 42 ((• 23 •) 10 •)) 5 •) 1 •)" fun height(t) = if t is Node(_, l, r) then 1 + max(height(l), height(r)) Empty then 0 height(Empty) ~~> 0 height(Node(0, Empty, Empty)) ~~> 1 height(example1) ~~> 3 height(example2) ~~> 5 fun isBalanced(t) = if t is Empty then true Node(_, l, r) and height(l) is hl and height(r) is hr then abs(hl - hr) <= 1 && isBalanced(l) && isBalanced(r) isBalanced(Empty) ~~> true isBalanced(Node(0, Empty, Empty)) ~~> true isBalanced(example1) ~~> false isBalanced(example2) ~~> false isBalanced(Node(1, single(-1), single(3))) ~~> true isBalanced(Node(1, single(-1), Node(3, single(2), Empty))) ~~> true isBalanced(Node(1, single(-1), Node(3, Empty, single(4)))) ~~> true ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/EitherOrBoth.mls ================================================ :js import "../../../mlscript-compile/StrOps.mls" import "../../../mlscript-compile/Option.mls" open Option open StrOps abstract class EitherOrBoth[out A, out B]: (Left[A, B] | Right[A, B] | Both[A, B]) data class Left[out A, out B](value: A) extends EitherOrBoth[A, B] data class Right[out A, out B](value: B) extends EitherOrBoth[A, B] data class Both[out A, out B](left: A, right: B) extends EitherOrBoth[A, B] type Either[A, B] = Left[A, B] | Right[A, B] fun getLeft[A, B](eob: EitherOrBoth[A, B]): Option[A] = if eob is Left(left) then Some(left) Right(_) then None Both(left, _) then Some(left) fun getRight[A, B](eob: EitherOrBoth[A, B]): Option[B] = if eob is Left(_) then None Right(right) then Some(right) Both(_, right) then Some(right) fun getBoth[A, B](eob: EitherOrBoth[A, B]): Option[[A, B]] = if eob is Left(_) then None Right(_) then None Both(left, right) then Some([left, right]) fun mapLeft[A, B, C](eob: EitherOrBoth[A, B], f: A -> C): EitherOrBoth[C, B] = if eob is Left(left) then Left(f(left)) Right(right) then Right(right) Both(left, right) then Both(f(left), right) fun mapRight[A, B, C](eob: EitherOrBoth[A, B], f: B -> C): EitherOrBoth[A, C] = if eob is Left(left) then Left(left) Right(right) then Right(f(right)) Both(left, right) then Both(left, f(right)) fun map[A, B, C, D](eob: EitherOrBoth[A, B], f: A -> C, g: B -> D): EitherOrBoth[C, D] = if eob is Left(left) then Left(f(left)) Right(right) then Right(g(right)) Both(left, right) then Both(f(left), g(right)) fun fold[A, B, C](eob: EitherOrBoth[A, B], f: A -> C, g: B -> C, h: [A, B] -> C): C = if eob is Left(left) then f(left) Right(right) then g(right) Both(left, right) then h(left, right) fun isLeft[A, B](eob: EitherOrBoth[A, B]): Bool = if eob is Left(_) then true Right(_) then false Both(_, _) then false fun isRight[A, B](eob: EitherOrBoth[A, B]): Bool = if eob is Left(_) then false Right(_) then true Both(_, _) then false fun isBoth[A, B](eob: EitherOrBoth[A, B]): Bool = if eob is Left(_) then false Right(_) then false Both(_, _) then true fun eobToString[A, B](eob: EitherOrBoth[A, B]): Str = if eob is Left(left) then "Left(" ~ StrOps.from(left) ~ ")" Right(right) then "Right(" ~ StrOps.from(right) ~ ")" Both(left, right) then "Both(" ~ StrOps.from(left) ~ ", " ~ StrOps.from(right) ~ ")" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/LeftistTree.mls ================================================ :js import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/Stack.mls" open Option { Some, None } open Stack fun (??) getOrElse = Option.getOrElse abstract class Tree[out A]: (Empty | Node[A]) data class Node[out A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree[A] object Empty extends Tree[Nothing] fun show(t: Tree[Any]): Str = if t is Node(v, l, r, _) then "(" + show(l) + " " + String(v) + " " + show(r) + ")" Empty then "•" fun singleton(x) = Node(x, Empty, Empty, 1) fun rank(t) = if t is Empty then 0 Node(_, _, _, r) then r fun merge(t1: Tree[Num], t2: Tree[Num]): Tree[Num] = if t1 is Empty then t2 t2 is Empty then t1 t1 is Node(v1, l1, r1, _) and t2 is Node(v2, _, _, _) and v1 <= v2 and let merged = merge(r1, t2) let rank_left = rank of l1 let rank_right = rank of r1 rank_left >= rank_right then Node(v1, l1, merged, rank_right + 1) else Node(v1, merged, l1, rank_left + 1) else merge(t2, t1) fun insert(t, v) = merge(t, singleton(v)) fun getMin(t) = if t is Empty then None Node(x, _, _, _) then Some(x) fun deleteMin(t) = if t is Empty then Empty Node(_, l, r, _) then merge(l, r) fun fromList(t, xs) = if xs is Cons(x, xs') then fromList(insert(t, x), xs') Nil then t let tree1 = fromList(Empty, 3 :: 4 :: 1 :: 2 :: Nil) //│ tree1 = Node( //│ 1, //│ Node(2, Empty, Node(3, Empty, Node(4, Empty, Empty, 1), 1), 1), //│ Empty, //│ 1 //│ ) tree1 |> show //│ = "((• 2 (• 3 (• 4 •))) 1 •)" :expect 1 // Remove the smallest element. It should be 1. getMin(tree1) ?? "Nothing" //│ = 1 let tree1' = deleteMin(tree1) //│ tree1' = Node(2, Empty, Node(3, Empty, Node(4, Empty, Empty, 1), 1), 1) tree1' |> show //│ = "(• 2 (• 3 (• 4 •)))" :expect 2 // Remove one more element. It should be 2. getMin(tree1') ?? "Nothing" //│ = 2 let tree1'' = deleteMin(tree1') //│ tree1'' = Node(3, Empty, Node(4, Empty, Empty, 1), 1) tree1'' |> show //│ = "(• 3 (• 4 •))" :expect 3 // Remove one more element. It should be 3. getMin(tree1'') ?? "Nothing" //│ = 3 let tree1''' = deleteMin(tree1'') //│ tree1''' = Node(4, Empty, Empty, 1) tree1''' |> show //│ = "(• 4 •)" :expect 4 // Remove the last element. It should be 4. getMin(tree1''') ?? "Nothing" //│ = 4 let tree1'''' = deleteMin(tree1''') //│ tree1'''' = Empty tree1'''' |> show //│ = "•" fun drain(t) = if getMin(t) is None then Nil Some(x) then x :: drain(deleteMin(t)) fun sorted(xs) = fromList(Empty, xs) |> drain fun showList(xs) = if xs is Cons(x, Nil) then String(x) Cons(x, xs') then String(x) + ", " + showList(xs') Nil then "" sorted(3 :: 4 :: 1 :: 2 :: Nil) |> showList //│ = "1, 2, 3, 4" sorted(42 :: 58 :: 19 :: 37 :: 44 :: 99 :: 68 :: 60 :: 77 :: 61 :: Nil) |> showList //│ = "19, 37, 42, 44, 58, 60, 61, 68, 77, 99" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/ListFold.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/StrOps.mls" open Stack open Option open StrOps fun (|>) pipe(x, f) = f(x) fun findFirst(f, xs) = if xs is Nil then None Cons(x, _) and f(x) then Some(x) Cons(_, xs) then findFirst(f, xs) fun (::) cons(head, tail) = Cons(head, tail) let oneTwoThree = 1 :: 2 :: 3 :: Nil //│ oneTwoThree = Cons(1, Cons(2, Cons(3, Nil))) // Note that JavaScript doesn't have tail call optimization. Therefore, this // implementation is still inefficient in practice. fun join(sep) = fun aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ~ sep ~ StrOps.from(x), xs') (xs) => if xs is Cons(x, xs') then aux(StrOps.from(x), xs') Nil then "" join(", ")(1 :: 2 :: 3 :: Nil) (1 :: 2 :: 3 :: Nil) |> join(", ") //│ = "1, 2, 3" fun showList(xs) = "[" ~ join(", ")(xs) ~ "]" fun (:::) appendAll(xs, ys) = if xs is Nil then ys Cons(x, xs') then x :: (xs' ::: ys) ((1 :: 2 :: 3 :: Nil) ::: (4 :: 5 :: 6 :: Nil)) |> showList //│ = "[1, 2, 3, 4, 5, 6]" fun reverse(xs) = fun aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) (1 :: 2 :: 3 :: Nil) |> showList reverse(1 :: 2 :: 3 :: Nil) |> showList //│ = "[3, 2, 1]" // __ _ _ _ __ _ // / _| ___ | | __| | | ___ / _| |_ // | |_ / _ \| |/ _` | | / _ \ |_| __| // | _| (_) | | (_| | |__| __/ _| |_ // |_| \___/|_|\__,_|_____\___|_| \__| // fun foldLeft(f)(z) = fun aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(f(acc, x), xs') (xs) => aux(z, xs) let sum = foldLeft((acc, x) => acc + x)(0) //│ sum = fun :expect 0 sum(Nil) //│ = 0 :expect 6 sum(1 :: 2 :: 3 :: Nil) //│ = 6 let product = foldLeft((acc, x) => acc * x)(1) //│ product = fun :expect 1 product(Nil) //│ = 1 :expect 6 product(1 :: 2 :: 3 :: Nil) //│ = 6 let length = foldLeft((acc, _) => acc + 1)(0) //│ length = fun :expect 0 length(Nil) //│ = 0 :expect 3 length(1 :: 2 :: 3 :: Nil) //│ = 3 let reverse' = foldLeft((acc, x) => x :: acc)(Nil) //│ reverse' = fun reverse'(Nil) //│ = Nil reverse'(1 :: 2 :: 3 :: Nil) |> showList //│ = "[3, 2, 1]" // __ _ _ ____ _ _ _ // / _| ___ | | __| | _ \(_) __ _| |__ | |_ // | |_ / _ \| |/ _` | |_) | |/ _` | '_ \| __| // | _| (_) | | (_| | _ <| | (_| | | | | |_ // |_| \___/|_|\__,_|_| \_\_|\__, |_| |_|\__| // |___/ fun foldRight(f)(z) = fun aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then f(x, aux(acc, xs')) (xs) => aux(z, xs) let double = foldRight((x, acc) => x :: x :: acc)(Nil) //│ double = fun double(Nil) |> showList //│ = "[]" double(1 :: 2 :: 3 :: Nil) |> showList //│ = "[1, 1, 2, 2, 3, 3]" let flatten = foldRight((xs, acc) => xs ::: acc)(Nil) //│ flatten = fun flatten(Nil) |> showList //│ = "[]" flatten(oneTwoThree :: oneTwoThree :: oneTwoThree :: Nil) |> showList //│ = "[1, 2, 3, 1, 2, 3, 1, 2, 3]" fun id(x) = x fun foldLeft'[A, B](f: (A, B) -> A)(z: A) = let g(x, y)(z) = y(f(z, x)) (xs) => foldRight(g)(id)(xs)(z) let minus = foldLeft'((acc, x) => acc - x)(0) //│ minus = fun minus(Nil) //│ = 0 minus(1 :: 2 :: 3 :: Nil) //│ = -6 let reverse'' = foldLeft'((acc, x) => x :: acc)(Nil) //│ reverse'' = fun reverse(Nil) //│ = Nil reverse(1 :: 2 :: 3 :: Nil) |> showList //│ = "[3, 2, 1]" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/Permutations.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/StrOps.mls" open Stack fun showList(xs) = "[" + xs Iter.fromStack() Iter.joined(", ") + "]" fun foldLeft(f)(z) = fun aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(f(acc, x), xs') (xs) => aux(z, xs) fun showLists(xs) = print of "[\n" + xs Iter.fromStack() Iter.mapping(showList) Iter.mapping(s => " " + s) Iter.joined(",\n") + "\n]" fun (~>..) insertAllPositions(z, xs) = fun go(prev, lists, next) = if next is Nil then ((prev :+ z) :: lists) |> reverse head :: tail then let nu = ((prev :+ z) ::: next) go(prev :+ head, nu :: lists, tail) go(Nil, Nil, xs) 0 ~>.. (1 :: 2 :: 3 :: Nil) |> showLists //│ > [ //│ > [0, 1, 2, 3], //│ > [1, 0, 2, 3], //│ > [1, 2, 0, 3], //│ > [1, 2, 3, 0] //│ > ] fun permutations(xs) = if xs is Nil then Nil x :: Nil then (x :: Nil) :: Nil x :: xs' then permutations(xs') Iter.fromStack() Iter.folded of Nil, (acc, ys) => acc ::: (x ~>.. ys) permutations(Nil) |> showLists //│ > [ //│ > //│ > ] permutations(1 :: Nil) |> showLists //│ > [ //│ > [1] //│ > ] permutations(1 :: 2 :: Nil) |> showLists //│ > [ //│ > [1, 2], //│ > [2, 1] //│ > ] permutations(1 :: 2 :: 3 :: Nil) |> showLists //│ > [ //│ > [1, 2, 3], //│ > [2, 1, 3], //│ > [2, 3, 1], //│ > [1, 3, 2], //│ > [3, 1, 2], //│ > [3, 2, 1] //│ > ] fun permutations'(xs) = if xs is Nil then Nil Cons(x, Nil) then (x :: Nil) :: Nil else xs Iter.fromStack() Iter.folded of Nil, (acc, x) => acc ::: permutations'(xs filter of (y) => x != y) Iter.fromStack() Iter.mapping((ys) => x :: ys) Iter.toStack() permutations'(Nil) |> showLists //│ > [ //│ > //│ > ] permutations'(1 :: Nil) |> showLists //│ > [ //│ > [1] //│ > ] permutations'(1 :: 2 :: Nil) |> showLists //│ > [ //│ > [1, 2], //│ > [2, 1] //│ > ] permutations'(1 :: 2 :: 3 :: Nil) |> showLists //│ > [ //│ > [1, 2, 3], //│ > [1, 3, 2], //│ > [2, 1, 3], //│ > [2, 3, 1], //│ > [3, 1, 2], //│ > [3, 2, 1] //│ > ] permutations'(1 :: 2 :: 3 :: 4 :: Nil) |> showLists //│ > [ //│ > [1, 2, 3, 4], //│ > [1, 2, 4, 3], //│ > [1, 3, 2, 4], //│ > [1, 3, 4, 2], //│ > [1, 4, 2, 3], //│ > [1, 4, 3, 2], //│ > [2, 1, 3, 4], //│ > [2, 1, 4, 3], //│ > [2, 3, 1, 4], //│ > [2, 3, 4, 1], //│ > [2, 4, 1, 3], //│ > [2, 4, 3, 1], //│ > [3, 1, 2, 4], //│ > [3, 1, 4, 2], //│ > [3, 2, 1, 4], //│ > [3, 2, 4, 1], //│ > [3, 4, 1, 2], //│ > [3, 4, 2, 1], //│ > [4, 1, 2, 3], //│ > [4, 1, 3, 2], //│ > [4, 2, 1, 3], //│ > [4, 2, 3, 1], //│ > [4, 3, 1, 2], //│ > [4, 3, 2, 1] //│ > ] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/SimpleTree.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option { Some, None } type Tree[out A] = Node[A] | Empty object Empty data class Node[out A](value: A, left: Tree[A], right: Tree[A]) fun find(t, v) = if t is Node(v', l, r) and v < v' then find(l, v) v > v' then find(r, v) _ then Some(v) Empty then None fun insert(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) v > v' then Node(v', l, insert(r, v)) _ then t Empty then Node(v, Empty, Empty) find(Empty, 0) //│ = None find(Node(0, Empty, Empty), 0) //│ = Some(0) find(Node(1, Empty, Empty), 0) //│ = None ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/examples/ULC.mls ================================================ :js import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" open Option { Some, None } open Stack fun par(a) = "(" + a + ")" fun showList(xs) = "[" + xs Iter.fromStack() Iter.joined(", ") + "]" fun contains(xs, x) = xs Iter.fromStack() Iter.some of _ === x contains("x" :: "y" :: "z" :: Nil, "y") //│ = true contains("x" :: "y" :: "z" :: Nil, "w") //│ = false fun exclude(xs, x) = if xs is Nil then Nil Cons(x', xs') and x === x' then exclude(xs', x) else Cons(x', exclude(xs', x)) exclude("x" :: "y" :: "z" :: Nil, "y") |> showList //│ = "[x, z]" exclude("x" :: "y" :: "z" :: Nil, "w") |> showList //│ = "[x, y, z]" // _____ // |_ _|___ _ __ _ __ ___ // | | / _ \| '__|| '_ ` _ \ // | || __/| | | | | | | | // |_| \___||_| |_| |_| |_| // type Term = Var | Abs | App data class Var(name: Str) data class Abs(lhs: Var, rhs: Term) data class App(lhs: Term, rhs: Term) fun showTerm(t) = if t is Var(name) then String(name) Abs(lhs, rhs) then "λ" + showTerm(lhs) + ". " + showTerm(rhs) App(Abs(lhs0, lhs1), rhs) then "((" + "λ" + showTerm(lhs0) + ". " + showTerm(lhs1) + ") " + showTerm(rhs) + ")" App(lhs, rhs) then par(showTerm(lhs) + " " + showTerm(rhs)) showTerm(Var("x")) //│ = "x" showTerm(Abs(Var("x"), Var("y"))) //│ = "λx. y" showTerm(App(Var("x"), Var("y"))) //│ = "(x y)" showTerm(App(Abs(Var("x"), Var("y")), Var("z"))) //│ = "((λx. y) z)" fun (=:=) equalTerm(t1: Term, t2: Term) = if t1 is Var(x1) and t2 is Var(x2) then x1 === x2 Abs(x1, t1') and t2 is Abs(x2, t2') then (x1 =:= x2) && (t1' =:= t2') App(t1', t1'') and t2 is App(t2', t2'') then (t1' =:= t2') && (t1'' =:= t2'') else false Var("x") =:= Var("x") Var("x") =:= Var("y") Abs(Var("x"), Var("x")) =:= Abs(Var("x"), Var("x")) Abs(Var("x"), Var("x")) =:= Abs(Var("x"), Var("y")) Abs(Var("x"), Var("y")) =:= Abs(Var("x"), Var("x")) //│ = false fun isValue(t) = if t is Abs then true Var then false App then false isValue(Var("x")) isValue(Abs(Var("x"), Var("y"))) isValue(App(Var("x"), Var("y"))) //│ = false fun hasFree(t, x) = if t is Var(x') then x === x' Abs(Var(x'), body) and x === x' then false Abs(Var(_), body) then hasFree(body, x) App(lhs, rhs) then hasFree(lhs, x) || hasFree(rhs, x) _ then false fun showHasFree(t, n) = showTerm(t) + (if hasFree(t, n) then " has " else " DOES NOT have ") + "free variable " + n showHasFree(Var("x"), "x") //│ = "x has free variable x" showHasFree(Var("x"), "y") //│ = "x DOES NOT have free variable y" showHasFree(Abs(Var("x"), Var("x")), "x") //│ = "λx. x DOES NOT have free variable x" showHasFree(Abs(Var("x"), Var("x")), "y") //│ = "λx. x DOES NOT have free variable y" showHasFree(Abs(Var("x"), Var("y")), "x") //│ = "λx. y DOES NOT have free variable x" showHasFree(Abs(Var("x"), Var("y")), "y") //│ = "λx. y has free variable y" showHasFree(App(Var("x"), Var("y")), "x") //│ = "(x y) has free variable x" showHasFree(App(Var("x"), Var("y")), "y") //│ = "(x y) has free variable y" showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "x") //│ = "((λx. x) x) has free variable x" showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") //│ = "((λx. x) x) DOES NOT have free variable y" showHasFree(App(Abs(Var("x"), Var("x")), Var("y")), "y") //│ = "((λx. x) y) has free variable y" showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") //│ = "((λx. x) x) DOES NOT have free variable y" fun freeVars(t) = if t is Var(x) then x :: Nil Abs(Var(x), body) then exclude(freeVars(body), x) App(lhs, rhs) then freeVars(lhs) ::: freeVars(rhs) (freeVars of Var("x")) |> showList //│ = "[x]" (freeVars of Abs(Var("x"), Var("x"))) |> showList //│ = "[]" (freeVars of Abs(Var("x"), Var("y"))) |> showList //│ = "[y]" (freeVars of App(Var("x"), Var("y"))) |> showList //│ = "[x, y]" (freeVars of App(Abs(Var("x"), Var("x")), Var("x"))) |> showList //│ = "[x]" :silent let alphabet = Iter.toStack("abcdefghijklmnopqrstuvwxyz") showList of alphabet //│ = "[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]" fun nextVar[T](n: Int, alphabet: Stack[T], used: Stack[Str]): Option[Str] = fun enumerate(n, acc, alphabet) = if n <= 0 then let x = reverse(acc) Iter.fromStack() Iter.joined("") if used contains(x) then None else Some(x) else alphabet Iter.fromStack() Iter.firstDefined of x => nextVar(n - 1, x :: acc, alphabet) enumerate(n, Nil, alphabet) let oneTwoThree = 1 :: 2 :: 3 :: Nil //│ oneTwoThree = Cons(1, Cons(2, Cons(3, Nil))) nextVar(1, oneTwoThree, Nil) //│ = Some("") nextVar(1, oneTwoThree, "1" :: "2" :: "3" :: Nil) //│ = Some("") nextVar(2, oneTwoThree, Nil) //│ = Some("") nextVar(3, oneTwoThree, Nil) //│ = Some("") nextVar(1, oneTwoThree, "1" :: "3" :: Nil) //│ = Some("") nextVar(2, oneTwoThree, "11" :: "12" :: "13" :: Nil) //│ = Some("") nextVar(3, oneTwoThree, "111" :: "112" :: "113" :: "121" :: Nil) //│ = Some("") fun freshVar(t: Term): Str = let fvs = freeVars(t) fun aux(n: Int): Str = if nextVar(n, alphabet, fvs) is Some(x) then x None then aux(n + 1) aux(1) freshVar(Var("x")) //│ = "" freshVar(App(Var("a"), Var("b"))) //│ = "" freshVar(App(Abs(Var("a"), Var("a")), Var("b"))) //│ = "" fun subst(t: Term, x: Str, v: Term): Term = if t is Var(y) and x === y then v Abs(Var(y), t') and x !== y and hasFree(v, y) then let y' = freshVar(t') let t'' = subst(t', y, Var(y')) Abs(Var(y'), subst(t'', x, v)) else Abs(Var(y), subst(t', x, v)) App(lhs, rhs) then App(subst(lhs, x, v), subst(rhs, x, v)) else t fun showSubst(t, n, v) = showTerm(t) + " [" + n + " / " + showTerm(v) + "]" + " = " + showTerm(subst(t, n, v)) showSubst(Var("x"), "x", Var("y")) //│ = "x [x / y] = y" showSubst(Abs(Var("x"), Var("x")), "x", Var("z")) //│ = "λx. x [x / z] = λx. x" showSubst(App(Var("x"), Var("y")), "x", Abs(Var("x"), Var("x"))) //│ = "(x y) [x / λx. x] = ((λx. x) y)" showSubst(App(Abs(Var("x"), Var("x")), Var("x")), "x", Abs(Var("y"), Var("y"))) //│ = "((λx. x) x) [x / λy. y] = ((λx. x) λy. y)" showSubst(Abs(Var("x"), App(Var("x"), Var("y"))), "y", Var("x")) //│ = "λx. (x y) [y / x] = λ. ( x)" showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", Var("x")) //│ = "λz. λx. (z (x y)) [y / x] = λz. λ. (z ( x))" showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", App(Var("x"), Var("z"))) //│ = "λz. λx. (z (x y)) [y / (x z)] = λ. λ. ( ( (x z)))" // ____ _ _ ____ _ // / ___| _ __ ___ __ _ | || | / ___| | |_ ___ _ __ // \___ \ | '_ ` _ \ / _` || || | \___ \ | __|/ _ \| '_ \ // ___) || | | | | || (_| || || | ___) || |_| __/| |_) | // |____/ |_| |_| |_| \__,_||_||_| |____/ \__|\___|| .__/ // |_| type Result = Normal | Stuck | Stepped data class Normal(term: Term) with fun toString() = "Normal form: " + showTerm(term) data class Stuck(term: Term, part: Term) with fun toString() = "Stuck: " + showTerm(part) + " in " + showTerm(term) data class Stepped(from: Term, to: Term) with fun toString() = showTerm(from) + " => " + showTerm(to) fun stepByValue(t) = if t is Var then Stuck(t, t) Abs then Normal(t) App(lhs, rhs) and stepByValue(lhs) is Stepped(_, lhs) then Stepped(t, App(lhs, rhs)) Stuck(_, part) then Stuck(t, part) Normal and stepByValue(rhs) is Stepped(_, rhs) then Stepped(t, App(lhs, rhs)) Stuck(_, part) then Stuck(t, part) Normal and lhs is Abs(Var(name), body) then Stepped(t, subst(body, name, rhs)) _ then Stuck(t, lhs) String of stepByValue of Var("x") //│ = "Stuck: x in x" String of stepByValue of Abs(Var("x"), Var("y")) //│ = "Normal form: λx. y" String of stepByValue of App(Var("x"), Var("y")) //│ = "Stuck: x in (x y)" String of stepByValue of App(Abs(Var("x"), Var("x")), Var("x")) //│ = "Stuck: x in ((λx. x) x)" String of stepByValue of App(Abs(Var("x"), Var("x")), Abs(Var("y"), Var("y"))) //│ = "((λx. x) λy. y) => λy. y" // _____ _ _ _ // | ____|__ __ __ _ | | _ _ __ _ | |_ (_) ___ _ __ // | _| \ \ / // _` || || | | | / _` || __|| | / _ \ | '_ \ // | |___ \ V /| (_| || || |_| || (_| || |_ | || (_) || | | | // |_____| \_/ \__,_||_| \__,_| \__,_| \__||_| \___/ |_| |_| // fun eval(step) = fun aux(t) = if step(t) is result and result is Stepped(_, t') then aux(t') else result aux let evalByValue = eval(stepByValue) //│ evalByValue = fun aux // Let's program with Church encoding! let zero = Abs(Var("f"), Abs(Var("x"), Var("x"))) //│ zero = Abs(Var("f"), Abs(Var("x"), Var("x"))) let one = Abs(Var("f"), Abs(Var("x"), App(Var("f"), Var("x")))) //│ one = Abs(Var("f"), Abs(Var("x"), App(Var("f"), Var("x")))) String of stepByValue of zero //│ = "Normal form: λf. λx. x" String of stepByValue of one //│ = "Normal form: λf. λx. (f x)" let succ = Abs(Var("n"), Abs(Var("f"), Abs(Var("x"), App(Var("f"), App(App(Var("n"), Var("f")), Var("x")))))) //│ succ = Abs( //│ Var("n"), //│ Abs( //│ Var("f"), //│ Abs(Var("x"), App(Var("f"), App(App(Var("n"), Var("f")), Var("x")))) //│ ) //│ ) String of stepByValue of succ //│ = "Normal form: λn. λf. λx. (f ((n f) x))" String of stepByValue of App(succ, zero) //│ = "((λn. λf. λx. (f ((n f) x))) λf. λx. x) => λf. λx. (f (((λf. λx. x) f) x))" String of evalByValue of App(succ, App(succ, zero)) //│ = "Normal form: λf. λx. (f (((λf. λx. (f (((λf. λx. x) f) x))) f) x))" String of evalByValue of App(succ, App(succ, App(succ, App(succ, zero)))) //│ = "Normal form: λf. λx. (f (((λf. λx. (f (((λf. λx. (f (((λf. λx. (f (((λf. λx. x) f) x))) f) x))) f) x))) f) x))" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/future/AppSplits.mls ================================================ :js // AppSplits.mls // ============= // Experimenting with conditional splits on function applications. fun foo(x) = x > 1 :todo :e if foo of 0 then "a" 1 then "b" //│ ╔══[COMPILATION ERROR] Unrecognized term split (application) //│ ║ l.11: if foo of //│ ║ ^^^^^^ //│ ║ l.12: 0 then "a" //│ ║ ^^^^^^^^^^^^ //│ ║ l.13: 1 then "b" //│ ╙── ^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error :todo :e :re if foo of 1, 0 then "a" 1 then "b" //│ ╔══[COMPILATION ERROR] Unrecognized term split (application) //│ ║ l.26: if foo of 1, //│ ║ ^^^^^^^^^ //│ ║ l.27: 0 then "a" //│ ║ ^^^^^^^^^^^^ //│ ║ l.28: 1 then "b" //│ ╙── ^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error :todo :e :re if foo (0) then "a" (1) then "b" //│ ╔══[COMPILATION ERROR] Unrecognized term split (juxtaposition) //│ ║ l.41: if foo //│ ║ ^^^ //│ ║ l.42: (0) then "a" //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/future/Or.mls ================================================ :js data class Some[T](value: T) :fixme // TODO // TODO support `or` in UCS fun f(a, b) = if a is Some(v) and b is Some(v') then v + v' or b is Some(v) then v else 0 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.10: and b is Some(v') then v + v' //│ ║ ^^^^^^ //│ ║ l.11: or b is Some(v) then v //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/future/SymbolicClass.mls ================================================ :js // :de // type List = Nil | Cons :ctx type List = Nil | Cons object Nil data class (::) Cons(head: Int, tail: List) //│ Env: //│ :: -> RefElem(member:Cons) //│ Cons -> RefElem(member:Cons) //│ List -> RefElem(member:List) //│ Nil -> RefElem(member:Nil) Cons(1, Nil) //│ = Cons(1, Nil) 1 :: 2 //│ = Cons(1, 2) fun map(f, xs) = if xs is Nil then Nil x :: xs' then f(x) :: map(f, xs') // TODO? :e :re :sjs new 1 :: 2 //│ ╔══[COMPILATION ERROR] Expected a statically known class; found integer literal. //│ ║ l.31: new 1 :: 2 //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; tmp = globalThis.Object.freeze(new 1()); Cons1(tmp, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: 1 is not a constructor // TODO? :e :re :sjs new (1 :: 2) //│ ╔══[COMPILATION ERROR] Expected a statically known class; found application of type Cons. //│ ║ l.45: new (1 :: 2) //│ ║ ^^^^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp1; tmp1 = Cons1(1, 2); globalThis.Object.freeze(new tmp1()) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: tmp1 is not a constructor new ::(1, 2) //│ = Cons(1, 2) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/BooleanPatterns.mls ================================================ :js // * It's neat how the complicated pattern matching logic is desugared into a simple `switch` :sjs fun test(x) = if x is 0 then "0" ~2 & ~3 then "not 2 and not 3" ~3 then "not 3" _ then "other" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test; //│ test = function test(x) { //│ switch (x) { //│ case 0: //│ return "0"; //│ case 2: //│ return "not 3"; //│ case 3: //│ return "other"; //│ } //│ return "not 2 and not 3" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— test(0) //│ = "0" test(1) //│ = "not 2 and not 3" test(2) //│ = "not 3" test(3) //│ = "other" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/CardSuits.mls ================================================ :js data class Card[T](color: T) class Suit object Heart extends Suit object Diamond extends Suit object Spade extends Suit object Club extends Suit fun suit(x) = if x is Card(a) then a let card = Card(Heart) //│ card = Card(Heart) suit of card //│ = Heart // :ucs desugared normalized fun foo(x) = if x is Card(Heart) then 0 Card(Diamond) then 1 Card(Spade) then 2 Card(Club) then 3 else 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/CrossModules.mls ================================================ :js // https://github.com/hkust-taco/mlscript/issues/151#issuecomment-3235677951 module BaseLib0 with class Foo(val x) module DerivedLib1 with val BaseLib: module BaseLib0 = BaseLib0 fun testFoo = case BaseLib.Foo then 1 _ then 0 DerivedLib1.testFoo(new DerivedLib1.BaseLib.Foo(123)) //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/DualOptions.mls ================================================ :js // DualOptions.mls // =============== // Matching two options in any possible order. abstract class Option[T] data class Some[T](value: T) extends Option[T] object None extends Option[Nothing] data class Pair[A, B](x: A, y: B) // All `add_n` functions should be inferred to have the same type. fun add_1(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 add_1(None, None) //│ = 0 add_1(Some(5), None) //│ = 5 add_1(None, Some(9)) //│ = 9 add_1(Some(5), Some(9)) //│ = 14 fun add_2(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None then xv None and y is Some(yv) then yv None then 0 add_2(None, None) //│ = 0 add_2(Some(5), None) //│ = 5 add_2(None, Some(9)) //│ = 9 add_2(Some(5), Some(9)) //│ = 14 fun add_3(x, y) = if Pair(x, y) is Pair(Some(xv), Some(yv)) then xv + yv Pair(Some(xv), None) then xv Pair(None, Some(yv)) then yv Pair(None, None) then 0 add_3(None, None) //│ = 0 add_3(Some(5), None) //│ = 5 add_3(None, Some(9)) //│ = 9 add_3(Some(5), Some(9)) //│ = 14 fun add_4(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv is None then xv None and y is Some(yv) then yv is None then 0 add_4(None, None) //│ = 0 add_4(Some(5), None) //│ = 5 add_4(None, Some(9)) //│ = 9 add_4(Some(5), Some(9)) //│ = 14 fun add_5(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and x is Some(xv) then xv x is None and y is Some(yv) then yv y is None and x is None then 0 add_5(None, None) //│ = 0 add_5(Some(5), None) //│ = 5 add_5(None, Some(9)) //│ = 9 add_5(Some(5), Some(9)) //│ = 14 fun add_6(x, y) = if [x, y] is [Some(xv), Some(yv)] then xv + yv [Some(xv), None] then xv [None, Some(yv)] then yv [None, None] then 0 add_6(None, None) //│ = 0 add_6(Some(5), None) //│ = 5 add_6(None, Some(9)) //│ = 9 add_6(Some(5), Some(9)) //│ = 14 // Functions from now on have a predicate `p` that can be used to add some preconditions. fun add_6(p, x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and p(x) and x is Some(xv) then 42 y is None and x is Some(xv) then xv x is None and y is Some(yv) then yv y is None and x is None then 0 :expect 0 add_6((x) => true, None, None) //│ = 0 :expect 42 add_6((x) => true, Some(5), None) //│ = 42 :expect 5 add_6((x) => false, Some(5), None) //│ = 5 :expect 9 add_6((x) => true, None, Some(9)) //│ = 9 :expect 14 add_6((x) => true, Some(5), Some(9)) //│ = 14 fun add_7(p, x, y) = if x is Some(xv) and y is Some(yv) then xv + yv // y is None and p(x) and x is Some(xv) then 42 y is None and x is Some(xv) then xv y is Some(yv) and p(yv) and x is None then 36 y is Some(yv) and x is None then yv y is None and x is None then 0 :expect 0 add_7((x) => x > 0, None, None) //│ = 0 :expect 5 add_7((x) => x > 0, Some(5), None) //│ = 5 :expect 36 add_7((x) => x > 0, None, Some(9)) //│ = 36 :expect -9 add_7((x) => x > 0, None, Some(-9)) //│ = -9 :expect 14 add_7((x) => x > 0, Some(5), Some(9)) //│ = 14 fun add_8(p, x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and p(x) and x is Some(xv) then 42 y is None and x is Some(xv) then xv y is Some(yv) and p(yv) and x is None then 36 y is Some(yv) and x is None then yv y is None and x is None then 0 :expect 0 add_8((x) => x > 0, None, None) //│ = 0 :expect 42 add_8((x) => true, Some(9), None) //│ = 42 :expect 5 add_8((x) => x > 0, Some(5), None) //│ = 5 :expect 36 add_8((x) => x > 0, None, Some(9)) //│ = 36 :expect -9 add_8((x) => x > 0, None, Some(-9)) //│ = -9 :expect 14 add_8((x) => x > 0, Some(5), Some(9)) //│ = 14 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/InterleavedLet.mls ================================================ :js abstract class List[out T] data class Cons[out T](head: T, tail: List[T]) extends List[T] object Nil extends List[Nothing] fun map(f, xs) = if xs is Nil then Nil let xs' = map(f, xs) Cons(x, xs) then Cons(f(x), xs') object Bot abstract class Option[T] data class Some[T](value: T) extends Option[T] object None extends Option[Nothing] fun normalize(tp) = () fun glb(tp1, tp2) = () // TODO: `as` keyword fun merge(tp1, tp2) = if let tp1_n = normalize(tp1) tp1_n is Bot then Bot let tp2_n = normalize(tp2) tp2_n is Bot then Bot let m = merge(tp1_n, tp2_n) m is Some(tp) then tp m is None then glb(tp1_n, tp2_n) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/JoinPoints.mls ================================================ :js // * Note: no join point needed :sjs x => if x is 0 then 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; //│ lambda = (undefined, function (x) { //│ if (x === 0) { //│ return 1 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // * Note: join point needed :sjs x => if x is [[0]] then 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; //│ lambda1 = (undefined, function (x) { //│ let element0$, element0$1; //│ if (runtime.Tuple.isArrayLike(x) && x.length === 1) { //│ element0$ = runtime.Tuple.get(x, 0); //│ if (runtime.Tuple.isArrayLike(element0$) && element0$.length === 1) { //│ element0$1 = runtime.Tuple.get(element0$, 0); //│ if (element0$1 === 0) { //│ return 1 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ lambda1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun data class S(value) // * Note: the computation in the default branch should not be duplicated. :sjs fun crazy(v) = if v is S(S(S(S(S(S(0)))))) then "bruh!" _ then S(S(S(S(S(S(0)))))) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let crazy; //│ crazy = function crazy(v) { //│ let arg$S$0$, arg$S$0$1, arg$S$0$2, arg$S$0$3, arg$S$0$4, arg$S$0$5, tmp, tmp1, tmp2, tmp3, tmp4; //│ if (v instanceof S1.class) { //│ arg$S$0$ = v.value; //│ if (arg$S$0$ instanceof S1.class) { //│ arg$S$0$1 = arg$S$0$.value; //│ if (arg$S$0$1 instanceof S1.class) { //│ arg$S$0$2 = arg$S$0$1.value; //│ if (arg$S$0$2 instanceof S1.class) { //│ arg$S$0$3 = arg$S$0$2.value; //│ if (arg$S$0$3 instanceof S1.class) { //│ arg$S$0$4 = arg$S$0$3.value; //│ if (arg$S$0$4 instanceof S1.class) { //│ arg$S$0$5 = arg$S$0$4.value; //│ if (arg$S$0$5 === 0) { //│ return "bruh!" //│ } //│ } //│ } //│ } //│ } //│ } //│ } //│ tmp = S1(0); //│ tmp1 = S1(tmp); //│ tmp2 = S1(tmp1); //│ tmp3 = S1(tmp2); //│ tmp4 = S1(tmp3); //│ return S1(tmp4) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/List.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Option.mls" open Stack open Option { Some, None } fun length(xs) = if xs is Nil then 0 Cons(_, xs) then 1 + length(xs) length of Nil //│ = 0 length of 1 :: 2 :: Nil //│ = 2 :re length of () //│ ═══[RUNTIME ERROR] Error: match error fun map(f, xs) = if xs is Nil then Nil Cons(x, xs) then Cons(f(x), map(f, xs)) fun findFirst(f, xs) = if xs is Nil then None Cons(x, _) and f(x) then Some(x) Cons(_, xs) then findFirst(f, xs) findFirst of _ > 2, Nil //│ = None findFirst of _ > 2, 1 :: 2 :: 3 :: 4 :: Nil //│ = Some(3) findFirst of _ > 2, 1 :: 2 :: 4 :: 3 :: Nil //│ = Some(4) fun findFirst(f, xs) = if xs is Nil then None Cons(x, _) and f(x) then Some(x) Cons(_, xs) then findFirst(f, xs) findFirst of _ > 2, Nil //│ = None findFirst of _ > 2, 1 :: 2 :: 3 :: 4 :: Nil //│ = Some(3) findFirst of _ > 2, 1 :: 2 :: 4 :: 3 :: Nil //│ = Some(4) fun findFirst(f, xs) = if xs is Nil then None Cons(x, _) and f(x) then Some(x) Cons(_, xs) then findFirst(f, xs) findFirst of _ > 2, Nil //│ = None findFirst of _ > 2, 1 :: 2 :: 3 :: 4 :: Nil //│ = Some(3) findFirst of _ > 2, 1 :: 2 :: 4 :: 3 :: Nil //│ = Some(4) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls ================================================ :js :fixme true or true //│ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. //│ ║ l.5: true or true //│ ╙── ^^^^^^^^^^^^ //│ = true :fixme while false or false do print("ok") //│ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. //│ ║ l.12: while false or false do print("ok") //│ ╙── ^^^^^^^^^^^^^^ fun test(x) = print(x) x 123 and test(42) //│ = false :sjs true and test(42) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut4, scrut5; //│ scrut4 = true; //│ if (scrut4 === true) { //│ scrut5 = test(42); //│ if (scrut5 === true) { true } else { false } //│ } else { false } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 42 //│ = false :fixme true or test(42) //│ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. //│ ║ l.39: true or test(42) //│ ╙── ^^^^^^^^^^^^^^^^ //│ > 42 //│ = false true and test(true) //│ > true //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/Seqs.mls ================================================ :js assert [""] is [..stack, Str as id], [stack, id] //│ = [[], ""] assert [..[], ""] is [..stack, Str as id], [stack, id] //│ = [[], ""] assert [..[..[""]], ""] is [..stack, Str as id], [stack, id] //│ = [[], ""] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/general/Simple.mls ================================================ :js // Simple.mls // ========== // Simple and single-line UCS expressions. data class Box[T](value: T) data class Bottle[T](value: T) fun foo(x) = if x is Box(y) then y fun foo(x) = if x is Box(y) then y Bottle(y) then y fun foo(x, y) = if x is Box(xv) and y is Bottle(yv) then xv + yv fun foo(x, y, z) = if x is Box(xv) and y is Box(yv) and z is Box(zv) then xv + yv + zv ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/hygiene/CrossBranchCapture.mls ================================================ :js data class Numb(n: Int) :e fun process(e) = if e is Numb(n) and n > 0 then n Numb(m) then n //│ ╔══[COMPILATION ERROR] Name not found: n //│ ║ l.9: Numb(m) then n //│ ╙── ^ :re process(Numb(-10)) //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun process(e, n) = if e is Numb(n) and n > 0 then n Numb(m) then n + m process(Numb(0), 10) process(Numb(-1), 10) process(Numb(1), 10) //│ = 1 // class Vec(xs: Array[Numb | Vec]) // Array is not available data abstract class Vec[out T](n: Int) data class Cons[out T](head: T, tail: Vec[T]) extends Vec[T] object Nil extends Vec[Nothing] data class Pair[A, B](a: A, b: B) :e fun process(e) = if e is Pair(Numb(n), Numb(m)) then Numb(n + m) Pair(Vec(xs), Vec(ys)) then n Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n //│ ╔══[COMPILATION ERROR] Name not found: n //│ ║ l.41: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Duplicated pattern variable. //│ ║ l.42: Pair(Vec(n), Numb(n)) then n //│ ║ ^ //│ ╟── The previous definition is as follows. //│ ║ l.42: Pair(Vec(n), Numb(n)) then n //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Duplicated pattern variable. //│ ║ l.43: Pair(Numb(n), Vec(n)) then n //│ ║ ^ //│ ╟── The previous definition is as follows. //│ ║ l.43: Pair(Numb(n), Vec(n)) then n //│ ╙── ^ // * TODO: we might want to accept this and compare the two values for equality :e fun process(e) = if e is Pair(Numb(n), Numb(n)) then n //│ ╔══[COMPILATION ERROR] Duplicated pattern variable. //│ ║ l.65: Pair(Numb(n), Numb(n)) then n //│ ║ ^ //│ ╟── The previous definition is as follows. //│ ║ l.65: Pair(Numb(n), Numb(n)) then n //│ ╙── ^ process(Pair(Numb(1), Numb(2))) //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/hygiene/Hygiene.mls ================================================ :js data class Left[out T](value: T) data class Some[out T](value: T) data class Right[out T](value: T) fun foo(x) = if x is Some(Left(y)) then x Some(x) then x :expect Some(Left(1)) foo(Some(Left(1))) //│ = Some(Left(1)) :expect 2 foo(Some(2)) //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/hygiene/HygienicBindings.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option { Some, None } fun (~~>) expect(a, b) = if a == b then () else throw Error("unexpected result") type Either[A, B] = Left[A] | Right[B] data class Left[A](val leftValue: A) data class Right[B](val rightValue: B) fun justTrue(_) = true fun justFalse(_) = false fun h0(a) = if a is Some(Left(y)) then y a is Some(Right(z)) then z a is None then 0 // If a class parameter is bound to the same variable in different branches, // the bindings can be merged and can be typed and coverage checked. See the // desugared version below. // :ducs:postprocess.result fun h0'(a) = if a is Some(x) and x is Left(y) then y a is Some(x) and x is Right(z) then z a is None then 0 :ucs normalized // If a class parameter is bound to different variables in different // branches, the bindings with the same name in different branches should not // interfere with each other, and normalization should be able to factorize. fun h1(a) = if a is Some(x) and x is Left(y) then y a is Some(y) and y is Right(z) then z a is None then 0 //│ Normalized: //│ > ‹if|while› //│ > a is Option.Some(arg$Some$0$) and //│ > let x = tmp:arg$Some$0$⁰ //│ > x is Left(arg$Left$0$) and //│ > let y = tmp:arg$Left$0$⁰ //│ > else y⁰ //│ > let y = tmp:arg$Some$0$⁰ //│ > y is Right(arg$Right$0$) and //│ > let z = tmp:arg$Right$0$⁰ //│ > else z⁰ //│ > a is Option.None then 0 h1(Some(Left(42))) //│ = 42 h1(Some(Right(8))) //│ = 8 h1(None) //│ = 0 :ucs normalized // But it is difficult to merge bindings of different variables if one // of them is bound via a let binding. Maybe this can be left for future work. fun h2(a) = if a is Some(x) and x is x' and x' is Left(y) then y a is Some(y) and let y' = y y' is Right(z) then z a is None then 0 //│ Normalized: //│ > ‹if|while› //│ > a is Option.Some(arg$Some$0$) and //│ > let x = tmp:arg$Some$0$¹ //│ > let x' = x⁰ //│ > x' is Left(arg$Left$0$) and //│ > let y = tmp:arg$Left$0$¹ //│ > else y¹ //│ > let y = tmp:arg$Some$0$¹ //│ > let y' = y² //│ > y' is Right(arg$Right$0$) and //│ > let z = tmp:arg$Right$0$¹ //│ > else z¹ //│ > a is Option.None then 0 // :w fun h3(x, y, f, p) = if x is _ and f(x) is y and p(x) then y None then y _ then "anyway" h3("anything", "not me", _ => "should be me", _ => true) ~~> "should be me" h3(None, "should be me", _ => "not me", _ => false) ~~> "should be me" h3("anything", "anything", _ => "not me", _ => false) ~~> "anyway" // :ducs:postprocess.result // :w fun h4(x, y, p) = if x is y and p(x) then y None then y _ then "default" :expect "should be me" h4("should be me", "not me", justTrue) //│ = "should be me" :expect None h4(None, "not me", justTrue) //│ = None :expect "should be me" h4(None, "should be me", justFalse) //│ = "should be me" :expect "default" h4("anything", "not me", justFalse) //│ = "default" // :ducs:postprocess.result fun h5(x, y, p) = if x is Some(y) and p(x) then y None then y _ then y h5(Some(1), 2, justTrue) ~~> 1 h5(Some(1), 2, justFalse) ~~> 2 h5(None, 0, justTrue) ~~> 0 h5(None, 0, justFalse) ~~> 0 h5("foo", 42, justTrue) ~~> 42 h5("foo", 42, justFalse) ~~> 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/hygiene/PatVars.mls ================================================ :js class x if 0 is x then 12 //│ = 12 :re class X if 0 is X then 12 //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/Deduplication.mls ================================================ :js :patMatConsequentSharingThreshold 5 let x = 0 let y = 0 let z = 0 //│ x = 0 //│ y = 0 //│ z = 0 :sjs if x === 0 then 1 else 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut; scrut = x === 0; if (scrut === true) { 1 } else { 2 } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 :sjs let a = if x === 0 then 1 else 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a, scrut1, tmp; scrut1 = x === 0; if (scrut1 === true) { tmp = 1; } else { tmp = 2; } a = tmp; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ a = 1 :sjs print of if x === 0 then 1 else 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let scrut2, tmp1; //│ scrut2 = x === 0; //│ if (scrut2 === true) { tmp1 = 1; Predef.print(tmp1) } else { tmp1 = 2; Predef.print(tmp1) } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 :sjs if x is 0 and y is 0 and z is 0 then "000" 1 then "001" 1 then "01" 1 then "1" else "" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ switch (x) { //│ case 0: //│ switch (y) { //│ case 0: //│ switch (z) { //│ case 0: //│ "000"; //│ break; //│ case 1: //│ "001"; //│ break; //│ default: //│ ""; //│ } //│ break; //│ case 1: //│ "01"; //│ break; //│ default: //│ ""; //│ } //│ break; //│ case 1: //│ "1"; //│ break; //│ default: //│ ""; //│ } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "000" // * Making the terms artificially big to force sharing of consequents :sjs if x is 0 and y is 0 and z is 0 then "000-------------------------------------------------------------------------" 1 then "001-------------------------------------------------------------------------" 1 then "01-------------------------------------------------------------------------" 1 then "1-------------------------------------------------------------------------" else "-------------------------------------------------------------------------" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp2; //│ split_root$: { //│ switch (x) { //│ case 0: //│ switch (y) { //│ case 0: //│ switch (z) { //│ case 0: //│ tmp2 = "000-------------------------------------------------------------------------"; //│ break split_root$; //│ case 1: //│ tmp2 = "001-------------------------------------------------------------------------"; //│ break split_root$; //│ } //│ break; //│ case 1: //│ tmp2 = "01-------------------------------------------------------------------------"; //│ break split_root$; //│ } //│ break; //│ case 1: //│ tmp2 = "1-------------------------------------------------------------------------"; //│ break split_root$; //│ } //│ tmp2 = "-------------------------------------------------------------------------"; //│ } //│ tmp2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "000-------------------------------------------------------------------------" :sjs let qqq = if x is 0 and y is 0 and z is 0 then "000-------------------------------------------------------------------------" 1 then "001-------------------------------------------------------------------------" 1 then "01-------------------------------------------------------------------------" 1 then "1-------------------------------------------------------------------------" else "-------------------------------------------------------------------------" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let qqq, tmp3; //│ split_root$1: { //│ switch (x) { //│ case 0: //│ switch (y) { //│ case 0: //│ switch (z) { //│ case 0: //│ tmp3 = "000-------------------------------------------------------------------------"; //│ break split_root$1; //│ case 1: //│ tmp3 = "001-------------------------------------------------------------------------"; //│ break split_root$1; //│ } //│ break; //│ case 1: //│ tmp3 = "01-------------------------------------------------------------------------"; //│ break split_root$1; //│ } //│ break; //│ case 1: //│ tmp3 = "1-------------------------------------------------------------------------"; //│ break split_root$1; //│ } //│ tmp3 = "-------------------------------------------------------------------------"; //│ } //│ qqq = tmp3; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ qqq = "000-------------------------------------------------------------------------" :sjs print of if x is 0 and y is 0 and z is 0 then "000" 1 then "001" 1 then "01" 1 then "1" else "" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp4; //│ switch (x) { //│ case 0: //│ switch (y) { //│ case 0: //│ switch (z) { //│ case 0: //│ tmp4 = "000"; //│ break; //│ case 1: //│ tmp4 = "001"; //│ break; //│ default: //│ tmp4 = ""; //│ } //│ Predef.print(tmp4); //│ break; //│ case 1: //│ tmp4 = "01"; //│ Predef.print(tmp4); //│ break; //│ default: //│ tmp4 = ""; //│ Predef.print(tmp4); //│ } //│ break; //│ case 1: //│ tmp4 = "1"; //│ Predef.print(tmp4); //│ break; //│ default: //│ tmp4 = ""; //│ Predef.print(tmp4); //│ } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 000 :sjs print of if x is 0 and y is 0 and z is 0 then "000" 1 then "001" 1 then "01" 1 then "1" else "" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp5; //│ switch (x) { //│ case 0: //│ switch (y) { //│ case 0: //│ switch (z) { //│ case 0: //│ tmp5 = "000"; //│ break; //│ case 1: //│ tmp5 = "001"; //│ break; //│ default: //│ tmp5 = ""; //│ } //│ Predef.print(tmp5); //│ break; //│ case 1: //│ tmp5 = "01"; //│ Predef.print(tmp5); //│ break; //│ default: //│ tmp5 = ""; //│ Predef.print(tmp5); //│ } //│ break; //│ case 1: //│ tmp5 = "1"; //│ Predef.print(tmp5); //│ break; //│ default: //│ tmp5 = ""; //│ Predef.print(tmp5); //│ } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 000 :sjs fun foo(x, y, z) = if x is 0 and y is 0 and z is 0 then "000" 1 then "001" 1 then "01" 1 then "1" else "" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo; //│ foo = function foo(x1, y1, z1) { //│ switch (x1) { //│ case 0: //│ switch (y1) { //│ case 0: //│ switch (z1) { //│ case 0: //│ return "000"; //│ case 1: //│ return "001"; //│ } //│ return ""; //│ case 1: //│ return "01"; //│ } //│ return ""; //│ case 1: //│ return "1"; //│ } //│ return "" //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— foo(0, 0, 0) //│ = "000" :sjs print of if x is 0 and y is 0 and z is 0 then "000" y is 1 then "01_" z is 1 then "0_1" x is 1 then "1__" y is 2 then "_2_" else "___" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp6; //│ switch (x) { //│ case 0: //│ switch (y) { //│ case 0: //│ switch (z) { //│ case 0: //│ tmp6 = "000"; //│ break; //│ case 1: //│ tmp6 = "0_1"; //│ break; //│ default: //│ tmp6 = "___"; //│ } //│ Predef.print(tmp6); //│ break; //│ case 1: //│ tmp6 = "01_"; //│ Predef.print(tmp6); //│ break; //│ default: //│ if (z === 1) { //│ tmp6 = "0_1"; //│ } else { //│ if (y === 2) { //│ tmp6 = "_2_"; //│ } else { //│ tmp6 = "___"; //│ } //│ } //│ Predef.print(tmp6); //│ } //│ break; //│ case 1: //│ tmp6 = "1__"; //│ Predef.print(tmp6); //│ break; //│ default: //│ if (y === 2) { tmp6 = "_2_"; Predef.print(tmp6) } else { tmp6 = "___"; Predef.print(tmp6) } //│ } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 000 :sjs fun foo(x, y, z) = if x is 0 and y is 0 and z is 0 then "000" y is 1 then "01_" z is 1 then "0_1" x is 1 then "1__" y is 2 then "_2_" else "___" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo1; //│ foo1 = function foo(x1, y1, z1) { //│ switch (x1) { //│ case 0: //│ switch (y1) { //│ case 0: //│ switch (z1) { //│ case 0: //│ return "000"; //│ case 1: //│ return "0_1"; //│ } //│ return "___"; //│ case 1: //│ return "01_"; //│ } //│ if (z1 === 1) { //│ return "0_1" //│ } //│ if (y1 === 2) { //│ return "_2_" //│ } //│ return "___"; //│ case 1: //│ return "1__"; //│ } //│ if (y1 === 2) { return "_2_" } //│ return "___"; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun expensive_call(str) = Math.pow(2, str.length) :sjs fun foo(x, y, z) = if x is 0 and y is 0 and z is 0 then "000" 1 then "001" 1 then "01" 1 then "1" else let value = "hello" expensive_call(value) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo2; //│ foo2 = function foo(x1, y1, z1) { //│ let value; //│ switch (x1) { //│ case 0: //│ switch (y1) { //│ case 0: //│ switch (z1) { //│ case 0: //│ return "000"; //│ case 1: //│ return "001"; //│ } //│ break; //│ case 1: //│ return "01"; //│ } //│ break; //│ case 1: //│ return "1"; //│ } //│ value = "hello"; //│ return expensive_call(value) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— foo(0, 0, 0) //│ = "000" object A object B object C :sjs // Since no branch is duplicated, there is no label or `break`. fun foo(x, y, z) = if x is A then "Hello-------------------------------------------------------------------------" else "Goodbye-------------------------------------------------------------------------" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo3; //│ foo3 = function foo(x1, y1, z1) { //│ if (x1 instanceof A1.class) { //│ return "Hello-------------------------------------------------------------------------" //│ } //│ return "Goodbye-------------------------------------------------------------------------"; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs // Before the deduplication is introduced: // let foo; // foo = function foo(x1, y, z) { // if (x1 instanceof A1) { // if (y instanceof B1) { // if (z instanceof C1) { // return "Hello" // } else { return "Goodbye" } // } else { return "Goodbye" } // } else { return "Goodbye" } // }; fun foo(x, y, z) = if x is A and y is B and z is C then "Hello" else "Goodbye" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo4; //│ foo4 = function foo(x1, y1, z1) { //│ if (x1 instanceof A1.class) { //│ if (y1 instanceof B1.class) { //│ if (z1 instanceof C1.class) { //│ return "Hello" //│ } //│ return "Goodbye" //│ } //│ return "Goodbye"; //│ } //│ return "Goodbye"; //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs // The default branch which throws the match error should be also deduplicated. fun foo(x, y, z) = if x is A and y is B and z is C then "Hello" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo5; //│ foo5 = function foo(x1, y1, z1) { //│ if (x1 instanceof A1.class) { //│ if (y1 instanceof B1.class) { //│ if (z1 instanceof C1.class) { //│ return "Hello" //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :expect "Hello" foo(A, B, C) //│ = "Hello" :re foo(A, A, A) //│ ═══[RUNTIME ERROR] Error: match error :re foo(A, B, B) //│ ═══[RUNTIME ERROR] Error: match error :sjs let y = if true then 1 else 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let y1, scrut3, tmp7; //│ scrut3 = true; //│ if (scrut3 === true) { tmp7 = 1; } else { tmp7 = 2; } //│ y1 = tmp7; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ y = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/DeduplicationWhile.mls ================================================ :js :patMatConsequentSharingThreshold 1 // ^ force dedup even for small consequents // :sjs () => while true and false do print(1) else print(2) //│ = fun :sjs () => while true and true and false do print(1) else print(2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda1; //│ lambda1 = (undefined, function () { //│ lbl: while (true) { //│ let scrut, scrut1, scrut2; //│ scrut = true; //│ if (scrut === true) { //│ scrut2 = true; //│ if (scrut2 === true) { //│ scrut1 = false; //│ if (scrut1 === true) { //│ Predef.print(1); //│ continue lbl //│ } //│ } //│ } //│ Predef.print(2); //│ continue lbl; //│ } //│ }); //│ lambda1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // :sjs let x = 3 if x > 0 and x === 1 do print("A") 2 do print("A") 3 do print("B") 4 do print("B") //│ > B //│ x = 3 :sjs x => while x > 0 and x === 1 do print("A") 2 do print("A") 3 do print("B") 4 do print("B") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda2; //│ lambda2 = (undefined, function (x1) { //│ lbl: while (true) { //│ let scrut5, scrut6, scrut7, scrut8, scrut9; //│ split_root$1: { //│ split_1$1: { //│ split_2$1: { //│ scrut5 = x1 > 0; //│ if (scrut5 === true) { //│ scrut6 = x1 === 1; //│ if (scrut6 === true) { //│ break split_1$1 //│ } //│ scrut7 = x1 === 2; //│ if (scrut7 === true) { //│ break split_1$1 //│ } //│ scrut8 = x1 === 3; //│ if (scrut8 === true) { //│ break split_2$1 //│ } //│ scrut9 = x1 === 4; //│ if (scrut9 === true) { //│ break split_2$1 //│ } //│ break split_root$1; //│ } //│ break split_root$1; //│ } //│ Predef.print("B"); //│ continue lbl; //│ } //│ Predef.print("A"); //│ continue lbl; //│ } //│ break; //│ } //│ return runtime.Unit //│ }); //│ lambda2 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun :sjs x => while x > 0 and x === 1 and false and false do print("A") 2 and false do print("B") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda3; //│ lambda3 = (undefined, function (x1) { //│ lbl: while (true) { //│ let scrut5, scrut6, scrut7, scrut8, scrut9, scrut10; //│ split_root$1: { //│ split_1$1: { //│ scrut5 = x1 > 0; //│ if (scrut5 === true) { //│ scrut6 = x1 === 1; //│ if (scrut6 === true) { //│ scrut8 = false; //│ if (scrut8 === true) { //│ scrut7 = false; //│ if (scrut7 === true) { //│ Predef.print("A"); //│ continue lbl //│ } //│ scrut9 = x1 === 2; //│ if (scrut9 === true) { //│ scrut10 = false; //│ if (scrut10 === true) { //│ break split_1$1 //│ } //│ } //│ } else { //│ scrut9 = x1 === 2; //│ if (scrut9 === true) { //│ scrut10 = false; //│ if (scrut10 === true) { //│ break split_1$1 //│ } //│ } //│ } //│ break split_root$1 //│ } //│ scrut9 = x1 === 2; //│ if (scrut9 === true) { //│ scrut10 = false; //│ if (scrut10 === true) { //│ break split_1$1 //│ } //│ } //│ break split_root$1; //│ } //│ break split_root$1; //│ } //│ Predef.print("B"); //│ continue lbl; //│ } //│ break; //│ } //│ return runtime.Unit //│ }); //│ lambda3 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls ================================================ :js :patMatConsequentSharingThreshold 1 // ^ force dedup even for small consequents let x = true y = 2 z = true //│ x = true //│ y = 2 //│ z = true // TODO: This should *not* be automatically deduplicated. // * In fact, we should probably never try to deduplicate things that are already duplicated in the source. :sjs if x then 0 y then 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let tmp; //│ if (x !== true) { //│ if (y !== true) { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ } //│ tmp = 0; //│ tmp //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 // * Compare with: :sjs if x then 0 y then 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ if (x === true) { //│ 0 //│ } else { //│ if (y === true) { //│ 1 //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } //│ } //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 // * Currently, dedup' is implemented by hash-consing after the fact, which doesn't work when bindings are involved; // * it fails here: :ssjs :ucs normalized if x and z then 1 y is [b] then let x = 1 in x + b //│ Normalized: //│ > ‹if|while› //│ > x is true and //│ > z is true then 1 //│ > y is []=1 and //│ > let tmp:element0$ = ((tmp:runtime⁰.)Tuple.)get‹term:get›(y⁰, 0) //│ > let b = tmp:element0$⁰ //│ > else //│ > let x //│ > x = 1 //│ > builtin:+⁰(x⁰, b⁰) //│ > y is []=1 and //│ > let tmp:element0$ = ((tmp:runtime⁰.)Tuple.)get‹term:get›(y⁰, 0) //│ > let b = tmp:element0$⁰ //│ > else //│ > let x //│ > x = 1 //│ > builtin:+⁰(x⁰, b⁰) //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— //│ let b, x1, element0$, tmp1; //│ split_root$: { //│ split_default$: { //│ if (x === true) { //│ if (z === true) { //│ tmp1 = 1; //│ break split_root$ //│ } //│ if (runtime.Tuple.isArrayLike(y) && y.length === 1) { //│ element0$ = runtime.checkCall(runtime.Tuple.get(y, 0)); //│ b = element0$; //│ } else { //│ break split_default$ //│ } //│ } else { //│ if (runtime.Tuple.isArrayLike(y) && y.length === 1) { //│ element0$ = runtime.checkCall(runtime.Tuple.get(y, 0)); //│ b = element0$; //│ } else { //│ break split_default$ //│ } //│ } //│ x1 = 1; //│ tmp1 = x1 + b; //│ break split_root$; //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } //│ block$res4 = tmp1; //│ undefined //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 1 // * What we want is a way of let-binding split tails (ie, join points), as in: // > if // > let-split $j = // > y is Some(argument0$) and // > let b = $argument0$ // > else // > let x // > x = 1 // > builtin:+(x, b) // > x is true and // > z is true then 1 // > $j // > $j // * Contemplate this absolutely horrendous code (before optimization): :sir :soir :sjs if false do () 123 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ 123 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let scrut; //│ block split_root$: //│ block split_1$: //│ set scrut = false; //│ match scrut //│ true => //│ break split_1$ //│ else //│ break split_1$ //│ end //│ end //│ 123 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ 123 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 :patMatConsequentSharingThreshold 10 // * How it ought to be: :sir if false do () 123 //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let scrut; set scrut = false; 123 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 123 object Obj with fun test = print("hi") false :sjs if Obj.test do () //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ Obj1.test; runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/InheritanceNormalization.mls ================================================ :js class A class B extends A :breakme // Should warn that the `B` branch is unreachable let f = case A then "A" B then "B" //│ f = fun f f(new A) //│ = "A" f(new B) //│ = "A" let f = case B then "B" A then "A" //│ f = fun f f(new A) //│ = "A" f(new B) //│ = "B" :sir let f = case A & B then "A & B" A then "A" //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let f⁰, lambda⁰; //│ define lambda⁰ as fun lambda¹(caseScrut) { //│ match caseScrut //│ A⁰ => //│ match caseScrut //│ B⁰ => //│ return "A & B" //│ else //│ return "A" //│ end //│ else //│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ set f⁰ = lambda¹; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun :expect "A" f(new A) //│ = "A" :expect "A & B" f(new B) //│ = "A & B" // Test with imported class hierarchy import "../../../mlscript-compile/Example.mls" open Example let g = case Base & Child1 then "Base & Child1" Base then "Base" //│ g = fun :expect "Base & Child1" g(new Child1) //│ = "Base & Child1" :expect "Base" g(new Child2) //│ = "Base" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/OverlapOfPrimitives.mls ================================================ :js :ucs normalized fun test(x) = if x is Int then "Int" 0 then "Zero" //│ Normalized: //│ > ‹if|while› x is Int then "Int" test(0) //│ = "Int" fun test(x, p) = if x is Int and p(x) then "foo" 0 then "bar" else "qax" //│ Normalized: //│ > ‹if|while› //│ > x is refined Int and //│ > let tmp:scrut = p⁰(x⁰) //│ > tmp:scrut is true then "foo" //│ > x is 0 then "bar" //│ > else "qax" //│ > else "qax" fun test(x, p) = if x is Str and p(x) then "foo" "lol" then "bar" else "qax" //│ Normalized: //│ > ‹if|while› //│ > x is refined Str and //│ > let tmp:scrut = p¹(x¹) //│ > tmp:scrut is true then "foo" //│ > x is "lol" then "bar" //│ > else "qax" //│ > else "qax" test("test", _ => true) //│ = "foo" test("lol", _ => true) //│ = "foo" test("lol", _ => false) //│ = "bar" test("", _ => false) //│ = "qax" fun test(x, p) = if x is Num and p(x) then "great" 2.71828 then "E" 3.14159 then "PI" else "other" //│ Normalized: //│ > ‹if|while› //│ > x is refined Num and //│ > let tmp:scrut = p²(x²) //│ > tmp:scrut is true then "great" //│ > x is 2.71828 then "E" //│ > x is 3.14159 then "PI" //│ > else "other" //│ > else "other" fun test(x, p) = if x is Bool and p(x) then "great" true then "false" false then "true" //│ Normalized: //│ > ‹if|while› x is refined Bool and //│ > let tmp:scrut = p³(x³) //│ > tmp:scrut is true then "great" //│ > x is true then "false" //│ > x is false then "true" fun test(x, p) = if x is Object and p(x) then "great" Bool and p(x) then "great, again" true then "false" false then "true" //│ Normalized: //│ > ‹if|while› //│ > x is Object and //│ > let tmp:scrut = p⁴(x⁴) //│ > tmp:scrut is true then "great" //│ > x is refined Bool and //│ > let tmp:scrut = p⁴(x⁴) //│ > tmp:scrut is true then "great, again" //│ > x is true then "false" //│ > x is false then "true" object Foo fun f(arg) = if arg is undefined then "undefined" null then "null" Object then String(arg) //│ Normalized: //│ > ‹if|while› //│ > arg is undefined then "undefined" //│ > arg is null then "null" //│ > arg is Object then member:String⁰(arg⁰) f of undefined //│ = "undefined" f of null //│ = "null" f of Foo //│ = "Foo" fun f(arg) = if arg is Object then String(arg) undefined then "undefined" null then "null" //│ Normalized: //│ > ‹if|while› //│ > arg is Object then member:String⁰(arg¹) //│ > arg is undefined then "undefined" //│ > arg is null then "null" f of undefined //│ = "undefined" f of null //│ = "null" f of Foo //│ = "Foo" fun f(arg) = if arg is Object then String(arg) Foo then "just Foo" //│ Normalized: //│ > ‹if|while› arg is Object then member:String⁰(arg²) import "../../../mlscript-compile/Runtime.mls" fun f(arg) = if arg is Object then String(arg) Runtime.MatchSuccess then "just Foo" //│ Normalized: //│ > ‹if|while› arg is Object then member:String⁰(arg³) fun f(arg) = if arg is Foo then "just Foo" Object then String(arg) //│ Normalized: //│ > ‹if|while› //│ > arg is Foo then "just Foo" //│ > arg is Object then member:String⁰(arg⁴) fun f(arg) = if arg is Int then "Int" Object then String(arg) //│ Normalized: //│ > ‹if|while› //│ > arg is Int then "Int" //│ > arg is Object then member:String⁰(arg⁵) fun f(arg) = if arg is Object then String(arg) Int then "Int" //│ Normalized: //│ > ‹if|while› //│ > arg is Object then member:String⁰(arg⁶) //│ > arg is Int then "Int" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls ================================================ :js data class Pair(a, b) class A class B :sjs x => if x is Pair(A, B) then 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; //│ lambda = (undefined, function (x) { //│ let arg$Pair$0$, arg$Pair$1$; //│ if (x instanceof Pair1.class) { //│ arg$Pair$0$ = x.a; //│ arg$Pair$1$ = x.b; //│ if (arg$Pair$0$ instanceof A1) { //│ if (arg$Pair$1$ instanceof B1) { //│ return 1 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ lambda //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun // :e // FIXME: should be an exhaustiveness error :sjs fun f(x) = if x is Pair(A, A) then 1 Pair(B, B) then 2 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f; //│ f = function f(x) { //│ let arg$Pair$0$, arg$Pair$1$; //│ if (x instanceof Pair1.class) { //│ arg$Pair$0$ = x.a; //│ arg$Pair$1$ = x.b; //│ if (arg$Pair$0$ instanceof A1) { //│ if (arg$Pair$1$ instanceof A1) { //│ return 1 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } else if (arg$Pair$0$ instanceof B1) { //│ if (arg$Pair$1$ instanceof B1) { //│ return 2 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/UnifySubScrutinees.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Option.mls" open Stack open Option data class Pair[A, B](x: A, y: B) fun foo(xs) = if xs is "abc" :: "xyz" :: xs then 0 "abc" :: "uvw" :: xs then 1 Nil then 3 fun sum(acc, xs) = if xs is x :: xs then sum(acc + x, xs) Nil then acc // :ucs normalized fun test(xs) = if xs is Some(Cons("add", Cons(x, Cons(y, Nil)))) then x + y Some(Cons("mul", Cons(x, Cons(y, Nil)))) then x * y Some(Cons("sum", xs)) then sum(0, xs) Some(Nil) then "nothing" None then "nothing" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/normalization/UnifyTupleElements.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option // Normalization should unify fixed-length tuple patterns. fun sum_options(x, y) = if [x, y] is [Some(xv), Some(yv)] then xv + yv [Some(xv), None] then xv [None, Some(yv)] then yv [None, None] then 0 // ========================================================================== // // Normalization should also unify the sub-scrutinees at the same position of // tuple patterns of different lengths. fun foo(xs) = if xs is [Some(x)] then x [Some(y), ..rest] then y fun do_something(x) = "done" fun foo(zs) = if zs is [Some(x), Some(y)] then x + y [None, ..rest] then do_something(rest) :expect 4 foo([Some(1), Some(3)]) //│ = 4 :expect "done" foo([None, Some(1), Some(2)]) //│ = "done" :re foo([Some(0)]) //│ ═══[RUNTIME ERROR] Error: match error :re foo([Some(0), Some(1), Some(2)]) //│ ═══[RUNTIME ERROR] Error: match error :re foo([]) //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/papers/OperatorSplit.mls ================================================ :js fun example(x) = if x |> Math.sign < 0 then "invalid" == 0 then "null" |> Math.abs > 100 then "large" < 10 then "small" else "medium" example(0) //│ = "null" example(-24) //│ = "invalid" :expect "large" example(200) //│ = "large" :expect "small" example(5) //│ = "small" :expect "medium" example(79) //│ = "medium" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/AliasPattern.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option fun map(f) = case Some(x) then Some(f(x)) None as n then n fun map(f) = case Some(x as n) then Some of f(n) None as n then n fun foo = case Some(Some(a as b) as c) as d then [a, b, c, d] foo of Some(Some(42)) //│ = [42, 42, Some(42), Some(Some(42))] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/Compilation.mls ================================================ :js open annotations // * Note: these syntaxes work but, the compilation doesn't support this yet :fixme fun trimStart(str) = if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str //│ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. //│ ║ l.9: if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' //│ ║ l.9: if str is @compile { (" " | "\t") ~ rest } then trimStart(rest) else str //│ ╙── ^^^^ :fixme fun trimStart(str) = if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. //│ ║ l.19: if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' //│ ║ l.19: if str is (@compile ((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╙── ^^^^ :fixme fun trimStart(str) = if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str //│ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. //│ ║ l.29: if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' //│ ║ l.29: if str is @compile ((" " | "\t") ~ rest) then trimStart(rest) else str //│ ╙── ^^^^ // * Note: `@compile(...)` is parsed as a whole annotation :pe :e fun trimStart(str) = if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╔══[PARSE ERROR] Expected start of expression in this position //│ ║ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ║ ^ //│ ╟── found a lone annotation instead //│ ║ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (‹erroneous syntax›). //│ ║ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: rest //│ ║ l.42: if str is (@compile((" " | "\t") ~ rest)) then trimStart(rest) else str //│ ╙── ^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/ConjunctionPattern.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" open Stack abstract class Q abstract class A extends Q abstract class B extends Q // Note: A and B are sibling classes (both extend Q but neither extends the // other), so under the single-inheritance restriction they are provably // disjoint. Conjunction patterns like `A & B` for such classes should // correctly be eliminated by normalization. if new A is A & A & B then 0 B & A & B then 1 A & A & A then 2 else 3 //│ = 2 if new A is A & A & B then 0 B & A & B then 1 A & A & A then 2 else 3 //│ = 2 :sjs fun foo(v) = if v is A & B then 1 else 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let foo; foo = function foo(v) { if (v instanceof A1) { return 0 } return 0; }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun range(i, j) = if i > j then Nil else i :: range(i+1, j) fun suffixes(l) = if l is Nil then Cons(Nil, Nil) l & (_ :: tl) then l :: suffixes(tl) suffixes(range(0, 5)) //│ = Cons( //│ Cons(0, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))), //│ Cons( //│ Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil))))), //│ Cons( //│ Cons(2, Cons(3, Cons(4, Cons(5, Nil)))), //│ Cons( //│ Cons(3, Cons(4, Cons(5, Nil))), //│ Cons(Cons(4, Cons(5, Nil)), Cons(Cons(5, Nil), Cons(Nil, Nil))) //│ ) //│ ) //│ ) //│ ) suffixes(range(0, -1)) //│ = Cons(Nil, Nil) suffixes(range(0, 0)) //│ = Cons(Cons(0, Nil), Cons(Nil, Nil)) if 5 is a & b & c then a+b+c //│ = 15 data class C(a) fun foo(x) = if x is {a:_ & a} & C and a is 1 then 10 a is 2 then 20 else 30 else 40 foo(C(1)) //│ = 10 foo(4) //│ = 40 foo(C(2)) //│ = 20 foo(C("a")) //│ = 30 if C(1) is {:a} & C then a //│ = 1 if C(1) is C & {:a} then a //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/GuardedPatternBindings.mls ================================================ :js :w pattern PlainPair = [x, y] //│ ╔══[WARNING] Unused pattern binding: x. //│ ║ l.5: pattern PlainPair = [x, y] //│ ╙── ^ //│ ╔══[WARNING] Unused pattern binding: y. //│ ║ l.5: pattern PlainPair = [x, y] //│ ╙── ^ // Regression test: variables used in `where` guards should not produce // spurious "Useless pattern binding" warnings. pattern Symmetry = ([x, y] where (x == y)) [1, 1] is Symmetry //│ = true [1, 2] is Symmetry //│ = false // We should check the free variables in `where` guards. :w pattern PairSum = [x, y] where (let x = true, let y = x, y) // We should check the free variables in `where` guards. //│ ╔══[WARNING] Unused pattern binding: x. //│ ║ l.28: pattern PairSum = [x, y] where (let x = true, let y = x, y) //│ ╙── ^ //│ ╔══[WARNING] Unused pattern binding: y. //│ ║ l.28: pattern PairSum = [x, y] where (let x = true, let y = x, y) //│ ╙── ^ :w pattern PairSum = [x, y] where (if x is [y] then y) //│ ╔══[WARNING] Unused pattern binding: y. //│ ║ l.38: pattern PairSum = [x, y] where (if x is [y] then y) //│ ╙── ^ :w pattern PairSum = [x, y] where (class C(y) with { fun test = y }, C(x).test) //│ ╔══[WARNING] Unused pattern binding: y. //│ ║ l.44: pattern PairSum = [x, y] where (class C(y) with { fun test = y }, C(x).test) //│ ╙── ^ :w pattern PairSum = [x, y] where (fun y = 1, print(y), x) //│ ╔══[WARNING] Unused pattern binding: y. //│ ║ l.50: pattern PairSum = [x, y] where (fun y = 1, print(y), x) //│ ╙── ^ // Forward reference to `y` should not be counted as a free variable :w pattern PairSum = [x, y] where (print(y), fun y = 1, x) //│ ╔══[WARNING] Unused pattern binding: y. //│ ║ l.57: pattern PairSum = [x, y] where (print(y), fun y = 1, x) //│ ╙── ^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/Literals.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option data class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(1) then true else false [f(Some(1)), f(None), f(Some(2)), f(Some(-1))] //│ = [true, false, false, false] fun f(x) = if x is Some(1) then true fun f(x) = if x is Some(1) then true Some(2) then false [f(Some(1)), f(Some(2))] //│ = [true, false] fun g(x) = if x then 1 else 2 // :e fun test_must_be_boolean(x) = if 0 then 1 else 2 fun g(x) = if x is true then 1 else 2 :expect [1, 2, 2] [g(true), g(false), g(None)] //│ = [1, 2, 2] fun g(x) = if x && true is true then 1 else 2 fun h(x) = if (x as Bool) then 1 else 2 fun mix(x) = if x is true then "true" Some(value) then "Some" 0 then "zero" [mix(true), mix(Some(1)), mix(0)] //│ = ["true", "Some", "zero"] :re [mix(false), mix(None)] //│ ═══[RUNTIME ERROR] Error: match error fun string_literals(x) = if x is "foo" then 0 "bar" then 1 "qax" then 2 class Foo fun mixed_patterns(x) = if x is Foo then 1 23 then 2 "test" then 3 fun bool_patterns(x) = if x is true then 1 false then 2 fun dual_patterns(x, y) = if x is "some" and y is "none" then 0 x is "none" and y is "some" then 1 x is "some" and y is "some" then 2 x is "none" and y is "none" then 3 :fixme // TODO () is () //│ ╔══[COMPILATION ERROR] Unrecognized pattern (unit). //│ ║ l.78: () is () //│ ╙── ^^ //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/NamePattern.mls ================================================ :js fun id(x) = x :ucs desugared if id(0) is t then t //│ Split with nested patterns: //│ > if //│ > let tmp:scrut = member:id⁰(0) //│ > tmp:scrut is t then t⁰ //│ Expanded split with flattened patterns: //│ > ‹if|while› //│ > let tmp:scrut = member:id⁰(0) //│ > let t = tmp:scrut⁰ //│ > else t⁰ //│ = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/Parameters.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option { Some, None } class Foo(a: Int, val b: Int) :e fun foo(x) = if x is Foo(p, q) then p + q //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.10: fun foo(x) = if x is Foo(p, q) then p + q //│ ║ ^ //│ ╟── because the corresponding parameter `a` is not publicly accessible //│ ║ l.7: class Foo(a: Int, val b: Int) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible :re foo(Foo(2, 3)) //│ ═══[RUNTIME ERROR] Error: match error fun foo(x) = if x is Foo(_, q) then q foo(Foo(2, 3)) //│ = 3 class SecretPair(a: Int, val b: Int) // Binding the overall constructor output with `as result` should still be allowed // when all constructor arguments preserve their scrutinees, even if some // private fields are matched with `_`. fun keepSecret(x) = if x is (SecretPair(_, q)) as result then q keepSecret(SecretPair(2, 3)) //│ = 3 :e fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.40: fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result //│ ║ ^ //│ ╟── because the corresponding parameter `a` is not publicly accessible //│ ║ l.29: class SecretPair(a: Int, val b: Int) //│ ║ ^ //│ ╟── because rebuilding the matched `SecretPair` value requires reading every constructor argument //│ ║ l.40: fun rebuildSecret(x) = if x is (SecretPair(_, (q => q + 1))) as result then result //│ ║ ^^^^^^^^^^ //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible class Test(a, b, c, d, e, f) fun test(x) = if x is Test then "ok" :e fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `a` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `b` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `c` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `d` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `e` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.57: fun test(x) = if x is Test(p, q, r, s, t, u) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `f` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible :e fun test(x) = if x is Test(_, _, _) then "ok" //│ ╔══[COMPILATION ERROR] Expected six arguments, but found only three arguments. //│ ║ l.108: fun test(x) = if x is Test(_, _, _) then "ok" //│ ╙── ^^^^^^^ fun test(x) = if x is Test(_, _, _, _, _, _) then "ok" :e fun test(x) = if x is Test(_, _, _, a, _, _) then "ok" //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.116: fun test(x) = if x is Test(_, _, _, a, _, _) then "ok" //│ ║ ^ //│ ╟── because the corresponding parameter `d` is not publicly accessible //│ ║ l.52: class Test(a, b, c, d, e, f) //│ ║ ^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible class Point(val x: Int, val y: Int) fun point(p) = if p is Point(x, y) then x + y point(Point(2, 4)) //│ = 6 object Main with fun run(input) = if input is Data(count) then 0 data class Data(value: Int) // Validation on object patterns object A :e fun foo(x) = if x is A() then 0 //│ ╔══[COMPILATION ERROR] `A` is an object. //│ ║ l.139: object A //│ ║ ^ //│ ╟── Its pattern cannot have an argument list. //│ ║ l.142: fun foo(x) = if x is A() then 0 //│ ╙── ^ :e fun foo(x) = if x is A(y) then y //│ ╔══[COMPILATION ERROR] `A` is an object. //│ ║ l.139: object A //│ ║ ^ //│ ╟── Its pattern cannot have arguments. //│ ║ l.151: fun foo(x) = if x is A(y) then y //│ ╙── ^^^ 0 is Foo //│ = false data class Pair[A, B](a: A, b: B) pattern PairLike(a, b) = [a, b] | Pair(a, b) fun foo(x) = if x is PairLike(a, b) then Some(a + b) else None foo(Pair(1, 2)) //│ = Some(3) foo([1, 2]) //│ = Some(3) foo([]) //│ = None pattern AtPrec = ( "+" => 2 ) | ( "*" => 5 ) fun f(x) = if x is str as AtPrec as prec then [str, prec] f("+") //│ = ["+", 2] f("*") //│ = ["*", 5] pattern Op(str, prec) = str as AtPrec as prec if "+" is Op as p then p //│ = ["+", 2] if "+" is Op(s, p) then [s, p] //│ = ["+", 2] pattern Crazy(a, b, c, d, e, f, g) = a as b as c as d as e as f as g if 42 is Crazy(a, b, c, d, e, f, g) then print(a, b, c, d, e, f, g) //│ > 42 42 42 42 42 42 42 let p = Pair(42, 42) //│ p = Pair(42, 42) if p is Crazy(a, b, c, d, e, f, g) then p === a and a === b and b === c and c === d and d === e and e === f and f === g //│ = true :e :re if p is Crazy(a, b, c, d, e, f) then 42 //│ ╔══[COMPILATION ERROR] Expected seven extraction arguments, but found only six arguments. //│ ║ l.209: if p is Crazy(a, b, c, d, e, f) then 42 //│ ╙── ^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error :e :re if p is Crazy(all) then 42 //│ ╔══[COMPILATION ERROR] Expected seven extraction arguments, but found only one argument. //│ ║ l.217: if p is Crazy(all) then 42 //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] Error: match error if p is Crazy as all then all //│ = [ //│ Pair(42, 42), //│ Pair(42, 42), //│ Pair(42, 42), //│ Pair(42, 42), //│ Pair(42, 42), //│ Pair(42, 42), //│ Pair(42, 42) //│ ] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/RecordPattern.mls ================================================ :js fun take_1(p) = if p is { x: a, y: b } then a + b else 0 fun take_2(p) = if p is { :x, :y } then x + y else 0 take_1({x: 0, y: 10}) //│ = 10 take_1({t: 5, x: 0, y: 10, z: 42}) //│ = 10 take_2({x: 0, y: 10}) //│ = 10 take_2({t: 5, x: 0, y: 10, z: 42}) //│ = 10 take_1({x:"a", y:"b"}) //│ = "ab" let r = {z:4} take_1(r) //│ = 0 //│ r = {z: 4} let f = case { a: 1 as a } then a //│ f = fun f(a: 1) //│ = 1 // * It would probably be surprising to bind `a` in the RHS here :e let f = case { a: 1 } then a //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.44: { a: 1 } then a //│ ╙── ^ //│ f = fun // * OTOH, note that `a` is bound here let rcd = a: 1 b: a + 1 //│ rcd = {a: 1, b: 2} // * As that will make sense when we have dependent types, // * as in: `{ n: Int, v: Vec[Int, n] }` let r = {x: 2, y : 4} //│ r = {x: 2, y: 4} :e if r is {x:a, y:a} then a //│ ╔══[COMPILATION ERROR] Duplicated pattern variable. //│ ║ l.64: {x:a, y:a} then a //│ ║ ^ //│ ╟── The previous definition is as follows. //│ ║ l.64: {x:a, y:a} then a //│ ╙── ^ //│ = 4 let rr = {x: {y:10, z:100}, t:1000} if rr is {x : {:y, z:t}} then y+t //│ = 110 //│ rr = {x: {y: 10, z: 100}, t: 1000} if rr is { "x": x } then x //│ = {y: 10, z: 100} if rr is { 'x: x } then x //│ = {y: 10, z: 100} let f = case { a: 1 & a } then a //│ f = fun let f = case { :a } then a //│ f = fun f(a: 1) //│ = 1 import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/Stack.mls" open Option { Some, None } open Stack Some(42) is { value: 42 } //│ = true 1 :: 2 :: 3 :: Nil is { tail: { tail: Nil } } //│ = false 1 :: 2 :: 3 :: Nil is { tail: { tail: _ } } //│ = true 1 :: 2 :: 3 :: Nil is { tail: { tail: { tail: Nil } } } //│ = true 1 :: 2 :: 3 :: Nil is { head: 42 } //│ = false fun pairwise(xs) = if xs is { head: a, tail: { head: b, tail: xs' } } then [a, b] :: pairwise(xs') else Nil pairwise(49) //│ = Nil pairwise("hello" :: "world" :: "bonjour" :: "le monde" :: Nil) //│ = Cons(["hello", "world"], Cons(["bonjour", "le monde"], Nil)) pairwise(1 :: 2 :: 3 :: Nil) //│ = Cons([1, 2], Nil) fun foo(xs) = if xs is { tail: { tail: { tail: _ } } } then "at least three of them" { tail: { tail: Nil } } then "there are two of them" { tail: Nil } then "only one left" else "yeah we're done" foo(Nil) //│ = "yeah we're done" foo(999) //│ = "yeah we're done" foo(1 :: Nil) //│ = "only one left" foo(1 :: "this is invalid") //│ = "yeah we're done" foo(1 :: 2 :: Nil) //│ = "there are two of them" foo(1 :: 2 :: 4 :: Nil) //│ = "at least three of them" foo(1 :: 2 :: 4 :: 8 :: Nil) //│ = "at least three of them" fun should_reject_null(x) = if x is { a: 0 } then 42 else -42 should_reject_null(null) //│ = -42 fun is_object(x) = if x is {} then true else false is_object(null) //│ = false is_object(Object) //│ = true is_object(0) //│ = false is_object("") //│ = false is_object(Nil) //│ = true is_object([1, 2]) //│ = true is_object(is_object) //│ = true fun is_record_alt(x) = x is {} is_record_alt of [] //│ = true is_record_alt of 0 //│ = false is_object("") //│ = false is_object(Nil) //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/Refinement.mls ================================================ :ignore :ucs normalized import "../../../mlscript-compile/Option.mls" open Option // OK x => if x is refined(None) then x //│ ╔══[COMPILATION ERROR] Name not found: refined //│ ║ l.10: refined(None) then x //│ ╙── ^^^^^^^ //│ ═══[COMPILATION ERROR] Cannot use this ‹error› as a pattern. //│ Normalized: //│ > ‹if|while› // OK x => if x is refined(Some) then x //│ ╔══[COMPILATION ERROR] Name not found: refined //│ ║ l.20: refined(Some) then x //│ ╙── ^^^^^^^ //│ ═══[COMPILATION ERROR] Cannot use this ‹error› as a pattern. //│ Normalized: //│ > ‹if|while› // Should warn about the inconsistent refinement x => if x is refined(None) then x Some then x //│ ╔══[COMPILATION ERROR] Name not found: refined //│ ║ l.30: refined(None) then x //│ ╙── ^^^^^^^ //│ ═══[COMPILATION ERROR] Cannot use this ‹error› as a pattern. //│ Normalized: //│ > ‹if|while› x is Option.Some then x⁰ x => if x is refined(None) then x refined(Some) then x //│ ╔══[COMPILATION ERROR] Name not found: refined //│ ║ l.40: refined(None) then x //│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: refined //│ ║ l.41: refined(Some) then x //│ ╙── ^^^^^^^ //│ ═══[COMPILATION ERROR] Cannot use this ‹error› as a pattern. //│ ═══[COMPILATION ERROR] Cannot use this ‹error› as a pattern. //│ Normalized: //│ > ‹if|while› ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls ================================================ :js :sjs fun nonsense(xs) = if xs is [..ys] then ys [] then "empty" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let nonsense; //│ nonsense = function nonsense(xs) { //│ let ys, middleElements; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 0) { //│ middleElements = runtime.Tuple.slice(xs, 0, 0); //│ ys = middleElements; //│ return ys //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— nonsense([]) //│ = [] nonsense([1, 2, 3, 4]) //│ = [1, 2, 3, 4] :sjs fun lead_and_last(xs) = if xs is [x, ..ys, y] then x + y [] then 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lead_and_last; //│ lead_and_last = function lead_and_last(xs) { //│ let x, y, lastElement1$, element0$; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { //│ element0$ = runtime.Tuple.get(xs, 0); //│ runtime.Tuple.slice(xs, 1, 1); //│ lastElement1$ = runtime.Tuple.get(xs, -1); //│ y = lastElement1$; //│ x = element0$; //│ return x + y //│ } else if (runtime.Tuple.isArrayLike(xs) && xs.length === 0) { //│ return 0 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— lead_and_last(["foo", "bar"]) //│ = "foobar" lead_and_last([1, 2, 3, 4]) //│ = 5 lead_and_last([]) //│ = 0 :re lead_and_last(["boom"]) //│ ═══[RUNTIME ERROR] Error: match error :sjs fun nested_tuple_patterns(xs) = if xs is [x, ..[y, z], w] then x + y + z + w [] then 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let nested_tuple_patterns; //│ nested_tuple_patterns = function nested_tuple_patterns(xs) { //│ let x, y, w, z, lastElement1$, middleElements, element0$, element1$, element0$1, tmp6, tmp7; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { //│ element0$ = runtime.Tuple.get(xs, 0); //│ middleElements = runtime.Tuple.slice(xs, 1, 1); //│ lastElement1$ = runtime.Tuple.get(xs, -1); //│ if (runtime.Tuple.isArrayLike(middleElements) && middleElements.length === 2) { //│ element0$1 = runtime.Tuple.get(middleElements, 0); //│ element1$ = runtime.Tuple.get(middleElements, 1); //│ w = lastElement1$; //│ z = element1$; //│ y = element0$1; //│ x = element0$; //│ tmp6 = x + y; //│ tmp7 = tmp6 + z; //│ return tmp7 + w //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } else if (runtime.Tuple.isArrayLike(xs) && xs.length === 0) { //│ return 0 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— nested_tuple_patterns of [1, 2, 3, 4] //│ = 10 fun hack(tupleSlice) = if tupleSlice is [..tupleGet, x] then x :expect 4 hack([1, 2, 3, 4]) //│ = 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/SimpleTuple.mls ================================================ :js if [1, 1] is [x, y] and x == y then "equal" //│ = "equal" fun sum(x, y) = x + y sum(1, 2) //│ = 3 :fixme // TODO fun sum([x, y]) = x + y sum([1, 2]) //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple //│ ║ l.15: fun sum([x, y]) = x + y //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.15: fun sum([x, y]) = x + y //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: y //│ ║ l.15: fun sum([x, y]) = x + y //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Expected 0 arguments, got 1 //│ ║ l.16: sum([1, 2]) //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun sum(pair) = if pair is [x, y] then x + y sum([1, 2]) //│ = 3 fun test(thing) = if thing is [] then 0 :re test("") //│ ═══[RUNTIME ERROR] Error: match error :re test(12) //│ ═══[RUNTIME ERROR] Error: match error data class Point(x: Int, y: Int) fun with_other_constructors(thing) = if thing is [x, y] then x * y Point(x, y) then x + y :expect 7 with_other_constructors(Point(3, 4)) //│ = 7 :expect 12 with_other_constructors([3, 4]) //│ = 12 // A workaround is to move the tuple pattern to the last case. fun with_other_constructors(thing) = if thing is Point(x, y) then x + y [x, y] then x * y :expect 7 with_other_constructors(Point(3, 4)) //│ = 7 :expect 12 with_other_constructors([3, 4]) //│ = 12 fun with_else(x) = if x is [a, b, c] then a + b + c else 0 with_else([1, 2, 3]) //│ = 6 with_else([1, 2]) //│ = 0 fun match_against_different_length(xs) = if xs is [] then 0 [x] then x + 1 [x, y] then x + y + 2 [x, y, z] then x + y + z + 3 :expect 0 match_against_different_length([]) //│ = 0 :expect 18 match_against_different_length([17]) //│ = 18 :expect 22 match_against_different_length([9, 11]) //│ = 22 :expect 42 match_against_different_length([13, 13, 13]) //│ = 42 import "../../../mlscript-compile/Option.mls" open Option fun with_the_common_prefix(xs) = if xs is [Some(x)] then x + 1 [None] then 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/SpreadTrailingIndex.mls ================================================ :js // Regression test: spread destructuring with different trailing sizes should // correctly index elements from the end of the array. fun go(stack) = if stack is [..stack, "+", "+"] then 0 [..stack2, Str] then 1 else print("Str??", stack is [..stack2, Str]) go([1, 2, "z"]) //│ = 1 // When the last two elements are "+", branch 0 matches go([1, "+", "+"]) //│ = 0 // Fallback to else branch go([1, 2, 3]) //│ > Str?? false // Single element array go(["hello"]) //│ = 1 // Same with `...` (spread rest) fun go2(stack) = if stack is [...stack, "+", "+"] then 0 [...stack2, Str] then 1 else 2 go2([1, 2, "z"]) //│ = 1 go2([1, "+", "+"]) //│ = 0 go2([1, 2, 3]) //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/String.mls ================================================ :js fun parseIntegerSimple(str) = if str is "0x" ~ body then parseInt(body, 16) "0b" ~ body then parseInt(body, 2) "0o" ~ body then parseInt(body, 8) else parseInt(str, 10) :expect 65535 parseIntegerSimple("0xFFFF") //│ = 65535 :expect 255 parseIntegerSimple("0b11111111") //│ = 255 :expect 4095 parseIntegerSimple("0o7777") //│ = 4095 :expect 7777 parseIntegerSimple("07777") //│ = 7777 fun trimStart(str) = if str is (" " | "\t") ~ rest then trimStart(rest) else str :expect "up" trimStart of " up" //│ = "up" :expect "" trimStart of "" //│ = "" :expect "hello" trimStart of "\thello" //│ = "hello" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/patterns/where.mls ================================================ :js data class Pair[A, B](fst: A, snd: B) fun orderedPair(p) = p is Pair(a, b) where a <= b orderedPair(Pair(4, 5)) //│ = true orderedPair(4) //│ = false orderedPair(Pair(1, 1)) //│ = true orderedPair(Pair(2, 1)) //│ = false fun foo(p) = p is Pair(a, b) where let c = a a == 0 foo(Pair(5, 5)) //│ = false foo(Pair(0, 0)) //│ = true foo(Pair(0, 4)) //│ = true foo(Pair(4, 0)) //│ = false // Should we report `a` and `b` as inconsistent variables? fun bar(p) = p is (Pair(a, b) where a > b) | Int bar(4) //│ = true bar(Pair(4, 5)) //│ = false bar(Pair(4, 3)) //│ = true :e fun baz(p) = p is (Pair(a, b) | Int) where a > b //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.56: (Pair(a, b) | Int) where a > b //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: b //│ ║ l.56: (Pair(a, b) | Int) where a > b //│ ╙── ^ pattern Positive = ((Int as x) where x > 0) => x 0 is Positive //│ = false 1 is Positive //│ = true pattern OrderedPair = (Pair(a, b) where a < b) => b - a :expect true Pair(1, 2) is OrderedPair as 1 //│ = true :expect false Pair(3, 2) is OrderedPair //│ = false :expect true Pair(42, 96) is OrderedPair as 54 //│ = true pattern OrderedPairLike = (Pair(a, b) | [a, b] where a < b) => b - a :expect [true, false, true] tuple of Pair(1, 2) is OrderedPairLike as 1 Pair(3, 2) is OrderedPairLike Pair(42, 96) is OrderedPairLike as 54 //│ = [true, false, true] :expect [true, false, true] tuple of [1, 2] is OrderedPairLike as 1 [3, 2] is OrderedPairLike [42, 96] is OrderedPairLike as 54 //│ = [true, false, true] :fixme // TODO fun get(p, m) = if p is Pair(k, v) where m.get(k) is w then v === w else false //│ ╔══[COMPILATION ERROR] Name not found: w //│ ║ l.108: Pair(k, v) where m.get(k) is w then v === w //│ ╙── ^ :fixme // TODO? fun get(p, m) = p is Pair(k, v) where m.get(k) is w and v === w //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'where' here //│ ║ l.116: p is Pair(k, v) where m.get(k) is w and v === w //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :fixme // TODO? fun get(p, m) = p is Pair(k, v) where m.get(k) is w where v === w //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'where' here //│ ║ l.123: p is Pair(k, v) where m.get(k) is w where v === w //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let m0 = {get: _ + 1} //│ m0 = {get: fun get} class C1(m) with pattern P = Pair(k, v) where m.get(k) is w and v === w C1(m0).P.unapply(Pair(3, 4)) //│ = MatchSuccess(Pair(3, 4), null) C1(m0).P.unapply(Pair(3, 5)) //│ = MatchFailure(null) :e class C2(m) with pattern P = Pair(k, v) where m.get(k) is w where v === w //│ ╔══[COMPILATION ERROR] Name not found: w //│ ║ l.145: pattern P = Pair(k, v) where m.get(k) is w where v === w //│ ╙── ^ :re C2(m0).P.unapply(Pair(3, 4)) //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :re C2(m0).P.unapply(Pair(3, 5)) //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. class C3(m) with pattern P = Pair(k, v) where m.get(k) is (w where v === w) C3(m0).P.unapply(Pair(3, 4)) //│ = MatchSuccess(Pair(3, 4), null) C3(m0).P.unapply(Pair(3, 5)) //│ = MatchFailure(null) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/Unapply.mls ================================================ :NewDefs class Point(x: Int, y: Int, z: Int) //│ class Point(x: Int, y: Int, z: Int) fun f_0(p) = if p is Point(x, _, z) then x + z //│ fun f_0: Point -> Int f_0(Point(1, 2, 3)) //│ Int //│ res //│ = 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/ConflictedCoveredCases.mls ================================================ :NewDefs class A() class B() extends A() //│ class A() //│ class B() extends A fun p(x) = true //│ fun p: anything -> true :w :ducs:normalize.result fun f(x) = if x is B and x is A then 1 x is A then 31 //│ Normalized UCS term: //│ case x*‡ of //│ B*◊ -> 1 //│ _ -> //│ case x*‡ of //│ A*◊ -> 31 //│ ╔══[WARNING] the pattern always matches //│ ║ l.15: x is A then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against B //│ ║ l.14: x is B and //│ ║ ^ //│ ╟── which is a subtype of A //│ ║ l.4: class B() extends A() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: (A & ~#B | B) -> (1 | 31) // FIXME: wrong missing cases :w :ducs:normalize.result fun f(x) = if x is 1 and x is Int then true x is 2 then false //│ Normalized UCS term: //│ case x*‡ of //│ 1 -> true //│ _ -> //│ case x*‡ of //│ 2 -> false //│ ╔══[WARNING] the pattern always matches //│ ║ l.39: x is Int then true //│ ║ ^^^ //│ ╟── the scrutinee was matched against 1 //│ ║ l.38: x is 1 and //│ ║ ^ //│ ╟── which is a subtype of Int //│ ║ l.39: x is Int then true //│ ╙── ^^^ //│ ╔══[ERROR] `x` has 1 missing case //│ ║ l.38: x is 1 and //│ ║ ^ //│ ╟── it can be class `Int` //│ ║ l.39: x is Int then true //│ ╙── ^^^ //│ fun f: (1 | 2) -> Bool :w fun f(x) = if x is B and x is A then 1 p(x) then 2 x is A then 31 x is B then 3 else 4 //│ ╔══[WARNING] the pattern always matches //│ ║ l.67: x is A then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against B //│ ║ l.66: x is B and //│ ║ ^ //│ ╟── which is a subtype of A //│ ║ l.4: class B() extends A() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: (B | Object & ~#B) -> (1 | 31 | 4) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/ConflictedPatterns.mls ================================================ :NewDefs // This test file contains the cases where inner cases are conflicting with outer cases. class X class Y class Z //│ class X { //│ constructor() //│ } //│ class Y { //│ constructor() //│ } //│ class Z { //│ constructor() //│ } :w :ducs:normalize.result fun f(x) = if x is X and x is Y then 1 else 2 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.21: x is X and x is Y then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.21: x is X and x is Y then 1 //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.21: x is X and x is Y then 1 //│ ╙── ^ //│ fun f: Object -> 2 :w :ducs:normalize.result fun f(x) = if x is X and x is Y then 1 x is Y then 2 else 3 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.43: x is Y then 1 //│ ╙── ^ //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.44: x is Y then 2 //│ ╙── ^ //│ fun f: Object -> 3 :w :ducs:normalize.result fun f(x) = if x is X and x is Y then 1 x is Z then 2 else 3 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.74: x is Y then 1 //│ ╙── ^ //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── which is unrelated with Z //│ ║ l.75: x is Z then 2 //│ ╙── ^ //│ fun f: Object -> 3 :w :ducs:normalize.result fun f(x) = if x is X and x is Y and x is Z then 1 else 2 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.104: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.104: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.105: x is Y and //│ ╙── ^ //│ fun f: Object -> 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/CoveredCases.mls ================================================ :NewDefs // This test file contains the cases where latter cases are covered by the former cases. // B <: A class A() class B() extends A() //│ class A() //│ class B() extends A :w fun f(x) = if x is A then 1 x is A then 2 //│ ╔══[WARNING] found a duplicated case //│ ║ l.14: x is A then 2 //│ ║ ^ //│ ╟── there is an identical pattern A //│ ║ l.13: x is A then 1 //│ ╙── ^ //│ fun f: A -> 1 :w :ducs:normalize.result fun f(x) = if x is A then 1 x is B then 2 //│ Normalized UCS term: //│ case x*‡ of //│ A*◊ -> 1 //│ ╔══[WARNING] found a duplicated case //│ ║ l.27: x is B then 2 //│ ║ ^ //│ ╟── the case is covered by pattern A //│ ║ l.26: x is A then 1 //│ ║ ^ //│ ╟── due to the subtyping relation //│ ║ l.7: class B() extends A() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: A -> 1 :ducs:normalize.result fun f(x) = if x is A and x is B then 1 //│ Normalized UCS term: //│ case x*‡ of //│ refined A*◊ -> //│ case x*‡ of //│ B*◊ -> 1 //│ fun f: B -> 1 // :w // FIXME :ducs:normalize.result fun f(x) = if x is A and x is B then 1 x is B then 2 //│ Normalized UCS term: //│ case x*‡ of //│ refined A*◊ -> //│ case x*‡ of //│ B*◊ -> 1 //│ fun f: B -> 1 fun p(x) = true: Bool //│ fun p: anything -> Bool // :w // FIXME :ducs:normalize.result fun f(x) = if x is A and x is B then 1 p(x) then 2 x is B then 3 else 4 //│ Normalized UCS term: //│ case x*‡ of //│ refined A*◊ -> //│ case x*‡ of //│ B*◊ -> 1 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> 2 //│ _ -> 4 //│ _ -> 4 //│ fun f: Object -> (1 | 2 | 4) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/DuplicatedCases.mls ================================================ :NewDefs // This test file contains the cases containing cases with identical patterns. :w :ducs:normalize.result fun f(x) = if x is "a" then 1 "b" then 2 "a" then 3 //│ Normalized UCS term: //│ case x*‡ of //│ "a" -> 1 //│ _ -> //│ case x*‡ of //│ "b" -> 2 //│ ╔══[WARNING] found a duplicated case //│ ║ l.10: "a" then 3 //│ ║ ^^^ //│ ╟── there is an identical pattern "a" //│ ║ l.8: "a" then 1 //│ ╙── ^^^ //│ fun f: ("a" | "b") -> (1 | 2) class X class Y //│ class X { //│ constructor() //│ } //│ class Y { //│ constructor() //│ } :w :ducs:normalize.result fun f(x) = if x is X then 1 X then 2 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 1 //│ ╔══[WARNING] found a duplicated case //│ ║ l.38: X then 2 //│ ║ ^ //│ ╟── there is an identical pattern X //│ ║ l.37: X then 1 //│ ╙── ^ //│ fun f: X -> 1 :w :ducs:normalize.result fun f(x) = if x is X then 1 Y then 2 X then 3 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 1 //│ _ -> //│ case x*‡ of //│ Y*◊ -> 2 //│ ╔══[WARNING] found a duplicated case //│ ║ l.55: X then 3 //│ ║ ^ //│ ╟── there is an identical pattern X //│ ║ l.53: X then 1 //│ ╙── ^ //│ fun f: (X | Y) -> (1 | 2) class Box[T](value: T) //│ class Box[T](value: T) :ducs:normalize.result fun f(x) = if x is Box(1) then true Box then false //│ Normalized UCS term: //│ case x*‡ of //│ Box*◊ -> //│ let ucs$args_x$Box*† = (Box).unapply(x,) //│ let x$Box_0*‡ = (ucs$args_x$Box).0 //│ case x$Box_0*‡ of //│ 1 -> true //│ _ -> false //│ fun f: Box[Object] -> Bool f(Box(0)) f(Box(1)) //│ Bool //│ res //│ = false //│ res //│ = true :ducs:postprocess.result fun a_tale_of_scrutinees(x, y) = if x is "A" and y is "B" then "AB" y is "A" and x is "B" then "BA" y is "A" and x is "A" then "AA" x is "B" and y is "B" then "BB" //│ Post-processed UCS term: //│ case x*‡ of //│ "A" -> //│ case y*‡ of //│ "B" -> "AB" //│ "A" -> "AA" //│ "B" -> //│ case y*‡ of //│ "A" -> "BA" //│ "B" -> "BB" //│ fun a_tale_of_scrutinees: ("A" | "B", "A" | "B") -> ("AA" | "AB" | "BA" | "BB") :ducs:normalize.result fun test(x, p) = if x is Bool and p(x) then "great" true then "false" false then "true" //│ Normalized UCS term: //│ case x*‡ of //│ refined Bool*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "great" //│ _ -> //│ case x*‡ of //│ true*† -> "false" //│ _ -> //│ case x*‡ of //│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") class P[A](x: A) class Q[A, B](x: A, y: B) //│ class P[A](x: A) //│ class Q[A, B](x: A, y: B) fun f(x) = if x is P(1) then 1 P(y) then 2 //│ fun f: P[Object] -> (1 | 2) :ducs:normalize.result fun f(x) = if x is Q(a, b) and a is 1 and b is 1 then 1 Q(a, b) and b is 1 then 2 //│ Normalized UCS term: //│ case x*‡ of //│ Q*◊ -> //│ let ucs$args_x$Q*† = (Q).unapply(x,) //│ let a*‡ = (ucs$args_x$Q).0 //│ let b*‡ = (ucs$args_x$Q).1 //│ case a*‡ of //│ 1 -> //│ case b*‡ of //│ 1 -> 1 //│ _ -> //│ case b*‡ of //│ 1 -> 2 //│ fun f: Q[Object, 1] -> (1 | 2) :e fun f(x) = if x is Q(a, b) and a is 1 and b is 2 then 1 Q(a, b) and b is 1 then 2 //│ ╔══[ERROR] when `x` is `Q` //│ ║ l.167: Q(a, b) and a is 1 and b is 2 then 1 //│ ║ ^ //│ ╟── the second argument of `Q` has 1 missing case //│ ║ l.168: Q(a, b) and b is 1 then 2 //│ ║ ^ //│ ╟── it can be literal 2 //│ ║ l.167: Q(a, b) and a is 1 and b is 2 then 1 //│ ╙── ^ //│ fun f: Q[Object, 1] -> (1 | 2) :ducs:normalize.result fun f(x) = if x is Q(1, 1) then 1 Q(y, 1) then 2 //│ Normalized UCS term: //│ case x*‡ of //│ Q*◊ -> //│ let ucs$args_x$Q*† = (Q).unapply(x,) //│ let x$Q_0*‡ = (ucs$args_x$Q).0 //│ let x$Q_1*‡ = (ucs$args_x$Q).1 //│ case x$Q_0*‡ of //│ 1 -> //│ case x$Q_1*‡ of //│ 1 -> 1 //│ _ -> (ucs$args_x$Q).0 //│ _ -> //│ let y*‡ = (ucs$args_x$Q).0 //│ case x$Q_1*‡ of //│ 1 -> 2 //│ fun f: forall 'a. Q[Object & 'a, 1] -> (1 | 2 | 'a) fun f(x) = if x is Q(0, 0) then 1 Q(1, 1) then 2 Q(y, 1) then 3 _ then 4 //│ fun f: (Object & ~#Q | Q[Object, Object]) -> (1 | 2 | 3 | 4) fun f(x) = if x is P(P(P(1))) then 1 P(P(1)) then 2 P(1) then 3 //│ fun f: P[1 | P[1 | P[1]]] -> (1 | 2 | 3) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/MissingCases.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun good_add_1(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ fun good_add_1: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) :e fun bad_add_missing_SS(x, y) = if x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.24: x is None and y is Some(yv) then yv //│ ╙── ^^^^ //│ fun bad_add_missing_SS: forall 'a. (None | Some['a], None) -> (0 | 'a) :e fun bad_add_missing_SN(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.42: x is None and y is None then 0 //│ ╙── ^^^^ //│ fun bad_add_missing_SN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) :e fun bad_add_missing_NS(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `None` //│ ║ l.59: x is None and y is None then 0 //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.59: x is None and y is None then 0 //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.57: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun bad_add_missing_NS: (None | Some[Int], None) -> Int :e fun bad_add_missing_NN(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv //│ ╔══[ERROR] when `x` is `None` //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.75: x is Some(xv) and y is None then xv //│ ╙── ^^^^ //│ fun bad_add_missing_NN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) :e fun bad_add_missing_SS_NN(x, y) = if x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `None` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ╙── ^^^^ //│ fun bad_add_missing_SS_NN: forall 'a. (None | Some['a], nothing) -> 'a :e fun bad_add_missing_SN_NS(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.117: x is None and y is None then 0 //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `None` //│ ║ l.117: x is None and y is None then 0 //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.117: x is None and y is None then 0 //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun bad_add_missing_SN_NS: (None | Some[Int], nothing) -> Int fun actually_fine_add(x, y) = if x is Some(xv) and y is None then xv //│ fun actually_fine_add: forall 'a. (Some['a], None) -> 'a ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/Refinement.mls ================================================ :NewDefs class Point(x: Int, y: Int) abstract class Shape: (Circle | Rectangle | LineSegment) class Circle(center: Point, radius: Int) extends Shape class Rectangle(position: Point, width: Int, height: Int) extends Shape class LineSegment(start: Point, end: Point) extends Shape //│ class Point(x: Int, y: Int) //│ abstract class Shape: Circle | LineSegment | Rectangle //│ class Circle(center: Point, radius: Int) extends Shape //│ class Rectangle(position: Point, width: Int, height: Int) extends Shape //│ class LineSegment(start: Point, end: Point) extends Shape fun hidden(p) = if p is Circle then true else false //│ fun hidden: Object -> Bool fun this_is_sealed(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" LineSegment(_, _) then "LineSegment" //│ fun this_is_sealed: (x: Shape) -> ("Circle" | "LineSegment" | "Rectangle") // The error message here makes some sense right now. :e fun missing_a_case(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.27: if x is //│ ║ ^^^^ //│ ║ l.28: Circle(_, _) then "Circle" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.29: Rectangle(_, _, _) then "Rectangle" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Circle | LineSegment | Rectangle` does not match type `Circle | Rectangle` //│ ║ l.26: fun missing_a_case(x: Shape) = //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `Circle | Rectangle` //│ ║ l.27: if x is //│ ╙── ^ //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle") fun missing_a_case(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" else x //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle" | LineSegment) fun countLineSegments(x) = if x is Shape and hidden(x) then "1" Rectangle(_, _, _) then "2" LineSegment(_, _) then "3" Circle(_, _) then "4" //│ fun countLineSegments: (Circle | LineSegment | Rectangle) -> ("1" | "2" | "3" | "4") ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/SealedClasses.mls ================================================ :NewDefs abstract class Term: Abs | App | Var class Var(name: Str) extends Term class Abs(param: Str, body: Term) extends Term class App(func: Term, arg: Term) extends Term //│ abstract class Term: Abs | App | Var //│ class Var(name: Str) extends Term //│ class Abs(param: Str, body: Term) extends Term //│ class App(func: Term, arg: Term) extends Term :ducs:desugar.result fun is_value_explicit_refinement(term) = if term is refined(Term) and term is Abs(_, _) then true Var(_) then false App(_, _) then false //│ Desugared UCS term: //│ if term*‡ is refined Term //│ term*‡ is Abs then true //│ term*‡ is Var then false //│ term*‡ is App then false //│ fun is_value_explicit_refinement: (Abs | App | Var) -> Bool :ducs:normalize.result,postprocess.result fun is_value_automatic_refinement(term) = if term is Term and term is Abs(_, _) then true Var(_) then false App(_, _) then false //│ Normalized UCS term: //│ case term*‡ of //│ refined Term*◊ -> //│ case term*‡ of //│ Abs*◊ -> true //│ _ -> //│ case term*‡ of //│ Var*◊ -> false //│ _ -> //│ case term*‡ of //│ App*◊ -> false //│ Post-processed UCS term: //│ case term*‡ of //│ refined Term*◊ -> //│ case term*‡ of //│ Abs*◊ -> true //│ Var*◊ -> false //│ App*◊ -> false //│ fun is_value_automatic_refinement: (Abs | App | Var) -> Bool fun is_value'(term) = if term is Term and term is Abs(_, _) then true Var(_) then false //│ fun is_value': (Abs | Var) -> Bool ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/Tautology.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun useless_negate_1(x) = if x is Some(y) and x is Some(z) then y + z //│ fun useless_negate_1: Some[Int] -> Int useless_negate_1(Some(1)) //│ Int //│ res //│ = 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/coverage/Unreachable.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(xv) and xv === 1 then true else false //│ fun f: (Object & ~#Some | Some[Eql[1]]) -> Bool f(Some(1)) f(Some(2)) f(None) //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = false fun reachable_1(x) = if x is _ and f(x) then "cos" Some(xv) then "sin" None then "tan" //│ fun reachable_1: (None | Some[anything] & ~#Some | Some[Eql[1]]) -> ("cos" | "sin" | "tan") reachable_1(Some(1)) reachable_1(Some(2)) reachable_1(None) //│ "cos" | "sin" | "tan" //│ res //│ = 'cos' //│ res //│ = 'sin' //│ res //│ = 'tan' :w fun unreachable_1(x) = if x is _ and f(x) then "tmux" else "screen" Some(xv) then "sin" None then "tan" //│ ╔══[WARNING] this case is unreachable //│ ║ l.46: if x is //│ ║ ^^^^ //│ ║ l.47: _ and //│ ║ ^^^^^^^^^ //│ ║ l.48: f(x) then "tmux" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.49: else "screen" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.50: Some(xv) then "sin" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.51: None then "tan" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── because it is subsumed by the branch //│ ║ l.49: else "screen" //│ ╙── ^^^^^^^^ //│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/edges/Unconditional.mls ================================================ :NewDefs class Point(x: Int, y: Int) class Rectangle(x: Int, y: Int, width: Int, height: Int) //│ class Point(x: Int, y: Int) //│ class Rectangle(x: Int, y: Int, width: Int, height: Int) fun sum(p) = if p is Point(x, y) then x + y //│ fun sum: Point -> Int sum(Point(1, 2)) //│ Int //│ res //│ = 3 fun abs(x) = if x < 0 then -x else x //│ fun abs: Int -> Int fun dist(p, q) = if p is Point(x1, y1) and q is Point(x2, y2) then abs(x1 - x2) + abs(y1 - y2) //│ fun dist: (Point, Point) -> Int dist(Point(1, 2), Point(3, 4)) //│ Int //│ res //│ = 4 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/AVLTree.mls ================================================ :NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error fun (?) max(x, y) = if x > y then x else y fun abs(x) = if x < 0 then -x else x //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () //│ fun ( 'd //│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e //│ fun abs: Int -> Int abstract class Option[out T]: (Some[T] | None) class Some[out T](val value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option //│ module None extends Option fun (??) getOrElse(o, v) = if o is Some(v') then v' None then v //│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a fun (++) strcat(s1, s2) = concat(s1)(s2) //│ fun (++) strcat: (Str, Str) -> Str let anyToString = toString //│ let anyToString: anything -> Str //│ anyToString //│ = [Function: toString] abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[nothing] //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] abstract class Tree[out A]: (Empty | Node[A]) class Node[out A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree[A] module Empty extends Tree[nothing] //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree //│ module Empty extends Tree fun showTree(t: Tree[anything]): Str = if t is Node(v, l, r, _) then "(" ++ showTree(l) ++ " " ++ toString(v) ++ " " ++ showTree(r) ++ ")" Empty then "•" //│ fun showTree: (t: Tree[anything]) -> Str fun showTreeWithHeight(t: Tree[anything]): Str = if t is Node(v, l, r, h) then "(" ++ showTreeWithHeight(l) ++ " " ++ toString(v) ++ " [" ++ toString(h) ++ "]" ++ " " ++ showTreeWithHeight(r) ++ ")" Empty then "•" //│ fun showTreeWithHeight: (t: Tree[anything]) -> Str fun height(t) = if t is Node(_, _, _, h) then h Empty then 0 //│ fun height: (Empty | Node[anything]) -> Int let nil = Empty fun node(v, l, r) = Node(v, l, r, max(height(l), height(r)) + 1) fun single(v) = Node(v, Empty, Empty, 1) //│ let nil: Empty //│ fun node: forall 'A. ('A, Empty & Tree['A] | Node[anything] & Tree['A], Empty & Tree['A] | Node[anything] & Tree['A]) -> Node['A] //│ fun single: forall 'A0. 'A0 -> Node['A0] //│ nil //│ = Empty { class: [class Empty extends Tree] } fun isBalanced(t) = if t is Node(t, l, r, _) then (abs(height(l) - height(r)) <= 1) && isBalanced(l) && isBalanced(r) Empty then true //│ fun isBalanced: (Empty | Node[anything]) -> Bool isBalanced(nil) ~~> true //│ () //│ res //│ = undefined // _ _ _ _ // ___(_)_ __ __ _| | ___ _ __ ___ | |_ __ _| |_ ___ // / __| | '_ \ / _` | |/ _ \ | '__/ _ \| __/ _` | __/ _ \ // \__ \ | | | | (_| | | __/ | | | (_) | || (_| | || __/ // |___/_|_| |_|\__, |_|\___| |_| \___/ \__\__,_|\__\___| // |___/ fun rotateLeft(t: Tree['A]): Tree['A] = if t is Node(v, l, Node(v', l', r', _), _) and max(height(l), height(l')) + 1 is h and max(h, height(r')) + 1 is h' then Node(v', Node(v, l, l', h), r', h') _ then t //│ fun rotateLeft: forall 'A. (t: Tree['A]) -> Tree['A] rotateLeft(nil) |> showTree rotateLeft(single(0)) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' let unbalanced1 = node(0, nil, node(1, nil, single(2))) isBalanced(unbalanced1) ~~> false unbalanced1 |> showTree rotateLeft(unbalanced1) |> showTree //│ let unbalanced1: Node[0 | 1 | 2] //│ Str //│ unbalanced1 //│ = Node {} //│ res //│ = undefined //│ res //│ = '(• 0 (• 1 (• 2 •)))' //│ res //│ = '((• 0 •) 1 (• 2 •))' fun rotateRight(t: Tree['A]): Tree['A] = if t is Node(v, Node(v', l', r', _), r, _) then Node(v', l', Node(v, r', r, 0), 0) _ then t //│ fun rotateRight: forall 'A. (t: Tree['A]) -> Tree['A] rotateRight(nil) |> showTree rotateRight(single(0)) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' let unbalanced2 = node(2, node(1, single(0), nil), nil) isBalanced(unbalanced2) ~~> false unbalanced2 |> showTree rotateRight(unbalanced2) |> showTree //│ let unbalanced2: Node[0 | 1 | 2] //│ Str //│ unbalanced2 //│ = Node {} //│ res //│ = undefined //│ res //│ = '(((• 0 •) 1 •) 2 •)' //│ res //│ = '((• 0 •) 1 (• 2 •))' // _ _ _ _ _ // __| | ___ _ _| |__ | | ___ _ __ ___ | |_ __ _| |_ ___ // / _` |/ _ \| | | | '_ \| |/ _ \ | '__/ _ \| __/ _` | __/ _ \ // | (_| | (_) | |_| | |_) | | __/ | | | (_) | || (_| | || __/ // \__,_|\___/ \__,_|_.__/|_|\___| |_| \___/ \__\__,_|\__\___| // fun rotateRightLeft(t: Tree['A]): Tree['A] = if t is Node(v, t1, Node(v', Node(v'', t2, t3, _), t4, _), _) then Node(v'', Node(v, t1, t2, 0), Node(v', t3, t4, 0), 0) else t //│ fun rotateRightLeft: forall 'A. (t: Tree['A]) -> Tree['A] // Should remain the same. rotateRightLeft(nil) |> showTree rotateRightLeft(single(0)) |> showTree rotateRightLeft(unbalanced1) |> showTree rotateRightLeft(unbalanced2) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 (• 1 (• 2 •)))' //│ res //│ = '(((• 0 •) 1 •) 2 •)' let unbalanced3 = node(0, nil, node(3, node(1, nil, single(2)), nil)) isBalanced(unbalanced3) ~~> false unbalanced3 |> showTree rotateRightLeft(unbalanced3) |> showTree //│ let unbalanced3: Node[0 | 1 | 2 | 3] //│ Str //│ unbalanced3 //│ = Node {} //│ res //│ = undefined //│ res //│ = '(• 0 ((• 1 (• 2 •)) 3 •))' //│ res //│ = '((• 0 •) 1 ((• 2 •) 3 •))' fun rotateLeftRight(t: Tree['A]): Tree['A] = if t is Node(v, Node(v', t1, Node(v'', t2, t3, _), _), t4, _) then Node(v'', Node(v', t1, t2, 0), Node(v, t3, t4, 0), 0) else t //│ fun rotateLeftRight: forall 'A. (t: Tree['A]) -> Tree['A] // Should remain the same. rotateLeftRight(nil) |> showTree rotateLeftRight(single(0)) |> showTree rotateLeftRight(unbalanced1) |> showTree rotateLeftRight(unbalanced2) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 (• 1 (• 2 •)))' //│ res //│ = '(((• 0 •) 1 •) 2 •)' let unbalanced4 = node(3, node(0, nil, node(2, single(1), nil)), nil) isBalanced(unbalanced4) ~~> false unbalanced4 |> showTree rotateRightLeft(unbalanced4) |> showTree //│ let unbalanced4: Node[0 | 1 | 2 | 3] //│ Str //│ unbalanced4 //│ = Node {} //│ res //│ = undefined //│ res //│ = '((• 0 ((• 1 •) 2 •)) 3 •)' //│ res //│ = '((• 0 ((• 1 •) 2 •)) 3 •)' fun bf(t) = if t is Node(_, l, r, _) then height(l) - height(r) Empty then 0 //│ fun bf: (Empty | Node[anything]) -> Int // _ _ // (_)_ __ ___ ___ _ __| |_ // | | '_ \/ __|/ _ \ '__| __| // | | | | \__ \ __/ | | |_ // |_|_| |_|___/\___|_| \__| // // This function does not work for now as it exposed a lot of problems we have // in desugaring and normalization. For example: // // - [x] We need to mark the Boolean scrutinees and skip the specialization of // these scrutinees in the normalization process, otherwise, it would result // in many futile computations. // - [x] We should cache the expressions that are broken by conditional splits, // otherwise, they will be evaluated for more than one time. // - [ ] The branches of an operator split should be "chained" rather // than placed in parallel, otherwise, the later branches will appear in the // else branch of all its previous branches. **WORK IN PROGRESS** // // :ducs:postprocess fun balance(t: Tree['A]): Tree['A] = if t is Node(x, l, r, _) and height(r) - height(l) > 1 and r is Node(y, l', r', _) and height(r') - height(l') > 0 then rotateLeft(t) < 0 and l' is Node then rotateRightLeft(t) < 1 and l is Node(y, l', r', _) and height(r') - height(l') > 0 and r' is Node then rotateLeftRight(t) < 0 then rotateRight(t) _ then t //│ fun balance: forall 'A. (t: Tree['A]) -> Tree['A] fun insert(t: Tree[Num], v: Num) = if t is Node(v', l, r, h) and v < v' then Node(v', insert(l, v), r, h) |> balance v > v' then Node(v', l, insert(r, v), h) |> balance _ then t Empty then Node(v, Empty, Empty, 1) //│ fun insert: (t: Tree[Num], v: Num) -> (Node[Num] | Tree[Num]) insert(nil, 0) |> showTree insert(single(0), 1) |> showTree insert(single(0), -1) |> showTree //│ Str //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 (• 1 •))' //│ res //│ = '((• -1 •) 0 •)' insert(node(0, nil, single(1)), 2) |> showTreeWithHeight insert(node(0, nil, single(1)), 2) |> showTree //│ Str //│ res //│ = '(• 0 [2] (• 1 [1] (• 2 [1] •)))' //│ res //│ = '(• 0 (• 1 (• 2 •)))' insert(unbalanced1, 3) |> showTree //│ Str //│ res //│ = '((• 0 •) 1 (• 2 (• 3 •)))' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/Calculator.mls ================================================ :NewDefs // This test file explores implementing a calculator using UCS. fun (++) concatOp(a, b) = concat(a)(b) fun (|>) pipe(a, f) = f(a) fun (!==) notEqual(x, y) = not(x === y) let anyToString = toString //│ fun (++) concatOp: (Str, Str) -> Str //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (!==) notEqual: forall 'c. (Eql['c], 'c) -> Bool //│ let anyToString: anything -> Str //│ anyToString //│ = [Function: toString] fun par(s, p) = if p then "(" ++ s ++ ")" else s //│ fun par: (Str, Bool) -> Str type StringOps = { length: Int, charAt: Int => Str, charCodeAt: Int => Int, slice: Int => Str } declare fun String: nothing let toStringOps: anything => StringOps = String //│ type StringOps = {charAt: Int -> Str, charCodeAt: Int -> Int, length: Int, slice: Int -> Str} //│ let toStringOps: anything -> StringOps //│ fun String: nothing //│ toStringOps //│ = [Function: String] type Option[A] = Some[A] | None class Some[A](value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None fun showOption(x) = if x is Some(value) then "Some(" ++ toString(value) ++ ")" None then "None" fun mapOption(f, x) = if x is Some(value) then Some(f(value)) None then None fun (??) getOrElse(x, default) = if x is Some(value) then value None then default fun flatten(x) = if x is Some(value) then value other then other //│ fun showOption: (None | Some[anything]) -> Str //│ fun mapOption: forall 'a 'A. ('a -> 'A, None | Some['a]) -> (None | Some['A]) //│ fun (??) getOrElse: forall 'b. (None | Some['b], 'b) -> 'b //│ fun flatten: forall 'c. (Object & 'c & ~#Some | Some['c]) -> 'c type List[A] = Cons[A] | Nil class Cons[A](head: A, tail: List[A]) module Nil fun (::) cons(head, tail) = Cons(head, tail) fun reverse(xs) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) fun join(sep) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') (xs) => if xs is Cons(x, xs') then aux(toString(x), xs') Nil then "" fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) //│ module Nil //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] //│ fun reverse: forall 'A0 'A1. (Cons['A0] | Nil) -> (Cons['A1] | Nil) //│ fun join: Str -> (forall 'A2. (Cons['A2] | Nil) -> Str) //│ fun showList: forall 'A3. (Cons['A3] | Nil) -> Str //│ where //│ 'A0 <: 'A1 // _ // | | _____ _____ _ __ // | | / _ \ \/ / _ \ '__| // | |__| __/> < __/ | // |_____\___/_/\_\___|_| // fun isDigit(n) = (48 <= n) && (n <= 57) fun isBlank(n) = (n === 32) || (n === 9) || (n === 10) || (n === 13) //│ fun isDigit: Num -> Bool //│ fun isBlank: Eql[10 | 13 | 32 | 9] -> Bool fun scanInt(text: StringOps, at: Int): Option[[Int, Int]] = let rec aux(acc, i: Int): Option[[Int, Int]] = if i < 0 then None i >= text.length then mapOption(n => [n, i], acc) let c = text.charCodeAt(i) isDigit(c) then aux(Some((acc ?? 0) * 10 + c - 48), i + 1) else mapOption(n => [n, i], acc) aux(None, at) //│ fun scanInt: (text: StringOps, at: Int) -> Option[[Int, Int]] scanInt("a123" |> toStringOps, 0) |> showOption scanInt("a123" |> toStringOps, 1) |> showOption scanInt("a123" |> toStringOps, 2) |> showOption scanInt("a123" |> toStringOps, 3) |> showOption scanInt("a123" |> toStringOps, 4) |> showOption //│ Str //│ res //│ = 'None' //│ res //│ = 'Some(123,4)' //│ res //│ = 'Some(23,4)' //│ res //│ = 'Some(3,4)' //│ res //│ = 'None' fun skipBlank(text: StringOps, at: Int): Int = let rec aux(i: Int): Int = if i >= text.length then i isBlank(text.charCodeAt(i)) then aux(i + 1) else i aux(at) //│ fun skipBlank: (text: StringOps, at: Int) -> Int skipBlank("abc" |> toStringOps, 0) skipBlank("abc" |> toStringOps, 1) skipBlank("abc" |> toStringOps, 2) skipBlank(" \t\n\r123" |> toStringOps, 0) //│ Int //│ res //│ = 0 //│ res //│ = 1 //│ res //│ = 2 //│ res //│ = 5 class Span(val start: Int, val end: Int) //│ class Span(start: Int, end: Int) abstract class Token(val span: Span) class IntegerLiteral(value: Int, span2: Span) extends Token(span2) { // It seems that constructor parameters is not sanitized fun toString(): Str = "IntegerLiteral(" ++ anyToString(value) ++ ")" } class LeftParen(at: Int) extends Token(Span(at, at + 1)) { fun toString(): Str = "LeftParen" } class RightParen(at: Int) extends Token(Span(at, at + 1)) { fun toString(): Str = "RightParen" } class BinaryOperator(value: Str, val bp: Int, at: Int) extends Token(Span(at, at + 1)) { fun toString(): Str = "BinaryOperator(" ++ value ++ ", " ++ anyToString(bp) ++ ")" } class EndOfInput(at: Int) extends Token(Span(at, at)) { fun toString(): Str = "EndOfInput" } class UnknownInput(rest: Str, at: Int, length: Int) extends Token(Span(at, at + length)) { fun toString(): Str = "UnknownInput(" ++ rest ++ ")" } //│ abstract class Token(span: Span) //│ class IntegerLiteral(value: Int, span2: Span) extends Token { //│ fun toString: () -> Str //│ } //│ class LeftParen(at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class RightParen(at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class BinaryOperator(value: Str, bp: Int, at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class EndOfInput(at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class UnknownInput(rest: Str, at: Int, length: Int) extends Token { //│ fun toString: () -> Str //│ } fun scanToken(text: StringOps, at: Int): [Token, Int] = if let at' = skipBlank(text, at) at' >= text.length then [EndOfInput(at'), at'] let head = text.charCodeAt(at') head === 37 then [BinaryOperator("%", 20, at'), at' + 1] 40 then [LeftParen(at'), at' + 1] 41 then [RightParen(at'), at' + 1] 42 then [BinaryOperator("*", 20, at'), at' + 1] 43 then [BinaryOperator("+", 10, at'), at' + 1] 45 then [BinaryOperator("-", 10, at'), at' + 1] 47 then [BinaryOperator("/", 20, at'), at' + 1] (48 <= head) && (head <= 57) and scanInt(text, at') is Some([n, at'']) then [IntegerLiteral(n, Span(at', at'')), at''] else [UnknownInput(text.slice(at'), at', text.length - at'), at'] //│ fun scanToken: (text: StringOps, at: Int) -> [Token, Int] scanToken("bruh" |> toStringOps, 0) scanToken("1" |> toStringOps, 0) scanToken("+" |> toStringOps, 0) scanToken(" 42" |> toStringOps, 0) //│ [Token, Int] //│ res //│ = [ UnknownInput {}, 0 ] //│ res //│ = [ IntegerLiteral {}, 1 ] //│ res //│ = [ BinaryOperator {}, 1 ] //│ res //│ = [ IntegerLiteral {}, 4 ] fun tokenize(str: Str): List[Token] = let text = str |> toStringOps let rec aux(acc, at) = if scanToken(text, at) is [token, at'] and token is UnknownInput then (token :: acc) |> reverse EndOfInput then acc |> reverse else aux(token :: acc, at') aux(Nil, 0) //│ fun tokenize: (str: Str) -> List[Token] tokenize("0") |> showList tokenize("1 + 2 * 3") |> showList tokenize("bruh") |> showList //│ Str //│ res //│ = '[IntegerLiteral(0)]' //│ res //│ = '[IntegerLiteral(1), BinaryOperator(+, 10), IntegerLiteral(2), BinaryOperator(*, 20), IntegerLiteral(3)]' //│ res //│ = '[UnknownInput(bruh)]' // ____ _ // | _ \ __ _ _ __ ___(_)_ __ __ _ // | |_) / _` | '__/ __| | '_ \ / _` | // | __/ (_| | | \__ \ | | | | (_| | // |_| \__,_|_| |___/_|_| |_|\__, | // |___/ type Expression = IntegerLiteral | BinaryExpression class BinaryExpression(op: BinaryOperator, left: Expression, right: Expression) //│ type Expression = BinaryExpression | IntegerLiteral //│ class BinaryExpression(op: BinaryOperator, left: Expression, right: Expression) fun bindingPower(t: Expression): Int = if t is IntegerLiteral then 30 BinaryExpression(op, _, _) then op.bp //│ fun bindingPower: (t: Expression) -> Int fun showExpression(t: Expression): Str = if t is IntegerLiteral(n) then anyToString(n) BinaryExpression(BinaryOperator(op, bp), left, right) then let lbp = bindingPower of left let rbp = bindingPower of right par(showExpression(left), lbp < bp) ++ " " ++ op ++ " " ++ par(showExpression(right), rbp < bp) //│ fun showExpression: (t: Expression) -> Str let s = Span(0, 0) IntegerLiteral(42, s) |> showExpression let t1 = BinaryExpression(BinaryOperator("+", 10, 0), IntegerLiteral(1, s), IntegerLiteral(2, s)) t1 |> showExpression let t2 = BinaryExpression(BinaryOperator("*", 20, 0), t1, IntegerLiteral(3, s)) t2 |> showExpression let t3 = BinaryExpression(BinaryOperator("*", 20, 0), t2, IntegerLiteral(4, s)) t3 |> showExpression //│ let s: Span //│ let t1: BinaryExpression //│ let t2: BinaryExpression //│ let t3: BinaryExpression //│ Str //│ s //│ = Span {} //│ res //│ = '42' //│ t1 //│ = BinaryExpression {} //│ res //│ = '1 + 2' //│ t2 //│ = BinaryExpression {} //│ res //│ = '(1 + 2) * 3' //│ t3 //│ = BinaryExpression {} //│ res //│ = '(1 + 2) * 3 * 4' type ParseResult[A] = Some[A] | Failed class Failed(message: Str) fun showParseResult(r: ParseResult['A]) = if r is Some(value) then "Some(" ++ toString(value) ++ ")" Failed(message) then "Failed(" ++ message ++ ")" fun (?>) mapParseResult(x, f) = if x is Some(value) then Some(f(value)) failed then failed //│ type ParseResult[A] = Failed | Some[A] //│ class Failed(message: Str) //│ fun showParseResult: forall 'A. (r: ParseResult['A]) -> Str //│ fun (?>) mapParseResult: forall 'a 'b 'A0. (Object & 'a & ~#Some | Some['b], 'b -> 'A0) -> (Some['A0] | 'a) fun showParsedExpression(r: ParseResult[Expression]) = if r is Some(value) then "Some(" ++ showExpression(value) ++ ")" Failed(message) then "Failed(" ++ message ++ ")" //│ fun showParsedExpression: (r: ParseResult[Expression]) -> Str fun lastPosition(t: Expression): Int = if t is IntegerLiteral(_, span) then span.end BinaryExpression(_, _, right) then lastPosition(right) //│ fun lastPosition: (t: Expression) -> Int fun parseAtom(ts: List[Token]): ParseResult[[Expression, List[Token]]] = if ts is Cons(IntegerLiteral(n, span), ts') then Some([IntegerLiteral(n, span), ts']) Cons(LeftParen, ts') and parseExpression(0, ts') is Some([body, Cons(RightParen, ts'')]) then Some([body, ts'']) Some([body, _]) then Failed("Expected a right parenthesis at " ++ toString(lastPosition of body)) failed then failed Cons(token, _) then Failed("Unexpected token " ++ toString(token) ++ " at " ++ toString(token.span.start)) Nil then Failed("Unexpected end of input") fun parseExpression(bp: Int, ts: List[Token]): ParseResult[[Expression, List[Token]]] = if parseAtom(ts) is Some([leftmost, ts']) then let rec aux(left, ts) = if ts is Cons(BinaryOperator(op, bp', opAt), ts') and bp < bp' and parseExpression(bp', ts') is Some([right, ts'']) then aux(BinaryExpression(BinaryOperator(op, bp', opAt), left, right), ts'') failed then failed else Some([left, ts]) aux(leftmost, ts') failed then failed fun parse(source: Str): ParseResult[Expression] = if parseExpression(0, tokenize(source)) is Some([expr, Nil]) then Some(expr) Some([expr, rest]) then Failed("Unexpected token: " ++ showList(rest) ++ " at " ++ toString(lastPosition of expr)) failed then failed //│ fun parseAtom: (ts: List[Token]) -> ParseResult[[Expression, List[Token]]] //│ fun parseExpression: (bp: Int, ts: List[Token]) -> ParseResult[[Expression, List[Token]]] //│ fun parse: (source: Str) -> ParseResult[Expression] parse("1 + 2 * 3") |> showParsedExpression parse("(1 + 2) * 3") |> showParsedExpression parse("2 * (1 + 3 + 5 + 7 + 9 + 11) - 2 - 4 - 6") |> showParsedExpression parse("2 * (1 + 3) * (5 + 7) * (9 - 11)") |> showParsedExpression parse("(((((((((42)))))))))") |> showParsedExpression //│ Str //│ res //│ = 'Some(1 + 2 * 3)' //│ res //│ = 'Some((1 + 2) * 3)' //│ res //│ = 'Some(2 * (1 + 3 + 5 + 7 + 9 + 11) - 2 - 4 - 6)' //│ res //│ = 'Some(2 * (1 + 3) * (5 + 7) * (9 - 11))' //│ res //│ = 'Some(42)' parse("1 + ") |> showParsedExpression parse("1 bruh") |> showParsedExpression parse("1 * (2 + 3") |> showParsedExpression parse("1 - bruh") |> showParsedExpression //│ Str //│ res //│ = 'Failed(Unexpected end of input)' //│ res //│ = 'Failed(Unexpected token: [UnknownInput(bruh)] at 1)' //│ res //│ = 'Failed(Expected a right parenthesis at 10)' //│ res //│ = 'Failed(Unexpected token UnknownInput(bruh) at 4)' // _____ _ _ _ // | ____|_ ____ _| |_ _ __ _| |_(_) ___ _ __ // | _| \ \ / / _` | | | | |/ _` | __| |/ _ \| '_ \ // | |___ \ V / (_| | | |_| | (_| | |_| | (_) | | | | // |_____| \_/ \__,_|_|\__,_|\__,_|\__|_|\___/|_| |_| // fun evaluate(t: Expression): Option[Int] = if t is IntegerLiteral(n) then Some(n) BinaryExpression(BinaryOperator(op, _, _), left, right) and evaluate(left) is Some(leftResult) and evaluate(right) is Some(rightResult) and op === "+" then Some(leftResult + rightResult) "-" then Some(leftResult - rightResult) "*" then Some(leftResult * rightResult) // "/" then Some(leftResult / rightResult) "%" then Some(leftResult % rightResult) else None //│ fun evaluate: (t: Expression) -> Option[Int] fun evaluation(source: Str): Str = if parse(source) is Some(expression) and evaluate(expression) is Some(result) then toString(result) None then "Evaluation failed" Failed(message) then "Parsing failed: " ++ message //│ fun evaluation: (source: Str) -> Str evaluation("1 + 2 * 3") evaluation("(((((42)))))") evaluation("1 * (3 + 4) - 5") evaluation("1 + ") evaluation("1 bruh") //│ Str //│ res //│ = '7' //│ res //│ = '42' //│ res //│ = '2' //│ res //│ = 'Parsing failed: Unexpected end of input' //│ res //│ = 'Parsing failed: Unexpected token: [UnknownInput(bruh)] at 1' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/JSON.mls ================================================ :NewDefs type NStr = Str & { length: Int, at: Int -> NStr, charAt: Int -> NStr, charCodeAt: Int -> Int, slice: (Int, Int) -> NStr, startsWith: (Str, Int) -> Bool, endsWith: Str -> Bool, split: Str -> Array[NStr], trim: () -> NStr, trimStart: () -> NStr, trimEnd: () -> NStr, padStart: (Int, Str) -> NStr, padEnd: (Int, Str) -> NStr, repeat: Int -> NStr, indexOf: Str -> Int, lastIndexOf: Str -> Int, includes: Str -> Bool, localeCompare: Str -> Int } declare fun String: (anything -> NStr) & { fromCodePoint: Int -> NStr } fun (++) strcat(a, b) = String of concat(a)(b) fun (<*) strlt(a: NStr, b: NStr) = a.localeCompare(b) < 0 fun (*>) strgt(a: NStr, b: NStr) = a.localeCompare(b) > 0 //│ type NStr = Str & { //│ at: Int -> NStr, //│ charAt: Int -> NStr, //│ charCodeAt: Int -> Int, //│ endsWith: Str -> Bool, //│ includes: Str -> Bool, //│ indexOf: Str -> Int, //│ lastIndexOf: Str -> Int, //│ length: Int, //│ localeCompare: Str -> Int, //│ padEnd: (Int, Str) -> NStr, //│ padStart: (Int, Str) -> NStr, //│ repeat: Int -> NStr, //│ slice: (Int, Int) -> NStr, //│ split: Str -> Array[NStr], //│ startsWith: (Str, Int) -> Bool, //│ trim: () -> NStr, //│ trimEnd: () -> NStr, //│ trimStart: () -> NStr //│ } //│ fun (++) strcat: (Str, Str) -> NStr //│ fun (<*) strlt: (a: NStr, b: NStr) -> Bool //│ fun (*>) strgt: (a: NStr, b: NStr) -> Bool //│ fun String: anything -> NStr & {fromCodePoint: Int -> NStr} declare fun Math: { log10: Num -> Num, floor: Num -> Num, ceil: Num -> Num } //│ fun Math: {ceil: Num -> Num, floor: Num -> Num, log10: Num -> Num} fun (!==) notEqual(x, y) = not(x === y) declare fun parseInt: (Str, Int) -> Int //│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool //│ fun parseInt: (Str, Int) -> Int // `List` and its utilities: abstract class List[out T]: Cons[T] | Nil class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) fun reverse(l: List['A]): List['A] = let rec r(l', l) = if l is Cons(x, xs) then r(x :: l', xs) else l' r(Nil, l) fun join(sep: Str, xs: List['B]) = if xs is Cons(x, Nil) then toString(x) Cons(x, xs) then toString(x) ++ sep ++ join(sep, xs) Nil then "" fun showList(xs: List['C]) = "[" ++ join(", ", xs) ++ "]" fun map(f: 'D -> 'E, xs: List['D]): List['E] = if xs is Cons(x, xs) then f(x) :: map(f, xs) Nil then Nil fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs is Cons(x, xs') and ys is Cons(y, ys') then equal(x, y) and equalList(xs', ys', equal) Nil and ys is Nil then true else false //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] //│ fun reverse: forall 'T0. (l: List['T0]) -> List['T0] //│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) //│ fun showList: (xs: List[anything]) -> NStr //│ fun map: forall 'D 'T1. (f: 'D -> 'T1, xs: List['D]) -> List['T1] //│ fun equalList: forall 'A. (xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool) -> Bool // `Option` and its utilities: abstract class Option[out A]: Some[A] | None class Some[out A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun (->) makePair(a, b) = [a, b] //│ fun (->) makePair: forall 'a 'b. ('a, 'b) -> ['a, 'b] abstract class ListMap[K, out V]: (ConsMap[K, V] | NilMap) class ConsMap[K, out V](head: [K, V], tail: ListMap[K, V]) extends ListMap[K, V] module NilMap extends ListMap fun containsKey(map: ListMap['K, 'V], key: 'K): Bool = if map is ConsMap([k, _], _) and k === key then true ConsMap(_, tail) then containsKey(tail, key) NilMap then false fun (:+) insert(map, entry) = if map is ConsMap(entry', map) and entry'.0 === entry.0 then ConsMap(entry, map) else ConsMap(entry', insert(map, entry)) NilMap then ConsMap(entry, NilMap) fun showMap(map) = let showEntry([k, v]) = toString(k) ++ " -> " ++ toString(v) let rec aux(map) = if map is ConsMap(last, NilMap) then showEntry(last) ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) NilMap then "" if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" //│ abstract class ListMap[K, V]: ConsMap[K, V] | NilMap //│ class ConsMap[K, V](head: [K, V], tail: ListMap[K, V]) extends ListMap //│ module NilMap extends ListMap //│ fun containsKey: forall 'K 'a. (map: ListMap['K, anything], key: 'a) -> Bool //│ fun (:+) insert: forall 'K0 'b 'V. (ConsMap['K0, 'V] | NilMap, ['K0 & 'b, 'V]) -> ConsMap['K0, 'V] //│ fun showMap: forall 'K1. (ConsMap['K1, anything] | NilMap) -> NStr //│ where //│ 'K0 <: Eql['b] //│ 'K <: Eql['a] showMap of NilMap showMap of NilMap :+ ["b", 2] showMap of NilMap :+ [1, "a"] :+ [2, "b"] showMap of NilMap :+ [1, "a"] :+ [2, "b"] :+ [1, "c"] //│ NStr //│ res //│ = '{}' //│ res //│ = '{ b -> 2 }' //│ res //│ = '{ 1 -> a, 2 -> b }' //│ res //│ = '{ 1 -> c, 2 -> b }' abstract class JsonValue: JsonNumber | JsonString | JsonArray | JsonObject | JsonBoolean | JsonNull class JsonNumber(value: Num) extends JsonValue class JsonString(value: Str) extends JsonValue class JsonArray(value: List[JsonValue]) extends JsonValue class JsonObject(value: ListMap[Str, JsonValue]) extends JsonValue class JsonBoolean(value: Bool) extends JsonValue module JsonNull extends JsonValue //│ abstract class JsonValue: JsonArray | JsonBoolean | JsonNull | JsonNumber | JsonObject | JsonString //│ class JsonNumber(value: Num) extends JsonValue //│ class JsonString(value: Str) extends JsonValue //│ class JsonArray(value: List[JsonValue]) extends JsonValue //│ class JsonObject(value: ListMap[Str, JsonValue]) extends JsonValue //│ class JsonBoolean(value: Bool) extends JsonValue //│ module JsonNull extends JsonValue class ParserState(val text: NStr, val at: Int) { fun drained: Bool = at === text.length fun peek: Option[NStr] = if drained then None else Some(text.charAt(at)) fun peekCode: Option[Int] = if drained then None else Some(text.charCodeAt(at)) fun next: ParserState = if drained then this else ParserState(text, at + 1) fun nextDigit: Option[[Num, ParserState]] = if peekCode is Some(ch) and 48 <= ch and ch <= 57 then Some([ch - 48, next]) else None fun match(prefix: Str): Option[ParserState] = let prefix' = String(prefix) if prefix'.length > text.length - at then None text.startsWith(prefix', at) then Some(ParserState(text, at + prefix'.length)) else None fun rest: NStr = text.slice(at, text.length) } fun showParserState(state) = "ParserState(_, " ++ toString(state.at) ++ ")" fun success: forall 't: ('t, ParserState) -> ParseResult['t] fun success = (value, state) => Success(value, state) fun failure: forall 't: Str -> ParseResult[nothing] fun failure = error => Failure(error) abstract class ParseResult[out T]: (Success[T] | Failure) { virtual fun flatMap(f: (T, ParserState) -> ParseResult['U]): ParseResult['U] virtual fun map(f: T -> 'U): ParseResult['U] } class Success[out T](value: T, state: ParserState) extends ParseResult[T] { fun flatMap(f) = f(value, state) fun map(f) = success(f(value), state) } class Failure(error: Str) extends ParseResult[nothing] { fun flatMap(_) = failure(error) fun map(_) = failure(error) } fun showParseResult(result) = if result is Success(value, state) then "Success after " ++ toString(state.at) ++ ": " ++ toString(value) Failure(error) then "Failure: " ++ toString(error) //│ class ParserState(text: NStr, at: Int) { //│ fun drained: Bool //│ fun match: (prefix: Str) -> Option[ParserState] //│ fun next: ParserState //│ fun nextDigit: Option[[Num, ParserState]] //│ fun peek: Option[NStr] //│ fun peekCode: Option[Int] //│ fun rest: NStr //│ } //│ fun showParserState: {at: anything} -> NStr //│ fun success: forall 'T. ('T, ParserState) -> Success['T] //│ fun failure: Str -> Failure //│ abstract class ParseResult[T]: Failure | Success[T] { //│ fun flatMap: forall 'U. (f: (T, ParserState) -> ParseResult['U]) -> ParseResult['U] //│ fun map: forall 'U0. (f: T -> 'U0) -> ParseResult['U0] //│ } //│ class Success[T](value: T, state: ParserState) extends ParseResult { //│ fun flatMap: forall 'a. ((T, ParserState) -> 'a) -> 'a //│ fun map: forall 't. (T -> 't) -> ParseResult['t] //│ } //│ class Failure(error: Str) extends ParseResult { //│ fun flatMap: anything -> ParseResult[nothing] //│ fun map: anything -> ParseResult[nothing] //│ } //│ fun showParseResult: (Failure | Success[anything]) -> NStr //│ fun success: forall 't0. ('t0, ParserState) -> ParseResult['t0] //│ fun failure: Str -> ParseResult[nothing] fun isWhiteSpace(ch: NStr): Bool = (ch === " ") || (ch === "\n") || (ch === "\r") || (ch === "\t") fun skipWhiteSpace(state: ParserState): ParserState = if state.peek is Some(ch) and isWhiteSpace(ch) then skipWhiteSpace(state.next) else state //│ fun isWhiteSpace: (ch: NStr) -> Bool //│ fun skipWhiteSpace: (state: ParserState) -> ParserState (skipWhiteSpace of ParserState(String(" \n\r\t"), 0)).at //│ Int //│ res //│ = 4 fun isDigit(ch) = sge(ch, "0") && sle(ch, "9") //│ fun isDigit: Str -> Bool fun parseNumber(state: ParserState): ParseResult[Num] = let toFraction(n) = n / (10 ** Math.ceil of Math.log10 of n) let parseNegative(state): ParseResult[Bool] = if state.peek is Some("-") then Success(true, state.next) else Success(false, state) // Parse one or more decimal digits // -------------------------------- let parseDigits(state): ParseResult[Num] = // Parse remaining digits let rec aux(acc, state) = if state.nextDigit is Some([digit, state']) then aux((acc *. 10) +. digit, state') None then [acc, state] // Parse the first digit if state.nextDigit is Some([digit, state']) and aux(digit, state') is [num, state''] then Success(num, state'') None then Failure("expected one or more decimal digits") // Parse the integral part of the number // ------------------------------------- let parseIntegral(state): ParseResult[Num] = if state.nextDigit is Some([0, state']) then Success(0, state') else parseDigits(state) // Parse the fractional part of the number // --------------------------------------- let parseFraction(state): ParseResult[Num] = if state.peek is Some(".") then parseDigits(state.next).map of toFraction else Success(0, state) let parseExponent(state): ParseResult[Num] = let parseSign(state): ParseResult[Bool] = if state.peek is Some("-") then Success(true, state.next) Some("+") then Success(false, state.next) else Success(false, state) if state.peek is Some(e) and (e === "e") || (e === "E") then parseSign(state.next).flatMap of (sign, state) => parseDigits(state).map of exponent => if sign then 10 ** (0 -. exponent) else 10 ** exponent else Success(1, state) parseNegative(state).flatMap of (negative, state) => parseIntegral(state).flatMap of (integral, state) => parseFraction(state).flatMap of (fraction, state) => parseExponent(state).flatMap of (exponent, state) => let value = (integral +. fraction) *. exponent Success of (if negative then (0 -. value) else value), state //│ fun parseNumber: (state: ParserState) -> ParseResult[Num] showParseResult of parseNumber of ParserState of String("0"), 0 showParseResult of parseNumber of ParserState of String("0234"), 0 showParseResult of parseNumber of ParserState of String("123"), 0 showParseResult of parseNumber of ParserState of String("12.34"), 0 showParseResult of parseNumber of ParserState of String("1e10"), 0 showParseResult of parseNumber of ParserState of String("1E5"), 0 showParseResult of parseNumber of ParserState of String("1E-1"), 0 showParseResult of parseNumber of ParserState of String("1E+1"), 0 //│ NStr //│ res //│ = 'Success after 1: 0' //│ res //│ = 'Success after 1: 0' //│ res //│ = 'Success after 3: 123' //│ res //│ = 'Success after 5: 12.34' //│ res //│ = 'Success after 4: 10000000000' //│ res //│ = 'Success after 3: 100000' //│ res //│ = 'Success after 4: 0.1' //│ res //│ = 'Success after 4: 10' fun parseString(state: ParserState): ParseResult[Str] = let rec parseCodePoint(n, acc, state) = if n === 0 then Success(acc, state) state.peekCode is Some(code) and 48 <= code and code <= 57 then parseCodePoint(n - 1, acc * 16 + code - 48, state.next) 65 <= code and code <= 70 then parseCodePoint(n - 1, acc * 16 + code - 55, state.next) 97 <= code and code <= 102 then parseCodePoint(n - 1, acc * 16 + code - 87, state.next) else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of '" ++ String.fromCodePoint(code) ++ "'") else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of end of input") let rec parseContent(acc, state) = if state.peek is Some("\"") then Success(acc, state.next) Some("\\") and let state' = state.next state'.peek is Some("\"") then parseContent(acc ++ "\"", state'.next) Some("\\") then parseContent(acc ++ "\\", state'.next) Some("/") then parseContent(acc ++ "/", state'.next) Some("b") then parseContent(acc ++ "\b", state'.next) Some("f") then parseContent(acc ++ "\f", state'.next) Some("n") then parseContent(acc ++ "\n", state'.next) Some("r") then parseContent(acc ++ "\r", state'.next) Some("t") then parseContent(acc ++ "\t", state'.next) Some("u") then parseCodePoint(4, 0, state'.next).flatMap of (codePoint, state) => if codePoint < 0xD800 || 0xDFFF < codePoint then parseContent(acc ++ String.fromCodePoint(codePoint), state) else Failure("invalid code point") else Failure("invalid escape sequence") Some(ch) then parseContent(acc ++ ch, state.next) None then Failure("expected '\"' instead of end of input") if state.peek is Some("\"") then parseContent("", state.next) Some(ch) then Failure("expected '\"' instead of '" ++ ch ++ "'") else Failure("expected '\"' instead of end of input") //│ fun parseString: (state: ParserState) -> ParseResult[Str] showParseResult of parseString of ParserState of String("\"\""), 0 showParseResult of parseString of ParserState of String("\"abc\""), 0 showParseResult of parseString of ParserState of String("\"\\\"\""), 0 showParseResult of parseString of ParserState of String("\"\\\\\""), 0 showParseResult of parseString of ParserState of String("\"\\/\""), 0 showParseResult of parseString of ParserState of String("\"\\b\""), 0 showParseResult of parseString of ParserState of String("\""), 0 showParseResult of parseString of ParserState of String("\"\\u\""), 0 showParseResult of parseString of ParserState of String("\"\\u0\""), 0 showParseResult of parseString of ParserState of String("\"\\u004c\""), 0 //│ NStr //│ res //│ = 'Success after 2: ' //│ res //│ = 'Success after 5: abc' //│ res //│ = 'Success after 4: "' //│ res //│ = 'Success after 4: \\' //│ res //│ = 'Success after 4: /' //│ res //│ = 'Success after 4: \b' //│ res //│ = `Failure: expected '"' instead of end of input` //│ res //│ = `Failure: expect 4 hex digit(s) instead of '"'` //│ res //│ = `Failure: expect 3 hex digit(s) instead of '"'` //│ res //│ = 'Success after 8: L' fun parseTrue(state: ParserState): ParseResult[Bool] = if state.match("true") is Some(state) then Success(true, state) None then Failure("expected 'true'") fun parseFalse(state: ParserState): ParseResult[Bool] = if state.match("false") is Some(state) then Success(false, state) None then Failure("expected 'false'") fun parseNull(state: ParserState): ParseResult[()] = if state.match("null") is Some(state) then Success((), state) None then Failure("expected 'null'") //│ fun parseTrue: (state: ParserState) -> ParseResult[Bool] //│ fun parseFalse: (state: ParserState) -> ParseResult[Bool] //│ fun parseNull: (state: ParserState) -> ParseResult[()] fun parseObjectEntry(state: ParserState): ParseResult[[Str, JsonValue]] = let state' = skipWhiteSpace(state) parseString(state').flatMap of (key, state) => let state' = skipWhiteSpace(state) if state'.peek is Some(":") then parseValue(state'.next).flatMap of (value, state') => Success([key, value], state') Some(ch) then Failure("expected ':' instead of '" ++ ch ++ "'") None then Failure("expected ':' instead of end of input") else Failure("expected ':' instead of end of input") fun parseObject(state: ParserState): ParseResult[ListMap[Str, JsonValue]] = let rec parseObjectTail(acc: ListMap[Str, JsonValue], state: ParserState) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then parseObjectEntry(state'.next).flatMap of (entry, state') => if containsKey(acc, entry.0) then Failure("duplicate key '" ++ toString(entry.0) ++ "'") else parseObjectTail(ConsMap(entry, acc), state') Some("}") then Success(acc, state'.next) Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) None then Failure("expected ',' or ']' instead of end of input") let state' = skipWhiteSpace(state) if state'.peek is Some("}") then Success(NilMap, state'.next) None then Failure("expected ',' or ']' instead of end of input") else parseObjectEntry(state').flatMap of (head, state) => parseObjectTail(ConsMap(head, NilMap), state) fun parseArray(state: ParserState): ParseResult[List[JsonValue]] = let rec parseArrayTail(acc, state) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then parseValue(state'.next).flatMap of (value, state') => parseArrayTail(value :: acc, state') Some("]") then Success(reverse(acc), state'.next) Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) None then Failure("expected ',' or ']' instead of end of input") let state' = skipWhiteSpace(state) if state'.peek is Some("]") then Success(Nil, state'.next) None then Failure("expected ',' or ']' instead of end of input") else parseValue(state').flatMap of (head, state) => parseArrayTail(head :: Nil, state) fun parseValue(state: ParserState): ParseResult[JsonValue] = let state' = skipWhiteSpace(state) if state'.peek is Some(ch) and ch === "\"" then parseString(state').map of JsonString (ch === "-") || isDigit(ch) then parseNumber(state').map of JsonNumber ch === "[" then parseArray(state'.next).map of JsonArray ch === "{" then parseObject(state'.next).map of JsonObject ch === "t" then parseTrue(state').map of JsonBoolean ch === "f" then parseFalse(state').map of JsonBoolean ch === "n" then parseNull(state').map of _ => JsonNull else Failure("cannot recognize " ++ ch ++ " as the beginning of a JSON value") None then Failure("expected a JSON value instead of end of input") //│ fun parseObjectEntry: (state: ParserState) -> ParseResult[[Str, JsonValue]] //│ fun parseObject: (state: ParserState) -> ParseResult[ListMap[Str, JsonValue]] //│ fun parseArray: (state: ParserState) -> ParseResult[List[JsonValue]] //│ fun parseValue: (state: ParserState) -> ParseResult[JsonValue] fun parse(source: Str): ParseResult[JsonValue] = (parseValue of ParserState of String(source), 0).flatMap of (value, finalState) => let shouldBeEnd = skipWhiteSpace of finalState if shouldBeEnd.drained then Success(value, shouldBeEnd) else Failure("expected end of input instead of: " ++ shouldBeEnd.rest) //│ fun parse: (source: Str) -> ParseResult[JsonValue] fun stringify(value: JsonValue): Str = let stringifyObject(map) = let showEntry([k, v]) = "\"" ++ toString(k) ++ "\": " ++ stringify(v) let rec aux(map) = if map is ConsMap(last, NilMap) then showEntry(last) ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) NilMap then "" if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" if value is JsonNumber(n) then toString(n) JsonString(s) then "\"" ++ s ++ "\"" JsonArray(xs) then "[" ++ join(", ", map(stringify, xs)) ++ "]" JsonObject(m) then stringifyObject(m) JsonBoolean(b) then if b then "true" else "false" JsonNull then "null" //│ fun stringify: (value: JsonValue) -> Str fun showResult(result) = if result is Success(value, state) then "Success after " ++ toString(state.at) ++ ": " ++ stringify(value) Failure(error) then "Failure: " ++ toString(error) //│ fun showResult: (Failure | Success[JsonValue]) -> NStr // Simple tests. showResult of parse of "null" showResult of parse of "true" showResult of parse of "false" showResult of parse of "123" showResult of parse of "\"abc\"" showResult of parse of "[1, 2, 3]" showResult of parse of "{\"a\": 1, \"b\": 2}" showResult of parse of "nul" showResult of parse of "[1, 3, 5" showResult of parse of "[1, 3, 5]" //│ NStr //│ res //│ = 'Success after 4: null' //│ res //│ = 'Success after 4: true' //│ res //│ = 'Success after 5: false' //│ res //│ = 'Success after 3: 123' //│ res //│ = 'Success after 5: "abc"' //│ res //│ = 'Success after 9: [1, 2, 3]' //│ res //│ = 'Success after 16: { "b": 2, "a": 1 }' //│ res //│ = "Failure: expected 'null'" //│ res //│ = "Failure: expected ',' or ']' instead of end of input" //│ res //│ = 'Success after 9: [1, 3, 5]' // Complicated tests. showResult of parse of "{ \"origin\": { \"x\": 0, \"y\": 0 } }" showResult of parse of "[ { \"origin\": { \"x\": 0, \"y\": 0 } , \"size\": { \"width\": 100, \"height\": 100 } } ]" showResult of parse of "{\"id\":\"658f34f88882211aa8679240\",\"children\":[{\"name\":\"Jo Rosales\",\"age\":8},{\"name\":\"Shawn Burke\",\"age\":7},{\"name\":\"Gomez Guthrie\",\"age\":10},{\"name\":\"Tandy Christensen\",\"age\":9},{\"name\":\"Jody Langley\",\"age\":3}],\"currentJob\":{\"title\":\"Developer\",\"salary\":\"mask;\"},\"jobs\":[{\"title\":\"medic\",\"salary\":\"R$ 6.400,90\"},{\"title\":\"teacher\",\"salary\":\"R$ 7.960,31\"}],\"maxRunDistance\":14.7,\"cpf\":\"713.763.356-03\",\"cnpj\":\"33.385.435/0001-50\",\"pretendSalary\":\"R$ 9.247,29\",\"age\":63,\"gender\":\"male\",\"firstName\":\"Parker\",\"lastName\":\"Case\",\"phone\":\"+55 (83) 95023-7077\",\"address\":\"14 Orient Avenue - Harmon, Northern Mariana Islands, Myanmar.\",\"hairColor\":\"yellow\"}" //│ NStr //│ res //│ = 'Success after 32: { "origin": { "y": 0, "x": 0 } }' //│ res //│ = 'Success after 82: [{ "size": { "height": 100, "width": 100 }, "origin": { "y": 0, "x": 0 } }]' //│ res //│ = 'Success after 647: { "hairColor": "yellow", "address": "14 Orient Avenue - Harmon, Northern Mariana Islands, Myanmar.", "phone": "+55 (83) 95023-7077", "lastName": "Case", "firstName": "Parker", "gender": "male", "age": 63, "pretendSalary": "R$ 9.247,29", "cnpj": "33.385.435/0001-50", "cpf": "713.763.356-03", "maxRunDistance": 14.7, "jobs": [{ "salary": "R$ 6.400,90", "title": "medic" }, { "salary": "R$ 7.960,31", "title": "teacher" }], "currentJob": { "salary": "mask;", "title": "Developer" }, "children": [{ "age": 8, "name": "Jo Rosales" }, { "age": 7, "name": "Shawn Burke" }, { "age": 10, "name": "Gomez Guthrie" }, { "age": 9, "name": "Tandy Christensen" }, { "age": 3, "name": "Jody Langley" }], "id": "658f34f88882211aa8679240" }' // Nice. ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/LispInterpreter.mls ================================================ :NewDefs // Summon the underlying JavaScript `Object.is` function so that we can compare // any objects. For example, functions do not conforms to `Eql` so we cannot // compare them with `===` directly. But, we can use `Object.is`. declare fun Object: nothing let (=:=) objectEqual: (anything, anything) -> Bool = Object.is fun (=/=) objectNotEqual(x, y) = not(x =:= y) //│ let (=:=) objectEqual: (anything, anything) -> Bool //│ fun (=/=) objectNotEqual: (anything, anything) -> Bool //│ fun Object: nothing //│ objectEqual //│ = [Function: is] type NStr = Str & { length: Int, at: Int -> NStr, charAt: Int -> NStr, charCodeAt: Int -> Int, slice: (Int, Int) -> NStr, startsWith: Str -> Bool, endsWith: Str -> Bool, split: Str -> Array[NStr], trim: () -> NStr, trimStart: () -> NStr, trimEnd: () -> NStr, padStart: (Int, Str) -> NStr, padEnd: (Int, Str) -> NStr, repeat: Int -> NStr, indexOf: Str -> Int, lastIndexOf: Str -> Int, includes: Str -> Bool, } declare fun String: anything -> NStr fun (++) strcat(a, b) = String of concat(a)(b) //│ type NStr = Str & { //│ at: Int -> NStr, //│ charAt: Int -> NStr, //│ charCodeAt: Int -> Int, //│ endsWith: Str -> Bool, //│ includes: Str -> Bool, //│ indexOf: Str -> Int, //│ lastIndexOf: Str -> Int, //│ length: Int, //│ padEnd: (Int, Str) -> NStr, //│ padStart: (Int, Str) -> NStr, //│ repeat: Int -> NStr, //│ slice: (Int, Int) -> NStr, //│ split: Str -> Array[NStr], //│ startsWith: Str -> Bool, //│ trim: () -> NStr, //│ trimEnd: () -> NStr, //│ trimStart: () -> NStr //│ } //│ fun (++) strcat: (Str, Str) -> NStr //│ fun String: anything -> NStr fun (!==) notEqual(x, y) = not(x === y) declare fun parseInt: (Str, Int) -> Int //│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool //│ fun parseInt: (Str, Int) -> Int // `List` and its utilities: abstract class List[out T]: Cons[T] | Nil class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) fun reverse(l: List['A]): List['A] = let rec r(l', l) = if l is Cons(x, xs) then r(x :: l', xs) else l' r(Nil, l) fun join(sep: Str, xs: List['B]) = if xs is Cons(x, Nil) then toString(x) Cons(x, xs) then toString(x) ++ sep ++ join(sep, xs) Nil then "" fun showList(xs: List['C]) = "[" ++ join(", ", xs) ++ "]" fun map(f: 'D -> 'E, xs: List['D]): List['E] = if xs is Cons(x, xs) then f(x) :: map(f, xs) Nil then Nil fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs is Cons(x, xs') and ys is Cons(y, ys') then equal(x, y) and equalList(xs', ys', equal) Nil and ys is Nil then true else false // `Option` and its utilities: abstract class Option[out A]: Some[A] | None class Some[out A](value: A) extends Option[A] module None extends Option //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] //│ fun reverse: forall 'T0. (l: List['T0]) -> List['T0] //│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) //│ fun showList: (xs: List[anything]) -> NStr //│ fun map: forall 'D 'T1. (f: 'D -> 'T1, xs: List['D]) -> List['T1] //│ fun equalList: forall 'A. (xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool) -> Bool //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option // _ ____ _____ // / \ / ___|_ _| // / _ \ \___ \ | | // / ___ \ ___) || | // /_/ \_\____/ |_| // abstract class Expr: Lambda | BuiltIn | Instance | Thunk | StrLit | IntLit | ExprList class Lambda(f: List[Data] -> Data) extends Expr class BuiltIn(e: List[Data] -> Data) extends Expr class Instance(n: Str, m: List[[Str, Expr]]) extends Expr class Thunk(e: () -> Data) extends Expr class StrLit(s: Str) extends Expr class IntLit(n: Int) extends Expr class ExprList(l: List[Expr]) extends Expr fun (=@=) equalExpr(x: Expr, y: Expr): Bool = if x is Lambda(f) and y is Lambda(g) then f =:= g BuiltIn(f) and y is BuiltIn(g) then f =:= g Instance(n, m) and y is Instance(n', m') then let equalPair = ([k, v], [k', v']) => k =:= k' and equalExpr(v, v') n === n' and equalList(m, m', equalPair) Thunk(e) and y is Thunk(e') then e =:= e' StrLit(s) and y is StrLit(s') then s === s' IntLit(n) and y is IntLit(n') then n === n' ExprList(l) and y is ExprList(l') then equalList(l, l', equalExpr) else false fun showExpr(e: Expr): Str = if e is Lambda(f) then "" BuiltIn(f) then "" Instance(n, m) then "" Thunk(e) then "" // StrLit(s) then "str:" ++ s // IntLit(n) then "int:" ++ toString(n) StrLit(s) then s IntLit(n) then toString(n) ExprList(l) then showList(map(showExpr, l)) abstract class Data: Literal | DataList | Symbol class Literal(e: Expr) extends Data class DataList(l: List[Data]) extends Data class Symbol(s: Str) extends Data fun (=#=) equalData(x: Data, y: Data): Bool = if x is Literal(e) and y is Literal(e') then e =@= e' DataList(l) and y is DataList(l') then equalList(l, l', equalData) Symbol(s) and y is Symbol(s') then s === s' else false fun showData(d: Data): Str = if d is Literal(e) then showExpr(e) DataList(l) then "(" ++ join(", ", map(showData, l)) ++ ")" // Symbol(s) then "sym:" ++ s Symbol(s) then s //│ abstract class Expr: BuiltIn | ExprList | Instance | IntLit | Lambda | StrLit | Thunk //│ class Lambda(f: List[Data] -> Data) extends Expr //│ class BuiltIn(e: List[Data] -> Data) extends Expr //│ class Instance(n: Str, m: List[[Str, Expr]]) extends Expr //│ class Thunk(e: () -> Data) extends Expr //│ class StrLit(s: Str) extends Expr //│ class IntLit(n: Int) extends Expr //│ class ExprList(l: List[Expr]) extends Expr //│ fun (=@=) equalExpr: (x: Expr, y: Expr) -> Bool //│ fun showExpr: (e: Expr) -> Str //│ abstract class Data: DataList | Literal | Symbol //│ class Literal(e: Expr) extends Data //│ class DataList(l: List[Data]) extends Data //│ class Symbol(s: Str) extends Data //│ fun (=#=) equalData: (x: Data, y: Data) -> Bool //│ fun showData: (d: Data) -> Str // _____ _ _ // |_ _|___ | | __ ___ _ __ (_) ____ ___ _ __ // | | / _ \ | |/ // _ \| '_ \ | ||_ // _ \| '__| // | || (_) || <| __/| | | || | / /| __/| | // |_| \___/ |_|\_\\___||_| |_||_|/___|\___||_| // fun skipBlank(s: NStr, i: Int): Int = if i < 0 then skipBlank(s, 0) i >= s.length then s.length s.charCodeAt(i) == 32 then skipBlank(s, i + 1) else i //│ fun skipBlank: (s: NStr, i: Int) -> Int fun scanWhile(s: NStr, i: Int, p: Str -> Bool): Option[[Str, Int]] = let rec aux(acc, i) = if i < s.length and s.charAt(i) is ch and p of ch then aux(acc ++ ch, i + 1) else [acc, i] if aux("", i) is ["", _] then None [acc, i] then Some([acc, i]) //│ fun scanWhile: (s: NStr, i: Int, p: Str -> Bool) -> Option[[Str, Int]] fun isDelimiter(ch) = ch !== " " and ch !== "(" and ch !== ")" //│ fun isDelimiter: Eql[" " | "(" | ")"] -> Bool fun nextToken(s: NStr, i: Int): Option[[Str, Int]] = let i' = skipBlank(s, i) if s.charAt(i') is "(" then Some(["(", i' + 1]) ")" then Some([")", i' + 1]) else scanWhile(s, i', isDelimiter) //│ fun nextToken: (s: NStr, i: Int) -> Option[[Str, Int]] fun tokenize(s: Str): List[Str] = let s' = String(s) let rec aux(acc, i) = if nextToken(s', i) is None then reverse(acc) Some([token, i']) then aux(token :: acc, i') aux(Nil, 0) //│ fun tokenize: (s: Str) -> List[Str] showList of tokenize("") showList of tokenize("12") showList of tokenize("x") showList of tokenize("(quote (cons 1 nil))") showList of tokenize("(+ 1 2)") //│ NStr //│ res //│ = '[]' //│ res //│ = '[12]' //│ res //│ = '[x]' //│ res //│ = '[(, quote, (, cons, 1, nil, ), )]' //│ res //│ = '[(, +, 1, 2, )]' // ____ // | _ \ __ _ _ __ ___ ___ _ __ // | |_) |/ _` || '__|/ __| / _ \| '__| // | __/| (_| || | \__ \| __/| | // |_| \__,_||_| |___/ \___||_| // fun isDigit(n) = 48 <= n and n <= 57 fun isDigits(s: Str): Bool = let s' = String(s) let rec aux(i) = if i < s'.length and isDigit of s'.charCodeAt(i) then aux(i + 1) else i === s'.length aux(0) isDigits("123") isDigits("123jump") isDigits("bruh") //│ fun isDigit: Num -> Bool //│ fun isDigits: (s: Str) -> Bool //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = false abstract class ParseResult[out A]: Success[A] | Failure class Success[out A](value: A) extends ParseResult[A] class Failure(error: Str) extends ParseResult[nothing] //│ abstract class ParseResult[A]: Failure | Success[A] //│ class Success[A](value: A) extends ParseResult //│ class Failure(error: Str) extends ParseResult // Notes // ===== // Sometimes, the precedence of comma is less than `of`. // For example, in `[Success of DataList of reverse of acc, tail]`. fun parseExpr(tokens: List[Str]): [ParseResult[Data], List[Str]] = if tokens is Cons("(", tail) then parseList(tail) Cons(")", _) then [Failure("Unmatched closing parenthesis."), tokens] Cons(token, tail) and isDigits(token) then [(Success of Literal of IntLit of parseInt(token, 10)), tail] else [(Success of Symbol of token), tail] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] fun parseList(tokens: List[Str]): [ParseResult[DataList], List[Str]] = let rec collect(acc, ts) = if ts is Cons(")", tail) then [(Success of DataList of reverse of acc), tail] Cons and parseExpr(ts) is [Success(data), rest] then collect(data :: acc, rest) [Failure(_) as failure, rest] then [failure, rest] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] collect(Nil, tokens) //│ fun parseExpr: (tokens: List[Str]) -> [ParseResult[Data], List[Str]] //│ fun parseList: (tokens: List[Str]) -> [ParseResult[DataList], List[Str]] fun showParse(source: Str): Str = if parseExpr(tokenize(source)) is [Success(data), _] then "Ok: " ++ showData(data) [Failure(error), _] then "Error: " ++ error //│ fun showParse: (source: Str) -> Str showParse("(cons 1 nil)") showParse("(cons 1 (cons 2 nil))") showParse("(cons 1 (cons 2 (cons 3 nil)))") showParse("(+ 1 2)") showParse("(car (cons 1 nil))") //│ Str //│ res //│ = 'Ok: (cons, 1, nil)' //│ res //│ = 'Ok: (cons, 1, (cons, 2, nil))' //│ res //│ = 'Ok: (cons, 1, (cons, 2, (cons, 3, nil)))' //│ res //│ = 'Ok: (+, 1, 2)' //│ res //│ = 'Ok: (car, (cons, 1, nil))' // _____ // | ____| _ __ __ __ // | _| | '_ \\ \ / / // | |___ | | | |\ V / // |_____||_| |_| \_/ // // As of the time I wrote this code, MLscript did not yet support term // refinement, so I used lambda expression to implement `Env` first. type Env[T] = Str -> T let emptyEnv: Str -> nothing = _ => error // The lookup function becomes useless because we can just call the `Env`. fun lookup(env, name: Str): 'A = env(name) //│ type Env[T] = Str -> T //│ let emptyEnv: Str -> nothing //│ fun lookup: forall 'A. (Str -> 'A, name: Str) -> 'A //│ emptyEnv //│ = [Function: emptyEnv] // It is tricky to write an annotation that simplifies the inferred type. fun (+:) extend(env, [name, expr]: [Str, 'A]) = (name': Str) => if name' === name then expr else env(name') //│ fun (+:) extend: forall 'A. (Str -> 'A, [Str, 'A]) -> (name': Str) -> 'A fun extendRec(env, name: Str, expr: (Str -> 'A) -> 'A) = let rec env'(name': Str): 'A = if name' === name then expr(env') else env(name') env' //│ fun extendRec: forall 'A. (Str -> 'A, name: Str, expr: (Str -> 'A) -> 'A) -> (name': Str) -> 'A fun extendParameters(env: Str -> 'A, ps: List[Str], vs: List['A]) = if [ps, vs] is [Nil, Nil] then env [Cons(p, ps'), Cons(v, vs')] then extendParameters(extend(env, [p, v]), ps', vs') else error //│ fun extendParameters: forall 'A. (env: Str -> 'A, ps: List[Str], vs: List['A]) -> Str -> 'A fun (++:) extendEntries(env, ps: List[[Str, 'A]]) = if ps is Nil then env Cons(p, ps') then extendEntries(extend(env, p), ps') else error //│ fun (++:) extendEntries: forall 'A 'a. (Str -> 'A & 'a, ps: List[[Str, 'A]]) -> ((name': Str) -> 'A | 'a) let intEnv1 = extend(emptyEnv, ["one", 1]) let intEnv2 = extend(intEnv1, ["two", 2]) let intEnv3 = extend(intEnv2, ["three", 3]) //│ let intEnv1: (name': Str) -> 1 //│ let intEnv2: (name': Str) -> (1 | 2) //│ let intEnv3: (name': Str) -> (1 | 2 | 3) //│ intEnv1 //│ = [Function (anonymous)] //│ intEnv2 //│ = [Function (anonymous)] //│ intEnv3 //│ = [Function (anonymous)] // Make a tuple to save some lines. [intEnv1("one"), intEnv2("one"), intEnv3("one"), intEnv2("two"), intEnv3("two"), intEnv3("three")] //│ [1, 1 | 2, 1 | 2 | 3, 1 | 2, 1 | 2 | 3, 1 | 2 | 3] //│ res //│ = [ 1, 1, 1, 2, 2, 3 ] :re intEnv1("two") intEnv2("three") intEnv3("bruh") //│ 1 | 2 | 3 //│ res //│ Runtime error: //│ Error: an error was thrown //│ res //│ Runtime error: //│ Error: an error was thrown //│ res //│ Runtime error: //│ Error: an error was thrown let intEnv6 = intEnv3 ++: (["four", 4] :: ["five", 5] :: ["six", 6] :: Nil) //│ let intEnv6: (name': Str) -> (1 | 2 | 3 | 4 | 5 | 6) //│ intEnv6 //│ = [Function (anonymous)] [intEnv6("one"), intEnv6("two"), intEnv6("three"), intEnv6("four"), intEnv6("five"), intEnv6("six")] //│ [1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6] //│ res //│ = [ 1, 2, 3, 4, 5, 6 ] :re intEnv6("seven") //│ 1 | 2 | 3 | 4 | 5 | 6 //│ res //│ Runtime error: //│ Error: an error was thrown fun builtinEq(args: List[Data]): Data = if args is Cons(x, Cons(y, Nil)) and x =#= y then Literal(IntLit(1)) else Literal(IntLit(0)) else error fun builtinAdd(args: List[Data]): Data = if args is Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x + y)) else error fun builtinSub(args: List[Data]): Data = if args is Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x - y)) else error fun builtinMul(args: List[Data]): Data = if args is Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x * y)) else error let builtinNil = DataList(Nil) fun builtinCons(args: List[Data]): Data = if args is Cons(x, Cons(DataList(xs), Nil)) then DataList(x :: xs) else error fun builtinCar(args: List[Data]): Data = if args is Cons(DataList(Cons(x, _)), Nil) then x else error fun builtinCdr(args: List[Data]): Data = if args is Cons(DataList(Cons(_, xs)), Nil) then DataList(xs) else error fun builtinNull(args: List[Data]): Data = if args is Cons(DataList(Nil), Nil) then Literal(IntLit(1)) else Literal(IntLit(0)) //│ fun builtinEq: (args: List[Data]) -> Data //│ fun builtinAdd: (args: List[Data]) -> Data //│ fun builtinSub: (args: List[Data]) -> Data //│ fun builtinMul: (args: List[Data]) -> Data //│ let builtinNil: DataList //│ fun builtinCons: (args: List[Data]) -> Data //│ fun builtinCar: (args: List[Data]) -> Data //│ fun builtinCdr: (args: List[Data]) -> Data //│ fun builtinNull: (args: List[Data]) -> Data //│ builtinNil //│ = DataList {} let globalEnv = emptyEnv ++: ["eq", Literal(Lambda(builtinEq))] :: ["+", Literal(Lambda(builtinAdd))] :: ["-", Literal(Lambda(builtinSub))] :: ["*", Literal(Lambda(builtinMul))] :: ["nil", builtinNil] :: ["cons", Literal(Lambda(builtinCons))] :: ["car", Literal(Lambda(builtinCar))] :: ["cdr", Literal(Lambda(builtinCdr))] :: ["null", Literal(Lambda(builtinNull))] :: Nil //│ let globalEnv: (name': Str) -> (DataList | Literal) //│ globalEnv //│ = [Function (anonymous)] fun toName(x: Data): Str = if x is Symbol(s) then s else error //│ fun toName: (x: Data) -> Str fun asList(x: Data): List[Data] = if x is DataList(ys) then ys else error //│ fun asList: (x: Data) -> List[Data] fun eval(x: Data, env: Str -> Data): Data = if x is Literal(StrLit(_)) as lit then lit Literal(IntLit(_)) as lit then lit Symbol(name) then env(name) DataList(Cons(Symbol("val"), tail)) and tail is Cons(param, Cons(expr, Cons(rest, Nil))) then eval(rest, env +: [toName(param), eval(expr, env)]) else error DataList(Cons(Symbol("def"), Cons(param, Cons(body, Cons(rest, Nil))))) then let env' = extendRec of env, toName(param), (env => eval(body, env)) eval(rest, env') DataList(Cons(Symbol("if"), Cons(cond, Cons(thenPart, Cons(elsePart, Nil))))) and eval(cond, env) is Literal(IntLit(0)) then eval(elsePart, env) else eval(thenPart, env) DataList(Cons(Symbol("quote"), Cons(y, Nil))) then y DataList(Cons(Symbol("lambda"), Cons(params, Cons(body, Nil)))) then let ps = map(toName, asList(params)) Literal(Lambda(args => eval(body, extendParameters(env, ps, args)))) DataList(Cons(operator, operands)) and eval(operator, env) is Literal(Lambda(f)) then f of map((x) => eval(x, env), operands) else error // application of a non-function else error // unrecognized program //│ fun eval: (x: Data, env: Str -> Data) -> Data fun showEval(source: Str): Str = if parseExpr(tokenize(source)) is [Success(data), _] then "Ok: " ++ showData of eval(data, globalEnv) [Failure(error), _] then "Error: " ++ error //│ fun showEval: (source: Str) -> Str showEval("1") showEval("(+ 1 2)") showEval("(val x 7 (val y 6 (* x y)))") showEval("(quote x)") showEval("(cons 1 nil)") showEval("(car (cons 1 nil))") showEval("(null 0)") showEval("(null nil)") showEval("(def id (lambda (x) x) (id 42))") //│ Str //│ res //│ = 'Ok: 1' //│ res //│ = 'Ok: 3' //│ res //│ = 'Ok: 42' //│ res //│ = 'Ok: x' //│ res //│ = 'Ok: (1)' //│ res //│ = 'Ok: 1' //│ res //│ = 'Ok: 0' //│ res //│ = 'Ok: 1' //│ res //│ = 'Ok: 42' showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") //│ Str //│ res //│ = 'Ok: 120' //│ res //│ = 'Ok: 55' //│ res //│ = 'Ok: 1275' //│ res //│ = 'Ok: 29' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/List.mls ================================================ :NewDefs // TODO abstract class List[out A]: (Cons[A] | Nil) { fun isEmpty: Bool fun map[B]: (A -> B) -> List[B] } class Cons[out A](head: A, tail: List[A]) extends List[A] { fun isEmpty: Bool = false fun map(f) = Cons(f(head), tail.map(f)) } module Nil extends List { fun isEmpty: Bool = true fun map(f) = Nil } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.10: fun map(f) = Cons(f(head), tail.map(f)) //│ ╙── ^^^^ //│ abstract class List[A]: Cons[A] | Nil { //│ fun isEmpty: Bool //│ fun map: forall 'B. (A -> 'B) -> List['B] //│ } //│ class Cons[A](head: A, tail: List[A]) extends List { //│ fun isEmpty: Bool //│ fun map: forall 'A. (A -> 'A) -> Cons['A] //│ } //│ module Nil extends List { //│ fun isEmpty: Bool //│ fun map: anything -> Nil //│ } ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/Option.mls ================================================ :NewDefs abstract class MyOption[out T]: (MySome[T] | MyNone) { virtual fun filter: (p: T -> Bool) -> MyOption[T] } class MySome[out T](val value: T) extends MyOption[T] { fun filter(p) = if p of value then MySome(value) else MyNone } module MyNone extends MyOption[nothing] { fun filter(_) = MyNone } //│ abstract class MyOption[T]: MyNone | MySome[T] { //│ fun filter: (p: T -> Bool) -> MyOption[T] //│ } //│ class MySome[T](value: T) extends MyOption { //│ fun filter: (T -> Bool) -> (MyNone | MySome[T]) //│ } //│ module MyNone extends MyOption { //│ fun filter: anything -> MyNone //│ } // The following code aims to find a workaround that allows the functions // operating ADT to be defined as member functions of classes, and also ensures // the correct generation of JavaScript code. // Create an alias constructor for `Some` // ====================================== // // This works: fun some: forall 'a: 'a -> Option['a] fun some = x => Some(x) // // This doesn't work with `map`: // let some: forall 'a: 'a -> Option['a] = x => Some(x) // // Create an alias constructor for `None` // ====================================== // // This works: // fun none: forall 'a: () -> Option['a] // fun none = () => None // // This also works: // fun none: Option[nothing] // fun none = None // This also works: let none: () -> Option[nothing] = () => None // // This also works but failed in code generation: // let none: Option[nothing] = None // // The class definitions // ===================== abstract class Option[out T]: (Some[T] | None) { virtual fun isEmpty: Bool virtual fun isDefined: Bool virtual fun map: forall 'b: (T -> 'b) -> Option['b] virtual fun flatMap: forall 'b: (T -> Option['b]) -> Option['b] virtual fun filter: (p: T -> Bool) -> Option[T] virtual fun get: T } class Some[out T](val value: T) extends Option[T] { fun isEmpty = false fun isDefined = true fun map(f) = some(f(value)) fun flatMap(f) = f(value) fun filter(p) = if p of value then some(value) else none() fun get = value } module None extends Option[nothing] { fun isEmpty = true fun isDefined = false fun map(_) = none() fun flatMap(_) = none() fun filter(_) = none() fun get = error } //│ fun some: forall 'T. 'T -> Some['T] //│ let none: () -> Option[nothing] //│ abstract class Option[T]: None | Some[T] { //│ fun filter: (p: T -> Bool) -> Option[T] //│ fun flatMap: forall 'b. (T -> Option['b]) -> Option['b] //│ fun get: T //│ fun isDefined: Bool //│ fun isEmpty: Bool //│ fun map: forall 'b0. (T -> 'b0) -> Option['b0] //│ } //│ class Some[T](value: T) extends Option { //│ fun filter: (T -> Bool) -> Option[T] //│ fun flatMap: forall 'c. (T -> 'c) -> 'c //│ fun get: T //│ fun isDefined: true //│ fun isEmpty: false //│ fun map: forall 'a. (T -> 'a) -> Option['a] //│ } //│ module None extends Option { //│ fun filter: anything -> Option[nothing] //│ fun flatMap: anything -> Option[nothing] //│ fun get: nothing //│ fun isDefined: false //│ fun isEmpty: true //│ fun map: anything -> Option[nothing] //│ } //│ fun some: forall 'a0. 'a0 -> Option['a0] //│ none //│ = [Function: none] some(0).map(x => x + 1).get //│ Int //│ res //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/STLC.mls ================================================ :NewDefs fun (++) concatOp(a, b) = concat(a)(b) //│ fun (++) concatOp: (Str, Str) -> Str fun par(a) = "(" ++ a ++ ")" //│ fun par: Str -> Str type Option[out A] = Some[A] | None class Some[out A](value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None type Result[A, B] = Ok[A] | Err[B] class Ok[A](value: A) class Err[A](message: A) //│ type Result[A, B] = Err[B] | Ok[A] //│ class Ok[A](value: A) //│ class Err[A](message: A) type Type = FunctionType | PrimitiveType class PrimitiveType(name: Str) class FunctionType(lhs: Type, rhs: Type) //│ type Type = FunctionType | PrimitiveType //│ class PrimitiveType(name: Str) //│ class FunctionType(lhs: Type, rhs: Type) // Helpers. fun _f(lhs, rhs) = FunctionType(lhs, rhs) fun _t(name) = PrimitiveType(name) //│ fun _f: (Type, Type) -> FunctionType //│ fun _t: Str -> PrimitiveType type Term = Lit | Var | Abs | App class Lit(tag: Str, ty: Type) class Var(name: Str) class Abs(lhs: Var, lty: Type, rhs: Term) class App(lhs: Term, rhs: Term) // class App(lhs: Term, rhs: Term): Term //│ type Term = Abs | App | Lit | Var //│ class Lit(tag: Str, ty: Type) //│ class Var(name: Str) //│ class Abs(lhs: Var, lty: Type, rhs: Term) //│ class App(lhs: Term, rhs: Term) type TreeMap[A] = Node[A] | Empty class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) module Empty //│ type TreeMap[A] = Empty | Node[A] //│ class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) //│ module Empty fun insert(t, k, v) = if t is Node(k', _, l, r) and slt(k, k') then Node(k', v, insert(l, k, v), r) sgt(k, k') then Node(k', v, l, insert(r, k, v)) _ then Node(k, v, l, r) Empty then Node(k, v, Empty, Empty) fun find(t, k) = if t is Node(k', v, l, r) and slt(k, k') then find(l, k) sgt(k, k') then find(r, k) _ then Some(v) Empty then None //│ fun insert: forall 'A. (Empty | Node['A], Str, 'A) -> Node['A] //│ fun find: forall 'A0. (Empty | Node['A0], Str) -> (None | Some['A0]) fun showType(ty) = if ty is FunctionType(PrimitiveType(name), rhs) then name ++ " -> " ++ showType(rhs) FunctionType(lhs, rhs) then "(" ++ showType(lhs) ++ ") -> " ++ showType(rhs) PrimitiveType(name) then name //│ fun showType: (FunctionType | PrimitiveType) -> Str showType(_t("int")) showType(_f(_t("int"), _t("bool"))) showType(_f(_f(_t("int"), _t("bool")), _t("bool"))) showType(_f(_t("bool"), _f(_t("int"), _t("bool")))) //│ Str //│ res //│ = 'int' //│ res //│ = 'int -> bool' //│ res //│ = '(int -> bool) -> bool' //│ res //│ = 'bool -> int -> bool' fun typeEqual(t1, t2) = if t1 is PrimitiveType(name1) and t2 is PrimitiveType(name2) then eq(name1)(name2) t1 is FunctionType(lhs1, rhs1) and t2 is FunctionType(lhs2, rhs2) then typeEqual(lhs1, lhs2) and typeEqual(rhs1, rhs2) _ then false //│ fun typeEqual: (Object, Object) -> Bool fun showTerm(t) = if t is Lit(tag, _) then tag Var(name) then name Abs(lhs, ty, rhs) then "&" ++ showTerm(lhs) ++ ": " ++ showType(ty) ++ " => " ++ showTerm(rhs) App(Abs(lhs0, ty, lhs1), rhs) then "((" ++ showTerm(Abs(lhs0, ty, rhs)) ++ ") " ++ showTerm(rhs) ++ ")" App(lhs, rhs) then par(showTerm(lhs) ++ " " ++ showTerm(rhs)) //│ fun showTerm: (Abs | App | Lit | Var) -> Str showTerm(Var("x")) showTerm(Abs(Var("x"), _t("int"), Var("y"))) showTerm(App(Var("x"), Var("y"))) showTerm(App(Abs(Var("x"), _t("int"), Var("y")), Var("z"))) //│ Str //│ res //│ = 'x' //│ res //│ = '&x: int => y' //│ res //│ = '(x y)' //│ res //│ = '((&x: int => z) z)' // Removing the return type annotation causes stack overflow. fun typeTerm(t: Term, ctx: TreeMap[Type]): Result[Type, Str] = if t is Lit(_, ty) then Ok(ty) Var(name) and find(ctx, name) is Some(ty) then Ok(ty) None then Err("unbound variable `" ++ name ++ "`") Abs(Var(name), ty, body) and typeTerm(body, insert(ctx, name, ty)) is Ok(resTy) then Ok(FunctionType(ty, resTy)) Err(message) then Err(message) App(lhs, rhs) and typeTerm(lhs, ctx) is Ok(FunctionType(pTy, resTy)) and typeTerm(rhs, ctx) is Ok(aTy) and typeEqual(pTy, aTy) then Ok(resTy) else Err("expect the argument to be of type `" ++ showType(pTy) ++ "` but found `" ++ showType(aTy) ++ "`") Err(message) then Err(message) Ok(PrimitiveType(name)) then Err("cannot apply primitive type `" ++ name ++ "`") Err(message) then Err(message) //│ fun typeTerm: (t: Term, ctx: TreeMap[Type]) -> Result[Type, Str] fun showTypeTerm(t, ctx) = if typeTerm(t, ctx) is Ok(ty) then showTerm(t) ++ " : " ++ showType(ty) Err(message) then "Type error: " ++ message //│ fun showTypeTerm: (Term, TreeMap[Type]) -> Str showTypeTerm(Var("x"), Empty) showTypeTerm(Abs(Var("x"), _t("int"), Var("x")), Empty) showTypeTerm(App(Var("f"), Lit("0", _t("int"))), insert(Empty, "f", _f(_t("int"), _t("int")))) showTypeTerm(App(Var("f"), Lit("0.2", _t("float"))), insert(Empty, "f", _f(_t("int"), _t("int")))) showTypeTerm(App(Var("f"), Lit("0", _t("int"))), insert(Empty, "f", _t("Str"))) //│ Str //│ res //│ = 'Type error: unbound variable `x`' //│ res //│ = '&x: int => x : int -> int' //│ res //│ = '(f 0) : int' //│ res //│ = 'Type error: expect the argument to be of type `int` but found `float`' //│ res //│ = 'Type error: cannot apply primitive type `Str`' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/SimpleLisp.mls ================================================ :NewDefs fun (++) strcat(a: Str, b: Str): Str = concat(a)(b) //│ fun (++) strcat: (a: Str, b: Str) -> Str declare module JSON { fun stringify: (data: anything) -> Str } declare class Function(source: Str) { fun call: () -> nothing } fun fatal(message: Str): nothing = let raise = Function("throw new Error(" ++ JSON.stringify(message) ++ ")") raise.call() //│ declare module JSON { //│ fun stringify: (data: anything) -> Str //│ } //│ declare class Function(source: Str) { //│ fun call: () -> nothing //│ } //│ fun fatal: (message: Str) -> nothing abstract class List[A]: Nil | Cons[A] class Cons[A](head: A, tail: List[A]) extends List[A] module Nil extends List fun (::) cons[A](head: A, tail: List[A]): List[A] = Cons(head, tail) fun map(f: 'A -> 'B, list: List['A]): List['B] = if list is Nil then Nil Cons(head, tail) then Cons(f(head), map(f, tail)) fun showList(sep: Str, showItem: 'A -> Str) = let rec aux(list: List['A]) = if list is Nil then "" Cons(head, Nil) then showItem(head) Cons(head, tail) then showItem(head) ++ sep ++ aux(tail) aux //│ abstract class List[A]: Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'A. (head: 'A, tail: List['A]) -> List['A] //│ fun map: forall 'A0 'A1. (f: 'A0 -> 'A1, list: List['A0]) -> List['A1] //│ fun showList: forall 'A2. (sep: Str, showItem: 'A2 -> Str) -> (forall 'A3. (list: List['A3]) -> Str) //│ where //│ 'A3 <: 'A2 abstract class Context: Empty | Bind module Empty extends Context class Bind(name: Str, data: Data, tail: Context) extends Context fun find(name: Str, context: Context): Data = if context is Empty then fatal("undefined symbol " ++ name) Bind(name', data, t) then if name' === name then data else find(name, t) abstract class Data: ListData | Quote | Symbol | Literal | Builtin | Thunk class ListData(list: List[Data]) extends Data class Quote(data: Data) extends Data class Symbol(name: Str) extends Data class Literal(value: Int) extends Data class Builtin(impl: (List[Data], Context) -> Data) extends Data class Thunk(impl: (Context) -> Data) extends Data //│ abstract class Context: Bind | Empty //│ module Empty extends Context //│ class Bind(name: Str, data: Data, tail: Context) extends Context //│ fun find: (name: Str, context: Context) -> Data //│ abstract class Data: Builtin | ListData | Literal | Quote | Symbol | Thunk //│ class ListData(list: List[Data]) extends Data //│ class Quote(data: Data) extends Data //│ class Symbol(name: Str) extends Data //│ class Literal(value: Int) extends Data //│ class Builtin(impl: (List[Data], Context) -> Data) extends Data //│ class Thunk(impl: Context -> Data) extends Data fun showData(data: Data): Str = if data is ListData(list) then "(" ++ showList(" ", showData)(list) ++ ")" Quote(data) then "'" ++ showData(data) Symbol(name) then name Literal(value) then toString(value) Builtin(impl) then "" Thunk(impl) then "" //│ fun showData: (data: Data) -> Str fun add(arguments: List[Data], context: Context): Data = if arguments is Cons(Literal(a), Cons(Literal(b), Nil)) then Literal(a + b) else fatal("invalid arguments") //│ fun add: (arguments: List[Data], context: Context) -> Data let context = Bind("+", Builtin(add), Empty) //│ let context: Bind //│ context //│ = Bind {} fun eval(program: Data, context: Context): Data = if program is Literal(value) then Literal(value) Symbol(name) then find(name, context) Thunk(impl) then impl(context) ListData(Cons(Symbol("let"), Cons(Symbol(name), Cons(data, Cons(rest, Nil))))) then let data' = eval(data, context) let context' = Bind(name, data', context) eval(rest, context') ListData(Cons(Symbol("val"), Cons(Symbol(name), Cons(data, Cons(rest, Nil))))) then let data' = Thunk((context) => eval(data, context)) let context' = Bind(name, data', context) eval(rest, context') ListData(Cons(Symbol("if"), Cons(test, Cons(thenPart, Cons(elsePart, Nil))))) and eval(test, context) is Literal(0) then eval(elsePart, context) else eval(thenPart, context) ListData(Cons(Symbol("quote"), Cons(data, Nil))) then data ListData(Cons(Symbol(callee), arguments)) and let callee' = find(callee, context) let arguments' = map(argument => eval(argument, context), arguments) callee' is Builtin(impl) then impl(arguments', context) else fatal("callee is not callable") else fatal("unknown program") //│ fun eval: (program: Data, context: Context) -> Data let data1 = ListData of Symbol("+") :: Literal(1) :: Literal(2) :: Nil showData of data1 showData of eval(data1, context) //│ let data1: ListData //│ Str //│ data1 //│ = ListData {} //│ res //│ = '(+ 1 2)' //│ res //│ = '3' let data2 = ListData of Symbol("let") :: Symbol("x") :: Literal(1) :: (ListData of Symbol("+") :: Symbol("x") :: Literal(2) :: Nil) :: Nil showData of data2 showData of eval(data2, context) //│ let data2: ListData //│ Str //│ data2 //│ = ListData {} //│ res //│ = '(let x 1 (+ x 2))' //│ res //│ = '3' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/SimpleList.mls ================================================ :NewDefs abstract class List[T]: (Cons[T] | Nil) class Cons[T](val head: T, val tail: List[T]) extends List[T] module Nil extends List //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] 1 :: 2 :: 3 :: 4 :: 5 :: 6 :: 7 :: 8 :: Nil //│ Cons['T] //│ where //│ 'T :> 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 //│ res //│ = Cons {} ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/examples/SimpleTree.mls ================================================ :NewDefs // abstract class Tree[out A]: (Empty | Node[A]) // class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] // module Empty extends Tree // //│ abstract class Tree[A]: Empty | Node[A] // //│ class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree // //│ module Empty extends Tree // fun insert(t, v) = if t is // Node(v', l, r) and // v < v' then Node(v', insert(l, v), r) // v > v' then Node(v', l, insert(r, v)) // _ then t // Empty then Node(v, Empty, Empty) // //│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] 1 :: 2 :: 3 :: 4 :: Nil //│ Cons[1 | 2 | 3 | 4] //│ res //│ = Cons {} ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/DirectLines.mls ================================================ :NewDefs fun f(x, y) = if x == 0 then "x" y == 0 then "y" _ then "nah" //│ fun f: (Num, Num) -> ("nah" | "x" | "y") abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun isValid(x) = if x then false else true //│ fun isValid: Bool -> Bool fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" is None and allowNone then "okay" is _ then "bad" //│ fun f: (Object & ~#Some | Some[Bool], Bool) -> ("bad" | "good" | "okay") fun f(x, y, z) = if x == 0 then "x" y == 1 then "y = 1" 2 and z == 0 then "z = 0" 9 then "z = 9" _ then "bruh" 3 then "y = 3" _ then "bruh" //│ fun f: (Num, Num, Num) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") :w fun f(a, b) = if a == 0 then 0 b == 1 then 1 2 then 2 _ then 7 else 3 //│ ╔══[WARNING] this case is unreachable //│ ║ l.48: else 3 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.47: _ then 7 //│ ╙── ^ //│ fun f: (Num, Num) -> (0 | 1 | 2 | 7) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/ElseIf.mls ================================================ :NewDefs fun f(x, y) = if x === 0 then true 1 then false else if y === 0 then true 1 then false else false //│ fun f: (Eql[0 | 1], Eql[0 | 1]) -> Bool fun f(x, y) = if x === 0 then true 1 then false else if y === 0 then true _ then false //│ fun f: (Eql[0 | 1], Eql[0]) -> Bool module Tru module Fals //│ module Tru //│ module Fals :e fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false //│ ╔══[ERROR] when `x` is `Tru` //│ ║ l.29: Tru and y is Tru then true //│ ║ ^^^ //│ ╟── `y` has 1 missing case //│ ║ l.29: Tru and y is Tru then true //│ ║ ^ //│ ╟── it can be module `Fals` //│ ║ l.30: Fals and y is Fals then false //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `Fals` //│ ║ l.30: Fals and y is Fals then false //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.30: Fals and y is Fals then false //│ ║ ^ //│ ╟── it can be module `Tru` //│ ║ l.29: Tru and y is Tru then true //│ ╙── ^^^ //│ fun f: (Fals | Tru, nothing) -> Bool // The base case. fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false Tru and y is Fals then true Fals and y is Tru then true //│ fun f: (Fals | Tru, Fals | Tru) -> Bool // Replace the `x is Fals` with `_` fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false _ and y is Tru then true Fals then false //│ fun f: (Object, Fals | Tru) -> Bool f(Tru, Tru) f(Tru, Fals) f(Fals, Tru) f(Fals, Fals) //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = true //│ res //│ = false :e fun g(x, y) = if x is true and y is true then true false and y is false then false //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.84: true and y is true then true //│ ║ ^ //│ ╟── it can be Boolean value `false` //│ ║ l.85: false and y is false then false //│ ╙── ^^^^^ //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.85: false and y is false then false //│ ║ ^ //│ ╟── it can be Boolean value `true` //│ ║ l.84: true and y is true then true //│ ╙── ^^^^ //│ fun g: (Bool, nothing) -> Bool // Test with real booleans fun g(x, y) = if x is true and y is true then true false and y is false then false _ and y is true then true false then false //│ fun g: (Object, Bool) -> Bool // Chained UCS terms fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false else if y is Tru then true Fals then false //│ fun f: (Object, Fals | Tru) -> Bool :e fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false else if y is Tru and x is Fals then true Fals and x is Tru then false //│ ╔══[ERROR] when `y` is `Tru` //│ ║ l.123: Tru and x is Fals then true //│ ║ ^^^ //│ ╟── `x` has 1 missing case //│ ║ l.123: Tru and x is Fals then true //│ ║ ^ //│ ╟── it can be module `Tru` //│ ║ l.124: Fals and x is Tru then false //│ ╙── ^^^ //│ ╔══[ERROR] when `y` is `Fals` //│ ║ l.124: Fals and x is Tru then false //│ ║ ^^^^ //│ ╟── `x` has 1 missing case //│ ║ l.124: Fals and x is Tru then false //│ ║ ^ //│ ╟── it can be module `Fals` //│ ║ l.123: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.123: Tru and x is Fals then true //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Tru` is not an instance of type `Fals` //│ ║ l.120: Tru and y is Tru then true //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Fals` //│ ║ l.123: Tru and x is Fals then true //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.123: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.124: Fals and x is Tru then false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Fals` is not an instance of type `Tru` //│ ║ l.121: Fals and y is Fals then false //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `Tru` //│ ║ l.124: Fals and x is Tru then false //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.124: Fals and x is Tru then false //│ ╙── ^^^ //│ fun f: (Fals | Tru, Fals | Tru) -> Bool fun h(x, y, p) = if x and p(x) then 0 y is Tru then 1 Fals then 2 //│ fun h: forall 'a. ('a & Bool, Fals | Tru, 'a -> Bool) -> (0 | 1 | 2) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/ErrorMessage.mls ================================================ :NewDefs class Point(x: Int, y: Int) //│ class Point(x: Int, y: Int) :e fun f(p) = if p is Point(x, y, z) then x + y + z //│ ╔══[ERROR] Type mismatch in field selection: //│ ╟── tuple literal of type `{0: ?#x, 1: ?#y}` does not have field '2' //│ ║ l.3: class Point(x: Int, y: Int) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into operator application with expected type `{2: ?a}` //│ ║ l.8: if p is //│ ║ ^^^^ //│ ║ l.9: Point(x, y, z) then x + y + z //│ ╙── ^^^^^^^^^ //│ fun f: Point -> Int :e :ge fun g(xs) = if xs is head :: _ then head //│ ╔══[ERROR] type identifier `::` not found //│ ║ l.25: head :: _ then head //│ ╙── ^^ //│ ╔══[ERROR] type identifier not found: :: //│ ║ l.25: head :: _ then head //│ ╙── ^^ //│ fun g: nothing -> error //│ Code generation encountered an error: //│ unresolved symbol :: ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/Exhaustiveness.mls ================================================ :NewDefs :NoJS class A() class B() class C() //│ class A() //│ class B() //│ class C() :e fun f(x, y) = if y is A and x is A then 0 B then 1 C then 2 y is B and x is A then 4 //│ ╔══[ERROR] when `y` is `B` //│ ║ l.19: y is B and //│ ║ ^ //│ ╟── `x` has 2 missing cases //│ ║ l.20: x is //│ ║ ^ //│ ╟── it can be class `B` //│ ║ l.17: B then 1 //│ ║ ^ //│ ╟── it can be class `C` //│ ║ l.18: C then 2 //│ ╙── ^ //│ fun f: (A, A | B) -> (0 | 1 | 2 | 4) :e // These operators are uninterpreted. So, it's impossible to reason the // exhaustiveness without SMT solvers. type Tree[A] = Node[A] | Empty module Empty { fun contains(wanted) = false } class Node[A](value: int, left: Tree[A], right: Tree[A]) { fun contains(wanted) = if wanted <= value then left.find(wanted) >= value then right.find(wanted) == value then true } //│ ╔══[ERROR] missing else branch //│ ║ l.47: == value then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ //│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `Num` //│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.45: <= value then left.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#Node & {Node#A = A}` does not contain member `find` //│ ║ l.45: <= value then left.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ //│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: >= value then right.find(wanted) //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `Num` //│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.46: >= value then right.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#Node & {Node#A = A}` does not contain member `find` //│ ║ l.46: >= value then right.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ //│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: >= value then right.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.47: == value then true //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `Num` //│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.47: == value then true //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.47: == value then true //│ ║ ^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ type Tree[A] = Empty | Node[A] //│ module Empty { //│ fun contains: anything -> false //│ } //│ class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ fun contains: Num -> (error | true) //│ } ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/Humiliation.mls ================================================ :NewDefs class Foo[T](x: T) //│ class Foo[T](x: T) if 1 is 1 then 1 else 0 //│ 0 | 1 //│ res //│ = 1 fun test(x) = if x is 1 then 0 else 1 //│ fun test: Object -> (0 | 1) :w fun testF(x) = if x is Foo(a) then a Foo(a) then a //│ ╔══[WARNING] found a duplicated case //│ ║ l.18: Foo(a) then a //│ ║ ^^^ //│ ╟── there is an identical pattern Foo //│ ║ l.17: Foo(a) then a //│ ╙── ^^^ //│ fun testF: forall 'a. Foo['a] -> 'a class Bar[Y, Z](y: Y, z: Z) //│ class Bar[Y, Z](y: Y, z: Z) fun test(f) = if f is Foo(a) then a Bar(b, c) then b + c //│ fun test: forall 'a. (Bar[Int, Int] | Foo['a]) -> (Int | 'a) class Pair[A, B](fst: A, snd: B) //│ class Pair[A, B](fst: A, snd: B) fun f(x) = if x is Pair(0, 0) then "zeros" Pair(1, 1) then "ones" Pair(y, 1) then x _ then "nah" //│ fun f: (Object & ~#Pair | Pair[Object, Object]) -> ("nah" | "ones" | "zeros" | Pair[nothing, nothing]) class Z() class O() //│ class Z() //│ class O() // This is not exhaustive. :e fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" //│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `Z`, //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `O` //│ ║ l.56: Pair(O(), O()) then "ones" //│ ╙── ^ //│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `O`, //│ ║ l.56: Pair(O(), O()) then "ones" //│ ║ ^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") // Change `Pair` to a real pair. :e fun foo(x) = if x is [Z(), Z()] then "zeros" [O(), O()] then "ones" //│ ╔══[ERROR] when `x$Tuple$2_0` is `O` //│ ║ l.83: [O(), O()] then "ones" //│ ║ ^ //│ ╟── `x$Tuple$2_1` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.82: [Z(), Z()] then "zeros" //│ ╙── ^ //│ fun foo: forall 'a. {0: O | Z, 1: O & 'a} -> ("ones" | "zeros" | 'a) fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is Z() then "zeros" O() then if b is O() then "ones" //│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is Z() then "zeros" else "???" O() then if b is O() then "ones" //│ fun foo: Pair[O | Z, O] -> ("???" | "ones" | "zeros") fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is Z() then "zeros" else "???" O() then if b is O() then "zeros" else "???" //│ fun foo: Pair[O | Z, Object] -> ("???" | "zeros") class S(pred: S | Z | O) //│ class S(pred: O | S | Z) // TODO: Cannot check exhaustiveness of nested UCS yet. fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is S(x) then x else "???" O() then if b is O() then "zeros" else "???" //│ fun foo: Pair[O | Z, Object] -> ("???" | "zeros" | O | S | Z) foo(Pair(Z(), Z())) //│ "???" | "zeros" | O | S | Z //│ res //│ = '???' :e fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" Pair(y, O()) then x //│ ╔══[ERROR] when `x` is `Pair` //│ ║ l.141: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.141: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: forall 'A 'B. Pair['A, O & 'B] -> ("ones" | "zeros" | Pair['A, 'B] | 'A) //│ where //│ 'A <: Object fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ fun foo: (Object, Object) -> (0 | 1) fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ fun foo: (Object, Object) -> (0 | 1) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/InterleavedLet.mls ================================================ :NewDefs fun f(x) = if x == let v = 0 v then v else 0 //│ fun f: Num -> 0 abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either :ducs:normalize.result fun q(x) = if x is Some and x is Some and x is Some then 0 //│ Normalized UCS term: //│ case x*‡ of //│ Some*◊ -> 0 //│ fun q: Some[anything] -> 0 :e // TODO: post-processing should combine two matches on `y` fun p(x, y) = if x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 //│ ╔══[WARNING] found a duplicated case //│ ║ l.39: x is Some and y is Some then 0 //│ ║ ^^^^ //│ ╟── there is an identical pattern Some //│ ║ l.38: y is Some and x is Some then 1 //│ ╙── ^^^^ //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.38: y is Some and x is Some then 1 //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.37: x is Some and y is None then 0 //│ ╙── ^^^^ //│ fun p: (Object & ~#Some | Some[anything], Some[anything]) -> (0 | 1) fun h(x, y) = if x is None then y let y_square = y * y Some(z) then z + y_square //│ fun h: (None | Some[Int], Int) -> Int h(Some(5), 6) //│ Int //│ res //│ = 41 fun h(x, y) = if x is None then y let y_square = y * y Some(y_square) then 0 //│ fun h: forall 'a. (None | Some[anything], Int & 'a) -> (0 | 'a) :e fun f(a, y) = if a is Some(v) and v is Left(x) then x let y = v + 1 Right(x) then x + y else 0 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.78: let y = v + 1 //│ ║ ^^^^^ //│ ╟── reference of type `Right[?B]` is not an instance of type `Int` //│ ║ l.78: let y = v + 1 //│ ╙── ^ //│ fun f: forall 'a. (Object & ~#Some | Some[Int | Left['a] | Right[Int]], anything) -> (Int | 'a) :pe fun q(a) = if a is Left(x) then x let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.93: let y = a + 1 //│ ║ ^^^^^ //│ ║ l.94: then y //│ ╙── ^^^^^^^^^^ //│ fun q: forall 'a. (Left['a] | Object & ~#Left) -> (() | 'a) class A() class B() //│ class A() //│ class B() :e fun w() = if A then "A" let y = 0 B then "B" else "?" //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.110: A then "A" //│ ║ ^ //│ ╙── reference of type `() -> A` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.112: B then "B" //│ ║ ^ //│ ╙── reference of type `() -> B` is not an instance of type `Bool` //│ fun w: () -> ("?" | "A" | "B") w() //│ "?" | "A" | "B" //│ res //│ = '?' fun i(x) = if x is A() then "A" let y = 0 B() then "B" //│ fun i: (A | B) -> ("A" | "B") fun inc(x) = x + 1 //│ fun inc: Int -> Int fun qq(x, z) = if x == let y = inc(z) y * y then 0 else 0 //│ fun qq: (Num, Int) -> 0 fun bruh(x) = if x == 0 then 0 let y = 1 else y //│ fun bruh: Num -> (0 | 1) fun f1(x) = x + 1 fun f2(x, y) = x + y //│ fun f1: Int -> Int //│ fun f2: (Int, Int) -> Int fun ff(x) = if x == 0 then 0 let y = f1(x) let z = f2(x, y) z == 1 then 1 z == 2 then 2 else 0 //│ fun ff: Int -> (0 | 1 | 2) fun p(x): Bool = true //│ fun p: anything -> Bool fun ip(x, y) = if p(x) and let z = inc(y) y === z * z then "bruh" else "rocks" //│ fun ip: (anything, Eql[Int] & Int) -> ("bruh" | "rocks") fun tr(x) = if x is Some(v) then v let tmp = 1 None then tmp //│ fun tr: forall 'a. (None | Some['a]) -> (1 | 'a) class Pair[A, B](val fst: A, val snd: B) abstract class List[out A]: Nil | Cons[A] module Nil extends List[nothing] class Cons[out A](head: A, tail: List[A]) extends List[A] //│ class Pair[A, B](fst: A, snd: B) //│ abstract class List[A]: Cons[A] | Nil //│ module Nil extends List //│ class Cons[A](head: A, tail: List[A]) extends List fun (::) cons(h, t) = Cons(h, t) fun (++) strcat(a, b) = concat(a)(b) //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] //│ fun (++) strcat: (Str, Str) -> Str fun showList(xs) = if xs is Nil then "" Cons(head, Nil) then toString(head) Cons(head, tail) then toString(head) ++ ", " ++ showList(tail) //│ fun showList: (Cons[anything] | Nil) -> Str let zeroToThree = 0 :: 1 :: 2 :: 3 :: Nil //│ let zeroToThree: Cons[0 | 1 | 2 | 3] //│ zeroToThree //│ = Cons {} showList(zeroToThree) //│ Str //│ res //│ = '0, 1, 2, 3' fun evenness(x) = if x % 2 is 0 then Left(x) else Right(x) //│ fun evenness: forall 'A 'B. (Int & 'A & 'B) -> (Left['A] | Right['B]) fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0 'A1 'A2 'B 'B0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[in 'A1 & 'A2 out 'A2, in 'B & 'B0 out 'B0 | Cons['A0]] //│ where //│ 'B <: List['A0] & 'B0 //│ 'B0 :> Cons['A0] | Nil //│ <: 'B //│ 'A1 <: List['A] & 'A2 //│ 'A2 :> Cons['A] | Nil //│ <: 'A1 mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) //│ forall 'A 'A0 'B 'B0. Pair[in 'A & 'A0 out 'A0, in 'B & 'B0 out 'B0 | Cons[0 | 1 | 2 | 3 | 'A1]] //│ where //│ 'B <: List['A1] & 'B0 //│ 'B0 :> Cons[0 | 1 | 2 | 3 | 'A1] | Nil //│ <: 'B //│ 'A <: List['A2] & 'A0 //│ 'A0 :> Cons[0 | 1 | 2 | 3 | 'A2] | Nil //│ <: 'A //│ res //│ = Pair {} // This should be the desugaring of the above: fun mapPartition'(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is res and res.fst is l and res.snd is r and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition': forall 'a 'A 'A0 'A1 'A2 'A3 'B. ('a -> (Left['A & 'A0] | Right['A1 & 'A2]), Cons['a] | Nil) -> Pair[in 'A3 out Cons['A] | Nil | 'A3 | Cons['A0], in 'B out Cons['A2] | 'B | Cons['A1] | Nil] mapPartition'(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) //│ forall 'A 'B. Pair[in 'A out Cons[0 | 1 | 2 | 3] | Nil | 'A, in 'B out Cons[0 | 1 | 2 | 3] | 'B | Nil] //│ res //│ = Pair {} // This is a very interesting side-effect example! fun mn(a) = if a is Some(x) and x is Left(b) and b is 0 then "b is 1" let _ = log(b) 1 then "b is 2" 2 then "b is 3" Right(b) then "right-defined" None then "undefined" //│ fun mn: (None | Some[Left[0 | 1 | 2] | Right[anything]]) -> ("b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined") mn of None mn of Some of Left of 0 mn of Some of Left of 1 mn of Some of Left of 2 mn of Some of Right of () //│ "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" //│ res //│ = 'undefined' //│ res //│ = 'b is 1' //│ res //│ = 'b is 2' //│ // Output //│ 1 //│ res //│ = 'b is 3' //│ // Output //│ 2 //│ res //│ = 'right-defined' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/LeadingAnd.mls ================================================ :NewDefs class Some[T](value: T) //│ class Some[T](value: T) fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int // FIXME (parser) fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/LitUCS.mls ================================================ :NewDefs module A //│ module A // This one is easy to fix but what about the next one? // The following example can better reveal the essence of the problem. fun test(x: 0 | A) = if x is 0 then 0 A then A //│ fun test: (x: 0 | A) -> (0 | A) :e // case === (x,) (0,) of { true => 0; _ => case x of { A => A } } fun test(x: 0 | A) = if x === 0 then 0 x is A then A //│ ╔══[ERROR] Module 'A' does not support equality comparison because it does not have a parameter list //│ ║ l.17: x === 0 then 0 //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.18: x is A then A //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `0` is not an instance of type `A` //│ ║ l.15: fun test(x: 0 | A) = //│ ║ ^ //│ ╟── but it flows into reference with expected type `A` //│ ║ l.18: x is A then A //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.18: x is A then A //│ ╙── ^ //│ fun test: (x: 0 | A) -> (0 | A) fun test2(x) = if x === 0 then 0 x is A then A //│ fun test2: (A & Eql[0]) -> (0 | A) :e test2(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: test2(0) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `A` //│ ║ l.43: test2(0) //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.39: x is A then A //│ ║ ^ //│ ╟── from reference: //│ ║ l.39: x is A then A //│ ╙── ^ //│ 0 | A | error //│ res //│ = 0 :e test2(A) //│ ╔══[ERROR] Module 'A' does not support equality comparison because it does not have a parameter list //│ ║ l.61: test2(A) //│ ╙── ^^^^^^^^ //│ 0 | A | error //│ res //│ = A {} ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/MultiwayIf.mls ================================================ :NewDefs fun f(x) = if x > 0 then 0 x == 0 then 1 _ then 2 //│ fun f: Num -> (0 | 1 | 2) fun f(x) = if x > 0 and x % 2 === 0 then true _ then false x == 0 then true _ then false //│ fun f: Int -> Bool f(0) f(2) f(3) f(0 - 1) f(0 - 2) //│ Bool //│ res //│ = true //│ res //│ = true //│ res //│ = false //│ res //│ = false //│ res //│ = false fun f(x) = if x > 0 and x % 2 === 0 then true else false x == 0 then true else false //│ fun f: Int -> Bool f(0) f(2) f(1) f(0 - 1) //│ Bool //│ res //│ = true //│ res //│ = true //│ res //│ = false //│ res //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/NestedBranches.mls ================================================ :NewDefs class Some[out A](val value: A) module None class Left[out A](val leftValue: A) class Right[out A](val rightValue: A) module Nil class Cons[out A](val head: A, val tail: Cons[A] | Nil) class Pair[out A, out B](val fst: A, val snd: B) //│ class Some[A](value: A) //│ module None //│ class Left[A](leftValue: A) //│ class Right[A](rightValue: A) //│ module Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) //│ class Pair[A, B](fst: A, snd: B) fun optionApply(x, y, f) = if x is Some(xv) and y is Some(yv) then Some(f(xv, yv)) None then None None then None //│ fun optionApply: forall 'a 'b 'A. (None | Some['a], None | Some['b], ('a, 'b) -> 'A) -> (None | Some['A]) fun (::) cons(h, t) = Cons(h, t) //│ fun (::) cons: forall 'A. ('A, Cons['A] | Nil) -> Cons['A] let zeroToThree = 0 :: 1 :: 2 :: 3 :: Nil //│ let zeroToThree: Cons[0 | 1 | 2 | 3] //│ zeroToThree //│ = Cons {} fun f(x) = if x % 2 == 0 then Left(x) else Right(x) //│ fun f: forall 'A. (Int & 'A) -> (Left['A] | Right['A]) fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] mapPartition(x => Left(x + 1), zeroToThree) //│ Pair[Cons[Int] | Nil, Cons[nothing] | Nil] //│ res //│ = Pair {} mapPartition(f, zeroToThree) //│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = Pair {} fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] mapPartition(f, zeroToThree) //│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = Pair {} fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] mapPartition(f, zeroToThree) //│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = Pair {} fun mapPartition(f, xs) = if xs is Nil then [Nil, Nil] Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> [Cons['A] | Nil, Cons['A0] | Nil] mapPartition(f, zeroToThree) //│ [Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = [ Cons {}, Cons {} ] // * Vertical alignment is not allowed! (good) :pe :w :e fun mapPartition(f, xs) = if xs is Nil then [Nil, Nil] Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[?a, ?b]` is not a function //│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ╙── ^^^^^^^^^^^^^^^ //│ fun mapPartition: forall 'a. ('a -> Left[anything], Cons['a] | Nil) -> (error | [Nil, Nil]) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/NestedPattern.mls ================================================ :NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either fun compute(v) = if v is Left(Some(x)) then x * 5 Left(None()) then 1 Right(Some(x)) then x * 3 Right(None()) then 0 //│ fun compute: (Left[None | Some[Int]] | Right[None | Some[Int]]) -> Int fun crazy(v) = if v is Some(Some(Some(Some(Some(Some(None())))))) then "bruh!" _ then "lol" //│ fun crazy: (Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object]]]]]]) -> ("bruh!" | "lol") // * TODO(@chengluyu) fix these missing locations fun f(x) = if x is [0, 0] then "zeros" [1, 1] then "ones" _ then "bruh" //│ fun f: {0: Object, 1: Object} -> ("bruh" | "ones" | "zeros") f([0, 0]) f([1, 1]) f([1, 0]) //│ "bruh" | "ones" | "zeros" //│ res //│ = 'zeros' //│ res //│ = 'ones' //│ res //│ = 'bruh' fun f(x) = if x is [0, 0] then "zeros" [1, 1] then "ones" [y, 1] then x _ then "que?" //│ fun f: forall 'a. ({0: Object, 1: Object} & 'a) -> ("ones" | "que?" | "zeros" | 'a) f([0, 0]) f([1, 1]) f([0, 1]) f([1, 0]) //│ "ones" | "que?" | "zeros" | [1, 0] //│ res //│ = 'zeros' //│ res //│ = 'ones' //│ res //│ = [ 0, 1 ] //│ res //│ = 'que?' fun f(p) = if p is Some([x, y]) then x + y None() then 0 //│ fun f: (None | Some[{0: Int, 1: Int}]) -> Int class Union[A, B](a: A, b: B) //│ class Union[A, B](a: A, b: B) // Name conflict between the scrutinee and the positionals. // Desugar result: let tmp13 = x in case tmp13 of { Union => let x = (tmp13).a in let y = (tmp13).b in x } fun hmm(x) = if x is Union(x, y) then x //│ fun hmm: forall 'a. Union['a, anything] -> 'a hmm(Union(1, 2)) //│ 1 //│ res //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/NuPlainConditionals.mls ================================================ :NewDefs class Pair[A](fst: A, snd: A) //│ class Pair[A](fst: A, snd: A) Pair(0, 1) is Pair //│ Bool //│ res //│ = true Pair(0, 1) is Pair(a, b) //│ Bool //│ res //│ = true Pair(0, 1) is Pair(0, _) //│ Bool //│ res //│ = true if Pair(0, 1) is Pair(a, b) then true else false //│ Bool //│ res //│ = true fun foo(x) = x is Pair(a, b) //│ fun foo: (Object & ~#Pair | Pair[anything]) -> Bool Pair(0, 1) is Pair(a, b) and a > b //│ Bool //│ res //│ = false if Pair(0, 1) is Pair(a, b) then a > b else false //│ Bool //│ res //│ = false fun foo(x) = x is Pair(a, b) and a > b //│ fun foo: (Object & ~#Pair | Pair[Num]) -> Bool fun foo(x) = if x is Pair(a, b) then a > b else false //│ fun foo: (Object & ~#Pair | Pair[Num]) -> Bool // TODO proper error fun foo(x) = x is Pair Int //│ ╔══[ERROR] unknown pattern {Pair; Int} //│ ║ l.54: Pair //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO proper error fun foo(x) = x is Pair(a, b) and a > b Int //│ ╔══[ERROR] unknown pattern {and(Pair(a, b,), >(a, b,),); Int} //│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int //│ ╙── ^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO support `|` fun foo1(x) = x is Pair(a, b) | Int fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `and` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] type identifier `>` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ fun foo1: nothing -> error //│ fun foo2: nothing -> error //│ Code generation encountered an error: //│ unresolved symbol | class A(arg: Int) //│ class A(arg: Int) // TODO make `is` lower precedence than `=>` x => (x is A(_)) //│ Object -> Bool //│ res //│ = [Function: res] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/OverlappedBranches.mls ================================================ :NewDefs class Base() class Derived1() extends Base() class Derived2() extends Base() class Derived3() extends Derived2() //│ class Base() //│ class Derived1() extends Base //│ class Derived2() extends Base //│ class Derived3() extends Base, Derived2 // The very basic case. // It should warn about that the last two cases are unreachable. :w :ducs:normalize.result fun f1(x) = if x is Base then "b" Derived1 then "d1" Derived2 then "d2" //│ Normalized UCS term: //│ case x*‡ of //│ Base*◊ -> "b" //│ ╔══[WARNING] found a duplicated case //│ ║ l.18: Derived1 then "d1" //│ ║ ^^^^^^^^ //│ ╟── the case is covered by pattern Base //│ ║ l.17: Base then "b" //│ ║ ^^^^ //│ ╟── due to the subtyping relation //│ ║ l.4: class Derived1() extends Base() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] found a duplicated case //│ ║ l.19: Derived2 then "d2" //│ ║ ^^^^^^^^ //│ ╟── the case is covered by pattern Base //│ ║ l.17: Base then "b" //│ ║ ^^^^ //│ ╟── due to the subtyping relation //│ ║ l.5: class Derived2() extends Base() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun f1: Base -> "b" f1(Base()) f1(Derived1()) f1(Derived2()) //│ "b" //│ res //│ = 'b' //│ res //│ = 'b' //│ res //│ = 'b' // Decision paths: // + «x is Base» and «p (x,)» => "b and p" // + «x is Derived1» => "d1" // + «x is Derived2» => "d2" // + => "otherwise" // The case tree: // «x» match // case Base => // if «p (x,)» // «"b and p"» // else // «x» match // case Derived1 => // «"d1"» // case Derived2 => // «"d2"» // default // «"otherwise"» // default // «"otherwise"» fun f2(x, p) = if x is Base and p(x) then "b and p" Derived1 then "d1" Derived2 then "d2" else "otherwise" //│ fun f2: forall 'a. (Object & ~#Base | 'a & (Base & ~#Derived1 & ~#Derived2 | Derived1 | Derived2), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") f2(Base(), _ => true) // => b and p f2(Base(), _ => false) // otherwise //│ "b and p" | "d1" | "d2" | "otherwise" //│ res //│ = 'b and p' //│ res //│ = 'otherwise' f2(Derived1(), _ => true) // => b and p f2(Derived2(), _ => true) // => b and p //│ "b and p" | "d1" | "d2" | "otherwise" //│ res //│ = 'b and p' //│ res //│ = 'b and p' f2(Derived1(), _ => false) // => d1 f2(Derived2(), _ => false) // => d2 //│ "b and p" | "d1" | "d2" | "otherwise" //│ res //│ = 'd1' //│ res //│ = 'd2' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/ParseFailures.mls ================================================ :NewDefs :NoJS type Tree[A] = Node[A] | Empty module Empty class Node[A](value: Int, left: Tree[A], right: Tree[A]) //│ type Tree[A] = Empty | Node[A] //│ module Empty //│ class Node[A](value: Int, left: Tree[A], right: Tree[A]) fun contains(node, wanted) = if node is Node(value, left, right) and wanted <= value then contains(left, wanted) >= value then contains(right, wanted) else true Empty then false //│ fun contains: forall 'A. (Empty | Node['A], Num) -> Bool fun contains'(node, wanted) = if node is Node(value, left, right) and wanted <= value then contains'(left, wanted) >= value then contains'(right, wanted) _ then true Empty then false //│ fun contains': forall 'A. (Empty | Node['A], Num) -> Bool :pe class Z() class O() fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.33: Z() and y is O() then 0 else 1 //│ ╙── ^^^^ //│ class Z() //│ class O() //│ fun foo: (Z, O) -> 0 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/ParserFailures.mls ================================================ :NewDefs :NoJS // FIXME: Interleaved let bindings are not implemented in `IfOpsApp`. if x == 0 then "bad" let y = f(z) == y * y then 0 //│ ╔══[PARSE ERROR] expect an operator //│ ║ l.7: let y = f(z) //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected 'let' keyword here //│ ║ l.7: let y = f(z) //│ ╙── ^^^ //│ ╔══[ERROR] missing else branch //│ ║ l.6: == 0 then "bad" //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.5: if x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: == 0 then "bad" //│ ║ ^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ "bad" // FIXME: Interleaved let bindings are not implemented in `IfOpsApp`. fun tt(x) = if x is A() then "A" let y = 0 is B() then "B" //│ ╔══[PARSE ERROR] expect an operator //│ ║ l.31: let y = 0 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected 'let' keyword here //│ ║ l.31: let y = 0 //│ ╙── ^^^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.30: is A() then "A" //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.30: is A() then "A" //│ ╙── ^ //│ fun tt: nothing -> error ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/SplitAfterOp.mls ================================================ :NewDefs :e fun f(x, b) = if x == 0 and b then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.6: 0 and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Bool) -> 0 :e fun f(x, y) = if x == y + 5 then 0 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.24: 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: if x == y + //│ ║ ^ //│ ║ l.23: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.22: if x == y + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.22: if x == y + //│ ║ ^ //│ ║ l.23: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.22: if x == y + //│ ║ ^ //│ ║ l.23: 5 then 0 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.24: 7 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.24: 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 :e fun f(x, y) = if x == y * 5 then 0 6 + 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.60: 6 + 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.58: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.60: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.58: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.60: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.60: 6 + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 :e fun f(x, y) = if x == y + 5 then 0 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.107: 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.105: y + //│ ║ ^ //│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.104: if x == //│ ║ ^^^^ //│ ║ l.105: y + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.105: y + //│ ║ ^ //│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.105: y + //│ ║ ^ //│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.107: 7 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.107: 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 :e fun f(x, b) = if x == 1 and b then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.144: 1 and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.144: 1 and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.144: 1 and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Bool) -> 0 :e fun toEnglish(x) = if x == true then "t" 0 then "z" //│ ╔══[ERROR] missing else branch //│ ║ l.163: 0 then "z" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.161: if x == //│ ║ ^^^^ //│ ║ l.162: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` //│ ║ l.162: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.163: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") :e fun toEnglish(x) = if x == 0 then "z" true then "t" //│ ╔══[ERROR] missing else branch //│ ║ l.185: true then "t" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.183: if x == //│ ║ ^^^^ //│ ║ l.184: 0 then "z" //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.185: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` //│ ║ l.185: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.185: true then "t" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") :e fun toEnglish(x) = if x == 1 then "o" 0 then "z" //│ ╔══[ERROR] missing else branch //│ ║ l.209: 0 then "z" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.209: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("o" | "z") fun toEnglish(x) = if x == 0 then 1 else 1 //│ fun toEnglish: Num -> 1 :pe :e fun toEnglish(x) = if x == else 1 //│ ╔══[PARSE ERROR] Unexpected indented block in expression position //│ ║ l.229: else 1 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.228: if x == //│ ║ ^^^^ //│ ║ l.229: else 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.228: if x == //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.228: if x == //│ ║ ^^^^ //│ ║ l.229: else 1 //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Num` //│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.229: else 1 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> () ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/SplitAnd.mls ================================================ :NewDefs fun f(x, y) = if x == 0 and y == 0 then "bruh" y == 1 then "lol" else "okay" //│ fun f: (Num, Num) -> ("bruh" | "lol" | "okay") class A() class B() //│ class A() //│ class B() :e fun f(x) = if x == 0 and x is A() then "A" B() then "B" x == 0 then "lol" else "bruh" //│ ╔══[ERROR] missing else branch //│ ║ l.18: x is //│ ║ ^^^^ //│ ║ l.19: A() then "A" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.20: B() then "B" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.21: x == 0 then "lol" //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: else "bruh" //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.18: x is //│ ║ ^^^^ //│ ║ l.19: A() then "A" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.20: B() then "B" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.21: x == 0 then "lol" //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: else "bruh" //│ ║ ^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Num -> ("A" | "B" | "bruh" | "lol") :e fun f(x, y) = if x == 0 and y == 0 then "bruh" else "lol" //│ ╔══[ERROR] missing else branch //│ ║ l.52: y == 0 then "bruh" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.53: else "lol" //│ ╙── ^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.52: y == 0 then "bruh" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.53: else "lol" //│ ║ ^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> ("bruh" | "lol") ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/SplitAroundOp.mls ================================================ :NewDefs fun f(x, b) = if x === 0 and b then "n0" 1 and b then "n1" 2 then "n2" === "0" then "s0" "1" then "s1" "2" then "s2" else ":p" //│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Bool) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") fun f(x, y, a, b) = if x === 0 and y === 0 then "x, y" a === 0 then "x, a" b === 0 then "x, b" else "nah" //│ fun f: (Eql[0], Eql[0], Eql[0], Eql[0]) -> ("nah" | "x, a" | "x, b" | "x, y") class A() class B() //│ class A() //│ class B() fun f(x) = if x is A() then 0 B() then 1 //│ fun f: (A | B) -> (0 | 1) :e // FIXME if x is A() === 0 then 0 > 0 then 1 < 0 then 2 //│ ╔══[ERROR] cannot transform due to an illegal split operator === //│ ║ l.40: === 0 then 0 //│ ║ ^^^ //│ ╟── the following branch will be discarded //│ ║ l.40: === 0 then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] cannot transform due to an illegal split operator > //│ ║ l.41: > 0 then 1 //│ ║ ^ //│ ╟── the following branch will be discarded //│ ║ l.41: > 0 then 1 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] cannot transform due to an illegal split operator < //│ ║ l.42: < 0 then 2 //│ ║ ^ //│ ╟── the following branch will be discarded //│ ║ l.42: < 0 then 2 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] identifier `x` not found //│ ║ l.38: if x is //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.38: if x is //│ ╙── ^ //│ nothing //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/SplitBeforeOp.mls ================================================ :NewDefs :e :ge if x == 0 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.6: == 0 then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.5: if x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: == 0 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 //│ Code generation encountered an error: //│ unresolved symbol x :e :ge if x is A and y then 0 //│ ╔══[ERROR] identifier `x` not found //│ ║ l.23: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.24: is A and //│ ╙── ^ //│ ╔══[ERROR] missing else branch //│ ║ l.25: y then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.23: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.24: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol x :e :ge if x is A and y then 0 else 1 //│ ╔══[ERROR] identifier `x` not found //│ ║ l.47: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.48: is A and //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.47: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.48: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol x :e :ge if x == 0 then 0 is A() then "A" B() then "B" //│ ╔══[ERROR] identifier `x` not found //│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.72: A() then "A" //│ ╙── ^ //│ ╔══[ERROR] type identifier `B` not found //│ ║ l.73: B() then "B" //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.72: A() then "A" //│ ╙── ^ //│ 0 | error //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/SplitOps.mls ================================================ :NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either :e :ge fun f(x) = if x is Left(v) then 0 is Right(v) then 1 <> undefined then 2 //│ ╔══[ERROR] missing else branch //│ ║ l.23: <> undefined then 2 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: if x //│ ║ ^ //│ ║ l.21: is Left(v) then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: is Right(v) then 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.23: <> undefined then 2 //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Num` //│ ║ l.23: <> undefined then 2 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.23: <> undefined then 2 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Left[anything] | Num | Right[anything]) -> (0 | 1 | 2) //│ Code generation encountered an error: //│ unresolved symbol <> :e :ge fun f(x) = if x is Some(xv) and y is Some(yv) then xv + yv is None() and y is None() then 0 //│ ╔══[ERROR] identifier `y` not found //│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found //│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ fun f: (None | Some[Int]) -> Int //│ Code generation encountered an error: //│ unresolved symbol y class A() class B() //│ class A() //│ class B() fun f(a, b) = if a is A() and b is B() then 0 //│ fun f: (A, B) -> 0 class C() //│ class C() :e fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.85: == 0 and b is B() and c is C() then 0 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.85: == 0 and b is B() and c is C() then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, B, C) -> 0 fun f(x) = if x is A() then "A" is B() then "B" //│ fun f: (A | B) -> ("A" | "B") fun sumOpt(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None() then xv is None() and y is Some(yv) then yv None() then 0 //│ fun sumOpt: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) fun f(x, y, z) = if x is A() and y == z then 1 is B() then 0 //│ fun f: (A, nothing, Num) -> (0 | 1) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/SplitScrutinee.mls ================================================ :NewDefs fun f(x) = if x + 1 is 2 then 1 3 then 2 _ then "I don't know." //│ fun f: Int -> ("I don't know." | 1 | 2) [f(0), f(1), f(2), f(3)] //│ ["I don't know." | 1 | 2, "I don't know." | 1 | 2, "I don't know." | 1 | 2, "I don't know." | 1 | 2] //│ res //│ = [ "I don't know.", 1, 2, "I don't know." ] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/ThenIndent.mls ================================================ :NewDefs // FIXME x => if x == 0 then "a" _ then "b" //│ ╔══[PARSE ERROR] Unexpected indented block here //│ ║ l.7: then "a" //│ ║ ^^^^^^^^^^^^ //│ ║ l.8: _ then "b" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.5: x => if x == //│ ║ ^^^^ //│ ║ l.6: 0 //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.5: x => if x == //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.6: 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ Num -> () //│ res //│ = [Function: res] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/TrivialIf.mls ================================================ :NewDefs :NoJS fun abs(x) = if x < 0 then 0 - x else x //│ fun abs: Int -> Int abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun getOrElse(opt, default) = if opt is Some(value) then value None then default //│ fun getOrElse: forall 'a. (None | Some['a], 'a) -> 'a getOrElse(None, 0) //│ 0 getOrElse(Some(42), 0) //│ 0 | 42 fun map(v, f) = if v is Some(x) then Some(f(x)) None then None //│ fun map: forall 'a 'A. (None | Some['a], 'a -> 'A) -> (None | Some['A]) fun inc(x) = x + 5 //│ fun inc: Int -> Int map(Some(5), x => x + 5) //│ None | Some['A] //│ where //│ 'A :> Int map(None, inc) //│ None | Some['A] //│ where //│ 'A :> Int :e fun f(a, b) = if a and b then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Bool, Bool) -> 0 :e fun f(x, y) = if x == y + 5 then 0 else if x == y + 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.63: else if x == y + 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.63: else if x == y + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Int) -> 0 // TODO support fun foo(x) = if x is Some (0) then 0 (1) then 1 //│ ╔══[PARSE ERROR] Unexpected parenthesis section here //│ ║ l.76: (1) then 1 //│ ╙── ^^^ //│ /!!!\ Uncaught error: java.lang.StackOverflowError // TODO support fun foo(x) = if x is Some of 0 then 0 1 then 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.84: 0 then 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.83: fun foo(x) = if x is Some of //│ ║ ^^^^^^^^^^^^ //│ ║ l.84: 0 then 0 //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.83: fun foo(x) = if x is Some of //│ ╙── ^^ //│ fun foo: Some[0] -> () ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/WeirdIf.mls ================================================ :NewDefs :w if _ then 0 else 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.6: else 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.5: _ then 0 //│ ╙── ^ //│ ╔══[WARNING] this case is unreachable //│ ║ l.7: else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.5: _ then 0 //│ ╙── ^ //│ 0 //│ res //│ = 0 :w if else 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.25: if else 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.25: if else 0 else 1 //│ ╙── ^ //│ 0 //│ res //│ = 0 :w fun f(x) = if x is else 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ╙── ^ //│ fun f: anything -> 0 fun f(x) = if x is else 0 //│ fun f: anything -> 0 :e if true then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.51: then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.51: then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 //│ res //│ = 0 // This cannot be parsed. But the next one works. :pe :e fun f(x) = if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position //│ ║ l.68: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.68: else "bruh" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.67: if x === //│ ║ ^^^^^ //│ ║ l.68: else "bruh" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.67: if x === //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.68: else "bruh" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.68: else "bruh" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Eql[()] -> () // But this works. fun f(x) = if x === _ then "bruh" //│ fun f: anything -> "bruh" fun boolToStr(x) = if x is true then "yah" false then "nah" //│ fun boolToStr: Bool -> ("nah" | "yah") boolToStr of true boolToStr of false //│ "nah" | "yah" //│ res //│ = 'yah' //│ res //│ = 'nah' ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/WeirdSplit.mls ================================================ :NewDefs class A() class B() //│ class A() //│ class B() fun f(x) = if x is A then 0 B then 1 //│ fun f: (A | B) -> (0 | 1) // Precedence problem: should we restruct terms when push them to the stack? :e fun f(x) = if x == 1 + 2 then 0 + _ then 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.20: + 2 then 0 //│ ║ ^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.18: if x == //│ ║ ^^^^ //│ ║ l.19: 1 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.20: + 2 then 0 //│ ║ ^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ fun f: Num -> (0 | 1) fun f(x, s, t) = if x is A() and t then 0 and s then 0 is _ then 1 //│ fun f: (Object, Bool, Bool) -> (0 | 1) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/Wildcard.mls ================================================ :NewDefs fun (++) strcat(a, b) = concat(a)(b) //│ fun (++) strcat: (Str, Str) -> Str type Option[T] = None | Some[T] module None class Some[T](val value: T) //│ type Option[T] = None | Some[T] //│ module None //│ class Some[T](value: T) type Either[A, B] = Left[A] | Right[B] class Left[A](val leftValue: A) class Right[B](val rightValue: B) //│ type Either[A, B] = Left[A] | Right[B] //│ class Left[A](leftValue: A) //│ class Right[B](rightValue: B) fun w1(x, e_0, e_1) = if x is Left(None) then "Left of None" Right(None) then "Right of None" _ and e_0 is y_0 and x is Left(Some(lv)) then "Left of Some of " ++ toString(lv) _ and e_1 is y_1 and x is Right(Some(rv)) then "Right of Some of " ++ toString(rv) //│ fun w1: forall 'a. (Left[None | Object & ~#None & ~#Some | Some[anything]] | Object & ~#Left & ~#Right | Right[None | Some[anything]], anything, 'a) -> (Str | 'a) w1(Left(None), "a", "b") w1(Right(None), "a", "b") w1(Left(Some(0)), "a", "b") w1(Right(Some(0)), "a", "b") //│ Str //│ res //│ = 'Left of None' //│ res //│ = 'Right of None' //│ res //│ = 'Left of Some of 0' //│ res //│ = 'Right of Some of 0' // Should be // case x of // Some -> 1 // None -> // case p(x) of // true -> 2 // _ -> 4 // _ -> // case p(x) of // true -> 3 // _ -> 5 :ducs:normalize.result,postprocess.result fun w2(x, p) = if x is Some then 1 _ and p(x) and x is None then 2 _ then 3 None then 4 _ then 5 //│ Normalized UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> //│ case x*‡ of //│ None*† -> 2 //│ _ -> 3 //│ _ -> //│ case x*‡ of //│ None*† -> 4 //│ _ -> 5 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 2 //│ _ -> 4 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 3 //│ _ -> 5 //│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Bool) -> (1 | 2 | 3 | 4 | 5) // Should be // case x of // Some -> 1 // None -> // case p(x) of // true -> 2 // _ -> 3 // _ -> // case p(x) of // true -> 2 // _ -> 4 :ducs:normalize.result,postprocess.result fun w2(x, p) = if x is Some then 1 _ and p(x) then 2 None then 3 _ then 4 //│ Normalized UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> 2 //│ _ -> //│ case x*‡ of //│ None*† -> 3 //│ _ -> 4 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 2 //│ _ -> 3 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 2 //│ _ -> 4 //│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Bool) -> (1 | 2 | 3 | 4) w2(Some(0), x => true) w2(None, x => true) w2(None, x => false) w2(0, x => false) //│ 1 | 2 | 3 | 4 //│ res //│ = 1 //│ res //│ = 2 //│ res //│ = 3 //│ res //│ = 4 fun w3(x, p) = if x is _ and p(x) then "r1" Some(xv) then "r2: " ++ toString(xv) None then "r3" _ then "r4" //│ fun w3: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str // Expect "r1" w3(0, _ => true) w3(None, _ => true) w3(Some(0), _ => true) //│ Str //│ res //│ = 'r1' //│ res //│ = 'r1' //│ res //│ = 'r1' // Expect "r2" w3(Some(0), _ => false) //│ Str //│ res //│ = 'r2: 0' // Expect "r3" w3(None, _ => false) //│ Str //│ res //│ = 'r3' // Expect "r4" w3(0, _ => false) //│ Str //│ res //│ = 'r4' :w // Decision paths: // + «tmp2 @ f (x,) is any => 0 // + => 1 fun w3_1(x, f) = if f(x) is _ then 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.194: if f(x) is _ then 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.194: if f(x) is _ then 0 else 1 //│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 w3_1(0, _ => true) w3_1(0, _ => false) //│ 0 //│ res //│ = 0 //│ res //│ = 0 :w fun w3_1_1(x, f) = if f(x) is a then a else 0 //│ ╔══[WARNING] this case is unreachable //│ ║ l.213: if f(x) is a then a else 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.213: if f(x) is a then a else 0 //│ ╙── ^ //│ fun w3_1_1: forall 'a 'b. ('a, 'a -> 'b) -> 'b w3_1_1(0, x => x) w3_1_1(0, x => x + 1) //│ Int //│ res //│ = 0 //│ res //│ = 1 // Decision paths: // + «a = x» and «p (x,)» => "r1" // + «x is Some» => concat ("r2: ",) (toString (xv,),) // + «x is None» => "r3" fun w4(x, p) = if x is a and p(x) then "r1" Some(xv) then "r2: " ++ toString(xv) None then "r3" _ then "r4" //│ fun w4: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str // Expect "r1" w4(0, _ => true) w4(None, _ => true) w4(Some(0), _ => true) //│ Str //│ res //│ = 'r1' //│ res //│ = 'r1' //│ res //│ = 'r1' // Expect "r2" w4(Some(0), _ => false) //│ Str //│ res //│ = 'r2: 0' // Expect "r3" w4(None, _ => false) //│ Str //│ res //│ = 'r3' // Expect "r4" w4(0, _ => false) //│ Str //│ res //│ = 'r4' class Alpha() class Beta() class Gamma() class Delta() //│ class Alpha() //│ class Beta() //│ class Gamma() //│ class Delta() // This should generate only one case expression instead of a chain of case // expressions. DO check the desugared term! :ducs:postprocess.result fun w5(y) = if y is Alpha then "alpha" _ and y is Beta then "beta" _ and y is Gamma then "gamma" _ and y is Delta then "delta" _ then "unknown" //│ Post-processed UCS term: //│ case y*‡ of //│ Alpha*◊ -> "alpha" //│ Gamma*◊ -> "gamma" //│ Delta*◊ -> "delta" //│ Beta*◊ -> "beta" //│ _ -> "unknown" //│ fun w5: Object -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") w5(0) w5(Alpha()) w5(Beta()) w5(Gamma()) w5(Delta()) //│ "alpha" | "beta" | "delta" | "gamma" | "unknown" //│ res //│ = 'unknown' //│ res //│ = 'alpha' //│ res //│ = 'beta' //│ res //│ = 'gamma' //│ res //│ = 'delta' fun w6(x, y) = if x is _ and y is Some(z) then z None then 0 else x //│ fun w6: forall 'a. ('a, Object & ~#Some | Some['a]) -> (0 | 'a) w6("42", Some(42)) w6("42", None) w6("42", "42") //│ "42" | 0 //│ res //│ = 42 //│ res //│ = 0 //│ res //│ = '42' fun w7(x, f) = if x is _ and f(x) is Some(v) then v None then x Left(x) then x + 1 Right(x) then x + 2 //│ fun w7: forall 'a 'b. ('a & (Left[Int] | Right[Int]), 'a -> (Object & ~#Some | Some['b])) -> (Int | 'b | 'a) // The results are wrong: w7(Left(99), _ => Some(0)) // => 0 w7(Left(99), _ => None) // => Left(99) w7(Right(99), _ => Some(0)) // => 0 w7(Right(99), _ => None) // => Right(99) //│ Int | Right['B] //│ where //│ 'B :> 99 //│ <: Int //│ res //│ = 0 //│ res //│ = Left {} //│ res //│ = 0 //│ res //│ = Right {} w7(Left(99), _ => "test") w7(Right(99), _ => "test") //│ Int | Right['B] //│ where //│ 'B :> 99 //│ <: Int //│ res //│ = 100 //│ res //│ = 101 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/legacy/zipWith.mls ================================================ :NewDefs declare val nothing: nothing //│ val nothing: nothing //│ nothing //│ = module None { fun value = nothing } class Some[out A](val value: A) //│ module None { //│ fun value: nothing //│ } //│ class Some[A](value: A) type List[out A] = Cons[A] | Nil module Nil { fun toArray = [] } class Cons[out A](val head: A, val tail: List[A]) { fun toArray: Array[anything] fun toArray = [head, tail.toArray] } //│ type List[A] = Cons[A] | Nil //│ module Nil { //│ fun toArray: [] //│ } //│ class Cons[A](head: A, tail: List[A]) { //│ fun toArray: Array[anything] //│ } fun pairup(x, y) = [x, y] //│ fun pairup: forall 'a 'b. ('a, 'b) -> ['a, 'b] // FIXME parsing fun zipWith_wrong(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application followed by newline instead //│ ║ l.42: if xs is Cons(x, xs) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.43: and ys is Cons(y, ys) //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.42: if xs is Cons(x, xs) //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.43: and ys is Cons(y, ys) //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.44: and zipWith_wrong(f, xs, ys) is Some(tail) //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.42: if xs is Cons(x, xs) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.43: and ys is Cons(y, ys) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.44: and zipWith_wrong(f, xs, ys) is Some(tail) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.45: then Some(Cons(f(x, y), tail)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: else None //│ ╙── ^^^^^^^^^^^ //│ fun zipWith_wrong: (anything, anything, anything) -> () fun zipWith_wrong(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None //│ fun zipWith_wrong: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) fun zipWith_wrong(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None //│ fun zipWith_wrong: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) // * Notice the result is wrong (duh) zipWith_wrong(pairup, Nil, Nil) //│ None | Some[Cons[[nothing, nothing]]] //│ res //│ = None {} fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else if xs is Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) then if zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None else if xs is Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) then if ys is Cons(y, ys) then if zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) None then None Nil then None Nil then if ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Nil, Cons['b] | Nil) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Nil, Nil).value.toArray //│ Array[anything] //│ res //│ = [] :re zipWith(pairup, Nil, Cons(0, Nil)).value.toArray //│ Array[anything] //│ res //│ Runtime error: //│ ReferenceError: nothing is not defined zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] let ls = zipWith(pairup, Cons(0, Cons(1, Nil)), Cons("0", Cons("1", Nil))) ls.value.toArray //│ let ls: None | Some[Cons[[0 | 1, "0" | "1"]] | Nil] //│ Array[anything] //│ ls //│ = Some {} //│ res //│ = [ [ 0, '0' ], [ [ 1, '1' ], [] ] ] fun zipWith_wrong2(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong2(f, xs, ys) is Some(tail) then Cons(Some(f(x, y)), tail) else if xs is Nil and ys is Nil then Some(Nil) else None //│ fun zipWith_wrong2: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (Cons[Some['A]] | None | Some[Nil]) // * No type error! The definition and use are well-typed... zipWith_wrong2(pairup, Cons(0, Cons(1, Nil)), Cons("0", Cons("1", Nil))) //│ Cons[Some[[0 | 1, "0" | "1"]]] | None | Some[Nil] //│ res //│ = None {} ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/stages/PostProcessing.mls ================================================ :NewDefs :ducs:postprocess.result,desugared fun mixed_literals(v) = if v is true then "true" false then "false" 1 then "1" 2 then "2" //│ Post-processed UCS term: //│ case v*‡ of //│ true*† -> "true" //│ 2 -> "2" //│ 1 -> "1" //│ false*† -> "false" //│ Desugared term: case v of { true => "true"; 2 => "2"; 1 => "1"; false => "false" } //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") :ducs:postprocess.result fun separated_by_and(v) = if v is true then "true" _ and v is false then "false" //│ Post-processed UCS term: //│ case v*‡ of //│ true*† -> "true" //│ false*† -> "false" //│ fun separated_by_and: Bool -> ("false" | "true") :ducs:postprocess.result fun dual_patterns(x, y) = if x is "some" and y is "none" then 0 x is "none" and y is "some" then 1 x is "some" and y is "some" then 2 x is "none" and y is "none" then 3 //│ Post-processed UCS term: //│ case x*‡ of //│ "some" -> //│ case y*‡ of //│ "none" -> 0 //│ "some" -> 2 //│ "none" -> //│ case y*‡ of //│ "some" -> 1 //│ "none" -> 3 //│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) :ducs:postprocess.result fun unordered_dual_patterns(x, y) = if x is "some" and y is "none" then 0 y is "some" and x is "none" then 1 y is "some" and x is "some" then 2 x is "none" and y is "none" then 3 //│ Post-processed UCS term: //│ case x*‡ of //│ "some" -> //│ case y*‡ of //│ "none" -> 0 //│ "some" -> 2 //│ "none" -> //│ case y*‡ of //│ "some" -> 1 //│ "none" -> 3 //│ fun unordered_dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/stages/SpecilizationCollision.mls ================================================ :NewDefs // This test file is to track possible name collision during specialization. fun (~~>) check(x, y) = if x === y then "passed" else error class Pair[out A, out B](val first: A, val second: B) //│ fun (~~>) check: forall 'a. (Eql['a], 'a) -> "passed" //│ class Pair[A, B](first: A, second: B) fun p1(x) = x < 0 fun p2(x) = x > 0 fun p3(x) = x == 0 //│ fun p1: Num -> Bool //│ fun p2: Num -> Bool //│ fun p3: Num -> Bool fun example1(p) = if p is Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then "both positive" else "nah" //│ fun example1: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "both positive" | "nah") // FIXME: The following test case should fail, but it doesn't. The reason is // `x` and `y` are in the desugared lexical scope, although they don't in the // original lexical scope. fun example2(p) = if p is Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then x + y else "nah" //│ fun example2: (Object & ~#Pair | Pair[Int, Int]) -> ("both negative" | "nah" | Int) // Next, let's check the name collision between a class and its super class. class Base(x: Int) class Derived(y: Int) extends Base(y + 1) //│ class Base(x: Int) //│ class Derived(y: Int) extends Base // Notice that Derived is not in the inferred type. :ducs:postprocess.result fun example3(t) = if t is Base(x) and p1(x) then x Derived(y) then y else 42 //│ Post-processed UCS term: //│ case t*‡ of //│ refined Base*◊ -> //│ let ucs$args_t$Base*† = (Base).unapply(t,) //│ let x*‡ = (ucs$args_t$Base).0 //│ let ucs$test$0*† = p1(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> x //│ _ -> //│ case t*‡ of //│ Derived*◊ -> //│ let ucs$args_t$Derived*† = (Derived).unapply(t,) //│ let y*‡ = (ucs$args_t$Derived).0 //│ y //│ _ -> 42 //│ _ -> 42 //│ fun example3: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base) -> (Int | 'a) example3(Derived(1)) //│ Int //│ res //│ = 1 fun example4(t, x) = if t is Base(x) and p1(x) then x Derived(y) then y + x // ^ // Note that this branch will be absorbed by the previous one. As the // previous branch shadows the variable `x`, a correct implementation // should restore the original value of `x` in this branch. else 42 //│ fun example4: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base, Int) -> (Int | 'a) example4(Base(-1), 0) ~~> -1 example4(Base(1), 2) ~~> 42 //│ "passed" //│ res //│ = 'passed' //│ res //│ = 'passed' example4(Derived(1), 4) ~~> 5 //│ "passed" //│ res //│ = 'passed' class Base(x: Int) class Derived[A](y: A) extends Base(1) //│ class Base(x: Int) //│ class Derived[A](y: A) extends Base // Notice that now Derived is generic, so it's appear in the inferred type. fun example5(t) = if t is Base(x) and p1(x) then x Derived(y) then y else 42 //│ fun example5: forall 'a 'b. (Base & {#x: Num & 'a} & ~#Derived | Derived['b] & {#x: Num & 'a} | Object & ~#Base) -> (42 | 'a | 'b) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/staging/stages/Transformation.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] class Cons[T](head: T, tail: List[T]) module Nil type List[T] = Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) //│ module Nil //│ type List[T] = Cons[T] | Nil abstract class Either[out A, out B] class Left[out A, out B](value: A) extends Either[A, B] class Right[out A, out B](value: B) extends Either[A, B] //│ abstract class Either[A, B] //│ class Left[A, B](value: A) extends Either //│ class Right[A, B](value: B) extends Either class Pair[A, B](x: A, y: B) { fun mapFirst[C](f: A -> C): Pair[C, B] = Pair(f(x), y) fun mapSecond[C](f: B -> C): Pair[A, C] = Pair(x, f(y)) } //│ class Pair[A, B](x: A, y: B) { //│ fun mapFirst: forall 'C. (f: A -> 'C) -> Pair['C, B] //│ fun mapSecond: forall 'C0. (f: B -> 'C0) -> Pair[A, 'C0] //│ } :ducs:transform.result fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None //│ Transformed UCS term: //│ if //│ xs is //│ Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys,) is Some(tail) then Some(Cons(f(x, y,), tail,),) //│ Nil and ys is Nil then Some(Nil,) //│ else None //│ fun zipWith: forall 'T 'T0 'T1 'T2. (('T, 'T0) -> 'T1, Cons['T] | Object & ~#Cons, Cons['T0] | Object & ~#Cons) -> (None | Some[in List['T1] & 'T2 out Nil | 'T2 | Cons['T1]]) :ducs:transform.result fun getOrElse[T](x: Option[T], default: T): T = if x is Some(value) then value None then default //│ Transformed UCS term: //│ if x is //│ Some(value) then value //│ None then default //│ fun getOrElse: forall 'T. (x: Option['T], default: 'T) -> 'T fun m3(x: Int): Bool = x % 3 == 0 fun m5(x: Int): Bool = x % 5 == 0 //│ fun m3: (x: Int) -> Bool //│ fun m5: (x: Int) -> Bool :ducs:transform.result fun f(x) = if x is Some(v) and m3(v) and m5(v) then "FizzBuzz" Some(v) and m3(v) then "Fizz" Some(v) and m5(v) then "Buzz" else "meh" //│ Transformed UCS term: //│ if x is //│ Some(v) and m3(v,) and m5(v,) then "FizzBuzz" //│ Some(v) and m3(v,) then "Fizz" //│ Some(v) and m5(v,) then "Buzz" //│ else "meh" //│ fun f: (Object & ~#Some | Some[Int]) -> ("Buzz" | "Fizz" | "FizzBuzz" | "meh") ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/And.mls ================================================ :parseOnly :pt x and y //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'and' //│ rhs = Ident of "y" x and y //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'and' //│ rhs = Ident of "y" if x and y then z //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'and' //│ rhs = Ident of "y" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" if x and y then z else d //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'and' //│ rhs = Ident of "y" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" //│ PrefixApp: //│ kw = Keywrd of keyword 'else' //│ rhs = Ident of "d" if x is A and y then z //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "A" //│ kw = Keywrd of keyword 'and' //│ rhs = Ident of "y" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" if x is A(u, v, w) and y then z B then C //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = App: //│ lhs = Ident of "A" //│ rhs = Tup of Ls of //│ Ident of "u" //│ Ident of "v" //│ Ident of "w" //│ kw = Keywrd of keyword 'and' //│ rhs = Ident of "y" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "C" if x is A(u, v, w) and v is B then z B then C //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = App: //│ lhs = Ident of "A" //│ rhs = Tup of Ls of //│ Ident of "u" //│ Ident of "v" //│ Ident of "w" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "v" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "C" :todo if x is A(u, v, w) and v is B then z B then C //│ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead //│ ║ l.132: and v is //│ ║ ^ //│ ║ l.133: B then z //│ ╙── ^^^^ //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = App: //│ lhs = Ident of "A" //│ rhs = Tup of Ls of //│ Ident of "u" //│ Ident of "v" //│ Ident of "w" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "v" //│ kw = Keywrd of keyword 'is' //│ rhs = Error //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "C" if x is A(u, v, w) and v is B and w is B then z B then C //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = InfixApp: //│ lhs = App: //│ lhs = Ident of "A" //│ rhs = Tup of Ls of //│ Ident of "u" //│ Ident of "v" //│ Ident of "w" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "v" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "B" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "w" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "C" if x is A(u, v, w) and v is B then z and w is B then z B then C //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = App: //│ lhs = Ident of "A" //│ rhs = Tup of Ls of //│ Ident of "u" //│ Ident of "v" //│ Ident of "w" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "v" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "z" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "w" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "z" //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "C" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/BadUCSSyntax.mls ================================================ :js // ——— ——— ——— :e if x else z //│ ╔══[COMPILATION ERROR] Unrecognized term split (identifier) //│ ║ l.7: if x else z //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: z //│ ║ l.7: if x else z //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :e fun f(x, z) = if x else z //│ ╔══[COMPILATION ERROR] Unrecognized term split (identifier) //│ ║ l.18: if x //│ ╙── ^ // ——— ——— ——— // Works if true and true do print("ok") //│ > ok // Works if true and true and true do print("ok") //│ > ok // Should work? Probably not :e if true and true let x = 1 and true do print("ok") //│ ╔══[COMPILATION ERROR] Unrecognized term split (true literal) //│ ║ l.45: and true //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'do' here //│ ║ l.46: let x = 1 //│ ║ ^ //│ ║ l.47: and true do //│ ║ ^^^^^^^^^^^^^ //│ ║ l.48: print("ok") //│ ╙── ^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // ——— ——— ——— ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/ConjunctMatches.mls ================================================ // ConjunctMatches.mls // =================== // Examine the structure of conjunct matches. :global :parseOnly :pt // This test cases should be parsed as // (((x is A) and (b is Y)) and (c is Z)) // To achieve that, the following rules should be applied: // - is should be left associative // - and should be right associative // - is should have higher precedence than and if x is A and y is B and c is Z then 1 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "A" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "y" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "B" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "c" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "Z" //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 1 // But since naughty users may add parentheses, // we should also parse the following case. if x is A and (y is B and c is Z) then 1 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = Block of Ls of //│ InfixApp: //│ lhs = InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "A" //│ kw = Keywrd of keyword 'and' //│ rhs = Bra: //│ k = Round //│ inner = InfixApp: //│ lhs = InfixApp: //│ lhs = Ident of "y" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "B" //│ kw = Keywrd of keyword 'and' //│ rhs = InfixApp: //│ lhs = Ident of "c" //│ kw = Keywrd of keyword 'is' //│ rhs = Ident of "Z" //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/Do.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option // Using `do` will not cause match errors. let x = false //│ x = false if x do print("executed") set x = false x //│ = false if not(x) do print("executed") //│ > executed if not x do print("executed") //│ > executed if (not x) do print("executed") set x = false //│ > executed x //│ = false // Completely using `do` // ===================== fun f(y) = let x = Some(y) if x is Some(0) do set x = None Some(v) and v % 2 == 0 do set x = Some(v / 2) x f(0) //│ = None f(42) //│ = Some(21) f(41) //│ = Some(41) // Mix using `then` and `do` // ========================= :e fun g(y) = let x = Some(y) if x is Some(0) do set x = None Some(v) and v % 2 == 0 then set x = Some(v / 2) x //│ ╔══[COMPILATION ERROR] Mixed use of `do` and `then` in the `if` expression. //│ ║ l.66: if x is //│ ║ ^^ //│ ╟── Keyword `then` is used here. //│ ║ l.68: Some(v) and v % 2 == 0 then set x = Some(v / 2) //│ ║ ^^^^ //│ ╟── Keyword `do` is used here. //│ ║ l.67: Some(0) do set x = None //│ ╙── ^^ g(0) //│ = None g(42) //│ = Some(21) g(41) //│ = Some(41) // Completely using `then` // ======================= fun h(y) = let x = Some(y) if x is Some(0) then set x = None Some(v) and v % 2 == 0 then set x = Some(v / 2) x h(0) //│ = None h(42) //│ = Some(21) :re h(41) //│ ═══[RUNTIME ERROR] Error: match error // [Adapted from Copilot (Claude Opus 4.6) summary:] // `pattern do body` inside `is` expression is not handled. // The root cause is at the parser level: in `Some of [process, rest] do "hi"`, // the `of` keyword greedily includes `do "hi"` inside the constructor argument, // producing `App(Some, Tup([InfixApp(Tup([process, rest]), do, "hi")]))`. // In `if`/`case`/`while` splits, the parser treats `do` as a split boundary // at the branch level, so it correctly separates pattern from consequent. But // in standalone `is` expressions, the block lines are parsed as regular // expressions, so `do` is parsed as an infix operator inside the constructor's // argument. // * [My note:] // * Operator `of` parses a _block_ on the RHS, // * and there's currently no way of parsing blocks at a lower precedence level. // * This code is not supposed to compile, anyway. :e Some(1) is Some of [process, rest] do "hi" //│ ╔══[COMPILATION ERROR] Unrecognized pattern (infix operator 'do'). //│ ║ l.129: Some of [process, rest] do "hi" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/DoProblems.mls ================================================ :js // * It looks like trying to support both infix `do` and prefix `do` is a problem; // * maybe this part of the syntax needs to be redesigned. // ——— ——— ——— // [Comment from Copilot (Claude Opus 4.6):] // Bug: `do` on a new line after `while` condition is not parsed as infix. // The parser explicitly excludes `do` from newline-infix handling (Parser.scala ~line 1088) // to prevent ambiguity with UCS splits (e.g., `0 then "null"\ndo body` should be two split // arms, not `(0 then "null") do body`). Fixing this would require context-sensitive parsing // to distinguish `while` conditions from `if` split arms, which is a significant refactoring // of the parser's `exprCont` method. :fixme while let arrMatches = true arrMatches do print(arrMatches) //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'do' here //│ ║ l.18: let arrMatches = true //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.19: arrMatches //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.20: do //│ ║ ^^^^ //│ ║ l.21: print(arrMatches) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. // ——— ——— ——— // [Comment from Copilot (Claude Opus 4.6):] // Bug: `do` on a new line inside `if` block is not parsed as infix continuation. // Same root cause as the `while...do` bug above: the `do` exclusion in Parser.scala // prevents `do` from being treated as a newline-infix operator. In the `if` block, // `arrMatches !== null` and `do ()` are parsed as separate block items instead of // `arrMatches !== null do ()`. Fixing requires the same context-sensitive parsing // refactoring mentioned above. let arrMatches = () //│ arrMatches = () // * Note: produces a match error :fixme if arrMatches !== null do () //│ ╔══[COMPILATION ERROR] Unrecognized term split (null) //│ ║ l.48: arrMatches !== null //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] Error: match error // * This one works: if arrMatches !== null do () ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/Else.mls ================================================ :js // Else.mls // ======== // Examine how the `else` keyword is parsed. fun f(x, y, z) = if x then y :pt fun f(x, y, z) = if x then y else z //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TermDef: //│ k = Fun //│ head = App: //│ lhs = Ident of "f" //│ rhs = Tup of Ls of //│ Ident of "x" //│ Ident of "y" //│ Ident of "z" //│ rhs = S of IfLike: //│ kw = Keywrd of keyword 'if' //│ split = Block of Ls of //│ InfixApp: //│ lhs = Ident of "x" //│ kw = Keywrd of keyword 'then' //│ rhs = Ident of "y" //│ PrefixApp: //│ kw = Keywrd of keyword 'else' //│ rhs = Ident of "z" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z fun f(x, y, z) = if x then y else z :fixme fun f(x, y, z) = if x then y else z //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.77: x then y else z //│ ╙── ^^^^ fun f(x, y) = if x then y let audits = new Set() //│ audits = Set(0) {} if audits.has(1) === true do print("ok") else print(1) //│ > 1 (if true then 5 else 1 ) + 3 //│ = 8 :fixme (if true then 5 else 1 ) + 3 //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.107: else 1 //│ ╙── ^^^^ //│ = 8 :fixme (if true then 5 else 1 ) + 3 //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.117: 5 else 1 //│ ╙── ^^^^ //│ = 8 data class Box[A](value: A) :w fun foo(x) = if x is Box(0) then "The box contains zero." Box(1) then "The box contains one." else "I can't tell you what's inside the box." else "I don't want to tell you what's inside the box." else "I shouldn't tell you what's inside the box." //│ ╔══[WARNING] This catch-all clause makes the following branches unreachable. //│ ║ l.131: else "I can't tell you what's inside the box." //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── This branch is unreachable. //│ ║ l.132: else "I don't want to tell you what's inside the box." //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── This branch is unreachable. //│ ║ l.133: else "I shouldn't tell you what's inside the box." //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/Empty.mls ================================================ :js :sjs :re if {} //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: match error :re if let x = 1 //│ ═══[RUNTIME ERROR] Error: match error :re if do print("whoops") //│ > whoops //│ ═══[RUNTIME ERROR] Error: match error :re :e if let x //│ ╔══[COMPILATION ERROR] Unrecognized term split (let) //│ ║ l.29: let x //│ ╙── ^ //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/IfOpSplit.mls ================================================ :js let x = 1 //│ x = 1 if x is 0 then "zero" 1 then "one" //│ = "one" if x > 0 then true //│ = true if x > 1 then true 0 then false //│ = false // * This is an error because the two split branches would bind differently to the common prefix :pe :e if 1 + 2 * 3 then 0 + 4 then 0 //│ ╔══[PARSE ERROR] Operator cannot be used inside this operator split //│ ║ l.28: + 4 then 0 //│ ║ ^ //│ ╟── as it has lower precedence than the splitting operator here //│ ║ l.27: * 3 then 0 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in this operator split inner position //│ ║ l.28: + 4 then 0 //│ ║ ^^^^ //│ ╟── Note: the operator split starts here //│ ║ l.27: * 3 then 0 //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.26: if 1 + 2 //│ ║ ^ //│ ║ l.27: * 3 then 0 //│ ║ ^^^^^^^^^^^^ //│ ║ l.28: + 4 then 0 //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. if 1 + 2 * 3 then 0 * 4 then 1 else 2 //│ = 2 if (1 + 2) * 3 === 12 then 0 * 4 === 12 then 1 //│ = 1 if (1 + 2) * 3 === 7 then 0 + 4 === 7 then 1 //│ = 1 :e if 1 + 2 * 3 then 0 then 0 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here //│ ║ l.70: * 3 then 0 //│ ║ ^ //│ ║ l.71: then 0 //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error if x > 1 then true == 1 then false //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/Is.mls ================================================ :parseOnly x is A //│ Parsed: //│ InfixApp(Ident(x),Keywrd(keyword 'is'),Ident(A)) x is A is B //│ Parsed: //│ OpSplit(Ident(x),List(InfixApp(SplitPoint(),Keywrd(keyword 'is'),Ident(A)), InfixApp(SplitPoint(),Keywrd(keyword 'is'),Ident(B)))) ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/NestedOpSplits.mls ================================================ :js :ucs desugared // * Note that this always associates to the left fun f(x) = if x == 1 + 2 then 0 _ then 1 //│ Split with nested patterns: //│ > if //│ > let tmp:scrut = 1 //│ > let tmp:scrut = (member:Predef⁰.)equals‹member:equals›(x⁰, builtin:+⁰(tmp:scrut⁰, 2)) //│ > tmp:scrut is true then 0 //│ > else 1 //│ Expanded split with flattened patterns: //│ > ‹if|while› //│ > let tmp:scrut = 1 //│ > let tmp:scrut = (member:Predef⁰.)equals‹member:equals›(x⁰, builtin:+⁰(tmp:scrut⁰, 2)) //│ > tmp:scrut is true then 0 //│ > else 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/Of.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option { Some, None } fun getOrHello(x) = if x is None then "hello" Some of a then a getOrHello(None) //│ = "hello" getOrHello(Some of 42) //│ = 42 fun getOrElse(x, defaultValue) = if x is None then defaultValue Some of value then value None getOrElse(42) //│ = 42 Some(0) getOrElse(42) //│ = 0 let getOr42 = case Some of value then value None then 42 //│ getOr42 = fun None getOr42() //│ = 42 Some(0) getOr42() //│ = 0 fun check(x) = x is Some of 42 check(None) //│ = false check(Some(42)) //│ = true check(Some(77)) //│ = false data class Left(leftValue) data class Right(rightValue) fun getBothOrElse(either, defaultValue) = if either is None then defaultValue Some of Left of value then value Right of value then value :expect 42 getBothOrElse of None, 42 //│ = 42 :expect 7 getBothOrElse of Some(Left(7)), 42 //│ = 7 :expect "noice" getBothOrElse of Some(Left("noice")), 42 //│ = "noice" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/PlainConditionals.mls ================================================ :js fun f(x) = x fun g(x) = x fun h(x) = x data class Pair[A, B](fst: A, snd: B) // Simple conditionals in the form of `x is P`. // ============================================ :expect true Pair(0, 1) is Pair //│ = true :expect true Pair(0, 1) is Pair(a, b) //│ = true :expect true Pair(0, 1) is Pair(0, _) //│ = true :expect true if Pair(0, 1) is Pair(a, b) then true else false //│ = true // Based on the above, add an `and` at the end, in the form of `x is P and t`. // =========================================================================== :expect false Pair(0, 1) is Pair(a, b) and a > b //│ = false :expect true Pair(1, 0) is Pair(a, b) and a > b //│ = true fun foo(x) = f(x) and g(x) and h(x) :expect [true, false] [foo(true), foo(false)] //│ = [true, false] // Test matching multiple patterns at once. // ======================================== fun foo(x) = x is Pair Int :expect [true, false, true] [foo(2), foo(3.14159), foo(Pair(0, 1))] //│ = [true, false, true] fun foo(x) = x is Pair | Int :expect [true, false, true] [foo(2), foo(3.14159), foo(Pair(0, 1))] //│ = [true, false, true] fun foo(x) = x is Pair | Int :expect [true, false, true] [foo(2), foo(3.14159), foo(Pair(0, 1))] //│ = [true, false, true] fun foo(x) = x is { Pair, Int } :expect [true, false, true] [foo(2), foo(3.14159), foo(Pair(0, 1))] //│ = [true, false, true] :e fun foo(x) = x is Pair(a, b) and a > b Int //│ ╔══[COMPILATION ERROR] Unrecognized pattern (infix operator 'and'). //│ ║ l.77: Pair(a, b) and a > b //│ ╙── ^^^^^^^^^^^^^^^^^^^^ :e fun foo(x) = x is Pair(a, b) | Int //│ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. //│ ║ l.85: fun foo(x) = x is Pair(a, b) | Int //│ ║ ^ //│ ╟── The variable is missing from this sub-pattern. //│ ║ l.85: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. //│ ║ l.85: fun foo(x) = x is Pair(a, b) | Int //│ ║ ^ //│ ╟── The variable is missing from this sub-pattern. //│ ║ l.85: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^^^ // Test how shorthand conditionals work together with lambda functions. // ==================================================================== data class A[T](arg: T) let foo = x => x is A(_) //│ foo = fun foo :expect [false, true] [foo(0), foo(A(0))] //│ = [false, true] let foo = _ is A //│ foo = fun foo :expect [false, true] [foo(0), foo(A(0))] //│ = [false, true] let foo = _ is A(_) //│ foo = fun foo :expect [false, true] [foo(0), foo(A(0))] //│ = [false, true] let foo = _ is A(x) and x > 0 //│ foo = fun :expect [false, false, true] [foo(0), foo(A(0)), foo(A(1))] //│ = [false, false, true] ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/SimpleUCS.mls ================================================ :js abstract class Option[A]: Some[A] | None data class Some[A](value: A) extends Option[A] object None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] data class Left[A](leftValue: A) extends Either[A, Nothing] data class Right[B](rightValue: B) extends Either[Nothing, B] fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv Right(xv) and y is Right(yv) then xv * yv None and y is None then 0 fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv None then 0 fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv Right(yv) then xv * yv None then 0 fun f(x) = if x is Some(v) and v < 0 then "negative" v > 0 then "positive" _ then "zero" None then "nothing" fun f(x, y) = if x is Some(x) and y is Some(y) then 0 data class A[T](value: T) data class B[T](value: T) fun f(x, y, u, v) = if x is A(a) and y == u then 0 v then 1 A(a) and y is B(0) then 0 B(1) then 1 A(_) then 99 fun f(x) = if x is A(_) then "A" B(_) then "B" // :e fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None and y is None then 0 // :e fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None then xv * 2 None and y is Some(yv) then yv * 3 fun f(x, y) = if x is A and y is B then "bruh" fun f(x, y, z) = if x is A and z == 0 and y == 0 and y is B then "bruh" A then "oui" :breakme // Note: should report missing final else clause. :e fun f(x, y) = if x is Some(x) and y > 0 then "gt" < 0 then "le" == 0 then "eq" fun isValid(x) = if x then false else true fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" None and allowNone then "okay" else "bad" fun f(x) = if x is None then "bruh" Some(x) then "roll" _ and x == 0 then 0 _ then "rock" fun f(x, a, b) = if x is A(aa) and a then aa B(bb) and b then bb _ then 0 fun f(x, y, b) = if x is Some(xv) and y is Some(yv) then "bruh" is None then "bruh" Some(xv) and b then xv + b _ then "roll" fun g(x, y, b) = if x is Some(xv) and y is Some(yv) then yv is None then "bruh" Some(xv) and b then xv + b _ then "roll" fun foo(x, y, z) = if x - y > 0 then Some(x + y + z) else None // Uncomment this block to make the following block work. // fun foo(x, y, z) = // if x - y > 0 then Some( // if x % 2 == 0 then Left(x) else Right(x) // ) else None fun f(u, v, w) = if foo(u, v, w) is Some(x) and x is Left(_) then "left-defined" Right(_) then "right-defined" None then "undefined" fun p(x) = if x >= 0 then Right(x) else Left(x) fun g(a, b) = if p(a) is Left(x) and b is Some(y) then x + y None then x * a Right(x) and b is Some(y) then x * y None then x g(5, None) //│ = 5 g(5, Some(7)) //│ = 35 g(0 - 5, None) //│ = 25 g(0 - 5, Some(9)) //│ = 4 data class Var(name: Str) abstract class ValBase: (IntVal | BoolVal) data class IntVal(value: Int) extends ValBase data class BoolVal(value: Bool) extends ValBase data class Lit(value: ValBase) fun p(e, context) = if e is Var(x) and context.get(x) is Some(IntVal(v)) then Left(v) Some(BoolVal(v)) then Right(v) Lit(IntVal(v)) then Left(v) Lit(BoolVal(v)) then Right(v) data class Nil() // Support operator constructor like :: :todo :e fun f(x) = if x is 0 :: Nil() then "oh" //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (operator application). //│ ║ l.199: 0 :: //│ ║ ^^^^ //│ ║ l.200: Nil() then "oh" //│ ╙── ^^^^^^ fun f(x) = if x == 0 and x is A(_) then "A" B(_) then "B" else "bruh" fun helper(x) = if x == 0 then None else Some(x) fun g(x, y) = if x == 0 and helper(x) is Some(a) and helper(y) is Some(b) then a + b None then a + 1 None and helper(y) is Some(b) then 2 + b None then 1 else 0 fun test(x) = if x then 0 else "oops" test(true) //│ = 0 test(false) //│ = "oops" test(0) //│ = "oops" test(1) //│ = "oops" ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/Split.mls ================================================ :parseOnly :pt :pt 1 + 2 3 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "+" //│ rhss = Ls of //│ Block of Ls of //│ IntLit of 2 //│ IntLit of 3 if f(x) == 1 + 1 then 0 2 then 3 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = OpApp: //│ lhs = App: //│ lhs = Ident of "f" //│ rhs = Tup of Ls of //│ Ident of "x" //│ op = Ident of "==" //│ rhss = Ls of //│ Block of Ls of //│ InfixApp: //│ lhs = OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 1 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 0 //│ InfixApp: //│ lhs = IntLit of 2 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 3 if x + 1 * 2 then 3 4 then 5 y / 6 is A then 7 B then 9 else 42 8 then 9 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ IfLike: //│ kw = Keywrd of keyword 'if' //│ split = OpApp: //│ lhs = Ident of "x" //│ op = Ident of "+" //│ rhss = Ls of //│ Block of Ls of //│ OpApp: //│ lhs = IntLit of 1 //│ op = Ident of "*" //│ rhss = Ls of //│ Block of Ls of //│ InfixApp: //│ lhs = IntLit of 2 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 3 //│ InfixApp: //│ lhs = IntLit of 4 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 5 //│ OpApp: //│ lhs = Ident of "y" //│ op = Ident of "/" //│ rhss = Ls of //│ Block of Ls of //│ InfixApp: //│ lhs = IntLit of 6 //│ kw = Keywrd of keyword 'is' //│ rhs = Block of Ls of //│ InfixApp: //│ lhs = Ident of "A" //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 7 //│ InfixApp: //│ lhs = Ident of "B" //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 9 //│ PrefixApp: //│ kw = Keywrd of keyword 'else' //│ rhs = IntLit of 42 //│ InfixApp: //│ lhs = IntLit of 8 //│ kw = Keywrd of keyword 'then' //│ rhs = IntLit of 9 :todo if x is A then "A" //│ /!!!\ Uncaught error: scala.NotImplementedError: List() (of class Nil$) x + 1 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpApp: //│ lhs = Ident of "x" //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 1 x + 1 + 2 + 3 //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ OpSplit: //│ lhs = Ident of "x" //│ ops_rhss = Ls of //│ OpApp: //│ lhs = SplitPoint //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 1 //│ OpApp: //│ lhs = SplitPoint //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 2 //│ OpApp: //│ lhs = SplitPoint //│ op = Ident of "+" //│ rhss = Ls of //│ IntLit of 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/TupleRest.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" open Stack fun f(xs) = if xs is [..xs] then 0 [...xs] then 1 [..[]] then 2 [...[]] then 3 [..Cons(x, xs)] then 4 [...Cons(x, xs)] then 5 [..] then 6 [...] then 7 [.., x] then 8 [..., x] then 9 [... , x] then 10 ================================================ FILE: hkmc2/shared/src/test/mlscript/ucs/syntax/WithBraces.mls ================================================ // :elt x => if x is { 0 then false, 1 then true } // :pt // :elt x => if x is 0 then false 1 then true if 0 is { 0 then false } x => if x is { 0 then false 1 then true } ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls ================================================ :js :e pattern P //│ ╔══[COMPILATION ERROR] Pattern definitions must have a body. //│ ║ l.5: pattern P //│ ╙── ^^^^^^^^^ :e pattern Foo1[A]: ... //│ ╔══[COMPILATION ERROR] Pattern definitions must have a body. //│ ║ l.11: pattern Foo1[A]: ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^ :fixme pattern Foo2[pattern A]: ... //│ /!!!\ Uncaught error: scala.MatchError: TypeDef(Pat,Ident(A),None) (of class hkmc2.syntax.Tree$TypeDef) :e pattern Foo(pattern T) //│ ╔══[COMPILATION ERROR] Pattern definitions must have a body. //│ ║ l.21: pattern Foo(pattern T) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ pattern P = P :breakme // should be rejected :e :re 1 is P //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/BasicStackPatterns.mls ================================================ :js import "../../mlscript-compile/Stack.mls" open Stack // * Wrong parse – could we make it parse the expected result instead? :e :w pattern P = x :: xs => [x, xs] //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.11: pattern P = x :: xs => [x, xs] //│ ╙── ^ //│ ╔══[WARNING] Unused pattern binding: x. //│ ║ l.11: pattern P = x :: xs => [x, xs] //│ ╙── ^ pattern P = (x :: xs) => [x, xs] 1 :: Nil is P //│ = true let f = case P as extr then extr //│ f = fun f(1 :: 2 :: 3 :: Nil) //│ = [1, Cons(2, Cons(3, Nil))] pattern P(x, xs) = x :: xs 1 :: Nil is P //│ = true 1 :: Nil is P(a, b) //│ = true let f = case P(x, xs) then [x, xs] //│ f = fun f(1 :: 2 :: 3 :: Nil) //│ = [1, Cons(2, Cons(3, Nil))] let f = case P as extracted then extracted //│ f = fun f(1 :: 2 :: 3 :: Nil) //│ = [1, Cons(2, Cons(3, Nil))] ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/Future.mls ================================================ :js :e // Hypothetical syntax: parameterized patterns declared by type parameters. pattern Rep0[A] = "" | A ~ Rep0[A] //│ ╔══[COMPILATION ERROR] Pattern name not found: A. //│ ║ l.5: pattern Rep0[A] = "" | A ~ Rep0[A] //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (application). //│ ║ l.5: pattern Rep0[A] = "" | A ~ Rep0[A] //│ ╙── ^^^^^^^ :fixme // Hypothetical syntax: put pattern parameters in the type parameter list but // prefixed with `pattern`. pattern Rep0[pattern A] = "" | A ~ Rep0[A] //│ /!!!\ Uncaught error: scala.MatchError: TypeDef(Pat,Ident(A),None) (of class hkmc2.syntax.Tree$TypeDef) :e // Hypothetical syntax: separate parameter list for the extraction. pattern Rep0(pattern A, B, C)(head) = "" | (A as head) ~ Rep0[A] //│ ╔══[COMPILATION ERROR] Multiple parameter lists are not supported for this definition. //│ ║ l.21: pattern Rep0(pattern A, B, C)(head) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: "" | (A as head) ~ Rep0[A] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (application). //│ ║ l.22: "" | (A as head) ~ Rep0[A] //│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. //│ ║ l.22: "" | (A as head) ~ Rep0[A] //│ ║ ^^^^ //│ ╟── The variable is missing from this sub-pattern. //│ ║ l.22: "" | (A as head) ~ Rep0[A] //│ ╙── ^^ pattern Identifier = ("a" ..= "z") ~ (Identifier | "") // Pattern extractions through non-`pattern` parameters. pattern Email(name, domain) = (Identifier as name) ~ "@" ~ (Identifier as domain) :e // Hypothetical syntax (very unlikely to implement): view patterns pattern GreaterThan(value) = case n and n > value then n //│ ╔══[COMPILATION ERROR] Unrecognized pattern (case). //│ ║ l.47: n and n > value then n //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ :e // Hypothetical syntax (very unlikely to implement): normal view patterns fun view = case n and n > 5 then n else undefined fun foo(x) = if x is view as Unit then .... Arrow(...) then .... //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator 'as'). //│ ║ l.58: view as //│ ║ ^^^^^^^ //│ ║ l.59: Unit then .... //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.60: Arrow(...) then .... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ // In the future, any function can be a pattern // (x => view(x)) as ... // (case { ... }) as ... :pe :e // Hypothetical syntax: use `to` instead of ugly `..=` pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" pattern Many(pattern T) = T ~ (Many(T) | "") pattern Star(pattern T) = "" | Many(T) pattern Email(name, domains) = Rep(Char | ".") as name ~ "@" ~ Rep(Rep(Char) ~ ) as domain //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.78: pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (juxtaposition). //│ ║ l.78: pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" //│ ╙── ^^^^^^ :pe :e // Hypothetical syntax: use `to` instead of ugly `..=` pattern Digits = "0" to "9" ~ (Digits | "") //│ ╔══[PARSE ERROR] Expected end of input; found literal instead //│ ║ l.93: pattern Digits = "0" to "9" ~ (Digits | "") //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (juxtaposition). //│ ║ l.93: pattern Digits = "0" to "9" ~ (Digits | "") //│ ╙── ^^^^^^ pattern Test(foo, bar) = ("foo" as foo) ~ ("bar" as bar) "foobar" is Test //│ = true :expect MatchSuccess(["foo", "bar"], null) Test.unapply("foobar") //│ = MatchSuccess(["foo", "bar"], null) :pt // TODO: We cannot distinguish between `...` and `undefined` in the `Elaborator` now. pattern Email(name, domain) = ... //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TypeDef: //│ k = Pat //│ head = App: //│ lhs = Ident of "Email" //│ rhs = Tup of Ls of //│ Ident of "name" //│ Ident of "domain" //│ rhs = S of Block of Ls of //│ UnitLit of false //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // * Future: `as`-splits :fixme // TODO x => if x as 0 then false 1 then true //│ ╔══[COMPILATION ERROR] Unrecognized term split (infix operator 'as') //│ ║ l.129: x => if x as //│ ║ ^^^^ //│ ║ l.130: 0 then false //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.131: 1 then true //│ ╙── ^^^^^^^^^^^^^ //│ = fun :fixme // TODO pattern Test = (0 => false) | (1 => true) fun test(x) = if x is Test as true then "T" false then "F" //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator 'as'). //│ ║ l.144: Test as //│ ║ ^^^^^^^ //│ ║ l.145: true then "T" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.146: false then "F" //│ ╙── ^^^^^^^^^^^^^^^^^^ :fixme // TODO This should work! pattern Test = (0 => false) | (1 => true) if 0 is ((Test as r) where (r is x)) then print(x) //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.158: if 0 is ((Test as r) where (r is x)) then print(x) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. :fixme // TODO This should work! pattern Test = 0 | 1 class Id(name: Test) //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. //│ ║ l.167: class Id(name: Test) //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. //│ ║ l.167: class Id(name: Test) //│ ║ ^^^^ //│ ╙── Non-module parameter must have a non-module type. //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. //│ ║ l.167: class Id(name: Test) //│ ╙── ^^^^ // * TODO: maybe we could allow this more lightweight alternative syntax? :todo pattern StatementBased = 0 => "zero" 1 => "one" _ => "many" //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. //│ ║ l.184: 0 => "zero" //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. //│ ║ l.185: 1 => "one" //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. //│ ║ l.186: _ => "many" //│ ╙── ^^^^^^^^^^^ | x => 1 //│ = fun // :de :fixme // TODO: support lambdas with patterns defined this way? | 0 => 1 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.203: | 0 => 1 //│ ╙── ^ //│ = fun :fixme // TODO | [x] => 1 //│ ╔══[COMPILATION ERROR] Illegal constraint syntax. //│ ║ l.210: | [x] => 1 //│ ╙── ^ //│ /!!!\ Uncaught error: scala.MatchError: Constrained(List(),Lit(IntLit(1))) (of class hkmc2.semantics.Term$Constrained) | (x, y) => 1 //│ = fun :fixme // TODO | (x, 0) => 1 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.220: | (x, 0) => 1 //│ ╙── ^ //│ = fun ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/JoinPatterns.mls ================================================ :js pattern SimpleJoin = ("abc" | "xyz") ~ ("def" | "") :expect true "abc" is SimpleJoin //│ = true :expect true "abcdef" is SimpleJoin //│ = true :expect true "xyzdef" is SimpleJoin //│ = true :expect true "xyz" is SimpleJoin //│ = true :expect false "abcxyzdef" is SimpleJoin //│ = false // TODO: If we want to get rid of exponential blowup here, we need to memoize the // entire sub-split instead of the innermost consequents. pattern Exponential = ("a" | "b") ~ ("c" | "d") ~ ("e" | "f") ~ ("g" | "h") // ~ ("i" | "j") ~ ("k" | "l") ~ ("m" | "n") ~ ("o" | "p") ~ ("q" | "r") ~ ("s" | "t") ~ ("u" | "v") ~ ("w" | "x") ~ ("y" | "z") :expect true "aceg" is Exponential //│ = true :expect true "bcfh" is Exponential //│ = true "abcdefghijklm" is Exponential //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/LocalPatterns.mls ================================================ :js pattern One = "1" module Playground with pattern Zero = "0" pattern DoubleZero = Zero ~ Zero pattern ZeroOne = Zero ~ One Playground //│ = class Playground { //│ Zero: pattern Zero, //│ DoubleZero: pattern DoubleZero, //│ ZeroOne: pattern ZeroOne //│ } // Pattern defined in a module can be used with qualified name. // ============================================================ Playground.Zero //│ = pattern Zero Playground.Zero.unapply("0") //│ = MatchSuccess("0", null) :expect true "0" is Playground.Zero //│ = true // Patterns defined in a module can refer to other patterns in the same module. // ============================================================================ Playground.DoubleZero //│ = pattern DoubleZero Playground.DoubleZero.unapply("00") //│ = MatchSuccess("00", null) :expect true "00" is Playground.DoubleZero //│ = true // Patterns defined in a module can refer to patterns in the global scope. // ======================================================================= Playground.ZeroOne //│ = pattern ZeroOne Playground.ZeroOne.unapply("01") //│ = MatchSuccess("01", null) :expect true "01" is Playground.ZeroOne //│ = true // Patterns defined in the global scope can refer to patterns in a module. // ======================================================================= pattern TripleZero = Playground.DoubleZero ~ Playground.Zero TripleZero //│ = pattern TripleZero TripleZero.unapply("000") //│ = MatchSuccess("000", null) :expect true "000" is TripleZero //│ = true :expect false "001" is TripleZero //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/MatchResult.mls ================================================ :js import "../../mlscript-compile/Runtime.mls" open Runtime { MatchSuccess, MatchFailure } MatchSuccess(0, new Object) //│ = MatchSuccess(0, {}) if MatchSuccess(0, new Object) is MatchSuccess(x, _) then x //│ = 0 MatchFailure("oops") //│ = MatchFailure("oops") if MatchFailure("oops") is MatchFailure(x) then x //│ = "oops" data class MatchSuccess(x) MatchSuccess(0) //│ = MatchSuccess(0) open annotations { compile } pattern Cross = "X" pattern Id(x) = _ data class Pair(left, right) pattern BacktrackOnRight = Pair("same", 0) | Pair("same", 1) pattern BacktrackOnLeft = Pair(0, "same") | Pair(1, "same") Cross.unapply("X") //│ = MatchSuccess("X", null) Cross.unapply("0") //│ = MatchFailure(null) // :sir fun foo(x) = x is Cross :sir // Keep the IR output to prevent regressions. fun isCompiledCross(x) = x is @compile Cross //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let isCompiledCross⁰; //│ define isCompiledCross⁰ as fun isCompiledCross¹(x) { //│ let matcher__Cross$, matchSuccess, lambda; //│ define lambda as fun lambda⁰(input) { //│ let p_0$, p_0$1, tmp, tmp1; //│ match input //│ "X" => //│ set tmp = true; //│ set p_0$ = tmp; //│ return p_0$ //│ else //│ set tmp1 = false; //│ set p_0$1 = tmp1; //│ return p_0$1 //│ end //│ }; //│ set matcher__Cross$ = lambda⁰; //│ set matchSuccess = matcher__Cross$(x); //│ match matchSuccess //│ true => //│ return true //│ else //│ return false //│ end //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— // :sir fun getCompiledCross(x) = if x is (@compile Cross) as result then result else "nope" // :sir fun isCompiledId(x) = x is @compile Id // :sir fun isCompiledBacktrackOnRight(x) = x is @compile BacktrackOnRight // :sir fun getCompiledBacktrackOnRight(x) = if x is (@compile BacktrackOnRight) as result then result else "nope" // :sir fun isCompiledBacktrackOnLeft(x) = x is @compile BacktrackOnLeft ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/RangePatterns.mls ================================================ :js pattern Lower = "a"..="z" "a" is Lower //│ = true "z" is Lower //│ = true "0" is Lower //│ = false pattern Upper = "A"..="Z" "A" is Upper //│ = true "Q" is Upper //│ = true "b" is Upper //│ = false pattern UnsignedShort = 0 ..< 65536 0 is UnsignedShort //│ = true -1 is UnsignedShort //│ = false 65535 is UnsignedShort //│ = true 65536 is UnsignedShort //│ = false 2147483647 is UnsignedShort //│ = false "b" is UnsignedShort //│ = false [] is UnsignedShort //│ = true :pe :e // Don't forget to add a space between numbers and the range operator. pattern UnsignedByte = 0..< 256 //│ ╔══[LEXICAL ERROR] Expected at least one digit after the decimal point //│ ║ l.51: pattern UnsignedByte = 0..< 256 //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: .< //│ ║ l.51: pattern UnsignedByte = 0..< 256 //│ ╙── ^^ //│ ═══[COMPILATION ERROR] Cannot use this ‹error› as a pattern. :e pattern BadRange = "s"..=0 //│ ╔══[COMPILATION ERROR] The upper and lower bounds of range patterns should be literals of the same type. //│ ║ l.61: pattern BadRange = "s"..=0 //│ ╙── ^^^^^^^ // It becomes an absurd pattern. 0 is BadRange //│ = true :e pattern BadRange = 0 ..= "s" //│ ╔══[COMPILATION ERROR] The upper and lower bounds of range patterns should be literals of the same type. //│ ║ l.71: pattern BadRange = 0 ..= "s" //│ ╙── ^^^^^^^^^ :e pattern BadRange = "yolo" ..= "swag" //│ ╔══[COMPILATION ERROR] The lower bound of character ranges must be a single character. //│ ║ l.77: pattern BadRange = "yolo" ..= "swag" //│ ║ ^^^^^^ //│ ╟── The upper bound of character ranges must be a single character. //│ ║ l.77: pattern BadRange = "yolo" ..= "swag" //│ ╙── ^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/RecursiveTransformations.mls ================================================ :js import "../../mlscript-compile/Stack.mls" open Stack pattern Bin = (0 => "false") | (1 => "true") | ([Bin as b] => b + "!") fun f(x) = if x is Bin as b then b ~Bin as c then [c] f(0) //│ = "false" f(1) //│ = "true" f(2) //│ = [2] f([1]) //│ = "true!" f([[[0]]]) //│ = "false!!!" let arr = fromArray of [1, 2, 3] //│ arr = Cons(1, Cons(2, Cons(3, Nil))) pattern Find(pattern P) = | ((P as p) :: t) => [p, t] | (h :: (Find(P) as [res, t])) => [res, h :: t] assert arr is Find(1) as res, res //│ = [1, Cons(2, Cons(3, Nil))] assert arr is Find(2) as res, res //│ = [2, Cons(1, Cons(3, Nil))] assert arr is Find(0) as res else "oops", res //│ = "oops" // * List-like pattern that disregards the order of elements! (quite inefficient) pattern MultiSet(pattern Head, pattern Rest) = | Find(Head) as [h, Rest as t] => h :: t arr is MultiSet(1, _) //│ = true arr is MultiSet(0, _) //│ = false arr is MultiSet(3, _) //│ = true arr is MultiSet(3, MultiSet(1, MultiSet(2, Nil))) //│ = true arr is MultiSet(3, MultiSet(0, _)) //│ = false :fixme assert arr is MultiSet(1, rest), rest //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'rest' //│ ║ l.65: assert arr is MultiSet(1, rest), rest //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] ReferenceError: rest is not defined :e assert arr is MultiSet as res, res //│ ═══[COMPILATION ERROR] Expected two pattern arguments, but found only zero pattern arguments. //│ ═══[RUNTIME ERROR] Error: Assertion failed (RecursiveTransformations.mls:73) pattern Id = _ :fixme if [1, 2, 3] is MultiSet(2, MultiSet(1, Id(x))) do print(x) //│ ═══[COMPILATION ERROR] No definition found in scope for member 'arg$Id$0$' //│ ═══[COMPILATION ERROR] No definition found in scope for member 'arg$Id$0$' //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //│ ║ l.81: MultiSet(2, MultiSet(1, Id(x))) do print(x) //│ ╙── ^ // ——— // * Whoopsie: bad definition pattern MultiSet(pattern Head, pattern Rest) = | ((Head as h) :: (Rest as r)) => h :: r | (((h0 :: h :: r) => h :: h0 :: r) as MultiSet(Head, Rest) as res) => res arr is MultiSet(1, _) //│ = true :re arr is MultiSet(3, _) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded arr is MultiSet(2, MultiSet(1, _)) //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/SimpleConjunction.mls ================================================ :js import "../../mlscript-compile/Option.mls" open Option { Some, None } open annotations pattern OverlappedIntervals = (0 ..= 2) & (1 ..= 3) fun test(n) = n is @compile OverlappedIntervals print of "0:", test of 0 print of "1:", test of 1 print of "2:", test of 2 print of "3:", test of 3 print of "42:", test of 42 //│ > 0: false //│ > 1: true //│ > 2: true //│ > 3: false //│ > 42: false fun show(n) = if n is (@compile OverlappedIntervals) as v then Some(v) else None print of "0:", show of 0 print of "1:", show of 1 print of "2:", show of 2 print of "3:", show of 3 print of "42:", show of 42 //│ > 0: None //│ > 1: Some(1) //│ > 2: Some(2) //│ > 3: None //│ > 42: None fun observeLeft(x) = print("left " + x) x fun observeRight(x) = print("right " + x) x pattern LoggedOverlappedIntervals = (((0 ..= 2) as x) => observeLeft(x)) & (((1 ..= 3) as y) => observeRight(y)) print of 1 is @compile LoggedOverlappedIntervals //│ > true if 1 is (@compile LoggedOverlappedIntervals) as value then print("logged interval: " + value) //│ > left 1 //│ > right 1 //│ > logged interval: 1,1 data class A(x: Int) data class B(x: Int) pattern BothTransformedConjunction = (((0 ..= 2) as x) => A(x)) & (((1 ..= 3) as y) => B(y)) fun show(n) = if n is (@compile BothTransformedConjunction) as v then Some(v) else None print of "0:", show of 0 print of "1:", show of 1 print of "2:", show of 2 print of "3:", show of 3 print of "42:", show of 42 //│ > 0: None //│ > 1: Some([A(1), B(1)]) //│ > 2: Some([A(2), B(2)]) //│ > 3: None //│ > 42: None pattern RightTransformedConjunction = (0 ..= 2) & (((1 ..= 3) as y) => B(y)) fun show(n) = if n is (@compile RightTransformedConjunction) as v then Some(v) else None print of "0:", show of 0 print of "1:", show of 1 print of "2:", show of 2 print of "3:", show of 3 print of "42:", show of 42 //│ > 0: None //│ > 1: Some([1, B(1)]) //│ > 2: Some([2, B(2)]) //│ > 3: None //│ > 42: None pattern LeftTransformedConjunction = (((0 ..= 2) as x) => A(x)) & (1 ..= 3) fun show(n) = if n is (@compile LeftTransformedConjunction) as v then Some(v) else None print of "0:", show of 0 print of "1:", show of 1 print of "2:", show of 2 print of "3:", show of 3 print of "42:", show of 42 //│ > 0: None //│ > 1: Some([A(1), 1]) //│ > 2: Some([A(2), 2]) //│ > 3: None //│ > 42: None // We can statically reduce this pattern to bottom. pattern ImpossibleIntervals = (0 ..= 2) & (3 ..= 4) // From the generated code we can see that all cases lead to match failure. fun test(n) = n is @compile ImpossibleIntervals // This pattern is equivalent to bottom but we can't statically reduce it. pattern UnionRecords = (a: 1, b: 2) & (a: 3, b: 4) fun test(rcd) = rcd is @compile UnionRecords test of (a: 1, b: 2) //│ = false test of (a: 3, b: 4) //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls ================================================ :js import "../../mlscript-compile/Option.mls" open Option { Some, None } open annotations data class Pair[A, B](first: A, second: B) :sir pattern SumPair = Pair(a, b) => a + b //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let SumPair⁰; //│ define SumPair⁰ as pattern SumPair¹ { //│ method unapply⁰ = fun unapply¹(input) { //│ let transform, arg$Pair$0$, arg$Pair$1$, transformResult, lambda; //│ define lambda as fun lambda⁰(a, b) { //│ return +⁰(a, b) //│ }; //│ set transform = lambda⁰; //│ match input //│ Pair⁰ => //│ set arg$Pair$0$ = input.first⁰; //│ set arg$Pair$1$ = input.second⁰; //│ set transformResult = transform(arg$Pair$0$, arg$Pair$1$); //│ return new runtime⁰.MatchSuccess⁰(transformResult, null) //│ else //│ return new runtime⁰.MatchFailure⁰(null) //│ end //│ } //│ method unapplyStringPrefix⁰ = fun unapplyStringPrefix¹(input) { //│ return new runtime⁰.MatchFailure⁰(null) //│ } //│ }; //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— fun foo(value) = if value is SumPair as sum then print("sum: " + sum) else print("not a pair") foo of Pair(1, 2) foo of Pair(10, 25) foo of () foo of 10 foo of "hello" //│ > sum: 3 //│ > sum: 35 //│ > not a pair //│ > not a pair //│ > not a pair pattern BoolLike = true | false | ((Int as x) => x !== 0) fun get_output_using_as(value) = if value is BoolLike as b then print("" + value + " is equivalent to " + b) else print("the input " + value + " is not a boolean") get_output_using_as of true get_output_using_as of false get_output_using_as of 0 get_output_using_as of 1 get_output_using_as of 2 //│ > true is equivalent to true //│ > false is equivalent to false //│ > 0 is equivalent to false //│ > 1 is equivalent to true //│ > 2 is equivalent to true fun get_output_using_as'(value) = if value is (@compile BoolLike) as b then print("" + value + " is equivalent to " + b) else print("the input " + value + " is not a boolean") get_output_using_as' of true get_output_using_as' of false get_output_using_as' of 0 get_output_using_as' of 1 get_output_using_as' of 2 //│ > true is equivalent to true //│ > false is equivalent to false //│ > 0 is equivalent to false //│ > 1 is equivalent to true //│ > 2 is equivalent to true fun get_output_through_param(value) = if value is BoolLike(b) then print("" + value + " is equivalent to " + b) else print("the input " + value + " is not a boolean") get_output_through_param of true get_output_through_param of false get_output_through_param of 0 get_output_through_param of 1 get_output_through_param of 2 //│ > true is equivalent to true //│ > false is equivalent to false //│ > 0 is equivalent to false //│ > 1 is equivalent to true //│ > 2 is equivalent to true pattern TrueLike = BoolLike as true :expect true true is TrueLike //│ = true :expect true 42 is BoolLike //│ = true :expect true 42 is TrueLike //│ = true fun observeInt(x) = print("observe " + x) x + 1 pattern LoggedInt = (Int as x) => observeInt(x) print of 42 is @compile LoggedInt //│ > true if 42 is (@compile LoggedInt) as value then print("logged value: " + value) //│ > observe 42 //│ > logged value: 43 pattern LoggedIntWithSlot(value) = (Int as value) => observeInt(value) print of 42 is @compile LoggedIntWithSlot //│ > observe 42 //│ > true if 42 is (@compile LoggedIntWithSlot) as value then print("logged slot value: " + value) //│ > observe 42 //│ > logged slot value: 43 class Term with constructor Literal(val value: Int) Infix(val op: Str, val left: Term, val right: Term) Prefix(val op: Str, val right: Term) // The following pattern generates a lot of code. pattern EvaluatedTerm = | Literal(value) => value | Infix("+", EvaluatedTerm as left, EvaluatedTerm as right) => left + right | Infix("-", EvaluatedTerm as left, EvaluatedTerm as right) => left - right | Infix("*", EvaluatedTerm as left, EvaluatedTerm as right) => left * right | Infix("/", EvaluatedTerm as left, EvaluatedTerm as right) => left / right | Prefix("-", EvaluatedTerm as operand) => -operand | Prefix("+", EvaluatedTerm as operand) => +operand // Some helper functions to create `Term`s. fun wrapLiteral(value) = if value is Int then Literal(value) else value fun (+:+) add(a, b) = Infix("+", wrapLiteral(a), wrapLiteral(b)) fun (*:*) mul(a, b) = Infix("*", wrapLiteral(a), wrapLiteral(b)) fun (-:-) sub(a, b) = Infix("-", wrapLiteral(a), wrapLiteral(b)) fun (/:/) div(a, b) = Infix("/", wrapLiteral(a), wrapLiteral(b)) Literal(1) is EvaluatedTerm(1) //│ = true 2 *:* 3 +:+ 1 is EvaluatedTerm(7) //│ = true (4 +:+ 5) *:* (6 -:- 2) is EvaluatedTerm(36) //│ = true Prefix("-", 10 +:+ 2 *:* 3) is EvaluatedTerm(-16) //│ = true (8 /:/ 2) +:+ (3 *:* 4) -:- 5 is EvaluatedTerm(11) //│ = true Infix("**", Literal(1), Literal(2)) is EvaluatedTerm //│ = false fun evaluate(term) = if term is (@compile EvaluatedTerm) as v then Some(v) else None evaluate of Literal(1) //│ = Some(1) evaluate of Literal(1) //│ = Some(1) evaluate of 2 *:* 3 +:+ 1 //│ = Some(7) evaluate of (4 +:+ 5) *:* (6 -:- 2) //│ = Some(36) evaluate of Prefix("-", 10 +:+ 2 *:* 3) //│ = Some(-16) evaluate of (8 /:/ 2) +:+ (3 *:* 4) -:- 5 //│ = Some(11) evaluate of Infix("**", Literal(1), Literal(2)) //│ = None pattern DropZ = ((:x, :y, :z)) => (x: x, y: y) (x: 1, y: 2, z: 3) is DropZ((x: 1, y: 2)) //│ = true (x: 1, y: 2, z: 3) is DropZ((x: 1, y: 3)) //│ = false pattern ShapeArea = (((:radius)) => Math.PI * radius * radius) | // Circle (((:width, :height)) => width * height) | // Rectangle (((:base, :height)) => base * height / 2) // Triangle fun area(shape) = if shape is ShapeArea(area) then print("area: " + area) else print("invalid shape") area of radius: 10 area of width: 10, height: 20 area of base: 10, height: 20 //│ > area: 314.1592653589793 //│ > area: 200 //│ > area: 100 pattern PairLike = [_, _] | Pair(_, _) [1, 2] is PairLike //│ = true [] is PairLike //│ = false [1] is PairLike //│ = false Pair(1, 2) is PairLike //│ = true pattern TransformTwice = (((Int as x) => x + 4) as y) => y * 2 2 is TransformTwice(12) //│ = true 100 is TransformTwice(0) //│ = false 17 is TransformTwice(42) //│ = true fun testTransformTwice(x) = if x is (@compile TransformTwice) as v then Some(v) else None testTransformTwice of 100 //│ = Some(208) testTransformTwice of 2 //│ = Some(12) testTransformTwice of 17 //│ = Some(42) // We can easily define predicative patterns using `as true`. pattern Positive = ((Int as x) => x > 0) as true print of -100 is Positive print of 0 is Positive print of 8080 is Positive //│ > false //│ > false //│ > true // Hmm, but how to make the pattern return the input value? Positive.unapply(100) //│ = MatchSuccess(true, null) // Just use conjunction patterns. pattern Positive = (x & (((Int as x) => x > 0) as true)) => x Positive.unapply(100) //│ = MatchSuccess(100, null) // ____ _ _ // / ___|| |_ _ __(_)_ __ __ _ // \___ \| __| '__| | '_ \ / _` | // ___) | |_| | | | | | | (_| | // |____/ \__|_| |_|_| |_|\__, | // |___/ // ============================== pattern HelloWorld = (("hello" as a) ~ " " ~ ("world" as b)) => "the user said " + a + " to the " + b "hello world" is HelloWorld //│ = true if "hello world" is HelloWorld as x then x //│ = "the user said hello to the world" ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/TransformFree.mls ================================================ :js open annotations data class Pair[A, B](first: A, second: B) pattern NumPair = Pair(Num, Num) pattern SumPair = Pair((Num as x), (Num as y)) => x + y // TLDR: This file tests the case where a compiled pattern's result is bound // with `as`, but the pattern itself does not involve any transformations. // // `toNumPair` should still compile without creating `MatchSuccess` or // `MatchFailure`: all matchers should return Boolean values, and the use site // should set the output binding `s` to the original scrutinee `t`. // // `toSumPair` is the counterexample: because it has a transform and the use // site binds the pattern's output, it still creates `MatchSuccess` and // `MatchFailure`. // :sir fun toNumPair(t) = if t is (@compile NumPair) as s then s else null // :sir fun toSumPair(t) = if t is (@compile SumPair) as s then s else null ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/UpsBugsBacklog.mls ================================================ :js open annotations // ——— ——— ——— import "../../mlscript-compile/Char.mls" pattern Input = ((Char.Whitespace ~ (Input as inp)) => inp) | ((c ~ (Input as inp)) => [c, ..inp]) | ("" => []) :fixme if "a b c" is Input as chars then chars //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded // ——— ——— ——— pattern Bin = [(1 => true) | (_ => false)] fun f(x) = if x is Bin as res then res :todo :expect [false] f([0]) //│ ═══[RUNTIME ERROR] Expected: '[false]', got: '[0]' //│ = [0] :todo :expect [true] f([1]) //│ ═══[RUNTIME ERROR] Expected: '[true]', got: '[1]' //│ = [1] // ——— ——— ——— pattern Parse = 0 let tokens //│ tokens = undefined // Works: if tokens is @compile Parse as stack do print(stack) :fixme // Should be a "name not found" if tokens is @compile Parse as stack do print(stack) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'stack' //│ ║ l.48: @compile Parse as stack do print(stack) //│ ╙── ^^^^^ // What's actually intended: if tokens is (@compile Parse) as stack do print(stack) // ——— ——— ——— ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/BadRecStackParse.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" open Stack // * Just a note that it does *not* work to try and do everything recursively; // * the parser needs an opportunity to zoom out and make progress elsewhere. pattern Ident = ("a" ..= "z") ~ (Ident | "") data class Expr with constructor Id(name: Str) Op(lhs: Expr, op: Str, rhs: Expr) fun go(stack) = // print(stack) if stack is... ((Ident as name) :: rest) then go of Id(name) :: rest ((Expr as e1) :: "+" :: (Expr as e2) :: rest) then go of Op(e1, "+", e2) :: rest ((Expr as e1) :: "+" :: rest) then go of e1 :: "+" :: go of rest ("(" :: (Expr as e) :: ")" :: rest) then go of e :: rest ("(" :: rest) then go of "(" :: go of rest _ then stack fun parse(tokens) = if go(tokens) is (Expr as res) :: Nil then Success: res toks then Failure: toks parse(Nil) //│ = {Failure: Nil} parse("x" :: Nil) //│ = {Success: Id("x")} parse("x" :: "+" :: "y" :: Nil) //│ = {Success: Op(Id("x"), "+", Id("y"))} parse("(" :: "x" :: ")" :: Nil) //│ = {Success: Id("x")} parse("a" :: "+" :: "(" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ = {Success: Op(Id("a"), "+", Op(Id("x"), "+", Id("y")))} // * Notice: wrong assoc. parse("a" :: "+" :: "(" :: "b" :: "+" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ = {Success: Op(Id("a"), "+", Op(Id("b"), "+", Op(Id("x"), "+", Id("y"))))} // * It parses the same as: parse("a" :: "+" :: "(" :: "b" :: "+" :: "(" :: "x" :: "+" :: "y" :: ")" :: ")" :: Nil) //│ = {Success: Op(Id("a"), "+", Op(Id("b"), "+", Op(Id("x"), "+", Id("y"))))} ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/BasicSeqStackParse.mls ================================================ :js // * There are currently bugs in tuple matching, // * preventing most of these definitions from working. // * See also: `ups/UpsBugsBacklog.mls` import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/Char.mls" open Iter pattern Ident = ("a" ..= "z") ~ (Ident | "") data class Expr with constructor Id(name: Str) Op(lhs: Expr, op: Str, rhs: Expr) fun go(stack, input) = if stack is [..rest, Expr as e1, "+", Expr as e2] then go([..rest, Op(e1, "+", e2)], input) [..rest, Ident as id] then go([..rest, Id(id)], input) do print("not Ident", stack) do print("not Ident??", stack is [..stack, Ident as id]) do print("Str?", stack.at(-1), stack.at(-1) is Str) do print("Str??", stack is [..stack, Str as id]) [Expr as res] and input is [] then Success: res _ and input is [tok, ..input] then go([..stack, tok], input) [] then Error: stack let Space = new RegExp of "(\\s+)" fun parse(input) = let input = input.split(Space) \filtering of (_ !== "") \filtering of (_ \every of (_ !== " ")) \toArray() go([], input) //│ Space = /(\s+)/ // FIXME: the results below seem to indicate miscompilation: parse("") //│ > not Ident [] //│ > not Ident?? false //│ > Str? () false //│ > Str?? false //│ = {Error: []} parse("x") //│ > not Ident [] //│ > not Ident?? false //│ > Str? () false //│ > Str?? false //│ > not Ident [Id("x")] //│ > not Ident?? false //│ > Str? Id("x") false //│ > Str?? false //│ = {Success: Id("x")} parse("x y") //│ > not Ident [] //│ > not Ident?? false //│ > Str? () false //│ > Str?? false //│ > not Ident [Id("x")] //│ > not Ident?? false //│ > Str? Id("x") false //│ > Str?? false //│ > not Ident [Id("x"), Id("y")] //│ > not Ident?? false //│ > Str? Id("y") false //│ > Str?? false //│ = {Error: [Id("x"), Id("y")]} parse("x y z") //│ > not Ident [] //│ > not Ident?? false //│ > Str? () false //│ > Str?? false //│ > not Ident [Id("x")] //│ > not Ident?? false //│ > Str? Id("x") false //│ > Str?? false //│ > not Ident [Id("x"), Id("y")] //│ > not Ident?? false //│ > Str? Id("y") false //│ > Str?? false //│ > not Ident [Id("x"), Id("y"), Id("z")] //│ > not Ident?? false //│ > Str? Id("z") false //│ > Str?? false //│ = {Error: [Id("x"), Id("y"), Id("z")]} go([], ["x", "y", "z"]) //│ > not Ident [] //│ > not Ident?? false //│ > Str? () false //│ > Str?? false //│ > not Ident [Id("x")] //│ > not Ident?? false //│ > Str? Id("x") false //│ > Str?? false //│ > not Ident [Id("x"), Id("y")] //│ > not Ident?? false //│ > Str? Id("y") false //│ > Str?? false //│ > not Ident [Id("x"), Id("y"), Id("z")] //│ > not Ident?? false //│ > Str? Id("z") false //│ > Str?? false //│ = {Error: [Id("x"), Id("y"), Id("z")]} go([Id("x"), Id("y"), "z"], []) //│ > not Ident [Id("x"), Id("y"), Id("z")] //│ > not Ident?? false //│ > Str? Id("z") false //│ > Str?? false //│ = {Error: [Id("x"), Id("y"), Id("z")]} parse("x + y") //│ > not Ident [] //│ > not Ident?? false //│ > Str? () false //│ > Str?? false //│ > not Ident [Id("x")] //│ > not Ident?? false //│ > Str? Id("x") false //│ > Str?? false //│ > not Ident [Id("x"), +] //│ > not Ident?? false //│ > Str? + true //│ > Str?? true //│ > not Ident [Op(Id("y"), "+", Id("x"))] //│ > not Ident?? false //│ > Str? Op(Id("y"), "+", Id("x")) false //│ > Str?? false //│ = {Success: Op(Id("y"), "+", Id("x"))} ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/BasicStackParse.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" open Stack pattern Ident = ("a" ..= "z") ~ (Ident | "") data class Expr with constructor Id(name: Str) Op(lhs: Expr, op: Str, rhs: Expr) pattern Parse = | ((Ident as name) :: rest) => Id(name) :: rest | ((Expr as e1) :: "+" :: (Expr as e2) :: rest) => Op(e1, "+", e2) :: rest | ((Expr as e1) :: "+" :: (Parse as rest)) => e1 :: "+" :: rest | ("(" :: (Expr as e) :: ")" :: rest) => e :: rest | ("(" :: (Parse as rest)) => "(" :: rest fun parse(tokens) = while tokens is Parse as stack do set tokens = stack print of ... if tokens is (Expr as e) :: Nil then ["Success:", e] toks then ["Failure: can't parse:", toks] parse(Nil) //│ > Failure: can't parse: Nil parse("x" :: Nil) //│ > Success: Id("x") let input = "x" :: "+" :: "y" :: Nil //│ input = Cons("x", Cons("+", Cons("y", Nil))) parse(input) //│ > Success: Op(Id("x"), "+", Id("y")) if input is Parse as rest then rest //│ = Cons(Id("x"), Cons("+", Cons("y", Nil))) parse("(" :: "x" :: ")" :: Nil) //│ > Success: Id("x") parse("a" :: "+" :: "(" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ > Success: Op(Id("a"), "+", Op(Id("x"), "+", Id("y"))) parse("a" :: "+" :: "(" :: "b" :: "+" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ > Success: Op(Id("a"), "+", Op(Op(Id("b"), "+", Id("x")), "+", Id("y"))) parse("a" :: "+" :: "(" :: "b" :: "+" :: "(" :: "x" :: "+" :: "y" :: ")" :: ")" :: Nil) //│ > Success: Op(Id("a"), "+", Op(Id("b"), "+", Op(Id("x"), "+", Id("y")))) // ——— // TODO improve: confusing errors for wrong code :e pattern Parse = ( ((Str as name) :: rest) => Id(name) :: rest ((Expr as e) :: "+" :: (Expr as e) :: rest) => Op(e, "+", e) :: rest ) //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. //│ ║ l.69: ((Str as name) :: rest) => Id(name) :: rest //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. //│ ║ l.70: ((Expr as e) :: "+" :: (Expr as e) :: rest) => Op(e, "+", e) :: rest //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/Computation.mls ================================================ :js open annotations data class Celsius(value: Num) data class Fahrenheit(value: Num) pattern ToFahrenheit = | (Celsius(c)) => Fahrenheit((c * 9 / 5) + 32) | (Fahrenheit as f) => f pattern ToCelsius = | (Fahrenheit(f)) => Celsius((f - 32) * 5 / 9) | (Celsius as c) => c Celsius(0) is ToFahrenheit(Fahrenheit(32)) //│ = true Fahrenheit(212) is ToCelsius(Celsius(100)) //│ = true Celsius(100) is ToFahrenheit(Fahrenheit(212)) //│ = true Fahrenheit(32) is ToCelsius(Celsius(0)) //│ = true Celsius(-40) is ToFahrenheit(Fahrenheit(-40)) //│ = true Fahrenheit(-40) is ToCelsius(Celsius(-40)) //│ = true fun toFahrenheit(c) = if c is (@compile ToFahrenheit) as output then output else "What's this?" toFahrenheit of Celsius(100) //│ = Fahrenheit(212) toFahrenheit of Fahrenheit(100) //│ = Fahrenheit(100) toFahrenheit of Celsius(-40) //│ = Fahrenheit(-40) toFahrenheit of "Cheers!" //│ = "What's this?" fun toCelsius(f) = if f is (@compile ToCelsius) as output then output else "What's this?" toCelsius of Fahrenheit(100) //│ = Celsius(37.77777777777778) toCelsius of Celsius(100) //│ = Celsius(100) toCelsius of "Cheers!" //│ = "What's this?" pattern HourAM = (0 => 12) | (1 ..= 11) pattern HourPM = 12 | (((13 ..= 23) as hour) => hour - 12) pattern Minute = ((0 ..= 59) as minute) => minute.toString().padStart(2, "0") // Convert 24-hour time format to 12-hour format with AM/PM pattern Time12Hour = (( hour: ((HourAM | HourPM) as hour) & (((HourAM => "AM") | (HourPM => "PM")) as flag), minute: Minute as minute )) => hour + ":" + minute + " " + flag fun formatTime(ps) = if ps is Time12Hour as time then time print of "I had my breakfast at", formatTime of hour: 7, minute: 45 print of "Let's have lunch at", formatTime of hour: 12, minute: 30 print of "I plan to have dinner at", formatTime of hour: 18, minute: 45 print of "I should go to bed before", formatTime of hour: 23, minute: 0 //│ > I had my breakfast at 7:45 AM //│ > Let's have lunch at 12:30 PM //│ > I plan to have dinner at 6:45 PM //│ > I should go to bed before 11:00 PM // This makes use of the chain pattern. It generates 29,232 lines of code! pattern Time12Hour = (( hour: (( | (0 ..= 11) => "AM" | (12 ..= 23) => "PM" ) & ( | 0 => 12 | 1 ..= 11 | 12 | ((13 ..= 23) as h) => (h - 12) )) as [hour, flag], minute: Minute as minute )) => hour + ":" + minute + " " + flag fun formatTime(ps) = if ps is Time12Hour as time then time print of "I had my breakfast at", formatTime of hour: 7, minute: 45 print of "Let's have lunch at", formatTime of hour: 12, minute: 30 print of "I plan to have dinner at", formatTime of hour: 18, minute: 45 print of "I should go to bed before", formatTime of hour: 23, minute: 0 //│ > I had my breakfast at AM:45 7 //│ > Let's have lunch at PM:30 12 //│ > I plan to have dinner at PM:45 6 //│ > I should go to bed before PM:00 11 pattern Channel = 0 ..= 255 // Convert a color in RGB format to grayscale pattern Grayscale = ((r: Channel as r, g: Channel as g, b: Channel as b)) => Math.round(r * 0.299 + g * 0.587 + b * 0.114) fun grayscale(rgb) = if rgb is Grayscale as g then g else -1 grayscale of r: 0, g: 0, b: 0 //│ = 0 grayscale of r: 1, g: 1, b: 1 //│ = 1 grayscale of r: 255, g: 255, b: 255 //│ = 255 grayscale of r: 256, g: 255, b: 255 //│ = -1 ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/DnfCnf.mls ================================================ :js open annotations class Formula with constructor Var(val name: Str) Pred(val name: Str, val arg: Formula) And(val left: Formula, val right: Formula) Or(val left: Formula, val right: Formula) pattern Dnf = Or(Dnf, Dnf) | Atoms pattern Atoms = And(Atoms, Atoms) | Atom pattern Atom = Pred | Var fun isDnf(x) = x is @compile Dnf let f1 = Var("x") let f2 = Pred("P", Var("y")) //│ f1 = Var("x") //│ f2 = Pred("P", Var("y")) f1 is Dnf //│ = true isDnf of f1 //│ = true f2 is Dnf //│ = true isDnf of f2 //│ = true let f3 = And(f1, f2) //│ f3 = And(Var("x"), Pred("P", Var("y"))) f3 is Dnf //│ = true isDnf of f3 //│ = true let f4 = Or(f3, Pred("Q", Var("z"))) //│ f4 = Or(And(Var("x"), Pred("P", Var("y"))), Pred("Q", Var("z"))) f4 is Dnf //│ = true isDnf of f4 //│ = true let f5 = Or(And(Pred("A", Var("a")), Pred("B", Var("b"))), Pred("C", Var("c"))) //│ f5 = Or(And(Pred("A", Var("a")), Pred("B", Var("b"))), Pred("C", Var("c"))) f5 is Dnf //│ = true isDnf of f5 //│ = true let cnf1 = And(Or(Var("x"), Var("y")), Pred("P", Var("z"))) //│ cnf1 = And(Or(Var("x"), Var("y")), Pred("P", Var("z"))) let cnf2 = And(Or(Var("a"), Var("b")), Or(Pred("P", Var("c")), Var("d"))) //│ cnf2 = And(Or(Var("a"), Var("b")), Or(Pred("P", Var("c")), Var("d"))) let cnf3 = And(And(Or(Var("x"), Pred("Q", Var("y"))), Or(Var("z"), Var("w"))), Or(Pred("R", Var("u")), Var("v"))) //│ cnf3 = And( //│ And(Or(Var("x"), Pred("Q", Var("y"))), Or(Var("z"), Var("w"))), //│ Or(Pred("R", Var("u")), Var("v")) //│ ) let cnf4 = And(Or(And(Var("m"), Var("n")), Var("o")), Or(Pred("S", Var("p")), And(Var("q"), Var("r")))) //│ cnf4 = And( //│ Or(And(Var("m"), Var("n")), Var("o")), //│ Or(Pred("S", Var("p")), And(Var("q"), Var("r"))) //│ ) let cnf5 = And(Or(Var("a"), Or(Var("b"), Var("c"))), And(Or(Pred("T", Var("d")), Var("e")), Or(Var("f"), Var("g")))) //│ cnf5 = And( //│ Or(Var("a"), Or(Var("b"), Var("c"))), //│ And(Or(Pred("T", Var("d")), Var("e")), Or(Var("f"), Var("g"))) //│ ) let cnf6 = And(Or(Var("x"), And(Var("y"), Or(Var("z"), Var("w")))), Or(Pred("U", Var("t")), Var("s"))) //│ cnf6 = And( //│ Or(Var("x"), And(Var("y"), Or(Var("z"), Var("w")))), //│ Or(Pred("U", Var("t")), Var("s")) //│ ) print of cnf1 is Dnf print of cnf2 is Dnf print of cnf3 is Dnf print of cnf4 is Dnf print of cnf5 is Dnf print of cnf6 is Dnf //│ > false //│ > false //│ > false //│ > false //│ > false //│ > false pattern Dnf'(pattern P) = Or(Dnf'(P), Dnf'(P)) | Atoms'(P) pattern Atoms'(pattern P) = And(Atoms'(P), Atoms'(P)) | Atom'(P) pattern Atom'(pattern P) = Pred(_, P) | Var pattern DeepDnf = Dnf'(DeepDnf) fun isDeepDnf(f) = f is @compile DeepDnf print of isDeepDnf of f1 print of isDeepDnf of f2 print of isDeepDnf of f3 print of isDeepDnf of f4 print of isDeepDnf of f5 //│ > true //│ > true //│ > true //│ > true //│ > true print of isDeepDnf of cnf1 print of isDeepDnf of cnf2 print of isDeepDnf of cnf3 print of isDeepDnf of cnf4 print of isDeepDnf of cnf5 print of isDeepDnf of cnf6 //│ > false //│ > false //│ > false //│ > false //│ > false //│ > false pattern Cnf'(pattern P) = And(Cnf'(P), Cnf'(P)) pattern DnfOrCnf = Dnf'(DnfOrCnf) | Cnf'(DnfOrCnf) ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/DoubleOrSum.mls ================================================ :js open annotations import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/Option.mls" open Stack open Option { Some, None } fun list(...elements) = elements Iter.toStack() // This is a helper function to print the addition of two numbers. fun (++) addWithPrint(x, y) = let result = x + y print of "" + x + " + " + y + " = " + result result pattern DoubleList = Nil | (Int :: Int :: DoubleList) pattern SumList = (Nil => 0) | (((Int as hd) :: (SumList as tl)) => hd ++ tl) Nil is SumList(0) //│ = true let _1234 = list(1, 2, 3, 4) let _1234567 = list(1, 2, 3, 4, 5, 6, 7) //│ _1234 = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) //│ _1234567 = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(7, Nil))))))) _1234 is SumList(10) //│ > 4 + 0 = 4 //│ > 3 + 4 = 7 //│ > 2 + 7 = 9 //│ > 1 + 9 = 10 //│ = true _1234567 is SumList(28) //│ > 7 + 0 = 7 //│ > 6 + 7 = 13 //│ > 5 + 13 = 18 //│ > 4 + 18 = 22 //│ > 3 + 22 = 25 //│ > 2 + 25 = 27 //│ > 1 + 27 = 28 //│ = true _1234567 is SumList(42) //│ > 7 + 0 = 7 //│ > 6 + 7 = 13 //│ > 5 + 13 = 18 //│ > 4 + 18 = 22 //│ > 3 + 22 = 25 //│ > 2 + 25 = 27 //│ > 1 + 27 = 28 //│ = false _1234 is DoubleList //│ = true _1234567 is DoubleList //│ = false fun sum(xs) = if xs is SumList as output then Some(output) else None sum of _1234 //│ > 4 + 0 = 4 //│ > 3 + 4 = 7 //│ > 2 + 7 = 9 //│ > 1 + 9 = 10 //│ = Some(10) sum of _1234567 //│ > 7 + 0 = 7 //│ > 6 + 7 = 13 //│ > 5 + 13 = 18 //│ > 4 + 18 = 22 //│ > 3 + 22 = 25 //│ > 2 + 25 = 27 //│ > 1 + 27 = 28 //│ = Some(28) sum of Nil //│ = Some(0) // Try to make a disjunction of two patterns. pattern SumOrDouble = SumList | DoubleList fun isSumOrDouble__naive(xs) = if xs is SumOrDouble as output then output else "noop" isSumOrDouble__naive of _1234 //│ > 4 + 0 = 4 //│ > 3 + 4 = 7 //│ > 2 + 7 = 9 //│ > 1 + 9 = 10 //│ = 10 isSumOrDouble__naive of _1234567 //│ > 7 + 0 = 7 //│ > 6 + 7 = 13 //│ > 5 + 13 = 18 //│ > 4 + 18 = 22 //│ > 3 + 22 = 25 //│ > 2 + 25 = 27 //│ > 1 + 27 = 28 //│ = 28 isSumOrDouble__naive of Nil //│ = 0 fun isSumOrDouble__compiled(xs) = if xs is (@compile SumOrDouble) as output then output else "noop" isSumOrDouble__compiled of _1234 //│ > 4 + 0 = 4 //│ > 3 + 4 = 7 //│ > 2 + 7 = 9 //│ > 1 + 9 = 10 //│ = 10 isSumOrDouble__compiled of _1234567 //│ > 7 + 0 = 7 //│ > 6 + 7 = 13 //│ > 5 + 13 = 18 //│ > 4 + 18 = 22 //│ > 3 + 22 = 25 //│ > 2 + 25 = 27 //│ > 1 + 27 = 28 //│ = 28 isSumOrDouble__compiled of Nil //│ = 0 // Swap the order of two patterns. pattern DoubleOrSum = DoubleList | SumList fun isDoubleOrSum__naive(xs) = if xs is DoubleOrSum as output then output else "noop" isDoubleOrSum__naive of _1234 //│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) isDoubleOrSum__naive of list(1, 2, 3) //│ > 3 + 0 = 3 //│ > 2 + 3 = 5 //│ > 1 + 5 = 6 //│ = 6 fun isDoubleOrSum__compiled(xs) = if xs is (@compile DoubleOrSum) as output then output else "noop" isDoubleOrSum__compiled of _1234 //│ > 4 + 0 = 4 //│ > 3 + 4 = 7 //│ > 2 + 7 = 9 //│ = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) isDoubleOrSum__compiled of _1234567 //│ > 7 + 0 = 7 //│ > 6 + 7 = 13 //│ > 5 + 13 = 18 //│ > 4 + 18 = 22 //│ > 3 + 22 = 25 //│ > 2 + 25 = 27 //│ > 1 + 27 = 28 //│ = 28 ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/DoubleTripleList.mls ================================================ :js open annotations import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" open Stack fun list(...elements) = elements Iter.toStack() // The example of lists whose length is a multiple of two. // Note that the parentheses are necessary. pattern DoubleList = Nil | (Int :: Int :: DoubleList) list(1, 2, 3, 4) is DoubleList //│ = true list(1, 2, 3) is DoubleList //│ = false Nil is DoubleList //│ = true fun isDoubleList(xs) = xs is @compile DoubleList isDoubleList of list(1, 2, 3, 4) //│ = true isDoubleList of list(1, 2, 3) //│ = false isDoubleList of Nil //│ = true // The example of lists whose length is a multiple of three. // Note that the parentheses are necessary. pattern TripleList = Nil | (Int :: Int :: Int :: TripleList) list(1, 2, 3, 4, 5, 6) is TripleList //│ = true list(1, 2, 3) is TripleList //│ = true list(1, 2, 3, 4) is TripleList //│ = false Nil is TripleList //│ = true fun isTripleList(xs) = xs is @compile TripleList isTripleList of list(1, 2, 3, 4, 5, 6) //│ = true isTripleList of list(1, 2, 3) //│ = true isTripleList of list(1, 2, 3, 4) //│ = false isTripleList of Nil //│ = true pattern DoubleTripleList = DoubleList | TripleList // Make a `Cons` object and print a message whenever the `head` or `tail` // property is accessed. let doLog = false fun (::) makeObservableCons(x, xs) = let cons = new mut Cons(x, xs) let repr = "[" + (cons Iter.fromStack() Iter.joined of ", ") + "]" Object.defineProperty of cons "head" enumerable: true configurable: false get: () => if doLog do print("access the `head` of " + repr) x Object.defineProperty of cons "tail" enumerable: true configurable: false get: () => if doLog do print("access the `tail` of " + repr) xs cons //│ doLog = false fun withLog(f) = set doLog = true val result = f() set doLog = false result let testList = 1 :: 2 :: Nil //│ testList = Cons(1, Cons(2, Nil)) withLog of () => testList.head testList.tail () //│ > access the `head` of [1, 2] //│ > access the `tail` of [1, 2] fun list(...elements) = elements Iter.rightFolded of Nil, (::) fun isDoubleTripleList__compiled(xs) = withLog of () => xs is @compile DoubleTripleList isDoubleTripleList__compiled of list(1, 2, 3) //│ > access the `head` of [1, 2, 3] //│ > access the `tail` of [1, 2, 3] //│ > access the `head` of [2, 3] //│ > access the `tail` of [2, 3] //│ > access the `head` of [3] //│ > access the `tail` of [3] //│ = true isDoubleTripleList__compiled of list(1, 2, 3, 4) //│ > access the `head` of [1, 2, 3, 4] //│ > access the `tail` of [1, 2, 3, 4] //│ > access the `head` of [2, 3, 4] //│ > access the `tail` of [2, 3, 4] //│ > access the `head` of [3, 4] //│ > access the `tail` of [3, 4] //│ > access the `head` of [4] //│ > access the `tail` of [4] //│ = true isDoubleTripleList__compiled of list(1, 2, 3, 4, 5) //│ > access the `head` of [1, 2, 3, 4, 5] //│ > access the `tail` of [1, 2, 3, 4, 5] //│ > access the `head` of [2, 3, 4, 5] //│ > access the `tail` of [2, 3, 4, 5] //│ > access the `head` of [3, 4, 5] //│ > access the `tail` of [3, 4, 5] //│ > access the `head` of [4, 5] //│ > access the `tail` of [4, 5] //│ > access the `head` of [5] //│ > access the `tail` of [5] //│ = false isDoubleTripleList__compiled of list(1, 2, 3, 4, 5, 6, 7, 8, 9) //│ > access the `head` of [1, 2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [1, 2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [4, 5, 6, 7, 8, 9] //│ > access the `tail` of [4, 5, 6, 7, 8, 9] //│ > access the `head` of [5, 6, 7, 8, 9] //│ > access the `tail` of [5, 6, 7, 8, 9] //│ > access the `head` of [6, 7, 8, 9] //│ > access the `tail` of [6, 7, 8, 9] //│ > access the `head` of [7, 8, 9] //│ > access the `tail` of [7, 8, 9] //│ > access the `head` of [8, 9] //│ > access the `tail` of [8, 9] //│ > access the `head` of [9] //│ > access the `tail` of [9] //│ = true isDoubleTripleList__compiled of Nil //│ = true fun isDoubleTripleList__naive(xs) = set doLog = true let result = xs is DoubleTripleList set doLog = false result // It does not backtracking because the `DoubleList` is tested first. isDoubleTripleList__naive of list(1, 2, 3, 4) //│ > access the `head` of [1, 2, 3, 4] //│ > access the `tail` of [1, 2, 3, 4] //│ > access the `head` of [2, 3, 4] //│ > access the `tail` of [2, 3, 4] //│ > access the `head` of [3, 4] //│ > access the `tail` of [3, 4] //│ > access the `head` of [4] //│ > access the `tail` of [4] //│ = true // It does backtracking when `DoubleList` does not match. isDoubleTripleList__naive of list(1, 2, 3) //│ > access the `head` of [1, 2, 3] //│ > access the `tail` of [1, 2, 3] //│ > access the `head` of [2, 3] //│ > access the `tail` of [2, 3] //│ > access the `head` of [3] //│ > access the `tail` of [3] //│ > access the `head` of [1, 2, 3] //│ > access the `tail` of [1, 2, 3] //│ > access the `head` of [2, 3] //│ > access the `tail` of [2, 3] //│ > access the `head` of [3] //│ > access the `tail` of [3] //│ = true isDoubleTripleList__naive of list(1, 2, 3, 4, 5, 6, 7, 8, 9) //│ > access the `head` of [1, 2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [1, 2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [4, 5, 6, 7, 8, 9] //│ > access the `tail` of [4, 5, 6, 7, 8, 9] //│ > access the `head` of [5, 6, 7, 8, 9] //│ > access the `tail` of [5, 6, 7, 8, 9] //│ > access the `head` of [6, 7, 8, 9] //│ > access the `tail` of [6, 7, 8, 9] //│ > access the `head` of [7, 8, 9] //│ > access the `tail` of [7, 8, 9] //│ > access the `head` of [8, 9] //│ > access the `tail` of [8, 9] //│ > access the `head` of [9] //│ > access the `tail` of [9] //│ > access the `head` of [1, 2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [1, 2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [2, 3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [3, 4, 5, 6, 7, 8, 9] //│ > access the `tail` of [3, 4, 5, 6, 7, 8, 9] //│ > access the `head` of [4, 5, 6, 7, 8, 9] //│ > access the `tail` of [4, 5, 6, 7, 8, 9] //│ > access the `head` of [5, 6, 7, 8, 9] //│ > access the `tail` of [5, 6, 7, 8, 9] //│ > access the `head` of [6, 7, 8, 9] //│ > access the `tail` of [6, 7, 8, 9] //│ > access the `head` of [7, 8, 9] //│ > access the `tail` of [7, 8, 9] //│ > access the `head` of [8, 9] //│ > access the `tail` of [8, 9] //│ > access the `head` of [9] //│ > access the `tail` of [9] //│ = true pattern DoubleAndTripleList = DoubleList & TripleList fun isDoubleAndTriple__naive(xs) = withLog of () => xs is DoubleAndTripleList isDoubleAndTriple__naive of list(8, 9) //│ > access the `head` of [8, 9] //│ > access the `tail` of [8, 9] //│ > access the `head` of [9] //│ > access the `tail` of [9] //│ > access the `head` of [8, 9] //│ > access the `tail` of [8, 9] //│ > access the `head` of [9] //│ > access the `tail` of [9] //│ = false isDoubleAndTriple__naive of list(1, 2, 3) //│ > access the `head` of [1, 2, 3] //│ > access the `tail` of [1, 2, 3] //│ > access the `head` of [2, 3] //│ > access the `tail` of [2, 3] //│ > access the `head` of [3] //│ > access the `tail` of [3] //│ = false isDoubleAndTriple__naive of list(4, 3, 2, 1) //│ > access the `head` of [4, 3, 2, 1] //│ > access the `tail` of [4, 3, 2, 1] //│ > access the `head` of [3, 2, 1] //│ > access the `tail` of [3, 2, 1] //│ > access the `head` of [2, 1] //│ > access the `tail` of [2, 1] //│ > access the `head` of [1] //│ > access the `tail` of [1] //│ > access the `head` of [4, 3, 2, 1] //│ > access the `tail` of [4, 3, 2, 1] //│ > access the `head` of [3, 2, 1] //│ > access the `tail` of [3, 2, 1] //│ > access the `head` of [2, 1] //│ > access the `tail` of [2, 1] //│ > access the `head` of [1] //│ > access the `tail` of [1] //│ = false isDoubleAndTriple__naive of list(1, 2, 3, 4, 5, 6) //│ > access the `head` of [1, 2, 3, 4, 5, 6] //│ > access the `tail` of [1, 2, 3, 4, 5, 6] //│ > access the `head` of [2, 3, 4, 5, 6] //│ > access the `tail` of [2, 3, 4, 5, 6] //│ > access the `head` of [3, 4, 5, 6] //│ > access the `tail` of [3, 4, 5, 6] //│ > access the `head` of [4, 5, 6] //│ > access the `tail` of [4, 5, 6] //│ > access the `head` of [5, 6] //│ > access the `tail` of [5, 6] //│ > access the `head` of [6] //│ > access the `tail` of [6] //│ > access the `head` of [1, 2, 3, 4, 5, 6] //│ > access the `tail` of [1, 2, 3, 4, 5, 6] //│ > access the `head` of [2, 3, 4, 5, 6] //│ > access the `tail` of [2, 3, 4, 5, 6] //│ > access the `head` of [3, 4, 5, 6] //│ > access the `tail` of [3, 4, 5, 6] //│ > access the `head` of [4, 5, 6] //│ > access the `tail` of [4, 5, 6] //│ > access the `head` of [5, 6] //│ > access the `tail` of [5, 6] //│ > access the `head` of [6] //│ > access the `tail` of [6] //│ = true fun isDoubleAndTriple__compiled(xs) = withLog of () => xs is @compile DoubleAndTripleList isDoubleAndTriple__compiled of list(8, 9) //│ > access the `head` of [8, 9] //│ > access the `tail` of [8, 9] //│ > access the `head` of [9] //│ > access the `tail` of [9] //│ = false isDoubleAndTriple__compiled of list(1, 2, 3) //│ > access the `head` of [1, 2, 3] //│ > access the `tail` of [1, 2, 3] //│ > access the `head` of [2, 3] //│ > access the `tail` of [2, 3] //│ > access the `head` of [3] //│ > access the `tail` of [3] //│ = false isDoubleAndTriple__compiled of list(4, 3, 2, 1) //│ > access the `head` of [4, 3, 2, 1] //│ > access the `tail` of [4, 3, 2, 1] //│ > access the `head` of [3, 2, 1] //│ > access the `tail` of [3, 2, 1] //│ > access the `head` of [2, 1] //│ > access the `tail` of [2, 1] //│ > access the `head` of [1] //│ > access the `tail` of [1] //│ = false isDoubleAndTriple__compiled of list(1, 2, 3, 4, 5, 6) //│ > access the `head` of [1, 2, 3, 4, 5, 6] //│ > access the `tail` of [1, 2, 3, 4, 5, 6] //│ > access the `head` of [2, 3, 4, 5, 6] //│ > access the `tail` of [2, 3, 4, 5, 6] //│ > access the `head` of [3, 4, 5, 6] //│ > access the `tail` of [3, 4, 5, 6] //│ > access the `head` of [4, 5, 6] //│ > access the `tail` of [4, 5, 6] //│ > access the `head` of [5, 6] //│ > access the `tail` of [5, 6] //│ > access the `head` of [6] //│ > access the `tail` of [6] //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/EvaluationContext.mls ================================================ :js // This is an interesting experiment in evaluation context. // Author: Luyu Cheng import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/MutMap.mls" open annotations open Option { Some, None } data class Term with constructor Var(name: Str) App(lhs: Term, rhs: Term) Abs(lhs: Var, rhs: Term) // A cached set of free variables let _freeVars = None // Compute free variables. Retrieve the cache if it exists. fun freeVars = if _freeVars is Some(fv) then fv None then let fv = if this is Var(name) then new Set([name]) App(lhs, rhs) then lhs.freeVars.union(rhs.freeVars) Abs(name, body) then body.freeVars.difference(new Set([name])) set _freeVars = Some(fv) fv // Keep increasing the index until the based name with the index is not a free // variable in this term. fun freshName(baseName) = let freeVars = this.freeVars nameIndex = 1 if freeVars.has(baseName) is false then baseName else... while (baseName + "_" + nameIndex) is currentName and freeVars.has(currentName) then set nameIndex += 1 else return currentName fun freshVar(baseName) = Var of freshName(baseName) fun equals(that) = if this is Var(name) and that is Var(name') then name === name' App(lhs, rhs) and that is App(lhs', rhs') then lhs.equals(lhs') && rhs.equals(rhs') Abs(Var(name), body) and that is Abs(Var(name'), body') then name === name' && body.equals(body') else false // Pretty-print the term. fun show = if this is Var(name) then name App(Abs as lhs, rhs) then "(" + lhs.show + ") " + rhs.show App(lhs, App as rhs) then lhs.show + " (" + rhs.show + ")" App(lhs, rhs) then lhs.show + " " + rhs.show Abs(Var(name), body) then "λ" + name + ". " + body.show Var("a").freshVar("a") //│ = Var("a_1") App(Var("a"), Var("a_1")).freshVar("a") //│ = Var("a_2") Var("a").freshVar("b") //│ = Var("b") fun (<~>) alphaEquivalent(lhs, rhs) = let varMap = MutMap.empty pattern Var' = Var(n) => varMap |> MutMap.get(n) |. Option.getOrElse(n) fun go(lhs, rhs) = if lhs is Var(n) and rhs is Var' as n' then n === n' App(l, r) and r is App(l', r') then go(l, l') and go(r, r') Abs(Var(n), b) and rhs is Abs(Var(n'), b') then let prev = varMap |> MutMap.get(n) varMap |> MutMap.insert(n', n) let equal = go(b, b') varMap |> if prev is Some(n'') then MutMap.insert(n', n'') None then MutMap.delete(n') equal else false go(lhs, rhs) :silent // A few frequently used terms. let id_x = Abs(Var("x"), Var("x")) let id_y = Abs(Var("y"), Var("y")) let id_a = Abs(Var("a"), Var("a")) let id_b = Abs(Var("b"), Var("b")) // λf. λx. x let zero = Abs(Var("f"), Abs(Var("x"), Var("x"))) // λn. λf. λx. f (n f x) let succ = Abs(Var("n"), Abs(Var("f"), Abs(Var("x"), App(Var("f"), App(App(Var("n"), Var("f")), Var("x")))))) // λm. λn. λf. λx. m f (n f x) let plus = Abs(Var("m"), Abs(Var("n"), Abs(Var("f"), Abs(Var("x"), App(App(Var("m"), Var("f")), App(App(Var("n"), Var("f")), Var("x"))))))) // λm. λn. λf. m (n f) let multiply = Abs(Var("m"), Abs(Var("n"), Abs(Var("f"), App(Var("m"), App(Var("n"), Var("f")))))) // λx. λy. x let true_ = Abs(Var("x"), Abs(Var("y"), Var("x"))) let false_ = Abs(Var("x"), Abs(Var("y"), Var("y"))) // λb. λx. λy. b y x let not_ = Abs(Var("b"), Abs(Var("x"), Abs(Var("y"), App(App(Var("b"), Var("y")), Var("x"))))) // λp. λq. p q p let and_ = Abs(Var("p"), Abs(Var("q"), App(App(Var("p"), Var("q")), Var("p")))) // λp. λq. p p q let or_ = Abs(Var("p"), Abs(Var("q"), App(App(Var("p"), Var("p")), Var("q")))) print of id_x.show print of id_y.show print of id_a.show print of id_b.show print of zero.show print of succ.show print of plus.show print of multiply.show print of true_.show print of false_.show print of not_.show print of and_.show print of or_.show //│ > λx. x //│ > λy. y //│ > λa. a //│ > λb. b //│ > λf. λx. x //│ > λn. λf. λx. f (n f x) //│ > λm. λn. λf. λx. m f (n f x) //│ > λm. λn. λf. m (n f) //│ > λx. λy. x //│ > λx. λy. y //│ > λb. λx. λy. b y x //│ > λp. λq. p q p //│ > λp. λq. p p q id_x <~> id_y //│ = true id_x <~> id_a //│ = true id_x <~> succ //│ = false zero <~> false_ //│ = true fun subst(term, name, target) = if term is Var(name') and name === name' then target else term Abs(Var(name'), body) and name === name' then term let target' = if target.freeVars.has(name') then subst(target, name', target.freshVar(name')) else target else Abs(Var(name'), subst(body, name, target')) App(lhs, rhs) then App(subst(lhs, name, target), subst(rhs, name, target)) :expect Abs(Var("a"), Var("a")) subst(Var("c"), "c", id_a) //│ = Abs(Var("a"), Var("a")) :expect Var("a") subst(Var("a"), "b", id_a) //│ = Var("a") :expect Abs(Var("b"), Var("b")) subst(Var("b"), "b", id_b) //│ = Abs(Var("b"), Var("b")) :expect App(Abs(Var("x"), Var("x")), Var("y")) subst(App(Var("x"), Var("y")), "x", id_x) //│ = App(Abs(Var("x"), Var("x")), Var("y")) :expect Abs(Var("y"), Abs(Var("x"), Var("x"))) subst(Abs(Var("y"), Var("x")), "x", id_x) //│ = Abs(Var("y"), Abs(Var("x"), Var("x"))) :expect Abs(Var("x"), Var("x")) subst(Abs(Var("x"), Var("x")), "x", id_y) //│ = Abs(Var("x"), Var("x")) :expect App(Var("z"), Var("z")) subst(App(Var("x"), Var("x")), "x", Var("z")) //│ = App(Var("z"), Var("z")) subst(Abs(Var("a"), App(Var("b"), Var("a"))), "b", Var("a")) //│ = Abs(Var("a"), App(Var("a_1"), Var("a"))) pattern Value = Abs object ByValue with // This pattern matches a beta redex where the argument is a value and // performs the substitution. pattern Redex = App(Abs(Var(x), m), Value as n) => subst(m, x, n) // Call-by-value evaluation contexts // // E ::= □ // | E t // | v E // // It matches any terms that can take at least one step and extracts the next // redex that we should make a progress and a function to reconstruct the term. pattern Context(pattern Hole) = ( (Hole as hole) => [hole, id] ) | ( App(Abs as lhs, Context(Hole) as [rhs, remake]) => [rhs, App(lhs, _)] ) | ( App(Context(Hole) as [lhs, remake], Term as rhs) => [lhs, lhs' => App(remake(lhs'), rhs)] ) fun redex(term) = if term is ByValue.Redex as term' then term' redex of App(id_x, id_y) //│ = Abs(Var("y"), Var("y")) :re redex of id_x //│ ═══[RUNTIME ERROR] Error: match error :re redex of Var("a") //│ ═══[RUNTIME ERROR] Error: match error fun step(term) = if term is ByValue.Context(ByValue.Redex) as [subterm, remake] then remake(subterm) :re step of Var("x") //│ ═══[RUNTIME ERROR] Error: match error :re step of App(App(Var("a"), Var("b")), Var("c")) //│ ═══[RUNTIME ERROR] Error: match error :re step of id_x //│ ═══[RUNTIME ERROR] Error: match error :re step of App(App(id_x, Var("b")), Var("c")) //│ ═══[RUNTIME ERROR] Error: match error :expect "(λb. b) λx. x" step(App(App(id_a, id_b), id_x)).show //│ = "(λb. b) λx. x" :expect "λy. y" step(App(id_x, id_y)).show //│ = "λy. y" fun evalByValue(term) = while term is ByValue.Context(ByValue.Redex) as [subterm, remake] then set term = remake(subterm) term evalByValue(App(id_x, App(id_x, App(id_x, App(id_x, id_y))))).show //│ = "λy. y" evalByValue(App(succ, zero)).show //│ = "λf. λx. f ((λf. λx. x) f x)" evalByValue(App(succ, App(succ, zero))).show //│ = "λf. λx. f ((λf. λx. f ((λf. λx. x) f x)) f x)" :fixme // More inconsistent origin locations to fix... fun evalByValue'(term) = while term is (@compile ByValue.Context(ByValue.Redex)) as [subterm, remake] then set term = remake(subterm) else term //│ ╔══[COMPILATION ERROR] Pattern chaining is not supported in pattern compilation. //│ ║ l.214: App(Abs as lhs, Context(Hole) as [rhs, remake]) => //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Pattern chaining is not supported in pattern compilation. //│ ║ l.217: App(Context(Hole) as [lhs, remake], Term as rhs) => //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.282: else term //│ ╙── ^^^^ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.282: else term //│ ╙── ^^^^ object ByName with // Call-by-name allows reducing any beta redex. The argument `n` may be any // term, not necessarily a value. pattern Redex = App(Abs(Var(x), m), Term as n) => subst(m, x, n) // Call-by-name evaluation contexts // // E ::= □ // | E t pattern Context(pattern Hole) = ( (Hole as hole) => [hole, id] ) | ( App(Context(Hole) as [lhs, remake], Term as rhs) => [lhs, lhs' => App(remake(lhs'), rhs)] ) :silent let t = App(Abs(Var("x"), id_a), App(id_x, id_y)) print of t.show //│ > (λx. λa. a) (λx. x) λy. y import "../../../mlscript-compile/Runtime.mls" fun stepByStep(system, term, maxSteps) = let numSteps = 0 print of "Term: " + term.show while numSteps < maxSteps and system.Context.unapply(system.Redex, term) is Runtime.MatchSuccess([subterm, remake], _) do set numSteps += 1 set term = remake(subterm) print of "Step " + numSteps + ": " + term.show if numSteps == maxSteps do print of "Stopped due to too many steps." :silent let applySelf = Abs(Var("x"), App(Var("x"), Var("x"))) let omega = App(applySelf, applySelf) print of applySelf.show print of omega.show //│ > λx. x x //│ > (λx. x x) λx. x x stepByStep(ByValue, t, 10) //│ > Term: (λx. λa. a) (λx. x) λy. y //│ > Step 1: (λx. λa. a) λy. y //│ > Step 2: λa. a stepByStep(ByName, App(id_x, id_y), 10) //│ > Term: (λx. x) λy. y //│ > Step 1: λy. y stepByStep(ByValue, App(id_x, App(id_y, id_a)), 10) //│ > Term: (λx. x) (λy. y) λa. a //│ > Step 1: (λx. x) λa. a //│ > Step 2: λa. a stepByStep(ByName, App(id_x, App(id_y, id_a)), 10) //│ > Term: (λx. x) (λy. y) λa. a //│ > Step 1: (λy. y) λa. a //│ > Step 2: λa. a stepByStep(ByName, App(App(id_x, id_y), id_a), 10) //│ > Term: (λx. x) λy. y λa. a //│ > Step 1: (λy. y) λa. a //│ > Step 2: λa. a stepByStep(ByName, App(id_x, App(id_a, id_b)), 10) //│ > Term: (λx. x) (λa. a) λb. b //│ > Step 1: (λa. a) λb. b //│ > Step 2: λb. b stepByStep(ByName, App(Abs(Var("u"), id_a), omega), 10) //│ > Term: (λu. λa. a) (λx. x x) λx. x x //│ > Step 1: λa. a // The following term is evaluated differently in two systems. :silent let t = App(Abs(Var("u"), id_a), omega) print of t.show //│ > (λu. λa. a) (λx. x x) λx. x x stepByStep(ByValue, t, 10) //│ > Term: (λu. λa. a) (λx. x x) λx. x x //│ > Step 1: (λu. λa. a) (λx. x x) λx. x x //│ > Step 2: (λu. λa. a) (λx. x x) λx. x x //│ > Step 3: (λu. λa. a) (λx. x x) λx. x x //│ > Step 4: (λu. λa. a) (λx. x x) λx. x x //│ > Step 5: (λu. λa. a) (λx. x x) λx. x x //│ > Step 6: (λu. λa. a) (λx. x x) λx. x x //│ > Step 7: (λu. λa. a) (λx. x x) λx. x x //│ > Step 8: (λu. λa. a) (λx. x x) λx. x x //│ > Step 9: (λu. λa. a) (λx. x x) λx. x x //│ > Step 10: (λu. λa. a) (λx. x x) λx. x x //│ > Stopped due to too many steps. stepByStep(ByName, t, 10) //│ > Term: (λu. λa. a) (λx. x x) λx. x x //│ > Step 1: λa. a ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/EvaluationContext2.mls ================================================ :js // This is an interesting experiment in evaluation context. // Author: Lionel Parreaux import "../../../mlscript-compile/Option.mls" open annotations open Option { Some, None } class Ident(val name: Str) data class Term with constructor Lit(value: Int) Var(name: Ident) App(lhs: Term, rhs: Term) Abs(lhs: Ident, rhs: Term) Add(lhs: Term, rhs: Term) // A cached set of free variables let _freeVars = None // Compute free variables. Retrieve the cache if it exists. fun freeVars = if _freeVars is Some(fv) then fv None then let fv = if this is Var(name) then mkSet of name App(lhs, rhs) then lhs.freeVars.union(rhs.freeVars) Abs(name, body) then body.freeVars.difference(mkSet of name) set _freeVars = Some(fv) fv // Pretty-print the term. fun show = fun par(t) = if t is Lit | Var then t.show else "(" + t.show + ")" if this is Var(id) then id.name App(App as lhs, rhs) then lhs.show + par(rhs) App(lhs, rhs) then par(lhs) + " " + par(rhs) Abs(id, body) then "λ" + id.name + ". " + body.show Lit(value) then value.toString() Add(Add as lhs, rhs) then lhs.show + " + " + par(rhs) Add(lhs, rhs) then par(lhs) + " + " + par(rhs) App(Var(Ident of "a"), Var(Ident of "a_1")).show //│ = "a a_1" pattern Value = Abs | Lit module CBV with pattern Ctx(pattern Hole) = | Hole | Add(Ctx(Hole), _) | Add(Value, Ctx(Hole)) | App(Value, Ctx(Hole)) | App(Ctx(Hole), _) pattern Redex = | Add(Lit(v1), Lit(v2)) => Lit(v1 + v2) | App(Abs(id, body), Value as arg) => body.subst(id, arg) pattern Step = Ctx(Redex) pattern Steps = Step as Steps | _ fun eval(term) = print("Evaluating:", term.show) if term is CBV.Step(next) then eval(next) Value then print("Done.") else print("Stuck!") :silent let a = Var(Ident("a")) b = Var(Ident("b")) x = Lit(2) y = Lit(3) z = Lit(4) t = Add(Add(x, y), z) eval(t) //│ > Evaluating: 2 + 3 + 4 //│ > Evaluating: 5 + 4 //│ > Evaluating: 9 //│ > Done. eval(Add(t, t)) //│ > Evaluating: 2 + 3 + 4 + (2 + 3 + 4) //│ > Evaluating: 5 + 4 + (2 + 3 + 4) //│ > Evaluating: 9 + (2 + 3 + 4) //│ > Evaluating: 9 + (5 + 4) //│ > Evaluating: 9 + 9 //│ > Evaluating: 18 //│ > Done. eval(Add(t, a)) //│ > Evaluating: 2 + 3 + 4 + a //│ > Evaluating: 5 + 4 + a //│ > Evaluating: 9 + a //│ > Stuck! assert Add(t, t) is CBV.Steps(res), res //│ = Lit(18) CBV.Step.unapply(x) //│ = MatchFailure(null) CBV.Step.unapply(t) //│ = MatchSuccess(Add(Lit(5), Lit(4)), null) eval(x) //│ > Evaluating: 2 //│ > Done. ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/Extraction.mls ================================================ :js import "../../../mlscript-compile/Option.mls" open Option { Some, None } open annotations pattern Itch = Some(Some(Some(Some(Some(Some(Some(42))))))) fun isItch(x) = x is @compile Itch :expect true isItch of Some(Some(Some(Some(Some(Some(Some(42))))))) //│ = true :expect false isItch of Some(Some(Some(Some(Some(Some(Some(Some(42)))))))) //│ = false :expect false isItch of 42 //│ = false pattern Itch' = Some(Some(Some(Some(Some("scratch" as x))))) => x fun action(x) = if x is (@compile Itch') as output then "Just " + output + "!" else "I don't know." action of Some(Some(Some(Some(Some("scratch"))))) //│ = "Just scratch!" // Test the semantics of biased disjunction. pattern BiasedOr1 = Some(1 | Some(_)) fun biasedOr1(x) = x is @compile BiasedOr1 biasedOr1 of Some(1) //│ = true biasedOr1 of Some(42) //│ = false biasedOr1 of Some(Some(2)) //│ = true biasedOr1 of Some(Some("hello")) //│ = true biasedOr1 of Some(Some(Some(Some(Some(Some(Some(Some(42)))))))) //│ = true fun observeSome(x) = print("observe some " + x) x + 1 fun observePlain(x) = print("observe plain " + x) x + 2 pattern LoggedBiasedOr = | Some((Some(x) => observeSome(x)) as res) => res | Some((x => observePlain(x)) as res) => res print of Some(1) is @compile LoggedBiasedOr //│ > true if Some(Some(1)) is (@compile LoggedBiasedOr) as output then print("logged biased: " + output) //│ > observe some 1 //│ > observe plain Some(1) //│ > logged biased: 2 // Make it a bit more complicated: biased disjunction with transformations. pattern BiasedOr2 = | Some((Some(x) => x + 1) as res) => res | Some((x => x + 2) as res) => res :fixme // TODO: fix IR rebinding issue (each symbol should be bound at most once) :checkIR fun biasedOr2__compiled(x) = if x is (@compile BiasedOr2) as output then output //│ ═══[INTERNAL ERROR] [BlockChecker] Invalid IR: symbol x⁰ is bound more than once fun biasedOr2__naive(x) = if x is BiasedOr2(output) then output biasedOr2__compiled of Some(1) //│ = 3 biasedOr2__naive of Some(1) //│ = 3 biasedOr2__compiled of Some(Some(1)) //│ = 2 biasedOr2__naive of Some(Some(1)) //│ = 2 data class Pair[A, B](first: A, second: B) pattern XorPair = Pair(None, Some(_)) | Pair(Some(_), None) fun xorPair(x) = if x is (@compile XorPair) as output then output else "failed" xorPair of Pair(None, None) //│ = "failed" xorPair of Pair(None, Some(1)) //│ = Pair(None, Some(1)) xorPair of Pair(Some(56), None) //│ = Pair(Some(56), None) fun xorPairInterp(x) = if x is XorPair as output then output else "failed" xorPairInterp of Pair(None, None) //│ = "failed" xorPairInterp of Pair(None, Some(1)) //│ = Pair(None, Some(1)) xorPairInterp of Pair(Some(56), None) //│ = Pair(Some(56), None) pattern XorPairT = Pair(None, Some(_ => 111)) | Pair(Some(_), None) fun xorPairT(x) = if x is (@compile XorPairT) as output then output else "failed" xorPairT of Pair(None, None) //│ = "failed" // When one branch transforms a constructor field, both compiled and // interpreted matching rebuild a fresh `Pair` with the transformed field. xorPairT of Pair(None, Some(1)) //│ = Pair(None, Some(111)) xorPairT of Pair(Some(56), None) //│ = Pair(Some(56), None) fun xorPairTInterp(x) = if x is XorPairT as output then output else "failed" xorPairTInterp of Pair(None, None) //│ = "failed" xorPairTInterp of Pair(None, Some(1)) //│ = Pair(None, Some(111)) xorPairTInterp of Pair(Some(56), None) //│ = Pair(Some(56), None) data class Triplet[A, B, C](first: A, second: B, third: C) pattern ExactlyOneNone = | Triplet(None, Some(_), Some(_)) | Triplet(Some(_), None, Some(_)) | Triplet(Some(_), Some(_), None ) fun exactlyOneNone(x) = if x is (@compile ExactlyOneNone) as output then output else "failed" fun exactlyOneNone__naive(x) = if x is ExactlyOneNone(output) then output else "failed" // For transform-free compiled patterns, we can now reuse the original // scrutinee as the output instead of synthesizing a plain record. exactlyOneNone of Triplet(None, Some(1), Some(2)) //│ = Triplet(None, Some(1), Some(2)) exactlyOneNone of Triplet(Some(1), None, Some(2)) //│ = Triplet(Some(1), None, Some(2)) exactlyOneNone of Triplet(Some(1), Some(2), None) //│ = Triplet(Some(1), Some(2), None) // This now agrees with the naive compilation of patterns, which also returns // the scrutinee directly. exactlyOneNone__naive of Triplet(None, Some(1), Some(100)) //│ = Triplet(None, Some(1), Some(100)) exactlyOneNone__naive of Triplet(Some(1), None, Some(400)) //│ = Triplet(Some(1), None, Some(400)) exactlyOneNone__naive of Triplet(Some(1), Some(2), None) //│ = Triplet(Some(1), Some(2), None) :expect "failed" exactlyOneNone of Triplet(None, Some(1), None) //│ = "failed" :expect "failed" exactlyOneNone of Triplet(None, None, Some(1)) //│ = "failed" :expect "failed" exactlyOneNone of Triplet(Some(1), None, None) //│ = "failed" :expect "failed" exactlyOneNone of Triplet(None, None, None) //│ = "failed" :expect "failed" exactlyOneNone of Triplet(Some(1), Some(2), Some(3)) //│ = "failed" ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/Flatten.mls ================================================ :js open annotations import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" open Stack fun list(...elements) = elements Iter.toStack() // This pattern can flatten nested lists. It tries to flatten the head and // concatenate the output with the tail. If the head cannot be flattened, it // would just append the head to the flattened tail. pattern Flatten = | Nil => Nil | ((Flatten as hd) :: (Flatten as tl)) => hd ::: tl | (hd :: (Flatten as tl)) => hd :: tl fun flatten__naive(xs) = if xs is Flatten as ys then print of "Flattened: [" + (ys Iter.fromStack() Iter.joined(", ")) + "]" else "Cannot flatten: " + xs flatten__naive of Nil //│ > Flattened: [] flatten__naive of list(1) //│ > Flattened: [1] flatten__naive of list(1, 2, 3, 4) //│ > Flattened: [1, 2, 3, 4] flatten__naive of list(list(1, 2), list(3, 4)) //│ > Flattened: [1, 2, 3, 4] flatten__naive of list(list(list(list(1, 2, 3, 4)))) //│ > Flattened: [1, 2, 3, 4] flatten__naive of list(list(1, 2), list(list(3, 4), list(list(5, 6)))) //│ > Flattened: [1, 2, 3, 4, 5, 6] fun flatten__compiled(xs) = if xs is (@compile Flatten) as ys then print of "Flattened: [" + (ys Iter.fromStack() Iter.joined(", ")) + "]" else "Cannot flatten: " + xs flatten__compiled of Nil //│ > Flattened: [] flatten__compiled of list(1) //│ > Flattened: [1] flatten__compiled of list(1, 2, 3, 4) //│ > Flattened: [1, 2, 3, 4] flatten__compiled of list(list(1, 2), list(3, 4)) //│ > Flattened: [1, 2, 3, 4] flatten__compiled of list(list(list(list(1, 2, 3, 4)))) //│ > Flattened: [1, 2, 3, 4] flatten__compiled of list(list(1, 2), list(list(3, 4), list(list(5, 6)))) //│ > Flattened: [1, 2, 3, 4, 5, 6] ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/HindleyMilner.mls ================================================ :js // Hindley-Milner type inference implementation import "../../../mlscript-compile/Option.mls" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/Runtime.mls" open annotations open Option { Some, None } data class Type with constructor TVar(name: Str) TFun(lhs: Type, rhs: Type) TPrim(name: Str) fun show = if this is TVar(name) then name TFun(TFun as lhs, rhs) then "(" + lhs.show + ") -> " + rhs.show TFun(lhs, rhs) then lhs.show + " -> " + rhs.show TPrim(name) then name fun equals(that) = if this is TVar(name) and that is TVar(name') then name === name' TFun(lhs, rhs) and that is TFun(lhs', rhs') then lhs.equals(lhs') and rhs.equals(rhs') TPrim(name) and that is TPrim(name') then name === name' else false let _freeVars = None // Get free type variables fun freeVars = if _freeVars is Some(vars) then vars None then let vars = if this is TVar(name) then new Set([name]) TFun(lhs, rhs) then lhs.freeVars.union(rhs.freeVars) TPrim(_) then new Set([]) set _freeVars = Some(vars) vars fun hasFreeVar(name) = freeVars.has(name) let TInt = TPrim("Int") let TBool = TPrim("Bool") //│ TBool = TPrim("Bool") //│ TInt = TPrim("Int") pattern ShowTerm = | Var(name) => name | App(Abs as ShowTerm as lhs, rhs) => "(" + lhs + ") " + rhs | App(ShowTerm as lhs, App as ShowTerm as rhs) => lhs + " (" + rhs + ")" | App(ShowTerm as lhs, ShowTerm as rhs) => lhs + " " + rhs | Abs(name, ShowTerm as body) => "λ" + name + ". " + body | IntLit(value) => value.toString() | BoolLit(value) => if value then "true" else "false" // data class Term with constructor Var(name: Str) App(lhs: Term, rhs: Term) Abs(name: Str, body: Term) IntLit(value: Int) BoolLit(value: Bool) // Any more convenient way to apply a pattern? // For example, `this through ShowTerm`? fun show = if this is ShowTerm as result then result data class Scheme(vars: Set[Str], typ: Type) with fun show = if vars.size === 0 then typ.show else "∀" + vars Iter.joined(", ") + ". " + typ.show // Substitution for types data class Subst(map: Map[Str, Type]) with fun apply(typ) = if typ is TVar(name) and this is Subst(map) and map.has(name) then map.get(name) else typ TFun(lhs, rhs) then TFun(apply(lhs), apply(rhs)) else typ fun applyScheme(scheme) = if scheme is Scheme(vars, typ) then let newMap = new Map() this.map Iter.each of case [key, value] then if vars.has(key) is false then newMap.set(key, value) Subst(newMap).apply(typ) fun compose(that) = let newMap = new Map() that.map Iter.each of case [key, value] then newMap.set(key, apply(value)) this.map Iter.each of case [key, value] then newMap.set(key, value) Subst(newMap) fun show = let pairs = mut [] map Iter.each of case [key, value] then pairs.push(key + " -> " + value.show) "{" + pairs.join(", ") + "}" let emptySub = Subst(new Map()) //│ emptySub = Subst(Map(0) {}) fun (~>) applyPattern(x, P) = if P.unapply(x) is Runtime.MatchSuccess(result, _) then result // Note that how we use `where` clauses in disjunction patterns with bindings. pattern Unify = ( ([TFun(l1, r1), TFun(l2, r2)]) => let s1 = [l1, l2] ~> Unify let s2 = [s1.apply(r1), s1.apply(r2)] ~> Unify s2.compose(s1) ) | ( ( ([TVar(name1), TVar(name2)] | [TPrim(name1), TPrim(name2)]) where name1 === name2 ) => emptySub ) | ( ( ([TVar(nme), Type as typ] | [Type as typ, TVar(nme)]) where not(typ.hasFreeVar(nme)) ) => Subst(new Map([[nme, typ]])) ) | ( ([TPrim(name1), TPrim(name2)] where name1 === name2) => emptySub ) fun unify'(t1, t2) = [t1, t2] ~> Unify fun unify(t1, t2) = if t1 is TVar(name1) and t2 is TVar(name2) and name1 === name2 then emptySub t1 is TVar(name) and t2.hasFreeVar(name) then throw new Error("Occurs check failed") else Subst(new Map([[name, t2]])) t2 is TVar(name) and t1.hasFreeVar(name) then throw new Error("Occurs check failed") else Subst(new Map([[name, t1]])) t1 is TFun(l1, r1) and t2 is TFun(l2, r2) then let s1 = unify(l1, l2) let s2 = unify(s1.apply(r1), s1.apply(r2)) s2.compose(s1) t1 is TPrim(name) and t2 is TPrim(name') and name === name' then emptySub else throw new Error("Cannot unify " + t1.show + " with " + t2.show) unify(TInt, TInt).show //│ = "{}" unify'(TInt, TInt).show //│ = "{}" :expect "{t1 -> Int}" unify(TVar("t1"), TInt).show //│ = "{t1 -> Int}" unify'(TVar("t1"), TInt).show //│ = "{t1 -> Int}" :expect "{}" unify(TFun(TVar("t2"), TVar("t3")), TFun(TVar("t2"), TVar("t3"))).show //│ = "{}" unify'(TFun(TVar("t2"), TVar("t3")), TFun(TVar("t2"), TVar("t3"))).show //│ = "{}" :re unify(TVar("t1"), TFun(TVar("t1"), TInt)).show //│ ═══[RUNTIME ERROR] Error: Occurs check failed :re unify'(TVar("t1"), TFun(TVar("t1"), TInt)).show //│ ═══[RUNTIME ERROR] Error: match error // Note: missing type arguments for `Map`. data class Env(bindings: Map[Str, Scheme]) with fun lookup(name) = if bindings.has(name) then bindings.get(name) else throw new Error("Unbound variable: " + name) fun extend(name, scheme) = let newBindings = new Map(bindings) newBindings.set(name, scheme) Env(newBindings) fun freeVars = let vars = new Set([]) bindings.values() Iter.each of scheme => scheme => scheme.typ.freeVars Iter.each of v => vars.add(v) vars fun generalize(typ) = let envFree = freeVars let typeFree = typ.freeVars let genVars = new Set([]) typeFree Iter.each of v => if envFree.has(v) is false then genVars.add(v) Scheme(genVars, typ) let emptyEnv = Env(new Map()) //│ emptyEnv = Env(Map(0) {}) class Gen with let counter = 0 fun subscript = let value = counter text = "" while value > 0 || text.length === 0 do set text += String.fromCodePoint(0x2080 + value % 10) set value = Math.floor(value / 10) text fun fresh = let name = "t" + subscript set counter += 1 TVar(name) fun infer(env, term)(using gen: Gen) = if term is IntLit then [emptySub, TInt] BoolLit then [emptySub, TBool] Var(name) and env.lookup(name) is Scheme(vars, typ) then // Instantiate the type scheme let subst = new Map() vars Iter.each of v => subst.set(v, gen.fresh) let s = Subst(subst) [emptySub, s.apply(typ)] App(lhs, rhs) and infer(env, lhs) is [s1, t1] then let env1 = Env(new Map()) env.bindings Iter.each of case [name, scheme] then env1.bindings.set(name, Scheme(scheme.vars, s1.apply(scheme.typ))) if infer(env1, rhs) is [s2, t2] then... let tv = gen.fresh let s3 = unify(s2.apply(t1), TFun(t2, tv)) [s3.compose(s2).compose(s1), s3.apply(tv)] Abs(name, body) and let tv = gen.fresh let env1 = env.extend(name, Scheme(new Set([]), tv)) infer(env1, body) is [s, t] then [s, TFun(s.apply(tv), t)] // Type inference with generalization fun inferTop(env, name, term)(using Gen) = if infer(env, term) is [subst, typ] then... [subst, env.generalize(typ)] fun (|-) inferType(env, term) = using Gen = new Gen env.generalize(infer(env, term).1).show :silent // Example terms let id = Abs("x", Var("x")) let const = Abs("x", Abs("y", Var("x"))) let apply = Abs("f", Abs("x", App(Var("f"), Var("x")))) let compose = Abs("f", Abs("g", Abs("x", App(Var("f"), App(Var("g"), Var("x")))))) print of id.show print of const.show print of apply.show print of compose.show //│ > λx. x //│ > λx. λy. x //│ > λf. λx. f x //│ > λf. λg. λx. f (g x) emptyEnv |- id //│ = "∀t₀. t₀ -> t₀" emptyEnv |- const //│ = "∀t₀, t₁. t₀ -> t₁ -> t₀" emptyEnv |- apply //│ = "∀t₁, t₂. (t₁ -> t₂) -> t₁ -> t₂" emptyEnv |- compose //│ = "∀t₃, t₄, t₂. (t₃ -> t₄) -> (t₂ -> t₃) -> t₂ -> t₄" :expect "Int" emptyEnv |- IntLit(42) //│ = "Int" :expect "Bool" emptyEnv |- BoolLit(true) //│ = "Bool" :expect "Int" emptyEnv |- App(id, IntLit(5)) //│ = "Int" :expect "Int" emptyEnv |- App(id, App(id, IntLit(5))) //│ = "Int" :silent let env1 = emptyEnv.extend of "id" Scheme(new Set(["a"]), TFun(TVar("a"), TVar("a"))) :expect "Int" env1 |- App(Var("id"), IntLit(5)) //│ = "Int" :expect "Bool" env1 |- App(Var("id"), BoolLit(true)) //│ = "Bool" :silent let env2 = env1.extend of "add" Scheme(new Set(), TFun(TInt, TFun(TInt, TInt))) env2 |- App(App(Var("add"), IntLit(5)), IntLit(5)) //│ = "Int" :re env2 |- App(App(Var("add"), IntLit(5)), BoolLit(true)) //│ ═══[RUNTIME ERROR] Error: Cannot unify Int with Bool :silent let env3 = env2.extend of "if" Scheme(new Set(["a"]), TFun(TBool, TFun(TVar("a"), TFun(TVar("a"), TVar("a"))))) env3 |- Var("if") //│ = "∀t₀. Bool -> t₀ -> t₀ -> t₀" :silent let t = App(App(App(Var("if"), BoolLit(true)), App(Var("id"), Var("id"))), Var("id")) print of t.show print of env3 |- t //│ > if true (id id) id //│ > ∀t₆. t₆ -> t₆ :silent :re let t = App(App(App(Var("if"), BoolLit(true)), App(Var("id"), Var("id"))), Var("add")) print of t.show print of env3 |- t //│ > if true (id id) add //│ ═══[RUNTIME ERROR] Error: Cannot unify Int with Int -> Int ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/ListPredicates.mls ================================================ :js open annotations import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Iter.mls" open Stack fun contains(xs, x) = if xs is hd :: tl and { hd === x then true, else tl contains(x) } Nil then false let list1 = 1 :: 2 :: 3 :: Nil //│ list1 = Cons(1, Cons(2, Cons(3, Nil))) list1 contains of 3 //│ = true list1 contains of 4 //│ = false // Apply a pattern to a list, returns a list of unique elements if the output of // each element is unique, and rejects the list otherwise. pattern Unique(pattern P) = Nil | ( ((((P as hd) :: (Unique(P) as tl)) => let there = tl contains(hd) [there, if there then tl else hd :: tl]) as [false, list]) => list) list1 is Unique(_) //│ = true :expect false list1 is Unique((Num as x) => x.toString().length) //│ = false fun addProperty(obj, k, v) = (...obj, (k): v) addProperty of (a: 1), "b", 2 //│ = {a: 1, b: 2} // A workaround for the above issue. fun addProperty(obj, k, v) = let newObj = Object.fromEntries of [[k, v]] (...obj, ...newObj) addProperty of (a: 1), "b", 2 //│ = {a: 1, b: 2} // I forgot the syntax of empty object. fun emptyObject = Object.fromEntries of [] // Convert a list of pairs to a map-like object pattern ListToObject = (Nil => emptyObject) | (([Str as k, v] :: (ListToObject as rest)) => addProperty(rest, k, v)) fun toObject(ps) = if ps is ListToObject as obj then obj else emptyObject print of toObject of ["a", 1] :: ["b", 2] :: Nil //│ > {b: 2, a: 1} print of toObject of 0 :: Nil //│ > {} ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/Negation.mls ================================================ :js pattern NonZero = ~0 0 is NonZero //│ = false 1 is NonZero //│ = true -1 is NonZero //│ = true pattern HomogeneousCoordinate = ((:x, :y, w: ~0 as w)) => (x: x / w, y: y / w) (x: 0, y: 0, w: 0) is HomogeneousCoordinate //│ = false (x: 0, y: 0, w: 1) is HomogeneousCoordinate //│ = true fun flatten(xyw) = if xyw is HomogeneousCoordinate as xy then xy flatten of x: 0, y: 0, w: 1 //│ = {x: 0, y: 0} flatten of x: 4, y: 4, w: 2 //│ = {x: 2, y: 2} ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/PrecedenceClimbStackParse.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" open Stack pattern Ident = ("a" ..= "z") ~ (Ident | "") data class Subexpr with constructor Id(name: Str) Op(lhs: Subexpr, op: Str, rhs: Subexpr) data class Expr(sub: Subexpr, prec: Int) pattern AtPrec(str, prec) = str as ( | ( "+" => 5 ) | ( "*" => 2 ) ) as prec fun f(x) = if x is AtPrec(op, p) then [op, p] f("+") //│ = ["+", 5] f("*") //│ = ["*", 2] pattern ParseStep = | (Ident(name) :: rest) => Expr(Id(name), 0) :: rest | (Expr(e1, p1) :: AtPrec(op, p) :: rest where p1 < p) => Expr(e1, p) :: op :: rest | (Expr(e1, p1) :: AtPrec(op, p) :: Expr(e2, p2) :: rest where p1 >= p and p2 >= p) => Expr(Op(e1, op, e2), p) :: rest | ((Expr as e1) :: AtPrec(op, _) :: ParseStep(rest)) => e1 :: op :: rest | ((Expr as e1) :: AtPrec(op, p) :: Expr(e2, p2) :: rest where p2 < p) => e1 :: op :: Expr(e2, p) :: rest | ("(" :: Expr(e, p) :: ")" :: rest) => Expr(e, 0) :: rest | ("(" :: ParseStep(rest)) => "(" :: rest pattern Parse = (ParseStep as Parse) | ( | ((Expr as e) :: Nil) => ["Success:", e] | toks => ["Failure: can't parse:", toks] ) fun parse(tokens) = print of ... if tokens is Parse(res) then res parse(Nil) //│ > Failure: can't parse: Nil parse("x" :: Nil) //│ > Success: Expr(Id("x"), 0) let input = "x" :: "+" :: "y" :: Nil //│ input = Cons("x", Cons("+", Cons("y", Nil))) parse(input) //│ > Success: Expr(Op(Id("x"), "+", Id("y")), 5) if input is ParseStep as rest then rest //│ = Cons(Expr(Id("x"), 0), Cons("+", Cons("y", Nil))) parse("(" :: "x" :: ")" :: Nil) //│ > Success: Expr(Id("x"), 0) parse("a" :: "*" :: "x" :: "+" :: "y" :: Nil) //│ > Success: Expr(Op(Op(Id("a"), "*", Id("x")), "+", Id("y")), 5) parse("a" :: "+" :: "x" :: "*" :: "y" :: Nil) //│ > Success: Expr(Op(Id("a"), "+", Op(Id("x"), "*", Id("y"))), 5) parse("(" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ > Success: Expr(Op(Id("x"), "+", Id("y")), 0) parse("a" :: "+" :: "(" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ > Success: Expr(Op(Id("a"), "+", Op(Id("x"), "+", Id("y"))), 5) parse("a" :: "+" :: "(" :: "b" :: "+" :: "x" :: "+" :: "y" :: ")" :: Nil) //│ > Success: Expr(Op(Id("a"), "+", Op(Op(Id("b"), "+", Id("x")), "+", Id("y"))), 5) :silent let input = "a" :: "+" :: "(" :: "b" :: "+" :: "(" :: "x" :: "+" :: "y" :: ")" :: ")" :: Nil parse(input) //│ > Success: Expr(Op(Id("a"), "+", Op(Id("b"), "+", Op(Id("x"), "+", Id("y")))), 5) // * These also work: pattern Parse = (ParseStep as Parse) | _ fun parse(tokens) = print of ... if tokens is Parse(res) and res is (Expr as e) :: Nil then ["Success:", e] toks then ["Failure: can't parse:", toks] parse(input) //│ > Success: Expr(Op(Id("a"), "+", Op(Id("b"), "+", Op(Id("x"), "+", Id("y")))), 5) fun parse(tokens) = fun go = case ParseStep as stack then go(stack) (Expr as e) :: Nil then ["Success:", e] toks then ["Failure: can't parse:", toks] print of ...go(tokens) parse(input) //│ > Success: Expr(Op(Id("a"), "+", Op(Id("b"), "+", Op(Id("x"), "+", Id("y")))), 5) // * Other related example definitions: pattern AtPrec = | ( "+" => 5 ) | ( "*" => 2 ) assert "+" is AtPrec(n), n //│ = 5 fun f(x) = assert x is str as AtPrec as prec [str, prec] f("+") //│ = ["+", 5] f("*") //│ = ["*", 2] // * Note: this fails due to `,` terminating the function scope... :e fun f(x) = assert x is str as AtPrec as prec, [str, prec] //│ ╔══[COMPILATION ERROR] Name not found: str //│ ║ l.142: fun f(x) = assert x is str as AtPrec as prec, [str, prec] //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Name not found: prec //│ ║ l.142: fun f(x) = assert x is str as AtPrec as prec, [str, prec] //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. pattern Oper(str, prec) = str as AtPrec as prec assert "+" is Oper as p, p //│ = ["+", 5] assert "+" is Oper(s, p), [s, p] //│ = ["+", 5] ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/Record.mls ================================================ :js open annotations import "../../../mlscript-compile/Option.mls" open Option { Some, None } // Compute Body Mass Index (BMI) from weight (kg) and height (m) pattern CalculateBMI = ((:weight, :height)) => weight / (height * height) pattern Rounded = (Num as value) => Math.round(value) (weight: 70, height: 1.75) is CalculateBMI as Rounded(22) //│ = false (weight: 70, height: 1.75) is CalculateBMI as Rounded(23) //│ = true pattern RoundedBMI = CalculateBMI as Rounded fun bmi(record) = if record is RoundedBMI as value then value bmi of weight: 70, height: 1.75 //│ = 23 :re bmi of weight: 70 //│ ═══[RUNTIME ERROR] Error: match error // This pattern is useful because it turns a pattern into a function that // returns `Some` if the pattern matches, and `None` otherwise. pattern MatchOrNone(pattern P) = ((P as x) => Some(x)) | (_ => None) fun bmi(record) = if record is MatchOrNone(RoundedBMI) as value then value :e // alternatively? fun bmi'(record) = if record is MatchOrNone[RoundedBMI] as value then value //│ ╔══[COMPILATION ERROR] Unrecognized pattern (application). //│ ║ l.39: fun bmi'(record) = if record is MatchOrNone[RoundedBMI] as value then value //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ :expect Some(23) bmi of weight: 70, height: 1.75 //│ = Some(23) bmi of height: 1.75 //│ = None :todo // Make it happen! fun bmi'(record) = if record is (@compile MatchOrNone(RoundedBMI)) as value then value //│ ╔══[COMPILATION ERROR] Pattern chaining is not supported in pattern compilation. //│ ║ l.20: pattern RoundedBMI = CalculateBMI as Rounded //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ // Calculate the area of different shapes pattern ShapeArea = (((:radius)) => Math.PI * radius * radius) | // Circle (((:width, :height)) => width * height) | // Rectangle (((:base, :height)) => base * height / 2) // Triangle fun area(shape) = if shape is ShapeArea as a then Some(a) else None area of (radius: 10) //│ = Some(314.1592653589793) area of (width: 10, height: 20) //│ = Some(200) area of (base: 10, height: 20) //│ = Some(100) // Fields are tested in the same order as they are written in the pattern. area of (radius: 10, height: 20) //│ = Some(314.1592653589793) area of (width: 10, height: 20, base: 10) //│ = Some(200) fun area__compiled(shape) = if shape is (@compile ShapeArea) as a then Some(a) else None area__compiled of (radius: 10) //│ = Some(314.1592653589793) area__compiled of (width: 10, height: 20) //│ = Some(200) area__compiled of (base: 10, height: 20) //│ = Some(100) area__compiled of (width: 10, height: 20, base: 10) //│ = Some(200) // Convert between different number systems pattern NumberSystem = | ((:digits, radix: "x")) => parseInt(digits, 16) | ((:digits, radix: "o")) => parseInt(digits, 8) | ((:digits, radix: "b")) => parseInt(digits, 2) fun parseIntegerLiteral(value) = if value is "0" ~ (("x" | "o" | "b") as radix) ~ digits and (:radix, :digits) is NumberSystem as value then Some(value) else None parseIntegerLiteral of "0xFF" //│ = Some(255) parseIntegerLiteral of "0b11111111" //│ = Some(255) parseIntegerLiteral of "0o377" //│ = Some(255) pattern IntegerLiteral = ("0" ~ (("x" | "o" | "b") as radix) ~ digits) => (:radix, :digits) fun parseIntegerLiteral(value) = if value is IntegerLiteral as output then Some(output) else None parseIntegerLiteral of "0x1234" //│ = Some({radix: "x", digits: "1234"}) parseIntegerLiteral of "0b0000111100001111" //│ = Some({radix: "b", digits: "0000111100001111"}) parseIntegerLiteral of "0o673264673" //│ = Some({radix: "o", digits: "673264673"}) parseIntegerLiteral of "0k1234" //│ = None ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/examples/TupleSpread.mls ================================================ :js // Extract the first and last elements of a list pattern FirstLast = ([x, ..._, y] => [x, y]) [0, 1, 2] is FirstLast([0, 2]) //│ = true [5, 8] is FirstLast([5, 8]) //│ = true [] is FirstLast //│ = false [1] is FirstLast //│ = false // Test if a list is a palindrome. This example is not very good because it // outputs a boolean value, rather than rejecting the input. pattern TestPalindrome = | [x, ...TestPalindrome as m, y] => m && x == y | ([_] | []) => true | _ => false [1, 2, 1] is TestPalindrome(true) //│ = true [] is TestPalindrome(true) //│ = true [1] is TestPalindrome(true) //│ = true [1, 2] is TestPalindrome(true) //│ = false // A better palindrome tuple, which matches palindrome sequences exactly. pattern Palindrome = (([head, ...Palindrome, last] => head === last) as true) | [_] | [] [1, 2, 2, 1] is Palindrome //│ = true [] is Palindrome //│ = true [1] is Palindrome //│ = true [1, 2] is Palindrome //│ = false [1, 2, 3] is Palindrome //│ = false [1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1] is Palindrome //│ = true import "../../../mlscript-compile/Stack.mls" open Stack pattern StackLike = ([] => Nil) | ([x, ...StackLike as xs] => x :: xs) [] is StackLike(Nil) //│ = true [] is StackLike(1 :: Nil) //│ = false [1] is StackLike(1 :: Nil) //│ = true :expect true [1, 2, 3, 4, 5] is StackLike(1 :: 2 :: 3 :: 4 :: 5 :: Nil) //│ = true [1, 2, 3, 4, 5] is StackLike as (1 :: 2 :: 3 :: 4 :: 5 :: Nil) //│ = true :expect false [1, 2, 3, 4, 5] is StackLike(1 :: 2 :: 3 :: 4 :: Nil) //│ = false // Extract elements at even indices in a tuple. pattern ElementAtEvenIndex = ([] => []) | ([x] => [x]) | ([x, _, ...ElementAtEvenIndex as rest] => [x, ...rest]) [] is ElementAtEvenIndex([]) //│ = true [1] is ElementAtEvenIndex([1]) //│ = true [1, 2, 3, 4, 5] is ElementAtEvenIndex([1, 3, 5]) //│ = true [1, 2, 3, 4, 5] is ElementAtEvenIndex([1, 3]) //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/nondeterminism/BitArithmetic.mls ================================================ :js open annotations { compile } data class (&&) And(lhs: Bool, rhs: Bool) (||) Or(lhs: Bool, rhs: Bool) Not(arg: Bool) pattern Truthy = true | And(Truthy, Truthy) | Or(Truthy, Truthy) | Or(Truthy, Falsy) | Or(Falsy, Truthy) | Not(Falsy) pattern Falsy = false | And(Falsy, Falsy) | And(Falsy, Truthy) | And(Truthy, Falsy) | Or(Falsy, Falsy) | Not(Truthy) :expect [true, false, false, true] [ true is @compile Truthy, false is @compile Truthy, true is @compile Falsy, false is @compile Falsy ] //│ = [true, false, false, true] fun isTruthy(value) = value is @compile Truthy fun isFalsy(value) = value is @compile Falsy fun isTruthy'(value) = value is Truthy fun isFalsy'(value) = value is Falsy :global :expect true // Test each case of `Truthy`. // =========================== isTruthy of true //│ = true isTruthy' of true //│ = true isTruthy of true && true //│ = true isTruthy' of true && true //│ = true isTruthy of true || true //│ = true isTruthy' of true || true //│ = true isTruthy of true || false //│ = true isTruthy' of true || false //│ = true isTruthy of false || true //│ = true isTruthy' of false || true //│ = true isTruthy of Not of false //│ = true isTruthy' of Not of false //│ = true // Test each case of `Falsy`. // ========================== isFalsy of false //│ = true isFalsy of false && false //│ = true // This example requires the ability to handle non-determinism. isFalsy of false && true //│ = true isFalsy' of false && true //│ = true isFalsy of true && false //│ = true isFalsy' of true && false //│ = true isFalsy of false || false //│ = true isFalsy' of false || false //│ = true isFalsy of Not of true //│ = true isFalsy' of Not of true //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/nondeterminism/EvenOddTree.mls ================================================ :js open annotations { compile } object A object B data class Node[T](left: Node[T], value: T, right: Node[T]) // The patterns generate trees that have odd or even numbers of node `A`. pattern OddTree = | A | Node(EvenTree, A, EvenTree) | Node(OddTree, A, OddTree) | Node(EvenTree, B, OddTree) | Node(OddTree, B, EvenTree) pattern EvenTree = | B | Node(EvenTree, A, OddTree) | Node(OddTree, A, EvenTree) | Node(EvenTree, B, EvenTree) | Node(OddTree, B, OddTree) fun a(lc, rc) = Node(lc, A, rc) fun b(lc, rc) = Node(lc, B, rc) :global :expect true // Test each case of `EvenTree`. // ============================= B is @compile EvenTree //│ = true B is EvenTree //│ = true not A is @compile EvenTree //│ = true not A is EvenTree //│ = true not a(B, B) is @compile EvenTree //│ = true not a(B, B) is EvenTree //│ = true a(B, A) is @compile EvenTree //│ = true a(B, A) is EvenTree //│ = true a(A, B) is @compile EvenTree //│ = true a(A, B) is EvenTree //│ = true b(A, A) is @compile EvenTree //│ = true b(A, A) is EvenTree //│ = true b(B, B) is @compile EvenTree //│ = true b(B, B) is EvenTree //│ = true // Test each case of `OddTree`. // ============================ A is @compile OddTree //│ = true A is OddTree //│ = true a(B, B) is @compile OddTree //│ = true a(B, B) is OddTree //│ = true a(A, A) is @compile OddTree //│ = true a(A, A) is OddTree //│ = true b(B, A) is @compile OddTree //│ = true b(B, A) is OddTree //│ = true b(A, B) is @compile OddTree //│ = true b(A, B) is OddTree //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/nondeterminism/LaRbTree.mls ================================================ :js open annotations { compile } data class Pair[S, T](val first: S, val second: T) object A object B // The patterns generate exactly those trees whose leftmost leaf is labeled `A` // and whose rightmost leaf is labeled `B`. pattern LaRbTree = Pair(LaTree, RbTree) pattern LaTree = A | Pair(A, AnyTree) pattern RbTree = B | Pair(AnyTree, B) pattern AnyTree = A | B | Pair(AnyTree, AnyTree) :global :expect true A is @compile LaTree //│ = true B is @compile RbTree //│ = true A is @compile AnyTree //│ = true B is @compile AnyTree //│ = true Pair(A, B) is @compile LaRbTree //│ = true Pair(Pair(A, A), Pair(B, B)) is @compile LaRbTree //│ = true Pair(Pair(A, B), B) is @compile LaRbTree //│ = true Pair(A, Pair(A, B)) is @compile LaRbTree //│ = true Pair(Pair(A, A), B) is @compile LaRbTree //│ = true Pair(A, Pair(B, B)) is @compile LaRbTree //│ = true :global :expect false A is @compile LaRbTree //│ = false B is @compile LaRbTree //│ = false Pair(B, A) is @compile LaRbTree //│ = false Pair(A, A) is @compile LaRbTree //│ = false Pair(B, B) is @compile LaRbTree //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/parametric/EtaConversion.mls ================================================ :js pattern Nullable(pattern T) = T | null // Using patterns as pattern argument directly should not create local objects. pattern Zero = 0 :ucs normalized 0 is Nullable(Zero) //│ Normalized: //│ > ‹if|while› //│ > let tmp:scrut = 0 //│ > let tmp:patternArgument0$ = member:Zero‹pattern:Zero› //│ > let tmp:unapplyResult = (member:Nullable⁰.)unapply(tmp:patternArgument0$⁰, tmp:scrut⁰) //│ > tmp:unapplyResult is (tmp:runtime⁰.)MatchSuccess‹class:MatchSuccess›(output, bindings) then true //│ > else false //│ = true import "../../../mlscript-compile/Char.mls" :ucs normalized fun foo(x) = x is Nullable(Char.Letter) //│ Normalized: //│ > ‹if|while› //│ > let tmp:patternArgument0$ = (member:Char‹module:Char›.)Letter‹pattern:Letter› //│ > let tmp:unapplyResult = (member:Nullable⁰.)unapply(tmp:patternArgument0$¹, x⁰) //│ > tmp:unapplyResult is (tmp:runtime⁰.)MatchSuccess‹class:MatchSuccess›(output, bindings) then true //│ > else false foo of null //│ = true foo of "A" //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls ================================================ :js open annotations { compile } pattern Nullable(pattern T) = null | T object A :e null is @compile Nullable //│ ╔══[COMPILATION ERROR] Pattern `Nullable` has one pattern parameter. //│ ║ l.5: pattern Nullable(pattern T) = null | T //│ ║ ^ //│ ╟── But zero pattern arguments were provided. //│ ║ l.10: null is @compile Nullable //│ ╙── ^^^^^^^^ //│ = false :e null is @compile Nullable(A, A) //│ ╔══[COMPILATION ERROR] Pattern `Nullable` has one pattern parameter. //│ ║ l.5: pattern Nullable(pattern T) = null | T //│ ║ ^ //│ ╟── But two pattern arguments were provided. //│ ║ l.20: null is @compile Nullable(A, A) //│ ╙── ^^^^^^^^^^^^^ //│ = false pattern Nullable(pattern T) = null | T null is @compile Nullable(Int) //│ = true A is @compile Nullable(A) //│ = true 2 is @compile Nullable(1 | 2) //│ = true data class Pair[A, B](val first: A, val second: B) pattern Stack(pattern T) = null | Pair(T, Stack(T)) null is @compile Stack(A) //│ = true fun (#:) pair(a, b) = Pair(a, b) A #: null is @compile Stack(A) //│ = true A #: A #: null is @compile Stack(A) //│ = true A #: A #: A #: null is @compile Stack(A) //│ = true object B A #: B #: A #: null is @compile Stack(A) //│ = false A #: B #: A #: null is @compile Stack(B) //│ = false let zeroOne = 0 #: 1 #: null let oneZero = 1 #: 0 #: null //│ oneZero = Pair(1, Pair(0, null)) //│ zeroOne = Pair(0, Pair(1, null)) :global :expect true A #: B #: A #: null is @compile Stack(A | B) //│ = true A #: B #: A #: null is @compile Stack(B | A) //│ = true zeroOne is @compile Stack(0 | 1) //│ = true oneZero is @compile Stack(0 | 1) //│ = true null #: null is @compile Stack(Stack(0 | 1)) //│ = true zeroOne #: null is @compile Stack(Stack(0 | 1)) //│ = true null #: (1 #: null) #: null is @compile Stack(Stack(0 | 1)) //│ = true zeroOne #: oneZero #: null is @compile Stack(Stack(0 | 1)) //│ = true zeroOne #: null #: oneZero #: null is @compile Stack(Stack(0 | 1)) //│ = true null #: zeroOne #: zeroOne #: null is @compile Stack(Stack(0 | 1)) //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/parametric/ListLike.mls ================================================ :js open annotations import "../../../mlscript-compile/Stack.mls" open Stack pattern ListLike(pattern T) = Nil | (T :: ListLike(T)) fun isOddIntList(xs) = xs is ListLike(((Int as x) => x % 2 === 1) as true) isOddIntList of Nil //│ = true isOddIntList of 1 :: 3 :: Nil //│ = true isOddIntList of 1 :: 2 :: 3 :: Nil //│ = false isOddIntList of 1 :: 71 :: "bruh" :: Nil //│ = false isOddIntList of "hello" :: "world" :: Nil //│ = false pattern Nullable(pattern T) = null | T fun isNullableIntList(xs) = xs is @compile ListLike(Nullable(Int)) ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls ================================================ :js open annotations pattern Null = null null is Null //│ = true pattern Nullable(pattern T) = null | T // This creates an inline object with `unapply` method and pass it to // `Nullable.unapply`. fun isPositiveOrNull(x) = x is Nullable(((Int as x) => x > 0) as true) isPositiveOrNull of 0 //│ = false isPositiveOrNull of null //│ = true isPositiveOrNull of 1 //│ = true isPositiveOrNull of -2 //│ = false // No transformation, so the output is the same as the input. fun mightBeInt(x) = if x is (@compile Nullable(Int)) as output then output mightBeInt of 42 //│ = 42 mightBeInt of null //│ = null :re mightBeInt of "hello" //│ ═══[RUNTIME ERROR] Error: match error import "../../../mlscript-compile/Option.mls" open Option { Some, None } pattern Optional(pattern T) = (null => None) | ((T as x) => Some(x)) null is Optional(Int) as None //│ = true 42 is Optional(Int) as Some(42) //│ = true null is Optional(Int) as Some(42) //│ = false :e // Test the error message. fun toIntOption(x) = if x is (@compile Optional) as o then o //│ ╔══[COMPILATION ERROR] Pattern `Optional` has one pattern parameter. //│ ║ l.47: pattern Optional(pattern T) = (null => None) | ((T as x) => Some(x)) //│ ║ ^ //│ ╟── But zero pattern arguments were provided. //│ ║ l.60: fun toIntOption(x) = if x is (@compile Optional) as o then o //│ ╙── ^^^^^^^^ fun toIntOption(x) = if x is (@compile Optional(Int)) as o then o toIntOption of 42 //│ = Some(42) toIntOption of null //│ = None :re toIntOption of "hello" //│ ═══[RUNTIME ERROR] Error: match error ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/recursion/BitSeq.mls ================================================ :js open annotations { compile } data class Pair[A, B](val first: A, val second: B) pattern Bit = 0 | 1 :expect true 0 is @compile Bit //│ = true :expect true 1 is @compile Bit //│ = true :expect false 42 is @compile Bit //│ = false pattern BitSeq = null | Pair(Bit, BitSeq) :global :expect true null is @compile BitSeq //│ = true Pair(0, null) is @compile BitSeq //│ = true Pair(1, Pair(0, null)) is @compile BitSeq //│ = true :global :expect false Pair(2, null) is @compile BitSeq //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/recursion/BitTree.mls ================================================ :js open annotations { compile } data class Pair[A, B](val first: A, val second: B) pattern Bit = 0 | 1 :expect true 0 is @compile Bit //│ = true :expect true 1 is @compile Bit //│ = true :expect false 42 is @compile Bit //│ = false pattern BitTree = null | Pair(Bit | BitTree, Bit | BitTree) null is @compile BitTree //│ = true Pair(null, null) is @compile BitTree //│ = true pattern BitTreeAlt = null | Pair(BitTreeAlt | 0 | 1, BitTreeAlt | 0 | 1) :global :expect true null is @compile BitTreeAlt //│ = true Pair(null, null) is @compile BitTreeAlt //│ = true Pair(0, null) is @compile BitTreeAlt //│ = true Pair(0, Pair(0, null)) is @compile BitTreeAlt //│ = true Pair(0, Pair(0, Pair(1, Pair(0, null)))) is @compile BitTreeAlt //│ = true Pair(0, Pair(0, Pair(1, Pair(1, null)))) is @compile BitTreeAlt //│ = true :global :expect false Pair(2, Pair(0, Pair(1, null))) is @compile BitTreeAlt //│ = false Pair(0, Pair(0, Pair(2, null))) is @compile BitTreeAlt //│ = false Pair(0, Pair(0, Pair(1, Pair(2, null)))) is @compile BitTreeAlt //│ = false Pair(0, Pair(0, Pair(1, Pair(2, null)))) is @compile BitTreeAlt //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/recursion/LeafEvenOddTree.mls ================================================ :js open annotations { compile } object A object B data class Pair[A, B](first: A, second: B) [A, B] //│ = [A, B] // The patterns generate trees that have odd or even numbers of node `A`. // In this example, only leaves carry the value `A` or `B`. For each internal // node, the combination of its children uniquely determines the pattern. // Therefore, the definition is deterministic. See `EvenOddTree.mls` for a // non-deterministic example. pattern OddTree = A | Pair(EvenTree, OddTree) | Pair(OddTree, EvenTree) pattern EvenTree = B | Pair(EvenTree, EvenTree) | Pair(OddTree, OddTree) fun (##) pair(a, b) = Pair(a, b) :global :expect true B is @compile EvenTree //│ = true not A is @compile EvenTree //│ = true B ## B is @compile EvenTree //│ = true A ## A is @compile EvenTree //│ = true (A ## A) ## (A ## A) is @compile EvenTree //│ = true (A ## A) ## (A ## A) ## (A ## A) is @compile EvenTree //│ = true ((A ## A) ## (A ## A)) ## ((A ## A) ## (A ## A)) is @compile EvenTree //│ = true ((A ## A) ## (A ## A)) ## ((A ## A) ## (B ## B)) is @compile EvenTree //│ = true :global :expect false ((A ## A) ## (A ## B)) ## ((A ## A) ## (A ## B)) is @compile OddTree //│ = false ((A ## A) ## (A ## A)) ## ((A ## A) ## (A ## B)) is @compile EvenTree //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls ================================================ :js open annotations { compile } data class Box[A](val value: A) pattern NatBox = null | Box(NatBox) fun nat(n) = if n is 0 then null else Box(nat(n - 1)) fun int(n) = if n is null then 0 Box(x) then 1 + int(x) [nat(0), nat(1), nat(2), nat(3)] //│ = [null, Box(null), Box(Box(null)), Box(Box(Box(null)))] int(nat(42)) //│ = 42 // FIXME: why are we checking `Object` and `{ value }` patterns? :sir :expect true nat(0) is @compile NatBox //│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— //│ let scrut, matcher__NatBox$, matchSuccess, lambda⁰; //│ set scrut = nat⁰(0); //│ define lambda⁰ as fun lambda¹(input) { //│ let p_0$, value, value1, p_0$1, result0$, p_0$2, tmp, tmp1, tmp2, tmp3, tmp4; //│ match input //│ null => //│ set tmp = true; //│ set p_0$ = tmp; //│ return p_0$ //│ Box⁰ => //│ match input //│ Object⁰ => //│ match input //│ { value } => //│ set value1 = input.value﹖; //│ set tmp1 = matcher__NatBox$(value1); //│ set tmp2 = { "input": value1, "result": tmp1 }; //│ end //│ else //│ set tmp2 = { "input": null, "result": false }; //│ end //│ end //│ else //│ set tmp2 = { "input": null, "result": false }; //│ end //│ set value = tmp2; //│ set result0$ = value.result﹖; //│ match result0$ //│ true => //│ set tmp3 = true; //│ end //│ else //│ set tmp3 = false; //│ end //│ set p_0$1 = tmp3; //│ return p_0$1 //│ else //│ set tmp4 = false; //│ set p_0$2 = tmp4; //│ return p_0$2 //│ end //│ }; //│ set matcher__NatBox$ = lambda¹; //│ set matchSuccess = matcher__NatBox$(scrut); //│ match matchSuccess //│ true => //│ true //│ else //│ false //│ end //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true :expect true nat(1) is @compile NatBox //│ = true :expect false Box(0) is @compile NatBox //│ = false :expect true nat(7) is @compile NatBox //│ = true :expect true nat(14) is @compile NatBox //│ = true :expect false Box(Box(Box(Box(Box(Box(Box(0))))))) is @compile NatBox //│ = false pattern PosNatBox = Box(null | NatBox) :expect false null is @compile PosNatBox //│ = false nat(1) is @compile PosNatBox //│ = true :expect true nat(2) is @compile PosNatBox //│ = true :expect true nat(3) is @compile PosNatBox //│ = true :expect false Box(Box(Box(Box(42)))) is @compile PosNatBox //│ = false pattern EvenNatBox = Box(Box(null | EvenNatBox)) :expect false nat(0) is @compile EvenNatBox //│ = false :expect false nat(1) is @compile EvenNatBox //│ = false :expect true nat(2) is @compile EvenNatBox //│ = true :expect false nat(3) is @compile EvenNatBox //│ = false :expect true nat(4) is @compile EvenNatBox //│ = true // Make the base case a new pattern. pattern Zero = null null is Zero //│ = true pattern OddNatBox = Box(Zero | Box(OddNatBox)) :expect false nat(0) is @compile OddNatBox //│ = false :expect true nat(1) is @compile OddNatBox //│ = true :expect false nat(2) is @compile OddNatBox //│ = false :expect true nat(3) is @compile OddNatBox //│ = true :expect false nat(4) is @compile OddNatBox //│ = false :expect false Box(Box(78)) is @compile OddNatBox //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/recursion/NullTree.mls ================================================ :js open annotations { compile } data class Pair[A, B](val first: A, val second: B) pattern Null = null [null is Null, 0 is Null, false is Null] //│ = [true, false, false] pattern Tree = Null | Pair(Tree, Tree) fun (##) concat(a, b) = Pair(a, b) 0 ## 1 //│ = Pair(0, 1) // Positive Test Cases // =================== :global :expect true null is @compile Tree //│ = true null ## null is @compile Tree //│ = true ((null ## null) ## null) is @compile Tree //│ = true null ## (null ## null) is @compile Tree //│ = true ((null ## null) ## (null ## null)) is @compile Tree //│ = true // Negative Test Cases // =================== :global :expect false 0 is @compile Tree //│ = false 0 ## 0 is @compile Tree //│ = false 0 ## null is @compile Tree //│ = false null ## 0 is @compile Tree //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/recursion/SignBox.mls ================================================ :js open annotations { compile } data class Box[A](val value: A) pattern Sign = -1 | 0 | 1 [-1 is Sign, 0 is Sign, 1 is Sign] //│ = [true, true, true] pattern SignBox = Box(Sign | SignBox) [ Box(-1) is @compile SignBox, Box(0) is @compile SignBox, Box(1) is @compile SignBox ] //│ = [true, true, true] [ Box(Box(-1)) is @compile SignBox, Box(Box(0)) is @compile SignBox, Box(Box(1)) is @compile SignBox ] //│ = [true, true, true] [ Box(Box(Box(-1))) is @compile SignBox, Box(Box(Box(0))) is @compile SignBox, Box(Box(Box(1))) is @compile SignBox ] //│ = [true, true, true] :expect [false, false, false] [ Box(Box(Box(2))) is @compile SignBox, Box(Box(Box(null))) is @compile SignBox, -1 is SignBox ] //│ = [false, false, false] ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/EmailAddress.mls ================================================ :js import "../../../mlscript-compile/Char.mls" import "../../../mlscript-compile/Iter.mls" import "../../../mlscript-compile/Stack.mls" open Iter { joined } // To match simple email addresses matched by the following regular expressions: // ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$ pattern UserNameLetter = Char.Letter | Char.Digit | "." | "_" | "%" | "+" | "-" pattern Rep1(pattern S) = S ~ (Rep1(S) | "") pattern UserName = Rep1(UserNameLetter) :expect true "john.doe" is UserName //│ = true :expect true "jane_doe123" is UserName //│ = true :expect true "user+name%test" is UserName //│ = true :expect false "invalid@email!" is UserName //│ = false :expect false "" is UserName //│ = false // Nested pattern arguments seem problematic. pattern DomainSuffix = Rep1("." ~ Char.Letter ~ Rep1(Char.Letter)) pattern DomainSuffixFragment = "." ~ Char.Letter ~ Rep1(Char.Letter) [".com", ".hk", ".fr", ".cn"] Iter.mapping of _ is DomainSuffixFragment Iter.folded of true, _ && _ //│ = true [".", ".a", "com"] Iter.mapping of _ is DomainSuffixFragment Iter.folded of false, _ || _ //│ = false pattern DomainSuffix = Rep1(DomainSuffixFragment) pattern DomainNameLetter = Char.Letter | Char.Digit | "-" pattern DomainName = Rep1(DomainNameLetter) "google" is DomainName //│ = true pattern Domain = DomainName ~ DomainSuffix "google.com" is Domain //│ = true pattern Email = UserName ~ "@" ~ Domain :silent let emails = [ "example@example.com" "john.doe@guardian.co.uk" "alice_bob123@sub-domain.example.org" "user+mailbox@my-domain.net" "test.user%filter@service-provider.co.in" "foo-bar@company-name.io" "simple123@abc.xyz" ] emails Iter.mapping of _ is Email Iter.folded of true, _ && _ //│ = true open Stack pattern CommaSep(pattern S) = | ((S as head) ~ "," ~ (CommaSep(S) as tail)) => head :: tail | (S as head) => head :: Nil | "" => Nil fun parseEmails(input) = if input is CommaSep(Email) as emails then emails else Nil parseEmails(emails.join(",")) Iter.fromStack() Iter.toArray() //│ = [ //│ "example@example.com", //│ "john.doe@guardian.co.uk", //│ "alice_bob123@sub-domain.example.org", //│ "user+mailbox@my-domain.net", //│ "test.user%filter@service-provider.co.in", //│ "foo-bar@company-name.io", //│ "simple123@abc.xyz" //│ ] ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/EmptyString.mls ================================================ :js pattern Oops = "" ~ (Oops | "") :re :todo "" is Oops //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded pattern Funny = "" ~ "" ~ "" "" is Funny //│ = true pattern Funny = "" ~ "hello" ~ "" "hello" is Funny //│ = true "" is Funny //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/Identifier.mls ================================================ :js open annotations fun check(pred, what) = Array.from(length: 10).forEach of (_, i, _) => let n = i.toString() print of n, (if pred(n) then "is" else "is not"), what pattern Zero = "0" :expect true "0" is Zero //│ = true pattern Binary = "0" | "1" // Currently, we expand range patterns into disjunction. fun isBinary(x) = x is @compile Binary check of isBinary, "binary" //│ > 0 is binary //│ > 1 is binary //│ > 2 is not binary //│ > 3 is not binary //│ > 4 is not binary //│ > 5 is not binary //│ > 6 is not binary //│ > 7 is not binary //│ > 8 is not binary //│ > 9 is not binary :expect false "2" is Binary //│ = false pattern Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" :expect true "0" is Digit //│ = true :expect true "9" is Digit //│ = true :expect false "a" is Digit //│ = false fun isDigit(x) = x is @compile Digit check of isDigit, "digit" //│ > 0 is digit //│ > 1 is digit //│ > 2 is digit //│ > 3 is digit //│ > 4 is digit //│ > 5 is digit //│ > 6 is digit //│ > 7 is digit //│ > 8 is digit //│ > 9 is digit pattern Lower = "a"..="z" :expect true "a" is Lower //│ = true :expect true "z" is Lower //│ = true :expect false "0" is Lower //│ = false pattern Upper = "A"..="Z" :expect true "A" is Upper //│ = true :expect true "Q" is Upper //│ = true :expect false "b" is Upper //│ = false pattern Letter = Lower | Upper :expect true "b" is Letter //│ = true :expect true "V" is Letter //│ = true :expect false "0" is Letter //│ = false :expect false "9" is Letter //│ = false // Inspect the generated code. The use of `Map` makes the expanded disjunction // out of order. I wonder if we should use a `SeqMap` for literals. fun isLetter(x) = x is @compile Letter isLetter of "a" //│ = true isLetter of "0" //│ = false pattern Word = Letter ~ (Word | "") :expect false "" is Word //│ = false :expect true "b" is Word //│ = true :expect true "pattern" is Word //│ = true :expect false "b0rked" is Word //│ = false :e fun isWord(x) = x is @compile Word //│ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. //│ ║ l.120: pattern Word = Letter ~ (Word | "") //│ ╙── ^^^^^^^^^^^^^^^^^^^ // Unsupported patterns are equivalent to `Never`. isWord of "pattern" //│ = false pattern ManyDigits = ("0" ..= "9") ~ (ManyDigits | "") :expect true "0" is ManyDigits //│ = true :expect true "42" is ManyDigits //│ = true :expect true "1234" is ManyDigits //│ = true pattern Integer = "0" | ("1" ..= "9") ~ (ManyDigits | "") :expect true "0" is Integer //│ = true :expect false "012" is Integer //│ = false :expect true "42" is Integer //│ = true pattern IdentifierStart = Letter | "_" pattern IdentifierBody = (Letter | Digit | "_") ~ (IdentifierBody | "") pattern Identifier = IdentifierStart ~ (IdentifierBody | "") :expect true "abc" is Identifier //│ = true :expect true "abc123" is Identifier //│ = true :expect true "abc_123" is Identifier //│ = true :expect true "_abc_123" is Identifier //│ = true :expect false "123abc" is Identifier //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/Number.mls ================================================ :js pattern Digits = ("0"..="9") ~ (Digits | "") pattern Integer = "0" | ("1"..="9") ~ (Digits | "") pattern FractionalPart = "." ~ Digits ".1" is FractionalPart //│ = true pattern ExponentPart = ("e" | "E") ~ ("+" | "-" | "") ~ Integer "e10" is ExponentPart //│ = true "e100" is ExponentPart //│ = true pattern Number = Integer ~ (FractionalPart | "") ~ (ExponentPart | "") // Test cases for Digits pattern // ============================= :expect true "123" is Digits //│ = true :expect true "0" is Digits //│ = true :expect true "9" is Digits //│ = true :expect false "a" is Digits //│ = false // Test cases for Integer pattern // ============================== :expect true "0" is Integer //│ = true :expect true "123" is Integer //│ = true :expect false "001" is Integer //│ = false :expect false "a" is Integer //│ = false // Test cases for FractionalPart pattern // ===================================== :expect true ".123" is FractionalPart //│ = true :expect true ".0" is FractionalPart //│ = true :expect false "." is FractionalPart //│ = false :expect false "0.1" is FractionalPart //│ = false // Test cases for ExponentPart pattern // =================================== :expect true "e10" is ExponentPart //│ = true :expect true "E-10" is ExponentPart //│ = true :expect true "e+10" is ExponentPart //│ = true :expect false "e" is ExponentPart //│ = false :expect false "e1a" is ExponentPart //│ = false // Test cases for Number pattern // ============================= :expect true "3.14" is Number //│ = true :expect true "42" is Number //│ = true :expect true "3.14e10" is Number //│ = true :expect true "1e100" is Number //│ = true :expect true "1234e-789" is Number //│ = true :expect true "0.0314E+2" is Number //│ = true :expect true "0.0314E-2" is Number //│ = true :expect false "." is Number //│ = false :expect false "e10" is Number //│ = false :expect false "3.14e" is Number //│ = false :expect true "3.14" is Number //│ = true :expect true "42" is Number //│ = true :expect true "3.14e10" is Number //│ = true :expect true "1e100" is Number //│ = true :expect true "1234e-789" is Number //│ = true :expect true "0.0314E+2" is Number //│ = true :expect true "0.0314E-2" is Number //│ = true :expect true "1.7976931348623158e+308" is Number //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/Separation.mls ================================================ :js import "../../../mlscript-compile/Iter.mls" pattern TailLines(pattern L) = | ("\n" ~ (Lines(L) as t)) => t | "" => [] pattern Lines(pattern L) = | (((L as h) ~ (TailLines(L) as t)) => [h, ...t]) | "" => [] :expect true "hello" is Lines("hello") //│ = true :expect true "hello\nworld" is Lines("hello" | "world") //│ = true :expect false "hello\nworld" is Lines("hello") //│ = false pattern Digit = "0" ..= "9" pattern Integer = Digit ~ (Integer | "") "12345" is Integer //│ = true Integer.unapplyStringPrefix("123") //│ = MatchSuccess(["123", ""], null) // Beautiful! "123\n456\n789" is Lines of Integer //│ = true fun parseIntegers(input) = if input is (Lines of (Integer as n) => parseInt(n, 10)) as n then n parseIntegers of "123\n456\n789" //│ = [123, 456, 789] :e :re // We may need a shorthand syntax to obtain the result of pattern matching. // `t through P` means match `t` against `P` and returns the result. "123\n456\n789" through Lines(Integer) // ==> [123, 456, 789] //│ ╔══[COMPILATION ERROR] Illegal juxtaposition right-hand side (identifier). //│ ║ l.48: "123\n456\n789" through Lines(Integer) // ==> [123, 456, 789] //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] TypeError: Lines1 is not a function ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/Simplification.mls ================================================ :js pattern X = "x" "x" is X //│ = true pattern Xs = X ~ (Xs | "") "xxxxxxx" is Xs //│ = true pattern Xss = Xs ~ (Xss | "") "xxxxxxxxxxxxxx" is Xss //│ = true pattern XsX = Xs ~ "x" // In the following tests, the left-hand side of concatenation should not // eagerly consume all letters. We should compile these string patterns even in // naive compilation, rather than just transliterate them. :expect true :fixme "xxxxxxxxxxxxxx" is XsX //│ ═══[RUNTIME ERROR] Expected: 'true', got: 'false' //│ = false pattern XssX = Xss ~ "x" :expect true :fixme "xxxxxxxx" is XssX //│ ═══[RUNTIME ERROR] Expected: 'true', got: 'false' //│ = false pattern XsXs = Xs ~ Xs :expect true :fixme "xxxxxx" is XsXs //│ ═══[RUNTIME ERROR] Expected: 'true', got: 'false' //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/regex/TailRepetition.mls ================================================ :js import "../../../mlscript-compile/Runtime.mls" pattern Zero = "0" Zero //│ = pattern Zero :expect true Zero.unapply("0") is Runtime.MatchSuccess //│ = true :expect true "0" is Zero //│ = true pattern ManyZeros = "0" ~ (ManyZeros | "") :expect false "" is ManyZeros //│ = false :expect true "0" is ManyZeros //│ = true :expect true "000" is ManyZeros //│ = true :expect false "0001" is ManyZeros //│ = false :expect false "1" is ManyZeros //│ = false :expect false "1000" is ManyZeros //│ = false pattern Rep(pattern S) = S ~ (Rep(S) | "") "00000" is Rep("0") //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/specialization/SimpleList.mls ================================================ :js open annotations { compile } data class Pair[A, B](a: A, b: B) data class Box[A](value: A) data class Bowl[A](value: A) data class Basket[A](value: A) data class Triplet[A, B, C](a: A, b: B, c: C) pattern Everything = _ :expect true 0 is @compile Everything //│ = true :expect true null is @compile Everything //│ = true pattern A = Box | Box Box(0) is @compile A //│ = true pattern BoxedAnswer = Box(42) Box(42) is @compile BoxedAnswer //│ = true pattern A_0 = Box(_) | Box(_) Box(0) is @compile A_0 //│ = true pattern A_1 = Box | Bowl Box(0) is @compile A_1 //│ = true Bowl(0) is @compile A_1 //│ = true pattern A_2 = _ | Box pattern A_3 = Box | _ pattern A_4 = Box(_) | Bowl(_) pattern A_3 = Box(Box) | Bowl pattern B = Box(Box) | Bowl(Box) pattern B_0 = Box(Box) | Box(Bowl) pattern C = Box(Box(Box)) pattern C_1 = Box(Box | Bowl) pattern C_2 = Box(Box | Box) pattern C_2 = Box(Box | Bowl | Box) pattern D = Box(Box(Box) | Bowl) pattern D_0 = Box(Box(Box) | Box(Bowl)) pattern D_1 = Box(Box(Bowl) | Box) pattern D_2 = Box(Bowl | Bowl | Bowl) pattern D_2 = Box(Box(Bowl) | Box(Basket) | Box(Box)) pattern E = Box(Box(Box) | Bowl(Box(Basket))) pattern F = Pair(Pair(Box, Bowl), Pair(Box, Basket)) pattern F_0 = Pair(Box, Bowl) :expect true Pair(Box(42), Bowl(42)) is @compile F_0 //│ = true pattern F_1 = Pair(Box, Box) | Pair(Box, Bowl) :expect true Pair(Box(42), Box(42)) is @compile F_1 //│ = true :expect true Pair(Box(42), Bowl(42)) is @compile F_1 //│ = true :expect false Pair(Box(42), Basket(42)) is @compile F_1 //│ = false :expect false Pair(Bowl(42), Bowl(42)) is @compile F_1 //│ = false pattern G = Triplet(Triplet(Box, Bowl, Basket), Pair(0, 1), Basket) pattern G_0 = Triplet(Box, Bowl, Basket) | Triplet(Box, Bowl, Basket) pattern G_0 = Triplet(Triplet(Box, Box, Box), Bowl, Basket) | Triplet(Triplet(Box, Box, Box), Bowl, Basket) pattern G_1 = | Triplet(Triplet(Box, Bowl, Box), Bowl, Basket) | Triplet(Triplet(Box, Box, Box), Bowl, Basket) | Triplet(Triplet(Box, Box, Bowl), Bowl, Basket) pattern BinSeq'(pattern D) = D | Pair(0 | 1, BinSeq'(D)) pattern BinSeq = null | Pair(0 | 1, BinSeq) fun (::) cons(head, tail) = Pair(head, tail) 0 :: null //│ = Pair(0, null) [ null is @compile BinSeq, 0 :: null is @compile BinSeq, 0 :: 1 :: null is @compile BinSeq, 0 :: 1 :: 0 :: null is @compile BinSeq, 0 is @compile BinSeq, 1 is @compile BinSeq, 0 :: 1 :: 2 :: null is @compile BinSeq, ] //│ = [true, true, true, true, false, false, false] [ null is @compile BinSeq'(null), 0 :: null is @compile BinSeq'(null), 0 :: 1 :: null is @compile BinSeq'(null), 0 :: 1 :: 0 :: null is @compile BinSeq'(null), 0 is @compile BinSeq'(null), 1 is @compile BinSeq'(null), 0 :: 1 :: 2 :: null is @compile BinSeq'(null), ] //│ = [true, true, true, true, false, false, false] 0 :: 1 :: 0 :: null is @compile BinSeq //│ = true :e pattern List(a) = null | Pair(a, List) //│ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. //│ ║ l.152: pattern List(a) = null | Pair(a, List) //│ ║ ^ //│ ╟── The variable is missing from this sub-pattern. //│ ║ l.152: pattern List(a) = null | Pair(a, List) //│ ╙── ^^^^ null is @compile List //│ = true ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/specialization/SimpleLiterals.mls ================================================ :js open annotations { compile } pattern Zero = "0" pattern One = "1" pattern Two = "2" fun checkZeroOneTwo(x) = if x is @compile Zero then 0 @compile One then 1 @compile Two then 2 else () checkZeroOneTwo of "0" //│ = 0 checkZeroOneTwo of "1" //│ = 1 checkZeroOneTwo of "2" //│ = 2 checkZeroOneTwo of "hello" import "../../../mlscript-compile/Option.mls" open Option fun observeZero(x) = print("zero " + x) x pattern ZeroLogged = ("0" as x) => observeZero(x) print of Some("0") is Some(@compile ZeroLogged) //│ > true if Some("0") is Some((@compile ZeroLogged) as zero) then print("nested zero: " + zero) //│ > zero 0 //│ > nested zero: 0 fun checkSomeZeroOneTwo(x) = if x is Some(@compile Zero) then 0 Some(@compile One) then 1 Some(@compile Two) then 2 else () pattern ManyZero = ("0" ~ (ManyZero | "")) | "" :e :todo let res = "1" is @compile ManyZero //│ ╔══[COMPILATION ERROR] String concatenation is not supported in pattern compilation. //│ ║ l.49: pattern ManyZero = ("0" ~ (ManyZero | "")) | "" //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/CrossCompilation.mls ================================================ :js open annotations // After sharing UCS desugaring with UPS, we can selectively efficiently compile // sub-patterns in a naively compiled pattern. pattern ZeroOne = 0 | 1 pattern SevenEight = 7 | 8 :ucs instantiation // Note that only `ZeroOne` is instantiated. pattern Bar = SevenEight | @compile ZeroOne //│ | Instantiating ZeroOne //│ | Instantiated ZeroOne //│ | > arity = 0 //│ | > arguments = //│ | > instantiated = (0 ∨ 1) :expect true 0 is Bar //│ = true :expect true 1 is Bar //│ = true :expect false 2 is Bar //│ = false :expect true 0 is @compile Bar //│ = true :expect true 1 is @compile Bar //│ = true :expect false 2 is @compile Bar //│ = false ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/Declaration.mls ================================================ :js :e pattern Foo(val T) = null | T //│ ╔══[COMPILATION ERROR] Unexpected pattern parameter T with modifiers: val //│ ║ l.5: pattern Foo(val T) = null | T //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Pattern name not found: T. //│ ║ l.5: pattern Foo(val T) = null | T //│ ╙── ^ pattern Foo(pattern T) = null | T :fixme // TODO: fix IR rebinding issue (each symbol should be bound at most once) :checkIR pattern Foo(pattern T) = null | T //│ ╔══[INTERNAL ERROR] [BlockChecker] Invalid IR: symbol T⁰ is bound more than once //│ ║ l.18: pattern Foo(pattern T) = null | T //│ ╙── ^ :breakme data pattern Foo = _ ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/InterestingPatterns.mls ================================================ :js import "../../../mlscript-compile/Stack.mls" import "../../../mlscript-compile/Option.mls" open Stack open Option { Some, None } pattern DropZ = ((:x, :y, :z)) => (x: x, y: y) :ucs ups pattern SumList = (Nil => 0) | ((Int as hd) :: (SumList as tl)) => hd + tl //│ elaborated pattern body: Nil => 0 ∨ Cons(Int as hd, SumList as tl) => builtin:+⁰(hd⁰, tl⁰) // Flatten the nested tuples. Note that it makes use of biased union. pattern Flatten = ([Flatten as hd, ...Flatten as tl] => Array.concat(hd, tl)) | _ object Leaf data class Node[A](value: A, left: Node[A], right: Node[A]) pattern TreeDepth = (Leaf => 0) | (Node(_, TreeDepth as left, TreeDepth as right) => 1 + Math.max(left, right)) pattern CountTree(pattern P) = | Node(P as x, CountTree(P) as left, CountTree(P) as right) => 1 + left + right | Node(_, CountTree(P) as left, CountTree(P) as right) => left + right | Leaf => 0 // Match a 2x2 matrix and return its transpose. :ucs ups pattern Transpose = (null => null) | (([[a, b], [c, d]]) => [[a, c], [b, d]]) //│ elaborated pattern body: null => null ∨ [[a, b], [c, d]] => [[a⁰, c⁰], [b⁰, d⁰]] // Return the middle element of a list. It won't match if the length is even. :ucs ups pattern Middle = ([x] => x) | ([x, ...(Middle as m), y] => m) //│ elaborated pattern body: [x] => x⁰ ∨ [x, ...Middle as m, y] => m⁰ // Return the leaves of a tree. pattern Leaves = (null => Nil) | ([x] => x :: Nil) | ([Leaves as left, Leaves as right] => left ::: right) // Extract the diagonal elements of a 3x3 matrix pattern MatrixDiagonal = ([[a, _, _], [_, b, _], [_, _, c]] => [a, b, c]) // Flatten a binary tree to a list using in-order traversal pattern InOrderTraversal = | Leaf => [] | Node(x, InOrderTraversal as left, InOrderTraversal as right) => left ::: [x] ::: right fun max(ow, v) = if ow is Some(w) and w > v then w else v fun min(ow, v) = if ow is Some(w) and w < v then w else v // Extract the maximum value from a binary tree pattern TreeMax = | Leaf => None | Node(x, TreeMax as left, TreeMax as right) => max(max(left, x), right) pattern TreeMin = | Leaf => None | Node(x, TreeMin as left, TreeMin as right) => min(min(left, x), right) // Check if a binary tree is balanced pattern IsBalanced = | Leaf => true | Node(_, IsBalanced as left, IsBalanced as right) => Math.abs(TreeDepth(left) - TreeDepth(right)) <= 1 // Extract the path from root to a target value in a binary tree pattern PathToValue(pattern Target) = | Leaf => [] | Node(PathToValue(Target) as left, Target, _) => [0] ::: left | Node(_, Target, PathToValue(Target) as right) => [1] ::: right | Node(PathToValue(Target) as left, _, _) => [0] ::: left | Node(_, _, PathToValue(Target) as right) => [1] ::: right // Extract elements at even indices in a list. pattern EvenIndicesList = | Nil => Nil | (x :: Nil) => x | (x :: _ :: EvenIndicesList as xs) => x :: xs ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/MixedParameters.mls ================================================ :js // The semantics of mixed pattern parameters and extraction parameters. pattern Bit = 0 | 1 pattern P(pattern Q, a, b) = [Q as a, Q as b] if [0, 0] is P(Bit, a, b) then "values: " + a + ", " + b //│ = "values: 0, 0" // We can omit all extraction parameters completely. All extraction parameters // will be placed in a tuple and returned. if [0, 0] is P(Bit) as ab then ab //│ = [0, 0] // Because the above pattern definition can be seen as: pattern P'(pattern Q) = [Q as a, Q as b] => [a, b] :e :re // However, we cannot provide only part of the extraction parameters. The rule // is that we either provide all of them or none at all. if [0, 0] is P(Bit, a) then a //│ ╔══[COMPILATION ERROR] Expected two extraction arguments, but found only one argument. //│ ║ l.24: if [0, 0] is P(Bit, a) then a //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error :e :re // But `P` is not equivalent to `P'`! If no extraction parameters are declared, // then none can be provided. if [0, 0] is P'(Bit, a, b) then "values: " + a + ", " + b //│ ╔══[COMPILATION ERROR] Expected zero extraction arguments, but found two arguments. //│ ║ l.34: if [0, 0] is P'(Bit, a, b) then "values: " + a + ", " + b //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error :fixme // Order actually matters. But it seems that I didn't implement the error // reporting correctly. if [0, 0] is P(a, Bit, b) then "values: " + a + ", " + b //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'a' //│ ║ l.43: if [0, 0] is P(a, Bit, b) then "values: " + a + ", " + b //│ ╙── ^ //│ = "values: 0, 0" pattern S(a, pattern Q, b) = [Q as a, Q as b] fun f(x) = if x is S(a, 0, b) then [a, b] f([0, 0]) //│ = [0, 0] :fixme // Order actually matters. fun f(x) = if x is S(Bit, a, b) then [a, b] //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'a' //│ ║ l.58: fun f(x) = if x is S(Bit, a, b) then [a, b] //│ ╙── ^ f([0, 0]) //│ = [0, 0] // The last thing to note is that if there is only one pattern parameter, it // will not be put into a tuple. pattern Q(pattern A, result) = A as result if 0 is Q(Bit) as y then y //│ = 0 // The value of y is the same in the example above and in the one below. if 0 is Q(Bit, y) then y //│ = 0 pattern Q'(pattern A) = A if 0 is Q'(Bit) as y then y //│ = 0 :e :re // Similarly, it cannot be written like this: if 0 is Q'(Bit, y) then y //│ ╔══[COMPILATION ERROR] Expected zero extraction arguments, but found one argument. //│ ║ l.85: if 0 is Q'(Bit, y) then y //│ ╙── ^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error pattern ZeroToOne = 0 => 1 pattern CaptureBit(value) = Bit as value :w // Extraction matches on pattern symbols only use their success/failure. If the // supplied extraction pattern transforms its output, that transformed value is // discarded unless the user makes the discard explicit with `as _`. if 0 is CaptureBit(ZeroToOne) then "discarded implicitly" //│ ╔══[WARNING] This extraction argument's transformation result is discarded by pattern `CaptureBit`. //│ ║ l.98: if 0 is CaptureBit(ZeroToOne) then "discarded implicitly" //│ ║ ^^^^^^^^^ //│ ╙── Write `... as _` to discard it explicitly. //│ = "discarded implicitly" if 0 is CaptureBit(ZeroToOne as _) then "discarded explicitly" //│ = "discarded explicitly" if 0 is CaptureBit(Bit) then "output preserving" //│ = "output preserving" ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/PatternBody.mls ================================================ :js :todo // Patterns at parameter position are not supported yet. 0 => false //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.4: 0 => false //│ ╙── ^ //│ = fun :ucs ups pattern BooleanLike = true | false | (0 => false) | (1 => true) //│ elaborated pattern body: true ∨ false ∨ 0 => false ∨ 1 => true import "../../../mlscript-compile/Stack.mls" open Stack class Pair[A, B](val fst: A, val snd: B) :ucs ups pattern PlainList(pattern T) = (null => Nil) | (Pair(T as hd, PlainList(T) as tl) => hd :: tl) //│ elaborated pattern body: null => (member:Stack⁰.)Nil‹member:Nil› ∨ Pair(T as hd, PlainList(T) as tl) => (member:Stack⁰.)Cons‹member:Cons›(hd⁰, tl⁰) :ucs ups // TODO: Decide on the relative operator precedence between `as` and `=>`. // This is elaborated into `T as (x => x)`, which might not be what we want. pattern Identity(pattern T) = T as x => x //│ elaborated pattern body: T as (x => x⁰) :ucs ups pattern Identity(pattern T) = (T as x) => x //│ elaborated pattern body: (T as x) => x¹ pattern Identity = x => x :ucs ups pattern Sick = (0 as a as b as c as d as e as f) => a + b + c + d + e + f //│ elaborated pattern body: ((((((0 as a) as b) as c) as d) as e) as f) => builtin:+⁰(builtin:+⁰(builtin:+⁰(builtin:+⁰(builtin:+⁰(a⁰, b⁰), c⁰), d⁰), e⁰), f⁰) :ucs ups pattern PairLike = [fst, snd] => Pair(fst, snd) //│ elaborated pattern body: [fst, snd] => member:Pair⁰(fst⁰, snd⁰) :ucs ups pattern PairLike(fst, snd) = Pair(fst, snd) | [fst, snd] //│ elaborated pattern body: Pair(fst, snd) ∨ [fst, snd] :w pattern UselessParameter = x //│ ╔══[WARNING] Unused pattern binding: x. //│ ║ l.49: pattern UselessParameter = x //│ ╙── ^ :ucs ups pattern PlainList(pattern T) = (null => Nil) | ([T as hd, PlainList(T) as tl] => hd :: tl) //│ elaborated pattern body: null => (member:Stack⁰.)Nil‹member:Nil› ∨ [T as hd, PlainList(T) as tl] => (member:Stack⁰.)Cons‹member:Cons›(hd¹, tl¹) :ucs ups pattern Consistent = ((Int as x) | (Str as x)) => x.toString() //│ elaborated pattern body: (Int as x ∨ Str as x) => x².toString() :e pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //│ ╔══[COMPILATION ERROR] This pattern cannot be bound. //│ ║ l.63: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //│ ║ ^^^ //│ ╟── Because the pattern it belongs to is negated. //│ ║ l.63: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.63: pattern Inconsistent = ~(~(Int as x) | Str) => x.toString() //│ ╙── ^ :e pattern Inconsistent = (~(~((Int as x) => x)) | Str) => x.toString() //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.75: pattern Inconsistent = (~(~((Int as x) => x)) | Str) => x.toString() //│ ╙── ^ :ucs ups pattern Negated = ~(Int | Str) //│ elaborated pattern body: ¬(Int ∨ Str) // FIXME: The precedence of `=>` is higher than `~`? :pt pattern BadNegated = ~(Int as x) => x //│ ———————————————| Parsed tree |—————————————————————————————————————————————————————————————————————— //│ TypeDef: //│ k = Pat //│ head = Ident of "BadNegated" //│ rhs = S of App: //│ lhs = Ident of "~" //│ rhs = Tup of Ls of //│ InfixApp: //│ lhs = Tup of Ls of //│ InfixApp: //│ lhs = Ident of "Int" //│ kw = Keywrd of keyword 'as' //│ rhs = Ident of "x" //│ kw = Keywrd of keyword '=>' //│ rhs = Ident of "x" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :ucs ups :e pattern BadNegated = (~(Int as x)) => x //│ ╔══[COMPILATION ERROR] This pattern cannot be bound. //│ ║ l.106: pattern BadNegated = (~(Int as x)) => x //│ ║ ^^^ //│ ╟── Because the pattern it belongs to is negated. //│ ║ l.106: pattern BadNegated = (~(Int as x)) => x //│ ╙── ^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.106: pattern BadNegated = (~(Int as x)) => x //│ ╙── ^ //│ elaborated pattern body: ¬(Int as x) => :ucs ups :e pattern BadNegated = (~Pair(Int, x)) => x //│ ╔══[COMPILATION ERROR] This variable cannot be accessed. //│ ║ l.120: pattern BadNegated = (~Pair(Int, x)) => x //│ ║ ^ //│ ╟── Because the pattern it belongs to is negated. //│ ║ l.120: pattern BadNegated = (~Pair(Int, x)) => x //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: x //│ ║ l.120: pattern BadNegated = (~Pair(Int, x)) => x //│ ╙── ^ //│ elaborated pattern body: ¬Pair(Int, x) => :ucs ups pattern DoubleNegated = ~(~Int) //│ elaborated pattern body: ¬¬Int :ucs ups pattern Nothing = ~_ //│ elaborated pattern body: ¬_ :ucs ups pattern Origin = (x: 0, y: 0) //│ elaborated pattern body: {x: 0, y: 0} :ucs ups pattern Origin = (x: 0, y: 0, z: 0) //│ elaborated pattern body: {x: 0, y: 0, z: 0} :ucs ups pattern PointLike = ((x: x, y: y)) => x + y //│ elaborated pattern body: {x: x, y: y} => builtin:+⁰(x³, y⁰) :ucs ups pattern PointLike = ((:x, :y, :z)) => x + y //│ elaborated pattern body: {x: x, y: y, z: z} => builtin:+⁰(x⁴, y¹) :ucs ups pattern NestedTuple = [[[]]] //│ elaborated pattern body: [[[]]] pattern Digit = "0" ..= "9" pattern Naughty = CanYouSeeMe(n) => n data class CanYouSeeMe(wow: Int) class SecretPair(a: Int, val b: Int) :ucs ups pattern KeepSecret(b) = SecretPair(_, b) //│ elaborated pattern body: SecretPair(_, b) :e pattern RebuildSecret = SecretPair(_, b => b + 1) //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.172: pattern RebuildSecret = SecretPair(_, b => b + 1) //│ ║ ^ //│ ╟── because the corresponding parameter `a` is not publicly accessible //│ ║ l.165: class SecretPair(a: Int, val b: Int) //│ ║ ^ //│ ╟── because rebuilding the matched `SecretPair` value requires reading every constructor argument //│ ║ l.172: pattern RebuildSecret = SecretPair(_, b => b + 1) //│ ║ ^^^^^^^^^^ //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible :e // Even if a certain class is under the selection of multiple modules, we should // still be able to resolve it correctly. fun y = HiddenCorner.m.m.k pattern Naughty = HiddenCorner.m.m.YouCantSeeMe(n) => n + HiddenCorner.m.m.k pattern Obedient = HiddenCorner.m.m.CantYouSeeMe(n) => n + HiddenCorner.m.m.k module HiddenCorner with val m: module HiddenCorner = HiddenCorner class YouCantSeeMe(wow: Int) class CantYouSeeMe(val wow: Int) val k = 0 //│ ╔══[COMPILATION ERROR] This pattern cannot be matched //│ ║ l.188: pattern Naughty = HiddenCorner.m.m.YouCantSeeMe(n) => n + HiddenCorner.m.m.k //│ ║ ^ //│ ╟── because the corresponding parameter `wow` is not publicly accessible //│ ║ l.192: class YouCantSeeMe(wow: Int) //│ ║ ^^^ //│ ╟── Suggestion: use a wildcard pattern `_` in this position //│ ╙── Suggestion: mark this parameter with `val` so it becomes accessible ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/Precedence.mls ================================================ :ucs ups // The right binding power of `=>` is lower than the left binding power of `|`. // Therefore, if we want to union two transformations, we should parenthesize // the second transformation. pattern Test = 1 | 2 //│ elaborated pattern body: 1 ∨ 2 pattern Test = 1 | 2 => 3 //│ elaborated pattern body: 1 ∨ 2 => 3 // The following two examples are rejected because `4 => 8` is considered as a // part of the right hand side term of `1 => 2`. Currently, our implementation // does not support patterns as lambda parameters. :todo pattern Test = 1 => 2 | 4 => 8 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.18: pattern Test = 1 => 2 | 4 => 8 //│ ╙── ^ //│ elaborated pattern body: 1 => 2 | λ(). 8 //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composed type) //│ ║ l.18: pattern Test = 1 => 2 | 4 => 8 //│ ╙── ^ :todo pattern Test = 1 => 2 | (4 => 8) //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal //│ ║ l.28: pattern Test = 1 => 2 | (4 => 8) //│ ╙── ^ //│ elaborated pattern body: 1 => 2 | λ(). 8 //│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (composed type) //│ ║ l.28: pattern Test = 1 => 2 | (4 => 8) //│ ╙── ^ pattern Test = (1 => 2) | 4 => 8 //│ elaborated pattern body: 1 => 2 ∨ 4 => 8 // The precedence of `as` is lower than `=>`. pattern Test = 0 as x => 2 //│ elaborated pattern body: 0 as (x => 2) pattern Test = (0 as x) => 2 //│ elaborated pattern body: (0 as x) => 2 :e pattern Test(x) = 0 as x | 1 as x //│ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. //│ ║ l.49: pattern Test(x) = 0 as x | 1 as x //│ ║ ^ //│ ╟── The variable is missing from this sub-pattern. //│ ║ l.49: pattern Test(x) = 0 as x | 1 as x //│ ╙── ^ //│ elaborated pattern body: (0 as (x ∨ 1)) as x pattern Test(x) = 0 as x | (1 as x) //│ elaborated pattern body: 0 as (x ∨ 1 as x) pattern Test(x) = 0 as x | (1 as x) //│ elaborated pattern body: 0 as (x ∨ 1 as x) pattern Test(x) = (0 as x) | (1 as x) //│ elaborated pattern body: 0 as x ∨ 1 as x ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls ================================================ :js class Foo(val bar: Int) pattern Bar = Foo(0) Foo(0) is Bar //│ = true Foo(1) is Bar //│ = false pattern Baz(pattern A) = A :e pattern Baz(pattern A) = A() //│ ╔══[COMPILATION ERROR] `A` is a pattern parameter. //│ ║ l.16: pattern Baz(pattern A) = A() //│ ║ ^ //│ ╟── It cannot be applied. //│ ║ l.16: pattern Baz(pattern A) = A() //│ ╙── ^ :e pattern Baz(pattern A) = A(1, 2, 3) //│ ╔══[COMPILATION ERROR] `A` is a pattern parameter. //│ ║ l.25: pattern Baz(pattern A) = A(1, 2, 3) //│ ║ ^ //│ ╟── It cannot be applied to any arguments. //│ ║ l.25: pattern Baz(pattern A) = A(1, 2, 3) //│ ╙── ^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls ================================================ :js data class Pair[A, B](val first: A, val second: B) :e pattern LessArity = Pair("meow") //│ ╔══[COMPILATION ERROR] Expected two arguments, but found only one argument. //│ ║ l.6: pattern LessArity = Pair("meow") //│ ╙── ^^^^^^ :e pattern MoreArity = Pair("meow", "woof", "moo") //│ ╔══[COMPILATION ERROR] Expected two arguments, but found three arguments. //│ ║ l.12: pattern MoreArity = Pair("meow", "woof", "moo") //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/ups/transformation/BindingLess.mls ================================================ :js import "../../../mlscript-compile/Char.mls" import "../../../mlscript-compile/Stack.mls" open Char { AnyChar } open Stack open annotations pattern Inc = (Int as x) => x + 1 data class Pair[A, B](fst: A, snd: B) pattern IncFst = Pair(Inc, _) let ps = Pair("hello", "world") //│ ps = Pair("hello", "world") let pi = Pair(1, 2) //│ pi = Pair(1, 2) fun foo(p) = if p is IncFst as q then q else p :expect true ps == foo(ps) //│ = true :expect false pi == foo(pi) //│ = false fun foo(p) = if p is (@compile IncFst) as q then q else p :expect true ps == foo(ps) //│ = true :expect false pi == foo(pi) //│ = false :expect false ps is IncFst //│ = false :expect true pi is IncFst //│ = true :expect false ps is @compile IncFst //│ = false :expect true pi is @compile IncFst //│ = true pattern ToUppercase = | ("a" => "A") | ("b" => "B") | ("c" => "C") | ("d" => "D") | ("e" => "E") | ("f" => "F") | ("g" => "G") | ("h" => "H") | ("i" => "I") | ("j" => "J") | ("k" => "K") | ("l" => "L") | ("m" => "M") | ("n" => "N") | ("o" => "O") | ("p" => "P") | ("q" => "Q") | ("r" => "R") | ("s" => "S") | ("t" => "T") | ("u" => "U") | ("v" => "V") | ("w" => "W") | ("x" => "X") | ("y" => "Y") | ("z" => "Z") | AnyChar pattern ToUppercaseList = Nil | (ToUppercase :: ToUppercaseList) fun toUppercaseList(input) = if input is ToUppercaseList as output then output toUppercaseList of "a" :: "X" :: "c" :: Nil //│ = Cons("A", Cons("X", Cons("C", Nil))) pattern ToUppercaseString = ToUppercase ~ (ToUppercaseString | "") fun toUppercase(input) = if input is ToUppercaseString as output then output toUppercase of "abc" //│ = "ABC" toUppercase of "ThIs Is AlTeRnAtInG cApS!" //│ = "THIS IS ALTERNATING CAPS!" ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Basics.mls ================================================ :global :wasm false //│ Wasm result: //│ = 0 true //│ Wasm result: //│ = 1 2 //│ Wasm result: //│ = 2 1 + 1 //│ Wasm result: //│ = 2 let x = 0 x //│ Wasm result: //│ = 0 :ge fun foo() = 42 foo //│ ╔══[COMPILATION ERROR] Returning function instances is not supported //│ ║ l.34: foo //│ ╙── ^^^ :ge class Foo with val x = 1 let f = Foo //│ ╔══[COMPILATION ERROR] Plain class references are not supported in Wasm; instantiate the class instead. //│ ║ l.42: let f = Foo //│ ╙── ^^^ fun foo() = 42 foo() //│ Wasm result: //│ = 42 :wat fun foo() = 42 foo() + foo() //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $foo (func (result (ref null any)))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (func $foo (export "foo") (type $foo) (result (ref null any)) //│ (return //│ (ref.i31 //│ (i32.const 42)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $foo (ref null any)) //│ (local $tmp (ref null any)) //│ (local $tmp1 (ref null any)) //│ (local $inlinedVal (ref null any)) //│ (local $inlinedVal1 (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $inlinedVal //│ (ref.i31 //│ (i32.const 42))) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (local.get $inlinedVal)) //│ (block (result (ref null any)) //│ (local.set $inlinedVal1 //│ (ref.i31 //│ (i32.const 42))) //│ (block (result (ref null any)) //│ (local.set $tmp1 //│ (local.get $inlinedVal1)) //│ (call $plus_impl //│ (local.get $tmp) //│ (local.get $tmp1)))))))) //│ (elem $foo declare func $foo) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 84 fun foo(x) = x + 1 foo(41) //│ Wasm result: //│ = 42 class Foo(val x) new Foo(0) //│ Wasm result: //│ = {} :wat class Foo(val a) (new Foo(42)).Foo#a //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $a (mut (ref null any)))))) //│ (type $Foo_init (func (param $this (ref null any)) (param $a (ref null any)) (result (ref null any)))) //│ (type $Foo_ctor (func (param $a (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (func $Foo_init (type $Foo_init) (param $this (ref null any)) (param $a (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $a)) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Foo_ctor (export "Foo") (type $Foo_ctor) (param $a (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) //│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Foo_init //│ (local.get $this) //│ (local.get $a))) //│ (return //│ (local.get $this)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (call $Foo_ctor //│ (ref.i31 //│ (i32.const 42)))) //│ (struct.get $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $tmp)))))) //│ (elem $Foo_init declare func $Foo_init) //│ (elem $Foo_ctor declare func $Foo_ctor) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 42 class Foo(val x) fun getX(f: Foo) = f.x getX(Foo(42)) //│ Wasm result: //│ = 42 :wat class Foo(val x) with val y = this.x (new Foo(42)).Foo#y //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any))) (field $y (mut (ref null any)))))) //│ (type $Foo_init (func (param $this (ref null any)) (param $x (ref null any)) (result (ref null any)))) //│ (type $Foo_ctor (func (param $x (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (func $Foo_init (type $Foo_init) (param $this (ref null any)) (param $x (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Foo $x //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $x)) //│ (block //│ (struct.set $Foo $y //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (struct.get $Foo $x //│ (ref.cast (ref $Foo) //│ (local.get $this)))) //│ (nop))) //│ (return //│ (local.get $this)))) //│ (func $Foo_ctor (export "Foo") (type $Foo_ctor) (param $x (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) //│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Foo_init //│ (local.get $this) //│ (local.get $x))) //│ (return //│ (local.get $this)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (call $Foo_ctor //│ (ref.i31 //│ (i32.const 42)))) //│ (struct.get $Foo $y //│ (ref.cast (ref $Foo) //│ (local.get $tmp)))))) //│ (elem $Foo_init declare func $Foo_init) //│ (elem $Foo_ctor declare func $Foo_ctor) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 42 :wat object O with val x = 1 val y = this.x O.y //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $O (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any))) (field $y (mut (ref null any)))))) //│ (type $O_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $O_ctor (func (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (type $start (func)) //│ (global $O$inst (export "O$inst") (mut (ref null $O)) (ref.null $O)) //│ (func $O_init (type $O_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $O $x //│ (ref.cast (ref $O) //│ (local.get $this)) //│ (ref.i31 //│ (i32.const 1))) //│ (block //│ (struct.set $O $y //│ (ref.cast (ref $O) //│ (local.get $this)) //│ (struct.get $O $x //│ (ref.cast (ref $O) //│ (local.get $this)))) //│ (nop))) //│ (return //│ (local.get $this)))) //│ (func $O_ctor (type $O_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $O)) //│ (struct.set $O $$tag //│ (ref.cast (ref $O) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $O_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $start (type $start) //│ (block //│ (global.set $O$inst //│ (ref.cast (ref null $O) //│ (call $O_ctor))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (struct.get $O $y //│ (ref.cast (ref $O) //│ (global.get $O$inst))))) //│ (elem $O_init declare func $O_init) //│ (elem $O_ctor declare func $O_ctor) //│ (elem $start declare func $start) //│ (elem $entry declare func $entry) //│ (start $start)) //│ Wasm result: //│ = 1 :ge fun bar() = 42 fun foo() = bar foo()() //│ ╔══[COMPILATION ERROR] Returning function instances is not supported //│ ║ l.304: fun foo() = bar //│ ╙── ^^^ //│ ═══[COMPILATION ERROR] Expected static function reference in Call(...) expression ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Binaryen.mls ================================================ :js :silent // Helper functions import "binaryen" fun compileWatToWasm(wat) = let mod = binaryen.parseText(wat) mod.setFeatures(binaryen.Features.All) assert mod.validate() != false val modBuf = mod.emitBinary() mod.dispose() modBuf fun instantiateWasm(wasm, importObj) = assert WebAssembly.validate(wasm) let mod = new! WebAssembly.Module(wasm) let inst = new! WebAssembly.Instance(mod, importObj) inst // Parsing a WAT, compiling it to a Wasm bianry, and executing it let wasmMod = compileWatToWasm of """ (module (type $Foo (struct (field $i i32) (field $r i31ref))) (func (export "main") (result i32) (local $tmp (ref $Foo)) (block (result i32) (local.set $tmp (struct.new $Foo (i32.const 1) (ref.i31 (i32.const 2)))) (i32.add (struct.get $Foo $i (local.get $tmp)) (i31.get_s (struct.get $Foo $r (local.get $tmp))))))) """ let inst = instantiateWasm of wasmMod, {} inst.exports.main() |> print //│ > 3 // Building a module using Binaryen APIs, and executing it let mod = new! binaryen.Module() mod.setFeatures(binaryen.Features.All) let fooTb = new! binaryen.TypeBuilder(1) fooTb.setStructType of 0, tuple of ( "type": binaryen.i32 packedType: binaryen.notPacked mutable: false ) ( "type": binaryen.i31ref packedType: binaryen.notPacked mutable: false ) let foo = fooTb.buildAndDispose().0 mod.addFunction of "main" binaryen.none binaryen.i32 [foo] mod.block of null tuple of mod.local.set of 0, mod.struct.new of tuple of mod.i32.const(1) mod.ref.i31 of mod.i32.const of 2 foo mod.i32.add of mod.struct.get of 0, mod.local.get(0, foo) foo false mod.i31.get_s of mod.struct.get of 1, mod.local.get(0, foo) foo false binaryen.i32 mod.addFunctionExport of "main", "main" Predef.js_assert of mod.validate() let wasmMod = mod.emitBinary() mod.dispose() let inst = instantiateWasm of wasmMod, {} inst.exports.main() |> print //│ > 3 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls ================================================ :global :wasm :wat 1 + 2 //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (call $plus_impl //│ (ref.i31 //│ (i32.const 1)) //│ (ref.i31 //│ (i32.const 2)))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 3 :wat -2 //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $neg_impl (func (param $arg (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "neg_impl" (func $neg_impl (type $neg_impl))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (call $neg_impl //│ (ref.i31 //│ (i32.const 2)))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = -2 not true //│ Wasm result: //│ = 0 2 - 1 //│ Wasm result: //│ = 1 3 * 4 //│ Wasm result: //│ = 12 4 / 2 //│ Wasm result: //│ = 2 4 % 3 //│ Wasm result: //│ = 1 :wat 1 === 1 //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $eq_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "eq_impl" (func $eq_impl (type $eq_impl))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (call $eq_impl //│ (ref.i31 //│ (i32.const 1)) //│ (ref.i31 //│ (i32.const 1)))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 1 1 === 2 //│ Wasm result: //│ = 0 1 !== 1 //│ Wasm result: //│ = 0 1 !== 2 //│ Wasm result: //│ = 1 1 < 1 //│ Wasm result: //│ = 0 1 <= 1 //│ Wasm result: //│ = 1 1 < 2 //│ Wasm result: //│ = 1 1 <=2 //│ Wasm result: //│ = 1 2 > 1 //│ Wasm result: //│ = 1 2 >= 1 //│ Wasm result: //│ = 1 2 > 2 //│ Wasm result: //│ = 0 2 >= 2 //│ Wasm result: //│ = 1 +3 //│ Wasm result: //│ = 3 -1 + 2 //│ Wasm result: //│ = 1 -1 - 2 //│ Wasm result: //│ = -3 -6 / 3 //│ Wasm result: //│ = -2 -6 % 4 //│ Wasm result: //│ = -2 -1 > 2 //│ Wasm result: //│ = 0 -1 > -3 //│ Wasm result: //│ = 1 - 1>= -1 //│ Wasm result: //│ = 1 not false //│ Wasm result: //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/ClassInheritance.mls ================================================ :global :wasm class A(val x) class B(val y) extends A(y + 1) let b = B(1) b.A#x //│ Wasm result: //│ = 2 class A(val x) class B(val y) extends A(1) B(2).A#x //│ Wasm result: //│ = 1 :wat // Parent type/init/ctor appear before child let c = Child(2) class Child(val y) extends Parent(y + 1) class Parent(val x) c.Parent#x //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Parent (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any)))))) //│ (type $Child (sub $Parent (struct (field $$tag (mut i32)) (field $x (mut (ref null any))) (field $y (mut (ref null any)))))) //│ (type $Parent_init (func (param $this (ref null any)) (param $x (ref null any)) (result (ref null any)))) //│ (type $Child_init (func (param $this (ref null any)) (param $y (ref null any)) (result (ref null any)))) //│ (type $Parent_ctor (func (param $x (ref null any)) (result (ref null any)))) //│ (type $Child_ctor (func (param $y (ref null any)) (result (ref null any)))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (global $c (export "c") (mut (ref null any)) (ref.null any)) //│ (func $Parent_init (type $Parent_init) (param $this (ref null any)) (param $x (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Parent $x //│ (ref.cast (ref $Parent) //│ (local.get $this)) //│ (local.get $x)) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Child_init (type $Child_init) (param $this (ref null any)) (param $y (ref null any)) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) //│ (block //│ (block //│ (local.set $tmp //│ (call $plus_impl //│ (local.get $y) //│ (ref.i31 //│ (i32.const 1)))) //│ (nop)) //│ (drop //│ (call $Parent_init //│ (local.get $this) //│ (local.get $tmp)))) //│ (block //│ (struct.set $Child $y //│ (ref.cast (ref $Child) //│ (local.get $this)) //│ (local.get $y)) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Parent_ctor (export "Parent") (type $Parent_ctor) (param $x (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Parent)) //│ (struct.set $Parent $$tag //│ (ref.cast (ref $Parent) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Parent_init //│ (local.get $this) //│ (local.get $x))) //│ (return //│ (local.get $this)))) //│ (func $Child_ctor (export "Child") (type $Child_ctor) (param $y (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Child)) //│ (struct.set $Child $$tag //│ (ref.cast (ref $Child) //│ (local.get $this)) //│ (i32.const 2)) //│ (drop //│ (call $Child_init //│ (local.get $this) //│ (local.get $y))) //│ (return //│ (local.get $this)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block (result (ref null any)) //│ (global.set $c //│ (call $Child_ctor //│ (ref.i31 //│ (i32.const 2)))) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (nop) //│ (struct.get $Parent $x //│ (ref.cast (ref $Parent) //│ (global.get $c))))))) //│ (elem $Parent_init declare func $Parent_init) //│ (elem $Child_init declare func $Child_init) //│ (elem $Parent_ctor declare func $Parent_ctor) //│ (elem $Child_ctor declare func $Child_ctor) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 3 class A(val x) class B(val y) extends A(y + 1) class C(val z) extends B(z + 1) let c = C(1) c.A#x //│ Wasm result: //│ = 3 class A() class B() extends A() B() is A //│ Wasm result: //│ = 1 class B() extends A() class A() let b = B() if b is A then 1 else 0 //│ Wasm result: //│ = 1 class A(val x) class B(val y) extends A(1) if B(2) is A(a) then a else 0 //│ Wasm result: //│ = 1 class Bar(val a) class Foo(val x, val y) extends Bar(x + y) if Foo(1,2) is Foo(x,y) then Foo(1,2).Bar#a //│ Wasm result: //│ = 3 class Bar(val a) class Foo(val x) extends Bar(x * 2) fun getXor(f) = if f is Foo(z) then f.Bar#a else 0 getXor(Foo(42)) //│ Wasm result: //│ = 84 class Foo(val x) class Bar(val y) extends Foo(y + y) class Baz(val z) extends Bar(z - 1) if Baz(10) is Foo(a) then a Bar(b) then b Baz(c) then c else 0 //│ Wasm result: //│ = 18 class Bar(val y) class Baz(val z) extends Bar(not z) if Baz(true) is Bar(a) then if a then Bar(a).Bar#y else a Baz(b) then b else 2 //│ Wasm result: //│ = 0 class A() class B() B() is A //│ Wasm result: //│ = 0 // Unrelated top-level classes preserve their source order in type declaration. :wat class A class B //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $A (sub $Object (struct (field $$tag (mut i32))))) //│ (type $B (sub $Object (struct (field $$tag (mut i32))))) //│ (type $A_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $B_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $A_ctor (func (result (ref null any)))) //│ (type $B_ctor (func (result (ref null any)))) //│ (type $Unit (sub $Object (struct (field $$tag (mut i32))))) //│ (type $Unit_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $Unit_ctor (func (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Unit$inst (export "Unit$inst") (mut (ref null $Unit)) (ref.null $Unit)) //│ (func $A_init (type $A_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (nop) //│ (return //│ (local.get $this)))) //│ (func $B_init (type $B_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (nop) //│ (return //│ (local.get $this)))) //│ (func $A_ctor (export "A") (type $A_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $A)) //│ (struct.set $A $$tag //│ (ref.cast (ref $A) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $A_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $B_ctor (export "B") (type $B_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $B)) //│ (struct.set $B $$tag //│ (ref.cast (ref $B) //│ (local.get $this)) //│ (i32.const 2)) //│ (drop //│ (call $B_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $Unit_init (type $Unit_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (nop) //│ (return //│ (local.get $this)))) //│ (func $Unit_Unit (type $Unit_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Unit)) //│ (struct.set $Unit $$tag //│ (ref.cast (ref $Unit) //│ (local.get $this)) //│ (i32.const 3)) //│ (drop //│ (call $Unit_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $start (type $start) //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call $Unit_Unit))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block (result (ref null any)) //│ (block //│ (nop) //│ (block //│ (nop) //│ (nop))) //│ (global.get $Unit$inst))) //│ (elem $A_init declare func $A_init) //│ (elem $B_init declare func $B_init) //│ (elem $A_ctor declare func $A_ctor) //│ (elem $B_ctor declare func $B_ctor) //│ (elem $Unit_init declare func $Unit_init) //│ (elem $Unit_Unit declare func $Unit_Unit) //│ (elem $start declare func $start) //│ (elem $entry declare func $entry) //│ (start $start)) //│ Wasm result: //│ = {} :ge class A extends B class B extends A //│ ╔══[COMPILATION ERROR] Inheritance cycles are not supported. //│ ║ l.307: class A extends B //│ ╙── ^^^^^^^^^^^^^^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls ================================================ :global :wasm :wat class A(val x) with fun get() = x let a = A(1) a.A#get() + A(2).get() //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $A (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any)))))) //│ (type $A_init (func (param $this (ref null any)) (param $x (ref null any)) (result (ref null any)))) //│ (type $A_ctor (func (param $x (ref null any)) (result (ref null any)))) //│ (type $A_get (func (param $this (ref null any)) (result (ref null any)))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (global $a (export "a") (mut (ref null any)) (ref.null any)) //│ (func $A_init (type $A_init) (param $this (ref null any)) (param $x (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $A $x //│ (ref.cast (ref $A) //│ (local.get $this)) //│ (local.get $x)) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $A_ctor (export "A") (type $A_ctor) (param $x (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $A)) //│ (struct.set $A $$tag //│ (ref.cast (ref $A) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $A_init //│ (local.get $this) //│ (local.get $x))) //│ (return //│ (local.get $this)))) //│ (func $A_get (type $A_get) (param $this (ref null any)) (result (ref null any)) //│ (return //│ (struct.get $A $x //│ (ref.cast (ref $A) //│ (local.get $this))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (local $tmp1 (ref null any)) //│ (local $tmp2 (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (global.set $a //│ (call $A_ctor //│ (ref.i31 //│ (i32.const 1)))) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (call $A_get //│ (global.get $a))) //│ (block (result (ref null any)) //│ (local.set $tmp1 //│ (call $A_ctor //│ (ref.i31 //│ (i32.const 2)))) //│ (block (result (ref null any)) //│ (local.set $tmp2 //│ (call $A_get //│ (local.get $tmp1))) //│ (call $plus_impl //│ (local.get $tmp) //│ (local.get $tmp2)))))))) //│ (elem $A_init declare func $A_init) //│ (elem $A_ctor declare func $A_ctor) //│ (elem $A_get declare func $A_get) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 3 class A(val x) with fun get = x let a = A(1) a.A#get //│ Wasm result: //│ = 1 class A(val x) with fun add(y) = x + y A(1).add(2) //│ Wasm result: //│ = 3 class C(val x) with fun get() = double fun double = x + this.x C(2).get() //│ Wasm result: //│ = 4 class SumDown() with fun sum(n) = if n === 0 then 0 else n + sum(n - 1) SumDown().sum(5) //│ Wasm result: //│ = 15 class Parity() with fun even(n) = if n === 0 then true else odd(n - 1) fun odd(n) = if n === 0 then false else even(n - 1) Parity().even(6) //│ Wasm result: //│ = 1 class Box(val x) with fun get() = x fun inc() = Box(x + 1).get() Box(2).inc() //│ Wasm result: //│ = 3 class Counter(val x) with fun inc() = Counter(x + 1) fun double() = Counter(x + x) fun get() = x let c = Counter(1) c.Counter#inc().Counter#double().Counter#get() //│ Wasm result: //│ = 4 class Box(val x) with fun mk() = Next(x + 1).get() class Next(val y) with fun get() = y Box(41).mk() //│ Wasm result: //│ = 42 class Counter(val x) with fun inc() = set x += 1 this fun double() = set x *= 2 this fun get() = x let c = Counter(1) c.Counter#inc().Counter#double().Counter#get() //│ Wasm result: //│ = 4 :todo let d = Counter(c.Counter#get()) //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing field `term:Counter/get` in struct `member:Counter` with type `(type $Counter (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any))))))` :todo d.Counter#inc().Counter#double().Counter#get() //│ ╔══[COMPILATION ERROR] Cannot find variable `d` (VarSymbol) in local or global scope. //│ ║ l.172: let d = Counter(c.Counter#get()) //│ ╙── ^ //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing type definition for symbol `member:Counter` :todo c.Counter#get() //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Missing type definition for symbol `member:Counter` class GetterOnly() with fun f = 1 GetterOnly().f() //│ Wasm result: //│ = 1 :ge class A(val x) with fun get() = x A(1).get //│ ╔══[COMPILATION ERROR] `member:get` is neither a field access nor a callable method //│ ║ l.197: A(1).get //│ ╙── ^^^^ ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls ================================================ :global :wasm if 1 === 2 do 2 + 2 //│ Wasm result: //│ = {} :wat let i = 0 in while i < 10 then set i += 1 i //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $lt_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $Unit (sub $Object (struct (field $$tag (mut i32))))) //│ (type $Unit_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $Unit_ctor (func (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (type $start (func)) //│ (import "system" "lt_impl" (func $lt_impl (type $lt_impl))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (global $Unit$inst (export "Unit$inst") (mut (ref null $Unit)) (ref.null $Unit)) //│ (func $Unit_init (type $Unit_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (nop) //│ (return //│ (local.get $this)))) //│ (func $Unit_Unit (type $Unit_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Unit)) //│ (struct.set $Unit $$tag //│ (ref.cast (ref $Unit) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Unit_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $start (type $start) //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call $Unit_Unit))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $i (ref null any)) //│ (local $scrut (ref null any)) //│ (local $tmp (ref null any)) //│ (local $matchRes (ref null any)) //│ (block (result (ref null any)) //│ (local.set $i //│ (ref.i31 //│ (i32.const 0))) //│ (block (result (ref null any)) //│ (block $lbl //│ (loop $lbl_cont //│ (drop //│ (block (result (ref null any)) //│ (local.set $scrut //│ (call $lt_impl //│ (local.get $i) //│ (ref.i31 //│ (i32.const 10)))) //│ (block (result (ref null any)) //│ (block $match //│ (local.set $matchRes //│ (ref.null any)) //│ (if //│ (i32.eq //│ (i31.get_s //│ (ref.cast (ref null i31) //│ (local.get $scrut))) //│ (i32.const 1)) //│ (then //│ (block $arm //│ (block //│ (block //│ (local.set $tmp //│ (call $plus_impl //│ (local.get $i) //│ (ref.i31 //│ (i32.const 1)))) //│ (block //│ (local.set $i //│ (local.get $tmp)) //│ (br $lbl_cont))) //│ (local.set $matchRes //│ (global.get $Unit$inst))) //│ (br $match)))) //│ (block //│ (nop) //│ (local.set $matchRes //│ (global.get $Unit$inst)))) //│ (local.get $matchRes)))))) //│ (local.get $i)))) //│ (elem $Unit_init declare func $Unit_init) //│ (elem $Unit_Unit declare func $Unit_Unit) //│ (elem $start declare func $start) //│ (elem $entry declare func $entry) //│ (start $start)) //│ Wasm result: //│ = 10 fun f() = while true do return 42 f() //│ Wasm result: //│ = 42 let x = true while x do set x = false //│ Wasm result: //│ = {} fun foo() = let x = 1 while false do set x = 0 foo() //│ Wasm result: //│ = {} let c = 0 let x = 0 while x < 5 do let y = 0 while y < 5 do set c += 1 set y += 1 set x += 1 c //│ Wasm result: //│ = 25 fun nonTailMatchReturn(x) = if x is 0 then return 100 1 then 10 else 20 x + 1 nonTailMatchReturn(0) //│ Wasm result: //│ = 100 fun tailMatchReturn(x) = if x is 0 then return 7 1 then x + 41 else 99 tailMatchReturn(0) //│ Wasm result: //│ = 7 fun loopMatchReturn() = let i = 0 in while true do if i is 3 then return i else set i += 1 loopMatchReturn() //│ Wasm result: //│ = 3 :wat fun tailMatchAllValue(x) = if x is 0 then 10 1 then 20 else 30 tailMatchAllValue(1) //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $tailMatchAllValue (func (param $x (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (func $tailMatchAllValue (export "tailMatchAllValue") (type $tailMatchAllValue) (param $x (ref null any)) (result (ref null any)) //│ (local $matchRes (ref null any)) //│ (block (result (ref null any)) //│ (block $match //│ (local.set $matchRes //│ (ref.null any)) //│ (if //│ (i32.eq //│ (i31.get_s //│ (ref.cast (ref null i31) //│ (local.get $x))) //│ (i32.const 0)) //│ (then //│ (block $arm //│ (return //│ (ref.i31 //│ (i32.const 10))) //│ (br $match)))) //│ (if //│ (i32.eq //│ (i31.get_s //│ (ref.cast (ref null i31) //│ (local.get $x))) //│ (i32.const 1)) //│ (then //│ (block $arm //│ (return //│ (ref.i31 //│ (i32.const 20))) //│ (br $match)))) //│ (return //│ (ref.i31 //│ (i32.const 30)))) //│ (local.get $matchRes))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tailMatchAllValue (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (call $tailMatchAllValue //│ (ref.i31 //│ (i32.const 1))))) //│ (elem $tailMatchAllValue declare func $tailMatchAllValue) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 20 fun tailMatchStmtBranch(x) = let y = 0 in if x is 0 then set y += 1 else 42 tailMatchStmtBranch(0) //│ Wasm result: //│ = {} fun tailMatchStmtDefault(x) = let y = 0 in if x is 0 then 11 else set y += 1 tailMatchStmtDefault(2) //│ Wasm result: //│ = {} fun nonTailStmtMatch(x) = let y = 0 in if x is 0 then set y += 1 else 5 y nonTailStmtMatch(0) //│ Wasm result: //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Exceptions.mls ================================================ :global :wasm :wat :re throw 1 //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $mlx_exn (func (param $ex (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (tag $mlx_exn (export "mlx_exn") (type $mlx_exn)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (throw $mlx_exn //│ (ref.i31 //│ (i32.const 1)))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ ═══[RUNTIME ERROR] 1 :re let x = 2 throw x //│ Wasm result: //│ ═══[RUNTIME ERROR] 2 :re throw false //│ Wasm result: //│ ═══[RUNTIME ERROR] 0 :ge let x = 1 set x += 1 in x //│ ═══[COMPILATION ERROR] WatBuilder::returningTerm for TryBlock(...) not implemented yet ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/MainFunctions.mls ================================================ :global :wasm let x = 0 //│ Wasm result: //│ = {} class Foo(val x) //│ Wasm result: //│ = {} object Bar with val x = 1 //│ Wasm result: //│ = {} fun foo() = 42 //│ Wasm result: //│ = {} :re :wat let x = 2 throw x //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $mlx_exn (func (param $ex (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (tag $mlx_exn (export "mlx_exn") (type $mlx_exn)) //│ (global $x (export "x") (mut (ref null any)) (ref.null any)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block //│ (global.set $x //│ (ref.i31 //│ (i32.const 2))) //│ (throw $mlx_exn //│ (global.get $x)))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ ═══[RUNTIME ERROR] 2 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Matching.mls ================================================ :global :wasm if true then 0 else 2 //│ Wasm result: //│ = 0 if false then 1 else 0 //│ Wasm result: //│ = 0 class Foo(val x) if Foo(42) is Foo(a) then 0 else 1 //│ Wasm result: //│ = 0 class Foo(val x) if Foo(42) is Foo(a) then a else 1 //│ Wasm result: //│ = 42 class Foo(val x) if 1 is Foo(a) then a else 1 //│ Wasm result: //│ = 1 class Bar(val y) class Baz(val z) if Baz(10) is Bar(a) then a Baz(b) then 1 else 0 //│ Wasm result: //│ = 1 :wat class Bar(val y) class Baz(val z) if Bar(true) is Bar(a) then if a then 3 else 4 Baz(b) then b else 2 //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Bar (sub $Object (struct (field $$tag (mut i32)) (field $y (mut (ref null any)))))) //│ (type $Baz (sub $Object (struct (field $$tag (mut i32)) (field $z (mut (ref null any)))))) //│ (type $Bar_init (func (param $this (ref null any)) (param $y (ref null any)) (result (ref null any)))) //│ (type $Baz_init (func (param $this (ref null any)) (param $z (ref null any)) (result (ref null any)))) //│ (type $Bar_ctor (func (param $y (ref null any)) (result (ref null any)))) //│ (type $Baz_ctor (func (param $z (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (func $Bar_init (type $Bar_init) (param $this (ref null any)) (param $y (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Bar $y //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (local.get $y)) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Baz_init (type $Baz_init) (param $this (ref null any)) (param $z (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Baz $z //│ (ref.cast (ref $Baz) //│ (local.get $this)) //│ (local.get $z)) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Bar_ctor (export "Bar") (type $Bar_ctor) (param $y (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Bar)) //│ (struct.set $Bar $$tag //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Bar_init //│ (local.get $this) //│ (local.get $y))) //│ (return //│ (local.get $this)))) //│ (func $Baz_ctor (export "Baz") (type $Baz_ctor) (param $z (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Baz)) //│ (struct.set $Baz $$tag //│ (ref.cast (ref $Baz) //│ (local.get $this)) //│ (i32.const 2)) //│ (drop //│ (call $Baz_init //│ (local.get $this) //│ (local.get $z))) //│ (return //│ (local.get $this)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $scrut (ref null any)) //│ (local $a (ref null any)) //│ (local $b (ref null any)) //│ (local $arg$Baz$0$ (ref null any)) //│ (local $arg$Bar$0$ (ref null any)) //│ (local $matchRes (ref null any)) //│ (local $matchRes1 (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $scrut //│ (call $Bar_ctor //│ (ref.i31 //│ (i32.const 1)))) //│ (block (result (ref null any)) //│ (block $match //│ (local.set $matchRes //│ (ref.null any)) //│ (if //│ (ref.test (ref null $Object) //│ (local.get $scrut)) //│ (then //│ (if //│ (i32.eq //│ (struct.get $Object $$tag //│ (ref.cast (ref $Object) //│ (local.get $scrut))) //│ (i32.const 1)) //│ (then //│ (block $arm //│ (local.set $matchRes //│ (block (result (ref null any)) //│ (local.set $arg$Bar$0$ //│ (struct.get $Bar $y //│ (ref.cast (ref $Bar) //│ (local.get $scrut)))) //│ (block (result (ref null any)) //│ (local.set $a //│ (local.get $arg$Bar$0$)) //│ (block (result (ref null any)) //│ (block $match1 //│ (local.set $matchRes1 //│ (ref.null any)) //│ (if //│ (i32.eq //│ (i31.get_s //│ (ref.cast (ref null i31) //│ (local.get $a))) //│ (i32.const 1)) //│ (then //│ (block $arm //│ (local.set $matchRes1 //│ (ref.i31 //│ (i32.const 3))) //│ (br $match1)))) //│ (local.set $matchRes1 //│ (ref.i31 //│ (i32.const 4)))) //│ (local.get $matchRes1))))) //│ (br $match)))))) //│ (if //│ (ref.test (ref null $Object) //│ (local.get $scrut)) //│ (then //│ (if //│ (i32.eq //│ (struct.get $Object $$tag //│ (ref.cast (ref $Object) //│ (local.get $scrut))) //│ (i32.const 2)) //│ (then //│ (block $arm //│ (local.set $matchRes //│ (block (result (ref null any)) //│ (local.set $arg$Baz$0$ //│ (struct.get $Baz $z //│ (ref.cast (ref $Baz) //│ (local.get $scrut)))) //│ (block (result (ref null any)) //│ (local.set $b //│ (local.get $arg$Baz$0$)) //│ (local.get $b)))) //│ (br $match)))))) //│ (local.set $matchRes //│ (ref.i31 //│ (i32.const 2)))) //│ (local.get $matchRes)))))) //│ (elem $Bar_init declare func $Bar_init) //│ (elem $Baz_init declare func $Baz_init) //│ (elem $Bar_ctor declare func $Bar_ctor) //│ (elem $Baz_ctor declare func $Baz_ctor) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 3 :re class Bar(val y) class Baz(val z) if Baz(10) is Bar(a) then 0 //│ Wasm result: //│ ═══[RUNTIME ERROR] match error :re if false then 1 //│ Wasm result: //│ ═══[RUNTIME ERROR] match error class Foo(val x, val y) if Foo(1,2) is Foo(a,b) then 0 //│ Wasm result: //│ = 0 class Foo(val x) fun getXor(f) = if f is Foo(a) then a else false getXor(Foo(42)) //│ Wasm result: //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/ReplImports.mls ================================================ :global :wasm object Foo with val x = 1 class C(val x) fun inc(x) = x + 1 val step = 2 let counter = 1 let unit = () //│ Wasm result: //│ = {} inc(Foo.x) //│ Wasm result: //│ = 2 if C(7) is C(v) then v else 0 //│ Wasm result: //│ = 7 :wat set counter += step counter //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (import "repl" "counter" (global $counter (mut (ref null any)))) //│ (import "repl" "step" (global $step (mut (ref null any)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (call $plus_impl //│ (global.get $counter) //│ (global.get $step))) //│ (block (result (ref null any)) //│ (global.set $counter //│ (local.get $tmp)) //│ (global.get $counter)))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 3 unit //│ Wasm result: //│ = {} :wat () //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Unit (sub $Object (struct (field $$tag (mut i32))))) //│ (type $entry (func (result (ref null any)))) //│ (import "repl" "Unit$inst" (global $Unit (mut (ref null $Unit)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (global.get $Unit)) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = {} ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls ================================================ :global :wasm :wat let topA = 1 let topB = 2 topA + topB //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (global $topA (export "topA") (mut (ref null any)) (ref.null any)) //│ (global $topB (export "topB") (mut (ref null any)) (ref.null any)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block (result (ref null any)) //│ (global.set $topA //│ (ref.i31 //│ (i32.const 1))) //│ (block (result (ref null any)) //│ (global.set $topB //│ (ref.i31 //│ (i32.const 2))) //│ (call $plus_impl //│ (global.get $topA) //│ (global.get $topB))))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 3 :wat fun chain(x) = let a = x + 1 let b = a + 1 a + b chain(1) //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $plus_impl (func (param $lhs (ref null any)) (param $rhs (ref null any)) (result (ref null any)))) //│ (type $chain (func (param $x (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "plus_impl" (func $plus_impl (type $plus_impl))) //│ (func $chain (export "chain") (type $chain) (param $x (ref null any)) (result (ref null any)) //│ (local $a (ref null any)) //│ (local $b (ref null any)) //│ (block (result (ref null any)) //│ (local.set $a //│ (call $plus_impl //│ (local.get $x) //│ (ref.i31 //│ (i32.const 1)))) //│ (block (result (ref null any)) //│ (local.set $b //│ (call $plus_impl //│ (local.get $a) //│ (ref.i31 //│ (i32.const 1)))) //│ (return //│ (call $plus_impl //│ (local.get $a) //│ (local.get $b)))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $chain (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (call $chain //│ (ref.i31 //│ (i32.const 1))))) //│ (elem $chain declare func $chain) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 5 fun ifLocals(flag) = if flag then let a = 10 a + 1 else let b = 20 b + 2 ifLocals(true) ifLocals(false) //│ Wasm result: //│ = 22 fun litMatch(x) = if x is 0 then let arm = 10 arm 1 then let arm = 20 arm else let arm = 30 arm litMatch(0) litMatch(1) litMatch(9) //│ Wasm result: //│ = 30 fun nestedInArm(x) = if x is 0 then let a = 40 if true then let b = a + 2 b else 0 else 0 nestedInArm(0) //│ Wasm result: //│ = 42 :wat class Foo(val a, val b) (new Foo(42, 1)).Foo#a //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $a (mut (ref null any))) (field $b (mut (ref null any)))))) //│ (type $Foo_init (func (param $this (ref null any)) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)))) //│ (type $Foo_ctor (func (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (func $Foo_init (type $Foo_init) (param $this (ref null any)) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $a)) //│ (block //│ (struct.set $Foo $b //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (local.get $b)) //│ (nop))) //│ (return //│ (local.get $this)))) //│ (func $Foo_ctor (export "Foo") (type $Foo_ctor) (param $a (ref null any)) (param $b (ref null any)) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) //│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Foo_init //│ (local.get $this) //│ (local.get $a) //│ (local.get $b))) //│ (return //│ (local.get $this)))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (call $Foo_ctor //│ (ref.i31 //│ (i32.const 42)) //│ (ref.i31 //│ (i32.const 1)))) //│ (struct.get $Foo $a //│ (ref.cast (ref $Foo) //│ (local.get $tmp)))))) //│ (elem $Foo_init declare func $Foo_init) //│ (elem $Foo_ctor declare func $Foo_ctor) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/SingletonUnit.mls ================================================ :global :wasm :wat () //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Unit (sub $Object (struct (field $$tag (mut i32))))) //│ (type $Unit_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $Unit_ctor (func (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Unit$inst (export "Unit$inst") (mut (ref null $Unit)) (ref.null $Unit)) //│ (func $Unit_init (type $Unit_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (nop) //│ (return //│ (local.get $this)))) //│ (func $Unit_Unit (type $Unit_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Unit)) //│ (struct.set $Unit $$tag //│ (ref.cast (ref $Unit) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Unit_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $start (type $start) //│ (block //│ (global.set $Unit$inst //│ (ref.cast (ref null $Unit) //│ (call $Unit_Unit))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (global.get $Unit$inst)) //│ (elem $Unit_init declare func $Unit_init) //│ (elem $Unit_Unit declare func $Unit_Unit) //│ (elem $start declare func $start) //│ (elem $entry declare func $entry) //│ (start $start)) //│ Wasm result: //│ = {} if true then () else 0 //│ Wasm result: //│ = {} fun foo() = () foo() //│ Wasm result: //│ = {} let x = 1 () //│ Wasm result: //│ = {} [(), 1].[0] //│ Wasm result: //│ = {} object O with val unit = () O.unit //│ Wasm result: //│ = {} // confirms no start function when singletons are not present :wat let x = 1 x //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $entry (func (result (ref null any)))) //│ (global $x (export "x") (mut (ref null any)) (ref.null any)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block (result (ref null any)) //│ (global.set $x //│ (ref.i31 //│ (i32.const 1))) //│ (global.get $x))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Singletons.mls ================================================ :global :wasm :wat object Foo with val x = 1 object Bar with val y = 2 Bar.y //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $Foo (sub $Object (struct (field $$tag (mut i32)) (field $x (mut (ref null any)))))) //│ (type $Bar (sub $Object (struct (field $$tag (mut i32)) (field $y (mut (ref null any)))))) //│ (type $Foo_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $Bar_init (func (param $this (ref null any)) (result (ref null any)))) //│ (type $Foo_ctor (func (result (ref null any)))) //│ (type $Bar_ctor (func (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (type $start (func)) //│ (global $Foo$inst (export "Foo$inst") (mut (ref null $Foo)) (ref.null $Foo)) //│ (global $Bar$inst (export "Bar$inst") (mut (ref null $Bar)) (ref.null $Bar)) //│ (func $Foo_init (type $Foo_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Foo $x //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (ref.i31 //│ (i32.const 1))) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Bar_init (type $Bar_init) (param $this (ref null any)) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block //│ (struct.set $Bar $y //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (ref.i31 //│ (i32.const 2))) //│ (nop)) //│ (return //│ (local.get $this)))) //│ (func $Foo_ctor (type $Foo_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Foo)) //│ (struct.set $Foo $$tag //│ (ref.cast (ref $Foo) //│ (local.get $this)) //│ (i32.const 1)) //│ (drop //│ (call $Foo_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $Bar_ctor (type $Bar_ctor) (result (ref null any)) //│ (local $this (ref null any)) //│ (block (result (ref null any)) //│ (local.set $this //│ (struct.new_default $Bar)) //│ (struct.set $Bar $$tag //│ (ref.cast (ref $Bar) //│ (local.get $this)) //│ (i32.const 2)) //│ (drop //│ (call $Bar_init //│ (local.get $this))) //│ (return //│ (local.get $this)))) //│ (func $start (type $start) //│ (block //│ (global.set $Foo$inst //│ (ref.cast (ref null $Foo) //│ (call $Foo_ctor))) //│ (global.set $Bar$inst //│ (ref.cast (ref null $Bar) //│ (call $Bar_ctor))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (block (result (ref null any)) //│ (nop) //│ (block (result (ref null any)) //│ (nop) //│ (struct.get $Bar $y //│ (ref.cast (ref $Bar) //│ (global.get $Bar$inst)))))) //│ (elem $Foo_init declare func $Foo_init) //│ (elem $Bar_init declare func $Bar_init) //│ (elem $Foo_ctor declare func $Foo_ctor) //│ (elem $Bar_ctor declare func $Bar_ctor) //│ (elem $start declare func $start) //│ (elem $entry declare func $entry) //│ (start $start)) //│ Wasm result: //│ = 2 object Example with val a = [10, 20] Example.a.[1] //│ Wasm result: //│ = 20 object O with val x = 1 val gy = x O.gy //│ Wasm result: //│ = 1 ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Strings.mls ================================================ :global :wasm "Hello World!" //│ Wasm result: //│ = "Hello World!" "" //│ Wasm result: //│ = "" :wat let dup1 = "dup" let dup2 = "dup" [dup1, dup2].[1] //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $mlx_str_from_utf16 (func (param $glob_offset (ref null any)) (param $len (ref null any)) (result (ref null any)))) //│ (type $TupleArray (array (ref null any))) //│ (type $TupleArrayMut (array (mut (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "mlx_str_from_utf16" (func $mlx_str_from_utf16 (type $mlx_str_from_utf16))) //│ (import "system" "mem" (memory $mem 1)) //│ (global $dup1 (export "dup1") (mut (ref null any)) (ref.null any)) //│ (global $dup2 (export "dup2") (mut (ref null any)) (ref.null any)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tmp (ref null any)) //│ (local $tuple (ref null any)) //│ (block (result (ref null any)) //│ (global.set $dup1 //│ (call $mlx_str_from_utf16 //│ (ref.i31 //│ (i32.const 0)) //│ (ref.i31 //│ (i32.const 6)))) //│ (block (result (ref null any)) //│ (global.set $dup2 //│ (call $mlx_str_from_utf16 //│ (ref.i31 //│ (i32.const 0)) //│ (ref.i31 //│ (i32.const 6)))) //│ (block (result (ref null any)) //│ (local.set $tmp //│ (array.new_fixed $TupleArray 2 //│ (global.get $dup1) //│ (global.get $dup2))) //│ (if (result (ref null any)) //│ (ref.test (ref null $TupleArrayMut) //│ (local.tee $tuple //│ (local.get $tmp))) //│ (then //│ (array.get $TupleArrayMut //│ (ref.cast (ref $TupleArrayMut) //│ (local.get $tuple)) //│ (i32.const 1))) //│ (else //│ (array.get $TupleArray //│ (ref.cast (ref $TupleArray) //│ (local.get $tuple)) //│ (i32.const 1)))))))) //│ (data $dup (i32.const 0) "\64\00\75\00\70\00") //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = "dup" let s = "mlx" s //│ Wasm result: //│ = "mlx" let t = ["a", "b", "c"] t.[1] //│ Wasm result: //│ = "b" // For testing that function indices are stable fun id(x) = x let s2 = "hello" if true then id(1) else s2 //│ Wasm result: //│ = 1 // Identifier Truncation :wat "abcdefghijklmnopqrstuvwxyz" //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $mlx_str_from_utf16 (func (param $glob_offset (ref null any)) (param $len (ref null any)) (result (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (import "system" "mlx_str_from_utf16" (func $mlx_str_from_utf16 (type $mlx_str_from_utf16))) //│ (import "system" "mem" (memory $mem 1)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (call $mlx_str_from_utf16 //│ (ref.i31 //│ (i32.const 0)) //│ (ref.i31 //│ (i32.const 52)))) //│ (data $abcdefghijklmnop (i32.const 0) "\61\00\62\00\63\00\64\00\65\00\66\00\67\00\68\00\69\00\6a\00\6b\00\6c\00\6d\00\6e\00\6f\00\70\00\71\00\72\00\73\00\74\00\75\00\76\00\77\00\78\00\79\00\7a\00") //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = "abcdefghijklmnopqrstuvwxyz" ================================================ FILE: hkmc2/shared/src/test/mlscript/wasm/Tuples.mls ================================================ :global :wasm [] //│ Wasm result: //│ = {} [0, 1, 2] //│ Wasm result: //│ = {} fun makePair(x, y) = [x, y] makePair(1, 2).[1] //│ Wasm result: //│ = 2 class Foo(val x, val y) let b = [Foo(0, 1), true, 2] if b.[0] is Foo(x, y) then x else 3 //│ Wasm result: //│ = 0 let nums = [1, 2, 3] nums.[-1] //│ Wasm result: //│ = 3 let nums = mut [1, 2, 3] set nums.[0] = 0 nums.[0] //│ Wasm result: //│ = 0 :re let nums = [1, 2, 3] set nums.[0] = 0 nums.[0] //│ Wasm result: //│ ═══[RUNTIME ERROR] RuntimeError: illegal cast :re let nums = [1, 2, 3] nums.[3] //│ Wasm result: //│ ═══[RUNTIME ERROR] RuntimeError: array element access out of bounds :wat let c = [10, 20, 30, 40] c.[3] //│ Wat: //│ (module //│ (type $Object (sub (struct (field $$tag (mut i32))))) //│ (type $TupleArray (array (ref null any))) //│ (type $TupleArrayMut (array (mut (ref null any)))) //│ (type $entry (func (result (ref null any)))) //│ (global $c (export "c") (mut (ref null any)) (ref.null any)) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tuple (ref null any)) //│ (block (result (ref null any)) //│ (global.set $c //│ (array.new_fixed $TupleArray 4 //│ (ref.i31 //│ (i32.const 10)) //│ (ref.i31 //│ (i32.const 20)) //│ (ref.i31 //│ (i32.const 30)) //│ (ref.i31 //│ (i32.const 40)))) //│ (if (result (ref null any)) //│ (ref.test (ref null $TupleArrayMut) //│ (local.tee $tuple //│ (global.get $c))) //│ (then //│ (array.get $TupleArrayMut //│ (ref.cast (ref $TupleArrayMut) //│ (local.get $tuple)) //│ (i32.const 3))) //│ (else //│ (array.get $TupleArray //│ (ref.cast (ref $TupleArray) //│ (local.get $tuple)) //│ (i32.const 3)))))) //│ (elem $entry declare func $entry)) //│ Wasm result: //│ = 40 ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/.gitignore ================================================ *.mjs !/Predef.mjs !/Runtime.mjs !/RuntimeJS.mjs !/apps/parsing/vendors/railroad/railroad.mjs !/NoFreeze.mjs ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Block.mls ================================================ import "./Predef.mls" import "./Option.mls" import "./StrOps.mls" import "./Runtime.mls" open Predef open StrOps open Option module Block with... type Opt[A] = Option[A] // dependancies referenced in Block classes, referencing implementation in Term.mls type Literal = null | undefined | Str | Int | Num | Bool type ParamList = Array[Symbol] class Symbol(val name: Str) // this is so that we're able to retrieve information about the class from the symbol class ClassSymbol(val name: Str) extends Symbol(name) class VirtualClassSymbol(val name: Str) extends ClassSymbol(name) class ConcreteClassSymbol(val name: Str, val value: Class, val paramsOpt: Opt[ParamList], val auxParams: Array[ParamList]) extends ClassSymbol(name) class ModuleSymbol(val name: Str, val value: Class) extends Symbol(name) class NoSymbol() extends Symbol("$no_symbol$") class Arm(val cse: Case, val body: Block) fun isPrimitiveType(sym: Symbol) = if sym.name is "Str" then true "Int" then true "Num" then true "Bool" then true else false fun isPrimitiveTypeOf(sym: Symbol, l: Literal) = if [sym.name, l] is ["Str", l] and l is Str then true ["Int", i] and i is Int then true ["Num", n] and n is Num then true ["Bool", b] and b is Bool then true else false // Classes defined in Block.scala class Arg(val value: Path) class Case with constructor Lit(val lit: Literal) Cls(val cls: Symbol, val path: Path) Tup(val len: Int) class Result with constructor Call(val _fun: Path, val args: Array[Arg]) Instantiate(val cls: Path, val args: Array[Arg]) // assume immutable Tuple(val elems: Array[Arg]) // assume immutable class Path extends Result with constructor Select(val qual: Path, val name: Symbol) DynSelect(val qual: Path, val fld: Path, val arrayIdx: Bool) // is arrayIdx used? ValueRef(val l: Symbol) ValueLit(val lit: Literal) class Defn with constructor ValDefn(val owner: Opt[Symbol], val sym: Symbol, val rhs: Path) ClsLikeDefn(val sym: ConcreteClassSymbol, val methods: Array[FunDefn], val companion: Opt[ClsLikeBody]) // companion unused FunDefn(val sym: Symbol, val params: Array[ParamList], val body: Block) class ClsLikeBody(val isym: Symbol, val methods: Array[FunDefn], val publicFields: Array[[Symbol, Symbol]]) // unused class Block with constructor Match(val scrut: Path, val arms: Array[Arm], val dflt: Opt[Block], val rest: Block) Return(val res: Result, val implct: Bool) Assign(val lhs: Symbol, val rhs: Result, val rest: Block) Define(val defn: Defn, val rest: Block) Scoped(val symbols: Array[Symbol], val rest: Block) End() fun concat(b1: Block, b2: Block) = if b1 is Match(scrut, arms, dflt, rest) then Match(scrut, arms, dflt, concat(rest, b2)) Return(res, implct) then b1 Assign(lhs, rhs, rest) then Assign(lhs, rhs, concat(rest, b2)) Define(defn, rest) then Define(defn, concat(rest, b2)) Scoped(symbols, rest) then Scoped(symbols, concat(rest, b2)) End() then b2 fun indent(s: Str) = s.replaceAll("\n", "\n ") fun showLiteral(l: Literal) = if l is undefined then "undefined" null then "null" Str then "\"" + l.toString() + "\"" else l.toString() fun showSymbol(s: Symbol) = s.name.replaceAll("$", "_") fun showPath(p: Path): Str = if p is // avoids needing to import the runtime module Select(ValueRef(Symbol("runtime")), ModuleSymbol("Unit", Runtime.Unit)) then "()" Select(qual, name) then showPath(qual) + "." + showSymbol(name) DynSelect(qual, fld, false) then showPath(qual) + ".(" + showPath(fld) + ")" DynSelect(qual, fld, true) then showPath(qual) + ".[" + showPath(fld) + "]" ValueRef(l) then showSymbol(l) ValueLit(lit) then showLiteral(lit) fun showArg(arg: Arg) = showPath(arg.value) fun showArgs(args: Array[Arg]) = args.map(showArg).join(", ") fun showResult(r: Result): Str = if r is Path then showPath(r) Call(fun_, args) and // use infix to avoid parsing + and - as taking a unary argument args is [lhs, rhs] and fun_ is ValueRef(Symbol("+")) then showArg(lhs) + " + " + showArg(rhs) ValueRef(Symbol("-")) then showArg(lhs) + " - " + showArg(rhs) else showPath(fun_) + "(" + showArgs(args) + ")" Instantiate(cls, args) then "new " + showPath(cls) + (if args.length != 0 then "(" + showArgs(args) + ")" else "") Tuple(elems) then "[" + showArgs(elems) + "]" _ then "" // Case (match arm patterns) fun showCase(c) = if c is Lit(l) then showLiteral(l) Cls(cls, _) then showSymbol(cls) Tup(len) then "[" + Array(len).fill("_").join(", ") + "]" _ then "" fun showArm(a) = showCase(a.cse) + " then" + (if a.body is Return then " " else "\n ") + indent(showBlock(a.body)) fun showParams(p: ParamList) = "(" + p.map(showSymbol(_)).join(", ") + ")" fun showParamsOpt(p) = if p is Some(s) then showParams(s) None then "" fun showParamList(ps: Array[ParamList]) = ps.map(showParams).join("") fun showDefn(d: Defn): Str = if d is FunDefn(sym, ps, body) then "fun " + showSymbol(sym) + showParamList(ps) + " =" + (if body is Return | End then " " else "\n ") + indent(showBlock(body)) ClsLikeDefn(sym, methods, _) then "class " + showSymbol(sym) + showParamsOpt(sym.paramsOpt) + indent((if methods is [] then "" else " with\n") + methods.map(showDefn).join("\n")) // TODO: used to represent assignments in constructors, which still need some adjustments ValDefn(owner, sym, rhs) then "val " + showSymbol(sym) + " = " + showPath(rhs) _ then "" fun showBlock(b) = if b is Assign(lhs, rhs, rest) then (if lhs is NoSymbol then "" else showSymbol(lhs) + " = ") + showResult(rhs) + showRestBlock(rest) Define(d, rest) then showDefn(d) + showRestBlock(rest) Return(res, _) then showResult(res) Match(scrut, arms, dflt, rest) then "if " + showPath(scrut) + " is" + indent("\n" + arms.map(showArm).join("\n")) + if dflt is Some(db) then indent("\nelse" + indent((if db is Return then " " else "\n") + showBlock(db))) else "" + showRestBlock(rest) Scoped(symbols, rest) then // initialize symbols "let {" + symbols.map(showSymbol).join(", ") + "}" + showRestBlock(rest) End() then "()" _ then "" + b // removes trailing newline fun showRestBlock(b : Block): Str = if b is End then "" else "\n" + showBlock(b) fun show(x) = if x is Symbol then showSymbol(x) Path then showPath(x) Result then showResult(x) Case then showCase(x) Defn then showDefn(x) Block then showBlock(x) else "" fun printCode(x) = print(show(x)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/CSP.mls ================================================ import "./Example.mls" import "./quotes/CSPNest.mls" open Example open CSPNest module CSP with ... fun test() = 123 fun foo() = `test`() `+ `1 fun bar() = `inc`(`0) fun baz() = `nest_f`() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/CachedHash.mls ================================================ class CachedHash with let _hash = null fun hash() = if _hash !== null then _hash else let h = this.toString() // TODO: use a proper hash function!! set _hash = h h ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Char.mls ================================================ module Char with pattern AnyChar = (Str as s where s.length is 1) => s pattern Lowercase = "a" ..= "z" pattern Uppercase = "A" ..= "Z" pattern Letter = Lowercase | Uppercase pattern Digit = "0" ..= "9" pattern HexDigit = Digit | "a" ..= "f" | "A" ..= "F" pattern OctDigit = "0" ..= "7" pattern BinDigit = "0" | "1" pattern Whitespace = " " | "\t" | "\n" | "\r" ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Example.mls ================================================ import "./Predef.mls" open Predef fun privFun = "hi" module Example with ... fun pubFun = privFun fun (/) funnySlash(f, arg) = f(arg) fun inc(x) = x + 1 fun test(x) = if x is Int then "int" Num then "num" Str then "str" else "other" fun assertFail() = assert false class Test(val field: Int) module Test with fun next(self: Test) = Test(self.field + 1) val t = new Test(123) // Example of inheritance abstract class Base class Child1 extends Base class Child2 extends Base ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls ================================================ import "./Option.mls" import "./Iter.mls" open Option open Iter open FingerTreeList fun normIdx(idx, len) = if idx < 0 then idx + len else idx // Follows the semantics of Array.prototype.slice indices: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice fun normIdxSlice(i, len) = if i < 0 and i >= -len then len + i else 0 else i type Node[T] = Branch2[T] | Branch3[T] data class Branch2[T](size: Int, e1: T, e2: T) data class Branch3[T](size: Int, e1: T, e2: T, e3: T) fun getNodeSize(node) = if node is Branch2(s, _, _) then s Branch3(s, _, _, _) then s _ then 1 object Nil data class View[T](e1: T, rest: FingerTree[T]) class FingerTree[T] extends IterableBase with fun iterator() = let current = this Iterator of () => if popFront(current) is Nil then Result.Done View(head, tail) then set current = tail Result.Next of head fun toString() = "[" + this joined(", ") + "]" fun length = FingerTreeList.length(this) fun at(i) = get(i)(this) fun concat(other) = FingerTreeList.concat(this, other) fun slice(beg, end) = let len = this.length set beg = normIdxSlice(beg, len) set end = normIdxSlice(end, len) if beg >= len do return Empty if end >= len do set end = len if end <= beg do return Empty FingerTreeList.dropLeftRight(beg, len - end)(this) object Empty extends FingerTree[Nothing] with fun toString() = "[" + this joined(", ") + "]" class Single[T](val e: T) extends FingerTree[T] with fun toString() = "[" + this joined(", ") + "]" // prefix and suffix are at most length 4 class Deep[T](val size: Int, val prefix: Array[T], val middle: FingerTree[Node[T]], val suffix: Array[T]) extends FingerTree[T] with fun toString() = "[" + [...this].join(", ") + "]" // amortized O(log n) fun concatMiddle(ft1, middle, ft2) = if [ft1, middle, ft2] is [Empty, [], right] then right [Empty, [x, ...xs], right] then cons(x, concatMiddle(Empty, xs, right)) [Single(y), xs, right] then cons(y, concatMiddle(Empty, xs, right)) [left, [], Empty] then left [left, [...xs, x], Empty] then snoc(concatMiddle(left, xs, Empty), x) [left, xs, Single(y)] then snoc(concatMiddle(left, xs, Empty), y) [Deep(s1, pre1, dt1, ay1), middle, Deep(s2, ax2, dt2, post2)] then Deep(s1 + s2 + middle.length, pre1, concatMiddle(dt1, toNodes([...ay1, ...middle, ...ax2]), dt2), post2) // arr should be at most size 8, and at least size 2 fun toNodes(arr) = if arr is [] then [] [a, b, c, d] then [Branch2(getNodeSize(a) + getNodeSize(b), a, b), Branch2(getNodeSize(c) + getNodeSize(d), c, d)] [a, b, c, ...rest] then [Branch3(getNodeSize(a) + getNodeSize(b) + getNodeSize(c), a, b, c), ...toNodes(rest)] [a, b, ...rest] then [Branch2(getNodeSize(a) + getNodeSize(b), a, b), ...toNodes(rest)] // O(1) fun arrayOfNode(node) = if node is Branch2(_, a, b) then [a, b] Branch3(_, a, b, c) then [a, b, c] // affix should be at most size 4 fun getAffixSize(arr) = if arr is [] then 0 [a, ...rest] then getNodeSize(a) + getAffixSize(rest) fun ithAffixElement(idx, arr) = if arr is [] then None [a, ...rest] then if idx < getNodeSize(a) then ithNode(idx, a) else ithAffixElement(idx - getNodeSize(a), rest) fun ithNode(idx, node) = if node is Branch2(s, a, b) and idx >= 0 && idx < s and idx < getNodeSize(a) then ithNode(idx, a) else ithNode(idx - getNodeSize(a), b) else None Branch3(s, a, b, c) and idx >= 0 && idx < s and idx < getNodeSize(a) then ithNode(idx, a) idx < getNodeSize(a) + getNodeSize(b) then ithNode(idx - getNodeSize(a), b) else ithNode(idx - getNodeSize(a) - getNodeSize(b), c) else None a and idx == 0 then Some(a) else None fun repeatPopFront(n)(xs) = if n is 0 then xs _ then if popFront(xs) is Nil then xs View(_, rest) then repeatPopFront(n - 1)(rest) fun repeatPopBack(n)(xs) = if n is 0 then xs _ then if popBack(xs) is Nil then xs View(_, rest) then repeatPopBack(n - 1)(rest) object SpliceMarker module FingerTreeList with... fun toFingerTree(xs) = if xs is FingerTree then xs else mk(...xs) // assume that the input is iterable if not a finger tree // amortized O(1) fun (+:) cons(x, xs) = if toFingerTree(xs) is Empty then Single(x) Single(y) then Deep(getNodeSize(y) + getNodeSize(x), [x], Empty, [y]) Deep(s, ax, dt, ay) then if (ax.length >= 4) then Deep(s + getNodeSize(x), [x, ax.0], cons(Branch3( getNodeSize(ax.1) + getNodeSize(ax.2) + getNodeSize(ax.3), ax.1, ax.2, ax.3), dt), ay) else Deep(s + getNodeSize(x), [x, ...ax], dt, ay) // amortized O(1) fun (:+) snoc(xs, x) = if toFingerTree(xs) is Empty then Single(x) Single(y) then Deep(getNodeSize(y) + getNodeSize(x), [y], Empty, [x]) Deep(s, ax, dt, ay) then if (ay.length >= 4) then Deep(s + getNodeSize(x), ax, snoc(dt, Branch3( getNodeSize(ay.0) + getNodeSize(ay.1) + getNodeSize(ay.2), ay.0, ay.1, ay.2)), [ay.3, x]) else Deep(s + getNodeSize(x), ax, dt, [...ay, x]) fun mk(...args) = args.reduce( (acc, x) => snoc(acc, x), Empty) fun isEmpty = case Empty then true _ then false // O(1) fun length = case Array as a then a.length Empty then 0 Single(x) then getNodeSize(x) Deep(s, _, _, _) then s // amortized O(1) fun popFront(ft) = if toFingerTree(ft) is Empty then Nil Single(x) then View(x, Empty) Deep(s, [x], dt, ay) then if (popFront(dt)) is Nil then if ay is [y] then View(x, Single(y)) [...front, y] then View(x, Deep(s - getNodeSize(x), front, Empty, [y])) View(x', dt') then View(x, Deep(s - getNodeSize(x), arrayOfNode(x'), dt', ay)) Deep(s, [x, ...ax], dt, ay) then View(x, Deep(s - getNodeSize(x), ax, dt, ay)) // amortized O(1) fun popBack(ft) = if toFingerTree(ft) is Empty then Nil Single(x) then View(x, Empty) Deep(s, ax, dt, [x]) then if popBack(dt) is Nil then if ax is [y] then View(x, Single(y)) [y, ...front] then View(x, Deep(s - getNodeSize(x), [y], Empty, front)) View(x', dt') then View(x, Deep(s - getNodeSize(x), ax, dt', arrayOfNode(x'))) Deep(s, ax, dt, [...ay, y]) then View(y, Deep(s - getNodeSize(y), ax, dt, ay)) // amortized O(log n) fun (++) concat(ft1, ft2) = concatMiddle(toFingerTree(ft1), [], toFingerTree(ft2)) // amortized O(log n) fun ith(idx) = case Empty then None Single(x) then ithNode(idx, x) Deep(s, ax, dt, ay) and idx >= 0 && idx < s and idx < getAffixSize(ax) then ithAffixElement(idx, ax) idx < getAffixSize(ax) + length(dt) then ith(idx - getAffixSize(ax))(dt) else ithAffixElement(idx - getAffixSize(ax) - length(dt), ay) else None fun get(idx) = case Array as a then a.at(idx) FingerTree as lft then if ith(normIdx(idx, length(lft)))(lft) is None then throw "get: index out of bounds" Some(x) then x fun toArray(ft) = [...ft] /// amortized O(beg + end) fun dropLeftRight(beg, end)(und) = if beg < 0 || end < 0 || beg + end > length(und) then throw "dropLeftRight: index out of bounds" else repeatPopFront(beg)(repeatPopBack(end)(toFingerTree(und))) fun isFingerTree(xs) = if xs is FingerTree then true else false // The following functions are not meant for users; it's meant referred to by Runtime.mls // TODO: use `FingerTreeList.internals.concat` when this is implemented fun __markerConcat(...arr) = let acc = FingerTreeList.mk() let idx = 0 while idx < arr.length do if arr.[idx] is SpliceMarker then let ftl = arr.[idx + 1] set acc = acc ++ toFingerTree(ftl) set idx = idx + 2 else set acc = acc :+ arr.[idx] set idx = idx + 1 acc val __split = SpliceMarker ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Iter.mls ================================================ import "./Predef.mls" import "./Option.mls" import "./Stack.mls" open Predef open Option { Some, None } open Stack { Nil, Cons } module Iter with ... class IterableBase with set this.(Symbol.iterator) = iterator fun iterator() = throw new Error("Not implemented: iterator()") // require that subclasses are immutable fun toArray() = [...this] let array = null fun asArray = if array is null then set array = toArray() Object.freeze() array else array // * Functions that create a fresh new result fun at(idx) = asArray.at(idx) fun concat(...xs) = asArray.concat(...xs) fun copyWithin(...args) = asArray.copyWithin(...args) fun entries() = asArray.entries() fun every(...args) = asArray.every(...args) fun filter(...args) = asArray.filter(...args) fun find(...args) = asArray.find(...args) fun findIndex(...args) = asArray.findIndex(...args) fun findLast(...args) = asArray.findLast(...args) fun findLastIndex(...args) = asArray.findLastIndex(...args) fun flat(...args) = asArray.flat(...args) fun flatMap(...args) = asArray.flatMap(...args) fun forEach(...args) = asArray.forEach(...args) fun includes(...args) = asArray.includes(...args) fun indexOf(...args) = asArray.indexOf(...args) fun join(...args) = asArray.join(...args) fun keys() = asArray.keys() fun lastIndexOf(...args) = asArray.lastIndexOf(...args) fun map(...args) = asArray.map(...args) fun reduce(...args) = asArray.reduce(...args) fun reduceRight(...args) = asArray.reduceRight(...args) fun slice(...args) = asArray.slice(...args) fun some(...args) = asArray.some(...args) fun toLocaleString(...args) = asArray.toLocaleString(...args) fun toReversed() = asArray.toReversed() fun toSorted(...args) = asArray.toSorted(...args) fun toSpliced(...args) = asArray.toSpliced(...args) fun toString() = asArray.toString() // `with` is an mls keyword fun id"with"(...args) = asArray.with(...args) // * Functions that modify the array in place fun fill(...args) = toArray().fill(...args) fun pop() = toArray().pop() fun push(...args) = toArray().push(...args) fun reverse() = toArray().reverse() fun shift() = toArray().shift() fun sort(...args) = toArray().sort(...args) fun splice(...args) = toArray().splice(...args) fun unshift(...args) = toArray().unshift(...args) fun length = asArray.length fun values() = iterator() // * Implements the `Iterable` protocol. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol class Iterable(mk) extends IterableBase with fun iterator() = mk() // * Implements the `Iterator` protocol. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol class Iterator(val next) module Result with class Next(val value) with val done = false object Done with val done = true // A helper function to detect if `Symbol.iterator` is on an object, and if not, // indicate the object's class in the error message, which is useful for debugging. fun getIterator(something) = let test = something.(Symbol.iterator) if test is undefined then throw TypeError of "Not an iterable: " + if something is undefined then "undefined" something is null then "null" let prototype = Reflect.getPrototypeOf of something prototype is null then "object" else prototype.constructor.name else test.call(something) fun adaptIterable(iterable, makeNext) = Iterable of () => let iterator = getIterator of iterable Iterator of makeNext of iterator fun mapping(xs, op) = xs adaptIterable of iterator => () => let next = iterator.next() if next.done then Result.Done else Result.Next of op(next.value) fun withMap(op) = _ mapping(op) fun flattening(xss) = Iterable of () => let iterableIterator = getIterator of xss // Get the first `Iterator` from the first `Iterable`. let currentIterator = if let firstIterableResult = iterableIterator.next() firstIterableResult.done then None else Some of getIterator of firstIterableResult.value // Recursively skip empty `Iterable`s and get the next `Iterator` and value. fun skipEmptyIterables() = if let nextIterableResult = iterableIterator.next() nextIterableResult.done then None let nextIterator = getIterator of nextIterableResult.value let nextResult = nextIterator.next() nextResult.done then skipEmptyIterables() else Some of [nextIterator, nextResult.value] Iterator of () => if currentIterator is None then Result.Done // No more `Iterator`s means we're done. Some(iterator) and let next = iterator.next() // The next item from the current `Iterator`. next.done and skipEmptyIterables() is // The current `Iterator` is drained. Some([nextIterator, value]) then set currentIterator = Some of nextIterator Result.Next of value None then set currentIterator = None Result.Done else Result.Next of next.value fun filtering(xs, op) = xs adaptIterable of iterator => () => let next = iterator.next() while next.done is false and op(next.value) is false do set next = iterator.next() if next.done then Result.Done else Result.Next of next.value fun withFilter(op) = _ filtering(op) fun taking(xs, n) = filtering of xs, _ => set n -= 1 n >= 0 fun withMax(n) = _ taking(n) fun leaving(xs, n) = filtering of xs, _ => set n -= 1 n < 0 fun withoutAtLeast(n) = _ leaving(n) fun zippingWithIndex(xs) = let i = 0 mapping of xs, x => let j = i set i = i + 1 [x, j] fun foldingImpl(iterator, acc, op) = let next = iterator.next() while next.done is false do set acc = op(acc, next.value) set next = iterator.next() acc fun appended(xs, ys) = Iterable of () => let xsIterator = getIterator of xs let currentIterator = xsIterator Iterator of () => if let next = currentIterator.next() next.done and currentIterator == xsIterator and do set currentIterator = getIterator of ys let next = currentIterator.next() next.done then Result.Done else Result.Next of next.value else Result.Done else Result.Next of next.value fun withAppended(xs) = _ appended(xs) fun reduced(xs, op) = let iterator = getIterator of xs let next = iterator.next() if next.done do throw new Error("Empty iterator") foldingImpl(iterator, next.value, op) fun folded(xs, z, op) = let iterator = getIterator of xs foldingImpl(iterator, z, op) fun rightFolded(xs, z, op) = let iterator = getIterator of xs fun go() = if let next = iterator.next() next.done is true then z else op of next.value, go() go() fun joined(xs, sep) = let iterator = getIterator of xs let next = iterator.next() if next.done then "" else let sep' = String(sep) foldingImpl of iterator String(next.value) (acc, x) => acc + sep + String(x) fun firstDefined(xs, op) = let iterator = getIterator of xs let next = iterator.next() let result = None while next.done is false and result is None do set result = op(next.value) next = iterator.next() result // `exists` is a keyword fun some(xs, op) = let iterator = getIterator of xs let next = iterator.next() while next.done is false do if op(next.value) do return true set next = iterator.next() false // `forall` is a keyword fun every(xs, op) = let iterator = getIterator of xs let next = iterator.next() while next.done is false do if op(next.value) is false do return false set next = iterator.next() true fun each(xs, op) = let iterator = getIterator of xs let next = iterator.next() while next.done is false do op(next.value) set next = iterator.next() fun toArray(view) = Array.from(view) fun fromStack(stack) = Iterable of () => let current = stack Iterator of () => if current is Cons(head, tail) then set current = tail Result.Next of head Nil then Result.Done fun toStack(xs) = xs rightFolded of Nil, Cons // Important: This function is used by runtime directly. Do not call any function or make any lambda here. fun isArrayLike(xs) = xs is Array | IterableBase ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/LazyArray.mls ================================================ import "./Iter.mls" open Iter open LazyArray // Helper Methods fun normIdx(i, len) = if i < 0 then len + i else i fun enforceArray = case LazyArr | Array | String | Str | TypedArray as a then a ow then throw Error("Expected an Array, got: " + ow.toString()) fun concatHelper(a, l, ...args) = let idx = 0 init_len = a.length set a.length = init_len + args.length * 2 while (idx < args.length) do let x = args.[idx] set a.[init_len + idx * 2] = SpliceMarker a.[init_len + idx * 2 + 1] = enforceArray(x) l += x.length idx += 1 Splice of a, l // Follows the semantics of Array.prototype.slice indices: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice fun normIdxSlice(i, len) = if i < 0 and i >= -len then len + i else 0 else i fun sliceHelper(beg, fin, arr) = let len = arr.length set beg = normIdxSlice(beg, len) set fin = normIdxSlice(fin, len) if beg >= len do return View of [], 0, 0 if fin >= len do set fin = len if fin <= beg do return View of [], 0, 0 if arr is View(itr, start, end) then View of itr, start + beg, end + (len - fin) Splice(bits, l) as s then View of s.materialize().slice(beg, fin), 0, 0 else View of enforceArray(arr), beg, (arr.length - fin) // Marker base class for View and Splice class LazyArr extends IterableBase with fun toString() = "[" + this joined(", ") + "]" fun concat(...args) = concatHelper(mut [SpliceMarker, this], this.length, ...args) // Requires that the underlying iterable is constant-time indexable class View(val underlying: IterableBase, val start, val end) extends LazyArr with fun iterator() = let idx = start let until = underlying.length - end Iterator of () => if idx < until then let next = underlying.at(idx) set idx = idx + 1 Result.Next of next else Result.Done fun materialize() = underlying.slice(start, underlying.length - end) fun at(i) = if i < -length || i >= length do throw RangeError("View.at: Index out of bounds") underlying.at(normIdx(i, length) + start) fun slice(beg, fin) = sliceHelper(beg, fin, this) val len = underlying.length - start - end fun length = len fun toString() = "[" + this joined(", ") + "]" object SpliceMarker class Splice(val bits, val len) extends LazyArr with let materialized = null fun iterator() = let idx = 0 let uitr = null fun nextf() = if idx < bits.length and bits.[idx] is SpliceMarker then if uitr === null do set uitr = bits.[idx + 1].(Symbol.iterator)() let n = uitr.next() if n.done then set uitr = null set idx += 2 nextf() else n else let value = bits.[idx] set idx += 1 Result.Next of value else Result.Done Iterator of nextf fun reify = let counter = 0 let arr = new mut Array(len) let stack = mut [] stack.push([0, bits]) while stack.length > 0 do let e = stack.pop() let idx = e.[0] let vals = e.[1] if vals is Splice do set vals = vals.bits while (idx < vals.length and (vals.at(idx) is SpliceMarker) is false) do set arr.[counter] = vals.at(idx) set counter += 1 set idx += 1 if (idx < vals.length and vals.at(idx) is SpliceMarker) do stack.push([idx + 2, vals]) stack.push([0, vals.at(idx + 1)]) arr fun materialize() = if materialized === null do set materialized = reify materialized fun at(i) = materialize().at(i) fun slice(beg, fin) = sliceHelper(beg, fin, this) fun concat(...args) = if materialized === null then concatHelper(mut [SpliceMarker, this], len, ...args) else concatHelper(mut [SpliceMarker, materialized], len, ...args) fun length = len fun toString() = "[" + materialize() joined(", ") + "]" module LazyArray with... fun mk(...args) = View of args, 0, 0 fun concat(...args) = concatHelper(mut [], 0, ...args) fun dropLeftRight(beg, fin)(xs) = if beg < 0 || fin < 0 do throw RangeError("LazyArray.dropLeftRight: indices must be non-negative") if beg > xs.length || fin > xs.length do throw RangeError("LazyArray.dropLeftRight: indices out of bounds") sliceHelper(beg, xs.length - fin, xs) fun equals(xs, ys) = xs.length === ys.length && let idx = 0 while (idx < xs.length) do if xs.at(idx) !== ys.at(idx) do return false set idx += 1 true // This is not meant for users; it's referred to by Runtime.mls // TODO: use `LazyArray.internals.concat` when this is implemented fun __concat(...args) = let len = 0 let idx = 0 while (idx < args.length) do if args.[idx] is SpliceMarker then set len += enforceArray(args.[idx + 1]).length set idx += 2 else set len += 1 set idx += 1 Splice of args, len // This is not meant for users; it's referred to by Runtime.mls // TODO: use `LazyArray.internals.split` when this is implemented val __split = SpliceMarker ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/LazyFingerTree.mls ================================================ import "./Iter.mls" import "./FingerTreeList.mls" open Iter open LazyFingerTree open FingerTreeList fun normIdx(i, len) = if i < 0 then len + i else i fun concatHelper(a, l, ...args) = let idx = 0 let init_len = a.length set a.length = init_len + args.length * 2 while (idx < args.length) do let x = args.[idx] set a.[init_len + idx * 2] = SpliceMarker set a.[init_len + idx * 2 + 1] = x set l += x.length set idx += 1 Splice of a, l // Follows the semantics of Array.prototype.slice indices: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice fun normIdxSlice(i, len) = if i < 0 and i >= -len then len + i else 0 else i fun sliceHelper(beg, fin, arr) = let len = arr.length set beg = normIdxSlice(beg, len) set fin = normIdxSlice(fin, len) if beg >= len do return View of [], 0, 0 if fin >= len do set fin = len if fin <= beg do return View of [], 0, 0 if arr is View(itr, start, end) then View of itr, start + beg, end + (len - fin) Splice(bits, l) as s then s.materialize().slice(beg, fin) ft and FingerTreeList.isFingerTree(ft) then FingerTreeList.dropLeftRight(beg, (len - fin))(ft) else View of arr, beg, (len - fin) // Marker base class for View and Splice class LazyFT extends IterableBase with fun toString() = "[" + this joined(", ") + "]" fun concat(...args) = concatHelper(mut [SpliceMarker, this], this.length, ...args) class View(val underlying: IterableBase, val start, val end) extends LazyFT with fun iterator() = let idx = start let until = underlying.length - end Iterator of () => if idx < until then let next = underlying.at(idx) set idx = idx + 1 Result.Next of next else Result.Done fun materialize() = underlying.slice(start, underlying.length - end) fun at(i) = if i < -length || i >= length do throw RangeError("View.at: Index out of bounds") underlying.at(normIdx(i, length) + start) fun slice(beg, fin) = sliceHelper(beg, fin, this) val len = underlying.length - start - end fun length = len fun toString() = "[" + this joined(", ") + "]" object SpliceMarker class Splice(val bits, val len) extends LazyFT with let materialized = null fun iterator() = materialize().iterator() fun reify = let acc = FingerTreeList.mk() let stack = mut [] stack.push([0, bits]) while stack.length > 0 do let e = stack.pop() let idx = e.[0] let vals = e.[1] if vals is Splice do set vals = vals.bits while (idx < vals.length and (vals.at(idx) is SpliceMarker) is false) do set acc = acc :+ vals.at(idx) set idx += 1 if (idx < vals.length and vals.at(idx) is SpliceMarker) do stack.push([idx + 2, vals]) let next = vals.at(idx + 1) if FingerTreeList.isFingerTree(next) then set acc = acc ++ next else stack.push([0, next]) acc fun materialize() = if materialized == null do set materialized = reify materialized fun at(i) = materialize().at(i) // called on eager spreads in matches fun slice(beg, fin) = sliceHelper(beg, fin, this) fun concat(...args) = if materialized === null then concatHelper(mut [SpliceMarker, this], len, ...args) else concatHelper(mut [SpliceMarker, materialized], len, ...args) fun length = len fun toString() = "[" + materialize() joined(", ") + "]" module LazyFingerTree with... fun mk(...args) = View of FingerTreeList.mk(...args), 0, 0 fun concat(...args) = concatHelper(mut [], 0, ...args) fun dropLeftRight(beg, fin)(xs) = if beg < 0 || fin < 0 do throw RangeError("LazyFingerTree.dropLeftRight: indices must be non-negative") if beg > xs.length || fin > xs.length do throw RangeError("LazyFingerTree.dropLeftRight: indices out of bounds") sliceHelper(beg, xs.length - fin, xs) fun equals(xs, ys) = if xs is [x, ...xs] then if ys is [b, ...bs] then x == b && equals(xs, bs) [] then false [] then ys.length === 0 // The following functions are not meant for users; it's meant referred to by Runtime.mls // TODO: use `LazyFingerTree.internals.concat` when this is implemented fun __concat(...args) = let len = 0 let idx = 0 while (idx < args.length) do if args.[idx] is SpliceMarker then set len += args.[idx + 1].length set idx += 2 else set len += 1 set idx += 1 Splice of args, len val __split = SpliceMarker ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/MutMap.mls ================================================ import "./Option.mls" import "./Predef.mls" open Predef open Option { Some, None } module MutMap with ... class MutMap(val underlying) with set this.(Symbol.iterator) = () => underlying.(Symbol.iterator)() fun get(key)(m) = if m.underlying.has(key) then Some of m.underlying.get(key) else None fun insert(key, value)(m) = m.underlying.set(key, value) () fun delete(key)(m) = m.underlying.delete(key) () fun updateWith(key)(op)(m) = if op(m |> get(key)) is Some(value) then m.underlying.set(key, value) None then m.underlying.delete(key) fun keysIterator(m) = m.underlying.keys() fun valuesIterator(m) = m.underlying.values() fun values(m) = Array.from of m.underlying.values() fun toMap(entries) = let m = empty i = 0 length = entries.length while i < length do m |> insert of ...entries.at(i) set i += 1 m fun empty = new MutMap(new Map()) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/NoFreeze.mls ================================================ #config(noFreeze: true) #config(noModuleCheck: true) module NoFreeze with class Foo(val x) fun foo() = new Foo(0) fun bar() = NoFreeze."foo"() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/ObjectBuffer.mls ================================================ fun max(a, b) = if a < b then b else a module ObjectBuffer with abstract class ObjectBuffer with val buf fun mkNew(cls) fun del(cls, inst) fun read(idx) fun write(idx, v) class DefaultObjectBuffer(initLength) extends ObjectBuffer with val buf = new mut Array(max(16, initLength)) // val buf = Array.from({length: max(16, initLength)}, _ => null) let freeListHead = 0 set buf.[0] = buf.length buf.[1] = -1 fun mkNew(cls) = if cls.size === 0 do return 0 val idx = _alloc(getBlockSize(cls.size)) cls.ctor(this, idx) fun del(cls, inst) = if cls.size !== 0 do _free(inst, getBlockSize(cls.size)) fun read(idx) = buf.[idx] fun write(idx, v) = set buf.[idx] = v fun getBlockSize(sz) = Math.ceil(sz / 2) * 2 fun grow(needed) = let oldLen = buf.length let newLen = if buf.length >= needed then // double the length buf.length * 2 else // compute closest power of 2 enough for 2 * needed Math.pow(2, Math.ceil(Math.log2(needed)) + 1) set buf.length = newLen // Add the new block to head of free list _free(oldLen, newLen - oldLen) fun _tryAlloc(sz) = let prev = -1 let cur = freeListHead // first-fit while cur !== -1 do if buf.[cur] === sz do if prev === -1 then set freeListHead = buf.[cur + 1] else set buf.[prev] = buf.[cur + 1] return cur if buf.[cur] > sz do // Slice remainder, always possible due to block size set buf.[cur + sz] = buf.[cur] - sz buf.[cur + sz + 1] = buf.[cur + 1] if prev === -1 then set freeListHead = cur + sz else set buf.[prev] = cur + sz return cur set prev = cur cur = buf.[cur + 1] -1 fun alloc(sz) = if sz === 0 do return 0 _alloc(getBlockSize(sz)) fun _alloc(bsz) = let res = _tryAlloc(bsz) if res !== -1 do return res grow(bsz) _tryAlloc(bsz) fun free(ptr, sz) = if sz !== 0 do _free(ptr, getBlockSize(sz)) fun _free(ptr, bsz) = set buf.[ptr] = bsz buf.[ptr + 1] = freeListHead freeListHead = ptr ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Option.mls ================================================ import "./Predef.mls" open Predef type Option[A] = Option.{ Some(A) | None } module Option with ... data class Some[T](value) object None fun isDefined(x) = if x is Some then true None then false data class Both(fst, snd) fun getOrElse(opt, default) = if opt is Some(value) then value None then default fun flatMap(opt, f) = if opt is Some(value) then f(value) None then None module unsafe with fun get(opt) = if opt is Some(value) then value None then throw Error("None.get") ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Predef.mls ================================================ import "./RuntimeJS.mjs" // * Importing the mjs for now to avoid reparsing/re-elaborating these files Runtime all the time // * TODO: proper caching of parsed/elaborated files import "./Runtime.mjs" import "./Rendering.mjs" import "./Term.mjs" // import "./Runtime.mls" // import "./Rendering.mls" module Predef with ... fun id(x) = x fun (@) apply(f, ...args) = f(...args) fun (|>) pipeInto(x, f) = f(x) fun (<|) pipeFrom(f, x) = f(x) // Same as above but with high precedence fun (.>) pipeIntoHi(x, f) = f(x) fun (<.) pipeFromHi(f, x) = f(x) fun (!>) tap(x, f) = f(x); x fun (>) andThen(f, g)(x) = g(f(x)) fun (<<) compose(f, g)(x) = f(g(x)) // x \foo(a, b, c) == foo(x, a, b, c) fun (\) passTo(receiver, f)(...args) = f(receiver, ...args) // Same as above but with low precedence fun (|.) passToLo(receiver, f)(...args) = f(receiver, ...args) // x |>. foo(a, b, c) == foo.call(x, a, b, c) fun (|>.) call(receiver, f)(...args) = f.call(receiver, ...args) object Symbols with val prettyPrint = RuntimeJS.symbols.prettyPrint val definitionMetadata = RuntimeJS.symbols.definitionMetadata // val definitionMetadata = Symbol.for("mlscript.definitionMetadata") abstract class (<:<) Sub[A, B] with fun apply(x: A): B abstract class (=:=) Eq[A, B] extends Sub[A, B] with fun apply(x: A): B object Refl[A] extends Eq[A, A] with fun apply(x: A): A = x type (>:>) Sup[A, B] = Sub[B, A] // * A generic equality comparison. // * Returns true for: // * - values that compare equal according to strict equality (===), which includes reference equality; // * - arrays of values that are pairwise recursively equal; // * - instances of the same parameterized MLscript class when all fields are public and recursively equal. // TODO: compare lazy arrays and other sequences and collections fun (==) equals(a, b) = if a === b then true a is Array and b is Array and a.length === b.length then a.every((a, i) => a == b.at(i)) else a !== undefined and a !== null and b !== undefined and b !== null and let ac = a.constructor ac !== undefined and ac === b.constructor and let md = ac.(Symbols.definitionMetadata) md !== undefined and md.2.every(field => field !== null and a.(field) == b.(field)) fun (!=) nequals(a, b) = not a == b val pass1 = Rendering.pass1 val pass2 = Rendering.pass2 val pass3 = Rendering.pass3 val passing = Rendering.passing val map = Rendering.map val fold = Rendering.fold val interleave = Rendering.interleave val render = Rendering.render fun print(...xs) = console.log(...map(renderAsStr)(...xs)) fun renderAsStr(arg) = if arg is Str then arg else render(arg) val js_assert = globalThis.console.("assert") // `console.("assert")` gives "Unexpected moduleful reference of type console." fun check(...args) = js_assert(...args) fun (??) notImplemented(msg) = throw Error("Not implemented: " + msg) fun (???) notImplementedError = throw Error("Not implemented") fun tuple(...xs) = xs fun mkSet(...xs) = new Set(xs) val foldl = fold // fun foldr(f)(...rest, init) = // TODO allow this syntax fun foldr(f)(first, ...rest) = let len = rest.length if len === 0 then first else... let i = len - 1 init = rest.at(i) while i > 0 do set i -= 1 init = f(rest.at(i), init) f(first, init) fun mkStr(...xs) = fold((acc, x) => check(x is Str); acc + x) of ...xs // Return the implicit instance of type T from the current scope. // Analogous to the `summon` function in Scala. fun use[T](using instance: T) = instance // This binds the handler in body, which means body can raise effects in handler fun enterHandleBlock(handler, body) = // This is a simple wrapper of a handler runtime function, which must not be instrumented so cannot be directly implemented here. // It also calls other internal runtime functions as well. Runtime.enterHandleBlock(handler, body) fun raiseUnhandledEffect() = Runtime.mkEffect(Runtime.FatalEffect, null) module meta with fun codegen(t, file) = Term.codegen(t, file) fun print(t) = Term.print(t) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/QuoteExample.mls ================================================ import "./Predef.mls" module QuoteExample with fun foo() = `1 `+ `1 fun inc() = x `=> x `+ `1 fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) fun bind(rhs, k) = `let x = rhs `in k(x) fun body(x, y) = case 0 then x 1 then y n then bind of x `+ y, (z => body(y, z)(n - 1)) fun gib(n) = (x, y) `=> body(x, y)(n) fun safeDiv() = (x, y, d) `=> `if y `== `0.0 then d else x `/ y ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/QuoteExample1.mls ================================================ import "./Predef.mls" open Predef module QuoteExample1 with fun foo() = `id`(`1) `== id(`2) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/QuoteExample2.mls ================================================ import "./Term.mls" import "./QuoteExample.mls" import "./QuoteExample1.mls" module QuoteExample2 with fun codegen() = Term.codegen(QuoteExample.foo(), "./hkmc2/shared/src/test/mlscript-compile/quotes/QuoteFoo.mls") Term.codegen(QuoteExample.inc(), "./hkmc2/shared/src/test/mlscript-compile/quotes/QuoteInc.mls") fun genCubic() = Term.codegen(x `=> QuoteExample.power(x)(3), "./hkmc2/shared/src/test/mlscript-compile/quotes/Cubic.mls") fun genGib12() = Term.codegen(QuoteExample.gib(12), "./hkmc2/shared/src/test/mlscript-compile/quotes/Gib12.mls") fun genSafeDiv() = Term.codegen(QuoteExample.safeDiv(), "./hkmc2/shared/src/test/mlscript-compile/quotes/SafeDiv.mls") fun genOpened() = Term.codegen(QuoteExample1.foo(), "./hkmc2/shared/src/test/mlscript-compile/quotes/Opened.mls") ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Record.mls ================================================ import "./Predef.mls" open Predef import "./Iter.mls" module Record with... fun steal(from, ...fields) = let rcd = new mut Object fields Iter.each of f => set rcd.(f) = from.(f) rcd ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Rendering.mls ================================================ import "./RuntimeJS.mjs" module Rendering with ... let definitionMetadataSymbol = RuntimeJS.symbols.definitionMetadata let prettyPrintSymbol = RuntimeJS.symbols.prettyPrint fun pass1(f)(...xs) = f(xs.0) fun pass2(f)(...xs) = f(xs.0, xs.1) fun pass3(f)(...xs) = f(xs.0, xs.1, xs.2) fun passing(f, ...args) = f.bind(null, ...args) fun map(f)(...xs) = xs.map(pass1(f)) fun fold(f)(init, ...rest) = let i = 0 len = rest.length while i < len do set init = f(init, rest.at(i)) i += 1 init fun interleave(sep)(...args) = if args.length === 0 then [] else... let res = Array of args.length * 2 - 1 len = args.length i = 0 while i < len do let idx = i * 2 set res.[idx] = args.[i] i += 1 if i < len do set res.[idx + 1] = sep res // The following regular expression matches non-empty strings that qualify as // Unicode identifiers accepted by ECMAScript. let identifierPattern = new RegExp of // A breakdown of the regular expression: // 1. `\p{...}` is a Unicode character class escape. It matches a set of // characters specified by a Unicode property. // 2. `ID_Start` includes letters and a few letter-like characters; but it // does not include `_` or `$`, hence they’re added explicitly. // 3. `ID_Continue` includes `ID_Start` plus combining marks, decimal digits, // connector punctuation (which covers `_`), etc. But it does not include // `$`, hence `$` is added explicitly. // 4. `^` and `$` assert that the current position is the start or the end of // input respectively. Hence, they make sure the entire string matches. // The regular expression follows the production of identifiers defined in // ECMA-262: https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#prod-IdentifierName. """^[\p{ID_Start}_$][\p{ID_Continue}$]*$""" "u" // The Unicode flag is required for `\p{...}` Unicode property escapes. // Return `true` if the identifier needs to be enclosed in quotes. let noNeedForQuotes(id) = identifierPattern.test(id) // Use this object instead when no options object is given. let emptyOptions = new Object let DEFAULT_BREAK_LENGTH = 80 let symbolsForArray = start: "[", end: "]", separator: ",", empty: "[]", padding: true let symbolsForSet(n: Int) = let headline = "Set(" + n + ") " start: headline + "{", end: "}", separator: ",", empty: headline + "{}", padding: true let symbolsForClass(name: Str) = start: name + "(", end: ")", separator: ",", empty: name + "()", padding: false let symbolsForObject = start: "{", end: "}", entrySeparator: ",", keyValueSeparator: ": ", empty: "{}", padding: true let symbolsForMap(n: Int) = let headline = "Map(" + n + ")" start: headline + " {", end: "}", entrySeparator: ",", keyValueSeparator: " => ", empty: headline + " {}", padding: true fun render(target, ...args) = let options = if args is [options, ...] then options else emptyOptions indent = if options is (:indent) and indent is true then " " // The default indentation is of two spaces. // A positive integer will be interpreted as the number of spaces for a // single level of indentation. Int and indent > 0 then " ".repeat(indent) // `null` to indicate no indentation and put all the content on a single line. else null // If the joined string is less than or equal `breakLength`, it stays on one // line; otherwise it becomes multiline and each item is prefixed with `\n` // plus `indent`. breakLength = if indent is null then Number.POSITIVE_INFINITY options is (:breakLength) and breakLength is Int and breakLength > 0 then breakLength else DEFAULT_BREAK_LENGTH // Should we add padding after "["/"{" and before "]"/"}". padding = if options is (padding: true) then " " else "" circularCounter = 1 visitingObjects = new WeakMap // Object -> null | Int visitedObjects = new WeakMap // Object -> Str // Add indentations to results of `toString` from other objects. fun indentText(text: Str, currentLevel: Int): Str = if indent is null then text else... let indents = indent.repeat(currentLevel + 1) text .split("\n") .map of (line, index, lines) => let postfix = if index + 1 === lines.length then "" else " \\" let prefix = if index === 0 then "" else indents prefix + line + postfix .join("\n") fun renderArray(array, level, keyLength, startPos) = let i = 0 length = array.length emptyItemCount = 0 // The length of indentations that should be prepended to items in this // array if this array is displayed in multi-lines. itemIndentationLength = if indent is null then 0 else (level + 1) * indent.length // Return `true` if the array has no remaining elements. let done() = let isDone = null while isDone is null do if... i >= length then set isDone = true Reflect.has(array, i) then set isDone = false else set { emptyItemCount += 1, i += 1 } (emptyItemCount === 0) && (if isDone is null then true else isDone) let next(isFirst: Bool, prefixLength: Int) = if emptyItemCount > 0 then let emptyItemCount' = emptyItemCount set emptyItemCount = 0 "<" + emptyItemCount' + " empty items>" i < length then let // The starting column if the current item is placed on the same line. prefixLengthIfSameLine = prefixLength + (if isFirst then 0 else 2) valueStartLength = if prefixLengthIfSameLine <= breakLength then prefixLengthIfSameLine else itemIndentationLength valueStr = renderValue(array.[i], level + 1, 0, valueStartLength) set i += 1 valueStr else throw new Error("All items in this array has been rendered.") renderSequence of done, next, level, keyLength, startPos, symbolsForArray fun renderSet(theSet, level, keyLength, startPos) = let iterator = theSet.(Symbol.iterator)() peek = iterator.next() // The length of indentations that should be prepended to items in this // array if this array is displayed in multi-lines. itemIndentationLength = if indent is null then 0 else (level + 1) * indent.length // Return `true` if the array has no remaining elements. let done() = peek.done let next(isFirst: Bool, prefixLength: Int) = let result = if peek is (done: false, :value) then let // The starting column if the current item is placed on the same line. prefixLengthIfSameLine = prefixLength + (if isFirst then 0 else 2) valueStartLength = if prefixLengthIfSameLine > breakLength then itemIndentationLength else prefixLengthIfSameLine renderValue(value, level + 1, 0, valueStartLength) set peek = iterator.next() result renderSequence of done, next, level, keyLength, startPos, symbolsForSet(theSet.size) // Render instances of MLscript classes. fun renderClassInstance(constructorName, fieldNames, instance, level, keyLength, startPos) = let length = fieldNames.length i = 0 itemIndentationLength = if indent is null then 0 else (level + 1) * indent.length let done() = i >= length let next(isFirst: Bool, prefixLength: Int) = if fieldNames.[i] is do set i += 1 null then "_" Str as key then let value = instance.(key) prefixLengthIfSameLine = prefixLength + (if isFirst then 0 else 2) valueStartLength = if prefixLengthIfSameLine > breakLength then itemIndentationLength else prefixLengthIfSameLine renderValue(value, level + 1, 0, valueStartLength) renderSequence of done, next, level, keyLength, startPos, symbolsForClass(constructorName) fun renderSequence(done, next, level: Int, keyLength: Int, startPos: Int, symbols) = let items = mut [] hasMultiline = false // Try calculating the length needed to place all items in one line. The // brackets and spaces around items are excluded from the length. singleLineLength = startPos + symbols.start.length + (if symbols.padding then padding.length else 0) parentIndent = if indent is null then null else indent.repeat(level) itemIdent = if indent is null then null else indent.repeat(level + 1) // Iterate through and render all items. while done() is false do let item = next(items.length is 0, singleLineLength) // Append a new item and update needed variables. if item.indexOf("\n") >= 0 do set hasMultiline = true set singleLineLength += if items.length is 0 then 0 else symbols.separator.length + 1 set singleLineLength += item.length items.push of item set singleLineLength += symbols.end.length + (if symbols.padding then padding.length else 0) let layoutMultiLine() = symbols.start + "\n" + itemIdent + items.join(symbols.separator + "\n" + itemIdent) + "\n" + parentIndent + symbols.end layoutSingleLine() = symbols.start + (if symbols.padding then padding else "") + items.join(symbols.separator + " ") + (if symbols.padding then padding else "") + symbols.end if items.length is 0 then symbols.empty indent is null then layoutSingleLine() // If any item is multiline, place each item on a line. hasMultiline then layoutMultiLine() singleLineLength <= breakLength then layoutSingleLine() parentIndent.length + (singleLineLength - startPos + keyLength) <= breakLength then layoutSingleLine() else layoutMultiLine() // Render enumerable properties of the given object. fun renderObject(subject, level, keyLength, startPos, isRefinement) = let entries = Object.entries(Object.getOwnPropertyDescriptors(subject)) length = entries.length i = 0 itemIndentationLength = if indent is null then 0 else (level + 1) * indent.length let skipNonEnumerable() = while i < length and entries.[i].1.enumerable is false do set i += 1 let done() = i >= length let next(isFirst: Bool, prefixLength: Int) = let key = entries.[i].0 desc = entries.[i].1 skipNonEnumerable() set i += 1 let keyStr = if key noNeedForQuotes() then key else renderValue(key, 0, 0, 0) if desc is (:value) then let dryRunStartPos = prefixLength + // base length (if isFirst then 0 else 2) + // ", " keyStr.length + 2 // "key: " valueStartPos = if dryRunStartPos > breakLength then itemIndentationLength else dryRunStartPos valueStr = renderValue(value, level + 1, keyStr.length + 2, valueStartPos) [keyStr, valueStr] (get: getter, "set": setter) then let valueStr = if setter is undefined then "[Getter]" getter is undefined then "[Setter]" else "[Getter/Setter]" [keyStr, valueStr] else [keyStr, "‹Non-data property›"] // LP: not sure what to do (thsi was proposed by Copilot...) skipNonEnumerable() renderRecordLike of done, next, level, keyLength, startPos, symbolsForObject, isRefinement fun renderMap(map, level, keyLength, startPos) = let iterator = map.(Symbol.iterator)() peek = iterator.next() // The length of indentations that should be prepended to items in this // array if this array is displayed in multi-lines. itemIndentationLength = if indent is null then 0 else (level + 1) * indent.length // Return `true` if the array has no remaining elements. let done() = peek.done let next(isFirst: Bool, prefixLength: Int) = let last = peek set peek = iterator.next() if last is (done: false, value: [key, value]) then let // The starting column if the key is placed on the same line. keyPrefixLengthIfSameLine = prefixLength + (if isFirst then 0 else 2) keyPrefixLength = if keyPrefixLengthIfSameLine > breakLength then itemIndentationLength else keyPrefixLengthIfSameLine keyStr = renderValue(key, level + 1, 0, keyPrefixLength) // The starting column if the value is placed on the same line. valuePrefixLengthIfSameLine = if keyStr.indexOf("\n") >= 0 then itemIndentationLength + keyStr.length - keyStr.lastIndexOf("\n") + 5 else keyPrefixLength + keyStr.length + 5 let valuePrefixLength = if valuePrefixLengthIfSameLine > breakLength then itemIndentationLength else valuePrefixLengthIfSameLine valueStr = renderValue(value, level + 1, 0, valuePrefixLengthIfSameLine) [keyStr, valueStr] renderRecordLike of done, next, level, keyLength, startPos, symbolsForMap(map.size), false fun renderRecordLike(done, next, level, keyLength, startPos, symbols, isRefinement) = let startPadding = if isRefinement then " " symbols.padding then padding else "" endPadding = if isRefinement then " " symbols.padding then padding else "" items = mut [] hasMultiline = false singleLineLength = startPos + symbols.start.length + startPadding.length parentIndent = if indent is null then null else indent.repeat(level) itemIdent = if indent is null then null else indent.repeat(level + 1) while done() is false do let keyValue = next(items.length is 0, singleLineLength) key = keyValue.0 value = keyValue.1 length = key.length + 2 + value.length // "a: b" if value.indexOf("\n") >= 0 do set hasMultiline = true set singleLineLength += if items.length is 0 then 0 else symbols.entrySeparator.length set singleLineLength += length // the item items.push of key + symbols.keyValueSeparator + value // Complete the single line length. set singleLineLength += symbols.end.length + endPadding.length let layoutMultiLine() = symbols.start + "\n" + itemIdent + items.join(",\n" + itemIdent) + "\n" + parentIndent + symbols.end layoutSingleLine() = symbols.start + startPadding + items.join(", ") + endPadding + symbols.end if items.length is 0 then symbols.empty indent is null then layoutSingleLine() hasMultiline then layoutMultiLine() singleLineLength <= breakLength then layoutSingleLine() // Try to break the parent object into many lines and check if it fits. let lengthIfBreakParent = parentIndent.length + (singleLineLength - startPos + keyLength) lengthIfBreakParent <= breakLength then layoutSingleLine() else layoutMultiLine() fun renderValue(arg, level, keyLength, startPos) = if arg is // Primitive values undefined then "undefined" null then "null" Str then JSON.stringify(arg) BigInt then arg.toString() + "n" Symbol and arg.description is undefined then "Symbol()" else "Symbol(\"" + arg.description + "\")" Num then arg.toString() RegExp then arg.toString() true then "true" false then "false" arg === () then "()" // Reuse the cached result if the display mode is inline. visitedObjects.has(arg) and indent is null then visitedObjects.get(arg) visitingObjects.has(arg) and visitingObjects.get(arg) is // We have already found this circular reference before Int as index then "ref'" + index // The first time we find a circular reference else let index = circularCounter set circularCounter += 1 visitingObjects.set(arg, index) "ref'" + index else... // Otherwise, add this object to the visited objects // `null` is a marker for visited objects visitingObjects.set(arg, null) let rendered = if arg is Array then renderArray of arg, level, keyLength, startPos Set then renderSet of arg, level, keyLength, startPos Map then renderMap of arg, level, keyLength, startPos WeakSet then "WeakSet { }" WeakMap then "WeakMap { }" Error then arg.name + ": " + arg.message Function then let desc = Object.getOwnPropertyDescriptor(arg, "prototype") let head = if // ES 2015 classes: `desc.writable` is `false`. desc is Object and desc.writable is false and // If it is from MLscript, use the kind written in metadata. desc.value.constructor.hasOwnProperty(definitionMetadataSymbol) and desc.value.constructor.(definitionMetadataSymbol) is [kind, name, ...] then kind + " " + name arg.name is "" then "class" // Show "class" if there is no name. else "class " + arg.name // Or, we can use the name. // Display as functions if `desc.writable` is not `false` else "fun" + (if arg.name is "" then "" else " " + arg.name) head + if // Add a space if the function do not have enumerable properties let properties = renderObject of arg, level, keyLength, startPos + head.length, true properties is "{}" then "" else " " + properties // The case for objects. Reflect.getPrototypeOf(arg) is // Objects with `null` as the prototype does not have prefix. null then renderObject(arg, level, keyLength, startPos, false) // The result of `getPrototypeOf` is either `null` or `Object`. proto and // For object literals, show its properties without the constructor. proto === Object.prototype then renderObject(arg, level, keyLength, startPos, false) // The `constructor` field refers to the class. proto.constructor is Function and // Call its implementation of `prettyPrint` if defined. proto.hasOwnProperty(prettyPrintSymbol) then arg.(prettyPrintSymbol)() indentText(level) // If the symbol `definitionMetadata` is defined on the constructor, // we render the function using the provided field names. proto.constructor.hasOwnProperty(definitionMetadataSymbol) and let definitionMetadata = proto.constructor.(definitionMetadataSymbol) definitionMetadata is [kind, name, fields] then renderClassInstance(name, fields, arg, level, keyLength, startPos) // If no field names are provided (the case for modules, objects, // patterns, or parameter-less classes), use the constructor name. definitionMetadata is [kind, name] then // For class instances and objects, do not show the kind. let head = if kind is "class" | "object" then name else kind + " " + name let body = renderObject of arg, level, keyLength, startPos + head.length, true head + (if body is "{}" then "" else " " + body) // Otherwise, we print the name of the constructor and the properties. let head = proto.constructor.name + " " else head + renderObject of arg, level, keyLength, startPos + head.length, true // Otherwise, the prototype is not a function. // If the prototype has its own implementation of `toString`, call it. proto.hasOwnProperty("toString") then arg.toString() indentText(level) // Otherwise, treat the input as a normal object. else renderObject(arg, level, keyLength, startPos, false) // We have finished visiting this object. let postfix = if visitingObjects.has(arg) and visitingObjects.get(arg) is Int as index then " as ref'" + index else "" let result = rendered + postfix // Add to the visited objects if the object is a circular reference. if postfix.length > 0 do visitedObjects.set(arg, result) visitingObjects.delete(arg) result renderValue(target, 0, 0, 0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Runtime.mls ================================================ import "./RuntimeJS.mjs" import "./Rendering.mls" import "./LazyArray.mjs" import "./Iter.mjs" module Runtime with ... object Unit with fun toString() = "()" // This object is used as a marker for the end of a while loop in the compiled codegen. // It is possible for the loop body to contain a return statement, // in which case we want to distinguish between returning from the loop body and loop termination. object LoopEnd val short_and = RuntimeJS.short_and val short_or = RuntimeJS.short_or fun unreachable = throw Error("unreachable") fun assertFail(file, line) = throw Error("Assertion failed (" + file + ":" + line + ")") fun checkArgs(functionName, expected, isUB, got) = if got < expected || isUB && got > expected do let name = if functionName.length > 0 then " '" + functionName + "'" else "" // throw globalThis.Error("Function" + name + " expected " // + (if isUB then "" else "at least ") // + expected // + " argument(s) but got " + got) throw Error of "Function" + name + " expected " + (if isUB then "" else "at least " ) + expected + " argument" + (if expected === 1 then "" else "s") + " but got " + got fun safeCall(x) = if x is undefined then () else x fun checkCall(x) = if x is undefined then throw Error("MLscript call unexpectedly returned `undefined`, the forbidden value.") else x fun deboundMethod(mtdName, clsName) = throw Error of "[debinding error] Method '" + mtdName + "' of class '" + clsName + "' was accessed without being called." val bitand = RuntimeJS.bitand val bitnot = RuntimeJS.bitnot val bitor = RuntimeJS.bitor val shl = RuntimeJS.shl val try_catch = RuntimeJS.try_catch class EffectHandle(_reified) with val reified = _reified fun resumeWith(value) = Runtime.try(() => resume(reified.contTrace)(value)) fun raise() = set curEffect = reified fun try(f) = let res = f() if curEffect !== null then let tmp = curEffect set curEffect = null EffectHandle(tmp) else res // For `pattern` definitions data class MatchSuccess(output, bindings) data class MatchFailure(errors) // For pattern matching on tuples module Tuple with fun slice(xs, i, j) = xs.slice(i, xs.length - j) fun lazySlice(xs, i, j) = LazyArray.dropLeftRight(i, j)(xs) fun lazyConcat(...args) = LazyArray.__concat(...args) fun get(xs, i) = if i >= xs.length do throw RangeError("Tuple.get: index out of bounds") if i < -xs.length do throw RangeError("Tuple.get: negative index out of bounds") xs.[i] fun isArrayLike(xs) = Iter.isArrayLike(xs) val split = LazyArray.__split module Str with fun startsWith(string, prefix) = string.startsWith(prefix) fun get(string, i) = if i >= string.length then throw RangeError("Str.get: index out of bounds") else string.at(i) fun take(string, n) = string.slice(0, n) fun leave(string, n) = string.slice(n) // Re-export rendering functions val render = Rendering.render fun printRaw(x) = console.log(render(x, indent: 2, breakLength: 76)) // TraceLogger module TraceLogger with mut val enabled = false mut val indentLvl = 0 fun indent() = if enabled then let prev = indentLvl set indentLvl = prev + 1 prev else () fun resetIndent(n) = if enabled then set indentLvl = n else () fun log(msg) = if enabled then console.log("| ".repeat(indentLvl) + msg.replaceAll("\n", "\n" + " ".repeat(indentLvl))) else () // Private definitions for algebraic effects // layout: // | f | pc | pcStr | debugInfo | this | argLists... (*) | localCnt | locals... | // (*) argLists is length encoded nested array mut val curEffect = null mut val resumeValue = null mut val resumeArr = null mut val resumeIdx = null mut val resumePc = -1 object FatalEffect object PrintStackEffect data class FunctionContFrame(next, saved) with fun resume(value) = let i = 0 f = saved.[0] argListsLength = saved.[5] currentArgList = 6 set resumeValue = value resumeArr = saved resumePc = saved.[1] if argListsLength === 0 do console.log("cannot resume getters") while i < argListsLength - 1 do let argListLength = saved.[currentArgList] set f = f.apply(saved.[4], saved.slice(currentArgList + 1, currentArgList + 1 + argListLength)) currentArgList += argListLength + 1 i += 1 let argListLength = saved.[currentArgList] set resumeIdx = currentArgList + argListLength + 2 f.apply(saved.[4], saved.slice(currentArgList + 1, currentArgList + 1 + argListLength)) fun getLocals = let debugInfo = saved.[3] let i = 0 let cur = 6 while i < saved.[5] do set cur += saved.[cur] + 1 i += 1 let res = mut [] let i = 1 while i < debugInfo.length do res.push(new LocalVarInfo(debugInfo.[i + 1], saved.[cur + 1 + debugInfo.[i]])) set i += 2 res fun getNme = saved.[3].[0] fun getLoc = let loc = saved.[2] if loc is null then "pc=" + saved.[1] else loc data class HandlerContFrame(next, nextHandler, handler) data class ContTrace(next, last, nextHandler, lastHandler, resumed) data class EffectSig(contTrace, handler, handlerFun) class NonLocalReturn with fun ret(value) data class FnLocalsInfo(fnName, locals) data class LocalVarInfo(localName, value) data class CustomStackError(stack) with fun toString() = stack fun resetEffects() = set curEffect = null resumePc = -1 fun raisePrintStackEffect(showLocals) = mkEffect(PrintStackEffect, showLocals) fun topLevelEffect(debug) = let tr = curEffect let v = null while tr is EffectSig and tr.handler === PrintStackEffect do console.log(showStackTrace("Stack Trace:", tr, debug, tr.handlerFun)) set curEffect = null v = resume(tr.contTrace)(()) tr = curEffect if tr is EffectSig then set curEffect = null throw CustomStackError(showStackTrace("Error: Unhandled effect " + tr.handler.constructor.name, tr, debug, false)) else v fun illegalEffect(position) = let tmp = curEffect set curEffect = null throw CustomStackError(showStackTrace("Error: Effect " + tmp.handler.constructor.name + " is raised " + position, tmp, false, false)) fun showStackTrace(header, tr, debug, showLocals) = let msg = header curHandler = tr.contTrace atTail = true if debug do while curHandler !== null do let cur = curHandler.next while cur !== null do let curLocals = cur.getLocals let loc = cur.getLoc let localsMsg = if showLocals and curLocals.length > 0 then " with locals: " + curLocals.map(l => l.localName + "=" + Rendering.render(l.value)).join(", ") else "" set msg += "\n\tat " + cur.getNme + " (" + loc + ")" msg += localsMsg cur = cur.next atTail = false set curHandler = curHandler.nextHandler if curHandler !== null do set msg += "\n\twith handler " + curHandler.handler.constructor.name atTail = false if atTail do set msg += "\n\tat tail position" msg fun showFunctionContChain(cont, hl, vis, reps) = if cont is FunctionContFrame then let result = cont.constructor.name + "(pc=" + cont.saved.[1] hl.forEach((m, marker) => if m.has(cont) do set result += ", " + marker) if vis.has(cont) then set reps = reps + 1 if reps > 10 do throw Error("10 repeated continuation frame (loop?)") set result += ", REPEAT" else vis.add(cont) result + ") -> " + showFunctionContChain(cont.next, hl, vis, reps) else if cont === null then "(null)" else "(NOT CONT)" fun showHandlerContChain(cont, hl, vis, reps) = if cont is HandlerContFrame then let result = cont.handler.constructor.name hl.forEach((m, marker) => if m.has(cont) do set result += ", " + marker) if vis.has(cont) then set reps = reps + 1 if reps > 10 do throw Error("10 repeated continuation frame (loop?)") set result += ", REPEAT" else vis.add(cont) result + " -> " + showFunctionContChain(cont.next, hl, vis, reps) else if cont === null then "(null)" else "(NOT HANDLER CONT)" fun debugCont(cont) = console.log(showFunctionContChain(cont, new Map(), new Set(), 0)) fun debugHandler(cont) = console.log(showHandlerContChain(cont, new Map(), new Set(), 0)) fun debugContTrace(contTrace) = if contTrace is ContTrace then console.log("resumed: ", contTrace.resumed) if contTrace.last === contTrace do console.log("") if contTrace.lastHandler === contTrace do console.log("") let vis = new Set() let hl = new Map() hl.set("last", new Set([contTrace.last])) hl.set("last-handler", new Set([contTrace.lastHandler])) console.log(showFunctionContChain(contTrace.next, hl, vis, 0)) let cur = contTrace.nextHandler while cur !== null do console.log(showHandlerContChain(cur, hl, vis, 0)) set cur = cur.nextHandler console.log() else console.log("Not a cont trace:") console.log(contTrace) fun debugEff(eff) = if eff is EffectSig then console.log("Debug EffectSig:") console.log("handler: ", eff.handler.constructor.name) console.log("handlerFun: ", eff.handlerFun) debugContTrace(eff.contTrace) else console.log("Not an effect:") console.log(eff) // runtime implementations fun unwind(...saved) = set curEffect.contTrace.last.next = new mut FunctionContFrame(null, saved) curEffect.contTrace.last = curEffect.contTrace.last.next fun mkEffect(handler, handlerFun) = let res = new mut EffectSig(new mut ContTrace(null, null, null, null, false), handler, handlerFun) set res.contTrace.last = res.contTrace res.contTrace.lastHandler = res.contTrace curEffect = res fun handleBlockImpl(cur, handler) = let handlerFrame = new mut HandlerContFrame(null, null, handler) set cur.contTrace.lastHandler.nextHandler = handlerFrame cur.contTrace.lastHandler = handlerFrame cur.contTrace.last = handlerFrame handleEffects(cur) fun enterHandleBlock(handler, body) = let tmp = body() if curEffect === null then tmp else handleBlockImpl(curEffect, handler) fun handleEffects(cur) = while cur is EffectSig then let nxt = handleEffect(cur) if cur === nxt then set curEffect = cur return null else set cur = nxt else return cur // return either new effect, final result or the same continuation if there is no handler fun handleEffect(cur) = // debugEff(cur) // find the handle block corresponding to the current effect let prevHandlerFrame = cur.contTrace while prevHandlerFrame.nextHandler !== null and prevHandlerFrame.nextHandler.handler !== cur.handler do set prevHandlerFrame = prevHandlerFrame.nextHandler // no matching handle block if prevHandlerFrame.nextHandler === null do return cur // the matching handle block let handlerFrame = prevHandlerFrame.nextHandler // unlink and save frames let saved = new mut ContTrace( handlerFrame.next, cur.contTrace.last, handlerFrame.nextHandler, cur.contTrace.lastHandler, false ) set cur.contTrace.last = handlerFrame cur.contTrace.lastHandler = handlerFrame handlerFrame.next = null handlerFrame.nextHandler = null // handle the effect set curEffect = null let tmp = set stackDepth += 2 in cur.handlerFun(resume(cur.contTrace)) if curEffect !== null then set cur = curEffect // relink the saved frames if saved.next !== null do set cur.contTrace.last.next = saved.next cur.contTrace.last = saved.last if saved.nextHandler !== null do set cur.contTrace.lastHandler.nextHandler = saved.nextHandler cur.contTrace.lastHandler = saved.lastHandler cur else // resume the unlinked handle blocks resumeContTrace(saved, tmp) fun resume(contTrace)(value) = if contTrace.resumed do throw Error("Multiple resumption") set contTrace.resumed = true handleEffects(resumeContTrace(contTrace, value)) fun resumeContTrace(contTrace, value) = let cont = contTrace.next let handlerCont = contTrace.nextHandler while cont is FunctionContFrame then set curEffect = null set value = set stackDepth += 3 in cont.resume(value) if curEffect !== null do set value = curEffect if value is EffectSig then set value.contTrace.last.next = cont.next value.contTrace.lastHandler.nextHandler = handlerCont if contTrace.last !== cont do set value.contTrace.last = contTrace.last if handlerCont !== null do set value.contTrace.lastHandler = contTrace.lastHandler return value else set cont = cont.next handlerCont is HandlerContFrame then set cont = handlerCont.next set handlerCont = handlerCont.nextHandler else return value // stack safety mut val stackLimit = 0 // How deep the stack can go before heapifying the stack mut val stackDepth = 0 mut val stackHandler = null mut val stackResume = null object StackDelayHandler with fun delay() = mkEffect of this, k => set stackResume = k fun checkDepth() = if stackDepth >= stackLimit && stackHandler !== null then // this is a tail call to effectful function stackHandler.delay() else () fun runStackSafe(limit, f) = set stackLimit = limit in... set stackDepth = 1 in... set stackHandler = StackDelayHandler in... let result = enterHandleBlock(StackDelayHandler, f) if curEffect !== null do throw new Error("Effect crossed through stack safe boundary") while stackResume !== null do let saved = stackResume set stackResume = null stackDepth = 1 result = saved(()) if curEffect !== null do throw new Error("Effect crossed through stack safe boundary") result class Int31(v) with fun zext() = bitand(v, bitnot(shl(1, 31))) as Int fun sext() = bitor(v, shl(1, 31)) as Int fun plus_impl(lhs, rhs) = if lhs is Int31 and rhs is Int31 then lhs + rhs else unreachable() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Shape.mls ================================================ import "./Block.mls" import "./Option.mls" open Block { Literal, ClassSymbol, showSymbol, isPrimitiveType, isPrimitiveTypeOf } open Option type Shape = Shape.Shape module Shape with... class Shape with constructor Dyn() Lit(val l: Literal) Arr(val shapes: Array[Shape]) Class(val sym: ClassSymbol, val params: Array[Shape]) fun show(s: Shape) = if s is Dyn then "Dyn" Lit(lit) then "Lit(" + Block.showLiteral(lit) + ")" Arr(shapes) then "Arr(" + shapes.map(show).join(", ") + ")" Class(sym, params) then "Class(" + showSymbol(sym) + ", [" + params.map(show).join(", ") + "])" fun sel(s1: Shape, s2: Shape): Array[Shape] = if [s1, s2] is [Class(sym, params), Lit(n)] and n is Str and sym.args is Some(args) and args.find(_ == n) == () then [] is i then [params.(i)] [Dyn, Lit(n)] and n is Str then [Dyn()] [Arr(shapes), Lit(n)] and n is Int then [shapes.(n)] [Arr(shapes), Dyn] then shapes [Dyn, Lit(n)] and n is Int then [Dyn()] [Dyn, Dyn] then [Dyn()] else [] // TODO: return no possibility instead of err? fun static(s: Shape) = if s is Dyn then false Lit(l) then not (l is Str and isPrimitiveType(l)) // redundant bracket? Class(_, params) then params.every(static) Arr(shapes) then shapes.every(static) open Block { Case } fun silh(p: Case): Shape = if p is Block.Lit(l) then Lit(l) Block.Cls(sym, path) then val size = if sym.args is Some(i) then i else 0 Class(sym, Array(size).fill(Dyn)) Block.Tup(n) then Arr(Array(n).fill(Dyn)) // TODO: use Option instead, since all of them return at most one shape fun filter(s: Shape, p: Case): Array[Shape] = if [s, p] is [Lit(l1), Block.Lit(l2)] and l1 == l2 then [s] [Lit(l), Block.Cls(c, _)] and isPrimitiveTypeOf(c, l) then [s] [Arr(ls), Block.Tup(n)] and ls.length == n then [s] [Class(c1, _), Block.Cls(c2, _)] and c1.name == c2.name then [s] [Dyn, _] then [silh(p)] else [] fun rest(s: Shape, p: Case): Array[Shape] = if [s, p] is [Lit(l1), Block.Lit(l2)] and l1 == l2 then [] [Lit(l), Block.Cls(c, _)] and isPrimitiveTypeOf(c, l) then [] [Arr(ls), Block.Tup(n)] and ls.length == n then [] [Class(c1, _), Block.Cls(c2, _)] and c1.name == c2.name then [] [Dyn, _] then [s] else [s] ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Stack.mls ================================================ import "./Predef.mls" open Predef type Stack[A] = Stack.{ Cons[A] | Nil } module Stack with ... data class (::) Cons[A](head: A, tail: Stack[A]) object Nil fun isEmpty(xs) = xs is Nil fun reverseAndAppend(xs, tail) = if xs is h :: t then reverseAndAppend(t, h :: tail) Nil then tail fun reverse(xs) = reverseAndAppend(xs, Nil) fun fromArray(arr) = let ls = Nil i = arr.length - 1 while i >= 0 do ls = arr.at(i) :: ls set i -= 1 ls fun toArray(xs) = xs reverse() toReverseArray() fun toReverseArray(xs) = let arr = mut [] i = 0 while xs is h :: t do arr.push(h) set xs = t arr fun zip(...xss) = fun go(heads, tails) = case h :: t and h is h2 :: t2 then go(h2 :: heads, t2 :: tails)(t) Nil then go(heads, tails)(t) Nil and heads is Nil then (assert tails is Nil, Nil) // * TODO: improve syntax (parens are currently needed) else heads toArray() :: go(Nil, Nil) of tails reverse() go(Nil, Nil) of fromArray(xss) // * Non-recursive efficient internally-mutable list concatenation. fun (:::) concat(xs, ys) = if ys is Nil then xs xs is Nil then ys head' :: tail' then let result = new mut ::(head', ys) current = result // the insertion point rest = tail' // the remaining of `xs` while rest is head :: tail do let next = new mut ::(head, ys) set current.tail = next Object.freeze(current) set current = next rest = tail Object.freeze(result) fun (:+) append(xs, y) = xs ::: y :: Nil fun filter(xs, f) = if xs is head :: tail and f(head) then head :: filter(tail, f) else filter(tail, f) Nil then Nil ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/StrOps.mls ================================================ module StrOps with ... fun (~) concat2(a, b) = a + b fun concat(...xs) = xs.join("") fun from(value) = globalThis.String(value) fun parenthesizedIf(x, cond) = if cond then "(" + x + ")" else x ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/Term.mls ================================================ import "fs" import "process" import "path" import "url" import "./Predef.mls" import "./StrOps.mls" open Predef module Term with... class Symbol(val name: Str) type Literal = null | undefined | Str | Int | Num | Bool class Pattern with constructor LitPattern(val lit: Literal) Var(val sym: Symbol) ClassLike(val sym: Symbol, val trm: Term, val parameters: Array[Symbol] | null) Tuple(val size: Int, val inf: Bool) Record(val entities: Array[[Str, Symbol]]) class Branch(val scrutinee: Ref, val ptrn: Pattern, val continuation: Split) class Split with constructor Cons(val head: Branch, val tail: Split) Let(val sym: Symbol, val term: Term, val tail: Split) Else(val default: Term) End module Keyword with object If While class Statement with constructor LetDecl(val sym: Symbol) DefineVar(val sym: Symbol, val rhs: Term) // TODO: other statements class Term with constructor Lit(val lit: Literal) Builtin(val name: Str) Ref(val sym: Symbol) CSRef(val sym: Symbol, val base: Str, val file: Str | undefined) App(val lhs: Term, val rhs: Term) Sel(val prefix: Term, val nme: Str) DynSel(val prefix: Term, val fld: Term, val arrayIdx: Bool) Tup(val fields: Array[Term]) IfLike(val kw: Keyword.If | Keyword.While, val desugared: Split) Lam(val params: Array[Symbol], val body: Term) Blk(val stats: Array[Statement], val res: Term) New(val cls: Term, val args: Array[Term]) Region(val name: Symbol, val body: Term) RegRef(val reg: Term, val value: Term) Assgn(val lhs: Term, val rhs: Term) Deref(val ref: Term) SetRef(val ref: Term, val value: Term) Ret(val result: Term) Throw(val result: Term) Try(val body: Term, val finallyDo: Term) class Context(val names: Map[Str, Int], val bindings: Map[Symbol, Str], val dependencies: Set[Str], val printOnly: Bool) with fun get(sym) = if bindings.has(sym) then bindings.get(sym) else if printOnly then sym.name else throw Error(StrOps.concat("Invalid binding name ", sym.name)) fun nest = Context(names, new Map(bindings), dependencies, printOnly) fun add(sym) = let fn = freshName(sym.name) bindings.set(sym, fn) fn fun depends(d) = dependencies.add(d) fun freshName(name) = if not names.has(name) do names.set(name, 0) let i = names.get(name) names.set(name, i + 1) StrOps.concat of name, "_", i.toString() fun indent(str, ind, keepLeading) = let res = str.split("\n").map(s => StrOps.concat(ind, s)).join("\n") if keepLeading then res else res.substring(ind.length) fun showStmt(s, ctx) = if s is LetDecl(sym) then let freshName = ctx.add(sym) StrOps.concat("let ", freshName) DefineVar(sym, value) then StrOps.concat of ctx.get(sym), " = ", show(value, ctx) fun showPattern(p, ctx) = if p is LitPattern(lit) then lit.toString() // TODO: other patterns fun paren(t, ctx) = if (t is Ref) || (t is CSRef) || (t is Lit) || (t is Sel) then show(t, ctx) else StrOps.concat of "(", show(t, ctx), ")" fun showSplit(s, ctx, isCont) = if s is Cons(Branch(scrut, ptrn, cont), tail) then StrOps.concat of show(scrut, ctx), " is ", showPattern(ptrn, ctx), " then ", showSplit(cont, ctx, true), "\n", showSplit(tail, ctx, false) Let(sym, term, split) then let nest = ctx.nest let freshName = nest.add(sym) StrOps.concat of "let ", freshName, " = ", show(term, nest), "\n", showSplit(split, nest, false) Else(term) then if isCont then show(term, ctx) else StrOps.concat of "else ", show(term, ctx) End then "" fun show(t, ctx) = if t is Ref(sym) then ctx.get(sym) CSRef(Symbol(name), baseFile, file) then if file is undefined then ctx.depends(baseFile) else ctx.depends(path.join of path.dirname(baseFile), file) name Lit(lit) then lit.toString() Builtin(name) then name Sel(prefix, name) then StrOps.concat of paren(prefix, ctx), ".", name App(lhs, rhs) then StrOps.concat of paren(lhs, ctx), show(rhs, ctx) Tup(fields) then StrOps.concat of "(", fields.map(t => show(t, ctx)).join(", "), ")" Lam(params, body) then let nest = ctx.nest let freshParams = params.map(s => nest.add(s)) StrOps.concat of "(", freshParams.join(", "), ") =>\n", indent(show(body, nest), " ", true) Blk(stats, res) then let nest = ctx.nest StrOps.concat of stats.map(s => showStmt(s, nest)).join("\n"), "\n", show(res, nest) IfLike(Keyword.If, split) then StrOps.concat of "if \n", indent(showSplit(split, ctx, false), " ", true) // TODO: other terms fun print(t) = let ctx = Context(new Map(), new Map(), new Set(), true) console.log(show(t, ctx)) fun genImport(base, p) = StrOps.concat of "import \"./", path.relative(base, url.fileURLToPath(p)).slice(0, -4), ".mls\"" fun codegen(t, file) = let ctx = Context(new Map(), new Map(), new Set(), false) let moduleName = path.parse(file).name let fullpath = path.join of process.cwd(), file let code = StrOps.concat of "module ", moduleName, " with ...\nfun res =\n", indent(show(t, ctx), " ", true), "\n" let dependencies = Array.from(ctx.dependencies).map(s => genImport(path.dirname(fullpath), s)) if not fs.existsSync(file) do fs.writeFileSync(file, "", "utf8") let originData = fs.readFileSync(file, "utf8") let newData = StrOps.concat of dependencies.join("\n"), "\n", code if newData != originData do fs.writeFileSync(file, newData, "utf8") ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/TreeTracer.mls ================================================ import "./Predef.mls" import "./StrOps.mls" open Predef { fold } module TreeTracer with ... class TreeTracer with mut val steps = 0 mut val enabled = false fun output(outie, innie, innieAlt, message) = if enabled do let lines = message.split("\n") lines.forEach of (line, i, xs) => if let postfix = if lines.length > 1 then " ↵" else "" i is 0 then console.log(outie + innie + line + postfix) i + 1 == lines.length then console.log(outie + innieAlt + line) else console.log(outie + innieAlt + line + postfix) fun enter(...pieces) = output of if steps > 0 then "│ ".repeat(steps - 1) + "├─" else "" if steps > 0 then "┮ " else "┍ " "│ " fold(+) of ...pieces set steps += 1 fun print(...pieces) = let message = if pieces is [..pieces, Int as line] then fold(+) of ...pieces, " [Ln " + line + "]" else fold(+) of ...pieces output of if steps > 0 then "│ ".repeat(steps - 1) else "" if steps > 0 then "├ " else "" "│ " message fun leave(...pieces) = set steps -= 1 output of "│ ".repeat(steps) "┕ " " " fold(+) of ...pieces fun trace[A](intro: Str, makeOutro: A -> Str, thunk: () -> A): A = enter of intro val result = thunk() leave of makeOutro of result result fun reset() = set steps = 0 ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/XML.mls ================================================ import "./Predef.mls" import "./Iter.mls" open Predef open Iter { mapping, joined } data class StyleAttributeValue(rules) with fun toValue() = JSON.stringify of Object.entries(rules) mapping of case [name, value] then name + ": " + value joined("; ") module XML with ... fun serializeValue(value) = if value is Str then JSON.stringify(value) StyleAttributeValue then value.toValue() fun joinAttributes(attributes) = attributes mapping of case [name, value] then name + "=" + serializeValue(value) StyleAttributeValue as style then "style=" + style.toValue() Object as record then joinAttributes(Object.entries(record)) joined(" ") fun elem(tagName, ...attributes)(...elements) = fold(+) of "<" tagName if attributes is [] then "" else " " + joinAttributes(attributes) ">" ...elements "" fun tag(tagName)(...attributes) = fold(+) of "<" tagName if attributes is [] then "" else " " + joinAttributes(attributes) " " "/>" fun style(rules) = StyleAttributeValue(rules) fun html(...attributes)(...elements) = "" + elem("html", ...attributes)(...elements) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mls ================================================ import "fs" import "../StrOps.mls" open StrOps import "../Predef.mls" open Predef declare class Num declare class Bool declare val String data class Accounting with ... fun display(amt): Str = (amt / 1000).toFixed(1) val warnings = mut [] data class Project(val num: Str) data class Line(val name: Str, val proj: Project, val starting_balance: Num, val isMatchable: Bool) with mut val balance = starting_balance fun expense(amt) = set balance = balance -. amt fun mustBeEmpty() = if balance > 10_000 do warnings.push of "> **❗️** Unspent balance of " ~ name ~ ": `" ~ display(balance) ~ "`" val lines = mut [] fun mkLine(nme, proj, starting_balance, matchable) = let line = Line(nme, proj, starting_balance, matchable) lines.push(line) line data class Report(fileName) with fs.writeFileSync(fileName, "# Accounting\n") fun w(txt) = fs.appendFileSync(fileName, txt) fun wln(txt) = fs.appendFileSync(fileName, txt ~ "\n") fun init() = wln of "" wln of "|" ~ "Year" ~ "|" ~ lines.map(x => x.name).join("|") ~ "|" wln of "|" ~ "---" ~ "|" ~ lines.map(x => "--:").join("|") ~ "|" fun snapShot(label) = wln of "|" ~ String(label) ~ "|" ~ lines.map(x => display(x.balance)).join("|") ~ "|" fun wrapUp() = wln of "" warnings.forEach of x => (wln(x), wln("")) wln of "### Remaining Available Funds" wln of "" wln of "|" ~ "Summary" ~ "| |" wln of "|" ~ "---" ~ "|--:|" wln of "|" ~ "Matchable" ~ "|" ~ display( lines.filter(x => x.isMatchable).map(x => x.balance).reduce((a, b) => a +. b, 0) ) ~ "|" wln of "|" ~ "Non-matchable" ~ "|" ~ display( lines.filter(x => not x.isMatchable).map(x => x.balance).reduce((a, b) => a +. b, 0) ) ~ "|" fun process(filename, k) = let report = Report(filename) report.init() k(report) report.wrapUp() print of "Report written to " ~ filename ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/CSV.mls ================================================ // Adapted from https://www.bennadel.com/blog/1504-ask-ben-parsing-csv-strings-with-javascript-exec-regular-expression-command.htm // (JS code commented at bottom) import "../Predef.mls" open Predef // The default delimiter is the comma, but this can be overriden here. data class CSV(strDelimiter) with ... // Check to see if the delimiter is defined. If not, then default to comma. if strDelimiter is undefined then set strDelimiter = "," val objPattern = new mut RegExp of // Delimiters. "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" // Quoted fields. + "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" // Standard fields. + "([^\"\\" + strDelimiter + "\\r\\n]*))" "gi" // This will parse a delimited string into an array of arrays. fun toArrays(strData) = // Create an array to hold our data. Give the array a default empty first row. let arrData = mut [mut []] // Keep looping over the regular expression matches until we can no longer find a match. while let arrMatches = objPattern.exec(strData) arrMatches !== null do // Get the delimiter that was found. let strMatchedDelimiter = arrMatches.1 // Check to see if the given delimiter has a length (is not the start of string) // and if it matches field delimiter. // If id does not, then we know that this delimiter is a row delimiter. if strMatchedDelimiter.length != 0 and strMatchedDelimiter != strDelimiter do // Since we have reached a new row of data, add an empty row to our data array. arrData.push(mut []) // Now that we have our delimiter out of the way, // let's check to see which kind of value we captured (quoted or unquoted). let strMatchedValue = if arrMatches.2 != undefined then // We found a quoted value. When we capture this value, unescape any double quotes. arrMatches.2.replace of new mut RegExp("\"\"", "g") "\"" else // We found a non-quoted value. arrMatches.3 arrData.at(arrData.length - 1).push(strMatchedValue) arrData /* // This will parse a delimited string into an array of // arrays. The default delimiter is the comma, but this // can be overriden in the second argument. function CSVToArray( strData, strDelimiter ){ // Check to see if the delimiter is defined. If not, // then default to comma. strDelimiter = (strDelimiter || ","); // Create a regular expression to parse the CSV values. var objPattern = new mut RegExp( ( // Delimiters. "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" + // Quoted fields. "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + // Standard fields. "([^\"\\" + strDelimiter + "\\r\\n]*))" ), "gi" ); // Create an array to hold our data. Give the array // a default empty first row. var arrData = [[]]; // Create an array to hold our individual pattern // matching groups. var arrMatches = null; // Keep looping over the regular expression matches // until we can no longer find a match. while (arrMatches = objPattern.exec( strData )){ // Get the delimiter that was found. var strMatchedDelimiter = arrMatches[ 1 ]; // Check to see if the given delimiter has a length // (is not the start of string) and if it matches // field delimiter. If id does not, then we know // that this delimiter is a row delimiter. if ( strMatchedDelimiter.length != 0 && (strMatchedDelimiter != strDelimiter) ){ // Since we have reached a new row of data, // add an empty row to our data array. arrData.push( [] ); } // Now that we have our delimiter out of the way, // let's check to see which kind of value we // captured (quoted or unquoted). if (arrMatches[ 2 ]){ // We found a quoted value. When we capture // this value, unescape any double quotes. var strMatchedValue = arrMatches[ 2 ].replace( new mut RegExp( "\"\"", "g" ), "\"" ); } else { // We found a non-quoted value. var strMatchedValue = arrMatches[ 3 ]; } // Now that we have our value string, let's add // it to the data array. arrData[ arrData.length - 1 ].push( strMatchedValue ); } // Return the parsed data. return( arrData ); } */ ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/BasicExpr.mls ================================================ import "../../StrOps.mls" import "../../Option.mls" import "../../Predef.mls" open StrOps { parenthesizedIf } open Option { Some, None } open Predef { mkStr } type OptionT[A] = Some[A] | None module BasicExpr with type Expr = Lit | Var | Add | Mul | Err data class Lit(value: Int) Var(name: Str) Add(left: Expr, right: Expr) Mul(left: Expr, right: Expr) Err(expr: OptionT[Expr], msg: Str) fun withErr(expr, msg) = Err(Some(expr), msg) fun justErr(msg) = Err(None, msg) fun prettyPrint(tree: Expr): Str = if tree is Lit(value) then value.toString() Var(name) then name Add(left, right) then left prettyPrint() + " + " + right prettyPrint() Mul(left, right) then StrOps.concat of left prettyPrint() parenthesizedIf(left is Add) " * " right prettyPrint() parenthesizedIf(right is Add) Err(Some(expr), msg) then "{ " + expr prettyPrint() + " | " + JSON.stringify(msg) + " }" Err(None, msg) then "{ " + JSON.stringify(msg) + " }" ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Expr.mls ================================================ import "../../StrOps.mls" import "../../Option.mls" import "../../Predef.mls" open StrOps { parenthesizedIf } open Option { Some, None } type OptionT[A] = Some[A] | None module Expr with val opPrec = case "**" then [70, 69] "*" then [50, 50] "/" then [50, 50] "+" then [30, 30] "-" then [30, 30] type Expr = Lit | Var | Inf | Err data class Lit(value: Int) Var(name: Str) Inf(op: Str, left: Expr, right: Expr) Err(expr: OptionT[Expr], msg: Str) fun withErr(expr, msg) = Err(Some(expr), msg) fun justErr(msg) = Err(None, msg) fun prettyPrint(tree: Expr): Str = if tree is Lit(value) then value.toString() Var(name) then name Inf(op, left, right) then if opPrec(op) is [l, r] then StrOps.concat of left prettyPrint() parenthesizedIf of left is Inf(op', _, _) and opPrec(op') is [_, r'] and r' < l " " op " " right prettyPrint() parenthesizedIf of right is Inf(op', _, _) and opPrec(op') is [l', _] and r > l' Err(Some(expr), msg) then "{ " + expr prettyPrint() + " | " + JSON.stringify(msg) + " }" Err(None, msg) then "{ " + JSON.stringify(msg) + " }" ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Extension.mls ================================================ import "./Keywords.mls" import "./Rules.mls" import "./Token.mls" import "./ParseRule.mls" import "./Tree.mls" import "../../Stack.mls" import "../../Option.mls" import "../../Predef.mls" import "../../Iter.mls" import "../../MutMap.mls" open Predef open Stack open Option { Some, None } open Token { LiteralKind } open ParseRule { Choice } module Extension with ... fun isDiagramDirective(tree: Tree) = tree is Tree.Define(Tree.DefineKind.Directive, [Tree.Ident("diagram", _), _] :: Nil) fun parsePrecedenceTree(tree: Tree) = if tree is Tree.Ident("None", _) then None Tree.App(Tree.Ident("Some", _), Tree.Literal(Token.LiteralKind.Integer, value)) then Some(parseInt(value, 10)) fun extendKeyword(tree: Tree) = if tree is Tree.Tuple(keyword :: leftPrec :: rightPrec :: Nil) and keyword is Tree.Literal(Token.LiteralKind.String, name) then let leftPrec' = parsePrecedenceTree(leftPrec) let rightPrec' = parsePrecedenceTree(rightPrec) Keywords.keyword(name, leftPrec', rightPrec') else print of "expect a string literal but found " + keyword else print of "expect a tuple but found " + tree // * Add an empty parse rule to the map associated with the given name. fun newCategory(tree: Tree) = if tree is Tree.Literal(Token.LiteralKind.String, name) and Rules.syntaxKinds |> MutMap.get(name) is Some(rule) then print of "Category already exists: " + rule.display None then Rules.syntaxKinds |> MutMap.insert(name, ParseRule.ParseRule(name, Nil)) Rules.extendedKinds.add(name) else print of "expect a string literal but found " + tree pattern OpenCategory = "term" | "type" | "decl" pattern ClosedCategory = "ident" | "typevar" fun extendCategory(choiceBodyTree: Tree) = if parseChoiceTree(choiceBodyTree) is Some([kindName, choice]) and Rules.syntaxKinds |> MutMap.get(kindName) is Some(rule) and kindName is // If the `kindName` is built-in and extensible, we check if the choice // refers to another category in the beginning. If so, we inline the // referenced category's choices into the current choice. OpenCategory and choice is Choice.Ref(refKindName, process, outerPrec, innerPrec, rest) and Rules.syntaxKinds |> MutMap.get(refKindName) is Some(refRule) then // TODO: The `process` function should be applied to the referenced rule. // TODO: How to handle `outerPrec` and `innerPrec`? rule.extendChoices of refRule.andThen(rest, process).choices else // The referenced category is not found in the map. Stop. print of "Unknown referenced syntax category: " + refKindName else // Otherwise, we can directly extend the target parse rule. rule.extendChoices(choice :: Nil) ClosedCategory then print of "Cannot extend a closed category: " + kindName else rule.extendChoices(choice :: Nil) None then print of "Unknown syntax kind: " + kindName None then print of "Invalid syntax description: " + choiceBodyTree Tree.summary() fun parseChoiceTree(tree: Tree) = fun go(trees: Stack[Tree]): Choice[Tree] = let res = if trees is Tree.App(Tree.Ident("keyword", _), Tree.Literal(LiteralKind.String, name)) :: rest and Keywords.all |> MutMap.get(name) is Some(keyword) then Choice.keyword(keyword)(go(rest)) Tree.Literal(LiteralKind.String, name) :: rest then Choice.reference(name)(process: Cons, name: "unnamed", choices: tuple of go(rest)) Nil then Choice.end(Nil) res if tree is Tree.Tuple(categoryIdent :: choiceTree :: funcIdent :: Nil) and categoryIdent is Tree.Literal(LiteralKind.String, categoryName) and funcIdent is Tree.Ident and let op = (trees) => trees Iter.fromStack() Iter.folded of funcIdent, (f, x) => Tree.App(f, x) choiceTree is Tree.Bracketed(Token.Square, Tree.Tuple(elements)) then Some of tuple of categoryName go(elements) Choice.map(op) Tree.Bracketed(Token.Square, other) then Some of tuple of categoryName go(other :: Nil) Choice.map(op) else print of "Expect the choiceTree to be a bracketed term but found", choiceTree Tree.summary() None else print of "Expect a the category to be an identifier but found " + categoryIdent Tree.summary() None else print of "Expect the definition to be a tuple but found " + tree Tree.summary() None ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Keywords.mls ================================================ import "../../Option.mls" import "../../Iter.mls" import "../../Predef.mls" import "../../MutMap.mls" open Predef open Option { Some, None } module Keywords with ... // https://v8.dev/blog/pointer-compression // The maximal and minimal values of V8's Smi on 64-bit platforms. val INT_MIN = -2147483648 val INT_MAX = 2147483647 class Keyword(val name: Str, val leftPrec, val rightPrec) with fun leftPrecOrMin = if leftPrec is Some(prec) then prec else INT_MIN fun rightPrecOrMin = if rightPrec is Some(prec) then prec else INT_MIN fun leftPrecOrMax = if leftPrec is Some(prec) then prec else INT_MAX fun rightPrecOrMax = if rightPrec is Some(prec) then prec else INT_MAX fun toString() = fold(+) of "Keyword(`", name, "`, " (if leftPrec is Some(prec) then prec.toString() else "N/A"), ", ", (if rightPrec is Some(prec) then prec.toString() else "N/A"), ")" fun makePrecMap(startPrec: Int, ...ops) = let m = MutMap.empty i = 0 while i < ops.length do ops.at(i).split(" ").forEach of (op, _, _) => if op.length > 0 do m |> MutMap.insert of op, i + startPrec set i += 1 m val all = MutMap.empty fun keyword(name, leftPrec, rightPrec) = let result = Keyword(name, leftPrec, rightPrec) all |> MutMap.insert(name, result) result let prec = 0 fun currPrec = Some(prec) fun nextPrec = set prec = prec + 1 Some(prec) let basePrec = currPrec // the lowest precedence val _terminator = keyword(";;", basePrec, basePrec) val _class = keyword("class", None, basePrec) let semiPrec = nextPrec let commaPrec = nextPrec val _semicolon = keyword(";", semiPrec, basePrec) val _comma = keyword(",", commaPrec, semiPrec) let eqPrec = nextPrec let ascPrec = nextPrec val _equal = keyword("=", eqPrec, eqPrec) val _and = keyword("and", None, currPrec) val _bar = keyword of "|", None, None val _thinArrow = keyword of "->", nextPrec, eqPrec val _colon = keyword(":", ascPrec, eqPrec) val _match = keyword("match", nextPrec, currPrec) val _while = keyword of "while", nextPrec, currPrec val _for = keyword of "for", nextPrec, currPrec val _to = keyword of "to", None, None val _downto = keyword of "downto", None, None val _do = keyword of "do", None, None val _done = keyword of "done", None, None val _of = keyword of "of", None, None val _with = keyword("with", None, currPrec) val _case = keyword("case", None, currPrec) let thenPrec = nextPrec val _if = keyword("if", nextPrec, thenPrec) val _leftArrow = keyword of "<-", thenPrec, thenPrec val _then = keyword("then", thenPrec, thenPrec) val _else = keyword("else", thenPrec, thenPrec) val _let = keyword("let", eqPrec, semiPrec) val _in = keyword("in", thenPrec, thenPrec) val _true = keyword("true", None, None) val _false = keyword("false", None, None) // val _fatArrow = keyword of "=>", nextPrec, eqPrec val _as = keyword("as", nextPrec, currPrec) val _fun = keyword("fun", currPrec, _thinArrow.leftPrec) val _function = keyword("function", currPrec, eqPrec) val _type = keyword("type", currPrec, None) val _exception = keyword("exception", currPrec, None) val _rec = keyword("rec", currPrec, eqPrec) val _hash = keyword("#", None, None) val maxKeywordPrec = prec let precMap = makePrecMap of maxKeywordPrec "," "@" ":" "|" "&" "=" "/ \\" "^" "!" "< >" "+ -" "* %" "~" "" // prefix operators "" // applications "." // The largest precedence value of all operators. val periodPrec = precMap |> MutMap.get(".") |> Option.unsafe.get val _period = keyword of ".", Some(periodPrec), Some(periodPrec) val maxOperatorPrec = periodPrec val appPrec = maxOperatorPrec - 1 val prefixPrec = appPrec - 1 // Get the precedence of a single character operator. fun charPrec(op) = precMap |> MutMap.get(op) |> Option.unsafe.get fun charPrecOpt(op) = precMap |> MutMap.get(op) val _asterisk = keyword of "*", charPrecOpt("*"), charPrecOpt("*") val _equalequal = keyword of "==", charPrecOpt("="), charPrecOpt("=") let bracketPrec = Some(maxOperatorPrec + 1) val _leftRound = keyword of "(", bracketPrec, basePrec val _rightRound = keyword of ")", basePrec, None val _leftSquare = keyword of "[", bracketPrec, basePrec val _rightSquare = keyword of "]", basePrec, None val _leftCurly = keyword of "{", bracketPrec, basePrec val _rightCurly = keyword of "}", basePrec, None val _begin = keyword of "begin", bracketPrec, basePrec val _end = keyword of "end", basePrec, None let builtinKeywords = new Set(all |> MutMap.keysIterator) fun extended = all Iter.filtering of case [k, _] then builtinKeywords.has(k) is false Iter.toArray() MutMap.toMap() pattern Letter = "a" ..= "z" | "A" ..= "Z" fun hasLetter(s) = s Iter.some((ch, _, _) => ch is Letter) pattern FloatOperator = "+." | "-." | "*." | "/." pattern RightAssociative = "@" | "/" | "," | ":" fun opPrec(opStr) = if opStr is FloatOperator then tuple of Keywords.charPrec of opStr.at(0) Keywords.charPrec of opStr.at(0) opStr hasLetter() then tuple of Keywords.maxKeywordPrec Keywords.maxKeywordPrec let lastChar = opStr.at(-1) let rightPrec = Keywords.charPrec of lastChar else tuple of Keywords.charPrec of opStr.at(0) (Keywords.charPrec of lastChar) + if lastChar is RightAssociative then -1 else 0 fun opPrecOpt(opStr) = if opStr is "" then None opStr is FloatOperator then Some of tuple of Keywords.charPrec of opStr.at(0) Keywords.charPrec of opStr.at(0) opStr hasLetter() then Some of tuple of Keywords.maxKeywordPrec Keywords.maxKeywordPrec let lastChar = opStr.at(-1) Keywords.charPrecOpt(lastChar) is Some(rightPrec) and Keywords.charPrecOpt(opStr.at(0)) is Some(leftPrec) then Some of tuple of leftPrec rightPrec + (if lastChar is RightAssociative then -1 else 0) else None ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Lexer.mls ================================================ #config(liftDefns: Some(LiftDefns)) import "../../Predef.mls" open Predef import "../../Char.mls" import "../../Stack.mls" import "../../StrOps.mls" import "../../Option.mls" import "../../Iter.mls" import "./Token.mls" open Stack open StrOps open Option { Some, None} open Token { LiteralKind, LineLookupTable } type TokenType = Token.Token type Opt[T] = Some[T] | None module Lexer with ... class Location(start: Int, end: Int) class Message(description: Str, location: Location) class Report(messages: Stack[Message]) pattern IdentifierStart = Char.Letter | "_" pattern IdentifierBody = Char.Letter | Char.Digit | "_" | "'" pattern Operator = "," | ";" | "!" | "#" | "%" | "&" | "*" | "+" | "-" | "/" | ":" | "<" | "=" | ">" | "?" | "@" | "\\" | "^" | "|" | "~" | "." pattern Bracket = "(" | ")" | "[" | "]" | "{" | "}" pattern IdentifierQuote = "'" | "`" fun makeLineLookupTable(text: Str): LineLookupTable = let i = 0 n = text.length ns = mut [] while i < n do let i' = text.indexOf("\n", i) if i' == -1 then set i = n ns.push(n) else set i = i' + 1 ns.push(i') LineLookupTable(ns) fun lex(str: Str, options) = using LineLookupTable = makeLineLookupTable(str) fun char(idx: Int) = if idx < str.length then (Some of str.charAt of idx) else None // Consume a sequence of characters that satisfy a predicate. // The function returns the accumulated string and the following index. fun take(pred: Str -> Bool, idx: Int, acc: Str): [Int, Str] = while (char of idx) is Some(ch) and pred(ch) do set idx += 1 acc += ch [idx, acc] fun whitespace(idx: Int): Int = while char(idx) is Some(Char.Whitespace) do set idx = idx + 1 idx fun digits(idx: Int, acc: Str) = while char(idx) is Some(Char.Digit as ch) do set idx = idx + 1 set acc = acc + ch [idx, acc] fun hex(idx: Int, acc: Str) = while char(idx) is Some(Char.Digit as ch) do set idx = idx + 1 set acc = acc + ch [idx, acc] fun identifier(idx: Int, acc: Str) = while char(idx) is Some(IdentifierBody as ch) do set idx = idx + 1 set acc = acc + ch tuple of idx if acc is "true" then Token.boolean("true", idx) "false" then Token.boolean("false", idx) else Token.identifier(acc, idx) fun operator(idx: Int, acc: Str) = while char(idx) is Some(Operator as ch) do set idx = idx + 1 set acc = acc + ch [idx, Token.symbol(acc, idx)] fun comment(idx: Int) = let start = idx let content = "" if char(idx) is Some("/") then set idx = idx + 1 while char(idx) is Some(ch) and ch !== "\n" do set idx = idx + 1 set content = content + ch [idx, Token.comment(content, start, idx)] Some("*") then let terminated = false set idx = idx + 1 while terminated is false and char(idx) is Some("*") and char(idx + 1) is Some("/") then set idx = idx + 2 set terminated = true Some(ch) then set idx = idx + 1 set content = content + ch if terminated then [idx, Token.comment(content, start, idx)] else [idx, Token.error(start, idx)] else operator(idx, "/") fun scanHexDigits(idx: Int, lim: Int, acc: Int, cnt: Int): [Int, Int, Int] = if char(idx) is Some(Char.HexDigit as ch) and cnt < lim then scanHexDigits(idx + 1, lim, acc * 16 + parseInt(ch, 16), cnt + 1) else scanHexDigits(idx + 1, lim, acc, cnt + 1) else [idx, acc, cnt] fun escape(idx: Int): [Int, Opt[Str]] = if char(idx) is Some("n") then [idx + 1, Some("\n")] Some("r") then [idx + 1, Some("\r")] Some("t") then [idx + 1, Some("\t")] Some("0") then [idx + 1, Some("\u{0}")] Some("b") then [idx + 1, Some("\b")] Some("f") then [idx + 1, Some("\f")] Some("\"") then [idx + 1, Some("\"")] Some("\\") then [idx + 1, Some("\\")] Some("x") then if scanHexDigits(idx + 1, 2, 0, 0) is [idx, cp, cnt] then ... // TODO: ensure that `cnt == 2` tuple of idx, if cnt is 0 then None else Some of String.fromCodePoint(cp) Some("u") and // Unicode code point escape: "\u{XXXXXX}" char(idx + 1) is Some("{") then if scanHexDigits(idx + 2, 6, 0, 0) is [idx, cp, cnt] then ... // TODO: ensure that `1 <= cnt <= 6` let idx = if char(idx) is Some("}") then idx + 1 else // TODO: report missing "}" idx tuple of idx, if cnt is 0 then None else Some of String.fromCodePoint(cp) // Trandition Unicode range: "\uXXXX" else if scanHexDigits(idx + 1, 4, 0, 0) is [idx, cp, cnt] then ... // TODO: ensure that `cnt == 4` tuple of idx, if cnt is 0 then None else Some of String.fromCodePoint(cp) Some(ch) then [idx + 1, Some(ch)] None then [idx, None] fun string(idx: Int): [Int, Token.Literal] = let startIndex = idx content = "" terminated = false while terminated is false do if char(idx) is Some("\"") do set terminated = true idx += 1 Some("\\") do if escape(idx + 1) is [idx', chOpt] then ... set idx = idx' if chOpt is Some(ch) do set content += ch Some(ch) do set idx += 1 content += ch None do // TODO report missing quote set terminated = true [idx, Token.string(content, startIndex, idx)] fun number(idx: Int, head: Str) = if head is "0" and char(idx) is None then [idx, Token.integer("0", idx)] Some("b") and take(x => x is Char.BinDigit, idx + 1, "") is [idx', bs] then [idx', Token.integer("0b" ~ bs, idx)] Some("o") and take(x => x is Char.OctDigit, idx + 1, "") is [idx', os] then [idx', Token.integer("0o" ~ os, idx)] Some("x") and take(x => x is Char.HexDigit, idx + 1, "") is [idx', xs] then [idx', Token.integer("0x" ~ xs, idx)] Some(".") and digits(idx + 1, ".") is [idx', ds] then [idx', Token.decimal("0." ~ ds, idx)] Some(_) and digits(idx, head) is [idx', integer] then [idx', Token.integer(integer, idx)] digits(idx, head) is [idx', integer] and char(idx') is Some(".") and digits(idx' + 1, "") is [idx'', fraction] then [idx'', Token.decimal(integer ~ "." ~ fraction, idx)] else [idx', Token.integer(integer, idx)] fun scan(idx: Int, acc: Stack[TokenType]): Stack[TokenType] = fun go(idx: Int, tok: TokenType) = if options.noWhitespace and tok is Token.Comment then scan(idx, acc) Token.Space then scan(idx, acc) else scan(idx, tok :: acc) fun go_tup(tup: [Int, TokenType]) = go(tup.0, tup.1) if char(idx) is None then reverse of acc Some(ch) and ch is... Char.Whitespace and whitespace(idx) is idx' then go(idx', Token.space(idx, idx')) "\"" then go_tup(string(idx + 1)) Bracket as b then go(idx + 1, Token.symbol(b, idx)) "/" then go_tup(comment(idx + 1)) Operator as ch then go_tup(operator(idx + 1, ch)) Char.Digit as ch then go_tup(number(idx + 1, ch)) IdentifierStart as ch then go_tup(identifier(idx + 1, ch)) IdentifierQuote as quote and char(idx + 1) is Some(IdentifierStart as ch) and identifier(idx + 2, quote + ch) is [idx', token] and token is Token.Identifier(name, _) then go(idx', Token.identifier(name, idx)) else go(idx + 1, Token.error(idx, idx + 1)) else print("Unrecognized character: '" ~ ch ~ "'") go(idx + 1, Token.error(idx, idx + 1)) scan(0, Nil) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRule.mls ================================================ import "../../MutMap.mls" import "../../Iter.mls" import "../../Option.mls" import "../../Stack.mls" import "../../Predef.mls" import "./Keywords.mls" import "./Token.mls" import "./Tree.mls" open Option { Some, None } open Predef { check, tuple } open Stack module ParseRule with ... abstract class Choice[A] module Choice with data class Keyword[A](keyword: Keywords.Keyword, rest: ParseRule[A]) extends Choice[A] data class Ref[A, B]( kind: Str, process: (Tree, B) -> A, outerPrec: Option[Int], innerPrec: Option[Int], rest: ParseRule[B] ) extends Choice[A] data class End[A](value: A) extends Choice[A] // * An alternative route that branches off from the main railroad and // * eventually connects back to the main railroad. For example, `foo`, `bar`, // * and `baz` are choices represented by `init`. If `optional` is set to // * `true`, an additional empty choice is added to `rule`, making it possible // * to skip the entire `rule`. // * _______ foo ______ // * / \ // * /-------- bar -------\ // * / \ // * ----- start ----+---------- baz ---------+--- end --------- data class Siding[A, B, C]( init: ParseRule[B], optional: Bool, rest: ParseRule[C], process: (Option[B], C) -> A ) extends Choice[A] let ensureChoices(xs, name) = xs Iter.zippingWithIndex() Iter.each of case [item, index] then check(item is Choice, name + ": element [" + index + "] is not Choice") // Shorthands for constructing rule choices. fun keyword(keyword)(...choices) = ensureChoices(choices, "Choice.keyword") Keyword(keyword, rule("`" + keyword.name + "` keyword", ...choices)) let shouldHaveFunction(options, key: Str, defaultValue, callerName: Str) = if let func = options.(key) typeof(func) === "function" then func func is undefined then defaultValue else throw TypeError(callerName + ": `" + key + "` is not a string") let shouldHaveStr(options, key: Str, defaultValue: Str, callerName: Str) = if let value = options.(key) value is Str then value value is undefined then defaultValue else throw TypeError(callerName + ": `" + key + "` is not a string") let shouldHaveInt(options, key: Str, callerName: Str) = if let value = options.(key) value is Int then Some(value) value is undefined then None else throw TypeError(callerName + ": `" + key + "` is not an Int") let shouldHaveBool(options, key: Str, defaultValue: Bool, callerName: Str) = if let value = options.(key) value is Bool then value value is undefined then defaultValue else throw TypeError(callerName + ": `" + key + "` is not a Boolean") let shouldHaveRuleLike(options, key: Str, ruleName: Str, callerName: Str) = if let choices = options.(key) choices is ParseRule then choices choices is Choice then rule(ruleName, choices) choices is [..._] then ensureChoices(choices, "Choice.reference") rule(ruleName, ...choices) choices is undefined then rule(ruleName) else throw TypeError(callerName + ": `" + key + "` is neither a rule nor a choice") fun reference(kind: Str)(fields: Object) = let ruleName = fields shouldHaveStr("name", "unnamed", "Choice.reference") Ref of kind fields shouldHaveFunction("process", tuple, "Choice.reference") fields shouldHaveInt("outerPrec", "Choice.reference") fields shouldHaveInt("innerPrec", "Choice.reference") fields shouldHaveRuleLike("choices", ruleName, "Choice.reference") val term = reference("term") val typeExpr = reference("type") val ident = reference("ident") val typeVar = reference("typevar") fun optional(init, rest) = check(init is ParseRule, "Choice.optional: init is not ParseRule") check(rest is ParseRule, "Choice.optional: rest is not ParseRule") Siding(init, true, rest, tuple) fun siding(fields) = let optional = fields shouldHaveBool("optional", false, "Choice.siding") let initName = fields shouldHaveStr("initName", "unnamed", "Choice.siding") let restName = fields shouldHaveStr("restName", "unnamed", "Choice.siding") let init = fields shouldHaveRuleLike("init", initName, "Choice.siding") let rest = fields shouldHaveRuleLike("rest", restName, "Choice.siding") let defaultProcess = if optional then tuple else (initRes, restRes) => if initRes is Some(initRes) then [initRes, restRes] let process = fields shouldHaveFunction("process", defaultProcess, "Choice.siding") Siding of init, optional, rest, process fun end(value) = End(value) fun map[A, B](choice: Choice[A], op: A -> B): Choice[B] = if choice is Keyword(keyword, rest) then Keyword(keyword, rest.map(op)) Ref(kind, process, outerPrec, innerPrec, rest) then Ref(kind, (x, y) => op(process(x, y)), outerPrec, innerPrec, rest) Siding(init, optional, rest, process) then Siding(init, optional, rest, (x, y) => op(process(x, y))) End(value) then End(op(value)) data class Lazy[out A](init: () -> A) with mut val cached: Option[A] = None fun reset() = set cached = None fun get() = if cached is Some(v) then v else let v = init() set cached = Some(v) v fun lazy(init) = new Lazy of init // ____ ____ _ // | _ \ __ _ _ __ ___ ___| _ \ _ _| | ___ // | |_) / _` | '__/ __|/ _ \ |_) | | | | |/ _ \ // | __/ (_| | | \__ \ __/ _ <| |_| | | __/ // |_| \__,_|_| |___/\___|_| \_\\__,_|_|\___| // // ============================================= data class ParseRule[A](name: Str, mut val choices: Stack[Choice[A]]) with open Choice { Keyword, Ref, End, Siding } fun map[B](op: A -> B) = new mut ParseRule of name, choices Iter.fromStack() Iter.mapping of choice => choice Choice.map(op) Iter.toStack() fun andThen[B, C](rest: ParseRule[B], process: (A, B) -> C) = fun go(rule: ParseRule[B]) = new ParseRule of rule.name, rule.choices Iter.fromStack() Iter.mapping of case Keyword(keyword, rest') then [Keyword(keyword, go(rest'))] Ref(kind, process, outerPrec, innerPrec, rest') then let process' = (lhs, rhsInnerResult) => if rhsInnerResult is [rhs, innerResult] then [process(lhs, rhs), innerResult] do check(false, "illgeal result from inner") [Ref(kind, process', outerPrec, innerPrec, go(rest'))] End(value) then rest.choices Iter.fromStack() Iter.mapping of choice => choice Choice.map(result => [value, result]) Siding(rule, optional, rest', process) then let process' = (initRes, restRes) => if restRes is [restRes', innerRes] then [process(initRes, restRes'), innerRes] do check(false, "illegal result from inner") [Siding(rule, optional, go(rest'), process')] Iter.flattening() Iter.toStack() go(this).map of res => process(res.0, res.1) let _endChoice = lazy of () => choices Iter.fromStack() Iter.firstDefined of case End(value) then Some(value) Siding(init, optional, rest, process) and optional and rest.endChoice is Some(restRes) then // It's actually ambiguous here. process(None, restRes) init.endChoice is Some(initRes) and rest.endChoice is Some(restRes) then process(Some(initRes), restRes) else None // Collect the first end choice in this rule. fun endChoice = _endChoice.get() let _keywordChoices = lazy of () => choices Iter.fromStack() Iter.mapping of case Keyword(keyword, rest) then [[keyword.name, rest]] Siding(init, optional, rest, process) then init.keywordChoices Iter.mapping of case [keyword, rule] then [keyword, rule.map(Some).andThen(rest, process)] Iter.appended of if optional then rest.keywordChoices Iter.mapping of case [keyword, rule] then [keyword, rule.map(res => process(None, res))] else [] Iter.toArray() else [] Iter.flattening() Iter.toArray() MutMap.toMap() // Collect all keyword choices in this rule. fun keywordChoices = _keywordChoices.get() let _refChoice = lazy of () => choices Iter.fromStack() Iter.firstDefined of case Ref as ref then Some(ref) Siding(init, optional, rest, process) and init.refChoice is Some(Ref(k, process', op, ip, rest')) then let process''(exprRes, pairRes) = if pairRes is [restRes', restRes] then process(process'(exprRes, restRes'), restRes) let rest'' = rest'.andThen(rest, tuple) Some of Ref(k, process'', op, ip, rest'') optional and rest.refChoice is Some(Ref(k, process', op, ip, rest')) then let process''(exprRes, restRes) = process(None, process'(exprRes, restRes)) Some of Ref(k, process'', op, ip, rest') else None fun refChoice = _refChoice.get() fun extendChoices(newChoices: Stack[Choice[A]]) = set choices = choices ::: newChoices _endChoice.reset() _keywordChoices.reset() _refChoice.reset() this // Display parse rules as a tree in a BNF-like format. fun display = /// Display a single `Choice`. fun displayChoice[A](choice: Choice[A]) = if choice is Choice.Keyword(keyword, rest) then "\"" + keyword.name + "\"" + tail(rest).1 Choice.Ref(kind, _, _, _, rest) then "<" + kind + ">" + tail(rest).1 Choice.Siding(init, opt, rest, _) then let init' = go(init, false).1 (if opt then "[" + init' + "]" else "(" + init' + ")") + tail(rest).1 Choice.End then "" other then "" // Display the remaining list of choices. // ++ fun tail(rest) = if rest is ParseRule(_, choices) and fun tail(rest) = if rest is ParseRule and // --- let choices = rest.choices // --- choices is Choice.End :: Nil then ["", ""] go(rest, false) is [name, line] and choices is _ :: _ :: _ and choices Iter.fromStack() Iter.some(c => c is Choice.End) then [name, " [" + line + "]"] else [name, " (" + line + ")"] else [name, " " + line] fun go(rule, top) = let lines = rule.choices Iter.fromStack() Iter.filtering of case Choice.End then false else true Iter.mapping of displayChoice Iter.toArray() tuple of rule.name if lines is [] then "ε" lines is [line] then line top then "\n | " + lines.join("\n | ") else lines.join(" | ") if go(this, true) is [name, line] then "<" + name + "> ::= " + line // Shorthands for constructing parse rules. // Automatically adds an end choice to the rule if no choices are provided. fun rule(name, ...choices) = new ParseRule of name if choices.length == 0 then Choice.end(()) :: Nil else choices Iter.toStack() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/ParseRuleVisualizer.mls ================================================ import "../../Predef.mls" import "../../Stack.mls" import "../../Iter.mls" import "../../Option.mls" import "../../TreeTracer.mls" import "../../XML.mls" import "../../MutMap.mls" import "./ParseRule.mls" import "./Rules.mls" import "./Parser.mls" open Predef open Stack open Option open ParseRule { Choice } open Rules { syntaxKinds } open TreeTracer open XML { html, tag, style } module ParseRuleVisualizer with ... val tracer = new TreeTracer.TreeTracer() let defaultKinds = tuple of "type", "term", "typevar", "ident" let renderedKinds = new Set of defaultKinds fun reset() = set renderedKinds = new Set of defaultKinds // * `rr` is the railroad library. fun render[T](rr, title: Str, rule: ParseRule.ParseRule[T]) = let helperRules = mut [] let referencedKinds = new Set() let renderCache = new Map() fun sequence(lhs, rhsOpt) = if rhsOpt is Some(rhs) then rr.Sequence(lhs, rhs) None then lhs fun diagram(choicesOpt) = rr.Diagram of if choicesOpt is Some(choices) then choices else [] fun renderChoice(parentRule, choice) = if choice is Choice.End then tracer.print of "found Choice.End" None Choice.Keyword(keyword, rest) then tracer.print of "found Choice.Keyword" Some of rr.Terminal(keyword.name) sequence(renderRule(rest)) Choice.Siding(rule, optional, rest, _) then tracer.print of "found Choice.Siding" Some of if renderRule(rule) is let latterPart = renderRule(rest) Some(optionalPart) and optional then rr.Optional(optionalPart) sequence(latterPart) else optionalPart sequence(latterPart) None then latterPart Choice.Ref(kind, _, outerPrec, innerPrec, rest) then tracer.print of "found Choice.Ref to " + kind if renderedKinds.has(kind) is false do referencedKinds.add of kind Some of rr.NonTerminal(kind, href: "#" + kind) sequence(renderRule(rest)) fun renderRule(rule: ParseRule.ParseRule[T]) = tracer.trace of "renderRule <<< " + rule.name result => "renderRule >>> " () => ... let rest = rule.choices optional = false nodes = mut [] while rest is head :: tail do if renderChoice(rule, head) is Some(node) do nodes.push of node None do set optional = true set rest = tail tracer.print of "nodes: ", nodes.length.toString() if nodes.length is 0 then None let choice = rr.Choice(0, ...nodes) optional is true then Some(rr.Optional(choice)) else Some(choice) // Iteratively render the rule and its dependencies. let diagrams = mut [[title, diagram of renderRule(rule)]] while referencedKinds.size > 0 do let currentKinds = referencedKinds set renderedKinds = renderedKinds.union of currentKinds referencedKinds = new Set() diagrams.push of ...currentKinds Iter.mapping of kind => let theRule = syntaxKinds |> MutMap.get(kind) |> Option.unsafe.get [kind, diagram of renderRule(theRule)] Iter.toArray() diagrams ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Parser.mls ================================================ import "../../Predef.mls" import "../../Option.mls" import "../../Stack.mls" import "../../TreeTracer.mls" import "../../Iter.mls" import "../../MutMap.mls" import "./Lexer.mls" import "./Extension.mls" import "./Token.mls" import "./TokenHelpers.mls" import "./Keywords.mls" import "./Tree.mls" import "./Rules.mls" import "./ParseRule.mls" open Predef open Option open Stack open ParseRule { Choice } open Choice { Ref } open Keywords { opPrecOpt } open Rules { termRule, typeRule, declRule, syntaxKinds } type Opt[A] = Some[A] | None type TreeT = Tree type StackT[A] = Stack.Cons[A] | Stack.Nil module Parser with ... val tracer = new TreeTracer.TreeTracer let termOptions = kind: "term", rule: termRule allowOperators: true, allowLiterals: true let typeOptions = kind: "type", rule: typeRule allowOperators: false, allowLiterals: true, fun parse(tokens) = let counter = 0 fun consume = if tokens is head :: tail then tracer.print of "consume: `" + Token.summary(head) + "` at #" + counter set tokens = tail set counter = counter + 1 else tracer.print of "consume: the end of input" fun parseKind(kind: Str, prec: Int): TreeT = if kind is "type" then expr(prec, typeOptions) kind is "term" then expr(prec, termOptions) // built-in kinds: plain identifiers kind is "ident" then if tokens is Token.Identifier(name, false) :: _ and Keywords.all |> MutMap.get(name) is None then consume Tree.Ident(name, false) token :: _ then Tree.error("expect an identifier but found " + token) Nil then Tree.error("expect an identifier but found the end of input") // built-in kinds: type variables kind is "typevar" then if tokens is Token.Identifier(name, false) :: _ and name.at(0) is "'" then consume Tree.Ident(name, false) token :: _ then Tree.error("expect a type variable but found " + token) Nil then Tree.error("expect a type variable but found the end of input") // * other rule-based kinds syntaxKinds |> MutMap.get(kind) is Some(rule) then let tree = parseRule(prec, rule) if rule.refChoice is Some(Ref(kind', process, None, None, rest)) and kind == kind' do let shouldParse = true while shouldParse do let tree' = parseRule(prec, rest) if tree' Tree.nonEmpty() then tracer.print of ">>> " + kind + "Cont " + prec + " " + tree Tree.summary() + " <<<", source.line set tree = process(tree, tree') else set shouldParse = false tree else throw Error("Unknown syntax kind: \"" + kind + "\"") fun parseRule(prec: Int, rule) = tracer.trace of "parsing rule \"" + rule.name + "\" with precedence " + prec result => "parsed rule \"" + rule.name + "\": " + result Tree.summary() () => if tokens is... Token.Identifier(name, _) :: _ and do tracer.print of "found an identifier \"" + name + "\"", source.line Keywords.all |> MutMap.get(name) is Some(keyword) and do tracer.print of keyword.toString(), source.line do tracer.print of "keyword choices: ", rule.keywordChoices Iter.mapping of case [k, v] then "`" + k + "`" Iter.joined(", ") rule.keywordChoices |> MutMap.get(name) is Some(rest) then tracer.print of "found a rule starting with `" + name + "`", source.line tracer.print of "the rest of the rule: " + rest.display, source.line consume parseRule(0, rest) do tracer.print of "\"" + name + "\" is not a keyword", source.line other :: _ and do tracer.print of "the current rule is " + rule.display rule.refChoice is Some(Ref(kind, process, outerPrec, innerPrec, rest)) and do tracer.print of "try to parse kind \"" + kind + "\" at " + TokenHelpers.preview(tokens), source.line let outerPrec' = outerPrec Option.getOrElse(Keywords.maxKeywordPrec) let innerPrec' = innerPrec Option.getOrElse(prec) outerPrec' > prec and let acc = parseKind(kind, prec) acc Tree.nonEmptyError() and parseRule(prec, rest) is Tree.Error(_, message) as tree then do tracer.print of "cannot parse due to error: " + message, source.line tree tree then tracer.print of "acc: " + acc Tree.summary(), source.line tracer.print of "parsed from rest rule: " + tree Tree.summary(), source.line process(acc, tree) do tracer.print of "cannot parse more", source.line rule.endChoice is Some(value) then tracer.print of "found end choice", source.line value do tracer.print of "no end choice", source.line else acc do tracer.print of "did not parse kind \"" + kind + "\" because of the precedence", source.line do tracer.print of "no reference choice", source.line rule.endChoice is Some(value) then tracer.print of "found end choice", source.line value do tracer.print of "no end choice", source.line else consume Tree.error("unexpected token " + render(other)) // TODO: Pretty-print the token. Nil and rule.endChoice is Some(value) then value None then tracer.print of "no end choice but found the end of input", source.line Tree.error("unexpected end of input") fun expr(prec: Int, options): TreeT = tracer.trace of options.kind + " <<< " + prec + " " + TokenHelpers.preview(tokens) result => options.kind + " >>> " + result Tree.summary() () => if tokens is ... Token.Identifier(name, symbolic) :: _ and Keywords.all |> MutMap.get(name) is // * the keyword case Some(keyword) and options.rule.keywordChoices |> MutMap.get(name) is Some(rule) and keyword.leftPrecOrMin > prec then consume parseRule(keyword.rightPrecOrMax, rule) exprCont(prec, options) else tracer.print of "the left precedence of \"" + name + "\" is less", source.line Tree.empty None then tracer.print("no rule starting with " + name, source.line) Tree.empty // * the non-keyword case None then if not options.allowOperators and symbolic then Tree.error("symbolic identifiers are disallowed in kind \"" + options.kind + "\"") else consume Tree.Ident(name, symbolic) exprCont(prec, options) // * the literal case Token.Literal(kind, literal) :: _ and options.allowLiterals then consume Tree.Literal(kind, literal) exprCont(prec, options) // * other cases token :: _ then Tree.error("unrecognized token: " + token) Nil then Tree.error("unexpected end of input") fun exprCont(acc: TreeT, prec: Int, options) = if tokens is let infix = options.rule.refChoice Option.flatMap of case Ref(kind, process, None, None, rest) and kind == options.kind then { process: process, rule: rest } else throw Error("Kind " + options.kind + " does not have infix rules") do tracer.print of ">>> " + options.kind + "Cont " + prec + " " + acc Tree.summary() + " <<<", source.line // * the case of infix keyword rules do tracer.print of "check keyword " + TokenHelpers.preview(tokens), source.line Token.Identifier(name, _) :: _ and Keywords.all |> MutMap.get(name) is Some(keyword) and do tracer.print of "found a keyword: " + name, source.line infix.rule.keywordChoices |> MutMap.get(name) is Some(rule) and do tracer.print of "keyword `" + name + "` is found in infix rules", source.line keyword.leftPrecOrMin > prec and rule.refChoice is Some(Ref(kind, process, outerPrec, innerPrec, rest)) and do tracer.print of "try to parse kind \"" + kind + "\" at " + TokenHelpers.preview(tokens), source.line let outerPrec' = outerPrec Option.getOrElse(Keywords.maxOperatorPrec) let innerPrec' = innerPrec Option.getOrElse(outerPrec') outerPrec' > prec then consume let rhs = parseKind(kind, keyword.rightPrecOrMin) let restRes = parseRule(innerPrec', rest) infix.process(acc, process(rhs, restRes)) exprCont(prec, options) None then acc do tracer.print of "keyword `" + name + "` does not have infix rules", source.line // * the case of infix operators (non-keywords) Token.Identifier(name, true) :: _ and Keywords.all |> MutMap.get(name) is None and options.allowOperators and do tracer.print of "found an operator \"" + name + "\"", source.line opPrecOpt(name) is Some([leftPrec, rightPrec]) and do tracer.print of "leftPrec = " + leftPrec + "; rightPrec = " + rightPrec, source.line leftPrec > prec then consume let op = Tree.Ident(name, true) let rhs = expr(rightPrec, termOptions) Tree.App(op, acc :: rhs :: Nil) exprCont(prec, options) else acc // * the case of application do tracer.print of "not a keyword", source.line token :: _ and infix.rule.refChoice is Some(Ref(kind, process, outerPrec, innerPrec, rest)) and do tracer.print of "found reference to " + kind + " with outerPrec = " + outerPrec, source.line let outerPrec' = outerPrec Option.getOrElse(Keywords.maxOperatorPrec) let innerPrec' = innerPrec Option.getOrElse(outerPrec') outerPrec' > prec and parseKind(kind, innerPrec Option.getOrElse(outerPrec')) is Tree.Empty then do tracer.print of "nothing was parsed", source.line acc Tree.Error then do tracer.print of "cannot parse more", source.line acc rhs then do tracer.print of "parsed " + rhs Tree.summary(), source.line let restRes = parseRule(innerPrec', rest) infix.process(acc, process(rhs, restRes)) exprCont(prec, options) do tracer.print of "the outer precedence is less than " + prec, source.line else acc None then tracer.print of "cannot consume " + token, source.line acc Nil then acc fun handleDirective(tree: TreeT, acc: TreeT) = if tree is // Call the corresponding handler for the directive. Tree.Define(Tree.DefineKind.Directive, [name, body] :: Nil) as tree and name is Tree.Ident("newKeyword", _) then Extension.extendKeyword(body) modCont(acc) Tree.Ident("newCategory", _) then Extension.newCategory(body) modCont(acc) Tree.Ident("extendCategory", _) then Extension.extendCategory(body) modCont(acc) else modCont(tree :: acc) tree then modCont(tree :: acc) fun mod(acc: StackT[TreeT]) = if tokens is do tracer.print of ">>>>>> mod <<<<<<", source.line Token.Identifier(";;", _) :: _ then consume mod Token.Identifier(name, _) :: _ and Keywords.all |> MutMap.get(name) is Some(keyword) and // First, try if the keyword is the prefix of the term. termRule.keywordChoices |> MutMap.get(name) is Some(rule) then let tree = expr(0, termOptions) if tree is Tree.LetIn(bindings, Tree.Empty) then // If the body is empty, then this is a let-definition. modCont of Tree.Define(Tree.DefineKind.Let(false), bindings) :: acc else modCont of tree :: acc // Then try to parse according to the rules of the definition. declRule.keywordChoices |> MutMap.get(name) is Some(rule) then consume parseRule(0, rule) handleDirective(acc) _ :: _ then modCont of expr(0, termOptions) :: acc Nil then acc reverse() fun modCont(acc: StackT[TreeT]) = if tokens is do tracer.print of ">>>>>> modCont <<<<<< " + TokenHelpers.preview(tokens), source.line Token.Identifier(";;", _) :: _ then consume; acc mod() _ :: _ then parseRule(0, declRule) handleDirective(acc) Nil then acc reverse() let tree = tracer.trace of "module <<< " result => "module >>> " + result Tree.summary() () => mod(Nil) if tokens is token :: _ then let message = "expect EOF instead of " + token tracer.print of message, source.line tree Tree.Error(message) Nil then tree ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/PrattParsing.mls ================================================ import "../../Predef.mls" import "../../Stack.mls" import "../../Option.mls" import "../../Iter.mls" import "../../StrOps.mls" import "./Lexer.mls" import "./Token.mls" import "./Expr.mls" open Stack open Option { Some, None } open Token { Round, LiteralKind } type Stack[A] = Cons[A] | Nil module PrattParsing with ... fun parse(tokens) = fun advance = if tokens is Token.Space :: tail then set tokens = tail advance head :: tail then set tokens = tail Some(head) Nil then None let peek = advance fun consume = set peek = advance fun require(result: Expr.Expr, expected) = if peek is Some(actual) and expected Token.same(actual) then consume result else result Expr.withErr of StrOps.concat of "Expected token " expected Token.summary() ", but found " actual Token.summary() None then result Expr.withErr of StrOps.concat of "Expected token " expected Token.summary() ", but found end of input" fun expr(prec: Int): Expr.Expr = if peek is Some of Token.Literal(LiteralKind.Integer, literal) then consume Expr.Lit(parseInt(literal, 10)) exprCont(prec) Token.Identifier(name, false) then consume Expr.Var(name) exprCont(prec) Token.Identifier("(", true) then consume expr(0) require(Token.Identifier(")", true)) exprCont(prec) token then Expr.justErr of "Unexpected token " + token Token.summary() None then Expr.justErr of "Unexpected end of input" fun exprCont(acc: Expr.Expr, prec: Int): Expr.Expr = if peek is Some(Token.Identifier(op, true)) and op !== ")" // * this is important and Expr.opPrec(op) is [leftPrec, rightPrec] and leftPrec > prec then consume let right = expr of rightPrec Expr.Inf(op, acc, right) exprCont(prec) else acc let result = expr(0) if peek is Some(token) then result Expr.withErr of "Expect end of input, but found " + token Token.summary() None then result ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/RecursiveDescent.mls ================================================ import "../../Predef.mls" import "../../Stack.mls" import "../../Option.mls" import "../../Iter.mls" import "./Token.mls" import "./BasicExpr.mls" open Predef { mkStr } open Stack open Option { Some, None } open Token { Round, LiteralKind } open BasicExpr { Expr } type StackT[A] = Cons[A] | Nil module RecursiveDescent with ... fun parse(tokens) = fun advance = if tokens is Token.Space :: tail then set tokens = tail advance head :: tail then set tokens = tail Some(head) Nil then None let peek = advance fun consume = set peek = advance fun require(result: Expr, expected) = if peek is Some(actual) and expected Token.same(actual) then consume result else result BasicExpr.withErr of mkStr of "Expected token " expected Token.summary() ", but found " actual Token.summary() None then result BasicExpr.withErr of mkStr of "Expected token " expected Token.summary() ", but found end of input" fun atom: Expr = if peek is Some of Token.Literal(LiteralKind.Integer, literal) then consume BasicExpr.Lit(parseInt(literal, 10)) Token.Identifier("(", true) then consume expr require of Token.Identifier(")", true) Token.Identifier(name, false) then consume BasicExpr.Var(name) token then BasicExpr.justErr of "Unexpected token " + token Token.summary() None then BasicExpr.justErr of "Unexpected end of input" fun expr: Expr = let leftmost = product addSeq Iter.fromStack() Iter.folded of leftmost, BasicExpr.Add fun addSeq: StackT[Expr] = if peek is Some(Token.Identifier("+", _)) then consume product :: addSeq else Nil fun product: Expr = let leftmost = atom mulSeq Iter.fromStack() Iter.folded of leftmost, BasicExpr.Mul fun mulSeq: StackT[Expr] = if peek is Some(Token.Identifier("*", _)) then consume atom :: mulSeq else Nil let result = expr if peek is Some(token) then result BasicExpr.withErr of "Expect end of input, but found " + token Token.summary() None then result ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Rules.mls ================================================ import "../../Predef.mls" import "../../Option.mls" import "../../Stack.mls" import "../../MutMap.mls" import "../../Iter.mls" import "./Token.mls" import "./Keywords.mls" import "./Tree.mls" import "./ParseRule.mls" open Predef open Option open Stack open ParseRule { Choice, rule } open Token { Curly, Square, Round } open Choice { keyword, reference, term, typeExpr, ident, typeVar, end } type Opt[A] = Some[A] | None module Rules with ... val syntaxKinds = MutMap.empty val extendedKinds = new Set() fun getRuleByKind(kind: Str) = syntaxKinds |> MutMap.get(kind) |> Option.unsafe.get fun define(name: Str)(...choices) = syntaxKinds |> MutMap.updateWith(name) of case None then Some(rule(name, ...choices)) Some(rule) then Some(rule.extendChoices(choices Iter.toStack())) fun idFirst(value, _) = value fun idSecond(_, value) = value fun someFirst(value, _) = Some(value) fun listFirst(value, _) = value :: Nil fun listLike(fields) = let mkTail = if fields.("sep") is undefined then id else keyword(fields.("sep")) reference(fields.head) of process: Cons, name: "the first " + fields.name choices: tuple of end(Nil), mkTail of reference(fields.tail) of process: idFirst, name: "more " + fields.name + "s" define("let-bindings") of term of process: (lhs, rhsBindings) => if rhsBindings is [rhs, bindings] then Tree.Infix(Keywords._equal, lhs, rhs) :: bindings name: "left-hand side" choices: tuple of keyword(Keywords._equal) of term of name: "right-hand side" choices: tuple of end of Nil keyword(Keywords._and) of reference("let-bindings") of process: idFirst, name: "let-bindings tail" fun makeLetBindings(hasInClause: Bool) = let intro = "let binding: " keyword(Keywords._let) of Choice.siding of optional: true init: keyword(Keywords._rec)() rest: reference("let-bindings") of process: Tree.LetIn, name: "let-bindings" choices: if hasInClause then tuple of keyword(Keywords._in) of term of process: someFirst, name: intro + "body" end of None else tuple of end of None process: idSecond let letExpression = makeLetBindings(true) define("simple-matching") of term of process: (lhs, rhsTail) => if rhsTail is [rhs, tail] then Tree.Infix(Keywords._thinArrow, lhs, rhs) :: tail // TODO: Better error handling? name: "case body" choices: tuple of keyword(Keywords._thinArrow) of term of name: "rhs" choices: tuple of end of Nil keyword(Keywords._bar) of reference("simple-matching") of process: idFirst, name: "simple-matching tail" define("pattern-list") of term of process: (head, tail) => head :: tail name: "pattern" choices: tuple of reference("pattern-list") of process: idFirst, name: "pattern list tail" define("multiple-matching") of reference("pattern-list") of process: Tree.infix of Keywords._thinArrow name: "list of patterns" choices: tuple of keyword(Keywords._thinArrow) of term of process: idFirst, name: "the right-hand side of the arrow" choices: tuple of end of Nil keyword(Keywords._bar) of reference("multiple-matching") of process: idFirst, name: "multiple-matching tail" fun makeInfixChoice(kw: Keywords.Keyword, rhsKind: Str, compose: (Tree, Tree) -> Tree) = keyword(kw) of reference(rhsKind) of process: (rhs, _) => lhs => compose(lhs, rhs) name: "operator `" + kw.name + "` right-hand side" fun makeBracketRule(fields) = // opening, closing, kind, wrapContent // Pass the error message of closing bracket to the content. keyword(fields.opening) of reference(fields.kind) of process: (tree: Tree, next: Tree) => if next is Tree.Error(Tree.Empty, msg) then fields.wrapContent(tree) Tree.Error(msg) Tree.Empty then fields.wrapContent(tree) name: fields.kind + " in bracket" choices: tuple of keyword(fields.closing) of end of Tree.empty // Prefix rules and infix rules for expressions. val termRule = rule of "prefix rules for expressions" letExpression // `fun` term keyword(Keywords._fun) of term of process: (params, body) => Tree.Lambda(params :: Nil, body) name: "function parameters" choices: tuple of keyword(Keywords._thinArrow) of term of process: idFirst, name: "function body" // `match`-`with` term keyword(Keywords._match) of term of process: Tree.Match name: "pattern matching scrutinee" choices: tuple of keyword(Keywords._with) of Choice.siding of optional: true init: keyword(Keywords._bar)() rest: getRuleByKind("simple-matching") process: idSecond // `function` term keyword(Keywords._function) of Choice.siding of optional: true init: keyword(Keywords._bar)() rest: getRuleByKind("simple-matching") process: (_, branches) => Tree.Match(Tree.empty, branches) // `if`-`then`-`else` term keyword(Keywords._if) of term of process: (tst, conAlt) => if conAlt is [con, alt] then Tree.Ternary(Keywords._if, tst, con, alt) name: "if-then-else condition" choices: tuple of keyword(Keywords._then) of term of name: "if-then-else consequent" choices: tuple of end of None keyword(Keywords._else) of term of process: someFirst, name: "if-then-else alternative" // `while` term keyword(Keywords._while) of term of process: Tree.While name: "while body" choices: tuple of keyword(Keywords._do) of term of name: "while end", process: idFirst choices: tuple of keyword(Keywords._done)() // `for` term keyword(Keywords._for) of term of name: "`for` head" process: (head, startEndBody) => Tree.For(head, ...startEndBody) choices: tuple of keyword(Keywords._equal) of term of process: (start, endBody) => [start, ...endBody] name: "`for` `to` or `downto` keyword" choices: tuple of Choice.siding of init: tuple of keyword(Keywords._to)(), keyword(Keywords._downto)() rest: term of name: "`for` `do` keyword" choices: tuple of keyword(Keywords._do) of term of name: "`for` `done` keyword", process: idFirst choices: tuple of keyword(Keywords._done)() process: idSecond // Choices for brackets makeBracketRule of opening: Keywords._leftRound, closing: Keywords._rightRound, kind: "term" wrapContent: (tree) => if tree is Tree.Empty then Tree.Tuple(Nil) else tree makeBracketRule of opening: Keywords._leftSquare, closing: Keywords._rightSquare, kind: "term" wrapContent: (tree) => Tree.Bracketed of Square, if tree is Tree.Empty then Tree.Sequence(Nil) else tree makeBracketRule of opening: Keywords._leftCurly, closing: Keywords._rightCurly kind: "term", wrapContent: id makeBracketRule of opening: Keywords._begin, closing: Keywords._end, kind: "term" wrapContent: (tree) => if tree is Tree.Empty then Tree.Sequence(Nil) else tree // "infix" rule starting with term of process: pipeInto choices: tuple of // Tuple (separated by commas) makeInfixChoice of Keywords._comma, "term", (lhs, rhs) => if rhs is Tree.Tuple(tail) then Tree.Tuple(lhs :: tail) else Tree.Tuple(lhs :: rhs :: Nil) // Sequence (separated by semicolons) makeInfixChoice of Keywords._semicolon, "term", (lhs, rhs) => if rhs is Tree.Sequence(tail) then Tree.Sequence(lhs :: tail) else Tree.Sequence(lhs :: rhs :: Nil) // Assignment: <- makeInfixChoice of Keywords._leftArrow, "term", (lhs, rhs) => Tree.Infix(Keywords._leftArrow, lhs, rhs) // Comparison: == makeInfixChoice of Keywords._equalequal, "term", (lhs, rhs) => Tree.Infix(Keywords._equalequal, lhs, rhs) // Comparison: * makeInfixChoice of Keywords._asterisk, "term", (lhs, rhs) => Tree.App(Tree.Ident("*", true), lhs :: rhs :: Nil) // Selection: "." // Access: "." "(" ")" keyword(Keywords._period) of keyword(Keywords._leftRound) of term of process: (argument, _) => lhs => Tree.Infix(Keywords._period, lhs, Tree.Bracketed(Round, argument)) name: "application argument" choices: tuple of keyword(Keywords._rightRound)() term of process: (rhs, _) => lhs => Tree.Infix(Keywords._period, lhs, rhs) name: "operator `.` right-hand side" // Type ascription: : keyword(Keywords._colon) of typeExpr of process: (rhs, _) => lhs => Tree.Infix(Keywords._colon, lhs, rhs) name: "right-hand side type" // Application: term of process: (argument, _) => callee => Tree.App(callee, argument) name: "application argument" outerPrec: Keywords.appPrec // Prefix rules and infix rules for types. val typeRule = rule of "rules for types" // "(" { "," } ")" [ ] keyword(Keywords._leftRound) of typeExpr of process: (headArg, tailArgsCtor) => if tailArgsCtor is [tailArgs, ctor] then Tree.App(ctor, Tree.Tuple(headArg :: tailArgs)) Some(ctor) then Tree.App(ctor, headArg) None then headArg name: "the first type in the parentheses" choices: tuple of reference("type-arguments-tail") of name: "the remaining type arguments" choices: tuple of keyword(Keywords._rightRound) of ident of process: someFirst, name: "the type constructor's name" keyword(Keywords._rightRound) of // either an identifier or nothing end of None ident of process: someFirst, name: "the type constructor's name" // "infix" rule starting with typeExpr of process: pipeInto choices: tuple of // "->" makeInfixChoice of Keywords._thinArrow, "type", (lhs, rhs) => Tree.Infix(Keywords._thinArrow, lhs, rhs) // "*" makeInfixChoice of Keywords._asterisk, "type", (lhs, rhs) => Tree.Infix(Keywords._asterisk, lhs, rhs) // Application: typeExpr of process: (callee, _) => argument => Tree.App(callee, argument) outerPrec: Keywords.appPrec define("type-arguments-tail") of keyword(Keywords._comma) of listLike of head: "type", tail: "type-arguments-tail", name: "type argument" define("constr-decl") of ident of process: (ctor, argOpt) => if argOpt is Some(arg) then Tree.Infix(Keywords._of, ctor, arg) None then ctor name: "the variant constructor's name" choices: tuple of end of None keyword(Keywords._of) of typeExpr of process: someFirst, name: "the variant constructor's argument" define("variants") of reference("constr-decl") of process: (lhs, rhsOpt) => if rhsOpt is Some(rhs) then Tree.Infix(Keywords._bar, lhs, rhs) else lhs name: "variants item" choices: tuple of end of None keyword(Keywords._bar) of reference("variants") of process: someFirst, name: "variants end" define("typedefs") of reference("typedef-lhs") of process: (lhs, rhsMore) => if rhsMore is [rhs, more] then rhs(lhs) :: more name: "typedef name" choices: tuple of reference("typedef-rhs") of name: "typedef body" choices: tuple of end of Nil keyword(Keywords._and) of reference("typedefs") of process: idFirst, name: "typedef end" define("typedef-rhs") of keyword(Keywords._equal) of reference("variants") of process: (rhs, _) => lhs => Tree.Infix(Keywords._equal, lhs, rhs) name: "typedef-rhs: variants" Choice.map of keyword(Keywords._leftCurly) of reference("label-decls") of process: (content, _) => if content is Nil then Tree.Bracketed(Curly, Tree.Sequence(Nil)) else Tree.Bracketed(Curly, Tree.Sequence(content)) name: "label-decl" choices: keyword(Keywords._rightCurly) of end of Tree.empty rhs => lhs => Tree.Infix(Keywords._equal, lhs, rhs) define("typedef-rhs") of keyword(Keywords._equalequal) of typeExpr of process: (rhs, _) => lhs => Tree.Infix(Keywords._equalequal, lhs, rhs) name: "type alias body" define("label-decl") of typeExpr of process: Tree.infix of Keywords._colon name: "label-decl name" choices: tuple of keyword(Keywords._colon) of typeExpr of process: idFirst, name: "label-decl body" define("label-decls") of listLike of head: "label-decl", tail: "label-decls", name: "label and declaration pair", sep: Keywords._semicolon define("constr-decls") of listLike of head: "constr-decl", tail: "constr-decls", name: "constructor declaration", sep: Keywords._bar define("typedef-lhs") of reference("type-params") of process: (params, ident) => if params is Nil then ident else Tree.App(ident, Tree.Tuple(params)) name: "the type parameters" choices: tuple of ident of process: idFirst, name: "the type identifier" define("type-params") of end(Nil) define("type-params") of typeVar of process: (h, _) => h :: Nil, name: "the only type parameter" define("type-params") of keyword(Keywords._leftRound) of typeVar of process: Cons name: "the first type parameter" choices: tuple of reference("type-params-tail") of process: idFirst, name: "more type parameters" choices: tuple of keyword(Keywords._rightRound)() define("type-params-tail") of end(Nil) define("type-params-tail") of keyword(Keywords._comma) of typeVar of process: Cons, name: "the first type parameter" choices: tuple of reference("type-params-tail") of process: idFirst, name: "more type parameters" val declRule = rule of "prefix rules for module items" makeLetBindings(false) // let definition keyword(Keywords._type) of reference("typedefs") of process: (typedefs, _) => Tree.Define(Tree.DefineKind.Type, typedefs) name: "more typedefs" keyword(Keywords._exception) of reference("constr-decls") of process: (decls, _) => Tree.Define(Tree.DefineKind.Exception, decls) name: "constructor declarations" keyword(Keywords._hash) of ident of process: (ident, body) => Tree.Define(Tree.DefineKind.Directive, [ident, body] :: Nil) name: "directive name" choices: tuple of term of process: idFirst, name: "directive body" syntaxKinds |> MutMap.insert of "term", termRule syntaxKinds |> MutMap.insert of "type", typeRule syntaxKinds |> MutMap.insert of "decl", declRule ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Test.mls ================================================ import "./Lexer.mls" import "./Keywords.mls" import "./Token.mls" import "./Tree.mls" import "./TokenHelpers.mls" import "./TreeHelpers.mls" import "./Parser.mls" import "../../Iter.mls" import "../../Option.mls" import "../../Predef.mls" import "../../Stack.mls" open Token { LiteralKind } open Parser { parse, tracer } open Option { Some, None } open Predef { print } open Stack open TreeHelpers { showAsTree } module Test with ... pattern Flag = "tree" | "trace" | "tokens" fun flags(tokens) = let result = new Set while tokens is Token.Space :: Token.Identifier(":", _) :: Token.Identifier(Flag as flag, _) :: tail then result.add(flag) set tokens = tail Token.Identifier(":", _) :: Token.Identifier(Flag as flag, _) :: tail then result.add(flag) set tokens = tail [result, tokens] fun example(...lines) = let source = lines.join("\n") tokens = Lexer.lex(source, noWhitespace: true) if flags(tokens) is [flags, tokens] then ... if flags.has("tokens") do print of TokenHelpers.panorama(tokens) tracer.reset() set tracer.enabled = flags.has("trace") let trees = parse(tokens) set tracer.enabled = false if flags.has("tree") do print of trees Iter.fromStack() Iter.mapping of showAsTree Iter.joined of "\n" print of trees Iter.fromStack() Iter.mapping of Tree.summary Iter.joined of "\n" ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Token.mls ================================================ import "../../Option.mls" import "../../Predef.mls" open Option { Some, None } open Predef { mkStr } module Token with ... object Angle Round Square Curly BeginEnd type BracketKind = Angle | Round | Square | Curly | BeginEnd module LiteralKind with object Integer Decimal String Boolean abstract class Token with let _location = None fun withLocation(start, end, lookupTable) = set _location = Some of start: start, end: end, lookupTable: lookupTable this fun location = _location fun displayLocation = if _location is Some(location) then let start = location.lookupTable.lookup(location.start) let end = location.lookupTable.lookup(location.end) mkStr of start.0.toString(), ":", start.1.toString() "-" end.0.toString(), ":", end.1.toString() else "" class LineLookupTable(lines: Array[Int]) with fun lookup(index: Int): [Int, Int] = if index < 0 do set index = 0 let begin = 0 end = lines.length mid = Math.floor of (begin + end) / 2 while begin < end do if index <= lines.at(mid) then set end = mid else set begin = mid + 1 set mid = Math.floor of (begin + end) / 2 let line = mid + 1 let column = index - if mid == 0 then -1 else lines.at(mid - 1) [line, column] data class Space() extends Token Error() extends Token Comment(content: Str) extends Token Identifier(name: Str, symbolic: Bool) extends Token Literal(kind, literal: Str) extends Token fun same(a, b) = if a is Space and b is Space then true a is Comment(c) and b is Comment(c') then c == c' a is Identifier(n, s) and b is Identifier(n', s') then n == n' and s == s' a is Literal(k, l) and b is Literal(k', l') then k == k' and l == l' else false fun integer(literal, endIndex)(using llt: LineLookupTable) = Literal(LiteralKind.Integer, literal).withLocation of endIndex - literal.length, endIndex, llt fun decimal(literal, endIndex)(using llt: LineLookupTable) = Literal(LiteralKind.Decimal, literal).withLocation of endIndex - literal.length, endIndex, llt fun string(literal, startIndex, endIndex)(using llt: LineLookupTable) = Literal(LiteralKind.String, literal).withLocation(startIndex, endIndex, llt) fun boolean(literal, endIndex)(using llt: LineLookupTable) = Literal(LiteralKind.Boolean, literal).withLocation of endIndex - literal.length, endIndex, llt fun identifier(name, endIndex)(using llt: LineLookupTable) = Identifier(name, false).withLocation of endIndex - name.length, endIndex, llt fun symbol(name, endIndex)(using llt: LineLookupTable) = Identifier(name, true).withLocation of endIndex - name.length, endIndex, llt fun comment(content, startIndex, endIndex)(using llt: LineLookupTable) = Comment(content).withLocation(startIndex, endIndex, llt) fun error(startIndex, endIndex)(using llt: LineLookupTable) = Error().withLocation(startIndex, endIndex, llt) fun space(startIndex, endIndex)(using llt: LineLookupTable) = Space().withLocation(startIndex, endIndex, llt) fun summary(token) = if token is Space then "␠" Error then "⚠" Comment(_) then "\uD83D\uDCAC" // The text ballon emoji. Identifier(name, _) then name Literal(_, literal) then literal fun display(token) = (if token is Space then "space" Error then "error" Comment then "comment" Identifier(name, _) then "identifier `" + name + "`" Literal(kind, value) then mkStr of kind.toString().toLowerCase() " " JSON.stringify(value) ) + " at " + token.displayLocation ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/TokenHelpers.mls ================================================ import "./Token.mls" import "../../Stack.mls" import "../../Predef.mls" open Stack open Predef { mkStr } type StackT[A] = Stack.Cons[A] | Stack.Nil module TokenHelpers with ... fun display(tokens: StackT[Token.Token], limit: Int): Str = let i = 0 values = mut [] while i < limit and tokens is head :: tail do values.push of head Token.summary() set tokens = tail i += 1 mkStr of "┃" values.join("│"), if tokens is _ :: _ then "│⋯" else "┃" fun panorama(tokens) = display(tokens, Number.MAX_SAFE_INTEGER) fun preview(tokens) = display(tokens, 5) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/Tree.mls ================================================ import "../../Iter.mls" import "../../Predef.mls" import "../../Stack.mls" import "../../Option.mls" import "../../StrOps.mls" import "./Keywords.mls" import "./Token.mls" open Predef { fold, mkStr } open Stack open Option { Some, None } open Token { LiteralKind, BracketKind } open Keywords { opPrec, INT_MAX } // _____ // |_ _| __ ___ ___ // | || '__/ _ \/ _ \ // | || | | __/ __/ // |_||_| \___|\___| // // ==================== abstract class Tree module Tree with module DefineKind with data class Let(recursive: Bool) object Type object Exception object Directive type DefineKind = Let | Type | Exception | Directive open DefineKind data class Empty() extends Tree Error(tree: Tree, message: Str) extends Tree Bracketed(kind: BracketKind, tree: Tree) extends Tree Ident(name: Str, symbolic: Bool) extends Tree Underscore() extends Tree Modified(modifier, subject) extends Tree Tuple(trees: Stack[Tree]) extends Tree Sequence(trees: Stack[Tree]) extends Tree Literal(kind, value) extends Tree Match(scrutinee: Tree, branches: Stack[Tree]) extends Tree Lambda(params: Stack[Tree], body: Tree) extends Tree App(callee: Tree, argument: Tree) extends Tree Infix(op: Keywords.Keyword, lhs: Tree, rhs: Tree) extends Tree // `items` is separated by `and` Define(kind: DefineKind.DefineKind, items: Stack[[Tree, Tree]]) extends Tree LetIn(bindings: Stack[Tree], body: Tree) extends Tree While(cond: Tree, body: Tree) extends Tree For(head: Tree, start: Tree, end: Tree, body: Tree) extends Tree // For `if`-`then`-`else`. The last part is optional. Ternary(keyword: Keywords.Keyword, lhs: Tree, rhs: Tree, body) extends Tree fun empty = Empty() fun error(message: Str) = empty Error(message) fun summary(tree) = fun par(text: Str, cond: Bool) = if cond then "(" + text + ")" else text fun prec(tree: Tree, side: Bool) = if tree is Empty then INT_MAX Error(tree, _) then prec(tree, side) Bracketed(_, _) then INT_MAX Ident then INT_MAX Underscore then INT_MAX Modified then 1 Tuple then INT_MAX Sequence then 1 Literal then INT_MAX Match then 2 App(callee, _) and callee is Ident(op, true) and opPrec(op) is [leftPrec, rightPrec] and side then rightPrec else leftPrec else Keywords.appPrec Infix(op, _, _) and side then op.rightPrecOrMax else op.leftPrecOrMax Ternary then 3 Lambda then Keywords._fun.leftPrecOrMax // Wrap trees in guillemet so they are easier to spot. fun wrap(something) = if something is Tree then "«" + go(something) + "»" else go(something) fun go(tree) = if tree is Empty then "{}" Error(Empty, _) then "⚠" Error(tree, _) then "<⚠:" + go(tree) + ">" Bracketed(kind, tree) and kind is Token.Round then "(" + go(tree) + ")" Token.Square then "[" + go(tree) + "]" Token.Curly then "{" + go(tree) + "}" Token.Angle then "<" + go(tree) + ">" Ident(name, _) then name Underscore() then "_" Modified(modifier, subject) then go(modifier) + " " + go(subject) Tuple(trees) then "(" + trees Iter.fromStack() Iter.mapping(go) Iter.joined(", ") + ")" Sequence(trees) then trees Iter.fromStack() Iter.mapping(go) Iter.joined("; ") Literal(LiteralKind.String, value) and value.length > 5 then JSON.stringify(value.slice(0, 5)).slice(0, -1) + "…\"" else JSON.stringify(value) Literal(_, value) then value Match(scrutinee, branches) then mkStr of if scrutinee is Empty then "function " else "match " + go(scrutinee) + " with " branches Iter.fromStack() Iter.mapping(go) Iter.joined(" | ") // Function application for binary operators. App(Ident(op, true), lhs :: rhs :: Nil) then if opPrec(op) is [leftPrec, rightPrec] then fold(+) of par of go(lhs), prec(lhs, false) < leftPrec " ", op, " " par of go(rhs), prec(rhs, true) < rightPrec // Unary expressions App(Ident(op, true), arg) then mkStr of op par of go(arg), prec(arg, false) <= Keywords.prefixPrec // Function application App(callee, argument) then mkStr of go(callee) " " par of go(argument), prec(argument, false) <= Keywords.appPrec Infix(Keywords.Keyword(".", _, _), target, Ident(field, _)) then if opPrec(".") is [leftPrec, _] then mkStr of par of go(target), prec(target, false) < leftPrec "." field Infix(op, lhs, rhs) then fold(+) of go(lhs), " ", go(op), " ", go(rhs) Define(Directive, [name, value] :: Nil) then StrOps.concat of "#", go(name), " ", go(value) Define(kind, items) then mkStr of if kind is Let(true) then "let rec " Let(false) then "let " Type then "type " Exception then "exception " items Iter.fromStack() Iter.mapping of case Tree as tree then go(tree) [lhs, rhs] then go(lhs) + " = " + go(rhs) Iter.joined(" and ") LetIn(bindings, body) then mkStr of "let " bindings Iter.fromStack() Iter.mapping of go Iter.joined(" and ") ...if body is Some(body) then [" in ", go(body)] None then [] While(cond, body) then mkStr of "while ", go(cond), " do ", go(body), " done" For(head, start, end, body) then mkStr of "for ", go(head), " = ", go(start), " to ", go(end), " do ", go(body), " done" Ternary(keyword, lhs, rhs, body) then fold(+) of keyword.name, " ", go(lhs), if keyword.name is "if" then " then " "type" then " = " "let" then " = " if rhs is Some(rhs') then go(rhs') else go(rhs) if keyword.name is "if" then " then " "type" then "" "let" then " in " if body is Some(body) then go(body) else go(body) Lambda(params, body) then fold(+) of "fun ", params Iter.fromStack() Iter.mapping(go) Iter.joined(" "), " -> ", go(body) Keywords.Keyword(name, _, _) then name Some(tree) then "Some(" + wrap(tree) + ")" None then "None" _ :: _ then tree Iter.fromStack() Iter.mapping(wrap) Iter.joined(" :: ") + " :: Nil" Nil then "Nil" [..trees] then "[" + trees Iter.mapping((tree, _, _) => wrap(tree)) Iter.joined(", ") + "]" else "" wrap(tree) fun infix(op)(lhs, rhs) = Infix(op, lhs, rhs) fun bracketed(tree, kind) = Bracketed(kind, tree) fun asSequence(tree) = if tree is Empty then Sequence of Nil Sequence then tree else Sequence of tree :: Nil fun tupleWithHead(tree, head) = if tree is Tuple(tail) then Tuple(head :: tail) else Tuple(head :: tree :: Nil) fun sequenceWithHead(tree, head) = if tree is Sequence(tail) then Sequence(head :: tail) else Sequence(head :: tree :: Nil) fun nonEmpty(tree) = if tree is Empty then false Error(Empty, _) then false else true fun nonEmptyError(tree) = if tree is Error(Empty, _) then false else true ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/TreeHelpers.mls ================================================ import "../../Option.mls" import "../../Predef.mls" import "../../Stack.mls" import "./Tree.mls" import "./Keywords.mls" import "./Token.mls" open Predef open Stack open Token { LiteralKind } open Option { Some, None } module TreeHelpers with ... fun first(array) = if array is [first, ...] then first fun second(array) = if array is [_, second, ...] then second fun indented(text) = text.split("\n").join("\n ") fun showAsTree(thing) = fun itemize(something) = if something is Some(content) then tuple of ["Some of " + go(content)], [] None then tuple of "None", [] head :: tail then let items = mut [go(head)] remaining = tail while remaining is head' :: tail' do items.push(go of head') set remaining = tail' tuple of ("Stack of \n" + " " + indented of items.join("\n")), [] Nil then ["Nil", []] Str then [JSON.stringify(something), []] // TODO: This doesn't work. Int then [something.toString(), []] Tree.Empty then ["Empty", []] Tree.Error(Tree.Empty, m) then tuple of "Error", [["message", go(m)]] Tree.Error(t, m) then tuple of "Error", [["tree", go(t)], ["message", go(m)]] Tree.Ident(n, _) then tuple of "Ident", [["name", go(n)]] Tree.Bracketed(k, items) then tuple of "Bracketed#" + k.toString(), [["items", go(items)]] Tree.Underscore() then tuple of "Underscore", [] Tree.Modified(m, s) then tuple of "Modified", [["modifier", go(m)], ["subject", go(s)]] Tree.Tuple(t) then tuple of "Tuple", [["items", go(t)]] Tree.Sequence(t) then tuple of "Sequence", [["items", go(t)]] Tree.Literal(k, v) then tuple of ("Literal#" + go(k) + " of " + go(v)), [] Tree.Match(scrutinee, branches) then tuple of "Match", [["scrutinee", scrutinee], ["branches", go(branches)]] Tree.App(c, a) then tuple of "App", [["callee", go(c)], ["argument", go(a)]] Tree.Infix(op, lhs, rhs) then tuple of "Infix", [["op", go(op)], ["lhs", go(lhs)], ["rhs", go(rhs)]] Tree.Define(k, i) then tuple of "Define", [["kind", k.toString()], ["items", go(i)]] Tree.LetIn(bds, b) then tuple of "LetIn", [["bindings", go(bds)], ["body", go(b)]] Tree.While(c, b) then tuple of "While", [["condition", go(c)], ["body", go(b)]] Tree.For(h, s, e, b) then tuple of "For", [["head", go(h)], ["start", go(s)], ["end", go(e)], ["body", go(b)]] Tree.Ternary(n, l, r, b) then tuple of "Ternary", [["name", go(n)], ["lhs", go(l)], ["rhs", go(r)], ["body", go(b)]] Tree.Lambda(p, b) then tuple of "Lambda", [["params", go(p)], ["body", go(b)]] Keywords.Keyword as keyword then [keyword.toString(), []] LiteralKind.Integer then tuple of "Integer", [] LiteralKind.Decimal then tuple of "Decimal", [] LiteralKind.String then tuple of "String", [] LiteralKind.Boolean then tuple of "Boolean", [] [x, y] then tuple of "Pair", [["first", go(x)], ["second", go(y)]] else tuple of "Unknown", [["JSON.stringify(_)", JSON.stringify(something)]] fun go(something) = if itemize(something) is [intro, []] then intro [intro, [field]] and intro != "Unknown" then intro + " of " + second of field [intro, fields] then let dialogue = fields.map of (field, _, _) => field first() + " = " + field second() intro + ":\n " + indented of dialogue.join("\n") go(thing) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing/vendors/railroad/railroad.css ================================================ svg.railroad-diagram { /* background-color: hsl(30,20%,95%); */ background: transparent; } svg.railroad-diagram path { stroke-width: 2; stroke: rgba(1, 1, 1, 0.80); fill: transparent; } svg.railroad-diagram text { text-anchor: middle; white-space: pre; } svg.railroad-diagram text.diagram-text { font-size: 12px; } svg.railroad-diagram text.diagram-arrow { font-size: 16px; } svg.railroad-diagram text.label { text-anchor: start; } svg.railroad-diagram text.comment { font: italic 12px monospace; } svg.railroad-diagram g.non-terminal text { font-style: -apple-system, system-ui, sans-serif; font-weight: 600; font-style: italic; } svg.railroad-diagram g.terminal text { font-family: Inconsolata, monospace; } svg.railroad-diagram rect { stroke-width: 1.5; stroke: black; fill: #ffffff; } svg.railroad-diagram rect.group-box { stroke: gray; stroke-dasharray: 10 5; fill: none; } svg.railroad-diagram path.diagram-text { stroke-width: 1.5; stroke: black; fill: white; cursor: help; } svg.railroad-diagram g.diagram-text:hover path.diagram-text { fill: #eee; } ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/Examples.mls ================================================ import "../../MutMap.mls" import "../../Predef.mls" open Predef module Examples with... val examples = MutMap.empty examples |> MutMap.insert of "hanoi" name: "Hanoi from Caml Light" source: """let spaces n = make_string n " ";; let disk size = let right_half = make_string size ">" and left_half = make_string size "<" in left_half ^ "|" ^ right_half;; let disk_number n largest_disk_size = let white_part = spaces (largest_disk_size + 1 - n) in white_part ^ (disk n) ^ white_part;; let peg_base largest_disk_size = let half = make_string largest_disk_size "_" in " " ^ half ^ "|" ^ half ^ " ";; let rec peg largest_disk_size = function | (0, []) -> [] | (0, head::rest) -> disk_number head largest_disk_size :: peg largest_disk_size (0, rest) | (offset, lst) -> disk_number 0 largest_disk_size :: peg largest_disk_size (offset-1, lst);; let rec join_lines l1 l2 l3 = match (l1, l2, l3) with | ([], [], []) -> [] | (t1::r1, t2::r2, t3::r3) -> (t1 ^ t2 ^ t3) :: join_lines r1 r2 r3 | _ -> failwith "join_lines";; """ examples |> MutMap.insert of "extensible" name: "Extensible Syntax" source: """#newKeyword ("hello", Some 3, Some 3) #newKeyword ("goodbye", None, None) #newCategory("greeting") #extendCategory("greeting", [ keyword("hello"), "term", "greeting" ], foo) #extendCategory("greeting", [ keyword("goodbye") ], bar) #extendCategory("decl", [ "greeting" ], baz) hello "Rob" hello "Bob" goodbye #diagram "" """ ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/index.html ================================================ Document

Syntax Diagrams

================================================ FILE: hkmc2/shared/src/test/mlscript-compile/apps/parsing-web-demo/main.mls ================================================ // Note: This file does not have a corresponding test file. It is recommended to // test it through the following steps. // // 1. Install `serve` globally using `npm install -g serve`. // 2. Run `serve hkmc2/shared/src/test/mlscript-compile` in the project's root. // 3. Open the URL `http://localhost:3000/apps/parsing-web-demo` in your browser. // If port 3000 is not available, use the URL suggested by the serve command. // 4. Try and test it in the browser. // // Note that after the file is updated, we need to manually refresh the browser. // Or we can use Live Server extension (or its alternatives) in VSCode to // automatically refresh the browser. import "../parsing/Parser.mls" import "../parsing/Lexer.mls" import "../../StrOps.mls" import "../../Iter.mls" import "../../XML.mls" import "../../Option.mls" import "../../Runtime.mls" import "../../Predef.mls" import "../parsing/TreeHelpers.mls" import "../parsing/Extension.mls" import "../parsing/ParseRuleVisualizer.mls" import "../parsing/Rules.mls" import "../parsing/vendors/railroad/railroad.mjs" import "./Examples.mls" open XML { elem } open Predef open Option { Some, None } open Examples module Main with ... let query = document.querySelector.bind(document) let editor = query("#editor") let selector = query("select#example") let parseButton = query("button#parse") let outputPanel = query("#output") examples Iter.each of case [key, example] then let option = document.createElement of "option" set option.value = key set option.textContent = example.name selector.appendChild of option // Load the first example. if editor.value is "" do set editor.value = example.source // Make pressing Tab add two spaces at the caret's position. editor.addEventListener of "keydown", event => if event.key is "Tab" do // Prevent the default tab behavior (focusing the next element). event.preventDefault() let start = editor.selectionStart let end = editor.selectionEnd set editor.value = editor.value.substring(0, start) + " " + editor.value.substring(end) set editor.selectionEnd = start + 2 set editor.selectionStart = editor.selectionEnd selector.addEventListener of "change", event => if examples.get(selector.value) is Some(example) do set editor.value = example.source None do throw new Error of "Example \"" + selector.value + "\" not found" parseButton.addEventListener of "click", event => let tokens = Lexer.lex(editor.value, noWhitespace: true) set outputPanel.innerHTML = "" Runtime.try_catch of () => let trees = Parser.parse(tokens) trees Iter.fromStack() Iter.each of tree => if Extension.isDiagramDirective(tree) then displayRules() else let collapsibleTree = document.createElement("collapsible-tree") set collapsibleTree.textContent = TreeHelpers.showAsTree(tree) outputPanel.appendChild of collapsibleTree error => let errorDisplay = document.createElement("error-display") errorDisplay.setError of error outputPanel.appendChild of errorDisplay let indentRegex = new RegExp("""^(\s*)""") fun parseIndentedText(text) = let root = (text: "", children: []) let stack = [(node: root, indent: -1)] text.split("\n") Iter.filtering(line => line.trim().length > 0) Iter.each of line => let indent = line.match(indentRegex).[1].length let text = line.substring(indent) while indent <= stack.[stack.length - 1].indent do stack.pop() let newNode = (text: text, children: []) stack.[stack.length - 1].node.children.push of newNode stack.push of node: newNode, indent: indent root.children // * CSS styles for the error display. // * The content in custom elements is placed inside the ShadowDOM, which is // * scoped. Therefore, styles placed directly in HTML won't be effective on // * elements inside custom elements. Placing CSS here is more convenient. let errorDisplayStyle = """ .error-container { background-color: #fdd; padding: 0.375rem 0.75rem 0.5rem; font-family: var(--monospace); color: #991b1bff; display: flex; flex-direction: column; gap: 0.25rem; } .error-message { margin: 0; font-weight: bold; font-size: 1.125rem; } .stack-trace { font-size: 0.875rem; margin: 0; list-style-type: none; padding-left: 0.5rem; }""" class CollapsibleTree extends HTMLElement with fun connectedCallback() = let rawText = this.textContent set this.textContent = "" let treeData = parseIndentedText(rawText) let treeElement = createDetailsTree(treeData) this.appendChild(treeElement) fun createDetailsTree(nodes) = let fragment = document.createDocumentFragment() nodes Iter.each of node => let details = document.createElement("details") details.setAttribute("open", "") let summary = document.createElement("summary") set summary.textContent = node.text details.appendChild of summary if node.children.length > 0 then details.appendChild of createDetailsTree(node.children) else details.setAttribute("leaf" ,"") fragment.appendChild of details let rule = document.createElement("rule") rule.classList.add("rule") fragment.appendChild of rule fragment customElements.define of "collapsible-tree", CollapsibleTree class ErrorDisplay extends HTMLElement with this.attachShadow(mode: "open") let _error = None fun connectedCallback() = this.render() fun setError(value) = set _error = Some(value) this.render() fun render() = if _error is Some(error) do let stackLines = error.stack.split("\n") if stackLines.[0].startsWith(error.name) do stackLines.shift() set this.shadowRoot.innerHTML = elem("div", "class": "error-container") of elem("h3", "class": "error-message") of error.name + ": " + error.message elem("ul", "class": "stack-trace") of stackLines Iter.mapping of line => elem("li") of line.trim() Iter.joined("") elem("style") of errorDisplayStyle customElements.define of "error-display", ErrorDisplay fun makeFigures(entries) = entries Iter.mapping of case [name, svg] then elem("figure") of elem("figcaption")(name), svg Iter.joined("") fun displayRules() = open ParseRuleVisualizer open Rules reset() set query("#syntax-diagrams>main").innerHTML = StrOps.concat of elem("h3") of "Term" makeFigures of render(railroad, "term", termRule) elem("h3") of "Type" makeFigures of render(railroad, "term", typeRule) elem("h3") of "Definition" makeFigures of render(railroad, "term", declRule) elem("h3") of "Extension" extendedKinds Iter.mapping of kindName => makeFigures of render(railroad, kindName, getRuleByKind(kindName)) Iter.joined("") displayRules() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/cpp/Makefile ================================================ CXX := g++ CFLAGS += -O3 -Wall -Wextra -std=c++20 -I. -Wno-inconsistent-missing-override -I/opt/homebrew/include LDFLAGS += -L/opt/homebrew/lib LDLIBS := -lmimalloc -lgmp SRC := INCLUDES := mlsprelude.h DST := DEFAULT_TARGET := mls TARGET := $(or $(DST),$(DEFAULT_TARGET)) .PHONY: pre all run clean auto all: $(TARGET) run: $(TARGET) ./$(TARGET) pre: $(SRC) sed -i '' 's#^//│ ##g' $(SRC) clean: rm -r $(TARGET) $(TARGET).dSYM auto: $(TARGET) $(TARGET): $(SRC) $(INCLUDES) $(CXX) $(CFLAGS) $(LDFLAGS) mlsaux.cxx $(SRC) $(LDLIBS) -o $(TARGET) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/cpp/mlsaux.cxx ================================================ #include "mlsprelude.h" #include #include ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/cpp/mlsprelude.h ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include constexpr std::size_t _mlsAlignment = 8; template class tuple_type { template > struct impl; template struct impl> { template using wrap = T; using type = std::tuple...>; }; public: using type = typename impl<>::type; }; template struct counter { using tag = counter; struct generator { friend consteval auto is_defined(tag) { return true; } }; friend consteval auto is_defined(tag); template static consteval auto exists(auto) { return true; } static consteval auto exists(...) { return generator(), false; } }; template consteval auto nextTypeTag() { if constexpr (not counter::exists(Id)) return Id; else return nextTypeTag(); } constexpr static inline uint32_t unitTag = nextTypeTag(); constexpr static inline uint32_t floatTag = nextTypeTag(); constexpr static inline uint32_t strTag = nextTypeTag(); constexpr static inline uint32_t lazyTag = nextTypeTag(); struct _mlsObject { uint32_t refCount; uint32_t tag; constexpr static inline uint32_t stickyRefCount = std::numeric_limits::max(); void incRef() { if (refCount != stickyRefCount) ++refCount; } bool decRef() { if (refCount != stickyRefCount && --refCount == 0) return true; return false; } virtual void print() const = 0; virtual void destroy() = 0; }; #define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED #include class _mlsUtil { public: [[noreturn]] static void panic(const char *msg) { std::fprintf(stderr, "Panic: %s\n", msg); std::string st = boost::stacktrace::to_string(boost::stacktrace::stacktrace()); std::fprintf(stderr, "%s\n", st.c_str()); std::abort(); } [[noreturn]] static void panic_with(const char *msg, const char *func, const char *file, int line) { std::fprintf(stderr, "Panic: %s at %s %s:%d\n", msg, func, file, line); std::string st = boost::stacktrace::to_string(boost::stacktrace::stacktrace()); std::fprintf(stderr, "%s\n", st.c_str()); std::abort(); } }; #define _mls_assert(e) \ (__builtin_expect(!(e), 0) \ ? _mlsUtil::panic_with("assertion failed", __func__, \ __FILE__, __LINE__) \ : (void)0) struct _mlsFloatShape : public _mlsObject { double f; }; class _mlsValue { using uintptr_t = std::uintptr_t; using intptr_t = std::intptr_t; using int64_t = std::int64_t; void *value; bool isInt63() const { return (reinterpret_cast(value) & 1) == 1; } bool isFloat() const { return isPtr() && asObject()->tag == floatTag; } bool isPtr() const { return (reinterpret_cast(value) & 1) == 0; } int64_t asInt63() const { return reinterpret_cast(value) >> 1; } uintptr_t asRawInt() const { return reinterpret_cast(value); } static _mlsValue fromRawInt(uintptr_t i) { return _mlsValue(reinterpret_cast(i)); } static _mlsValue fromInt63(int64_t i) { return _mlsValue(reinterpret_cast((i << 1) | 1)); } void *asPtr() const { _mls_assert(!isInt63()); return value; } _mlsObject *asObject() const { _mls_assert(isPtr()); return static_cast<_mlsObject *>(value); } bool eqInt63(const _mlsValue &other) const { return asRawInt() == other.asRawInt(); } _mlsValue addInt63(const _mlsValue &other) const { return fromRawInt(asRawInt() + other.asRawInt() - 1); } _mlsValue subInt63(const _mlsValue &other) const { return fromRawInt(asRawInt() - other.asRawInt() + 1); } _mlsValue mulInt63(const _mlsValue &other) const { return fromInt63(asInt63() * other.asInt63()); } _mlsValue divInt63(const _mlsValue &other) const { return fromInt63(asInt63() / other.asInt63()); } _mlsValue modInt63(const _mlsValue &other) const { return fromInt63(asInt63() % other.asInt63()); } _mlsValue gtInt63(const _mlsValue &other) const { return fromBoolLit(asInt63() > other.asInt63()); } _mlsValue ltInt63(const _mlsValue &other) const { return fromBoolLit(asInt63() < other.asInt63()); } _mlsValue geInt63(const _mlsValue &other) const { return fromBoolLit(asInt63() >= other.asInt63()); } _mlsValue leInt63(const _mlsValue &other) const { return fromBoolLit(asInt63() <= other.asInt63()); } _mlsValue minInt63(const _mlsValue &other) const { int64_t a = asInt63(); int64_t b = other.asInt63(); return fromInt63(a < b ? a : b); } _mlsValue maxInt63(const _mlsValue &other) const { int64_t a = asInt63(); int64_t b = other.asInt63(); return fromInt63(a > b ? a : b); } _mlsValue absInt63() const { int64_t a = asInt63(); return fromInt63(a < 0 ? -a : a); } _mlsValue floorDivInt63(const _mlsValue &other) const { int64_t a = asInt63(); int64_t b = other.asInt63(); int64_t q = a / b; int64_t r = a % b; if ((r > 0 && b < 0) || (r < 0 && b > 0)) q = q - 1; return fromInt63(q); } _mlsValue floorModInt63(const _mlsValue &other) const { int64_t a = asInt63(); int64_t b = other.asInt63(); long r = a % b; if ((r > 0 && b < 0) || (r < 0 && b > 0)) r = r + b; return fromInt63(r); } public: struct inc_ref_tag {}; explicit _mlsValue() : value(nullptr) {} explicit _mlsValue(void *value) : value(value) {} explicit _mlsValue(void *value, inc_ref_tag) : value(value) { if (isPtr()) asObject()->incRef(); } _mlsValue(const _mlsValue &other) : value(other.value) { if (isPtr()) asObject()->incRef(); } _mlsValue &operator=(const _mlsValue &other) { if (value != nullptr && isPtr()) asObject()->decRef(); value = other.value; if (isPtr()) asObject()->incRef(); return *this; } ~_mlsValue() { if (isPtr()) if (asObject()->decRef()) { asObject()->destroy(); value = nullptr; } } int64_t asInt() const { _mls_assert(isInt63()); return asInt63(); } static _mlsValue fromIntLit(int64_t i) { return fromInt63(i); } static _mlsValue fromBoolLit(bool b) { return fromInt63(b); } template static tuple_type<_mlsValue, N> never() { __builtin_unreachable(); } static _mlsValue never() { __builtin_unreachable(); } template static _mlsValue create(U... args) { return _mlsValue(T::create(args...)); } static void destroy(_mlsValue &v) { v.~_mlsValue(); } template static bool isValueOf(const _mlsValue &v) { return v.asObject()->tag == T::typeTag; } static bool isIntLit(const _mlsValue &v, int64_t n) { return v.asInt63() == n; } static bool isInt(const _mlsValue &v) { return v.isInt63(); } template static T *as(const _mlsValue &v) { return dynamic_cast(v.asObject()); } template static T *cast(_mlsValue &v) { return static_cast(v.asObject()); } _mlsValue floorDiv(const _mlsValue &other) const; _mlsValue floorMod(const _mlsValue &other) const; _mlsValue pow(const _mlsValue &other) const; _mlsValue abs() const; // Operators _mlsValue operator==(const _mlsValue &other) const; _mlsValue operator!=(const _mlsValue &other) const; _mlsValue operator&&(const _mlsValue &other) const; _mlsValue operator||(const _mlsValue &other) const; _mlsValue operator+(const _mlsValue &other) const; _mlsValue operator-(const _mlsValue &other) const; _mlsValue operator-() const; _mlsValue operator*(const _mlsValue &other) const; _mlsValue operator/(const _mlsValue &other) const; _mlsValue operator%(const _mlsValue &other) const; _mlsValue operator>(const _mlsValue &other) const; _mlsValue operator<(const _mlsValue &other) const; _mlsValue operator>=(const _mlsValue &other) const; _mlsValue operator<=(const _mlsValue &other) const; // Auxiliary functions void print() const { if (isInt63()) std::printf("%" PRIu64, asInt63()); else if (isPtr() && asObject()) asObject()->print(); } }; struct _mls_Callable : public _mlsObject { virtual _mlsValue _mls_apply0() { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply1(_mlsValue) { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply2(_mlsValue, _mlsValue) { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply3(_mlsValue, _mlsValue, _mlsValue) { throw std::runtime_error("Not implemented"); } virtual _mlsValue _mls_apply4(_mlsValue, _mlsValue, _mlsValue, _mlsValue) { throw std::runtime_error("Not implemented"); } virtual void destroy() override {} }; inline static _mls_Callable *_mlsToCallable(_mlsValue fn) { auto *ptr = _mlsValue::as<_mls_Callable>(fn); if (!ptr) throw std::runtime_error("Not a callable object"); return ptr; } template inline static _mlsValue _mlsCall(_mlsValue f, U... args) { static_assert(sizeof...(U) <= 4, "Too many arguments"); if constexpr (sizeof...(U) == 0) return _mlsToCallable(f)->_mls_apply0(); else if constexpr (sizeof...(U) == 1) return _mlsToCallable(f)->_mls_apply1(args...); else if constexpr (sizeof...(U) == 2) return _mlsToCallable(f)->_mls_apply2(args...); else if constexpr (sizeof...(U) == 3) return _mlsToCallable(f)->_mls_apply3(args...); else if constexpr (sizeof...(U) == 4) return _mlsToCallable(f)->_mls_apply4(args...); } template inline static T *_mlsMethodCall(_mlsValue self) { auto *ptr = _mlsValue::as(self); if (!ptr) throw std::runtime_error("unable to convert object for method calls"); return ptr; } inline int _mlsLargeStack(void *(*fn)(void *)) { pthread_t thread; pthread_attr_t attr; size_t stacksize = 1024 * 1024 * 1024; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, stacksize); int rc = pthread_create(&thread, &attr, fn, nullptr); if (rc) { printf("ERROR: return code from pthread_create() is %d\n", rc); return 1; } pthread_join(thread, NULL); return 0; } _mlsValue _mlsMain(); inline void *_mlsMainWrapper(void *) { _mlsValue res = _mlsMain(); res.print(); return nullptr; } struct _mls_Unit final : public _mlsObject { constexpr static inline const char *typeName = "Unit"; constexpr static inline uint32_t typeTag = unitTag; virtual void print() const override { std::printf(typeName); } static _mlsValue create() { static _mls_Unit mlsUnit alignas(_mlsAlignment); mlsUnit.refCount = stickyRefCount; mlsUnit.tag = typeTag; return _mlsValue(&mlsUnit); } virtual void destroy() override {} }; struct _mls_Float final : public _mlsFloatShape { constexpr static inline const char *typeName = "Float"; constexpr static inline uint32_t typeTag = floatTag; virtual void print() const override { std::printf(typeName); std::printf("("); std::printf("%f", f); std::printf(")"); } static _mlsValue create(double f) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Float; _mlsVal->f = f; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } _mlsValue operator+(const _mls_Float &other) const { return _mlsValue::create<_mls_Float>(f + other.f); } _mlsValue operator-(const _mls_Float &other) const { return _mlsValue::create<_mls_Float>(f - other.f); } _mlsValue operator*(const _mls_Float &other) const { return _mlsValue::create<_mls_Float>(f * other.f); } _mlsValue operator/(const _mls_Float &other) const { return _mlsValue::create<_mls_Float>(f / other.f); } _mlsValue operator==(const _mls_Float &other) const { return _mlsValue::fromBoolLit(f == other.f); } _mlsValue operator!=(const _mls_Float &other) const { return _mlsValue::fromBoolLit(f == other.f); } _mlsValue operator>(const _mls_Float &other) const { return _mlsValue::fromBoolLit(f > other.f); } _mlsValue operator<(const _mls_Float &other) const { return _mlsValue::fromBoolLit(f < other.f); } _mlsValue operator>=(const _mls_Float &other) const { return _mlsValue::fromBoolLit(f >= other.f); } _mlsValue operator<=(const _mls_Float &other) const { return _mlsValue::fromBoolLit(f <= other.f); } virtual void destroy() override { operator delete(this, std::align_val_t(_mlsAlignment)); } }; struct _mls_Str final : public _mlsObject { std::string str; constexpr static inline const char *typeName = "Str"; constexpr static inline uint32_t typeTag = strTag; virtual void print() const override { std::printf("\""); for (const auto c : str) { switch (c) { case '\'': std::printf("\\\'"); break; case '\"': std::printf("\\\""); break; case '\?': std::printf("\\\?"); break; case '\\': std::printf("\\\\"); break; case '\a': std::printf("\\a"); break; case '\b': std::printf("\\b"); break; case '\f': std::printf("\\f"); break; case '\n': std::printf("\\n"); break; case '\r': std::printf("\\r"); break; case '\t': std::printf("\\t"); break; case '\v': std::printf("\\v"); break; default: if (c < 32 || c > 126) std::printf("\\x%02x", c); else std::putchar(c); } } std::printf("\""); std::fflush(stdout); } static _mlsValue create(const std::string_view str) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Str; _mlsVal->str = str; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } static _mlsValue create(const std::string_view str1, const std::string_view str2) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Str; _mlsVal->str = str1; _mlsVal->str += str2; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } virtual void destroy() override { str.~basic_string(); operator delete(this, std::align_val_t(_mlsAlignment)); } }; struct _mls_Lazy final : public _mlsObject { _mlsValue init; _mlsValue value; bool evaluated; constexpr static inline const char *typeName = "Lazy"; constexpr static inline uint32_t typeTag = lazyTag; virtual void print() const override { std::printf(typeName); } static _mlsValue create(_mlsValue init) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_Lazy; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; _mlsVal->init = init; _mlsVal->value = _mlsValue::create<_mls_Unit>(); _mlsVal->evaluated = false; return _mlsValue(_mlsVal); } virtual void destroy() override { _mlsValue::destroy(init); _mlsValue::destroy(value); operator delete(this, std::align_val_t(_mlsAlignment)); } _mlsValue _mls_get() { if (!evaluated) { value = _mlsCall(init); evaluated = true; } return value; } }; #include struct _mls_ZInt final : public _mlsObject { boost::multiprecision::mpz_int z; constexpr static inline const char *typeName = "Z"; constexpr static inline uint32_t typeTag = nextTypeTag(); virtual void print() const override { std::printf(typeName); std::printf("("); std::printf("%s", z.str().c_str()); std::printf(")"); } virtual void destroy() override { z.~number(); operator delete(this, std::align_val_t(_mlsAlignment)); } static _mlsValue create() { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_ZInt; _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } static _mlsValue create(_mlsValue z) { auto _mlsVal = new (std::align_val_t(_mlsAlignment)) _mls_ZInt; _mlsVal->z = z.asInt(); _mlsVal->refCount = 1; _mlsVal->tag = typeTag; return _mlsValue(_mlsVal); } _mlsValue operator+(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z + other.z; return _mlsVal; } _mlsValue operator-(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z - other.z; return _mlsVal; } _mlsValue operator*(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z * other.z; return _mlsVal; } _mlsValue operator/(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z / other.z; return _mlsVal; } _mlsValue operator%(const _mls_ZInt &other) const { auto _mlsVal = _mlsValue::create<_mls_ZInt>(); _mlsValue::cast<_mls_ZInt>(_mlsVal)->z = z % other.z; return _mlsVal; } _mlsValue operator==(const _mls_ZInt &other) const { return _mlsValue::fromBoolLit(z == other.z); } _mlsValue operator>(const _mls_ZInt &other) const { return _mlsValue::fromBoolLit(z > other.z); } _mlsValue operator<(const _mls_ZInt &other) const { return _mlsValue::fromBoolLit(z < other.z); } _mlsValue operator>=(const _mls_ZInt &other) const { return _mlsValue::fromBoolLit(z >= other.z); } _mlsValue operator<=(const _mls_ZInt &other) const { return _mlsValue::fromBoolLit(z <= other.z); } _mlsValue toInt() const { return _mlsValue::fromIntLit(z.convert_to()); } static _mlsValue fromInt(int64_t i) { return _mlsValue::create<_mls_ZInt>(_mlsValue::fromIntLit(i)); } }; [[noreturn, gnu::noinline]] inline void _mlsNonExhaustiveMatch() { _mlsUtil::panic("Non-exhaustive match"); } inline _mlsValue _mls_builtin_pow(_mlsValue a, _mlsValue b) { return a.pow(b); } inline _mlsValue _mls_builtin_abs(_mlsValue a) { return a.abs(); } inline _mlsValue _mls_builtin_floor_div(_mlsValue a, _mlsValue b) { return a.floorDiv(b); } inline _mlsValue _mls_builtin_floor_mod(_mlsValue a, _mlsValue b) { return a.floorMod(b); } inline _mlsValue _mls_builtin_trunc_div(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isInt(a)); _mls_assert(_mlsValue::isInt(b)); return a / b; } inline _mlsValue _mls_builtin_trunc_mod(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isInt(a)); _mls_assert(_mlsValue::isInt(b)); return a % b; } inline _mlsValue _mls_builtin_int2str(_mlsValue a) { _mls_assert(_mlsValue::isInt(a)); char buf[32]; std::snprintf(buf, sizeof(buf), "%" PRIu64, a.asInt()); return _mlsValue::create<_mls_Str>(buf); } inline _mlsValue _mls_builtin_float2str(_mlsValue a) { _mls_assert(_mlsValue::isValueOf<_mls_Float>(a)); char buf[128]; std::snprintf(buf, sizeof(buf), "%f", _mlsValue::cast<_mls_Float>(a)->f); return _mlsValue::create<_mls_Str>(buf); } inline _mlsValue _mls_builtin_int2float(_mlsValue a) { return _mlsValue::create<_mls_Float>(a.asInt()); } inline _mlsValue _mls_builtin_str_concat(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_Str>(a)); _mls_assert(_mlsValue::isValueOf<_mls_Str>(b)); auto *strA = _mlsValue::cast<_mls_Str>(a); auto *strB = _mlsValue::cast<_mls_Str>(b); return _mlsValue::create<_mls_Str>(strA->str.c_str(), strB->str.c_str()); } inline _mlsValue _mls_builtin_z_add(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) + *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_sub(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) - *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_mul(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) * *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_div(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) / *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_mod(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) % *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_equal(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) == *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_gt(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) > *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_lt(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) < *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_geq(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) >= *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_leq(_mlsValue a, _mlsValue b) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(b)); return *_mlsValue::cast<_mls_ZInt>(a) <= *_mlsValue::cast<_mls_ZInt>(b); } inline _mlsValue _mls_builtin_z_to_int(_mlsValue a) { _mls_assert(_mlsValue::isValueOf<_mls_ZInt>(a)); return _mlsValue::cast<_mls_ZInt>(a)->toInt(); } inline _mlsValue _mls_builtin_z_of_int(_mlsValue a) { _mls_assert(_mlsValue::isInt(a)); return _mlsValue::create<_mls_ZInt>(a); } inline _mlsValue _mls_builtin_print(_mlsValue a) { a.print(); return _mlsValue::create<_mls_Unit>(); } inline _mlsValue _mls_builtin_println(_mlsValue a) { a.print(); std::puts(""); return _mlsValue::create<_mls_Unit>(); } inline _mlsValue _mls_builtin_debug(_mlsValue a) { a.print(); std::puts(""); return a; } inline _mlsValue _mlsValue::floorDiv(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return floorDivInt63(other); _mls_assert(false); } inline _mlsValue _mlsValue::floorMod(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return floorModInt63(other); _mls_assert(false); } inline _mlsValue _mlsValue::pow(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return fromInt63(std::pow(asInt63(), other.asInt63())); if (isFloat() && other.isFloat()) return _mlsValue::create<_mls_Float>( std::pow(as<_mls_Float>(*this)->f, as<_mls_Float>(other)->f)); _mls_assert(false); } inline _mlsValue _mlsValue::abs() const { if (isInt63()) return absInt63(); if (isFloat()) return _mlsValue::create<_mls_Float>(std::abs(as<_mls_Float>(*this)->f)); _mls_assert(false); } // Operators inline _mlsValue _mlsValue::operator==(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return _mlsValue::fromBoolLit(eqInt63(other)); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) == *as<_mls_Float>(other); bool sameTag = isPtr() && other.isPtr() && asObject()->tag == other.asObject()->tag; if (!sameTag) return _mlsValue::fromBoolLit(false); _mls_assert(false); } inline _mlsValue _mlsValue::operator!=(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return _mlsValue::fromBoolLit(!eqInt63(other)); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) != *as<_mls_Float>(other); bool sameTag = isPtr() && other.isPtr() && asObject()->tag == other.asObject()->tag; if (!sameTag) return _mlsValue::fromBoolLit(true); _mls_assert(false); } inline _mlsValue _mlsValue::operator&&(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return _mlsValue::fromBoolLit(asInt63() && other.asInt63()); _mls_assert(false); } inline _mlsValue _mlsValue::operator||(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return _mlsValue::fromBoolLit(asInt63() || other.asInt63()); _mls_assert(false); } inline _mlsValue _mlsValue::operator+(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return addInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) + *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator-(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return subInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) - *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator-() const { if (isInt63()) return fromInt63(-asInt63()); if (isFloat()) return _mlsValue::create<_mls_Float>(-as<_mls_Float>(*this)->f); _mls_assert(false); } inline _mlsValue _mlsValue::operator*(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return mulInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) * *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator/(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return divInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) / *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator%(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return modInt63(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator>(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return gtInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) > *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator<(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return ltInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) < *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator>=(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return geInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) >= *as<_mls_Float>(other); _mls_assert(false); } inline _mlsValue _mlsValue::operator<=(const _mlsValue &other) const { if (isInt63() && other.isInt63()) return leInt63(other); if (isFloat() && other.isFloat()) return *as<_mls_Float>(*this) <= *as<_mls_Float>(other); _mls_assert(false); } ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/NofibPrelude.mls ================================================ import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open Predef module NofibPrelude with ... type Char = String abstract class Option[out T]: Some[T] | None data class Some[out T](x: T) extends Option[T] object None extends Option fun fromSome(s) = if s is Some(x) then x data class Lazy[out A](init: () -> A) with mut val cached: Option[A] = None fun get() = if cached is Some(v) then v else let v = init() set cached = Some(v) v fun lazy(x) = Lazy(x) fun force(x) = if x is Lazy then x.Lazy#get() abstract class List[out T]: Cons[T] | Nil data class (::) Cons[out T](head: T, tail: List[T]) extends List[T] with fun toString() = "[" + _internal_cons_to_str(Cons(head, tail)) + "]" object Nil extends List with fun toString() = "[]" fun _internal_cons_to_str(ls) = if ls is Nil then "" h :: Nil then render(h) h :: t then render(h) + "," + _internal_cons_to_str(t) fun ltList(xs, ys, lt, gt) = if xs is Nil and ys is Nil then false else true x :: xs and ys is Nil then false y :: ys and lt(x, y) then true gt(x, y) then false else ltList(xs, ys, lt, gt) fun list(...args) = if args is [] then Nil [x, ...xs] then x :: list(...xs) type LazyList[out T] = Lazy[LzList[T]] abstract class LzList[out T]: LzCons[T] | LzNil data class LzCons[out T](head: T, tail: LazyList[T]) extends LzList[T] object LzNil extends LzList fun ltTup2(t1, t2, lt1, gt1, lt2) = if t1 is [a, b] and t2 is [c, d] and lt1(a, c) then true gt1(a, c) then false else lt2(b, d) fun eqTup2(t1, t2) = if t1 is [a, b] and t2 is [c, d] then a == c and b == d fun compose(f, g) = x => f(g(x)) fun snd(x) = if x is [f, s] then s fun fst(x) = if x is [f, s] then f fun until(p, f, i) = if p(i) then i else until(p, f, f(i)) fun flip(f, x, y) = f(y)(x) fun power(a, n) = globalThis.Math.pow(a, n) fun intDiv(a, b) = globalThis.Math.floor(a / b) fun intQuot(a, b) = globalThis.Math.trunc(a / b) fun intMod(a, b) = a - (b * intDiv(a, b)) fun intRem(a, b) = a - (b * intQuot(a, b)) fun quotRem(a, b) = [intQuot(a, b), intRem(a, b)] fun divMod(a, b) = [intDiv(a, b), intMod(a, b)] fun max(a, b) = globalThis.Math.max(a, b) fun min(a, b) = globalThis.Math.min(a, b) fun abs(x) = globalThis.Math.abs(x) fun head(l) = if l is h :: t then h fun tail(l) = if l is h :: t then t fun while_(p, f, x) = if p(x) then while_(p, f, f(x)) else x fun reverse(l) = fun r(l', l) = if l is x :: xs then r(x :: l', xs) else l' r(Nil, l) fun map(f, xs) = if xs is x :: xs then f(x) :: map(f, xs) Nil then Nil fun listLen(ls) = fun l(ls, a) = if ls is Nil then a h :: t then l(t, a + 1) l(ls, 0) fun listEq(xs, ys) = if xs is Nil and ys is Nil then true xs is hx :: tx and ys is hy :: ty and (hx == hy) then listEq(tx, ty) else false fun listEqBy(f, a, b) = if a is Nil and b is Nil then true x :: xs and b is y :: ys then f(x, y) && listEqBy(f, xs, ys) else false fun listNeq(xs, ys) = if xs is Nil and ys is Nil then false xs is hx :: tx and ys is hy :: ty and (hx == hy) then listNeq(tx, ty) else true fun enumFromTo(a, b) = if a <= b then a :: enumFromTo(a + 1, b) else Nil fun enumFromThenTo(a, t, b) = if a <= b then a :: enumFromThenTo(t, 2 * t - a, b) else Nil fun leave(n, ls) = if ls is Nil then Nil h :: t and n <= 0 then ls else leave(n - 1, t) fun take(n, ls) = if ls is Nil then Nil h :: t and n <= 0 then Nil else h :: take(n - 1, t) fun splitAt(n, ls) = [take(n, ls), leave(n, ls)] fun zip(xs, ys) = if xs is x :: xs and ys is y :: ys then [x, y] :: zip(xs, ys) else Nil fun inList(x, ls) = if ls is h :: t and x === h then true else inList(x, t) Nil then false fun notElem(x, ls) = not(inList(x, ls)) fun (+:) append(xs, ys) = if xs is Nil then ys x :: xs then x :: append(xs, ys) fun concat(ls) = if ls is Nil then Nil x :: xs then append(x, concat(xs)) fun filter(f, ls) = if ls is Nil then Nil h :: t and f(h) then h :: filter(f, t) else filter(f, t) fun all(p, ls) = if ls is Nil then true h :: t and p(h) then all(p, t) else false fun orList(ls) = if ls is Nil then false h :: t and h then true else orList(t) fun leaveWhile(f, ls) = if ls is Nil then Nil h :: t and f(h) then leaveWhile(f, t) else h :: t fun foldl(f, a, xs) = if xs is Nil then a h :: t then foldl(f, f(a, h), t) fun scanl(f, q, ls) = if ls is Nil then q :: Nil x :: xs then q :: scanl(f, f(q, x), xs) fun scanr(f, q, ls) = if ls is Nil then q :: Nil x :: xs and scanr(f, q, xs) is q :: t then f(x, q) :: q :: t fun foldr(f, z, xs) = if xs is Nil then z h :: t then f(h, foldr(f, z, t)) fun foldl1(f, ls) = if ls is x :: xs then foldl(f, x, xs) fun foldr1(f, ls) = if ls is x :: Nil then x x :: xs then f(x, foldr1(f, xs)) fun maximum(xs) = foldl1((x, y) => if x > y then x else y, xs) fun nubBy(eq, ls) = if ls is Nil then Nil h :: t then h :: nubBy(eq, filter(y => not(eq(h, y)), t)) fun zipWith(f, xss, yss) = if xss is x :: xs and yss is y :: ys then f(x, y) :: zipWith(f, xs, ys) else Nil fun deleteBy(eq, x, ys) = if ys is Nil then Nil y :: ys and eq(x, y) then ys else y :: deleteBy(eq, x, ys) fun unionBy(eq, xs, ys) = append(xs, foldl((acc, y) => deleteBy(eq, y, acc), nubBy(eq, ys), xs)) fun union(xs, ys) = unionBy((x, y) => x == y, xs, ys) fun atIndex(i, ls) = if ls is h :: t and i == 0 then h else atIndex(i - 1, t) fun sum(xs) = fun go(xs, a) = if xs is Nil then a h :: t then go(t, a + h) go(xs, 0) fun null_(ls) = if ls is Nil then true else false fun replicate(n, x) = if n == 0 then Nil else x :: replicate(n - 1, x) fun unzip(l) = fun f(l, a, b) = if l is Nil then [reverse(a), reverse(b)] [x, y] :: t then f(t, x :: a, y :: b) f(l, Nil, Nil) fun zip3(xs, ys, zs) = if xs is x :: xs and ys is y :: ys and zs is z :: zs then [x, y, z] :: zip3(xs, ys, zs) else Nil fun transpose(xss) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is hd :: tl then [hd, tl] :: lscomp(t) else lscomp(t) fun combine(y, h, ys, t) = (y :: h) :: transpose(ys :: t) if xss is Nil then Nil Nil :: xss then transpose(xss) (x :: xs) :: xss and unzip(lscomp(xss)) is [hds, tls] then combine(x, hds, xs, tls) fun break_(p, ls) = if ls is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] break_(p, xs) is [ys, zs] then [x :: ys, zs] fun flatMap(f, ls) = if ls is Nil then Nil h :: t then append(f(h), flatMap(f, t)) // ===================== fun map_lz(f, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(f(h), map_lz(f, t)) fun filter_lz(p, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) and p(h) then LzCons(h, filter_lz(p, t)) else force(filter_lz(p, t)) fun nubBy_lz(eq, ls) = lazy of () => if force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(h, nubBy_lz(eq, filter_lz(y => not(eq(h, y)), t))) fun nub_lz(ls) = nubBy_lz((x, y) => x == y, ls) fun take_lz(n, ls) = if n > 0 and force(ls) is LzNil then Nil LzCons(h, t) then h :: take_lz(n - 1, t) else Nil fun take_lz_lz(n, ls) = lazy of () => if n > 0 and force(ls) is LzNil then LzNil LzCons(h, t) then LzCons(h, take_lz_lz(n - 1, t)) else LzNil fun leave_lz(n, ls) = if n <= 0 then ls force(ls) is LzNil then lazy of () => LzNil LzCons(h, t) then leave_lz(n - 1, t) fun splitAt_lz(n, ls) = [take_lz(n, ls), leave_lz(n, ls)] fun zip_lz_nl(xs, ys) = if force(xs) is LzCons(x, xs) and ys is y :: ys then [x, y] :: zip_lz_nl(xs, ys) else Nil fun zip_lz_lz(xs, ys) = if force(xs) is LzCons(x, xs) and force(ys) is LzCons(y, ys) then lazy of () => LzCons([x, y], zip_lz_lz(xs, ys)) else lazy of () => LzNil fun zipWith_lz_lz(f, xss, yss) = lazy of () => if force(xss) is LzCons(x, xs) and (force(yss)) is LzCons(y, ys) then LzCons(f(x, y), zipWith_lz_lz(f, xs, ys)) else LzNil fun zipWith_lz_nl(f, xss, yss) = if force(xss) is LzCons(x, xs) and yss is y :: ys then f(x, y) :: zipWith_lz_nl(f, xs, ys) else Nil fun iterate(f, x) = lazy of () => LzCons(x, iterate(f, f(x))) fun append_nl_lz(xs, ys) = if xs is Nil then ys h :: t then lazy of () => LzCons(h, append_nl_lz(t, ys)) fun append_lz_lz(xs, ys) = lazy of () => if force(xs) is LzNil then force(ys) LzCons(h, t) then LzCons(h, append_lz_lz(t, ys)) fun replicate_lz(n, x) = if n == 0 then lazy of () => LzNil else lazy of () => LzCons(x, replicate_lz(n - 1, x)) fun enumFrom(a) = lazy of () => LzCons(a, enumFrom(a + 1)) fun head_lz(ls) = if force(ls) is LzCons(h, t) then h fun repeat(x) = lazy of () => LzCons(x, repeat(x)) // ===================== fun stringOfFloat(x) = x + "" fun stringOfInt(x) = x + "" fun stringConcat(x, y) = x + y fun stringListConcat(ls) = if ls is Nil then "" h :: t then stringConcat(h, stringListConcat(t)) fun sqrt(x) = globalThis.Math.sqrt(x) fun tan(x) = globalThis.Math.tan(x) fun sin(x) = globalThis.Math.sin(x) fun cos(x) = globalThis.Math.cos(x) fun round(x) = globalThis.Math.round(x) fun int_of_char(x) = x.charCodeAt(0) fun nofibStringToList(s) = fun go(i) = if i < s.length then s.charAt(i) :: go(i + 1) else Nil go(0) fun nofibListToString(ls) = if ls is Nil then "" h :: t then h + nofibListToString(t) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/ansi.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module ansi with ... val cls = nofibStringToList("L") //│ cls = ["L"] fun goto(x, y) = "E" :: "[" :: (nofibStringToList(stringOfInt(y)) +: (";" :: nofibStringToList(stringOfInt(x)) +: nofibStringToList("H"))) fun at(x_y, s) = if x_y is [x, y] then goto(x, y) +: s fun highlight(s) = nofibStringToList("ESC[7m") +: s +: nofibStringToList("ESC[0m") fun end(xs) = nofibStringToList("") fun readChar(eof, consume, cs) = if cs is Nil then eof(Nil) c :: cs then consume(c, cs) fun peekChar(eof, consume, cs) = if cs is Nil then eof(Nil) c :: cs then consume(c, c :: cs) fun pressAnyKey(prog, x) = readChar(prog, (c, x) => prog(x), x) fun unreadChar(c, prog, cs) = prog(c :: cs) fun writeChar(c, prog, cs) = c :: prog(cs) fun writeString(s, prog, cs) = s +: prog(cs) fun writes(ss, a, b) = writeString(concat(ss), a, b) fun ringBell(prog, cs) = writeChar("B", prog, cs) fun clearScreen(a, b) = writeString(cls, a, b) fun writeAt(x_y, s, a) = if x_y is [x, y] then p => writeString(goto(x, y) +: s, a, p) fun moveTo(x_y, a) = if x_y is [x, y] then p => writeString(goto(x, y), a, p) fun returnn(s, consume) = consume(reverse(s)) //│ ———————————————————————————————————————————————————————————————————————————————— fun deletee(n, s, l, consume, d) = if n > 0 then writeString(nofibStringToList("BS_BS"), loop(n - 1, tail(s), l, consume), d) else ringBell(loop(0, nofibStringToList(""), l, consume), d) fun loop(n, s, l, consume) = x => readChar of returnn(s, consume) (c, d) => if c == "B" then deletee(n, s, l, consume, d) c == "D" then deletee(n, s, l, consume, d) c == "`" then returnn(s, consume)(d) n < l then writeChar(c, loop(n + 1, c :: s, l, consume), d) else ringBell(loop(n, s, l, consume), d) x //│ ———————————————————————————————————————————————————————————————————————————————— fun readAt(x_y, l, consume) = writeAt(x_y, replicate(l, "_"), moveTo(x_y, loop(0, "", l, consume))) fun promptReadAt(x_y, l, prompt, consume) = if x_y is [x, y] then writeAt([x, y], prompt, readAt([x + listLen(prompt), y], l, consume)) fun program(input) = writes( cls :: at([17, 5], highlight(nofibStringToList("Demonstration program"))) :: at([48, 5], nofibStringToList("Version 1.0")) :: at([17, 7], nofibStringToList("This program illustrates a simple approach")) :: at([17, 8], nofibStringToList("to screen-based interactive programs using")) :: at([17, 9], nofibStringToList("the Hugs functional programming system.")) :: at([17, 11], nofibStringToList("Please press any key to continue ...")) :: Nil, x => pressAnyKey(promptReadAt( [17, 15], 18, nofibStringToList("Please enter your name: "), (name) => let reply = nofibStringToList("Hello ") +: name +: nofibStringToList("!") writeAt( [40 - (listLen(reply) / 2), 18], reply, moveTo( [1, 23], y => writeString(nofibStringToList("I'm waiting..."), x => pressAnyKey(end, x), y) ) ) ), x), input ) fun testAnsi_nofib(n) = foldr(compose, (x) => x, replicate(n, program))(nofibStringToList("testtesttest")) fun main() = nofibListToString(testAnsi_nofib(1)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/atom.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module atom with ... data class State(position: List[Num], velocity: List[Num]) fun dotPlus(fs, gs) = if fs is Nil then gs gs is Nil then fs fs is f :: fs and gs is g :: gs then (f + g) :: dotPlus(fs, gs) fun dotMult(fs, gs) = if fs is f :: fs and gs is g :: gs then (f * g) :: dotMult(fs, gs) else Nil fun scalarMut(c, fs) = if fs is Nil then Nil f :: fs then (c * f) :: scalarMut(c, fs) fun testforce(k, ss) = lazy of () => if force(ss) is LzCons(State(pos, vel), atoms) then LzCons(dotMult(scalarMut(-1.0, k), pos), testforce(k, atoms)) fun show(s) = fun lscomp(ls) = if ls is Nil then Nil component :: t then Cons(stringConcat(stringOfFloat(component), "\t"), lscomp(t)) if s is State(pos, vel) then stringListConcat of lscomp(pos) fun propagate(dt, aforce, state) = if state is State(pos, vel) then State(dotPlus(pos, scalarMut(dt, vel)), dotPlus(vel, scalarMut(dt, aforce))) fun runExperiment(law, dt, param, init) = lazy of () => let stream = runExperiment(law, dt, param, init) LzCons(init, zipWith_lz_lz((x, y) => propagate(dt, x, y), law(param, stream), stream)) fun testAtom_nofib(n) = fun lscomp(ls) = if ls is Nil then Nil state :: t then stringConcat(show(state), "\n") :: lscomp(t) stringListConcat of lscomp(take_lz(n, runExperiment(testforce, 0.02, 1.0 :: Nil, State(1.0 :: Nil, 0.0 :: Nil)))) // NOTE: original input 1000 fun main() = testAtom_nofib(20) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/awards.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module awards with ... fun delete_(xs, e) = deleteBy((x, y) => x == y, e, xs) fun listDiff(a, ls) = foldl(delete_, a, ls) //│ ———————————————————————————————————————————————————————————————————————————————— fun qsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) fun qpart(le, x, ys, rlt, rge, r) = if ys is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) else qpart(le, x, ys, y :: rlt, rge, r) fun rqsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) fun rqpart(le, x, yss, rle, rgt, r) = if yss is Nil then qsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) else rqpart(le, x, ys, rle, y :: rgt, r) //│ ———————————————————————————————————————————————————————————————————————————————— fun sort(l) = qsort((a, b) => ltTup2(a, b, (a, b) => a < b, (a, b) => a > b, (a, b) => ltList(a, b, (a, b) => a < b, (a, b) => a > b)), l, Nil) fun perms(m, nns) = if nns is Nil then Nil m == 1 then map(x => x :: Nil, nns) nns is n :: ns then map(x => n :: x, perms(m-1, ns)) +: perms(m, ns) fun awards(scores) = let sumscores = map(p => [sum(p), p], perms(3, scores)) fun atleast(threshold) = filter(case { [sum_, p] then sum_ >= threshold }, sumscores) fun award(name_threshold) = if name_threshold is [name, threshold] then map(ps => [name, ps], sort(atleast(threshold))) award(["Gold", 70]) +: award(["Silver", 60]) +: award(["Bronze", 50]) fun findawards(scores) = if awards(scores) is Nil then Nil head_ :: tail_ and head_ is [award, [sum_, perm]] then [award, [sum_, perm]] :: findawards(listDiff(scores, perm)) fun findallawards(competitors) = map(case { [name, scores] then [name, findawards(scores)] }, competitors) fun competitors(i) = list of ["Simon", list of 35, 27, 40, i, 34, 21] ["Hans", list of 23, 19, 45, i, 17, 10, 5, 8, 14] ["Phil", list of 1, 18, i, 20, 21, 19, 34, 8, 16, 21] ["Kevin", list of 9, 23, 17, 54, i, 41, 9, 18, 14] fun testAwards_nofib(n) = map(x => findallawards(competitors(intMod(x, 100))), enumFromTo(1, n)) // NOTE: original input 1000 fun main() = testAwards_nofib(100) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/banner.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module banner with ... val blank = nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" ") :: Nil //│ blank = [[" "," "," "," "," "],[" "," "," "," "," "],[" "," "," "," "," "],[" "," "," "," "," "],[" "," "," "," "," "]] val alphas = (nofibStringToList(" A ") :: nofibStringToList(" A A ") :: nofibStringToList("AAAAA") :: nofibStringToList("A A") :: nofibStringToList("A A") :: Nil) :: (nofibStringToList("BBBB ") :: nofibStringToList("B B") :: nofibStringToList("BBBB ") :: nofibStringToList("B B") :: nofibStringToList("BBBB ") :: Nil) :: (nofibStringToList(" CCCC") :: nofibStringToList("C ") :: nofibStringToList("C ") :: nofibStringToList("C ") :: nofibStringToList(" CCCC") :: Nil) :: (nofibStringToList("DDDD ") :: nofibStringToList("D D") :: nofibStringToList("D D") :: nofibStringToList("D D") :: nofibStringToList("DDDD ") :: Nil) :: (nofibStringToList("EEEEE") :: nofibStringToList("E ") :: nofibStringToList("EEEEE") :: nofibStringToList("E ") :: nofibStringToList("EEEEE") :: Nil) :: (nofibStringToList("FFFFF") :: nofibStringToList("F ") :: nofibStringToList("FFFF ") :: nofibStringToList("F ") :: nofibStringToList("F ") :: Nil) :: (nofibStringToList(" GGGG") :: nofibStringToList("G ") :: nofibStringToList("G GG") :: nofibStringToList("G G") :: nofibStringToList(" GGG ") :: Nil) :: (nofibStringToList("H H") :: nofibStringToList("H H") :: nofibStringToList("HHHHH") :: nofibStringToList("H H") :: nofibStringToList("H H") :: Nil) :: (nofibStringToList("IIIII") :: nofibStringToList(" I ") :: nofibStringToList(" I ") :: nofibStringToList(" I ") :: nofibStringToList("IIIII") :: Nil) :: (nofibStringToList("JJJJJ") :: nofibStringToList(" J ") :: nofibStringToList(" J ") :: nofibStringToList("J J ") :: nofibStringToList(" JJ ") :: Nil) :: (nofibStringToList("K K") :: nofibStringToList("K K ") :: nofibStringToList("KKK ") :: nofibStringToList("K K ") :: nofibStringToList("K K") :: Nil) :: (nofibStringToList("L ") :: nofibStringToList("L ") :: nofibStringToList("L ") :: nofibStringToList("L ") :: nofibStringToList("LLLLL") :: Nil) :: (nofibStringToList("M M") :: nofibStringToList("MM MM") :: nofibStringToList("M M M") :: nofibStringToList("M M") :: nofibStringToList("M M") :: Nil) :: (nofibStringToList("N N") :: nofibStringToList("NN N") :: nofibStringToList("N N N") :: nofibStringToList("N NN") :: nofibStringToList("N N") :: Nil) :: (nofibStringToList(" OOO ") :: nofibStringToList("O O") :: nofibStringToList("O O") :: nofibStringToList("O O") :: nofibStringToList(" OOO ") :: Nil) :: (nofibStringToList("PPPP ") :: nofibStringToList("P P") :: nofibStringToList("PPPP ") :: nofibStringToList("P ") :: nofibStringToList("P ") :: Nil) :: (nofibStringToList(" QQQ ") :: nofibStringToList("Q Q") :: nofibStringToList("Q Q Q") :: nofibStringToList("Q Q ") :: nofibStringToList(" QQ Q") :: Nil) :: (nofibStringToList("RRRR ") :: nofibStringToList("R R") :: nofibStringToList("RRRR ") :: nofibStringToList("R R ") :: nofibStringToList("R R") :: Nil) :: (nofibStringToList(" SSSS") :: nofibStringToList("S ") :: nofibStringToList(" SSS ") :: nofibStringToList(" S") :: nofibStringToList("SSSS ") :: Nil) :: (nofibStringToList("TTTTT") :: nofibStringToList(" T ") :: nofibStringToList(" T ") :: nofibStringToList(" T ") :: nofibStringToList(" T ") :: Nil) :: (nofibStringToList("U U") :: nofibStringToList("U U") :: nofibStringToList("U U") :: nofibStringToList("U U") :: nofibStringToList(" UUU ") :: Nil) :: (nofibStringToList("V V") :: nofibStringToList("V V") :: nofibStringToList("V V") :: nofibStringToList(" V V ") :: nofibStringToList(" V ") :: Nil) :: (nofibStringToList("W W") :: nofibStringToList("W W") :: nofibStringToList("W W") :: nofibStringToList("W W W") :: nofibStringToList(" W W ") :: Nil) :: (nofibStringToList("X X") :: nofibStringToList(" X X ") :: nofibStringToList(" X ") :: nofibStringToList(" X X ") :: nofibStringToList("X X") :: Nil) :: (nofibStringToList("Y Y") :: nofibStringToList(" Y Y ") :: nofibStringToList(" Y ") :: nofibStringToList(" Y ") :: nofibStringToList(" Y ") :: Nil) :: (nofibStringToList("ZZZZZ") :: nofibStringToList(" Z ") :: nofibStringToList(" Z ") :: nofibStringToList(" Z ") :: nofibStringToList("ZZZZZ") :: Nil) :: Nil //│ alphas = [[[" "," ","A"," "," "],[" ","A"," ","A"," "],["A","A","A","A","A"],["A"," "," "," ","A"],["A"," "," "," ","A"]],[["B","B","B","B"," "],["B"," "," "," ","B"],["B","B","B","B"," "],["B"," "," "," ","B"],["B","B","B","B"," "]],[[" ","C","C","C","C"],["C"," "," "," "," "],["C"," "," "," "," "],["C"," "," "," "," "],[" ","C","C","C","C"]],[["D","D","D","D"," "],["D"," "," "," ","D"],["D"," "," "," ","D"],["D"," "," "," ","D"],["D","D","D","D"," "]],[["E","E","E","E","E"],["E"," "," "," "," "],["E","E","E","E","E"],["E"," "," "," "," "],["E","E","E","E","E"]],[["F","F","F","F","F"],["F"," "," "," "," "],["F","F","F","F"," "],["F"," "," "," "," "],["F"," "," "," "," "]],[[" ","G","G","G","G"],["G"," "," "," "," "],["G"," "," ","G","G"],["G"," "," "," ","G"],[" ","G","G","G"," "]],[["H"," "," "," ","H"],["H"," "," "," ","H"],["H","H","H","H","H"],["H"," "," "," ","H"],["H"," "," "," ","H"]],[["I","I","I","I","I"],[" "," ","I"," "," "],[" "," ","I"," "," "],[" "," ","I"," "," "],["I","I","I","I","I"]],[["J","J","J","J","J"],[" "," "," ","J"," "],[" "," "," ","J"," "],["J"," "," ","J"," "],[" ","J","J"," "," "]],[["K"," "," "," ","K"],["K"," "," ","K"," "],["K","K","K"," "," "],["K"," "," ","K"," "],["K"," "," "," ","K"]],[["L"," "," "," "," "],["L"," "," "," "," "],["L"," "," "," "," "],["L"," "," "," "," "],["L","L","L","L","L"]],[["M"," "," "," ","M"],["M","M"," ","M","M"],["M"," ","M"," ","M"],["M"," "," "," ","M"],["M"," "," "," ","M"]],[["N"," "," "," ","N"],["N","N"," "," ","N"],["N"," ","N"," ","N"],["N"," "," ","N","N"],["N"," "," "," ","N"]],[[" ","O","O","O"," "],["O"," "," "," ","O"],["O"," "," "," ","O"],["O"," "," "," ","O"],[" ","O","O","O"," "]],[["P","P","P","P"," "],["P"," "," "," ","P"],["P","P","P","P"," "],["P"," "," "," "," "],["P"," "," "," "," "]],[[" ","Q","Q","Q"," "],["Q"," "," "," ","Q"],["Q"," ","Q"," ","Q"],["Q"," "," ","Q"," "],[" ","Q","Q"," ","Q"]],[["R","R","R","R"," "],["R"," "," "," ","R"],["R","R","R","R"," "],["R"," "," ","R"," "],["R"," "," "," ","R"]],[[" ","S","S","S","S"],["S"," "," "," "," "],[" ","S","S","S"," "],[" "," "," "," ","S"],["S","S","S","S"," "]],[["T","T","T","T","T"],[" "," ","T"," "," "],[" "," ","T"," "," "],[" "," ","T"," "," "],[" "," ","T"," "," "]],[["U"," "," "," ","U"],["U"," "," "," ","U"],["U"," "," "," ","U"],["U"," "," "," ","U"],[" ","U","U","U"," "]],[["V"," "," "," ","V"],["V"," "," "," ","V"],["V"," "," "," ","V"],[" ","V"," ","V"," "],[" "," ","V"," "," "]],[["W"," "," "," ","W"],["W"," "," "," ","W"],["W"," "," "," ","W"],["W"," ","W"," ","W"],[" ","W"," ","W"," "]],[["X"," "," "," ","X"],[" ","X"," ","X"," "],[" "," ","X"," "," "],[" ","X"," ","X"," "],["X"," "," "," ","X"]],[["Y"," "," "," ","Y"],[" ","Y"," ","Y"," "],[" "," ","Y"," "," "],[" "," ","Y"," "," "],[" "," ","Y"," "," "]],[["Z","Z","Z","Z","Z"],[" "," "," ","Z"," "],[" "," ","Z"," "," "],[" ","Z"," "," "," "],["Z","Z","Z","Z","Z"]]] val slant = nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList("") :: Nil //│ slant = [[" "," "," "," "],[" "," "," "],[" "," "],[" "],[]] val punct = [".", nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList(" .. ") :: nofibStringToList(" .. ") :: Nil] :: ["?", nofibStringToList(" ??? ") :: nofibStringToList("? ?") :: nofibStringToList(" ? ") :: nofibStringToList(" ? ") :: nofibStringToList(" . ") :: Nil] :: ["!", nofibStringToList(" ! ") :: nofibStringToList(" ! ") :: nofibStringToList(" ! ") :: nofibStringToList(" ! ") :: nofibStringToList(" . ") :: Nil] :: ["-", nofibStringToList(" ") :: nofibStringToList(" ") :: nofibStringToList("-----") :: nofibStringToList(" ") :: nofibStringToList(" ") :: Nil] :: ["+", nofibStringToList(" + ") :: nofibStringToList(" + ") :: nofibStringToList("+++++") :: nofibStringToList(" + ") :: nofibStringToList(" + ") :: Nil] :: [":", nofibStringToList(" ") :: nofibStringToList(" :: ") :: nofibStringToList(" ") :: nofibStringToList(" :: ") :: nofibStringToList(" ") :: Nil] :: [";", nofibStringToList(" ") :: nofibStringToList(" ;; ") :: nofibStringToList(" ") :: nofibStringToList(" ;; ") :: nofibStringToList(" ;; ") :: Nil] :: Nil //│ punct = [[".", [[" "," "," "," "," "],[" "," "," "," "," "],[" "," "," "," "," "],[" "," ",".","."," "],[" "," ",".","."," "]]],["?", [[" ","?","?","?"," "],["?"," "," "," ","?"],[" "," "," ","?"," "],[" "," ","?"," "," "],[" "," ","."," "," "]]],["!", [[" "," ","!"," "," "],[" "," ","!"," "," "],[" "," ","!"," "," "],[" "," ","!"," "," "],[" "," ","."," "," "]]],["-", [[" "," "," "," "," "],[" "," "," "," "," "],["-","-","-","-","-"],[" "," "," "," "," "],[" "," "," "," "," "]]],["+", [[" "," ","+"," "," "],[" "," ","+"," "," "],["+","+","+","+","+"],[" "," ","+"," "," "],[" "," ","+"," "," "]]],[":", [[" "," "," "," "," "],[" "," ",":",":"," "],[" "," "," "," "," "],[" "," ",":",":"," "],[" "," "," "," "," "]]],[";", [[" "," "," "," "," "],[" "," ",";",";"," "],[" "," "," "," "," "],[" "," ",";",";"," "],[" ",";",";"," "," "]]]] val digits = (nofibStringToList(" OOO ") :: nofibStringToList("0 00") :: nofibStringToList("0 0 0") :: nofibStringToList("00 0") :: nofibStringToList(" 000 ") :: Nil) :: (nofibStringToList(" 1 ") :: nofibStringToList(" 11 ") :: nofibStringToList(" 1 ") :: nofibStringToList(" 1 ") :: nofibStringToList("11111") :: Nil) :: (nofibStringToList(" 222 ") :: nofibStringToList("2 2") :: nofibStringToList(" 2 ") :: nofibStringToList(" 2 ") :: nofibStringToList("22222") :: Nil) :: (nofibStringToList("3333 ") :: nofibStringToList(" 3") :: nofibStringToList(" 333 ") :: nofibStringToList(" 3") :: nofibStringToList("3333 ") :: Nil) :: (nofibStringToList(" 4 ") :: nofibStringToList(" 44 ") :: nofibStringToList(" 4 4 ") :: nofibStringToList("44444") :: nofibStringToList(" 4 ") :: Nil) :: (nofibStringToList("55555") :: nofibStringToList("5 ") :: nofibStringToList("5555 ") :: nofibStringToList(" 5") :: nofibStringToList("5555 ") :: Nil) :: (nofibStringToList(" 66") :: nofibStringToList(" 6 ") :: nofibStringToList(" 666 ") :: nofibStringToList("6 6") :: nofibStringToList(" 666 ") :: Nil) :: (nofibStringToList("77777") :: nofibStringToList(" 7") :: nofibStringToList(" 7 ") :: nofibStringToList(" 7 ") :: nofibStringToList(" 7 ") :: Nil) :: (nofibStringToList(" 888 ") :: nofibStringToList("8 8") :: nofibStringToList(" 888 ") :: nofibStringToList("8 8") :: nofibStringToList(" 888 ") :: Nil) :: (nofibStringToList(" 999 ") :: nofibStringToList("9 9") :: nofibStringToList(" 999 ") :: nofibStringToList(" 9 ") :: nofibStringToList("99 ") :: Nil) :: Nil //│ digits = [[[" ","O","O","O"," "],["0"," "," ","0","0"],["0"," ","0"," ","0"],["0","0"," "," ","0"],[" ","0","0","0"," "]],[[" "," ","1"," "," "],[" ","1","1"," "," "],[" "," ","1"," "," "],[" "," ","1"," "," "],["1","1","1","1","1"]],[[" ","2","2","2"," "],["2"," "," "," ","2"],[" "," "," ","2"," "],[" "," ","2"," "," "],["2","2","2","2","2"]],[["3","3","3","3"," "],[" "," "," "," ","3"],[" ","3","3","3"," "],[" "," "," "," ","3"],["3","3","3","3"," "]],[[" "," "," ","4"," "],[" "," ","4","4"," "],[" ","4"," ","4"," "],["4","4","4","4","4"],[" "," "," ","4"," "]],[["5","5","5","5","5"],["5"," "," "," "," "],["5","5","5","5"," "],[" "," "," "," ","5"],["5","5","5","5"," "]],[[" "," "," ","6","6"],[" "," ","6"," "," "],[" ","6","6","6"," "],["6"," "," "," ","6"],[" ","6","6","6"," "]],[["7","7","7","7","7"],[" "," "," "," ","7"],[" "," "," ","7"," "],[" "," "," ","7"," "],[" "," ","7"," "," "]],[[" ","8","8","8"," "],["8"," "," "," ","8"],[" ","8","8","8"," "],["8"," "," "," ","8"],[" ","8","8","8"," "]],[[" ","9","9","9"," "],["9"," "," "," ","9"],[" ","9","9","9"," "],[" "," ","9"," "," "],["9","9"," "," "," "]]] fun unlines(ls) = concat(map(x => x +: ("\n" :: Nil), ls)) fun join(ls) = foldr1((xs, ys) => xs +: (" " :: " " :: Nil) +: ys, ls) fun isUpper(c) = let n = int_of_char(c) (n >= 65) and (n <= 90) fun isLower(c) = let n = int_of_char(c) (n >= 97) and (n <= 122) fun isDigit(c) = let n = int_of_char(c) (n >= 48) and (n <= 57) fun isSpace(c) = let n = int_of_char(c) (n == 32) fun picChar(c) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is [c_, letter] and c_ === c then letter :: lscomp(t) else lscomp(t) if isUpper(c) then atIndex(int_of_char(c) - int_of_char("A"), alphas) isLower(c) then atIndex(int_of_char(c) - int_of_char("a"), alphas) isSpace(c) then blank isDigit(c) then atIndex(int_of_char(c) - int_of_char("0"), digits) c === "/" then slant c === "=" then reverse(slant) else head(lscomp(punct) +: Nil :: Nil :: Nil) fun say(s) = "\n" :: (unlines(map(join, transpose(map(picChar, s))))) fun testBanner_nofib(n) = let x = nofibStringToList("Is this not a great banner?") say(concat(replicate(n, x))) fun main() = nofibListToString(testBanner_nofib(1)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/boyer.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module boyer with ... abstract class Id: A | B | C | D | X | Y | Z | U | W | ADD1 | AND | APPEND | CONS | CONSP | DIFFERENCE | DIVIDES | EQUAL | EVEN | EXP | F | FALSE | FOUR | GCD | GREATEREQP | GREATERP | IF | IFF | IMPLIES | LENGTH | LESSEQP | LESSP | LISTP | MEMBER | NIL | NILP | NLISTP | NOT | ODD | ONE | OR | PLUS | QUOTIENT | REMAINDER | REVERSE | SUB1 | TIMES | TRUE | TWO | ZERO | ZEROP object A extends Id B extends Id C extends Id D extends Id X extends Id Y extends Id Z extends Id U extends Id W extends Id ADD1 extends Id AND extends Id APPEND extends Id CONS extends Id CONSP extends Id DIFFERENCE extends Id DIVIDES extends Id EQUAL extends Id EVEN extends Id EXP extends Id F extends Id FALSE extends Id FOUR extends Id GCD extends Id GREATEREQP extends Id GREATERP extends Id IF extends Id IFF extends Id IMPLIES extends Id LENGTH extends Id LESSEQP extends Id LESSP extends Id LISTP extends Id MEMBER extends Id NIL extends Id NILP extends Id NLISTP extends Id NOT extends Id ODD extends Id ONE extends Id OR extends Id PLUS extends Id QUOTIENT extends Id REMAINDER extends Id REVERSE extends Id SUB1 extends Id TIMES extends Id TRUE extends Id TWO extends Id ZERO extends Id ZEROP extends Id abstract class Term: Var | Fun | ERROR data class Var(i: Id) extends Term Fun(i: Id, t: List[Term], l: [Term, Term]) extends Term object ERROR extends Term //│ ———————————————————————————————————————————————————————————————————————————————— fun termLsEq(h1t1, h2t2) = if h1t1 is h1 :: t1 and h2t2 is h2 :: t2 and termEq(h1, h2) then termLsEq(t1, t2) else false else true fun termEq(t1, t2) = if t1 is Var(i1) and t2 is Var(i2) then i1 === i2 t1 is Fun(f1, ts1, _) and t2 is Fun(f2, ts2, _) then (f1 === f2) and termLsEq(ts1, ts2) else false //│ ———————————————————————————————————————————————————————————————————————————————— fun termInList(term, ht) = if ht is h :: t and termEq(term, h) then true else termInList(term, t) Nil then false fun find(vid, ls) = if ls is Nil then [false, ERROR] [vid2, val2] :: bs and vid === vid2 then [true, val2] else find(vid, bs) //│ ———————————————————————————————————————————————————————————————————————————————— fun one_way_unify1(term1, term2, subst) = if term2 is Var(vid2) and find(vid2, subst) is [found, v2] and found then [termEq(term1, v2), subst] else [true, [vid2, term1] :: subst] term1 is Fun(f1, as1, _) and term2 is Fun(f2, as2, _) and f1 === f2 then one_way_unify1_lst(as1, as2, subst) else [false, Nil] else [false, Nil] fun one_way_unify1_lst(tts1, tts2, subst) = if tts1 is Nil and tts2 is Nil then [true, subst] tts1 is t1 :: ts1 and tts2 is t2 :: ts2 and one_way_unify1(t1, t2, subst) is [hd_ok, subst_] and one_way_unify1_lst(ts1, ts2, subst_) is [tl_ok, subst__] then [hd_ok and tl_ok, subst__] else [false, Nil] //│ ———————————————————————————————————————————————————————————————————————————————— fun one_way_unify(term1, term2) = one_way_unify1(term1, term2, Nil) fun apply_subst(subst, t) = if t is Var(vid) and find(vid, subst) is [found, value] and found then value else Var(vid) Fun(f, args, ls) then Fun(f, map(x => apply_subst(subst, x), args), ls) //│ ———————————————————————————————————————————————————————————————————————————————— fun rewrite_with_lemmas_helper(term, lss) = if lss is Nil then term [lhs, rhs] :: ls and one_way_unify(term, lhs) is [unified, subst] and unified then rewrite(apply_subst(subst, rhs)) else rewrite_with_lemmas_helper(term, ls) fun rewrite_with_lemmas(term, lss) = rewrite_with_lemmas_helper(term, force(lss)) fun rewrite(t) = if t is Var(v) then Var(v) Fun(f, args, lemmas) then rewrite_with_lemmas(Fun(f, map(rewrite, args), lemmas), lemmas) //│ ———————————————————————————————————————————————————————————————————————————————— fun truep(x, l) = if x is Fun(TRUE, _, _) then true else termInList(x, l) fun falsep(x, l) = if x is Fun(FALSE, _, _) then true else termInList(x, l) fun tautologyp(x, true_lst, false_lst) = if truep(x, true_lst) then true falsep(x, false_lst) then false x is Fun(IF, cond :: t :: e :: Nil, _) and truep(cond, true_lst) then tautologyp(t, true_lst, false_lst) falsep(cond, false_lst) then tautologyp(e, true_lst, false_lst) else tautologyp(t, cond :: true_lst, false_lst) and tautologyp(e, true_lst, cond :: false_lst) else false fun tautp(x) = tautologyp(rewrite(x), Nil, Nil) fun test0(xxxx) = let a = Var(A) let b = Var(B) let c = Var(C) let d = Var(D) let u = Var(U) let w = Var(W) let x = Var(X) let y = Var(Y) let z = Var(Z) let boyerFalse = Fun(FALSE, Nil, lazy of () => Nil) let nil = Fun(NIL , Nil, lazy of () => Nil) let boyerTrue = Fun(TRUE , Nil, lazy of () => Nil) let zero = Fun(ZERO , Nil, lazy of () => Nil) fun one() = Fun(ONE, Nil, lazy of () => [one(), add1(zero)] :: Nil) fun two() = Fun(TWO, Nil, lazy of () => [two(), add1(one())] :: Nil) fun four() = Fun(FOUR, Nil, lazy of () => [four(), add1(add1(two()))] :: Nil) fun add1(a) = Fun(ADD1, a :: Nil, lazy of () => Nil) fun if_(a, b, c) = Fun(IF, a :: b :: c :: Nil, lazy of () => [if_(if_(x, y, z), u, w), if_(x, if_(y, u, w), if_(z, u, w))] :: Nil) fun not_(a) = Fun(NOT, a :: Nil, lazy of () => [not_(x), if_(x, boyerFalse, boyerTrue)] :: Nil) fun and_(a, b) = Fun(AND, a :: b :: Nil, lazy of () => [and_(x, y), if_(x, if_(y, boyerTrue, boyerFalse), boyerFalse)] :: Nil) fun append_(a, b) = Fun(APPEND, a :: b :: Nil, lazy of () => [append_(append_(x, y), z), append_(x, append_(y, z))] :: Nil) fun cons(a, b) = Fun(CONS, a :: b :: Nil, lazy of () => Nil) fun consp(a) = Fun(CONSP, a :: Nil, lazy of () => [consp(cons(x, y)), boyerTrue] :: Nil) fun difference(a, b) = Fun(DIFFERENCE, a :: b :: Nil, lazy of () => [difference(x, x), zero] :: [difference(plus(x, y), x), y] :: [difference(plus(y, x), x), y] :: [difference(plus(x, y), plus(x, z)), difference(y, z)] :: [difference(plus(y, plus(x, z)), x), plus(y, z)] :: [difference(add1(plus(y, z)), z), add1(y)] :: [difference(add1(add1(x)), two()), x] :: Nil) fun divides(a, b) = Fun(DIVIDES, a :: b :: Nil, lazy of () => [divides(x, y), zerop(remainder(y, x))] :: Nil) fun equal(a, b) = Fun( EQUAL, a :: b :: Nil, lazy of () => [equal(plus(x, y), zero), and_(zerop(x), zerop(y))] :: [equal(plus(x, y), plus(x, z)), equal(y, z)] :: [equal(zero, difference(x, y)), not_(lessp(y, x))] :: [equal(x, difference(x, y)), or_(equal(x, zero), zerop(y))] :: [equal(times(x, y), zero), or_(zerop(x), zerop(y))] :: [equal(append_(x, y), append_(x, z)), equal(y, z)] :: [equal(y, times(x, y)), or_(equal(y, zero), equal(x, one()))] :: [equal(x, times(x, y)), or_(equal(x, zero), equal(y, one()))] :: [equal(times(x, y), one()), and_(equal(x, one()), equal(y, one()))] :: [equal(difference(x, y), difference(z, y)), if_(lessp(x, y), not_(lessp(y, z)), if_(lessp(z, y), not_(lessp(y, x)), equal(x, z)))] :: [equal(lessp(x, y), z), if_(lessp(x, y), equal(boyerTrue, z), equal(boyerFalse, z))] :: Nil ) fun even_(a) = Fun(EVEN, a :: Nil, lazy of () => [even_(x), if_(zerop(x), boyerTrue, odd_(sub1(x)))] :: Nil) fun exp_(a, b) = Fun(EXP, a :: b :: Nil, lazy of () => [exp_(x, plus(y, z)), times(exp_(x, y), exp_(x, z))] :: [exp_(x, times(y, z)), exp_(exp_(x, y), z)] :: Nil) fun f(a) = Fun(F, a :: Nil, lazy of () => Nil) fun gcd_(a, b) = Fun(GCD, a :: b :: Nil, lazy of () => [gcd_(x, y), gcd_(y, x)] :: [gcd_(times(x, z), times(y, z)), times(z, gcd_(x, y))] :: Nil) fun greatereqp(a, b) = Fun(GREATEREQP, a :: b :: Nil, lazy of () => [greatereqp(x, y), not_(lessp(x, y))] :: Nil) fun greaterp(a, b) = Fun(GREATERP, a :: b :: Nil, lazy of () => [greaterp(x, y), lessp(y, x)] :: Nil) fun implies(a, b) = Fun(IMPLIES, a :: b :: Nil, lazy of () => [implies(x, y), if_(x, if_(y, boyerTrue, boyerFalse), boyerTrue)] :: Nil) fun iff(a, b) = Fun(IFF, a :: b :: Nil, lazy of () => [iff(x, y), and_(implies(x, y), implies(y, x))] :: Nil) fun length_(a) = Fun(LENGTH, a :: Nil, lazy of () => [length_(reverse_(x)), length_(x)] :: [length_(cons(x, cons(y, cons(z, cons(u, w))))), plus(four(), length_(w))] :: Nil) fun lesseqp(a, b) = Fun(LESSEQP, a :: b :: Nil, lazy of () => [lesseqp(x, y), not_(lessp(y, x))] :: Nil) fun lessp(a, b) = Fun( LESSP, a :: b :: Nil, lazy of () => [lessp(remainder(x, y), y), not_(zerop(y))] :: [lessp(quotient(x, y), x), and_(not_(zerop(x)), lessp(one(), y))] :: [lessp(plus(x, y), plus(x, z)), lessp(y, z)] :: [lessp(times(x, z), times(y, z)), and_(not_(zerop(z)), lessp(x, y))] :: [lessp(y, plus(x, y)), not_(zerop(x))] :: Nil ) fun nilp(a) = Fun(NILP, a :: Nil, lazy of () => [nilp(x), equal(x, nil)] :: Nil) fun listp(a) = Fun(LISTP, a :: Nil, lazy of () => [listp(x), or_(nilp(x), consp(x))] :: Nil) fun member(a, b) = Fun(MEMBER, a :: b :: Nil, lazy of () => [member(x, append_(y, z)), or_(member(x, y), member(x, z))] :: [member(x, reverse_(y)), member(x, y)] :: Nil) fun nlistp(a) = Fun(NLISTP, a :: Nil, lazy of () => [nlistp(x), not_(listp(x))] :: Nil) fun odd_(a) = Fun(ODD, a :: Nil, lazy of () => [odd_(x), even_(sub1(x))] :: Nil) fun or_(a, b) = Fun(OR, a :: b :: Nil, lazy of () => [or_(x, y), if_(x, boyerTrue, if_(y, boyerTrue, boyerFalse))] :: Nil) fun plus(a, b) = Fun(PLUS, a :: b :: Nil, lazy of () => [plus(plus(x, y), z), plus(x, plus(y, z))] :: [plus(remainder(x, y), times(y, quotient(x, y))), x] :: [plus(x, add1(y)), add1(plus(x, y))] :: Nil) fun quotient(a, b) = Fun(QUOTIENT, a :: b :: Nil, lazy of () => [quotient(plus(x, plus(x, y)), two()), plus(x, quotient(y, two()))] :: [quotient(times(y, x), y), if_(zerop(y), zero, x)] :: Nil) fun remainder(a, b) = Fun(REMAINDER, a :: b :: Nil, lazy of () => [remainder(x, one()), zero] :: [remainder(x, x), zero] :: [remainder(times(x, y), x), zero] :: [remainder(times(x, y), y), zero] :: Nil) fun reverse_(a) = Fun(REVERSE, a :: Nil, lazy of () => [reverse_(append_(x, y)), append_(reverse_(y), reverse_(x))] :: Nil) fun sub1(a) = Fun(SUB1, a :: Nil, lazy of () => [sub1(add1(x)), x] :: Nil) fun times(a, b) = Fun(TIMES, a :: b :: Nil, lazy of () => [times(x, plus(y, z)), plus(times(x, y), times(x, z))] :: [times(times(x, y), z), times(x, times(y, z))] :: [times(x, difference(y, z)), difference(times(y, x), times(z, x))] :: [times(x, add1(y)), plus(x, times(x, y))] :: Nil) fun zerop(a) = Fun(ZEROP, a :: Nil, lazy of () => [zerop(x), equal(x, zero)] :: Nil) let subst0 = [X, f(plus(plus(a, b), plus(c, zero)))] :: [Y, f(times(times(a, b), plus(c, d)))] :: [Z, f(reverse_(append_(append_(a, b), nil)))] :: [U, equal(plus(a, b), difference(x, y))] :: [W, lessp(remainder(a, b), member(a, length_(b)))] :: Nil let theorem = implies(and_(implies(xxxx, y), and_(implies(y, z), and_(implies(z, u), implies(u, w)))), implies(x, w)) tautp(apply_subst(subst0, theorem)) fun testBoyer_nofib(n) = all(test0, replicate(n, Var(X))) fun main() = testBoyer_nofib(5) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/boyer2.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module boyer2 with ... abstract class Lisplist: Nill | Atom | Conss object Nill extends Lisplist data class Atom(a: List[Char]) extends Lisplist Conss(a: [Lisplist, Lisplist]) extends Lisplist fun lispListEq(x, y) = if x is Nill and y is Nill then true Atom(a) and y is Atom(b) then listEq(a, b) Conss([a, b]) and y is Conss([c, d]) and (lispListEq(a, c)) then lispListEq(b, d) else false fun lispmember(e_x) = if e_x is [e, Conss([x, xs])] and lispListEq(e, x) then true else lispmember([e, xs]) [_, _] then false fun truep(term_l) = if term_l is [Nill, _] then false [Conss([Atom("t" :: Nil), Nill]), _] then true [term, l] then lispmember([term, l]) fun falsep(term_l) = if term_l is [Nill, _] then false [Conss([Atom("f" :: Nil), Nill]), _] then true [term, l] then lispmember([term, l]) fun tv(x) = if x is Atom(a) then a fun atom(x) = if x is Atom(_) then true else false fun car(x) = if x is Conss([a, _]) then a else Nill fun cdr(x) = if x is Conss([_, b]) then b else Nill fun cadr(x) = car(cdr(x)) fun caddr(x) = car(cdr(cdr(x))) fun cadddr(x) = car(cdr(cdr(cdr(x)))) fun tautologyp(f_truelst_falselst) = if f_truelst_falselst is [f, truelst, falselst] and f is Nill then false Atom(x) then truep([Atom(x), truelst]) Conss([x, y]) and truep([Conss([x, y]), truelst]) then true falsep([Conss([x, y]), falselst]) then false x is Atom("i" :: "f" :: Nil) and truep([car(y), truelst]) then tautologyp([cadr(y), truelst, falselst]) falsep([car(y), falselst]) then tautologyp([caddr(y), truelst, falselst]) else tautologyp([cadr(y), Conss([car(y), truelst]), falselst]) and tautologyp([caddr(y), truelst, Conss([car(y), falselst])]) else false fun sublist(t) = if t is Nil then [Nil, Nill] ("(" :: Nil) :: t and sublist(t) is [r1, l1] and sublist(r1) is [r2, l2] then [r2, Conss([l1, l2])] (")" :: Nil) :: t then [t, Nill] h :: t and sublist(t) is [r, l] then [r, Conss([Atom(h), l])] fun mkLispList(ls) = if ls is ("(" :: Nil) :: t and sublist(t) is [r, l] and r is Nil then l else Nill _ then Nill fun restOfToken(s) = if s is Nil then [Nil, Nil] h :: t and (h === "(") || (h === ")") || (h === " ") then [Nil, h :: t] restOfToken(t) is [a, b] then [h :: a, b] fun getToken(s) = if s is Nil then [Nil, Nil] h :: t and (h === " ") then getToken(t) (h === "(") || (h === ")") then [h :: Nil, t] boyer2.restOfToken(t) is [a, b] then [h :: a, b] fun strToToken(s) = if s is Nil then Nil getToken(s) is [a, b] then a :: strToToken(b) fun assoc(term_x_y) = if term_x_y is [term, Conss([x, y])] and x is Conss([Atom(key), rest]) and lispListEq(term, Atom(key)) then x else assoc([term, y]) _ then Nill [_, _] then Nill type LUTentry = [String, List[Lisplist]] abstract class LUT: Empty | Node object Empty extends LUT data class Node(x: [LUT, LUTentry, LUT]) extends LUT fun addtoLUT(k_l_lut) = if k_l_lut is [k, l, boyer2.Empty] then boyer2.Node([boyer2.Empty, [k, l :: Nil], boyer2.Empty]) [k, l, boyer2.Node([left, [k1, kl], right])] and listEq(k, k1) then boyer2.Node([left, [k1, l :: kl], right]) ltList(k, k1, (x, y) => x < y, (x, y) => x > y) then boyer2.Node([addtoLUT([k, l, left]), [k1, kl], right]) else boyer2.Node([left, [k1, kl], addtoLUT([k, l, right])]) fun getLUT(t_lut) = if t_lut is [t, Empty] then Nil [t, Node([left, [k, kl], right])] and listEq(t, k) then kl ltList(t, k, (x, y) => x < y, (x, y) => x > y) then getLUT([t, left]) else getLUT([t, right]) fun makelemmas(rules) = if rules is Nil then Nil h :: t then boyer2.mkLispList(strToToken(h)) :: makelemmas(t) fun addlemma(lspls, term) = if lspls is boyer2.Nill then term boyer2.Atom(x) then throw new Error("error") boyer2.Conss([x, y]) then let z = boyer2.car(y) if listEq(boyer2.tv(x), nofibStringToList("equal")) and (not(boyer2.atom(z))) then addtoLUT([boyer2.tv(boyer2.car(z)), boyer2.Conss([x, y]), term]) else throw new Error("error") fun addlemmalst(lspls, term) = if lspls is Nil then term h :: t then addlemmalst(t, boyer2.addlemma(h, term)) //│ ———————————————————————————————————————————————————————————————————————————————— fun applysubstlst(alist, y) = if y is Nill then Nill Atom(x) then throw new Error("error") Conss([x, y]) then Conss([applysubst(alist, x), applysubstlst(alist, y)]) fun applysubst(alist, x) = if x is Nill then Nill Atom(x) and assoc([Atom(x), alist]) is Conss([_, y]) then y else Atom(x) Conss([x, y]) then Conss([x, applysubstlst(alist, y)]) //│ ———————————————————————————————————————————————————————————————————————————————— //│ ———————————————————————————————————————————————————————————————————————————————— fun onewayunify1lst(l1, l2, u) = if l1 is Nill then [true, u] onewayunify1(car(l1), car(l2), u) is [b, u1] and b then onewayunify1lst(cdr(l1), cdr(l2), u1) else [false, u1] fun onewayunify1(t1, t2, u) = if atom(t2) and assoc([t2, u]) is Conss([_, y]) then [lispListEq(t1, y), u] else [true, Conss([Conss([t2, t1]), u])] atom(t1) then [false, u] lispListEq(car(t1), car(t2)) then onewayunify1lst(cdr(t1), cdr(t2), u) else [false, u] //│ ———————————————————————————————————————————————————————————————————————————————— fun onewayunify(t1, t2) = onewayunify1(t1, t2, Nill) //│ ———————————————————————————————————————————————————————————————————————————————— fun rewritewithlemmas(t, l, term) = if l is Nil then t lh :: lt and onewayunify(t, cadr(lh)) is [b, u] and b then rewrite(applysubst(u, caddr(lh)), term) else rewritewithlemmas(t, lt, term) fun rewriteargs(x, term) = if x is Nill then Nill Atom(_) then throw new Error("error") Conss([x, y]) then Conss([rewrite(x, term), rewriteargs(y, term)]) fun rewrite(x, term) = if x is Nill then Nill Atom(x) then Atom(x) Conss([l1, l2]) then rewritewithlemmas(Conss([l1, rewriteargs(l2, term)]), getLUT([tv(l1), term]), term) //│ ———————————————————————————————————————————————————————————————————————————————— val statement = boyer2.mkLispList(strToToken( nofibStringToList("( implies ( and ( implies x y )( and ( implies y z )( and ( implies z u )( implies u w ) ) ) )( implies x w ) )") )) //│ statement = Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["z"]), Conss([Atom(["u"]), Nill])])]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["u"]), Conss([Atom(["w"]), Nill])])]), Nill])])]), Nill])])]), Nill])])]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["x"]), Conss([Atom(["w"]), Nill])])]), Nill])])]) fun subterm(i) = let c = stringConcat("c", stringOfInt(i)) let str = stringConcat( "( ( x f ( plus ( plus a b )( plus ", stringConcat( c, stringConcat( " ( zero ) ) ) )( y f ( times ( times a b )( plus ", stringConcat(c, " d ) ) )( z f ( reverse ( append ( append a b ) ( [] ) ) ) )(u equal ( plus a b ) ( difference x y ) )(w lessp ( remainder a b )( member a ( length b ) ) ) )") ) ) ) mkLispList(strToToken(nofibStringToList(str))) fun report(b) = if b then "The term is a tautology" else "The term is not a tautology" val rules = nofibStringToList("(equal (compile form)(reverse (codegen (optimize form) (Nill) ) ) )") :: nofibStringToList("(equal (eqp x y)(equal (fix x)(fix y) ) )") :: nofibStringToList("(equal (greaterp x y)(lessp y x) )") :: nofibStringToList("(equal (lesseqp x y)(not (lessp y x) ) )") :: nofibStringToList("(equal (greatereqp x y)(not (lessp y x) ) )") :: nofibStringToList("(equal (boolean x)(or (equal x (t) )(equal x (f) ) )") :: nofibStringToList("(equal (iff x y)(and (implies x y)(implies y x) ) )") :: nofibStringToList("(equal (even1 x)(if (zerop x)(t)(odd (1- x) ) ) )") :: nofibStringToList("(equal (countps- l pred)(countps-loop l pred (zero) ) )") :: nofibStringToList("(equal (fact- i)(fact-loop i 1) )") :: nofibStringToList("(equal (reverse- x)(reverse-loop x (Nill) ) )") :: nofibStringToList("(equal (divides x y)(zerop (remainder y x) ) )") :: nofibStringToList("(equal (assume-true var alist)(Conss (Conss var (t) )alist) )") :: nofibStringToList("(equal (assume-false var alist)(Conss (Conss var (f) )alist) )") :: nofibStringToList("(equal (tautology-checker x)(tautologyp (normalize x)(Nill) ) )") :: nofibStringToList("(equal (falsify x)(falsify1 (normalize x)(Nill) ) )") :: nofibStringToList("(equal (prime x)(and (not (zerop x))(not (equal x (add1 (zero) ) ) )(prime1 x (1- x) ) ) )") :: nofibStringToList("(equal (and p q)(if p (if q (t) (f) ) (f) ) )") :: nofibStringToList("(equal (or p q)(if p (t) (if q (t) (f) ) ) )") :: nofibStringToList("(equal (not p)(if p (f) (t) ) )") :: nofibStringToList("(equal (implies p q)(if p (if q (t) (f) ) (t) ) )") :: nofibStringToList("(equal (fix x)(if (numberp x) x (zero) ) )") :: nofibStringToList("(equal (if (if a b c) d e)(if a (if b d e) (if c d e) ) )") :: nofibStringToList("(equal (zerop x)(or (equal x (zero) )(not (numberp x) ) ) )") :: nofibStringToList("(equal (plus (plus x y) z )(plus x (plus y z) ) )") :: nofibStringToList("(equal (equal (plus a b) (zero ) )(and (zerop a) (zerop b) ) )") :: nofibStringToList("(equal (difference x x)(zero) )") :: nofibStringToList("(equal (equal (plus a b) (plus a c) )(equal (fix b) (fix c) ) )") :: nofibStringToList("(equal (equal (zero) (difference x y) )(not (lessp y x) ) )") :: nofibStringToList("(equal (equal x (difference x y) )(and (numberp x)(or (equal x (zero) )(zerop y) ) ) )") :: nofibStringToList("(equal (meaning (plus-tree (append x y) ) a)(plus (meaning (plus-tree x) a)(meaning (plus-tree y) a) ) )") :: nofibStringToList("(equal (meaning (plus-tree (plus-fringe x) ) a)(fix (meaning x a) ) )") :: nofibStringToList("(equal (append (append x y) z)(append x (append y z) ) )") :: nofibStringToList("(equal (reverse (append a b) )(append (reverse b) (reverse a) ) )") :: nofibStringToList("(equal (times x (plus y z) )(plus (times x y)(times x z) ) )") :: nofibStringToList("(equal (times (times x y) z)(times x (times y z) ) )") :: nofibStringToList("(equal (equal (times x y) (zero) )(or (zerop x)(zerop y) ) )") :: nofibStringToList("(equal (exec (append x y)pds envrn)(exec y (exec x pds envrn)envrn) )") :: nofibStringToList("(equal (mc-flatten x y)(append (flatten x)y) )") :: nofibStringToList("(equal (member x (append a b) )(or (member x a)(member x b) ) )") :: nofibStringToList("(equal (member x (reverse y) )(member x y) )") :: nofibStringToList("(equal (length (reverse x) )(length x) )") :: nofibStringToList("(equal (member a (intersect b c) )(and (member a b)(member a c) ) )") :: nofibStringToList("(equal (nth (zero)i)(zero) )") :: nofibStringToList("(equal (exp i (plus j k) )(times (exp i j)(exp i k) ) )") :: nofibStringToList("(equal (exp i (times j k) )(exp (exp i j)k) )") :: nofibStringToList("(equal (reverse-loop x y)(append (reverse x)y) )") :: nofibStringToList("(equal (reverse-loop x (Nill) )(reverse x) )") :: nofibStringToList("(equal (count-list z (sort-lp x y) )(plus (count-list z x)(count-list z y) ) )") :: nofibStringToList("(equal (equal (append a b)(append a c) )(equal b c) )") :: nofibStringToList("(equal (plus (remainder x y)(times y (quotient x y) ) )(fix x) )") :: nofibStringToList("(equal (power-eval (big-plus1 l i base)base)(plus (power-eval l base)i) )") :: nofibStringToList("(equal (power-eval (big-plus x y i base)base)(plus i (plus (power-eval x base)(power-eval y base) ) ) )") :: nofibStringToList("(equal (remainder y 1)(zero) )") :: nofibStringToList("(equal (lessp (remainder x y)y)(not (zerop y) ) )") :: nofibStringToList("(equal (remainder x x)(zero) )") :: nofibStringToList("(equal (lessp (quotient i j)i)(and (not (zerop i) )(or (zerop j)(not (equal j 1) ) ) ) )") :: nofibStringToList("(equal (lessp (remainder x y)x)(and (not (zerop y) )(not (zerop x) )(not (lessp x y) ) ) )") :: nofibStringToList("(equal (power-eval (power-rep i base)base)(fix i) )") :: nofibStringToList("(equal (power-eval (big-plus (power-rep i base)(power-rep j base)(zero)base)base)(plus i j) )") :: nofibStringToList("(equal (gcd x y)(gcd y x) )") :: nofibStringToList("(equal (nth (append a b)i)(append (nth a i)(nth b (difference i (length a) ) ) ) )") :: nofibStringToList("(equal (difference (plus x y)x)(fix y) )") :: nofibStringToList("(equal (difference (plus y x)x)(fix y) )") :: nofibStringToList("(equal (difference (plus x y)(plus x z) )(difference y z) )") :: nofibStringToList("(equal (times x (difference c w) )(difference (times c x)(times w x) ) )") :: nofibStringToList("(equal (remainder (times x z)z)(zero) )") :: nofibStringToList("(equal (difference (plus b (plus a c) )a)(plus b c) )") :: nofibStringToList("(equal (difference (add1 (plus y z)z)(add1 y) )") :: nofibStringToList("(equal (lessp (plus x y)(plus x z ) )(lessp y z) )") :: nofibStringToList("(equal (lessp (times x z)(times y z) )(and (not (zerop z) )(lessp x y) ) )") :: nofibStringToList("(equal (lessp y (plus x y) )(not (zerop x) ) )") :: nofibStringToList("(equal (gcd (times x z)(times y z) )(times z (gcd x y) ) )") :: nofibStringToList("(equal (value (normalize x)a)(value x a) )") :: nofibStringToList("(equal (equal (flatten x)(Conss y (Nill) ) )(and (nlistp x)(equal x y) ) )") :: nofibStringToList("(equal (listp (gopher x) )(listp x) )") :: nofibStringToList("(equal (samefringe x y)(equal (flatten x)(flatten y) ) )") :: nofibStringToList("(equal (equal (greatest-factor x y)(zero) )(and (or (zerop y)(equal y 1) )(equal x (zero) ) ) )") :: nofibStringToList("(equal (equal (greatest-factor x y)1)(equal x 1) )") :: nofibStringToList("(equal (numberp (greatest-factor x y) )(not (and (or (zerop y)(equal y 1) )(not (numberp x) ) ) ) )") :: nofibStringToList("(equal (times-list (append x y) )(times (times-list x)(times-list y) ) )") :: nofibStringToList("(equal (prime-list (append x y) )(and (prime-list x)(prime-list y) ) )") :: nofibStringToList("(equal (equal z (times w z) )(and (numberp z)(or (equal z (zero) )(equal w 1) ) ) )") :: nofibStringToList("(equal (greatereqpr x y)(not (lessp x y) ) )") :: nofibStringToList("(equal (equal x (times x y) )(or (equal x (zero) )(and (numberp x)(equal y 1) ) ) )") :: nofibStringToList("(equal (remainder (times y x)y)(zero) )") :: nofibStringToList("(equal (equal (times a b)1)(and (not (equal a (zero) ) )(not (equal b (zero) ) )(numberp a)(numberp b)(equal (1- a)(zero) )(equal (1- b)(zero) ) ) )") :: nofibStringToList("(equal (lessp (length (delete x l) )(length l) )(member x l) )") :: nofibStringToList("(equal (sort2 (delete x l) )(delete x (sort2 l) ) )") :: nofibStringToList("(equal (dsort x)(sort2 x) )") :: nofibStringToList("(equal (length(Conss x1(Conss x2(Conss x3(Conss x4(Conss x5(Conss x6 x7) ) ) ) ) ) )(plus 6 (length x7) ) )") :: nofibStringToList("(equal (difference (add1 (add1 x) )2)(fix x) )") :: nofibStringToList("(equal (quotient (plus x (plus x y) )2)(plus x (quotient y 2) ) )") :: nofibStringToList("(equal (sigma (zero)i)(quotient (times i (add1 i) )2) )") :: nofibStringToList("(equal (plus x (add1 y) )(if (numberp y)(add1 (plus x y) )(add1 x) ) )") :: nofibStringToList("(equal (equal (difference x y)(difference z y) )(if (lessp x y)(not (lessp y z) )(if (lessp z y)(not (lessp y x) )(equal (fix x)(fix z) ) ) ) )") :: nofibStringToList("(equal (meaning (plus-tree (delete x y) )a)(if (member x y)(difference (meaning (plus-tree y)a)(meaning x a) )(meaning (plus-tree y)a) ) )") :: nofibStringToList("(equal (times x (add1 y) )(if (numberp y)(plus x (times x y) )(fix x) ) )") :: nofibStringToList("(equal (nth (Nill)i)(if (zerop i)(Nill)(zero) ) )") :: nofibStringToList("(equal (last (append a b) )(if (listp b)(last b)(if (listp a)(Conss (car (last a) )b)b) ) )") :: nofibStringToList("(equal (equal (lessp x y)z)(if (lessp x y)(equal t z)(equal f z) ) )") :: nofibStringToList("(equal (assignment x (append a b) )(if (assignedp x a)(assignment x a)(assignment x b) ) )") :: nofibStringToList("(equal (car (gopher x) )(if (listp x)(car (flatten x) )(zero) ) )") :: nofibStringToList("(equal (flatten (cdr (gopher x) ) )(if (listp x)(cdr (flatten x) )(Conss (zero)(Nill) ) ) )") :: nofibStringToList("(equal (quotient (times y x)y)(if (zerop y)(zero)(fix x) ) )") :: nofibStringToList("(equal (get j (set i val mem) )(if (eqp j i)val(get j mem) ) )") :: Nil //│ rules = [["(","e","q","u","a","l"," ","(","c","o","m","p","i","l","e"," ","f","o","r","m",")","(","r","e","v","e","r","s","e"," ","(","c","o","d","e","g","e","n"," ","(","o","p","t","i","m","i","z","e"," ","f","o","r","m",")"," ","(","N","i","l","l",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","p"," ","x"," ","y",")","(","e","q","u","a","l"," ","(","f","i","x"," ","x",")","(","f","i","x"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","g","r","e","a","t","e","r","p"," ","x"," ","y",")","(","l","e","s","s","p"," ","y"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","e","q","p"," ","x"," ","y",")","(","n","o","t"," ","(","l","e","s","s","p"," ","y"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","g","r","e","a","t","e","r","e","q","p"," ","x"," ","y",")","(","n","o","t"," ","(","l","e","s","s","p"," ","y"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","b","o","o","l","e","a","n"," ","x",")","(","o","r"," ","(","e","q","u","a","l"," ","x"," ","(","t",")"," ",")","(","e","q","u","a","l"," ","x"," ","(","f",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","i","f","f"," ","x"," ","y",")","(","a","n","d"," ","(","i","m","p","l","i","e","s"," ","x"," ","y",")","(","i","m","p","l","i","e","s"," ","y"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","v","e","n","1"," ","x",")","(","i","f"," ","(","z","e","r","o","p"," ","x",")","(","t",")","(","o","d","d"," ","(","1","-"," ","x",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","c","o","u","n","t","p","s","-"," ","l"," ","p","r","e","d",")","(","c","o","u","n","t","p","s","-","l","o","o","p"," ","l"," ","p","r","e","d"," ","(","z","e","r","o",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","f","a","c","t","-"," ","i",")","(","f","a","c","t","-","l","o","o","p"," ","i"," ","1",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","v","e","r","s","e","-"," ","x",")","(","r","e","v","e","r","s","e","-","l","o","o","p"," ","x"," ","(","N","i","l","l",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","v","i","d","e","s"," ","x"," ","y",")","(","z","e","r","o","p"," ","(","r","e","m","a","i","n","d","e","r"," ","y"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","a","s","s","u","m","e","-","t","r","u","e"," ","v","a","r"," ","a","l","i","s","t",")","(","C","o","n","s","s"," ","(","C","o","n","s","s"," ","v","a","r"," ","(","t",")"," ",")","a","l","i","s","t",")"," ",")"],["(","e","q","u","a","l"," ","(","a","s","s","u","m","e","-","f","a","l","s","e"," ","v","a","r"," ","a","l","i","s","t",")","(","C","o","n","s","s"," ","(","C","o","n","s","s"," ","v","a","r"," ","(","f",")"," ",")","a","l","i","s","t",")"," ",")"],["(","e","q","u","a","l"," ","(","t","a","u","t","o","l","o","g","y","-","c","h","e","c","k","e","r"," ","x",")","(","t","a","u","t","o","l","o","g","y","p"," ","(","n","o","r","m","a","l","i","z","e"," ","x",")","(","N","i","l","l",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","f","a","l","s","i","f","y"," ","x",")","(","f","a","l","s","i","f","y","1"," ","(","n","o","r","m","a","l","i","z","e"," ","x",")","(","N","i","l","l",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","p","r","i","m","e"," ","x",")","(","a","n","d"," ","(","n","o","t"," ","(","z","e","r","o","p"," ","x",")",")","(","n","o","t"," ","(","e","q","u","a","l"," ","x"," ","(","a","d","d","1"," ","(","z","e","r","o",")"," ",")"," ",")"," ",")","(","p","r","i","m","e","1"," ","x"," ","(","1","-"," ","x",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","a","n","d"," ","p"," ","q",")","(","i","f"," ","p"," ","(","i","f"," ","q"," ","(","t",")"," ","(","f",")"," ",")"," ","(","f",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","o","r"," ","p"," ","q",")","(","i","f"," ","p"," ","(","t",")"," ","(","i","f"," ","q"," ","(","t",")"," ","(","f",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","n","o","t"," ","p",")","(","i","f"," ","p"," ","(","f",")"," ","(","t",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","i","m","p","l","i","e","s"," ","p"," ","q",")","(","i","f"," ","p"," ","(","i","f"," ","q"," ","(","t",")"," ","(","f",")"," ",")"," ","(","t",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","f","i","x"," ","x",")","(","i","f"," ","(","n","u","m","b","e","r","p"," ","x",")"," ","x"," ","(","z","e","r","o",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","i","f"," ","(","i","f"," ","a"," ","b"," ","c",")"," ","d"," ","e",")","(","i","f"," ","a"," ","(","i","f"," ","b"," ","d"," ","e",")"," ","(","i","f"," ","c"," ","d"," ","e",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","z","e","r","o","p"," ","x",")","(","o","r"," ","(","e","q","u","a","l"," ","x"," ","(","z","e","r","o",")"," ",")","(","n","o","t"," ","(","n","u","m","b","e","r","p"," ","x",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","p","l","u","s"," ","(","p","l","u","s"," ","x"," ","y",")"," ","z"," ",")","(","p","l","u","s"," ","x"," ","(","p","l","u","s"," ","y"," ","z",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","p","l","u","s"," ","a"," ","b",")"," ","(","z","e","r","o"," ",")"," ",")","(","a","n","d"," ","(","z","e","r","o","p"," ","a",")"," ","(","z","e","r","o","p"," ","b",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","x"," ","x",")","(","z","e","r","o",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","p","l","u","s"," ","a"," ","b",")"," ","(","p","l","u","s"," ","a"," ","c",")"," ",")","(","e","q","u","a","l"," ","(","f","i","x"," ","b",")"," ","(","f","i","x"," ","c",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","z","e","r","o",")"," ","(","d","i","f","f","e","r","e","n","c","e"," ","x"," ","y",")"," ",")","(","n","o","t"," ","(","l","e","s","s","p"," ","y"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","x"," ","(","d","i","f","f","e","r","e","n","c","e"," ","x"," ","y",")"," ",")","(","a","n","d"," ","(","n","u","m","b","e","r","p"," ","x",")","(","o","r"," ","(","e","q","u","a","l"," ","x"," ","(","z","e","r","o",")"," ",")","(","z","e","r","o","p"," ","y",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","(","a","p","p","e","n","d"," ","x"," ","y",")"," ",")"," ","a",")","(","p","l","u","s"," ","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","x",")"," ","a",")","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","y",")"," ","a",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","(","p","l","u","s","-","f","r","i","n","g","e"," ","x",")"," ",")"," ","a",")","(","f","i","x"," ","(","m","e","a","n","i","n","g"," ","x"," ","a",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","a","p","p","e","n","d"," ","(","a","p","p","e","n","d"," ","x"," ","y",")"," ","z",")","(","a","p","p","e","n","d"," ","x"," ","(","a","p","p","e","n","d"," ","y"," ","z",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","v","e","r","s","e"," ","(","a","p","p","e","n","d"," ","a"," ","b",")"," ",")","(","a","p","p","e","n","d"," ","(","r","e","v","e","r","s","e"," ","b",")"," ","(","r","e","v","e","r","s","e"," ","a",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","t","i","m","e","s"," ","x"," ","(","p","l","u","s"," ","y"," ","z",")"," ",")","(","p","l","u","s"," ","(","t","i","m","e","s"," ","x"," ","y",")","(","t","i","m","e","s"," ","x"," ","z",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","t","i","m","e","s"," ","(","t","i","m","e","s"," ","x"," ","y",")"," ","z",")","(","t","i","m","e","s"," ","x"," ","(","t","i","m","e","s"," ","y"," ","z",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","t","i","m","e","s"," ","x"," ","y",")"," ","(","z","e","r","o",")"," ",")","(","o","r"," ","(","z","e","r","o","p"," ","x",")","(","z","e","r","o","p"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","x","e","c"," ","(","a","p","p","e","n","d"," ","x"," ","y",")","p","d","s"," ","e","n","v","r","n",")","(","e","x","e","c"," ","y"," ","(","e","x","e","c"," ","x"," ","p","d","s"," ","e","n","v","r","n",")","e","n","v","r","n",")"," ",")"],["(","e","q","u","a","l"," ","(","m","c","-","f","l","a","t","t","e","n"," ","x"," ","y",")","(","a","p","p","e","n","d"," ","(","f","l","a","t","t","e","n"," ","x",")","y",")"," ",")"],["(","e","q","u","a","l"," ","(","m","e","m","b","e","r"," ","x"," ","(","a","p","p","e","n","d"," ","a"," ","b",")"," ",")","(","o","r"," ","(","m","e","m","b","e","r"," ","x"," ","a",")","(","m","e","m","b","e","r"," ","x"," ","b",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","m","e","m","b","e","r"," ","x"," ","(","r","e","v","e","r","s","e"," ","y",")"," ",")","(","m","e","m","b","e","r"," ","x"," ","y",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","n","g","t","h"," ","(","r","e","v","e","r","s","e"," ","x",")"," ",")","(","l","e","n","g","t","h"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","m","e","m","b","e","r"," ","a"," ","(","i","n","t","e","r","s","e","c","t"," ","b"," ","c",")"," ",")","(","a","n","d"," ","(","m","e","m","b","e","r"," ","a"," ","b",")","(","m","e","m","b","e","r"," ","a"," ","c",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","n","t","h"," ","(","z","e","r","o",")","i",")","(","z","e","r","o",")"," ",")"],["(","e","q","u","a","l"," ","(","e","x","p"," ","i"," ","(","p","l","u","s"," ","j"," ","k",")"," ",")","(","t","i","m","e","s"," ","(","e","x","p"," ","i"," ","j",")","(","e","x","p"," ","i"," ","k",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","x","p"," ","i"," ","(","t","i","m","e","s"," ","j"," ","k",")"," ",")","(","e","x","p"," ","(","e","x","p"," ","i"," ","j",")","k",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","v","e","r","s","e","-","l","o","o","p"," ","x"," ","y",")","(","a","p","p","e","n","d"," ","(","r","e","v","e","r","s","e"," ","x",")","y",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","v","e","r","s","e","-","l","o","o","p"," ","x"," ","(","N","i","l","l",")"," ",")","(","r","e","v","e","r","s","e"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","c","o","u","n","t","-","l","i","s","t"," ","z"," ","(","s","o","r","t","-","l","p"," ","x"," ","y",")"," ",")","(","p","l","u","s"," ","(","c","o","u","n","t","-","l","i","s","t"," ","z"," ","x",")","(","c","o","u","n","t","-","l","i","s","t"," ","z"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","a","p","p","e","n","d"," ","a"," ","b",")","(","a","p","p","e","n","d"," ","a"," ","c",")"," ",")","(","e","q","u","a","l"," ","b"," ","c",")"," ",")"],["(","e","q","u","a","l"," ","(","p","l","u","s"," ","(","r","e","m","a","i","n","d","e","r"," ","x"," ","y",")","(","t","i","m","e","s"," ","y"," ","(","q","u","o","t","i","e","n","t"," ","x"," ","y",")"," ",")"," ",")","(","f","i","x"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","p","o","w","e","r","-","e","v","a","l"," ","(","b","i","g","-","p","l","u","s","1"," ","l"," ","i"," ","b","a","s","e",")","b","a","s","e",")","(","p","l","u","s"," ","(","p","o","w","e","r","-","e","v","a","l"," ","l"," ","b","a","s","e",")","i",")"," ",")"],["(","e","q","u","a","l"," ","(","p","o","w","e","r","-","e","v","a","l"," ","(","b","i","g","-","p","l","u","s"," ","x"," ","y"," ","i"," ","b","a","s","e",")","b","a","s","e",")","(","p","l","u","s"," ","i"," ","(","p","l","u","s"," ","(","p","o","w","e","r","-","e","v","a","l"," ","x"," ","b","a","s","e",")","(","p","o","w","e","r","-","e","v","a","l"," ","y"," ","b","a","s","e",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","m","a","i","n","d","e","r"," ","y"," ","1",")","(","z","e","r","o",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","(","r","e","m","a","i","n","d","e","r"," ","x"," ","y",")","y",")","(","n","o","t"," ","(","z","e","r","o","p"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","m","a","i","n","d","e","r"," ","x"," ","x",")","(","z","e","r","o",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","(","q","u","o","t","i","e","n","t"," ","i"," ","j",")","i",")","(","a","n","d"," ","(","n","o","t"," ","(","z","e","r","o","p"," ","i",")"," ",")","(","o","r"," ","(","z","e","r","o","p"," ","j",")","(","n","o","t"," ","(","e","q","u","a","l"," ","j"," ","1",")"," ",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","(","r","e","m","a","i","n","d","e","r"," ","x"," ","y",")","x",")","(","a","n","d"," ","(","n","o","t"," ","(","z","e","r","o","p"," ","y",")"," ",")","(","n","o","t"," ","(","z","e","r","o","p"," ","x",")"," ",")","(","n","o","t"," ","(","l","e","s","s","p"," ","x"," ","y",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","p","o","w","e","r","-","e","v","a","l"," ","(","p","o","w","e","r","-","r","e","p"," ","i"," ","b","a","s","e",")","b","a","s","e",")","(","f","i","x"," ","i",")"," ",")"],["(","e","q","u","a","l"," ","(","p","o","w","e","r","-","e","v","a","l"," ","(","b","i","g","-","p","l","u","s"," ","(","p","o","w","e","r","-","r","e","p"," ","i"," ","b","a","s","e",")","(","p","o","w","e","r","-","r","e","p"," ","j"," ","b","a","s","e",")","(","z","e","r","o",")","b","a","s","e",")","b","a","s","e",")","(","p","l","u","s"," ","i"," ","j",")"," ",")"],["(","e","q","u","a","l"," ","(","g","c","d"," ","x"," ","y",")","(","g","c","d"," ","y"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","n","t","h"," ","(","a","p","p","e","n","d"," ","a"," ","b",")","i",")","(","a","p","p","e","n","d"," ","(","n","t","h"," ","a"," ","i",")","(","n","t","h"," ","b"," ","(","d","i","f","f","e","r","e","n","c","e"," ","i"," ","(","l","e","n","g","t","h"," ","a",")"," ",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","(","p","l","u","s"," ","x"," ","y",")","x",")","(","f","i","x"," ","y",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","(","p","l","u","s"," ","y"," ","x",")","x",")","(","f","i","x"," ","y",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","(","p","l","u","s"," ","x"," ","y",")","(","p","l","u","s"," ","x"," ","z",")"," ",")","(","d","i","f","f","e","r","e","n","c","e"," ","y"," ","z",")"," ",")"],["(","e","q","u","a","l"," ","(","t","i","m","e","s"," ","x"," ","(","d","i","f","f","e","r","e","n","c","e"," ","c"," ","w",")"," ",")","(","d","i","f","f","e","r","e","n","c","e"," ","(","t","i","m","e","s"," ","c"," ","x",")","(","t","i","m","e","s"," ","w"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","m","a","i","n","d","e","r"," ","(","t","i","m","e","s"," ","x"," ","z",")","z",")","(","z","e","r","o",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","(","p","l","u","s"," ","b"," ","(","p","l","u","s"," ","a"," ","c",")"," ",")","a",")","(","p","l","u","s"," ","b"," ","c",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","(","a","d","d","1"," ","(","p","l","u","s"," ","y"," ","z",")","z",")","(","a","d","d","1"," ","y",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","(","p","l","u","s"," ","x"," ","y",")","(","p","l","u","s"," ","x"," ","z"," ",")"," ",")","(","l","e","s","s","p"," ","y"," ","z",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","(","t","i","m","e","s"," ","x"," ","z",")","(","t","i","m","e","s"," ","y"," ","z",")"," ",")","(","a","n","d"," ","(","n","o","t"," ","(","z","e","r","o","p"," ","z",")"," ",")","(","l","e","s","s","p"," ","x"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","y"," ","(","p","l","u","s"," ","x"," ","y",")"," ",")","(","n","o","t"," ","(","z","e","r","o","p"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","g","c","d"," ","(","t","i","m","e","s"," ","x"," ","z",")","(","t","i","m","e","s"," ","y"," ","z",")"," ",")","(","t","i","m","e","s"," ","z"," ","(","g","c","d"," ","x"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","v","a","l","u","e"," ","(","n","o","r","m","a","l","i","z","e"," ","x",")","a",")","(","v","a","l","u","e"," ","x"," ","a",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","f","l","a","t","t","e","n"," ","x",")","(","C","o","n","s","s"," ","y"," ","(","N","i","l","l",")"," ",")"," ",")","(","a","n","d"," ","(","n","l","i","s","t","p"," ","x",")","(","e","q","u","a","l"," ","x"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","l","i","s","t","p"," ","(","g","o","p","h","e","r"," ","x",")"," ",")","(","l","i","s","t","p"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","s","a","m","e","f","r","i","n","g","e"," ","x"," ","y",")","(","e","q","u","a","l"," ","(","f","l","a","t","t","e","n"," ","x",")","(","f","l","a","t","t","e","n"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","g","r","e","a","t","e","s","t","-","f","a","c","t","o","r"," ","x"," ","y",")","(","z","e","r","o",")"," ",")","(","a","n","d"," ","(","o","r"," ","(","z","e","r","o","p"," ","y",")","(","e","q","u","a","l"," ","y"," ","1",")"," ",")","(","e","q","u","a","l"," ","x"," ","(","z","e","r","o",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","g","r","e","a","t","e","s","t","-","f","a","c","t","o","r"," ","x"," ","y",")","1",")","(","e","q","u","a","l"," ","x"," ","1",")"," ",")"],["(","e","q","u","a","l"," ","(","n","u","m","b","e","r","p"," ","(","g","r","e","a","t","e","s","t","-","f","a","c","t","o","r"," ","x"," ","y",")"," ",")","(","n","o","t"," ","(","a","n","d"," ","(","o","r"," ","(","z","e","r","o","p"," ","y",")","(","e","q","u","a","l"," ","y"," ","1",")"," ",")","(","n","o","t"," ","(","n","u","m","b","e","r","p"," ","x",")"," ",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","t","i","m","e","s","-","l","i","s","t"," ","(","a","p","p","e","n","d"," ","x"," ","y",")"," ",")","(","t","i","m","e","s"," ","(","t","i","m","e","s","-","l","i","s","t"," ","x",")","(","t","i","m","e","s","-","l","i","s","t"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","p","r","i","m","e","-","l","i","s","t"," ","(","a","p","p","e","n","d"," ","x"," ","y",")"," ",")","(","a","n","d"," ","(","p","r","i","m","e","-","l","i","s","t"," ","x",")","(","p","r","i","m","e","-","l","i","s","t"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","z"," ","(","t","i","m","e","s"," ","w"," ","z",")"," ",")","(","a","n","d"," ","(","n","u","m","b","e","r","p"," ","z",")","(","o","r"," ","(","e","q","u","a","l"," ","z"," ","(","z","e","r","o",")"," ",")","(","e","q","u","a","l"," ","w"," ","1",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","g","r","e","a","t","e","r","e","q","p","r"," ","x"," ","y",")","(","n","o","t"," ","(","l","e","s","s","p"," ","x"," ","y",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","x"," ","(","t","i","m","e","s"," ","x"," ","y",")"," ",")","(","o","r"," ","(","e","q","u","a","l"," ","x"," ","(","z","e","r","o",")"," ",")","(","a","n","d"," ","(","n","u","m","b","e","r","p"," ","x",")","(","e","q","u","a","l"," ","y"," ","1",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","r","e","m","a","i","n","d","e","r"," ","(","t","i","m","e","s"," ","y"," ","x",")","y",")","(","z","e","r","o",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","t","i","m","e","s"," ","a"," ","b",")","1",")","(","a","n","d"," ","(","n","o","t"," ","(","e","q","u","a","l"," ","a"," ","(","z","e","r","o",")"," ",")"," ",")","(","n","o","t"," ","(","e","q","u","a","l"," ","b"," ","(","z","e","r","o",")"," ",")"," ",")","(","n","u","m","b","e","r","p"," ","a",")","(","n","u","m","b","e","r","p"," ","b",")","(","e","q","u","a","l"," ","(","1","-"," ","a",")","(","z","e","r","o",")"," ",")","(","e","q","u","a","l"," ","(","1","-"," ","b",")","(","z","e","r","o",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","(","l","e","n","g","t","h"," ","(","d","e","l","e","t","e"," ","x"," ","l",")"," ",")","(","l","e","n","g","t","h"," ","l",")"," ",")","(","m","e","m","b","e","r"," ","x"," ","l",")"," ",")"],["(","e","q","u","a","l"," ","(","s","o","r","t","2"," ","(","d","e","l","e","t","e"," ","x"," ","l",")"," ",")","(","d","e","l","e","t","e"," ","x"," ","(","s","o","r","t","2"," ","l",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","d","s","o","r","t"," ","x",")","(","s","o","r","t","2"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","l","e","n","g","t","h","(","C","o","n","s","s"," ","x","1","(","C","o","n","s","s"," ","x","2","(","C","o","n","s","s"," ","x","3","(","C","o","n","s","s"," ","x","4","(","C","o","n","s","s"," ","x","5","(","C","o","n","s","s"," ","x","6"," ","x","7",")"," ",")"," ",")"," ",")"," ",")"," ",")"," ",")","(","p","l","u","s"," ","6"," ","(","l","e","n","g","t","h"," ","x","7",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","(","a","d","d","1"," ","(","a","d","d","1"," ","x",")"," ",")","2",")","(","f","i","x"," ","x",")"," ",")"],["(","e","q","u","a","l"," ","(","q","u","o","t","i","e","n","t"," ","(","p","l","u","s"," ","x"," ","(","p","l","u","s"," ","x"," ","y",")"," ",")","2",")","(","p","l","u","s"," ","x"," ","(","q","u","o","t","i","e","n","t"," ","y"," ","2",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","s","i","g","m","a"," ","(","z","e","r","o",")","i",")","(","q","u","o","t","i","e","n","t"," ","(","t","i","m","e","s"," ","i"," ","(","a","d","d","1"," ","i",")"," ",")","2",")"," ",")"],["(","e","q","u","a","l"," ","(","p","l","u","s"," ","x"," ","(","a","d","d","1"," ","y",")"," ",")","(","i","f"," ","(","n","u","m","b","e","r","p"," ","y",")","(","a","d","d","1"," ","(","p","l","u","s"," ","x"," ","y",")"," ",")","(","a","d","d","1"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","d","i","f","f","e","r","e","n","c","e"," ","x"," ","y",")","(","d","i","f","f","e","r","e","n","c","e"," ","z"," ","y",")"," ",")","(","i","f"," ","(","l","e","s","s","p"," ","x"," ","y",")","(","n","o","t"," ","(","l","e","s","s","p"," ","y"," ","z",")"," ",")","(","i","f"," ","(","l","e","s","s","p"," ","z"," ","y",")","(","n","o","t"," ","(","l","e","s","s","p"," ","y"," ","x",")"," ",")","(","e","q","u","a","l"," ","(","f","i","x"," ","x",")","(","f","i","x"," ","z",")"," ",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","(","d","e","l","e","t","e"," ","x"," ","y",")"," ",")","a",")","(","i","f"," ","(","m","e","m","b","e","r"," ","x"," ","y",")","(","d","i","f","f","e","r","e","n","c","e"," ","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","y",")","a",")","(","m","e","a","n","i","n","g"," ","x"," ","a",")"," ",")","(","m","e","a","n","i","n","g"," ","(","p","l","u","s","-","t","r","e","e"," ","y",")","a",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","t","i","m","e","s"," ","x"," ","(","a","d","d","1"," ","y",")"," ",")","(","i","f"," ","(","n","u","m","b","e","r","p"," ","y",")","(","p","l","u","s"," ","x"," ","(","t","i","m","e","s"," ","x"," ","y",")"," ",")","(","f","i","x"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","n","t","h"," ","(","N","i","l","l",")","i",")","(","i","f"," ","(","z","e","r","o","p"," ","i",")","(","N","i","l","l",")","(","z","e","r","o",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","l","a","s","t"," ","(","a","p","p","e","n","d"," ","a"," ","b",")"," ",")","(","i","f"," ","(","l","i","s","t","p"," ","b",")","(","l","a","s","t"," ","b",")","(","i","f"," ","(","l","i","s","t","p"," ","a",")","(","C","o","n","s","s"," ","(","c","a","r"," ","(","l","a","s","t"," ","a",")"," ",")","b",")","b",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","e","q","u","a","l"," ","(","l","e","s","s","p"," ","x"," ","y",")","z",")","(","i","f"," ","(","l","e","s","s","p"," ","x"," ","y",")","(","e","q","u","a","l"," ","t"," ","z",")","(","e","q","u","a","l"," ","f"," ","z",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","a","s","s","i","g","n","m","e","n","t"," ","x"," ","(","a","p","p","e","n","d"," ","a"," ","b",")"," ",")","(","i","f"," ","(","a","s","s","i","g","n","e","d","p"," ","x"," ","a",")","(","a","s","s","i","g","n","m","e","n","t"," ","x"," ","a",")","(","a","s","s","i","g","n","m","e","n","t"," ","x"," ","b",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","c","a","r"," ","(","g","o","p","h","e","r"," ","x",")"," ",")","(","i","f"," ","(","l","i","s","t","p"," ","x",")","(","c","a","r"," ","(","f","l","a","t","t","e","n"," ","x",")"," ",")","(","z","e","r","o",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","f","l","a","t","t","e","n"," ","(","c","d","r"," ","(","g","o","p","h","e","r"," ","x",")"," ",")"," ",")","(","i","f"," ","(","l","i","s","t","p"," ","x",")","(","c","d","r"," ","(","f","l","a","t","t","e","n"," ","x",")"," ",")","(","C","o","n","s","s"," ","(","z","e","r","o",")","(","N","i","l","l",")"," ",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","q","u","o","t","i","e","n","t"," ","(","t","i","m","e","s"," ","y"," ","x",")","y",")","(","i","f"," ","(","z","e","r","o","p"," ","y",")","(","z","e","r","o",")","(","f","i","x"," ","x",")"," ",")"," ",")"],["(","e","q","u","a","l"," ","(","g","e","t"," ","j"," ","(","s","e","t"," ","i"," ","v","a","l"," ","m","e","m",")"," ",")","(","i","f"," ","(","e","q","p"," ","j"," ","i",")","v","a","l","(","g","e","t"," ","j"," ","m","e","m",")"," ",")"," ",")"]] val lemmas = addlemmalst(makelemmas(rules), Empty) //│ lemmas = Node([Node([Node([Node([Node([Empty, [["a","n","d"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["a","n","d"]), Conss([Atom(["p"]), Conss([Atom(["q"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["p"]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["q"]), Conss([Conss([Atom(["t"]), Nill]), Conss([Conss([Atom(["f"]), Nill]), Nill])])])]), Conss([Conss([Atom(["f"]), Nill]), Nill])])])]), Nill])])])]], Node([Empty, [["a","p","p","e","n","d"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["x"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Nill])])])]], Node([Empty, [["a","s","s","i","g","n","m","e","n","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["a","s","s","i","g","n","m","e","n","t"]), Conss([Atom(["x"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["a","s","s","i","g","n","e","d","p"]), Conss([Atom(["x"]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["a","s","s","i","g","n","m","e","n","t"]), Conss([Atom(["x"]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["a","s","s","i","g","n","m","e","n","t"]), Conss([Atom(["x"]), Conss([Atom(["b"]), Nill])])]), Nill])])])]), Nill])])])]], Empty])])]), [["a","s","s","u","m","e","-","f","a","l","s","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["a","s","s","u","m","e","-","f","a","l","s","e"]), Conss([Atom(["v","a","r"]), Conss([Atom(["a","l","i","s","t"]), Nill])])]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["v","a","r"]), Conss([Conss([Atom(["f"]), Nill]), Nill])])]), Conss([Atom(["a","l","i","s","t"]), Nill])])]), Nill])])])]], Empty]), [["a","s","s","u","m","e","-","t","r","u","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["a","s","s","u","m","e","-","t","r","u","e"]), Conss([Atom(["v","a","r"]), Conss([Atom(["a","l","i","s","t"]), Nill])])]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["v","a","r"]), Conss([Conss([Atom(["t"]), Nill]), Nill])])]), Conss([Atom(["a","l","i","s","t"]), Nill])])]), Nill])])])]], Empty]), [["b","o","o","l","e","a","n"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["b","o","o","l","e","a","n"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["t"]), Nill]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["f"]), Nill]), Nill])])]), Nill])])]), Nill])])])]], Node([Empty, [["c","a","r"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["c","a","r"]), Conss([Conss([Atom(["g","o","p","h","e","r"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","i","s","t","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["c","a","r"]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]), Nill])])])]], Empty])]), [["c","o","m","p","i","l","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["c","o","m","p","i","l","e"]), Conss([Atom(["f","o","r","m"]), Nill])]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Conss([Atom(["c","o","d","e","g","e","n"]), Conss([Conss([Atom(["o","p","t","i","m","i","z","e"]), Conss([Atom(["f","o","r","m"]), Nill])]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Nill])]), Nill])])])]], Node([Node([Node([Empty, [["c","o","u","n","t","-","l","i","s","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["c","o","u","n","t","-","l","i","s","t"]), Conss([Atom(["z"]), Conss([Conss([Atom(["s","o","r","t","-","l","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["c","o","u","n","t","-","l","i","s","t"]), Conss([Atom(["z"]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["c","o","u","n","t","-","l","i","s","t"]), Conss([Atom(["z"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Nill])])])]], Empty]), [["c","o","u","n","t","p","s","-"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["c","o","u","n","t","p","s","-"]), Conss([Atom(["l"]), Conss([Atom(["p","r","e","d"]), Nill])])]), Conss([Conss([Atom(["c","o","u","n","t","p","s","-","l","o","o","p"]), Conss([Atom(["l"]), Conss([Atom(["p","r","e","d"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]), Nill])])])]], Node([Node([Empty, [["d","i","f","f","e","r","e","n","c","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Atom(["2"]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["b"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["a"]), Conss([Atom(["c"]), Nill])])]), Nill])])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["b"]), Conss([Atom(["c"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["y"]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["y"]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["x"]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]], Empty]), [["d","i","v","i","d","e","s"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","v","i","d","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])]), Nill])])])]], Node([Empty, [["d","s","o","r","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","s","o","r","t"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["s","o","r","t","2"]), Conss([Atom(["x"]), Nill])]), Nill])])])]], Empty])])]), [["e","q","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])])])]], Node([Node([Node([Node([Empty, [["e","q","u","a","l"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["t"]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["f"]), Conss([Atom(["z"]), Nill])])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["z"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["z"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["z"]), Nill])]), Nill])])]), Nill])])])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Conss([Atom(["1"]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["a"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Nill])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["b"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Nill])]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["a"]), Nill])]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["b"]), Nill])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["1","-"]), Conss([Atom(["a"]), Nill])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["1","-"]), Conss([Atom(["b"]), Nill])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Nill])])])])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["y"]), Conss([Atom(["1"]), Nill])])]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["z"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["w"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["z"]), Nill])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["z"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["w"]), Conss([Atom(["1"]), Nill])])]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","r","e","a","t","e","s","t","-","f","a","c","t","o","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["1"]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Atom(["1"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","r","e","a","t","e","s","t","-","f","a","c","t","o","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["y"]), Conss([Atom(["1"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["y"]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","l","i","s","t","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["c"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["b"]), Conss([Atom(["c"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["a"]), Conss([Atom(["c"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["b"]), Nill])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["c"]), Nill])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["a"]), Nill])]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["b"]), Nill])]), Nill])])]), Nill])])])]], Empty]), [["e","v","e","n","1"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","v","e","n","1"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["t"]), Nill]), Conss([Conss([Atom(["o","d","d"]), Conss([Conss([Atom(["1","-"]), Conss([Atom(["x"]), Nill])]), Nill])]), Nill])])])]), Nill])])])]], Node([Node([Empty, [["e","x","e","c"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","x","e","c"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["p","d","s"]), Conss([Atom(["e","n","v","r","n"]), Nill])])])]), Conss([Conss([Atom(["e","x","e","c"]), Conss([Atom(["y"]), Conss([Conss([Atom(["e","x","e","c"]), Conss([Atom(["x"]), Conss([Atom(["p","d","s"]), Conss([Atom(["e","n","v","r","n"]), Nill])])])]), Conss([Atom(["e","n","v","r","n"]), Nill])])])]), Nill])])])]], Node([Empty, [["e","x","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","x","p"]), Conss([Atom(["i"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["j"]), Conss([Atom(["k"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["e","x","p"]), Conss([Conss([Atom(["e","x","p"]), Conss([Atom(["i"]), Conss([Atom(["j"]), Nill])])]), Conss([Atom(["k"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["e","x","p"]), Conss([Atom(["i"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["j"]), Conss([Atom(["k"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Conss([Atom(["e","x","p"]), Conss([Atom(["i"]), Conss([Atom(["j"]), Nill])])]), Conss([Conss([Atom(["e","x","p"]), Conss([Atom(["i"]), Conss([Atom(["k"]), Nill])])]), Nill])])]), Nill])])])]], Empty])]), [["f","a","c","t","-"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","a","c","t","-"]), Conss([Atom(["i"]), Nill])]), Conss([Conss([Atom(["f","a","c","t","-","l","o","o","p"]), Conss([Atom(["i"]), Conss([Atom(["1"]), Nill])])]), Nill])])])]], Node([Empty, [["f","a","l","s","i","f","y"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","a","l","s","i","f","y"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["f","a","l","s","i","f","y","1"]), Conss([Conss([Atom(["n","o","r","m","a","l","i","z","e"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Nill])])])]], Node([Empty, [["f","i","x"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["x"]), Nill])]), Conss([Atom(["x"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]), Nill])])])]], Node([Node([Empty, [["f","l","a","t","t","e","n"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Conss([Atom(["c","d","r"]), Conss([Conss([Atom(["g","o","p","h","e","r"]), Conss([Atom(["x"]), Nill])]), Nill])]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","i","s","t","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["c","d","r"]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Nill])])])]), Nill])])])]], Empty]), [["g","c","d"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","c","d"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["z"]), Conss([Conss([Atom(["g","c","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","c","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["g","c","d"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])])])]], Node([Empty, [["g","e","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","e","t"]), Conss([Atom(["j"]), Conss([Conss([Atom(["s","e","t"]), Conss([Atom(["i"]), Conss([Atom(["v","a","l"]), Conss([Atom(["m","e","m"]), Nill])])])]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["e","q","p"]), Conss([Atom(["j"]), Conss([Atom(["i"]), Nill])])]), Conss([Atom(["v","a","l"]), Conss([Conss([Atom(["g","e","t"]), Conss([Atom(["j"]), Conss([Atom(["m","e","m"]), Nill])])]), Nill])])])]), Nill])])])]], Empty])])])])])]), [["g","r","e","a","t","e","r","e","q","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","r","e","a","t","e","r","e","q","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])]), Nill])])])]], Node([Empty, [["g","r","e","a","t","e","r","e","q","p","r"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","r","e","a","t","e","r","e","q","p","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Nill])])])]], Empty])]), [["g","r","e","a","t","e","r","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["g","r","e","a","t","e","r","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])])])]], Node([Node([Node([Empty, [["i","f"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Conss([Atom(["c"]), Nill])])])]), Conss([Atom(["d"]), Conss([Atom(["e"]), Nill])])])]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["a"]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["b"]), Conss([Atom(["d"]), Conss([Atom(["e"]), Nill])])])]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["c"]), Conss([Atom(["d"]), Conss([Atom(["e"]), Nill])])])]), Nill])])])]), Nill])])])]], Empty]), [["i","f","f"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["i","f","f"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])])]), Nill])])])]], Node([Empty, [["i","m","p","l","i","e","s"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["i","m","p","l","i","e","s"]), Conss([Atom(["p"]), Conss([Atom(["q"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["p"]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["q"]), Conss([Conss([Atom(["t"]), Nill]), Conss([Conss([Atom(["f"]), Nill]), Nill])])])]), Conss([Conss([Atom(["t"]), Nill]), Nill])])])]), Nill])])])]], Node([Node([Empty, [["l","a","s","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","a","s","t"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","i","s","t","p"]), Conss([Atom(["b"]), Nill])]), Conss([Conss([Atom(["l","a","s","t"]), Conss([Atom(["b"]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["l","i","s","t","p"]), Conss([Atom(["a"]), Nill])]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Conss([Atom(["c","a","r"]), Conss([Conss([Atom(["l","a","s","t"]), Conss([Atom(["a"]), Nill])]), Nill])]), Conss([Atom(["b"]), Nill])])]), Conss([Atom(["b"]), Nill])])])]), Nill])])])]), Nill])])])]], Empty]), [["l","e","n","g","t","h"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["x","1"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["x","2"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["x","3"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["x","4"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["x","5"]), Conss([Conss([Atom(["C","o","n","s","s"]), Conss([Atom(["x","6"]), Conss([Atom(["x","7"]), Nill])])]), Nill])])]), Nill])])]), Nill])])]), Nill])])]), Nill])])]), Nill])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["6"]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Atom(["x","7"]), Nill])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Atom(["x"]), Nill])]), Nill])])])]], Empty])])]), [["l","e","s","s","e","q","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","e","q","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Nill])]), Nill])])])]], Node([Node([Node([Node([Node([Node([Node([Empty, [["l","e","s","s","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Conss([Atom(["d","e","l","e","t","e"]), Conss([Atom(["x"]), Conss([Atom(["l"]), Nill])])]), Nill])]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Atom(["l"]), Nill])]), Nill])])]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Atom(["l"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["x"]), Nill])]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["z"]), Nill])]), Nill])]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Nill])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Conss([Atom(["q","u","o","t","i","e","n","t"]), Conss([Atom(["i"]), Conss([Atom(["j"]), Nill])])]), Conss([Atom(["i"]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["i"]), Nill])]), Nill])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["j"]), Nill])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["j"]), Conss([Atom(["1"]), Nill])])]), Nill])]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","e","s","s","p"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Nill])]), Nill])])])]], Node([Empty, [["l","i","s","t","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["l","i","s","t","p"]), Conss([Conss([Atom(["g","o","p","h","e","r"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["l","i","s","t","p"]), Conss([Atom(["x"]), Nill])]), Nill])])])]], Empty])]), [["m","c","-","f","l","a","t","t","e","n"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","c","-","f","l","a","t","t","e","n"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Atom(["x"]), Nill])]), Conss([Atom(["y"]), Nill])])]), Nill])])])]], Empty]), [["m","e","a","n","i","n","g"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Conss([Atom(["d","e","l","e","t","e"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Atom(["y"]), Nill])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Atom(["x"]), Conss([Atom(["a"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Atom(["y"]), Nill])]), Conss([Atom(["a"]), Nill])])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Conss([Atom(["p","l","u","s","-","f","r","i","n","g","e"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Atom(["x"]), Conss([Atom(["a"]), Nill])])]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Atom(["x"]), Nill])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["m","e","a","n","i","n","g"]), Conss([Conss([Atom(["p","l","u","s","-","t","r","e","e"]), Conss([Atom(["y"]), Nill])]), Conss([Atom(["a"]), Nill])])]), Nill])])]), Nill])])])]], Node([Empty, [["m","e","m","b","e","r"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["a"]), Conss([Conss([Atom(["i","n","t","e","r","s","e","c","t"]), Conss([Atom(["b"]), Conss([Atom(["c"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["a"]), Conss([Atom(["c"]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["m","e","m","b","e","r"]), Conss([Atom(["x"]), Conss([Atom(["b"]), Nill])])]), Nill])])]), Nill])])])]], Empty])]), [["n","o","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["n","o","t"]), Conss([Atom(["p"]), Nill])]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["p"]), Conss([Conss([Atom(["f"]), Nill]), Conss([Conss([Atom(["t"]), Nill]), Nill])])])]), Nill])])])]], Node([Empty, [["n","t","h"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["n","t","h"]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Conss([Atom(["i"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["i"]), Nill])]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["n","t","h"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Conss([Atom(["i"]), Nill])])]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Conss([Atom(["n","t","h"]), Conss([Atom(["a"]), Conss([Atom(["i"]), Nill])])]), Conss([Conss([Atom(["n","t","h"]), Conss([Atom(["b"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["i"]), Conss([Conss([Atom(["l","e","n","g","t","h"]), Conss([Atom(["a"]), Nill])]), Nill])])]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["n","t","h"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Conss([Atom(["i"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]], Node([Empty, [["n","u","m","b","e","r","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Conss([Atom(["g","r","e","a","t","e","s","t","-","f","a","c","t","o","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["y"]), Conss([Atom(["1"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["x"]), Nill])]), Nill])]), Nill])])]), Nill])]), Nill])])])]], Empty])])]), [["o","r"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["o","r"]), Conss([Atom(["p"]), Conss([Atom(["q"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["p"]), Conss([Conss([Atom(["t"]), Nill]), Conss([Conss([Atom(["i","f"]), Conss([Atom(["q"]), Conss([Conss([Atom(["t"]), Nill]), Conss([Conss([Atom(["f"]), Nill]), Nill])])])]), Nill])])])]), Nill])])])]], Node([Empty, [["p","l","u","s"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["y"]), Nill])]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Atom(["x"]), Nill])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["y"]), Conss([Conss([Atom(["q","u","o","t","i","e","n","t"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Nill])])])]], Node([Empty, [["p","o","w","e","r","-","e","v","a","l"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Conss([Atom(["b","i","g","-","p","l","u","s"]), Conss([Conss([Atom(["p","o","w","e","r","-","r","e","p"]), Conss([Atom(["i"]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["p","o","w","e","r","-","r","e","p"]), Conss([Atom(["j"]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Conss([Atom(["b","a","s","e"]), Nill])])])])]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["i"]), Conss([Atom(["j"]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Conss([Atom(["p","o","w","e","r","-","r","e","p"]), Conss([Atom(["i"]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["i"]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Conss([Atom(["b","i","g","-","p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Conss([Atom(["i"]), Conss([Atom(["b","a","s","e"]), Nill])])])])]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["i"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Atom(["x"]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Atom(["y"]), Conss([Atom(["b","a","s","e"]), Nill])])]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Conss([Atom(["b","i","g","-","p","l","u","s","1"]), Conss([Atom(["l"]), Conss([Atom(["i"]), Conss([Atom(["b","a","s","e"]), Nill])])])]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["p","o","w","e","r","-","e","v","a","l"]), Conss([Atom(["l"]), Conss([Atom(["b","a","s","e"]), Nill])])]), Conss([Atom(["i"]), Nill])])]), Nill])])])]], Empty])])]), [["p","r","i","m","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","r","i","m","e"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["x"]), Nill])]), Nill])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])]), Nill])])]), Nill])]), Conss([Conss([Atom(["p","r","i","m","e","1"]), Conss([Atom(["x"]), Conss([Conss([Atom(["1","-"]), Conss([Atom(["x"]), Nill])]), Nill])])]), Nill])])])]), Nill])])])]], Node([Node([Node([Empty, [["p","r","i","m","e","-","l","i","s","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["p","r","i","m","e","-","l","i","s","t"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Conss([Conss([Atom(["a","n","d"]), Conss([Conss([Atom(["p","r","i","m","e","-","l","i","s","t"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["p","r","i","m","e","-","l","i","s","t"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])])])]], Node([Empty, [["q","u","o","t","i","e","n","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["q","u","o","t","i","e","n","t"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["y"]), Nill])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["q","u","o","t","i","e","n","t"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Atom(["2"]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["q","u","o","t","i","e","n","t"]), Conss([Atom(["y"]), Conss([Atom(["2"]), Nill])])]), Nill])])]), Nill])])])]], Empty])]), [["r","e","m","a","i","n","d","e","r"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["y"]), Conss([Atom(["x"]), Nill])])]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["z"]), Nill])])]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Atom(["x"]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","m","a","i","n","d","e","r"]), Conss([Atom(["y"]), Conss([Atom(["1"]), Nill])])]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])])]], Empty]), [["r","e","v","e","r","s","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["a"]), Conss([Atom(["b"]), Nill])])]), Nill])]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Atom(["b"]), Nill])]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Atom(["a"]), Nill])]), Nill])])]), Nill])])])]], Empty])]), [["r","e","v","e","r","s","e","-"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","v","e","r","s","e","-"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["r","e","v","e","r","s","e","-","l","o","o","p"]), Conss([Atom(["x"]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Nill])])])]], Node([Node([Empty, [["r","e","v","e","r","s","e","-","l","o","o","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","v","e","r","s","e","-","l","o","o","p"]), Conss([Atom(["x"]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Atom(["x"]), Nill])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["r","e","v","e","r","s","e","-","l","o","o","p"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Conss([Atom(["r","e","v","e","r","s","e"]), Conss([Atom(["x"]), Nill])]), Conss([Atom(["y"]), Nill])])]), Nill])])])]], Node([Empty, [["s","a","m","e","f","r","i","n","g","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["s","a","m","e","f","r","i","n","g","e"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["f","l","a","t","t","e","n"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])])])]], Node([Node([Empty, [["s","i","g","m","a"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["s","i","g","m","a"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Conss([Atom(["i"]), Nill])])]), Conss([Conss([Atom(["q","u","o","t","i","e","n","t"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["i"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Atom(["i"]), Nill])]), Nill])])]), Conss([Atom(["2"]), Nill])])]), Nill])])])]], Empty]), [["s","o","r","t","2"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["s","o","r","t","2"]), Conss([Conss([Atom(["d","e","l","e","t","e"]), Conss([Atom(["x"]), Conss([Atom(["l"]), Nill])])]), Nill])]), Conss([Conss([Atom(["d","e","l","e","t","e"]), Conss([Atom(["x"]), Conss([Conss([Atom(["s","o","r","t","2"]), Conss([Atom(["l"]), Nill])]), Nill])])]), Nill])])])]], Empty])])]), [["t","a","u","t","o","l","o","g","y","-","c","h","e","c","k","e","r"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","a","u","t","o","l","o","g","y","-","c","h","e","c","k","e","r"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["t","a","u","t","o","l","o","g","y","p"]), Conss([Conss([Atom(["n","o","r","m","a","l","i","z","e"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["N","i","l","l"]), Nill]), Nill])])]), Nill])])])]], Node([Node([Empty, [["t","i","m","e","s"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["a","d","d","1"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Conss([Conss([Atom(["i","f"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["y"]), Nill])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["f","i","x"]), Conss([Atom(["x"]), Nill])]), Nill])])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Atom(["c"]), Conss([Atom(["w"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["d","i","f","f","e","r","e","n","c","e"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["c"]), Conss([Atom(["x"]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["w"]), Conss([Atom(["x"]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Atom(["z"]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Nill])])]),Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Atom(["y"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Conss([Conss([Atom(["p","l","u","s"]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Atom(["x"]), Conss([Atom(["z"]), Nill])])]), Nill])])]), Nill])])])]], Node([Node([Empty, [["t","i","m","e","s","-","l","i","s","t"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["t","i","m","e","s","-","l","i","s","t"]), Conss([Conss([Atom(["a","p","p","e","n","d"]), Conss([Atom(["x"]), Conss([Atom(["y"]), Nill])])]), Nill])]), Conss([Conss([Atom(["t","i","m","e","s"]), Conss([Conss([Atom(["t","i","m","e","s","-","l","i","s","t"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["t","i","m","e","s","-","l","i","s","t"]), Conss([Atom(["y"]), Nill])]), Nill])])]), Nill])])])]], Empty]), [["v","a","l","u","e"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["v","a","l","u","e"]), Conss([Conss([Atom(["n","o","r","m","a","l","i","z","e"]), Conss([Atom(["x"]), Nill])]), Conss([Atom(["a"]), Nill])])]), Conss([Conss([Atom(["v","a","l","u","e"]), Conss([Atom(["x"]), Conss([Atom(["a"]), Nill])])]), Nill])])])]], Empty])]), [["z","e","r","o","p"], [Conss([Atom(["e","q","u","a","l"]), Conss([Conss([Atom(["z","e","r","o","p"]), Conss([Atom(["x"]), Nill])]), Conss([Conss([Atom(["o","r"]), Conss([Conss([Atom(["e","q","u","a","l"]), Conss([Atom(["x"]), Conss([Conss([Atom(["z","e","r","o"]), Nill]), Nill])])]), Conss([Conss([Atom(["n","o","t"]), Conss([Conss([Atom(["n","u","m","b","e","r","p"]), Conss([Atom(["x"]), Nill])]), Nill])]), Nill])])]), Nill])])])]], Empty])])])])])])]) fun tautp(term) = tautologyp([rewrite(term, lemmas), Nill, Nill]) fun teststatement(i) = applysubst(subterm(i), statement) fun testresult(i) = tautp(teststatement(i)) fun testBoyer2_nofib(n) = report(testresult(n)) fun main() = testBoyer2_nofib(3) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/calendar.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module calendar with ... fun unlines(ls) = concat(map(x => x +: ("\n" :: Nil), ls)) fun height(p) = listLen(p) fun width(p) = listLen(head(p)) fun stack(ls) = foldr1((a, b) => a +: b, ls) fun spread(ls) = foldr1((a, b) => zipWith((a, b) => a +: b, a, b), ls) fun emptyPic(hw) = if hw is [h, w] then replicate(h, replicate(w, " ")) fun groop(n, xs) = if xs is Nil then Nil else take(n, xs) :: groop(n, leave(n, xs)) fun block(n, t) = stack(map(spread, groop(n, t))) fun blockT(n, t) = stack(map(stack, groop(n, t))) fun lframe(mn, p) = if mn is [m,n] then let h = height(p) let w = width(p) zipWith(append, p, emptyPic([h, n - w])) +: emptyPic([m - h, n]) fun leap(year) = if intMod(year, 100) == 0 then intMod(year, 400) == 0 else intMod(year, 4) == 0 fun monthLengths(year) = let feb = if leap(year) then 29 else 28 31 :: feb :: 31 :: 30 :: 31 :: 30 :: 31 :: 31 :: 30 :: 31 :: 30 :: 31 :: Nil val monthNames = nofibStringToList("January") :: nofibStringToList("February") :: nofibStringToList("March") :: nofibStringToList("April") :: nofibStringToList("May") :: nofibStringToList("June") :: nofibStringToList("July") :: nofibStringToList("August") :: nofibStringToList("September") :: nofibStringToList("October") :: nofibStringToList("November") :: nofibStringToList("December") :: Nil //│ monthNames = [["J","a","n","u","a","r","y"],["F","e","b","r","u","a","r","y"],["M","a","r","c","h"],["A","p","r","i","l"],["M","a","y"],["J","u","n","e"],["J","u","l","y"],["A","u","g","u","s","t"],["S","e","p","t","e","m","b","e","r"],["O","c","t","o","b","e","r"],["N","o","v","e","m","b","e","r"],["D","e","c","e","m","b","e","r"]] fun jan1st(year) = let last = year - 1 intMod(year + intDiv(last, 4) - intDiv(last, 100) + intDiv(last, 400), 7) fun firstDays(year) = take(12, map(x => intMod(x, 7), scanl((a, b) => a + b, jan1st(year), monthLengths(year)))) fun space(n) = replicate(n, " ") fun ljustify(n, s) = s +: space(n - listLen(s)) fun rjustify(n, s) = space(n - listLen(s)) +: s fun date(ml, d) = if (d < 1) || (ml < d) then nofibStringToList(" ") :: Nil else rjustify(3, nofibStringToList(stringOfInt(d))) :: Nil fun dates(fd, ml) = map(d => date(ml, d), enumFromTo(1 - fd, 42 - fd)) fun cjustify(n, s) = let m = n - listLen(s) let halfm = intDiv(m, 2) space(halfm) +: s +: space(m - halfm) fun cal(year) = let side = emptyPic([8, 2]) let end = emptyPic([1, 25]) let daynames = nofibStringToList(" Su Mo Tu We Th Fr Sa") :: Nil fun banner(yr) = cjustify(75, nofibStringToList(stringOfInt(yr))) :: emptyPic([1, 75]) fun body(yr) = block(3, map(x => pad(pic(x)), months(yr))) fun pic(mnfdml) = if mnfdml is [mn, fd, ml] then title(mn) +: table(fd, ml) fun pad(p) = zipWith(append, zipWith(append, side, p), side) +: end fun title(mn) = cjustify(21, mn) :: Nil fun table(fd, ml) = daynames +: entries(fd, ml) fun entries(fd, ml) = block(7, dates(fd, ml)) fun months(yer) = zip3(monthNames, firstDays(yer), monthLengths(yer)) unlines(banner(year) +: body(year)) fun testCalendar_nofib(n) = map(x => cal(x), enumFromTo(1993, 1993 + n)) // NOTE: original input: 1000 fun main() = nofibListToString of concat of testCalendar_nofib(0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/cichelli.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module cichelli with ... val keys = nofibStringToList("case") :: nofibStringToList("class") :: nofibStringToList("data") :: nofibStringToList("default") :: nofibStringToList("deriving") :: nofibStringToList("else") :: nofibStringToList("hiding") :: nofibStringToList("if") :: nofibStringToList("import") :: nofibStringToList("in") :: nofibStringToList("infix") :: nofibStringToList("infixl") :: nofibStringToList("instance") :: nofibStringToList("interface") :: nofibStringToList("let") :: nofibStringToList("module") :: nofibStringToList("of") :: nofibStringToList("renaming") :: nofibStringToList("then") :: nofibStringToList("to") :: nofibStringToList("type") :: nofibStringToList("where") :: Nil //│ keys = [["c","a","s","e"],["c","l","a","s","s"],["d","a","t","a"],["d","e","f","a","u","l","t"],["d","e","r","i","v","i","n","g"],["e","l","s","e"],["h","i","d","i","n","g"],["i","f"],["i","m","p","o","r","t"],["i","n"],["i","n","f","i","x"],["i","n","f","i","x","l"],["i","n","s","t","a","n","c","e"],["i","n","t","e","r","f","a","c","e"],["l","e","t"],["m","o","d","u","l","e"],["o","f"],["r","e","n","a","m","i","n","g"],["t","h","e","n"],["t","o"],["t","y","p","e"],["w","h","e","r","e"]] fun enumFromTo_lz(a, b) = lazy of () => if a <= b then LzCons(a, enumFromTo_lz(a + 1, b)) else LzNil fun last(ls) = fun go(h, t) = if t is Nil then h head :: t then go(head, t) if ls is h :: t then go(h, t) else throw Error("last: empty list") data class K(s: String, c1: Char, c2: Char, i: Int) data class H(f: Option[Int], s: Option[Int], ls: List[Int]) fun ends(k) = if k is K(_, a, z, _) then a :: z :: Nil fun assoc(x, yz) = if yz is [y, z] :: yzs then if x === y then z else assoc(x, yzs) else throw Error("assoc: not found") fun assocm(x, yz) = if yz is [y, z] :: yzs then if x === y then Some(z) else assocm(x, yzs) else None fun histins(x, yns) = if yns is [y, n] :: yns then if x === y then [y, n + 1] :: yns else [y, n] :: histins(x, yns) else [x, 1] :: Nil fun histo(ls) = foldr(cichelli.histins, Nil, ls) fun subset(xs, ys) = all(x => inList(x, ys), xs) fun union(xs, ys) = fun lscomp(ls) = if ls is Nil then Nil h :: t and not(inList(h, xs)) then h :: lscomp(t) else lscomp(t) append(xs, lscomp(ys)) fun attribkeys(ks) = map(k => K(k, head(k), cichelli.last(k), listLen(k)), ks) val numberofkeys = listLen(keys) //│ numberofkeys = 22 fun minm(x, y) = if x is None then y Some(x) then min(x, y) fun maxm(x, y) = if x is None then y Some(x) then max(x, y) fun hash(cvs, k) = if k is K(_, a, z, n) then n + assoc(a, cvs) + assoc(z, cvs) fun select(p, x, ts_fs) = if ts_fs is [ts, fs] and p(x) then [x :: ts, fs] else [ts, x :: fs] fun partition_(p, ls) = foldr((x, y) => select(p, x, y), [Nil, Nil], ls) fun freqsorted(x) = x fun blocked_(ds, ls) = if ls is Nil then Nil k :: ks then let ds_ = union(ds, ends(k)) if partition_(x => subset(ends(x), ds_), ks) is [det, rest] then k :: (det +: blocked_(ds_, rest)) fun blocked(ls) = blocked_(Nil, ls) val freqtab = histo(concat(map(ends, attribkeys(keys)))) //│ freqtab = [["e", 8],["w", 1],["t", 6],["o", 2],["n", 2],["g", 3],["r", 1],["f", 2],["m", 1],["l", 2],["i", 7],["x", 1],["h", 1],["d", 3],["a", 1],["s", 1],["c", 2]] val maxval = listLen(freqtab) //│ maxval = 17 abstract class Status[T]: NotEver | YesIts[T] data class NotEver(i: Int) extends Status[Nothing] YesIts[T](i: Int, t: T) extends Status[T] fun hinsert(h, hh) = if hh is H(lo, hi, hs) then let lo_ = minm(lo, h) let hi_ = maxm(hi, h) if inList(h, hs) || (1 + hi_ - lo_) > numberofkeys then None else Some(H(Some(lo_), Some(hi_), h :: hs)) fun first(k, ls) = if force(ls) is LzNil then NotEver(k) LzCons(a, l) and a is YesIts(leaves, y) then YesIts(k + leaves, y) NotEver(leaves) then first(k + leaves, l) fun firstSuccess(f, possibles) = first(0, map_lz(f, possibles)) fun findhash_(keyHashSet, charAssocs, ks) = if ks is Nil then YesIts(1, charAssocs) K(s, a, z, n) :: ks then fun tryy(newAssocs) = let newCharAssocs = newAssocs +: charAssocs if hinsert(hash(newCharAssocs, K(s, a, z, n)), keyHashSet) is None then NotEver(1) Some(newKeyHashSet) then findhash_(newKeyHashSet, newCharAssocs, ks) if [assocm(a, charAssocs), assocm(z, charAssocs)] is [None, None] and a === z then firstSuccess(m => tryy([a, m] :: Nil), enumFromTo_lz(0, maxval)) else fun lscomp1(ls1) = lazy of () => if force(ls1) is LzNil then LzNil LzCons(m, ms) then fun lscomp2(ls2) = if force(ls2) is LzNil then lscomp1(ms) LzCons(n, ns) then lazy of () => LzCons([m, n], lscomp2(ns)) force(lscomp2(enumFromTo_lz(0, maxval))) firstSuccess(case { [m, n] then tryy([a, m] :: [z, n] :: Nil) }, lscomp1(enumFromTo_lz(0, maxval))) [None, Some(zc)] then firstSuccess(m => tryy([a, m] :: Nil), enumFromTo_lz(0, maxval)) [Some(ac), None] then firstSuccess(n => tryy([z, n] :: Nil), enumFromTo_lz(0, maxval)) [Some(ac), Some(zc)] then tryy(Nil) fun findhash(keys) = findhash_(H(None, None, Nil), Nil, keys) fun freq(c) = assoc(c, freqtab) fun morefreq(k1, k2) = if k1 is K(_, a, x, _) and k2 is K(_, b, y, _) then (freq(a) + freq(x)) > (freq(b) + freq(y)) fun cichelli_(n) = let attribkeys_ = attribkeys(keys +: take(intMod(n, 2), keys)) let hashkeys = blocked(freqsorted(attribkeys_)) findhash(hashkeys) fun prog(n) = cichelli_(n) fun main() = prog(6).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/circsim.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module circsim with ... abstract class BinTree[T, U]: Cell[T] | Node[T, U] data class Cell[T](value: T) extends BinTree[T, Nothing] Node[T, U](value: U, left: BinTree[T, U], right: BinTree[T, U]) extends BinTree[T, U] abstract class Componenet: None_ | Inp | Outp | Dff | Inv | And2 | Or2 | Xor object None_ extends Componenet Inp extends Componenet Outp extends Componenet Dff extends Componenet Inv extends Componenet And2 extends Componenet Or2 extends Componenet Xor extends Componenet object Unit data class PS[T](pid: Int, compType: Componenet, pathDepth: Int, inports: List[[Int, Int, T]], outports: List[[Int, T, Bool, Int, Bool, Int]]) fun pid(p) = p.pid fun compType(p) = p.compType fun pathDepth(p) = p.pathDepth fun inports(p) = p.inports fun outports(p) = p.outports fun updateOutports(p, noutports) = PS(pid(p), compType(p), pathDepth(p), inports(p), noutports) fun updateInports(p, ninports) = PS(pid(p), compType(p), pathDepth(p), ninports, outports(p)) abstract class Boolean: F | T object F extends Boolean T extends Boolean fun put(xs) = if xs is x :: Nil then Cell(x) splitAt(intDiv(listLen(xs), 2), xs) is [fstHalf, sndHalf] then Node(Unit, put(fstHalf), put(sndHalf)) fun get(t) = if t is Cell(x) then x :: Nil Node(_, l, r) then get(l) +: get(r) fun upsweep(f, t) = if t is Cell(a) then [a, Cell(a)] Node(x, l, r) and upsweep(f, l) is [lv, l_] and upsweep(f, r) is [rv, r_] then [f(lv, rv), Node([lv, rv], l_, r_)] fun downsweep(g, d, t) = if t is Cell(x) then Cell(d) Node([lv, rv], l, r) and g(lv, rv, d) is [dl, dr] then Node(Unit, downsweep(g, dl, l), downsweep(g, dr, r)) fun sweep_ud(up, down, u, t) = if upsweep(up, t) is [ans, t_] then [ans, downsweep(down, u, t_)] fun scanL(f, u, xs) = fun down1(l, r, x) = [x, f(x, l)] if sweep_ud(f, down1, u, put(xs)) is [up_ans, t_] then [up_ans, get(t_)] fun scanR(f, u, xs) = fun down2(l, r, x) = [f(r, x), x] if sweep_ud(f, down2, u, put(xs)) is [up_ans, t_] then [up_ans, get(t_)] fun scanlr(f, g, lu, ru, xs) = fun up(f, g, lxly, rxry) = if lxly is [lx, ly] and rxry is [rx, ry] then [f(lx, rx), g(ly, ry)] fun down3(f, g, lxly, rxry, ab) = if lxly is [lx, ly] and rxry is [rx, ry] and ab is [a, b] then [[a, g(ry, b)], [f(a, lx), b]] let xs_ = map(x => [x, x], xs) if sweep_ud((a, b) => up(f, g, a, b), (a, b, c) => down3(f, g, a, b, c), [lu, ru], put(xs_)) is [[l_ans, r_ans], t_] then let ans = [g(r_ans, ru), f(lu, l_ans)] [ans, get(t_)] fun nearest_power_of_two(x) = until(a => a >= x, a => a * 2, 1) val emptyState = PS(-1, None_, -1, Nil, Nil) //│ emptyState = PS(-1, None_, -1, [], []) fun pad_circuit(size_ins_outs_states) = if size_ins_outs_states is [size, ins, outs, states] then let p2 = nearest_power_of_two(size) let states_ = append_nl_lz(states, replicate_lz(p2, emptyState)) [p2, ins, outs, take_lz(p2, states_)] fun inv(x) = if x === T then F else T fun and2(x, y) = if x === T && y === T then T else F fun or2(x, y) = if x === T || y === T then T else F fun xor(x, y) = if x === y then T else F val emptyPacket = [-1, -1, F, false, 0, false, 0, 1] //│ emptyPacket = [-1, -1, F, false, 0, false, 0, 1] fun send_right(a, b) = if a is [ia, sa, ma, qla, dla, qra, dra, ea] and b is [ib, sb, mb, qlb, dlb, qrb, drb, eb] and qra and (dra > eb) then [ia, sa, ma, qla, dla, qra, dra - eb, ea + eb] else [ib, sb, mb, qlb, dlb, qrb, drb, ea + eb] fun send_left(a, b) = if a is [ia, sa, ma, qla, dla, qra, dra, ea] and b is [ib, sb, mb, qlb, dlb, qrb, drb, eb] and qlb && (dlb > ea) then [ib, sb, mb, qlb, dlb - ea, qrb, drb, ea + eb] else [ia, sa, ma, qla, dla, qra, dra, ea + eb] fun send(xs) = scanlr(send_right, send_left, emptyPacket, emptyPacket, xs) fun update_outports(state, value) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is [p, m, ql, dl, qr, dr] then [p, value, ql, dl, qr, dr] :: lscomp(t) else lscomp(t) updateOutports(state, lscomp(outports(state))) fun critical_path_depth(siot) = if siot is [size, ins, outs, states] then maximum(map(pathDepth, states)) fun collect_outputs(tp4) = if tp4 is [size, ins, outs, states] then fun thrid(tp3) = if tp3 is [_, _, v] then v fun get_output(states, label_p) = if label_p is [label, p] then fun lscomp(ls) = if ls is Nil then Nil s :: t and p == pid(s) then head(inports(s)) :: lscomp(t) else lscomp(t) thrid(head(lscomp(states))) map(p => get_output(states, p), outs) fun store_inputs(label_inputs, state) = if state is PS(pid_, Inp, _, _, _) then fun lscomp(ls) = if ls is Nil then Nil h :: t and h is [[label, input_pid], value] and pid_ == input_pid then update_outports(state, value) :: lscomp(t) else lscomp(t) head(lscomp(label_inputs)) else state fun apply_component(comp, signals) = if comp is Inp then None Outp and signals is x :: Nil then Some(x) Dff and signals is x :: Nil then Some(x) Inv and signals is x :: Nil then Some(inv(x)) And2 and signals is x :: y :: Nil then Some(and2(x, y)) Or2 and signals is x :: y :: Nil then Some(or2(x, y)) Xor and signals is x :: y :: Nil then Some(xor(x, y)) None_ then None fun init_dffs(state) = if compType(state) === Dff then update_outports(state, F) else state fun restore_requests(old_states, new_states) = fun restore(os, ns) = updateOutports(ns, zipWith(restore_outport, outports(os), outports(ns))) fun restore_outport(pql, mdq) = if pql is [p, _, ql, dl, qr, dq] and mdq is [_, m, _, _, _, _] then [p, m, ql, dl, qr, dq] zipWith(restore, old_states, new_states) fun update_requests(b, state) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is [p, m, ql, dl, qr, dr] then [p, m, b, dl, b, dr] :: lscomp(t) else lscomp(t) updateOutports(state, lscomp(outports(state))) fun check_depth(d, state) = if pathDepth(state) == d then state else update_requests(false, state) fun acknowledge(d, states) = fun check_requests(xs) = orList(map(check_lr_requests, xs)) fun check_lr_requests(pql) = if pql is [p,m,ql,dl,qr,dr] then ql || qr let states1 = map(s => check_depth(d, s), states) not(orList(map(s => check_requests(outports(s)), states1))) fun pad_packets(pss) = fun pad(xs) = let max_ps = maximum(map(x => listLen(x), pss)) take_lz(max_ps, append_nl_lz(xs, replicate_lz(max_ps, emptyPacket))) map(x => pad(x), pss) fun make_packet(state) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is [p, m, ql, dl, qr, dr] then [pid(state), p, m, ql, dl, qr, dr, 1] :: lscomp(t) else lscomp(t) lscomp(outports(state)) fun compare_and_update(ipm_, pid_port_m) = if ipm_ is [i, p, m_] and pid_port_m is [pid_, port, m] and eqTup2([i, p], [pid_, port]) then [pid_, port, m_] else [pid_, port, m] fun up_i(ipm_, ins) = if ipm_ is [i, p, m_, _, _, _, _, _] then map(x => compare_and_update([i, p, m_], x), ins) fun update_i(l_r, ins) = if l_r is [l, r] then up_i(l, up_i(r, ins)) fun check_left(a, b) = if a is [pid_, port, pm, pql, pdl, pqr, pdr, e] and b is [p, m, ql, dl, qr, dr] and pqr && (pdr > 0) then [p, m, ql, dl, qr, dr] else [p, m, ql, dl, false, dr] fun check_right(a, b) = if a is [pid_, port, pm, pql, pdl, pqr, pdr, e] and b is [p, m, ql, dl, qr, dr] and pql && (pdl > 0) then [p, m, ql, dl, qr, dr] else [p, m, false, dl, qr, dr] fun update_o(lp_rp, out_) = if lp_rp is [lp, rp] then check_left(lp, check_right(rp, out_)) fun update_io(d, lrps, state) = fun update_is(state) = updateInports(state, foldr(update_i, inports(state), lrps)) fun update_os(state) = if pathDepth(state) == d then updateOutports(state, zipWith(update_o, lrps, outports(state))) else state update_os(update_is(state)) fun do_send(d, states) = let states1 = map(s => check_depth(d, s), states) let send_results = map(x => snd(send(x)), transpose(pad_packets(map(make_packet, states1)))) let pss_ = transpose(send_results) zipWith((x, y) => update_io(d, x, y), pss_, states) fun do_sends(d, states) = until(s => acknowledge(d, s), x => do_send(d, x), states) fun simulate_component(d, state) = fun lscomp(ls) = if ls is Nil then Nil h :: t and h is [_, _, sig] then sig :: lscomp(t) else lscomp(t) let out_signals = lscomp(inports(state)) let new_value = apply_component(compType(state), out_signals) if d == pathDepth(state) and not(new_value === None) and new_value is Some(v) then update_outports(state, v) else state fun simulate_components(depth, states) = map(s => simulate_component(depth, s), states) fun do_cycle(cpd, tp4, inputs) = fun sim_then_send(state, d) = do_sends(d, simulate_components(d, state)) if tp4 is [size, ins, outs, states] then let states1 = map(s => store_inputs(zip(ins, inputs), s), states) let states2 = do_sends(0, states1) let states3 = foldl(sim_then_send, states2, enumFromTo(1, cpd)) let states4 = restore_requests(states, states3) [size, ins, outs, states4] else throw Error(tp4) fun simulate(inputs_list, b) = if b is [size, ins, outs, states] then tail(scanl((x, y) => do_cycle(critical_path_depth([size, ins, outs, states]), x, y), [size, ins, outs, map(init_dffs, states)], inputs_list)) fun reg(sto, n) = PS(n, Inp, 0, Nil, [0, F, false, 0, true, 4] :: Nil) :: PS(n + 1, Dff, 1, [n + 5, 0, F] :: Nil, [0, F, false, 0, true, 5] :: Nil) :: PS(n + 2, Inv, 1, [sto, 0, F] :: Nil, [0, F, false, 0, true, 1] :: Nil) :: PS(n + 3, And2, 2, [n + 1, 0, F] :: [n + 2, 0, F] :: Nil, [0, F, false, 0, true, 2] :: Nil) :: PS(n + 4, And2, 1, [sto, 0, F] :: [n, 0, F] :: Nil, [0, F, false, 0, true, 1] :: Nil) :: PS(n + 5, Or2, 3, [n + 3, 0, F] :: [n + 4, 0, F] :: Nil, [0, F, true, 4, false, 0] :: Nil) :: PS(n + 6, Outp, 4, [n + 1, 0, F] :: Nil, Nil) :: Nil fun regs(bits) = fun ilabel(n, pid_) = [stringConcat("x", stringOfInt(n)), pid_] fun olabel(n, pid_) = [stringConcat("y", stringOfInt(n)), pid_] let is_ = ["sto", 0] :: zipWith_lz_nl(ilabel, enumFrom(0), map(x => (7 * x) + 1, enumFromTo(0, bits - 1))) let os = zipWith_lz_nl(olabel, enumFrom(0), map(x => (7 * x) + 7, enumFromTo(0, bits - 1))) let sto = PS(0, Inp, 0, Nil, [0, F, false, 0, true, (8 * (bits - 1)) + 5] :: Nil) let states = sto :: concat(map(x => reg(0, x), map(x => (7 * x) + 1, enumFromTo(0, bits - 1)))) [1 + (7 * bits), is_, os, states] fun circuit_simulate(inputs_list, circuit) = map(collect_outputs, simulate(inputs_list, circuit)) fun run(num_bits, num_cycles) = let example = pad_circuit(regs(num_bits)) let inputs = replicate(num_bits + 1, T) let cycles = replicate(num_cycles, inputs) circuit_simulate(cycles, example) fun testCircsim_nofib(n) = run(8, n) fun main() = testCircsim_nofib(40).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/clausify.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module clausify with ... abstract class Formula: Sym | Not | Dis | Con | Imp | Eqv data class Sym(a: Char) extends Formula Not(a: Formula) extends Formula Dis(a: Formula, b: Formula) extends Formula Con(a: Formula, b: Formula) extends Formula Imp(a: Formula, b: Formula) extends Formula Eqv(a: Formula, b: Formula) extends Formula abstract class StackFrame: Ast | Lex data class Ast(f: Formula) extends StackFrame Lex(s: Char) extends StackFrame fun charLt(a, b) = a < b fun charLeq(a, b) = a <= b fun charGt(a, b) = a > b fun charGeq(a, b) = a >= b fun insert(x, ys) = if ys is Nil then x :: Nil y :: ys and charLt(x, y) then x :: y :: ys charGt(x, y) then y :: insert(x, ys) else y :: ys fun clauseHelper(p, x) = if p is Dis(p, q) then clauseHelper(p, clauseHelper(q, x)) Sym(s) and x is [c, a] then [insert(s, c), a] Not(Sym(s)) and x is [c, a] then [c, insert(s, a)] fun clause(p) = clauseHelper(p, [Nil, Nil]) fun conjunct(p) = if p is Con(_, _) then true else false fun disin(p) = if p is Dis(p, Con(q, r)) then Con(disin(Dis(p, q)), disin(Dis(p, r))) Dis(Con(p, q), r) then Con(disin(Dis(p, r)), disin(Dis(q, r))) Dis(p, q) then let dp = disin(p) let dq = disin(q) if conjunct(dp) || conjunct(dq) then disin(Dis(dp, dq)) else Dis(dp, dq) Con(p, q) then Con(disin(p), disin(q)) else p fun elim(p) = if p is Sym(s) then Sym(s) Not(p) then Not(elim(p)) Dis(p, q) then Dis(elim(p), elim(q)) Con(p, q) then Con(elim(p), elim(q)) Imp(p, q) then Dis(Not(elim(p)), elim(q)) Eqv(f, f_) then Con(elim(Imp(f, f_)), elim(Imp(f_, f))) fun interleave(xs, ys) = if xs is x :: xs then x :: interleave(ys, xs) Nil then Nil fun negin(p) = if p is Not(Not(p)) then negin(p) Not(Con(p, q)) then Dis(negin(Not(p)), negin(Not(q))) Not(Dis(p, q)) then Con(negin(Not(p)), negin(Not(q))) Dis(p, q) then Dis(negin(p), negin(q)) Con(p, q) then Con(negin(p), negin(q)) else p fun opri(c) = if c === "(" then 0 === "=" then 1 === ">" then 2 === "|" then 3 === "&" then 4 === "~" then 5 else throw Error(c) fun red(s) = if s is Ast(p) :: Lex("=") :: Ast(q) :: s then Ast(Eqv(q, p)) :: s Ast(p) :: Lex(">") :: Ast(q) :: s then Ast(Imp(q, p)) :: s Ast(p) :: Lex("|") :: Ast(q) :: s then Ast(Dis(q, p)) :: s Ast(p) :: Lex("&") :: Ast(q) :: s then Ast(Con(q, p)) :: s Ast(p) :: Lex("~") :: s then Ast(Not(p)) :: s fun spri(s) = if s is Ast(x) :: Lex(c) :: s then opri(c) else 0 fun redstar(s) = while_(s => spri(s) != 0, red, s) fun spaces(n) = replicate(n, " ") fun parseHelper(t, s) = if t is Nil then redstar(s) " " :: t then parseHelper(t, s) "(" :: t then parseHelper(t, Lex("(") :: s) ")" :: t and redstar(s) is x :: Lex("(") :: ss then parseHelper(t, x :: ss) c :: t and charLeq("a", c) && charLeq(c, "z") then parseHelper(t, Ast(Sym(c)) :: s) spri(s) > opri(c) then parseHelper(c :: t, red(s)) else parseHelper(t, Lex(c) :: s) fun parse(t) = if parseHelper(t, Nil) is Ast(f) :: Nil then f fun splitHelper(p, a) = if p is Con(p, q) then splitHelper(p, splitHelper(q, a)) else p :: a fun split(p) = splitHelper(p, Nil) fun tautclause(c_a) = if c_a is [c, a] then fun lscomp(ls) = if ls is Nil then Nil h :: t and inList(h, a) then h :: lscomp(t) else lscomp(t) listNeq(lscomp(c), Nil) fun uniclHelper(p, x) = let cp = clause(p) if tautclause(cp) then x else insert(cp, x) fun unicl(a) = foldr(uniclHelper, Nil, a) fun disp(l_r) = if l_r is [l, r] then interleave(l, spaces(listLen(l))) +: nofibStringToList("<=") +: interleave(spaces(listLen(r)), r) +: nofibStringToList("\n") fun clauses(t) = concat of map of disp unicl of split of disin of negin of elim of parse of t // NOTE: original input "(a = a = a) = (a = a = a) = (a = a = a)" fun testClausify_nofib(n) = let xs = replicate(n, nofibStringToList("a = a = a")) concat(map(clauses, xs)) fun main() = nofibListToString(testClausify_nofib(10)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/constraints.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module constraints with ... // -- Figure 1. CSPs in Haskell. data class Assign(varr: Int, value: Int) data class CSP(vars: Int, vals: Int, rel: Int) //│ ———————————————————————————————————————————————————————————————————————————————— fun qsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) fun qpart(le, x, ls, rlt, rge, r) = if ls is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) else qpart(le, x, ys, y :: rlt, rge, r) fun rqsort(le, ls, r) = if ls is Nil then r x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) fun rqpart(le, x, ls, rle, rgt, r) = if ls is Nil then rqsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) else rqpart(le, x, ys, rle, y :: rgt, r) //│ ———————————————————————————————————————————————————————————————————————————————— fun level(a) = if a is Assign(v, _) then v fun value(a) = if a is Assign(_, v) then v fun maxLevel(ls) = if ls is Nil then 0 Assign(v, _) :: t then v fun complete(csp, s) = if csp is CSP(v, _, _) then maxLevel(s) == v fun safe(as1, as2) = if as1 is Assign(i, m) and as2 is Assign(j, n) then not(m == n) and not(abs(i - j) == abs(m - n)) fun queens(n) = CSP(n, n, safe) // -- Figure 2. Trees in Haskell. data class Node[out T](lab: T, children: List[Node[T]]) fun label(n) = if n is Node(l, _) then l fun mapTree(f, n) = if n is Node(l, c) then Node(f(l), map((x => mapTree(f, x)), c)) fun foldTree(f, n) = if n is Node(l, c) then f(l, map((x => foldTree(f, x)), c)) fun filterTree(p, t) = fun f1(a, cs) = Node(a, filter(x => p(label(x)), cs)) foldTree(f1, t) fun prune(p, t) = filterTree(x => not(p(x)), t) fun leaves(t) = if t is Node(leaf, Nil) then leaf :: Nil Node(_, cs) then concat(map(leaves, cs)) fun initTree(f, x) = Node(x, map(y => initTree(f, y), f(x))) // -- Figure 3. Simple backtracking solver for CSPs. fun mkTree(csp) = if csp is CSP(vars, vals, rel) then fun next(ss) = if maxLevel(ss) < vars then fun lscomp1(ls) = if ls is Nil then Nil j :: t1 then (Assign(maxLevel(ss) + 1, j) :: ss) :: lscomp1(t1) lscomp1(enumFromTo(1, vals)) else Nil initTree(next, Nil) fun earliestInconsistency(csp, aas) = if csp is CSP(vars, vals, rel) and aas is Nil then None a :: as_ and filter(x => not(rel(a, x)), reverse(as_)) is Nil then None b :: _ then Some([level(a), level(b)]) fun labelInconsistencies(csp, t) = fun f2(s) = [s, earliestInconsistency(csp, s)] mapTree(f2, t) fun btsolver0(csp) = filter of x => complete(csp, x) leaves of mapTree of fst prune of x => not(snd(x) === None) labelInconsistencies(csp, mkTree(csp)) // -- Figure 6. Conflict-directed solving of CSPs. abstract class ConflictSet: Known | Unknown data class Known(vs: List[Int]) extends ConflictSet object Unknown extends ConflictSet fun knownConflict(c) = if c is Known(a :: as_) then true else false fun knownSolution(c) = if c is Known(Nil) then true else false fun checkComplete(csp, s) = if complete(csp, s) then Known(Nil) else Unknown fun search(labeler, csp) = map of fst filter of x => knownSolution(snd(x)) leaves of prune of x => knownConflict(snd(x)) labeler(csp, mkTree(csp)) fun bt(csp, t) = fun f3(s) = [s, (if earliestInconsistency(csp, s) is Some([a, b]) then Known(a :: b :: Nil) else checkComplete(csp, s))] mapTree(f3, t) // -- Figure 8. Backmarking. fun emptyTable(csp) = if csp is CSP(vars, vals, rel) then fun lscomp1(ls) = if ls is Nil then Nil n :: t1 then fun lscomp2(ls) = if ls is Nil then Nil m :: t2 then Unknown :: lscomp2(t2) lscomp2(enumFromTo(1, vals)) :: lscomp1(t1) Nil :: lscomp1(enumFromTo(1, vars)) fun fillTable(s, csp, tbl) = if s is Nil then tbl Assign(var_, val_) :: as_ and csp is CSP(vars, vals, rel) then fun f4(cs, varval) = if varval is [varr, vall] and cs === Unknown and not(rel(Assign(var_, val_), Assign(varr, vall))) then Known(var_ :: varr :: Nil) else cs fun lscomp1(ls) = if ls is Nil then Nil varrr :: t1 then fun lscomp2(ls) = if ls is Nil then Nil valll :: t2 then [varrr, valll] :: lscomp2(t2) lscomp2(enumFromTo(1, vals)) :: lscomp1(t1) zipWith((x, y) => zipWith(f4, x, y), tbl, lscomp1(enumFromTo(var_ + 1, vars))) fun lookupCache(csp, t) = fun f5(csp, tp) = if tp is [Nil, tbl] then [[Nil, Unknown], tbl] [a :: as_, tbl] then let tableEntry = atIndex(value(a) - 1, head(tbl)) let cs = if tableEntry === Unknown then checkComplete(csp, a :: as_) else tableEntry [[a :: as_, cs], tbl] mapTree(x => f5(csp, x), t) fun cacheChecks(csp, tbl, n) = if n is Node(s, cs) then Node([s, tbl], map(x => cacheChecks(csp, fillTable(s, csp, tail(tbl)), x), cs)) fun bm(csp, t) = mapTree(fst, lookupCache(csp, cacheChecks(csp, emptyTable(csp), t))) // -- Figure 10. Conflict-directed backjumping. fun combine(ls, acc) = if ls is Nil then acc [s, Known(cs)] :: css and notElem(maxLevel(s), cs) then cs else combine(css, union(cs, acc)) fun bj_(csp, t) = fun f7(tp2, chs) = if tp2 is [a, Known(cs)] then Node([a, Known(cs)], chs) [a, Unknown] and let cs_ = Known(combine(map(label, chs), Nil)) knownConflict(cs_) then Node([a, cs_], Nil) else Node([a, cs_], chs) foldTree(f7, t) fun bj(csp, t) = fun f6(tp2, chs) = if tp2 is [a, Known(cs)] then Node([a, Known(cs)], chs) [a, Unknown] then Node([a, Known(combine(map(label, chs), Nil))], chs) foldTree(f6, t) fun bjbt(csp, t) = bj(csp, bt(csp, t)) fun bjbt_(csp, t) = bj_(csp, bt(csp, t)) // -- Figure 11. Forward checking. fun collect(ls) = if ls is Nil then Nil Known(cs) :: css then union(cs, collect(css)) fun domainWipeout(csp, t) = if csp is CSP(vars, vals, rel) then fun f8(tp2) = if tp2 is [[as_, cs], tbl] then let wipedDomains = fun lscomp1(ls) = if ls is Nil then Nil vs :: t1 and all(knownConflict, vs) then vs :: lscomp1(t1) else lscomp1(t1) lscomp1(tbl) let cs_ = if null_(wipedDomains) then cs else Known(collect(head(wipedDomains))) [as_, cs_] mapTree(f8, t) fun fc(csp, t) = domainWipeout(csp, lookupCache(csp, cacheChecks(csp, emptyTable(csp), t))) fun try_(n, algorithm) = listLen(search(algorithm, queens(n))) fun testConstraints_nofib(n) = map(x => try_(n, x), bt :: bm :: bjbt :: bjbt_ :: fc :: Nil) fun main() = testConstraints_nofib(6).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm1.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module cryptarithm1 with ... fun expand(a, b, c, d, e, f) = f + (e*10) + (d*100) + (c*1000) + (b*10000) + (a*100000) fun condition(thirywelvn) = if thirywelvn is t :: h :: i :: r :: y :: w :: e :: l :: v :: n :: Nil then (expand(t, h, i, r, t, y) + 5 * expand(t, w, e, l, v, e)) == expand(n, i, n, e, t, y) fun addj(j, ls) = if ls is Nil then (j :: Nil) :: Nil k :: ks then fun lscomp(p1) = if p1 is Nil then Nil h1 :: t1 then (k :: h1) :: lscomp(t1) (j :: (k :: ks)) :: lscomp(addj(j, ks)) fun permutations(ls) = if ls is Nil then Nil :: Nil j :: js then fun lscomp1(p1) = if p1 is Nil then Nil pjs :: t1 then fun lscomp2(p2) = if p2 is Nil then lscomp1(t1) r :: t2 then r :: lscomp2(t2) lscomp2(addj(j, pjs)) lscomp1(permutations(js)) fun testCryptarithm_nofib(n) = map((i => let p0 = take(10, enumFromTo(0, 9 + i)) in filter(condition, permutations(p0))), enumFromTo(1, n)) fun main() = testCryptarithm_nofib(1) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm2.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module cryptarithm2 with ... object Unit fun unlines(ls) = concat of map(x => x +: ("\n" :: Nil), ls) fun lookup(k, t) = if t is Nil then None [x, v] :: t then if k === x then Some(v) else lookup(k, t) fun delete_(xs, e) = deleteBy((x, y) => x === y, e, xs) fun listDiff(a, ls) = foldl(delete_, a, ls) data class StateT[S, M, A](run) fun runStateT(m, s) = if m is StateT(run) then run(s) fun bind(m, f) = StateT of s => concat of map(case { [a, ss] then runStateT(f(a), ss) }, runStateT(m, s)) fun return_(a) = StateT(s => [a, s] :: Nil) fun mapM(f, ls) = foldr((a, r) => bind(f(a), x => bind(r, xs => return_(x :: xs))), return_(Nil), ls) fun lift(ls) = StateT(s => concat(map(x => [x, s] :: Nil, ls))) fun execStateT(m, s) = concat(map(case { [a, s] then s :: Nil }, runStateT(m, s))) fun guard(b) = if b then StateT(s => [Unit, s] :: Nil) else StateT(s => Nil) fun put(s) = StateT(x => [Unit, s] :: Nil) val get = StateT(s => [s, s] :: Nil) //│ get = StateT([function]) data class Digits(i: List[Int], c: List[[Char, Int]]) fun digits(d) = if d is Digits(a, b) then a fun digitEnv(d) = if d is Digits(a, b) then b fun permute(c) = bind of get st => bind of let xs = digits(st) in lift(map(x => [x, listDiff(xs, x :: Nil)], xs)) iis => if iis is [i, iss] then bind of put(Digits(iss, [c, i] :: digitEnv(st))) _p => return_(i) fun select(c) = bind of get st => if lookup(c, digitEnv(st)) is Some(r) then return_(r) None then permute(c) fun rest(ls) = if ls is Nil then Nil x :: xs then xs fun solve(tops, bots, carry) = if bots is bot :: botss then bind of if tops is Nil then return_(carry) top :: _ then bind(mapM(select, top), topNS => return_(sum(topNS) + carry)) topN => bind of select(bot) botN => bind of guard(intMod(topN, 10) === botN) _s => solve(rest(tops), botss, intDiv(topN, 10)) Nil and tops is Nil and carry === 0 then return_(Unit) else StateT(_p => Nil) fun puzzle(top, bot) = let solution = solve(transpose(map(reverse, top)), reverse(bot), 0) let answer = if execStateT(solution, Digits(enumFromTo(0, 9), Nil)) is a :: _ then a let env = digitEnv(answer) let look = c => fromSome(lookup(c, env)) let expand = ls => foldl((a, b) => a * 10 + look(b), 0, ls) let topVal = sum(map(xs => expand(xs), top)) let botVal = expand(bot) if listLen(nubBy((x,y) => x === y, concat(top) +: bot)) > 10 then throw Error("error") topVal != botVal then throw Error("error") else unlines of map of case { [c, i] then c :: nofibStringToList(" => ") +: nofibStringToList(stringOfInt(i)) } env fun testCryptarithm2_nofib(n) = let args = nofibStringToList("THIRTY") :: nofibStringToList("TWELVE") :: nofibStringToList("TWELVE") :: nofibStringToList("TWELVE") :: nofibStringToList("TWELVE") :: append(nofibStringToList("TWELVE"), (if n > 999999 then nofibStringToList("1") else Nil)) :: Nil puzzle(args, nofibStringToList("NINETY")) fun main() = testCryptarithm2_nofib(1).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/cse.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module cse with ... fun retURN(a) = s => [s, a] fun bind(m, f) = s => if m(s) is [s_, a] then f(a)(s_) fun join(m) = s => if m(s) is [s_, ma] then ma(s_) fun mmap(f, m) = s => if m(s) is [s_, a] then [s_, f(a)] fun mmapl(f, aas) = if aas is Nil then retURN(Nil) a :: as_ then bind of f(a) b => bind of mmapl(f, as_) bs => retURN(b :: bs) fun mmapr(f, xs) = if xs is Nil then retURN(Nil) x :: xs then bind of mmapr(f, xs) ys => bind of f(x) y => retURN(y :: ys) fun mfoldl(f, a, xs) = if xs is Nil then retURN(a) x :: xs then bind(f(a, x), fax => mfoldl(f, fax, xs)) fun mfoldr(f, a, xs) = if xs is Nil then retURN(a) x :: xs then bind(mfoldr(f, a, xs), y => f(x, y)) fun mif(c, t, f) = bind(c, cond => if cond then t else f) fun startingWith(m, v) = if m(v) is [final, answer] then answer fun fetch(s) = [s, s] fun fetchWith(f) = s => [s, f(s)] fun update(f) = s => [f(s), s] fun set_(s_) = s => [s_, s] val incr = update(x => x + 1) //│ incr = [function] data class Node[T](a: T, b: List[Node[T]]) fun labelTree(t) = fun label(t) = if t is Node(x, xs) then bind of incr n => bind of mmapl(label, xs) ts => retURN(Node([n, x], ts)) startingWith(label(t), 0) fun ltGraph(t) = fun labelOf(t) = if t is Node([n, x], xs) then n if t is Node([n, x], xs) then [n, x, map(labelOf, xs)] :: concat(map(ltGraph, xs)) fun visited(n) = bind of fetch us => if inList(n, us) then retURN(true) else bind(set_(n :: us), _p => retURN(false)) fun newlyDefined(x, fx, f, y) = if x === y then fx else f(y) fun findCommon(ls) = fun sim(n_s_cs, r_lg) = if n_s_cs is [n, s, cs] and r_lg is [r, lg] then let rcs = map(r, cs) fun lscomp(ls) = if ls is Nil then Nil [m, s_, cs_] :: t and s === s_ and listEq(cs_, rcs) then m :: lscomp(t) else lscomp(t) let ms = lscomp(lg) if null_(ms) then [r, [n, s, rcs] :: lg] else [x => newlyDefined(n, head(ms), r, x), lg] if foldr(sim, [x => x, Nil], ls) is [a, b] then b else throw Error(ls.toString()) fun cse(t) = findCommon(ltGraph(labelTree(t))) fun plus_(x, y) = Node("+", x :: y :: Nil) fun mult_(x, y) = Node("*", x :: y :: Nil) fun prod(xs) = Node("X", xs) val zerO = Node("0", Nil) //│ zerO = Node("0", []) val a = Node("a", Nil) //│ a = Node("a", []) val b = Node("b", Nil) //│ b = Node("b", []) val c = Node("c", Nil) //│ c = Node("c", []) val d = Node("d", Nil) //│ d = Node("d", []) val example0 = a //│ example0 = Node("a", []) val example1 = plus_(a, a) //│ example1 = Node("+", [Node("a", []),Node("a", [])]) val example2 = plus_(mult_(a, b), mult_(a, b)) //│ example2 = Node("+", [Node("*", [Node("a", []),Node("b", [])]),Node("*", [Node("a", []),Node("b", [])])]) val example3 = plus_(mult_(plus_(a, b), c), plus_(a, b)) //│ example3 = Node("+", [Node("*", [Node("+", [Node("a", []),Node("b", [])]),Node("c", [])]),Node("+", [Node("a", []),Node("b", [])])]) val example4 = prod(scanl(plus_, zerO, a :: b :: c :: d :: Nil)) //│ example4 = Node("X", [Node("0", []),Node("+", [Node("0", []),Node("a", [])]),Node("+", [Node("+", [Node("0", []),Node("a", [])]),Node("b", [])]),Node("+", [Node("+", [Node("+", [Node("0", []),Node("a", [])]),Node("b", [])]),Node("c", [])]),Node("+", [Node("+", [Node("+", [Node("+", [Node("0", []),Node("a", [])]),Node("b", [])]),Node("c", [])]),Node("d", [])])]) val example5 = prod(scanr(plus_, zerO, a :: b :: c :: d :: Nil)) //│ example5 = Node("X", [Node("+", [Node("a", []),Node("+", [Node("b", []),Node("+", [Node("c", []),Node("+", [Node("d", []),Node("0", [])])])])]),Node("+", [Node("b", []),Node("+", [Node("c", []),Node("+", [Node("d", []),Node("0", [])])])]),Node("+", [Node("c", []),Node("+", [Node("d", []),Node("0", [])])]),Node("+", [Node("d", []),Node("0", [])]),Node("0", [])]) fun testCse_nofib(n) = map( i => map( cse, take(intMod(i, 6), example0 :: example1 :: example2 :: example3 :: example4 :: example5 :: Nil) ), enumFromTo(1, n) ) fun main() = testCse_nofib(6).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/eliza.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module eliza with ... fun toUpper(c) = c.toUpperCase() fun lz_map(f, ls) = lazy of () => if ls is Nil then LzNil h :: t then LzCons(f(h), lz_map(f, t)) fun append_lz(xs, ys) = if xs is Nil then force(ys) h :: t then lazy of () => LzCons(h, append_lz(t, ys)) fun cycle(xs) = append_lz(xs, lazy of () => cycle(xs)) fun isSpace(c) = c === " " fun words(s) = if leaveWhile(eliza.isSpace, s) is Nil then Nil h2h :: t2t and break_(eliza.isSpace, h2h :: t2t) is [w, s_] then w :: eliza.words(s_) fun unwords(ws) = fun go(ws) = if ws is Nil then Nil w :: ws then " " :: w +: go(ws) if ws is Nil then Nil w :: ws then w +: go(ws) fun null_lz(ls) = if force(ls) is LzNil then true LzCons(h, t) then false fun trim(ls) = fun cons(x, xs) = if inList(x, nofibStringToList(" .!?,")) and null_(xs) then Nil else x :: xs foldr(cons, Nil, leaveWhile(x => inList(x, nofibStringToList(" .!?,")), ls)) fun repeated(kt_rp) = if kt_rp is [kt, r :: rp] then [r, [kt, rp]] fun newKeyTab(kt_, kt_rp) = if kt_rp is [kt, rp] then [kt_, rp] fun keyTabOf(kt_rp) = if kt_rp is [kt, rp] then kt fun makeResponse(cs, us) = if cs is "?" :: cs_ then cs_ +: nofibStringToList(" ") +: us +: nofibStringToList("?") "." :: cs_ then cs_ +: nofibStringToList(" ") +: us +: nofibStringToList(".") else cs val repeatMsgs = Predef.id of nofibStringToList("Why did you repeat yourself?") :: nofibStringToList("Do you expect a different answer by repeating yourself?") :: nofibStringToList("Come, come, elucidate your thoughts.") :: nofibStringToList("Please don't repeat yourself!") :: Nil //│ repeatMsgs = [["W","h","y"," ","d","i","d"," ","y","o","u"," ","r","e","p","e","a","t"," ","y","o","u","r","s","e","l","f","?"],["D","o"," ","y","o","u"," ","e","x","p","e","c","t"," ","a"," ","d","i","f","f","e","r","e","n","t"," ","a","n","s","w","e","r"," ","b","y"," ","r","e","p","e","a","t","i","n","g"," ","y","o","u","r","s","e","l","f","?"],["C","o","m","e",","," ","c","o","m","e",","," ","e","l","u","c","i","d","a","t","e"," ","y","o","u","r"," ","t","h","o","u","g","h","t","s","."],["P","l","e","a","s","e"," ","d","o","n","'","t"," ","r","e","p","e","a","t"," ","y","o","u","r","s","e","l","f","!"]] val respMsgs = let canYou = nofibStringToList("?Don_t you believe that I can") :: nofibStringToList("?Perhaps you would like to be able to") :: nofibStringToList("?You want me to be able to") :: Nil let canI = nofibStringToList("?Perhaps you don_t want to") :: nofibStringToList("?Do you want to be able to") :: Nil let youAre = nofibStringToList("?What makes you think I am") :: nofibStringToList("?Does it please you to believe I am") :: nofibStringToList("?Perhaps you would like to be") :: nofibStringToList("?Do you sometimes wish you were") :: Nil let iDont = nofibStringToList("?Don_t you really") :: nofibStringToList("?Why don_t you") :: nofibStringToList("?Do you wish to be able to") :: nofibStringToList("Does that trouble you?") :: Nil let iFeel = nofibStringToList("Tell me more about such feelings.") :: nofibStringToList("?Do you often feel") :: nofibStringToList("?Do you enjoy feeling") :: Nil let whyDont = nofibStringToList("?Do you really believe I don't") :: nofibStringToList(".Perhaps in good time I will") :: nofibStringToList("?Do you want me to") :: Nil let whyCant = nofibStringToList("?Do you think you should be able to") :: nofibStringToList("?Why can't you") :: Nil let areYou = nofibStringToList("?Why are you interested in whether or not I am") :: nofibStringToList("?Would you prefer if I were not") :: nofibStringToList("?Perhaps in your fantasies I am") :: Nil let iCant = nofibStringToList("?How do you know you can't") :: nofibStringToList("Have you tried?") :: nofibStringToList("?Perhaps you can now") :: Nil let iAm = nofibStringToList("?Did you come to me because you are") :: nofibStringToList("?How long have you been") :: nofibStringToList("?Do you believe it is normal to be") :: nofibStringToList("?Do you enjoy being") :: Nil let you = nofibStringToList("We were discussing you --not me.") :: nofibStringToList("?Oh,") :: nofibStringToList("You're not really talking about me, are you?") :: Nil let yes = nofibStringToList("You seem quite positive.") :: nofibStringToList("Are you Sure?") :: nofibStringToList("I see.") :: nofibStringToList("I understand.") :: Nil let no = nofibStringToList("Are you saying no just to be negative?") :: nofibStringToList("You are being a bit negative.") :: nofibStringToList("Why not?") :: nofibStringToList("Are you sure?") :: nofibStringToList("Why no?") :: Nil let computer = nofibStringToList("Do computers worry you?") :: nofibStringToList("Are you talking about me in particular?") :: nofibStringToList("Are you frightened by machines?") :: nofibStringToList("Why do you mention computers?") :: nofibStringToList("What do you think machines have to do with your problems?") :: nofibStringToList("Don't you think computers can help people?") :: nofibStringToList("What is it about machines that worries you?") :: Nil let iWant = nofibStringToList("?Why do you want") :: nofibStringToList("?What would it mean to you if you got") :: nofibStringToList("?Suppose you got") :: nofibStringToList("?What if you never got") :: nofibStringToList(".I sometimes also want") :: Nil let question = nofibStringToList("Why do you ask?") :: nofibStringToList("Does that question interest you?") :: nofibStringToList("What answer would please you the most?") :: nofibStringToList("What do you think?") :: nofibStringToList("Are such questions on your mind often?") :: nofibStringToList("What is it that you really want to know?") :: nofibStringToList("Have you asked anyone else?") :: nofibStringToList("Have you asked such questions before?") :: nofibStringToList("What else comes to mind when you ask that?") :: Nil let name = nofibStringToList("Names don't interest me.") :: nofibStringToList("I don't care about names --please go on.") :: Nil let because = nofibStringToList("Is that the real reason?") :: nofibStringToList("Don't any other reasons come to mind?") :: nofibStringToList("Does that reason explain anything else?") :: nofibStringToList("What other reasons might there be?") :: Nil let sorry = nofibStringToList("Please don't apologise!") :: nofibStringToList("Apologies are not necessary.") :: nofibStringToList("What feelings do you have when you apologise?") :: nofibStringToList("Don't be so defensive!") :: Nil let dream = nofibStringToList("What does that dream suggest to you?") :: nofibStringToList("Do you dream often?") :: nofibStringToList("What persons appear in your dreams?") :: nofibStringToList("Are you disturbed by your dreams?") :: Nil let hello = nofibStringToList("How do you...please state your problem.") :: Nil let maybe = nofibStringToList("You don't seem quite certain.") :: nofibStringToList("Why the uncertain tone?") :: nofibStringToList("Can't you be more positive?") :: nofibStringToList("You aren't sure?") :: nofibStringToList("Don't you know?") :: Nil let your = nofibStringToList("?Why are you concerned about my") :: nofibStringToList("?What about your own") :: Nil let always = nofibStringToList("Can you think of a specific example?") :: nofibStringToList("When?") :: nofibStringToList("What are you thinking of?") :: nofibStringToList("Really, always?") :: Nil let think = nofibStringToList("Do you really think so?") :: nofibStringToList("?But you are not sure you") :: nofibStringToList("?Do you doubt you") :: Nil let alike = nofibStringToList("In what way?") :: nofibStringToList("What resemblence do you see?") :: nofibStringToList("What does the similarity suggest to you?") :: nofibStringToList("What other connections do you see?") :: nofibStringToList("Cound there really be some connection?") :: nofibStringToList("How?") :: Nil let friend = nofibStringToList("Why do you bring up the topic of friends?") :: nofibStringToList("Do your friends worry you?") :: nofibStringToList("Do your friends pick on you?") :: nofibStringToList("Are you sure you have any friends?") :: nofibStringToList("Do you impose on your friends?") :: nofibStringToList("Perhaps your love for friends worries you.") :: Nil let nokeyMsgs = nofibStringToList("I'm not sure I understand you fully.") :: nofibStringToList("What does that suggest to you?") :: nofibStringToList("I see.") :: nofibStringToList("Can you elaborate on that?") :: nofibStringToList("Say, do you have any psychological problems?") :: Nil Predef.id of [nofibStringToList("CAN YOU"), canYou] :: [nofibStringToList("CAN I"), canI] :: [nofibStringToList("YOU ARE"), youAre] :: [nofibStringToList("YOU'RE"), youAre] :: [nofibStringToList("I DON'T"), iDont] :: [nofibStringToList("I FEEL"), iFeel] :: [nofibStringToList("WHY DON'T YOU"), whyDont] :: [nofibStringToList("WHY CAN'T I"), whyCant] :: [nofibStringToList("ARE YOU"), areYou] :: [nofibStringToList("I CAN'T"), iCant] :: [nofibStringToList("I AM"), iAm] :: [nofibStringToList("I'M"), iAm] :: [nofibStringToList("YOU"), you] :: [nofibStringToList("YES"), yes] :: [nofibStringToList("NO"), no] :: [nofibStringToList("COMPUTER"), computer] :: [nofibStringToList("COMPUTERS"), computer] :: [nofibStringToList("I WANT"), iWant] :: [nofibStringToList("WHAT"), question] :: [nofibStringToList("HOW"), question] :: [nofibStringToList("WHO"), question] :: [nofibStringToList("WHERE"), question] :: [nofibStringToList("WHEN"), question] :: [nofibStringToList("NAME"), name] :: [nofibStringToList("WHY"), question] :: [nofibStringToList("CAUSE"), because] :: [nofibStringToList("BECAUSE"), because] :: [nofibStringToList("DREAM"), dream] :: [nofibStringToList("SORRY"), sorry] :: [nofibStringToList("HI"), hello] :: [nofibStringToList("DREAMS"), dream] :: [nofibStringToList("MAYBE"), maybe] :: [nofibStringToList("HELLO"), hello] :: [nofibStringToList("ALWAYS"), always] :: [nofibStringToList("YOUR"), your] :: [nofibStringToList("ALIKE"), alike] :: [nofibStringToList("THINK"), think] :: [nofibStringToList("FRIENDS"), friend] :: [nofibStringToList("FRIEND"), friend] :: [Nil, nokeyMsgs] :: Nil //│ respMsgs = [[["C","A","N"," ","Y","O","U"], [["?","D","o","n","_","t"," ","y","o","u"," ","b","e","l","i","e","v","e"," ","t","h","a","t"," ","I"," ","c","a","n"],["?","P","e","r","h","a","p","s"," ","y","o","u"," ","w","o","u","l","d"," ","l","i","k","e"," ","t","o"," ","b","e"," ","a","b","l","e"," ","t","o"],["?","Y","o","u"," ","w","a","n","t"," ","m","e"," ","t","o"," ","b","e"," ","a","b","l","e"," ","t","o"]]],[["C","A","N"," ","I"], [["?","P","e","r","h","a","p","s"," ","y","o","u"," ","d","o","n","_","t"," ","w","a","n","t"," ","t","o"],["?","D","o"," ","y","o","u"," ","w","a","n","t"," ","t","o"," ","b","e"," ","a","b","l","e"," ","t","o"]]],[["Y","O","U"," ","A","R","E"], [["?","W","h","a","t"," ","m","a","k","e","s"," ","y","o","u"," ","t","h","i","n","k"," ","I"," ","a","m"],["?","D","o","e","s"," ","i","t"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","o"," ","b","e","l","i","e","v","e"," ","I"," ","a","m"],["?","P","e","r","h","a","p","s"," ","y","o","u"," ","w","o","u","l","d"," ","l","i","k","e"," ","t","o"," ","b","e"],["?","D","o"," ","y","o","u"," ","s","o","m","e","t","i","m","e","s"," ","w","i","s","h"," ","y","o","u"," ","w","e","r","e"]]],[["Y","O","U","'","R","E"], [["?","W","h","a","t"," ","m","a","k","e","s"," ","y","o","u"," ","t","h","i","n","k"," ","I"," ","a","m"],["?","D","o","e","s"," ","i","t"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","o"," ","b","e","l","i","e","v","e"," ","I"," ","a","m"],["?","P","e","r","h","a","p","s"," ","y","o","u"," ","w","o","u","l","d"," ","l","i","k","e"," ","t","o"," ","b","e"],["?","D","o"," ","y","o","u"," ","s","o","m","e","t","i","m","e","s"," ","w","i","s","h"," ","y","o","u"," ","w","e","r","e"]]],[["I"," ","D","O","N","'","T"], [["?","D","o","n","_","t"," ","y","o","u"," ","r","e","a","l","l","y"],["?","W","h","y"," ","d","o","n","_","t"," ","y","o","u"],["?","D","o"," ","y","o","u"," ","w","i","s","h"," ","t","o"," ","b","e"," ","a","b","l","e"," ","t","o"],["D","o","e","s"," ","t","h","a","t"," ","t","r","o","u","b","l","e"," ","y","o","u","?"]]],[["I"," ","F","E","E","L"], [["T","e","l","l"," ","m","e"," ","m","o","r","e"," ","a","b","o","u","t"," ","s","u","c","h"," ","f","e","e","l","i","n","g","s","."],["?","D","o"," ","y","o","u"," ","o","f","t","e","n"," ","f","e","e","l"],["?","D","o"," ","y","o","u"," ","e","n","j","o","y"," ","f","e","e","l","i","n","g"]]],[["W","H","Y"," ","D","O","N","'","T"," ","Y","O","U"], [["?","D","o"," ","y","o","u"," ","r","e","a","l","l","y"," ","b","e","l","i","e","v","e"," ","I"," ","d","o","n","'","t"],[".","P","e","r","h","a","p","s"," ","i","n"," ","g","o","o","d"," ","t","i","m","e"," ","I"," ","w","i","l","l"],["?","D","o"," ","y","o","u"," ","w","a","n","t"," ","m","e"," ","t","o"]]],[["W","H","Y"," ","C","A","N","'","T"," ","I"], [["?","D","o"," ","y","o","u"," ","t","h","i","n","k"," ","y","o","u"," ","s","h","o","u","l","d"," ","b","e"," ","a","b","l","e"," ","t","o"],["?","W","h","y"," ","c","a","n","'","t"," ","y","o","u"]]],[["A","R","E"," ","Y","O","U"], [["?","W","h","y"," ","a","r","e"," ","y","o","u"," ","i","n","t","e","r","e","s","t","e","d"," ","i","n"," ","w","h","e","t","h","e","r"," ","o","r"," ","n","o","t"," ","I"," ","a","m"],["?","W","o","u","l","d"," ","y","o","u"," ","p","r","e","f","e","r"," ","i","f"," ","I"," ","w","e","r","e"," ","n","o","t"],["?","P","e","r","h","a","p","s"," ","i","n"," ","y","o","u","r"," ","f","a","n","t","a","s","i","e","s"," ","I"," ","a","m"]]],[["I"," ","C","A","N","'","T"], [["?","H","o","w"," ","d","o"," ","y","o","u"," ","k","n","o","w"," ","y","o","u"," ","c","a","n","'","t"],["H","a","v","e"," ","y","o","u"," ","t","r","i","e","d","?"],["?","P","e","r","h","a","p","s"," ","y","o","u"," ","c","a","n"," ","n","o","w"]]],[["I"," ","A","M"], [["?","D","i","d"," ","y","o","u"," ","c","o","m","e"," ","t","o"," ","m","e"," ","b","e","c","a","u","s","e"," ","y","o","u"," ","a","r","e"],["?","H","o","w"," ","l","o","n","g"," ","h","a","v","e"," ","y","o","u"," ","b","e","e","n"],["?","D","o"," ","y","o","u"," ","b","e","l","i","e","v","e"," ","i","t"," ","i","s"," ","n","o","r","m","a","l"," ","t","o"," ","b","e"],["?","D","o"," ","y","o","u"," ","e","n","j","o","y"," ","b","e","i","n","g"]]],[["I","'","M"], [["?","D","i","d"," ","y","o","u"," ","c","o","m","e"," ","t","o"," ","m","e"," ","b","e","c","a","u","s","e"," ","y","o","u"," ","a","r","e"],["?","H","o","w"," ","l","o","n","g"," ","h","a","v","e"," ","y","o","u"," ","b","e","e","n"],["?","D","o"," ","y","o","u"," ","b","e","l","i","e","v","e"," ","i","t"," ","i","s"," ","n","o","r","m","a","l"," ","t","o"," ","b","e"],["?","D","o"," ","y","o","u"," ","e","n","j","o","y"," ","b","e","i","n","g"]]],[["Y","O","U"], [["W","e"," ","w","e","r","e"," ","d","i","s","c","u","s","s","i","n","g"," ","y","o","u"," ","-","-","n","o","t"," ","m","e","."],["?","O","h",","],["Y","o","u","'","r","e"," ","n","o","t"," ","r","e","a","l","l","y"," ","t","a","l","k","i","n","g"," ","a","b","o","u","t"," ","m","e",","," ","a","r","e"," ","y","o","u","?"]]],[["Y","E","S"], [["Y","o","u"," ","s","e","e","m"," ","q","u","i","t","e"," ","p","o","s","i","t","i","v","e","."],["A","r","e"," ","y","o","u"," ","S","u","r","e","?"],["I"," ","s","e","e","."],["I"," ","u","n","d","e","r","s","t","a","n","d","."]]],[["N","O"], [["A","r","e"," ","y","o","u"," ","s","a","y","i","n","g"," ","n","o"," ","j","u","s","t"," ","t","o"," ","b","e"," ","n","e","g","a","t","i","v","e","?"],["Y","o","u"," ","a","r","e"," ","b","e","i","n","g"," ","a"," ","b","i","t"," ","n","e","g","a","t","i","v","e","."],["W","h","y"," ","n","o","t","?"],["A","r","e"," ","y","o","u"," ","s","u","r","e","?"],["W","h","y"," ","n","o","?"]]],[["C","O","M","P","U","T","E","R"], [["D","o"," ","c","o","m","p","u","t","e","r","s"," ","w","o","r","r","y"," ","y","o","u","?"],["A","r","e"," ","y","o","u"," ","t","a","l","k","i","n","g"," ","a","b","o","u","t"," ","m","e"," ","i","n"," ","p","a","r","t","i","c","u","l","a","r","?"],["A","r","e"," ","y","o","u"," ","f","r","i","g","h","t","e","n","e","d"," ","b","y"," ","m","a","c","h","i","n","e","s","?"],["W","h","y"," ","d","o"," ","y","o","u"," ","m","e","n","t","i","o","n"," ","c","o","m","p","u","t","e","r","s","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k"," ","m","a","c","h","i","n","e","s"," ","h","a","v","e"," ","t","o"," ","d","o"," ","w","i","t","h"," ","y","o","u","r"," ","p","r","o","b","l","e","m","s","?"],["D","o","n","'","t"," ","y","o","u"," ","t","h","i","n","k"," ","c","o","m","p","u","t","e","r","s"," ","c","a","n"," ","h","e","l","p"," ","p","e","o","p","l","e","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","a","b","o","u","t"," ","m","a","c","h","i","n","e","s"," ","t","h","a","t"," ","w","o","r","r","i","e","s"," ","y","o","u","?"]]],[["C","O","M","P","U","T","E","R","S"], [["D","o"," ","c","o","m","p","u","t","e","r","s"," ","w","o","r","r","y"," ","y","o","u","?"],["A","r","e"," ","y","o","u"," ","t","a","l","k","i","n","g"," ","a","b","o","u","t"," ","m","e"," ","i","n"," ","p","a","r","t","i","c","u","l","a","r","?"],["A","r","e"," ","y","o","u"," ","f","r","i","g","h","t","e","n","e","d"," ","b","y"," ","m","a","c","h","i","n","e","s","?"],["W","h","y"," ","d","o"," ","y","o","u"," ","m","e","n","t","i","o","n"," ","c","o","m","p","u","t","e","r","s","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k"," ","m","a","c","h","i","n","e","s"," ","h","a","v","e"," ","t","o"," ","d","o"," ","w","i","t","h"," ","y","o","u","r"," ","p","r","o","b","l","e","m","s","?"],["D","o","n","'","t"," ","y","o","u"," ","t","h","i","n","k"," ","c","o","m","p","u","t","e","r","s"," ","c","a","n"," ","h","e","l","p"," ","p","e","o","p","l","e","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","a","b","o","u","t"," ","m","a","c","h","i","n","e","s"," ","t","h","a","t"," ","w","o","r","r","i","e","s"," ","y","o","u","?"]]],[["I"," ","W","A","N","T"], [["?","W","h","y"," ","d","o"," ","y","o","u"," ","w","a","n","t"],["?","W","h","a","t"," ","w","o","u","l","d"," ","i","t"," ","m","e","a","n"," ","t","o"," ","y","o","u"," ","i","f"," ","y","o","u"," ","g","o","t"],["?","S","u","p","p","o","s","e"," ","y","o","u"," ","g","o","t"],["?","W","h","a","t"," ","i","f"," ","y","o","u"," ","n","e","v","e","r"," ","g","o","t"],[".","I"," ","s","o","m","e","t","i","m","e","s"," ","a","l","s","o"," ","w","a","n","t"]]],[["W","H","A","T"], [["W","h","y"," ","d","o"," ","y","o","u"," ","a","s","k","?"],["D","o","e","s"," ","t","h","a","t"," ","q","u","e","s","t","i","o","n"," ","i","n","t","e","r","e","s","t"," ","y","o","u","?"],["W","h","a","t"," ","a","n","s","w","e","r"," ","w","o","u","l","d"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","h","e"," ","m","o","s","t","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k","?"],["A","r","e"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","o","n"," ","y","o","u","r"," ","m","i","n","d"," ","o","f","t","e","n","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","t","h","a","t"," ","y","o","u"," ","r","e","a","l","l","y"," ","w","a","n","t"," ","t","o"," ","k","n","o","w","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","a","n","y","o","n","e"," ","e","l","s","e","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","b","e","f","o","r","e","?"],["W","h","a","t"," ","e","l","s","e"," ","c","o","m","e","s"," ","t","o"," ","m","i","n","d"," ","w","h","e","n"," ","y","o","u"," ","a","s","k"," ","t","h","a","t","?"]]],[["H","O","W"], [["W","h","y"," ","d","o"," ","y","o","u"," ","a","s","k","?"],["D","o","e","s"," ","t","h","a","t"," ","q","u","e","s","t","i","o","n"," ","i","n","t","e","r","e","s","t"," ","y","o","u","?"],["W","h","a","t"," ","a","n","s","w","e","r"," ","w","o","u","l","d"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","h","e"," ","m","o","s","t","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k","?"],["A","r","e"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","o","n"," ","y","o","u","r"," ","m","i","n","d"," ","o","f","t","e","n","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","t","h","a","t"," ","y","o","u"," ","r","e","a","l","l","y"," ","w","a","n","t"," ","t","o"," ","k","n","o","w","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","a","n","y","o","n","e"," ","e","l","s","e","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","b","e","f","o","r","e","?"],["W","h","a","t"," ","e","l","s","e"," ","c","o","m","e","s"," ","t","o"," ","m","i","n","d"," ","w","h","e","n"," ","y","o","u"," ","a","s","k"," ","t","h","a","t","?"]]],[["W","H","O"], [["W","h","y"," ","d","o"," ","y","o","u"," ","a","s","k","?"],["D","o","e","s"," ","t","h","a","t"," ","q","u","e","s","t","i","o","n"," ","i","n","t","e","r","e","s","t"," ","y","o","u","?"],["W","h","a","t"," ","a","n","s","w","e","r"," ","w","o","u","l","d"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","h","e"," ","m","o","s","t","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k","?"],["A","r","e"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","o","n"," ","y","o","u","r"," ","m","i","n","d"," ","o","f","t","e","n","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","t","h","a","t"," ","y","o","u"," ","r","e","a","l","l","y"," ","w","a","n","t"," ","t","o"," ","k","n","o","w","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","a","n","y","o","n","e"," ","e","l","s","e","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","b","e","f","o","r","e","?"],["W","h","a","t"," ","e","l","s","e"," ","c","o","m","e","s"," ","t","o"," ","m","i","n","d"," ","w","h","e","n"," ","y","o","u"," ","a","s","k"," ","t","h","a","t","?"]]],[["W","H","E","R","E"], [["W","h","y"," ","d","o"," ","y","o","u"," ","a","s","k","?"],["D","o","e","s"," ","t","h","a","t"," ","q","u","e","s","t","i","o","n"," ","i","n","t","e","r","e","s","t"," ","y","o","u","?"],["W","h","a","t"," ","a","n","s","w","e","r"," ","w","o","u","l","d"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","h","e"," ","m","o","s","t","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k","?"],["A","r","e"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","o","n"," ","y","o","u","r"," ","m","i","n","d"," ","o","f","t","e","n","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","t","h","a","t"," ","y","o","u"," ","r","e","a","l","l","y"," ","w","a","n","t"," ","t","o"," ","k","n","o","w","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","a","n","y","o","n","e"," ","e","l","s","e","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","b","e","f","o","r","e","?"],["W","h","a","t"," ","e","l","s","e"," ","c","o","m","e","s"," ","t","o"," ","m","i","n","d"," ","w","h","e","n"," ","y","o","u"," ","a","s","k"," ","t","h","a","t","?"]]],[["W","H","E","N"], [["W","h","y"," ","d","o"," ","y","o","u"," ","a","s","k","?"],["D","o","e","s"," ","t","h","a","t"," ","q","u","e","s","t","i","o","n"," ","i","n","t","e","r","e","s","t"," ","y","o","u","?"],["W","h","a","t"," ","a","n","s","w","e","r"," ","w","o","u","l","d"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","h","e"," ","m","o","s","t","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k","?"],["A","r","e"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","o","n"," ","y","o","u","r"," ","m","i","n","d"," ","o","f","t","e","n","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","t","h","a","t"," ","y","o","u"," ","r","e","a","l","l","y"," ","w","a","n","t"," ","t","o"," ","k","n","o","w","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","a","n","y","o","n","e"," ","e","l","s","e","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","b","e","f","o","r","e","?"],["W","h","a","t"," ","e","l","s","e"," ","c","o","m","e","s"," ","t","o"," ","m","i","n","d"," ","w","h","e","n"," ","y","o","u"," ","a","s","k"," ","t","h","a","t","?"]]],[["N","A","M","E"], [["N","a","m","e","s"," ","d","o","n","'","t"," ","i","n","t","e","r","e","s","t"," ","m","e","."],["I"," ","d","o","n","'","t"," ","c","a","r","e"," ","a","b","o","u","t"," ","n","a","m","e","s"," ","-","-","p","l","e","a","s","e"," ","g","o"," ","o","n","."]]],[["W","H","Y"], [["W","h","y"," ","d","o"," ","y","o","u"," ","a","s","k","?"],["D","o","e","s"," ","t","h","a","t"," ","q","u","e","s","t","i","o","n"," ","i","n","t","e","r","e","s","t"," ","y","o","u","?"],["W","h","a","t"," ","a","n","s","w","e","r"," ","w","o","u","l","d"," ","p","l","e","a","s","e"," ","y","o","u"," ","t","h","e"," ","m","o","s","t","?"],["W","h","a","t"," ","d","o"," ","y","o","u"," ","t","h","i","n","k","?"],["A","r","e"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","o","n"," ","y","o","u","r"," ","m","i","n","d"," ","o","f","t","e","n","?"],["W","h","a","t"," ","i","s"," ","i","t"," ","t","h","a","t"," ","y","o","u"," ","r","e","a","l","l","y"," ","w","a","n","t"," ","t","o"," ","k","n","o","w","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","a","n","y","o","n","e"," ","e","l","s","e","?"],["H","a","v","e"," ","y","o","u"," ","a","s","k","e","d"," ","s","u","c","h"," ","q","u","e","s","t","i","o","n","s"," ","b","e","f","o","r","e","?"],["W","h","a","t"," ","e","l","s","e"," ","c","o","m","e","s"," ","t","o"," ","m","i","n","d"," ","w","h","e","n"," ","y","o","u"," ","a","s","k"," ","t","h","a","t","?"]]],[["C","A","U","S","E"], [["I","s"," ","t","h","a","t"," ","t","h","e"," ","r","e","a","l"," ","r","e","a","s","o","n","?"],["D","o","n","'","t"," ","a","n","y"," ","o","t","h","e","r"," ","r","e","a","s","o","n","s"," ","c","o","m","e"," ","t","o"," ","m","i","n","d","?"],["D","o","e","s"," ","t","h","a","t"," ","r","e","a","s","o","n"," ","e","x","p","l","a","i","n"," ","a","n","y","t","h","i","n","g"," ","e","l","s","e","?"],["W","h","a","t"," ","o","t","h","e","r"," ","r","e","a","s","o","n","s"," ","m","i","g","h","t"," ","t","h","e","r","e"," ","b","e","?"]]],[["B","E","C","A","U","S","E"], [["I","s"," ","t","h","a","t"," ","t","h","e"," ","r","e","a","l"," ","r","e","a","s","o","n","?"],["D","o","n","'","t"," ","a","n","y"," ","o","t","h","e","r"," ","r","e","a","s","o","n","s"," ","c","o","m","e"," ","t","o"," ","m","i","n","d","?"],["D","o","e","s"," ","t","h","a","t"," ","r","e","a","s","o","n"," ","e","x","p","l","a","i","n"," ","a","n","y","t","h","i","n","g"," ","e","l","s","e","?"],["W","h","a","t"," ","o","t","h","e","r"," ","r","e","a","s","o","n","s"," ","m","i","g","h","t"," ","t","h","e","r","e"," ","b","e","?"]]],[["D","R","E","A","M"], [["W","h","a","t"," ","d","o","e","s"," ","t","h","a","t"," ","d","r","e","a","m"," ","s","u","g","g","e","s","t"," ","t","o"," ","y","o","u","?"],["D","o"," ","y","o","u"," ","d","r","e","a","m"," ","o","f","t","e","n","?"],["W","h","a","t"," ","p","e","r","s","o","n","s"," ","a","p","p","e","a","r"," ","i","n"," ","y","o","u","r"," ","d","r","e","a","m","s","?"],["A","r","e"," ","y","o","u"," ","d","i","s","t","u","r","b","e","d"," ","b","y"," ","y","o","u","r"," ","d","r","e","a","m","s","?"]]],[["S","O","R","R","Y"], [["P","l","e","a","s","e"," ","d","o","n","'","t"," ","a","p","o","l","o","g","i","s","e","!"],["A","p","o","l","o","g","i","e","s"," ","a","r","e"," ","n","o","t"," ","n","e","c","e","s","s","a","r","y","."],["W","h","a","t"," ","f","e","e","l","i","n","g","s"," ","d","o"," ","y","o","u"," ","h","a","v","e"," ","w","h","e","n"," ","y","o","u"," ","a","p","o","l","o","g","i","s","e","?"],["D","o","n","'","t"," ","b","e"," ","s","o"," ","d","e","f","e","n","s","i","v","e","!"]]],[["H","I"], [["H","o","w"," ","d","o"," ","y","o","u",".",".",".","p","l","e","a","s","e"," ","s","t","a","t","e"," ","y","o","u","r"," ","p","r","o","b","l","e","m","."]]],[["D","R","E","A","M","S"], [["W","h","a","t"," ","d","o","e","s"," ","t","h","a","t"," ","d","r","e","a","m"," ","s","u","g","g","e","s","t"," ","t","o"," ","y","o","u","?"],["D","o"," ","y","o","u"," ","d","r","e","a","m"," ","o","f","t","e","n","?"],["W","h","a","t"," ","p","e","r","s","o","n","s"," ","a","p","p","e","a","r"," ","i","n"," ","y","o","u","r"," ","d","r","e","a","m","s","?"],["A","r","e"," ","y","o","u"," ","d","i","s","t","u","r","b","e","d"," ","b","y"," ","y","o","u","r"," ","d","r","e","a","m","s","?"]]],[["M","A","Y","B","E"], [["Y","o","u"," ","d","o","n","'","t"," ","s","e","e","m"," ","q","u","i","t","e"," ","c","e","r","t","a","i","n","."],["W","h","y"," ","t","h","e"," ","u","n","c","e","r","t","a","i","n"," ","t","o","n","e","?"],["C","a","n","'","t"," ","y","o","u"," ","b","e"," ","m","o","r","e"," ","p","o","s","i","t","i","v","e","?"],["Y","o","u"," ","a","r","e","n","'","t"," ","s","u","r","e","?"],["D","o","n","'","t"," ","y","o","u"," ","k","n","o","w","?"]]],[["H","E","L","L","O"], [["H","o","w"," ","d","o"," ","y","o","u",".",".",".","p","l","e","a","s","e"," ","s","t","a","t","e"," ","y","o","u","r"," ","p","r","o","b","l","e","m","."]]],[["A","L","W","A","Y","S"], [["C","a","n"," ","y","o","u"," ","t","h","i","n","k"," ","o","f"," ","a"," ","s","p","e","c","i","f","i","c"," ","e","x","a","m","p","l","e","?"],["W","h","e","n","?"],["W","h","a","t"," ","a","r","e"," ","y","o","u"," ","t","h","i","n","k","i","n","g"," ","o","f","?"],["R","e","a","l","l","y",","," ","a","l","w","a","y","s","?"]]],[["Y","O","U","R"], [["?","W","h","y"," ","a","r","e"," ","y","o","u"," ","c","o","n","c","e","r","n","e","d"," ","a","b","o","u","t"," ","m","y"],["?","W","h","a","t"," ","a","b","o","u","t"," ","y","o","u","r"," ","o","w","n"]]],[["A","L","I","K","E"], [["I","n"," ","w","h","a","t"," ","w","a","y","?"],["W","h","a","t"," ","r","e","s","e","m","b","l","e","n","c","e"," ","d","o"," ","y","o","u"," ","s","e","e","?"],["W","h","a","t"," ","d","o","e","s"," ","t","h","e"," ","s","i","m","i","l","a","r","i","t","y"," ","s","u","g","g","e","s","t"," ","t","o"," ","y","o","u","?"],["W","h","a","t"," ","o","t","h","e","r"," ","c","o","n","n","e","c","t","i","o","n","s"," ","d","o"," ","y","o","u"," ","s","e","e","?"],["C","o","u","n","d"," ","t","h","e","r","e"," ","r","e","a","l","l","y"," ","b","e"," ","s","o","m","e"," ","c","o","n","n","e","c","t","i","o","n","?"],["H","o","w","?"]]],[["T","H","I","N","K"], [["D","o"," ","y","o","u"," ","r","e","a","l","l","y"," ","t","h","i","n","k"," ","s","o","?"],["?","B","u","t"," ","y","o","u"," ","a","r","e"," ","n","o","t"," ","s","u","r","e"," ","y","o","u"],["?","D","o"," ","y","o","u"," ","d","o","u","b","t"," ","y","o","u"]]],[["F","R","I","E","N","D","S"], [["W","h","y"," ","d","o"," ","y","o","u"," ","b","r","i","n","g"," ","u","p"," ","t","h","e"," ","t","o","p","i","c"," ","o","f"," ","f","r","i","e","n","d","s","?"],["D","o"," ","y","o","u","r"," ","f","r","i","e","n","d","s"," ","w","o","r","r","y"," ","y","o","u","?"],["D","o"," ","y","o","u","r"," ","f","r","i","e","n","d","s"," ","p","i","c","k"," ","o","n"," ","y","o","u","?"],["A","r","e"," ","y","o","u"," ","s","u","r","e"," ","y","o","u"," ","h","a","v","e"," ","a","n","y"," ","f","r","i","e","n","d","s","?"],["D","o"," ","y","o","u"," ","i","m","p","o","s","e"," ","o","n"," ","y","o","u","r"," ","f","r","i","e","n","d","s","?"],["P","e","r","h","a","p","s"," ","y","o","u","r"," ","l","o","v","e"," ","f","o","r"," ","f","r","i","e","n","d","s"," ","w","o","r","r","i","e","s"," ","y","o","u","."]]],[["F","R","I","E","N","D"], [["W","h","y"," ","d","o"," ","y","o","u"," ","b","r","i","n","g"," ","u","p"," ","t","h","e"," ","t","o","p","i","c"," ","o","f"," ","f","r","i","e","n","d","s","?"],["D","o"," ","y","o","u","r"," ","f","r","i","e","n","d","s"," ","w","o","r","r","y"," ","y","o","u","?"],["D","o"," ","y","o","u","r"," ","f","r","i","e","n","d","s"," ","p","i","c","k"," ","o","n"," ","y","o","u","?"],["A","r","e"," ","y","o","u"," ","s","u","r","e"," ","y","o","u"," ","h","a","v","e"," ","a","n","y"," ","f","r","i","e","n","d","s","?"],["D","o"," ","y","o","u"," ","i","m","p","o","s","e"," ","o","n"," ","y","o","u","r"," ","f","r","i","e","n","d","s","?"],["P","e","r","h","a","p","s"," ","y","o","u","r"," ","l","o","v","e"," ","f","o","r"," ","f","r","i","e","n","d","s"," ","w","o","r","r","i","e","s"," ","y","o","u","."]]],[[], [["I","'","m"," ","n","o","t"," ","s","u","r","e"," ","I"," ","u","n","d","e","r","s","t","a","n","d"," ","y","o","u"," ","f","u","l","l","y","."],["W","h","a","t"," ","d","o","e","s"," ","t","h","a","t"," ","s","u","g","g","e","s","t"," ","t","o"," ","y","o","u","?"],["I"," ","s","e","e","."],["C","a","n"," ","y","o","u"," ","e","l","a","b","o","r","a","t","e"," ","o","n"," ","t","h","a","t","?"],["S","a","y",","," ","d","o"," ","y","o","u"," ","h","a","v","e"," ","a","n","y"," ","p","s","y","c","h","o","l","o","g","i","c","a","l"," ","p","r","o","b","l","e","m","s","?"]]]] val initial = fun lscomp(ls) = if ls is Nil then Nil [k, rs] :: t then [eliza.words(k), eliza.cycle(rs)] :: lscomp(t) [lscomp(eliza.respMsgs), eliza.cycle(eliza.repeatMsgs)] //│ initial = [[[[["C","A","N"],["Y","O","U"]], Lazy([function])],[[["C","A","N"],["I"]], Lazy([function])],[[["Y","O","U"],["A","R","E"]], Lazy([function])],[[["Y","O","U","'","R","E"]], Lazy([function])],[[["I"],["D","O","N","'","T"]], Lazy([function])],[[["I"],["F","E","E","L"]], Lazy([function])],[[["W","H","Y"],["D","O","N","'","T"],["Y","O","U"]], Lazy([function])],[[["W","H","Y"],["C","A","N","'","T"],["I"]], Lazy([function])],[[["A","R","E"],["Y","O","U"]], Lazy([function])],[[["I"],["C","A","N","'","T"]], Lazy([function])],[[["I"],["A","M"]], Lazy([function])],[[["I","'","M"]], Lazy([function])],[[["Y","O","U"]], Lazy([function])],[[["Y","E","S"]], Lazy([function])],[[["N","O"]], Lazy([function])],[[["C","O","M","P","U","T","E","R"]], Lazy([function])],[[["C","O","M","P","U","T","E","R","S"]], Lazy([function])],[[["I"],["W","A","N","T"]], Lazy([function])],[[["W","H","A","T"]], Lazy([function])],[[["H","O","W"]], Lazy([function])],[[["W","H","O"]], Lazy([function])],[[["W","H","E","R","E"]], Lazy([function])],[[["W","H","E","N"]], Lazy([function])],[[["N","A","M","E"]], Lazy([function])],[[["W","H","Y"]], Lazy([function])],[[["C","A","U","S","E"]], Lazy([function])],[[["B","E","C","A","U","S","E"]], Lazy([function])],[[["D","R","E","A","M"]], Lazy([function])],[[["S","O","R","R","Y"]], Lazy([function])],[[["H","I"]], Lazy([function])],[[["D","R","E","A","M","S"]], Lazy([function])],[[["M","A","Y","B","E"]], Lazy([function])],[[["H","E","L","L","O"]], Lazy([function])],[[["A","L","W","A","Y","S"]], Lazy([function])],[[["Y","O","U","R"]], Lazy([function])],[[["A","L","I","K","E"]], Lazy([function])],[[["T","H","I","N","K"]], Lazy([function])],[[["F","R","I","E","N","D","S"]], Lazy([function])],[[["F","R","I","E","N","D"]], Lazy([function])],[[], Lazy([function])]], Lazy([function])] fun prefix(xxs, yys) = if xxs is Nil then true x :: xs and force(yys) is LzNil then false LzCons(y, ys) then listEq(x, y) && prefix(xs, ys) fun tails(xs) = lazy of () => if xs is Nil then LzNil xss then LzCons(xss, tails(tail(xss))) fun ucase(ls) = map(toUpper, ls) val conjugates = let oneways = [nofibStringToList("me"), nofibStringToList("you")] :: Nil let bothways = [nofibStringToList("are"), nofibStringToList("am")] :: [nofibStringToList("we're"), nofibStringToList("was")] :: [nofibStringToList("you"), nofibStringToList("I")] :: [nofibStringToList("your"), nofibStringToList("my")] :: [nofibStringToList("I've"), nofibStringToList("you've")] :: [nofibStringToList("I'm"), nofibStringToList("you're")] :: Nil fun prepare(ls) = map(case { [w, r] then [ucase(w), r] }, ls) fun lscomp(ls) = if ls is Nil then Nil [x, y] :: t then ([x, y] :: [y, x] :: Nil) :: lscomp(t) prepare(oneways +: concat(lscomp(bothways))) //│ conjugates = [[["M","E"], ["y","o","u"]],[["A","R","E"], ["a","m"]],[["A","M"], ["a","r","e"]],[["W","E","'","R","E"], ["w","a","s"]],[["W","A","S"], ["w","e","'","r","e"]],[["Y","O","U"], ["I"]],[["I"], ["y","o","u"]],[["Y","O","U","R"], ["m","y"]],[["M","Y"], ["y","o","u","r"]],[["I","'","V","E"], ["y","o","u","'","v","e"]],[["Y","O","U","'","V","E"], ["I","'","v","e"]],[["I","'","M"], ["y","o","u","'","r","e"]],[["Y","O","U","'","R","E"], ["I","'","m"]]] fun conjug(d, w) = fun maybe(d, xs) = if null_(xs) then d else xs fun conj(w) = fun lscomp(ls) = if ls is Nil then Nil [w_, m] :: t and listEq(ucase(w), w_) then m :: lscomp(t) else lscomp(t) head(lscomp(conjugates) +: w :: Nil) fun trailingI(ls) = fun cons(x, xs) = if listEq(x, nofibStringToList("I")) && null_(xs) then nofibStringToList("me") :: Nil else x :: xs foldr(cons, Nil, ls) unwords(trailingI(map(conj, maybe(d, w)))) fun replies(key, l) = map_lz of x => conjug(l, leave(listLen(key), x)) filter_lz of ls => prefix(key, lz_map(ucase, ls)) tails(l) fun answer(st, l) = fun cons(e, r_es) = if r_es is [r, es] then [r, e :: es] fun ans(e_es, l) = if e_es is [key, a_as] :: es and force(a_as) is LzCons(a, as_) then let rs = replies(key, l) if null_lz(rs) then cons([key, a_as], ans(es, l)) else [makeResponse(a, head_lz(rs)), [key, as_] :: es] if ans(keyTabOf(st), l) is [response, kt] then [response, newKeyTab(kt, st)] fun session(rs, prev, ls) = if ls is Nil then Nil l :: ls and (if listEqBy(listEq, prev, l) then repeated(rs) else answer(rs, l)) is [response, rs_] then response +: nofibStringToList("\n\n") +: session(rs_, l, ls) fun testEliza_nofib(n) = let input = Predef.id of nofibStringToList("Are we alone?") :: nofibStringToList("That the Roswell event was actually an alien encounter. Do you agreed?") :: nofibStringToList("But why not talk about you, its more fun.") :: nofibStringToList("I dont ask, you do") :: nofibStringToList("do ray me") :: nofibStringToList("Nop, thats because your a computer") :: nofibStringToList("you dont") :: nofibStringToList("Oh, a paranoid computer, ehh?") :: nofibStringToList("Tell me about *your* mother") :: nofibStringToList("No, what what was she like?") :: nofibStringToList("I'm asking questions, not you") :: nofibStringToList("no") :: nofibStringToList("yes") :: nofibStringToList("but I'm not") :: Nil map of i => session of initial Nil filter of x => not(null_(x)) map(x => eliza.words(trim(x)), take(intMod(i, 20), input)) enumFromTo(1, n) fun main() = map(x => nofibListToString(x), testEliza_nofib(20)).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/fish.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module fish with ... fun vec_add(v1, v2) = if v1 is [x1, y1] and v2 is [x2, y2] then [x1 + x2, y1 + y2] fun vec_sub(v1, v2) = if v1 is [x1, y1] and v2 is [x2, y2] then [x1 - x2, y1 - y2] fun scale_vec2(v, a, b) = if v is [x, y] then [intDiv(x * a, b), intDiv(y * a, b)] let p_tile = [0, 3, 3, 4] :: [3, 4, 0, 8] :: [0, 8, 0, 3] :: [6, 0, 4, 4] :: [4, 5, 4, 10] :: [4, 10, 7, 6] :: [7, 6, 4, 5] :: [11, 0, 10, 4] :: [10, 4, 9, 6] :: [9, 6, 8, 8] :: [8, 8, 4, 13] :: [4, 13, 0, 16] :: [0, 16, 6, 15] :: [6, 15, 8, 16] :: [8, 16, 12, 12] :: [12, 12, 16, 12] :: [10, 16, 12, 14] :: [12, 14, 16, 13] :: [12, 16, 13, 15] :: [13, 15, 16, 14] :: [14, 16, 16, 15] :: [8, 12, 16, 10] :: [8, 8, 12, 9] :: [12, 9, 16, 8] :: [9, 6, 12, 7] :: [12, 7, 16, 6] :: [10, 4, 13, 5] :: [13, 5, 16, 4] :: [11, 0, 14, 2] :: [14, 2, 16, 2] :: Nil //│ p_tile = [[0, 3, 3, 4],[3, 4, 0, 8],[0, 8, 0, 3],[6, 0, 4, 4],[4, 5, 4, 10],[4, 10, 7, 6],[7, 6, 4, 5],[11, 0, 10, 4],[10, 4, 9, 6],[9, 6, 8, 8],[8, 8, 4, 13],[4, 13, 0, 16],[0, 16, 6, 15],[6, 15, 8, 16],[8, 16, 12, 12],[12, 12, 16, 12],[10, 16, 12, 14],[12, 14, 16, 13],[12, 16, 13, 15],[13, 15, 16, 14],[14, 16, 16, 15],[8, 12, 16, 10],[8, 8, 12, 9],[12, 9, 16, 8],[9, 6, 12, 7],[12, 7, 16, 6],[10, 4, 13, 5],[13, 5, 16, 4],[11, 0, 14, 2],[14, 2, 16, 2]] let q_tile = [0, 8, 4, 7] :: [4, 7, 6, 7] :: [6, 7, 8, 8] :: [8, 8, 12, 10] :: [12, 10, 16, 16] :: [0, 12, 3, 13] :: [3, 13, 5, 14] :: [5, 14, 7, 15] :: [7, 15, 8, 16] :: [2, 16, 3, 13] :: [4, 16, 5, 14] :: [6, 16, 7, 15] :: [0, 10, 7, 11] :: [9, 13, 8, 15] :: [8, 15, 11, 15] :: [11, 15, 9, 13] :: [10, 10, 8, 12] :: [8, 12, 12, 12] :: [12, 12, 10, 10] :: [2, 0, 4, 5] :: [4, 5, 4, 7] :: [4, 0, 6, 5] :: [6, 5, 6, 7] :: [6, 0, 8, 5] :: [8, 5, 8, 8] :: [10, 0, 14, 11] :: [12, 0, 13, 4] :: [13, 4, 16, 8] :: [16, 8, 15, 10] :: [15, 10, 16, 16] :: [13, 0, 16, 6] :: [14, 0, 16, 4] :: [15, 0, 16, 2] :: [0, 0, 8, 0] :: [12, 0, 16, 0] :: [0, 0, 0, 8] :: [0, 12, 0, 16] :: Nil //│ q_tile = [[0, 8, 4, 7],[4, 7, 6, 7],[6, 7, 8, 8],[8, 8, 12, 10],[12, 10, 16, 16],[0, 12, 3, 13],[3, 13, 5, 14],[5, 14, 7, 15],[7, 15, 8, 16],[2, 16, 3, 13],[4, 16, 5, 14],[6, 16, 7, 15],[0, 10, 7, 11],[9, 13, 8, 15],[8, 15, 11, 15],[11, 15, 9, 13],[10, 10, 8, 12],[8, 12, 12, 12],[12, 12, 10, 10],[2, 0, 4, 5],[4, 5, 4, 7],[4, 0, 6, 5],[6, 5, 6, 7],[6, 0, 8, 5],[8, 5, 8, 8],[10, 0, 14, 11],[12, 0, 13, 4],[13, 4, 16, 8],[16, 8, 15, 10],[15, 10, 16, 16],[13, 0, 16, 6],[14, 0, 16, 4],[15, 0, 16, 2],[0, 0, 8, 0],[12, 0, 16, 0],[0, 0, 0, 8],[0, 12, 0, 16]] let r_tile = [0, 0, 8, 8] :: [12, 12, 16, 16] :: [0, 4, 5, 10] :: [0, 8, 2, 12] :: [0, 12, 1, 14] :: [16, 6, 11, 10] :: [11, 10, 6, 16] :: [16, 4, 14, 6] :: [14, 6, 8, 8] :: [8, 8, 5, 10] :: [5, 10, 2, 12] :: [2, 12, 0, 16] :: [16, 8, 12, 12] :: [12, 12, 11, 16] :: [1, 1, 4, 0] :: [2, 2, 8, 0] :: [3, 3, 8, 2] :: [8, 2, 12, 0] :: [5, 5, 12, 3] :: [12, 3, 16, 0] :: [11, 16, 12, 12] :: [12, 12, 16, 8] :: [13, 13, 16, 10] :: [14, 14, 16, 12] :: [15, 15, 16, 14] :: Nil //│ r_tile = [[0, 0, 8, 8],[12, 12, 16, 16],[0, 4, 5, 10],[0, 8, 2, 12],[0, 12, 1, 14],[16, 6, 11, 10],[11, 10, 6, 16],[16, 4, 14, 6],[14, 6, 8, 8],[8, 8, 5, 10],[5, 10, 2, 12],[2, 12, 0, 16],[16, 8, 12, 12],[12, 12, 11, 16],[1, 1, 4, 0],[2, 2, 8, 0],[3, 3, 8, 2],[8, 2, 12, 0],[5, 5, 12, 3],[12, 3, 16, 0],[11, 16, 12, 12],[12, 12, 16, 8],[13, 13, 16, 10],[14, 14, 16, 12],[15, 15, 16, 14]] let s_tile = [0, 0, 4, 2] :: [4, 2, 8, 2] :: [8, 2, 16, 0] :: [0, 4, 2, 1] :: [0, 6, 7, 4] :: [0, 8, 8, 6] :: [0, 10, 7, 8] :: [0, 12, 7, 10] :: [0, 14, 7, 13] :: [13, 13, 16, 14] :: [14, 11, 16, 12] :: [15, 9, 16, 10] :: [16, 0, 10, 4] :: [10, 4, 8, 6] :: [8, 6, 7, 8] :: [7, 8, 7, 13] :: [7, 13, 8, 16] :: [12, 16, 13, 13] :: [13, 13, 14, 11] :: [14, 11, 15, 9] :: [15, 9, 16, 8] :: [10, 16, 11, 10] :: [12, 4, 10, 6] :: [10, 6, 12, 7] :: [12, 7, 12, 4] :: [15, 5, 13, 7] :: [13, 7, 15, 8] :: [15, 8, 15, 5] :: Nil //│ s_tile = [[0, 0, 4, 2],[4, 2, 8, 2],[8, 2, 16, 0],[0, 4, 2, 1],[0, 6, 7, 4],[0, 8, 8, 6],[0, 10, 7, 8],[0, 12, 7, 10],[0, 14, 7, 13],[13, 13, 16, 14],[14, 11, 16, 12],[15, 9, 16, 10],[16, 0, 10, 4],[10, 4, 8, 6],[8, 6, 7, 8],[7, 8, 7, 13],[7, 13, 8, 16],[12, 16, 13, 13],[13, 13, 14, 11],[14, 11, 15, 9],[15, 9, 16, 8],[10, 16, 11, 10],[12, 4, 10, 6],[10, 6, 12, 7],[12, 7, 12, 4],[15, 5, 13, 7],[13, 7, 15, 8],[15, 8, 15, 5]] fun nil(a, b, c) = Nil fun tup2(a_b, c_d) = if a_b is [a, b] and c_d is [c, d] then [a, b, c, d] fun grid(m, n, segments, a, b, c) = fun lscomp(ls) = if ls is Nil then Nil [x0, y0, x1, y1] :: t then tup2( vec_add(vec_add(a, scale_vec2(b, x0, m)), scale_vec2(c, y0, n)), vec_add(vec_add(a, scale_vec2(b, x1, m)), scale_vec2(c, y1, n)) ) :: lscomp(t) lscomp(segments) fun rot(p, a, b, c) = p(vec_add(a, b), c, vec_sub([0, 0], b)) fun beside(m, n, p, q, a, b, c) = p(a, scale_vec2(b, m, m + n), c) +: q(vec_add(a, scale_vec2(b, m, m + n)), scale_vec2(b, n, n + m), c) fun above(m, n, p, q, a, b, c) = p(vec_add(a, scale_vec2(c, n, m + n)), b, scale_vec2(c, m, n + m)) +: q(a, b, scale_vec2(c, n, m + n)) fun tile_to_grid(arg, arg2, arg3, arg4) = grid(16, 16, arg, arg2, arg3, arg4) fun p(arg, q6, q7) = tile_to_grid(p_tile, arg, q6, q7) fun q(arg, q6, q7) = tile_to_grid(q_tile, arg, q6, q7) fun r(arg, q6, q7) = tile_to_grid(r_tile, arg, q6, q7) fun s(arg, q6, q7) = tile_to_grid(s_tile, arg, q6, q7) fun quartet(a, b, c, d, arg, a6, a7) = above of 1 1 (p5, p6, p7) => beside(1, 1, a, b, p5, p6, p7) (p5, p6, p7) => beside(1, 1, c, d, p5, p6, p7) arg a6 a7 fun t(arg, q6, q7) = quartet(p, q, r, s, arg, q6, q7) fun cycle_(p1, arg, p3, p4) = quartet of p1 (a, b, c) => rot of (a, b, c) => rot of (a, b, c) => rot(p1, a, b, c) a b c a b c (a, b, c) => rot(p1, a, b, c) (a, b, c) => rot of (a, b, c) => rot(p1, a, b, c) a b c arg p3 p4 fun u(arg, p2, p3) = cycle_((a, b, c) => rot(q, a, b, c), arg, p2, p3) fun side1(arg, q6, q7) = quartet(nil, nil, (a, b, c) => rot(t, a, b, c), t, arg, q6, q7) fun side2(arg, q6, q7) = quartet(side1, side1, (a, b, c) => rot(t, a, b, c), t, arg, q6, q7) fun corner1(arg, q6, q7) = quartet(nil, nil, nil, u, arg, q6, q7) fun corner2(arg, q6, q7) = quartet(corner1, side1, (a, b, c) => rot(side1, a, b, c), u, arg, q6, q7) fun pseudocorner(arg, q6, q7) = quartet(corner2, side2, (a, b, c) => rot(side2, a, b, c), (a, b, c) => rot(t, a, b, c), arg, q6, q7) fun pseudolimit(arg, p2, p3) = cycle_(pseudocorner, arg, p2, p3) fun showFourTupleofInt(a_b_c_d) = if a_b_c_d is [a, b, c, d] then nofibStringToList("(") +: nofibStringToList(stringOfInt(a)) +: nofibStringToList(",") +: nofibStringToList(stringOfInt(b)) +: nofibStringToList(",") +: nofibStringToList(stringOfInt(c)) +: nofibStringToList(",") +: nofibStringToList(stringOfInt(d)) fun fmt(ls) = if ls is Nil then nofibStringToList("[]") x :: xs then fun showl(ls, s) = if ls is Nil then "]" :: s x :: xs then nofibStringToList(",|") +: showFourTupleofInt(x) +: showl(xs, s) nofibStringToList("[|") +: showFourTupleofInt(x) +: showl(xs, "") fun testFish_nofib(n) = map of i => let n = min(0, i) in (pseudolimit([0, 0], [640 + n, 0], [0, 640 + n])), enumFromTo(0, n) // With a non-recursive definition of `+:` in `NofibPrelude.mls`, // this won't stack overflow fun main() = testFish_nofib(1) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/gcd.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module gcd with ... fun g(u1u2u3, v1v2v3) = if u1u2u3 is [u1, u2, u3] and v1v2v3 is [v1, v2, v3] then if v3 == 0 then [u3, u1, u2] else if quotRem(u3, v3) is [q, r] then g([v1, v2, v3], [u1 - (q * v1), u2 - (q * v2), r]) fun gcdE(x, y) = if x == 0 then [y, 0, 1] else g([1, 0, x], [0, 1, y]) fun max_(ls) = if ls is x :: Nil then x x :: y :: xs then if x < y then max_(y :: xs) else max_(x :: xs) fun test(d) = let ns = enumFromTo(5000, 5000 + d) let ms = enumFromTo(10000, 10000 + d) fun lscomp1(p1) = if p1 is Nil then Nil h1 :: t1 then fun lscomp2(p2) = if p2 is Nil then lscomp1(t1) h2 :: t2 then [h1, h2] :: lscomp2(t2) lscomp2(ms) let tripls = map(case { [x, y] then [x, y, gcdE(x, y)] }, lscomp1(ns)) let rs = map(case { [d1, d2, [gg, u, v]] then abs(gg + u + v) }, tripls) max_(rs) fun testGcd_nofib(x) = test(x) // NOTE: original input 400 fun main() = testGcd_nofib(40) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/integer.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module integer with ... fun integerbench(op, astart, astep, alim, bstart, bstep, blim) = fun lscomp1(ls) = if ls is Nil then Nil a :: t1 then fun lscomp2(ls) = if ls is Nil then lscomp1(t1) b :: t2 then op(a, b) :: lscomp2(t2) lscomp2(enumFromThenTo(bstart, bstart + bstep, blim)) lscomp1(enumFromThenTo(astart, astart + astep, alim)) fun intbench(op, astart, astep, alim, bstart, bstep, blim) = fun lscomp1(ls) = if ls is Nil then Nil a :: t1 then fun lscomp2(ls) = if ls is Nil then lscomp1(t1) b :: t2 then op(a, b) :: lscomp2(t2) lscomp2(enumFromThenTo(bstart, bstart + bstep, blim)) lscomp1(enumFromThenTo(astart, astart + astep, alim)) fun runbench(jop, iop, opstr, astart, astep, alim, bstart, bstep, blim) = intbench(iop, astart, astep, alim, astart, astep, alim); integerbench(jop, astart, astep, alim, astart, astep, alim) fun runalltests(astart, astep, alim, bstart, bstep, blim) = fun z_add(a, b) = a + b fun z_sub(a, b) = a - b fun z_mul(a, b) = a * b fun z_div(a, b) = intDiv(a, b) fun z_mod(a, b) = intMod(a, b) fun z_equal(a, b) = a == b fun z_lt(a, b) = a < b fun z_leq(a, b) = a <= b fun z_gt(a, b) = a > b fun z_geq(a, b) = a >= b runbench((a, b) => z_add(a, b), (a, b) => a + b, "(+)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_sub(a, b), (a, b) => a - b, "(-)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_mul(a, b), (a, b) => a * b, "(*)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_div(a, b), (a, b) => intDiv(a, b), "div", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_mod(a, b), (a, b) => intMod(a, b), "mod", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_equal(a, b), (a, b) => a == b, "(==)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_lt(a, b), (a, b) => a < b, "(<)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_leq(a, b), (a, b) => a <= b, "(<=)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_gt(a, b), (a, b) => a > b, "(>)", astart, astep, alim, astart, astep, alim); runbench((a, b) => z_geq(a, b), (a, b) => a >= b, "(>=)", astart, astep, alim, astart, astep, alim) fun testInteger_nofib(n) = runalltests(-2100000000, n, 2100000000, -2100000000, n, -2100000000) // NOTE: original input -2100000000 14000001 2100000000 fun main() = testInteger_nofib(700000001).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/knights.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module knights with ... fun myIsDigit(c) = c.codePointAt(0) >= 48 && c.codePointAt(0) <= 57 fun intintComp(a_b, c_d) = if a_b is [a, b] and c_d is [c, d] then (a < c) || ((a === c) && (b < d)) fun intChessSetComp(a_b, c_d) = if a_b is [a, b] and c_d is [c, d] then a < c fun myInit(a_t) = if a_t is a :: Nil then Nil a :: t then a :: myInit(t) fun myLast(a_t) = fun go(h, t) = if t is Nil then h head :: t then go(head, t) if a_t is a :: t then go(a, t) fun quickSortIntInt(xs) = if xs is Nil then Nil x :: xs then fun lscomp1(ls) = if ls is Nil then Nil h :: t and intintComp(h, x) then h :: lscomp1(t) else lscomp1(t) fun lscomp2(ls) = if ls is Nil then Nil h :: t and not(intintComp(h, x)) then h :: lscomp2(t) else lscomp2(t) quickSortIntInt(lscomp1(xs)) +: (x :: quickSortIntInt(lscomp2(xs))) fun quickSortIntChessSet(xs) = if force(xs) is LzNil then lazy of () => LzNil LzCons(x, xs) then fun lscomp1(ls) = if force(ls) is LzNil then lazy of () => LzNil LzCons(h, t) and intChessSetComp(h, x) then lazy of () => LzCons(h, lscomp1(t)) else lscomp1(t) fun lscomp2(ls) = if force(ls) is LzNil then lazy of () => LzNil LzCons(h, t) and not(intChessSetComp(h, x)) then lazy of () => LzCons(h, lscomp2(t)) else lscomp2(t) append_lz_lz(quickSortIntChessSet(lscomp1(xs)), lazy of () => LzCons(x, quickSortIntChessSet(lscomp2(xs)))) fun sizeQueue(xs) = listLen(xs) fun emptyQueue(x) = listEq(x, Nil) fun removeBack(xs) = if xs is x :: Nil then Nil x :: xs then x :: removeBack(xs) fun removeFront(xs) = if xs is h :: t then t fun inquireBack(xs) = if xs is x :: Nil then x x :: xs then inquireBack(xs) fun inquireFront(h_t) = head(h_t) fun addAllBack(list, q) = q +: list fun addAllFront(list, q) = list +: q fun addBack(x, q) = q +: (x :: Nil) fun addFront(x, q) = x :: q val createQueue = Nil //│ createQueue = [] data class Board(a: Int, b: Int, c: Lazy[[Int, Int]], d: List[[Int, Int]]) fun createBoard(x, t) = Board(x, 1, (lazy of (() => t)), t :: Nil) fun sizeBoard(b) = if b is Board(a, _, _, _) then a fun noPieces(b) = if b is Board(_, n, _, _) then n fun addPiece(t, b) = if b is Board(s, n, f, ts) then Board(s, n + 1, f, t :: ts) fun deleteFirst(b) = if b is Board(s, n, f, ts) then let ts_ = myInit(ts) Board(s, n - 1, (lazy of () => myLast(ts_)), ts_) fun positionPiece(x, b) = if b is Board(_, n, _, ts) then atIndex(n - x, ts) fun lastPiece(b) = if b is Board(_, _, _, t :: ts) then t fun firstPiece(b) = if b is Board(_, _, f, _) then force(f) fun pieceAtTile(x, b) = if b is Board(_, _, _, ts) then fun find(x, xs) = if xs is Nil then throw Error("Tile not used") y :: xs then if eqTup2(x, y) then 1 + listLen(xs) else find(x, xs) find(x, ts) fun tup2InList(y, xs) = if xs is Nil then false x :: xs and eqTup2(y, x) then true else tup2InList(y, xs) fun isSquareFree(x, b) = if b is Board(_, _, _, ts) then not(tup2InList(x, ts)) fun assignMoveNo(t, size, z) = if t is Nil then Nil [x, y] :: t then [(y - 1) * size + x, z] :: assignMoveNo(t, size, z - 1) fun spaces(s, y) = fun logTen(x) = if x === 0 then 0 else 1 + logTen(intDiv(x, 10)) replicate((logTen(s) - logTen(y)) + 1, " ") fun printBoard(s, n, xs) = if xs is Nil and n > s * s then Nil intMod(n, s) != 0 then "*" :: (spaces(s * s, 1) +: printBoard(s, n + 1, Nil)) intMod(n, s) === 0 then nofibStringToList("*\n") +: printBoard(s, n + 1, Nil) else throw Error("printBoard empty list error") [i, j] :: xs and i === n and intMod(n, s) === 0 then nofibStringToList(stringOfInt(j)) +: nofibStringToList("\n") +: printBoard(s, n + 1, xs) i === n and intMod(n, s) != 0 then nofibStringToList(stringOfInt(j)) +: spaces(s * s, j) +: printBoard(s, n + 1, xs) intMod(n, s) != 0 then "*" :: (spaces(s * s, 1) +: printBoard(s, n + 1, [i, j] :: xs)) intMod(n, s) === 0 then nofibStringToList("*\n") +: printBoard(s, n + 1, [i, j] :: xs) else throw Error("printBoard non-empty list error") abstract class Direction: UL | UR | DL | DR | LU | LD | RU | RD object UL extends Direction UR extends Direction DL extends Direction DR extends Direction LU extends Direction LD extends Direction RU extends Direction RD extends Direction fun move(d, x_y) = if x_y is [x, y] and d is UL then [x - 1, y - 2] UR then [x + 1, y - 2] DL then [x - 1, y + 2] DR then [x + 1, y + 2] LU then [x - 2, y - 1] LD then [x - 2, y + 1] RU then [x + 2, y - 1] RD then [x + 2, y + 1] fun startTour(st, size) = if intMod(size, 2) === 0 then createBoard(size, st) else throw Error("Tour doesnt exist for odd size board") fun moveKnight(board, dir) = addPiece(move(dir, lastPiece(board)), board) fun canMoveTo(x_y, board) = if x_y is [x, y] then let sze = sizeBoard(board) let res = (x >= 1) and (x <= sze) and (y >= 1) and (y <= sze) and (isSquareFree(x_y, board)) res fun canMove(board, dir) = canMoveTo(move(dir, lastPiece(board)), board) fun canJumpFirst(board) = canMoveTo(firstPiece(board), deleteFirst(board)) fun tourFinished(board) = let sze = sizeBoard(board) (noPieces(board) === (sze * sze)) && canJumpFirst(board) fun possibleMoves(board) = fun lscomp(ls) = if ls is Nil then Nil x :: t and canMove(board, x) then x :: lscomp(t) else lscomp(t) let res = lscomp(UL :: UR :: DL :: DR :: LU :: LD :: RU :: RD :: Nil) res fun deadEnd(board) = listLen(possibleMoves(board)) === 0 fun allDescend(board) = map(b => moveKnight(board, b), possibleMoves(board)) fun descAndNo(board) = fun lscomp(ls) = if ls is Nil then lazy of () => LzNil x :: t then lazy of () => LzCons([listLen(possibleMoves(deleteFirst(x))), x], lscomp(t)) lscomp(allDescend(board)) fun singleDescend(board) = fun lscomp(ls) = if force(ls) is LzNil then Nil LzCons([y, x], t) and y === 1 then x :: lscomp(t) else lscomp(t) lscomp(descAndNo(board)) fun descendents(board) = if canJumpFirst(board) && deadEnd(addPiece(firstPiece(board), board)) then lazy of () => LzNil else let singles = singleDescend(board) let scrut = listLen(singles) let res = if scrut === 0 then map_lz(snd, quickSortIntChessSet(descAndNo(board))) 1 then if singles is h :: Nil then lazy of () => LzCons(h, lazy of () => LzNil) else throw Error("unreachable") else lazy of () => LzNil res fun showChessSet(b) = if b is Board(sze, n, f, ts) then let sortedTrail = quickSortIntInt(assignMoveNo(ts, sze, n)) printBoard(sze, 1, sortedTrail) fun root(sze) = fun lscomp1(ls) = if ls is Nil then lazy of () => LzNil h1 :: t1 then fun lscomp2(ls) = if ls is Nil then lscomp1(t1) h2 :: t2 then lazy of () => LzCons([h1, h2], lscomp2(t2)) lscomp2(enumFromTo(1, sze)) append_lz_lz( zip_lz_lz( repeat(1 - (sze * sze)), zipWith_lz_lz(startTour, lscomp1(enumFromTo(1, sze)), replicate_lz(sze * sze, sze)) ), lazy of () => LzNil ) fun grow(x_y) = if x_y is [x, y] then zip_lz_lz(repeat(x + 1), descendents(y)) fun isFinished(x_y) = if x_y is [x, y] then tourFinished(y) fun emptyQueue_lz(x) = force(x) is LzNil fun removeFront_lz(xs) = if force(xs) is LzCons(h, t) then t fun inquireFront_lz(h_t) = if force(h_t) is LzCons(h, t) then h fun addAllFront_lz(list, q) = append_lz_lz(list, q) fun depthSearch(q, growFn, finFn) = if emptyQueue_lz(q) then lazy of () => LzNil finFn(inquireFront_lz(q)) then lazy of () => LzCons(inquireFront_lz(q), depthSearch(removeFront_lz(q), growFn, finFn)) else depthSearch(addAllFront_lz(growFn(inquireFront_lz(q)), removeFront_lz(q)), growFn, finFn) fun printTour(ss) = fun strToInt(y, xs) = if xs is Nil then y x :: xs then strToInt((10 * y) + (x.codePointAt(0) - 48), xs) fun pp(xs) = if xs is Nil then Nil [x, y] :: xs then nofibStringToList("\nKnights tour with ") +: nofibStringToList(stringOfInt(x)) +: nofibStringToList(" backtracking moves\n") +: showChessSet(y) +: pp(xs) if map(x => strToInt(0, x), ss) is size :: number :: Nil then pp(take_lz(number, depthSearch(root(size), grow, isFinished))) else throw Error("printTour error") fun testKnights_nofib(ss) = let usageString = "\nUsage: knights \n" fun all_digits(s) = foldr((a, b) => (myIsDigit(a)) && b, true, s) fun argsOk(ss) = (listLen(ss) === 2) && (foldr((a, b) => (all_digits(a)) && b, true, ss)) if argsOk(ss) then (printTour(ss)) else throw Error(usageString) fun main() = nofibListToString(testKnights_nofib(nofibStringToList("8") :: nofibStringToList("1") :: Nil)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/lambda.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module lambda with ... fun lookup(k, t) = if t is Nil then None [x, v] :: t then if listEq(k, x) then Some(v) else lookup(k, t) data class MyState[S, A](r: S -> [S, A]) fun myRunState(m, s) = if m is MyState(f) then f(s) fun myBind(m, f) = MyState(s => if myRunState(m, s) is [s_, a] then myRunState(f(a), s_)) fun myReturn(a) = MyState(s => [s, a]) val myGet = MyState(s => [s, s]) //│ myGet = MyState([function]) fun myEvalState(m, s) = if myRunState(m, s) is [s_, a] then a abstract class Term: Var | Con | Incr | Add | Lam | App | IfZero | Thunk object Incr extends Term data class Var(s: String) extends Term Con(i: Int) extends Term Add(a: Term, b: Term) extends Term Lam(s: String, t: Term) extends Term App(a: Term, b: Term) extends Term IfZero(a: Term, b: Term, c: Term) extends Term Thunk(t: Term, e: List[[String, Term]]) extends Term object Unit //│ ———————————————————————————————————————————————————————————————————————————————— fun eqEnv(a, b) = if a is Nil and b is Nil then true [s1, t1] :: b and b is [s2, t2] :: d and listEq(s1, s2) and eqTerm(t1, t2) then eqEnv(b, d) else false else false fun eqTerm(a, b) = if a is Var(a) and b is Var(b) then listEq(a, b) Con(a) and b is Con(b) then a === b Incr and b is Incr then true Add(a, b) and b is Add(c, d) then eqTerm(a, c) && eqTerm(b, d) Lam(a, b) and b is Lam(c, d) then listEq(a, c) && eqTerm(b, d) App(a, b) and b is App(c, d) then eqTerm(a, c) && eqTerm(b, d) IfZero(a, b, c) and b is IfZero(d, e, f) then eqTerm(a, d) && eqTerm(b, e) && eqTerm(c, f) Thunk(a, b) and b is Thunk(c, d) then eqTerm(a, c) && eqEnv(b, d) else false //│ ———————————————————————————————————————————————————————————————————————————————— fun myMaybe(d, f, x) = if x is Some(x) then f(x) val incr = myReturn(Unit) //│ incr = MyState([function]) fun lookupVar(v) = fun lookup2(env) = myMaybe(dummy => throw Error("undefined"), x => x, lookup(v, env)) myBind(myGet, env => myReturn(lookup2(env))) fun withEnv(tmp, m) = myReturn(myEvalState(m, tmp)) fun pushVar(v, t, m) = myBind(myGet, env => withEnv([v, t] :: env, m)) //│ ———————————————————————————————————————————————————————————————————————————————— fun traverseTerm(t) = eval(t) fun traverseCon(t) = myBind( traverseTerm(t), _t => if _t is Con(c) then myReturn(c) else throw Error("Not a Con") ) fun apply(t, a) = if t is Thunk(Lam(x, b), e) then myBind( myGet, orig => withEnv(e, pushVar(x, Thunk(a, orig), traverseTerm(b))) ) fun eval(ter) = if ter is Var(x) then myBind of myGet e => myBind(lookupVar(x), t => traverseTerm(t)) Add(u, v) then myBind of traverseCon(u) u_ => myBind(traverseCon(v), v_ => myReturn(Con(u_ + v_))) Thunk(t, e) then withEnv(e, traverseTerm(t)) Lam(x, b) then myBind(myGet, env => myReturn(Thunk(Lam(x, b), env))) App(u, v) then myBind(traverseTerm(u), u_ => apply(u_, v)) IfZero(c, a, b) then myBind of traverseTerm(c) vall => if eqTerm(vall, Con(0)) then traverseTerm(a) else traverseTerm(b) Con(i) then myReturn(Con(i)) Incr then myBind(incr, _dummy => myReturn(Con(0))) //│ ———————————————————————————————————————————————————————————————————————————————— //│ ———————————————————————————————————————————————————————————————————————————————— fun simpleEval(env, ter) = if ter is Var(v) then simpleEval(env, myMaybe(dummy => throw Error("undefined var"), x => x, lookup(v, env))) Con(e) then Con(e) Incr then Con(0) Add(u, v) then let u_ = simpleEvalCon(env, u) let v_ = simpleEvalCon(env, v) Con(u_ + v_) Lam(x, b) then Thunk(Lam(x, b), env) App(u, v) then let u_ = simpleEval(env, u) simpleApply(env, u_, v) IfZero(c, a, b) then let val_ = simpleEval(env, c) if eqTerm(val_, Con(0)) then simpleEval(env, a) else simpleEval(env, b) Thunk(t, e) then simpleEval(e, t) else throw Error(ter) fun simpleApply(env, t, a) = if t is Thunk(Lam(x, b), e) then simpleEval([x, Thunk(a, env)] :: e, b) else throw Error("bad application") fun simpleEvalCon(env, e) = let e_ = simpleEval(env, e) if e_ is Con(c) then c else throw Error("Not a Con") //│ ———————————————————————————————————————————————————————————————————————————————— fun bracket(ot, ths, t) = if ths <= ot then "(" :: (t +: nofibStringToList(")")) else t //│ ———————————————————————————————————————————————————————————————————————————————— fun ppn(n, ter) = if ter is Var(v) then v Con(i) then nofibStringToList(stringOfInt(i)) Incr then nofibStringToList("INCR") Lam(v, t) then bracket(n, 0, "@" :: (v +: nofibStringToList(". ") +: ppn(0-1, t))) Add(a, b) then bracket(n, 1, ppn(1, a) +: nofibStringToList(" + ") +: ppn(1, b)) App(a, b) then bracket(n, 2, ppn(2, a) +: nofibStringToList(" ") +: ppn(2, b)) IfZero(c, a, b) then bracket(n, 0, nofibStringToList("IF ") +: ppn(0, c) +: nofibStringToList(" THEN ") +: ppn(0, a) +: nofibStringToList(" ELSE ") +: ppn(0, b)) Thunk(t, e) then bracket(n, 0, ppn(3, t) +: nofibStringToList("::") +: ppenv(e)) fun pp(t) = ppn(0, t) fun ppenv(env) = nofibStringToList("[") +: flatMap( case { [v, t] then v +: nofibStringToList("=") +: pp(t) +: nofibStringToList(", ") } env ) +: nofibStringToList("]") //│ ———————————————————————————————————————————————————————————————————————————————— val lfxx = Lam(nofibStringToList("x"), App(Var(nofibStringToList("F")), App(Var(nofibStringToList("x")), Var(nofibStringToList("x"))))) //│ lfxx = Lam(["x"], App(Var(["F"]), App(Var(["x"]), Var(["x"])))) val fix = Lam(nofibStringToList("F"), App(lfxx, lfxx)) //│ fix = Lam(["F"], App(Lam(["x"], App(Var(["F"]), App(Var(["x"]), Var(["x"])))), Lam(["x"], App(Var(["F"]), App(Var(["x"]), Var(["x"])))))) val nMinus1 = Add(Var(nofibStringToList("n")), Con(-1)) //│ nMinus1 = Add(Var(["n"]), Con(-1)) val partialSum0 = Lam(nofibStringToList("sum"), Lam(nofibStringToList("n"), IfZero(Var(nofibStringToList("n")), Con(0), Add(Var(nofibStringToList("n")), App(Var(nofibStringToList("sum")), nMinus1))))) //│ partialSum0 = Lam(["s","u","m"], Lam(["n"], IfZero(Var(["n"]), Con(0), Add(Var(["n"]), App(Var(["s","u","m"]), Add(Var(["n"]), Con(-1))))))) val sum0 = App(fix, partialSum0) //│ sum0 = App(Lam(["F"], App(Lam(["x"], App(Var(["F"]), App(Var(["x"]), Var(["x"])))), Lam(["x"], App(Var(["F"]), App(Var(["x"]), Var(["x"])))))), Lam(["s","u","m"], Lam(["n"], IfZero(Var(["n"]), Con(0), Add(Var(["n"]), App(Var(["s","u","m"]), Add(Var(["n"]), Con(-1)))))))) fun showTerm(t) = if t is Con(a) then nofibStringToList("Con ") +: nofibStringToList(stringOfInt(a)) fun ev(t) = let envt2 = myRunState(traverseTerm(t), Nil) if envt2 is [env, t2] then pp(t2) +: nofibStringToList(" ") +: ppenv(env) fun mainSimple(args) = if null_(args) then throw Error("Args: number-to-sum-up-to") else showTerm(simpleEval(Nil, App(sum0, Con(head(args))))) fun mainMonad(args) = if null_(args) then throw Error("Args: number-to-sum-up-to") else ev(App(sum0, Con(head(args)))) fun testLambda_nofib(n) = [mainSimple(n :: Nil), mainMonad(n :: Nil)] fun main() = testLambda_nofib(80).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/lastpiece.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module lastpiece with ... fun isSome(x) = x is Some fun mapMaybe(f, ls) = if ls is Nil then Nil h :: t and f(h) is None then mapMaybe(f, t) Some(a) then a :: mapMaybe(f, t) object GT LT EQ fun compareIntInt(ab, cd) = if ab is [a, b] and cd is [c, d] and a > c then GT a < c then LT b > d then GT b < d then LT else EQ abstract class Map[K, A]: Tip | Bin[K, A] object Tip extends Map[Anything, Nothing] data class Bin[K, A](i: Int, k: K, v: A, l: Map[K, A], r: Map[K, A]) extends Map[K, A] fun mapLookup(k, m) = if m is Tip then None Bin(_, kx, x, l, r) and compareIntInt(k, kx) is LT then mapLookup(k, l) GT then mapLookup(k, r) EQ then Some(x) fun size(p) = if p is Tip then 0 Bin(sz, _, _, _, _) then sz fun bin(k, x, l, r) = Bin(size(l) + size(r) + 1, k, x, l, r) fun singleL(k1, x1, t1, r) = if r is Bin(_, k2, x2, t2, t3) then bin(k2, x2, bin(k1, x1, t1, t2), t3) else throw Error("singleL Tip") fun singleR(k1, x1, l, t3) = if l is Bin(_, k2, x2, t1, t2) then bin(k2, x2, t1, bin(k1, x1, t2, t3)) else throw Error("singleR Tip") fun doubleL(k1, x1, t1, r) = if r is Bin(_, k2, x2, Bin(_, k3, x3, t2, t3), t4) then bin(k3, x3, bin(k1, x1, t1, t2), bin(k2, x2, t3, t4)) else throw Error("doubleL Tip") fun doubleR(k1, x1, l, t4) = if l is Bin(_, k2, x2, t1, Bin(_, k3, x3, t2, t3)) then bin(k3, x3, bin(k2, x2, t1, t2), bin(k1, x1, t3, t4)) else throw Error("doubleR Tip") fun rotateL(k, x, l, r) = if r is Bin(_, _, _, ly, ry) and size(ly) < 2 * size(ry) then singleL(k, x, l, r) else doubleL(k, x, l, r) else throw Error("rotateL Tip") fun rotateR(k, x, l, r) = if l is Bin(_, _, _, ly, ry) and size(ry) < 2 * size(ly) then singleR(k, x, l, r) else doubleR(k, x, l, r) else throw Error("rotateR Tip") fun balance(k, x, l, r) = let sizeL = size(l) let sizeR = size(r) let sizeX = sizeL + sizeR + 1 if (sizeL + sizeR) <= 1 then Bin(sizeX, k, x, l, r) sizeR >= (4 * sizeL) then rotateL(k, x, l, r) sizeL >= (4 * sizeR) then rotateR(k, x, l, r) else Bin(sizeX, k, x, l, r) fun insert(kx, x, m) = if m is Tip then Bin(1, kx, x, Tip, Tip) Bin(sz, ky, y, l, r) and compareIntInt(kx, ky) is LT then balance(ky, y, insert(kx, x, l), r) GT then balance(ky, y, l, insert(kx, x, r)) EQ then Bin(sz, kx, x, l, r) fun indent(n) = if n <= 0 then Nil else " " :: indent(n - 1) data class P(i: Char, a: List[List[[Int, Int]]], b: List[List[[Int, Int]]]) abstract class S: Male | Female object Male extends S Female extends S fun flip(s) = if s is Male then Female Female then Male abstract class Solution: Soln | Choose | Fail type Board = Map[[Int, Int], Char] data class Soln(b: Board) extends Solution Choose(s: List[Solution]) extends Solution Fail(b: Board, s: [Int, Int]) extends Solution fun addIntInt(row_col, orow_ocol) = if row_col is [row, col] and orow_ocol is [orow, ocol] then [row + orow, col + ocol] fun next(row_col) = if row_col is [row, col] then [row, col + 1] val maxRow = 8 //│ maxRow = 8 val maxCol = 8 //│ maxCol = 8 val emptyBoard = Tip //│ emptyBoard = Tip fun check(bd, sq) = mapLookup(sq, bd) fun extend(bd, sq, id) = insert(sq, id, bd) fun extend_maybe(bd, sq, id) = if sq is [row, col] and (row > maxRow) || (col < 1) || (col > maxCol) then None check(bd, sq) is Some(_) then None None then Some(extend(bd, sq, id)) fun pickOne(xs) = fun go(f, xs) = if xs is Nil then Nil x :: xs then [x, f(xs)] :: go(p => x :: f(p), xs) go(x => x, xs) fun fit(bd, sq, id, os) = if os is Nil then Some(extend(bd, sq, id)) o :: os and extend_maybe(bd, addIntInt(sq, o), id) is Some(bd1) then fit(bd1, sq, id, os) None then None //│ ———————————————————————————————————————————————————————————————————————————————— fun tryy(sq, se, bd, id_is_ps) = if id_is_ps is [id, os, ps] and fit(bd, sq, id, os) is Some(bd1) then Some(search(next(sq), flip(se), bd1, ps)) None then None fun search(row_col, sey, bd, ps) = if row_col is [row, col] and ps is Nil then Soln(bd) col === (maxCol + 1) then search([row + 1, 1], flip(sey), bd, ps) check(bd, row_col) is Some(_) then search(next(row_col), flip(sey), bd, ps) else fun lscomp1(ls) = if ls is Nil then Nil [P(id, ms, fs), ps] :: ls then fun lscomp2(ls2) = if ls2 is Nil then lscomp1(ls) os :: ls then [id, os, ps] :: lscomp2(ls) lscomp2(if sey is Male then ms else fs) let choices = lscomp1(pickOne(ps)) if mapMaybe(x => tryy(row_col, sey, bd, x), choices) is Nil then Fail(bd, row_col) ss then Choose(ss) //│ ———————————————————————————————————————————————————————————————————————————————— val nPiece = P( "n", ([0,1] :: [1,1] :: [2,1] :: [2,2] :: Nil) :: ([1,0] :: [1,-1] :: [1,-2] :: [2,-2] :: Nil) :: Nil, Nil ) //│ nPiece = P("n", [[[0, 1],[1, 1],[2, 1],[2, 2]],[[1, 0],[1, -1],[1, -2],[2, -2]]], []) val mPiece = P( "m", ([0,1] :: [1,0] :: [2,0] :: [3,0] :: Nil) :: Nil, ([0,1] :: [0,2] :: [0,3] :: [1,3] :: Nil) :: ([1,0] :: [2,0] :: [3,0] :: [3,-1] :: Nil) :: Nil ) //│ mPiece = P("m", [[[0, 1],[1, 0],[2, 0],[3, 0]]], [[[0, 1],[0, 2],[0, 3],[1, 3]],[[1, 0],[2, 0],[3, 0],[3, -1]]]) val lPiece = P( "l", ([0,1] :: [0,2] :: [0,3] :: [1,2] :: Nil) :: ([1,0] :: [2,0] :: [3,0] :: [2,-1] :: Nil) :: Nil, ([1,-1] :: [1,0] :: [1,1] :: [1,2] :: Nil) :: ([1,0] :: [2,0] :: [3,0] :: [1,1] :: Nil) :: Nil ) //│ lPiece = P("l", [[[0, 1],[0, 2],[0, 3],[1, 2]],[[1, 0],[2, 0],[3, 0],[2, -1]]], [[[1, -1],[1, 0],[1, 1],[1, 2]],[[1, 0],[2, 0],[3, 0],[1, 1]]]) val kPiece = P( "k", ([0,1] :: [1,0] :: [2,0] :: [2,-1] :: Nil) :: Nil, ([1,0] :: [1,1] :: [1,2] :: [2,2] :: Nil) :: Nil ) //│ kPiece = P("k", [[[0, 1],[1, 0],[2, 0],[2, -1]]], [[[1, 0],[1, 1],[1, 2],[2, 2]]]) val jPiece = P( "j", ([0,1] :: [0,2] :: [0,3] :: [1,1] :: Nil) :: ([1,0] :: [2,0] :: [3,0] :: [1,-1] :: Nil) :: ([1,-2] :: [1,-1] :: [1,0] :: [1,1] :: Nil) :: Nil, ([1,0] :: [2,0] :: [3,0] :: [2,2] :: Nil) :: Nil ) //│ jPiece = P("j", [[[0, 1],[0, 2],[0, 3],[1, 1]],[[1, 0],[2, 0],[3, 0],[1, -1]],[[1, -2],[1, -1],[1, 0],[1, 1]]], [[[1, 0],[2, 0],[3, 0],[2, 2]]]) val iPiece = P( "i", ([1,0] :: [2,0] :: [2,1] :: [3,1] :: Nil) :: ([0,1] :: [0,2] :: [1,0] :: [1,-1] :: Nil) :: ([1,0] :: [1,1] :: [2,1] :: [3,1] :: Nil) :: Nil, ([0,1] :: [1,0] :: [1,-1] :: [1,-2] :: Nil) :: Nil ) //│ iPiece = P("i", [[[1, 0],[2, 0],[2, 1],[3, 1]],[[0, 1],[0, 2],[1, 0],[1, -1]],[[1, 0],[1, 1],[2, 1],[3, 1]]], [[[0, 1],[1, 0],[1, -1],[1, -2]]]) val hPiece = P( "h", ([0,1] :: [1,1] :: [1,2] :: [2,2] :: Nil) :: ([1,0] :: [1,-1] :: [2,-1] :: [2,-2] :: Nil) :: ([1,0] :: [1,1] :: [2,1] :: [2,2] :: Nil) :: Nil, ([0,1] :: [1,0] :: [1,-1] :: [2,-1] :: Nil) :: Nil ) //│ hPiece = P("h", [[[0, 1],[1, 1],[1, 2],[2, 2]],[[1, 0],[1, -1],[2, -1],[2, -2]],[[1, 0],[1, 1],[2, 1],[2, 2]]], [[[0, 1],[1, 0],[1, -1],[2, -1]]]) val gPiece = P( "g", Nil, ([0,1] :: [1,1] :: [1,2] :: [1,3] :: Nil) :: ([1,0] :: [1,-1] :: [2,-1] :: [3,-1] :: Nil) :: ([0,1] :: [0,2] :: [1,2] :: [1,3] :: Nil) :: ([1,0] :: [2,0] :: [2,-1] :: [3,-1] :: Nil) :: Nil ) //│ gPiece = P("g", [], [[[0, 1],[1, 1],[1, 2],[1, 3]],[[1, 0],[1, -1],[2, -1],[3, -1]],[[0, 1],[0, 2],[1, 2],[1, 3]],[[1, 0],[2, 0],[2, -1],[3, -1]]]) val fPiece = P( "f", ([0,1] :: [1,1] :: [2,1] :: [3,1] :: Nil) :: ([1,0] :: [1,-1] :: [1,-2] :: [1,-3] :: Nil) :: ([1,0] :: [2,0] :: [3,0] :: [3,1] :: Nil) :: Nil, ([0,1] :: [0,2] :: [0,3] :: [1,0] :: Nil) :: Nil ) //│ fPiece = P("f", [[[0, 1],[1, 1],[2, 1],[3, 1]],[[1, 0],[1, -1],[1, -2],[1, -3]],[[1, 0],[2, 0],[3, 0],[3, 1]]], [[[0, 1],[0, 2],[0, 3],[1, 0]]]) val ePiece = P( "e", ([0,1] :: [1,1] :: [1,2] :: Nil) :: ([1,0] :: [1,-1] :: [2,-1] :: Nil) :: Nil, ([0,1] :: [1,1] :: [1,2] :: Nil) :: ([1,0] :: [1,-1] :: [2,-1] :: Nil) :: Nil ) //│ ePiece = P("e", [[[0, 1],[1, 1],[1, 2]],[[1, 0],[1, -1],[2, -1]]], [[[0, 1],[1, 1],[1, 2]],[[1, 0],[1, -1],[2, -1]]]) val dPiece = P( "d", ([0,1] :: [1,1] :: [2,1] :: Nil) :: ([1,0] :: [1,-1] :: [1,-2] :: Nil) :: Nil, ([1,0] :: [2,0] :: [2,1] :: Nil) :: Nil ) //│ dPiece = P("d", [[[0, 1],[1, 1],[2, 1]],[[1, 0],[1, -1],[1, -2]]], [[[1, 0],[2, 0],[2, 1]]]) val cPiece = P( "c", Nil, ([0,1] :: [0,2] :: [1,1] :: Nil) :: ([1,0] :: [1,-1] :: [2,0] :: Nil) :: ([1,-1] :: [1,0] :: [1,1] :: Nil) :: ([1,0] :: [1,1] :: [2,0] :: Nil) :: Nil ) //│ cPiece = P("c", [], [[[0, 1],[0, 2],[1, 1]],[[1, 0],[1, -1],[2, 0]],[[1, -1],[1, 0],[1, 1]],[[1, 0],[1, 1],[2, 0]]]) val bPiece = P( "b", ([0,1] :: [0,2] :: [1,2] :: Nil) :: ([1,0] :: [2,0] :: [2,-1] :: Nil) :: ([0,1] :: [1,0] :: [2,0] :: Nil) :: Nil, ([1,0] :: [1,1] :: [1,2] :: Nil) :: Nil ) //│ bPiece = P("b", [[[0, 1],[0, 2],[1, 2]],[[1, 0],[2, 0],[2, -1]],[[0, 1],[1, 0],[2, 0]]], [[[1, 0],[1, 1],[1, 2]]]) val initialPieces = bPiece :: cPiece :: dPiece :: ePiece :: fPiece :: gPiece :: hPiece :: iPiece :: jPiece :: kPiece :: lPiece :: mPiece :: nPiece :: Nil //│ initialPieces = [P("b", [[[0, 1],[0, 2],[1, 2]],[[1, 0],[2, 0],[2, -1]],[[0, 1],[1, 0],[2, 0]]], [[[1, 0],[1, 1],[1, 2]]]),P("c", [], [[[0, 1],[0, 2],[1, 1]],[[1, 0],[1, -1],[2, 0]],[[1, -1],[1, 0],[1, 1]],[[1, 0],[1, 1],[2, 0]]]),P("d", [[[0, 1],[1, 1],[2, 1]],[[1, 0],[1, -1],[1, -2]]], [[[1, 0],[2, 0],[2, 1]]]),P("e", [[[0, 1],[1, 1],[1, 2]],[[1, 0],[1, -1],[2, -1]]], [[[0, 1],[1, 1],[1, 2]],[[1, 0],[1, -1],[2, -1]]]),P("f", [[[0, 1],[1, 1],[2, 1],[3, 1]],[[1, 0],[1, -1],[1, -2],[1, -3]],[[1, 0],[2, 0],[3, 0],[3, 1]]], [[[0, 1],[0, 2],[0, 3],[1, 0]]]),P("g", [], [[[0, 1],[1, 1],[1, 2],[1, 3]],[[1, 0],[1, -1],[2, -1],[3, -1]],[[0, 1],[0, 2],[1, 2],[1, 3]],[[1, 0],[2, 0],[2, -1],[3, -1]]]),P("h", [[[0, 1],[1, 1],[1, 2],[2, 2]],[[1, 0],[1, -1],[2, -1],[2, -2]],[[1, 0],[1, 1],[2, 1],[2, 2]]], [[[0, 1],[1, 0],[1, -1],[2, -1]]]),P("i", [[[1, 0],[2, 0],[2, 1],[3, 1]],[[0, 1],[0, 2],[1, 0],[1, -1]],[[1, 0],[1, 1],[2, 1],[3, 1]]], [[[0, 1],[1, 0],[1, -1],[1, -2]]]),P("j", [[[0, 1],[0, 2],[0, 3],[1, 1]],[[1, 0],[2, 0],[3, 0],[1, -1]],[[1, -2],[1, -1],[1, 0],[1, 1]]], [[[1, 0],[2, 0],[3, 0],[2, 2]]]),P("k", [[[0, 1],[1, 0],[2, 0],[2, -1]]], [[[1, 0],[1, 1],[1, 2],[2, 2]]]),P("l", [[[0, 1],[0, 2],[0, 3],[1, 2]],[[1, 0],[2, 0],[3, 0],[2, -1]]], [[[1, -1],[1, 0],[1, 1],[1, 2]],[[1, 0],[2, 0],[3, 0],[1, 1]]]),P("m", [[[0, 1],[1, 0],[2, 0],[3, 0]]], [[[0, 1],[0, 2],[0, 3],[1, 3]],[[1, 0],[2, 0],[3, 0],[3, -1]]]),P("n", [[[0, 1],[1, 1],[2, 1],[2, 2]],[[1, 0],[1, -1],[1, -2],[2, -2]]], [])] abstract class Mode: PageMode | ZigZagMode | LeftMode | OneLineMode object PageMode extends Mode ZigZagMode extends Mode LeftMode extends Mode OneLineMode extends Mode abstract class TextDetails: Chr | Str | PStr data class Chr(c: Char) extends TextDetails Str(s: String) extends TextDetails PStr(s: String) extends TextDetails abstract class AnnotDetails: AnnotStart | NoAnnot | AnnotEnd object AnnotStart extends AnnotDetails AnnotEnd extends AnnotDetails data class NoAnnot(t: TextDetails, i: Int) extends AnnotDetails abstract class IsEmptyy: IsEmpty | NotEmpty object IsEmpty extends IsEmptyy NotEmpty extends IsEmptyy abstract class Doc: Empty | NilAbove | TextBeside | Nest | Union | NoDoc | Beside | Above object Empty extends Doc NoDoc extends Doc data class NilAbove(d: Doc) extends Doc TextBeside(a: AnnotDetails, d: Doc) extends Doc Nest(i: Int, d: Doc) extends Doc Union(d1: Doc, d2: Doc) extends Doc Beside(d1: Doc, b: Bool, d2: Doc) extends Doc Above(d1: Doc, b: Bool, d2: Doc) extends Doc val spaceText = NoAnnot(Chr(" "), 1) //│ spaceText = NoAnnot(Chr(" "), 1) val nlText = NoAnnot(Chr("\n"), 1) //│ nlText = NoAnnot(Chr("\n"), 1) fun annotSize(p) = if p is NoAnnot(_, l) then l _ then 0 //│ ———————————————————————————————————————————————————————————————————————————————— fun display(s) = if s is Soln(bd) then vcat(text(nofibStringToList("Success!")) :: nest(2, displayBoard(bd)) :: Nil) Choose(ss) then vcat(map(display, ss)) Fail(bd, [row, col]) then Empty fun displayBoard(bd) = fun sq(n, col) = if check(bd, [n, col]) is Some(id) then char(id) None then char(".") fun row(n) = hcat(map(col => sq(n, col), enumFromTo(1, maxCol))) above_(vcat(map(row, enumFromTo(1, maxCol))), false, text(Nil)) fun eliminateEmpty(cons, p, g, q) = if p is Empty then q else [ NotEmpty, if q is [NotEmpty, q1] then cons(p, g, q1) [IsEmpty, _] then p ] fun reduceVert(doc) = if doc is Above(p, g, q) then eliminateEmpty((a, b, c) => Above(a, b, c), snd(reduceVert(p)), g, reduceVert(q)) else [NotEmpty, doc] fun vcat(ls) = snd(reduceVert(foldr((p, q) => Above(p, false, q), Empty, ls))) fun text(s) = let sl = listLen(s) TextBeside(NoAnnot(Str(s), sl), Empty) fun char(c) = TextBeside(NoAnnot(Chr(c), 1), Empty) fun reduceHoriz(doc) = if doc is Beside(p, g, q) then eliminateEmpty((a, b, c) => Beside(a, b, c), snd(reduceHoriz(p)), g, reduceHoriz(q)) else [NotEmpty, doc] fun hcat(ls) = snd of reduceHoriz of foldr of (p, q) => Beside(p, false, q) Empty ls fun above_(p, g, q) = if q is Empty then p g is Empty then q else Above(p, g, q) fun nest(k, p) = mkNest(k, reduceDoc(p)) fun mkNest(k, p) = if p is Nest(k1, p1) then mkNest(k + k1, p1) p is NoDoc then NoDoc p is Empty then Empty k === 0 then p else Nest(k, p) fun reduceDoc(p) = if p is Beside(p1, g, q) then beside(p1, g, reduceDoc(q)) Above(p1, g, q) then above(p1, g, reduceDoc(q)) else p fun beside(p, g, q) = if p is NoDoc then NoDoc Union(p1, p2) then Union(beside(p1, g, q), beside(p2, g, q)) Empty then q Nest(k, p1) then Nest(k, beside(p1, g, q)) Beside(p1, g1, q1) and g1 === g then beside(p1, g1, beside(q1, g, q)) else beside(reduceDoc(Beside(p1, g1, q1)), g, q) Above(_, _, _) then beside(reduceDoc(p), g, q) NilAbove(p1) then NilAbove(beside(p1, g, q)) TextBeside(t, p1) then let rest = if p1 is Empty then nilBeside(g, q) else beside(p1, g, q) TextBeside(t, rest) fun above(p, g, q) = if p is Above(p1, g1, q1) then above(p1, g1, above(q1, g, q)) Beside(_, _, _) then aboveNest(reduceDoc(p), g, 0, reduceDoc(q)) else aboveNest(p, g, 0, reduceDoc(q)) fun nilBeside(g, p) = if p is Empty then Empty p is Nest(_, p1) then nilBeside(g, p1) g then TextBeside(spaceText, p) else p fun aboveNest(p, g, k, q) = if p is NoDoc then NoDoc Union(p1, p2) then Union(aboveNest(p1, g, k, q), aboveNest(p2, g, k, q)) Empty then mkNest(k, q) Nest(k1, p1) then Nest(k1, aboveNest(p1, g, k - k1, q)) NilAbove(p1) then NilAbove(aboveNest(p1, g, k, q)) TextBeside(s, p1) then let k1 = k - annotSize(s) let rest = if p1 is Empty then nilAboveNest(g, k1, q) else aboveNest(p1, g, k1, q) TextBeside(s, rest) Above(_, _, _) then throw Error("aboveNest Above") Beside(_, _, _) then throw Error("aboveNest Beside") fun nilAboveNest(g, k, q) = if q is Empty then Empty q is Nest(k1, q1) then nilAboveNest(g, k + k1, q1) not(g) and (k > 0) then TextBeside(NoAnnot(Str(indent(k)), k), q) else NilAbove(mkNest(k, q)) fun printDoc(d) = fun put(k, next) = if k is Chr(c) then c :: next Str(s) then s +: next PStr(s) then s +: next let done = "\n" :: Nil fullRender(ZigZagMode, 200, 1.5, put, done, d) fun fullRender(m, l, r, txt, a, b) = fun annTxt(p, x) = if p is NoAnnot(s, _) then txt(s, x) _ then x fullRenderAnn(m, l, r, annTxt, a, b) fun ceiling(x) = globalThis.Math.ceil(x) fun fullRenderAnn(m, lineLen, ribbons, txt, rest, doc) = if m is OneLineMode then easyDisplay(spaceText, (a, b) => b, txt, rest, reduceDoc(doc)) LeftMode then easyDisplay(nlText, first, txt, rest, reduceDoc(doc)) else let ribbonLen = ceiling(lineLen / ribbons) let bestLineLen = if m is ZigZagMode then 2147483647 else lineLen let doc1 = best(bestLineLen, ribbonLen, reduceDoc(doc)) displayDoc(m, lineLen, ribbonLen, txt, rest, doc1) fun easyDisplay(nlSpaceText, choose, txt, end, x) = fun lay(x) = if x is NoDoc then throw Error("easyDisplay: NoDoc") Union(p, q) then lay(choose(p, q)) Nest(_, p) then lay(p) Empty then end NilAbove(p) then txt(nlSpaceText, lay(p)) TextBeside(s, p) then txt(s, lay(p)) Above(_, _, _) then throw Error("easyDisplay Above") Beside(_, _, _) then throw Error("easyDisplay Beside") lay(x) fun displayDoc(m, pageWidth, ribbonWidth, txt, end, doc) = let gapWidth = pageWidth - ribbonWidth let shift = intDiv(gapWidth, 2) fun lay(k, docc) = fun lay2(k, param) = if param is NilAbove(p) then txt(nlText, lay(k, p)) TextBeside(s, p) then txt(s, lay2(k + annotSize(s), p)) Nest(_, p) then lay2(k, p) Empty then end fun lay1(k, s, p) = let r = k + annotSize(s) txt(NoAnnot(Str(indent(k)), k), txt(s, lay2(r, p))) if docc is Nest(k1, p) then lay(k + k1, p) Empty then end NilAbove(p) then txt(nlText, lay(k, p)) TextBeside(s, p) and m is ZigZagMode and k >= gapWidth then txt(nlText, txt(NoAnnot(Str(replicate(shift, "/")), shift), txt(nlText, lay1(k - shift, s, p)))) < 0 then txt(nlText, txt(NoAnnot(Str(replicate(shift, "|")), shift), txt(nlText, lay1(k + shift, s, p)))) else lay1(k, s, p) else lay1(k, s, p) lay(0, doc) fun best(w0, r, doc) = fun get(r, w, docc) = if docc is Empty then Empty NoDoc then NoDoc NilAbove(p) then NilAbove(get(r, w, p)) TextBeside(s, p) then TextBeside(s, get1(r, w, annotSize(s), p)) Nest(k, p) then Nest(k, get(r, w - k, p)) Union(p, q) then nicest(w, r, get(r, w, p), get(r, w, q)) Above(_, _, _) then throw Error("best get Above") Beside(_, _, _) then throw Error("best get Beside") fun get1(r, w, sl, p) = if p is Empty then Empty NoDoc then NoDoc NilAbove(p) then NilAbove(get(r, w - sl, p)) TextBeside(s, p) then TextBeside(s, get1(r, w, sl + annotSize(s), p)) Nest(_, p) then get1(r, w, sl, p) Union(p, q) then nicest1(w, r, sl, get1(r, w, sl, p), get1(r, w, sl, q)) Above(_, _, _) then throw Error("best get1 Above") Beside(_, _, _) then throw Error("best get1 Beside") get(r, w0, doc) fun nonEmptySet(doc) = if doc is NoDoc then false Union(_, _) then true Empty then true NilAbove(_) then true TextBeside(_, p) then nonEmptySet(p) Nest(_, p) then nonEmptySet(p) Above(_, _, _) then throw Error("nonEmptySet Above") Beside(_, _, _) then throw Error("nonEmptySet Beside") fun fits(n, param) = if n < 0 then false param is NoDoc then false Empty then true NilAbove(_) then true TextBeside(s, p) then fits(n - annotSize(s), p) Above(_, _, _) then throw Error("fits Above") Beside(_, _, _) then throw Error("fits Beside") Union(_, _) then throw Error("fits Union") Nest(_, _) then throw Error("fits Nest") fun first(p, q) = if nonEmptySet(p) then p else q fun nicest1(w, r, sl, p, q) = if fits(min(w, r) - sl, p) then p else q fun nicest(w, r, p, q) = nicest1(w, r, 0, p, q) //│ ———————————————————————————————————————————————————————————————————————————————— fun testLastPiece_nofib() = let initialBoard = fromSome(fit(emptyBoard, [1, 1], "a", [1, 0] :: [1, 1] :: Nil)) let solutions = search([1,2], Female, initialBoard, initialPieces) printDoc(display(solutions)) fun main() = nofibListToString(testLastPiece_nofib()) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/lcss.mls ================================================ import "./NofibPrelude.mls" open NofibPrelude module lcss with ... fun algb2(x, k0j1, k1j1, yss) = if yss is Nil then Nil [y, k0j] :: ys then let kjcurr = if x == y then k0j1 + 1 else max(k1j1, k0j) [y, kjcurr] :: algb2(x, k0j, kjcurr, ys) fun algb1(xss, yss) = if xss is Nil then map(snd, yss) x :: xs then algb1(xs, algb2(x, 0, 0, yss)) fun algb(xs, ys) = fun listcomp_fun(listcomp_fun_para) = if listcomp_fun_para is listcomp_fun_ls_h :: listcomp_fun_ls_t then [listcomp_fun_ls_h, 0] :: listcomp_fun(listcomp_fun_ls_t) Nil then Nil 0 :: algb1(xs, listcomp_fun(ys)) fun findk(k, km, m, ls) = if ls is Nil then km [x,y] :: xys then if (x + y) >= m then findk(k+1, k, x+y, xys) else findk(k+1, km, m, xys) fun algc(m, n, xs, ys) = if ys is Nil then x => x xs is x :: Nil and inList(x, ys) then t => x :: t else x => x else let m2 = intDiv(m, 2) let xs1 = take(m2, xs) let xs2 = leave(m2, xs) let l1 = algb(xs1, ys) let l2 = reverse(algb(reverse(xs2), reverse(ys))) let k = findk(0, 0, -1, zip(l1, l2)) compose(algc(m2, k, xs1, take(k, ys)), (algc(m-m2, n-k, xs2, leave(k, ys)))) fun lcss(xs, ys) = algc(listLen(xs), listLen(ys), xs, ys)(Nil) fun lcssMain(a, b ,c, d, e, f) = lcss(enumFromThenTo(a,b,c), enumFromThenTo(d,e,f)) fun testLCSS_nofib(d) = lcssMain(1,2,60,30,31,90) fun main() = testLCSS_nofib(0).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/life.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module life with ... fun last(a_t) = fun go(h, t) = if t is Nil then h head :: t then go(head, t) if a_t is a :: t then go(a, t) fun copy_lz(n, x) = lazy of () => if n === 0 then LzNil else LzCons(x, copy_lz(n - 1, x)) fun append_lz_lz(xs, ys) = lazy of () => if force(xs) is LzNil then force(ys) LzCons(h, t) then LzCons(h, append_lz_lz(t, ys)) fun init(ls) = if ls is a :: Nil then Nil a :: t then a :: init(t) else throw Error(ls) fun zipWith3(f, xs, ys, zs) = if xs is hx :: tx and ys is hy :: ty and zs is hz :: tz then f(hx, hy, hz) :: zipWith3(f, tx, ty, tz) else Nil fun zip3(xs, ys, zs) = if xs is hx :: tx and ys is hy :: ty and zs is hz :: tz then [hx, hy, hz] :: zip3(tx, ty, tz) else Nil fun lzfy(ls) = lazy of () => if ls is a :: t then LzCons(a, lzfy(t)) else LzNil val start = (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: (lazy of () => LzNil) :: lzfy(0 :: 0 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: Nil) :: Nil //│ start = [Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function])] fun elt(a_b_c, d_e_f, g_h_i) = if a_b_c is [a, b, c] and d_e_f is [d, e, f] and g_h_i is [g, h, i] then let tot = a + b + c + d + f + g + h + i if tot < 2 || tot > 3 then 0 tot === 3 then 1 else e fun shiftr(x, xs) = x :: init(xs) fun shiftl(x, xs) = init(xs) +: (x :: Nil) fun shift(x, xs) = zip3(shiftr(x, xs), xs, shiftl(x, xs)) fun row(last_this_next) = if last_this_next is [last, this_, next] then zipWith3(elt, shift(0, last), shift(0, this_), shift(0, next)) fun gen(n, board) = map(row, shift(replicate(n, 0), board)) fun star(x) = if x === 0 then nofibStringToList(" ") 1 then nofibStringToList(" o") fun glue(s, xs, ys) = xs +: s +: ys fun limit(ls) = if force(ls) is LzCons(x, ys) and force(ys) is LzCons(y, xs) and listEqBy(listEq, x, y) then x :: Nil else x :: limit(lazy of () => LzCons(y, xs)) fun disp(gen_xss) = if gen_xss is [genn, xss] then lazy of () => genn +: nofibStringToList("nn") +: foldr((a, b) => glue("n" :: Nil, a, b), Nil, map(x => concat(map(star, x)), xss)) fun generations(sz) = map of disp zip_lz_nl of map_lz(i => nofibStringToList(stringOfInt(i)), enumFrom(0)), limit of iterate of b => gen(sz, b) take_lz of sz map_lz of l => take_lz(sz, append_lz_lz(l, copy_lz(sz, 0))), append_nl_lz(start, copy_lz(sz, copy_lz(sz, 0))) fun testLife_nofib(n) = listLen(force(last(generations(n)))) fun main() = testLife_nofib(15) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/mandel.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module mandel with ... data class Pixmap(a: Int, b: Int, c: Int, d:List[[Int, Int, Int]]) fun createPixmap(width, height, max, colours) = Pixmap(width, height, max, colours) data class Complex(r: Num, i: Num) fun comp_magnitude(c) = if c is Complex(a, b) then sqrt(a * a + b * b) fun comp_times(x, y) = if x is Complex(a, b) and y is Complex(c, d) then Complex((a * c) - (b * d), (a * d) + (b * c)) fun comp_plus(x, y) = if x is Complex(a, b) and y is Complex(c, d) then Complex(a + c, b + d) fun mandel(c) = fun infiniteMandel() = lazy of () => LzCons(c, map_lz((z => comp_plus(comp_times(z, z), c)), infiniteMandel())) infiniteMandel() fun diverge(cmplx, radius) = comp_magnitude(cmplx) > radius fun whenDiverge(limit, radius, c) = fun walkIt(ls) = if force(ls) is LzNil then 0 LzCons(x, xs) and diverge(x, radius) then 0 else 1 + walkIt(xs) walkIt(take_lz_lz(limit, mandel(c))) fun parallelMandel(mat, limit, radius) = map(c => whenDiverge(limit, radius, c), mat) fun mandelset(x, y, x_, y_, screenX, screenY, lIMIT) = fun prettyRGB(s) = let t = lIMIT - s [s, t, t] fun windowToViewport(s, t) = Complex(x + ((s * (x_ - x)) / screenX), y + ((t * (y_ - y)) / screenY)) fun lscomp1(ls1) = if ls1 is Nil then Nil t :: t1 then fun lscomp2(ls2) = if ls2 is Nil then lscomp1(t1) s :: t2 then windowToViewport(s, t) :: lscomp2(t2) lscomp2(enumFromTo(1, screenX)) let result = parallelMandel(lscomp1(enumFromTo(1, screenY)), lIMIT, max(x_ - x, y_ - y) / 2) createPixmap(screenX, screenY, lIMIT, map(prettyRGB, result)) fun testMandel_nofib(dummy) = let minx = -2.0 let miny = -2.0 let maxx = 2.0 let maxy = 2.0 let screenX = 25 let screenY = 25 let limit = 75 mandelset(minx, miny, maxx, maxy, screenX, screenY, limit) fun main() = testMandel_nofib(0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/mandel2.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module mandel2 with ... abstract class MandTree: NS | EW | Leaf data class NS(l: MandTree, r: MandTree) extends MandTree EW(l: MandTree, r: MandTree) extends MandTree Leaf(colour: Int) extends MandTree val size = 200 val pmn = -2.25 val pmx = 0.75 val qmn = -1.5 val qmx = 1.5 val m = 20 val num_cols = 26 val delta_p = (pmx - pmn) / (size - 1) val delta_q = (qmx - qmn) / (size - 1) val up = [ 0,-1] val down = [ 0, 1] val left = [-1, 0] val right = [ 1, 0] //│ delta_p = 0.01507537688442211 //│ delta_q = 0.01507537688442211 //│ down = [0, 1] //│ left = [-1, 0] //│ m = 20 //│ num_cols = 26 //│ pmn = -2.25 //│ pmx = 0.75 //│ qmn = -1.5 //│ qmx = 1.5 //│ right = [1, 0] //│ size = 200 //│ up = [0, -1] fun equalp(p1, p2) = if p1 is [x1, x2] and p2 is [y1, y2] then (x1 == y1) and (x2 == y2) fun np(x) = pmn + (x * delta_p) fun nq(y) = qmn + (y * delta_q) fun radius(x, y) = (x * x) + (y * y) fun new_x(x, y, p) = (x * x) - (y * y) + p fun new_y(x, y, q) = (2.0 * x * y) + q fun finite(t) = if t is Leaf(c) then c == c NS(t1, t2) then finite(t1) and finite(t2) EW(t1, t2) then finite(t1) and finite(t2) fun check_radius(p, q, k, x, y) = let xn = new_x(x, y, p) let yn = new_y(x, y, q) let r = radius(xn, yn) let kp = k + 1 if kp == num_cols then 0 r > m then kp else check_radius(p, q, kp, xn, yn) fun point_colour(xy) = if xy is [x, y] then check_radius(np(x), nq(y), 0, 0.0, 0.0) fun check_perim(x1y1, x2y2) = let col1 = point_colour(x1y1) if x1y1 is [x1, y1] and x2y2 is [x2, y2] then fun check_line(xcyc, xdyd) = if xcyc is [xc, yc] and xdyd is [xd, yd] and let finished = if equalp(xdyd, right) then xc >= x2 equalp(xdyd, down) then yc <= y2 equalp(xdyd, left) then xc <= x1 else yc >= y1 finished then true not(point_colour(xcyc) == col1) then false else check_line([xc + xd, yc + yd], [xd, yd]) if equalp(x1y1, x2y2) then col1 let col2 = point_colour([x2, y1]) let col3 = point_colour(x2y2) let col4 = point_colour([x1, y2]) let corners_diff = if (col1 == col2) and (col1 == col3) and (col1 == col4) then false else true corners_diff then -1 check_line([x1+1,y1], right) and check_line([x2,y1+1], down) and check_line([x2-1,y2], left) and check_line([x1,y2-1], up) then col1 else -1 fun build_tree(x1y1, x2y2) = if x1y1 is [x1, y1] and x2y2 is [x2, y2] then let rec_col = check_perim(x1y1, x2y2) if not(rec_col == -1) then Leaf(rec_col) let split = if (x2-x1) >= (y2-y1) then "NS" else "EW" let split_x = intDiv((x2+x1), 2) let split_y = intDiv((y2+y1), 2) let nsp1 = x1y1 let nsp2 = [split_x, y2] let nsp3 = [split_x+1, y1] let nsp4 = x2y2 let ewp1 = x1y1 let ewp2 = [x2, split_y] let ewp3 = [x1, split_y+1] let ewp4 = x2y2 split == "NS" then NS(build_tree(nsp1, nsp2), build_tree(nsp3, nsp4)) else EW(build_tree(ewp1, ewp2), build_tree(ewp3, ewp4)) fun testMandel2_nofib(n) = finite(build_tree([0,0], [size, intDiv(size, 2)])) fun main() = testMandel2_nofib(0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/mate.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module mate with ... //│ ———————————————————————————————————————————————————————————————————————————————— fun rqpart(le, x, ys, rle, rgt, r) = if ys is Nil then qsort(le, rle, x :: qsort(le, rgt, r)) y :: ys and le(y, x) then rqpart(le, x, ys, y :: rle, rgt, r) else rqpart(le, x, ys, rle, y :: rgt, r) fun rqsort(le, xs, r) = if xs is Nil then r x :: Nil then x :: r x :: xs then rqpart(le, x, xs, Nil, Nil, r) fun qpart(le, x, ys, rlt, rge, r) = if ys is Nil then rqsort(le, rlt, x :: rqsort(le, rge, r)) y :: ys and le(x, y) then qpart(le, x, ys, rlt, y :: rge, r) else qpart(le, x, ys, y :: rlt, rge, r) fun qsort(le, xs, r) = if xs is Nil then r x :: Nil then x :: r x :: xs then qpart(le, x, xs, Nil, Nil, r) //│ ———————————————————————————————————————————————————————————————————————————————— fun sort(l) = qsort of (a, b) => if a is [aa, _] and b is [bb, _] then listLen(aa) <= listLen(bb) l Nil abstract class Kind: King | Queen | Rook | Bishop | Knight | Pawn object King extends Kind Queen extends Kind Rook extends Kind Bishop extends Kind Knight extends Kind Pawn extends Kind abstract class Colour: Black | White object Black extends Colour White extends Colour type Piece = [Colour,Kind] type Square = [Int,Int] data class Board(a: List[[Kind, Square]], b: List[[Kind, Square]]) data class Move(a: Square, b: Option[Piece], c: Option[Piece]) data class MoveInFull(a: Piece, b: Square, c: Move) data class Solution(a: MoveInFull, b: List[[List[MoveInFull],Solution]]) fun kindOrder(k) = if k is King then 1 Queen then 2 Rook then 3 Bishop then 4 Knight then 5 Pawn then 6 fun colourOrder(c) = if c is Black then 1 else 2 fun pieceCompare(p1, p2) = if p1 is [c1, k1] and p2 is [c2, k2] then let co = colourOrder(c1) - colourOrder(c2) if co === 0 then kindOrder(k1) - kindOrder(k2) else co fun squareCompare(s1, s2) = if s1 is [f1, r1] and s2 is [f2, r2] then let ro = r1 - r2 if ro === 0 then f1 - f2 else ro fun optPieceCompare(op1, op2) = if op1 is None and op2 is None then 0 op1 is None and op2 is Some(_) then -1 op1 is Some(_) and op2 is None then 1 op1 is Some(p1) and op2 is Some(p2) then pieceCompare(p1, p2) fun moveCompare(m1, m2) = if m1 is Move(ma1, mb1, mc1) and m2 is Move(ma2, mb2, mc2) then let ac = squareCompare(ma1, ma2) if ac === 0 then let bc = optPieceCompare(mb1, mb2) if bc === 0 then optPieceCompare(mc1, mc2) else bc else ac fun moveInFullCompare(mif1, mif2) = if mif1 is MoveInFull(p1, s1, m1) and mif2 is MoveInFull(p2, s2, m2) then let pc = pieceCompare(p1, p2) if pc === 0 then let sc = squareCompare(s1, s2) if sc === 0 then moveCompare(m1, m2) else sc else pc fun maybe(d, f, x) = if x is None then d Some(x) then f(x) fun isUpper(c) = let x = c.charCodeAt(0) in (x >= 65 and x <= 90) fun isLower(c) = let x = c.charCodeAt(0) in (x >= 97 and x <= 122) fun toLower(c) = if isUpper(c) then String.fromCharCode(c.charCodeAt(0) + 32) else c fun words(s) = if leaveWhile(x => x === " ", s) is Nil then Nil s_ and break_(x => x === " ", s_) is [w, s__] then w :: words(s__) fun unlines(ls) = concat(map(l => append(l, "\n" :: Nil), ls)) fun lines(s) = if break_(x => x === "\n", s) is [l, s_] then Cons of l if s_ is Nil then Nil Cons(_, s__) then lines(s__) fun any(p, ls) = if ls is Nil then false x :: xs then p(x) || any(p, xs) fun showColour(c) = nofibStringToList(if c is Black then "Black" else "White") fun pieceAt(bd, sq) = if bd is Board(wkss, bkss) then fun pieceAtWith(c, n, ls) = if ls is Nil then n [k, s] :: xs and eqTup2(s, sq) then Some([c, k]) else pieceAtWith(c, n, xs) pieceAtWith(White, pieceAtWith(Black, None, bkss), wkss) fun kindToChar(k) = if k is King then "K" Queen then "Q" Rook then "R" Bishop then "B" Knight then "N" Pawn then "P" fun pieceToChar(p) = if p is [Black, k] then kindToChar(k) [White, k] then toLower(kindToChar(k)) fun showBoard(bd) = fun showRank(r) = fun consFile(f, s) = if pieceAt(bd, [f, r]) is None then nofibStringToList(" -") +: s Some(p) then " " :: pieceToChar(p) :: s foldr(consFile, Nil, enumFromTo(1, 8)) unlines(map(showRank, reverse(enumFromTo(1, 8)))) fun showPiece(p) = if p is [c, k] then kindToChar(k) :: Nil fun showSquare(c, x_y) = if x_y is [x, y] then atIndex( x - 1, nofibStringToList("QR") :: nofibStringToList("QN") :: nofibStringToList("QB") :: nofibStringToList("Q") :: nofibStringToList("K") :: nofibStringToList("KB") :: nofibStringToList("KN") :: nofibStringToList("KR") :: Nil ) +: nofibStringToList(stringOfInt(if c is Black then 9 - y else y)) fun emptyAtAll(bd, e) = if bd is Board(wkss, bkss) then fun emptyAtAllAnd(b, ls) = if ls is Nil then b [_, s] :: xs then (not(e(s)) and emptyAtAllAnd(b, xs)) emptyAtAllAnd(emptyAtAllAnd(true, bkss), wkss) fun rPa(sq, kss) = if kss is Nil then throw Error("rPa") [k, s] :: kss then if eqTup2(s, sq) then kss else [k, s] :: rPa(sq, kss) fun rmPieceAt(c, sq, bd) = if bd is Board(wkss, bkss) and c is White then Board(rPa(sq, wkss), bkss) Black then Board(wkss, rPa(sq, bkss)) fun putPieceAt(sq, c_k, bd) = if c_k is [c, k] and bd is Board(wkss, bkss) and c is White then Board([k, sq] :: wkss, bkss) Black then Board(wkss, [k, sq] :: bkss) fun kSq(kss) = if kss is [King, s] :: _ then s _ :: kss then kSq(kss) Nil then throw Error("kSq") fun kingSquare(c, bd) = if bd is Board(wkss, bkss) and c is White then kSq(wkss) Black then kSq(bkss) fun opponent(c) = if c is White then Black else White fun colourOf(c_k) = if c_k is [c, _] then c fun kindOf(c_k) = if c_k is [_, k] then k fun onboard(p_q) = if p_q is [p, q] then (p >= 1 and p <= 8) and (q >= 1 and q <= 8) fun forcesColoured(c, bd) = if bd is Board(wkss, bkss) and c is White then wkss Black then bkss val emptyBoard = Board(Nil, Nil) //│ emptyBoard = Board([], []) fun showMove(withPiece, m) = if m is MoveInFull([c, k], sq, Move(sq_, mcp, mpp)) then let capt = mcp is Some(_) let prom = mpp is Some(_) (if withPiece then showPiece([c, k]) +: if ((k === King) || (k is Pawn and not(capt || prom))) then Nil else "/" :: showSquare(c, sq) else Nil ) +: maybe("-" :: Nil, cp => "x" :: append(showPiece(cp), "/" :: Nil), mcp) +: showSquare(c, sq_) +: maybe(Nil, pp => "(" :: append(showPiece(pp), ")" :: Nil), mpp) fun showMoveInFull(a) = showMove(true, a) fun showMovesAfter(p_, mifs) = if mifs is Nil then Nil MoveInFull(p, sq, d_) :: mifs and p_ is MoveInFull(p_, sq_, _) then nofibStringToList(", ") +: showMove(not(eqTup2(p, p_)) || not(eqTup2(sq, sq_)), MoveInFull(p, sq, d_)) +: showMovesAfter(MoveInFull(p, sq, d_), mifs) fun showMoves(mifs) = if mifs is Nil then throw Error("showMoves") mif :: mifs then showMoveInFull(mif) +: showMovesAfter(mif, mifs) fun sift(c, bd, ms, sqs) = if sqs is Nil then ms sq :: sqs and onboard(sq) and pieceAt(bd, sq) is None then sift(c, bd, Move(sq, None, None) :: ms, sqs) Some(p_) and colourOf(p_) === c then sift(c, bd, ms, sqs) else sift(c, bd, Move(sq, Some(p_), None) :: ms, sqs) else sift(c, bd, ms, sqs) fun moveLine(bd, c, sq, inc, cont) = fun ml(sq, ms) = let sq_ = inc(sq) if onboard(sq_) and pieceAt(bd, sq_) is None then ml(sq_, Move(sq_, None, None) :: ms) Some(p_) and not(colourOf(p_) === c) then cont(Move(sq_, Some(p_), None) :: ms) else cont(ms) else cont(ms) ms => ml(sq, ms) fun bishopmoves(c, sq, bd) = (moveLine of bd c sq case { [x, y] then [x - 1, y + 1] } moveLine of bd c sq case { [x, y] then [x + 1, y + 1] } moveLine of bd c sq case { [x, y] then [x - 1, y - 1] } moveLine of bd c sq case { [x, y] then [x + 1, y - 1] } x => x ) of Nil fun rookmoves(c, sq, bd) = (moveLine of bd c sq case { [x, y] then [x - 1, y] } moveLine of bd c sq case { [x, y] then [x + 1, y] } moveLine of bd c sq case { [x, y] then [x, y - 1] } moveLine of bd c sq case { [x, y] then [x, y + 1] } x => x ) of Nil fun kingmoves(c, pq, bd) = if pq is [p, q] then sift of c bd Nil [p - 1, q + 1] :: [p, q + 1] :: [p + 1, q + 1] :: [p - 1, q] :: [p + 1, q] :: [p - 1, q - 1] :: [p, q - 1] :: [p + 1, q - 1] :: Nil fun knightmoves(c, pq, bd) = if pq is [p, q] then sift of c bd Nil [p - 1, q + 2] :: [p + 1, q + 2] :: [p - 2, q + 1] :: [p + 2, q + 1] :: [p - 2, q - 1] :: [p + 2, q - 1] :: [p - 1, q - 2] :: [p + 1, q - 2] :: Nil fun pawnmoves(c, pq, bd) = if pq is [p, q] then let fwd = if c is White then 1 else -1 fun promote(xy, mcp) = if xy is [x, y] and ((c is Black) and y === 1) || ((c is White) and y === 8) then map( param => Move([x, y], mcp, Some(param)), [c, Queen] :: [c, Rook] :: [c, Bishop] :: [c, Knight] :: Nil ) else Move([x, y], mcp, None) :: Nil let movs = let on1 = [p, q + fwd] let on2 = [p, q + 2 * fwd] if pieceAt(bd, on1) is None then promote(on1, None) +: (if (q === 2 and c is White) || (q === 7 and c is Black) and pieceAt(bd, on2) is None then Move(on2, None, None) :: Nil else Nil) else Nil fun lscomp1(ls) = if ls is Nil then Nil sq :: sqs then fun lscomp2(ls) = if ls is Nil then lscomp1(sqs) h :: ls and h is Some(p_) and not(colourOf(p_) === c) then promote(sq, Some(p_)) :: lscomp2(ls) else lscomp2(ls) lscomp2(pieceAt(bd, sq) :: Nil) let caps = concat(lscomp1([p + 1, q + fwd] :: [p - 1, q + fwd] :: Nil)) movs +: caps fun queenmoves(c, sq, bd) = bishopmoves(c, sq, bd) +: rookmoves(c, sq, bd) fun kingincheck(c, bd) = fun givesCheck(kxy) = if kxy is [k, [x, y]] then fun kthreat(param) = if kingSquare(c, bd) is [xk, yk] and param is King then (abs(x - xk) <= 1) and (abs(y - yk) <= 1) Queen then kthreat(Rook) || kthreat(Bishop) Rook then ((x === xk) && emptyAtAll(bd, case { [xe, ye] then (xe === xk) && ((min(y, yk) < ye) && (ye < max(y, yk))) } )) || ((y === yk) && emptyAtAll(bd, case { [xe, ye] then (ye === yk) && ((min(x, xk) < xe) && (xe < max(x, xk))) } )) Bishop then (((x + y) === (xk + yk)) && emptyAtAll(bd, case { [xe, ye] then ((xe + ye) === (xk + yk)) && ((min(x, xk) < xe) && (xe < max(x, xk))) } )) || (((x - y) === (xk - yk)) && emptyAtAll(bd, case { [xe, ye] then ((xe - ye) === (xk - yk)) && ((min(x, xk) < xe) && (xe < max(x, xk))) } )) Knight then (((abs(x - xk)) === 2) && ((abs(y - yk)) === 1)) || (((abs(x - xk)) === 1) && ((abs(y - yk)) === 2)) Pawn then ((abs(x - xk) === 1) && (if c is Black then yk === (y + 1) else yk === (y - 1))) kthreat(k) any(givesCheck, forcesColoured(opponent(c), bd)) fun tryMove(c, ksq, m, bd) = if ksq is [k, sq] and m is Move(sq_, mcp, mpp) then let p = [c, k] let bd1 = rmPieceAt(c, sq, bd) let p_ = maybe(p, x => x, mpp) let bd2 = maybe( putPieceAt(sq_, p_, bd1), dummy => putPieceAt(sq_, p_, rmPieceAt(opponent(c), sq_, bd1)), mcp ) if not(kingincheck(c, bd2)) then Some([MoveInFull(p, sq, Move(sq_, mcp, mpp)), bd2]) else None else throw Error(m) fun rawmoves(c, ksq, bd) = if ksq is [k, sq] then let m = if k is King then kingmoves Queen then queenmoves Rook then rookmoves Bishop then bishopmoves Knight then knightmoves Pawn then pawnmoves let res = m(c, sq, bd) res fun moveDetailsFor(c, bd) = foldr of (ksq, ms) => foldr of (rm, ms_) => maybe(x => x, h => (t => h :: t), tryMove(c, ksq, rm, bd))(ms_) ms rawmoves(c, ksq, bd) Nil forcesColoured(c, bd) fun comment(s) = (s is Nil) || listEq(take(2, s), nofibStringToList("--")) fun last(ls) = if ls is x :: Nil then x h :: t then last(t) fun intOfString(s) = globalThis.parseInt(nofibListToString(s)) fun parseGoal(ls) = if ls is gltxt :: Nil then let ws = words(gltxt) let c = if listEq(head(ws), nofibStringToList("Black")) then Black else White let n = intOfString(last(ws)) [c, n] else throw Error("parseGoal") fun parseSquare(r, f, c) = if c === "-" then Nil else let clr = if isUpper(c) then Black else White let kin = if toLower(c) === "k" then King "q" then Queen "r" then Rook "b" then Bishop "n" then Knight "p" then Pawn [[clr, kin], [f, r]] :: Nil fun parseRank(r, x) = concat of zipWith of (a, b) => parseSquare(r, a, b) enumFromTo(1, 8) filter(pp => not(pp === " "), x) fun parseBoard(ls) = fun addPiece(p_sq, x) = if p_sq is [p, sq] then putPieceAt(sq, p, x) foldr of addPiece emptyBoard concat(zipWith(parseRank, reverse(enumFromTo(1, 8)), ls)) fun parseProblem(s) = let bdtxt_gltxt = splitAt(8, filter(x => not(comment(x)), s)) if bdtxt_gltxt is [bdtxt, gltxt] then let bd = parseBoard(bdtxt) let gl = parseGoal(gltxt) [bd, gl] fun readProblem(s) = parseProblem(lines(s)) fun foldr_lz(f, a, x) = if x is h :: t then f(h, lazy of () => foldr_lz(f, a, t)) Nil then a //│ ———————————————————————————————————————————————————————————————————————————————— fun replies(bd, c, n) = let mds = moveDetailsFor(c, bd) fun solnAnd(mifb, rest) = if mifb is [mif, b] then let sm = solution(b, opponent(c), n - 1) if sm is None then None Some(s) and force(rest) is None then None Some(ms) then Some([mif, s] :: ms) if n === 0 and null_(mds) then Some(Nil) else None > 0 then foldr_lz(solnAnd, Some(Nil), mds) else throw Error("n < 0") fun solution(bd, c, n) = fun solnOr(mifb, other) = if mifb is [mif, b] then let rsm = replies(b, opponent(c), n - 1) if rsm is None then force(other) Some(Nil) and kingincheck(opponent(c), b) then Some(Solution(mif, Nil)) else force(other) Some(rs) then Some(Solution(mif, rs)) if n > 0 then let mds = moveDetailsFor(c, bd) foldr_lz(solnOr, None, mds) else throw Error("n <= 0") //│ ———————————————————————————————————————————————————————————————————————————————— data class Soln(a: MoveInFull, b: List[[List[MoveInFull],Soln]]) fun tab(n) = if n <= 0 then Nil else " " :: tab(n - 1) //│ ———————————————————————————————————————————————————————————————————————————————— fun showReplies(rs, n) = if rs is Nil then Nil [mifs, s] :: rs then tab(n) +: nofibStringToList("if ") +: if null_(rs) && (listLen(mifs) > 1) then nofibStringToList("others") else showMoves(mifs) +: nofibStringToList("; ") +: showSoln(s, n + 1) +: showReplies(rs, n) fun showSoln(s, n) = if s is Soln(mif, rs) then nofibStringToList(stringOfInt(n)) +: nofibStringToList(". ") +: showMoveInFull(mif) +: if rs is Nil then nofibStringToList("++\n") [mifs, s_] :: Nil then nofibStringToList(", ") +: (if listLen(mifs) > 1 then nofibStringToList("...") else showMoves(mifs)) +: nofibStringToList("; ") +: showSoln(s_, n + 1) else nofibStringToList(",\n") +: showReplies(sort(rs), n) //│ ———————————————————————————————————————————————————————————————————————————————— //│ ———————————————————————————————————————————————————————————————————————————————— fun compact(s) = if s is Solution(mif, rs) then Soln(mif, foldr(insertCompact, Nil, rs)) fun insertCompact(mif_s, ls) = if mif_s is [mif, s] then fun insert(x, ls) = if ls is Nil then x :: Nil y :: ys then if moveInFullCompare(x, y) > 0 then y :: insert(x, ys) else x :: y :: ys let cs = compact(s) fun ic(ls) = if ls is Nil then [mif :: Nil, cs] :: Nil [mifs, cs_] :: etc then let a = showSoln(cs, 1) let b = showSoln(cs_, 1) if ltList(a, b, (x, y) => x < y, (x, y) => x > y) then [mif :: Nil, cs] :: [mifs, cs_] :: etc listEq(a, b) then [insert(mif, mifs), cs] :: etc not(ltList(a, b, (x, y) => x < y, (x, y) => x > y)) then [mifs, cs_] :: ic(etc) else throw Error("compare error") ic(ls) //│ ———————————————————————————————————————————————————————————————————————————————— fun showResult(s) = if s is None then nofibStringToList("No solution!") Some(s) then showSoln(compact(s), 1) fun solve(bd, c, n) = showResult(solution(bd, c, (2 * n) - 1)) fun testMate_nofib(dummy) = let input = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/heathcote3.prob").toString() let bdcn = readProblem(input) if bdcn is [bd, [c, n]] then showBoard(bd) +: nofibStringToList("\n") +: showColour(c) +: nofibStringToList(" to move and mate in ") +: nofibStringToList(stringOfInt(n)) +: nofibStringToList("\n") +: nofibStringToList("\n") +: solve(bd, c, n) fun main() = nofibListToString(testMate_nofib(0)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/minimax.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module minimax with ... fun andd(ls) = if ls is Nil then true b :: bs then b && andd(bs) abstract class Piece: X | O | Empty object X extends Piece O extends Piece Empty extends Piece fun eqPiece(p1, p2) = if p1 is X and p2 is X then true O and p2 is O then true Empty and p2 is Empty then true else false abstract class Evaluation: XWin | OWin | Score object XWin extends Evaluation OWin extends Evaluation data class Score(i: Int) extends Evaluation fun evaluationEq(x, y) = if x is XWin and y is XWin then true OWin and y is OWin then true Score(i) and y is Score(j) and i === j then true else false data class Branch[A](a: A, cs: List[Branch[A]]) fun showEvaluation(e) = if e is XWin then nofibStringToList("XWin") OWin then nofibStringToList("OWin") Score(i) then nofibStringToList("Score ") +: nofibStringToList(stringOfInt(i)) val win1 = (1 :: 1 :: 1 :: Nil) :: (0 :: 0 :: 0 :: Nil) :: (0 :: 0 :: 0 :: Nil) :: Nil //│ win1 = [[1,1,1],[0,0,0],[0,0,0]] val win2 = (0 :: 0 :: 0 :: Nil) :: (1 :: 1 :: 1 :: Nil) :: (0 :: 0 :: 0 :: Nil) :: Nil //│ win2 = [[0,0,0],[1,1,1],[0,0,0]] val win3 = (0 :: 0 :: 0 :: Nil) :: (0 :: 0 :: 0 :: Nil) :: (1 :: 1 :: 1 :: Nil) :: Nil //│ win3 = [[0,0,0],[0,0,0],[1,1,1]] val win4 = (1 :: 0 :: 0 :: Nil) :: (1 :: 0 :: 0 :: Nil) :: (1 :: 0 :: 0 :: Nil) :: Nil //│ win4 = [[1,0,0],[1,0,0],[1,0,0]] val win5 = (0 :: 1 :: 0 :: Nil) :: (0 :: 1 :: 0 :: Nil) :: (0 :: 1 :: 0 :: Nil) :: Nil //│ win5 = [[0,1,0],[0,1,0],[0,1,0]] val win6 = (0 :: 0 :: 1 :: Nil) :: (0 :: 0 :: 1 :: Nil) :: (0 :: 0 :: 1 :: Nil) :: Nil //│ win6 = [[0,0,1],[0,0,1],[0,0,1]] val win7 = (1 :: 0 :: 0 :: Nil) :: (0 :: 1 :: 0 :: Nil) :: (0 :: 0 :: 1 :: Nil) :: Nil //│ win7 = [[1,0,0],[0,1,0],[0,0,1]] val win8 = (0 :: 0 :: 1 :: Nil) :: (0 :: 1 :: 0 :: Nil) :: (1 :: 0 :: 0 :: Nil) :: Nil //│ win8 = [[0,0,1],[0,1,0],[1,0,0]] val wins = win1 :: win2 :: win3 :: win4 :: win5 :: win6 :: win7 :: win8 :: Nil //│ wins = [[[1,1,1],[0,0,0],[0,0,0]],[[0,0,0],[1,1,1],[0,0,0]],[[0,0,0],[0,0,0],[1,1,1]],[[1,0,0],[1,0,0],[1,0,0]],[[0,1,0],[0,1,0],[0,1,0]],[[0,0,1],[0,0,1],[0,0,1]],[[1,0,0],[0,1,0],[0,0,1]],[[0,0,1],[0,1,0],[1,0,0]]] fun showPiece(p) = if p is X then nofibStringToList("X") O then nofibStringToList("O") Empty then nofibStringToList(" ") fun showRow(ps) = if ps is p1 :: p2 :: p3 :: Nil then showPiece(p1) +: nofibStringToList("|") +: showPiece(p2) +: nofibStringToList("|") +: showPiece(p3) fun showBoard(rs) = if rs is r1 :: r2 :: r3 :: Nil then showRow(r1) +: nofibStringToList("\n------\n") +: showRow(r2) +: nofibStringToList("\n------\n") +: showRow(r3) +: nofibStringToList("\n\n") fun insert(p, ps, i) = if ps is Cons(p1, Cons(p2, Cons(p3, Nil))) and i === 1 then p :: p2 :: p3 :: Nil 2 then p1 :: p :: p3 :: Nil 3 then p1 :: p2 :: p :: Nil fun empty_(x, r) = if x === 1 and r is Empty :: _ :: _ :: Nil then true x === 2 and r is _ :: Empty :: _ :: Nil then true x === 3 and r is _ :: _ :: Empty :: Nil then true else false fun empty(pos, board) = if board is r1 :: r2 :: r3 :: Nil and pos is [1, x] then empty_(x, r1) [2, x] then empty_(x, r2) [3, x] then empty_(x, r3) fun placePiece(p, board, pos) = if not(empty(pos, board)) then Nil board is r1 :: r2 :: r3 :: Nil and pos is [1, x] then (insert(p, r1, x) :: r2 :: r3 :: Nil) :: Nil [2, x] then (r1 :: insert(p, r2, x) :: r3 :: Nil) :: Nil [3, x] then (r1 :: r2 :: insert(p, r3, x) :: Nil) :: Nil fun fullBoard(b) = andd of map of x => not(eqPiece(x, Empty)) concat(b) fun newPositions(piece, board) = fun lscomp1(ls) = if ls is Nil then Nil x :: xs then fun lscomp2(ls) = if ls is Nil then lscomp1(xs) y :: ys then [x, y] :: lscomp2(ys) lscomp2(1 :: 2 :: 3 :: Nil) concat of map of pos => placePiece(piece, board, pos) lscomp1(1 :: 2 :: 3 :: Nil) val initialBoard = replicate(3, replicate(3, Empty)) //│ initialBoard = [[Empty,Empty,Empty],[Empty,Empty,Empty],[Empty,Empty,Empty]] fun eval(x) = if x === 3 then XWin -3 then OWin else Score(x) fun interpret(x, l) = if l is Nil then Score(x) Score(y) :: ls then interpret(x + y, ls) XWin :: _ then XWin OWin :: _ then OWin fun scorePiece(p, score) = if p is X then score Empty then 0 O then -score fun map2(f, xs, ys) = if xs is Nil then Nil x :: xs and ys is y :: ys then f(x, y) :: map2(f, xs, ys) Nil then Nil fun score(board, win) = eval of sum of map of sum map2((x, y) => map2(scorePiece, x, y), board, win) fun static(board) = interpret(0, map(x => score(board, x), wins)) fun repTree(f, g, a) = Branch(a, map(x => repTree(g, f, x), f(a))) fun mapTree(f, t) = if t is Branch(a, l) then Branch(f(a), map(x => mapTree(f, x), l)) fun prune(n, t) = if t is Branch(a, l) and n === 0 then Branch(a, Nil) < 0 then throw Error("Tree.prune: < 0") else Branch(a, map(x => prune(n - 1, x), l)) fun opposite(p) = if p is X then O O then X else throw Error("opposite") fun best(f, bs, ss) = if bs is b :: bs and ss is s :: ss then fun best_(b, s, ls1, ls2) = if ls1 is Nil and ls2 is Nil then [b, s] b_ :: bs and ls2 is s_ :: ss and evaluationEq(s, f(s, s_)) then best_(b, s, bs, ss) else best_(b_, s_, bs, ss) best_(b, s, bs, ss) fun showMove(m) = if m is [b, e] then showEvaluation(e) +: nofibStringToList("\n") +: showBoard(b) fun max_(e1, e2) = if e1 is XWin then XWin e2 is XWin then XWin e2 is OWin then e1 e1 is OWin then e2 e1 is Score(x) and e2 is Score(y) and x > y then Score(x) else Score(y) fun min_(e1, e2) = if e1 is OWin then OWin e2 is OWin then OWin e2 is XWin then e1 e1 is XWin then e2 e1 is Score(x) and e2 is Score(y) and x < y then Score(x) else Score(y) fun mise(f, g, t) = if t is Branch(a, Nil) then a Branch(_, l) then foldr(f, g(OWin, XWin), map(x => mise(g, f, x), l)) fun searchTree(p, board) = prune of 5 repTree of x => newPositions(p, x), x => newPositions(opposite(p), x), board fun cropTree(t) = if t is Branch(a, Nil) then Branch(a, Nil) Branch(Score(x), l) then Branch(Score(x), map(cropTree, l)) Branch(x, l) then Branch(x, Nil) fun bestMove(p, f, g, b) = mise(f, g, cropTree(mapTree(static, searchTree(p, b)))) fun alternate(player, f, g, board) = if fullBoard(board) then Nil evaluationEq(static(board), XWin) then Nil evaluationEq(static(board), OWin) then Nil let opposition = opposite(player) let possibles = newPositions(player, board) let scores = map(x => bestMove(opposition, g, f, x), possibles) let boardd_eval = best(f, possibles, scores) boardd_eval is [boardd, eval] then [boardd, eval] :: alternate(opposition, g, f, boardd) fun prog(input) = let testBoard = (Empty :: O :: Empty :: Nil) :: (Empty :: X :: Empty :: Nil) :: (Empty :: Empty :: Empty :: Nil) :: Nil fun board(x) = if x === "doesn't happen" then testBoard +: testBoard else testBoard let game = alternate(X, max_, min_, board(input)) nofibStringToList("OXO\n") +: concat of map(showMove, game) fun main() = nofibListToString(prog("180000")) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/para.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module para with ... fun unwords(ws) = if ws is Nil then Nil w :: ws then fun go(vs) = if vs is Nil then Nil v :: vs then " " :: (v +: go(vs)) w +: go(ws) fun break_(p, xs) = if xs is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] break_(p, xs) is [ys, zs] then [x :: ys, zs] fun isSpace(c) = c === " " fun words(s) = if leaveWhile(isSpace, s) is Nil then Nil h :: t and break_(isSpace, h :: t) is [w, s_] then w :: words(s_) fun lines(s) = if break_(x => x === "\n", s) is [l, s_] then Cons( l, if s_ is Nil then Nil _ :: s__ then lines(s__) ) fun unlines(ls) = concat(map(l => l +: nofibStringToList("\n"), ls)) fun all(p, xs) = if xs is Nil then true x :: xs then p(x) && all(p, xs) fun fold1(f, g, xs) = if xs is a :: Nil then g(a) a :: x then f(a, fold1(f, g, x)) fun scan1(f, g, xs) = fold1((a, s) => f(a, head(s)) :: s, a => g(a) :: Nil, xs) fun tails(xs) = scan1((a, s) => a :: s, a => a :: Nil, xs) fun single(xs) = if xs is a :: Nil then true _ then false fun minWith(f, xs) = fold1((a, b) => if f(a) < f(b) then a else b, x => x, xs) fun new_(w, ls) = (w :: Nil) :: ls fun glue(w, ls) = if ls is l :: ls_ then (w :: l) :: ls_ fun formats(txt) = fold1 of (w, ps) => map(p => new_(w, p), ps) +: map(p => glue(w, p), ps) x => ((x :: Nil) :: Nil) :: Nil txt val maxw = 70 //│ maxw = 70 fun width(ls) = fun plus(w, n) = listLen(w) + 1 + n fold1(plus, listLen, ls) fun fits(xs) = width(xs) <= maxw fun feasible(a) = all(fits, a) val optw = 63 //│ optw = 63 fun cost(ls) = fun linc(l) = let a = optw - width(l) in a * a fun plus(l, n) = linc(l) + n fold1(plus, x => 0, ls) fun par0(x) = minWith(cost, filter(feasible, formats(x))) fun fitH(ls) = fits(head(ls)) fun fst3(a_b_c) = if a_b_c is [a, b, c] then a fun snd3(a_b_c) = if a_b_c is [a, b, c] then b fun thd3(a_b_c) = if a_b_c is [a, b, c] then c fun width_tl(a_b_c) = fst3(a_b_c) fun cost_tl(a_b_c) = snd3(a_b_c) fun len_tl(a_b_c) = thd3(a_b_c) fun tile(ws, a_b) = if a_b is [Nil, n] then Nil [m :: ms, n] then let l = n - m if splitAt(l, ws) is [ws1, ws2] then ws1 :: tile(ws2, [leave(l, m :: ms), m]) fun null__(a_b) = if a_b is [Nil, Nil] then true else false fun single_(a_b) = if a_b is [x, y] then (null_(x) && single(y)) || (single(x) && null_(y)) val nil_ = [Nil, Nil] //│ nil_ = [[], []] fun head_(a_b) = if a_b is [x, y] and not(null_(x)) then head(x) else head(y) fun last_(a_b) = if a_b is [y, x] and not(null_(x)) then head(x) else head(y) fun cons_(a, a_b) = if a_b is [x, y] and not(null_(y)) then [a :: x, y] else [a :: Nil, x] fun snoc_(a, a_b) = if a_b is [y, x] and not(null_(y)) then [y, a :: x] else [x, a :: Nil] fun tail_(a_b) = if a_b is [x, y] and null_(x) then [Nil, Nil] single(x) and splitAt(intDiv(listLen(y), 2), y) is [y0, y1] then [reverse(y1), y0] else [tail(x), y] fun init_(a_b) = if a_b is [y, x] and null_(x) then [Nil, Nil] single(x) and splitAt(intDiv(listLen(y), 2), y) is [y0, y1] then [y0, reverse(y1)] else [y, tail(x)] fun unformat(a, l) = fold1((xs, ys) => xs +: (a :: Nil) +: ys, x => x, l) fun format(a, x) = if x is Nil then Nil :: Nil else fun unknownEq(a, b) = a === b fun breakk(a, b, xs) = if unknownEq(a, b) then Nil :: xs else (b :: head(xs)) :: tail(xs) fun start(a, b) = breakk(a, b, Nil :: Nil) fold1((x, y) => breakk(a, x, y), y => start(a, y), x) fun unparas(ls) = unformat(Nil, ls) fun paras(ls) = filter(x => listNeq(Nil, x), format(Nil, ls)) fun parse(ls) = paras(map(words, lines(ls))) fun unparse(ls) = unlines(map(unwords, unparas(ls))) fun startr(a) = if a <= maxw then [cons_([0, 0, 0], nil_), a, 1] else throw Error("startr param error") fun ceildiv(n, m) = intDiv(n + m - 1, m) fun fmtWith(par) = unparse(map(x => par(concat(x)), parse(par))) fun stepr(w, ps_tw_tl) = if ps_tw_tl is [ps, tw, tl] then let tot_width = w + 1 + tw let tot_len = 1 + tl fun single(p) = len_tl(p) === 0 fun width_hd(p) = if single(p) then tot_width else tot_width - width_tl(p) - 1 fun cost(p) = if single(p) then 0 else cost_tl(p) + (let a = optw - width_hd(p) in a * a) fun old_width_hd(p) = if single(p) then tw else tw - width_tl(p) - 1 fun new_(p) = if single(p) then [tw, 0, tl] else [tw, cost_tl(p) + (let x = optw - old_width_hd(p) in x * x), tl] fun trim(ps_pq) = if null__(ps_pq) then ps_pq single_(ps_pq) then ps_pq let ps_p = init_(ps_pq) let q = last_(ps_pq) let p = last_(ps_p) cost(p) <= cost(q) then trim(ps_p) else ps_pq fun leave_nofit(ps_p) = if null__(ps_p) then ps_p width_hd(last_(ps_p)) > maxw then leave_nofit(init_(ps_p)) else ps_p fun bf(p, q) = let wqh = width_hd(q) let rqh = maxw - wqh + 1 if single(q) && cost_tl(p) === 0 then min(optw - width_hd(p), rqh) single(q) then rqh else min(ceildiv(cost(p) - cost(q), 2 * (wqh - width_hd(p))), rqh) fun myAdd(p, qr_rs) = if single_(qr_rs) || null__(qr_rs) then cons_(p, qr_rs) let q = head_(qr_rs) let r_rs = tail_(qr_rs) let r = head_(r_rs) bf(p, q) <= bf(q, r) then myAdd(p, r_rs) else cons_(p, qr_rs) [trim(leave_nofit(myAdd(new_(last_(ps)), ps))), tot_width, tot_len] fun par3(ws) = let zs = scan1(stepr, startr, map(listLen, ws)) tile(ws, [map(x => len_tl(last_(fst3(x))), zs), thd3(head(zs))]) fun fmt(x) = unparse(map(par3, concat(parse(x)))) val test = concat of nofibStringToList("In the constructive programming community it is commonplace to see ") :: nofibStringToList("formal developments of textbook algorithms. In the algorithm design ") :: nofibStringToList("community, on the other hand, it may be well known that the textbook ") :: nofibStringToList("solution to a problem is not the most efficient possible. However, in ") :: nofibStringToList("presenting the more efficient solution, the algorithm designer will ") :: nofibStringToList("usually omit some of the implementation details, this creating an ") :: nofibStringToList("algorithm gap between the abstract algorithm and its concrete ") :: nofibStringToList("implementation. This is in contrast to the formal development, which ") :: nofibStringToList("usually presents the complete concrete implementation of the less ") :: nofibStringToList("efficient solution.\n\n") :: Nil //│ test = ["I","n"," ","t","h","e"," ","c","o","n","s","t","r","u","c","t","i","v","e"," ","p","r","o","g","r","a","m","m","i","n","g"," ","c","o","m","m","u","n","i","t","y"," ","i","t"," ","i","s"," ","c","o","m","m","o","n","p","l","a","c","e"," ","t","o"," ","s","e","e"," ","f","o","r","m","a","l"," ","d","e","v","e","l","o","p","m","e","n","t","s"," ","o","f"," ","t","e","x","t","b","o","o","k"," ","a","l","g","o","r","i","t","h","m","s","."," ","I","n"," ","t","h","e"," ","a","l","g","o","r","i","t","h","m"," ","d","e","s","i","g","n"," ","c","o","m","m","u","n","i","t","y",","," ","o","n"," ","t","h","e"," ","o","t","h","e","r"," ","h","a","n","d",","," ","i","t"," ","m","a","y"," ","b","e"," ","w","e","l","l"," ","k","n","o","w","n"," ","t","h","a","t"," ","t","h","e"," ","t","e","x","t","b","o","o","k"," ","s","o","l","u","t","i","o","n"," ","t","o"," ","a"," ","p","r","o","b","l","e","m"," ","i","s"," ","n","o","t"," ","t","h","e"," ","m","o","s","t"," ","e","f","f","i","c","i","e","n","t"," ","p","o","s","s","i","b","l","e","."," ","H","o","w","e","v","e","r",","," ","i","n"," ","p","r","e","s","e","n","t","i","n","g"," ","t","h","e"," ","m","o","r","e"," ","e","f","f","i","c","i","e","n","t"," ","s","o","l","u","t","i","o","n",","," ","t","h","e"," ","a","l","g","o","r","i","t","h","m"," ","d","e","s","i","g","n","e","r"," ","w","i","l","l"," ","u","s","u","a","l","l","y"," ","o","m","i","t"," ","s","o","m","e"," ","o","f"," ","t","h","e"," ","i","m","p","l","e","m","e","n","t","a","t","i","o","n"," ","d","e","t","a","i","l","s",","," ","t","h","i","s"," ","c","r","e","a","t","i","n","g"," ","a","n"," ","a","l","g","o","r","i","t","h","m"," ","g","a","p"," ","b","e","t","w","e","e","n"," ","t","h","e"," ","a","b","s","t","r","a","c","t"," ","a","l","g","o","r","i","t","h","m"," ","a","n","d"," ","i","t","s"," ","c","o","n","c","r","e","t","e"," ","i","m","p","l","e","m","e","n","t","a","t","i","o","n","."," ","T","h","i","s"," ","i","s"," ","i","n"," ","c","o","n","t","r","a","s","t"," ","t","o"," ","t","h","e"," ","f","o","r","m","a","l"," ","d","e","v","e","l","o","p","m","e","n","t",","," ","w","h","i","c","h"," ","u","s","u","a","l","l","y"," ","p","r","e","s","e","n","t","s"," ","t","h","e"," ","c","o","m","p","l","e","t","e"," ","c","o","n","c","r","e","t","e"," ","i","m","p","l","e","m","e","n","t","a","t","i","o","n"," ","o","f"," ","t","h","e"," ","l","e","s","s"," ","e","f","f","i","c","i","e","n","t"," ","s","o","l","u","t","i","o","n",".","\n","\n"] fun testPara_nofib() = if null_(test) then Nil else fmt(test) fun main() = nofibListToString(testPara_nofib()) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/power.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module power with ... abstract class Pss[T]: Pz | Pc[T] type Ps[T] = Lazy[Pss[T]] data class Pc[T](f: T, s: Ps[T]) extends Pss[T] object Pz extends Pss fun list() = lazy of () => Pc(1, list()) fun x_() = lazy of () => Pc(0, lazy of () => Pc(1, lazy of () => Pz)) fun fromIntegerPs(c) = if c == 0 then lazy of () => Pz else lazy of () => Pc(c, lazy of () => Pz) fun extract(n, ps) = if n == 0 then Nil force(ps) is Pz then Nil Pc(x, ps) then x :: extract(n - 1, ps) fun dotMult(c, ps) = lazy of () => if force(ps) is Pz then Pz Pc(f, fs_) then Pc(c * f, dotMult(c, fs_)) fun dotMultSndLz(c, ps) = lazy of () => if force(force(ps)) is Pz then Pz Pc(f, fs_) then Pc(c * f, dotMult(c, fs_)) fun negatePs(ps) = lazy of () => if force(ps) is Pz then Pz Pc(f, fs_) then Pc(-f, negatePs(fs_)) fun addPs(fss, gs) = lazy of () => if force(fss) is Pz then force(gs) Pc(f, fs_) and force(gs) is Pz then force(fss) Pc(g, gs) then Pc(f+g, addPs(fs_, gs)) fun minusPs(a, b) = addPs(a, negatePs(b)) fun multPs(fss, gss) = lazy of () => if force(fss) is Pz then Pz Pc(f, fs_) and force(gss) is Pz then Pz Pc(g, gs) then Pc(f*g, addPs(addPs(dotMult(f, gs), dotMult(g, fs_)), multPs(multPs(x_(), fs_), gs))) fun multPsFstLz(fss, gss) = lazy of () => if force(force(fss)) is Pz then Pz Pc(f, fs_) and force(gss) is Pz then Pz Pc(g, gs) then Pc(f*g, addPs(addPs(dotMult(f, gs), dotMult(g, fs_)), multPs(multPs(x_(), fs_), gs))) fun powerPs(a, n) = if n <= 0 then fromIntegerPs(1) else multPs(a, powerPs(a, n - 1)) fun divPs(fss, gss) = lazy of () => if force(fss) is Pz and force(gss) is Pz then throw Error("power series 0/0") Pc(0, gs) then force(divPs((lazy of () => Pz), gs)) else Pz Pc(0, fs_) and force(gss) is Pc(0, gs) then force(divPs(fs_, gs)) Pc(g, gs) then let q = 0 Pc(q, divPs(addPs(fs_, negatePs(dotMult(q, gs))), lazy of () => Pc(g, gs))) Pc(f, fs_) and force(gss) is Pc(g, gs) then let q = f/g Pc(q, divPs(addPs(fs_, negatePs(dotMult(q, gs))), lazy of () => Pc(g, gs))) fun compose_(fss, gss) = lazy of () => if force(fss) is Pz then Pz Pc(f, fs_) and force(gss) is Pz then Pc(f, lazy of () => Pz) Pc(0, gs) then Pc(f, multPs(gs, compose_(fs_, lazy of () => Pc(0, gs)))) else force(addPs((lazy of () => Pc(f, lazy of () => Pz)), multPs(gss, compose_(fs_, gss)))) fun composeSndLz_(fss, gss) = lazy of () => if force(fss) is Pz then Pz Pc(f, fs_) and force(force(gss)) is Pz then Pc(f, lazy of () => Pz) Pc(0, gs) then Pc(f, multPs(gs, compose_(fs_, lazy of () => Pc(0, gs)))) else force(addPs((lazy of () => Pc(f, lazy of () => Pz)), multPs(gss, composeSndLz_(fs_, gss)))) fun revert(fss) = lazy of () => if force(fss) is Pc(0, fs_) then fun rs() = lazy of () => Pc(0, divPs(fromIntegerPs(1), compose_(fs_, rs()))) force(rs()) Pc(f0, kss) and force(kss) is Pc(f1, gss) and force(gss) is Pz then Pc(-1/f1, lazy of () => Pc(1/f1, lazy of () => Pz)) fun deriv(fss) = lazy of () => if force(fss) is Pz then Pz Pc(_, fs_) then fun deriv1(gss, n) = lazy of () => if force(gss) is Pz then Pz Pc(f, fs_) then Pc(n * f, deriv1(fs_, n+1)) force(deriv1(fs_, 1)) fun integral(fs_) = fun int1(fss, n) = lazy of () => if force(fss) is Pz then Pz Pc(f, fs_) then Pc(f/n, int1(fs_, n + 1)) lazy of () => Pc(0, int1(fs_, 1)) fun integralLz(fs_) = fun int1(fss, n) = lazy of () => if force(fss) is Pz then Pz Pc(f, fs_) then Pc(f/n, int1(fs_, n + 1)) lazy of () => Pc(0, int1(fs_(), 1)) fun sqrtPs(fss) = lazy of () => if force(fss) is Pz then Pz Pc(0, gss) and force(gss) is Pc(0, fs_) then Pc(0, sqrtPs(fs_)) Pc(1, fs_) then fun qs() = lazy of () => addPs(fromIntegerPs(1), integral(divPs(deriv(lazy of () => Pc(1, fs_)), dotMultSndLz(2, qs())))) force(force(qs())) fun ts() = lazy of () => Pc(1, multPs(ts(), ts())) fun tree() = lazy of () => Pc(0, composeSndLz_(list(), lazy of () => tree())) fun cosx() = minusPs((lazy of () => Pc(1, lazy of () => Pz)), integral(integralLz(cosx))) fun sinx() = integral(minusPs((lazy of () => Pc(1, lazy of () => Pz)), integralLz(sinx))) fun testPower_nofib(p) = extract(p, minusPs(sinx(), sqrtPs(minusPs(fromIntegerPs(1), powerPs(cosx(), 2))))); extract(p, minusPs(divPs(sinx(), cosx()), revert(integral(divPs(fromIntegerPs(1), addPs(fromIntegerPs(1), powerPs(x_(), 2))))))); extract(p, ts()); extract(p, tree()) fun main() = testPower_nofib(14).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/pretty.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module pretty with ... abstract class CSeq: CNil | CAppend | CIndent | CNewline | CStr | CCh data class CAppend(a: CSeq, b: CSeq) extends CSeq CIndent(a: Int, b: CSeq) extends CSeq CStr(a: List[Char]) extends CSeq CCh(a: Char) extends CSeq object CNil extends CSeq CNewline extends CSeq abstract class PprStyle: PprForUser | PprDebug | PprShowAll | PprInterface object PprForUser extends PprStyle PprDebug extends PprStyle PprShowAll extends PprStyle PprInterface extends PprStyle val cNil = CNil //│ cNil = CNil val cNL = CNewline //│ cNL = CNewline fun cAppend(cs1, cs2) = CAppend(cs1, cs2) fun cIndent(n, cs) = CIndent(n, cs) fun cStr(s) = CStr(s) fun cCh(c) = CCh(c) fun mkIndent(n, s) = if n === 0 then s >= 8 then "\t" :: mkIndent(n - 8, s) else " " :: mkIndent(n - 1, s) //│ ———————————————————————————————————————————————————————————————————————————————— fun flattenS(nlp, seqs) = if seqs is Nil then Nil [col, seq] :: seqs then flatten(col, nlp, seq, seqs) fun flatten(n, nlp, cseq, seqs) = if cseq is CNil then flattenS(nlp, seqs) CAppend(seq1, seq2) then flatten(n, nlp, seq1, [n, seq2] :: seqs) CIndent(n_, seq) then flatten(n_ + n, nlp, seq, seqs) CNewline then "\n" :: flattenS(true, seqs) CStr(s) and nlp then mkIndent(n, s +: flattenS(false, seqs)) else s +: flattenS(false, seqs) CCh(c) and nlp then mkIndent(n, c :: flattenS(false, seqs)) else c :: flattenS(false, seqs) //│ ———————————————————————————————————————————————————————————————————————————————— fun cShow(seq) = flatten(0, true, seq, Nil) data class MkPrettyRep(cseq: CSeq, n: Int, b1: Bool, b2: Bool) fun ppShow(width, p) = if p(width, false) is MkPrettyRep(seq, ll, emp, sl) then cShow(seq) fun ppUnformatted(p) = if p(80, false) is MkPrettyRep(seq, ll, emp, sl) then cShow(seq) fun ppNil(width, is_vert) = MkPrettyRep(cNil, 0, true, width >= 0) fun ppStr(s, width, is_vert) = let ls = listLen(s) MkPrettyRep(cStr(s), ls, false, width >= ls) fun ppChar(c, width, is_vert) = MkPrettyRep(cCh(c), 1, false, width >= 1) fun ppInt(n, width, is_vert) = ppStr(nofibStringToList(stringOfInt(n)), width, is_vert) fun pp_SP(a, b) = ppStr(nofibStringToList(", "), a, b) fun ppSP(a, b) = ppChar(" ", a, b) fun ppLbrack(a, b) = ppChar("[", a, b) fun ppRbrack(a, b) = ppChar("]", a, b) fun ppLparen(a, b) = ppChar("(", a, b) fun ppRparen(a, b) = ppChar(")", a, b) fun ppSemi(a, b) = ppChar(";", a, b) fun ppComma(a, b) = ppChar(",", a, b) fun andL(a, b) = if a then b else false fun orL(a, b) = if a then true else b fun ppBeside(p1, p2, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) and p2(width - ll1, false) is MkPrettyRep(seq2, ll2, emp2, sl2) then MkPrettyRep(cAppend(seq1, cIndent(ll1, seq2)), ll1 + ll2, andL(emp1, emp2), andL(width >= 0, andL(sl1, sl2))) fun ppBesides(ps) = if ps is Nil then ppNil else foldr1((a, b) => (c, d) => ppBeside(a, b, c, d), ps) fun ppBesideSP(p1, p2, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) then let li = if emp1 then 0 else ll1 + 1 if p2(width - li, false) is MkPrettyRep(seq2, ll2, emp2, sl2) then let wi = if emp1 then 0 else 1 let sp = if orL(emp1, emp2) then cNil else cCh(" ") MkPrettyRep(cAppend(seq1, cAppend(sp, cIndent(li, seq2))), li + ll2, andL(emp1, emp2), andL(width >= wi, andL(sl1, sl2))) fun ppCat(ps) = if ps is Nil then ppNil else foldr1((a, b) => (c, d) => ppBesideSP(a, b, c, d), ps) fun ppAbove(p1, p2, width, is_vert) = if p1(width, true) is MkPrettyRep(seq1, ll1, emp1, sl1) and p2(width, true) is MkPrettyRep(seq2, ll2, emp2, sl2) then let nl = if orL(emp1, emp2) then cNil else cNL MkPrettyRep(cAppend(seq1, cAppend(nl, seq2)), ll2, andL(emp1, emp2), false) fun ppAboves(ps, a, b) = if ps is Nil then ppNil(a, b) else foldr1((a, b) => (c, d) => ppAbove(a, b, c, d), ps)(a, b) fun ppNest(n, p, width, is_vert) = if is_vert and p(width - n, true) is MkPrettyRep(seq, ll, emp, sl) then MkPrettyRep(cIndent(n, seq), ll + n, emp, sl) else p(width, false) fun ppHang(p1, n, p2, width, is_vert) = if p1(width, false) is MkPrettyRep(seq1, ll1, emp1, sl1) and p2(width - (ll1 + 1), false) is MkPrettyRep(seq2, ll2, emp2, sl2) and p2(width - n, false) is MkPrettyRep(seq2_, ll2_, emp2_, sl2_) and emp1 then p2(width, is_vert) orL(ll1 <= n, sl2) then MkPrettyRep(cAppend(seq1, cAppend(cCh(" "), cIndent(ll1 + 1, seq2))), ll1 + 1 + ll2, false, andL(sl1, sl2)) else MkPrettyRep(cAppend(seq1, cAppend(cNL, cIndent(n, seq2_))), ll2_, false, false) fun testPretty_nofib() = fun pp_word(a, b) = ppStr(nofibStringToList("xxxxx"), a, b) let pp_words = replicate(50, pp_word) fun pretty_stuff(a, b) = ppAboves( ppBesides(((a, b) => ppInt(-42, a, b)) :: ((a, b) => ppChar("@", a, b)) :: ((a, b) => ppStr(nofibStringToList("This is a string"), a, b)) :: Nil) :: ((a, b) => pp_SP(a, b)) :: ((a, b) => ppHang( (a, b) => ppStr(nofibStringToList("This is the label"), a, b), 8, ppCat(pp_words), a, b )) :: Nil, a, b ) ppShow(80, pretty_stuff) +: nofibStringToList("\n") fun main() = nofibListToString(testPretty_nofib()) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/primetest.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module primetest with ... fun even(x) = intMod(x, 2) == 0 fun int_val_of_char(x) = int_of_char(x) - 48 fun int_val_of_string(s) = fun f(l, a) = if l is Nil then a h :: t then f(t, 10 * a + int_val_of_char(h)) f(s, 0) fun break_(p, ls) = if ls is Nil then [Nil, Nil] x :: xs and p(x) then [Nil, x :: xs] break_(p, xs) is [ys, zs] then [x :: ys, zs] fun lines(s) = if break_(x => x == "|", s) is [l, s_] then let tt = if s_ is Nil then Nil _ :: s__ then lines(s__) l :: tt fun makeNumber(b, ls) = foldl((a, x) => a * b + x, 0, ls) fun chop(b, n) = fun chop_(a, n) = if divMod(n, b) is [q, r] and n == 0 then a else chop_(r :: a, q) chop_(Nil, n) fun powerMod(a, b, m) = fun f(a, b, c) = fun g(a, b) = if even(b) then g(intMod(a * a, m), intDiv(b, 2)) else f(a, b - 1, intMod(a * c, m)) if b == 0 then c else g(a, b) if b == 0 then 1 let a_ = intMod(a, m) else f(a_, b - 1, a_) fun log2(x) = listLen(chop(2, x)) fun rands(s1, s2) = let k = intDiv(s1, 53668) let s1_ = ((40014 * (s1 - (k * 53668))) - (k * 12211)) let s1__ = (if s1_ < 0 then s1_ + 2147483563 else s1_) let k_ = intDiv(s2, 52774) let s2_ = ((40692 * (s2 - (k_ * 52774))) - (k_ * 3791)) let s2__ = (if s2_ < 0 then s2_ + 2147483399 else s2_) let z = (s1__ - s2__) if z < 1 then lazy of () => LzCons(z + 2147483562, rands(s1__, s2__)) else lazy of () => LzCons(z, rands(s1__, s2__)) fun randomInts(s1, s2) = if (1 <= s1) and (s1 <= 2147483562) and (1 <= s2) and (s2 <= 2147483398) then rands(s1, s2) fun findKQ(n) = fun f(k, q) = if divMod(q, 2) is [d, r] and r == 0 then f(k + 1, d) else [k, q] f(0, n - 1) fun uniform(nns, rrs) = if nns is n :: Nil and rrs is r :: rs then intMod(r, n) :: Nil nns is n :: ns and rrs is r :: rs and let t = intMod(r, n + 1) t == n then t :: uniform(ns, rs) else t :: map(x => intMod(x, 65536), rs) fun random(n, rs) = let ns = chop(65536, n) if splitAt_lz(listLen(ns), rs) is [rs1, rs2] then [makeNumber(65536, uniform(ns, rs1)), rs2] fun singleTestX(n, kq, x) = fun square(x) = intMod(x * x, n) fun witness(ls) = if ls is Nil then false t :: ts and t == (n - 1) then true t == 1 then false else witness(ts) if kq is [k, q] and take_lz(k, iterate(square, powerMod(x, q, n))) is t :: ts then (t == 1) || (t == (n - 1)) || witness(ts) fun singleTest(n, kq, rs) = if random(n - 2, rs) is [x, rs_] then [singleTestX(n, kq, 2 + x), rs_] fun multiTest(k, rs, n) = fun mTest(k, rs) = if k == 0 then [true, rs] singleTest(n, findKQ(n), rs) is [t, rs_] and t then mTest(k - 1, rs_) else [false, rs_] if ((n <= 1) || even(n)) then [n==2, rs] else mTest(k, rs) fun doLine(cs, cont, rs) = let n = int_val_of_string(cs) if multiTest(100, rs, n) is [t, rs_] and t then "Probably prime" :: (cont(rs_)) else "Composite" :: (cont(rs_)) fun doInput(state, lls) = if lls is Nil then Nil l :: ls then doLine(l, state => doInput(state, ls), state) fun process(process_arg1) = doInput(randomInts(111, 47), process_arg1) fun testPrimetest_nofib(d) = let cts = nofibStringToList("24|48|47|1317|8901") process(lines(cts)) fun main() = testPrimetest_nofib(0).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/puzzle.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module puzzle with ... abstract class ItemType: Bono | Edge | Larry | Adam object Bono extends ItemType Edge extends ItemType Larry extends ItemType Adam extends ItemType fun itemEq(a, b) = if a is Bono and b is Bono then true Edge and b is Edge then true Larry and b is Larry then true Adam and b is Adam then true else false fun succItem(i) = if i is Bono then Edge Edge then Larry Larry then Adam fun isEnd(i) = if i is Bono then false Edge then false Larry then false Adam then true fun itemFromTo(a, b) = if itemEq(a, b) then a :: Nil else a :: itemFromTo(succItem(a), b) abstract class BankType: LeftBank | RightBank object LeftBank extends BankType RightBank extends BankType fun bankEq(a, b) = if a is LeftBank and b is LeftBank then true RightBank and b is RightBank then true else false data class State(b: BankType, e: BankType, l: BankType, a: BankType) fun stateEq(s1, s2) = if s1 is State(a, b, c, d) and s2 is State(e, f, g, h) then (bankEq(a, e) && bankEq(b, f) && bankEq(c, g) && bankEq(d, h)) fun bonoPos(s) = if s is State(a, b, c, d) then a fun edgePos(s) = if s is State(a, b, c, d) then b fun larryPos(s) = if s is State(a, b, c, d) then c fun adamPos(s) = if s is State(a, b, c, d) then d val initialState = State(LeftBank, LeftBank, LeftBank, LeftBank) //│ initialState = State(LeftBank, LeftBank, LeftBank, LeftBank) val finalState = State(RightBank, RightBank, RightBank, RightBank) //│ finalState = State(RightBank, RightBank, RightBank, RightBank) fun position(i, s) = if i is Bono then bonoPos(s) Edge then edgePos(s) Larry then larryPos(s) Adam then adamPos(s) fun updateState(s, i, pos) = if s is State(a, b, c, d) and i is Bono then State(pos, b, c, d) Edge then State(a, pos, c, d) Larry then State(a, b, pos, d) Adam then State(a, b, c, pos) fun opposite(b) = if b is LeftBank then RightBank RightBank then LeftBank fun notSeen(state, states) = all(case { [_, s] then not(stateEq(state, s)) }, states) fun writeItem(i, b, rest) = if i is Bono and b is LeftBank then nofibStringToList(" Bono | |\n") +: rest Edge and b is LeftBank then nofibStringToList("The Edge | |\n") +: rest Larry and b is LeftBank then nofibStringToList(" Larry | |\n") +: rest Adam and b is LeftBank then nofibStringToList(" Adam | |\n") +: rest Bono and b is RightBank then nofibStringToList(" | | Bono\n") +: rest Edge and b is RightBank then nofibStringToList(" | | The Edge\n") +: rest Larry and b is RightBank then nofibStringToList(" | | Larry\n") +: rest Adam and b is RightBank then nofibStringToList(" | | Adam\n") +: rest fun writeState(state, s) = nofibStringToList("----------------------------------------\n") +: writeItem of Bono bonoPos(state) writeItem of Edge edgePos(state) writeItem of Larry larryPos(state) writeItem of Adam adamPos(state) nofibStringToList("----------------------------------------\n") +: s fun totalTime(history) = if history is [time, _] :: _ then time fun writeHistory(history, x) = if history is Nil then x else foldr( (timestate, acc) => (s) => if timestate is [time, state] then nofibStringToList("Time: ") +: nofibStringToList(stringOfInt(totalTime(history) - time)) +: ("\n" :: writeState(state, acc(s))), x => x, history )(x) fun writeSolutions(solutions, count, s) = if solutions is Nil then s item :: next then nofibStringToList("Solution ") +: nofibStringToList(stringOfInt(count)) +: "\n" :: writeHistory(item, writeSolutions(next, count + 1, s)) fun minSolutions(history) = if history is Nil then Nil history :: next then // @tailrec // TODO fun minAcc(minSoFar, mins, ls) = if ls is Nil then mins history :: next then let total = totalTime(history) if minSoFar < total then minAcc(minSoFar, mins, next) minSoFar === total then minAcc(minSoFar, history :: mins, next) else minAcc(total, history :: Nil, next) reverse(minAcc(totalTime(history), history :: Nil, next)) fun u2times(i) = if i is Bono then 10 Edge then 5 Larry then 2 Adam then 1 fun transfer(source, dest, location, countdown, history) = if stateEq(source, dest) then ([countdown, dest] :: history) :: Nil else let newHistory = [countdown, dest] :: history let newLocation = opposite(location) fun lscomp1(ls) = if ls is Nil then Nil item :: xs then if bankEq(position(item, dest), location) then let newDest = updateState(dest, item, newLocation) if notSeen(newDest, history) then let newTime = countdown + u2times(item) transfer(source, newDest, newLocation, newTime, newHistory) :: lscomp1(xs) else lscomp1(xs) else lscomp1(xs) let moveOne = concat(lscomp1(itemFromTo(Bono, Adam))) fun lscomp2(ls) = if ls is Nil then Nil i :: xs then fun lscomp3(ls) = if ls is Nil then lscomp2(xs) j :: ys then if bankEq(position(i, dest), location) and bankEq(position(j, dest), location) then let newDest = updateState(updateState(dest, i, newLocation), j, newLocation) if notSeen(newDest, history) then let newTime = countdown + u2times(i) transfer(source, newDest, newLocation, newTime, newHistory) :: lscomp3(ys) else lscomp3(ys) else lscomp3(ys) lscomp3(itemFromTo(succItem(i), Adam)) let moveTwo = concat(lscomp2(itemFromTo(Bono, Larry))) moveOne +: moveTwo fun testPuzzle_nofib(x) = let time = if listLen(x) === 1 then 0 else throw Error("puzzle expects exactly one argument") let solutions = transfer(initialState, finalState, RightBank, time, Nil) let mins = minSolutions(solutions) writeSolutions(mins, 1, Nil) fun main() = nofibListToString(testPuzzle_nofib(2 :: Nil)) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/rsa.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module rsa with ... fun z_of_int(x) = globalThis.BigInt(x) fun string_of_z(x) = nofibStringToList(x + "") fun z_add(x, y) = x + y fun z_mul(x, y) = x * y fun z_sub(x, y) = x - y fun z_div(x, y) = x / y fun z_mod(x, y) = x % y fun z_equal(x, y) = x === y fun z_sqr(x) = x * x fun int_if_char(c) = c.codePointAt(0) val const0 = z_of_int(0) //│ const0 = 0 val const31 = z_of_int(31) //│ const31 = 31 val const1 = z_of_int(1) //│ const1 = 1 val const2 = z_of_int(2) //│ const2 = 2 val const128 = z_of_int(128) //│ const128 = 128 fun hash(str) = foldl of (acc, c) => z_add(z_of_int(int_if_char(c)), z_mul(acc, const31)) const0 str fun and_(ls) = if ls is Nil then true h :: t and h then and_(t) else false fun unlines(ls) = concat of map(l => l +: ("\n" :: Nil), ls) fun even(a) = z_mod(a, const2) === const0 fun code(ls) = foldl of (x, y) => z_add(z_mul(const128, x), z_of_int(int_if_char(y))) const0 ls fun collect(n, xs) = if n === 0 then Nil xs is Nil then Nil else take(n, xs) :: collect(n, leave(n, xs)) fun size(n) = intDiv(listLen(string_of_z(n)) * 47, 100) fun encrypt(n, e, s) = unlines of map of c => string_of_z(power(e, n, code(c))) collect(size(n), s) fun power(n, m, x) = if z_equal(n, const0) then const1 even(n) then z_mod(z_sqr(power(z_div(n, const2), m, x)), m) else z_mod(z_mul(x, power(z_sub(n, const1), m, x)), m) val intput = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/rsa.faststdin").toString() //│ intput = ["m","o","d","u","l","e"," ","R","s","a"," ","(","e","n","c","r","y","p","t",","," ","d","e","c","r","y","p","t",","," ","m","a","k","e","K","e","y","s",")","\n","w","h","e","r","e","\n","\n","\n","e","n","c","r","y","p","t",","," ","d","e","c","r","y","p","t"," ",":",":"," ","I","n","t","e","g","e","r"," ","-",">"," ","I","n","t","e","g","e","r"," ","-",">"," ","S","t","r","i","n","g"," ","-",">"," ","S","t","r","i","n","g","\n","e","n","c","r","y","p","t"," ","n"," ","e"," ","="," ","u","n","l","i","n","e","s"," ","."," ","m","a","p"," ","(","s","h","o","w"," ","."," ","p","o","w","e","r"," ","e"," ","n"," ","."," ","c","o","d","e",")"," ","."," ","c","o","l","l","e","c","t"," ","(","s","i","z","e"," ","n",")","\n","d","e","c","r","y","p","t"," ","n"," ","d"," ","="," ","c","o","n","c","a","t"," ","."," ","m","a","p"," ","(","d","e","c","o","d","e"," ","."," ","p","o","w","e","r"," ","d"," ","n"," ","."," ","r","e","a","d",")"," ","."," ","l","i","n","e","s","\n","\n","-","-","-","-","-","-","-","-"," ","C","o","n","v","e","r","t","i","n","g"," ","b","e","t","w","e","e","n"," ","S","t","r","i","n","g","s"," ","a","n","d"," ","I","n","t","e","g","e","r","s"," ","-","-","-","-","-","-","-","-","-","-","-","\n","\n","c","o","d","e"," ",":",":"," ","S","t","r","i","n","g"," ","-",">"," ","I","n","t","e","g","e","r","\n","c","o","d","e"," ","="," ","f","o","l","d","l"," ","a","c","c","u","m"," ","0","\n"," "," ","w","h","e","r","e"," ","a","c","c","u","m"," ","x"," ","y"," ","="," ","(","1","2","8"," ","*"," ","x",")"," ","+"," ","f","r","o","m","I","n","t","e","g","r","a","l"," ","(","f","r","o","m","E","n","u","m"," ","y",")","\n"] fun testRsa_nofib(_) = hash(encrypt( z_of_int("2036450659413645137870851576872812267542175329986469156678671505255564383842535488743101632280716717779536712424613501441720195827856504007305662157107"), z_of_int("387784473137902876992546516170169092918207676456888779623592396031349415024943784869634893342729620092877891356118467738167515879252473323905128540213"), intput )) fun main() = testRsa_nofib(0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/scc.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module scc with ... fun dfs(r, vsns, xs) = if vsns is [vs, ns] and xs is Nil then [vs, ns] x :: xs and inList(x, vs) then dfs(r, [vs, ns], xs) dfs(r, [x :: vs, Nil], r(x)) is [vs', ns'] then dfs(r, [vs', (x :: ns') +: ns], xs) fun stronglyConnComp(es, vs) = fun swap(a) = if a is [f, s] then [s, f] fun new_range(xys, w) = if xys is Nil then Nil [x, y] :: xys and x == w then y :: new_range(xys, w) else new_range(xys, w) fun span_tree(r, vsns, xs) = if vsns is [vs, ns] and xs is Nil then [vs, ns] x :: xs and inList(x, vs) then span_tree(r, [vs, ns], xs) dfs(r, [x :: vs, Nil], r(x)) is [vs', ns'] then span_tree(r, [vs', (x :: ns') :: ns], xs) snd of span_tree of x => new_range(map(swap, es), x) [Nil, Nil] snd of dfs of x => new_range(es, x) [Nil, Nil] vs fun testScc_nofib(d) = let a = 1 let b = 2 let c = 3 let d = 4 let f = 5 let g = 6 let h = 7 let vertices = a :: b :: c :: d :: f :: g :: h :: Nil let edges = [b, a] :: [c, b] :: [c, d] :: [c, h] :: [d, c] :: [f, a] :: [f, g] :: [f, h] :: [g, f] :: [h, g] :: Nil stronglyConnComp(edges, vertices) fun main() = testScc_nofib(0).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/secretary.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module secretary with ... fun infRand(m, s) = fun f(x) = lazy(() => LzCons((intMod(x, m) + 1), f(intMod((97 * x + 11), power(2, 7))))) f(s) fun simulate(n, m, proc) = fun lscomp(ls) = if ls is Nil then Nil seed :: t then proc(infRand(m, seed)) :: lscomp(t) listLen(filter(x => x, lscomp(enumFromTo(1, n)))) / n fun sim(n, k) = fun proc(rs) = let xs = take_lz(100, nub_lz(rs)) let best = 100 let bestk = maximum(take(k, xs)) let afterk = leaveWhile(x => x < bestk, leave(k, xs)) listEq(best :: Nil, take(1, afterk)) simulate(n, 100, proc) fun testSecretary_nofib(n) = fun listcomp(ls) = if ls is Nil then Nil h :: t then sim(n, h) :: listcomp(t) listcomp(enumFromTo(35, 39)) // NOTE: original input 5000 fun main() = testSecretary_nofib(50).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/sorting.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module sorting with ... fun int_of_char(c) = c.codePointAt(0) object EQ GT LT fun compareList(xs, ys) = if xs is Nil and ys is Nil then EQ _ :: _ then LT _ :: _ and ys is Nil then GT x :: xs_ and ys is y :: ys_ and int_of_char(x) === int_of_char(y) then compareList(xs_, ys_) int_of_char(x) < int_of_char(y) then LT else GT fun gtList(a, b) = compareList(a, b) is GT fun leList(a, b) = not(gtList(a, b)) fun ltList(a, b) = compareList(a, b) is LT fun geList(a, b) = not(ltList(a, b)) fun eqList(a, b) = compareList(a, b) is EQ fun prependToAll(sep, xs) = if xs is Nil then Nil x :: xs_ then sep :: x :: prependToAll(sep, xs_) fun intersperse(sep, xs) = if xs is Nil then Nil x :: xs_ then x :: prependToAll(sep, xs_) fun lines(s) = if s is Nil then Nil break_(x => x === "\n", s) is [l, s_] then let tt = if s_ is Nil then Nil _ :: s__ then lines(s__) l :: tt fun unlines(ls) = concat(map(l => l +: ("\n" :: Nil), ls)) fun odd(x) = intMod(x, 2) === 0 fun z_of_int(x) = globalThis.BigInt(x) fun hash(str) = foldl( (acc, c) => z_of_int(int_of_char(c)) + (acc * z_of_int(31)), z_of_int(0), str ) fun quickSort(xs) = if xs is Nil then Nil x :: xs_ then fun lscomp1(ls) = if ls is Nil then Nil h :: t and leList(h, x) then h :: lscomp1(t) else lscomp1(t) fun lscomp2(ls) = if ls is Nil then Nil h :: t and gtList(h, x) then h :: lscomp2(t) else lscomp2(t) quickSort(lscomp1(xs_)) +: (x :: quickSort(lscomp2(xs_))) fun select(p, x, ts_fs) = if ts_fs is [ts, fs] and p(x) then [x :: ts, fs] else [ts, x :: fs] fun partition(p, xs) = foldr((x, y) => select(p, x, y), [Nil, Nil], xs) fun quickSort2(xs) = if xs is Nil then Nil x :: xs_ then if partition(y => geList(x, y), xs_) is [lo, hi] then quickSort2(lo) +: (x :: quickSort2(hi)) fun quickerSort(xss) = if xss is Nil then Nil x :: Nil then x :: Nil x :: xs then fun split(x, lo, hi, ys) = if ys is Nil then quickerSort(lo) +: (x :: quickerSort(hi)) y :: ys_ and leList(y, x) then split(x, y :: lo, hi, ys_) else split(x, lo, y :: hi, ys_) split(x, Nil, Nil, xs) fun insertSort(xss) = if xss is Nil then Nil x :: xs then fun trins(rev, xs, ys) = if xs is Nil and ys is y :: ys_ then trins(Nil, reverse(rev) +: (y :: Nil), ys_) xs and ys is Nil then reverse(rev) +: xs x :: xs_ and ys is y :: ys_ and ltList(x, y) then trins(x :: rev, xs_, y :: ys_) else trins(Nil, reverse(rev) +: (y :: x :: xs_), ys_) trins(Nil, x :: Nil, xs) abstract class Tree[T]: Tip | Branch[T] object Tip extends Tree[Nothing] data class Branch[T](a: T, l: Tree[T], r: Tree[T]) extends Tree[T] fun treeSort(param) = fun mkTree(innerparam) = fun to_tree(x, t) = if t is Tip then Branch(x, Tip, Tip) Branch(y, l, r) and leList(x, y) then Branch(y, to_tree(x, l), r) else Branch(y, l, to_tree(x, r)) foldr(to_tree, Tip, innerparam) fun readTree(t) = if t is Tip then Nil Branch(x, l, r) then readTree(l) +: (x :: readTree(r)) readTree(mkTree(param)) abstract class Tree2[T]: Tip2 | Twig2[T] | Branch2[T] object Tip2 extends Tree2[Nothing] data class Twig2[T](a: T) extends Tree2[T] Branch2[T](a: T, l: Tree2[T], r: Tree2[T]) extends Tree2[T] fun treeSort2(param) = fun mkTree(innerparam) = fun to_tree(x, t) = if t is Tip2 then Twig2(x) Twig2(y) and leList(x, y) then Branch2(y, Twig2(x), Tip2) else Branch2(y, Tip2, Twig2(x)) Branch2(y, l, r) and leList(x, y) then Branch2(y, to_tree(x, l), r) else Branch2(y, l, to_tree(x, r)) foldr(to_tree, Tip2, innerparam) fun readTree(t) = if t is Tip2 then Nil Twig2(x) then x :: Nil Branch2(x, l, r) then readTree(l) +: (x :: readTree(r)) readTree(mkTree(param)) fun heapSort(xs) = fun heap(k, xs) = if xs is Nil then Tip x :: xs_ then to_heap(k, x, heap(k + 1, xs_)) fun to_heap(k, x, t) = if t is Tip then Branch(x, Tip, Tip) Branch(y, l, r) and leList(x, y) and odd(k) then Branch(x, to_heap(intDiv(k, 2), y, l), r) leList(x, y) then Branch(x, l, to_heap(intDiv(k, 2), y, r)) odd(k) then Branch(y, to_heap(intDiv(k, 2), x, l), r) else Branch(y, l, to_heap(intDiv(k, 2), x, r)) fun clear(t) = if t is Tip then Nil Branch(x, l, r) then x :: clear(mix(l, r)) fun mix(l, r) = if l is Tip then r r is Tip then l l is Branch(x, l1, r1) and r is Branch(y, l2, r2) and leList(x, y) then Branch(x, mix(l1, r1), Branch(y, l2, r2)) else Branch(y, Branch(x, l1, r1), mix(l2, r2)) clear(heap(0, xs)) fun mergeSort(param) = fun runsplit(run, xs) = if run is Nil and xs is Nil then Nil xs is Nil then run :: Nil run is Nil and xs is x :: xs_ then runsplit(x :: Nil, xs_) run is r :: rs and xs is x :: xs_ and rs is Nil and gtList(x, r) then runsplit(r :: x :: Nil, xs_) leList(x, r) then runsplit(x :: r :: rs, xs_) else (r :: rs) :: runsplit(x :: Nil, xs_) rs and leList(x, r) then runsplit(x :: r :: rs, xs_) else (r :: rs) :: runsplit(x :: Nil, xs_) fun merge_lists(xs) = if xs is Nil then Nil x :: xs_ then merge(x, merge_lists(xs_)) fun merge(xs, ys) = if xs is Nil then ys ys is Nil then xs xs is x :: xs_ and ys is y :: ys_ and eqList(x, y) then x :: y :: merge(xs_, ys_) ltList(x, y) then x :: merge(xs_, y :: ys_) else y :: merge(x :: xs_, ys_) merge_lists(runsplit(Nil, param)) fun mangle(inpt) = fun sort(param) = foldr( (f, g) => x => f(g(x)), x => x, intersperse(reverse, (heapSort :: insertSort :: mergeSort :: quickSort :: quickSort2 :: quickerSort :: treeSort :: treeSort2 :: Nil)), )(param) unlines(sort(lines(inpt))) fun testSorting_nofib(d) = let f = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/Main.hs").toString() hash(mangle(f)) fun main() = testSorting_nofib(0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/sphere.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module sphere with ... val pi = globalThis.Math.PI val epsilon = 0.000001 val infinity = 100000000.0 //│ epsilon = 0.000001 //│ infinity = 100000000 //│ pi = 3.141592653589793 fun vecadd(a1, a2) = if a1 is [x1, y1, z1] and a2 is [x2, y2, z2] then [x1 + x2, y1 + y2, z1 + z2] fun vecsub(a1, a2) = if a1 is [x1, y1, z1] and a2 is [x2, y2, z2] then [x1 - x2, y1 - y2, z1 - z2] fun vecmult(a1, a2) = if a1 is [x1, y1, z1] and a2 is [x2, y2, z2] then [x1 * x2, y1 * y2, z1 * z2] fun vecsum(param) = foldr(vecadd, [0.0, 0.0, 0.0], param) fun vecnorm(xyz) = if xyz is [x, y, z] then let len = sqrt(x * x + y * y + z * z) [[x / len , y / len, z / len], len] fun vecscale(xyz, a) = if xyz is [x, y, z] then [a * x, a * y, a * z] fun vecdot(x1, x2) = if x1 is [x1, y1, z1] and x2 is [x2, y2, z2] then x1 * x2 + y1 * y2 + z1 * z2 fun veccross(x1, x2) = if x1 is [x1, y1, z1] and x2 is [x2, y2, z2] then [(y1 * z2) - (y2 * z1), (z1 * x2) - (z2 * x1), (x1 * y2) - (x2 * y1)] fun is_zerovector(x) = if x is [x, y, z] then (x < epsilon) && (y < epsilon) && (z < epsilon) abstract class Light: Directional | Point data class Directional(x: [Num, Num, Num], y: [Num, Num, Num]) extends Light Point(x: [Num, Num, Num], y: [Num, Num, Num]) extends Light fun lightpos(p) = if p is Point(pos, col) then pos fun lightdir(d) = if d is Directional(dir, col) then fst(vecnorm(dir)) fun lightcolour(x) = if x is Directional(dir, col) then col Point(pos, col) then col abstract class Surfspec: Ambient | Diffuse | Specular | Specpow | Reflect | Transmit | Refract | Body data class Ambient(v: [Num, Num, Num]) extends Surfspec Diffuse(v: [Num, Num, Num]) extends Surfspec Specular(v: [Num, Num, Num]) extends Surfspec Specpow(v: Num) extends Surfspec Reflect(v: Num) extends Surfspec Transmit(v: Num) extends Surfspec Refract(v: Num) extends Surfspec Body(v: [Num, Num, Num]) extends Surfspec fun ambientsurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Ambient(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), ([0.0, 0.0, 0.0] :: Nil))) fun diffusesurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Diffuse(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), ([0.0, 0.0, 0.0] :: Nil))) fun specularsurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Specular(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), ([0.0, 0.0, 0.0] :: Nil))) fun specpowsurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Specpow(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), (8.0 :: Nil))) fun reflectsurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Reflect(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), (0.0 :: Nil))) fun transmitsurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Transmit(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), (0.0 :: Nil))) fun refractsurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Refract(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), (1.0 :: Nil))) fun bodysurf(ss) = fun lscomp(ls) = if ls is Nil then Nil x :: t and x is Body(s) then s :: lscomp(t) else lscomp(t) head(append(lscomp(ss), ([1.0, 1.0, 1.0] :: Nil))) data class Sphere(pos: [Num, Num, Num], radius: Num, surface: List[Surfspec]) fun spheresurf(s) = if s is Sphere(pos, rad, surf) then surf val lookat = [0.0, 0.0, 0.0] val vup = [0.0, 0.0, 1.0] val fov = 45.0 val s2 = Ambient([0.035, 0.0325, 0.025]) :: Diffuse([0.5, 0.45, 0.35]) :: Specular([0.8, 0.8, 0.8]) :: Specpow(3.0) :: Reflect(0.5) :: Nil val testspheres = Sphere([0.0, 0.0, 0.0], 0.5, s2) :: Sphere([0.272166, 0.272166, 0.544331], 0.166667, s2) :: Sphere([0.643951, 0.172546, 0.0], 0.166667, s2) :: Sphere([0.172546, 0.643951, 0.0], 0.166667, s2) :: Sphere([-0.371785, 0.0996195, 0.544331], 0.166667, s2) :: Sphere([-0.471405, 0.471405, 0.0], 0.166667, s2) :: Sphere([-0.643951, -0.172546, 0.0], 0.166667, s2) :: Sphere([0.0996195, -0.371785, 0.544331], 0.166667, s2) :: Sphere([-0.172546, -0.643951, 0.0], 0.166667, s2) :: Sphere([0.471405, -0.471405, 0.0], 0.166667, s2) :: Nil val testlights = Point([4.0, 3.0, 2.0], [0.288675, 0.288675, 0.288675]) :: Point([1.0, -4.0, 4.0], [0.288675, 0.288675, 0.288675]) :: Point([-3.0, 1.0, 5.0], [0.288675, 0.288675, 0.288675]) :: Nil val lookfrom = [2.1, 1.3, 1.7] val background = [0.078, 0.361, 0.753] //│ background = [0.078, 0.361, 0.753] //│ fov = 45 //│ lookat = [0, 0, 0] //│ lookfrom = [2.1, 1.3, 1.7] //│ s2 = [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)] //│ testlights = [Point([4, 3, 2], [0.288675, 0.288675, 0.288675]),Point([1, -4, 4], [0.288675, 0.288675, 0.288675]),Point([-3, 1, 5], [0.288675, 0.288675, 0.288675])] //│ testspheres = [Sphere([0, 0, 0], 0.5, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([0.272166, 0.272166, 0.544331], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([0.643951, 0.172546, 0], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([0.172546, 0.643951, 0], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([-0.371785, 0.0996195, 0.544331], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([-0.471405, 0.471405, 0], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([-0.643951, -0.172546, 0], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([0.0996195, -0.371785, 0.544331], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([-0.172546, -0.643951, 0], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)]),Sphere([0.471405, -0.471405, 0], 0.166667, [Ambient([0.035, 0.0325, 0.025]),Diffuse([0.5, 0.45, 0.35]),Specular([0.8, 0.8, 0.8]),Specpow(3),Reflect(0.5)])] //│ vup = [0, 0, 1] fun spherenormal(pos, sp) = if sp is Sphere(spos, rad, _) then vecscale(vecsub(pos, spos), 1 / rad) fun dtor(x) = x * pi / 180.0 fun camparams(lookfrom, lookat, vup, fov, winsize) = let initfirstray = vecsub(lookat, lookfrom) if vecnorm(initfirstray) is [lookdir, dist] and vecnorm(veccross(lookdir, vup)) is [scrni, _] and vecnorm(veccross(scrni, lookdir)) is [scrnj, _] then let xfov = fov let yfov = fov let xwinsize = winsize let ywinsize = winsize let magx = 2.0 * dist * tan(dtor(xfov/2)) / xwinsize let magy = 2.0 * dist * tan(dtor(yfov/2)) / ywinsize let scrnx = vecscale(scrni, magx) let scrny = vecscale(scrnj, magy) let firstray = vecsub(initfirstray, vecadd(vecscale(scrnx, (0.5 * xwinsize)), vecscale(scrny, (0.5 * ywinsize)))) [firstray, scrnx, scrny] fun sphereintersect(pos, dir, sp) = if sp is Sphere(spos, rad, _) then let m = vecsub(pos, spos) let bm = vecdot(m, dir) let m2 = vecdot(m, m) let disc = (bm * bm) - m2 + (rad * rad) let slo = -bm - sqrt(disc) let shi = -bm + sqrt(disc) if disc < 0.0 then [false, 0.0] slo < 0.0 and shi < 0.0 then [false, 0.0] else [true, shi] else [true, slo] fun trace(spheres, pos, dir) = fun f(d1s1, d2s2) = if d1s1 is [d1, s1] and d2s2 is [d2, s2_] and d1 < d2 then [d1, s1] else [d2, s2_] fun sphmap(xss) = if xss is Nil then Nil x :: xs and sphereintersect(pos, dir, x) is [is_hit, where_hit] and is_hit then [where_hit, x] :: sphmap(xs) else sphmap(xs) let dists = sphmap(spheres) if (null_(dists)) then [false, infinity, head(spheres)] else if foldr(f, head(dists), tail(dists)) is [mindist, sp] then [true, mindist, sp] fun refractray(newindex, olddir, innorm) = let dotp = -vecdot(olddir, innorm) let matchIdent_17 = if dotp < 0.0 then [vecscale(innorm, -1.0), -dotp, 1.0 / newindex] else [innorm, dotp, newindex] if matchIdent_17 is [norm, k, nr] then let disc = 1.0 - nr * nr * (1.0 - (k * k)) let t = (nr * k) - sqrt(disc) if disc < 0.0 then [true, [0.0, 0.0, 0.0]] else ([false, vecadd(vecscale(norm, t), vecscale(olddir, nr))]) fun lightdirection(l, pt) = if l is Directional(dir, col) then [fst(vecnorm(dir)), infinity] Point(pos, col) then vecnorm(vecsub(pos, pt)) fun shadowed(pos, dir, lcolour) = if trace(testspheres, vecadd(pos, vecscale(dir, epsilon)), dir) is [is_hit, dist, sp] then if not(is_hit) then [false, lcolour] else [true, lcolour] fun lightray(l, pos, norm, refl, surf) = if lightdirection(l, pos) is [ldir, dist] then let cosangle = vecdot(ldir, norm) if shadowed(pos, ldir, lightcolour(l)) is [is_inshadow, lcolour] then if is_inshadow then [0.0, 0.0, 0.0] let diff = diffusesurf(surf) let spow = specpowsurf(surf) cosangle <= 0.0 then let bodycol = bodysurf(surf) let cosalpha = -vecdot(refl, ldir) let diffcont = vecmult(vecscale(diff, -cosangle), lcolour) let speccont = if cosalpha <= 0.0 then [0.0, 0.0, 0.0] else (vecmult(vecscale(bodycol, power(cosalpha, spow)), lcolour)) vecadd(diffcont, speccont) let spec = specularsurf(surf) let cosalpha = vecdot(refl, ldir) let diffcont = vecmult(vecscale(diff, cosangle), lcolour) let speccont = if cosalpha < 0.0 then [0.0, 0.0, 0.0] else (vecmult(vecscale(spec, power(cosalpha, spow)), lcolour)) else vecadd(diffcont, speccont) //│ ———————————————————————————————————————————————————————————————————————————————— fun shade(lights, sp, lookpos, dir, dist, contrib) = let hitpos = vecadd(lookpos, vecscale(dir, dist)) let ambientlight = [1.0, 1.0, 1.0] let surf = spheresurf(sp) let amb = vecmult(ambientlight, ambientsurf(surf)) let norm = spherenormal(hitpos, sp) let refl = vecadd(dir, vecscale(norm, -2.0 * vecdot(dir, norm))) let diff = vecsum(map(l => (lightray(l, hitpos, norm, refl, surf)), lights)) let transmitted = transmitsurf(surf) let simple = vecadd(amb, diff) let trintensity = vecscale(bodysurf(surf), transmitted) let matchIdent_1 = if transmitted < epsilon then [false, simple] else transmitray(lights, simple, hitpos, dir, refractsurf(surf), trintensity, contrib, norm) if matchIdent_1 is [is_tir, trcol] then let reflsurf = vecscale(specularsurf(surf), reflectsurf(surf)) let reflectiv = if is_tir then vecadd(trintensity, reflsurf) else reflsurf let rcol = if is_zerovector(reflectiv) then trcol else (reflectray(hitpos, refl, lights, reflectiv, contrib, trcol)) rcol fun transmitray(lights, colour, pos, dir, index, intens, contrib, norm) = let newcontrib = vecmult(intens, contrib) if refractray(index, dir, norm) is [is_tir, newdir] then let nearpos = vecadd(pos, vecscale(newdir, epsilon)) if trace(testspheres, nearpos, newdir) is [is_hit, dist, sp] then let newcol = if is_hit then shade(lights, sp, nearpos, newdir, dist, newcontrib) else background if is_zerovector(newcontrib) then [false, colour] else ([false, vecadd(vecmult(newcol, intens), colour)]) fun reflectray(pos, newdir, lights, intens, contrib, colour) = let newcontrib = vecmult(intens, contrib) let nearpos = vecadd(pos, vecscale(newdir, epsilon)) if trace(testspheres, nearpos, newdir) is [is_hit, dist, sp] then let newcol = if is_hit then shade(lights, sp, nearpos, newdir, dist, newcontrib) else background if is_zerovector(newcontrib) then colour else (vecadd(colour, vecmult(newcol, intens))) //│ ———————————————————————————————————————————————————————————————————————————————— fun tracepixel(spheres, lights, x, y, firstray, scrnx, scrny) = let pos = lookfrom if vecnorm(vecadd(vecadd(firstray, vecscale(scrnx, x)), vecscale(scrny, y))) is [dir, tracepixel_Tup2_1] and trace(spheres, pos, dir) is [hit, dist, sp] and hit then shade(lights, sp, pos, dir, dist, [1.0, 1.0, 1.0]) else background fun z_of_int(x) = globalThis.BigInt(x) fun hash(param) = fun u8(x) = z_of_int(round(255 * x)) foldr(((rgb, acc) => if rgb is [r, g, b] then u8(r) + (u8(g) * z_of_int(7)) + (u8(b) * z_of_int(23)) + (acc * z_of_int(61))), z_of_int(0), param) fun ray(winsize) = let lights = testlights if camparams(lookfrom, lookat, vup, fov, winsize) is [firstray, scrnx, scrny] then fun f(i, j) = tracepixel(testspheres, lights, i, j, firstray, scrnx, scrny) fun lscomp1(ls1) = if ls1 is Nil then Nil i :: t1 then fun lscomp2(ls2) = if ls2 is Nil then lscomp1(t1) j :: t2 then [[i, j], f(i, j)] :: lscomp2(t2) lscomp2(enumFromTo(0, winsize - 1)) lscomp1(enumFromTo(0, winsize - 1)) fun run(winsize) = hash(map(snd, ray(winsize))) fun testSphere_nofib(n) = run(n) fun main() = testSphere_nofib(30) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/nofib/treejoin.mls ================================================ import "./NofibPrelude.mls" import "../../../../../../hkmc2/shared/src/test/mlscript-compile/Predef.mls" open NofibPrelude module treejoin with ... fun isSpace(c) = c === " " || c === "\n" fun isDigit(c) = let n = c.codePointAt(0) in n >= 48 && n <= 57 abstract class Tree[E]: Node | Leaf | Empty data class Node[E](k: Int, l: Tree[E], r: Tree[E]) extends Tree[E] Leaf[E](k: Int, e: E) extends Tree[E] object Empty extends Tree fun insertT(k, e, t) = if t is Node(k_, l, r) and k <= k_ then Node(k_, insertT(k, e, l), r) else Node(k_, l, insertT(k, e, r)) Leaf(k_, k__) then let l_ = Leaf(k, e) if k < k_ then Node(k, l_, Leaf(k_, k__)) > k_ then Node(k_, Leaf(k_, k__), l_) else throw Error("already exist") Empty then Leaf(k, e) fun lookupT(k, t) = if t is Node(k_, l, r) and k <= k_ then lookupT(k, l) else lookupT(k, r) Leaf(k_, e) then if k === k_ then Some(e) else None Empty then None fun readInt(s) = fun readInt_(n, cs) = if cs is c :: cs_ and isDigit(c) then readInt_(n * 10 + c.codePointAt(0) - 48, cs_) else let s_ = leaveWhile(isSpace, c :: cs) in [n, s_] else let s_ = leaveWhile(isSpace, cs) in [n, s_] readInt_(0, s) fun join(t1, t2, j) = if t1 is Empty then j t2 is Empty then j t1 is Leaf(k, [a, b, c]) and lookupT(c, t2) is None then j Some([d, e, f]) then insertT(c, [a, b, c, d, e], j) t1 is Node(k, l, r) then join(l, t2, join(r, t2, j)) fun readTree(fk, s, t) = if s is Nil then t readInt(s) is [f, s_] and readInt(s_) is [g, s__] and readInt(s__) is [h, s___] then let e = [f, g, h] let k = fk(e) readTree(fk, s___, insertT(k, e, t)) fun testTreejoin_nofib(n) = let c1 = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/1500.1").toString() let c2 = nofibStringToList of fs.readFileSync("./hkmc2/shared/src/test/mlscript/nofib/input/1500.2").toString() let a = readTree(case { [xx, _, _] then xx }, c1, Empty) let b = readTree(case { [xx, _, _] then xx }, c2, Empty) join(a, b, Empty) fun main() = testTreejoin_nofib(0).toString() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/CSPBar.mls ================================================ import "./../Example.mls" module CSPBar with ... fun res = Example.inc(0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/CSPBaz.mls ================================================ import "./CSPNest.mls" module CSPBaz with ... fun res = CSPNest.nest_f() ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/CSPFoo.mls ================================================ import "./../CSP.mls" module CSPFoo with ... fun res = (+)(CSP.test(), 1) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/CSPNest.mls ================================================ module CSPNest with ... fun nest_f() = 42 ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/Cubic.mls ================================================ module Cubic with ... fun res = (x_0) => (*)(x_0, (*)(x_0, (*)(x_0, 1))) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/Gib12.mls ================================================ module Gib12 with ... fun res = (x_0, y_0) => let x_1 x_1 = (+)(x_0, y_0) let x_2 x_2 = (+)(y_0, x_1) let x_3 x_3 = (+)(x_1, x_2) let x_4 x_4 = (+)(x_2, x_3) let x_5 x_5 = (+)(x_3, x_4) let x_6 x_6 = (+)(x_4, x_5) let x_7 x_7 = (+)(x_5, x_6) let x_8 x_8 = (+)(x_6, x_7) let x_9 x_9 = (+)(x_7, x_8) let x_10 x_10 = (+)(x_8, x_9) let x_11 x_11 = (+)(x_9, x_10) x_11 ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/Opened.mls ================================================ import "./../Predef.mls" module Opened with ... fun res = Predef.equals(Predef.id(1), 2) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/QuoteFoo.mls ================================================ module QuoteFoo with ... fun res = (+)(1, 1) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/QuoteInc.mls ================================================ module QuoteInc with ... fun res = (x_0) => (+)(x_0, 1) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/quotes/SafeDiv.mls ================================================ module SafeDiv with ... fun res = (x_0, y_0, d_0) => if let scrut_0 = (==)(y_0, 0) scrut_0 is true then d_0 else (/)(x_0, y_0) ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/ups/DnfCnf.mls ================================================ open annotations module DnfCnf with ... class Formula with constructor Var(val name: Str) Pred(val name: Str, val arg: Formula) And(val left: Formula, val right: Formula) Or(val left: Formula, val right: Formula) pattern Dnf(pattern P) = Or(Dnf(P), Dnf(P)) | Atoms(P) pattern Atoms(pattern P) = And(Atoms(P), Atoms(P)) | Atom(P) pattern Atom(pattern P) = Pred(_, P) | Var pattern Cnf(pattern P) = And(Cnf(P), Cnf(P)) pattern DnfOrCnf = Dnf(DnfOrCnf) | Cnf(DnfOrCnf) pattern DnfAndCnf = Dnf(DnfAndCnf) & Cnf(DnfAndCnf) fun isDnfOrCnf_naive(t) = t is DnfOrCnf fun isDnfOrCnf_optimized(t) = if t is (@compile DnfOrCnf) as s then s else () fun isDnfOrCnf_optimized_matchOnly(t) = t is @compile DnfOrCnf fun isDnfAndCnf_naive(t) = t is DnfAndCnf fun isDnfAndCnf_optimized(t) = if t is (@compile DnfAndCnf) as s then s else () fun isDnfAndCnf_optimized_matchOnly(t) = t is @compile DnfAndCnf ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/ups/EvaluationContext.mls ================================================ import "../Option.mls" open Option { Some, None } open annotations module EvaluationContext with ... data class Term with constructor Var(name: Str) App(lhs: Term, rhs: Term) Abs(lhs: Var, rhs: Term) // A cached set of free variables let _freeVars = None // Compute free variables. Retrieve the cache if it exists. fun freeVars = if _freeVars is Some(fv) then fv None then let fv = if this is Var(name) then new Set([name]) App(lhs, rhs) then lhs.freeVars.union(rhs.freeVars) Abs(name, body) then body.freeVars.difference(new Set([name])) set _freeVars = Some(fv) fv // Keep increasing the index until the based name with the index is not a free // variable in this term. fun freshName(baseName) = let freeVars = this.freeVars nameIndex = 1 if freeVars.has(baseName) is false then baseName else... while (baseName + "_" + nameIndex) is currentName and freeVars.has(currentName) then set nameIndex += 1 else return currentName fun freshVar(baseName) = Var of freshName(baseName) fun equals(that) = if this is Var(name) and that is Var(name') then name === name' App(lhs, rhs) and that is App(lhs', rhs') then lhs.equals(lhs') && rhs.equals(rhs') Abs(Var(name), body) and that is Abs(Var(name'), body') then name === name' && body.equals(body') else false // Pretty-print the term. fun show = if this is Var(name) then name App(Abs as lhs, rhs) then "(" + lhs.show + ") " + rhs.show App(lhs, App as rhs) then lhs.show + " (" + rhs.show + ")" App(lhs, rhs) then lhs.show + " " + rhs.show Abs(Var(name), body) then "λ" + name + ". " + body.show fun subst(term, name, target) = if term is Var(name') and name === name' then target else term Abs(Var(name'), body) and name === name' then term let target' = if target.freeVars.has(name') then subst(target, name', target.freshVar(name')) else target else Abs(Var(name'), subst(body, name, target')) App(lhs, rhs) then App(subst(lhs, name, target), subst(rhs, name, target)) pattern Value = Abs pattern Ctx(pattern Hole) = | Hole | App(Value, Ctx(Hole)) | App(Ctx(Hole), _) pattern Redex = App(Abs(Var(x), m), Abs as n) => subst(m, x, n) pattern Step = (Ctx(Redex) as res) => res fun isStep_naive(t) = t is Step fun isStep_optimized(t) = if t is (@compile Step) as s then s else () fun isStep_optimized_matchOnly(t) = t is @compile Step ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/ups/EvenOddTree.mls ================================================ open annotations module EvenOddTree with ... object A object B data class Node[T](left: Node[T], value: T, right: Node[T]) // The patterns generate trees that have odd or even numbers of node `A`. pattern OddTree = | A | Node(EvenTree, A, EvenTree) | Node(OddTree, A, OddTree) | Node(EvenTree, B, OddTree) | Node(OddTree, B, EvenTree) pattern EvenTree = | B | Node(EvenTree, A, OddTree) | Node(OddTree, A, EvenTree) | Node(EvenTree, B, EvenTree) | Node(OddTree, B, OddTree) fun isOddTree_naive(t) = t is OddTree fun isOddTree_optimized(t) = if t is (@compile OddTree) as s then s else () fun isOddTree_optimized_matchOnly(t) = t is @compile OddTree fun isEvenTree_naive(t) = t is EvenTree fun isEvenTree_optimized(t) = if t is (@compile EvenTree) as s then s else () fun isEvenTree_optimized_matchOnly(t) = t is @compile EvenTree ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/ups/README.md ================================================ This directory contains five pattern-compilation example programs, which define seven compiled entry patterns. The table below counts non-empty lines in the generated `.mjs` files. "Referenced pattern names" means direct pattern references in the source pattern body. | Pattern name | Referenced pattern names | Referenced pattern class SLOC | `_optimized` SLOC | `_optimized_matchOnly` SLOC | | --- | --- | ---: | ---: | ---: | | `Truthy` | `Truthy`, `Falsy` | 368 | 1173 | 710 | | `Falsy` | `Falsy`, `Truthy` | 368 | 1173 | 710 | | `OddTree` | `OddTree`, `EvenTree` | 858 | 3355 | 2006 | | `EvenTree` | `EvenTree`, `OddTree` | 858 | 3355 | 2006 | | `Step` | `Ctx`, `Redex` | 289 | 1266 | 516 | | `DnfOrCnf` | `DnfOrCnf`, `Dnf`, `Cnf` | 506 | 777 | 541 | | `DnfAndCnf` | `DnfAndCnf`, `Dnf`, `Cnf` | 506 | 326 | 222 | ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/ups/TruthyFalsy.mls ================================================ open annotations module TruthyFalsy with ... data class (&&) And(lhs: Bool, rhs: Bool) (||) Or(lhs: Bool, rhs: Bool) Not(arg: Bool) pattern Truthy = | true | And(Truthy, Truthy) | Or(Truthy, Truthy) | Or(Truthy, Falsy) | Or(Falsy, Truthy) | Not(Falsy) pattern Falsy = | false | And(Falsy, Falsy) | And(Falsy, Truthy) | And(Truthy, Falsy) | Or(Falsy, Falsy) | Not(Truthy) fun isTruthy_naive(t) = t is Truthy fun isTruthy_optimized(t) = if t is (@compile Truthy) as s then s else () fun isTruthy_optimized_matchOnly(t) = t is @compile Truthy fun isFalsy_naive(t) = t is Falsy fun isFalsy_optimized(t) = if t is (@compile Falsy) as s then s else () fun isFalsy_optimized_matchOnly(t) = t is @compile Falsy ================================================ FILE: hkmc2/shared/src/test/mlscript-compile/wasm/Wasm.mls ================================================ import "binaryen" import "../Predef.mls" import "../Runtime.mls" module Wasm with ... // WebAssembly utilities fun loadWasmBinary(modBuf, importObject): Promise = assert WebAssembly.validate of modBuf WebAssembly.instantiate of modBuf, importObject fun dumpWasmImpExp(wasmMod) = console.log of "Imports: %s", WebAssembly.Module.imports(wasmMod.module) console.log of "Exports: %s", WebAssembly.Module.exports(wasmMod.module) // Binaryen helpers fun binaryenFmtWat(wat, foldExprs): String = val mod = binaryen.parseText of wat mod.setFeatures(binaryen.Features.All) // Note: it seems `mod.validate()` sometimes returns `1` instead of `true`; so we don't use `assert` here Predef.js_assert of mod.validate() val fmtWat = if foldExprs then mod.emitText() else mod.emitStackIR() mod.dispose() fmtWat fun binaryenCompileToModule(wat, importObject): Promise = val mod = binaryen.parseText of wat mod.setFeatures(binaryen.Features.All) // Note: it seems `mod.validate()` sometimes returns `1` instead of `true`; so we don't use `assert` here Predef.js_assert of mod.validate() val modBuf = mod.emitBinary() mod.dispose() Promise.resolve(modBuf).then(modBuf => loadWasmBinary of modBuf, importObject ) fun unwrapWasmException(err, tag) = if ( err !== null && err !== undefined && typeof(err.is) === "function" && typeof(err.getArg) === "function" && err.is(tag) ) then err.getArg(tag, 0) else err fun binaryenRunFunc(wat, importObject, func): Promise = binaryenCompileToModule(wat, importObject) .then(wasmMod => let exnTag = wasmMod.instance.exports.mlx_exn Runtime.try_catch of () => func(wasmMod.instance.exports) err => throw unwrapWasmException(err, exnTag) ) fun binaryenPrintFuncRes(wat, importObject, func): Promise = binaryenRunFunc(wat, importObject, func) .then(result => Predef.print of Runtime.render of result ) ================================================ FILE: hkmc2AppsTests/src/test/scala/hkmc2/AppsCompileTestRunner.scala ================================================ package hkmc2 import mlscript.utils._, shorthands._ import io.PlatformPath.given class AppsCompileTestRunner extends CompileTestRunnerBase( compileDirs = TestFolders.appsCompileDirs(os.pwd), ): protected def cctx: CompilerCtx = AppsCompileTestRunner.cctx end AppsCompileTestRunner object AppsCompileTestRunner: given cctx: CompilerCtx = CompilerCtx.fresh(io.FileSystem.default) end AppsCompileTestRunner ================================================ FILE: hkmc2AppsTests/src/test/scala/hkmc2/AppsDiffTestRunner.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import mlscript.utils._ import os.Path import io.PlatformPath.given object AppsDiffTestState extends DiffTestRunner.State: override def testDir = TestFolders.appsDiffDir(workingDir) class AppsDiffTestRunner extends DiffTestRunnerBase(AppsDiffTestState) with ParallelTestExecution ================================================ FILE: hkmc2Benchmarks/src/test/bench/.gitignore ================================================ *.mjs ================================================ FILE: hkmc2Benchmarks/src/test/bench/FingerTreesAsStacks.mls ================================================ :js :silent import "../../../../hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls" import "../../../../hkmc2/shared/src/test/mlscript-compile/Stack.mls" import "./mlscript-compile/Benchmark.mls" open Benchmark :silent let suite = mkSuite() :silent let ftree = FingerTreeList.mk() let stack = Stack.Nil // Push an array of elements :silent suite.add("FingerTreeList.push100", () => let ft = FingerTreeList.mk() let i = 0 while i < 100 do set ft = FingerTreeList.cons(i, ft) set i += 1 ) :silent suite.add("FingerTreeList.push1000", () => let ft = FingerTreeList.mk() let i = 0 while (i < 1000) do set ft = FingerTreeList.cons(i, ft) set i = i + 1 ) :silent suite.add("FingerTreeList.push10000", () => let ft = FingerTreeList.mk() let i = 0 while (i < 10000) do set ft = FingerTreeList.cons(i, ft) set i = i + 1 ) :silent suite.add("FingerTreeList.push100000", () => let ft = FingerTreeList.mk() let i = 0 while (i < 100000) do set ft = FingerTreeList.cons(i, ft) set i = i + 1 set ftree = ft ) :silent suite.add("Stack.push100", () => let st = Stack.Nil let i = 0 while (i < 100) do set st = Stack.Cons(i, st) set i = i + 1 ) :silent suite.add("Stack.push1000", () => let st = Stack.Nil let i = 0 while (i < 1000) do set st = Stack.Cons(i, st) set i = i + 1 ) :silent suite.add("Stack.push10000", () => let st = Stack.Nil let i = 0 while (i < 10000) do set st = Stack.Cons(i, st) set i = i + 1 ) :silent suite.add("Stack.push100000", () => let st = Stack.Nil let i = 0 while (i < 100000) do set st = Stack.Cons(i, st) set i = i + 1 set stack = st ) :silent let ftElems = new Array(100000) let stElems = new Array(100000) // Pop elements :silent suite.add("FingerTreeList.pop100", () => let popft = ftree let i = 0 while (i < 100) do val v = FingerTreeList.popFront(popft) set ftElems.[i] = v.e1 set popft = FingerTreeList.popFront(popft).rest set i = i + 1 ) :silent suite.add("FingerTreeList.pop1000", () => let popft = ftree let i = 0 while (i < 1000) do val v = FingerTreeList.popFront(popft) set ftElems.[i] = v.e1 set popft = FingerTreeList.popFront(popft).rest set i = i + 1 ) :silent suite.add("FingerTreeList.pop10000", () => let popft = ftree let i = 0 while (i < 10000) do val v = FingerTreeList.popFront(popft) set ftElems.[i] = v.e1 set popft = FingerTreeList.popFront(popft).rest set i = i + 1 ) :silent suite.add("FingerTreeList.pop100000", () => let popft = ftree let i = 0 while (FingerTreeList.isEmpty(popft) is false) do val v = FingerTreeList.popFront(popft) set ftElems.[i] = v.e1 set popft = FingerTreeList.popFront(popft).rest set i = i + 1 ) :silent suite.add("Stack.pop100", () => let popStack = stack let i = 0 while (i < 100) do set stElems.[i] = popStack.head set popStack = popStack.tail set i = i + 1 ) :silent suite.add("Stack.pop1000", () => let popStack = stack let i = 0 while (i < 1000) do set stElems.[i] = popStack.head set popStack = popStack.tail set i = i + 1 ) :silent suite.add("Stack.pop10000", () => let popStack = stack let i = 0 while (i < 10000) do set stElems.[i] = popStack.head set popStack = popStack.tail set i = i + 1 ) :silent suite.add("Stack.pop100000", () => let popStack = stack let i = 0 while (popStack is Stack.Cons) do set stElems.[i] = popStack.head set popStack = popStack.tail set i = i + 1 ) :silent suite |> runSuite writeResults(suite, "FingerTreesAsStack.json") :expect true :silent let i = 0 let same = true while (i < 100000) do set same = same && (ftElems.[i] === stElems.[i]) && (ftElems.[i] === 100000 - i - 1) set i = i + 1 same ================================================ FILE: hkmc2Benchmarks/src/test/bench/LazySpreads.mls ================================================ :js import "../../../../hkmc2/shared/src/test/mlscript-compile/LazyArray.mls" import "../../../../hkmc2/shared/src/test/mlscript-compile/FingerTreeList.mls" import "../../../../hkmc2/shared/src/test/mlscript-compile/LazyFingerTree.mls" import "./mlscript-compile/Benchmark.mls" import "../../../../hkmc2/shared/src/test/mlscript-compile/Runtime.mls" open LazyArray open FingerTreeList open LazyFingerTree open Benchmark open Runtime :silent let suite = mkSuite() fun materialize(xs) = if (xs |> FingerTreeList.isFingerTree) then xs else xs.materialize() :sjs fun buildLazy(i) = if i > 0 then [i, ..buildLazy(i - 1), -i] else [] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let buildLazy; //│ buildLazy = function buildLazy(i) { //│ let scrut, tmp, tmp1, tmp2; //│ scrut = i > 0; //│ if (scrut === true) { //│ tmp = i - 1; //│ tmp1 = buildLazy(tmp); //│ tmp2 = - i; //│ return globalThis.Object.freeze(runtime.Tuple.lazyConcat(i, runtime.Tuple.split, tmp1, tmp2)) //│ } //│ return globalThis.Object.freeze([]); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun buildEager(i) = if i > 0 then [i, ...buildEager(i - 1), -i] else [] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let buildEager; //│ buildEager = function buildEager(i) { //│ let scrut, tmp, tmp1, tmp2; //│ scrut = i > 0; //│ if (scrut === true) { //│ tmp = i - 1; //│ tmp1 = buildEager(tmp); //│ tmp2 = - i; //│ return globalThis.Object.freeze([ i, ...tmp1, tmp2 ]) //│ } //│ return globalThis.Object.freeze([]); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun funnySum(xs) = if xs is [a, ..ls, b] then a + funnySum(ls) - b [] then 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let funnySum; //│ funnySum = function funnySum(xs) { //│ let ls, a, b, lastElement1$, middleElements, element0$, tmp, tmp1; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { //│ element0$ = runtime.Tuple.get(xs, 0); //│ middleElements = runtime.Tuple.slice(xs, 1, 1); //│ lastElement1$ = runtime.Tuple.get(xs, -1); //│ b = lastElement1$; //│ ls = middleElements; //│ a = element0$; //│ tmp = funnySum(ls); //│ tmp1 = a + tmp; //│ return tmp1 - b //│ } else if (runtime.Tuple.isArrayLike(xs) && xs.length === 0) { //│ return 0 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun funnySumEager(xs) = if xs is [a, ...ls, b] then a + funnySumEager(ls) - b [] then 0 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let funnySumEager; //│ funnySumEager = function funnySumEager(xs) { //│ let ls, a, b, lastElement1$, middleElements, element0$, tmp, tmp1; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { //│ element0$ = runtime.Tuple.get(xs, 0); //│ middleElements = runtime.Tuple.slice(xs, 1, 1); //│ lastElement1$ = runtime.Tuple.get(xs, -1); //│ b = lastElement1$; //│ ls = middleElements; //│ a = element0$; //│ tmp = funnySumEager(ls); //│ tmp1 = a + tmp; //│ return tmp1 - b //│ } else if (runtime.Tuple.isArrayLike(xs) && xs.length === 0) { //│ return 0 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :silent suite.add("BuildLazy3", () => buildLazy(3)) suite.add("BuildLazy33", () => buildLazy(33)) suite.add("BuildLazy333", () => buildLazy(333)) suite.add("BuildLazy3333", () => buildLazy(3333)) let res = buildLazy(1000) res |> funnySumEager //│ = 1001000 :silent suite.add("LazyBuiltFunnySumEager1000", () => let res = buildLazy(1000) res |> funnySumEager) :silent let res = buildLazy(1000) // saving from materialization probably balancing // slowdown from logn random access res |> funnySum //│ = 1001000 :silent suite.add("LazyBuiltFunnySum1000", () => let res = buildLazy(1000) res |> funnySum) suite.add("LazyBuiltFunnySum100", () => let res = buildLazy(100) res |> funnySum) suite.add("LazyBuiltFunnySum10", () => let res = buildLazy(10) res |> funnySum) //========================================================================= :silent suite.add("BuildEager3", () => buildEager(3)) suite.add("BuildEager33", () => buildEager(33)) suite.add("BuildEager333", () => buildEager(333)) suite.add("BuildEager3333", () => buildEager(3333)) let res = buildEager(1000) let res100 = buildEager(100) let res10 = buildEager(10)//+ runtime clock time (321984c): 128.79833 ms res |> funnySumEager //│ = 1001000 :silent suite.add("EagerBuiltFunnySumEager1000", () => res |> funnySumEager) suite.add("EagerBuiltFunnySumEager100", () => res100 |> funnySumEager) suite.add("EagerBuiltFunnySumEager10", () => res10 |> funnySumEager) res |> funnySum //│ = 1001000 :silent suite.add("EagerBuiltFunnySum1000", () => res |> funnySum) suite.add("EagerBuiltFunnySum100", () => res100 |> funnySum) suite.add("EagerBuiltFunnySum10", () => res10 |> funnySum) // ======================================================================== buildLazy(2).concat([11, 12, 13], LazyArray.mk(2, 4, 6, 8, 10)) //│ = [2, 1, -1, -2, 11, 12, 13, 2, 4, 6, 8, 10] :sjs fun map(f) = case [a, ..rest] then [f(a), ..map(f)(rest)] [] then [] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let map; //│ map = function map(f) { //│ let lambda18; //│ lambda18 = (undefined, function (caseScrut) { //│ let rest, a, middleElements, element0$, tmp3, tmp4, tmp5; //│ if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length >= 1) { //│ element0$ = runtime.Tuple.get(caseScrut, 0); //│ middleElements = runtime.Tuple.slice(caseScrut, 1, 0); //│ rest = middleElements; //│ a = element0$; //│ tmp3 = runtime.safeCall(f(a)); //│ tmp4 = map(f); //│ tmp5 = runtime.safeCall(tmp4(rest)); //│ return globalThis.Object.freeze(runtime.Tuple.lazyConcat(tmp3, runtime.Tuple.split, tmp5)) //│ } else if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length === 0) { //│ return globalThis.Object.freeze([]) //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ return lambda18 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :sjs fun mapEager(f) = case [a, ...rest] then [f(a), ...mapEager(f)(rest)] [] then [] //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let mapEager; //│ mapEager = function mapEager(f) { //│ let lambda18; //│ lambda18 = (undefined, function (caseScrut) { //│ let rest, a, middleElements, element0$, tmp3, tmp4, tmp5; //│ if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length >= 1) { //│ element0$ = runtime.Tuple.get(caseScrut, 0); //│ middleElements = runtime.Tuple.slice(caseScrut, 1, 0); //│ rest = middleElements; //│ a = element0$; //│ tmp3 = runtime.safeCall(f(a)); //│ tmp4 = mapEager(f); //│ tmp5 = runtime.safeCall(tmp4(rest)); //│ return globalThis.Object.freeze([ //│ tmp3, //│ ...tmp5 //│ ]) //│ } else if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length === 0) { //│ return globalThis.Object.freeze([]) //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); //│ return lambda18 //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— :silent let res = buildEager(1500) let res150 = buildEager(150) let res15 = buildEager(15) :silent let lm = map(x => x + 1)(res) :silent mapEager(x => x + 1)(res) :silent suite.add("EagerBuiltMapLazy1500", () => materialize(map(x => x + 1)(res))) suite.add("EagerBuiltMapLazy150", () => materialize(map(x => x + 1)(res150))) suite.add("EagerBuiltMapLazy15", () => materialize(map(x => x + 1)(res15))) :silent suite.add("EagerBuiltMapEager1500", () => mapEager(x => x + 1)(res)) suite.add("EagerBuiltMapEager150", () => mapEager(x => x + 1)(res150)) suite.add("EagerBuiltMapEager15", () => mapEager(x => x + 1)(res15)) fun map2(f) = case [a, ..rest, b] then [f(a), ..map2(f)(rest), f(b)] [] then [] fun map2Eager(f) = case [a, ...rest, b] then [f(a), ...map2Eager(f)(rest), f(b)] [] then [] :silent suite.add("EagerBuiltMap2Lazy1500", () => materialize(map2(x => x + 1)(res))) suite.add("EagerBuiltMap2Lazy150", () => materialize(map2(x => x + 1)(res150))) suite.add("EagerBuiltMap2Lazy15", () => materialize(map2(x => x + 1)(res15))) :silent suite.add("EagerBuiltMap2Eager1500", () => map2Eager(x => x + 1)(res)) suite.add("EagerBuiltMap2Eager150", () => map2Eager(x => x + 1)(res150)) suite.add("EagerBuiltMap2Eager15", () => map2Eager(x => x + 1)(res15)) :ge mapEager(..[[1]]) //│ ╔══[COMPILATION ERROR] Lazy spreads are not supported in call arguments //│ ║ l.288: mapEager(..[[1]]) //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 'mapEager' expected 1 argument but got 2 :e fun what(..args) = args //│ ╔══[COMPILATION ERROR] Lazy spread parameters not allowed. //│ ║ l.295: fun what(..args) = args //│ ╙── ^^^^^^ fun silly = case [a, b, ..xs] then [..xs, ..silly([a, ..xs])] [a, ..xs] then [a, ..silly(xs)] [] then [] fun sillyEager = case [a, b, ...xs] then [...xs, ...sillyEager([a, ...xs])] [a, ...xs] then [a, ...sillyEager(xs)] [] then [] :silent let xs = buildEager(150) :silent let ys = silly(xs) :silent suite.add("EagerBuiltSillyLazy1500", () => let xs = buildEager(1500) let ys = silly(xs) materialize(ys)) suite.add("EagerBuiltSillyLazy150", () => let xs = buildEager(150) let ys = silly(xs) materialize(ys)) suite.add("EagerBuiltSillyLazy15", () => let xs = buildEager(15) let ys = silly(xs) materialize(ys)) :silent sillyEager(xs) :silent suite.add("EagerBuiltSillyEager150", () => let xs = buildEager(150) sillyEager(xs)) suite.add("EagerBuiltSillyEager15", () => let xs = buildEager(15) sillyEager(xs)) :silent let configs = tuple of LazyArray.{ (name: "LazyArray", lazySlice: dropLeftRight, lazyConcat: __concat, split: __split) } FingerTreeList.{ (name: "FingerTreeList", lazySlice: dropLeftRight, lazyConcat: __markerConcat, split: __split) } LazyFingerTree.{ (name: "LazyFingerTree", lazySlice: dropLeftRight, lazyConcat: __concat, split: __split) } let i = 0 while i < configs.length do let cfg = configs.[i] let name = cfg.name // dirty hack to modify lazy spread behavior set Runtime.Tuple.lazySlice = (xs, a, b) => cfg.lazySlice(a, b)(xs) set Runtime.Tuple.lazyConcat = cfg.lazyConcat set Runtime.Tuple.split = cfg.split suite |> runSuite writeResults(suite, name + ".json") set i += 1 //│ i = 3 ================================================ FILE: hkmc2Benchmarks/src/test/bench/mlscript-compile/Benchmark.mls ================================================ import "benchmark" let bm = benchmark module Benchmark with... val benchmark = bm let prefixPath = "hkmc2Benchmarks/src/test/logs/" fun mkSuite() = new! mut bm.Suite() fun setPrefixPath(path) = set prefixPath = path fun runSuite(suite) = let settings = { async: false } suite.run(settings) fun writeResults(suite, path) = let jsonStr = JSON.stringify(suite.map((res, _, _) => res), null, 2) fs.writeFileSync(prefixPath + path, jsonStr) ================================================ FILE: hkmc2Benchmarks/src/test/bench/nofibs-bench1.mls ================================================ :js :silent // Comment to run the benchmarks :exit ==================================================================================================== import "./mlscript-compile/Benchmark.mls" open Benchmark let suite = mkSuite() import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/ansi.mls" suite.add("ansi", () => ansi.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/atom.mls" suite.add("atom", () => atom.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/awards.mls" suite.add("awards", () => awards.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/banner.mls" suite.add("banner", () => banner.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/lcss.mls" suite.add("lcss", () => lcss.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/boyer.mls" suite.add("boyer", () => boyer.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/boyer2.mls" suite.add("boyer2", () => boyer2.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/calendar.mls" suite.add("calendar", () => calendar.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/cichelli.mls" suite.add("cichelli", () => cichelli.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/circsim.mls" suite.add("circsim", () => circsim.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/clausify.mls" suite.add("clausify", () => clausify.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/constraints.mls" suite.add("constraints", () => constraints.main()) runSuite(suite) writeResults(suite, "nofibs1.json") ================================================ FILE: hkmc2Benchmarks/src/test/bench/nofibs-bench2.mls ================================================ :js :silent // Comment to run the benchmarks :exit ==================================================================================================== import "./mlscript-compile/Benchmark.mls" open Benchmark let suite = mkSuite() // need more stack space // import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm1.mls" // suite.add("cryptarithm1", () => cryptarithm1.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/cryptarithm2.mls" suite.add("cryptarithm2", () => cryptarithm2.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/cse.mls" suite.add("cse", () => cse.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/eliza.mls" suite.add("eliza", () => eliza.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/gcd.mls" suite.add("gcd", () => gcd.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/integer.mls" suite.add("integer", () => integer.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/knights.mls" suite.add("knights", () => knights.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/lambda.mls" suite.add("lambda", () => lambda.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/lastpiece.mls" suite.add("lastpiece", () => lastpiece.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/life.mls" suite.add("life", () => life.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/mandel.mls" suite.add("mandel", () => mandel.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/mandel2.mls" suite.add("mandel2", () => mandel2.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/mate.mls" suite.add("mate", () => mate.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/minimax.mls" suite.add("minimax", () => minimax.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/para.mls" suite.add("para", () => para.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/power.mls" suite.add("power", () => power.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/pretty.mls" suite.add("pretty", () => pretty.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/primetest.mls" suite.add("primetest", () => primetest.main()) // ======================= // need more stack // import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/puzzle.mls" // suite.add("puzzle", () => puzzle.main()) runSuite(suite) writeResults(suite, "nofibs2.json") ================================================ FILE: hkmc2Benchmarks/src/test/bench/nofibs-bench3.mls ================================================ :js :silent // Comment to run the benchmarks :exit ==================================================================================================== import "./mlscript-compile/Benchmark.mls" open Benchmark let suite = mkSuite() import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/rsa.mls" suite.add("rsa", () => rsa.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/scc.mls" suite.add("scc", () => scc.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/secretary.mls" suite.add("secretary", () => secretary.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/sorting.mls" suite.add("sorting", () => sorting.main()) // ======================= import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/sphere.mls" suite.add("sphere", () => sphere.main()) // ======================= // need more stack // import "../../../../hkmc2/shared/src/test/mlscript-compile/nofib/treejoin.mls" // suite.add("treejoin", () => treejoin.main()) runSuite(suite) writeResults(suite, "nofibs3.json") ================================================ FILE: hkmc2Benchmarks/src/test/bench/viz.html ================================================ Benchmark Results Visualization

📊 Benchmark Results Visualization

Upload your JSON benchmark files to visualize execution times with error bars

================================================ FILE: hkmc2Benchmarks/src/test/logs/.gitignore ================================================ *.json ================================================ FILE: hkmc2Benchmarks/src/test/scala/hkmc2/BenchDiffMaker.scala ================================================ package hkmc2 import mlscript.utils._, shorthands._ import hkmc2.syntax.Tree import hkmc2.syntax.Keyword class BenchDiffMaker (val rootPath: Str, val file: io.Path, val preludeFile: io.Path, val predefFile: io.Path, val relativeName: Str) (using val cctx: CompilerCtx) extends LlirDiffMaker: override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit = super.processTerm(blk, inImport) ================================================ FILE: hkmc2Benchmarks/src/test/scala/hkmc2/BenchTestRunner.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import mlscript.utils._ import os.Path import io.PlatformPath.given object BenchTestState extends DiffTestRunner.State: override def testDir = workingDir/"hkmc2Benchmarks"/"src"/"test"/"bench" override val TimeLimit = Span(1, Hour) class BenchTestRunner extends DiffTestRunnerBase(BenchTestState) with ParallelTestExecution : override protected def createDiffMaker (file: Path, preludePath: Path, predefPath: Path, relativeName: String) : DiffMaker = new BenchDiffMaker(state.workingDir.toString, file, preludePath, predefPath, relativeName)(using state.cctx) ================================================ FILE: hkmc2Benchmarks/src/test/scala/hkmc2/Watcher.scala ================================================ package hkmc2 import better.files.* object MainWatcher extends Watcher(File("../hkmc2/shared/src") :: File("./src") :: File("../hkmc2DiffTests/src") :: Nil): def main(args: Array[String]): Unit = run ================================================ FILE: hkmc2DiffTests/src/test/out/Basic Document Tests.out ================================================ // Generated from: hkmc2DiffTests/src/test/scala/hkmc2/DocumentTests.scala // L.44: // DocCons(DocText(hello),DocCons(DocText( ),DocText(world))) hello world // L.46: // DocNest(2,DocCons(DocText(hello),DocCons(DocBreak(false),DocText(world)))) hello world // L.48: hello hello hello world world world // L.50: hello world // L.52: hello world // L.54: hello hello hello world world world // L.56: hello hello hello world world world // L.58: hello hello hello world world world // L.60: hi hi world world // L.62: hi hi world world // L.64: hi hi W W ! ! // L.66: hi hi W W ! ! // L.68: hi hi W W ! ! // L.70: hi hi world world // L.72: hello hello hello world world world // L.74: hello hello hello universe universe universe // L.76: hello hello hello world world world // L.78: hello hello hello world world world // L.80: hello hello hello world world world ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/DiffMaker.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import hkmc2.utils.* class Outputter(val out: java.io.PrintWriter): val outputMarker = "//│ " // val oldOutputMarker = "/// " val diffBegMarker = "<<<<<<<" val diffMidMarker = "=======" val diff3MidMarker = "|||||||" // * Appears under `git config merge.conflictstyle diff3` (https://stackoverflow.com/a/18131595/1518588) val diffEndMarker = ">>>>>>>" val ColWidth = 100 val exitMarker = "=" * ColWidth val blockSeparator = "—" * 80 val fullBlockSeparator = outputMarker + blockSeparator /** Tracks the net difference between lines written to the output and lines * consumed from the original file so far. Adding a new output line (via * [[apply]]) increments it; consuming an original output line (starting * with [[outputMarker]]) decrements it. This is used to adjust block * line numbers so they refer to positions in the output file rather than * the original, avoiding the need for a second run to stabilize them. */ var linesDelta: Int = 0 def apply(str: String) = // out.println(outputMarker + str) val ls = str.splitSane('\n') linesDelta += ls.size ls.foreach(l => out.println(outputMarker + l)) abstract class DiffMaker: def cctx: CompilerCtx given CompilerCtx = cctx val file: io.Path val relativeName: Str def processOrigin(origin: Origin)(using Raise): Unit val dbgPrinter: DebugPrinter given dbgPrinter.type = dbgPrinter def doFail(blockLineNum: Int, msg: String): Unit = System.err.println(fansi.Color.Red("FAILURE: ").toString + msg) def unhandled(blockLineNum: Int, exc: Throwable): Unit = unexpected("exception", blockLineNum, N, () => N) final def unexpected(what: Str, blockLineNum: Int, srcLoc: Opt[Str], mkExtraInfo: () => Opt[Any]): Unit = output(s"FAILURE: Unexpected $what") srcLoc match case S(loc) => output(s"FAILURE LOCATION: $loc") case N => () mkExtraInfo() match case S(info: Product) => output(s"FAILURE INFO: ${info.showAsTree}") case S(info) => output(s"FAILURE INFO: ${info.showAsPlain}") case N => () doFail(blockLineNum, s"unexpected $what at $relativeName.${file.ext}:" + blockLineNum) private val commands: mutable.Map[Str, Command[?]] = mutable.Map.empty def resetCommands: Unit = commands.valuesIterator.foreach(cmd => if !cmd.isGlobal then cmd.unset) class Command[A](val name: Str, var isGlobal: Bool = false)(val process: Str => A): require(name.nonEmpty) // require(name.forall(l => l.isLetterOrDigit || l === '!')) if commands.contains(name) then throw new IllegalArgumentException(s"Option '$name' already exists") commands += name -> this private var currentValue: Opt[A] = N private[hkmc2] def setCurrentValue(a: A): Unit = currentValue = S(a) onSet() def get: Opt[A] = currentValue def isSet: Bool = currentValue.isDefined def isUnset: Bool = !isSet def unset: Unit = currentValue = N def onSet(): Unit = () override def toString: Str = s"${if isGlobal then "global " else ""}$name: $currentValue" class NullaryCommand[R](name: Str, k: () => R = () => ()) extends Command[R](name)( line => val commentIndex = line.indexOf("//") val body = if commentIndex == -1 then line else line.take(commentIndex) // assert(body.forall(_.isWhitespace)) if !body.forall(_.isWhitespace) then output(s"/!\\ Warning: non-empty body for command '$name' is ignored: '$body'") k() ): def set: Unit = setCurrentValue(k()) class FlagCommand(init: Bool, name: Str) extends NullaryCommand(name): self => val disable = new NullaryCommand("!" + name): override def onSet(): Unit = if isSet then self.unset else self.setCurrentValue(()) if init then setCurrentValue(()) else disable.setCurrentValue(()) val initCmd = NullaryCommand("init") initCmd.setCurrentValue(()) // * Starts enabled at the top of the file val global = NullaryCommand("global") global.setCurrentValue(()) // * Starts enabled at the top of the file val consumeEmptyLines = NullaryCommand("...", () => output(output.blockSeparator) ) val fixme = Command("fixme")(_ => ()) val todo = Command("todo")(_ => ()) val breakme = Command("breakme")(_ => ()) val ignore = Command("ignore")(_ => ()) def expectErrors = fixme.isSet || todo.isSet def tolerateErrors = expectErrors || ignore.isSet val fullExceptionStack = NullaryCommand("s") val verbose = NullaryCommand("v") val debug = NullaryCommand("d") val expectParseErrors = NullaryCommand("pe") val expectTypeErrors = NullaryCommand("te") val expectTypeOrCodeGenErrors = NullaryCommand("e") val expectRuntimeErrors = NullaryCommand("re") val expectCodeGenErrors = NullaryCommand("ge") def expectRuntimeOrCodeGenErrors = expectRuntimeErrors.isSet || expectCodeGenErrors.isSet || expectTypeOrCodeGenErrors.isSet val allowRuntimeErrors = NullaryCommand("allowRuntimeErrors") val expectWarnings = NullaryCommand("w") val showRelativeLineNums = NullaryCommand("showRelativeLineNums") val tests = Command("tests"): case "" => // * Note that making `DiffTestRunnerBase` extend `ParallelTestExecution`, // * as we used to do, is quite dangerous, because of the way ScalaTest works (which is pretty dumb): // * it would try to re-instantiate the test classes haphazardly without passing it any arguments, // * which either crashes (as it would here) or recomputes the state every time // * (as would be the case if we created an anonymous subclass here), // * even when the tests, when run with `execute()`, are not run in parallel (also for dumb reasons). DiffTestRunnerBase(new DiffTestRunner.StateWithGit).execute() val fileName = file.last val fileContents = cctx.fs.read(file) val allLines = fileContents.splitSane('\n').toList val strw = new java.io.StringWriter val out = new java.io.PrintWriter(strw) val output = Outputter(out) val report = ReportFormatter(output(_), colorize = false) var printedSeparatedSection = false def outputSeparator(title: Str): Unit = printedSeparatedSection = true val totalSepLen = output.ColWidth - title.length - 4 val preSepLen = output.ColWidth/5 - title.length/2 output("—" * preSepLen + s"| $title |" + "—" * (totalSepLen - preSepLen)) val failures = mutable.Buffer.empty[Int] val unmergedChanges = mutable.Buffer.empty[Int] var _onlyParse = false var _allowTypeErrors = false var _showRelativeLineNums = false val errMarker: Str = "/!!!\\" def uncaught(err: Throwable): Unit = output(s"$errMarker Uncaught error: $err" + err.getStackTrace().take( if fullExceptionStack.isSet || debug.isSet then Int.MaxValue else if tolerateErrors || err.isInstanceOf[StackOverflowError] then 0 else 10 ).map("\n" + "\tat: " + _).mkString) def processBlock(origin: Origin): Unit = printedSeparatedSection = false val globalStartLineNum = origin.startLineNum val blockLineNum = origin.startLineNum // * ^ In previous DiffTest versions, these two could be different due to relative line numbers var parseErrors, typeErrors, compilationErrors, runtimeErrors, warnings, internalErrors = 0 val raise: Raise = d => d.kind match case Diagnostic.Kind.Error => d.source match case Diagnostic.Source.Lexing => parseErrors += 1 if expectParseErrors.isUnset && !tolerateErrors then failures += globalStartLineNum unexpected("lexing error", blockLineNum, S(d.srcLoc), d.mkExtraInfo) case Diagnostic.Source.Parsing => parseErrors += 1 if expectParseErrors.isUnset && !tolerateErrors then failures += globalStartLineNum // doFail(fileName, blockLineNum, "unexpected parse error at ") unexpected("parse error", blockLineNum, S(d.srcLoc), d.mkExtraInfo) // report(blockLineNum, d :: Nil, showRelativeLineNums.isSet) case Diagnostic.Source.Typing => typeErrors += 1 if expectTypeErrors.isUnset && !tolerateErrors then failures += globalStartLineNum unexpected("type error", blockLineNum, S(d.srcLoc), d.mkExtraInfo) case Diagnostic.Source.Compilation => compilationErrors += 1 if expectCodeGenErrors.isUnset && expectTypeOrCodeGenErrors.isUnset && !tolerateErrors then failures += globalStartLineNum unexpected("compilation error", blockLineNum, S(d.srcLoc), d.mkExtraInfo) case Diagnostic.Source.Runtime => runtimeErrors += 1 if !expectRuntimeOrCodeGenErrors && !tolerateErrors then failures += globalStartLineNum unexpected("runtime error", blockLineNum, S(d.srcLoc), d.mkExtraInfo) case Diagnostic.Kind.Warning => warnings += 1 if expectWarnings.isUnset && !tolerateErrors then failures += globalStartLineNum unexpected("warning", blockLineNum, S(d.srcLoc), d.mkExtraInfo) case Diagnostic.Kind.Internal => internalErrors += 1 if !tolerateErrors then failures += globalStartLineNum unexpected("internal error", blockLineNum, S(d.srcLoc), d.mkExtraInfo) // throw d if fullExceptionStack.isSet then d.printStackTrace() report(blockLineNum, d :: Nil, showRelativeLineNums.isSet) processOrigin(origin)(using raise) // Note: when `todo` is set, we allow the lack of errors. // Use `todo` when the errors are expected but not yet implemented. if expectParseErrors.isSet && parseErrors === 0 && ignore.isUnset && breakme.isUnset then failures += globalStartLineNum unexpected("lack of parse error", blockLineNum, N, () => N) if expectTypeErrors.isSet && typeErrors === 0 && ignore.isUnset && breakme.isUnset then failures += globalStartLineNum unexpected("lack of type error", blockLineNum, N, () => N) if expectCodeGenErrors.isSet && compilationErrors === 0 && ignore.isUnset && breakme.isUnset then failures += globalStartLineNum unexpected("lack of compilation error", blockLineNum, N, () => N) else if expectTypeOrCodeGenErrors.isSet && (compilationErrors + typeErrors) === 0 && ignore.isUnset && breakme.isUnset then failures += globalStartLineNum unexpected("lack of compilation or type error", blockLineNum, N, () => N) if expectRuntimeErrors.isSet && runtimeErrors === 0 && ignore.isUnset && breakme.isUnset then failures += globalStartLineNum unexpected("lack of runtime error", blockLineNum, N, () => N) if expectWarnings.isSet && warnings === 0 && ignore.isUnset && breakme.isUnset then failures += globalStartLineNum unexpected("lack of warnings", blockLineNum, N, () => N) if expectErrors && ( + parseErrors + typeErrors + compilationErrors + runtimeErrors + warnings + internalErrors) === 0 then failures += globalStartLineNum unexpected("lack of error to fix", blockLineNum, N, () => N) @annotation.tailrec final def rec(lines: List[String]): Unit = lines match case "" :: Nil => // To prevent adding an extra newline at the end case (line @ "") :: ls if consumeEmptyLines.isUnset => out.println(line) if initCmd.isSet then init() resetCommands rec(ls) case ":exit" :: ls => out.println(":exit") out.println(output.exitMarker) ls.dropWhile(_ =:= output.exitMarker).tails.foreach { case Nil => case lastLine :: Nil => out.print(lastLine) case l :: _ => out.println(l) } case line :: ls if line.startsWith(":") => out.println(line) val cmd = line.tail.takeWhile(!_.isWhitespace) val rest = line.drop(cmd.length + 1) commands.get(cmd) match case S(cmd) => if global.isSet then cmd.isGlobal = true cmd.setCurrentValue(cmd.process(rest)) case N => failures += allLines.size - lines.size + 1 output("/!\\ Unrecognized command: " + cmd) rec(ls) case line :: ls if line.startsWith(output.outputMarker) //|| line.startsWith(oldOutputMarker) => output.linesDelta -= 1 rec(ls) case line :: ls if line.startsWith("//") => out.println(line) rec(ls) case begLine :: ls if begLine.startsWith(output.diffBegMarker) => // Check if there are unmerged git conflicts val diff = ls.takeWhile(l => !l.startsWith(output.diffEndMarker)) assert(diff.exists(_.startsWith(output.diffMidMarker)), diff) val rest = ls.drop(diff.length) val hdo = rest.head assert(hdo.startsWith(output.diffEndMarker), hdo) val blankLines = diff.count(_.isEmpty) val hasBlankLines = diff.exists(_.isEmpty) if diff.forall(l => l.startsWith(output.outputMarker) || l.startsWith(output.diffMidMarker) || l.startsWith(output.diff3MidMarker) || l.isEmpty) then { for _ <- 1 to blankLines do out.println() } else { val blockLineNum = allLines.size - lines.size + 1 failures += blockLineNum doFail(blockLineNum, s"Unmerged non-output changes at $relativeName.${file.ext}:" + blockLineNum) unmergedChanges += allLines.size - lines.size + 1 out.println(begLine) diff.foreach(out.println) out.println(hdo) } if hasBlankLines then resetCommands rec(rest.tail) case l :: ls => val blockLineNum = allLines.size - lines.size + 1 val block = (l :: ls.takeWhile(l => (l.nonEmpty || consumeEmptyLines.isSet) && !( l.startsWith(output.outputMarker) || l.startsWith(output.diffBegMarker) // || l.startsWith(oldOutputMarker) ))).toIndexedSeq block.foreach(out.println) val processedBlock = block val processedBlockStr = processedBlock.mkString val fph = new FastParseHelpers(block) val origin = Origin(file, blockLineNum + output.linesDelta, fph) try processBlock(origin) catch case oh_noes: ThreadDeath => throw oh_noes case err: Throwable => if !tolerateErrors then failures += allLines.size - lines.size + 1 unhandled(blockLineNum, err) // err.printStackTrace(out) // println(err.getCause()) uncaught(err) if consumeEmptyLines.isSet then output(output.blockSeparator) consumeEmptyLines.unset rec(lines.drop(block.size)) case Nil => def run(): Unit = val starttime = System.currentTimeMillis() try rec(allLines) finally val endtime = System.currentTimeMillis() val duration = (endtime - starttime).toString println(s"${fansi.Color.Cyan.escape}Processed in ${Console.BOLD}${ " " * (5 - duration.length) + duration } ms${Console.RESET} ${ Console.YELLOW + relativeName + "." + file.ext + Console.RESET }") out.close() val result = strw.toString if result =/= fileContents then println(s"Updating $file...") cctx.fs.write(file, result) // * Called after the very first command block // * and every time a further command block with `:init` finishes def init(): Unit = () end DiffMaker ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/DiffTestRunner.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import org.scalatest.concurrent.{TimeLimitedTests, Signaler} import os.up import mlscript.utils._, shorthands._ import io.PlatformPath.given, io.FileSystem // * Note: we used to use: // * class AllTests extends org.scalatest.Suites( // * new CompileTestRunner(DiffTestRunner.State){}, // * new DiffTestRunner(DiffTestRunner.State){}, // * ) // * but this (very surprisinbgly) disables parallel execution each individual suite. // * So now we just split tests into separate SBT projects. object DiffTestRunner: class State: val cctx: CompilerCtx = CompilerCtx.fresh(io.FileSystem.default) val pwd = os.pwd // println(s"INITIALIZING DiffTestRunner.State in ${pwd}") val workingDir = if pwd.last == "hkmc2DiffTests" then pwd/up // For some reason, when run from ~hkmc2JVM/Test/run in sbt, the pwd is ".../hkmc2/jvm" else pwd // val dir = workingDir/"hkmc2"/"shared"/"src"/"test"/"mlscript" val dir = workingDir/"hkmc2"/"shared"/"src"/"test" // To be overridden in subproject-specific State classes def testDir: os.Path = dir val validExt = Set("mls") val allFiles = os.walk(testDir) .filter(_.toIO.isFile) .filter(_.ext in validExt) def filter(file: os.RelPath): Bool = true val TimeLimit = if sys.env.get("CI").isDefined then Span(60, Seconds) else Span(30, Seconds) end State class StateWithGit extends State: println(s"Running git in ${dir}...") // * Aggregate unstaged modified files to only run the tests on them, if there are any val modified: Set[os.RelPath] = try os.proc("git", "status", "--porcelain", dir).call().out.lines().iterator.flatMap { gitStr => println(" [git] " + gitStr) val prefix = gitStr.take(2) val filePath = os.RelPath(gitStr.drop(3)) if prefix =:= "A " || prefix =:= "M " || prefix =:= "R " || prefix =:= "D " then N // * Disregard modified files that are staged else if filePath.ext =/= "mls" then N else S(filePath) }.toSet catch case err: Throwable => System.err.println("/!\\ git command failed with: " + err) Set.empty if modified.isEmpty then println("No test file with unstaged changes detected; no test will run.") override def filter(file: os.RelPath): Bool = // println(s"Filtering: $file ${modified(file)}") modified(file) end StateWithGit lazy val State = new State end DiffTestRunner class DiffTestRunner extends DiffTestRunnerBase(DiffTestRunner.State) with ParallelTestExecution: override protected def excludedDiffDirs: Ls[os.Path] = TestFolders.mainExcludedDiffDirs(state.workingDir) class DiffTestRunnerBase(val state: DiffTestRunner.State) extends funsuite.AnyFunSuite with TimeLimitedTests : import state.* private val inParallel = isInstanceOf[ParallelTestExecution] val timeLimit = TimeLimit override val defaultTestSignaler: Signaler = new Signaler: @annotation.nowarn("msg=method stop in class Thread is deprecated") def apply(testThread: Thread): Unit = println(s"!! Test at $testThread has run out out time !! stopping..." + "\n\tNote: you can increase this limit by changing DiffTests.TimeLimit") // * Thread.stop() is considered bad practice because normally it's better to implement proper logic // * to terminate threads gracefully, avoiding leaving applications in a bad state. // * But here we DGAF since all the test is doing is running a type checker and some Node REPL, // * which would be a much bigger pain to make receptive to "gentle" interruption. // * It would feel extremely wrong to intersperse the pure type checker algorithms // * with ugly `Thread.isInterrupted` checks everywhere... testThread.stop() protected def excludedDiffDirs: Ls[os.Path] = TestFolders.alwaysExcludedDiffDirs(state.workingDir) protected lazy val diffTestFiles = allFiles.filter: file => ( !TestFolders.isExcluded(file, excludedDiffDirs) && filter(file.relativeTo(state.workingDir)) ) protected def createDiffMaker( file: os.Path, preludePath: os.Path, predefPath: os.Path, relativeName: String ): DiffMaker = new MainDiffMaker(workingDir.toString, file, preludePath, predefPath, relativeName): def cctx = state.cctx diffTestFiles.foreach: file => val basePath = file.segments.drop(dir.segmentCount).toList.init val relativeName = basePath.map(_ + "/").mkString + file.baseName test(relativeName): val preludePath = dir/"mlscript"/"decls"/"Prelude.mls" val predefPath = dir/"mlscript-compile"/"Predef.mls" val dm = createDiffMaker(file, preludePath, predefPath, relativeName) dm.run() if dm.failures.nonEmpty then fail(s"Unexpected test outcome(s) at: " + dm.failures.distinct.map("\n\t"+relativeName+"."+file.ext+":"+_).mkString(", ")) end DiffTestRunnerBase ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/DocumentTests.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time.* import org.scalatest.concurrent.{TimeLimitedTests, Signaler} import mlscript.utils.*, shorthands.* import document.* import document.Document.* import sourcecode.{Line, File} class DocumentTests extends funsuite.AnyFunSuite: def runTest(nme: Str)(body: StringBuilder => Unit) = test(nme): val path = os.pwd/"hkmc2DiffTests"/"src"/"test"/"out"/(nme + ".out") val contents = if os.exists(path) then os.read(path) else "" val strb = new StringBuilder body(strb) val res = strb.toString if res =/= contents then os.write.over(path, res) println(s"Updating $path") runTest("Basic Document Tests"): strb => strb ++= s"// Generated from: ${os.Path(summon[File].value).relativeTo(os.pwd)}\n" def mk(d: Document, showRaw: Bool = false)(using Line) = strb ++= "\n" strb ++= s"// L.${summon[Line].value}:\n" if showRaw then strb ++= s"//\t$d\n" strb ++= d.mkString(20) strb ++= "\n" mk(doc"hello" :: " " :: doc"world", showRaw = true) mk(nest(doc"hello" :/: doc"world"), showRaw = true) mk(doc"hello hello hello" :: break :: doc"world world world") mk(doc"hello" :: forceBreak :: doc"world") mk(nest(doc"hello" :: forceBreak :: doc"world")) mk(nest(doc"hello hello hello" :: break :: doc"world world world")) mk(group(doc"hello hello hello" :: break :: doc"world world world")) mk(nest(group(doc"hello hello hello" :: break :: doc"world world world"))) mk(doc"\{hi # hi\} # \{world # world\}") mk(doc"\{hi # hi\}\n\{world # world\}") mk(doc"\{hi # hi\} # \{W # W\} # \{! # !\}") mk(doc"\{hi # hi\}\n\{W # W\} # \{! # !\}") mk(doc"\{hi # hi\} # \{W # W\}\n\{! # !\}") mk(doc"\{hi # hi\} # \{world\nworld\}") mk(doc"\{hello # hello # hello\} # \{world # world # world\}") mk(doc"\{hello # hello # hello\} # \{universe # universe # universe\}") mk(doc" #{ \{hello # hello # hello\}\n\{world # world # world\} #} ") mk(doc" #{ \{hello # hello\nhello\}\n\{world # world # world\} #} ") mk(doc" #{ \{hello # hello\nhello\}\n\{world # world\nworld\} #} ") end DocumentTests ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/InvalmlDiffMaker.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import hkmc2.semantics.* import hkmc2.invalml.* import utils.Scope abstract class InvalMLDiffMaker extends JSBackendDiffMaker: val invalPreludeFile = io.Path(rootPath) / "hkmc2" / "shared" / "src" / "test" / "mlscript" / "invalml" / "InvalMLPrelude.mls" val invalmlOpt = new NullaryCommand("invalml"): override def onSet(): Unit = super.onSet() noSanityCheck.isGlobal = true noSanityCheck.set if file =/= invalPreludeFile then curCtx = Elaborator.State.init given Config = mkConfig importFile(invalPreludeFile, verbose = false) override def init(): Unit = super.init() lazy val invalCtx = given Elaborator.Ctx = curCtx invalml.InvalCtx.init(_ => die) var invalmlTyper: Opt[InvalTyper] = None override def processTerm(trm: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit = super.processTerm(trm, inImport) if invalmlOpt.isSet then given Scope = Scope.empty(Scope.Cfg.default) if invalmlTyper.isEmpty then given Elaborator.Ctx = curCtx invalmlTyper = S(InvalTyper()) given hkmc2.invalml.InvalCtx = invalCtx.copy(raise = summon) val typer = invalmlTyper.get val ty = typer.typePurely(trm) val printer = PrettyPrinter((msg: String) => output(msg)) if debug.isSet then printer.print(ty) val simplif = TypeSimplifier(tl) val sty = simplif(true, 0)(ty) printer.print(sty) ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import utils.* import semantics.* import codegen.* import codegen.js.{JSBuilder, JSBuilderArgNumSanityChecks} import document.* import codegen.Block import utils.Scope import hkmc2.syntax.Tree.Ident import hkmc2.codegen.Path import hkmc2.Diagnostic.Source import hkmc2.Message.MessageContext abstract class JSBackendDiffMaker extends MLsDiffMaker: val debugLowering = NullaryCommand("dl") val noCodeGen = NullaryCommand("noCodeGen") val js = NullaryCommand("js") val showSanitizedJS = NullaryCommand("ssjs") val showJS = NullaryCommand("sjs") val showRepl = NullaryCommand("showRepl") val traceJS = NullaryCommand("traceJS") val expect = Command("expect"): ln => ln.trim private val baseScp: utils.Scope = utils.Scope.empty(utils.Scope.Cfg.default) private lazy val irPrintingScp: utils.Scope = // for IR printing only Scope.empty(Scope.Cfg.default.copy( escapeChars = false, useSuperscripts = false, includeZero = false, )) val runtimeNme = baseScp.allocateName(Elaborator.State.runtimeSymbol)(using throw _) val termNme = baseScp.allocateName(Elaborator.State.termSymbol)(using throw _) val blockNme = baseScp.allocateName(Elaborator.State.blockSymbol)(using throw _) val optionNme = baseScp.allocateName(Elaborator.State.optionSymbol)(using throw _) val definitionMetadataNme = baseScp.allocateName(Elaborator.State.definitionMetadataSymbol)(using throw _) val prettyPrintNme = baseScp.allocateName(Elaborator.State.prettyPrintSymbol)(using throw _) val ltl = new TraceLogger: override def doTrace = debugLowering.isSet || scope.exists: showUCS.get.getOrElse(Set.empty).contains override def emitDbg(str: String): Unit = output(str) val replTL = new TraceLogger: override def doTrace = showRepl.isSet override def emitDbg(str: String): Unit = output(str) lazy val host = hostCreated = true given TL = replTL val h = ReplHost(rootPath) def importRuntimeModule(name: Str, file: io.Path) = h.execute(s"const $name = (await import(\"${file}\")).default;") match case ReplHost.Result(msg) => if msg.startsWith("Uncaught") then output(s"Failed to load $name: $msg") case r => output(s"Failed to load $name: $r") importRuntimeModule(runtimeNme, runtimeFile) h.execute(s"const $definitionMetadataNme = Symbol.for(\"mlscript.definitionMetadata\");") h.execute(s"const $prettyPrintNme = Symbol.for(\"mlscript.prettyPrint\");") if importQQ.isSet then importRuntimeModule(termNme, termFile) if stageCode.isSet then importRuntimeModule(blockNme, blockFile) importRuntimeModule(optionNme, optionFile) h private var hostCreated = false override def run(): Unit = try super.run() finally if hostCreated then host.terminate() override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit = super.processTerm(blk, inImport) val outerRaise: Raise = summon val reportedMessages = mutable.Set.empty[Str] def definedValues(includeNonTerms: Bool) = import Elaborator.Ctx.* curCtx.env.iterator.flatMap: case (nme, e @ (_: RefElem | SelElem(base = RefElem(_: InnerSymbol)))) => e.symbol match case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) case S(ts: BlockMemberSymbol) if includeNonTerms || ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts, N)) case S(vs: VarSymbol) => S((nme, vs, N)) case _ => N case _ => N .toList val symbolsToPreserve = definedValues(includeNonTerms = true).iterator.map(_._2).toSet val effectiveConfig = Config.extractConfigFromStats(blk) if showJS.isSet then given Raise = case d @ ErrorReport(source = Source.Compilation) => reportedMessages += d.mainMsg outerRaise(d) case d => outerRaise(d) given Elaborator.Ctx = curCtx val low = ltl.givenIn: codegen.Lowering()(using effectiveConfig) val jsb = ltl.givenIn: JSBuilder(using effectiveConfig) val le_0 = low.program(blk) val le_1 = ltl.givenIn: BlockSimplifier(symbolsToPreserve)(le_0) val le_2 = ltl.givenIn: DeadParamElim(le_1) val nestedScp = baseScp.nest val je = nestedScp.givenIn: jsb.programBody(le_2, N, wd) val jsStr = je.stripBreaks.mkString(output.ColWidth) outputSeparator("JS (unsanitized)") output(jsStr) if noCodeGen.isUnset then given Elaborator.Ctx = curCtx given Raise = case e: ErrorReport if reportedMessages.contains(e.mainMsg) => if verbose.isSet then output(s"Skipping already reported diagnostic: ${e.mainMsg}") case d => outerRaise(d) val low = ltl.givenIn: new codegen.Lowering()(using effectiveConfig) with codegen.LoweringSelSanityChecks with codegen.LoweringTraceLog(traceJS.isSet) val lowered_0 = low.program(blk) if showLoweredTree.isSet then outputSeparator("Lowered IR Tree") output(lowered_0.showAsTree) if showIR.isSet then outputSeparator("Lowered IR") given ShowCfg = ShowCfg( showExpansionMappings = false, showFlowSymbols = true, debug = debug.isSet, ) output(Printer().worksheet(lowered_0)(using irPrintingScp).mkString(output.ColWidth)) val lowered_1 = ltl.givenIn: BlockSimplifier(symbolsToPreserve)(lowered_0) val lowered_2 = ltl.givenIn: DeadParamElim(lowered_1) // TODO: Test that transformers retain object identity when there are no changes if (lowered_2 isnt lowered_0) && (lowered_2 === lowered_0) then output("/!\\ Warning: object identity between equal objects was not preserved by BlockSimplifier or DeadParamElim") def rec(lhs: Block, rhs: Block): Bool = (lhs is rhs) || { if lhs.subBlocks.iterator.zip(rhs.subBlocks.iterator).forall: case (s1: Block, s2: Block) => rec(s1, s2) then output(s"/!\\ Offending subblock: ${lhs.showAsTree}") false else false } rec(lowered_0.main, lowered_2.main) if checkIR.isSet then BlockChecker().applyProgram(lowered_2) if showOptimizedIR.isSet then outputSeparator("Optimized IR") given ShowCfg = ShowCfg( showExpansionMappings = false, showFlowSymbols = true, debug = debug.isSet, ) output(Printer().worksheet(lowered_2)(using irPrintingScp).mkString(output.ColWidth)) if showOptimizedTree.isSet then outputSeparator("Optimized IR Tree") output(lowered_2.showAsTree) processIRBlock(lowered_2, definedValues) end processTerm type ComputeDefinedValues = (includeNonTerms: Bool) => Ls[(Str, Symbol, Opt[Str])] def processIRBlock(pgrm: Program, definedValues: ComputeDefinedValues)(using Config, Raise, Elaborator.Ctx): Unit = if js.isSet then // * We used to do this to avoid needlessly generating new variable names in separate blocks: // val nestedScp = baseScp.nest val nestedScp = baseScp // val nestedScp = codegen.js.Scope(S(baseScp), curCtx.outer, collection.mutable.Map.empty) // * not needed val resSym = new TempSymbol(N, "block$res") val resNme = nestedScp.allocateName(resSym) val loweredMapped = pgrm.copy(main = pgrm.main.mapReturn: case Return(res, implct) => assert(implct) Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) ) val jsb = ltl.givenIn: new JSBuilder with JSBuilderArgNumSanityChecks val (pre, js) = nestedScp.givenIn: jsb.worksheet(loweredMapped) val preStr = pre.stripBreaks.mkString(output.ColWidth) val jsStr = js.stripBreaks.mkString(output.ColWidth) if showSanitizedJS.isSet then outputSeparator("JS (sanitized)") if preStr.nonEmpty then output(preStr) output(jsStr) if printedSeparatedSection then outputSeparator("Output") def mkQuery(preStr: Str, jsStr: Str)(k: Str => Unit) = val queryStr = jsStr.replaceAll("\n", " ") val (reply, stderr) = host.query(preStr, queryStr, !expectRuntimeOrCodeGenErrors && !tolerateErrors) reply match case ReplHost.Result(content) => k(content) case ReplHost.Empty => case ReplHost.Unexecuted(message) => ??? case ReplHost.Error(isSyntaxError, message, otherOutputs) => if otherOutputs.nonEmpty then otherOutputs.splitSane('\n').foreach: line => output(s"> ${line}") if (isSyntaxError) then // If there is a syntax error in the generated code, // it should be a code generation error. raise(ErrorReport(msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, source = Diagnostic.Source.Compilation)) else // Otherwise, it is considered a simple runtime error. raise(ErrorReport(msg"${message}" -> N :: Nil, source = Diagnostic.Source.Runtime)) if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") if traceJS.isSet then host.execute( s"$runtimeNme.TraceLogger.enabled = true; " + s"$runtimeNme.TraceLogger.resetIndent(0)") // * Sometimes the JS block won't execute due to a syntax or runtime error so we always set this first host.execute(s"$resNme = undefined") mkQuery(preStr, jsStr): stdout => stdout.splitSane('\n').init // should always ends with "undefined" (TODO: check) .foreach: line => output(s"> ${line}") if traceJS.isSet then host.execute(s"$runtimeNme.TraceLogger.enabled = false") if silent.isUnset then val valuesToPrint = ("", resSym, expect.get) +: definedValues(includeNonTerms = false).toSeq.sortBy(_._1) valuesToPrint.foreach: (nme, sym, expect) => val le = import codegen.* Return( Call( Value.Ref(Elaborator.State.runtimeSymbol).selSN("printRaw"), (Arg(N, Value.Ref(sym, N)) :: Nil) ne_:: Nil)(true, false, false), implct = true) val je = nestedScp.givenIn: jsb.block(le, endSemi = false) val jsStr = je.stripBreaks.mkString(output.ColWidth) mkQuery("", jsStr): out => // Omit the last line which is always "undefined" or the unit. val result = out.lastIndexOf('\n') match case n if n >= 0 => out.substring(0, n) case _ => "" expect match case S(expected) if result =/= expected => raise: ErrorReport(msg"Expected: '${expected}', got: '${result}'" -> N :: Nil, source = Diagnostic.Source.Runtime) case _ => () val anon = nme.isEmpty result match case "undefined" if anon => case "()" if anon => case _ => output(s"${if anon then "" else s"$nme "}= $result") ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/LlirDiffMaker.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import utils.* import document.* import codegen.Block import codegen.llir.* import codegen.cpp.* import hkmc2.syntax.Tree.Ident import hkmc2.codegen.Path import hkmc2.semantics.Term.Blk import hkmc2.utils.Scope import hkmc2.codegen.llir._ import hkmc2.codegen.cpp._ import hkmc2.semantics.Elaborator import scala.collection.mutable.ListBuffer abstract class LlirDiffMaker extends InvalMLDiffMaker: val llir = NullaryCommand("llir") val sllir = NullaryCommand("sllir") val intl = NullaryCommand("intl") val lprelude = NullaryCommand("lpre") // C++ codegen generation commands for individual blocks val cpp = NullaryCommand("cpp") val scpp = NullaryCommand("scpp") val rcpp = NullaryCommand("rcpp") val wcpp = Command[Str]("wcpp", false)(x => x.stripLeading()) // C++ codegen generation commands for the whole program val wholeCpp = NullaryCommand("wholeCpp") val sWholeCpp = NullaryCommand("showWholeCpp") val rWholeCpp = NullaryCommand("runWholeCpp") val wWholeCpp = Command[Str]("writeWholeCpp", false)(x => x.stripLeading()) def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) = val p = new java.io.PrintWriter(f) try { op(p) } finally { p.close() } given Elaborator.Ctx = curCtx object Llir: // Avoid polluting the namespace val freshId = FreshInt() var ctx = codegen.llir.Ctx.empty val scope = Scope.empty(Scope.Cfg.default) val wholeProg = ListBuffer.empty[Program] import Llir.* def mkWholeProgram: Program = if wholeProg.length == 0 then throw new Exception("No program to make") else Program( classes = wholeProg.iterator.flatMap(_.classes).toSet, defs = wholeProg.iterator.flatMap(_.defs).toSet, entry = wholeProg.last.entry ) override def processTerm(trm: Blk, inImport: Bool)(using Config, Raise): Unit = super.processTerm(trm, inImport) if llir.isSet then val low = ltl.givenIn: codegen.Lowering() // TODO: There should be a third compilation target of CPP? var le = low.program(trm) given Scope = scope given Ctx = ctx val llb = LlirBuilder(tl, freshId) try val (llirProg, ctx2) = llb.bProg(le) ctx = ctx2 wholeProg += llirProg if sllir.isSet && !silent.isSet then output("LLIR:") if debug.isSet then output(LlirDebugPrinter.mkDocument(llirProg).toString) else output(LlirPrinter(using summon[Raise], Scope.empty(Scope.Cfg.default)) .mkDocument(llirProg).mkString(output.ColWidth)) def cppGen(name: String, prog: Program, gen: Bool, show: Bool, run: Bool, write: Opt[Str]): Unit = tl.log(s"Generating $name") if gen || show || run || write.isDefined then val cpp = CppCodeGen(ctx.builtinSym.hiddenClasses, tl).codegen(prog) if show then output(s"\n$name:") output(cpp.toDocument.mkString(output.ColWidth)) val rPath = os.Path(rootPath) val auxPath = rPath/"hkmc2"/"shared"/"src"/"test"/"mlscript-compile"/"cpp" if write.isDefined then printToFile(java.io.File((auxPath / s"${write.get}").toString)): p => p.println(cpp.toDocument.mkString(output.ColWidth)) if run then val cppHost = CppCompilerHost(auxPath.toString, output.apply) if !cppHost.ready then output("\nCpp Compilation Failed: Cpp compiler or GNU Make not found") else if !silent.isSet then output("\n") cppHost.compileAndRun(cpp.toDocument.mkString(output.ColWidth)) cppGen("Cpp", llirProg, cpp.isSet, scpp.isSet, rcpp.isSet, wcpp.get) cppGen("WholeProgramCpp", mkWholeProgram, wholeCpp.isSet, sWholeCpp.isSet, rWholeCpp.isSet, wWholeCpp.get) if intl.isSet then val intr = codegen.llir.Interpreter(tl) output("\nInterpreted:") output(intr.interpret(llirProg)) catch case e: LowLevelIRError => output("Stopped due to an error during the Llir generation") ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala ================================================ package hkmc2 import scala.collection.mutable import mlscript.utils.*, shorthands.* import utils.* import hkmc2.semantics.{Elaborator, Resolver, Resolvable, Symbol, SymbolPrinter} import semantics.Elaborator.{Ctx, State} abstract class MLsDiffMaker extends DiffMaker: val invalmlOpt: Command[?] val rootPath: Str // * Absolute path to the root of the project val preludeFile: io.Path // * Contains declarations of JS builtins val predefFile: io.Path // * Contains MLscript standard library definitions val runtimeFile: io.Path = predefFile.up / "Runtime.mjs" // * Contains MLscript runtime definitions val termFile: io.Path = predefFile.up / "Term.mjs" // * Contains MLscript runtime term definitions val blockFile: io.Path = predefFile.up / "Block.mjs" // * Contains MLscript runtime block definitions val optionFile: io.Path = predefFile.up / "Option.mjs" // * Contains MLscipt runtime option definition val wd = file.up val silent = NullaryCommand("silent") val dbgElab = NullaryCommand("de") val dbgParsing = NullaryCommand("dp") val dbgResolving = NullaryCommand("dr") val dbgFlow = NullaryCommand("df") val showLocations = NullaryCommand("loc") val showParse = NullaryCommand("p") val showParsedTree = NullaryCommand("pt") val showElab = NullaryCommand("el") val showElaboratedTree = NullaryCommand("elt") val showResolve = NullaryCommand("r") val showResolvedTree = NullaryCommand("rt") val showFlows = FlagCommand(false, "sf") val showLoweredTree = NullaryCommand("lot") val ppLoweredTreeOld = NullaryCommand("slot", () => output("Option ':slot' is deprecated, use ':sir' instead.")) val showIR = NullaryCommand("sir") val checkIR = NullaryCommand("checkIR") val showOptimizedIR = NullaryCommand("soir") val showOptimizedTree = NullaryCommand("olot") val showContext = NullaryCommand("ctx") val parseOnly = NullaryCommand("parseOnly") val funcToCls = NullaryCommand("ftc") val flow = FlagCommand(false, "flow") private val flowScp: utils.Scope = utils.Scope.empty(utils.Scope.Cfg.default.copy( escapeChars = false, useSuperscripts = true, includeZero = true, )) /** * Enables Wasm support. All options in [[WasmDiffMaker]] are no-op if this option is not set. */ val wasm = NullaryCommand("wasm") // * Compiler configuration val noSanityCheck = NullaryCommand("noSanityCheck") val noFreeze = NullaryCommand("noFreeze") val noModuleCheck = NullaryCommand("noModuleCheck") val effectHandlers = Command("effectHandlers")(_.trim) val effectHandlersOptions = Set("debug", "") val stackSafe = Command("stackSafe")(_.trim) val liftDefns = NullaryCommand("lift") val importQQ = NullaryCommand("qq") val stageCode = NullaryCommand("staging") val rewriteWhile = NullaryCommand("rewriteWhile") val noInlineOpt = NullaryCommand("noInline") val inlineThreshold = Command("inlineThreshold")(_.trim.toInt) val noTailRecOpt = NullaryCommand("noTailRec") val deforest = Command("deforest")(_.trim) val patMatConsequentSharingThreshold = Command("patMatConsequentSharingThreshold")(_.trim.toInt) val deadParamElim = Command("deadParamElim")(_.trim) def mkConfig: Config = import Config.* if stackSafe.isSet && effectHandlers.isUnset then output(s"$errMarker Option ':stackSafe' requires ':effectHandlers' to be set") if !effectHandlers.get.forall(effectHandlersOptions.contains(_)) then output(s"$errMarker Option ':effectHandlers' only supports 'debug' as option") if effectHandlers.isSet then if liftDefns.isUnset then output(s"$errMarker Option ':effectHandlers' requires ':lift'") if inlineThreshold.isSet && noInlineOpt.isSet then output(s"$errMarker Option ':noInline' conflicts with option ':inlineThreshold'") Config( baseDir = wd, sanityChecks = Opt.when(noSanityCheck.isUnset)(SanityChecks(light = true)), effectHandlers = Opt.when(effectHandlers.isSet)(EffectHandlers( debug = effectHandlers.get.contains("debug"), stackSafety = stackSafe.get.flatMap: case "off" => N case value => value.toIntOption match case N => S(StackSafety.default) case S(value) => if value < 0 then failures += 1 output("/!\\ Stack limit must be positive, but the stack limit here is set to " + value) S(StackSafety.default) // Minimum: 1 for initial depth, 3 for resuming in the trampoline, 1 for function entry. // The limit needs to be strictly greater than 1 + 3 + 1 = 5. else if value < 6 then failures += 1 output("/!\\ Stack limit is too low, the minimum supported is 6.") S(StackSafety.default) else S(StackSafety(stackLimit = value)) , )), liftDefns = Opt.when(liftDefns.isSet)(LiftDefns()), patMatConsequentSharingThreshold = patMatConsequentSharingThreshold.get .orElse(Config.default.patMatConsequentSharingThreshold), stageCode = stageCode.isSet, target = if wasm.isSet then CompilationTarget.Wasm else CompilationTarget.JS, rewriteWhileLoops = rewriteWhile.isSet, tailRecOpt = !noTailRecOpt.isSet, deforest = Opt.when(deforest.isSet): Deforest( debug = true, mono = deforest.get.exists(_.contains("mono"))), inlining = Opt.when(!noInlineOpt.isSet)(Config.Inliner(inlineThreshold.get.getOrElse(1))), qqEnabled = importQQ.isSet, funcToCls = funcToCls.isSet, commentGeneratedCode = debug.isSet, noFreeze = noFreeze.isSet, noModuleCheck = noModuleCheck.isSet, deadParamElim = if deadParamElim.isUnset then S(DeadParamElim.default) else val value = deadParamElim.get.getOrElse("") val flags = value.split("\\s+").filter(_.nonEmpty).toSet val unknownFlags = flags -- Set("debug", "mono", "poly", "off") if unknownFlags.nonEmpty then output(s"$errMarker Unknown ':deadParamElim' flags: ${unknownFlags.toList.sorted.mkString(", ")}") if flags.contains("mono") && flags.contains("poly") then output(s"$errMarker ':deadParamElim' flags 'mono' and 'poly' conflict") if flags.contains("off") && (flags & Set("debug", "mono", "poly")).nonEmpty then output(s"$errMarker ':deadParamElim off' conflicts with other flags") if flags.contains("off") then N else S(DeadParamElim( debug = flags.contains("debug"), mono = !flags.contains("poly") )), ) val importCmd = Command("import"): ln => given Config = mkConfig importFile(file.up / io.RelPath(ln.trim), verbose = silent.isUnset) // eg: `:ucs desugared normalized lowered` val showUCS = Command("ucs"): ln => ln.split(" ").iterator.map(x => "ucs:" + x.trim).toSet given Elaborator.State = new Elaborator.State: override def dbg: Bool = dbgParsing.isSet || dbgElab.isSet || dbgResolving.isSet || debug.isSet protected lazy val dbgScp: utils.Scope = // for unique symbol debug-printing only Scope.empty(Scope.Cfg.default.copy( escapeChars = false, useSuperscripts = true, includeZero = true, )) val dbgPrinter: SymbolPrinter = new SymbolPrinter(dbgScp): override def preProcess(t: Product): Product = super.preProcess: case class Unexpanded(origin: Resolvable) t match case t: Resolvable if t.hasExpansion => t.expanded case t: Resolvable if dbgResolving.isSet => Unexpanded(t.duplicate.resolve) case t => t override def postProcess(t: Product): Str = super.postProcess(t) + ( if showLocations.isSet then t match case t: Located => t.toLoc.fold(" loc[none]"): loc => val (sl, _, sc) = loc.origin.fph.getLineColAt(loc.spanStart) val (el, _, ec) = loc.origin.fph.getLineColAt(loc.spanEnd) s" loc[$sl:$sc-$el:$ec]" else "" ) val etl = new TraceLogger: override def doTrace = dbgElab.isSet || scope.exists: showUCS.get.getOrElse(Set.empty).contains override def emitDbg(str: String): Unit = output(str) override def trace[T](pre: => Str, post: T => Str = noPostTrace)(thunk: => T): T = // * This override is for avoiding to increase the indentation when tracing if doTrace is false, // * so that selectively-enabled tracing doesn't get strange indentation. // * Perhaps this should be the default behavior of TraceLogger. if doTrace then super.trace(pre, post)(thunk) else thunk val rtl = new TraceLogger: override def doTrace = dbgResolving.isSet override def emitDbg(str: String): Unit = output(str) val ftl = new TraceLogger: override def doTrace = dbgFlow.isSet override def emitDbg(str: String): Unit = output(str) var curCtx = Elaborator.State.init var curICtx = Resolver.ICtx.empty /** Persistent config modification from `#config(...)` directives. */ var configModify: Config => Config = identity var prelude = Elaborator.Ctx.empty override def run(): Unit = if file =/= preludeFile then given Config = mkConfig importFile(preludeFile, verbose = false) prelude = curCtx super.run() override def init(): Unit = import syntax.* import Tree.* import Keyword.* given raise: Raise = d => output(s"Error: $d") () if file != preludeFile then val cfg = mkConfig given Config = cfg.copy( deforest = cfg.deforest.map(_.copy(debug = false)), deadParamElim = cfg.deadParamElim.map(_.copy(debug = false)) ) processTrees( PrefixApp(Keywrd(`import`), StrLit(predefFile.toString)) :: Open(Ident("Predef")) :: Nil) super.init() def importFile(file: io.Path, verbose: Bool)(using Config): Unit = // val raise: Raise = throw _ given raise: Raise = d => output(s"Error: $d") () val block = cctx.fs.read(file) val fph = new FastParseHelpers(block) val origin = Origin(file, 0, fph) val lexer = new syntax.Lexer(origin, dbg = dbgParsing.isSet) // Stupid hack to ignore diff-test directives like `:ignore` def dropCrap(ts: Ls[syntax.Stroken -> Loc]): Ls[syntax.Stroken -> Loc] = ts match case (syntax.IDENT(":", true), _) :: (syntax.IDENT(nme, false), _) :: rest => dropCrap(rest.dropWhile(_._1 isnt syntax.NEWLINE).drop(1)) case _ => ts val tokens = dropCrap(lexer.bracketedTokens) if showParse.isSet || dbgParsing.isSet then output(syntax.Lexer.printTokens(tokens)) val rules = syntax.ParseRules() val p = new syntax.Parser(origin, tokens, rules, raise, dbg = dbgParsing.isSet): def doPrintDbg(msg: => Str): Unit = if dbg then output(msg) val res = p.parseAll(p.block(allowNewlines = true)) val imprtSymbol = semantics.TopLevelSymbol("import#"+file.baseName) given Elaborator.Ctx = curCtx.nestLocal("import:"+file.baseName) val elab = Elaborator(etl, wd, Ctx.empty) try val resBlk = new syntax.Tree.Block(res) val (e, newCtx) = elab.importFrom(resBlk) val ctxWithImports = newCtx.withMembers(resBlk.definedSymbols) if verbose then output(s"Imported ${resBlk.definedSymbols.size} member(s)") curCtx = ctxWithImports processTerm(e, inImport = true) catch case err: Throwable => uncaught(err) given tl: TraceLogger with override def doTrace = debug.isSet override def emitDbg(str: String): Unit = output(str) def processOrigin(origin: Origin)(using Raise): Unit = val oldCtx = curCtx given Config = configModify(mkConfig) val lexer = new syntax.Lexer(origin, dbg = dbgParsing.isSet) val tokens = lexer.bracketedTokens if showParse.isSet || dbgParsing.isSet then output(syntax.Lexer.printTokens(tokens)) val rules = syntax.ParseRules() val p = new syntax.Parser(origin, tokens, rules, raise, dbg = dbgParsing.isSet): def doPrintDbg(msg: => Str): Unit = if dbg then output(msg) val res = p.parseAll(p.block(allowNewlines = true)) // If parsed tree is displayed, don't show the string serialization. if (parseOnly.isSet || showParse.isSet) && !showParsedTree.isSet then output(s"Parsed:${res.map("\n\t"+_.showDbg).mkString}") if showParsedTree.isSet then outputSeparator(s"Parsed tree") res.foreach(t => output(t.showAsTree)) // if showParse.isSet then // output(s"AST: $res") if parseOnly.isUnset then processTrees(res)(using summon, raise) if showContext.isSet then output("Env:") curCtx.env.foreach: (k, v) => if !(oldCtx.env contains k) then output(s" $k -> $v") private var blockNum = 0 def processTrees(trees: Ls[syntax.Tree])(using Config, Raise): Unit = val elab = Elaborator(etl, file.up, prelude) // val blockSymbol = // semantics.TopLevelSymbol("block#"+blockNum) blockNum += 1 // given Elaborator.Ctx = curCtx.nest(S(blockSymbol)) given Elaborator.Ctx = curCtx.nestLocal(s"block:${blockNum}") val blk = new syntax.Tree.Block(trees) val (e, newCtx) = elab.topLevel(blk) curCtx = newCtx // Extract SetConfig statements and update persistent config e.stats.foreach: case sc: semantics.SetConfig => val prev = configModify configModify = cfg => sc.modify(prev(cfg)) case _ => () // If elaborated tree is displayed, don't show the string serialization. if (showElab.isSet || debug.isSet) && !showElaboratedTree.isSet then output(s"Elab: ${e.showDbg}") showElaboratedTree.get.foreach: post => outputSeparator(s"Elaborated tree") output(e.showAsTree) processTerm(e, inImport = false) def processTerm(trm: semantics.Term.Blk, inImport: Bool)(using Config, Raise): Unit = given Ctx = curCtx given Config = Config.extractConfigFromStats(trm) val resolver = Resolver(rtl) curICtx = resolver.traverseBlock(trm)(using curICtx) if showResolve.isSet then output(s"Resolved: ${trm.showDbg}") showResolvedTree.get.foreach: post => outputSeparator(s"Resolved tree") output(trm.showAsTree) if flow.isSet then val floan = semantics.flow.FlowAnalysis(using ftl) val flo = floan.typeProd(trm) floan.solveConstraints() floan.expandTerms() if showFlows.isSet then import semantics.ShowCfg given ShowCfg = ShowCfg( showExpansionMappings = true, showFlowSymbols = true, debug = debug.isSet, ) outputSeparator(s"Flowed") output: import document.* doc" #{ ${trm.showTopLevel(using flowScp)} #} \nwhere #{ ${floan.showFlows(using flowScp)} #} ".mkString() ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/MainDiffMaker.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import org.scalatest.concurrent.{TimeLimitedTests, Signaler} import mlscript.utils._, shorthands._ abstract class MainDiffMaker (val rootPath: Str, val file: io.Path, val preludeFile: io.Path, val predefFile: io.Path, val relativeName: Str) extends WasmDiffMaker: // println(s"Running diff test for $relativeName") // * useful to debug nonterminating tests end MainDiffMaker ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/WasmDiffMaker.scala ================================================ package hkmc2 import mlscript.utils.*, shorthands.* import codegen.* import codegen.js.JSBuilder import codegen.Local import codegen.wasm.* import document.* import semantics.* import semantics.Elaborator import semantics.Term.Blk import text.{SessionBinding, CompiledWasmModule, WatBuilder} import Diagnostic.Source import Message.MessageContext import scala.collection.mutable abstract class WasmDiffMaker extends LlirDiffMaker: /** Outputs the compiled module as [[WasmGenerator]] implementation-defined text. */ val wat = NullaryCommand("wat") /** Outputs the compiled module as stack-based text. */ val swat = NullaryCommand("swat") /** Outputs the compiled module as folded text (i.e. S-expression). */ val fwat = NullaryCommand("fwat") private val baseScp: utils.Scope = utils.Scope.empty(utils.Scope.Cfg.default) private val wasmReplImportsNme = s"${wasmSuppNme}ReplImports" private val wasmReplImportsRef = s"globalThis.$wasmReplImportsNme" private val sessionImportsBySymbol = mutable.Map.empty[Local, mutable.LinkedHashMap[Str, SessionBinding]] private var wasmSessionInitialized = false private var wasmSessionMemPages = 0 final lazy val wasmSuppFile: io.Path = predefFile.up / "wasm" / "Wasm.mjs" final lazy val wasmSuppNme = baseScp.allocateName(Elaborator.State.wasmSymbol)(using throw _) final lazy val loadWasm: Unit = host.execute( s"const $wasmSuppNme = (await import(\"${wasmSuppFile}\")).default;", ) match case ReplHost.Result(msg) => if msg.startsWith(ReplHost.uncaughtErrorHead) then output(s"Failed to load wasm support library: $msg") case r => output(s"Failed to load wasm support library: $r") () /** Prettifies a JSON-stringified Binaryen-formatted Wat. */ lazy val prettifyBinaryenWat = (content: Str) => content.substring(2, content.length() - 2).replace("\\\\n", "\n").replace("\\\\\"", "\"") override def processIRBlock( pgrm: Program, definedValues: ComputeDefinedValues, )(using Config, Raise, Elaborator.Ctx): Unit = super.processIRBlock(pgrm, definedValues) val outerRaise: Raise = summon def computeDefinedValues(includeNonTerms: Bool) = import Elaborator.Ctx.* curCtx.env.iterator.flatMap: case (nme, e @ (_: RefElem | SelElem(base = RefElem(_: InnerSymbol)))) => e.symbol match case S(ts: TermSymbol) if ts.k.isInstanceOf[syntax.ValLike] => S((nme, ts, N)) case S(ts: BlockMemberSymbol) if includeNonTerms || ts.trmImplTree.exists(_.k.isInstanceOf[syntax.ValLike]) => S((nme, ts, N)) case S(vs: VarSymbol) => S((nme, vs, N)) case _ => N case _ => N .toList val symbolsToPreserve = computeDefinedValues(includeNonTerms = true).iterator.map(_._2).toSet if wasm.isSet then val reportedMessages = mutable.Set.empty[Str] loadWasm var errored = false given Raise = case d @ ErrorReport(source = Source.Compilation) => errored = true outerRaise(d) case d => outerRaise(d) val sessionImportSymbols = mutable.LinkedHashSet.from(pgrm.main.freeVars) new BlockTraverser: override def applyPath(p: Path): Unit = p match case sel: Select => sel.symbol.foreach: case sym: ModuleOrObjectSymbol => sessionImportSymbols += sym case _ => () super.applyPath(sel) case _ => super.applyPath(p) .applyBlock(pgrm.main) val sessionImports = mutable.LinkedHashMap.empty[Str, SessionBinding] sessionImportSymbols.iterator.foreach: sym => sessionImportsBySymbol.get(sym).foreach: bindings => bindings.foreach: (bindingKey, binding) => sessionImports.update(bindingKey, binding) val CompiledWasmModule(modWat, mainFnNme, systemMemMinPages, sessionExports) = ltl.givenIn: WatBuilder().program(pgrm, N, wd, sessionImports.values.toSeq, symbolsToPreserve) val modWatJsLit = JSBuilder.makeStringLiteral(modWat.mkString(output.ColWidth)) if wat.isSet then output("Wat:") output(modWat.mkString(output.ColWidth)) // A program with errors may have a WAT that is worth inspecting, but anything that involves // using Binaryen requires a valid WAT if errored then return if fwat.isSet then output("Formatted Wat (Folded):") doc"JSON.stringify(wasm.binaryenFmtWat($modWatJsLit, true));" .stripBreaks .mkString(output.ColWidth) .replace('\n', ' ') |> host.execute match case ReplHost.Result(content) => output(prettifyBinaryenWat(content)) case err => output(s"Error: $err") return if swat.isSet then output("Formatted Wat (Stack):") doc"JSON.stringify(wasm.binaryenFmtWat($modWatJsLit, false));" .stripBreaks .mkString(output.ColWidth) .replace('\n', ' ') |> host.execute match case ReplHost.Result(content) => output(prettifyBinaryenWat(content)) case err => output(s"Error: $err") return def mkQuery(preStr: Str, jsStr: Str)(k: Str => Unit) = val queryStr = jsStr.replaceAll("\n", " ") val (reply, stderr) = host.query( preStr, queryStr, !expectRuntimeOrCodeGenErrors && !expectErrors, ) reply match case ReplHost.Result(content) => k(content) case ReplHost.Empty => case ReplHost.Unexecuted(message) => ??? case ReplHost.Error(isSyntaxError, message, otherOutputs) => if otherOutputs.nonEmpty then otherOutputs.splitSane('\n').foreach: line => output(s"> ${line}") if isSyntaxError then // If there is a syntax error in the generated code, // it should be a code generation error. raise(ErrorReport( msg"[Uncaught SyntaxError] ${message}" -> N :: Nil, source = Diagnostic.Source.Compilation, )) else // Otherwise, it is considered a simple runtime error. raise(ErrorReport( msg"${message}" -> N :: Nil, source = Diagnostic.Source.Runtime, )) end match if stderr.nonEmpty then output(s"// Standard Error:\n${stderr}") end mkQuery if !wasmSessionInitialized then val intrinsicWatJsLit = JSBuilder.makeStringLiteral( ltl.givenIn: baseScp.nest.givenIn: WatBuilder().intrinsicSupportModule().mkString(output.ColWidth), ) host.execute( doc"""await (async () => { # const mem = new WebAssembly.Memory({ initial: ${systemMemMinPages} }); # const decodeUtf16 = new TextDecoder("utf-16le"); # const system = { # mem, # mlx_str_from_utf16: (ptr, byteLen) => # decodeUtf16.decode(new Uint8Array(mem.buffer, ptr, byteLen)), # }; # const intrinsicModule = await $wasmSuppNme.binaryenCompileToModule($intrinsicWatJsLit, {}); # Object.assign(system, intrinsicModule.instance.exports); # $wasmReplImportsRef = { # repl: Object.create(null), # system, # }; # })();""" .stripBreaks .mkString(output.ColWidth), ) match case ReplHost.Result(_) => wasmSessionInitialized = true wasmSessionMemPages = systemMemMinPages case r => output(s"Failed to initialize wasm REPL session object: $r") end match else if systemMemMinPages > wasmSessionMemPages then host.execute( doc"""(() => { # const extraPages = ${systemMemMinPages - wasmSessionMemPages}; # $wasmReplImportsRef.system.mem.grow(extraPages); # })();""" .stripBreaks .mkString(output.ColWidth), ) match case ReplHost.Result(_) => wasmSessionMemPages = systemMemMinPages case r => output(s"Failed to grow wasm REPL session memory: $r") end if val exportAssignments = sessionExports.flatMap(_.exportNameOpt.toSeq).map: exportName => s"""$wasmReplImportsRef.repl["$exportName"] = exports["$exportName"];""" val jsBody = if exportAssignments.nonEmpty then s"""const result = exports["$mainFnNme"](); ${exportAssignments.mkString(" ")} return result;""" else s"""return exports["$mainFnNme"]();""" val jsStr = s"""await wasm.binaryenPrintFuncRes($modWatJsLit, $wasmReplImportsRef, exports => { $jsBody });""" output("Wasm result:") mkQuery("", jsStr): out => // Omit the last line which is always "undefined" or the unit. val result = out.lastIndexOf('\n') match case n if n >= 0 => out.substring(0, n) case _ => "" sessionExports.foreach: binding => binding.bindingSyms.foreach: sym => sessionImportsBySymbol .getOrElseUpdate(sym, mutable.LinkedHashMap.empty) .update(binding.bindingKey, binding) output(s"= $result") end if end processIRBlock end WasmDiffMaker ================================================ FILE: hkmc2DiffTests/src/test/scala/hkmc2/Watcher.scala ================================================ package hkmc2 import scala.collection.mutable import scala.jdk.CollectionConverters.* import mlscript.utils.*, shorthands.* import better.files.* import _root_.io.methvin import methvin.better.files.* import methvin.watcher.{DirectoryWatcher, PathUtils, DirectoryChangeEvent, DirectoryChangeListener} import methvin.watcher.hashing.{FileHash, FileHasher} import java.time.LocalDateTime import java.time.temporal._ import io.FileSystem, io.PlatformPath.given // Note: when SBT's `fork` is set to `false`, the path should be `File("hkmc2/")` instead... // * Only the first path can contain tests. The other paths are only watched for source changes. object MainWatcher extends Watcher( File("../hkmc2/shared/src") :: File("./src") :: // File("../hkmc2Benchmarks/src") :: Nil ): def main(args: Array[String]): Unit = run class Watcher(dirs: Ls[File]): val dirPaths = dirs.map(d => os.Path(d.pathAsString)) dirs.foreach: dir => println((fansi.Color.Blue("Watching directory ") ++ fansi.Color.DarkGray(dir.toString)).toString) val fileHashes = mutable.Map.empty[File, FileHash] val completionTime = mutable.Map.empty[File, LocalDateTime] val fileHasher = FileHasher.DEFAULT_FILE_HASHER given cctx: CompilerCtx = CompilerCtx.fresh(FileSystem.default) val watcher: DirectoryWatcher = DirectoryWatcher.builder() .logger(org.slf4j.helpers.NOPLogger.NOP_LOGGER) .paths(dirs.map(_.toJava.toPath).asJava) .fileHashing(false) // so that simple save events trigger processing eve if there's no file change .listener(new DirectoryChangeListener { def onEvent(event: DirectoryChangeEvent): Unit = try // println(event) val hash = PathUtils.hash(fileHasher, event.path) val file = File(event.path) val old = fileHashes.get(event.path) fileHashes(event.path) = hash old match case S(existingHash) => if existingHash === hash then // if file.extension =/= S(".cmd") then return // else val newTime = LocalDateTime.now() completionTime.get(event.path) match case S(time) => val diff = time.until(newTime, ChronoUnit.SECONDS) if diff <= 1 then // println(s"Debounced $time -> $newTime = $diff s") return case N => System.err.println("It seems the previous completion time was not recorded") return case N => import java.nio.file.StandardWatchEventKinds import java.nio.file.WatchEvent import java.nio.file.Path val et = event.eventType val count = event.count et match case DirectoryChangeEvent.EventType.OVERFLOW => ??? case _ => et.getWatchEventKind.asInstanceOf[WatchEvent.Kind[Path]] match case StandardWatchEventKinds.ENTRY_CREATE => onCreate(file, count) case StandardWatchEventKinds.ENTRY_MODIFY => onModify(file, count) case StandardWatchEventKinds.ENTRY_DELETE => onDelete(file, count) completionTime(event.path) = LocalDateTime.now() catch ex => // System.err.println("Unexpected error in watcher: " + ex) // ex.printStackTrace() System.err.println("Unexpected error in watcher (" + ex.getClass() + ")") watcher.close() throw ex }) .build(); def run: Unit = try watcher.watch() finally watcher.close() def go(file: File) = // println(s"go $file") val isMls = file.toString.endsWith(".mls") if file.toString.endsWith(".scala") then watcher.close() else if isMls || file.toString.endsWith(".cmd") then Thread.sleep(100) val path = os.Path(file.pathAsString) val basePath = path.segments.drop(dirPaths.head.segmentCount).toList.init val relativeName = basePath.map(_ + "/").mkString + path.baseName val rootPath = os.pwd/os.up val testBasePath = rootPath/"hkmc2"/"shared"/"src"/"test" val preludePath = testBasePath/"mlscript"/"decls"/"Prelude.mls" val predefPath = testBasePath/"mlscript-compile"/"Predef.mls" val isModuleFile = path.segments.contains("mlscript-compile") if isModuleFile then given Config = Config.default(testBasePath) MLsCompiler( paths = new MLsCompiler.Paths: val preludeFile = preludePath val runtimeFile = testBasePath/"mlscript-compile"/"Runtime.mjs" val termFile = testBasePath/"mlscript-compile"/"Term.mjs", mkRaise = ReportFormatter(System.out.println, colorize = true).mkRaise ).compileModule(path) else val dm = new MainDiffMaker(rootPath.toString, path, preludePath, predefPath, relativeName): def cctx = Watcher.this.cctx override def unhandled(blockLineNum: Int, exc: Throwable): Unit = exc.printStackTrace() super.unhandled(blockLineNum, exc) dm.run() def show(file: File) = fansi.Color.Yellow: file.toString.stripPrefix(dirs.head.toString) def pre = fansi.Color.Blue(">> ").toString def onCreate(file: File, count: Int) = println(pre + show(file).toString + fansi.Color.Blue(" created")) go(file) def onModify(file: File, count: Int) = println(pre + show(file).toString + fansi.Color.Blue(s" modified $count times")) go(file) def onDelete(file: File, count: Int) = println(pre + show(file).toString + fansi.Color.Blue(" deleted")) // go(file) ================================================ FILE: hkmc2NofibTests/src/test/scala/hkmc2/NofibCompileTestRunner.scala ================================================ package hkmc2 import mlscript.utils._, shorthands._ import io.PlatformPath.given class NofibCompileTestRunner extends CompileTestRunnerBase( compileDirs = TestFolders.nofibCompileDirs(os.pwd), ): protected def cctx: CompilerCtx = NofibCompileTestRunner.cctx end NofibCompileTestRunner object NofibCompileTestRunner: given cctx: CompilerCtx = CompilerCtx.fresh(io.FileSystem.default) end NofibCompileTestRunner ================================================ FILE: hkmc2NofibTests/src/test/scala/hkmc2/NofibDiffTestRunner.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import mlscript.utils._ import os.Path import io.PlatformPath.given object NofibDiffTestState extends DiffTestRunner.State: override def testDir = TestFolders.nofibDiffDir(workingDir) class NofibDiffTestRunner extends DiffTestRunnerBase(NofibDiffTestState) with ParallelTestExecution ================================================ FILE: hkmc2WasmTests/src/test/scala/hkmc2/WasmCompileTestRunner.scala ================================================ package hkmc2 import mlscript.utils._, shorthands._ import io.PlatformPath.given class WasmCompileTestRunner extends CompileTestRunnerBase( compileDirs = TestFolders.wasmCompileDirs(os.pwd), ): protected def cctx: CompilerCtx = WasmCompileTestRunner.cctx end WasmCompileTestRunner object WasmCompileTestRunner: given cctx: CompilerCtx = CompilerCtx.fresh(io.FileSystem.default) end WasmCompileTestRunner ================================================ FILE: hkmc2WasmTests/src/test/scala/hkmc2/WasmDiffTestRunner.scala ================================================ package hkmc2 import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import mlscript.utils._ import os.Path import io.PlatformPath.given object WasmDiffTestState extends DiffTestRunner.State: override def testDir = TestFolders.wasmDiffDir(workingDir) class WasmDiffTestRunner extends DiffTestRunnerBase(WasmDiffTestState) with ParallelTestExecution ================================================ FILE: index.css ================================================ body { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; background-color: #eee8d5; max-width: 1200px; margin: 0px auto; padding: 5px; color: #073642; --keyword-color: #93a1a1; --name-color: LightGreen; --type-color: LightBlue; --no-value-color: Gray; --panel-background: #002b36; } h1, p{ padding: 0px 10px; } #content { width: 100%; padding: 0px; height: 750px; display: grid; grid-template-columns: 50% 50%; grid-template-rows: 100%; grid-gap: 1.5rem; } .panel { padding: 1rem; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; width: 100%; height: 100%; box-sizing: border-box; border-radius: 10px; background: var(--panel-background); /* color: #93a1a1; */ /* color: #cacaca; */ color: #efefef; font-size: 14px; overflow: auto; } #mlscript-input { resize: none; outline: none; } #mlscript-output { width: 130%; } #mlscript-output > table { border-collapse: collapse; } #mlscript-output > table > thead { font-weight: bold; } #mlscript-output td { padding: 0.25rem 0.5rem; border: 1px solid white; } #mlscript-output td.name { color: var(--name-color); } #mlscript-output td.type { color: var(--type-color); } #mlscript-output td.no-value { color: var(--no-value-color); } ================================================ FILE: index.html ================================================ MLscript demonstration

MLscript online demonstration


The code is available on github.


Note: JavaScript code generation (used to evaluate the result) is not perfect yet and has rough edges. Also, try typing `window.alert of "Hello!"` and see what happens!

================================================ FILE: local_testing.html ================================================ MLscript demonstration

MLscript demonstration



================================================ FILE: out/apps/.gitkeep ================================================ ================================================ FILE: out/apps/AccountingTest.md ================================================ # Accounting |Year|L1|L2| |---|--:|--:| |Initial|200.0|1000.0| |Y1|166.1|333.3| |Y2|156.1|133.3| |Y3|146.1|3.3| > **❗️** Unspent balance of L1: `146.1` ### Remaining Available Funds |Summary| | |---|--:| |Matchable|149.5| |Non-matchable|0.0| ================================================ FILE: out/test.md ================================================ whoops ================================================ FILE: package.json ================================================ { "name": "mlscript", "dependencies": { "benchmark": "^2.1.4", "binaryen": "^129.0.0", "typescript": "^4.7.4" } } ================================================ FILE: project/build.properties ================================================ sbt.version=1.12.8 ================================================ FILE: project/plugins.sbt ================================================ addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.5.6") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.2") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") ================================================ FILE: shared/src/main/scala/mlscript/ConstraintSolver.scala ================================================ package mlscript import scala.collection.mutable.{Map => MutMap, SortedMap => MutSortMap, Set => MutSet, Stack => MutStack, Buffer} import scala.collection.immutable.{SortedSet, SortedMap} import scala.util.chaining._ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ class ConstraintSolver extends NormalForms { self: Typer => def stopConstrainingOnFirstFailure: Bool = false def verboseConstraintProvenanceHints: Bool = verbose def defaultStartingFuel: Int = // 5000 10000 // necessary for fat definitions in OCamlList.mls var startingFuel: Int = defaultStartingFuel def depthLimit: Int = // 150 // 200 250 type ExtrCtx = MutSortMap[TV, Buffer[(Bool, ST)]] // tv, is-lower, bound protected var currentConstrainingRun = 0 private def noSuchMember(info: DelayedTypeInfo, fld: Var): Diagnostic = ErrorReport( msg"${info.decl.kind.str.capitalize} `${info.decl.name}` does not contain member `${fld.name}`" -> fld.toLoc :: Nil, newDefs) def lookupMember(clsNme: Str, rfnt: Var => Opt[FieldType], fld: Var) (implicit ctx: Ctx, raise: Raise) : Either[Diagnostic, NuMember] = { val info = ctx.tyDefs2.getOrElse(clsNme, ???/*TODO*/) if (info.isComputing) { ??? // TODO support? } else info.complete() match { case cls: TypedNuCls => cls.members.get(fld.name) match { case S(m) => R(m) case N => L(noSuchMember(info, fld)) } case _ => ??? // TODO } } /** Note: `mkType` is just for reporting errors. */ def lookupField(mkType: () => ST, clsNme: Opt[Str], rfnt: Var => Opt[FieldType], tags: SortedSet[AbstractTag], _fld: Var) (implicit ctx: Ctx, raise: Raise) : FieldType = trace(s"Looking up field ${_fld.name} in $clsNme & ${tags} & {...}") { // * Field selections with field names starting with `#` are a typer hack to access private members. val (fld, allowPrivateAccess) = if (_fld.name.startsWith("#")) (Var(_fld.name.tail).withLocOf(_fld), true) else (_fld, false) val fromRft = rfnt(fld) var foundRec: Opt[Diagnostic] = N def getFieldType(info: DelayedTypeInfo): Opt[FieldType] = { // * The raw type of this member, with original references to the class' type variables/type parameters val raw = (if (info.isComputed) N else info.typedFields.get(fld)) match { case S(fty) => if (info.privateParams.contains(fld) && !allowPrivateAccess) err(msg"Parameter '${fld.name}' cannot be accessed as a field" -> fld.toLoc :: Nil) S(fty) case N if info.isComputing => if (info.allFields.contains(fld)) // TODO don't report this if the field can be found somewhere else! foundRec = S(ErrorReport( msg"Indirectly-recursive member should have a type signature" -> fld.toLoc :: Nil, newDefs)) N case N => def handle(virtualMembers: Map[Str, NuMember]): Opt[FieldType] = virtualMembers.get(fld.name) match { case S(d: TypedNuFun) => if (d.fd.isLetOrLetRec) err(msg"Let binding '${d.name}' cannot tbe accessed as a field" -> fld.toLoc :: msg"Use a `val` declaration to make it a field" -> d.fd.toLoc :: Nil) val ty = d.typeSignature S( if (d.fd.isMut) FieldType(S(ty), ty)(d.prov) else ty.toUpper(d.prov) ) case S(p: NuParam) => if (!allowPrivateAccess && !p.isPublic) err(msg"Parameter '${p.nme.name}' cannot be accessed as a field" -> fld.toLoc :: msg"Either make the parameter a `val` or access it through destructuring" -> p.nme.toLoc :: Nil) S(p.ty) case S(m) => S(err(msg"Access to ${m.kind.str} member not yet supported", fld.toLoc).toUpper(noProv)) case N => N } info.complete() match { case cls: TypedNuCls => handle(cls.virtualMembers) case trt: TypedNuTrt => handle(trt.virtualMembers) case mxn: TypedNuMxn => handle(mxn.virtualMembers) case TypedNuDummy(d) => N case _ => ??? // TODO } } println(s"Lookup ${info.decl.name}.${fld.name} : $raw where ${raw.fold("")(_.ub.showBounds)}") val freshenedRaw = raw.map { raw => implicit val freshened: MutMap[TV, ST] = MutMap.empty implicit val shadows: Shadows = Shadows.empty info.tparams.foreach { case (tn, _tv, vi) => val targ = rfnt(Var(info.decl.name + "#" + tn.name)) match { // * TODO to avoid infinite recursion due to ever-expanding type args, // * we should set the shadows of the targ to be the same as that of the parameter it replaces... case S(fty) if vi === S(VarianceInfo.co) => fty.ub case S(fty) if vi === S(VarianceInfo.contra) => fty.lb.getOrElse(BotType) case S(fty) => TypeBounds.mk( fty.lb.getOrElse(BotType), fty.ub, ) case N => TypeBounds( // _tv.lowerBounds.foldLeft(BotType: ST)(_ | _), // _tv.upperBounds.foldLeft(TopType: ST)(_ & _), _tv.lowerBounds.foldLeft( Extruded(true, SkolemTag(_tv)(provTODO))(provTODO, Nil): ST // ^ TODO provide extrusion reason? )(_ | _), _tv.upperBounds.foldLeft( Extruded(false, SkolemTag(_tv)(provTODO))(provTODO, Nil): ST // ^ TODO provide extrusion reason? )(_ & _), )(_tv.prov) } freshened += _tv -> targ } raw.freshenAbove(info.level, rigidify = false) } println(s"Fresh[${info.level}] ${info.decl.name}.${fld.name} : $freshenedRaw where ${freshenedRaw.map(_.ub.showBounds)}") freshenedRaw } val fromCls = clsNme.flatMap(clsNme => getFieldType(ctx.tyDefs2(clsNme))) val fromTrts = tags.toList.collect { case TraitTag(nme, iht) => getFieldType(ctx.tyDefs2(nme.name)) }.flatten val fields = fromRft.toList ::: fromCls.toList ::: fromTrts println(s" & ${fromRft} (from refinement)") fields match { case x :: xs => xs.foldRight(x)(_ && _) case Nil => foundRec match { case S(d) => err(d).toBoth(noProv) case N => err(msg"Type `${mkType().expPos}` does not contain member `${fld.name}`" -> fld.toLoc :: Nil).toBoth(noProv) } } }() // * Each type has a shadow which identifies all variables created from copying // * variables that existed at the start of constraining. // * The intent is to make the total number of shadows in a given constraint // * resolution run finite, so we can avoid divergence with a "cyclic-lookign constraint" error. type ShadowSet = Set[ST -> ST] case class Shadows(current: ShadowSet, previous: ShadowSet) { def size: Int = current.size + previous.size } object Shadows { val empty: Shadows = Shadows(Set.empty, Set.empty) } /** Constrains the types to enforce a subtyping relationship `lhs` <: `rhs`. */ def constrain(lhs: SimpleType, rhs: SimpleType) (implicit raise: Raise, prov: TypeProvenance, ctx: Ctx, shadows: Shadows = Shadows.empty) : Unit = { currentConstrainingRun += 1 if (stopConstrainingOnFirstFailure) constrainImpl(lhs, rhs)(err => { raise(err) return() }, prov, ctx, shadows) else constrainImpl(lhs, rhs) } def constrainImpl(lhs: SimpleType, rhs: SimpleType) (implicit raise: Raise, prov: TypeProvenance, ctx: Ctx, shadows: Shadows) : Unit = { val outerCtx = ctx ; { val ctx = () // We need a cache to remember the subtyping tests in process; we also make the cache remember // past subtyping tests for performance reasons (it reduces the complexity of the algoritghm): val cache: MutSet[(SimpleType, SimpleType)] = MutSet.empty val startingFuel = self.startingFuel var fuel = startingFuel val stack = MutStack.empty[ST -> ST] println(s"CONSTRAIN $lhs Ls[SimpleType] val abort = () => return def abbreviate(msgs: Ls[Message -> Opt[Loc]]) = { val treshold = 15 if (msgs.sizeCompare(treshold) <= 0) msgs else msgs.take(treshold) ::: msg"......" -> N :: msg"......" -> N :: msgs.reverseIterator.take(treshold).toList.reverse } def consumeFuel()(implicit cctx: ConCtx, ctx: Ctx) = { def msgHead = msg"Subtyping constraint of the form `${lhs.expPos} <: ${rhs.expNeg}`" if (stack.sizeIs > depthLimit) { err( msg"$msgHead exceeded recursion depth limit (${depthLimit.toString})" -> prov.loco :: ( if (!explainErrors) msg"Note: use flag `:ex` to see internal error info." -> N :: Nil else if (verbose) stack.toList.filterOutConsecutive().flatMap { case (l, r) => msg"while constraining: ${s"$l"}" -> l.prov.loco :: msg" r.prov.loco :: Nil } else abbreviate(stack.toList.filterOutConsecutive().map(c => msg"while constraining: ${s"${c._1} N)) ) ) abort() } else if (fuel <= 0) { err( msg"$msgHead took too many steps and ran out of fuel (${startingFuel.toString})" -> prov.loco :: ( if (!explainErrors) msg"Note: use flag `:ex` to see internal error info." -> N :: Nil else cctx._1.map(c => msg" + ${s"$c"}" -> c.prov.loco) ::: cctx._2.map(c => msg" - ${s"$c"}" -> c.prov.loco)) ) abort() } else fuel -= 1 } def mkCase[A](str: Str)(k: Str => A)(implicit dbgHelp: Str): A = { val newStr = dbgHelp + "." + str println(newStr) k(newStr) } /** This is used in the context of constrained types. * We "unstash" all constraints that were currently stashed/saved for later * due to a polymorphism level mismatch. */ def unstash(oldCtx: Ctx)(implicit ctx: Ctx, cctx: ConCtx, prevCctxs: Ls[ConCtx]): Unit = { val ec = ctx.extrCtx trace(s"UNSTASHING...") { implicit val ctx: Ctx = oldCtx ec.foreach { case (tv, bs) => println(s"where($tv) ${tv.showBounds}") bs.foreach { case (true, b) => println(s"UNSTASH ${b} <: $tv where ${b.showBounds}"); rec(b, tv, false) case (false, b) => println(s"UNSTASH ${tv} <: $b where ${b.showBounds}"); rec(tv, b, false) }} ec.clear() }() } // ensuring ctx.extrCtx.isEmpty /* To solve constraints that are more tricky. */ def goToWork(lhs: ST, rhs: ST)(implicit cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows): Unit = { val lhsDNF = DNF.mkDeep(MaxLevel, Nil, lhs, true) val rhsDNF = DNF.mkDeep(MaxLevel, Nil, rhs, false) val oldCtx = ctx if (!rhsDNF.isPolymorphic) { constrainDNF(lhsDNF, rhsDNF) } else ctx.nextLevel { implicit ctx => implicit val state: MutMap[TV, ST] = MutMap.empty val rigid = DNF(MaxLevel, rhsDNF.cons.mapKeys(_.freshenAbove(rhsDNF.polymLevel, rigidify = true)), rhsDNF.cs.map(_.freshenAbove(rhsDNF.polymLevel, rigidify = true)), ) println(s"DNF BUMP TO LEVEL ${lvl} --> $rigid") // println(s"where ${rigid.showBounds}") constrainDNF(lhsDNF, rigid) unstash(oldCtx) } } def constrainDNF(_lhs: DNF, rhs: DNF)(implicit cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows): Unit = trace(s"${lvl}. ARGH ${_lhs} rec(c._1, c._2, false)) }() // * Same remark as in the `rec` method [note:1] // assert(lvl >= rhs.level) lhsCs.foreach { case Conjunct(lnf, vars, rnf, nvars) => def local(): Unit = { // * Used to return early in simple cases vars.headOption match { case S(v) => rec(v, rhs.toType() | Conjunct(lnf, vars - v, rnf, nvars).toType().neg(), true) case N => implicit val etf: ExpandTupleFields = true val fullRhs = nvars.iterator.map(DNF.mkDeep(MaxLevel, Nil, _, true)) .foldLeft(rhs | DNF.mkDeep(MaxLevel, Nil, rnf.toType(), false))(_ | _) println(s"Consider ${lnf} <: ${fullRhs}") // The following crutch is necessary because the pesky Without types may get stuck // on type variables and type variable negations: lnf match { case LhsRefined(S(Without(NegType(tv: TV), ns)), tts, rcd, trs) => return rec(( fullRhs.toType() | LhsRefined(N, tts, rcd, trs).toType().neg()).without(ns).neg(), tv, true) case LhsRefined(S(Without(b, ns)), tts, rcd, trs) => assert(b.isInstanceOf[TV]) return rec(b, ( fullRhs.toType() | LhsRefined(N, tts, rcd, trs).toType().neg()).without(ns), true) case _ => () } // First, we filter out those RHS alternatives that obviously don't match our LHS: val possible = fullRhs.cs.filter { r => // Note that without this subtyping check, // the number of constraints in the `eval1_ty_ugly = eval1_ty` // ExprProb subsumption test case explodes. if ((r.rnf is RhsBot) && r.vars.isEmpty && r.nvars.isEmpty && lnf <:< r.lnf) { println(s"OK $lnf <: $r") return () } // println(s"Possible? $r ${lnf & r.lnf}") !vars.exists(r.nvars) && ((lnf & (r.lnf, pol = false))(ctx, etf = false)).isDefined && ((lnf, r.rnf) match { case (LhsRefined(_, ttags, _, _), RhsBases(objTags, rest, trs)) if objTags.exists { case t: TraitTag => ttags(t); case _ => false } => false case (LhsRefined(S(ot: ClassTag), _, _, _), RhsBases(objTags, rest, trs)) => !objTags.contains(ot) case _ => true }) } println(s"Possible: " + possible) if (doFactorize) { // We try to factorize the RHS to help make subsequent solving take shortcuts: val fact = factorize(possible, sort = false) println(s"Factorized: " + fact) // Finally, we enter the "annoying constraint" resolution routine: annoying(Nil, lnf, fact, RhsBot) } else { // Alternatively, without factorization (does not actually make a difference most of the time): annoying(Nil, lnf, possible.map(_.toType()), RhsBot) } } } local() } }() /* Solve annoying constraints, which are those that involve either unions and intersections at the wrong polarities or negations. This works by constructing all pairs of "conjunct <: disjunct" implied by the conceptual "DNF <: CNF" form of the constraint. */ def annoying(ls: Ls[SimpleType], done_ls: LhsNf, rs: Ls[SimpleType], done_rs: RhsNf) (implicit cctx: ConCtx, prevCctxs: Ls[ConCtx], shadows: Shadows, ctx: Ctx, dbgHelp: Str = "Case") : Unit = { annoyingCalls += 1 consumeFuel() annoyingImpl(ls, done_ls, rs, done_rs) } // TODO improve by moving things to the right side *before* branching out in the search! def annoyingImpl(ls: Ls[SimpleType], done_ls: LhsNf, rs: Ls[SimpleType], done_rs: RhsNf) (implicit cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows, dbgHelp: Str = "Case") : Unit = trace(s"${lvl}. A $done_ls % $ls rec(tv, mkRhs(ls), done_ls.isTop && ls.forall(_.isTop)) case (_, (tv: TypeVariable) :: rs) => rec(mkLhs(rs), tv, done_rs.isBot && rs.forall(_.isBot)) case (TypeBounds(lb, ub) :: ls, _) => annoying(ub :: ls, done_ls, rs, done_rs) case (_, TypeBounds(lb, ub) :: rs) => annoying(ls, done_ls, lb :: rs, done_rs) case (ComposedType(true, ll, lr) :: ls, _) => mkCase("1"){ implicit dbgHelp => annoying(ll :: ls, done_ls, rs, done_rs) } mkCase("2"){ implicit dbgHelp => annoying(lr :: ls, done_ls, rs, done_rs) } case (_, ComposedType(false, rl, rr) :: rs) => mkCase("1"){ implicit dbgHelp => annoying(ls, done_ls, rl :: rs, done_rs) } mkCase("2"){ implicit dbgHelp => annoying(ls, done_ls, rr :: rs, done_rs) } case (_, ComposedType(true, rl, rr) :: rs) => annoying(ls, done_ls, rl :: rr :: rs, done_rs) case (ComposedType(false, ll, lr) :: ls, _) => annoying(ll :: lr :: ls, done_ls, rs, done_rs) case (p @ ProxyType(und) :: ls, _) => annoying(und :: ls, done_ls, rs, done_rs) case (_, p @ ProxyType(und) :: rs) => annoying(ls, done_ls, und :: rs, done_rs) // ^ TODO retain the proxy provs wrapping each ConstructedType... for better errors later on? case (n @ NegType(ty) :: ls, _) => annoying(ls, done_ls, ty :: rs, done_rs) case (_, n @ NegType(ty) :: rs) => annoying(ty :: ls, done_ls, rs, done_rs) case (ExtrType(true) :: ls, rs) => () // Bot in the LHS intersection makes the constraint trivial case (ls, ExtrType(false) :: rs) => () // Top in the RHS union makes the constraint trivial case (ExtrType(false) :: ls, rs) => annoying(ls, done_ls, rs, done_rs) case (ls, ExtrType(true) :: rs) => annoying(ls, done_ls, rs, done_rs) // case ((tr @ TypeRef(_, _)) :: ls, rs) => annoying(tr.expand :: ls, done_ls, rs, done_rs) case ((tr @ TypeRef(_, _)) :: ls, rs) => annoying(ls, (done_ls & (tr, pol = true)) getOrElse (return println(s"OK $done_ls & $tr =:= ${BotType}")), rs, done_rs) // case (ls, (tr @ TypeRef(_, _)) :: rs) => annoying(ls, done_ls, tr.expand :: rs, done_rs) case (ls, (tr @ TypeRef(_, _)) :: rs) => annoying(ls, done_ls, rs, done_rs | (tr, pol = false) getOrElse (return println(s"OK $done_rs & $tr =:= ${TopType}"))) /* // This logic is now in `constrainDNF`... and even if we got here, // the new BaseType `&` implementation can now deal with these. case (Without(b: TypeVariable, ns) :: ls, rs) => rec(b, mkRhs(ls).without(ns)) case (Without(NegType(b: TypeVariable), ns) :: ls, rs) => rec(b, NegType(mkRhs(ls).without(ns))(noProv)) case ((w @ Without(_, _)) :: ls, rs) => lastWords(s"`pushPosWithout` should have removed Without type: ${w}") case (ls, (w @ Without(_, _)) :: rs) => lastWords(s"unexpected Without in negative position not at the top level: ${w}") */ case ((l: BaseTypeOrTag) :: ls, rs) => annoying(ls, (done_ls & (l, pol = true))(ctx, etf = true) getOrElse (return println(s"OK $done_ls & $l =:= ${BotType}")), rs, done_rs) case (ls, (r: BaseTypeOrTag) :: rs) => annoying(ls, done_ls, rs, done_rs | r getOrElse (return println(s"OK $done_rs | $r =:= ${TopType}"))) case ((l: RecordType) :: ls, rs) => annoying(ls, done_ls & l, rs, done_rs) case (ls, (r @ RecordType(Nil)) :: rs) => () case (ls, (r @ RecordType(f :: Nil)) :: rs) => annoying(ls, done_ls, rs, done_rs | f getOrElse (return println(s"OK $done_rs | $f =:= ${TopType}"))) case (ls, (r @ RecordType(fs)) :: rs) => annoying(ls, done_ls, r.toInter :: rs, done_rs) // TODO statically prevent these cases by refining `annoyingImpl`'s parameter types case (_, (_: PolymorphicType) :: _) | ((_: PolymorphicType) :: _, _) => die case (_, (_: ConstrainedType) :: _) | ((_: ConstrainedType) :: _, _) => die case (Nil, Nil) => // TODO improve: // Most of the `rec` calls below will yield ugly errors because we don't maintain // the original constraining context! (done_ls, done_rs) match { case (LhsRefined(S(Without(b, _)), _, _, _), RhsBot) => rec(b, BotType, true) case (LhsTop, _) | (LhsRefined(N, EmptyColl(), RecordType(Nil), EmptyColl()), _) => // TODO ^ actually get rid of LhsTop and RhsBot...? (might make constraint solving slower) reportError() case (LhsRefined(_, ts, _, trs), RhsBases(pts, _, _)) if ts.exists(pts.contains) => () case (LhsRefined(bo, ts, r, trs), _) if trs.nonEmpty => annoying(trs.valuesIterator.map { tr => tr.expand }.toList, LhsRefined(bo, ts, r, SortedMap.empty), Nil, done_rs) case (_, RhsBases(pts, bf, trs)) if trs.nonEmpty => annoying(Nil, done_ls, trs.valuesIterator.map(_.expand).toList, RhsBases(pts, bf, SortedMap.empty)) case (_, RhsBases(pts, S(L(ov: Overload)), trs)) => ov.alts.foreach(alt => annoying(Nil, done_ls, Nil, RhsBases(pts, S(L(alt)), trs))) // From this point on, trs should be empty! case (LhsRefined(_, _, _, trs), _) if trs.nonEmpty => die case (_, RhsBases(_, _, trs)) if trs.nonEmpty => die case (_, RhsBot) | (_, RhsBases(Nil, N, _)) => reportError() case (LhsRefined(S(f0@FunctionType(l0, r0)), ts, r, _) , RhsBases(_, S(L(f1@FunctionType(l1, r1))), _)) => rec(f0, f1, true) case (LhsRefined(S(f: FunctionType), ts, r, trs), RhsBases(pts, _, _)) => annoying(Nil, LhsRefined(N, ts, r, trs), Nil, done_rs) // * Note: We could avoid the need for this rule by adding `Eql` to *all* class tag parent sets, // * but I chose not to for performance reasons (better keep parent sets small). case (LhsRefined(S(ct: ClassTag), ts, r, trs0), RhsBases(ots, _, trs)) if EqlTag in ots => println(s"OK ~ magic Eql ~") // * These deal with the implicit Eql type member in primitive types. // * (Originally I added this to all such types, // * but it requires not expanding primitive type refs, // * which causes regressions in simplification // * because we don't yet simplify unexpanded type refs...) case (LhsRefined(S(ct @ ClassTag(lit: Lit, _)), ts, r, trs0), RhsBases(ots, S(R(RhsField(Var("Eql#A"), fldTy))), trs)) => lit match { case _: IntLit | _: DecLit => rec(fldTy.lb.getOrElse(TopType), DecType, false) case _: StrLit => rec(fldTy.lb.getOrElse(TopType), StrType, false) case _: UnitLit => reportError() } // * This deals with the implicit Eql type member for user-defined classes. case (LhsRefined(S(ClassTag(Var(nme), _)), ts, r, trs0), RhsBases(ots, S(R(RhsField(fldNme, fldTy))), trs)) if ctx.tyDefs2.contains(nme) => if (newDefs && nme =/= "Eql" && fldNme.name === "Eql#A") { val info = ctx.tyDefs2(nme) if (info.typedParams.isEmpty && !primitiveTypes.contains(nme)) // TODO shoudl actually reject all non-data classes... err(msg"${info.decl.kind.str.capitalize} '${info.decl.name }' does not support equality comparison because it does not have a parameter list", prov.loco) info.typedParams .getOrElse(Nil) // FIXME?... prim type .foreach { p => val fty = lookupField(() => done_ls.toType(sort = true), S(nme), r.fields.toMap.get, ts, p._1) rec(fldTy.lb.getOrElse(die), RecordType(p._1 -> TypeRef(TypeName("Eql"), fty.ub // FIXME check mutable? :: Nil )(provTODO).toUpper(provTODO) :: Nil)(provTODO), false) } } else { val fty = lookupField(() => done_ls.toType(sort = true), S(nme), r.fields.toMap.get, ts, fldNme) rec(fty.ub, fldTy.ub, false) recLb(fldTy, fty) } case (l @ LhsRefined(S(pt: ClassTag), ts, r, trs), RhsBases(pts, bf, trs2)) => println(s"class checking $pt $pts") if (pts.exists(p => (p.id === pt.id) || l.allTags.contains(p.id))) println(s"OK $pt <: ${pts.mkString(" | ")}") // else f.fold(reportError())(f => annoying(Nil, done_ls, Nil, f)) else annoying(Nil, LhsRefined(N, ts, r, trs), Nil, RhsBases(Nil, bf, trs2)) case (lr @ LhsRefined(bo, ts, r, _), rf @ RhsField(n, t2)) => // Reuse the case implemented below: (this shortcut adds a few more annoying calls in stats) annoying(Nil, lr, Nil, RhsBases(Nil, S(R(rf)), SortedMap.empty)) case (LhsRefined(N, ts, r, _), RhsBases(ots, S(R(RhsField(fldNme, fldTy))), trs)) if newDefs => val fty = lookupField(() => done_ls.toType(sort = true), N, r.fields.toMap.get, ts, fldNme) rec(fty.ub, fldTy.ub, false) recLb(fldTy, fty) case (LhsRefined(bo, ts, r, _), RhsBases(ots, S(R(RhsField(n, t2))), trs)) => // TODO simplify - merge with above? r.fields.find(_._1 === n) match { case S(nt1) => recLb(t2, nt1._2) rec(nt1._2.ub, t2.ub, false) case N => bo match { case S(Without(b, ns)) => if (ns(n)) rec(b, RhsBases(ots, N, trs).toType(), true) else rec(b, done_rs.toType(), true) case _ => reportError() } } case (LhsRefined(N, ts, r, trs), RhsBases(pts, N, trs2)) => println(s"Tag checking ${ts} ${pts}") if (pts.exists(p => ts.iterator.flatMap { case TraitTag(n, h) => n :: h.toList.map(n => Var(n.name)) case _ => Nil }.contains(p.id))) println(s"OK $ts <: $pts") else reportError() case (LhsRefined(N, ts, r, _), RhsBases(pts, S(L(_: FunctionType | _: ArrayBase)), _)) => reportError() case (LhsRefined(S(b: TupleType), ts, r, _), RhsBases(pts, S(L(ty: TupleType)), _)) if b.fields.size === ty.fields.size => (b.fields.unzip._2 lazyZip ty.fields.unzip._2).foreach { (l, r) => rec(l.ub, r.ub, false) recLb(r, l) } case (LhsRefined(S(b: ArrayBase), ts, r, _), RhsBases(pts, S(L(ar: ArrayType)), _)) => recLb(ar.inner, b.inner) rec(b.inner.ub, ar.inner.ub, false) case (LhsRefined(S(b: ArrayBase), ts, r, _), _) => reportError() case (LhsRefined(S(ov: Overload), ts, r, trs), RhsBases(_, S(L(f: FunctionType)), _)) if noApproximateOverload => TupleSetConstraints.mk(ov, f) match { case S(tsc) => if (tsc.tvs.nonEmpty) { tsc.tvs.mapValuesIter(_.unwrapProxies).zipWithIndex.flatMap { case ((true, tv: TV), i) => tv.lowerBounds.iterator.map((_,tv,i,true)) case ((false, tv: TV), i) => tv.upperBounds.iterator.map((_,tv,i,false)) case _ => Nil }.find { case (b,_,i,_) => tsc.updateImpl(i,b) tsc.constraints.isEmpty }.foreach { case (b,tv,_,p) => if (p) rec(b,tv,false) else rec(tv,b,false) } if (tsc.constraints.sizeCompare(1) === 0) { tsc.tvs.values.map(_.unwrapProxies).foreach { case tv: TV => tv.tsc.remove(tsc) case _ => () } tsc.constraints.head.iterator.zip(tsc.tvs).foreach { case (c, (pol, t)) => if (!pol) rec(c, t, false) if (pol) rec(t, c, false) } } } case N => reportError(S(msg"is not an instance of `${f.expNeg}`")) } case (LhsRefined(S(ov: Overload), ts, r, trs), _) => annoying(Nil, LhsRefined(S(ov.approximatePos), ts, r, trs), Nil, done_rs) // TODO remove approx. with ambiguous constraints case (LhsRefined(S(Without(b, ns)), ts, r, _), RhsBases(pts, N | S(L(_)), _)) => rec(b, done_rs.toType(), true) case (_, RhsBases(pts, S(L(Without(base, ns))), _)) => // rec((pts.map(_.neg()).foldLeft(done_ls.toType())(_ & _)).without(ns), base) // ^ This less efficient version creates a slightly different error message // (see test in Annoying.mls) annoying(pts.map(_.neg()), done_ls, base :: Nil, RhsBot) } } }() /** Helper function to constrain Field lower bounds. */ def recLb(lhs: FieldType, rhs: FieldType) (implicit raise: Raise, cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows): Unit = { (lhs.lb, rhs.lb) match { case (Some(l), Some(r)) => rec(l, r, false) case (Some(l), None) => println(s"RHS not mutable! in $lhs <- $rhs") reportError( if (lhs.prov.loco.isEmpty || rhs.prov.loco.isEmpty) S(msg"cannot be reassigned") else S(msg"is not mutable") )( (rhs.ub.withProv(rhs.prov) :: l.withProv(lhs.prov) :: Nil, l.withProv(noProv) :: Nil), ctx ) case (None, Some(_)) | (None, None) => () } } /** Extrudes and also checks type variable avoidance (which widens skolems to top/bot) * did not introduce bad bounds. To do this, we reconstrain the bounds of all new variables. * This is a bit of a sledgehammer approach that could be improved – it will duplicate TV bounds! * For instance, it would be better to accumulate new TVs' future bounds first * and add them by constraining later. */ def extrudeAndCheck(ty: SimpleType, lowerLvl: Int, pol: Boolean, upperLvl: Level, reason: Ls[Ls[ST]]) (implicit raise: Raise, cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows) : SimpleType = { val originalVars = ty.getVars val res = extrude(ty, lowerLvl, pol, upperLvl)(ctx, MutMap.empty, MutSortMap.empty, reason) val newVars = res.getVars -- originalVars if (newVars.nonEmpty) trace(s"RECONSTRAINING TVs") { newVars.foreach { case tv @ AssignedVariable(bnd) => println(s"No need to reconstrain assigned $tv") // * This is unlikely to happen, but it should be fine anyway, // * as all bounds of vars being assigned are checked against the assigned type. () case tv => println(s"Reconstraining $tv") if (tv.level > lowerLvl) tv.lowerBounds.foreach(lb => // * Q: is it fine to constrain with the current ctx's level? tv.upperBounds.foreach(ub => rec(lb, ub, false))) } }() res } /** `cctx` accumulates the types that have been compared up to this point; * it is reset when going through a nested position (sameLevel = false). * `prevCctxs` accumulate the previous `cctx`s that led to this constraint. * Currently `prevCctxs` is just used to show possibly-helpful type annotation * locations upon skolem extrusion. */ def rec(lhs: SimpleType, rhs: SimpleType, sameLevel: Bool) (implicit raise: Raise, cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows) : Unit = { constrainCalls += 1 val lhs_rhs = lhs -> rhs stack.push(lhs_rhs) consumeFuel() // Thread.sleep(10) // useful for debugging constraint-solving explosions piped to stdout recImpl(lhs, rhs)(raise, if (sameLevel) (if (cctx._1.headOption.exists(_ is lhs)) cctx._1 else lhs :: cctx._1) -> (if (cctx._2.headOption.exists(_ is rhs)) cctx._2 else rhs :: cctx._2) else (lhs :: Nil) -> (rhs :: Nil), if (sameLevel || prevCctxs.isEmpty) prevCctxs // * See [note:2] below else cctx :: prevCctxs, ctx, if (sameLevel) shadows else shadows.copy(current = Set.empty) ) stack.pop() () } def recImpl(lhs: SimpleType, rhs: SimpleType) (implicit raise: Raise, cctx: ConCtx, prevCctxs: Ls[ConCtx], ctx: Ctx, shadows: Shadows) : Unit = // trace(s"$lvl. C $lhs // println(s">> $sh ${sh.hashCode}") // } // println(s"[[ ${cctx._1.map(_.prov).mkString(", ")} << ${cctx._2.map(_.prov).mkString(", ")} ]]") // println(s"{{ ${cache.mkString(", ")} }}") // if (!lhs.mentionsTypeBounds && lhs === rhs) return () // * ^ The check above is mostly good enough but it leads to slightly worse simplified type outputs // * in corner cases. // * v The check below is a bit more precise but it incurs a lot more subtyping checks, // * especially in complex comparisons like those done in the `ExprProb` test family. // * Therefore this subtyping check may not be worth it. // * In any case, we make it more lightweight by not traversing type variables // * and not using a subtyping cache (cf. `CompareRecTypes = false`). if ({ implicit val ctr: CompareRecTypes = false; lhs <:< rhs }) println(s"Already a subtype by <:<") // println(s" where ${FunctionType(lhs, rhs)(primProv).showBounds}") else { val lhs_rhs = lhs -> rhs (lhs_rhs match { case (_: ProvType, _) | (_, _: ProvType) => shadows // * Note: contrary to Simple-sub, we do have to remember subtyping tests performed // * between things that are not syntactically type variables or type references. // * Indeed, due to the normalization of unions and intersections in the wrong polarity, // * cycles in regular trees may only ever go through unions or intersections, // * and not plain type variables. case _ => if (!noRecursiveTypes && cache(lhs_rhs)) return println(s"Cached!") val shadow = lhs.shadow -> rhs.shadow // println(s"SH: $shadow") // println(s"ALLSH: ${shadows.iterator.map(s => s._1 + "<:" + s._2).mkString(", ")}") if (shadows.current.contains(lhs_rhs)) return println(s"Spurious cycle involving $lhs_rhs") // * Spurious cycle, like alpha <: beta <: alpha else if (!noCycleCheck && shadows.previous.contains(shadow) && !shadows.current.contains(shadow) ) { println(s"SHADOWING DETECTED!") if (!lhs_rhs.matches{ case (ClassTag(ErrTypeId, _), _) | (_, ClassTag(ErrTypeId, _)) => true }) err(msg"Cyclic-looking constraint while typing ${ prov.desc}; a type annotation may be required" -> prov.loco :: ( if (!explainErrors) msg"Note: use flag `:ex` to see internal error info." -> N :: Nil else // msg"this constraint: ${lhs.expPos} <: ${rhs.expNeg}" -> N :: // msg" ... looks like: ${shadow._1.expPos} <: ${shadow._2.expNeg}" -> N :: msg"————————— Additional debugging info: —————————" -> N :: msg"this constraint: ${lhs.toString} <: ${rhs.toString} ${lhs.getClass.getSimpleName} ${rhs.getClass.getSimpleName}" -> N :: msg" ... looks like: ${shadow._1.toString} <: ${shadow._2.toString}" -> N :: Nil )) return () } if (!noRecursiveTypes) cache += lhs_rhs Shadows(shadows.current + lhs_rhs + shadow, // * FIXME this conflation is not quite correct shadows.previous + shadow) }) |> { implicit shadows: Shadows => lhs_rhs match { case (ExtrType(true), _) => () case (_, ExtrType(false) | RecordType(Nil)) => () case (TypeBounds(lb, ub), _) => rec(ub, rhs, true) case (_, TypeBounds(lb, ub)) => rec(lhs, lb, true) case (p @ ProvType(und), _) => rec(und, rhs, true) case (_, p @ ProvType(und)) => rec(lhs, und, true) case (_: TypeTag, _: TypeTag) if lhs === rhs => () case (NegType(lhs), NegType(rhs)) => rec(rhs, lhs, true) case (ClassTag(Var(nme), _), rt: RecordType) if newDefs && nme.isCapitalized => val lti = ctx.tyDefs2(nme) rt.fields.foreach { case (fldNme @ Var("Eql#A"), fldTy) => goToWork(lhs, RecordType(fldNme -> fldTy :: Nil)(noProv)) case (fldNme, fldTy) => val fty = lookupField(() => lhs, S(nme), _ => N, SortedSet.empty, fldNme) rec(fty.ub, fldTy.ub, false) recLb(fldTy, fty) } // * Note: at this point, it could be that a polymorphic type could be distribbed // * out of `r1`, but this would likely not result in something useful, since the // * LHS is a normal non-polymorphic function type... case (FunctionType(l0, r0), FunctionType(l1, r1)) => rec(l1, l0, false) rec(r0, r1, false) case (prim: ClassTag, ot: TypeTag) if prim.parentsST.contains(ot.id) => () case (tv @ AssignedVariable(lhs), rhs) => rec(lhs, rhs, true) case (lhs, tv @ AssignedVariable(rhs)) => rec(lhs, rhs, true) case (lhs: TypeVariable, rhs) if rhs.level <= lhs.level => println(s"NEW $lhs UB (${rhs.level})") val newBound = (cctx._1 ::: cctx._2.reverse).foldRight(rhs)((c, ty) => if (c.prov is noProv) ty else mkProxy(ty, c.prov)) lhs.upperBounds ::= newBound // update the bound if (noApproximateOverload) { lhs.tsc.foreachEntry { (tsc, v) => v.foreach { i => if (!tsc.tvs(i)._1) { tsc.updateOn(i, rhs) if (tsc.constraints.isEmpty) reportError() } } } val u = lhs.tsc.keysIterator.filter(_.constraints.sizeCompare(1)===0).duplicate u._1.foreach { k => k.tvs.mapValuesIter(_.unwrapProxies).foreach { case (_,tv: TV) => tv.tsc.remove(k) case _ => () } } u._2.foreach { k => k.constraints.head.iterator.zip(k.tvs).foreach { case (c, (pol, t)) => if (pol) rec(t, c, false) else rec(c, t, false) } } } lhs.lowerBounds.foreach(rec(_, rhs, true)) // propagate from the bound case (lhs, rhs: TypeVariable) if lhs.level <= rhs.level => println(s"NEW $rhs LB (${lhs.level})") val newBound = (cctx._1 ::: cctx._2.reverse).foldLeft(lhs)((ty, c) => if (c.prov is noProv) ty else mkProxy(ty, c.prov)) rhs.lowerBounds ::= newBound // update the bound if (noApproximateOverload) { rhs.tsc.foreachEntry { (tsc, v) => v.foreach { i => if(tsc.tvs(i)._1) { tsc.updateOn(i, lhs) if (tsc.constraints.isEmpty) reportError() } } } val u = rhs.tsc.keysIterator.filter(_.constraints.sizeCompare(1)===0).duplicate u._1.foreach { k => k.tvs.mapValuesIter(_.unwrapProxies).foreach { case (_,tv: TV) => tv.tsc.remove(k) case _ => () } } u._2.foreach { k => k.constraints.head.iterator.zip(k.tvs).foreach { case (c, (pol, t)) => if (pol) rec(t, c, false) else rec(c, t, false) } } } rhs.upperBounds.foreach(rec(lhs, _, true)) // propagate from the bound case (lhs: TypeVariable, rhs) => val tv = lhs println(s"wrong level: ${rhs.level}") if (constrainedTypes && rhs.level <= lvl) { println(s"STASHING $tv bound in extr ctx") val buf = ctx.extrCtx.getOrElseUpdate(tv, Buffer.empty) buf += false -> rhs cache -= lhs -> rhs () } else { val rhs2 = extrudeAndCheck(rhs, lhs.level, false, MaxLevel, cctx._1 :: prevCctxs.unzip._1 ::: prevCctxs.unzip._2) println(s"EXTR RHS ~> $rhs2 to ${lhs.level}") println(s" where ${rhs2.showBounds}") // println(s" and ${rhs.showBounds}") rec(lhs, rhs2, true) } case (lhs, rhs: TypeVariable) => val tv = rhs println(s"wrong level: ${lhs.level}") if (constrainedTypes && lhs.level <= lvl) { println(s"STASHING $tv bound in extr ctx") val buf = ctx.extrCtx.getOrElseUpdate(tv, Buffer.empty) buf += true -> lhs cache -= lhs -> rhs () } else { val lhs2 = extrudeAndCheck(lhs, rhs.level, true, MaxLevel, cctx._2 :: prevCctxs.unzip._2 ::: prevCctxs.unzip._1) println(s"EXTR LHS ~> $lhs2 to ${rhs.level}") println(s" where ${lhs2.showBounds}") // println(s" and ${lhs.showBounds}") rec(lhs2, rhs, true) } case (TupleType(fs0), TupleType(fs1)) if fs0.size === fs1.size => // TODO generalize (coerce compatible tuples) fs0.lazyZip(fs1).foreach { case ((ln, l), (rn, r)) => ln.foreach { ln => rn.foreach { rn => if (ln =/= rn) err( msg"Wrong tuple field name: found '${ln.name}' instead of '${rn.name}'", lhs.prov.loco // TODO better loco )}} recLb(r, l) rec(l.ub, r.ub, false) } case (t: ArrayBase, a: ArrayType) => recLb(a.inner, t.inner) rec(t.inner.ub, a.inner.ub, false) case (ComposedType(true, l, r), _) => rec(l, rhs, true) // Q: really propagate the outerProv here? rec(r, rhs, true) case (_, ComposedType(false, l, r)) => rec(lhs, l, true) // Q: really propagate the outerProv here? rec(lhs, r, true) case (p @ ProxyType(und), _) => rec(und, rhs, true) case (_, p @ ProxyType(und)) => rec(lhs, und, true) case (_, TupleType(f :: Nil)) if funkyTuples => rec(lhs, f._2.ub, true) // FIXME actually needs reified coercion! not a true subtyping relationship case (err @ ClassTag(ErrTypeId, _), FunctionType(l1, r1)) => rec(l1, err, false) rec(err, r1, false) case (FunctionType(l0, r0), err @ ClassTag(ErrTypeId, _)) => rec(err, l0, false) rec(r0, err, false) case (RecordType(fs0), RecordType(fs1)) => fs1.foreach { case (n1, t1) => fs0.find(_._1 === n1).fold { reportError() } { case (n0, t0) => recLb(t1, t0) rec(t0.ub, t1.ub, false) } } case (tup: TupleType, _: RecordType) => rec(tup.toRecord, rhs, true) // Q: really support this? means we'd put names into tuple reprs at runtime case (err @ ClassTag(ErrTypeId, _), RecordType(fs1)) => fs1.foreach(f => rec(err, f._2.ub, false)) case (_, RecordType(fs1)) => goToWork(lhs, rhs) case (RecordType(fs1), err @ ClassTag(ErrTypeId, _)) => fs1.foreach(f => rec(f._2.ub, err, false)) case (tr1: TypeRef, tr2: TypeRef) if tr1.defn.name =/= "Array" && tr2.defn.name =/= "Eql" => if (tr1.defn === tr2.defn) { assert(tr1.targs.sizeCompare(tr2.targs) === 0) ctx.tyDefs.get(tr1.defn.name) match { case S(td) => val tvv = td.getVariancesOrDefault td.tparamsargs.unzip._2.lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { (tv, targ1, targ2) => val v = tvv(tv) if (!v.isContravariant) rec(targ1, targ2, false) if (!v.isCovariant) rec(targ2, targ1, false) } case N => /* ctx.tyDefs2(tr1.defn.name).complete() match { case cls: TypedNuCls => cls.tparams.map(_._2).lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { (tv, targ1, targ2) => val v = cls.varianceOf(tv) if (!v.isContravariant) rec(targ1, targ2, false) if (!v.isCovariant) rec(targ2, targ1, false) } // case _ => ??? } */ ctx.tyDefs2.get(tr1.defn.name) match { case S(lti) => lti.tparams.map(_._2).lazyZip(tr1.targs).lazyZip(tr2.targs).foreach { (tv, targ1, targ2) => val v = lti.varianceOf(tv) if (!v.isContravariant) rec(targ1, targ2, false) if (!v.isCovariant) rec(targ2, targ1, false) } case N => ??? // TODO } } } else { if (tr1.mayHaveTransitiveSelfType) rec(tr1.expand, tr2.expand, true) else (tr1.mkClsTag, tr2.mkClsTag) match { case (S(tag1), S(tag2)) if !(tag1 <:< tag2) => reportError() case _ => rec(tr1.expand, tr2.expand, true) } } case (tr: TypeRef, _) => rec(tr.expand, rhs, true) case (err @ ClassTag(ErrTypeId, _), tr: TypeRef) => // rec(tr.copy(targs = tr.targs.map(_ => err))(noProv), tr, true) // * ^ Nicely propagates more errors to the result, // * but can incur vast amounts of unnecessary constraining in the context of recursive types! () case (_, tr: TypeRef) => rec(lhs, tr.expand, true) case (ClassTag(ErrTypeId, _), _) => () case (_, ClassTag(ErrTypeId, _)) => () case (_, w @ Without(b, ns)) => rec(lhs.without(ns), b, true) case (_, n @ NegType(w @ Without(b, ns))) => rec(Without(lhs, ns)(w.prov), NegType(b)(n.prov), true) // this is weird... TODO check sound case (_, poly: PolymorphicType) => val oldCtx = ctx ctx.nextLevel { implicit ctx => val rigid = poly.rigidify println(s"BUMP TO LEVEL ${lvl} --> $rigid") println(s"where ${rigid.showBounds}") val res = rec(lhs, rigid, true) unstash(oldCtx) res } case (AliasOf(PolymorphicType(plvl, bod)), _) if bod.level <= plvl => rec(bod, rhs, true) case (_, PolyFunction(newRhs)) if distributeForalls => println(s"DISTRIB-R ~> $newRhs") rec(lhs, newRhs, true) // * Simple case when the parameter type vars don't need to be split case (AliasOf(PolymorphicType(plvl, AliasOf(FunctionType(param, bod)))), _) if distributeForalls && param.level <= plvl && bod.level > plvl => val newLhs = FunctionType(param, PolymorphicType(plvl, bod))(rhs.prov) println(s"DISTRIB-L ~> $newLhs") rec(newLhs, rhs, true) // * Difficult case: split off type parameters that are quantified in body but NOT in param case (SplittablePolyFun(newLhs), _) if distributeForalls => println(s"DISTRIB-L' ~> $newLhs") rec(newLhs, rhs, true) case (poly: PolymorphicType, _) => // TODO Here it might actually be better to try and put poly into a TV if the RHS contains one // ^ Note: similar remark applies inside constrainDNF // * [note:1] This assertion seems to hold most of the time, // * with notable exceptions occurring in QML existential encoding tests // assert(lvl >= rhs.level) // * Note: we instantiate `poly` at the current context's polymorphic level. // * This level can either be the current typing level, // * at which the type variables obtained from cosntraining are expected to be; // * or it can be a "bumped-up" level locally introdcued // * when comparing against a polymorphic RHS signature that needed to be rigidified // * – in this case, the higher level is meant to allow further instantiations to match // * the rigid type variables without extrusion, // * while preventing the rigid variables from leaking out. // * [note:2] Hack ("heuristic"): we only start remembering `prevCctxs` // * after going through at least one instantiation. // * This is to filter out locations that were unlikely to cause // * any skolem extrusion down the line. // * – indeed, skolem extrusions are often caused by premature instantiation. (if (prevCctxs.isEmpty) (Nil -> Nil) :: Nil else prevCctxs) |> { implicit prevCctxs => rec(poly.instantiate, rhs, true) } case (ConstrainedType(cs, bod), _) => trace(s"DISCHARGE CONSTRAINTS") { cs.foreach { case (lb, ub) => rec(lb, ub, false) } }() rec(bod, rhs, true) case (_, ConstrainedType(cs, bod)) => ??? // TODO? case (_, ComposedType(true, l, r)) => goToWork(lhs, rhs) case (ComposedType(false, l, r), _) => goToWork(lhs, rhs) case (ov: Overload, _) => // * This is a large approximation // * TODO: remove approx. through use of ambiguous constraints rec(ov.approximatePos, rhs, true) case (_: NegType | _: Without, _) | (_, _: NegType | _: Without) => goToWork(lhs, rhs) case (_: ClassTag | _: TraitTag, _: TraitTag) => goToWork(lhs, rhs) case _ => reportError() }} }}() def reportError(failureOpt: Opt[Message] = N)(implicit cctx: ConCtx, ctx: Ctx): Unit = { val lhs = cctx._1.head val rhs = cctx._2.head println(s"CONSTRAINT FAILURE: $lhs <: $rhs") // println(s"CTX: ${cctx.map(_.map(lr => s"${lr._1} <: ${lr._2} [${lr._1.prov}] [${lr._2.prov}]"))}") def doesntMatch(ty: SimpleType) = msg"does not match type `${ty.expNeg}`" def doesntHaveField(n: Str) = msg"does not have field '$n'" val lhsChain: List[ST] = cctx._1 val rhsChain: List[ST] = cctx._2 // The first located provenance coming from the left val lhsProv = lhsChain.iterator .filterNot(_.prov.isOrigin) .find(_.prov.loco.isDefined) .map(_.prov).getOrElse(lhs.prov) // The first located provenance coming from the right val rhsProv = rhsChain.iterator .filterNot(_.prov.isOrigin) .find(_.prov.loco.isDefined) .map(_.prov).getOrElse(rhs.prov) // The last located provenance coming from the right (this is a bit arbitrary) val rhsProv2 = rhsChain.reverseIterator .filterNot(_.prov.isOrigin) .find(_.prov.loco.isDefined) .map(_.prov).getOrElse(rhs.prov) val relevantFailures = lhsChain.collect { case st if st.prov.loco =/= lhsProv.loco && st.prov.loco.exists(ll => prov.loco.forall(pl => (ll touches pl) || (pl covers ll))) => st } val tighestRelevantFailure = relevantFailures.headOption val shownLocos = MutSet.empty[Loc] def show(loco: Opt[Loc]): Opt[Loc] = loco.tap(_.foreach(shownLocos.+=)) val originProvList = (lhsChain.headOption ++ rhsChain.lastOption).iterator .map(_.prov).collect { case tp @ TypeProvenance(loco, desc, S(nme), _) if loco.isDefined => nme -> tp } .toList.distinctBy(_._2.loco) def printProv(prov: TP): Message = if (prov.isType) msg"type" else msg"${prov.desc} of type" // * Accumulate messages showing possibly-helpful type annotations. def getPossibleAnnots(cctxs: Ls[Ls[ST]]): Ls[Message -> Opt[Loc]] = { val suggested = MutSet.empty[Loc] val removed = MutSet.empty[Loc] def go(cctxs: Ls[Ls[ST]]): Ls[Message -> Opt[Loc]] = cctxs match { case reason :: reasons => val possible = reason.iterator.map(_.prov).collect { case p @ TypeProvenance(loco @ S(loc), desc, _, _) if !suggested.contains(loc) && !p.isType && !suggested.exists(loc covers _) => suggested.foreach(loc2 => if (loc2 covers loc) removed += loc2) suggested.add(loc) msg"• this ${desc}:" -> loco }.toList possible ::: go(reasons) case Nil => Nil } go(cctxs).filterNot(_._2.exists(removed)) } def mk_constraintProvenanceHints = rhsProv.loco match { case S(rhsProv_loc) if !prov.loco.contains(rhsProv_loc) && !shownLocos(rhsProv_loc) => msg"Note: constraint arises from ${rhsProv.desc}:" -> show(rhsProv.loco) :: ( rhsProv2.loco match { case S(rhsProv2_loc) if rhsProv2_loc =/= rhsProv_loc && !prov.loco.contains(rhsProv2_loc) && !lhsProv.loco.contains(rhsProv2_loc) && !shownLocos(rhsProv2_loc) => msg"from ${rhsProv2.desc}:" -> show(rhsProv2.loco) :: Nil case _ => Nil }) case _ => Nil } val lhsBase = lhs.typeBase def lhsIsPlain = lhsBase matches { case _: FunctionType | _: RecordType | _: TypeTag | _: TupleType | _: TypeRef | _: ExtrType => true } val failure = failureOpt.getOrElse((lhsBase, rhs.unwrapProvs) match { case lhs_rhs @ ((_: Extruded, _) | (_, _: Extruded)) => val (mainExtr, extr1, extr2, reason) = lhs_rhs match { case (extr: Extruded, extr2: Extruded) => (extr, S(extr), S(extr2), extr.reason ++ extr2.reason) // * ^ Note: I expect extr.reason and extr2.reason to have the same size. // * Interleave them for more natural reporting order? case (extr: Extruded, _) => (extr, S(extr), N, extr.reason) case (_, extr: Extruded) => (extr, N, S(extr), extr.reason) case _ => die } val possibleAnnots = getPossibleAnnots(reason) val e1loco = show(lhsProv.loco).orElse(show(mainExtr.underlying.prov.loco)) .orElse(show(mainExtr.underlying.id.prov.loco)) val msgs = msg"Type error in ${prov.desc}" -> show(prov.loco) :: msg"type variable `${mainExtr.underlying.expPos}` leaks out of its scope" -> e1loco :: (extr2 match { case S(extr2) => val e2loco = show(rhsProv.loco).orElse(show(extr2.underlying.prov.loco)) .orElse(show(extr2.underlying.id.prov.loco)) if (extr1.isDefined && e2loco =/= e1loco) msg"back into type variable `${extr2.underlying.expNeg}`" -> e2loco :: Nil else Nil case N => msg"into ${printProv(rhsProv)} `${rhs.expNeg}`" -> show(rhsProv.loco) :: Nil }) ::: ( if (possibleAnnots.nonEmpty) msg"adding a type annotation to any of the following terms may help resolve the problem" -> N :: possibleAnnots else Nil ) ::: ( rhsProv2.loco match { case S(rhsProv2_loc) if !rhsProv.loco.contains(rhsProv2_loc) && !prov.loco.contains(rhsProv2_loc) && !lhsProv.loco.contains(rhsProv2_loc) && !shownLocos(rhsProv2_loc) => msg"Note: constraint arises from ${rhsProv2.desc}:" -> show(rhsProv2.loco) :: Nil case _ => Nil } ) return raise(ErrorReport(msgs ::: mk_constraintProvenanceHints, newDefs)) case (_: TV | _: ProxyType, _) => doesntMatch(rhs) case (RecordType(fs0), RecordType(fs1)) => (fs1.map(_._1).toSet -- fs0.map(_._1).toSet) .headOption.fold(doesntMatch(rhs)) { n1 => doesntHaveField(n1.name) } case (lunw, obj: ObjectTag) if obj.id.isInstanceOf[Var] => msg"is not an instance of type `${ if (primitiveTypes(obj.id.idStr)) obj.id.idStr else obj.id.idStr.capitalize}`" case (lunw, obj: TypeRef) => msg"is not an instance of `${obj.expNeg}`" case (lunw, TupleType(fs)) if !lunw.isInstanceOf[TupleType] => msg"is not a ${fs.size.toString}-element tuple" case (lunw, FunctionType(_, _)) if !lunw.isInstanceOf[FunctionType] => msg"is not a function" case (lunw, RecordType((n, _) :: Nil)) if !lunw.isInstanceOf[RecordType] => doesntHaveField(n.name) case (lunw, RecordType(fs @ (_ :: _))) if lhsIsPlain && !lunw.isInstanceOf[RecordType] => msg"is not a record (expected a record with field${ if (fs.sizeCompare(1) > 0) "s" else ""}: ${fs.map(_._1.name).mkString(", ")})" case (lunw, RecordType(fs @ (_ :: _))) => msg"does not have all required fields ${fs.map("'" + _._1.name + "'").mkString(", ")}" case _ => doesntMatch(rhs) }) val mismatchMessage = msg"Type mismatch in ${prov.desc}:" -> show(prov.loco) :: ( msg"${printProv(lhsProv)} `${lhs.expPos}` $failure" ) -> (if (lhsProv.loco === prov.loco) N else show(lhsProv.loco)) :: Nil val flowHint = tighestRelevantFailure.map { l => val expTyMsg = msg" with expected type `${rhs.expNeg}`" msg"but it flows into ${l.prov.desc}$expTyMsg" -> show(l.prov.loco) :: Nil }.toList.flatten val constraintProvenanceHints = mk_constraintProvenanceHints var first = true val originProvHints = originProvList.collect { case (nme, l) if l.loco.exists(!shownLocos.contains(_)) => val msgHead = if (first) msg"Note: ${l.desc} $nme" else msg" ${l.desc} $nme" first = false msg"${msgHead} is defined at:" -> l.loco } val detailedContext = if (explainErrors) msg"========= Additional explanations below =========" -> N :: lhsChain.flatMap { lhs => if (dbg) msg"[info] LHS >> ${lhs.prov.toString} : ${lhs.expPos}" -> lhs.prov.loco :: Nil else msg"[info] flowing from ${printProv(lhs.prov)} `${lhs.expPos}`" -> lhs.prov.loco :: Nil } ::: rhsChain.reverse.flatMap { rhs => if (dbg) msg"[info] RHS << ${rhs.prov.toString} : ${rhs.expNeg}" -> rhs.prov.loco :: Nil else msg"[info] flowing into ${printProv(rhs.prov)} `${rhs.expNeg}`" -> rhs.prov.loco :: Nil } else Nil val msgs = Ls[Ls[Message -> Opt[Loc]]]( mismatchMessage, flowHint, constraintProvenanceHints, originProvHints, detailedContext, ).flatten raise(ErrorReport(msgs, newDefs)) } rec(lhs, rhs, true)(raise, Nil -> Nil, Nil, outerCtx, shadows) }} def subsume(ty_sch: ST, sign: ST) (implicit ctx: Ctx, raise: Raise, prov: TypeProvenance): Unit = { println(s"CHECKING SUBSUMPTION...") var errCnt = 0 constrain(ty_sch, sign)({ err => errCnt += 1 if (errCnt > maxSuccessiveErrReports) { // * Silence further errors if (showAllErrors) notifyMoreErrors("signature-checking", prov) return } else if (showAllErrors || errCnt === 1) raise(err) }, prov, ctx, Shadows.empty) } /** Copies a type up to its type variables of wrong level (and their extruded bounds), * meaning those non-locally-quantified type variables whose level is strictly greater than `lowerLvl`. * Parameter `upperLvl` is used to track above which level we DON'T want to extrude variables, * as we may be traversing types that are quantified by polymorphic types in the process of being copied. * `upperLvl` tracks the lowest such current quantification level. */ private final def extrude(ty: SimpleType, lowerLvl: Int, pol: Boolean, upperLvl: Level) (implicit ctx: Ctx, cache: MutMap[TypeVarOrRigidVar->Bool, TypeVarOrRigidVar], cache2: MutSortMap[TraitTag, TraitTag], reason: Ls[Ls[ST]]) : SimpleType = // (trace(s"EXTR[${printPol(S(pol))}] $ty || $lowerLvl .. $upperLvl ${ty.level} ${ty.level <= lowerLvl}"){ if (ty.level <= lowerLvl) ty else ty match { case t @ TypeBounds(lb, ub) => if (pol) extrude(ub, lowerLvl, true, upperLvl) else extrude(lb, lowerLvl, false, upperLvl) case t @ FunctionType(l, r) => FunctionType(extrude(l, lowerLvl, !pol, upperLvl), extrude(r, lowerLvl, pol, upperLvl))(t.prov) case t @ ComposedType(p, l, r) => ComposedType(p, extrude(l, lowerLvl, pol, upperLvl), extrude(r, lowerLvl, pol, upperLvl))(t.prov) case t @ RecordType(fs) => RecordType(fs.mapValues(_.update(extrude(_, lowerLvl, !pol, upperLvl), extrude(_, lowerLvl, pol, upperLvl))))(t.prov) case t @ TupleType(fs) => TupleType(fs.mapValues(_.update(extrude(_, lowerLvl, !pol, upperLvl), extrude(_, lowerLvl, pol, upperLvl))))(t.prov) case t @ ArrayType(ar) => ArrayType(ar.update(extrude(_, lowerLvl, !pol, upperLvl), extrude(_, lowerLvl, pol, upperLvl)))(t.prov) case w @ Without(b, ns) => Without(extrude(b, lowerLvl, pol, upperLvl), ns)(w.prov) case tv @ AssignedVariable(ty) => cache.getOrElse(tv -> true, { val nv = freshVar(tv.prov, S(tv), tv.nameHint)(lowerLvl) cache += tv -> true -> nv val tyPos = extrude(ty, lowerLvl, true, upperLvl) val tyNeg = extrude(ty, lowerLvl, false, upperLvl) if (tyPos === tyNeg) nv.assignedTo = S(tyPos) else { assert(nv.lowerBounds.isEmpty) assert(nv.upperBounds.isEmpty) nv.lowerBounds = tyPos :: Nil nv.upperBounds = tyNeg :: Nil } nv }) case tv: TypeVariable if tv.level > upperLvl => assert(!cache.contains(tv -> false), (tv, cache)) // * If the TV's level is strictly greater than `upperLvl`, // * it means the TV is quantified by a type being copied, // * so all we need to do is copy this TV along (it is not extruded). // * We pick `tv -> true` (and not `tv -> false`) arbitrarily. if (tv.lowerBounds.isEmpty && tv.upperBounds.isEmpty) tv else cache.getOrElse(tv -> true, { val nv = freshVar(tv.prov, S(tv), tv.nameHint)(tv.level) cache += tv -> true -> nv nv.lowerBounds = tv.lowerBounds.map(extrude(_, lowerLvl, true, upperLvl)) nv.upperBounds = tv.upperBounds.map(extrude(_, lowerLvl, false, upperLvl)) nv }) case t @ SpliceType(fs) => t.updateElems(extrude(_, lowerLvl, pol, upperLvl), extrude(_, lowerLvl, !pol, upperLvl), extrude(_, lowerLvl, pol, upperLvl), t.prov) case tv: TypeVariable => cache.getOrElse(tv -> pol, { val nv = freshVar(tv.prov, S(tv), tv.nameHint)(lowerLvl) cache += tv -> pol -> nv if (pol) { tv.upperBounds ::= nv nv.lowerBounds = tv.lowerBounds.map(extrude(_, lowerLvl, pol, upperLvl)) } else { tv.lowerBounds ::= nv nv.upperBounds = tv.upperBounds.map(extrude(_, lowerLvl, pol, upperLvl)) } nv }) case n @ NegType(neg) => NegType(extrude(neg, lowerLvl, !pol, upperLvl))(n.prov) case e @ ExtrType(_) => e case p @ ProvType(und) => ProvType(extrude(und, lowerLvl, pol, upperLvl))(p.prov) case p @ ProxyType(und) => extrude(und, lowerLvl, pol, upperLvl) case tt @ SkolemTag(id) => if (tt.level > upperLvl) { extrude(id, lowerLvl, pol, upperLvl) match { case id: TV => SkolemTag(id)(tt.prov) case _ => die } } else if (tt.level > lowerLvl) { // * When a rigid type variable is extruded, // * we need to essentially widen it to Top or Bot. // * Creating a new skolem instead, as was done at some point, is actually unsound. // * But for better error messages, we instead use an `Extruded` abstract tag, // * making sure we pick a *different* one for positive and negative positions, // * which achieves the same effect as Top/Bot. new Extruded(!pol, tt)( tt.prov.copy(desc = "extruded type variable reference"), reason) } else die // shouldn't happen case _: ClassTag | _: TraitTag | _: Extruded => ty case tr @ TypeRef(d, ts) => TypeRef(d, tr.mapTargs(S(pol)) { case (N, targ) => // * Note: the semantics of TypeBounds is inappropriuate for this use (known problem; FIXME later) TypeBounds.mk(extrude(targ, lowerLvl, false, upperLvl), extrude(targ, lowerLvl, true, upperLvl)) // Q: ? subtypes? // * A sanity-checking version, making sure the type range is correct (LB subtype of UB): /* val a = extrude(targ, lowerLvl, false, upperLvl) val b = extrude(targ, lowerLvl, true, upperLvl) implicit val r: Raise = throw _ implicit val p: TP = noProv constrain(a, b) TypeBounds.mk(a, b) */ case (S(pol), targ) => extrude(targ, lowerLvl, pol, upperLvl) })(tr.prov) case PolymorphicType(polymLevel, body) => PolymorphicType(polymLevel, extrude(body, lowerLvl, pol, upperLvl = // upperLvl min polymLevel // * for some crazy reason, this stopped type checking Math.min(upperLvl, polymLevel) )) case ConstrainedType(cs, bod) => ConstrainedType(cs.map { case (lo, hi) => extrude(lo, lowerLvl, true, upperLvl) -> extrude(hi, lowerLvl, false, upperLvl) }, extrude(bod, lowerLvl, pol, upperLvl)) case o @ Overload(alts) => o.mapAlts(extrude(_, lowerLvl, !pol, upperLvl))(extrude(_, lowerLvl, pol, upperLvl)) } // }(r => s"=> $r")) def err(msg: Message, loco: Opt[Loc])(implicit raise: Raise): SimpleType = { err(msg -> loco :: Nil) } def err(msgs: List[Message -> Opt[Loc]])(implicit raise: Raise): SimpleType = { err(ErrorReport(msgs, newDefs)) } def err(diag: Diagnostic)(implicit raise: Raise): SimpleType = { raise(diag) errType } def errType: SimpleType = ClassTag(ErrTypeId, Set.empty)(noProv) def warn(msg: Message, loco: Opt[Loc])(implicit raise: Raise): Unit = warn(msg -> loco :: Nil) def warn(msgs: List[Message -> Opt[Loc]])(implicit raise: Raise): Unit = raise(WarningReport(msgs, newDefs)) def unify(lhs: ST, rhs: ST)(implicit raise: Raise, prov: TypeProvenance, ctx: Ctx, shadows: Shadows = Shadows.empty ): Unit = { trace(s"$lvl. U $lhs =! $rhs") { def rec(lhs: ST, rhs: ST, swapped: Bool): Unit = if (!lhs.mentionsTypeBounds && lhs === rhs) () else (lhs, rhs) match { // TODO handle more cases case (tv: TV, bound) if bound.level <= tv.level => tv.assignedTo match { case S(et) => unify(et, bound) case N => println(s"$tv := $bound") val lbs = tv.lowerBounds val ubs = tv.upperBounds tv.assignedTo = S(bound) lbs.foreach(constrainImpl(_, bound)) ubs.foreach(constrainImpl(bound, _)) } case _ => if (swapped) { constrain(lhs, rhs) constrain(rhs, lhs) } else rec(rhs, lhs, true) } rec(lhs, rhs, false) }() } /** Freshens all the type variables whose level is comprised in `(above, below]` * or which have bounds and whose level is greater than `above`. */ def freshenAbove(above: Level, ty: SimpleType, rigidify: Bool = false, below: Level = MaxLevel, leaveAlone: Set[TV] = Set.empty) (implicit ctx: Ctx, freshened: MutMap[TV, ST]) : SimpleType = { def freshenImpl(ty: SimpleType, below: Level): SimpleType = // (trace(s"${lvl}. FRESHEN $ty || $above .. $below ${ty.level} ${ty.level <= above}") { // * Cannot soundly freshen if the context's level is above the current polymorphism level, // * as that would wrongly capture the newly-freshened variables. require(below >= lvl) def freshen(ty: SimpleType): SimpleType = freshenImpl(ty, below) if ( // * Note that commenting this broke the old semantics of wildcard TypeBound-s in signatures: /* !rigidify // Rigidification now also substitutes TypeBound-s with fresh vars; // since these have the level of their bounds, when rigidifying // we need to make sure to copy the whole type regardless of level... && */ ty.level <= above) ty else ty match { case tv: TypeVariable if leaveAlone(tv) => tv case tv @ AssignedVariable(ty) => freshened.getOrElse(tv, { val nv = freshVar(tv.prov, S(tv), tv.nameHint)(if (tv.level > below) tv.level else lvl) freshened += tv -> nv val ty2 = freshen(ty) nv.assignedTo = S(ty2) nv }) // * Note: I forgot why I though this was unsound... /* case tv: TypeVariable // THIS IS NOT SOUND: WE NEED TO REFRESH REGARDLESS!! if tv.level > below // It is not sound to ignore the bounds here, // as the bounds could contain references to other TVs with lower level; // OTOH, we don't want to traverse the whole bounds graph every time just to check // (using `levelBelow`), // so if there are any bounds registered, we just conservatively freshen the TV. && tv.lowerBounds.isEmpty && tv.upperBounds.isEmpty => tv */ case tv: TypeVariable => freshened.get(tv) match { case Some(tv) => tv case None if rigidify && tv.level <= below => // * Rigid type variables (ie, skolems) are encoded as SkolemTag-s val rv = SkolemTag(freshVar(noProv, S(tv), tv.nameHint/* .orElse(S("_"))*/))(tv.prov) println(s"New skolem: $tv ~> $rv") if (tv.lowerBounds.nonEmpty || tv.upperBounds.nonEmpty) { // TODO just add bounds to skolems! should lead to simpler constraints // The bounds of `tv` may be recursive (refer to `tv` itself), // so here we create a fresh variabe that will be able to tie the presumed recursive knot // (if there is no recursion, it will just be a useless type variable) val tv2 = freshVar(tv.prov, S(tv), tv.nameHint)(lvl) freshened += tv -> tv2 // Assuming there were no recursive bounds, given L <: tv <: U, // we essentially need to turn tv's occurrence into the type-bounds (rv | L)..(rv & U), // meaning all negative occurrences should be interpreted as rv | L // and all positive occurrences should be interpreted as rv & U // where rv is the rigidified variables. // Now, since there may be recursive bounds, we do the same // but through the indirection of a type variable tv2: tv2.lowerBounds ::= tv.upperBounds.map(freshen).foldLeft(rv: ST)(_ & _) println(s"$tv2 :> ${tv2.lowerBounds}") tv2.upperBounds ::= tv.lowerBounds.map(freshen).foldLeft(rv: ST)(_ | _) println(s"$tv2 <: ${tv2.upperBounds}") tv2 } else { // NOTE: tv.level may be different from lvl; is that OK? freshened += tv -> rv rv } case None => val v = freshVar(tv.prov, S(tv), tv.nameHint)(if (tv.level > below) tv.level else { assert(lvl <= below, "this condition should be false for the result to be correct") lvl }) val freshentsc = tv.tsc.flatMap { case (tsc,_) => if (tsc.tvs.values.map(_.unwrapProxies).forall { case tv: TV => !freshened.contains(tv) case _ => true }) S(tsc) else N } freshened += tv -> v v.lowerBounds = tv.lowerBounds.mapConserve(freshen) v.upperBounds = tv.upperBounds.mapConserve(freshen) freshentsc.foreach { tsc => val t = new TupleSetConstraints(tsc.constraints, tsc.tvs) t.constraints = t.constraints.map(_.map(freshen)) t.tvs = t.tvs.map(x => (x._1,freshen(x._2))) t.tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { case (tv: TV, i) => tv.tsc.updateWith(t)(_.map(_ + i).orElse(S(Set(i)))) case _ => () } } v } case t @ TypeBounds(lb, ub) => // * This was done to make `?` behave similarly to an existential. // * But this niche treatment just needlessly complicates things; // * better implement proper existentials later on! /* if (rigidify) { val tv = freshVar(t.prov, N) // FIXME coudl N here result in divergence? cf. absence of shadow tv.lowerBounds ::= freshen(lb) tv.upperBounds ::= freshen(ub) tv } else TypeBounds(freshen(lb), freshen(ub))(t.prov) */ TypeBounds(freshen(lb), freshen(ub))(t.prov) case t @ FunctionType(l, r) => FunctionType(freshen(l), freshen(r))(t.prov) case t @ ComposedType(p, l, r) => ComposedType(p, freshen(l), freshen(r))(t.prov) case t @ RecordType(fs) => RecordType(fs.mapValues(_.update(freshen, freshen)))(t.prov) case t @ TupleType(fs) => TupleType(fs.mapValues(_.update(freshen, freshen)))(t.prov) case t @ ArrayType(ar) => ArrayType(ar.update(freshen, freshen))(t.prov) case t @ SpliceType(fs) => t.updateElems(freshen, freshen, freshen, t.prov) case n @ NegType(neg) => NegType(freshen(neg))(n.prov) case e @ ExtrType(_) => e case p @ ProvType(und) => ProvType(freshen(und))(p.prov) case p @ ProxyType(und) => freshen(und) case s @ SkolemTag(id) if s.level > above && s.level <= below => freshen(id) case _: ClassTag | _: TraitTag | _: SkolemTag | _: Extruded => ty case w @ Without(b, ns) => Without(freshen(b), ns)(w.prov) case tr @ TypeRef(d, ts) => TypeRef(d, ts.map(freshen(_)))(tr.prov) case pt @ PolymorphicType(polyLvl, bod) if pt.level <= above => pt // is this really useful? case pt @ PolymorphicType(polyLvl, bod) => if (lvl > polyLvl) freshen(pt.raiseLevelToImpl(lvl, leaveAlone)) else PolymorphicType(polyLvl, freshenImpl(bod, below = below min polyLvl)) case ct @ ConstrainedType(cs, bod) => val cs2 = cs.map(lu => freshen(lu._1) -> freshen(lu._2)) ConstrainedType(cs2, freshen(bod)) case o @ Overload(alts) => o.mapAlts(freshen)(freshen) }} // (r => s"=> $r")) freshenImpl(ty, below) } } ================================================ FILE: shared/src/main/scala/mlscript/Diagnostic.scala ================================================ package mlscript import scala.util.chaining._ import mlscript.utils._, shorthands._ import Diagnostic._ sealed abstract class Diagnostic(val theMsg: String) extends Exception(theMsg) { val allMsgs: Ls[Message -> Opt[Loc]] val kind: Kind val source: Source } object Diagnostic { sealed abstract class Kind case object Error extends Kind case object Warning extends Kind sealed abstract class Source case object Lexing extends Source case object Parsing extends Source case object PreTyping extends Source case object Desugaring extends Source case object Typing extends Source case object Compilation extends Source case object Runtime extends Source } final case class ErrorReport(mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], source: Source) extends Diagnostic(mainMsg) { val kind: Kind = Error } object ErrorReport { def apply(msgs: Ls[Message -> Opt[Loc]], newDefs: Bool, source: Source = Typing): ErrorReport = ErrorReport(msgs.head._1.show(newDefs), msgs, source) } final case class WarningReport(mainMsg: Str, allMsgs: Ls[Message -> Opt[Loc]], source: Source) extends Diagnostic(mainMsg) { val kind: Kind = Warning } object WarningReport { def apply(msgs: Ls[Message -> Opt[Loc]], newDefs: Bool, source: Source = Typing): WarningReport = WarningReport(msgs.head._1.show(newDefs), msgs, source) } final case class Loc(spanStart: Int, spanEnd: Int, origin: Origin) { assert(spanStart >= 0) assert(spanEnd >= spanStart) def covers(that: Loc): Bool = that.origin === this.origin && ( that.spanStart >= this.spanStart && that.spanEnd <= this.spanEnd ) def touches(that: Loc): Bool = that.origin === this.origin && ( that.spanStart >= this.spanStart && that.spanStart <= this.spanEnd || that.spanEnd <= this.spanEnd && that.spanEnd >= this.spanStart ) def ++(that: Loc): Loc = { require(this.origin is that.origin) Loc(this.spanStart min that.spanStart, this.spanEnd max that.spanEnd, origin) } def ++(that: Opt[Loc]): Loc = that.fold(this)(this ++ _) def right: Loc = copy(spanStart = spanEnd) def left: Loc = copy(spanEnd = spanStart) } object Loc { def apply(xs: IterableOnce[Located]): Opt[Loc] = xs.iterator.foldLeft(none[Loc])((acc, l) => acc.fold(l.toLoc)(_ ++ l.toLoc |> some)) } final case class Origin(fileName: Str, startLineNum: Int, fph: FastParseHelpers) { override def toString = s"$fileName:+$startLineNum" } ================================================ FILE: shared/src/main/scala/mlscript/JSBackend.scala ================================================ package mlscript import mlscript.utils._, shorthands._, algorithms._ import mlscript.codegen._ import scala.collection.mutable.{ListBuffer, HashMap, HashSet} import mlscript.{JSField, JSLit} import scala.collection.mutable.{Set => MutSet} import scala.util.control.NonFatal import scala.util.chaining._ abstract class JSBackend { def oldDefs: Bool protected implicit class TermOps(term: Term) { def isLam: Bool = term match { case _: Lam => true case Bra(false, inner) => inner.isLam case Asc(inner, _) => inner.isLam case _ => false } } /** * The root scope of the program. */ protected val topLevelScope = Scope("root") /** * The prelude code manager. */ protected val polyfill = Polyfill() /** * This function translates parameter destructions in `def` declarations. * * The production rules of MLscript `def` parameters are: * * subterm ::= "(" term ")" | record | literal | identifier * term ::= let | fun | ite | withsAsc | _match * * JavaScript supports following destruction patterns: * * - Array patterns: `[x, y, ...]` where `x`, `y` are patterns. * - Object patterns: `{x: y, z: w, ...}` where `z`, `w` are patterns. * - Identifiers: an identifier binds the position to a name. * * This function only translate name patterns and object patterns. I was thinking if we can * support literal parameter matching by merging multiple function `def`s into one. */ private def translatePattern(t: Term)(implicit scope: Scope): JSPattern = t match { // fun x -> ... ==> function (x) { ... } // should returns ("x", ["x"]) case Var(name) => val runtimeName = scope.declareParameter(name) JSNamePattern(runtimeName) // fun { x, y } -> ... ==> function ({ x, y }) { ... } // should returns ("{ x, y }", ["x", "y"]) case Rcd(fields) => JSObjectPattern(fields map { case (Var(nme), Fld(_, Var(als))) => val runtimeName = scope.declareParameter(als) val fieldName = JSField.emitValidFieldName(nme) if (runtimeName === fieldName) fieldName -> N else fieldName -> S(JSNamePattern(runtimeName)) case (Var(nme), Fld(_, subTrm)) => JSField.emitValidFieldName(nme) -> S(translatePattern(subTrm)) }) // This branch supports `def f (x: int) = x`. case Asc(trm, _) => translatePattern(trm) // Replace literals with wildcards. case _: Lit => JSWildcardPattern() case Bra(_, trm) => translatePattern(trm) case Tup(fields) => JSArrayPattern(fields map { case (_, Fld(_, t)) => translatePattern(t) }) // Others are not supported yet. case TyApp(base, _) => translatePattern(base) case Inst(bod) => translatePattern(bod) case Ann(ann, receiver) => translatePattern(receiver) case _: Lam | _: App | _: Sel | _: Let | _: Blk | _: Bind | _: Test | _: With | _: CaseOf | _: Subs | _: Assign | _: If | _: New | _: NuNew | _: Splc | _: Forall | _: Where | _: Super | _: Eqn | _: AdtMatchWith | _: Rft | _: While | _: Quoted | _: Unquoted => throw CodeGenError(s"term $t is not a valid pattern") } private def translateParams(t: Term)(implicit scope: Scope): Ls[JSPattern] = t match { case Tup(params) => params map { case N -> Fld(_, p) => translatePattern(p) case S(nme) -> Fld(_, p) => translatePattern(nme) } case _ => throw CodeGenError(s"term $t is not a valid parameter list") } // Set `requireActualCls` to true if we need the actual class rather than the constrcutor function (if the class has) private def translateNuTypeSymbol(sym: NuTypeSymbol, requireActualCls: Bool)(implicit scope: Scope): JSExpr = { val trm = sym.qualifier.fold[JSExpr](JSIdent(sym.name))(qualifier => { scope.resolveQualifier(qualifier).visited = true JSIdent(qualifier).member(sym.name) }) if (requireActualCls && !sym.isPlainJSClass) trm.member("class") else trm } protected def translateVar(name: Str, isCallee: Bool)(implicit scope: Scope): JSExpr = translateVarImpl(name, isCallee).fold(throw _, identity) /** Try to retrieve a name from the scope, returning a Left value if the name is not found, * a Right value if it is found, and throwing an exception in case of unrecoverable error. */ protected def translateVarImpl(name: Str, isCallee: Bool)(implicit scope: Scope): Either[CodeGenError, JSExpr] = Right(scope.resolveValue(name) match { case S(sym: BuiltinSymbol) => sym.accessed = true if (!polyfill.used(sym.feature)) polyfill.use(sym.feature, sym.runtimeName) val ident = JSIdent(sym.runtimeName) if (sym.feature === "error") ident() else ident case S(sym: StubValueSymbol) => if (sym.accessible) JSIdent(sym.runtimeName) else throw new UnimplementedError(sym) case S(sym: ValueSymbol) => if (sym.isByvalueRec.getOrElse(false) && !sym.isLam) throw CodeGenError(s"unguarded recursive use of by-value binding $name") sym.visited = true val ident = JSIdent(sym.runtimeName) if (sym.isByvalueRec.isEmpty && !sym.isLam) ident() else ident case S(sym: NuTypeSymbol) => translateNuTypeSymbol(sym, isCallee) // `isCallee` is true in a `new` expression, which requires the actual class case S(sym: NewClassMemberSymbol) => if (sym.isByvalueRec.getOrElse(false) && !sym.isLam) throw CodeGenError(s"unguarded recursive use of by-value binding $name") sym.qualifier.fold[JSExpr](throw CodeGenError(s"unqualified member symbol $sym"))(qualifier => { sym.visited = true scope.resolveQualifier(qualifier).visited = true val ident = if (sym.isPrivate) JSIdent(s"${qualifier}.#${sym.name}") else JSIdent(qualifier).member(sym.name) if (sym.isByvalueRec.isEmpty && !sym.isLam) ident() else ident }) case S(sym: ClassSymbol) => if (isCallee || !oldDefs) JSNew(JSIdent(sym.runtimeName)) else JSArrowFn(JSNamePattern("x") :: Nil, L(JSNew(JSIdent(sym.runtimeName))(JSIdent("x")))) case S(sym: TraitSymbol) => if (oldDefs) JSIdent(sym.lexicalName)("build") else return Left(CodeGenError(s"trait used in term position")) case N => scope.getType(name) match { case S(sym: TypeAliasSymbol) => return Left(CodeGenError(s"type alias ${name} is not a valid expression")) case S(_) => lastWords("register mismatch in scope") case N => return Left(CodeGenError(s"unresolved symbol ${name}")) } }) /** * Handle all possible cases of MLscript function applications. We extract * this method to prevent exhaustivity check from reaching recursion limit. */ protected def translateApp(term: App)(implicit scope: Scope): JSExpr = term match { // Binary expressions case App(App(Var(op), Tup((N -> Fld(_, lhs)) :: Nil)), Tup((N -> Fld(_, rhs)) :: Nil)) if oldDefs && (JSBinary.operators contains op) => JSBinary(op, translateTerm(lhs), translateTerm(rhs)) // Binary expressions with new-definitions case App(Var(op), Tup(N -> Fld(_, lhs) :: N -> Fld(_, rhs) :: Nil)) // JS doesn't support operators like `+.` so we need to map them before testing if JSBinary.operators.contains(mapFloatingOperator(op)) && (!translateVarImpl(op, isCallee = true).isRight || op =/= mapFloatingOperator(op)) => JSBinary(mapFloatingOperator(op), translateTerm(lhs), translateTerm(rhs)) // If-expressions case App(App(App(Var("if"), Tup((_, Fld(_, tst)) :: Nil)), Tup((_, Fld(_, con)) :: Nil)), Tup((_, Fld(_, alt)) :: Nil)) => JSTenary(translateTerm(tst), translateTerm(con), translateTerm(alt)) case App(App(App(Var("if"), tst), con), alt) => die // Function invocation case App(trm, Tup(args)) => val callee = trm match { case Var(nme) if oldDefs => scope.resolveValue(nme) match { case S(sym: NuTypeSymbol) => translateNuTypeSymbol(sym, false) // ClassName(params) case _ => translateVar(nme, true) // Keep this case for the legacy test cases } case _ => translateTerm(trm) } callee(args map { case (_, Fld(_, arg)) => translateTerm(arg) }: _*) case App(trm, splice) => ??? // TODO represents `trm(...splice)` case _ => throw CodeGenError(s"ill-formed application $term") } // * Generate an `App` node for AST constructors private def createASTCall(tp: Str, args: Ls[Term]): App = App(Var(tp), Tup(args.map(a => N -> Fld(FldFlags.empty, a)))) // * Bound free variables appearing in quasiquotes class FreeVars(val vs: Set[Str]) // * Left: the branch is quoted and it has been desugared // * Right: the branch is not quoted and quoted subterms have been desugared private def desugarQuotedBranch(branch: CaseBranches)( implicit scope: Scope, isQuoted: Bool, freeVars: FreeVars ): Either[Term, CaseBranches] = branch match { case cse @ Case(pat, body, rest) => val dp = desugarQuote(pat) val db = desugarQuote(body) desugarQuotedBranch(rest) match { case L(t) => L(createASTCall("Case", dp :: db :: t :: Nil)) case R(b) => dp match { case dp: SimpleTerm => R(Case(dp, db, b)(cse.refined)) case _ => die } } case Wildcard(body) => if (isQuoted) L(createASTCall("Wildcard", desugarQuote(body) :: Nil)) else R(Wildcard(desugarQuote(body))) case NoCases => if (isQuoted) L(createASTCall("NoCases", Nil)) else R(NoCases) } // * Operators `+`, `-`, and `*` will not be available for floating numbers until we have the correct overloading. // * Currently, we use OCaml-style floating operators temporarily and translate them into normal JS operators. private def mapFloatingOperator(op: Str) = op match { case "+." => "+" case "-." => "-" case "*." => "*" case _ => op } // * Desugar `Quoted` into AST constructor invocations. // * example 1: `` `42 `` is desugared into `IntLit(42)` // * example 2: `` x `=> id(x) `+ `1 `` is desugared into `let x1 = freshName("x") in Lam(Var(x1), App(Var("+"), id(Var(x1)), IntLit(1)))` private def desugarQuote(term: Term)(implicit scope: Scope, isQuoted: Bool, freeVars: FreeVars): Term = term match { case Var(name) => val isFreeVar = freeVars.vs(name) if (isQuoted || isFreeVar) { val runtimeName = scope.resolveValue(name).fold[Str]( throw CodeGenError(s"unbound free variable $name is not supported yet.") )(_.runtimeName) if (isFreeVar) createASTCall("Var", Var(runtimeName) :: Nil) // quoted variables else createASTCall("Var", StrLit(runtimeName) :: Nil) // built-in symbols (e.g., true, error) } else term case lit: IntLit => if (isQuoted) createASTCall("IntLit", lit :: Nil) else lit case lit: DecLit => if (isQuoted) createASTCall("DecLit", lit :: Nil) else lit case lit: StrLit => if (isQuoted) createASTCall("StrLit", lit :: Nil) else lit case lit: UnitLit => if (isQuoted) createASTCall("UnitLit", lit :: Nil) else lit case Lam(params, body) => if (isQuoted) { val lamScope = scope.derive("Lam") params match { case Tup(params) => val newfreeVars = params.map { case N -> Fld(_, Var(nme)) => lamScope.declareParameter(nme) nme -> lamScope.declareValue(nme, S(false), false, N).runtimeName case S(Var(nme)) -> _ => lamScope.declareParameter(nme) nme -> lamScope.declareValue(nme, S(false), false, N).runtimeName case p => throw CodeGenError(s"parameter $p is not supported in quasiquote") } newfreeVars.foldRight(desugarQuote(body)(lamScope, isQuoted, new FreeVars(freeVars.vs ++ newfreeVars.map(_._1))))((p, res) => Let(false, Var(p._2), createASTCall("freshName", StrLit(p._1) :: Nil), createASTCall("Lam", createASTCall("Var", Var(p._2) :: Nil) :: res :: Nil))) case _ => throw CodeGenError(s"term $params is not a valid parameter list") } } else Lam(params, desugarQuote(body)) case Unquoted(body) => if (isQuoted) { val unquoteScope = scope.derive("unquote") desugarQuote(body)(unquoteScope, false, freeVars) } else throw CodeGenError("unquoted term should be wrapped by quotes.") case Quoted(body) => val quoteScope = scope.derive("quote") val res = desugarQuote(body)(quoteScope, true, freeVars) if (isQuoted) throw CodeGenError("nested quotation is not allowed.") else res case App(Var(op), Tup(N -> Fld(f1, lhs) :: N -> Fld(f2, rhs) :: Nil)) if JSBinary.operators.contains(mapFloatingOperator(op)) && (!translateVarImpl(op, isCallee = true).isRight || op =/= mapFloatingOperator(op)) => if (isQuoted) createASTCall("App", createASTCall("Var", StrLit(mapFloatingOperator(op)) :: Nil) :: desugarQuote(lhs) :: desugarQuote(rhs) :: Nil) else App(Var(op), Tup(N -> Fld(f1, desugarQuote(lhs)) :: N -> Fld(f2, desugarQuote(rhs)) :: Nil)) case App(lhs, rhs) => if (isQuoted) createASTCall("App", desugarQuote(lhs) :: desugarQuote(rhs) :: Nil) else App(desugarQuote(lhs), desugarQuote(rhs)) case Rcd(fields) => if (isQuoted) createASTCall("Rcd", fields.flatMap(f => createASTCall("Var", StrLit(f._1.name) :: Nil) :: desugarQuote(f._2.value) :: Nil)) else Rcd(fields.map(f => (f._1, Fld(f._2.flags, desugarQuote(f._2.value))))) case Bra(rcd, trm) => if (isQuoted) createASTCall("Bra", desugarQuote(trm) :: Nil) else Bra(rcd, desugarQuote(trm)) case Sel(receiver, f @ Var(name)) => if (isQuoted) createASTCall("Sel", desugarQuote(receiver) :: createASTCall("Var", StrLit(name) :: Nil) :: Nil) else Sel(desugarQuote(receiver), f) case Let(rec, Var(name), value, body) => val letScope = scope.derive("Let") if (isQuoted) { letScope.declareParameter(name) val freshedName = letScope.declareValue(name, S(false), false, N).runtimeName Let(false, Var(freshedName), createASTCall("freshName", StrLit(name) :: Nil), createASTCall("Let", createASTCall("Var", Var(freshedName) :: Nil) :: desugarQuote(value) :: desugarQuote(body)(letScope, isQuoted, new FreeVars(freeVars.vs ++ (name :: Nil))) :: Nil )) } else Let(rec, Var(name), desugarQuote(value), desugarQuote(body)(letScope, isQuoted, freeVars)) case Blk(stmts) => val blkScope = scope.derive("blk") if (isQuoted) createASTCall("Blk", stmts.map { case t: Term => desugarQuote(t)(blkScope, isQuoted, freeVars) case s => throw CodeGenError(s"statement $s is not supported in quasiquotes") }) else Blk(stmts.map { case t: Term => desugarQuote(t)(blkScope, isQuoted, freeVars) case s => desugarStatementInUnquote(s)(blkScope, freeVars) }) case Tup(eles) => def toVar(b: Bool) = if (b) Var("true") else Var("false") def toVars(flg: FldFlags) = toVar(flg.mut) :: toVar(flg.spec) :: toVar(flg.genGetter) :: Nil if (isQuoted) createASTCall("Tup", eles flatMap { case S(Var(name)) -> Fld(flags, t) => createASTCall("Var", Var(name) :: Nil) :: createASTCall("Fld", desugarQuote(t) :: toVars(flags)) :: Nil case N -> Fld(flags, t) => createASTCall("Fld", desugarQuote(t) :: toVars(flags)) :: Nil }) else Tup(eles.map { case v -> Fld(flags, t) => v -> Fld(flags, desugarQuote(t)) }) case Subs(arr, idx) => if (isQuoted) createASTCall("Subs", desugarQuote(arr) :: desugarQuote(idx) :: Nil) else Subs(desugarQuote(arr), desugarQuote(idx)) case Asc(trm, ty) => if (isQuoted) desugarQuote(trm) else Asc(desugarQuote(trm), ty) case With(lhs, rhs @ Rcd(fields)) => if (isQuoted) createASTCall("With", desugarQuote(lhs) :: desugarQuote(rhs) :: Nil) else With(desugarQuote(lhs), Rcd(fields.map(f => (f._1, Fld(f._2.flags, desugarQuote(f._2.value)))))) case CaseOf(trm, cases) => desugarQuotedBranch(cases) match { case L(t) => createASTCall("CaseOf", desugarQuote(trm) :: t :: Nil) case R(b) => CaseOf(desugarQuote(trm), b) } case _ if term.desugaredTerm.isDefined => desugarQuote(term.desugaredTerm.getOrElse(die)) case Assign(lhs, rhs) if !isQuoted => Assign(desugarQuote(lhs), desugarQuote(rhs)) case NuNew(cls) if !isQuoted => NuNew(desugarQuote(cls)) case TyApp(lhs, targs) if !isQuoted => TyApp(desugarQuote(lhs), targs) case Forall(p, body) if !isQuoted => Forall(p, desugarQuote(body)) case Inst(body) if !isQuoted => Inst(desugarQuote(body)) case _: Super if !isQuoted => term case Eqn(lhs, rhs) if !isQuoted => Eqn(lhs, desugarQuote(rhs)) case Ann(ann, receiver) => Ann(desugarQuote(ann), desugarQuote(receiver)) case While(cond, body) if !isQuoted => While(desugarQuote(cond), desugarQuote(body)) case _: Bind | _: Test | _: If | _: Splc | _: Where | _: AdtMatchWith | _: Rft | _: New | _: Assign | _: NuNew | _: TyApp | _: Forall | _: Inst | _: Super | _: Eqn | _: While => throw CodeGenError("this quote syntax is not supported yet.") } // * Statements inside **Unquote** can refer to quoted code fragments. // * Desugar them recursively. private def desugarStatementInUnquote(s: Statement)(implicit scope: Scope, freeVars: FreeVars): Statement = { implicit val isQuoted: Bool = false s match { case nd @ NuFunDef(isLetRec, nme, symbol, tparams, rhs) => NuFunDef(isLetRec, nme, symbol, tparams, rhs match { case L(t) => L(desugarQuote(t)) case R(t) => R(t) })(nd.declareLoc, nd.virtualLoc, nd.mutLoc, nd.signature, nd.outer, nd.genField, nd.annotations) case nt @ NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, superAnnot, thisAnnot, TypingUnit(body)) => NuTypeDef(kind, nme, tparams, params, ctor.map(c => desugarStatementInUnquote(c) match { case c: Constructor => c case _ => die }), sig, parents.map(p => desugarQuote(p)), superAnnot, thisAnnot, TypingUnit(body.map(s => desugarStatementInUnquote(s))))(nt.declareLoc, nt.abstractLoc, nt.annotations) case Constructor(ps, body) => Constructor(ps, desugarQuote(body) match { case b: Blk => b case _ => die }) case t: Term => desugarQuote(t) case _: LetS | _: DataDefn | _: DatatypeDefn | _: TypeDef | _: Def => die // * Impossible. newDef is true } } /** * Translate MLscript terms into JavaScript expressions. */ protected def translateTerm(term: Term)(implicit scope: Scope): JSExpr = term match { case _ if term.desugaredTerm.isDefined => translateTerm(term.desugaredTerm.getOrElse(die)) case Var(name) => translateVar(name, false) case Super() => JSIdent("super") case Lam(params, body) => val lamScope = scope.derive("Lam") val patterns = translateParams(params)(lamScope) JSArrowFn(patterns, lamScope.tempVars `with` translateTerm(body)(lamScope)) case t: App => translateApp(t) case Rcd(fields) => JSRecord(fields map { case (key, Fld(_, value)) => key.name -> translateTerm(value) }) case Sel(receiver, fieldName) => JSField(translateTerm(receiver), fieldName.name) // Turn let into an IIFE. case Let(true, Var(name), Lam(args, body), expr) => val letScope = scope.derive("Let") val runtimeName = letScope.declareParameter(name) val fn = { val fnScope = letScope.derive("Function") val params = translateParams(args)(fnScope) val fnBody = fnScope.tempVars.`with`(translateTerm(body)(fnScope)) JSFuncExpr(S(runtimeName), params, fnBody.fold(_.`return` :: Nil, identity)) } JSImmEvalFn( N, JSNamePattern(runtimeName) :: Nil, letScope.tempVars.`with`(translateTerm(expr)(letScope)), fn :: Nil ) case Let(true, Var(name), _, _) => throw new CodeGenError(s"recursive non-function definition $name is not supported") case Let(_, Var(name), value, body) => val letScope = scope.derive("Let") val runtimeName = letScope.declareParameter(name) JSImmEvalFn( N, JSNamePattern(runtimeName) :: Nil, letScope.tempVars `with` translateTerm(body)(letScope), translateTerm(value) :: Nil ) case Blk(stmts) => val blkScope = scope.derive("Blk") val flattened = stmts.iterator.flatMap { case nt: NuTypeDef => nt :: Nil case nf @ NuFunDef(_, Var(nme), symNme, _, _) => val symb = symNme.map(_.name) blkScope.declareStubValue(nme, symb)(true) nf.desugared._2 case other => other.desugared._2 }.toList JSImmEvalFn( N, Nil, R(blkScope.tempVars `with` (flattened.iterator.zipWithIndex.map { case (t: Term, index) if index + 1 == flattened.length => translateTerm(t)(blkScope).`return` case (t: Term, index) => JSExprStmt(translateTerm(t)(blkScope)) case (NuFunDef(isLetRec, Var(nme), symNme, _, L(rhs)), _) => val symb = symNme.map(_.name) val isLocalFunction = isLetRec.isEmpty || rhs.isLam val pat = blkScope.declareValue(nme, isLetRec, isLocalFunction, symb) JSLetDecl(Ls(pat.runtimeName -> S(translateTerm(rhs)(blkScope)))) case (nt: NuTypeDef, _) => translateLocalNewType(nt)(blkScope) // TODO: find out if we need to support this. case (_: Def | _: TypeDef | _: NuFunDef | _: DataDefn | _: DatatypeDefn | _: LetS | _: Constructor, _) => throw CodeGenError("unsupported definitions in blocks") }.toList)), Nil ) // Pattern match with only one branch -> comma expression case CaseOf(trm, Wildcard(default)) => JSCommaExpr(translateTerm(trm) :: translateTerm(default) :: Nil) // Pattern match with two branches -> tenary operator case CaseOf(trm, cs @ Case(tst, csq, Wildcard(alt))) => translateCase(translateTerm(trm), tst)(scope)(translateTerm(csq), translateTerm(alt)) // Pattern match with more branches -> chain of ternary expressions with cache case CaseOf(trm, cases) => val arg = translateTerm(trm) if (arg.isSimple) { translateCaseBranch(arg, cases) } else { val name = scope.declareRuntimeSymbol() scope.tempVars += name val ident = JSIdent(name) JSCommaExpr(JSAssignExpr(ident, arg) :: translateCaseBranch(ident, cases) :: Nil) } case IntLit(value) => JSLit(value.toString + (if (JSBackend isSafeInteger value) "" else "n")) case DecLit(value) => JSLit(value.toString) case StrLit(value) => JSExpr(value) case UnitLit(value) => JSLit(if (value) "undefined" else "null") // `Asc(x, ty)` <== `x: Type` case Asc(trm, _) => translateTerm(trm) // `c with { x = "hi"; y = 2 }` case With(trm, Rcd(fields)) => JSInvoke( JSIdent(polyfill get "withConstruct" match { case S(fnName) => fnName case N => polyfill.use("withConstruct", topLevelScope.declareRuntimeSymbol("withConstruct")) }), translateTerm(trm) :: JSRecord(fields map { case (Var(name), Fld(_, value)) => name -> translateTerm(value) }) :: Nil ) // Only parenthesize binary operators // Custom operators do not need special handling since they are desugared to plain methods case Bra(false, trm) => trm match { case App(Var(op), _) if JSBinary.operators.contains(op) => JSParenthesis(translateTerm(trm)) case trm => translateTerm(trm) } case Bra(_, trm) => translateTerm(trm) case Tup(terms) => JSArray(terms map { case (_, Fld(_, term)) => translateTerm(term) }) case Subs(arr, idx) => JSMember(translateTerm(arr), translateTerm(idx)) case While(cond, body) => JSImmEvalFn(N, Nil, R(JSWhileStmt(translateTerm(cond), translateTerm(body)) :: Nil), Nil) case Assign(lhs, value) => lhs match { case _: Subs | _: Sel | _: Var => JSUnary("void", JSAssignExpr(translateTerm(lhs), translateTerm(value))) case _ => throw CodeGenError(s"illegal assignemnt left-hand side: $lhs") } case Inst(bod) => translateTerm(bod) case iff: If => throw CodeGenError(s"if expression was not desugared") case NuNew(cls) => // * The following logic handles the case when `new C(123)` needs to be translated to `new C.class(123)` cls match { case Var(className) => translateVar(className, isCallee = true) match { case n: JSNew => n case t => JSNew(t) } case _ => throw CodeGenError(s"Unsupported `new` class term: $cls") } // * Would not be quite correct: // JSNew(translateTerm(cls)) case New(N, TypingUnit(Nil)) => JSRecord(Nil) case New(S(TypeName(className) -> Tup(args)), TypingUnit(Nil)) => val callee = translateVar(className, true) match { case n: JSNew => n case t => JSNew(t) } callee(args.map { case (_, Fld(_, arg)) => translateTerm(arg) }: _*) case New(_, TypingUnit(_)) => throw CodeGenError("custom class body is not supported yet") case Forall(_, bod) => translateTerm(bod) case TyApp(base, _) => translateTerm(base) case Eqn(Var(name), _) => throw CodeGenError(s"assignment of $name is not supported outside a constructor") case Quoted(body) => val quotedScope = scope.derive("quote") translateTerm(desugarQuote(body)(quotedScope, true, new FreeVars(Set.empty)))(quotedScope) case Ann(ann, receiver) => translateTerm(receiver) case _: Bind | _: Test | _: If | _: Splc | _: Where | _: AdtMatchWith | _: Rft | _: Unquoted => throw CodeGenError(s"cannot generate code for term $term") } private def translateCaseBranch(scrut: JSExpr, branch: CaseBranches)(implicit scope: Scope ): JSExpr = branch match { case Case(pat, body, rest) => translateCase(scrut, pat)(scope)(translateTerm(body), translateCaseBranch(scrut, rest)) case Wildcard(body) => translateTerm(body) case NoCases => JSImmEvalFn(N, Nil, R(JSInvoke( JSNew(JSIdent("Error")), JSExpr("non-exhaustive case expression") :: Nil ).`throw` :: Nil), Nil) } private def translateCase(scrut: JSExpr, pat: SimpleTerm)(implicit scope: Scope) = { JSTenary( pat match { case Var("int") => JSInvoke(JSField(JSIdent("Number"), "isInteger"), scrut :: Nil) case Var("Int") if !oldDefs => JSInvoke(JSField(JSIdent("Number"), "isInteger"), scrut :: Nil) case Var("Num") if !oldDefs => JSBinary("===", scrut.typeof(), JSLit(JSLit.makeStringLiteral("number"))) case Var("Bool") if !oldDefs => JSBinary("===", scrut.typeof(), JSLit(JSLit.makeStringLiteral("boolean"))) case Var("Str") if !oldDefs => JSBinary("===", scrut.typeof(), JSLit(JSLit.makeStringLiteral("string"))) case Var("bool") => JSBinary("===", scrut.member("constructor"), JSLit("Boolean")) case Var(s @ ("true" | "false")) => JSBinary("===", scrut, JSLit(s)) case Var("string") => // JS is dumb so `instanceof String` won't actually work on "primitive" strings... JSBinary("===", scrut.member("constructor"), JSLit("String")) case Var(name) => scope.resolveValue(name) match { case S(sym: NewClassSymbol) => JSInstanceOf(scrut, translateNuTypeSymbol(sym, true)) // a is case ClassName(params) -> a instanceof ClassName.class case S(sym: ModuleSymbol) => JSInstanceOf(scrut, translateNuTypeSymbol(sym, true)) case _ => topLevelScope.getType(name) match { case S(ClassSymbol(_, runtimeName, _, _, _)) => JSInstanceOf(scrut, JSIdent(runtimeName)) case S(TraitSymbol(_, runtimeName, _, _, _)) => JSIdent(runtimeName)("is")(scrut) case S(_: TypeAliasSymbol) => throw new CodeGenError(s"cannot match type alias $name") case _ => throw new CodeGenError(s"unknown match case: $name") } } case lit: Lit => JSBinary("===", scrut, translateTerm(lit)) }, _, _ ) } protected def translateTraitDeclaration( traitSymbol: TraitSymbol )(implicit scope: Scope): JSConstDecl = { import JSCodeHelpers._ val instance = id("instance") val bases = traitSymbol.body.collectTypeNames.flatMap { name => topLevelScope.getType(name) match { case S(t: TraitSymbol) => S(id(t.runtimeName)("implement")(instance).stmt) case S(_: ClassSymbol) | S(_: TypeSymbol) | N => N } } val members = traitSymbol.methods.map { method => val name = method.nme.name val define = method.rhs.value match { // Define methods for functions. case Lam(params, body) => val methodScope = scope.derive(s"Method $name") val methodParams = translateParams(params)(methodScope) methodScope.declareValue("this", Some(false), false, N) instance(name) := JSFuncExpr( N, methodParams, `return`(translateTerm(body)(methodScope)) :: Nil ) // Define getters for pure expressions. case term => val getterScope = scope.derive(s"Getter $name") getterScope.declareValue("this", Some(false), false, N) id("Object")("defineProperty")( instance, JSExpr(name), JSRecord( "get" -> JSFuncExpr( N, Nil, `return`(translateTerm(term)(getterScope)) :: Nil ) :: Nil ) ).stmt } JSIfStmt( JSExpr(name).binary("in", instance).unary("!"), define :: Nil, ) } val implement = JSFuncExpr( S("implement"), param("instance") :: Nil, JSIfStmt( id("tag").binary("in", instance), `return`() :: Nil, ) :: id("Object")("defineProperty")( instance, id("tag"), JSRecord("value" -> JSRecord(Nil) :: Nil) ).stmt :: members ::: bases ) // function build(instance) { // if (typeof instance !== "object") { // instance = Object.assign(instance, {}); // } // this.implement(instance); // return instance; // } val build = JSFuncExpr( S("build"), param("instance") :: Nil, JSIfStmt( instance.typeof().binary("!==", JSExpr("object")), (instance := id("Object")("assign")(instance, JSRecord(Nil))) :: Nil ) :: id("this")("implement")(instance).stmt :: `return`(instance) :: Nil ) val is = JSFuncExpr( S("is"), param("x") :: Nil, `return`( id("x").typeof() .binary("===", JSExpr("object")) .binary("&&", id("x").binary("!==", JSLit("null"))) .binary("&&", id("tag").binary("in", id("x"))) ) :: Nil ) const( traitSymbol.runtimeName, JSFuncExpr( N, Nil, Ls( const("tag", id("Symbol")()), `return` { JSRecord("implement" -> implement :: "build" -> build :: "is" -> is :: Nil) } ) )() ) } /** * Translate MLscript class declaration to JavaScript class declaration. * First, we will analyze its fields and base class name. * Then, we will check if the base class exists. */ protected def translateClassDeclaration( classSymbol: ClassSymbol, baseClassSymbol: Opt[ClassSymbol] )(implicit scope: Scope): JSClassDecl = { // Translate class methods and getters. val classScope = scope.derive(s"class ${classSymbol.lexicalName}") val members = classSymbol.methods.flatMap { translateClassMember(_)(classScope) } // Collect class fields. val fields = classSymbol.body.collectFields ++ classSymbol.body.collectTypeNames.flatMap(resolveTraitFields) val base = baseClassSymbol.map { sym => JSIdent(sym.runtimeName) } val traits = classSymbol.body.collectTypeNames.flatMap { name => scope.getType(name) match { case S(TraitSymbol(_, runtimeName, _, _, _)) => S(runtimeName) case S(_: ClassSymbol) => N case S(_: TypeSymbol) => N case N => N } } JSClassDecl(classSymbol.runtimeName, fields, base, members, traits) } protected def translateQualifierDeclaration(qualifier: ValueSymbol): Ls[JSStmt] = if (qualifier.visited) JSConstDecl(qualifier.runtimeName, JSIdent("this")) :: Nil else Nil protected def addNuTypeToGlobalThis(typeDef: NuTypeDef, moduleName: Str) = { import JSCodeHelpers._ typeDef match { case NuTypeDef(Mxn, TypeName(nme), _, _, _, _, _, _, _, _) => JSAssignExpr(id("globalThis").member(nme), JSArrowFn(param("base") :: Nil, L( JSInvoke(id(moduleName).member(nme), id("base") :: Nil) ))).stmt case NuTypeDef(_, TypeName(nme), _, _, _, _, _, _, _, _) => JSAssignExpr(id("globalThis").member(nme), id(moduleName).member(nme)).stmt } } protected def translateLocalNewType(typeDef: NuTypeDef)(implicit scope: Scope): JSConstDecl = { // TODO: support traitSymbols val (traitSymbols, classSymbols, mixinSymbols, moduleSymbols) = declareNewTypeDefs(typeDef :: Nil, N) val sym = classSymbols match { case s :: _ => S(s) case Nil => mixinSymbols match { case s :: _ => S(s) case Nil => moduleSymbols match { case s :: _ => S(s) case _ => N } } } sym match { case S(sym: NewClassSymbol) => val localScope = scope.derive(s"local ${sym.name}") val nd = translateNewTypeDefinition(sym, N, false)(localScope) val ctorMth = localScope.declareValue("ctor", Some(false), false, N).runtimeName val (constructor, params) = translateNewClassParameters(nd) val initList = if (sym.isPlainJSClass) Ls(JSReturnStmt(S(JSIdent(sym.name)))) else Ls( JSLetDecl.from(Ls(ctorMth)), JSAssignExpr(JSIdent(ctorMth), JSArrowFn(constructor, L(JSInvoke(JSNew(JSIdent(sym.name)), params)))).stmt, JSExprStmt(JSAssignExpr(JSIdent(ctorMth).member("class"), JSIdent(sym.name))), JSReturnStmt(S(JSIdent(ctorMth))) ) JSConstDecl(sym.name, JSImmEvalFn( N, Nil, R(nd :: initList), Nil )) case S(sym: MixinSymbol) => val localScope = scope.derive(s"local ${sym.name}") val base = localScope.declareValue("base", Some(false), false, N) val nd = translateNewTypeDefinition(sym, S(base), false)(localScope) JSConstDecl(sym.name, JSArrowFn( Ls(JSNamePattern(base.runtimeName)), R(Ls( JSReturnStmt(S(JSClassExpr(nd))) )) )) case S(sym: ModuleSymbol) => val localScope = scope.derive(s"local ${sym.name}") val nd = translateNewTypeDefinition(sym, N, false)(localScope) val ins = localScope.declareValue("ins", Some(false), false, N).runtimeName JSConstDecl(sym.name, JSImmEvalFn( N, Nil, R(Ls( nd, JSLetDecl.from(Ls(ins)), JSAssignExpr(JSIdent(ins), JSInvoke(JSNew(JSIdent(sym.name)), Nil)).stmt, JSExprStmt(JSAssignExpr(JSIdent(ins).member("class"), JSIdent(sym.name))), JSReturnStmt(S(JSIdent(ins))) )), Nil )) case _ => throw CodeGenError(s"unsupported NuTypeDef in local blocks: $typeDef") } } protected def translateMixinDeclaration( mixinSymbol: MixinSymbol, siblingsMembers: Ls[RuntimeSymbol] )(implicit getterScope: Scope): JSClassMethod = { val base = getterScope.declareValue("base", Some(false), false, N) val classBody = translateNewTypeDefinition(mixinSymbol, S(base), false)(getterScope) val qualifierStmt = mixinSymbol.qualifier.fold[JSConstDecl](die)(qualifier => JSConstDecl(qualifier, JSIdent("this"))) JSClassMethod(mixinSymbol.name, Ls(JSNamePattern(base.runtimeName)), R((qualifierStmt :: Nil) ::: Ls(JSReturnStmt(S(JSClassExpr(classBody))) )) ) } private def translateParents(superFields: Ls[Term], constructorScope: Scope)(implicit scope: Scope): Opt[JSExpr] = { def translateParent(current: Term, base: JSExpr, mixinOnly: Bool): JSExpr = { def resolveName(term: Term): Str = term match { case App(lhs, _) => resolveName(lhs) case Var(name) => name case Sel(_, Var(fieldName)) => fieldName case TyApp(lhs, _) => resolveName(lhs) case _ => throw CodeGenError("unsupported parents.") } val name = resolveName(current) scope.resolveValue(name) match { case Some(_: TraitSymbol) => base // TODO: case Some(sym: MixinSymbol) => JSInvoke(translateNuTypeSymbol(sym, true), Ls(base)) // class D() extends B -> class D extends B.class case Some(sym: NuTypeSymbol) if !mixinOnly => translateNuTypeSymbol(sym, true) case Some(t) => throw CodeGenError(s"unexpected parent symbol $t.") case N => throw CodeGenError(s"unresolved parent $name.") } } // for non-first parent classes, they must be mixins or we would get more than one parent classes, // which is not allowed in JS superFields match { case head :: tail => S(tail.foldLeft( translateParent(head, JSIdent("Object"), false) )((res, next) => translateParent(next, res, true))) case Nil => N } } protected def translateTopModuleDeclaration( moduleSymbol: ModuleSymbol, keepTopLevelScope: Bool )(implicit scope: Scope): JSClassNewDecl = translateNewTypeDefinition(moduleSymbol, N, keepTopLevelScope) protected def translateModuleDeclaration( moduleSymbol: ModuleSymbol, siblingsMembers: Ls[RuntimeSymbol] )(implicit getterScope: Scope): JSClassGetter = { val decl = translateNewTypeDefinition(moduleSymbol, N, false)(getterScope) val privateIdent = JSIdent(s"this.#${moduleSymbol.name}") val qualifierStmt = moduleSymbol.qualifier.fold[JSConstDecl](die)(qualifier => JSConstDecl(qualifier, JSIdent("this"))) JSClassGetter(moduleSymbol.name, R((qualifierStmt :: Nil) ::: Ls( JSIfStmt(JSBinary("===", privateIdent, JSIdent("undefined")), Ls( decl, JSExprStmt(JSAssignExpr(privateIdent, JSNew(JSInvoke(JSIdent(moduleSymbol.name), Nil)))), JSExprStmt(JSAssignExpr(privateIdent.member("class"), JSIdent(moduleSymbol.name))), )), JSReturnStmt(S(privateIdent)) ))) } protected def translateNewClassParameters(classBody: JSClassNewDecl) = { val constructor = classBody.ctorParams.map(JSNamePattern(_)) val params = classBody.ctorParams.map(JSIdent(_)) (constructor, params) } protected def translateNewClassDeclaration( classSymbol: NewClassSymbol, siblingsMembers: Ls[RuntimeSymbol] )(implicit getterScope: Scope): JSClassGetter = { val classBody = translateNewTypeDefinition(classSymbol, N, false)(getterScope) val (constructor, params) = translateNewClassParameters(classBody) val privateIdent = JSIdent(s"this.#${classSymbol.name}") val qualifierStmt = classSymbol.qualifier.fold[JSConstDecl](die)(qualifier => JSConstDecl(qualifier, JSIdent("this"))) val initList = if (classSymbol.isPlainJSClass) Ls(JSExprStmt(JSAssignExpr(privateIdent, JSIdent(classSymbol.name)))) else Ls( JSExprStmt(JSAssignExpr(privateIdent, JSArrowFn(constructor, L( JSInvoke(JSIdent("Object").member("freeze"), Ls(JSInvoke(JSNew(JSIdent(classSymbol.name)), params))) )))), JSExprStmt(JSAssignExpr(privateIdent.member("class"), JSIdent(classSymbol.name))), JSExprStmt(JSAssignExpr(privateIdent.member("unapply"), JSIdent(classSymbol.name).member("unapply"))) ) JSClassGetter(classSymbol.name, R(qualifierStmt :: Ls( JSIfStmt(JSBinary("===", privateIdent, JSIdent("undefined")), JSExprStmt(JSClassExpr(classBody)) :: initList), JSReturnStmt(S(privateIdent)) ))) } protected def translateNewTypeDefinition( sym: TypeSymbol with NuTypeSymbol, baseSym: Opt[ValueSymbol], keepTopLevelScope: Bool )(implicit scope: Scope): JSClassNewDecl = { // * nuTypeScope: root scope // ** inheritanceScope: contains specialized parameters for `super(...)` // ** bodyScope: contains the part of the class between the `{...}` // *** constructorScope: contains variables in the ctor statements // *** memberScopes: contains member methods and variables val nuTypeScope = scope.derive(sym.toString) val inheritanceScope = nuTypeScope.derive(s"${sym.name} inheritance") val bodyScope = nuTypeScope.derive(s"${sym.name} body") val constructorScope = bodyScope.derive(s"${sym.name} constructor") val memberList = ListBuffer[RuntimeSymbol]() // pass to the getter of nested types val typeList = ListBuffer[Str]() // Store the scope for each member and the qualifier's name in the corresponding scope // Avoid `m._1` or `m._2` in the following code final case class QualifierPack(memberScope: Scope, qualifier: Str) val qualifierName = "qualifier" val memberScopes = (sym.nested.map(nd => { val memberScope = bodyScope.derive(s"member ${nd.name}") val sym = memberScope.declareQualifierSymbol(qualifierName) nd.name -> QualifierPack(memberScope, sym) }) ++ sym.methods.map(m => { val memberScope = bodyScope.derive(s"member ${m.nme.name}") val sym = memberScope.declareQualifierSymbol(qualifierName) m.nme.name -> QualifierPack(memberScope, sym) })).toMap // `qualifier` should always be the first value in the getter scope so all qualifiers should have the same name! val qualifier = memberScopes.values.headOption.fold(S(constructorScope.declareQualifierSymbol(qualifierName)))(mh => { memberScopes.values.foreach(m => assert(m.qualifier === mh.qualifier, s"the expected qualifier's runtime name should be ${mh.qualifier}, ${m.qualifier} found") ) assert(constructorScope.declareQualifierSymbol(mh.qualifier) === mh.qualifier) S(mh.qualifier) }) val fields = sym.matchingFields ++ sym.body.collectTypeNames.flatMap(resolveTraitFields) val getters = new ListBuffer[Bool -> Str]() // mut -> name val ctorParams = sym.ctorParams.fold( fields.map { f => memberList += NewClassMemberSymbol(f, Some(false), false, !sym.publicCtors.contains(f), qualifier).tap(bodyScope.register) inheritanceScope.declareValue(f, Some(false), false, N).runtimeName constructorScope.declareValue(f, Some(false), false, N).runtimeName } )(lst => lst.map { p => if (p._2) { // `constructor(val name)` will also generate a field and a getter memberList += NewClassMemberSymbol(p._1, Some(false), false, false, qualifier).tap(bodyScope.register) getters += false -> p._1 } constructorScope.declareValue(p._1, Some(false), false, N).runtimeName // Otherwise, it is only available in the constructor }) val initFields = getters.toList.map { case (mut, name) => JSAssignExpr(JSIdent(s"this.#$name"), JSIdent(name)).stmt } sym.methods.foreach( md => memberList += NewClassMemberSymbol(md.nme.name, N, true, false, qualifier).tap(bodyScope.register) ) sym.signatures.foreach( md => memberList += bodyScope.declareStubValue(md.nme.name, N)(true) ) sym.ctor.foreach { case nd @ NuFunDef(rec, Var(nme), _, _, _) => memberList += NewClassMemberSymbol(nme, rec, false, !nd.genField, qualifier).tap(bodyScope.register) case _ => () } // TODO: support traitSymbols val (traitSymbols, classSymbols, mixinSymbols, moduleSymbols) = declareNewTypeDefs(sym.nested, qualifier)(bodyScope) if (keepTopLevelScope) // also declare in the top level for diff tests declareNewTypeDefs(sym.nested, N)(topLevelScope) classSymbols.foreach(s => {memberList += s; typeList += s.name}) mixinSymbols.foreach(s => {memberList += s;}) moduleSymbols.foreach(s => {memberList += s; typeList += s.name}) val members = sym.methods.map(m => translateNewClassMember(m, fields, qualifier)(memberScopes.getOrElse(m.nme.name, die).memberScope)) ++ mixinSymbols.map(s => translateMixinDeclaration(s, memberList.toList)(memberScopes.getOrElse(s.name, die).memberScope)) ++ moduleSymbols.map(s => translateModuleDeclaration(s, memberList.toList)(memberScopes.getOrElse(s.name, die).memberScope)) ++ classSymbols.map(s => translateNewClassDeclaration(s, memberList.toList)(memberScopes.getOrElse(s.name, die).memberScope)) val base: Opt[JSExpr] = baseSym match { case Some(base) => S(JSIdent(base.runtimeName)) case _ => translateParents(sym.superParameters, inheritanceScope) } val traits = sym.body.collectTypeNames.flatMap { name => scope.getType(name) match { case S(TraitSymbol(_, runtimeName, _, _, _)) => S(runtimeName) case S(_: ClassSymbol) => N case S(_: TypeSymbol) => N case N => N } } val (superParameters, rest) = if (baseSym.isDefined) { val rest = constructorScope.declareValue("rest", Some(false), false, N) (Ls(JSIdent(s"...${rest.runtimeName}")), S(rest.runtimeName)) } else (sym.superParameters.map { case App(lhs, Tup(rhs)) => rhs map { case (_, Fld(_, trm)) => translateTerm(trm)(inheritanceScope) } case _ => Nil }.flatMap(_.reverse).reverse, N) val privateMems = new ListBuffer[Str]() val stmts = sym.ctor.flatMap { case Eqn(Var(name), rhs) => Ls( JSAssignExpr(JSIdent(s"this.#$name"), translateTerm(rhs)(constructorScope)).stmt, JSConstDecl(constructorScope.declareValue(name, S(false), false, N).runtimeName, JSIdent(s"this.#$name")) ) case s: Term => JSExprStmt(translateTerm(s)(constructorScope)) :: Nil case nd @ NuFunDef(_, Var(nme), _, _, Left(rhs)) => if (nd.genField) { getters += nd.isMut -> nme Ls[JSStmt]( JSExprStmt(JSAssignExpr(JSIdent(s"this.#$nme"), translateTerm(rhs)(constructorScope))), JSConstDecl(constructorScope.declareValue(nme, S(false), false, N).runtimeName, JSIdent(s"this.#$nme")) ) } else { val sym = bodyScope.resolveValue(nme) match { case Some(sym: NewClassMemberSymbol) => sym case _ => throw new AssertionError(s"error when handling $nme") } if (sym.visited || ctorParams.contains(nme)) { // This field is used in other methods, or it overrides the ctor parameter privateMems += nme Ls[JSStmt]( JSExprStmt(JSAssignExpr(JSIdent(s"this.#$nme"), translateTerm(rhs)(constructorScope))), JSConstDecl(constructorScope.declareValue(nme, S(false), false, N).runtimeName, JSIdent(s"this.#$nme")) ) } else JSConstDecl(constructorScope.declareValue(nme, S(false), false, N).runtimeName, translateTerm(rhs)(constructorScope)) :: Nil } case _ => Nil } val tempDecs = constructorScope.tempVars.emit() match { case S(decs) => decs :: Nil case _ => Nil } val staticMethods = sym.unapplyMtd match { // * Note: this code is a bad temporary hack until we have proper `unapply` desugaring case S(unapplyMtd) => unapplyMtd.rhs match { case Left(Lam(Tup(_ -> Fld(_, Var(nme)) :: Nil), Let(_, _, _, Tup(fields)))) => val unapplyScope = nuTypeScope.derive(s"unapply ${sym.name}") val ins = unapplyScope.declareParameter(nme) JSClassMethod("unapply", JSNamePattern(ins) :: Nil, L(JSArray(fields.map { case _ -> Fld(_, trm) => trm match { case Sel(Var(ins), Var(f)) => JSIdent(s"$ins.$f") case _ => translateTerm(trm) } }))) :: Nil case _ => throw CodeGenError(s"invalid unapply method in ${sym.name}") } case _ => Nil } val qualifierStmt = qualifier.fold[Ls[JSStmt]](Nil)(qualifier => translateQualifierDeclaration(constructorScope.resolveQualifier(qualifier))) JSClassNewDecl( sym.name, fields, fields.filter(sym.publicCtors.contains(_)).map(false -> _) ++ getters.toList, privateMems.toList ++ fields, base, superParameters, ctorParams, rest, members, traits, qualifierStmt ++ tempDecs ++ initFields ++ stmts, typeList.toList, sym.ctorParams.isDefined, staticMethods ) } /** * Translate class methods and getters. */ private def translateClassMember( method: MethodDef[Left[Term, Type]], )(implicit scope: Scope): Ls[JSClassMemberDecl] = { val name = method.nme.name // Create the method/getter scope. val memberScope = method.rhs.value match { case _: Lam => scope.derive(s"method $name") case _ => scope.derive(s"getter $name") } // Declare the alias for `this` before declaring parameters. val selfSymbol = memberScope.declareThisAlias() // Declare parameters. val (memberParams, body) = method.rhs.value match { case Lam(params, body) => val methodParams = translateParams(params)(memberScope) (S(methodParams), body) case term => (N, term) } // Translate class member body. val bodyResult = translateTerm(body)(memberScope).`return` // If `this` is accessed, add `const self = this`. val bodyStmts = if (selfSymbol.visited) { val thisDecl = JSConstDecl(selfSymbol.runtimeName, JSIdent("this")) R(thisDecl :: bodyResult :: Nil) } else { R(bodyResult :: Nil) } // Returns members depending on what it is. memberParams match { case S(memberParams) => JSClassMethod(name, memberParams, bodyStmts) :: Nil case N => JSClassGetter(name, bodyStmts) :: Nil } } private def translateNewClassMember( method: MethodDef[Left[Term, Type]], props: Ls[Str], // for overriding qualifier: Opt[Str] )(implicit memberScope: Scope): JSClassMemberDecl = { val name = method.nme.name val preDecs = props.map(p => { val runtime = memberScope.declareValue(p, Some(false), false, N) JSConstDecl(runtime.runtimeName, JSIdent(s"this.#$p")) }) // Declare parameters. val (memberParams, body) = method.rhs.value match { case Lam(params, body) => val methodParams = translateParams(params)(memberScope) (S(methodParams), body) case term => (N, term) } // Translate class member body. val bodyResult = translateTerm(body)(memberScope).`return` val tempDecs = memberScope.tempVars.emit() match { case S(decs) => decs :: Nil case _ => Nil } val qualifierStmts = qualifier.fold[Ls[JSStmt]](Nil)(qualifier => translateQualifierDeclaration(memberScope.resolveQualifier(qualifier))) val bodyStmts = R(preDecs ++ tempDecs ++ qualifierStmts ++ (bodyResult :: Nil)) // Returns members depending on what it is. memberParams match { case S(memberParams) => JSClassMethod(name, memberParams, bodyStmts) case N => JSClassGetter(name, bodyStmts) } } /** * Declare symbols for types, traits and classes. * Call this before the code generation. * * @return defined class symbols */ protected def declareTypeDefs(typeDefs: Ls[TypeDef]): (Ls[TraitSymbol], Ls[ClassSymbol]) = { val traits = new ListBuffer[TraitSymbol]() val classes = new ListBuffer[ClassSymbol]() typeDefs.foreach { case TypeDef(Als, TypeName(name), tparams, body, _, _, _, _) => topLevelScope.declareTypeAlias(name, tparams map { _.name }, body) case TypeDef(Trt, TypeName(name), tparams, body, _, methods, _, _) => traits += topLevelScope.declareTrait(name, tparams map { _.name }, body, methods) case TypeDef(Cls, TypeName(name), tparams, baseType, _, members, _, _) => classes += topLevelScope.declareClass(name, tparams map { _.name }, baseType, members) case TypeDef(Mxn, _, _, _, _, _, _, _) => throw CodeGenError("Mixins are not supported yet.") case TypeDef(Mod, _, _, _, _, _, _, _) => throw CodeGenError("Namespaces are not supported yet.") } (traits.toList, classes.toList) } protected def declareNewTypeDefs(typeDefs: Ls[NuTypeDef], qualifier: Opt[Str])(implicit scope: Scope): (Ls[TraitSymbol], Ls[NewClassSymbol], Ls[MixinSymbol], Ls[ModuleSymbol]) = { val traits = new ListBuffer[TraitSymbol]() val classes = new ListBuffer[NewClassSymbol]() val mixins = new ListBuffer[MixinSymbol]() val modules = new ListBuffer[ModuleSymbol]() def tt(trm: Term): Type = trm.toType match { case L(ds) => Top case R(ty) => ty } def prepare(nme: Str, fs: Ls[Opt[Var] -> Fld], pars: Ls[Term], unit: TypingUnit) = { val params = fs.map { case (S(nme), Fld(FldFlags(mut, spec, _), trm)) => val ty = tt(trm) nme -> Field(if (mut) S(ty) else N, ty) case (N, Fld(FldFlags(mut, spec, _), nme: Var)) => nme -> Field(if (mut) S(Bot) else N, Top) case _ => die } val publicCtors = fs.filter { case (_, Fld(flags, _)) => flags.genGetter case _ => false }.map { case (S(name), _) => name.name case (N, Fld(_, nme: Var)) => nme.name case _ => die } val body = pars.map(tt).foldRight(Record(params): Type)(Inter) val implemented = new HashSet[Str]() val members = unit.entities.collect { case NuFunDef(isLetRec, mnme, _, tys, Left(rhs)) if (isLetRec.isEmpty || isLetRec.getOrElse(false)) => implemented.add(mnme.name) MethodDef[Left[Term, Type]](isLetRec.getOrElse(false), TypeName(nme), mnme, tys, Left(rhs)) } val signatures = unit.entities.collect { case nd @ NuFunDef(isLetRec, mnme, _, tys, Right(rhs)) if nd.genField && !implemented.contains(mnme.name) => MethodDef[Right[Term, Type]](isLetRec.getOrElse(false), TypeName(nme), mnme, tys, Right(rhs)) } val stmts = unit.entities.filter { case Asc(Var("this"), _) => false case Asc(Super(), _) => false case NuFunDef(S(false), _, _, _, Left(rhs)) => true case _: Term => true case _ => false } val nested = unit.entities.collect { case nd: NuTypeDef => nd } (body, members, signatures, stmts, nested, publicCtors) } typeDefs.foreach { case td @ NuTypeDef(Mxn, TypeName(mxName), tps, tup, ctor, sig, pars, sup, ths, unit) => { val (body, members, signatures, stmts, nested, publicCtors) = prepare(mxName, tup.getOrElse(Tup(Nil)).fields, pars, unit) val sym = MixinSymbol(mxName, tps map { _._2.name }, body, members, signatures, stmts, publicCtors, nested, qualifier).tap(scope.register) if (!td.isDecl) mixins += sym } case td @ NuTypeDef(Mod, TypeName(nme), tps, tup, ctor, sig, pars, sup, ths, unit) => { val (body, members, signatures, stmts, nested, _) = prepare(nme, tup.getOrElse(Tup(Nil)).fields, pars, unit) val sym = ModuleSymbol(nme, tps map { _._2.name }, body, members, signatures, stmts, pars, nested, qualifier).tap(scope.register) if (!td.isDecl) modules += sym } case td @ NuTypeDef(Als, TypeName(nme), tps, _, ctor, sig, pars, _, _, _) => { scope.declareTypeAlias(nme, tps map { _._2.name }, sig.getOrElse(Top)) } case td @ NuTypeDef(Cls, TypeName(nme), tps, tup, ctor, sig, pars, sup, ths, unit) => { val (params, preStmts) = ctor match { case S(Constructor(Tup(ls), Blk(stmts))) => (S(ls.map { case (S(Var(nme)), Fld(flags, _)) => (nme, flags.genGetter) case (N, Fld(flags, Var(nme))) => (nme, flags.genGetter) case _ => throw CodeGenError(s"Unexpected constructor parameters in $nme.") }), stmts) case _ => (N, Nil) } val (body, members, signatures, stmts, nested, publicCtors) = prepare(nme, tup.getOrElse(Tup(Nil)).fields, pars, unit) val sym = NewClassSymbol(nme, tps map { _._2.name }, params, body, members, td.genUnapply match { case S(NuFunDef(isLetRec, mnme, _, tys, Left(rhs))) => S(MethodDef[Left[Term, Type]](isLetRec.getOrElse(false), TypeName(nme), mnme, tys, Left(rhs))) case _ => N }, signatures, preStmts ++ stmts, pars, publicCtors, nested, qualifier, td.isPlainJSClass).tap(scope.register) if (!td.isDecl) classes += sym } case td @ NuTypeDef(Trt, TypeName(nme), tps, tup, ctor, sig, pars, sup, ths, unit) => { val (body, members, _, _, _, _) = prepare(nme, tup.getOrElse(Tup(Nil)).fields, pars, unit) val sym = scope.declareTrait(nme, tps map { _._2.name }, body, members) if (!td.isDecl) traits += sym } } (traits.toList, classes.toList, mixins.toList, modules.toList) } /** * Recursively collect fields from trait definitions. * Caveat: this might cause stack overflow if cyclic inheritance exists. */ private def resolveTraitFields(name: Str): Ls[Str] = topLevelScope.getType(name) match { case S(sym: TraitSymbol) => sym.body.collectFields ++ resolveTraitFields(sym) case S(_: TypeSymbol) | S(_: ClassSymbol) | N => Nil } /** * Recursively collect fields from trait definitions. * Caveat: this might cause stack overflow if cyclic inheritance exists. */ private def resolveTraitFields(sym: TraitSymbol): Ls[Str] = sym.body.collectTypeNames.flatMap(resolveTraitFields) /** * Find the base class for a specific class. */ private def resolveBaseClass(ty: Type): Opt[ClassSymbol] = { val baseClasses = ty.collectTypeNames.flatMap { name => topLevelScope.getType(name) match { case S(sym: ClassSymbol) => S(sym) case S(sym: TraitSymbol) => N // TODO: inherit from traits case S(sym: TypeAliasSymbol) => throw new CodeGenError(s"cannot inherit from type alias $name" ) case S(_: NuTypeSymbol) => throw new CodeGenError(s"NuType symbol $name is not supported when resolving base classes") case N => throw new CodeGenError(s"undeclared type name $name when resolving base classes") } } if (baseClasses.lengthIs > 1) throw CodeGenError( s"cannot have ${baseClasses.length} base classes: " + baseClasses.map { _.lexicalName }.mkString(", ") ) else baseClasses.headOption } /** * Resolve inheritance of all declared classes. * * @return sorted class symbols with their base classes */ protected def sortClassSymbols(classSymbols: Ls[ClassSymbol]): Iterable[(ClassSymbol, Opt[ClassSymbol])] = { // Cache base classes for class symbols. val baseClasses = Map.from(classSymbols.iterator.flatMap { derivedClass => topLevelScope.resolveBaseClass(derivedClass.body).map(derivedClass -> _) }) val sorted = try topologicalSort(baseClasses, classSymbols) catch { case e: CyclicGraphError => throw CodeGenError("cyclic inheritance detected") } // Their base classes might be class symbols defined in previous translation // units. So we filter them out here. sorted.flatMap(sym => if (classSymbols.contains(sym)) S(sym -> baseClasses.get(sym)) else N) } } class JSWebBackend extends JSBackend { override def oldDefs: Bool = false // Name of the array that contains execution results val resultsName: Str = topLevelScope declareRuntimeSymbol "results" val prettyPrinterName: Str = topLevelScope declareRuntimeSymbol "prettyPrint" polyfill.use("prettyPrint", prettyPrinterName) private def generate(pgrm: Pgrm): (Ls[Str], Ls[Str]) = { val (diags, (typeDefs, otherStmts)) = pgrm.desugared val (traitSymbols, classSymbols) = declareTypeDefs(typeDefs) val defStmts = traitSymbols.map { translateTraitDeclaration(_)(topLevelScope) } ++ sortClassSymbols(classSymbols).map { case (derived, base) => translateClassDeclaration(derived, base)(topLevelScope) }.toList val resultsIdent = JSIdent(resultsName) val stmts: Ls[JSStmt] = JSConstDecl(resultsName, JSArray(Nil)) :: defStmts // Generate something like: // ```js // const = ; // .push(); // ``` .concat(otherStmts.flatMap { case Def(recursive, Var(name), L(body), isByname) => val (originalExpr, sym) = if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) val sym = topLevelScope.declareValue(name, isByvalueRecIn, body.isLam, N) val translated = translateTerm(body)(topLevelScope) topLevelScope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) (translated, topLevelScope.declareValue(name, isByvalueRecOut, body.isLam, N)) } else { val translatedBody = translateTerm(body)(topLevelScope) val isByvalueRec = if (isByname) None else Some(false) (translatedBody, topLevelScope.declareValue(name, isByvalueRec, body.isLam, N)) } val translatedBody = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr topLevelScope.tempVars `with` JSConstDecl(sym.runtimeName, translatedBody) :: JSInvoke(resultsIdent("push"), JSIdent(sym.runtimeName) :: Nil).stmt :: Nil // Ignore type declarations. case Def(_, _, R(_), isByname) => Nil // `exprs.push()`. case term: Term => topLevelScope.tempVars `with` JSInvoke( resultsIdent("push"), translateTerm(term)(topLevelScope) :: Nil ).stmt :: Nil }) val epilogue = resultsIdent.member("map")(JSIdent(prettyPrinterName)).`return` :: Nil (JSImmEvalFn(N, Nil, R(polyfill.emit() ::: stmts ::: epilogue), Nil).toSourceCode.toLines, Nil) } private def generateNewDef(pgrm: Pgrm): (Ls[Str], Ls[Str]) = { val (typeDefs, otherStmts) = pgrm.tops.partitionMap { case ot: Terms => R(ot) case fd: NuFunDef => R(fd) case nd: NuTypeDef => L(nd) case _ => die } // don't pass `otherStmts` to the top-level module, because we need to execute them one by one later val topModule = topLevelScope.declareTopModule("TypingUnit", Nil, typeDefs, true) val moduleIns = topLevelScope.declareValue("typing_unit", Some(false), false, N) val moduleDecl = translateTopModuleDeclaration(topModule, true)(topLevelScope) val insDecl = JSConstDecl(moduleIns.runtimeName, JSNew(JSIdent(topModule.name))) val includes = typeDefs.filter(!_.isDecl).map(addNuTypeToGlobalThis(_, moduleIns.runtimeName)) val resultsIdent = JSIdent(resultsName) val resultNames = ListBuffer[Str]() val stmts: Ls[JSStmt] = JSConstDecl(resultsName, JSArray(Nil)) :: (moduleDecl :: insDecl :: includes) // Generate something like: // ```js // const = ; // .push(); // ``` .concat(otherStmts.flatMap { case NuFunDef(isLetRec, nme @ Var(name), symNme, tys, rhs @ L(body)) => val recursive = isLetRec.getOrElse(true) val isByname = isLetRec.isEmpty val symb = symNme.map(_.name) val (originalExpr, sym) = (if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) // TODO Improve: (Lionel) what?! val sym = topLevelScope.declareValue(name, isByvalueRecIn, body.isLam, N) val translated = translateTerm(body)(topLevelScope) topLevelScope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) (translated, topLevelScope.declareValue(name, isByvalueRecOut, body.isLam, symb)) } else { val translated = translateTerm(body)(topLevelScope) val isByvalueRec = if (isByname) None else Some(false) (translated, topLevelScope.declareValue(name, isByvalueRec, body.isLam, symb)) }) val translatedBody = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr resultNames += sym.runtimeName topLevelScope.tempVars `with` JSConstDecl(sym.runtimeName, translatedBody) :: JSInvoke(resultsIdent("push"), JSIdent(sym.runtimeName) :: Nil).stmt :: Nil case fd @ NuFunDef(isLetRec, Var(name), _, tys, R(ty)) => Nil case _: Def | _: TypeDef | _: Constructor => throw CodeGenError("Def and TypeDef are not supported in NewDef files.") case term: Term => val res = translateTerm(term)(topLevelScope) resultNames += term.show(true) topLevelScope.tempVars `with` JSInvoke( resultsIdent("push"), res :: Nil ).stmt :: Nil }) val epilogue = resultsIdent.member("map")(JSIdent(prettyPrinterName)).`return` :: Nil (JSImmEvalFn(N, Nil, R(polyfill.emit() ::: stmts ::: epilogue), Nil).toSourceCode.toLines, resultNames.toList) } def apply(pgrm: Pgrm): (Ls[Str], Ls[Str]) = if (!oldDefs) generateNewDef(pgrm) else generate(pgrm) } abstract class JSTestBackend extends JSBackend { private val lastResultSymbol = topLevelScope.declareValue("res", Some(false), false, N) private val resultIdent = JSIdent(lastResultSymbol.runtimeName) private var numRun = 0 /** * Generate a piece of code for test purpose. It can be invoked repeatedly. * `prettyPrintQQ` is a temporary hack due to lack of runtime support and should be removed later. */ def apply(pgrm: Pgrm, allowEscape: Bool, isNewDef: Bool, prettyPrintQQ: Bool): JSTestBackend.Result = if (!isNewDef) try generate(pgrm)(topLevelScope, allowEscape) catch { case e: CodeGenError => JSTestBackend.IllFormedCode(e.getMessage()) case e: UnimplementedError => JSTestBackend.Unimplemented(e.getMessage()) // case NonFatal(e) => JSTestBackend.UnexpectedCrash(e.getClass().getName, e.getMessage()) } else try generateNewDef(pgrm, prettyPrintQQ)(topLevelScope, allowEscape) catch { case e: CodeGenError => JSTestBackend.IllFormedCode(e.getMessage()) case e: UnimplementedError => JSTestBackend.Unimplemented(e.getMessage()) // case NonFatal(e) => JSTestBackend.UnexpectedCrash(e.getClass().getName, e.getMessage()) } // generate(pgrm)(topLevelScope, allowEscape) /** * Generate JavaScript code which targets MLscript test from the given block. * * @param pgrm the program to translate * @param scope the top-level scope * @param allowEscape whether to try executing code even if it refers to unimplemented definitions * @return */ private def generate(pgrm: Pgrm)(implicit scope: Scope, allowEscape: Bool): JSTestBackend.TestCode = { val (diags, (typeDefs, otherStmts)) = pgrm.desugared val (traitSymbols, classSymbols) = declareTypeDefs(typeDefs) val defStmts = traitSymbols.map { translateTraitDeclaration(_)(topLevelScope) } ++ sortClassSymbols(classSymbols).map { case (derived, base) => translateClassDeclaration(derived, base)(topLevelScope) }.toList val zeroWidthSpace = JSLit("\"\\u200B\"") val catchClause = JSCatchClause( JSIdent("e"), (zeroWidthSpace + JSIdent("e") + zeroWidthSpace).log() :: Nil ) // Generate statements. val queries = otherStmts.map { case Def(recursive, Var(name), L(body), isByname) => (if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) val sym = scope.declareValue(name, isByvalueRecIn, body.isLam, N) try { val translated = translateTerm(body) scope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) R((translated, scope.declareValue(name, isByvalueRecOut, body.isLam, N))) } catch { case e: UnimplementedError => scope.stubize(sym, e.symbol) L(e.getMessage()) case NonFatal(e) => scope.unregisterSymbol(sym) val isByvalueRecOut = if (isByname) None else Some(false) scope.declareValue(name, isByvalueRecOut, body.isLam, N) throw e } } else { (try R(translateTerm(body)) catch { case e: UnimplementedError => scope.declareStubValue(name, e.symbol, N) L(e.getMessage()) }) map { val isByvalueRec = if (isByname) None else Some(false) expr => (expr, scope.declareValue(name, isByvalueRec, body.isLam, N)) } }) match { case R((originalExpr, sym)) => val expr = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr JSTestBackend.CodeQuery( scope.tempVars.emit(), ((JSIdent("globalThis").member(sym.runtimeName) := (expr match { case t: JSArrowFn => t.toFuncExpr(S(sym.runtimeName)) case t => t })) :: Nil), sym.runtimeName ) case L(reason) => JSTestBackend.AbortedQuery(reason) } case Def(_, Var(name), _, _) => scope.declareStubValue(name, N) JSTestBackend.EmptyQuery case term: Term => try { val body = translateTerm(term)(scope) val res = JSTestBackend.CodeQuery(scope.tempVars.emit(), (resultIdent := body) :: Nil) scope.refreshRes() res } catch { case e: UnimplementedError => JSTestBackend.AbortedQuery(e.getMessage()) } } // If this is the first time, insert the declaration of `res`. var prelude: Ls[JSStmt] = defStmts if (numRun === 0) prelude = JSLetDecl(lastResultSymbol.runtimeName -> N :: Nil) :: prelude // Increase the run number. numRun = numRun + 1 JSTestBackend.TestCode(SourceCode.fromStmts(polyfill.emit() ::: prelude).toLines, queries) } private def generateNewDef(pgrm: Pgrm, prettyPrintQQ: Bool)(implicit scope: Scope, allowEscape: Bool): JSTestBackend.TestCode = { val (typeDefs, otherStmts) = pgrm.tops.partitionMap { case _: Constructor => throw CodeGenError("unexpected constructor.") case ot: Terms => R(ot) case fd: NuFunDef => R(fd) case nd: NuTypeDef => L(nd) case _ => die } otherStmts.foreach { case fd @ NuFunDef(isLetRec, Var(nme), symNme, _, L(body)) => val isByname = isLetRec.isEmpty val isByvalueRecIn = if (isByname) None else Some(true) val symb = symNme.map(_.name) scope.declareValue(nme, isByvalueRecIn, body.isLam, symb, true) case _ => () } // don't pass `otherStmts` to the top-level module, because we need to execute them one by one later val topModule = topLevelScope.declareTopModule("TypingUnit", Nil, typeDefs, true) val moduleIns = topLevelScope.declareValue("typing_unit", Some(false), false, N) val moduleDecl = translateTopModuleDeclaration(topModule, true) val insDecl = JSConstDecl(moduleIns.runtimeName, JSNew(JSIdent(topModule.runtimeName))) val includes = typeDefs.filter(!_.isDecl).map(addNuTypeToGlobalThis(_, moduleIns.runtimeName)) val zeroWidthSpace = JSLit("\"\\u200B\"") val catchClause = JSCatchClause( JSIdent("e"), (zeroWidthSpace + JSIdent("e") + zeroWidthSpace).log() :: Nil ) // TODO Improve: (Lionel) I find this logic very strange! What's going on here? // Why are we declaring some things above AND below? // Why does the fact that a binding is recursive affect its declaration in the OUTER scope? // Generate statements. val queries = otherStmts.map { case NuFunDef(isLetRec, nme @ Var(name), symNme, tys, rhs @ L(body)) => val recursive = isLetRec.getOrElse(true) val isByname = isLetRec.isEmpty val symb = symNme.map(_.name) (if (recursive) { val isByvalueRecIn = if (isByname) None else Some(true) val sym = scope.resolveValue(name) match { case Some(s: ValueSymbol) => s case _ => scope.declareValue(name, isByvalueRecIn, body.isLam, symb) } val isByvalueRecOut = if (isByname) None else Some(false) try { val translated = translateTerm(body) // TODO Improve: (Lionel) Why are the bodies translated in the SAME scope?! scope.unregisterSymbol(sym) // TODO Improve: (Lionel) ??? R((translated, scope.declareValue(name, isByvalueRecOut, body.isLam, symb))) } catch { case e: UnimplementedError => scope.stubize(sym, e.symbol) L(e.getMessage()) case NonFatal(e) => scope.unregisterSymbol(sym) // TODO Improve: (Lionel) You should only try/catch around the part that may actually fail, and if `unregisterSymbol` should always be called, that should be done in `finally`... but the very logic of calling `unregisterSymbol` is very fishy, to say the least scope.declareValue(name, isByvalueRecOut, body.isLam, symb) throw e } } else { (try R(translateTerm(body)) catch { // TODO Improve: Why are the bodies translated in the SAME scope?! case e: UnimplementedError => scope.declareStubValue(name, e.symbol, symb) L(e.getMessage()) }) map { val isByvalueRec = if (isByname) None else Some(false) expr => (expr, scope.declareValue(name, isByvalueRec, body.isLam, symb)) } }) match { case R((originalExpr, sym)) => val expr = if (sym.isByvalueRec.isEmpty && !sym.isLam) JSArrowFn(Nil, L(originalExpr)) else originalExpr JSTestBackend.CodeQuery( scope.tempVars.emit(), ((JSIdent("globalThis").member(sym.runtimeName) := (expr match { case t: JSArrowFn => t.toFuncExpr(S(sym.runtimeName)) case t => t })) :: Nil), sym.runtimeName ) case L(reason) => JSTestBackend.AbortedQuery(reason) } case fd @ NuFunDef(isLetRec, Var(name), symNme, tys, R(ty)) => val symb = symNme.map(_.name) scope.declareStubValue(name, symb)(allowEscape || fd.isDecl) JSTestBackend.EmptyQuery case term: Term => try { val body = translateTerm(term)(scope) val res = JSTestBackend.CodeQuery(scope.tempVars.emit(), (resultIdent := body) :: Nil) scope.refreshRes() res } catch { case e: UnimplementedError => JSTestBackend.AbortedQuery(e.getMessage()) } case _: Def | _: TypeDef | _: Constructor => throw CodeGenError("Def and TypeDef are not supported in NewDef files.") } // If this is the first time, insert the declaration of `res`. var prelude: Ls[JSStmt] = Ls(moduleDecl, insDecl) ::: includes val isFirst = numRun === 0 if (isFirst) prelude = JSLetDecl(lastResultSymbol.runtimeName -> N :: Nil) :: prelude // Increase the run number. numRun = numRun + 1 val qqPredefs = SourceCode(if (isFirst && prettyPrintQQ) QQHelper.prettyPrinter else "") JSTestBackend.TestCode((qqPredefs ++ SourceCode.fromStmts(polyfill.emit() ::: prelude)).toLines, queries) } } object JSTestBackend { sealed abstract class Query /** * The generation was aborted due to some reason. */ final case class AbortedQuery(reason: Str) extends Query /** * The entry generates nothing. */ final object EmptyQuery extends Query /** * The entry generates meaningful code. */ final case class CodeQuery(prelude: Ls[Str], code: Ls[Str], res: Str) extends Query { } object CodeQuery { def apply(decls: Opt[JSLetDecl], stmts: Ls[JSStmt], res: Str = "res"): CodeQuery = CodeQuery( decls match { case S(stmt) => stmt.toSourceCode.toLines case N => Nil }, SourceCode.fromStmts(stmts).toLines, res ) } /** * Represents the result of code generation. */ abstract class Result { def showFirstResult(prefixLength: Int): Unit = () } /** * Emitted code. */ final case class TestCode(prelude: Ls[Str], queries: Ls[Query]) extends Result sealed abstract class ErrorMessage(val content: Str) extends Result /** * The input MLscript is ill-formed (e.g. impossible inheritance). */ final case class IllFormedCode(override val content: Str) extends ErrorMessage(content) /** * Some referenced symbols are not implemented. */ final case class Unimplemented(override val content: Str) extends ErrorMessage(content) /** * Code generation crashed. */ // final case class UnexpectedCrash(val name: Str, override val content: Str) extends ErrorMessage(content) /** * The result is not executed for some reasons. E.g. `:NoJS` flag. */ final object ResultNotExecuted extends JSTestBackend.Result } object JSBackend { // For integers larger than this value, use BigInt notation. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER val MaximalSafeInteger: BigInt = BigInt("9007199254740991") // For integers less than this value, use BigInt notation. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER val MinimalSafeInteger: BigInt = BigInt("-9007199254740991") def isSafeInteger(value: BigInt): Boolean = MinimalSafeInteger <= value && value <= MaximalSafeInteger } ================================================ FILE: shared/src/main/scala/mlscript/Lexer.scala ================================================ package mlscript import fastparse._ import fastparse.NoWhitespace._ /** Inspired by and adapted from: * scalaparse: https://github.com/lihaoyi/fastparse/tree/master/scalaparse * pythonparse: https://github.com/lihaoyi/fastparse/tree/master/pythonparse */ @SuppressWarnings(Array("org.wartremover.warts.All")) object Lexer { def kw[p: P](s: String) = s ~ !(letter | digit | "_") def comment[p: P] = P( "//" ~ CharsWhile(_ != '\n', /*min =*/ 0) ) def wscomment[p: P] = P( (CharsWhileIn(" \n") | Lexer.comment | "\\\n").rep ) def nonewlinewscomment[p: P] = P( (CharsWhileIn(" ") | Lexer.comment | "\\\n").rep ) def identifier[p: P]: P[String] = P( (letter|"_"|"`") ~ (letter | digit | "_" | "-" | "'" | "!" | "?").rep ).!.filter(!keywordList.contains(_)) def letter[p: P] = P( lowercase | uppercase ) def lowercase[p: P] = P( CharIn("a-z") ) def uppercase[p: P] = P( CharIn("A-Z") ) def digit[p: P] = P( CharIn("0-9") ) //def operator[p: P]: P[String] = // P( ("_" | "-" | opchar).rep ).!.filter(!keywordList.contains(_)) def operator[p: P]: P[Unit] = P( !symbolicKeywords ~ (!StringIn("/*", "//") ~ (CharsWhile(OpCharNotSlash) | "/")).rep(1) ).opaque("operator") val OpCharNotSlash = NamedFunction(x => isOpChar(x) && x != '/') val NotBackTick = NamedFunction(_ != '`') case class NamedFunction(f: Char => Boolean) (implicit name: sourcecode.Name) extends (Char => Boolean){ def apply(t: Char) = f(t) override def toString() = name.value } def OpChar[p: P] = P ( CharPred(isOpChar) ) val isOpChar = NamedFunction{ case '!' | '#' | '%' | '&' | '*' | '+' | '-' | '/' | ':' | '<' | '=' | '>' | '?' | '@' | '\\' | '^' | '|' | '~' | '.' => true //case c => isOtherSymbol(c) || isMathSymbol(c) case c => false } val keywordList = Set( "and", "or", // "not", "is", "as", "with", "if", "then", "else", "yield", "import", "class", "interface", "trait", "object", "let", "rec", "in", "of", "val", "data", "type", ) def symbolicKeywords[p: P] = P{ StringIn( //":", ";", "=>", "=", "->", "<-", ":", "/", //"<:", "<%", ">:", "#", "@", "\\", "\u21d2", "\u2190" ) ~ !OpChar }.opaque("SymbolicKeywords") def stringliteral[p: P]: P[String] = P( stringprefix.? ~ (longstring | shortstring) ) def stringprefix[p: P]: P[Unit] = identifier def shortstring[p: P]: P[String] = P( shortstring0("'") | shortstring0("\"") ) def shortstring0[p: P](delimiter: String) = P( delimiter ~ shortstringitem(delimiter).rep.! ~ delimiter) def shortstringitem[p: P](quote: String): P[Unit] = P( shortstringchar(quote) | escapeseq ) def shortstringchar[p: P](quote: String): P[Unit] = P( CharsWhile(!s"\\\n${quote(0)}".contains(_)) ) def longstring[p: P]: P[String] = P( longstring0("'''") | longstring0("\"\"\"") ) def longstring0[p: P](delimiter: String) = P( delimiter ~ longstringitem(delimiter).rep.! ~ delimiter) def longstringitem[p: P](quote: String): P[Unit] = P( longstringchar(quote) | escapeseq | !quote ~ quote.take(1) ) def longstringchar[p: P](quote: String): P[Unit] = P( CharsWhile(!s"\\${quote(0)}".contains(_)) ) def escapeseq[p: P]: P[Unit] = P( "\\" ~ AnyChar ) def negatable[T, _P: P](p: => P[T])(implicit ev: Numeric[T]) = ( //("+" | "-") // '+' is useless and causes problems, e.g., f +1 and f+1 parsed as f (+1)... "-" .?.! ~ p).map { case ("-", i) => ev.negate(i) case (_, i) => i } def longinteger[p: P]: P[BigInt] = P( integer ~ ("l" | "L") ) def integer[p: P]: P[BigInt] = negatable[BigInt, Any](P( octinteger | hexinteger | bininteger | decimalinteger)) def decimalinteger[p: P]: P[BigInt] = P( nonzerodigit ~ digit.rep | "0" ).!.map(scala.BigInt(_)) def octinteger[p: P]: P[BigInt] = P( "0" ~ ("o" | "O") ~ octdigit.rep(1).! | "0" ~ octdigit.rep(1).! ).map(scala.BigInt(_, 8)) def hexinteger[p: P]: P[BigInt] = P( "0" ~ ("x" | "X") ~ hexdigit.rep(1).! ).map(scala.BigInt(_, 16)) def bininteger[p: P]: P[BigInt] = P( "0" ~ ("b" | "B") ~ bindigit.rep(1).! ).map(scala.BigInt(_, 2)) def nonzerodigit[p: P]: P[Unit] = P( CharIn("1-9") ) def octdigit[p: P]: P[Unit] = P( CharIn("0-7") ) def bindigit[p: P]: P[Unit] = P( "0" | "1" ) def hexdigit[p: P]: P[Unit] = P( digit | CharIn("a-f", "A-F") ) def floatnumber[p: P]: P[BigDecimal] = negatable[BigDecimal, Any](P( pointfloat | exponentfloat )) def pointfloat[p: P]: P[BigDecimal] = P( intpart.? ~ fraction | intpart ~ "." ).!.map(BigDecimal(_)) def exponentfloat[p: P]: P[BigDecimal] = P( (intpart | pointfloat) ~ exponent ).!.map(BigDecimal(_)) def intpart[p: P]: P[BigDecimal] = P( digit.rep(1) ).!.map(BigDecimal(_)) def fraction[p: P]: P[Unit] = P( "." ~ digit.rep(1) ) def exponent[p: P]: P[Unit] = P( ("e" | "E") ~ ("+" | "-").? ~ digit.rep(1) ) } ================================================ FILE: shared/src/main/scala/mlscript/MLParser.scala ================================================ package mlscript import scala.util.chaining._ import scala.collection.mutable import fastparse._, fastparse.ScalaWhitespace._ import mlscript.utils._, shorthands._ import mlscript.Lexer._ /** Parser for an ML-style input syntax, used in the legacy `ML*` tests. */ @SuppressWarnings(Array("org.wartremover.warts.All")) class MLParser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { val keywords = Set( "def", "class", "trait", "type", "method", "mut", "let", "rec", "in", "fun", "with", "undefined", "null", "if", "then", "else", "match", "case", "of", "forall", "datatype", "match", "as") def kw[p: P](s: String) = s ~~ !(letter | digit | "_" | "'") // NOTE: due to bug in fastparse, the parameter should be by-name! def locate[p:P, L <: Located](tree: => P[L]): P[L] = (Index ~~ tree ~~ Index).map { case (i0, n, i1) => n.withLoc(i0, i1, origin) } def toParam(t: Term): Tup = Tup((N, Fld(FldFlags.empty, t)) :: Nil) def toParams(t: Term): Tup = t match { case t: Tup => t case _ => toParam(t) } def toParamsTy(t: Type): Tuple = t match { case t: Tuple => t case _ => Tuple((N, Field(None, t)) :: Nil) } def letter[p: P] = P( lowercase | uppercase ) def lowercase[p: P] = P( CharIn("a-z") ) def uppercase[p: P] = P( CharIn("A-Z") ) def digit[p: P] = P( CharIn("0-9") ) def number[p: P]: P[Int] = P( CharIn("0-9").repX(1).!.map(_.toInt) ) def ident[p: P]: P[String] = P( (letter | "_") ~~ (letter | digit | "_" | "'").repX ).!.filter(!keywords(_)) def index[p: P]: P[String] = P( "0" | CharIn("1-9") ~~ digit.repX ).!.map(_.toString) def termOrAssign[p: P]: P[Statement] = P( term ~ ("=" ~ term).? ).map { case (expr, N) => expr case (pat, S(bod)) => LetS(false, pat, bod) } def term[p: P]: P[Term] = P(let | fun | ite | forall | withsAscAs | _match | adtMatchWith) def forall[p: P]: P[Term] = P( (kw("forall") ~/ tyVar.rep ~ "." ~ term).map { case (vars, ty) => Forall(vars.toList, ty) } ) def lit[p: P]: P[Lit] = locate(number.map(x => IntLit(BigInt(x))) | Lexer.stringliteral.map(StrLit(_)) | P(kw("undefined")).map(x => UnitLit(true)) | P(kw("null")).map(x => UnitLit(false))) def variable[p: P]: P[Var] = locate(ident.map(Var)) def fieldName[p: P]: P[Var] = locate( (ident | index).map(Var) ) def parenCell[p: P]: P[Either[Term, (Term, Boolean)]] = (("..." | kw("mut")).!.? ~ term).map { case (Some("..."), t) => Left(t) case (Some("mut"), t) => Right(t -> true) case (_, t) => Right(t -> false) } def parens[p: P]: P[Term] = locate(P( "(" ~/ parenCell.rep(0, ",") ~ ",".!.? ~ ")" ).map { case (Seq(Right(t -> false)), N) => Bra(false, t) case (Seq(Right(t -> true)), N) => Tup(N -> Fld(FldFlags(true, false, false), t) :: Nil) // ? single tuple with mutable case (ts, _) => if (ts.forall(_.isRight)) Tup(ts.iterator.map { case R(f) => N -> Fld(FldFlags(f._2, false, false), f._1) case _ => die // left unreachable }.toList) else Splc(ts.map { case R((v, m)) => R(Fld(FldFlags(m, false, false), v)) case L(spl) => L(spl) }.toList) }) def subtermNoSel[p: P]: P[Term] = P( parens | record | lit | variable ) def subterm[p: P]: P[Term] = P( Index ~~ subtermNoSel ~ ( // Fields: ("." ~/ (fieldName | locate(("(" ~/ ident ~ "." ~ ident ~ ")") .map {case (prt, id) => Var(s"${prt}.${id}")}))) .map {(t: Var) => Left(t)} | // Array subscripts: ("[" ~ term ~/ "]" ~~ Index).map {Right(_)} // Assignment: ).rep ~ "!".!.? ~ ("<-" ~ term).?).map { case (i0, st, sels, bang, a) => val base0 = sels.foldLeft(st)((acc, t) => t match { case Left(se) => Sel(acc, se) case Right((su, i1)) => Subs(acc, su).withLoc(i0, i1, origin) }) val base = if (bang.isEmpty) base0 else Inst(base0) a.fold(base)(Assign(base, _)) } def record[p: P]: P[Rcd] = locate(P( "{" ~/ (kw("mut").!.? ~ fieldName ~ "=" ~ term map L.apply).|(kw("mut").!.? ~ variable map R.apply).rep(sep = ";" | ",") ~ "}" ).map { fs => Rcd(fs.map{ case L((mut, v, t)) => v -> Fld(FldFlags(mut.isDefined, false, false), t) case R(mut -> id) => id -> Fld(FldFlags(mut.isDefined, false, false), id) }.toList)}) def fun[p: P]: P[Term] = locate(P( kw("fun") ~/ term ~ "->" ~ term ).map(nb => Lam(toParams(nb._1), nb._2))) def let[p: P]: P[Term] = locate(P( kw("let") ~/ kw("rec").!.?.map(_.isDefined) ~ variable ~ subterm.rep ~ "=" ~ term ~ kw("in") ~ term ) map { case (rec, id, ps, rhs, bod) => Let(rec, id, ps.foldRight(rhs)((i, acc) => Lam(toParams(i), acc)), bod) }) def ite[p: P]: P[Term] = P( kw("if") ~/ term ~ kw("then") ~ term ~ kw("else") ~ term ).map(ite => App(App(App(Var("if"), toParam(ite._1)), toParam(ite._2)), toParam(ite._3))) def withsAscAs[p: P]: P[Term] = P( withsAsc ~ (kw("as") ~/ term).rep ).map { case (withs, ascs) => ascs.foldLeft(withs)(Bind) } def withsAsc[p: P]: P[Term] = P( withs ~ (":" ~/ ty).rep ).map { case (withs, ascs) => ascs.foldLeft(withs)(Asc) } def withs[p: P]: P[Term] = P( binops ~ (kw("with") ~ record).rep ).map { case (as, ws) => ws.foldLeft(as)((acc, w) => With(acc, w)) } def mkApp(lhs: Term, rhs: Term): Term = App(lhs, toParams(rhs)) def apps[p: P]: P[Term] = P( subterm.rep(1).map(_.reduce(mkApp)) ) def _match[p: P]: P[CaseOf] = locate(P( kw("case") ~/ term ~ "of" ~ ("{" ~ "|".? ~ matchArms("|") ~ "}" | matchArms(",")) ).map(CaseOf.tupled)) def matchArms[p: P](sep: Str): P[CaseBranches] = P( ( ("_" ~ "->" ~ term).map(Wildcard) | ((lit | variable) ~ "->" ~ term ~ matchArms2(sep)) .map { case (t, b, rest) => Case(t, b, rest)(refined = false) } ).?.map { case None => NoCases case Some(b) => b } ) def matchArms2[p: P](sep: Str): P[CaseBranches] = (sep ~ matchArms(sep)).?.map(_.getOrElse(NoCases)) private val prec: Map[Char,Int] = List( ":", "|", "^", "&", "= !", "< >", "+ -", "* / %", ".", ).zipWithIndex.flatMap { case (cs,i) => cs.filterNot(_ == ' ').map(_ -> i) }.toMap.withDefaultValue(Int.MaxValue) def precedence(op: String): Int = prec(op.head) min prec(op.last) // Adapted from: https://github.com/databricks/sjsonnet/blob/master/sjsonnet/src/sjsonnet/Parser.scala#L136-L180 def binops[p: P]: P[Term] = P(apps ~ (Index ~~ operator.! ~~ Index ~/ apps).rep ~ "").map { case (pre, fs) => var remaining = fs def climb(minPrec: Int, current: Term): Term = { var result = current while ( remaining.headOption match { case None => false case Some((off0, op, off1, next)) => val prec: Int = precedence(op) if (prec < minPrec) false else { remaining = remaining.tail val rhs = climb(prec + 1, next) result = App(App(Var(op).withLoc(off0, off1, origin), toParams(result)), toParams(rhs)) true } } )() result } climb(0, pre) } def operator[p: P]: P[Unit] = P( !symbolicKeywords ~~ (!StringIn("/*", "//") ~~ (CharsWhile(OpCharNotSlash) | "/")).rep(1) ).opaque("operator") def symbolicKeywords[p: P] = P{ StringIn( "|", "~", ";", "=>", "=", "->", "<-", ":", "#", "@", "\\", "\u21d2", "\u2190" ) ~~ !OpChar }.opaque("symbolic keywords") def expr[p: P]: P[Term] = P( term ~ End ) def defDecl[p: P]: P[Def] = locate(P((kw("def") ~ variable ~ tyParams ~ ":" ~/ ty map { case (id, tps, t) => Def(true, id, R(PolyType(tps.map(L(_)), t)), true) }) | (kw("rec").!.?.map(_.isDefined) ~ kw("def") ~/ variable ~ subterm.rep ~ "=" ~ term map { case (rec, id, ps, bod) => Def(rec, id, L(ps.foldRight(bod)((i, acc) => Lam(toParams(i), acc))), true) }))) def tyKind[p: P]: P[TypeDefKind] = (kw("class") | kw("trait") | kw("type")).! map { case "class" => Cls case "trait" => Trt case "type" => Als } def tyDecl[p: P]: P[TypeDef] = P((tyKind ~/ tyName ~ tyParams).flatMap { case (k @ (Cls | Trt), id, ts) => (":" ~ ty).? ~ (mthDecl(id) | mthDef(id)).rep.map(_.toList) map { case (bod, ms) => TypeDef(k, id, ts, bod.getOrElse(Top), ms.collect { case R(md) => md }, ms.collect{ case L(md) => md }, Nil, N) } case (k @ Als, id, ts) => "=" ~ ty map (bod => TypeDef(k, id, ts, bod, Nil, Nil, Nil, N)) case (k @ Mod, _, _) => throw new NotImplementedError("Namespaces are not supported yet.") case (k @ Mxn, _, _) => throw new NotImplementedError("Mixins are not supported yet.") }) def tyParams[p: P]: P[Ls[TypeName]] = ("[" ~ tyName.rep(0, ",") ~ "]").?.map(_.toList.flatten) def mthDecl[p: P](prt: TypeName): P[R[MethodDef[Right[Term, Type]]]] = P(kw("method") ~ variable ~ tyParams ~ ":" ~/ ty map { case (id, ts, t) => R(MethodDef[Right[Term, Type]](true, prt, id, ts, R(t))) }) def mthDef[p: P](prt: TypeName): P[L[MethodDef[Left[Term, Type]]]] = P(kw("rec").!.?.map(_.isDefined) ~ kw("method") ~ variable ~ tyParams ~ subterm.rep ~ "=" ~/ term map { case (rec, id, ts, ps, bod) => L(MethodDef(rec, prt, id, ts, L(ps.foldRight(bod)((i, acc) => Lam(toParams(i), acc))))) }) def ty[p: P]: P[Type] = P( tyNoRange ~ (".." ~ tyNoRange).? ).map { case (res, N) => res case (lb, S(ub)) => Bounds(lb, ub) } def tyNoRange[p: P]: P[Type] = P( tyNoAs ~ ("as" ~ tyVar).rep ).map { case (ty, ass) => ass.foldLeft(ty)((a, b) => Recursive(b, a)) } def tyNoAs[p: P]: P[Type] = P( (kw("forall") ~/ tyVar.rep ~ "." ~ tyNoAs).map { case (vars, ty) => PolyType(vars.map(R(_)).toList, ty) } | tyNoForall ) def tyNoForall[p: P]: P[Type] = P( tyNoUnion.rep(1, "|") ).map(_.reduce(Union) ) def tyNoUnion[p: P]: P[Type] = P( tyNoInter.rep(1, "&") ).map(_.reduce(Inter) ) def tyNoInter[p: P]: P[Type] = P( tyNoFun ~ ("->" ~/ tyNoInter).? ).map { case (l, S(r)) => Function(toParamsTy(l), r) case (l, N) => l } // Note: field removal types are not supposed to be explicitly used by programmers, // and they won't work in negative positions, // but parsing them is useful in tests (such as shared/src/test/diff/mlscript/Annoying.mls) def tyNoFun[p: P]: P[Type] = P( (rcd | ctor | parTy) ~ ("\\" ~ fieldName).rep(0) ) map { case (ty, Nil) => ty case (ty, ids) => Rem(ty, ids.toList) } def ctor[p: P]: P[Type] = locate(P( tyName ~ "[" ~ ty.rep(0, ",") ~ "]" ) map { case (tname, targs) => AppliedType(tname, targs.toList) }) | tyNeg | tyName | tyTag | tyVar | tyWild | litTy def tyNeg[p: P]: P[Type] = locate(P("~" ~/ tyNoFun map { t => Neg(t) })) def tyTag[p: P]: P[TypeTag] = locate(P("#" ~~ (ident map TypeTag))) def tyName[p: P]: P[TypeName] = locate(P(ident map TypeName)) def tyVar[p: P]: P[TypeVar] = locate(P( (("α" | "β" | "γ" | "δ" | "'").! ~ ident.? map { case (pre, id) => TypeVar(R(pre + id.getOrElse("")), N) }) )) def tyWild[p: P]: P[Bounds] = locate(P("?".! map (_ => Bounds(Bot, Top)))) def rcd[p: P]: P[Record] = locate(P( "{" ~/ ( kw("mut").!.? ~ fieldName ~ ":" ~ ty).rep(sep = ";") ~ "}" ) .map(_.toList.map { case (None, v, t) => v -> Field(None, t) case (Some(_), v, t) => v -> Field(Some(t), t) } pipe Record)) def parTyCell[p: P]: P[Either[Type, (Type, Boolean)]] = (("..." | kw("mut")).!.? ~ ty). map { case (Some("..."), t) => Left(t) case (Some("mut"), t) => Right(t -> true) case (_, t) => Right(t -> false) } def parTy[p: P]: P[Type] = locate(P( "(" ~/ parTyCell.rep(0, ",").map(_.map(N -> _).toList) ~ ",".!.? ~ ")" ).map { case (N -> Right(ty -> false) :: Nil, N) => ty case (fs, _) => if (fs.forall(_._2.isRight)) Tuple(fs.map { case (l, Right(t -> false)) => l -> Field(None, t) case (l, Right(t -> true)) => l -> Field(Some(t), t) case _ => ??? // unreachable }) else Splice(fs.map{ _._2 match { case L(l) => L(l) case R(r -> true) => R(Field(Some(r), r)) case R(r -> false) => R(Field(None, r)) } }) }) def litTy[p: P]: P[Type] = P( lit.map(l => Literal(l).withLocOf(l)) ) def toplvl[p: P]: P[Ls[Statement]] = P(adtTyDecl) | P( defDecl | tyDecl | termOrAssign ).map(_ :: Nil) def pgrm[p: P]: P[Pgrm] = P( (";".rep ~ toplvl ~ topLevelSep.rep).rep.map(_.flatten.toList) ~ End ).map(Pgrm) def topLevelSep[p: P]: P[Unit] = ";" private var curHash = 0 def nextHash: Int = { val res = curHash curHash = res + 1 res } /////////////////////////////////////////////////////// /// ADT types /////////////////////////////////////////////////////// def ctorName[p: P]: P[TypeName] = locate(P(uppercase ~~ (letter | digit | "_" | "'").repX).!.filter(!keywords(_)).map(TypeName)) /** Data constructor declaration. */ def adtCtorDecl[p: P](alsName: TypeName, tparams: List[TypeName]): P[TypeDef] = { val parent = tparams match { case Nil => alsName case _ :: _ => AppliedType(alsName, tparams) } def tup = parTy.map { case t: Tuple => t case t => Tuple(N -> Field(N, t) :: Nil) } P((ctorName ~ tup.?).map { case (id, S(body: Tuple)) => val positionals = body.fields.zipWithIndex.map { case (_, i) => Var("_"+(i+1)) } val rcdBody = Record(positionals.zip(body.fields.map(_._2))) TypeDef(Cls, id, tparams, Inter(parent, rcdBody), Nil, Nil, positionals, S(AdtInfo(alsName))) case (id, None) => TypeDef(Cls, id, tparams, parent, Nil, Nil, Nil, S(AdtInfo(alsName))) case t => throw new Exception(s"Unable to handle case $t") }) } def adtDataCtor[p: P](alsName: TypeName, tparams: List[TypeName]): P[List[TypeDef]] = adtCtorDecl(alsName, tparams).rep(1, "|").map(_.toList) def adtTyDecl[p: P]: P[Ls[Statement]] = P((kw("datatype") ~ ctorName ~ tyParams).flatMap { case (alsName, tparams) => "=" ~/ adtDataCtor(alsName, tparams).map { bodies => val paramSet = tparams.toSet val constructors = bodies.map(cls => adtTyConstructors(cls, alsName, tparams)) val parent = TypeDef(Cls, alsName, tparams, Top, constructors.map { case Def(_, nme, R(body), _) => val ctorParams = body match { case PolyType(_, Function(lhs, _)) => lhs case PolyType(_, _: TypeName | _: AppliedType) => Top case _ => die } MethodDef(false, alsName, nme, Nil, R(ctorParams)) case Def(_, nme, L(body), _) => ??? }, Nil, Nil, S(AdtInfo(alsName))) parent :: bodies ::: constructors }}) /** Create a helper function for a class constructor. */ def adtTyConstructors(tyDef: TypeDef, alsName: TypeName, alsParams: Ls[TypeName]): Def = { assert(tyDef.kind === Cls) tyDef.body match { case _: TypeName | _: AppliedType => val funAppTy = PolyType(alsParams.map(L.apply), tyDef.body) val fun = Def(false, Var(tyDef.nme.name), R(funAppTy), true).withLocOf(tyDef.nme) fun case Inter(alsTy, Record(fields)) => val funTy = PolyType(alsParams.map(L.apply), Function(Tuple(fields.map(N -> _._2)), alsTy.withLocOf(tyDef))) val fun = Def(false, Var(tyDef.nme.name), R(funTy), true).withLocOf(tyDef.nme) fun case _ => die } } /////////////////////////////////////////////////////// /// ADT match with /////////////////////////////////////////////////////// def adtMatchWith[p: P]: P[AdtMatchWith] = locate(P(kw("match") ~/ term ~ "with" ~ "|".? ~ matchArms).map { case (expr, arms) => AdtMatchWith(expr, arms) }) def matchArms[p: P]: P[Ls[AdtMatchPat]] = P( ( ("_" ~ "->" ~ term).map(AdtMatchPat(Var("_"), _) :: Nil) | (variable ~ "_" ~ "->" ~ term ~ matchArms2).map { case (t, b, rest) => AdtMatchPat(mkApp(t, Var("_")), b) :: rest } | (term ~ "->" ~ term ~ matchArms2).map { case (t, b, rest) => AdtMatchPat(t, b) :: rest } ).?.map { case None => Ls.empty case Some(b) => b }) def matchArms2[p: P]: P[Ls[AdtMatchPat]] = ("|" ~ matchArms).?.map(_.getOrElse(Ls.empty)) } object MLParser { def addTopLevelSeparators(lines: IndexedSeq[Str]): IndexedSeq[Str] = { (lines.iterator ++ lines.lastOption).toList.sliding(2).map { case l0 :: l1 :: Nil => if (l1.startsWith(" ") || l1.startsWith("\t") || l0.startsWith("//")) l0 + "\n" else l0 + ";" case l :: Nil => l case _ => die }.toIndexedSeq } } ================================================ FILE: shared/src/main/scala/mlscript/Message.scala ================================================ package mlscript import scala.language.implicitConversions import mlscript.utils._, shorthands._ final case class Message(bits: Ls[Message.Bit]) { def show(newDefs: Bool): Str = { val ctx = ShowCtx.mk(typeBits, newDefs) showIn(ctx) } def typeBits: Ls[TypeLike] = bits.collect{ case Message.Code(t) => t } def showIn(implicit ctx: ShowCtx): Str = { bits.map { case Message.Code(ty) => ty.showIn(0) case Message.Text(txt) => txt }.mkString } def showDbg: Str = { bits.iterator.map { case Message.Code(trm) => s"$trm" case Message.Text(txt) => txt }.mkString } def +(that: Message): Message = Message(bits ++ that.bits) } object Message { def mkCtx(msgs: IterableOnce[Message], newDefs: Bool,pre: Str = "'"): ShowCtx = ShowCtx.mk(msgs.iterator.flatMap(_.typeBits), newDefs, pre) def join(msgs: Seq[Message]): Message = Message(msgs.iterator.flatMap(_.bits).toList) sealed abstract class Bit final case class Text(str: Str) extends Bit final case class Code(ty: TypeLike) extends Bit implicit def fromType(ty: TypeLike): Message = Message(Code(ty)::Nil) implicit def fromStr(str: Str): Message = Message(Text(str)::Nil) implicit class MessageContext(private val ctx: StringContext) { def msg(inserted: Message*): Message = { assert(inserted.length === ctx.parts.length - 1) val parts = ctx.parts.map(str => Text(StringContext(str).s())) val h = parts.head val t = parts.tail Message((h +: inserted.lazyZip(t).flatMap(_.bits :+ _)).toList) } } } ================================================ FILE: shared/src/main/scala/mlscript/NewLexer.scala ================================================ package mlscript import scala.annotation.tailrec import utils._, shorthands._ import Message.MessageContext import Diagnostic.{Lexing, Parsing} import NewLexer._ class NewLexer(origin: Origin, raise: Diagnostic => Unit, dbg: Bool) { val bytes: Array[Char] = origin.fph.blockStr.toArray private val length = bytes.length type State = Int private val isOpChar = Set( '!', '#', '%', '&', '*', '+', '-', '/', ':', '<', '=', '>', '?', '@', '\\', '^', '|', '~' , '.', // ',', // ';' ) def isIdentFirstChar(c: Char): Bool = c.isLetter || c === '_' || c === '\'' def isIdentChar(c: Char): Bool = isIdentFirstChar(c) || isDigit(c) || c === '\'' def isHexDigit(c: Char): Bool = isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') def isOctDigit(c: Char): Bool = c >= '0' && c <= '7' def isBinDigit(c: Char): Bool = c === '0' || c === '1' def isDigit(c: Char): Bool = c >= '0' && c <= '9' def matches(i: Int, syntax: Str, start: Int): Bool = if (start < syntax.length && i + start < length && bytes(i + start) === syntax(start)) matches(i, syntax, start + 1) else start >= syntax.length /* // TODO remove (unused) private val isNonStickyKeywordChar = Set( ',', ':', ';', ) */ private val isSymKeyword = Set( // "->", "=>", "=", ":", ";", // ",", "#", "`" // ".", // "<", // ">", ) private val isAlphaOp = Set( "with", "and", "or", "is", "as", ) @tailrec final def takeWhile(i: Int, cur: Ls[Char] = Nil)(pred: Char => Bool): (Str, Int) = if (i < length && pred(bytes(i))) takeWhile(i + 1, bytes(i) :: cur)(pred) else (cur.reverseIterator.mkString, i) final def num(i: Int): (Lit, Int) = { def test(i: Int, p: Char => Bool): Bool = i < length && p(bytes(i)) def zero: IntLit = IntLit(BigInt(0)) /** Take a sequence of digits interleaved with underscores. */ def takeDigits(i: Int, pred: Char => Bool): (Opt[Str], Int) = { @tailrec def rec(i: Int, acc: Ls[Char], firstSep: Bool, lastSep: Bool): (Str, Bool, Bool, Int) = if (i < length) { val c = bytes(i) if (pred(c)) rec(i + 1, c :: acc, firstSep, false) else if (c === '_') rec(i + 1, acc, acc.isEmpty, true) else (acc.reverseIterator.mkString, firstSep, lastSep, i) } else (acc.reverseIterator.mkString, firstSep, lastSep, i) val (str, firstSep, lastSep, j) = rec(i, Nil, false, false) if (firstSep) raise(WarningReport( msg"Leading separator is not allowed" -> S(loc(i - 1, i)) :: Nil, newDefs = true, source = Lexing)) if (lastSep) raise(WarningReport( msg"Trailing separator is not allowed" -> S(loc(j - 1, j)) :: Nil, newDefs = true, source = Lexing)) (if (str.isEmpty) N else S(str), j) } /** Take an integer and coverts to `BigInt`. Also checks if it is empty. */ def integer(i: Int, radix: Int, desc: Str, pred: Char => Bool): (IntLit, Int) = { takeDigits(i, pred) match { case (N, j) => raise(ErrorReport(msg"Expect at least one $desc digit" -> S(loc(i, i + 2)) :: Nil, newDefs = true, source = Lexing)) (zero, j) case (S(str), j) => (IntLit(BigInt(str, radix)), j) } } def isDecimalStart(ch: Char) = ch === '.' || ch === 'e' || ch === 'E' /** Take a fraction part with an optional exponent part. Call at periods. */ def decimal(i: Int, integral: Str): (DecLit, Int) = { val (fraction, j) = if (test(i, _ === '.')) { takeDigits(i + 1, isDigit) match { case (N, j) => raise(ErrorReport(msg"Expect at least one digit after the decimal point" -> S(loc(i + 1, i + 2)) :: Nil, newDefs = true, source = Lexing)) ("", j) case (S(digits), j) => ("." + digits, j) } } else ("", i) val (exponent, k) = if (test(j, ch => ch === 'e' || ch === 'E')) { val (sign, k) = if (test(j + 1, ch => ch === '+' || ch === '-')) { (bytes(j + 1), j + 2) } else { ('+', j + 1) } takeDigits(k, isDigit) match { case (N, l) => raise(ErrorReport(msg"Expect at least one digit after the exponent sign" -> S(loc(l - 1, l)) :: Nil, newDefs = true, source = Lexing)) ("", l) case (S(digits), l) => ("E" + sign + digits, l) } } else { ("", j) } (DecLit(BigDecimal(integral + fraction + exponent)), k) } if (i < length) { bytes(i) match { case '0' if i + 1 < length => bytes(i + 1) match { case 'x' => integer(i + 2, 16, "hexadecimal", isHexDigit) case 'o' => integer(i + 2, 8, "octal", isOctDigit) case 'b' => integer(i + 2, 2, "binary", isBinDigit) case '.' | 'E' | 'e' => decimal(i + 1, "0") case _ => integer(i, 10, "decimal", isDigit) } case '0' => (zero, i + 1) case _ => takeDigits(i, isDigit) match { case (N, j) => raise(ErrorReport(msg"Expect a numeric literal" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) (zero, i) case (S(integral), j) => if (j < length && isDecimalStart(bytes(j))) decimal(j, integral) else (IntLit(BigInt(integral)), j) } } } else { raise(ErrorReport(msg"Expect a numeric literal instead of end of input" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) (zero, i) } } final def char(i: Int): (Char, Int) = { if (i < length) { bytes(i) match { case '\\' => { val j = i + 1 if (j < length) bytes(j) match { case 'n' => ('\n', j + 1) case 't' => ('\t', j + 1) case 'r' => ('\r', j + 1) case 'b' => ('\b', j + 1) case 'f' => ('\f', j + 1) case '\'' => ('\'', j + 1) case '"' => ('"', j + 1) case '\\' => ('\\', j + 1) case ch => raise(ErrorReport(msg"Invalid escape character" -> S(loc(j, j + 1)) :: Nil, newDefs = true, source = Lexing)) ('\u0000', j + 1) } else { raise(ErrorReport(msg"Expect an escape character" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) ('\u0000', i + 1) } } case '\n' | '\r' => raise(ErrorReport(msg"Unexpected newline in a char literal" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) ('\u0000', i + 1) case '\"' => raise(ErrorReport(msg"Empty character literal" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) ('\u0000', i + 1) case ch => (ch, i + 1) } } else { raise(ErrorReport(msg"Expect a character literal" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) ('\u0000', i) } } final def closeChar(i: Int): Int = if (bytes.lift(i) === Some('\"')) i + 1 else { raise(ErrorReport(msg"Unclosed character literal" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) i } // * Check the end of a string (either single quotation or triple quotation) final def closeStr(i: Int, isTriple: Bool): Int = if (!isTriple && bytes.lift(i) === Some('"')) i + 1 else if (isTriple && matches(i, "\"\"\"", 0)) i + 3 else { raise(ErrorReport(msg"unclosed quotation mark" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) i } @tailrec final def str(i: Int, escapeMode: Bool, cur: Ls[Char] = Nil)(implicit triple: Bool): (Str, Int) = if (escapeMode) if (i < length) bytes(i) match { case '\\' => str(i + 1, false, '\\' :: cur) case '"' => str(i + 1, false, '"' :: cur) case 'n' => str(i + 1, false, '\n' :: cur) case 't' => str(i + 1, false, '\t' :: cur) case 'r' => str(i + 1, false, '\r' :: cur) case 'b' => str(i + 1, false, '\b' :: cur) case 'f' => str(i + 1, false, '\f' :: cur) case ch => raise(WarningReport(msg"Found invalid escape character" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) str(i + 1, false, ch :: cur) } else { raise(ErrorReport(msg"Expect an escape character" -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) (cur.reverseIterator.mkString, i) } else if (triple) { if (i < length) bytes(i) match { case '"' => if (matches(i, "\"\"\"", 0) && !matches(i + 1, "\"\"\"", 0)) // Find the last """ (cur.reverseIterator.mkString, i) else str(i + 1, false, '"' :: cur) case ch => str(i + 1, false, ch :: cur) } else (cur.reverseIterator.mkString, i) } else { if (i < length) bytes(i) match { case '\\' => str(i + 1, true, cur) case '"' | '\n' => (cur.reverseIterator.mkString, i) case ch => str(i + 1, false, ch :: cur) } else (cur.reverseIterator.mkString, i) } def loc(start: Int, end: Int): Loc = Loc(start, end, origin) @tailrec final def lex(i: Int, ind: Ls[Int], acc: Ls[TokLoc])(implicit qqList: Ls[BracketKind]): Ls[TokLoc] = if (i >= length) acc.reverse else { val c = bytes(i) def pe(msg: Message): Unit = // raise(ParseError(false, msg -> S(loc(i, i + 1)) :: Nil)) raise(ErrorReport(msg -> S(loc(i, i + 1)) :: Nil, newDefs = true, source = Lexing)) def isQuasiquoteOpening(i: Int): Bool = matches(i, BracketKind.Quasiquote.beg, 0) def isQuasiquoteTripleOpening(i: Int): Bool = matches(i, BracketKind.QuasiquoteTriple.beg, 0) def isUnquoteOpening(i: Int): Bool = matches(i, BracketKind.Unquote.beg, 0) def isQuasiquoteTripleClosing(i: Int): Bool = matches(i, BracketKind.QuasiquoteTriple.end, 0) // @inline // def go(j: Int, tok: Token) = lex(j, ind, (tok, loc(i, j)) :: acc) def next(j: Int, tok: Token) = (tok, loc(i, j)) :: acc c match { case ' ' => val (_, j) = takeWhile(i)(_ === ' ') // go(j, SPACE) lex(j, ind, next(j, SPACE)) case ',' => val j = i + 1 // go(j, COMMA) lex(j, ind, next(j, COMMA)) case '`' => lex(i + 1, ind, next(i + 1, QUOTE)) case 'c' if isQuasiquoteOpening(i) || isQuasiquoteTripleOpening(i) => val isTripleQuoteQQ = isQuasiquoteTripleOpening(i) val bracket_kind = if (isTripleQuoteQQ) BracketKind.QuasiquoteTriple else BracketKind.Quasiquote val len = bracket_kind.beg.length lex(i + len, ind, next(i + len, OPEN_BRACKET(bracket_kind)))(bracket_kind :: qqList) case '$' if isUnquoteOpening(i) => lex(i + 2, ind, next(i + 2, OPEN_BRACKET(BracketKind.Unquote))) case '$' if i + 1 < length && isIdentFirstChar(bytes(i + 1)) => val (n, j) = takeWhile(i + 1)(isIdentChar) lex(j, ind, next(j, BRACKETS(BracketKind.Unquote, (if (keywords.contains(n)) KEYWORD(n) else IDENT(n, isAlphaOp(n)), loc(i + 1, j)) :: Nil)(loc(i, j)))) case ';' => val j = i + 1 lex(j, ind, next(j, SEMI)) case '"' => val (isTripleQQ, cons) = qqList match { case h :: t => (h === BracketKind.QuasiquoteTriple, t) case Nil => (false, Nil) } if (isTripleQQ && isQuasiquoteTripleClosing(i)) { val length = BracketKind.QuasiquoteTriple.end.length lex(i + length, ind, next(i + length, CLOSE_BRACKET(BracketKind.QuasiquoteTriple)))(cons) } else if (!isTripleQQ && qqList.nonEmpty) { lex(i + 1, ind, next(i + 1, CLOSE_BRACKET(BracketKind.Quasiquote)))(cons) } else { val isTriple = matches(i, "\"\"\"", 0) val j = i + (if (isTriple) 3 else 1) val (chars, k) = str(j, false)(isTriple) val k2 = closeStr(k, isTriple) // go(k2, LITVAL(StrLit(chars))) lex(k2, ind, next(k2, LITVAL(StrLit(chars)))) } case '/' if bytes.lift(i + 1).contains('/') => val j = i + 2 val (txt, k) = takeWhile(j)(c => c =/= '\n') // go(k, COMMENT(txt)) lex(k, ind, next(k, COMMENT(txt))) case '/' if bytes.lift(i + 1).contains('*') => // multiple-line comment val j = i + 2 var prev1 = '/'; var prev2 = '*' val (txt, k) = takeWhile(j)(c => { val res = prev1 =/= '*' || prev2 =/= '/' prev1 = prev2; prev2 = c res }) // go(k, COMMENT(txt.dropRight(2))) lex(k, ind, next(k, COMMENT(txt.dropRight(2)))) // case BracketKind(Left(k)) => go(i + 1, OPEN_BRACKET(k)) // case BracketKind(Right(k)) => go(i + 1, CLOSE_BRACKET(k)) case BracketKind(Left(k)) => lex(i + 1, ind, next(i + 1, OPEN_BRACKET(k))) case BracketKind(Right(k)) => lex(i + 1, ind, next(i + 1, CLOSE_BRACKET(k))) case '\n' => val j = i + 1 val (space, k) = takeWhile(j)(c => c === ' ' || c === '\n') val nextInd = space.reverseIterator.takeWhile(_ =/= '\n').size if (ind.headOption.forall(_ < nextInd) && nextInd > 0) lex(k, nextInd :: ind, (INDENT, loc(j, k)) :: acc) else { val newIndBase = ind.dropWhile(_ > nextInd) val droppedNum = ind.size - newIndBase.size val hasNewIndent = newIndBase.headOption.forall(_ < nextInd) && nextInd > 0 val newInd = if (hasNewIndent) nextInd :: newIndBase else newIndBase if (dbg) { println("dbg: " + bytes.drop(i).take(10).map(escapeChar).mkString+"...") println((ind, nextInd, newIndBase, droppedNum, hasNewIndent, newInd)) } lex(k, newInd, if (droppedNum > 0) { if (hasNewIndent) (INDENT, loc(j, k)) else (NEWLINE, loc(i, k)) } :: List.fill(droppedNum)((DEINDENT, loc(j-1, k))) ::: acc else (NEWLINE, loc(i, k)) :: acc ) } case _ if isIdentFirstChar(c) => val (n, j) = takeWhile(i)(isIdentChar) // go(j, if (keywords.contains(n)) KEYWORD(n) else IDENT(n, isAlphaOp(n))) lex(j, ind, next(j, if (keywords.contains(n)) KEYWORD(n) else IDENT(n, isAlphaOp(n)))) case _ if isOpChar(c) => val (n, j) = takeWhile(i)(isOpChar) if (n === "." && j < length) { val nc = bytes(j) if (isIdentFirstChar(nc)) { val (name, k) = takeWhile(j)(isIdentChar) // go(k, SELECT(name)) lex(k, ind, next(k, SELECT(name))) } else if ( // The first character is '0' and the next character is not a digit (nc === '0' && !(j + 1 < length && isDigit(bytes(j + 1)))) || ('0' < nc && nc <= '9') // The first character is a digit other than '0' ) { val (name, k) = takeWhile(j)(isDigit) // go(k, SELECT(name)) lex(k, ind, next(k, SELECT(name))) } else lex(j, ind, next(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true))) } // else go(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true)) else lex(j, ind, next(j, if (isSymKeyword.contains(n)) KEYWORD(n) else IDENT(n, true))) case _ if isDigit(c) => val (lit, j) = num(i) // go(j, LITVAL(IntLit(BigInt(str)))) lex(j, ind, next(j, LITVAL(lit))) case _ => pe(msg"unexpected character '${escapeChar(c)}'") // go(i + 1, ERROR) lex(i + 1, ind, next(i + 1, ERROR)) } } def escapeChar(ch: Char): String = ch match { case '\b' => "\\b" case '\t' => "\\t" case '\n' => "\\n" case '\f' => "\\f" case '\r' => "\\r" case '"' => "\\\"" case '\'' => "\\\'" case '\\' => "\\\\" case _ => if (ch.isControl) "\\0" + Integer.toOctalString(ch.toInt) else String.valueOf(ch) } lazy val tokens: Ls[Token -> Loc] = lex(0, Nil, Nil)(Nil) /** Converts the lexed tokens into structured tokens. */ lazy val bracketedTokens: Ls[Stroken -> Loc] = { import BracketKind._ def go(toks: Ls[Token -> Loc], canStartAngles: Bool, stack: Ls[BracketKind -> Loc -> Ls[Stroken -> Loc]], acc: Ls[Stroken -> Loc]): Ls[Stroken -> Loc] = toks match { case (QUOTE, l0) :: (IDENT("<", true), l1) :: rest => go(rest, false, stack, (IDENT("<", true), l1) :: (QUOTE, l0) :: acc) case (QUOTE, l0) :: (IDENT(">", true), l1) :: rest => go(rest, false, stack, (IDENT(">", true), l1) :: (QUOTE, l0) :: acc) case (OPEN_BRACKET(k), l0) :: rest => go(rest, false, k -> l0 -> acc :: stack, Nil) case (CLOSE_BRACKET(k1), l1) :: rest => stack match { case ((Indent, loc), oldAcc) :: _ if k1 =/= Indent => go(CLOSE_BRACKET(Indent) -> l1.left :: toks, false, stack, acc) case ((Indent, loc), oldAcc) :: stack if k1 === Indent && acc.forall { case (SPACE | NEWLINE, _) => true; case _ => false } => // * Ignore empty indented blocks: go(rest, false, stack, oldAcc) case ((k0, l0), oldAcc) :: stack => if (k0 =/= k1 && !(k0 === Unquote && k1 === Curly)) raise(ErrorReport(msg"Mistmatched closing ${k1.name}" -> S(l1) :: msg"does not correspond to opening ${k0.name}" -> S(l0) :: Nil, newDefs = true, source = Parsing)) go(rest, true, stack, BRACKETS(k0, acc.reverse)(l0.right ++ l1.left) -> (l0 ++ l1) :: oldAcc) case Nil => raise(ErrorReport(msg"Unexpected closing ${k1.name}" -> S(l1) :: Nil, newDefs = true, source = Parsing)) go(rest, false, stack, acc) } case (INDENT, loc) :: rest => go(OPEN_BRACKET(Indent) -> loc :: rest, false, stack, acc) case (DEINDENT, loc) :: rest => go(CLOSE_BRACKET(Indent) -> loc :: rest, false, stack, acc) case (IDENT("<", true), loc) :: rest if canStartAngles => go(OPEN_BRACKET(Angle) -> loc :: rest, false, stack, acc) case (IDENT(">", true), loc) :: rest if canStartAngles && (stack match { case ((Angle, _), _) :: _ => true case _ => false }) => go(CLOSE_BRACKET(Angle) -> loc :: rest, false, stack, acc) case (IDENT(id, true), loc) :: rest if (canStartAngles && id.forall(_ == '>') && id.length > 1 && (stack match { case ((Angle, _), _) :: _ => true case _ => false })) => // split `>>` to `>` and `>` so that code like `A>` can be parsed correctly go((CLOSE_BRACKET(Angle) -> loc.left) :: (IDENT(id.drop(1), true) -> loc) :: rest, false, stack, acc) case ((tk @ IDENT(">", true), loc)) :: rest if canStartAngles => raise(WarningReport( msg"This looks like an angle bracket, but it does not close any angle bracket section" -> S(loc) :: msg"Add spaces around it if you intended to use `<` as an operator" -> N :: Nil, newDefs = true, source = Parsing)) go(rest, false, stack, tk -> loc :: acc) case (tk: Stroken, loc) :: rest => go(rest, tk match { case SPACE | NEWLINE => false case _ => true }, stack, tk -> loc :: acc) case Nil => stack match { case ((Indent, loc), oldAcc) :: _ => go(CLOSE_BRACKET(Indent) -> loc/*FIXME not proper loc...*/ :: Nil, false, stack, acc) case ((k, l0), oldAcc) :: stack => raise(ErrorReport(msg"Unmatched opening ${k.name}" -> S(l0) :: ( if (k === Angle) msg"Note that `<` without spaces around it is considered as an angle bracket and not as an operator" -> N :: Nil else Nil ), newDefs = true, source = Parsing)) (oldAcc ::: acc).reverse case Nil => acc.reverse } } go(tokens, false, Nil, Nil) } } object NewLexer { type TokLoc = (Token, Loc) val keywords: Set[Str] = Set( "if", "then", "else", "case", "fun", "val", "var", // "is", // "as", "of", // "and", // "or", "let", "rec", "in", // "any", // "all", "mut", "set", "do", "while", "declare", "class", "trait", "mixin", "interface", "extends", "override", "super", "new", "namespace", "module", "type", "where", "forall", "exists", "in", "out", "null", "undefined", "abstract", "constructor", "virtual" ) def printToken(tl: TokLoc): Str = tl match { case (SPACE, _) => " " case (COMMA, _) => "," case (SEMI, _) => ";" case (NEWLINE, _) => "↵" case (INDENT, _) => "→" case (DEINDENT, _) => "←" case (ERROR, _) => "" case (QUOTE, _) => "`" case (LITVAL(lv), _) => lv.idStr case (KEYWORD(name: String), _) => "#" + name case (IDENT(name: String, symbolic: Bool), _) => name case (SELECT(name: String), _) => "." + name case (OPEN_BRACKET(k), _) => k.beg case (CLOSE_BRACKET(k), _) => k.end case (BRACKETS(k @ BracketKind.Indent, contents), _) => k.beg + printTokens(contents) + k.end case (BRACKETS(k, contents), _) => k.beg + printTokens(contents) + k.end case (COMMENT(text: String), _) => "/*" + text + "*/" } def printTokens(ts: Ls[TokLoc]): Str = ts.iterator.map(printToken).mkString("|", "|", "|") } ================================================ FILE: shared/src/main/scala/mlscript/NewParser.scala ================================================ package mlscript import scala.util.chaining._ import scala.annotation.tailrec import sourcecode.{Name, Line} import utils._, shorthands._ import mlscript.Message._ import BracketKind._ object NewParser { type ExpectThen >: Bool type FoundErr >: Bool // may be better done as: class FoundErr(var found: Bool) final def expectThen(implicit ptr: ExpectThen): Bool = ptr === true final def foundErr(implicit ptr: FoundErr): Bool = ptr === true type TokLoc = (Stroken, Loc) type LTL = Ls[TokLoc] private val MinPrec = 0 private val NoElsePrec = MinPrec + 1 private val prec: Map[Char,Int] = List( "", // 0 is the virtual precedence of 'else' "", "", "", "", "", // ^ for keywords // ";", ",", "=", "@", ":", "|", "/ \\", "^", "&", // "= !", "!", "< >", "+ -", // "* / %", "* %", "", // Precedence of application ".", ).zipWithIndex.flatMap { case (cs, i) => cs.filterNot(_ === ' ').map(_ -> (i + 1)) }.toMap.withDefaultValue(Int.MaxValue) private val AppPrec = prec('.') - 1 final def opCharPrec(opChar: Char): Int = prec(opChar) final def opPrec(opStr: Str): (Int, Int) = opStr match { case "is" => (4, 4) case "and" => (3, 3) case "or" => (2, 2) case "=>" => // * The lambda operator is special: // * it should associate very strongly on the left and very loosely on the right // * so that we can write things like `f() |> x => x is 0` ie `(f()) |> (x => (x is 0))` val eqPrec = prec('.') // * We pick the tightest precedence (eqPrec, 1) // * Note: we used to do this instead which broke the example above on both sides: // val eqPrec = prec('=') // (eqPrec, eqPrec - 1) case "+." | "-." | "*." => (prec(opStr.head), prec(opStr.head)) case _ if opStr.exists(_.isLetter) => (5, 5) case _ => val r = opStr.last (prec(opStr.head), prec(r) - (if (r === '@' || r === '/' || r === ',' || r === ':') 1 else 0)) } } import NewParser._ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], newDefs: Bool, raiseFun: Diagnostic => Unit, val dbg: Bool, fallbackLoc: Opt[Loc], description: Str = "input") { outer => private var freshCnt: Int = 0 final def freshVar: Var = { val res = Var("_$" + freshCnt) freshCnt += 1 res } object Spaces { def unapply(xs: Ls[Stroken -> Loc]): S[(() => Unit, Ls[Stroken -> Loc])] = xs match { case (SPACE, _) :: Spaces(cons, rest) => S((() => {cons(); consume}, rest)) case _ => S(() => (), xs) } } final def rec(tokens: Ls[Stroken -> Loc], fallbackLoc: Opt[Loc], description: Str): NewParser = new NewParser(origin, tokens, newDefs, raiseFun, dbg, fallbackLoc, description) { def doPrintDbg(msg: => Str): Unit = outer.printDbg("> " + msg) } final def raise(mkDiag: => Diagnostic)(implicit fe: FoundErr = false): Unit = if (!foundErr) raiseFun(mkDiag) final def err(msgs: Ls[Message -> Opt[Loc]]): Unit = raise(ErrorReport(msgs, newDefs = true, source = Diagnostic.Parsing)) final def mkLoc(l: Int, r: Int): Loc = Loc(l, r, origin) protected def doPrintDbg(msg: => Str): Unit protected def printDbg(msg: => Any): Unit = doPrintDbg("│ " * this.indent + msg) protected var indent = 0 private def wrap[R](args: => Any)(mkRes: Unit => R)(implicit l: Line, n: Name): R = { printDbg(s"@ ${n.value}${args match { case it: Iterable[_] => it.mkString("(", ",", ")") case _: Product => args case _ => s"($args)" }} [at l.${l.value}]") val res = try { indent += 1 // mkRes mkRes(()) } finally indent -= 1 printDbg(s"= $res") res } final def parseAll[R](parser: => R): R = { val res = parser cur match { case c @ (tk, tkl) :: _ => val (relevantToken, rl) = c.dropWhile(_._1 === SPACE).headOption.getOrElse(tk, tkl) err(msg"Expected end of input; found ${relevantToken.describe} instead" -> S(rl) :: Nil) case Nil => () } res } final def concludeWith[R](f: this.type => R): R = { val res = f(this) cur.dropWhile(tk => (tk._1 === SPACE || tk._1 === NEWLINE) && { consume; true }) match { case c @ (tk, tkl) :: _ => val (relevantToken, rl) = c.dropWhile(_._1 === SPACE).headOption.getOrElse(tk, tkl) err(msg"Unexpected ${relevantToken.describe} here" -> S(rl) :: Nil) case Nil => () } printDbg(s"Concluded with $res") res } // def pe(msg: Message, l: Loc, rest: (Message, Opt[Loc])*): Unit = // err((msg -> S(l) :: rest.toList)) // TODO parse err private var _cur: Ls[TokLoc] = tokens private var _modifiersCache: ModifierSet = ModifierSet.empty def resetCur(newCur: Ls[TokLoc]): Unit = { _cur = newCur _modifiersCache = ModifierSet.empty } private def summarizeCur = NewLexer.printTokens(_cur.take(5)) + (if (_cur.sizeIs > 5) "..." else "") private def cur(implicit l: Line, n: Name) = { if (dbg) printDbg(s"? ${n.value}\t\tinspects ${summarizeCur} [at l.${l.value}]") while (!_cur.isEmpty && (_cur.head._1 match { case COMMENT(_) => true case _ => false })) consume _cur } final def consume(implicit l: Line, n: Name): Unit = { if (dbg) printDbg(s"! ${n.value}\t\tconsumes ${NewLexer.printTokens(_cur.take(1))} [at l.${l.value}]") resetCur(_cur.tailOption.getOrElse(Nil)) // FIXME throw error if empty? } // TODO simplify logic – this is no longer used much final def skip(tk: Stroken, ignored: Set[Stroken] = Set(SPACE), allowEnd: Bool = false, note: => Ls[Message -> Opt[Loc]] = Nil) (implicit fe: FoundErr): (Bool, Opt[Loc]) = wrap(tk, ignored, allowEnd) { l => require(!ignored(tk)) val skip_res = cur match { case (tk2, l2) :: _ => if (ignored(tk2)) { consume return skip(tk, ignored, allowEnd, note) } else if (tk2 =/= tk) { if (!foundErr) err(( msg"Expected ${tk.describe}; found ${tk2.describe} instead" -> S(l2) :: note)) (false, S(l2)) } else (true, S(l2)) case Nil => if (!allowEnd) if (!foundErr) err(( msg"Expected ${tk.describe}; found end of input instead" -> lastLoc :: note)) (allowEnd, N) } consume skip_res } private lazy val lastLoc = tokens.lastOption.map(_._2.right).orElse(fallbackLoc) private def curLoc = _cur.headOption.map(_._2) /* // * TODO rm final def blockTerm: Blk = { val ts = block(false, false) val es = ts.map { case L(t) => err(msg"Unexpected 'then'/'else' clause" -> t.toLoc :: Nil) errExpr case R(e) => e } Blk(es) } */ final def typingUnit: TypingUnit = { val ts = block(false, false) val es = ts.map { case L(t) => err(msg"Unexpected 'then'/'else' clause" -> t.toLoc :: Nil) errExpr case R(d: NuDecl) => d case R(e: Term) => e case R(c: Constructor) => c case _ => ??? } TypingUnit(es) } final def typingUnitMaybeIndented(implicit fe: FoundErr): TypingUnit = yeetSpaces match { case (br @ BRACKETS(Indent, toks), _) :: _ => consume rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnit) case _ => typingUnit } final def curlyTypingUnit(implicit fe: FoundErr): Opt[TypingUnit] = yeetSpaces match { case (br @ BRACKETS(Curly, toks), l1) :: _ => consume S(rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnitMaybeIndented).withLoc(S(l1))) case _ => N } final def toParamsTy(t: Type): Tuple = t match { case t: Tuple => t case _ => Tuple((N, Field(None, t)) :: Nil) } final def typ(prec: Int = 0)(implicit fe: FoundErr, l: Line): Type = mkType(expr(prec)) case class ModifierSet(mods: Map[Str, Loc]) { def handle(mod: Str): (Opt[Loc], ModifierSet) = mods.get(mod) -> copy(mods = mods - mod) def done: Unit = mods.foreachEntry { (mod, loc) => err(msg"Unrecognized modifier `${mod}` in this position" -> S(loc) :: Nil) } } object ModifierSet { val empty: ModifierSet = ModifierSet(Map.empty) private def go(acc: ModifierSet): ModifierSet = cur match { case (KEYWORD(kw), l0) :: c if acc.mods.contains(kw) => err(msg"Repeated modifier `${kw}`" -> S(l0) :: Nil) consume yeetSpaces go(acc) case (KEYWORD("declare"), l0) :: c => consume yeetSpaces go(acc.copy(acc.mods + ("declare" -> l0))) case (KEYWORD("virtual"), l0) :: c => consume yeetSpaces go(acc.copy(acc.mods + ("virtual" -> l0))) case (KEYWORD("mut"), l0) :: c => consume yeetSpaces go(acc.copy(acc.mods + ("mut" -> l0))) case (KEYWORD("abstract"), l0) :: c => consume yeetSpaces go(acc.copy(acc.mods + ("abstract" -> l0))) case _ if acc.mods.isEmpty => acc case (KEYWORD("class" | "infce" | "trait" | "mixin" | "type" | "namespace" | "module" | "fun" | "val" | "let"), l0) :: _ => acc case (tok, loc) :: _ => // TODO support indented blocks of modified declarations... err(msg"Unexpected ${tok.describe} token after modifier${if (acc.mods.sizeIs > 1) "s" else ""}" -> S(loc) :: Nil) acc case Nil => ??? // TODO: } def unapply(__ : Ls[TokLoc]): S[ModifierSet -> Ls[TokLoc]] = { val res = go(_modifiersCache) _modifiersCache = res S(res, _cur) } } final def block(implicit et: ExpectThen, fe: FoundErr): Ls[IfBody \/ Statement] = { val annotations = parseAnnotations(true) cur match { case Nil => Nil case (NEWLINE, _) :: _ => consume; block case (SPACE, _) :: _ => consume; block case (KEYWORD("constructor"), l0) :: _ => consume val res = yeetSpaces match { case (br @ BRACKETS(Round, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // TODO val body = curlyTypingUnit.getOrElse(TypingUnit(Nil)) Constructor(Tup(as).withLoc(S(loc)), Blk(body.entities).withLocOf(body)) case _ => err(msg"Expect parameter list for the constructor" -> S(l0) :: Nil) Constructor(Tup(Nil), Blk(Nil)) } R(res.withLoc(S(l0 ++ res.getLoc))) :: block case c => val t = c match { case ModifierSet(mods, (KEYWORD(k @ ("class" | "infce" | "trait" | "mixin" | "type" | "module")), l0) :: c) => consume val (isDecl, mods2) = mods.handle("declare") val (isAbs, mods3) = mods2.handle("abstract") mods3.done val kind = k match { case "class" => Cls case "trait" => Trt case "mixin" => Mxn case "type" => Als case "module" => Mod case _ => die } val (tn, success) = yeetSpaces match { case (IDENT(idStr, _), l1) :: _ => consume (TypeName(idStr).withLoc(S(l1)), true) case c => val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some)) err(( msg"Expected a type name; found ${tkstr} instead" -> loc :: Nil)) consume // R(errExpr) (TypeName("").withLoc(curLoc.map(_.left)), false) } val tparams = yeetSpaces match { case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume /* val ts = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()).map { case (N, Fld(false, false, v @ Var(nme))) => TypeName(nme).withLocOf(v) case nmeo -> param => err(msg"unsupported type parameter shape (${param.describe})" -> param.value.toLoc :: Nil) TypeName(nmeo.fold("")(_.name)).withLocOf(param.value) } ts */ rec(toks, S(br.innerLoc), br.describe).concludeWith(_.maybeIndented((p, _) => p.typeParams)) case _ => Nil } val params = yeetSpaces match { case (br @ BRACKETS(Round, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // TODO S(Tup(as).withLoc(S(loc))) case _ => N } def otherParents: Ls[Term] = yeetSpaces match { case (COMMA, _) :: _ => consume expr(prec(',')) :: otherParents // we don't want to parse parent lists as including comma expressions case _ => Nil } val sigTrm = yeetSpaces match { case (KEYWORD("="), _) :: _ if kind is Als => consume S(expr(0)) case (KEYWORD(":"), _) :: _ if !(kind is Als) => consume S(expr(0)) case _ => N } val ps = yeetSpaces match { // case (KEYWORD("="), _) :: _ if kind is Als => // consume // expr(0) :: otherParents case (KEYWORD("extends"), _) :: _ => consume expr(prec(',')) :: otherParents // we don't want to parse parent lists as including comma expressions case _ => Nil } val (sigTrm2, ps2, fullTu) = curlyTypingUnit.fold { ps.lastOption match { case S(Rft(bse, tu)) => (sigTrm, (bse :: ps.reverse.tail).reverse, tu) case _ => sigTrm match { case S(Rft(bse, tu)) => (S(bse), ps, tu) case _ => (sigTrm, ps, TypingUnit(Nil)) } } }(tu => (sigTrm, ps, tu)) val sig = sigTrm2.map(mkType(_)) val (ctors, bodyStmts) = fullTu.entities.partitionMap { case c: Constructor => L(c) case t => R(t) } val tu = TypingUnit(bodyStmts).withLocOf(fullTu) if (ctors.lengthIs > 1) { err(msg"A class may have at most one explicit constructor" -> S(l0) :: Nil) N } val ctor = ctors.headOption val res = NuTypeDef(kind, tn, tparams, params, ctor, sig, ps2, N, N, tu)(isDecl, isAbs, annotations) R(res.withLoc(S(l0 ++ tn.getLoc ++ res.getLoc))) R(res.withLoc(S(l0 ++ res.getLoc))) case ModifierSet(mods, (KEYWORD(kwStr @ ("fun" | "val" | "let")), l0) :: c) => // TODO support rec? consume val (isDecl, mods2) = mods.handle("declare") val (isVirtual, mods3) = mods2.handle("virtual") val (isMut, mods4) = mods3.handle("mut") mods4.done val genField = kwStr =/= "let" val isLetRec = yeetSpaces match { case (KEYWORD("rec"), l1) :: _ if kwStr === "let" => consume S(true) case c => if (kwStr === "fun") N else S(false) } val opStr = yeetSpaces match { case (BRACKETS(Round, ts), brackloc) :: _ => ts match { case (IDENT(opStr, true), l1) :: rest => consume rest.dropWhile(_._1 === SPACE) match { case Nil => case (tok, loc) :: ts => err((msg"Unexpected ${tok.describe} after symbolic name" -> S(loc) :: Nil)) } S(Var(opStr).withLoc(S(l1))) case (tok, loc) :: _ => consume err((msg"Expected a symbolic name, found ${tok.describe} instead" -> S(loc) :: Nil)) N case Nil => consume err((msg"Expected a symbolic name between brackets, found nothing" -> S(brackloc) :: Nil)) N } case _ => N } val (v, success) = yeetSpaces match { case (IDENT(idStr, false), l1) :: _ => consume (Var(idStr).withLoc(S(l1)), true) case c => val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some)) err((msg"Expected a function name; found ${tkstr} instead" -> loc :: Nil)) consume // R(errExpr) (Var("").withLoc(curLoc.map(_.left)), false) } foundErr || !success pipe { implicit fe => val tparams = if (kwStr === "let") Ls[TypeName]() else yeetSpaces match { case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume val ts = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()).map { case (N, Fld(FldFlags(false, false, _), v @ Var(nme))) => TypeName(nme).withLocOf(v) case _ => ??? } ts case _ => Nil } val (ps, transformBody) = yeetSpaces match { case (br @ BRACKETS(Round, Spaces(cons, (KEYWORD("override"), ovLoc) :: rest)), loc) :: rest2 => resetCur(BRACKETS(Round, rest)(br.innerLoc) -> loc :: rest2) funParams match { case ps @ Tup(N -> Fld(FldFlags(false, false, gen), pat) :: Nil) :: Nil => val fv = freshVar (Tup(N -> Fld(FldFlags(false, false, gen), fv) :: Nil) :: Nil, S( (body: Term) => If(IfOpApp(fv, Var("is"), IfThen(pat, body)), S( App(Sel(Super().withLoc(S(ovLoc)), v), Tup(N -> Fld(FldFlags(false, false, gen), fv) :: Nil)) )) )) case r => err(msg"Unsupported 'override' parameter list shape" -> S(br.innerLoc) :: Nil) (r, N) } case _ => (funParams, N) } val asc = yeetSpaces match { case (KEYWORD(":"), _) :: _ => consume // S(typ(2)) S(typ(0)) case _ => N } yeetSpaces match { case (KEYWORD("="), _) :: _ => consume val body = expr(0) val newBody = transformBody.fold(body)(_(body)) val annotatedBody = asc.fold(newBody)(ty => Asc(newBody, ty)) yeetSpaces match { case (KEYWORD("in"), l1) :: _ if kwStr === "let" => consume if (tparams.nonEmpty) err(msg"Unsupported type parameters on 'let' binding" -> S(l1) :: Nil) val rest = expr(0) R(Let(isLetRec.getOrElse(die), v, annotatedBody, rest).withLoc(S(l0 ++ annotatedBody.toLoc))) case _ => R(NuFunDef( isLetRec, v, opStr, tparams, L(ps.foldRight(annotatedBody)((i, acc) => Lam(i, acc))) )(isDecl, isVirtual, isMut, N, N, genField, annotations).withLoc(S(l0 ++ annotatedBody.toLoc))) } case c => asc match { case S(ty) => if (transformBody.nonEmpty) die // TODO R(NuFunDef(isLetRec, v, opStr, tparams, R(PolyType(Nil, ps.foldRight(ty)((p, r) => Function(p.toType match { case L(diag) => raise(diag); Top // TODO better case R(tp) => tp }, r)))))(isDecl, isVirtual, isMut, N, N, genField, annotations).withLoc(S(l0 ++ ty.toLoc))) // TODO rm PolyType after FCP is merged case N => // TODO dedup: val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some)) err(( msg"Expected ':' or '=' followed by a function body or signature; found ${tkstr} instead" -> loc :: Nil)) consume val bod = errExpr R(NuFunDef( isLetRec, v, opStr, Nil, L(ps.foldRight(bod: Term)((i, acc) => Lam(i, acc))) )(isDecl, isVirtual, isMut, N, N, genField, annotations).withLoc(S(l0 ++ bod.toLoc))) } } } case _ => exprOrIf(0, allowSpace = false, annotations = annotations) } val finalTerm = yeetSpaces match { case (KEYWORD("="), l0) :: _ => t match { case R(v: Var) => consume R(Eqn(v, expr(0))) case R(App(v: Var, args)) => consume R(Eqn(v, Lam(args, expr(0)))) case _ => t } case _ => t } yeetSpaces match { case (SEMI, _) :: _ => consume; finalTerm :: block case (NEWLINE, _) :: _ => consume; finalTerm :: block case _ => finalTerm :: Nil } } } private def parseAnnotations(allowNewLines: Bool): Ls[Term] = { @tailrec def rec(acc: Ls[Term]): Ls[Term] = cur match { case (SPACE, _) :: c => consume rec(acc) case (NEWLINE, _) :: c if allowNewLines => consume rec(acc) case (IDENT("@", true), l0) :: c => { consume val (name, loc) = c match { case (IDENT(nme, false), l1) :: next => (nme, l1) case c => val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some)) err((msg"Expected an identifier; found ${tkstr} instead" -> loc :: Nil)) ("", l0) } consume val annotation = Var(name).withLoc(S(loc)) rec(annotation :: acc) } case _ => acc.reverse } rec(Nil) } private def yeetSpaces: Ls[TokLoc] = cur.dropWhile(tkloc => (tkloc._1 === SPACE || tkloc._1.isInstanceOf[COMMENT] // TODO properly retrieve and sotre all comments in AST? ) && { consume; true }) final def funParams(implicit et: ExpectThen, fe: FoundErr, l: Line): Ls[Tup] = wrap(()) { l => yeetSpaces match { case (KEYWORD("=" | ":"), _) :: _ => Nil case (KEYWORD("of"), _) :: _ => consume Tup(args(false) // TODO ) :: funParams case (br @ BRACKETS(Round, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // TODO Tup(as).withLoc(S(loc)) :: funParams case (tk, l0) :: _ => err(( // msg"Expected a function name; found ${"[TODO]"} instead" -> N :: Nil)) msg"Expected function parameter list; found ${tk.describe} instead" -> S(l0) :: Nil)) consume Nil case Nil => Nil } } private def unexpectedThenElse(loc: Opt[Loc]) = { err(msg"Expected an expression; found a 'then'/'else' clause instead" -> loc :: Nil) errExpr } private def unsupportedQuote(loc: Opt[Loc]) = { err(msg"This quote syntax is not supported yet" -> loc :: Nil) errExpr } final def expr(prec: Int, allowSpace: Bool = true)(implicit fe: FoundErr, l: Line): Term = wrap(prec,allowSpace) { l => exprOrIf(prec, allowSpace)(et = false, fe = fe, l = implicitly) match { case R(e) => e case L(e) => unexpectedThenElse(e.toLoc) } } def exprOrBlockContinuation(implicit et: ExpectThen, fe: FoundErr, l: Line): Term = yeetSpaces match { case (NEWLINE, l0) :: _ => consume val stmts = block val es = stmts.map { case L(t) => unexpectedThenElse(t.toLoc); case R(e) => e } Blk(es) case _ => expr(0) } private def warnDbg(msg: Any, loco: Opt[Loc] = curLoc): Unit = raise(WarningReport(msg"[${cur.headOption.map(_._1).mkString}] ${""+msg}" -> loco :: Nil, newDefs = true)) private def letBindings(genQuote: Bool)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = { val bs = bindings(Nil) val body = if (genQuote) yeetSpaces match { case (QUOTE, l1) :: (KEYWORD("in"), l2) :: _ => consume consume exprOrIf(0)(et, fe, implicitly) case (NEWLINE, _) :: _ => consume val stmts = block val es = stmts.map { case L(t) => unexpectedThenElse(t.toLoc); case R(e) => e } R(Blk(es)) case (tk, loc) :: _ => err(msg"Expected '`in'; found ${tk.describe} instead" -> S(loc) :: Nil) R(errExpr) case Nil => err(msg"Expected '`in'; found end of input instead" -> lastLoc :: Nil) R(errExpr) } else yeetSpaces match { case (KEYWORD("in") | SEMI, _) :: _ => consume exprOrIf(0)(et, fe, implicitly) case (NEWLINE, _) :: _ => consume exprOrIf(0)(et, fe, implicitly) case _ => R(UnitLit(true).withLoc(curLoc.map(_.left))) } bs.foldRight(body) { case ((v, r), R(acc)) if genQuote => R(Quoted(Let(false, v, Unquoted(r), Unquoted(acc)))) case ((v, r), R(acc)) => R(Let(false, v, r, acc)) case ((v, r), L(acc)) if genQuote => R(unsupportedQuote(acc.toLoc)) case ((v, r), L(acc)) => L(IfLet(false, v, r, acc)) } } final def exprOrIf(prec: Int, allowSpace: Bool = true, annotations: Ls[Term] = Nil)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(prec, allowSpace) { l => val moreAnnotations: Ls[Term] = parseAnnotations(false) if (moreAnnotations.nonEmpty) { yeetSpaces } val allAnns = annotations ++ moreAnnotations val res = cur match { case (SPACE, l0) :: _ if allowSpace => // Q: do we really need the `allowSpace` flag? consume exprOrIf(prec, allowSpace) case (br @ BRACKETS(Indent, toks), _) :: _ if (toks.headOption match { // TODO factor case S((KEYWORD("then" | "else"), _)) => false case _ => true }) => consume val ts = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.block) val es = ts.map { case L(t) => return L(IfBlock(ts)); case R(e) => e } R(Blk(es)) case (QUOTE, loc) :: _ => consume cur match { case (IDENT(nme, false), l0) :: _ => consume exprCont(Quoted(Var(nme)).withLoc(S(loc ++ l0)), prec, allowNewlines = false) case (LITVAL(lit), l0) :: _ => consume exprCont(Quoted(lit.withLoc(S(l0))).withLoc(S(loc ++ l0)), prec, allowNewlines = false) case (KEYWORD("let"), l0) :: _ => consume letBindings(true) match { case R(bd) => R(bd.withLoc(S(loc ++ bd.toLoc))) case _ => R(unsupportedQuote(S(l0))) } case (KEYWORD("if"), l0) :: _ => val term = exprOrIf(prec, allowSpace) term match { case R(it @ If(IfThen(cond, body), els)) => R(Quoted(If(IfThen(Unquoted(cond), Unquoted(body)), els.map(els => Unquoted(els)))).withLoc(S(loc ++ it.toLoc))) case _ => R(unsupportedQuote(S(l0))) } case (br @ BRACKETS(bk @ Round, toks), loc) :: _ => consume val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) match { case (N, Fld(FldFlags(false, false, _), elt)) :: Nil => Quoted(Bra(false, elt)).withLoc(S(loc ++ elt.toLoc)) case _ => unsupportedQuote(S(loc)) } exprCont(res, prec, allowNewlines = false) case _ => R(unsupportedQuote(S(loc))) } case (LITVAL(lit), l0) :: _ => consume exprCont(lit.withLoc(S(l0)), prec, allowNewlines = false) case (KEYWORD(kwStr @ ("undefined" | "null")), l0) :: _ => consume exprCont(UnitLit(kwStr === "undefined").withLoc(S(l0)), prec, allowNewlines = false) case (IDENT(nme, false), l0) :: _ => consume exprCont(Var(nme).withLoc(S(l0)), prec, allowNewlines = false) case (br @ BRACKETS(Quasiquote | QuasiquoteTriple, toks), loc) :: _ => consume val body = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0)) exprCont(Quoted(body).withLoc(S(loc)), prec, allowNewlines = false) case (br @ BRACKETS(Unquote, toks), loc) :: _ => consume val body = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0)) exprCont(Unquoted(body).withLoc(S(loc)), prec, allowNewlines = false) case (KEYWORD("super"), l0) :: _ => consume exprCont(Super().withLoc(S(l0)), prec, allowNewlines = false) case (IDENT("~", _), l0) :: _ => consume val rest = expr(prec, allowSpace = true) exprCont(App(Var("~").withLoc(S(l0)), rest).withLoc(S(l0 ++ rest.toLoc)), prec, allowNewlines = false) case (BRACKETS(Round, (IDENT(opStr, true), l1) :: Nil), l0) :: _ => consume exprCont(Var(opStr).withLoc(S(l1)), prec, allowNewlines = false) case (br @ BRACKETS(bk @ (Round | Square | Curly), toks), loc) :: _ => consume val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) val bra = (bk, res) match { case (Curly, _) => Bra(true, Rcd(res.map { case S(n) -> fld => n -> fld case N -> (fld @ Fld(_, v: Var)) => v -> fld case N -> fld => err(( msg"Record field should have a name" -> fld.value.toLoc :: Nil)) Var("") -> fld })) case (Round, (N, Fld(FldFlags(false, false, _), elt)) :: Nil) => Bra(false, elt) case (Round, _) => yeetSpaces match { case (KEYWORD(opStr @ "=>"), l1) :: (NEWLINE, l2) :: _ /* if opPrec(opStr)._1 > prec */ => consume val rhs = Blk(typingUnit.entities) Lam(Tup(res), rhs) case (KEYWORD("=>"), l1) :: _ => consume val e = expr(NewParser.opPrec("=>")._2) Lam(Tup(res), e) case (QUOTE, l0) :: (KEYWORD("=>"), l1) :: _ => exprCont(Tup(res), 0, true) match { case L(t) => unsupportedQuote(t.toLoc) case R(t) => t } case (IDENT("->", true), l1) :: _ => consume val rhs = expr(opPrec("->")._2) Lam(Tup(res), rhs) case _ => res match { case Nil => UnitLit(true) case _ => res.map { case N -> Fld(FldFlags.empty, t) => t case no -> Fld(_, t) => err((msg"Illegal position for field specification" -> Loc(no.toList :+ t) :: Nil)) t }.reduceRight((t, acc) => App(Var(",").withLoc(Loc(t :: acc :: Nil)), PlainTup(t, acc))) } } case _ => Tup(res) } exprCont(bra.withLoc(S(loc)), prec, allowNewlines = false) case (KEYWORD("forall"), l0) :: _ => consume def getIdents: Ls[TypeVar] = yeetSpaces match { case (IDENT(nme, false), l0) :: _ => consume val res = TypeVar(R(nme), N).withLoc(S(l0)) yeetSpaces match { case (COMMA, _) :: _ => consume res :: getIdents case _ => res :: Nil } case _ => Nil } val idents = getIdents val rest = cur match { case (KEYWORD(":"), l0) :: _ => consume expr(0) case _ => err((msg"Expected `:` after `forall` section" -> curLoc.orElse(lastLoc) :: Nil)) errExpr } R(Forall(idents, rest)) case (KEYWORD("while"), l0) :: _ => consume val cond = expr(0) val (success, _) = skip(KEYWORD("do")) // TODO kw? val body = expr(0) exprCont(While(cond, body).withLoc(S(l0 ++ body.toLoc)), prec, allowNewlines = false) case (KEYWORD("set"), l0) :: _ => consume val lhs = expr(0) val (success, _) = skip(KEYWORD("=")) // TODO kw? val rhs = expr(0)(fe = foundErr || !success, l = implicitly) exprCont(Assign(lhs, rhs).withLoc(S(l0 ++ rhs.toLoc)), prec, allowNewlines = false) case (KEYWORD("let"), l0) :: _ => consume letBindings(false) case (KEYWORD("new"), l0) :: c => consume val body = expr(NewParser.prec('.')) exprCont(NuNew(body).withLoc(S(l0 ++ body.toLoc)), prec, allowNewlines = false) case (KEYWORD("else"), l0) :: _ => consume yeetSpaces match { case (NEWLINE, l0) :: _ => consume ??? // TODO case _ => val e = expr(0) L(IfElse(e).withLoc(S(l0 ++ e.toLoc))) } case (KEYWORD("case"), l0) :: _ => consume exprOrIf(0)(et = true, fe = fe, l = implicitly) match { case L(body) => R(Lam(PlainTup(Var("case$scrut")), If(IfOpApp(Var("case$scrut"), Var("is"), body), N))) case R(rhs) => err((msg"Expected 'then'/'else' clause after 'case'; found ${rhs.describe} instead" -> rhs.toLoc :: msg"Note: 'case' expression starts here:" -> S(l0) :: Nil)) R(Lam(PlainTup(Var("case$scrut")), If(IfElse(rhs), N))) } case (KEYWORD("if"), l0) :: _ => consume exprOrIf(0)(et = true, fe = fe, l = implicitly) match { case L(body) => val els = yeetSpaces match { case (KEYWORD("else"), _) :: _ => consume S(exprOrBlockContinuation) case (NEWLINE, _) :: (KEYWORD("else"), _) :: _ => consume consume S(expr(0)) case (br @ BRACKETS(Indent, (KEYWORD("else"), _) :: toks), _) :: _ => consume val nested = rec(toks, S(br.innerLoc), br.describe) S(nested.concludeWith(_.expr(0))) case _ => N } R(If(body, els)) case R(e) => yeetSpaces match { case (br @ BRACKETS(Indent, (KEYWORD("then"), _) :: toks), _) :: _ => consume val nested = rec(toks, S(br.innerLoc), br.describe) val thn = nested.expr(0) val els = nested.yeetSpaces match { case (KEYWORD("else"), _) :: _ => nested.consume S(nested.concludeWith(_.expr(0))) case (NEWLINE, _) :: (KEYWORD("else"), _) :: _ => nested.consume nested.consume // S(thn, S(nested.concludeWith(_.expr(0)))) S(nested.concludeWith(_.expr(0))) case _ => nested.concludeWith(_ => ()) // S(thn, N) N } R(If(IfThen(e, thn), els)) case _cur => val (found, loc) = _cur match { case (tk, l1) :: _ => (msg"${e.describe} followed by ${tk.describe}", S(e.toLoc.foldRight(l1)(_ ++ _))) case Nil => (msg"${e.describe}", e.toLoc) } err((msg"Expected 'then'/'else' clause after 'if'; found $found instead" -> loc :: msg"Note: 'if' expression starts here:" -> S(l0) :: Nil)) R(If(IfThen(e, errExpr), N)) } } case Nil => err(msg"Unexpected end of $description; an expression was expected here" -> lastLoc :: Nil) R(errExpr) case ((SEMI /* | NEWLINE */ /* | BRACKETS(Curly, _) */, l0) :: _) => R(UnitLit(true).withLoc(S(l0))) // R(errExpr) // TODO case (IDENT("-", true), l0) :: _ /*if opPrec("-")._1 > prec*/ => // Unary minus consume val v = Var("-").withLoc(S(l0)) expr(opPrec("-")._2) match { case IntLit(i) => // Special case for negative literals exprCont(IntLit(-i), prec, false) case rhs: Term => // General case exprCont( if (newDefs) App(v, PlainTup(IntLit(BigInt(0)), rhs)) else App(App(v, PlainTup(IntLit(BigInt(0)))), PlainTup(rhs)) , prec, false) } case (tk, l0) :: _ => err(msg"Unexpected ${tk.describe} in expression position" -> S(l0) :: Nil) consume exprOrIf(prec)(et = et, fe = true, l = implicitly) } if (allAnns.isEmpty) res else res match { case Left(body) => body match { case IfThen(expr, rhs) => Left(IfThen(wrapAnns(expr, allAnns), rhs)) case _ => err(msg"Unexpected annotation" -> allAnns.head.toLoc :: Nil) L(body) // discard annotations for now } case Right(term) => R(wrapAnns(term, allAnns)) } } private def wrapAnns(trm: Term, anns: List[Term]) = anns.foldRight(trm)(Ann(_, _)) private def errExpr = // Tup(Nil).withLoc(lastLoc) // TODO FIXME produce error term instead UnitLit(true).withLoc(lastLoc) // TODO FIXME produce error term instead final def exprCont(acc: Term, prec: Int, allowNewlines: Bool)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(prec, s"`$acc`", allowNewlines) { l => cur match { case (QUOTE, l) :: _ => cur match { case _ :: (KEYWORD(opStr @ "=>"), l0) :: _ if opPrec(opStr)._1 > prec => consume consume exprCont(Quoted(Lam(acc match { case t: Tup => t case _ => PlainTup(acc) }, Unquoted(expr(1)(fe, implicitly)))), prec, allowNewlines) case _ :: (br @ BRACKETS(Round, toks), loc) :: _ => consume consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()).map { case nme -> Fld(flgs, t) => nme -> Fld(flgs, Unquoted(t)) } val res = App(Unquoted(acc), Tup(as).withLoc(S(loc))) exprCont(Quoted(res), prec, allowNewlines) case _ :: (IDENT(opStr, true), l0) :: _ => if (opPrec(opStr)._1 > prec) { consume consume val v = Var(opStr).withLoc(S(l0)) yeetSpaces match { case (NEWLINE, l0) :: _ => consume case _ => } exprOrIf(opPrec(opStr)._2) match { case L(rhs) => R(unsupportedQuote(S(l0))) case R(rhs) => exprCont(opStr match { case "with" => unsupportedQuote(S(l0)) case _ => Quoted(App(v, PlainTup(Unquoted(acc), Unquoted(rhs)))) }, prec, allowNewlines) } } else R(acc) case _ :: (KEYWORD("in"), _) :: _ => R(acc) case _ => consume unsupportedQuote(acc.toLoc) R(acc) } case (COMMA, l0) :: _ if prec === 0 => consume yeetSpaces match { case (NEWLINE, _) :: _ => consume case _ => } val rhs = expr(prec) // TODO support exprOrIf for comma operators R(App(Var(",").withLoc(S(l0)), PlainTup(acc, rhs))) case (KEYWORD(opStr @ "=>"), l0) :: (NEWLINE, l1) :: _ if opPrec(opStr)._1 > prec => consume val rhs = Blk(typingUnit.entities) R(Lam(PlainTup(acc), rhs)) case (KEYWORD(opStr @ "=>"), l0) :: _ if opPrec(opStr)._1 > prec => consume val rhs = expr(1) val res = Lam(PlainTup(acc), rhs) exprCont(res, prec, allowNewlines) case (IDENT(".", _), l0) :: (br @ BRACKETS(Square, toks), l1) :: _ => consume consume val idx = rec(toks, S(br.innerLoc), br.describe) .concludeWith(_.expr(0, allowSpace = true)) val newAcc = Subs(acc, idx).withLoc(S(l0 ++ l1 ++ idx.toLoc)) exprCont(newAcc, prec, allowNewlines) case (IDENT(opStr, true), l0) :: _ if /* isInfix(opStr) && */ opPrec(opStr)._1 > prec => consume val v = Var(opStr).withLoc(S(l0)) yeetSpaces match { case (NEWLINE, l0) :: _ => consume case _ => } exprOrIf(opPrec(opStr)._2) match { case L(rhs) => L(IfOpApp(acc, v, rhs)) case R(rhs) => exprCont(opStr match { case "with" => rhs match { case rhs: Rcd => With(acc, rhs)//.withLocOf(term) case Bra(true, rhs: Rcd) => With(acc, rhs)//.withLocOf(term) case _ => err(msg"record literal expected here; found ${rhs.describe}" -> rhs.toLoc :: Nil) acc } case _ => if (newDefs) App(v, PlainTup(acc, rhs)) else App(App(v, PlainTup(acc)), PlainTup(rhs)) }, prec, allowNewlines) } case (KEYWORD(":"), l0) :: _ if prec <= NewParser.prec(':') => consume R(Asc(acc, typ(0))) case (KEYWORD("where"), l0) :: _ if prec <= 1 => consume val tu = typingUnitMaybeIndented val res = Where(acc, tu.entities).withLoc(S(l0)) exprCont(res, prec, allowNewlines = false) case (SPACE, l0) :: _ => consume exprCont(acc, prec, allowNewlines) case (SELECT(name), l0) :: _ => // TODO precedence? consume exprCont(Sel(acc, Var(name).withLoc(S(l0))), prec, allowNewlines) // case (br @ BRACKETS(Indent, (SELECT(name), l0) :: toks), _) :: _ => case (br @ BRACKETS(Indent, (SELECT(name), l0) :: toks), _) :: _ if prec <= 1 => consume val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.exprCont(Sel(acc, Var(name).withLoc(S(l0))), 0, allowNewlines = true)) if (allowNewlines) res match { case L(ifb) => L(ifb) // TODO something else? case R(res) => exprCont(res, 0, allowNewlines) } else res case (br @ BRACKETS(Indent, (IDENT(opStr, true), l0) :: toks), _) :: _ => consume rec(toks, S(br.innerLoc), br.describe).concludeWith(_.opBlock(acc, opStr, l0)) case (KEYWORD("then"), _) :: _ if /* expectThen && */ prec === 0 => // case (KEYWORD("then"), _) :: _ if /* expectThen && */ prec <= 1 => consume L(IfThen(acc, exprOrBlockContinuation)) case (NEWLINE, _) :: (KEYWORD("then"), _) :: _ if /* expectThen && */ prec === 0 => consume consume L(IfThen(acc, exprOrBlockContinuation)) case (NEWLINE, _) :: _ if allowNewlines => consume exprCont(acc, 0, allowNewlines) case (br @ BRACKETS(Curly, toks), loc) :: _ if prec <= AppPrec => consume val tu = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.typingUnitMaybeIndented).withLoc(S(loc)) exprCont(Rft(acc, tu), prec, allowNewlines) case (COMMA | SEMI | NEWLINE | KEYWORD("then" | "else" | "in" | "=" | "do") | IDENT(_, true) | BRACKETS(Curly, _), _) :: _ => R(acc) case (KEYWORD("of"), _) :: _ if prec <= 1 => consume val as = argsMaybeIndented() val res = App(acc, Tup(as)) exprCont(res, prec, allowNewlines) case (br @ BRACKETS(Indent, (KEYWORD("of"), _) :: toks), _) :: _ if prec <= 1 => consume // // val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // val res = App(acc, Tup(as)) // exprCont(res, 0, allowNewlines = true) // ?! // val res = rec(toks, S(br.innerLoc), br.describe).concludeWith { nested => val as = nested.argsMaybeIndented() nested.exprCont(App(acc, Tup(as)), 0, allowNewlines = true) } // if (allowNewlines) res match { case L(ifb) => L(ifb) // TODO something else? case R(res) => exprCont(res, 0, allowNewlines) } // else res case (BRACKETS(Indent, (KEYWORD("then"|"else"), _) :: toks), _) :: _ => R(acc) /* case (br @ BRACKETS(Indent, toks), _) :: _ if prec === 0 && !toks.dropWhile(_._1 === SPACE).headOption.map(_._1).contains(KEYWORD("else")) // FIXME => consume val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.blockTerm) R(App(acc, res)) */ // case (br @ BRACKETS(Indent, (BRACKETS(Round | Square, toks1), _) :: toks2), _) :: _ => case (br @ BRACKETS(Indent, toks @ (BRACKETS(Round | Square, _), _) :: _), _) :: _ if prec <= 1 => consume val res = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.exprCont(acc, 0, allowNewlines = true)) res match { case L(ifb) => L(ifb) // TODO something else? case R(res) => exprCont(res, 0, allowNewlines) } case (br @ BRACKETS(Angle | Square, toks), loc) :: _ => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) // val res = TyApp(acc, as.map(_.mapSecond.to)) val res = TyApp(acc, as.map { case (N, Fld(FldFlags(false, false, _), trm)) => trm.toType match { case L(d) => raise(d); Top // TODO better case R(ty) => ty } case _ => ??? }).withLoc(acc.toLoc.fold(some(loc))(_ ++ loc |> some)) exprCont(res, prec, allowNewlines) /*case (br @ BRACKETS(Square, toks), loc) :: _ => // * Currently unreachable because we match Square brackets as tparams consume val idx = rec(toks, S(br.innerLoc), "subscript").concludeWith(_.expr(0)) val res = Subs(acc, idx.withLoc(S(loc))) exprCont(res, prec, allowNewlines)*/ case (br @ BRACKETS(Round, toks), loc) :: _ if prec <= AppPrec => consume val as = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()) val res = App(acc, Tup(as).withLoc(S(loc))) exprCont(res, prec, allowNewlines) case (KEYWORD("of"), _) :: _ => consume val as = argsMaybeIndented() // val as = argsOrIf(Nil) // TODO val res = App(acc, Tup(as)) exprCont(res, prec, allowNewlines) case c @ (h :: _) if (h._1 match { case KEYWORD(":" | "of" | "where" | "extends") | SEMI | BRACKETS(Round | Square, _) | BRACKETS(Indent, ( KEYWORD("of") | SEMI | BRACKETS(Round | Square, _) | SELECT(_) , _) :: _) => false case _ => true }) => val as = argsMaybeIndented() val res = App(acc, Tup(as)) raise(WarningReport(msg"Paren-less applications should use the 'of' keyword" -> res.toLoc :: Nil, newDefs = true)) exprCont(res, prec, allowNewlines) case _ => R(acc) } } final def opBlock(acc: Term, opStr: Str, opLoc: Loc)(implicit et: ExpectThen, fe: FoundErr, l: Line): IfBody \/ Term = wrap(s"`$acc`", opStr) { l => val opv = Var(opStr).withLoc(S(opLoc)) val rhs = exprOrIf(0) // val rhs = exprOrIf(1) rhs match { case R(rhs) => val res = App(opv, PlainTup(acc, rhs)) cur match { case (NEWLINE, _) :: c => // TODO allow let bindings... consume c match { case (IDENT(opStr2, true), opLoc2) :: _ => consume opBlock(res, opStr2, opLoc2) case (tk, lo) :: _ => err(msg"Unexpected ${tk.describe} in operator block" -> S(lo) :: Nil) consume R(res) case Nil => R(res) } case _ => R(res) } case L(rhs) => val (opsRhss, els) = opIfBlock(opv -> rhs :: Nil) val opsApp = IfOpsApp(acc, opsRhss) L(els.fold[IfBody](opsApp)(trm => IfBlock(L(opsApp) :: L(IfElse(trm)) :: Nil))) } } final def opIfBlock(acc: Ls[Var -> IfBody])(implicit et: ExpectThen, fe: FoundErr): (Ls[Var -> IfBody], Opt[Term]) = wrap(acc) { l => cur match { case (NEWLINE, _) :: c => // TODO allow let bindings... consume c match { case (IDENT("_", false), wcLoc) :: _ => exprOrIf(0) match { case R(rhs) => err(msg"expect an operator branch" -> S(wcLoc) :: Nil) (acc.reverse, N) case L(IfThen(_, els)) => (acc.reverse, S(els)) case L(rhs) => err(msg"expect 'then' after the wildcard" -> rhs.toLoc :: Nil) (acc.reverse, N) } case (IDENT(opStr2, true), opLoc2) :: _ => consume val rhs = exprOrIf(0) // val rhs = exprOrIf(1) rhs match { case R(rhs) => ??? case L(rhs) => opIfBlock(Var(opStr2).withLoc(S(opLoc2)) -> rhs :: acc) } case (KEYWORD("else"), elseLoc) :: tail => consume exprOrIf(0) match { case R(rhs) => (acc.reverse, S(rhs)) case L(rhs) => err(msg"expect a term" -> rhs.toLoc :: Nil) (acc.reverse, N) } case (_, headLoc) :: _ => // printDbg(c) err(msg"expect an operator" -> S(headLoc) :: Nil) (acc.reverse, N) case Nil => (acc.reverse, N) } case _ => (acc.reverse, N) } } final def tyArgs(implicit fe: FoundErr, et: ExpectThen): Opt[Ls[Type]] = cur match { case (IDENT("<", true), l0) :: _ => ??? case _ => ??? } // TODO support line-broken param lists; share logic with args/argsOrIf def typeParams(implicit fe: FoundErr, et: ExpectThen): Ls[(Opt[VarianceInfo], TypeName)] = { val vinfo = yeetSpaces match { case (KEYWORD("in"), l0) :: (KEYWORD("out"), l1) :: _ => consume S(VarianceInfo.in, l0 ++ l1) case (KEYWORD("in"), l0) :: _ => consume S(VarianceInfo.contra, l0) case (KEYWORD("out"), l0) :: _ => consume S(VarianceInfo.co, l0) case _ => N } yeetSpaces match { case (IDENT(nme, false), l0) :: _ => consume val tyNme = TypeName(nme).withLoc(S(l0)) yeetSpaces match { case (COMMA, l0) :: _ => consume vinfo.map(_._1) -> tyNme :: typeParams case _ => vinfo.map(_._1) -> tyNme :: Nil } case _ => vinfo match { case S((_, loc)) => err(msg"dangling variance information" -> S(loc) :: Nil) case N => } Nil } } final def maybeIndented[R](f: (NewParser, Bool) => R)(implicit fe: FoundErr, et: ExpectThen): R = cur match { case (br @ BRACKETS(Indent, toks), _) :: _ if (toks.headOption match { case S((KEYWORD("then" | "else"), _)) => false case _ => true }) => consume rec(toks, S(br.innerLoc), br.describe).concludeWith(f(_, true)) case _ => f(this, false) } final def argsMaybeIndented()(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = maybeIndented(_.args(_)) // final def argsMaybeIndented()(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = // cur match { // case (br @ BRACKETS(Indent, toks), _) :: _ if (toks.headOption match { // case S((KEYWORD("then" | "else"), _)) => false // case _ => true // }) => // consume // rec(toks, S(br.innerLoc), br.describe).concludeWith(_.args(true)) // case _ => args(false) // } // TODO support comma-less arg blocks...? final def args(allowNewlines: Bool, prec: Int = NoElsePrec)(implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> Fld] = // argsOrIf(Nil).map{case (_, L(x))=> ???; case (n, R(x))=>n->x} // TODO argsOrIf(Nil, Nil, allowNewlines, prec).flatMap{case (n, L(x))=> err(msg"Unexpected 'then'/'else' clause" -> x.toLoc :: Nil) n->Fld(FldFlags.empty, errExpr)::Nil case (n, R(x))=>n->x::Nil} // TODO /* final def argsOrIf2()(implicit fe: FoundErr, et: ExpectThen): IfBlock \/ Ls[Opt[Var] -> Fld] = { // argsOrIf(Nil).partitionMap(identity).mapFirst(ifbods => ???) argsOrIf(Nil) match { case n -> L(ib) => case n -> R(tm) => case Nil => R(Nil) } } */ final def argsOrIf(acc: Ls[Opt[Var] -> (IfBody \/ Fld)], seqAcc: Ls[Statement], allowNewlines: Bool, prec: Int = NoElsePrec) (implicit fe: FoundErr, et: ExpectThen): Ls[Opt[Var] -> (IfBody \/ Fld)] = wrap(acc, seqAcc) { l => val anns = parseAnnotations(false) cur match { case Nil => seqAcc match { case res :: seqAcc => (N -> R(Fld(FldFlags.empty, Blk((res :: seqAcc).reverse))) :: acc).reverse case Nil => acc.reverse } case (SPACE, _) :: _ => consume argsOrIf(acc, seqAcc, allowNewlines, prec) case (NEWLINE, _) :: _ => // TODO: | ... assert(seqAcc.isEmpty) acc.reverse case (IDENT(nme, true), _) :: _ if nme =/= "-" => // TODO: | ... assert(seqAcc.isEmpty) acc.reverse case _ => // val blck = block val argVal = yeetSpaces match { case (KEYWORD("val"), l0) :: _ => consume S(l0) case _ => N } val argMut = yeetSpaces match { case (KEYWORD("mut"), l0) :: _ => consume S(l0) case _ => N } val argSpec = yeetSpaces match { case (KEYWORD("#"), l0) :: _ => consume S(l0) case _ => N } val argName = yeetSpaces match { case (IDENT(idStr, false), l0) :: (KEYWORD(":"), _) :: _ => // TODO: | ... consume consume S(Var(idStr).withLoc(S(l0))) case (LITVAL(IntLit(i)), l0) :: (KEYWORD(":"), _) :: _ => // TODO: | ... consume consume S(Var(i.toString).withLoc(S(l0))) case _ => N } // val e = expr(NoElsePrec) -> argMut.isDefined val e = exprOrIf(prec, true, anns).map(Fld(FldFlags(argMut.isDefined, argSpec.isDefined, argVal.isDefined), _)) def mkSeq = if (seqAcc.isEmpty) argName -> e else e match { case L(_) => ??? case R(Fld(flags, res)) => argName -> R(Fld(flags, Blk((res :: seqAcc).reverse))) } cur match { case (COMMA, l0) :: (NEWLINE, l1) :: _ => consume consume argsOrIf(mkSeq :: acc, Nil, allowNewlines) case (COMMA, l0) :: _ => consume argsOrIf(mkSeq :: acc, Nil, allowNewlines) case (NEWLINE, l1) :: _ if allowNewlines => consume argName match { case S(nme) => err(msg"Unexpected named argument name here" -> nme.toLoc :: Nil) case N => } e match { case L(_) => ??? case R(Fld(FldFlags(false, false, _), res)) => argsOrIf(acc, res :: seqAcc, allowNewlines) case R(_) => ??? } case _ => (mkSeq :: acc).reverse } } } final def bindings(acc: Ls[Var -> Term])(implicit fe: FoundErr): Ls[Var -> Term] = cur match { case (SPACE, _) :: _ => consume bindings(acc) case (NEWLINE | IDENT(_, true) | SEMI, _) :: _ => // TODO: | ... acc.reverse case (IDENT(id, false), l0) :: _ => consume val (success, _) = skip(KEYWORD("=")) // TODO kw? val rhs = expr(0)(fe = foundErr || !success, l = implicitly) // cur.dropWhile(_ === SPACE) match { // case (KEYWORD("in"), _) :: _ => // acc.reverse // case _ => ??? // } val v = Var(id).withLoc(S(l0)) cur match { case (COMMA, l1) :: _ => consume bindings((v -> rhs) :: acc) case _ => ((v -> rhs) :: acc).reverse } case _ => Nil } final def mkType(trm: Term): Type = trm.toType match { case L(d) => raise(d); Top // TODO better case R(ty) => ty } } ================================================ FILE: shared/src/main/scala/mlscript/NormalForms.scala ================================================ package mlscript import scala.collection.mutable import scala.collection.mutable.{Map => MutMap, Set => MutSet} import scala.collection.immutable.{SortedSet, SortedMap} import SortedSet.{empty => ssEmp}, SortedMap.{empty => smEmp} import scala.util.chaining._ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ class NormalForms extends TyperDatatypes { self: Typer => type ExpandTupleFields >: Bool type PreserveTypeRefs >: Bool def preserveTypeRefs(implicit ptr: PreserveTypeRefs): Bool = ptr === true def expandTupleFields(implicit etf: ExpandTupleFields): Bool = etf === true private def mergeTypeRefs(pol: Bool, trs1: SortedMap[TN, TR], trs2: SortedMap[TN, TR])(implicit ctx: Ctx): SortedMap[TN, TR] = mergeSortedMap(trs1, trs2) { (tr1, tr2) => assert(tr1.defn === tr2.defn) assert(tr1.targs.size === tr2.targs.size) TypeRef(tr1.defn, (tr1.targs lazyZip tr2.targs).map((ta1, ta2) => if (pol) TypeBounds.mk(ta1 & ta2, ta1 | ta2) else TypeBounds.mk(ta1 | ta2, ta1 & ta2) ))(noProv) } sealed abstract class LhsNf { final def compareEquiv(that: LhsNf): Int = (this, that) match { case (LhsRefined(b1, ts1, r1, trs1), LhsRefined(b2, ts2, r2, trs2)) => var cmp = (b1, b2) match { case (S(c1), S(c2)) => c1.compareEquiv(c2) case (S(c1), N) => -1 case (N, S(c2)) => 1 case (N, N) => 0 } if (cmp =/= 0) return cmp // * Just compare the heads for simplicity... cmp = (trs1.headOption, trs2.headOption) match { case (S((n1, _)), S((n2, _))) => n1.compare(n2) // * in principle we could go on to compare the tails if this is 0 case (S(_), N) => 1 case (N, S(_)) => -1 case (N, N) => 0 } if (cmp =/= 0) return cmp cmp = -trs1.sizeCompare(trs2) if (cmp =/= 0) return cmp cmp = ts1.sizeCompare(ts2.size) if (cmp =/= 0) return cmp cmp = r1.fields.sizeCompare(r2.fields) cmp case (LhsTop, _) => 1 case (_, LhsTop) => -1 } def toTypes: Ls[SimpleType] = toType() :: Nil def toType(sort: Bool = false): SimpleType = if (sort) mkType(true) else underlying private def mkType(sort: Bool): SimpleType = this match { case LhsRefined(bo, ts, r, trs) => val sr = if (sort) r.sorted else r val bo2 = bo.filter { case ClassTag(id, parents) => !trs.contains(TypeName(id.idStr.capitalize)) case _ => true } val trsBase = trs.valuesIterator.foldRight(bo2.fold[ST](sr)(_ & sr))(_ & _) (if (sort) ts.toArray.sorted else ts.toArray).foldLeft(trsBase)(_ & _) case LhsTop => TopType } lazy val underlying: SimpleType = mkType(false) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = this match { case LhsRefined(base, ttags, reft, trefs) => (base.iterator.map(_.levelBelow(ub)) ++ ttags.iterator.map(_.levelBelow(ub)) ++ reft.fields.iterator.values.map(_.levelBelow(ub)) ++ trefs.iterator.values.map(_.levelBelow(ub)) ).reduceOption(_ max _).getOrElse(MinLevel) case LhsTop => MinLevel } def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]): LhsNf = this match { case LhsRefined(bo, ts, r, trs) => LhsRefined(bo.map(_.freshenAbove(lim, rigidify)), ts, r.freshenAbove(lim, rigidify), trs.view.mapValues(_.freshenAbove(lim, rigidify)).to(SortedMap)) case LhsTop => this } def hasTag(ttg: AbstractTag): Bool = this match { case LhsRefined(bo, ts, r, trs) => ts(ttg) case LhsTop => false } def size: Int = this match { case LhsRefined(bo, ts, r, trs) => bo.size + ts.size + r.fields.size + trs.size case LhsTop => 0 } def & (that: AbstractTag): LhsNf = this match { case LhsTop => LhsRefined(N, SortedSet.single(that), RecordType.empty, smEmp) case LhsRefined(b, ts, r, trs) => LhsRefined(b, ts + that, r, trs) } def & (that: BaseTypeOrTag, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[LhsNf] = (this, that) match { case (LhsTop, that: TupleType) => S(LhsRefined(S(that), ssEmp, if (expandTupleFields) that.toRecord else RecordType.empty, smEmp)) case (LhsTop, that: BaseType) => S(LhsRefined(S(that), ssEmp, RecordType.empty, smEmp)) case (LhsTop, that: AbstractTag) => S(LhsRefined(N, SortedSet.single(that), RecordType.empty, smEmp)) case (LhsRefined(b1, ts, r1, trs), that: AbstractTag) => S(LhsRefined(b1, ts + that, r1, trs)) case (LhsRefined(b1, ts, r1, trs), that: BaseType) => var r1Final = r1 ((b1, that) match { case (S(p0 @ ClassTag(pt0, ps0)), p1 @ ClassTag(pt1, ps1)) => // println(s"!GLB! $this $that ${p0.glb(p1)}") p0.glb(p1) case (S(l @ FunctionType(l0, r0)), FunctionType(l1, r1)) if approximateNegativeFunction && !pol => S(FunctionType(l0 | l1, r0 & r1)(l.prov)) // * Note: it also feels natural to simplify `f: int -> int & nothing -> string` // * to just `f: int -> int`, but these types are not strictly-speaking equivalent; // * indeed, we could still call such a function as `f error : string`... // * (This is similar to how `{ x: nothing }` is not the same as `nothing`.) // * Note: in any case it should be fine to make this approximation in positive positions. // * Still, it seems making this approximation is morally correct even in negative positions, // * at least in a CBV setting, since the only way to use the bad function component is // * by passing a non-returning computation. So this should be semantically sound. case (S(FunctionType(AliasOf(TupleType(fs)), _)), _: Overload | _: FT) if fs.exists(_._2.ub.isBot) => S(that) case (sov @ S(Overload(alts)), FunctionType(AliasOf(TupleType(fs)), _)) if fs.exists(_._2.ub.isBot) => sov case (S(ov @ Overload(alts)), ft: FunctionType) => def go(alts: Ls[FT]): Ls[FT] = alts match { case (f @ FunctionType(l, r)) :: alts => /* // * Note: A simpler version that gets most of the way there: if (l >:< ft.lhs) FunctionType(l, r & ft.rhs)(f.prov) :: alts else if (r >:< ft.rhs) FunctionType(l | ft.lhs, r)(f.prov) :: alts else f :: go(alts) */ val l_LT_lhs = l <:< ft.lhs lazy val l_GT_lhs = ft.lhs <:< l lazy val r_LT_rhs = r <:< ft.rhs lazy val r_GT_rhs = ft.rhs <:< r if (l_LT_lhs && r_GT_rhs) ft :: alts else if (l_GT_lhs && r_LT_rhs) f :: alts else if (l_LT_lhs && l_GT_lhs) FunctionType(l, r & ft.rhs)(f.prov) :: alts else if (r_LT_rhs && r_GT_rhs) FunctionType(l | ft.lhs, r)(f.prov) :: alts else f :: go(alts) case Nil => ft :: Nil } S(Overload(go(alts))(ov.prov)) case (S(ft: FunctionType), _: Overload | _: FT) => return LhsRefined(S(Overload(ft :: Nil)(that.prov)), ts, r1, trs) & (that, pol) case (S(Overload(alts1)), ov2 @ Overload(a2 :: alts2)) => alts2 match { case Nil => return this & (a2, pol) case _ => return this & (a2, pol) flatMap (_ & (Overload(alts2)(ov2.prov), pol)) } case (S(TupleType(fs0)), tup @ TupleType(fs1)) => if (fs0.size === fs1.size) { if (expandTupleFields) r1Final = RecordType(mergeSortedMap(r1Final.fields, tup.toRecord.fields)(_ && _).toList)(noProv) S(TupleType(tupleIntersection(fs0, fs1))(noProv)) } else N case (S(ArrayType(ar)), tup @ TupleType(fs)) => if (expandTupleFields) r1Final = RecordType(mergeSortedMap(r1Final.fields, tup.toRecord.fields)(_ && _).toList)(noProv) S(TupleType(fs.map { ty => ty._1 -> (ar && ty._2) })(noProv)) case (S(TupleType(fs)), ArrayType(ar)) => S(TupleType(fs.map { ty => ty._1 -> (ty._2 && ar) })(noProv)) case (S(ArrayType(i1)), ArrayType(i2)) => // Array[p] & Array[q] => Array[p & q] S(ArrayType(i1 && i2)(noProv)) case (S(w1 @ Without(b1, ns1)), w2 @ Without(b2, ns2)) if ns1 === ns2 => S(Without(b1 & b2, ns1)(w1.prov & w2.prov)) // The following cases are quite hacky... if we find two incompatible Without types, // just make a new dummy Without type to merge them. // The workaround is due to the fact that unlike other types, we can't fully // reduce Without types away, so they are "unduly" treated as `BaseType`s. // This will be fixed whe we support proper TV bounds for homomorphic type computations case (S(b), w: Without) => S(Without(b & w, ssEmp)(noProv)) case (S(w: Without), b) => S(Without(w & b, ssEmp)(noProv)) case (S(_: TypeTag), _: FunctionType | _: ArrayBase | _: Overload) | (S(_: FunctionType | _: ArrayBase | _: Overload), _: TypeTag) | (S(_: FunctionType), _: ArrayBase | _: Overload) | (S(_: ArrayBase | _: Overload), _: FunctionType) | (S( _: ArrayBase), _: Overload) | (S(_: Overload), _: ArrayBase) => N case (S(Overload(Nil)), _) | (S(_), Overload(Nil)) => die // Overload must have nonempty alts case (S(_: SpliceType), _) | (S(_), _: SpliceType) => ??? // TODO case (N, tup: TupleType) => if (expandTupleFields) r1Final = RecordType(mergeSortedMap(r1Final.fields, tup.toRecord.fields)(_ && _).toList)(noProv) S(that) case (N, _) => S(that) }) map { b => LhsRefined(S(b), ts, r1Final, trs) } } def & (that: RecordType): LhsNf = this match { case LhsTop => LhsRefined(N, ssEmp, that, smEmp) case LhsRefined(b1, ts, r1, trs) => LhsRefined(b1, ts, RecordType(recordIntersection(r1.fields, that.fields))(noProv/*TODO*/), trs) } def & (that: TypeRef, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[LhsNf] = this match { case LhsTop => S(LhsRefined(N, ssEmp, RecordType.empty, SortedMap.single(that.defn -> that))) case LhsRefined(b, ts, rt, trs) => val trs2 = trs + (that.defn -> trs.get(that.defn).fold(that) { other => var thatTargs = that.targs assert(thatTargs.sizeCompare(other.targs) === 0) val newTargs = other.mapTargs(S(true)) { (vce, otherTarg) => val thatTarg = thatTargs.head thatTargs = thatTargs.tail vce match { case S(true) => otherTarg & thatTarg case S(false) => otherTarg | thatTarg case N => if (pol) TypeBounds.mk(otherTarg | thatTarg, otherTarg & thatTarg) else TypeBounds.mk(otherTarg & thatTarg, otherTarg | thatTarg) } } TypeRef(that.defn, newTargs)(that.prov) }) val res = LhsRefined(b, ts, rt, trs2) that.mkClsTag.fold(S(res): Opt[LhsNf])(res & (_, pol)) } def & (that: LhsNf, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[LhsNf] = (this, that) match { case (_, LhsTop) => S(this) case (LhsTop, _) => S(that) case (_, LhsRefined(bo, ts, rt, trs)) => ts.iterator.foldLeft( trs.valuesIterator.foldLeft((bo.fold(some(this & rt))(this & rt & (_, pol))))(_.getOrElse(return N) & (_, pol)) )(_.getOrElse(return N) & (_, pol)) } def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = (this, that) match { case (LhsRefined(_, _, reft, trs), RhsField(nme, ft)) => reft <:< RecordType(nme -> ft :: Nil)(noProv) case (lhs @ LhsRefined(bse, tts, reft, trs), RhsBases(tags, rest, trefs)) => tags.exists(tag => bse match { case S(cls: ClassTag) => tag.id === cls.id || lhs.allTags.contains(tag.id) case _ => false } ) || (rest match { case S(R(f: RhsField)) => this <:< f case S(L(rhs @ (_: FunctionType | _: Overload | _: ArrayBase | _: TupleType))) => bse.exists(_ <:< rhs) case S(L(wt: Without)) => return underlying <:< that.underlying case N => false }) || trefs.exists { case (_, tr) => underlying <:< tr } case (LhsTop, _) => false case (_, RhsBot) => false } def <:< (that: LhsNf)(implicit ctx: Ctx): Bool = (this, that) match { case (_, LhsTop) => true case (LhsTop, _) => false case (LhsRefined(b1, ts1, rt1, trs1), LhsRefined(b2, ts2, rt2, trs2)) => b2.forall(b2 => b1.exists(_ <:< b2)) && ts2.forall(ts1) && rt1 <:< rt2 && trs2.valuesIterator.forall(tr2 => trs1.valuesIterator.exists(_ <:< tr2)) } def isTop: Bool = isInstanceOf[LhsTop.type] } case class LhsRefined(base: Opt[BaseType], ttags: SortedSet[AbstractTag], reft: RecordType, trefs: SortedMap[TypeName, TypeRef]) extends LhsNf { // assert(!trefs.exists(primitiveTypes contains _._1.name)) lazy val allTags: Set[IdentifiedTerm] = ttags.iterator.foldLeft(base match { case S(cls: ClassTag) => cls.parentsST + cls.id case _ => Set.empty[IdentifiedTerm] }) { case (acc, tt: TraitTag) => acc ++ tt.parentsST + tt.id case (acc, _) => acc } override def toString: Str = s"${base.getOrElse("")}${reft}${ (ttags.iterator ++ trefs.valuesIterator).map("∧"+_).mkString}" } case object LhsTop extends LhsNf { override def toString: Str = "⊤" } sealed abstract class RhsNf { final def compareEquiv(that: RhsNf): Int = (this, that) match { case (RhsField(n1, t1), RhsField(n2, t2)) => n1.compare(n2) case (RhsBases(ps1, bf1, trs1), RhsBases(ps2, bf2, trs2)) => var cmp = ps1.minOption match { case S(m1) => ps2.minOption match { case S(m2) => m1.compare(m2) case N => ps1.size.compare(ps2.size) } case N => ps1.size.compare(ps2.size) } if (cmp =/= 0) return cmp cmp = (trs1.headOption, trs2.headOption) match { case (S((n1, _)), S((n2, _))) => n1.compare(n2) case (S(_), N) => 1 case (N, S(_)) => -1 case (N, N) => 0 } if (cmp =/= 0) return cmp cmp = -trs1.sizeCompare(trs2) cmp case (_: RhsBases, _) => -1 case (_, _: RhsBases) => 1 case (_: RhsField, _) => -1 case (_, _: RhsField) => 1 case (RhsBot, RhsBot) => 0 } def toTypes: Ls[SimpleType] = toType() :: Nil def toType(sort: Bool = false): SimpleType = if (sort) mkType(true) else underlying private def mkType(sort: Bool): SimpleType = this match { case RhsField(n, t) => RecordType(n -> t :: Nil)(noProv) // FIXME prov case RhsBases(ps, bf, trs) => val sr = bf.fold(BotType: ST)(_.fold(identity, _.toType(sort))) val trsBase = trs.valuesIterator.foldRight(sr)(_ | _) (if (sort) ps.sorted else ps).foldLeft(trsBase)(_ | _) case RhsBot => BotType } lazy val underlying: SimpleType = mkType(false) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = underlying.levelBelow(ub) // TODO avoid forcing `underlying`! def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): RhsNf def hasTag(ttg: AbstractTag): Bool = this match { case RhsBases(ts, _, trs) => ts.contains(ttg) case RhsBot | _: RhsField => false } def | (that: TypeRef, pol: Bool)(implicit ctx: Ctx): Opt[RhsNf] = this match { case RhsBot => S(RhsBases(Nil, N, SortedMap.single(that.defn -> that))) case RhsField(name, ty) => this | name -> ty case RhsBases(prims, bf, trs) => val trs2 = trs + (that.defn -> trs.get(that.defn).fold(that) { other => var thatTargs = that.targs assert(thatTargs.sizeCompare(other.targs) === 0) val newTargs = other.mapTargs(S(true)) { (vce, otherTarg) => val thatTarg = thatTargs.head thatTargs = thatTargs.tail vce match { case S(true) => otherTarg | thatTarg case S(false) => otherTarg & thatTarg case N => if (pol) TypeBounds.mk(otherTarg & thatTarg, otherTarg | thatTarg) else TypeBounds.mk(otherTarg | thatTarg, otherTarg & thatTarg) } } TypeRef(that.defn, newTargs)(that.prov) }) S(RhsBases(prims, bf, trs2)) } def | (that: RhsNf, pol: Bool)(implicit ctx: Ctx): Opt[RhsNf] = that match { case RhsBases(prims, bf, trs) => val thisWithTrs = trs.valuesIterator.foldLeft(this)(_ | (_, pol) getOrElse (return N)) val tmp = prims.foldLeft(thisWithTrs)(_ | _ getOrElse (return N)) S(bf.fold(tmp)(_.fold(tmp | _ getOrElse (return N), tmp | _.name_ty getOrElse (return N)))) case RhsField(name, ty) => this | name -> ty case RhsBot => S(this) } def tryMergeInter(that: RhsNf)(implicit ctx: Ctx): Opt[RhsNf] = (this, that) match { case (RhsBot, _) | (_, RhsBot) => S(RhsBot) case (RhsField(name1, ty1), RhsField(name2, ty2)) if name1 === name2 => S(RhsField(name1, ty1 && ty2)) case (RhsBases(prims1, S(R(r1)), trs1), RhsBases(prims2, S(R(r2)), trs2)) if prims1 === prims2 && trs1 === trs2 && r1.name === r2.name => S(RhsBases(prims1, S(R(RhsField(r1.name, r1.ty && r2.ty))), trs1)) case (RhsBases(prims1, bf1, trs1), RhsBases(prims2, bf2, trs2)) if prims1 === prims2 && bf1 === bf2 && trs1.keySet === trs2.keySet => // * eg for merging `~Foo[S] & ~Foo[T]`: S(RhsBases(prims1, bf1, mergeTypeRefs(false, trs1, trs2))) case (RhsBases(prims1, bf1, trs1), RhsBases(prims2, bf2, trs2)) => N // TODO could do some more merging here – for the possible base types case _ => N } def | (that: AbstractTag): RhsNf = this match { case RhsBot => RhsBases(that :: Nil, N, smEmp) case rf: RhsField => RhsBases(that :: Nil, S(R(rf)), smEmp) case RhsBases(ps, bf, trs) => RhsBases(that :: ps, bf, trs) } // TODO use inheritance hierarchy to better merge these def | (that: BaseTypeOrTag): Opt[RhsNf] = (this, that) match { case (RhsBot, p: TypeTag) => S(RhsBases(p::Nil,N,smEmp)) case (RhsBot, that: MiscBaseType) => S(RhsBases(Nil,S(L(that)),smEmp)) case (RhsBases(ps, bf, trs), p: ClassTag) => S(RhsBases(if (ps.contains(p)) ps else p :: ps , bf, trs)) case (RhsBases(ps, N, trs), that: MiscBaseType) => S(RhsBases(ps, S(L(that)), trs)) case (RhsBases(ps, S(L(t1@TupleType(fs1))), trs), t2@TupleType(fs2)) => if (fs1.size =/= fs2.size) RhsBases(ps, S(L(t1.toArray)), trs) | t2.toArray // upcast tuples of different sizes to array else S(RhsBases(ps, S(L(TupleType(fs1.lazyZip(fs2).map { case ((S(n1), ty1), (S(n2), ty2)) => (if (n1 === n2) S(n1) else N, ty1 || ty2) case ((n1o, ty1), (n2o, ty2)) => (n1o orElse n2o, ty1 || ty2) })(noProv))), trs)) case (RhsBases(ps, S(L(ArrayType(_))), trs), t@TupleType(_)) => this | t.toArray case (RhsBases(ps, S(L(t@TupleType(_))), trs), ar@ArrayType(_)) => RhsBases(ps, S(L(t.toArray)), trs) | ar case (RhsBases(ps, S(L(ArrayType(ar1))), trs), ArrayType(ar2)) => S(RhsBases(ps, S(L(ArrayType(ar1 || ar2)(noProv))), trs)) case (RhsBases(_, S(L(_: Without)), _), _) | (_, _: Without) => die // Without should be handled elsewhere case (RhsBases(ps, S(L(bt)), trs), _) if (that === bt) => S(this) // * Note: despite erased first-class polymorphism meaning we no longer have // * `(A -> B) & (C -> D) =:= (A | C) -> (B & D)`, // * I think we do still have `(A -> B) | (C -> D) =:= (A & C) -> (B | D)`, // * because these two types still have no other meaningful LUB. case (RhsBases(ps, S(L(FunctionType(l0, r0))), trs), FunctionType(l1, r1)) => S(RhsBases(ps, S(L(FunctionType(l0 & l1, r0 | r1)(noProv))), trs)) case (RhsBases(ps, S(L(ov @ Overload(fts))), trs), FunctionType(l2, r2)) => S(RhsBases(ps, S(L(Overload(fts.map { case ft1 @FunctionType(l1, r1) => FunctionType(l1 & l2, r1 | r2)(ft1.prov) })(ov.prov))), trs)) case (RhsBases(ps, S(L(ft: FunctionType)), trs), ov: Overload) => RhsBases(ps, S(L(ov)), trs) | ft // * TODO? or it might be counter-productive if it leads to bigger types due by distribution // case (RhsBases(ps, S(L(ov1: Overload)), trs), ov2: Overload) => ??? case (RhsBases(_, S(L(_: Overload)), _), _) | (_, _: Overload) => N case (RhsBases(ps, bf, trs), tt: AbstractTag) => S(RhsBases(if (ps.contains(tt)) ps else tt :: ps, bf, trs)) case (f @ RhsField(_, _), p: TypeTag) => S(RhsBases(p::Nil, S(R(f)), smEmp)) case (f @ RhsField(_, _), _: FunctionType | _: ArrayBase) => // S(RhsBases(Nil, S(that), S(f))) N // can't merge a record and a function or a tuple -> it's the same as Top // NOTE: in the future, if we do actually register fields in named tuples // (so their fields is not pure compiler fiction, // as it is currently and in TypeScript arrays), // we will want to revisit this... case (RhsBases(_, S(L(_: FunctionType)), _), _: ArrayBase) | (RhsBases(_, S(L(_: ArrayBase)), _), _: FunctionType) | (RhsBases(_, S(R(_)), _), _: FunctionType | _: ArrayBase) => N case (RhsBases(_, Some(Left(SpliceType(_))), _), _) | (_, _: SpliceType) => ??? // TODO } def | (that: (Var, FieldType)): Opt[RhsNf] = this match { case RhsBot => S(RhsField(that._1, that._2)) case RhsField(n1, t1) if n1 === that._1 => S(RhsField(n1, t1 || that._2)) case RhsBases(p, N, trs) => S(RhsBases(p, S(R(RhsField(that._1, that._2))), trs)) case RhsBases(p, S(R(RhsField(n1, t1))), trs) if n1 === that._1 => S(RhsBases(p, S(R(RhsField(n1, t1 || that._2))), trs)) case _: RhsField | _: RhsBases => N } // def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = this.underlying <:< that.underlying def <:< (that: RhsNf)(implicit ctx: Ctx): Bool = (this, that) match { case (RhsBases(tags, S(R(fld)), trs), fld2: RhsField) => tags.isEmpty && trs.valuesIterator.forall(_ <:< fld2.underlying) && fld <:< fld2 case (RhsBases(tags, _, trs), fld2: RhsField) => false case (RhsField(nme, fty), RhsBases(tags2, S(R(fld)), trs2)) => this <:< fld case (RhsField(nme, fty), RhsBases(tags2, S(L(_)) | N, trs2)) => false case (RhsField(nme, fty), RhsField(nme2, fty2)) => nme === nme2 && fty <:< fty2 case (RhsBases(tags1, res1, trs1), rhs @ RhsBases(tags2, res2, trs2)) => tags1.forall(tag1 => tags2.exists(_.id === tag1.id) // TODO also take parents into account... ) && trs1.forall { case (_, tr1) => // tr1 <:< rhs.underlying // * probably not necessary trs2.exists { case (_, tr2) => tr1 <:< tr2 } } && ((res1, res2) match { case (S(L(b)), S(L(b2))) => b <:< b2 case (N, _) => true case _ => false }) case (RhsBot, _) => true case (_, RhsBot) => false } def isBot: Bool = isInstanceOf[RhsBot.type] } case class RhsField(name: Var, ty: FieldType) extends RhsNf { def name_ty: Var -> FieldType = name -> ty def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): RhsField = RhsField(name, ty.update(self.freshenAbove(lim, _, rigidify = rigidify), self.freshenAbove(lim, _, rigidify = rigidify))) override def toString: Str = s"{${name.name}:$ty}" } case class RhsBases(tags: Ls[TypeTag], rest: Opt[MiscBaseType \/ RhsField], trefs: SortedMap[TypeName, TypeRef]) extends RhsNf { def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): RhsBases = RhsBases(tags, rest.map(_ match { case L(v) => L(v.freshenAboveImpl(lim, rigidify)) case R(v) => R(v.freshenAbove(lim, rigidify)) }), trefs.view.mapValues(_.freshenAbove(lim, rigidify)).to(SortedMap)) override def toString: Str = s"${tags.mkString("|")}${rest.fold("")("|" + _.fold(""+_, ""+_))}${trefs.valuesIterator.map("|"+_).mkString}" } case object RhsBot extends RhsNf { def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): this.type = this override def toString: Str = "⊥" } case class Conjunct(lnf: LhsNf, vars: SortedSet[TypeVariable], rnf: RhsNf, nvars: SortedSet[TypeVariable]) { final def compareEquiv(that: Conjunct): Int = // trace(s"compareEquiv($this, $that)")(compareEquivImpl(that))(r => s"= $r") compareEquivImpl(that) final def compareEquivImpl(that: Conjunct): Int = { var cmp = lnf.compareEquiv(that.lnf) if (cmp =/= 0) return cmp cmp = rnf.compareEquiv(that.rnf) if (cmp =/= 0) return cmp cmp = -vars.sizeCompare(that.vars) if (cmp =/= 0) return cmp cmp = -nvars.sizeCompare(that.nvars) cmp } def toType(sort: Bool = false): SimpleType = toTypeWith(_.toType(sort), _.toType(sort), sort) def toTypeWith(f: LhsNf => SimpleType, g: RhsNf => SimpleType, sort: Bool = false): SimpleType = ((if (sort) vars.toArray.sorted.iterator else vars.iterator) ++ Iterator(g(rnf).neg()) ++ (if (sort) nvars.toArray.sorted.iterator else nvars).map(_.neg())).foldLeft(f(lnf))(_ & _) lazy val level: Int = levelBelow(MaxLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = (vars.iterator ++ nvars).map(_.levelBelow(ub)).++(Iterator(lnf.levelBelow(ub), rnf.levelBelow(ub))).max def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): Conjunct = { val (vars2, tags2) = vars.toBuffer[TV].partitionMap( _.freshenAbove(lim, rigidify) match { case tv: TV => L(tv); case tt: AbstractTag => R(tt); case _ => die }) val (nvars2, ntags2) = nvars.toBuffer[TV].partitionMap( _.freshenAbove(lim, rigidify) match { case tv: TV => L(tv); case tt: AbstractTag => R(tt); case _ => die }) Conjunct( tags2.foldLeft(lnf.freshenAbove(lim, rigidify))(_ & _), vars2.toSortedSet, ntags2.foldLeft(rnf.freshenAbove(lim, rigidify))(_ | _), nvars2.toSortedSet) } def - (fact: Factorizable): Conjunct = fact match { case tv: TV => Conjunct(lnf, vars - tv, rnf, nvars) case NegVar(tv) => Conjunct(lnf, vars, rnf, nvars - tv) case tt: AbstractTag => lnf match { case LhsRefined(b, tts, rft, trs) => if (tts(tt)) copy(lnf = LhsRefined(b, tts - tt, rft, trs)) else this case LhsTop => this } case NegAbsTag(tt) => rnf match { case RhsBases(tts, r, trs) => copy(rnf = RhsBases(tts.filterNot(_ === tt), r, trs)) case RhsBot | _: RhsField => this } } def <:< (that: Conjunct)(implicit ctx: Ctx): Bool = // trace(s"?? $this <:< $that") { that.vars.forall(vars) && lnf <:< that.lnf && that.rnf <:< rnf && that.nvars.forall(nvars) // }(r => s"!! $r") def & (that: Conjunct, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[Conjunct] = // trace(s"?? $this & $that ${lnf & that.lnf} ${rnf | that.rnf}") { if (lnf <:< that.rnf) N else S(Conjunct.mk(lnf & (that.lnf, pol) getOrElse (return N), vars | that.vars , rnf | (that.rnf, pol) getOrElse (return N) , nvars | that.nvars, pol)) // }(r => s"!! $r") def neg: Disjunct = Disjunct(rnf, nvars, lnf, vars) /** `tryMergeUnion` tries to compute the union of two conjuncts as a conjunct, * failing if this merging cannot be done without losing information. * This ends up simplifying away things like: * {x: int} | {y: int} ~> anything * (A -> B) | {x: C} ~> anything */ def tryMergeUnion(that: Conjunct)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[Conjunct] = (this, that) match { case _ if this <:< that => S(that) case _ if that <:< this => S(this) case (Conjunct(LhsTop, vs1, r1, nvs1), Conjunct(LhsTop, vs2, r2, nvs2)) if vs1 === vs2 && nvs1 === nvs2 => S(Conjunct(LhsTop, vs1, r1 tryMergeInter r2 getOrElse (return N), nvs1)) // * Conceptually, `tryMergeInter` could return None either because the ThsNfs cannot be merged // * or because merging them would return bottom... but conjuncts cannot represent bottom. case (Conjunct(LhsRefined(bse1, ts1, rcd1, trs1), vs1, r1, nvs1) , Conjunct(LhsRefined(bse2, ts2, rcd2, trs2), vs2, r2, nvs2)) if bse1 === bse2 && ts1 === ts2 && vs1 === vs2 && r1 === r2 && nvs1 === nvs2 && trs1.keySet === trs2.keySet => val trs = mergeTypeRefs(true, trs1, trs2) val rcd = RecordType(recordUnion(rcd1.fields, rcd2.fields))(noProv) S(Conjunct(LhsRefined(bse1, ts1, rcd, trs), vs1, r1, nvs1)) case (Conjunct(LhsRefined(bse1, ts1, rcd1, trs1), vs1, r1, nvs1) , Conjunct(LhsRefined(bse2, ts2, rcd2, trs2), vs2, r2, nvs2)) if ts1 === ts2 && vs1 === vs2 && r1 === r2 && nvs1 === nvs2 && trs1 === trs2 // TODO!! => val ts = ts1 val rcdU = RecordType(recordUnion( if (expandTupleFields) rcd1.fields else bse1.map(_.toRecord.fields).fold(rcd1.fields)(recordIntersection(rcd1.fields, _)), if (expandTupleFields) rcd2.fields else bse2.map(_.toRecord.fields).fold(rcd2.fields)(recordIntersection(rcd2.fields, _)), ))(noProv) // Example: // Why is it sound to merge (A -> B) & {R} | (C -> D) & {S} // into ((A & C) -> (B | D)) & ({R} | {S}) ? // Because the former can be distributed to // (A -> B | C -> D) & (A -> B | {S}) & ({R} | C -> D) & ({R} | {S}) // == ((A & C) -> (B | D)) & Top & Top & ({R} | {S}) (bse1, bse2) match { case (S(FunctionType(l1, r1)), S(FunctionType(l2, r2))) => // TODO Q: records ok here?! S(Conjunct( LhsRefined(S(FunctionType(l1 & l2, r1 | r2)(noProv)), ts, rcdU, trs1), vs1, RhsBot, nvs1)) case (S(tup1 @ TupleType(fs1)), S(tup2 @ TupleType(fs2))) => // TODO Q: records ok here?! if (fs1.size =/= fs2.size) S(Conjunct( LhsRefined(S(ArrayType(tup1.inner || tup2.inner)(noProv)), ts, rcdU, trs1), vs1, RhsBot, nvs1)) else S(Conjunct( LhsRefined(S(TupleType(tupleUnion(fs1, fs2))(noProv)), ts, rcdU, trs1), vs1, RhsBot, nvs1)) case (S(tup @ TupleType(fs)), S(ArrayType(ar))) => S(Conjunct(LhsRefined(S(ArrayType(tup.inner || ar)(noProv)), ts, rcdU, trs1), vs1, RhsBot, nvs1)) case (S(ArrayType(ar)), S(tup @ TupleType(fs))) => S(Conjunct( LhsRefined(S(ArrayType(tup.inner || ar)(noProv)), ts, rcdU, trs1), vs1, RhsBot, nvs1)) case (S(ArrayType(ar1)), S(ArrayType(ar2))) => S(Conjunct(LhsRefined(S(ArrayType(ar1 || ar2)(noProv)), ts, rcdU, trs1), vs1, RhsBot, nvs1)) case (N, N) | (S(_: FunctionType), S(_: ArrayBase)) | (S(_: ArrayBase), S(_: FunctionType)) => S(Conjunct(LhsRefined(N, ts, rcdU, trs1), vs1, RhsBot, nvs1)) case _ => N } case _ => N } private lazy val mkString: Str = (Iterator(lnf).filter(_ =/= LhsTop) ++ vars ++ (Iterator(rnf).filter(_ =/= RhsBot) ++ nvars).map("~("+_+")")).mkString("∧") override def toString: Str = mkString } object Conjunct { def of(tvs: SortedSet[TypeVariable]): Conjunct = Conjunct(LhsTop, tvs, RhsBot, ssEmp) def mk(lnf: LhsNf, vars: SortedSet[TypeVariable], rnf: RhsNf, nvars: SortedSet[TypeVariable], pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Conjunct = { Conjunct(lnf, vars, rnf match { case RhsField(name, ty) => RhsField(name, ty) case RhsBases(prims, bf, trs) => RhsBases(prims.filter(lnf & (_, pol) pipe (_.isDefined)), bf.filter { case L(b) => lnf & (b, pol) pipe (_.isDefined) case R(r) => true }, trs) case RhsBot => RhsBot }, nvars) } /** Scala's standard library is weird. I would have normally made Conjunct extend Ordered[Conjunct], * but the contract of Ordered says that `equals` and `hashCode` should be "consistent" with `compare`, * which I understand as two things comparing to 0 HAVING to be equal and to have the same hash code... * But achieving this is very expensive for general type forms. * All we want to do here is to define an ordering between implicit equivalence classes * whose members are not necessarily equal. Which is fine since we only use this to do stable sorts. */ implicit object Ordering extends Ordering[Conjunct] { def compare(x: Conjunct, y: Conjunct): Int = x.compareEquiv(y) } } case class Disjunct(rnf: RhsNf, vars: SortedSet[TypeVariable], lnf: LhsNf, nvars: SortedSet[TypeVariable]) { def neg: Conjunct = Conjunct(lnf, nvars, rnf, vars) def | (that: Disjunct, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): Opt[Disjunct] = S(Disjunct(rnf | (that.rnf, pol) getOrElse (return N), vars | that.vars, lnf & (that.lnf, pol) getOrElse (return N), nvars | that.nvars)) override def toString: Str = (Iterator(rnf).filter(_ =/= RhsBot) ++ vars ++ (Iterator(lnf).filter(_ =/= LhsTop) ++ nvars).map("~"+_)).mkString("∨") } object Disjunct { def of(tvs: SortedSet[TypeVariable]): Disjunct = Disjunct(RhsBot, tvs, LhsTop, ssEmp) } /** `polymLevel` denotes the level at which this type is quantified (MaxLevel if it is not) */ case class DNF(polymLevel: Int, cons: Constrs, cs: Ls[Conjunct]) { assert(polymLevel <= MaxLevel, polymLevel) private def levelBelow(ub: Level): Level = cs.iterator.map(_.levelBelow(ub)(MutSet.empty)).maxOption.getOrElse(MinLevel) lazy val levelBelowPolym = levelBelow(polymLevel) def isBot: Bool = cs.isEmpty def toType(sort: Bool = false): SimpleType = ConstrainedType.mk(cons, if (cs.isEmpty) BotType else { val css = if (sort) cs.sorted else cs PolymorphicType.mk(polymLevel, css.map(_.toType(sort)).foldLeft(BotType: ST)(_ | _)) }) lazy val level: Level = cs.maxByOption(_.level).fold(MinLevel)(_.level) def isPolymorphic: Bool = level > polymLevel lazy val effectivePolymLevel: Level = if (isPolymorphic) polymLevel else level def instantiate(implicit ctx: Ctx, shadows: Shadows): (Constrs, Ls[Conjunct]) = if (isPolymorphic) { implicit val state: MutMap[TV, ST] = MutMap.empty ( cons.map { case (l, r) => ( l.freshenAbove(polymLevel, rigidify = false), r.freshenAbove(polymLevel, rigidify = false) )}, cs.map(_.freshenAbove(polymLevel, rigidify = false)) ) } else (cons, cs) def rigidify(implicit ctx: Ctx, shadows: Shadows): Ls[Conjunct] = if (isPolymorphic) { implicit val state: MutMap[TV, ST] = MutMap.empty cs.map(_.freshenAbove(polymLevel, rigidify = true)) } else cs def & (that: DNF, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): DNF = { val (newLvl, thisCs, thatCs, thisCons, thatCons) = levelWith(that) thatCs.map(DNF(newLvl, thisCons ::: thatCons, thisCs) & (_, pol)).foldLeft(DNF.extr(false))(_ | _) } /** Returns new DNF components for `this` and `that` * with their polymorphism levels harmonized to a single level. */ private def levelWith(that: DNF)(implicit ctx: Ctx) : (Level, Ls[Conjunct], Ls[Conjunct], Constrs, Constrs) = { // println(s"--- $levelBelowPolym ${that.polymLevel} ${that.levelBelow(polymLevel)}") implicit val freshened: MutMap[TV, ST] = MutMap.empty // * Some easy cases to avoid having to adjust levels when we can: if (levelBelowPolym <= that.polymLevel && that.levelBelowPolym <= polymLevel) (polymLevel min that.polymLevel, cs, that.cs, cons, that.cons) else if (levelBelow(that.polymLevel) <= polymLevel && levelBelowPolym <= that.polymLevel) (that.polymLevel, cs, that.cs, cons, that.cons) else if (that.levelBelow(polymLevel) <= that.polymLevel && that.levelBelowPolym <= polymLevel) (polymLevel, cs, that.cs, cons, that.cons) // * The two difficult cases: else if (that.polymLevel > polymLevel) ctx.copy(lvl = that.polymLevel + 1) |> { implicit ctx => assert((polymLevel max that.polymLevel) === that.polymLevel) (polymLevel max that.polymLevel, cs.map(_.freshenAbove(polymLevel, rigidify = false)), that.cs, cons.mapKeys(_.freshenAbove(polymLevel, rigidify = false)) .mapValues(_.freshenAbove(polymLevel, rigidify = false)), that.cons ) } else if (polymLevel > that.polymLevel) ctx.copy(lvl = polymLevel + 1) |> { implicit ctx => assert((polymLevel max that.polymLevel) === polymLevel) (polymLevel max that.polymLevel, cs, that.cs.map(_.freshenAbove(that.polymLevel, rigidify = false)), cons, that.cons.mapKeys(_.freshenAbove(polymLevel, rigidify = false)) .mapValues(_.freshenAbove(polymLevel, rigidify = false)), ) } // * One more easy case: else (polymLevel, cs, that.cs, cons, that.cons) ensuring (that.polymLevel === polymLevel) } def | (that: DNF)(implicit ctx: Ctx, etf: ExpandTupleFields): DNF = { val (newLvl, thisCs, thatCs, thisCons, thatCons) = levelWith(that) // println(s"-- $polymLevel ${that.polymLevel} $newLvl") thatCs.foldLeft(DNF(newLvl, thisCons ::: thatCons, thisCs))(_ | _) // ^ Note: conjuncting the constrained-type constraints here is probably the wrong thing to do... } def & (that: Conjunct, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): DNF = DNF(polymLevel, cons, cs.flatMap(_ & (that, pol))) // TODO may need further simplif afterward def | (that: Conjunct)(implicit ctx: Ctx, etf: ExpandTupleFields): DNF = { def go(cs: Ls[Conjunct], acc: Ls[Conjunct], toMerge: Conjunct): Ls[Conjunct] = // trace(s"go?? $cs $acc M $toMerge") { cs match { case c :: cs => if (c <:< toMerge) acc.reverse ::: toMerge :: cs else if (toMerge <:< c) acc.reverse ::: c :: cs else c.tryMergeUnion(toMerge) match { case Some(value) => acc.reverse ::: value :: cs case None => go(cs, c :: acc, toMerge) } case Nil => (toMerge :: acc).reverse } // }(r => s"go!! $r") DNF(polymLevel, cons, // FIXME?! go(cs, Nil, that)) } override def toString: Str = s"DNF($polymLevel, ${cs.mkString(" | ")})${ if (cons.isEmpty) "" else s"{${cons.mkString(", ")}}" }" } object DNF { def of(polymLvl: Level, cons: Constrs, lnf: LhsNf): DNF = of(polymLvl, cons, Conjunct(lnf, ssEmp, RhsBot, ssEmp) :: Nil) def of(polymLvl: Level, cons: Constrs, cs: Ls[Conjunct]): DNF = { val res = DNF(polymLvl, cons, cs) val epl = res.effectivePolymLevel if (epl < polymLvl) res.copy(polymLevel = epl) else res } def extr(dir: Bool): DNF = if (dir) of(MinLevel, Nil, LhsTop) else DNF(MinLevel, Nil, Nil) def merge(dir: Bool, pol: Bool)(l: DNF, r: DNF)(implicit ctx: Ctx, etf: ExpandTupleFields): DNF = if (dir) l | r else l & (r, pol) def mkDeep(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool) (implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true, expandedTVs: Set[TV] = Set.empty): DNF = { mk(polymLvl, cons, mkDeepST(polymLvl, cons, ty, pol), pol) } def mkDeepST(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool) (implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true): ST = // trace(s"mkDeepST[$pol,$polymLvl](${ty})") { ty match { case ProvType(und) => mkDeepST(polymLvl, cons, und, pol).withProv(ty.prov) case TypeBounds(lb, ub) => mkDeepST(polymLvl, cons, if (pol) ub else lb, pol).withProv(ty.prov) case _ => val dnf = mk(polymLvl, cons, ty, pol) def go(polo: Opt[Bool], st: ST): ST = polo match { case _ if st === ty => ty.mapPol(polo)(go) case S(pol) => mkDeepST(polymLvl, cons, st, pol)(ctx, ptr = true, etf = false) case N => TypeBounds.mk( mkDeepST(polymLvl, cons, st, false)(ctx, ptr = true, etf = false), mkDeepST(polymLvl, cons, st, true)(ctx, ptr = true, etf = false)) } dnf.toType().mapPol(S(pol))(go) } // }(r => s"= $r") def mk(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool)(implicit ctx: Ctx, ptr: PreserveTypeRefs = false, etf: ExpandTupleFields = true, expandedTVs: Set[TV] = Set.empty): DNF = // trace(s"DNF[${printPol(pol)},$ptr,$etf,$polymLvl](${ty})") { (if (pol) ty.pushPosWithout else ty) match { case bt: BaseType => DNF.of(polymLvl, cons, LhsRefined(S(bt), ssEmp, if (expandTupleFields) bt.toRecord else RecordType.empty, smEmp)) case tt: AbstractTag => DNF.of(polymLvl, cons, LhsRefined(N, SortedSet.single(tt), RecordType.empty, smEmp)) case rcd: RecordType => DNF.of(polymLvl, cons, LhsRefined(N, ssEmp, rcd, smEmp)) case ExtrType(pol) => extr(!pol) case ty @ ComposedType(p, l, r) => merge(p, pol)(mk(polymLvl, cons, l, pol), mk(polymLvl, cons, r, pol)) case NegType(und) => DNF.of(polymLvl, cons, CNF.mk(polymLvl, Nil, und, !pol).ds.map(_.neg)) case tv @ AssignedVariable(ty) if !preserveTypeRefs && !expandedTVs.contains(tv) => (expandedTVs + tv) |> { implicit expandedTVs => DNF.mk(polymLvl, cons, ty, pol) } case tv: TypeVariable => DNF.of(polymLvl, cons, Conjunct.of(SortedSet.single(tv)) :: Nil) case ProxyType(underlying) => mk(polymLvl, cons, underlying, pol) case tr @ TypeRef(defn, targs) => // * TODO later: when proper TypeRef-based simplif. is implemented, can remove this special case if (preserveTypeRefs && !primitiveTypes.contains(defn.name) || !tr.canExpand) { of(polymLvl, cons, LhsRefined(tr.mkClsTag, ssEmp, RecordType.empty, SortedMap(defn -> tr))) } else mk(polymLvl, cons, tr.expandOrCrash, pol) case TypeBounds(lb, ub) => mk(polymLvl, cons, if (pol) ub else lb, pol) case PolymorphicType(lvl, bod) => mk(lvl, cons, bod, pol) case ConstrainedType(cs, bod) => mk(polymLvl, cs ::: cons, bod, pol) } // }(r => s"= $r") } case class CNF(ds: Ls[Disjunct]) { def & (that: CNF): CNF = that.ds.foldLeft(this)(_ & _) def | (that: CNF, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): CNF = that.ds.map(this | (_, pol)).foldLeft(CNF.extr(false))(_ & _) def & (that: Disjunct): CNF = // TODO try to merge it with the others if possible CNF(that :: ds) def | (that: Disjunct, pol: Bool)(implicit ctx: Ctx, etf: ExpandTupleFields): CNF = CNF(ds.flatMap(_ | (that, pol))) override def toString: Str = s"CNF(${ds.mkString(" & ")})" } object CNF { def of(rnf: RhsNf): CNF = CNF(Disjunct(rnf, ssEmp, LhsTop, ssEmp) :: Nil) def of(bt: BaseType): CNF = CNF.of(RhsBot | bt getOrElse (return CNF.extr(true))) def of(tvs: SortedSet[TypeVariable]): CNF = CNF(Disjunct.of(tvs) :: Nil) def of(rcd: RecordType): CNF = CNF(rcd.fields.iterator.map(f => Disjunct(RhsField(f._1, f._2), ssEmp, LhsTop, ssEmp)).toList) def extr(pol: Bool): CNF = if (pol) CNF(Nil) else of(RhsBot) def merge(pol: Bool)(l: CNF, r: CNF)(implicit ctx: Ctx, etf: ExpandTupleFields): CNF = if (pol) l | (r, pol) else l & r def mk(polymLvl: Level, cons: Constrs, ty: SimpleType, pol: Bool)(implicit ctx: Ctx, ptr: PreserveTypeRefs, etf: ExpandTupleFields, expandedTVs: Set[TV] = Set.empty): CNF = // trace(s"?CNF $ty") { ty match { case bt: BaseType => of(bt) case tt: AbstractTag => of(RhsBases(tt :: Nil, N, smEmp)) case rt @ RecordType(fs) => of(rt) case ExtrType(pol) => extr(!pol) case ty @ ComposedType(p, l, r) => merge(p)(mk(polymLvl, cons, l, pol), mk(polymLvl, cons, r, pol)) case NegType(und) => CNF(DNF.mk(polymLvl, cons, und, !pol).cs.map(_.neg)) case tv @ AssignedVariable(ty) if !preserveTypeRefs && !expandedTVs.contains(tv) => (expandedTVs + tv) |> { implicit expandedTVs => CNF.mk(polymLvl, cons, ty, pol) } case tv: TypeVariable => of(SortedSet.single(tv)) case ProxyType(underlying) => mk(polymLvl, cons, underlying, pol) case tr @ TypeRef(defn, targs) => if (preserveTypeRefs && !primitiveTypes.contains(defn.name) || !tr.canExpand) { CNF(Disjunct(RhsBases(Nil, N, SortedMap.single(defn -> tr)), ssEmp, LhsTop, ssEmp) :: Nil) } else mk(polymLvl, cons, tr.expandOrCrash, pol) case TypeBounds(lb, ub) => mk(polymLvl, cons, if (pol) ub else lb, pol) case PolymorphicType(lvl, bod) => mk(lvl, cons, bod, pol) case ConstrainedType(cs, bod) => mk(lvl, cs ::: cons, bod, pol) } // }(r => s"!CNF $r") } } ================================================ FILE: shared/src/main/scala/mlscript/NuTypeDefs.scala ================================================ package mlscript import scala.collection.mutable import scala.collection.mutable.{Map => MutMap, Set => MutSet} import scala.collection.immutable.{SortedSet, SortedMap} import scala.util.chaining._ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ class NuTypeDefs extends ConstraintSolver { self: Typer => import TypeProvenance.{apply => tp} type Params = Ls[Var -> FieldType] type TyParams = Ls[(TN, TV, Opt[VarianceInfo])] sealed abstract class NuDeclInfo case class FunInfo() extends NuDeclInfo case class TypeDefInfo() extends NuDeclInfo sealed trait NuMember { def name: Str def kind: DeclKind def toLoc: Opt[Loc] def level: Level def isImplemented: Bool def isPublic: Bool def isPrivate: Bool = !isPublic // * We currently don't support `protected` def isValueParam: Bool = this match { case p: NuParam => !p.isType case _ => false } protected def withLevel[R](k: Ctx => R)(implicit ctx: Ctx): R = k(ctx.copy(lvl = ctx.lvl + 1)) def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]) : NuMember def map(f: ST => ST)(implicit ctx: Ctx): NuMember = mapPol(N, false)((_, ty) => f(ty)) // TODO rm – just use `mapPolMap` def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): NuMember def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): NuMember def showBounds: Str = TypedTypingUnit(this :: Nil, N).showBounds } case class NuParam(nme: NameRef, ty: FieldType, isPublic: Bool)(val level: Level) extends NuMember with TypedNuTermDef { def name: Str = nme.name def isType: Bool = nme.isInstanceOf[TypeName] def kind: DeclKind = if (isType) Als // FIXME? else Val def toLoc: Opt[Loc] = nme.toLoc def isImplemented: Bool = true def isVirtual: Bool = false // TODO allow annotating parameters with `virtual` def typeSignature: ST = ty.ub def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]) : NuParam = NuParam(nme, ty.freshenAbove(lim, rigidify), isPublic)(ctx.lvl) def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): NuParam = NuParam(nme, ty.update(t => f(pol.map(!_), t), t => f(pol, t)), isPublic)(level) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): NuParam = NuParam(nme, ty.update(t => f(pol.contravar, t), t => f(pol, t)), isPublic)(level) } sealed trait TypedNuDecl extends NuMember { def name: Str def level: Level def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuDecl def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuDecl } // TODO rm? /** Those declarations that introduce term names in scope. */ sealed trait TypedNuTermDef extends TypedNuDecl with AnyTypeDef { def typeSignature: ST } sealed abstract class TypedNuTypeDef(kind: TypeDefKind) extends TypedNuDecl { def nme: TypeName def decl: NuTypeDef def toLoc: Opt[Loc] = decl.toLoc def tparams: TyParams def members: Map[Str, NuMember] val allFields: Set[Var] = members.valuesIterator.map(_.name |> Var).toSet val td: NuTypeDef val prov: TP = TypeProvenance(td.toLoc, td.describe, isType = true) val level: Level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = ??? } case class TypedNuAls(level: Level, td: NuTypeDef, tparams: TyParams, body: ST) extends TypedNuTypeDef(Als) { def decl: NuTypeDef = td def kind: DeclKind = td.kind def name: Str = nme.name def nme: mlscript.TypeName = td.nme def members: Map[Str, NuMember] = Map.empty def isImplemented: Bool = td.sig.isDefined def isPublic = true // TODO def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV,ST]) : TypedNuAls = { val outer = ctx; withLevel { implicit ctx => TypedNuAls(outer.lvl, td, tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), body.freshenAbove(lim, rigidify)) }} def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuDecl = TypedNuAls( level, td, tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), f(pol, body) ) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuDecl = TypedNuAls( level, td, tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), f(pol, body) ) } sealed trait PolyNuDecl extends TypedNuDecl { def tparams: TyParams } case class TypedNuTrt( level: Level, td: NuTypeDef, tparams: TyParams, members: Map[Str, NuMember], thisTy: ST, sign: ST, inheritedTags: Set[TypeName], parentTP: Map[Str, NuMember] ) extends TypedNuTypeDef(Trt) with PolyNuDecl { def decl: NuTypeDef = td def kind: DeclKind = td.kind def nme: TypeName = td.nme def name: Str = nme.name def isImplemented: Bool = true def isPublic = true // TODO // TODO dedup with the one in TypedNuCls lazy val virtualMembers: Map[Str, NuMember] = members ++ tparams.map { case (nme @ TypeName(name), tv, _) => td.nme.name+"#"+name -> NuParam(nme, FieldType(S(tv), tv)(provTODO), isPublic = true)(level) } ++ parentTP def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV,ST]) : TypedNuTrt = { val outer = ctx; withLevel { implicit ctx => TypedNuTrt(outer.lvl, td, tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), members.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, thisTy.freshenAbove(lim, rigidify), sign.freshenAbove(lim, rigidify), inheritedTags, parentTP.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap ) }} def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuTrt = TypedNuTrt(level, td, tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), members.mapValuesIter(_.mapPol(pol, smart)(f)).toMap, f(pol.map(!_), thisTy), f(pol, sign), inheritedTags, parentTP.mapValuesIter(_.mapPol(pol, smart)(f)).toMap ) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuTrt = TypedNuTrt(level, td, tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), members.mapValuesIter(_.mapPolMap(pol)(f)).toMap, f(pol.contravar, thisTy), f(pol, sign), inheritedTags, parentTP.mapValuesIter(_.mapPolMap(pol)(f)).toMap ) } case class TypedNuCls( level: Level, td: NuTypeDef, tparams: TyParams, params: Opt[Ls[Var -> FieldType]], auxCtorParams: Opt[Ls[Var -> ST]], members: Map[Str, NuMember], thisTy: ST, sign: ST, inheritedTags: Set[TypeName], parentTP: Map[Str, NuMember] ) extends TypedNuTypeDef(Cls) with PolyNuDecl { def decl: NuTypeDef = td def kind: DeclKind = td.kind def nme: TypeName = td.nme def name: Str = nme.name def isImplemented: Bool = true def isPublic = true // TODO /** The type of a palin term reference to this type definition. */ def typeSignature(usesNew: Bool, loco: Opt[Loc])(implicit raise: Raise): ST = typeSignatureOf(usesNew, loco, td, level, tparams, params, auxCtorParams, sign, inheritedTags) /** Includes class-name-coded type parameter fields. */ lazy val virtualMembers: Map[Str, NuMember] = members ++ tparams.map { case (nme @ TypeName(name), tv, _) => td.nme.name+"#"+name -> NuParam(nme, FieldType(S(tv), tv)(provTODO), isPublic = true)(level) } ++ parentTP // TODO // def checkVariances // lazy val explicitVariances: VarianceStore = // MutMap.from(tparams.iterator.map(tp => tp._2 -> tp._3.getOrElse(VarianceInfo.in))) // TODO should we really recompute them on freshened instances, or can we avoid that? private var _variances: Opt[VarianceStore] = N def variances(implicit ctx: Ctx): VarianceStore = { _variances match { case S(res) => res case N => trace(s"Computing variances of ${this.name}") { val store = VarianceStore.empty val traversed = MutSet.empty[Pol -> TV] object Trav extends Traverser2.InvariantFields { override def apply(pol: PolMap)(ty: ST): Unit = trace(s"Trav($pol)($ty)") { ty match { case tv: TypeVariable => if (traversed.add(pol(tv) -> tv)) { store(tv) = store.getOrElse(tv, VarianceInfo.bi) && (pol(tv) match { case S(true) => VarianceInfo.co case S(false) => VarianceInfo.contra case N => VarianceInfo.in }) super.apply(pol)(ty) } case ty @ RecordType(fs) => // Ignore type param members such as `C#A` in `{C#A: mut A30'..A30'}` super.apply(pol)(RecordType(fs.filterNot(_._1.name.contains('#')))(ty.prov)) case _ => super.apply(pol)(ty) } }() } members.foreachEntry { case (_, m: NuParam) if m.isType => case (_, m) => Trav.applyMem(PolMap.pos)(m) } // TODO check consistency with explicitVariances val res = store ++ tparams.iterator.collect { case (_, tv, S(vi)) => tv -> vi } _variances = S(res) res }(r => s"= $r") } } def varianceOf(tv: TV)(implicit ctx: Ctx): VarianceInfo = variances.getOrElse(tv, VarianceInfo.in) def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV,ST]) : TypedNuCls = { val outer = ctx; withLevel { implicit ctx => TypedNuCls(outer.lvl, td, tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), params.map(_.mapValues(_.freshenAbove(lim, rigidify))), auxCtorParams.map(_.mapValues(_.freshenAbove(lim, rigidify))), members.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, thisTy.freshenAbove(lim, rigidify), sign.freshenAbove(lim, rigidify), inheritedTags, parentTP.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, ) }} def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuCls = TypedNuCls(level, td, tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), params.map(_.mapValues(_.update(t => f(pol.map(!_), t), t => f(pol, t)))), auxCtorParams.map(_.mapValues(t => f(pol.map(!_), t))), members.mapValuesIter(_.mapPol(pol, smart)(f)).toMap, f(pol.map(!_), thisTy), f(pol, sign), inheritedTags, parentTP.mapValuesIter(_.mapPol(pol, smart)(f)).toMap, ) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuCls = TypedNuCls(level, td, tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), params.map(_.mapValues(_.update(t => f(pol.contravar, t), t => f(pol, t)))), auxCtorParams.map(_.mapValues(t => f(pol.contravar, t))), members.mapValuesIter(_.mapPolMap(pol)(f)).toMap, f(pol.contravar, thisTy), f(pol, sign), inheritedTags, parentTP.mapValuesIter(_.mapPolMap(pol)(f)).toMap, ) override def toString: Str = s"TypedNuCls($level, ${td.nme},\n\t$tparams,\n\t$params,\n\tthis: $thisTy, ${ members.lnIndent()},\n\t: $sign, $inheritedTags, $parentTP)" } case class TypedNuMxn( level: Level, td: NuTypeDef, thisTy: ST, superTy: ST, tparams: TyParams, params: Ls[Var -> FieldType], members: Map[Str, NuMember], ) extends TypedNuTypeDef(Mxn) with PolyNuDecl { def decl: NuTypeDef = td def kind: DeclKind = td.kind def nme: TypeName = td.nme def name: Str = nme.name def isImplemented: Bool = true def isPublic = true // TODO lazy val virtualMembers: Map[Str, NuMember] = members ++ tparams.map { case (nme @ TypeName(name), tv, _) => td.nme.name+"#"+name -> NuParam(nme, FieldType(S(tv), tv)(provTODO), isPublic = false)(level) } def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV,ST]) : TypedNuMxn = { val outer = ctx; withLevel { implicit ctx => TypedNuMxn(outer.lvl, td, thisTy.freshenAbove(lim, rigidify), superTy.freshenAbove(lim, rigidify), tparams.map(tp => (tp._1, tp._2.freshenAbove(lim, rigidify).assertTV, tp._3)), params.mapValues(_.freshenAbove(lim, rigidify)), members.mapValuesIter(_.freshenAbove(lim, rigidify)).toMap, ) }} def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuMxn = TypedNuMxn(level, td, f(pol.map(!_), thisTy), f(pol.map(!_), superTy), tparams.map(tp => (tp._1, f(N, tp._2).assertTV, tp._3)), params.mapValues(_.update(t => f(pol.map(!_), t), t => f(pol, t))), members.mapValuesIter(_.mapPol(pol, smart)(f)).toMap) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuMxn = TypedNuMxn(level, td, f(pol.contravar, thisTy), f(pol.contravar, superTy), tparams.map(tp => (tp._1, f(pol.invar, tp._2).assertTV, tp._3)), params.mapValues(_.update(t => f(pol.contravar, t), t => f(pol, t))), members.mapValuesIter(_.mapPolMap(pol)(f)).toMap) override def toString: Str = s"TypedNuMxn($level, ${td.nme},\n\tthis: $thisTy,\n\tsuper: $superTy,\n\ttparams: $tparams,\n\tparams: $params,\n\tmembers: ${members.lnIndent()}\n)" } /** Used when there was an error while tying a definition. */ case class TypedNuDummy(d: NuDecl) extends TypedNuDecl with TypedNuTermDef { def level = MinLevel def kind: DeclKind = Val def toLoc: Opt[Loc] = N def name: Str = d.name def isImplemented: Bool = true def isPublic = true // TODO def typeSignature: ST = errType def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]) = this def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuTermDef = this def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuTermDef = this } // Field `thisRef` is defined when the member refers to `this` selecting a field on it // e.g., val x = this // Field `refs` contains all `Var`s accessed by the member with their possible `this` qualifiers (`None` if it is an unqualified access) case class RefMap(thisRef: Opt[Var], refs: Set[(Var, Opt[Var])]) object RefMap { lazy val nothing: RefMap = RefMap(N, Set.empty) } /** Note: the type `bodyType` is stored *without* its polymorphic wrapper! (unlike `typeSignature`) */ case class TypedNuFun(level: Level, fd: NuFunDef, bodyType: ST)(val isImplemented: Bool) extends TypedNuDecl with TypedNuTermDef { def kind: DeclKind = Val def name: Str = fd.nme.name def symbolicName: Opt[Str] = fd.symbolicNme.map(_.name) def toLoc: Opt[Loc] = fd.toLoc lazy val prov = TypeProvenance(toLoc, "member") def isPublic: Bool = !fd.isLetOrLetRec lazy val typeSignature: ST = if (fd.isMut) bodyType else PolymorphicType.mk(level, bodyType) def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]) : TypedNuFun = { val outer = ctx; withLevel { implicit ctx => this match { case TypedNuFun(level, fd, ty) => TypedNuFun(outer.lvl, fd, ty.freshenAbove(lim, rigidify))(isImplemented) // .tap(res => println(s"Freshen[$level,${ctx.lvl}] $this ~> $res")) }}} def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuFun = TypedNuFun(level, fd, f(pol, bodyType))(isImplemented) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedNuFun = TypedNuFun(level, fd, f(pol, bodyType))(isImplemented) def getFunRefs: RefMap = fd.rhs match { case L(term) => getRefs(term) case _ => RefMap.nothing } } case class TypedTypingUnit(implementedMembers: Ls[NuMember], result: Opt[ST]) extends OtherTypeLike { def map(f: ST => ST)(implicit ctx: Ctx): TypedTypingUnit = TypedTypingUnit(implementedMembers.map(_.map(f)), result.map(f)) def mapPol(pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): TypedTypingUnit = TypedTypingUnit(implementedMembers.map(_.mapPol(pol, smart)(f)), result.map(f(pol, _))) def mapPolMap(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType) (implicit ctx: Ctx): TypedTypingUnit = TypedTypingUnit(implementedMembers.map(_.mapPolMap(pol)(f)), result.map(f(pol, _))) def freshenAbove(lim: Int, rigidify: Bool) (implicit ctx: Ctx, freshened: MutMap[TV, ST]) : TypedTypingUnit = TypedTypingUnit(implementedMembers.map(_.freshenAbove(lim, rigidify)), result.map(_.freshenAbove(lim, rigidify))) override def toString: Str = s"TypedTypingUnit(${(implementedMembers :+ result).lnIndent()})" } def typeSignatureOf(usesNew: Bool, loco: Opt[Loc], td: NuTypeDef, level: Level, tparams: TyParams, params: Opt[Params], acParams: Opt[Ls[Var -> ST]], selfTy: ST, ihtags: Set[TypeName]) (implicit raise: Raise) : ST = if ((td.kind is Mod) && params.isEmpty) if (tparams.isEmpty) TypeRef(td.nme, Nil)(provTODO) else PolymorphicType.mk(level, TypeRef(td.nme, tparams.map(_._2))(provTODO)) else if ((td.kind is Cls) || (td.kind is Mod)) { if (td.kind is Mod) err(msg"Parameterized modules are not yet supported", loco) println(s"params: $params $acParams") if (!usesNew) if (params.isEmpty) if (acParams.isEmpty) return err(msg"Class ${td.nme.name} cannot be instantiated as it exposes no constructor", loco) else err(msg"Construction of unparameterized class ${td.nme.name} should use the `new` keyword", loco) else if (acParams.isDefined) err(msg"Construction of class with auxiliary constructor should use the `new` keyword", loco) val ps: Params = acParams match { case S(acParams) => acParams.mapValues(_.toUpper(noProv)) case N => params.getOrElse(Nil) } PolymorphicType.mk(level, FunctionType( TupleType(ps.mapKeys(some))(provTODO), TypeRef(td.nme, tparams.map(_._2))(provTODO) )(provTODO) ) } else errType // FIXME def getRefs(body: Statement): RefMap = { val refs = mutable.HashSet[(Var, Opt[Var])]() def visit(s: Located): Opt[Var] = s match { case Sel(ths @ Var("this"), v) => refs.add((v, S(ths))) N case v @ Var(name) => if (name === "this") S(v) else { refs.add((v, N)) N } case _: Type => N case _: Term| _: Statement | _: NuDecl | _: IfBody | _: CaseBranches | _: TypingUnit => s.children.foldLeft[Opt[Var]](N)((r, c) => r.orElse(visit(c))) } RefMap(visit(body), refs.toSet) } /** Type checks a typing unit, which is a sequence of possibly-mutually-recursive type and function definitions * interleaved with plain statements. */ def typeTypingUnit(tu: TypingUnit, outer: Opt[Outer]) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): TypedTypingUnit = trace(s"${ctx.lvl}. Typing $tu") { val topLevel: Bool = outer.isEmpty // println(s"vars ${vars}") tu.entities.foreach { case fd: NuFunDef if fd.isLetRec.isEmpty && outer.exists(_.kind is Block) => err(msg"Cannot use `val` or `fun` in local block; use `let` instead.", fd.toLoc) case _ => } val named = mutable.Map.empty[Str, LazyTypeInfo] // * Not sure we should support declaring signature with the `ident: type` syntax // val (signatures, otherEntities) = tu.entities.partitionMap { // case Asc(v: Var, ty) => L(v -> ty) // case s => R(s) // } val (decls, statements) = tu.entities.partitionMap { case decl: NuDecl => L(decl) case s => R(s) } val funSigs = MutMap.empty[Str, NuFunDef] val implems = decls.filter { case fd @ NuFunDef(_, nme, snme, tparams, R(rhs)) => if (fd.isLetOrLetRec) err(s"`let` bindings must have a right-hand side", fd.toLoc) funSigs.updateWith(nme.name) { case S(s) => err(s"A type signature for '${nme.name}' was already given", fd.toLoc) S(s) case N => S(fd) } false // * Explicit signatures will already be typed in DelayedTypeInfo's typedSignatures case _ => true } val sigInfos = if (topLevel) funSigs.map { case (nme, decl) => val lti = new DelayedTypeInfo(decl, implicitly) // TODO check against duplicated symbolic names in same scope... decl.symbolicNme.foreach(snme => ctx += snme.name -> lti) decl.name -> lti } else Nil val infos = implems.map { case _decl: NuDecl => val decl = _decl match { case fd: NuFunDef => assert(fd.signature.isEmpty) funSigs.get(fd.nme.name) match { case S(sig) => fd.copy()(fd.declareLoc, fd.virtualLoc, fd.mutLoc, S(sig), outer, fd.genField, fd.annotations) case _ => fd.copy()(fd.declareLoc, fd.virtualLoc, fd.mutLoc, fd.signature, outer, fd.genField, fd.annotations) } case td: NuTypeDef => if (td.nme.name in reservedTypeNames) err(msg"Type name '${td.nme.name}' is reserved", td.toLoc) td } val lti = new DelayedTypeInfo(decl, implicitly) decl match { case td: NuTypeDef => ctx.tyDefs2 += td.nme.name -> lti case fd: NuFunDef => // TODO check against duplicated symbolic names in same scope... fd.symbolicNme.foreach(snme => ctx += snme.name -> lti) } named.updateWith(decl.name) { case sv @ S(v) => decl match { case NuFunDef(S(_), _, _, _, _) => () case _ => err(msg"Redefinition of '${decl.name}'", decl.toLoc) } S(lti) case N => S(lti) } decl.name -> lti } ctx ++= infos ctx ++= sigInfos val tpdFunSigs = sigInfos.mapValues(_.complete() match { case res: TypedNuFun if res.fd.isDecl => TopType case res: TypedNuFun => res.typeSignature case _ => die }).toMap // * Complete typing of block definitions and add results to context val completedInfos = (infos ++ sigInfos).mapValues(_.complete() match { case res: TypedNuFun => tpdFunSigs.get(res.name) match { case S(expected) => implicit val prov: TP = TypeProvenance(res.fd.toLoc, res.fd.describe) subsume(res.typeSignature, expected) case _ => // * Generalize functions as they are typed. // * Note: eventually we'll want to first reorder their typing topologically so as to maximize polymorphism. ctx += res.name -> VarSymbol(res.typeSignature, res.fd.nme) res.symbolicName.foreach(ctx += _ -> VarSymbol(res.typeSignature, res.fd.nme)) } CompletedTypeInfo(res) case res => CompletedTypeInfo(res) }) ctx ++= completedInfos val returnsLastExpr = outer.map(_.kind) match { case N | S(Block | Val) => true case S(_: TypeDefKind) => false } // * Type the block statements def go(stmts: Ls[Statement]): Opt[ST] = stmts match { case s :: stmts => val res_ty = s match { case decl: NuDecl => N case t: Term => implicit val genLambdas: GenLambdas = true val ty = typeTerm(t) if (!topLevel && !(stmts.isEmpty && returnsLastExpr)) { t match { // * We do not include `_: Var` because references to `fun`s and lazily-initialized // * definitions may have side effects. case _: Lit | _: Lam => warn("Pure expression does nothing in statement position.", t.toLoc) case _ => constrain(mkProxy(ty, TypeProvenance(t.toCoveringLoc, "expression in statement position")), UnitType)( raise = err => raise(WarningReport( // Demote constraint errors from this to warnings msg"Expression in statement position should have type `()`." -> N :: msg"Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer." -> N :: err.allMsgs, newDefs)), prov = TypeProvenance(t.toLoc, t.describe), ctx) } } S(ty) case s: DesugaredStatement => err(msg"Illegal position for this ${s.describe} statement.", s.toLoc)(raise) N case _ => die } stmts match { case Nil => res_ty case stmts => // TODO check discarded non-unit values go(stmts) } case Nil => N } val res_ty = trace("Typing unit statements") { go(statements) } (r => s": $r") TypedTypingUnit(completedInfos.map(_._2.member), res_ty) }() trait DelayedTypeInfoImpl { this: DelayedTypeInfo => private def outerCtx = ctx var isComputing: Bool = false // Replace by a Ctx entry? var result: Opt[TypedNuDecl] = N def isComputed: Bool = result.isDefined val level: Level = ctx.lvl val kind: DeclKind = decl.kind val name: Str = decl.name private implicit val prov: TP = TypeProvenance(decl.toLoc, decl.describe) // * TODO should we use this? It could potentially improve error messages implicit val newDefsInfo: Map[Str, (TypeDefKind, Int)] = Map.empty // TODO? println(s"${ctx.lvl}. Created lazy type info for $decl") type ParentSpec = (Term, Var, LazyTypeInfo, Ls[Type], Ls[Opt[Var] -> Fld]) lazy val parentSpecs: Ls[ParentSpec] = decl match { case td: NuTypeDef => td.parents.flatMap { case v @ Var(nme) => S(v, v, Nil, Nil) case p @ App(v @ Var(nme), Tup(args)) => S(p, v, Nil, args) case TyApp(v @ Var(nme), targs) => S(v, v, targs, Nil) case p @ App(TyApp(v @ Var(nme), targs), Tup(args)) => S(p, v, targs, args) case p => err(msg"Unsupported parent specification", p.toLoc) // TODO N }.flatMap { case (p, v @ Var(parNme), parTargs, parArgs) => ctx.get(parNme) match { case S(lti: LazyTypeInfo) => S((p, v, lti, parTargs, parArgs)) case S(_) => err(msg"Cannot inherit from this", p.toLoc) N case N => err(msg"Could not find definition `${parNme}`", p.toLoc) N } } case _ => Nil } /** First list of members is for the typed arguments; * the map of members is for the inherited virtual type argument members. */ type TypedParentSpec = (TypedNuTypeDef, Ls[NuParam], Map[Str, NuMember], Opt[Loc]) private var typingParents = false lazy val typedParents: Ls[TypedParentSpec] = ctx.nest.nextLevel { implicit ctx => ctx ++= paramSymbols if (typingParents === true) { err(msg"Unhandled cyclic parent specification", decl.toLoc) Nil } else try {typingParents = true; parentSpecs}.flatMap { case (p, v @ Var(parNme), lti, parTargs, parArgs) => trace(s"${lvl}. Typing parent spec $p") { val info = lti.complete() info match { case rawMxn: TypedNuMxn => // println(s"Raw $rawMxn") val (fr, ptp) = refreshHelper(rawMxn, v, if (parTargs.isEmpty) N else S(parTargs)) // type args inferred val mxn = { implicit val frenshened: MutMap[TV,ST] = fr implicit val ctx: Ctx = outerCtx rawMxn.freshenAbove(info.level, rigidify = false) } // println(s"Fresh $mxn") val argMembs = { if (parArgs.sizeCompare(mxn.params) =/= 0) err(msg"mixin $parNme expects ${ mxn.params.size.toString} parameter(s); got ${parArgs.size.toString}", Loc(v :: parArgs.unzip._2)) val paramMems = mxn.params.lazyZip(parArgs).flatMap { case (nme -> p, _ -> Fld(FldFlags(mut, spec, get), a)) => // TODO factor this with code for classes: assert(!mut && !spec && !get, "TODO") // TODO check mut, spec, get implicit val genLambdas: GenLambdas = true val a_ty = typeTerm(a) p.lb.foreach(constrain(_, a_ty)) constrain(a_ty, p.ub) val isPublic = mxn.members(nme.name).isPublic val fty = if (p.lb.isDefined) // * We don't refine the field type when it's mutable as that could lead to muable updates being rejected FieldType(p.lb, p.ub)(provTODO) else FieldType(p.lb, a_ty)(provTODO) Option.when(isPublic)(NuParam(nme, fty, isPublic = isPublic)(lvl)) } paramMems //++ mxn.members.valuesIterator } println(s"Mixin arg members $argMembs") S((mxn, argMembs, Map.empty[Str, NuMember], // TODO add ptp here once we support explicit type args p.toLoc )) case rawTrt: TypedNuTrt => if (parArgs.nonEmpty) err(msg"trait arguments are not yet supported", p.toLoc) val (fr, ptp) = refreshHelper(rawTrt, v, if (parTargs.isEmpty) N else S(parTargs)) // infer ty args if not provided val trt = { implicit val frenshened: MutMap[TV,ST] = fr implicit val ctx: Ctx = outerCtx rawTrt.freshenAbove(info.level, rigidify = false) } val paramMems = Nil // * Maybe support trait params? (not sure) S((trt, paramMems, ptp ++ trt.parentTP, p.toLoc)) case rawCls: TypedNuCls => // println(s"Raw $rawCls where ${rawCls.showBounds}") val (fr, ptp) = refreshHelper(rawCls, v, if (parTargs.isEmpty) N else S(parTargs)) // infer ty args if not provided val cls = { implicit val frenshened: MutMap[TV,ST] = fr implicit val ctx: Ctx = outerCtx rawCls.freshenAbove(info.level, rigidify = false) } // println(s"Fresh[${ctx.lvl}] $cls where ${cls.showBounds}") def checkArgsNum(effectiveParamSize: Int) = if (parArgs.sizeCompare(effectiveParamSize) =/= 0) err(msg"class $parNme expects ${ effectiveParamSize.toString} parameter(s); got ${parArgs.size.toString }", Loc(v :: parArgs.unzip._2)) val argMembs = { implicit val genLambdas: GenLambdas = true cls.auxCtorParams match { case S(ps) => checkArgsNum(ps.size) ps.lazyZip(parArgs).map { case (nme -> p_ty, _ -> Fld(FldFlags(mut, spec, get), a)) => assert(!mut && !spec && !get, "TODO") // TODO check mut, spec, get val a_ty = typeTerm(a) constrain(a_ty, p_ty) } Nil case N => cls.params match { case S(ps) => checkArgsNum(ps.size) ps.lazyZip(parArgs).flatMap { case (nme -> p, _ -> Fld(FldFlags(mut, spec, get), a)) => assert(!mut && !spec && !get, "TODO") // TODO check mut, spec, get val a_ty = typeTerm(a) p.lb.foreach(constrain(_, a_ty)) constrain(a_ty, p.ub) val isPublic = cls.members(nme.name).isPublic val fty = if (p.lb.isDefined) // * We don't refine the field type when it's mutable as that could lead to muable updates being rejected FieldType(p.lb, p.ub)(provTODO) else FieldType(p.lb, a_ty)(provTODO) Option.when(isPublic)(NuParam(nme, fty, isPublic = isPublic)(lvl)) } case N => checkArgsNum(0) Nil } } } println(s"Class arg members $argMembs") S((cls, argMembs, ptp ++ cls.parentTP, p.toLoc)) case als: TypedNuAls => // TODO dealias first? err(msg"Cannot inherit from a type alias", p.toLoc) N case als: NuParam => // TODO first-class mixins/classes... err(msg"Cannot inherit from a parameter", p.toLoc) N // case als: NuTypeParam => // err(msg"Cannot inherit from a type parameter", p.toLoc) // Nil case cls: TypedNuFun => err(msg"Cannot inherit from a function", p.toLoc) N case cls: TypedNuDummy => N } }() } finally { typingParents = false } } def lookupTags(parents: Ls[ParentSpec], tags: Set[TypeName]): Set[TypeName] = { parents match { case Nil => tags case (p, Var(nm), lti, _, _) :: ps => lti match { case lti: DelayedTypeInfo => lti.kind match { case Trt | Cls | Mod => lookupTags(ps, Set.single(TypeName(nm)) union lti.inheritedTags union tags) case Val | Mxn | Als => lookupTags(ps, tags) } case CompletedTypeInfo(trt: TypedNuTrt) => lookupTags(ps, Set.single(TypeName(nm)) union trt.inheritedTags union tags) case CompletedTypeInfo(cls: TypedNuCls) => lookupTags(ps, Set.single(TypeName(nm)) union cls.inheritedTags union tags) case CompletedTypeInfo(_: NuParam | _: TypedNuFun | _: TypedNuAls | _: TypedNuMxn | _: TypedNuDummy) => lookupTags(ps, tags) } } } private var inheritedTagsStartedComputing = false lazy val inheritedTags: Set[TypeName] = if (inheritedTagsStartedComputing) Set.empty // * Deals with malformed inheritances (cycles) else { inheritedTagsStartedComputing = true lookupTags(parentSpecs, Set.empty) } lazy val tparams: TyParams = ctx.nest.nextLevel { implicit ctx => decl match { case td: NuTypeDef => td.tparams.map(tp => (tp._2, freshVar( TypeProvenance(tp._2.toLoc, "type parameter", S(tp._2.name), isType = true), N, S(tp._2.name)), tp._1)) case fd: NuFunDef => fd.tparams.map { tn => (tn, freshVar( TypeProvenance(tn.toLoc, "method type parameter", originName = S(tn.name), isType = true), N, S(tn.name)), N) } } } lazy val tparamsSkolems: Ls[Str -> SkolemTag] = tparams.map { case (tp, tv, vi) => (tp.name, SkolemTag(tv)(tv.prov)) } lazy val explicitVariances: VarianceStore = MutMap.from(tparams.iterator.map(tp => tp._2 -> tp._3.getOrElse(VarianceInfo.in))) def varianceOf(tv: TV)(implicit ctx: Ctx): VarianceInfo = // TODO make use of inferred vce if result is completed explicitVariances.get(tv).getOrElse(VarianceInfo.in) lazy private implicit val vars: Map[Str, SimpleType] = outerVars ++ tparamsSkolems lazy val typedParams: Opt[Ls[Var -> FieldType]] = ctx.nest.nextLevel { implicit ctx => decl match { case td: NuTypeDef => td.params.map(_.fields.flatMap { case (S(nme), Fld(FldFlags(mut, spec, getter), value)) => assert(!mut && !spec, "TODO") // TODO val tpe = value.toTypeRaise val ty = typeType(tpe) nme -> FieldType(N, ty)(provTODO) :: Nil case (N, Fld(FldFlags(mut, spec, getter), nme: Var)) => assert(!mut && !spec, "TODO") // TODO // nme -> FieldType(N, freshVar(ttp(nme), N, S(nme.name)))(provTODO) nme -> FieldType(N, err(msg"${td.kind.str.capitalize} parameters currently need type annotations", nme.toLoc))(provTODO) :: Nil case (_, fld) => err(msg"Unsupported field specification", fld.toLoc) Nil }) case fd: NuFunDef => N } } lazy val paramSymbols = typedParams.getOrElse(Nil).map(p => p._1.name -> VarSymbol(p._2.ub, p._1)) // TODO also import signatures from base classes and mixins! lazy val (typedSignatures, funImplems) : (Ls[(NuFunDef, ST)], Ls[NuFunDef]) = decl match { case td: NuTypeDef => ctx.nest.nextLevel { implicit ctx => val (signatures, rest) = td.body.entities.partitionMap { case fd @ NuFunDef(_, nme, snme, tparams, R(rhs)) if !fd.isLetOrLetRec => L((fd, rhs)) // TODO also pick up signature off implems with typed params/results case s => R(s) } val implems = rest.collect { case fd @ NuFunDef(N | S(false), nme, snme, tparams, L(rhs)) => fd } ctx ++= paramSymbols signatures.map { case (fd, rhs) => (fd, ctx.poly { implicit ctx: Ctx => vars ++ fd.tparams.map { tn => tn.name -> freshVar(TypeProvenance(tn.toLoc, "method type parameter", originName = S(tn.name), isType = true), N, S(tn.name)) } |> { implicit vars => typeType(rhs).withProv( TypeProvenance(Loc(rhs :: fd.nme :: fd.tparams), s"signature of member `${fd.nme.name}`") ) } }) } -> implems } case _: NuFunDef => Nil -> Nil } lazy val typedSignatureMembers: Ls[Str -> TypedNuFun] = { val implemented = funImplems.iterator.map(_.nme.name).toSet typedSignatures.iterator.map { case (fd, ty) => fd.nme.name -> TypedNuFun(level + 1, fd, ty)(implemented.contains(fd.nme.name)) }.toList } lazy val inheritedFields: Set[Var] = decl match { case td: NuTypeDef => parentSpecs.iterator.flatMap(_._3 match { case dti: DelayedTypeInfo => dti.allFields case CompletedTypeInfo(m: TypedNuTypeDef) => m.allFields case _ => Set.empty}).toSet case _: NuFunDef => Set.empty } lazy val privateParams: Set[Var] = decl match { case td: NuTypeDef => // td.params.dlof(_.fields)(Nil).iterator.collect { // case (S(nme), Fld(flags, _)) if !flags.genGetter => nme // case (N, Fld(flags, nme: Var)) if !flags.genGetter => nme // // case (N, Fld(flags, _)) => die // }.toSet td.params.dlof(_.fields)(Nil).iterator.flatMap { case (S(nme), Fld(flags, _)) => Option.when(!flags.genGetter)(nme) case (N, Fld(flags, nme: Var)) => Option.when(!flags.genGetter)(nme) case (N, Fld(flags, _)) => die }.toSet case _: NuFunDef => Set.empty } lazy val allFields: Set[Var] = decl match { case td: NuTypeDef => (td.params.getOrElse(Tup(Nil)).fields.iterator.flatMap(_._1) ++ td.body.entities.iterator.collect { case fd: NuFunDef if !fd.isLetOrLetRec => fd.nme }).toSet ++ inheritedFields ++ tparams.map { case (nme @ TypeName(name), tv, _) => Var(td.nme.name+"#"+name).withLocOf(nme) } case _: NuFunDef => Set.empty } lazy val typedFields: Map[Var, FieldType] = { (typedParams.getOrElse(Nil).toMap // -- privateFields -- inheritedFields /* parameters can be overridden by inherited fields/methods */ ) ++ typedSignatures.iterator.map(fd_ty => fd_ty._1.nme -> ( if (fd_ty._1.isMut) FieldType(S(fd_ty._2), fd_ty._2)( fd_ty._2.prov) // FIXME prov else fd_ty._2.toUpper(noProv) )) ++ typedParents.flatMap(_._3).flatMap { case (k, p: NuParam) => Var(k) -> p.ty :: Nil case _ => Nil } } private lazy val isGeneralized: Bool = decl match { case fd: NuFunDef => println(s"Type ${fd.nme.name} polymorphically? ${fd.isGeneralized} && (${ctx.lvl} === 0 || ${ fd.signature.nonEmpty} || ${fd.outer.exists(_.kind isnt Mxn)})") // * We only type polymorphically: // * definitions that can be generalized (ie `fun`s or function-valued `let`s and `val`s); and fd.isGeneralized && ( ctx.lvl === 0 // * top-level definitions || fd.signature.nonEmpty // * definitions with a provided type signature || fd.outer.exists(_.kind isnt Mxn) // * definitions NOT occurring in mixins ) // * The reason to not type unannotated mixin methods polymorphically is that // * doing so yields too much extrusion and cycle check failures, // * in the context of inferred precisely-typed open recursion through mixin composition. case _ => die } lazy val mutRecTV: TV = decl match { case fd: NuFunDef => freshVar( TypeProvenance(decl.toLoc, decl.describe, S(decl.name), decl.isInstanceOf[NuTypeDef]), N, S(decl.name) )(if (isGeneralized) level + 1 else level) case _ => lastWords(s"Not supposed to use mutRecTV for ${decl.kind}") } private lazy val thisTV: TV = freshVar(provTODO, N, S(decl.name.decapitalize))(lvl + 1) def refreshHelper(raw: PolyNuDecl, v: Var, parTargs: Opt[Ls[Type]]) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]) : (MutMap[TV, ST], Map[Str, NuParam]) = { val rawName = v.name parTargs foreach { pta => if (raw.tparams.sizeCompare(pta.size) =/= 0) err(msg"${raw.kind.str} $rawName expects ${ raw.tparams.size.toString} type parameter(s); got ${pta.size.toString}", Loc(v :: pta)) } refreshHelper2(raw: PolyNuDecl, v: Var, parTargs.map(_.map(typeType(_)))) } def complete()(implicit raise: Raise): TypedNuDecl = result.getOrElse { if (isComputing) { err(msg"Unhandled cyclic definition", decl.toLoc) TypedNuDummy(decl) } else trace(s"Completing ${decl.showDbg}") { println(s"Type params ${tparams.mkString(" ")}") println(s"Params ${typedParams.mkString(" ")}") val res = try { isComputing = true decl match { case fd: NuFunDef => def checkNoTyParams() = if (fd.tparams.nonEmpty) err(msg"Type parameters are not yet supported in this position", fd.tparams.head.toLoc) val res_ty = fd.rhs match { case R(PolyType(tps, ty)) => checkNoTyParams() val body_ty = ctx.poly { implicit ctx: Ctx => typeType(ty)(ctx, raise, vars = vars ++ tps.map { case L(tn) => tn.name -> freshVar(provTODO, N)(1) case _ => die }.toMap) } TypedNuFun(ctx.lvl, fd, PolymorphicType(ctx.lvl, body_ty))(isImplemented = false) case R(_) => die case L(body) => if (fd.isLetRec.isDefined) checkNoTyParams() implicit val gl: GenLambdas = // * Don't generalize lambdas if we're already in generalization mode; // * unless the thing is just a simple let binding with functions, // * as in `let r = if true then id else x => x` !isGeneralized && fd.isLetRec.isDefined && !fd.genField val outer_ctx = ctx val body_ty = if (isGeneralized) { // * Note: // * Can't use `ctx.poly` instead of `ctx.nextLevel` here because all the methods // * in the current typing unit are quantified together. // * So instead of inserting an individual `forall` in the type of each method, // * we consider the `forall` to be implicit in the definition of TypedNuFun, // * and that forall is eschewed while typing mutually-recursive methods // * in the same typing unit, which will see the method's type through its mutRecTV. // * This avoids cyclic-looking constraints due to the polymorphic recursion limitation. // * Subnote: in the future, we should type each mutual-recursion-component independently // * and polymorphically wrt to external uses of them. // * Currently they are typed in order. ctx.nextLevel { implicit ctx: Ctx => assert(fd.tparams.sizeCompare(tparamsSkolems) === 0, (fd.tparams, tparamsSkolems)) vars ++ tparamsSkolems |> { implicit vars => val ty = typeTerm(body) if (noApproximateOverload) { val ambiguous = ty.getVars.unsorted.flatMap(_.tsc.keys.flatMap(_.tvs)) .groupBy(_._2) .filter { case (v,pvs) => pvs.sizeIs > 1 } if (ambiguous.nonEmpty) raise(ErrorReport( msg"ambiguous" -> N :: ambiguous.map { case (v,_) => msg"cannot determine satisfiability of type ${v.expPos}" -> v.prov.loco }.toList , true)) } ty } } } else { if (fd.isMut) { constrain(typeTerm(body), mutRecTV) mutRecTV } else typeTerm(body) } val tp = TypeProvenance(fd.toLoc, s"definition of ${fd.isLetRec match { case S(_) => if (fd.genField) "value" else "let binding" case N => "method" }} ${fd.nme.name}") TypedNuFun(ctx.lvl, fd, body_ty.withProv(tp))(isImplemented = true) } ctx.nextLevel { implicit ctx: Ctx => constrain(res_ty.bodyType, mutRecTV) } // Check annotations fd.annotations.foreach(ann => { implicit val gl: GenLambdas = false; val annType = typeTerm(ann) constrain(annType, AnnType) }) res_ty case td: NuTypeDef => /** Check no `this` access in ctor statements or val rhs and reject unqualified accesses to virtual members.. */ def qualificationCheck(members: Ls[NuMember], stmts: Ls[Statement], base: Ls[NuMember], sigs: Ls[NuMember]): Unit = { val cache = mutable.HashMap[Str, Opt[Var]]() val sigMap = sigs.map(m => m.name -> m).toMap val allMembers = sigMap ++ (base.iterator ++ members).map(m => m.name -> m).toMap def isVirtual(nf: TypedNuFun) = nf.fd.isVirtual || (sigMap.get(nf.name) match { case S(sig: TypedNuFun) => sig.fd.virtualLoc.nonEmpty // The signature is virtual by itself, so we need to check the virtual keyword case _ => false }) // Return S(v) when there is an invalid access to the v. def checkThisInCtor(refs: RefMap, name: Opt[Str], stack: Ls[Str])(expection: Bool): Opt[Var] = { def run: Opt[Var] = { refs.thisRef.orElse( refs.refs.foldLeft[Opt[Var]](N)((res, p) => res.orElse(allMembers.get(p._1.name) match { case S(nf: TypedNuFun) if name.fold(true)(name => name =/= p._1.name) && !stack.contains(p._1.name) => // Avoid cycle checking p._2 match { case q @ S(_) if !expection || isVirtual(nf) => q case _ => checkThisInCtor(nf.getFunRefs, S(p._1.name), p._1.name :: stack)(false) } case _ => N // Refer to outer })) ) } name.fold(run)(name => cache.getOrElseUpdate(name, run)) } def checkUnqualifiedVirtual(refs: RefMap, parentLoc: Opt[Loc]) = refs.refs.foreach(p => if (p._2.isEmpty) allMembers.get(p._1.name) match { // unqualified access case S(nf: TypedNuFun) if isVirtual(nf) => err(msg"Unqualified access to virtual member ${p._1.name}" -> parentLoc :: msg"Declared here:" -> nf.fd.toLoc :: Nil) case _ => () }) // If the second error message location is covered by the first one, // we show the first error message with more precise location def mergeErrMsg(msg1: Message -> Opt[Loc], msg2: Message -> Opt[Loc]) = (msg1._2, msg2._2) match { case (S(loc1), l @ S(loc2)) if loc1 covers loc2 => msg1._1 -> l :: Nil case _ => msg1 :: msg2 :: Nil } members.foreach { case tf @ TypedNuFun(_, fd, _) => val refs = tf.getFunRefs if (fd.isLetRec.isDefined) checkThisInCtor(refs, S(tf.name), tf.name :: Nil)(true) match { case S(v) => // not a function && access `this` in the ctor err(mergeErrMsg(msg"Cannot access `this` while initializing field ${tf.name}" -> fd.toLoc, msg"The access to `this` is here" -> v.toLoc)) case N => () } checkUnqualifiedVirtual(refs, fd.toLoc) case _ => () } stmts.foreach{ case Asc(Var("this"), _) => () case s => val refs = getRefs(s) checkThisInCtor(refs, N, Nil)(false) match { case S(v) => err(mergeErrMsg(msg"Cannot access `this` during object initialization" -> s.toLoc, msg"The access to `this` is here" -> v.toLoc)) case N => () } checkUnqualifiedVirtual(refs, s.toLoc) } } /** Checks everything is implemented and there are no implementation duplicates. */ def implemCheck(implementedMembers: Ls[NuMember], toImplement: Ls[NuMember]): Unit = { val implemsMap = MutMap.empty[Str, NuMember] implementedMembers.foreach { m => implemsMap.updateWith(m.name) { case S(_) => err(msg"Duplicated `${m.name}` member definition in `${td.name}`", m.toLoc) N case N => S(m) } } if (!td.isDecl && td.kind =/= Trt && !td.isAbstract) { toImplement.foreach { m => def mkErr(postfix: Ls[Message -> Opt[Loc]]) = err( msg"Member `${m.name}` is declared (or its declaration is inherited) but is not implemented in `${ td.nme.name}`" -> td.nme.toLoc :: msg"Declared here:" -> m.toLoc :: postfix) implemsMap.get(m.name) match { case S(impl) => if (impl.isPrivate) mkErr( msg"Note: ${impl.kind.str} member `${m.name}` is private and cannot be used as a valid implementation" -> impl.toLoc :: Nil) case N => mkErr(Nil) } } } } /** Checks overriding members against their parent types. */ def overrideCheck(newMembers: Ls[NuMember], signatures: Ls[NuMember], clsSigns: Ls[NuMember]): Unit = ctx.nextLevel { implicit ctx: Ctx => // * Q: why exactly do we need `ctx.nextLevel`? val sigMap = MutMap.empty[Str, NuMember] signatures.foreach { m => sigMap.updateWith(m.name) { case S(_) => die case N => S(m) } } newMembers.foreach { m => println(s"Checking overriding for ${m} against ${sigMap.get(m.name)}...") (m, sigMap.get(m.name)) match { case (_, N) => case (m: TypedNuTermDef, S(sig: TypedNuTermDef)) => sig match { // * If the implementation and the declaration are in the same class, // * it does not require to be virtual. case _ if sig.isPrivate => () // * Private members are not actually inherited case td: TypedNuFun if (!td.fd.isVirtual && !clsSigns.contains(sig)) => err(msg"${m.kind.str.capitalize} member '${m.name }' is not virtual and cannot be overridden" -> m.toLoc :: msg"Originally declared here:" -> sig.toLoc :: Nil) case p: NuParam if (!p.isVirtual && !clsSigns.contains(p)) => err(msg"Inherited parameter named '${m.name }' is not virtual and cannot be overridden" -> m.toLoc :: msg"Originally declared here:" -> sig.toLoc :: Nil) case _ => m match { case fun: TypedNuFun if (fun.fd.isLetOrLetRec) => err(msg"Cannot implement ${m.kind.str} member '${m.name }' with a let binding" -> m.toLoc :: msg"Originally declared here:" -> sig.toLoc :: Nil) case _ => } val mSign = m.typeSignature implicit val prov: TP = mSign.prov constrain(mSign, sig.typeSignature) } case (_, S(that)) => err(msg"${m.kind.str.capitalize} member '${m.name}' cannot override ${ that.kind.str} member of the same name declared in parent" -> td.toLoc :: msg"Originally declared here:" -> that.toLoc :: Nil) } } } // intersection of members def membersInter(l: Ls[NuMember], r: Ls[NuMember]): Ls[NuMember] = { def merge(ltm: NuMember, rtm: Option[NuMember]) = { rtm.foreach(rtm => assert(rtm.level === ltm.level, (ltm.level, rtm.level))) (ltm, rtm) match { case (a: TypedNuFun, S(b: TypedNuFun)) => assert(!(a.isImplemented && b.isImplemented)) val fd = NuFunDef((a.fd.isLetRec, b.fd.isLetRec) match { case (S(a), S(b)) => S(a || b) case _ => N // if one is fun, then it will be fun }, a.fd.nme, N/*no sym name?*/, a.fd.tparams, a.fd.rhs)( a.fd.declareLoc, a.fd.virtualLoc, a.fd.mutLoc, N, a.fd.outer orElse b.fd.outer, a.fd.genField, a.fd.annotations) S(TypedNuFun(a.level, fd, a.bodyType & b.bodyType)(a.isImplemented || b.isImplemented)) case (a: NuParam, S(b: NuParam)) => if (!a.isPublic) S(b) else if (!b.isPublic) S(a) else S(NuParam(a.nme, a.ty && b.ty, isPublic = true)(a.level)) case (a: NuParam, S(b: TypedNuFun)) => S(TypedNuFun(a.level, b.fd, a.ty.ub & b.bodyType)(a.isImplemented || b.isImplemented)) case (a: TypedNuFun, S(b: NuParam)) => S(TypedNuFun(a.level, a.fd, b.ty.ub & a.bodyType)(a.isImplemented || b.isImplemented)) case (a, N) => S(a) case (a, S(b)) => err(msg"Intersection of ${a.kind.str} member and ${b.kind.str} members currently unsupported" -> td.toLoc :: msg"The ${a.kind.str} member is defined here:" -> a.toLoc :: msg"The ${b.kind.str} member is defined here:" -> b.toLoc :: Nil) N } } l.foldLeft(r.map(d => d.name -> d).toMap) { case (acc, ltm) => acc.updatedWith(ltm.name)(merge(ltm, _)) }.values.toList } if (((td.kind is Mod) || (td.kind is Mxn)) && td.ctor.isDefined) err(msg"Explicit ${td.kind.str} constructors are not supported", td.ctor.fold[Opt[Loc]](N)(c => c.toLoc)) // * To type signatures correctly, we need to deal with unbound type variables 'X, // * which should be treated as unknowns (extruded skolems). // * Allowing such type variables is important for the typing of signatures such as // * `: Foo | Bar` where `Foo` and `Bar` take type parameters, // * as these will be (in the future) desugared to `: Foo['A0] | Bar['A1]` def typeTypeSignature(sign: Type)(implicit ctx: Ctx): ST = { val outer = ctx val ty = ctx.nest.nextLevel { implicit ctx => // * Type the signature in a higher level, so as to contain unbound type vars val ty = typeType(td.sig.getOrElse(Top)) // * Make these type vars skolems implicit val freshened: MutMap[TV, ST] = MutMap.empty ty.freshenAbove(outer.lvl, rigidify = true) } // * Create a lower-levl type variable to extrude the type through it, // * which will result in making the unbound type vars extruded skolems (i.e., unknowns) val res = freshVar(provTODO, N, N)(ctx.lvl) // * Subtle note: it is sufficient and important to add the type as a LB of the TV // * instead of unifying them because: // * 1. currently we expand type defs the same no matter of the position polarity // * so `class S: T` is always expanded as `#S & 'X` where `'X :> T` // * 2. we don't want to have to check every single subtype of S against T, // * as this check will already have been performed generally when typing class T, // * but this would happen if we instead expanded into a type equivalent to #S & T... constrain(ty, res) // * Retrieve the extruded lower bound. // * Note that there should be only one, and in particular it should not be recursive, // * since the variable is never shared outside this scope. res.lowerBounds match { // case lb :: Nil => TypeBounds.mk(TopType, lb) case lb :: Nil => lb case _ => die } } // Check annotations td.annotations.foreach { ann => implicit val gl: GenLambdas = false val annType = typeTerm(ann) constrain(annType, AnnType) } td.kind match { case Trt => td.params match { case S(ps) => err(msg"trait parameters are not yet supported", ps.toLoc) case _ => } ctx.nest.nextLevel { implicit ctx => ctx ++= paramSymbols ctx ++= typedSignatures.map(nt => nt._1.name -> VarSymbol(nt._2, nt._1.nme)) ctx += "this" -> VarSymbol(thisTV, Var("this")) def inherit(parents: Ls[TypedParentSpec], tags: ST, members: Ls[NuMember], tparamMembs: Map[Str, NuMember], sig_ty: ST) : (ST, Ls[NuMember], Map[Str, NuMember], ST) = parents match { case (trt: TypedNuTrt, argMembs, tpms, loc) :: ps => assert(argMembs.isEmpty, argMembs) inherit(ps, tags & trt.sign, membersInter(members, trt.members.values.toList), tparamMembs ++ tpms, // with type members of parent class sig_ty & trt.sign, ) case (_, _, _, loc) :: ps => err(msg"A trait can only inherit from other traits", loc) inherit(ps, tags, members, tparamMembs, sig_ty) case Nil => (tags, members, tparamMembs, sig_ty) } val (tags, trtMembers, tparamMembs, sig_ty) = inherit(typedParents, trtNameToNomTag(td)(noProv, ctx), Nil, Map.empty, td.sig.fold(TopType: ST)(typeTypeSignature)) td.body.entities.foreach { case fd @ NuFunDef(_, _, _, _, L(_)) => err(msg"Method implementations in traits are not yet supported", fd.toLoc) case _ => } val ttu = typeTypingUnit(td.body, S(td)) val allMembers = ( trtMembers.iterator.map(d => d.name -> d) ++ ttu.implementedMembers.map(d => d.name -> d) ++ typedSignatureMembers ).toMap // check trait overriding overrideCheck(typedSignatureMembers.map(_._2), trtMembers, Nil) TypedNuTrt(outerCtx.lvl, td, tparams, allMembers, TopType, // thisType (same as for Cls) sig_ty, inheritedTags, tparamMembs ) } case Als => if (td.params.getOrElse(Tup(Nil)).fields.nonEmpty) err(msg"Type alias definitions cannot have value parameters" -> td.params.getOrElse(Tup(Nil)).toLoc :: Nil) if (td.parents.nonEmpty) err(msg"Type alias definitions cannot extend parents" -> Loc(td.parents) :: Nil) val body_ty = td.sig match { case S(sig) => ctx.nextLevel { implicit ctx: Ctx => typeType(sig) } case N => err(msg"Type alias definition requires a right-hand side", td.toLoc) } TypedNuAls(outerCtx.lvl, td, tparams, body_ty) case Cls | Mod => ctx.nest.nextLevel { implicit ctx => if ((td.kind is Mod) && typedParams.nonEmpty) // * Can we do better? (Memoization semantics?) err(msg"${td.kind.str.capitalize} parameters are not supported", typedParams.fold(td.nme.toLoc)(tp => Loc(tp.iterator.map(_._1)))) if (!td.isAbstract && !td.isDecl) td.sig match { case S(sig) => warn( msg"Self-type annotations have no effects on non-abstract ${td.kind.str} definitions" -> sig.toLoc :: msg"Did you mean to use `extends` and inherit from a parent class?" -> N :: Nil) case N => } ctx ++= paramSymbols ctx ++= typedSignatures.map(nt => nt._1.name -> VarSymbol(nt._2, nt._1.nme)) val sig_ty = td.sig.fold(TopType: ST)(typeTypeSignature) implicit val prov: TP = TypeProvenance(decl.toLoc, decl.describe) val finalType = thisTV val tparamMems = tparams.map { case (tp, tv, vi) => // TODO use vi val fldNme = td.nme.name + "#" + tp.name val skol = SkolemTag(tv)(tv.prov) NuParam(TypeName(fldNme).withLocOf(tp), FieldType(S(skol), skol)(tv.prov), isPublic = true)(lvl) } val tparamFields = tparamMems.map(p => p.nme.toVar -> p.ty) assert(!typedParams.exists(_.keys.exists(tparamFields.keys.toSet)), ???) case class Pack( superType: ST, mxnMembers: Ls[NuMember], baseClsNme: Opt[Str], baseClsMembers: Ls[NuMember], traitMembers: Ls[NuMember], tparamMembers: Map[Str, NuMember], selfSig: ST, ) def inherit(parents: Ls[TypedParentSpec], pack: Pack): Pack = parents match { case (p, argMembs, tpms, loc) :: ps => println(s"=> Inheriting from $p"); p match { case mxn: TypedNuMxn => assert(finalType.level === lvl) assert(mxn.superTy.level === lvl) assert(mxn.thisTy.level === lvl) constrain(pack.superType, mxn.superTy) constrain(finalType, mxn.thisTy) assert(tpms.isEmpty) // Mixins do not introduce virtual members for type params val newMembs = argMembs ++ mxn.members.valuesIterator.filterNot(_.isValueParam) val newSuperType = WithType( pack.superType, RecordType( newMembs.collect { case m: NuParam => m.nme.toVar -> m.ty case m: TypedNuFun => // val ty = m.typeSignature // * ^ Note: this also works and is more precise (some types made more specific), // * but it causes duplication of recursive type structures // * in typical SuperOOP mixin compositions, so it's better to be less precise // * but simpler/more efficient/more concise here. val ty = m.bodyType m.fd.nme -> ty.toUpper(provTODO) } )(provTODO) )(provTODO) inherit(ps, pack.copy( superType = newSuperType, mxnMembers = newMembs ++ pack.mxnMembers )) case trt: TypedNuTrt => assert(argMembs.isEmpty, argMembs) inherit(ps, pack.copy( traitMembers = membersInter(pack.traitMembers, trt.members.valuesIterator.filterNot(_.isValueParam).toList), tparamMembers = pack.tparamMembers ++ tpms, selfSig = pack.selfSig & trt.sign )) case cls: TypedNuCls => val parNme = cls.nme.name pack.baseClsNme.foreach { cls => err(msg"Cannot inherit from more than one base class: ${ cls} and ${parNme}", loc) } val (baseParamMems, otherBaseMems) = // cls.members.toList.partition(_._2.isValueParam) cls.members.valuesIterator.toList.partition(_.isValueParam) println(s"argMembs $argMembs") println(s"selfSig ${cls.sign}") inherit(ps, pack.copy( baseClsNme = S(parNme), // baseClsMembers = argMembs ++ cls.members.valuesIterator.filterNot(_.isValueParam), // baseClsMembers = argMembs.filterNot(_.isPrivate) ++ cls.members.valuesIterator.filterNot(_.isValueParam), // baseClsMembers = cls.members.valuesIterator.filter(_.isValueParam) ++ argMembs ++ cls.members.valuesIterator.filterNot(_.isValueParam), // baseClsMembers = baseParamMems ::: argMembs ::: otherBaseMems, baseClsMembers = argMembs ++ cls.members.valuesIterator, tparamMembers = pack.tparamMembers ++ tpms, selfSig = pack.selfSig & cls.sign )) case als: TypedNuAls => // Should be rejected in `typedParents` inherit(ps, pack) } case Nil => println(s"Done inheriting: $pack") val thisType = WithType(pack.superType, RecordType(typedParams.getOrElse(Nil))(ttp(td.params.getOrElse(Tup(Nil)), isType = true)) )(provTODO) & clsNameToNomTag(td)(provTODO, ctx) & RecordType(tparamFields)(TypeProvenance(Loc(td.tparams.map(_._2)), "type parameters", isType = true)) trace(s"${lvl}. Finalizing inheritance with $thisType <: $finalType") { assert(finalType.level === lvl) constrain(thisType & sig_ty, finalType) }() if (!td.isAbstract) trace(s"Checking self signature...") { constrain(thisType, pack.selfSig) }() // println(s"${lvl}. Finalized inheritance with $superType ~> $thisType") pack.copy(superType = thisType, selfSig = pack.selfSig & sig_ty) } // * We start from an empty super type. val baseType = RecordType(Nil)(TypeProvenance(Loc(td.parents).map(_.left), "Object")) val paramMems = typedParams.getOrElse(Nil).map(f => NuParam(f._1, f._2, isPublic = !privateParams.contains(f._1))(lvl)) val Pack(thisType, mxnMembers, _, baseClsMembers, traitMembers, tparamMembers, selfSig) = inherit(typedParents, Pack(baseType, tparamMems ++ paramMems, N, Nil, Nil, Map.empty, sig_ty)) // println(s"Final self-type: ${selfSig}") ctx += "this" -> VarSymbol(thisTV, Var("this")) ctx += "super" -> VarSymbol(thisType, Var("super")) val ttu = typeTypingUnit(td.body, S(td)) // * `baseClsImplemMembers` actually also includes parameter members and their arg-based refinements val (baseClsImplemMembers, baseClsIfaceMembers) = baseClsMembers.partition(_.isImplemented) println(s"baseClsImplemMembers ${baseClsImplemMembers}") val newImplems = ttu.implementedMembers val clsSigns = typedSignatureMembers.map(_._2) trace(s"Checking `this` accesses...") { val toCheckImplems = newImplems.filter(_.isImplemented) qualificationCheck(toCheckImplems, td.body.entities.filter { case _: NuDecl => false case _ => true } ++ td.ctor.fold[Ls[Statement]](Nil)(s => s.body.stmts), baseClsMembers, clsSigns) }() // * Those member implementations we inherit from the base class that are not overridden val implemsInheritedFromBaseCls = { val possiblyOverridingNames = (newImplems.iterator ++ mxnMembers).map(_.name).toSet baseClsImplemMembers.iterator.distinctBy(_.name) .filterNot(possiblyOverridingNames contains _.name) .toList } // * ... must type check against the trait signatures trace(s"Checking base class implementations against inherited signatures...") { overrideCheck(implemsInheritedFromBaseCls, traitMembers, Nil) }() // * The following are type signatures all implementations must satisfy // * (but we already know the base class implems satisfy the baseClsMembers signatures) val ifaceMembers = membersInter(baseClsMembers, traitMembers) // * We now check current and non-overridden mixin implementations against // * the signatures from the base class and traits val toCheck = (newImplems.iterator ++ mxnMembers).distinctBy(_.name).toList trace(s"Checking new implementations against inherited signatures...") { overrideCheck(toCheck, (clsSigns.iterator ++ ifaceMembers).distinctBy(_.name).toList, clsSigns) }() val impltdMems = ( newImplems // local members override mixin members: ++ mxnMembers // local and mixin members override parent members: ++ baseClsImplemMembers ).distinctBy(_.name) trace(s"Checking new signatures against inherited signatures...") { overrideCheck(clsSigns, ifaceMembers, clsSigns) }() trace(s"Checking signature implementations...") { implemCheck(impltdMems, (clsSigns.iterator ++ ifaceMembers.iterator) .distinctBy(_.name).filterNot(_.isImplemented).toList) }() val allMembers = (ifaceMembers ++ impltdMems).map(d => d.name -> d).toMap ++ typedSignatureMembers println(s"allMembers $allMembers") val auxCtorParams = td.ctor match { case S(ctor @ Constructor(ps, bod)) => outerCtx.nest.nextLevel { implicit ctx => def getterError(loco: Opt[Loc]) = err(msg"Cannot use `val` in constructor parameters", loco) val res = ps.fields.map { case (S(nme), Fld(FldFlags(mut, spec, getter), value)) => assert(!mut && !spec, "TODO") // TODO if (getter) // TODO we could support this to some extent getterError(nme.toLoc) value.toType match { case R(tpe) => val ty = typeType(tpe) nme -> ty case _ => ??? } case (N, Fld(FldFlags(mut, spec, getter), nme: Var)) => assert(!mut && !spec, "TODO") // TODO if (getter) getterError(nme.toLoc) nme -> freshVar(ttp(nme), N, S(nme.name)) case (N, Fld(_, rhs)) => Var("") -> err(msg"Unsupported constructor parameter shape", rhs.toLoc) } res.foreach { case (nme, ty) => ctx += nme.name -> VarSymbol(ty, nme) } implicit val gl: GenLambdas = false implicit val prov: TP = TypeProvenance(ctor.toLoc, "auxiliary class constructor") val bodStmts = bod match { case Blk(sts) => sts case _ => bod :: Nil } // * TODO later: for each `typedParams`, first add sthg like `ctx += lhs.name -> UndefinedParam(...)` val classParamsMap = MutMap.from(typedParams.getOrElse(Nil).mapValues(some)) bodStmts.foreach { case Eqn(lhs, rhs) => classParamsMap.updateWith(lhs) { case S(S(p)) => val rhs_ty = typeTerm(rhs) constrain(rhs_ty, p.ub) ctx += lhs.name -> VarSymbol(rhs_ty, lhs) S(N) case S(N) => err(msg"Class parameter '${lhs.name}' was already set", lhs.toLoc) N case N => err(msg"Unknown class parameter '${lhs.name}'", lhs.toLoc) N } case stmt: DesugaredStatement => typeStatement(stmt, allowPure = false) case _ => die } S(res) } case N => N } // * After a class is type checked, we need to "solidify" the inferred types of its unannotated mutable fields, // * otherwise they would be treated as polymorphic, which would be wrong. allMembers.foreachEntry { case (nme, v: TypedNuFun) if v.fd.isMut => // * A bit hacky but should work: v.bodyType.unwrapProvs match { case _ if v.fd.rhs.isRight || v.fd.signature.nonEmpty => // * If the type was annotated, we don't need to do anything. case tv: TV if tv.assignedTo.isEmpty => // * Could this `tv` ever be assigned? val res_ty = tv.lowerBounds.reduceOption(_ | _) .getOrElse(err(msg"Could not infer a type for unused mutable field $nme", v.toLoc)) println(s"Setting type of $nme based on inferred lower bounds: $tv := $res_ty") tv.assignedTo = S(res_ty) case ty => lastWords(ty.toString) } case (nme, m) => } TypedNuCls(outerCtx.lvl, td, tparams, typedParams, auxCtorParams.orElse(Option.when( typedParams.isEmpty && (td.kind is Cls) && !td.isAbstract)(Nil)), allMembers, TopType, if (td.isAbstract) selfSig else sig_ty, inheritedTags, tparamMembers ).tap(_.variances) // * Force variance computation } case Mxn => if (td.parents.nonEmpty) err(msg"mixin definitions cannot yet extend parents" -> Loc(td.parents) :: Nil) val outer = ctx ctx.nest.nextLevel { implicit ctx => ctx ++= paramSymbols ctx ++= typedSignatures.map(nt => nt._1.name -> VarSymbol(nt._2, nt._1.nme)) val paramMems = typedParams.map(_.map(f => f._1.name -> NuParam(f._1, f._2, !privateParams.contains(f._1))(lvl))).getOrElse(Nil).toMap val thisTV = freshVar(provTODO, N, S("this")) val superTV = freshVar(provTODO, N, S("super")) ctx += "this" -> VarSymbol(thisTV, Var("this")) ctx += "super" -> VarSymbol(superTV, Var("super")) val ttu = typeTypingUnit(td.body, S(td)) val impltdMems = ttu.implementedMembers val signs = typedSignatureMembers.map(_._2) overrideCheck(impltdMems, signs, signs) implemCheck(impltdMems, signs) val mems = paramMems ++ impltdMems.map(m => m.name -> m).toMap ++ typedSignatureMembers TypedNuMxn(outer.lvl, td, thisTV, superTV, tparams, typedParams.getOrElse(Nil), mems) } } } } finally { isComputing = false } result = S(res) res }(r => s"Completed ${r} where ${r.showBounds}") } def typeSignature(usesNew: Bool, loco: Opt[Loc])(implicit raise: Raise): ST = decl match { case _: NuFunDef => if (isComputing) { println(s"Already computing! Using TV: $mutRecTV") mutRecTV // TODO make sure this is never misused (ie not accessed from difft scope/level) } else complete() match { case TypedNuFun(_, fd, ty) => ty case _ => die } case td: NuTypeDef => // * We want to avoid forcing completion of types needlessly // * OTOH we need the type to be completed to use its aux ctor (whose param types are optional) // * TODO: avoid forcing when the aux ctor has type-annotated params if (td.ctor.isDefined) complete() match { case cls: TypedNuCls => cls.typeSignature(usesNew, loco) case _: TypedNuDummy => errType case _ => die } else typeSignatureOf(usesNew, loco, td, level, tparams, typedParams, N, TopType, inheritedTags) } override def toString: String = s"${decl.name} ~> ${if (isComputing) "" else result.fold("")(_.toString)}" } def refreshHelper2(raw: PolyNuDecl, v: Var, parTargs: Opt[Ls[ST]]) (implicit ctx: Ctx): (MutMap[TV, ST], Map[Str, NuParam]) = { val freshened: MutMap[TV, ST] = MutMap.empty val rawName = v.name val parTP = raw.tparams.lazyZip(parTargs.getOrElse(raw.tparams.map { case (_, tv, _) => freshVar(tv.prov, S(tv), tv.nameHint)(tv.level) })).map { case ((tn, _tv, vi), targ) => val tv = (targ match { case tv: TV => println(s"Passing ${tn.name} :: ${_tv} <=< ${tv}") tv case _ => println(s"Assigning ${tn.name} :: ${_tv} := $targ where ${targ.showBounds}") val tv = freshVar(_tv.prov, S(_tv), _tv.nameHint, lbs = _tv.lowerBounds, ubs = _tv.upperBounds, )(targ.level) println(s"Set ${tv} ~> ${_tv}") assert(tv.assignedTo.isEmpty) // * Note: no checks that the assigned variable satisfies the bounds... // * When we support bounded types, bounds check will be needed at the type definition site assert(tv.lowerBounds.isEmpty, tv.lowerBounds) assert(tv.upperBounds.isEmpty, tv.upperBounds) tv.assignedTo = S(targ) // println(s"Assigned ${tv.assignedTo}") tv }) freshened += _tv -> tv rawName+"#"+tn.name -> NuParam(tn, FieldType(S(tv), tv)(provTODO), isPublic = true)(ctx.lvl) } freshened -> parTP.toMap } } ================================================ FILE: shared/src/main/scala/mlscript/Parser.scala ================================================ package mlscript import scala.annotation.nowarn import scala.util.chaining._ import scala.language.implicitConversions import fastparse._ import mlscript.utils._, shorthands._ import mlscript.Lexer._ /** Inspired by and adapted from: * scalaparse: https://github.com/lihaoyi/fastparse/tree/master/scalaparse * pythonparse: https://github.com/lihaoyi/fastparse/tree/master/pythonparse */ @nowarn("cat=other") // "comparing a fresh object using `ne` will always yield true" in macrco-generated code @SuppressWarnings(Array("org.wartremover.warts.All")) class Parser(origin: Origin, indent: Int = 0, recordLocations: Bool = true) { //implicit def whitespace(cfg: P[_]): P[Unit] = Lexical.wscomment(cfg) implicit def whitespace(cfg: P[_]): P[Unit] = Lexer.nonewlinewscomment(cfg) lazy val nextLevel = new Parser(origin: Origin, indent + 1, recordLocations) def toParams(t: Term) = t def UnitLit = Tup(Nil) // NOTE: due to bug in fastparse, the parameter should be by-name! def locate[p:P, L <: Located](tree: => P[L]) = (Index ~~ tree ~~ Index).map { case (i0, n, i1) => n.withLoc(i0, i1, origin) } def space[p: P] = P( CharIn(" \n") ) def NEWLINE[p: P]: P0 = P( "\n" | End ) def ENDMARKER[p: P]: P0 = P( End ).opaque("unexpected token in this position") def nl_indents[p: P] = P( "\n" ~~ emptyLines ~~ " ".repX(indent, max = indent) ) def emptyLines[p: P] = P( ("" ~ Lexer.comment.? ~ "\n").repX(0) ) def spaces[p: P] = P( (Lexer.nonewlinewscomment.? ~~ "\n").repX(1) ) def NAME[p: P]: P[Var] = locate(ident.map(Var(_))) def ident[p: P]: P[String] = Lexer.identifier | "(" ~ operator.! ~ ")" def NUMBER[p: P]: P[Lit] = locate( P( Lexer.longinteger | Lexer.integer ).map(IntLit) | P( Lexer.floatnumber ).map(DecLit) ) def STRING[p: P]: P[StrLit] = locate(Lexer.stringliteral.map(StrLit(_))) def FIELD[p: P]: P[Var] = locate( P( Lexer.identifier ).map(Var(_)) | P( Lexer.decimalinteger ).map(n => Var(n.toString)) ) def expr[p: P]: P[Term] = P( ite | basicExpr ).opaque("expression") def ite[p: P]: P[Term] = P( kw("if") ~/ expr ~ kw("then") ~ expr ~ kw("else") ~ expr ).map { ite => App(App(App(Var("if"), ite._1), ite._2), ite._3) } def basicExpr[p: P]: P[Term] = P( lams ~ operator_suite.? ).map { case (lhs, N) => lhs case (lhs, S(ops)) => ops.foldLeft(lhs) { case (acc, (op, rhs)) => App(App(op, acc), rhs) } }.opaque("expression") def stmt[p: P]: P[Statement] = defn | let | expr def datatypeDefn[p: P]: P[DatatypeDefn] = P( kw("data") ~ kw("type") ~/ expr ~ emptyLines ~ ( kw("of") ~ (binops.rep(1, ",").map(es => Blk(es.toList)) | suite) ).? ).map { case (hd, N) => DatatypeDefn(hd, Blk(Nil)) case (hd, S(cs)) => DatatypeDefn(hd, cs) }.opaque("data type definition") def dataDefn[p: P]: P[DataDefn] = P( kw("data") ~/ expr ).map(DataDefn(_)).opaque("data definition") def defn[p: P]: P[Statement] = datatypeDefn | dataDefn def let[p: P]: P[LetS] = locate(P( kw("let") ~ kw("rec").!.? ~ commas ~ "=" ~/ (expr | suite) ).map { case (r, p, e) => LetS(r.isDefined, p, e) }).opaque("let binding") def multilineBlock[p: P]: P[Blk] = P( stmt ~ (";" ~ stmt).rep ~ (";".? ~ nl_indents ~~ multilineBlock).? ).map { case (s, ss1, N) => Blk(s :: ss1.toList) case (s, ss1, S(Blk(ss2))) => Blk(s :: ss1.toList ::: ss2.toList) } def operatorBlock[p: P]: P[Seq[(Var, Term)]] = P( Index ~~ operator.! ~~ Index ~ expr ~ (nl_indents ~~ operatorBlock).? ).map { case (i0, op, i1, t, opts) => (Var(op).withLoc(i0, i1, origin), t) +: opts.toList.flatten } def lams[p: P]: P[Term] = P( commas ~ (("/".! | "=>".!) ~/ (expr | suite) | "".! ~ suite).? ).map(checkless { case (trm, N) => trm case (trm, S(("", rest))) => App(trm, toParams(rest)) case (trm, S(("/", rest))) => App(trm, toParams(rest)) case (trm, S(("=>", rest))) => Lam(toParams(trm), rest) }).opaque("applied expressions") // TODO support spreads ""...xs"" def commas[p: P]: P[Term] = P( Index ~~ (NAME ~ ":" ~ (noCommas | suite) | noCommas.map(Var("") -> _)).rep(1, ",").map(_.toList) ~ ",".!.? ~~ Index ).map { case (_, (Var(""), x) :: Nil, N, _) => x case (i0, xs, _, i1) => Tup(xs.map { case (n, t) => (n optionIf (_.name.nonEmpty), Fld(FldFlags.empty, t)) }).withLoc(i0, i1, origin) } def booleans[p: P]: P[Term] = P(binops rep (1, kw("and")) rep (1, kw("or"))) // TODO locs .map(_.map(_.reduce((l, r) => App(OpApp("and", l), r))).reduce((l, r) => App(OpApp("or", l), r))) def noCommas[p: P]: P[Term] = P(booleans ~ ((kw("as") | kw("is")).! ~ booleans).rep).map { case (lhs, casts) => casts.foldLeft(lhs) { case (acc, ("as", rhs)) => Bind(acc, rhs) case (acc, ("is", rhs)) => Test(acc, rhs) } } /** Note that `,` implicitly has the lowest precedence, followed by the ones below. */ private val prec: Map[Char,Int] = List( //",", // Used to have it here; but that made it left-associative ":", "|", "^", "&", "= !", "< >", "+ -", "* / %", ".", ).zipWithIndex.flatMap { case (cs,i) => cs.filterNot(_ == ' ').map(_ -> i) }.toMap.withDefaultValue(Int.MaxValue) def precedence(op: String): Int = prec(op.head) min prec(op.last) // Note: There are three right-associative operators, dealt with above, not here: `=>`, `/`, and `,` // Adapted from: https://github.com/databricks/sjsonnet/blob/master/sjsonnet/src/sjsonnet/Parser.scala#L136-L180 def binops[p: P]: P[Term] = P(apps ~ (Index ~~ operator.! ~~ Index ~/ (apps | suite)).rep ~ "") // Note: interestingly, the ~/ cut above prevents: // a + // b + // c // from being parsed as: // (a + // b) + // c .map { case (pre, fs) => var remaining = fs def climb(minPrec: Int, current: Term): Term = { var result = current while ( remaining.headOption match { case None => false case Some((off0, op, off1, next)) => val prec: Int = precedence(op) if (prec < minPrec) false else { remaining = remaining.tail val rhs = climb(prec + 1, next) result = App(App(Var(op).withLoc(off0, off1, origin), result), rhs) true } } )() result } climb(0, pre) } // Note: the `suite.?` here is used in cases like: // foo // bar // baz // outer app, uses this suite... def apps[p: P]: P[Term] = P( atomOrSelect.rep(1) ~ suite.? ).map { case (as, ao) => (as ++ ao.toList).reduceLeft((f, a) => App(f, toParams(a))) } def atomOrSelect[p: P]: P[Term] = P(atom ~ (Index ~~ "." ~ FIELD ~~ Index).rep).map { case (lhs, sels) => sels.foldLeft(lhs) { case (acc, (i0,str,i1)) => Sel(lhs, str).withLoc(i0, i1, origin) } } def recordBrackets[p: P]: P[Term] = locate(P( "{" ~ (suite | nextLevel.multilineBlock).? ~ nl_indents.? ~ "}" ) .map(xo => Bra(true, xo.getOrElse(Tup(Nil))))) def tupleBrackets[p: P]: P[Term] = locate(P( "(" ~ (suite | nextLevel.multilineBlock).? ~ nl_indents.? ~ ")" ) .map(xo => Bra(false, xo.getOrElse(Tup(Nil))))) def atom[p: P]: P[Term] = P(tupleBrackets | recordBrackets | STRING | NAME | NUMBER) def nextIndentP[p: P]: P[Int] = " ".repX(indent + 1).!.map(_.length) def indented[p: P, A](p: Parser => P[A]): P[A] = "\n" ~~ emptyLines ~~ nextIndentP.flatMapX { nextIndent => p(new Parser(origin, nextIndent, recordLocations)) } def suite[p: P]: P[Term] = P( indented(_.multilineBlock) ).opaque("indented block") def operator_suite[p: P]: P[Seq[(Var, Term)]] = P( indented(_.operatorBlock) ).opaque("operators block") // def repl_input[p: P]: P[Term] = P( (expr | P("").map(_ => UnitLit)) ~ ENDMARKER ) def pgrm[p: P]: P[Pgrm] = P( multilineBlock ~ emptyLines ~ End map (b => Pgrm(b.stmts)) ) } ================================================ FILE: shared/src/main/scala/mlscript/Token.scala ================================================ package mlscript import mlscript.utils._, shorthands._ /** Type of general Tokens */ sealed abstract class Token { def describe: Str = this match { case SPACE => "space" case COMMA => "comma" case SEMI => "semicolon" case NEWLINE => "newline" case INDENT => "indentation" case DEINDENT => "deindentation" case ERROR => "error" case QUOTE => "quote" case LITVAL(value) => "literal" case KEYWORD(name) => if (name.headOption.exists(_.isLetter)) s"'$name' keyword" else s"'$name'" case IDENT(name, symbolic) => if (symbolic) "operator" else "identifier" case SELECT(name) => "selector" case OPEN_BRACKET(k) => s"opening ${k.name}" case CLOSE_BRACKET(k) => s"closing ${k.name}" case BRACKETS(BracketKind.Indent, contents) => s"indented block" case BRACKETS(k, contents) => s"${k.name} section" case COMMENT(text) => "comment" } } /** Type of 'Structured Tokens' aka 'Strokens', * which use a `BRACKETS` construct instead of `OPEN_BRACKET`/`CLOSE_BRACKET` and `INDENT`/`DEINDENT` */ sealed trait Stroken extends Token case object SPACE extends Token with Stroken case object COMMA extends Token with Stroken case object SEMI extends Token with Stroken case object NEWLINE extends Token with Stroken // TODO rm case object INDENT extends Token case object DEINDENT extends Token case object ERROR extends Token with Stroken case object QUOTE extends Token with Stroken final case class LITVAL(value: Lit) extends Token with Stroken final case class KEYWORD(name: String) extends Token with Stroken final case class IDENT(name: String, symbolic: Bool) extends Token with Stroken final case class SELECT(name: String) extends Token with Stroken final case class OPEN_BRACKET(k: BracketKind) extends Token final case class CLOSE_BRACKET(k: BracketKind) extends Token final case class BRACKETS(k: BracketKind, contents: Ls[Stroken -> Loc])(val innerLoc: Loc) extends Token with Stroken final case class COMMENT(text: String) extends Token with Stroken sealed abstract class BracketKind { import BracketKind._ lazy val (beg, end) = this match { case Round => "(" -> ")" case Curly => "{" -> "}" case Square => "[" -> "]" case Angle => "‹" -> "›" case Indent => "→" -> "←" case Quasiquote => "code\"" -> "\"" case QuasiquoteTriple => "code\"\"\"" -> "\"\"\"" case Unquote => "${" -> "}" } def name: Str = this match { case Round => "parenthesis" case Curly => "curly brace" case Square => "square bracket" case Angle => "angle bracket" case Indent => "indentation" case Quasiquote => "quasiquote" case QuasiquoteTriple => "quasiquote triple" case Unquote => "unquote" } } object BracketKind { case object Round extends BracketKind case object Curly extends BracketKind case object Square extends BracketKind case object Angle extends BracketKind case object Indent extends BracketKind case object Quasiquote extends BracketKind case object QuasiquoteTriple extends BracketKind case object Unquote extends BracketKind def unapply(c: Char): Opt[Either[BracketKind, BracketKind]] = c |>? { case '(' => Left(Round) case ')' => Right(Round) case '{' => Left(Curly) case '}' => Right(Curly) case '[' => Left(Square) case ']' => Right(Square) case '‹' => Left(Angle) case '›' => Right(Angle) } } ================================================ FILE: shared/src/main/scala/mlscript/TypeDefs.scala ================================================ package mlscript import scala.collection.mutable import scala.collection.mutable.{Map => MutMap, Set => MutSet} import scala.collection.immutable.{SortedSet, SortedMap} import scala.util.chaining._ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ class TypeDefs extends NuTypeDefs { Typer: Typer => import TypeProvenance.{apply => tp} trait AnyTypeDef { // val kind: TypeDefKind // val nme: TypeName // val tparamsargs: List[(TypeName, TypeVariable)] } /** * TypeDef holds information about declarations like classes, interfaces, and type aliases * * @param kind tells if it's a class, interface or alias * @param nme name of the defined type * @param tparamsargs list of type parameter names and their corresponding type variable names used in the definition of the type * @param tvars * @param bodyTy type of the body, this means the fields of a class or interface or the type that is being aliased * @param mthDecls method type declarations in a class or interface, not relevant for type alias * @param mthDefs method definitions in a class or interface, not relevant for type alias * @param baseClasses base class if the class or interface inherits from any * @param toLoc source location related information * @param positionals positional term parameters of the class * @param adtData maps a class to its ADT by name */ case class TypeDef( kind: TypeDefKind, nme: TypeName, tparamsargs: List[(TypeName, TypeVariable)], bodyTy: SimpleType, mthDecls: List[MethodDef[Right[Term, Type]]], mthDefs: List[MethodDef[Left[Term, Type]]], baseClasses: Set[TypeName], toLoc: Opt[Loc], positionals: Ls[Str], adtData: Opt[AdtInfo] = N, ) { def allBaseClasses(ctx: Ctx)(implicit traversed: Set[TypeName]): Set[TypeName] = baseClasses.map(v => TypeName(v.name)) ++ baseClasses.iterator.filterNot(traversed).flatMap(v => ctx.tyDefs.get(v.name).fold(Set.empty[TypeName])(_.allBaseClasses(ctx)(traversed + v))) val (tparams: List[TypeName], targs: List[TypeVariable]) = tparamsargs.unzip // * This is lazy so that the variable is not created if the type doesn't end up being processed, // * which may happen if it is ill-formed. lazy val thisTv: TypeVariable = freshVar(noProv, N, S("this"), Nil, TypeRef(nme, targs)(noProv) :: Nil)(1) // FIXME coudl N here result in divergence? cf. absence of shadow var tvarVariances: Opt[VarianceStore] = N def getVariancesOrDefault: collection.Map[TV, VarianceInfo] = tvarVariances.getOrElse(Map.empty[TV, VarianceInfo].withDefaultValue(VarianceInfo.in)) } /** Represent a set of methods belonging to some owner type. * This includes explicitly declared/defined as well as inherited methods. */ private case class MethodSet( ownerType: TypeName, parents: List[MethodSet], decls: Map[Str, MethodType], defns: Map[Str, MethodType], ) { private def &(that: MethodSet)(tn: TypeName, overridden: Set[Str])(implicit raise: Raise): MethodSet = MethodSet( tn, this.parents ::: that.parents, mergeMap(this.decls, that.decls)(_ & _), (this.defns.iterator ++ that.defns.iterator).toSeq.groupMap(_._1)(_._2).flatMap { case _ -> Nil => die case mn -> (defn :: Nil) => S(mn -> defn) case mn -> defns if overridden(mn) => N // ignore an inherited method definition if it's overridden case mn -> defns => err(msg"An overriding method definition must be given when inheriting from multiple method definitions" -> tn.toLoc :: msg"Definitions of method $mn inherited from:" -> N :: defns.iterator.map(mt => msg"• ${mt.parents.head}" -> mt.prov.loco).toList) S(mn -> MethodType(defns.head.level, N, tn :: Nil, isInherited = false)( TypeProvenance(tn.toLoc, "inherited method declaration"))) } ) /** Returns a `MethodSet` of the _inherited_ methods only, * disregarding the current MethodSet's methods... * Useful for subsumption checking * as inherited and current methods should be considered separately * An overriding definition is required when multiple method definitions are inherited. * An error is raised if no overriding definition is given. */ def processInheritedMethods(implicit ctx: Ctx, raise: Raise): MethodSet = processInheritedMethodsHelper(includeCurrentMethods = false) private def processInheritedMethodsHelper(includeCurrentMethods: Bool) (implicit ctx: Ctx, raise: Raise): MethodSet = { def addParent(mt: MethodSet): MethodSet = { val td = ctx.tyDefs(ownerType.name) def addThis(mt: MethodType): MethodType = mt.copy(body = mt.body.map(b => b.copy(_1 = td.thisTv)))(mt.prov) def add(mt: MethodType): MethodType = mt.copy(parents = ownerType :: mt.parents)(mt.prov) mt.copy(decls = mt.decls.view.mapValues(addThis).mapValues(add).toMap, defns = mt.defns.view.mapValues(addThis).toMap) } parents.map(_.processInheritedMethodsHelper(true)) .reduceOption(_.&(_)(ownerType, defns.keySet)).map(addParent) .foldRight(if (includeCurrentMethods) this else copy(decls = Map.empty, defns = Map.empty)) { (mds1, mds2) => mds2.copy(decls = mds1.decls ++ mds2.decls, defns = mds1.defns ++ mds2.defns) } } } def tparamField(clsNme: TypeName, tparamNme: TypeName): Var = Var(clsNme.name + "#" + tparamNme.name) def clsNameToNomTag(td: NuTypeDef)(prov: TypeProvenance, ctx: Ctx): ClassTag = { require((td.kind is Cls) || (td.kind is Mod), td.kind) ClassTag(Var(td.nme.name), if(newDefs && td.nme.name =/= "Object") Set.single(TN("Object")) | ctx.tyDefs2.get(td.nme.name).map(_.inheritedTags).getOrElse(Set.empty) else ctx.allBaseClassesOf(td.nme.name) )(prov) } def clsNameToNomTag(td: TypeDef)(prov: TypeProvenance, ctx: Ctx): ClassTag = { require((td.kind is Cls) || (td.kind is Mod), td.kind) ClassTag(Var(td.nme.name), ctx.allBaseClassesOf(td.nme.name))(prov) } def trtNameToNomTag(td: TypeDef)(prov: TypeProvenance, ctx: Ctx): TraitTag = { require(td.kind is Trt) TraitTag(Var(td.nme.name), Set.empty)(prov) } def trtNameToNomTag(td: NuTypeDef)(prov: TypeProvenance, ctx: Ctx): TraitTag = { require(td.kind is Trt) TraitTag(Var(td.nme.name), ctx.tyDefs2.get(td.nme.name).map(_.inheritedTags).getOrElse(Set.empty))(prov) } def baseClassesOf(tyd: mlscript.TypeDef): Set[TypeName] = if (tyd.kind === Als) Set.empty else baseClassesOf(tyd.body) private def baseClassesOf(ty: Type): Set[TypeName] = ty match { case Inter(l, r) => baseClassesOf(l) ++ baseClassesOf(r) case TypeName(nme) => Set.single(TypeName(nme)) case AppliedType(b, _) => baseClassesOf(b) case Record(_) => Set.empty case _: Union => Set.empty case _ => Set.empty // TODO TupleType? } /** Only supports getting the fields of a valid base class type. * Notably, does not traverse type variables. * Note: this does not retrieve the positional fields implicitly defined by tuples */ def fieldsOf(ty: SimpleType, paramTags: Bool)(implicit ctx: Ctx): Map[Var, FieldType] = // trace(s"Fields of $ty {${travsersed.mkString(",")}}") { ty match { case tr @ TypeRef(td, targs) => fieldsOf(tr.expandWith(paramTags, selfTy = false), paramTags) case ComposedType(false, l, r) => mergeMap(fieldsOf(l, paramTags), fieldsOf(r, paramTags))(_ && _) case RecordType(fs) => fs.toMap case p: ProxyType => fieldsOf(p.underlying, paramTags) case Without(base, ns) => fieldsOf(base, paramTags).filter(ns contains _._1) case TypeBounds(lb, ub) => fieldsOf(ub, paramTags) case _: TypeTag | _: FunctionType | _: ArrayBase | _: TypeVariable | _: NegType | _: ExtrType | _: ComposedType | _: SpliceType | _: ConstrainedType | _: PolymorphicType | _: Overload => Map.empty } } // () def processTypeDefs(newDefs0: List[mlscript.TypeDef])(implicit ctx: Ctx, raise: Raise): Ctx = { var allDefs = ctx.tyDefs val allEnv = ctx.env.clone val allMthEnv = ctx.mthEnv.clone val newDefsInfo = newDefs0.iterator.map { case td => td.nme.name -> (td.kind, td.tparams.size) }.toMap val newDefs = newDefs0.flatMap { td0 => val n = td0.nme.name.capitalize val td = if (td0.nme.name.isCapitalized) td0 else { err(msg"Type names must start with a capital letter", td0.nme.toLoc) td0.copy(nme = td0.nme.copy(n).withLocOf(td0.nme)).withLocOf(td0) } if (reservedTypeNames.contains(n)) { err(msg"Type name '$n' is reserved.", td.nme.toLoc) } td.tparams.groupBy(_.name).foreach { case s -> tps if tps.sizeIs > 1 => err( msg"Multiple declarations of type parameter ${s} in ${td.kind.str} definition" -> td.toLoc :: tps.map(tp => msg"Declared at" -> tp.toLoc)) case _ => } allDefs.get(n) match { case S(other) => err(msg"Type '$n' is already defined.", td.nme.toLoc) N case N => val dummyTargs = td.tparams.map(p => freshVar(originProv(p.toLoc, s"${td.kind.str} type parameter", p.name), N, S(p.name))(ctx.lvl + 1)) val tparamsargs = td.tparams.lazyZip(dummyTargs) val bodyTy = typePolyType(td.body, simplify = false)(ctx, raise, tparamsargs.map(_.name -> _).toMap, newDefsInfo) val td1 = TypeDef(td.kind, td.nme, tparamsargs.toList, bodyTy, td.mthDecls, td.mthDefs, baseClassesOf(td), td.toLoc, td.positionals.map(_.name), td.adtInfo) allDefs += n -> td1 S(td1) } } import ctx.{tyDefs => oldDefs} /* Type the bodies of type definitions, ensuring the correctness of parent types * and the regularity of the definitions, then register the constructors and types in the context. */ def typeTypeDefs(implicit ctx: Ctx): Ctx = ctx.copy(tyDefs = oldDefs ++ newDefs.flatMap { td => implicit val prov: TypeProvenance = tp(td.toLoc, "type definition") val n = td.nme def gatherMthNames(td: TypeDef): (Set[Var], Set[Var]) = td.baseClasses.iterator.flatMap(bn => ctx.tyDefs.get(bn.name)).map(gatherMthNames(_)).fold( (td.mthDecls.iterator.map(md => md.nme.copy().withLocOf(md)).toSet, td.mthDefs.iterator.map(md => md.nme.copy().withLocOf(md)).toSet) ) { case ((decls1, defns1), (decls2, defns2)) => ( (decls1.toSeq ++ decls2.toSeq).groupBy(identity).map { case (mn, mns) => if (mns.sizeIs > 1) Var(mn.name).withLoc(td.toLoc) else mn }.toSet, defns1 ++ defns2 )} def checkCycle(ty: SimpleType)(implicit travsersed: Set[TypeName \/ TV]): Bool = // trace(s"Cycle? $ty {${travsersed.mkString(",")}}") { ty match { case TypeRef(tn, _) if travsersed(L(tn)) => err(msg"illegal cycle involving type ${tn}", prov.loco) false case tr @ TypeRef(tn, targs) => checkCycle(tr.expand)(travsersed + L(tn)) case ComposedType(_, l, r) => checkCycle(l) && checkCycle(r) case NegType(u) => checkCycle(u) case p: ProxyType => checkCycle(p.underlying) case Without(base, _) => checkCycle(base) case TypeBounds(lb, ub) => checkCycle(lb) && checkCycle(ub) case tv: TypeVariable => travsersed(R(tv)) || { val t2 = travsersed + R(tv) tv.assignedTo match { case S(ty) => checkCycle(ty)(t2) case N => tv.lowerBounds.forall(checkCycle(_)(t2)) && tv.upperBounds.forall(checkCycle(_)(t2)) } } case PolymorphicType(_, body) => checkCycle(body) case Overload(alts) => alts.forall(checkCycle) case ConstrainedType(cs, bod) => cs.forall(lu => checkCycle(lu._1) && checkCycle(lu._2)) && checkCycle(bod) case _: ExtrType | _: TypeTag | _: FunctionType | _: RecordType | _: ArrayBase | _: SpliceType => true } // }() val rightParents = td.kind match { case Als => checkCycle(td.bodyTy)(Set.single(L(td.nme))) case Mod => err(msg"modules cannot inherit from other types", prov.loco) false case k: ObjDefKind => val parentsClasses = MutSet.empty[TypeRef] def checkParents(ty: SimpleType): Bool = ty match { // case ClassTag(Var("string"), _) => true // Q: always? case _: TypeTag => true // Q: always? // FIXME actually no case tr @ TypeRef(tn2, _) => val td2 = ctx.tyDefs(tn2.name) td2.kind match { case Cls => if (td.kind is Cls) { parentsClasses.isEmpty || { err(msg"${td.kind.str} $n cannot inherit from class ${tn2 } as it already inherits from class ${parentsClasses.head.defn}", prov.loco) false } tap (_ => parentsClasses += tr) } else checkParents(tr.expand) case Trt => checkParents(tr.expand) case Mod => err(msg"cannot inherit from a module", prov.loco) false case Als => err(msg"cannot inherit from a type alias", prov.loco) false case Mxn => err(msg"cannot inherit from a mixin", prov.loco) false } case ComposedType(false, l, r) => checkParents(l) && checkParents(r) case ComposedType(true, l, r) => err(msg"cannot inherit from a type union", prov.loco) false case tv: TypeVariable => err(msg"cannot inherit from a type variable", prov.loco) false case _: FunctionType | _: Overload => err(msg"cannot inherit from a function type", prov.loco) false case _: NegType => err(msg"cannot inherit from a type negation", prov.loco) false case _: TupleType => err(msg"cannot inherit from a tuple type", prov.loco) false case _: ArrayType => err(msg"cannot inherit from a array type", prov.loco) false case _: SpliceType => err(msg"cannot inherit from a splice type", prov.loco) false case _: Without => err(msg"cannot inherit from a field removal type", prov.loco) false case _: TypeBounds => err(msg"cannot inherit from type bounds", prov.loco) false case _: PolymorphicType => err(msg"cannot inherit from a polymorphic type", prov.loco) false case _: ConstrainedType => err(msg"cannot inherit from a constrained type", prov.loco) false case _: RecordType | _: ExtrType => true case p: ProxyType => checkParents(p.underlying) } lazy val checkAbstractAddCtors = { val (decls, defns) = gatherMthNames(td) val isTraitWithMethods = (k is Trt) && defns.nonEmpty val fields = fieldsOf(td.bodyTy, true) fields.foreach { // * Make sure the LB/UB of all inherited type args are consistent. // * This is not actually necessary for soundness // * (if they aren't, the object type just won't be instantiable), // * but will help report inheritance errors earlier (see test BadInherit2). case (nme, FieldType(S(lb), ub)) => constrain(lb, ub) case _ => () } (decls -- defns) match { case absMths if absMths.nonEmpty || isTraitWithMethods => if (ctx.get(n.name).isEmpty) // The class may already be defined in an erroneous program ctx += n.name -> AbstractConstructor(absMths, isTraitWithMethods) case _ => val fields = fieldsOf(td.bodyTy, paramTags = true) val tparamTags = td.tparamsargs.map { case (tp, tv) => tparamField(td.nme, tp) -> FieldType(Some(tv), tv)(tv.prov) } val ctor = k match { case Cls => val nomTag = clsNameToNomTag(td)(originProv(td.nme.toLoc, "class", td.nme.name), ctx) val fieldsRefined = fields.iterator.map(f => if (f._1.name.isCapitalized) f else { val fv = freshVar(noProv, N, S(f._1.name.drop(f._1.name.indexOf('#') + 1)) // strip any "...#" prefix )(1).tap(_.upperBounds ::= f._2.ub) f._1 -> ( if (f._2.lb.isDefined) FieldType(Some(fv), fv)(f._2.prov) else fv.toUpper(f._2.prov) ) }).toList PolymorphicType(MinLevel, FunctionType( singleTup(RecordType.mk(fieldsRefined.filterNot(_._1.name.isCapitalized))(noProv)), nomTag & RecordType.mk( fieldsRefined ::: tparamTags )(noProv) // * TODO try later: // TypeRef(td.nme, td.tparamsargs.unzip._2)(noProv) & RecordType.mk(fieldsRefined)(noProv) )(originProv(td.nme.toLoc, "class constructor", td.nme.name))) case Trt => val nomTag = trtNameToNomTag(td)(originProv(td.nme.toLoc, "trait", td.nme.name), ctx) val tv = freshVar(noProv, N)(1) tv.upperBounds ::= td.bodyTy PolymorphicType(MinLevel, FunctionType( singleTup(tv), tv & nomTag & RecordType.mk(tparamTags)(noProv) )(originProv(td.nme.toLoc, "trait constructor", td.nme.name))) case _ => ??? // TODO } ctx += n.name -> VarSymbol(ctor, Var(n.name)) } true } checkParents(td.bodyTy) && checkCycle(td.bodyTy)(Set.single(L(td.nme))) && checkAbstractAddCtors case _ => ??? // TODO } def checkRegular(ty: SimpleType)(implicit reached: Map[Str, Ls[SimpleType]]): Bool = ty match { case tr @ TypeRef(defn, targs) => reached.get(defn.name) match { case None => checkRegular(tr.expandWith(false, selfTy = false))(reached + (defn.name -> targs)) case Some(tys) => // Note: this check *has* to be relatively syntactic because // the termination of constraint solving relies on recursive type occurrences // obtained from unrolling a recursive type to be *equal* to the original type // and to have the same has hashCode (see: the use of a cache MutSet) if (defn === td.nme && tys =/= targs) { err(msg"Type definition is not regular: it occurs within itself as ${ expandType(tr).show(Typer.newDefs) }, but is defined as ${ expandType(TypeRef(defn, td.targs)(noProv)).show(Typer.newDefs) }", td.toLoc)(raise) false } else true } case _ => ty.children(includeBounds = false).forall(checkRegular) } // Note: this will end up going through some types several times... We could make sure to // only go through each type once, but the error messages would be worse. if (rightParents && (irregularTypes || checkRegular(td.bodyTy)(Map(n.name -> td.targs)))) td.nme.name -> td :: Nil else Nil }) def typeMethods(implicit ctx: Ctx): Ctx = { /* Perform subsumption checking on method declarations and definitions by rigidifying class type variables, * then register the method signatures in the context */ def checkSubsume(td: TypeDef, mds: MethodSet): Unit = { val tn = td.nme val MethodSet(_, _, decls, defns) = mds val MethodSet(_, _, declsInherited, defnsInherited) = mds.processInheritedMethods val rigidtargs = { implicit val state: MutMap[TV, ST] = MutMap.empty td.targs.map(freshenAbove(ctx.lvl, _, true)) } val targsMap = td.targs.lazyZip(rigidtargs).toMap[SimpleType, SimpleType] def ss(mt: MethodType, bmt: MethodType)(implicit prov: TypeProvenance) = { // implicit val ec: Opt[ExtrCtx] = N constrain(subst(mt.bodyPT, targsMap).instantiate, subst(bmt.bodyPT, targsMap).rigidify) } def registerImplicitSignatures(mn: Str, mthTy: MethodType) = ctx.getMth(N, mn) match { // If the currently registered method belongs to one of the base classes of this class, // then we don't need to do anything. // This is because implicit method calls always default to the parent methods. case S(MethodType(_, _, parents, _)) if { val bcs = ctx.allBaseClassesOf(tn.name) parents.forall(prt => bcs(TypeName(prt.name))) } => // If this class is one of the base classes of the parent(s) of the currently registered method, // then we need to register the new method. Only happens when the class definitions are "out-of-order", // and can disambiguate implicit calls previously marked as ambiguous. // Example: // class B: A // method F: 0 // class C: A // method F: 42 // class A // method F: int case S(MethodType(_, _, parents, _)) if { val v = TypeName(tn.name) parents.forall(prt => ctx.allBaseClassesOf(prt.name).contains(v)) } => ctx.addMth(N, mn, mthTy) // If this class is unrelated to the parent(s) of the currently registered method, // then we mark it as ambiguous: case S(mt2) => // Create an ambiguous method "placeholder" (i.e., it has no `body`) val ambiguousMth = MethodType(0, N, (mt2.parents ::: mthTy.parents).distinct, isInherited = false)(mt2.prov) ctx.addMth(N, mn, ambiguousMth) case N => ctx.addMth(N, mn, mthTy) } def overrideError(mn: Str, mt: MethodType, mt2: MethodType) = { mt2.parents.foreach(parent => err(msg"Overriding method ${parent}.${mn} without explicit declaration is not allowed." -> mt.prov.loco :: msg"Note: method definition inherited from" -> mt2.prov.loco :: Nil)(raise)) println(s">> Checking subsumption (against inferred type) for inferred type of $mn : $mt") } declsInherited.foreach { case mn -> mt => ctx.addMth(S(tn.name), mn, mt) } defnsInherited.foreach { case mn -> mt => println(s">> Checking subsumption for inferred type of $mn : $mt") (if (decls.isDefinedAt(mn) && !defns.isDefinedAt(mn)) decls.get(mn) else N).orElse(declsInherited.get(mn)) .foreach(ss(mt, _)(mt.prov)) if (!declsInherited.get(mn).exists(decl => !decl.isInherited && decl.parents.last === mt.parents.last)) ctx.addMth(S(tn.name), mn, mt) } decls.foreach { case mn -> mt => println(s">> Checking subsumption for declared type of $mn : $mt") ((declsInherited.get(mn), defnsInherited.get(mn)) match { case (S(decl), S(defn)) => if (!defns.isDefinedAt(mn)) defn.parents.foreach(parent => err(msg"Overriding method ${parent}.${mn} without an overriding definition is not allowed." -> mt.prov.loco :: msg"Note: method definition inherited from" -> defn.prov.loco :: Nil)(raise)) S(decl) case (S(decl), N) => S(decl) case (N, S(defn)) => overrideError(mn, mt, defn) S(defn) case (N, N) => N }).foreach(ss(mt, _)(mt.prov)) ctx.addMth(S(tn.name), mn, mt) registerImplicitSignatures(mn, mt) } defns.foreach { case mn -> mt => implicit val prov: TypeProvenance = mt.prov println(s">> Checking subsumption for inferred type of $mn : $mt") decls.get(mn).orElse((declsInherited.get(mn), defnsInherited.get(mn)) match { case (S(decl), S(defn)) if defn.parents.toSet.subsetOf(decl.parents.toSet) => S(decl) case (_, S(defn)) => overrideError(mn, mt, defn) S(defn) case (S(decl), N) => S(decl) case (N, N) => N }).foreach(ss(mt, _)) if (!decls.isDefinedAt(mn)) { // If the class declares that method explicitly, // the declared signature is used so we don't have to do anything // If the class does not declare that method explicitly (it could be inherited), // we still want to make the method available using its inferred signature // both for implicit method calls and for explicit ones ctx.addMth(S(tn.name), mn, mt) registerImplicitSignatures(mn, mt) } ctx.addMthDefn(tn.name, mn, mt) } } newDefs.foreach { td => if (ctx.tyDefs.isDefinedAt(td.nme.name)) { /* Recursive traverse the type definition and type the bodies of method definitions * by applying the targs in `TypeRef` and rigidifying class type parameters. */ val rigidtargs = { implicit val state: MutMap[TV, ST] = MutMap.empty td.targs.map(freshenAbove(ctx.lvl, _, true)) } val reverseRigid = rigidtargs.lazyZip(td.targs).toMap def rec(tr: TypeRef, top: Bool = false)(ctx: Ctx): MethodSet = ctx.tyDefs.get(tr.defn.name) match { case N => err(msg"type identifier not found: ${tr.defn.name}" -> tr.prov.loco :: Nil) MethodSet(tr.defn, Nil, Map.empty, Map.empty) case S(td2) => implicit val thisCtx: Ctx = ctx.nest val targsMap = td2.tparams.iterator.map(_.name).zip(tr.targs).toMap val declared = MutMap.empty[Str, Opt[Loc]] val defined = MutMap.empty[Str, Opt[Loc]] def filterTR(ty: SimpleType): List[TypeRef] = ty match { case ProxyType(und) => filterTR(und) case tr: TypeRef => tr :: Nil case ComposedType(false, l, r) => filterTR(l) ::: filterTR(r) case _ => Nil } def go(md: MethodDef[_ <: Term \/ Type]): (Str, MethodType) = { val thisTag = TraitTag(Var("this"), Set.empty)(noProv) // or Skolem?! // val thisTag = SkolemTag(thisCtx.lvl/*TODO correct?*/, Var("this"))(noProv) val thisTy = thisTag & tr thisCtx += "this" -> VarSymbol(thisTy, Var("this")) val MethodDef(rec, prt, nme, tparams, rhs) = md val prov: TypeProvenance = tp(md.toLoc, (if (!top) "inherited " else "") + "method " + rhs.fold(_ => "definition", _ => "declaration")) val fullName = s"${td.nme.name}.${nme.name}" if (top) { if (!nme.name.isCapitalized) err(msg"Method names must start with a capital letter", nme.toLoc) rhs.fold(_ => defined, _ => declared).get(nme.name) match { case S(loco) => err( msg"Method '$fullName' is already ${rhs.fold(_ => "defined", _ => "declared")}" -> nme.toLoc :: msg"at" -> loco :: Nil) case N => } tparams.groupBy(_.name).foreach { case s -> tps if tps.sizeIs > 1 => err( msg"Multiple declarations of type parameter ${s} in ${prov.desc}" -> md.toLoc :: tps.map(tp => msg"Declared at" -> tp.toLoc)) case _ => } val tp1s = td2.tparams.iterator.map(tp => tp.name -> tp).toMap tparams.foreach(tp2 => tp1s.get(tp2.name) match { case S(tp1) => warn( msg"Method type parameter ${tp1}" -> tp1.toLoc :: msg"shadows class type parameter ${tp2}" -> tp2.toLoc :: Nil) case N => }) } rhs.fold(_ => defined, _ => declared) += nme.name -> nme.toLoc val dummyTargs2 = tparams.map(p => TraitTag(Var(p.name), Set.empty)(originProv(p.toLoc, "method type parameter", p.name))) // FIXME or Skolem?! val targsMap2 = targsMap ++ tparams.iterator.map(_.name).zip(dummyTargs2).toMap val reverseRigid2 = reverseRigid ++ dummyTargs2.map(t => t -> freshVar(t.prov, N, S(t.id.idStr))(thisCtx.lvl + 1)) + (thisTag -> td.thisTv) + (td.thisTv -> td.thisTv) // needed to prevent the type variable from being refreshed during substitution! val bodyTy: PolymorphicType = subst(rhs.fold(term => ctx.getMthDefn(prt.name, nme.name) .fold(typeLetRhs(rec, nme.name, term)(thisCtx, raise, targsMap2, genLambdas = !distributeForalls || generalizeCurriedFunctions)) { mt => // Now buckle-up because this is some seriously twisted stuff: // If the method is already in the environment, // it means it belongs to a previously-defined class/trait (not the one being typed), // in which case we need to perform a substitution on the corresponding method body... val targsMap3 = td2.targs.lazyZip(tr.targs).toMap[ST, ST] + (td2.thisTv -> td.thisTv) + (td.thisTv -> td.thisTv) // Subsitute parent this TVs to current this TV. PolymorphicType(mt.bodyPT.level, subst(mt.bodyPT.body, targsMap3) match { // Try to wnwrap one layer of prov, which would have been wrapped by `MethodType.bodyPT`, // and will otherwise mask the more precise new prov that contains "inherited" case ProvType(underlying) => underlying case pt => pt }) }, ty => { implicit val tp: TP = prov thisCtx.nextLevel { newCtx => PolymorphicType(ctx.lvl, typeType(ty)(newCtx, raise, targsMap2)) } // ^ Note: we need to go to the next level here, // which is also done automatically by `typeLetRhs` in the case above }), reverseRigid2) val mthTy = MethodType(bodyTy.level, S((td.thisTv, bodyTy.body)), td2.nme :: Nil, false)(prov) if (rhs.isRight || !declared.isDefinedAt(nme.name)) { if (top) thisCtx.addMth(S(td.nme.name), nme.name, mthTy) thisCtx.addMth(N, nme.name, mthTy) } nme.name -> mthTy } MethodSet(td2.nme, filterTR(tr.expand).map(rec(_)(thisCtx)), td2.mthDecls.iterator.map(go).toMap, td2.mthDefs.iterator.map(go).toMap) } val mds = rec(TypeRef(td.nme, rigidtargs)(tp(td.toLoc, "type definition")), true)(ctx) checkSubsume(td, mds) }} ctx } typeMethods(typeTypeDefs(ctx.copy(env = allEnv, mthEnv = allMthEnv, tyDefs = allDefs))) } /** * Finds the variances of all type variables in the given type definitions with the given * context using a fixed point computation. The algorithm starts with each type variable * as bivariant by default and each type defintion position as covariant and * then keeps updating the position variance based on the types it encounters. * * It uses the results to update variance info in the type defintions * * @param tyDefs * @param ctx */ def computeVariances(tyDefs: List[TypeDef], ctx: Ctx): Unit = { println(s"VARIANCE ANALYSIS") var varianceUpdated: Bool = false; /** Update variance information for all type variables belonging * to a type definition. * * @param ty * type tree to check variance for * @param curVariance * variance of current position where the type tree has been found * @param tyDef * type definition which is currently being processed * @param visited * set of type variables visited along with the variance * true polarity if covariant position visit * false polarity if contravariant position visit * both if invariant position visit */ def updateVariance(ty: SimpleType, curVariance: VarianceInfo)(implicit tyDef: TypeDef, visited: MutSet[Bool -> TypeVariable]): Unit = { def fieldVarianceHelper(fieldTy: FieldType): Unit = { fieldTy.lb.foreach(lb => updateVariance(lb, curVariance.flip)) updateVariance(fieldTy.ub, curVariance) } trace(s"upd[$curVariance] $ty") { // Note: could simplify this (at some perf cost) by just using ty.childrenPol ty match { case ProxyType(underlying) => updateVariance(underlying, curVariance) case _: TypeTag => () case ExtrType(pol) => () case t: TypeVariable => // update the variance information for the type variable val tvv = tyDef.tvarVariances.getOrElse(die) val oldVariance = tvv.getOrElseUpdate(t, VarianceInfo.bi) val newVariance = oldVariance && curVariance if (newVariance =/= oldVariance) { tvv(t) = newVariance println(s"UPDATE ${tyDef.nme.name}.$t from $oldVariance to $newVariance") varianceUpdated = true } val (visitLB, visitUB) = ( !curVariance.isContravariant && !visited(true -> t), !curVariance.isCovariant && !visited(false -> t), ) t.assignedTo match { case S(ty) => if (visitLB || visitUB) { visited += true -> t; visited += false -> t updateVariance(ty, curVariance) } case N => if (visitLB) visited += true -> t if (visitUB) visited += false -> t if (visitLB) t.lowerBounds.foreach(lb => updateVariance(lb, VarianceInfo.co)) if (visitUB) t.upperBounds.foreach(ub => updateVariance(ub, VarianceInfo.contra)) } case RecordType(fields) => fields.foreach { case (_ , fieldTy) => fieldVarianceHelper(fieldTy) } case NegType(negated) => updateVariance(negated, curVariance.flip) case TypeRef(defn, targs) => // it's possible that the type definition may not exist in the // context because it is malformed or incorrect. Do nothing in // such cases ctx.tyDefs.get(defn.name).foreach(typeRefDef => { // variance for all type parameters of type definitions has been preset // do nothing if variance for the parameter does not exist targs.zip(typeRefDef.tparamsargs).foreach { case (targ, (_, tvar)) => typeRefDef.tvarVariances.getOrElse(die).get(tvar).foreach { case in @ VarianceInfo(false, false) => updateVariance(targ, in) case VarianceInfo(true, false) => updateVariance(targ, curVariance) case VarianceInfo(false, true) => updateVariance(targ, curVariance.flip) case VarianceInfo(true, true) => () } } }) case ComposedType(pol, lhs, rhs) => updateVariance(lhs, curVariance) updateVariance(rhs, curVariance) case TypeBounds(lb, ub) => updateVariance(lb, VarianceInfo.contra) updateVariance(ub, VarianceInfo.co) case ArrayType(inner) => fieldVarianceHelper(inner) case TupleType(fields) => fields.foreach { case (_ , fieldTy) => fieldVarianceHelper(fieldTy) } case SpliceType(elems) => elems.foreach { case L(ty) => updateVariance(ty, curVariance) case R(fld) => fieldVarianceHelper(fld) } case FunctionType(lhs, rhs) => updateVariance(lhs, curVariance.flip) updateVariance(rhs, curVariance) case Without(base, names) => updateVariance(base, curVariance.flip) case Overload(alts) => alts.foreach(updateVariance(_, curVariance)) case PolymorphicType(lvl, bod) => // * It seems we should want to ignore from the analysis // * those type vars that are being quantified... // * When the same variable occurs both as quantified and not quantified // * in a type, this could make a difference // * (like it used to in `analysis/Weird.mls`) updateVariance(bod, curVariance) case ConstrainedType(cs, bod) => cs.foreach { lu => updateVariance(lu._1, VarianceInfo.co) updateVariance(lu._2, VarianceInfo.contra) } updateVariance(bod, curVariance) } }() } // set default value for all type variables as bivariant // this prevents errors when printing type defintions in // DiffTests for type variables that are not used at all // and hence are not set in the variance info map tyDefs.foreach { t => if (t.tvarVariances.isEmpty) { // * ^ This may not be empty if the type def was (erroneously) defined several types in the same block t.tvarVariances = S(MutMap.empty) t.tparamsargs.foreach { case (_, tvar) => t.tvarVariances.getOrElse(die).put(tvar, VarianceInfo.bi) } } } var i = 1 do trace(s"⬤ ITERATION $i") { val visitedSet: MutSet[Bool -> TypeVariable] = MutSet() varianceUpdated = false; tyDefs.foreach { case t @ TypeDef(k, nme, _, body, mthDecls, mthDefs, _, _, _, _) => trace(s"${k.str} ${nme.name} ${ t.tvarVariances.getOrElse(die).iterator.map(kv => s"${kv._2} ${kv._1}").mkString(" ")}") { updateVariance(body, VarianceInfo.co)(t, visitedSet) val stores = (mthDecls ++ mthDefs).foreach { mthDef => val mthBody = ctx.mthEnv.getOrElse( Right(Some(nme.name), mthDef.nme.name), throw new Exception(s"Method ${mthDef.nme.name} does not exist in the context") ).body mthBody.foreach { case (_, body) => updateVariance(body, VarianceInfo.co)(t, visitedSet) } } }() } i += 1 }() while (varianceUpdated) println(s"DONE") } type VarianceStore = MutMap[TypeVariable, VarianceInfo] object VarianceStore { def empty: VarianceStore = MutMap.empty } } ================================================ FILE: shared/src/main/scala/mlscript/TypeSimplifier.scala ================================================ package mlscript import scala.collection.mutable.{Map => MutMap, Set => MutSet, LinkedHashMap, LinkedHashSet} import scala.collection.immutable.{SortedMap, SortedSet} import scala.util.chaining._ import mlscript.utils._, shorthands._ trait TypeSimplifier { self: Typer => def printPols(pols: Map[TypeVariable, Opt[Bool]]): Str = pols.iterator.map(e => s"${printPol(e._2)}${e._1}").mkString(", ") /** Remove bounds that are not reachable by traversing the type following variances. * Note that doing this on annotated type signatures would need to use polarity None * because a type signature can both be used (positively) and checked against (negatively). */ def removeIrrelevantBounds(ty: TypeLike, pol: Opt[Bool], reverseBoundsOrder: Bool, inPlace: Bool = false) (implicit ctx: Ctx): TypeLike = { val _ctx = ctx val allVarPols = ty.getVarsPol(PolMap(pol)) println(s"allVarPols: ${printPols(allVarPols)}") val renewed = MutMap.empty[TypeVariable, TypeVariable] val renewedtsc = MutMap.empty[TupleSetConstraints, TupleSetConstraints] def renew(tv: TypeVariable): TypeVariable = renewed.getOrElseUpdate(tv, if (inPlace) tv else freshVar(noProv, S(tv), tv.nameHint)(tv.level) tap { fv => println(s"Renewed $tv ~> $fv") }) def processLike(ty: TypeLike): TypeLike = ty match { case ty: ST => process(ty, N) case OtherTypeLike(tu) => tu.map(process(_, N)) } def process(ty: ST, parent: Opt[Bool -> TV], canDistribForall: Opt[Level] = N): ST = // trace(s"process($ty) $canDistribForall") { ty match { case SkolemTag(tv: TypeVariable) => process(tv, parent) case tv: TypeVariable => parent.filter(_._2 === tv).foreach(p => return ExtrType(p._1)(noProv)) var isNew = false val nv = renewed.getOrElseUpdate(tv, { isNew = true; renew(tv) }) if (isNew) tv.assignedTo match { case S(ty) => // * If the variable is polar, we turn the `assignedTo` into a simple bound. // * I'm not actually sure if that's useful/a good idea. // * Maybe we should process with the appropriate parent, but still generate an `assignedTo`? // * (Tried it, and it makes almost no difference in the end result.) allVarPols(tv) match { case p if p.isEmpty || nv.assignedTo.nonEmpty => nv.assignedTo = S(process(ty, N)) case N => die // covered case S(true) => nv.lowerBounds = (process(ty, S(true -> tv)) :: Nil).filterNot(_.isBot) case S(false) => nv.upperBounds = (process(ty, S(false -> tv)) :: Nil).filterNot(_.isTop) } case N => nv.lowerBounds = if (allVarPols(tv).forall(_ === true)) (if (reverseBoundsOrder) tv.lowerBounds.reverseIterator else tv.lowerBounds.iterator ).map(process(_, S(true -> tv))) .reduceOption(_ | _).filterNot(_.isBot).toList else Nil nv.upperBounds = if (allVarPols(tv).forall(_ === false)) (if (reverseBoundsOrder) tv.upperBounds.reverseIterator else tv.upperBounds.iterator ).map(process(_, S(false -> tv))) .reduceOption(_ &- _).filterNot(_.isTop).toList else Nil if (noApproximateOverload) nv.tsc ++= tv.tsc.iterator.map { case (tsc, i) => renewedtsc.get(tsc) match { case S(tsc) => (tsc, i) case N if inPlace => (tsc, i) case N => val t = new TupleSetConstraints(tsc.constraints, tsc.tvs) renewedtsc += tsc -> t t.tvs = t.tvs.map(x => (x._1, process(x._2, N))) (t, i) }} } nv case ComposedType(true, l, r) => process(l, parent, canDistribForall = canDistribForall) | process(r, parent, canDistribForall = canDistribForall) case ComposedType(false, l, r) => process(l, parent, canDistribForall = canDistribForall) & process(r, parent, canDistribForall = canDistribForall) case NegType(ty) => process(ty, parent.map(_.mapFirst(!_))).neg(ty.prov) case ProvType(ty) if inPlace => ProvType(process(ty, parent, canDistribForall = canDistribForall))(ty.prov) case ProvType(ty) => process(ty, parent, canDistribForall = canDistribForall) case tr @ TypeRef(defn, targs) if builtinTypes.contains(defn) && tr.canExpand => process(tr.expandOrCrash, parent) case RecordType(fields) => RecordType.mk(fields.flatMap { case (v @ Var(fnme), fty) => // * We make a pass to transform the LB and UB of variant type parameter fields into their extrema val prefix = fnme.takeWhile(_ =/= '#') val postfix = fnme.drop(prefix.length + 1) lazy val default = fty.update(process(_ , N), process(_ , N)) if (postfix.isEmpty || prefix.isEmpty) v -> default :: Nil else ctx.tyDefs.get(prefix) match { case S(td) => td.tvarVariances.fold(v -> default :: Nil)(tvv => tvv(td.tparamsargs.find(_._1.name === postfix).getOrElse(die)._2) match { case VarianceInfo(true, true) => Nil case VarianceInfo(co, contra) => if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil else v -> default :: Nil }) case N => // v -> default :: Nil ctx.tyDefs2.get(prefix) match { case S(info) => info.result match { case S(cls: TypedNuCls) => cls.varianceOf(cls.tparams.find(_._1.name === postfix).getOrElse(die)._2) match { case VarianceInfo(true, true) => Nil case VarianceInfo(co, contra) => if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil else v -> default :: Nil } case S(trt: TypedNuTrt) => // TODO factor w/ above & generalize trt.tparams.iterator.find(_._1.name === postfix).flatMap(_._3).getOrElse(VarianceInfo.in) match { case VarianceInfo(true, true) => Nil case VarianceInfo(co, contra) => if (co) v -> FieldType(S(BotType), process(fty.ub, N))(fty.prov) :: Nil else if (contra) v -> FieldType(fty.lb.map(process(_, N)), TopType)(fty.prov) :: Nil else v -> default :: Nil } case S(_) => ??? // TODO: case N => ??? // TODO use info.explicitVariances } case N => lastWords(s"'$prefix' not found") } } })(ty.prov) case PolymorphicType(plvl, bod) => val res = process(bod, parent, canDistribForall = S(plvl)) canDistribForall match { case S(outerLvl) if distributeForalls => implicit val ctx: Ctx = _ctx.copy(lvl = outerLvl + 1) PolymorphicType(plvl, res).instantiate case _ => PolymorphicType.mk(plvl, res) } case ft @ FunctionType(l, r) => FunctionType(process(l, N), process(r, N, canDistribForall = canDistribForall))(ft.prov) case _ => ty.mapPol(N, smart = true)((_, ty) => process(ty, N)) } // }(r => s"= $r") processLike(ty) } /** Transform the type recursively, putting everything in Disjunctive Normal Forms and reconstructing class types * from their structural components. * Note that performing this _in place_ is not fully correct, * as this will lead us to assume TV bounds while simplifying the TV bounds themselves! * – see [test:T3] – * However, I think this only manifests in contrived manually-constructed corner cases like * unguarded recursive types such as `C['a] | 'a as 'a`. */ def normalizeTypes_!(st: TypeLike, pol: Opt[Bool])(implicit ctx: Ctx): TypeLike = { val _ctx = ctx lazy val allVarPols = st.getVarsPol(PolMap(pol)) println(s"allVarPols: ${printPols(allVarPols)}") val processed = MutSet.empty[TV] def helper(dnf: DNF, pol: Opt[Bool], canDistribForall: Opt[Level] = N): ST = { println(s"DNF: $dnf") val cs = dnf.cs val (csNegs, otherCs) = cs.partitionMap { case c @ Conjunct(l, vs, r, nvs) if l.isTop && vs.isEmpty && !(r.isBot && nvs.isEmpty) => // L(r, nvs) L(c) case c => R(c) } // * The goal here is to normalize things like `... | ~A | ~B | ~C` the same as we would `... | ~T` // * where `T` is `A & B & C`. // * It is fine to call `go` because we made sure A, B, C, etc. do not themsleves have any negative components. val csNegs2 = if (csNegs.isEmpty) BotType else go(csNegs.foldLeft(TopType: ST)(_ & _.toType(sort = true).neg()), pol.map(!_)).neg() // TODO sort?! csNegs and toType val otherCs2 = otherCs.sorted.map { c => c.vars.foreach(processVar) c.nvars.foreach(processVar) c.toTypeWith(_ match { case LhsRefined(bo, tts, rcd, trs) => // * The handling of type parameter fields is currently a little wrong here, // * because we remove: // * - type parameter fields of parent classes, // * whereas they could _in principle_ be refined and // * not correspond exactly to these of the currenly-reconstructed class; // * and // * - type parameter fields of the current trait tags // * whereas we don't actually reconstruct applied trait types... // * it would be better to just reconstruct them (TODO) val trs2 = trs.map { case (d, tr @ TypeRef(defn, targs)) => d -> TypeRef(defn, tr.mapTargs(pol)((pol, ta) => go(ta, pol)))(tr.prov) } val traitPrefixes = tts.iterator.collect{ case TraitTag(Var(tagNme), _) => tagNme.capitalize }.toSet bo match { case S(cls @ ClassTag(Var(tagNme), ps)) if !primitiveTypes.contains(tagNme) && ctx.tyDefs.contains(tagNme.capitalize) && !newDefs => val clsNme = tagNme.capitalize // TODO rm capitalize val clsTyNme = TypeName(clsNme) val td = ctx.tyDefs(clsNme) val rcdMap = rcd.fields.toMap val rcd2 = rcd.copy(rcd.fields.mapValues(_.update(go(_, pol.map(!_)), go(_, pol))))(rcd.prov) println(s"rcd2 ${rcd2}") val vs = td.getVariancesOrDefault // * Reconstruct a TypeRef from its current structural components val typeRef = TypeRef(td.nme, td.tparamsargs.zipWithIndex.map { case ((tp, tv), tpidx) => val fieldTagNme = tparamField(clsTyNme, tp) val fromTyRef = trs2.get(clsTyNme).map(_.targs(tpidx) |> { ta => FieldType(S(ta), ta)(noProv) }) fromTyRef.++(rcd2.fields.iterator.filter(_._1 === fieldTagNme).map(_._2)) .foldLeft((BotType: ST, TopType: ST)) { case ((acc_lb, acc_ub), FieldType(lb, ub)) => (acc_lb | lb.getOrElse(BotType), acc_ub & ub) }.pipe { case (lb, ub) => vs(tv) match { case VarianceInfo(true, true) => TypeBounds.mk(BotType, TopType) case VarianceInfo(false, false) => // * FIXME: this usage of type bounds is wrong! // * We're here using it as though it meant a bounded wildcard, // * for the purpose of type pretty-printing... // * But this is inconsistent with other uses of these types as *absolute* type ranges! TypeBounds.mk(lb, ub) // * However, the fix is to make all TR arguments actual bounded wildcards // * which is not easy as it requires extensive refactoring // * // * Note that the version below doesn't work because the refinement redundancy tests // * below require non-polar types to compare against, so TypeBounds is inadequate. /* pol match { case N => ??? TypeBounds.mk(lb, ub) case S(true) => TypeBounds.mk(lb, ub) case S(false) => TypeBounds.mk(ub, lb) } */ // * FIXME In fact, the use of such subtyping checks should render // * all uses of TypeBounds produced by the simplifier inadequate! // * We should find a proper solution to this at some point... // * (Probably by only using proper wildcards in the type simplifier.) case VarianceInfo(co, contra) => if (co) ub else lb } } })(noProv) println(s"typeRef ${typeRef}") val clsFields = fieldsOf(typeRef.expandWith(paramTags = true, selfTy = false), paramTags = true) println(s"clsFields ${clsFields.mkString(", ")}") val cleanPrefixes = ps.map(_.name.capitalize) + clsNme ++ traitPrefixes val cleanedRcd = RecordType( rcd2.fields.filterNot { case (field, fty) => // * This is a bit messy, but was the only way I was able to achieve maximal simplification: // * We remove fields that are already inclued by definition of the class by testing for subtyping // * with BOTH the new normalized type (from `clsFields`) AND the old one too (from `rcdMap`). // * The reason there's a difference is probably because: // * - Subtye checking with <:< is an imperfect heuristic and may stop working after normalizing. // * - Recursive types will be normalized progressively... // * at this point we may look at some bounds that have not yet been normalized. clsFields.get(field).exists(cf => cf <:< fty || rcdMap.get(field).exists(cf <:< _)) } )(rcd2.prov) val rcd2Fields = rcd2.fields.unzip._1.toSet // * Which fields were NOT part of the original type, // * and should therefore be excluded from the reconstructed TypeRef: val removedFields = clsFields.keysIterator .filterNot(field => field.name.isCapitalized || rcd2Fields.contains(field)).toSortedSet val withoutType = if (removedFields.isEmpty) typeRef else typeRef.without(removedFields) // * Whether we need a `with` (which overrides field types) // * as opposed to simply an intersection (which refines them): val needsWith = !rcd2.fields.forall { case (field, fty) => clsFields.get(field).forall(cf => fty <:< cf || rcdMap.get(field).exists(_ <:< cf)) } val withType = if (needsWith) if (cleanedRcd.fields.isEmpty) withoutType else WithType(withoutType, cleanedRcd.sorted)(noProv) else typeRef & cleanedRcd.sorted val withTraits = tts.toArray.sorted // TODO also filter out tts that are inherited by the class .foldLeft(withType: ST)(_ & _) val trs3 = trs2 - td.nme // TODO also filter out class refs that are inherited by the class trs3.valuesIterator.foldLeft(withTraits)(_ & _) case S(cls @ ClassTag(Var(clsNme), ps)) if !primitiveTypes.contains(clsNme) && ctx.tyDefs2.contains(clsNme) && ctx.tyDefs2(clsNme).result.isDefined => val clsTyNme = TypeName(clsNme) val lti = ctx.tyDefs2(clsNme) val cls = lti.result match { case S(r: TypedNuCls) => r case _ => die } val rcdMap = rcd.fields.toMap val rcd2 = rcd.copy(rcd.fields.mapValues(_.update(go(_, pol.map(!_)), go(_, pol))))(rcd.prov) println(s"rcd2 ${rcd2}") // val vs = // // td.getVariancesOrDefault // // Map.empty[TV, VarianceInfo].withDefaultValue(VarianceInfo.in) // cls.variances // * Reconstruct a TypeRef from its current structural components val typeRef = TypeRef(cls.td.nme, cls.tparams.zipWithIndex.map { case ((tp, tv, vi), tpidx) => val fieldTagNme = tparamField(clsTyNme, tp) val fromTyRef = trs2.get(clsTyNme).map(_.targs(tpidx) |> { ta => FieldType(S(ta), ta)(noProv) }) fromTyRef.++(rcd2.fields.iterator.filter(_._1 === fieldTagNme).map(_._2)) .foldLeft((BotType: ST, TopType: ST)) { case ((acc_lb, acc_ub), FieldType(lb, ub)) => (acc_lb | lb.getOrElse(BotType), acc_ub & ub) }.pipe { case (lb, ub) => cls.varianceOf(tv) match { case VarianceInfo(true, true) => TypeBounds.mk(BotType, TopType) case VarianceInfo(false, false) => TypeBounds.mk(lb, ub) case VarianceInfo(co, contra) => if (co) ub else lb } } })(noProv) println(s"typeRef ${typeRef}") val clsFields = fieldsOf(typeRef.expandWith(paramTags = true, selfTy = false), paramTags = true) println(s"clsFields ${clsFields.mkString(", ")}") val cleanPrefixes = ps.map(_.name.capitalize) + clsNme ++ traitPrefixes val cleanedRcd = RecordType( rcd2.fields.filterNot { case (field, fty) => // * This is a bit messy, but was the only way I was able to achieve maximal simplification: // * We remove fields that are already inclued by definition of the class by testing for subtyping // * with BOTH the new normalized type (from `clsFields`) AND the old one too (from `rcdMap`). // * The reason there's a difference is probably because: // * - Subtye checking with <:< is an imperfect heuristic and may stop working after normalizing. // * - Recursive types will be normalized progressively... // * at this point we may look at some bounds that have not yet been normalized. clsFields.get(field).exists(cf => cf <:< fty || rcdMap.get(field).exists(cf <:< _)) } )(rcd2.prov) val rcd2Fields = rcd2.fields.unzip._1.toSet val withTraits = tts.toArray.sorted // TODO also filter out tts that are inherited by the class .foldLeft(typeRef & cleanedRcd: ST)(_ & _) val trs3 = trs2 - cls.nme // TODO also filter out class refs that are inherited by the class trs3.valuesIterator.foldLeft(withTraits)(_ & _) case _ => lazy val nFields = rcd.fields .filterNot(traitPrefixes contains _._1.name.takeWhile(_ =/= '#')) .mapValues(_.update(go(_, pol.map(!_)), go(_, pol))) val (res, nfs) = bo match { case S(tt @ TupleType(fs)) => val arity = fs.size val (componentFields, rcdFields) = rcd.fields .filterNot(traitPrefixes contains _._1.name.takeWhile(_ =/= '#')) .partitionMap(f => f._1.toIndexOption.filter((0 until arity).contains).map(_ -> f._2).toLeft(f)) val componentFieldsMap = componentFields.toMap val tupleComponents = fs.iterator.zipWithIndex.map { case ((nme, ty), i) => nme -> (ty && componentFieldsMap.getOrElse(i, TopType.toUpper(noProv))).update(go(_, pol.map(!_)), go(_, pol)) }.toList S(TupleType(tupleComponents)(tt.prov)) -> rcdFields.mapValues(_.update(go(_, pol.map(!_)), go(_, pol))) case S(ct: ClassTag) => S(ct) -> nFields case S(ft @ FunctionType(l, r)) => S(FunctionType( go(l, pol.map(!_)), go(r, pol, canDistribForall = canDistribForall.orElse(Option.when(dnf.isPolymorphic)(dnf.polymLevel))) )(ft.prov)) -> nFields case S(ot @ Overload(alts)) => S(ot.mapAltsPol(pol)((p, t) => go(t, p))) -> nFields case S(at @ ArrayType(inner)) => S(ArrayType(inner.update(go(_, pol.map(!_)), go(_, pol)))(at.prov)) -> nFields case S(sp @ SpliceType(elems)) => S(sp.updateElems(go(_, pol), go(_, pol.map(!_)), go(_, pol))) -> nFields case S(wt @ Without(b: ComposedType, ns @ EmptyColl())) => S(Without(b.map(go(_, pol)), ns)(wt.prov)) -> nFields // FIXME very hacky case S(wt @ Without(b, ns)) => S(Without(go(b, pol), ns)(wt.prov)) -> nFields case N => N -> nFields } LhsRefined(res, tts, rcd.copy(nfs)(rcd.prov).sorted, trs2).toType(sort = true) } case LhsTop => TopType }, { case RhsBot => BotType case RhsField(n, t) => RecordType(n -> t.update(go(_, pol.map(!_)), go(_, pol)) :: Nil)(noProv) case RhsBases(ots, rest, trs) => // Note: could recosntruct class tags for these, but it would be pretty verbose, // as in showing `T & ~C[?] & ~D[?, ?]` instead of just `T & ~c & ~d` // ots.map { case t @ (_: ClassTag | _: TraitTag) => ... } val r = rest match { case v @ S(R(RhsField(n, t))) => RhsField(n, t.update(go(_, pol.map(!_)), go(_, pol))).toType(sort = true) case v @ S(L(bty)) => go(bty, pol) case N => BotType } trs.valuesIterator.map(go(_, pol)).foldLeft(BotType: ST)(_ | _) | ots.sorted.foldLeft(r)(_ | _) }, sort = true) }.foldLeft(BotType: ST)(_ | _) |> factorize(ctx) val res = otherCs2 | csNegs2 val cons = dnf.cons.map { case (lo,hi) => (go(lo,S(true)), go(hi,S(false))) } val base = ConstrainedType.mk(cons, res) canDistribForall match { case S(outerLvl) if distributeForalls => implicit val ctx: Ctx = _ctx.copy(lvl = outerLvl + 1) PolymorphicType(dnf.polymLevel, base).instantiate case _ => PolymorphicType.mk(dnf.polymLevel, base) } } def goLike(ty: TL, pol: Opt[Bool], canDistribForall: Opt[Level] = N): TL = trace(s"normLike[${printPol(pol)}] $ty") { ty match { case ty: ST => go(ty, pol) case OtherTypeLike(tu) => tu.mapPol(pol, true)((p, t) => go(t, p)) }}() def go(ty: ST, pol: Opt[Bool], canDistribForall: Opt[Level] = N): ST = trace(s"norm[${printPol(pol)}] $ty") { pol match { case S(p) => helper(DNF.mk(MaxLevel, Nil, ty, p)(ctx, ptr = true, etf = false), pol, canDistribForall) case N => val dnf1 = DNF.mk(MaxLevel, Nil, ty, false)(ctx, ptr = true, etf = false) val dnf2 = DNF.mk(MaxLevel, Nil, ty, true)(ctx, ptr = true, etf = false) TypeBounds.mk(helper(dnf1, S(false), canDistribForall), helper(dnf2, S(true), canDistribForall)) } }(r => s"~> $r") def processVar(tv: TV): Unit = { processed.setAndIfUnset(tv) { tv.assignedTo match { case S(ty) => tv.assignedTo = S(go(ty, N)) case N => // tv.lowerBounds = tv.lowerBounds.map(go(_, S(true))) // tv.upperBounds = tv.upperBounds.map(go(_, S(false))) tv.lowerBounds = tv.lowerBounds.reduceOption(_ | _).fold(nil[ST])(go(_, S(true)) :: Nil) tv.upperBounds = tv.upperBounds.reduceOption(_ & _).fold(nil[ST])(go(_, S(false)) :: Nil) } } } goLike(st, pol) } /** Remove polar type variables, unify indistinguishable ones, and inline the bounds of non-recursive ones. */ def simplifyType(st: TypeLike, removePolarVars: Bool, pol: Opt[Bool], inlineBounds: Bool = true)(implicit ctx: Ctx): TypeLike = { // * There are two main analyses, which are quite subtle. // * TODO: add assertion to check that their results are consistent! // * * Analysis 1: count number of TV occurrences at each polarity // * and find whether they're used in invariant positions // * (in which case we won't inline their bounds, to avoid producing ugly type intervals in the final result). val occNums: MutMap[(Bool, TypeVariable), Int] = LinkedHashMap.empty[(Bool, TypeVariable), Int].withDefaultValue(0) val occursInvariantly = MutSet.empty[TV] val analyzed1 = MutSet.empty[PolarVariable] // * Note: it is important here to make sure the interpretation of invariant position // * coincides with that of the later `transform` function. // * In particular, the traversal of fields with identical UB/LB is considered invariant. object Analyze1 extends Traverser2.InvariantFields { override def apply(pol: PolMap)(st: ST): Unit = trace(s"analyze1[${(pol)}] $st") { st match { case tv: TV => pol(tv) match { case S(pol) => occNums(pol -> tv) += 1 case N => occursInvariantly += tv occNums(true -> tv) += 1 occNums(false -> tv) += 1 } tv.assignedTo match { case S(ty) => // * This is quite subtle! // * We should traverse assigned type variables as though they weren't there, // * but they may appear in their own assignment, // * so we still need to check they haven't been traversed yet. // * Moreover, traversing them at different polarities may produce different results // * (think of `'A# -> 'A#` where 'A# := 'X`), // * so we should remember the traversal polarity in the cache. // * Thanks to the invariant that the assignment shouldn't have a higher level than // * the type variable itself, I think it is fine to never re-traverse the assignment // * at the same polarity *even though the polmap may be different*. analyzed1.setAndIfUnset(tv -> pol(tv).getOrElse(false)) { apply(pol)(ty) } case N => if (pol(tv) =/= S(false)) analyzed1.setAndIfUnset(tv -> true) { tv.lowerBounds.foreach(apply(pol.at(tv.level, true))) if (noApproximateOverload) tv.tsc.keys.flatMap(_.tvs).foreach(u => apply(pol.at(tv.level,u._1))(u._2)) } if (pol(tv) =/= S(true)) analyzed1.setAndIfUnset(tv -> false) { tv.upperBounds.foreach(apply(pol.at(tv.level, false))) if (noApproximateOverload) tv.tsc.keys.flatMap(_.tvs).foreach(u => apply(pol.at(tv.level,u._1))(u._2)) } } case _ => super.apply(pol)(st) } }() } Analyze1.applyLike(PolMap(pol))(st) println(s"[inv] ${occursInvariantly.mkString(", ")}") println(s"[nums] ${occNums.iterator .map(occ => s"${printPol(S(occ._1._1))}${occ._1._2} ${occ._2}") .mkString(" ; ") }") // * * Analysis 2: find the polar co-occurrences of each TV // * Note: for negatively-quantified vars, the notion of co-occurrence is reversed (wrt unions/inters)... val coOccurrences: MutMap[(Bool, TypeVariable), LinkedHashSet[SimpleType]] = MutMap.empty // * Remember which TVs we analyzed at which polarity val analyzed2 = MutSet.empty[Bool -> ST] // * The above is not enough. Sometimes we want to analyze a group of bounds and co-occurring types altogether. // * However, the TV component of these types may have polarities that change depending on the context! // * So we can't just assign a polarity to th ewhole thing... // * Instead, we remember the polarities of each of these type subexpressions. val analyzed22 = MutSet.empty[Set[Opt[Bool] -> ST]] // ********************** // * An alternative approach that tries to learn more co-occurrence info by // * accumulating the effective bounds on type vriables // * (ie including those not specified in the TV itself). // * Commented because this still needs work before it can be used (Q: should it be?). // ********************** /* val lbs, ubs = MutMap.empty[TV, MutSet[ST]] def getLbs(tv: TV) = lbs.getOrElseUpdate(tv, MutSet.empty) def getUbs(tv: TV) = ubs.getOrElseUpdate(tv, MutSet.empty) def getBounds(ty: ST, pol: Bool): Set[ST] = { val traversed = MutSet.empty[TV] def go(ty: ST): Set[ST] = ty.unwrapProxies match { // TODO(later) guard against bad rec types? case tv2: TV => if (traversed.add(tv2)) tv2.assignedTo match { case S(ty2) => go(ty2) + tv2 case N => Set.single(tv2) ++ (if (pol) tv2.lowerBounds else tv2.upperBounds).iterator.flatMap(go) } else Set.empty case ComposedType(p, l, r) => if (p === pol) go(l) ++ go(r) else Set.single(ty) ++ (go(l) & go(r)) case _ => Set.single(ty) } go(ty) } analyzed1.foreach { case (tv, pol) => val bs = getBounds(tv, pol) println(tv,pol,bs) (if (pol) getLbs(tv) else getUbs(tv)) ++= bs.iterator.filterNot(_ is tv) bs.foreach { case tv2: TV if tv2 isnt tv => (if (pol) getUbs(tv2) else getLbs(tv2)) += tv case _ => } } println(s"Bounds:") s"${lbs.foreach { case (tv, bs) => println(s" $tv") bs.foreach(b => println(s"\t:> $b")) }}" s"${ubs.foreach { case (tv, bs) => println(s" $tv") bs.foreach(b => println(s"\t<: $b")) }}" */ // FIXME Currently we don't traverse TVs witht he correct PolMap, which introduces misatches with other analyses in tricky cases def analyze2(st: TL, pol: PolMap): Unit = Analyze2.applyLike(pol)(st.unwrapProvs) object Analyze2 extends Traverser2 { override def apply(pol: PolMap)(st: ST): Unit = trace(s"analyze2[${(pol)}] $st") { st match { case tv: TypeVariable => pol(tv) match { case S(pol_tv) => if (analyzed2.add(pol_tv -> tv)) { processImpl(st, pol, pol_tv) if (noApproximateOverload) tv.tsc.keys.flatMap(_.tvs).foreach(u => processImpl(u._2,pol.at(tv.level,u._1),pol_tv)) } case N => if (analyzed2.add(true -> tv)) // * To compute the positive co-occurrences processImpl(st, pol.at(tv.level, true), true) if (analyzed2.add(false -> tv)) // * To compute the negative positive co-occurrences processImpl(st, pol.at(tv.level, false), false) } case ct: ComposedType => def getComponents(ty: ST): Set[Opt[Bool] -> ST] = ty.unwrapProxies match { case tv: TV => Set.single(pol(tv) -> tv) case ty @ ComposedType(p, l, r) => if (p === ct.pol) getComponents(l) ++ getComponents(r) else Set.single(N -> ty) case _ => Set.single(N -> ty) } val comps = getComponents(ct) println(s"Components $comps") if (analyzed22.add(comps)) processImpl(st, pol, ct.pol) else println(s"Found in $analyzed22") case _ => super.apply(pol)(st) } }() } def processImpl(st: SimpleType, pol: PolMap, occPol: Bool) = { val newOccs = LinkedHashSet.empty[SimpleType] println(s">> Processing $st at [${printPol(S(occPol))}]") def go(ty: ST): Unit = trace(s"go $ty (${newOccs.mkString(", ")})") { ty.unwrapProxies match { case tv2: TV => // println(s"${printPol(pol(tv2))}$tv2") if (newOccs.add(tv2)) tv2.assignedTo match { case S(ty2) => go(ty2) case N => pol(tv2) match { case S(p) => (if (p) tv2.lowerBounds else tv2.upperBounds).foreach(go) // (if (p) getLbs(tv2) else getUbs(tv2)).foreach(go) if (noApproximateOverload) tv2.tsc.keys.flatMap(_.tvs).foreach(u => go(u._2)) case N => trace(s"Analyzing invar-occ of $tv2") { analyze2(tv2, pol) }() } } case ComposedType(p, l, r) if p === occPol => go(l); go(r) case _ => newOccs += ty; () }}() go(st) println(s">> Occurrences $newOccs") newOccs.foreach { case tv: TypeVariable => def occ(pol: Bool): Unit = { val occs = if (pol === occPol) newOccs else MutSet.single[ST](tv) println(s">>>> occs[${printPol(pol)}$tv] := $occs <~ ${coOccurrences.get(pol -> tv)}") coOccurrences.get(pol -> tv) match { case Some(os) => // Q: filter out vars of different level? os.filterInPlace(occs) // computes the intersection case None => coOccurrences(pol -> tv) = LinkedHashSet.from(occs) // copy not needed? } } pol(tv) match { case S(p) => occ(p) case N => occ(true) occ(false) } case _ => () } newOccs.foreach { case tv: TypeVariable => () case ty => analyze2(ty, pol) } } analyze2(st, PolMap(pol)) coOccurrences.foreach(kv => assert(kv._2.nonEmpty)) println(s"[occs] ${coOccurrences.iterator .map(occ => s"${printPol(S(occ._1._1))}${occ._1._2} ${occ._2.mkString("{",",","}")}") .mkString(" ; ") }") // * * Processing: decide what type variables to remove/unify/inline bounds of. // * NOTE: This phase logically unifies type variables by merging their bounds and co-occurrence reults. // * In particular, it may change the recursive-ness of type variables! // * (We may unfy a non-rec TV with a rec one, makingthe non-rec TV recursive.) // * This will be filled during the processing phase, to guide the transformation phase: val varSubst = MutMap.empty[TypeVariable, Option[ST]] // val allVars = st.getVars val allVars = analyzed1.iterator.map(_._1).toSortedSet def computeRecVars = allVars.iterator.filter(v => !varSubst.contains(v) && ( v.isRecursive_$(omitIrrelevantVars = false) // * Note: a more precise version could be the following, // * but it doesn't seem to change anything in our test suite, so I left if commented for now: // // * Only consider recursive those variables that recursive in their *reachable* bounds: // occNums.contains(true -> v) && v.isPosRecursive_$(false) || occNums.contains(false -> v) && v.isNegRecursive_$(false) )).toSet var recVars = computeRecVars println(s"[vars] ${allVars}") println(s"[rec] ${recVars}") // println(s"[bounds] ${st.showBounds}") // * Remove polar type variables that only occur once, including if they are part of a recursive bounds graph: if (inlineBounds) occNums.iterator.foreach { case (k @ (pol, tv), num) => assert(num > 0) if (num === 1 && !occNums.contains(!pol -> tv)) { println(s"0[1] $tv") varSubst += tv -> None } } // * Simplify away those non-recursive variables that only occur in positive or negative positions // * (i.e., polar ones): allVars.foreach { case v0 => if (!recVars.contains(v0)) { (coOccurrences.get(true -> v0), coOccurrences.get(false -> v0)) match { case (Some(_), None) | (None, Some(_)) => println(s"1[!] $v0") varSubst += v0 -> None case occ => assert(occ =/= (None, None), s"$v0 has no occurrences...") } }} // * Remove variables that are 'dominated' by another type or variable // * A variable v dominated by T if T is in both of v's positive and negative cooccurrences allVars.foreach { case v => if (v.assignedTo.isEmpty && !varSubst.contains(v) && v.tsc.isEmpty) { println(s"2[v] $v ${coOccurrences.get(true -> v)} ${coOccurrences.get(false -> v)}") coOccurrences.get(true -> v).iterator.flatMap(_.iterator).foreach { case atom @ (_: BaseType | _: TypeRef) if !recVars(v) // can't reduce recursive sandwiches, obviously && coOccurrences.get(false -> v).exists(_(atom)) => val bundle = TypeBounds.mkSafe( v.upperBounds.foldLeft(atom)(_ &- _), v.lowerBounds.foldLeft(atom)(_ | _), ) println(s" [..] $v := ${bundle}") varSubst += v -> S(bundle) case w: TV if !(w is v) && !varSubst.contains(w) && !varSubst.contains(v) && !recVars(v) && coOccurrences.get(false -> v).exists(_(w)) && w.tsc.isEmpty => // * Here we know that v is 'dominated' by w, so v can be inlined. // * Note that we don't want to unify the two variables here // * – if v has bounds and does not dominate w, then doing so would be wrong. // * Logic to preserve name hints, but seems overkill and did not seem to have any effect so far: // if (coOccurrences.get(true -> w).exists(_(v)) && coOccurrences.get(false -> w).exists(_(v)) && v.nameHint.nonEmpty && !recVars(w)) { // println(s" [..] $w ${v}") // varSubst += w -> N // } else { val bundle = TypeBounds.mkSafe( v.upperBounds.foldLeft(w: ST)(_ &- _), v.lowerBounds.foldLeft(w: ST)(_ | _), ) println(s" [..] $v := ${bundle}") varSubst += v -> S(bundle) // } case _ => } }} // * Unify equivalent variables based on polar co-occurrence analysis: allVars.foreach { case v => if (!v.assignedTo.isDefined && !varSubst.contains(v) && v.tsc.isEmpty) // TODO also handle v.assignedTo.isDefined? trace(s"3[v] $v +${coOccurrences.get(true -> v).mkString} -${coOccurrences.get(false -> v).mkString}") { def go(pol: Bool): Unit = coOccurrences.get(pol -> v).iterator.flatMap(_.iterator).foreach { case w: TypeVariable if !(w is v) && !w.assignedTo.isDefined && !varSubst.contains(w) //&& (if (pol) ) // && (recVars.contains(v) === recVars.contains(w)) // * ^ Note: We no longer avoid merging rec and non-rec vars, // * even though the non-rec one may not be strictly polar (as an example of this, see [test:T1]). // * We may introduce recursive types anyway so the `recVars` info here is no longer up to date. && (v.nameHint.nonEmpty || w.nameHint.isEmpty // * ^ Don't merge in this direction if that would override a nameHint || varSubst.contains(v) // * we won't be able to do it the other way // || varSubst.contains(v) ) && (v.level === w.level) // ^ Don't merge variables of differing levels && w.tsc.isEmpty => trace(s"[w] $w ${printPol(S(pol))}${coOccurrences.get(pol -> w).mkString}") { // * The bounds opposite to the polarity where the two variables co-occur // * We don't want to merge variables when this would introduce val otherBounds = (if (pol) v.upperBounds else v.lowerBounds) val otherBounds2 = (if (pol) w.upperBounds else w.lowerBounds) // if (otherBounds =/= otherBounds2) if (otherBounds.toSet =/= otherBounds2.toSet) // * ^ This is a bit of an ugly heuristic to simplify a couple of niche situations... // * More principled/regular approaches could be to either: // * 1. just not merge things with other bounds; or // * 2. merge non-recursive things with other bounds but be more careful in the `transform` part // * that we don't import these other bounds when inlining bounds... // * Choice (2.) seems tricky to implement. println(s"$v and $w have non-equal other bounds and won't be merged") else if (coOccurrences.get(pol -> w).forall(_(v))) { // * Unify w into v println(s" [U] $w := $v") varSubst += w -> S(v) // * Since w gets unified with v, we need to merge their bounds, // * and also merge the other co-occurrences of v and w from the other polarity (!pol). // * For instance, // * consider that if we merge v and w in `(v & w) -> v & x -> w -> x` // * we get `v -> v & x -> v -> x` // * and the old positive co-occ of v, {v,x} should be changed to just {v,x} & {w,v} == {v}! v.lowerBounds :::= w.lowerBounds v.upperBounds :::= w.upperBounds // TODO when we have `assignedTo` use that instead here? w.lowerBounds = v :: Nil w.upperBounds = v :: Nil // * When removePolarVars is enabled, wCoOcss/vCoOcss may not be defined: coOccurrences.get((!pol) -> w).foreach { wCoOccs => coOccurrences.get((!pol) -> v) match { case S(vCoOccs) => vCoOccs.filterInPlace(t => t === v || wCoOccs(t)) case N => coOccurrences((!pol) -> v) = wCoOccs } } } }() case _ => } go(true) go(false) }() } println(s"[sub] ${varSubst.map(k => k._1.toString + " -> " + k._2).mkString(", ")}") println(s"[bounds] ${st.showBounds}") // * * Transformation: transform the type recursively, // * applying the var substitution and simplifying some things on the fly. // * The recursive vars may have changed due to the previous phase! recVars = computeRecVars println(s"[rec] ${recVars}") val renewals = MutMap.empty[TypeVariable, TypeVariable] val renewaltsc = MutMap.empty[TupleSetConstraints, TupleSetConstraints] val semp = Set.empty[TV] def mergeTransform(pol: Bool, polmap: PolMap, tv: TV, parents: Set[TV], canDistribForall: Opt[Level]): ST = transform(tv.assignedTo match { case S(ty) => ty case N => merge(pol, if (pol) tv.lowerBounds else tv.upperBounds) }, polmap.at(tv.level, pol), parents, canDistribForall) def transformLike(ty: TL, pol: PolMap): TL = ty match { case ty: ST => transform(ty, pol, semp) case OtherTypeLike(tu) => tu.mapPolMap(pol)((p,t) => transform(t, p, semp)) } def transform(st: SimpleType, pol: PolMap, parents: Set[TV], canDistribForall: Opt[Level] = N): SimpleType = trace(s"transform[${printPol(pol)}] $st (${parents.mkString(", ")}) $pol $canDistribForall") { def transformField(f: FieldType): FieldType = f match { case FieldType(S(lb), ub) if lb === ub => val b = transform(ub, pol.invar, semp) FieldType(S(b), b)(f.prov) case _ => f.update(transform(_, pol.contravar, semp), transform(_, pol, semp)) } st match { case RecordType(fs) => RecordType(fs.mapValues(_ |> transformField))(st.prov) case TupleType(fs) => TupleType(fs.mapValues(_ |> transformField))(st.prov) case ArrayType(inner) => ArrayType(inner |> transformField)(st.prov) case sp @ SpliceType(elems) => SpliceType(elems map { case L(l) => L(transform(l, pol, semp)) case R(r) => R(transformField(r))})(st.prov) case FunctionType(l, r) => FunctionType(transform(l, pol.contravar, semp), transform(r, pol, semp, canDistribForall))(st.prov) case ot @ Overload(as) => ot.mapAltsPol(pol)((p, t) => transform(t, p, parents, canDistribForall)) case SkolemTag(id) => transform(id, pol, parents) case _: ObjectTag | _: Extruded | _: ExtrType => st case tv: TypeVariable if parents(tv) => pol(tv) match { case S(true) => BotType case S(false) => TopType case N => transform(tv, pol, parents - tv) } case tv: TypeVariable => varSubst.get(tv) match { case S(S(tv2)) => println(s"-> $tv2") transform(tv2, pol, parents + tv, canDistribForall) case S(N) => println(s"-> bound ${pol(tv)}") val p = pol(tv) if (!removePolarVars && ( tv.lowerBounds.isEmpty && p.contains(true) || tv.upperBounds.isEmpty && p.contains(false) || tv.lowerBounds.isEmpty && tv.upperBounds.isEmpty )) ClassTag(Var("?"), Set.empty)(noProv) else p.fold { // TypeBounds.mk(mergeTransform(true, tv, parents + tv), mergeTransform(false, tv, parents + tv)) // FIXME polarities seem inverted lastWords("Should not be replacing an invariant type variable by its bound...") // ? pol.quantifPolarity(tv.level).base match { case S(true) => TypeBounds.mkSafe(mergeTransform(false, pol, tv, parents + tv, canDistribForall), mergeTransform(true, pol, tv, parents + tv, canDistribForall)) case S(false) => TypeBounds.mkSafe(mergeTransform(true, pol, tv, parents + tv, canDistribForall), mergeTransform(false, pol, tv, parents + tv, canDistribForall)) case N => ??? } }(mergeTransform(_, pol, tv, parents + tv, canDistribForall)) case N => var wasDefined = true val res = renewals.getOrElseUpdate(tv, { wasDefined = false val nv = freshVar(noProv, S(tv), tv.nameHint)(tv.level) println(s"Renewed $tv ~> $nv") nv }) pol(tv) match { case S(p) if inlineBounds && !occursInvariantly(tv) && !recVars.contains(tv) && tv.tsc.isEmpty => // * Inline the bounds of non-rec non-invar-occ type variables println(s"Inlining [${printPol(p)}] bounds of $tv (~> $res)") // if (p) mergeTransform(true, pol, tv, Set.single(tv), canDistribForall) | res // else mergeTransform(false, pol, tv, Set.single(tv), canDistribForall) & res if (p) mergeTransform(true, pol, tv, parents + tv, canDistribForall) | res else mergeTransform(false, pol, tv, parents + tv, canDistribForall) & res case poltv if (!wasDefined) => def setBounds = { trace(s"Setting [±] bounds of $res... (failing ${printPol(poltv)}, inlineBounds $inlineBounds, !occursInvariantly ${!occursInvariantly(tv)}, !recVars.contains(tv) ${!recVars.contains(tv)})") { tv.assignedTo match { case S(ty) => res.assignedTo = S(transform(ty, pol.invar, semp, canDistribForall)) case N => if (occNums.contains(true -> tv)) res.lowerBounds = tv.lowerBounds.map(transform(_, pol.at(tv.level, true), Set.single(tv))) if (occNums.contains(false -> tv)) res.upperBounds = tv.upperBounds.map(transform(_, pol.at(tv.level, false), Set.single(tv))) if (noApproximateOverload) res.tsc ++= tv.tsc.map { case (tsc, i) => renewaltsc.get(tsc) match { case S(tsc) => (tsc, i) case N => val t = new TupleSetConstraints(tsc.constraints, tsc.tvs) renewaltsc += tsc -> t t.tvs = t.tvs.map(x => (x._1, transform(x._2, PolMap.neu, Set.empty))) (t, i) }} } res }() } poltv match { case polo @ S(p) if !coOccurrences.contains(!p -> tv) // * If tv is polar... && tv.assignedTo.isEmpty // TODO handle? => val bounds = if (p) tv.lowerBounds else tv.upperBounds // * only true if we do a pass of `removeIrrelevantBounds` before calling `simplifyType`: // assert(tv.lowerBounds.isEmpty || tv.upperBounds.isEmpty, (tv, tv.lowerBounds, tv.upperBounds)) // println(s">?> $tv $res $bounds ${tv.lowerBounds} ${tv.upperBounds}") if (bounds.forall { // * If tv only has type variables as upper bounds, inline it case tv2: TV => varSubst.get(tv2).forall(_.isDefined) case _ => false }) { println(s"NEW SUBS $tv -> N") varSubst += tv -> N transform(merge(p, bounds), pol, parents + tv, canDistribForall) } else setBounds case _ => setBounds } case _ => res } } case ty @ ComposedType(true, l, r) => transform(l, pol, parents, canDistribForall) | transform(r, pol, parents, canDistribForall) case ty @ ComposedType(false, l, r) => transform(l, pol, parents, canDistribForall) & transform(r, pol, parents, canDistribForall) case NegType(und) => transform(und, pol.contravar, semp).neg() case WithType(base, RecordType(fs)) => WithType(transform(base, pol, semp, canDistribForall), RecordType(fs.mapValues(_.update(transform(_, pol.contravar, semp), transform(_, pol, semp))))(noProv))(noProv) case ProxyType(underlying) => transform(underlying, pol, parents, canDistribForall) case tr @ TypeRef(defn, targs) => TypeRef(defn, tr.mapTargs(pol)((pol, ty) => transform(ty, pol, semp)))(tr.prov) case wo @ Without(base, names) => if (names.isEmpty) transform(base, pol, semp, canDistribForall) else if (pol.base === S(true)) transform(base, pol, semp, canDistribForall).withoutPos(names) else transform(base, pol, semp, canDistribForall).without(names) case tb @ TypeBounds(lb, ub) => pol.base.fold[ST](TypeBounds.mkSafe( transform(lb, PolMap.neg, parents, canDistribForall), transform(ub, PolMap.pos, parents, canDistribForall), // transform(lb, pol, parents, canDistribForall), // transform(ub, pol, parents, canDistribForall), // transform(lb, pol.contravar, parents, canDistribForall), // transform(ub, pol.covar, parents, canDistribForall), noProv ))(p => if (p) transform(ub, pol, parents) else transform(lb, pol, parents) ) case PolymorphicType(plvl, bod) => val res = transform(bod, pol.enter(plvl), parents, canDistribForall = S(plvl)) canDistribForall match { case S(outerLvl) if distributeForalls => ctx.copy(lvl = outerLvl + 1) |> { implicit ctx => PolymorphicType(plvl, res).instantiate } case _ => PolymorphicType.mk(plvl, res) } case ConstrainedType(cs, bod) => ConstrainedType( cs.map { case (lo, hi) => (transform(lo, PolMap.pos, semp), transform(hi, PolMap.posAtNeg, semp)) }, transform(bod, pol, parents) ) } }(r => s"~> $r") transformLike(st, PolMap(pol)) } /** Remove recursive types that have 'skidded' across several type variables * due to the (crucially important) type variable bounds propagation logic of the constraint solver. * For example, when `?a :> ?b` and we constrain `?a if (pol) (true, ty) -> tv else (false, ty) -> tv case (tv, S(pol)) => if (pol) (true, tv.lowerBounds.foldLeft(BotType: ST)(_ | _)) -> tv else (false, tv.upperBounds.foldLeft(TopType: ST)(_ &- _)) -> tv }.filter { case ((pol, bnd), tv) => bnd.getVarsImpl(includeBounds = false).contains(tv) }.toMap println(s"consed: $consed") // * Q: shouldn't this use a PolMap instead? // * It should actually be sound not to, though, as the handling is symmetric... def process(pol: Opt[Bool], st: ST, parent: Opt[TV]): ST = // trace(s"cons[${printPol(pol)}] $st") { st.unwrapProvs match { case tv @ AssignedVariable(ty) => processed.setAndIfUnset(tv) { // tv.assignedTo = S(process(pol, ty, S(tv))) // * WRONG! tv.assignedTo = S(process(N, ty, S(tv))) } tv case tv: TV => processed.setAndIfUnset(tv) { tv.lowerBounds = tv.lowerBounds.map(process(S(true), _, S(tv))) tv.upperBounds = tv.upperBounds.map(process(S(false), _, S(tv))) } tv case _ => lazy val mapped = st.mapPol(pol, smart = true)(process(_, _, parent)) // println(s"mapped $mapped") pol match { case S(p) => // println(s"!1! ${st} ${consed.get(p -> st)}") consed.get(p -> st) match { case S(tv) if parent.forall(_ isnt tv) => println(s"!unskid-1! ${st} -> $tv") process(pol, tv, parent) case _ => // println(s"!2! ${mapped} ${consed.get(p -> mapped)}") consed.get(p -> mapped) match { case S(tv) if parent.forall(_ isnt tv) => println(s"!unskid-2! ${mapped} -> $tv") process(pol, tv, parent) case _ => mapped } } case N => mapped } } // }(r => s"~> $r") def processLike(pol: Opt[Bool], ty: TL): TL = ty match { case ty: ST => process(pol, ty, N) case OtherTypeLike(tu) => tu.mapPol(pol, smart = true)(process(_, _, N)) } processLike(S(pol), st) } /** Unify polar recursive type variables that have the same structure. * For example, `?a <: {x: ?a}` and `?b <: {x: ?b}` will be unified if they are bith polar. */ def factorRecursiveTypes_!(st: TypeLike, approximateRecTypes: Bool, pol: Opt[Bool])(implicit ctx: Ctx): TypeLike = { val allVarPols = st.getVarsPol(PolMap(pol)) println(s"allVarPols: ${printPols(allVarPols)}") val processed = MutSet.empty[TV] val varSubst = MutMap.empty[TV, TV] allVarPols.foreach { case (tv1, S(p1)) => println(s"Consider $tv1") (tv1.assignedTo.map(_::Nil).getOrElse(if (p1) tv1.lowerBounds else tv1.upperBounds)) match { case b1 :: Nil => allVarPols.foreach { case (tv2, S(p2)) if p2 === p1 && (tv2 isnt tv1) && !varSubst.contains(tv1) && !varSubst.contains(tv2) => (tv2.assignedTo.map(_::Nil).getOrElse(if (p2) tv2.lowerBounds else tv2.upperBounds)) match { case b2 :: Nil => // TODO could be smarter, using sets of assumed equalities instead of just one: def unify(ty1: ST, ty2: ST): Bool = { def nope: false = { println(s"Nope(${ty1.getClass.getSimpleName}): $ty1 ~ $ty2"); false } def unifyF(f1: FieldType, f2: FieldType): Bool = (f1, f2) match { case (FieldType(S(l1), u1), FieldType(S(l2), u2)) => unify(l1, l2) && unify(u1, u2) case (FieldType(N, u1), FieldType(N, u2)) => unify(u1, u2) case _ => nope } (ty1, ty2) match { case (`tv1`, `tv2`) | (`tv2`, `tv1`) => true case (v1: TypeVariable, v2: TypeVariable) => (v1 is v2) || nope case (NegType(negated1), NegType(negated2)) => unify(negated1, negated2) case (ClassTag(id1, parents1), ClassTag(id2, parents2)) => id1 === id2 || nope case (ArrayType(inner1), ArrayType(inner2)) => unifyF(inner1, inner2) case (TupleType(fields1), TupleType(fields2)) => (fields1.size === fields2.size || nope) && fields1.map(_._2).lazyZip(fields2.map(_._2)).forall(unifyF) case (FunctionType(lhs1, rhs1), FunctionType(lhs2, rhs2)) => unify(lhs1, lhs2) && unify(rhs1, rhs2) case (Without(base1, names1), Without(base2, names2)) => unify(base1, base2) && (names1 === names2 || nope) case (TraitTag(id1, _), TraitTag(id2, _)) => id1 === id2 || nope case (SkolemTag(id1), SkolemTag(id2)) => id1 === id2 || nope case (ExtrType(pol1), ExtrType(pol2)) => pol1 === pol2 || nope case (TypeBounds(lb1, ub1), TypeBounds(lb2, ub2)) => unify(lb1, lb2) && unify(ub1, ub2) case (ComposedType(pol1, lhs1, rhs1), ComposedType(pol2, lhs2, rhs2)) => (pol1 === pol2 || nope) && unify(lhs1, lhs2) && unify(rhs1, rhs2) case (RecordType(fields1), RecordType(fields2)) => fields1.size === fields2.size && fields1.lazyZip(fields2).forall((f1, f2) => (f1._1 === f2._1 || nope) && unifyF(f1._2, f2._2)) case (WithType(base1, rcd1), WithType(base2, rcd2)) => unify(base1, base2) && unify(rcd1, rcd2) case (ProxyType(underlying1), _) => unify(underlying1, ty2) case (_, ProxyType(underlying2)) => unify(ty1, underlying2) case (TypeRef(defn1, targs1), TypeRef(defn2, targs2)) => (defn1 === defn2 || nope) && targs1.lazyZip(targs2).forall(unify) case _ => nope } } println(s"Consider $tv1 ~ $tv2") if (unify(b1, b2)) { println(s"Yes! $tv2 := $tv1") varSubst += tv2 -> tv1 } case _ => } case _ => } case _ => } case _ => } println(s"[subs] ${varSubst}") if (varSubst.nonEmpty) substLike(st, varSubst.toMap, substInMap = true) else st } abstract class SimplifyPipeline { def debugOutput(msg: => Str): Unit def apply(st: TL, pol: Opt[Bool], removePolarVars: Bool = true)(implicit ctx: Ctx): TypeLike = { var cur = st debugOutput(s"⬤ Initial: ${cur}") debugOutput(s" where: ${cur.showBounds}") cur = removeIrrelevantBounds(cur, pol, reverseBoundsOrder = true, // bounds are accumulated by type inference in reverse order of appearance; so nicer to reverse them here inPlace = false) debugOutput(s"⬤ Cleaned up: ${cur}") debugOutput(s" where: ${cur.showBounds}") pol.foreach(pol => cur = unskidTypes_!(cur, pol)) debugOutput(s"⬤ Unskid: ${cur}") debugOutput(s" where: ${cur.showBounds}") cur = simplifyType(cur, removePolarVars, pol) debugOutput(s"⬤ Type after simplification: ${cur}") debugOutput(s" where: ${cur.showBounds}") // * Has a very small (not worth it?) positive effect here: // cur = factorRecursiveTypes_!(cur, approximateRecTypes = false) // debugOutput(s"⬤ Factored: ${cur}") // debugOutput(s" where: ${cur.showBounds}") cur = normalizeTypes_!(cur, pol) debugOutput(s"⬤ Normalized: ${cur}") debugOutput(s" where: ${cur.showBounds}") cur = removeIrrelevantBounds(cur, pol, reverseBoundsOrder = false, inPlace = true) debugOutput(s"⬤ Cleaned up: ${cur}") debugOutput(s" where: ${cur.showBounds}") pol.foreach(pol => cur = unskidTypes_!(cur, pol)) debugOutput(s"⬤ Unskid: ${cur}") debugOutput(s" where: ${cur.showBounds}") // * The DNFs introduced by `normalizeTypes_!` may lead more coocc info to arise // * by merging things like function types together... // * So we need another pass of simplification! cur = simplifyType(cur, removePolarVars, pol) debugOutput(s"⬤ Resim: ${cur}") debugOutput(s" where: ${cur.showBounds}") cur = factorRecursiveTypes_!(cur, approximateRecTypes = false, pol) debugOutput(s"⬤ Factored: ${cur}") debugOutput(s" where: ${cur.showBounds}") cur } } } ================================================ FILE: shared/src/main/scala/mlscript/Typer.scala ================================================ package mlscript import scala.collection.mutable import scala.collection.mutable.{Map => MutMap, Set => MutSet, SortedMap => MutSortMap, LinkedHashMap, LinkedHashSet, Buffer} import scala.collection.immutable.{SortedSet, SortedMap} import Set.{empty => semp} import scala.util.chaining._ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ /** A class encapsulating type inference state. * It uses its own internal representation of types and type variables, using mutable data structures. * Inferred SimpleType values are then turned into CompactType values for simplification. * In order to turn the resulting CompactType into a mlscript.Type, we use `expandCompactType`. */ class Typer(var dbg: Boolean, var verbose: Bool, var explainErrors: Bool, val newDefs: Bool) extends TypeDefs with TypeSimplifier { def funkyTuples: Bool = false def doFactorize: Bool = false def showAllErrors: Bool = false // TODO enable? def maxSuccessiveErrReports: Int = 3 var generalizeCurriedFunctions: Boolean = false var approximateNegativeFunction: Boolean = false var preciselyTypeRecursion: Bool = false var distributeForalls: Boolean = false var generalizeArguments: Boolean = false var noCycleCheck: Boolean = false var noRecursiveTypes: Boolean = false var irregularTypes: Boolean = false var constrainedTypes: Boolean = false var recordProvenances: Boolean = true var noApproximateOverload: Boolean = false type Binding = Str -> SimpleType type Bindings = Map[Str, SimpleType] type Level = Int val MinLevel: Int = 0 val MaxLevel: Int = 1024 type Pol = Opt[Bool] type GenLambdas >: Bool def doGenLambdas(implicit gl: GenLambdas): Bool = gl === true /** `env`: maps the names of all global and local bindings to their types * Keys of `mthEnv`: * `L` represents the inferred types of method definitions. The first value is the parent name, * and the second value is the method name. * `R` represents the actual method types. * The first optional value is the parent name, with `N` representing implicit calls, * and the second value is the method name. * (See the case for `Sel` in `typeTerm` for documentation on explicit vs. implicit calls.) * The public helper functions should be preferred for manipulating `mthEnv` */ case class Ctx( parent: Opt[Ctx], env: MutMap[Str, TypeInfo], mthEnv: MutMap[(Str, Str) \/ (Opt[Str], Str), MethodType], lvl: Int, quoteSkolemEnv: MutMap[Str, SkolemTag], // * SkolemTag for variables in quasiquotes freeVarsInCurrentQuote: LinkedHashSet[ST], // * Free variables appearing in the current quote scope inQuote: Bool, // * Is in quasiquote inPattern: Bool, tyDefs: Map[Str, TypeDef], tyDefs2: MutMap[Str, DelayedTypeInfo], inRecursiveDef: Opt[Var], // TODO rm extrCtx: ExtrCtx, ) { def +=(b: Str -> TypeInfo): Unit = { env += b if (inQuote) { val tag = SkolemTag(freshVar(NoProv, N, nameHint = S(b._1))(lvl))(NoProv) println(s"Create skolem tag $tag for ${b._2} in quasiquote.") quoteSkolemEnv += b._1 -> tag } } def ++=(bs: IterableOnce[Str -> TypeInfo]): Unit = bs.iterator.foreach(+=) def get(name: Str): Opt[TypeInfo] = { /** * 1. If we try to get a quoted variable from a quoted context, or get a non-quoted variable from a non-quoted context, * we can directly search the `env`; * 2. If we try to get a quoted variable from a non-quoted context, we need to wrap it with its contextual Skolem; * 3. Otherwise, it is a top-level defined symbol, or the context has a quoted parent. */ def rec(ctx: Ctx): Opt[TypeInfo] = { if (inQuote === ctx.inQuote) ctx.env.get(name) orElse ctx.parent.flatMap(rec(_)) else if (!inQuote) ctx.env.get(name) match { case S(VarSymbol(ty, _)) => ctx.getQuoteSkolem(name) match { case S(tag) => S(VarSymbol(TypeRef(TypeName("Var"), ty :: tag :: Nil)(noProv), Var(name))) case _ => ctx.parent.flatMap(rec(_)) } case _ => ctx.parent.flatMap(rec(_)) } else ctx.parent match { case S(parent) => rec(parent) case _ => ctx.env.get(name) } } rec(this) } def getQuoteSkolem(name: Str): Opt[SkolemTag] = quoteSkolemEnv.get(name) orElse parent.dlof(_.getQuoteSkolem(name))(N) def getAllQuoteSkolemsWith(ctxTy: TV): ST = quoteSkolemEnv.foldLeft[ST](ctxTy)((res, ty) => ty._2 | res) def getCtxTy: ST = freeVarsInCurrentQuote.foldLeft[ST](BotType)((res, ty) => res | ty) def trackFVs(fvsType: ST): Unit = { println(s"Capture free variable type $fvsType") freeVarsInCurrentQuote += fvsType } def contains(name: Str): Bool = env.contains(name) || parent.exists(_.contains(name)) def addMth(parent: Opt[Str], nme: Str, ty: MethodType): Unit = mthEnv += R(parent, nme) -> ty def addMthDefn(parent: Str, nme: Str, ty: MethodType): Unit = mthEnv += L(parent, nme) -> ty private def getMth(key: (Str, Str) \/ (Opt[Str], Str)): Opt[MethodType] = mthEnv.get(key) orElse parent.dlof(_.getMth(key))(N) def getMth(parent: Opt[Str], nme: Str): Opt[MethodType] = getMth(R(parent, nme)) def getMthDefn(parent: Str, nme: Str): Opt[MethodType] = getMth(L(parent, nme)) private def containsMth(key: (Str, Str) \/ (Opt[Str], Str)): Bool = mthEnv.contains(key) || parent.exists(_.containsMth(key)) def containsMth(parent: Opt[Str], nme: Str): Bool = containsMth(R(parent, nme)) def nest: Ctx = { assert(!inQuote) copy(Some(this), MutMap.empty, MutMap.empty) } /** * Enter a new quoted environment with empty `quoteSkolemEnv` and `freeVarsInCurrentQuote`. * A whole quasiquote (i.e., code"...") contains no binding or free variables at the beginning. * For a quoted binding (e.g., code"x => ..."): * 1. The context of the lambda body is still in the quotation so `inQuote = true` * 2. `quoteSkolemEnv` only contains skolems created by the current binding and `freeVarsInCurrentQuote` only contains free variables appearing in the body. * So we also apply empty `quoteSkolemEnv` and `freeVarsInCurrentQuote` at the beginning. * e.g. `code"x => y => x + y"`. For `y => x + y`, freeVarsInCurrentQuote = {'gx, 'gy}, quoteSkolemEnv = {'gy}. * To get the contextual type, we solve constraint the 'gx \/ 'gy <= 'a \/ 'gy, where 'a is a fresh type variable for `y => x + y`'s contextual type. * So for `code"x => ..."`, freeVarsInCurrentQuote = {'a}, quoteSkolemEnv = {'gx}, where 'gx <= 'a. * After calling `enterQuotedScope`, **solve the constraints** using `solveQuoteContext` to make sure free variables are handled correctly. */ def enterQuotedScope: Ctx = copy(Some(this), MutMap.empty, MutMap.empty, lvl = lvl + 1, inQuote = true, quoteSkolemEnv = MutMap.empty, freeVarsInCurrentQuote = LinkedHashSet.empty) def enterUnquote: Ctx = copy(Some(this), MutMap.empty, MutMap.empty, inQuote = false) def nextLevel[R](k: Ctx => R)(implicit raise: Raise, prov: TP): R = { val newCtx = copy(lvl = lvl + 1, extrCtx = MutSortMap.empty) val res = k(newCtx) val ec = newCtx.extrCtx assert(constrainedTypes || newCtx.extrCtx.isEmpty) if (ec.nonEmpty) trace(s"UNSTASHING... (out)") { implicit val ctx: Ctx = this ec.foreach { case (tv, bs) => bs.foreach { case (true, b) => constrain(b, tv) case (false, b) => constrain(tv, b) }} ec.clear() }() res } def poly(k: Ctx => ST)(implicit raise: Raise, prov: TP, shadows: Shadows = Shadows.empty): ST = { nextLevel { newCtx => val innerTy = k(newCtx) assert(constrainedTypes || newCtx.extrCtx.isEmpty) implicit val ctx: Ctx = newCtx implicit val freshened: MutMap[TV, ST] = MutMap.empty val cty = ConstrainedType.mk(newCtx.extrCtx.iterator.flatMap { case (tv, bs) => bs.iterator // .filter(_._2.level > lvl) // does not seem to change anything! .map { case (p, b) => assert(b.level > lvl) if (p) (b, tv) else (tv, b) } }.toList, innerTy) if (noApproximateOverload) { val ambiguous = innerTy.getVars.unsorted.flatMap(_.tsc.keys.flatMap(_.tvs)) .groupBy(_._2) .filter { case (v,pvs) => pvs.sizeIs > 1 } if (ambiguous.nonEmpty) raise(ErrorReport( msg"ambiguous" -> N :: ambiguous.map { case (v,_) => msg"cannot determine satisfiability of type ${v.expPos}" -> v.prov.loco }.toList , true)) } println(s"Inferred poly constr: $cty —— where ${cty.showBounds}") val cty_fresh = // * Sanity check: uncommenting this should change nothing (modulo type simplification randomness) // cty.freshenAbove(lvl, false) cty if (dbg) if (cty_fresh =/= cty) println(s"Refreshed: $cty_fresh —— where ${cty_fresh.showBounds}") val poly = PolymorphicType.mk(lvl, cty_fresh) /* newCtx.extrCtx.valuesIterator.foreach { buff => val filtered = buff.filter(_._2.level <= lvl) if (filtered.nonEmpty) println(s"FILTER $filtered") assert(filtered.isEmpty) buff.clear() } */ newCtx.extrCtx.clear() poly } } private val abcCache: MutMap[Str, Set[TypeName]] = MutMap.empty def allBaseClassesOf(name: Str): Set[TypeName] = abcCache.getOrElseUpdate(name, tyDefs.get(name).fold(Set.empty[TypeName])(_.allBaseClasses(this)(Set.empty))) } object Ctx { private val initBase: Ctx = Ctx( parent = N, env = MutMap.from(builtinBindings.iterator.map(nt => nt._1 -> VarSymbol(nt._2, Var(nt._1)))), mthEnv = MutMap.empty, lvl = MinLevel, quoteSkolemEnv = MutMap.empty, freeVarsInCurrentQuote = LinkedHashSet.empty, inQuote = false, inPattern = false, tyDefs = Map.from(builtinTypes.map(t => t.nme.name -> t)), tyDefs2 = MutMap.empty, inRecursiveDef = N, MutSortMap.empty, ) def init: Ctx = if (!newDefs) initBase else { val res = initBase.copy( tyDefs2 = MutMap.from(nuBuiltinTypes.map { t => val lti = new DelayedTypeInfo(t, Map.empty)(initBase, e => lastWords(e.theMsg)) initBase.env += t.nme.name -> lti t.nme.name -> lti }), ) implicit val raise: Raise = throw _ res.tyDefs2.valuesIterator.foreach { dti => val mem = dti.complete() // * Not strictly necessary, but it makes sense to use the completed member symbols: res.env += mem.name -> CompletedTypeInfo(mem) } res } val empty: Ctx = init } implicit def lvl(implicit ctx: Ctx): Int = ctx.lvl import TypeProvenance.{apply => tp} def ttp(trm: Term, desc: Str = "", isType: Bool = false): TypeProvenance = TypeProvenance(trm.toLoc, if (desc === "") trm.describe else desc, isType = isType) def originProv(loco: Opt[Loc], desc: Str, name: Str): TypeProvenance = { tp(loco, desc, S(name), isType = true) // ^ If we did not treat "origin provenances" differently, // it would yields unnatural errors like: //│ ╟── expression of type `B` is not a function //│ ║ l.6: method Map[B]: B -> A //│ ║ ^ // So we should keep the info but not shadow the more relevant later provenances } object NoProv extends TypeProvenance(N, "expression") { override def toString: Str = "[NO PROV]" } def noProv: TypeProvenance = NoProv def provTODO: TypeProvenance = noProv def noTyProv: TypeProvenance = TypeProvenance(N, "type", isType = true) private def sing[A](x: A): Set[A] = Set.single(x) val TopType: ExtrType = ExtrType(false)(noTyProv) val BotType: ExtrType = ExtrType(true)(noTyProv) val UnitType: ClassTag = if (newDefs) ClassTag(UnitLit(true), semp)(noTyProv) else ClassTag(Var("unit"), semp)(noTyProv) val BoolType: ST = if (newDefs) TR(TN("Bool"), Nil)(noTyProv) else ClassTag(Var("bool"), semp)(noTyProv) val ObjCls: ClassTag = ClassTag(Var("Object"), semp)(noTyProv) val ObjType: ST = if (newDefs) TR(TN("Object"), Nil)(noTyProv) else TopType val IntType: ST = if (newDefs) TR(TN("Int"), Nil)(noTyProv) else ClassTag(Var("int"), sing(TN("number")))(noTyProv) val DecType: ST = if (newDefs) TR(TN("Num"), Nil)(noTyProv) else ClassTag(Var("number"), semp)(noTyProv) val StrType: ST = if (newDefs) TR(TN("Str"), Nil)(noTyProv) else ClassTag(Var("string"), semp)(noTyProv) val TrueType: ST = if (newDefs) TR(TN("true"), Nil)(noTyProv) else ClassTag(Var("true"), sing(TN("bool")))(noTyProv) val FalseType: ST = if (newDefs) TR(TN("false"), Nil)(noTyProv) else ClassTag(Var("false"), sing(TN("bool")))(noTyProv) val AnnType: ST = TR(TN("Annotation"), Nil)(noTyProv) val EqlTag: TraitTag = TraitTag(Var("Eql"), Set.empty)(noProv) val ErrTypeId: SimpleTerm = Var("error") // TODO rm this obsolete definition (was there for the old frontend) private val primTypes = List("unit" -> UnitType, "bool" -> BoolType, "int" -> IntType, "number" -> DecType, "string" -> StrType, "anything" -> TopType, "nothing" -> BotType) private val preludeLoc = Loc(0, 0, Origin("", 0, new FastParseHelpers(""))) val nuBuiltinTypes: Ls[NuTypeDef] = Ls( NuTypeDef(Cls, TN("Object"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Trt, TN("Eql"), (S(VarianceInfo.contra), TN("A")) :: Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Num"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Int"), Nil, N, N, N, Var("Num") :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Bool"), Nil, N, N, S(Union(TN("true"), TN("false"))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Mod, TN("true"), Nil, N, N, N, Var("Bool") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), NuTypeDef(Mod, TN("false"), Nil, N, N, N, Var("Bool") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), NuTypeDef(Cls, TN("Str"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Als, TN("undefined"), Nil, N, N, S(Literal(UnitLit(true))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Als, TN("null"), Nil, N, N, S(Literal(UnitLit(false))), Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Annotation"), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Code"), (S(VarianceInfo.co) -> TN("T")) :: (S(VarianceInfo.co) -> TN("C")) :: Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Cls, TN("Var"), (S(VarianceInfo.in) -> TN("T")) :: (S(VarianceInfo.in) -> TN("C")) :: Nil, N, N, N, TyApp(Var("Code"), TN("T") :: TN("C") :: Nil) :: Nil, N, N, TypingUnit(Nil))(N, S(preludeLoc), Nil), NuTypeDef(Mod, TN("tailrec"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), NuTypeDef(Mod, TN("tailcall"), Nil, N, N, N, Var("Annotation") :: Nil, N, N, TypingUnit(Nil))(N, N, Nil), ) val builtinTypes: Ls[TypeDef] = TypeDef(Cls, TN("?"), Nil, TopType, Nil, Nil, Set.empty, N, Nil) :: // * Dummy for pretty-printing unknown type locations TypeDef(Cls, TN("int"), Nil, TopType, Nil, Nil, sing(TN("number")), N, Nil) :: TypeDef(Cls, TN("number"), Nil, TopType, Nil, Nil, semp, N, Nil) :: TypeDef(Cls, TN("bool"), Nil, TopType, Nil, Nil, semp, N, Nil) :: TypeDef(Cls, TN("true"), Nil, TopType, Nil, Nil, sing(TN("bool")), N, Nil) :: TypeDef(Cls, TN("false"), Nil, TopType, Nil, Nil, sing(TN("bool")), N, Nil) :: TypeDef(Cls, TN("string"), Nil, TopType, Nil, Nil, semp, N, Nil) :: TypeDef(Als, TN("undefined"), Nil, ClassTag(UnitLit(true), semp)(noProv), Nil, Nil, semp, N, Nil) :: TypeDef(Als, TN("null"), Nil, ClassTag(UnitLit(false), semp)(noProv), Nil, Nil, semp, N, Nil) :: TypeDef(Als, TN("anything"), Nil, TopType, Nil, Nil, semp, N, Nil) :: TypeDef(Als, TN("nothing"), Nil, BotType, Nil, Nil, semp, N, Nil) :: TypeDef(Cls, TN("error"), Nil, TopType, Nil, Nil, semp, N, Nil) :: TypeDef(Cls, TN("unit"), Nil, TopType, Nil, Nil, semp, N, Nil) :: { val tv = freshVar(noTyProv, N)(1) val tyDef = TypeDef(Als, TN("Array"), List(TN("A") -> tv), ArrayType(FieldType(None, tv)(noTyProv))(noTyProv), Nil, Nil, semp, N, Nil) // * ^ Note that the `noTyProv` here is kind of a problem // * since we currently expand primitive types eagerly in DNFs. // * For instance, see `inn2 v1` in test `Yicong.mls`. // * We could instead treat these primitives like any other TypeRef, // * but that currently requires more simplifier work // * to get rid of things like `1 & int` and `T | nothing`. tyDef.tvarVariances = S(MutMap(tv -> VarianceInfo.co)) tyDef } :: { val tv = freshVar(noTyProv, N)(1) val tyDef = TypeDef(Als, TN("MutArray"), List(TN("A") -> tv), ArrayType(FieldType(Some(tv), tv)(noTyProv))(noTyProv), Nil, Nil, semp, N, Nil) tyDef.tvarVariances = S(MutMap(tv -> VarianceInfo.in)) tyDef } :: Nil val primitiveTypes: Set[Str] = builtinTypes.iterator.map(_.nme.name).flatMap(n => n.decapitalize :: n.capitalize :: Nil).toSet + "Object" + "Num" + "Str" val reservedTypeNames: Set[Str] = primitiveTypes + "Eql" def singleTup(ty: ST): ST = if (funkyTuples) ty else TupleType((N, ty.toUpper(ty.prov) ) :: Nil)(noProv) def pair(ty1: ST, ty2: ST): ST = TupleType(N -> ty1.toUpper(ty1.prov) :: N -> ty2.toUpper(ty2.prov) :: Nil)(noProv) private val sharedVar = freshVar(noProv, N)(1) val builtinBindings: Bindings = { val tv = freshVar(noProv, N)(1) import FunctionType.{ apply => fun } val (intBinOpTy, numberBinOpTy, numberBinPred, stringBinPred) = if (newDefs) ( fun(pair(IntType, IntType), IntType)(noProv), fun(pair(DecType, DecType), DecType)(noProv), fun(pair(DecType, DecType), BoolType)(noProv), fun(pair(StrType, StrType), BoolType)(noProv), ) else ( fun(singleTup(IntType), fun(singleTup(IntType), IntType)(noProv))(noProv), fun(singleTup(DecType), fun(singleTup(DecType), DecType)(noProv))(noProv), fun(singleTup(DecType), fun(singleTup(DecType), BoolType)(noProv))(noProv), fun(singleTup(StrType), fun(singleTup(StrType), BoolType)(noProv))(noProv), ) Map( "true" -> TrueType, "false" -> FalseType, "True" -> TypeRef(TN("True"), Nil)(noProv), "False" -> TypeRef(TN("False"), Nil)(noProv), "NaN" -> DecType, "document" -> BotType, "window" -> BotType, "typeof" -> fun(singleTup(TopType), StrType)(noProv), "toString" -> fun(singleTup(TopType), StrType)(noProv), "String" -> fun(singleTup(TopType), StrType)(noProv), "not" -> fun(singleTup(BoolType), BoolType)(noProv), "succ" -> fun(singleTup(IntType), IntType)(noProv), "log" -> PolymorphicType(MinLevel, fun(singleTup(tv), UnitType)(noProv)), "discard" -> PolymorphicType(MinLevel, fun(singleTup(tv), UnitType)(noProv)), "negate" -> fun(singleTup(IntType), IntType)(noProv), "round" -> fun(singleTup(DecType), IntType)(noProv), "add" -> intBinOpTy, "sub" -> intBinOpTy, "mul" -> intBinOpTy, "div" -> intBinOpTy, "numAdd" -> numberBinOpTy, "numSub" -> numberBinOpTy, "numMul" -> numberBinOpTy, "sqrt" -> fun(singleTup(IntType), IntType)(noProv), "lt" -> numberBinPred, "le" -> numberBinPred, "gt" -> numberBinPred, "ge" -> numberBinPred, "slt" -> stringBinPred, "sle" -> stringBinPred, "sgt" -> stringBinPred, "sge" -> stringBinPred, "length" -> fun(singleTup(StrType), IntType)(noProv), "concat" -> fun(singleTup(StrType), fun(singleTup(StrType), StrType)(noProv))(noProv), "join" -> fun(ArrayType(StrType.toUpper(noProv))(noProv), StrType)(noProv), "eq" -> { val v = freshVar(noProv, N)(1) PolymorphicType(MinLevel, fun(singleTup(v), fun(singleTup(v), BoolType)(noProv))(noProv)) }, "ne" -> { val v = freshVar(noProv, N)(1) PolymorphicType(MinLevel, fun(singleTup(v), fun(singleTup(v), BoolType)(noProv))(noProv)) }, "error" -> BotType, "," -> { val v = sharedVar PolymorphicType(MinLevel, fun(TupleType(N -> TopType.toUpper(provTODO) :: N -> v.toUpper(provTODO) :: Nil)(noProv), v)(noProv)) }, "+" -> intBinOpTy, "-" -> intBinOpTy, "*" -> intBinOpTy, "+." -> numberBinOpTy, "-." -> numberBinOpTy, "*." -> numberBinOpTy, "%" -> intBinOpTy, "/" -> numberBinOpTy, "**" -> numberBinOpTy, "<" -> numberBinPred, ">" -> numberBinPred, "<=" -> numberBinPred, ">=" -> numberBinPred, "==" -> numberBinPred, "===" -> { val v = sharedVar val eq = TypeRef(TypeName("Eql"), v :: Nil)(noProv) PolymorphicType(MinLevel, fun(pair(eq, v), BoolType)(noProv)) }, "<>" -> numberBinPred, "&&" -> (if (newDefs) fun(pair(BoolType, BoolType), BoolType)(noProv) else fun(singleTup(BoolType), fun(singleTup(BoolType), BoolType)(noProv))(noProv)), "||" -> (if (newDefs) fun(pair(BoolType, BoolType), BoolType)(noProv) else fun(singleTup(BoolType), fun(singleTup(BoolType), BoolType)(noProv))(noProv)), "id" -> { val v = freshVar(noProv, N)(1) PolymorphicType(MinLevel, fun(singleTup(v), v)(noProv)) }, "if" -> { val v = freshVar(noProv, N)(1) PolymorphicType(MinLevel, fun(singleTup(BoolType), fun(singleTup(v), fun(singleTup(v), v)(noProv))(noProv))(noProv)) }, "emptyArray" -> { val v = freshVar(noProv, N)(1) PolymorphicType(0, ArrayType(FieldType(S(v), v)(noProv))(noProv)) }, "run" -> { val tv = freshVar(noProv, N)(1) PolymorphicType(0, fun(singleTup(TypeRef(TypeName("Code"), tv :: BotType :: Nil)(noProv)), tv)(noProv)) }, "Const" -> fun(singleTup(IntType), TypeRef(TypeName("Code"), IntType :: BotType :: Nil)(noProv))(noProv), ) ++ (if (!newDefs) primTypes ++ primTypes.map(p => p._1.capitalize -> p._2) // TODO settle on naming convention... else Nil) } /* Parameters `vars` and `newDefsInfo` are used for typing `TypeName`s. * If the key is found in `vars`, the type is typed as the associated value. Use case: type arguments. * If the key is found in `newDefsInfo`, the type is typed as a `TypeRef`, where the associated value * is used to check the kind of the definition and the number of type arguments expected. Use case: * for typing bodies of type definitions with mutually recursive references. */ def typeType(ty: Type, simplify: Bool = true) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], newDefsInfo: Map[Str, (TypeDefKind, Int)] = Map.empty): SimpleType = { typeType2(ty, simplify)._1 } def typePolyType(ty: Type, simplify: Bool = true) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], newDefsInfo: Map[Str, (TypeDefKind, Int)] = Map.empty): SimpleType = { implicit val prov: TP = tp(ty.toLoc, "type") val baseLevel = vars.valuesIterator.map(_.level).maxOption.getOrElse(MinLevel) ctx.copy(lvl = baseLevel).poly { implicit ctx => typeType2(ty, simplify)._1 } } /* Also returns an iterable of `TypeVariable`s instantiated when typing `TypeVar`s. * Useful for instantiating them by substitution when expanding a `TypeRef`. */ def typeType2(ty: Type, simplify: Bool = true) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], newDefsInfo: Map[Str, (TypeDefKind, Int)]): (SimpleType, Iterable[TypeVariable]) = // TODO rm _2 result? // trace(s"$lvl. Typing type $ty") { trace(s"Typing type ${ty.showDbg}") { println(s"vars=$vars newDefsInfo=$newDefsInfo") val typeType2 = () // val outerCtxLvl = MinLevel + 1 val outerCtxLvl = ctx.lvl def checkKind(k: DeclKind, nme: Str, loc: Opt[Loc]): Unit = k match { case Cls | Mod | Als | Trt => () case _ => err(msg"${k.str} ${nme} cannot be used as a type", loc); () } def typeNamed(loc: Opt[Loc], name: Str): (() => ST) \/ (TypeDefKind, Int) = newDefsInfo.get(name) .orElse(ctx.tyDefs.get(name).map(td => (td.kind, td.tparamsargs.size))) .orElse(ctx.get(name).flatMap { case CompletedTypeInfo(mem: TypedNuTypeDef) => checkKind(mem.decl.kind, mem.nme.name, loc) S(mem.td.kind, mem.tparams.size) case ti: DelayedTypeInfo => checkKind(ti.decl.kind, ti.decl.name, loc) ti.decl match { case NuTypeDef(k @ (Cls | Mod | Als | Trt), _, tps, _, _, _, _, _, _, _) => S(k, tps.size) case NuTypeDef(k @ Mxn, nme, tps, _, _, _, _, _, _, _) => S(k, tps.size) case fd: NuFunDef => N } case _ => N }) .toRight(() => err("type identifier not found: " + name, loc)(raise)) val localVars = mutable.Map.empty[TypeVar, TypeVariable] def tyTp(loco: Opt[Loc], desc: Str, originName: Opt[Str] = N) = TypeProvenance(loco, desc, originName, isType = true) def rec(ty: Type)(implicit ctx: Ctx, recVars: Map[TypeVar, TypeVariable]): SimpleType = trace(s"$lvl. type ${ty.showDbg}") { ty match { case Top => ExtrType(false)(tyTp(ty.toLoc, "top type")) case Bot => ExtrType(true)(tyTp(ty.toLoc, "bottom type")) case Bounds(Bot, Top) => val p = tyTp(ty.toLoc, "type wildcard") TypeBounds(ExtrType(true)(p), ExtrType(false)(p))(p) case Bounds(lb, ub) => val lb_ty = rec(lb) val ub_ty = rec(ub) implicit val prov: TP = tyTp(ty.toLoc, "type bounds") constrain(lb_ty, ub_ty) TypeBounds(lb_ty, ub_ty)(prov) case Tuple(fields) => TupleType(fields.mapValues(f => FieldType(f.in.map(rec), rec(f.out))(tp(f.toLoc, "tuple field")) ))(tyTp(ty.toLoc, "tuple type")) case Splice(fields) => SpliceType(fields.map{ case L(l) => { val t = rec(l) val res = ArrayType(freshVar(t.prov, N).toUpper(t.prov))(t.prov) constrain(t, res)(raise, t.prov, ctx) L(t) } case R(f) => { R(FieldType(f.in.map(rec), rec(f.out))(tp(f.toLoc, "splice field"))) } })(tyTp(ty.toLoc, "splice type")) case Inter(lhs, rhs) => (if (simplify) rec(lhs) & (rec(rhs), _: TypeProvenance) else ComposedType(false, rec(lhs), rec(rhs)) _ )(tyTp(ty.toLoc, "intersection type")) case Union(lhs, rhs) => (if (simplify) rec(lhs) | (rec(rhs), _: TypeProvenance) else ComposedType(true, rec(lhs), rec(rhs)) _ )(tyTp(ty.toLoc, "union type")) case Neg(t) => NegType(rec(t))(tyTp(ty.toLoc, "type negation")) case Record(fs) => val prov = tyTp(ty.toLoc, "record type") fs.groupMap(_._1.name)(_._1).foreach { case s -> fieldNames if fieldNames.sizeIs > 1 => err( msg"Multiple declarations of field name ${s} in ${prov.desc}" -> ty.toLoc :: fieldNames.map(tp => msg"Declared at" -> tp.toLoc))(raise) case _ => } RecordType.mk(fs.map { nt => if (nt._1.name.isCapitalized) err(msg"Field identifiers must start with a small letter", nt._1.toLoc)(raise) nt._1 -> FieldType(nt._2.in.map(rec), rec(nt._2.out))( tp(App(nt._1, Var("").withLocOf(nt._2)).toCoveringLoc, (if (nt._2.in.isDefined) "mutable " else "") + "record field")) })(prov) case Function(lhs, rhs) => FunctionType(rec(lhs), rec(rhs))(tyTp(ty.toLoc, "function type")) case WithExtension(b, r) => WithType(rec(b), RecordType( r.fields.map { case (n, f) => n -> FieldType(f.in.map(rec), rec(f.out))( tyTp(App(n, Var("").withLocOf(f)).toCoveringLoc, "extension field")) } )(tyTp(r.toLoc, "extension record")))(tyTp(ty.toLoc, "extension type")) case Literal(lit) => ClassTag(lit, if (newDefs) lit.baseClassesNu else lit.baseClassesOld)(tyTp(ty.toLoc, "literal type")) case TypeName("this") => ctx.env.get("this") match { case S(_: AbstractConstructor | _: LazyTypeInfo) => die case S(VarSymbol(t: SimpleType, _)) => t case N => err(msg"undeclared `this`" -> ty.toLoc :: Nil) } case tn @ TypeTag(name) => rec(TypeName(name.decapitalize)) // TODO rm this hack // case tn @ TypeTag(name) => rec(TypeName(name)) case tn @ TypeName(name) => val tyLoc = ty.toLoc val tpr = tyTp(tyLoc, "type reference") vars.getOrElse(name, { typeNamed(tyLoc, name) match { case R((_, tpnum)) => if (tpnum === 0) TypeRef(tn, Nil)(tpr) else ctx.tyDefs2.get(name) match { case S(lti) => lti.decl match { case NuTypeDef(Cls | Mod, _, _, _, _, _, _, _, _, _) => clsNameToNomTag(ctx.tyDefs2(name).decl.asInstanceOf[NuTypeDef])(tyTp(tyLoc, "class tag"), ctx) case NuTypeDef(Trt, _, _, _, _, _, _, _, _, _) => trtNameToNomTag(ctx.tyDefs2(name).decl.asInstanceOf[NuTypeDef])(tyTp(tyLoc, "class tag"), ctx) case NuTypeDef(Als, _, _, _, _, _, _, _, _, _) => TypeRef(tn, List.fill(tpnum)(freshVar(noProv, N, N)))(tpr) case _ => die // TODO } case _ => err(msg"Type $name takes parameters", tyLoc)(raise) } case L(e) => if (name.isEmpty || !name.head.isLower) e() else (typeNamed(tyLoc, name.capitalize), ctx.tyDefs.get(name.capitalize)) match { case (R((kind, _)), S(td)) => kind match { case Cls => clsNameToNomTag(td)(tyTp(tyLoc, "class tag"), ctx) case Trt => trtNameToNomTag(td)(tyTp(tyLoc, "trait tag"), ctx) case Als => err( msg"Type alias ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) case Mod => err( msg"Module ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) case Mxn => err( msg"Mixin ${name.capitalize} cannot be used as a type tag", tyLoc)(raise) } case _ => e() } } }) case tv: TypeVar => tv.identifier.toOption.flatMap(vars.get).getOrElse { recVars.getOrElse(tv, localVars.getOrElseUpdate(tv, freshVar(noProv, N, tv.name.filter(_.exists(_ =/= '\''))) (outerCtxLvl)) // * Type variables not explicily bound are assigned the widest (the outer context's) level ).withProv(tyTp(ty.toLoc, "type variable")) } case AppliedType(base, targs) => val prov = tyTp(ty.toLoc, "applied type reference") typeNamed(ty.toLoc, base.name) match { case R((_, tpnum)) => val realTargs = if (targs.size === tpnum) targs.map(rec) else { err(msg"Wrong number of type arguments – expected ${tpnum.toString}, found ${ targs.size.toString}", ty.toLoc)(raise) (targs.iterator.map(rec) ++ Iterator.continually(freshVar(noProv, N))).take(tpnum).toList } TypeRef(base, realTargs)(prov) case L(e) => e() } case Selection(base, nme) => implicit val gl: GenLambdas = false // val base_ty = typeTerm(base) val base_ty = rec(base) def go(b_ty: ST, rfnt: Var => Opt[FieldType]): ST = b_ty.unwrapAll match { case ct: TypeRef => die // TODO actually case ClassTag(Var(clsNme), _) => // TODO we should still succeed even if the member is not completed... lookupMember(clsNme, rfnt, nme.toVar) match { case R(cls: TypedNuCls) => if (cls.tparams.nonEmpty) ??? // TODO clsNameToNomTag(cls.td)(TypeProvenance(ty.toLoc, "type selection", isType = true), ctx) case R(als: TypedNuAls) => if (als.tparams.nonEmpty) ??? // TODO als.body case R(m) => err(msg"Illegal selection of ${m.kind.str} member in type position", nme.toLoc) case L(d) => err(d) } case _ => err(msg"Illegal prefix of type selection: ${b_ty.expPos}", base.toLoc) } go(base_ty, _ => N) case Recursive(uv, body) => val tv = freshVar(tyTp(ty.toLoc, "local type binding"), N, uv.name) val bod = rec(body)(ctx, recVars + (uv -> tv)) tv.assignedTo = S(bod) tv case Rem(base, fs) => Without(rec(base), fs.toSortedSet)(tyTp(ty.toLoc, "field removal type")) case Constrained(base, tvbs, where, tscs) => val res = rec(base match { case ty: Type => ty case _ => die }) tvbs.foreach { case (tv, Bounds(lb, ub)) => constrain(rec(lb), tv)(raise, tp(lb.toLoc, "lower bound specifiation"), ctx) constrain(tv, rec(ub))(raise, tp(ub.toLoc, "upper bound specifiation"), ctx) } where.foreach { case Bounds(lo, hi) => constrain(rec(lo), rec(hi))(raise, tp(mergeOptions(lo.toLoc, hi.toLoc)(_ ++ _), "constraint specifiation"), ctx) } tscs.foreach { case (typevars, constrs) => val tvs = typevars.map(x => (x._1, rec(x._2))) val tsc = new TupleSetConstraints(constrs.map(_.map(rec)), tvs) tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { case (tv: TV, i) => tv.tsc.updateWith(tsc)(_.map(_ + i).orElse(S(Set(i)))) case _ => () } } res case PolyType(vars, ty) => val oldLvl = ctx.lvl implicit val prov: TP = TypeProvenance(ty.toLoc, "polymorphic type") ctx.poly { implicit ctx => var newVars = recVars val tvs = vars.map { case L(tn) => die // this probably never happens... freshVar(tyTp(tn.toLoc, "quantified type name"), N, S(tn.name)) case R(tv) => val nv = freshVar(tyTp( tv.toLoc, // N, // * Here we choose to omit this location, // * because pointing to the binding place of forall TVs in error messages // * is often redundant, as these forall types are usually self-contained. "quantified type variable", tv.name ), N, tv.name) newVars += tv -> nv nv } rec(ty)(ctx, newVars) } }}(r => s"=> $r") (rec(ty)(ctx, Map.empty), localVars.values) }(r => s"=> ${r._1} ——— ${r._2.mkString(", ")}") def typePattern(pat: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType] = Map.empty): SimpleType = typeTerm(pat)(ctx.copy(inPattern = true), raise, vars, genLambdas = false) def typeStatement(s: DesugaredStatement, allowPure: Bool) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], genLambdas: GenLambdas): ST \/ Opt[Binding] = s match { case Def(false, Var("_"), L(rhs), isByname) => typeStatement(rhs, allowPure) case Def(isrec, nme, L(rhs), isByname) => // TODO reject R(..) if (nme.name === "_") err(msg"Illegal definition name: ${nme.name}", nme.toLoc)(raise) val ty_sch = typeLetRhs(isrec, nme.name, rhs) nme.uid = S(nextUid) ctx += nme.name -> VarSymbol(ty_sch, nme) R(S(nme.name -> ty_sch)) case t @ Tup(fs) if !allowPure => // Note: not sure this is still used! val thing = fs match { case (S(_), _) :: Nil => "field" case Nil => "empty tuple" case _ => "tuple" } warn(s"Useless $thing in statement position.", t.toLoc) L(PolymorphicType(MinLevel, typeTerm(t))) case t: Term => val ty = typeTerm(t) if (!allowPure) { if (t.isInstanceOf[Var] || t.isInstanceOf[Lit]) warn("Pure expression does nothing in statement position.", t.toLoc) else constrain(mkProxy(ty, TypeProvenance(t.toCoveringLoc, "expression in statement position")), UnitType)( raise = err => raise(WarningReport( // Demote constraint errors from this to warnings msg"Expression in statement position should have type `unit`." -> N :: msg"Use the `discard` function to discard non-unit values, making the intent clearer." -> N :: err.allMsgs, newDefs)), prov = TypeProvenance(t.toLoc, t.describe), ctx) } L(ty) case _ => err(msg"Illegal position for this ${s.describe} statement.", s.toLoc)(raise) R(N) } /** Like `typeLetRhs` but removes unnecessary polymorphic type wrappers. */ def typeLetRhs2(isrec: Boolean, nme: Str, rhs: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): ST = { val res = typeLetRhs(isrec, nme, rhs)(ctx, raise, vars, genLambdas = true) def stripPoly(ty: ST): ST = ty match { case pt: PolymorphicType => PolymorphicType.mk(pt.polymLevel, stripPoly(pt.body)) case _ => ty } stripPoly(res) } /** Infer the type of a let binding right-hand side. */ def typeLetRhs(isrec: Boolean, nme: Str, rhs: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], genLambdas: GenLambdas): PolymorphicType = { implicit val prov: TP = TypeProvenance(rhs.toLoc, "binding of " + rhs.describe) // * TODO eventually these should NOT introduce PolymorphicType-s on their own // * (don't use `nextLevel`) val res = if (isrec) { val e_ty = freshVar( // It turns out it is better to NOT store a provenance here, // or it will obscure the true provenance of constraints causing errors // across recursive references. noProv, // TypeProvenance(rhs.toLoc, "let-bound value"), N, S(nme), recPlaceholder = true )(lvl + 1) ctx += nme -> VarSymbol(e_ty, Var(nme)) ctx.copy(inRecursiveDef = S(Var(nme))).nextLevel { implicit ctx: Ctx => implicit val extrCtx: Opt[ExtrCtx] = N implicit val genLambdas: GenLambdas = preciselyTypeRecursion val ty = typeTerm(rhs) constrain(ty, e_ty)(raise, prov, ctx) e_ty.assignedTo = S(ty) } e_ty } else ctx.nextLevel { ctx => // * Note: let polymorphism (`ctx.nextLevel`) typeTerm(rhs)(ctx, raise, vars, genLambdas = true) } PolymorphicType(lvl, res) // * ^ TODO change: this only needs to be done in the rec case; // * and in that case, only for functions! } def mkProxy(ty: SimpleType, prov: TypeProvenance): SimpleType = { if (recordProvenances) if (ty.prov is prov) ty // * ^ Hacky: without this we get some prov accumulation explosions... would be better to fix at the root! else ProvType(ty)(prov) else ty // TODO don't do this when debugging errors // TODO switch to return this in perf mode: // ty } // TODO also prevent rebinding of "not" val reservedVarNames: Set[Str] = Set("|", "&", "~", "neg", "and", "or", "is", "refined") object ValidVar { def unapply(v: Var)(implicit raise: Raise): S[Str] = S { if (reservedVarNames(v.name)) err(s"Illegal use of reserved operator: " + v.name, v.toLoc)(raise) v.name } } object ValidPatVar { def unapply(v: Var)(implicit ctx: Ctx, raise: Raise): Opt[Str] = if (ctx.inPattern && v.isPatVar) { ctx.parent.dlof(_.get(v.name))(N) |>? { case S(VarSymbol(ts: SimpleType, _)) => ts.unwrapProxies } |>? { case S(ClassTag(Var(v.name), _)) => warn(msg"Variable name '${v.name}' already names a symbol in scope. " + s"If you want to refer to that symbol, you can use `scope.${v.name}`; " + s"if not, give your future readers a break and use another name :^)", v.toLoc) } ValidVar.unapply(v) } else N } def typeMonomorphicTerm(term: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): SimpleType = { implicit val genLambdas: GenLambdas = false typeTerm(term) } def typePolymorphicTerm(term: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): SimpleType = { implicit val genLambdas: GenLambdas = true typeTerm(term) } def notifyMoreErrors(action_ing: Str, prov: TypeProvenance)(implicit raise: Raise): Unit = { err(msg"Note: further errors omitted while ${action_ing} ${prov.desc}", prov.loco) () } /** * Solve constraints for quote context. * See `enterQuotedScope` * ctx: outer context * newCtx: inner context generated by `enterQuotedScope` */ def solveQuoteContext(ctx: Ctx, newCtx: Ctx)(implicit raise: Raise): Unit = if (ctx.inQuote) { val ctxTy = freshVar(noTyProv, N)(ctx.lvl) constrain(newCtx.getCtxTy, newCtx.getAllQuoteSkolemsWith(ctxTy))({ case err: ErrorReport => constrain(errType, ctxTy)(_ => (), noProv, ctx) raise(err) case diag => raise(diag) }, noProv, ctx) ctx.trackFVs(ctxTy) } /** Infer the type of a term. * genLambdas: whether to generalize lambdas that are found immediately in the term. * Note that the generalization of inner/nested lambdas is determined by other parameters; eg: * - we never generalize lambdas on the LHS of an application * (since they will be instantiated immediately anyweay) * - we don't generalize curried lambdas by default * (since we can always distribute the quantification of the inferred type variables later) * UNLESS generalizeCurriedFunctions or constrainedTypes are enabled * NOTE: when distrib. is disabled, we typically enable generalizeCurriedFunctions to make up for it * - we always generalize lambdas found in arguments, record/tuple fields, etc. */ def typeTerm(term: Term)(implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], genLambdas: GenLambdas): SimpleType = trace[ST](s"$lvl. Typing ${if (ctx.inPattern) "pattern" else "term"} $term") { // = trace[ST](s"$lvl. Typing ${if (ctx.inPattern) "pattern" else "term"} $term ${extrCtx.map(_.size)}") { implicit val prov: TypeProvenance = ttp(term) def con(lhs: SimpleType, rhs: SimpleType, res: SimpleType)(implicit ctx: Ctx): SimpleType = { var errorsCount = 0 constrain(lhs, rhs)({ case err: ErrorReport => // Note that we do not immediately abort constraining because we still // care about getting the non-erroneous parts of the code return meaningful types. // In other words, this is so that errors do not interfere too much // with the rest of the (hopefully good) code. if (errorsCount === 0) { constrain(errType, res)(_ => (), noProv, ctx) // ^ This is just to get error types leak into the result raise(err) } else if (errorsCount < maxSuccessiveErrReports) { // * Silence further errors from this location. if (showAllErrors) raise(err) } else { if (showAllErrors) notifyMoreErrors("typing", prov) return res // ^ Stop constraining, at this point. // This is to avoid rogue (explosive) constraint solving from badly-behaved error cases. // For instance see the StressTraits.mls test. } errorsCount += 1 case diag => raise(diag) }, prov, ctx) // Q: extrCtx here? res } term match { case v @ Var("_") => if (ctx.inPattern || funkyTuples) freshVar(tp(v.toLoc, "wildcard"), N) else err(msg"Widlcard in expression position.", v.toLoc) case Ann(ann, receiver) => val annType = typeTerm(ann) con(annType, AnnType, UnitType) typeTerm(receiver) case Asc(v @ ValidPatVar(nme), ty) => val ty_ty = typeType(ty)(ctx.copy(inPattern = false), raise, vars) val prov = tp(if (verboseConstraintProvenanceHints) v.toLoc else N, "variable") ctx.env.get(nme) match { case S(_) => err(s"Duplicate use of annotated pattern variable $nme", v.toLoc) case N => ctx += nme -> VarSymbol(ty_ty, v) ty_ty } case Asc(trm, ty) => val trm_ty = typePolymorphicTerm(trm) val ty_ty = typeType(ty)(ctx.copy(inPattern = false), raise, vars) if (ctx.inPattern) { unify(trm_ty, ty_ty); ty_ty } // * In patterns, we actually _unify_ the pattern and ascribed type else con(trm_ty, ty_ty, ty_ty) case (v @ ValidPatVar(nme)) => val prov = tp(if (verboseConstraintProvenanceHints) v.toLoc else N, "variable") // * Note: only look at ctx.env, and not the outer ones! ctx.env.get(nme).collect { case VarSymbol(ts, dv) => assert(v.uid.isDefined); v.uid = dv.uid; ts } .getOrElse { val res = new TypeVariable(lvl, Nil, Nil, N, Option.when(dbg)(nme))(prov) v.uid = S(nextUid) ctx += nme -> VarSymbol(res, v) res } case v @ ValidVar(name) => val ty = ctx.get(name).fold(err("identifier not found: " + name, term.toLoc): ST) { case AbstractConstructor(absMths, traitWithMths) => val td = ctx.tyDefs(name) err((msg"Instantiation of an abstract type is forbidden" -> term.toLoc) :: ( if (traitWithMths) { assert(td.kind is Trt) msg"Note that traits with methods are always considered abstract" -> td.toLoc :: Nil } else msg"Note that ${td.kind.str} ${td.nme} is abstract:" -> td.toLoc :: absMths.map { case mn => msg"Hint: method ${mn.name} is abstract" -> mn.toLoc }.toList ) ) case VarSymbol(ty, _) => if (ctx.inQuote) ctx.getQuoteSkolem(name).foreach(sk => ctx.trackFVs(sk)) ty case lti: LazyTypeInfo => // TODO deal with classes without parameter lists (ie needing `new`) def checkNotAbstract(decl: NuDecl) = if (decl.isAbstract) err(msg"Class ${decl.name} is abstract and cannot be instantiated", term.toLoc) lti match { case ti: CompletedTypeInfo => ti.member match { case ti: TypedNuFun => ti.typeSignature case p: NuParam => p.typeSignature case ti: TypedNuCls => checkNotAbstract(ti.decl) ti.typeSignature(false, prov.loco) case ti: TypedNuDecl => err(msg"${ti.kind.str} ${ti.name} cannot be used in term position", prov.loco) } case ti: DelayedTypeInfo => checkNotAbstract(ti.decl) ti.typeSignature(false, prov.loco) } } mkProxy(ty, prov) // ^ TODO maybe use a description passed in param? // currently we get things like "flows into variable reference" // but we used to get the better "flows into object receiver" or "flows into applied expression"... case lit: Lit => ClassTag(lit, if (newDefs) lit.baseClassesNu else lit.baseClassesOld)(prov) case Super() => err(s"Illegal use of `super`", term.toLoc)(raise) typeTerm(Var("super").withLocOf(term)) case App(Var("neg" | "~"), trm) if funkyTuples => typeTerm(trm).neg(prov) case App(App(Var("|"), lhs), rhs) if funkyTuples => typeTerm(lhs) | (typeTerm(rhs), prov) case App(App(Var("&"), lhs), rhs) if funkyTuples => typeTerm(lhs) & (typeTerm(rhs), prov) case Rcd(fs) => val prov = tp(term.toLoc, "record literal") fs.groupMap(_._1.name)(_._1).foreach { case s -> fieldNames if fieldNames.sizeIs > 1 => err( msg"Multiple declarations of field name ${s} in ${prov.desc}" -> term.toLoc :: fieldNames.map(tp => msg"Declared at" -> tp.toLoc))(raise) case _ => } RecordType.mk(fs.map { case (n, Fld(FldFlags(mut, _, _), t)) => if (n.name.isCapitalized) err(msg"Field identifiers must start with a small letter", term.toLoc)(raise) val tym = typePolymorphicTerm(t) val fprov = tp(App(n, t).toLoc, (if (mut) "mutable " else "") + "record field") if (mut) { val res = freshVar(fprov, N, S(n.name)) val rs = con(tym, res, res) (n, FieldType(Some(rs), rs)(fprov)) } else (n, tym.toUpper(fprov)) })(prov) case tup: Tup if funkyTuples => typeTerms(tup :: Nil, false, Nil) case Tup(fs) => TupleType(fs.mapConserve { case e @ (n, Fld(flags, t)) => n match { case S(v) if ctx.inPattern => (n, Fld(flags, Asc(v, t.toTypeRaise).withLoc(v.toLoc.fold(t.toLoc)(_ ++ t.toLoc |> some)))) case _ => e } }.map { case (n, Fld(FldFlags(mut, spec, getter), t)) => if (getter) err(msg"Cannot use `val` in this position", Loc(t :: n.toList)) val tym = typePolymorphicTerm(t) // val tym = if (n.isDefined) typeType(t.toTypeRaise) // else typePolymorphicTerm(t) val fprov = tp(t.toLoc, (if (mut) "mutable " else "") + "tuple field") if (mut) { val res = freshVar(fprov, N, n.map(_.name)) val rs = con(tym, res, res) (n, FieldType(Some(rs), rs)(fprov)) } else (n, tym.toUpper(fprov)) })(fs match { case Nil | ((N, _) :: Nil) => noProv // TODO rm? case _ => tp(term.toLoc, "tuple literal") }) case Subs(a, i) => val t_a = typeMonomorphicTerm(a) val t_i = typeMonomorphicTerm(i) con(t_i, IntType, TopType) val elemType = freshVar(prov, N) elemType.upperBounds ::= // * We forbid using [⋅] indexing to access elements that possibly have `undefined` value, // * which could result in surprising behavior and bugs in the presence of parametricity! // * Note that in modern JS, `undefined` is arguably not a value you're supposed to use explicitly; // * `null` should be used instead for those willing to indulge in the Billion Dollar Mistake. TypeRef(TypeName("undefined"), Nil)(noProv).neg( prov.copy(desc = "prohibited undefined element")) // TODO better reporting for this; the prov isn't actually used con(t_a, ArrayType(elemType.toUpper(tp(i.toLoc, "array element")))(prov), elemType) | TypeRef(TypeName("undefined"), Nil)(prov.copy(desc = "possibly-undefined array access")) case While(cnd, bod) => val t_cnd = typeMonomorphicTerm(cnd) con(t_cnd, BoolType, UnitType) typeTerm(Blk(bod :: UnitLit(true) :: Nil)) case Assign(s @ Sel(r, f), rhs) => val o_ty = typeMonomorphicTerm(r) val sprov = tp(s.toLoc, "assigned selection") val fieldType = freshVar(sprov, N, Opt.when(!f.name.startsWith("_"))(f.name)) val obj_ty = // Note: this proxy does not seem to make any difference: mkProxy(o_ty, tp(r.toCoveringLoc, "receiver")) con(obj_ty, RecordType.mk((f, FieldType(Some(fieldType), TopType)( tp(f.toLoc, "assigned field") )) :: Nil)(sprov), fieldType) val vl = typeMonomorphicTerm(rhs) con(vl, fieldType, UnitType.withProv(prov)) case Assign(s @ Subs(a, i), rhs) => val a_ty = typeMonomorphicTerm(a) val sprov = tp(s.toLoc, "assigned array element") val elemType = freshVar(sprov, N) val arr_ty = // Note: this proxy does not seem to make any difference: mkProxy(a_ty, tp(a.toCoveringLoc, "receiver")) con(arr_ty, ArrayType(FieldType(Some(elemType), elemType)(sprov))(prov), TopType) val i_ty = typeMonomorphicTerm(i) con(i_ty, IntType, TopType) val vl = typeMonomorphicTerm(rhs) con(vl, elemType, UnitType.withProv(prov)) case Assign(lhs @ (v: Var), rhs) => val rhs_ty = typeTerm(rhs) def checkMut(fd: NuFunDef) = if (!fd.isMut) err(msg"${fd.describe} `${fd.nme.name }` is not mutable and cannot be reassigned", prov.loco) ctx.get(v.name) match { case S(VarSymbol(ty, vr)) => con(rhs_ty, ty, UnitType.withProv(prov)) case S(CompletedTypeInfo(m: TypedNuFun)) => checkMut(m.fd) val lhs_ty = m.typeSignature con(rhs_ty, lhs_ty, UnitType.withProv(prov)) case S(dti @ DelayedTypeInfo(fd: NuFunDef)) => checkMut(fd) val lhs_ty = dti.mutRecTV con(rhs_ty, lhs_ty, UnitType.withProv(prov)) case _ => // TODO dedup w/ below err(msg"Illegal assignment" -> prov.loco :: msg"cannot assign to ${lhs.describe}" -> lhs.toLoc :: Nil) } case Assign(lhs, rhs) => err(msg"Illegal assignment" -> prov.loco :: msg"cannot assign to ${lhs.describe}" -> lhs.toLoc :: Nil) case Splc(es) => SpliceType(es.map{ case L(l) => L({ val t_l = typeMonomorphicTerm(l) val t_a = ArrayType(freshVar(prov, N).toUpper(prov))(prov) con(t_l, t_a, t_l) }) case R(Fld(FldFlags(mt, sp, _), r)) => { val t = typeMonomorphicTerm(r) if (mt) { R(FieldType(Some(t), t)(t.prov)) } else {R(t.toUpper(t.prov))} } })(prov) case Bra(false, trm: Blk) => typeTerm(trm) case Bra(rcd, trm @ (_: Tup | _: Blk)) if funkyTuples => typeTerms(trm :: Nil, rcd, Nil) case Bra(_, trm) => typeTerm(trm) case Blk((s: Term) :: Nil) => typeTerm(s) case Blk(Nil) => UnitType.withProv(prov) case pat if ctx.inPattern => err(msg"Unsupported pattern shape${ if (dbg) " ("+pat.getClass.toString+")" else ""}:", pat.toLoc)(raise) case Lam(pat, body) if doGenLambdas => println(s"TYPING POLY LAM") val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest newCtx.poly { newCtx => val param_ty = typePattern(pat)(newCtx, raise, vars) val body_ty = typeTerm(body)(newCtx, raise, vars, generalizeCurriedFunctions || doGenLambdas && constrainedTypes) solveQuoteContext(ctx, newCtx) FunctionType(param_ty, body_ty)(tp(term.toLoc, "function")) } case Lam(pat, body) => val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest val param_ty = typePattern(pat)(newCtx, raise, vars) assert(!doGenLambdas) val body_ty = typeTerm(body)(newCtx, raise, vars, generalizeCurriedFunctions || doGenLambdas) solveQuoteContext(ctx, newCtx) FunctionType(param_ty, body_ty)(tp(term.toLoc, "function")) case NuNew(cls) => typeMonomorphicTerm(App(NuNew(cls), Tup(Nil).withLoc(term.toLoc.map(_.right)))) case app @ App(nw @ NuNew(cls), args) => cls match { case _: TyApp => // * TODO improve (hacky) err(msg"Type arguments in `new` expressions are not yet supported", prov.loco) case _ => } val cls_ty = typeType(cls.toTypeRaise) def process(clsNme: Str) = { println(clsNme, ctx.tyDefs2.get(clsNme)) ctx.tyDefs2.get(clsNme) match { case N => err(msg"Type `${clsNme}` cannot be used in `new` expression", term.toLoc) case S(lti) => def checkNotAbstract(decl: NuDecl) = if (decl.isAbstract) err(msg"Class ${decl.name} is abstract and cannot be instantiated", term.toLoc) lti match { case dti: DelayedTypeInfo if !(dti.kind is Cls) => err(msg"${dti.kind.str.capitalize} ${dti.name} cannot be used in `new` expression", prov.loco) case dti: DelayedTypeInfo => checkNotAbstract(dti.decl) dti.typeSignature(true, prov.loco) } } } val new_ty = cls_ty.unwrapProxies match { case TypeRef(clsNme, targs) => // FIXME don't disregard `targs` process(clsNme.name) case err @ ClassTag(ErrTypeId, _) => err case ClassTag(Var(clsNme), _) => process(clsNme) case _ => // * Debug with: ${cls_ty.getClass.toString} err(msg"Unexpected type `${cls_ty.expPos}` after `new` keyword" -> cls.toLoc :: Nil) } val res = freshVar(prov, N) val argProv = tp(args.toLoc, "argument list") con(new_ty, FunctionType(typeTerm(args).withProv(argProv), res)(noProv), res) case App(App(Var("is"), _), _) => // * Old-style operators typeTerm(term.desugaredTerm.getOrElse { val desug = If(IfThen(term, Var("true")), S(Var("false"))) term.desugaredTerm = S(desug) desug }) case App(Var("is"), _) => typeTerm(term.desugaredTerm.getOrElse { val desug = If(IfThen(term, Var("true")), S(Var("false"))) term.desugaredTerm = S(desug) desug }) case App(App(Var("and"), PlainTup(lhs)), PlainTup(rhs)) => // * Old-style operators typeTerm(term.desugaredTerm.getOrElse { val desug = If(IfThen(lhs, rhs), S(Var("false"))) term.desugaredTerm = S(desug) desug }) case App(Var("and"), PlainTup(lhs, rhs)) => typeTerm(term.desugaredTerm.getOrElse { val desug = If(IfThen(lhs, rhs), S(Var("false"))) term.desugaredTerm = S(desug) desug }) case App(f: Term, a @ Tup(fields)) if (fields.exists(x => x._1.isDefined)) => def getLowerBoundFunctionType(t: SimpleType): List[FunctionType] = t.unwrapProvs match { case PolymorphicType(_, AliasOf(fun_ty @ FunctionType(_, _))) => List(fun_ty) case tt @ FunctionType(_, _) => List(tt) case tv: TypeVariable => tv.lowerBounds.map(getLowerBoundFunctionType(_)).flatten case ct @ ComposedType(pol, lhs, rhs) => if (pol === false) { getLowerBoundFunctionType(lhs) ++ getLowerBoundFunctionType(rhs) } else Nil case _ => Nil } val f_ty = typeTerm(f) val fun_tys: List[FunctionType] = getLowerBoundFunctionType(f_ty) fun_tys match { case FunctionType(TupleType(fields), _) :: Nil => val hasUntypedArg = fields.exists(_._1.isEmpty) if (hasUntypedArg) { err("Cannot use named arguments as the function type has untyped arguments", a.toLoc) } else { val argsList = fields.map(x => x._1 match { case Some(arg) => arg case N => die // cannot happen, because already checked with the hasUntypedArg }) desugarNamedArgs(term, f, a, argsList, f_ty) } case _ :: _ :: _ => err(msg"More than one function signature found in type `${f_ty.expPos}` for function call with named arguments", f.toLoc) case Nil | _ :: Nil => err(msg"Cannot retrieve appropriate function signature from type `${f_ty.expPos}` for applying named arguments", f.toLoc) } case App(f, a) => val f_ty = typeMonomorphicTerm(f) // * ^ Note: typing the function monomorphically simplifies type inference but // * breaks beta expansion stability property // * (ie a well-typed term may stop type-checking after beta expansion) val a_ty = { def typeArg(a: Term): ST = if (!generalizeArguments) typePolymorphicTerm(a) else ctx.poly { implicit ctx => typePolymorphicTerm(a) } a match { case tup @ Tup(as) => TupleType(as.map { case (n, Fld(FldFlags(mut, spec, _), a)) => // TODO handle mut? // assert(!mut) val fprov = tp(a.toLoc, "argument") val tym = typeArg(a) (n, tym.toUpper(fprov)) })(as match { // TODO dedup w/ general Tup case case Nil | ((N, _) :: Nil) => noProv case _ => tp(tup.toLoc, "argument list") }) case _ => // can happen in the old parser typeArg(a) } } val res = freshVar(prov, N) val arg_ty = mkProxy(a_ty, tp(a.toCoveringLoc, "argument")) // ^ Note: this no longer really makes a difference, due to tupled arguments by default val funProv = tp(f.toCoveringLoc, "applied expression") val fun_ty = mkProxy(f_ty, funProv) // ^ This is mostly not useful, except in test Tuples.fun with `(1, true, "hey").2` val resTy = con(fun_ty, FunctionType(arg_ty, res)( prov // funProv // TODO: better? ), res) resTy case Sel(obj, fieldName) => // Explicit method calls have the form `x.(Class.Method)` // Implicit method calls have the form `x.Method` // If two unrelated classes define methods of the same name, // implicit calls to this method are marked as ambiguous and are forbidden // Explicit method retrievals have the form `Class.Method` // Returns a function expecting an additional argument of type `Class` before the method arguments def rcdSel(obj: Term, fieldName: Var) = { val o_ty = typeMonomorphicTerm(obj) val res = freshVar(prov, N, Opt.when(!fieldName.name.startsWith("_") && !fieldName.isIndex)(fieldName.name)) val obj_ty = mkProxy(o_ty, tp(obj.toCoveringLoc, "receiver")) val rcd_ty = RecordType.mk( fieldName -> res.toUpper(tp(fieldName.toLoc, "field selector")) :: Nil)(prov) con(obj_ty, rcd_ty, res) } def mthCallOrSel(obj: Term, fieldName: Var) = ( if (newDefs) N else fieldName.name match { case s"$parent.$nme" => ctx.getMth(S(parent), nme) // explicit calls case nme => ctx.getMth(N, nme) // implicit calls }) match { case S(mth_ty) => if (mth_ty.body.isEmpty) { assert(mth_ty.parents.sizeCompare(1) > 0, mth_ty) err(msg"Implicit call to method ${fieldName.name} is forbidden because it is ambiguous." -> term.toLoc :: msg"Unrelated methods named ${fieldName.name} are defined by:" -> N :: mth_ty.parents.map { prt => val td = ctx.tyDefs(prt.name) msg"• ${td.kind.str} ${td.nme}" -> td.nme.toLoc }) } val o_ty = typeMonomorphicTerm(obj) val res = freshVar(prov, N) con(mth_ty.toPT.instantiate, FunctionType(singleTup(o_ty), res)(prov), res) case N => if (!newDefs && fieldName.name.isCapitalized) err(msg"Method ${fieldName.name} not found", term.toLoc) else { val realPrefix = obj match { case Super() => Var("super").withLocOf(obj) case _ => obj } rcdSel(realPrefix, fieldName) } } obj match { case Var(name) if name.isCapitalized && ctx.tyDefs.isDefinedAt(name) => // explicit retrieval ctx.getMth(S(name), fieldName.name) match { case S(mth_ty) => mth_ty.toPT.instantiate case N => err(msg"Class ${name} has no method ${fieldName.name}", term.toLoc) mthCallOrSel(obj, fieldName) } // * The code below is only a temporary solution to type `ClassName.unapply`. // * It removed when static methods can be typed properly. case Var(nuCls) => if (fieldName.name === "unapply") (ctx.get(nuCls) match { case S(CompletedTypeInfo(cls: TypedNuCls)) => cls.td.genUnapply case S(ti: DelayedTypeInfo) => ti.decl.genUnapply case _ => N }) match { case S(NuFunDef(_, _, _, _, L(unapplyMtd))) => typePolymorphicTerm(unapplyMtd) case _ => mthCallOrSel(obj, fieldName) } else mthCallOrSel(obj, fieldName) case _ => mthCallOrSel(obj, fieldName) } case Let(isrec, nme, rhs, bod) => if (ctx.inQuote) { val rhs_ty = typeTerm(rhs) val newCtx = ctx.enterQuotedScope newCtx += nme.name -> VarSymbol(rhs_ty, nme) val res_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) solveQuoteContext(ctx, newCtx) res_ty } else if (newDefs && !isrec) { // if (isrec) ??? val rhs_ty = typeTerm(rhs) val newCtx = ctx.nest newCtx += nme.name -> VarSymbol(rhs_ty, nme) typeTerm(bod)(newCtx, raise, vars, genLambdas) } else { val n_ty = typeLetRhs(isrec, nme.name, rhs) val newCtx = ctx.nest newCtx += nme.name -> VarSymbol(n_ty, nme) typeTerm(bod)(newCtx, raise, vars, genLambdas) } // case Blk(s :: stmts) => // val (newCtx, ty) = typeStatement(s) // typeTerm(Blk(stmts))(newCtx, lvl, raise) case b @ Blk(stmts) => if (newDefs) { val ttu = typeTypingUnit(TypingUnit(stmts), S(b)) // TODO check unused defs ttu.result.getOrElse(UnitType) } else typeTerms(stmts, false, Nil)(ctx.nest, raise, prov, vars, genLambdas) case Bind(l, r) => val l_ty = typeMonomorphicTerm(l) val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest // so the pattern's context don't merge with the outer context! val r_ty = typePattern(r)(newCtx, raise) ctx ++= newCtx.env solveQuoteContext(ctx, newCtx) con(l_ty, r_ty, r_ty) case Test(l, r) => val l_ty = typeMonomorphicTerm(l) val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest val r_ty = typePattern(r)(newCtx, raise) // TODO make these bindings flow solveQuoteContext(ctx, newCtx) con(l_ty, r_ty, TopType) BoolType case With(t, rcd) => val t_ty = typeMonomorphicTerm(t) val rcd_ty = typeMonomorphicTerm(rcd) (t_ty without rcd.fields.iterator.map(_._1).toSortedSet) & (rcd_ty, prov) case CaseOf(s, cs) => val oldCtx = ctx (if (ctx.inQuote) ctx.enterQuotedScope else ctx) |> { implicit ctx => val s_ty = typeMonomorphicTerm(s) if (newDefs) con(s_ty, ObjType.withProv(prov), TopType) val (tys, cs_ty) = typeArms(s |>? { case v: Var => v case Asc(v: Var, _) => v }, cs) solveQuoteContext(oldCtx, ctx) val req = tys.foldRight(BotType: SimpleType) { case ((a_ty, tv), req) => a_ty & tv | req & a_ty.neg() } con(s_ty, req, cs_ty) } case elf: If => elf.desugaredTerm match { case S(desugared) => typeTerm(desugared) case N => err(msg"not desugared UCS term found", elf.toLoc) } case AdtMatchWith(cond, arms) => println(s"typed condition term ${cond}") val cond_ty = typeTerm(cond) val ret_ty = if (arms.length === 1) { freshVar(prov.copy(desc = "let expression"), N) } else { freshVar(prov.copy(desc = "match expression"), N) } // the assumed shape of an IfBody is a List[IfThen, IfThen, IfElse] with an optional IfElse at the end arms.foreach { case AdtMatchPat(pat, rhs) => val nestCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest def handlePat(pat: Term, expected: SimpleType): Unit = pat match { case Var("_") => // Cases where the pattern is a single variable term // it can introduce a new pattern variable or it can be a constructor // that takes no argument // `case x -> expr` // `case End -> expr` or `case false -> expr` case v@Var(name) => println(s"type pattern $v with loc: ${v.toLoc}") // update context with variables ctx.tyDefs.get(name).flatMap { case TypeDef(Cls, _, _, _: TypeRef, _, _, _, _, _, S(adtData)) => S(adtData) case TypeDef(Cls, _, _, _, _, _, _, _, _, S(adtData)) => err(msg"Missing parameter list for pattern $name", v.toLoc) S(adtData) case _ => N }.fold { // `case x -> expr` catch all with a new variable in the context println(s"catch all $v") nestCtx += name -> VarSymbol(expected, v) } { // `case End -> expr` or `case false -> expr` or `case [] -> expr` // where case is a variant of an adt with no type arguments case AdtInfo(alsName) => // get adt from cache or initialize a new one with fresh vars // this is so that all case expressions can share // the same type variables for the adt val newTargs = // alsCache.getOrElseUpdate( // alsName.name, ctx.tyDefs.getOrElse(alsName.name, lastWords(s"Could not find ${alsName}")) .targs.map(tv => freshVar(tv.prov, N, tv.nameHint)) // ) println(s"pattern is adt: $alsName with $newTargs") val adt_ty = TypeRef(alsName, newTargs)(TypeProvenance(v.toLoc, "pattern")) .withProv(TypeProvenance(cond.toLoc, "match `condition`")) con(expected, adt_ty, expected) () } // Handle tuples specially since they don't have an explicit constructor case tup@Tup(fs) => println(s"fields $fs") val tupArgs = // alsCache.getOrElseUpdate("Tup" + fs.length.toString, fs.map(_ => freshVar(noProv, N)) // ) val fld_ty = tupArgs.map(elem => N -> FieldType(N, elem)(elem.prov)) val caseAdtTyp = TypeProvenance(tup.toLoc, "pattern") val adt_ty = TupleType(fld_ty)(caseAdtTyp) .withProv(TypeProvenance(cond.toLoc, "match `condition`")) con(expected, adt_ty, adt_ty) fs.zipWithIndex.foreach { case ((_, Fld(_, argTerm)), fieldIdx) => println(s"Typing $argTerm field $fieldIdx in tup") val fieldType = tupArgs(fieldIdx) println(s"Field $argTerm : $fieldType") handlePat(argTerm, fieldType) } case caseAdt@App(Var(ctorNme), patArgs: Tup) => println(s"Typing case ($ctorNme)") object BodyRecordType { def unapply(ty: SimpleType): Opt[RecordType] = ty match { case ty: RecordType => S(ty) case ProvType(underlying) => unapply(underlying) case ComposedType(false, lhs, rhs) => unapply(lhs) orElse unapply(rhs) case _ => N } } // find the alias type returned by constructor val (body, tparams, AdtInfo(alsName)) = ctx.tyDefs.get(ctorNme).flatMap { case TypeDef(Cls, _, tparamsargs, BodyRecordType(body), _, _, _, _, _, S(adtInfo)) => S(body, tparamsargs, adtInfo) case r => N }.getOrElse(lastWords(s"$ctorNme cannot be pattern matched")) // get alias type from cache or initialize a new one with fresh vars // this is so that all case expressions can share // the same type variables for the adt val newTargs = // alsCache.getOrElseUpdate( // alsName.name, ctx.tyDefs.getOrElse(alsName.name, lastWords(s"Could not find $alsName")) .targs.map(tv => freshVar(tv.prov, N, tv.nameHint)) // ) val caseAdtTyp = TypeProvenance(caseAdt.toLoc, "pattern") val newAlsTy = TypeRef(alsName, newTargs)(caseAdtTyp) con(expected, newAlsTy, expected) println(s"adt_ty $newAlsTy") val mapping: Map[ST, ST] = tparams.map(_._2).zip(newTargs).toMap val argFields = subst(body, mapping) match { case RecordType(fields) => fields.map(_._2.ub) case _ => die } val patArgFields = patArgs.fields.map(_._2.value) assert(argFields.sizeCompare(patArgFields) === 0) patArgFields.lazyZip(argFields).foreach(handlePat) case pat: Lit => val litTy = pat match { case DecLit(_) => DecType case IntLit(_) => IntType case StrLit(_) => StrType case UnitLit(_) => UnitType } con(expected, litTy.withProv(tp(pat.toLoc, "literal pattern")), expected) () case Bind(t, v@Var(name)) => nestCtx += name -> VarSymbol(expected, v) handlePat(t, expected) case Bra(false, pat) => handlePat(pat, expected) // case Asc(trm, ty) => // con(expected, typeType(ty), expected) // () // and others case pat => lastWords(s"Cannot handle pattern ${pat.showDbg}") } handlePat(pat, cond_ty) nestCtx |> { implicit ctx => con(typeTerm(rhs), ret_ty, ret_ty) } solveQuoteContext(ctx, nestCtx) } ret_ty case New(base, args) => err(msg"Currently unsupported `new` syntax", term.toCoveringLoc) case TyApp(base, _) => err(msg"Type application syntax is not yet supported", term.toLoc) // TODO handle typeTerm(base) case Where(bod, sts) => typeTerms(sts :+ bod, false, Nil, allowPure = true) case Forall(vs, bod) => ctx.poly { implicit ctx => val newVars = vs.map { case tv @ TypeVar(R(nme), _) => nme -> SkolemTag(freshVar(tp(tv.toLoc, "quantified type variable"), N, S(nme)))( tp(tv.toLoc, "rigid type variable")) case _ => die } vars ++ newVars |> { implicit vars => typeMonomorphicTerm(bod) } } case Inst(bod) => val bod_ty = typePolymorphicTerm(bod) var founPoly = false def go(ty: ST): ST = ty.unwrapAll match { case pt: PolymorphicType => founPoly = true go(pt.instantiate) case _ => ty } val res = go(bod_ty) if (!founPoly) warn(msg"Inferred type `${bod_ty.expPos}` of this ${ bod_ty.prov.desc} cannot be instantiated", prov.loco) res case Eqn(lhs, rhs) => err(msg"Unexpected equation in this position", term.toLoc) case q @ Quoted(body) => if (ctx.inQuote) err(msg"Nested quotation is not allowed.", q.toLoc) else { val newCtx = ctx.enterQuotedScope val bodyType = typeTerm(body)(newCtx, raise, vars, genLambdas) TypeRef(TypeName("Code"), bodyType :: newCtx.getCtxTy :: Nil)(TypeProvenance(q.toLoc, "code fragment")) } case uq @ Unquoted(body) => if (ctx.inQuote) { val newCtx = ctx.enterUnquote val bodyType = typeTerm(body)(newCtx, raise, vars, genLambdas) val res = freshVar(TypeProvenance(uq.toLoc, "code fragment body type"), N) val ctxTy = freshVar(TypeProvenance(body.toLoc, "code fragment context type"), N) val ty = con(bodyType, TypeRef(TypeName("Code"), res :: ctxTy :: Nil)(TypeProvenance(body.toLoc, "unquote body")), res)(newCtx) ctx.trackFVs(ctxTy) ty.withProv(TypeProvenance(uq.toLoc, "unquote")) } else err("Unquotes should be enclosed with a quasiquote.", uq.toLoc)(raise) case Rft(bse, tu) => err(msg"Refinement terms are not yet supported", term.toLoc) } }(r => s"$lvl. : ${r}") def typeArms(scrutVar: Opt[Var], arms: CaseBranches) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType], genLambdas: GenLambdas) : Ls[SimpleType -> SimpleType] -> SimpleType = arms match { case NoCases => Nil -> BotType case Wildcard(b) => val fv = freshVar(tp(arms.toLoc, "wildcard pattern"), N) val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest val res = scrutVar match { case Some(v) => newCtx += v.name -> VarSymbol(fv, v) val b_ty = typeTerm(b)(newCtx, raise, vars, genLambdas) (fv -> TopType :: Nil) -> b_ty case _ => (fv -> TopType :: Nil) -> typeTerm(b) } solveQuoteContext(ctx, newCtx) res case cse @ Case(pat, bod, rest) => val (tagTy, patTy) : (ST, ST) = pat match { case lit: Lit => val t = ClassTag(lit, if (newDefs) lit.baseClassesNu else lit.baseClassesOld)(tp(pat.toLoc, "literal pattern")) t -> t case v @ Var(nme) => val tpr = tp(pat.toLoc, "type pattern") ctx.tyDefs.get(nme) match { case Some(td) if !newDefs => td.kind match { case Als | Mod | Mxn => val t = err(msg"can only match on classes and traits", pat.toLoc)(raise); t -> t case Cls => val t = clsNameToNomTag(td)(tp(pat.toLoc, "class pattern"), ctx); t -> t case Trt => val t = trtNameToNomTag(td)(tp(pat.toLoc, "trait pattern"), ctx); t -> t } case _ => val bail = () => { val e = ClassTag(ErrTypeId, Set.empty)(tpr) return ((e -> e) :: Nil) -> e } ctx.get(nme) match { case S(lti: LazyTypeInfo) => if ((lti.kind isnt Cls) && (lti.kind isnt Mod) && (lti.kind isnt Trt)) err(msg"can only match on classes and traits", pat.toLoc)(raise) val prov = tp(pat.toLoc, "class pattern") lti match { case dti: DelayedTypeInfo => val tag = clsNameToNomTag(dti.decl match { case decl: NuTypeDef => decl; case _ => die })(prov, ctx) val ty = // TODO update as below for refined RecordType.mk(dti.tparams.map { case (tn, tv, vi) => val nv = freshVar(tv.prov, S(tv), tv.nameHint) (Var(nme+"#"+tn.name).withLocOf(tn), FieldType.mk(vi.getOrElse(VarianceInfo.in), nv, nv)(provTODO)) })(provTODO) println(s"Match arm $nme: $tag & $ty") tag -> ty case CompletedTypeInfo(cls: TypedNuCls) => val tag = clsNameToNomTag(cls.td)(prov, ctx) println(s"CASE $tag ${cse.refined}") val ty = if (cse.refined) freshVar(tp(v.toLoc, "refined scrutinee"), N) else RecordType.mk(cls.tparams.map { case (tn, tv, vi) => val nv = freshVar(tv.prov, S(tv), tv.nameHint) (Var(nme+"#"+tn.name).withLocOf(tn), FieldType.mk(vi.getOrElse(cls.varianceOf(tv)), nv, nv)(provTODO)) })(provTODO) println(s"Match arm $nme: $tag & $ty") tag -> ty case CompletedTypeInfo(_) => bail() } case _ => err("type identifier not found: " + nme, pat.toLoc)(raise) bail() } } } val newCtx = if (ctx.inQuote) ctx.enterQuotedScope else ctx.nest val (req_ty, bod_ty, (tys, rest_ty)) = scrutVar match { case S(v) => if (newDefs) { newCtx += v.name -> VarSymbol(tagTy & patTy, v) val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) (tagTy -> patTy, bod_ty, typeArms(scrutVar, rest)) } else { val tv = freshVar(tp(v.toLoc, "refined scrutinee"), N, // S(v.name), // this one seems a bit excessive ) newCtx += v.name -> VarSymbol(tv, v) val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) (patTy -> tv, bod_ty, typeArms(scrutVar, rest)) } case N => val bod_ty = typeTerm(bod)(newCtx, raise, vars, genLambdas) (tagTy -> TopType, bod_ty, typeArms(scrutVar, rest)) } solveQuoteContext(ctx, newCtx) (req_ty :: tys) -> (bod_ty | rest_ty) } def typeTerms(term: Ls[Statement], rcd: Bool, fields: List[Opt[Var] -> SimpleType], allowPure: Bool = false) (implicit ctx: Ctx, raise: Raise, prov: TypeProvenance, vars: Map[Str, SimpleType], genLambdas: GenLambdas): SimpleType = term match { case (trm @ Var(nme)) :: sts if rcd => // field punning typeTerms(Tup(S(trm) -> Fld(FldFlags.empty, trm) :: Nil) :: sts, rcd, fields) case Blk(sts0) :: sts1 => typeTerms(sts0 ::: sts1, rcd, fields) case Tup(Nil) :: sts => typeTerms(sts, rcd, fields) case Tup((no, Fld(FldFlags(tmut, _, _), trm)) :: ofs) :: sts => val ty = { trm match { case Bra(false, t) if ctx.inPattern => // we use syntax `(x: (p))` to type `p` as a pattern and not a type... typePattern(t) case _ => ctx.copy(inPattern = ctx.inPattern && no.isEmpty) |> { implicit ctx => // TODO change this? if (ofs.isEmpty) typeTerm(Bra(rcd, trm)) // ^ This is to type { a: ... } as { a: { ... } } to facilitate object literal definitions; // not sure that's a good idea... else typeTerm(trm) } } } val res_ty = no |> { case S(nme) if ctx.inPattern => // TODO in 'opaque' definitions we should give the exact specified type and not something more precise // as in `(x: Int) => ...` should not try to refine the type of `x` further val prov = tp(trm.toLoc, "parameter type") val t_ty = // TODO in positive position, this should create a new VarType instead! (i.e., an existential) new TypeVariable(lvl, Nil, Nil, N)(prov)//.tap(ctx += nme -> _) // constrain(ty, t_ty)(raise, prov) constrain(t_ty, ty)(raise, prov, ctx) ctx += nme.name -> VarSymbol(t_ty, nme) t_ty // ty // ComposedType(false, t_ty, ty)(prov) // ComposedType(true, t_ty, ty)(prov) // loops! case S(nme) => ctx += nme.name -> VarSymbol(ty, nme) ty case _ => ty } typeTerms(Tup(ofs) :: sts, rcd, (no, res_ty) :: fields) case (trm: Term) :: Nil => if (fields.nonEmpty) warn("Previous field definitions are discarded by this returned expression.", trm.toLoc) typeTerm(trm) // case (trm: Term) :: Nil => // assert(!rcd) // val ty = typeTerm(trm) // typeBra(Nil, rcd, (N, ty) :: fields) case s :: sts => val (diags, desug) = s.desugared diags.foreach(raise) val newBindings = desug.flatMap(typeStatement(_, allowPure).toOption) ctx ++= newBindings.iterator.flatten.map(nt => nt._1 -> VarSymbol(nt._2, Var(nt._1))) typeTerms(sts, rcd, fields) case Nil => if (rcd) { val fs = fields.reverseIterator.zipWithIndex.map { case ((S(n), t), i) => n -> t.toUpper(noProv) case ((N, t), i) => // err("Missing name for record field", t.prov.loco) warn("Missing name for record field", t.prov.loco) (Var("_" + (i + 1)), t.toUpper(noProv)) }.toList RecordType.mk(fs)(prov) } else TupleType(fields.reverseIterator.mapValues(_.toUpper(noProv)))(prov) } def getNewVarName(prefix: Str, nonValidVars: Set[Var]): Str = { // we check all possibe prefix_num combination, till we find one that is not in the nonValidVars val ints = LazyList.from(1) prefix + "_" + ints.find(index => { !nonValidVars.contains(Var(prefix + "_" + index)) }).getOrElse(die) } def desugarNamedArgs(term: Term, f: Term, a: Tup, argsList: List[Var], f_ty: ST) (implicit ctx: Ctx, raise: Raise, vars: Map[Str, SimpleType]): SimpleType = { def rec (as: List[(String -> Fld) -> Boolean], acc: Map[String, Either[Var, Term]]): Term = { as match { case ((v, fld), isNamed) :: tail => if (isNamed) { fld.value match { case _: Lit | _: Var => rec(tail, acc + (v -> R(fld.value))) case _ => val newVar = Var(getNewVarName(v, a.freeVars)) Let(false, newVar, fld.value, rec(tail, acc + (v -> L(newVar)))) } } else { rec(tail, acc + (v -> R(fld.value))) } case Nil => val y: Term = Tup(argsList.map(x => acc.get(x.name) match { case Some(Left(v)) => (None, Fld(FldFlags.empty, v)) case Some(Right(t)) => (None, Fld(FldFlags.empty, t)) case None => err(msg"Argument named '${x.name}' is missing from this function call", a.toLoc) (None, Fld(FldFlags.empty, Var("error"))) } )) App(f, y) } } val hasDefined = a.fields.exists(x => x._1.isDefined) val hasEmpty = a.fields.exists(x => x._1.isEmpty) val areArgsMisplaced = a.fields.indexWhere(x => x._1.isDefined) < a.fields.lastIndexWhere(x => x._1.isEmpty) if (hasDefined && hasEmpty && areArgsMisplaced) { err(msg"Unnamed arguments should appear first when using named arguments", a.toLoc) } else a.fields.sizeCompare(argsList) match { case 0 => val as = a.fields.zipWithIndex.map{ case(x, idx) => x._1 match { case Some(value) => ((value.name, x._2), true) case N => ((argsList(idx).name, x._2), false) }} val asGroupedByVarName = as.groupBy(x => x._1._1) if (asGroupedByVarName.sizeCompare(argsList) < 0) { asGroupedByVarName.foreach(x => x._2 match { case x1 :: y1 :: xs => err(msg"Argument for parameter '${x._1}' is duplicated", a.toLoc) case _ => }) } val desugared = rec(as, Map()) println("Desugared is here => " + desugared) term.desugaredTerm = S(desugared) typeTerm(desugared)(ctx = ctx, raise = raise, vars = vars, genLambdas = false) case _ => err(msg"Number of arguments doesn't match function signature `${f_ty.expPos}`", a.toLoc) } } /** Convert an inferred SimpleType into the immutable Type representation. */ def expandType(st: TypeLike, stopAtTyVars: Bool = false)(implicit ctx: Ctx): mlscript.TypeLike = { val expandType = () var bounds: Ls[TypeVar -> Bounds] = Nil var tscs: Ls[Ls[(Bool, Type)] -> Ls[Ls[Type]]] = Nil val seenVars = mutable.Set.empty[TV] val seenTscs = mutable.Set.empty[TupleSetConstraints] def field(ft: FieldType)(implicit ectx: ExpCtx): Field = ft match { case FieldType(S(l: TV), u: TV) if l === u => val res = go(u) Field(S(res), res) // TODO improve Field case f => Field(f.lb.map(go), go(f.ub)) } class ExpCtx(val tps: Map[TV, TN]) { def apply(tparams: Ls[(TN, TV, Opt[VarianceInfo])]): ExpCtx = new ExpCtx(tps ++ tparams.iterator.map{case (tn, tv, vi) => tv -> tn}) } def mkTypingUnit(thisTy: ST, members: Map[Str, NuMember])(implicit ectx: ExpCtx): TypingUnit = { val sorted = members.toList.sortBy(_._1) TypingUnit(sorted.collect { case (_, d: TypedNuFun) => goDecl(d) case (_, d: TypedNuTypeDef) => goDecl(d) }) } def goDecl(d: NuMember)(implicit ectx: ExpCtx): NuDecl = d match { case TypedNuAls(level, td, tparams, body) => ectx(tparams) |> { implicit ectx => NuTypeDef(td.kind, td.nme, td.tparams, N, N, S(go(body)), Nil, N, N, TypingUnit(Nil))( td.declareLoc, td.abstractLoc, td.annotations) } case TypedNuMxn(level, td, thisTy, superTy, tparams, params, members) => ectx(tparams) |> { implicit ectx => NuTypeDef(td.kind, td.nme, td.tparams, S(Tup(params.map(p => N -> Fld(FldFlags.empty, Asc(p._1, go(p._2.ub)))))), N,//TODO N, Nil, // TODO mixin parents? Option.when(!(TopType <:< superTy))(go(superTy)), Option.when(!(TopType <:< thisTy))(go(thisTy)), mkTypingUnit(thisTy, members) )(td.declareLoc, td.abstractLoc, td.annotations) } case TypedNuCls(level, td, tparams, params, acParams, members, thisTy, sign, ihtags, ptps) => ectx(tparams) |> { implicit ectx => NuTypeDef(td.kind, td.nme, td.tparams, params.map(ps => Tup(ps.map(p => N -> Fld(FldFlags.empty, Asc(p._1, go(p._2.ub)))))), td.ctor, Option.when(!(TopType <:< sign))(go(sign)), ihtags.toList.sorted.map(_.toVar), // TODO provide targs/args N,//TODO Option.when(!(TopType <:< thisTy))(go(thisTy)), { val tun = mkTypingUnit(thisTy, members) acParams match { case S(ps) => TypingUnit(Constructor( Tup(ps.map(p => N -> Fld(FldFlags.empty, Asc(p._1, go(p._2))))), Blk(Nil)) :: tun.entities) case N => tun } } )(td.declareLoc, td.abstractLoc, td.annotations) } case TypedNuTrt(level, td, tparams, members, thisTy, sign, ihtags, ptps) => ectx(tparams) |> { implicit ectx => NuTypeDef(td.kind, td.nme, td.tparams, N, td.ctor, Option.when(!(TopType <:< sign))(go(sign)), ihtags.toList.sorted.map(_.toVar), // TODO provide targs/args N,//TODO Option.when(!(TopType <:< thisTy))(go(thisTy)), mkTypingUnit(thisTy, members) )(td.declareLoc, td.abstractLoc, td.annotations) } case tf @ TypedNuFun(level, fd, bodyTy) => NuFunDef(fd.isLetRec, fd.nme, fd.symbolicNme, Nil, R(go(tf.typeSignature)))( fd.declareLoc, fd.virtualLoc, fd.mutLoc, fd.signature, fd.outer, fd.genField, fd.annotations) case p: NuParam => ??? // TODO case TypedNuDummy(d) => ??? // TODO } def goLike(ty: TypeLike)(implicit ectx: ExpCtx): mlscript.TypeLike = ty match { case ty: SimpleType => val res = go(ty) // if (bounds.isEmpty) res // else Constrained(res, bounds, Nil) res case OtherTypeLike(tu) => val mems = tu.implementedMembers.map(goDecl) Signature(mems, tu.result.map(go)) } def go(st: SimpleType)(implicit ectx: ExpCtx): Type = // trace(s"expand $st") { st.unwrapProvs match { case tv: TypeVariable if stopAtTyVars => tv.asTypeVar case tv: TypeVariable => ectx.tps.getOrElse(tv, { val nv = tv.asTypeVar if (seenVars.add(tv)) { tv.assignedTo match { case S(ty) => val b = go(ty) bounds ::= nv -> Bounds(b, b) case N => val l = go(tv.lowerBounds.foldLeft(BotType: ST)(_ | _)) val u = go(tv.upperBounds.foldLeft(TopType: ST)(_ &- _)) if (l =/= Bot || u =/= Top) bounds ::= nv -> Bounds(l, u) } tv.tsc.foreachEntry { case (tsc, i) => if (seenTscs.add(tsc)) { val tvs = tsc.tvs.map(x => (x._1,go(x._2))) val constrs = tsc.constraints.map(_.map(go)) tscs ::= tvs -> constrs } } } nv }) case FunctionType(l, r) => Function(go(l), go(r)) case ct @ ComposedType(true, l, r) => if (ct >:< (TrueType | FalseType)) TN("Bool") // TODO should rather be done in TypeSimplifier else Union(go(l), go(r)) case ComposedType(false, l, r) => Inter(go(l), go(r)) case RecordType(fs) => Record(fs.mapValues(field)) case TupleType(fs) => Tuple(fs.mapValues(field)) case ArrayType(FieldType(None, ub)) => AppliedType(TypeName("Array"), go(ub) :: Nil) case ArrayType(f) => val f2 = field(f) AppliedType(TypeName("MutArray"), Bounds(f2.in.getOrElse(Bot), f2.out) :: Nil) case SpliceType(elems) => Splice(elems.map { case L(l) => L(go(l)) case R(v) => R(Field(v.lb.map(go(_)), go(v.ub))) }) case NegType(t) => Neg(go(t)) case ExtrType(true) => Bot case ExtrType(false) => Top case WithType(base, rcd) => WithExtension(go(base), Record(rcd.fields.mapValues(field))) case ProxyType(und) => go(und) case obj: ObjectTag => obj.id match { case Var(n) => if (primitiveTypes.contains(n) // primitives like `int` are internally maintained as class tags || n === "this" // `this` type ) TypeName(n) else TypeTag(n.capitalize) case lit: Lit => Literal(lit) } case SkolemTag(tv) => tv.nameHint match { case S(n) if n.isCapitalized // rigid type params like A in class Foo[A] => TypeName(n) case _ => go(tv) } case ex @ Extruded(p, SkolemTag(tv)) => if (p) tv.asPosExtrudedTypeVar else tv.asNegExtrudedTypeVar case TypeRef(td, Nil) => td case tr @ TypeRef(td, targs) => AppliedType(td, tr.mapTargs(S(true)) { case ta @ ((S(true), TopType) | (S(false), BotType)) => Bounds(Bot, Top) case (_, ty) => go(ty) }) case TypeBounds(lb, ub) => Bounds(go(lb), go(ub)) case Without(base, names) => Rem(go(base), names.toList) case Overload(as) => as.map(go).reduce(Inter) case PolymorphicType(lvl, bod) => val boundsSize = bounds.size val tscsSize = tscs.size val b = go(bod) // This is not completely correct: if we've already traversed TVs as part of a previous sibling PolymorphicType, // the bounds of these TVs won't be registered again... // FIXME in principle we'd want to compute a transitive closure... val newBounds = bounds.reverseIterator.drop(boundsSize).toBuffer val newTscs = tscs.reverseIterator.drop(tscsSize).toBuffer val qvars = bod.varsBetween(lvl, MaxLevel).iterator val ftvs = b.freeTypeVariables ++ newBounds.iterator.map(_._1) ++ newBounds.iterator.flatMap(_._2.freeTypeVariables) ++ newTscs.iterator.flatMap(_._1.map(_._2)) val fvars = qvars.filter(tv => ftvs.contains(tv.asTypeVar)) if (fvars.isEmpty) b else PolyType(fvars .toArray.sorted .map(_.asTypeVar pipe (R(_))).toList, b) case ConstrainedType(cs, bod) => val groups1, groups2 = LinkedHashMap.empty[ST, Buffer[ST]] cs.foreach { case (lo, hi) => groups1.getOrElseUpdate(lo, Buffer.empty) += hi } val (ubs, others1) = groups1.toList.partition(_._2.sizeIs > 1) others1.foreach { case (k, vs) => groups2.getOrElseUpdate(vs.head, Buffer.empty) += k } val lbs = groups2.toList val bounds = (ubs.mapValues(_.reduce(_ &- _)) ++ lbs.mapValues(_.reduce(_ | _)).map(_.swap)) val processed = bounds.map { case (lo, hi) => Bounds(go(lo), go(hi)) } Constrained(go(bod), Nil, processed, Nil) // case DeclType(lvl, info) => } // }(r => s"~> $r") val res = goLike(st)(new ExpCtx(Map.empty)) if (bounds.isEmpty && tscs.isEmpty) res else Constrained(res, bounds, Nil, tscs) // goLike(st) } private var curUid: Int = 0 def nextUid: Int = { val res = curUid curUid += 1 res } } ================================================ FILE: shared/src/main/scala/mlscript/TyperDatatypes.scala ================================================ package mlscript import scala.collection.mutable import scala.collection.mutable.{Map => MutMap, Set => MutSet, Buffer, LinkedHashMap} import scala.collection.immutable.{SortedSet, SortedMap} import scala.util.chaining._ import scala.annotation.tailrec import mlscript.utils._, shorthands._ import mlscript.Message._ abstract class TyperDatatypes extends TyperHelpers { Typer: Typer => def recordTypeVars: Bool = false type TN = TypeName val TN: TypeName.type = TypeName // The data types used for type inference: case class TypeProvenance(loco: Opt[Loc], desc: Str, originName: Opt[Str] = N, isType: Bool = false) { val isOrigin: Bool = originName.isDefined def & (that: TypeProvenance): TypeProvenance = this // arbitrary; maybe should do better override def toString: Str = (if (isOrigin) "o: " else "") + "‹"+loco.fold(desc)(desc+":"+_)+"›" } type TP = TypeProvenance sealed abstract class TypeInfo /** A type for abstract classes that is used to check and throw * errors if the abstract class is being instantiated */ case class AbstractConstructor(absMths: Set[Var], isTraitWithMethods: Bool) extends TypeInfo case class VarSymbol(ty: ST, definingVar: Var) extends TypeInfo /** Some type information which may not yet be available. */ sealed abstract class LazyTypeInfo extends TypeInfo { def complete()(implicit raise: Raise): NuMember def kind: DeclKind def name: Str } /** A LazyTypeInfo whose typing has been completed. */ case class CompletedTypeInfo(member: NuMember) extends LazyTypeInfo { def complete()(implicit raise: Raise): NuMember = member def kind: DeclKind = member.kind val name: Str = member.name } /** Initialized lazy type information, to be computed soon. */ class DelayedTypeInfo(val decl: NuDecl, val outerVars: Map[Str, SimpleType]) (implicit val ctx: Ctx, val raise: Raise) extends LazyTypeInfo with DelayedTypeInfoImpl object DelayedTypeInfo { def unapply(dti: DelayedTypeInfo): S[NuDecl] = S(dti.decl) } /** A type with universally quantified type variables * (by convention, those variables of level greater than `level` are considered quantified). */ case class PolymorphicType(polymLevel: Level, body: SimpleType) // TODO add own type provenance for consistency extends SimpleType with PolymorphicTypeImpl { require(polymLevel < MaxLevel, polymLevel) val prov: TypeProvenance = body.prov lazy val level = levelBelow(polymLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = body.levelBelow(ub min polymLevel) override def toString = s"‹∀ $polymLevel. $body›" } object PolymorphicType { def mk(polymLevel: Level, body: SimpleType): SimpleType = { require(polymLevel <= MaxLevel) if (polymLevel === MaxLevel || body.level <= polymLevel) body else body.unwrapProvs match { // Q: unwrap other proxies? case PolymorphicType(lvl, bod) => PolymorphicType.mk(polymLevel min lvl, bod) // * Not very helpful (also seems to result in breaking some recursive types... not sure why): // case tv @ AssignedVariable(ty) => // PolymorphicType(polymLevel, ty) // case tv: TV if tv.level > polymLevel && tv.assignedTo.isEmpty => // PolymorphicType(polymLevel, tv.lowerBounds.foldLeft(BotType: ST)(_ | _)) case _ => PolymorphicType(polymLevel, body) } } } object SplittablePolyFun { def unapply(pt: PolymorphicType)(implicit ctx: Ctx, raise: Raise, shadows: Shadows): Opt[ST] = if (distributeForalls) pt.splitFunction else N } /** A list of constraints. */ type Constrs = List[ST -> ST] case class ConstrainedType(constraints: Constrs, body: ST) extends SimpleType { // TODO add own prov? val prov: TypeProvenance = body.prov lazy val level = children(false).iterator.map(_.level).max def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = children(false).iterator.map(_.levelBelow(ub)).max override def toString: Str = s"{$body where: ${constraints.map { case (lb, ub) => s"$lb <: $ub" }.mkString(", ")}}" } object ConstrainedType { def mk(constraints: Constrs, body: ST): ST = if (constraints.isEmpty) body else ConstrainedType(constraints, body) } /** `body.get._1`: implicit `this` parameter * `body.get._2`: actual body of the method * `body` being `None` indicates an error: * - when this MethodType is computed from `MethodSet#processInheritedMethods`, * it means two or more parent classes defined or declared the method * and the current class did not override it; * - when this MethodType is obtained from the environment, it means the method is ambiguous, * which happens when two or more unrelated classes define or declare a method with the same name. * So note that in this case, it will have more than one parent. * Note: This is some fairly brittle and error-prone logic, which would gain to be refactored. * Especially aggravating is the fact that `toPT`/`bodyPT` return `errorType` when `body` is `None`, * whereas this case should probably be checked and carefully considered in each call site. * `isInherited`: whether the method declaration comes from the intersection of multiple inherited declarations */ case class MethodType( level: Level, body: Opt[(SimpleType, SimpleType)], parents: List[TypeName], isInherited: Bool, )(val prov: TypeProvenance) { def &(that: MethodType): MethodType = { require(this.level === that.level) MethodType(level, mergeOptions(this.body, that.body)((b1, b2) => (b1._1 & b2._1, b1._2 & b2._2)), (this.parents ::: that.parents).distinct, isInherited = true)(prov) } val toPT: PolymorphicType = body.fold(PolymorphicType(MinLevel, errType))(b => PolymorphicType(level, FunctionType(singleTup(b._1), b._2)(prov))) val bodyPT: PolymorphicType = body.fold(PolymorphicType(MinLevel, errType))(b => PolymorphicType(level, ProvType(b._2)(prov))) } sealed abstract class TypeLike extends TypeLikeImpl { def unwrapProvs: TypeLike } type TL = TypeLike abstract class OtherTypeLike extends TypeLike { this: TypedTypingUnit => def self: TypedTypingUnit = this def unwrapProvs: TypeLike = this } object OtherTypeLike { def unapply(ot: OtherTypeLike): S[TypedTypingUnit] = S(ot.self) } /** A general type form (TODO: rename to AnyType). */ sealed abstract class SimpleType extends TypeLike with SimpleTypeImpl { val prov: TypeProvenance def level: Level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): SimpleType = Typer.freshenAbove(lim, this, rigidify) constructedTypes += 1 } type ST = SimpleType sealed abstract class BaseTypeOrTag extends SimpleType sealed abstract class BaseType extends BaseTypeOrTag { def compareEquiv(that: BaseType): Int = (this, that) match { case (a: TypeTag, b: TypeTag) => a.compare(b) case (a: TypeTag, _) => -1 case (_, b: TypeTag) => 1 case (_: FunctionType, _: FunctionType) => 0 case (_: FunctionType, _) => -1 case (_, _: FunctionType) => 1 case (_: ArrayType, _: ArrayType) => 0 case (_: ArrayType, _) => -1 case (_, _: ArrayType) => 1 case (_: TupleType, _: TupleType) => 0 case (_: TupleType, _) => -1 case (_, _: TupleType) => 1 case (_: Without, _: Without) => 0 case (_: Without, _) => -1 case (_, _: Without) => 1 case (_: Overload, _: Overload) => 0 case (_: Overload, _) => -1 case (_, _: Overload) => 1 case (_: SpliceType, _: SpliceType) => 0 } def toRecord: RecordType = RecordType.empty protected def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): BaseType override def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): BaseType = freshenAboveImpl(lim, rigidify) } sealed abstract class MiscBaseType extends BaseType { override def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): MiscBaseType } sealed trait Factorizable extends SimpleType type FT = FunctionType case class FunctionType(lhs: SimpleType, rhs: SimpleType)(val prov: TypeProvenance) extends MiscBaseType { lazy val level: Int = lhs.level max rhs.level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = lhs.levelBelow(ub) max rhs.levelBelow(ub) def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): FunctionType = FunctionType(lhs.freshenAbove(lim, rigidify), rhs.freshenAbove(lim, rigidify))(prov) override def toString = s"(${lhs match { case TupleType((N, FieldType(N, f: TupleType)) :: Nil) => "[" + f.showInner + "]" case TupleType((N, f) :: Nil) => f.toString case lhs => lhs }} -> $rhs)" } case class Overload(alts: Ls[FunctionType])(val prov: TypeProvenance) extends MiscBaseType { require(alts.lengthIs > 0) def mapAlts(lf: ST => ST)(rf: ST => ST): Overload = Overload(alts.map(ft => FunctionType(lf(ft.lhs), rf(ft.rhs))(ft.prov)))(prov) def mapAltsPol(pol: Opt[Bool])(f: (Opt[Bool], SimpleType) => SimpleType): Overload = Overload(alts.map(ft => FunctionType(f(pol.map(!_), ft.lhs), f(pol, ft.rhs))(ft.prov)))(prov) def mapAltsPol(pol: PolMap)(f: (PolMap, SimpleType) => SimpleType): Overload = Overload(alts.map(ft => FunctionType(f(pol.contravar, ft.lhs), f(pol, ft.rhs))(ft.prov)))(prov) def approximatePos: FunctionType = { val (lhss, rhss) = alts.map(ft => ft.lhs -> ft.rhs).unzip FunctionType(lhss.reduce(_ | _), rhss.reduce(_ | _))(prov) // * Note: technically the following is another valid (but probably less useful) // * approximation of the same function type: // FunctionType(lhss.reduce(_ & _), rhss.reduce(_ & _))(prov) } lazy val level: Level = levelBelow(MaxLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = alts.iterator.map(_.levelBelow(ub)).max def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): Overload = Overload(alts.map(_.freshenAboveImpl(lim, rigidify)))(prov) } object Overload { def mk(alts: Ls[FunctionType])(prov: TypeProvenance): ST = alts match { case Nil => mkProxy(TopType, prov) case ft :: Nil => mkProxy(ft, prov) case alts => Overload(alts)(prov) } } case class RecordType(fields: List[(Var, FieldType)])(val prov: TypeProvenance) extends SimpleType { // TODO: assert no repeated fields lazy val level: Level = levelBelow(MaxLevel)(MutSet.empty) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = fields.iterator.map(_._2.levelBelow(ub)).maxOption.getOrElse(MinLevel) override def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): RecordType = Typer.mapPol(this, N, false)((_, x) => x.freshenAbove(lim, rigidify)) def toInter: SimpleType = fields.map(f => RecordType(f :: Nil)(prov)).foldLeft(TopType: ST)(((l, r) => ComposedType(false, l, r)(noProv))) def mergeAllFields(fs: Iterable[Var -> FieldType]): RecordType = { val res = mutable.SortedMap.empty[Var, FieldType] fs.foreach(f => res.get(f._1) match { case N => res(f._1) = f._2 case S(ty) => res(f._1) = ty && f._2 }) RecordType(res.toList)(prov) } def addFields(fs: Ls[Var -> FieldType]): RecordType = { val shadowing = fs.iterator.map(_._1).toSet RecordType(fields.filterNot(f => shadowing(f._1)) ++ fs)(prov) } def sorted: RecordType = RecordType(fields.sortBy(_._1))(prov) override def toString = s"{${fields.map(f => s"${f._1.name}: ${f._2}").mkString(", ")}}" } object RecordType { def empty: RecordType = RecordType(Nil)(noProv) def mk(fields: List[(Var, FieldType)])(prov: TypeProvenance = noProv): SimpleType = if (fields.isEmpty) ExtrType(false)(prov) else RecordType(fields)(prov) } sealed abstract class ArrayBase extends MiscBaseType { def inner: FieldType } case class ArrayType(val inner: FieldType)(val prov: TypeProvenance) extends ArrayBase { def level: Level = inner.level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = inner.levelBelow(ub) def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): ArrayType = ArrayType(inner.freshenAbove(lim, rigidify))(prov) override def toString = s"Array‹$inner›" } case class TupleType(fields: List[Opt[Var] -> FieldType])(val prov: TypeProvenance) extends ArrayBase { lazy val inner: FieldType = fields.map(_._2).reduceLeftOption(_ || _).getOrElse(BotType.toUpper(noProv)) lazy val level: Level = fields.iterator.map(_._2.level).maxOption.getOrElse(0) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = fields.iterator.map(_._2.levelBelow(ub)).maxOption.getOrElse(MinLevel) def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): TupleType = TupleType(fields.mapValues(_.freshenAbove(lim, rigidify)))(prov) lazy val toArray: ArrayType = ArrayType(inner)(prov) // upcast to array override lazy val toRecord: RecordType = RecordType( fields.zipWithIndex.map { case ((_, t), i) => (Var(i.toString), t) } // Note: In line with TypeScript, tuple field names are pure type system fictions, // with no runtime existence. Therefore, they should not be included in the record type // corresponding to this tuple type. // i.e., no `::: fields.collect { case (S(n), t) => (n, t) }` )(prov) def showInner: Str = fields.map(f => s"${f._1.fold("")(_.name+": ")}${f._2},").mkString(" ") override def toString = s"($showInner)" // override def toString = s"(${fields.map(f => s"${f._1.fold("")(_+": ")}${f._2},").mkString(" ")})" } case class SpliceType(elems: Ls[Either[SimpleType, FieldType]])(val prov: TypeProvenance) extends ArrayBase { require(elems.nonEmpty) // ? – since `max` is used below... lazy val level: Int = elems.map{ case L(l) => l.level case R(r) => r.level }.max def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV,ST]): MiscBaseType = SpliceType(elems.map{ case L(l) => L(l.freshenAbove(lim, rigidify)) case R(r) => R(r.freshenAbove(lim, rigidify)) })(prov) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = elems.map{ case L(l) => l.levelBelow(ub) case R(r) => r.levelBelow(ub) }.max lazy val inner: FieldType = elems.map { case L(l) => l match { case a: ArrayBase => a.inner case _ => ??? } case R(r) => r }.reduceLeft(_ || _) def updateElems(f: SimpleType => SimpleType, g: SimpleType => SimpleType, h: SimpleType => SimpleType,newProv: TypeProvenance = prov): SpliceType = SpliceType(elems.map{case L(l) => L(f(l)) case R(r) => R(r.update(g, h))})(newProv) } /** Polarity `pol` being `true` means Bot; `false` means Top. These are extrema of the subtyping lattice. */ case class ExtrType(pol: Bool)(val prov: TypeProvenance) extends SimpleType { def level: Level = MinLevel def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = MinLevel override def toString = if (pol) "⊥" else "⊤" } /** Represents a type variable skolem that was extruded outsdie its polym level. * The goal is to retain precise information to produce good errors, * but still have this be functionally equivalent to `ExtrType(pol)`. */ case class Extruded(pol: Bool, underlying: SkolemTag)(val prov: TypeProvenance, val reason: Ls[Ls[ST]]) extends AbstractTag with TypeVarOrRigidVar { val level: Level = MinLevel val id = underlying.id def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = 0 override def toString = if (pol) s"⊥(${underlying})" else s"⊤(${underlying})" } /** Polarity `pol` being `true` means union; `false` means intersection. */ case class ComposedType(pol: Bool, lhs: SimpleType, rhs: SimpleType)(val prov: TypeProvenance) extends SimpleType { def level: Level = lhs.level max rhs.level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = lhs.levelBelow(ub) max rhs.levelBelow(ub) override def toString = s"($lhs ${if (pol) "|" else "&"} $rhs)" } case class NegType(negated: SimpleType)(val prov: TypeProvenance) extends SimpleType { def level: Level = negated.level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = negated.levelBelow(ub) override def toString = s"~(${negated})" } /** Represents a type `base` from which we have removed the fields in `names`. */ case class Without(base: SimpleType, names: SortedSet[Var])(val prov: TypeProvenance) extends MiscBaseType { def level: Int = base.level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = base.levelBelow(ub) def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): Without = Without(base.freshenAbove(lim, rigidify), names)(prov) override def toString = s"${base}\\${names.mkString("-")}" } /** A proxy type is a derived type form storing some additional information, * but which can always be converted into an underlying simple type. */ sealed abstract class ProxyType extends SimpleType { def level: Level = underlying.level def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = underlying.levelBelow(ub) def underlying: SimpleType override def toString = s"[$underlying]" } object ProxyType { def unapply(proxy: ProxyType): S[ST] = S(proxy.underlying) } /** The sole purpose of ProvType is to store additional type provenance info. */ case class ProvType(underlying: SimpleType)(val prov: TypeProvenance) extends ProxyType { override def toString = s"$underlying" // override def toString = s"[$underlying]" // override def toString = s"$underlying[${prov.desc.take(5)}]" // override def toString = s"$underlying[${prov.toString.take(5)}]" // override def toString = s"$underlying@${prov.loco.fold("?")(l => l.spanStart+"–"+l.spanEnd)}" // override def toString = showProvOver(true)(""+underlying) // TOOD override equals/hashCode? — could affect hash consing... // override def equals(that: Any): Bool = super.equals(that) || underlying.equals(that) // override def equals(that: Any): Bool = unwrapProxies.equals(that) } /** A proxy type, `S with {x: T; ...}` is equivalent to `S\x\... & {x: T; ...}`. */ case class WithType(base: SimpleType, rcd: RecordType)(val prov: TypeProvenance) extends ProxyType { lazy val underlying: ST = base.without(rcd.fields.iterator.map(_._1).toSortedSet) & rcd override def toString = s"${base} w/ ${rcd}" } type TR = TypeRef val TR: TypeRef.type = TypeRef case class TypeRef(defn: TypeName, targs: Ls[SimpleType])(val prov: TypeProvenance) extends SimpleType with TypeRefImpl { def level: Level = targs.iterator.map(_.level).maxOption.getOrElse(MinLevel) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = targs.iterator.map(_.levelBelow(ub)).maxOption.getOrElse(MinLevel) override def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): TypeRef = TypeRef(defn, targs.map(_.freshenAbove(lim, rigidify)))(prov) override def toString = showProvOver(false) { val displayName = if (primitiveTypes.contains(defn.name)) defn.name.capitalize else defn.name if (targs.isEmpty) displayName else s"$displayName[${targs.mkString(",")}]" } } sealed trait TypeTag extends BaseTypeOrTag with Ordered[TypeTag] { val id: IdentifiedTerm def compare(that: TypeTag): Int = (this, that) match { case (obj1: ObjectTag, obj2: ObjectTag) => obj1.id compare obj2.id case (SkolemTag(id1), SkolemTag(id2)) => id1 compare id2 case (Extruded(_, id1), Extruded(_, id2)) => id1 compare id2 case (_: ObjectTag, _: SkolemTag | _: Extruded) => -1 case (_: SkolemTag | _: Extruded, _: ObjectTag) => 1 case (_: SkolemTag, _: Extruded) => -1 case (_: Extruded, _: SkolemTag) => 1 } } case class ClassTag(id: SimpleTerm, parents: Set[TypeName])(val prov: TypeProvenance) extends BaseType with TypeTag with ObjectTag { def glb(that: ClassTag): Opt[ClassTag] = if (that.id === this.id) S(this) else if (that.parentsST.contains(this.id)) S(that) else if (this.parentsST.contains(that.id)) S(this) else N def lub(that: ClassTag): Set[ClassTag] = // TODO rm? it's unused if (that.id === this.id) Set.single(that) else if (that.parentsST.contains(this.id)) Set.single(this) else if (this.parentsST.contains(that.id)) Set.single(that) // else this.parentsST.union(that.parentsST) else Set(this, that) def level: Level = MinLevel def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = MinLevel def freshenAboveImpl(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): this.type = this } sealed trait TypeVarOrRigidVar extends SimpleType sealed trait ObjectTag extends TypeTag { val id: SimpleTerm val parents: Set[TypeName] lazy val parentsST = parents.iterator.map(tn => Var(tn.name)).toSet[IdentifiedTerm] override def toString = "#" + showProvOver(false)(id.idStr+s"<${parents.map(_.name).mkString(",")}>") } sealed abstract class AbstractTag extends BaseTypeOrTag with TypeTag with Factorizable case class TraitTag(id: Var, parents: Set[TypeName])(val prov: TypeProvenance) extends AbstractTag with ObjectTag { def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = MinLevel def level: Level = MinLevel } case class SkolemTag(id: TV)(val prov: TypeProvenance) extends AbstractTag with TypeVarOrRigidVar { def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = id.levelBelow(ub) val level: Level = id.level override def toString = { val str = id.mkStr // (if (id.idStr.startsWith("'")) "‘"+id.idStr.tail else id.idStr) + showLevel(level) "‘"+(if (str.startsWith("'")) str.tail else str) + (if (str.last==='\'') "_" else "") + showLevel(level) } } /** `TypeBounds(lb, ub)` represents an unknown type between bounds `lb` and `ub`. * The only way to give something such a type is to make the type part of a def or method signature, * as it will be replaced by a fresh bounded type variable upon subsumption checking (cf rigidification). */ case class TypeBounds(lb: SimpleType, ub: SimpleType)(val prov: TypeProvenance) extends SimpleType { def level: Level = lb.level max ub.level def levelBelow(ubLvl: Level)(implicit cache: MutSet[TV]): Int = lb.levelBelow(ubLvl) max ub.levelBelow(ubLvl) override def toString = s"$lb..$ub" } object TypeBounds { final def mkSimple(lb: SimpleType, ub: SimpleType, prov: TypeProvenance = noProv): SimpleType = (lb, ub) match { case (TypeBounds(lb, _), ub) => mkSimple(lb, ub, prov) case (lb, TypeBounds(_, ub)) => mkSimple(lb, ub, prov) case _ => TypeBounds(lb, ub)(prov) } final def mk(lb: SimpleType, ub: SimpleType, prov: TypeProvenance = noProv)(implicit ctx: Ctx): SimpleType = if ((lb is ub) || lb === ub || !lb.mentionsTypeBounds && !ub.mentionsTypeBounds && lb <:< ub && ub <:< lb ) lb else (lb, ub) match { case _ => mkSimple(lb, ub, prov) } /** A version of `mk` that does not check for subtyping, * to be used in type simplification code which modifies subtype bounds on the fly * (in particular, the `transform` function may replace TV bounds `TypeBound` bundles, * and creating these `TypeBound`s should NOT rely on the bounds still being there at the time * the bundle is constructed). */ final def mkSafe(lb: SimpleType, ub: SimpleType, prov: TypeProvenance = noProv)(implicit ctx: Ctx): SimpleType = if ((lb is ub) || lb === ub ) lb else (lb, ub) match { case _ => mkSimple(lb, ub, prov) } } case class FieldType(lb: Option[SimpleType], ub: SimpleType)(val prov: TypeProvenance) { def level: Int = lb.map(_.level).getOrElse(ub.level) max ub.level def levelBelow(ubLvl: Level)(implicit cache: MutSet[TV]): Level = lb.fold(MinLevel)(_.levelBelow(ubLvl)) max ub.levelBelow(ubLvl) def <:< (that: FieldType)(implicit ctx: Ctx, cache: MutMap[ST -> ST, Bool] = MutMap.empty): Bool = (that.lb.getOrElse(BotType) <:< this.lb.getOrElse(BotType)) && (this.ub <:< that.ub) def && (that: FieldType, prov: TypeProvenance = noProv): FieldType = FieldType(lb.fold(that.lb)(l => Some(that.lb.fold(l)(l | _))), ub & that.ub)(prov) def || (that: FieldType, prov: TypeProvenance = noProv): FieldType = FieldType(for {l <- lb; r <- that.lb} yield (l & r), ub | that.ub)(prov) def update(lb: SimpleType => SimpleType, ub: SimpleType => SimpleType): FieldType = FieldType(this.lb.map(lb), ub(this.ub))(prov) def freshenAbove(lim: Int, rigidify: Bool)(implicit ctx: Ctx, freshened: MutMap[TV, ST]): FieldType = update(_.freshenAbove(lim, rigidify), _.freshenAbove(lim, rigidify)) override def toString = lb.fold(s"$ub")(lb => s"mut ${if (lb === BotType) "" else lb}..$ub") } object FieldType { def mk(vi: VarianceInfo, lb: ST, ub: ST)(prov: TP): FieldType = vi match { case VarianceInfo(true, true) => FieldType(N, TopType)(prov) case VarianceInfo(true, false) => FieldType(N, ub)(prov) case VarianceInfo(false, true) => FieldType(S(lb), TopType)(prov) case VarianceInfo(false, false) => FieldType(S(lb), ub)(prov) } } val createdTypeVars: Buffer[TV] = Buffer.empty /** A type variable living at a certain polymorphism level `level`, with mutable bounds. * Invariant: Types appearing in the bounds never have a level higher than this variable's `level`. */ final class TypeVariable( val level: Level, var _lowerBounds: List[SimpleType], var _upperBounds: List[SimpleType], originalTV: Opt[TV], val nameHint: Opt[Str] = N, val recPlaceholder: Bool = false )(val prov: TypeProvenance) extends SimpleType with TypeVarOrRigidVar with Ordered[TypeVariable] with Factorizable with IdentifiedTerm { require(level <= MaxLevel) if (recordTypeVars) createdTypeVars += this // var assignedTo: Opt[ST] = N private var _assignedTo: Opt[ST] = N def assignedTo: Opt[ST] = _assignedTo def assignedTo_=(value: Opt[ST]): Unit = { require(value.forall(_.level <= level)) _assignedTo = value } val tsc: LinkedHashMap[TupleSetConstraints, Set[Int]] = LinkedHashMap.empty // * Bounds should always be disregarded when `equatedTo` is defined, as they are then irrelevant: def lowerBounds: List[SimpleType] = { require(assignedTo.isEmpty, this); _lowerBounds } def upperBounds: List[SimpleType] = { require(assignedTo.isEmpty, this); _upperBounds } def lowerBounds_=(bs: Ls[ST]): Unit = { require(assignedTo.isEmpty, this); _lowerBounds = bs } def upperBounds_=(bs: Ls[ST]): Unit = { require(assignedTo.isEmpty, this); _upperBounds = bs } private val creationRun = currentConstrainingRun def original: TV = if (currentConstrainingRun === creationRun) originalTV.getOrElse(this) else this private lazy val trueOriginal: Opt[TV] = originalTV.flatMap(_.trueOriginal.orElse(originalTV)) def levelBelow(ub: Level)(implicit cache: MutSet[TV]): Level = if (level <= ub) level else { if (cache(this)) MinLevel else { cache += this assignedTo match { case S(ty) => ty.levelBelow(ub) case N => (lowerBounds.iterator ++ upperBounds.iterator) .map(_.levelBelow(ub)).maxOption.getOrElse(MinLevel) } } } private[mlscript] val uid: Int = freshUid lazy val asTypeVar = new TypeVar(L(uid), nameHint) lazy val (asPosExtrudedTypeVar, asNegExtrudedTypeVar) = { val nme = S(ExtrusionPrefix+nameHint.getOrElse("_").dropWhile(_ === '\'')) (new TypeVar(L(freshUid), nme), new TypeVar(L(freshUid), nme)) } def compare(that: TV): Int = this.uid compare that.uid override def toString: String = (trueOriginal match { case S(to) => assert(to.nameHint === nameHint, (to.nameHint, nameHint)) to.mkStr + "_" + uid + showLevel(level) case N => showProvOver(false)(mkStr + showLevel(level)) }) + (if (assignedTo.isDefined) "#" else "") private[mlscript] def mkStr = nameHint.getOrElse("α") + uid // * `omitIrrelevantVars` omits top-level as well as quantified variable occurrences final def isRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = isPosRecursive_$(omitIrrelevantVars) || isNegRecursive_$(omitIrrelevantVars) // * Variables occurring strictly negatively in their own lower bound // * (resp. strictly positively in their own upper bound, ie contravariantly) // * are NOT recursive, as these occurrences only demonstrate "spurious" cycles // * which are easily removed. final def isPosRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = lbRecOccs_$(omitIrrelevantVars) match { case S(N | S(true)) => true case _ => false } final def isNegRecursive_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx) : Bool = ubRecOccs_$(omitIrrelevantVars) match { case S(N | S(false)) => true case _ => false } /** None: not recursive in this bound; Some(Some(pol)): polarly-recursive; Some(None): nonpolarly-recursive. * Note that if we have something like 'a :> Bot <: 'a -> Top, 'a is not truly recursive * and its bounds can actually be inlined. * Also note that unfortunately, contrary to whta I previously thought, * it is not sound to ignore quantified variables during the getVarsPol search. * indeed, we can be in freaky situations like in `ListBuild.mls` * where we have `'a :> Ls[('x, forall 'a. 'a)]`! */ private[mlscript] final def lbRecOccs_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx): Opt[Opt[Bool]] = { // println("+", this, assignedTo getOrElse lowerBounds) // assignedTo.getOrElse(TupleType(lowerBounds.map(N -> _.toUpper(noProv)))(noProv)).getVarsPol(PolMap.pos, ignoreTopLevelOccs = true).get(this) val bs = assignedTo.fold(lowerBounds)(_ :: Nil) bs.foldLeft(BotType: ST)(_ | _).getVarsPol(PolMap.pos, ignoreTopLevelOccs = omitIrrelevantVars, // ignoreQuantifiedVars = omitIrrelevantVars, ignoreQuantifiedVars = false, ).get(this) } private[mlscript] final def ubRecOccs_$(omitIrrelevantVars: Bool)(implicit ctx: Ctx): Opt[Opt[Bool]] ={ // println("-", this, assignedTo getOrElse upperBounds) // assignedTo.getOrElse(TupleType(upperBounds.map(N -> _.toUpper(noProv)))(noProv)).getVarsPol(PolMap.posAtNeg, ignoreTopLevelOccs = true).get(this) val bs = assignedTo.fold(upperBounds)(_ :: Nil) bs.foldLeft(TopType: ST)(_ & _).getVarsPol(PolMap.posAtNeg, ignoreTopLevelOccs = omitIrrelevantVars, // ignoreQuantifiedVars = omitIrrelevantVars, ignoreQuantifiedVars = false, ).get(this) // .tap(r => println(s"= $r")) } } type TV = TypeVariable private var freshCount = 0 private def freshUid: Int = { freshCount += 1; freshCount - 1 } def freshVar(p: TypeProvenance, original: Opt[TV], nameHint: Opt[Str] = N, lbs: Ls[ST] = Nil, ubs: Ls[ST] = Nil, recPlaceholder: Bool = false) (implicit lvl: Int): TypeVariable = new TypeVariable(lvl, lbs, ubs, original, nameHint, recPlaceholder)(p) def resetState(): Unit = { freshCount = 0 } type PolarVariable = (TypeVariable, Boolean) object AssignedVariable { def unapply(tv: TV): Opt[ST] = tv.assignedTo } case class NegVar(tv: TV) extends ProxyType with Factorizable { lazy val underlying: SimpleType = tv.neg() val prov = noProv } case class NegAbsTag(tt: AbstractTag) extends ProxyType with Factorizable { lazy val underlying: SimpleType = tt.neg() val prov = noProv } class TupleSetConstraints(var constraints: Ls[Ls[ST]], var tvs: Ls[(Bool, ST)]) { def updateImpl(index: Int, bound: ST)(implicit raise: Raise, ctx: Ctx) : Unit = { val u0 = constraints.flatMap { c => TupleSetConstraints.lcg(tvs(index)._1, bound, c(index)).map(tvs.zip(c)++_) } val u = u0.map { x => x.groupMap(_._1)(_._2).map { case (u@(p,_),l) => (u,l.reduce((x,y) => ComposedType(!p,x,y)(noProv))) } } if (!u.isEmpty) { tvs.values.map(_.unwrapProxies).foreach { case tv: TV => tv.tsc += this -> Set.empty case _ => () } tvs = u.flatMap(_.keys).distinct constraints = tvs.map(x => u.map(_.getOrElse(x,if (x._1) TopType else BotType))).transpose tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { case (tv: TV, i) => tv.tsc.updateWith(this)(_.map(_ + i).orElse(S(Set(i)))) case _ => () } } else { constraints = Nil } } def updateOn(index: Int, bound: ST)(implicit raise: Raise, ctx: Ctx) : Unit = { updateImpl(index, bound) println(s"TSC update: $tvs in $constraints") } } object TupleSetConstraints { def lcgField(pol: Bool, first: FieldType, rest: FieldType)(implicit ctx: Ctx) : Opt[Ls[(Bool, ST) -> ST]] = { for { ubm <- lcg(pol, first.ub, rest.ub) lbm <- { if (first.lb.isEmpty && rest.lb.isEmpty) S(Nil) else lcg(!pol, first.lb.getOrElse(BotType), rest.lb.getOrElse(BotType)) } } yield { ubm ++ lbm } } def lcg(pol: Bool, first: ST, rest: ST)(implicit ctx: Ctx) : Opt[Ls[(Bool, ST) -> ST]] = (first.unwrapProxies, rest.unwrapProxies) match { case (a, ExtrType(p)) if p =/= pol => S(Nil) case (a, ComposedType(p,l,r)) if p =/= pol => for { lm <- lcg(pol,a,l) rm <- lcg(pol,a,r) } yield { lm ++ rm } case (a: TV, b: TV) if a.compare(b) === 0 => S(Nil) case (a: TV, b) => S(List((pol, first) -> rest)) case (a, b: TV) => S(List((pol, first) -> rest)) case (a: FT, b: FT) => lcgFunction(pol, a, b) case (a: ArrayType, b: ArrayType) => lcgField(pol, a.inner, b.inner) case (a: TupleType, b: TupleType) if a.fields.sizeCompare(b.fields) === 0 => val fs = a.fields.map(_._2).zip(b.fields.map(_._2)).map(u => lcgField(pol, u._1, u._2)) if (!fs.contains(N)) { S(fs.flatten.reduce(_++_)) } else N case (a: TupleType, b: RecordType) if pol => lcg(pol, a.toRecord, b) case (a: RecordType, b: RecordType) => val default = FieldType(N, if (pol) TopType else BotType)(noProv) if (b.fields.map(_._1).forall(a.fields.map(_._1).contains)) { val u = a.fields.map { case (v, f) => lcgField(pol, f, b.fields.find(_._1 === v).fold(default)(_._2)) } if (!u.contains(N)) { S(u.flatten.reduce(_++_)) } else N } else N case (a, b) if a === b => S(Nil) case (a, b) => val dnf = DNF.mk(MaxLevel, Nil, if (pol) a & b.neg() else b & a.neg(), true) if (dnf.isBot) S(Nil) else if (dnf.cs.forall(c => !(c.vars.isEmpty && c.nvars.isEmpty))) S(List((pol, first) -> rest)) else N } def lcgFunction(pol: Bool, first: FT, rest: FT)(implicit ctx: Ctx) : Opt[Ls[(Bool, ST) -> ST]] = { for { lm <- lcg(!pol, first.lhs, rest.lhs) rm <- lcg(pol, first.rhs, rest.rhs) } yield { lm ++ rm } } def mk(ov: Overload, f: FT)(implicit raise: Raise, ctx: Ctx): Opt[TupleSetConstraints] = { val u = ov.alts.flatMap(lcgFunction(false, f, _)).map { x => x.groupMap(_._1)(_._2).map { case (u@(p,_),l) => (u,l.reduce((x,y) => ComposedType(!p,x,y)(noProv))) } } if (u.isEmpty) { return N } val tvs = u.flatMap(_.keys).distinct val m = tvs.map(x => u.map(_.getOrElse(x,if (x._1) TopType else BotType))) val tsc = new TupleSetConstraints(m.transpose, tvs) tvs.values.map(_.unwrapProxies).zipWithIndex.foreach { case (tv: TV, i) => tv.tsc.updateWith(tsc)(_.map(_ + i).orElse(S(Set(i)))) case _ => () } println(s"TSC mk: ${tsc.tvs} in ${tsc.constraints}") S(tsc) } } } ================================================ FILE: shared/src/main/scala/mlscript/TyperHelpers.scala ================================================ package mlscript import scala.collection.mutable.{Map => MutMap, SortedMap => MutSortMap, Set => MutSet, LinkedHashMap, LinkedHashSet} import scala.collection.immutable.{SortedMap, SortedSet} import scala.annotation.tailrec import mlscript.utils._, shorthands._ /** Inessential methods used to help debugging. */ abstract class TyperHelpers { Typer: Typer => type CompareRecTypes >: Bool protected var constrainCalls = 0 protected var annoyingCalls = 0 protected var subtypingCalls = 0 protected var constructedTypes = 0 def stats: (Int, Int, Int, Int) = (constrainCalls, annoyingCalls, subtypingCalls, constructedTypes) def resetStats(): Unit = { constrainCalls = 0 annoyingCalls = 0 subtypingCalls = 0 constructedTypes = 0 } protected val noPostTrace: Any => String = _ => "" protected var indent = 0 def trace[T](pre: => String)(thunk: => T)(post: T => String = noPostTrace): T = { println(pre) indent += 1 val res = try thunk finally indent -= 1 if (post isnt noPostTrace) println(post(res)) res } @inline def traceNot[T](pre: => String)(thunk: => T)(post: T => String = noPostTrace): T = thunk def emitDbg(str: String): Unit = scala.Predef.println(str) // Shadow Predef functions with debugging-flag-enabled ones: def println(msg: => Any): Unit = if (dbg) emitDbg("| " * indent + msg) /** A more advanced println version to show where things are printed from. */ // def println(msg: => Any)(implicit file: sourcecode.FileName, line: sourcecode.Line): Unit = // if (dbg) { // emitDbg((if (showPrintPrefix) { // val prefix = s"[${file.value}:${line.value}]" // prefix + " " * (30 - prefix.length) // } else "") + "| " * indent + msg) // } // val showPrintPrefix = // // false // true def dbg_assert(assertion: => Boolean): Unit = if (dbg) scala.Predef.assert(assertion) // def dbg_assert(assertion: Boolean): Unit = scala.Predef.assert(assertion) final def printPol(pol: Bool): Str = pol match { case true => "+" case false => "-" } final def printPol(pol: Opt[Bool]): Str = pol match { case S(p) => printPol(p) case N => "=" } def printPol(pol: PolMap): Str = printPol(pol.base) def recordIntersection(fs1: Ls[Var -> FieldType], fs2: Ls[Var -> FieldType]): Ls[Var -> FieldType] = mergeMap(fs1, fs2)(_ && _).toList def recordUnion(fs1: Ls[Var -> FieldType], fs2: Ls[Var -> FieldType]): Ls[Var -> FieldType] = { val fs2m = fs2.toMap fs1.flatMap { case (k, v) => fs2m.get(k).map(v2 => k -> (v || v2)) } } /** Note that this version of `subst` intentionally substitutes unhygienically * over the outer polymorphic type, as needed by the class typing infrastructure. */ def subst(ts: PolymorphicType, map: Map[SimpleType, SimpleType]) (implicit ctx: Ctx): PolymorphicType = PolymorphicType(ts.polymLevel, subst(ts.body, map)) def substLike(ty: TL, map: Map[SimpleType, SimpleType], substInMap: Bool)(implicit ctx: Ctx): TL = ty match { case ty: ST => subst(ty, map, substInMap) case OtherTypeLike(ot) => TypedTypingUnit( ot.implementedMembers.map(_.map(subst(_, map, substInMap))), ot.result.map(subst(_, map, substInMap))) } def subst(st: SimpleType, map: Map[SimpleType, SimpleType], substInMap: Bool = false) (implicit ctx: Ctx): SimpleType = { val cache: MutMap[TypeVariable, SimpleType] = MutMap.empty implicit val freshened: MutMap[TV, ST] = MutMap.empty val subsLvl: Level = map.valuesIterator.map(_.level).reduceOption(_ max _).getOrElse(MinLevel) def go(st: SimpleType): SimpleType = { // trace(s"subst($st)") { map.get(st) match { case S(res) => if (substInMap) go(res) else res case N => st match { case tv @ AssignedVariable(ty) => cache.getOrElse(tv, { val v = freshVar(tv.prov, S(tv), tv.nameHint)(tv.level) cache += tv -> v v.assignedTo = S(go(ty)) v }) case tv: TypeVariable if tv.lowerBounds.isEmpty && tv.upperBounds.isEmpty => cache += tv -> tv tv case tv: TypeVariable => cache.getOrElse(tv, { val v = freshVar(tv.prov, S(tv), tv.nameHint)(tv.level) cache += tv -> v v.lowerBounds = tv.lowerBounds.map(go(_)) v.upperBounds = tv.upperBounds.map(go(_)) v }) case poly: PolymorphicType if poly.polymLevel < subsLvl => go(poly.raiseLevelToImpl(subsLvl, Set.empty)) case _ => st.map(go(_)) } } // }(r => s"= $r") } go(st) } /** Substitutes only at the syntactic level, without updating type variables nor traversing their bounds. */ def substSyntax(st: SimpleType)(map: PartialFunction[SimpleType, SimpleType]): SimpleType = // trace(s"substSyntax $st") { map.applyOrElse[ST, ST](st, _.map(substSyntax(_)(map))) // }(r => s"=> $r") def tupleIntersection(fs1: Ls[Opt[Var] -> FieldType], fs2: Ls[Opt[Var] -> FieldType]): Ls[Opt[Var] -> FieldType] = { require(fs1.size === fs2.size) (fs1 lazyZip fs2).map { case ((S(n1), t1), (S(n2), t2)) if n1 =/= n2 => (N, t1 && t2) case ((no1, t1), (no2, t2)) => (no1 orElse no2, t1 && t2) } } def tupleUnion(fs1: Ls[Opt[Var] -> FieldType], fs2: Ls[Opt[Var] -> FieldType]): Ls[Opt[Var] -> FieldType] = { require(fs1.size === fs2.size) (fs1 lazyZip fs2).map { case ((S(n1), t1), (S(n2), t2)) => (Option.when(n1 === n2)(n1), t1 || t2) case ((no1, t1), (no2, t2)) => (N, t1 || t2) } } def factorize(cs: Ls[Conjunct], sort: Bool): Ls[ST] = { val factors = MutMap.empty[Factorizable, Int] cs.foreach { c => c.vars.foreach { v => factors(v) = factors.getOrElse(v, 0) + 1 } c.nvars.foreach { v => val nv = NegVar(v) factors(nv) = factors.getOrElse(nv, 0) + 1 } c.lnf match { case LhsTop => () case LhsRefined(_, ttags, _, _) => ttags.foreach { ttg => factors(ttg) = factors.getOrElse(ttg, 0) + 1 } } c.rnf match { case RhsBot | _: RhsField => () case RhsBases(ps, _, _) => ps.foreach { case ttg: AbstractTag => val nt = NegAbsTag(ttg) factors(nt) = factors.getOrElse(nt, 0) + 1 case _ => () } } } factors.maxByOption(_._2) match { // case S((fact, n)) => // Very strangely, this seems to improve some StressTrait tests slightly... case S((fact, n)) if n > 1 => val (factored, rest) = fact match { case v: TV => cs.partitionMap(c => if (c.vars(v)) L(c) else R(c)) case NegVar(v) => cs.partitionMap(c => if (c.nvars(v)) L(c) else R(c)) case ttg: AbstractTag => cs.partitionMap(c => if (c.lnf.hasTag(ttg)) L(c) else R(c)) case NegAbsTag(ttg) => cs.partitionMap(c => if (c.rnf.hasTag(ttg)) L(c) else R(c)) } (fact & factorize(factored.map(_ - fact), sort).reduce(_ | _)) :: ( if (factors.sizeCompare(1) > 0 && factors.exists(f => (f._1 isnt fact) && f._2 > 1)) factorize(rest, sort) else rest.map(_.toType(sort)) ) case _ => cs.map(_.toType(sort)) } } private def cleanupUnion(tys: Ls[ST])(implicit ctx: Ctx): Ls[ST] = { var res: Ls[ST] = Nil tys.reverseIterator.foreach { ty => if (!res.exists(ty <:< _)) res ::= ty } res } def factorize(ctx: Ctx)(ty: ST): ST = { cleanupUnion(ty.components(true))(ctx) match { case Nil => BotType case ty :: Nil => ty case cs => factorizeImpl(cs.map(_.components(false))) } } def factorizeImpl(cs: Ls[Ls[ST]]): ST = trace(s"factorize? ${cs.map(_.mkString(" & ")).mkString(" | ")}") { def rebuild(cs: Ls[Ls[ST]]): ST = cs.iterator.map(_.foldLeft(TopType: ST)(_ & _)).foldLeft(BotType: ST)(_ | _) if (cs.sizeCompare(1) <= 0) return rebuild(cs) val factors = LinkedHashMap.empty[Factorizable, Int] cs.foreach { c => c.foreach { case tv: TV => factors(tv) = factors.getOrElse(tv, 0) + 1 case tt: AbstractTag => factors(tt) = factors.getOrElse(tt, 0) + 1 case nv: NegVar => factors(nv) = factors.getOrElse(nv, 0) + 1 case nt: NegAbsTag => factors(nt) = factors.getOrElse(nt, 0) + 1 case _ => } } println(s"Factors ${factors.mkString(", ")}") factors.maxByOption(_._2) match { // case S((fact, n)) => case S((fact, n)) if n > 1 => val (factored, rest) = cs.partitionMap(c => if (c.contains(fact)) L(c) else R(c)) println(s"Factor $fact -> ${factored.mkString(", ")}") assert(factored.size === n, factored -> n) val factoredFactored = fact & factorizeImpl(factored.map(_.filterNot(_ === fact))) val restFactored = if (factors.sizeCompare(1) > 0 && factors.exists(f => (f._1 isnt fact) && f._2 > 1)) factorizeImpl(rest) else rebuild(rest) restFactored | factoredFactored case _ => rebuild(cs) } }(r => s"yes: $r") def mapPol(rt: RecordType, pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType): RecordType = RecordType(rt.fields.mapValues(_.update(f(pol.map(!_), _), f(pol, _))))(rt.prov) def mapPol(bt: BaseType, pol: Opt[Bool], smart: Bool)(f: (Opt[Bool], SimpleType) => SimpleType): BaseType = bt match { case FunctionType(lhs, rhs) => FunctionType(f(pol.map(!_), lhs), f(pol, rhs))(bt.prov) case ov @ Overload(alts) => ov.mapAltsPol(pol)(f) case TupleType(fields) => TupleType(fields.mapValues(_.update(f(pol.map(!_), _), f(pol, _))))(bt.prov) case ArrayType(inner) => ArrayType(inner.update(f(pol.map(!_), _), f(pol, _)))(bt.prov) case sp @SpliceType(elems) => sp.updateElems(f(pol, _), f(pol.map(!_), _), f(pol, _)) case wt @ Without(b: ComposedType, ns @ EmptyColl()) => Without(b.map(f(pol, _)), ns)(wt.prov) // FIXME very hacky case Without(base, names) => Without(f(pol, base), names)(bt.prov) case _: ClassTag => bt } trait SimpleTypeImpl { self: SimpleType => // TODO eventually find a way of statically getting rid of those def assertTV: TV = this match { case tv: TV => tv case _ => lastWords(s"$this was not a type variable") } def showProvOver(enabled: Bool)(str: Str): Str = if (enabled) str + prov.toString else str // Note: we implement hashCode and equals manually because: // 1. On one hand, we want a ProvType to compare equal to its underlying type, // which is necessary for recursive types to associate type provenances to // their recursive uses without making the constraint solver diverge; and // 2. Additionally, caching hashCode should have performace benefits // — though I'm not sure whether it's best as a `lazy val` or a `val`. override lazy val hashCode: Int = this match { case tv: TypeVariable => tv.uid case ProvType(und) => und.hashCode case p: Product => scala.runtime.ScalaRunTime._hashCode(p) } override def equals(that: Any): Bool = // trace(s"$this == $that") { this match { case ProvType(und) => (und: Any) === that case tv1: TV => that match { case tv2: Typer#TV => tv1.uid === tv2.uid case ProvType(und) => this === und case _ => false } case p1: Product => that match { case that: ST => that match { case ProvType(und) => this === und case tv: TV => false case p2: Product => p1.canEqual(p2) && p1.productArity === p2.productArity && { val it1 = p1.productIterator val it2 = p2.productIterator while(it1.hasNext && it2.hasNext) { if (it1.next() =/= it2.next()) return false } return !it1.hasNext && !it2.hasNext } } case _ => false } } // }(r => s"= $r") private val initialRun = currentConstrainingRun private var shadowRun = initialRun - 1 private var _shadow: ST = this private def computeShadow: ST = this match { case tv: TV => tv.original // * Q: no special tratment for assigned TVs? case _ => map(_.shadow) } def shadow: ST = if (currentConstrainingRun === shadowRun) _shadow else { _shadow = computeShadow shadowRun = currentConstrainingRun _shadow } def map(f: SimpleType => SimpleType): SimpleType = this match { case TypeBounds(lb, ub) => TypeBounds.mkSimple(f(lb), f(ub)) case FunctionType(lhs, rhs) => FunctionType(f(lhs), f(rhs))(prov) case ov @ Overload(as) => ov.mapAltsPol(N)((_, x) => f(x)) case RecordType(fields) => RecordType(fields.mapValues(_.update(f, f)))(prov) case TupleType(fields) => TupleType(fields.mapValues(_.update(f, f)))(prov) case sp @ SpliceType(fs) => sp.updateElems(f, f, f) case ArrayType(inner) => ArrayType(inner.update(f, f))(prov) case ComposedType(pol, lhs, rhs) => ComposedType(pol, f(lhs), f(rhs))(prov) case NegType(negated) => NegType(f(negated))(prov) case Without(base, names) => Without(f(base), names)(prov) case ProvType(underlying) => ProvType(f(underlying))(prov) case WithType(bse, rcd) => WithType(f(bse), RecordType(rcd.fields.mapValues(_.update(f, f)))(rcd.prov))(prov) case ProxyType(underlying) => f(underlying) // TODO different? case TypeRef(defn, targs) => TypeRef(defn, targs.map(f(_)))(prov) case PolymorphicType(plvl, und) => PolymorphicType(plvl, f(und)) case ConstrainedType(cs, bod) => ConstrainedType(cs.map(lu => f(lu._1) -> f(lu._2)), f(bod)) case _: TypeVariable | _: TypeTag | _: ExtrType => this } def mapPol(pol: Opt[Bool], smart: Bool = false)(f: (Opt[Bool], SimpleType) => SimpleType) (implicit ctx: Ctx): SimpleType = this match { case TypeBounds(lb, ub) if smart && pol.isDefined => if (pol.getOrElse(die)) f(S(true), ub) else f(S(false), lb) case TypeBounds(lb, ub) => TypeBounds.mkSimple(f(S(false), lb), f(S(true), ub)) case rt: RecordType => Typer.mapPol(rt, pol, smart)(f) case Without(base, names) if smart => f(pol, base).without(names) case bt: BaseType => Typer.mapPol(bt, pol, smart)(f) case ComposedType(kind, lhs, rhs) if smart => if (kind) f(pol, lhs) | f(pol, rhs) else f(pol, lhs) & f(pol, rhs) case ComposedType(kind, lhs, rhs) => ComposedType(kind, f(pol, lhs), f(pol, rhs))(prov) case NegType(negated) if smart => f(pol.map(!_), negated).neg(prov) case NegType(negated) => NegType(f(pol.map(!_), negated))(prov) case ProvType(underlying) => ProvType(f(pol, underlying))(prov) case WithType(bse, rcd) => WithType(f(pol, bse), RecordType(rcd.fields.mapValues(_.update(f(pol.map(!_), _), f(pol, _))))(rcd.prov))(prov) case ProxyType(underlying) => f(pol, underlying) // TODO different? case tr @ TypeRef(defn, targs) => TypeRef(defn, tr.mapTargs(pol)(f))(prov) case PolymorphicType(plvl, und) => if (smart) PolymorphicType.mk(plvl, f(pol, und)) else PolymorphicType(plvl, f(pol, und)) case ConstrainedType(cs, bod) => ConstrainedType(cs.map(lu => f(S(true), lu._1) -> f(S(false), lu._2)), f(pol, bod)) case _: TypeVariable | _: TypeTag | _: ExtrType => this } def toUpper(prov: TypeProvenance): FieldType = FieldType(None, this)(prov) def toLower(prov: TypeProvenance): FieldType = FieldType(Some(this), TopType)(prov) def toBoth(prov: TypeProvenance): FieldType = FieldType(S(this), this)(prov) def | (that: SimpleType, prov: TypeProvenance = noProv, swapped: Bool = false): SimpleType = (this, that) match { case (TopType, _) => this case (BotType, _) => that case (_, TopType) => that case (_, BotType) => this case (Extruded(true, sk), _) => that case (Extruded(false, sk), _) => TopType // These were wrong! During constraint solving it's important to keep them! // case (_: RecordType, _: PrimType | _: FunctionType) => TopType // case (_: FunctionType, _: PrimType | _: RecordType) => TopType case (_: RecordType, _: FunctionType) => TopType case (RecordType(fs1), RecordType(fs2)) => RecordType(recordUnion(fs1, fs2))(prov) case (t0 @ TupleType(fs0), t1 @ TupleType(fs1)) // If the sizes are different, to merge these we'd have to return // the awkward `t0.toArray & t0.toRecord | t1.toArray & t1.toRecord` if fs0.sizeCompare(fs1) === 0 => TupleType(tupleUnion(fs0, fs1))(t0.prov) case _ if !swapped => that | (this, prov, swapped = true) case (`that`, _) => this case (NegType(`that`), _) => TopType case _ => ComposedType(true, that, this)(prov) } /** This is to intersect two types that occur in negative position, * where it may be sound to perform some online simplifications/approximations. */ def &- (that: SimpleType, prov: TypeProvenance = noProv): SimpleType = (this, that) match { // * This one is only correct in negative position because // * the existence of first-class polymorphic types makes it unsound in positive positions. case (FunctionType(l1, r1), FunctionType(l2, r2)) if approximateNegativeFunction => FunctionType(l1 | l2, r1 &- r2)(prov) case _ => this & (that, prov) } def & (that: SimpleType, prov: TypeProvenance = noProv, swapped: Bool = false): SimpleType = (this, that) match { case (TopType | RecordType(Nil), _) => that case (BotType, _) => BotType // * Unnecessary and can complicate constraint solving quite a lot: // case (ComposedType(true, l, r), _) => l & that | r & that // * Tempting, but wrong: this is only valid in positive positions! // * (in which case the LBs are irrelevant anyway) // * For, instance consider `((A..A) & (B..B)) -> C` in positive position, // * which is `(A & B) -> C` and obviously not `(A | B) -> C`. // case (TypeBounds(l0, u0), TypeBounds(l1, u1)) => // TypeBounds(l0 | l1, u0 & u1)(prov) case (_: ClassTag, _: FunctionType) => BotType case (RecordType(fs1), RecordType(fs2)) => RecordType(mergeSortedMap(fs1, fs2)(_ && _).toList)(prov) case (t0 @ TupleType(fs0), t1 @ TupleType(fs1)) => if (fs0.sizeCompare(fs1) =/= 0) BotType else TupleType(tupleIntersection(fs0, fs1))(t0.prov) case _ if !swapped => that & (this, prov, swapped = true) case (`that`, _) => this case _ if !swapped => that & (this, prov, swapped = true) case (NegType(`that`), _) => BotType case _ => ComposedType(false, that, this)(prov) } def neg(prov: TypeProvenance = noProv, force: Bool = false): SimpleType = this match { case ExtrType(b) => ExtrType(!b)(noProv) case ComposedType(true, l, r) => l.neg() & r.neg() case ComposedType(false, l, r) if force => l.neg() | r.neg() case NegType(n) => n // case _: RecordType => BotType // Only valid in positive positions! // Because Top<:{x:S}|{y:T}, any record type negation neg{x:S}<:{y:T} for any y=/=x, // meaning negated records are basically bottoms. case _ => NegType(this)(prov) } /** This is used to know when two types can be assumed to be mutual subtypes * based on a simple equality check. This may not hold when the types involve `TypeBound`s. * Indeed, say `type Foo[A] = A -> A`; * then `Foo[bot..top]` is an alias for `(bot..top) -> (bot..top)` * which IN POSITIVE POSITION is equivalent to `bot -> top` * and IN NEGATIVE POSITION is equivalent to `top -> bot`. * Therefore, while syntactically `Foo[bot..top] === Foo[bot..top]`, * we DO NOT have that `Foo[bot..top] <: Foo[bot..top]`. * This check is still a little wrong in that we don't look into type definitions, * someone may register type definitions whose bodies involve `TypeBound`s... Though * it seems the typer should desugar these bounds when psosible and reject them otherwise. * OTOH, note that we truly don't have to look into type variable bounds because these * bounds are maintained consistent. */ lazy val mentionsTypeBounds: Bool = this match { case _: TypeBounds => true case _ => children(includeBounds = false).exists(_.mentionsTypeBounds) } def >:< (that: SimpleType)(implicit ctx: Ctx, crt: CompareRecTypes = true): Bool = this <:< that && that <:< this // TODO for composed types and negs, should better first normalize the inequation def <:< (that: SimpleType) (implicit ctx: Ctx, crt: CompareRecTypes = true, cache: MutMap[ST -> ST, Bool] = MutMap.empty): Bool = { // trace(s"? $this <: $that") { // trace(s"? $this <: $that assuming: ${ // cache.iterator.map(kv => "" + kv._1._1 + (if (kv._2) " <: " else " ST, Bool] => R): R = if (doCompareRecTypes) k(cache.map(kv => kv._1 -> true)) else k(cache) if (!mentionsTypeBounds && ((this is that) || this === that)) return true (this, that) match { case (_, ExtrType(false)) | (ExtrType(true), _) => true case (ProxyType(und), _) => und <:< that case (_, ProxyType(und)) => this <:< und // * Leads to too much simplification in printed types: // case (ClassTag(ErrTypeId, _), _) | (_, ClassTag(ErrTypeId, _)) => true case (_: TypeTag, _: TypeTag) | (_: TV, _: TV) if this === that => true case (ab: ArrayBase, at: ArrayType) => ab.inner <:< at.inner case (TupleType(fs1), TupleType(fs2)) => fs1.sizeCompare(fs2) === 0 && fs1.lazyZip(fs2).forall { case ((_, ty1), (_, ty2)) => ty1 <:< ty2 } case (RecordType(Nil), _) => TopType <:< that case (_, RecordType(Nil)) => this <:< TopType case (pt1 @ ClassTag(id1, ps1), pt2 @ ClassTag(id2, ps2)) => (id1 === id2) || pt1.parentsST(id2) case (TypeBounds(lb, ub), _) => ub <:< that case (_, TypeBounds(lb, ub)) => this <:< lb case (FunctionType(l1, r1), FunctionType(l2, r2)) => assume { implicit cache => l2 <:< l1 && r1 <:< r2 } case (ComposedType(true, l, r), _) => l <:< that && r <:< that case (_, ComposedType(false, l, r)) => this <:< l && this <:< r case (ComposedType(false, l, r), _) => l <:< that || r <:< that case (_, ComposedType(true, l, r)) => this <:< l || this <:< r case (RecordType(fs1), RecordType(fs2)) => assume { implicit cache => fs2.forall(f => fs1.find(_._1 === f._1).exists(_._2 <:< f._2)) } // * Field removal is special // * and can't be compared without taking polarity into account... case (_, _: Without) => false /* case (Without(bs1, ns1), Without(bs2, ns2)) => ns1.forall(ns2) && bs1 <:< that case (_, Without(bs, ns)) => this <:< bs */ case (_: TypeVariable, _) | (_, _: TypeVariable) if !doCompareRecTypes => false case (_: TypeVariable, _) | (_, _: TypeVariable) if cache.contains(this -> that) => cache(this -> that) case (tv: TypeVariable, _) => cache(this -> that) = false val tmp = tv.assignedTo match { case S(ty) => ty <:< that case N => tv.upperBounds.exists(_ <:< that) } if (tmp) cache(this -> that) = true tmp case (_, tv: TypeVariable) => cache(this -> that) = false val tmp = tv.assignedTo match { case S(ty) => this <:< ty case N => tv.lowerBounds.exists(this <:< _) } if (tmp) cache(this -> that) = true tmp case (ConstrainedType(Nil, bod), rhs) => bod <:< that case (_, ConstrainedType(cs, bod)) => this <:< bod // could assume cs here case (_, NegType(und)) => (this & und) <:< BotType case (NegType(und), _) => TopType <:< (that | und) case (tr: TypeRef, _) if (primitiveTypes contains tr.defn.name) && tr.canExpand => tr.expandOrCrash <:< that case (_, tr: TypeRef) if (primitiveTypes contains tr.defn.name) && tr.canExpand => this <:< tr.expandOrCrash case (tr1: TypeRef, _) => ctx.tyDefs.get(tr1.defn.name) match { case S(td1) => that match { case tr2: TypeRef if tr2.defn === tr1.defn => val tvv = td1.getVariancesOrDefault td1.tparamsargs.unzip._2.lazyZip(tr1.targs).lazyZip(tr2.targs).forall { (tv, targ1, targ2) => val v = tvv(tv) (v.isContravariant || targ1 <:< targ2) && (v.isCovariant || targ2 <:< targ1) } case _ => (td1.kind is Cls) && clsNameToNomTag(td1)(noProv, ctx) <:< that } case N => false // TODO look into ctx.tyDefs2 } case (_, _: TypeRef) => false // TODO try to expand them (this requires populating the cache because of recursive types) case (_: PolymorphicType, _) | (_, _: PolymorphicType) => false case (_, ov: Overload) => ov.alts.forall(this <:< _) case (ov: Overload, _) => ov.alts.exists(_ <:< that) case (_: ConstrainedType, _) => false case (_: Without, _) | (_: ArrayBase, _) | (_, _: ArrayBase) | (_: AbstractTag, _) | (_, _: AbstractTag) => false // don't even try case (_: FunctionType, _) | (_, _: FunctionType) => false case (_: RecordType, _: TypeTag) | (_: TypeTag, _: RecordType) => false case (_, ExtrType(true)) | (ExtrType(false), _) => false // not sure whether LHS <: Bot (or Top <: RHS) // case _ => lastWords(s"TODO $this $that ${getClass} ${that.getClass()}") } // }(r => s"! $r") } def isTop(implicit ctx: Ctx): Bool = TopType <:< this def isBot(implicit ctx: Ctx): Bool = this <:< BotType // * Sometimes, Without types are temporarily pushed to the RHS of constraints, // * sometimes behind a single negation, // * just for the time of massaging the constraint through a type variable. // * So it's important we only push and simplify Without types only in _positive_ position! def without(names: SortedSet[Var]): SimpleType = if (names.isEmpty) this else this match { case Without(b, ns) => Without(b, ns ++ names)(this.prov) case _ => Without(this, names)(noProv) } def without(name: Var): SimpleType = without(SortedSet.single(name)) def withoutPos(names: SortedSet[Var]): SimpleType = if (names.isEmpty) this else this match { case Without(b, ns) => Without(b, ns ++ names)(this.prov) case t @ FunctionType(l, r) => t case t @ ComposedType(true, l, r) => l.without(names) | r.without(names) case t @ ComposedType(false, l, r) => l.without(names) & r.without(names) case t @ RecordType(fs) => RecordType(fs.filter(nt => !names(nt._1)))(t.prov) case t @ TupleType(fs) => val relevantNames = names.filter(n => n.toIndexOption.exists((0 until fs.length).contains)) if (relevantNames.isEmpty) t else { val rcd = t.toRecord rcd.copy(fields = rcd.fields.filterNot(_._1 |> relevantNames))(rcd.prov) } case t @ ArrayType(ar) => t case t @ SpliceType(fs) => ??? // TODO case n @ NegType(_ : ClassTag | _: FunctionType | _: RecordType) => n case n @ NegType(nt) if (nt match { case _: ComposedType | _: ExtrType | _: NegType => true // case c: ComposedType => c.pol // case _: ExtrType | _: NegType => true case _ => false }) => nt.neg(n.prov, force = true).withoutPos(names) case e @ ExtrType(_) => e // valid? -> seems we could go both ways, but this way simplifies constraint solving // case e @ ExtrType(false) => e case p @ ProvType(und) => ProvType(und.withoutPos(names))(p.prov) case p @ ProxyType(und) => und.withoutPos(names) case p: TypeTag => p case TypeBounds(lo, hi) => hi.withoutPos(names) case _: TypeVariable | _: NegType | _: TypeRef => Without(this, names)(noProv) case PolymorphicType(plvl, bod) => PolymorphicType.mk(plvl, bod.withoutPos(names)) case ot: Overload => ot case ct: ConstrainedType => ct } def unwrapAll(implicit ctx: Ctx): SimpleType = unwrapProxies match { case tr: TypeRef if tr.canExpand => tr.expandOrCrash.unwrapAll case u => u } def negNormPos(f: SimpleType => SimpleType, p: TypeProvenance) (implicit ctx: Ctx, ptr: PreserveTypeRefs): SimpleType = (if (preserveTypeRefs) this else unwrapAll) match { case ExtrType(b) => ExtrType(!b)(noProv) case ComposedType(true, l, r) => l.negNormPos(f, p) & r.negNormPos(f, p) case ComposedType(false, l, r) => l.negNormPos(f, p) | r.negNormPos(f, p) case NegType(n) => f(n).withProv(p) case tr: TypeRef if !preserveTypeRefs && tr.canExpand => tr.expandOrCrash.negNormPos(f, p) case rw => NegType(f(rw))(p) } def withProvOf(ty: SimpleType): ST = withProv(ty.prov) def withProv(p: TypeProvenance): ST = mkProxy(this, p) def pushPosWithout(implicit ctx: Ctx, ptr: PreserveTypeRefs): SimpleType = this match { case NegType(n) => n.negNormPos(_.pushPosWithout, prov) case Without(b, ns) => if (ns.isEmpty) b.pushPosWithout else (if (preserveTypeRefs) b.unwrapProxies else b.unwrapAll).withoutPos(ns) match { case Without(c @ ComposedType(pol, l, r), ns) => ComposedType(pol, l.withoutPos(ns), r.withoutPos(ns))(c.prov) case Without(NegType(nt), ns) => nt.negNormPos(_.pushPosWithout, nt.prov).withoutPos(ns) match { case rw @ Without(NegType(nt), ns) => nt match { case _: TypeVariable | _: ClassTag | _: RecordType => rw case _ => if (preserveTypeRefs) rw else lastWords(s"$this $rw (${nt.getClass})") } case rw => rw } case rw => rw } case _ => this } def abs(that: SimpleType)(prov: TypeProvenance): SimpleType = FunctionType(this, that)(prov) def unwrapProxies: SimpleType = this match { case ProxyType(und) => und.unwrapProxies case _ => this } def unwrapProvs: SimpleType = this match { case ProvType(und) => und.unwrapProvs case _ => this } /** Used for error reporting. */ def typeBase: SimpleType = unwrapProxies match { case Without(und, ns) => und.typeBase case PolymorphicType(pl, bod) => bod.typeBase case TypeBounds(lb, ub) => ub.typeBase case _ => this } def components(union: Bool): Ls[ST] = this match { case ExtrType(`union`) => Nil case ComposedType(`union`, l, r) => l.components(union) ::: r.components(union) case NegType(tv: TypeVariable) if !union => NegVar(tv) :: Nil case NegType(tt: AbstractTag) if !union => NegAbsTag(tt) :: Nil case ProvType(und) => und.components(union) case _ => this :: Nil } /** (exclusive, inclusive) */ def varsBetween(lb: Level, ub: Level): Set[TV] = { val res = MutSet.empty[TypeVariable] val traversed = MutSet.empty[TypeVariable] def go(ty: ST, lb: Level, ub: Level): Unit = if (lb < ub && ty.level > lb) { // * Don't traverse types with lower levels // trace(s"varsBetween($ty, $lb, $ub)") { ty match { case tv: TypeVariable => if (traversed(tv)) () else { traversed += tv if (tv.level > lb && tv.level <= ub) { // println(s"ADD $tv") res += tv } tv.children(includeBounds = true) // * Note: `children` deals with `assignedTo` .foreach(go(_, lb, ub)) } case pt: PolymorphicType => go(pt.body, lb, pt.polymLevel min ub) case ty => ty.children(includeBounds = true) // * Q: is `includeBounds` useful here? .foreach(go(_, lb, ub)) } // }() } go(this, lb, ub) res.toSet } def expPos(implicit ctx: Ctx): mlscript.TypeLike = exp(S(true), this) def expNeg(implicit ctx: Ctx): mlscript.TypeLike = exp(S(false), this) def exp(pol: Opt[Bool], ty: ST)(implicit ctx: Ctx): mlscript.TypeLike = ( ty // |> (_.normalize(false)) // |> (simplifyType(_, pol, removePolarVars = false, inlineBounds = false)) // |> (shallowCopy) |> (subst(_, Map.empty)) // * Make a copy of the type and its TV graph – although we won't show the TV bounds, we still care about the bounds as they affect class type reconstruction in normalizeTypes_! |> (normalizeTypes_!(_, pol)(ctx)) |> (expandType(_, stopAtTyVars = true)) ) } trait TypeLikeImpl { self: TypeLike => def childrenPol(pol: PolMap)(implicit ctx: Ctx): List[PolMap -> SimpleType] = { def childrenPolField(pol: PolMap)(fld: FieldType): List[PolMap -> SimpleType] = fld.lb.map(pol.contravar -> _).toList ::: pol.covar -> fld.ub :: Nil def childrenPolMem(m: NuMember): List[PolMap -> SimpleType] = m match { case NuParam(nme, ty, pub) => childrenPolField(PolMap.pos)(ty) // TODO invariant when mutable case TypedNuFun(level, fd, ty) => pol -> ty :: Nil case td: TypedNuDecl => TypedTypingUnit(td :: Nil, N).childrenPol(pol: PolMap) // TODO refactor // case NuTypeParam(nme, ty) => childrenPolField(PolMap.pos)(ty) } this match { case tv @ AssignedVariable(ty) => pol -> ty :: Nil case tv: TypeVariable => val poltv = pol(tv) (if (poltv =/= S(false)) tv.lowerBounds.map(pol.at(tv.level, true) -> _) else Nil) ::: (if (poltv =/= S(true)) tv.upperBounds.map(pol.at(tv.level, false) -> _) else Nil) ++ tv.tsc.keys.flatMap(_.tvs).map(u => pol.at(tv.level,u._1) -> u._2) case FunctionType(l, r) => pol.contravar -> l :: pol.covar -> r :: Nil case Overload(as) => as.map(pol -> _) case ComposedType(_, l, r) => pol -> l :: pol -> r :: Nil case RecordType(fs) => fs.unzip._2.flatMap(childrenPolField(pol)) case TupleType(fs) => fs.unzip._2.flatMap(childrenPolField(pol)) case ArrayType(fld) => childrenPolField(pol)(fld) case SpliceType(elems) => elems flatMap {case L(l) => pol -> l :: Nil case R(r) => childrenPolField(pol)(r)} case NegType(n) => pol.contravar -> n :: Nil case ExtrType(_) => Nil case ProxyType(und) => pol -> und :: Nil // case _: TypeTag => Nil case _: ObjectTag | _: Extruded => Nil case SkolemTag(id) => pol -> id :: Nil case tr: TypeRef => tr.mapTargs(pol)(_ -> _) case Without(b, ns) => pol -> b :: Nil case TypeBounds(lb, ub) => PolMap.neg -> lb :: PolMap.pos -> ub :: Nil case PolymorphicType(_, und) => pol -> und :: Nil case ConstrainedType(cs, bod) => cs.flatMap(vbs => PolMap.pos -> vbs._1 :: PolMap.posAtNeg -> vbs._2 :: Nil) ::: pol -> bod :: Nil case OtherTypeLike(tu) => // tu.entities.flatMap(_.childrenPol) ::: tu.result.toList val ents = tu.implementedMembers.flatMap { case ta: TypedNuAls => // Q: PolMap.neu or pol.invar?! ta.tparams.map(pol.invar -> _._2) ::: pol -> ta.body :: Nil case tf: TypedNuFun => PolMap.pos -> tf.bodyType :: Nil case mxn: TypedNuMxn => mxn.tparams.iterator.map(pol.invar -> _._2) ++ mxn.members.valuesIterator.flatMap(childrenPolMem) ++ S(pol.contravar -> mxn.superTy) ++ S(pol.contravar -> mxn.thisTy) case cls: TypedNuCls => cls.tparams.iterator.map(pol.invar -> _._2) ++ // cls.params.flatMap(p => childrenPolField(pol.invar)(p._2)) cls.params.toList.flatMap(_.flatMap(p => childrenPolField(PolMap.pos)(p._2))) ++ cls.auxCtorParams.toList.flatMap(_.map(PolMap.neg -> _._2)) ++ cls.members.valuesIterator.flatMap(childrenPolMem) ++ S(pol.contravar -> cls.thisTy) ++ S(pol.covar -> cls.sign) ++ // S(pol.covar -> cls.instanceType) ++ // Not a real child; to remove cls.parentTP.valuesIterator.flatMap(childrenPolMem) case trt: TypedNuTrt => trt.tparams.iterator.map(pol.invar -> _._2) ++ trt.members.valuesIterator.flatMap(childrenPolMem) ++ S(pol.contravar -> trt.thisTy) ++ S(pol.covar -> trt.sign) ++ trt.parentTP.valuesIterator.flatMap(childrenPolMem) case prm: NuParam => childrenPolField(pol)(prm.ty) case TypedNuDummy(d) => N } ents ::: tu.result.toList.map(pol -> _) }} /** `ignoreTopLevelOccs` is used to discard immediate occurrences of a variable which * would constitute spurious recursive occurrences when traversing the variable's bounds. */ def getVarsPol(pol: PolMap, ignoreTopLevelOccs: Bool = false, ignoreQuantifiedVars: Bool = false) (implicit ctx: Ctx) : SortedMap[TypeVariable, Opt[Bool]] = { val res = MutMap.empty[TV, Pol] val traversed = MutSet.empty[TV -> Bool] // * We ignore variables above `upperLvl` when `ignoreQuantifiedVars` is true. def go(pol: PolMap, ignoreTLO: Bool, upperLvl: Level)(ty: TypeLike): Unit = { // trace(s"getVarsPol[${printPol(pol.base)}] $ty ${pol} $ignoreTLO") { ty match { case tv: TypeVariable => if (ignoreQuantifiedVars && tv.level > upperLvl) return println(s"Quantified! ${tv}") val tvpol = pol(tv.level) if (!ignoreTLO) res.updateWith(tv) { case S(p) if p =/= tvpol => // println(s"> $tv: $tvpol =/= $p ~> S(N)") S(N) case _ => // println(s"> $tv: $tvpol") S(tvpol) } val needsTraversing = tvpol match { case S(p) => !traversed(tv -> p) && { traversed += tv -> p true } case N => !(traversed(tv -> true) && traversed(tv -> false)) && { traversed += tv -> true traversed += tv -> false true } } // println(s"$tv ${printPol(tvpol)} $needsTraversing") if (needsTraversing) tv.childrenPol(pol) // * Note: `childrenPol` deals with `assignedTo` .foreach(cp => go(cp._1, ignoreTLO, upperLvl)(cp._2)) case ProxyType(und) => go(pol, ignoreTLO, upperLvl)(und) case Overload(as) => as.foreach(go(pol, ignoreTLO, upperLvl)) case NegType(n) => go(pol.contravar, ignoreTLO, upperLvl)(n) case Without(b, ns) => go(pol, ignoreTLO, upperLvl)(b) case TypeBounds(lb, ub) => go(PolMap.neg, ignoreTLO, upperLvl)(lb) go(PolMap.pos, ignoreTLO, upperLvl)(ub) // * or simply: // pol.traverseRange(lb, ub)(go(_, ignoreTLO, upperLvl)(_)) case ConstrainedType(cs, bod) => cs.foreach { vbs => go(PolMap.pos, false, upperLvl)(vbs._1) go(PolMap.posAtNeg, false, upperLvl)(vbs._2) } go(pol, ignoreTLO, upperLvl)(bod) case ComposedType(p, l, r) => go(pol, ignoreTLO, upperLvl)(l) go(pol, ignoreTLO, upperLvl)(r) case pt: PolymorphicType => go(pol.enter(pt.polymLevel), ignoreTLO, upperLvl min pt.polymLevel)(pt.body) case ty => ty.childrenPol(pol).foreach(cp => go(cp._1, // * We should have handled above all top-level cases, // * so the children here are not supposed to be top level. // * Note: We won't get unsoundness if we forgot cases, // * just spurious occurs-check failures! ignoreTLO = false, upperLvl)(cp._2)) } // }() } go(pol, ignoreTopLevelOccs, MaxLevel)(this) res.toSortedMap } private def childrenMem(m: NuMember): IterableOnce[ST] = m match { case tf: TypedNuFun => tf.bodyType :: Nil case als: TypedNuAls => als.tparams.iterator.map(_._2) ++ S(als.body) case mxn: TypedNuMxn => mxn.tparams.iterator.map(_._2) ++ mxn.members.valuesIterator.flatMap(childrenMem) ++ S(mxn.superTy) ++ S(mxn.thisTy) case cls: TypedNuCls => cls.tparams.iterator.map(_._2) ++ cls.params.toList.flatMap(_.flatMap(p => p._2.lb.toList ::: p._2.ub :: Nil)) ++ cls.auxCtorParams.toList.flatMap(_.values) ++ cls.members.valuesIterator.flatMap(childrenMem) ++ S(cls.thisTy) ++ S(cls.sign) case trt: TypedNuTrt => trt.tparams.iterator.map(_._2) ++ trt.members.valuesIterator.flatMap(childrenMem) ++ S(trt.thisTy) ++ S(trt.sign) ++ trt.parentTP.valuesIterator.flatMap(childrenMem) case p: NuParam => p.ty.lb.toList ::: p.ty.ub :: Nil case TypedNuDummy(d) => Nil } def children(includeBounds: Bool): List[SimpleType] = this match { case tv @ AssignedVariable(ty) => if (includeBounds) ty :: Nil else Nil case tv: TypeVariable => if (includeBounds) tv.lowerBounds ::: tv.upperBounds ++ tv.tsc.keys.flatMap(_.tvs.values) else Nil case FunctionType(l, r) => l :: r :: Nil case Overload(as) => as case ComposedType(_, l, r) => l :: r :: Nil case RecordType(fs) => fs.flatMap(f => f._2.lb.toList ::: f._2.ub :: Nil) case TupleType(fs) => fs.flatMap(f => f._2.lb.toList ::: f._2.ub :: Nil) case ArrayType(inner) => inner.lb.toList ++ (inner.ub :: Nil) case NegType(n) => n :: Nil case ExtrType(_) => Nil case ProxyType(und) => und :: Nil // case _: TypeTag => Nil case _: ObjectTag | _: Extruded => Nil case SkolemTag(id) => id :: Nil case TypeRef(d, ts) => ts case Without(b, ns) => b :: Nil case TypeBounds(lb, ub) => lb :: ub :: Nil case PolymorphicType(_, und) => und :: Nil case ConstrainedType(cs, und) => cs.flatMap(lu => lu._1 :: lu._2 :: Nil) ::: und :: Nil case SpliceType(fs) => fs.flatMap{ case L(l) => l :: Nil case R(r) => r.lb.toList ::: r.ub :: Nil} case OtherTypeLike(tu) => val ents = tu.implementedMembers.flatMap(childrenMem) ents ::: tu.result.toList } def getVarsImpl(includeBounds: Bool): SortedSet[TypeVariable] = { val res = MutSet.empty[TypeVariable] @tailrec def rec(queue: List[TypeLike]): Unit = queue match { case (tv: TypeVariable) :: tys => if (res(tv)) rec(tys) else { res += tv; rec(tv.children(includeBounds = includeBounds) ::: tys) } case ty :: tys => rec(ty.children(includeBounds = includeBounds) ::: tys) case Nil => () } rec(this :: Nil) SortedSet.from(res)(Ordering.by(_.uid)) } def getVars: SortedSet[TypeVariable] = getVarsImpl(includeBounds = true) def showBounds: String = getVars.iterator.filter(tv => tv.assignedTo.nonEmpty || (tv.upperBounds ++ tv.lowerBounds).nonEmpty).map { case tv @ AssignedVariable(ty) => "\n\t\t" + tv.toString + " := " + ty case tv => ("\n\t\t" + tv.toString + (if (tv.lowerBounds.isEmpty) "" else " :> " + tv.lowerBounds.mkString(" | ")) + (if (tv.upperBounds.isEmpty) "" else " <: " + tv.upperBounds.mkString(" & "))) }.mkString + { val visited: MutSet[TupleSetConstraints] = MutSet.empty getVars.iterator.flatMap(_.tsc).map { case (tsc, i) => if (visited.add(tsc)) ("\n\t\t[ " + tsc.tvs.map(t => s"${printPol(t._1)}${t._2}").mkString(", ") + " ] in { " + tsc.constraints.mkString(", ") + " }") else "" }.mkString } } trait PolymorphicTypeImpl { self: PolymorphicType => def instantiate(implicit ctx: Ctx): SimpleType = { implicit val state: MutMap[TV, ST] = MutMap.empty println(s"INST [${polymLevel}] $this") println(s" where ${showBounds}") val res = body.freshenAbove(polymLevel, rigidify = false) println(s"TO [${lvl}] ~> $res") println(s" where ${res.showBounds}") res } def rigidify(implicit ctx: Ctx, raise: Raise): SimpleType = { implicit val state: MutMap[TV, ST] = MutMap.empty body.freshenAbove(polymLevel, rigidify = true) } def raiseLevelTo(newPolymLevel: Level, leaveAlone: Set[TV] = Set.empty) (implicit ctx: Ctx): PolymorphicType = { implicit val freshened: MutMap[TV, ST] = MutMap.empty raiseLevelToImpl(newPolymLevel, leaveAlone) } def raiseLevelToImpl(newPolymLevel: Level, leaveAlone: Set[TV]) (implicit ctx: Ctx, freshened: MutMap[TV, ST]): PolymorphicType = { require(newPolymLevel >= polymLevel) if (newPolymLevel === polymLevel) return this PolymorphicType(newPolymLevel, Typer.freshenAbove(polymLevel, body, leaveAlone = leaveAlone)( ctx.copy(lvl = newPolymLevel + 1), // * Q: is this really fine? cf. stashing/unstashing etc. freshened) ) //(prov) } /** Tries to split a polymorphic function type * by distributing the quantification of *some* of its type vars into the function result. */ def splitFunction(implicit ctx: Ctx, raise: Raise): Opt[ST] = { def go(ty: ST, traversed: Set[AnyRef], polymLevel: Level): Opt[ST] = ty match { case ft @ FunctionType(par, bod) => val couldBeDistribbed = bod.varsBetween(polymLevel, MaxLevel) println(s"could be distribbed: $couldBeDistribbed") if (couldBeDistribbed.isEmpty) return N val cannotBeDistribbed = par.varsBetween(polymLevel, MaxLevel) println(s"cannot be distribbed: $cannotBeDistribbed") val canBeDistribbed = couldBeDistribbed -- cannotBeDistribbed if (canBeDistribbed.isEmpty) return N // TODO val newInnerLevel = (polymLevel + 1) max cannotBeDistribbed.maxByOption(_.level).fold(MinLevel)(_.level) val innerPoly = PolymorphicType(polymLevel, bod) println(s"inner: ${innerPoly}") val res = FunctionType(par, innerPoly.raiseLevelTo(newInnerLevel, cannotBeDistribbed))(ft.prov) println(s"raised: ${res}") println(s" where: ${res.showBounds}") if (cannotBeDistribbed.isEmpty) S(res) else S(PolymorphicType(polymLevel, res)) case tr: TypeRef if !traversed.contains(tr.defn) => go(tr.expand, traversed + tr.defn, polymLevel) case proxy: ProxyType => go(proxy.underlying, traversed, polymLevel) case tv @ AssignedVariable(ty) if !traversed.contains(tv) => go(ty, traversed + tv, polymLevel) case tv: TV if tv.level > polymLevel && !traversed.contains(tv) => // * A quantified variable in positive position can always be replaced by its LBs go(tv.lowerBounds.foldLeft(BotType: ST)(_ | _), traversed + tv, polymLevel) case PolymorphicType(plvl, bod) => go(bod, traversed, polymLevel min plvl) case _ => N } go(body, Set.empty, polymLevel) } } trait TypeRefImpl { self: TypeRef => def canExpand(implicit ctx: Ctx): Bool = ctx.tyDefs2.get(defn.name).forall(info => // * Object types do not need to be completed in order to be expanded info.kind.isInstanceOf[ObjDefKind] || info.isComputed) def expand(implicit ctx: Ctx, raise: Raise): SimpleType = { ctx.tyDefs2.get(defn.name) match { case S(info) => if (!info.kind.isInstanceOf[ObjDefKind]) { info.complete() if (info.result.isEmpty) // * This can only happen if completion yielded an error return errType } case N => } expandWith(paramTags = true, selfTy = true) } def expandOrCrash(implicit ctx: Ctx): SimpleType = { require(canExpand) expandWith(paramTags = true, selfTy = true) } def expandWith(paramTags: Bool, selfTy: Bool)(implicit ctx: Ctx): SimpleType = ctx.tyDefs2.get(defn.name).map { info => lazy val mkTparamRcd = RecordType(info.tparams.lazyZip(targs).map { case ((tn, tv, vi), ta) => val fldNme = defn.name + "#" + tn.name // TODO also use computed variance info when available! Var(fldNme).withLocOf(tn) -> FieldType.mk(vi.getOrElse(VarianceInfo.in), ta, ta)(provTODO) })(provTODO) info.result match { case S(td: TypedNuAls) => assert(td.tparams.size === targs.size) subst(td.body, td.tparams.lazyZip(targs).map { case (tp, ta) => SkolemTag(tp._2)(noProv) -> ta }.toMap) case S(td: TypedNuTrt) => assert(td.tparams.size === targs.size) // println(s"EXP ${td.sign}") val (freshenMap, _) = refreshHelper2(td, Var(td.name).withLoc(prov.loco), S(targs)) // infer ty args if not provided val freshSelf = if (!selfTy) TopType else { implicit val freshened: MutMap[TV, ST] = freshenMap implicit val shadows: Shadows = Shadows.empty td.sign.freshenAbove(td.level, rigidify = false) } // println(s"Fresh $freshSelf") freshSelf & trtNameToNomTag(td.decl)(provTODO, ctx) & mkTparamRcd case S(td: TypedNuCls) => assert(td.tparams.size === targs.size) val (freshenMap, _) = refreshHelper2(td, Var(td.name).withLoc(prov.loco), S(targs)) // infer ty args if not provided val freshSelf = if (!selfTy) TopType else { implicit val freshened: MutMap[TV, ST] = freshenMap implicit val shadows: Shadows = Shadows.empty td.sign.freshenAbove(td.level, rigidify = false) } clsNameToNomTag(td.decl)(provTODO, ctx) & freshSelf & mkTparamRcd case _ => // * Case for when the type has not been completed yet info.decl match { case td: NuTypeDef if td.kind.isInstanceOf[ClsLikeKind] => // TODO in the future, add the self signature to DelayedTypeInfo and use it here assert(td.tparams.size === targs.size) clsNameToNomTag(td)(provTODO, ctx) & mkTparamRcd case td: NuTypeDef if td.kind is Trt => assert(td.tparams.size === targs.size) trtNameToNomTag(td)(provTODO, ctx) & mkTparamRcd case td: NuTypeDef if td.kind is Als => // * Definition was not forced yet, which indicates an error (hopefully) lastWords("cannot expand unforced type alias") case d => // * Other kinds of type defs are not allowed to be used as types // * (an error should have been reported earlier) errType } } }.getOrElse { val td = ctx.tyDefs(defn.name) require(targs.size === td.tparamsargs.size) lazy val tparamTags = if (paramTags) RecordType.mk(td.tparamsargs.map { case (tp, tv) => val tvv = td.getVariancesOrDefault tparamField(defn, tp) -> FieldType( Some(if (tvv(tv).isCovariant) BotType else tv), if (tvv(tv).isContravariant) TopType else tv)(prov) })(noProv) else TopType subst(td.kind match { case Als => td.bodyTy case Mod => throw new NotImplementedError("Namespaces are not supported yet.") case Cls => clsNameToNomTag(td)(prov, ctx) & td.bodyTy & tparamTags case Trt => trtNameToNomTag(td)(prov, ctx) & td.bodyTy & tparamTags case Mxn => lastWords("mixins cannot be used as types") }, td.targs.lazyZip(targs).toMap) //.withProv(prov) } //tap { res => println(s"Expand $this => $res") } private var tag: Opt[Opt[ClassTag]] = N def expansionFallback(implicit ctx: Ctx): Opt[ST] = mkClsTag /** Note: self types can be inherited from parents if the definition is abstract * (if it is not abstract, then the class is already known to subtype the inherited self-type) */ def mayHaveTransitiveSelfType(implicit ctx: Ctx): Bool = ctx.tyDefs2.get(defn.name) match { case S(lti) => lti.decl match { case td: NuTypeDef if !td.isAbstract => td.sig.nonEmpty case _ => true } case _ => true } def mkClsTag(implicit ctx: Ctx): Opt[ClassTag] = tag.getOrElse { val res = ctx.tyDefs.get(defn.name) match { case S(td: TypeDef) if (td.kind is Cls) || (td.kind is Mod) => S(clsNameToNomTag(td)(noProv, ctx)) case S(td: TypeDef) if td.kind is Trt => N case _ => ctx.tyDefs2.get(defn.name) match { case S(lti) => lti.decl match { case td: NuTypeDef if (td.kind is Cls) || (td.kind is Mod) => S(clsNameToNomTag(td)(noProv, ctx)) case _ => N } case _ => N } } tag = S(res) res } def mapTargs[R](pol: Opt[Bool])(f: (Opt[Bool], ST) => R)(implicit ctx: Ctx): Ls[R] = { // TODO factor w/ below val (tvarVariances, tparamsargs) = ctx.tyDefs.get(defn.name) match { case S(td) => (td.tvarVariances, td.tparamsargs) case N => val td = ctx.tyDefs2(defn.name) (N, td.tparams.map(tp => (tp._1, tp._2))) } tvarVariances.fold(targs.map(f(N, _))) { tvv => assert(tparamsargs.sizeCompare(targs) === 0) (tparamsargs lazyZip targs).map { case ((_, tv), ta) => tvv(tv) match { case VarianceInfo(true, true) => f(N, TypeBounds(BotType, TopType)(noProv)) case VarianceInfo(co, contra) => f(if (co) pol else if (contra) pol.map(!_) else N, ta) } }} } // TODO dedup w/ above def mapTargs[R](pol: PolMap)(f: (PolMap, ST) => R)(implicit ctx: Ctx): Ls[R] = { val (tvarVariances, tparamsargs) = ctx.tyDefs.get(defn.name) match { case S(td) => (td.tvarVariances, td.tparamsargs) case N => val td = ctx.tyDefs2.getOrElse(defn.name, // * This should only happen in the presence of ill-formed type definitions; // * TODO: Make sure to report this and raise a compiler internal error if the source // * does not actually have a type error! Otherwise we could silently get wrong results... return Nil ) // TODO use computed varces (some(td.explicitVariances), td.tparams.map(tp => (tp._1, tp._2))) } tvarVariances.fold(targs.map(f(pol.invar, _))) { tvv => assert(tparamsargs.sizeCompare(targs) === 0) (tparamsargs lazyZip targs).map { case ((_, tv), ta) => tvv(tv) match { case VarianceInfo(true, true) => f(pol.invar, TypeBounds(BotType, TopType)(noProv)) case VarianceInfo(co, contra) => f(if (co) pol else if (contra) pol.contravar else pol.invar, ta) } }} } } def merge(pol: Bool, ts: Ls[ST]): ST = if (pol) ts.foldLeft(BotType: ST)(_ | _) else ts.foldLeft(TopType: ST)(_ & _) class Traverser(implicit ctx: Ctx) { def apply(pol: Opt[Bool])(st: ST): Unit = st match { case tv @ AssignedVariable(ty) => apply(pol)(ty) case tv: TypeVariable => if (pol =/= S(false)) tv.lowerBounds.foreach(apply(S(true))) if (pol =/= S(true)) tv.upperBounds.foreach(apply(S(false))) case FunctionType(l, r) => apply(pol.map(!_))(l); apply(pol)(r) case Overload(as) => as.foreach(apply(pol)) case ComposedType(_, l, r) => apply(pol)(l); apply(pol)(r) case RecordType(fs) => fs.unzip._2.foreach(applyField(pol)) case TupleType(fs) => fs.unzip._2.foreach(applyField(pol)) case ArrayType(fld) => applyField(pol)(fld) case SpliceType(elems) => elems foreach {case L(l) => apply(pol)(l) case R(r) => applyField(pol)(r)} case NegType(n) => apply(pol.map(!_))(n) case ExtrType(_) => () case ProxyType(und) => apply(pol)(und) case SkolemTag(id) => apply(pol)(id) case _: TypeTag => () case tr: TypeRef => tr.mapTargs(pol)(apply(_)(_)); () case Without(b, ns) => apply(pol)(b) case TypeBounds(lb, ub) => if (pol =/= S(true)) apply(S(false))(lb) if (pol =/= S(false)) apply(S(true))(ub) case PolymorphicType(plvl, und) => apply(pol)(und) case ConstrainedType(cs, bod) => cs.foreach { case (lo, hi) => apply(S(true))(lo) apply(S(false))(hi) } apply(pol)(bod) } def applyField(pol: Opt[Bool])(fld: FieldType): Unit = { fld.lb.foreach(apply(pol.map(!_))) apply(pol)(fld.ub) } } object Traverser { trait InvariantFields extends Traverser { override def applyField(pol: Opt[Bool])(fld: FieldType): Unit = if (fld.lb.exists(_ === fld.ub)) apply(N)(fld.ub) else super.applyField(pol)(fld) } } class Traverser2(implicit ctx: Ctx) { def applyLike(pol: PolMap)(ty: TypeLike): Unit = ty match { case ty: ST => apply(pol)(ty) case OtherTypeLike(tu) => tu.implementedMembers.foreach(applyMem(pol)) tu.result.foreach(apply(pol)) } def applyMem(pol: PolMap)(m: NuMember): Unit = m match { case TypedNuAls(level, td, tparams, body) => tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) apply(pol)(body) case TypedNuCls(level, td, tparams, params, acParams, members, thisTy, sign, _, ptps) => tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) params.foreach(_.foreach(p => applyField(pol)(p._2))) acParams.foreach(_.foreach(p => apply(pol.contravar)(p._2))) members.valuesIterator.foreach(applyMem(pol)) apply(pol.contravar)(thisTy) apply(pol.contravar)(sign) ptps.valuesIterator.foreach(applyMem(pol)) case TypedNuTrt(level, td, tparams, members, thisTy, sign, _, ptps) => tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) members.valuesIterator.foreach(applyMem(pol)) apply(pol.contravar)(thisTy) apply(pol.covar)(sign) ptps.valuesIterator.foreach(applyMem(pol)) case TypedNuMxn(level, td, thisTy, superTy, tparams, params, members) => tparams.iterator.foreach(tp => apply(pol.invar)(tp._2)) params.foreach(p => applyField(pol)(p._2)) members.valuesIterator.foreach(applyMem(pol)) apply(pol.contravar)(thisTy) apply(pol.contravar)(superTy) case NuParam(nme, ty, pub) => applyField(pol)(ty) case TypedNuFun(level, fd, ty) => apply(pol)(ty) case TypedNuDummy(d) => () // case NuTypeParam(nme, ty) => applyField(pol)(ty) } def apply(pol: PolMap)(st: ST): Unit = st match { case tv @ AssignedVariable(ty) => apply(pol)(ty) case tv: TypeVariable => val poltv = pol(tv) if (poltv =/= S(false)) tv.lowerBounds.foreach(apply(pol.at(tv.level, true))) if (poltv =/= S(true)) tv.upperBounds.foreach(apply(pol.at(tv.level, false))) tv.tsc.keys.flatMap(_.tvs).foreach(u => apply(pol.at(tv.level,u._1))(u._2)) case FunctionType(l, r) => apply(pol.contravar)(l); apply(pol)(r) case Overload(as) => as.foreach(apply(pol)) case ComposedType(_, l, r) => apply(pol)(l); apply(pol)(r) case RecordType(fs) => fs.unzip._2.foreach(applyField(pol)) case TupleType(fs) => fs.unzip._2.foreach(applyField(pol)) case ArrayType(fld) => applyField(pol)(fld) case SpliceType(elems) => elems foreach {case L(l) => apply(pol)(l) case R(r) => applyField(pol)(r)} case NegType(n) => apply(pol.contravar)(n) case ExtrType(_) => () case ProxyType(und) => apply(pol)(und) // case _: TypeTag => () case _: ObjectTag | _: Extruded => () case SkolemTag(id) => apply(pol)(id) case tr: TypeRef => tr.mapTargs(pol)(apply(_)(_)); () case Without(b, ns) => apply(pol)(b) case TypeBounds(lb, ub) => pol.traverseRange(lb, ub)(apply(_)(_)) case PolymorphicType(plvl, und) => apply(pol.enter(plvl))(und) case ConstrainedType(cs, bod) => cs.foreach { case (lo, hi) => apply(PolMap.pos)(lo) apply(PolMap.posAtNeg)(hi) } apply(pol)(bod) } def applyField(pol: PolMap)(fld: FieldType): Unit = { fld.lb.foreach(apply(pol.contravar)) apply(pol)(fld.ub) } } object Traverser2 { trait InvariantFields extends Traverser2 { override def applyField(pol: PolMap)(fld: FieldType): Unit = if (fld.lb.exists(_ === fld.ub)) apply(pol.invar)(fld.ub) else super.applyField(pol)(fld) } } object PolyFunction { def unapply(ty: ST)(implicit ctx: Ctx, raise: Raise, shadows: Shadows): Opt[PolymorphicType] = { def go(ty: ST, traversed: Set[AnyRef]): Opt[PolymorphicType] = //trace(s"go $ty") { if (!distributeForalls) N else ty match { case poly @ PolymorphicType(plvl, bod) => S(poly) case tr: TypeRef if !traversed.contains(tr.defn) => go(tr.expand, traversed + tr.defn) case proxy: ProxyType => go(proxy.underlying, traversed) case tv @ AssignedVariable(ty) if !traversed.contains(tv) => go(ty, traversed + tv) case ft @ FunctionType(param, funbod) => for { poly @ PolymorphicType(plvl, bod) <- go(funbod, traversed) } yield { val newRhs = if (param.level > plvl) { val poly2 = poly.raiseLevelTo(param.level) PolymorphicType(poly2.polymLevel, FunctionType(param, poly2.body)(ft.prov)) } else PolymorphicType(plvl, FunctionType(param, bod)(ft.prov)) newRhs } case _ => N } // }(res => s"= $res") go(ty, Set.empty) } } object AliasOf { def unapply(ty: ST)(implicit ctx: Ctx): S[ST] = { def go(ty: ST, traversedVars: Set[TV]): S[ST] = ty match { case tr: TypeRef if tr.canExpand => go(tr.expandOrCrash, traversedVars) case proxy: ProxyType => go(proxy.underlying, traversedVars) case tv @ AssignedVariable(ty) if !traversedVars.contains(tv) => go(ty, traversedVars + tv) case _ => S(ty) } go(ty, Set.empty) } } protected def showLevel(level: Level): Str = (if (level === MaxLevel) "^" else if (level > 5 ) "^" + level else "'" * level) /** This class helps keep track of the *relative polarity* of different [olymorphism levels. * Polarity is relative because polymorphic types can exist at arbitrary polarities, * yet the type variables they quantify have their polarity determined by their local * use in the polymorphic type. * Things get quite involved once type ranges are involved. */ abstract class PolMap(val base: Pol) { outer => private val ctx = 0 private val lvl = 0 def apply(lvl: Level): Pol def quantifPolarity(lvl: Level): PolMap final def apply(tv: TV): Pol = apply(tv.level) def enter(polymLvl: Level): PolMap = new PolMap(base) { def apply(lvl: Level): Pol = if (lvl > polymLvl) S(true) else outer(lvl) def quantifPolarity(lvl: Level): PolMap = if (lvl > polymLvl) this else outer.quantifPolarity(lvl) def show: Str = s"$outer;Q($lvl)" } def invar: PolMap = new PolMap(N) { def apply(lvl: Level): Pol = N def quantifPolarity(lvl: Level): PolMap = outer.quantifPolarity(lvl) def show: Str = s"$outer;=" } def covar: PolMap = this def contravar: PolMap = new PolMap(base.map(!_)) { def apply(lvl: Level): Pol = outer(lvl).map(!_) def quantifPolarity(lvl: Level): PolMap = outer.quantifPolarity(lvl) def show: Str = s"$outer;-" } /** Used to traverse type variable bounds. * The tricky part is that when a TV is quantified negatively, * then from the POV of lower-level variables, * the polarity of its upper bound is actually POSITIVE! */ def at(atLvl: Level, pol: Bool): PolMap = new PolMap(base) { val pm = quantifPolarity(atLvl) def apply(lvl: Level): Pol = // * Everything above or at `atLvl` gets the new polarity pol; // * things under it get the new polarity unless atLvl is quantified negatively, // * in which case they get the opposite polarity !pol. if (lvl >= atLvl) S(pol) else pm(lvl) match { case S(true) => S(pol) case S(false) => S(!pol) case N => N } def quantifPolarity(lvl: Level): PolMap = outer.quantifPolarity(lvl) def show: Str = s"$outer;@[${printPol(S(pol))}]($lvl)" } def traverseRange(lb: ST, ub: ST)(k: (PolMap, ST) => Unit): Unit = { if (base =/= S(true)) k(PolMap.neg, lb) if (base =/= S(false)) k(PolMap.pos, ub) } // * Note: We used to have this weird impelmentation, // * which was not consistent with the other places in the code where // * we traversed TypeBound instances... // * I am still unsure what's the proper way of traversing them // * and it's possible there was some truth in this implementation, // * but I no longer remember how it was justified. /* def traverseRange(lb: ST, ub: ST)(k: (PolMap, ST) => Unit): Unit = { def polma(p: Bool) = new PolMap(S(p)) { def apply(lvl: Level): Pol = outer.quantifPolarity(lvl).base match { case N => N case S(true) => S(p) case S(false) => S(!p) } def quantifPolarity(lvl: Level): PolMap = outer.quantifPolarity(lvl) def show: Str = s"$outer;R${printPol(S(p))}" } if (base =/= S(true)) k(polma(false), lb) if (base =/= S(false)) k(polma(true), ub) } */ protected def show: Str override def toString: String = show } object PolMap { private def mk(init: Pol): PolMap = new PolMap(init) { def apply(lvl: Level): Pol = init def quantifPolarity(lvl: Level): PolMap = this def show: Str = s"${printPol(init)}" } val pos: PolMap = mk(S(true)) val neg: PolMap = mk(S(false)) val posAtNeg: PolMap = pos.at(MinLevel, false) val neu: PolMap = mk(N) def apply(init: Pol = S(true)): PolMap = init match { case S(true) => pos case S(false) => neg case N => neu } } } ================================================ FILE: shared/src/main/scala/mlscript/codegen/Codegen.scala ================================================ package mlscript // Goals: // - Better readability. // - Remove unnecessary parentheses. // - Add better indentations. // - Consistent runtime behavior. // - Currying methods declared in MLscript. // - Combine curry calls on other functions. import mlscript.utils._, shorthands._ import scala.collection.immutable import scala.util.matching.Regex import scala.collection.mutable.ListBuffer import mlscript.codegen.Symbol class SourceLine(val content: Str, indent: Int = 0) { def indented: SourceLine = new SourceLine(content, indent + 1) def +(that: SourceLine): SourceLine = new SourceLine(content + that.content, indent) def withPrefix(prefix: Str): SourceLine = new SourceLine(prefix + content, indent) def withPostfix(postfix: Str): SourceLine = new SourceLine(content + postfix, indent) def between(prefix: Str, postfix: Str): SourceLine = new SourceLine(prefix + content + postfix, indent) override def toString: Str = " " * indent + content } object SourceLine { def apply(line: Str): SourceLine = new SourceLine(line) } /** * SourceCode is a list of SourceLines that can be exported to a code file * * SourceCode provides a number of helper methods to create SourceCode * from strings and manipulate them. It also abstracts common code patterns * like block, condition, clause * * @param lines */ class SourceCode(val lines: Ls[SourceLine]) { /** Concat two parts of source code vertically. "1 \\ 2" + "3 \\ 4" == "1 \\ 2 \\ 3 \\ 4" * * @param that * another part of source code * @return */ def +(that: SourceCode): SourceCode = new SourceCode(lines ++ that.lines) /** Concat two parts of source code horizontally. "1 \\ 2" ++ "3 \\ 4" == "1 \\ 2 3 \\ 4" * * @param that * another part of source code * @return */ def ++(that: SourceCode): SourceCode = that.lines match { case head :: next => if (lines.nonEmpty) { new SourceCode(lines.init ::: Ls(lines.last + head) ::: next) } else { that } case Nil => this } def isSingleLine: Bool = lines.sizeCompare(1) === 0 def isMultipleLine: Bool = lines.lengthIs > 1 def isEmpty: Bool = lines.isEmpty def indented: SourceCode = new SourceCode(lines map { _.indented }) def parenthesized(implicit run: Bool = true): SourceCode = if (run) { lines.length match { case 0 => SourceCode("()") case 1 => new SourceCode(lines map { _.between("(", ")") }) case _ => val head = lines.head val middle = lines.tail.dropRight(1) val last = lines.last new SourceCode( head.withPrefix("(") :: middle ::: Ls(last.withPostfix(")")) ) } } else { this } def clause: SourceCode = lines.length match { case 0 | 1 => this case _ => val head = lines.head val middle = lines.tail.dropRight(1) val last = lines.last new SourceCode( head.withPrefix("(") :: middle ::: Ls(last.withPostfix(")")) ) } def condition: SourceCode = lines.length match { case 0 => this case 1 => new SourceCode(lines map { _.between("(", ")") }) case _ => new SourceCode( SourceLine("(") :: lines.map({ _.indented }) ::: Ls(SourceLine(")")) ) } // Surround the source code with braces in a block style. def block: SourceCode = lines.length match { case 0 => SourceCode("{}") case _ => new SourceCode( SourceLine("{") :: lines.map({ _.indented }) ::: Ls(SourceLine("}")) ) } override def toString: Str = lines.mkString("\n") def toLines: Ls[Str] = lines.map(_.toString) } object SourceCode { def apply(line: Str): SourceCode = new SourceCode(Ls(new SourceLine(line, 0))) def apply(lines: Ls[Str]): SourceCode = new SourceCode(lines map { new SourceLine(_, 0) }) def fromStmts(stmts: Ls[JSStmt]): SourceCode = SourceCode.concat(stmts map { _.toSourceCode }) val ampersand: SourceCode = SourceCode(" & ") val space: SourceCode = SourceCode(" ") val semicolon: SourceCode = SourceCode(";") val colon: SourceCode = SourceCode(": ") val separator: SourceCode = SourceCode(" | ") val comma: SourceCode = SourceCode(",") val commaSpace: SourceCode = SourceCode(", ") val empty: SourceCode = SourceCode(Nil) val openCurlyBrace: SourceCode = SourceCode("{") val closeCurlyBrace: SourceCode = SourceCode("}") val openAngleBracket: SourceCode = SourceCode("<") val closeAngleBracket: SourceCode = SourceCode(">") val fatArrow: SourceCode = SourceCode(" => ") val equalSign: SourceCode = SourceCode(" = ") def concat(codes: Ls[SourceCode]): SourceCode = codes.foldLeft(SourceCode.empty) { _ + _ } // concatenate source codes without intermediate allocations def bulkConcat(codes: Iterable[SourceCode]): SourceCode = new SourceCode(codes.iterator.map(_.lines).foldRight(List.empty[SourceLine])((lines, accum) => lines ::: accum)) /** * Comma-separate elements of List[SourceCode] and wrap with curly braces. * Each element is on a new line. * * @param entries * @return */ def record(entries: Ls[SourceCode]): SourceCode = entries match { case Nil => SourceCode("{}") case entry :: Nil => if (entry.isMultipleLine) { SourceCode("{") + entry.indented + SourceCode("}") } else { SourceCode("{ ") ++ entry ++ SourceCode(" }") } case _ => (entries.zipWithIndex.foldLeft(SourceCode("{")) { case (acc, (entry, index)) => acc + (if (index + 1 === entries.length) { entry } else { entry ++ SourceCode.comma }).indented }) + SourceCode("}") } /** * Comma separate elements of List[SourceCode] and wrap with curly braces * on the same horizontal line * * @param entries * @return */ def horizontalRecord(entries: Ls[SourceCode]): SourceCode = { entries match { case Nil => SourceCode("{}") case _ => (entries .zipWithIndex.foldLeft(SourceCode("{")) { case (acc, (entry, index)) => acc ++ entry ++ (if (index + 1 === entries.length) SourceCode.closeCurlyBrace else SourceCode.commaSpace) }) } } def recordWithEntries(entries: List[SourceCode -> SourceCode]): SourceCode = { entries match { case Nil => SourceCode("{}") case _ => (entries .map(entry => entry._1 ++ colon ++ entry._2) .zipWithIndex.foldLeft(SourceCode("{")) { case (acc, (entry, index)) => acc ++ entry ++ (if (index + 1 === entries.length) SourceCode.closeCurlyBrace else SourceCode.commaSpace) }) } } /** ',' separate and wrap in angled brackets the given source code instances * and return empty string if list is empty * * @param entries * @return */ def paramList(entries: List[SourceCode]): SourceCode = { entries match { case Nil => SourceCode("") case _ => (entries .zipWithIndex.foldLeft(SourceCode.openAngleBracket) { case (acc, (entry, index)) => acc ++ entry ++ (if (index + 1 === entries.length) SourceCode.closeAngleBracket else SourceCode.commaSpace) }) } } /** * Surround the source code with braces. */ def array(entries: Ls[SourceCode]): SourceCode = entries match { case Nil => SourceCode("[]") case entry :: Nil => if (entry.isMultipleLine) { SourceCode("[") + entry.indented + SourceCode("]") } else { SourceCode("[") ++ entry ++ SourceCode("]") } case _ => (entries.zipWithIndex.foldLeft(SourceCode("[")) { case (acc, (entry, index)) => acc + (if (index + 1 === entries.length) { entry } else { entry ++ SourceCode.comma }).indented }) + SourceCode("]") } /** * Surround source code with square brackets concatenating elements * horizontally only. Single element is still wrapped in brackets * * @param entries * @return */ def horizontalArray(entries: Ls[SourceCode]): SourceCode = (entries.zipWithIndex.foldLeft(SourceCode("[")) { case (acc, (entry, index)) => acc ++ entry ++ (if (index + 1 === entries.length) SourceCode("]") else SourceCode.commaSpace) }) def sepBy(codes: Ls[SourceCode], sep: SourceCode = this.commaSpace): SourceCode = codes.zipWithIndex .foldLeft(this.empty) { case (x, (y, i)) => x ++ y ++ (if (i === codes.length - 1) this.empty else sep) } } abstract class JSCode { def toSourceCode: SourceCode } abstract class JSPattern extends JSCode { } object JSPattern { } final case class JSArrayPattern(elements: Ls[JSPattern]) extends JSPattern { def toSourceCode: SourceCode = SourceCode.array(elements map { _.toSourceCode }) } final case class JSObjectPattern(properties: Ls[Str -> Opt[JSPattern]]) extends JSPattern { // If no sub-patterns, use the property name as the binding name. def toSourceCode: SourceCode = SourceCode.record( properties .map { case name -> Some(JSWildcardPattern()) => SourceCode(name) case name -> Some(subPattern) => SourceCode(s"$name: ") ++ subPattern.toSourceCode case name -> N => SourceCode(name) } ) } final case class JSWildcardPattern() extends JSPattern { def toSourceCode: SourceCode = SourceCode.empty } final case class JSNamePattern(name: Str) extends JSPattern { def toSourceCode: SourceCode = SourceCode(name) } abstract class JSExpr extends JSCode { // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence implicit def precedence: Int def isSimple: Bool = false def prop(property: JSExpr): JSMember = JSMember(this, property) def stmt: JSExprStmt = JSExprStmt(this) def `return`: JSReturnStmt = JSReturnStmt(S(this)) def `throw`: JSThrowStmt = JSThrowStmt(this) def member(name: Str): JSField = JSField(this, name) def apply(name: Str): JSField = JSField(this, name) def apply(args: JSExpr*): JSInvoke = JSInvoke(this, args.toList) def unary(op: Str): JSUnary = JSUnary(op, this) def binary(op: Str, rhs: JSExpr): JSBinary = JSBinary(op, this, rhs) def typeof(): JSUnary = JSUnary("typeof", this) def instanceOf(rhs: JSExpr): JSBinary = JSBinary("instanceof", this, rhs) def +(rhs: JSExpr): JSBinary = binary("+", rhs) def ??(rhs: JSExpr): JSBinary = binary("??", rhs) def :||(rhs: JSExpr): JSBinary = binary("||", rhs) def :===(rhs: JSExpr): JSBinary = binary("===", rhs) def :=(value: JSExpr): JSStmt = JSAssignExpr(this, value).stmt def switch(default: Ls[JSStmt], cases: (JSExpr -> Ls[JSStmt])*): JSSwitchStmt = JSSwitchStmt( this, (cases map { case (t, c) => JSSwitchCase(t, c) }).toList, S(JSDefaultCase(default)) ) def switch(cases: (JSExpr -> Ls[JSStmt])*): JSSwitchStmt = JSSwitchStmt(this, (cases map { case (t, c) => JSSwitchCase(t, c) }).toList) def log(): JSStmt = JSIdent("console").member("log")(this).stmt def embed(implicit parentPrecedence: Int): SourceCode = if (precedence < parentPrecedence) { this.toSourceCode.parenthesized } else { this.toSourceCode } } object JSExpr { // Helper function for creating string literals. def apply(str: Str): JSLit = JSLit(JSLit.makeStringLiteral(str)) def params(params: Ls[JSPattern]): SourceCode = params.zipWithIndex .foldLeft(SourceCode.empty) { case (x, (y, i)) => x ++ (y match { case JSWildcardPattern() => SourceCode(s"_$i") case pattern => pattern.toSourceCode }) ++ (if (i === params.length - 1) SourceCode.empty else SourceCode(", ")) } .parenthesized def arguments(exprs: Ls[JSExpr]): SourceCode = exprs.zipWithIndex .foldLeft(SourceCode.empty) { case (x, (y, i)) => x ++ y.embed(parentPrecedence = JSCommaExpr.outerPrecedence) ++ (if (i === exprs.length - 1) SourceCode.empty else SourceCode(", ")) } .parenthesized } final case class JSCommaExpr(exprs: Ls[JSExpr]) extends JSExpr { implicit def precedence: Int = 1 def toSourceCode: SourceCode = SourceCode.sepBy(exprs map { _.toSourceCode }) } object JSCommaExpr { val outerPrecedence: Int = 2 } final case class JSAssignExpr(lhs: JSExpr, rhs: JSExpr) extends JSExpr { implicit def precedence: Int = 3 def toSourceCode: SourceCode = lhs.embed(precedence) ++ SourceCode(" = ") ++ rhs.embed(precedence) } final case class JSPlaceholderExpr() extends JSExpr { implicit def precedence: Int = ??? def toSourceCode: SourceCode = SourceCode.empty } final case class JSArrowFn(params: Ls[JSPattern], body: JSExpr \/ Ls[JSStmt]) extends JSExpr { // If the precedence is 2. We will have x => (x, 0) and (x => x)(1). implicit def precedence: Int = 2 def toSourceCode: SourceCode = params.zipWithIndex .foldLeft(SourceCode.empty) { case (x, (y, i)) => x ++ (y match { case JSWildcardPattern() => SourceCode(s"_$i") case pattern => pattern.toSourceCode }) ++ (if (i === params.length - 1) SourceCode.empty else SourceCode(", ")) } .parenthesized ++ SourceCode.fatArrow ++ (body match { // TODO: Figure out how `=>` competes with other operators. case L(expr: JSRecord) => expr.toSourceCode.parenthesized case L(expr) => expr.embed case R(stmts) => SourceCode.concat(stmts map { _.toSourceCode }).block }) def toFuncExpr(name: Opt[Str]): JSFuncExpr = JSFuncExpr(name, params, body match { case L(expr) => expr.`return` :: Nil case R(stmts) => stmts }) } final case class JSFuncExpr(name: Opt[Str], params: Ls[JSPattern], body: Ls[JSStmt]) extends JSExpr { implicit def precedence: Int = 22 def toSourceCode: SourceCode = SourceCode(s"function ${name getOrElse ""}") ++ JSExpr.params(params) ++ SourceCode.space ++ SourceCode.concat(body map { _.toSourceCode }).block } // IIFE: immediately invoked function expression final case class JSImmEvalFn( name: Opt[Str], params: Ls[JSPattern], body: Either[JSExpr, Ls[JSStmt]], arguments: Ls[JSExpr] ) extends JSExpr { implicit def precedence: Int = 22 def toSourceCode: SourceCode = name match { case None => (SourceCode(s"${JSExpr.params(params)} => ") ++ (body match { case Left(expr: JSRecord) => expr.toSourceCode.parenthesized case Left(expr) => expr.embed(parentPrecedence = 2) case Right(stmts) => stmts.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block })).parenthesized ++ JSExpr.arguments(arguments) case Some(fnName) => (SourceCode(s"function $fnName${JSExpr.params(params)} ") ++ (body match { case Left(expr) => expr.`return`.toSourceCode case Right(stmts) => stmts.foldLeft(SourceCode.empty) { _ + _.toSourceCode } }).block).parenthesized ++ JSExpr.arguments(arguments) } } final case class JSTenary(tst: JSExpr, csq: JSExpr, alt: JSExpr) extends JSExpr { implicit def precedence: Int = 4 def toSourceCode = tst.embed ++ SourceCode(" ? ") ++ csq.embed ++ SourceCode(" : ") ++ alt.embed } final case class JSInvoke(callee: JSExpr, arguments: Ls[JSExpr]) extends JSExpr { implicit def precedence: Int = 20 def toSourceCode = callee.embed(precedence) ++ arguments.zipWithIndex .foldLeft(SourceCode.empty) { case (x, (y, i)) => x ++ y.embed(JSCommaExpr.outerPrecedence) ++ (if (i === arguments.length - 1) SourceCode.empty else SourceCode(", ")) } .parenthesized } final case class JSUnary(op: Str, arg: JSExpr) extends JSExpr { implicit def precedence: Int = 15 override def toSourceCode: SourceCode = (op match { case "typeof" => SourceCode("typeof ") case _ => SourceCode(op) }) ++ arg.embed } final case class JSBinary(op: Str, left: JSExpr, right: JSExpr) extends JSExpr { def apply(op: Str, left: JSExpr, right: JSExpr): JSBinary = new JSBinary(op, left, right) implicit def precedence: Int = JSBinary.opPrecMap get op match { case Some(prec) => prec case None => throw new Error(s"Unknown binary operator: $op") } override def toSourceCode: SourceCode = left.embed ++ SourceCode(s" $op ") ++ right.embed } object JSBinary { private def opPrecMap = immutable.HashMap[Str, Int]( // infixr 14 "**" -> 14, // infixl 13 "*" -> 13, "/" -> 13, "%" -> 13, // infixl 12 "+" -> 12, "-" -> 12, // infixl 11 "<<" -> 11, ">>" -> 11, ">>>" -> 11, // infixl 10 "<" -> 10, "<=" -> 10, ">" -> 10, ">=" -> 10, "in" -> 10, "instanceof" -> 10, // infixl 9 "==" -> 9, "!=" -> 9, "===" -> 9, "!==" -> 9, // infixl 8 "&" -> 8, // infixl 7 "^" -> 7, // infixl 6 "|" -> 6, // infixl 5 "&&" -> 5, // infixl 4 "||" -> 4, "??" -> 4, "," -> 1, ) val operators: Set[Str] = opPrecMap.keySet } final case class JSLit(literal: Str) extends JSExpr { // Literals has the highest precedence. implicit def precedence: Int = 22 def toSourceCode = SourceCode(literal) } object JSLit { def makeStringLiteral(s: Str): Str = { s.map[Str] { _ match { case '"' => "\\\"" case '\\' => "\\\\" case '\b' => "\\b" case '\f' => "\\f" case '\n' => "\\n" case '\r' => "\\r" case '\t' => "\\t" case c => if (0 < c && c <= 255 && !c.isControl) { c.toString } else { f"\\u${c.toInt}%04X" } } }.mkString("\"", "", "\"") } } final case class JSInstanceOf(left: JSExpr, right: JSExpr) extends JSExpr { implicit def precedence: Int = 12 def toSourceCode: SourceCode = left.toSourceCode ++ SourceCode(" instanceof ") ++ right.toSourceCode } final case class JSIdent(name: Str) extends JSExpr { implicit def precedence: Int = 22 def toSourceCode: SourceCode = SourceCode(name) } final case class JSNew(ctor: JSExpr) extends JSExpr { implicit def precedence: Int = 21 def toSourceCode: SourceCode = SourceCode("new ") ++ ctor.toSourceCode } class JSMember(`object`: JSExpr, property: JSExpr) extends JSExpr { override def precedence: Int = 20 override def toSourceCode: SourceCode = `object`.toSourceCode.parenthesized( `object`.precedence < precedence || `object`.isInstanceOf[JSRecord] || `object`.isInstanceOf[JSNew] ) ++ SourceCode("[") ++ property.toSourceCode ++ SourceCode("]") override def isSimple: Bool = `object`.isSimple } object JSMember { def apply(`object`: JSExpr, property: JSExpr): JSMember = new JSMember(`object`, property) } class JSField(`object`: JSExpr, val property: JSIdent) extends JSMember(`object`, property) { override def toSourceCode: SourceCode = `object`.toSourceCode.parenthesized( `object`.precedence < precedence || `object`.isInstanceOf[JSRecord] || `object`.isInstanceOf[JSNew] ) ++ SourceCode( if (JSField.isValidFieldName(property.name)) { s".${property.name}" } else property.name.toIntOption match { case S(index) => s"[$index]" case N => s"[${JSLit.makeStringLiteral(property.name)}]" } ) } object JSField { def apply(`object`: JSExpr, property: String): JSField = new JSField(`object`, JSIdent(property)) private val identifierPattern: Regex = "^[A-Za-z$][A-Za-z0-9$]*$".r def isValidIdentifier(s: Str): Bool = identifierPattern.matches(s) && !Symbol.isKeyword(s) // in this case, a keyword can be used as a field name // e.g. `something.class` is valid def isValidFieldName(s: Str): Bool = identifierPattern.matches(s) def emitValidFieldName(s: Str): Str = if (isValidIdentifier(s)) s else JSLit.makeStringLiteral(s) } final case class JSArray(items: Ls[JSExpr]) extends JSExpr { // Precedence of literals is zero. override def precedence: Int = 22 // Make override def toSourceCode: SourceCode = SourceCode.array(items map { _.embed(JSCommaExpr.outerPrecedence) }) } final case class JSRecord(entries: Ls[Str -> JSExpr], methods: Ls[JSStmt] = Nil) extends JSExpr { override def precedence: Int = 22 // Make override def toSourceCode: SourceCode = SourceCode .record((entries map { case (key, value) => SourceCode(JSField.emitValidFieldName(key) + ": ") ++ value.embed(JSCommaExpr.outerPrecedence) }) ++ (methods.map((m) => m.toSourceCode))) } final case class JSClassExpr(cls: JSClassNewDecl) extends JSExpr { implicit def precedence: Int = 22 def toSourceCode: SourceCode = cls.toSourceCode } abstract class JSStmt extends JSCode final case class JSExprStmt(expr: JSExpr) extends JSStmt { def toSourceCode: SourceCode = expr.toSourceCode.parenthesized(expr.isInstanceOf[JSRecord]) ++ SourceCode.semicolon } // A single if statement without else clauses final case class JSIfStmt(test: JSExpr, body: Ls[JSStmt], `else`: Ls[JSStmt] = Nil) extends JSStmt { def toSourceCode: SourceCode = SourceCode("if ") ++ test.toSourceCode.condition ++ SourceCode.space ++ body.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block ++ (`else` match { case Nil => SourceCode.empty case _ => SourceCode(" else ") ++ `else`.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block }) } final case class JSForInStmt(pattern: JSPattern, iteratee: JSExpr, body: Ls[JSStmt]) extends JSStmt { def toSourceCode: SourceCode = SourceCode("for (const ") ++ pattern.toSourceCode ++ SourceCode(" in ") ++ iteratee.toSourceCode ++ SourceCode(")") ++ body.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block } final case class JSWhileStmt(cond: JSExpr, body: JSExpr) extends JSStmt { def toSourceCode: SourceCode = SourceCode("while (") ++ cond.toSourceCode ++ SourceCode(") ") ++ body.toSourceCode.block } // A single return statement. final case class JSReturnStmt(value: Opt[JSExpr]) extends JSStmt { def toSourceCode = (value match { case Some(value) => SourceCode("return ") ++ value.toSourceCode.clause case None => SourceCode("return") }) ++ SourceCode.semicolon } final case class JSTryStmt(block: Ls[JSStmt], handler: JSCatchClause) extends JSStmt { def toSourceCode: SourceCode = SourceCode("try ") ++ block.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block ++ handler.toSourceCode } final case class JSCatchClause(param: JSIdent, body: Ls[JSStmt]) extends JSCode { def toSourceCode: SourceCode = SourceCode(s" catch (${param.name}) ") ++ body.foldLeft(SourceCode.empty) { _ + _.toSourceCode }.block } // Throw statement currently only used in non-exhaustive pattern matchings. final case class JSThrowStmt(arg: JSExpr) extends JSStmt { def toSourceCode: SourceCode = SourceCode("throw ") ++ arg.toSourceCode.clause ++ SourceCode.semicolon } final case class JSSwitchStmt( discriminant: JSExpr, cases: Ls[JSSwitchCase], default: Opt[JSDefaultCase] = N ) extends JSStmt { def toSourceCode: SourceCode = SourceCode("switch (") ++ discriminant.toSourceCode ++ SourceCode(") {") + cases.foldLeft(SourceCode.empty) { _ + _.toSourceCode.indented } + (default match { case S(default) => default.toSourceCode.indented + SourceCode("}") case N => SourceCode("}") }) } final case class JSSwitchCase(test: JSExpr, consequent: Ls[JSStmt]) { def toSourceCode: SourceCode = SourceCode("case ") ++ test.toSourceCode ++ SourceCode(": ") + consequent.foldLeft(SourceCode.empty) { _ + _.toSourceCode.indented } } final case class JSDefaultCase(consequent: Ls[JSStmt]) { def toSourceCode: SourceCode = SourceCode("default:") + consequent.foldLeft(SourceCode.empty) { _ + _.toSourceCode.indented } } final case class JSLetDecl(decls: Ls[Str -> Opt[JSExpr]]) extends JSStmt { def toSourceCode: SourceCode = SourceCode(s"let ") ++ decls.zipWithIndex .foldLeft(SourceCode.empty) { case (x, (y, i)) => x ++ (y match { case (pat, N) => SourceCode(pat) case (pat, S(init)) => SourceCode(pat) ++ SourceCode(" = ") ++ init.toSourceCode }) ++ (if (i === decls.length - 1) SourceCode.empty else SourceCode(", ")) } ++ SourceCode.semicolon } object JSLetDecl { def from(names: Ls[Str]): JSLetDecl = JSLetDecl(names map { _ -> N }) } final case class JSConstDecl(pattern: Str, body: JSExpr) extends JSStmt { def toSourceCode: SourceCode = SourceCode(s"const $pattern = ") ++ (body match { case _: JSCommaExpr => body.toSourceCode.parenthesized case _ => body.toSourceCode }) ++ SourceCode.semicolon } final case class JSFuncDecl(name: Str, params: Ls[JSPattern], body: Ls[JSStmt]) extends JSStmt { def toSourceCode: SourceCode = SourceCode(s"function $name") ++ JSExpr.params(params) ++ SourceCode.space ++ body .foldLeft(SourceCode.empty) { case (x, y) => x + y.toSourceCode } .block } abstract class JSClassMemberDecl extends JSStmt; final case class JSClassGetter(name: Str, body: JSExpr \/ Ls[JSStmt]) extends JSClassMemberDecl { def toSourceCode: SourceCode = SourceCode(s"get ${JSField.emitValidFieldName(name)}() ") ++ (body match { case Left(expr) => new JSReturnStmt(S(expr)).toSourceCode case Right(stmts) => stmts.foldLeft(SourceCode.empty) { case (x, y) => x + y.toSourceCode } }).block } final case class JSClassSetter(name: Str) extends JSClassMemberDecl { def toSourceCode: SourceCode = SourceCode(s"set ${JSField.emitValidFieldName(name)}(value) ") ++ ( JSIdent(s"this.#$name") := JSIdent("value") ).toSourceCode.block } final case class JSClassMethod( name: Str, params: Ls[JSPattern], body: JSExpr \/ Ls[JSStmt] ) extends JSClassMemberDecl { def toSourceCode: SourceCode = SourceCode(JSField.emitValidFieldName(name)) ++ JSExpr.params(params) ++ SourceCode.space ++ (body match { case Left(expr) => new JSReturnStmt(S(expr)).toSourceCode case Right(stmts) => stmts.foldLeft(SourceCode.empty) { case (x, y) => x + y.toSourceCode } }).block } final case class JSClassMember(name: Str, body: JSExpr) extends JSClassMemberDecl { def toSourceCode: SourceCode = SourceCode(name) ++ SourceCode(" = ") ++ body.toSourceCode ++ SourceCode.semicolon } final case class JSClassDecl( name: Str, fields: Ls[Str], `extends`: Opt[JSExpr] = N, methods: Ls[JSClassMemberDecl] = Nil, implements: Ls[Str] = Nil, ) extends JSStmt { def toSourceCode: SourceCode = { val constructor: SourceCode = { val buffer = new ListBuffer[Str]() buffer += " constructor(fields) {" if (`extends`.isDefined) buffer += " super(fields);" implements.foreach { name => buffer += s" $name.implement(this);" } fields.foreach { name => val innerName = if (JSField.isValidIdentifier(name)) s".${name}" else s"[${JSLit.makeStringLiteral(name)}]" buffer += s" this$innerName = fields$innerName;" } buffer += " }" SourceCode(buffer.toList) } val methodsSourceCode = methods.foldLeft(SourceCode.empty) { case (x, y) => x + y.toSourceCode.indented } val epilogue = SourceCode("}") `extends` match { case Some(base) => SourceCode(s"class $name extends ") ++ base.toSourceCode ++ SourceCode(" {") + constructor + methodsSourceCode + epilogue case None => if (fields.isEmpty && methods.isEmpty && implements.isEmpty) { SourceCode(s"class $name {}") } else { SourceCode( s"class $name {" :: Nil ) + constructor + methodsSourceCode + epilogue } } } private val fieldsSet = collection.immutable.HashSet.from(fields) } final case class JSClassNewDecl( name: Str, fields: Ls[Str], accessors: Ls[Bool -> Str], // mut -> name privateMems: Ls[Str], `extends`: Opt[JSExpr], superFields: Ls[JSExpr], ctorParams: Ls[Str], rest: Opt[Str], methods: Ls[JSClassMemberDecl], implements: Ls[Str], initStmts: Ls[JSStmt], nestedTypes: Ls[Str], ctorOverridden: Bool, staticMethods: Ls[JSClassMemberDecl] ) extends JSStmt { def toSourceCode: SourceCode = { val constructor: SourceCode = { val buffer = new ListBuffer[Str]() val params = ctorParams.iterator.zipWithIndex.foldRight(rest match { case Some(rest) => s"...$rest" case _ => "" })((p, s) => if (s.isEmpty) s"${p._1}" else s"${p._1}, $s") nestedTypes.foreach(t => buffer += s" #$t;") privateMems.distinct.foreach(f => { buffer += s" #${f};" }) accessors.distinct.foreach { case (mut, f) => if (!privateMems.contains(f)) buffer += s" #${f};" buffer += s" get ${f}() { return this.#${f}; }" if (mut) buffer += s" set ${f}($$value) { return this.#${f} = $$value; }" } buffer += s" constructor($params) {" if (`extends`.isDefined) { val sf = superFields.iterator.zipWithIndex.foldLeft("")((res, p) => if (p._2 === superFields.length - 1) s"$res${p._1.toSourceCode}" else s"$res${p._1.toSourceCode}, " ) buffer += s" super($sf);" } implements.foreach { name => buffer += s" $name.implement(this);" } // if the default constructor is overridden, we generate the overridden version // otherwise, generate based on the class fields if (!ctorOverridden) { assert(fields.length === ctorParams.length, s"fields and ctorParams have different size in class $name.") fields.lazyZip(ctorParams).foreach { (field, param) => buffer += s" this.#$field = $param;" // TODO: invalid name? } } initStmts.foreach { s => s.toSourceCode.indented.indented.toString.split("\n").foreach { line => buffer += line } } buffer += " }" SourceCode(buffer.toList) } val methodsSourceCode = methods.foldLeft(SourceCode.empty) { case (x, y) => x + y.toSourceCode.indented } val staticMethodsSourceCode = staticMethods.foldLeft(SourceCode.empty) { case (x, y) => x + SourceCode("static") + y.toSourceCode.indented } val epilogue = SourceCode("}") `extends` match { case Some(base) => SourceCode(s"class $name extends ") ++ base.toSourceCode ++ SourceCode(" {") + constructor + methodsSourceCode + staticMethodsSourceCode + epilogue case None => if (fields.isEmpty && methods.isEmpty && implements.isEmpty && accessors.isEmpty && initStmts.isEmpty && staticMethods.isEmpty) { SourceCode(s"class $name {}") } else { SourceCode( s"class $name {" :: Nil ) + constructor + methodsSourceCode + staticMethodsSourceCode + epilogue } } } private val fieldsSet = collection.immutable.HashSet.from(fields) } final case class JSComment(text: Str) extends JSStmt { def toSourceCode: SourceCode = SourceCode(s"// $text") } final case class JSParenthesis(exp: JSExpr) extends JSExpr { implicit def precedence: Int = 0 def toSourceCode: SourceCode = exp.embed } object JSCodeHelpers { def id(name: Str): JSIdent = JSIdent(name) def lit(value: Int): JSLit = JSLit(value.toString()) def const(name: Str, init: JSExpr): JSConstDecl = JSConstDecl(name, init) def `return`(): JSReturnStmt = JSReturnStmt(N) def `return`(expr: JSExpr): JSReturnStmt = expr.`return` def `throw`(expr: JSExpr): JSThrowStmt = expr.`throw` def forIn(pattern: JSNamePattern, iteratee: JSExpr)(stmts: JSStmt*): JSForInStmt = JSForInStmt(pattern, iteratee, stmts.toList) def fn(name: Str, params: JSPattern*)(stmts: JSStmt*): JSFuncDecl = JSFuncDecl(name, params.toList, stmts.toList) def param(name: Str): JSNamePattern = JSNamePattern(name) } ================================================ FILE: shared/src/main/scala/mlscript/codegen/Error.scala ================================================ package mlscript.codegen import scala.collection.mutable.ArrayBuffer final case class CodeGenError(message: String) extends Exception(message) final case class UnimplementedError(symbol: StubValueSymbol) extends Exception({ val names = new ArrayBuffer[String]() var current: Option[StubValueSymbol] = Some(symbol) while ( current match { case Some(sym) => names += sym.lexicalName current = sym.previous true case None => false } ) () val sep = ", " val concat = { val joined = names.mkString(sep) val pos = joined.lastIndexOf(sep) if (pos > -1) { joined.substring(0, pos) + " and " + joined.substring(pos + sep.length) } else { joined } } val be = if (names.lengthIs > 1) "are" else "is" s"$concat $be not implemented" }) ================================================ FILE: shared/src/main/scala/mlscript/codegen/Polyfill.scala ================================================ package mlscript import mlscript.utils._, shorthands._ import scala.collection.immutable.HashMap import scala.collection.mutable.ArrayBuffer class Polyfill { private val featFnNameMap = collection.mutable.HashMap[Str, Str]() private val emittedFeatSet = collection.mutable.HashSet[Str]() def use(feat: Str, jsFnName: Str): Str = { featFnNameMap += feat -> jsFnName jsFnName } /** * Check whether a feature has been already used. */ def used(feat: Str): Bool = featFnNameMap.contains(feat) def get(feat: Str): Opt[Str] = featFnNameMap get feat def emit(): Ls[JSStmt] = (featFnNameMap flatMap { case (featName, fnName) => if (emittedFeatSet contains featName) N else Polyfill.getFeature(featName) match { case S(feature) => emittedFeatSet += featName S(feature(fnName)) case N => N } }).toList } object Polyfill { def apply(): Polyfill = new Polyfill() abstract class Feature { val name: Str def apply(name: Str): JSStmt } final case class BuiltinFunc(val name: Str, maker: Str => JSStmt) extends Feature { def apply(name: Str): JSStmt = maker(name) } final case class RuntimeHelper(val name: Str, maker: Str => JSStmt) extends Feature { def apply(name: Str): JSStmt = maker(name) } import JSCodeHelpers._ val features: Ls[Feature] = { val buffer = new ArrayBuffer[Feature]() buffer += RuntimeHelper( "prettyPrint", (name: Str) => { val arg = JSIdent("value") JSFuncDecl( name, JSNamePattern("value") :: Nil, arg .typeof() .switch( JSIdent("String")(arg).`return` :: Nil, JSExpr("number") -> Nil, JSExpr("boolean") -> { arg.member("toString")().`return` :: Nil }, // Returns `"[Function: ]"` JSExpr("function") -> { val name = arg.member("name") ?? JSExpr("") val repr = JSExpr("[Function: ") + name + JSExpr("]") (repr.`return` :: Nil) }, JSExpr("string") -> ((JSExpr("\"") + arg + JSExpr("\"")).`return` :: Nil), JSExpr("undefined") -> (JSExpr("undefined").`return` :: Nil), JSExpr("object") -> (JSIfStmt( arg :=== JSIdent("null"), JSExpr("null").`return` :: Nil, JSTryStmt( (arg.member("constructor").member("name") + JSExpr(" ") + JSIdent("JSON").member("stringify")(arg, JSIdent("undefined"), JSIdent("2"))).`return` :: Nil, JSCatchClause( JSIdent("_"), JSIdent("String")(arg).`return` :: Nil, ) ) :: Nil, ) :: Nil) ) :: Nil, ) } ) buffer += RuntimeHelper( "withConstruct", (name: Str) => { val obj = id("Object") val t = id("target") val f = id("fields") fn(name, param("target"), param("fields")) ( JSIfStmt( (t.typeof() :=== JSExpr("string")) :|| (t.typeof() :=== JSExpr("number")) :|| (t.typeof() :=== JSExpr("boolean")) :|| (t.typeof() :=== JSExpr("bigint")) :|| (t.typeof() :=== JSExpr("symbol")), obj("assign")(t, f).`return` :: Nil, ), JSIfStmt( t.instanceOf(id("String")) :|| t.instanceOf(id("Number")) :|| t.instanceOf(id("Boolean")) :|| t.instanceOf(id("BigInt")), obj("assign")(t("valueOf")(), t, f).`return` :: Nil, ), JSIfStmt( id("Array")("isArray")(t), Ls( const("clone", id("Array")("from")(t)), forIn(param("key"), t) { id("clone").prop(id("key")) := t.prop(id("key")) }, forIn(param("key"), f) { id("clone").prop(id("key")) := f.prop(id("key")) }, `return`(id("clone")) ) ), JSIfStmt( // * "Strict equality checks (===) should be used in favor of ==. // * The only exception is when checking for undefined and null by way of null." // * (http://contribute.jquery.org/style-guide/js/) JSBinary("==", t, JSLit("null")), `return`(obj("assign")(JSRecord(Nil), JSRecord(Nil), f)) :: Nil, ), JSConstDecl("copy", obj("assign")(JSRecord(Nil), t, f)), obj("setPrototypeOf")(id("copy"), obj("getPrototypeOf")(t)).stmt, id("copy").`return` ) } ) buffer += BuiltinFunc( "toString", fn(_, param("x")) { `return` { id("String")(id("x")) } } ) buffer += BuiltinFunc( "id", fn(_, param("x")) { `return`(id("x")) } ) buffer += BuiltinFunc( "emptyArray", fn(_) { `return`(JSArray(Nil)) } ) buffer += BuiltinFunc( "succ", fn(_, param("x")) { `return` { id("x") + lit(1) } } ) buffer += BuiltinFunc( "error", fn(_) { `throw`(JSNew(JSIdent("Error"))(JSExpr("an error was thrown"))) } ) buffer += BuiltinFunc( "length", fn(_, param("x")) { `return` { id("x").member("length") } } ) buffer += BuiltinFunc("concat", makeBinaryFunc("+")) buffer += BuiltinFunc("join", fn(_, param("...xs")) { `return` { id("xs")("join").apply(JSLit(JSLit.makeStringLiteral(""))) } }) buffer += BuiltinFunc("add", makeBinaryFunc("+")) buffer += BuiltinFunc("sub", makeBinaryFunc("-")) buffer += BuiltinFunc("mul", makeBinaryFunc("*")) buffer += BuiltinFunc("numAdd", makeBinaryFunc("+")) buffer += BuiltinFunc("numSub", makeBinaryFunc("-")) buffer += BuiltinFunc("numMul", makeBinaryFunc("*")) buffer += BuiltinFunc("div", makeBinaryFunc("/")) buffer += BuiltinFunc("gt", makeBinaryFunc(">")) buffer += BuiltinFunc("not", makeUnaryFunc("!")) buffer += BuiltinFunc("negate", makeUnaryFunc("-")) buffer += BuiltinFunc("eq", makeBinaryFunc("===")) buffer += BuiltinFunc("ne", makeBinaryFunc("!==")) buffer += BuiltinFunc("sgt", makeBinaryFunc(">")) buffer += BuiltinFunc("slt", makeBinaryFunc("<")) buffer += BuiltinFunc("sge", makeBinaryFunc(">=")) buffer += BuiltinFunc("sle", makeBinaryFunc("<=")) buffer += BuiltinFunc("eq", makeBinaryFunc("===")) buffer += BuiltinFunc("unit", makeUnaryFunc("undefined")) buffer += BuiltinFunc( "log", fn(_, param("x")) { `return` { id("console.info")(id("x")) } } ) buffer += BuiltinFunc( "discard", fn(_, param("x"))() ) buffer.toList } private val nameFeatureMap = HashMap.from(features map { feature => (feature.name, feature) }) def getFeature(name: Str): Opt[Feature] = nameFeatureMap get name def isPreludeFunction(name: Str): Bool = nameFeatureMap .get(name) .map({ case BuiltinFunc(_, _) => true case _ => false }) .getOrElse(false) private def makeBinaryFunc(op: Str)(name: Str): JSFuncDecl = JSFuncDecl( name, JSNamePattern("x") :: JSNamePattern("y") :: Nil, JSIfStmt( JSIdent("arguments").member("length") :=== JSLit("2"), (JSIdent("x").binary(op, JSIdent("y"))).`return` :: Nil, JSArrowFn( JSNamePattern("y") :: Nil, L(JSIdent("x").binary(op, JSIdent("y"))) ).`return` :: Nil ) :: Nil ) private def makeUnaryFunc(op: Str)(name: Str): JSFuncDecl = fn(name, param("x")) { `return`(id("x").unary(op)) } } ================================================ FILE: shared/src/main/scala/mlscript/codegen/QQHelper.scala ================================================ package mlscript.codegen import mlscript.utils._ import mlscript.utils.shorthands._ // * Pretty printer for quasiquotes. // * This is a temporary hack due to lack of runtime support and should be removed later. object QQHelper { val prettyPrinter: Str = """ |(() => { | const symbols = new Map(); | const printList = (lst, sep) => { | if (lst.length === 0) return ""; | else { | const r = lst.reduce((x, y) => `${x}${y}${sep}`, "") | return r.substring(0, r.length - sep.length); | } | } | const indent = (s) => s.split("\n").map(ln => ` ${ln}`).join("\n") | globalThis.freshName = (n) => { | if (!symbols.has(n)) { symbols.set(n, 0); } | const i = symbols.get(n); | symbols.set(n, i + 1); | return `${n}_${i}`; | } | globalThis.Const = (n) => `${n}`; | globalThis.IntLit = (v) => `${v}`; | globalThis.DecLit = (v) => `${v}`; | globalThis.StrLit = (v) => `${v}`; | globalThis.UnitLit = (v) => `${v}`; | globalThis.Lam = (x, e) => `(${x}) =>\n${indent(e)}`; | globalThis.Var = (x) => `${x}`; | globalThis.App = (f, ...xs) => { | if (f === '+' || f === '-' || f === '*' || f === '/' || f === '==' || f === '<' || f === '>' || f === 'and' || f === 'or' || f === 'is') | return `(${printList(xs, ` ${f} `)})`; | else | return `${f}${printList(xs, ", ")}`; | } | globalThis.Rcd = (...xs) => `{${printList(xs, ", ")}}`; | globalThis.Bra = (x) => `(${x})`; | globalThis.Sel = (x, y) => `${x}.${y}`; | globalThis.Blk = (...s) => `{\n${indent(printList(s, ";\n"))}\n}`; | globalThis.Tup = (...es) => `(${printList(es, ", ")})`; | globalThis.Fld = (v) => `${v}`; | globalThis.Let = (nme, v, bod) => `let ${nme} =\n${indent(v)}\n${indent(`in ${bod}`)}`; | globalThis.Subs = (arr, idx) => `${arr}[${idx}]`; | globalThis.With = (lhs, rhs) => `${lsh} with ${rhs}`; | globalThis.Quoted = (body) => `code"${body}"`; | globalThis.CaseOf = (trm, cse) => `match ${trm}:\n ${cse}`; | globalThis.Case = (pat, bod, trm) => `case ${pat} => ${bod}\n ${trm})`; | globalThis.Wildcard = (res) => `_ => ${res}`; | globalThis.NoCases = () => ``; | globalThis.run = (code) => {console.log("Quoted:\n" + code);} |})(); """.stripMargin } ================================================ FILE: shared/src/main/scala/mlscript/codegen/Scope.scala ================================================ package mlscript.codegen import mlscript.utils.shorthands._ import mlscript.{JSStmt, JSExpr, JSLetDecl} import mlscript.Type import scala.reflect.ClassTag import mlscript.{TypeName, Top, Bot, TypeDef, Als, Trt, Cls, Mod, Mxn} import mlscript.{MethodDef, Var} import mlscript.{Term, Statement, Record} import mlscript.utils.{AnyOps, lastWords} import mlscript.JSField import mlscript.{NuTypeDef, NuFunDef} class Scope(val name: Str, enclosing: Opt[Scope]) { private val lexicalTypeSymbols = scala.collection.mutable.HashMap[Str, TypeSymbol]() private val lexicalValueSymbols = scala.collection.mutable.HashMap[Str, RuntimeSymbol]() private val runtimeSymbols = scala.collection.mutable.HashSet[Str]() // To allow a class method/getter/constructor to access members of an outer class, // we insert `const qualifier = this;` before the class definition starts. // To access ALL qualifier variables correctly, we need to make sure // none of them would be shadowed. private val qualifierSymbols = scala.collection.mutable.HashMap[Str, ValueSymbol]() val tempVars: TemporaryVariableEmitter = TemporaryVariableEmitter() /** * Shorthands for creating top-level scopes. */ def this(name: Str) = { this(name, N) // TODO: allow types and values to have the same name // TODO: read built-in symbols from `Typer`. Ls( "true", "false", "NaN", "id", "emptyArray", "succ", "error", "length", "concat", "join", "add", "sub", "mul", "numAdd", "numSub", "numMul", "div", "gt", "not", "ne", "eq", "sgt", "slt", "sge", "sle", "typeof", "toString", "String", "negate", "eq", "unit", "log", "run", "Const", "freshName", "Lam", "Var", "App", "IntLit", "StrLit", "DecLit", "UnitLit", "Rcd", "Bra", "Sel", "Blk", "Tup", "Fld", "Let", "Subs", "With", "Quoted", "CaseOf", "Case", "Wildcard", "NoCases", "discard", "window", ) foreach { name => register(BuiltinSymbol(name, name)) } // TODO: add `true`, `false`, and `error` to this list register(TypeAliasSymbol("anything", Nil, Top)) register(TypeAliasSymbol("nothing", Nil, Bot)) // TODO: register them in the same way as `Typer` does. Ls("int", "number", "bool", "string", "unit") foreach { name => register(TypeAliasSymbol(name, Nil, TypeName(name))) } // TODO: eventually this should be properly registered along // with all other built-in classes. For now, this just helps the // unit tests relating to annotations pass. val annType = TraitSymbol("Annotation", "Annotation", Nil, Record(Nil), Nil) register(annType) } private val allocateRuntimeNameIter = for { i <- (1 to Int.MaxValue).iterator c <- Scope.nameAlphabet.combinations(i) name = c.mkString if !hasRuntimeName(name) } yield { name } /** * Check if a runtime name is used recursively. * * @param name the name * @return whether it's available or not */ private def hasRuntimeName(name: Str): Bool = runtimeSymbols.contains(name) || enclosing.exists(_.hasRuntimeName(name)) /** * Allocate a non-sense runtime name. */ private def allocateRuntimeName(): Str = allocateRuntimeNameIter.next() /** * Allocate a runtime name starting with the given prefix. */ private def allocateRuntimeName(prefix: Str): Str = { // Fallback case. if (prefix.isEmpty()) { return allocateRuntimeName() } // Replace ticks val realPrefix = Scope.replaceTicks(prefix) // Try just prefix. if (!runtimeSymbols.contains(realPrefix) && !Symbol.isKeyword(realPrefix)) { return realPrefix } // Try prefix with an integer. for (i <- 1 to Int.MaxValue) { val name = s"$realPrefix$i" if (!runtimeSymbols.contains(name)) { return name } } // Give up. throw CodeGenError( if (realPrefix.isEmpty()) "Cannot allocate a runtime name" else s"Cannot allocate a runtime name starting with '$realPrefix'" ) } private def register(symbol: TypeAliasSymbol): Unit = { lexicalTypeSymbols.put(symbol.lexicalName, symbol) () } /** * Register a lexical symbol in both runtime name set and lexical name set. */ def register(symbol: TypeSymbol with RuntimeSymbol): Unit = { lexicalTypeSymbols.put(symbol.lexicalName, symbol) lexicalValueSymbols.put(symbol.lexicalName, symbol) runtimeSymbols += symbol.runtimeName () } /** * Register a lexical symbol in both runtime name set and lexical name set. */ def register(symbol: RuntimeSymbol): Unit = { lexicalValueSymbols.put(symbol.lexicalName, symbol) runtimeSymbols += symbol.runtimeName () } private def unregister(symbol: ValueSymbol): Unit = { lexicalTypeSymbols.remove(symbol.lexicalName) runtimeSymbols.remove(symbol.runtimeName) () } def unregisterSymbol(symbol: ValueSymbol): Unit = { unregister(symbol) } def getType(name: Str): Opt[TypeSymbol] = lexicalTypeSymbols.get(name) /** * Look up for a class symbol locally. */ def getClassSymbol(name: Str): Opt[ClassSymbol] = lexicalTypeSymbols.get(name) collect { case c: ClassSymbol => c } /** * Look up a trait symbol locally. */ def getTraitSymbol(name: Str): Opt[TraitSymbol] = lexicalTypeSymbols.get(name) collect { case c: TraitSymbol => c } /** * Look up a type alias symbol locally. */ def getTypeAliasSymbol(name: Str): Opt[TypeAliasSymbol] = lexicalTypeSymbols.get(name) collect { case c: TypeAliasSymbol => c } /** * Look up for a type symbol locally. */ def getTypeSymbol(name: Str): Opt[TypeSymbol] = lexicalTypeSymbols.get(name) def resolveValue(name: Str): Opt[RuntimeSymbol] = lexicalValueSymbols.get(name).orElse(enclosing.flatMap(_.resolveValue(name))) /** * Find the base class for a specific class. */ def resolveBaseClass(ty: Type): Opt[ClassSymbol] = { val baseClasses = ty.collectTypeNames.flatMap { name => this.getType(name) match { case S(sym: ClassSymbol) => S(sym) case S(sym: TraitSymbol) => N case S(sym: TypeAliasSymbol) => throw new CodeGenError(s"cannot inherit from type alias $name" ) case S(_: NuTypeSymbol) => throw new CodeGenError(s"NuType symbol $name is not supported when resolving base classes") case N => throw new CodeGenError(s"undeclared type name $name when resolving base classes") } } if (baseClasses.lengthIs > 1) throw CodeGenError( s"cannot have ${baseClasses.length} base classes: " + baseClasses.map { _.lexicalName }.mkString(", ") ) else baseClasses.headOption } def resolveImplementedTraits(ty: Type): Ls[TraitSymbol] = { ty.collectTypeNames.flatMap { name => this.getType(name) match { case S(sym: ClassSymbol) => N case S(sym: TraitSymbol) => S(sym) case S(sym: TypeAliasSymbol) => throw new CodeGenError(s"cannot inherit from type alias $name" ) case S(sym: NuTypeSymbol) => throw new CodeGenError(s"NuType symbol $name is not supported when resolving implemented traits") case N => throw new CodeGenError(s"undeclared type name $name when resolving implemented traits") } } } def declareTypeSymbol(typeDef: TypeDef): TypeSymbol = typeDef match { case TypeDef(Als, TypeName(name), tparams, body, _, _, _, _) => declareTypeAlias(name, tparams map { _.name }, body) case TypeDef(Trt, TypeName(name), tparams, body, _, mthdDefs, _, _) => declareTrait(name, tparams map { _.name }, body, mthdDefs) case TypeDef(Cls, TypeName(name), tparams, baseType, _, members, _, _) => declareClass(name, tparams map { _.name }, baseType, members) case TypeDef(Mxn, _, _, _, _, _, _, _) => throw CodeGenError("Mixins are not supported yet.") case TypeDef(Mod, _, _, _, _, _, _, _) => throw CodeGenError("Modules are not supported yet.") } def declareClass( lexicalName: Str, params: Ls[Str], base: Type, methods: Ls[MethodDef[Left[Term, Type]]] ): ClassSymbol = { val runtimeName = allocateRuntimeName(lexicalName) val symbol = ClassSymbol(lexicalName, runtimeName, params.sorted, base, methods) register(symbol) symbol } // in DiffTests, we need to rename `TypingUnit` to some other names // because we would not indicate different names manually def declareTopModule( lexicalName: Str, stmts: Ls[Statement], nuTypes: Ls[NuTypeDef], allowRenaming: Bool ): ModuleSymbol = { val finalName = if (allowRenaming) allocateRuntimeName(lexicalName) else lexicalName val (mths, rest) = stmts.partitionMap { case NuFunDef(isLetRec, Var(nme), _, tys, Left(rhs)) if (isLetRec.isEmpty || isLetRec.getOrElse(false)) => Left(MethodDef[Left[Term, Type]](isLetRec.getOrElse(false), TypeName(finalName), Var(nme), tys, Left(rhs))) case s => Right(s) } val (signatures, ctor) = rest.partitionMap { case NuFunDef(isLetRec, Var(nme), _, tys, Right(rhs)) if (isLetRec.isEmpty || isLetRec.getOrElse(false)) => Left(MethodDef[Right[Term, Type]](isLetRec.getOrElse(false), TypeName(finalName), Var(nme), tys, Right(rhs))) case s => Right(s) } val symbol = ModuleSymbol(finalName, Nil, Record(Nil), mths, signatures, ctor, Nil, nuTypes, N) register(symbol) symbol } // We don't want `qualifier` symbols to be shadowed by each other // Add all runtime names of `qualifier` symbols from the parent scope private def pullOuterSymbols(syms: scala.collection.mutable.HashMap[Str, ValueSymbol]) = { syms.foreach { s => runtimeSymbols += s._1 qualifierSymbols += s } this } def declareTrait( lexicalName: Str, params: Ls[Str], base: Type, methods: Ls[MethodDef[Left[Term, Type]]] ): TraitSymbol = { val runtimeName = allocateRuntimeName(lexicalName) val symbol = TraitSymbol(lexicalName, runtimeName, params, base, methods) register(symbol) symbol } def declareTypeAlias(lexicalName: Str, params: Ls[Str], ty: Type): TypeAliasSymbol = { val symbol = TypeAliasSymbol(lexicalName, params, ty) register(symbol) symbol } def declareThisAlias(): ValueSymbol = { val runtimeName = allocateRuntimeName("self") val symbol = ValueSymbol("this", runtimeName, Some(false), false) register(symbol) symbol } def declareValue( lexicalName: Str, isByvalueRec: Option[Boolean], isLam: Boolean, symbolicName: Opt[Str], /** Workaround for the first pass traversal with new definition typing. */ forNewDefsDryRun: Bool = false ): ValueSymbol = { val runtimeName = lexicalValueSymbols.get(lexicalName) match { // If we are implementing a stub symbol and the stub symbol did not shadow any other // symbols, it is safe to reuse its `runtimeName`. case S(sym: StubValueSymbol) if !sym.shadowing => sym.runtimeName case S(sym: ValueSymbol) if sym.forNewDefsDryRun => sym.runtimeName case S(sym: BuiltinSymbol) if !sym.accessed => sym.runtimeName case _ => allocateRuntimeName(lexicalName) } val symbol = ValueSymbol(lexicalName, runtimeName, isByvalueRec, isLam, forNewDefsDryRun) register(symbol) symbolicName.foreach { symbolicName => register(ValueSymbol(symbolicName, runtimeName, isByvalueRec, isLam, forNewDefsDryRun)) } symbol } def declareQualifierSymbol(lexicalName: Str): Str = { val symbol = ValueSymbol("this", allocateRuntimeName(lexicalName), S(false), false) qualifierSymbols += (symbol.runtimeName -> symbol) register(symbol) symbol.runtimeName } def resolveQualifier(runtimeName: Str): ValueSymbol = qualifierSymbols.getOrElse(runtimeName, throw CodeGenError(s"qualifier $runtimeName not found")) def declareStubValue(lexicalName: Str, symbolicName: Opt[Str])(implicit allowEscape: Bool): StubValueSymbol = declareStubValue(lexicalName, N, symbolicName) def declareStubValue(lexicalName: Str, previous: StubValueSymbol, symbolicName: Opt[Str])(implicit allowEscape: Bool ): StubValueSymbol = declareStubValue(lexicalName, S(previous), symbolicName) private def declareStubValue(lexicalName: Str, previous: Opt[StubValueSymbol], symbolicName: Opt[Str])(implicit allowEscape: Bool ): StubValueSymbol = { val symbol = lexicalValueSymbols.get(lexicalName) match { // If the existing symbol is a value symbol, but the value symbol is // declared in the dry-run of new definition typing, we can reuse the // runtime name. case S(valueSymbol: ValueSymbol) if valueSymbol.forNewDefsDryRun => StubValueSymbol(lexicalName, valueSymbol.runtimeName, false, previous) // If a stub with the same name has been defined, use the name. case S(symbol) => StubValueSymbol(lexicalName, symbol.runtimeName, true, previous) // Otherwise, we will allocate a new name. case N => StubValueSymbol(lexicalName, allocateRuntimeName(lexicalName), false, previous) } register(symbol) symbolicName.foreach { symbolicName => register(StubValueSymbol(symbolicName, symbol.runtimeName, false, previous)) } symbol } def stubize(sym: ValueSymbol, previous: StubValueSymbol)(implicit allowEscape: Bool ): StubValueSymbol = { unregister(sym) declareStubValue(sym.lexicalName, S(previous), N) } def declareRuntimeSymbol(): Str = { val name = allocateRuntimeName() runtimeSymbols += name name } def declareRuntimeSymbol(prefix: Str): Str = { val name = allocateRuntimeName(prefix) runtimeSymbols += name name } /** * This function declares a parameter in current scope and returns the * symbol's runtime name. * * @param name * @return */ def declareParameter(name: Str): Str = { val prefix = if (JSField.isValidIdentifier(name)) name else if (Symbol.isKeyword(name)) name + "$" else Scope.replaceTicks(name) val runtimeName = allocateRuntimeName(prefix) register(ValueSymbol(name, runtimeName, Some(false), false)) runtimeName } def existsRuntimeSymbol(name: Str): Bool = runtimeSymbols.contains(name) /** * Shorthands for deriving normal scopes. */ def derive(name: Str): Scope = (new Scope(name, S(this))).pullOuterSymbols(qualifierSymbols) def refreshRes(): Unit = { lexicalValueSymbols("res") = ValueSymbol("res", "res", Some(false), false) } } object Scope { /** * Shorthands for creating top-level scopes. */ def apply(name: Str): Scope = new Scope(name) private val nameAlphabet: Ls[Char] = Ls.from("abcdefghijklmnopqrstuvwxyz") private def replaceTicks(str: Str): Str = str.replace('\'', '$') } /** * This class collects temporary variables declared during translation and * generates JavaScript declarations for them after the translation. */ final case class TemporaryVariableEmitter() { private val names = scala.collection.mutable.HashSet[Str]() /** * Add a new variable name. The name must be a runtime name. */ def +=(name: Str): Unit = names += name /** * Emit a `let`-declaration for collected names and clear the collection. */ def emit(): Opt[JSLetDecl] = if (names.isEmpty) { N } else { val decl = JSLetDecl.from(names.toList) names.clear() S(decl) } /** * Get all names and clear the collection. */ def get(): Ls[Str] = { val vars = names.toList names.clear() vars } /** * A helper method to prepend the declaration to given statements. This calls * `emit` so the name collection will be cleared. */ def `with`(stmts: Ls[JSStmt]): Ls[JSStmt] = emit() match { case S(decl) => decl :: stmts case N => stmts } /** * A helper method to prepend temp variable declarations to given expression. * If no temp variables, return the expression as `Left`. This calls `emit` * so the name collection will be cleared. */ def `with`(expr: JSExpr): JSExpr \/ Ls[JSStmt] = emit() match { case S(decl) => R(decl :: expr.`return` :: Nil) case N => L(expr) } } ================================================ FILE: shared/src/main/scala/mlscript/codegen/Symbol.scala ================================================ package mlscript.codegen import mlscript.utils.shorthands._ import mlscript.Type import mlscript.JSClassDecl import mlscript.MethodDef import mlscript.{Term, Statement} import mlscript.TypeName import mlscript.NuTypeDef sealed trait LexicalSymbol { /** * The lexical name of the symbol. This is different from the runtime name, * the name of the symbol in the generated code. We allow duplicates lexical * names in the same scope. */ def lexicalName: Str } sealed trait RuntimeSymbol extends LexicalSymbol { def runtimeName: Str var visited: Bool = false } sealed trait TypeSymbol extends LexicalSymbol { val params: Ls[Str] val body: Type } sealed trait NuTypeSymbol { sym: TypeSymbol => val name: Str val methods: Ls[MethodDef[Left[Term, Type]]] // implemented methods val signatures: Ls[MethodDef[Right[Term, Type]]] // methods signatures val ctor: Ls[Statement] // statements in the constructor val nested: Ls[NuTypeDef] // nested class/mixin/module val qualifier: Opt[Str] // if it is inside another NuTypeSymbol, it indicates the runtime alias of parent's `this` val superParameters: Ls[Term] // parameters that need to be passed to the `super()` val isPlainJSClass: Bool // is this a plain class in JS val ctorParams: Opt[Ls[(Str, Bool)]] // parameters in the constructor val publicCtors: Ls[Str] // public(i.e., val-) parameters in the ctor val matchingFields: Ls[Str] = sym.body.collectFields // matchable fields(i.e., fields in `class ClassName(...)`) val unapplyMtd: Opt[MethodDef[Left[Term, Type]]] // unapply method def isNested: Bool = qualifier.isDefined // is nested in another class/mixin/module } sealed class ValueSymbol( val lexicalName: Str, val runtimeName: Str, val isByvalueRec: Option[Boolean], val isLam: Boolean, /** * Workaround for the first pass traversal with new definition typing. * "Dry run" here means that we haven't generated the code for the symbol * yet in the new-definition-typing mode, so the symbol is just defined * for the sake of code generation of classes/mixins/modules. * * This field should be deprecated after the `PreTyper` is done. See [PR * #197](https://github.com/hkust-taco/mlscript/pull/197) for more details. */ val forNewDefsDryRun: Boolean ) extends RuntimeSymbol { override def toString: Str = s"value $lexicalName" } object ValueSymbol { def apply( lexicalName: Str, runtimeName: Str, isByvalueRec: Option[Boolean], isLam: Boolean, /** Workaround for the first pass traversal with new definition typing. */ forNewDefsDryRun: Boolean = false ): ValueSymbol = new ValueSymbol(lexicalName, runtimeName, isByvalueRec, isLam, forNewDefsDryRun) } sealed case class TypeAliasSymbol( val lexicalName: Str, val params: Ls[Str], val body: Type ) extends TypeSymbol with LexicalSymbol { override def toString: Str = s"type $lexicalName" } final case class BuiltinSymbol(val lexicalName: Str, feature: Str) extends RuntimeSymbol { val runtimeName = lexicalName override def toString: Str = s"function $lexicalName" /** * `true` if the built-in value had been accessed before. * `Scope` will reuse the `lexicalName` if `accessed` is false. */ var accessed: Bool = false } final case class StubValueSymbol( override val lexicalName: Str, override val runtimeName: Str, /** * Whether this stub is shadowing another symbol. */ val shadowing: Bool, previous: Opt[StubValueSymbol] )(implicit val accessible: Bool) extends RuntimeSymbol { override def toString: Str = s"value $lexicalName" } final case class ClassSymbol( lexicalName: Str, runtimeName: Str, params: Ls[Str], body: Type, methods: Ls[MethodDef[Left[Term, Type]]], ) extends TypeSymbol with RuntimeSymbol with Ordered[ClassSymbol] { import scala.math.Ordered.orderingToOrdered override def compare(that: ClassSymbol): Int = lexicalName.compare(that.lexicalName) override def toString: Str = s"class $lexicalName ($runtimeName)" } final case class NewClassMemberSymbol( name: Str, isByvalueRec: Option[Boolean], isLam: Boolean, isPrivate: Boolean, qualifier: Option[Str] ) extends RuntimeSymbol { override def toString: Str = s"new class member $name" // Class members should have fixed names determined by users override def lexicalName: Str = name override def runtimeName: Str = name } final case class NewClassSymbol( name: Str, params: Ls[Str], ctorParams: Opt[Ls[(Str, Bool)]], body: Type, methods: Ls[MethodDef[Left[Term, Type]]], unapplyMtd: Opt[MethodDef[Left[Term, Type]]], signatures: Ls[MethodDef[Right[Term, Type]]], ctor: Ls[Statement], superParameters: Ls[Term], publicCtors: Ls[Str], nested: Ls[NuTypeDef], qualifier: Opt[Str], isPlainJSClass: Bool ) extends TypeSymbol with RuntimeSymbol with NuTypeSymbol { override def toString: Str = s"new class $name" // Classes should have fixed names determined by users override def lexicalName: Str = name override def runtimeName: Str = name } final case class MixinSymbol( name: Str, params: Ls[Str], body: Type, methods: Ls[MethodDef[Left[Term, Type]]], signatures: Ls[MethodDef[Right[Term, Type]]], ctor: Ls[Statement], publicCtors: Ls[Str], nested: Ls[NuTypeDef], qualifier: Opt[Str] ) extends TypeSymbol with RuntimeSymbol with NuTypeSymbol { override def toString: Str = s"mixin $name" // Mixins should have fixed names determined by users override def lexicalName: Str = name override def runtimeName: Str = name // Mixins should pass `...rest` to the `super()` // But the variable name is not sure when we create the symbol object override val superParameters: Ls[Term] = Nil val isPlainJSClass: Bool = true val ctorParams: Opt[Ls[(Str, Bool)]] = N val unapplyMtd: Opt[MethodDef[Left[Term, Type]]] = N } final case class ModuleSymbol( name: Str, params: Ls[Str], body: Type, methods: Ls[MethodDef[Left[Term, Type]]], signatures: Ls[MethodDef[Right[Term, Type]]], ctor: Ls[Statement], superParameters: Ls[Term], nested: Ls[NuTypeDef], qualifier: Opt[Str] ) extends TypeSymbol with RuntimeSymbol with NuTypeSymbol { override def toString: Str = s"module $name" // Modules should have fixed names determined by users override def lexicalName: Str = name override def runtimeName: Str = name val isPlainJSClass: Bool = false val ctorParams: Opt[Ls[(Str, Bool)]] = N val publicCtors: Ls[Str] = Nil val unapplyMtd: Opt[MethodDef[Left[Term, Type]]] = N } final case class TraitSymbol( lexicalName: Str, runtimeName: Str, params: Ls[Str], body: Type, methods: Ls[MethodDef[Left[Term, Type]]], ) extends TypeSymbol with RuntimeSymbol { override def toString: Str = s"trait $lexicalName" } object Symbol { def isKeyword(name: Str): Bool = keywords contains name private val keywords: Set[Str] = Set( // Reserved keywords as of ECMAScript 2015 "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof", "new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "yield", // The following are reserved as future keywords by the ECMAScript specification. // They have no special functionality at present, but they might at some future time, // so they cannot be used as identifiers. These are always reserved: "enum", // The following are only reserved when they are found in strict mode code: "abstract", "boolean", "byte", "char", "double", "final", "float", "goto", "int", "long", "native", "short", "synchronized", "throws", "transient", "volatile", ) } ================================================ FILE: shared/src/main/scala/mlscript/codegen/typescript/TsTypegen.scala ================================================ package mlscript package codegen.typescript import scala.collection.mutable.{ListBuffer, Map => MutMap, SortedMap => MutSortedMap} import scala.collection.immutable.Set import mlscript.codegen.CodeGenError import mlscript.utils._, shorthands._ import mlscript.codegen._ /** Typescript typegen code builder for an mlscript typing unit */ final class TsTypegenCodeBuilder { private val typeScope: Scope = Scope("globalTypeScope") private val termScope: Scope = Scope("globalTermScope") private val typegenCode: ListBuffer[SourceCode] = ListBuffer.empty /** Return complete typegen code for current typing unit * * @return * SourceCode */ def toSourceCode(): SourceCode = { SourceCode("// start ts") +=: typegenCode typegenCode += SourceCode("// end ts") SourceCode.bulkConcat(typegenCode) } /** Context for converting a statement or type definition * * @param typeVarMapping * mapping from type variables to their clash-free parameter names * @param termScope * scope for argument names * @param typeScope * scope for type parameter names */ protected case class TypegenContext( typeVarMapping: MutSortedMap[TypeVar, String], termScope: Scope, typeScope: Scope, ) object TypegenContext { def apply(mlType: Type): TypegenContext = { val existingTypeVars = ShowCtx.mk(mlType :: Nil, false, "").vs val typegenTypeScope = typeScope.derive("localTypeScope") val typegenTermScope = termScope.derive("localTermScope") val typeVarMapping = existingTypeVars.to(MutSortedMap) // initialize local term scope with global term scope as parent // this will reduce cases where global names are similar to function // argument names. For e.g. the below case will be avoided // export const arg0: int // export const f: (arg0: int) => int TypegenContext(typeVarMapping, typegenTermScope, typegenTypeScope) } // create a context with pre-created type var to name mapping def apply(mlType: Type, typeVarMapping: MutSortedMap[TypeVar, String]): TypegenContext = { val typegenTypeScope = typeScope.derive("localTypeScope") val typegenTermScope = termScope.derive("localTermScope") TypegenContext(typeVarMapping, typegenTermScope, typegenTypeScope) } } def declareTypeDef(typeDef: TypeDef): TypeSymbol = typeScope.declareTypeSymbol(typeDef) /** * Start adding type definition and store partially translated * type definition as state * * @param typeDef */ def addTypeDef(typeDef: TypeDef, methods: List[(String, Type)]): Unit = { val tySymb = typeScope.getTypeSymbol(typeDef.nme.name).getOrElse( throw CodeGenError(s"No type definition for ${typeDef.nme.name} exists") ) tySymb match { case (classInfo: ClassSymbol) => addTypeGenClassDef(classInfo, methods) case (aliasInfo: TypeAliasSymbol) => addTypeGenTypeAlias(aliasInfo) case (traitInfo: TraitSymbol) => addTypegenTraitDef(traitInfo, methods) case _ => ??? // TODO } } /** * Translate class method to typescript declaration * * @param declTypeParms type params from class or trait declaring the method * @param methodName * @param methodType */ def addMethodDeclaration(declTypeParams: Set[String], methodName: String, methodType: Type): SourceCode = { // unwrap method function type to remove implicit this val methodSourceCode = methodType match { case Function(lhs, rhs) => { // Create a mapping from type var to their friendly name for lookup val typegenCtx = TypegenContext(rhs) rhs match { case f@Function(lhs, rhs) => val lhsTypeSource = toTypegenFunctionLhs(lhs, true)(typegenCtx, Some(false)) val rhsTypeSource = toTsType(rhs)(typegenCtx, Some(true)) // only use free variables for type parameters val typeParams = typegenCtx.typeVarMapping.iterator .filter(tup => rhs.freeTypeVariables.contains(tup._1) && // ignore class type parameters since they are implicitly part of class scope // if no name hint then the type variable is certainly not a class type parameter // its friendly name has been generated tup._1.nameHint.fold(true)(!declTypeParams.contains(_)) ) .map { case (_, varName) => SourceCode(varName)} .toList SourceCode(s" $methodName") ++ SourceCode.paramList(typeParams) ++ lhsTypeSource ++ SourceCode.colon ++ rhsTypeSource case _ => val tsType = toTsType(rhs)(typegenCtx, Some(true)) // only use free variables for type parameters val typeParams = typegenCtx.typeVarMapping.iterator .filter(tup => rhs.freeTypeVariables.contains(tup._1) && // ignore class type parameters since they are implicitly part of class scope // if no name hint then the type variable is certainly not a class type parameter // its friendly name has been generated tup._1.nameHint.fold(true)(!declTypeParams.contains(_)) ) .map { case (_, varName) => SourceCode(varName)} .toList // known value methods are translated to readonly attributes of the class if (typeParams.isEmpty) SourceCode(s" readonly $methodName") ++ SourceCode.colon ++ tsType // known value methods are not allowed to have type variables in ts // else throw CodeGenError(s"Cannot translate known value method named $methodName free type variables") else SourceCode(s" readonly $methodName") ++ SourceCode.paramList(typeParams) ++ SourceCode.colon ++ tsType } } // top level method type must be a function case _ => throw CodeGenError(s"Cannot translate malformed method: $methodName because it does not have top level function") } methodSourceCode } def addTypegenTraitDef(traitInfo: TraitSymbol, methods: List[(String, Type)]): Unit = { val traitName = traitInfo.lexicalName val traitBody = traitInfo.body val typeParams = traitInfo.params.iterator.map(SourceCode(_)).toList // extend super traits var traitDeclaration = SourceCode(s"export interface $traitName") ++ SourceCode.paramList(typeParams) val superTraits = typeScope.resolveImplementedTraits(traitBody) if (!superTraits.isEmpty) { val superTraitsCode = superTraits.map { symb => val traitName = symb.lexicalName val traitParams = symb.params.iterator.map(SourceCode(_)).toList SourceCode(s"$traitName") ++ SourceCode.paramList(traitParams) } traitDeclaration ++= SourceCode(" extends ") ++ SourceCode.sepBy(superTraitsCode) } traitDeclaration ++= SourceCode.space ++ SourceCode.openCurlyBrace // add fields defined in the trait traitBody.collectBodyFieldsAndTypes .foreach{case (fieldVar, fieldType) => val fieldCode = toTsType(fieldType)(TypegenContext(fieldType), Some(true)) traitDeclaration += SourceCode(" ") ++ SourceCode(fieldVar.name) ++ SourceCode.colon ++ fieldCode } // add methods methods.foreach { case (name, methodType) => traitDeclaration += addMethodDeclaration(traitInfo.params.toSet, name, methodType) } typegenCode += traitDeclaration + SourceCode.closeCurlyBrace } def addTypeGenClassDef(classInfo: ClassSymbol, methods: List[(String, Type)]): Unit = { val className = classInfo.lexicalName val classBody = classInfo.body val baseClass = typeScope.resolveBaseClass(classBody) val typeParams = classInfo.params.iterator.map(SourceCode(_)).toList // create mapping for class body fields and types // class body includes fields from all implemented traits val classFieldsAndType = getClassFieldAndTypes(classInfo) // fields can be re-declared with different types at different // levels of inheritance. The final type is a union of all these types .groupMap(_._1)(_._2) // extend with base class if it exists var classDeclaration = SourceCode(s"export declare class $className") ++ SourceCode.paramList(typeParams) baseClass.map(baseClass => { val baseClassName = baseClass.lexicalName val baseClassTypeParams = baseClass.params.iterator.map(SourceCode(_)).toList classDeclaration ++= SourceCode(s" extends ${baseClass.lexicalName}") ++ SourceCode.paramList(baseClassTypeParams) }) classDeclaration ++= SourceCode.space ++ SourceCode.openCurlyBrace // add body fields classFieldsAndType .map { case (fieldVar, fieldTypes) => val fieldTypesCode = toTypegenInheritedFields(fieldTypes) (SourceCode(fieldVar.name), fieldTypesCode) } .foreachEntry { (fieldVar, fieldType) => { classDeclaration += SourceCode(" ") ++ fieldVar ++ SourceCode.colon ++ fieldType }} // constructor needs all fields including super classes val allFieldsAndTypes = (classFieldsAndType ++ baseClass.map(getClassFieldAndTypes(_, true)).getOrElse(List.empty).groupMap(_._1)(_._2)) .map {case (fieldVar, fieldTypes) => val fieldTypesCode = toTypegenInheritedFields(fieldTypes) (SourceCode(fieldVar.name), fieldTypesCode) }.toList classDeclaration += SourceCode(" constructor(fields: ") ++ SourceCode.recordWithEntries(allFieldsAndTypes) ++ SourceCode(")") // add methods methods.foreach { case (name, methodType) => classDeclaration += addMethodDeclaration(classInfo.params.toSet, name, methodType) } typegenCode += classDeclaration + SourceCode.closeCurlyBrace } /** * Convert the intersections of all distinct fields * in the inheritance hierarchy of a trait or class * into SourceCode * * @param fieldTypes * list of field types * @return * SourceCode */ private def toTypegenInheritedFields(fieldTypes: List[Type]): SourceCode = { // field types will always have 1 or more elements fieldTypes.distinct match { case fieldType :: Nil => toTsType(fieldType)(TypegenContext(fieldType), Some(true)) case fieldTypes => // multiple types are intersected hence typegen is done // using the precedence of the intersection operator. // Only distinct types are considered for intersection val fieldTypesCode = fieldTypes.distinct .map(fieldType => toTsType(fieldType, false, 2)(TypegenContext(fieldType), Some(true))) SourceCode.sepBy(fieldTypesCode, SourceCode.ampersand) } } /** * Type conversion for function arguments is a special case. Explicit tuples * are handled differently from types that hide tuples. * * All parameters are same as `toTsType` * * @param lhs * @param funcArg * @param typePrecedence * @param typegenCtx * @param pol * @return */ private def toTypegenFunctionLhs(lhs: Type, funcArg: Boolean = false, typePrecedence: Int = 0)(implicit typegenCtx: TypegenContext, pol: Option[Boolean], ): SourceCode = { // flip polarity for input type of function // lhs translates to the complete argument list // method definition only affects source code representation of top level function lhs match { // tuple are handled specially because // they create their own arg names case Tuple(fields) => toTsType(lhs, true)(typegenCtx, Some(false)) // there are other types which might hide tuple arguments // like recursive types, AppliedTypes or bounded types // their tuple types can be uniformly converted // using a spread operator case _ => val arg = typegenCtx.termScope.declareRuntimeSymbol("arg") (SourceCode(s"...$arg: ") ++ toTsType(lhs, true)(typegenCtx, Some(false))).parenthesized } } /** * List of pairs of field variables and their types for the trait including its super traits * * @param traitSymbol * @return */ private def getTraitFieldAndTypes(body: Type): List[Var -> Type] = { val bodyFields = body.collectBodyFieldsAndTypes typeScope .resolveImplementedTraits(body) .foldLeft(bodyFields){ case (fields, symb) => fields ::: getTraitFieldAndTypes(symb.body) } } /** * find all fields and types for class including all traits * optionally include base class fields as well * * @param classSymbol * @param includeBaseClass include base class fields if true, default is false * @return */ private def getClassFieldAndTypes(classSymbol: ClassSymbol, includeBaseClass: Boolean = false): List[Var -> Type] = { val bodyFieldsAndTypes = classSymbol.body.collectBodyFieldsAndTypes ++ getTraitFieldAndTypes(classSymbol.body) if (includeBaseClass) { bodyFieldsAndTypes ++ typeScope.resolveBaseClass(classSymbol.body) .map(getClassFieldAndTypes(_, true)) .getOrElse(List.empty) } else { bodyFieldsAndTypes } } def addTypeGenTypeAlias(aliasInfo: TypeAliasSymbol): Unit = { val aliasName = aliasInfo.lexicalName val mlType = aliasInfo.body // Create a mapping from type var to their friendly name for lookup val typegenCtx = TypegenContext(mlType) val tsType = toTsType(mlType)(typegenCtx, Some(true)) // only use non recursive type variables for type parameters val typeParams = typegenCtx.typeVarMapping.iterator .filter(tup => mlType.freeTypeVariables.contains(tup._1)) .map { case (_, varName) => SourceCode(varName)} .toList typegenCode += SourceCode(s"export type $aliasName") ++ SourceCode.paramList(typeParams) ++ SourceCode.equalSign ++ tsType } /** Converts a term definition to its typescript declaration including any adhoc type aliases created for it * * @param mlType * the type to be converted to source code * @param termName * name of term that's been declared. If none is given default "res" is used to indicate type of value of result * from evaluating the term * @return */ def addTypeGenTermDefinition(mlType: Type, termName: Option[String]): Unit = { // `res` definitions are allowed to be shadowed val defName = termName.getOrElse("res") // Create a mapping from type var to their friendly name for lookup val typegenCtx = TypegenContext(mlType) val tsType = toTsType(mlType)(typegenCtx, Some(true)) // only use non recursive type variables for type parameters val typeParams = typegenCtx.typeVarMapping.iterator .filter(tup => mlType.freeTypeVariables.contains(tup._1)) .map { case (_, varName) => SourceCode(varName) } .toList typegenCode += (SourceCode(s"export declare const $defName") ++ SourceCode.paramList(typeParams) ++ SourceCode.colon ++ tsType) } /** Converts an mlscript type to source code representation of the ts type. It also keep tracks of extra type * variables and type parameters defined through the context. * * polarity tracks polarity of the current type. * Some(true) - positive polarity * Some(false) - negative polarity * * None - no polarity (invariant) * * @param mlType * mlscript type to convert * @param funcArg * true if Tuple type is the lhs of a function. Used to translate tuples as multi-parameter functions * @param typePrecedence * type precedence of an ml type when translating to ts. Decides where extra parenthesis gets placed * @param typegenCtx * type generation context for allocating new names * @param pol * polarity of type * @return */ private def toTsType(mlType: Type, funcArg: Boolean = false, typePrecedence: Int = 0)(implicit typegenCtx: TypegenContext, pol: Option[Boolean], ): SourceCode = { mlType match { // these types do not mutate typegen context case Union(TypeName("true"), TypeName("false")) | Union(TypeName("false"), TypeName("true")) => SourceCode("boolean") case u@Union(lhs, rhs) => val unionCode = SourceCode.sepBy( List(toTsType(lhs, funcArg, this.typePrecedence(u)), toTsType(rhs, funcArg, this.typePrecedence(u))), SourceCode.separator ) // a union has lower precedence than an intersection if (this.typePrecedence(u) < typePrecedence) unionCode.parenthesized else unionCode case i@Inter(lhs, rhs) => SourceCode.sepBy( List(toTsType(lhs, funcArg, this.typePrecedence(i)), toTsType(rhs, funcArg, this.typePrecedence(i))), SourceCode.ampersand ) case Record(fields) => // ts can only handle fields that have only out type or the same in out types fields.iterator.foreach { field => if (field._2.in.exists(in => in =/= field._2.out)) throw CodeGenError( s"Cannot convert mutable record field with different in out types to typescript (${ field})") } SourceCode.recordWithEntries( fields.map(entry => if (entry._2.in.isDefined) (SourceCode(entry._1.name), toTsType(entry._2.out)) else (SourceCode(s"readonly ${entry._1.name}"), toTsType(entry._2.out)) )) case Tuple(fields) => // ts can only handle fields that have only out type or the same in out types fields.iterator.foreach { field => if (field._2.in.exists(in => in =/= field._2.out)) throw CodeGenError( s"Cannot convert mutable tuple field with different in out types to typescript (${ field})") } // tuple that is a function argument becomes // multi-parameter argument list // ! Note: No equivalent to readonly fields for tuples if (funcArg) { val argList = fields .map{ case field => val arg = typegenCtx.termScope.declareRuntimeSymbol("arg") val argType = toTsType(field._2.out) SourceCode(s"$arg: ") ++ argType } SourceCode.sepBy(argList).parenthesized } // regular tuple becomes fixed length array else { val tupleArrayCode = SourceCode.horizontalArray(fields.map(field => toTsType(field._2.out))) // don't add a readonly if all fields in the tuple are mutable // this is because readonly is a weak constraint and can easily // be upcasted to non-readonly if (fields.iterator.map(_._1.isDefined).forall(identity)) { tupleArrayCode } else { SourceCode("readonly ") ++ tupleArrayCode } } case Top => SourceCode("unknown") case Bot => SourceCode("never") case TypeName(name) => SourceCode(name) case Literal(IntLit(n)) => SourceCode(n.toString + (if (JSBackend isSafeInteger n) "" else "n")) case Literal(DecLit(n)) => SourceCode(n.toString) case Literal(StrLit(s)) => SourceCode(JSLit.makeStringLiteral(s)) case Literal(UnitLit(b)) => SourceCode(if (b) "undefined" else "null") // these types may mutate typegen context by argCounter, or // by creating new type aliases case f@Function(lhs, rhs) => // flip polarity for input type of function // lhs translates to the complete argument list // method definition only affects source code representation of top level function val lhsTypeSource = toTypegenFunctionLhs(lhs, true)(typegenCtx, pol.map(!_)) val rhsTypeSource = toTsType(rhs) val funcSourceCode = lhsTypeSource ++ SourceCode.fatArrow ++ rhsTypeSource // if part of a higher precedence binary operator // in this case only Inter and Union is possible // parenthesize to maintain correct precedence if (this.typePrecedence(f) < typePrecedence) { funcSourceCode.parenthesized } else { funcSourceCode } // a recursive type is aliased as a self referencing type case r@Recursive(uv, body) => // allocate the clash-free name for uv in typegen scope // update mapping from type variables to val uvName = typegenCtx.typeVarMapping .getOrElse(uv, throw CodeGenError(s"Did not find mapping for type variable $uv. Unable to generated ts type.") ) val typeVarMapping = typegenCtx.typeVarMapping // create new type gen context and recalculate rec var and // non-rec var for current type val nestedTypegenCtx = TypegenContext(mlType, typeVarMapping) // recursive type does not have any other type variables // (except itself) if (mlType.freeTypeVariables.size === 0) { typeScope.declareTypeAlias(uvName, List.empty, r) val bodyType = toTsType(body)(typegenCtx, pol) typegenCode += (SourceCode(s"export type $uvName") ++ SourceCode.equalSign ++ bodyType) SourceCode(uvName) } // recursive type has other type variables // so now it is an applied type else { val uvTypeParams = typeVarMapping.iterator .filter(tup => mlType.freeTypeVariables.contains(tup._1) && // recursive type variable from outer scope have been converted // to type aliases. They do not need to be part of type parameters. // filter for type vars that are not declared as type aliases in type scope typeVarMapping.get(tup._1).flatMap(varName => typeScope.getTypeAliasSymbol(varName)).isEmpty ) .map(_._2) .toList // declare type alias to record that this type var has been aliased typeScope.declareTypeAlias(uvName, uvTypeParams, body) val uvAppliedName = SourceCode(uvName) ++ SourceCode.paramList(uvTypeParams.map(SourceCode(_))) val bodyType = toTsType(body)(nestedTypegenCtx, pol) typegenCode += (SourceCode(s"export type $uvAppliedName") ++ SourceCode.equalSign ++ bodyType) uvAppliedName } case AppliedType(base, targs) => if (targs.length =/= 0) { SourceCode(base.name) ++ SourceCode.openAngleBracket ++ SourceCode.sepBy(targs.map(toTsType(_)(typegenCtx, None))) ++ SourceCode.closeAngleBracket } else { // no type arguments required then print without brackets SourceCode(base.name) } // Neg requires creating a parameterized type alias to hold the negated definition case Neg(base) => // Negative type only works in positions of positive polarity if (pol === Some(false)) { throw CodeGenError( s"Cannot generate type for negated type at negative polarity for $mlType" ) } // try to allocate common Negate type alias if (!typeScope.existsRuntimeSymbol("Neg")) { typeScope.declareRuntimeSymbol("Neg") typegenCode += SourceCode("type Neg = FromType extends NegatedType ? never: FromType") } // introduce a new type parameter for the `FromType` val typeParam = typegenCtx.typeScope.declareRuntimeSymbol() typegenCtx.typeVarMapping += ((TypeVar(Right(typeParam), None), typeParam)) val baseType = toTsType(base) SourceCode(s"Neg<$baseType, $typeParam>") case Rem(base, names) => SourceCode("Omit") ++ SourceCode.openAngleBracket ++ toTsType(base) ++ SourceCode.commaSpace ++ SourceCode.sepBy(names.map(name => SourceCode(s"\"${name.name}\"")), SourceCode.separator) ++ SourceCode.closeAngleBracket case Bounds(lb, ub) if lb === ub => toTsType(lb) case Bounds(lb, ub) => pol match { // positive polarity takes upper bound case Some(true) => toTsType(ub) // negative polarity takes lower bound case Some(false) => toTsType(lb) case None => // TODO: Yet to handle invariant types throw CodeGenError(s"Cannot generate type for invariant type $mlType") } case WithExtension(base, rcd) => toTsType(Inter(Rem(base, rcd.fields.map(tup => tup._1)), rcd)) // get clash free name for type variable case t @ TypeVar(_, _) => val tvarName = typegenCtx.typeVarMapping.getOrElse(t, throw CodeGenError(s"Did not find mapping for type variable $t. Unable to generated ts type.")) // return type alias form if it exists typeScope.getTypeAliasSymbol(tvarName).map { taliasInfo => SourceCode(taliasInfo.lexicalName) ++ SourceCode.paramList(taliasInfo.params.map(SourceCode(_))) }.getOrElse(SourceCode(tvarName)) case Constrained(base, tvbs, where, _) => throw CodeGenError(s"Cannot generate type for `where` clause $tvbs $where") case _: Splice | _: TypeTag | _: PolyType | _: Selection => throw CodeGenError(s"Cannot yet generate type for: $mlType") } } /** * Decides the precendence of the mltype when translating to typescript * * @param mlType * @return */ private def typePrecedence(mlType: Type): Int = mlType match { case _: Inter => 2 case _: Union => 1 case _ => 0 } } ================================================ FILE: shared/src/main/scala/mlscript/helpers.scala ================================================ package mlscript import scala.util.chaining._ import scala.collection.mutable.{Map => MutMap, SortedMap => SortedMutMap, Set => MutSet, Buffer} import scala.collection.immutable.SortedMap import math.Ordered.orderingToOrdered import mlscript.utils._, shorthands._ import scala.annotation.tailrec // Auxiliary definitions for types trait SignatureImpl extends Located { self: Signature => def children: List[Located] = members } trait TypeLikeImpl extends Located { self: TypeLike => def showDbg2: Str = show(newDefs = true) // TODO more lightweight debug printing routine def show(newDefs: Bool): Str = showIn(0)(ShowCtx.mk(this :: Nil, newDefs)) private def parensIf(str: Str, cnd: Boolean): Str = if (cnd) "(" + str + ")" else str private def showField(f: Field)(implicit ctx: ShowCtx): Str = f match { case Field(N, ub) => ub.showIn(0) case Field(S(lb), ub) if lb === ub => ub.showIn(0) case Field(S(Bot), ub) => s"out ${ub.showIn(0)}" case Field(S(lb), Top) => s"in ${lb.showIn(0)}" case Field(S(lb), ub) => s"in ${lb.showIn(0)} out ${ub.showIn(0)}" } private def showFields(fs: Ls[Opt[Var] -> Field])(implicit ctx: ShowCtx): Ls[Str] = fs.map(nt => s"${nt._2.mutStr}${nt._1.fold("")(_.name + ": ")}${showField(nt._2)}") def showIn(outerPrec: Int)(implicit ctx: ShowCtx): Str = this match { // TODO remove obsolete pretty-printing hacks case Top => "anything" case Bot => "nothing" case TypeName(name) => name // case uv: TypeVar => ctx.vs.getOrElse(uv, s"[??? $uv ???]") case TypeTag(name) => "#"+name case uv: TypeVar => ctx.vs(uv) case Recursive(n, b) => parensIf(s"${b.showIn(2)} as ${ctx.vs(n)}", outerPrec > 1) case WithExtension(b, r) => parensIf(s"${b.showIn(2)} with ${r.showIn(0)}", outerPrec > 1) case Function(Tuple(fs), r) => val innerStr = fs match { case Nil => "()" case N -> Field(N, f) :: Nil if !f.isInstanceOf[Tuple] => f.showIn(31) case _ => val inner = showFields(fs) if (ctx.newDefs) inner.mkString("(", ", ", ")") else inner.mkString("(", ", ", ",)") } parensIf(innerStr + " -> " + r.showIn(30), outerPrec > 30) case Function(l, r) if ctx.newDefs => parensIf("(..." + l.showIn(0) + ") -> " + r.showIn(30), outerPrec > 30) case Function(l, r) => parensIf(l.showIn(31) + " -> " + r.showIn(30), outerPrec > 30) case Neg(t) => s"~${t.showIn(100)}" case Record(fs) => val strs = fs.map { nt => val nme = nt._1.name if (nme.isCapitalized) nt._2 match { case Field(N | S(Bot), Top) => s"$nme" case Field(S(lb), ub) if lb === ub => s"$nme = ${ub.showIn(0)}" case Field(N | S(Bot), ub) => s"$nme <: ${ub.showIn(0)}" case Field(S(lb), Top) => s"$nme :> ${lb.showIn(0)}" case Field(S(lb), ub) => s"$nme :> ${lb.showIn(0)} <: ${ub.showIn(0)}" } else s"${nt._2.mutStr}${nme}: ${showField(nt._2)}" } if (strs.foldLeft(0)(_ + _.length) > 80) strs.mkString("{\n" + ctx.indStr, ",\n" + ctx.indStr, "") .indentNewLines(ShowCtx.indentation) + "\n" + ctx.indStr + "}" else strs.mkString("{", ", ", "}") case Splice(fs) => val inner = fs.map{case L(l) => s"...${l.showIn(0)}" case R(r) => s"${showField(r)}"} if (ctx.newDefs) inner.mkString("[", ", ", "]") else inner.mkString("(", ", ", ")") case Tuple(fs) => val inner = showFields(fs)(ctx) if (ctx.newDefs) inner.mkString("[", ", ", "]") else inner.mkString("(", ", ", if (fs.nonEmpty) ",)" else ")") case Union(TypeName("true"), TypeName("false")) | Union(TypeName("false"), TypeName("true")) => TypeName("bool").showIn(0) // case Union(l, r) => parensIf(l.showIn(ctx, 20) + " | " + r.showIn(ctx, 20), outerPrec > 20) // case Inter(l, r) => parensIf(l.showIn(ctx, 25) + " & " + r.showIn(ctx, 25), outerPrec > 25) case c: Composed => val prec = if (c.pol) 20 else 25 val opStr = if (c.pol) " | " else " & " c.distinctComponents match { case Nil => (if (c.pol) Bot else Top).showIn(outerPrec) case x :: Nil => x.showIn(outerPrec) case _ => parensIf(c.distinctComponents.iterator .map(_.showIn(prec)) .reduce(_ + opStr + _), outerPrec > prec) } // case Bounds(Bot, Top) => s"?" case Bounds(lb, ub) if lb === ub => lb.showIn(outerPrec) case Bounds(Bot, ub) => s"out ${ub.showIn(0)}" case Bounds(lb, Top) => s"in ${lb.showIn(0)}" case Bounds(lb, ub) => s"in ${lb.showIn(0)} out ${ub.showIn(0)}" // case AppliedType(n, args) => s"${n.name}${args.map(_.showIn(0)).mkString(ctx.<, ", ", ctx.>)}" case Selection(b, n) => b.showIn(100) + "." + n.name case Rem(b, ns) => s"${b.showIn(90)}${ns.map("\\"+_.name).mkString}" case Literal(IntLit(n)) => n.toString case Literal(DecLit(n)) => n.toString case Literal(StrLit(s)) => "\"" + s + "\"" case Literal(UnitLit(b)) => if (b) if (ctx.newDefs) "()" else "undefined" else "null" case PolyType(Nil, body) => body.showIn(outerPrec) case PolyType(targs, body) => parensIf( s"${targs.iterator.map(_.fold(_.name, _.showIn(0))) .mkString("forall ", " ", ".")} ${body.showIn(0)}", outerPrec > 1 // or 0? ) case Constrained(b, bs, ws, tscs) => val oldCtx = ctx val bStr = b.showIn(0).stripSuffix("\n") val multiline = bStr.contains('\n') parensIf({ implicit val ctx: ShowCtx = if (multiline) oldCtx.indent else oldCtx.indent.indent s"${ bStr }\n${oldCtx.indStr}${if (multiline) "" else " "}where${ bs.map { case (uv, Bounds(Bot, ub)) => s"\n${ctx.indStr}${ctx.vs(uv)} <: ${ub.showIn(0)}" case (uv, Bounds(lb, Top)) => s"\n${ctx.indStr}${ctx.vs(uv)} :> ${lb.showIn(0)}" case (uv, Bounds(lb, ub)) if lb === ub => s"\n${ctx.indStr}${ctx.vs(uv)} := ${lb.showIn(0)}" case (uv, Bounds(lb, ub)) => val vstr = ctx.vs(uv) s"\n${ctx.indStr}${vstr } :> ${lb.showIn(0)}" + s"\n${ctx.indStr}${" " * vstr.length} <: ${ub.showIn(0)}" }.mkString }${ws.map{ case Bounds(lo, hi) => s"\n${ctx.indStr}${lo.showIn(0)} <: ${hi.showIn(0)}" // TODO print differently from bs? }.mkString }${tscs.map{ case (tvs, constrs) => val s = tvs.map(u => (if (u._1) "+" else "-") ++ u._2.showIn(0)) .mkString("[", ", ", "]") s"\n${ctx.indStr}" + s + s" in ${constrs.map(_.map(_.showIn(0)).mkString("[", ", ", "]")).mkString("{", ", ", "}")}" }.mkString}" }, outerPrec > 0) case fd @ NuFunDef(isLetRec, nme, snme, targs, rhs) => s"${if (fd.isMut) "mut " else ""}${isLetRec match { case S(false) => if (fd.genField) "val" else "let" case S(true) => if (fd.genField) die else "let rec" case N => "fun" }}${snme.fold("")(" (" + _.name + ")") } ${nme.name}${targs.map(_.showIn(0)).mkStringOr(", ", "[", "]")}${rhs match { case L(trm) => " = ..." case R(ty) => ": " + ty.showIn(0) }}" case Signature(decls, res) => (decls.map(ctx.indStr + _.showIn(0) + "\n") ::: (res match { case S(ty) => ctx.indStr + ty.showIn(0) + "\n" :: Nil case N => Nil })).mkString case NuTypeDef(kind @ Als, nme, tparams, params, ctor, sig, parents, sup, ths, body) => assert(params.isEmpty, params) assert(ctor.isEmpty, ctor) assert(parents.isEmpty, parents) assert(sup.isEmpty, sup) assert(ths.isEmpty, ths) assert(body.entities.isEmpty, body) s"type ${nme.name}${tparams.map(_._2.showIn(0)).mkStringOr(", ", "[", "]")} = ${ sig.getOrElse(die).showIn(0)}" case td @ NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, sup, ths, body) => val bodyCtx = ctx.indent s"${td.declareLoc.fold("")(_ => "declare ")}${td.abstractLoc.fold("")(_ => "abstract ")}${kind.str} ${ nme.name}${tparams.map(_._2.showIn(0)).mkStringOr(", ", "[", "]")}${params match { case S(Tup(fields)) => s"(${fields.map { case (N, Fld(_, Asc(v: Var, ty))) => v.name + ": " + ty.showIn(0) case (N | S(_), _) => lastWords("ill-formed type definition parameter") }.mkString(", ")})" case _ => "" }}${sig.fold("")(": " + _.showIn(0)(bodyCtx))}${parents match { case Nil => "" case ps => " extends " + ps.iterator.map(_.print(false)).mkString(", ") // TODO pp parent terms... }}${if (body.entities.isEmpty && sup.isEmpty && ths.isEmpty) "" else " {\n" + sup.fold("")(s"${bodyCtx.indStr}super: " + _.showIn(0)(bodyCtx) + "\n") + ths.fold("")(s"${bodyCtx.indStr}this: " + _.showIn(0)(bodyCtx) + "\n") + body.entities.collect { case Constructor(params, body) => s"${bodyCtx.indStr}constructor(${params.fields.map { case N -> Fld(FldFlags.empty, Asc(Var(nme), ty)) => s"${nme}: ${ty.showIn(0)(bodyCtx)}" case _ => lastWords("ill-formed constructor parameter") }.mkString(", ")})\n" }.mkString + Signature(body.entities.collect { case d: NuDecl => d }, N).showIn(0)(bodyCtx) + ctx.indStr + "}" }" } def childrenTypes: List[TypeLike] = this match { case _: NullaryType => Nil case Function(l, r) => l :: r :: Nil case Bounds(l, r) => l :: r :: Nil case Neg(b) => b :: Nil case Record(fs) => fs.flatMap(f => f._2.in.toList ++ (f._2.out :: Nil)) case Tuple(fs) => fs.flatMap(f => f._2.in ++ (f._2.out :: Nil)) case Union(l, r) => l :: r :: Nil.toList case Inter(l, r) => l :: r :: Nil case Recursive(n, b) => b :: Nil case AppliedType(n, ts) => ts case Selection(b, nme) => b :: nme :: Nil case Rem(b, _) => b :: Nil case WithExtension(b, r) => b :: r :: Nil case PolyType(targs, body) => targs.map(_.fold(identity, identity)) :+ body case Splice(fs) => fs.flatMap{ case L(l) => l :: Nil case R(r) => r.in.toList ++ (r.out :: Nil) } case Constrained(b, bs, ws, tscs) => b :: bs.flatMap(c => c._1 :: c._2 :: Nil) ::: ws.flatMap(c => c.lb :: c.ub :: Nil) ::: tscs.flatMap(tsc => tsc._1.map(_._2) ::: tsc._2.flatten) case Signature(xs, res) => xs ::: res.toList case NuFunDef(isLetRec, nme, snme, targs, rhs) => targs ::: rhs.toOption.toList case NuTypeDef(kind, nme, tparams, params, ctor, sig, parents, sup, ths, body) => // TODO improve this mess tparams.map(_._2) ::: params.getOrElse(Tup(Nil)).fields.collect { case (_, Fld(_, Asc(_, ty))) => ty } ::: sig.toList ::: sup.toList ::: ths.toList ::: Signature(body.entities.collect { case d: NuDecl => d }, N) :: Nil // TODO parents? } lazy val typeVarsList: List[TypeVar] = this match { case uv: TypeVar => uv :: Nil case Recursive(n, b) => n :: b.typeVarsList case _ => childrenTypes.flatMap(_.typeVarsList) } /** * @return * set of free type variables in type */ lazy val freeTypeVariables: Set[TypeVar] = this match { case Recursive(uv, body) => body.freeTypeVariables - uv case t: TypeVar => Set.single(t) case _ => childrenTypes.foldRight(Set.empty[TypeVar])((ty, acc) => ty.freeTypeVariables ++ acc) } } trait TypeImpl extends Located { self: Type => def children: List[Located] = childrenTypes /** * Collect fields recursively during code generation. * Note that the type checker will reject illegal cases. */ lazy val collectFields: Ls[Str] = this match { case Record(fields) => fields.map(_._1.name) case Inter(ty1, ty2) => ty1.collectFields ++ ty2.collectFields case _: Union | _: Function | _: Tuple | _: Recursive | _: Neg | _: Rem | _: Bounds | _: WithExtension | Top | Bot | _: Literal | _: TypeVar | _: AppliedType | _: TypeName | _: Constrained | _ : Splice | _: TypeTag | _: PolyType | _: Selection => Nil } /** * Collect `TypeName`s recursively during code generation. * Note that the type checker will reject illegal cases. */ lazy val collectTypeNames: Ls[Str] = this match { case TypeName(name) => name :: Nil case AppliedType(TypeName(name), _) => name :: Nil case Inter(lhs, rhs) => lhs.collectTypeNames ++ rhs.collectTypeNames case _: Union | _: Function | _: Record | _: Tuple | _: Recursive | _: Neg | _: Rem | _: Bounds | _: WithExtension | Top | Bot | _: PolyType | _: Literal | _: TypeVar | _: Constrained | _ : Splice | _: TypeTag | _: Selection => Nil } // Collect fields and types of record types that are intersected // by traversing the first level of intersection. This is used // for finding the fields and types of a class body, since the // body is made of up an intersection of classes and records lazy val collectBodyFieldsAndTypes: List[Var -> Type] = this match { case Record(fields) => fields.map(field => (field._1, field._2.out)) case Inter(ty1, ty2) => ty1.collectBodyFieldsAndTypes ++ ty2.collectBodyFieldsAndTypes case _: Union | _: Function | _: Tuple | _: Recursive | _: Neg | _: Rem | _: Bounds | _: WithExtension | Top | Bot | _: PolyType | _: Literal | _: TypeVar | _: AppliedType | _: TypeName | _: Constrained | _ : Splice | _: TypeTag | _: Selection => Nil } } final case class ShowCtx( vs: SortedMap[TypeVar, Str], debug: Bool, // TODO make use of `debug` or rm indentLevel: Int, newDefs: Bool, angletards: Bool = false, ) { lazy val indStr: Str = ShowCtx.indentation * indentLevel def lnIndStr: Str = "\n" + indStr def indent: ShowCtx = copy(indentLevel = indentLevel + 1) def < : Str = if (angletards) "<" else "[" def > : Str = if (angletards) ">" else "]" } object ShowCtx { def indentation: Str = " " /** * Create a context from a list of types. For named variables and * hinted variables use what is given. For unnamed variables generate * completely new names. If same name exists increment counter suffix * in the name. */ def mk(tys: IterableOnce[TypeLike], newDefs: Bool, _pre: Str = "'", debug: Bool = false): ShowCtx = { val (otherVars, namedVars) = tys.iterator.toList.flatMap(_.typeVarsList).distinct.partitionMap { tv => tv.identifier match { case L(_) => L(tv.nameHint -> tv); case R(nh) => R(nh -> tv) } } val (hintedVars, unnamedVars) = otherVars.partitionMap { case (S(nh), tv) => L(nh -> tv) case (N, tv) => R(tv) } val usedNames = MutMap.empty[Str, Int] def assignName(n: Str): Str = { val pre = if (n.startsWith("'") || n.startsWith(ExtrusionPrefix)) "" else _pre usedNames.get(n) match { case S(cnt) => usedNames(n) = cnt + 1 pre + n + cnt case N => usedNames(n) = 0 pre + n } } val namedMap = (namedVars ++ hintedVars).map { case (nh, tv) => // tv -> assignName(nh.dropWhile(_ === '\'')) tv -> assignName(nh.stripPrefix(_pre)) }.toSortedMap val used = usedNames.keySet // * Generate names for unnamed variables val numLetters = 'z' - 'a' + 1 val names = Iterator.unfold(0) { idx => val postfix = idx/numLetters S(('a' + idx % numLetters).toChar.toString + (if (postfix === 0) "" else postfix.toString), idx + 1) }.filterNot(used).map(assignName) ShowCtx(namedMap ++ unnamedVars.zip(names), debug, indentLevel = 0, newDefs) } } trait PolyTypeImpl { self: PolyType => } trait ComposedImpl { self: Composed => val lhs: Type val rhs: Type def components: Ls[Type] = (lhs match { case c: Composed if c.pol === pol => c.components case _ => lhs :: Nil }) ::: (rhs match { case c: Composed if c.pol === pol => c.components case _ => rhs :: Nil }) lazy val distinctComponents = components.filterNot(c => if (pol) c === Bot else c === Top).distinct } trait TypeVarImpl extends Ordered[TypeVar] { self: TypeVar => def name: Opt[Str] = identifier.toOption.orElse(nameHint) def compare(that: TypeVar): Int = { (this.identifier.fold((_, ""), (0, _))) compare (that.identifier.fold((_, ""), (0, _))) } } // Auxiliary definitions for terms trait PgrmImpl { self: Pgrm => lazy val desugared: (Ls[Diagnostic] -> (Ls[TypeDef], Ls[Terms])) = { val diags = Buffer.empty[Diagnostic] val res = tops.flatMap { s => val (ds, d) = s.desugared diags ++= ds d }.partitionMap { case td: TypeDef => L(td) case ot: Terms => R(ot) case NuFunDef(isLetRec, nme, _, tys, rhs) => R(Def(isLetRec.getOrElse(true), nme, rhs, isLetRec.isEmpty)) case _: Constructor => die } diags.toList -> res } def showDbg: Str = tops.iterator.map("" + _.showDbg + ";").mkString(" ") } object OpApp { def apply(op: Str, trm: Term): Term = App(Var(op), trm) def unapply(trm: Term): Opt[Term -> Term] = trm |>? { case App(op, lhs) if op.toLoc.exists(l => lhs.toLoc.exists(l.spanStart > _.spanStart)) => (op, lhs) } } trait DeclImpl extends Located { self: Decl => val body: Located def showBody: Str = this match { case d: Def => d.rhs.fold(_.showDbg, _.showDbg2) case td: TypeDef => td.body.showDbg2 } def describe: Str = this match { case _: Def => "definition" case _: TypeDef => "type declaration" } def showHead: Str = this match { case Def(true, n, b, isByname) => s"rec def ${n.showDbg}" case Def(false, n, b, isByname) => s"def ${n.showDbg}" case TypeDef(k, n, tps, b, _, _, pos, _) => s"${k.str} ${n.name}${if (tps.isEmpty) "" else tps.map(_.name).mkString("[", ", ", "]")}${ if (pos.isEmpty) "" else pos.mkString("(", ", ", ")") }" } } trait NuDeclImpl extends Located { self: NuDecl => val body: Located def kind: DeclKind val declareLoc: Opt[Loc] val abstractLoc: Opt[Loc] def isDecl: Bool = declareLoc.nonEmpty def isAbstract: Bool = abstractLoc.nonEmpty def declStr: Str = if (isDecl) "declare " else "" val nameVar: Var = self match { case td: NuTypeDef => td.nme.toVar case fd: NuFunDef => fd.nme } def name: Str = nameVar.name def showBody: Str = this match { case fd: NuFunDef => fd.rhs.fold(_.print(false), _.showDbg2) case td: NuTypeDef => td.body.showDbg } def describe: Str = this match { case _: NuFunDef => "definition" case _: NuTypeDef => "type declaration" } def showHead: Str = this match { case NuFunDef(N, n, snme, _, b) => s"fun${snme.fold("")(" ("+_.name+")")} ${n.name}" case NuFunDef(S(false), n, snme, _, b) => s"let${snme.fold("")(" "+_.name+")")} ${n.name}" case NuFunDef(S(true), n, snme, _, b) => s"let rec${snme.fold("")(" "+_.name+")")} ${n.name}" case NuTypeDef(k, n, tps, sps, ctor, sig, parents, sup, ths, bod) => s"${k.str} ${n.name}${if (tps.isEmpty) "" else tps.map(_._2.name).mkString("‹", ", ", "›")}${ sps.fold("")("(" + _.showElems + ")") }${sig.fold("")(": " + _.showDbg2)}${ if (parents.isEmpty) "" else if (k === Als) " = " else ": "}${parents.iterator.map(_.showDbg).mkString(", ")}" } lazy val genUnapply: Opt[NuFunDef] = this match { case td: NuTypeDef if td.kind is Cls => td.params.map { tup => val ret = Let(false, Var("_"), Asc(Var("x"), TypeName(name)), Tup(tup.fields.map { case S(p) -> f => N -> Fld(FldFlags.empty, Sel(Var("x"), Var("#" + p.name).withLocOf(p))) case N -> Fld(flags, p: Var) => N -> Fld(FldFlags.empty, Sel(Var("x"), Var("#" + p.name).withLocOf(p))) case _ => die })) NuFunDef(N, Var("unapply"), N, Nil, L(Lam( Tup(N -> Fld(FldFlags.empty, Var("x")) :: Nil), ret)))(N, N, N, N, N, true, Nil) } case _ => N } } trait TypingUnitImpl extends Located { self: TypingUnit => def showDbg: Str = entities.iterator.map { case t: Term => t.print(false) case d: NuDecl => d.showDbg case c: Constructor => c.showDbg case e => lastWords(s"Unexpected typing unit entity: $e") }.mkString("{", "; ", "}") def showIn(implicit ctx: ShowCtx): Str = { val newCtx = ctx.indent entities.map{ case t: Term => t.showIn(false)(newCtx) case nd: NuDecl => nd.show(newCtx.newDefs) case s if !ctx.newDefs => s.toString case _ => ??? }.mkString("{" + newCtx.lnIndStr, newCtx.lnIndStr, ctx.lnIndStr + "}") } lazy val children: List[Located] = rawEntities lazy val entities = { val declaredValueMembers = rawEntities.collect{ case fd: NuFunDef if fd.rhs.isRight => fd.nme.name }.toSet rawEntities.map { case Eqn(lhs, rhs) if declaredValueMembers(lhs.name) => NuFunDef(N, lhs, N, Nil, L(rhs))(N, N, N, N, N, true, Nil) case e => e } } def describe: Str = entities.iterator.map { case term: Term => term.describe case NuFunDef(S(rec), nme, _, _, _) => s"let ${if (rec) "rec " else ""}$nme" case NuFunDef(N, nme, _, _, _) => s"fun $nme" case typ: NuTypeDef => typ.describe case other => "?" }.mkString("{", "; ", "}") } trait ConstructorImpl { self: Constructor => // def children: List[Located] = fields.map(_._2) def describe: Str = "constructor" // def showDbg: Str = s"constructor(${fields.map(_._1.name).mkString(", ")})" } trait TypeNameImpl extends Ordered[TypeName] { self: TypeName => def base: TypeName = this def targs: Ls[Type] = Nil def compare(that: TypeName): Int = this.name compare that.name lazy val toVar: Var = Var(name).withLocOf(this) var symbol: Opt[pretyper.symbol.TypeSymbol] = N } trait FldFlagsImpl extends Located { self: FldFlags => def children: Ls[Located] = Nil override def toString(): String = { val FldFlags(m, s, g) = this val res = (if (m) "m" else "") + (if (s) "s" else "") + (if (g) "g" else "") if (res.isEmpty) "_" else res } } trait FldImpl extends Located { self: Fld => def children: Ls[Located] = self.value :: Nil def describe: Str = (if (self.flags.spec) "specialized " else "") + (if (self.flags.mut) "mutable " else "") + self.value.describe } trait TermImpl extends StatementImpl { self: Term => val original: this.type = this /** Used by code generation when the typer desugars this term into a different term. */ var desugaredTerm: Opt[Term] = N private var sugaredTerm: Opt[Term] = N def desugaredFrom(term: Term): this.type = { sugaredTerm = S(term) withLocOf(term) } def describe: Str = sugaredTerm match { case S(t) => t.describe case N => this match { case Ann(_, Ann(_, receiver)) => receiver.describe case Ann(_, receiver) => "annotated " + receiver.describe case Bra(true, Tup(_ :: _ :: _) | Tup((S(_), _) :: _) | Blk(_)) => "record" case Bra(_, trm) => trm.describe case Blk((trm: Term) :: Nil) => trm.describe case Blk(_) => "block of statements" case IntLit(value) => "integer literal" case DecLit(value) => "decimal literal" case StrLit(value) => "string literal" case UnitLit(value) => if (value) "undefined literal" else "null literal" case Var(name) => "reference" // "variable reference" case Asc(trm, ty) => "type ascription" case Lam(name, rhs) => "lambda expression" case App(OpApp(Var("|"), lhs), rhs) => "type union" case App(OpApp(Var("&"), lhs), rhs) => "type intersection" case App(OpApp(op, lhs), rhs) => "operator application" case OpApp(op, lhs) => "operator application" case App(lhs, rhs) => "application" case Rcd(fields) => "record" case Sel(receiver, fieldName) => "field selection" case Let(isRec, name, rhs, body) => "let binding" case Tup((N, Fld(_, x)) :: Nil) => x.describe case Tup((S(_), x) :: Nil) => "binding" case Tup(xs) => "tuple" case Bind(l, r) => "'as' binding" case Test(l, r) => "'is' test" case With(t, fs) => "`with` extension" case CaseOf(scrut, cases) => "`case` expression" case Subs(arr, idx) => "array access" case Assign(lhs, rhs) => "assignment" case While(cnd, bod) => "while loop" case Splc(fs) => "splice" case New(h, b) => "object instantiation" case NuNew(_) => "new instance" case Rft(_, _) => "refinement" case If(_, _) => "if-else block" case TyApp(_, _) => "type application" case Where(_, _) => s"constraint clause" case Forall(_, _) => s"forall clause" case Inst(bod) => "explicit instantiation" case Super() => "super" case Eqn(lhs, rhs) => "assign for ctor" case AdtMatchWith(cond, arms) => "ADT pattern matching" case Quoted(_) => "quasiquote" case Unquoted(_) => "unquote" } } override def showDbg: Str = print(false) def print(brackets: Bool): Str = { def bra(str: Str): Str = if (brackets) s"($str)" else str this match { case Ann(ann, receiver) => bra("@" + ann.print(false) + " ") + receiver.print(false) case Bra(true, trm) => s"'{' ${trm.showDbg} '}'" case Bra(false, trm) => s"'(' ${trm.showDbg} ')'" case Blk(stmts) => stmts.iterator.map(_.showDbg).mkString("{", "; ", "}") case IntLit(value) => value.toString case DecLit(value) => value.toString case StrLit(value) => value.iterator.map { case '\\' => "\\\\"; case '"' => "\\\""; case '\'' => "\\'" case '\n' => "\\n"; case '\r' => "\\r"; case '\t' => "\\t" case '\f' => "\\f"; case '\b' => "\\b"; case c => c.toString() }.mkString("\"", "", "\"") case UnitLit(value) => if (value) "undefined" else "null" case v @ Var(name) => name + v.uid.fold("")("::"+_.toString) case Asc(trm, ty) => s"${trm.showDbg} : ${ty.showDbg2}" |> bra case Lam(pat: Tup, rhs) => s"(${pat.showElems}) => ${rhs.showDbg}" |> bra case Lam(pat, rhs) => s"(...${pat.showDbg}) => ${rhs.showDbg}" |> bra case App(lhs, rhs: Tup) => s"${lhs.print(!lhs.isInstanceOf[App])}(${rhs.showElems})" |> bra case App(lhs, rhs) => s"${lhs.print(!lhs.isInstanceOf[App])}(...${rhs.print(true)})" |> bra case Rcd(fields) => fields.iterator.map(nv => (if (nv._2.flags.mut) "mut " else "") + nv._1.name + ": " + nv._2.value.showDbg).mkString("{", ", ", "}") case Sel(receiver, fieldName) => "(" + receiver.showDbg + ")." + fieldName.showDbg case Let(isRec, Var(name), rhs, body) => s"let${if (isRec) " rec" else ""} $name = ${rhs.showDbg} in ${body.showDbg}" |> bra case tup: Tup => "[" + tup.showElems + "]" case Splc(fields) => fields.map{ case L(l) => s"...$l" case R(Fld(FldFlags(m, s, g), r)) => ( (if (m) "mut " else "") + (if (g) "val " else "") + (if (s) "#" else "") + r ) }.mkString("(", ", ", ")") case Bind(l, r) => s"${l.showDbg} as ${r.showDbg}" |> bra case Test(l, r) => s"${l.showDbg} is ${r.showDbg}" |> bra case With(t, fs) => s"${t.showDbg} with ${fs.showDbg}" |> bra case CaseOf(s, c) => s"case ${s.showDbg} of { ${c.print(true)} }" |> bra case Subs(a, i) => s"(${a.showDbg})[${i.showDbg}]" case Assign(lhs, rhs) => s"${lhs.showDbg} <- ${rhs.showDbg}" |> bra case While(cnd, bod) => s"while ${cnd.showDbg} do ${bod.showDbg}" |> bra case New(S((at, ar)), bod) => s"new ${at.showDbg2}(${ar.showDbg}) ${bod.showDbg}" |> bra case New(N, bod) => s"new ${bod.showDbg}" |> bra case NuNew(cls) => s"new ${cls.showDbg}" |> bra case If(body, els) => s"if ${body.showDbg}" + els.fold("")(" else " + _.showDbg) |> bra case TyApp(lhs, targs) => s"${lhs.showDbg}‹${targs.iterator.map(_.showDbg2).mkString(", ")}›" case Where(bod, wh) => s"${bod.showDbg} where {${wh.iterator.map(_.showDbg).mkString("; ")}}" case Forall(ps, bod) => s"forall ${ps.mkString(", ")}. ${bod.showDbg}" case Inst(bod) => s"${bod.print(true)}!" case Super() => "super" case Eqn(lhs, rhs) => s"${lhs.showDbg} = ${rhs.showDbg}" case AdtMatchWith(cond, arms) => s"match ${cond.showDbg} with ${arms.map (patmat => s"${patmat.pat.showDbg} -> ${patmat.rhs.showDbg}").mkString (" | ") }" case Quoted(b) => s"code\"${b.showDbg}\"" case Unquoted(b) => s"$${${b.showDbg}}" case Rft(bse, tu) => s"${bse.showDbg} ${tu.showDbg}" }} def show(newDefs: Bool): Str = showIn(false)(ShowCtx.mk(Nil, newDefs)) def showIn(brackets: Bool)(implicit ctx: ShowCtx): Str = { def bra(str: Str): Str = if (brackets) s"($str)" else str this match { case Ann(ann, receiver) => bra(s"@${ann.toString()} ${receiver.showIn(false)}") case Bra(true, trm) => trm.showIn(false) case Bra(false, trm) => s"(${trm.showIn(false)})" case Blk(stmts) => val newCtx = ctx.indent stmts.map { case t: Term => t.showIn(false)(newCtx) case nd: NuDecl => nd.show(newCtx.newDefs) case s if !ctx.newDefs => s.toString case _ => ??? }.mkString("{" + newCtx.lnIndStr, newCtx.lnIndStr, ctx.indStr + "\n}") case IntLit(value) => value.toString case DecLit(value) => value.toString case StrLit(value) => '"'.toString + value + '"' case UnitLit(value) => if (ctx.newDefs) "()" else if (value) "undefined" else "null" case Var(name) => name case Asc(trm, ty) => s"$trm : ${ty.show(ctx.newDefs)}" |> bra case Lam(pat: Tup, rhs) => s"(${pat.showIn}) => ${rhs.showIn(false)}" |> bra case Lam(pat, rhs) => s"(...${pat.showIn(false)}) => ${rhs.showIn(false)}" |> bra case App(lhs, rhs: Tup) => s"${lhs.showIn(!lhs.isInstanceOf[App])}(${rhs.showIn})" |> bra case App(lhs, rhs) => s"${lhs.showIn(!lhs.isInstanceOf[App])}(...${rhs.showIn(true)})" |> bra case Rcd(fields) => val newCtx = ctx.indent fields.iterator.map(nv => (if (nv._2.flags.mut) "mut " else "") + nv._1.name + ": " + nv._2.value.showIn(false)(newCtx)).mkString("{" + newCtx.lnIndStr, "," + newCtx.lnIndStr, ctx.lnIndStr + "}") case Sel(receiver, fieldName) => receiver.showIn(!receiver.isInstanceOf[SimpleTerm]) + "." + fieldName case Let(isRec, name, rhs, body) => s"let${if (isRec) " rec" else ""} ${name.showIn(false)} = ${rhs.showIn(false)} in ${body.showIn(false)}" |> bra case tup: Tup => "[" + tup.showIn + "]" case Splc(fields) => fields.map{ case L(l) => s"...${l.showIn(false)}" case R(Fld(FldFlags(m, s, g), r)) => ( (if (m) "mut " else "") + (if (g) "val " else "") + (if (s) "#" else "") + r.showIn(false) ) }.mkString("(", ", ", ")") case Bind(l, r) => s"${l.showIn(false)} as ${r.showIn(false)}" |> bra case Test(l, r) => s"${l.showIn(false)} is ${r.showIn(false)}" |> bra case With(t, fs) => s"${t.showIn(false)} with ${fs.showIn(false)}" |> bra case CaseOf(s, c) => s"case ${s.showIn(false)} of {${c.showIn(ctx.indent)}${ctx.lnIndStr}}" |> bra case Subs(a, i) => s"(${a.showIn(false)})[${i.showIn(false)}]" case Assign(lhs, rhs) => s"${lhs.showIn(false)} <- ${rhs.showIn(false)}" |> bra case While(cnd, bod) => s"while ${cnd.showIn(false)} do ${bod.showIn(false)}" |> bra case New(S((at, ar)), bod) => s"new ${at.show(ctx.newDefs)}($ar) ${bod.showIn}" |> bra case New(N, bod) => s"new ${bod.showIn}" |> bra case NuNew(cls) => s"new ${cls.showIn(false)}" |> bra case If(body, els) => s"if ${body.showIn(ctx.indent)}" + els.fold("")(" else " + _.showIn(false)(ctx.indent)) |> bra case TyApp(lhs, targs) => s"${lhs.showIn(false)}${ctx.<}${targs.map(_.show(ctx.newDefs)).mkString(", ")}${ctx.>}" case Where(bod, wh) => s"${bod.showIn(false)} where ${Blk(wh).showIn(false)(ctx.indent)}" case Forall(ps, bod) => s"forall ${ps.map(_.show(ctx.newDefs)).mkString(", ")}. ${bod.showIn(false)}" case Inst(bod) => s"${bod.showIn(true)}!" case Super() => "super" case Eqn(lhs, rhs) => s"${lhs.showIn(false)} = ${rhs.showIn(false)}" case AdtMatchWith(cond, arms) => s"match ${cond.showIn(false)} with ${arms.map (patmat => s"${patmat.pat.showIn(false)} -> ${patmat.rhs.showIn(false)}").mkString (" | ") }" case Rft(bse, tu) => s"${bse.showIn(false)} { ${tu.showIn} }" case Quoted(b) => s"code\"${b.showIn(false)}\"" case Unquoted(b) => s"$${${b.showIn(false)}}" } } def toTypeRaise(implicit raise: Raise): Type = toType match { case L(d) => raise(d); Bot case R(ty) => ty } def toType: Diagnostic \/ Type = try R(toType_!.withLocOf(this)) catch { case e: NotAType => import Message._ L(ErrorReport(msg"Not a recognized type" -> e.trm.toLoc::Nil, newDefs=true, source=Diagnostic.Parsing)) } protected def toType_! : Type = (this match { case Var(name) if name.startsWith("`") => TypeVar(R(name.tail), N) case Var(name) if name.startsWith("'") => TypeVar(R(name), N) case Var(name) => TypeName(name) case lit: Lit => Literal(lit) case App(Var("->"), PlainTup(lhs @ (_: Tup | Bra(false, _: Tup)), rhs)) => // * ^ Note: don't think the plain _: Tup without a Bra can actually occur Function(lhs.toType_!, rhs.toType_!) case App(Var("->"), PlainTup(lhs, rhs)) => Function(Tuple(N -> Field(N, lhs.toType_!) :: Nil), rhs.toType_!) case App(Var("|"), PlainTup(lhs, rhs)) => Union(lhs.toType_!, rhs.toType_!) case App(Var("&"), PlainTup(lhs, rhs)) => Inter(lhs.toType_!, rhs.toType_!) case ty @ App(v @ Var("\\"), PlainTup(lhs, rhs)) => Inter(lhs.toType_!, Neg(rhs.toType_!).withLoc(Loc(v :: rhs :: Nil))).withLoc(ty.toCoveringLoc) case App(Var("~"), rhs) => Neg(rhs.toType_!) case Lam(lhs, rhs) => Function(lhs.toType_!, rhs.toType_!) case App(lhs, PlainTup(fs @ _*)) => lhs.toType_! match { case tn: TypeName => AppliedType(tn, fs.iterator.map(_.toType_!).toList) case _ => throw new NotAType(this) } case Tup(fields) => Tuple(fields.map(fld => (fld._1, fld._2 match { case Fld(FldFlags(m, s, _), v) => val ty = v.toType_!; Field(Option.when(m)(ty), ty) }))) case Bra(rcd, trm) => trm match { case _: Rcd => if (rcd) trm.toType_! else throw new NotAType(this) case _ => if (!rcd) trm.toType_! else throw new NotAType(this) } case TyApp(lhs, targs) => lhs.toType_! match { case p: TypeName => AppliedType(p, targs) case _ => throw new NotAType(this) } case Rcd(fields) => Record(fields.map(fld => (fld._1, fld._2 match { case Fld(FldFlags(m, s, _), v) => val ty = v.toType_!; Field(Option.when(m)(ty), ty) }))) case Where(body, where) => Constrained(body.toType_!, Nil, where.map { case Asc(l, r) => Bounds(l.toType_!, r) case s => throw new NotAType(s) }, Nil) case Forall(ps, bod) => PolyType(ps.map(R(_)), bod.toType_!) // case Sel(receiver, field) => Selection(receiver.toType_!, TypeName(field.name).withLocOf(field)) // case Sel(receiver, fieldName) => receiver match { // case Var(name) if !name.startsWith("`") => TypeName(s"$name.$fieldName") // case _ => throw new NotAType(this) // } // TODO: // case Let(isRec, name, rhs, body) => ??? // case Blk(stmts) => ??? // case Asc(trm, ty) => ??? // case Bind(lhs, rhs) => ??? // case Test(trm, ty) => ??? // case With(trm, fieldNme, fieldVal) => ??? // case CaseOf(trm, cases) => ??? case _ => throw new NotAType(this) }).withLocOf(this) /** Whether this is a lambda that, when let-rec-bound, should be generalized. */ def isGeneralizableLam: Bool = this match { case Lam(_, _) => true case Bra(false, that) => that.isGeneralizableLam case Where(that, _) => that.isGeneralizableLam case CaseOf(_, cs) => cs.bodies.forall(_.isGeneralizableLam) case _ => false } } private class NotAType(val trm: Statement) extends Throwable object PlainTup { def apply(fields: Term*): Term = Tup(fields.iterator.map(t => (N, Fld(FldFlags.empty, t))).toList) def unapplySeq(trm: Term): Opt[List[Term]] = trm match { case Tup(fields) if fields.forall(f => f._1.isEmpty && f._2.flags.mut === false && f._2.flags.spec === false ) => S(fields.map(_._2.value)) case _ => N } } trait LitImpl { self: Lit => def baseClassesOld: Set[TypeName] = this match { case _: IntLit => Set.single(TypeName("int")) + TypeName("number") case _: StrLit => Set.single(TypeName("string")) case _: DecLit => Set.single(TypeName("number")) case _: UnitLit => Set.empty } def baseClassesNu: Set[TypeName] = this match { case _: IntLit => Set.single(TypeName("Int")) + TypeName("Num") + TypeName("Object") case _: StrLit => Set.single(TypeName("Str")) + TypeName("Object") case _: DecLit => Set.single(TypeName("Num")) + TypeName("Object") case _: UnitLit => Set.single(TypeName("Object")) } } trait VarImpl extends pretyper.symbol.Symbolic { self: Var => /** Check if the variable name is an integer. */ def isIndex: Bool = name.headOption match { case S('0') => name.length === 1 case S(_) => name.forall(_.isDigit) case N => false } /** Get the integer if it's a valid index. */ def toIndexOption: Opt[Int] = if (isIndex) name.toIntOption else N def isPatVar: Bool = (name.head.isLetter && name.head.isLower || name.head === '_' || name.head === '$') && name =/= "true" && name =/= "false" def toVar: Var = this var uid: Opt[Int] = N } trait TupImpl { self: Tup => def showElems: Str = fields.iterator.map { case (n, t) => ( (if (t.flags.mut) "mut " else "") + (if (t.flags.genGetter) "val " else "") + (if (t.flags.spec) "#" else "") + n.fold("")(_.name + ": ") + t.value.print(false) + "," )}.mkString(" ") def showIn(implicit ctx: ShowCtx): Str = fields.iterator.map { case (n, t) => ( (if (t.flags.mut) "mut " else "") + (if (t.flags.genGetter) "val " else "") + (if (t.flags.spec) "#" else "") + n.fold("")(_.name + ": ") + t.value.showIn(false) )}.mkString(", ") } trait SimpleTermImpl extends Ordered[SimpleTerm] { self: SimpleTerm => def compare(that: SimpleTerm): Int = this.idStr compare that.idStr val idStr: Str = this match { case Var(name) => name case lit: Lit => lit.showDbg } } trait FieldImpl extends Located { self: Field => def children: List[Located] = self.in.toList ::: self.out :: Nil def isMutabe: Bool = in.isDefined def mutStr: Str = if (isMutabe) "mut " else "" } trait Located { def children: List[Located] lazy val freeVars: Set[Var] = { def statements(stmts: Ls[Statement]): Set[Var] = stmts.iterator.foldRight(Set.empty[Var]) { case (NuFunDef(isLetRec, nme, _, _, L(rhs)), fvs) => fvs - nme ++ (isLetRec match { case N | S(true) => rhs.freeVars - nme case S(false) => rhs.freeVars }) case (td: NuTypeDef, fvs) => if (td.kind === Mod || td.kind === Cls || td.kind === Trt) fvs - td.nameVar else fvs case (statement, fvs) => fvs ++ statement.freeVars } this match { // TypingUnit case TypingUnit(entities) => statements(entities) // Terms case v: Var => Set.single(v) case Lam(tup: Tup, body) => body.freeVars -- tup.freeVars case App(lhs, rhs) => lhs.freeVars ++ rhs.freeVars case Tup(fields) => fields.iterator.flatMap(_._2.value.freeVars.iterator).toSet case Rcd(fields) => fields.iterator.flatMap(_._2.value.freeVars.iterator).toSet case Sel(receiver, _) => receiver.freeVars case Let(true, nme, rhs, body) => body.freeVars ++ rhs.freeVars - nme case Let(false, nme, rhs, body) => body.freeVars - nme ++ rhs.freeVars case Blk(stmts) => statements(stmts) case Bra(_, trm) => trm.freeVars case Asc(trm, _) => trm.freeVars case Bind(lhs, rhs) => lhs.freeVars ++ rhs.freeVars case Test(trm, _) => trm.freeVars case With(trm, fields) => trm.freeVars ++ fields.freeVars case CaseOf(trm, cases) => cases.foldLeft(trm.freeVars)(_ ++ _._2.freeVars)(_ ++ _.fold(Set.empty[Var])(_.freeVars)) case Subs(arr, idx) => arr.freeVars ++ idx.freeVars case Assign(lhs, rhs) => lhs.freeVars ++ rhs.freeVars case Splc(fields) => fields.iterator.flatMap(_.fold(_.freeVars, _.value.freeVars)).toSet case New(head, body) => head.fold(Set.empty[Var])(_._2.freeVars) ++ body.freeVars case NuNew(cls) => cls.freeVars // Because `IfBody` uses the term to represent the pattern, direct // traversal is not correct. case If(_, _) => Set.empty case TyApp(lhs, _) => lhs.freeVars case Where(body, where) => body.freeVars ++ statements(where) case Forall(_, body) => body.freeVars case Inst(body) => body.freeVars case Super() => Set.empty case Eqn(lhs, rhs) => lhs.freeVars ++ rhs.freeVars case Rft(base, decls) => base.freeVars ++ decls.freeVars // Fallback for unsupported terms which is incorrect most of the time. case _ => children.iterator.flatMap(_.freeVars.iterator).toSet } } private var spanStart: Int = -1 private var spanEnd: Int = -1 private var origin: Opt[Origin] = N // TODO just store the Loc directly... def withLoc(s: Int, e: Int, ori: Origin): this.type = { // assert(origin.isEmpty) origin = S(ori) // assert(spanStart < 0) // assert(spanEnd < 0) spanStart = s spanEnd = e this } def withLoc(loco: Opt[Loc]): this.type = { loco.foreach { that => spanStart = that.spanStart spanEnd = that.spanEnd origin = S(that.origin) } this } def withLocOf(that: Located): this.type = withLoc(that.toLoc) def hasLoc: Bool = origin.isDefined lazy val toLoc: Opt[Loc] = getLoc private[mlscript] def getLoc: Opt[Loc] = { def subLocs = children.iterator.flatMap(_.toLoc.iterator) if (spanStart < 0) spanStart = subLocs.map(_.spanStart).minOption.getOrElse(return N) if (spanEnd < 0) spanEnd = subLocs.map(_.spanEnd).maxOption.getOrElse(return N) val origins = origin.fold(subLocs.map(_.origin).toList.distinct)(_ :: Nil) assert(origins.size === 1, origins) S(Loc(spanStart, spanEnd, origins.head)) } /** Like toLoc, but we make sure the span includes the spans of all subterms. */ def toCoveringLoc: Opt[Loc] = { // TODO factor logic with above def subLocs = (this :: children).iterator.flatMap(_.toLoc.iterator) val spanStart = subLocs.map(_.spanStart).minOption.getOrElse(return N) val spanEnd = subLocs.map(_.spanEnd).maxOption.getOrElse(return N) val origins = origin.fold(subLocs.map(_.origin).toList.distinct)(_ :: Nil) assert(origins.size === 1) S(Loc(spanStart, spanEnd, origins.head)) } } trait DesugaredStatementImpl extends Located { self: DesugaredStatement => def describe: Str def showDbg: Str } trait StatementImpl extends Located { self: Statement => lazy val desugared = doDesugar private def doDesugar: Ls[Diagnostic] -> Ls[DesugaredStatement] = this match { // case ctor: Constructor => // import Message._ // (ErrorReport(msg"constructor must be in a class." -> ctor.toLoc :: Nil, newDefs=true) :: Nil) -> Nil case l @ LetS(isrec, pat, rhs) => val (diags, v, args) = desugDefnPattern(pat, Nil) diags -> (Def(isrec, v, L(args.foldRight(rhs)(Lam(_, _))), false).withLocOf(l) :: Nil) // TODO use v, not v.name case d @ DataDefn(body) => desugarCases(body :: Nil, Nil) case d @ DatatypeDefn(hd, bod) => val (diags, v, args) = desugDefnPattern(hd, Nil) val (diags3, targs) = args.partitionMap { case v @ Var(nme) => R(TypeName(nme).withLocOf(v)) case t => import Message._ L(ErrorReport(msg"illegal datatype type parameter shape: ${t.showDbg}" -> t.toLoc :: Nil, newDefs=false)) } val (diags2, cs) = desugarCases(bod, targs) val dataDefs = cs.collect{case td: TypeDef => td} (diags ::: diags2 ::: diags3) -> (TypeDef(Als, TypeName(v.name).withLocOf(v), targs, dataDefs.map(td => AppliedType(td.nme, td.tparams)).reduceOption(Union).getOrElse(Bot), Nil, Nil, Nil, N ).withLocOf(hd) :: cs) case NuTypeDef(Mod, nme, tps, tup, ctor, sig, pars, sup, ths, unit) => ??? // TODO case NuTypeDef(Mxn, nme, tps, tup, ctor, sig, pars, sup, ths, unit) => ??? // TODO case NuTypeDef(k @ Als, nme, tps, tup, ctor, sig, pars, sup, ths, unit) => // TODO properly check: require(tup.forall(tup => tup.fields.isEmpty), tup) require(pars.size === 0, pars) require(sig.isDefined) require(ths.isEmpty, ths) require(unit.entities.isEmpty, unit) Nil -> (TypeDef(k, nme, tps.map(_._2), sig.getOrElse(die), Nil, Nil, Nil, N) :: Nil) case NuTypeDef(k @ (Cls | Trt), nme, tps, opt, ctor, sig, pars, sup, ths, unit) => val tup = opt.getOrElse(Tup(Nil)) val fs = tup.fields val diags = Buffer.empty[Diagnostic] def tt(trm: Term): Type = trm.toType match { case L(ds) => diags += ds; Top case R(ty) => ty } val params = fs.map { case (S(nme), Fld(FldFlags(mut, spec, _), trm)) => val ty = tt(trm) nme -> Field(if (mut) S(ty) else N, ty) case (N, Fld(FldFlags(mut, spec, _), nme: Var)) => nme -> Field(if (mut) S(Bot) else N, Top) case _ => die } val pos = params.unzip._1 val bod = pars.map(tt).foldRight(Record(params): Type)(Inter) val termName = Var(nme.name).withLocOf(nme) val ctor = Def(false, termName, L(Lam(tup, App(termName, Tup(N -> Fld(FldFlags.empty, Rcd(fs.map { case (S(nme), fld) => nme -> Fld(FldFlags(false, false, fld.flags.genGetter), nme) case (N, fld @ Fld(_, nme: Var)) => nme -> fld case _ => die })) :: Nil)))), true) diags.toList -> (TypeDef(k, nme, tps.map(_._2), bod, Nil, Nil, pos, N) :: ctor :: Nil) case d: DesugaredStatement => Nil -> (d :: Nil) } import Message._ protected def desugDefnPattern(pat: Term, args: Ls[Term]): (Ls[Diagnostic], Var, Ls[Term]) = pat match { case App(l, r) => desugDefnPattern(l, r :: args) case v: Var => (Nil, v, args) case _ => (ErrorReport(msg"Unsupported pattern shape" -> pat.toLoc :: Nil, newDefs=true) :: Nil, Var(""), args) // TODO } protected def desugarCases(bod: Term, baseTargs: Ls[TypeName]): (Ls[Diagnostic], Ls[Decl]) = bod match { case Blk(stmts) => desugarCases(stmts, baseTargs) case Tup(comps) => val stmts = comps.map { case N -> Fld(_, d) => d case S(n) -> Fld(_, d) => ??? } desugarCases(stmts, baseTargs) case _ => (ErrorReport(msg"Unsupported data type case shape" -> bod.toLoc :: Nil, newDefs=true) :: Nil, Nil) } protected def desugarCases(stmts: Ls[Statement], baseTargs: Ls[TypeName]): (Ls[Diagnostic], Ls[Decl]) = stmts match { case stmt :: stmts => val (diags0, sts) = stmt.desugared val (diags2, cs) = desugarCases(stmts, baseTargs) val allDiags = Buffer.from(diags0) val res = sts.flatMap { case t: Term => val (diags1, v, args) = desugDefnPattern(t, Nil) allDiags ++= diags1 val tparams = Buffer.from(baseTargs) val fields = SortedMutMap.empty[Var, Type] def getFields(t: Term): Ls[Type] = t match { case v: Var => // TOOD check not already defined val tp = baseTargs.find(_.name === v.name).map(_.copy()).getOrElse( if (v.name.startsWith("`")) TypeVar(R(v.name.tail), N) else TypeName(v.name) tap (tparams += _)).withLocOf(v) fields += v -> tp tp :: Nil case Blk((t: Term)::Nil) => getFields(t) case Blk(_) => ??? // TODO proper error case Bra(b, Blk((t:Term)::Nil)) => getFields(Bra(b, t)) case Bra(false, t) => getFields(t) case Bra(true, Tup(fs)) => Record(fs.map { case (S(n) -> Fld(FldFlags(mut, _, _), t)) => val ty = t.toType match { case L(d) => allDiags += d; Top case R(t) => t } fields += n -> ty n -> Field(None, ty) case _ => ??? }) :: Nil case Bra(true, t) => lastWords(s"$t ${t.getClass}") case Tup(fs) => // TODO factor with case Bra(true, Tup(fs)) above Tuple(fs.map { case (S(n) -> Fld(FldFlags(tmut, _, _), t)) => val ty = t.toType match { case L(d) => allDiags += d; Top case R(t) => t } fields += n -> ty S(n) -> Field(None, ty) case _ => ??? }) :: Nil case _ => ??? // TODO proper error } val params = args.flatMap(getFields) val clsNme = TypeName(v.name).withLocOf(v) val tps = tparams.toList val ctor = Def(false, v, R(PolyType(tps.map(L(_)), params.foldRight(AppliedType(clsNme, tps):Type)(Function(_, _)))), true).withLocOf(stmt) val td = TypeDef(Cls, clsNme, tps, Record(fields.toList.mapValues(Field(None, _))), Nil, Nil, Nil, N).withLocOf(stmt) td :: ctor :: cs case _ => ??? // TODO methods in data type defs? nested data type defs? } allDiags ++= diags2 allDiags.toList -> res case Nil => Nil -> Nil } def children: List[Located] = this match { case Ann(ann, trm) => ann :: trm :: Nil case Bra(_, trm) => trm :: Nil case Var(name) => Nil case Asc(trm, ty) => trm :: Nil case Lam(lhs, rhs) => lhs :: rhs :: Nil case App(lhs, rhs) => lhs :: rhs :: Nil case Tup(fields) => fields.map(_._2.value) case Rcd(fields) => fields.map(_._2.value) case Sel(receiver, fieldName) => receiver :: fieldName :: Nil case Let(isRec, name, rhs, body) => rhs :: body :: Nil case Blk(stmts) => stmts case LetS(_, pat, rhs) => pat :: rhs :: Nil case DatatypeDefn(head, body) => head :: body :: Nil case DataDefn(body) => body :: Nil case _: Lit => Nil case Bind(l, r) => l :: r :: Nil case Test(l, r) => l :: r :: Nil case With(t, fs) => t :: fs :: Nil case CaseOf(s, c) => s :: c :: Nil case d @ Def(_, n, b, _) => n :: d.body :: Nil case TypeDef(kind, nme, tparams, body, _, _, pos, _) => nme :: tparams ::: pos ::: body :: Nil case Subs(a, i) => a :: i :: Nil case Assign(lhs, rhs) => lhs :: rhs :: Nil case While(cnd, bod) => cnd :: bod :: Nil case Splc(fields) => fields.map{case L(l) => l case R(r) => r.value} case If(body, els) => body :: els.toList case d @ NuFunDef(_, v, v2, ts, rhs) => v :: v2.toList ::: ts ::: d.body :: Nil case TyApp(lhs, targs) => lhs :: targs case New(base, bod) => base.toList.flatMap(ab => ab._1 :: ab._2 :: Nil) ::: bod :: Nil case NuNew(cls) => cls :: Nil case Rft(bs, tu) => bs :: tu :: Nil case Where(bod, wh) => bod :: wh case Forall(ps, bod) => ps ::: bod :: Nil case Inst(bod) => bod :: Nil case Super() => Nil case Constructor(params, body) => params :: body :: Nil case Eqn(lhs, rhs) => lhs :: rhs :: Nil case NuTypeDef(k, nme, tps, ps, ctor, sig, pars, sup, ths, bod) => nme :: tps.map(_._2) ::: ps.toList ::: pars ::: ths.toList ::: bod :: Nil case AdtMatchWith(cond, _) => cond :: Nil // FIXME discards branches... case Quoted(body) => body :: Nil case Unquoted(body) => body :: Nil } def showDbg: Str = this match { case LetS(isRec, name, rhs) => s"let${if (isRec) " rec" else ""} ${name.showDbg} = ${rhs.showDbg}" case DatatypeDefn(head, body) => s"data type ${head.showDbg} of ${body.showDbg}" case DataDefn(head) => s"data ${head.showDbg}" case Constructor(params, body) => s"constructor(${params.showElems}) ${body.showDbg}" case t: Term => t.print(false) case d: Decl => d.showHead + (d match { case n: TypeDef if n.kind is Als => " = " case _ => ": " }) + d.showBody case n: NuDecl => n.showHead + (n match { case n: NuFunDef => if (n.rhs.isLeft) " = " else ": " case _: NuTypeDef => " " }) + n.showBody } } trait BlkImpl { self: Blk => def kind: Block.type = Block def flatten: Blk = Blk(stmts.flatMap { case b: Blk => b.flatten.stmts case t => t :: Nil }) } trait CaseOfImpl extends Located { self: CaseOf => def isEmpty: Bool = { val CaseOf(scrut, cases) = this cases === NoCases } } trait CaseBranchesImpl extends Located { self: CaseBranches => def children: List[Located] = this match { case Case(pat, body, rest) => pat :: body :: rest :: Nil case Wildcard(body) => body :: Nil case NoCases => Nil } def bodies: List[Term] = this match { case Case(_, body, rest) => body :: rest.bodies case Wildcard(body) => body :: Nil case NoCases => Nil } def print(isFirst: Bool): Str = this match { case c @ Case(pat, body, rest) => (if (isFirst) { "" } else { "; " }) + (if (c.refined) "refined " else "") + pat.print(false) + " => " + body.print(false) + rest.print(false) case Wildcard(body) => (if (isFirst) { "" } else { "; " }) + "_ => " + body.print(false) case NoCases => "" } def foldLeft[A, B](z: A)(f: (A, SimpleTerm -> Term) => A)(e: (A, Opt[Term]) => B): B = { @tailrec def rec(acc: A, current: CaseBranches): B = current match { case Case(pat, body, rest) => rec(f(acc, pat -> body), rest) case Wildcard(body) => e(acc, S(body)) case NoCases => e(acc, N) } rec(z, this) } def foreach(f: Opt[SimpleTerm] -> Term => Unit): Unit = { @tailrec def rec(current: CaseBranches): Unit = current match { case Case(pat, body, rest) => f(S(pat) -> body); rec(rest) case Wildcard(body) => f(N -> body) case NoCases => () } rec(this) } def showIn(implicit ctx: ShowCtx): Str = this match { case Case(pat, body, rest) => ctx.lnIndStr + pat.showIn(false) + " => " + body.showIn(false) + rest.showIn case Wildcard(body) => ctx.lnIndStr + "_ => " + body.showIn(false) case NoCases => "" } } trait AdtMatchPatImpl extends Located { self: AdtMatchPat => def children: List[Located] = pat :: rhs :: Nil override def toString: String = s"($pat) then $rhs" } trait IfBodyImpl extends Located { self: IfBody => def children: List[Located] = this match { case IfBlock(ts) => ts.map(_.fold(identity, identity)) case IfThen(l, r) => l :: r :: Nil case IfElse(t) => t :: Nil case IfLet(_, v, r, b) => v :: r :: b :: Nil case IfOpApp(t, v, b) => t :: v :: b :: Nil case IfOpsApp(t, ops) => t :: ops.flatMap(x => x._1 :: x._2 :: Nil) } def showDbg: String = this match { case IfThen(lhs, rhs) => s"(${lhs.showDbg}) then ${rhs.showDbg}" case IfElse(trm) => s"else ${trm.showDbg}" case IfBlock(ts) => s"‹${ts.iterator.map(_.fold(_.showDbg, _.showDbg)).mkString("; ")}›" case IfOpApp(lhs, op, ib) => s"${lhs.showDbg} ${op.showDbg} ${ib.showDbg}" case IfOpsApp(lhs, ops) => s"${lhs.showDbg} ‹${ops.iterator.map{case(v, r) => s"· ${v.showDbg} ${r.showDbg}"}.mkString("; ")}›" case IfLet(isRec, v, r, b) => s"${if (isRec) "rec " else ""}let ${v.showDbg} = ${r.showDbg} in ${b.showDbg}" } def showIn(implicit ctx: ShowCtx): Str = this match { case IfThen(lhs, rhs) => s"${lhs.showIn(!lhs.isInstanceOf[SimpleTerm])} then ${rhs.showIn(false)}" case IfElse(trm) => s"else ${trm.showIn(false)}" case IfBlock(ts) => s"${ts.map(_.fold(identity, identity)).mkString(ctx.lnIndStr)}" case IfOpApp(lhs, op, ib) => s"${lhs.showIn(false)} ${op.showIn(false)} ${ib.showIn}" case IfOpsApp(lhs, ops) => s"${lhs.showIn(false)} ${ops.iterator.map{case(v, r) => s"· $v $r"}.mkString(ctx.lnIndStr)}" case IfLet(isRec, v, r, b) => s"${if (isRec) "rec " else ""}let ${v.showIn(false)} = ${r.showIn(false)} in ${b.showIn}" } } ================================================ FILE: shared/src/main/scala/mlscript/package.scala ================================================ import mlscript.utils._ import mlscript.utils.shorthands._ package object mlscript { type Raise = Diagnostic => Unit val ExtrusionPrefix: Str = "??" } ================================================ FILE: shared/src/main/scala/mlscript/pretyper/Diagnosable.scala ================================================ package mlscript package pretyper import scala.collection.mutable.Buffer import Diagnostic.Source, Message.MessageContext import mlscript.utils._, shorthands._ /** * A trait containing a mutable buffer of diagnostics. */ trait Diagnosable { private val diagnosticBuffer = Buffer.empty[Diagnostic] protected def raise(diagnostics: Diagnostic): Unit = diagnosticBuffer += diagnostics protected def raiseMany(diagnostics: IterableOnce[Diagnostic]): Unit = diagnosticBuffer ++= diagnostics protected def raiseError(source: Source, messages: (Message -> Opt[Loc])*): Unit = raise(ErrorReport(messages.toList, newDefs = true, source)) protected def raiseWarning(source: Source, messages: (Message -> Opt[Loc])*): Unit = raise(WarningReport(messages.toList, newDefs = true, source)) @inline final def filterDiagnostics(f: Diagnostic => Bool): Ls[Diagnostic] = diagnosticBuffer.iterator.filter(f).toList @inline final def getDiagnostics: Ls[Diagnostic] = diagnosticBuffer.toList } ================================================ FILE: shared/src/main/scala/mlscript/pretyper/PreTyper.scala ================================================ package mlscript package pretyper import annotation.tailrec, collection.mutable.{Set => MutSet}, collection.immutable.SortedMap, util.chaining._ import utils._, shorthands._, Diagnostic.PreTyping, Message.MessageContext, symbol._, ucs.Desugarer class PreTyper extends Traceable with Diagnosable with Desugarer { /** A shorthand function to raise errors without specifying the source. */ protected def raiseError(messages: (Message -> Opt[Loc])*): Unit = raiseError(PreTyping, messages: _*) /** A shorthand function to raise warnings without specifying the source. */ protected def raiseWarning(messages: (Message -> Opt[Loc])*): Unit = raiseWarning(PreTyping, messages: _*) private def extractParameters(fields: Term): Ls[LocalTermSymbol] = { def rec(term: Term): Iterator[LocalTermSymbol] = term match { case nme: Var => Iterator.single(new LocalTermSymbol(nme)) case Bra(false, term) => rec(term) case Bra(true, Rcd(fields)) => fields.iterator.flatMap { case (_, Fld(_, pat)) => rec(pat) } case Asc(term, _) => rec(term) case literal: Lit => raiseWarning(msg"literal patterns are ignored" -> literal.toLoc) Iterator.empty case App(Var(name), parameters) if name.isCapitalized => rec(parameters) case Tup(arguments) => arguments.iterator.flatMap { case (S(nme: Var), Fld(_, _)) => Iterator.single(new LocalTermSymbol(nme)) case (N, Fld(_, term)) => rec(term) } case PlainTup(arguments @ _*) => arguments.iterator.flatMap(extractParameters) case other => raiseError(msg"unsupported pattern shape" -> other.toLoc) Iterator.empty } rec(fields).tap { rs => println(s"extractParameters ${fields.showDbg} ==> ${rs.map(_.name).mkString(", ")}") }.toList } protected def resolveVar(v: Var)(implicit scope: Scope): Unit = scope.getTermSymbol(v.name) match { case S(sym: LocalTermSymbol) => println(s"resolveVar `${v.name}` ==> local term") v.symbol = sym case S(sym: DefinedTermSymbol) => println(s"resolveVar `${v.name}` ==> defined term") v.symbol = sym case S(sym: ModuleSymbol) => println(s"resolveVar `${v.name}` ==> module") v.symbol = sym case N => scope.getTypeSymbol(v.name) match { case S(sym: ClassSymbol) => println(s"resolveVar `${v.name}` ==> class") v.symbol = sym case S(_) => raiseError(msg"identifier `${v.name}` is resolved to a type" -> v.toLoc) case N => raiseError(msg"identifier `${v.name}` not found" -> v.toLoc) } } protected def traverseVar(v: Var)(implicit scope: Scope): Unit = trace(s"traverseVar(name = \"${v.name}\")") { v.symbolOption match { case N => resolveVar(v) case S(symbol) => scope.getSymbols(v.name) match { case Nil => raiseError(msg"identifier `${v.name}` not found" -> v.toLoc) case symbols if symbols.contains(symbol) => () case symbols => def toNameLoc(symbol: Symbol): (Str, Opt[Loc]) = symbol match { case ds: DummyClassSymbol => s"`${ds.name}`" -> N case ts: TypeSymbol => s"`${ts.name}`" -> ts.defn.toLoc case ls: LocalTermSymbol => s"local `${ls.name}`" -> ls.nme.toLoc case dt: DefinedTermSymbol => s"`${dt.name}`" -> dt.defn.toLoc } val (name, loc) = toNameLoc(symbol) raiseError( (msg"identifier ${v.name} refers to different symbols." -> v.toLoc :: msg"it is resolved to $name" -> loc :: symbols.map { s => val (name, loc) = toNameLoc(s) msg"it was previously resolved to $name" -> loc }): _* ) } } }() protected def traverseTerm(term: Term)(implicit scope: Scope): Unit = trace(s"traverseTerm <== ${term.showDbg}") { term match { case Assign(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Bra(_, trm) => traverseTerm(trm) case Lam(lhs, rhs) => traverseTerm(rhs)(scope ++ extractParameters(lhs)) case Sel(receiver, fieldName) => traverseTerm(receiver) case Let(isRec, nme, rhs, body) => traverseTerm(rhs) traverseTerm(body)(scope + new LocalTermSymbol(nme)) case New(head, body) => // `new C(...)` or `new C(){...}` or `new{...}` case Tup(fields) => fields.foreach { case (S(t), Fld(_, _)) => traverseTerm(t) case (N, Fld(_, t)) => traverseTerm(t) } case Asc(trm, ty) => traverseTerm(trm) case ef @ If(_, _) => traverseIf(ef)(scope) case TyApp(lhs, targs) => // Be consistent with the error message from `Typer`. raiseError(msg"type application syntax is not yet supported" -> term.toLoc) case Eqn(lhs, rhs) => raiseWarning(msg"unsupported `Eqn`: ${term.showDbg}" -> term.toLoc) case Blk(stmts) => traverseStatements(stmts, "block", scope) () case Subs(arr, idx) => traverseTerm(arr); traverseTerm(idx) case Bind(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Splc(fields) => fields.foreach { case L(t) => traverseTerm(t) case R(Fld(_, t)) => traverseTerm(t) } case Forall(params, body) => traverseTerm(body) case Rcd(fields) => fields.foreach { case (_, Fld(_, t)) => traverseTerm(t) } case CaseOf(trm, cases) => case With(trm, fields) => traverseTerm(trm); traverseTerm(fields) case Where(body, where) => raiseWarning(msg"unsupported `Where`: ${term.showDbg}" -> term.toLoc) // begin UCS shorthands ================================================ // * Old-style operators case App(App(Var("is"), _), _) => println(s"found UCS shorthand: ${term.showDbg}") val desugared = If(IfThen(term, Var("true")), S(Var("false"))) traverseIf(desugared) term.desugaredTerm = desugared.desugaredTerm case App(Var("is"), _) => println(s"found UCS shorthand: ${term.showDbg}") val desugared = If(IfThen(term, Var("true")), S(Var("false"))) traverseIf(desugared) term.desugaredTerm = desugared.desugaredTerm // * Old-style operators case App(App(Var("and"), PlainTup(lhs)), PlainTup(rhs)) => println(s"found UCS shorthand: ${term.showDbg}") val desugared = If(IfThen(lhs, rhs), S(Var("false"))) traverseIf(desugared) term.desugaredTerm = desugared.desugaredTerm case App(Var("and"), PlainTup(lhs, rhs)) => println(s"found UCS shorthand: ${term.showDbg}") val desugared = If(IfThen(lhs, rhs), S(Var("false"))) traverseIf(desugared) term.desugaredTerm = desugared.desugaredTerm // end UCS shorthands ================================================== case App(lhs, Tup(fields)) => traverseTerm(lhs) fields.foreach { _._2.value |> traverseTerm } case App(lhs, rhs) => traverseTerm(lhs); traverseTerm(rhs) case Test(trm, ty) => traverseTerm(trm) case _: Lit | _: Super => () case v: Var => traverseVar(v) case AdtMatchWith(cond, arms) => raiseWarning(msg"unsupported `AdtMatchWith`: ${term.showDbg}" -> term.toLoc) case Inst(body) => traverseTerm(body) case NuNew(cls) => traverseTerm(cls) case Rft(base, decls) => // For object refinement traverseTerm(base) traverseStatements(decls.entities, "Rft", scope) () case While(cond, body) => traverseTerm(cond) traverseTerm(body) case Ann(ann, receiver) => traverseTerm(receiver) case Quoted(body) => traverseTerm(body) case Unquoted(body) => traverseTerm(body) } }(_ => s"traverseTerm ==> ${term.showDbg}") private def traverseTypeDefinition(symbol: TypeSymbol, defn: NuTypeDef)(implicit scope: Scope): Unit = trace(s"traverseTypeDefinition <== ${defn.describe}") { traverseStatements(defn.body.entities, defn.nme.name, scope) () }(_ => s"traverseTypeDefinition <== ${defn.describe}") private def traverseStatements(statements: Ls[Statement], name: Str, parentScope: Scope): Scope = trace(s"traverseStatements <== $name: ${"statement".pluralize(statements.size, true)}") { // Pass 1: Build a scope with type symbols only. val typeSymbols = statements.collect { case t: NuTypeDef => TypeSymbol(t) } val scopeWithTypes = parentScope.derive ++ typeSymbols println(typeSymbols.iterator.map(_.name).mkString("type symbols: {", ", ", "}")) // Pass 1.1: Resolve parent type symbols. typeSymbols.foreach { case s: ClassLikeSymbol => s.parentTypeNames.foreach { nme => scopeWithTypes.getTypeSymbol(nme.name) match { case S(symbol) => nme.symbol = symbol case N => raiseError(msg"could not find definition `${nme.name}`" -> nme.toLoc) } } case _ => () } // Pass 2: Build a complete scope and collect definitional terms and terms to be traversed. val (completeScope, thingsToTraverse) = statements.foldLeft[(Scope, Ls[(Term \/ DefinedTermSymbol, Scope)])](scopeWithTypes, Nil) { case ((scope, acc), term: Term) => (scope, (L(term), scope) :: acc) case ((scope, acc), defn: NuFunDef) => val termSymbol = new DefinedTermSymbol(defn) val scopeWithSymbol = scope + termSymbol (scopeWithSymbol, (R(termSymbol), if (termSymbol.isRecursive) scopeWithSymbol else scope) :: acc) case (acc, _: NuTypeDef) => acc // Ignore type definitions. case (acc, other @ (_: Constructor | _: DataDefn | _: DatatypeDefn | _: Def | _: LetS | _: TypeDef)) => println(s"unknown statement: ${other.getClass.getSimpleName}") acc } // Pass 2.5: Traverse type definitions statements.iterator.flatMap { case defn: NuTypeDef => S(defn); case _ => N }.foreach { defn => val parameters = defn.params.map(extractParameters).getOrElse(Nil) val thisSymbol = new LocalTermSymbol(Var("this")) traverseTypeDefinition(TypeSymbol(defn), defn)(completeScope ++ (thisSymbol :: parameters)) } println(thingsToTraverse.iterator.map { case (L(term), _) => term.showDbg case (R(symbol), _) => symbol.name }.mkString("to be traversed: {", ", ", "}")) // Pass 3: Traverse terms collected from the last pass. println("Pass 3") thingsToTraverse.foreach { case (L(term), scope) => println("traverseTerm: " + term.showDbg) println("scope: " + scope.showLocalSymbols) traverseTerm(term)(scope) case (R(symbol), scope) => symbol.body match { case L(term) => if (symbol.isFunction) { traverseTerm(term)(completeScope) } else { traverseTerm(term)(scope) } case R(_) => () } } completeScope }({ scope => s"traverseStatements ==> Scope {${scope.showLocalSymbols}}" }) def apply(typingUnit: TypingUnit, scope: Scope, name: Str): Scope = trace(s"PreTyper <== $name: ${typingUnit.describe}") { traverseStatements(typingUnit.entities, name, scope) }({ scope => s"PreTyper ==> ${scope.showLocalSymbols}" }) } ================================================ FILE: shared/src/main/scala/mlscript/pretyper/Scope.scala ================================================ package mlscript package pretyper import annotation.tailrec, collection.immutable.Map import utils._, shorthands._, symbol._ final class Scope(val enclosing: Opt[Scope], val types: Map[Str, TypeSymbol], val terms: Map[Str, TermSymbol]) { import Scope._ @tailrec final def getTypeSymbol(name: Str): Opt[TypeSymbol] = types.get(name) match { case entry @ S(_) => entry case N => enclosing match { case N => N case S(scope) => scope.getTypeSymbol(name) } } @tailrec final def getTermSymbol(name: Str): Opt[TermSymbol] = terms.get(name) match { case entry @ S(_) => entry case N => enclosing match { case N => N case S(scope) => scope.getTermSymbol(name) } } final def getSymbols(name: Str): Ls[Symbol] = Ls.concat(getTypeSymbol(name), getTermSymbol(name)) @inline def +(sym: Symbol): Scope = sym match { case symbol: ModuleSymbol => new Scope(enclosing, types + (symbol.name -> symbol), terms + (symbol.name -> symbol)) case symbol: TypeSymbol => new Scope(enclosing, types + (symbol.name -> symbol), terms) case symbol: DefinedTermSymbol => val newTerms = terms + (symbol.name -> symbol) new Scope(enclosing, types, symbol.operatorAlias match { case N => newTerms case S(alias) => newTerms + (alias.name -> symbol) }) case symbol: TermSymbol => new Scope(enclosing, types, terms + (symbol.name -> symbol)) } @inline def ++(symbols: IterableOnce[Symbol]): Scope = { val (newTypes, newTerms) = partitionSymbols((types, terms), symbols) new Scope(enclosing, newTypes, newTerms) } def withEntries(syms: IterableOnce[Var -> Symbol]): Scope = { val (newTypes, newTerms) = syms.iterator.foldLeft((types, terms)) { case ((types, terms), (nme, symbol: ModuleSymbol)) => (types + (nme.name -> symbol), terms + (nme.name -> symbol)) case ((types, terms), (nme, symbol: TypeSymbol)) => (types + (nme.name -> symbol), terms) case ((types, terms), (nme, symbol: TermSymbol)) => (types, terms + (nme.name -> symbol)) } new Scope(enclosing, newTypes, newTerms) } @inline def symbols: Iterator[Symbol] = Iterator.concat[Symbol](types.values, terms.values) def derive: Scope = new Scope(S(this), Map.empty, Map.empty) def derive(symbols: IterableOnce[Symbol]): Scope = { val (newTypes, newTerms) = partitionSymbols((types, terms), symbols) new Scope(S(this), newTypes, newTerms) } def showLocalSymbols: Str = symbols.iterator.map(_.name).mkString(", ") } object Scope { def partitionSymbols( z: (Map[Str, TypeSymbol], Map[Str, TermSymbol]), symbols: IterableOnce[Symbol] ): (Map[Str, TypeSymbol], Map[Str, TermSymbol]) = symbols.iterator.foldLeft((z._1, z._2)) { case ((types, terms), symbol: ModuleSymbol) => (types + (symbol.name -> symbol), terms + (symbol.name -> symbol)) case ((types, terms), symbol: TypeSymbol) => (types + (symbol.name -> symbol), terms) case ((types, terms), symbol: DefinedTermSymbol) => (types, terms + (symbol.name -> symbol) ++ symbol.operatorAlias.map(_.name -> symbol)) case ((types, terms), symbol: TermSymbol) => (types, terms + (symbol.name -> symbol)) } def from(symbols: IterableOnce[Symbol]): Scope = { val (newTypes, newTerms) = partitionSymbols((Map.empty, Map.empty), symbols) new Scope(N, newTypes, newTerms) } val global: Scope = { def mod(name: Str) = NuTypeDef(Mod, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N, Nil) // def cls(name: Str) = NuTypeDef(Trt, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N) def als(name: Str) = NuTypeDef(Als, TypeName(name), Nil, N, N, N, Nil, N, N, TypingUnit(Nil))(N, N, Nil) val builtinTypes = Ls( new ModuleSymbol(mod("true"), Nil), new ModuleSymbol(mod("false"), Nil), new TypeAliasSymbol(als("nothing")), new DummyClassSymbol(Var("Object")), new DummyClassSymbol(Var("Int")), new DummyClassSymbol(Var("Num")), new DummyClassSymbol(Var("Str")), new DummyClassSymbol(Var("Bool")), ) val commaSymbol = new LocalTermSymbol(Var(",")) Scope.from( """true,false,document,window,typeof,toString,not,succ,log,discard,negate, |round,add,sub,mul,div,sqrt,lt,le,gt,ge,slt,sle,sgt,sge,length,concat, |join,eq,ne,error,id,if,emptyArray, |+,-,*,%,/,**,<,>,<=,>=,==,===,<>,&&,||,and, |numAdd,numSub,numMul,NaN""" .stripMargin .split(",") .iterator .map(_.trim) .map(name => new LocalTermSymbol(Var(name))) .concat(commaSymbol :: builtinTypes) ) } } ================================================ FILE: shared/src/main/scala/mlscript/pretyper/Symbol.scala ================================================ package mlscript package pretyper import annotation.tailrec, collection.immutable.SortedSet import utils._, shorthands._, ucs.context.Matchable package object symbol { sealed trait Symbol { def name: Str def typeSymbolOption: Opt[TypeSymbol] = this match { case symbol: TypeSymbol => S(symbol) case _ => N } def termSymbolOption: Opt[TermSymbol] = this match { case symbol: TermSymbol => S(symbol) case _ => N } } sealed trait TypeSymbol extends Symbol { def defn: NuTypeDef override def name: Str = defn.name def showDbg: Str = s"${defn.kind.str} $name" } object TypeSymbol { def apply(defn: NuTypeDef): TypeSymbol = defn.kind match { case Cls => new ClassSymbol(defn, extractSuperTypes(defn.parents)) case Als => new TypeAliasSymbol(defn) case Mxn => new MixinSymbol(defn) case Trt => new TraitSymbol(defn, extractSuperTypes(defn.parents)) case Mod => new ModuleSymbol(defn, extractSuperTypes(defn.parents)) } def unapply(symbol: TypeSymbol): Opt[NuTypeDef] = S(symbol.defn) private def extractSuperTypes(parents: Ls[Term]): Ls[Var] = { @tailrec def rec(acc: Ls[Var], rest: Ls[Term]): Ls[Var] = rest match { case Nil => acc.reverse case (nme: Var) :: tail => rec(nme :: acc, tail) case (TyApp(ty, _)) :: tail => rec(acc, ty :: tail) case (App(term, Tup(_))) :: tail => rec(acc, term :: tail) case head :: _ => lastWords(s"unknown type in parent types: ${head.showDbg}") } rec(Nil, parents) } } /** * When a type symbol is not defined and we must need a symbol in some * scenarios, we use a dummy symbol to represent it. * * @param nme the name of the expect type symbol. */ final class DummyClassSymbol(val nme: Var) extends ClassLikeSymbol { override val parentTypeNames: Ls[Var] = Nil override def defn: NuTypeDef = die override def name: Str = nme.name override def showDbg: Str = s"dummy class $name" } trait ClassLikeSymbol extends TypeSymbol { val parentTypeNames: Ls[Var] private val _baseClassLikeSymbols: Lazy[SortedSet[ClassLikeSymbol]] = mlscript.utils.Lazy({ implicit val ord: Ordering[ClassLikeSymbol] = new Ordering[ClassLikeSymbol] { override def compare(x: ClassLikeSymbol, y: ClassLikeSymbol): Int = x.name.compareTo(y.name) } val parentClassLikeSymbols = parentTypeNames.iterator.map(_.symbol).collect { case s: ClassLikeSymbol => s }.toList SortedSet.from( parentClassLikeSymbols.iterator ++ parentClassLikeSymbols.iterator.flatMap(_.baseClassLikeSymbols)) }) lazy val baseClassLikeSymbols: SortedSet[ClassLikeSymbol] = _baseClassLikeSymbols.force_! def <:<(that: ClassLikeSymbol): Bool = this === that || baseClassLikeSymbols.contains(that) } final class ClassSymbol( override val defn: NuTypeDef, override val parentTypeNames: Ls[Var] ) extends ClassLikeSymbol { require(defn.kind === Cls) } final class TraitSymbol( override val defn: NuTypeDef, override val parentTypeNames: Ls[Var] ) extends ClassLikeSymbol { require(defn.kind === Trt) } final class MixinSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Mxn) } final class TypeAliasSymbol(override val defn: NuTypeDef) extends TypeSymbol { require(defn.kind === Als) } final class ModuleSymbol( override val defn: NuTypeDef, override val parentTypeNames: Ls[Var] ) extends ClassLikeSymbol with TermSymbol { require(defn.kind === Mod) override def nameVar: Var = defn.nameVar } sealed trait TermSymbol extends Symbol with Matchable { def nameVar: Var } class DefinedTermSymbol(val defn: NuFunDef) extends TermSymbol { override def name: Str = defn.name override def nameVar: Var = defn.nme def body: Term \/ Type = defn.rhs def isFunction: Bool = defn.isLetRec.isEmpty def isRecursive: Bool = defn.isLetRec.getOrElse(true) def isDeclaration: Bool = defn.rhs.isRight def operatorAlias: Opt[Var] = defn.symbolicNme def declaredLoc: Opt[Loc] = defn.nme.toLoc } class LocalTermSymbol(val nme: Var) extends TermSymbol { override def name: Str = nme.name override def nameVar: Var = nme } trait Symbolic { val name: String private var _symbol: Opt[Symbol] = N def symbolOption: Opt[Symbol] = _symbol def symbol: Symbol = _symbol.getOrElse(lastWords(s"Symbol not set for $name")) def symbol_=(symbol: Symbol): Unit = _symbol match { case N => _symbol = S(symbol) case S(`symbol`) => () case S(current) => println(s"symbol: old ${current.name} vs new ${symbol.name}") lastWords(s"Symbol already set for $name") } def withSymbol(symbol: Symbol): this.type = { this.symbol = symbol; this } def getClassLikeSymbol: Opt[ClassLikeSymbol] = _symbol.collectFirst { case symbol: ClassLikeSymbol => symbol } } } ================================================ FILE: shared/src/main/scala/mlscript/pretyper/Traceable.scala ================================================ package mlscript package pretyper import utils._, shorthands._ trait Traceable { /** * The set of topics to debug. Explanation of possible values. * - `N`: Show nothing. * - `S(Set.empty)`: Show everything. * - `S(someTopics)`: Show only the topics in `someTopics`. * Override this function to enable debugging. */ protected def debugTopicFilters: Opt[Set[Str]] = N private var debugIndent = 0 private var currentDebugTopics: Opt[Str] = N private def matchTopicFilters: Boolean = debugTopicFilters match { case S(topics) if topics.isEmpty => true case S(topics) => currentDebugTopics.fold(false)(topics) case N => false } /** Override this function to redirect debug messages. */ protected def emitString(str: Str): Unit = scala.Predef.println(str) @inline private def printLineByLine(x: => Any, withIndent: Bool): Unit = x.toString.linesIterator.foreach( if (withIndent) line => emitString("| " * debugIndent + line) else emitString ) protected def trace[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = { println(pre) debugIndent += 1 val res = try thunk finally debugIndent -= 1 if (post isnt Traceable.noPostTrace) println(post(res)) res } protected def traceWithTopic[T](currentDebugTopics: Str)(thunk: => T): T = { this.currentDebugTopics = S(currentDebugTopics) val res = thunk this.currentDebugTopics = N res } @inline def traceNot[T](pre: => Str)(thunk: => T)(post: T => Str = Traceable.noPostTrace): T = thunk @inline protected def println(x: => Any, withIndent: Bool = true, force: Bool = false): Unit = if (force || matchTopicFilters) printLineByLine(x, withIndent) } object Traceable { val noPostTrace: Any => Str = _ => "" } ================================================ FILE: shared/src/main/scala/mlscript/syntax.scala ================================================ package mlscript import mlscript.utils._, shorthands._ // Terms final case class Pgrm(tops: Ls[Statement]) extends PgrmImpl sealed abstract class Decl extends DesugaredStatement with DeclImpl final case class Def(rec: Bool, nme: Var, rhs: Term \/ Type, isByname: Bool) extends Decl with Terms { val body: Located = rhs.fold(identity, identity) } final case class AdtInfo(ctorName: TypeName) final case class TypeDef( kind: TypeDefKind, nme: TypeName, tparams: List[TypeName], body: Type, mthDecls: List[MethodDef[Right[Term, Type]]], mthDefs: List[MethodDef[Left[Term, Type]]], positionals: Ls[Var], adtInfo: Opt[AdtInfo], ) extends Decl /** * Method type can be a definition or a declaration based * on the type parameter set. A declaration has `Type` in rhs * and definition has `Term` in rhs. * * @param rec indicates that the method is recursive * @param prt name of class to which method belongs * @param nme name of method * @param tparams list of parameters for the method if any * @param rhs term or type if definition and declaration respectively */ final case class MethodDef[RHS <: Term \/ Type]( rec: Bool, parent: TypeName, nme: Var, tparams: List[TypeName], rhs: RHS, ) extends Located { val body: Located = rhs.fold(identity, identity) val children: Ls[Located] = nme :: body :: Nil } sealed trait NameRef extends Located { val name: Str; def toVar: Var } sealed abstract class OuterKind(val str: Str) case object Block extends OuterKind("block") sealed abstract class DeclKind(str: Str) extends OuterKind(str) case object Val extends DeclKind("value") sealed abstract class TypeDefKind(str: Str) extends DeclKind(str) sealed trait ObjDefKind sealed trait ClsLikeKind extends ObjDefKind case object Cls extends TypeDefKind("class") with ClsLikeKind case object Trt extends TypeDefKind("trait") with ObjDefKind case object Mxn extends TypeDefKind("mixin") case object Als extends TypeDefKind("type alias") case object Mod extends TypeDefKind("module") with ClsLikeKind sealed abstract class Term extends Terms with TermImpl sealed abstract class Lit extends SimpleTerm with LitImpl final case class Var(name: Str) extends SimpleTerm with VarImpl with NameRef final case class Lam(lhs: Term, rhs: Term) extends Term final case class App(lhs: Term, rhs: Term) extends Term final case class Tup(fields: Ls[Opt[Var] -> Fld]) extends Term with TupImpl final case class Rcd(fields: Ls[Var -> Fld]) extends Term final case class Sel(receiver: Term, fieldName: Var) extends Term final case class Let(isRec: Bool, name: Var, rhs: Term, body: Term) extends Term final case class Blk(stmts: Ls[Statement]) extends Term with BlkImpl with Outer final case class Bra(rcd: Bool, trm: Term) extends Term final case class Asc(trm: Term, ty: Type) extends Term final case class Bind(lhs: Term, rhs: Term) extends Term final case class Test(trm: Term, ty: Term) extends Term final case class With(trm: Term, fields: Rcd) extends Term final case class CaseOf(trm: Term, cases: CaseBranches) extends Term with CaseOfImpl final case class Subs(arr: Term, idx: Term) extends Term final case class Assign(lhs: Term, rhs: Term) extends Term final case class Splc(fields: Ls[Either[Term, Fld]]) extends Term final case class New(head: Opt[(NamedType, Term)], body: TypingUnit) extends Term // `new C(...)` or `new C(){...}` or `new{...}` final case class NuNew(cls: Term) extends Term final case class If(body: IfBody, els: Opt[Term]) extends Term final case class TyApp(lhs: Term, targs: Ls[Type]) extends Term final case class Where(body: Term, where: Ls[Statement]) extends Term final case class Forall(params: Ls[TypeVar], body: Term) extends Term final case class Inst(body: Term) extends Term // Explicit instantiation of polymohic term final case class Super() extends Term final case class Eqn(lhs: Var, rhs: Term) extends Term // equations such as x = y, notably used in constructors; TODO: make lhs a Term final case class Quoted(body: Term) extends Term final case class Unquoted(body: Term) extends Term final case class Rft(base: Term, decls: TypingUnit) extends Term final case class While(cond: Term, body: Term) extends Term final case class Ann(ann: Term, receiver: Term) extends Term final case class AdtMatchWith(cond: Term, arms: Ls[AdtMatchPat]) extends Term final case class AdtMatchPat(pat: Term, rhs: Term) extends AdtMatchPatImpl sealed abstract class IfBody extends IfBodyImpl final case class IfThen(expr: Term, rhs: Term) extends IfBody final case class IfElse(expr: Term) extends IfBody final case class IfLet(isRec: Bool, name: Var, rhs: Term, body: IfBody) extends IfBody final case class IfOpApp(lhs: Term, op: Var, rhs: IfBody) extends IfBody final case class IfOpsApp(lhs: Term, opsRhss: Ls[Var -> IfBody]) extends IfBody final case class IfBlock(lines: Ls[IfBody \/ Statement]) extends IfBody // final case class IfApp(fun: Term, opsRhss: Ls[Var -> IfBody]) extends IfBody final case class FldFlags(mut: Bool, spec: Bool, genGetter: Bool) extends FldFlagsImpl // TODO make it a Located and use in diagnostics final case class Fld(flags: FldFlags, value: Term) extends FldImpl object FldFlags { val empty: FldFlags = FldFlags(false, false, false) } sealed abstract class CaseBranches extends CaseBranchesImpl final case class Case(pat: SimpleTerm, body: Term, rest: CaseBranches)(val refined: Bool) extends CaseBranches final case class Wildcard(body: Term) extends CaseBranches // final case class TupleCase(numElems: Int, canHaveMore: Bool, body: Term, rest: CaseBranches) extends CaseBranches final case object NoCases extends CaseBranches final case class IntLit(value: BigInt) extends Lit final case class DecLit(value: BigDecimal) extends Lit final case class StrLit(value: Str) extends Lit final case class UnitLit(undefinedOrNull: Bool) extends Lit trait IdentifiedTerm sealed abstract class SimpleTerm extends Term with IdentifiedTerm with SimpleTermImpl sealed trait Statement extends StatementImpl final case class LetS(isRec: Bool, pat: Term, rhs: Term) extends Statement final case class DataDefn(body: Term) extends Statement final case class DatatypeDefn(head: Term, body: Term) extends Statement sealed trait DesugaredStatement extends Statement with DesugaredStatementImpl sealed trait Terms extends DesugaredStatement // Types sealed abstract class TypeLike extends TypeLikeImpl sealed abstract class Type extends TypeLike with TypeImpl sealed trait NamedType extends Type { def base: TypeName; def targs: Ls[Type] } sealed abstract class Composed(val pol: Bool) extends Type with ComposedImpl final case class Union(lhs: Type, rhs: Type) extends Composed(true) final case class Inter(lhs: Type, rhs: Type) extends Composed(false) final case class Function(lhs: Type, rhs: Type) extends Type final case class Record(fields: Ls[Var -> Field]) extends Type final case class Tuple(fields: Ls[Opt[Var] -> Field]) extends Type final case class Recursive(uv: TypeVar, body: Type) extends Type final case class AppliedType(base: TypeName, targs: List[Type]) extends Type with NamedType final case class Selection(base: Type, name: TypeName) extends Type final case class Neg(base: Type) extends Type final case class Rem(base: Type, names: Ls[Var]) extends Type final case class Bounds(lb: Type, ub: Type) extends Type final case class WithExtension(base: Type, rcd: Record) extends Type final case class Splice(fields: Ls[Either[Type, Field]]) extends Type final case class Constrained(base: TypeLike, tvBounds: Ls[TypeVar -> Bounds], where: Ls[Bounds], tscs: Ls[Ls[(Bool, Type)] -> Ls[Ls[Type]]]) extends Type // final case class FirstClassDefn(defn: NuTypeDef) extends Type // TODO // final case class Refinement(base: Type, decls: TypingUnit) extends Type // TODO final case class Field(in: Opt[Type], out: Type) extends FieldImpl sealed abstract class NullaryType extends Type case object Top extends NullaryType case object Bot extends NullaryType /** Literal type type, e.g. type `0` is a type with only one possible value `0`. */ final case class Literal(lit: Lit) extends NullaryType /** Reference to an existing type with the given name. */ final case class TypeName(name: Str) extends NullaryType with NamedType with TypeNameImpl with NameRef final case class TypeTag (name: Str) extends NullaryType final case class TypeVar(val identifier: Int \/ Str, nameHint: Opt[Str]) extends NullaryType with TypeVarImpl { require(nameHint.isEmpty || identifier.isLeft) // ^ The better data structure to represent this would be an EitherOrBoth override def toString: Str = identifier.fold("α" + _, identity) } final case class PolyType(targs: Ls[TypeName \/ TypeVar], body: Type) extends Type with PolyTypeImpl // New Definitions AST final case class TypingUnit(rawEntities: Ls[Statement]) extends TypingUnitImpl // final case class TypingUnit(entities: Ls[Statement]) extends TypeLike with PgrmOrTypingUnit with TypingUnitImpl final case class Signature(members: Ls[NuDecl], result: Opt[Type]) extends TypeLike with SignatureImpl sealed abstract class NuDecl extends TypeLike with Statement with NuDeclImpl sealed trait Outer { def kind: OuterKind } final case class NuTypeDef( kind: TypeDefKind, nme: TypeName, tparams: Ls[(Opt[VarianceInfo], TypeName)], params: Opt[Tup], // the specialized parameters for that type ctor: Opt[Constructor], sig: Opt[Type], parents: Ls[Term], superAnnot: Opt[Type], thisAnnot: Opt[Type], body: TypingUnit )(val declareLoc: Opt[Loc], val abstractLoc: Opt[Loc], val annotations: Ls[Term]) extends NuDecl with Statement with Outer { def isPlainJSClass: Bool = params.isEmpty } final case class NuFunDef( isLetRec: Opt[Bool], // None means it's a `fun`, which is always recursive; Some means it's a `let`/`let rec` or `val` nme: Var, symbolicNme: Opt[Var], tparams: Ls[TypeName], rhs: Term \/ Type, )( val declareLoc: Opt[Loc], val virtualLoc: Opt[Loc], // Some(Loc) means that the function is modified by keyword `virtual` val mutLoc: Opt[Loc], val signature: Opt[NuFunDef], val outer: Opt[Outer], val genField: Bool, // true means it's a `val`; false means it's a `let` val annotations: Ls[Term], ) extends NuDecl with DesugaredStatement { val body: Located = rhs.fold(identity, identity) def kind: DeclKind = Val val abstractLoc: Opt[Loc] = None def isLetOrLetRec: Bool = isLetRec.isDefined && !genField // If the member has no implementation, it is virtual automatically def isVirtual: Bool = virtualLoc.nonEmpty || rhs.isRight def isMut: Bool = mutLoc.nonEmpty def isGeneralized: Bool = isLetRec.isEmpty || (rhs match { case Left(value) => value.isGeneralizableLam case Right(ty) => false }) } final case class Constructor(params: Tup, body: Blk) extends DesugaredStatement with ConstructorImpl // constructor(...) { ... } final case class VarianceInfo(isCovariant: Bool, isContravariant: Bool) { /** Combine two pieces of variance information together */ def &&(that: VarianceInfo): VarianceInfo = VarianceInfo(isCovariant && that.isCovariant, isContravariant && that.isContravariant) /* Flip the current variance if it encounters a contravariant position */ def flip: VarianceInfo = VarianceInfo(isContravariant, isCovariant) override def toString: Str = show def show: Str = this match { case (VarianceInfo(true, true)) => "±" case (VarianceInfo(false, true)) => "-" case (VarianceInfo(true, false)) => "+" case (VarianceInfo(false, false)) => "=" } } object VarianceInfo { val bi: VarianceInfo = VarianceInfo(true, true) val co: VarianceInfo = VarianceInfo(true, false) val contra: VarianceInfo = VarianceInfo(false, true) val in: VarianceInfo = VarianceInfo(false, false) } ================================================ FILE: shared/src/main/scala/mlscript/ucs/Desugarer.scala ================================================ package mlscript package ucs import collection.mutable.{Map => MutMap} import syntax.{source => s, core => c}, stages._, context.{Context, Scrutinee} import mlscript.ucs.display.{showNormalizedTerm, showSplit} import mlscript.pretyper.{PreTyper, Scope} import mlscript.pretyper.symbol._ import Message.MessageContext import utils._, shorthands._ /** * The main class of the UCS desugaring. */ trait Desugarer extends Transformation with Desugaring with Normalization with PostProcessing with CoverageChecking { self: PreTyper => /** A shorthand function to raise _desugaring_ errors without specifying the source. */ protected def raiseDesugaringError(messages: (Message -> Opt[Loc])*): Unit = raiseError(Diagnostic.Desugaring, messages: _*) protected def reportUnreachableCase[T <: Located](unreachable: Located, subsumedBy: T, onlyIf: Bool = true): T = { if (onlyIf) raiseDesugaringWarning( msg"this case is unreachable" -> unreachable.toLoc, msg"because it is subsumed by the branch" -> subsumedBy.toLoc ) subsumedBy } /** A shorthand function to raise _desugaring_ warnings without specifying the source. */ protected def raiseDesugaringWarning(messages: (Message -> Opt[Loc])*): Unit = raiseWarning(Diagnostic.Desugaring, messages: _*) /** Create a fresh local symbol for the given `Var`. */ protected def freshSymbol(nme: Var): LocalTermSymbol = new LocalTermSymbol(nme) /** Common operations of `Var` which can be shared within all stages. */ protected implicit class VarOps(nme: Var) { /** Associate the given `Var` with a fresh `ValueSymbol`. */ def withFreshSymbol: Var = nme.withSymbol(freshSymbol(nme)) /** * Expect the given `symbol` to be a class-like symbol. If it is not, we * get or create a dummy class symbol for it. The desugaring can continue * and `Typer` will throw an error for this miuse. */ private def requireClassLikeSymbol(symbol: TypeSymbol)(implicit context: Context): ClassLikeSymbol = symbol match { case symbol: ClassLikeSymbol => symbol case symbol: MixinSymbol => raiseDesugaringError(msg"Mixins are not allowed in pattern" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) case symbol: TypeAliasSymbol => raiseDesugaringError(msg"Type alias is not allowed in pattern" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) } /** * If the given `Var` represents a class name, get its associated `ClassSymbol`. * * @param className the class name variable */ def getClassLikeSymbol(implicit context: Context): TypeSymbol = { val symbol = nme.symbolOption match { case S(symbol: TypeSymbol) => requireClassLikeSymbol(symbol) case S(symbol: TermSymbol) => raiseDesugaringError(msg"variable ${nme.name} is not associated with a class symbol" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) case N => raiseDesugaringError(msg"variable ${nme.name} is not associated with any symbols" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) } println(s"getClassLikeSymbol: ${nme.name} ==> ${symbol.showDbg}") symbol } /** * A short hand for `nme.symbol.getScrutinee` but add a diagnostic message * to a local diagnostic archive if there's any error. */ def getOrCreateScrutinee(implicit context: Context): Scrutinee = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.getOrCreateScrutinee case S(otherSymbol) => raiseDesugaringError(msg"cannot identifier `${nme.name}` with a scrutinee" -> nme.toLoc) context.freshScrutinee case N => lastWords(s"`${nme.name}` should be assoicated with a symbol") } /** Associate the `Var` with a scrutinee and returns the same `Var`. */ def withScrutinee(scrutinee: Scrutinee)(implicit context: Context): Var = nme.symbolOption match { case S(symbol: TermSymbol) => symbol.addScrutinee(scrutinee) nme case S(otherSymbol) => raiseDesugaringError(msg"cannot identifier `${nme.name}` with a scrutinee" -> nme.toLoc) nme case N => lastWords(s"`${nme.name}` should be assoicated with a symbol") } /** * If the `Var` is associated with a term symbol, returns it. Otherwise, * resolve the term symbol and associate the `Var` with the term symbol. */ def getOrResolveTermSymbol(implicit scope: Scope): TermSymbol = { nme.symbolOption match { case N => resolveTermSymbol case S(symbol: TermSymbol) => symbol case S(otherSymbol) => raiseDesugaringError(msg"identifier `${nme.name}` should be a term" -> nme.toLoc) freshSymbol(nme) } } /** Associate the `Var` with a term symbol and returns the term symbol. */ def resolveTermSymbol(implicit scope: Scope): TermSymbol = { val symbol = scope.getTermSymbol(nme.name).getOrElse { raiseDesugaringError(msg"identifier `${nme.name}` not found" -> nme.toLoc) freshSymbol(nme) } nme.symbol = symbol symbol } /** Associate the `Var` with a term symbol and returns the same `Var`. */ def withResolvedTermSymbol(implicit scope: Scope): Var = { nme.resolveTermSymbol; nme } /** Associate the `Var` with a class like symbol and returns the class like symbol. */ def resolveClassLikeSymbol(implicit scope: Scope, context: Context): ClassLikeSymbol = { val symbol = scope.getTypeSymbol(nme.name) match { case S(symbol) => requireClassLikeSymbol(symbol) case N => raiseDesugaringError(msg"type identifier `${nme.name}` not found" -> nme.toLoc) context.getOrCreateDummyClassSymbol(nme) } nme.symbol = symbol symbol } /** * Associate the `Var` with a class like symbol and returns the same `Var`. * We might be able to remove this function. Currently, it is only used for * resolving `Var("true")` and `Var("false")` in `Desugaring`. */ def withResolvedClassLikeSymbol(implicit scope: Scope, context: Context): Var = { nme.resolveClassLikeSymbol; nme } } /** * This class defines common operations on _splits_ in source abstract syntax * (i.e., `ucs.syntax.source.Split`). */ protected implicit class SourceSplitOps[+B <: s.Branch](these: s.Split[B]) { /** * Concatenate two splits and raise a warning if the latter is discarded. * * @param those the right-hand side `ucs.syntax.source.Split` * @return a new split which is the concatenation of LHS and RHS */ def ++[BB >: B <: s.Branch](those: s.Split[BB]): s.Split[BB] = if (those === s.Split.Nil) these else (these match { case s.Split.Cons(head, tail) => s.Split.Cons(head, tail ++ those) case s.Split.Let(rec, nme, rhs, tail) => s.Split.Let(rec, nme, rhs, tail ++ those) case s.Split.Else(_) => reportUnreachableCase(those, these) case s.Split.Nil => those }) } /** * This class defines common operations on _splits_ in _core_ abstract syntax * (i.e., `ucs.syntax.core.Split`). */ protected implicit class CoreSplitOps(these: c.Split) { /** * Concatenate two splits and raise a warning if the latter is discarded. * * @param those the right-hand side `ucs.syntax.core.Split` * @return a new split which is the concatenation of LHS and RHS */ def ++(those: c.Split): c.Split = if (those === c.Split.Nil) these else (these match { case me: c.Split.Cons => me.copy(tail = me.tail ++ those) case me: c.Split.Let => me.copy(tail = me.tail ++ those) case _: c.Split.Else => reportUnreachableCase(those, these) case c.Split.Nil => those }) } /** * The entry-point of desugaring a UCS syntax tree (`If` node) to a normal * MLscript syntax tree made of `CaseOf` and `Let` nodes. `PreTyper` is * supposed to call this function. Note that the caller doesn't need to * resolve symbols and bindings inside the UCS tree. * * @param if the UCS syntax tree to be desugared * @param scope the scope of the `If` node */ protected def traverseIf(`if`: If)(implicit scope: Scope): Unit = { `if`.desugaredTerm match { case S(desugaredTerm) => lastWords("the `if` expression has already been desugared, please make sure that the objects are copied") case N => () } implicit val context: Context = new Context(`if`) trace("traverseIf") { // Stage 0: Transformation val transformed = traceWithTopic("transform") { println("STEP 0") transform(`if`) } traceWithTopic("transform.result") { println("Transformed UCS term:", withIndent = false) println(showSplit(transformed), withIndent = false) } // Stage 1: Desugaring val desugared = traceWithTopic("desugar") { println("STEP 1") desugar(transformed) } traceWithTopic("desugar.result") { println("Desugared UCS term:", withIndent = false) println(showSplit(desugared), withIndent = false) } traceWithTopic("traverse") { println("STEP 1.5") traverseSplit(desugared) } // Stage 2: Normalization val normalized = traceWithTopic("normalize") { println("STEP 2") normalize(desugared) } traceWithTopic("normalize.result") { println("Normalized UCS term:", withIndent = false) println(showNormalizedTerm(normalized), withIndent = false) } // Stage 3: Post-processing val postProcessed = traceWithTopic("postprocess") { println("STEP 3") postProcess(normalized) } traceWithTopic("postprocess.result") { println("Post-processed UCS term:", withIndent = false) println(showNormalizedTerm(postProcessed), withIndent = false) } // Stage 4: Coverage checking traceWithTopic("coverage") { val checked = println("STEP 4") val diagnostics = checkCoverage(postProcessed) println(s"Coverage checking result: ${diagnostics.size} errors") raiseMany(diagnostics) } traceWithTopic("desugared") { println(s"Desugared term: ${postProcessed.showDbg}", withIndent = false) } // Epilogue `if`.desugaredTerm = S(postProcessed) }(_ => "traverseIf ==> ()") } /** * Traverse a desugared _core abstract syntax_ tree. The function takes care * of let bindings and resolves variables. */ private def traverseSplit(split: c.Split)(implicit scope: Scope): Unit = split match { case c.Split.Cons(c.Branch(scrutinee, pattern, continuation), tail) => traverseTerm(scrutinee) val patternSymbols = pattern.declaredVars.map(nme => nme -> nme.symbol) traverseSplit(continuation)(scope.withEntries(patternSymbols)) traverseSplit(tail) case c.Split.Let(isRec, name, rhs, tail) => val recScope = scope + name.symbol traverseTerm(rhs)(if (isRec) recScope else scope) traverseSplit(tail)(recScope) case c.Split.Else(default) => traverseTerm(default) case c.Split.Nil => () } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/context/Context.scala ================================================ package mlscript package ucs package context import collection.mutable.{Buffer, Map => MutMap} import utils._, shorthands._ import pretyper.symbol.{DummyClassSymbol, TypeSymbol} import pretyper.Scope class Context(originalTerm: If) { /** The prefix of all prefixes. */ private val prefix = Context.freshPrefix private val cachePrefix = prefix + "$cache$" private val scrutineePrefix = prefix + "$scrut$" private val testPrefix = prefix + "$test$" private val shadowPrefix = prefix + "$shadow$" val unappliedPrefix: Str = prefix + "$args_" def isCacheVar(nme: Var): Bool = nme.name.startsWith(cachePrefix) def isScrutineeVar(nme: Var): Bool = nme.name.startsWith(scrutineePrefix) def isTestVar(nme: Var): Bool = nme.name.startsWith(testPrefix) def isUnappliedVar(nme: Var): Bool = nme.name.startsWith(unappliedPrefix) def isGeneratedVar(nme: Var): Bool = isCacheVar(nme) || isScrutineeVar(nme) || isTestVar(nme) || isUnappliedVar(nme) // Call these objects to generate fresh variables for different purposes. // I plan to mix the unique identifiers of UCS expressions into the prefixes. // So that the generated variables will definitely not conflict with others. val freshCache: VariableGenerator = new VariableGenerator(cachePrefix) val freshScrutineeVar: VariableGenerator = new VariableGenerator(scrutineePrefix) val freshTest: VariableGenerator = new VariableGenerator(testPrefix) val freshShadowed: VariableGenerator = new VariableGenerator(shadowPrefix) // Symbol Management // ================= private val dummyClassSymbols: MutMap[Var, DummyClassSymbol] = MutMap.empty def getOrCreateDummyClassSymbol(nme: Var): DummyClassSymbol = dummyClassSymbols.getOrElseUpdate(nme, new DummyClassSymbol(nme)) // Scrutinee Management // ==================== /** The buffer contains all `ScrutineeData` created within this context. */ private val scrutineeBuffer: Buffer[Scrutinee] = Buffer.empty def freshScrutinee: Scrutinee = { val scrutinee = new Scrutinee(this, N) scrutineeBuffer += scrutinee scrutinee } private[context] def freshScrutinee(parent: Scrutinee): Scrutinee = { val scrutinee = new Scrutinee(this, S(parent)) scrutineeBuffer += scrutinee scrutinee } def scrutinees: Iterator[Scrutinee] = scrutineeBuffer.iterator } object Context { private val freshPrefix: Str = "ucs" } ================================================ FILE: shared/src/main/scala/mlscript/ucs/context/Matchable.scala ================================================ package mlscript.ucs.context import collection.mutable.{Map => MutMap} import mlscript.utils._, shorthands._ /** * A trait for "things" (e.g., variables, functions, symbols, etc) that can be * matched in a UCS term. This trait holds everything that is needed to track * scrutinee information. */ trait Matchable { /** * A map from the context to the scrutinee associated with the symbol in the * context. The reason why we need this map is that the same symbol may be * matched in different UCS terms. Each context corresponds to a UCS term. */ private val scrutinees: MutMap[Context, Scrutinee] = MutMap.empty /** * Get the scrutinee associated with the symbol in the given context. If * there's no scrutinee associated with the symbol, create a new one in the * given context. **Note**: This function should only be called from the * UCS desugarer. */ private[ucs] def getOrCreateScrutinee(implicit context: Context): Scrutinee = scrutinees.getOrElseUpdate(context, context.freshScrutinee) /** Get the scrutinee associated with the symbol in the given context. */ def getScrutinee(implicit context: Context): Opt[Scrutinee] = scrutinees.get(context) /** Check if the symbol is associated with a scrutinee in the given context. */ def isScrutinee(implicit context: Context): Bool = scrutinees.contains(context) /** Associate the symbol with a scrutinee in the given context. */ private[ucs] def addScrutinee(scrutinee: Scrutinee)(implicit context: Context): Unit = { require(!isScrutinee) // It should be impossible to add a scrutinee twice. scrutinees += context -> scrutinee } /** Associate the symbol with a scrutinee in the given context and returns the current object. */ private[ucs] def withScrutinee(scrutinee: Scrutinee)(implicit context: Context): this.type = { addScrutinee(scrutinee) this } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/context/Pattern.scala ================================================ package mlscript package ucs.context import collection.mutable.{Buffer, SortedMap => MutSortedMap} import utils._, shorthands._ import pretyper.symbol.{ClassLikeSymbol, DummyClassSymbol, ModuleSymbol, TypeSymbol} sealed abstract class Pattern { private val locationsBuffer: Buffer[Loc] = Buffer.empty def addLocation(located: Located): Unit = located.getLoc.foreach(locationsBuffer += _) def addLocation(location: Opt[Loc]): Unit = locationsBuffer ++= location /** Get the location of this pattern's first occurrence. */ def firstOccurrence: Option[Loc] = locationsBuffer.headOption def locations: Ls[Loc] = locationsBuffer.toList def arity: Opt[Int] def showDbg: Str /** Get a string suitable for diagnostics. */ def showInDiagnostics: Str /** * Checks if the pattern is same as expressed by the given `SimpleTerm`. Note * that we should pass `pat` of `Case` to this function. */ def matches(pat: SimpleTerm): Bool /** Create a `SimpleTerm` which can be used as `pat` of `Case`. */ def toCasePattern: SimpleTerm def refined: Bool } object Pattern { final case class ClassLike( val classLikeSymbol: ClassLikeSymbol, scrutinee: Scrutinee )(override val refined: Bool) extends Pattern { private var unappliedVarOpt: Opt[Var] = N private val parameters: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty private[context] def findSubScrutinee(scrutinee: Scrutinee): Opt[Int] = parameters.find(_._2 === scrutinee).map(_._1) /** * Get or create a sub-scrutinee for the given parameter index. * * @param index the index of the parameter. * @return a `ScrutineeData` for the parameter whose parent scrutinee is the * current scrutinee */ def getParameter(index: Int): Scrutinee = { require(index >= 0) parameters.getOrElseUpdate(index, scrutinee.freshSubScrutinee) } def getUnappliedVar(default: => Var): Var = unappliedVarOpt.getOrElse { val unappliedVar = default unappliedVarOpt = S(unappliedVar) unappliedVar } override def arity: Opt[Int] = parameters.keysIterator.maxOption.map(_ + 1) override def showDbg: Str = (if (refined) "refined " else "") + s"${classLikeSymbol.name}" override def showInDiagnostics: Str = s"${(classLikeSymbol match { case dummySymbol: DummyClassSymbol => "class" case s: ModuleSymbol if s.name === "true" || s.name === "false" => s"Boolean value" case otherSymbol: TypeSymbol => otherSymbol.defn.kind.str })} `${classLikeSymbol.name}`" /** Checks whether this pattern can cover the given pattern. */ override def matches(pat: SimpleTerm): Bool = pat match { case pat: Var => pat.getClassLikeSymbol.fold(false)(_ <:< classLikeSymbol) case _: Lit => false } override def toCasePattern: SimpleTerm = Var(classLikeSymbol.name).withLoc(firstOccurrence).withSymbol(classLikeSymbol) } final case class Tuple(scrutinee: Scrutinee) extends Pattern { private val fields: MutSortedMap[Int, Scrutinee] = MutSortedMap.empty def getField(index: Int): Scrutinee = fields.getOrElseUpdate(index, scrutinee.freshSubScrutinee) override def arity: Opt[Int] = fields.keysIterator.maxOption.map(_ + 1) override def showDbg: Str = s"tuple#${arity.getOrElse("?")}" override def showInDiagnostics: Str = "tuple of " + (arity match { case N => "certain number of elements" case S(1) => "1 element" case S(n) => s"${n} elements" }) override def matches(pat: SimpleTerm): Bool = false /** * Note that currently we only support simple tuple patterns. They should * disappear after desugaring stage, therefore, it will be an error if you * find an instance of this class. */ override def toCasePattern: SimpleTerm = ??? override def refined: Bool = ??? } final case class Literal(val literal: Lit) extends Pattern { override def arity: Opt[Int] = N override def showDbg: Str = literal.idStr override def showInDiagnostics: Str = s"literal ${literal.idStr}" override def matches(pat: SimpleTerm): Bool = pat match { case _: Var => false case pat => pat === literal } override def toCasePattern: SimpleTerm = literal.withLoc(firstOccurrence) override def refined: Bool = false } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/context/Scrutinee.scala ================================================ package mlscript package ucs.context import collection.mutable.{Buffer, SortedMap => MutSortedMap, SortedSet => MutSortedSet} import pretyper.symbol.{ClassLikeSymbol, TermSymbol, TypeSymbol}, utils._, shorthands._ import scala.annotation.tailrec class Scrutinee(val context: Context, parent: Opt[Scrutinee]) { import Scrutinee._ private val locations: Buffer[Loc] = Buffer.empty private var generatedVarOpt: Opt[Var] = N private val classLikePatterns: MutSortedMap[TypeSymbol, Pattern.ClassLike] = MutSortedMap.empty(classLikeSymbolOrdering) // Currently we only support simple tuple patterns, so there is only _one_ // slot for tuple patterns. After we support complex tuple patterns, we need // to extend this fields to a map from tuple arity to `Pattern.Tuple`. // private val tuplePatterns: MutMap[Int, Pattern.Tuple] = MutMap.empty // If we support tuple pattern splice, we need a more expressive key in the // map's type. private var tuplePatternOpt: Opt[Pattern.Tuple] = N private var aliasVarSet: MutSortedSet[Var] = MutSortedSet.empty private val literalPatterns: MutSortedMap[Lit, Pattern.Literal] = MutSortedMap.empty(literalOrdering) def addAliasVar(alias: Var): Unit = aliasVarSet += alias def withAliasVar(alias: Var): Scrutinee = { addAliasVar(alias); this } def aliasesIterator: Iterator[Var] = aliasVarSet.iterator /** * If there is already a `Pattern.ClassLike` for the given symbol, return it. * Otherwise, create a new `Pattern.ClassLike` and return it. */ def getOrCreateClassPattern(classLikeSymbol: ClassLikeSymbol, refined: Bool): Pattern.ClassLike = classLikePatterns.getOrElseUpdate(classLikeSymbol, Pattern.ClassLike(classLikeSymbol, this)(refined)) /** * Get the class pattern but DO NOT create a new one if there isn't. This * function is mainly used in post-processing because we don't want to * accidentally create new patterns. */ def getClassPattern(classLikeSymbol: TypeSymbol): Opt[Pattern.ClassLike] = classLikePatterns.get(classLikeSymbol) /** * If there is already a `Pattern.Tuple`, return it. Otherwise, create a * new `Pattern.Tuple` and return it. * * **NOTE**: There's only one slot for tuple patterns because we cannot * differentiate tuple types in underlying MLscript case terms. In the future, * we will support complex tuple patterns, so we need to extend this method to * a signature like this. * * ```scala * def getOrCreateTuplePattern(dimension: TupleDimension): Pattern.Tuple * case class TupleDimension(knownArity: Int, hasSplice: Bool) * ``` */ def getOrCreateTuplePattern: Pattern.Tuple = tuplePatternOpt.getOrElse { val tuplePattern = Pattern.Tuple(this) tuplePatternOpt = S(tuplePattern) tuplePattern } /** Get the tuple pattern and create a new one if there isn't. */ def getOrCreateLiteralPattern(literal: Lit): Pattern.Literal = literalPatterns.getOrElseUpdate(literal, Pattern.Literal(literal)) def classLikePatternsIterator: Iterator[Pattern.ClassLike] = classLikePatterns.valuesIterator def patternsIterator: Iterator[Pattern] = classLikePatterns.valuesIterator ++ literalPatterns.valuesIterator /** Get a list of string representation of patterns. Only for debugging. */ private[ucs] def showPatternsDbg: Str = { val classLikePatternsStr = classLikePatterns.iterator.map { case (symbol, pattern) => s"${symbol.name}(${pattern.arity.fold("?")(_.toString)})" } val tuplePatternStr = tuplePatternOpt.iterator.map { tuplePattern => s"tuple(${tuplePattern.arity.fold("?")(_.toString)})" } (classLikePatternsStr ++ tuplePatternStr).mkString(", ") } def freshSubScrutinee: Scrutinee = context.freshScrutinee(this) def getReadableName(scrutineeVar: Var): Str = { parent match { case N if context.isGeneratedVar(scrutineeVar) => "term" case N => s"`${scrutineeVar.name}`" case S(parentScrutinee) => parentScrutinee.classLikePatterns.iterator.flatMap { case (symbol, pattern) => pattern.findSubScrutinee(this).map(_ -> symbol.name) }.nextOption() match { case S(index -> typeName) => s"the ${index.toOrdinalWord} argument of `${typeName}`" case N => s"`${scrutineeVar.name}`" // Still not the best. } } } @tailrec final def isSubScrutineeOf(scrutinee: Scrutinee): Bool = this === scrutinee || (parent match { case Some(parentScrutinee) => parentScrutinee.isSubScrutineeOf(scrutinee) case N => false }) } object Scrutinee { // We might need to move these method to a private `VarOps` because they may // emit diagnostics. def unapply(term: Term)(implicit context: Context): Opt[Scrutinee] = term match { case v: Var => v.symbol match { case symbol: TermSymbol => symbol.getScrutinee case _ => N } case _ => N } type WithVar = Var -> Scrutinee object WithVar { def unapply(term: Term)(implicit context: Context): Opt[WithVar] = term match { case v @ Var(_) => v.symbol match { case symbol: TermSymbol => symbol.getScrutinee.map(v -> _) case _ => N } case _ => N } } private def literalInternalOrder(literal: Lit): Int = literal match { case UnitLit(true) => 0 case UnitLit(false) => 1 case _: DecLit => 2 case _: IntLit => 3 case _: StrLit => 4 } private implicit val classLikeSymbolOrdering: Ordering[TypeSymbol] = new Ordering[TypeSymbol] { override def compare(x: TypeSymbol, y: TypeSymbol): Int = x.name.compareTo(y.name) } private implicit val literalOrdering: Ordering[Lit] = new Ordering[Lit] { override def compare(x: Lit, y: Lit): Int = (x, y) match { case (DecLit(x), DecLit(y)) => x.compare(y) case (IntLit(x), IntLit(y)) => x.compare(y) case (StrLit(x), StrLit(y)) => x.compare(y) case _ => literalInternalOrder(x).compare(literalInternalOrder(y)) } } private implicit val varNameOrdering: Ordering[Var] = new Ordering[Var] { override def compare(x: Var, y: Var): Int = x.name.compareTo(y.name) } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/display.scala ================================================ package mlscript package ucs import syntax.{core => c, source => s} import context.{Context} import pretyper.symbol.{TermSymbol, TypeSymbol} import utils._, shorthands._ /** All the pretty-printing stuff go here. */ package object display { /** If the variable has a location, mark it with an asterisk. * If the variable is associated with a term symbol, mark it with an asterisk. * If the variable is associated with a term symbol and has a scrutinee, mark it with a double dagger. */ private def showVar(`var`: Var)(implicit context: Context): String = { val postfix = `var`.symbolOption match { case S(symbol: TermSymbol) if symbol.isScrutinee => "‡" case S(_: TermSymbol) => "†" case S(_: TypeSymbol) => "◊" case N => "" } `var`.name + (`var`.symbolOption.fold("")(_ => "*")) + postfix } def showSplit(split: s.TermSplit)(implicit context: Context): Str = { def termSplit(split: s.TermSplit, isFirst: Bool, isAfterAnd: Bool): Lines = split match { case s.Split.Cons(head, tail) => (termBranch(head) match { case (n, line) :: tail => (n, (if (isAfterAnd) "" else "and ") + s"$line") :: tail case Nil => Nil }) ::: termSplit(tail, false, isAfterAnd) case s.Split.Let(_, nme, rhs, tail) => (0, s"let ${nme.name} = ${rhs.showDbg}") :: termSplit(tail, false, isAfterAnd) case s.Split.Else(term) => (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil case s.Split.Nil => Nil } def termBranch(branch: s.TermBranch): Lines = branch match { case s.TermBranch.Boolean(test, continuation) => s"${test.showDbg}" #: termSplit(continuation, true, false) case s.TermBranch.Match(scrutinee, continuation) => s"${scrutinee.showDbg} is" #: patternSplit(continuation) case s.TermBranch.Left(left, continuation) => s"${left.showDbg}" #: operatorSplit(continuation) } def patternSplit(split: s.PatternSplit): Lines = split match { case s.Split.Cons(head, tail) => patternBranch(head) ::: patternSplit(tail) case s.Split.Let(rec, nme, rhs, tail) => (0, s"let ${nme.name} = ${rhs.showDbg}") :: patternSplit(tail) case s.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil case s.Split.Nil => Nil } def operatorSplit(split: s.OperatorSplit): Lines = split match { case s.Split.Cons(head, tail) => operatorBranch(head) ::: operatorSplit(tail) case s.Split.Let(rec, nme, rhs, tail) => (0, s"let ${nme.name} = ${rhs.showDbg}") :: operatorSplit(tail) case s.Split.Else(term) => (0, s"else ${term.showDbg}") :: Nil case s.Split.Nil => Nil } def operatorBranch(branch: s.OperatorBranch): Lines = s"${branch.operator.name}" #: (branch match { case s.OperatorBranch.Match(_, continuation) => patternSplit(continuation) case s.OperatorBranch.Binary(_, continuation) => termSplit(continuation, true, true) }) def patternBranch(branch: s.PatternBranch): Lines = { val s.PatternBranch(pattern, continuation) = branch termSplit(continuation, true, false) match { case (0, line) :: lines => (0, s"$pattern $line") :: lines case lines => (0, pattern.toString) :: lines } } ("if" #: termSplit(split, true, true)).toIndentedString } @inline def showSplit(s: c.Split, showFirstLevel: Bool = false)(implicit context: Context): Str = showSplit("if", s, showFirstLevel) def showSplit(prefix: Str, s: c.Split, showFirstLevel: Bool)(implicit context: Context): Str = { def split(s: c.Split, isFirst: Bool, isTopLevel: Bool): Lines = s match { case c.Split.Cons(head, tail) => (branch(head, isTopLevel) match { case (n, line) :: tail => (n, (if (isTopLevel) "" else "and ") + (if (s.isFallback) "?" else "") + line) :: tail case Nil => Nil }) ::: split(tail, false, isTopLevel) case c.Split.Let(_, nme, rhs, tail) => (0, s"let ${showVar(nme)} = ${rhs.showDbg}") :: split(tail, false, isTopLevel) case c.Split.Else(term) => (if (isFirst) (0, s"then ${term.showDbg}") else (0, s"else ${term.showDbg}")) :: Nil case c.Split.Nil => Nil } def branch(b: c.Branch, isTopLevel: Bool): Lines = { val c.Branch(scrutinee, pattern, continuation) = b if (showFirstLevel) { val continuation = b.continuation match { case c.Split.Nil => "empty" case c.Split.Else(_) => "then ..." case _ => "and ..." } (0, s"${showVar(scrutinee)} is $pattern " + continuation) :: Nil } else s"${showVar(scrutinee)} is $pattern" #: split(continuation, true, isTopLevel) } val lines = split(s, true, true) (if (prefix.isEmpty) lines else prefix #: lines).toIndentedString } /** * Convert a normalized term to a string with indentation. It makes use of * some special markers. * * - `*` if the variable has a location. * - `†` if the variable is associated with a term symbol. * - `‡` if the variable is associated with a term symbol and has a scrutinee. */ def showNormalizedTerm(term: Term)(implicit context: Context): String = { def showTerm(term: Term): Lines = term.desugaredTerm match { case N => term match { case let: Let => showLet(let) case caseOf: CaseOf => showCaseOf(caseOf) case other => (0, other.showDbg) :: Nil } case S(desugaredTerm) => "[desugared]" @: showTerm(desugaredTerm) } def showScrutinee(term: Term): Str = term match { case vari: Var => showVar(vari) case _ => term.showDbg } def showPattern(pat: SimpleTerm): Str = pat match { case vari: Var => showVar(vari) case _ => pat.showDbg } def showCaseOf(caseOf: CaseOf): Lines = { val CaseOf(trm, cases) = caseOf s"case ${showScrutinee(trm)} of" ##: showCaseBranches(cases) } def showCaseBranches(caseBranches: CaseBranches): Lines = caseBranches match { case k @ Case(pat, rhs, tail) => (s"${if (k.refined) "refined " else ""}${showPattern(pat)} ->" @: showTerm(rhs)) ++ showCaseBranches(tail) case Wildcard(term) => s"_ ->" @: showTerm(term) case NoCases => Nil } def showLet(let: Let): Lines = { val Let(rec, nme, rhs, body) = let (0, s"let ${showVar(nme)} = ${rhs.showDbg}") :: showTerm(body) } showTerm(term).toIndentedString } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/package.scala ================================================ package mlscript import scala.annotation.tailrec import utils._, shorthands._ package object ucs { class VariableGenerator(prefix: Str) { private var nextIndex = 0 def apply(): Var = { val thisIndex = nextIndex nextIndex += 1 Var(s"$prefix$thisIndex") } def reset(): Unit = nextIndex = 0 } type Lines = Ls[(Int, Str)] implicit class LinesOps(private val lines: Lines) extends AnyVal { /** Increase the indentation of all lines by one. */ def indent: Lines = lines.map { case (n, line) => (n + 1, line) } /** * Prepend a new line and indent the remaining lines. When you want to add * a "title" to several lines and indent them, you should use this function. * * Suppose we have the following `Lines` representing case branches. * ``` * A -> 0 * B -> 0 * ``` * We can prepend string `case x of` to lines and get the following result. * ``` * case x of * A -> 0 * B -> 0 * ``` */ def ##:(prefix: Str): Lines = (0, prefix) :: lines.indent /** * If the first line does not have indentation and the remaining lines are * indented, prepend the given string to the first line. Otherwise, prepend * the given string to the first line and indent all remaining lines. * * When you want to amend the title of lines, you should use this function. */ def #:(prefix: Str): Lines = { lines match { case (0, line) :: lines if lines.forall(_._1 > 0) => (0, s"$prefix $line") :: lines case lines => (0, prefix) :: lines.indent } } /** * If there is only one line, prepend the given string to the beginning of * this line. Otherwise, use the given string as the first line and indent * the remaining lines. * * Similar to `##:`, except this function does not indent if there is only * one line. */ def @:(prefix: Str): Lines = lines match { case (_, line) :: Nil => (0, prefix + " " + line) :: Nil case lines => (0, prefix) :: lines.indent } /** Make a multi-line string. */ def toIndentedString: Str = lines.iterator.map { case (n, line) => " " * n + line }.mkString("\n") } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/CoverageChecking.scala ================================================ package mlscript package ucs package stages import utils._, shorthands._, Message.MessageContext import context.{Context, Pattern, Scrutinee} import pretyper.Traceable, pretyper.symbol._ trait CoverageChecking { self: Desugarer with Traceable => import CoverageChecking._ def checkCoverage(term: Term)(implicit context: Context): Ls[Diagnostic] = { // Collect an immutable map from scrutinees to patterns. val registry: ScrutineePatternSetMap = context.scrutinees.flatMap { scrutinee => val caseSet = CaseSet(scrutinee.patternsIterator.toList) scrutinee.aliasesIterator.map(alias => (alias -> scrutinee) -> caseSet) }.toMap println("collected match registry: " + showRegistry(registry)) checkCoverage(term, Map.empty, registry, Map.empty) } private def checkCoverage( term: Term, pending: ScrutineePatternSetMap, working: ScrutineePatternSetMap, seen: SeenRegistry )(implicit context: Context): Ls[Diagnostic] = { term match { case Let(_, nme, _, body) => println(s"checkCoverage <== LET `${nme.name}`") checkCoverage(body, pending, working, seen) case CaseOf(scrutineeVar: Var, Case(Var("true"), body, NoCases)) if context.isTestVar(scrutineeVar) => raiseDesugaringError(msg"missing else branch" -> body.toLoc) Nil case CaseOf(scrutineeVar: Var, Case(Var("true"), whenTrue, Wildcard(whenFalse))) if context.isTestVar(scrutineeVar) => println(s"checkCoverage <== TEST `${scrutineeVar.name}`") checkCoverage(whenTrue, pending, working, seen) ++ checkCoverage(whenFalse, pending, working, seen) case CaseOf(Scrutinee.WithVar(scrutineeVar, scrutinee), cases) => trace(s"checkCoverage <== ${pending.size} pending, ${working.size} working, ${seen.size} seen") { println(s"CASE ${scrutineeVar.name}") println(s"SEEN: ${seen.showDbg}") // If the scrutinee is still pending (i.e., not matched yet), then we // remove it from the pending list. If the scrutinee is matched, and // there are still classes to be matched, then we find the remaining // classes from the working list. If neither of the above is true, // there are two possible cases: // 1. The scrutinee has been never visited, which is an error. // 2. It has been matched to be an instance of some class. Therefore, // we need to check if this is a contradiction. val namedScrutinee = scrutineeVar -> scrutinee val (unseenPatterns, newPending) = pending.get(namedScrutinee) match { case S(matchedClasses) => matchedClasses -> (pending - namedScrutinee) case N => working.get(namedScrutinee) match { case S(unseenPatterns) => unseenPatterns -> pending case N => // Neither in the working list nor in the pending list. seen.get(namedScrutinee) match { // The scrutine may have been matched. case S((_, _, remainingPatterns)) => remainingPatterns -> pending case N => // The scrutinee has never been visited. This should not happen. println("working list: " + showRegistry(working)) throw new Exception(s"Scrutinee ${scrutineeVar.name} is not in the working list.") } } } // We go through cases and keep removing the current pattern from the // unseen pattern set. // Meanwhile, we keep adding diagnostics if we meet warnings and errors. cases.foldLeft((unseenPatterns, Nil: Ls[Diagnostic]))({ case ((unseenPatterns, diagnostics), (boolLit: Var) -> body) if boolLit.name === "true" || boolLit.name === "false" => ( unseenPatterns.remove(boolLit), diagnostics ++ checkCoverage(body, newPending, working - namedScrutinee, seen) ) case ((unseenPatterns, diagnostics), (className: Var) -> body) => val classSymbol = className.getClassLikeSymbol.getOrElse { throw new Exception(s"$className is not associated with a type symbol") } println(s"class symbol: `${classSymbol.name}`") unseenPatterns.split(classSymbol) match { case S((pattern, refiningPatterns, remainingPatterns)) => println(s"REMOVE `${className.name}` from working") println(s"unseen: ${unseenPatterns.showInDiagnostics}") println(s"remaining: ${remainingPatterns.showInDiagnostics}") // Remove the scrutinee from the working list. val newWorking = if (remainingPatterns.isEmpty) working - namedScrutinee else working.updated(namedScrutinee, remainingPatterns) // Add "`scrutinee` is `className`" to the seen registry. val newSeen = seen + (namedScrutinee -> (classSymbol, pattern.locations, refiningPatterns)) ( remainingPatterns, diagnostics ++ checkCoverage(body, newPending, newWorking, newSeen) ) case N => println(s"cannot split the set by ${classSymbol.name}") unseenPatterns -> (diagnostics :+ (seen.get(namedScrutinee) match { case S((`classSymbol`, _, _)) => WarningReport("tautology", Nil, Diagnostic.Desugaring) case S(_) => ErrorReport("contradiction", Nil, Diagnostic.Desugaring) case N => ErrorReport("unvisited scrutinee", Nil, Diagnostic.Desugaring) })) } case ((unseenPatterns, diagnostics), (literal: Lit) -> body) => ( unseenPatterns.remove(literal), diagnostics ++ checkCoverage(body, newPending, working - namedScrutinee, seen) ) }) { case ((missingCases, diagnostics), N) => println(s"remaining cases are not covered: ${missingCases.showInDiagnostics}") diagnostics ++ explainMissingCases(namedScrutinee, seen, missingCases) case ((remainingCases, diagnostics), S(default)) => println("remaining cases should be covered by the wildcard") checkCoverage(default, newPending, working.updated(namedScrutinee, remainingCases), seen) } }(ls => s"checkCoverage ==> ${ls.length} diagnostics") case other => println(s"checkCoverage <== TERM ${other.showDbg}") Nil } } } object CoverageChecking { /** Create an `ErrorReport` that explains missing cases. */ private def explainMissingCases( scrutinee: Scrutinee.WithVar, seen: SeenRegistry, missingCases: CaseSet )(implicit context: Context): Opt[ErrorReport] = if (missingCases.isEmpty) { N } else { S(ErrorReport({ val readableName = scrutinee._2.getReadableName(scrutinee._1) val lines = (msg"$readableName has ${"missing case".pluralize(missingCases.size, true)}" -> scrutinee._1.toLoc) :: (missingCases.patterns.iterator.flatMap { pattern => (msg"it can be ${pattern.showInDiagnostics}" -> pattern.firstOccurrence) :: Nil }.toList) if (seen.isEmpty) { lines } else { seen.iterator.zipWithIndex.map { case ((scrutineeVar -> scrutinee, (classSymbol, locations, cases)), i) => val prologue = if (i === 0) "when " else "" val epilogue = if (seen.size === 1) "" else if (i === seen.size - 2) ", and" else "," val scrutineeName = scrutinee.getReadableName(scrutineeVar) msg"$prologue$scrutineeName is `${classSymbol.name}`$epilogue" -> locations.headOption }.toList ::: lines } }, true, Diagnostic.Desugaring)) } /** A helper function that prints entries from the given registry line by line. */ private def showRegistry(registry: ScrutineePatternSetMap): Str = if (registry.isEmpty) "empty" else registry.iterator.map { case (scrutineeVar -> scrutinee, matchedClasses) => matchedClasses.patterns.iterator.map(_.showInDiagnostics).mkString(s">>> ${scrutineeVar.name} => [", ", ", "]") }.mkString("\n", "\n", "") type ScrutineePatternSetMap = Map[Scrutinee.WithVar, CaseSet] type SeenRegistry = Map[Scrutinee.WithVar, (TypeSymbol, Ls[Loc], CaseSet)] implicit class SeenRegistryOps(val self: SeenRegistry) extends AnyVal { def showDbg: Str = if (self.isEmpty) "empty" else self.iterator.map { case ((k, _), (s, _, _)) => s"${k.name} is ${s.name}" }.mkString(", ") } /** * A `CaseSet` represents all patterns that a particular scrutinee is * being matched with within a UCS expression. * * @param patterns a list of patterns. */ final case class CaseSet(val patterns: List[Pattern]) { def showInDiagnostics: Str = patterns.iterator.map(_.showInDiagnostics).mkString("[", ", ", "]") /** Get a iterator of all class-like patterns. */ def classLikePatterns: Iterator[Pattern.ClassLike] = patterns.iterator.flatMap { case pattern: Pattern.ClassLike => S(pattern) case _: Pattern.Literal | _: Pattern.Tuple => N } /** Separate a class-like pattern if it appears in `patterns`. */ def separate(classLikeSymbol: ClassLikeSymbol): Opt[(Pattern.ClassLike, Ls[Pattern.ClassLike])] = { classLikePatterns.foldRight[(Opt[Pattern.ClassLike], Ls[Pattern.ClassLike])]((N, Nil)) { case (pattern, (S(separated), rest)) => (S(separated), pattern :: rest) case (pattern, (N, rest)) if pattern.classLikeSymbol === classLikeSymbol => (S(pattern), rest) case (pattern, (N, rest)) => (N, pattern :: rest) } match { case (N, _) => N case (S(separated), rest) => S((separated, rest)) } } /** * Split the pattern set into two pattern sets. * * For example, let A be the base type of B, C, and D. Plus, class `Z` is * unrelated to any of them. Suppose the initial pattern set is * `{ A, B, C, Z }`. Splitting the set results in two sets, one set * contains classes that are compatible with `A`, and the other set * contains classes that are unrelated to `A`. * * For example, if we split the set with `A`, then we get `{ B, C }` and * set `{ Z }`. Set `{ B, C }` represents that the scrutinee can be further * refined to class `B` or `class C`. Set `{ Z }` represents that if the * scrutinee is not `A`, then it can be `Z`. * * @param classLikeSymbol the type symbol represents the class like type * @return If the pattern set doesn't include the given type symbol, this * returns `None`. Otherwise, the function returns a triplet of the * locations where the pattern appears, the related patterns, and * unrelated patterns. */ def split(classLikeSymbol: ClassLikeSymbol): Opt[(Pattern.ClassLike, CaseSet, CaseSet)] = { def mk(pattern: Pattern): Opt[Lit \/ TypeSymbol] = pattern match { case Pattern.ClassLike(classLikeSymbol, _) => S(R(classLikeSymbol)) case Pattern.Literal(literal) => S(L(literal)) case _ => N } separate(classLikeSymbol) match { case N => N case S((pattern, patterns)) => val (unrelated, related) = patterns.partitionMap { otherPattern => if (compareCasePattern(mk(otherPattern), mk(pattern))) { R(otherPattern) } else { L(otherPattern) } } S((pattern, CaseSet(related), CaseSet(unrelated))) } } @inline def remove(boolLit: Var): CaseSet = { require(boolLit.name === "true" || boolLit.name === "false") CaseSet(patterns.filter(!_.matches(boolLit))) } @inline def remove(literal: Lit): CaseSet = CaseSet(patterns.filter(!_.matches(literal))) @inline def isEmpty: Bool = patterns.isEmpty @inline def size: Int = patterns.size } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/Desugaring.scala ================================================ package mlscript package ucs package stages import syntax.{core => c, source => s} import context.{Context, Scrutinee} import utils._, shorthands._, Message.MessageContext import pretyper.symbol._, pretyper.{PreTyper, Scope} /** * The desugaring stage of UCS. In this stage, we transform the source abstract * syntax into core abstract syntax, which is more concise. We will make * following changes during this stage: * * 1. Flatten nested patterns and generated bindings for nameless scrutinees * and parameters of class-like patterns. * 2. Desugar variable patterns to plain let bindings. * 3. Desugar literal patterns to equivalent boolean expressions. * 4. Reassemble partial terms that are broken by "conditional splits". * 5. Associate each scrutinee with a unique `ScrutineeData`. * * Desugared UCS terms (core abstract syntax) are in the form of `Split`, which * is a list of branches. Each branch consists of a scrutinee, a pattern, and a * continuation. */ trait Desugaring { self: PreTyper => import Desugaring._ /** * The entry point of the desugaring stage. * * @param term the root of source abstrax syntax term, obtained from the * transformation stage * @param scope the scope is for resolving type symbols. The scope should * contains a TypeSymbol for `true` literal. * @return the root of desugared core abstract syntax term */ @inline def desugar(term: s.TermSplit)(implicit scope: Scope, context: Context): c.Split = desugarTermSplit(term)(PartialTerm.Empty, scope, context) /** * Coin a fresh name for a destructed parameter. The name consists of three * parts: the name of the parent scrutinee, the name of matched class, and * the index of the parameter. For example, if variable `x` is matched as * `Cons(hd, tl)`, then the name of `hd` will be `x$Cons_0` and the name of * `tl` will be `x$Cons_1`. */ private def freshSubScrutineeVar(parentScrutinee: Var, parentClassName: Str, index: Int): Var = Var(s"${parentScrutinee.name}$$${parentClassName}_${index.toString}") /** * Coin a fresh name for the result of `unapply` method. The name begins with * `args_`, followed by the name of the scrutinee, and finally ends with the * name of the matched class. For example, if variable `x` is matched as * `Cons(hd, tl)`, then the name of `Cons.unapply(x)` will be `args_x$Cons`. * Parameters `hd` and `tl` are obtained by selecting `.1` and `.2` from * `args_x$Cons`. */ private def makeUnappliedVar(scrutinee: Var, className: Var)(implicit context: Context): Var = Var(s"${context.unappliedPrefix}${scrutinee.name}$$${className.name}") /** * A shorthand for making a true pattern, which is useful in desugaring * Boolean conditions. */ private def truePattern(implicit scope: Scope, context: Context) = { val className = Var("true") val classSymbol = className.resolveClassLikeSymbol c.Pattern.Class(className, classSymbol, false) } private def falsePattern(implicit scope: Scope, context: Context) = { val className = Var("false") val classSymbol = className.resolveClassLikeSymbol c.Pattern.Class(className, classSymbol, false) } private def desugarTermSplit(split: s.TermSplit)(implicit termPart: PartialTerm.Incomplete, scope: Scope, context: Context): c.Split = split match { case s.Split.Cons(head, tail) => desugarTermBranch(head) ++ desugarTermSplit(tail) case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarTermSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol, context)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default); case s.Split.Nil => c.Split.Nil } // This function does not need to can `withCachedTermPart` because all branches assume that // `termPart` is either empty or waiting for an RHS. private def desugarTermBranch(branch: s.TermBranch)(implicit termPart: PartialTerm.Incomplete, scope: Scope, context: Context): c.Split = trace(s"desugarTermBranch <== $termPart") { branch match { case s.TermBranch.Boolean(testPart, continuation) => val test = context.freshTest().withFreshSymbol c.Split.Let( rec = false, name = test, term = Asc(termPart.addTerm(testPart).get, TypeName("Bool")), tail = c.Branch(test, truePattern, desugarTermSplit(continuation)(PartialTerm.Empty, scope + test.symbol, context)) :: c.Split.Nil ) case s.TermBranch.Match(scrutinee, split) => desugarPatternSplit(termPart.addTerm(scrutinee).get, split) case s.TermBranch.Left(left, continuation) => desugarOperatorSplit(continuation)(termPart.addTerm(left), scope, context) } }() private def withCachedTermPart[B <: s.Branch](desugar: (PartialTerm.Total, Scope) => c.Split)(implicit termPart: PartialTerm.Total, scope: Scope, context: Context): c.Split = termPart.get match { case v: Var => desugar(termPart, scope) // No need to cache variables. case rhs => val cache = context.freshCache().withFreshSymbol c.Split.Let(false, cache, rhs, desugar(PartialTerm.Total(cache, Nil), scope + cache.symbol)) } private def desugarOperatorSplit(split: s.OperatorSplit)(implicit termPart: PartialTerm.Total, scope: Scope, context: Context): c.Split = withCachedTermPart { (termPart, scope) => split match { case s.Split.Cons(head, tail) => desugarOperatorBranch(head)(termPart, scope, context) ++ desugarOperatorSplit(tail)(termPart, scope, context) case s.Split.Let(rec, nme, rhs, tail) => c.Split.Let(rec, nme, rhs, desugarOperatorSplit(tail)(termPart, scope + nme.withFreshSymbol.symbol, context)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil }} private def desugarOperatorBranch(branch: s.OperatorBranch)(implicit termPart: PartialTerm.Total, scope: Scope, context: Context): c.Split = trace(s"desugarOperatorBranch <== $termPart") { branch match { case s.OperatorBranch.Binary(op, split) => desugarTermSplit(split)(termPart.addOp(op), scope, context) case s.OperatorBranch.Match(_, split) => desugarPatternSplit(termPart.get, split)(scope, context) } }() private def makeLiteralTest(scrutinee: Var, literal: Lit)(implicit scope: Scope): c.Split => c.Split = next => c.Branch(scrutinee, c.Pattern.Literal(literal), next) :: c.Split.Nil private def flattenClassParameters( parentScrutineeVar: Var, parentScrutinee: Scrutinee, parentClassLikeSymbol: ClassLikeSymbol, parentRefined: Bool, parameters: Ls[s.Pattern], )(implicit context: Context): Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]] = { // Make it `lazy` so that it will not be created if all fields are wildcards. lazy val classPattern = parentScrutinee.getOrCreateClassPattern(parentClassLikeSymbol, parentRefined) def flattenPattern( parameterPattern: s.Pattern, index: Int, subScrutineeVarOpt: Opt[(Var, Scrutinee)], aliasVars: Ls[Var], ): Opt[(Var, Opt[s.Pattern], Ls[Var])] = { // scrutineeVar, subPattern, aliasVars lazy val (subScrutineeVar, subScrutinee) = subScrutineeVarOpt.getOrElse { val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, parentClassLikeSymbol.name, index) val symbol = new LocalTermSymbol(subScrutineeVar) val subScrutinee = classPattern.getParameter(index).withAliasVar(subScrutineeVar.withSymbol(symbol)) symbol.addScrutinee(subScrutinee) (subScrutineeVar, subScrutinee) } parameterPattern match { case _: s.EmptyPattern => N case s.NamePattern(name) => val subScrutinee = classPattern.getParameter(index).withAliasVar(name) S((name.withFreshSymbol.withScrutinee(subScrutinee), N, aliasVars.reverse)) case parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)) => S((subScrutineeVar, S(parameterPattern), aliasVars.reverse)) case parameterPattern @ s.AliasPattern(aliasVar, innerPattern) => println(s"alias pattern found ${subScrutineeVar.name} -> ${aliasVar.name}") flattenPattern(innerPattern, index, S((subScrutineeVar, subScrutinee)), aliasVar.withFreshSymbol.withScrutinee(subScrutinee) :: aliasVars) case pattern => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) N } } trace(s"flattenClassParameters <== ${parentScrutineeVar.name} is ${parentClassLikeSymbol.name}") { parameters.iterator.zipWithIndex.map { case (pattern, index) => flattenPattern(pattern, index, N, Nil) }.toList }(r => s"flattenClassParameters ==> ${r.mkString(", ")}") } /** * Recursively decompose and flatten a possibly nested class pattern. Any * user-declared and generated variables will be added to the given scope and * a augmented scope will be returned. Meanwhile, it produces a function that * wrap a split with all bindings and matches. * * This function involves some higher-order function's compose operation, so * it is somewhat convoluted to read. However, this is a necessary * complication, as we need information from the scope when generating * variable names. * * @param pattern the class pattern * @param scrutinee the scrutinee of the pattern. The caller should make sure * that the scrutinee is associated with a symbol in the * given scope. * @param initialScope the scope before flattening the class pattern * @return a tuple of the augmented scope and a function that wrap a split */ private def desugarClassPattern( pattern: s.ClassPattern, scrutineeVar: Var, initialScope: Scope, refined: Bool )(implicit context: Context): (Scope, c.Split => c.Branch) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAliasVar(scrutineeVar) val patternClassSymbol = pattern.nme.resolveClassLikeSymbol(initialScope, context) val classPattern = scrutinee.getOrCreateClassPattern(patternClassSymbol, refined) println(s"desugarClassPattern: ${scrutineeVar.name} is ${pattern.nme.name}") classPattern.addLocation(pattern.nme) val (scopeWithAll, bindAll) = pattern.parameters match { case S(parameters) => // Before processing sub-patterns, we need to generate a variable that // holds the result of `unapply` method. Such variable might have been // generated by a previous branches. We MUST reuse so that we can merge // duplicated bindings during normalization. lazy val unapp = classPattern.getUnappliedVar { val vari = makeUnappliedVar(scrutineeVar, pattern.nme) vari.withSymbol(new LocalTermSymbol(vari)) } val nestedPatterns = flattenClassParameters(scrutineeVar, scrutinee, patternClassSymbol, refined, parameters) println(s"nestedPatterns = $nestedPatterns") // First, handle bindings of parameters of the current class pattern. val identity = (split: c.Split) => split val bindParameters = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextParameter) => bindNextParameter case ((S((parameter, _, _)), index), bindNextParameter) => bindNextParameter.andThen { c.Split.Let(false, parameter, Sel(unapp, Var(index.toString)), _) } } println(s"bindParameters === identity: ${bindParameters === identity}") val bindAll = if (bindParameters === identity) bindParameters else bindParameters.andThen { c.Split.Let(false, unapp, makeUnapplyCall(scrutineeVar, pattern.nme), _): c.Split } val scopeWithClassParameters = initialScope ++ (unapp.symbol :: nestedPatterns.flatMap(_.map(_._1.symbol))) desugarNestedPatterns(nestedPatterns, scopeWithClassParameters, bindAll) // If there is no parameter, then we are done. case N => (initialScope, identity(_: c.Split)) } println(s"${scrutineeVar.name}: ${scrutinee.showPatternsDbg}") // Last, return the scope with all bindings and a function that adds all matches and bindings to a split. (scopeWithAll, split => c.Branch(scrutineeVar, c.Pattern.Class(pattern.nme, patternClassSymbol, refined), bindAll(split))) } /** * This function collects bindings from nested patterns and accumulate a * function that add let bindings to a split (we call such function a * "binder"). This function is supposed to be called from pattern desugaring * functions. * * @param nestedPatterns nested patterns are a list of sub-scrutinees and * corresponding sub-patterns * @param scopeWithScrutinees a scope with all sub-scrutinees * @param bindScrutinees a function that adds all bindings to a split */ private def desugarNestedPatterns( nestedPatterns: Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]], scopeWithScrutinees: Scope, bindScrutinees: c.Split => c.Split )(implicit context: Context): (Scope, c.Split => c.Split) = trace("desugarNestedPatterns") { nestedPatterns.foldLeft((scopeWithScrutinees, bindScrutinees)) { // If this parameter is empty (e.g. produced by wildcard), then we do // nothing and pass on scope and binder. case (acc, N) => acc // If this sub-pattern is a class pattern, we need to recursively flatten // the class pattern. We will get a scope with all bindings and a function // that adds all bindings to a split. The scope can be passed on to the // next sub-pattern. The binder needs to be composed with the previous // binder. case (acc @ (scope, bindPrevious), S((nme, patternOpt, aliasVars))) => println(s"subScrut = ${nme.name}; aliasVars = ${aliasVars.iterator.map(_.name).mkString("[", ", ", "]")}") val bindAliasVars = aliasVars.foldRight[c.Split => c.Split](identity[c.Split]) { case (aliasVar, bindNext) => (inner: c.Split) => c.Split.Let(false, aliasVar, nme, bindNext(inner)) } patternOpt match { // If this parameter is not matched with a sub-pattern, then we do // nothing and pass on scope and binder. case N => (scope, bindAliasVars.andThen(bindPrevious)) case S(pattern) => pattern match { case pattern: s.ClassPattern => println(s"${nme.name} is ${pattern.nme.name}") val (scopeWithNestedAll, bindNestedAll) = desugarClassPattern(pattern, nme, scope, pattern.refined) (scopeWithNestedAll, split => bindPrevious(bindNestedAll(bindAliasVars(split)) :: c.Split.Nil)) case pattern @ s.ConcretePattern(Var("true") | Var("false")) => println(s"${nme.name} is ${pattern.nme.name}") val classSymbol = pattern.nme.resolveClassLikeSymbol(scope, context) (scope, split => bindPrevious(c.Branch(nme, c.Pattern.Class(pattern.nme, classSymbol, false), bindAliasVars(split)) :: c.Split.Nil)) case s.LiteralPattern(literal) => nme.getOrCreateScrutinee .withAliasVar(nme) .getOrCreateLiteralPattern(literal) .addLocation(literal) (scope, bindAliasVars.andThen(makeLiteralTest(nme, literal)(scope)).andThen(bindPrevious)) case s.TuplePattern(fields) => val (scopeWithNestedAll, bindNestedAll) = desugarTuplePattern(fields, nme, scope) (scopeWithNestedAll, bindAliasVars.andThen(bindNestedAll).andThen(bindPrevious)) // Well, other patterns are not supported yet. case _ => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) acc } } } }() private def flattenTupleFields( parentScrutineeVar: Var, parentScrutinee: Scrutinee, fields: Ls[s.Pattern] )(implicit context: Context): Ls[Opt[(Var, Opt[s.Pattern], Ls[Var])]] = { // Make it `lazy` so that it will not be created if all fields are wildcards. lazy val tuplePattern = parentScrutinee.getOrCreateTuplePattern lazy val tupleDummyClassName = s"Tuple$$${fields.length}" def flattenPattern( pattern: s.Pattern, index: Int, subScrutineeVarOpt: Opt[(Var, Scrutinee)], aliasVars: Ls[Var], ): Opt[(Var, Opt[s.Pattern], Ls[Var])] = { lazy val (subScrutineeVar, subScrutinee) = subScrutineeVarOpt.getOrElse { val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, tupleDummyClassName, index) val symbol = new LocalTermSymbol(subScrutineeVar) val subScrutinee = tuplePattern.getField(index).withAliasVar(subScrutineeVar.withSymbol(symbol)) symbol.addScrutinee(subScrutinee) (subScrutineeVar, subScrutinee) } pattern match { case _: s.EmptyPattern => N case s.NamePattern(name) => S((name.withFreshSymbol.withScrutinee(tuplePattern.getField(index)), N, aliasVars.reverse)) case parameterPattern @ (s.ClassPattern(_, _, _) | s.LiteralPattern(_) | s.TuplePattern(_)) => val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, tupleDummyClassName, index) val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) S((subScrutineeVar.withSymbol(symbol), S(parameterPattern), aliasVars.reverse)) case parameterPattern @ s.ConcretePattern(nme @ (Var("true") | Var("false"))) => val subScrutineeVar = freshSubScrutineeVar(parentScrutineeVar, tupleDummyClassName, index) val symbol = new LocalTermSymbol(subScrutineeVar) symbol.addScrutinee(tuplePattern.getField(index).withAliasVar(subScrutineeVar)) S((subScrutineeVar.withSymbol(symbol), S(parameterPattern), aliasVars.reverse)) case parameterPattern @ s.AliasPattern(aliasVar, innerPattern) => println(s"alias pattern found ${subScrutineeVar.name} -> ${aliasVar.name}") flattenPattern(innerPattern, index, S((subScrutineeVar, subScrutinee)), aliasVar.withFreshSymbol.withScrutinee(subScrutinee) :: aliasVars) case pattern => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) N } } fields.iterator.zipWithIndex.map { case (pattern, index) => flattenPattern(pattern, index, N, Nil) }.toList } private def desugarTuplePattern(fields: Ls[s.Pattern], scrutineeVar: Var, initialScope: Scope)(implicit context: Context): (Scope, c.Split => c.Split) = { val scrutinee = scrutineeVar.getOrCreateScrutinee.withAliasVar(scrutineeVar) val nestedPatterns = flattenTupleFields(scrutineeVar, scrutinee, fields) val bindFields = nestedPatterns.iterator.zipWithIndex.foldRight[c.Split => c.Split](identity) { case ((N, _), bindNextField) => bindNextField case ((S((parameter, _, _)), index), bindNextField) => val indexVar = Var(index.toString).withLoc(parameter.toLoc) bindNextField.andThen { c.Split.Let(false, parameter, Sel(scrutineeVar, indexVar), _) } } val scopeWithFields = initialScope ++ nestedPatterns.flatMap(_.map(_._1.symbol)) desugarNestedPatterns(nestedPatterns, scopeWithFields, bindFields) } private def desugarPatternSplit( scrutineeTerm: Term, split: s.PatternSplit )(implicit scope: Scope, context: Context): c.Split = { def rec(scrutineeVar: Var, split: s.PatternSplit)(implicit scope: Scope): c.Split = split match { case s.Split.Cons(head, tail) => val scrutineeSymbol = scrutineeVar.getOrResolveTermSymbol val scrutinee = scrutineeSymbol.getOrCreateScrutinee scrutinee.addAliasVar(scrutineeVar) // Some functions that allow me to write less code, this function was // very long before I introduced them. def desugarRight(implicit scope: Scope) = desugarTermSplit(head.continuation)(PartialTerm.Empty, scope, context) def desugarTail(implicit scope: Scope) = rec(scrutineeVar, tail) def desugarPatternBranch(pattern: s.Pattern): c.Split = pattern match { case pattern @ s.AliasPattern(aliasVar, nestedPattern) => scrutinee.addAliasVar(aliasVar.withFreshSymbol) c.Split.Let(false, aliasVar, scrutineeVar, desugarPatternBranch(nestedPattern)) case s.LiteralPattern(literal) => scrutinee.getOrCreateLiteralPattern(literal).addLocation(literal) c.Branch(scrutineeVar, c.Pattern.Literal(literal), desugarRight) :: desugarTail case s.ConcretePattern(nme @ (Var("true") | Var("false"))) => val classSymbol = nme.resolveClassLikeSymbol scrutinee.getOrCreateClassPattern(classSymbol, false).addLocation(nme) c.Branch( scrutinee = scrutineeVar, pattern = c.Pattern.Class(nme, classSymbol, false), continuation = desugarRight ) :: desugarTail case s.ConcretePattern(nme) => val testVar = context.freshTest().withFreshSymbol val testTerm = App(Var("==="), PlainTup(scrutineeVar, nme)) c.Split.Let(false, testVar, testTerm, c.Branch(testVar, truePattern, desugarRight(scope + testVar.symbol)) :: desugarTail) case s.EmptyPattern(_) | s.NamePattern(Var("_")) => desugarRight ++ desugarTail case s.NamePattern(nme) => // If the top-level pattern is a name pattern, we need to check if // `nme` shadows any variables in the scope. For example, code // `fun f(x, y) = if x is y then "yes" else "no"` may read like // `if x === y then "yes" else "no"`. scope.getTermSymbol(nme.name) match { case S(shadowed) => raiseDesugaringWarning( msg"the outer binding `${nme.name}`" -> shadowed.nameVar.toLoc, msg"is shadowed by name pattern `${nme.name}`" -> nme.toLoc ) case N => () } val symbol = freshSymbol(nme).withScrutinee(scrutinee) val scopeWithSymbol = scope + symbol c.Branch(scrutineeVar, c.Pattern.Name(nme.withSymbol(symbol)), desugarRight(scopeWithSymbol)) :: desugarTail(scopeWithSymbol) case pattern @ s.ClassPattern(nme, fields, rfd) => val (scopeWithAll, bindAll) = desugarClassPattern(pattern, scrutineeVar, scope, rfd) bindAll(desugarRight(scopeWithAll)) :: desugarTail case s.TuplePattern(fields) => val (scopeWithAll, bindAll) = desugarTuplePattern(fields, scrutineeVar, scope) bindAll(desugarRight(scopeWithAll)) ++ desugarTail case pattern @ s.RecordPattern(_) => raiseDesugaringError(msg"record pattern is not supported for now" -> pattern.toLoc) desugarTail } desugarPatternBranch(head.pattern) case s.Split.Let(isRec, nme, rhs, tail) => c.Split.Let(isRec, nme, rhs, rec(scrutineeVar, tail)(scope + nme.withFreshSymbol.symbol)) // <-- Weird use. case s.Split.Else(default) => c.Split.Else(default) case s.Split.Nil => c.Split.Nil } scrutineeTerm match { case nme: Var => rec(nme.withResolvedTermSymbol, split) case other => val alias = context.freshScrutineeVar().withFreshSymbol c.Split.Let(false, alias, other, rec(alias, split)(scope + alias.symbol)) } } } object Desugaring { /** Make a term like `ClassName.unapply(scrutinee)`. */ private def makeUnapplyCall(scrutinee: Var, className: Var) = App(Sel(className, Var("unapply").withLocOf(className)), Tup(N -> Fld(FldFlags.empty, scrutinee) :: Nil)) } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/Normalization.scala ================================================ package mlscript package ucs package stages import mlscript.utils._, shorthands._, Message.MessageContext import context.{Context, Scrutinee} import display.{showNormalizedTerm, showSplit} import syntax.core.{Pattern, Branch, Split} import pretyper.symbol._ import pretyper.{Diagnosable, Scope, Traceable} trait Normalization { self: Desugarer with Traceable => import Normalization._ private def fillImpl(these: Split, those: Split)(implicit scope: Scope, context: Context, declaredVars: Set[Var], shouldReportDiscarded: Bool ): Split = if (these.isFull) { reportUnreachableCase(those, these, onlyIf = those =/= Split.Nil && shouldReportDiscarded) } else (these match { case these @ Split.Cons(head, tail) => these.copy(tail = fillImpl(tail, those)) case these @ Split.Let(_, nme, _, tail) => println(s"fill let binding ${nme.name}") if (scope.getTermSymbol(nme.name).isDefined && (those.freeVars contains nme)) { val fresh = context.freshShadowed() val thoseWithShadowed = Split.Let(false, nme, fresh, those) val concatenated = these.copy(tail = fillImpl(tail, thoseWithShadowed)) Split.Let(false, fresh, nme, concatenated) } else { these.copy(tail = fillImpl(tail, those)(scope, context, declaredVars + nme, false)) } case _: Split.Else => these case Split.Nil => those.withoutBindings(declaredVars).markAsFallback() }) private implicit class SplitOps(these: Split) { /** * Fill the split into the previous split. * * @param those the split to append * @param shouldReportDiscarded whether we should raise an error if the given * split is discarded because of the else branch * @param declaredVars the generated variables which have been declared * @return the concatenated split */ def fill( those: Split, declaredVars: Set[Var], shouldReportDiscarded: Bool, )(implicit scope: Scope, context: Context, ): Split = trace(s"fill <== vars = ${declaredVars.iterator.map(_.name).mkString("{", ", ", "}")}") { println(s"LHS: ${showSplit(these)}") println(s"RHS: ${showSplit(those)}") fillImpl(these, those)(scope, context, declaredVars, shouldReportDiscarded) }(sp => s"fill ==> ${showSplit(sp)}") def :++(tail: => Split): Split = { if (these.isFull) { println("tail is discarded") // raiseDesugaringWarning(msg"Discarded split because of else branch" -> these.toLoc) these } else { these ++ tail } } } /** We don't care about `Pattern.Name` because they won't appear in `specialize`. */ private implicit class PatternOps(val self: Pattern) extends AnyRef { /** Checks if two patterns are the same. */ def =:=(other: Pattern): Bool = (self, other) match { case (Pattern.Class(_, s1, _), Pattern.Class(_, s2, _)) => s1 === s2 case (Pattern.Literal(l1), Pattern.Literal(l2)) => l1 === l2 case (_, _) => false } /** Checks if `self` can be subsumed under `other`. */ def <:<(other: Pattern): Bool = { def mk(pattern: Pattern): Opt[Lit \/ TypeSymbol] = pattern match { case Pattern.Class(_, s, _) => S(R(s)) case Pattern.Literal(l) => S(L(l)) case _ => N } compareCasePattern(mk(self), mk(other)) } /** * If two class-like patterns has different `refined` flag. Report the * inconsistency as a warning. */ def reportInconsistentRefinedWith(other: Pattern): Unit = (self, other) match { case (Pattern.Class(n1, _, r1), Pattern.Class(n2, _, r2)) if r1 =/= r2 => def be(value: Bool): Str = if (value) "is" else "is not" raiseDesugaringWarning( msg"inconsistent refined pattern" -> other.toLoc, msg"pattern `${n1.name}` ${be(r1)} refined" -> n1.toLoc, msg"but pattern `${n2.name}` ${be(r2)} refined" -> n2.toLoc ) case (_, _) => () } /** If the pattern is a class-like pattern, override its `refined` flag. */ def markAsRefined: Unit = self match { case self: Pattern.Class => self.refined = true case _ => () } } /** * Normalize core abstract syntax to MLscript syntax. * * @param split the split to normalize * @return the normalized term */ @inline protected def normalize(split: Split)(implicit scope: Scope, context: Context ): Term = normalizeToTerm(split, Set.empty)(scope, context) private def errorTerm: Term = Var("error") private def normalizeToTerm(split: Split, declaredVars: Set[Var])(implicit scope: Scope, context: Context, ): Term = trace(s"normalizeToTerm <== ${showSplit(split)}") { normalizeToTermImpl(split, declaredVars) }(split => "normalizeToTerm ==> " + showNormalizedTerm(split)) private def normalizeToTermImpl(split: Split, declaredVars: Set[Var])(implicit scope: Scope, context: Context, ): Term = split match { case Split.Cons(head, tail) => head match { case Branch(scrutinee, Pattern.Name(nme), _) => println(s"ALIAS: ${scrutinee.name} is ${nme.name}") val (wrap, realTail) = preventShadowing(nme, tail) val continuation = head.continuation.fill(realTail, declaredVars, true) wrap(Let(false, nme, scrutinee, normalizeToTermImpl(continuation, declaredVars))) // Skip Boolean conditions as scrutinees, because they only appear once. case Branch(test, pattern @ Pattern.Class(nme @ Var("true"), _, _), _) if context.isTestVar(test) => println(s"TRUE: ${test.name} is true") val continuation = head.continuation.fill(tail, declaredVars, false) val whenTrue = normalizeToTerm(continuation, declaredVars) val whenFalse = normalizeToCaseBranches(tail, declaredVars) CaseOf(test, Case(nme, whenTrue, whenFalse)(refined = false)) case Branch(Scrutinee.WithVar(scrutineeVar, scrutinee), pattern @ (Pattern.Literal(_) | Pattern.Class(_, _, _)), _) => println(s"CONS: ${scrutineeVar.name} is $pattern") val continuation = head.continuation.fill(tail, declaredVars, false) val whenTrue = normalizeToTerm(specialize(continuation, +, scrutineeVar, scrutinee, pattern), declaredVars) val whenFalse = normalizeToCaseBranches(specialize(tail, `-`(head.continuation.isFull), scrutineeVar, scrutinee, pattern).clearFallback(), declaredVars) CaseOf(scrutineeVar, Case(pattern.toSimpleTerm, whenTrue, whenFalse)(refined = pattern.refined)) case Branch(scrutinee, pattern, _) => raiseDesugaringError(msg"unsupported pattern matching: ${scrutinee.name} is ${pattern.toString}" -> pattern.toLoc) errorTerm } case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && declaredVars.contains(nme) => println(s"LET: SKIP already declared scrutinee ${nme.name}") normalizeToTermImpl(tail, declaredVars) case Split.Let(rec, nme, rhs, tail) if context.isGeneratedVar(nme) => println(s"LET: generated ${nme.name}") Let(rec, nme, rhs, normalizeToTermImpl(tail, declaredVars + nme)(scope, context)) case Split.Let(rec, nme, rhs, tail) => println(s"LET: ${nme.name}") Let(rec, nme, rhs, normalizeToTermImpl(tail, declaredVars)) case Split.Else(default) => println(s"DFLT: ${default.showDbg}") default case Split.Nil => // raiseDesugaringError(msg"unexpected empty split found" -> N) errorTerm } private def normalizeToCaseBranches(split: Split, declaredVars: Set[Var])(implicit scope: Scope, context: Context, ): CaseBranches = trace(s"normalizeToCaseBranches <==") { split match { // case Split.Cons(head, Split.Nil) => Case(head.pattern, normalizeToTerm(head.continuation), NoCases) case other: Split.Cons => Wildcard(normalizeToTerm(other, declaredVars)) case Split.Let(_, nme, _, tail) if context.isScrutineeVar(nme) && declaredVars.contains(nme) => normalizeToCaseBranches(tail, declaredVars) case Split.Let(rec, nme, rhs, tail) => val newDeclaredVars = if (context.isGeneratedVar(nme)) declaredVars + nme else declaredVars normalizeToCaseBranches(tail, newDeclaredVars)(scope, context) match { case NoCases => Wildcard(rhs) case Wildcard(term) => Wildcard(Let(rec, nme, rhs, term)) case _: Case => die } case Split.Else(default) => Wildcard(default) case Split.Nil => NoCases } }(r => "normalizeToCaseBranches ==>") /** * Specialize `split` with the assumption that `scrutinee` matches `pattern`. * If `matchOrNot` is `true`, the function _keeps_ branches that agree on * `scrutinee` matches `pattern`. Otherwise, the function _removes_ branches * that agree on `scrutinee` matches `pattern`. */ private def specialize( split: Split, mode: Mode, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern )(implicit context: Context): Split = trace(s"S$mode <== ${scrutineeVar.name} is ${pattern} : ${showSplit(split, true)}"){ val specialized = specializeImpl(split)(mode, scrutineeVar, scrutinee, pattern, context) // if (split =/= Split.Nil && specialized === Split.Nil && !split.isFallback) { // raiseDesugaringWarning(msg"the case is unreachable" -> split.toLoc) // } specialized }(r => s"S$mode ==> ${showSplit(r, true)}") /** * This function does not trace. Call it when handling `tail`s, so we can get * flattened debug logs. * @param keepOrRemove `N` if S+ or `S(...)` if S-. If we don't need to track * duplication information, this parameter can be denoted * by just a Boolean variable. */ private def specializeImpl(split: Split)(implicit mode: Mode, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern, context: Context): Split = split match { case split @ Split.Cons(head, tail) => println(s"CASE Cons ${head.showDbg}") lazy val continuation = specialize(head.continuation, mode, scrutineeVar, scrutinee, pattern) head match { case Branch(thatScrutineeVar, Pattern.Name(alias), _) => Split.Let(false, alias, thatScrutineeVar, continuation) case Branch(test, Pattern.Class(Var("true"), _, _), _) if context.isTestVar(test) => head.copy(continuation = continuation) :: specializeImpl(tail) case Branch(Scrutinee.WithVar(thatScrutineeVar, thatScrutinee), thatPattern, _) => if (scrutinee === thatScrutinee) mode match { case + => println(s"Case 1.1: ${scrutineeVar.name} === ${thatScrutineeVar.name}") if (thatPattern =:= pattern) { println(s"Case 1.1.1: $pattern =:= $thatPattern") thatPattern reportInconsistentRefinedWith pattern continuation :++ specializeImpl(tail) } else if (thatPattern <:< pattern) { println(s"Case 1.1.2: $pattern <:< $thatPattern") pattern.markAsRefined; split } else { println(s"Case 1.1.3: $pattern is unrelated with $thatPattern") // The `continuation` is discarded because `thatPattern` is unrelated // to the specialization topic. if (!split.isFallback) { println(s"report warning") if (pattern <:< thatPattern) { raiseDesugaringWarning( msg"the pattern always matches" -> thatPattern.toLoc, msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, msg"which is a subtype of ${thatPattern.toString}" -> (pattern match { case Pattern.Class(_, symbol, _) => symbol.defn.toLoc case _ => thatPattern.getLoc })) continuation :++ specializeImpl(tail) } else { raiseDesugaringWarning( msg"possibly conflicting patterns for this scrutinee" -> scrutineeVar.toLoc, msg"the scrutinee was matched against ${pattern.toString}" -> pattern.toLoc, msg"which is unrelated with ${thatPattern.toString}" -> thatPattern.toLoc) specializeImpl(tail) } } else { specializeImpl(tail) } } case -(full) => println(s"Case 1.2: ${scrutineeVar.name} === ${thatScrutineeVar.name}") thatPattern reportInconsistentRefinedWith pattern val samePattern = thatPattern =:= pattern if (samePattern || thatPattern <:< pattern) { println(s"Case 1.2.1: $pattern =:= (or <:<) $thatPattern") // The `continuation` is discarded because `thatPattern` is related // to the specialization topic. println(s"`${thatScrutineeVar.name} is ${thatPattern}` is${if (split.isFallback) " " else " not "}fallback") println(s"already removed = $full") if (!split.isFallback && full && !head.continuation.isEmpty) { println(s"report warning") if (pattern === thatPattern) { raiseDesugaringWarning( msg"found a duplicated case" -> thatPattern.toLoc, msg"there is an identical pattern ${pattern.toString}" -> pattern.toLoc, ) } else { raiseDesugaringWarning( msg"found a duplicated case" -> thatPattern.toLoc, msg"the case is covered by pattern ${pattern.toString}" -> pattern.toLoc, msg"due to the subtyping relation" -> (thatPattern match { case Pattern.Class(_, symbol, _) => symbol.defn.getLoc case _ => thatPattern.toLoc }) ) } } specializeImpl(tail)( `-`(if (samePattern) continuation.isFull else full), scrutineeVar, scrutinee, pattern, context) } else { println(s"Case 1.2.2: $pattern are unrelated to $thatPattern") split.copy(tail = specializeImpl(tail)) } } else { println(s"Case 2: ${scrutineeVar.name} =/= ${thatScrutineeVar.name}") head.copy(continuation = continuation) :: specializeImpl(tail) } case _ => raiseDesugaringError(msg"unsupported pattern" -> pattern.toLoc) split } case split @ Split.Let(_, nme, _, tail) => println(s"CASE Let ${nme.name}") split.copy(tail = specializeImpl(tail)) case Split.Else(_) => println("CASE Else"); split case Split.Nil => println("CASE Nil"); split } /** * If you want to prepend `tail` to another `Split` where the `nme` takes * effects, you should use this function to prevent shadowing. */ private def preventShadowing(nme: Var, tail: Split)(implicit scope: Scope, context: Context ): (Term => Term, Split) = scope.getTermSymbol(nme.name) match { case S(symbol) if tail.freeVars contains nme => val stashVar = context.freshShadowed() ((body: Term) => Let(false, stashVar, nme, body)) -> Split.Let(false, nme, stashVar, tail) case S(_) | N => identity[Term] _ -> tail } } object Normalization { /** Specialization mode */ sealed abstract class Mode final case object + extends Mode { override def toString(): String = "+" } final case class -(full: Bool) extends Mode { override def toString(): String = s"-($full)" } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/PartialTerm.scala ================================================ package mlscript package ucs.stages import utils._, shorthands._ /** * A `PartialTerm` represents a possibly incomplete term. * We'd better precisely track detailed locations of each parts. */ sealed abstract class PartialTerm { /** Individual terms that used to build this `PartialTerm`. */ def terms: Iterator[Term] override def toString(): String = this match { case PartialTerm.Empty => "" case PartialTerm.Total(term, _) => s" ${term.showDbg}" case PartialTerm.Half(lhs, op, _) => s" ${lhs.showDbg} ${op.name}" } } object PartialTerm { sealed abstract class Incomplete extends PartialTerm { def addTerm(term: Term): PartialTerm.Total } final case object Empty extends Incomplete { override def terms: Iterator[Term] = Iterator.empty def addTerm(term: Term): Total = Total(term, term :: Nil) } final case class Total(term: Term, parts: Ls[Term]) extends PartialTerm { override def terms: Iterator[Term] = parts.reverseIterator def addOp(op: Var): Half = Half(term, op, op :: parts) def get: Term = term } final case class Half(lhs: Term, op: Var, parts: Ls[Term]) extends Incomplete { override def terms: Iterator[Term] = parts.reverseIterator def addTerm(rhs: Term): Total = { val (realRhs, extraExprOpt) = separatePattern(rhs) val leftmost = App(op, PlainTup(lhs, realRhs)) extraExprOpt match { case N => Total(leftmost, parts) case S(extraExpr) => Total(App(Var("and"), PlainTup(leftmost, extraExpr)), extraExpr :: parts) } } } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/PostProcessing.scala ================================================ package mlscript package ucs package stages import annotation.tailrec import context.{Context, Pattern, Scrutinee} import pretyper.symbol._ import utils._, shorthands._, Message.MessageContext trait PostProcessing { self: Desugarer with mlscript.pretyper.Traceable => import PostProcessing._ def postProcess(term: Term)(implicit context: Context): Term = trace(s"postProcess <== ${term.showDbg}") { // Normalized terms are constructed using `Let` and `CaseOf`. term match { case top @ CaseOf(testVar: Var, fst @ Case(Var("true"), trueBranch, Wildcard(falseBranch))) if context.isTestVar(testVar) => println(s"TEST: ${testVar.name}") top.copy(cases = fst.copy(body = postProcess(trueBranch), rest = Wildcard(postProcess(falseBranch)))(refined = fst.refined)) case top @ CaseOf(Scrutinee(_), Wildcard(body)) => body case top @ CaseOf(Scrutinee.WithVar(scrutineeVar, scrutinee), fst @ Case(className: Var, body, NoCases)) => println(s"UNARY: ${scrutineeVar.name} is ${className.name}") top.copy(cases = fst.copy(body = postProcess(body))(refined = fst.refined)) case top @ CaseOf(Scrutinee.WithVar(scrutineeVar, scrutinee), fst @ Case(pat, trueBranch, Wildcard(falseBranch))) => println(s"BINARY: ${scrutineeVar.name} is ${pat.showDbg}") println(s"patterns of `${scrutineeVar.name}`: ${scrutinee.showPatternsDbg}") // Post-process the true branch. println("TRUE") val processedTrueBranch = postProcess(trueBranch) // Post-process the false branch. println("FALSE") // Get all patterns, except the current one `pat`. val patterns = scrutinee.patternsIterator.filter(!_.matches(pat)).toList println(s"found ${patterns.length} patterns to distentangle: ${patterns.iterator.map(_.showDbg).mkString(", ")}") val (default, cases) = patterns // Search each pattern in `falseBranch`. If there exists a branch for // the given pattern, we separate the branch body from the term. The // leftover term will be used in next iteration, until there is no // term left (i.e., `leftoverTerm` is `N`). .foldLeft[Opt[Term] -> Ls[(Pattern, Opt[Loc], Term)]](S(falseBranch) -> Nil) { // If the remaining term is not empty, we continue our search. case ((S(remainingTerm), cases), pattern) => println(s"searching for branches of pattern ${pattern.showDbg}") val (leftoverTerm, extracted) = disentangleTerm(remainingTerm)( context, scrutineeVar, scrutinee, pattern) trimEmptyTerm(leftoverTerm) -> (extracted match { // `remainingTerm` does not have branches for `pattern`. case N => println(s"cannot extract pattern ${pattern.showDbg}") cases // We extracted a term and it needs to be further post-processed. case terms @ S(extractedTerm) => println(s"extracted a term of pattern ${pattern.showDbg}") (pattern, pattern.firstOccurrence, postProcess(extractedTerm)) :: cases }) // If no terms are left, we pass on `acc` until the iteration ends. case (acc @ (N, _), _) => acc } println(s"found ${cases.length} case branches") println(s"default branch: ${default.fold("")(_.showDbg)}") val postProcessedDefault = default.map(postProcess) // Assemble a `CaseBranches`. val actualFalseBranch = cases.foldRight[CaseBranches]( postProcessedDefault.fold[CaseBranches](NoCases)(Wildcard(_)) ) { case ((pattern, loc, body), rest) => Case(pattern.toCasePattern, body, rest)(refined = pattern.refined) } // Assemble the final `CaseOf`. top.copy(cases = fst.copy(body = processedTrueBranch, rest = actualFalseBranch) (refined = fst.refined)) // We recursively process the body of `Let` bindings. case let @ Let(_, _, _, body) => let.copy(body = postProcess(body)) // Otherwise, this is not a part of a normalized term. case other => println(s"CANNOT post-process ${other.showDbg}"); other } }(res => s"postProcess ==> ${res.showDbg}") private def trimEmptyTerm(term: Term): Opt[Term] = term match { case k @ CaseOf(_, cases) => trimEmptyCaseBranches(cases).flatMap(_ match { case cases: Case => S(k.copy(cases = cases)) case NoCases => N case Wildcard(body) => S(body) }) case let @ Let(_, _, _, body) => trimEmptyTerm(body).map(t => let.copy(body = t)) case _ => S(term) } private def trimEmptyCaseBranches(cases: CaseBranches): Opt[CaseBranches] = cases match { case NoCases => N case w @ Wildcard(body) => trimEmptyTerm(body).map(t => w.copy(body = t)) case k @ Case(_, body, rest) => (trimEmptyTerm(body), trimEmptyCaseBranches(rest)) match { case (N, N) => N case (S(body), N) => S(k.copy(body = body, rest = NoCases)(refined = k.refined)) case (N, S(rest)) => S(rest) case (S(body), S(rest)) => S(k.copy(body = body, rest = rest)(refined = k.refined)) } } /** * Merge two optional terms. If both are not empty we will call the other * `mergeTerms`. */ private def mergeTerms(t1: Opt[Term], t2: Opt[Term]): Opt[Term] = (t1, t2) match { case (N, N) => N case (S(t1), N) => S(t1) case (N, S(t2)) => S(t2) case (S(t1), S(t2)) => S(mergeTerms(t1, t2)) } /** * Merge two terms. In most cases, two terms cannot be merged. This function * replaces `Wildcard` in `t1` with `t2`. */ private def mergeTerms(t1: Term, t2: Term): Term = { def recTerm(lhs: Term, rhs: Term): Term = lhs match { case lhs @ Let(_, _, _, body) => lhs.copy(body = mergeTerms(body, rhs)) case lhs @ CaseOf(scrutinee: Var, cases) => lhs.copy(cases = recCaseBranches(cases, rhs)) case _ => println("unreachable: " + rhs.describe) reportUnreachableCase(rhs, lhs) } def recCaseBranches(lhs: CaseBranches, rhs: Term): CaseBranches = lhs match { case NoCases => Wildcard(rhs).withLocOf(rhs) case Wildcard(body) => Wildcard(mergeTerms(body, rhs)) case lhs @ Case(_, _, rest) => lhs.copy(rest = recCaseBranches(rest, rhs))(refined = lhs.refined) } trace(s"mergeTerms <==") { println(s"LHS: ${t1.showDbg}") println(s"RHS: ${t2.showDbg}") recTerm(t1, t2) }(merged => s"mergedTerms ==> ${merged.showDbg}") } /** * Disentangle case branches that match `scrutinee` against `className` from `term`. * The `term` should be obtained from _normalization_. Because there may exists multiple * `CaseOf`s which contains such case branches, we merge them on the fly. * * @param term the term to disentangle from * @param scrutinee the symbol of the scrutinee variable * @param className the class name * @return the remaining term and the disentangled term */ private def disentangleTerm(term: Term)(implicit context: Context, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern ): (Term, Opt[Term]) = { def rec(term: Term): (Term, Opt[Term]) = term match { // Disentangle pattern matching case top @ CaseOf(Scrutinee.WithVar(otherScrutineeVar, otherScrutinee), cases) => if (scrutinee === otherScrutinee) { println(s"found a `CaseOf` that matches on `${scrutineeVar.name}`") val (n, y) = disentangleMatchedCaseBranches(cases) (top.copy(cases = n), y) } else { println(s"found a `CaseOf` that does NOT match on ${scrutineeVar.name}") val (n, y) = disentangleUnmatchedCaseBranches(cases) (top.copy(cases = n), (if (y === NoCases) N else S(top.copy(cases = y)))) } // Disentangle tests with two case branches case top @ CaseOf(testVar: Var, Case(Var("true"), whenTrue, Wildcard(whenFalse))) if context.isTestVar(testVar) => println(s"TEST `${testVar.name}`") val (n1, y1) = disentangleTerm(whenTrue) val (n2, y2) = disentangleTerm(whenFalse) ( CaseOf(testVar, Case(Var("true"), n1, Wildcard(n2))(false)), (y1, y2) match { case (N, N) => N case (S(t1), N) => S(CaseOf(testVar, Case(Var("true"), t1, Wildcard(n2))(false))) case (N, S(t2)) => S(CaseOf(testVar, Case(Var("true"), n1, Wildcard(t2))(false))) case (S(t1), S(t2)) => S(CaseOf(testVar, Case(Var("true"), t1, Wildcard(t2))(false))) } ) // For let bindings, we just go deeper. case let @ Let(_, _, _, body) => val (n, y) = rec(body) (let.copy(body = n), y.map(t => let.copy(body = t))) // Otherwise, the scrutinee does not belong to the UCS term. case other => println(s"cannot disentangle ${other.showDbg}. STOP") other -> N } trace[(Term, Opt[Term])](s"disentangleTerm <== ${scrutineeVar.name}: ${pattern.showDbg}") { rec(term) }({ case (n, y) => s"disentangleTerm ==> `${n.showDbg}` and `${y.fold("")(_.showDbg)}`" }) } /** * Helper function for `disentangleTerm`. */ private def disentangleMatchedCaseBranches(cases: CaseBranches)(implicit context: Context, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern ): (CaseBranches, Opt[Term]) = cases match { case NoCases => NoCases -> N case wildcard @ Wildcard(body) => println("found a wildcard, go deeper") val (n, y) = disentangleTerm(body) wildcard.copy(body = n) -> y case kase @ Case(pat, body, rest) => println(s"found a case branch matching against ${pat.showDbg}") if (pattern.matches(pat)) { rest -> S(body) } else { val (n1, y1) = disentangleTerm(body) val (n2, y2) = disentangleMatchedCaseBranches(rest) (kase.copy(body = n1, rest = n2)(kase.refined), mergeTerms(y1, y2)) } } /** Helper function for `disentangleTerm`. */ private def disentangleUnmatchedCaseBranches(cases: CaseBranches)(implicit context: Context, scrutineeVar: Var, scrutinee: Scrutinee, pattern: Pattern ): (CaseBranches, CaseBranches) = cases match { case NoCases => NoCases -> NoCases case wildcard @ Wildcard(body) => println("found a wildcard, go deeper") val (n, y) = disentangleTerm(body) (wildcard.copy(body = n), y.fold(NoCases: CaseBranches)(Wildcard(_))) case kase @ Case(_, body, rest) => println(s"found a case branch") val (n1, y1) = disentangleTerm(body) val (n2, y2) = disentangleUnmatchedCaseBranches(rest) (kase.copy(body = n1, rest = n2)(kase.refined), (y1 match { case S(term) => kase.copy(body = term, rest = y2)(kase.refined) case N => y2 })) } } object PostProcessing { private object typeSymbolOrdering extends Ordering[TypeSymbol] { override def compare(x: TypeSymbol, y: TypeSymbol): Int = { (x.defn.toLoc, y.defn.toLoc) match { case (S(xLoc), S(yLoc)) => xLoc.spanStart.compare(yLoc.spanStart) case (_, _) => x.defn.name.compare(y.defn.name) } } } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/Transformation.scala ================================================ package mlscript package ucs package stages import collection.immutable, annotation.tailrec, util.chaining._ import utils._, shorthands._, Message._ import syntax.source._, pretyper.Traceable /** * Transform the parsed AST into an AST similar to the one in the paper. * The parsed AST represents pattern with terms and does not distingiush * `is` and `and` operators. * The AST in the paper is more flexible. For example, it allows interleaved * `let` bindings in operator splits. */ trait Transformation { self: Desugarer with Traceable => import Transformation._ /** The entry point of transformation. */ def transform(`if`: If): TermSplit = transformIfBody(`if`.body) ++ `if`.els.fold(Split.empty)(Split.default) /** * Transform a conjunction of terms into a nested split. The conjunction is * of the following form. * ```bnf * conjunction ::= term "is" term conjunction-tail * | "_" conjunction-tail * | term conjunction-tail * conjunction-tail ::= "and" conjunction * | ε * ``` * @param init a list of term representing the conjunction * @param last the innermost split we should take if all terms of the * conjunction work * @return */ private def transformConjunction[B <: Branch](init: Ls[Term], last: TermSplit, skipWildcard: Bool): TermSplit = init.foldRight(last) { case (scrutinee is pattern, following) => val branch = PatternBranch(transformPattern(pattern), following).toSplit TermBranch.Match(scrutinee, branch).toSplit // Maybe we don't need `skipWildcard` flag and we should take care of // wildcards at _this_ level in all cases. case (Var("_"), following) if skipWildcard => following case (test, following) => TermBranch.Boolean(test, following).toSplit } private def transformIfBody(body: IfBody): TermSplit = trace(s"transformIfBody <== ${body.showDbg}") { body match { case IfThen(expr, rhs) => transformConjunction(splitAnd(expr), Split.then(rhs), true) case IfLet(isRec, name, rhs, body) => die case IfElse(expr) => Split.then(expr) case IfOpApp(lhs, Var("is"), rhs) => val (tests, scrutinee) = extractLast(splitAnd(lhs)) transformConjunction(tests, TermBranch.Match(scrutinee, transformPatternMatching(rhs)).toSplit, false) case IfOpApp(lhs, Var("and"), rhs) => transformConjunction(splitAnd(lhs), transformIfBody(rhs), true) case IfOpApp(lhs, op, rhs) => val (init, last) = extractLast(splitAnd(lhs)) transformConjunction(init, TermBranch.Left(last, OperatorBranch.Binary(op, transformIfBody(rhs)).toSplit).toSplit, false) case IfBlock(lines) => lines.foldLeft(Split.empty[TermBranch]) { case (acc, L(body)) => acc ++ transformIfBody(body) case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => raiseDesugaringError(msg"Unexpected statement in an if block" -> statement.toLoc) acc } case IfOpsApp(lhs, opsRhss) => splitAnd(lhs) match { case init :+ (scrutinee is pattern) => // If `last` is in the form of `scrutinee is pattern`, the `op` in // `opsRhss` must be `and`. Otherwise, it's a syntax error. val innermost = transformBrokenIfOpsApp(opsRhss) val inner = TermBranch.Match(scrutinee, PatternBranch(transformPattern(pattern), innermost).toSplit).toSplit transformConjunction(init, inner, false) case init :+ last => transformConjunction(init, TermBranch.Left(last, Split.from(opsRhss.map(transformOperatorBranch))).toSplit, false) case _ => die } } }(_ => "transformIfBody ==> ") /** * Transform the case where `lhs` of `IfOpsApp` concludes pattern matching * and we need to handle its `opsRhss`. This is special, because the first * field of elements of `opsRhss` must be `and`. */ private def transformBrokenIfOpsApp(opsRhss: Ls[Var -> IfBody]): TermSplit = { opsRhss.iterator.flatMap { case Var("and") -> rhs => S(transformIfBody(rhs)) case op -> rhs => raiseDesugaringError( msg"cannot transform due to an illegal split operator ${op.name}" -> op.toLoc, msg"the following branch will be discarded" -> rhs.toLoc) N }.foldLeft(Split.Nil: TermSplit)(_ ++ _) } private def transformOperatorBranch(opsRhs: Var -> IfBody): OperatorBranch = opsRhs match { case (op @ Var("is"), rhs) => OperatorBranch.Match(op, transformPatternMatching(rhs)) case (op, rhs) => OperatorBranch.Binary(op, transformIfBody(rhs)) } /** * Transform an `IfBody` into a `PatternSplit`. */ private def transformPatternMatching(body: IfBody): PatternSplit = trace(s"transformPatternMatching <== ${body.showDbg}") { body match { case IfThen(expr, rhs) => val ::(head, tail) = splitAnd(expr) PatternBranch(transformPattern(head), transformConjunction(tail, Split.then(rhs), false)).toSplit case IfOpApp(lhs, Var("and"), rhs) => val ::(head, tail) = splitAnd(lhs) PatternBranch(transformPattern(head), transformConjunction(tail, transformIfBody(rhs), false)).toSplit case IfOpApp(lhs, op, rhs) => raiseDesugaringError(msg"Syntactic split of patterns are not supported" -> op.toLoc) Split.Nil case IfOpsApp(lhs, opsRhss) => // BEGIN TEMPORARY PATCH // Generally, syntactic split of patterns are not supported. Examples // like the following code is impossible. // ``` // fun pairwise(xs) = // if xs is Cons of // x, Nil then [x, x] // x, Cons of x', tail then [x, x'] :: pairwise of tail // else Nil // ``` // We could support this in future but it's disallowed for now. But I // found some mis-parsed examples. For example, as in `zipWith.mls:76`. // ``` // fun zipWith_wrong(f, xs, ys) = // if xs is Cons(x, xs) // and ys is Cons(y, ys) // and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) // else None // ``` // This is parsed as `{ and ( Cons x xs ) [ is ys ( Cons y ys ) ] }`. // I think it's not very well-formed. But I still implement it for not // breaking existing tests. val ::(pattern, tail) = splitAnd(lhs) // Here, `pattern` will be `( Cons x xs )` and `tail` will be // `[ is ys (Cons y ys) ]`. We can make a new `IfOpsApp`. tail match { case init :+ last => val remake = IfOpsApp(last, opsRhss) val following = transformConjunction(init, transformIfBody(remake), true) PatternBranch(transformPattern(pattern), following).toSplit case _ => // This can only be `Nil`. PatternBranch(transformPattern(pattern), transformBrokenIfOpsApp(opsRhss)).toSplit } // END TEMPORARY PATCH case IfLet(rec, nme, rhs, body) => die case IfBlock(lines) => lines.foldLeft(Split.empty[PatternBranch]) { case (acc, L(body)) => acc ++ transformPatternMatching(body) case (acc, R(NuFunDef(S(rec), nme, _, _, L(rhs)))) => acc ++ Split.Let(rec, nme, rhs, Split.Nil) case (acc, R(statement)) => raiseDesugaringError(msg"Unexpected statement in an if block" -> statement.toLoc) acc } case IfElse(expr) => Split.default(expr) } }(_ => "transformPatternMatching ==>") private def transformTupleTerm(tuple: Tup): Ls[Pattern] = tuple.fields.map(_._2.value |> transformPattern) /** * If we know the `term` represents a pattern, we can transform it to a * pattern with this function. * * @param term the term representing a pattern * @return */ private def transformPattern(term: Term): Pattern = term match { case wildcard @ Var("_") => EmptyPattern(wildcard) // The case for wildcard. case nme @ Var("true" | "false") => ConcretePattern(nme) case nme @ Var(name) if name.isCapitalized => ClassPattern(nme, N, refined = false) case nme: Var => NamePattern(nme) case literal: Lit => LiteralPattern(literal) case App(Var("refined"), PlainTup(p)) => transformPattern(p) match { case cp: ClassPattern => cp.copy(refined = true).withLocOf(cp) case p => raiseDesugaringError(msg"only class patterns can be refined" -> p.toLoc) p } case App(Var("as"), PlainTup(pattern, alias)) => alias match { case nme @ Var(_) => AliasPattern(nme, transformPattern(pattern)) case other => raiseDesugaringError(msg"the pattern alias must be a variable" -> other.toLoc) transformPattern(pattern) } case App(TyApp(classNme @ Var(_), targs), parameters: Tup) => raiseDesugaringWarning(msg"type parameters in patterns are currently ignored" -> Loc(targs)) ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) case App(classNme @ Var(_), parameters: Tup) => ClassPattern(classNme, S(transformTupleTerm(parameters)), refined = false) case tuple: Tup => TuplePattern(transformTupleTerm(tuple)) case Blk((term: Term) :: Nil) => transformPattern(term) // A speical case for FunnyIndet.mls case Bra(false, term) => transformPattern(term) case other => println(s"other $other") raiseDesugaringError(msg"unknown pattern ${other.showDbg}" -> other.toLoc) EmptyPattern(other) } /** * Split a term into a list of terms. Note that the return type is `::[Term]` * because there should be at least one term even we don't split. It used to * split right-associate `and` terms, but it turned out that `and` may * nested in a left-associate manner. Therefore, the function now traverse * the entire term and split all `and` terms. */ private def splitAnd(t: Term): ::[Term] = { @tailrec def rec(acc: Ls[Term], rest: ::[Term]): ::[Term] = rest.head match { case lhs and rhs => rec(acc, ::(rhs, lhs :: rest.tail)) case sole => rest.tail match { case Nil => ::(sole, acc) case more @ ::(_, _) => rec(sole :: acc, more) } } rec(Nil, ::(t, Nil)).tap { rs => println(s"splitAnd ${t.showDbg} ==> ${rs.iterator.map(_.showDbg).mkString(" ∧ ")}") } } } object Transformation { private def extractLast[T](xs: List[T]): (List[T], T) = xs match { case init :+ last => init -> last case _ => die } /** Matches terms like `x is y`. */ private object is { def unapply(term: Term): Opt[Term -> Term] = term match { case App(Var("is"), PlainTup(scrutinee, pattern)) => S(scrutinee -> pattern) case _ => N } } /** Matches terms like `x and y` */ private object and { def unapply(term: Term): Opt[(Term, Term)] = term match { case App(Var("and"), PlainTup(lhs, rhs)) => S((lhs, rhs)) case _ => N } } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/stages/package.scala ================================================ package mlscript package ucs import utils._, shorthands._ import pretyper.symbol.{ClassLikeSymbol, TypeSymbol} package object stages { /** * Split a term into two parts: the pattern and the extra test. * This is used to extract patterns from UCS conjunctions. For example, * the second line results in `IfThen(Some(xv) and xv == 0, ...)` * in the following case. * * ``` * if x is * Some(xv) and xv == 0 then ... * ``` * * We must separate `Some(xv)` from the term to complete the pattern * `x is Some(xv)`. * * @param term a term which might contains a pattern and an extra test * @return a tuple, whose the first element is the pattern and the second * element is the extra test */ def separatePattern(term: Term): (Term, Opt[Term]) = term match { case App(and @ Var("and"), PlainTup(lhs, rhs)) => separatePattern(lhs) match { case (pattern, N) => (pattern, S(rhs)) case (pattern, S(lhsRhs)) => (pattern, S(App(and, PlainTup(lhsRhs, rhs)))) } case _ => (term, N) } /** * Hard-coded subtyping relations used in normalization and coverage checking. * Pass literals in `L` and pass variables together with `TypeSymbol` in `R`. */ private[ucs] def compareCasePattern( lhs: Opt[Lit \/ TypeSymbol], rhs: Opt[Lit \/ TypeSymbol] ): Bool = (lhs, rhs) match { case (S(lhs), S(rhs)) => compareCasePattern(lhs, rhs) case (_, _) => false } /** * Hard-coded subtyping relations used in normalization and coverage checking. * Pass literals in `L` and pass variables together with `TypeSymbol` in `R`. */ private[stages] def compareCasePattern( lhs: Lit \/ TypeSymbol, rhs: Lit \/ TypeSymbol ): Bool = (lhs, rhs) match { case (_, R(s)) if s.name === "Object" => true case (R(s1), R(s2)) if (s1.name === "true" || s1.name === "false") && s2.name === "Bool" => true case (R(s1), R(s2)) if s1.name === "Int" && s2.name === "Num" => true case (R(s1: ClassLikeSymbol), R(s2: ClassLikeSymbol)) => s1 <:< s2 case (L(IntLit(_)), R(s)) if s.name === "Int" || s.name === "Num" => true case (L(StrLit(_)), R(s)) if s.name === "Str" => true case (L(DecLit(_)), R(s)) if s.name === "Num" => true case (_, _) => false } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/syntax/core.scala ================================================ package mlscript package ucs package syntax import collection.mutable.{Set => MutSet} import utils._, shorthands._, pretyper.symbol.ClassLikeSymbol, context.Scrutinee package object core { sealed abstract class Pattern extends Located { def refined: Bool def toSimpleTerm: SimpleTerm = this match { case Pattern.Literal(literal) => literal case Pattern.Class(nme, _, _) => nme case Pattern.Name(_) => die } def declaredVars: Iterator[Var] = this match { case _: Pattern.Literal | _: Pattern.Class => Iterator.empty case Pattern.Name(nme) => Iterator.single(nme) } override def toString(): String = this match { case Pattern.Literal(literal) => literal.idStr case Pattern.Name(Var(name)) => name case Pattern.Class(Var(name), _, rfd) => (if (rfd) "refined " else "") + name } } object Pattern { final case class Literal(literal: Lit) extends Pattern { override def refined: Bool = false override def children: Ls[Located] = literal :: Nil } final case class Name(nme: Var) extends Pattern { override def refined: Bool = false override def children: Ls[Located] = nme :: Nil } /** * @param nme the name of the class-like symbol * @param originallyRefined whether the class is marked as refined from * in source AST */ final case class Class(nme: Var, symbol: ClassLikeSymbol, originallyRefined: Bool) extends Pattern { override def children: Ls[Located] = nme :: Nil /** * A mutable field to override the refinement status of the class. * During normalization, if a case can be further refined in its * descendants branches, we should mark it as `refined`. See relevant * tests in `Normalization.mls`. */ var refined: Bool = originallyRefined } } final case class Branch(scrutinee: Var, pattern: Pattern, continuation: Split) extends Located { override def children: List[Located] = scrutinee :: pattern :: continuation :: Nil def showDbg: String = s"${scrutinee.showDbg} is $pattern" } sealed abstract class Split extends Located { @inline def ::(head: Branch): Split = { val res = Split.Cons(head, this) // res.withFallbackOf(head.continuation) if (head.continuation.isFallback) res.markAsFallback() else res } /** * Returns true if the split has an else branch. */ lazy val isFull: Bool = this match { case Split.Cons(_, tail) => tail.isFull case Split.Let(_, _, _, tail) => tail.isFull case Split.Else(_) => true case Split.Nil => false } lazy val isEmpty: Bool = this match { case Split.Let(_, _, _, tail) => tail.isEmpty case Split.Else(_) | Split.Cons(_, _) => false case Split.Nil => true } override lazy val freeVars: Set[Var] = this match { case Split.Cons(Branch(scrutinee, pattern, continuation), tail) => continuation.freeVars ++ tail.freeVars case Split.Let(true, nme, rhs, tail) => tail.freeVars ++ rhs.freeVars - nme case Split.Let(false, nme, rhs, tail) => tail.freeVars - nme ++ rhs.freeVars case Split.Else(term) => term.freeVars case Split.Nil => Set.empty } /** * Remove duplicated bindings. */ def withoutBindings(vars: Set[Var]): Split = this match { case self @ Split.Cons(head @ Branch(_, _, continuation), tail) => self.copy(head.copy(continuation = continuation.withoutBindings(vars)), tail.withoutBindings(vars)) case self @ Split.Let(_, name, _, tail) if vars contains name => tail.withoutBindings(vars) case self @ Split.Let(_, _, _, tail) => self.copy(tail = tail.withoutBindings(vars)) case Split.Else(_) | Split.Nil => this } final override def children: Ls[Located] = this match { case Split.Cons(head, tail) => head :: tail :: Nil case Split.Let(rec, name, term, tail) => name :: term :: tail :: Nil case Split.Else(default) => default :: Nil case Split.Nil => Nil } // TODO: Make the following flag temporary. It is only meaningful in a // single run of specialization. The flag is useless outside specialization // functions. /** * The split is concatenated as a fallback when specializing the given * scrutinee and pattern. */ private var _fallback: Bool = false def isFallback: Bool = _fallback def markAsFallback(): this.type = { _fallback = true this match { case Split.Cons(head, tail) => head.continuation.markAsFallback() tail.markAsFallback() case Split.Let(_, _, _, tail) => tail.markAsFallback() case _: Split.Else | Split.Nil => () } this } def clearFallback(): this.type = { _fallback = false this match { case Split.Cons(head, tail) => head.continuation.clearFallback() tail.clearFallback() case Split.Let(_, _, _, tail) => tail.clearFallback() case _: Split.Else | Split.Nil => () } this } // private val _fallbackSource = MutSet.empty[(Scrutinee, Pattern)] // def isFallbackOf(scrutinee: Scrutinee, pattern: Pattern): Bool = // _fallbackSource.contains((scrutinee, pattern)) // def markAsFallback(target: Opt[(Scrutinee, Pattern)]): this.type = // target.fold[this.type](this)(this.markAsFallback) // def markAsFallback(target: (Scrutinee, Pattern)): this.type = { // _fallbackSource += target // this match { // case Split.Cons(head, tail) => // head.continuation.markAsFallback(target) // tail.markAsFallback(target) // case Split.Let(_, _, _, tail) => tail.markAsFallback(target) // case _: Split.Else | Split.Nil => () // } // this // } // def withFallbackOf(that: Split): this.type = { // _fallbackSource ++= that._fallbackSource // this // } } object Split { final case class Cons(head: Branch, tail: Split) extends Split final case class Let(rec: Bool, name: Var, term: Term, tail: Split) extends Split final case class Else(default: Term) extends Split final case object Nil extends Split } } ================================================ FILE: shared/src/main/scala/mlscript/ucs/syntax/source.scala ================================================ package mlscript.ucs.syntax import mlscript.{Lit, Located, Term, Var} import mlscript.utils._, shorthands._ import scala.annotation.tailrec import scala.collection.immutable package object source { sealed abstract class Pattern extends Located { override def toString(): String = this match { case AliasPattern(nme, pattern) => s"${nme.name} @ $pattern" case LiteralPattern(literal) => literal.idStr case ConcretePattern(nme) => s"`${nme.name}`" case NamePattern(nme) => nme.name case EmptyPattern(_) => "•" case ClassPattern(Var(name), ps, rfd) => (if (rfd) "refined " else "") + (ps match { case N => name case S(parameters) => parameters.mkString(s"$name(", ", ", ")") }) case TuplePattern(fields) => fields.mkString("(", ", ", ")") case RecordPattern(Nil) => "{}" case RecordPattern(entries) => entries.iterator.map { case (nme, als) => s"$nme: $als" }.mkString("{ ", ", ", " }") } } final case class AliasPattern(nme: Var, pattern: Pattern) extends Pattern { override def children: List[Located] = nme :: pattern :: Nil } final case class LiteralPattern(literal: Lit) extends Pattern { override def children: List[Located] = literal :: Nil } final case class ConcretePattern(nme: Var) extends Pattern { override def children: List[Located] = nme :: Nil } final case class NamePattern(nme: Var) extends Pattern { override def children: List[Located] = nme :: Nil } /** * Represents wildcard patterns or missing patterns which match everything. * Should be transformed from `Var("_")` or unrecognized terms. */ final case class EmptyPattern(source: Term) extends Pattern { override def children: List[Located] = source :: Nil } final case class ClassPattern(nme: Var, parameters: Opt[List[Pattern]], refined: Bool) extends Pattern { override def children: List[Located] = nme :: parameters.getOrElse(Nil) } final case class TuplePattern(fields: List[Pattern]) extends Pattern { override def children: List[Located] = fields } final case class RecordPattern(entries: List[(Var -> Pattern)]) extends Pattern { override def children: List[Located] = entries.iterator.flatMap { case (nme, als) => nme :: als :: Nil }.toList } sealed abstract class Split[+SomeBranch <: Branch] extends Located { def ::[OtherBranch >: SomeBranch <: Branch](head: OtherBranch): Split[OtherBranch] = Split.Cons(head, this) } object Split { import immutable.{Nil => LNil} final case class Cons[SomeBranch <: Branch](head: SomeBranch, tail: Split[SomeBranch]) extends Split[SomeBranch] { override def children: List[Located] = head :: tail :: LNil } final case class Let[SomeBranch <: Branch](rec: Bool, nme: Var, rhs: Term, tail: Split[SomeBranch]) extends Split[SomeBranch] { override def children: List[Located] = nme :: rhs :: tail :: LNil } final case class Else(term: Term) extends Split[Nothing] { override def children: List[Located] = term :: LNil } final case object Nil extends Split[Nothing] { override def children: List[Located] = LNil } def empty[SomeBranch <: Branch]: Split[SomeBranch] = Nil def single[SomeBranch <: Branch](branch: SomeBranch): Split[SomeBranch] = Cons(branch, Nil) def `then`(term: Term): Split[TermBranch] = Else(term) def default[SomeBranch <: Branch](term: Term): Split[SomeBranch] = Else(term) def from[SomeBranch <: Branch](branches: Iterable[SomeBranch]): Split[SomeBranch] = branches.foldRight(Nil: Split[SomeBranch])(Cons.apply) } sealed abstract class Branch extends Located sealed abstract class TermBranch extends Branch { final def toSplit: TermSplit = Split.single(this) } object TermBranch { final case class Boolean(test: Term, continuation: TermSplit) extends TermBranch { override def children: List[Located] = test :: continuation :: Nil } final case class Match(scrutinee: Term, continuation: PatternSplit) extends TermBranch { override def children: List[Located] = scrutinee :: continuation :: Nil } final case class Left(left: Term, continuation: OperatorSplit) extends TermBranch { override def children: List[Located] = left :: continuation :: Nil } } type TermSplit = Split[TermBranch] sealed abstract class OperatorBranch extends Branch { val operator: Var final def toSplit: OperatorSplit = Split.single(this) } object OperatorBranch { final case class Match(override val operator: Var, continuation: PatternSplit) extends OperatorBranch { override def children: List[Located] = operator :: continuation :: Nil } final case class Binary(override val operator: Var, continuation: TermSplit) extends OperatorBranch { override def children: List[Located] = operator :: continuation :: Nil } } type OperatorSplit = Split[OperatorBranch] final case class PatternBranch(val pattern: Pattern, val continuation: TermSplit) extends Branch { override def children: List[Located] = pattern :: continuation :: Nil final def toSplit: PatternSplit = Split.single(this) } type PatternSplit = Split[PatternBranch] } ================================================ FILE: shared/src/main/scala/mlscript/utils/FastParseHelpers.scala ================================================ package mlscript import mlscript.utils._, shorthands._ class FastParseHelpers(val blockStr: Str, val lines: collection.IndexedSeq[Str]) { def this(lines: IndexedSeq[Str]) = this(lines.mkString("\n"), lines) def this(blockStr: Str) = this(blockStr, blockStr.splitSane('\n')) // this line-parsing logic was copied from fastparse internals: val lineNumberLookup: Array[Int] = fastparse.internal.Util.lineNumberLookup(blockStr) def getLineColAt(index: Int): (Int, String, Int) = { val lineNum = lineNumberLookup.indexWhere(_ > index) match { case -1 => lineNumberLookup.length case n => math.max(1, n) } val idx = lineNum.min(lines.length) - 1 val colNum = index - lineNumberLookup(idx) + 1 val lineStr = lines(idx) (lineNum, lineStr, colNum) } } ================================================ FILE: shared/src/test/diff/analysis/Variance.mls ================================================ type UseT[T] = T -> int class C[X]: { y: X } method Create: int -> C[X] method MapWrap: (int -> int) -> int -> D[C[X]] class D[R]: { z: R } method Produce: string -> D[R] method Producer: int -> C[R] -> D[R] method Unwrap: D[R] -> R //│ Defined type alias UseT[-T] //│ Defined class C[=X] //│ Declared C.Create: C['X] -> int -> C['X] //│ Declared C.MapWrap: C['X] -> (int -> int) -> int -> D[C['X]] //│ Defined class D[=R] //│ Declared D.Produce: D['R] -> string -> D['R] //│ Declared D.Producer: D['R] -> int -> C['R] -> D['R] //│ Declared D.Unwrap: D['R] -> D['R] -> 'R :w class F[S, T]: E[T] method Create: S -> T class E[G] method Id: G -> G class Z[X, R] method Phantom: C[X] //│ Defined class F[-S, =T] //│ Declared F.Create: F['S, 'T] -> 'S -> 'T //│ Defined class E[=G] //│ Declared E.Id: E['G] -> 'G -> 'G //│ Defined class Z[=X, ±R] //│ Declared Z.Phantom: Z['X, ?] -> C['X] //│ ╔══[WARNING] Type definition Z has bivariant type parameters: //│ ║ l.24: class Z[X, R] //│ ║ ^ //│ ╟── R is irrelevant and may be removed //│ ║ l.24: class Z[X, R] //│ ╙── ^ class C0[A] method M0 (x: A) = x //│ Defined class C0[=A] //│ Defined C0.M0: C0['A] -> 'A -> 'A // Note that the variance that'd be implied by the method definition inferred type // is not taken into consideration because that inferred type is discarded // in favor of the explicit signature. :w class C1[A] method M1 (x: A) = x method M1: nothing -> anything //│ Defined class C1[±A] //│ Declared C1.M1: C1[?] -> nothing -> anything //│ Defined C1.M1: C1[?] -> 'A -> 'A //│ ╔══[WARNING] Type definition C1 has bivariant type parameters: //│ ║ l.50: class C1[A] //│ ║ ^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.50: class C1[A] //│ ╙── ^ ================================================ FILE: shared/src/test/diff/analysis/Weird.mls ================================================ class Base1[A] method M1: A -> Base1[A] //│ Defined class Base1[-A] //│ Declared Base1.M1: Base1['A] -> 'A -> Base1['A] :DistributeForalls // * Note: C used to be inferred as invariant here with :DistributeForalls; // * It happened when `subst` did not hygienically substitute under polymorphic types :w class Derived2[C]: Base1[anything] method M1 r = Derived2{} //│ Defined class Derived2[±C] //│ Defined Derived2.M1: Derived2[?] -> anything -> Derived2[?] //│ ╔══[WARNING] Type definition Derived2 has bivariant type parameters: //│ ║ l.13: class Derived2[C]: Base1[anything] //│ ║ ^^^^^^^^ //│ ╟── C is irrelevant and may be removed //│ ║ l.13: class Derived2[C]: Base1[anything] //│ ╙── ^ Derived2.M1 //│ res: Derived2[?] -> anything -> Derived2[?] //│ = undefined :DontDistributeForalls :w class Derived3[C]: Base1[anything] method M1 r = Derived3{} //│ Defined class Derived3[±C] //│ Defined Derived3.M1: Derived3[?] -> anything -> Derived3[?] //│ ╔══[WARNING] Type definition Derived3 has bivariant type parameters: //│ ║ l.32: class Derived3[C]: Base1[anything] //│ ║ ^^^^^^^^ //│ ╟── C is irrelevant and may be removed //│ ║ l.32: class Derived3[C]: Base1[anything] //│ ╙── ^ Derived3.M1 //│ res: Derived3[?] -> anything -> Derived3[?] //│ = undefined ================================================ FILE: shared/src/test/diff/basics/AsBindings.fun ================================================ let f x = discard / x as { v: (y) } // TODO accept plain `y` log / v log / y + 1 //│ f: {v: int} -> unit let f x = discard / x as { v: (y) } // TODO accept plain `y` log / v + 1 log / y //│ f: {v: int} -> unit ================================================ FILE: shared/src/test/diff/basics/Blocks.fun ================================================ // let foo x = // TODO let foo = x => let a = x + 1 a //│ foo: int -> int let foo = x => log x let u = x + 1 log true u + 1 //│ foo: int -> int let foo = x => log x; let u = x + 1; log true; u + 1 //│ foo: int -> int foo 1 foo / 1 foo / foo / 1 //│ res: int //│ res: int //│ res: int foo foo 1 //│ res: int foo discard / foo 1 foo 1 //│ res: int foo / foo / foo 1 //│ res: int :p discard / foo 1 //│ Parsed: discard(...(foo(...{1}))); //│ Desugared: discard(...(foo(...{1}))) //│ AST: App(Var(discard),App(Var(foo),Blk(List(IntLit(1))))) :e discard foo 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.50: discard foo //│ ║ ^^^^^^^^^^^ //│ ║ l.51: 1 //│ ║ ^^^ //│ ╟── application of type `unit` is not a function //│ ║ l.50: discard foo //│ ╙── ^^^^^^^^^^^ //│ res: error :w foo foo 1 foo 2 //│ ╔══[WARNING] Expression in statement position should have type `unit`. //│ ╟── Use the `discard` function to discard non-unit values, making the intent clearer. //│ ╟── Type mismatch in application: //│ ║ l.64: foo 1 //│ ║ ^^^^^ //│ ╟── operator application of type `int` is not an instance of type `unit` //│ ║ l.19: u + 1 //│ ║ ^^^^^ //│ ╟── but it flows into application with expected type `unit` //│ ║ l.64: foo 1 //│ ╙── ^^^^^ //│ res: int :p id id id //│ Parsed: id(...id)(...{id}); //│ Desugared: id(...id)(...{id}) //│ AST: App(App(Var(id),Var(id)),Blk(List(Var(id)))) //│ res: 'a -> 'a :p id id id id id id id id id id id id //│ Parsed: id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)})})}); //│ Desugared: id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)(...{id(...id)(...id)})})}) //│ AST: App(App(App(Var(id),Var(id)),Var(id)),Blk(List(App(App(App(Var(id),Var(id)),Var(id)),Blk(List(App(App(App(Var(id),Var(id)),Var(id)),Blk(List(App(App(Var(id),Var(id)),Var(id))))))))))) //│ res: 'a -> 'a :p id id / id id / id id //│ Parsed: id(...id)(...{id(...id)(...{id(...id)})}); //│ Desugared: id(...id)(...{id(...id)(...{id(...id)})}) //│ AST: App(App(Var(id),Var(id)),Blk(List(App(App(Var(id),Var(id)),Blk(List(App(Var(id),Var(id)))))))) //│ res: 'a -> 'a :p id id id id id id //│ Parsed: id(...id)(...{id(...id)})(...{id(...id)}); //│ Desugared: id(...id)(...{id(...id)})(...{id(...id)}) //│ AST: App(App(App(Var(id),Var(id)),Blk(List(App(Var(id),Var(id))))),Blk(List(App(Var(id),Var(id))))) //│ res: 'a -> 'a let foo = log 1 log 2 //│ foo: unit let foo = log 1 log 2 //│ foo: unit let foo = log 1 log 2 //│ foo: unit succ ( log 1 1 ) succ ( log 1 1) succ (succ 1) succ (succ 1 ) succ (succ succ 1) succ (succ let x = 1; x) //│ res: int //│ res: int //│ res: int //│ res: int //│ res: int //│ res: int :w succ ( succ 1 ) //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.159: succ //│ ╙── ^^^^ //│ res: int :pe succ (succ 1) //│ /!\ Parse error: Expected end-of-input:1:6, found "(succ\n1)\n" at l.168:6: succ (succ :pe succ (succ succ 1) //│ /!\ Parse error: Expected end-of-input:1:6, found "(succ\nsucc" at l.173:6: succ (succ :pe succ (succ succ 1) //│ /!\ Parse error: Expected end-of-input:1:6, found "(succ\nsucc" at l.178:6: succ (succ (let x = 1) (let x = 1; x) ( let x = 1 x ) succ( let x = 1 x ) //│ res: () //│ res: 1 //│ res: 1 //│ res: int succ ( let x = 1 x ) //│ res: int log 1; log 2; log 3 let a = 1; log a; let b = 2 //│ a: 1 //│ b: 2 (let a = 1; log a; let b = 2) //│ res: () (let a = 1; log a; let b = 2; a + b) //│ res: int :e let test = let aaa = let bbb = 42 bbb bbb //│ ╔══[ERROR] identifier not found: bbb //│ ║ l.223: bbb //│ ╙── ^^^ //│ test: error let test = let aaa = let bbb = 42 bbb aaa //│ test: 42 :e aaa //│ ╔══[ERROR] identifier not found: aaa //│ ║ l.237: aaa //│ ╙── ^^^ //│ res: error :pe succ ( let x = 1 x ) //│ /!\ Parse error: Expected expression:1:1, found "succ\n (\n " at l.245:1: succ :pe let a = succ 1 "?" //│ /!\ Parse error: Expected end-of-input:3:3, found "1\n \"?\"\n" at l.255:3: 1 :pe 1 //│ /!\ Parse error: Expected (data type definition | data definition | let binding | expression):1:1, found " 1\n" at l.260:1: 1 ================================================ FILE: shared/src/test/diff/basics/Data.fun ================================================ :p data Test a b //│ Parsed: data Test(...a)(...b); //│ Desugared: class Test[a, b]: {a: a, b: b} //│ Desugared: def Test: forall a b. (...a) -> (...b) -> Test[a, b] //│ AST: Def(false,Var(Test),Right(PolyType(List(Left(TypeName(a)), Left(TypeName(b))),Function(TypeName(a),Function(TypeName(b),AppliedType(TypeName(Test),List(TypeName(a), TypeName(b))))))),true) //│ Defined class Test[+a, +b] //│ Test: 'a -> 'b -> Test['a, 'b] :p data Person(name: string, age: int) //│ Parsed: data Person(...'(' {[name: string, age: int,]} ')'); //│ Desugared: class Person: {age: int, name: string} //│ Desugared: def Person: (name: string, age: int) -> Person[] //│ AST: Def(false,Var(Person),Right(PolyType(List(),Function(Tuple(List((Some(Var(name)),Field(None,TypeName(string))), (Some(Var(age)),Field(None,TypeName(int))))),AppliedType(TypeName(Person),List())))),true) //│ Defined class Person //│ Person: (name: string, age: int,) -> Person let p = Person("Bob", 42) //│ p: Person let foo q = q.age foo p //│ foo: {age: 'age} -> 'age //│ res: int // TODO properly check pattern types! let bar (q: Person _) = q.age //│ bar: (q: {age: 'age},) -> 'age bar p //│ res: int :e bar {} bar {name: "Bob"} bar {age: 1} // TODO B/E //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.36: bar {} //│ ║ ^^^^^^ //│ ╟── tuple of type `anything` does not have field 'age' //│ ║ l.36: bar {} //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.29: let bar (q: Person _) = q.age //│ ║ ^^^^ //│ ╟── from binding: //│ ║ l.29: let bar (q: Person _) = q.age //│ ╙── ^^^^^^^^^^^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.37: bar {name: "Bob"} //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{name: "Bob"}` does not have field 'age' //│ ║ l.37: bar {name: "Bob"} //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.29: let bar (q: Person _) = q.age //│ ║ ^^^^ //│ ╟── from binding: //│ ║ l.29: let bar (q: Person _) = q.age //│ ╙── ^^^^^^^^^^^ //│ res: error //│ res: 1 let fake-p = { name: "Bob", age: 42 } //│ fake-p: {age: 42, name: "Bob"} // :e // TODO B/E bar fake-p //│ res: 42 data Wine(name: string, age: int) let w = Wine("Côtes du Rhône", 3) //│ Defined class Wine //│ Wine: (name: string, age: int,) -> Wine //│ w: Wine // :e bar w bar (q: w) //│ res: int //│ res: int let bar2 (q: Person _) = succ q.age //│ bar2: (q: {age: int},) -> int :e let nested x = data Foo a // Note: we get one error for the synthetic class, and one for the synthetic def... Foo x //│ ╔══[ERROR] Illegal position for this type declaration statement. //│ ║ l.92: data Foo a // Note: we get one error for the synthetic class, and one for the synthetic def... //│ ╙── ^^^^^ //│ ╔══[ERROR] Illegal position for this definition statement. //│ ║ l.92: data Foo a // Note: we get one error for the synthetic class, and one for the synthetic def... //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: Foo //│ ║ l.93: Foo x //│ ╙── ^^^ //│ nested: error -> error ================================================ FILE: shared/src/test/diff/basics/Datatypes.fun ================================================ :p data type Boolean of Tru, Fals //│ Parsed: data type Boolean of {Tru; Fals}; //│ Desugared: type alias Boolean = Tru[] | Fals[] //│ Desugared: class Tru: {} //│ Desugared: class Fals: {} //│ Desugared: def Tru: Tru[] //│ AST: Def(false,Var(Tru),Right(PolyType(List(),AppliedType(TypeName(Tru),List()))),true) //│ Desugared: def Fals: Fals[] //│ AST: Def(false,Var(Fals),Right(PolyType(List(),AppliedType(TypeName(Fals),List()))),true) //│ Defined type alias Boolean //│ Defined class Tru //│ Defined class Fals //│ Tru: Tru //│ Fals: Fals :e Boolean //│ ╔══[ERROR] identifier not found: Boolean //│ ║ l.19: Boolean //│ ╙── ^^^^^^^ //│ res: error :p :e data type Bool2 of True2 & False2 //│ Parsed: data type Bool2 of {&(...True2)(...False2)}; //│ Desugared: type alias Bool2 = &[True2, False2] //│ Desugared: class &[True2, False2]: {False2 <: False2, True2 <: True2} //│ Desugared: def &: forall True2 False2. (...True2) -> (...False2) -> &[True2, False2] //│ AST: Def(false,Var(&),Right(PolyType(List(Left(TypeName(True2)), Left(TypeName(False2))),Function(TypeName(True2),Function(TypeName(False2),AppliedType(TypeName(&),List(TypeName(True2), TypeName(False2))))))),true) //│ ╔══[ERROR] type identifier not found: True2 //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^^^^^ //│ ╔══[ERROR] type identifier not found: False2 //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type names must start with a capital letter //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^ //│ ╔══[ERROR] Field identifiers must start with a small letter //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Field identifiers must start with a small letter //│ ║ l.27: data type Bool2 of True2 & False2 //│ ╙── ^^^^^ //│ Defined type alias Bool2 //│ Defined class &[+True2, +False2] //│ &: 'a -> 'b -> &['a, 'b] data type Bool3 of True3; False3 //│ Defined type alias Bool3 //│ Defined class True3 //│ Defined class False3 //│ True3: True3 //│ False3: False3 data type Bool4 of True4 False4 //│ Defined type alias Bool4 //│ Defined class True4 //│ Defined class False4 //│ True4: True4 //│ False4: False4 :e Boolean //│ ╔══[ERROR] identifier not found: Boolean //│ ║ l.70: Boolean //│ ╙── ^^^^^^^ //│ res: error Tru //│ res: Tru :e // TODO support types on RHS of `as` Tru as Boolean Tru : Boolean //│ ╔══[ERROR] identifier not found: Boolean //│ ║ l.80: Tru as Boolean //│ ╙── ^^^^^^^ //│ res: error //│ ╔══[ERROR] identifier not found: Boolean //│ ║ l.81: Tru : Boolean //│ ╙── ^^^^^^^ //│ res: (Tru: error,) :e // Maybe we shouldn't interpret capitalized identifiers as field names... Tru : Boolean //│ ╔══[ERROR] identifier not found: Boolean //│ ║ l.92: Tru : Boolean //│ ╙── ^^^^^^^ //│ res: (Tru: error,) :pe (Tru) : Boolean //│ /!\ Parse error: Expected end-of-input:1:7, found ": Boolean\n" at l.99:7: (Tru) : Boolean // TODO treat the ending curly-blocks as bodies (not params)? // data type List of // Nil { T: Nothing } // Cons head tail { T: head | tail.T } // TODO also try the one-line version: // data type List of Nil { T: Nothing }, Cons head tail { T: head | tail.T } :p :pe :w data type List a of Nil Cons (head: a) (tail: List a) //│ Parsed: data type List(...a) of {Nil; Cons(...'(' {[head: a,]} ')')(...'(' {[tail: List(...a),]} ')')}; //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.116: Cons (head: a) (tail: List a) //│ ╙── ^^^^^^ //│ Desugared: type alias List[a] = Nil[a] | Cons[a] //│ Desugared: class Nil[a]: {} //│ Desugared: class Cons[a]: {head: a, tail: anything} //│ Desugared: def Nil: forall a. Nil[a] //│ AST: Def(false,Var(Nil),Right(PolyType(List(Left(TypeName(a))),AppliedType(TypeName(Nil),List(TypeName(a))))),true) //│ Desugared: def Cons: forall a. (head: a) -> (tail: anything) -> Cons[a] //│ AST: Def(false,Var(Cons),Right(PolyType(List(Left(TypeName(a))),Function(Tuple(List((Some(Var(head)),Field(None,TypeName(a))))),Function(Tuple(List((Some(Var(tail)),Field(None,Top)))),AppliedType(TypeName(Cons),List(TypeName(a))))))),true) //│ Defined type alias List[+a] //│ Defined class Nil[±a] //│ Defined class Cons[+a] //│ ╔══[WARNING] Type definition Nil has bivariant type parameters: //│ ║ l.115: Nil //│ ║ ^^^ //│ ╟── a is irrelevant and may be removed //│ ║ l.114: data type List a of //│ ╙── ^ //│ Nil: Nil[?] //│ Cons: (head: 'a,) -> (tail: anything,) -> Cons['a] // TODO interpret as free type variable? :p data type Ls of LsA a //│ Parsed: data type Ls of {LsA(...a)}; //│ Desugared: type alias Ls = LsA[a] //│ Desugared: class LsA[a]: {a: a} //│ Desugared: def LsA: forall a. (...a) -> LsA[a] //│ AST: Def(false,Var(LsA),Right(PolyType(List(Left(TypeName(a))),Function(TypeName(a),AppliedType(TypeName(LsA),List(TypeName(a)))))),true) //│ ╔══[ERROR] type identifier not found: a //│ ║ l.142: data type Ls of LsA a //│ ╙── ^ //│ Defined type alias Ls //│ Defined class LsA[+a] //│ LsA: 'a -> LsA['a] :p :e data type Ls2 of LsA2 `a //│ Parsed: data type Ls2 of {LsA2(...`a)}; //│ Desugared: type alias Ls2 = LsA2[] //│ Desugared: class LsA2: {`a: 'a} //│ Desugared: def LsA2: (...'a) -> LsA2[] //│ AST: Def(false,Var(LsA2),Right(PolyType(List(),Function(a,AppliedType(TypeName(LsA2),List())))),true) //│ ╔══[ERROR] cannot inherit from a polymorphic type //│ ║ l.157: data type Ls2 of LsA2 `a //│ ╙── ^^^^^^^ //│ ╔══[ERROR] type identifier not found: LsA2 //│ ╙── //│ Defined type alias Ls2 //│ ╔══[ERROR] type identifier not found: LsA2 //│ ╙── //│ LsA2: anything -> error Nil Cons Cons 1 Cons 2 Nil Cons 1 (Cons 2 Nil) //│ res: Nil[?] //│ res: (head: 'a,) -> (tail: anything,) -> Cons['a] //│ res: (tail: anything,) -> Cons[1] //│ res: Cons[2] //│ res: Cons[1] (Cons 3 Nil).head succ (Cons 3 Nil).head not (Cons false Nil).head //│ res: 3 //│ res: int //│ res: bool :e not (Cons 42 Nil).head //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.192: not (Cons 42 Nil).head //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `42` is not an instance of type `bool` //│ ║ l.192: not (Cons 42 Nil).head //│ ║ ^^ //│ ╟── but it flows into field selection with expected type `bool` //│ ║ l.192: not (Cons 42 Nil).head //│ ╙── ^^^^^ //│ res: bool | error :e (Cons 4).head //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.205: (Cons 4).head //│ ║ ^^^^^ //│ ╟── type `(tail: anything,) -> Cons[?a]` does not have field 'head' //│ ║ l.114: data type List a of //│ ║ ^ //│ ╟── but it flows into receiver with expected type `{head: ?head}` //│ ║ l.205: (Cons 4).head //│ ╙── ^^^^^^^^ //│ res: error // :e Cons 1 2 //│ res: Cons[1] // TODO Allow method/field defintions in the same file (lose the let?): :e let List.head = () // ... //│ ╔══[ERROR] Unsupported pattern shape //│ ║ l.223: let List.head = () // ... //│ ╙── ^^^^^ //│ : () ================================================ FILE: shared/src/test/diff/basics/Either.fun ================================================ :p :w data type Either l r of Left l Right r //│ Parsed: data type Either(...l)(...r) of {Left(...l); Right(...r)}; //│ Desugared: type alias Either[l, r] = Left[l, r] | Right[l, r] //│ Desugared: class Left[l, r]: {l: l} //│ Desugared: class Right[l, r]: {r: r} //│ Desugared: def Left: forall l r. (...l) -> Left[l, r] //│ AST: Def(false,Var(Left),Right(PolyType(List(Left(TypeName(l)), Left(TypeName(r))),Function(TypeName(l),AppliedType(TypeName(Left),List(TypeName(l), TypeName(r)))))),true) //│ Desugared: def Right: forall l r. (...r) -> Right[l, r] //│ AST: Def(false,Var(Right),Right(PolyType(List(Left(TypeName(l)), Left(TypeName(r))),Function(TypeName(r),AppliedType(TypeName(Right),List(TypeName(l), TypeName(r)))))),true) //│ Defined type alias Either[+l, +r] //│ Defined class Left[+l, ±r] //│ Defined class Right[±l, +r] //│ ╔══[WARNING] Type definition Left has bivariant type parameters: //│ ║ l.5: Left l //│ ║ ^^^^ //│ ╟── r is irrelevant and may be removed //│ ║ l.4: data type Either l r of //│ ╙── ^ //│ ╔══[WARNING] Type definition Right has bivariant type parameters: //│ ║ l.6: Right r //│ ║ ^^^^^ //│ ╟── l is irrelevant and may be removed //│ ║ l.4: data type Either l r of //│ ╙── ^ //│ Left: 'a -> Left['a, ?] //│ Right: 'a -> Right[?, 'a] :e data type Either2 (l: _) (r: _) of Left2 l Right2 r //│ ╔══[ERROR] illegal datatype type parameter shape: '(' {[l: _,]} ')' //│ ║ l.34: data type Either2 (l: _) (r: _) of //│ ╙── ^^^^^^ //│ ╔══[ERROR] illegal datatype type parameter shape: '(' {[r: _,]} ')' //│ ║ l.34: data type Either2 (l: _) (r: _) of //│ ╙── ^^^^^^ //│ ╔══[ERROR] type identifier not found: l //│ ║ l.35: Left2 l //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: r //│ ║ l.36: Right2 r //│ ╙── ^ //│ Defined type alias Either2 //│ Defined class Left2[+l] //│ Defined class Right2[+r] //│ Left2: 'a -> Left2['a] //│ Right2: 'a -> Right2['a] let l = Left 1 let r = Right "ok" let e = if _ then l else r //│ l: Left[1, ?] //│ r: Right[?, "ok"] //│ e: Left[1, ?] | Right[?, "ok"] :e // TODO e as Either Int String //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.63: e as Either Int String //│ ╙── ^^^^^^^^^^^^^^^^^ //│ res: error // TODO // e as (_: Either Int String) // e as (_: Either (L: Int) (R: String)) :e e as Either //│ ╔══[ERROR] identifier not found: Either //│ ║ l.74: e as Either //│ ╙── ^^^^^^ //│ res: error ================================================ FILE: shared/src/test/diff/basics/Errors.fun ================================================ :ShowRelativeLineNums let a = succ //│ a: int -> int let b = a 1 //│ b: int // identifier not found :e c id c log 1 log / c + 1 log / abc + 1 c + 1 abc + 1 1 + c let d = c + 1 let d = 1 + abc + 1 + 1 d + 1 //│ ╔══[ERROR] identifier not found: c //│ ║ l.+1: c //│ ╙── ^ //│ res: error //│ ╔══[ERROR] identifier not found: c //│ ║ l.+2: id c //│ ╙── ^ //│ res: error //│ ╔══[ERROR] identifier not found: c //│ ║ l.+4: log / c + 1 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: abc //│ ║ l.+5: log / abc + 1 //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: c //│ ║ l.+6: c + 1 //│ ╙── ^ //│ res: int //│ ╔══[ERROR] identifier not found: abc //│ ║ l.+7: abc + 1 //│ ╙── ^^^ //│ res: int //│ ╔══[ERROR] identifier not found: c //│ ║ l.+8: 1 + c //│ ╙── ^ //│ res: int //│ ╔══[ERROR] identifier not found: c //│ ║ l.+9: let d = c + 1 //│ ╙── ^ //│ d: int //│ ╔══[ERROR] identifier not found: abc //│ ║ l.+10: let d = 1 + abc + 1 + 1 //│ ╙── ^^^ //│ d: int //│ res: int // cannot constrain :e 1 2 3 a b c let oops = succ false false + 1 1 + false true + false log / false + 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: 1 2 3 //│ ║ ^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.+1: 1 2 3 //│ ╙── ^ //│ res: error //│ ╔══[ERROR] identifier not found: c //│ ║ l.+2: a b c //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: a b c //│ ║ ^^^^^ //│ ╟── application of type `int` is not a function //│ ║ l.+2: a b c //│ ╙── ^^^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: let oops = succ false //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+3: let oops = succ false //│ ╙── ^^^^^ //│ oops: error | int //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+4: false + 1 //│ ║ ^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+4: false + 1 //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+5: 1 + false //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+5: 1 + false //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+6: true + false //│ ║ ^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.+6: true + false //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+6: true + false //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+6: true + false //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+7: log / false + 1 //│ ║ ^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+7: log / false + 1 //│ ╙── ^^^^^ :e succ succ 1 ( succ ) false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: succ succ //│ ║ ^^^^^^^^^ //│ ╟── reference of type `int -> int` is not an instance of type `int` //│ ║ l.+1: succ succ //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: succ succ //│ ║ ^^^^^^^^^ //│ ║ l.+2: 1 //│ ║ ^^^ //│ ╟── application of type `int` is not a function //│ ║ l.+1: succ succ //│ ╙── ^^^^^^^^^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: ( //│ ║ ^ //│ ║ l.+4: succ //│ ║ ^^^^^^ //│ ║ l.+5: ) false //│ ║ ^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+5: ) false //│ ╙── ^^^^^ //│ res: error | int :e :w log 1 2 log 1 2 log 1 2 log let f = 1 f 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: 1 //│ ║ ^ //│ ║ l.+3: 2 //│ ║ ^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.+2: 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+4: log 1 //│ ║ ^^^^^ //│ ║ l.+5: 2 //│ ║ ^^^ //│ ╟── application of type `unit` is not a function //│ ║ l.+4: log 1 //│ ╙── ^^^^^ //│ res: error //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.+7: 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+11: f 2 //│ ║ ^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.+10: let f = 1 //│ ║ ^ //│ ╟── but it flows into reference with expected type `2 -> ?a` //│ ║ l.+11: f 2 //│ ╙── ^ :w log succ 1 2 //│ ╔══[WARNING] Expression in statement position should have type `unit`. //│ ╟── Use the `discard` function to discard non-unit values, making the intent clearer. //│ ╟── Type mismatch in application: //│ ║ l.+2: succ 1 //│ ║ ^^^^^^ //│ ╙── application of type `int` is not an instance of type `unit` :e succ ((((false)))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: succ ((((false)))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+1: succ ((((false)))) //│ ║ ^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: succ ((((false)))) //│ ╙── ^^^^^^^^^^^^^ //│ res: error | int :e let rec f = n => if n then 0 else f (miss + 1) //│ ╔══[ERROR] identifier not found: miss //│ ║ l.+1: let rec f = n => if n then 0 else f (miss + 1) //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in binding of lambda expression: //│ ║ l.+1: let rec f = n => if n then 0 else f (miss + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` is not an instance of type `bool` //│ ║ l.+1: let rec f = n => if n then 0 else f (miss + 1) //│ ║ ^^^^^^^^ //│ ╟── but it flows into argument with expected type `bool` //│ ║ l.+1: let rec f = n => if n then 0 else f (miss + 1) //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.+1: let rec f = n => if n then 0 else f (miss + 1) //│ ╙── ^ //│ f: bool -> 0 // missing field, cannot constrain :e 1.u {}.u {a: 1}.u {a: 1}.a 1 1 + {a: true}.a {a: true}.a + 1 succ {a: 1} {a: 1} succ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: 1.u //│ ║ ^^ //│ ╟── integer literal of type `1` does not have field 'u' //│ ║ l.+1: 1.u //│ ╙── ^ //│ res: error //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+2: {}.u //│ ║ ^^ //│ ╟── tuple of type `anything` does not have field 'u' //│ ║ l.+2: {}.u //│ ╙── ^^ //│ res: error //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+3: {a: 1}.u //│ ║ ^^ //│ ╟── record of type `{a: 1}` does not have field 'u' //│ ║ l.+3: {a: 1}.u //│ ╙── ^^^^^^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+4: {a: 1}.a 1 //│ ║ ^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.+4: {a: 1}.a 1 //│ ║ ^ //│ ╟── but it flows into field selection with expected type `1 -> ?a` //│ ║ l.+4: {a: 1}.a 1 //│ ╙── ^^ //│ res: error //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+5: 1 + {a: true}.a //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.+5: 1 + {a: true}.a //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.+5: 1 + {a: true}.a //│ ╙── ^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+6: {a: true}.a + 1 //│ ║ ^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.+6: {a: true}.a + 1 //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.+6: {a: true}.a + 1 //│ ╙── ^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+7: succ {a: 1} //│ ║ ^^^^^^^^^^^ //│ ╟── record of type `{a: 1}` is not an instance of type `int` //│ ║ l.+7: succ {a: 1} //│ ╙── ^^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+8: {a: 1} succ //│ ║ ^^^^^^^^^^^ //│ ╟── record of type `{a: 1}` is not a function //│ ║ l.+8: {a: 1} succ //│ ╙── ^^^^^^ //│ res: error let f = x => log / succ x.prop x.prop f { prop: 42 } //│ f: {prop: int & 'prop} -> 'prop //│ res: 42 // FIXME 'prop' requirement is added twice :e f 42 f { prap: 1 } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: f 42 //│ ║ ^^^^ //│ ╟── integer literal of type `42` does not have field 'prop' //│ ║ l.+1: f 42 //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.328: x.prop //│ ╙── ^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: f { prap: 1 } //│ ║ ^^^^^^^^^^^^^ //│ ╟── record of type `{prap: 1}` does not have field 'prop' //│ ║ l.+2: f { prap: 1 } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.328: x.prop //│ ╙── ^ //│ res: error :e f { prop: false } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: f { prop: false } //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+1: f { prop: false } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.327: log / succ x.prop //│ ║ ^^^^^^ //│ ╟── from field selection: //│ ║ l.327: log / succ x.prop //│ ╙── ^^^^^ //│ res: error | false :e let arg = 0 f arg //│ arg: 0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: f arg //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'prop' //│ ║ l.+1: let arg = 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{prop: ?prop}` //│ ║ l.+2: f arg //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.328: x.prop //│ ╙── ^ //│ res: error :e let arg = {prop: not true} f arg //│ arg: {prop: bool} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: f arg //│ ║ ^^^^^ //│ ╟── application of type `bool` is not an instance of type `int` //│ ║ l.+1: let arg = {prop: not true} //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.327: log / succ x.prop //│ ║ ^^^^^^ //│ ╟── from field selection: //│ ║ l.327: log / succ x.prop //│ ╙── ^^^^^ //│ res: bool | error let g = y => f { prop: y.fld } g { fld: 42 } //│ g: {fld: int & 'a} -> 'a //│ res: 42 :e g 1 g { fld: false } g { fld: { oops: 1 } } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: g 1 //│ ║ ^^^ //│ ╟── integer literal of type `1` does not have field 'fld' //│ ║ l.+1: g 1 //│ ║ ^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.421: f { prop: y.fld } //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.421: f { prop: y.fld } //│ ╙── ^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: g { fld: false } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+2: g { fld: false } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.327: log / succ x.prop //│ ║ ^^^^^^ //│ ╟── from field selection: //│ ║ l.421: f { prop: y.fld } //│ ╙── ^^^^ //│ res: error | false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: g { fld: { oops: 1 } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{oops: 1}` is not an instance of type `int` //│ ║ l.+3: g { fld: { oops: 1 } } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.327: log / succ x.prop //│ ║ ^^^^^^ //│ ╟── from field selection: //│ ║ l.421: f { prop: y.fld } //│ ╙── ^^^^ //│ res: error | {oops: 1} :e let arg1 = {fld: not true} let arg2 = {fld: arg} f arg1 f arg2 //│ arg1: {fld: bool} //│ arg2: {fld: {prop: bool}} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: f arg1 //│ ║ ^^^^^^ //│ ╟── record of type `forall ?a. {fld: ?a}` does not have field 'prop' //│ ║ l.+1: let arg1 = {fld: not true} //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{prop: ?prop}` //│ ║ l.+3: f arg1 //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.328: x.prop //│ ╙── ^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+4: f arg2 //│ ║ ^^^^^^ //│ ╟── record of type `{fld: forall ?a. {prop: ?a}}` does not have field 'prop' //│ ║ l.+2: let arg2 = {fld: arg} //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{prop: ?prop}` //│ ║ l.+4: f arg2 //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.328: x.prop //│ ╙── ^ //│ res: error let h = y => succ / f y succ / h { prop: 42 } //│ h: {prop: int} -> int //│ res: int :e h arg2 h arg h / 42 x => h / succ x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: h arg2 //│ ║ ^^^^^^ //│ ╟── record of type `{fld: forall ?a. {prop: ?a}}` does not have field 'prop' //│ ║ l.472: let arg2 = {fld: arg} //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{prop: ?prop}` //│ ║ l.+1: h arg2 //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.511: succ / f y //│ ╙── ^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: h arg //│ ║ ^^^^^ //│ ╟── application of type `bool` is not an instance of type `int` //│ ║ l.402: let arg = {prop: not true} //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.511: succ / f y //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.328: x.prop //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: h / 42 //│ ║ ^^^^^^ //│ ╟── integer literal of type `42` does not have field 'prop' //│ ║ l.+3: h / 42 //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.511: succ / f y //│ ╙── ^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+4: x => h / succ x //│ ║ ^^^^^^^^^^ //│ ╟── application of type `int` does not have field 'prop' //│ ║ l.+4: x => h / succ x //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.328: x.prop //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.511: succ / f y //│ ╙── ^ //│ res: int -> (error | int) :e let mkArg2 = a => {prop: succ a} h / mkArg2 false //│ mkArg2: int -> {prop: int} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: h / mkArg2 false //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+2: h / mkArg2 false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.+1: let mkArg2 = a => {prop: succ a} //│ ╙── ^ //│ res: int let i = y => succ / f y.fld //│ i: {fld: {prop: int}} -> int :e i arg2 i arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: i arg2 //│ ║ ^^^^^^ //│ ╟── application of type `bool` is not an instance of type `int` //│ ║ l.402: let arg = {prop: not true} //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.593: succ / f y.fld //│ ║ ^^^^^^^ //│ ╟── from field selection: //│ ║ l.328: x.prop //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: i arg //│ ║ ^^^^^ //│ ╟── record of type `forall ?a. {prop: ?a}` does not have field 'fld' //│ ║ l.402: let arg = {prop: not true} //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{fld: ?fld}` //│ ║ l.+2: i arg //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.593: succ / f y.fld //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.593: succ / f y.fld //│ ╙── ^ //│ res: error | int let test x y = if x.prop then i x else y //│ test: {fld: {prop: int}, prop: bool} -> 'a -> (int | 'a) :e test arg2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: test arg2 //│ ║ ^^^^^^^^^ //│ ╟── application of type `bool` is not an instance of type `int` //│ ║ l.402: let arg = {prop: not true} //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.593: succ / f y.fld //│ ║ ^^^^^^^ //│ ╟── from field selection: //│ ║ l.328: x.prop //│ ╙── ^^^^^ //│ res: error | 'a -> (int | 'a) let mkArg = a => {prop: a} h / mkArg 1 i { fld: mkArg 1 } //│ mkArg: 'a -> {prop: 'a} //│ res: int //│ res: int :e g { fld: mkArg 1 } // TODO multi-step flow message? h / mkArg false i { fld: mkArg false } i / mkArg 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: g { fld: mkArg 1 } // TODO multi-step flow message? //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{prop: ?a}` is not an instance of type `int` //│ ║ l.648: let mkArg = a => {prop: a} //│ ║ ^^^^^^^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.+1: g { fld: mkArg 1 } // TODO multi-step flow message? //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.327: log / succ x.prop //│ ║ ^^^^^^ //│ ╟── from field selection: //│ ║ l.421: f { prop: y.fld } //│ ╙── ^^^^ //│ res: error | {prop: 1} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: h / mkArg false //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+2: h / mkArg false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.511: succ / f y //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.328: x.prop //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: i { fld: mkArg false } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.+3: i { fld: mkArg false } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.593: succ / f y.fld //│ ║ ^^^^^^^ //│ ╟── from field selection: //│ ║ l.328: x.prop //│ ╙── ^^^^^ //│ res: error | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+4: i / mkArg 1 //│ ║ ^^^^^^^^^^^ //│ ╟── record of type `{prop: ?a}` does not have field 'fld' //│ ║ l.648: let mkArg = a => {prop: a} //│ ║ ^^^^^^^^^ //│ ╟── but it flows into application with expected type `{fld: ?fld}` //│ ║ l.+4: i / mkArg 1 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.593: succ / f y.fld //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.593: succ / f y.fld //│ ╙── ^ //│ res: error | int // parse error :pe foo ba)r baz //│ /!\ Parse error: Expected end-of-input:2:3, found ")r\nbaz\n" at l.725:3: ba)r ================================================ FILE: shared/src/test/diff/basics/ExplicitDatatypes.fun ================================================ data Left v data Right v //│ Defined class Left[+v] //│ Defined class Right[+v] //│ Left: 'a -> Left['a] //│ Right: 'a -> Right['a] let Either l r = Left l | Right r // TODO actual type parameters //│ Either: 'a -> 'b -> (Left['a] | Right['b]) Either 1 2 //│ res: Left[1] | Right[2] res.v //│ res: 1 | 2 ================================================ FILE: shared/src/test/diff/basics/Flow.fun ================================================ :p data L x data R x //│ Parsed: data L(...x); data R(...x); //│ Desugared: class L[x]: {x: x} //│ Desugared: class R[x]: {x: x} //│ Desugared: def L: forall x. (...x) -> L[x] //│ AST: Def(false,Var(L),Right(PolyType(List(Left(TypeName(x))),Function(TypeName(x),AppliedType(TypeName(L),List(TypeName(x)))))),true) //│ Desugared: def R: forall x. (...x) -> R[x] //│ AST: Def(false,Var(R),Right(PolyType(List(Left(TypeName(x))),Function(TypeName(x),AppliedType(TypeName(R),List(TypeName(x)))))),true) //│ Defined class L[+x] //│ Defined class R[+x] //│ L: 'a -> L['a] //│ R: 'a -> R['a] // TODO flow-type :e let f x = if x is L y then y else 0 //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.19: let f x = if x is L y then y else 0 //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.19: let f x = if x is L y then y else 0 //│ ╙── ^ //│ f: error -> (0 | error) // TODO // true and false // :e // 1 and 2 ================================================ FILE: shared/src/test/diff/basics/Intersections.fun ================================================ // Overloading is not yet really supported... // the simplifier thinks it's an impossible type! let foo = _ as (_: (Int => Int) & (Bool => Bool)) //│ foo: (_: nothing,) :ns let foo = _ as (_: (Int => Int) & (Bool => Bool)) let foo = (_ as (_: (Int => Int) & (Bool => Bool))).0 //│ foo: forall 'a. (_: 'a,) //│ where //│ 'a <: int -> int & bool -> bool //│ foo: forall 'a. 'a foo(1) //│ res: nothing :ns foo(1) //│ res: 'a succ / foo(1) //│ res: int // Intersection-based overloading is not actually supported... a value of this type is impossible to provide: let foo = (Int => Int) & (Bool => Bool) //│ foo: int -> int & bool -> bool :e foo(1) // returns int & bool, equivalent to nothing succ / foo(1) foo(true) not / foo(true) //│ res: bool | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.31: succ / foo(1) //│ ║ ^^^^^^^^^^^^^ //│ ╟── reference of type `bool` is not an instance of type `int` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.31: succ / foo(1) //│ ╙── ^^^^^^ //│ res: error | int //│ res: bool | int //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.33: not / foo(true) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── reference of type `int` is not an instance of type `bool` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^ //│ ╟── but it flows into application with expected type `bool` //│ ║ l.33: not / foo(true) //│ ╙── ^^^^^^^^^ //│ res: bool | error :e not / foo(1) foo(1) as Nothing //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: not / foo(1) //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `int` is not an instance of type `bool` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^ //│ ╟── but it flows into application with expected type `bool` //│ ║ l.58: not / foo(1) //│ ╙── ^^^^^^ //│ res: bool | error //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.59: foo(1) as Nothing //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int` does not match type `nothing` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^ //│ ╟── but it flows into application with expected type `nothing` //│ ║ l.59: foo(1) as Nothing //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.59: foo(1) as Nothing //│ ╙── ^^^^^^^ //│ res: nothing :e foo as Nothing //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.85: foo as Nothing //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type intersection of type `int -> int & bool -> bool` does not match type `nothing` //│ ║ l.26: let foo = (Int => Int) & (Bool => Bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing` //│ ║ l.85: foo as Nothing //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.85: foo as Nothing //│ ╙── ^^^^^^^ //│ res: nothing :e let oops = (&) //│ ╔══[ERROR] Illegal use of reserved operator: & //│ ║ l.101: let oops = (&) //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: & //│ ║ l.101: let oops = (&) //│ ╙── ^^^ //│ oops: error ================================================ FILE: shared/src/test/diff/basics/Iso.fun ================================================ // A file to isolating tests for easier debugging ================================================ FILE: shared/src/test/diff/basics/Lambdas.fun ================================================ let a = succ let x = true //│ a: int -> int //│ x: true x => add (a x) //│ res: int -> int -> int x => add (a x) //│ res: int -> int -> int let x = x => add (a x) //│ x: int -> int -> int let id = v => v //│ id: 'a -> 'a f => f f //│ res: ('a -> 'b & 'a) -> 'b f => id f id f id //│ res: ((forall 'a. 'a -> 'a) -> 'b -> (forall 'a. 'a -> 'a) -> 'c & 'b) -> 'c :pe let oops = hu(h //│ /!\ Parse error: Expected end-of-input:1:14, found "(h\n" at l.29:14: let oops = hu(h x => x; y => y //│ res: 'a -> 'a //│ res: 'a -> 'a :pe x => let y = x; y //│ /!\ Parse error: Expected expression:1:1, found "x => let y" at l.37:1: x => let y = x; y x => (let y = x; y) x => let y = x; y x => let y = x y //│ res: 'a -> 'a //│ res: 'a -> 'a //│ res: 'a -> 'a let f x = x + 1 let f x y = x + y let f x y z = if x then y else z let f x y z = { log x; if y < z then y else z } //│ f: int -> int //│ f: int -> int -> int //│ f: bool -> 'a -> 'a -> 'a //│ f: anything -> (number & 'a) -> (number & 'a) -> 'a // TODO // let f (x: int) = x + 1 // TODO :pe let f / x: int = x + 1 let f / x: int, y: int = x + y //│ /!\ Parse error: Expected (data type definition | data definition | let binding | expression):1:1, found "let f / x:" at l.64:1: let f / x: int = x + 1 // TODO // let f ( // x // y // ) = x + 1 let f(x: int) = x + 1 //│ f: (x: int,) -> int f 42 //│ res: int f (x: 42) //│ res: int :e f (y: 42) //│ ╔══[ERROR] Wrong tuple field name: found 'y' instead of 'x' //│ ║ l.84: f (y: 42) //│ ╙── ^^^^^ //│ res: error | int :e f (x: 42, y: 43) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.91: f (x: 42, y: 43) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── tuple of type `(x: 42, y: 43,)` is not an instance of type `int` //│ ║ l.91: f (x: 42, y: 43) //│ ║ ^^^^^^^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.91: f (x: 42, y: 43) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.74: let f(x: int) = x + 1 //│ ║ ^ //│ ╟── from binding: //│ ║ l.74: let f(x: int) = x + 1 //│ ╙── ^^^^^^ //│ res: error | int (a, b) => a + b //│ res: (int, int,) -> int res(1,2) //│ res: int ================================================ FILE: shared/src/test/diff/basics/Literals.fun ================================================ 1 //│ res: 1 "hello" //│ res: "hello" // TODO literal booleans true //│ res: true 1 as Int "hello" as String true as Bool //│ res: int //│ res: string //│ res: bool :w 1 as int "hello" as string //│ ╔══[WARNING] Variable name 'int' already names a symbol in scope. If you want to refer to that symbol, you can use `scope.int`; if not, give your future readers a break and use another name :^) //│ ║ l.20: 1 as int //│ ╙── ^^^ //│ res: 1 //│ ╔══[WARNING] Variable name 'string' already names a symbol in scope. If you want to refer to that symbol, you can use `scope.string`; if not, give your future readers a break and use another name :^) //│ ║ l.21: "hello" as string //│ ╙── ^^^^^^ //│ res: "hello" 1 as (_: int) "hello" as (_: string) //│ res: (_: 1,) //│ res: (_: "hello",) :e 1 as true true as Int false as 1 //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.37: 1 as true //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `true` //│ ║ l.37: 1 as true //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.37: 1 as true //│ ╙── ^^^^ //│ res: true //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.38: true as Int //│ ║ ^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.38: true as Int //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.38: true as Int //│ ╙── ^^^ //│ res: int //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.39: false as 1 //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `false` does not match type `1` //│ ║ l.39: false as 1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.39: false as 1 //│ ╙── ^ //│ res: 1 let f = b => if b then 0 else 1 //│ f: bool -> (0 | 1) let pred = n => 0 < n //│ pred: number -> bool let g = x => if pred x then x else f false //│ g: (number & 'a) -> (0 | 1 | 'a) g 3 //│ res: 0 | 1 | 3 g / succ 3 //│ res: int x => if x then x else f false //│ res: (bool & 'a) -> (0 | 1 | 'a) res false //│ res: 0 | 1 | false let rec f = n => if pred n then n else f (n + 1) //│ f: int -> int let g = n => if pred n then 0 else if not (pred n) then 1 else f n //│ g: int -> int x => if pred x then x else f x //│ res: int -> int :e f false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.104: f false //│ ║ ^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.104: f false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.93: if pred n then n else f (n + 1) //│ ╙── ^ //│ res: error | false | int let take0 (x: 0) = 0 let take1 (x: 1) = 1 //│ take0: (x: 0,) -> 0 //│ take1: (x: 1,) -> 1 let takeWhat y = if y < 0 then take0 y else take1 y //│ takeWhat: nothing -> (0 | 1) let takeWhat y = if y < 0 then take0 (x: y) else take1 (x: y) //│ takeWhat: nothing -> (0 | 1) ================================================ FILE: shared/src/test/diff/basics/Negations.fun ================================================ data Wine //│ Defined class Wine //│ Wine: Wine let jesus = neg Wine => Wine //│ jesus: ~Wine -> Wine let w = jesus(water: "Evian") //│ w: Wine let jesus = (water: neg Wine) => Wine //│ jesus: (water: ~Wine,) -> Wine let w = jesus(water: "Evian") //│ w: Wine :e jesus w jesus(water: w) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.21: jesus w //│ ║ ^^^^^^^ //│ ╟── reference of type `Wine` does not match type `~Wine` //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `~Wine` //│ ║ l.21: jesus w //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ║ ^^^^^^^^ //│ ╟── from binding: //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ╙── ^^^^^^^^^^^^^^^ //│ res: Wine | error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: jesus(water: w) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── reference of type `Wine` does not match type `~Wine` //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `~Wine` //│ ║ l.22: jesus(water: w) //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.14: let jesus = (water: neg Wine) => Wine //│ ╙── ^^^^^^^^ //│ res: Wine | error (0 | 1) & neg 0 //│ res: 1 (0 | 1) & neg 0 as 1 //│ res: 1 :e (0 | 1) & neg 0 as 0 //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.61: (0 | 1) & neg 0 as 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type intersection of type `1` does not match type `0` //│ ║ l.61: (0 | 1) & neg 0 as 0 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.61: (0 | 1) & neg 0 as 0 //│ ╙── ^ //│ res: 0 (0 | 1) & neg 0 & neg 1 as "wat" //│ res: "wat" :e neg 0 as 1 //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.77: neg 0 as 1 //│ ║ ^^^^^^^^^^ //│ ╟── application of type `~0` does not match type `1` //│ ║ l.77: neg 0 as 1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.77: neg 0 as 1 //│ ╙── ^ //│ res: 1 1 as neg 0 //│ res: ~0 ================================================ FILE: shared/src/test/diff/basics/Operators.fun ================================================ let a = 1 let b = 2 let c = 3 let d = 4 //│ a: 1 //│ b: 2 //│ c: 3 //│ d: 4 a + b //│ res: int :p a + b //│ Parsed: +(...a)(...{b}); //│ Desugared: +(...a)(...{b}) //│ AST: App(App(Var(+),Var(a)),Blk(List(Var(b)))) //│ res: int :pe a + b + c //│ /!\ Parse error: Expected expression:1:1, found "a +\n b +\n" at l.23:1: a + :p succ a + b + c //│ Parsed: +(...(succ(...a)))(...{+(...b)(...{c})}); //│ Desugared: +(...(succ(...a)))(...{+(...b)(...{c})}) //│ AST: App(App(Var(+),App(Var(succ),Var(a))),Blk(List(App(App(Var(+),Var(b)),Blk(List(Var(c))))))) //│ res: int :p succ / a + b + c //│ Parsed: succ(...(+(...a)(...{+(...b)(...{c})}))); //│ Desugared: succ(...(+(...a)(...{+(...b)(...{c})}))) //│ AST: App(Var(succ),App(App(Var(+),Var(a)),Blk(List(App(App(Var(+),Var(b)),Blk(List(Var(c)))))))) //│ res: int :p a + b a + b + c a + b + c + d //│ Parsed: +(...a)(...b); +(...(+(...a)(...b)))(...c); +(...(+(...(+(...a)(...b)))(...c)))(...d); //│ Desugared: +(...a)(...b) //│ AST: App(App(Var(+),Var(a)),Var(b)) //│ Desugared: +(...(+(...a)(...b)))(...c) //│ AST: App(App(Var(+),App(App(Var(+),Var(a)),Var(b))),Var(c)) //│ Desugared: +(...(+(...(+(...a)(...b)))(...c)))(...d) //│ AST: App(App(Var(+),App(App(Var(+),App(App(Var(+),Var(a)),Var(b))),Var(c))),Var(d)) //│ res: int //│ res: int //│ res: int :p a + b + c + 1 + 2 + 3 + d //│ Parsed: +(...(+(...(+(...a)(...b)))(...(+(...(+(...c)(...1)))(...(+(...2)(...3)))))))(...d); //│ Desugared: +(...(+(...(+(...a)(...b)))(...(+(...(+(...c)(...1)))(...(+(...2)(...3)))))))(...d) //│ AST: App(App(Var(+),App(App(Var(+),App(App(Var(+),Var(a)),Var(b))),App(App(Var(+),App(App(Var(+),Var(c)),IntLit(1))),App(App(Var(+),IntLit(2)),IntLit(3))))),Var(d)) //│ res: int :pe a + b //│ /!\ Parse error: Expected end-of-input:2:1, found "+ b\n" at l.82:1: + b :pe let x = 1 + 2 //│ /!\ Parse error: Expected end-of-input:2:1, found "+ 2\n" at l.87:1: + 2 let x = 1 + 2 //│ x: int :pe let x = 1 + 2 //│ /!\ Parse error: Expected end-of-input:3:3, found "+ 2\n" at l.97:3: + 2 let x = 1 + 2 //│ x: int succ / succ 1 + 1 //│ res: int :p succ a + b + c //│ Parsed: +(...(succ(...{+(...a)(...b)})))(...c); //│ Desugared: +(...(succ(...{+(...a)(...b)})))(...c) //│ AST: App(App(Var(+),App(Var(succ),Blk(List(App(App(Var(+),Var(a)),Var(b)))))),Var(c)) //│ res: int // Maybe allow this as it lets us nicely align the operands? :pe let test = a + b + c //│ /!\ Parse error: Expected end-of-input:3:3, found "+ b\n + c\n" at l.122:3: + b ================================================ FILE: shared/src/test/diff/basics/RecFuns.fun ================================================ // From a comment on the Simple-sub blog post: let rec r a = r //│ r: 'r //│ where //│ 'r :> anything -> 'r let join a b = if true then a else b //│ join: 'a -> 'a -> 'a // "Lateral" hash consing let s = join r r //│ s: 'r //│ where //│ 'r :> anything -> 'r ================================================ FILE: shared/src/test/diff/basics/Records.fun ================================================ let empty = {} //│ empty: anything 1 (1) ((1)) {1} {{1}} {(1)} //│ res: 1 //│ res: 1 //│ res: 1 //│ res: 1 //│ res: 1 //│ res: 1 x : 1 x: 1 {x: 1} x: 1, y: 2 //│ res: (x: 1,) //│ res: (x: 1,) //│ res: {x: 1} //│ res: (x: 1, y: 2,) x : 1, 2, 3 x: 1, 2, z: 3 1, y: 2, z: 3 x: 1, y: 2, z: 3 //│ res: (x: 1, 2, 3,) //│ res: (x: 1, 2, z: 3,) //│ res: (1, y: 2, z: 3,) //│ res: (x: 1, y: 2, z: 3,) let r = {u:1,v:2} let r = { u:1 , v:2 } let r = { u :1 , v :2 } let r = {u: 1, v: 2} let r = { u: 1, v: 2 } let r = { u: 1,v: 2 } //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} r.u + r.v r . u + r . v r .u + r .v r. u + r. v //│ res: int //│ res: int //│ res: int //│ res: int :e empty.w r.w //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.59: empty.w //│ ║ ^^ //│ ╟── tuple of type `anything` does not have field 'w' //│ ║ l.2: let empty = {} //│ ║ ^^ //│ ╟── but it flows into reference with expected type `{w: ?w}` //│ ║ l.59: empty.w //│ ╙── ^^^^^ //│ res: error //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.60: r.w //│ ║ ^^ //│ ╟── record of type `{u: 1, v: 2}` does not have field 'w' //│ ║ l.41: let r = { u: 1,v: 2 } //│ ║ ^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{w: ?w}` //│ ║ l.60: r.w //│ ╙── ^ //│ res: error let rec sumHeads = x => x.head + sumHeads x.tail //│ sumHeads: 'a -> int //│ where //│ 'a <: {head: int, tail: 'a} let rec ouroboros = {head: 0, tail: ouroboros, eyes: {l: 1, r: 2}} //│ ouroboros: 'ouroboros //│ where //│ 'ouroboros :> {eyes: {l: 1, r: 2}, head: 0, tail: 'ouroboros} sumHeads ouroboros //│ res: int let r = { u: 1, v: 2 } let r = { u: 1, v: 2, } let r = { u: 1 v: 2 } let r = { u: 1, v: 2 } let r = { u: 1; v: 2 } let r = { u: 1 v: u + 1 } //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: 1, v: int} :pe let r = { u: 1; v: 2; } //│ /!\ Parse error: Expected let binding:1:1, found "let r = {\n" at l.125:1: let r = { let r = { u: 1 v: 2 } let r = { u: log "ok" 1 v: log "ko" 2 } let r = { u: let x = 1 x v: let y = 2 y } //│ r: {u: 1, v: 2} //│ r: {u: 1, v: 2} //│ r: {u: {x: 1}, v: {y: 2}} // ^ FIXME? field punning... // TODO let r = { u: x: 1 y: 2 v: x: 3 y: 4 } //│ r: {u: {x: 1, y: 2}, v: {x: 3, y: 4}} :w // Disallow or warn about this? let r = { u: 1, v: 2 } //│ ╔══[WARNING] Missing name for record field //│ ║ l.171: 1, v: 2 } //│ ╙── ^ //│ r: {u: {_1: 1, v: 2}} // :e // used to raise: useless fields in statement position let r = u: x: 1 y: 2 v: x: 3 y: 4 let r = ( u: x: 1 y: 2 v: x: 3 y: 4 ) let r = ( u: ( x: 1 y: 2 ), v: x: 3 y: 4 ) //│ r: (u: (x: 1, y: 2,), v: (x: 3, y: 4,),) //│ r: (u: (x: 1, y: 2,), v: (x: 3, y: 4,),) //│ r: (u: (x: 1, y: 2,), v: (x: 3, y: 4,),) let r = ( u: ( x: 1, y: 2, ), v: x: 3 y: 4 ) let r = ( u: ( x: 1, y: 2, ), v: ( x: 3, y: 4, ), ) //│ r: (u: (x: 1, y: 2,), v: (x: 3, y: 4,),) //│ r: (u: (x: 1, y: 2,), v: (x: 3, y: 4,),) let r = ( u: x: 1, y: 2,, v: x: 3, y: 4, ) //│ r: (u: (x: 1, y: 2,), v: (x: 3, y: 4,),) :pe let r = ( u: x: 1, y: 2, , v: x: 3, y: 4, ) //│ /!\ Parse error: Expected let binding:1:1, found "let r = (\n" at l.239:1: let r = ( a: b: c: 1 a: { b: c: 1 d: 2 } a: b: { c: 1 } d: 2 //│ res: (a: (b: (c: 1,),),) //│ res: (a: {b: {c: 1}, d: 2},) //│ res: (a: (b: {c: 1}, d: 2,),) // :e // used to raise: useless fields in statement position a: b: c: 1 d: 2 //│ res: (a: (b: (c: 1,), d: 2,),) :w a: b: 1 c: 2 3 a: { b: 1 c: 2 3 } //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. //│ ║ l.282: 3 //│ ╙── ^ //│ res: (a: 3,) //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. //│ ║ l.286: 3 //│ ╙── ^ //│ res: (a: 3,) let r = x: 1 log x y: 2 let r = x: 1 log x y: 2 let _ = log y //│ r: (x: 1, y: 2,) //│ r: (x: 1, y: 2,) // TODO ignore return-position unit expressions? :w let r = x: 1 log x y: 2 log y //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. //│ ║ l.315: log y //│ ╙── ^^^^^ //│ r: unit // Funnily, because of dependent record literals, one can do: :w let res = arg: 0 arg + 1 //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. //│ ║ l.326: arg + 1 //│ ╙── ^^^^^^^ //│ res: int ================================================ FILE: shared/src/test/diff/basics/Simplesub1.fun ================================================ // Tests ported from Simplesub :ShowRelativeLineNums // --- basic --- // 42 //│ res: 42 x => 42 //│ res: anything -> 42 x => x //│ res: 'a -> 'a x => x 42 //│ res: (42 -> 'a) -> 'a (x => x) 42 //│ res: 42 f => x => f (f x) // twice //│ res: ('a -> 'b & 'b -> 'c) -> 'a -> 'c let twice = f => x => f (f x) //│ twice: ('a -> 'b & 'b -> 'c) -> 'a -> 'c // --- booleans --- // true //│ res: true not true //│ res: bool x => not x //│ res: bool -> bool (x => not x) true //│ res: bool x => y => z => if x then y else z //│ res: bool -> 'a -> 'a -> 'a x => y => if x then y else x //│ res: (bool & 'a) -> 'a -> 'a :e succ true //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: succ true //│ ║ ^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.+1: succ true //│ ╙── ^^^^ //│ res: error | int :e x => succ (not x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: x => succ (not x) //│ ║ ^^^^^^^^^^^^ //│ ╟── application of type `bool` is not an instance of type `int` //│ ║ l.+1: x => succ (not x) //│ ║ ^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: x => succ (not x) //│ ╙── ^^^^^^^ //│ res: bool -> (error | int) :e (x => not x.f) { f: 123 } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (x => not x.f) { f: 123 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `123` is not an instance of type `bool` //│ ║ l.+1: (x => not x.f) { f: 123 } //│ ║ ^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.+1: (x => not x.f) { f: 123 } //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.+1: (x => not x.f) { f: 123 } //│ ╙── ^^ //│ res: bool | error :e (f => x => not (f x.u)) false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (f => x => not (f x.u)) false //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not a function //│ ║ l.+1: (f => x => not (f x.u)) false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.+1: (f => x => not (f x.u)) false //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.+1: (f => x => not (f x.u)) false //│ ╙── ^ //│ res: error | {u: anything} -> bool // --- records --- // x => x.f //│ res: {f: 'f} -> 'f // note: MLsub returns "⊤" (equivalent) {} //│ res: anything { f: 42 } //│ res: {f: 42} { f: 42 }.f //│ res: 42 (x => x.f) { f: 42 } //│ res: 42 f => { x: f 42 }.x //│ res: (42 -> 'x) -> 'x f => { x: f 42, y: 123 }.y //│ res: (42 -> anything) -> 123 if true then { a: 1, b: true } else { b: false, c: 42 } //│ res: {b: Bool} :e { a: 123, b: true }.c //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: { a: 123, b: true }.c //│ ║ ^^ //│ ╟── record of type `{a: 123, b: true}` does not have field 'c' //│ ║ l.+1: { a: 123, b: true }.c //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ res: error :e x => { a: x }.b //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: x => { a: x }.b //│ ║ ^^ //│ ╟── record of type `{a: ?a}` does not have field 'b' //│ ║ l.+1: x => { a: x }.b //│ ╙── ^^^^^^^^ //│ res: anything -> error // --- self-app --- // x => x x //│ res: ('a -> 'b & 'a) -> 'b res id //│ res: 'a -> 'a let f = (x => x + 1); {a: f; b: f 2} //│ f: int -> int //│ res: {a: int -> int, b: int} x => x x x //│ res: ('a -> 'a -> 'b & 'a) -> 'b x => y => x y x //│ res: ('a -> 'b -> 'c & 'b) -> 'a -> 'c x => y => x x y //│ res: ('a -> 'b -> 'c & 'a) -> 'b -> 'c :e // Omega: causes divergence in first-class-polymorphic type inference, as expected (x => x x) (x => x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.+1: (x => x x) (x => x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error x => {l: x x, r: x } //│ res: ('a -> 'b & 'a) -> {l: 'b, r: 'a} // * From https://github.com/stedolan/mlsub // * Y combinator: :e // similarly to Omega (f => (x => f (x x)) (x => f (x x))) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.+1: (f => (x => f (x x)) (x => f (x x))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: ('a -> 'a) -> (error | 'a) // * Z combinator: :e (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.+1: (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: (('a -> 'b) -> 'c & ('d -> 'e) -> ('d -> 'e & 'a -> 'b)) -> (error | 'c) // * Function that takes arbitrarily many arguments: :e (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) (f => x => f) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.+1: (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) (f => x => f) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error | anything -> anything -> anything -> 'a //│ where //│ 'a :> forall 'a. anything -> 'a res 1 2 //│ res: error | anything -> 'a //│ where //│ 'a :> forall 'a. anything -> 'a let rec trutru = g => trutru (g true) //│ trutru: 'a -> nothing //│ where //│ 'a <: true -> 'a i => if ((i i) true) then true else true //│ res: ('a -> true -> bool & 'a) -> true // ^ for: λi. if ((i i) true) then true else true, // Dolan's thesis says MLsub infers: (α → ((bool → bool) ⊓ α)) → bool // which does seem equivalent, despite being quite syntactically different // --- let-poly --- // let f = x => x; {a: f 0, b: f true} //│ f: 'a -> 'a //│ res: {a: 0, b: true} y => (let f = x => x; {a: f y, b: f true}) //│ res: 'a -> {a: 'a, b: true} y => (let f = x => y x; {a: f 0, b: f true}) //│ res: ((0 | true) -> 'a) -> {a: 'a, b: 'a} y => (let f = x => x y; {a: f (z => z), b: f (z => true)}) //│ res: 'a -> {a: 'a, b: true} y => (let f = x => x y; {a: f (z => z), b: f (z => succ z)}) //│ res: (int & 'a) -> {a: 'a, b: int} // --- recursion --- // let rec f = x => f x.u //│ f: 'a -> nothing //│ where //│ 'a <: {u: 'a} // from https://www.cl.cam.ac.uk/~sd601/mlsub/ let rec recursive_monster = x => { thing: x, self: recursive_monster x } //│ recursive_monster: 'a -> 'b //│ where //│ 'b :> {self: 'b, thing: 'a} // --- random --- // (let rec x = {a: x, b: x}; x) //│ res: 'x //│ where //│ 'x :> {a: 'x, b: 'x} (let rec x = v => {a: x v, b: x v}; x) //│ res: anything -> 'a //│ where //│ 'a :> {a: 'a, b: 'a} :e let rec x = (let rec y = {u: y, v: (x y)}; 0); 0 //│ ╔══[ERROR] Type mismatch in binding of block of statements: //│ ║ l.+1: let rec x = (let rec y = {u: y, v: (x y)}; 0); 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: let rec x = (let rec y = {u: y, v: (x y)}; 0); 0 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.+1: let rec x = (let rec y = {u: y, v: (x y)}; 0); 0 //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.+1: let rec x = (let rec y = {u: y, v: (x y)}; 0); 0 //│ ╙── ^ //│ x: 0 //│ res: 0 (x => (let y = (x x); 0)) //│ res: ('a -> anything & 'a) -> 0 // TODO simplify more? (let rec x = (y => (y (x x))); x) //│ res: 'a -> 'b //│ where //│ 'a <: 'b -> 'b //│ 'b <: 'a :e // * Note: this works with precise-rec-typing (see below) res (z => (z, z)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: res (z => (z, z)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── tuple of type `(?a, ?a,)` is not a function //│ ║ l.+1: res (z => (z, z)) //│ ║ ^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.318: (let rec x = (y => (y (x x))); x) //│ ╙── ^^^^^^^ //│ res: error | 'a //│ where //│ 'a :> ('a, 'a,) :precise-rec-typing (let rec x = (y => (y (x x))); x) //│ res: (nothing -> 'a) -> 'a res (z => (z, z)) //│ res: (nothing, nothing,) next => 0 //│ res: anything -> 0 ((x => (x x)) (x => x)) //│ res: 'a -> 'a (let rec x = (y => (x (y y))); x) //│ res: 'a -> nothing //│ where //│ 'a <: 'a -> 'a x => (y => (x (y y))) //│ res: ('a -> 'b) -> ('c -> 'a & 'c) -> 'b (let rec x = (let y = (x x); (z => z)); x) //│ res: 'a -> 'a //│ where //│ 'a :> 'a -> 'a (let rec x = (y => (let z = (x x); y)); x) //│ res: 'a -> 'a //│ where //│ 'a :> 'a -> 'a (let rec x = (y => {u: y, v: (x x)}); x) //│ res: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ 'b :> {u: 'a, v: 'b} (let rec x = (y => {u: (x x), v: y}); x) //│ res: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ 'b :> {u: 'b, v: 'a} (let rec x = (y => (let z = (y x); y)); x) //│ res: 'x //│ where //│ 'x :> 'a -> 'a //│ 'a <: 'x -> anything (x => (let y = (x x.v); 0)) //│ res: ('v -> anything & {v: 'v}) -> 0 let rec x = (let y = (x x); (z => z)); (x (y => y.u)) // [test:T1] //│ x: 'a -> 'a //│ where //│ 'a :> 'a -> 'a //│ res: ({u: 'u} & 'a) -> ('u | 'a) | 'b //│ where //│ 'a :> forall 'u. ({u: 'u} & 'a) -> ('a | 'u) //│ <: 'b :ns let rec x = (let y = (x x); (z => z)) //│ x: forall 'x 'a 'b. 'x //│ where //│ 'x := 'b -> 'b //│ 'b :> 'b -> 'b //│ <: 'a //│ 'a :> 'b -> 'b // Converges under normal-order reduction, but type inference follows more of an applicative order: :e (w => x => x) ((y => y y) (y => y y)) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.+1: (w => x => x) ((y => y y) (y => y y)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: 'a -> 'a // * === With Constrained Types === :DontDistributeForalls :ConstrainedTypes // * Z combinator: // :e // Works thanks to inconsistent constrained types... (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) //│ res: ((forall 'a 'b. 'a -> 'b //│ where //│ forall 'c 'd. 'c -> 'd //│ where //│ 'e <: (forall 'f 'g. 'f -> 'g //│ where //│ 'c <: 'c -> 'f -> 'g) -> 'd <: (forall 'c 'd. 'c -> 'd //│ where //│ 'e <: (forall 'f 'g. 'f -> 'g //│ where //│ 'c <: 'c -> 'f -> 'g) -> 'd) -> 'a -> 'b) -> 'h & 'e) -> 'h // * Function that takes arbitrarily many arguments: // :e // Works thanks to inconsistent constrained types... (f => (x => f (v => (x x) v)) (x => f (v => (x x) v))) (f => x => f) //│ res: anything -> (forall 'a 'b. 'a -> 'b //│ where //│ forall 'c 'd. 'c -> 'd //│ where //│ forall 'e. 'e -> anything -> 'e <: (forall 'f 'g. 'f -> 'g //│ where //│ 'c <: 'c -> 'f -> 'g) -> 'd <: (forall 'c 'd. 'c -> 'd //│ where //│ forall 'e. 'e -> anything -> 'e <: (forall 'f 'g. 'f -> 'g //│ where //│ 'c <: 'c -> 'f -> 'g) -> 'd) -> 'a -> 'b) :NoCycleCheck // Exceeds recursion depth limit: // :e // (w => x => x) ((y => y y) (y => y y)) ================================================ FILE: shared/src/test/diff/basics/Simplesub2.fun ================================================ // Tests ported from Simplesub // --- mlsub --- // let id = x => x //│ id: 'a -> 'a let twice = f => x => f (f x) //│ twice: ('a -> 'b & 'b -> 'c) -> 'a -> 'c let object1 = { x: 42, y: id } //│ object1: {x: 42, y: forall 'a. 'a -> 'a} let object2 = { x: 17, y: false } //│ object2: {x: 17, y: false} let pick_an_object = b => if b then object1 else object2 //│ pick_an_object: bool -> {x: 17 | 42, y: forall 'a. false | 'a -> 'a} let rec recursive_monster = x => { thing: x, self: recursive_monster x } //│ recursive_monster: 'a -> 'b //│ where //│ 'b :> {self: 'b, thing: 'a} // --- top-level-polymorphism --- // let id = x => x //│ id: 'a -> 'a let ab = {u: id 0, v: id true} //│ ab: {u: 0, v: true} // --- rec-producer-consumer --- // let rec produce = arg => { head: arg, tail: produce (succ arg) } let rec consume = strm => add strm.head (consume strm.tail) //│ produce: int -> 'a //│ where //│ 'a :> {head: int, tail: 'a} //│ consume: 'a -> int //│ where //│ 'a <: {head: int, tail: 'a} let codata = produce 42 let res = consume codata //│ codata: 'a //│ where //│ 'a :> {head: int, tail: 'a} //│ res: int let rec codata2 = { head: 0, tail: { head: 1, tail: codata2 } } let res = consume codata2 //│ codata2: 'codata2 //│ where //│ 'codata2 :> {head: 0, tail: {head: 1, tail: 'codata2}} //│ res: int // TODO better parser error :pe let rec produce3 = b => { head: 123, tail: if b then codata else codata2 } //│ /!\ Parse error: Expected let binding:1:1, found "let rec pr" at l.71:1: let rec produce3 = b => { head: 123, tail: if b then codata else codata2 } let rec produce3 = b => { head: 123, tail: (if b then codata else codata2) } let res = x => consume (produce3 x) //│ produce3: bool -> {head: 123, tail: forall 'codata2 'a. 'codata2 | 'a} //│ where //│ 'a :> {head: int, tail: 'a} //│ 'codata2 :> {head: 0, tail: {head: 1, tail: 'codata2}} //│ res: bool -> int let consume2 = let rec go = strm => add strm.head (add strm.tail.head (go strm.tail.tail)) strm => add strm.head (go strm.tail) // go // let rec consume2 = strm => add strm.head (add strm.tail.head (consume2 strm.tail.tail)) let res = consume2 codata2 //│ consume2: 'a -> int //│ where //│ 'a <: {head: int, tail: 'a} //│ res: int ================================================ FILE: shared/src/test/diff/basics/Slashes.fun ================================================ succ / 1 succ / succ / 1 //│ res: int //│ res: int let foo = f => f 1 //│ foo: (1 -> 'a) -> 'a foo / x => x //│ res: 1 foo / x => succ x //│ res: int x => succ / x + 1 //│ res: int -> int x => succ / succ / x + 1 //│ res: int -> int :p foo / x => succ / succ / x //│ Parsed: foo(...((...x) => succ(...(succ(...x))))); //│ Desugared: foo(...((...x) => succ(...(succ(...x))))) //│ AST: App(Var(foo),Lam(Var(x),App(Var(succ),App(Var(succ),Var(x))))) //│ res: int :e foo / foo / x => succ / succ / x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.30: foo / foo / x => succ / succ / x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `int` is not a function //│ ║ l.30: foo / foo / x => succ / succ / x //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `1 -> ?a` //│ ║ l.30: foo / foo / x => succ / succ / x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.7: let foo = f => f 1 //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.7: let foo = f => f 1 //│ ╙── ^ //│ res: error :e foo / foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.49: foo / foo //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.7: let foo = f => f 1 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.7: let foo = f => f 1 //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.7: let foo = f => f 1 //│ ╙── ^ //│ res: error ================================================ FILE: shared/src/test/diff/basics/Tuples.fun ================================================ let t3 = 1, 2, 3 let t3 = 1, 2, 3, let t2 = 1, 2, let t1 = 1, let t0 = () //│ t3: (1, 2, 3,) //│ t3: (1, 2, 3,) //│ t2: (1, 2,) //│ t1: (1,) //│ t0: () let t = 1, y: 2, 3 let t = x: 1, y: 2, z: 3 //│ t: (1, y: 2, 3,) //│ t: (x: 1, y: 2, z: 3,) (1, true, "hey").1 (1, true, "hey").2 //│ res: true //│ res: "hey" :e (1, true, "hey").3 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.24: (1, true, "hey").3 //│ ║ ^^ //│ ╟── tuple of type `{0: 1, 1: true, 2: "hey"}` does not have field '3' //│ ║ l.24: (1, true, "hey").3 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into receiver with expected type `{3: ?a}` //│ ║ l.24: (1, true, "hey").3 //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: error (1, true, "hey").1 //│ res: true :w let not-tup = ( 1 2 ) //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.41: 1 //│ ╙── ^ //│ not-tup: 2 :w let tup = ( 1, 2 ) //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. //│ ║ l.52: 2 //│ ╙── ^ //│ tup: 2 :w let tup = 1, 2, 3 //│ ╔══[WARNING] Previous field definitions are discarded by this returned expression. //│ ║ l.63: 3 //│ ╙── ^ //│ tup: 3 let tup = 1, 2, 3, //│ tup: (1, 2, 3,) ================================================ FILE: shared/src/test/diff/basics/Twice.fun ================================================ let twice f x = f / f x //│ twice: ('a -> 'b & 'b -> 'c) -> 'a -> 'c // Note: the pretty-printed type of `twice` *used to be* simplified to ('a -> ('a & 'b)) -> 'a -> 'b // (another equivalent simplification is ('a | 'b -> 'a) -> 'b -> 'a); // this simplification lost some information in the context of first-class polymorphism // because function types effectively become non-mergeable without losing precsion... // (Also see this HN thread: https://news.ycombinator.com/item?id=13783237) twice(x => x + 1) //│ res: int -> int twice twice //│ res: ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c let f = x => 1, x //│ f: 'a -> (1, 'a,) // Note: now that we instantiate during constraint solving instead of on variable reference, // we get the more useful type: 'a -> (1, (1, 'a,),). // Previously, we were getting: 'a -> ((1, 'c | 'b | 'a,) as 'b) twice f //│ res: 'a -> (1, (1, 'a,),) twice / x => x, x //│ res: 'a -> (('a, 'a,), ('a, 'a,),) let one = twice (o => o.x) { x: { x: 1 } } //│ one: 1 ================================================ FILE: shared/src/test/diff/basics/Unions.fun ================================================ let f(x: 0 | 1) = x //│ f: (x: 'a & (0 | 1),) -> 'a let f(x: 0 | 1) = succ x //│ f: (x: 0 | 1,) -> int let f(x) = x as 0 | 1 //│ f: (0 | 1) -> (0 | 1) f 1 f 0 f(0 as 0 | 1) //│ res: 0 | 1 //│ res: 0 | 1 //│ res: 0 | 1 :e f 3 f (0 as 1 | 3) f (0 as 0 | 3) f (0 as 3 | 4) f (0 as Int) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.20: f 3 //│ ║ ^^^ //│ ╟── integer literal of type `3` does not match type `0 | 1` //│ ║ l.20: f 3 //│ ║ ^ //│ ╟── Note: constraint arises from type union: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ╙── ^ //│ res: 0 | 1 | error //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.21: f (0 as 1 | 3) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` does not match type `1 | 3` //│ ║ l.21: f (0 as 1 | 3) //│ ║ ^ //│ ╟── Note: constraint arises from type union: //│ ║ l.21: f (0 as 1 | 3) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.21: f (0 as 1 | 3) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── integer literal of type `3` does not match type `0 | 1` //│ ║ l.21: f (0 as 1 | 3) //│ ║ ^ //│ ╟── but it flows into type union with expected type `0 | 1` //│ ║ l.21: f (0 as 1 | 3) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type union: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ╙── ^ //│ res: 0 | 1 | error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: f (0 as 0 | 3) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── integer literal of type `3` does not match type `0 | 1` //│ ║ l.22: f (0 as 0 | 3) //│ ║ ^ //│ ╟── but it flows into type union with expected type `0 | 1` //│ ║ l.22: f (0 as 0 | 3) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type union: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ╙── ^ //│ res: 0 | 1 | error //│ ╔══[ERROR] Type mismatch in 'as' binding: //│ ║ l.23: f (0 as 3 | 4) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` does not match type `3 | 4` //│ ║ l.23: f (0 as 3 | 4) //│ ║ ^ //│ ╟── Note: constraint arises from type union: //│ ║ l.23: f (0 as 3 | 4) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.23: f (0 as 3 | 4) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── integer literal of type `3` does not match type `0 | 1` //│ ║ l.23: f (0 as 3 | 4) //│ ║ ^ //│ ╟── but it flows into type union with expected type `0 | 1` //│ ║ l.23: f (0 as 3 | 4) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type union: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ╙── ^ //│ res: 0 | 1 | error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.24: f (0 as Int) //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `int` does not match type `0 | 1` //│ ║ l.24: f (0 as Int) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `0 | 1` //│ ║ l.24: f (0 as Int) //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from type union: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.9: let f(x) = x as 0 | 1 //│ ╙── ^ //│ res: 0 | 1 | error let g(x: int) = succ x g 0 g (0 as 0 | 1) let h = y => g(y as 0 | 1) h(0) //│ g: (x: int,) -> int //│ res: int //│ res: int //│ h: (0 | 1) -> int //│ res: int let foo(r: { v: 0 } | { v: 1 }) = if r.v < 1 then r.v else 2 //│ foo: (r: {v: 'a & (0 | 1)},) -> (2 | 'a) foo({ v: 0 }) foo({ v: 1 }) //│ res: 0 | 2 //│ res: 1 | 2 x => foo(x) //│ res: (r: {v: 'a & (0 | 1)},) -> (2 | 'a) x => foo { v: x } //│ res: ('a & (0 | 1)) -> (2 | 'a) // Notice that in MLscript, `(0, 0) | (1, 1)` is equivalent to `(0 | 1, 0 | 1)` let bar(r: (0, 0) | (1, 1)) = if r.0 < 1 then r.0 else r.1 //│ bar: (r: ('a & (0 | 1), 'a & (0 | 1),),) -> 'a bar(0, 1) //│ res: 0 | 1 :e bar(2, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.155: bar(2, 2) //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `2` does not match type `0 | 1` //│ ║ l.155: bar(2, 2) //│ ╙── ^ //│ res: 2 | error bar(0, 0) bar(1, 1) bar(0, _) bar(_, 1) //│ res: 0 //│ res: 1 //│ res: 0 //│ res: 1 let f x = bar(x, x) //│ f: ('a & (0 | 1)) -> 'a f 0 f 1 //│ res: 0 //│ res: 1 :e f 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.182: f 2 //│ ║ ^^^ //│ ╟── integer literal of type `2` does not match type `0 | 1` //│ ║ l.182: f 2 //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.173: let f x = bar(x, x) //│ ╙── ^ //│ res: 2 | error x => bar(1, x) x => bar(x, 0) //│ res: ('a & (0 | 1)) -> (1 | 'a) //│ res: ('a & (0 | 1)) -> (0 | 'a) bar(_, _) (x, y) => bar(x, y) //│ res: nothing //│ res: ('a & (0 | 1), 'a & (0 | 1),) -> 'a // ^ TODO allow explicit request for inferring an overloaded type in case of ambiguities x => bar(bar(0, x), 0) x => bar(bar(x, x), 0) x => bar(bar(0, x), x) x => bar(bar(x, x), 0) //│ res: ('a & (0 | 1)) -> (0 | 'a) //│ res: ('a & (0 | 1)) -> (0 | 'a) //│ res: ('a & (0 | 1)) -> (0 | 'a) //│ res: ('a & (0 | 1)) -> (0 | 'a) x => bar(bar(x, 1), 0) (x, y) => bar(bar(x, y), x) //│ res: ('a & (0 | 1)) -> (0 | 1 | 'a) //│ res: ('a & (0 | 1), 'a & (0 | 1),) -> 'a (x, y) => bar(bar(x, y), 0) //│ res: ('a & (0 | 1), 'a & (0 | 1),) -> (0 | 'a) let baz(r: (0, 0) | _) = if r.0 < 1 then r.0 else r.1 //│ baz: (r: {0: number & 'a, 1: 'a},) -> 'a :e baz(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.228: baz(0) //│ ║ ^^^^^^ //│ ╟── integer literal of type `0` does not have field '1' //│ ║ l.228: baz(0) //│ ║ ^ //│ ╟── but it flows into argument with expected type `{1: ?a}` //│ ║ l.228: baz(0) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.224: let baz(r: (0, 0) | _) = if r.0 < 1 then r.0 else r.1 //│ ║ ^^ //│ ╟── from binding: //│ ║ l.224: let baz(r: (0, 0) | _) = if r.0 < 1 then r.0 else r.1 //│ ╙── ^^^^^^^^^^^^^ //│ res: error baz(0, 0) baz(0, 1) baz(1, 1) //│ res: 0 //│ res: 0 | 1 //│ res: 1 x => baz(x, 0) x => baz(0, x) x => baz(x, x) (x, y) => baz(x, y) //│ res: (number & 'a) -> (0 | 'a) //│ res: 'a -> (0 | 'a) //│ res: (number & 'a) -> 'a //│ res: (number & 'a, 'a,) -> 'a let baz(r: (0, 0) | (1, _)) = if r.0 < 1 then r.0 else r.1 //│ baz: (r: ('a & (0 | 1), 'a,),) -> 'a :e baz(0) baz(0, 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.267: baz(0) //│ ║ ^^^^^^ //│ ╟── integer literal of type `0` does not have field '1' //│ ║ l.267: baz(0) //│ ║ ^ //│ ╟── but it flows into argument with expected type `{1: ?a}` //│ ║ l.267: baz(0) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.263: let baz(r: (0, 0) | (1, _)) = if r.0 < 1 then r.0 else r.1 //│ ║ ^^ //│ ╟── from binding: //│ ║ l.263: let baz(r: (0, 0) | (1, _)) = if r.0 < 1 then r.0 else r.1 //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ res: error //│ res: 0 | 1 baz(0, 0) baz(1, 1) x => baz(0, x) x => baz(1, x) x => baz(x, 1) //│ res: 0 //│ res: 1 //│ res: 'a -> (0 | 'a) //│ res: 'a -> (1 | 'a) //│ res: ('a & (0 | 1)) -> (1 | 'a) x => baz(x, 0) x => baz(x, x) (x, y) => baz(x, y) //│ res: ('a & (0 | 1)) -> (0 | 'a) //│ res: ('a & (0 | 1)) -> 'a //│ res: ('a & (0 | 1), 'a,) -> 'a ================================================ FILE: shared/src/test/diff/basics/VerboseErrors.fun ================================================ :v // Note: removing this will mean not showing parameter provs in downstream errors let f = x => log / succ x.prop x.prop let h = y => succ / f y let mkArg = a => {prop: a} //│ f: {prop: int & 'prop} -> 'prop //│ h: {prop: int} -> int //│ mkArg: 'a -> {prop: 'a} :v :e h / mkArg false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.15: h / mkArg false //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.15: h / mkArg false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.7: succ / f y //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.5: x.prop //│ ╙── ^^^^^ //│ res: error | int :v :e (x => succ x) false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.32: (x => succ x) false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.32: (x => succ x) false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.32: (x => succ x) false //│ ║ ^ //│ ╟── from variable: //│ ║ l.32: (x => succ x) false //│ ╙── ^ //│ res: error | int let f = x => log / succ x.prop x.prop let arg = {prop: not true} let arg2 = {fld: arg} let i = y => succ / f y.fld let test = x => y => if x.prop then i x else y //│ f: {prop: int & 'prop} -> 'prop //│ arg: {prop: bool} //│ arg2: {fld: {prop: bool}} //│ i: {fld: {prop: int}} -> int //│ test: {fld: {prop: int}, prop: bool} -> 'a -> (int | 'a) :e :verbose test arg2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.64: test arg2 //│ ║ ^^^^^^^^^ //│ ╟── application of type `bool` is not an instance of type `int` //│ ║ l.51: let arg = {prop: not true} //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.54: succ / f y.fld //│ ║ ^^^^^^^ //│ ╟── from field selection: //│ ║ l.50: x.prop //│ ╙── ^^^^^ //│ res: error | 'a -> (int | 'a) ================================================ FILE: shared/src/test/diff/codegen/AuxiliaryConstructors.mls ================================================ :NewDefs :js class A(x: Int) {} //│ class A(x: Int) //│ // Prelude //│ let res; //│ class TypingUnit { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #x; //│ constructor(x) { //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#A = ((x) => Object.freeze(new A(x))); //│ this.#A.class = A; //│ this.#A.unapply = A.unapply; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.A = typing_unit.A; //│ // End of generated code :js class B {} //│ class B { //│ constructor() //│ } //│ // Prelude //│ class TypingUnit1 { //│ #B; //│ constructor() { //│ } //│ get B() { //│ const qualifier = this; //│ if (this.#B === undefined) { //│ class B {}; //│ this.#B = B; //│ } //│ return this.#B; //│ } //│ } //│ const typing_unit1 = new TypingUnit1; //│ globalThis.B = typing_unit1.B; //│ // End of generated code new B //│ B //│ res //│ = B {} :e B() //│ ╔══[ERROR] Construction of unparameterized class B should use the `new` keyword //│ ║ l.65: B() //│ ╙── ^ //│ B //│ res //│ Runtime error: //│ TypeError: Class constructor B cannot be invoked without 'new' abstract class C //│ abstract class C :e new C //│ ╔══[ERROR] Class C is abstract and cannot be instantiated //│ ║ l.78: new C //│ ╙── ^ //│ C //│ res //│ = C {} :e C() //│ ╔══[ERROR] Class C is abstract and cannot be instantiated //│ ║ l.87: C() //│ ╙── ^ //│ ╔══[ERROR] Class C cannot be instantiated as it exposes no constructor //│ ║ l.87: C() //│ ╙── ^ //│ error //│ res //│ Runtime error: //│ TypeError: Class constructor C cannot be invoked without 'new' :js class C { constructor(x: Int) { log(x) } } //│ class C { //│ constructor(x: Int) //│ } //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ class TypingUnit7 { //│ #C; //│ constructor() { //│ } //│ get C() { //│ const qualifier = this; //│ if (this.#C === undefined) { //│ class C { //│ constructor(x) { //│ log(x); //│ } //│ }; //│ this.#C = C; //│ } //│ return this.#C; //│ } //│ } //│ const typing_unit7 = new TypingUnit7; //│ globalThis.C = typing_unit7.C; //│ // End of generated code :e let c = new C() //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.132: let c = new C() //│ ║ ^^^^^^^ //│ ╟── argument list of type `[]` does not match type `[x: Int]` //│ ║ l.132: let c = new C() //│ ╙── ^^ //│ let c: C | error //│ c //│ = C {} //│ // Output //│ undefined let c = new C(1) //│ let c: C //│ c //│ = C {} //│ // Output //│ 1 :js class D(val x: Int) { constructor(y: Int) { x = y + 1 } log(x) } //│ class D(x: Int) { //│ constructor(y: Int) //│ } //│ // Prelude //│ class TypingUnit10 { //│ #D; //│ constructor() { //│ } //│ get D() { //│ const qualifier = this; //│ if (this.#D === undefined) { //│ class D { //│ #x; //│ get x() { return this.#x; } //│ constructor(y) { //│ this.#x = y + 1; //│ const x = this.#x; //│ log(x); //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#D = ((y) => Object.freeze(new D(y))); //│ this.#D.class = D; //│ this.#D.unapply = D.unapply; //│ } //│ return this.#D; //│ } //│ } //│ const typing_unit10 = new TypingUnit10; //│ globalThis.D = typing_unit10.D; //│ // End of generated code let dd = new D(41) dd.x //│ let dd: D //│ Int //│ dd //│ = D {} //│ // Output //│ 42 //│ res //│ = 42 let dd = new D(41) dd.x //│ let dd: D //│ Int //│ dd //│ = D {} //│ // Output //│ 42 //│ res //│ = 42 :pe class E { constructor(x: Int) constructor(y: Int) } //│ ╔══[PARSE ERROR] A class may have at most one explicit constructor //│ ║ l.217: class E { //│ ╙── ^^^^^ //│ class E { //│ constructor(x: Int) //│ } :e constructor(x: Int) //│ ╔══[ERROR] Illegal position for this constructor statement. //│ ║ l.229: constructor(x: Int) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ //│ Code generation encountered an error: //│ unexpected constructor. :js class F(x: Int) extends C(x + 1) {} class G extends C(2) {} class H extends B {} //│ class F(x: Int) extends C //│ class G extends C { //│ constructor() //│ } //│ class H extends B { //│ constructor() //│ } //│ // Prelude //│ class TypingUnit14 { //│ #F; //│ #G; //│ #H; //│ constructor() { //│ } //│ get F() { //│ const qualifier = this; //│ if (this.#F === undefined) { //│ class F extends C { //│ #x; //│ constructor(x) { //│ super(x + 1); //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#F = ((x) => Object.freeze(new F(x))); //│ this.#F.class = F; //│ this.#F.unapply = F.unapply; //│ } //│ return this.#F; //│ } //│ get G() { //│ const qualifier = this; //│ if (this.#G === undefined) { //│ class G extends C { //│ constructor() { //│ super(2); //│ } //│ }; //│ this.#G = G; //│ } //│ return this.#G; //│ } //│ get H() { //│ const qualifier = this; //│ if (this.#H === undefined) { //│ class H extends B { //│ constructor() { //│ super(); //│ } //│ }; //│ this.#H = H; //│ } //│ return this.#H; //│ } //│ } //│ const typing_unit14 = new TypingUnit14; //│ globalThis.F = typing_unit14.F; //│ globalThis.G = typing_unit14.G; //│ globalThis.H = typing_unit14.H; //│ // End of generated code :js fun f(c) = if c is F(x) then x G() then 2 _ then 0 //│ fun f: Object -> Int //│ // Prelude //│ class TypingUnit15 {} //│ const typing_unit15 = new TypingUnit15; //│ // Query 1 //│ globalThis.f = function f(c) { //│ return ((() => { //│ let a; //│ return a = c, a instanceof F.class ? ((ucs$args_c$F) => ((x) => x)(ucs$args_c$F[0]))(F.unapply(c)) : a instanceof G ? 2 : 0; //│ })()); //│ }; //│ // End of generated code f(F(12)) f(new G()) //│ Int //│ res //│ = 12 //│ // Output //│ 13 //│ res //│ = 2 //│ // Output //│ 2 :js module I { class J { constructor(x: Int) } module K { class L extends J(0) } } //│ module I { //│ class J { //│ constructor(x: Int) //│ } //│ module K { //│ class L extends J { //│ constructor() //│ } //│ } //│ } //│ // Prelude //│ class TypingUnit17 { //│ #I; //│ constructor() { //│ } //│ get I() { //│ const qualifier = this; //│ if (this.#I === undefined) { //│ class I { //│ #J; //│ #K; //│ constructor() { //│ } //│ get K() { //│ const qualifier1 = this; //│ if (this.#K === undefined) { //│ class K { //│ #L; //│ constructor() { //│ } //│ get L() { //│ const qualifier2 = this; //│ if (this.#L === undefined) { //│ class L extends qualifier1.J { //│ constructor() { //│ super(0); //│ } //│ }; //│ this.#L = L; //│ } //│ return this.#L; //│ } //│ } //│ this.#K = new K(); //│ this.#K.class = K; //│ } //│ return this.#K; //│ } //│ get J() { //│ const qualifier1 = this; //│ if (this.#J === undefined) { //│ class J {}; //│ this.#J = J; //│ } //│ return this.#J; //│ } //│ } //│ this.#I = new I(); //│ this.#I.class = I; //│ } //│ return this.#I; //│ } //│ } //│ const typing_unit17 = new TypingUnit17; //│ globalThis.I = typing_unit17.I; //│ // End of generated code :js fun g(x: Int) = class L(y: Int) { constructor(z: Int) { y = z + 1 } fun ll = x + y } x => new L(x) //│ fun g: (x: Int) -> Int -> L //│ // Prelude //│ class TypingUnit18 {} //│ const typing_unit18 = new TypingUnit18; //│ // Query 1 //│ globalThis.g = function g(x) { //│ return ((() => { //│ const L = (() => { //│ class L { //│ #y; //│ constructor(z) { //│ this.#y = z + 1; //│ const y = this.#y; //│ } //│ get ll() { //│ const y = this.#y; //│ return x + y; //│ } //│ static //│ unapply(x) { //│ return [x.#y]; //│ } //│ } //│ let ctor; //│ ctor = ((z) => new L(z)); //│ ctor.class = L; //│ return ctor; //│ })(); //│ return (x) => new L.class(x); //│ })()); //│ }; //│ // End of generated code :js let m = g(1) let n = m(2) n.ll //│ let m: Int -> L //│ let n: L //│ Int //│ // Prelude //│ class TypingUnit19 {} //│ const typing_unit19 = new TypingUnit19; //│ // Query 1 //│ globalThis.m = g(1); //│ // Query 2 //│ globalThis.n = m(2); //│ // Query 3 //│ res = n.ll; //│ // End of generated code //│ m //│ = [Function (anonymous)] //│ n //│ = L {} //│ res //│ = 4 class M() //│ class M() :js let mm = new M() //│ let mm: M //│ // Prelude //│ class TypingUnit21 {} //│ const typing_unit21 = new TypingUnit21; //│ // Query 1 //│ globalThis.mm = new M.class(); //│ // End of generated code //│ mm //│ = M {} :e // TODO support first-class classes fun h(z: Int) = class N { constructor(x: Int) { log(x + z) } } N //│ ╔══[ERROR] Construction of unparameterized class N should use the `new` keyword //│ ║ l.501: N //│ ╙── ^ //│ fun h: (z: Int) -> (x: Int) -> N let hh = h(1) //│ let hh: (x: Int) -> N //│ hh //│ = [class N] :e new hh(1) //│ ╔══[ERROR] type identifier not found: hh //│ ║ l.513: new hh(1) //│ ╙── ^^ //│ error //│ res //│ = N {} //│ // Output //│ 2 :e module O { constructor(x: Int) } mixin P { constructor(x: Int) } //│ ╔══[ERROR] Explicit module constructors are not supported //│ ║ l.525: constructor(x: Int) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Explicit mixin constructors are not supported //│ ║ l.528: constructor(x: Int) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ module O { //│ constructor(x: Int) //│ } //│ mixin P() :w :e class QQ(qq: Str) { constructor(foo: Int) { lol qq = foo } } //│ ╔══[ERROR] identifier not found: lol //│ ║ l.545: lol //│ ╙── ^^^ //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.545: lol //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in auxiliary class constructor: //│ ║ l.544: constructor(foo: Int) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.545: lol //│ ║ ^^^^^^^ //│ ║ l.546: qq = foo //│ ║ ^^^^^^^^^^^^ //│ ║ l.547: } //│ ║ ^^^ //│ ╟── type `Int` is not an instance of type `Str` //│ ║ l.544: constructor(foo: Int) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Str` //│ ║ l.546: qq = foo //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.543: class QQ(qq: Str) { //│ ╙── ^^^ //│ class QQ(qq: Str) { //│ constructor(foo: Int) //│ } //│ Code generation encountered an error: //│ unresolved symbol lol ================================================ FILE: shared/src/test/diff/codegen/Classes.mls ================================================ // To generate the prelude without printing it in the next test case () with { x = add 1 } //│ res: () & {x: int -> int} //│ = [ x: [Function (anonymous)] ] :js def f = 0 //│ // Query 1 //│ globalThis.f = function f() { //│ return 0; //│ }; //│ // End of generated code //│ f: 0 //│ = [Function: f] :js f //│ // Query 1 //│ res = f(); //│ // End of generated code //│ res: 0 //│ = 0 :js class Box[T]: { inner: T } method Map: (T -> 'a) -> Box['a] method Map f = Box { inner = f this.inner } method Get = this.inner method Get2 () = this.inner method Foo () = 0 //│ Defined class Box[+T] //│ Declared Box.Map: Box['T] -> ('T -> 'a) -> Box['a] //│ Defined Box.Map: Box['T] -> ('T -> 'inner) -> Box['inner] //│ Defined Box.Get: Box['T] -> 'T //│ Defined Box.Get2: Box['T] -> () -> 'T //│ Defined Box.Foo: Box[?] -> () -> 0 //│ // Prelude //│ class Box { //│ constructor(fields) { //│ this.inner = fields.inner; //│ } //│ Map(f) { //│ const self = this; //│ return new Box({ inner: f(self.inner) }); //│ } //│ get Get() { //│ const self = this; //│ return self.inner; //│ } //│ Get2() { //│ const self = this; //│ return self.inner; //│ } //│ Foo() { //│ return 0; //│ } //│ } //│ // End of generated code :js def Box value = Box { inner = value } //│ // Query 1 //│ globalThis.Box1 = function Box1(value) { //│ return new Box({ inner: value }); //│ }; //│ // End of generated code //│ Box: 'inner -> Box['inner] //│ = [Function: Box1] :js def box1 = Box 1 def box2 = box1.Map (fun x -> add x 1) //│ // Query 1 //│ globalThis.box1 = function box1() { //│ return Box1(1); //│ }; //│ // Query 2 //│ globalThis.box2 = function box2() { //│ return box1().Map((x) => add(x)(1)); //│ }; //│ // End of generated code //│ box1: Box[1] //│ = [Function: box1] //│ box2: Box[int] //│ = [Function: box2] :js box2.inner + box2.Get //│ // Query 1 //│ res = box2().inner + box2().Get; //│ // End of generated code //│ res: int //│ = 4 :js class MyBox: Box[int] & { info: string } method Map f = Box (f this.inner) with { info = this.info } method Inc = MyBox { inner = this.inner + 1; info = this.info } def MyBox inner info = MyBox { inner; info } //│ Defined class MyBox //│ Defined MyBox.Map: MyBox -> (int -> 'inner) -> (Box['inner] & {info: string}) //│ Defined MyBox.Inc: MyBox -> MyBox //│ // Prelude //│ class MyBox extends Box { //│ constructor(fields) { //│ super(fields); //│ this.info = fields.info; //│ } //│ Map(f) { //│ const self = this; //│ return withConstruct(Box1(f(self.inner)), { info: self.info }); //│ } //│ get Inc() { //│ const self = this; //│ return (new MyBox({ //│ inner: self.inner + 1, //│ info: self.info //│ })); //│ } //│ } //│ // Query 1 //│ globalThis.MyBox1 = function MyBox1(inner) { //│ return ((info) => new MyBox({ //│ inner: inner, //│ info: info //│ })); //│ }; //│ // End of generated code //│ MyBox: (int & 'inner) -> (string & 'info) -> (MyBox with {info: 'info, inner: 'inner}) //│ = [Function: MyBox1] :js mb = MyBox 1 "hello" mb = mb.Inc mb.Get mb2 = mb.Map (fun x -> x * 3) mb2.Get //│ // Query 1 //│ globalThis.mb = MyBox1(1)("hello"); //│ // Query 2 //│ globalThis.mb1 = mb.Inc; //│ // Query 3 //│ res = mb1.Get; //│ // Query 4 //│ globalThis.mb2 = mb1.Map((x) => x * 3); //│ // Query 5 //│ res = mb2.Get; //│ // End of generated code //│ mb: MyBox & {info: "hello", inner: 1} //│ = MyBox { inner: 1, info: 'hello' } //│ mb: MyBox //│ = MyBox { inner: 2, info: 'hello' } //│ res: int //│ = 2 //│ mb2: Box[int] //│ = Box { inner: 6, info: 'hello' } //│ res: int //│ = 6 // TODO support this syntax :js mb.(MyBox.Map) (fun x -> x * 3) MyBox.Map mb (fun x -> x * 3) //│ // Query 1 //│ res = mb1["MyBox.Map"]((x) => x * 3); //│ // Query 2 //│ res = MyBox1.Map(mb1)((x) => x * 3); //│ // End of generated code //│ res: Box[int] & {info: string} //│ Runtime error: //│ TypeError: mb1.MyBox.Map is not a function //│ res: Box[int] & {info: string} //│ Runtime error: //│ TypeError: MyBox1.Map is not a function // Note that there is currently an inconsistency with wildcards: // The type simplifier uses `?` to denote a class type argument wildcard, // meaning there are no constraints on the corresponding class parameter in that type application, // while user-written `?` in type signatures is handled like a definition-wide existential, // which is NOT the same: def f0 (x: Box['_]) = 42 //│ f0: Box[?] -> 42 //│ = [Function: f0] f0(Box{}) //│ res: 42 //│ = 42 // i.e., def f: exists T. Box[T] -> int def f: Box[?] -> int //│ f: in Box[?] -> int out Box[nothing] -> int //│ = :e f(Box{}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.199: f(Box{}) //│ ║ ^^^^^^^^ //│ ╟── record literal of type `anything` does not match type `nothing` //│ ║ l.199: f(Box{}) //│ ║ ^^ //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.194: def f: Box[?] -> int //│ ╙── ^ //│ res: error | int //│ = //│ f is not implemented f = f0 //│ Box[?] -> 42 //│ <: f: //│ Box[?] -> int //│ = [Function: f0] // The following test ensures `const self = this` is generated correctly. :js class Point: { x: int; y: int } method Zero = 0 method Move (dx: int, dy: int) = Point { x = this.x + dx; y = this.y + dy } method Sum self = Point { x = this.x + self.x; y = this.y + self.y } method Shadow this = Point { x = this.x + 1; y = this.y + 1 } method Shadow2 ((this,)) = Point { x = this.x + 1; y = this.y + 1 } method Shadow3 { this } = Point { x = this.x + 1; y = this.y + 1 } //│ Defined class Point //│ Defined Point.Zero: Point -> 0 //│ Defined Point.Move: Point -> (int, int,) -> Point //│ Defined Point.Sum: Point -> {x: int, y: int} -> Point //│ Defined Point.Shadow: Point -> {x: int, y: int} -> Point //│ Defined Point.Shadow2: Point -> (({x: int, y: int},),) -> Point //│ Defined Point.Shadow3: Point -> {this: {x: int, y: int}} -> Point //│ // Prelude //│ class Point { //│ constructor(fields) { //│ this.x = fields.x; //│ this.y = fields.y; //│ } //│ get Zero() { //│ return 0; //│ } //│ Move(dx, dy) { //│ const self = this; //│ return (new Point({ //│ x: self.x + dx, //│ y: self.y + dy //│ })); //│ } //│ Sum(self1) { //│ const self = this; //│ return (new Point({ //│ x: self.x + self1.x, //│ y: self.y + self1.y //│ })); //│ } //│ Shadow(this$) { //│ return (new Point({ //│ x: this$.x + 1, //│ y: this$.y + 1 //│ })); //│ } //│ Shadow2([this$]) { //│ return (new Point({ //│ x: this$.x + 1, //│ y: this$.y + 1 //│ })); //│ } //│ Shadow3({ "this": this$ }) { //│ return (new Point({ //│ x: this$.x + 1, //│ y: this$.y + 1 //│ })); //│ } //│ } //│ // End of generated code // Some simplification tests: :NoJS error : 2 & int //│ res: 2 error : 2 | int //│ res: int error : Box[int] & ~box //│ res: nothing error : Box[?] | Box[?] & {x: '_} //│ res: Box[?] error : ((Box[?] | Box[?] & {x: 'a -> int}) & 'a) -> 'a //│ res: (Box[nothing] & 'a) -> 'a error: "" | string //│ res: string ================================================ FILE: shared/src/test/diff/codegen/ConstructorStmt.mls ================================================ :NewDefs log("Hello!") //│ () //│ res //│ = undefined //│ // Output //│ Hello! :js module Test0 { log("Hello!") } //│ module Test0 //│ // Prelude //│ class TypingUnit1 { //│ #Test0; //│ constructor() { //│ } //│ get Test0() { //│ const qualifier = this; //│ if (this.#Test0 === undefined) { //│ class Test0 { //│ constructor() { //│ log("Hello!"); //│ } //│ } //│ this.#Test0 = new Test0(); //│ this.#Test0.class = Test0; //│ } //│ return this.#Test0; //│ } //│ } //│ const typing_unit1 = new TypingUnit1; //│ globalThis.Test0 = typing_unit1.Test0; //│ // End of generated code :js Test0 //│ Test0 //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ res = Test0; //│ // End of generated code //│ res //│ = Test0 { class: [class Test0] } //│ // Output //│ Hello! :js Test0 //│ Test0 //│ // Prelude //│ class TypingUnit3 {} //│ const typing_unit3 = new TypingUnit3; //│ // Query 1 //│ res = Test0; //│ // End of generated code //│ res //│ = Test0 { class: [class Test0] } :js class A(a: Int) { log(a) } //│ class A(a: Int) //│ // Prelude //│ class TypingUnit4 { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #a; //│ constructor(a) { //│ this.#a = a; //│ log(a); //│ } //│ static //│ unapply(x) { //│ return [x.#a]; //│ } //│ }; //│ this.#A = ((a) => Object.freeze(new A(a))); //│ this.#A.class = A; //│ this.#A.unapply = A.unapply; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit4 = new TypingUnit4; //│ globalThis.A = typing_unit4.A; //│ // End of generated code :js let aa = A(42) //│ let aa: A //│ // Prelude //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 //│ globalThis.aa = A(42); //│ // End of generated code //│ aa //│ = A {} //│ // Output //│ 42 :js aa //│ A //│ // Prelude //│ class TypingUnit6 {} //│ const typing_unit6 = new TypingUnit6; //│ // Query 1 //│ res = aa; //│ // End of generated code //│ res //│ = A {} :js let ab = A(0) //│ let ab: A //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ globalThis.ab = A(0); //│ // End of generated code //│ ab //│ = A {} //│ // Output //│ 0 :e :w :js class Foo { this: { x: Int } } //│ ╔══[ERROR] Type `#Foo` does not contain member `x` //│ ║ l.145: this: { x: Int } //│ ╙── ^ //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in type ascription: //│ ║ l.145: this: { x: Int } //│ ║ ^^^^ //│ ╟── type `{x: Int}` does not match type `()` //│ ║ l.145: this: { x: Int } //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into expression in statement position with expected type `()` //│ ║ l.145: this: { x: Int } //│ ╙── ^^^^ //│ class Foo { //│ constructor() //│ } //│ // Prelude //│ class TypingUnit8 { //│ #Foo; //│ constructor() { //│ } //│ get Foo() { //│ const qualifier = this; //│ if (this.#Foo === undefined) { //│ class Foo {}; //│ this.#Foo = Foo; //│ } //│ return this.#Foo; //│ } //│ } //│ const typing_unit8 = new TypingUnit8; //│ globalThis.Foo = typing_unit8.Foo; //│ // End of generated code :e :w :js class Bar { super: { x: Int } } //│ ╔══[ERROR] Illegal use of `super` //│ ║ l.186: super: { x: Int } //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#Bar` does not contain member `x` //│ ║ l.186: super: { x: Int } //│ ╙── ^ //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in type ascription: //│ ║ l.186: super: { x: Int } //│ ║ ^^^^^ //│ ╟── type `{x: Int}` does not match type `()` //│ ║ l.186: super: { x: Int } //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into expression in statement position with expected type `()` //│ ║ l.186: super: { x: Int } //│ ╙── ^^^^^ //│ class Bar { //│ constructor() //│ } //│ // Prelude //│ class TypingUnit9 { //│ #Bar; //│ constructor() { //│ } //│ get Bar() { //│ const qualifier = this; //│ if (this.#Bar === undefined) { //│ class Bar {}; //│ this.#Bar = Bar; //│ } //│ return this.#Bar; //│ } //│ } //│ const typing_unit9 = new TypingUnit9; //│ globalThis.Bar = typing_unit9.Bar; //│ // End of generated code :js class Baz() { val x = 123 log([1, x]) val y = log([2, x]) x + 1 log([3, y]) } //│ class Baz() { //│ val x: 123 //│ val y: Int //│ } //│ // Prelude //│ class TypingUnit10 { //│ #Baz; //│ constructor() { //│ } //│ get Baz() { //│ const qualifier = this; //│ if (this.#Baz === undefined) { //│ class Baz { //│ #x; //│ get x() { return this.#x; } //│ #y; //│ get y() { return this.#y; } //│ constructor() { //│ this.#x = 123; //│ const x = this.#x; //│ log([ //│ 1, //│ x //│ ]); //│ this.#y = (() => { //│ log([ //│ 2, //│ x //│ ]); //│ return x + 1; //│ })(); //│ const y = this.#y; //│ log([ //│ 3, //│ y //│ ]); //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#Baz = (() => Object.freeze(new Baz())); //│ this.#Baz.class = Baz; //│ this.#Baz.unapply = Baz.unapply; //│ } //│ return this.#Baz; //│ } //│ } //│ const typing_unit10 = new TypingUnit10; //│ globalThis.Baz = typing_unit10.Baz; //│ // End of generated code let baz = Baz() log([baz.x, baz.y]) //│ let baz: Baz //│ () //│ baz //│ = Baz {} //│ // Output //│ [ 1, 123 ] //│ [ 2, 123 ] //│ [ 3, 124 ] //│ res //│ = undefined //│ // Output //│ [ 123, 124 ] :js class Q() { let q = 42 fun qq = let f = (x: Int) => {q: x + q}; f(1) } //│ class Q() { //│ let q: 42 //│ fun qq: {q: Int} //│ } //│ // Prelude //│ class TypingUnit12 { //│ #Q; //│ constructor() { //│ } //│ get Q() { //│ const qualifier = this; //│ if (this.#Q === undefined) { //│ class Q { //│ #q; //│ constructor() { //│ this.#q = 42; //│ const q = this.#q; //│ } //│ get qq() { //│ const qualifier1 = this; //│ return ((() => { //│ let f = (x) => ({ q: x + qualifier1.#q }); //│ return f(1); //│ })()); //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#Q = (() => Object.freeze(new Q())); //│ this.#Q.class = Q; //│ this.#Q.unapply = Q.unapply; //│ } //│ return this.#Q; //│ } //│ } //│ const typing_unit12 = new TypingUnit12; //│ globalThis.Q = typing_unit12.Q; //│ // End of generated code let q = Q() q.qq.q //│ let q: Q //│ Int //│ q //│ = Q {} //│ res //│ = 43 :js class W() { let x = 42 fun add(self: Int) = x + self } //│ class W() { //│ fun add: (self: Int) -> Int //│ let x: 42 //│ } //│ // Prelude //│ class TypingUnit14 { //│ #W; //│ constructor() { //│ } //│ get W() { //│ const qualifier = this; //│ if (this.#W === undefined) { //│ class W { //│ #x; //│ constructor() { //│ this.#x = 42; //│ const x = this.#x; //│ } //│ add(self) { //│ const qualifier1 = this; //│ return qualifier1.#x + self; //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#W = (() => Object.freeze(new W())); //│ this.#W.class = W; //│ this.#W.unapply = W.unapply; //│ } //│ return this.#W; //│ } //│ } //│ const typing_unit14 = new TypingUnit14; //│ globalThis.W = typing_unit14.W; //│ // End of generated code :js let www = W() www.add(42) //│ let www: W //│ Int //│ // Prelude //│ class TypingUnit15 {} //│ const typing_unit15 = new TypingUnit15; //│ // Query 1 //│ globalThis.www = W(); //│ // Query 2 //│ res = www.add(42); //│ // End of generated code //│ www //│ = W {} //│ res //│ = 84 ================================================ FILE: shared/src/test/diff/codegen/Declare.mls ================================================ // This file tests the error message of unimplemented symbols. () //│ res: () //│ = [] :js def a: int //│ // Query 1 is empty //│ // End of generated code //│ a: int //│ = :js def b = a //│ // Query 1 aborted: a is not implemented //│ // End of generated code //│ b: int //│ = //│ a is not implemented // We can use some unimplemented value. // But preserve other implemented things in the same block. :js def c = b def q = 0 //│ // Query 1 aborted: b and a are not implemented //│ // Query 2 //│ globalThis.q = function q() { //│ return 0; //│ }; //│ // End of generated code //│ c: int //│ = //│ b and a are not implemented //│ q: 0 //│ = [Function: q] // We will display all unimplemented symbols. :js def d = c //│ // Query 1 aborted: c, b and a are not implemented //│ // End of generated code //│ d: int //│ = //│ c, b and a are not implemented def q: int def q: string //│ q: int //│ = //│ q: string //│ = q: anything //│ res: anything //│ = //│ q is not implemented // Use unimplemented symbols defined in the same block. 0 def f: int -> int f 0 //│ res: 0 //│ = 0 //│ f: int -> int //│ = //│ res: int //│ = //│ f is not implemented def a: int //│ a: int //│ = a = 0 //│ 0 //│ <: a: //│ int //│ = 0 def b = a //│ b: int //│ = [Function: b] // Declare JavaScript reserved words. :js def break = 1 def str = "str" //│ // Query 1 //│ globalThis.break1 = function break1() { //│ return 1; //│ }; //│ // Query 2 //│ globalThis.str = function str() { //│ return "str"; //│ }; //│ // End of generated code //│ break: 1 //│ = [Function: break1] //│ str: "str" //│ = [Function: str] ================================================ FILE: shared/src/test/diff/codegen/Escape.mls ================================================ () //│ res: () //│ = [] :js :escape def console: nothing //│ // Query 1 is empty //│ // End of generated code //│ console: nothing //│ = :js console.log "hello" //│ // Query 1 //│ res = console.log("hello"); //│ // End of generated code //│ res: nothing //│ = undefined :js 0 //│ // Query 1 //│ res = 0; //│ // End of generated code //│ res: 0 //│ = 0 ================================================ FILE: shared/src/test/diff/codegen/FieldOverride.mls ================================================ :NewParser :NewDefs :js class C(a: Int) { val a = 1 } //│ class C(a: Int) { //│ val a: 1 //│ } //│ // Prelude //│ let res; //│ class TypingUnit { //│ #C; //│ constructor() { //│ } //│ get C() { //│ const qualifier = this; //│ if (this.#C === undefined) { //│ class C { //│ #a; //│ get a() { return this.#a; } //│ constructor(a) { //│ this.#a = a; //│ this.#a = 1; //│ const a1 = this.#a; //│ } //│ static //│ unapply(x) { //│ return [x.#a]; //│ } //│ }; //│ this.#C = ((a) => Object.freeze(new C(a))); //│ this.#C.class = C; //│ this.#C.unapply = C.unapply; //│ } //│ return this.#C; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.C = typing_unit.C; //│ // End of generated code // should return 1 let a = C(2) a.a //│ let a: C //│ 1 //│ a //│ = C {} //│ res //│ = 1 :js class C2(a: Int, b: Int) { val a = b + 1 val b = a + 1 } //│ class C2(a: Int, b: Int) { //│ val a: Int //│ val b: Int //│ } //│ // Prelude //│ class TypingUnit2 { //│ #C2; //│ constructor() { //│ } //│ get C2() { //│ const qualifier = this; //│ if (this.#C2 === undefined) { //│ class C2 { //│ #a; //│ #b; //│ get a() { return this.#a; } //│ get b() { return this.#b; } //│ constructor(a, b) { //│ this.#a = a; //│ this.#b = b; //│ this.#a = b + 1; //│ const a1 = this.#a; //│ this.#b = a1 + 1; //│ const b1 = this.#b; //│ } //│ static //│ unapply(x) { //│ return ([ //│ x.#a, //│ x.#b //│ ]); //│ } //│ }; //│ this.#C2 = ((a, b) => Object.freeze(new C2(a, b))); //│ this.#C2.class = C2; //│ this.#C2.unapply = C2.unapply; //│ } //│ return this.#C2; //│ } //│ } //│ const typing_unit2 = new TypingUnit2; //│ globalThis.C2 = typing_unit2.C2; //│ // End of generated code let c2 = C2(1, 2) c2.a c2.b //│ let c2: C2 //│ Int //│ c2 //│ = C2 {} //│ res //│ = 3 //│ res //│ = 4 class C3(a: Int) { val a = 42 class C4(a: Int) { val a = 44 } } //│ class C3(a: Int) { //│ class C4(a: Int) { //│ val a: 44 //│ } //│ val a: 42 //│ } :e let c3 = C3(1) let c4 = c3.C4(2) c3.a c4.a //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.128: let c4 = c3.C4(2) //│ ╙── ^^^ //│ let c3: C3 //│ let c4: error //│ error //│ c3 //│ = C3 {} //│ c4 //│ = C4 {} //│ res //│ = 42 //│ res //│ = 44 ================================================ FILE: shared/src/test/diff/codegen/IndirectRecursion.mls ================================================ def z: (('a -> 'b) -> (('a -> 'b) & 'c)) -> 'c //│ z: (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ = :e def z = (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ (('a -> 'b) -> 'c & ('d -> 'e) -> ('d -> 'e & 'a -> 'b)) -> (error | 'c) //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.9: (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: z] def z = (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))!) //│ (('a -> 'b) -> 'c & ('d -> 'e) -> ('d -> 'e & 'a -> 'b)) -> 'c //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ = [Function: z1] def pow1 = z (fun pow0 -> fun n -> fun x -> if n > 0 then pow0 (n - 1) x * x else 1 ) //│ pow1: int -> int -> int //│ = [Function: pow1] pow1 3 4 //│ res: int //│ = 64 :e z (fun self -> 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.40: z (fun self -> 1) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.40: z (fun self -> 1) //│ ║ ^ //│ ╟── Note: constraint arises from function type: //│ ║ l.3: def z: (('a -> 'b) -> (('a -> 'b) & 'c)) -> 'c //│ ║ ^^^^^^^^^^ //│ ╟── from intersection type: //│ ║ l.3: def z: (('a -> 'b) -> (('a -> 'b) & 'c)) -> 'c //│ ╙── ^^^^^^^^^^^^^^^^^ //│ res: 1 | error //│ = 1 zid = z (fun self -> id) //│ zid: 'a -> 'a //│ = [Function: id] zid 1 //│ res: 1 //│ = 1 zargs = z (fun self -> fun x -> self) //│ zargs: 'b //│ where //│ 'b :> anything -> 'b //│ = [Function (anonymous)] zargs 1 //│ res: 'b //│ where //│ 'b :> anything -> 'b //│ = [Function (anonymous)] zargs 1 2 3 4 //│ res: 'b //│ where //│ 'b :> anything -> 'b //│ = [Function (anonymous)] loop = z (fun self -> self) //│ loop: anything -> nothing //│ = [Function (anonymous)] :re loop 1 //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded def pow pow n x = if n > 0 then pow (n - 1) x * x else 1 //│ pow: (int -> 'a -> int) -> int -> (int & 'a) -> int //│ = [Function: pow] def pow = z pow pow 3 4 //│ pow: int -> int -> int //│ = [Function: pow2] //│ res: int //│ = 64 def oops = z (fun f -> f) //│ oops: anything -> nothing //│ = [Function: oops] :re oops 1 //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :NoRecursiveTypes :e def z = (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ (('a -> 'b) -> 'c & ('d -> 'e) -> ('d -> 'e & 'a -> 'b)) -> (error | 'c) //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.126: (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: z2] :e def z = (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))!) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> ? //│ <: 'a -> ? -> ? //│ ╙── //│ (('a -> 'b) -> 'c & ('d -> 'e) -> ('d -> 'e & 'a -> 'b)) -> 'c //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ = [Function: z3] :NoCycleCheck // Exceeds recursion depth limit: :e def z = (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ ((anything -> nothing) -> anything) -> error //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?c -> ?d) -> ?e` exceeded recursion depth limit (250) //│ ║ l.154: (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: z4] :e def z = (fun f -> (fun x -> f (fun v -> (x x) v)) (fun x -> f (fun v -> (x x) v))!) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> ? //│ <: 'a -> ? -> ? //│ ╙── //│ (('a -> 'b) -> 'c & ('d -> 'e) -> ('d -> 'e & 'a -> 'b)) -> 'c //│ <: z: //│ (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ = [Function: z5] ================================================ FILE: shared/src/test/diff/codegen/Inheritance.mls ================================================ :js trait X: { x: int } class Y: X method MX = this.x //│ Defined trait X //│ Defined class Y //│ Defined Y.MX: Y -> int //│ // Prelude //│ let res; //│ const X = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ class Y { //│ constructor(fields) { //│ X.implement(this); //│ this.x = fields.x; //│ } //│ get MX() { //│ const self = this; //│ return self.x; //│ } //│ } //│ // End of generated code :js (Y {x = 1}).x //│ // Query 1 //│ res = new Y({ x: 1 }).x; //│ // End of generated code //│ res: 1 //│ = 1 :js trait A: { a: int } trait B: { b: int } & A class C: B & { c: int } //│ Defined trait A //│ Defined trait B //│ Defined class C //│ // Prelude //│ const A = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ const B = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ A.implement(instance); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ class C { //│ constructor(fields) { //│ B.implement(this); //│ this.c = fields.c; //│ this.b = fields.b; //│ this.a = fields.a; //│ } //│ } //│ // End of generated code :js class P: Q & { p: int } trait Q: R & { q: int } trait R: S & { r: int } trait S: { s: int } //│ Defined class P //│ Defined trait Q //│ Defined trait R //│ Defined trait S //│ // Prelude //│ const Q = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ R.implement(instance); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ const R = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ S.implement(instance); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ const S = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ class P { //│ constructor(fields) { //│ Q.implement(this); //│ this.p = fields.p; //│ this.q = fields.q; //│ this.r = fields.r; //│ this.s = fields.s; //│ } //│ } //│ // End of generated code def checkS x = case x of { S -> true | _ -> false } def checkR x = case x of { R -> true | _ -> false } def checkQ x = case x of { Q -> true | _ -> false } def checkP x = case x of { P -> true | _ -> false } //│ checkS: anything -> Bool //│ = [Function: checkS] //│ checkR: anything -> Bool //│ = [Function: checkR] //│ checkQ: anything -> Bool //│ = [Function: checkQ] //│ checkP: anything -> Bool //│ = [Function: checkP] // Should pass all checks. :js p = P { s = 0; q = 0; r = 0; p = 0 } checkS p && checkR p && checkQ p && checkP p //│ // Query 1 //│ globalThis.p = new P({ //│ s: 0, //│ q: 0, //│ r: 0, //│ p: 0 //│ }); //│ // Query 2 //│ res = checkS(p) && checkR(p) && checkQ(p) && checkP(p); //│ // End of generated code //│ p: P & {p: 0, q: 0, r: 0, s: 0} //│ = P { p: 0, q: 0, r: 0, s: 0 } //│ res: bool //│ = true trait Foo method Foo = 1 //│ Defined trait Foo //│ Defined Foo.Foo: Foo -> 1 class Bar: Foo //│ Defined class Bar // `Foo` should not appear. bar = Bar{} //│ bar: Bar //│ = Bar {} // But it is actually there. bar.Foo //│ res: 1 //│ = 1 ================================================ FILE: shared/src/test/diff/codegen/LogInfo.mls ================================================ :js log 1 //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ let res; //│ // Query 1 //│ res = log(1); //│ // End of generated code //│ = undefined //│ // Output //│ 1 :escape def console: nothing //│ console: nothing //│ = console.log 1 //│ res: nothing //│ = undefined console.info(1) //│ res: nothing //│ = undefined //│ // Output //│ 1 ================================================ FILE: shared/src/test/diff/codegen/MemberInitShadowing.mls ================================================ :NewDefs // * This is a valid use of let-binding shadowing :js class A(x0: Int) { let x1 = x0 + 1 let x1 = x1 + 1 log(x1) } //│ class A(x0: Int) { //│ let x1: Int //│ } //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ let res; //│ class TypingUnit { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #x0; //│ constructor(x0) { //│ this.#x0 = x0; //│ const x1 = x0 + 1; //│ const x11 = x11 + 1; //│ log(x11); //│ } //│ static //│ unapply(x) { //│ return [x.#x0]; //│ } //│ }; //│ this.#A = ((x0) => Object.freeze(new A(x0))); //│ this.#A.class = A; //│ this.#A.unapply = A.unapply; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.A = typing_unit.A; //│ // End of generated code // FIXME A(123) //│ A //│ res //│ Runtime error: //│ ReferenceError: Cannot access 'x11' before initialization // TODO `val` redefinition should be a type error :js class A(x0: Int) { val x1 = x0 + 1 val x1 = x1 + 1 log(x1) } //│ class A(x0: Int) { //│ val x1: Int //│ } //│ // Prelude //│ class TypingUnit2 { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #x0; //│ #x1; //│ get x1() { return this.#x1; } //│ constructor(x0) { //│ this.#x0 = x0; //│ this.#x1 = x0 + 1; //│ const x1 = this.#x1; //│ this.#x1 = x1 + 1; //│ const x11 = this.#x1; //│ log(x11); //│ } //│ static //│ unapply(x) { //│ return [x.#x0]; //│ } //│ }; //│ this.#A = ((x0) => Object.freeze(new A(x0))); //│ this.#A.class = A; //│ this.#A.unapply = A.unapply; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit2 = new TypingUnit2; //│ globalThis.A = typing_unit2.A; //│ // End of generated code A(123) //│ A //│ res //│ = A {} //│ // Output //│ 125 ================================================ FILE: shared/src/test/diff/codegen/Mixin.mls ================================================ :NewDefs :js class Add(lhs: E, rhs: E) class Lit(n: Int) //│ class Add[E](lhs: E, rhs: E) //│ class Lit(n: Int) //│ // Prelude //│ let res; //│ class TypingUnit { //│ #Add; //│ #Lit; //│ constructor() { //│ } //│ get Add() { //│ const qualifier = this; //│ if (this.#Add === undefined) { //│ class Add { //│ #lhs; //│ #rhs; //│ constructor(lhs, rhs) { //│ this.#lhs = lhs; //│ this.#rhs = rhs; //│ } //│ static //│ unapply(x) { //│ return ([ //│ x.#lhs, //│ x.#rhs //│ ]); //│ } //│ }; //│ this.#Add = ((lhs, rhs) => Object.freeze(new Add(lhs, rhs))); //│ this.#Add.class = Add; //│ this.#Add.unapply = Add.unapply; //│ } //│ return this.#Add; //│ } //│ get Lit() { //│ const qualifier = this; //│ if (this.#Lit === undefined) { //│ class Lit { //│ #n; //│ constructor(n) { //│ this.#n = n; //│ } //│ static //│ unapply(x) { //│ return [x.#n]; //│ } //│ }; //│ this.#Lit = ((n) => Object.freeze(new Lit(n))); //│ this.#Lit.class = Lit; //│ this.#Lit.unapply = Lit.unapply; //│ } //│ return this.#Lit; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.Add = typing_unit.Add; //│ globalThis.Lit = typing_unit.Lit; //│ // End of generated code :js mixin EvalBase { fun eval(e) = if e is Lit(n) then n: Int Add(l, r) then this.eval(l) + this.eval(r) } //│ mixin EvalBase() { //│ this: {eval: 'a -> Int} //│ fun eval: (Add['a] | Lit) -> Int //│ } //│ // Prelude //│ class TypingUnit1 { //│ constructor() { //│ } //│ EvalBase(base) { //│ const qualifier = this; //│ return (class EvalBase extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ eval(e) { //│ const qualifier1 = this; //│ return ((() => { //│ let a; //│ return (a = e, a instanceof Lit.class ? ((ucs$args_e$Lit) => ((n) => n)(ucs$args_e$Lit[0]))(Lit.unapply(e)) : a instanceof Add.class ? ((ucs$args_e$Add) => ((l) => ((r) => qualifier1.eval(l) + qualifier1.eval(r))(ucs$args_e$Add[1]))(ucs$args_e$Add[0]))(Add.unapply(e)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); //│ } //│ }); //│ } //│ } //│ const typing_unit1 = new TypingUnit1; //│ globalThis.EvalBase = ((base) => typing_unit1.EvalBase(base)); //│ // End of generated code :js class Neg(expr: A) //│ class Neg[A](expr: A) //│ // Prelude //│ class TypingUnit2 { //│ #Neg; //│ constructor() { //│ } //│ get Neg() { //│ const qualifier = this; //│ if (this.#Neg === undefined) { //│ class Neg { //│ #expr; //│ constructor(expr) { //│ this.#expr = expr; //│ } //│ static //│ unapply(x) { //│ return [x.#expr]; //│ } //│ }; //│ this.#Neg = ((expr) => Object.freeze(new Neg(expr))); //│ this.#Neg.class = Neg; //│ this.#Neg.unapply = Neg.unapply; //│ } //│ return this.#Neg; //│ } //│ } //│ const typing_unit2 = new TypingUnit2; //│ globalThis.Neg = typing_unit2.Neg; //│ // End of generated code :js mixin EvalNeg { fun eval(e) = if e is Neg(d) then 0 - this.eval(d) else super.eval(e) } //│ mixin EvalNeg() { //│ super: {eval: 'a -> 'b} //│ this: {eval: 'c -> Int} //│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) //│ } //│ // Prelude //│ class TypingUnit3 { //│ constructor() { //│ } //│ EvalNeg(base) { //│ const qualifier = this; //│ return (class EvalNeg extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ eval(e) { //│ const qualifier1 = this; //│ return ((() => { //│ return e instanceof Neg.class ? ((ucs$args_e$Neg) => ((d) => 0 - qualifier1.eval(d))(ucs$args_e$Neg[0]))(Neg.unapply(e)) : super.eval(e); //│ })()); //│ } //│ }); //│ } //│ } //│ const typing_unit3 = new TypingUnit3; //│ globalThis.EvalNeg = ((base) => typing_unit3.EvalNeg(base)); //│ // End of generated code :js mixin EvalNegNeg { fun eval(e) = if e is Neg(Neg(d)) then this.eval(d) else super.eval(e) } //│ mixin EvalNegNeg() { //│ super: {eval: (Neg[nothing] | 'a) -> 'b} //│ this: {eval: 'c -> 'b} //│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b //│ } //│ // Prelude //│ class TypingUnit4 { //│ constructor() { //│ } //│ EvalNegNeg(base) { //│ const qualifier = this; //│ return (class EvalNegNeg extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ eval(e) { //│ const qualifier1 = this; //│ return ((() => { //│ return e instanceof Neg.class ? ((ucs$args_e$Neg) => ((e$Neg_0) => e$Neg_0 instanceof Neg.class ? ((ucs$args_e$Neg_0$Neg) => ((d) => qualifier1.eval(d))(ucs$args_e$Neg_0$Neg[0]))(Neg.unapply(e$Neg_0)) : super.eval(e))(ucs$args_e$Neg[0]))(Neg.unapply(e)) : super.eval(e); //│ })()); //│ } //│ }); //│ } //│ } //│ const typing_unit4 = new TypingUnit4; //│ globalThis.EvalNegNeg = ((base) => typing_unit4.EvalNegNeg(base)); //│ // End of generated code :js module TestLang extends EvalBase, EvalNeg, EvalNegNeg //│ module TestLang { //│ fun eval: (Neg['A] | Object & 'a & ~#Neg) -> Int //│ } //│ where //│ 'A <: 'b & (Neg['b] | Object & ~#Neg) //│ 'b <: Neg['A] | Object & 'a & ~#Neg //│ 'a <: Add['b] | Lit | Neg['b] //│ // Prelude //│ class TypingUnit5 { //│ #TestLang; //│ constructor() { //│ } //│ get TestLang() { //│ const qualifier = this; //│ if (this.#TestLang === undefined) { //│ class TestLang extends EvalNegNeg(EvalNeg(EvalBase(Object))) { //│ constructor() { //│ super(); //│ } //│ } //│ this.#TestLang = new TestLang(); //│ this.#TestLang.class = TestLang; //│ } //│ return this.#TestLang; //│ } //│ } //│ const typing_unit5 = new TypingUnit5; //│ globalThis.TestLang = typing_unit5.TestLang; //│ // End of generated code fun mk(n) = if n is 0 then Lit(0) 1 then Neg(mk(n)) _ then Add(mk(n), mk(n)) TestLang.eval(mk(0)) //│ fun mk: forall 'a. Object -> (Lit | 'a) //│ Int //│ where //│ 'a :> Add[Lit | 'a] | Neg[Lit | 'a] //│ res //│ = 0 class Foo(x: Int) //│ class Foo(x: Int) class Bar(x2: Int, y: Int) extends Foo(x2 + y) //│ class Bar(x2: Int, y: Int) extends Foo mixin AA(a: Int) { } //│ mixin AA(a: Int) mixin BB {} //│ mixin BB() class C(x: Int) extends BB //│ class C(x: Int) class D(x: Int) extends AA(x) //│ class D(x: Int) class E(x: Int) extends BB, AA(x) //│ class E(x: Int) :js mixin Fooo(val x: Int) { fun f = [x, this.x] } //│ mixin Fooo(x: Int) { //│ this: {x: 'x} //│ fun f: [Int, 'x] //│ } //│ // Prelude //│ class TypingUnit14 { //│ constructor() { //│ } //│ Fooo(base) { //│ const qualifier = this; //│ return (class Fooo extends base { //│ #x; //│ get x() { return this.#x; } //│ constructor(x, ...rest) { //│ super(...rest); //│ this.#x = x; //│ } //│ get f() { //│ const x = this.#x; //│ const qualifier1 = this; //│ return ([ //│ x, //│ qualifier1.x //│ ]); //│ } //│ }); //│ } //│ } //│ const typing_unit14 = new TypingUnit14; //│ globalThis.Fooo = ((base) => typing_unit14.Fooo(base)); //│ // End of generated code :js mixin Bazz(val y: Int) //│ mixin Bazz(y: Int) //│ // Prelude //│ class TypingUnit15 { //│ constructor() { //│ } //│ Bazz(base) { //│ const qualifier = this; //│ return (class Bazz extends base { //│ #y; //│ get y() { return this.#y; } //│ constructor(y, ...rest) { //│ super(...rest); //│ this.#y = y; //│ } //│ }); //│ } //│ } //│ const typing_unit15 = new TypingUnit15; //│ globalThis.Bazz = ((base) => typing_unit15.Bazz(base)); //│ // End of generated code :js module Barr extends Fooo(0), Bazz(1) //│ module Barr { //│ fun f: [Int, 0] //│ } //│ // Prelude //│ class TypingUnit16 { //│ #Barr; //│ constructor() { //│ } //│ get Barr() { //│ const qualifier = this; //│ if (this.#Barr === undefined) { //│ class Barr extends Bazz(Fooo(Object)) { //│ constructor() { //│ super(1, 0); //│ } //│ } //│ this.#Barr = new Barr(); //│ this.#Barr.class = Barr; //│ } //│ return this.#Barr; //│ } //│ } //│ const typing_unit16 = new TypingUnit16; //│ globalThis.Barr = typing_unit16.Barr; //│ // End of generated code Barr.x //│ 0 //│ res //│ = 0 Barr.y //│ 1 //│ res //│ = 1 :e :ge mixin Base { fun x = y } //│ ╔══[ERROR] identifier not found: y //│ ║ l.368: fun x = y //│ ╙── ^ //│ mixin Base() { //│ fun x: error //│ } //│ Code generation encountered an error: //│ unresolved symbol y :re module Test extends Base //│ module Test { //│ fun x: error //│ } //│ Runtime error: //│ ReferenceError: Base is not defined mixin MA(val a: Int) mixin MB(val b1: Int, val b2: Int) mixin MC(val c: Int) //│ mixin MA(a: Int) //│ mixin MB(b1: Int, b2: Int) //│ mixin MC(c: Int) :js module MM extends MA(1), MB(2, 3), MC(4) //│ module MM //│ // Prelude //│ class TypingUnit22 { //│ #MM; //│ constructor() { //│ } //│ get MM() { //│ const qualifier = this; //│ if (this.#MM === undefined) { //│ class MM extends MC(MB(MA(Object))) { //│ constructor() { //│ super(4, 2, 3, 1); //│ } //│ } //│ this.#MM = new MM(); //│ this.#MM.class = MM; //│ } //│ return this.#MM; //│ } //│ } //│ const typing_unit22 = new TypingUnit22; //│ globalThis.MM = typing_unit22.MM; //│ // End of generated code MM.a MM.b1 MM.b2 MM.c //│ 4 //│ res //│ = 1 //│ res //│ = 2 //│ res //│ = 3 //│ res //│ = 4 ================================================ FILE: shared/src/test/diff/codegen/MixinCapture.mls ================================================ :NewDefs :js class Lit(n: Int) mixin EvalAddLit { fun eval(e) = if e is Lit(n) then n } //│ class Lit(n: Int) //│ mixin EvalAddLit() { //│ fun eval: Lit -> Int //│ } //│ // Prelude //│ let res; //│ class TypingUnit { //│ #Lit; //│ constructor() { //│ } //│ EvalAddLit(base) { //│ const qualifier = this; //│ return (class EvalAddLit extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ eval(e) { //│ return ((() => { //│ let a; //│ return (a = e, a instanceof qualifier.Lit.class ? ((ucs$args_e$Lit) => ((n) => n)(ucs$args_e$Lit[0]))(qualifier.Lit.unapply(e)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); //│ } //│ }); //│ } //│ get Lit() { //│ const qualifier = this; //│ if (this.#Lit === undefined) { //│ class Lit { //│ #n; //│ constructor(n) { //│ this.#n = n; //│ } //│ static //│ unapply(x) { //│ return [x.#n]; //│ } //│ }; //│ this.#Lit = ((n) => Object.freeze(new Lit(n))); //│ this.#Lit.class = Lit; //│ this.#Lit.unapply = Lit.unapply; //│ } //│ return this.#Lit; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.Lit = typing_unit.Lit; //│ globalThis.EvalAddLit = ((base) => typing_unit.EvalAddLit(base)); //│ // End of generated code module TestLang extends EvalAddLit //│ module TestLang { //│ fun eval: Lit -> Int //│ } TestLang.eval(Lit(0)) //│ Int //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/codegen/Modules.mls ================================================ :NewDefs module test { // hello fun a = 1 } //│ module test { //│ fun a: 1 //│ } :js fun y = 1 module Foo { fun x = y } //│ fun y: 1 //│ module Foo { //│ fun x: 1 //│ } //│ // Prelude //│ class TypingUnit1 { //│ #Foo; //│ constructor() { //│ } //│ get Foo() { //│ const qualifier = this; //│ if (this.#Foo === undefined) { //│ class Foo { //│ constructor() { //│ } //│ get x() { //│ return y(); //│ } //│ } //│ this.#Foo = new Foo(); //│ this.#Foo.class = Foo; //│ } //│ return this.#Foo; //│ } //│ } //│ const typing_unit1 = new TypingUnit1; //│ globalThis.Foo = typing_unit1.Foo; //│ // Query 1 //│ globalThis.y = function y() { //│ return 1; //│ }; //│ // End of generated code :js module Foo { fun x = y } fun y = 1 //│ module Foo { //│ fun x: 1 //│ } //│ fun y: 1 //│ // Prelude //│ class TypingUnit2 { //│ #Foo; //│ constructor() { //│ } //│ get Foo() { //│ const qualifier = this; //│ if (this.#Foo === undefined) { //│ class Foo { //│ constructor() { //│ } //│ get x() { //│ return y1(); //│ } //│ } //│ this.#Foo = new Foo(); //│ this.#Foo.class = Foo; //│ } //│ return this.#Foo; //│ } //│ } //│ const typing_unit2 = new TypingUnit2; //│ globalThis.Foo = typing_unit2.Foo; //│ // Query 1 //│ globalThis.y1 = function y1() { //│ return 1; //│ }; //│ // End of generated code ================================================ FILE: shared/src/test/diff/codegen/MutFields.mls ================================================ :NewDefs :js module Foo { log("Creating Foo") mut val x: Int = 1 } //│ module Foo { //│ mut val x: Int //│ } //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ let res; //│ class TypingUnit { //│ #Foo; //│ constructor() { //│ } //│ get Foo() { //│ const qualifier = this; //│ if (this.#Foo === undefined) { //│ class Foo { //│ #x; //│ get x() { return this.#x; } //│ set x($value) { return this.#x = $value; } //│ constructor() { //│ log("Creating Foo"); //│ this.#x = 1; //│ const x = this.#x; //│ } //│ } //│ this.#Foo = new Foo(); //│ this.#Foo.class = Foo; //│ } //│ return this.#Foo; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.Foo = typing_unit.Foo; //│ // End of generated code Foo //│ Foo //│ res //│ = Foo { class: [class Foo] } //│ // Output //│ Creating Foo log(Foo.x) //│ () //│ res //│ = undefined //│ // Output //│ 1 :js set Foo.x = Foo.x + 1 //│ () //│ // Prelude //│ class TypingUnit3 {} //│ const typing_unit3 = new TypingUnit3; //│ // Query 1 //│ res = void(Foo.x = Foo.x + 1); //│ // End of generated code //│ res //│ = undefined :js log(Foo.x) //│ () //│ // Prelude //│ class TypingUnit4 {} //│ const typing_unit4 = new TypingUnit4; //│ // Query 1 //│ res = log(Foo.x); //│ // End of generated code //│ res //│ = undefined //│ // Output //│ 2 :e set Foo.y = 123 //│ ╔══[ERROR] Type `Foo` does not contain member `y` //│ ║ l.86: set Foo.y = 123 //│ ╙── ^^ //│ () //│ res //│ = undefined :e Foo.y //│ ╔══[ERROR] Type `Foo` does not contain member `y` //│ ║ l.95: Foo.y //│ ╙── ^^ //│ error //│ res //│ = 123 :js class Test { mut val x = 1 fun incr = log(x) set x = x + 1 log(x) } //│ class Test { //│ constructor() //│ fun incr: () //│ mut val x: Int //│ } //│ // Prelude //│ class TypingUnit7 { //│ #Test; //│ constructor() { //│ } //│ get Test() { //│ const qualifier = this; //│ if (this.#Test === undefined) { //│ class Test { //│ #x; //│ get x() { return this.#x; } //│ set x($value) { return this.#x = $value; } //│ constructor() { //│ this.#x = 1; //│ const x = this.#x; //│ } //│ get incr() { //│ const qualifier1 = this; //│ return ((() => { //│ log(qualifier1.x); //│ void(qualifier1.x = qualifier1.x + 1); //│ return log(qualifier1.x); //│ })()); //│ } //│ }; //│ this.#Test = Test; //│ } //│ return this.#Test; //│ } //│ } //│ const typing_unit7 = new TypingUnit7; //│ globalThis.Test = typing_unit7.Test; //│ // End of generated code let t = new Test() //│ let t: Test //│ t //│ = Test {} t.x //│ Int //│ res //│ = 1 t.incr //│ () //│ res //│ = undefined //│ // Output //│ 1 //│ 2 t.x //│ Int //│ res //│ = 2 class Cont class MySumCont extends Cont { val init(n) = n + 1 mut val pc = init(0) } //│ class Cont { //│ constructor() //│ } //│ class MySumCont extends Cont { //│ constructor() //│ val init: Int -> Int //│ mut val pc: Int //│ } fun f(b) = if b then 0 else new MySumCont //│ fun f: Bool -> (0 | MySumCont) :js if f(false) is MySumCont then 1 else 2 //│ 1 | 2 //│ // Prelude //│ class TypingUnit14 {} //│ const typing_unit14 = new TypingUnit14; //│ // Query 1 //│ res = ((ucs$scrut$0) => ucs$scrut$0 instanceof MySumCont ? 1 : 2)(f(false)); //│ // End of generated code //│ res //│ = 1 new MySumCont().init //│ Int -> Int //│ res //│ = [Function (anonymous)] :pe :e :ng class Test(mut val x: Int) //│ ╔══[PARSE ERROR] Unexpected 'val' keyword in expression position //│ ║ l.212: class Test(mut val x: Int) //│ ╙── ^^^ //│ ╔══[ERROR] Unsupported field specification //│ ║ l.212: class Test(mut val x: Int) //│ ╙── ^ //│ class Test() module Foo { val x = 0 } //│ module Foo { //│ val x: 0 //│ } :e fun test = set Foo.x = 1 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.228: fun test = set Foo.x = 1 //│ ║ ^^^^^^^^^^^^^ //│ ╟── member of type `0` is not mutable //│ ║ l.222: module Foo { val x = 0 } //│ ║ ^^^^^ //│ ╟── but it flows into assigned field with expected type `?x` //│ ║ l.228: fun test = set Foo.x = 1 //│ ╙── ^^ //│ fun test: () test //│ () //│ res //│ = undefined Foo.x //│ 0 //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/codegen/NamedArgsCurry.mls ================================================ :NewDefs :js fun print(x: Int) = (y: Int, z: Int) => log([x, y, z]) let p = print(0) let r = p(z: 1, y: 2) //│ fun print: (x: Int) -> (y: Int, z: Int) -> () //│ let p: (y: Int, z: Int) -> () //│ let r: () //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ globalThis.print = function print(x) { //│ return ((y, z) => log([ //│ x, //│ y, //│ z //│ ])); //│ }; //│ // Query 2 //│ globalThis.p = print(0); //│ // Query 3 //│ globalThis.r = p(2, 1); //│ // End of generated code //│ p //│ = [Function (anonymous)] //│ r //│ = undefined //│ // Output //│ [ 0, 2, 1 ] :js fun print(x) = [x, x] let p = print let r = p(1) //│ fun print: forall 'a. 'a -> ['a, 'a] //│ let p: forall 'a. 'a -> ['a, 'a] //│ let r: [1, 1] //│ // Prelude //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ globalThis.print1 = function print1(x) { //│ return ([ //│ x, //│ x //│ ]); //│ }; //│ // Query 2 //│ globalThis.p1 = print1; //│ // Query 3 //│ globalThis.r1 = p1(1); //│ // End of generated code //│ p //│ = [Function: print1] //│ r //│ = [ 1, 1 ] :js fun print(x) = (y) => [x, y] let p = print(0) let r = p(1) //│ fun print: forall 'a 'b. 'a -> 'b -> ['a, 'b] //│ let p: forall 'c. 'c -> [0, 'c] //│ let r: [0, 1] //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ globalThis.print2 = function print2(x) { //│ return ((y) => [ //│ x, //│ y //│ ]); //│ }; //│ // Query 2 //│ globalThis.p2 = print2(0); //│ // Query 3 //│ globalThis.r2 = p2(1); //│ // End of generated code //│ p //│ = [Function (anonymous)] //│ r //│ = [ 0, 1 ] ================================================ FILE: shared/src/test/diff/codegen/Nested.mls ================================================ :NewDefs :js module A { val a = 42 class B(x: Int) { fun b = x + 1 } } //│ module A { //│ class B(x: Int) { //│ fun b: Int //│ } //│ val a: 42 //│ } //│ // Prelude //│ let res; //│ class TypingUnit { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #B; //│ #a; //│ get a() { return this.#a; } //│ constructor() { //│ this.#a = 42; //│ const a = this.#a; //│ } //│ get B() { //│ const qualifier1 = this; //│ if (this.#B === undefined) { //│ class B { //│ #x; //│ constructor(x) { //│ this.#x = x; //│ } //│ get b() { //│ const x = this.#x; //│ return x + 1; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#B = ((x) => Object.freeze(new B(x))); //│ this.#B.class = B; //│ this.#B.unapply = B.unapply; //│ } //│ return this.#B; //│ } //│ } //│ this.#A = new A(); //│ this.#A.class = A; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.A = typing_unit.A; //│ // End of generated code :e :js let bb = A.B(A.a) bb.b //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.69: let bb = A.B(A.a) //│ ╙── ^^ //│ let bb: error //│ error //│ // Prelude //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ globalThis.bb = A.B(A.a); //│ // Query 2 //│ res = bb.b; //│ // End of generated code //│ bb //│ = B {} //│ res //│ = 43 :e class B(x: Int) { val outer = 42 class C(y: Int) { val outer1 = outer + outer } class D(val outer: Int) } let b = B(1) b.outer let c = b.C(1) c.outer1 let d = b.D(1) d.outer //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.100: let c = b.C(1) //│ ╙── ^^ //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.102: let d = b.D(1) //│ ╙── ^^ //│ class B(x: Int) { //│ class C(y: Int) { //│ val outer1: Int //│ } //│ class D(outer: Int) //│ val outer: 42 //│ } //│ let b: B //│ let c: error //│ let d: error //│ error //│ b //│ = B {} //│ res //│ = 42 //│ c //│ = C {} //│ res //│ = 84 //│ d //│ = D {} //│ res //│ = 1 :js mixin C() { mixin D() { mixin E() {} } } //│ mixin C() { //│ mixin D() { //│ mixin E() //│ } //│ } //│ // Prelude //│ class TypingUnit3 { //│ constructor() { //│ } //│ C(base) { //│ const qualifier = this; //│ return (class C extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ D(base) { //│ const qualifier1 = this; //│ return (class D extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ E(base) { //│ const qualifier2 = this; //│ return (class E extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ }); //│ } //│ }); //│ } //│ }); //│ } //│ } //│ const typing_unit3 = new TypingUnit3; //│ globalThis.C = ((base) => typing_unit3.C(base)); //│ // End of generated code :js module D { class E(val x: Int) {} fun createE(x: Int) = E(x + 1) } //│ module D { //│ class E(x: Int) //│ fun createE: (x: Int) -> E //│ } //│ // Prelude //│ class TypingUnit4 { //│ #D; //│ constructor() { //│ } //│ get D() { //│ const qualifier = this; //│ if (this.#D === undefined) { //│ class D { //│ #E; //│ constructor() { //│ } //│ createE(x) { //│ const qualifier1 = this; //│ return qualifier1.E(x + 1); //│ } //│ get E() { //│ const qualifier1 = this; //│ if (this.#E === undefined) { //│ class E { //│ #x; //│ get x() { return this.#x; } //│ constructor(x) { //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#E = ((x) => Object.freeze(new E(x))); //│ this.#E.class = E; //│ this.#E.unapply = E.unapply; //│ } //│ return this.#E; //│ } //│ } //│ this.#D = new D(); //│ this.#D.class = D; //│ } //│ return this.#D; //│ } //│ } //│ const typing_unit4 = new TypingUnit4; //│ globalThis.D = typing_unit4.D; //│ // End of generated code :js let ee = D.createE(42) ee.x //│ let ee: E //│ Int //│ // Prelude //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 //│ globalThis.ee = D.createE(42); //│ // Query 2 //│ res = ee.x; //│ // End of generated code //│ ee //│ = E {} //│ res //│ = 43 :js class E(x: Int) { class F(y: Int) { fun sum = x + y class G(z: Int) { fun sum = x + y + z } } } //│ class E(x: Int) { //│ class F(y: Int) { //│ class G(z: Int) { //│ fun sum: Int //│ } //│ fun sum: Int //│ } //│ } //│ // Prelude //│ class TypingUnit6 { //│ #E; //│ constructor() { //│ } //│ get E() { //│ const qualifier = this; //│ if (this.#E === undefined) { //│ class E { //│ #F; //│ #x; //│ constructor(x) { //│ this.#x = x; //│ } //│ get F() { //│ const qualifier1 = this; //│ if (this.#F === undefined) { //│ class F { //│ #G; //│ #y; //│ constructor(y) { //│ this.#y = y; //│ } //│ get sum() { //│ const y = this.#y; //│ return qualifier1.#x + y; //│ } //│ get G() { //│ const qualifier2 = this; //│ if (this.#G === undefined) { //│ class G { //│ #z; //│ constructor(z) { //│ this.#z = z; //│ } //│ get sum() { //│ const z = this.#z; //│ return qualifier1.#x + qualifier2.#y + z; //│ } //│ static //│ unapply(x) { //│ return [x.#z]; //│ } //│ }; //│ this.#G = ((z) => Object.freeze(new G(z))); //│ this.#G.class = G; //│ this.#G.unapply = G.unapply; //│ } //│ return this.#G; //│ } //│ static //│ unapply(x) { //│ return [x.#y]; //│ } //│ }; //│ this.#F = ((y) => Object.freeze(new F(y))); //│ this.#F.class = F; //│ this.#F.unapply = F.unapply; //│ } //│ return this.#F; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#E = ((x) => Object.freeze(new E(x))); //│ this.#E.class = E; //│ this.#E.unapply = E.unapply; //│ } //│ return this.#E; //│ } //│ } //│ const typing_unit6 = new TypingUnit6; //│ globalThis.E = typing_unit6.E; //│ // End of generated code :e :js let es = E(1) let fff = es.F(2) let gg = fff.G(3) gg.sum //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.349: let fff = es.F(2) //│ ╙── ^^ //│ let es: E //│ let fff: error //│ let gg: error //│ error //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ globalThis.es = E(1); //│ // Query 2 //│ globalThis.fff = es.F(2); //│ // Query 3 //│ globalThis.gg = fff.G(3); //│ // Query 4 //│ res = gg.sum; //│ // End of generated code //│ es //│ = E {} //│ fff //│ = F {} //│ gg //│ = G {} //│ res //│ = 6 :js class F() { let x = 42 class G() { let x1 = x + 1 } } //│ class F() { //│ class G() { //│ let x1: Int //│ } //│ let x: 42 //│ } //│ // Prelude //│ class TypingUnit8 { //│ #F; //│ constructor() { //│ } //│ get F() { //│ const qualifier = this; //│ if (this.#F === undefined) { //│ class F { //│ #G; //│ #x; //│ constructor() { //│ this.#x = 42; //│ const x = this.#x; //│ } //│ get G() { //│ const qualifier1 = this; //│ if (this.#G === undefined) { //│ class G { //│ constructor() { //│ const x1 = qualifier1.#x + 1; //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#G = (() => Object.freeze(new G())); //│ this.#G.class = G; //│ this.#G.unapply = G.unapply; //│ } //│ return this.#G; //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#F = (() => Object.freeze(new F())); //│ this.#F.class = F; //│ this.#F.unapply = F.unapply; //│ } //│ return this.#F; //│ } //│ } //│ const typing_unit8 = new TypingUnit8; //│ globalThis.F = typing_unit8.F; //│ // End of generated code :js module G { class I(val x: Int) {} module H { fun i1(x: Int) = I(x + 1) class J(x: Int) { fun ii(a: Int) = I(x + a) } } } //│ module G { //│ module H { //│ class J(x: Int) { //│ fun ii: (a: Int) -> I //│ } //│ fun i1: (x: Int) -> I //│ } //│ class I(x: Int) //│ } //│ // Prelude //│ class TypingUnit9 { //│ #G; //│ constructor() { //│ } //│ get G() { //│ const qualifier = this; //│ if (this.#G === undefined) { //│ class G { //│ #I; //│ #H; //│ constructor() { //│ } //│ get H() { //│ const qualifier1 = this; //│ if (this.#H === undefined) { //│ class H { //│ #J; //│ constructor() { //│ } //│ i1(x) { //│ return qualifier1.I(x + 1); //│ } //│ get J() { //│ const qualifier2 = this; //│ if (this.#J === undefined) { //│ class J { //│ #x; //│ constructor(x) { //│ this.#x = x; //│ } //│ ii(a) { //│ const x = this.#x; //│ return qualifier1.I(x + a); //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#J = ((x) => Object.freeze(new J(x))); //│ this.#J.class = J; //│ this.#J.unapply = J.unapply; //│ } //│ return this.#J; //│ } //│ } //│ this.#H = new H(); //│ this.#H.class = H; //│ } //│ return this.#H; //│ } //│ get I() { //│ const qualifier1 = this; //│ if (this.#I === undefined) { //│ class I { //│ #x; //│ get x() { return this.#x; } //│ constructor(x) { //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#I = ((x) => Object.freeze(new I(x))); //│ this.#I.class = I; //│ this.#I.unapply = I.unapply; //│ } //│ return this.#I; //│ } //│ } //│ this.#G = new G(); //│ this.#G.class = G; //│ } //│ return this.#G; //│ } //│ } //│ const typing_unit9 = new TypingUnit9; //│ globalThis.G = typing_unit9.G; //│ // End of generated code :e :js let jj = G.H.J(42) let i = jj.ii(2) i.x //│ ╔══[ERROR] Access to module member not yet supported //│ ║ l.548: let jj = G.H.J(42) //│ ╙── ^^ //│ let jj: error //│ let i: error //│ error //│ // Prelude //│ class TypingUnit10 {} //│ const typing_unit10 = new TypingUnit10; //│ // Query 1 //│ globalThis.jj = G.H.J(42); //│ // Query 2 //│ globalThis.i = jj.ii(2); //│ // Query 3 //│ res = i.x; //│ // End of generated code //│ jj //│ = J {} //│ i //│ = I {} //│ res //│ = 44 :js module H { class I(val x: Int) class J(x: Int) { val i = I(x + 1) } } //│ module H { //│ class I(x: Int) //│ class J(x: Int) { //│ val i: I //│ } //│ } //│ // Prelude //│ class TypingUnit11 { //│ #H; //│ constructor() { //│ } //│ get H() { //│ const qualifier = this; //│ if (this.#H === undefined) { //│ class H { //│ #I; //│ #J; //│ constructor() { //│ } //│ get I() { //│ const qualifier1 = this; //│ if (this.#I === undefined) { //│ class I { //│ #x; //│ get x() { return this.#x; } //│ constructor(x) { //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#I = ((x) => Object.freeze(new I(x))); //│ this.#I.class = I; //│ this.#I.unapply = I.unapply; //│ } //│ return this.#I; //│ } //│ get J() { //│ const qualifier1 = this; //│ if (this.#J === undefined) { //│ class J { //│ #x; //│ #i; //│ get i() { return this.#i; } //│ constructor(x) { //│ this.#x = x; //│ this.#i = qualifier1.I(x + 1); //│ const i = this.#i; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#J = ((x) => Object.freeze(new J(x))); //│ this.#J.class = J; //│ this.#J.unapply = J.unapply; //│ } //│ return this.#J; //│ } //│ } //│ this.#H = new H(); //│ this.#H.class = H; //│ } //│ return this.#H; //│ } //│ } //│ const typing_unit11 = new TypingUnit11; //│ globalThis.H = typing_unit11.H; //│ // End of generated code :e :js let j = H.J(42) j.i.x //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.657: let j = H.J(42) //│ ╙── ^^ //│ let j: error //│ error //│ // Prelude //│ class TypingUnit12 {} //│ const typing_unit12 = new TypingUnit12; //│ // Query 1 //│ globalThis.j = H.J(42); //│ // Query 2 //│ res = j.i.x; //│ // End of generated code //│ j //│ = J {} //│ res //│ = 43 :js :e class I(x: Int) { let y = x + 1 class J(x: Int) { let y = x + 2 fun incY = y + 1 } } let i = I(1) let ij = i.J(0) ij.incY //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.687: let ij = i.J(0) //│ ╙── ^^ //│ class I(x: Int) { //│ class J(x: Int) { //│ fun incY: Int //│ let y: Int //│ } //│ let y: Int //│ } //│ let i: I //│ let ij: error //│ error //│ // Prelude //│ class TypingUnit13 { //│ #I; //│ constructor() { //│ } //│ get I() { //│ const qualifier = this; //│ if (this.#I === undefined) { //│ class I { //│ #J; //│ #x; //│ constructor(x) { //│ this.#x = x; //│ const y = x + 1; //│ } //│ get J() { //│ const qualifier1 = this; //│ if (this.#J === undefined) { //│ class J { //│ #y; //│ #x; //│ constructor(x) { //│ this.#x = x; //│ this.#y = x + 2; //│ const y = this.#y; //│ } //│ get incY() { //│ const x = this.#x; //│ const qualifier2 = this; //│ return qualifier2.#y + 1; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#J = ((x) => Object.freeze(new J(x))); //│ this.#J.class = J; //│ this.#J.unapply = J.unapply; //│ } //│ return this.#J; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#I = ((x) => Object.freeze(new I(x))); //│ this.#I.class = I; //│ this.#I.unapply = I.unapply; //│ } //│ return this.#I; //│ } //│ } //│ const typing_unit13 = new TypingUnit13; //│ globalThis.I = typing_unit13.I; //│ // Query 1 //│ globalThis.i1 = I(1); //│ // Query 2 //│ globalThis.ij = i1.J(0); //│ // Query 3 //│ res = ij.incY; //│ // End of generated code //│ i //│ = I {} //│ ij //│ = J {} //│ res //│ = 3 :js module J { class K(x: Int) {} mixin L() {} class M() extends K(1) {} class N(x2: Int) extends K(x2 + 2), L } //│ module J { //│ class K(x: Int) //│ mixin L() //│ class M() extends K //│ class N(x2: Int) extends K //│ } //│ // Prelude //│ class TypingUnit14 { //│ #J; //│ constructor() { //│ } //│ get J() { //│ const qualifier = this; //│ if (this.#J === undefined) { //│ class J { //│ #K; //│ #M; //│ #N; //│ constructor() { //│ } //│ L(base) { //│ const qualifier1 = this; //│ return (class L extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ }); //│ } //│ get K() { //│ const qualifier1 = this; //│ if (this.#K === undefined) { //│ class K { //│ #x; //│ constructor(x) { //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#K = ((x) => Object.freeze(new K(x))); //│ this.#K.class = K; //│ this.#K.unapply = K.unapply; //│ } //│ return this.#K; //│ } //│ get M() { //│ const qualifier1 = this; //│ if (this.#M === undefined) { //│ class M extends qualifier1.K.class { //│ constructor() { //│ super(1); //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#M = (() => Object.freeze(new M())); //│ this.#M.class = M; //│ this.#M.unapply = M.unapply; //│ } //│ return this.#M; //│ } //│ get N() { //│ const qualifier1 = this; //│ if (this.#N === undefined) { //│ class N extends qualifier1.L(qualifier1.K.class) { //│ #x2; //│ constructor(x2) { //│ super(x2 + 2); //│ this.#x2 = x2; //│ } //│ static //│ unapply(x) { //│ return [x.#x2]; //│ } //│ }; //│ this.#N = ((x2) => Object.freeze(new N(x2))); //│ this.#N.class = N; //│ this.#N.unapply = N.unapply; //│ } //│ return this.#N; //│ } //│ } //│ this.#J = new J(); //│ this.#J.class = J; //│ } //│ return this.#J; //│ } //│ } //│ const typing_unit14 = new TypingUnit14; //│ globalThis.J = typing_unit14.J; //│ // End of generated code :e :js let m = J.M() let n = J.N(2) //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.878: let m = J.M() //│ ╙── ^^ //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.879: let n = J.N(2) //│ ╙── ^^ //│ let m: error //│ let n: error //│ // Prelude //│ class TypingUnit15 {} //│ const typing_unit15 = new TypingUnit15; //│ // Query 1 //│ globalThis.m = J.M(); //│ // Query 2 //│ globalThis.n = J.N(2); //│ // End of generated code //│ m //│ = M {} //│ n //│ = N {} module K { let x = 1 module L { let x = 42 class M() { fun f = x } } } //│ module K { //│ module L { //│ class M() { //│ fun f: 42 //│ } //│ let x: 42 //│ } //│ let x: 1 //│ } :e let m = K.L.M() m.f //│ ╔══[ERROR] Access to module member not yet supported //│ ║ l.922: let m = K.L.M() //│ ╙── ^^ //│ let m: error //│ error //│ m //│ = M {} //│ res //│ = 42 module L { class M(val x: Int) {} module N { module O { class P(y: Int) extends M(y + 1) {} } } } //│ module L { //│ class M(x: Int) //│ module N { //│ module O { //│ class P(y: Int) extends M //│ } //│ } //│ } :e let op = L.N.O.P(0) op.x //│ ╔══[ERROR] Access to module member not yet supported //│ ║ l.952: let op = L.N.O.P(0) //│ ╙── ^^ //│ let op: error //│ error //│ op //│ = P {} //│ res //│ = 1 :js :e module M { module N { fun op(x) = if x is O then 0 P then 1 _ then 2 } class O() class P() fun op(x) = if x is O then 0 P then 1 _ then 2 } M.N.op(M.P()) //│ ╔══[ERROR] Access to module member not yet supported //│ ║ l.980: M.N.op(M.P()) //│ ╙── ^^ //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.980: M.N.op(M.P()) //│ ╙── ^^ //│ module M { //│ module N { //│ fun op: Object -> (0 | 1 | 2) //│ } //│ class O() //│ class P() //│ fun op: Object -> (0 | 1 | 2) //│ } //│ error //│ // Prelude //│ class TypingUnit20 { //│ #M; //│ constructor() { //│ } //│ get M() { //│ const qualifier = this; //│ if (this.#M === undefined) { //│ class M { //│ #O; //│ #P; //│ #N; //│ constructor() { //│ } //│ op(x) { //│ let a; //│ const qualifier1 = this; //│ return a = x, a instanceof qualifier1.O.class ? 0 : a instanceof qualifier1.P.class ? 1 : 2; //│ } //│ get N() { //│ const qualifier1 = this; //│ if (this.#N === undefined) { //│ class N { //│ constructor() { //│ } //│ op(x) { //│ let a; //│ return a = x, a instanceof qualifier1.O.class ? 0 : a instanceof qualifier1.P.class ? 1 : 2; //│ } //│ } //│ this.#N = new N(); //│ this.#N.class = N; //│ } //│ return this.#N; //│ } //│ get O() { //│ const qualifier1 = this; //│ if (this.#O === undefined) { //│ class O { //│ constructor() { //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#O = (() => Object.freeze(new O())); //│ this.#O.class = O; //│ this.#O.unapply = O.unapply; //│ } //│ return this.#O; //│ } //│ get P() { //│ const qualifier1 = this; //│ if (this.#P === undefined) { //│ class P { //│ constructor() { //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#P = (() => Object.freeze(new P())); //│ this.#P.class = P; //│ this.#P.unapply = P.unapply; //│ } //│ return this.#P; //│ } //│ } //│ this.#M = new M(); //│ this.#M.class = M; //│ } //│ return this.#M; //│ } //│ } //│ const typing_unit20 = new TypingUnit20; //│ globalThis.M = typing_unit20.M; //│ // Query 1 //│ res = M.N.op(M.P()); //│ // End of generated code //│ res //│ = 1 :js module N { module O { class P() extends Q } class Q() } //│ module N { //│ module O { //│ class P() extends Q //│ } //│ class Q() //│ } //│ // Prelude //│ class TypingUnit21 { //│ #N; //│ constructor() { //│ } //│ get N() { //│ const qualifier = this; //│ if (this.#N === undefined) { //│ class N { //│ #Q; //│ #O; //│ constructor() { //│ } //│ get O() { //│ const qualifier1 = this; //│ if (this.#O === undefined) { //│ class O { //│ #P; //│ constructor() { //│ } //│ get P() { //│ const qualifier2 = this; //│ if (this.#P === undefined) { //│ class P extends qualifier1.Q.class { //│ constructor() { //│ super(); //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#P = (() => Object.freeze(new P())); //│ this.#P.class = P; //│ this.#P.unapply = P.unapply; //│ } //│ return this.#P; //│ } //│ } //│ this.#O = new O(); //│ this.#O.class = O; //│ } //│ return this.#O; //│ } //│ get Q() { //│ const qualifier1 = this; //│ if (this.#Q === undefined) { //│ class Q { //│ constructor() { //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#Q = (() => Object.freeze(new Q())); //│ this.#Q.class = Q; //│ this.#Q.unapply = Q.unapply; //│ } //│ return this.#Q; //│ } //│ } //│ this.#N = new N(); //│ this.#N.class = N; //│ } //│ return this.#N; //│ } //│ } //│ const typing_unit21 = new TypingUnit21; //│ globalThis.N = typing_unit21.N; //│ // End of generated code :e N.O.P() //│ ╔══[ERROR] Access to module member not yet supported //│ ║ l.1166: N.O.P() //│ ╙── ^^ //│ error //│ res //│ = P {} :js :e class I(x: Int) { let y = x + 1 class J(z: Int) { val a = [x, y, z] } } I(1).J(3).a //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.1182: I(1).J(3).a //│ ╙── ^^ //│ class I(x: Int) { //│ class J(z: Int) { //│ val a: [Int, Int, Int] //│ } //│ let y: Int //│ } //│ error //│ // Prelude //│ class TypingUnit23 { //│ #I; //│ constructor() { //│ } //│ get I() { //│ const qualifier = this; //│ if (this.#I === undefined) { //│ class I { //│ #J; //│ #y; //│ #x; //│ constructor(x) { //│ this.#x = x; //│ this.#y = x + 1; //│ const y = this.#y; //│ } //│ get J() { //│ const qualifier1 = this; //│ if (this.#J === undefined) { //│ class J { //│ #z; //│ #a; //│ get a() { return this.#a; } //│ constructor(z) { //│ this.#z = z; //│ this.#a = [ //│ qualifier1.#x, //│ qualifier1.#y, //│ z //│ ]; //│ const a = this.#a; //│ } //│ static //│ unapply(x) { //│ return [x.#z]; //│ } //│ }; //│ this.#J = ((z) => Object.freeze(new J(z))); //│ this.#J.class = J; //│ this.#J.unapply = J.unapply; //│ } //│ return this.#J; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#I = ((x) => Object.freeze(new I(x))); //│ this.#I.class = I; //│ this.#I.unapply = I.unapply; //│ } //│ return this.#I; //│ } //│ } //│ const typing_unit23 = new TypingUnit23; //│ globalThis.I = typing_unit23.I; //│ // Query 1 //│ res = I(1).J(3).a; //│ // End of generated code //│ res //│ = [ 1, 2, 3 ] :js fun main = let f(x: Int): Int = if x is 0 then 1 else g(x - 1) let g(x: Int): Int = f(x) f //│ fun main: (x: Int) -> Int //│ // Prelude //│ class TypingUnit24 {} //│ const typing_unit24 = new TypingUnit24; //│ // Query 1 //│ globalThis.main = function main() { //│ return ((() => { //│ let f = (x) => x === 0 ? 1 : g(x - 1); //│ let g = (x) => f(x); //│ return f; //│ })()); //│ }; //│ // End of generated code :js fun mian = class A(x: Int) mixin B() module C A(42) //│ fun mian: A //│ // Prelude //│ class TypingUnit25 {} //│ const typing_unit25 = new TypingUnit25; //│ // Query 1 //│ globalThis.mian = function mian() { //│ return ((() => { //│ const A = (() => { //│ class A { //│ #x; //│ constructor(x) { //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ } //│ let ctor; //│ ctor = ((x) => new A(x)); //│ ctor.class = A; //│ return ctor; //│ })(); //│ const B = (base) => { //│ return (class B extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ }); //│ }; //│ const C = (() => { //│ class C {} //│ let ins; //│ ins = new C(); //│ ins.class = C; //│ return ins; //│ })(); //│ return A(42); //│ })()); //│ }; //│ // End of generated code :js fun mian = mixin B() class A(x: Int) extends B module C extends B [A, C] //│ fun mian: [(x: Int) -> A, C] //│ // Prelude //│ class TypingUnit26 {} //│ const typing_unit26 = new TypingUnit26; //│ // Query 1 //│ globalThis.mian1 = function mian1() { //│ return ((() => { //│ const B = (base) => { //│ return (class B extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ }); //│ }; //│ const A = (() => { //│ class A extends B(Object) { //│ #x; //│ constructor(x) { //│ super(); //│ this.#x = x; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ } //│ let ctor; //│ ctor = ((x) => new A(x)); //│ ctor.class = A; //│ return ctor; //│ })(); //│ const C = (() => { //│ class C extends B(Object) { //│ constructor() { //│ super(); //│ } //│ } //│ let ins; //│ ins = new C(); //│ ins.class = C; //│ return ins; //│ })(); //│ return ([ //│ A, //│ C //│ ]); //│ })()); //│ }; //│ // End of generated code :js fun main(arg) = let x = arg + 1 let foo(y) = x + y class C(u: Int) { fun z = [foo(u), bar] } let bar = x C(123) //│ fun main: Int -> C //│ // Prelude //│ class TypingUnit27 {} //│ const typing_unit27 = new TypingUnit27; //│ // Query 1 //│ globalThis.main1 = function main1(arg) { //│ return ((() => { //│ let x = arg + 1; //│ let foo = (y) => x + y; //│ const C = (() => { //│ class C { //│ #u; //│ constructor(u) { //│ this.#u = u; //│ } //│ get z() { //│ const u = this.#u; //│ return ([ //│ foo(u), //│ bar //│ ]); //│ } //│ static //│ unapply(x) { //│ return [x.#u]; //│ } //│ } //│ let ctor; //│ ctor = ((u) => new C(u)); //│ ctor.class = C; //│ return ctor; //│ })(); //│ let bar = x; //│ return C(123); //│ })()); //│ }; //│ // End of generated code module Test { log(0) module Foo { log(2) } log(1) discard(Foo) log(3) discard(Foo) } //│ module Test { //│ module Foo //│ } :js class Outer1(outer: Int) { log(outer) class Outer2(x: Int) { let outer = x + 1 } } //│ class Outer1(outer: Int) { //│ class Outer2(x: Int) { //│ let outer: Int //│ } //│ } //│ // Prelude //│ class TypingUnit29 { //│ #Outer1; //│ constructor() { //│ } //│ get Outer1() { //│ const qualifier = this; //│ if (this.#Outer1 === undefined) { //│ class Outer1 { //│ #Outer2; //│ #outer; //│ constructor(outer) { //│ this.#outer = outer; //│ log(outer); //│ } //│ get Outer2() { //│ const qualifier1 = this; //│ if (this.#Outer2 === undefined) { //│ class Outer2 { //│ #x; //│ constructor(x) { //│ this.#x = x; //│ const outer = x + 1; //│ } //│ static //│ unapply(x) { //│ return [x.#x]; //│ } //│ }; //│ this.#Outer2 = ((x) => Object.freeze(new Outer2(x))); //│ this.#Outer2.class = Outer2; //│ this.#Outer2.unapply = Outer2.unapply; //│ } //│ return this.#Outer2; //│ } //│ static //│ unapply(x) { //│ return [x.#outer]; //│ } //│ }; //│ this.#Outer1 = ((outer) => Object.freeze(new Outer1(outer))); //│ this.#Outer1.class = Outer1; //│ this.#Outer1.unapply = Outer1.unapply; //│ } //│ return this.#Outer1; //│ } //│ } //│ const typing_unit29 = new TypingUnit29; //│ globalThis.Outer1 = typing_unit29.Outer1; //│ // End of generated code ================================================ FILE: shared/src/test/diff/codegen/New.mls ================================================ :NewDefs class C //│ class C { //│ constructor() //│ } :js new C //│ C //│ // Prelude //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ res = new C; //│ // End of generated code //│ res //│ = C {} :e :js C() //│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword //│ ║ l.23: C() //│ ╙── ^ //│ C //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ res = C(); //│ // End of generated code //│ res //│ Runtime error: //│ TypeError: Class constructor C cannot be invoked without 'new' :js :e // TODO support first-class classes let c = C //│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword //│ ║ l.40: let c = C //│ ╙── ^ //│ let c: () -> C //│ // Prelude //│ class TypingUnit3 {} //│ const typing_unit3 = new TypingUnit3; //│ // Query 1 //│ globalThis.c = C; //│ // End of generated code //│ c //│ = [class C] :re // TODO should eventually be reject in type checking c() //│ C //│ res //│ Runtime error: //│ TypeError: Class constructor C cannot be invoked without 'new' class C() //│ class C() :js new C //│ C //│ // Prelude //│ class TypingUnit6 {} //│ const typing_unit6 = new TypingUnit6; //│ // Query 1 //│ res = new C.class; //│ // End of generated code //│ res //│ = C {} :js C() //│ C //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ res = C(); //│ // End of generated code //│ res //│ = C {} :js let c = C //│ let c: () -> C //│ // Prelude //│ class TypingUnit8 {} //│ const typing_unit8 = new TypingUnit8; //│ // Query 1 //│ globalThis.c1 = C; //│ // End of generated code //│ c //│ = [Function (anonymous)] { //│ class: [class C], //│ unapply: [Function: unapply] //│ } c() //│ C //│ res //│ = C {} :js class X(val a: Int) new X(1).a //│ class X(a: Int) //│ Int //│ // Prelude //│ class TypingUnit10 { //│ #X; //│ constructor() { //│ } //│ get X() { //│ const qualifier = this; //│ if (this.#X === undefined) { //│ class X { //│ #a; //│ get a() { return this.#a; } //│ constructor(a) { //│ this.#a = a; //│ } //│ static //│ unapply(x) { //│ return [x.#a]; //│ } //│ }; //│ this.#X = ((a) => Object.freeze(new X(a))); //│ this.#X.class = X; //│ this.#X.unapply = X.unapply; //│ } //│ return this.#X; //│ } //│ } //│ const typing_unit10 = new TypingUnit10; //│ globalThis.X = typing_unit10.X; //│ // Query 1 //│ res = new X.class(1).a; //│ // End of generated code //│ res //│ = 1 :js class X { val a = 1 } (new X).a //│ class X { //│ constructor() //│ val a: 1 //│ } //│ 1 //│ // Prelude //│ class TypingUnit11 { //│ #X; //│ constructor() { //│ } //│ get X() { //│ const qualifier = this; //│ if (this.#X === undefined) { //│ class X { //│ #a; //│ get a() { return this.#a; } //│ constructor() { //│ this.#a = 1; //│ const a = this.#a; //│ } //│ }; //│ this.#X = X; //│ } //│ return this.#X; //│ } //│ } //│ const typing_unit11 = new TypingUnit11; //│ globalThis.X = typing_unit11.X; //│ // Query 1 //│ res = (new X).a; //│ // End of generated code //│ res //│ = 1 ================================================ FILE: shared/src/test/diff/codegen/NewClasses.mls ================================================ :NewDefs class C[A](n: A) { fun f = g fun g = 0 } //│ class C[A](n: A) { //│ fun f: 0 //│ fun g: 0 //│ } ================================================ FILE: shared/src/test/diff/codegen/NewMatching.mls ================================================ :NewDefs class V0() class V1(val x: Int) class V2(x: Int, y: Int) class Pos(x: Int) class V22(c1: V2, c2: V2) class Half(invalid: Int, valid: Int) class None(no: Int) //│ class V0() //│ class V1(x: Int) //│ class V2(x: Int, y: Int) //│ class Pos(x: Int) //│ class V22(c1: V2, c2: V2) //│ class Half(invalid: Int, valid: Int) //│ class None(no: Int) V2.unapply(V2(114, 514)) //│ [Int, Int] //│ res //│ = [ 114, 514 ] :js fun sum(v) = if v is V0() then 0 V1(a) then a V2(a, b) then a + b Pos(x) and x > 0 then x V22(V2(x1, y1), V2(x2, y2)) then x1 + y1 + x2 + y2 Half(_, x) then x None(_) then 0 _ then -1 //│ fun sum: Object -> Int //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ globalThis.sum = function sum(v) { //│ return ((() => { //│ let a; //│ return a = v, a instanceof V0.class ? 0 : a instanceof V22.class ? ((ucs$args_v$V22) => ((v$V22_0) => ((v$V22_1) => v$V22_0 instanceof V2.class ? ((ucs$args_v$V22_0$V2) => ((x1) => ((y1) => v$V22_1 instanceof V2.class ? ((ucs$args_v$V22_1$V2) => ((x2) => ((y2) => x1 + y1 + x2 + y2)(ucs$args_v$V22_1$V2[1]))(ucs$args_v$V22_1$V2[0]))(V2.unapply(v$V22_1)) : -1)(ucs$args_v$V22_0$V2[1]))(ucs$args_v$V22_0$V2[0]))(V2.unapply(v$V22_0)) : -1)(ucs$args_v$V22[1]))(ucs$args_v$V22[0]))(V22.unapply(v)) : a instanceof V2.class ? ((ucs$args_v$V2) => ((a) => ((b) => a + b)(ucs$args_v$V2[1]))(ucs$args_v$V2[0]))(V2.unapply(v)) : a instanceof V1.class ? ((ucs$args_v$V1) => ((a) => a)(ucs$args_v$V1[0]))(V1.unapply(v)) : a instanceof Pos.class ? ((ucs$args_v$Pos) => ((x) => ((ucs$test$0) => ucs$test$0 === true ? x : -1)(x > 0))(ucs$args_v$Pos[0]))(Pos.unapply(v)) : a instanceof None.class ? 0 : a instanceof Half.class ? ((ucs$args_v$Half) => ((x) => x)(ucs$args_v$Half[1]))(Half.unapply(v)) : -1; //│ })()); //│ }; //│ // End of generated code sum(V0()) sum(V1(42)) sum(V2(1, 1)) sum(Pos(1)) sum(Pos(0)) sum(V22(V2(1, 2), V2(3, 4))) sum(Half(-1, 1)) sum(None(42)) sum(42) //│ Int //│ res //│ = 0 //│ res //│ = 42 //│ res //│ = 2 //│ res //│ = 1 //│ res //│ = -1 //│ res //│ = 10 //│ res //│ = 1 //│ res //│ = 0 //│ res //│ = -1 class Some[out T](val value: T) //│ class Some[T](value: T) :js fun get1(s) = if s is Some(V1(x)) then x Some(y) then y //│ fun get1: forall 'a. Some[Object & 'a] -> (Int | 'a) //│ // Prelude //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 //│ globalThis.get1 = function get1(s) { //│ return ((() => { //│ let a; //│ return (a = s, a instanceof Some.class ? ((ucs$args_s$Some) => ((s$Some_0) => s$Some_0 instanceof V1.class ? ((ucs$args_s$Some_0$V1) => ((x) => x)(ucs$args_s$Some_0$V1[0]))(V1.unapply(s$Some_0)) : ((y) => y)(ucs$args_s$Some[0]))(ucs$args_s$Some[0]))(Some.unapply(s)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); //│ }; //│ // End of generated code get1(Some(V1(1))) get1(Some(V0())) //│ Int | V0 //│ res //│ = 1 //│ res //│ = V0 {} let s2 = Some(1) //│ let s2: Some[1] //│ s2 //│ = Some {} :js fun foo(s) = if s is Some(t) then let b = s2.value in b + t.x _ then 0 //│ fun foo: (Object & ~#Some | Some[{x: Int}]) -> Int //│ // Prelude //│ class TypingUnit8 {} //│ const typing_unit8 = new TypingUnit8; //│ // Query 1 //│ globalThis.foo = function foo(s) { //│ return ((() => { //│ return s instanceof Some.class ? ((ucs$args_s$Some) => ((t) => ((b) => b + t.x)(s2.value))(ucs$args_s$Some[0]))(Some.unapply(s)) : 0; //│ })()); //│ }; //│ // End of generated code foo(Some(V1(12))) //│ Int //│ res //│ = 13 fun bar(s) = if s is Some(_) then let b = s2.value in b + 1 _ then 0 //│ fun bar: (Object & ~#Some | Some[anything]) -> Int bar(Some(V1(12))) //│ Int //│ res //│ = 2 :js class FooBar { val x = 42 } //│ class FooBar { //│ constructor() //│ val x: 42 //│ } //│ // Prelude //│ class TypingUnit12 { //│ #FooBar; //│ constructor() { //│ } //│ get FooBar() { //│ const qualifier = this; //│ if (this.#FooBar === undefined) { //│ class FooBar { //│ #x; //│ get x() { return this.#x; } //│ constructor() { //│ this.#x = 42; //│ const x = this.#x; //│ } //│ }; //│ this.#FooBar = FooBar; //│ } //│ return this.#FooBar; //│ } //│ } //│ const typing_unit12 = new TypingUnit12; //│ globalThis.FooBar = typing_unit12.FooBar; //│ // End of generated code :js fun t(x) = if x is FooBar then 1 _ then 0 //│ fun t: Object -> (0 | 1) //│ // Prelude //│ class TypingUnit13 {} //│ const typing_unit13 = new TypingUnit13; //│ // Query 1 //│ globalThis.t = function t(x) { //│ return ((() => { //│ return x instanceof FooBar ? 1 : 0; //│ })()); //│ }; //│ // End of generated code t(new FooBar()) //│ 0 | 1 //│ res //│ = 1 :e fun ft(x) = if x is FooBar(x) then x _ then 0 //│ ╔══[ERROR] Construction of unparameterized class FooBar should use the `new` keyword //│ ║ l.203: FooBar(x) then x //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.203: FooBar(x) then x //│ ║ ^^^^^^ //│ ╙── reference of type `() -> FooBar` does not have field 'unapply' //│ fun ft: Object -> (0 | error) :js module MM fun m(x) = if x is MM then 1 _ then 0 //│ module MM //│ fun m: Object -> (0 | 1) //│ // Prelude //│ class TypingUnit16 { //│ #MM; //│ constructor() { //│ } //│ get MM() { //│ const qualifier = this; //│ if (this.#MM === undefined) { //│ class MM {} //│ this.#MM = new MM(); //│ this.#MM.class = MM; //│ } //│ return this.#MM; //│ } //│ } //│ const typing_unit16 = new TypingUnit16; //│ globalThis.MM = typing_unit16.MM; //│ // Query 1 //│ globalThis.m = function m(x) { //│ return ((() => { //│ return x instanceof MM.class ? 1 : 0; //│ })()); //│ }; //│ // End of generated code :e class VVC(v: Int, vc: Int) fun c(x) = if x is VVC(x, y, z) then x + y + z _ then 0 //│ ╔══[ERROR] Type mismatch in field selection: //│ ╟── tuple literal of type `{0: ?#v, 1: ?#vc}` does not have field '2' //│ ║ l.248: class VVC(v: Int, vc: Int) //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into operator application with expected type `{2: ?a}` //│ ║ l.250: if x is //│ ║ ^^^^ //│ ║ l.251: VVC(x, y, z) then x + y + z //│ ╙── ^^^^^^^ //│ class VVC(v: Int, vc: Int) //│ fun c: Object -> Int // * Testing polymorphic `unapply` class C[A](f: A -> A) //│ class C[A](f: A -> A) let r = C.unapply //│ let r: forall '#f. (C[?] & {#f: '#f}) -> ['#f] //│ r //│ = [Function: unapply] let r2 = r(C(succ)) //│ let r2: [Int -> Int] //│ r2 //│ = [ [Function: succ] ] let r3 = (([f]) => f) of r2 //│ let r3: Int -> Int //│ r3 //│ = [Function: succ] r3(123) //│ Int //│ res //│ = 124 let r4 = r(C(id)) //│ let r4: ['A -> 'A] //│ r4 //│ = [ [Function: id] ] // * Notice that the type is not generalized (lack of distributivity?) let g = (([f]) => f) of r4 //│ let g: 'A -> 'A //│ g //│ = [Function: id] g(0) //│ 0 //│ res //│ = 0 // * Approximated type g(true) //│ 0 | true //│ res //│ = true ================================================ FILE: shared/src/test/diff/codegen/NewMutualRef.mls ================================================ :NewDefs :js fun foo: Int -> Int fun foo = x => x + 1 // <- This will be added as a value symbol before translating `Bar`. class Bar { fun calc(x) = foo(x) } //│ fun foo: Int -> Int //│ class Bar { //│ constructor() //│ fun calc: Int -> Int //│ } //│ fun foo: Int -> Int //│ // Prelude //│ let res; //│ class TypingUnit { //│ #Bar; //│ constructor() { //│ } //│ get Bar() { //│ const qualifier = this; //│ if (this.#Bar === undefined) { //│ class Bar { //│ constructor() { //│ } //│ calc(x) { //│ return foo(x); //│ } //│ }; //│ this.#Bar = Bar; //│ } //│ return this.#Bar; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.Bar = typing_unit.Bar; //│ // Query 1 is empty //│ // Query 2 //│ globalThis.foo = function foo(x) { //│ return x + 1; //│ }; //│ // End of generated code // Note: This test case looks trivial but it was like: // // ``` // :re // (new Bar()).calc(0) // //│ Int // //│ res // //│ Runtime error: // //│ ReferenceError: foo is not defined // ``` // // My fix is a little bit hacky. The root of the problem is: when generating // code within a class, we need all top-level bindings to be accessible. This // part of implementation of new-definition-typing chose to declare all term // `NuFunDef` as `ValueSymbol` in advance, but this can lead to the fact that // the same symbol is declared multiple times, thus wasting some runtime names. // Consequently, the code that references these wasted runtime names are invalid. // // Actually, I have a better solution, but it requires adjusting the order of // translation, and I don't have much time to spend on this at the moment. So, // my current fix is rather hacky. But I will complete this part after `PreTyper` // is finished, when I replacing the old Scope with the new Scope. // // Luyu Cheng on 2023/12/30 (new Bar()).calc(0) //│ Int //│ res //│ = 1 :js fun foo: Int -> Int fun foo = x => x + 1 class Bar { fun calc(x) = foo(x) } //│ fun foo: Int -> Int //│ class Bar { //│ constructor() //│ fun calc: Int -> Int //│ } //│ fun foo: Int -> Int //│ // Prelude //│ class TypingUnit2 { //│ #Bar; //│ constructor() { //│ } //│ get Bar() { //│ const qualifier = this; //│ if (this.#Bar === undefined) { //│ class Bar { //│ constructor() { //│ } //│ calc(x) { //│ return foo1(x); //│ } //│ }; //│ this.#Bar = Bar; //│ } //│ return this.#Bar; //│ } //│ } //│ const typing_unit2 = new TypingUnit2; //│ globalThis.Bar = typing_unit2.Bar; //│ // Query 1 is empty //│ // Query 2 //│ globalThis.foo1 = function foo1(x) { //│ return x + 1; //│ }; //│ // End of generated code foo(0) new Bar().calc(0) //│ Int //│ res //│ = 1 //│ res //│ = 1 :js class Bar { fun calc(x) = foo } fun foo: Int fun foo = 123 //│ class Bar { //│ constructor() //│ fun calc: anything -> Int //│ } //│ fun foo: 123 //│ fun foo: Int //│ // Prelude //│ class TypingUnit4 { //│ #Bar; //│ constructor() { //│ } //│ get Bar() { //│ const qualifier = this; //│ if (this.#Bar === undefined) { //│ class Bar { //│ constructor() { //│ } //│ calc(x) { //│ return foo2(); //│ } //│ }; //│ this.#Bar = Bar; //│ } //│ return this.#Bar; //│ } //│ } //│ const typing_unit4 = new TypingUnit4; //│ globalThis.Bar = typing_unit4.Bar; //│ // Query 1 is empty //│ // Query 2 //│ globalThis.foo2 = function foo2() { //│ return 123; //│ }; //│ // End of generated code foo new Bar().calc(0) //│ Int //│ res //│ = 123 //│ res //│ = 123 ================================================ FILE: shared/src/test/diff/codegen/NuClasses.mls ================================================ :NewDefs :js class Test(val n: Int) { fun inc = Test(n + 1) } //│ class Test(n: Int) { //│ fun inc: Test //│ } //│ // Prelude //│ let res; //│ class TypingUnit { //│ #Test; //│ constructor() { //│ } //│ get Test() { //│ const qualifier = this; //│ if (this.#Test === undefined) { //│ class Test { //│ #n; //│ get n() { return this.#n; } //│ constructor(n) { //│ this.#n = n; //│ } //│ get inc() { //│ const n = this.#n; //│ return qualifier.Test(n + 1); //│ } //│ static //│ unapply(x) { //│ return [x.#n]; //│ } //│ }; //│ this.#Test = ((n) => Object.freeze(new Test(n))); //│ this.#Test.class = Test; //│ this.#Test.unapply = Test.unapply; //│ } //│ return this.#Test; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.Test = typing_unit.Test; //│ // End of generated code Test(0).inc.n //│ Int //│ res //│ = 1 class Test2(val n: Int) { fun inc = Test3.inc(n) } module Test3 { fun inc(n) = Test2(n + 1) } //│ class Test2(n: Int) { //│ fun inc: Test2 //│ } //│ module Test3 { //│ fun inc: Int -> Test2 //│ } Test2(0).inc.n //│ Int //│ res //│ = 1 class C[out A](n: A) { fun f = g fun g = n } //│ class C[A](n: A) { //│ fun f: A //│ fun g: A //│ } :e let a = C[Int](42) a.f //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.78: let a = C[Int](42) //│ ╙── ^^^^^^ //│ let a: C[42] //│ 42 //│ a //│ = C {} //│ res //│ = 42 module Foo { fun f = C0() class C0() } //│ module Foo { //│ class C0() //│ fun f: C0 //│ } mixin M0(n: Int) { val m = n // this refers to specifically the `n` we had in parameter, not necessarily this.n fun bar = m fun foo = [n, m, bar] // should this be the same as `[this.n, this.m, this.bar]`? } //│ mixin M0(n: Int) { //│ fun bar: Int //│ fun foo: [Int, Int, Int] //│ val m: Int //│ } module M1 extends M0(123) { fun n = "n" fun m = "m" fun bar = "bar" } //│ module M1 { //│ fun bar: "bar" //│ fun foo: [Int, Int, Int] //│ fun m: "m" //│ fun n: "n" //│ } [M1.n, M1.m, M1.bar] //│ ["n", "m", "bar"] //│ res //│ = [ 'n', 'm', 'bar' ] // FIXME typing/runtime mismatch M1.foo //│ [Int, Int, Int] //│ res //│ = [ 123, 'm', 'bar' ] :e :js module M2 { val m = 100 fun foo(y) = fun bar(x) = x + y + this.m bar(10) } //│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. //│ ║ l.140: fun bar(x) = x + y + this.m //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.140: fun bar(x) = x + y + this.m //│ ╙── ^^ //│ module M2 { //│ fun foo: Int -> Int //│ val m: 100 //│ } //│ // Prelude //│ class TypingUnit11 { //│ #M2; //│ constructor() { //│ } //│ get M2() { //│ const qualifier = this; //│ if (this.#M2 === undefined) { //│ class M2 { //│ #m; //│ get m() { return this.#m; } //│ constructor() { //│ this.#m = 100; //│ const m = this.#m; //│ } //│ foo(y) { //│ const qualifier1 = this; //│ return ((() => { //│ let bar = (x) => x + y + qualifier1.m; //│ return bar(10); //│ })()); //│ } //│ } //│ this.#M2 = new M2(); //│ this.#M2.class = M2; //│ } //│ return this.#M2; //│ } //│ } //│ const typing_unit11 = new TypingUnit11; //│ globalThis.M2 = typing_unit11.M2; //│ // End of generated code M2.foo(1) //│ Int //│ res //│ = 111 ================================================ FILE: shared/src/test/diff/codegen/NuFuns.mls ================================================ :NewDefs :js fun foo = let bar(x) = x + 1 bar(10) //│ fun foo: Int //│ // Prelude //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ globalThis.foo = function foo() { //│ return ((() => { //│ let bar = (x) => x + 1; //│ return bar(10); //│ })()); //│ }; //│ // End of generated code fun foo = class C(a: Int) { fun bar(x) = a + x + 1 } C(100).bar(10) foo //│ fun foo: Int //│ Int //│ res //│ = 111 fun main = log("Hello") main //│ fun main: () //│ () //│ res //│ = undefined //│ // Output //│ Hello fun main = log("Hello") //│ fun main: () main //│ () //│ res //│ = undefined //│ // Output //│ Hello fun main = mixin B { log(1) } log(0) module M extends B log(2) main //│ fun main: () //│ () //│ res //│ = undefined //│ // Output //│ 0 //│ 1 //│ 2 ================================================ FILE: shared/src/test/diff/codegen/NuParentheses.mls ================================================ :NewDefs :js 16 / (2 / 2) //│ Num //│ // Prelude //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ res = 16 / (2 / 2); //│ // End of generated code //│ res //│ = 16 :js 1 - (3 - 5) //│ Int //│ // Prelude //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ res = 1 - (3 - 5); //│ // End of generated code //│ res //│ = 3 fun (--) minusminus(a, b) = a - b //│ fun (--) minusminus: (Int, Int) -> Int :js 1 -- (3 -- 5) //│ Int //│ // Prelude //│ class TypingUnit3 {} //│ const typing_unit3 = new TypingUnit3; //│ // Query 1 //│ res = minusminus(1, minusminus(3, 5)); //│ // End of generated code //│ res //│ = 3 fun (-+-) complex(a, b) = a - 2*b //│ fun (-+-) complex: (Int, Int) -> Int :js 1 -+- (3 -+- 5) //│ Int //│ // Prelude //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 //│ res = complex(1, complex(3, 5)); //│ // End of generated code //│ res //│ = 15 ================================================ FILE: shared/src/test/diff/codegen/NuReplHost.mls ================================================ :NewDefs // * This should crash due to `error`, // * but the crash is somehow swallowed and we get the result of the previous statement instead! // * The same happens with any other side effect, like `log(...)` // * Note: this doesn't happen if the last line is in a spearate diff-test block :showRepl :re fun foo(x) = error let r = foo(1) //│ fun foo: anything -> nothing //│ let r: nothing //│ ┌ Block at NuReplHost.mls:10 //│ ├─┬ Prelude //│ │ ├── Code //│ │ │ function error() { //│ │ │ throw new Error("an error was thrown"); //│ │ │ } //│ │ │ let res; //│ │ │ class TypingUnit {} //│ │ │ const typing_unit = new TypingUnit; //│ │ └── Reply //│ │ undefined //│ ├─┬ Query 1/2 //│ │ ├── Prelude: //│ │ ├── Code: //│ │ ├── globalThis.foo = function foo(x) { //│ │ ├── return error(); //│ │ ├── }; //│ │ ├── Intermediate: [Function: foo] //│ │ └── Reply: [success] [Function: foo] //│ └─┬ Query 2/2 //│ ├── Prelude: //│ ├── Code: //│ ├── globalThis.r = foo(1); //│ └── Reply: [runtime error] Error: an error was thrown //│ r //│ Runtime error: //│ Error: an error was thrown :re r //│ nothing //│ res //│ Runtime error: //│ ReferenceError: r is not defined ================================================ FILE: shared/src/test/diff/codegen/ParameterPattern.mls ================================================ // This test file ensures that parameter destruction patterns are translated correctly. () //│ res: () //│ = [] :js def volatile ((this, break)) = this + break //│ // Query 1 //│ globalThis.volatile1 = function volatile1([ //│ this$, //│ break$ //│ ]) { //│ return this$ + break$; //│ }; //│ // End of generated code //│ volatile: ((int, int,),) -> int //│ = [Function: volatile1] :js def volatile1 { debugger; continue } = debugger + continue //│ // Query 1 //│ globalThis.volatile11 = function volatile11({ //│ "debugger": debugger$, //│ "continue": continue$ //│ }) { //│ return debugger$ + continue$; //│ }; //│ // End of generated code //│ volatile1: {continue: int, debugger: int} -> int //│ = [Function: volatile11] :js def volatile2 export = export + 2 //│ // Query 1 //│ globalThis.volatile2 = function volatile2(export$) { //│ return export$ + 2; //│ }; //│ // End of generated code //│ volatile2: int -> int //│ = [Function: volatile2] ================================================ FILE: shared/src/test/diff/codegen/Parentheses.mls ================================================ () add mul id //│ res: () //│ = [] //│ res: int -> int -> int //│ = [Function: add] //│ res: int -> int -> int //│ = [Function: mul] //│ res: 'a -> 'a //│ = [Function: id] // Simple expressions. :e :js 1 + 1 * 2 / 4 - 5 //│ // Query 1 //│ res = 1 + 1 * 2 / 4 - 5; //│ // End of generated code //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.17: 1 + 1 * 2 / 4 - 5 //│ ║ ^^^^^^^^^^^^^ //│ ╟── operator application of type `number` is not an instance of type `int` //│ ║ l.17: 1 + 1 * 2 / 4 - 5 //│ ╙── ^^^^^^^^^ //│ res: int //│ = -3.5 :e :js 1 * (2 + 3) / 4 - 5 //│ // Query 1 //│ res = 1 * (2 + 3) / 4 - 5; //│ // End of generated code //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.32: 1 * (2 + 3) / 4 - 5 //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `number` is not an instance of type `int` //│ ║ l.32: 1 * (2 + 3) / 4 - 5 //│ ╙── ^^^^^^^^^^^^^^^ //│ res: error | int //│ = -3.75 :js x = 0 x > 0 && x < 0 //│ // Query 1 //│ globalThis.x = 0; //│ // Query 2 //│ res = x > 0 && x < 0; //│ // End of generated code //│ x: 0 //│ = 0 //│ res: bool //│ = false // Some complicated expressions. :js 1 + (if (case x + 1 of { int -> 8 | string -> 9 }) == 3 then 2 else 3) //│ // Query 1 //│ let a; //│ res = 1 + ((a = x + 1, Number.isInteger(a) ? 8 : a.constructor === String ? 9 : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()) == 3 ? 2 : 3); //│ // End of generated code //│ res: int //│ = 4 :js case x of { int -> 1 | string -> 0 } //│ // Query 1 //│ let b; //│ res = (b = x, Number.isInteger(b) ? 1 : b.constructor === String ? 0 : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ // End of generated code //│ res: 0 | 1 //│ = 1 // When generating comma expressions in comma-separated lists, don't forget to // wrap the expression in parentheses. :js (case x of { int -> 1 | string -> 0 }, case x of { int -> 1 | string -> 0 }) //│ // Query 1 //│ let c, d; //│ res = [ //│ (c = x, Number.isInteger(c) ? 1 : c.constructor === String ? 0 : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()), //│ (d = x, Number.isInteger(d) ? 1 : d.constructor === String ? 0 : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()) //│ ]; //│ // End of generated code //│ res: (0 | 1, 0 | 1,) //│ = [ 1, 1 ] // Don't add extra parentheses when generating invocations. :js add 0 1 //│ // Query 1 //│ res = add(0)(1); //│ // End of generated code //│ res: int //│ = 1 :js id (fun x -> x) //│ // Query 1 //│ res = id((x) => x); //│ // End of generated code //│ res: 'a -> 'a //│ = [Function (anonymous)] :js fun x -> {t1 = gt 1 2; t2 = 1} //│ // Prelude //│ function gt(x, y) { //│ if (arguments.length === 2) { //│ return x > y; //│ } else { //│ return (y) => x > y; //│ } //│ } //│ // Query 1 //│ res = ((x) => ({ //│ t1: gt(1)(2), //│ t2: 1 //│ })); //│ // End of generated code //│ res: anything -> {t1: bool, t2: 1} //│ = [Function: res] ================================================ FILE: shared/src/test/diff/codegen/PatternMatch.mls ================================================ trait T //│ Defined trait T // Traits with Primitives t = T 1 //│ t: 1 & #T //│ = [Number: 1] t + 1 //│ res: int //│ = 2 case t of { T -> 0 } //│ res: 0 //│ = 0 ================================================ FILE: shared/src/test/diff/codegen/Polyfill.mls ================================================ // If you didn't use any prelude, it will not be inserted. :js 0 //│ // Prelude //│ let res; //│ // Query 1 //│ res = 0; //│ // End of generated code //│ res: 0 //│ = 0 // Look, prelude is empty! :js 1 //│ // Query 1 //│ res = 1; //│ // End of generated code //│ res: 1 //│ = 1 // If we use `concat`, it will appear. :js concat "a" "b" //│ // Prelude //│ function concat(x, y) { //│ if (arguments.length === 2) { //│ return x + y; //│ } else { //│ return (y) => x + y; //│ } //│ } //│ // Query 1 //│ res = concat("a")("b"); //│ // End of generated code //│ res: string //│ = 'ab' // Define a function with the same name will shadow the default one. Of course! :js def add x y = x + y + x + y //│ // Query 1 //│ globalThis.add = function add(x) { //│ return (y) => x + y + x + y; //│ }; //│ // End of generated code //│ add: int -> int -> int //│ = [Function: add] // Now the add function is not the default one. :js add 1 2 //│ // Query 1 //│ res = add(1)(2); //│ // End of generated code //│ res: int //│ = 6 // `withConstruct` will be inserted only when `with` is used. :js n = 123 with { x = 1 } //│ // Prelude //│ function withConstruct(target, fields) { //│ if (typeof target === "string" || typeof target === "number" || typeof target === "boolean" || typeof target === "bigint" || typeof target === "symbol") { //│ return Object.assign(target, fields); //│ } //│ if (target instanceof String || target instanceof Number || target instanceof Boolean || target instanceof BigInt) { //│ return Object.assign(target.valueOf(), target, fields); //│ } //│ if (Array.isArray(target)) { //│ const clone = Array.from(target); //│ for (const key in target){ //│ clone[key] = target[key]; //│ } //│ for (const key in fields){ //│ clone[key] = fields[key]; //│ } //│ return clone; //│ } //│ if (target == null) { //│ return Object.assign({}, {}, fields); //│ } //│ const copy = Object.assign({}, target, fields); //│ Object.setPrototypeOf(copy, Object.getPrototypeOf(target)); //│ return copy; //│ } //│ // Query 1 //│ globalThis.n = withConstruct(123, { x: 1 }); //│ // End of generated code //│ n: 123 & {x: 1} //│ = [Number: 123] { x: 1 } :js n + 1 //│ // Query 1 //│ res = n + 1; //│ // End of generated code //│ res: int //│ = 124 // `withConstruct` should works on extended primitive values. m = n with { y = 2 } //│ m: 123 & {x: 1, y: 2} //│ = [Number: 123] { x: 1, y: 2 } // `m` should work well with following operations. m.x m.y m + 1 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 //│ res: int //│ = 124 p = m with { z = 9 } p.x p.y p.z p + 2 //│ p: 123 & {x: 1, y: 2, z: 9} //│ = [Number: 123] { x: 1, y: 2, z: 9 } //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 //│ res: 9 //│ = 9 //│ res: int //│ = 125 q = p with { x = 0; y = 0; z = 0 } q.x q.y q.z q + 3 //│ q: 123 & {x: 0, y: 0, z: 0} //│ = [Number: 123] { x: 0, y: 0, z: 0 } //│ res: 0 //│ = 0 //│ res: 0 //│ = 0 //│ res: 0 //│ = 0 //│ res: int //│ = 126 :js toString 0 //│ // Prelude //│ function toString(x) { //│ return String(x); //│ } //│ // Query 1 //│ res = toString(0); //│ // End of generated code //│ res: string //│ = '0' ================================================ FILE: shared/src/test/diff/codegen/Recursion.mls ================================================ :js rec def pow0 n x = if n > 0 then pow0 (n - 1) x * x else 1 //│ // Prelude //│ let res; //│ // Query 1 //│ globalThis.pow0 = function pow0(n) { //│ return (x) => n > 0 ? pow0(n - 1)(x) * x : 1; //│ }; //│ // End of generated code //│ pow0: int -> int -> int //│ = [Function: pow0] pow0 3 4 //│ res: int //│ = 64 :js def pow1 x = let rec go n = if n > 0 then go (n - 1) * x else 1 in go //│ // Query 1 //│ globalThis.pow1 = function pow1(x) { //│ return (((go) => go)(function go(n) { //│ return n > 0 ? go(n - 1) * x : 1; //│ })); //│ }; //│ // End of generated code //│ pow1: int -> int -> int //│ = [Function: pow1] pow1 3 4 //│ res: int //│ = 81 :js rec def pow2 n = { call = fun x -> if n > 0 then (pow2 (n - 1)).call x * x else 1 } //│ // Query 1 //│ globalThis.pow2 = function pow2(n) { //│ return { call: (x) => n > 0 ? pow2(n - 1).call(x) * x : 1 }; //│ }; //│ // End of generated code //│ pow2: int -> {call: int -> int} //│ = [Function: pow2] (pow2 3).call 4 //│ res: int //│ = 64 :ge let rec x = { f = x } in x //│ res: 'x //│ where //│ 'x :> {f: 'x} //│ Code generation encountered an error: //│ recursive non-function definition x is not supported :ge let x = { f = x } in x //│ res: 'x //│ where //│ 'x :> {f: 'x} //│ Code generation encountered an error: //│ unresolved symbol x // TODO later: code generation for recursive method :ge class A rec method M x = M //│ Defined class A //│ Defined A.M: A -> 'M //│ where //│ 'M :> anything -> 'M //│ Code generation encountered an error: //│ unresolved symbol M ================================================ FILE: shared/src/test/diff/codegen/ReplHost.mls ================================================ :showRepl class Box[T]: { inner: T } method Map: (T -> 'a) -> Box['a] method Map f = Box { inner = f this.inner } method Get = this.inner box0 = Box { inner = 0 } //│ Defined class Box[+T] //│ Declared Box.Map: Box['T] -> ('T -> 'a) -> Box['a] //│ Defined Box.Map: Box['T] -> ('T -> 'inner) -> Box['inner] //│ Defined Box.Get: Box['T] -> 'T //│ ┌ Block at ReplHost.mls:3 //│ ├─┬ Prelude //│ │ ├── Code //│ │ │ let res; //│ │ │ class Box { //│ │ │ constructor(fields) { //│ │ │ this.inner = fields.inner; //│ │ │ } //│ │ │ Map(f) { //│ │ │ const self = this; //│ │ │ return new Box({ inner: f(self.inner) }); //│ │ │ } //│ │ │ get Get() { //│ │ │ const self = this; //│ │ │ return self.inner; //│ │ │ } //│ │ │ } //│ │ └── Reply //│ │ undefined //│ └─┬ Query 1/1 //│ ├── Prelude: //│ ├── Code: //│ ├── globalThis.box0 = new Box({ inner: 0 }); //│ ├── Intermediate: Box { inner: 0 } //│ └── Reply: [success] Box { inner: 0 } //│ box0: Box[0] //│ = Box { inner: 0 } :showRepl box1 = Box { inner = 1 } //│ ┌ Block at ReplHost.mls:41 //│ ├── No prelude //│ └─┬ Query 1/1 //│ ├── Prelude: //│ ├── Code: //│ ├── globalThis.box1 = new Box({ inner: 1 }); //│ ├── Intermediate: Box { inner: 1 } //│ └── Reply: [success] Box { inner: 1 } //│ box1: Box[1] //│ = Box { inner: 1 } :showRepl case box1 of { Box -> 0 } //│ ┌ Block at ReplHost.mls:54 //│ ├── No prelude //│ └─┬ Query 1/1 //│ ├── Prelude: //│ ├── let a; //│ ├── Code: //│ ├── res = (a = box1, a instanceof Box ? 0 : (() => { //│ ├── throw new Error("non-exhaustive case expression"); //│ ├── })()); //│ ├── Intermediate: 0 //│ └── Reply: [success] 0 //│ res: 0 //│ = 0 :showRepl box1.Map (fun x -> add x 1) box1.Map (fun x -> add x 2) box1.Map (fun x -> Box { inner = x }) //│ ┌ Block at ReplHost.mls:70 //│ ├─┬ Prelude //│ │ ├── Code //│ │ │ function add(x, y) { //│ │ │ if (arguments.length === 2) { //│ │ │ return x + y; //│ │ │ } else { //│ │ │ return (y) => x + y; //│ │ │ } //│ │ │ } //│ │ └── Reply //│ │ undefined //│ ├─┬ Query 1/3 //│ │ ├── Prelude: //│ │ ├── Code: //│ │ ├── res = box1.Map((x) => add(x)(1)); //│ │ ├── Intermediate: Box { inner: 2 } //│ │ └── Reply: [success] Box { inner: 2 } //│ ├─┬ Query 2/3 //│ │ ├── Prelude: //│ │ ├── Code: //│ │ ├── res = box1.Map((x) => add(x)(2)); //│ │ ├── Intermediate: Box { inner: 3 } //│ │ └── Reply: [success] Box { inner: 3 } //│ └─┬ Query 3/3 //│ ├── Prelude: //│ ├── Code: //│ ├── res = box1.Map((x) => new Box({ inner: x })); //│ ├── Intermediate: Box { inner: Box { inner: 1 } } //│ └── Reply: [success] Box { inner: Box { inner: 1 } } //│ res: Box[int] //│ = Box { inner: 2 } //│ res: Box[int] //│ = Box { inner: 3 } //│ res: Box[Box[1]] //│ = Box { inner: Box { inner: 1 } } box1.Map (fun x -> Box { inner = x }) //│ res: Box[Box[1]] //│ = Box { inner: Box { inner: 1 } } ================================================ FILE: shared/src/test/diff/codegen/Res.mls ================================================ // This test tracks how `res` works. 0 //│ res: 0 //│ = 0 :js res + 1 //│ // Query 1 //│ res = res + 1; //│ // End of generated code //│ res: int //│ = 1 :js res + 1 //│ // Query 1 //│ res = res + 1; //│ // End of generated code //│ res: int //│ = 2 :js res + 1 //│ // Query 1 //│ res = res + 1; //│ // End of generated code //│ res: int //│ = 3 :js res + 1 //│ // Query 1 //│ res = res + 1; //│ // End of generated code //│ res: int //│ = 4 :js res + 1 //│ // Query 1 //│ res = res + 1; //│ // End of generated code //│ res: int //│ = 5 :js res == 4 //│ // Query 1 //│ res = res == 4; //│ // End of generated code //│ res: bool //│ = false :js def res = 0 //│ // Query 1 //│ globalThis.res1 = function res1() { //│ return 0; //│ }; //│ // End of generated code //│ res: 0 //│ = [Function: res1] // However, we can shadow `res`! :js res + 1 //│ // Query 1 //│ res = res1() + 1; //│ // End of generated code //│ res: int //│ = 1 :js res + 1 //│ // Query 1 //│ res = res + 1; //│ // End of generated code //│ res: int //│ = 2 ================================================ FILE: shared/src/test/diff/codegen/Shadowing.mls ================================================ :NewDefs :js x => x => x + 1 //│ anything -> Int -> Int //│ // Prelude //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ res = ((x) => (() => { //│ return ((x) => (() => { //│ return x + 1; //│ })()); //│ })()); //│ // End of generated code //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/codegen/SortClass.mls ================================================ // Simple one :js class B: A class A //│ Defined class B //│ Defined class A //│ // Prelude //│ let res; //│ class A {} //│ class B extends A { //│ constructor(fields) { //│ super(fields); //│ } //│ } //│ // End of generated code :js class E: D class D: C class C //│ Defined class E //│ Defined class D //│ Defined class C //│ // Prelude //│ class C {} //│ class D extends C { //│ constructor(fields) { //│ super(fields); //│ } //│ } //│ class E extends D { //│ constructor(fields) { //│ super(fields); //│ } //│ } //│ // End of generated code :js class F: { x: int } & G class G: { y: int } & H class K class H: { b: int } class I: J class J //│ Defined class F //│ Defined class G //│ Defined class K //│ Defined class H //│ Defined class I //│ Defined class J //│ // Prelude //│ class H { //│ constructor(fields) { //│ this.b = fields.b; //│ } //│ } //│ class J {} //│ class K {} //│ class G extends H { //│ constructor(fields) { //│ super(fields); //│ this.y = fields.y; //│ } //│ } //│ class I extends J { //│ constructor(fields) { //│ super(fields); //│ } //│ } //│ class F extends G { //│ constructor(fields) { //│ super(fields); //│ this.x = fields.x; //│ } //│ } //│ // End of generated code ================================================ FILE: shared/src/test/diff/codegen/Super.mls ================================================ :NewDefs :js mixin Foo0 { val foo0 = 0 } //│ mixin Foo0() { //│ val foo0: 0 //│ } //│ // Prelude //│ let res; //│ class TypingUnit { //│ constructor() { //│ } //│ Foo0(base) { //│ const qualifier = this; //│ return (class Foo0 extends base { //│ #foo0; //│ get foo0() { return this.#foo0; } //│ constructor(...rest) { //│ super(...rest); //│ this.#foo0 = 0; //│ const foo0 = this.#foo0; //│ } //│ }); //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.Foo0 = ((base) => typing_unit.Foo0(base)); //│ // End of generated code :js mixin Foo1 { val foo0 = 1 val foo1 = super.foo0 } //│ mixin Foo1() { //│ super: {foo0: 'foo0} //│ val foo0: 1 //│ val foo1: 'foo0 //│ } //│ // Prelude //│ class TypingUnit1 { //│ constructor() { //│ } //│ Foo1(base) { //│ const qualifier = this; //│ return (class Foo1 extends base { //│ #foo0; //│ get foo0() { return this.#foo0; } //│ #foo1; //│ get foo1() { return this.#foo1; } //│ constructor(...rest) { //│ super(...rest); //│ this.#foo0 = 1; //│ const foo0 = this.#foo0; //│ this.#foo1 = super.foo0; //│ const foo1 = this.#foo1; //│ } //│ }); //│ } //│ } //│ const typing_unit1 = new TypingUnit1; //│ globalThis.Foo1 = ((base) => typing_unit1.Foo1(base)); //│ // End of generated code module Test0 extends Foo0, Foo1 //│ module Test0 { //│ val foo0: 1 //│ val foo1: 0 //│ } [Test0.foo0, Test0.foo1] //│ [1, 0] //│ res //│ = [ 1, 0 ] :js :e mixin Foo2 { fun foo2 = super } //│ ╔══[ERROR] Illegal use of `super` //│ ║ l.84: fun foo2 = super //│ ╙── ^^^^^ //│ mixin Foo2() { //│ super: 'super //│ fun foo2: 'super //│ } //│ // Prelude //│ class TypingUnit4 { //│ constructor() { //│ } //│ Foo2(base) { //│ const qualifier = this; //│ return (class Foo2 extends base { //│ constructor(...rest) { //│ super(...rest); //│ } //│ get foo2() { //│ return super; //│ } //│ }); //│ } //│ } //│ const typing_unit4 = new TypingUnit4; //│ globalThis.Foo2 = ((base) => typing_unit4.Foo2(base)); //│ // End of generated code //│ Syntax error: //│ 'super' keyword unexpected here :re :js module Test0 extends Foo2 //│ module Test0 { //│ fun foo2: anything //│ } //│ // Prelude //│ class TypingUnit5 { //│ #Test0; //│ constructor() { //│ } //│ get Test0() { //│ const qualifier = this; //│ if (this.#Test0 === undefined) { //│ class Test0 extends Foo2(Object) { //│ constructor() { //│ super(); //│ } //│ } //│ this.#Test0 = new Test0(); //│ this.#Test0.class = Test0; //│ } //│ return this.#Test0; //│ } //│ } //│ const typing_unit5 = new TypingUnit5; //│ globalThis.Test0 = typing_unit5.Test0; //│ // End of generated code //│ Runtime error: //│ ReferenceError: Foo2 is not defined Test0 //│ Test0 //│ res //│ = Test0 { class: [Function: Test0] } Test0.foo2 //│ anything //│ res //│ = undefined class Foo extends Foo0 { fun foo0(n) = [super.foo0, super.foo0 + n] } //│ class Foo { //│ constructor() //│ fun foo0: Int -> [0, Int] //│ } ================================================ FILE: shared/src/test/diff/codegen/SymbolicOps.mls ================================================ :NewDefs fun (>>) compose(f, g) = x => g(f(x)) //│ fun (>>) compose: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c :js let r = succ >> succ //│ let r: Int -> Int //│ // Prelude //│ function succ(x) { //│ return x + 1; //│ } //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ globalThis.r = compose(succ, succ); //│ // End of generated code //│ r //│ = [Function (anonymous)] r(3) //│ Int //│ res //│ = 5 compose(succ, succ)(3) //│ Int //│ res //│ = 5 (succ >> succ)(3) //│ Int //│ res //│ = 5 (succ >> succ) of 3 //│ Int //│ res //│ = 5 // * Note the high left-precedence of `of` :e succ >> succ of 3 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: succ >> succ of 3 //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Int` is not a function //│ ║ l.44: succ >> succ of 3 //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) //│ ║ ^^^^^^^ //│ ╟── from reference: //│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) //│ ╙── ^ //│ error | Int -> nothing //│ res //│ = [Function (anonymous)] :js let f = (>>) //│ let f: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ globalThis.f = compose; //│ // End of generated code //│ f //│ = [Function: compose] f(succ, succ)(3) //│ Int //│ res //│ = 5 (>>)(succ, succ)(3) //│ Int //│ res //│ = 5 (>>) of succ, succ //│ Int -> Int //│ res //│ = [Function (anonymous)] ((>>) of succ, succ) of 3 //│ Int //│ res //│ = 5 :e // TODO parse this differently? (>>) of succ, succ of 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.94: (>>) of succ, succ //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.95: of 3 //│ ║ ^^^^^^ //│ ╟── application of type `Int` is not a function //│ ║ l.94: (>>) of succ, succ //│ ║ ^^^^ //│ ║ l.95: of 3 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) //│ ║ ^^^^^^^ //│ ╟── from reference: //│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) //│ ╙── ^ //│ error | Int -> nothing //│ res //│ = [Function (anonymous)] :ge // TODO support this syntax for builtin operators fun oops = (+) //│ fun oops: (Int, Int) -> Int //│ Code generation encountered an error: //│ unresolved symbol + fun (%) mod = div //│ fun (%) mod: (Int, Int) -> Int // TODO builtin `div` should compute *integer* division 12 % 5 //│ Int //│ res //│ = 2.4 // TODO support code-gen for non-top-level operator definitions module Nested { fun (++) conc(a, b) = concat(a)(b) log("abc" ++ "def") } //│ module Nested { //│ fun (++) conc: (Str, Str) -> Str //│ } //│ Code generation encountered an error: //│ unresolved symbol ++ // TODO abstract class Nested { fun (++) conc: (Int, Int) -> Int log("abc" ++ "def") } //│ ╔══[ERROR] identifier not found: ++ //│ ║ l.149: log("abc" ++ "def") //│ ╙── ^^ //│ abstract class Nested { //│ fun (++) conc: (Int, Int) -> Int //│ } //│ Code generation encountered an error: //│ unresolved symbol ++ fun (??) oops: (Int, Int) => Int //│ fun (??) oops: (Int, Int) -> Int 1 ?? 2 //│ Int //│ res //│ = //│ ?? is not implemented fun (??) oops: (Int, Int) => Int fun oops(a, b) = a + b //│ fun oops: (Int, Int) -> Int //│ fun (??) oops: (Int, Int) -> Int // FIXME-later (stub symbols) This is actually implemented... 1 ?? 2 //│ Int //│ res //│ = //│ ?? is not implemented fun oops: (Int, Int) => Int fun (??) oops(a, b) = a + b //│ fun (??) oops: (Int, Int) -> Int //│ fun oops: (Int, Int) -> Int 1 ?? 2 //│ Int //│ res //│ = 3 fun (!?) oops: (Int, Int) => Int fun (??) oops(a, b) = a + b //│ fun (??) oops: (Int, Int) -> Int //│ fun (!?) oops: (Int, Int) -> Int 1 ?? 2 //│ Int //│ res //│ = 3 1 !? 2 //│ Int //│ res //│ = //│ !? is not implemented // * Note: some malformed definitions :pe :e fun (>>)(f, g) = x => g(f(x)) //│ ╔══[PARSE ERROR] Expected a function name; found parenthesis section instead //│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: g //│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) //│ ║ ^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.215: fun (>>)(f, g) = x => g(f(x)) //│ ║ ^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.4: fun (>>) compose(f, g) = x => g(f(x)) //│ ╙── ^^^^^^ //│ fun (>>) : anything -> error //│ Code generation encountered an error: //│ unresolved symbol g :pe fun compose(>>)(f, g) = x => g(f(x)) //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.236: fun compose(>>)(f, g) = x => g(f(x)) //│ ╙── ^^ //│ fun compose: forall 'a 'b 'c. () -> ('a -> 'b, 'b -> 'c) -> 'a -> 'c :pe fun () foo(a, b) = a + b //│ ╔══[PARSE ERROR] Expected a symbolic name between brackets, found nothing //│ ║ l.244: fun () foo(a, b) = a + b //│ ╙── ^^ //│ fun foo: (Int, Int) -> Int :pe fun ( ) foo(a, b) = a + b //│ ╔══[PARSE ERROR] Expected a symbolic name, found space instead //│ ║ l.251: fun ( ) foo(a, b) = a + b //│ ╙── ^^^ //│ fun foo: (Int, Int) -> Int :pe fun ( ) foo(a, b) = a + b //│ ╔══[PARSE ERROR] Expected a symbolic name, found newline instead //│ ║ l.258: fun ( //│ ║ ^ //│ ║ l.259: ) foo(a, b) = a + b //│ ╙── //│ fun foo: (Int, Int) -> Int :pe fun (1) foo(a, b) = a + b //│ ╔══[PARSE ERROR] Expected a symbolic name, found literal instead //│ ║ l.268: fun (1) foo(a, b) = a + b //│ ╙── ^ //│ fun foo: (Int, Int) -> Int :pe fun (++ 1) foo(a, b) = a + b //│ ╔══[PARSE ERROR] Unexpected literal after symbolic name //│ ║ l.275: fun (++ 1) foo(a, b) = a + b //│ ╙── ^ //│ fun (++) foo: (Int, Int) -> Int :pe fun (a ++ 1) foo(a, b) = a + b //│ ╔══[PARSE ERROR] Expected a symbolic name, found identifier instead //│ ║ l.282: fun (a ++ 1) foo(a, b) = a + b //│ ╙── ^ //│ fun foo: (Int, Int) -> Int // should be `<<|+_+|>>`, but we got `<<|+` :pe fun (<<|+_+|>>) robot(a, b) = a + b //│ ╔══[PARSE ERROR] Unexpected identifier after symbolic name //│ ║ l.291: fun (<<|+_+|>>) robot(a, b) = a + b //│ ╙── ^ //│ fun (<<|+) robot: (Int, Int) -> Int fun (<<|+-+|>>) robot(a, b) = a + b //│ fun (<<|+-+|>>) robot: (Int, Int) -> Int 2 <<|+-+|>> 2 //│ Int //│ res //│ = 4 :pe fun (:-D) dd(a, b) = a + b //│ ╔══[PARSE ERROR] Unexpected identifier after symbolic name //│ ║ l.307: fun (:-D) dd(a, b) = a + b //│ ╙── ^ //│ fun (:-) dd: (Int, Int) -> Int // should be `:-D`, but we got `:-` val (->) f(x, y) = [x, y] //│ val (->) f: forall 'a 'b. ('a, 'b) -> ['a, 'b] //│ f //│ = [Function: f1] 12 -> 34 //│ [12, 34] //│ res //│ = [ 12, 34 ] let (->) _ = f //│ let (->) _: forall 'a 'b. ('a, 'b) -> ['a, 'b] //│ _ //│ = [Function: f1] :js 12 -> 34 //│ [12, 34] //│ // Prelude //│ class TypingUnit42 {} //│ const typing_unit42 = new TypingUnit42; //│ // Query 1 //│ res = _(12, 34); //│ // End of generated code //│ res //│ = [ 12, 34 ] ================================================ FILE: shared/src/test/diff/codegen/Terms.mls ================================================ // Define some classes. class A: { a: int } class B: { b: int } class C: { c: int } class D: { d: int } //│ Defined class A //│ Defined class B //│ Defined class C //│ Defined class D // Then define some values. def x = 0 def y = 1 def z = 2 def s = "s" //│ x: 0 //│ = [Function: x] //│ y: 1 //│ = [Function: y] //│ z: 2 //│ = [Function: z] //│ s: "s" //│ = [Function: s] // Var :js x //│ // Query 1 //│ res = x(); //│ // End of generated code //│ res: 0 //│ = 0 // Lam :js fun x -> x //│ // Query 1 //│ res = ((x) => x); //│ // End of generated code //│ res: 'a -> 'a //│ = [Function: res] // App(Var(op), lhs, rhs) :js 0 + 1 //│ // Query 1 //│ res = 0 + 1; //│ // End of generated code //│ res: int //│ = 1 // App(Var(op), lhs, rhs): Complex 1 :js x * y - 1 * 3 //│ // Query 1 //│ res = x() * y() - 1 * 3; //│ // End of generated code //│ res: int //│ = -3 // App(Var(op), lhs, rhs): Complex 2 :js :e z ** 4 * z / (x + 5) //│ // Query 1 //│ res = z() ** 4 * z() / (x() + 5); //│ // End of generated code //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.65: z ** 4 * z / (x + 5) //│ ║ ^^^^^^^^ //│ ╟── operator application of type `number` is not an instance of type `int` //│ ║ l.65: z ** 4 * z / (x + 5) //│ ╙── ^^^^^^ //│ res: number //│ = 6.4 // App(App(App(Var("if"), test), consequent), alternative) :js if true then 1 else 2 //│ // Query 1 //│ res = true ? 1 : 2; //│ // End of generated code //│ res: 1 | 2 //│ = 1 // App(Var(callee), ...): Construct objects conforming some traits :js trait T: { x: int } T { x = 0 } //│ Defined trait T //│ // Prelude //│ const T = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // Query 1 //│ res = T.build({ x: 0 }); //│ // End of generated code //│ res: {x: 0} & #T //│ = { x: 0 } // App(callee, Tup(args)): Call a constructor :js A { a = 0 } //│ // Query 1 //│ res = new A({ a: 0 }); //│ // End of generated code //│ res: A & {a: 0} //│ = A { a: 0 } // App(callee, Tup(args)): Call a function :js def f x = x f 0 //│ // Query 1 //│ globalThis.f = function f(x) { //│ return x; //│ }; //│ // Query 2 //│ res = f(0); //│ // End of generated code //│ f: 'a -> 'a //│ = [Function: f] //│ res: 0 //│ = 0 // Rcd: Empty :js {} //│ // Query 1 //│ res = {}; //│ // End of generated code //│ res: anything //│ = {} // Rcd: Sole :js { x = 0 } //│ // Query 1 //│ res = { x: 0 }; //│ // End of generated code //│ res: {x: 0} //│ = { x: 0 } // Rcd: Flat :js { x = 0; y = 0 } //│ // Query 1 //│ res = { //│ x: 0, //│ y: 0 //│ }; //│ // End of generated code //│ res: {x: 0, y: 0} //│ = { x: 0, y: 0 } // Rcd: Nested :js { x = { y = 0 }; z = 0 } //│ // Query 1 //│ res = { //│ x: { y: 0 }, //│ z: 0 //│ }; //│ // End of generated code //│ res: {x: {y: 0}, z: 0} //│ = { x: { y: 0 }, z: 0 } // Rcd: Super nested :js { x = { y = { a = 0; b = "s" } }; w = 0; a = (0, 0); z } //│ // Query 1 //│ res = { //│ x: { //│ y: { //│ a: 0, //│ b: "s" //│ } //│ }, //│ w: 0, //│ a: [ //│ 0, //│ 0 //│ ], //│ z: z() //│ }; //│ // End of generated code //│ res: {a: (0, 0,), w: 0, x: {y: {a: 0, b: "s"}}, z: 2} //│ = { x: { y: { a: 0, b: 's' } }, w: 0, a: [ 0, 0 ], z: 2 } // Sel: Select an immediate record :js { x = 0; y = 0 }.x //│ // Query 1 //│ res = ({ //│ x: 0, //│ y: 0 //│ }).x; //│ // End of generated code //│ res: 0 //│ = 0 // Sel: Select from an expression :js (if true then { x = 0 } else { x = 1 }).x //│ // Query 1 //│ res = (true ? { x: 0 } : { x: 1 }).x; //│ // End of generated code //│ res: 0 | 1 //│ = 0 // Let :js let x = 0 in x + 1 //│ // Query 1 //│ res = ((x) => x + 1)(0); //│ // End of generated code //│ res: int //│ = 1 // Blk: Unused in MLParser // CaseOf: Empty :js def f x = case x of { } //│ // Query 1 //│ globalThis.f1 = function f1(x) { //│ let a; //│ return (a = x, (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ }; //│ // End of generated code //│ f: nothing -> nothing //│ = [Function: f1] // CaseOf: Just the wildcard :js case 0 of { _ -> 1 } //│ // Query 1 //│ res = (0, 1); //│ // End of generated code //│ res: 1 //│ = 1 // CaseOf: One branch with the wildcard :js case 0 of { A -> 1 | _ -> 0 } //│ // Query 1 //│ res = 0 instanceof A ? 1 : 0; //│ // End of generated code //│ res: 0 | 1 //│ = 0 // CaseOf: Two branches with the wildcard :js case 0 of { A -> 1 | B -> 2 | _ -> 0 } //│ // Query 1 //│ let a; //│ res = (a = 0, a instanceof A ? 1 : a instanceof B ? 2 : 0); //│ // End of generated code //│ res: 0 | 1 | 2 //│ = 0 // CaseOf: Two branches without the wildcard :js case A { a = 0 } of { A -> "A" | B -> "B" } //│ // Query 1 //│ let b; //│ res = (b = new A({ a: 0 }), b instanceof A ? "A" : b instanceof B ? "B" : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ // End of generated code //│ res: "A" | "B" //│ = 'A' // IntLit :js 42 //│ // Query 1 //│ res = 42; //│ // End of generated code //│ res: 42 //│ = 42 // StrLit :js "Hello" //│ // Query 1 //│ res = "Hello"; //│ // End of generated code //│ res: "Hello" //│ = 'Hello' // Asc :js 42 : int //│ // Query 1 //│ res = 42; //│ // End of generated code //│ res: int //│ = 42 // Bra: How? // Tup :js (x, y) //│ // Query 1 //│ res = [ //│ x(), //│ y() //│ ]; //│ // End of generated code //│ res: (0, 1,) //│ = [ 0, 1 ] // With {} with {} //│ res: anything //│ = {} :js rcd1 = { x = "a"; y = "b" } rcd2 = rcd1 with { x = 1; z = 2 } (rcd1.x, rcd1.y, rcd2.x, rcd2.y, rcd2.z) //│ // Query 1 //│ globalThis.rcd1 = { //│ x: "a", //│ y: "b" //│ }; //│ // Query 2 //│ globalThis.rcd2 = withConstruct(rcd1, { //│ x: 1, //│ z: 2 //│ }); //│ // Query 3 //│ res = [ //│ rcd1.x, //│ rcd1.y, //│ rcd2.x, //│ rcd2.y, //│ rcd2.z //│ ]; //│ // End of generated code //│ rcd1: {x: "a", y: "b"} //│ = { x: 'a', y: 'b' } //│ rcd2: {x: 1, y: "b", z: 2} //│ = { x: 1, y: 'b', z: 2 } //│ res: ("a", "b", 1, "b", 2,) //│ = [ 'a', 'b', 1, 'b', 2 ] class M: {mut x : int} //│ Defined class M :js a1 = M {mut x = 233} a1.x <- 666 a1.x //│ // Query 1 //│ globalThis.a1 = new M({ x: 233 }); //│ // Query 2 //│ res = void(a1.x = 666); //│ // Query 3 //│ res = a1.x; //│ // End of generated code //│ a1: M with {mut x: 'x} //│ where //│ 'x :> 233 //│ <: int //│ = M { x: 233 } //│ = undefined //│ res: 233 //│ = 666 :js t1 = (mut "hello", mut true) //│ // Query 1 //│ globalThis.t1 = [ //│ "hello", //│ true //│ ]; //│ // End of generated code //│ t1: (mut 'a, mut 'b,) //│ where //│ 'b :> true //│ 'a :> "hello" //│ = [ 'hello', true ] :js t1.0 <- "bye" t1.0 //│ // Query 1 //│ res = void(t1[0] = "bye"); //│ // Query 2 //│ res = t1[0]; //│ // End of generated code //│ = undefined //│ res: "hello" //│ = 'bye' :js def muta1 : MutArray[int] muta1 = (mut 1, mut 2, mut 3, mut 4) //│ // Query 1 is empty //│ // Query 2 //│ globalThis.muta1 = [ //│ 1, //│ 2, //│ 3, //│ 4 //│ ]; //│ // End of generated code //│ muta1: MutArray[int] //│ = //│ (mut 'a, mut 'b, mut 'c, mut 'd,) //│ where //│ 'd :> 4 //│ 'c :> 3 //│ 'b :> 2 //│ 'a :> 1 //│ <: muta1: //│ MutArray[int] //│ = [ 1, 2, 3, 4 ] :js muta1[1] <- 233 muta1 //│ // Query 1 //│ res = void(muta1[1] = 233); //│ // Query 2 //│ res = muta1; //│ // End of generated code //│ = undefined //│ res: MutArray[int] //│ = [ 1, 233, 3, 4 ] :js def xpp rc = (fun _ -> rc) (rc.x <- rc.x + 1) xpp a1 //│ // Query 1 //│ globalThis.xpp = function xpp(rc) { //│ return ((_) => rc)(void(rc.x = rc.x + 1)); //│ }; //│ // Query 2 //│ res = xpp(a1); //│ // End of generated code //│ xpp: ({mut x: int} & 'a) -> 'a //│ = [Function: xpp] //│ res: M with {mut x: 'x} //│ where //│ 'x :> 233 //│ <: int //│ = M { x: 667 } :js tu = (mut (), mut 2) tu.0 <- (tu.1 <- 3) tu.0 tu.1 //│ // Query 1 //│ globalThis.tu = [ //│ [], //│ 2 //│ ]; //│ // Query 2 //│ res = void(tu[0] = void(tu[1] = 3)); //│ // Query 3 //│ res = tu[0]; //│ // Query 4 //│ res = tu[1]; //│ // End of generated code //│ tu: (mut 'a, mut 'b,) //│ where //│ 'b :> 2 //│ 'a :> () //│ = [ [], 2 ] //│ = undefined //│ res: () //│ = undefined //│ res: 2 //│ = 3 ================================================ FILE: shared/src/test/diff/codegen/TraitMethods.mls ================================================ // To generate the prelude without printing it in the next test case () //│ res: () //│ = [] :js trait T0 method Foo: int //│ Defined trait T0 //│ Declared T0.Foo: T0 -> int //│ // Prelude //│ const T0 = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code def foo x = x.Foo //│ foo: T0 -> int //│ = [Function: foo] :js trait T1 method Foo = 1 //│ Defined trait T1 //│ Defined T1.Foo: T1 -> 1 //│ // Prelude //│ const T1 = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ if (!("Foo" in instance)) { //│ Object.defineProperty(instance, "Foo", { //│ get: function () { //│ return 1; //│ } //│ }); //│ } //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code :js class A: T0 //│ Defined class A //│ // Prelude //│ class A { //│ constructor(fields) { //│ T0.implement(this); //│ } //│ } //│ // End of generated code :js class B: A & T1 //│ Defined class B //│ // Prelude //│ class B extends A { //│ constructor(fields) { //│ super(fields); //│ T1.implement(this); //│ } //│ } //│ // End of generated code :e :js a = A{} //│ // Query 1 //│ globalThis.a = new A({}); //│ // End of generated code //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.101: a = A{} //│ ║ ^ //│ ╟── Note that class A is abstract: //│ ║ l.77: class A: T0 //│ ║ ^^^^^ //│ ╟── Hint: method Foo is abstract //│ ║ l.9: method Foo: int //│ ╙── ^^^^^^^^ //│ a: error //│ = A {} foo a //│ res: int //│ = undefined // Note: calling convention not yet supported in JS a.(A.Foo) //│ res: int //│ = undefined :js b = B{} foo b //│ // Query 1 //│ globalThis.b = new B({}); //│ // Query 2 //│ res = foo(b); //│ // End of generated code //│ b: B //│ = B {} //│ res: int //│ = 1 b.(A.Foo) //│ res: int //│ = undefined :e type Id[X] = X class AAA method F: int method F = 1 method G: 'a -> 'a method G x = x class BBB: AAA method F: int method G: 'a -> 'a //│ ╔══[ERROR] Overriding method AAA.F without an overriding definition is not allowed. //│ ║ l.151: method F: int //│ ║ ^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.147: method F = 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Overriding method AAA.G without an overriding definition is not allowed. //│ ║ l.152: method G: 'a -> 'a //│ ║ ^^^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.149: method G x = x //│ ╙── ^^^^^^^ //│ Defined type alias Id[+X] //│ Defined class AAA //│ Declared AAA.F: AAA -> int //│ Declared AAA.G: AAA -> 'a -> 'a //│ Defined AAA.F: AAA -> 1 //│ Defined AAA.G: AAA -> 'a -> 'a //│ Defined class BBB //│ Declared BBB.F: BBB -> int //│ Declared BBB.G: BBB -> 'a -> 'a trait T3 method Foo: 1 | 2 trait T4 method Foo: 0 | 1 class C: T3 & T4 //│ Defined trait T3 //│ Declared T3.Foo: T3 -> (1 | 2) //│ Defined trait T4 //│ Declared T4.Foo: T4 -> (0 | 1) //│ Defined class C C.Foo //│ res: C -> 1 //│ = undefined :e fun x -> x.Foo //│ ╔══[ERROR] Implicit call to method Foo is forbidden because it is ambiguous. //│ ║ l.192: fun x -> x.Foo //│ ║ ^^^^^ //│ ╟── Unrelated methods named Foo are defined by: //│ ╟── • trait T0 //│ ║ l.8: trait T0 //│ ║ ^^ //│ ╟── • trait T1 //│ ║ l.41: trait T1 //│ ║ ^^ //│ ╟── • trait T3 //│ ║ l.176: trait T3 //│ ║ ^^ //│ ╟── • trait T4 //│ ║ l.178: trait T4 //│ ╙── ^^ //│ res: anything -> error //│ = [Function: res] ================================================ FILE: shared/src/test/diff/codegen/TrickyShadowing.mls ================================================ :js class A: {} A A = 2 //│ Defined class A //│ // Prelude //│ let res; //│ class A {} //│ // Query 1 //│ res = ((x) => new A(x)); //│ // Query 2 //│ globalThis.A1 = 2; //│ // End of generated code //│ res: anything -> A //│ = [Function: res] //│ A: 2 //│ = 2 A //│ res: 2 //│ = 2 :e :js class add //│ ╔══[ERROR] Type names must start with a capital letter //│ ║ l.26: class add //│ ╙── ^^^ //│ // Prelude //│ class add1 {} //│ // End of generated code add //│ res: int -> int -> int //│ = [Function: res] add = 2 //│ add: 2 //│ = 2 add //│ res: 2 //│ = 2 Test = 1 //│ Test: 1 //│ = 1 class Test //│ Defined class Test :js x = Test //│ // Query 1 //│ globalThis.x = function x(x) { //│ return new Test1(x); //│ }; //│ // End of generated code //│ x: anything -> Test //│ = [Function: x] x 1 //│ res: Test //│ = Test1 {} class Test2 //│ Defined class Test2 :js x = Test2 //│ // Query 1 //│ globalThis.x1 = function x1(x) { //│ return new Test2(x); //│ }; //│ // End of generated code //│ x: anything -> Test2 //│ = [Function: x1] :js type B = Test2 //│ Defined type alias B //│ // End of generated code :e :ge :js class C: B //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.91: class C: B //│ ╙── ^^^^ //│ Code generation encountered an error: //│ cannot inherit from type alias B :js B = 1 //│ // Query 1 //│ globalThis.B = 1; //│ // End of generated code //│ B: 1 //│ = 1 :js def f: int // def f = 1 //│ // Query 1 is empty //│ // End of generated code //│ f: int //│ = :js def f = 2 def f = 3 //│ // Query 1 //│ globalThis.f = function f() { //│ return 2; //│ }; //│ // Query 2 //│ globalThis.f1 = function f1() { //│ return 3; //│ }; //│ // End of generated code //│ 2 //│ <: f: //│ int //│ = [Function: f] //│ 3 //│ <: f: //│ int //│ = [Function: f1] f //│ res: int //│ = 3 g = 1 //│ g: 1 //│ = 1 h = g //│ h: 1 //│ = 1 def g: string //│ g: string //│ = :js g = "a" //│ // Query 1 //│ globalThis.g1 = "a"; //│ // End of generated code //│ "a" //│ <: g: //│ string //│ = 'a' def h: int //│ h: int //│ = h //│ res: int //│ = //│ h is not implemented :js i = 1 def i: string i = "a" //│ // Query 1 //│ globalThis.i = 1; //│ // Query 2 is empty //│ // Query 3 //│ globalThis.i1 = "a"; //│ // End of generated code //│ i: 1 //│ = 1 //│ i: string //│ = //│ "a" //│ <: i: //│ string //│ = 'a' i //│ res: string //│ = 'a' class C1 //│ Defined class C1 :js C1 = "a" //│ // Query 1 //│ globalThis.C11 = "a"; //│ // End of generated code //│ C1: "a" //│ = 'a' :js class C2: C1 //│ Defined class C2 //│ // Prelude //│ class C2 extends C1 { //│ constructor(fields) { //│ super(fields); //│ } //│ } //│ // End of generated code type Lol = 1 //│ Defined type alias Lol :e :ge :js Lol //│ ╔══[ERROR] identifier not found: Lol //│ ║ l.226: Lol //│ ╙── ^^^ //│ res: error //│ Code generation encountered an error: //│ type alias Lol is not a valid expression ================================================ FILE: shared/src/test/diff/codegen/TrickyTerms.mls ================================================ // To generate the prelude without printing it in the next test case succ 1 //│ res: int //│ = 2 :js fun ((x, y)) -> x //│ // Query 1 //│ res = (([ //│ x, //│ y //│ ]) => x); //│ // End of generated code //│ res: (('a, anything,),) -> 'a //│ = [Function: res] :js fun {x = 1} -> 0 res { x = 1 } //│ // Query 1 //│ res = (({ x }) => 0); //│ // Query 2 //│ res = res({ x: 1 }); //│ // End of generated code //│ res: {x: 1} -> 0 //│ = [Function: res] //│ res: 0 //│ = 0 :js def f x = case 1 of { 1 -> x } //│ // Query 1 //│ globalThis.f = function f(x) { //│ let a; //│ return (a = 1, a === 1 ? x : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ }; //│ // End of generated code //│ f: 'a -> 'a //│ = [Function: f] // Top-level `def` shadowing: :js def tmp = 1 def tmp = succ tmp //│ // Query 1 //│ globalThis.tmp = function tmp() { //│ return 1; //│ }; //│ // Query 2 //│ globalThis.tmp1 = function tmp1() { //│ return succ(tmp()); //│ }; //│ // End of generated code //│ tmp: 1 //│ = [Function: tmp] //│ tmp: int //│ = [Function: tmp1] // Local shadowing: :js def tmp tmp = tmp tmp (let tmp = 1 in tmp) let t = tmp in let tmp = 1 in t tmp //│ // Query 1 //│ globalThis.tmp2 = function tmp2(tmp) { //│ return tmp; //│ }; //│ // Query 2 //│ res = tmp2(((tmp) => tmp)(1)); //│ // Query 3 //│ res = ((t) => ((tmp) => t(tmp))(1))(tmp2); //│ // End of generated code //│ tmp: 'a -> 'a //│ = [Function: tmp2] //│ res: 1 //│ = 1 //│ res: 1 //│ = 1 ================================================ FILE: shared/src/test/diff/codegen/TrickyWiths.mls ================================================ () with {} //│ res: () //│ = [] :js n = 42 with { x = 1 } n + n.x //│ // Query 1 //│ globalThis.n = withConstruct(42, { x: 1 }); //│ // Query 2 //│ res = n + n.x; //│ // End of generated code //│ n: 42 & {x: 1} //│ = [Number: 42] { x: 1 } //│ res: int //│ = 43 // * Note that `with` works on arrays: :js a = (1,2,3) with {} //│ // Query 1 //│ globalThis.a = withConstruct([ //│ 1, //│ 2, //│ 3 //│ ], {}); //│ // End of generated code //│ a: (1, 2, 3,) //│ = [ 1, 2, 3 ] a : Array[int] //│ res: Array[int] //│ = [ 1, 2, 3 ] a[0] //│ res: 1 | 2 | 3 | undefined //│ = 1 :escape def a: nothing // unsound escape hatch //│ a: nothing //│ = a.length a.map(fun x -> x + 1) //│ res: nothing //│ = 3 //│ res: nothing //│ = [ 2, 3, 4 ] :js ax = (1,2,3) with {x = 4} //│ // Query 1 //│ globalThis.ax = withConstruct([ //│ 1, //│ 2, //│ 3 //│ ], { x: 4 }); //│ // End of generated code //│ ax: (1, 2, 3,) & {x: 4} //│ = [ 1, 2, 3, x: 4 ] ax.x + 1 //│ res: int //│ = 5 ax[0] //│ res: 1 | 2 | 3 | undefined //│ = 1 :escape def ax: nothing // unsound escape hatch //│ ax: nothing //│ = ax.length ax.map(fun x -> x + 1) //│ res: nothing //│ = 3 //│ res: nothing //│ = [ 2, 3, 4 ] // * Note that `with` currently doesn work on functions: f = fun x -> x + 1 g = f with { a = 123 } //│ f: int -> int //│ = [Function: f] //│ g: int -> int & {a: 123} //│ = Function { a: 123 } g.a //│ res: 123 //│ = 123 :re // FIXME g 0 g g.a //│ res: int //│ Runtime error: //│ TypeError: g is not a function //│ res: int //│ Runtime error: //│ TypeError: g is not a function def f x = x with { a = x.a + 1 } //│ f: ({a: int} & 'a) -> ('a\a & {a: int}) //│ = [Function: f1] f { a = 12; b = 34 } //│ res: {a: int, b: 34} //│ = { a: 13, b: 34 } :e f { a = "oops"; b = 34 } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.122: f { a = "oops"; b = 34 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.122: f { a = "oops"; b = 34 } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.113: def f x = x with { a = x.a + 1 } //│ ╙── ^^^ //│ res: error | {a: int, b: 34} //│ = { a: 'oops1', b: 34 } class A //│ Defined class A A{} with { x = 1 } //│ res: A & {x: 1} //│ = A { x: 1 } class A2: { x: string } //│ Defined class A2 a2 = A2{x="a"} r = a2 with { x = 1 } //│ a2: A2 & {x: "a"} //│ = A2 { x: 'a' } //│ r: A2 with {x: 1} //│ = A2 { x: 1 } // * Field removal is not supported in negative positions! // * (When it appears in such positions, it means somethign different.) // * TODO reject such uses this in the typer :e r : (A2 \ x) & {x: 1} //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.158: r : (A2 \ x) & {x: 1} //│ ║ ^ //│ ╟── application of type `A2` does not match type `{x: string}` //│ ║ l.147: a2 = A2{x="a"} //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{x: string}` //│ ║ l.158: r : (A2 \ x) & {x: 1} //│ ║ ^ //│ ╟── Note: constraint arises from record type: //│ ║ l.144: class A2: { x: string } //│ ║ ^^^^^^^^^^^^^ //│ ╟── from intersection type: //│ ║ l.158: r : (A2 \ x) & {x: 1} //│ ╙── ^^^^^^^^^^^^^^^^^ //│ res: A2\x & {x: 1} //│ = A2 { x: 1 } :e // * Field removal in negative position (see above) r2 = r : (A2 \ y) & {x: 1} //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.178: r2 = r : (A2 \ y) & {x: 1} //│ ║ ^ //│ ╟── integer literal of type `1` is not an instance of type `string` //│ ║ l.148: r = a2 with { x = 1 } //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.144: class A2: { x: string } //│ ╙── ^^^^^^ //│ r2: A2\y & {x: 1} //│ = A2 { x: 1 } r2.x //│ res: nothing //│ = 1 test a = case a of A2 -> a.x //│ test: (A2 with {x: 'x}) -> 'x //│ = [Function: test] test a2 //│ res: "a" //│ = 'a' test r //│ res: 1 //│ = 1 test r2 //│ res: nothing //│ = 1 f = (fun x -> x) with {a = 0} //│ f: 'a -> 'a & {a: 0} //│ = Function { a: 0 } f.a //│ res: 0 //│ = 0 :re // `with` is currently broken with functions f(0) //│ res: 0 //│ Runtime error: //│ TypeError: f2 is not a function ================================================ FILE: shared/src/test/diff/codegen/Unicode.mls ================================================ :js "τ → π" //│ // Prelude //│ let res; //│ // Query 1 //│ res = "\u03C4 \u2192 \u03C0"; //│ // End of generated code //│ res: "τ → π" //│ = 'τ → π' :js "🌮" //│ // Query 1 //│ res = "\uD83C\uDF2E"; //│ // End of generated code //│ res: "🌮" //│ = '🌮' ================================================ FILE: shared/src/test/diff/codegen/ValLet.mls ================================================ :NewDefs :js class A(x0: Int) { let x1 = x0 + 1 log([x1]) let x2 = x1 + 1 log([x1, x2]) val x3 = x2 + 1 } //│ class A(x0: Int) { //│ let x1: Int //│ let x2: Int //│ val x3: Int //│ } //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ let res; //│ class TypingUnit { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #x0; //│ #x3; //│ get x3() { return this.#x3; } //│ constructor(x0) { //│ this.#x0 = x0; //│ const x1 = x0 + 1; //│ log([x1]); //│ const x2 = x1 + 1; //│ log([ //│ x1, //│ x2 //│ ]); //│ this.#x3 = x2 + 1; //│ const x3 = this.#x3; //│ } //│ static //│ unapply(x) { //│ return [x.#x0]; //│ } //│ }; //│ this.#A = ((x0) => Object.freeze(new A(x0))); //│ this.#A.class = A; //│ this.#A.unapply = A.unapply; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.A = typing_unit.A; //│ // End of generated code :e A(0).x1 //│ ╔══[ERROR] Let binding 'x1' cannot tbe accessed as a field //│ ║ l.61: A(0).x1 //│ ║ ^^^ //│ ╟── Use a `val` declaration to make it a field //│ ║ l.5: let x1 = x0 + 1 //│ ╙── ^^^^^^^^^^^ //│ Int | error //│ res //│ = undefined //│ // Output //│ [ 1 ] //│ [ 1, 2 ] A(1).x3 //│ Int //│ res //│ = 4 //│ // Output //│ [ 2 ] //│ [ 2, 3 ] :js class AA() { let x = 42 let no = 0 fun f(y: Int) = x + y } AA().f(0) //│ class AA() { //│ fun f: (y: Int) -> Int //│ let no: 0 //│ let x: 42 //│ } //│ Int //│ // Prelude //│ class TypingUnit3 { //│ #AA; //│ constructor() { //│ } //│ get AA() { //│ const qualifier = this; //│ if (this.#AA === undefined) { //│ class AA { //│ #x; //│ constructor() { //│ this.#x = 42; //│ const x = this.#x; //│ const no = 0; //│ } //│ f(y) { //│ const qualifier1 = this; //│ return qualifier1.#x + y; //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ }; //│ this.#AA = (() => Object.freeze(new AA())); //│ this.#AA.class = AA; //│ this.#AA.unapply = AA.unapply; //│ } //│ return this.#AA; //│ } //│ } //│ const typing_unit3 = new TypingUnit3; //│ globalThis.AA = typing_unit3.AA; //│ // Query 1 //│ res = AA().f(0); //│ // End of generated code //│ res //│ = 42 :js class B(x: Int, val y: Int) //│ class B(x: Int, y: Int) //│ // Prelude //│ class TypingUnit4 { //│ #B; //│ constructor() { //│ } //│ get B() { //│ const qualifier = this; //│ if (this.#B === undefined) { //│ class B { //│ #x; //│ #y; //│ get y() { return this.#y; } //│ constructor(x, y) { //│ this.#x = x; //│ this.#y = y; //│ } //│ static //│ unapply(x) { //│ return ([ //│ x.#x, //│ x.#y //│ ]); //│ } //│ }; //│ this.#B = ((x, y) => Object.freeze(new B(x, y))); //│ this.#B.class = B; //│ this.#B.unapply = B.unapply; //│ } //│ return this.#B; //│ } //│ } //│ const typing_unit4 = new TypingUnit4; //│ globalThis.B = typing_unit4.B; //│ // End of generated code :e B(0, 0).x //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.174: B(0, 0).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.136: class B(x: Int, val y: Int) //│ ╙── ^ //│ Int | error //│ res //│ = undefined B(0, 0).y //│ Int //│ res //│ = 0 :e :js class C { constructor(val x: Int, y: Int) } //│ ╔══[ERROR] Cannot use `val` in constructor parameters //│ ║ l.193: constructor(val x: Int, y: Int) //│ ╙── ^ //│ class C { //│ constructor(x: Int, y: Int) //│ } //│ // Prelude //│ class TypingUnit7 { //│ #C; //│ constructor() { //│ } //│ get C() { //│ const qualifier = this; //│ if (this.#C === undefined) { //│ class C { //│ #x; //│ get x() { return this.#x; } //│ constructor(x, y) { //│ this.#x = x; //│ } //│ }; //│ this.#C = C; //│ } //│ return this.#C; //│ } //│ } //│ const typing_unit7 = new TypingUnit7; //│ globalThis.C = typing_unit7.C; //│ // End of generated code // * TODO improve error location :e fun f(val x: Int) = x + 1 //│ ╔══[ERROR] Cannot use `val` in this position //│ ║ l.227: fun f(val x: Int) = x + 1 //│ ╙── ^^^^^^ //│ fun f: (x: Int) -> Int :pe (val x: 1) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.234: (val x: 1) //│ ╙── ^^^^ //│ 1 //│ res //│ = 1 :pe :e (val x: 1) => //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.244: (val x: 1) => //│ ╙── ^ //│ ╔══[ERROR] Cannot use `val` in this position //│ ║ l.244: (val x: 1) => //│ ╙── ^^^^ //│ (x: 1) -> () //│ res //│ = [Function: res] :e (val x: 1) => () //│ ╔══[ERROR] Cannot use `val` in this position //│ ║ l.256: (val x: 1) => () //│ ╙── ^^^^ //│ (x: 1) -> () //│ res //│ = [Function: res] class D(x: Int) { val x = 1 } if D(42) is D(x) then x else 0 //│ class D(x: Int) { //│ val x: 1 //│ } //│ 0 | 1 //│ res //│ = 1 class E(x: Int) { val x = 2 } if E(42) is E(x) then x else 0 //│ class E(x: Int) { //│ val x: 2 //│ } //│ 0 | 2 //│ res //│ = 2 class F(val x: Int) { val x = 3 } F(0).x //│ class F(x: Int) { //│ val x: 3 //│ } //│ 3 //│ res //│ = 3 class G(val x: Int) { val x = 4 } G(1).x //│ class G(x: Int) { //│ val x: 4 //│ } //│ 4 //│ res //│ = 4 ================================================ FILE: shared/src/test/diff/codegen/While.mls ================================================ :NewDefs // * TODO improve code-gen (no immediately-applied lambdas) :js mut let i = 0 while i < 10 do log(i) set i = i + 1 i //│ mut let i: Int //│ Int //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ globalThis.i = 0; //│ // Query 2 //│ res = (() => { //│ while (i < 10) { //│ (() => { //│ log(i); //│ return void(i = i + 1); //│ })() //│ } //│ })(); //│ // Query 3 //│ res = i; //│ // End of generated code //│ i //│ = 0 //│ res //│ = undefined //│ // Output //│ 0 //│ 1 //│ 2 //│ 3 //│ 4 //│ 5 //│ 6 //│ 7 //│ 8 //│ 9 //│ res //│ = 10 i //│ Int //│ res //│ = 10 let foo = if true then (set i = 0), () else () () //│ let foo: () //│ foo //│ = undefined mut let min = 123 let go = if true then set min = 0 else () 1 //│ mut let min: 0 | 123 //│ let go: 1 //│ min //│ = 123 //│ go //│ = 1 ================================================ FILE: shared/src/test/diff/contys/AbstractBounds.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls :e class Test[A, B] method Foo (a: A) = a: B //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.8: method Foo (a: A) = a: B //│ ║ ^ //│ ╟── reference of type `A` does not match type `B` //│ ╟── Note: constraint arises from class type parameter: //│ ║ l.7: class Test[A, B] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.7: class Test[A, B] //│ ╙── ^ //│ Defined class Test[-A, +B] //│ Defined Test.Foo: Test['A, 'B] -> 'A -> 'B fun x -> fun y -> x.Foo y //│ res: Test['A, 'B] -> 'A -> 'B //│ = [Function: res] :GeneralizeCurriedFunctions :e class Test2[A, B] method Foo2 (a: A) = a: B //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.31: method Foo2 (a: A) = a: B //│ ║ ^ //│ ╟── reference of type `A` does not match type `B` //│ ╟── Note: constraint arises from class type parameter: //│ ║ l.30: class Test2[A, B] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.30: class Test2[A, B] //│ ╙── ^ //│ Defined class Test2[-A, +B] //│ Defined Test2.Foo2: Test2['A, 'B] -> 'A -> 'B fun x -> fun y -> x.Foo2 y //│ res: Test2['A, 'B] -> 'A -> 'B //│ = [Function: res] :ConstrainedTypes :e class Test3[A, B] method Foo3 (a: A) = a: B method Bar3 (a: A) = ((fun x -> x) a): B //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.54: method Foo3 (a: A) = a: B //│ ║ ^ //│ ╟── reference of type `A` does not match type `B` //│ ╟── Note: constraint arises from class type parameter: //│ ║ l.53: class Test3[A, B] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.53: class Test3[A, B] //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.55: method Bar3 (a: A) = ((fun x -> x) a): B //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── reference of type `A` does not match type `B` //│ ║ l.55: method Bar3 (a: A) = ((fun x -> x) a): B //│ ║ ^ //│ ╟── but it flows into reference with expected type `B` //│ ║ l.55: method Bar3 (a: A) = ((fun x -> x) a): B //│ ║ ^ //│ ╟── Note: constraint arises from class type parameter: //│ ║ l.53: class Test3[A, B] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.53: class Test3[A, B] //│ ╙── ^ //│ Defined class Test3[-A, +B] //│ Defined Test3.Foo3: Test3['A, 'B] -> 'A -> 'B //│ Defined Test3.Bar3: Test3['A, 'B] -> 'A -> 'B fun x -> fun y -> x.Foo3 y //│ res: 'a -> (forall 'A 'b. 'A -> 'b //│ where //│ 'a <: Test3['A, 'b]) //│ = [Function: res] // * `Baz4` is equivalent to `Foo3`, and yet it is accepted when the other isn't! class Test4[A, B] method Bar4 i (a: A) = i a : B method Baz4 = this.Bar4 id //│ Defined class Test4[=A, =B] //│ Defined Test4.Bar4: Test4['A, 'B] -> (forall 'a. 'a -> (forall 'a. 'A -> 'B //│ where //│ 'a <: 'A -> 'B)) //│ Defined Test4.Baz4: Test4['A, 'B] -> ('A -> 'B //│ where //│ forall 'a. 'a -> 'a <: 'A -> 'B) fun x -> fun y -> x.Bar4 y id //│ res: 'a -> (forall 'A 'B. ('A -> 'B) -> 'B //│ where //│ 'a <: Test4['A, 'B]) //│ where //│ 'A :> forall 'b. 'b -> 'b //│ = [Function: res] fun x -> fun y -> x.Baz4 y //│ res: 'a -> (forall 'A 'B. ('A & 'B) -> 'B //│ where //│ 'a <: Test4['A, 'B]) //│ where //│ 'A <: 'B //│ = [Function: res] ================================================ FILE: shared/src/test/diff/contys/ExplicitConstraints.mls ================================================ :NewParser :NoJS :ConstrainedTypes fun f: int => int //│ f: int -> int :e fun f: int => int where int : string //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.11: fun f: int => int where int : string //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.11: fun f: int => int where int : string //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.11: fun f: int => int where int : string //│ ╙── ^^^^^^ //│ f: int -> int :e fun f: int => (int where int : string) //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.24: fun f: int => (int where int : string) //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.24: fun f: int => (int where int : string) //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.24: fun f: int => (int where int : string) //│ ╙── ^^^^^^ //│ f: int -> int fun f: 'a => ('a where 'a : string) //│ f: (string & 'a) -> 'a :e fun f: 'a => ('a where 'a : string) where int : 'a //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ║ ^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ║ ^^^^^^ //│ ╟── from type variable: //│ ║ l.41: fun f: 'a => ('a where 'a : string) where int : 'a //│ ╙── ^^ //│ f: (string & 'a) -> (int | 'a) :e fun f: 'a => 'a where 'a : string int : 'a //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.60: int : 'a //│ ║ ^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.60: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.59: 'a : string //│ ║ ^^^^^^ //│ ╟── from type variable: //│ ║ l.60: int : 'a //│ ╙── ^^ //│ f: (string & 'a) -> (int | 'a) :e fun f: 'a => forall 'b: 'a where 'a : 'b 'b : string int : 'a //│ ╔══[ERROR] Type mismatch in constraint specifiation: //│ ║ l.80: int : 'a //│ ║ ^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.80: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.79: 'b : string //│ ║ ^^^^^^ //│ ╟── from type variable: //│ ║ l.80: int : 'a //│ ╙── ^^ //│ f: (string & 'a) -> (int | 'a) // * Constraint is stashed! fun f: 'a => forall 'b: 'a where 'a : 'b => 'b int : 'a //│ f: 'a -> (int | 'a //│ where //│ int | 'a <: 'b -> 'b) :ns f //│ res: forall 'a. 'a -> (forall 'b. 'a //│ where //│ 'a <: 'b -> 'b) //│ where //│ 'a :> int // * Note the first-class polymorphic type with impossible bound... let r = f(1) //│ r: int //│ where //│ int <: 'b -> 'b :ns r //│ res: forall 'c 'a. 'c //│ where //│ 'c :> forall 'b. 'a //│ where //│ 'a <: 'b -> 'b //│ 'a :> int :e r(2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.127: r(2) //│ ║ ^^^^ //│ ╟── type `int` is not a function //│ ║ l.99: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.98: 'a : 'b => 'b //│ ╙── ^^^^^^^^ //│ res: error :e r + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.140: r + 1 //│ ║ ^^^ //│ ╟── type `int` is not a function //│ ║ l.99: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.98: 'a : 'b => 'b //│ ╙── ^^^^^^^^ //│ res: error | int fun f: 'a => forall 'b: 'b where 'a : 'b => 'b int : 'a //│ f: 'a -> ('b //│ where //│ int | 'a <: 'b -> 'b) :ns f //│ res: forall 'a. 'a -> (forall 'b. 'b //│ where //│ 'a <: 'b -> 'b) //│ where //│ 'a :> int let r = f(1) //│ r: 'b //│ where //│ int <: 'b -> 'b :e r(2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.174: r(2) //│ ║ ^^^^ //│ ╟── type `int` is not a function //│ ║ l.155: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.154: 'a : 'b => 'b //│ ╙── ^^^^^^^^ //│ res: error :e r + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.187: r + 1 //│ ║ ^^^ //│ ╟── type `int` is not a function //│ ║ l.155: int : 'a //│ ║ ^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.154: 'a : 'b => 'b //│ ╙── ^^^^^^^^ //│ res: error | int ================================================ FILE: shared/src/test/diff/ecoop23/ComparePointPoly.mls ================================================ :NewDefs class Some[out A](val value: A) module None //│ class Some[A](value: A) //│ module None mixin ComparePoint { fun compare(lhs, rhs) = (lhs.x === rhs.x) && (lhs.y === rhs.y) } mixin CompareColored { fun compare(lhs, rhs) = super.compare(lhs, rhs) && (lhs.color === rhs.color) } mixin CompareNested { fun compare(lhs, rhs) = super.compare(lhs, rhs) && if lhs.parent is Some(p) then rhs.parent is Some(q) and this.compare(p, q) else rhs.parent is None } //│ mixin ComparePoint() { //│ fun compare: ({x: Eql['a], y: Eql['b]}, {x: 'a, y: 'b}) -> Bool //│ } //│ mixin CompareColored() { //│ super: {compare: ('c, 'd) -> Bool} //│ fun compare: ({color: Eql['e]} & 'c, {color: 'e} & 'd) -> Bool //│ } //│ mixin CompareNested() { //│ super: {compare: ('f, 'g) -> Bool} //│ this: {compare: ('h, 'i) -> Bool} //│ fun compare: ({parent: Object & ~#Some | Some['h]} & 'f, {parent: Object & ~#Some | Some['i]} & 'g) -> Bool //│ } class MyPoint[out Col](val x: Int, val y: Int, val color: Col, val parent: Some[MyPoint[Col]] | None) //│ class MyPoint[Col](x: Int, y: Int, color: Col, parent: None | Some[MyPoint[Col]]) module CompareMyPoint extends ComparePoint, CompareColored, CompareNested //│ module CompareMyPoint { //│ fun compare: ('a, 'b) -> Bool //│ } //│ where //│ 'b <: {color: 'c, parent: Object & ~#Some | Some['b], x: 'd, y: 'e} //│ 'a <: {color: Eql['c], parent: Object & ~#Some | Some['a], x: Eql['d], y: Eql['e]} let Red = 0 let p0 = MyPoint(0, 0, Red, None) let p1 = MyPoint(0, 1, Red, None) let p2 = MyPoint(0, 1, Red, None) let p3 = MyPoint(0, 1, Red, Some(p1)) let p4 = MyPoint(0, 1, Red, Some(p2)) let p5 = MyPoint(0, 1, Red, Some(p3)) //│ let Red: 0 //│ let p0: MyPoint[0] //│ let p1: MyPoint[0] //│ let p2: MyPoint[0] //│ let p3: MyPoint[0] //│ let p4: MyPoint[0] //│ let p5: MyPoint[0] //│ Red //│ = 0 //│ p0 //│ = MyPoint {} //│ p1 //│ = MyPoint {} //│ p2 //│ = MyPoint {} //│ p3 //│ = MyPoint {} //│ p4 //│ = MyPoint {} //│ p5 //│ = MyPoint {} CompareMyPoint.compare(p0, p1) CompareMyPoint.compare(p1, p2) CompareMyPoint.compare(p3, p4) CompareMyPoint.compare(p3, p5) //│ Bool //│ res //│ = false //│ res //│ = true //│ res //│ = true //│ res //│ = false ================================================ FILE: shared/src/test/diff/ecoop23/ExpressionProblem.mls ================================================ :NewDefs // * Motivating paper example, demonstrating the expression problem solution class Add(lhs: E, rhs: E) class Lit(n: Int) //│ class Add[E](lhs: E, rhs: E) //│ class Lit(n: Int) fun add11 = Add(Lit(1), Lit(2)) //│ fun add11: Add[Lit] fun eval(e) = if e is Lit(n) then n Add(l, r) then eval(l) + eval(r) //│ fun eval: forall 'a. 'a -> Int //│ where //│ 'a <: Add['a] | Lit mixin EvalBase { fun eval(e) = if e is Lit(n) then n: Int Add(l, r) then this.eval(l) + this.eval(r) } //│ mixin EvalBase() { //│ this: {eval: 'a -> Int} //│ fun eval: (Add['a] | Lit) -> Int //│ } module TestLang extends EvalBase //│ module TestLang { //│ fun eval: 'a -> Int //│ } //│ where //│ 'a <: Add['a] | Lit TestLang.eval //│ 'a -> Int //│ where //│ 'a <: Add['a] | Lit //│ res //│ = [Function: eval] TestLang.eval(add11) //│ Int //│ res //│ = 3 mixin EvalNothing { fun eval(e) = e : nothing } mixin EvalAddLit { fun eval(e) = if e is Lit(n) then n: Int Add(l, r) then this.eval(l) + this.eval(r) else super.eval(e) } module TestLang extends EvalNothing, EvalAddLit //│ mixin EvalNothing() { //│ fun eval: nothing -> nothing //│ } //│ mixin EvalAddLit() { //│ super: {eval: 'a -> 'b} //│ this: {eval: 'c -> Int} //│ fun eval: (Add['c] | Lit | Object & 'a & ~#Add & ~#Lit) -> (Int | 'b) //│ } //│ module TestLang { //│ fun eval: 'd -> Int //│ } //│ where //│ 'd <: Add['d] | Lit TestLang.eval //│ 'a -> Int //│ where //│ 'a <: Add['a] | Lit //│ res //│ = [Function: eval] TestLang.eval(add11) //│ Int //│ res //│ = 3 class Neg(expr: A) //│ class Neg[A](expr: A) let add2negadd11 = Add(Lit(2), Neg(add11)) //│ let add2negadd11: Add[Lit | Neg[Add[Lit]]] //│ add2negadd11 //│ = Add {} mixin EvalNeg { fun eval(e) = if e is Neg(d) then 0 - this.eval(d) else super.eval(e) } //│ mixin EvalNeg() { //│ super: {eval: 'a -> 'b} //│ this: {eval: 'c -> Int} //│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) //│ } module TestLang extends EvalBase, EvalNeg //│ module TestLang { //│ fun eval: 'a -> Int //│ } //│ where //│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg TestLang.eval //│ 'a -> Int //│ where //│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg //│ res //│ = [Function: eval] TestLang.eval(add11) //│ Int //│ res //│ = 3 TestLang.eval(Neg(add11)) //│ Int //│ res //│ = -3 TestLang.eval(Add(Lit(2), Neg(Lit(1)))) //│ Int //│ res //│ = 1 TestLang.eval(Neg(Neg(add11))) //│ Int //│ res //│ = 3 TestLang.eval(add2negadd11) //│ Int //│ res //│ = -1 // add11 TestLang.eval(Add(Lit(2), Neg(add11))) //│ Int //│ res //│ = -1 mixin EvalNegNeg_0 { fun eval(e) = if e is Neg(Neg(d)) then this.eval(d) else super.eval(e) // * Note: the above is equivalent to: // if e is Neg(f) then // if f is Neg(d) then this.eval(d) // else super.eval(e) // else super.eval(e) } //│ mixin EvalNegNeg_0() { //│ super: {eval: (Neg[nothing] | 'a) -> 'b} //│ this: {eval: 'c -> 'b} //│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b //│ } // * Concise alternative, usign syntax sugar: mixin EvalNegNeg { fun eval(override Neg(Neg(d))) = this.eval(d) } //│ mixin EvalNegNeg() { //│ super: {eval: (Neg[nothing] | 'a) -> 'b} //│ this: {eval: 'c -> 'b} //│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b //│ } module TestLang extends EvalBase, EvalNeg, EvalNegNeg //│ module TestLang { //│ fun eval: (Neg['A] | Object & 'a & ~#Neg) -> Int //│ } //│ where //│ 'A <: 'b & (Neg['b] | Object & ~#Neg) //│ 'b <: Neg['A] | Object & 'a & ~#Neg //│ 'a <: Add['b] | Lit | Neg['b] fun mk(n) = if n is 0 then Lit(0) 1 then Neg(mk(n)) _ then Add(mk(n), mk(n)) //│ fun mk: forall 'a. Object -> (Lit | 'a) //│ where //│ 'a :> Add[Lit | 'a] | Neg[Lit | 'a] TestLang.eval //│ (Neg['A] | Object & 'a & ~#Neg) -> Int //│ where //│ 'A <: 'b & (Neg['b] | Object & ~#Neg) //│ 'b <: Neg['A] | Object & 'a & ~#Neg //│ 'a <: Add['b] | Lit | Neg['b] //│ res //│ = [Function: eval] TestLang.eval(mk(0)) //│ Int //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/ecoop23/Intro.mls ================================================ :NewDefs // * Examples from paper intro class Some(value: A) module None //│ class Some[A](value: A) //│ module None mixin ComparePoint { fun compare(lhs, rhs) = (lhs.x === rhs.x) && (lhs.y === rhs.y) } //│ mixin ComparePoint() { //│ fun compare: ({x: Eql['a], y: Eql['b]}, {x: 'a, y: 'b}) -> Bool //│ } class Color(val str: Str) { fun equals(that) = str === that.str } //│ class Color(str: Str) { //│ fun equals: {str: anything} -> Bool //│ } let Red = Color("red") //│ let Red: Color //│ Red //│ = Color {} mixin CompareColored { fun compare(lhs, rhs) = super.compare(lhs, rhs) && lhs.color.equals(rhs.color) } //│ mixin CompareColored() { //│ super: {compare: ('a, 'b) -> Bool} //│ fun compare: ({color: {equals: 'color -> Bool}} & 'a, {color: 'color} & 'b) -> Bool //│ } // * Explicit version from paper: // interface Nested[Base] { parent: Option[Base] } // mixin CompareNested[Base, Final] { // super: { compare: (Base, Base) -> Bool } // this: { compare: (Final, Final) -> Bool } // // fun compare(lhs: Base & Nested[Final], rhs: Base & Nested[Final]): Bool = // super.compare(lhs, rhs) && // if lhs.parent is Some(p) // then rhs.parent is Some(q) and this.compare(p, q) // else rhs.parent is None // } // * Implicit version: mixin CompareNested { fun compare(lhs, rhs): Bool = super.compare(lhs, rhs) && if lhs.parent is Some(p) then rhs.parent is Some(q) and this.compare(p, q) else rhs.parent is None } //│ mixin CompareNested() { //│ super: {compare: ('a, 'b) -> Bool} //│ this: {compare: ('c, 'd) -> Bool} //│ fun compare: ({parent: Object & ~#Some | Some['c]} & 'a, {parent: Object & ~#Some | Some['d]} & 'b) -> Bool //│ } // * Alternatively: // mixin CompareNested { // fun compare(lhs, rhs): Bool = // super.compare(lhs, rhs) && // if lhs.parent is // Some(p) then rhs.parent is Some(q) and this.compare(p, q) // None then rhs.parent is None // } class MyPoint(val x: Int, val y: Int, val color: Color, val parent: Some[MyPoint] | None) //│ class MyPoint(x: Int, y: Int, color: Color, parent: None | Some[MyPoint]) module CompareMyPoint extends ComparePoint, CompareColored, CompareNested //│ module CompareMyPoint { //│ fun compare: ('a, 'b) -> Bool //│ } //│ where //│ 'b <: {color: 'color, parent: Object & ~#Some | Some['b], x: 'c, y: 'd} //│ 'a <: { //│ color: {equals: 'color -> Bool}, //│ parent: Object & ~#Some | Some['a], //│ x: Eql['c], //│ y: Eql['d] //│ } let p0 = MyPoint(0, 0, Red, None) let p1 = MyPoint(0, 1, Red, None) let p2 = MyPoint(0, 1, Red, None) let p3 = MyPoint(0, 1, Red, Some(p1)) let p4 = MyPoint(0, 1, Red, Some(p2)) let p5 = MyPoint(0, 1, Red, Some(p3)) //│ let p0: MyPoint //│ let p1: MyPoint //│ let p2: MyPoint //│ let p3: MyPoint //│ let p4: MyPoint //│ let p5: MyPoint //│ p0 //│ = MyPoint {} //│ p1 //│ = MyPoint {} //│ p2 //│ = MyPoint {} //│ p3 //│ = MyPoint {} //│ p4 //│ = MyPoint {} //│ p5 //│ = MyPoint {} CompareMyPoint.compare(p0, p1) //│ Bool //│ res //│ = false CompareMyPoint.compare(p1, p2) //│ Bool //│ res //│ = true CompareMyPoint.compare(p3, p4) //│ Bool //│ res //│ = true CompareMyPoint.compare(p3, p5) //│ Bool //│ res //│ = false ================================================ FILE: shared/src/test/diff/ecoop23/PolymorphicVariants.mls ================================================ :NewDefs // * Adapted example from Code reuse through polymorphic variants (FOSE 2000) class Cons[out A](head: A, tail: Cons[A] | Nil) module Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) //│ module Nil let l = Cons(1, Nil) //│ let l: Cons[1] //│ l //│ = Cons {} class NotFound() class Success[out A](result: A) //│ class NotFound() //│ class Success[A](result: A) fun list_assoc(s, l) = if l is Cons(h, t) then if s === h.0 then Success(h.1) else list_assoc(s, t) Nil then NotFound() //│ fun list_assoc: forall 'a 'A. (Eql['a], Cons[{0: 'a, 1: 'A}] | Nil) -> (NotFound | Success['A]) // fun list_assoc(s: Str, l: Cons[{ _1: Str, _2: 'b }] | Nil): NotFound | Success['b] class Var(s: Str) //│ class Var(s: Str) mixin EvalVar { fun eval(sub, v) = if v is Var(s) then if list_assoc(s, sub) is NotFound then v Success(r) then r } //│ mixin EvalVar() { //│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, Var) -> (Var | 'a) //│ } class Abs[out A](x: Str, t: A) class App[out A](s: A, t: A) //│ class Abs[A](x: Str, t: A) //│ class App[A](s: A, t: A) fun gensym(): Str = "fun" //│ fun gensym: () -> Str fun int_to_string(x: Int): Str = "0" //│ fun int_to_string: (x: Int) -> Str mixin EvalLambda { fun eval(sub, v) = if v is App(t1, t2) then let l1 = this.eval(sub, t1) let l2 = this.eval(sub, t2) if t1 is Abs(x, t) then this.eval(Cons([x, l2], Nil), t) else App(l1, l2) Abs(x, t) then let s = gensym() Abs(s, this.eval(Cons([x, Var(s)], sub), t)) else super.eval(sub, v) } //│ mixin EvalLambda() { //│ super: {eval: ('a, 'b) -> 'c} //│ this: { //│ eval: ('a, 'd) -> 'A & (Cons[[Str, 'A]], 'e) -> 'c & (Cons[[Str, Var] | 'A0], 'f) -> 'A1 //│ } //│ fun eval: ('a & (Cons['A0] | Nil), Abs['f] | App['d & (Abs['e] | Object & ~#Abs)] | Object & 'b & ~#Abs & ~#App) -> (Abs['A1] | App['A] | 'c) //│ } module Test1 extends EvalVar, EvalLambda //│ module Test1 { //│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, 'b) -> 'a //│ } //│ where //│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Var //│ 'a :> Abs['a] | App['a] | Var Test1.eval(Nil, Var("a")) //│ 'a //│ where //│ 'a :> Abs['a] | App['a] | Var //│ res //│ = Var {} Test1.eval(Nil, Abs("b", Var("a"))) //│ 'a //│ where //│ 'a :> Abs['a] | App['a] | Var //│ res //│ = Abs {} Test1.eval(Cons(["c", Var("d")], Nil), App(Abs("b", Var("b")), Var("c"))) //│ 'a //│ where //│ 'a :> Abs['a] | App['a] | Var //│ res //│ = Var {} Test1.eval(Cons(["c", Abs("d", Var("d"))], Nil), App(Abs("b", Var("b")), Var("c"))) //│ Abs['a] | 'a //│ where //│ 'a :> Abs['a] | App['a] | Var //│ res //│ = Abs {} class Numb(n: Int) class Add[out A](l: A, r: A) class Mul[out A](l: A, r: A) //│ class Numb(n: Int) //│ class Add[A](l: A, r: A) //│ class Mul[A](l: A, r: A) fun map_expr(f, v) = if v is Var then v Numb then v Add(l, r) then Add(f(l), f(r)) Mul(l, r) then Mul(f(l), f(r)) //│ fun map_expr: forall 'a 'A 'b 'A0. ('a -> 'A & 'b -> 'A0, Add['b] | Mul['a] | Numb | Var) -> (Add['A0] | Mul['A] | Numb | Var) mixin EvalExpr { fun eval(sub, v) = let eta(e) = this.eval(sub, e) let vv = map_expr(eta, v) if vv is Var then super.eval(sub, vv) Add(Numb(l), Numb(r)) then Numb(l + r) Mul(Numb(l), Numb(r)) then Numb(l * r) else v } //│ mixin EvalExpr() { //│ super: {eval: ('a, Var) -> 'b} //│ this: {eval: ('a, 'c) -> Object} //│ fun eval: ('a, 'b & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | 'b) //│ } module Test2 extends EvalVar, EvalExpr //│ module Test2 { //│ fun eval: forall 'a. (Cons[{0: anything, 1: Object & 'b}] | Nil, 'a & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | Var | 'b | 'c | 'a) //│ } //│ where //│ 'c <: Add['c] | Mul['c] | Numb | Var Test2.eval(Nil, Var("a")) //│ Numb | Var //│ res //│ = Var {} Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Var("a")) //│ Abs[Var] | Numb | Var //│ res //│ = Var {} Test2.eval(Cons(["a", Numb(1)], Nil), Var("a")) //│ Numb | Var //│ res //│ = Numb {} // * This expected error shows that Test2 does not handle Abs expression inputs :e Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.172: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Abs[?A]` does not match type `Add[?A0] | Mul[?A1] | Numb | Var` //│ ║ l.172: Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.125: if v is //│ ║ ^ //│ ╟── from reference: //│ ║ l.135: let vv = map_expr(eta, v) //│ ╙── ^ //│ Abs[Var] | Numb | Var | error //│ res //│ Runtime error: //│ Error: non-exhaustive case expression Test2.eval(Cons(["a", Abs("d", Var("d"))], Nil), Add(Numb(1), Var("a"))) //│ Abs[Var] | Add[Numb | Var] | Numb | Var //│ res //│ = Add {} module Test3 extends EvalVar, EvalExpr, EvalLambda //│ module Test3 { //│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, 'b) -> (Abs['c] | App['c] | 'c) //│ } //│ where //│ 'a :> 'c //│ <: Object //│ 'c :> 'a | 'd //│ 'd <: Add['b] | Mul['b] | Numb | Var //│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Object & 'd & ~#Abs & ~#App Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ Abs['a] | 'a //│ where //│ 'a :> Abs['a] | App['a] | Numb | Var //│ res //│ = Abs {} Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), App(Abs("a", Var("a")), Add(Numb(1), Var("c")))) //│ Abs['a] | 'a //│ where //│ 'a :> Abs['a] | Add[Numb | Var] | App['a] | Numb | Var //│ res //│ = Add {} // * Incorrect version, for regression testing – EvalLambda should be mixed in after EvalExpr module Test3 extends EvalVar, EvalLambda, EvalExpr //│ module Test3 { //│ fun eval: (Cons[{0: anything, 1: 'a}] | Nil, 'a & (Add['b] | Mul['b] | Numb | Var)) -> (Numb | 'c | 'a | 'b) //│ } //│ where //│ 'a :> 'b | 'c //│ <: Object //│ 'c :> Abs['c | 'a] | App['c | 'a] | 'a //│ 'b <: Add['b] | Mul['b] | Numb | Var // * Because EvalExpr does not dispatch lambdas to super and map_expr only // * handles exprs :e Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.234: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Abs[?A]` does not match type `Add[?A0] | Mul[?A1] | Numb | Var` //│ ║ l.234: Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil), Abs("a", Var("a"))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.125: if v is //│ ║ ^ //│ ╟── from reference: //│ ║ l.135: let vv = map_expr(eta, v) //│ ╙── ^ //│ Abs[Var] | error | 'a //│ where //│ 'a :> Abs['a] | App['a] | Numb | Var //│ res //│ Runtime error: //│ Error: non-exhaustive case expression ================================================ FILE: shared/src/test/diff/ecoop23/SimpleRegionDSL_annot.mls ================================================ :NewDefs // * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) // ******************* Initial System ******************* class Vector(val x: Int, val y: Int) //│ class Vector(x: Int, y: Int) class Circle(radius: Int) class Outside[out Region](a: Region) class Union[out Region](a: Region, b: Region) class Intersect[out Region](a: Region, b: Region) class Translate[out Region](v: Vector, a: Region) //│ class Circle(radius: Int) //│ class Outside[Region](a: Region) //│ class Union[Region](a: Region, b: Region) //│ class Intersect[Region](a: Region, b: Region) //│ class Translate[Region](v: Vector, a: Region) type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] //│ type BaseLang[T] = Circle | Intersect[T] | Outside[T] | Translate[T] | Union[T] mixin SizeBase { fun size(r) = if r is Circle(_) then 1 Outside(a) then this.size(a) + 1 Union(a, b) then this.size(a) + this.size(b) + 1 Intersect(a, b) then this.size(a) + this.size(b) + 1 Translate(_, a) then this.size(a) + 1 } //│ mixin SizeBase() { //│ this: {size: 'a -> Int} //│ fun size: (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) -> Int //│ } // ******************* Linguistic Reuse and Meta-Language Optimizations ******************* fun round(n: Num): Int = 0 //│ fun round: (n: Num) -> Int fun go(x, offset) = if x is 0 then Circle(1) else let shared = go(x - 1, round(offset / 2)) Union(Translate(Vector(0 - offset, 0), shared), Translate(Vector(offset, 0), shared)) //│ fun go: forall 'a. (0 | Int & ~0, Int) -> (Circle | 'a) //│ where //│ 'a :> Union[Translate[Circle | 'a]] // * Note that first-class polymorphism manages (correctly) to preserve the universal quantification let circles = go(2, 1024) //│ let circles: forall 'a. Circle | 'a //│ where //│ 'a :> Union[Translate[Circle | 'a]] //│ circles //│ = Union {} // ******************* Adding More Language Constructs ******************* class Univ() class Empty() class Scale[out Region](v: Vector, a: Region) //│ class Univ() //│ class Empty() //│ class Scale[Region](v: Vector, a: Region) type ExtLang[T] = Univ | Empty | Scale[T] //│ type ExtLang[T] = Empty | Scale[T] | Univ mixin SizeExt { fun size(a) = if a is Univ then 1 Empty then 1 Scale(_, b) then this.size(b) + 1 else super.size(a) } //│ mixin SizeExt() { //│ super: {size: 'a -> 'b} //│ this: {size: 'c -> Int} //│ fun size: (Empty | Object & 'a & ~#Empty & ~#Scale & ~#Univ | Scale['c] | Univ) -> (Int | 'b) //│ } type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] //│ type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] module TestSize extends SizeBase, SizeExt { fun size: RegionLang -> Int } //│ module TestSize { //│ fun size: RegionLang -> Int //│ } TestSize.size(Empty()) //│ Int //│ res //│ = 1 TestSize.size(circles) //│ Int //│ res //│ = 13 TestSize.size(Scale(Vector(1, 1), circles)) //│ Int //│ res //│ = 14 // ******************* Adding a New Interpretation ******************* // a stupid power (Int ** Int) implementation fun pow(x, a) = if a is 0 then 1 else x * pow(x, a - 1) //│ fun pow: (Int, 0 | Int & ~0) -> Int mixin Contains { fun contains(a, p) = if a is Circle(r) then pow(p.x, 2) + pow(p.y, 2) <= pow(r, 2) Outside(a) then not (this.contains(a, p)) Union(lhs, rhs) then this.contains(lhs, p) || this.contains(rhs, p) Intersect(lhs, rhs) then this.contains(lhs, p) && this.contains(rhs, p) Translate(v, a) then this.contains(a, Vector(p.x - v.x, p.y - v.y)) } //│ mixin Contains() { //│ this: {contains: ('a, 'b) -> Bool & ('c, Vector) -> 'd} //│ fun contains: (Circle | Intersect['a] | Outside['a] | Translate['c] | Union['a], {x: Int, y: Int} & 'b) -> (Bool | 'd) //│ } type BaseRegionLang = BaseLang[BaseRegionLang] //│ type BaseRegionLang = BaseLang[BaseRegionLang] module TestContains extends Contains { fun contains: (BaseRegionLang, Vector) -> Bool } //│ module TestContains { //│ fun contains: (BaseRegionLang, Vector) -> Bool //│ } TestContains.contains(Translate(Vector(0, 0), Circle(1)), Vector(0, 0)) //│ Bool //│ res //│ = true TestContains.contains(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1)), Vector(0, 0)) //│ Bool //│ res //│ = true TestContains.contains(circles, Vector(0, 0)) //│ Bool //│ res //│ = false // ******************* Dependencies, Complex Interpretations, and Domain-Specific Optimizations ******************* fun toString(a: Int): Str = "foo" fun concat(a: Str, b: Str): Str = a //│ fun toString: (a: Int) -> Str //│ fun concat: (a: Str, b: Str) -> Str mixin Text { fun text(e) = if e is Circle(r) then concat("a circular region of radius ", toString(r)) Outside(a) then concat("outside a region of size ", toString(this.size(a))) Union then concat("the union of two regions of size ", toString(this.size(e))) Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) Translate then concat("a translated region of size ", toString(this.size(e))) } //│ mixin Text() { //│ this: {size: (Intersect['Region] | Translate[nothing] | Union[nothing] | 'a) -> Int} //│ fun text: (Circle | Intersect['Region] | Outside['a] | Translate[anything] | Union[anything]) -> Str //│ } :e module SizeText extends Text //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.172: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.170: Outside(a) then concat("outside a region of size ", toString(this.size(a))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.173: Translate then concat("a translated region of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.171: Union then concat("the union of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ module SizeText { //│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str //│ } // * Note: this inferred type got *much worse* after this commit (field access type refinement) module SizeText extends SizeBase, Text { fun size: BaseRegionLang -> Int fun text: BaseRegionLang -> Str } //│ module SizeText { //│ fun size: BaseRegionLang -> Int //│ fun text: BaseRegionLang -> Str //│ } SizeText.text(circles) //│ Str //│ res //│ = 'the union of two regions of size ' SizeText.size(circles) //│ Int //│ res //│ = 13 SizeText.text(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) //│ Str //│ res //│ = 'the intersection of two regions of size ' SizeText.size(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) //│ Int //│ res //│ = 4 mixin IsUniv { fun isUniv(e) = if e is Univ then true Outside(a) then this.isEmpty(a) Union(a, b) then this.isUniv(a) || this.isUniv(b) Intersect(a, b) then this.isUniv(a) && this.isUniv(b) Translate(_, a) then this.isUniv(a) Scale(_, a) then this.isUniv(a) else false } //│ mixin IsUniv() { //│ this: {isEmpty: 'a -> 'b, isUniv: 'c -> Bool & 'd -> 'b} //│ fun isUniv: (Intersect['c] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['d] | Translate['d] | Union['c] | Univ) -> (Bool | 'b) //│ } mixin IsEmpty { fun isEmpty(e) = if e is Univ then true Outside(a) then this.isUniv(a) Union(a, b) then this.isEmpty(a) || this.isEmpty(b) Intersect(a, b) then this.isEmpty(a) && this.isEmpty(b) Translate(_, a) then this.isEmpty(a) Scale(_, a) then this.isEmpty(a) else false } //│ mixin IsEmpty() { //│ this: {isEmpty: 'a -> Bool & 'b -> 'c, isUniv: 'd -> 'c} //│ fun isEmpty: (Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['d] | Scale['b] | Translate['b] | Union['a] | Univ) -> (Bool | 'c) //│ } module IsUnivIsEmpty extends IsUniv, IsEmpty { fun isEmpty: RegionLang -> Bool fun isUniv: RegionLang -> Bool } //│ module IsUnivIsEmpty { //│ fun isEmpty: RegionLang -> Bool //│ fun isUniv: RegionLang -> Bool //│ } module IsUnivIsEmpty extends IsEmpty, IsUniv { fun isEmpty: RegionLang -> Bool fun isUniv: RegionLang -> Bool } //│ module IsUnivIsEmpty { //│ fun isEmpty: RegionLang -> Bool //│ fun isUniv: RegionLang -> Bool //│ } IsUnivIsEmpty.isUniv(circles) //│ Bool //│ res //│ = false IsUnivIsEmpty.isEmpty(circles) //│ Bool //│ res //│ = false :e // Expected since the annotation only allows Lang variants class Foo() IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.290: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Foo` does not match type `BaseLang[RegionLang] | ExtLang[RegionLang]` //│ ║ l.290: IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.88: type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.88: type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] //│ ╙── ^^^^^^^^^^ //│ class Foo() //│ error | false | true //│ res //│ = false mixin Eliminate { fun eliminate(e) = if e is Outside(Outside(a)) then this.eliminate(a) Outside(a) then Outside(this.eliminate(a)) Union(a, b) then Union(this.eliminate(a), this.eliminate(b)) Intersect(a, b) then Intersect(this.eliminate(a), this.eliminate(b)) Translate(v, a) then Translate(v, this.eliminate(a)) Scale(v, a) then Scale(v, this.eliminate(a)) else e } //│ mixin Eliminate() { //│ this: { //│ eliminate: 'a -> 'b & 'c -> 'Region & 'd -> 'Region0 & 'e -> 'Region1 & 'f -> 'Region2 & 'g -> 'Region3 //│ } //│ fun eliminate: (Intersect['g] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['c & (Object & ~#Outside | Outside['a])] | Scale['f] | Translate['e] | Union['d]) -> (Intersect['Region3] | Outside['Region] | Scale['Region2] | Translate['Region1] | Union['Region0] | 'b) //│ } module TestElim extends Eliminate { fun eliminate: RegionLang -> RegionLang } //│ module TestElim { //│ fun eliminate: RegionLang -> RegionLang //│ } TestElim.eliminate(Outside(Outside(Univ()))) //│ RegionLang //│ res //│ = Univ {} TestElim.eliminate(circles) //│ RegionLang //│ res //│ = Union {} fun mk(n) = if n is 1 then Outside(mk(n)) 2 then Union(mk(n), mk(n)) 3 then Intersect(mk(n), mk(n)) 4 then Translate(Vector(0, 0), mk(n)) _ then Scale(Vector(0, 0), mk(n)) //│ fun mk: forall 'a. Object -> 'a //│ where //│ 'a :> Intersect['a] | Outside['a] | Scale['a] | Translate['a] | Union['a] :re TestElim.eliminate(mk(100)) //│ RegionLang //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // ************************************************************************* module Lang extends SizeBase, SizeExt, Contains, Text, IsUniv, IsEmpty, Eliminate { fun contains: (BaseRegionLang, Vector) -> Bool fun eliminate: RegionLang -> RegionLang fun isEmpty: RegionLang -> Bool fun isUniv: RegionLang -> Bool fun size: RegionLang -> Int fun text: BaseRegionLang -> Str } //│ module Lang { //│ fun contains: (BaseRegionLang, Vector) -> Bool //│ fun eliminate: RegionLang -> RegionLang //│ fun isEmpty: RegionLang -> Bool //│ fun isUniv: RegionLang -> Bool //│ fun size: RegionLang -> Int //│ fun text: BaseRegionLang -> Str //│ } Lang.size(circles) //│ Int //│ res //│ = 13 Lang.contains(circles, Vector(0, 0)) //│ Bool //│ res //│ = false Lang.text(circles) //│ Str //│ res //│ = 'the union of two regions of size ' Lang.isUniv(circles) //│ Bool //│ res //│ = false Lang.isEmpty(circles) //│ Bool //│ res //│ = false Lang.size(Lang.eliminate(circles)) //│ Int //│ res //│ = 13 :re Lang.size(mk(100)) //│ Int //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re Lang.contains(mk(100), Vector(0, 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.418: Lang.contains(mk(100), Vector(0, 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[BaseRegionLang] | Outside[BaseRegionLang] | Translate[BaseRegionLang] | Union[BaseRegionLang]` //│ ║ l.348: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.23: type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.134: type BaseRegionLang = BaseLang[BaseRegionLang] //│ ╙── ^^^^^^^^^^^^^^ //│ error | false | true //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re Lang.text(mk(100)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.438: Lang.text(mk(100)) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[BaseRegionLang] | Outside[BaseRegionLang] | Translate[BaseRegionLang] | Union[BaseRegionLang]` //│ ║ l.348: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.23: type BaseLang[T] = Circle | Intersect[T] | Union[T] | Outside[T] | Translate[T] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.134: type BaseRegionLang = BaseLang[BaseRegionLang] //│ ╙── ^^^^^^^^^^^^^^ //│ Str | error //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re Lang.isUniv(mk(100)) //│ Bool //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re Lang.isEmpty(mk(100)) //│ Bool //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re Lang.size(Lang.eliminate(mk(100))) //│ Int //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/ecoop23/SimpleRegionDSL_raw.mls ================================================ :NewDefs // * Adapted example from Compositional Embeddings of Domain-Specific Languages (OOPSLA 2022) // ******************* Initial System ******************* class Vector(val x: Int, val y: Int) //│ class Vector(x: Int, y: Int) class Circle(radius: Int) class Outside[out Region](a: Region) class Union[out Region](a: Region, b: Region) class Intersect[out Region](a: Region, b: Region) class Translate[out Region](v: Vector, a: Region) //│ class Circle(radius: Int) //│ class Outside[Region](a: Region) //│ class Union[Region](a: Region, b: Region) //│ class Intersect[Region](a: Region, b: Region) //│ class Translate[Region](v: Vector, a: Region) mixin SizeBase { fun size(r) = if r is Circle(_) then 1 Outside(a) then this.size(a) + 1 Union(a, b) then this.size(a) + this.size(b) + 1 Intersect(a, b) then this.size(a) + this.size(b) + 1 Translate(_, a) then this.size(a) + 1 } //│ mixin SizeBase() { //│ this: {size: 'a -> Int} //│ fun size: (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) -> Int //│ } // ******************* Linguistic Reuse and Meta-Language Optimizations ******************* fun round(n: Num): Int = 0 //│ fun round: (n: Num) -> Int fun go(x, offset) = if x is 0 then Circle(1) else let shared = go(x - 1, round(offset / 2)) Union(Translate(Vector(0 - offset, 0), shared), Translate(Vector(offset, 0), shared)) //│ fun go: forall 'a. (0 | Int & ~0, Int) -> (Circle | 'a) //│ where //│ 'a :> Union[Translate[Circle | 'a]] // * Note that first-class polymorphism manages (correctly) to preserve the universal quantification let circles = go(2, 1024) //│ let circles: forall 'a. Circle | 'a //│ where //│ 'a :> Union[Translate[Circle | 'a]] //│ circles //│ = Union {} // ******************* Adding More Language Constructs ******************* class Univ() class Empty() class Scale[out Region](v: Vector, a: Region) //│ class Univ() //│ class Empty() //│ class Scale[Region](v: Vector, a: Region) mixin SizeExt { fun size(a) = if a is Univ then 1 Empty then 1 Scale(_, b) then this.size(b) + 1 else super.size(a) } //│ mixin SizeExt() { //│ super: {size: 'a -> 'b} //│ this: {size: 'c -> Int} //│ fun size: (Empty | Object & 'a & ~#Empty & ~#Scale & ~#Univ | Scale['c] | Univ) -> (Int | 'b) //│ } module TestSize extends SizeBase, SizeExt //│ module TestSize { //│ fun size: 'a -> Int //│ } //│ where //│ 'a <: Empty | Object & (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) & ~#Empty & ~#Scale & ~#Univ | Scale['a] | Univ TestSize.size(Empty()) //│ Int //│ res //│ = 1 TestSize.size(circles) //│ Int //│ res //│ = 13 TestSize.size(Scale(Vector(1, 1), circles)) //│ Int //│ res //│ = 14 // ******************* Adding a New Interpretation ******************* // a stupid power (Int ** Int) implementation fun pow(x, a) = if a is 0 then 1 else x * pow(x, a - 1) //│ fun pow: (Int, 0 | Int & ~0) -> Int mixin Contains { fun contains(a, p) = if a is Circle(r) then pow(p.x, 2) + pow(p.y, 2) <= pow(r, 2) Outside(a) then not (this.contains(a, p)) Union(lhs, rhs) then this.contains(lhs, p) || this.contains(rhs, p) Intersect(lhs, rhs) then this.contains(lhs, p) && this.contains(rhs, p) Translate(v, a) then this.contains(a, Vector(p.x - v.x, p.y - v.y)) } //│ mixin Contains() { //│ this: {contains: ('a, 'b) -> Bool & ('c, Vector) -> 'd} //│ fun contains: (Circle | Intersect['a] | Outside['a] | Translate['c] | Union['a], {x: Int, y: Int} & 'b) -> (Bool | 'd) //│ } module TestContains extends Contains //│ module TestContains { //│ fun contains: ('a, {x: Int, y: Int}) -> Bool //│ } //│ where //│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] TestContains.contains(Translate(Vector(0, 0), Circle(1)), Vector(0, 0)) //│ Bool //│ res //│ = true TestContains.contains(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1)), Vector(0, 0)) //│ Bool //│ res //│ = true TestContains.contains(circles, Vector(0, 0)) //│ Bool //│ res //│ = false // ******************* Dependencies, Complex Interpretations, and Domain-Specific Optimizations ******************* fun toString(a: Int): Str = "foo" fun concat(a: Str, b: Str): Str = a //│ fun toString: (a: Int) -> Str //│ fun concat: (a: Str, b: Str) -> Str mixin Text { fun text(e) = if e is Circle(r) then concat("a circular region of radius ", toString(r)) Outside(a) then concat("outside a region of size ", toString(this.size(a))) Union then concat("the union of two regions of size ", toString(this.size(e))) Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) Translate then concat("a translated region of size ", toString(this.size(e))) } //│ mixin Text() { //│ this: {size: (Intersect['Region] | Translate[nothing] | Union[nothing] | 'a) -> Int} //│ fun text: (Circle | Intersect['Region] | Outside['a] | Translate[anything] | Union[anything]) -> Str //│ } :e module SizeText extends Text //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.160: Intersect then concat("the intersection of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.158: Outside(a) then concat("outside a region of size ", toString(this.size(a))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.161: Translate then concat("a translated region of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#SizeText & {text: ?a -> (?b | ?c | ?d | ?e | ?f)}` does not contain member `size` //│ ║ l.159: Union then concat("the union of two regions of size ", toString(this.size(e))) //│ ╙── ^^^^^ //│ module SizeText { //│ fun text: (Circle | Intersect[anything] | Outside[anything] | Translate[anything] | Union[anything]) -> Str //│ } // * Note: this inferred type got *much worse* after this commit (field access type refinement) module SizeText extends SizeBase, Text //│ module SizeText { //│ fun size: (Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a]) -> Int //│ fun text: 'a -> Str //│ } //│ where //│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] SizeText.text(circles) //│ Str //│ res //│ = 'the union of two regions of size ' SizeText.size(circles) //│ Int //│ res //│ = 13 SizeText.text(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) //│ Str //│ res //│ = 'the intersection of two regions of size ' SizeText.size(Intersect(Translate(Vector(0, 0), Circle(1)), Circle(1))) //│ Int //│ res //│ = 4 mixin IsUniv { fun isUniv(e) = if e is Univ then true Outside(a) then this.isEmpty(a) Union(a, b) then this.isUniv(a) || this.isUniv(b) Intersect(a, b) then this.isUniv(a) && this.isUniv(b) Translate(_, a) then this.isUniv(a) Scale(_, a) then this.isUniv(a) else false } //│ mixin IsUniv() { //│ this: {isEmpty: 'a -> 'b, isUniv: 'c -> Bool & 'd -> 'b} //│ fun isUniv: (Intersect['c] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['d] | Translate['d] | Union['c] | Univ) -> (Bool | 'b) //│ } mixin IsEmpty { fun isEmpty(e) = if e is Univ then true Outside(a) then this.isUniv(a) Union(a, b) then this.isEmpty(a) || this.isEmpty(b) Intersect(a, b) then this.isEmpty(a) && this.isEmpty(b) Translate(_, a) then this.isEmpty(a) Scale(_, a) then this.isEmpty(a) else false } //│ mixin IsEmpty() { //│ this: {isEmpty: 'a -> Bool & 'b -> 'c, isUniv: 'd -> 'c} //│ fun isEmpty: (Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['d] | Scale['b] | Translate['b] | Union['a] | Univ) -> (Bool | 'c) //│ } module IsUnivIsEmpty extends IsUniv, IsEmpty //│ module IsUnivIsEmpty { //│ fun isEmpty: 'a -> Bool //│ fun isUniv: 'b -> Bool //│ } //│ where //│ 'b <: Intersect['b] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['b] | Scale['b] | Translate['b] | Union['b] | Univ //│ 'a <: Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['a] | Translate['a] | Union['a] | Univ module IsUnivIsEmpty extends IsEmpty, IsUniv //│ module IsUnivIsEmpty { //│ fun isEmpty: 'a -> Bool //│ fun isUniv: 'b -> Bool //│ } //│ where //│ 'b <: Intersect['b] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['b] | Scale['b] | Translate['b] | Union['b] | Univ //│ 'a <: Intersect['a] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['a] | Scale['a] | Translate['a] | Union['a] | Univ IsUnivIsEmpty.isUniv(circles) //│ Bool //│ res //│ = false IsUnivIsEmpty.isEmpty(circles) //│ Bool //│ res //│ = false class Foo() IsUnivIsEmpty.isEmpty(Scale(Vector(1, 2), Intersect(Foo(), circles))) //│ class Foo() //│ Bool //│ res //│ = false mixin Eliminate { fun eliminate(e) = if e is Outside(Outside(a)) then this.eliminate(a) Outside(a) then Outside(this.eliminate(a)) Union(a, b) then Union(this.eliminate(a), this.eliminate(b)) Intersect(a, b) then Intersect(this.eliminate(a), this.eliminate(b)) Translate(v, a) then Translate(v, this.eliminate(a)) Scale(v, a) then Scale(v, this.eliminate(a)) else e } //│ mixin Eliminate() { //│ this: { //│ eliminate: 'a -> 'b & 'c -> 'Region & 'd -> 'Region0 & 'e -> 'Region1 & 'f -> 'Region2 & 'g -> 'Region3 //│ } //│ fun eliminate: (Intersect['g] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['c & (Object & ~#Outside | Outside['a])] | Scale['f] | Translate['e] | Union['d]) -> (Intersect['Region3] | Outside['Region] | Scale['Region2] | Translate['Region1] | Union['Region0] | 'b) //│ } module TestElim extends Eliminate //│ module TestElim { //│ fun eliminate: 'a -> 'b //│ } //│ where //│ 'a <: Intersect['a] | Object & 'b & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['a & (Object & ~#Outside | Outside['a])] | Scale['a] | Translate['a] | Union['a] //│ 'b :> Intersect['b] | Outside['b] | Scale['b] | Translate['b] | Union['b] TestElim.eliminate(Outside(Outside(Univ()))) //│ 'a //│ where //│ 'a :> Intersect['a] | Outside[Univ | 'a] | Scale[Univ | 'a] | Translate[Univ | 'a] | Union[Univ | 'a] | Univ //│ res //│ = Univ {} TestElim.eliminate(circles) //│ 'a //│ where //│ 'a :> Circle | Intersect['a] | Outside[Circle | 'a] | Scale[Circle | 'a] | Translate[Circle | 'a] | Union[Circle | 'a] //│ res //│ = Union {} fun mk(n) = if n is 1 then Outside(mk(n)) 2 then Union(mk(n), mk(n)) 3 then Intersect(mk(n), mk(n)) 4 then Translate(Vector(0, 0), mk(n)) _ then Scale(Vector(0, 0), mk(n)) //│ fun mk: forall 'a. Object -> 'a //│ where //│ 'a :> Intersect['a] | Outside['a] | Scale['a] | Translate['a] | Union['a] :re TestElim.eliminate(mk(100)) //│ 'a //│ where //│ 'a :> Intersect['a] | Outside['a] | Scale['a] | Translate['a] | Union['a] //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // ************************************************************************* module Lang extends SizeBase, SizeExt, Contains, Text, IsUniv, IsEmpty, Eliminate //│ module Lang { //│ fun contains: ('a, {x: Int, y: Int}) -> Bool //│ fun eliminate: 'b -> 'c //│ fun isEmpty: 'd -> Bool //│ fun isUniv: 'e -> Bool //│ fun size: 'f -> Int //│ fun text: (Circle | Intersect['g] | Outside['g] | Translate['g] | Union['g]) -> Str //│ } //│ where //│ 'g <: Empty | Object & (Circle | Intersect['g] | Outside['g] | Translate['g] | Union['g]) & ~#Empty & ~#Scale & ~#Univ | Scale['g] | Univ //│ 'f <: Empty | Object & (Circle | Intersect['f] | Outside['f] | Translate['f] | Union['f]) & ~#Empty & ~#Scale & ~#Univ | Scale['f] | Univ //│ 'e <: Intersect['e] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['e] | Scale['e] | Translate['e] | Union['e] | Univ //│ 'd <: Intersect['d] | Object & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union & ~#Univ | Outside['d] | Scale['d] | Translate['d] | Union['d] | Univ //│ 'b <: Intersect['b] | Object & 'c & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union | Outside['b & (Object & ~#Outside | Outside['b])] | Scale['b] | Translate['b] | Union['b] //│ 'c :> Intersect['c] | Outside['c] | Scale['c] | Translate['c] | Union['c] //│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] Lang.size(circles) //│ Int //│ res //│ = 13 Lang.contains(circles, Vector(0, 0)) //│ Bool //│ res //│ = false Lang.text(circles) //│ Str //│ res //│ = 'the union of two regions of size ' Lang.isUniv(circles) //│ Bool //│ res //│ = false Lang.isEmpty(circles) //│ Bool //│ res //│ = false Lang.size(Lang.eliminate(circles)) //│ Int //│ res //│ = 13 :re Lang.size(mk(100)) //│ Int //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re Lang.contains(mk(100), Vector(0, 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.400: Lang.contains(mk(100), Vector(0, 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[?Region0] | Outside[?Region1] | Translate[?Region2] | Union[?Region3]` //│ ║ l.327: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.113: if a is //│ ║ ^ //│ ╟── from field selection: //│ ║ l.13: class Outside[out Region](a: Region) //│ ║ ^ //│ ╟── Note: type parameter Region is defined at: //│ ║ l.13: class Outside[out Region](a: Region) //│ ╙── ^^^^^^ //│ error | false | true //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re Lang.text(mk(100)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.423: Lang.text(mk(100)) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Scale[?Region]` does not match type `Circle | Intersect[?Region0] | Outside[?Region1] | Translate[?Region2] | Union[?Region3]` //│ ║ l.327: _ then Scale(Vector(0, 0), mk(n)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `Circle | Intersect[?Region4] | Outside[?Region5] | Translate[?Region6] | Union[?Region7]` //│ ║ l.423: Lang.text(mk(100)) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.156: if e is //│ ╙── ^ //│ Str | error //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re Lang.isUniv(mk(100)) //│ Bool //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re Lang.isEmpty(mk(100)) //│ Bool //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re Lang.size(Lang.eliminate(mk(100))) //│ Int //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/ex/RepMin.mls ================================================ :NewDefs // * https://web.cecs.pdx.edu/~sheard/course/CS457-557/Notes/Laziness-6up.pdf // * Example Haskell implementation: https://gist.github.com/23Skidoo/3869595 class defined[out A](val value: A) type Option[A] = defined[A] | () //│ class defined[A](value: A) //│ type Option[A] = defined[A] | () // * TODO should check the variance; making it covariant should require using private state class Lazy[out A](init: () => A) { mut val cached: Option[A] = () fun get = if cached is defined(v) then v else let v = init() set cached = defined(v) v fun toString() = join of "Lazy(", (if cached is defined(v) then String(v) else "..."), ")" } //│ class Lazy[A](init: () -> A) { //│ mut val cached: Option[A] //│ fun get: A //│ fun toString: () -> Str //│ } let l = Lazy(() => (log("Hello!"), 42)) //│ let l: Lazy[42] //│ l //│ = Lazy {} String of l //│ Str //│ res //│ = 'Lazy(...)' l.get //│ 42 //│ res //│ = 42 //│ // Output //│ Hello! String of l //│ Str //│ res //│ = 'Lazy(42)' l.get //│ 42 //│ res //│ = 42 type Tree[out A] = Leaf[A] | Node[A] class Leaf[out A](val value: A) { fun toString() = join of "Leaf(", String(value), ")" } class Node[out A](val left: Tree[A], val right: Tree[A]) { fun toString() = join of "Node(", String(left), ", ", String(right), ")" } //│ type Tree[A] = Leaf[A] | Node[A] //│ class Leaf[A](value: A) { //│ fun toString: () -> Str //│ } //│ class Node[A](left: Tree[A], right: Tree[A]) { //│ fun toString: () -> Str //│ } String of Leaf(12) //│ Str //│ res //│ = 'Leaf(12)' fun min(x, y) = if x < y then x else y //│ fun min: forall 'a. (Num & 'a, Num & 'a) -> 'a // * An eager walk function just for fun fun walk(t, mn) = if t is Leaf(n) then [n, Leaf(if n > 2 * mn then mn else n)] Node(l, r) then // * Currently unsupported syntax: // let [mn1, l1] = walk(l, mn) // let [mn2, r1] = walk(r, mn) // [min(mn1, mn2), Node(l1, r1)] let mnl = walk(l, mn) let mnr = walk(r, mn) [min(mnl.0, mnr.0), Node(mnl.1, mnr.1)] //│ fun walk: forall 'a 'b. (Leaf[Num & 'a] | Node[Num & 'a], Int & 'b) -> ['a, Leaf['b | 'a] | Node['b | 'a]] let tree = Node of (Node of (Node of Leaf(1), Leaf(5)), Leaf(3)), Node of Leaf(3), Leaf(2) //│ let tree: Node[1 | 2 | 3 | 5] //│ tree //│ = Node {} String of tree //│ Str //│ res //│ = 'Node(Node(Node(Leaf(1), Leaf(5)), Leaf(3)), Node(Leaf(3), Leaf(2)))' String of walk(tree, 2) //│ Str //│ res //│ = '1,Node(Node(Node(Leaf(1), Leaf(2)), Leaf(3)), Node(Leaf(3), Leaf(2)))' String of walk(tree, 1) //│ Str //│ res //│ = '1,Node(Node(Node(Leaf(1), Leaf(1)), Leaf(1)), Node(Leaf(1), Leaf(2)))' // * An eager walk function using Pair class Pair[out A, out B](val left: A, right: B) { fun toString() = join of "Pair(", String(left), ", ", String(right), ")" } //│ class Pair[A, B](left: A, right: B) { //│ fun toString: () -> Str //│ } fun walk(t, mn) = if t is Leaf(n) then Pair of n, Leaf(if n > 2 * mn then mn else n) Node(l, r) and walk(l, mn) is Pair(mn1, l1) and walk(r, mn) is Pair(mn2, r1) then Pair of min(mn1, mn2), Node(l1, r1) //│ fun walk: forall 'A 'a. (Leaf['A] | Node['A], Int & 'a) -> Pair['A, Leaf['a | 'A] | Node['a | 'A]] //│ where //│ 'A <: Num String of walk(tree, 2) //│ Str //│ res //│ = 'Pair(1, Node(Node(Node(Leaf(1), Leaf(2)), Leaf(3)), Node(Leaf(3), Leaf(2))))' // * A lazy walk function fun walkLazy(t, mn) = if t is Leaf(n) then log of join of "Traversing leaf ", String of n [n, Lazy of () => Leaf of if n > 2 * mn.get then mn.get else n] Node(l, r) then let mnl = walkLazy(l, mn) let mnr = walkLazy(r, mn) [min(mnl.0, mnr.0), Lazy of () => Node of mnl.1.get, mnr.1.get] //│ fun walkLazy: forall 'a 'get. (Leaf[Num & 'a] | Node[Num & 'a], {get: Int & 'get}) -> ['a, Lazy[Leaf['get | 'a] | Node['get | 'a]]] // * One way to tie the recursive knot class repmin(t: Tree[Int]) { val mn_res = walkLazy of t, Lazy of () => mn_res.0 val mn = mn_res.0 val tree = mn_res.1.get } //│ class repmin(t: Tree[Int]) { //│ val mn: Int //│ val mn_res: [Int, Lazy[Leaf[Int] | Node[Int]]] //│ val tree: Leaf[Int] | Node[Int] //│ } let rm = repmin(tree) //│ let rm: repmin //│ rm //│ = repmin {} //│ // Output //│ Traversing leaf 1 //│ Traversing leaf 5 //│ Traversing leaf 3 //│ Traversing leaf 3 //│ Traversing leaf 2 [rm.mn, String of rm.tree] //│ [Int, Str] //│ res //│ = [ //│ 1, //│ 'Node(Node(Node(Leaf(1), Leaf(1)), Leaf(1)), Node(Leaf(1), Leaf(2)))' //│ ] // * Another way fun repmin(t) = module Rec { val mn_res = walkLazy of t, Lazy of () => mn_res.0 } [Rec.mn_res.0, Rec.mn_res.1.get] //│ fun repmin: forall 'a. (Leaf[Int & 'a] | Node[Int & 'a]) -> ['a, Leaf['a] | Node['a]] String of repmin(tree) //│ Str //│ res //│ = '1,Node(Node(Node(Leaf(1), Leaf(1)), Leaf(1)), Node(Leaf(1), Leaf(2)))' //│ // Output //│ Traversing leaf 1 //│ Traversing leaf 5 //│ Traversing leaf 3 //│ Traversing leaf 3 //│ Traversing leaf 2 // * A way that could work but currently doesn't because of a codegen limitation :ge fun repmin(t) = let rec mn_res = walkLazy of t, Lazy(() => mn_res.0) [mn_res.0, mn_res.1.get] //│ fun repmin: forall 'a. (Leaf[Int & 'a] | Node[Int & 'a]) -> ['a, Leaf['a] | Node['a]] //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding mn_res :re String of repmin(tree) //│ Str //│ res //│ Runtime error: //│ ReferenceError: repmin2 is not defined // * An alternative solution using mutation and thunks instead of laziness let IntMax = 2147483648 //│ let IntMax: 2147483648 //│ IntMax //│ = 2147483648 fun repmin(t) = mut let min = IntMax let rec go(t) = if t is Leaf(n) then log of join of "Traversing leaf ", String of n if n < min then set min = n else () () => Leaf of if n > 2 * min then min else n Node(l, r) then let l' = go(l) let r' = go(r) () => Node of l'(), r'() let res = go(t) [min, res()] //│ fun repmin: forall 'min. (Leaf[Int & 'min] | Node[Int & 'min]) -> [2147483648 | 'min, Leaf[2147483648 | 'min] | Node[2147483648 | 'min]] String of repmin(tree) //│ Str //│ res //│ = '1,Node(Node(Node(Leaf(1), Leaf(1)), Leaf(1)), Node(Leaf(1), Leaf(2)))' //│ // Output //│ Traversing leaf 1 //│ Traversing leaf 5 //│ Traversing leaf 3 //│ Traversing leaf 3 //│ Traversing leaf 2 // * Aside on UCS parsing: p => if p is Pair of a, b then a + b //│ Pair[Int, Int] -> Int //│ res //│ = [Function: res] :e // TODO handle this pattern better in the parser? (p, q) => if p is Pair of a, b and q is Pair of a', b' then Pair of a + a', b + b' //│ ╔══[ERROR] type identifier `and` not found //│ ║ l.267: (p, q) => if p is Pair of a, b and q is Pair of a', b' then Pair of a + a', b + b' //│ ╙── ^^^ //│ ╔══[ERROR] type identifier `is` not found //│ ║ l.267: (p, q) => if p is Pair of a, b and q is Pair of a', b' then Pair of a + a', b + b' //│ ╙── ^^ //│ ╔══[ERROR] type identifier not found: and //│ ║ l.267: (p, q) => if p is Pair of a, b and q is Pair of a', b' then Pair of a + a', b + b' //│ ╙── ^^^ //│ (Pair[anything, nothing], anything) -> error //│ Code generation encountered an error: //│ unresolved symbol is (p, q) => if p is (Pair of a, b) and q is Pair of a', b' then Pair of a + a', b + b' //│ (Pair[Int, Int], Pair[Int, Int]) -> Pair[Int, Int] //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/ex/UnboxedOptions.mls ================================================ :NewDefs // * Similar in spirit to https://github.com/sjrd/scala-unboxed-option // * In these tests, we need to use an `Object` bound to allow pattern-matching on unboxed option values. // * This bound is needed by the type system in order to preserve parametricity in the general case. // * However, a real implementation could keep the definition of Opt abstract and do away with the `Object` bound // * through unchecked casts without compromising safety or parametricity. module None class Some[out A](val value: A) //│ module None //│ class Some[A](value: A) type Opt[out A] = Some[A] | None | A & ~Some & ~None //│ type Opt[A] = None | Some[A] | A & ~None & ~#Some fun some(x) = if x is refined(None) then Some(x) refined(Some) then Some(x) else x //│ fun some: forall 'a 'b 'c. (None & 'a | Object & 'b & ~#None & ~#Some | Some[anything] & 'c) -> (Some[None & 'a | Some[anything] & 'c] | 'b) let some_ = some : forall 'a: (Object & 'a) -> Opt['a] //│ let some_: forall 'a. (Object & 'a) -> Opt['a] //│ some_ //│ = [Function: some] fun fold(opt, k, d) = if opt is refined(None) then d refined(Some) then k(opt.value) else k(opt) //│ fun fold: forall 'value 'a. (None | Object & 'value & ~#None & ~#Some | Some[anything] & {value: 'value}, 'value -> 'a, 'a) -> 'a let fold_ = fold : forall 'a, 'b: (Opt['a] & Object, 'a -> 'b, 'b) -> 'b //│ let fold_: forall 'a 'b. (Opt['a] & Object, 'a -> 'b, 'b) -> 'b //│ fold_ //│ = [Function: fold] let fold_ = fold : forall 'a, 'b: (Opt['a & Object], 'a -> 'b, 'b) -> 'b //│ let fold_: forall 'a 'b. (Opt[Object & 'a], 'a -> 'b, 'b) -> 'b //│ fold_ //│ = [Function: fold] // * Examples using the annotated definitions, which is what things will look like from the outside world. let s = some_(42) //│ let s: Opt[42] //│ s //│ = 42 s : Opt[Int] //│ Opt[Int] //│ res //│ = 42 fold_(s, id, 0) //│ 0 | 42 //│ res //│ = 42 // * This wont work when `Opt` is made opaque to the outside: s : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = 42 let ss = some_(some_(42)) //│ let ss: Opt[Opt[42]] //│ ss //│ = 42 :e ss : Opt[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.79: ss : Opt[Int] //│ ║ ^^ //│ ╟── type `Some['a]` is not an instance of type `Int` //│ ║ l.17: type Opt[out A] = Some[A] | None | A & ~Some & ~None //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.79: ss : Opt[Int] //│ ╙── ^^^ //│ Opt[Int] //│ res //│ = 42 ss : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = 42 fold_(ss, o => fold_(o, id, -1), 0) //│ -1 | 0 | 42 //│ res //│ = 42 let s = some_(None) //│ let s: Opt[None] //│ s //│ = Some {} :e s : Opt[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.110: s : Opt[Int] //│ ║ ^ //│ ╟── reference of type `None` is not an instance of `Int` //│ ║ l.104: let s = some_(None) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.110: s : Opt[Int] //│ ╙── ^^^ //│ Opt[Int] //│ res //│ = Some {} s : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = Some {} fold_(s, o => fold_(o, id, -1), 0) //│ -1 | 0 //│ res //│ = -1 some_(some_(None)) //│ Opt[Opt[None]] //│ res //│ = Some {} :e ss : Opt[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.140: ss : Opt[Int] //│ ║ ^^ //│ ╟── type `Some['a]` is not an instance of type `Int` //│ ║ l.17: type Opt[out A] = Some[A] | None | A & ~Some & ~None //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.140: ss : Opt[Int] //│ ╙── ^^^ //│ Opt[Int] //│ res //│ = 42 ss : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = 42 fold_(ss, o => fold_(o, id, -1), 0) //│ -1 | 0 | 42 //│ res //│ = 42 // * Here we use the precise, unannotated types directly to showcase precise inferred types. let s = some(42) //│ let s: 42 | Some[nothing] //│ s //│ = 42 s : Opt[Int] //│ Opt[Int] //│ res //│ = 42 fold(s, id, 0) //│ 0 | 42 //│ res //│ = 42 s : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = 42 let ss = some(some(42)) //│ let ss: 42 | Some[Some[nothing]] //│ ss //│ = 42 :e ss : Opt[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.193: ss : Opt[Int] //│ ║ ^^ //│ ╟── application of type `Some[?A]` does not match type `Int | ~Some[anything]` //│ ║ l.23: refined(Some) then Some(x) //│ ╙── ^^^^^^^ //│ Opt[Int] //│ res //│ = 42 ss : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = 42 fold(ss, o => fold(o, id, -1), 0) //│ -1 | 0 | 42 //│ res //│ = 42 let s = some(None) //│ let s: Some[None] //│ s //│ = Some {} :e s : Opt[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.221: s : Opt[Int] //│ ║ ^ //│ ╟── reference of type `None` does not match type `Int | ~None` //│ ║ l.215: let s = some(None) //│ ╙── ^^^^ //│ Opt[Int] //│ res //│ = Some {} s : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = Some {} fold(s, o => fold(o, id, -1), 0) //│ -1 | 0 //│ res //│ = -1 some(some(None)) //│ Some[Some[None]] //│ res //│ = Some {} :e ss : Opt[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.248: ss : Opt[Int] //│ ║ ^^ //│ ╟── application of type `Some[?A]` does not match type `Int | ~Some[anything]` //│ ║ l.23: refined(Some) then Some(x) //│ ╙── ^^^^^^^ //│ Opt[Int] //│ res //│ = 42 ss : Opt[Opt[Int]] //│ Opt[Opt[Int]] //│ res //│ = 42 fold(ss, o => fold(o, id, -1), 0) //│ -1 | 0 | 42 //│ res //│ = 42 // * Demonstration that we can't lift a parametric value into an unboxed option without casts. fun mk(x: Object) = some(x) //│ fun mk: (x: Object) -> (Object & ~#None & ~#Some | Some[None | Some[anything]]) :e fun mk[A](x: A) = some(x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.276: fun mk[A](x: A) = some(x) //│ ║ ^^^^^^^ //│ ╟── reference of type `A` is not an instance of type `Object` //│ ║ l.276: fun mk[A](x: A) = some(x) //│ ║ ^ //│ ╟── Note: constraint arises from `case` expression: //│ ║ l.21: fun some(x) = if x is //│ ║ ^^^^ //│ ║ l.22: refined(None) then Some(x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.23: refined(Some) then Some(x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.24: else x //│ ║ ^^^^^^^^ //│ ╟── from reference: //│ ║ l.21: fun some(x) = if x is //│ ║ ^ //│ ╟── Note: method type parameter A is defined at: //│ ║ l.276: fun mk[A](x: A) = some(x) //│ ╙── ^ //│ fun mk: forall 'A. (x: 'A) -> (Some['A & (None | Some[anything])] | error | 'A & ~#None & ~#Some) fun mk[A](x: A & Object) = some(x) //│ fun mk: forall 'A. (x: Object & 'A) -> (Object & 'A & ~#None & ~#Some | Some['A & (None | Some[anything])]) ================================================ FILE: shared/src/test/diff/fcp/AA.mls ================================================ // https://github.com/cliffclick/aa/issues/28 pair x y = (x, y) I = (fun x->x); K = (fun x->(fun y->x)); W = (fun x->(x x)); term = (fun z->(fun y ->((y (z I))(z K)))); test = (term W); (test (fun x -> (fun y -> (pair (x 3) (((y "a") "b") "c")) ) )) //│ pair: 'a -> 'b -> ('a, 'b,) //│ = [Function: pair] //│ I: 'a -> 'a //│ = [Function: I] //│ K: 'a -> anything -> 'a //│ = [Function: K] //│ W: ('a -> 'b & 'a) -> 'b //│ = [Function: W] //│ term: ((forall 'a. 'a -> 'a) -> 'b & (forall 'c. 'c -> anything -> 'c) -> 'd) -> ('b -> 'd -> 'e) -> 'e //│ = [Function: term] //│ test: ((forall 'a. 'a -> 'a) -> (forall 'b. anything -> 'b -> anything -> 'b) -> 'c) -> 'c //│ = [Function (anonymous)] //│ res: (3, "b",) //│ = [ 3, 'b' ] ================================================ FILE: shared/src/test/diff/fcp/Aleks.mls ================================================ :NoJS // ∀x. (x -> x, x -> x) <: ∀y. (y -> y, a) // SKOLEM y // FLEX x // x < y < x // x -> x < a -- wrong level // x_L -> x_U < a // where // x_L < x_U // x < x_U // x > x_L // x_U > Top ( > y ) // x_L < Bot ( < y ) def foo: forall 'x. ('x -> 'x, 'x -> 'x) //│ foo: ('x -> 'x, 'x -> 'x,) def bar: (forall 'y. ('y -> 'y, 'a)) -> 'a //│ bar: (forall 'y. ('y -> 'y, 'a,)) -> 'a r1 = bar foo //│ r1: ??y -> anything def baz: (forall 'y. ('a, 'y -> 'y)) -> 'a //│ baz: (forall 'y. ('a, 'y -> 'y,)) -> 'a r2 = bar foo //│ r2: ??y -> anything :e r1 id //│ ╔══[ERROR] Type error in application //│ ║ l.38: r1 id //│ ║ ^^^^^ //│ ╟── type variable `'y` leaks out of its scope //│ ║ l.38: r1 id //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.25: r1 = bar foo //│ ║ ^^^ //│ ╟── • this reference: //│ ║ l.25: r1 = bar foo //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.18: def foo: forall 'x. ('x -> 'x, 'x -> 'x) //│ ╙── ^^ //│ res: anything :e r2 id //│ ╔══[ERROR] Type error in application //│ ║ l.58: r2 id //│ ║ ^^^^^ //│ ╟── type variable `'y` leaks out of its scope //│ ║ l.58: r2 id //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.32: r2 = bar foo //│ ║ ^^^ //│ ╟── • this reference: //│ ║ l.32: r2 = bar foo //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.18: def foo: forall 'x. ('x -> 'x, 'x -> 'x) //│ ╙── ^^ //│ res: anything ================================================ FILE: shared/src/test/diff/fcp/Aliases.mls ================================================ :NoJS type Sid = forall 'A. 'A -> 'A //│ Defined type alias Sid type Sid2 = 'B -> 'B //│ Defined type alias Sid2 type A1 = forall 'S. 'S -> ('S | Sid) //│ Defined type alias A1 def a: Sid -> Sid //│ a: Sid -> Sid a : Sid2 -> Sid2 : Sid -> Sid //│ res: Sid -> Sid a = error : A1 //│ A1 //│ <: a: //│ Sid -> Sid // def f: A1 -> Sid def f: A1 -> A1 //│ f: A1 -> A1 def f (x: A1) = x id //│ A1 -> ('a -> 'a | Sid) //│ <: f: //│ A1 -> A1 // def f: A1 -> Sid def f: A1 -> A1 //│ f: A1 -> A1 def f (x: Sid -> Sid) = x (fun y -> y) //│ (Sid -> Sid) -> Sid //│ <: f: //│ A1 -> A1 type A2 = forall 'a. (('a | Sid) -> 'a) -> 'a -> ('a | Sid) type A3 = (Sid -> Sid) -> Sid -> Sid //│ Defined type alias A2 //│ Defined type alias A3 error : A2 : A3 //│ res: A3 ================================================ FILE: shared/src/test/diff/fcp/Basics.mls ================================================ :NoRecursiveTypes def id: forall 'a. 'a -> 'a //│ id: 'a -> 'a //│ = def id x = x //│ 'a -> 'a //│ <: id: //│ 'a -> 'a //│ = [Function: id1] test1 f = (f 0, f true) //│ test1: (0 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: test1] test1 (fun value -> { value }) //│ res: ({value: 0}, {value: true},) //│ = [ { value: 0 }, { value: true } ] test2 f = let r x = f x in (r 0, r true) //│ test2: ((0 | true) -> 'a) -> ('a, 'a,) //│ = [Function: test2] test2 (fun value -> { value }) //│ res: ({value: 0 | true}, {value: 0 | true},) //│ = [ { value: 0 }, { value: true } ] // * From supertype's `freeze-ml.sup` def poly f = (f 42, f true) //│ poly: (42 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: poly] def bad1 f = (poly f, f 42 + 1) def bad2 f = (f 42 + 1, poly f) //│ bad1: (42 -> (int & 'a) & true -> 'b) -> (('a, 'b,), int,) //│ = [Function: bad1] //│ bad2: (42 -> (int & 'a) & true -> 'b) -> (int, ('a, 'b,),) //│ = [Function: bad2] def example f = (f: forall 'a. 'a -> 'a) //│ example: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: example] :e example 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.57: example 1 //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.57: example 1 //│ ║ ^ //│ ╟── Note: constraint arises from function type: //│ ║ l.52: def example f = (f: forall 'a. 'a -> 'a) //│ ║ ^^^^^^^^ //│ ╟── from reference: //│ ║ l.52: def example f = (f: forall 'a. 'a -> 'a) //│ ╙── ^ //│ res: error | 'a -> 'a //│ = 1 :e example succ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.74: example succ //│ ║ ^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.52: def example f = (f: forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.52: def example f = (f: forall 'a. 'a -> 'a) //│ ╙── ^^ //│ res: error | 'a -> 'a //│ = [Function: succ] example id //│ res: 'a -> 'a //│ = [Function: id1] def example f = (f: forall 'a. 'a -> int -> 'b) f //│ example: (anything -> int -> 'b) -> int -> 'b //│ = [Function: example1] def example f = ((fun x -> x, f) : forall 'a. ('a -> 'a, 'a -> 'a)) //│ example: (??a -> ??a0) -> ('a -> 'a, 'a -> 'a,) //│ = [Function: example2] def example f = ((0, f) : forall 'a. (0, 'a)) //│ example: ??a -> (0, nothing,) //│ = [Function: example3] def ex = example //│ ex: ??a -> (0, nothing,) //│ = [Function: ex] def ex x = example x //│ ex: ??a -> (0, nothing,) //│ = [Function: ex1] :e ex 1 //│ ╔══[ERROR] Type error in application //│ ║ l.113: ex 1 //│ ║ ^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.113: ex 1 //│ ║ ^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.100: def example f = ((0, f) : forall 'a. (0, 'a)) //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.108: def ex x = example x //│ ╙── ^ //│ res: error | (0, nothing,) //│ = [ 0, 1 ] def example f = ((0, f) : forall 'a. (0, 'a -> 'a)) //│ example: (??a -> ??a0) -> (0, 'a -> 'a,) //│ = [Function: example4] ================================================ FILE: shared/src/test/diff/fcp/Church_CT.mls ================================================ :NoRecursiveTypes :ConstrainedTypes :DontDistributeForalls type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ Defined type alias ChurchInt // * === Explicitly Annotated === def zero: forall 'a. ('a -> 'a) -> ('a -> 'a) def zero f x = x //│ zero: ('a -> 'a) -> 'a -> 'a //│ = //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ('a -> 'a) -> 'a -> 'a //│ = [Function: zero] def zero: ChurchInt def zero f x = x //│ zero: ChurchInt //│ = //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ChurchInt //│ = [Function: zero1] def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ succ: (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = :e // * Since "sound extrusion" def succ n f x = f (n f x) //│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.39: def succ n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.39: def succ n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ╙── ^^ //│ = [Function: succ1] def succ: ChurchInt -> ChurchInt //│ succ: ChurchInt -> ChurchInt //│ = :e // * Since "sound extrusion" def succ n f x = f (n f x) //│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.69: def succ n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.69: def succ n f x = f (n f x) //│ ╙── ^^^^^^^^^^^^^^^ //│ = [Function: succ2] def succD: forall 'M. ChurchInt -> ('M -> 'M) -> ('M -> 'M) def succD n f x = f (n f x) //│ succD: ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = //│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: succD] succD = succ //│ ChurchInt -> ChurchInt //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: succ2] // * This does not hold without distrobutivity :e succ = succD //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.112: succ = succD //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.112: succ = succD //│ ╙── ^^^^^ //│ = [Function: succ2] // * === Usages === n1 = succ zero //│ n1: ChurchInt //│ = [Function (anonymous)] n2 = succ (succ zero) //│ n2: ChurchInt //│ = [Function (anonymous)] n3 = succ (succ (succ zero)) //│ n3: ChurchInt //│ = [Function (anonymous)] wrap x = { x } //│ wrap: 'a -> {x: 'a} //│ = [Function: wrap] :e n1w = n1 wrap //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> {x: 'a} //│ ╙── //│ n1w: 'N -> 'N //│ where //│ 'N :> {x: 'N} //│ = [Function (anonymous)] :e n1w 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: 0 } :e n1 wrap 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: 0 } :e res.x + 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.184: res.x + 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.173: n1 wrap 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.184: res.x + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.184: res.x + 1 //│ ║ ^^^^^^^ //│ ╟── record literal of type `{x: ?a}` is not an instance of type `int` //│ ║ l.146: wrap x = { x } //│ ║ ^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.184: res.x + 1 //│ ╙── ^^^^^ //│ res: error | int //│ = 1 :e n2 wrap 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: { x: 0 } } :e res.x.x + 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.222: res.x.x + 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.211: n2 wrap 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.222: res.x.x + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.222: res.x.x + 1 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.211: n2 wrap 0 //│ ║ ^ //│ ╟── but it flows into field selection with expected type `{x: ?x}` //│ ║ l.222: res.x.x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.222: res.x.x + 1 //│ ║ ^^^^^^^^^ //│ ╟── record literal of type `{x: ?a}` is not an instance of type `int` //│ ║ l.146: wrap x = { x } //│ ║ ^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.222: res.x.x + 1 //│ ╙── ^^^^^^^ //│ res: error | int //│ = 1 def wrap_ty: 'a -> {x: 'a} //│ wrap_ty: 'a -> {x: 'a} //│ = wrap_ty = wrap //│ 'a -> {x: 'a} //│ <: wrap_ty: //│ 'a -> {x: 'a} //│ = [Function: wrap] :e n1w = n1 wrap_ty //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> {x: 'a} //│ ╙── //│ n1w: 'N -> 'N //│ where //│ 'N :> {x: 'N} //│ = [Function (anonymous)] :e n1w 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: 0 } :e res.x + 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.291: res.x + 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.280: n1w 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.291: res.x + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.291: res.x + 1 //│ ║ ^^^^^^^ //│ ╟── type `{x: 'a}` is not an instance of type `int` //│ ║ l.258: def wrap_ty: 'a -> {x: 'a} //│ ║ ^^^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.291: res.x + 1 //│ ╙── ^^^^^ //│ res: error | int //│ = 1 // * === Unannotated (More Powerful) === def z f x = x //│ z: anything -> (forall 'a. 'a -> 'a) //│ = [Function: z] def s n f x = f (n f x) //│ s: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ = [Function: s] zero = z //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ChurchInt //│ = [Function: z] :e // * Since "sound extrusion" succ = s //│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.340: succ = s //│ ║ ^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.326: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.340: succ = s //│ ╙── ^ //│ = [Function: s] :stats z: ChurchInt //│ res: ChurchInt //│ = [Function: z] //│ constrain calls : 16 //│ annoying calls : 0 //│ subtyping calls : 33 :e // * Since "sound extrusion" :stats s: ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in type ascription //│ ║ l.375: s: ChurchInt -> ChurchInt //│ ║ ^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.326: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.375: s: ChurchInt -> ChurchInt //│ ╙── ^ //│ res: ChurchInt -> ChurchInt //│ = [Function: s] //│ constrain calls : 87 //│ annoying calls : 0 //│ subtyping calls : 229 // * === Usages === n1 = s z //│ n1: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ anything -> (forall 'e. 'e -> 'e) <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] n2 = s (s z) //│ n2: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ forall 'e. 'e -> (forall 'f 'g 'h. 'f -> 'h //│ where //│ anything -> (forall 'i. 'i -> 'i) <: 'e -> 'f -> 'g //│ 'e <: 'g -> 'h) <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] n3 = s (s (s z)) //│ n3: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ forall 'e. 'e -> (forall 'f 'g 'h. 'f -> 'h //│ where //│ forall 'i. 'i -> (forall 'j 'k 'l. 'j -> 'l //│ where //│ anything -> (forall 'm. 'm -> 'm) <: 'i -> 'j -> 'k //│ 'i <: 'k -> 'l) <: 'e -> 'f -> 'g //│ 'e <: 'g -> 'h) <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] wrap x = { x } //│ wrap: 'a -> {x: 'a} //│ = [Function: wrap1] n1w = n1 wrap //│ n1w: 'a -> 'b //│ where //│ anything -> (forall 'c. 'c -> 'c) <: (forall 'd. 'd -> {x: 'd}) -> 'a -> 'e //│ forall 'd. 'd -> {x: 'd} <: 'e -> 'b //│ = [Function (anonymous)] n1w 0 //│ res: {x: 0} //│ = { x: 0 } n1 wrap 0 //│ res: {x: 0} //│ = { x: 0 } res.x + 1 //│ res: int //│ = 1 n2 wrap 0 //│ res: {x: {x: 0}} //│ = { x: { x: 0 } } res.x.x + 1 //│ res: int //│ = 1 n3 wrap 0 //│ res: {x: {x: {x: 0}}} //│ = { x: { x: { x: 0 } } } res.x.x.x + 1 //│ res: int //│ = 1 def wrap_ty: 'a -> {x: 'a} //│ wrap_ty: 'a -> {x: 'a} //│ = wrap_ty = wrap //│ 'a -> {x: 'a} //│ <: wrap_ty: //│ 'a -> {x: 'a} //│ = [Function: wrap1] n1w = n1 wrap_ty //│ n1w: 'b -> 'c //│ where //│ anything -> (forall 'd. 'd -> 'd) <: (forall 'a. 'a -> {x: 'a}) -> 'b -> 'e //│ forall 'a. 'a -> {x: 'a} <: 'e -> 'c //│ = [Function (anonymous)] n1w 0 //│ res: {x: 0} //│ = { x: 0 } res.x + 1 //│ res: int //│ = 1 n2 wrap_ty 0 //│ res: {x: {x: 0}} //│ = { x: { x: 0 } } res.x.x + 1 //│ res: int //│ = 1 n3 wrap_ty 0 //│ res: {x: {x: {x: 0}}} //│ = { x: { x: { x: 0 } } } res.x.x.x + 1 //│ res: int //│ = 1 // * === Other Usages === sz = s zero //│ sz: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] :ns sz //│ res: forall 'a 'b. 'a //│ where //│ 'a :> forall 'c. 'c -> (forall 'd 'e 'f 'g. 'e -> 'g //│ where //│ 'b <: 'c -> 'd //│ 'c <: 'f -> 'g) //│ 'd <: 'e -> 'f //│ 'b :> ChurchInt //│ = [Function (anonymous)] sz: ChurchInt //│ res: ChurchInt //│ = [Function (anonymous)] :e // * Since "sound extrusion" s zero : ChurchInt //│ ╔══[ERROR] Type error in type ascription //│ ║ l.544: s zero : ChurchInt //│ ║ ^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ res: ChurchInt //│ = [Function (anonymous)] // * Error delayed by inconsistent constrained types // * Notice the unsatisfiable constraint `1 <: 'c -> 'b` sz1 = sz 1 //│ sz1: 'a -> 'b //│ where //│ ChurchInt <: 1 -> 'a -> 'c //│ 1 <: 'c -> 'b //│ = [Function (anonymous)] :ns sz1 //│ res: forall 'a 'b 'c. 'a //│ where //│ 'a :> forall 'd 'e 'f 'g. 'e -> 'g //│ where //│ 'b <: 'c -> 'd //│ 'c <: 'f -> 'g //│ 'd <: 'e -> 'f //│ 'c :> 1 //│ 'b :> ChurchInt //│ = [Function (anonymous)] :e // * Since inconsistent constrained types (delayed error from above) sz1 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.580: sz1 2 //│ ║ ^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.559: sz1 = sz 1 //│ ║ ^ //│ ╟── Note: constraint arises from function type: //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^^^^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: f is not a function // * === Conversions (Using Annotated Defs) === rec def to_church n = if n == 0 then zero else succ (to_church (n - 1)) //│ to_church: int -> ChurchInt //│ = [Function: to_church] def to_church_ty: int -> ChurchInt //│ to_church_ty: int -> ChurchInt //│ = to_church_ty = to_church //│ int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church] // * Non-recursive def emulates annotated recursive def def to_church_ty n = if n == 0 then zero else succ (to_church_ty (n - 1)) //│ int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_ty1] // * === Conversions (Using Unannotated Defs) === :e rec def to_ch_s n = if n == 0 then z else s (to_ch_s (n - 1)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. (? & 'b) -> (forall 'b 'c 'd 'e 'f. ('f & 'c) -> ('f | 'e) //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ to_ch_s: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('d | 'e) //│ where //│ 'f <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'f :> forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('e | 'd) //│ where //│ 'f <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function: to_ch_s] :e rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. (? & 'b) -> (forall 'b 'c 'd 'e 'f. ('f & 'c) -> ('f | 'e) //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ to_ch: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('d | 'e) //│ where //│ 'f <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'f :> forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('e | 'd) //│ where //│ 'f <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function: to_ch] :e // * Needs distrib (see below) to_church_ty = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. (? & 'b) -> (forall 'b 'c 'd 'e 'f. ('f & 'c) -> ('f | 'e) //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('d | 'e) //│ where //│ 'f <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'f :> forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('e & 'b) -> ('e | 'd) //│ where //│ 'f <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> ChurchInt` exceeded recursion depth limit (250) //│ ║ l.673: to_church_ty = to_ch //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch] :e rec def to_ch_simplif n = s (to_ch_simplif n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ to_ch_simplif: anything -> 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ = [Function: to_ch_simplif] :e to_church_ty = to_ch_simplif //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ anything -> 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch_simplif. ?to_ch_simplif <: int -> ChurchInt` exceeded recursion depth limit (250) //│ ║ l.716: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch_simplif] // * This annotation now only helps when also having :precise-rec-typing (see below) :e rec def to_ch_A1 n = if n == 0 then z else s (to_ch_A1 (n - 1) : ChurchInt) //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.741: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.742: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.743: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.741: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.742: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.743: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.741: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.742: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.743: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.326: def s n f x = f (n f x) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.741: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.742: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.743: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.326: def s n f x = f (n f x) //│ ╙── ^^^^^ //│ to_ch_A1: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ = [Function: to_ch_A1] :precise-rec-typing :e // occurs-check rec def to_ch_A1 n = if n == 0 then z else s (to_ch_A1 (n - 1) : ChurchInt) //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A1 //│ where //│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. (? & 'a) -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'to_ch_A1 <: int -> ChurchInt //│ ╙── //│ to_ch_A1: 'to_ch_A1 //│ where //│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'to_ch_A1 <: int -> ChurchInt //│ = [Function: to_ch_A11] // * But we can't check the corresponding type :e to_church_ty = to_ch_A1 //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A1 //│ where //│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. (? & 'a) -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'to_ch_A1 <: int -> ChurchInt //│ ╙── //│ 'to_ch_A1 //│ where //│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'to_ch_A1 <: int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.834: to_church_ty = to_ch_A1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch_A11] // * Similarly: :e rec def to_ch_A2 n = ( if n == 0 then z else s (to_ch_A2 (n - 1)) ) : ChurchInt //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.862: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.863: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.864: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.865: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.862: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.863: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.864: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.865: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.862: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.863: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.864: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.865: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.326: def s n f x = f (n f x) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.862: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.863: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.864: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.865: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.326: def s n f x = f (n f x) //│ ╙── ^^^^^ //│ to_ch_A2: int -> ChurchInt //│ = [Function: to_ch_A2] :precise-rec-typing :e // occurs-check rec def to_ch_A2 n = ( if n == 0 then z else s (to_ch_A2 (n - 1)) ) : ChurchInt //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A2 //│ where //│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt //│ where //│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ ╙── //│ to_ch_A2: 'to_ch_A2 //│ where //│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt //│ where //│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ = [Function: to_ch_A21] :e // * Since the removal of "recursive definition hacks" to_church_ty = to_ch_A2 //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A2 //│ where //│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt //│ where //│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ ╙── //│ 'to_ch_A2 //│ where //│ 'to_ch_A2 :> forall 'to_ch_A2. int -> ChurchInt //│ where //│ 'to_ch_A2 <: int -> (??N -> ??N0) -> ??N0 -> ??N //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.955: to_church_ty = to_ch_A2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch_A21] // * Uses `to_church` as the rec call def to_church_mix n = if n == 0 then z else s (to_church (n - 1)) //│ to_church_mix: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ = [Function: to_church_mix] :e to_church_ty = to_church_mix //│ int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd 'e. ('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.987: to_church_ty = to_church_mix //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.326: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.979: else s (to_church (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.987: to_church_ty = to_church_mix //│ ╙── ^^^^^^^^^^^^^ //│ = [Function: to_church_mix] :e // * It would seem we need to distribute back (in the other direction) here, but it works: :precise-rec-typing rec def to_chD n = succD (to_chD n) //│ ╔══[ERROR] Inferred recursive type: 'to_chD //│ where //│ 'to_chD :> forall 'a 'M. 'a -> ('M -> 'M) -> 'M -> 'M //│ where //│ 'to_chD <: 'a -> ChurchInt //│ ╙── //│ to_chD: 'to_chD //│ where //│ 'to_chD :> forall 'a 'M. 'a -> ('M -> 'M) -> 'M -> 'M //│ where //│ 'to_chD <: 'a -> ChurchInt //│ = [Function: to_chD] // * === With Distributivity === :DistributeForalls // * Now everything Just Works! // * Now works succ = s //│ 'a -> 'b -> ('c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'e //│ 'b <: 'e -> 'd) //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: s] // * Now works succ = succD //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: succ2] // * Now works rec def to_chD n = succD (to_chD n) //│ to_chD: anything -> ('M -> 'M) -> 'M -> 'M //│ = [Function: to_chD1] :e // * Since the removal of "recursive definition hacks" to_church_ty = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e 'f. (? & 'b) -> (('f & 'c) -> ('f | 'e) //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ int -> 'a -> ('b -> ('c | 'b) //│ where //│ 'd <: 'a -> 'b -> 'e //│ 'a <: 'e -> 'c) //│ where //│ 'd :> forall 'f 'g 'h 'i 'j. 'f -> (('j & 'g) -> ('j | 'i) //│ where //│ 'd <: 'f -> 'g -> 'h //│ 'f <: 'h -> 'i) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> ChurchInt` exceeded recursion depth limit (250) //│ ║ l.1069: to_church_ty = to_ch //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch] :e // * Since the removal of "recursive definition hacks" to_church_ty = to_ch_simplif //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. 'b -> ('c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ anything -> 'a -> ('b -> 'c //│ where //│ 'd <: 'a -> 'b -> 'e //│ 'a <: 'e -> 'c) //│ where //│ 'd :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i //│ where //│ 'd <: 'f -> 'g -> 'h //│ 'f <: 'h -> 'i) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.1095: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch_simplif] // * Redefinition in the context of distrib, just for the record (still infers but doesn't check) :e rec def to_ch_simplif n = s (to_ch_simplif n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. 'b -> ('c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ to_ch_simplif: anything -> 'a -> ('b -> 'c //│ where //│ 'd <: 'a -> 'b -> 'e //│ 'a <: 'e -> 'c) //│ where //│ 'd :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i //│ where //│ 'd <: 'f -> 'g -> 'h //│ 'f <: 'h -> 'i) //│ = [Function: to_ch_simplif1] :e // * Since the removal of "recursive definition hacks" to_church_ty = to_ch_simplif //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. 'b -> ('c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e) //│ ╙── //│ anything -> 'a -> ('b -> 'c //│ where //│ 'd <: 'a -> 'b -> 'e //│ 'a <: 'e -> 'c) //│ where //│ 'd :> forall 'f 'g 'h 'i. 'f -> ('g -> 'i //│ where //│ 'd <: 'f -> 'g -> 'h //│ 'f <: 'h -> 'i) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.1142: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch_simplif1] // * Still doesn't work (Same with the full definition) :e to_church_ty = to_ch_A1 //│ ╔══[ERROR] Inferred recursive type: 'to_ch_A1 //│ where //│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a 'b 'c 'd 'e. (? & 'a) -> (('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'to_ch_A1 <: int -> ChurchInt //│ ╙── //│ 'to_ch_A1 //│ where //│ 'to_ch_A1 :> forall 'to_ch_A1. int -> (forall 'a 'b 'c 'd 'e. 'a -> (('b & 'e) -> ('d | 'e) //│ where //│ ChurchInt <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd)) //│ where //│ 'to_ch_A1 <: int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.1169: to_church_ty = to_ch_A1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch_A11] // * Now works to_church_ty = to_church_mix //│ int -> 'a -> ('b -> ('c | 'b) //│ where //│ ChurchInt <: 'a -> 'b -> 'd //│ 'a <: 'd -> 'c) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_mix] ================================================ FILE: shared/src/test/diff/fcp/Church_ST.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ Defined type alias ChurchInt // * === Explicitly Annotated === def zero: forall 'a. ('a -> 'a) -> ('a -> 'a) def zero f x = x //│ zero: ('a -> 'a) -> 'a -> 'a //│ = //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ('a -> 'a) -> 'a -> 'a //│ = [Function: zero] def zero: ChurchInt def zero f x = x //│ zero: ChurchInt //│ = //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ChurchInt //│ = [Function: zero1] def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ succ: (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = // * When we don't infer constrained types and extrude eagerly instead, // * this one requires distributing the RHS forall to rigidify it earlier: :e def succ n f x = f (n f x) //│ ('a -> 'b -> 'c) -> (forall 'd. ('c -> 'd & 'a) -> 'b -> 'd) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.41: def succ n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.41: def succ n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ╙── ^^ //│ = [Function: succ1] // * This explicit annotation makes it type check; like in MLF: def succ (n: (forall 'N. ('N -> 'N) -> ('N -> 'N))) = fun f -> fun x -> f (n f x) //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'N0 'N1 'a. ('N0 -> ('N0 & 'N1) & 'N1 -> 'a) -> ('N0 & 'N1) -> 'a) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: succ2] def succ (n: ChurchInt) f x = f (n f x) //│ ChurchInt -> (forall 'N 'N0 'a. ('N -> ('N & 'N0) & 'N0 -> 'a) -> ('N & 'N0) -> 'a) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: succ3] // * Manually distributing the signature also fixes the type checking: def succD: forall 'M. ChurchInt -> ('M -> 'M) -> ('M -> 'M) def succD n f x = f (n f x) //│ succD: ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = //│ ('a -> 'b -> 'c) -> (forall 'd. ('c -> 'd & 'a) -> 'b -> 'd) //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: succD] succD = succ //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: succ3] // * This does not hold without distrobutivity :e succ = succD //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.94: succ = succD //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.94: succ = succD //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.77: def succD: forall 'M. ChurchInt -> ('M -> 'M) -> ('M -> 'M) //│ ╙── ^^ //│ = [Function: succ3] // * === Usages === n1 = succ zero //│ n1: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] n2 = succ (succ zero) //│ n2: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] n3 = succ (succ (succ zero)) //│ n3: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] wrap x = { x } //│ wrap: 'a -> {x: 'a} //│ = [Function: wrap] :e n1w = n1 wrap //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> {x: 'a} //│ ╙── //│ n1w: 'M -> 'M //│ where //│ 'M :> {x: 'M} //│ = [Function (anonymous)] :e n1w 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: 0 } :e n1 wrap 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: 0 } :e res.x + 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.172: res.x + 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.161: n1 wrap 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.172: res.x + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.172: res.x + 1 //│ ║ ^^^^^^^ //│ ╟── record literal of type `{x: ?a}` is not an instance of type `int` //│ ║ l.134: wrap x = { x } //│ ║ ^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.172: res.x + 1 //│ ╙── ^^^^^ //│ res: error | int //│ = 1 :e n2 wrap 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: { x: 0 } } :e res.x.x + 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.210: res.x.x + 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.199: n2 wrap 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.210: res.x.x + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.210: res.x.x + 1 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.199: n2 wrap 0 //│ ║ ^ //│ ╟── but it flows into field selection with expected type `{x: ?x}` //│ ║ l.210: res.x.x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.210: res.x.x + 1 //│ ║ ^^^^^^^^^ //│ ╟── record literal of type `{x: ?a}` is not an instance of type `int` //│ ║ l.134: wrap x = { x } //│ ║ ^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.210: res.x.x + 1 //│ ╙── ^^^^^^^ //│ res: error | int //│ = 1 def wrap_ty: 'a -> {x: 'a} //│ wrap_ty: 'a -> {x: 'a} //│ = wrap_ty = wrap //│ 'a -> {x: 'a} //│ <: wrap_ty: //│ 'a -> {x: 'a} //│ = [Function: wrap] :e n1w = n1 wrap_ty //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> {x: 'a} //│ ╙── //│ n1w: 'M -> 'M //│ where //│ 'M :> {x: 'M} //│ = [Function (anonymous)] :e n1w 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ = { x: 0 } :e res.x + 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ ╙── //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.279: res.x + 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `0` does not have field 'x' //│ ║ l.268: n1w 0 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.279: res.x + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.279: res.x + 1 //│ ║ ^^^^^^^ //│ ╟── type `{x: 'a}` is not an instance of type `int` //│ ║ l.246: def wrap_ty: 'a -> {x: 'a} //│ ║ ^^^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.279: res.x + 1 //│ ╙── ^^^^^ //│ res: error | int //│ = 1 // * === Unannotated (More Powerful) === def z f x = x //│ z: anything -> (forall 'a. 'a -> 'a) //│ = [Function: z] def s n f x = f (n f x) //│ s: ('a -> 'b -> 'c) -> (forall 'd. ('c -> 'd & 'a) -> 'b -> 'd) //│ = [Function: s] def s_A1 (n: ChurchInt) f x = f (n f x) //│ s_A1: ChurchInt -> (forall 'N 'N0 'a. ('N -> ('N & 'N0) & 'N0 -> 'a) -> ('N & 'N0) -> 'a) //│ = [Function: s_A1] zero = z //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ChurchInt //│ = [Function: z] // * Similalry as before, unannotated succ impl requires constrained types :e succ = s //│ ('a -> 'b -> 'c) -> (forall 'd. ('c -> 'd & 'a) -> 'b -> 'd) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.330: succ = s //│ ║ ^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.330: succ = s //│ ║ ^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.34: def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ╙── ^^ //│ = [Function: s] succ = s_A1 //│ ChurchInt -> (forall 'N 'N0 'a. ('N -> ('N & 'N0) & 'N0 -> 'a) -> ('N & 'N0) -> 'a) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: s_A1] succD = s //│ ('a -> 'b -> 'c) -> (forall 'd. ('c -> 'd & 'a) -> 'b -> 'd) //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: s] :stats z: ChurchInt //│ res: ChurchInt //│ = [Function: z] //│ constrain calls : 16 //│ annoying calls : 0 //│ subtyping calls : 33 :e :stats s: ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in type ascription //│ ║ l.377: s: ChurchInt -> ChurchInt //│ ║ ^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.377: s: ChurchInt -> ChurchInt //│ ╙── ^ //│ res: ChurchInt -> ChurchInt //│ = [Function: s] //│ constrain calls : 90 //│ annoying calls : 0 //│ subtyping calls : 255 // * === Usages === // * Interestingly, even without constrained types, // * we can still use these inferred types to check the following uses: n1 = s z //│ n1: ('a -> 'b) -> 'a -> 'b //│ = [Function (anonymous)] n2 = s (s z) //│ n2: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function (anonymous)] n3 = s (s (s z)) //│ n3: ('a -> 'b & 'b -> 'c & 'c -> 'd) -> 'a -> 'd //│ = [Function (anonymous)] wrap x = { x } //│ wrap: 'a -> {x: 'a} //│ = [Function: wrap1] n1w = n1 wrap //│ n1w: 'a -> {x: 'a} //│ = [Function (anonymous)] n1w 0 //│ res: {x: 0} //│ = { x: 0 } n1 wrap 0 //│ res: {x: 0} //│ = { x: 0 } res.x + 1 //│ res: int //│ = 1 n2 wrap 0 //│ res: {x: {x: 0}} //│ = { x: { x: 0 } } res.x.x + 1 //│ res: int //│ = 1 n3 wrap 0 //│ res: {x: {x: {x: 0}}} //│ = { x: { x: { x: 0 } } } res.x.x.x + 1 //│ res: int //│ = 1 def wrap_ty: 'a -> {x: 'a} //│ wrap_ty: 'a -> {x: 'a} //│ = wrap_ty = wrap //│ 'a -> {x: 'a} //│ <: wrap_ty: //│ 'a -> {x: 'a} //│ = [Function: wrap1] n1w = n1 wrap_ty //│ n1w: 'a -> {x: 'a} //│ = [Function (anonymous)] n1w 0 //│ res: {x: 0} //│ = { x: 0 } res.x + 1 //│ res: int //│ = 1 n2 wrap_ty 0 //│ res: {x: {x: 0}} //│ = { x: { x: 0 } } res.x.x + 1 //│ res: int //│ = 1 n3 wrap_ty 0 //│ res: {x: {x: {x: 0}}} //│ = { x: { x: { x: 0 } } } res.x.x.x + 1 //│ res: int //│ = 1 // * === Conversions (Using Annotated Defs) === rec def to_church n = if n == 0 then zero else succ (to_church (n - 1)) //│ to_church: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M | ChurchInt) //│ = [Function: to_church] def to_church_ty: int -> ChurchInt //│ to_church_ty: int -> ChurchInt //│ = to_church_ty = to_church //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M | ChurchInt) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church] // * Non-recursive def emulates annotated recursive def def to_church_ty n = if n == 0 then zero else succ (to_church_ty (n - 1)) //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M | ChurchInt) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_ty1] // * === Other Usages === sz = s zero //│ sz: ('N -> ('N & 'a)) -> 'N -> 'a //│ = [Function (anonymous)] :ns sz //│ res: forall 'a 'b 'c 'd 'N. 'a //│ where //│ 'a :> forall 'e 'f 'g. 'e -> (forall 'h 'i. 'h -> 'i) //│ 'i :> 'g //│ 'h <: 'c //│ 'c <: 'N //│ 'e <: 'f -> 'g & 'b //│ 'b <: 'N -> 'N //│ 'N <: 'd //│ 'f :> 'd //│ = [Function (anonymous)] sz: ChurchInt //│ res: ChurchInt //│ = [Function (anonymous)] // * This one works with CT... // * Apparently worked when generalizing the LHS of ascriptions :e s zero : ChurchInt //│ ╔══[ERROR] Type error in type ascription //│ ║ l.557: s zero : ChurchInt //│ ║ ^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.557: s zero : ChurchInt //│ ╙── ^^^^^^ //│ res: ChurchInt //│ = [Function (anonymous)] // * Unlike with CT, here the error is not delayed :e sz1 = sz 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.579: sz1 = sz 1 //│ ║ ^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.579: sz1 = sz 1 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^ //│ ╟── from reference: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^ //│ sz1: error | anything -> nothing //│ = [Function (anonymous)] :re sz1 2 //│ res: error //│ Runtime error: //│ TypeError: f is not a function // * === Conversions (Using Unannotated Defs) === rec def to_ch_s n = if n == 0 then z else s (to_ch_s (n - 1)) //│ to_ch_s: int -> (forall 'a. ('b -> 'a & 'b -> 'b) -> (forall 'c. ('b & 'c) -> ('a | 'c))) //│ = [Function: to_ch_s] // Q: what's the difference with to_ch_s?! rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ to_ch: int -> (forall 'a. ('b -> 'a & 'b -> 'b) -> (forall 'c. ('b & 'c) -> ('a | 'c))) //│ = [Function: to_ch] :e // * Needs distrib (see below) to_church_ty = to_ch //│ int -> (forall 'a. ('b -> 'a & 'b -> 'b) -> (forall 'c. ('b & 'c) -> ('a | 'c))) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.620: to_church_ty = to_ch //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.615: else s (to_ch (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.620: to_church_ty = to_ch //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^^^^^^^^^ //│ = [Function: to_ch] // * Works with version where `s` has annotated `n: ChurchInt` parameter rec def to_ch_s_A1 n = if n == 0 then z else s_A1 (to_ch_s_A1 (n - 1)) //│ to_ch_s_A1: int -> (forall 'N 'N0 'a. ('N -> ('N & 'N0) & 'N0 -> 'a) -> (forall 'b. ('N & 'N0 & 'b) -> ('a | 'b))) //│ = [Function: to_ch_s_A1] to_church_ty = to_ch_s_A1 //│ int -> (forall 'N 'N0 'a. ('N -> ('N & 'N0) & 'N0 -> 'a) -> (forall 'b. ('N & 'N0 & 'b) -> ('a | 'b))) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch_s_A1] // * Boil down the problem with `to_ch` rec def to_ch_simplif n = s (to_ch_simplif n) //│ to_ch_simplif: anything -> (forall 'a. ('b -> 'a & 'b -> 'b) -> anything -> 'a) //│ = [Function: to_ch_simplif] :e to_church_ty = to_ch_simplif //│ anything -> (forall 'a. ('b -> 'a & 'b -> 'b) -> anything -> 'a) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.667: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.662: rec def to_ch_simplif n = s (to_ch_simplif n) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.667: to_church_ty = to_ch_simplif //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^^^^^^^^^ //│ = [Function: to_ch_simplif] // * This annotation here doesn't help :e rec def to_ch_A1 n = if n == 0 then z else s (to_ch_A1 (n - 1) : ChurchInt) //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.698: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.699: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.698: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.699: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^ //│ ╟── • this applied expression: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.698: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.699: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.698: rec def to_ch_A1 n = //│ ║ ^^^ //│ ║ l.699: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^ //│ ╟── • this applied expression: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^ //│ to_ch_A1: int -> (forall 'a. (anything -> (??N & 'N & 'a)) -> (forall 'b. (??N & 'N & 'b) -> ('a | 'b))) //│ = [Function: to_ch_A1] // * nope // to_church_ty = to_ch_A1 // * This annotation makes it work with Constrained Types, but not without them!! :e rec def to_ch_A2 n = ( if n == 0 then z else s (to_ch_A2 (n - 1)) ) : ChurchInt //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.801: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.802: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.804: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.801: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.802: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.804: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.801: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.802: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.804: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^ //│ ╟── • this applied expression: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.801: rec def to_ch_A2 n = ( //│ ║ ^^^^^ //│ ║ l.802: if n == 0 then z //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.804: ) : ChurchInt //│ ║ ^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.803: else s (to_ch_A2 (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^ //│ ╟── • this applied expression: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^^^^^ //│ to_ch_A2: int -> ChurchInt //│ = [Function: to_ch_A2] to_church_ty = to_ch_A2 //│ int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch_A2] // * Uses `to_church` as the rec call def to_church_mix n = if n == 0 then z else s (to_church (n - 1)) //│ to_church_mix: int -> (forall 'a. ('M -> ('M & 'b) & 'N -> ('b & 'N) & 'b -> 'a) -> (forall 'c. ('M & 'b & 'N & 'c) -> ('a | 'c))) //│ = [Function: to_church_mix] // * Works with Constrained Types :e to_church_ty = to_church_mix //│ int -> (forall 'a. ('M -> ('M & 'b) & 'N -> ('b & 'N) & 'b -> 'a) -> (forall 'c. ('M & 'b & 'N & 'c) -> ('a | 'c))) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.924: to_church_ty = to_church_mix //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.918: else s (to_church (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.924: to_church_ty = to_church_mix //│ ╙── ^^^^^^^^^^^^^ //│ = [Function: to_church_mix] :e // * Needs to distribute back (in the other direction) here: rec def to_chD n = succD (to_chD n) //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.951: rec def to_chD n = //│ ║ ^^^ //│ ║ l.952: succD (to_chD n) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.77: def succD: forall 'M. ChurchInt -> ('M -> 'M) -> ('M -> 'M) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.951: rec def to_chD n = //│ ║ ^^^ //│ ║ l.952: succD (to_chD n) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.77: def succD: forall 'M. ChurchInt -> ('M -> 'M) -> ('M -> 'M) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.951: rec def to_chD n = //│ ║ ^^^ //│ ║ l.952: succD (to_chD n) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.951: rec def to_chD n = //│ ║ ^^^ //│ ║ l.952: succD (to_chD n) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ to_chD: anything -> (anything -> ??N) -> ??N -> anything //│ = [Function: to_chD] // * === With Distributivity === :DistributeForalls def zero f x = x //│ anything -> 'a -> 'a //│ <: zero: //│ ChurchInt //│ = [Function: zero3] // * Now it works! def succ n f x = f (n f x) //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: succ7] zero = z //│ anything -> 'a -> 'a //│ <: zero: //│ ChurchInt //│ = [Function: z] // * Now works succ = s //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: s] // * Still works succD = s //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: succD: //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ = [Function: s] // * Now works succ = succD //│ ChurchInt -> ('M -> 'M) -> 'M -> 'M //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: s] // * Now works rec def to_chD n = succD (to_chD n) //│ to_chD: anything -> ('M -> 'M) -> 'M -> 'M //│ = [Function: to_chD1] :stats z: ChurchInt //│ res: ChurchInt //│ = [Function: z] //│ constrain calls : 16 //│ annoying calls : 0 //│ subtyping calls : 33 :stats s: ChurchInt -> ChurchInt //│ res: ChurchInt -> ChurchInt //│ = [Function: s] //│ constrain calls : 80 //│ annoying calls : 0 //│ subtyping calls : 228 // * Now works to_church_ty = to_ch //│ int -> ('a -> 'b & 'a -> 'a) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch] // * Still works to_church_ty = to_ch_s_A1 //│ int -> ('N -> ('N & 'N0) & 'N0 -> 'a) -> ('N & 'N0 & 'a) -> 'a //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch_s_A1] // * Now works to_church_ty = to_ch_simplif //│ anything -> ('a -> 'b & 'a -> 'a) -> anything -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch_simplif] // * Redefinition in the context of distrib, just for the record (still works) rec def to_ch_simplif n = s (to_ch_simplif n) to_church_ty = to_ch_simplif //│ to_ch_simplif: anything -> ('a -> 'b & 'a -> 'a) -> anything -> 'b //│ = [Function: to_ch_simplif1] //│ anything -> ('a -> 'b & 'a -> 'a) -> anything -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch_simplif1] :e to_church_ty = to_ch_A1 //│ int -> (anything -> (??N & 'N & 'a)) -> (??N & 'N & 'a) -> 'a //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.1108: to_church_ty = to_ch_A1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── into type `'N0` //│ ║ l.7: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.700: else s (to_ch_A1 (n - 1) : ChurchInt) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── • this function: //│ ║ l.314: def s n f x = f (n f x) //│ ╙── ^^^^^^^^^^^^^ //│ = [Function: to_ch_A1] // * Now works to_church_ty = to_ch_A2 //│ int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_ch_A2] // * Now works to_church_ty = to_church_mix //│ int -> ('M -> ('M & 'a) & 'N -> ('a & 'N) & 'a -> 'b) -> ('M & 'a & 'N & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_mix] // * === Usages === n3 = s (s (s z)) //│ n3: ('a -> 'b & 'b -> 'c & 'c -> 'd) -> 'a -> 'd //│ = [Function (anonymous)] n3 wrap_ty 0 //│ res: {x: {x: {x: 0}}} //│ = { x: { x: { x: 0 } } } res.x.x.x + 1 //│ res: int //│ = 1 ================================================ FILE: shared/src/test/diff/fcp/ConstrainedTypes1.mls ================================================ :ConstrainedTypes // :d foo x = log (succ x.prop) //│ foo: {prop: int} -> unit //│ = [Function: foo] :ns foo x = let _ = log (succ x.prop) in x //│ foo: forall 'a 'prop. 'a -> 'a //│ where //│ 'a <: {prop: 'prop} //│ 'prop <: int //│ = [Function: foo1] :e foo false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.21: foo false //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` does not have field 'prop' //│ ║ l.21: foo false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.12: let _ = log (succ x.prop) //│ ╙── ^^^^^^ //│ res: error | false //│ = false //│ // Output //│ NaN foo x = let _ = log (fun y -> x.prop y) in x //│ foo: 'a -> 'a //│ = [Function: foo2] // :e // * Error swallowed by inconsistent constrained types... but it seems this can't lead to unsoundness foo false //│ res: false //│ = false //│ // Output //│ [Function (anonymous)] foo x = let _ = log ((fun y -> x.prop y) 0) in x //│ foo: ({prop: 0 -> anything} & 'a) -> 'a //│ = [Function: foo3] :e foo false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.56: foo false //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` does not have field 'prop' //│ ║ l.56: foo false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.50: let _ = log ((fun y -> x.prop y) 0) //│ ╙── ^^^^^^ //│ res: error | false //│ Runtime error: //│ TypeError: x.prop is not a function :ng app = error : ('a -> 'a) -> () //│ app: ('a -> 'a) -> () :ng foo x = let _ = app (fun y -> x.prop y) in x //│ foo: ({prop: 'a -> 'a} & 'b) -> 'b :e foo false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.81: foo false //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` does not have field 'prop' //│ ║ l.81: foo false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.76: let _ = app (fun y -> x.prop y) //│ ╙── ^^^^^^ //│ res: error | false //│ Runtime error: //│ TypeError: x.prop is not a function ================================================ FILE: shared/src/test/diff/fcp/ConstrainedTypes2.mls ================================================ :ConstrainedTypes def f n = n 0 //│ f: (0 -> 'a) -> 'a //│ = [Function: f] :e f {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.9: f {} //│ ║ ^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.9: f {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.4: def f n = n 0 //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.4: def f n = n 0 //│ ╙── ^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function // :d :e f {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.28: f {} {} //│ ║ ^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.28: f {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.4: def f n = n 0 //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.4: def f n = n 0 //│ ╙── ^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function def f n m = n 0 //│ f: 'a -> (anything -> 'b //│ where //│ 'a <: 0 -> 'b) //│ = [Function: f1] // :e // Error delayed by inconsistent constrained types f {} //│ res: anything -> 'a //│ where //│ anything <: 0 -> 'a //│ = [Function (anonymous)] :e f {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.60: f {} {} //│ ║ ^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.60: f {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.46: def f n m = n 0 //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function def test extr = let f n m = n 0 in let _ = extr f in f 0 //│ test: ((forall 'a 'b. 'a -> (anything -> 'b //│ where //│ 'a <: 0 -> 'b)) -> anything) -> (anything -> 'c //│ where //│ 0 <: 0 -> 'c) //│ = [Function: test] :e test 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.88: test 1 //│ ║ ^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.88: test 1 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.78: in let _ = extr f //│ ╙── ^^^^^^ //│ res: error | anything -> 'a //│ where //│ 0 <: 0 -> 'a //│ Runtime error: //│ TypeError: extr is not a function // * Note: this extrudes a Constrainedtype referring to a higher-level variable (x) def test extr x = let f n m = x (n 0) in let _ = extr f in f 0 //│ test: ((forall 'a 'b 'c. 'a -> (anything -> 'c //│ where //│ 'd <: 'b -> 'c //│ 'a <: 0 -> 'b)) -> anything) -> 'd -> (anything -> 'e //│ where //│ 'd <: 'f -> 'e //│ 0 <: 0 -> 'f) //│ = [Function: test1] :e test 0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.119: test 0 //│ ║ ^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.119: test 0 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.107: in let _ = extr f //│ ╙── ^^^^^^ //│ res: error | 'a -> (anything -> 'b //│ where //│ 'a <: 'c -> 'b //│ 0 <: 0 -> 'c) //│ = [Function (anonymous)] def test extr x = let f n m = x (n 0) in f 0 //│ test: anything -> 'a -> (anything -> 'b //│ where //│ 'a <: 'c -> 'b //│ 0 <: 0 -> 'c) //│ = [Function: test2] // Error delayed by inconsistent constrained types test 0 1 //│ res: anything -> 'a //│ where //│ 1 <: 'b -> 'a //│ 0 <: 0 -> 'b //│ = [Function (anonymous)] :e test 0 1 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.153: test 0 1 2 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.153: test 0 1 2 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.136: let f n m = x (n 0) //│ ╙── ^^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function def test extr x = let f n m = (n x 0) in f 0 //│ test: anything -> 'a -> (anything -> 'b //│ where //│ 0 <: 'a -> 0 -> 'b) //│ = [Function: test3] // Error delayed by inconsistent constrained types test 0 1 //│ res: anything -> 'a //│ where //│ 0 <: 1 -> 0 -> 'a //│ = [Function (anonymous)] :e test 0 1 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.183: test 0 1 2 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.169: in f 0 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.168: let f n m = (n x 0) //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function ================================================ FILE: shared/src/test/diff/fcp/Demo.mls ================================================ :NoJS // TODO support JS code-gen for datatypes datatype Option[A] = Some(A) | None //│ Defined class Option[+A] //│ Declared Option.Some: Option['A] -> ('A,) //│ Declared Option.None: Option[?] -> anything //│ Defined class Some[+A] //│ Defined class None[+A] //│ Some: 'a -> Option['a] //│ None: Option[nothing] // * Original def foo f = (f 123, f true) fooid = foo id fooSome = foo Some //│ foo: (123 -> 'a & true -> 'b) -> ('a, 'b,) //│ fooid: (123, true,) //│ fooSome: (Option[123], Option[true],) def bar f = (f (fun x -> x), f (fun x -> Some x)) test = bar foo //│ bar: ((forall 'a. 'a -> 'a) -> 'b & (forall 'c. 'c -> Option['c]) -> 'd) -> ('b, 'd,) //│ test: ((123, true,), (Option[123], Option[true],),) // * Let-expanded def foo f = let g = f in (f 123, f true) fooid = foo id fooSome = foo Some //│ foo: (123 -> 'a & true -> 'b) -> ('a, 'b,) //│ fooid: (123, true,) //│ fooSome: (Option[123], Option[true],) def bar f = (f (fun x -> x), f (fun x -> Some x)) test = bar foo //│ bar: ((forall 'a. 'a -> 'a) -> 'b & (forall 'c. 'c -> Option['c]) -> 'd) -> ('b, 'd,) //│ test: ((123, true,), (Option[123], Option[true],),) // * Eta-expanded (notice the lesser generality) def foo f = let g x = f x in (g 123, g true) fooid = foo id fooSome = foo Some //│ foo: ((123 | true) -> 'a) -> ('a, 'a,) //│ fooid: (123 | true, 123 | true,) //│ fooSome: (Option[123 | true], Option[123 | true],) def bar f = (f (fun x -> x), f (fun x -> Some x)) test = bar foo //│ bar: ((forall 'a. 'a -> 'a) -> 'b & (forall 'c. 'c -> Option['c]) -> 'd) -> ('b, 'd,) //│ test: ((123 | true, 123 | true,), (Option[123 | true], Option[123 | true],),) // * With function type annotation (same lesser generality) def foo (f: 'x -> 'a) = (f 123, f true) fooid = foo id fooSome = foo Some //│ foo: ((123 | true) -> 'a) -> ('a, 'a,) //│ fooid: (123 | true, 123 | true,) //│ fooSome: (Option[123 | true], Option[123 | true],) def bar f = (f (fun x -> x), f (fun x -> Some x)) test = bar foo //│ bar: ((forall 'a. 'a -> 'a) -> 'b & (forall 'c. 'c -> Option['c]) -> 'd) -> ('b, 'd,) //│ test: ((123 | true, 123 | true,), (Option[123 | true], Option[123 | true],),) // * Here's an idea: we could use application types to retain this function's precise typing // let g x = f x // g : forall 'a. 'a -> ('f 'a) where constraint: 'f <: 'a -> anything // extruded to 'f <: 'a0 -> anything where 'a <: 'a0 // // foo : 'f -> ('f 123, 'f true) where 'f <: 'a0 -> anything, 123 <: 'a0, true <: 'a0 // foo : 'f -> ('f 123, 'f true) where 'f <: (123 | true) -> anything // // foo Some -- ok because Some : (forall 'a. 'a -> Option['a]) <: (123 | true) -> anything // // * Note: the old idea was to use constrained types, as in: // def example f = f 123 // example : 'a -> ('a 123) where 'a <: 123 -> anything // * ... which worked, but CTs are a pain to work with/implement // * AND they may be silently inconsistent, delaying error reporting to use sites. // * Moreover, ensuring that CTs remain consistent defeats their purpose (can't solve tis problem anymore) datatype List[A] = Cons(A, List[A]) | Nil //│ Defined class List[+A] //│ Declared List.Cons: List['A] -> ('A, List['A],) //│ Declared List.Nil: List[?] -> anything //│ Defined class Cons[+A] //│ Defined class Nil[+A] //│ Cons: ('a, List['a],) -> List['a] //│ Nil: List[nothing] rec def find(table, key) = match table with | Nil -> None | Cons(h, t) -> if h.key == key then Some(h.val) else find(t, key) //│ find: (List[{key: number, val: 'a}], number,) -> Option['a] ls = Cons({key=0; val=id}, Cons({key=1; val=fun x -> x x}, Nil)) //│ ls: List[{key: 0 | 1, val: forall 'a 'b 'c. ('a -> 'b & 'c & 'a) -> ('c | 'b)}] def found: Option[forall 'a 'b. ('a -> 'b & 'a) -> ('b | 'a)] //│ found: Option[forall 'a 'b. ('a -> 'b & 'a) -> ('b | 'a)] found = find(ls, 1) //│ Option[forall 'a 'b 'c. ('a -> 'b & 'a & 'c) -> ('b | 'c)] //│ <: found: //│ Option[forall 'a 'b. ('a -> 'b & 'a) -> ('b | 'a)] match found with | None -> ("???", false) | Some(r) -> (r id "!!!", r id true) //│ res: ("!!!" | "???", Bool,) using fnd = match fnd with | None -> ("???", false) | Some(r) -> (r id "!!!", r id true) //│ using: Option[(forall 'a. 'a -> 'a) -> ("!!!" -> 'b & true -> 'c)] -> ("???" | 'b, false | 'c,) using found //│ res: ("!!!" | "???", Bool,) ================================================ FILE: shared/src/test/diff/fcp/Distrib.mls ================================================ :NoRecursiveTypes :DistributeForalls :NoJS def f1: 'a -> (forall 'b. 'b -> ('a, 'b)) def f2: 'a -> 'b -> ('a, 'b) //│ f1: 'a -> 'b -> ('a, 'b,) //│ f2: 'a -> 'b -> ('a, 'b,) // * With distributivity, f1 and f2 have equivalent types, as shown below f1 = f2 //│ 'a -> 'b -> ('a, 'b,) //│ <: f1: //│ 'a -> 'b -> ('a, 'b,) f2 = f1 //│ 'a -> 'b -> ('a, 'b,) //│ <: f2: //│ 'a -> 'b -> ('a, 'b,) // * However, f2 currently is not as flexible: def test: (forall 'b. 'b -> (int, 'b)) -> (int, bool) //│ test: (forall 'b. 'b -> (int, 'b,)) -> (int, bool,) test (f1 42) //│ res: (int, bool,) // This used not to be handled because we didn't yet try to split // type parameters in order to distribute only *part* of the quantification test (f2 42) //│ res: (int, bool,) // * Though these work! f1_42 = f1 42 f2_42 = f2 42 //│ f1_42: 'b -> (42, 'b,) //│ f2_42: 'b -> (42, 'b,) test f1_42 test f2_42 //│ res: (int, bool,) //│ res: (int, bool,) def test: forall 'a. (forall 'b. 'b -> (int, 'b)) -> (int, bool) //│ test: (forall 'b. 'b -> (int, 'b,)) -> (int, bool,) def f1_1: forall 'a. 'a -> (forall 'b. 'b -> ('a, 'b)) //│ f1_1: 'a -> 'b -> ('a, 'b,) def f2_1: forall 'a. forall 'b. 'a -> 'b -> ('a, 'b) def f2_2: forall 'b. forall 'a. 'a -> 'b -> ('a, 'b) def f2_3: forall 'a 'b. 'a -> 'b -> ('a, 'b) //│ f2_1: 'a -> 'b -> ('a, 'b,) //│ f2_2: 'a -> 'b -> ('a, 'b,) //│ f2_3: 'a -> 'b -> ('a, 'b,) test (f1_1 42) //│ res: (int, bool,) test (f2_1 42) test (f2_2 42) test (f2_3 42) //│ res: (int, bool,) //│ res: (int, bool,) //│ res: (int, bool,) f1_1_42 = f1_1 42 f2_1_42 = f2_1 42 f2_2_42 = f2_2 42 f2_3_42 = f2_3 42 //│ f1_1_42: 'b -> (42, 'b,) //│ f2_1_42: 'b -> (42, 'b,) //│ f2_2_42: 'b -> (42, 'b,) //│ f2_3_42: 'b -> (42, 'b,) test f1_1_42 test f2_1_42 test f2_2_42 test f2_3_42 //│ res: (int, bool,) //│ res: (int, bool,) //│ res: (int, bool,) //│ res: (int, bool,) // * Note: without distributivity: :DontDistributeForalls :e f1 = f2 //│ 'a -> 'b -> ('a, 'b,) //│ <: f1: //│ 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ╔══[ERROR] Type error in def definition //│ ║ l.103: f1 = f2 //│ ║ ^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.7: def f1: 'a -> (forall 'b. 'b -> ('a, 'b)) //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.7: def f1: 'a -> (forall 'b. 'b -> ('a, 'b)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.103: f1 = f2 //│ ╙── ^^ f2 = f1 //│ 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ <: f2: //│ 'a -> 'b -> ('a, 'b,) ================================================ FILE: shared/src/test/diff/fcp/Distrib2.mls ================================================ :NoRecursiveTypes :DistributeForalls :NoJS type BTB = forall 'b. 'b -> 'b //│ Defined type alias BTB // * ========= This first version works fine because the LHS `forall 'a.` can be distributed. ========= def ty_1: (nothing -> 'a) -> 'a //│ ty_1: (nothing -> 'a) -> 'a def ty_2: (forall 'c. 'c -> 'c -> 'c) -> BTB //│ ty_2: (forall 'c. 'c -> 'c -> 'c) -> BTB // * Works thanks to `:DistributeForalls`, distributing the LHS (`forall 'a`) ty_2 = ty_1 //│ (nothing -> 'a) -> 'a //│ <: ty_2: //│ (forall 'c. 'c -> 'c -> 'c) -> BTB def ty_1_2: (nothing -> BTB) -> BTB //│ ty_1_2: (nothing -> BTB) -> BTB ty_1_2 = ty_1 //│ (nothing -> 'a) -> 'a //│ <: ty_1_2: //│ (nothing -> BTB) -> BTB ty_2 = ty_1_2 //│ (nothing -> BTB) -> BTB //│ <: ty_2: //│ (forall 'c. 'c -> 'c -> 'c) -> BTB // * ========= Here the `forall 'a.` can't be distributed. ========= // * (Unless we somehow knew to widen the function to `nothing -> ...` and then distribute.) def ty_1: 'a -> (nothing -> 'a) -> 'a //│ ty_1: 'a -> (nothing -> 'a) -> 'a def ty_2: nothing -> (forall 'c. 'c -> 'c -> 'c) -> BTB //│ ty_2: nothing -> (forall 'c. 'c -> 'c -> 'c) -> BTB // * But this still works thanks to `:DistributeForalls`, distributing the RHS (`forall 'b` out of BTB) ty_2 = ty_1 //│ 'a -> (nothing -> 'a) -> 'a //│ <: ty_2: //│ nothing -> (forall 'c. 'c -> 'c -> 'c) -> BTB // * ========= Now without distributivity. ========= :DontDistributeForalls // * This fails to constrain, as expected :e ty_2 = ty_1 //│ 'a -> (nothing -> 'a) -> 'a //│ <: ty_2: //│ nothing -> (forall 'c. 'c -> 'c -> 'c) -> BTB //│ ╔══[ERROR] Type error in def definition //│ ║ l.68: ty_2 = ty_1 //│ ║ ^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.7: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.7: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.68: ty_2 = ty_1 //│ ╙── ^^^^ // * We can manually take ty_1 closer to ty_2 def ty_1_2: BTB -> (nothing -> BTB) -> BTB //│ ty_1_2: BTB -> (nothing -> BTB) -> BTB ty_1_2 = ty_1 //│ 'a -> (nothing -> 'a) -> 'a //│ <: ty_1_2: //│ BTB -> (nothing -> BTB) -> BTB // * But it still need distributivity for the inner comparison :e ty_2 = ty_1_2 //│ BTB -> (nothing -> BTB) -> BTB //│ <: ty_2: //│ nothing -> (forall 'c. 'c -> 'c -> 'c) -> BTB //│ ╔══[ERROR] Type error in def definition //│ ║ l.99: ty_2 = ty_1_2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.7: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.7: type BTB = forall 'b. 'b -> 'b //│ ╙── ^^ // * Note def ty_1_3: BTB -> (nothing -> anything -> nothing) -> BTB //│ ty_1_3: BTB -> (nothing -> anything -> nothing) -> BTB ty_1_3 = ty_1_2 //│ BTB -> (nothing -> BTB) -> BTB //│ <: ty_1_3: //│ BTB -> (nothing -> anything -> nothing) -> BTB // * Again, with distrib. :DistributeForalls ty_2 = ty_1_2 //│ BTB -> (nothing -> BTB) -> BTB //│ <: ty_2: //│ nothing -> (forall 'c. 'c -> 'c -> 'c) -> BTB // * ========= Some additional semi-random tests. ========= def ty_0: nothing -> BTB //│ ty_0: nothing -> BTB ty_0 = id //│ 'a -> 'a //│ <: ty_0: //│ nothing -> BTB ty_0 = (fun x -> fun y -> y) //│ anything -> 'a -> 'a //│ <: ty_0: //│ nothing -> BTB idid = id (fun x -> id (fun y -> id y)) //│ idid: anything -> 'a -> 'a def foo: 'a -> 'a -> 'a //│ foo: 'a -> 'a -> 'a idid x = foo (x (fun y -> y)) //│ idid: ((forall 'b. 'b -> 'b) -> 'a) -> 'a -> 'a ty_0 = idid //│ ((forall 'b. 'b -> 'b) -> 'a) -> 'a -> 'a //│ <: ty_0: //│ nothing -> BTB // ∀𝛼{𝛽≤𝛼}.𝛼→𝛼 ≤ ⊤→(∀𝛾.𝛾→𝛾) ty_0 = idid id //│ 'a -> ('b -> 'b | 'a) //│ <: ty_0: //│ nothing -> BTB def idid = let x = (fun y -> y) : 'a in id : 'a -> 'a //│ idid: 'a -> 'a ty_0 = idid id //│ 'a -> 'a //│ <: ty_0: //│ nothing -> BTB ================================================ FILE: shared/src/test/diff/fcp/DistribRight.mls ================================================ // * Variations/minimizations on the case found in the Fiota test // * These tests exercise problems that could arise from distributing // * RHS functions with polymorphic codomain. S x = x x //│ S: ('a -> 'b & 'a) -> 'b //│ = [Function: S] S: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: S] S x y = x //│ S: 'a -> anything -> 'a //│ = [Function: S1] S: (forall 'a. 'a -> 'a) -> anything -> (forall 'c. 'c -> 'c) //│ res: (forall 'a. 'a -> 'a) -> anything -> (forall 'c. 'c -> 'c) //│ = [Function: S1] S: 'c -> 'b -> (forall 'a. 'a -> 'a) //│ res: (??a -> ??a0) -> anything -> (forall 'a. 'a -> 'a) //│ = [Function: S1] S: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1) //│ = [Function: S1] S x y = x y //│ S: ('a -> 'b) -> 'a -> 'b //│ = [Function: S2] S: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1) //│ = [Function: S2] S x y z = x z y //│ S: ('a -> 'b -> 'c) -> 'b -> 'a -> 'c //│ = [Function: S3] S: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1) -> (forall 'a2. 'a2 -> 'a2) //│ = [Function: S3] :NoJS def T: 'a -> 'a -> anything -> 'a //│ T: 'a -> 'a -> anything -> 'a T: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1) -> (forall 'a2. 'a2 -> 'a2) // * This one works out because we currently DO NOT distribute out when both sides of // * the constraint are plain function types: def T: ('a, 'a -> 'a) //│ T: ('a, 'a -> 'a,) T: (forall 'a. 'a -> 'a, (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a)) //│ res: (forall 'a. 'a -> 'a, (forall 'a0. 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1),) // * However, we can contort things enough so that we end up forcing the distribution, // * and causing a problematic extrusion/avoidance: def T: ('a, forall 'b. ('a, 'b) -> 'a) //│ T: ('a, ('a, anything,) -> 'a,) :e T: (anything, (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a)) //│ ╔══[ERROR] Type error in type ascription //│ ║ l.80: T: (anything, (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a)) //│ ║ ^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.80: T: (anything, (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a)) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.80: T: (anything, (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.80: T: (anything, (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a)) //│ ╙── ^ //│ res: (anything, (forall 'a. 'a -> 'a, anything,) -> (forall 'a0. 'a0 -> 'a0),) // * Similarly: def T: 'a -> (forall 'b. ('a, 'b) -> 'a) //│ T: 'a -> ('a, anything,) -> 'a :e T: anything -> (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.103: T: anything -> (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a) //│ ║ ^ //│ ╟── type `anything` is not a function //│ ║ l.103: T: anything -> (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.103: T: anything -> (forall 'a. 'a -> 'a, anything) -> (forall 'a. 'a -> 'a) //│ ╙── ^^^^^^^^ //│ res: anything -> (forall 'a. 'a -> 'a, anything,) -> (forall 'a0. 'a0 -> 'a0) ================================================ FILE: shared/src/test/diff/fcp/DistribUnionInter.mls ================================================ // * This test demonstrates how unions and intersections can interfere withb FCP type inference def foo: forall 'a 'b. 'a | 1 -> 'b -> 'b //│ foo: 1 -> 'b -> 'b //│ = foo //│ res: 1 -> 'b -> 'b //│ = //│ foo is not implemented def bar(f: 1 -> ('d | (forall 'c. 'c -> 'c))) = 1 //│ bar: (1 -> anything) -> 1 //│ = [Function: bar] // * Still works because we distribute from the right bar foo //│ res: 1 //│ = //│ foo is not implemented def bar(f: 1 -> ('d & (forall 'c. 'c -> 'c))) = 1 //│ bar: (1 -> (forall 'c. 'c -> 'c)) -> 1 //│ = [Function: bar1] bar //│ res: (1 -> (forall 'c. 'c -> 'c)) -> 1 //│ = [Function: bar1] // * Doesn't works because we are forced into DNF constraining where no distrib is currently done :e bar foo //│ ╔══[ERROR] Type error in application //│ ║ l.34: bar foo //│ ║ ^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.24: def bar(f: 1 -> ('d & (forall 'c. 'c -> 'c))) = 1 //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.24: def bar(f: 1 -> ('d & (forall 'c. 'c -> 'c))) = 1 //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.34: bar foo //│ ║ ^^^ //│ ╟── • this function: //│ ║ l.24: def bar(f: 1 -> ('d & (forall 'c. 'c -> 'c))) = 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.34: bar foo //│ ╙── ^^^ //│ res: 1 | error //│ = //│ foo is not implemented ================================================ FILE: shared/src/test/diff/fcp/DistribWorsening.mls ================================================ :NoRecursiveTypes :DistributeForalls :NoJS // * Test that exposes worseining of type inference due to eager distributivity // * (compared to no distributivity at all) type BTB = forall 'b. 'b -> 'b //│ Defined type alias BTB // * OK (fun g -> fun x -> x) : anything -> BTB //│ res: anything -> BTB // * OK def test f = f id : anything -> BTB test (fun g -> fun n -> id) //│ test: ((forall 'a. 'a -> 'a) -> anything -> BTB) -> anything -> BTB //│ res: anything -> BTB // * OK def test f = (fun a -> a : anything -> BTB) (f id) test (fun g -> fun n -> id) //│ test: ((forall 'a. 'a -> 'a) -> anything -> BTB) -> anything -> BTB //│ res: anything -> BTB // * OK def test f = (fun () -> f id)() : anything -> BTB test (fun g -> fun n -> id) //│ test: ((forall 'a. 'a -> 'a) -> anything -> BTB) -> anything -> BTB //│ res: anything -> BTB // * NOT OK: Notice the extruded skolem in `??b -> ??b0` whichb results from distributing 'b out of BTB def test f = let a = f() in a : anything -> BTB //│ test: (() -> anything -> ??b -> ??b0) -> anything -> BTB :e test (fun () -> fun n -> id) //│ ╔══[ERROR] Type error in application //│ ║ l.52: test (fun () -> fun n -> id) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.11: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.11: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.47: let a = f() in //│ ║ ^^^ //│ ╟── • this reference: //│ ║ l.48: a : anything -> BTB //│ ╙── ^ //│ res: error | anything -> BTB // * Idem def test f = let a() = f id in a() : nothing -> BTB //│ test: ((forall 'a. 'a -> 'a) -> nothing -> ??b -> ??b0) -> nothing -> BTB :e test (fun g -> fun n -> id) //│ ╔══[ERROR] Type error in application //│ ║ l.79: test (fun g -> fun n -> id) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.11: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.11: type BTB = forall 'b. 'b -> 'b //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.74: let a() = f id in //│ ║ ^^^^ //│ ╟── • this application: //│ ║ l.75: a() : nothing -> BTB //│ ╙── ^^^ //│ res: error | nothing -> BTB :DontDistributeForalls // * There is no longer an extruded skolem def test f = let a = f() in a : anything -> BTB //│ test: (() -> anything -> BTB) -> anything -> BTB test (fun () -> fun n -> id) //│ res: anything -> BTB // * Idem def test f = let a = f id in a : nothing -> BTB //│ test: ((forall 'a. 'a -> 'a) -> nothing -> BTB) -> nothing -> BTB test (fun g -> fun n -> id) //│ res: nothing -> BTB ================================================ FILE: shared/src/test/diff/fcp/FCPTony.mls ================================================ :NoRecursiveTypes :ConstrainedTypes def app n f = n f //│ app: 'a -> ('b -> 'c //│ where //│ 'a <: 'b -> 'c) //│ = [Function: app] def g x y = app x y //│ g: 'a -> ('b -> 'c //│ where //│ 'a <: 'b -> 'c) //│ = [Function: g] :e g {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.19: g {} {} //│ ║ ^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.19: g {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.6: def app n f = n f //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function // ———————————————————————— def app p q = p q //│ app: 'a -> ('b -> 'c //│ where //│ 'a <: 'b -> 'c) //│ = [Function: app1] def mul r s = r (app s) id //│ mul: 'a -> ('b -> 'c //│ where //│ 'a <: (forall 'd 'e. 'd -> 'e //│ where //│ 'b <: 'd -> 'e) -> (forall 'f. 'f -> 'f) -> 'c) //│ = [Function: mul] :e mul id {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.53: mul id {} //│ ║ ^^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.53: mul id {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.38: def app p q = p q //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.44: def mul r s = r (app s) id //│ ╙── ^ //│ res: error //│ Runtime error: //│ TypeError: p is not a function def fact t = mul t {} //│ fact: ((forall 'a 'b. 'a -> 'b //│ where //│ anything <: 'a -> 'b) -> (forall 'c. 'c -> 'c) -> 'd) -> 'd //│ = [Function: fact] :e fact id //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.77: fact id //│ ║ ^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.70: def fact t = mul t {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.38: def app p q = p q //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: p is not a function def mul r s = r (app s) //│ mul: 'a -> ('b -> 'c //│ where //│ 'a <: (forall 'd 'e. 'd -> 'e //│ where //│ 'b <: 'd -> 'e) -> 'c) //│ = [Function: mul1] mul id id {} //│ res: anything //│ = {} // ———————————————————————— :GeneralizeArguments :DistributeForalls type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias ChurchInt def succ: ChurchInt -> ChurchInt def succ n f x = f (n f x) //│ succ: ChurchInt -> ChurchInt //│ = //│ 'a -> 'b -> ('c -> 'd //│ where //│ 'b <: (forall 'e. 'e //│ where //│ 'a <: 'b -> 'c -> 'e) -> 'd) //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: succ1] :ns def add n m = n succ m //│ add: forall 'a. 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ 'a <: (ChurchInt -> ChurchInt) -> 'c) //│ where //│ 'c <: 'b -> 'd //│ = [Function: add] add //│ res: 'a -> ('b -> 'c //│ where //│ 'a <: (ChurchInt -> ChurchInt) -> 'b -> 'c) //│ = [Function: add] :ns def f x y = add x y //│ f: forall 'a. 'a -> (forall 'b 'c 'd 'e 'f. 'b -> 'c //│ where //│ 'a <: (ChurchInt -> ChurchInt) -> 'd) //│ where //│ 'b <: 'e //│ 'd <: 'e -> 'f //│ 'f <: 'c //│ = [Function: f] // :ds f //│ res: 'a -> ('b -> 'c //│ where //│ 'a <: (ChurchInt -> ChurchInt) -> 'b -> 'c) //│ = [Function: f] :e f {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.162: f {} {} //│ ║ ^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.162: f {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.129: def add n m = n succ m //│ ╙── ^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function ================================================ FILE: shared/src/test/diff/fcp/FCPTony3.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :NoCycleCheck // * ========= Minimized: ========= const a b = a // const x y = y //│ const: 'a -> anything -> 'a //│ = [Function: const1] def id2: 'A -> 'A id2 = id //│ id2: 'A -> 'A //│ = //│ 'a -> 'a //│ <: id2: //│ 'A -> 'A //│ = [Function: id] def f y = (fun h -> h (h (h (const y)))) id2 //│ f: 'a -> anything -> 'a //│ = [Function: f] // * This used to be accepted! :e f {} {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.28: f {} {} {} //│ ║ ^^^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.28: f {} {} {} //│ ║ ^^ //│ ╟── but it flows into application with expected type `anything -> ?a` //│ ║ l.28: f {} {} {} //│ ╙── ^^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: f(...)(...) is not a function // * ========= Original: ========= def f y = (fun h -> h (h (h (fun _ -> y)))) id {} //│ f: 'a -> 'a //│ = [Function: f1] :e f {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.50: f {} {} //│ ║ ^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.50: f {} {} //│ ║ ^^ //│ ╟── but it flows into application with expected type `anything -> ?a` //│ ║ l.50: f {} {} //│ ╙── ^^^^ //│ res: error //│ Runtime error: //│ TypeError: f1(...) is not a function ================================================ FILE: shared/src/test/diff/fcp/Fiota.mls ================================================ :NoRecursiveTypes // On the Power of Coercion Abstraction S x y z = x z (y z) //│ S: ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c //│ = [Function: S] type S1 = ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c //│ Defined type alias S1 type S2 = (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ Defined type alias S2 s1 = S: S1 //│ s1: S1 //│ = [Function: S] s1: S2 //│ res: S2 //│ = [Function: S] s2 = S: S2 //│ s2: S2 //│ = [Function: S] :e // * S2 is less genetral than S1 s2: S1 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.30: s2: S1 //│ ║ ^^ //│ ╟── type `'a` does not match type `'a0` //│ ║ l.14: type S2 = (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.11: type S1 = ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.14: type S2 = (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╙── ^^ //│ res: S1 //│ = [Function: S] type Id = forall 'a. 'a -> 'a //│ Defined type alias Id :e // TODO: could probably use different type var shadows from different type alias occurrences S: Id -> Id -> Id -> Id //│ ╔══[ERROR] Cyclic-looking constraint while typing type ascription; a type annotation may be required //│ ║ l.52: S: Id -> Id -> Id -> Id //│ ║ ^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: Id -> Id -> Id -> Id //│ = [Function: S] ================================================ FILE: shared/src/test/diff/fcp/ForallTerms.mls ================================================ :NoRecursiveTypes (forall 'A. fun (x: 'A) -> x) : forall 'X. 'X -> 'X //│ res: 'X -> 'X //│ = [Function: res] f = forall 'A. fun (x: 'A) -> x //│ f: 'A -> 'A //│ = [Function: f] f 0 f true f: forall 'X. 'X -> 'X //│ res: 0 //│ = 0 //│ res: true //│ = true //│ res: 'X -> 'X //│ = [Function: f] r = { f = forall 'A. fun (x: 'A) -> x } //│ r: {f: forall 'A. 'A -> 'A} //│ = { f: [Function: f] } r.f 0 r.f true //│ res: 0 //│ = 0 //│ res: true //│ = true def k1(f: forall 'X. 'X -> 'X) = (f 0, f true) //│ k1: (forall 'X. 'X -> 'X) -> (0, true,) //│ = [Function: k1] k1 f //│ res: (0, true,) //│ = [ 0, true ] k1 r.f //│ res: (0, true,) //│ = [ 0, true ] def k2(f) = (f 0, f true) //│ k2: (0 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: k2] k2 f //│ res: (0, true,) //│ = [ 0, true ] k2 r.f //│ res: (0, true,) //│ = [ 0, true ] // * Interesting trick: can use an empty `forall` to force generalization of curried lambdas: :ns f = fun x -> fun y -> (x, y) g = fun x -> forall. fun y -> (x, y) //│ f: forall 'a 'b. 'a -> 'b -> ('a, 'b,) //│ = [Function: f1] //│ g: forall 'a. 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ = [Function: g] // * They still appear the same after simplification, due to distributivity of `forall` over `->`: f g //│ res: 'a -> 'b -> ('a, 'b,) //│ = [Function: f1] //│ res: 'a -> 'b -> ('a, 'b,) //│ = [Function: g] // * Explicitly-bound type variables are rigid (like in OCaml) :e f = forall 'A. fun (x: 'A) -> x + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.84: f = forall 'A. fun (x: 'A) -> x + 1 //│ ║ ^^^ //│ ╟── rigid type variable of type `'A` is not an instance of type `int` //│ ║ l.84: f = forall 'A. fun (x: 'A) -> x + 1 //│ ║ ^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.84: f = forall 'A. fun (x: 'A) -> x + 1 //│ ╙── ^ //│ f: anything -> (error | int) //│ = [Function: f2] ================================================ FILE: shared/src/test/diff/fcp/FunnyId.mls ================================================ :NoRecursiveTypes def id_ty: forall 'a. 'a -> 'a //│ id_ty: 'a -> 'a //│ = rec def id x = let tmp = id x in x //│ id: 'a -> 'a //│ = [Function: id] :re id 1 //│ res: 1 //│ Runtime error: //│ RangeError: Maximum call stack size exceeded id_ty = id //│ 'a -> 'a //│ <: id_ty: //│ 'a -> 'a //│ = [Function: id] // * When we had the "recursive definition hacks", and the reason was: // * "we get `'a <: 'a -> anything` because `id` is passed for `x` // * and we can't infer polymorphic recursion, so all `x` should have the same type" :e // occurs-check rec def id x = let tmp = id id x in x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> ? //│ 'b :> 'b -> 'b //│ <: ? & 'a //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^^^^^ //│ id: 'a -> 'a //│ where //│ 'a :> 'a -> 'a //│ <: 'a -> anything //│ = [Function: id1] :e :re id 1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> ? //│ 'b :> 1 | 'b -> 'b //│ <: ? & 'a //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.46: id 1 //│ ║ ^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.46: id 1 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ║ ^^^^^^^ //│ ╟── from reference: //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^ //│ res: 1 | error | 'a -> 'a | 'b //│ where //│ 'a :> 1 | 'a -> 'a //│ <: 'a -> anything & 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e id_ty = id //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> ? //│ 'b :> 'b -> 'b //│ <: ? & 'a //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ╙── ^^^^^ //│ 'a -> 'a //│ where //│ 'a :> 'a -> 'a //│ <: 'a -> anything //│ <: id_ty: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.74: id_ty = id //│ ║ ^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ║ ^^^^^^^ //│ ╟── from reference: //│ ║ l.30: rec def id x = let tmp = id id x in x //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a //│ ╙── ^^ //│ = [Function: id1] // * Note: the above seem to work with :precise-rec-typing :precise-rec-typing rec def id x = let tmp = id id x in x //│ id: 'a -> 'a //│ = [Function: id2] :re id 1 //│ res: 1 //│ Runtime error: //│ RangeError: Maximum call stack size exceeded id_ty = id //│ 'a -> 'a //│ <: id_ty: //│ 'a -> 'a //│ = [Function: id2] // * Not sure why we get a cycle in this slight variation! :e rec def id x = if true then x else id id x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.126: rec def id x = if true then x else id id x //│ ╙── ^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required //│ ║ l.126: rec def id x = if true then x else id id x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ id: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id3] :e id_ty = id //│ ╔══[ERROR] Inferred recursive type: 'b //│ where //│ 'b :> 'c -> 'b | 'a //│ <: 'c -> 'a & 'a //│ 'c :> 'c -> 'b | 'a //│ <: 'a //│ 'a <: 'b //│ ║ l.126: rec def id x = if true then x else id id x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ <: id_ty: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.148: id_ty = id //│ ║ ^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.126: rec def id x = if true then x else id id x //│ ║ ^^^^^^^ //│ ╟── from reference: //│ ║ l.126: rec def id x = if true then x else id id x //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.4: def id_ty: forall 'a. 'a -> 'a //│ ╙── ^^ //│ = [Function: id3] :RecursiveTypes :NoJS def choose: 'a -> 'a -> 'a //│ choose: 'a -> 'a -> 'a rec def id1 x = choose x (id1 id1 x) //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b id1 id //│ res: ('a & 'b) -> 'a //│ where //│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> ('a | 'd) //│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a //│ 'c :> 'c -> 'd //│ <: 'd //│ 'd := 'c -> 'd rec def id1 x = if true then x else id1 id1 x //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b id1 id //│ res: ('a & 'b) -> 'a //│ where //│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> ('a | 'd) //│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a //│ 'c :> 'c -> 'd //│ <: 'd //│ 'd := 'c -> 'd ================================================ FILE: shared/src/test/diff/fcp/Inst.mls ================================================ :NoRecursiveTypes i = id! id! //│ i: 'a -> 'a //│ = [Function: id] i i! i 1 //│ res: 1 //│ = 1 :NoJS { x = id } //│ res: {x: forall 'a. 'a -> 'a} { x = id! } //│ res: {x: 'a -> 'a} { x = id id! } //│ res: {x: 'a -> 'a} { x = id! id } //│ res: {x: forall 'a. 'a -> 'a} { x = id! id! } //│ res: {x: 'a -> 'a} :w def foo x = x! //│ ╔══[WARNING] Inferred type `?a` of this reference cannot be instantiated //│ ║ l.32: def foo x = x! //│ ╙── ^ //│ foo: 'a -> 'a type Id = forall 'a. 'a -> 'a //│ Defined type alias Id def foo (x: Id) = x! //│ foo: Id -> 'a -> 'a def foo (x: Id) = x! x! //│ foo: Id -> 'a -> 'a def foo (x: Id) = x! x //│ foo: Id -> Id def foo (x: Id) = x x! //│ foo: Id -> 'a -> 'a def foo (x: Id) = x x //│ foo: Id -> Id // * Note that `this` selections currently don't provide a concrete type in method typing :w class A: { f: Id } method F x = x method G1 = this.f method G2 = this.f! method G3 = (this.f: Id)! //│ ╔══[WARNING] Inferred type `?f` of this field selection cannot be instantiated //│ ║ l.64: method G2 = this.f! //│ ╙── ^^^^^^ //│ Defined class A //│ Defined A.F: A -> 'a -> 'a //│ Defined A.G1: A -> Id //│ Defined A.G2: A -> Id //│ Defined A.G3: A -> 'a -> 'a ================================================ FILE: shared/src/test/diff/fcp/InvariantPolyContainer.mls ================================================ :NoRecursiveTypes :NoJS def single: 'a -> MutArray['a] //│ single: 'a -> MutArray['a] sid = single id //│ sid: MutArray['a] //│ where //│ 'a :> forall 'b. 'b -> 'b def ty1: MutArray[forall 'X. 'X -> 'X] def ty1 = sid //│ ty1: MutArray[forall 'X. 'X -> 'X] //│ MutArray['a] //│ where //│ 'a :> forall 'b. 'b -> 'b //│ <: ty1: //│ MutArray[forall 'X. 'X -> 'X] def ty2: forall 'A. MutArray['A -> 'A] def ty2 = sid //│ ty2: MutArray['A -> 'A] //│ MutArray['a] //│ where //│ 'a :> forall 'b. 'b -> 'b //│ <: ty2: //│ MutArray['A -> 'A] :e ty1 = ty2 //│ MutArray['A -> 'A] //│ <: ty1: //│ MutArray[forall 'X. 'X -> 'X] //│ ╔══[ERROR] Type error in def definition //│ ║ l.32: ty1 = ty2 //│ ║ ^^^^^^^^^ //│ ╟── type variable `'X` leaks out of its scope //│ ║ l.13: def ty1: MutArray[forall 'X. 'X -> 'X] //│ ║ ^^ //│ ╟── back into type variable `'X` //│ ║ l.13: def ty1: MutArray[forall 'X. 'X -> 'X] //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.32: ty1 = ty2 //│ ╙── ^^^ :e ty2 = ty1 //│ MutArray[forall 'X. 'X -> 'X] //│ <: ty2: //│ MutArray['A -> 'A] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.51: ty2 = ty1 //│ ║ ^^^^^^^^^ //│ ╟── type `'X` does not match type `'A` //│ ║ l.13: def ty1: MutArray[forall 'X. 'X -> 'X] //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.22: def ty2: forall 'A. MutArray['A -> 'A] //│ ║ ^^ //│ ╟── Note: quantified type variable 'X is defined at: //│ ║ l.13: def ty1: MutArray[forall 'X. 'X -> 'X] //│ ╙── ^^ ================================================ FILE: shared/src/test/diff/fcp/ListBuild.mls ================================================ :NoRecursiveTypes class Ls[A] method HeadTail: (A, Ls[A]) | undefined //│ Defined class Ls[+A] //│ Declared Ls.HeadTail: Ls['A] -> (undefined | ('A, Ls['A],)) // * Note that a more structural type such as this will raise cycle errors due to `:NoRecursiveTypes` // class Ls[A]: { head: A | undefined; tail: Ls[A] | undefined } class Nil: Ls[nothing] method HeadTail = undefined //│ Defined class Nil //│ Defined Nil.HeadTail: Nil -> undefined class Cons[A]: Ls[A] & { head: A; tail: Ls[A] } method HeadTail = (this.head, this.tail) //│ Defined class Cons[+A] //│ Defined Cons.HeadTail: Cons['A] -> ('A, Ls['A],) def nil: Ls['a] def cons: ('a, Ls['a]) -> Ls['a] def single: 'a -> Ls['a] //│ nil: Ls[nothing] //│ = //│ cons: ('a, Ls['a],) -> Ls['a] //│ = //│ single: 'a -> Ls['a] //│ = nil = Nil {} cons (head, tail) = Cons { head; tail } //│ Nil //│ <: nil: //│ Ls[nothing] //│ = Nil {} //│ ('head & 'A, Ls['A] & 'tail,) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ <: cons: //│ ('a, Ls['a],) -> Ls['a] //│ = [Function: cons] // * Random minimized trials: def build0 (g: forall 'b. ('a -> 'b) -> 'b) = g single //│ build0: (forall 'b. ('a -> 'b) -> 'b) -> Ls['a] //│ = //│ single is not implemented build0 (fun s -> s 1) //│ res: Ls[1] //│ = //│ build0 and single are not implemented res: Ls[int] //│ res: Ls[int] //│ = undefined def build0 (g: forall 'b. ('a -> 'b) -> 'b) = g (fun x -> single((x, x))) //│ build0: (forall 'b. ('a -> 'b) -> 'b) -> Ls[('a, 'a,)] //│ = //│ single is not implemented def build0 (g: forall 'b. ('a -> 'a -> 'b) -> 'b) = g (fun x -> fun y -> single((x, y))) //│ build0: (forall 'b. ('a -> 'a -> 'b) -> 'b) -> Ls[('a, 'a,)] //│ = //│ single is not implemented :e // * This is recursive because we place the list-typed value inside a new list along with the head. def build0 (g: forall 'b. ('a -> 'b -> 'b) -> 'b) = g (fun x -> fun y -> single((x, y))) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> Ls[(?, forall 'a. 'a,)] //│ ║ l.74: def build0 (g: forall 'b. ('a -> 'b -> 'b) -> 'b) = g (fun x -> fun y -> single((x, y))) //│ ╙── ^^^^^^^^^^^^^^ //│ build0: (forall 'b. ('a -> 'b -> 'b) -> 'b) -> 'c //│ where //│ 'c :> Ls[('a, forall 'c. 'c,)] //│ = //│ single is not implemented def build0 (g: forall 'b. (('a, 'b) -> 'b) -> 'b) = g cons //│ build0: (forall 'b. (('a, 'b,) -> 'b) -> 'b) -> Ls['a] //│ = [Function: build01] def build0 (g: forall 'b. (('a, 'b) -> 'b) -> 'b) = g (fun (x, xs) -> cons (x, xs)) //│ build0: (forall 'b. (('a, 'b,) -> 'b) -> 'b) -> Ls['a] //│ = [Function: build02] def g: forall 'b. (('a, 'b) -> 'b) -> 'b //│ g: ((nothing, 'b,) -> 'b) -> 'b //│ = g (fun (x, xs) -> cons (x, xs)) //│ res: Ls[nothing] //│ = //│ g is not implemented def g: ((int, 'b) -> 'b) -> 'b //│ g: ((int, 'b,) -> 'b) -> 'b //│ = g (fun (x, xs) -> cons (x, xs)) //│ res: Ls[int] //│ = //│ g is not implemented def cons2: ('a, {x:'a}) -> {x:'a} // def cons2: ('a, 'a) -> 'a //│ cons2: ('a, {x: 'a},) -> {x: 'a} //│ = f (x, xs) = cons2 (x, xs) //│ f: ('a, {x: 'a},) -> {x: 'a} //│ = //│ cons2 is not implemented g f //│ res: {x: int} //│ = //│ g is not implemented def build0 (g: forall 'b. ('a -> 'b -> 'b) -> 'b) = g (fun x -> fun xs -> cons (x, xs)) //│ build0: (forall 'b. ('a -> 'b -> 'b) -> 'b) -> Ls['a] //│ = [Function: build03] :re build0 (fun k -> k 1 error) //│ res: Ls[1] //│ Runtime error: //│ Error: an error was thrown // * The Real Deal: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> cons (x, xs)) nil //│ build: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: build] xs0 = build (fun c -> fun n -> n) //│ xs0: Ls[nothing] //│ = Nil {} xs1 = build (fun c -> fun n -> c 1 n) //│ xs1: Ls[1] //│ = Cons { head: 1, tail: Nil {} } xs3 = build (fun c -> fun n -> c 1 (c 2 (c 3 n))) //│ xs3: Ls[1 | 2 | 3] //│ = Cons { //│ head: 1, //│ tail: Cons { head: 2, tail: Cons { head: 3, tail: Nil {} } } //│ } build: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ res: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: build] def build_ty: forall 'a. (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ build_ty: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = build_ty = build_ty //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ <: build_ty: //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = //│ build_ty is not implemented build: forall 'a. (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ res: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: build] // * This `build` type is _too_ general as it lets the continuation assume it will handle lists instead of some unknown 'b :e build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.186: build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ ║ ^^^^^ //│ ╟── type `Ls['a]` does not match type `'b` //│ ║ l.186: build: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.143: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> cons (x, xs)) nil //│ ╙── ^^ //│ res: (('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a]) -> Ls['a] //│ = [Function: build] // * It also Just Works without type annotations! def build_ = fun g -> g (fun x -> fun xs -> cons (x, xs)) nil //│ build_: ((forall 'a. 'a -> Ls['a] -> Ls['a]) -> Ls[nothing] -> 'b) -> 'b //│ = [Function: build_] build_ (fun c -> fun n -> n) //│ res: Ls[nothing] //│ = Nil {} build_ (fun c -> fun n -> c 1 n) //│ res: Ls[1] //│ = Cons { head: 1, tail: Nil {} } build_ (fun c -> fun n -> c 1 (c 2 (c 3 n))) //│ res: Ls[1 | 2 | 3] //│ = Cons { //│ head: 1, //│ tail: Cons { head: 2, tail: Cons { head: 3, tail: Nil {} } } //│ } def g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b //│ g: (nothing -> 'b -> 'b) -> 'b -> 'b //│ = g : ('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a] //│ res: ('a -> Ls['a] -> Ls['a]) -> Ls['a] -> Ls['a] //│ = //│ g is not implemented build_ : (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ res: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: build_] def b: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ b: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = b = build_ //│ ((forall 'a. 'a -> Ls['a] -> Ls['a]) -> Ls[nothing] -> 'b) -> 'b //│ <: b: //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: build_] b g = build_ g //│ ((forall 'a. 'a -> Ls['a] -> Ls['a]) -> Ls[nothing] -> 'b) -> 'b //│ <: b: //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: b1] b g = build_ (fun x -> g x) //│ ((forall 'a. 'a -> Ls['a] -> Ls['a]) -> Ls[nothing] -> 'b) -> 'b //│ <: b: //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = [Function: b2] :e b g x = build_ g x //│ ((forall 'a. 'a -> Ls['a] -> Ls['a]) -> Ls[nothing] -> 'b -> 'c) -> 'b -> 'c //│ <: b: //│ (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.263: b g x = build_ g x //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `Ls['a]` is not a function //│ ║ l.22: def nil: Ls['a] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.263: b g x = build_ g x //│ ║ ^^^^^^^^^^ //│ ╟── from application: //│ ║ l.204: def build_ = fun g -> g (fun x -> fun xs -> cons (x, xs)) nil //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ = [Function: b3] rec def fold f z xs = let ht = xs.HeadTail in case ht of { | undefined -> z | _ -> (fun ((h, t)) -> f h (fold f z t)) ht } //│ fold: ('a -> 'b -> 'b) -> 'b -> Ls['a] -> 'b //│ = [Function: fold] fold add 0 xs0 //│ res: int //│ = 0 fold add 0 xs1 //│ res: int //│ = 1 fold add 0 xs3 //│ res: int //│ = 6 ================================================ FILE: shared/src/test/diff/fcp/Min1_ex_shallow.mls ================================================ :NoRecursiveTypes type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid type A1' = forall 'a. (('a | Sid) -> 'a) -> anything // type A1' = (Sid -> Sid) -> Sid -> Sid //│ Defined type alias A1' def a1' : ((forall 'a. 'a -> 'a) -> anything) -> nothing //│ a1': ((forall 'a. 'a -> 'a) -> anything) -> nothing //│ = // * Fails due to premature LHS instantiation (TODO improve) // :d :e a1' : A1' //│ ╔══[ERROR] Type error in type ascription //│ ║ l.18: a1' : A1' //│ ║ ^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.4: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.4: type Sid = forall 'a. 'a -> 'a //│ ╙── ^^ //│ res: A1' //│ = //│ a1' is not implemented def a2' (x: A1') = 0 //│ a2': A1' -> 0 //│ = [Function: a2$] :e def a0' = a2' a1' //│ ╔══[ERROR] Type error in application //│ ║ l.37: def a0' = a2' a1' //│ ║ ^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.4: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.4: type Sid = forall 'a. 'a -> 'a //│ ╙── ^^ //│ a0': 0 | error //│ = //│ a1' is not implemented ================================================ FILE: shared/src/test/diff/fcp/MoreChurch.mls ================================================ :DistributeForalls :NoRecursiveTypes // :GeneralizeArguments // * This actually causes extrusions in pred, failing its type checking type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias ChurchInt def zero_ty: ChurchInt //│ zero_ty: ChurchInt //│ = def zero f x = x //│ zero: anything -> 'a -> 'a //│ = [Function: zero] zero_ty = zero //│ anything -> 'a -> 'a //│ <: zero_ty: //│ ChurchInt //│ = [Function: zero] def succ_ty: ChurchInt -> ChurchInt //│ succ_ty: ChurchInt -> ChurchInt //│ = def succ n f x = f (n f x) //│ succ: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: succ] succ_ty = succ //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ = [Function: succ] def to_church_ty: int -> ChurchInt //│ to_church_ty: int -> ChurchInt //│ = rec def to_church n = if n == 0 then zero else succ (to_church (n - 1)) //│ to_church: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_church] to_church_ty = to_church //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church] def add_ty: ChurchInt -> ChurchInt -> ChurchInt //│ add_ty: ChurchInt -> ChurchInt -> ChurchInt //│ = def add n m = n succ m //│ add: ((forall 'a 'b 'c 'd. ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd) -> 'e -> 'f) -> 'e -> 'f //│ = [Function: add] // * Note: cycle check fails when generalizing curried lambdas add_ty = add //│ ((forall 'a 'b 'c 'd. ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd) -> 'e -> 'f) -> 'e -> 'f //│ <: add_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: add] def add (n: ChurchInt) m = n succ m // def add (n: ChurchInt) m = n succ_ty m //│ add: ChurchInt -> ('a -> 'b -> 'c & 'd) -> (('c -> ('c & 'e) & 'a) -> 'b -> 'e | 'd) //│ = [Function: add1] add_ty = add //│ ChurchInt -> ('a -> 'b -> 'c & 'd) -> (('c -> ('c & 'e) & 'a) -> 'b -> 'e | 'd) //│ <: add_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: add1] def mul_ty: ChurchInt -> ChurchInt -> ChurchInt //│ mul_ty: ChurchInt -> ChurchInt -> ChurchInt //│ = def mul n m = n (add m) zero //│ mul: ((forall 'a 'b 'c 'd 'e. ('a -> 'b -> 'c & 'd) -> (('c -> ('c & 'e) & 'a) -> 'b -> 'e | 'd)) -> (forall 'f. anything -> 'f -> 'f) -> 'g) -> ChurchInt -> 'g //│ = [Function: mul] // * Note: cycle check fails when generalizing curried lambdas mul_ty = mul //│ ((forall 'a 'b 'c 'd 'e. ('a -> 'b -> 'c & 'd) -> (('c -> ('c & 'e) & 'a) -> 'b -> 'e | 'd)) -> (forall 'f. anything -> 'f -> 'f) -> 'g) -> ChurchInt -> 'g //│ <: mul_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: mul] def mul (n: ChurchInt) m = n (add m) zero //│ mul: ChurchInt -> ChurchInt -> ('a -> ('a & 'b)) -> ('b & 'a) -> 'b //│ = [Function: mul1] mul_ty = mul //│ ChurchInt -> ChurchInt -> ('a -> ('a & 'b)) -> ('b & 'a) -> 'b //│ <: mul_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: mul1] def pow_ty: ChurchInt -> ChurchInt -> ChurchInt //│ pow_ty: ChurchInt -> ChurchInt -> ChurchInt //│ = :e def pow (n: ChurchInt) (m: ChurchInt) = n (mul m) (succ zero) //│ ╔══[ERROR] Type error in application //│ ║ l.120: def pow (n: ChurchInt) (m: ChurchInt) = n (mul m) (succ zero) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.7: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.7: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.31: def succ n f x = f (n f x) //│ ╙── ^ //│ pow: ChurchInt -> ChurchInt -> (error | (anything -> ??a & 'a -> 'a) -> (??a & 'a) -> anything) //│ = [Function: pow] def pow (n: ChurchInt) (m: ChurchInt) = n (mul_ty m) (succ_ty zero_ty) //│ pow: ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: pow1] pow_ty = pow //│ ChurchInt -> ChurchInt -> ChurchInt //│ <: pow_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: pow1] def pow (n: ChurchInt) m = n (mul m) (succ_ty zero) //│ pow: ChurchInt -> ChurchInt -> (('a -> ('a & 'b)) -> ('b & 'a) -> 'b | ChurchInt) //│ = [Function: pow2] pow_ty = pow //│ ChurchInt -> ChurchInt -> (('a -> ('a & 'b)) -> ('b & 'a) -> 'b | ChurchInt) //│ <: pow_ty: //│ ChurchInt -> ChurchInt -> ChurchInt //│ = [Function: pow2] def unit = id //│ unit: 'a -> 'a //│ = [Function: unit] type Unit_t = 'a -> 'a unit : Unit_t //│ Defined type alias Unit_t //│ res: Unit_t //│ = [Function: id] def tru x _ = x unit //│ tru: ((forall 'a. 'a -> 'a) -> 'b) -> anything -> 'b //│ = [Function: tru] def fls _ x = x unit //│ fls: anything -> ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: fls] type If_t = (Unit_t -> 'a) -> (Unit_t -> 'a) -> 'a tru : If_t fls : If_t //│ Defined type alias If_t //│ res: If_t //│ = [Function: tru] //│ res: If_t //│ = [Function: fls] def iszero n = n (fun _ -> fls) tru //│ iszero: ((forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> 'd) -> 'd //│ = [Function: iszero] iszero : ChurchInt -> If_t //│ res: ChurchInt -> If_t //│ = [Function: iszero] def pair x y f = f x y //│ pair: 'a -> 'b -> ('a -> 'b -> 'c) -> 'c //│ = [Function: pair] def fst p = p (fun x -> fun _ -> x) //│ fst: ((forall 'a. 'a -> anything -> 'a) -> 'b) -> 'b //│ = [Function: fst] def snd p = p (fun _ -> fun x -> x) //│ snd: ((forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function: snd] def pred_ty: ChurchInt -> ChurchInt //│ pred_ty: ChurchInt -> ChurchInt //│ = def s p = pair (snd p) (succ (snd p)) //│ s: ((forall 'a. anything -> 'a -> 'a) -> ('b -> 'c -> 'd & 'e)) -> ('e -> (('d -> 'f & 'b) -> 'c -> 'f) -> 'g) -> 'g //│ = [Function: s] def z = pair zero zero //│ z: ((forall 'a. anything -> 'a -> 'a) -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function: z] def pred (n: ChurchInt) = fst (n s z) //│ pred: ChurchInt -> ('a -> ('a & 'b)) -> ('b & 'a) -> 'b //│ = [Function: pred] pred_ty = pred //│ ChurchInt -> ('a -> ('a & 'b)) -> ('b & 'a) -> 'b //│ <: pred_ty: //│ ChurchInt -> ChurchInt //│ = [Function: pred] def pred n = let s p = pair (snd p) (succ (snd p)) in let z = pair zero zero in fst (n s z) //│ pred: ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('a -> 'b -> 'c & 'd)) -> ('d -> (('c -> 'e & 'a) -> 'b -> 'e) -> 'f) -> 'f) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k //│ = [Function: pred1] // * Note: cycle check fails when generalizing curried lambdas pred_ty = pred //│ ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('a -> 'b -> 'c & 'd)) -> ('d -> (('c -> 'e & 'a) -> 'b -> 'e) -> 'f) -> 'f) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k //│ <: pred_ty: //│ ChurchInt -> ChurchInt //│ = [Function: pred1] // * Only difference: use `succ_ty` def pred n = let s p = pair (snd p) (succ_ty (snd p)) in let z = pair zero zero in fst (n s z) //│ pred: ((forall 'a 'b. ((forall 'c. anything -> 'c -> 'c) -> (ChurchInt & 'a)) -> ('a -> ChurchInt -> 'b) -> 'b) -> (forall 'd. ((forall 'e. anything -> 'e -> 'e) -> (forall 'e. anything -> 'e -> 'e) -> 'd) -> 'd) -> (forall 'f. 'f -> anything -> 'f) -> 'g) -> 'g //│ = [Function: pred2] pred_ty = pred //│ ((forall 'a 'b. ((forall 'c. anything -> 'c -> 'c) -> (ChurchInt & 'a)) -> ('a -> ChurchInt -> 'b) -> 'b) -> (forall 'd. ((forall 'e. anything -> 'e -> 'e) -> (forall 'e. anything -> 'e -> 'e) -> 'd) -> 'd) -> (forall 'f. 'f -> anything -> 'f) -> 'g) -> 'g //│ <: pred_ty: //│ ChurchInt -> ChurchInt //│ = [Function: pred2] def fact_ty: ChurchInt -> ChurchInt //│ fact_ty: ChurchInt -> ChurchInt //│ = // * Note: cycle check fails when generalizing curried lambdas rec def fact (n: ChurchInt) = (iszero n) (fun _ -> succ zero) (fun _ -> mul n (fact (pred n))) //│ fact: ChurchInt -> ('a -> ('a & 'b) & 'c -> 'b) -> ('b & 'a & 'c) -> 'b //│ = [Function: fact] :precise-rec-typing rec def fact (n: ChurchInt) = (iszero n) (fun _ -> succ zero) (fun _ -> mul n (fact (pred n))) //│ fact: ChurchInt -> ('a -> ('a & 'b) & 'c -> 'b) -> ('b & 'a & 'c) -> 'b //│ = [Function: fact1] fact_ty = fact //│ ChurchInt -> ('a -> ('a & 'b) & 'c -> 'b) -> ('b & 'a & 'c) -> 'b //│ <: fact_ty: //│ ChurchInt -> ChurchInt //│ = [Function: fact1] // * A dummy mistake: :e def fact (n: ChurchInt) = (iszero n) (fun _ -> succ zero) (fun _ -> mul n {}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.279: def fact (n: ChurchInt) = (iszero n) (fun _ -> succ zero) (fun _ -> mul n {}) //│ ║ ^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.279: def fact (n: ChurchInt) = (iszero n) (fun _ -> succ zero) (fun _ -> mul n {}) //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.7: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.103: def mul (n: ChurchInt) m = n (add m) zero //│ ╙── ^ //│ fact: ChurchInt -> (error | ('a -> ('a & 'b) & 'c -> 'b) -> ('b & 'a & 'c) -> 'b) //│ = [Function: fact2] // * We can drop all intermediate annotations if the recursive call is annotated: def fact n = (iszero n) (fun _ -> succ zero) (fun _ -> mul n (fact_ty (pred n))) //│ fact: (ChurchInt & (forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> (forall 'd 'e. anything -> ('d -> 'e) -> 'd -> 'e) -> (forall 'f 'g. anything -> ('f -> ('f & 'g)) -> ('g & 'f) -> 'g) -> 'h & (forall 'i 'j. ((forall 'k. anything -> 'k -> 'k) -> (ChurchInt & 'i)) -> ('i -> ChurchInt -> 'j) -> 'j) -> (forall 'l. ((forall 'm. anything -> 'm -> 'm) -> (forall 'm. anything -> 'm -> 'm) -> 'l) -> 'l) -> (forall 'n. 'n -> anything -> 'n) -> ChurchInt) -> 'h //│ = [Function: fact3] fact_ty = fact //│ (ChurchInt & (forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> (forall 'd 'e. anything -> ('d -> 'e) -> 'd -> 'e) -> (forall 'f 'g. anything -> ('f -> ('f & 'g)) -> ('g & 'f) -> 'g) -> 'h & (forall 'i 'j. ((forall 'k. anything -> 'k -> 'k) -> (ChurchInt & 'i)) -> ('i -> ChurchInt -> 'j) -> 'j) -> (forall 'l. ((forall 'm. anything -> 'm -> 'm) -> (forall 'm. anything -> 'm -> 'm) -> 'l) -> 'l) -> (forall 'n. 'n -> anything -> 'n) -> ChurchInt) -> 'h //│ <: fact_ty: //│ ChurchInt -> ChurchInt //│ = [Function: fact3] fact (to_church 4) (fun x -> x + 1) 0 //│ res: int //│ = 24 fact_ty (to_church 4) (fun x -> x + 1) 0 //│ res: int //│ = 24 fact (to_church_ty 4) (fun x -> x + 1) 0 //│ res: int //│ = 24 ================================================ FILE: shared/src/test/diff/fcp/NestedDataTypes.mls ================================================ :NoRecursiveTypes :ConstrainedTypes // * The test takes too much time without this // * Perfect Trees (https://www.cis.upenn.edu/~plclub/blog/2020-12-04-nested-datatypes/) // data NTree (a :: Type) = // NLeaf a // | NNode (NTree (Two a)) type Two[A] = (A, A) //│ Defined type alias Two[+A] def mapTwo f ((a, b)) = (f a, f b) //│ mapTwo: 'a -> ((('b, 'c,),) -> ('d, 'e,) //│ where //│ 'a <: 'b -> 'd & 'c -> 'e) //│ = [Function: mapTwo] class Leaf[A]: { value: A } //│ Defined class Leaf[+A] :e class Node[A]: { subTree: PerfectTree[Two[A]] } type PerfectTree[A] = Leaf[A] | Node[A] //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as Node[Two['A]], but is defined as Node['A] //│ ║ l.24: class Node[A]: { subTree: PerfectTree[Two[A]] } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as PerfectTree[Two['A]], but is defined as PerfectTree['A] //│ ║ l.25: type PerfectTree[A] = Leaf[A] | Node[A] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :IrregularTypes class Node[A]: { subTree: PerfectTree[Two[A]] } type PerfectTree[A] = Leaf[A] | Node[A] //│ Defined class Node[+A] //│ Defined type alias PerfectTree[+A] n1 = Leaf { value = 1 } //│ n1: Leaf[1] //│ = Leaf { value: 1 } n1: PerfectTree[int] //│ res: PerfectTree[int] //│ = Leaf { value: 1 } n2 = Node { subTree = Leaf { value = (1, 2) } } //│ n2: Node[1 | 2] with {subTree: Leaf[(1, 2,)]} //│ = Node1 { subTree: Leaf { value: [ 1, 2 ] } } n2: PerfectTree[int] //│ res: PerfectTree[int] //│ = Node1 { subTree: Leaf { value: [ 1, 2 ] } } n4 = Node { subTree = Node { subTree = Leaf { value = ((1, 2), (3, 4)) } } } //│ n4: Node[1 | 2 | 3 | 4] with {subTree: Node[(1 | 3, 2 | 4,)] with {subTree: Leaf[((1, 2,), (3, 4,),)]}} //│ = Node1 { subTree: Node1 { subTree: Leaf { value: [Array] } } } n4: PerfectTree[int] //│ res: PerfectTree[int] //│ = Node1 { subTree: Node1 { subTree: Leaf { value: [Array] } } } // * On some systems, the default -Xss4M is not enough for this case // :e // * Needs precise-rec-typing (see below) // rec def map f tree = case tree of { // | Leaf -> Leaf { value = f tree.value } // | Node -> Node { subTree = map (mapTwo f) tree.subTree } // } :e // occurs-check :precise-rec-typing rec def map f tree = case tree of { | Leaf -> Leaf { value = f tree.value } | Node -> Node { subTree = map (mapTwo f) tree.subTree } } //│ ╔══[ERROR] Inferred recursive type: 'map //│ where //│ 'map :> forall 'a 'subTree 'A 'subTree0 'value 'value0. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree0})) //│ where //│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,),) -> ('c, 'e,) //│ where //│ 'a <: 'b -> 'c & 'd -> 'e) -> 'subTree -> (PerfectTree[Two['A]] & 'subTree0) //│ 'a <: 'value -> 'value0) //│ ╙── //│ map: 'map //│ where //│ 'map :> forall 'a 'subTree 'A 'subTree0 'value 'value0. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree0})) //│ where //│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,),) -> ('c, 'e,) //│ where //│ 'a <: 'b -> 'c & 'd -> 'e) -> 'subTree -> (PerfectTree[Two['A]] & 'subTree0) //│ 'a <: 'value -> 'value0) //│ = [Function: map] :e map succ n4 //│ ╔══[ERROR] Inferred recursive type: 'map //│ where //│ 'map :> forall 'subTree 'A 'subTree0 'value 'value0. 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: 'subTree})) -> (Leaf['value0] | (Node['A] with {subTree: 'subTree0})) //│ where //│ 'map <: (forall 'b 'c 'd 'e. (('b, 'd,),) -> ('c, 'e,) //│ where //│ 'a <: 'b -> 'c & 'd -> 'e) -> 'subTree -> (PerfectTree[Two['A]] & 'subTree0) //│ 'a <: 'value -> 'value0) //│ ╙── //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.96: map succ n4 //│ ║ ^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ = Node1 { subTree: Node1 { subTree: Leaf { value: [Array] } } } def map: ('a -> 'b) -> PerfectTree['a] -> PerfectTree['b] //│ map: ('a -> 'b) -> PerfectTree['a] -> PerfectTree['b] //│ = def map f tree = case tree of { | Leaf -> Leaf { value = f tree.value } | Node -> Node { subTree = map (mapTwo f) tree.subTree } } //│ 'a -> ((Leaf[?] & {value: 'value} | (Node[?] with {subTree: PerfectTree[('b, 'c,)]})) -> (Leaf['value0] | (Node['A] with {subTree: PerfectTree[('d, 'e,)]})) //│ where //│ 'a <: 'value -> 'value0 & 'b -> ('d & 'A) & 'c -> ('A & 'e)) //│ <: map: //│ ('a -> 'b) -> PerfectTree['a] -> PerfectTree['b] //│ = //│ map is not implemented map succ n4 //│ res: PerfectTree[int] //│ = //│ map and map are not implemented ================================================ FILE: shared/src/test/diff/fcp/NestedDataTypesGADT.mls ================================================ :NoRecursiveTypes :IrregularTypes // * Perfect Trees (https://www.cis.upenn.edu/~plclub/blog/2020-12-04-nested-datatypes/) // * Representing perfect trees with GADTs // data HTree (h :: Nat) (a :: Type) where // DLeaf :: a -> HTree Z a // DNode :: Two (HTree h a) -> HTree (S h) a type Two[A] = (A, A) //│ Defined type alias Two[+A] def mapTwo f ((a, b)) = (f a, f b) //│ mapTwo: ('a -> 'b & 'c -> 'd) -> (('a, 'c,),) -> ('b, 'd,) //│ = [Function: mapTwo] // class Z // class S: { value: Nat } // type NAT = S | Z class Z class S[P] method Inv: P -> P method Inv = id //│ Defined class Z //│ Defined class S[=P] //│ Declared S.Inv: S['P] -> 'P -> 'P //│ Defined S.Inv: S['P] -> 'a -> 'a :w class HTreeBase[N, A]: { n: N } class DLeaf[A]: HTreeBase[Z, A] & { value: A } class DNode[N, A]: HTreeBase[S[N], A] & { subTree: Two[HTree[N, A]] } type HTree[N, A] = forall 'r. (forall 'p. (DLeaf[A] | DNode[S['p], A] & DNode[N, A]) -> 'r) -> 'r //│ Defined class HTreeBase[+N, ±A] //│ Defined class DLeaf[+A] //│ Defined class DNode[=N, +A] //│ Defined type alias HTree[=N, +A] //│ ╔══[WARNING] Type definition HTreeBase has bivariant type parameters: //│ ║ l.34: class HTreeBase[N, A]: { n: N } //│ ║ ^^^^^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.34: class HTreeBase[N, A]: { n: N } //│ ╙── ^ d1 = DLeaf { value = 1; n = Z{} } //│ d1: DLeaf[1] //│ = DLeaf { n: Z {}, value: 1 } d1: HTreeBase[Z, int] //│ res: HTreeBase[Z, ?] //│ = DLeaf { n: Z {}, value: 1 } d1_ k = k d1 //│ d1_: (DLeaf[1] -> 'a) -> 'a //│ = [Function: d1_] d1_ : HTree[Z, int] //│ res: HTree[Z, int] //│ = [Function: d1_] d2 = DNode { subTree = (d1_, d1_); n = S{} } //│ d2: DNode['N, 1] with { //│ n: forall 'P. S['P], //│ subTree: (forall 'a. (DLeaf[1] -> 'a) -> 'a, forall 'a. (DLeaf[1] -> 'a) -> 'a,) //│ } //│ = DNode { n: S {}, subTree: [ [Function: d1_], [Function: d1_] ] } def d1_ty: HTree[Z, int] //│ d1_ty: HTree[Z, int] //│ = d2 = DNode { subTree = (d1_ty, d1_ty); n = S{} } //│ d2: DNode[Z, int] with {n: forall 'P. S['P], subTree: (HTree[Z, int], HTree[Z, int],)} //│ = //│ d1_ty is not implemented d2: DNode[Z, int] d2: HTreeBase[S[Z], int] //│ res: DNode[Z, int] //│ = //│ d2 and d1_ty are not implemented //│ res: HTreeBase[S[Z], ?] //│ = //│ d2 and d1_ty are not implemented d2_ k = k d2 //│ d2_: ((DNode[Z, int] with {n: forall 'P. S['P], subTree: (HTree[Z, int], HTree[Z, int],)}) -> 'a) -> 'a //│ = //│ d2 and d1_ty are not implemented :e // FIXME d2_ : HTree[S[Z], int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.95: d2_ : HTree[S[Z], int] //│ ║ ^^^ //│ ╟── expression of type `S[in Z & 'p out Z | 'p]` is not an instance of type `Z` //│ ╟── Note: constraint arises from type reference: //│ ║ l.72: def d1_ty: HTree[Z, int] //│ ║ ^ //│ ╟── Note: class type parameter N is defined at: //│ ║ l.36: class DNode[N, A]: HTreeBase[S[N], A] & { subTree: Two[HTree[N, A]] } //│ ╙── ^ //│ res: HTree[S[Z], int] //│ = //│ d2_, d2 and d1_ty are not implemented :e // FIXME d2_ k = k (d2:HTreeBase[S[Z], int]) d2_ : HTree[S[Z], int] //│ d2_: (HTreeBase[S[Z], ?] -> 'a) -> 'a //│ = //│ d2 and d1_ty are not implemented //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.112: d2_ : HTree[S[Z], int] //│ ║ ^^^ //│ ╟── type `HTreeBase[S[Z], ?]` does not match type `DLeaf[int] | DNode[S[in Z & 'p out Z | 'p], int]` //│ ║ l.111: d2_ k = k (d2:HTreeBase[S[Z], int]) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.37: type HTree[N, A] = forall 'r. (forall 'p. (DLeaf[A] | DNode[S['p], A] & DNode[N, A]) -> 'r) -> 'r //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: HTree[S[Z], int] //│ = //│ d2_, d2 and d1_ty are not implemented ================================================ FILE: shared/src/test/diff/fcp/NoRecursiveTypes.mls ================================================ :NoRecursiveTypes :e foo = let rec f x = f x.a in 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {a: 'a} //│ ║ l.6: let rec f x = f x.a in 0 //│ ╙── ^^^ //│ foo: 0 //│ = 0 ================================================ FILE: shared/src/test/diff/fcp/OCamlList.mls ================================================ :NoJS // From https://github.com/ocaml/ocaml/blob/5312b4d7b913cde2a69fc0eb5e97e353865b82df/stdlib/list.ml // and https://github.com/ocaml/ocaml/blob/5312b4d7b913cde2a69fc0eb5e97e353865b82df/stdlib/list.mli // *** Signatures of required definitions *** // `compare` and (=) in OCaml are polymorphic def compare' : 'a -> 'a -> int def eq' : 'a -> 'a -> bool //│ compare': anything -> anything -> int //│ eq': anything -> anything -> bool def asr: int -> int -> int //│ asr: int -> int -> int def fst: (('a, 'b),) -> 'a def snd: (('a, 'b),) -> 'b //│ fst: (('a, anything,),) -> 'a //│ snd: ((anything, 'b,),) -> 'b def assert: { fail: anything -> nothing } //│ assert: {fail: anything -> nothing} def failwith: string -> nothing def failwith s = assert.fail (concat "Failure: " s) def invalid_arg: string -> nothing def invalid_arg s = assert.fail (concat "Invalid argument: " s) //│ failwith: string -> nothing //│ string -> nothing //│ <: failwith: //│ string -> nothing //│ invalid_arg: string -> nothing //│ string -> nothing //│ <: invalid_arg: //│ string -> nothing class Exn def Not_found: Exn def raise: Exn -> nothing //│ Defined class Exn //│ Not_found: Exn //│ raise: Exn -> nothing datatype Option[A] = Some(A) | None //│ Defined class Option[+A] //│ Declared Option.Some: Option['A] -> ('A,) //│ Declared Option.None: Option[?] -> anything //│ Defined class Some[+A] //│ Defined class None[+A] //│ Some: 'a -> Option['a] //│ None: Option[nothing] datatype Either[A, B] = Left(A) | Right(B) //│ Defined class Either[+A, +B] //│ Declared Either.Left: Either['A, ?] -> ('A,) //│ Declared Either.Right: Either[?, 'B] -> ('B,) //│ Defined class Left[+A, +B] //│ Defined class Right[+A, +B] //│ Left: 'a -> Either['a, nothing] //│ Right: 'a -> Either[nothing, 'a] datatype List[A] = Cons(A, List[A]) | Nil //│ Defined class List[+A] //│ Declared List.Cons: List['A] -> ('A, List['A],) //│ Declared List.Nil: List[?] -> anything //│ Defined class Cons[+A] //│ Defined class Nil[+A] //│ Cons: ('a, List['a],) -> List['a] //│ Nil: List[nothing] datatype Node[A] = SeqNil | SeqCons(A, Seq[A]) type Seq[A] = unit -> Node[A] //│ Defined class Node[+A] //│ Declared Node.SeqNil: Node[?] -> anything //│ Declared Node.SeqCons: Node['A] -> ('A, Seq['A],) //│ Defined class SeqNil[+A] //│ Defined class SeqCons[+A] //│ Defined type alias Seq[+A] //│ SeqNil: Node[nothing] //│ SeqCons: ('a, Seq['a],) -> Node['a] // *** Signatures ported from List.mli *** def length: List['a] -> int //│ length: List[?] -> int def compare_lengths: List['a] -> List['b] -> int //│ compare_lengths: List[?] -> List[?] -> int def compare_length_with: List['a] -> int -> int //│ compare_length_with: List[?] -> int -> int def is_empty: List['a] -> bool //│ is_empty: List[?] -> bool def cons: 'a -> List['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] def hd: List['a] -> 'a //│ hd: List['a] -> 'a def tl: List['a] -> List['a] //│ tl: List['a] -> List['a] def nth: List['a] -> int -> 'a //│ nth: List['a] -> int -> 'a def nth_opt: List['a] -> int -> Option['a] //│ nth_opt: List['a] -> int -> Option['a] def rev: List['a] -> List['a] //│ rev: List['a] -> List['a] def init: int -> (int -> 'a) -> List['a] //│ init: int -> (int -> 'a) -> List['a] def append: List['a] -> List['a] -> List ['a] //│ append: List['a] -> List['a] -> List['a] def rev_append: List['a] -> List['a] -> List ['a] //│ rev_append: List['a] -> List['a] -> List['a] def concat: List[List['a]] -> List['a] //│ concat: List[List['a]] -> List['a] def flatten: List[List['a]] -> List['a] //│ flatten: List[List['a]] -> List['a] def equal: ('a -> 'a -> bool) -> List['a] -> List['a] -> bool //│ equal: ('a -> 'a -> bool) -> List['a] -> List['a] -> bool def compare: ('a -> 'a -> int) -> List['a] -> List['a] -> int //│ compare: ('a -> 'a -> int) -> List['a] -> List['a] -> int def iter: ('a -> unit) -> List['a] -> unit //│ iter: ('a -> unit) -> List['a] -> unit def iteri: (int -> 'a -> unit) -> List['a] -> unit //│ iteri: (int -> 'a -> unit) -> List['a] -> unit def map: ('a -> 'b) -> List['a] -> List['b] //│ map: ('a -> 'b) -> List['a] -> List['b] def mapi: (int -> 'a -> 'b) -> List['a] -> List['b] //│ mapi: (int -> 'a -> 'b) -> List['a] -> List['b] def rev_map: ('a -> 'b) -> List['a] -> List['b] //│ rev_map: ('a -> 'b) -> List['a] -> List['b] def filter_map: ('a -> Option['b]) -> List['a] -> List['b] //│ filter_map: ('a -> Option['b]) -> List['a] -> List['b] def concat_map: ('a -> List['b]) -> List['a] -> List['b] //│ concat_map: ('a -> List['b]) -> List['a] -> List['b] def fold_left_map: ('acc -> 'a -> ('acc, 'b)) -> 'acc -> List['a] -> ('acc, List['b]) //│ fold_left_map: ('acc -> 'a -> ('acc, 'b,)) -> 'acc -> List['a] -> ('acc, List['b],) def fold_left: ('acc -> 'a -> 'acc) -> 'acc -> List['a] -> 'acc //│ fold_left: ('acc -> 'a -> 'acc) -> 'acc -> List['a] -> 'acc def fold_right: ('a -> 'acc -> 'acc) -> List['a] -> 'acc -> 'acc //│ fold_right: ('a -> 'acc -> 'acc) -> List['a] -> 'acc -> 'acc def iter2: ('a -> 'b -> unit) -> List['a] -> List['b] -> unit //│ iter2: ('a -> 'b -> unit) -> List['a] -> List['b] -> unit def map2: ('a -> 'b -> 'c) -> List['a] -> List['b] -> List['c] //│ map2: ('a -> 'b -> 'c) -> List['a] -> List['b] -> List['c] def rev_map2: ('a -> 'b -> 'c) -> List['a] -> List['b] -> List['c] //│ rev_map2: ('a -> 'b -> 'c) -> List['a] -> List['b] -> List['c] def fold_left2: ('acc -> 'a -> 'b -> 'acc) -> 'acc -> List['a] -> List['b] -> 'acc //│ fold_left2: ('acc -> 'a -> 'b -> 'acc) -> 'acc -> List['a] -> List['b] -> 'acc def fold_right2: ('a -> 'b -> 'acc -> 'acc) -> List['a] -> List['b] -> 'acc -> 'acc //│ fold_right2: ('a -> 'b -> 'acc -> 'acc) -> List['a] -> List['b] -> 'acc -> 'acc def for_all: ('a -> bool) -> List['a] -> bool //│ for_all: ('a -> bool) -> List['a] -> bool def exists: ('a -> bool) -> List['a] -> bool //│ exists: ('a -> bool) -> List['a] -> bool def for_all2: ('a -> 'b -> bool) -> List['a] -> List['b] -> bool //│ for_all2: ('a -> 'b -> bool) -> List['a] -> List['b] -> bool def exists2: ('a -> 'b -> bool) -> List['a] -> List['b] -> bool //│ exists2: ('a -> 'b -> bool) -> List['a] -> List['b] -> bool def mem: 'a -> List['a] -> bool //│ mem: anything -> List[?] -> bool def memq: 'a -> List['a] -> bool //│ memq: anything -> List[?] -> bool def find: ('a -> bool) -> List['a] -> 'a //│ find: ('a -> bool) -> List['a] -> 'a def find_opt: ('a -> bool) -> List['a] -> Option['a] //│ find_opt: ('a -> bool) -> List['a] -> Option['a] def find_index: ('a -> bool) -> List['a] -> Option[int] //│ find_index: ('a -> bool) -> List['a] -> Option[int] def find_map: ('a -> Option['b]) -> List['a] -> Option['b] //│ find_map: ('a -> Option['b]) -> List['a] -> Option['b] def find_mapi: (int -> 'a -> Option['b]) -> List['a] -> Option['b] //│ find_mapi: (int -> 'a -> Option['b]) -> List['a] -> Option['b] def filter: ('a -> bool) -> List['a] -> List['a] //│ filter: ('a -> bool) -> List['a] -> List['a] def find_all: ('a -> bool) -> List['a] -> List['a] //│ find_all: ('a -> bool) -> List['a] -> List['a] def filteri: (int -> 'a -> bool) -> List['a] -> List['a] //│ filteri: (int -> 'a -> bool) -> List['a] -> List['a] def partition: ('a -> bool) -> List['a] -> (List['a], List['a]) //│ partition: ('a -> bool) -> List['a] -> (List['a], List['a],) def partition_map: ('a -> Either['b, 'c]) -> List['a] -> (List['b], List['c]) //│ partition_map: ('a -> Either['b, 'c]) -> List['a] -> (List['b], List['c],) def assoc: 'a -> List[('a, 'b)] -> 'b //│ assoc: anything -> List[(anything, 'b,)] -> 'b def assoc_opt: 'a -> List[('a, 'b)] -> Option['b] //│ assoc_opt: anything -> List[(anything, 'b,)] -> Option['b] def assq: 'a -> List[('a, 'b)] -> 'b //│ assq: anything -> List[(anything, 'b,)] -> 'b def assq_opt: 'a -> List[('a, 'b)] -> Option['b] //│ assq_opt: anything -> List[(anything, 'b,)] -> Option['b] def mem_assoc: 'a -> List[('a, 'b)] -> bool //│ mem_assoc: anything -> List[(anything, anything,)] -> bool def mem_assq: 'a -> List[('a, 'b)] -> bool //│ mem_assq: anything -> List[(anything, anything,)] -> bool def remove_assoc: 'a -> List[('a, 'b)] -> List[('a, 'b)] //│ remove_assoc: 'a -> List[('a, 'b,)] -> List[('a, 'b,)] def remove_assq: 'a -> List[('a, 'b)] -> List[('a, 'b)] //│ remove_assq: 'a -> List[('a, 'b,)] -> List[('a, 'b,)] def split: List[('a, 'b)] -> (List['a], List['b]) //│ split: List[('a, 'b,)] -> (List['a], List['b],) def combine: List['a] -> List['b] -> List[('a, 'b)] //│ combine: List['a] -> List['b] -> List[('a, 'b,)] def sort: ('a -> 'a -> int) -> List['a] -> List['a] //│ sort: ('a -> 'a -> int) -> List['a] -> List['a] def stable_sort: ('a -> 'a -> int) -> List['a] -> List['a] //│ stable_sort: ('a -> 'a -> int) -> List['a] -> List['a] def fast_sort: ('a -> 'a -> int) -> List['a] -> List['a] //│ fast_sort: ('a -> 'a -> int) -> List['a] -> List['a] def sort_uniq: ('a -> 'a -> int) -> List['a] -> List['a] //│ sort_uniq: ('a -> 'a -> int) -> List['a] -> List['a] def merge: ('a -> 'a -> int) -> List['a] -> List['a] -> List['a] //│ merge: ('a -> 'a -> int) -> List['a] -> List['a] -> List['a] def to_seq: List['a] -> Seq['a] //│ to_seq: List['a] -> Seq['a] def of_seq: Seq['a] -> List['a] //│ of_seq: Seq['a] -> List['a] // *** Implementations ported from List.ml *** rec def length_aux len = fun x -> match x with | Nil -> len | Cons(_, l) -> length_aux (len + 1) l //│ length_aux: int -> List[?] -> int def length l = length_aux 0 l //│ List[?] -> int //│ <: length: //│ List[?] -> int def cons a l = Cons(a, l) //│ 'a -> List['a] -> List['a] //│ <: cons: //│ 'a -> List['a] -> List['a] def hd = fun x -> match x with | Nil -> failwith "hd" | Cons(a, _) -> a //│ List['a] -> 'a //│ <: hd: //│ List['a] -> 'a def tl = fun x -> match x with | Nil -> failwith "tl" | Cons(_, l) -> l //│ List['A] -> List['A] //│ <: tl: //│ List['a] -> List['a] def nth l n = if n < 0 then invalid_arg "List.nth" else let rec nth_aux l n = match l with | Nil -> failwith "nth" | Cons(a, l) -> if n == 0 then a else nth_aux l (n-1) in nth_aux l n //│ List['a] -> int -> 'a //│ <: nth: //│ List['a] -> int -> 'a def nth_opt l n = if n < 0 then invalid_arg "List.nth" else let rec nth_aux l n = match l with | Nil -> None | Cons(a, l) -> if n == 0 then Some(a) else nth_aux l (n-1) in nth_aux l n //│ List['a] -> int -> Option['a] //│ <: nth_opt: //│ List['a] -> int -> Option['a] // https://github.com/ocaml/ocaml/blob/5312b4d7b913cde2a69fc0eb5e97e353865b82df/stdlib/stdlib.ml#L298 rec def append l1 l2 = match l1 with | Nil -> l2 | Cons(h1, Nil) -> Cons(h1, l2) | Cons(h1, Cons(h2, Nil)) -> Cons(h1, Cons(h2, l2)) | Cons(h1, Cons(h2, Cons(h3, tl))) -> Cons(h1, Cons(h2, Cons(h3, append tl l2))) //│ List['a] -> List['a] -> List['a] //│ <: append: //│ List['a] -> List['a] -> List['a] rec def rev_append l1 l2 = match l1 with Nil -> l2 | Cons(a, l) -> rev_append l (Cons(a, l2)) //│ List['a] -> List['a] -> List['a] //│ <: rev_append: //│ List['a] -> List['a] -> List['a] def rev l = rev_append l Nil //│ List['a] -> List['a] //│ <: rev: //│ List['a] -> List['a] // overloaded with `init` in OCaml rec def init_aux i last f = if i > last then Nil else if i == last then Cons(f i, Nil) else let r1 = f i in let r2 = f (i+1) in Cons(r1, Cons(r2, init_aux (i+2) last f)) //│ init_aux: int -> number -> (int -> 'a) -> List['a] def init len f = if len < 0 then invalid_arg "List.init" else init_aux 0 (len - 1) f //│ int -> (int -> 'a) -> List['a] //│ <: init: //│ int -> (int -> 'a) -> List['a] rec def flatten = fun x -> match x with Nil -> Nil | Cons(l, r) -> append l (flatten r) //│ List[List['a]] -> List['a] //│ <: flatten: //│ List[List['a]] -> List['a] def concat = flatten //│ List[List['a]] -> List['a] //│ <: concat: //│ List[List['a]] -> List['a] rec def map f = fun x -> match x with Nil -> Nil | Cons(a1, Nil) -> let r1 = f a1 in Cons(r1, Nil) | Cons(a1, Cons(a2, l)) -> let r1 = f a1 in let r2 = f a2 in Cons(r1, Cons(r2, map f l)) //│ ('A -> 'a) -> List['A] -> List['a] //│ <: map: //│ ('a -> 'b) -> List['a] -> List['b] // overloaded with mapi in OCaml rec def mapi_aux i f = fun x -> match x with Nil -> Nil | Cons(a1, Nil) -> let r1 = f i a1 in Cons(r1, Nil) | Cons(a1, Cons(a2, l)) -> let r1 = f i a1 in let r2 = f (i+1) a2 in Cons(r1, Cons(r2, mapi_aux (i+2) f l)) //│ mapi_aux: int -> (int -> 'A -> 'a) -> List['A] -> List['a] def mapi f l = mapi_aux 0 f l //│ (int -> 'A -> 'a) -> List['A] -> List['a] //│ <: mapi: //│ (int -> 'a -> 'b) -> List['a] -> List['b] def rev_map f l = let rec rmap_f accu = fun x -> match x with | Nil -> accu | Cons(a, l) -> rmap_f (Cons(f a, accu)) l in rmap_f Nil l //│ ('A -> 'a) -> List['A] -> List['a] //│ <: rev_map: //│ ('a -> 'b) -> List['a] -> List['b] rec def iter f = fun x -> match x with Nil -> Unit | Cons(a, l) -> let _ = f a in iter f l //│ ('A -> anything) -> List['A] -> unit //│ <: iter: //│ ('a -> unit) -> List['a] -> unit // overloaded with iteri in OCaml rec def iteri_aux i f = fun x -> match x with Nil -> Unit | Cons(a, l) -> let _ = f i a in iteri_aux (i + 1) f l //│ iteri_aux: int -> (int -> 'A -> anything) -> List['A] -> unit def iteri f l = iteri_aux 0 f l //│ (int -> 'A -> anything) -> List['A] -> unit //│ <: iteri: //│ (int -> 'a -> unit) -> List['a] -> unit rec def fold_left f accu l = match l with Nil -> accu | Cons(a, l) -> fold_left f (f accu a) l //│ ('a -> 'A -> 'a) -> 'a -> List['A] -> 'a //│ <: fold_left: //│ ('acc -> 'a -> 'acc) -> 'acc -> List['a] -> 'acc rec def fold_right f l accu = match l with Nil -> accu | Cons(a, l) -> f a (fold_right f l accu) //│ ('A -> 'a -> 'a) -> List['A] -> 'a -> 'a //│ <: fold_right: //│ ('a -> 'acc -> 'acc) -> List['a] -> 'acc -> 'acc rec def map2 f l1 l2 = match (l1, l2) with (Nil, Nil) -> Nil | (Cons(a1, Nil), Cons(b1, Nil)) -> let r1 = f a1 b1 in Cons(r1, Nil) | (Cons(a1, Cons(a2, l1)), Cons(b1, Cons(b2, l2))) -> let r1 = f a1 b1 in let r2 = f a2 b2 in Cons(r1, Cons(r2, map2 f l1 l2)) | (_, _) -> invalid_arg "List.map2" //│ ('A -> 'A0 -> 'a) -> List['A] -> List['A0] -> List['a] //│ <: map2: //│ ('a -> 'b -> 'c) -> List['a] -> List['b] -> List['c] def rev_map2 f l1 l2 = let rec rmap2_f accu l1 l2 = match (l1, l2) with | (Nil, Nil) -> accu | (Cons(a1, l1), Cons(a2, l2)) -> rmap2_f (Cons(f a1 a2, accu)) l1 l2 | (_, _) -> invalid_arg "List.rev_map2" in rmap2_f Nil l1 l2 //│ ('A -> 'A0 -> 'a) -> List['A] -> List['A0] -> List['a] //│ <: rev_map2: //│ ('a -> 'b -> 'c) -> List['a] -> List['b] -> List['c] rec def iter2 f l1 l2 = match (l1, l2) with (Nil, Nil) -> Unit | (Cons(a1, l1), Cons(a2, l2)) -> let _ = f a1 a2 in iter2 f l1 l2 | (_, _) -> invalid_arg "List.iter2" //│ ('A -> 'A0 -> anything) -> List['A] -> List['A0] -> unit //│ <: iter2: //│ ('a -> 'b -> unit) -> List['a] -> List['b] -> unit rec def fold_left2 f accu l1 l2 = match (l1, l2) with (Nil, Nil) -> accu | (Cons(a1, l1), Cons(a2, l2)) -> fold_left2 f (f accu a1 a2) l1 l2 | (_, _) -> invalid_arg "List.fold_left2" //│ ('a -> 'A -> 'A0 -> 'a) -> 'a -> List['A] -> List['A0] -> 'a //│ <: fold_left2: //│ ('acc -> 'a -> 'b -> 'acc) -> 'acc -> List['a] -> List['b] -> 'acc rec def fold_right2 f l1 l2 accu = match (l1, l2) with (Nil, Nil) -> accu | (Cons(a1, l1), Cons(a2, l2)) -> f a1 a2 (fold_right2 f l1 l2 accu) | (_, _) -> invalid_arg "List.fold_right2" //│ ('A -> 'A0 -> 'a -> 'a) -> List['A] -> List['A0] -> 'a -> 'a //│ <: fold_right2: //│ ('a -> 'b -> 'acc -> 'acc) -> List['a] -> List['b] -> 'acc -> 'acc rec def for_all p = fun x -> match x with Nil -> true | Cons(a, l) -> p a && for_all p l //│ ('A -> bool) -> List['A] -> bool //│ <: for_all: //│ ('a -> bool) -> List['a] -> bool rec def exists p = fun x -> match x with Nil -> false | Cons(a, l) -> p a || exists p l //│ ('A -> bool) -> List['A] -> bool //│ <: exists: //│ ('a -> bool) -> List['a] -> bool rec def for_all2 p l1 l2 = match (l1, l2) with (Nil, Nil) -> true | (Cons(a1, l1), Cons(a2, l2)) -> p a1 a2 && for_all2 p l1 l2 | (_, _) -> invalid_arg "List.for_all2" //│ ('A -> 'A0 -> bool) -> List['A] -> List['A0] -> bool //│ <: for_all2: //│ ('a -> 'b -> bool) -> List['a] -> List['b] -> bool rec def exists2 p l1 l2 = match (l1, l2) with (Nil, Nil) -> false | (Cons(a1, l1), Cons(a2, l2)) -> p a1 a2 || exists2 p l1 l2 | (_, _) -> invalid_arg "List.exists2" //│ ('A -> 'A0 -> bool) -> List['A] -> List['A0] -> bool //│ <: exists2: //│ ('a -> 'b -> bool) -> List['a] -> List['b] -> bool rec def mem x = fun y -> match y with Nil -> false | Cons(a, l) -> compare' a x == 0 || mem x l //│ anything -> List[?] -> bool //│ <: mem: //│ anything -> List[?] -> bool rec def memq x = fun y -> match y with Nil -> false | Cons(a, l) -> eq a x || memq x l //│ anything -> List[?] -> bool //│ <: memq: //│ anything -> List[?] -> bool rec def assoc x = fun y -> match y with Nil -> raise Not_found | Cons((a, b), l) -> if compare' a x == 0 then b else assoc x l //│ anything -> List[(anything, 'a,)] -> 'a //│ <: assoc: //│ anything -> List[(anything, 'b,)] -> 'b rec def assoc_opt x = fun y -> match y with Nil -> None | Cons((a, b), l) -> if compare' a x == 0 then Some(b) else assoc_opt x l //│ anything -> List[(anything, 'a,)] -> Option['a] //│ <: assoc_opt: //│ anything -> List[(anything, 'b,)] -> Option['b] rec def assq x = fun y -> match y with Nil -> raise Not_found | Cons((a, b), l) -> if eq a x then b else assq x l //│ anything -> List[(anything, 'a,)] -> 'a //│ <: assq: //│ anything -> List[(anything, 'b,)] -> 'b rec def assq_opt x = fun y -> match y with Nil -> None | Cons((a, b), l) -> if eq a x then Some(b) else assq_opt x l //│ anything -> List[(anything, 'a,)] -> Option['a] //│ <: assq_opt: //│ anything -> List[(anything, 'b,)] -> Option['b] rec def mem_assoc x = fun y -> match y with | Nil -> false | Cons((a, _), l) -> compare' a x == 0 || mem_assoc x l //│ anything -> List[(anything, anything,)] -> bool //│ <: mem_assoc: //│ anything -> List[(anything, anything,)] -> bool rec def mem_assq x = fun y -> match y with | Nil -> false | Cons((a, _), l) -> eq a x || mem_assq x l //│ anything -> List[(anything, anything,)] -> bool //│ <: mem_assq: //│ anything -> List[(anything, anything,)] -> bool rec def remove_assoc x = fun y -> match y with | Nil -> Nil | Cons((a, _) as pair, l) -> if compare' a x == 0 then l else Cons(pair, remove_assoc x l) //│ anything -> List[(anything, anything,) & 'A] -> List['A] //│ <: remove_assoc: //│ 'a -> List[('a, 'b,)] -> List[('a, 'b,)] rec def remove_assq x = fun y -> match y with | Nil -> Nil | Cons((a, _) as pair, l) -> if eq a x then l else Cons(pair, remove_assq x l) //│ anything -> List[(anything, anything,) & 'A] -> List['A] //│ <: remove_assq: //│ 'a -> List[('a, 'b,)] -> List[('a, 'b,)] rec def find p = fun x -> match x with | Nil -> raise Not_found | Cons(x, l) -> if p x then x else find p l //│ ('A -> bool) -> List['A] -> 'A //│ <: find: //│ ('a -> bool) -> List['a] -> 'a rec def find_opt p = fun x -> match x with | Nil -> None | Cons(x, l) -> if p x then Some(x) else find_opt p l //│ ('A -> bool) -> List['A] -> Option['A] //│ <: find_opt: //│ ('a -> bool) -> List['a] -> Option['a] def find_index p = let rec aux i = fun x -> match x with Nil -> None | Cons(a, l) -> if p a then Some(i) else aux (i+1) l in aux 0 //│ ('A -> bool) -> List['A] -> Option[int] //│ <: find_index: //│ ('a -> bool) -> List['a] -> Option[int] rec def find_map f = fun x -> match x with | Nil -> None | Cons(x, l) -> match f x with | Some(_) as result -> result | None -> find_map f l //│ ('A -> (Option[?] & 'a)) -> List['A] -> (Option[nothing] | 'a) //│ <: find_map: //│ ('a -> Option['b]) -> List['a] -> Option['b] def find_mapi f = let rec aux i = fun x -> match x with | Nil -> None | Cons(x, l) -> match f i x with | Some(_) as result -> result | None -> aux (i+1) l in aux 0 //│ (int -> 'A -> (Option[?] & 'a)) -> List['A] -> (Option[nothing] | 'a) //│ <: find_mapi: //│ (int -> 'a -> Option['b]) -> List['a] -> Option['b] rec def find_all p = fun x -> match x with | Nil -> Nil | Cons(x, l) -> if p x then Cons(x, find_all p l) else find_all p l //│ ('A -> bool) -> List['A] -> List['A] //│ <: find_all: //│ ('a -> bool) -> List['a] -> List['a] def filter = find_all //│ ('a -> bool) -> List['a] -> List['a] //│ <: filter: //│ ('a -> bool) -> List['a] -> List['a] // overloaded with filter in OCaml rec def filteri_aux p i = fun x -> match x with | Nil -> Nil | Cons(x, l) -> let i' = i + 1 in if p i x then Cons(x, filteri_aux p i' l) else filteri_aux p i' l //│ filteri_aux: (int -> 'A -> bool) -> int -> List['A] -> List['A] def filteri p l = filteri_aux p 0 l //│ (int -> 'A -> bool) -> List['A] -> List['A] //│ <: filteri: //│ (int -> 'a -> bool) -> List['a] -> List['a] rec def filter_map f = fun x -> match x with | Nil -> Nil | Cons(x, l) -> match f x with | None -> filter_map f l | Some(v) -> Cons(v, filter_map f l) //│ ('A -> Option['a]) -> List['A] -> List['a] //│ <: filter_map: //│ ('a -> Option['b]) -> List['a] -> List['b] def concat_map = let rec r = { concat_map = fun f -> fun x -> match x with | Nil -> Nil | Cons(x, xs) -> r.prepend_concat_map (f x) f xs, prepend_concat_map = fun ys -> fun f -> fun xs -> match ys with | Nil -> r.concat_map f xs | Cons(y, ys) -> r.prepend_concat_map ys f xs } in r.concat_map //│ ('A -> List[?]) -> List['A] -> (List[nothing] | 'a) //│ <: concat_map: //│ ('a -> List['b]) -> List['a] -> List['b] def fold_left_map f accu l = let rec aux accu l_accu = fun x -> match x with | Nil -> (accu, rev l_accu) | Cons(x, l) -> match f accu x with (accu, x) -> aux accu (Cons(x, l_accu)) l in aux accu Nil l //│ ('a -> 'A -> ('a, 'b,)) -> 'a -> List['A] -> ('a, List['b],) //│ <: fold_left_map: //│ ('acc -> 'a -> ('acc, 'b,)) -> 'acc -> List['a] -> ('acc, List['b],) def partition p l = let rec part yes no = fun x -> match x with | Nil -> (rev yes, rev no) | Cons(x, l) -> if p x then part (Cons(x, yes)) no l else part yes (Cons(x, no)) l in part Nil Nil l //│ ('A -> bool) -> List['A] -> (List['A], List['A],) //│ <: partition: //│ ('a -> bool) -> List['a] -> (List['a], List['a],) def partition_map p l = let rec part left right = fun x -> match x with | Nil -> (rev left, rev right) | Cons(x, l) -> match p x with | Left(v) -> part (Cons(v, left)) right l | Right(v) -> part left (Cons(v, right)) l in part Nil Nil l //│ ('A -> Either['A0, 'B]) -> List['A] -> (List['A0], List['B],) //│ <: partition_map: //│ ('a -> Either['b, 'c]) -> List['a] -> (List['b], List['c],) rec def split = fun x -> match x with Nil -> (Nil, Nil) | Cons((x, y), l) -> match split l with (rx, ry) -> (Cons(x, rx), Cons(y, ry)) //│ List[('a, 'b,)] -> (List['a], List['b],) //│ <: split: //│ List[('a, 'b,)] -> (List['a], List['b],) rec def combine l1 l2 = match (l1, l2) with (Nil, Nil) -> Nil | (Cons(a1, l1), Cons(a2, l2)) -> Cons((a1, a2), combine l1 l2) | (_, _) -> invalid_arg "List.combine" //│ List['A] -> List['A0] -> List[('A, 'A0,)] //│ <: combine: //│ List['a] -> List['b] -> List[('a, 'b,)] rec def merge cmp l1 l2 = match (l1, l2) with | (Nil, l2) -> l2 | (l1, Nil) -> l1 | (Cons(h1, t1), Cons(h2, t2)) -> if cmp h1 h2 <= 0 then Cons(h1, merge cmp t1 l2) else Cons(h2, merge cmp l1 t2) //│ ('A -> 'A0 -> number) -> List['A & 'a] -> List['A0 & 'a] -> List['a | 'A0 | 'A] //│ <: merge: //│ ('a -> 'a -> int) -> List['a] -> List['a] -> List['a] def stable_sort cmp l = let rec rev_merge l1 l2 accu = match (l1, l2) with | (Nil, l2) -> rev_append l2 accu | (l1, Nil) -> rev_append l1 accu | (Cons(h1, t1), Cons(h2, t2)) -> if cmp h1 h2 <= 0 then rev_merge t1 l2 (Cons(h1, accu)) else rev_merge l1 t2 (Cons(h2, accu)) in let rec rev_merge_rev l1 l2 accu = match (l1, l2) with | (Nil, l2) -> rev_append l2 accu | (l1, Nil) -> rev_append l1 accu | (Cons(h1, t1), Cons(h2, t2)) -> if cmp h1 h2 > 0 then rev_merge_rev t1 l2 (Cons(h1, accu)) else rev_merge_rev l1 t2 (Cons(h2, accu)) in let rec r = { sort = fun n -> fun l -> match (n, l) with | (2, Cons(x1, Cons(x2, tl))) -> let s = if cmp x1 x2 <= 0 then Cons(x1, Cons(x2, Nil)) else Cons(x2, Cons(x1, Nil)) in (s, tl) | (3, Cons(x1, Cons(x2, Cons(x3, tl)))) -> let s = if cmp x1 x2 <= 0 then if cmp x2 x3 <= 0 then Cons(x1, Cons(x2, Cons(x3, Nil))) else if cmp x1 x3 <= 0 then Cons(x1, Cons(x3, Cons(x2, Nil))) else Cons(x3, Cons(x1, Cons(x2, Nil))) else if cmp x1 x3 <= 0 then Cons(x2, Cons(x1, Cons(x3, Nil))) else if cmp x2 x3 <= 0 then Cons(x2, Cons(x3, Cons(x1, Nil))) else Cons(x3, Cons(x2, Cons(x1, Nil))) in (s, tl) | (n, l) -> let n1 = asr n 1 in let n2 = n - n1 in match r.rev_sort n1 l with (s1, l2) -> match r.rev_sort n2 l2 with (s2, tl) -> (rev_merge_rev s1 s2 Nil, tl), rev_sort = fun n -> fun l -> match (n, l) with | (2, Cons(x1, Cons(x2, tl))) -> let s = if cmp x1 x2 > 0 then Cons(x1, Cons(x2, Nil)) else Cons(x2, Cons(x1, Nil)) in (s, tl) | (3, Cons(x1, Cons(x2, Cons(x3, tl)))) -> let s = if cmp x1 x2 > 0 then if cmp x2 x3 > 0 then Cons(x1, Cons(x2, Cons(x3, Nil))) else if cmp x1 x3 > 0 then Cons(x1, Cons(x3, Cons(x2, Nil))) else Cons(x3, Cons(x1, Cons(x2, Nil))) else if cmp x1 x3 > 0 then Cons(x2, Cons(x1, Cons(x3, Nil))) else if cmp x2 x3 > 0 then Cons(x2, Cons(x3, Cons(x1, Nil))) else Cons(x3, Cons(x2, Cons(x1, Nil))) in (s, tl) | (n, l) -> let n1 = asr n 1 in let n2 = n - n1 in match r.sort n1 l with (s1, l2) -> match r.sort n2 l2 with (s2, tl) -> (rev_merge s1 s2 Nil, tl) } in let sort = r.sort in let rev_sort = r.rev_sort in let len = length l in if len < 2 then l else fst (sort len l) //│ ('A -> 'A -> number) -> (List['A & 'A0] & 'a & List['A & 'A1]) -> (List['A0 | 'A1] | 'a) //│ <: stable_sort: //│ ('a -> 'a -> int) -> List['a] -> List['a] def sort = stable_sort def fast_sort = stable_sort //│ ('a -> 'a -> int) -> List['a] -> List['a] //│ <: sort: //│ ('a -> 'a -> int) -> List['a] -> List['a] //│ ('a -> 'a -> int) -> List['a] -> List['a] //│ <: fast_sort: //│ ('a -> 'a -> int) -> List['a] -> List['a] def sort_uniq cmp l = let rec rev_merge l1 l2 accu = match (l1, l2) with | (Nil, l2) -> rev_append l2 accu | (l1, Nil) -> rev_append l1 accu | (Cons(h1, t1), Cons(h2, t2)) -> let c = cmp h1 h2 in if c == 0 then rev_merge t1 t2 (Cons(h1, accu)) else if c < 0 then rev_merge t1 l2 (Cons(h1, accu)) else rev_merge l1 t2 (Cons(h2, accu)) in let rec rev_merge_rev l1 l2 accu = match (l1, l2) with | (Nil, l2) -> rev_append l2 accu | (l1, Nil) -> rev_append l1 accu | (Cons(h1, t1), Cons(h2, t2)) -> let c = cmp h1 h2 in if c == 0 then rev_merge_rev t1 t2 (Cons(h1, accu)) else if c > 0 then rev_merge_rev t1 l2 (Cons(h1, accu)) else rev_merge_rev l1 t2 (Cons(h2, accu)) in let rec r = { sort = fun n -> fun l -> match (n, l) with | (2, Cons(x1, Cons(x2, tl))) -> let s = let c = cmp x1 x2 in if c == 0 then Cons(x1, Nil) else if c < 0 then Cons(x1, Cons(x2, Nil)) else Cons(x2, Cons(x1, Nil)) in (s, tl) | (3, Cons(x1, Cons(x2, Cons(x3, tl)))) -> let s = let c = cmp x1 x2 in if c == 0 then let c = cmp x2 x3 in if c == 0 then Cons(x2, Nil) else if c < 0 then Cons(x2, Cons(x3, Nil)) else Cons(x3, Cons(x2, Nil)) else if c < 0 then let c = cmp x2 x3 in if c == 0 then Cons(x1, Cons(x2, Nil)) else if c < 0 then Cons(x1, Cons(x2, Cons(x3, Nil))) else let c = cmp x1 x3 in if c == 0 then Cons(x1, Cons(x2, Nil)) else if c < 0 then Cons(x1, Cons(x3, Cons(x2, Nil))) else Cons(x3, Cons(x1, Cons(x2, Nil))) else let c = cmp x1 x3 in if c == 0 then Cons(x2, Cons(x1, Nil)) else if c < 0 then Cons(x2, Cons(x1, Cons(x3, Nil))) else let c = cmp x2 x3 in if c == 0 then Cons(x2, Cons(x1, Nil)) else if c < 0 then Cons(x2, Cons(x3, Cons(x1, Nil))) else Cons(x3, Cons(x2, Cons(x1, Nil))) in (s, tl) | (n, l) -> let n1 = asr n 1 in let n2 = n - n1 in match r.rev_sort n1 l with (s1, l2) -> match r.rev_sort n2 l2 with (s2, tl) -> (rev_merge_rev s1 s2 Nil, tl), rev_sort = fun n -> fun l -> match (n, l) with | (2, Cons(x1, Cons(x2, tl))) -> let s = let c = cmp x1 x2 in if c == 0 then Cons(x1, Nil) else if c > 0 then Cons(x1, Cons(x2, Nil)) else Cons(x2, Cons(x1, Nil)) in (s, tl) | (3, Cons(x1, Cons(x2, Cons(x3, tl)))) -> let s = let c = cmp x1 x2 in if c == 0 then let c = cmp x2 x3 in if c == 0 then Cons(x2, Nil) else if c > 0 then Cons(x2, Cons(x3, Nil)) else Cons(x3, Cons(x2, Nil)) else if c > 0 then let c = cmp x2 x3 in if c == 0 then Cons(x1, Cons(x2, Nil)) else if c > 0 then Cons(x1, Cons(x2, Cons(x3, Nil))) else let c = cmp x1 x3 in if c == 0 then Cons(x1, Cons(x2, Nil)) else if c > 0 then Cons(x1, Cons(x3, Cons(x2, Nil))) else Cons(x3, Cons(x1, Cons(x2, Nil))) else let c = cmp x1 x3 in if c == 0 then Cons(x2, Cons(x1, Nil)) else if c > 0 then Cons(x2, Cons(x1, Cons(x3, Nil))) else let c = cmp x2 x3 in if c == 0 then Cons(x2, Cons(x1, Nil)) else if c > 0 then Cons(x2, Cons(x3, Cons(x1, Nil))) else Cons(x3, Cons(x2, Cons(x1, Nil))) in (s, tl) | (n, l) -> let n1 = asr n 1 in let n2 = n - n1 in match r.sort n1 l with (s1, l2) -> match r.sort n2 l2 with (s2, tl) -> (rev_merge s1 s2 Nil, tl) } in let sort = r.sort in let rev_sort = r.rev_sort in let len = length l in if len < 2 then l else fst (sort len l) //│ ('A -> 'A -> number) -> (List['A & 'A0] & 'a & List['A & 'A1]) -> (List['A0 | 'A1] | 'a) //│ <: sort_uniq: //│ ('a -> 'a -> int) -> List['a] -> List['a] rec def compare_lengths l1 l2 = match (l1, l2) with | (Nil, Nil) -> 0 | (Nil, _) -> negate 1 | (_, Nil) -> 1 | (Cons(_, l1), Cons(_, l2)) -> compare_lengths l1 l2 //│ List[?] -> List[?] -> int //│ <: compare_lengths: //│ List[?] -> List[?] -> int rec def compare_length_with l n = match l with | Nil -> if n == 0 then 0 else if n > 0 then negate 1 else 1 | Cons(_, l) -> if n <= 0 then 1 else compare_length_with l (n-1) //│ List[?] -> int -> int //│ <: compare_length_with: //│ List[?] -> int -> int def is_empty = fun x -> match x with | Nil -> true | Cons(_, _) -> false //│ List[?] -> Bool //│ <: is_empty: //│ List[?] -> bool rec def equal eq l1 l2 = match (l1, l2) with | (Nil, Nil) -> true | (Nil, Cons(_, _)) -> false | (Cons(_, _), Nil) -> false | (Cons(a1, l1), Cons(a2, l2)) -> eq a1 a2 && equal eq l1 l2 //│ ('A -> 'A0 -> bool) -> List['A] -> List['A0] -> (bool | false) //│ <: equal: //│ ('a -> 'a -> bool) -> List['a] -> List['a] -> bool rec def compare cmp l1 l2 = match (l1, l2) with | (Nil, Nil) -> 0 | (Nil, Cons(_, _)) -> negate 1 | (Cons(_, _), Nil) -> 1 | (Cons(a1, l1), Cons(a2, l2)) -> let c = cmp a1 a2 in if c <> 0 then c else compare cmp l1 l2 //│ ('A -> 'A0 -> (number & 'a)) -> List['A] -> List['A0] -> (int | 'a) //│ <: compare: //│ ('a -> 'a -> int) -> List['a] -> List['a] -> int def to_seq l = let rec aux l Unit = match l with | Nil -> SeqNil | Cons(x, tail) -> SeqCons(x, aux tail) in aux l //│ List['a] -> unit -> Node['a] //│ <: to_seq: //│ List['a] -> Seq['a] rec def of_seq seq = match seq Unit with | SeqNil -> Nil | SeqCons(x1, seq) -> match seq Unit with | SeqNil -> Cons(x1, Nil) | SeqCons(x2, seq) -> Cons(x1, Cons(x2, of_seq seq)) //│ (unit -> Node['A]) -> List['A] //│ <: of_seq: //│ Seq['a] -> List['a] ================================================ FILE: shared/src/test/diff/fcp/OverloadSimplif.mls ================================================ :NoJS def foo f x = if true then f x else f (id x) //│ foo: ('a -> 'b) -> 'a -> 'b def foo k f x = if true then f x else f (k x) //│ foo: ('a -> 'b) -> (('a | 'b) -> 'c) -> 'a -> 'c // * Not ethe simplified type, similar to the one above, as expected foo id //│ res: ('a -> 'b) -> 'a -> 'b def f: ('a -> 'b & 'c -> 'b & 'a -> 'c) -> 'a -> 'b //│ f: (('a | 'c) -> 'b & 'a -> 'c) -> 'a -> 'b def f: int -> int & nothing -> string //│ f: int -> int def f: int -> int & string -> anything //│ f: int -> int & string -> anything def f: int -> int & 1 -> number //│ f: int -> int def f: int -> int & 'a -> 'a //│ f: int -> int & 'a -> 'a ================================================ FILE: shared/src/test/diff/fcp/Overloads.mls ================================================ :NoJS type IISS = int -> int & string -> string type BBNN = bool -> bool & number -> number type ZZII = 0 -> 0 & int -> int //│ Defined type alias IISS //│ Defined type alias BBNN //│ Defined type alias ZZII def IISS: int -> int & string -> string def BBNN: bool -> bool & number -> number def ZZII: 0 -> 0 & int -> int //│ IISS: int -> int & string -> string //│ BBNN: bool -> bool & number -> number //│ ZZII: 0 -> 0 & int -> int IISS : IISS //│ res: IISS IISS : int -> int & string -> string //│ res: int -> int & string -> string IISS : IISS | BBNN //│ res: BBNN | IISS :e IISS : ZZII //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.30: IISS : ZZII //│ ║ ^^^^ //│ ╟── type `int` does not match type `0` //│ ║ l.12: def IISS: int -> int & string -> string //│ ║ ^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.7: type ZZII = 0 -> 0 & int -> int //│ ╙── ^ //│ res: ZZII :e IISS : BBNN //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.43: IISS : BBNN //│ ║ ^^^^ //│ ╟── type `bool` does not match type `int | string` //│ ║ l.6: type BBNN = bool -> bool & number -> number //│ ╙── ^^^^ //│ res: BBNN // * These tests show that we currently throw away information when constraining LHS overloading sets: IISS : int -> int //│ res: int -> int IISS : (0 | 1) -> number //│ res: (0 | 1) -> number IISS : 'a -> 'a //│ res: ('a & (int | string)) -> (int | string | 'a) IISS 0 //│ res: int | string (IISS : int -> int) 0 //│ res: int (if true then IISS else BBNN) 0 //│ res: bool | number | string def f = fun x -> (if true then IISS else BBNN) x //│ f: int -> (bool | number | string) f(0) //│ res: bool | number | string :e f(0) + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.80: f(0) + 1 //│ ║ ^^^^^^ //│ ╟── type `bool` is not an instance of type `int` //│ ║ l.13: def BBNN: bool -> bool & number -> number //│ ║ ^^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.80: f(0) + 1 //│ ╙── ^^^^ //│ res: error | int :e f : int -> number //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.93: f : int -> number //│ ║ ^ //│ ╟── type `bool` is not an instance of type `number` //│ ║ l.13: def BBNN: bool -> bool & number -> number //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.93: f : int -> number //│ ╙── ^^^^^^ //│ res: int -> number :e f : number -> int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.106: f : number -> int //│ ║ ^ //│ ╟── type `number` does not match type `int | string` //│ ║ l.106: f : number -> int //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.73: def f = fun x -> (if true then IISS else BBNN) x //│ ╙── ^ //│ res: number -> int if true then IISS else BBNN //│ res: bool -> bool & number -> number | int -> int & string -> string (if true then IISS else ZZII) : int -> int //│ res: int -> int (if true then IISS else BBNN) : (0 | 1) -> number //│ res: (0 | 1) -> number :e (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.129: (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `true` does not match type `int | string` //│ ║ l.129: (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ╙── ^^^^ //│ res: (0 | 1 | true) -> number // * Note that type normalization used to be very aggressive at approximating non-tag type negations, // * to simplify the result, but this was changed as it was unsound def test: ~(int -> int) //│ test: ~(int -> int) // * See also test file BooleanFail.mls about this previous unsoundness :e test = 42 not test //│ 42 //│ <: test: //│ ~(int -> int) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.148: not test //│ ║ ^^^^^^^^ //│ ╟── type `~(int -> int)` is not an instance of type `bool` //│ ║ l.142: def test: ~(int -> int) //│ ║ ^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `bool` //│ ║ l.148: not test //│ ╙── ^^^^ //│ res: bool | error def test: ~(int -> int) & ~bool //│ test: ~bool & ~(int -> int) def test: ~(int -> int) & bool //│ test: bool def test: ~(int -> int) & ~(bool -> bool) //│ test: ~(nothing -> (bool | int)) def test: ~(int -> int | bool -> bool) //│ test: ~(nothing -> (bool | int)) def test: ~(int -> int & string -> string) & ~(bool -> bool & number -> number) //│ test: in ~(nothing -> (number | string) & int -> number & nothing -> (bool | string) & nothing -> (bool | int)) out ~(nothing -> (bool | int) & nothing -> (bool | string) & int -> number & nothing -> (number | string)) ================================================ FILE: shared/src/test/diff/fcp/Overloads_Precise.mls ================================================ :NoJS :NoApproximateOverload type IISS = int -> int & string -> string type BBNN = bool -> bool & number -> number type ZZII = 0 -> 0 & int -> int //│ Defined type alias IISS //│ Defined type alias BBNN //│ Defined type alias ZZII def IISS: int -> int & string -> string def BBNN: bool -> bool & number -> number def ZZII: 0 -> 0 & int -> int //│ IISS: int -> int & string -> string //│ BBNN: bool -> bool & number -> number //│ ZZII: 0 -> 0 & int -> int IISS : IISS //│ res: IISS IISS : int -> int & string -> string //│ res: int -> int & string -> string IISS : IISS | BBNN //│ res: BBNN | IISS :e IISS : ZZII //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.30: IISS : ZZII //│ ║ ^^^^ //│ ╟── type `int -> int & string -> string` is not an instance of `0 -> 0` //│ ║ l.12: def IISS: int -> int & string -> string //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `0 -> 0` //│ ║ l.30: IISS : ZZII //│ ║ ^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.7: type ZZII = 0 -> 0 & int -> int //│ ║ ^^^^^^ //│ ╟── from type reference: //│ ║ l.30: IISS : ZZII //│ ╙── ^^^^ //│ res: ZZII :e IISS : BBNN //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.49: IISS : BBNN //│ ║ ^^^^ //│ ╟── type `int -> int & string -> string` is not an instance of `bool -> bool` //│ ║ l.12: def IISS: int -> int & string -> string //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `bool -> bool` //│ ║ l.49: IISS : BBNN //│ ║ ^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.6: type BBNN = bool -> bool & number -> number //│ ║ ^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.49: IISS : BBNN //│ ╙── ^^^^ //│ res: BBNN // * These tests show that we currently throw away information when constraining LHS overloading sets: IISS : int -> int //│ res: int -> int IISS : (0 | 1) -> number //│ res: (0 | 1) -> number IISS : 'a -> 'a //│ res: 'a -> 'a //│ where //│ [-'a, +'a] in {[int, int], [string, string]} IISS 0 //│ res: int (IISS : int -> int) 0 //│ res: int (if true then IISS else BBNN) 0 //│ res: number // * Note that this is not considered ambiguous // * because the type variable occurrences are polar, // * meaning that the TSCs are always trivially satisfiable // * and thus the code is well-typed. // * Conceptually, we'd expect this inferred type to reduce to `int -> number`, // * but it's tricky to do such simplifications in general. def f = fun x -> (if true then IISS else BBNN) x //│ f: 'a -> 'b //│ where //│ [+'a, -'b] in {[int, int], [string, string]} //│ [+'a, -'b] in {[bool, bool], [number, number]} f(0) //│ res: number :e f(0) + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.106: f(0) + 1 //│ ║ ^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.13: def BBNN: bool -> bool & number -> number //│ ║ ^^^^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.106: f(0) + 1 //│ ╙── ^^^^ //│ res: error | int f : int -> number //│ res: int -> number :e f : number -> int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.122: f : number -> int //│ ║ ^ //│ ╟── type `number` does not match type `?a` //│ ║ l.122: f : number -> int //│ ╙── ^^^^^^ //│ res: number -> int if true then IISS else BBNN //│ res: bool -> bool & number -> number | int -> int & string -> string (if true then IISS else ZZII) : int -> int //│ res: int -> int (if true then IISS else BBNN) : (0 | 1) -> number //│ res: (0 | 1) -> number :e (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.142: (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int -> int & string -> string` is not an instance of `(0 | 1 | true) -> number` //│ ║ l.12: def IISS: int -> int & string -> string //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(0 | 1 | true) -> number` //│ ║ l.142: (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ║ ^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.142: (if true then IISS else BBNN) : (0 | 1 | true) -> number //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: (0 | 1 | true) -> number // * Note that type normalization used to be very aggressive at approximating non-tag type negations, // * to simplify the result, but this was changed as it was unsound def test: ~(int -> int) //│ test: ~(int -> int) // * See also test file BooleanFail.mls about this previous unsoundness :e test = 42 not test //│ 42 //│ <: test: //│ ~(int -> int) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.167: not test //│ ║ ^^^^^^^^ //│ ╟── type `~(int -> int)` is not an instance of type `bool` //│ ║ l.161: def test: ~(int -> int) //│ ║ ^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `bool` //│ ║ l.167: not test //│ ╙── ^^^^ //│ res: bool | error def test: ~(int -> int) & ~bool //│ test: ~bool & ~(int -> int) def test: ~(int -> int) & bool //│ test: bool def test: ~(int -> int) & ~(bool -> bool) //│ test: ~(nothing -> (bool | int)) def test: ~(int -> int | bool -> bool) //│ test: ~(nothing -> (bool | int)) def test: ~(int -> int & string -> string) & ~(bool -> bool & number -> number) //│ test: in ~(nothing -> (number | string) & int -> number & nothing -> (bool | string) & nothing -> (bool | int)) out ~(nothing -> (bool | int) & nothing -> (bool | string) & int -> number & nothing -> (number | string)) ================================================ FILE: shared/src/test/diff/fcp/Paper.mls ================================================ :NoJS :NoRecursiveTypes :NoConstrainedTypes I x = x K x y = x auto x = x x monster = (fun y -> (let tmp = y I in y K)) auto //│ I: 'a -> 'a //│ K: 'a -> anything -> 'a //│ auto: ('a -> 'b & 'a) -> 'b //│ monster: anything -> 'a -> anything -> 'a monster{} //│ res: 'a -> anything -> 'a fun y -> (let tmp = y I in y K) //│ res: ((forall 'a. 'a -> 'a) -> anything & (forall 'b. 'b -> anything -> 'b) -> 'c) -> 'c def bar x = if true then x else id //│ bar: 'a -> ('b -> 'b | 'a) bar succ //│ res: int -> int def k: forall 'a. (forall 'b. 'b -> ('a, 'b)) -> 'a //│ k: (forall 'b. 'b -> ('a, 'b,)) -> 'a :e k (fun x -> (x, x)) + 1 //│ ╔══[ERROR] Type error in operator application //│ ║ l.35: k (fun x -> (x, x)) + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.31: def k: forall 'a. (forall 'b. 'b -> ('a, 'b)) -> 'a //│ ║ ^^ //│ ╟── into type `int` //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this tuple literal: //│ ║ l.35: k (fun x -> (x, x)) + 1 //│ ║ ^^^^^^ //│ ╟── • this reference: //│ ║ l.35: k (fun x -> (x, x)) + 1 //│ ╙── ^ //│ res: error | int def k: (forall 'a. 'a -> 'a) -> int //│ k: (forall 'a. 'a -> 'a) -> int :e (fun f -> k (fun x -> f x)) id //│ ╔══[ERROR] Type error in application //│ ║ l.57: (fun f -> k (fun x -> f x)) id //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.53: def k: (forall 'a. 'a -> 'a) -> int //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.53: def k: (forall 'a. 'a -> 'a) -> int //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.57: (fun f -> k (fun x -> f x)) id //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.57: (fun f -> k (fun x -> f x)) id //│ ╙── ^^^ //│ res: error | int // * Expected error :e auto auto //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.80: auto auto //│ ║ ^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error // * Note: this creates a cyclic bound structure: // * (α32_94 -> α33_95) <: α32_94 <: ((α32_94,) -> α33_95) // * so it is rejected by the occurs-check :e // occurs-check auto auto! //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> 'b //│ <: 'a -> (? & 'b) //│ ╙── //│ res: nothing :NoCycleCheck :RecursiveTypes :e auto auto //│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b. ?b -> ?a <: (forall ?a ?c. ?c -> ?a) -> ?d` exceeded recursion depth limit (250) //│ ║ l.104: auto auto //│ ║ ^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error auto auto! //│ res: nothing auto! auto! //│ res: nothing :e auto! auto //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?d -> ?c) -> ?e` exceeded recursion depth limit (250) //│ ║ l.118: auto! auto //│ ║ ^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error (fun x -> x x) (fun x -> x x)! //│ res: nothing ================================================ FILE: shared/src/test/diff/fcp/PaperTable.mls ================================================ :NoConstrainedTypes :DontDistributeForalls :NoCycleCheck :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Nil[a]: List[a] method Head = error method Tail = this //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] class ST[S, A] method Inv_S: S -> S method Cov_A: A //│ Defined class ST[=S, +A] //│ Declared ST.Inv_S: ST['S, ?] -> 'S -> 'S //│ Declared ST.Cov_A: ST['S, 'A] -> 'A // ============ Type signatures for functions used in the examples ============ def head: List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] // Used to represent `[]` in the papers def nil: List['a] nil = Nil {} //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} def cons: 'a -> List['a] -> List['a] def cons head tail = Cons { head; tail } //│ cons: 'a -> List['a] -> List['a] //│ = //│ 'b -> (forall 'a 'tail. (List['a] & 'tail) -> (Cons['b | 'a] with {head: 'b, tail: 'tail})) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] def is_empty: List['a] -> bool def is_empty l = eq l nil //│ is_empty: List[?] -> bool //│ = //│ anything -> bool //│ <: is_empty: //│ List[?] -> bool //│ = [Function: is_empty] def single: 'a -> List['a] def single x = cons x nil //│ single: 'a -> List['a] //│ = //│ 'a -> List['a] //│ <: single: //│ 'a -> List['a] //│ = [Function: single] def append: List['a] -> List['a] -> List['a] rec def append l1 l2 = if is_empty l1 then l2 else cons (head l1) (append (tail l1) l2) //│ append: List['a] -> List['a] -> List['a] //│ = //│ List['a] -> List['a] -> List['a] //│ <: append: //│ List['a] -> List['a] -> List['a] //│ = [Function: append] def length: List['a] -> int rec def length l = if is_empty l then 0 else succ (length (tail l)) //│ length: List[?] -> int //│ = //│ List[?] -> int //│ <: length: //│ List[?] -> int //│ = [Function: length1] def id: 'a -> 'a def id x = x //│ id: 'a -> 'a //│ = //│ 'a -> 'a //│ <: id: //│ 'a -> 'a //│ = [Function: id1] def ids: List[forall 'a. 'a -> 'a] def ids = single id //│ ids: List[forall 'a. 'a -> 'a] //│ = //│ List[forall 'a. 'a -> 'a] //│ <: ids: //│ List[forall 'a. 'a -> 'a] //│ = [Function: ids] def inc: int -> int def inc = succ //│ inc: int -> int //│ = //│ int -> int //│ <: inc: //│ int -> int //│ = [Function: inc] def choose: 'a -> 'a -> 'a def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a //│ = //│ 'a -> (forall 'b. 'b -> ('a | 'b)) //│ <: choose: //│ 'a -> 'a -> 'a //│ = [Function: choose] def poly: (forall 'a. 'a -> 'a) -> (int, bool) def poly f = (f 1, f true) //│ poly: (forall 'a. 'a -> 'a) -> (int, bool,) //│ = //│ (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ <: poly: //│ (forall 'a. 'a -> 'a) -> (int, bool,) //│ = [Function: poly] def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) def auto x = x x //│ auto: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto: //│ (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b def auto_ x = x x //│ auto_: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto_: //│ (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] def map: ('a -> 'b) -> List['a] -> List['b] rec def map f l = if is_empty l then nil else cons (f (head l)) (map f (tail l)) //│ map: ('a -> 'b) -> List['a] -> List['b] //│ = //│ ('b -> 'a) -> List['b] -> List['a] //│ <: map: //│ ('a -> 'b) -> List['a] -> List['b] //│ = [Function: map] def app: ('a -> 'b) -> 'a -> 'b def app f x = f x //│ app: ('a -> 'b) -> 'a -> 'b //│ = //│ ('a -> 'b) -> 'a -> 'b //│ <: app: //│ ('a -> 'b) -> 'a -> 'b //│ = [Function: app] def revapp: 'a -> ('a -> 'b) -> 'b def revapp x f = f x //│ revapp: 'a -> ('a -> 'b) -> 'b //│ = //│ 'a -> (forall 'b. ('a -> 'b) -> 'b) //│ <: revapp: //│ 'a -> ('a -> 'b) -> 'b //│ = [Function: revapp] :ng def runST: (forall 's. ST['s, 'v]) -> 'v //│ runST: (forall 's. ST['s, 'v]) -> 'v :ng def argST: ST['s, int] //│ argST: ST['s, int] // A9 :ng def f: ('a -> 'a) -> List['a] -> 'a //│ f: ('a -> 'a) -> List['a] -> 'a // C8 :ng def g: List['a] -> List['a] -> 'a //│ g: List['a] -> List['a] -> 'a // E1, E2 :ng def h: int -> (forall 'a. 'a -> 'a) def k: 'a -> List['a] -> 'a def lst: List[forall 'a. int -> 'a -> 'a] //│ h: int -> (forall 'a. 'a -> 'a) //│ k: 'a -> List['a] -> 'a //│ lst: List[forall 'a. int -> 'a -> 'a] // E3 :ng def r: (forall 'a. 'a -> (forall 'b. 'b -> 'b)) -> int //│ r: (anything -> (forall 'b. 'b -> 'b)) -> int type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ Defined type alias ChurchInt type Pair[A, B] = (A, B) //│ Defined type alias Pair[+A, +B] def fst: forall 'a 'b. Pair['a, 'b] -> 'a def fst((x, y)) = x //│ fst: Pair['a, ?] -> 'a //│ = //│ (('a, anything,),) -> 'a //│ <: fst: //│ Pair['a, ?] -> 'a //│ = [Function: fst] def zero: ChurchInt def zero f x = x //│ zero: ChurchInt //│ = //│ anything -> (forall 'a. 'a -> 'a) //│ <: zero: //│ ChurchInt //│ = [Function: zero] def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) def succ (n: ChurchInt) f x = f (n f x) //│ succ: (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = //│ ChurchInt -> (forall 'N 'N0 'a. ('N -> ('N & 'N0) & 'N0 -> 'a) -> ('N & 'N0) -> 'a) //│ <: succ: //│ (forall 'N. ('N -> 'N) -> 'N -> 'N) -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: succ1] // ================================================================ // ======================== Vanilla SuperF ======================== // ================================================================ // ============ A. polymorphic instantiation ============ // A1 fun x -> fun y -> y //│ res: anything -> (forall 'a. 'a -> 'a) //│ = [Function: res] // A2 choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // A3 choose nil ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // A4 fun x -> x x //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // A5 id auto //│ res: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] // A6 id auto_ //│ res: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] // A7 choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> (forall 'b. 'b -> 'b | 'a0) //│ = [Function: id1] // A8 choose id auto_ //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A9 :ng f (choose id) ids //│ res: 'a -> 'a // A10 poly id //│ res: (int, bool,) //│ = [ 1, true ] // A11 poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // A12 id poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // ============ B. inference of polymorphic arguments ============ // B1 fun f -> (f 1, f true) //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // B2 fun xs -> poly (head xs) //│ res: List[forall 'a. 'a -> 'a] -> (int, bool,) //│ = [Function: res] // ============ C. functions on polymorphic lists ============ // C1 length ids //│ res: int //│ = 1 // C2 tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // C3 head ids //│ res: 'a -> 'a //│ = [Function: id1] // C4 single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // C5 cons id ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C6 cons (fun x -> x) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C7 append (single inc) (single id) //│ res: List[int -> int] //│ = Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C8 :ng g (single id) ids //│ res: 'a -> 'a // C9 map poly (single id) //│ res: List[(int, bool,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } // C10 map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ============ D. application functions ============ // D1 app poly id //│ res: (int, bool,) //│ = [ 1, true ] // D2 revapp id poly //│ res: (int, bool,) //│ = [ 1, true ] // D3 :ng runST argST //│ res: int // D4 :ng app runST argST //│ res: int // D5 :ng revapp argST runST //│ res: int // ============ E. η-expansion ============ // E1 :ng k h lst //│ res: int -> (forall 'a. 'a -> 'a) // E2 :ng k (fun x -> h x) lst //│ res: int -> (forall 'a. 'a -> 'a) // E3 :ng r (fun x -> fun y -> y) //│ res: int // ============ F. FreezeML Additions ============ // F5 auto id //│ res: 'b -> 'b //│ = [Function: id1] // F6 cons (head ids) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // F7 (head ids) 3 //│ res: 3 //│ = 3 // F8 choose (head ids) //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // F9 let f = revapp id in f poly //│ res: (int, bool,) //│ = [ 1, true ] // F10 choose id (fun x -> auto_ x) //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // ============ G. SuperF Additions ============ // G1 def z f x = x //│ z: anything -> (forall 'a. 'a -> 'a) //│ = [Function: z] // G2 def s n f x = f (n f x) //│ s: ('a -> 'b -> 'c) -> (forall 'd. ('c -> 'd & 'a) -> 'b -> 'd) //│ = [Function: s] // G3 n3 = s (s (s z)) //│ n3: ('a -> 'b & 'b -> 'c & 'c -> 'd) -> 'a -> 'd //│ = [Function (anonymous)] // G1A z: ChurchInt //│ res: ChurchInt //│ = [Function: z] :e // G2A s: ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in type ascription //│ ║ l.528: s: ChurchInt -> ChurchInt //│ ║ ^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.243: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.243: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.512: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.528: s: ChurchInt -> ChurchInt //│ ╙── ^ //│ res: ChurchInt -> ChurchInt //│ = [Function: s] // G3A n3: ChurchInt //│ res: ChurchInt //│ = [Function (anonymous)] // G4 fst (fst (fst (n3 (fun x -> (x, 0)) 1))) //│ res: 1 //│ = 1 (succ (succ zero)) (succ (succ zero)) //│ res: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] (s (s z)) (s (s z)) //│ res: ('a -> ('a & 'b) & 'b -> ('a & 'b & 'c)) -> 'a -> 'c //│ = [Function (anonymous)] // G7 rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ to_ch: int -> (forall 'a. ('b -> 'a & 'b -> 'b) -> (forall 'c. ('b & 'c) -> ('a | 'c))) //│ = [Function: to_ch] :e // G7A to_ch: int -> ChurchInt //│ ╔══[ERROR] Type error in type ascription //│ ║ l.573: to_ch: int -> ChurchInt //│ ║ ^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.243: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.243: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.512: def s n f x = f (n f x) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.567: rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.573: to_ch: int -> ChurchInt //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.512: def s n f x = f (n f x) //│ ╙── ^^^^^^^^^ //│ res: int -> ChurchInt //│ = [Function: to_ch] :e // G8 rec def id1 x = if true then x else id1 id1 x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.601: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: ?id1` exceeded recursion depth limit (250) //│ ║ l.601: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id11] :e // G9 id1 id1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c. ('d & 'b) -> ('a | 'c) //│ <: 'd -> 'a //│ 'd :> forall 'b 'c. ('d & 'b) -> ('a | 'c) //│ <: 'a & 'e //│ 'e :> forall 'b 'c. ('b & 'e) -> ('f | 'c) //│ <: 'f & 'g //│ 'g :> forall 'b 'c. ('b & 'g) -> ('h | 'c) //│ <: 'h & 'i //│ 'i :> forall 'b 'c. ('b & 'i) -> ('j | 'c) //│ <: 'j & 'k //│ 'k :> forall 'b 'c. ('b & 'k) -> ('l | 'c) //│ <: 'l & 'm //│ 'm :> forall 'b 'c. ('b & 'm) -> ('n | 'c) //│ <: 'n & 'o //│ 'o :> forall 'b 'c. ('b & 'o) -> ('p | 'c) //│ <: 'p & 'q //│ 'q :> forall 'b 'c. ('b & 'q) -> ('r | 'c) //│ <: 'r & 's //│ 's :> forall 'b 'c. ('b & 's) -> ('t | 'c) //│ <: 't & 'u //│ 'u :> forall 'b 'c. ('b & 'u) -> ('v | 'c) //│ <: 'v //│ 'v :> forall 'b 'c. ('b & 'u) -> ('v | 'c) //│ <: 'u -> 'v //│ 't :> forall 'b 'c. ('b & 's) -> ('t | 'c) //│ <: 's -> 't //│ 'r :> forall 'b 'c. ('b & 'q) -> ('r | 'c) //│ <: 'q -> 'r //│ 'p :> forall 'b 'c. ('b & 'o) -> ('p | 'c) //│ <: 'o -> 'p //│ 'n :> forall 'b 'c. ('b & 'm) -> ('n | 'c) //│ <: 'm -> 'n //│ 'l :> forall 'b 'c. ('b & 'k) -> ('l | 'c) //│ <: 'k -> 'l //│ 'j :> forall 'b 'c. ('b & 'i) -> ('j | 'c) //│ <: 'i -> 'j //│ 'h :> forall 'b 'c. ('b & 'g) -> ('h | 'c) //│ <: 'g -> 'h //│ 'f :> forall 'b 'c. ('b & 'e) -> ('f | 'c) //│ <: 'e -> 'f //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c := 'b -> 'c //│ ║ l.601: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Subtyping constraint of the form `forall ?id1. ?id1 <: (forall ?id10. ?id10) -> ?a` exceeded recursion depth limit (250) //│ ║ l.624: id1 id1 //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ = [Function: id11] // Gn :e auto auto //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.681: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^^^^^^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ //│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // Gn+1 :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?d -> ?c) -> ?e` exceeded recursion depth limit (250) //│ ║ l.700: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re // Gn+2 (fun x -> x x) (fun x -> x x)! //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> 'b //│ <: 'a -> (? & 'b) //│ ╙── //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // =========================================================== // ======================== SuperF +D ======================== // =========================================================== :DistributeForalls // ============ A. polymorphic instantiation ============ // A1 fun x -> fun y -> y //│ res: anything -> 'a -> 'a //│ = [Function: res] // A2 choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // A3 choose nil ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // A4 fun x -> x x //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // A5 id auto //│ res: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] // A6 id auto_ //│ res: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] // A7 choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A8 choose id auto_ //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A9 :ng f (choose id) ids //│ res: 'a -> 'a // A10 poly id //│ res: (int, bool,) //│ = [ 1, true ] // A11 poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // A12 id poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // ============ B. inference of polymorphic arguments ============ // B1 fun f -> (f 1, f true) //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // B2 fun xs -> poly (head xs) //│ res: List[forall 'a. 'a -> 'a] -> (int, bool,) //│ = [Function: res] // ============ C. functions on polymorphic lists ============ // C1 length ids //│ res: int //│ = 1 // C2 tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // C3 head ids //│ res: 'a -> 'a //│ = [Function: id1] // C4 single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // C5 cons id ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C6 cons (fun x -> x) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C7 append (single inc) (single id) //│ res: List[int -> int] //│ = Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C8 :ng g (single id) ids //│ res: 'a -> 'a // C9 map poly (single id) //│ res: List[(int, bool,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } // C10 map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ============ D. application functions ============ // D1 app poly id //│ res: (int, bool,) //│ = [ 1, true ] // D2 revapp id poly //│ res: (int, bool,) //│ = [ 1, true ] // D3 :ng runST argST //│ res: int // D4 :ng app runST argST //│ res: int // D5 :ng revapp argST runST //│ res: int // ============ E. η-expansion ============ // E1 :ng k h lst //│ res: int -> 'a -> 'a // E2 :ng k (fun x -> h x) lst //│ res: int -> 'a -> 'a // E3 :ng r (fun x -> fun y -> y) //│ res: int // ============ F. FreezeML Additions ============ // F5 auto id //│ res: 'b -> 'b //│ = [Function: id1] // F6 cons (head ids) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // F7 (head ids) 3 //│ res: 3 //│ = 3 // F8 choose (head ids) //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // F9 let f = revapp id in f poly //│ res: (int, bool,) //│ = [ 1, true ] // F10 choose id (fun x -> auto_ x) //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // ============ G. SuperF Additions ============ // G1 def z f x = x //│ z: anything -> 'a -> 'a //│ = [Function: z1] // G2 def s n f x = f (n f x) //│ s: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: s1] // G3 n3 = s (s (s z)) //│ n3: ('a -> 'b & 'c -> 'a & 'd -> 'c) -> 'd -> 'b //│ = [Function (anonymous)] // G1A z: ChurchInt //│ res: ChurchInt //│ = [Function: z1] // G2A s: ChurchInt -> ChurchInt //│ res: ChurchInt -> ChurchInt //│ = [Function: s1] // G3A n3: ChurchInt //│ res: ChurchInt //│ = [Function (anonymous)] // G4 fst (fst (fst (n3 (fun x -> (x, 0)) 1))) //│ res: 1 //│ = 1 (succ (succ zero)) (succ (succ zero)) //│ res: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] (s (s z)) (s (s z)) //│ res: ('a -> ('a & 'b & 'c) & 'c -> ('a & 'c)) -> 'c -> 'b //│ = [Function (anonymous)] // G7 rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ to_ch: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_ch1] // G7A to_ch: int -> ChurchInt //│ res: int -> ChurchInt //│ = [Function: to_ch1] :e // G8 rec def id1 x = if true then x else id1 id1 x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.1008: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: ?id1` exceeded recursion depth limit (250) //│ ║ l.1008: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id12] :e // G9 id1 id1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c. ('d & 'b) -> ('a | 'c) //│ <: 'd -> 'a //│ 'd :> forall 'b 'c. ('d & 'b) -> ('a | 'c) //│ <: 'a & 'e //│ 'e :> forall 'b 'c. ('b & 'e) -> ('f | 'c) //│ <: 'f & 'g //│ 'g :> forall 'b 'c. ('b & 'g) -> ('h | 'c) //│ <: 'h & 'i //│ 'i :> forall 'b 'c. ('b & 'i) -> ('j | 'c) //│ <: 'j & 'k //│ 'k :> forall 'b 'c. ('b & 'k) -> ('l | 'c) //│ <: 'l & 'm //│ 'm :> forall 'b 'c. ('b & 'm) -> ('n | 'c) //│ <: 'n & 'o //│ 'o :> forall 'b 'c. ('b & 'o) -> ('p | 'c) //│ <: 'p & 'q //│ 'q :> forall 'b 'c. ('b & 'q) -> ('r | 'c) //│ <: 'r & 's //│ 's :> forall 'b 'c. ('b & 's) -> ('t | 'c) //│ <: 't & 'u //│ 'u :> forall 'b 'c. ('b & 'u) -> ('v | 'c) //│ <: 'v //│ 'v :> forall 'b 'c. ('b & 'u) -> ('v | 'c) //│ <: 'u -> 'v //│ 't :> forall 'b 'c. ('b & 's) -> ('t | 'c) //│ <: 's -> 't //│ 'r :> forall 'b 'c. ('b & 'q) -> ('r | 'c) //│ <: 'q -> 'r //│ 'p :> forall 'b 'c. ('b & 'o) -> ('p | 'c) //│ <: 'o -> 'p //│ 'n :> forall 'b 'c. ('b & 'm) -> ('n | 'c) //│ <: 'm -> 'n //│ 'l :> forall 'b 'c. ('b & 'k) -> ('l | 'c) //│ <: 'k -> 'l //│ 'j :> forall 'b 'c. ('b & 'i) -> ('j | 'c) //│ <: 'i -> 'j //│ 'h :> forall 'b 'c. ('b & 'g) -> ('h | 'c) //│ <: 'g -> 'h //│ 'f :> forall 'b 'c. ('b & 'e) -> ('f | 'c) //│ <: 'e -> 'f //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c := 'b -> 'c //│ ║ l.1008: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Subtyping constraint of the form `forall ?id1. ?id1 <: (forall ?id10. ?id10) -> ?a` exceeded recursion depth limit (250) //│ ║ l.1031: id1 id1 //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ = [Function: id12] // Gn :e auto auto //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.1088: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^^^^^^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ //│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // Gn+1 :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: (forall ?c ?d. ?d -> ?c) -> ?e` exceeded recursion depth limit (250) //│ ║ l.1107: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re // Gn+2 (fun x -> x x) (fun x -> x x)! //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> 'b //│ <: 'a -> (? & 'b) //│ ╙── //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // ============================================================== // ======================== SuperF +D+CC ======================== // ============================================================== :CycleCheck // ============ A. polymorphic instantiation ============ // A1 fun x -> fun y -> y //│ res: anything -> 'a -> 'a //│ = [Function: res] // A2 choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // A3 choose nil ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // A4 fun x -> x x //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // A5 id auto //│ res: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] // A6 id auto_ //│ res: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] // A7 choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A8 choose id auto_ //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A9 :ng f (choose id) ids //│ res: 'a -> 'a // A10 poly id //│ res: (int, bool,) //│ = [ 1, true ] // A11 poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // A12 id poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // ============ B. inference of polymorphic arguments ============ // B1 fun f -> (f 1, f true) //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // B2 fun xs -> poly (head xs) //│ res: List[forall 'a. 'a -> 'a] -> (int, bool,) //│ = [Function: res] // ============ C. functions on polymorphic lists ============ // C1 length ids //│ res: int //│ = 1 // C2 tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // C3 head ids //│ res: 'a -> 'a //│ = [Function: id1] // C4 single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // C5 cons id ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C6 cons (fun x -> x) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C7 append (single inc) (single id) //│ res: List[int -> int] //│ = Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C8 :ng g (single id) ids //│ res: 'a -> 'a // C9 map poly (single id) //│ res: List[(int, bool,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } // C10 map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ============ D. application functions ============ // D1 app poly id //│ res: (int, bool,) //│ = [ 1, true ] // D2 revapp id poly //│ res: (int, bool,) //│ = [ 1, true ] // D3 :ng runST argST //│ res: int // D4 :ng app runST argST //│ res: int // D5 :ng revapp argST runST //│ res: int // ============ E. η-expansion ============ // E1 :ng k h lst //│ res: int -> 'a -> 'a // E2 :ng k (fun x -> h x) lst //│ res: int -> 'a -> 'a // E3 :ng r (fun x -> fun y -> y) //│ res: int // ============ F. FreezeML Additions ============ // F5 auto id //│ res: 'b -> 'b //│ = [Function: id1] // F6 cons (head ids) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // F7 (head ids) 3 //│ res: 3 //│ = 3 // F8 choose (head ids) //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // F9 let f = revapp id in f poly //│ res: (int, bool,) //│ = [ 1, true ] // F10 choose id (fun x -> auto_ x) //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // ============ G. SuperF Additions ============ // G1 def z f x = x //│ z: anything -> 'a -> 'a //│ = [Function: z2] // G2 def s n f x = f (n f x) //│ s: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: s2] // G3 n3 = s (s (s z)) //│ n3: ('a -> 'b & 'c -> 'a & 'd -> 'c) -> 'd -> 'b //│ = [Function (anonymous)] // G1A z: ChurchInt //│ res: ChurchInt //│ = [Function: z2] // G2A s: ChurchInt -> ChurchInt //│ res: ChurchInt -> ChurchInt //│ = [Function: s2] // G3A n3: ChurchInt //│ res: ChurchInt //│ = [Function (anonymous)] // G4 fst (fst (fst (n3 (fun x -> (x, 0)) 1))) //│ res: 1 //│ = 1 (succ (succ zero)) (succ (succ zero)) //│ res: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] (s (s z)) (s (s z)) //│ res: ('a -> ('a & 'b & 'c) & 'c -> ('a & 'c)) -> 'c -> 'b //│ = [Function (anonymous)] // G7 rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ to_ch: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_ch2] // G7A to_ch: int -> ChurchInt //│ res: int -> ChurchInt //│ = [Function: to_ch2] :e // G8 rec def id1 x = if true then x else id1 id1 x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.1415: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required //│ ║ l.1415: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id13] :e // G9 id1 id1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c. ('d & 'b) -> ('a | 'c) //│ <: 'd -> 'a //│ 'd :> forall 'b 'c. ('d & 'b) -> ('a | 'c) //│ <: 'a & 'e & 'f //│ 'f :> ('d & 'f) -> ('g | 'a) //│ <: 'd & 'g //│ 'g :> ('d & 'f) -> ('g | 'a) //│ <: 'f -> 'g //│ 'e :> ('d & 'e) -> ('h | 'a) //│ <: 'd & 'h //│ 'h :> ('d & 'e) -> ('h | 'a) //│ <: 'e -> 'h //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c := 'b -> 'c //│ ║ l.1415: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1438: id1 id1 //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ = [Function: id13] // Gn :e auto auto //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.1467: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^^^^^^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ //│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // Gn+1 :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1486: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e :re // Gn+2 (fun x -> x x) (fun x -> x x)! //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> 'b //│ <: 'a -> (? & 'b) //│ ╙── //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // ================================================================= // ======================== SuperF +D+CC+RT ======================== // ================================================================= :RecursiveTypes // ============ A. polymorphic instantiation ============ // A1 fun x -> fun y -> y //│ res: anything -> 'a -> 'a //│ = [Function: res] // A2 choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // A3 choose nil ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // A4 fun x -> x x //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // A5 id auto //│ res: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] // A6 id auto_ //│ res: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] // A7 choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A8 choose id auto_ //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A9 :ng f (choose id) ids //│ res: 'a -> 'a // A10 poly id //│ res: (int, bool,) //│ = [ 1, true ] // A11 poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // A12 id poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // ============ B. inference of polymorphic arguments ============ // B1 fun f -> (f 1, f true) //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // B2 fun xs -> poly (head xs) //│ res: List[forall 'a. 'a -> 'a] -> (int, bool,) //│ = [Function: res] // ============ C. functions on polymorphic lists ============ // C1 length ids //│ res: int //│ = 1 // C2 tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // C3 head ids //│ res: 'a -> 'a //│ = [Function: id1] // C4 single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // C5 cons id ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C6 cons (fun x -> x) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C7 append (single inc) (single id) //│ res: List[int -> int] //│ = Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C8 :ng g (single id) ids //│ res: 'a -> 'a // C9 map poly (single id) //│ res: List[(int, bool,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } // C10 map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ============ D. application functions ============ // D1 app poly id //│ res: (int, bool,) //│ = [ 1, true ] // D2 revapp id poly //│ res: (int, bool,) //│ = [ 1, true ] // D3 :ng runST argST //│ res: int // D4 :ng app runST argST //│ res: int // D5 :ng revapp argST runST //│ res: int // ============ E. η-expansion ============ // E1 :ng k h lst //│ res: int -> 'a -> 'a // E2 :ng k (fun x -> h x) lst //│ res: int -> 'a -> 'a // E3 :ng r (fun x -> fun y -> y) //│ res: int // ============ F. FreezeML Additions ============ // F5 auto id //│ res: 'b -> 'b //│ = [Function: id1] // F6 cons (head ids) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // F7 (head ids) 3 //│ res: 3 //│ = 3 // F8 choose (head ids) //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // F9 let f = revapp id in f poly //│ res: (int, bool,) //│ = [ 1, true ] // F10 choose id (fun x -> auto_ x) //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // ============ G. SuperF Additions ============ // G1 def z f x = x //│ z: anything -> 'a -> 'a //│ = [Function: z3] // G2 def s n f x = f (n f x) //│ s: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: s3] // G3 n3 = s (s (s z)) //│ n3: ('a -> 'b & 'c -> 'a & 'd -> 'c) -> 'd -> 'b //│ = [Function (anonymous)] // G1A z: ChurchInt //│ res: ChurchInt //│ = [Function: z3] // G2A s: ChurchInt -> ChurchInt //│ res: ChurchInt -> ChurchInt //│ = [Function: s3] // G3A n3: ChurchInt //│ res: ChurchInt //│ = [Function (anonymous)] // G4 fst (fst (fst (n3 (fun x -> (x, 0)) 1))) //│ res: 1 //│ = 1 (succ (succ zero)) (succ (succ zero)) //│ res: ('M -> 'M) -> 'M -> 'M //│ = [Function (anonymous)] (s (s z)) (s (s z)) //│ res: ('a -> ('a & 'b & 'c) & 'c -> ('a & 'c)) -> 'c -> 'b //│ = [Function (anonymous)] // G7 rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ to_ch: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_ch3] // G7A to_ch: int -> ChurchInt //│ res: int -> ChurchInt //│ = [Function: to_ch3] // G8 rec def id1 x = if true then x else id1 id1 x //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id14] // G9 id1 id1 //│ res: ('a & 'b) -> 'a //│ where //│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> ('a | 'd) //│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a //│ 'c :> 'c -> 'd //│ <: 'd //│ 'd := 'c -> 'd //│ = [Function: id14] // Gn :e auto auto //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.1814: auto auto //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ║ ^^^^^^^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.163: def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ ╙── ^^ //│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // Gn+1 :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1833: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re // Gn+2 (fun x -> x x) (fun x -> x x)! //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/fcp/PolyParams.mls ================================================ :NoRecursiveTypes def foo(f: forall 'a. 'a -> 'a) = (f 1, f true) //│ foo: (forall 'a. 'a -> 'a) -> (1, true,) //│ = [Function: foo] fooid = foo id //│ fooid: (1, true,) //│ = [ 1, true ] fooid.0 fooid.1 //│ res: 1 //│ = 1 //│ res: true //│ = true def foo(f: (forall 'A. 'A -> 'A) -> (forall 'B. 'B -> 'B)) = id f id (f id) //│ foo: ((forall 'A. 'A -> 'A) -> (forall 'B. 'B -> 'B)) -> 'B0 -> 'B0 //│ = [Function: foo1] foo id //│ res: 'B -> 'B //│ = [Function: id] foo id id //│ res: 'a -> 'a //│ = [Function: id] def bui_ty: forall 'a. (forall 'b. 'a -> 'b) -> () //│ bui_ty: (nothing -> nothing) -> () //│ = // :d bui_ty = bui_ty //│ (nothing -> nothing) -> () //│ <: bui_ty: //│ (nothing -> nothing) -> () //│ = //│ bui_ty is not implemented ================================================ FILE: shared/src/test/diff/fcp/PolymorphicTypeAliases.mls ================================================ :NoJS type F[A] = forall 'a. (A, 'a) -> (A, 'a) //│ Defined type alias F[=A] class Test[B] method M = (fun (x: B, y) -> (x, y)) : F[B] //│ Defined class Test[=B] //│ Defined Test.M: Test['B] -> F['B] class Test2[B] method M = id (fun x -> id (fun (x: B, y) -> (x, y)) : F[B]) //│ Defined class Test2[=B] //│ Defined Test2.M: Test2['B] -> anything -> F['B] def foo = forall 'a. (fun (x: 'a, y) -> (x, y)) : F['a] //│ foo: F['a] type G[A] = forall 'a. (A, 'a) //│ Defined type alias G[+A] def foo x = forall 'a. (x, x) : G['a] //│ foo: (??a & ??a0) -> G[nothing] :e foo 1 //│ ╔══[ERROR] Type error in application //│ ║ l.28: foo 1 //│ ║ ^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.28: foo 1 //│ ║ ^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.24: def foo x = forall 'a. (x, x) : G['a] //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.24: def foo x = forall 'a. (x, x) : G['a] //│ ╙── ^ //│ res: error | G[nothing] def foo = forall 'a. fun x -> (x, x) : G['a] //│ foo: (??a & 'a) -> G['a] :e foo 1 //│ ╔══[ERROR] Type error in application //│ ║ l.48: foo 1 //│ ║ ^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.48: foo 1 //│ ║ ^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.44: def foo = forall 'a. fun x -> (x, x) : G['a] //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.44: def foo = forall 'a. fun x -> (x, x) : G['a] //│ ╙── ^ //│ res: error | G[1] def foo x y = forall 'a. (x, y) : G['a] //│ foo: ??a -> ??a0 -> G[nothing] def foo = forall 'a. fun (x, y) -> (x, y) : G['a] //│ foo: ('a, ??a,) -> G['a] def foo = fun a -> forall 'a. fun (x, y) -> (x, y) : G['a] //│ foo: anything -> ('a, ??a,) -> G['a] foo 1 (2, error) //│ res: G[2] def foo = fun a -> forall 'a. fun x -> (x, error) : G['a] //│ foo: anything -> 'a -> G['a] foo 1 2 //│ res: G[2] ================================================ FILE: shared/src/test/diff/fcp/Proofs.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls // * Negation type Not[A] = A -> nothing //│ Defined type alias Not[-A] // * Disjunction class Left: { v: anything } class Right: { v: anything } type Either[A, B] = Left & { v: A } | Right & { v: B } //│ Defined class Left //│ Defined class Right //│ Defined type alias Either[+A, +B] // * Type of: excluded middle (EM) type EM = forall 'a. Either['a, Not['a]] //│ Defined type alias EM // * Type of double negation elimination (DNE) type DNE = forall 'a. Not[Not['a]] -> 'a //│ Defined type alias DNE // * Proof that EN implies DNE // * In explicit type parameters syntax: // * fun (em: forall[A]. Either[A, Not[A]]) -> // * forall[A]. fun (nna: Not[Not[A]]) -> (em[A] match Left a -> a; Right na -> nna na) def EM_to_DNE em nna = case em of Left -> em.v, Right -> nna em.v //│ EM_to_DNE: ((Left with {v: 'v}) | (Right with {v: 'v0})) -> (forall 'a. ('v0 -> 'a) -> ('v | 'a)) //│ = [Function: EM_to_DNE] // * This requires distributivity or retyping with constrained types :e EM_to_DNE: EM -> DNE //│ ╔══[ERROR] Type error in type ascription //│ ║ l.38: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.38: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── • this applied expression: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^ //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ╙── ^^^^ //│ res: EM -> DNE //│ = [Function: EM_to_DNE] // * Proof that EN implies DNE, annotated def EM_to_DNE_A0 (em: EM) nna = case em of Left -> em.v, Right -> nna em.v //│ EM_to_DNE_A0: EM -> (forall 'v. (Not['v] -> 'v) -> 'v) //│ = [Function: EM_to_DNE_A0] EM_to_DNE_A0: EM -> DNE //│ res: EM -> DNE //│ = [Function: EM_to_DNE_A0] // * This one still requires distributivity or constrained types def EM_to_DNE_A1 em = let poly (nna: Not[Not['A]]) = case em of Left -> em.v, Right -> nna em.v in poly //│ EM_to_DNE_A1: ((Left with {v: 'v}) | Right & {v: Not['A]}) -> Not[Not['A]] -> 'v //│ = [Function: EM_to_DNE_A1] :e EM_to_DNE_A1: EM -> DNE //│ ╔══[ERROR] Type error in type ascription //│ ║ l.85: EM_to_DNE_A1: EM -> DNE //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.80: in poly //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.85: EM_to_DNE_A1: EM -> DNE //│ ║ ^^^^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.79: let poly (nna: Not[Not['A]]) = case em of Left -> em.v, Right -> nna em.v //│ ╙── ^^^^ //│ res: EM -> DNE //│ = [Function: EM_to_DNE_A1] // * Proof that DNE implies EM // * In explicit type parameters syntax: // * (dne: forall[A]. Not[Not[A]] -> A) -> forall[A]. // * dne[Either[A, Not[A]]] (fun (not_em: Not[Either[A, Not[A]]]) -> // * not_em (Right (fun a -> n_em (Left a))) // * ) def DNE_to_EM dne = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ DNE_to_EM: ((forall 'v 'a 'b. ((Left with {v: 'v}) -> 'a & (Right & {v: 'v -> 'a}) -> 'b) -> 'b) -> 'c) -> 'c //│ = [Function: DNE_to_EM] // * Requires distributivity because we currently don't generalize function bodies :e DNE_to_EM: DNE -> EM //│ ╔══[ERROR] Type error in type ascription //│ ║ l.122: DNE_to_EM: DNE -> EM //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a0` //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.122: DNE_to_EM: DNE -> EM //│ ║ ^^^^^^^^^ //│ ╟── • this application: //│ ║ l.116: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ╙── ^^^^^^^^^^^^^^ //│ res: DNE -> EM //│ = [Function: DNE_to_EM] def DNE_to_EM_let dne = let poly = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) in poly //│ DNE_to_EM_let: ((forall 'v 'a 'b. ((Left with {v: 'v}) -> 'a & (Right & {v: 'v -> 'a}) -> 'b) -> 'b) -> 'c) -> 'c //│ = [Function: DNE_to_EM_let] // * This one used to work because we used let polymorphism // * Stopped working after "sound extrusion", but works with distrib (see below) :e DNE_to_EM_let: DNE -> EM //│ ╔══[ERROR] Type error in type ascription //│ ║ l.151: DNE_to_EM_let: DNE -> EM //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.143: let poly = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.144: in poly //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.151: DNE_to_EM_let: DNE -> EM //│ ╙── ^^^^^^^^^^^^^ //│ res: DNE -> EM //│ = [Function: DNE_to_EM_let] // * Note: this doesn't help def DNE_to_EM_A0 (dne: DNE) = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ DNE_to_EM_A0: DNE -> ((Left with {v: 'v}) | Right & {v: 'v -> nothing}) //│ = [Function: DNE_to_EM_A0] :e DNE_to_EM_A0: DNE -> EM //│ ╔══[ERROR] Type error in type ascription //│ ║ l.183: DNE_to_EM_A0: DNE -> EM //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a0` //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.183: DNE_to_EM_A0: DNE -> EM //│ ║ ^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.178: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ╙── ^^^^^^^^^^^^^^ //│ res: DNE -> EM //│ = [Function: DNE_to_EM_A0] // * Note: this doesn't help either type EM_of[A] = Either[A, Not[A]] //│ Defined type alias EM_of[=A] def DNE_to_EM_A1 dne = (dne: Not[Not[EM_of['a]]] -> EM_of['a]) (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ DNE_to_EM_A1: (Not[Not[EM_of['a]]] -> EM_of['a]) -> EM_of['a] //│ = [Function: DNE_to_EM_A1] :e DNE_to_EM_A1: DNE -> EM //│ ╔══[ERROR] Type error in type ascription //│ ║ l.215: DNE_to_EM_A1: DNE -> EM //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a0` //│ ║ l.210: (dne: Not[Not[EM_of['a]]] -> EM_of['a]) (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.210: (dne: Not[Not[EM_of['a]]] -> EM_of['a]) (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.215: DNE_to_EM_A1: DNE -> EM //│ ╙── ^^^^^^^^^^^^ //│ res: DNE -> EM //│ = [Function: DNE_to_EM_A1] // * Note: this doesn't help either def helper: Not[Either['A, Not['A]]] -> 'A -> nothing def helper not_em = fun a -> not_em (Left { v = a }) //│ helper: Not[Either['A, Not['A]]] -> 'A -> nothing //│ = //│ ((Left with {v: 'v}) -> 'a) -> 'v -> 'a //│ <: helper: //│ Not[Either['A, Not['A]]] -> 'A -> nothing //│ = [Function: helper] def DNE_to_EM_A2 (dne: DNE) = dne (fun (not_em: Not[Either['A, Not['A]]]) -> not_em (Right { v = helper not_em })) //│ DNE_to_EM_A2: DNE -> Either['A, Not['A]] //│ = [Function: DNE_to_EM_A2] // * ^ Note how this is not `DNE -> forall 'A. (Either['A, Not['A]] | 'a)`? :e DNE_to_EM_A2: DNE -> EM //│ ╔══[ERROR] Type error in type ascription //│ ║ l.255: DNE_to_EM_A2: DNE -> EM //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a0` //│ ║ l.248: dne (fun (not_em: Not[Either['A, Not['A]]]) -> not_em (Right { v = helper not_em })) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.248: dne (fun (not_em: Not[Either['A, Not['A]]]) -> not_em (Right { v = helper not_em })) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.255: DNE_to_EM_A2: DNE -> EM //│ ╙── ^^^^^^^^^^^^ //│ res: DNE -> EM //│ = [Function: DNE_to_EM_A2] // * === Usages === :NoJS def dne: DNE def em: EM //│ dne: DNE //│ em: EM EM_to_DNE em //│ res: (Not['v] -> 'a) -> ('v | 'a) DNE_to_EM dne //│ res: (Left with {v: 'v}) | Right & {v: 'v -> nothing} EM_to_DNE_A0 em //│ res: (Not['v] -> 'v) -> 'v EM_to_DNE_A1 em //│ res: Not[Not['v]] -> 'v DNE_to_EM_A0 dne //│ res: (Left with {v: 'v}) | Right & {v: 'v -> nothing} DNE_to_EM_A1 dne //│ res: EM_of['a] DNE_to_EM_A2 dne //│ res: Either['A, Not['A]] def to_DNE: DNE to_DNE nna = case em of Left -> em.v, Right -> nna em.v //│ to_DNE: DNE //│ (Not['v] -> 'v) -> 'v //│ <: to_DNE: //│ DNE def to_EM: EM to_EM = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ to_EM: EM //│ (Left with {v: 'v}) | Right & {v: 'v -> nothing} //│ <: to_EM: //│ EM // * === With Distributivity === :DistributeForalls EM_to_DNE: EM -> DNE DNE_to_EM: DNE -> EM DNE_to_EM_let: DNE -> EM //│ res: EM -> DNE //│ res: DNE -> EM //│ res: DNE -> EM EM_to_DNE_A0: EM -> DNE DNE_to_EM_A0: DNE -> EM //│ res: EM -> DNE //│ res: DNE -> EM EM_to_DNE_A1: EM -> DNE //│ res: EM -> DNE DNE_to_EM_A1: DNE -> EM //│ res: DNE -> EM EM_to_DNE em //│ res: (Not['v] -> 'a) -> ('v | 'a) DNE_to_EM dne //│ res: (Left with {v: 'v}) | Right & {v: 'v -> nothing} // * === With Constrained Types === :DontDistributeForalls :ConstrainedTypes // * We still need to retype these definitions :e EM_to_DNE: EM -> DNE //│ ╔══[ERROR] Type error in type ascription //│ ║ l.370: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.370: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ╙── ^^ //│ res: EM -> DNE :e EM_to_DNE: EM -> DNE //│ ╔══[ERROR] Type error in type ascription //│ ║ l.396: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.396: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.32: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ╙── ^^ //│ res: EM -> DNE // * Now retyping them def EM_to_DNE em nna = case em of Left -> em.v, Right -> nna em.v //│ EM_to_DNE: 'a -> (forall 'v 'v0. ('v0 -> 'v) -> 'v //│ where //│ 'a <: (Left with {v: 'v}) | (Right with {v: 'v0})) :e // * Still needs distrib! (after "sound extrusion") EM_to_DNE: EM -> DNE //│ ╔══[ERROR] Type error in type ascription //│ ║ l.431: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.23: type DNE = forall 'a. Not[Not['a]] -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.424: def EM_to_DNE em nna = //│ ║ ^^^^^ //│ ║ l.425: case em of Left -> em.v, Right -> nna em.v //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.431: EM_to_DNE: EM -> DNE //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ╙── ^^ //│ res: EM -> DNE def DNE_to_EM dne = dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ DNE_to_EM: ((forall 'a 'b. ((Right & {v: forall 'v 'c. 'v -> 'c //│ where //│ 'a <: (Left with {v: 'v}) -> 'c}) -> 'b & 'a) -> 'b) -> 'd) -> 'd :e // * Still needs distrib! (after "sound extrusion") DNE_to_EM: DNE -> EM //│ ╔══[ERROR] Type error in type ascription //│ ║ l.462: DNE_to_EM: DNE -> EM //│ ║ ^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.19: type EM = forall 'a. Either['a, Not['a]] //│ ║ ^^ //│ ╟── back into type variable `'a0` //│ ║ l.456: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ║ ^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.462: DNE_to_EM: DNE -> EM //│ ║ ^^^^^^^^^ //│ ╟── • this application: //│ ║ l.456: dne (fun not_em -> not_em (Right { v = fun a -> not_em (Left { v = a }) })) //│ ╙── ^^^^^^^^^^^^^^ //│ res: DNE -> EM // * They work with distrib: :DistributeForalls EM_to_DNE: EM -> DNE DNE_to_EM: DNE -> EM //│ res: EM -> DNE //│ res: DNE -> EM :DontDistributeForalls // * === === === // * [Some old notes, using obsolete notation:] // Type inference for: DNE_to_EM dne // 'fun (('a -> bot) -> bot) -> 'a <: (''fun ((Left ''a -> ''b) & (Right (''a -> ''b) -> ''c)) -> ''c) -> ?d // ((?a -> bot) -> bot) -> ?a <: (''fun ((Left ''a -> ''b) & (Right (''a -> ''b) -> ''c)) -> ''c) -> ?d) // ?a <: ?d AND ''fun ((Left ''a -> ''b) & (Right (''a -> ''b) -> ''c)) -> ''c <: ((?a -> bot) -> bot) // ((Left ?a2 -> ?b) & (Right (?a2 -> ?b) -> ?c)) -> ?c <: ((?a2 -> bot) -> bot) // ?c <: bot AND (?a -> bot) <: (Left ?a2 -> ?b) & (Right (?a2 -> ?b) -> ?c) // (?a -> bot) <: (Left ?a2 -> ?b) AND (?a -> bot) <: (Right (?a2 -> ?b) -> ?c) // Left ?a2 <: ?a AND Right (?a2 -> ?b) <: ?a // Result (generalized): // Left 'a | Right ('a -> Bot) // i.e., the excluded middle for any 'a !! ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Classes.mls ================================================ // * Adaptation of QML's original existentials example; using encoded existentials and classes :NoRecursiveTypes class ArraysRep[A, Rep] method Init: A -> Rep method Sub: Rep -> int -> A method Update: Rep -> int -> A -> Rep method Fold: (A -> 'b -> 'b) -> 'b -> Rep -> 'b //│ Defined class ArraysRep[=A, =Rep] //│ Declared ArraysRep.Init: ArraysRep['A, 'Rep] -> 'A -> 'Rep //│ Declared ArraysRep.Sub: ArraysRep['A, 'Rep] -> 'Rep -> int -> 'A //│ Declared ArraysRep.Update: ArraysRep['A, 'Rep] -> 'Rep -> int -> 'A -> 'Rep //│ Declared ArraysRep.Fold: ArraysRep['A, 'Rep] -> ('A -> 'b -> 'b) -> 'b -> 'Rep -> 'b class ArraysImpl[A, Rep]: ArraysRep[A, Rep] & { init: A -> Rep; sub: Rep -> int -> A; update: Rep -> int -> A -> Rep; fold: forall 'b. (A -> 'b -> 'b) -> 'b -> Rep -> 'b } method Init = this.init method Sub = this.sub method Update = this.update method Fold = this.fold //│ Defined class ArraysImpl[=A, =Rep] //│ Defined ArraysImpl.Init: ArraysImpl['A, 'Rep] -> 'A -> 'Rep //│ Defined ArraysImpl.Sub: ArraysImpl['A, 'Rep] -> 'Rep -> int -> 'A //│ Defined ArraysImpl.Update: ArraysImpl['A, 'Rep] -> 'Rep -> int -> 'A -> 'Rep //│ Defined ArraysImpl.Fold: ArraysImpl['A, 'Rep] -> ('A -> 'b -> 'b) -> 'b -> 'Rep -> 'b type ArraysRepConsumer[A, R] = forall 'rep. ArraysRep[A, 'rep] -> R //│ Defined type alias ArraysRepConsumer[=A, +R] type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ Defined type alias Arrays[=A] baseImpl = ArraysImpl { init = fun a -> a; sub = fun r -> fun (i : int) -> r; update = fun r -> fun (i : int) -> fun a -> a; fold = fun f -> fun b -> fun r -> f r b } //│ baseImpl: ArraysImpl['Rep, 'Rep] with { //│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, //│ init: forall 'd. 'd -> 'd, //│ sub: forall 'e. 'e -> int -> 'e, //│ update: forall 'f. anything -> int -> 'f -> 'f //│ } //│ = ArraysImpl { //│ init: [Function: init], //│ sub: [Function: sub], //│ update: [Function: update], //│ fold: [Function: fold] //│ } def base: Arrays['a] def base f = f baseImpl //│ base: Arrays['a] //│ = //│ ((forall 'Rep. ArraysImpl['Rep, 'Rep] with { //│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, //│ init: forall 'd. 'd -> 'd, //│ sub: forall 'e. 'e -> int -> 'e, //│ update: forall 'f. anything -> int -> 'f -> 'f //│ }) -> 'g) -> 'g //│ <: base: //│ Arrays['a] //│ = [Function: base] // * Notice the strange `anything` and `nothing` occurrences... // * They don't seem to cause problems. Maybe simplification bug artifacts? def simpleStepImpl arrImpl = ArraysImpl { init = fun a -> (arrImpl.Init a, "initialized"); sub = fun ((r0, r1)) -> fun i -> arrImpl.Sub r0 i; update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.Update r0 i a, "updated"); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f b r0 } //│ simpleStepImpl: ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with { //│ fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> (('Rep0, anything,),) -> 'b, //│ init: 'A -> ('Rep, "initialized",), //│ sub: (('Rep0, anything,),) -> int -> 'A0, //│ update: forall 'c. (('Rep0 & 'c, anything,),) -> int -> 'A -> ('Rep | 'c, "updated",) //│ }) //│ where //│ 'Rep1 :> ('Rep | 'd, "initialized" | "updated",) //│ <: ('Rep0 & 'a, anything,) //│ 'a <: 'Rep0 & 'd //│ 'd :> 'Rep //│ <: 'Rep0 & 'a //│ 'A1 :> 'A0 //│ <: 'A //│ = [Function: simpleStepImpl] def simpleStepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ simpleStepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = simpleStepImpl_ty = simpleStepImpl //│ ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with { //│ fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> (('Rep0, anything,),) -> 'b, //│ init: 'A -> ('Rep, "initialized",), //│ sub: (('Rep0, anything,),) -> int -> 'A0, //│ update: forall 'c. (('Rep0 & 'c, anything,),) -> int -> 'A -> ('Rep | 'c, "updated",) //│ }) //│ where //│ 'Rep1 :> ('Rep | 'd, "initialized" | "updated",) //│ <: ('Rep0 & 'a, anything,) //│ 'a <: 'Rep0 & 'd //│ 'd :> 'Rep //│ <: 'Rep0 & 'a //│ 'A1 :> 'A0 //│ <: 'A //│ <: simpleStepImpl_ty: //│ ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] // * This is weird. // * It seems to be the same subtype check as above, but it goes of the rails (see stats) // * Also weird: this type checks efficiently when the file is typed with `:ConstrainedTypes` :e :stats simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b. ?a -> ?b <: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)]` took too many steps and ran out of fuel (10000) //│ ║ l.129: simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ║ ^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] //│ constrain calls : 9926 //│ annoying calls : 78 //│ subtyping calls : 28186 // * Note that the above incidentally can be checked using recursive types :RecursiveTypes :stats simpleStepImpl : ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ res: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] //│ constrain calls : 432 //│ annoying calls : 104 //│ subtyping calls : 3111 :NoRecursiveTypes // * Apparently, it's due to excessive extrusion due to the type annot not being generalized! :stats simpleStepImpl : forall 'a 'r. ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ res: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: simpleStepImpl] //│ constrain calls : 576 //│ annoying calls : 104 //│ subtyping calls : 1183 // * First approach to implementing step def simpleStep: Arrays['a] -> Arrays['a] //│ simpleStep: Arrays['a] -> Arrays['a] //│ = // * Note: this one fails to type check when the file is typed with `:ConstrainedTypes` def simpleStep arr = arr (fun impl -> fun (k: ArraysRepConsumer['a, 'r]) -> k (simpleStepImpl impl)) //│ ((forall 'A 'A0 'A1 'Rep 'a. ArraysRep[in 'A & 'A0 & 'A1 out 'A | 'A0, 'Rep] -> ArraysRepConsumer['A, 'a] -> 'a) -> 'b) -> 'b //│ where //│ 'A :> 'A0 //│ <: 'A1 //│ 'A1 <: 'A //│ <: simpleStep: //│ Arrays['a] -> Arrays['a] //│ = [Function: simpleStep] // * Second approach to implementing step def simpleStep2: Arrays['a] -> Arrays['a] //│ simpleStep2: Arrays['a] -> Arrays['a] //│ = def simpleStep2 arr (k: ArraysRepConsumer['a, 'r]) = arr (fun impl -> k (simpleStepImpl impl)) //│ ((forall 'Rep. ArraysRep['a, 'Rep] -> 'r) -> 'b) -> ArraysRepConsumer['a, 'r] -> 'b //│ <: simpleStep2: //│ Arrays['a] -> Arrays['a] //│ = [Function: simpleStep2] sb = simpleStep base //│ sb: Arrays['a] //│ = [Function (anonymous)] sb (fun arr -> arr.Init true) //│ res: ??Rep //│ = [ true, 'initialized' ] sb (fun arr -> arr.Sub (arr.Init true) 1) //│ res: true //│ = true :e // * Type error is expected – argument order confusion sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.206: sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'Rep` is not an instance of type `int` //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.9: method Sub: Rep -> int -> A //│ ║ ^^^ //│ ╟── from application: //│ ║ l.206: sb (fun arr -> arr.Sub 0 (arr.Init true)) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: quantified type variable 'Rep is defined at: //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── class type parameter Rep is defined at: //│ ║ l.7: class ArraysRep[A, Rep] //│ ╙── ^^^ //│ res: error | true //│ Runtime error: //│ TypeError: number 0 is not iterable (cannot read property Symbol(Symbol.iterator)) sb (fun arr -> arr.Update (arr.Init true) 1 false) //│ res: anything //│ = [ false, 'updated' ] :e // * Rightly prevent skolem confusion sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ╔══[ERROR] Type error in application //│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.193: sb = simpleStep base //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^ //│ ╟── • this reference: //│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ║ ^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.234: sb (fun arr1 -> sb (fun arr2 -> arr2.Update (arr1.Init true))) //│ ╙── ^^^^^^^^^^^^^^ //│ res: error | int -> anything -> anything //│ = [Function (anonymous)] sb (fun arr -> let r2 = arr.Update (arr.Init true) 1 false in (arr.Sub r2 0, arr.Sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ false, false ] // * The unannotated versions of `step` do not work, as expected :e def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ((forall 'A 'A0 'A1 'Rep 'Rep0 'a 'c 'd. ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a out 'Rep | 'Rep0] -> ((forall 'Rep1. ArraysImpl['A1, 'Rep1] with { //│ fold: forall 'b. ('A0 -> 'b -> 'b) -> 'b -> (('Rep0, anything,),) -> 'b, //│ init: 'A -> ('Rep, "initialized",), //│ sub: (('Rep0, anything,),) -> int -> 'A0, //│ update: forall 'e. (('Rep0 & 'e, anything,),) -> int -> 'A -> ('Rep | 'e, "updated",) //│ }) -> 'd) -> 'd) -> 'f) -> 'f //│ where //│ 'Rep1 :> ('Rep | 'c, "initialized" | "updated",) //│ <: ('Rep0 & 'a, anything,) //│ 'a <: 'Rep0 & 'c //│ 'c :> 'Rep //│ <: 'Rep0 & 'a //│ 'A1 :> 'A0 //│ <: 'A //│ <: simpleStep: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.272: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.272: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ║ ^^^ //│ ╟── • this function: //│ ║ l.272: def simpleStep arr = arr (fun impl -> fun k -> k (simpleStepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.81: sub = fun ((r0, r1)) -> fun i -> arrImpl.Sub r0 i; //│ ╙── ^^ //│ = [Function: simpleStep1] :e def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ((ArraysRep[out 'A, out 'Rep] -> 'a) -> 'c) -> ((forall 'Rep0. ArraysImpl[in 'A & 'A0 out 'A0, 'Rep0] with { //│ fold: forall 'b. (nothing -> 'b -> 'b) -> 'b -> (('Rep, anything,),) -> 'b, //│ init: 'A -> (nothing, "initialized",), //│ sub: (('Rep, anything,),) -> int -> nothing, //│ update: forall 'd. (('Rep & 'd, anything,),) -> int -> 'A -> ('d, "updated",) //│ }) -> 'a) -> 'c //│ where //│ 'Rep0 :> ('e | 'f, "initialized" | "updated",) //│ <: ('Rep & 'g & 'h, anything,) //│ 'f :> 'e //│ <: 'Rep & 'g & 'h //│ 'h <: 'Rep & 'f //│ 'e <: 'Rep & 'g //│ 'g <: 'Rep & 'e //│ <: simpleStep2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.308: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.37: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.83: fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f b r0 //│ ║ ^^^^^^^ //│ ╟── • this reference: //│ ║ l.308: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ║ ^^^^ //│ ╟── • this applied expression: //│ ║ l.308: def simpleStep2 arr k = arr (fun impl -> k (simpleStepImpl impl)) //│ ╙── ^^^ //│ = [Function: simpleStep21] // * Now for the real version of `step` :escape def Math: nothing //│ Math: nothing //│ = def div: int -> int -> int def div a b = Math.trunc(a/b) //│ div: int -> int -> int //│ = //│ number -> number -> nothing //│ <: div: //│ int -> int -> int //│ = [Function: div1] def mod: int -> int -> int rec def mod a b = if a < b then a else mod (a - b) b //│ mod: int -> int -> int //│ = //│ int -> int -> int //│ <: mod: //│ int -> int -> int //│ = [Function: mod] def stepImpl arrImpl = ArraysImpl { init = fun a -> (arrImpl.Init a, arrImpl.Init a); sub = fun ((r0, r1)) -> fun i -> if mod i 2 == 0 then arrImpl.Sub r0 (div i 2) else arrImpl.Sub r1 (div i 2); update = fun ((r0, r1)) -> fun i -> fun a -> if mod i 2 == 0 then (arrImpl.Update r0 (div i 2) a, r1) else (r0, arrImpl.Update r1 (div i 2) a); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.Fold f (arrImpl.Fold f b r0) r1 } //│ stepImpl: ArraysRep[in 'A & 'A0 & 'A1 out 'A0 | 'A, in 'Rep & 'Rep0 & 'a & 'c out 'Rep | 'Rep0] -> (ArraysImpl['A1, 'Rep1] with { //│ fold: forall 'b 'b0. ('A0 -> 'b -> 'b & 'A0 -> 'b0 -> ('b & 'b0)) -> ('b & 'b0) -> (('Rep0, 'Rep0,),) -> 'b, //│ init: 'A -> ('Rep, 'Rep,), //│ sub: (('Rep0, 'Rep0,),) -> int -> 'A0, //│ update: forall 'd 'e. (('Rep0 & 'd, 'Rep0 & 'e,),) -> int -> 'A -> ('Rep | 'd, 'Rep | 'e,) //│ }) //│ where //│ 'Rep1 :> ('Rep | 'a | 'f, 'Rep | 'g | 'c,) //│ <: ('Rep0 & 'a, 'Rep0 & 'c,) //│ 'c <: 'Rep0 & 'g //│ 'g :> 'Rep //│ <: 'Rep0 & 'c //│ 'a <: 'Rep0 & 'f //│ 'f :> 'Rep //│ <: 'Rep0 & 'a //│ 'A1 :> 'A0 //│ <: 'A //│ = [Function: stepImpl] def step: Arrays['a] -> Arrays['a] //│ step: Arrays['a] -> Arrays['a] //│ = def step arr = arr (fun impl -> fun (k: ArraysRepConsumer['a, 'r]) -> k (stepImpl impl)) //│ ((forall 'A 'A0 'A1 'Rep 'a. ArraysRep[in 'A & 'A0 & 'A1 out 'A | 'A0, 'Rep] -> ArraysRepConsumer['A, 'a] -> 'a) -> 'b) -> 'b //│ where //│ 'A :> 'A0 //│ <: 'A1 //│ 'A1 <: 'A //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step] def step2: Arrays['a] -> Arrays['a] //│ step2: Arrays['a] -> Arrays['a] //│ = def step2 arr (k: ArraysRepConsumer['a, 'r]) = arr (fun impl -> k (simpleStepImpl impl)) //│ ((forall 'Rep. ArraysRep['a, 'Rep] -> 'r) -> 'b) -> ArraysRepConsumer['a, 'r] -> 'b //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step2] ssb = step (step base) //│ ssb: Arrays['a] //│ = [Function (anonymous)] ssb (fun arr -> let r2 = arr.Update (arr.Init true) 1 false in (arr.Sub r2 0, arr.Sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ true, false ] ssb = step2 (step2 base) //│ ssb: Arrays['a] //│ = [Function (anonymous)] ssb (fun arr -> let r2 = arr.Update (arr.Init true) 1 false in (arr.Sub r2 0, arr.Sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ false, false ] rec def mkMonoArray n = if n == 0 then base else step (mkMonoArray(n - 1)) //│ mkMonoArray: int -> Arrays['a] //│ = [Function: mkMonoArray] snb = mkMonoArray 5 //│ snb: Arrays['a] //│ = [Function (anonymous)] snb (fun arr -> arr.Init true) //│ res: anything //│ = [ //│ [ [ [Array], [Array] ], [ [Array], [Array] ] ], //│ [ [ [Array], [Array] ], [ [Array], [Array] ] ] //│ ] rec def mkMonoArray n = if n == 0 then base else step2 (mkMonoArray(n - 1)) //│ mkMonoArray: int -> Arrays['a] //│ = [Function: mkMonoArray1] snb = mkMonoArray 5 //│ snb: Arrays['a] //│ = [Function (anonymous)] snb (fun arr -> arr.Init true) //│ res: anything //│ = [ [ [ [Array], 'initialized' ], 'initialized' ], 'initialized' ] // * From the paper: // let mkPolyArray n = {∀α(∃ρ.∀β.sig(α,ρ,β))} (mkMonoArray n) ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Classes_min.mls ================================================ // * Some minimized failures :NoRecursiveTypes class ArraysRep[A, Rep] method Update: Rep -> int -> A -> Rep //│ Defined class ArraysRep[-A, =Rep] //│ Declared ArraysRep.Update: ArraysRep['A, 'Rep] -> 'Rep -> int -> 'A -> 'Rep class ArraysImpl[A, Rep]: ArraysRep[A, Rep] & { update: Rep -> int -> A -> Rep } method Update = this.update //│ Defined class ArraysImpl[-A, =Rep] //│ Defined ArraysImpl.Update: ArraysImpl['A, 'Rep] -> 'Rep -> int -> 'A -> 'Rep type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ Defined type alias Arrays[-A] def simpleStepImpl arrImpl = ArraysImpl { update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.Update r0 i a, "updated") } //│ simpleStepImpl: ArraysRep['A, in 'Rep & 'a out 'Rep | 'Rep0] -> (ArraysImpl['A, 'Rep1] with { //│ update: forall 'b. (('Rep0 & 'b, anything,),) -> int -> 'A -> ('Rep | 'b, "updated",) //│ }) //│ where //│ 'Rep1 :> ('c, "updated",) //│ <: ('a, anything,) //│ 'a <: 'Rep0 & 'c //│ 'c :> 'Rep //│ <: 'a //│ = [Function: simpleStepImpl] def arr: Arrays[int] //│ arr: Arrays[int] //│ = def mkArrays: ArraysImpl['a, 'r] -> Arrays['a] mkArrays impl k = k impl //│ mkArrays: ArraysImpl['a, 'r] -> Arrays['a] //│ = //│ 'a -> ('a -> 'b) -> 'b //│ <: mkArrays: //│ ArraysImpl['a, 'r] -> Arrays['a] //│ = [Function: mkArrays] // * Inconsistent bounds on 'Rep after type avoidance during extrusion (<- not sure comment is still current) :e def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ╔══[ERROR] Type error in application //│ ║ l.54: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'Rep` leaks out of its scope //│ ║ l.18: type Arrays[A] = (forall 'Rep. ArraysRep[A, 'Rep] -> 'r) -> 'r //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.54: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.54: def stepped = arr (fun arrImpl -> fun k -> k (simpleStepImpl arrImpl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.23: update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.Update r0 i a, "updated") //│ ╙── ^^ //│ stepped: error //│ = //│ arr is not implemented def stepped = arr (fun arrImpl -> fun (k: forall 'Rep. ArraysRep[int, 'Rep] -> 'r) -> k (simpleStepImpl arrImpl)) //│ stepped: (forall 'Rep. ArraysRep[int, 'Rep] -> 'a) -> 'a //│ = //│ arr is not implemented stepped: Arrays[int] //│ res: Arrays[int] //│ = //│ stepped and arr are not implemented ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Records_D.mls ================================================ // * Adaptation of QML's original existentials example; using encoded existentials and plain records :NoRecursiveTypes // *** Type definitions *** // type ArraysImpl[A, Rep] = { init: A -> Rep; sub: Rep -> int -> A; update: Rep -> int -> A -> Rep; fold: forall 'b. (A -> 'b -> 'b) -> 'b -> Rep -> 'b } //│ Defined type alias ArraysImpl[=A, =Rep] type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ Defined type alias ArraysImplConsumer[=A, +R] type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ Defined type alias Arrays[=A] // *** Base *** // baseImpl = { init = fun a -> a; sub = fun r -> fun i -> r; update = fun r -> fun i -> fun a -> a; fold = fun f -> fun b -> fun r -> f r b } //│ baseImpl: { //│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, //│ init: forall 'd. 'd -> 'd, //│ sub: forall 'e. 'e -> anything -> 'e, //│ update: forall 'f. anything -> anything -> 'f -> 'f //│ } //│ = { //│ init: [Function: init], //│ sub: [Function: sub], //│ update: [Function: update], //│ fold: [Function: fold] //│ } baseImpl : ArraysImpl['a, 'a] //│ res: ArraysImpl['a, 'a] //│ = { //│ init: [Function: init], //│ sub: [Function: sub], //│ update: [Function: update], //│ fold: [Function: fold] //│ } def base: Arrays['a] //│ base: Arrays['a] //│ = // * This works because `f` is used immediately, at the top level // * (not within a more polymorphic context), // * so we do not need first-class parametric polymorphism to type check the definition. def base f = f baseImpl //│ ({ //│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, //│ init: forall 'd. 'd -> 'd, //│ sub: forall 'e. 'e -> anything -> 'e, //│ update: forall 'f. anything -> anything -> 'f -> 'f //│ } -> 'g) -> 'g //│ <: base: //│ Arrays['a] //│ = [Function: base] // * Also works, but not necessary def base (f: ArraysImplConsumer['a, 'res]) = f baseImpl //│ ArraysImplConsumer['a, 'b] -> 'b //│ <: base: //│ Arrays['a] //│ = [Function: base1] // *** Step *** // def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = // * `arrImpl` is meant to be used polymorphically under lambdas, so it needs an annotation def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { init = fun a -> (arrImpl.init a, "hi"); sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.update r0 i a, "hey"); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.fold f b r0 } //│ stepImpl: ArraysImpl['a, 'r] -> { //│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (('r, anything,),) -> 'b, //│ init: 'a -> ('r, "hi",), //│ sub: (('r, anything,),) -> int -> 'a, //│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) //│ } //│ = [Function: stepImpl] stepImpl_ty = stepImpl //│ ArraysImpl['a, 'r] -> { //│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (('r, anything,),) -> 'b, //│ init: 'a -> ('r, "hi",), //│ sub: (('r, anything,),) -> int -> 'a, //│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) //│ } //│ <: stepImpl_ty: //│ ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: stepImpl] def step: Arrays['a] -> Arrays['a] //│ step: Arrays['a] -> Arrays['a] //│ = def helper impl (k: ArraysImplConsumer['a, '_]) = k (stepImpl impl) // * Or equivalently: // def helper impl (k: forall 'rep. ArraysImpl['a, 'rep] -> 'r) = k (stepImpl impl) //│ helper: ArraysImpl['a, 'r] -> ArraysImplConsumer['a, 'b] -> 'b //│ = [Function: helper] // * Note: these versions where we annotate `impl` instead don't work // * (cause problem in later definition of step) // def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) // def helper (impl: ArraysImpl['a, 'rep]) = let f = fun k -> k (stepImpl impl) in f // def helper (impl: ArraysImpl['a, 'rep]) k = k (let tmp = stepImpl impl in tmp) def step arr = arr helper //│ ((forall 'a 'r 'b. ArraysImpl['a, 'r] -> ArraysImplConsumer['a, 'b] -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step] // * Also works, but not necessary def step (arr: Arrays['a]) = arr helper //│ Arrays['a] -> ArraysImplConsumer['a, 'b] -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step1] // * In one go: <------------------------ this is probably the most canonical definition of `step` in this file // * With `stepImpl` def step arr = arr (fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl impl)) //│ ((forall 'a 'r 'b. ArraysImpl['a, 'r] -> ArraysImplConsumer['a, 'b] -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step2] // * With `stepImpl_ty` def step arr = arr (fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_ty impl)) //│ ((forall 'a 'r 'b. ArraysImpl['a, 'r] -> ArraysImplConsumer['a, 'b] -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step3] // * With one more annotation (not needed) def step (arr: Arrays['a0]) = arr (fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl impl)) //│ Arrays['a0] -> ArraysImplConsumer['a0, 'a] -> 'a //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step4] // *** Uses *** // ssb = step (step base) //│ ssb: Arrays['a] //│ = [Function (anonymous)] ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ false, false ] rec def mkMonoArray n = if n == 0 then base else step (mkMonoArray (n - 1)) //│ mkMonoArray: int -> Arrays['a] //│ = [Function: mkMonoArray] snb = mkMonoArray 3 //│ snb: Arrays['a] //│ = [Function (anonymous)] // * Here we are trying to leak the internally-quantified representation, resulting in `anything` being returned snb (fun arr -> arr.init true) //│ res: anything //│ = [ [ [ true, 'hi' ], 'hi' ], 'hi' ] // *** An alternative way of defining Step! *** // // * Use another name to clarify this is an alternative way of defining it def step2: Arrays['a] -> Arrays['a] //│ step2: Arrays['a] -> Arrays['a] //│ = def step2 (arr: Arrays['a]) (k: ArraysImplConsumer['a2, 'rep]) = arr (fun impl -> k (stepImpl impl)) //│ Arrays['a] -> ArraysImplConsumer['a, 'rep] -> 'rep //│ where //│ 'a := 'a0 //│ 'a0 :> 'a //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step21] // * Removing the `arr` annotation still works: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl impl)) //│ ((forall 'r. ArraysImpl['a, 'r] -> 'rep) -> 'b) -> ArraysImplConsumer['a, 'rep] -> 'b //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step22] // * Or just: def step2 arr (k: ArraysImplConsumer['a, 'rep]) = arr (fun impl -> k (stepImpl impl)) //│ ((forall 'r. ArraysImpl[in 'a & 'a0 out 'a, 'r] -> 'rep) -> 'b) -> ArraysImplConsumer['a, 'rep] -> 'b //│ where //│ 'a := 'a0 //│ 'a0 :> 'a //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step23] // *** Uses *** // ssb = step2 (step2 base) //│ ssb: Arrays['a] //│ = [Function (anonymous)] ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ false, false ] rec def mkMonoArray n = if n == 0 then base else step2 (mkMonoArray (n - 1)) //│ mkMonoArray: int -> Arrays['a] //│ = [Function: mkMonoArray1] snb = mkMonoArray 3 //│ snb: Arrays['a] //│ = [Function (anonymous)] // * Here we are trying to leak the internally-quantified representation, resulting in `anything` being returned snb (fun arr -> arr.init true) //│ res: anything //│ = [ [ [ true, 'hi' ], 'hi' ], 'hi' ] // *** Alternative definitions of `step` that do and don't work *** // // * Removing the `k` annotation breaks `step2`, even with `stepImpl_ty`, as before: :e def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ Arrays['a] -> (ArraysImpl['a, in (??rep, string,) out (anything, string,)] -> 'b) -> 'b //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.279: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.17: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.279: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.279: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.86: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╙── ^^ //│ = [Function: step24] // * `step` not work with no annotations (even with stepImpl_ty), // * because that makes `k` leak its argument type :e def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ((forall 'a 'r 'b. ArraysImpl['a, 'r] -> (ArraysImpl['a, ('r, string,)] -> 'b) -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.305: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.17: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.305: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ║ ^^^ //│ ╟── • this function: //│ ║ l.305: def step arr = arr (fun impl -> fun k -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.86: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╙── ^^ //│ = [Function: step5] // * Still doesn't work if we only annotate `arr`, as `k` still leaks the internal repr :e def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ Arrays['a] -> ({ //│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> ((??rep, anything,),) -> 'b, //│ init: 'a -> (anything, "hi",), //│ sub: ((??rep, anything,),) -> int -> 'a, //│ update: ((??rep, anything,),) -> int -> 'a -> (anything, "hey",) //│ } -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.329: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.17: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.329: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.329: def step (arr: Arrays['a]) = arr (fun impl -> fun k -> k (stepImpl impl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.93: sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; //│ ╙── ^^ //│ = [Function: step6] // * Works. Note: removing `forall 'a.` here leads to badness def step arr = arr (forall 'a. fun impl -> forall 'r. fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl impl)) //│ ((forall 'a 'r 'r0. ArraysImpl['a, 'r] -> ArraysImplConsumer['a, 'r0] -> 'r0) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step7] // * ...unless we use `stepImpl_ty` instead of `stepImpl` def step arr = arr (fun impl -> forall 'r. fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_ty impl)) //│ ((forall 'a 'a0 'r 'a1 'r0. ArraysImpl[in 'a & 'a0 out 'a | 'a0, 'r] -> ArraysImplConsumer['a1, 'r0] -> 'r0) -> 'b) -> 'b //│ where //│ 'a1 :> 'a //│ <: 'a0 //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step8] def step arr = arr (fun impl -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ ((forall 'a 'a0 'r. ArraysImpl[in 'a & 'a0 out 'a, 'r] -> Arrays['a0]) -> 'b) -> 'b //│ where //│ 'a :> 'a0 //│ 'a0 := 'a //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step9] def step arr = arr (fun impl -> (fun k -> k (stepImpl_ty impl)) : Arrays['a]) //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step10] def step = forall 'a. fun (arr : Arrays['a]) -> arr (fun impl -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ Arrays['a] -> Arrays['a] //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step11] def step = forall 'a. fun arr -> arr (forall 'rep. fun (impl : ArraysImpl['a, 'rep]) -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ ((forall 'rep. ArraysImpl['a, 'rep] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step12] // * The following `arrayRepToArray` definition can help type check `step`: def arrayRepToArray impl (k: forall 'rep. ArraysImpl['a, 'rep] -> 'r) = k impl //│ arrayRepToArray: ArraysImpl['a, 'rep] -> (forall 'rep0. ArraysImpl['a, 'rep0] -> 'b) -> 'b //│ = [Function: arrayRepToArray] def arrayRepToArray_A : ArraysImpl['a, 'rep] -> Arrays['a] //│ arrayRepToArray_A: ArraysImpl['a, 'rep] -> Arrays['a] //│ = def arrayRepToArray_A impl k = k impl //│ 'a -> ('a -> 'b) -> 'b //│ <: arrayRepToArray_A: //│ ArraysImpl['a, 'rep] -> Arrays['a] //│ = [Function: arrayRepToArray_A] def step arr = arr (fun impl -> arrayRepToArray_A (stepImpl_ty impl)) //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step13] def step arr = arr (fun impl -> arrayRepToArray (stepImpl_ty impl)) //│ ((forall 'a 'r 'b. ArraysImpl['a, 'r] -> (forall 'rep. ArraysImpl['a, 'rep] -> 'b) -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step14] def step (arr: Arrays['a]) = arr (fun impl -> arrayRepToArray_A (stepImpl impl)) //│ Arrays['a] -> Arrays['a] //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step15] def step arr = arr (fun impl -> arrayRepToArray (stepImpl impl)) //│ ((forall 'a 'r 'b. ArraysImpl['a, 'r] -> (forall 'rep. ArraysImpl['a, 'rep] -> 'b) -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step16] // * We can get away with not annotating `arrImpl` as long as we annotate its polymorphic field use: type Fold[A, Rep] = forall 'b. (A -> 'b -> 'b) -> 'b -> Rep -> 'b //│ Defined type alias Fold[+A, -Rep] def stepImpl_Ann = forall 'a 'rep. fun arrImpl -> { init = fun a -> (arrImpl.init a, "hi"); sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.update r0 i a, "hey"); fold = fun f -> fun b -> fun ((r0, r1)) -> (arrImpl.fold: Fold['a, 'rep]) f b r0 } //│ stepImpl_Ann: { //│ fold: Fold['a, 'rep], //│ init: 'c -> 'd, //│ sub: 'e -> 'f -> 'g, //│ update: 'h -> 'i -> 'j -> 'k //│ } -> { //│ fold: forall 'b. ('a -> 'b -> 'b) -> 'b -> (('rep, anything,),) -> 'b, //│ init: 'c -> ('d, "hi",), //│ sub: (('e, anything,),) -> 'f -> 'g, //│ update: (('h, anything,),) -> 'i -> 'j -> ('k, "hey",) //│ } //│ = [Function: stepImpl_Ann] def step arr = arr (fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_Ann impl)) //│ ((forall 'a 'rep 'b. { //│ fold: Fold['a, 'rep], //│ init: 'a -> 'rep, //│ sub: 'rep -> int -> 'a, //│ update: 'rep -> int -> 'a -> 'rep //│ } -> ArraysImplConsumer['a, 'b] -> 'b) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step17] ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Records_ND.mls ================================================ // * Adaptation of QML's original existentials example; using encoded existentials and plain records // * This alternative version is an exploration of how things go when we DON'T have forall distributivity (ie ND) :NoRecursiveTypes :DontDistributeForalls // *** Type definitions *** // type ArraysImpl[A, Rep] = { init: A -> Rep; sub: Rep -> int -> A; update: Rep -> int -> A -> Rep; fold: forall 'b. (A -> 'b -> 'b) -> 'b -> Rep -> 'b } //│ Defined type alias ArraysImpl[=A, =Rep] type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ Defined type alias ArraysImplConsumer[=A, +R] type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ Defined type alias Arrays[=A] // *** Base *** // baseImpl = { init = fun a -> a; sub = fun r -> fun i -> r; update = fun r -> fun i -> fun a -> a; fold = fun f -> fun b -> fun r -> f r b } //│ baseImpl: { //│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, //│ init: forall 'd. 'd -> 'd, //│ sub: forall 'e. 'e -> anything -> 'e, //│ update: anything -> anything -> (forall 'f. 'f -> 'f) //│ } //│ = { //│ init: [Function: init], //│ sub: [Function: sub], //│ update: [Function: update], //│ fold: [Function: fold] //│ } baseImpl : ArraysImpl['a, 'a] //│ res: ArraysImpl['a, 'a] //│ = { //│ init: [Function: init], //│ sub: [Function: sub], //│ update: [Function: update], //│ fold: [Function: fold] //│ } def base: Arrays['a] //│ base: Arrays['a] //│ = // * This works because `f` is used immediately, at the top level // * (not within a more polymorphic context), // * so we do not need first-class parametric polymorphism to type check the definition. def base f = f baseImpl //│ ({ //│ fold: forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c, //│ init: forall 'd. 'd -> 'd, //│ sub: forall 'e. 'e -> anything -> 'e, //│ update: anything -> anything -> (forall 'f. 'f -> 'f) //│ } -> 'g) -> 'g //│ <: base: //│ Arrays['a] //│ = [Function: base] // * Also works, but not necessary def base (f: ArraysImplConsumer['a, 'res]) = f baseImpl //│ ArraysImplConsumer['a, 'b] -> 'b //│ <: base: //│ Arrays['a] //│ = [Function: base1] // *** Step *** // def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = // * `arrImpl` is meant to be used polymorphically under lambdas, so it needs an annotation def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { init = fun a -> (arrImpl.init a, "hi"); sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; update = fun ((r0, r1)) -> fun i -> fun a -> (arrImpl.update r0 i a, "hey"); fold = fun f -> fun b -> fun ((r0, r1)) -> arrImpl.fold f b r0 } //│ stepImpl: ArraysImpl['a, 'r] -> { //│ fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'c. ('b & 'c) -> (('r, anything,),) -> ('c | 'b0)), //│ init: 'a -> ('r, "hi",), //│ sub: (('r, anything,),) -> int -> 'a, //│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) //│ } //│ = [Function: stepImpl] stepImpl_ty = stepImpl //│ ArraysImpl['a, 'r] -> { //│ fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'c. ('b & 'c) -> (('r, anything,),) -> ('c | 'b0)), //│ init: 'a -> ('r, "hi",), //│ sub: (('r, anything,),) -> int -> 'a, //│ update: (('r, anything,),) -> int -> 'a -> ('r, "hey",) //│ } //│ <: stepImpl_ty: //│ ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] //│ = [Function: stepImpl] def step: Arrays['a] -> Arrays['a] //│ step: Arrays['a] -> Arrays['a] //│ = // * --- Using stepImpl --- * // // * Works def helper impl (k: ArraysImplConsumer['a, 'res]) = k (stepImpl impl) //│ helper: ArraysImpl[in 'a & 'a0 out 'a | 'a0, 'r] -> (forall 'a1 'b. ArraysImplConsumer['a1, 'b] -> 'b) //│ where //│ 'a1 :> 'a //│ <: 'a0 //│ = [Function: helper] // * FIXME why does this require so much fuel?! :Fuel 50000 def step (arr: Arrays['a]) = arr helper //│ Arrays['a] -> (forall 'a0 'b. ArraysImplConsumer['a0, 'b] -> 'b) //│ where //│ 'a0 := 'a //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step] :ResetFuel // * It behaves better when we bind 'a at the right place def helper = forall 'a. fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl impl) // * Or equivalently: // def helper = forall 'a. fun impl -> forall 'b. fun (k: ArraysImplConsumer['a, 'b]) -> k (stepImpl impl) //│ helper: ArraysImpl['a, 'r] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ = [Function: helper1] def step arr = arr helper //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b)) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step1] // * In one go: <------------------------ this is probably the most canonical definition of `step` in this file def step arr = arr (forall 'a. fun impl -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl impl)) //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b)) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step2] // * Also kind of works – note that 'res is quantified in the right place (innermost lambda) // * Note that currently, undeclared TVs are not shared between type annots, so we couldn't use 'a in both def helper (impl: ArraysImpl['a, 'rep]) (k: ArraysImplConsumer['b, 'res]) = k (stepImpl impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'b 'c. ArraysImplConsumer['b, 'c] -> 'c) //│ where //│ 'b := 'a //│ = [Function: helper2] // * FIXME this works with `:Fuel 4000000` but takes ~10s!! // * Why require so much fuel? (notably, more than in the same `helper` but *without* the impl annot) // * -> probably due to 'b being generalized too early // * Note [2023-10-06]: // * It seems the fuel might be needed because of TV reconstraining after extrusion, // * which is currently implemented in a very naive and wasteful way! // * Indeed, if we set (includeBounds = true) in the `getVars` method, // * which is used for reconstraining, then this no longer require extra fuel! :e def step (arr: Arrays['a]) = arr helper //│ Arrays['a] -> error //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Subtyping constraint of the form `Arrays['a] <: (forall 'a0 'rep. ArraysImpl['a0, 'rep] -> (forall ?a 'b 'res. ArraysImplConsumer['b, 'res] -> ?a)) -> ?b` took too many steps and ran out of fuel (10000) //│ ║ l.186: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Subtyping constraint of the form `forall 'a ?a. Arrays['a] -> ?a <: forall 'a0. Arrays['a0] -> Arrays['a0]` took too many steps and ran out of fuel (10000) //│ ║ l.186: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: step3] // * With explicit foralls (otherwise each annot FV refers to a different var) // * Note: changing this to `ArraysImplConsumer['b, 'res]` requires similar crazy fuel consumption // * as above, as expected (in fact, I had to use even more `:Fuel 10000000`). // * The issue is probably in some excessive/pathological extrusion behavior due to 'b being too polymorphic // * Removing `'a` in the forall clause has a similar effect but "only" requires `:Fuel 4000000` // * — the difference is probably due to `'a` being rigid or not causing slightly different behaviors def helper = forall 'a 'rep. fun (impl: ArraysImpl['a, 'rep]) -> forall 'res. fun (k: ArraysImplConsumer['a, 'res]) -> k (stepImpl impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'res. ArraysImplConsumer['a, 'res] -> 'res) //│ = [Function: helper3] def step (arr: Arrays['a]) = arr helper //│ Arrays['a] -> (forall 'res. ArraysImplConsumer['a, 'res] -> 'res) //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step4] // * Doesn't work (`'rep` leaks out of its scope in `step`) def helper impl k = k (stepImpl impl) //│ helper: ArraysImpl[in 'a out 'a | 'a0, in 'r out 'r | 'r0] -> (forall 'a1 'r1 'c. ({ //│ fold: forall 'b 'b0. (('a | 'a1) -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> (('r0 & 'r1, anything,),) -> ('d | 'b0)), //│ init: ('a0 & 'a1) -> ('r | 'r1, "hi",), //│ sub: (('r0 & 'r1, anything,),) -> int -> ('a | 'a1), //│ update: (('r0 & 'r1, anything,),) -> int -> ('a0 & 'a1) -> ('r | 'r1, "hey",) //│ } -> 'c) -> 'c) //│ = [Function: helper4] // * Idem def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'c. ({ //│ fold: forall 'b 'b0. ('a -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> (('rep, anything,),) -> ('d | 'b0)), //│ init: 'a -> ('rep, "hi",), //│ sub: (('rep, anything,),) -> int -> 'a, //│ update: (('rep, anything,),) -> int -> 'a -> ('rep, "hey",) //│ } -> 'c) -> 'c) //│ = [Function: helper5] :e def step (arr: Arrays['a]) = arr helper //│ Arrays['a] -> (forall 'c. error | ({ //│ fold: forall 'b 'b0. ('a0 -> 'b -> ('b & 'b0)) -> (forall 'd. ('b & 'd) -> (('rep & 'rep0, anything,),) -> ('d | 'b0)), //│ init: ('a & 'a0) -> ('rep, "hi",), //│ sub: (('rep & 'rep0, anything,),) -> int -> 'a0, //│ update: (('rep & 'rep0, anything,),) -> int -> ('a & 'a0) -> ('rep, "hey",) //│ } -> 'c) -> 'c) //│ where //│ 'rep :> anything //│ <: 'rep0 //│ 'rep0 <: ??rep & 'rep //│ 'a <: 'a0 //│ 'a0 := 'a //│ <: step: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in application //│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.19: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.230: def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^ //│ ╟── • this reference: //│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.93: def stepImpl (arrImpl: ArraysImpl['a, 'r]) = { //│ ╙── ^^ //│ ╔══[ERROR] Type error in def definition //│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.19: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.230: def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl impl) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^^^^ //│ ╟── • this reference: //│ ║ l.240: def step (arr: Arrays['a]) = arr helper //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.95: sub = fun ((r0, r1)) -> fun i -> arrImpl.sub r0 i; //│ ╙── ^^ //│ = [Function: step5] // * An alternative way, and in one go: def step arr = arr (fun impl -> (fun k -> k (stepImpl_ty impl)) : Arrays['a]) //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step6] def step arr = arr (fun impl -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step7] def step = forall 'a. fun arr -> arr (fun impl -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ ((forall 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step8] // * This one is wrong (notice the extrusion in the type): def step_ arr = forall 'a. arr (fun (impl : ArraysImpl['a, 'rep]) -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ step_: ((forall 'rep. ArraysImpl[in ??a out ??a0, 'rep] -> Arrays[in ??a out ??a0]) -> 'a) -> 'a //│ = [Function: step_] def step = forall 'a. fun arr -> arr (fun (impl : ArraysImpl['a, 'rep]) -> (fun k -> k (stepImpl impl)) : Arrays['a]) //│ ((forall 'rep. ArraysImpl['a, 'rep] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step9] // * --- Using stepImpl_ty --- * // // * Still doesn't work (`'rep` leaks out of its scope in `step`) – expected def helper impl k = k (stepImpl_ty impl) //│ helper: ArraysImpl[in 'a out 'a | 'a0, in 'r out 'r | 'r0] -> (forall 'a1 'r1 'b. (ArraysImpl['a1, ('r1, string,)] -> 'b) -> 'b) //│ where //│ 'r1 :> 'r //│ <: 'r0 //│ 'a1 :> 'a //│ <: 'a0 //│ = [Function: helper6] // * Idem – expected, as `k` needs an annot to avoid leaking its argument's type def helper (impl: ArraysImpl['a, 'rep]) k = k (stepImpl_ty impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'b. (ArraysImpl['a, ('rep, string,)] -> 'b) -> 'b) //│ = [Function: helper7] // * Idem – this is a bit surprising, given that it works with the plain `stepImpl`! (tho with much fuel) def helper impl (k: ArraysImplConsumer['a2, 'res]) = k (stepImpl_ty impl) //│ helper: ArraysImpl[in 'a & 'a0 out 'a | 'a0, 'r] -> (forall 'a2 'b. ArraysImplConsumer['a2, 'b] -> 'b) //│ where //│ 'a2 :> 'a //│ <: 'a0 //│ = [Function: helper8] // * Now this works – so the problem was 'a2 not being generalized at the right place def helper = forall 'a2. fun impl -> fun (k: ArraysImplConsumer['a2, 'res]) -> k (stepImpl_ty impl) //│ helper: ArraysImpl['a2, 'r] -> (forall 'a. ArraysImplConsumer['a2, 'a] -> 'a) //│ = [Function: helper9] def step arr = arr helper //│ ((forall 'a2 'r. ArraysImpl['a2, 'r] -> (forall 'a. ArraysImplConsumer['a2, 'a] -> 'a)) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step10] // * Also works def helper (impl: ArraysImpl['a, 'rep]) (k: ArraysImplConsumer['a2, 'res]) = k (stepImpl_ty impl) // def helper (impl: ArraysImpl['a, 'rep]) (k: ArraysImplConsumer['a, 'rep]) = k (stepImpl_ty impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ = [Function: helper10] // * Why so much? – probably the same reason as above ('a2 instead of the same 'a) :Fuel 500000 def step arr = arr helper //│ ((forall 'a 'rep. ArraysImpl['a, 'rep] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b)) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step11] :ResetFuel // * Note that `:GeneralizeCurriedFunctions` is enabled here because of `:DontDistributeForalls`, // * so this version still generalizes the innermost lambda: def helper = forall 'a 'rep. fun (impl: ArraysImpl['a, 'rep]) -> fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_ty impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ = [Function: helper11] def step arr = arr helper //│ ((forall 'a 'rep. ArraysImpl['a, 'rep] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b)) -> 'c) -> 'c //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step12] :DontGeneralizeCurriedFunctions // * Note: could also force explicit generalization with `forall.` def helper = forall 'a 'rep. fun (impl: ArraysImpl['a, 'rep]) -> forall. fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_ty impl) //│ helper: ArraysImpl['a, 'rep] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ = [Function: helper12] // * Note: or could also let-bind the lambda to force generalization def helper = forall 'a 'rep. fun (impl: ArraysImpl['a, 'rep]) -> let res = fun (k: ArraysImplConsumer['a, 'r]) -> k (stepImpl_ty impl) in res //│ helper: ArraysImpl['a, 'rep] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ = [Function: helper13] :GeneralizeCurriedFunctions // * Interesting alternative to annotating `k` that also works: // * Works def helper = forall 'a 'rep. fun (impl: ArraysImpl['a, 'rep]) -> (fun k -> k (stepImpl_ty impl)) : Arrays['a] //│ helper: ArraysImpl['a, 'rep] -> Arrays['a] //│ = [Function: helper14] // * Works def helper = forall 'a. fun impl -> (fun k -> k (stepImpl_ty impl)) : Arrays['a] //│ helper: ArraysImpl['a, 'r] -> Arrays['a] //│ = [Function: helper15] def step arr = arr helper //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step13] // * Works // * Note the slight difference in type with above due to 'a being generalized at a different place: def helper impl = (fun k -> k (stepImpl_ty impl)) : Arrays['a] //│ helper: ArraysImpl['a, 'r] -> Arrays['a] //│ = [Function: helper16] helper : ArraysImplConsumer['a, 'r] //│ res: ArraysImplConsumer['a, Arrays['a]] //│ = [Function: helper16] def step arr = arr helper //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step14] // * In one go: def step arr = arr (fun impl -> (fun k -> k (stepImpl_ty impl)) : Arrays['a]) //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step15] def step arr = arr helper //│ ((forall 'a 'r. ArraysImpl['a, 'r] -> Arrays['a]) -> 'b) -> 'b //│ <: step: //│ Arrays['a] -> Arrays['a] //│ = [Function: step16] // *** Uses *** // ssb = step (step base) //│ ssb: Arrays['a] //│ = [Function (anonymous)] ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ false, false ] rec def mkMonoArray n = if n == 0 then base else step (mkMonoArray (n - 1)) //│ mkMonoArray: int -> (forall 'a. Arrays['a]) //│ = [Function: mkMonoArray] snb = mkMonoArray 3 //│ snb: Arrays['a] //│ = [Function (anonymous)] // * Here we are trying to leak the internally-quantified representation, resulting in extruded types being returned snb (fun arr -> arr.init true) //│ res: anything //│ = [ [ [ true, 'hi' ], 'hi' ], 'hi' ] // *** An alternative way of defining Step! *** // // * Use another name to clarify this is an alternative way of defining it def step2: Arrays['a] -> Arrays['a] //│ step2: Arrays['a] -> Arrays['a] //│ = // * Using `stepImpl` def step2 = forall 'a. fun (arr: Arrays['a]) -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl impl)) //│ Arrays['a] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step21] // * Using `stepImpl_ty` def step2 = forall 'a. fun (arr: Arrays['a]) -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) //│ Arrays['a] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step22] // * Being a bit more explicit about generalization points: def step2 = forall 'a. fun (arr: Arrays['a]) -> forall 'res. fun (k: forall 'rep. ArraysImpl['a, 'rep] -> 'res) -> arr (fun impl -> k (stepImpl_ty impl)) //│ Arrays['a] -> (forall 'res. (forall 'rep. ArraysImpl['a, 'rep] -> 'res) -> 'res) //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step23] // * Or being less explicit: removing `forall 'a` and using `stepImpl`: def step2 (arr: Arrays['a]) (k: ArraysImplConsumer['a2, 'rep]) = arr (fun impl -> k (stepImpl impl)) //│ Arrays['a] -> (forall 'b. ArraysImplConsumer['a, 'b] -> 'b) //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ = [Function: step24] // * Removing either annotation breaks it: :e def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ Arrays['a] -> (forall 'b. (ArraysImpl['a, in (??rep, string,) out (anything, string,)] -> 'b) -> 'b) //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.534: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.19: type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.534: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.534: def step2 = forall 'a. fun (arr: Arrays['a]) -> fun k -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.88: def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ ╙── ^^ //│ = [Function: step25] :e def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) // * Or equivalently: // def step = forall 'a. fun arr -> forall 'rep. fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) //│ ((forall 'r. ArraysImpl['a, 'r] -> 'rep) -> 'b) -> ArraysImplConsumer['a, 'rep] -> 'b //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.557: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.22: type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ ║ ^^ //│ ╟── back into type variable `'r` //│ ║ l.22: type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.557: def step2 = forall 'a. fun arr -> fun (k: ArraysImplConsumer['a, 'rep]) -> arr (fun impl -> k (stepImpl_ty impl)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ = [Function: step26] // * FIXME why is this displayed as `'a <: nothing`? Simplification bug? def s arr (k: ArraysImplConsumer['a, 'rep]) = arr (fun impl -> k (stepImpl_ty impl)) //│ s: ((forall 'r. ArraysImpl['a, 'r] -> 'rep) -> 'b) -> ArraysImplConsumer['a, 'rep] -> 'b //│ = [Function: s] // * We can see it shouldn't be simplified to nothing: :ns s //│ res: forall 'b 'a 'a0 'rep 'c. 'b -> (forall 'a1 'rep0 'd. ArraysImplConsumer['a1, 'rep0] -> 'd) //│ where //│ 'd :> 'c //│ 'rep0 <: 'rep //│ 'a1 :> 'a0 //│ <: 'a //│ 'b <: (forall 'r 'e 'a2 'f. 'e -> 'f) -> 'c //│ 'f :> 'rep //│ 'e <: ArraysImpl['a2, 'r] //│ 'a2 :> 'a //│ <: 'a0 //│ 'a <: 'a0 //│ 'a0 <: 'a //│ = [Function: s] // * BTW: this doesn't work (same reason as before: k needs an annotation) :e step2 = s //│ ((forall 'r. ArraysImpl['a, 'r] -> 'rep) -> 'b) -> ArraysImplConsumer['a, 'rep] -> 'b //│ <: step2: //│ Arrays['a] -> Arrays['a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.603: step2 = s //│ ║ ^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.22: type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ ║ ^^ //│ ╟── back into type variable `'r` //│ ║ l.22: type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.603: step2 = s //│ ║ ^ //│ ╟── • this application: //│ ║ l.580: def s arr (k: ArraysImplConsumer['a, 'rep]) = arr (fun impl -> k (stepImpl_ty impl)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ = [Function: s] // *** Uses 2 *** // ssb = step2 (step2 base) //│ ssb: Arrays['a] //│ = [Function (anonymous)] ssb (fun arr -> let r2 = arr.update (arr.init true) 1 false in (arr.sub r2 0, arr.sub r2 1) ) //│ res: (Bool, Bool,) //│ = [ false, false ] rec def mkMonoArray n = if n == 0 then base else step2 (mkMonoArray (n - 1)) //│ mkMonoArray: int -> (forall 'a. Arrays['a]) //│ = [Function: mkMonoArray1] snb = mkMonoArray 3 //│ snb: Arrays['a] //│ = [Function (anonymous)] // * Here we are trying to leak the internally-quantified representation, resulting in extruded types being returned snb (fun arr -> arr.init true) //│ res: anything //│ = [ [ [ true, 'hi' ], 'hi' ], 'hi' ] ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Records_min_1.mls ================================================ // * This is a minimization of the error that happens in `QML_exist_Records` // * when we don't annotate `arrImpl` in `stepImpl`. :NoRecursiveTypes :NoJS type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ Defined type alias ArraysImpl type ArraysImplConsumer[R] = ArraysImpl -> R //│ Defined type alias ArraysImplConsumer[+R] type Arrays = (ArraysImpl -> 'r) -> 'r //│ Defined type alias Arrays def stepImpl (arrImpl: ArraysImpl) = { fold = fun f -> arrImpl.fold f } //│ stepImpl: ArraysImpl -> {fold: forall 'a. 'a -> 'a} def stepImpl_noAnn arrImpl = { fold = fun f -> arrImpl.fold f } //│ stepImpl_noAnn: {fold: 'a -> 'b} -> {fold: 'a -> 'b} def step2: Arrays -> Arrays //│ step2: Arrays -> Arrays def step2 arr k = k (arr stepImpl) //│ ((ArraysImpl -> {fold: forall 'a. 'a -> 'a}) -> 'b) -> ('b -> 'c) -> 'c //│ <: step2: //│ Arrays -> Arrays def step2 arr (k: ArraysImplConsumer['rep]) = k (arr stepImpl) //│ ((ArraysImpl -> {fold: forall 'a. 'a -> 'a}) -> ArraysImpl) -> ArraysImplConsumer['b] -> 'b //│ <: step2: //│ Arrays -> Arrays def step2 (arr: Arrays) k = k (arr stepImpl) //│ Arrays -> ({fold: forall 'a. 'a -> 'a} -> 'b) -> 'b //│ <: step2: //│ Arrays -> Arrays // * Using `stepImpl_noAnn` fails to work, as expected: :e def step2 arr k = k (arr stepImpl_noAnn) //│ ((forall 'a 'b. {fold: 'a -> 'b} -> {fold: 'a -> 'b}) -> 'c) -> ('c -> 'd) -> 'd //│ <: step2: //│ Arrays -> Arrays //│ ╔══[ERROR] Type error in def definition //│ ║ l.48: def step2 arr k = k (arr stepImpl_noAnn) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.9: type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.9: type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.48: def step2 arr k = k (arr stepImpl_noAnn) //│ ║ ^ //│ ╟── • this application: //│ ║ l.48: def step2 arr k = k (arr stepImpl_noAnn) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.22: def stepImpl_noAnn arrImpl = { fold = fun f -> arrImpl.fold f } //│ ╙── ^^^^^^^^^^^^^^ :e def step2 arr (k: ArraysImplConsumer['rep]) = k (arr stepImpl_noAnn) //│ ((forall 'a 'b. {fold: 'a -> 'b} -> {fold: 'a -> 'b}) -> ArraysImpl) -> ArraysImplConsumer['c] -> 'c //│ <: step2: //│ Arrays -> Arrays //│ ╔══[ERROR] Type error in def definition //│ ║ l.73: def step2 arr (k: ArraysImplConsumer['rep]) = k (arr stepImpl_noAnn) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.9: type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.9: type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.73: def step2 arr (k: ArraysImplConsumer['rep]) = k (arr stepImpl_noAnn) //│ ║ ^^^ //│ ╟── • this application: //│ ║ l.22: def stepImpl_noAnn arrImpl = { fold = fun f -> arrImpl.fold f } //│ ╙── ^^^^^^^^^^^^^^ :e def step2 (arr: Arrays) k = k (arr stepImpl_noAnn) //│ Arrays -> ({fold: 'a -> 'a} -> 'b) -> 'b //│ <: step2: //│ Arrays -> Arrays //│ ╔══[ERROR] Type error in def definition //│ ║ l.95: def step2 (arr: Arrays) k = k (arr stepImpl_noAnn) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.9: type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.9: type ArraysImpl = { fold: forall 'b. 'b -> 'b } //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.95: def step2 (arr: Arrays) k = k (arr stepImpl_noAnn) //│ ║ ^ //│ ╟── • this application: //│ ║ l.95: def step2 (arr: Arrays) k = k (arr stepImpl_noAnn) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.22: def stepImpl_noAnn arrImpl = { fold = fun f -> arrImpl.fold f } //│ ╙── ^^^^^^^^^^^^^^ // * A slight variation with `:ConstrainedTypes`, because why not: :ConstrainedTypes type ArraysImpl2[Rep] = { fold: forall 'b. 'b -> Rep -> 'b } //│ Defined type alias ArraysImpl2[-Rep] type Arrays2 = (forall 'Rep. ArraysImpl2['Rep] -> 'r) -> 'r //│ Defined type alias Arrays2 def stepImpl arrImpl = { fold = fun f -> fun r0 -> arrImpl.fold f r0 } //│ stepImpl: 'a -> {fold: forall 'b 'c 'd. 'b -> ('c -> 'd //│ where //│ 'a <: {fold: 'b -> 'c -> 'd})} def step: Arrays2 -> Arrays2 //│ step: Arrays2 -> Arrays2 :e def step arr k = k (arr stepImpl) //│ 'a -> (('b -> 'c) -> 'c //│ where //│ 'a <: (forall 'd. 'd -> { //│ fold: forall 'e 'f 'g. 'e -> ('f -> 'g //│ where //│ 'd <: {fold: 'e -> 'f -> 'g}) //│ }) -> 'b) //│ <: step: //│ Arrays2 -> Arrays2 //│ ╔══[ERROR] Type error in def definition //│ ║ l.142: def step arr k = k (arr stepImpl) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.126: type ArraysImpl2[Rep] = { fold: forall 'b. 'b -> Rep -> 'b } //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.126: type ArraysImpl2[Rep] = { fold: forall 'b. 'b -> Rep -> 'b } //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.132: def stepImpl arrImpl = { fold = fun f -> fun r0 -> arrImpl.fold f r0 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.142: def step arr k = k (arr stepImpl) //│ ║ ^^^^^^^^^^^^ //│ ╟── • this applied expression: //│ ║ l.142: def step arr k = k (arr stepImpl) //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.132: def stepImpl arrImpl = { fold = fun f -> fun r0 -> arrImpl.fold f r0 } //│ ╙── ^^^^^^^^^^^^^^^^^ ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Records_min_2.mls ================================================ // * This creates a funny TV cycle which used to throw off type simplification into a SOF // * ie: // * 'a29_51'' <: 'a29_52'' // * 'a29_52'' :> 'a29_51'' // * whose bounds were inlined in a loop alternating between the positive and negative polarity :NoRecursiveTypes :DontDistributeForalls :NoJS type ArraysImpl[A, Rep] = { init: A -> Rep; sub: Rep -> A } //│ Defined type alias ArraysImpl[=A, =Rep] type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R //│ Defined type alias ArraysImplConsumer[=A, +R] type Arrays[A] = forall 'r. ArraysImplConsumer[A, 'r] -> 'r //│ Defined type alias Arrays[=A] def stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string)] //│ stepImpl_ty: ArraysImpl['a, 'r] -> ArraysImpl['a, ('r, string,)] :ns def helper impl (k: ArraysImplConsumer['a2, 'res]) = k (stepImpl_ty impl) //│ helper: forall 'b 'a 'a0 'r 'r0. 'b -> (forall 'a2 'res 'a1 'c. ArraysImplConsumer['a2, 'res] -> 'c) //│ where //│ 'res <: 'c //│ 'a2 :> 'a //│ <: 'a1 //│ 'a1 :> 'a //│ <: 'a2 & 'a0 //│ 'b <: ArraysImpl[in 'a out 'a0, in 'r out 'r0] //│ 'r0 :> 'r //│ 'a <: 'a0 //│ 'a0 :> 'a // * Used to SOF in simplif: helper //│ res: ArraysImpl[in 'a & 'a0 out 'a | 'a0, 'r] -> (forall 'a2 'b. ArraysImplConsumer['a2, 'b] -> 'b) //│ where //│ 'a2 :> 'a //│ <: 'a0 ================================================ FILE: shared/src/test/diff/fcp/QML_exist_Records_min_3.mls ================================================ :NoRecursiveTypes :DontDistributeForalls :NoJS type ArraysImpl[A] = { sub: A -> A } //│ Defined type alias ArraysImpl[=A] type ArraysImplConsumer[A] = ArraysImpl[A] -> int //│ Defined type alias ArraysImplConsumer[=A] def stepImpl_ty: ArraysImpl['a] -> ArraysImpl['a] //│ stepImpl_ty: ArraysImpl['a] -> ArraysImpl['a] // * There used to be a wrongly-simplified `'a <: nothing` bound here def s arr (k: ArraysImplConsumer['a]) = arr (fun impl -> k (stepImpl_ty impl)) //│ s: ((ArraysImpl['a] -> int) -> 'b) -> ArraysImplConsumer['a] -> 'b // * ...although we could see that it shouldn't be simplified to nothing: :ns s //│ res: forall 'b 'a 'a0 'c. 'b -> (forall 'a1 'd. ArraysImplConsumer['a1] -> 'd) //│ where //│ 'd :> 'c //│ 'a1 :> 'a0 //│ <: 'a //│ 'b <: (forall 'e 'a2 'f. 'e -> 'f) -> 'c //│ 'f :> int //│ 'e <: ArraysImpl['a2] //│ 'a2 :> 'a //│ <: 'a0 //│ 'a <: 'a0 //│ 'a0 <: 'a ================================================ FILE: shared/src/test/diff/fcp/QML_exist_nu.mls ================================================ // * TODO also a GADT version of this where we use `Arrays[A]: ArraysImpl[A, ?]` :NewDefs :DontDistributeForalls // * Also works without this declare module Math { fun trunc: Num -> Int } //│ declare module Math { //│ fun trunc: Num -> Int //│ } fun div(a, b) = Math.trunc(a/b) fun mod(a, b) = if a < b then a else mod(a - b, b) //│ fun div: (Num, Num) -> Int //│ fun mod: (Int, Int) -> Int abstract class ArraysImpl[A, Rep] { fun init: A -> Rep fun sub: (Rep, Int) -> A fun update: (Rep, Int, A) -> Rep fun fold: (Rep, 'b, A -> 'b -> 'b) -> 'b } //│ abstract class ArraysImpl[A, Rep] { //│ fun fold: forall 'b. (Rep, 'b, A -> 'b -> 'b) -> 'b //│ fun init: A -> Rep //│ fun sub: (Rep, Int) -> A //│ fun update: (Rep, Int, A) -> Rep //│ } type ArraysImplConsumer[A, R] = forall 'rep: ArraysImpl[A, 'rep] -> R //│ type ArraysImplConsumer[A, R] = forall 'rep. ArraysImpl[A, 'rep] -> R abstract class Arrays[A] { fun use: ArraysImplConsumer[A, 'res] -> 'res } //│ abstract class Arrays[A] { //│ fun use: forall 'res. ArraysImplConsumer[A, 'res] -> 'res //│ } class BaseImpl[A]() extends ArraysImpl[A, A] { fun init (a) = a fun sub (r, i) = r fun update(r, i, a) = a fun fold (r, b, f) = f(r)(b) } //│ class BaseImpl[A]() extends ArraysImpl { //│ fun fold: forall 'a 'b 'c. ('a, 'b, 'a -> 'b -> 'c) -> 'c //│ fun init: forall 'd. 'd -> 'd //│ fun sub: forall 'e. ('e, anything) -> 'e //│ fun update: forall 'f. (anything, anything, 'f) -> 'f //│ } class StepImpl[A, R](underlying: ArraysImpl[A, R]) extends ArraysImpl[A, [R, R]] { fun init(a) = [underlying.init(a), underlying.init(a)] fun sub([r0, r1], i) = if mod(i, 2) === 0 then underlying.sub(r0, div(i, 2)) else underlying.sub(r1, div(i, 2)) fun update([r0, r1], i, a) = if mod(i, 2) == 0 then [underlying.update(r0, div(i, 2), a), r1] else [r0, underlying.update(r1, div(i, 2), a)] fun fold([r0, r1], b, f) = underlying.fold(r0, underlying.fold(r1, b, f), f) } //│ class StepImpl[A, R](underlying: ArraysImpl[A, R]) extends ArraysImpl { //│ fun fold: forall 'b 'b0. ([R, R], 'b & 'b0, A -> ('b -> ('b & 'b0) & 'b0 -> 'b0)) -> 'b0 //│ fun init: A -> [R, R] //│ fun sub: ([R, R], Eql[0] & Int) -> A //│ fun update: ([R, R], Int, A) -> [R, R] //│ } class Base[A]() extends Arrays[A] { val impl = BaseImpl() fun use(k) = k(impl) } //│ class Base[A]() extends Arrays { //│ val impl: BaseImpl[A] //│ fun use: forall 'a. (BaseImpl[A] -> 'a) -> 'a //│ } class Step[A](from: Arrays[A]) extends Arrays[A] { // * Note: expansion of alias is capture-avoiding of polymorphic levels fun use(k: ArraysImplConsumer[A, 'res]) = from.use of forall 'rep: (impl: ArraysImpl[A, 'rep]) => k(StepImpl(impl)) } //│ class Step[A](from: Arrays[A]) extends Arrays { //│ fun use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res //│ } // * A version with fewer annotations class Step[A](from: Arrays[A]) extends Arrays[A] { fun use(k: ArraysImplConsumer[A, 'res]) = from.use of impl => k(StepImpl(impl)) // * Spelling out the type synonym: fun use': ArraysImplConsumer[A, 'res] -> 'res fun use'(k: forall 'rep: ArraysImpl[A, 'rep] -> 'res) = from.use of impl => k of StepImpl(impl) } //│ class Step[A](from: Arrays[A]) extends Arrays { //│ fun use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res //│ fun use': forall 'res0. ArraysImplConsumer[A, 'res0] -> 'res0 //│ } // * Note: the annotation on `k` is required, otherwise we leak the locally-polymorphic `impl` // * (We don't currently do any bidirectional typing.) :e class Step'[A](from: Arrays[A]) extends Arrays[A] { fun use(k) = from.use of impl => k(StepImpl(impl)) } //│ ╔══[ERROR] Type error in definition of method use //│ ║ l.123: fun use(k) = //│ ║ ^^^^^^^^ //│ ║ l.124: from.use of impl => k(StepImpl(impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.36: type ArraysImplConsumer[A, R] = forall 'rep: ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.124: from.use of impl => k(StepImpl(impl)) //│ ║ ^^^^ //│ ╟── • this signature of member `use`: //│ ║ l.40: fun use: ArraysImplConsumer[A, 'res] -> 'res //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.124: from.use of impl => k(StepImpl(impl)) //│ ╙── ^^^^^^^^ //│ class Step'[A](from: Arrays[A]) extends Arrays { //│ fun use: forall 'a. (StepImpl[A, in ??rep] -> 'a) -> 'a //│ } //│ Syntax error: //│ Unexpected string let ssb = Step(Step(Base())) //│ let ssb: Step['A] //│ ssb //│ = Step {} ssb.use of impl => let r = impl.update(impl.init(true), 1, false) log(r) [impl.sub(r, 0), impl.sub(r, 1)] //│ [Bool, Bool] //│ res //│ = [ true, false ] //│ // Output //│ [ [ true, true ], [ false, true ] ] fun mkMonoArray(n) = if n === 0 then Base() else Step(mkMonoArray(n - 1)) //│ fun mkMonoArray: forall 'A. (Eql[0] & Int) -> (Base['A] | Step['A]) let snb = mkMonoArray(3) //│ let snb: Base['A] | Step['A] //│ snb //│ = Step {} snb.use of impl => let r = impl.update(impl.init(true), 1, false) log(r) //│ () //│ res //│ = undefined //│ // Output //│ [ //│ [ [ true, true ], [ true, true ] ], //│ [ [ false, true ], [ true, true ] ] //│ ] // * Here we are trying to leak the internally-quantified representation, resulting in the `??rep` extrusion snb.use of impl => impl.init(true) // :d //│ anything //│ res //│ = [ //│ [ [ true, true ], [ true, true ] ], //│ [ [ true, true ], [ true, true ] ] //│ ] // * An alternative implementation of Step with the existential opened outside the function. class StepAlt[A](from: Arrays[A]) extends Arrays[A] { val use = from.use of impl => (k: ArraysImplConsumer[A, 'res]) => k(StepImpl(impl)) } //│ class StepAlt[A](from: Arrays[A]) extends Arrays { //│ val use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res //│ } // * With the following, we get "type variable `'rep` leaks out of its scope" :e class StepAlt'[A](from: Arrays[A]) extends Arrays[A] { val use = from.use of impl => k => k(StepImpl(impl)) // * ^ This is because we leak impl's representation to `k` in the local `k =>` lambda, // * which flows to the type of `use`, where it's extruded: // * forall 'r; (StepImpl[A, ??impl] -> 'r) -> 'r // * Interestingly, once we use first-class existentials to extrude things, // * this should start working, because we'll get // * exists impl; forall 'r; (StepImpl[A, impl] -> 'r) -> 'r // * which is a sutbype of the required // * (forall 'rep; ArraysImpl[A, 'rep] -> 'res) -> 'res // * because we can 0-rigidify `impl` and then subtype // * 0. (StepImpl[A, impl] -> 'r) -> 'r <: (forall 'rep; ArraysImpl[A, 'rep] -> 'res) -> 'res // * ie, constraining the parameters and 1-instantiating `forall 'rep`: // * 1. ArraysImpl[A, 'rep] -> 'res <: (StepImpl[A, impl] -> 'r) -> 'r // * which eventually leads to 'rep := impl and 'r := 'res. } //│ ╔══[ERROR] Type error in application //│ ║ l.211: val use = from.use of impl => //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.212: k => k(StepImpl(impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'rep` leaks out of its scope //│ ║ l.36: type ArraysImplConsumer[A, R] = forall 'rep: ArraysImpl[A, 'rep] -> R //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.211: val use = from.use of impl => //│ ║ ^^^^^^^ //│ ║ l.212: k => k(StepImpl(impl)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this signature of member `use`: //│ ║ l.40: fun use: ArraysImplConsumer[A, 'res] -> 'res //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this field selection: //│ ║ l.211: val use = from.use of impl => //│ ╙── ^^^^^^^^ //│ class StepAlt'[A](from: Arrays[A]) extends Arrays { //│ val use: forall 'R 'a. error | (StepImpl[A, 'R] -> 'a) -> 'a //│ } //│ where //│ 'R :> ??rep //│ <: ??rep0 //│ Syntax error: //│ Unexpected string // * An alternative implementation of Step which only allocates one StepImpl per instance! class StepAlt[A](from: Arrays[A]) extends Arrays[A] { // * The explicit `forall 'res` is needed with distributivity turned off val use = forall 'res: from.use of impl => val impl2 = StepImpl(impl) (k: ArraysImplConsumer[A, 'res]) => k(impl2) // * Version with full annotations (not necessary): val use2: ArraysImplConsumer[A, 'res] -> 'res val use2 = forall 'res: from.use of forall 'rr: (impl : ArraysImpl[A, 'rr]) => val impl2 = StepImpl(impl) (k: ArraysImplConsumer[A, 'res]) => k(impl2) } //│ class StepAlt[A](from: Arrays[A]) extends Arrays { //│ val use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res //│ val use2: forall 'res0. ArraysImplConsumer[A, 'res0] -> 'res0 //│ } // * A variation of the above without explicitly binding 'res, so it has to be distributed out :DistributeForalls // * Distributivity is needed here! class StepAlt[A](from: Arrays[A]) extends Arrays[A] { val use = from.use of impl => val impl2 = StepImpl(impl) (k: ArraysImplConsumer[A, 'res]) => k(impl2) } //│ class StepAlt[A](from: Arrays[A]) extends Arrays { //│ val use: forall 'res. (k: ArraysImplConsumer[A, 'res]) -> 'res //│ } // * Works the same: let ssb = StepAlt(StepAlt(Base())) //│ let ssb: StepAlt['A] //│ ssb //│ = StepAlt {} ssb.use of impl => let r = impl.update(impl.init(true), 1, false) log(r) [impl.sub(r, 0), impl.sub(r, 1)] //│ [Bool, Bool] //│ res //│ = [ true, false ] //│ // Output //│ [ [ true, true ], [ false, true ] ] fun mkMonoArray(n) = if n === 0 then Base() else StepAlt(mkMonoArray(n - 1)) //│ fun mkMonoArray: forall 'A. (Eql[0] & Int) -> (forall 'A0. Base['A0] | StepAlt['A]) let snb = mkMonoArray(3) //│ let snb: forall 'A 'A0. Base['A] | StepAlt['A0] //│ snb //│ = StepAlt {} snb.use of impl => let r = impl.update(impl.init(true), 1, false) log(r) //│ () //│ res //│ = undefined //│ // Output //│ [ //│ [ [ true, true ], [ true, true ] ], //│ [ [ false, true ], [ true, true ] ] //│ ] snb.use of impl => impl.init(true) //│ anything //│ res //│ = [ //│ [ [ true, true ], [ true, true ] ], //│ [ [ true, true ], [ true, true ] ] //│ ] ================================================ FILE: shared/src/test/diff/fcp/Rank2.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls // --------- // def test (f: forall 'a. 'a -> 'a) = f //│ test: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ = [Function: test] test(f: forall 'a. 'a -> 'a) = f 1 //│ test: (forall 'a. 'a -> 'a) -> 1 //│ = [Function: test1] test(f: forall 'a. 'a -> 'a) = (f 1, f true) //│ test: (forall 'a. 'a -> 'a) -> (1, true,) //│ = [Function: test2] test id //│ res: (1, true,) //│ = [ 1, true ] test(fun x -> x) //│ res: (1, true,) //│ = [ 1, true ] test(fun x -> let tmp = log x in x) //│ res: (1, true,) //│ = [ 1, true ] //│ // Output //│ 1 //│ true :e test(fun x -> x + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: test(fun x -> x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.19: test(f: forall 'a. 'a -> 'a) = //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.43: test(fun x -> x + 1) //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.19: test(f: forall 'a. 'a -> 'a) = //│ ╙── ^^ //│ res: error | (1, true,) //│ = [ 2, 2 ] :e test succ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.60: test succ //│ ║ ^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.19: test(f: forall 'a. 'a -> 'a) = //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.19: test(f: forall 'a. 'a -> 'a) = //│ ╙── ^^ //│ res: error | (1, true,) //│ = [ 2, 2 ] // --------- // test f = ((f: forall 'a. 'a -> 'a) 1, f 2) //│ test: (forall 'a. 'a -> 'a & 2 -> 'b) -> (1, 'b,) //│ = [Function: test3] def pair x y = test id //│ pair: anything -> anything -> (1, 2,) //│ = [Function: pair] def pair x y = test (fun x -> (let tmp = log x in x)) //│ pair: anything -> anything -> (1, 2,) //│ = [Function: pair1] def test f = pair (f 1) (f true) //│ test: ((1 | true) -> anything) -> (1, 2,) //│ = [Function: test4] test id //│ res: (1, 2,) //│ = [ 1, 2 ] //│ // Output //│ 1 //│ 2 :e test (fun x -> x + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.104: test (fun x -> x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.92: pair (f 1) (f true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.104: test (fun x -> x + 1) //│ ╙── ^ //│ res: error | (1, 2,) //│ = [ 1, 2 ] //│ // Output //│ 1 //│ 2 ================================================ FILE: shared/src/test/diff/fcp/RecExtr.mls ================================================ :NoRecursiveTypes :DontDistributeForalls :DontGeneralizeCurriedFunctions // * Note: funnily-recursive function rec def foo x y = let tmp = foo x y in (x, y,) //│ foo: 'a -> 'b -> ('a, 'b,) //│ = [Function: foo] foo //│ res: 'a -> 'b -> ('a, 'b,) //│ = [Function: foo] def foo_ty: 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ foo_ty: 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ = :e // * Needs distributivity (see at the end) foo_ty = foo //│ 'a -> 'b -> ('a, 'b,) //│ <: foo_ty: //│ 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ╔══[ERROR] Type error in def definition //│ ║ l.22: foo_ty = foo //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.16: def foo_ty: 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.16: def foo_ty: 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.22: foo_ty = foo //│ ║ ^^^ //│ ╟── • this reference: //│ ║ l.8: let tmp = foo x y in (x, y,) //│ ╙── ^ //│ = [Function: foo] :e // * Needs distributivity (see at the end) foo_ty x y = (x, y,) //│ 'a -> 'b -> ('a, 'b,) //│ <: foo_ty: //│ 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ╔══[ERROR] Type error in def definition //│ ║ l.45: foo_ty x y = (x, y,) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.16: def foo_ty: 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.16: def foo_ty: 'a -> (forall 'b. 'b -> ('a, 'b,)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.45: foo_ty x y = (x, y,) //│ ╙── ^ //│ = [Function: foo_ty1] rec def foo2 x y z = let tmp = foo2 x y z in (x, y, z) //│ foo2: 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ = [Function: foo2] def foo2_ty: 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ foo2_ty: 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ = :e foo2_ty = foo2 //│ 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ <: foo2_ty: //│ 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ ╔══[ERROR] Type error in def definition //│ ║ l.75: foo2_ty = foo2 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.70: def foo2_ty: 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.70: def foo2_ty: 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.75: foo2_ty = foo2 //│ ║ ^^^^ //│ ╟── • this reference: //│ ║ l.66: let tmp = foo2 x y z in (x, y, z) //│ ╙── ^ //│ = [Function: foo2] :e // * Needs distributivity (see at the end) foo2_ty x y z = (x, y, z,) //│ 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ <: foo2_ty: //│ 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ ╔══[ERROR] Type error in def definition //│ ║ l.98: foo2_ty x y z = (x, y, z,) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.70: def foo2_ty: 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.70: def foo2_ty: 'a -> (forall 'b. 'b -> (forall 'c. 'c -> ('a, 'b, 'c,))) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.98: foo2_ty x y z = (x, y, z,) //│ ╙── ^ //│ = [Function: foo2_ty1] rec def r x = let tmp = x in x //│ r: 'a -> 'a //│ = [Function: r] rec def x = x //│ x: nothing //│ = [Function: x] :e rec def lr1 = { tail = lr1 } //│ ╔══[ERROR] Inferred recursive type: 'lr1 //│ where //│ 'lr1 :> {tail: 'lr1} //│ ╙── //│ lr1: 'lr1 //│ where //│ 'lr1 :> {tail: 'lr1} //│ = [Function: lr1] :e rec def lr1 = { head = 0; tail = lr1 } //│ ╔══[ERROR] Inferred recursive type: 'lr1 //│ where //│ 'lr1 :> {head: 0, tail: 'lr1} //│ ╙── //│ lr1: 'lr1 //│ where //│ 'lr1 :> {head: 0, tail: 'lr1} //│ = [Function: lr11] // * With distributivity: :DistributeForalls foo_ty = foo //│ 'a -> 'b -> ('a, 'b,) //│ <: foo_ty: //│ 'a -> 'b -> ('a, 'b,) //│ = [Function: foo] foo_ty x y = (x, y,) //│ 'a -> 'b -> ('a, 'b,) //│ <: foo_ty: //│ 'a -> 'b -> ('a, 'b,) //│ = [Function: foo_ty3] foo2_ty = foo2 //│ 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ <: foo2_ty: //│ 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ = [Function: foo2] foo2_ty x y z = (x, y, z,) //│ 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ <: foo2_ty: //│ 'a -> 'b -> 'c -> ('a, 'b, 'c,) //│ = [Function: foo2_ty3] ================================================ FILE: shared/src/test/diff/fcp/SelfAppMin.mls ================================================ :NoRecursiveTypes class L class A: { f: anything } //│ Defined class L //│ Defined class A def eval eval x = case x of L -> 0, A -> eval eval x.f //│ eval: ('a -> 'f -> 'b & 'a) -> ((A with {f: 'f}) | L) -> (0 | 'b) //│ = [Function: eval] :e e = eval eval //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.19: e = eval eval //│ ║ ^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ e: error | (A & {f: A | L} | L) -> 0 //│ = [Function (anonymous)] e (A{f = L{}}) //│ res: 0 | error //│ = 0 :e e = eval eval! //│ ╔══[ERROR] Inferred recursive type: 'f //│ where //│ 'f <: A with {f: 'f} //│ ║ l.13: A -> eval eval x.f //│ ╙── ^^^ //│ e: 'a -> 0 //│ where //│ 'a <: (A with {f: 'a}) | L //│ = [Function (anonymous)] :e e (A{f = L{}}) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {f: A & 'a} //│ ║ l.11: def eval eval x = case x of //│ ╙── ^ //│ res: 0 //│ = 0 :e // * Note: this computation will diverge rec def v = A{f = v} //│ ╔══[ERROR] Inferred recursive type: 'f //│ where //│ 'f :> A with {f: 'f} //│ ╙── //│ v: 'f //│ where //│ 'f :> A with {f: 'f} //│ = [Function: v] :e e v //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {f: A & 'a} //│ ║ l.11: def eval eval x = case x of //│ ╙── ^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.67: e v //│ ║ ^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: 0 | error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :RecursiveTypes // * Note: Expected divergence :re e v //│ res: 0 //│ Runtime error: //│ RangeError: Maximum call stack size exceeded def eval eval x = case x of L -> 0, A -> { x = eval eval x.f } //│ eval: ('a -> 'f -> 'b & 'a) -> ((A with {f: 'f}) | L) -> (0 | {x: 'b}) //│ = [Function: eval1] :e e = eval eval //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.100: e = eval eval //│ ║ ^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ e: error | (A & {f: A | L} | L) -> (0 | {x: 0 | {x: nothing}}) //│ = [Function (anonymous)] :re e v //│ res: 0 | error | {x: 0 | {x: nothing}} //│ Runtime error: //│ RangeError: Maximum call stack size exceeded e = eval eval! //│ e: 'a -> 'b //│ where //│ 'b :> 0 | {x: 'b} //│ 'a <: A & {f: 'a} | L //│ = [Function (anonymous)] :re e v //│ res: 'a //│ where //│ 'a :> 0 | {x: 'a} //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/fcp/Simplif.mls ================================================ :NoJS def f: 'a -> int //│ f: anything -> int def f: forall 'a. 'a -> int //│ f: anything -> int def f: forall 'a. int -> 'a //│ f: int -> nothing def f: 'a -> 'b -> ('a, 'b) //│ f: 'a -> 'b -> ('a, 'b,) def f: (forall 'a. 'a -> 'b) -> 'b //│ f: (anything -> 'b) -> 'b def f: (forall 'a. 'b -> 'a) -> 'b //│ f: (nothing -> nothing) -> nothing def f: (forall 'a. 'b -> 'a) -> 'b -> 'b //│ f: ('b -> nothing) -> 'b -> 'b def bar: forall 'a. 'a -> int //│ bar: anything -> int def foo: (forall 'a. 'a -> 'b) -> 'b //│ foo: (anything -> 'b) -> 'b foo bar //│ res: int def foo: MutArray[forall 'a. 'a -> 'b] -> 'b //│ foo: MutArray[anything -> 'b] -> 'b def foo: (forall 'a. MutArray['a -> 'b]) -> 'b //│ foo: (forall 'a. MutArray['a -> 'b]) -> 'b // def foo: MutArray[forall 'b. forall 'a {'a <: 'b}. ('a .. 'b) -> 'b] -> 'b // def foo: MutArray[forall 'b. forall 'a. ('a .. 'b) -> ('a .. 'b)] -> 'b def foo: forall 'a 'b. ('a & 'b) -> ('a | 'b) //│ foo: 'b -> 'b def foo: (forall 'a 'b. ('a & 'b) -> ('a | 'b)) -> 1 //│ foo: (forall 'b. 'b -> 'b) -> 1 def foo: ((forall 'a 'b. ('a & 'b) -> ('a | 'b)) -> 1) -> 2 //│ foo: ((forall 'b. 'b -> 'b) -> 1) -> 2 def foo: (((forall 'a 'b. ('a & 'b) -> ('a | 'b)) -> 1) -> 2) -> 3 //│ foo: (((forall 'b. 'b -> 'b) -> 1) -> 2) -> 3 // * Note that this degenerate type is equivalent to `anything -> nothing` // * (Take 'a = Int and 'b = ~Int.) def foo: forall 'a 'b. ('a | 'b) -> ('a & 'b) //│ foo: ('a | 'b) -> ('a & 'b) foo (1 : anything) : nothing //│ res: nothing def foo: (forall 'a 'b. ('a | 'b) -> ('a & 'b)) -> 1 //│ foo: (forall 'a 'b. ('a | 'b) -> ('a & 'b)) -> 1 :e foo id //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.70: foo id //│ ║ ^^^^^^ //│ ╟── type `'a` does not match type `'b` //│ ║ l.66: def foo: (forall 'a 'b. ('a | 'b) -> ('a & 'b)) -> 1 //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.66: def foo: (forall 'a 'b. ('a | 'b) -> ('a & 'b)) -> 1 //│ ║ ^^ //│ ╟── from intersection type: //│ ║ l.66: def foo: (forall 'a 'b. ('a | 'b) -> ('a & 'b)) -> 1 //│ ║ ^^^^^^^^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.66: def foo: (forall 'a 'b. ('a | 'b) -> ('a & 'b)) -> 1 //│ ╙── ^^ //│ res: 1 | error def foo: MutArray[forall 'a 'b. ('a & 'b) -> ('a | 'b)] //│ foo: MutArray[forall 'b. 'b -> 'b] def foo: MutArray[MutArray[(forall 'a 'b. ('a & 'b) -> ('a | 'b)) -> 1]] //│ foo: MutArray[MutArray[(forall 'b. 'b -> 'b) -> 1]] def foo: forall 'a 'b. ('a & 'b) -> 'a -> 'b -> ('a | 'b, 'b | 'a) //│ foo: 'a -> 'a -> 'a -> ('a, 'a,) def foo: (forall 'a 'b. ('a & 'b) -> 'a -> 'b -> ('a | 'b, 'b | 'a)) -> 1 //│ foo: (forall 'a. 'a -> 'a -> 'a -> ('a, 'a,)) -> 1 ================================================ FILE: shared/src/test/diff/fcp/SkolemErrors.mls ================================================ :NoJS def k: (forall 'a. 'a -> 'a) -> int //│ k: (forall 'a. 'a -> 'a) -> int fun x -> k x //│ res: (forall 'a. 'a -> 'a) -> int fun x -> k (fun y -> x y) //│ res: (??a -> ??a0) -> int :ShowRelativeLineNums :AllowTypeErrors :e k (fun x -> x + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: k (fun x -> x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.5: def k: (forall 'a. 'a -> 'a) -> int //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.+1: k (fun x -> x + 1) //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.5: def k: (forall 'a. 'a -> 'a) -> int //│ ╙── ^^ //│ res: error | int ================================================ FILE: shared/src/test/diff/fcp/Skolems.mls ================================================ :NoRecursiveTypes // :GeneralizeArguments def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b def auto_ x = x x //│ auto_: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto_: //│ (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] fun (x: (forall 'a. 'a -> 'a)) -> auto_ x //│ res: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: res] // * Compares an extruded higher-level skolem; rightly rejected :e fun (x: ('a -> 'a)) -> auto_ x //│ ╔══[ERROR] Type error in application //│ ║ l.20: fun (x: ('a -> 'a)) -> auto_ x //│ ║ ^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ╙── ^^ //│ res: (anything -> ??a) -> (error | 'b -> 'b) //│ = [Function: res] foo(x: ('a -> 'b)) = auto_ x //│ foo: (??a -> ??a0) -> 'b -> 'b //│ = [Function: foo] :e foo (fun x -> x) //│ ╔══[ERROR] Type error in application //│ ║ l.38: foo (fun x -> x) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ //│ res: error | 'b -> 'b //│ = [Function (anonymous)] :e foo (fun x -> 0) //│ ╔══[ERROR] Type error in application //│ ║ l.55: foo (fun x -> 0) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.55: foo (fun x -> 0) //│ ║ ^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ //│ res: error | 'b -> 'b //│ = 0 :e foo (fun x -> foo (fun y -> y) x) //│ ╔══[ERROR] Type error in application //│ ║ l.69: foo (fun x -> foo (fun y -> y) x) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ //│ ╔══[ERROR] Type error in application //│ ║ l.69: foo (fun x -> foo (fun y -> y) x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.33: foo(x: ('a -> 'b)) = auto_ x //│ ╙── ^^ //│ res: error | 'b -> 'b //│ = [Function (anonymous)] // * This would be unsound; `fun y -> x` does not have type `forall 'a. 'a -> 'a` but it is eventually passed to auto_ :e foo (fun x -> foo (fun y -> x) x) //│ ╔══[ERROR] Type error in application //│ ║ l.99: foo (fun x -> foo (fun y -> x) x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.5: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.99: foo (fun x -> foo (fun y -> x) x) //│ ╙── ^ //│ res: error | 'b -> 'b //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // * Producing an actual crash from this: def oops (i: forall 'a. 'a -> 'a) = let _ = (i id) "hello" in i //│ oops: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ = [Function: oops] // If we were not careful this would lead to unsound skolem extrusion: def extrude (f: 'a -> 'b) = oops f //│ extrude: (??a -> ??a0) -> 'a -> 'a //│ = [Function: extrude] :e f = extrude (fun x -> extrude (fun y -> x) x) //│ ╔══[ERROR] Type error in application //│ ║ l.131: f = extrude (fun x -> extrude (fun y -> x) x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.119: def oops (i: forall 'a. 'a -> 'a) = //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.119: def oops (i: forall 'a. 'a -> 'a) = //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.131: f = extrude (fun x -> extrude (fun y -> x) x) //│ ╙── ^ //│ f: error | 'a -> 'a //│ = [Function (anonymous)] :re f 42 // Boom! //│ res: 42 | error //│ Runtime error: //│ TypeError: i(...) is not a function def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ swapWith: (forall 'x 'y. (('x, 'y,),) -> ('y, 'x,)) -> ('a, 'b,) -> ('b, 'a,) //│ = def swapWith f (a, b) = f ((a, b)) //│ ((('a, 'b,),) -> 'c) -> ('a, 'b,) -> 'c //│ <: swapWith: //│ (forall 'x 'y. (('x, 'y,),) -> ('y, 'x,)) -> ('a, 'b,) -> ('b, 'a,) //│ = [Function: swapWith] // * This is an error because it would force the swapped tuple to be the same 'a as the original :e fun (x: ('a -> 'a)) -> swapWith x //│ ╔══[ERROR] Type error in application //│ ║ l.168: fun (x: ('a -> 'a)) -> swapWith x //│ ║ ^^^^^^^^^^ //│ ╟── type variable `'x` leaks out of its scope //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ //│ res: (((??x, ??y,) | 'a) -> ((??y0, ??x0,) & 'a)) -> (error | ('a0, 'b,) -> ('b, 'a0,)) //│ = [Function: res] foo = fun (x: ('a -> 'b)) -> swapWith x //│ foo: (((??x, ??y,),) -> (??y0, ??x0,)) -> ('a, 'b,) -> ('b, 'a,) //│ = [Function: foo1] bar = fun f -> foo f (1, 2) //│ bar: (((??x, ??y,),) -> (??y0, ??x0,)) -> (2, 1,) //│ = [Function: bar] :e bar (fun ((u, v),) -> (v, u)) //│ ╔══[ERROR] Type error in application //│ ║ l.190: bar (fun ((u, v),) -> (v, u)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'y` leaks out of its scope //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ //│ res: error | (2, 1,) //│ = [ 2, 1 ] :e bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v2, u2)) in (v1, u1)) //│ ╔══[ERROR] Type error in application //│ ║ l.204: bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v2, u2)) in (v1, u1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'y` leaks out of its scope //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ //│ ╔══[ERROR] Type error in application //│ ║ l.204: bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v2, u2)) in (v1, u1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'y` leaks out of its scope //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ //│ res: error | (2, 1,) //│ = [ 2, 1 ] :e // * Not sure why this one is rejected (but the `extrude` version above is accepted.) bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v1, u1)) in (v1, u1)) //│ ╔══[ERROR] Type error in application //│ ║ l.227: bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v1, u1)) in (v1, u1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'x` leaks out of its scope //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── back into type variable `'x` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.227: bar (fun ((u1, v1),) -> let tmp = bar (fun ((u2, v2),) -> (v1, u1)) in (v1, u1)) //│ ╙── ^^ //│ res: error //│ = [ 2, 1 ] :e fun (x: ((('a, 'b),) -> ('b, 'a))) -> swapWith x //│ ╔══[ERROR] Type error in application //│ ║ l.245: fun (x: ((('a, 'b),) -> ('b, 'a))) -> swapWith x //│ ║ ^^^^^^^^^^ //│ ╟── type variable `'y` leaks out of its scope //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ║ ^^ //│ ╟── back into type variable `'y` //│ ║ l.155: def swapWith: (forall 'x 'y. (('x, 'y),) -> ('y, 'x)) -> ('a, 'b) -> ('b, 'a) //│ ╙── ^^ //│ res: (((anything, anything,),) -> (??y, ??x,)) -> (error | ('a, 'b,) -> ('b, 'a,)) //│ = [Function: res] fun (x: ((('a, 'b),) -> ('c, 'd))) -> swapWith x //│ res: (((??x, ??y,),) -> (??y0, ??x0,)) -> ('a, 'b,) -> ('b, 'a,) //│ = [Function: res] ================================================ FILE: shared/src/test/diff/fcp/Skolems2.mls ================================================ :NoRecursiveTypes :ConstrainedTypes :DontDistributeForalls // // * This asks for a polymorphic function and leverages that polymorphism def oops (i: forall 'c. ('c -> 'c, 0)) = let _ = log "Hi!" in let _ = (fun ((f, 0)) -> let _ = log f in let r = (f id) "hello" in log r) i in (fun ((f, 0)) -> f) i //│ oops: (forall 'c. ('c -> 'c, 0,)) -> 'c0 -> 'c0 //│ = [Function: oops] // * Notice the skolem extrusion/leakage which extrudes into `anything -> nothing` def extrude f = oops((f, 0)) //│ extrude: (??c -> ??c0) -> 'c -> 'c //│ = [Function: extrude] :e extrude(id) //│ ╔══[ERROR] Type error in application //│ ║ l.24: extrude(id) //│ ║ ^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this tuple literal: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ║ ^^^^^^ //│ ╟── • this function: //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.9: let _ = log "Hi!" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.10: in let _ = (fun ((f, 0)) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.11: let _ = log f //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.12: in let r = (f id) "hello" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.13: in log r) i //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.14: in (fun ((f, 0)) -> f) i //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ╙── ^^^^ //│ res: error | 'c -> 'c //│ = [Function: id] //│ // Output //│ Hi! //│ [Function: id] //│ hello :e extrude(id)(42) //│ ╔══[ERROR] Type error in application //│ ║ l.64: extrude(id)(42) //│ ║ ^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this tuple literal: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ║ ^^^^^^ //│ ╟── • this function: //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.9: let _ = log "Hi!" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.10: in let _ = (fun ((f, 0)) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.11: let _ = log f //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.12: in let r = (f id) "hello" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.13: in log r) i //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.14: in (fun ((f, 0)) -> f) i //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ╙── ^^^^ //│ res: 42 | error //│ = 42 //│ // Output //│ Hi! //│ [Function: id] //│ hello :e extrude(id)(id)(42) //│ ╔══[ERROR] Type error in application //│ ║ l.104: extrude(id)(id)(42) //│ ║ ^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this tuple literal: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ║ ^^^^^^ //│ ╟── • this function: //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.9: let _ = log "Hi!" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.10: in let _ = (fun ((f, 0)) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.11: let _ = log f //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.12: in let r = (f id) "hello" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.13: in log r) i //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.14: in (fun ((f, 0)) -> f) i //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ╙── ^^^^ //│ res: 42 | error //│ = 42 //│ // Output //│ Hi! //│ [Function: id] //│ hello // * This clearly shouldn't be possible – it passes one skolem as the other :e f = extrude (fun x -> extrude (fun y -> x) x) //│ ╔══[ERROR] Type error in application //│ ║ l.145: f = extrude (fun x -> extrude (fun y -> x) x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this tuple literal: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ║ ^^^^^^ //│ ╟── • this function: //│ ║ l.8: def oops (i: forall 'c. ('c -> 'c, 0)) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.9: let _ = log "Hi!" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.10: in let _ = (fun ((f, 0)) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.11: let _ = log f //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.12: in let r = (f id) "hello" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.13: in log r) i //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.14: in (fun ((f, 0)) -> f) i //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.19: def extrude f = oops((f, 0)) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.145: f = extrude (fun x -> extrude (fun y -> x) x) //│ ╙── ^ //│ f: error | 'c -> 'c //│ = [Function (anonymous)] //│ // Output //│ Hi! //│ [Function (anonymous)] //│ Hi! //│ [Function (anonymous)] //│ hello //│ hello // * Boom! (exhibits crash using ill-typed `f` above) :re f 42 //│ res: 42 | error //│ Runtime error: //│ TypeError: f(...) is not a function //│ // Output //│ Hi! //│ [Function (anonymous)] // * Note: parser parses this the same as `oops((f, 0)._1)` def extrude f = oops((f, 0)).0 //│ extrude: (forall 'c. ('c -> 'c, 0,)) -> 'c0 -> 'c0 //│ = [Function: extrude1] // *** Other random tests *** def oops (i: forall 'c. ('c -> 'c, 0)) = let _ = (i.0 id) "hello" in i.0 //│ oops: (forall 'c. ('c -> 'c, 0,)) -> 'c0 -> 'c0 //│ = [Function: oops1] def oops (i: forall 'c. ('c -> 'c, 0)) = let _ = log "Hi!" in (fun ((f, 0)) -> let _ = log f in let r = (f id) "hello" in let _ = log r in f) i //│ oops: (forall 'c. ('c -> 'c, 0,)) -> ("hello" -> anything & 'c0) -> (forall 'a. 'a -> 'a | 'c0) //│ = [Function: oops2] def foo(a, b) = (a, b) : forall 'c. ('c, 'c) //│ foo: (??c, ??c,) -> (nothing, nothing,) //│ = [Function: foo] :re foo(error, error) //│ res: (nothing, nothing,) //│ Runtime error: //│ Error: an error was thrown def foo(a, b) = (a, b) : forall 'c. ('c, 'c -> 'c) //│ foo: (??c, ??c0 -> ??c,) -> (forall 'c. ('c, 'c -> 'c,)) //│ = [Function: foo1] def foo(a, b) = (a, b) : forall 'c. ('c -> 'c, 'c -> 'c) //│ foo: (??c -> ??c0, ??c -> ??c0,) -> (forall 'c. ('c -> 'c, 'c -> 'c,)) //│ = [Function: foo2] :e foo(id, id) //│ ╔══[ERROR] Type error in application //│ ║ l.248: foo(id, id) //│ ║ ^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.243: def foo(a, b) = (a, b) : forall 'c. ('c -> 'c, 'c -> 'c) //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.243: def foo(a, b) = (a, b) : forall 'c. ('c -> 'c, 'c -> 'c) //│ ╙── ^^ //│ res: error | ('c -> 'c, 'c -> 'c,) //│ = [ [Function: id], [Function: id] ] def foo(f) = (fun a -> f a) : forall 'c. 'c -> 'c //│ foo: (??c -> ??c0) -> (forall 'c. 'c -> 'c) //│ = [Function: foo3] :e foo(id) //│ ╔══[ERROR] Type error in application //│ ║ l.267: foo(id) //│ ║ ^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.262: def foo(f) = (fun a -> f a) : forall 'c. 'c -> 'c //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.262: def foo(f) = (fun a -> f a) : forall 'c. 'c -> 'c //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.262: def foo(f) = (fun a -> f a) : forall 'c. 'c -> 'c //│ ╙── ^^^ //│ res: error | 'c -> 'c //│ = [Function (anonymous)] def foo(a, b) = let tmp = (a, b) : forall 'c. ('c -> 'c, 0) in a //│ foo: (??c -> ??c0 & 'a, 0,) -> 'a //│ = [Function: foo4] :e foo(id, 0) //│ ╔══[ERROR] Type error in application //│ ║ l.289: foo(id, 0) //│ ║ ^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.284: def foo(a, b) = let tmp = (a, b) : forall 'c. ('c -> 'c, 0) in a //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.284: def foo(a, b) = let tmp = (a, b) : forall 'c. ('c -> 'c, 0) in a //│ ╙── ^^ //│ res: error | 'a -> 'a //│ = [Function: id] :e foo(fun x -> foo(fun y -> x, 0) x, 0) //│ ╔══[ERROR] Type error in application //│ ║ l.303: foo(fun x -> foo(fun y -> x, 0) x, 0) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.284: def foo(a, b) = let tmp = (a, b) : forall 'c. ('c -> 'c, 0) in a //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.284: def foo(a, b) = let tmp = (a, b) : forall 'c. ('c -> 'c, 0) in a //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.303: foo(fun x -> foo(fun y -> x, 0) x, 0) //│ ╙── ^ //│ res: error | (??c & 'a) -> 'a //│ = [Function (anonymous)] :e def foo((a, b) : forall 'c. ('c -> 'c, 0)) = () //│ ╔══[ERROR] Type error in type ascription //│ ║ l.321: def foo((a, b) : forall 'c. ('c -> 'c, 0)) = () //│ ║ ^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.321: def foo((a, b) : forall 'c. ('c -> 'c, 0)) = () //│ ║ ^^ //│ ╟── back into type variable `'c` //│ ║ l.321: def foo((a, b) : forall 'c. ('c -> 'c, 0)) = () //│ ╙── ^^ //│ foo: (forall 'c. ('c -> 'c, 0,)) -> () //│ = [Function: foo5] foo((id, 0)) //│ res: () //│ = [] :e foo((fun x -> foo((fun y -> x, 0)), 0)) //│ ╔══[ERROR] Type error in application //│ ║ l.339: foo((fun x -> foo((fun y -> x, 0)), 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.321: def foo((a, b) : forall 'c. ('c -> 'c, 0)) = () //│ ╙── ^^ //│ res: error | () //│ = [] ================================================ FILE: shared/src/test/diff/fcp/Skolems3.mls ================================================ :NoConstrainedTypes :DontDistributeForalls :NoCycleCheck :NoRecursiveTypes // *** Here are interesting examples of (relatively minor) limitations of the current extrusion scheme. *** // * Here we essentially extrude skolem `a` in a constraint similar to `'y <: (a, a) -> a` // * which is correctly approximated as `'y <: (anything, anything) -> nothing`. def bar y = (fun x -> y (x, x)) : forall 'a. 'a -> 'a //│ bar: ((??a, ??a,) -> ??a0) -> (forall 'a. 'a -> 'a) //│ = [Function: bar] // * In fact, what happens above is that local TV 'x is extruded into 'x0 (where 'x <: 'x0) // * when passed to the lower-polymorphic-level TV 'y, as in `'y <: ('x0, 'x0) -> 'r0`. // * Then we get constraint `a <: 'x` leading to `a <: 'x0` extruded into `anything <: 'x0` // * and constraint `'r0 <: a` which is extruded into `'r0 <: nothing`. // * Hence the inferred type. // * This is all dandy and fine because we at no point try to unify `a` with a lower-level TV. // * But in the following contrived example, we want to extrude `a` through **the same** // * lower-polymorphic-level TV 's, resulting in extruded `a <: anything <: 's <: nothing <: a` // * which is inconsistent because of the implied `anything <: nothing` constraint. :e def baz x = (x: 's -> 's): forall 'a. 'a -> 'a //│ ╔══[ERROR] Type error in type ascription //│ ║ l.30: (x: 's -> 's): forall 'a. 'a -> 'a //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: (x: 's -> 's): forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: (x: 's -> 's): forall 'a. 'a -> 'a //│ ╙── ^^ //│ baz: (anything -> ??a) -> (forall 'a. 'a -> 'a) //│ = [Function: baz] // * Thankfully, it turns out this kind of situations just doesn't seem to arise very often. // * Usually, we extrude things through **different** lower-level TVs which represent the // * approximants of an extruded type variable, as in bar above as well as baz below: def baz x = (x: 's -> 't): forall 'a. 'a -> 'a //│ baz: (??a -> ??a0) -> (forall 'a. 'a -> 'a) //│ = [Function: baz1] // * Even when involving invariant type constructors, things just seem to work out fine. def bar y = (fun x -> y (mut x)) : forall 'a. 'a -> 'a //│ bar: (??a -> ??a0) -> (forall 'a. 'a -> 'a) //│ = [Function: bar1] def lift: 'a -> MutArray['a] def lift x = (mut x) //│ lift: 'a -> MutArray['a] //│ = //│ 'a -> (mut 'a,) //│ <: lift: //│ 'a -> MutArray['a] //│ = [Function: lift] def bar y = (fun x -> y (lift x)) : forall 'a. 'a -> 'a //│ bar: (MutArray[anything] -> ??a) -> (forall 'a. 'a -> 'a) //│ = [Function: bar2] def bar y = (fun x -> y (x, x)) : forall 'a. MutArray['a] -> 'a //│ bar: ((MutArray[in ??a out ??a0], MutArray[in ??a out ??a0],) -> ??a) -> (forall 'a. MutArray['a] -> 'a) //│ = [Function: bar3] def bar y = (fun x -> y (x, x)) : forall 'a. MutArray['a] -> MutArray['a] //│ bar: ((MutArray[in ??a out ??a0], MutArray[in ??a out ??a0],) -> MutArray[in ??a0 out ??a]) -> (forall 'a. MutArray['a] -> MutArray['a]) //│ = [Function: bar4] def bar y = (fun x -> y (mut x)) : forall 'a. MutArray['a] -> 'a //│ bar: (MutArray[in ??a out ??a0] -> ??a) -> (forall 'a. MutArray['a] -> 'a) //│ = [Function: bar5] def bar y = (fun x -> y (lift x)) : forall 'a. MutArray['a] -> 'a //│ bar: (MutArray[in 'a out MutArray[in ??a out ??a0] | 'a] -> ??a) -> (forall 'a0. MutArray['a0] -> 'a0) //│ = [Function: bar6] def bar y = (fun x -> y (lift x)) : forall 'a. MutArray['a] -> MutArray['a] //│ bar: (MutArray[in 'a out MutArray[in ??a out ??a0] | 'a] -> MutArray[in ??a0 out ??a]) -> (forall 'a0. MutArray['a0] -> MutArray['a0]) //│ = [Function: bar7] // * Again, we can still make it fail using contrived annotations. :e def baz x = (x: MutArray['s] -> 't): forall 'a. 'a -> 'a //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.101: (x: MutArray['s] -> 't): forall 'a. 'a -> 'a //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` does not match type `MutArray['s]` //│ ║ l.101: (x: MutArray['s] -> 't): forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.101: (x: MutArray['s] -> 't): forall 'a. 'a -> 'a //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.101: (x: MutArray['s] -> 't): forall 'a. 'a -> 'a //│ ╙── ^^ //│ baz: (MutArray['s] -> ??a) -> (forall 'a. 'a -> 'a) //│ = [Function: baz2] :e def baz x = (x: MutArray['s] -> 't): forall 'a. MutArray['a] -> 'a //│ ╔══[ERROR] Type error in type ascription //│ ║ l.118: (x: MutArray['s] -> 't): forall 'a. MutArray['a] -> 'a //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.118: (x: MutArray['s] -> 't): forall 'a. MutArray['a] -> 'a //│ ╙── ^^ //│ baz: (MutArray['s] -> ??a) -> (forall 'a. MutArray['a] -> 'a) //│ where //│ 's :> ??a0 //│ <: ??a //│ = [Function: baz3] // *** Other various tests and tweaks. *** // * Here, the polymorphic nested let binding introduces more intermediate extrusions, // * but things still work out similarly as before. def bar y = (fun x -> let tmp = y (x, x) in x) : forall 'a. 'a -> 'a //│ bar: ((??a, ??a,) -> anything) -> (forall 'a. 'a -> 'a) //│ = [Function: bar8] def bar y = (fun x -> let tmp = (fun x0 -> y (x0, x0)) x in x) : forall 'a. 'a -> 'a //│ bar: ((??a, ??a,) -> anything) -> (forall 'a. 'a -> 'a) //│ = [Function: bar9] def id2: (forall 's. 's -> 's) -> (forall 't. 't -> 't) id2 = id //│ id2: (forall 's. 's -> 's) -> (forall 't. 't -> 't) //│ = //│ 'a -> 'a //│ <: id2: //│ (forall 's. 's -> 's) -> (forall 't. 't -> 't) //│ = [Function: id] :e // * Legit (rigid var not a function) def bar y = (fun x -> y (id2 x, x)) : forall 'a. 'a -> 'a //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.159: (fun x -> y (id2 x, x)) : forall 'a. 'a -> 'a //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.159: (fun x -> y (id2 x, x)) : forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.148: def id2: (forall 's. 's -> 's) -> (forall 't. 't -> 't) //│ ║ ^^^^^^^^ //│ ╟── from reference: //│ ║ l.159: (fun x -> y (id2 x, x)) : forall 'a. 'a -> 'a //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.159: (fun x -> y (id2 x, x)) : forall 'a. 'a -> 'a //│ ╙── ^^ //│ bar: ((forall 't. 't -> 't, ??a,) -> ??a0) -> (forall 'a. 'a -> 'a) //│ = [Function: bar10] :e // * Legit (rigid var function can't be used as polymorphic function) def bar y = (fun x -> y (id2 x, x)) : forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.180: (fun x -> y (id2 x, x)) : forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'s` does not match type `'a` //│ ║ l.148: def id2: (forall 's. 's -> 's) -> (forall 't. 't -> 't) //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.180: (fun x -> y (id2 x, x)) : forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── Note: quantified type variable 's is defined at: //│ ║ l.148: def id2: (forall 's. 's -> 's) -> (forall 't. 't -> 't) //│ ╙── ^^ //│ bar: ((forall 't. 't -> 't, ??a -> ??a0,) -> ??a0 -> ??a) -> (forall 'a. ('a -> 'a) -> 'a -> 'a) //│ = [Function: bar11] def id3: ('s -> 's) -> ('s -> 's) id3 = id //│ id3: ('s -> 's) -> 's -> 's //│ = //│ 'a -> 'a //│ <: id3: //│ ('s -> 's) -> 's -> 's //│ = [Function: id] def bar y = (fun x -> y (id3 x, 0)) : forall 'a. ('a -> 'a) -> ('a -> 'a) //│ bar: ((??a -> anything, 0,) -> ??a0 -> ??a) -> (forall 'a. ('a -> 'a) -> 'a -> 'a) //│ = [Function: bar12] :e // * Legit (similar to previous baz example) def baz x = (x: ('s -> 's) -> 0): forall 'a. ('a -> 'a) -> 0 //│ ╔══[ERROR] Type error in type ascription //│ ║ l.213: (x: ('s -> 's) -> 0): forall 'a. ('a -> 'a) -> 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.213: (x: ('s -> 's) -> 0): forall 'a. ('a -> 'a) -> 0 //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.213: (x: ('s -> 's) -> 0): forall 'a. ('a -> 'a) -> 0 //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.213: (x: ('s -> 's) -> 0): forall 'a. ('a -> 'a) -> 0 //│ ╙── ^^ //│ baz: ((??a -> anything) -> 0) -> (forall 'a. ('a -> 'a) -> 0) //│ = [Function: baz4] :e def boo = let tmp = (id: 's -> 's): forall 'a. 'a -> 'a in 0 //│ ╔══[ERROR] Type error in type ascription //│ ║ l.232: let tmp = (id: 's -> 's): forall 'a. 'a -> 'a //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.232: let tmp = (id: 's -> 's): forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.232: let tmp = (id: 's -> 's): forall 'a. 'a -> 'a //│ ╙── ^^ //│ boo: 0 //│ = [Function: boo] def boo = let tmp = (error: 's -> 't): forall 'a. ('a -> 'a) -> 0 in 0 //│ boo: 0 //│ = [Function: boo1] def boo x = let tmp = (x: 's -> 't): forall 'a. ('a -> 'a) -> 0 in 0 //│ boo: ((??a -> ??a0) -> 0) -> 0 //│ = [Function: boo2] :e def boo x = let tmp = (x: ('s -> 's) -> 't): forall 'a. ('a -> 'a) -> 0 in 0 //│ ╔══[ERROR] Type error in type ascription //│ ║ l.260: let tmp = (x: ('s -> 's) -> 't): forall 'a. ('a -> 'a) -> 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.260: let tmp = (x: ('s -> 's) -> 't): forall 'a. ('a -> 'a) -> 0 //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.260: let tmp = (x: ('s -> 's) -> 't): forall 'a. ('a -> 'a) -> 0 //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.260: let tmp = (x: ('s -> 's) -> 't): forall 'a. ('a -> 'a) -> 0 //│ ╙── ^^ //│ boo: ((??a -> anything) -> 0) -> 0 //│ = [Function: boo3] ================================================ FILE: shared/src/test/diff/fcp/SystemF.mls ================================================ // * From https://crypto.stanford.edu/~blynn/lambda/systemf.html :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls def succ: int -> int //│ succ: int -> int //│ = (fun f -> f succ(f 0)) (fun x -> x) //│ res: int //│ = //│ succ is not implemented // Note: does not type check without precise function type intersections (fun f -> f succ (f 0)) (fun x -> x) //│ res: int //│ = //│ succ is not implemented // [Reset] // Polymorphic identity. id x = x def id_ty: forall 'A. 'A -> 'A //│ id: 'a -> 'a //│ = [Function: id] //│ id_ty: 'A -> 'A //│ = id_ty = id //│ 'a -> 'a //│ <: id_ty: //│ 'A -> 'A //│ = [Function: id] // Self-application. xx = fun x -> x x def xx_ty: (forall 'X. 'X -> 'X) -> (forall 'X. 'X -> 'X) //│ xx: ('a -> 'b & 'a) -> 'b //│ = [Function: xx] //│ xx_ty: (forall 'X. 'X -> 'X) -> (forall 'X0. 'X0 -> 'X0) //│ = xx_ty = xx //│ ('a -> 'b & 'a) -> 'b //│ <: xx_ty: //│ (forall 'X. 'X -> 'X) -> (forall 'X0. 'X0 -> 'X0) //│ = [Function: xx] xx id iter2 f x = f(f x) def iter2_ty: forall 'X. ('X -> 'X) -> 'X -> 'X //│ res: 'a -> 'a //│ = [Function: id] //│ iter2: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: iter2] //│ iter2_ty: ('X -> 'X) -> 'X -> 'X //│ = iter2_ty = iter2 //│ ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ <: iter2_ty: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function: iter2] iter4() = iter2 iter2 //│ iter4: () -> ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c //│ = [Function: iter4] def iter4_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ iter4_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ = // * Needs distributivity or constrained types :e iter4_ty() = iter2_ty iter2_ty iter4_ty = iter4 //│ () -> ('X -> 'X) -> 'X -> 'X //│ <: iter4_ty: //│ () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ╔══[ERROR] Type error in def definition //│ ║ l.81: iter4_ty() = iter2_ty iter2_ty //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'X` leaks out of its scope //│ ║ l.75: def iter4_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ║ ^^ //│ ╟── back into type variable `'X` //│ ║ l.75: def iter4_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.81: iter4_ty() = iter2_ty iter2_ty //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.57: def iter2_ty: forall 'X. ('X -> 'X) -> 'X -> 'X //│ ╙── ^^ //│ = [Function: iter4_ty] //│ () -> ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c //│ <: iter4_ty: //│ () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ╔══[ERROR] Type error in def definition //│ ║ l.82: iter4_ty = iter4 //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type variable `'X` leaks out of its scope //│ ║ l.75: def iter4_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ║ ^^ //│ ╟── back into type variable `'X` //│ ║ l.75: def iter4_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.56: iter2 f x = f(f x) //│ ║ ^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.71: iter4() = iter2 iter2 //│ ║ ^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.82: iter4_ty = iter4 //│ ╙── ^^^^^ //│ = [Function: iter4] :DistributeForalls iter4_ty = iter4 //│ () -> ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c //│ <: iter4_ty: //│ () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ = [Function: iter4] iter4_ty() = iter2_ty iter2_ty //│ () -> ('X -> 'X) -> 'X -> 'X //│ <: iter4_ty: //│ () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ = [Function: iter4_ty3] // :e // * Started failing after better distrib, not sure why... // 4^4 = 256. iter256() = iter4() (iter4()) def iter256_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) iter256_ty() = iter4_ty() (iter4_ty()) iter256_ty = iter256 //│ iter256: () -> ('a -> ('a & 'b) & ('c | 'b) -> ('a & 'c)) -> 'a -> 'c //│ where //│ 'a :> 'c //│ = [Function: iter256] //│ iter256_ty: () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ = //│ () -> ('X -> 'X) -> 'X -> 'X //│ <: iter256_ty: //│ () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ = [Function: iter256_ty] //│ () -> ('a -> ('a & 'b) & ('c | 'b) -> ('a & 'c)) -> 'a -> 'c //│ where //│ 'a :> 'c //│ <: iter256_ty: //│ () -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ = [Function: iter256] // Church numerals. Z s z = z def Z_ty: forall 'X. ('X -> 'X) -> 'X -> 'X Z_ty = Z //│ Z: anything -> 'a -> 'a //│ = [Function: Z] //│ Z_ty: ('X -> 'X) -> 'X -> 'X //│ = //│ anything -> 'a -> 'a //│ <: Z_ty: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function: Z] S n s z = s (n s z) def S_ty: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) S_ty = S //│ S: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: S] //│ S_ty: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ = //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: S_ty: //│ (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ = [Function: S] res1() = iter4() S Z res2() = iter256() S Z //│ res1: () -> ('a -> ('b & 'c) & 'c -> 'a) -> 'c -> 'b //│ = [Function: res1] //│ res2: () -> ('a -> ('a & 'b & 'c) & 'c -> ('a & 'c)) -> 'c -> 'b //│ = [Function: res2] res1_ty() = iter4_ty() S_ty Z_ty res2_ty() = iter256_ty() S_ty Z_ty //│ res1_ty: () -> ('X -> 'X & 'X0 -> 'X0) -> ('X & 'X0) -> ('X | 'X0) //│ = [Function: res1_ty] //│ res2_ty: () -> ('X -> 'X & 'X0 -> 'X0) -> ('X & 'X0) -> ('X | 'X0) //│ = [Function: res2_ty] S (S Z) //│ res: ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ = [Function (anonymous)] // [Pair] pair x y f = f x y def pair_ty: forall 'X. forall 'Y. 'X -> 'Y -> (forall 'Z. ('X -> 'Y -> 'Z) -> 'Z) pair_ty = pair fst p = p (fun x -> fun y -> x) def fst_ty: forall 'X. forall 'Y. (forall 'Z. ('X -> 'Y -> 'Z) -> 'Z) -> 'X fst_ty = fst snd p = p (fun x -> fun y -> y) def snd_ty: forall 'X. forall 'Y. (forall 'Z. ('X -> 'Y -> 'Z) -> 'Z) -> 'Y snd_ty = snd //│ pair: 'a -> 'b -> ('a -> 'b -> 'c) -> 'c //│ = [Function: pair] //│ pair_ty: 'X -> 'Y -> ('X -> 'Y -> 'Z) -> 'Z //│ = //│ 'a -> 'b -> ('a -> 'b -> 'c) -> 'c //│ <: pair_ty: //│ 'X -> 'Y -> ('X -> 'Y -> 'Z) -> 'Z //│ = [Function: pair] //│ fst: ((forall 'a. 'a -> anything -> 'a) -> 'b) -> 'b //│ = [Function: fst] //│ fst_ty: (forall 'Z. ('X -> anything -> 'Z) -> 'Z) -> 'X //│ = //│ ((forall 'a. 'a -> anything -> 'a) -> 'b) -> 'b //│ <: fst_ty: //│ (forall 'Z. ('X -> anything -> 'Z) -> 'Z) -> 'X //│ = [Function: fst] //│ snd: ((forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function: snd] //│ snd_ty: (forall 'Z. (anything -> 'Y -> 'Z) -> 'Z) -> 'Y //│ = //│ ((forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ <: snd_ty: //│ (forall 'Z. (anything -> 'Y -> 'Z) -> 'Z) -> 'Y //│ = [Function: snd] p02 = pair Z (S (S Z)) //│ p02: ((forall 'a. anything -> 'a -> 'a) -> (('b -> 'c & 'd -> 'b) -> 'd -> 'c) -> 'e) -> 'e //│ = [Function (anonymous)] p02_ty = pair_ty Z_ty (S_ty (S_ty Z_ty)) //│ p02_ty: ((forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) -> 'Z) -> 'Z //│ = [Function (anonymous)] fp = fst p02 sp = snd p02 //│ fp: anything -> 'a -> 'a //│ = [Function: Z] //│ sp: ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ = [Function (anonymous)] def fp_ty: forall 'a. ('a -> 'a) -> 'a -> 'a fp_ty = fst_ty p02_ty fp_ty = fp def sp_ty: forall 'a. ('a -> 'a) -> 'a -> 'a sp_ty = snd_ty p02_ty sp_ty = sp //│ fp_ty: ('a -> 'a) -> 'a -> 'a //│ = //│ ('X -> 'X) -> 'X -> 'X //│ <: fp_ty: //│ ('a -> 'a) -> 'a -> 'a //│ = [Function: Z] //│ anything -> 'a -> 'a //│ <: fp_ty: //│ ('a -> 'a) -> 'a -> 'a //│ = [Function: Z] //│ sp_ty: ('a -> 'a) -> 'a -> 'a //│ = //│ ('X -> 'X) -> 'X -> 'X //│ <: sp_ty: //│ ('a -> 'a) -> 'a -> 'a //│ = [Function (anonymous)] //│ ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ <: sp_ty: //│ ('a -> 'a) -> 'a -> 'a //│ = [Function (anonymous)] p01 = pair (()) Z //│ p01: (((),) -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function (anonymous)] p = pair (()) //│ p: 'a -> (((),) -> 'a -> 'b) -> 'b //│ = [Function (anonymous)] p01 = p Z //│ p01: (((),) -> (forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function (anonymous)] (fun p -> p (fun x -> fun y -> x)) : forall 'X. forall 'Y. (forall 'Z. ('X -> 'Y -> 'Z) -> 'Z) -> 'X //│ res: (forall 'Z. ('X -> anything -> 'Z) -> 'Z) -> 'X //│ = [Function: res] p02 = pair Z (S (S Z)) p02_ty = pair_ty Z_ty (S_ty (S_ty Z_ty)) p02_ty = p02 //│ p02: ((forall 'a. anything -> 'a -> 'a) -> (('b -> 'c & 'd -> 'b) -> 'd -> 'c) -> 'e) -> 'e //│ = [Function (anonymous)] //│ p02_ty: ((forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) -> 'Z) -> 'Z //│ = [Function (anonymous)] //│ p02_ty: ((forall 'a. anything -> 'a -> 'a) -> (('b -> 'c & 'd -> 'b) -> 'd -> 'c) -> 'e) -> 'e //│ = [Function (anonymous)] fp02 = fst p02 def fp02_ty: forall 'X. ('X -> 'X) -> 'X -> 'X fp02_ty = fst_ty p02_ty fp02_ty = fp02 //│ fp02: anything -> 'a -> 'a //│ = [Function: Z] //│ fp02_ty: ('X -> 'X) -> 'X -> 'X //│ = //│ anything -> 'a -> 'a //│ <: fp02_ty: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function: Z] //│ anything -> 'a -> 'a //│ <: fp02_ty: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function: Z] :ns fp02 //│ res: forall 'a. 'a //│ where //│ 'a :> forall 'b 'c. 'b -> 'c -> 'c //│ = [Function: Z] sp02 = snd p02 def sp02_ty: forall 'X. ('X -> 'X) -> 'X -> 'X sp02_ty = snd_ty p02_ty sp02_ty = sp02 //│ sp02: ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ = [Function (anonymous)] //│ sp02_ty: ('X -> 'X) -> 'X -> 'X //│ = //│ ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ <: sp02_ty: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function (anonymous)] //│ ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ <: sp02_ty: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function (anonymous)] // * TODO try: // [Booleans, Integers, Pairs, Lists] // true = \X x:X y:X.x // false = \X x:X y:X.y // not = \b:forall X.X->X->X X t:X f:X.b [X] f t // 0 = \X s:X->X z:X.z // succ = \n:(forall X.(X->X)->X->X) X s:X->X z:X.s(n[X] s z) // pair = \X Y x:X y:Y Z f:X->Y->Z.f x y // fst = \X Y p:forall Z.(X->Y->Z)->Z.p [X] (\x:X y:Y.x) // snd = \X Y p:forall Z.(X->Y->Z)->Z.p [Y] (\x:X y:Y.y) // nil = \X.(\R.\c:X->R->R.\n:R.n) // cons = \X h:X t:forall R.(X->R->R)->R->R.(\R c:X->R->R n:R.c h (t [R] c n)) // [Surprise!] // -- See Brown and Palsberg, "Breaking Through the Normalization Barrier: // -- A Self-Interpreter for F-omega": "Several books, papers, and web pages" // -- claim the following program cannot exist! That is, self-interpreters are // -- supposedly impossible in strongly normalizing languages. // id=\X x:X.x -- Polymorphic identity. // true = \X x:X y:X.x -- Church booleans. // false = \X x:X y:X.y // not = \b:forall X.X->X->X X t:X f:X.b [X] f t tru x y = x fls x y = y not b t f = b f t //│ tru: 'a -> anything -> 'a //│ = [Function: tru] //│ fls: anything -> 'a -> 'a //│ = [Function: fls] //│ not: ('a -> 'b -> 'c) -> 'b -> 'a -> 'c //│ = [Function: not] // E=\T q:(forall X.X->X)->T.q id -- Self-interpreter. E q = q id //│ E: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: E] // shallow (not (not true)) -- Shallow encoding. // λid.id(λb t f.b f t)(id(λb t f.b f t)(λx y.x)) sh id = (id not) ((id not) tru) //│ sh: ((forall 'a 'b 'c. ('a -> 'b -> 'c) -> 'b -> 'a -> 'c) -> ('d -> 'e & (forall 'f. 'f -> anything -> 'f) -> 'd)) -> 'e //│ = [Function: sh] // E[forall X.X->X->X](shallow (not( not true))) E sh //│ res: 'a -> anything -> 'a //│ = [Function (anonymous)] u = E pu x y = (u x) x sh_pu id x y = id (id u x) x //│ u: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: E] //│ pu: ((forall 'a. 'a -> 'a) -> 'b -> 'c & 'b) -> anything -> 'c //│ = [Function: pu] //│ sh_pu: ((forall 'a. ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> 'c -> 'd & 'd -> 'c -> 'e) -> 'c -> anything -> 'e //│ = [Function: sh_pu] :e pu sh_pu //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.421: pu sh_pu //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error | anything -> anything -> anything -> nothing //│ = [Function (anonymous)] // * "Of course, self-application of self-application ((\x.x x)(\x.x x)) remains untypable, because it has no normal form." :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.431: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/fcp/SystemF_2.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :DistributeForalls I x = x K x y = x //│ I: 'a -> 'a //│ = [Function: I] //│ K: 'a -> anything -> 'a //│ = [Function: K] id x = x iter2 f x = f(f x) //│ id: 'a -> 'a //│ = [Function: id] //│ iter2: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: iter2] iter2 K //│ res: 'a -> anything -> anything -> 'a //│ = [Function (anonymous)] iter2 iter2 //│ res: ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c //│ = [Function (anonymous)] id iter2 iter2 //│ res: ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c //│ = [Function (anonymous)] // * The following occurs-check failure is legit. // * We have `K: 'A -> anything -> 'A` which flows into `'a -> 'b & ('c | 'b) -> 'a` // * which implies `anything -> 'A <: 'b <: 'A`, a nested cycle. // * The cycle displayed below is a 1-unrolling of the one above. :e iter2 iter2 K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ? -> ? -> 'a //│ ╙── //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] iter2 iter2 iter2 //│ res: ('a -> ('a & 'b) & 'b -> ('a & 'b & 'c)) -> 'a -> 'c //│ = [Function (anonymous)] iter2 K iter2 iter2 iter2 //│ res: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: iter2] // (λzy. y(zI)(zK))(λx. xx). foo = (fun z -> fun y -> y (z I) (z K)) //│ foo: ((forall 'a. 'a -> 'a) -> 'b & (forall 'c. 'c -> anything -> 'c) -> 'd) -> ('b -> 'd -> 'e) -> 'e //│ = [Function: foo] foo (fun x -> x x) //│ res: ((forall 'a. 'a -> 'a) -> (forall 'b. anything -> 'b -> anything -> 'b) -> 'c) -> 'c //│ = [Function (anonymous)] n0_ s z = z def n0: forall 'X. ('X -> 'X) -> 'X -> 'X n0 = n0_ //│ n0_: anything -> 'a -> 'a //│ = [Function: n0_] //│ n0: ('X -> 'X) -> 'X -> 'X //│ = //│ anything -> 'a -> 'a //│ <: n0: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function: n0_] succ_ n s z = s (n s z) def succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) succ = succ_ //│ succ_: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: succ_] //│ succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ = //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: succ: //│ (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ = [Function: succ_] c2 = succ (succ n0) //│ c2: ('X -> 'X) -> 'X -> 'X //│ = [Function (anonymous)] :e c2 c2 K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ? -> 'a //│ ╙── //│ res: ('a & 'X) -> (anything -> 'a | 'X) //│ where //│ 'X :> anything -> 'X //│ = [Function (anonymous)] def c2_ n0 = succ_ (succ_ n0) //│ c2_: ('a -> 'b -> 'c) -> ('d -> 'e & 'c -> 'd & 'a) -> 'b -> 'e //│ = [Function: c2_] c2_ = succ_ (succ_ n0) //│ c2_: ('a -> 'b & 'X -> ('a & 'X)) -> 'X -> 'b //│ = [Function (anonymous)] // * Note: cycle check fails when generalizing curried lambdas c2_ c2_ //│ res: ('a -> ('X & 'X0 & 'b) & 'X0 -> ('a & 'X & 'X0) & 'c -> ('a & 'X & 'X0 & 'b & 'c) & 'X -> ('X & 'c)) -> ('X & 'X0) -> 'b //│ = [Function (anonymous)] // * Idem c2_ c2_! //│ res: ('a -> ('a & 'b & 'X) & 'X -> ('a & 'X)) -> 'X -> 'b //│ = [Function (anonymous)] // * Note: cycle check fails when generalizing curried lambdas :e // occurs-check c2_ c2_ K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ? -> ('a | 'b | 'c | 'd) //│ 'c :> ? -> ('d | 'b) | ? -> ('b | 'c) //│ 'd :> ? -> ('c | 'b) //│ 'b :> ? -> ('a | 'b) //│ ╙── //│ res: ('a & 'b & 'c) -> anything -> ('d | 'e) //│ where //│ 'a :> anything -> ('a | 'd | 'b | 'c | 'e) //│ 'b :> anything -> ('d | 'b | 'e) //│ 'e :> anything -> ('c | 'd) //│ 'c :> anything -> ('b | 'd | 'e) //│ 'd :> anything -> ('a | 'd) //│ = [Function (anonymous)] c2__ = succ_ (succ_ n0_) //│ c2__: ('a -> 'b & 'c -> 'a) -> 'c -> 'b //│ = [Function (anonymous)] c2__ c2__ //│ res: ('a -> ('b & 'c) & 'b -> 'a) -> 'b -> 'c //│ = [Function (anonymous)] :e c2__ c2__ K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ? -> ? -> 'a //│ ╙── //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] // -------------- :RecursiveTypes iter2 iter2 K //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] res id //│ res: anything -> 'a //│ where //│ 'a :> forall 'b. anything -> 'b -> ('a | 'b) //│ = [Function (anonymous)] r1 = res id id id id id //│ r1: 'a -> ('b | 'a) //│ where //│ 'b :> forall 'c. 'c -> (anything -> 'b | 'c) //│ = [Function: id] r1 iter2 iter2 K //│ res: ('a & 'b & 'c) -> (anything -> (anything -> 'b | 'd) | 'c | 'e) //│ where //│ 'e :> forall 'f. anything -> 'f -> ('e | 'f) //│ 'a :> anything -> 'd //│ 'd :> anything -> 'a //│ = [Function (anonymous)] r = r1 iter2 iter2 //│ r: ('a -> 'b & 'b -> 'c & 'd -> 'e & 'e -> ('d & 'f)) -> (('a & 'd) -> ('c | 'f) | 'g) //│ where //│ 'g :> forall 'h. 'h -> (anything -> 'g | 'h) //│ = [Function (anonymous)] r iter2 //│ res: ('a -> ('a & 'b) & 'b -> ('a & 'b & 'c) & 'd -> 'e & 'e -> ('d & 'f) & 'g) -> (('a & 'd) -> ('c | 'f) | 'g | 'h) //│ where //│ 'h :> forall 'i. anything -> 'i -> ('h | 'i) //│ = [Function (anonymous)] :NoRecursiveTypes // -------------- // ============== :ConstrainedTypes :DontDistributeForalls // ============== id x = x iter2 f x = f(f x) //│ id: 'a -> 'a //│ = [Function: id1] //│ iter2: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ 'a <: 'b -> 'c & 'c -> 'd) //│ = [Function: iter21] iter2 iter2 //│ res: 'a -> 'b //│ where //│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f //│ where //│ 'c <: 'd -> 'e & 'e -> 'f) <: 'a -> 'g & 'g -> 'b //│ = [Function (anonymous)] id iter2 iter2 //│ res: 'a -> 'b //│ where //│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f //│ where //│ 'c <: 'd -> 'e & 'e -> 'f) <: 'a -> 'g & 'g -> 'b //│ = [Function (anonymous)] iter2 iter2 K //│ res: 'a -> 'b //│ where //│ forall 'c 'd 'e. 'c -> 'e //│ where //│ forall 'f. 'f -> anything -> 'f <: 'c -> 'd & 'd -> 'e <: 'a -> 'g & 'g -> 'b //│ = [Function (anonymous)] // (λzy. y(zI)(zK))(λx. xx). foo = (fun z -> fun y -> y (z I) (z K)) //│ foo: 'a -> (forall 'b 'c 'd. ('b -> 'c -> 'd) -> 'd //│ where //│ 'a <: (forall 'e. 'e -> 'e) -> 'b & (forall 'f. 'f -> anything -> 'f) -> 'c) //│ = [Function: foo1] foo (fun x -> x x) //│ res: ('a -> 'b -> 'c) -> 'c //│ where //│ forall 'd 'e. ('d -> 'e & 'd) -> 'e <: (forall 'f. 'f -> 'f) -> 'a & (forall 'g. 'g -> anything -> 'g) -> 'b //│ = [Function (anonymous)] n0_ s z = z def n0: forall 'X. ('X -> 'X) -> 'X -> 'X n0 = n0_ //│ n0_: anything -> (forall 'a. 'a -> 'a) //│ = [Function: n0_1] //│ n0: ('X -> 'X) -> 'X -> 'X //│ = //│ anything -> (forall 'a. 'a -> 'a) //│ <: n0: //│ ('X -> 'X) -> 'X -> 'X //│ = [Function: n0_1] // :d succ_ n s z = s (n s z) //│ succ_: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ = [Function: succ_1] def succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ = :e // * Needs distrib succ = succ_ //│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ <: succ: //│ (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X0. ('X0 -> 'X0) -> 'X0 -> 'X0) //│ ╔══[ERROR] Type error in def definition //│ ║ l.294: succ = succ_ //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `'X` leaks out of its scope //│ ║ l.290: def succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ║ ^^ //│ ╟── back into type variable `'X` //│ ║ l.290: def succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.283: succ_ n s z = s (n s z) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.294: succ = succ_ //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.290: def succ: (forall 'X. ('X -> 'X) -> 'X -> 'X) -> (forall 'X. ('X -> 'X) -> 'X -> 'X) //│ ╙── ^^ //│ = [Function: succ_1] c2 = succ (succ n0) //│ c2: ('X -> 'X) -> 'X -> 'X //│ = [Function (anonymous)] :e c2 c2 K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ? -> 'a //│ ╙── //│ res: ('a & 'X) -> (anything -> 'a | 'X) //│ where //│ 'X :> anything -> 'X //│ = [Function (anonymous)] c2_ = succ_ (succ_ n0) //│ c2_: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ forall 'e. 'e -> (forall 'f 'g 'h. 'f -> 'h //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'e -> 'f -> 'g //│ 'e <: 'g -> 'h) <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] c2_ c2_ //│ res: 'a -> 'b //│ where //│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'c -> 'd -> 'e //│ 'c <: 'e -> 'f) <: (forall 'g. 'g -> (forall 'h 'i 'j. 'h -> 'j //│ where //│ forall 'k. 'k -> (forall 'l 'm 'n. 'l -> 'n //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'k -> 'l -> 'm //│ 'k <: 'm -> 'n) <: 'g -> 'h -> 'i //│ 'g <: 'i -> 'j)) -> 'a -> 'o //│ forall 'g. 'g -> (forall 'h 'i 'j. 'h -> 'j //│ where //│ forall 'k. 'k -> (forall 'l 'm 'n. 'l -> 'n //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'k -> 'l -> 'm //│ 'k <: 'm -> 'n) <: 'g -> 'h -> 'i //│ 'g <: 'i -> 'j) <: 'o -> 'b //│ = [Function (anonymous)] :e c2_ c2_ K //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. ('b & 'e) -> (? -> 'e | 'd) //│ where //│ forall 'f. 'f -> (forall 'g 'h 'i. 'g -> 'i //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'f -> 'g -> 'h //│ 'f <: 'h -> 'i) <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd //│ ╙── //│ res: 'a -> 'b //│ where //│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'c -> 'd -> 'e //│ 'c <: 'e -> 'f) <: (forall 'g 'h 'i. 'g -> 'i //│ where //│ forall 'j. 'j -> (forall 'k 'l 'm. 'k -> 'm //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'j -> 'k -> 'l //│ 'j <: 'l -> 'm) <: 'n -> 'g -> 'h //│ 'n <: 'h -> 'i) -> 'a -> 'o //│ forall 'g 'h 'i. 'g -> 'i //│ where //│ forall 'j. 'j -> (forall 'k 'l 'm. 'k -> 'm //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 'j -> 'k -> 'l //│ 'j <: 'l -> 'm) <: 'n -> 'g -> 'h //│ 'n <: 'h -> 'i <: 'o -> 'b //│ where //│ 'n :> forall 'p 'q 'r 's. ('p & 's) -> (anything -> 's | 'r) //│ where //│ forall 't. 't -> (forall 'u 'v 'w. 'u -> 'w //│ where //│ forall 'X. ('X -> 'X) -> 'X -> 'X <: 't -> 'u -> 'v //│ 't <: 'v -> 'w) <: 'n -> 'p -> 'q //│ 'n <: 'q -> 'r //│ = [Function (anonymous)] c2__ = succ_ (succ_ n0_) //│ c2__: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ forall 'e. 'e -> (forall 'f 'g 'h. 'f -> 'h //│ where //│ anything -> (forall 'i. 'i -> 'i) <: 'e -> 'f -> 'g //│ 'e <: 'g -> 'h) <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] c2__ c2__ //│ res: 'a -> 'b //│ where //│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f //│ where //│ anything -> (forall 'g. 'g -> 'g) <: 'c -> 'd -> 'e //│ 'c <: 'e -> 'f) <: (forall 'h. 'h -> (forall 'i 'j 'k. 'i -> 'k //│ where //│ forall 'l. 'l -> (forall 'm 'n 'o. 'm -> 'o //│ where //│ anything -> (forall 'g. 'g -> 'g) <: 'l -> 'm -> 'n //│ 'l <: 'n -> 'o) <: 'h -> 'i -> 'j //│ 'h <: 'j -> 'k)) -> 'a -> 'p //│ forall 'h. 'h -> (forall 'i 'j 'k. 'i -> 'k //│ where //│ forall 'l. 'l -> (forall 'm 'n 'o. 'm -> 'o //│ where //│ anything -> (forall 'g. 'g -> 'g) <: 'l -> 'm -> 'n //│ 'l <: 'n -> 'o) <: 'h -> 'i -> 'j //│ 'h <: 'j -> 'k) <: 'p -> 'b //│ = [Function (anonymous)] c2__ c2__ K //│ res: 'a -> 'b //│ where //│ forall 'c. 'c -> (forall 'd 'e 'f. 'd -> 'f //│ where //│ anything -> (forall 'g. 'g -> 'g) <: 'c -> 'd -> 'e //│ 'c <: 'e -> 'f) <: (forall 'h 'i 'j. 'h -> 'j //│ where //│ forall 'k. 'k -> (forall 'l 'm 'n. 'l -> 'n //│ where //│ anything -> (forall 'g. 'g -> 'g) <: 'k -> 'l -> 'm //│ 'k <: 'm -> 'n) <: (forall 'o. 'o -> anything -> 'o) -> 'h -> 'i //│ forall 'o. 'o -> anything -> 'o <: 'i -> 'j) -> 'a -> 'p //│ forall 'h 'i 'j. 'h -> 'j //│ where //│ forall 'k. 'k -> (forall 'l 'm 'n. 'l -> 'n //│ where //│ anything -> (forall 'g. 'g -> 'g) <: 'k -> 'l -> 'm //│ 'k <: 'm -> 'n) <: (forall 'o. 'o -> anything -> 'o) -> 'h -> 'i //│ forall 'o. 'o -> anything -> 'o <: 'i -> 'j <: 'p -> 'b //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/fcp/ToChurchSimplif_CT.mls ================================================ // * This test contains simplifications of the to_church function, // * used to minimize and reproduce problems with to_church and related definitions. :NoRecursiveTypes :ConstrainedTypes :DontDistributeForalls type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ Defined type alias ChurchInt def zero: ChurchInt def succ: ChurchInt -> ChurchInt //│ zero: ChurchInt //│ = //│ succ: ChurchInt -> ChurchInt //│ = def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ to_church: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = def z f x = x //│ z: anything -> (forall 'a. 'a -> 'a) //│ = [Function: z] // * Simpler functions that can be used instead of `s` to debug // def s n f x = f (n f x) // * original succ // def s n f = f (n f) def s n f x = (n f x) // def s n f = n f //│ s: 'a -> (forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd)) //│ = [Function: s] :ns s //│ res: forall 'a. 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'd)) //│ where //│ 'd <: 'c -> 'e //│ = [Function: s] :e // * Since "sound extrusion" succ = s //│ 'a -> (forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd)) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.50: succ = s //│ ║ ^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.50: succ = s //│ ╙── ^ //│ = [Function: s] :e // * Since "sound extrusion" succ n f = n f //│ 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ 'a <: 'b -> 'c) //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.75: succ n f = n f //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.75: succ n f = n f //│ ╙── ^^^^^^^ //│ = [Function: succ2] succ n = n //│ 'a -> 'a //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: succ3] def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) //│ succ_min: (forall 'N. 'N -> 'N) -> (forall 'M. 'M -> 'M) //│ = :e // * Since "sound extrusion" succ_min n f = n f //│ 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ 'a <: 'b -> 'c) //│ <: succ_min: //│ (forall 'N. 'N -> 'N) -> (forall 'M. 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.107: succ_min n f = n f //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.102: def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.102: def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.107: succ_min n f = n f //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.107: succ_min n f = n f //│ ╙── ^^^ //│ = [Function: succ_min] :e rec def to_ch n = if n == 0 then zero else s (to_ch n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ to_ch: number -> 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ = //│ zero is not implemented :e to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ number -> 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.152: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = //│ to_ch and zero are not implemented :e rec def to_ch n = if true then zero else s (to_ch n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ to_ch: anything -> 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ = //│ zero is not implemented :e to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ ╙── //│ anything -> 'a //│ where //│ 'a :> forall 'b. 'b -> (forall 'c 'd. 'c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) | ChurchInt //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.193: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = //│ to_ch and zero are not implemented rec def to_ch n = if true then zero else s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ = //│ zero is not implemented :e to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.225: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.217: else s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.225: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch and zero are not implemented def to_ch n = if true then z else s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd. ('b & 'd) -> ('c | 'd) //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch and zero are not implemented :e // * Since "sound extrusion" to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'a 'b 'c 'd. ('b & 'd) -> ('c | 'd) //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.264: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.256: else s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.264: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch and zero are not implemented def to_ch n = if true then zero else s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ = //│ zero is not implemented :e // * Since "sound extrusion" to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.303: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.295: else s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.303: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch and zero are not implemented def to_ch n = s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch and zero are not implemented :e // * Since "sound extrusion" to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.341: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.333: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.341: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch and zero are not implemented rec def to_ch n = s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.379: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.371: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.379: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented def to_ch (n:int) = s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e // * Since "sound extrusion" to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.417: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.409: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.417: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented // * Marking it `rec` (on a minimized version) makes it fail // * This is because of `destroyConstrainedTypes` which is invoked on rec defs: // * (disabling the destruction fixes this problem) rec def to_ch n = s (to_church n) //│ to_ch: int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch //│ int -> (forall 'a. 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c)) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.458: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.450: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.458: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented def to_ch = s (to_church 0) //│ to_ch: 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch //│ 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.496: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type `int` is not a function //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ╙── ^^^^^^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented // * But distributivity allows getting around the recursion limitation by recovering _some_ of the lost expressiveness :DistributeForalls rec def to_ch n = s (to_church n) //│ to_ch: int -> 'a -> ('b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented to_church = to_ch //│ int -> 'a -> ('b -> 'c //│ where //│ forall 'M. ('M -> 'M) -> 'M -> 'M <: 'a -> 'b -> 'c) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented rec def to_ch n = if n == 0 then zero else succ (to_ch (n - 1)) //│ to_ch: int -> ChurchInt //│ = //│ zero is not implemented to_church = to_ch //│ int -> ChurchInt //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = //│ to_ch and zero are not implemented :e rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. (? & 'b) -> (('e & 'c) -> ('e | 'd) //│ where //│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ to_ch: int -> 'a -> ('b -> ('c | 'b) //│ where //│ 'd <: 'a -> 'b -> 'c) //│ where //│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) //│ where //│ 'd <: 'e -> 'f -> 'g) //│ = [Function: to_ch7] :e // * Since the removal of "recursive definition hacks" to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. (? & 'b) -> (('e & 'c) -> ('e | 'd) //│ where //│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ int -> 'a -> ('b -> ('c | 'b) //│ where //│ 'd <: 'a -> 'b -> 'c) //│ where //│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) //│ where //│ 'd <: 'e -> 'f -> 'g) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.572: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch7] :e // occurs-check rec def to_ch_weird n = s (to_ch_weird n) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd. 'b -> ('c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ to_ch_weird: anything -> 'a -> ('b -> 'c //│ where //│ 'd <: 'a -> 'b -> 'c) //│ where //│ 'd :> forall 'e 'f 'g. 'e -> ('f -> 'g //│ where //│ 'd <: 'e -> 'f -> 'g) //│ = [Function: to_ch_weird] :e // * Since the removal of "recursive definition hacks" to_church = to_ch //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'b 'c 'd 'e. (? & 'b) -> (('e & 'c) -> ('e | 'd) //│ where //│ 'a <: 'b -> 'c -> 'd) //│ ╙── //│ int -> 'a -> ('b -> ('c | 'b) //│ where //│ 'd <: 'a -> 'b -> 'c) //│ where //│ 'd :> forall 'e 'f 'g 'h. 'e -> (('h & 'f) -> ('h | 'g) //│ where //│ 'd <: 'e -> 'f -> 'g) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Subtyping constraint of the form `forall ?to_ch. ?to_ch <: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M)` exceeded recursion depth limit (250) //│ ║ l.614: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: to_ch7] ================================================ FILE: shared/src/test/diff/fcp/ToChurchSimplif_ST.mls ================================================ // * This test contains simplifications of the to_church function, // * used to minimize and reproduce problems with to_church and related definitions. :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ Defined type alias ChurchInt def zero: ChurchInt def succ: ChurchInt -> ChurchInt //│ zero: ChurchInt //│ = //│ succ: ChurchInt -> ChurchInt //│ = def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ to_church: int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = def z f x = x //│ z: anything -> (forall 'a. 'a -> 'a) //│ = [Function: z] // * Simpler functions that can be used instead of `s` to debug // def s n f x = f (n f x) // * original succ // def s n f = f (n f) def s n f x = (n f x) // def s n f = n f //│ s: ('a -> 'b -> 'c) -> 'a -> 'b -> 'c //│ = [Function: s] :ns s //│ res: forall 'a 'b 'c 'd 'e. 'a -> (forall 'f. 'f -> (forall 'g 'h. 'g -> 'h)) //│ where //│ 'h :> 'e //│ 'g <: 'd //│ 'f <: 'b //│ 'a <: 'b -> 'c //│ 'c <: 'd -> 'e //│ = [Function: s] :e // * Works with CT succ = s //│ ('a -> 'b -> 'c) -> 'a -> 'b -> 'c //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.50: succ = s //│ ║ ^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.50: succ = s //│ ╙── ^ //│ = [Function: s] :e // * Works with CT succ n f = n f //│ ('a -> 'b) -> 'a -> 'b //│ <: succ: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.73: succ n f = n f //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type variable `'N` leaks out of its scope //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── back into type variable `'N` //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.73: succ n f = n f //│ ╙── ^^^^^^^ //│ = [Function: succ2] succ n = n //│ 'a -> 'a //│ <: succ: //│ ChurchInt -> ChurchInt //│ = [Function: succ3] def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) //│ succ_min: (forall 'N. 'N -> 'N) -> (forall 'M. 'M -> 'M) //│ = :e // * Works with CT succ_min n f = n f //│ ('a -> 'b) -> 'a -> 'b //│ <: succ_min: //│ (forall 'N. 'N -> 'N) -> (forall 'M. 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.103: succ_min n f = n f //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.98: def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.98: def succ_min : (forall 'N. ('N -> 'N)) -> (forall 'M. ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.103: succ_min n f = n f //│ ╙── ^^^ //│ = [Function: succ_min] rec def to_ch n = if n == 0 then zero else s (to_ch n) //│ to_ch: number -> (('N -> 'N) -> 'N -> 'N | ChurchInt) //│ = //│ zero is not implemented :e to_church = to_ch //│ number -> (('N -> 'N) -> 'N -> 'N | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.132: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.126: else s (to_ch n) //│ ║ ^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.132: to_church = to_ch //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ = //│ to_ch and zero are not implemented rec def to_ch n = if true then zero else s (to_ch n) //│ to_ch: anything -> (('N -> 'N) -> 'N -> 'N | ChurchInt) //│ = //│ zero is not implemented :e to_church = to_ch //│ anything -> (('N -> 'N) -> 'N -> 'N | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.170: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.164: else s (to_ch n) //│ ║ ^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.170: to_church = to_ch //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.10: type ChurchInt = forall 'N. ('N -> 'N) -> ('N -> 'N) //│ ╙── ^^ //│ = //│ to_ch and zero are not implemented rec def to_ch n = if true then zero else s (to_church n) //│ to_ch: int -> (('M -> 'M) -> 'M -> 'M | ChurchInt) //│ = //│ zero is not implemented :e // * Works with :GeneralizeArguments to_church = to_ch //│ int -> (('M -> 'M) -> 'M -> 'M | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.208: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.202: else s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.208: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch and zero are not implemented def to_ch n = if true then z else s (to_church n) //│ to_ch: int -> ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch and zero are not implemented :e // * Works with CT, :GeneralizeArguments to_church = to_ch //│ int -> ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.243: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.237: else s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.243: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch and zero are not implemented def to_ch n = if true then zero else s (to_church n) //│ to_ch: int -> (('M -> 'M) -> 'M -> 'M | ChurchInt) //│ = //│ zero is not implemented :e // * Works with CT, :GeneralizeArguments to_church = to_ch //│ int -> (('M -> 'M) -> 'M -> 'M | ChurchInt) //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.278: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.272: else s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.278: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch and zero are not implemented def to_ch n = s (to_church n) //│ to_ch: int -> ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch and zero are not implemented :e // * Works with CT to_church = to_ch //│ int -> ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.312: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.306: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.312: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch and zero are not implemented rec def to_ch n = s (to_church n) //│ to_ch: int -> ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch //│ int -> ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.346: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.340: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.346: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented def to_ch (n:int) = s (to_church n) //│ to_ch: int -> ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e // * Works with CT to_church = to_ch //│ int -> ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.380: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.374: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.380: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented // * Marking it `rec` (on a minimized version) makes it fail // * This is because of `destroyConstrainedTypes` which is invoked on rec defs: // * (disabling the destruction fixes this problem) rec def to_ch n = s (to_church n) //│ to_ch: int -> ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch //│ int -> ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type error in def definition //│ ║ l.417: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'M` leaks out of its scope //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── back into type variable `'M` //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.33: def s n f x = (n f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.411: s (to_church n) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.417: to_church = to_ch //│ ╙── ^^^^^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented def to_ch = s (to_church 0) //│ to_ch: ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented :e to_church = to_ch //│ ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.451: to_church = to_ch //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type `int` is not a function //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.20: def to_church: int -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) //│ ║ ^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.33: def s n f x = (n f x) //│ ╙── ^ //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented // * But distributivity allows getting around the recursion limitation by recovering _some_ of the lost expressiveness :DistributeForalls rec def to_ch n = s (to_church n) //│ to_ch: int -> ('M -> 'M) -> 'M -> 'M //│ = //│ to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented to_church = to_ch //│ int -> ('M -> 'M) -> 'M -> 'M //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = //│ to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch, to_church, to_ch and zero are not implemented rec def to_ch n = if n == 0 then zero else succ (to_ch (n - 1)) //│ to_ch: int -> ChurchInt //│ = //│ zero is not implemented to_church = to_ch //│ int -> ChurchInt //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = //│ to_ch and zero are not implemented rec def to_ch n = if n == 0 then z else s (to_ch (n - 1)) //│ to_ch: int -> anything -> 'a -> 'a //│ = [Function: to_ch7] to_church = to_ch //│ int -> anything -> 'a -> 'a //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: to_ch7] rec def to_ch_weird n = s (to_ch_weird n) //│ to_ch_weird: anything -> anything -> anything -> nothing //│ = [Function: to_ch_weird] to_church = to_ch //│ int -> anything -> 'a -> 'a //│ <: to_church: //│ int -> (forall 'M. ('M -> 'M) -> 'M -> 'M) //│ = [Function: to_ch7] ================================================ FILE: shared/src/test/diff/fcp/Vec.mls ================================================ // :GeneralizeArguments // Type of cons is much simpler without this... :AllowTypeErrors // TODO class Z class S[P] method Inv: P -> P method Inv = id //│ Defined class Z //│ Defined class S[=P] //│ Declared S.Inv: S['P] -> 'P -> 'P //│ Defined S.Inv: S['P] -> 'a -> 'a :IrregularTypes class Cons[A, N]: { size: S[N]; head: A; tail: Vec[A, N] } class Nil: { size: Z } type Vec[A, N] = forall 'r. (forall 'p. (Nil & { size: N } | Cons[A, S['p]] & Cons[A, N]) -> 'r) -> 'r //│ Defined class Cons[+A, =N] //│ Defined class Nil //│ Defined type alias Vec[+A, =N] type AnyVec[A] = forall 'r. (forall 'p. Nil | Cons[A, S['p]] -> 'r) -> 'r //│ Defined type alias AnyVec[+A] def Nil = Nil { size = Z{} } def Cons head tail = Cons { head; tail; size = S{} } //│ Nil: Nil //│ Cons: ('head & 'A) -> (Vec['A, 'N] & 'tail) -> (Cons['A, 'N] with {head: 'head, size: forall 'P. S['P], tail: 'tail}) def nil k = k Nil def cons h t k = k (Cons h t) //│ nil: (Nil -> 'a) -> 'a //│ cons: ('head & 'A) -> (Vec['A, 'N] & 'tail) -> ((Cons['A, 'N] with {head: 'head, size: forall 'P. S['P], tail: 'tail}) -> 'a) -> 'a nil_ty = nil : Vec[int, Z] //│ nil_ty: Vec[int, Z] def cons_ty : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ cons_ty: 'a -> Vec['a, 'n] -> Vec['a, S['n]] // * Note: test cases like this are quite slow... this one seems to take about 100ms!! cons_ty = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ where //│ 'n := S['n] //│ <: cons_ty: //│ 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.46: cons_ty = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `'n` is not an instance of type `S` //│ ║ l.42: def cons_ty : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.46: cons_ty = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^^^^ //│ ╟── from type variable: //│ ║ l.46: cons_ty = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ╙── ^^ cons_ty2 = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ cons_ty2: 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ where //│ 'n := S['n] type Cons_ty = forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ Defined type alias Cons_ty // :exit // ==================================================================================================== cons_ty = cons : Cons_ty //│ Cons_ty //│ <: cons_ty: //│ 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.104: cons_ty = cons : Cons_ty //│ ║ ^^^^ //│ ╟── expression of type `S[in 'n & 'p out 'n | 'p]` does not match type `'n` //│ ╟── Note: constraint arises from type variable: //│ ║ l.70: type Cons_ty = forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^ //│ ╟── Note: class type parameter N is defined at: //│ ║ l.18: class Cons[A, N]: { size: S[N]; head: A; tail: Vec[A, N] } //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.104: cons_ty = cons : Cons_ty //│ ║ ^^^^^^^^^^^^^^ //│ ╟── expression of type `'n` does not match type `'p` //│ ╟── Note: constraint arises from quantified type variable: //│ ║ l.20: type Vec[A, N] = forall 'r. (forall 'p. (Nil & { size: N } | Cons[A, S['p]] & Cons[A, N]) -> 'r) -> 'r //│ ╙── ^^ cons_ty3 = cons : Cons_ty //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.126: cons_ty3 = cons : Cons_ty //│ ║ ^^^^ //│ ╟── expression of type `S[in 'n & 'p out 'n | 'p]` does not match type `'n` //│ ╟── Note: constraint arises from type variable: //│ ║ l.70: type Cons_ty = forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^ //│ ╟── Note: class type parameter N is defined at: //│ ║ l.18: class Cons[A, N]: { size: S[N]; head: A; tail: Vec[A, N] } //│ ╙── ^ //│ cons_ty3: Cons_ty def cons h (t: Vec['a, 'n]) k = k (Cons h t) //│ cons: ('head & 'A) -> Vec['A & 'a, 'n] -> ((Cons['A, 'n] with {head: 'head, size: forall 'P. S['P], tail: Vec['a, 'n]}) -> 'b) -> 'b cons : Cons_ty //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.142: cons : Cons_ty //│ ║ ^^^^ //│ ╟── expression of type `S[in 'n & 'p out 'n | 'p]` does not match type `'n` //│ ╟── Note: constraint arises from type variable: //│ ║ l.70: type Cons_ty = forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^ //│ ╟── from type variable: //│ ║ l.139: def cons h (t: Vec['a, 'n]) k = k (Cons h t) //│ ║ ^^ //│ ╟── Note: class type parameter N is defined at: //│ ║ l.18: class Cons[A, N]: { size: S[N]; head: A; tail: Vec[A, N] } //│ ╙── ^ //│ res: Cons_ty def cons h (t: Vec['a, 'n]) k = k (Cons h t) //│ cons: ('head & 'A) -> Vec['A & 'a, 'n] -> ((Cons['A, 'n] with {head: 'head, size: forall 'P. S['P], tail: Vec['a, 'n]}) -> 'b) -> 'b cons : Cons_ty //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.161: cons : Cons_ty //│ ║ ^^^^ //│ ╟── expression of type `S[in 'n & 'p out 'n | 'p]` does not match type `'n` //│ ╟── Note: constraint arises from type variable: //│ ║ l.70: type Cons_ty = forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^ //│ ╟── from type variable: //│ ║ l.158: def cons h (t: Vec['a, 'n]) k = k (Cons h t) //│ ║ ^^ //│ ╟── Note: class type parameter N is defined at: //│ ║ l.18: class Cons[A, N]: { size: S[N]; head: A; tail: Vec[A, N] } //│ ╙── ^ //│ res: Cons_ty // v0 = nil // v0_ty = v0 : Vec[int, Z] v1_ = Cons 1 nil //│ v1_: Cons[1, 'N] with {size: forall 'P. S['P], tail: forall 'a. (Nil -> 'a) -> 'a} //│ where //│ 'N :> Z v1_ty = v1_ : Cons[int, Z] //│ v1_ty: Cons[int, Z] v1_0 = cons 1 nil //│ v1_0: ((Cons[1, 'n] with {size: forall 'P. S['P], tail: Vec[nothing, 'n]}) -> 'a) -> 'a //│ where //│ 'n :> Z v1_0 : Vec[int, S[Z]] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.195: v1_0 : Vec[int, S[Z]] //│ ║ ^^^^ //│ ╟── application of type `Z` is not an instance of type `S` //│ ║ l.28: def Nil = Nil { size = Z{} } //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.195: v1_0 : Vec[int, S[Z]] //│ ║ ^^^^ //│ ╟── Note: class Z is defined at: //│ ║ l.7: class Z //│ ╙── ^ //│ res: Vec[int, S[Z]] v1_1 k = k v1_ //│ v1_1: ((forall 'N. Cons[1, 'N] with {size: forall 'P. S['P], tail: forall 'a. (Nil -> 'a) -> 'a}) -> 'b) -> 'b //│ where //│ 'N :> Z // :e v1_1 : Vec[int, S[Z]] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.216: v1_1 : Vec[int, S[Z]] //│ ║ ^^^^ //│ ╟── application of type `Z` is not an instance of type `S` //│ ║ l.28: def Nil = Nil { size = Z{} } //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.216: v1_1 : Vec[int, S[Z]] //│ ║ ^^^^ //│ ╟── Note: class Z is defined at: //│ ║ l.7: class Z //│ ╙── ^ //│ res: Vec[int, S[Z]] v1_tty = cons_ty 1 nil_ty //│ v1_tty: Vec[int, S[Z]] v1_tty = cons_ty2 1 nil_ty //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.235: v1_tty = cons_ty2 1 nil_ty //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type `Z` is not an instance of type `S` //│ ║ l.39: nil_ty = nil : Vec[int, Z] //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.65: cons_ty2 = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ║ ^^^^^ //│ ╟── from type variable: //│ ║ l.65: cons_ty2 = cons : forall 'a. 'a -> Vec['a, 'n] -> Vec['a, S['n]] //│ ╙── ^^ //│ v1_tty: error | Vec[int, S['n]] //│ where //│ 'n :> S['n] | Z //│ <: nothing v1_tty = cons_ty3 1 nil_ty //│ v1_tty: Vec[int, S[Z]] v1 = cons 1 nil //│ v1: ((Cons[1, 'n] with {size: forall 'P. S['P], tail: Vec[nothing, 'n]}) -> 'a) -> 'a //│ where //│ 'n :> Z v2 = cons 1 (cons 2 nil) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.262: v2 = cons 1 (cons 2 nil) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Z` is not an instance of type `S` //│ ║ l.28: def Nil = Nil { size = Z{} } //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.20: type Vec[A, N] = forall 'r. (forall 'p. (Nil & { size: N } | Cons[A, S['p]] & Cons[A, N]) -> 'r) -> 'r //│ ║ ^^^^^ //│ ╟── Note: class Z is defined at: //│ ║ l.7: class Z //│ ╙── ^ //│ v2: error def impossible x = case x of {} //│ impossible: nothing -> nothing def head1 vec = vec (fun v -> case v of { | Nil -> impossible v.size | Cons -> v.head }) //│ head1: ((forall 'head. (Cons[?, ?] & {head: 'head} | Nil & {size: nothing}) -> 'head) -> 'a) -> 'a def head1_ty : Vec['a, S['p]] -> 'a head1_ty = head1 //│ head1_ty: Vec['a, S['p]] -> 'a //│ ((forall 'head. (Cons[?, ?] & {head: 'head} | Nil & {size: nothing}) -> 'head) -> 'a) -> 'a //│ <: head1_ty: //│ Vec['a, S['p]] -> 'a def head2 (vec: Vec['a, S['p]]) = vec (fun v -> case v of { | Nil -> impossible v.size | Cons -> v.head }) //│ head2: Vec['a, S['p]] -> 'a head1 v1 head1 v2 //│ res: 1 //│ res: error :e head1 nil //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.306: head1 nil //│ ║ ^^^^^^^^^ //│ ╟── application of type `Z` does not match type `nothing` //│ ║ l.28: def Nil = Nil { size = Z{} } //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.278: def impossible x = case x of {} //│ ║ ^ //│ ╟── from field selection: //│ ║ l.282: | Nil -> impossible v.size //│ ║ ^^^^^^ //│ ╟── Note: class Z is defined at: //│ ║ l.7: class Z //│ ╙── ^ //│ res: error rec def sum vec = vec (fun v -> case v of { | Nil -> 0 | Cons -> v.head + sum v.tail }) //│ sum: 'a -> 'b //│ where //│ 'a <: (((Cons[?, ?]\size with {head: int, tail: 'a}) | Nil) -> int) -> (int & 'b) def sum_ty : Vec[int, 'n] -> int //│ sum_ty: Vec[int, 'n] -> int // * Note: for crazy reasons, this one works out when we extrude top/bot // * instead of specific Extruded types... sum_ty = sum //│ 'a -> 'b //│ where //│ 'a <: (((Cons[?, ?]\size with {head: int, tail: 'a}) | Nil) -> int) -> (int & 'b) //│ <: sum_ty: //│ Vec[int, 'n] -> int //│ ╔══[ERROR] Subtyping constraint of the form `forall ?sum. ?sum <: forall 'n. Vec[int, 'n] -> int` exceeded recursion depth limit (250) //│ ║ l.338: sum_ty = sum //│ ║ ^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. sum nil //│ res: int // * Note: also worked woth top/bot extrusion sum v1_0 //│ ╔══[ERROR] Subtyping constraint of the form `forall ?sum. ?sum <: (forall ?a. ?a) -> ?b` exceeded recursion depth limit (250) //│ ║ l.354: sum v1_0 //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error // * Note: also worked woth top/bot extrusion sum v2 //│ res: error // === === === ERROR CASES === === === // :ShowRelativeLineNums :AllowTypeErrors def head1_ty v = case v of { | Nil -> impossible v.size | Cons -> v.head } //│ (Cons[?, ?] & {head: 'head} | Nil & {size: nothing}) -> 'head //│ <: head1_ty: //│ Vec['a, S['p]] -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: def head1_ty v = case v of { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+2: | Nil -> impossible v.size //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+3: | Cons -> v.head //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.+4: } //│ ║ ^^^ //│ ╟── type `(forall 'p. (Cons['a, in S[in 'p0 & 'p out 'p0 | 'p] out S[in 'p0 | 'p out 'p0 & 'p]] | (Nil with {size: S['p0]})) -> 'r) -> 'r` does not match type `Cons[?, ?] & ?a | Nil & ?b` //│ ║ l.20: type Vec[A, N] = forall 'r. (forall 'p. (Nil & { size: N } | Cons[A, S['p]] & Cons[A, N]) -> 'r) -> 'r //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.+1: def head1_ty v = case v of { //│ ╙── ^ sum v1_ty //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: sum v1_ty //│ ║ ^^^^^^^^^ //│ ╟── type `Cons[int, Z]` is not a function //│ ║ l.187: v1_ty = v1_ : Cons[int, Z] //│ ║ ^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(forall ?a ?b. ?a -> ?b) -> ?c` //│ ║ l.+1: sum v1_ty //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.325: rec def sum vec = vec (fun v -> case v of { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.326: | Nil -> 0 //│ ║ ^^^^^^^^^^^^ //│ ║ l.327: | Cons -> v.head + sum v.tail //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.328: }) //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.325: rec def sum vec = vec (fun v -> case v of { //│ ╙── ^^^ //│ res: error ================================================ FILE: shared/src/test/diff/fcp/YicongFCP.mls ================================================ def foo = forall 'a. fun (x: 'a) -> x //│ foo: 'a -> 'a //│ = [Function: foo] def foo esc = forall 'T. fun (x: 'T) -> esc x //│ foo: (??T -> 'a) -> anything -> 'a //│ = [Function: foo1] tmp = foo (fun x -> 1) //│ tmp: anything -> 1 //│ = [Function (anonymous)] tmp 0 //│ res: 1 //│ = 1 foo (fun x -> 1) "2" //│ res: 1 //│ = 1 foo id //│ res: anything -> ??T //│ = [Function (anonymous)] r = foo id true //│ r: ??T //│ = true :e not r //│ ╔══[ERROR] Type error in application //│ ║ l.35: not r //│ ║ ^^^^^ //│ ╟── type variable `'T` leaks out of its scope //│ ║ l.10: forall 'T. fun (x: 'T) -> esc x //│ ║ ^^ //│ ╟── into type `bool` //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.10: forall 'T. fun (x: 'T) -> esc x //│ ╙── ^^^ //│ res: bool | error //│ = false def foo esc = forall 'T. fun (x: 'T) -> esc x : 'T //│ foo: (??T -> ??T0) -> 'T -> 'T //│ = [Function: foo2] :e foo id //│ ╔══[ERROR] Type error in application //│ ║ l.57: foo id //│ ║ ^^^^^^ //│ ╟── type variable `'T` leaks out of its scope //│ ║ l.52: forall 'T. fun (x: 'T) -> esc x : 'T //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.52: forall 'T. fun (x: 'T) -> esc x : 'T //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.52: forall 'T. fun (x: 'T) -> esc x : 'T //│ ╙── ^^^^^ //│ res: error | 'T -> 'T //│ = [Function (anonymous)] foo (fun x -> error) //│ res: 'T -> 'T //│ = [Function (anonymous)] def foo (esc: forall 'a. 'a -> 'a) = forall 'T. fun (x: 'T) -> esc x : 'T //│ foo: (forall 'a. 'a -> 'a) -> (forall 'T. 'T -> 'T) //│ = [Function: foo3] foo id //│ res: 'T -> 'T //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/fcp-lit/Boxy.mls ================================================ :NoJS :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ // Char is represented as String // All type constructors beside arrow are invariant in Boxy class None class Some[a]: { val: a } type Option[a] = None | Some[a] //│ Defined class None //│ Defined class Some[+a] //│ Defined type alias Option[+a] class List[a] method Head: a //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a def cons[a]: a -> List[a] -> List[a] //│ cons: 'a -> List['a] -> List['a] def nil: List['a] //│ nil: List[nothing] // ============ Type signatures for functions used in the examples ============ def head: forall 'a. List['a] -> 'a //│ head: List['a] -> 'a def tail: forall 'a. List['a] -> List['a] //│ tail: List['a] -> List['a] def single: forall 'a. 'a -> List['a] //│ single: 'a -> List['a] def length: forall 'a. List['a] -> int //│ length: List[?] -> int def ids: List[forall 'a. 'a -> 'a] //│ ids: List[forall 'a. 'a -> 'a] // ============ Sec 1 ============ def f get = (get (cons 1 (cons 2 nil)), get (cons "a" (cons "b" (cons "c" nil)))) //│ f: (List[1 | 2] -> 'a & List["a" | "b" | "c"] -> 'b) -> ('a, 'b,) // definition in paper: // g None = (0, "0") // g (Some get) = (get [1,2], get ["a","b","c"]) def g x = case x of { None -> (0, "0") | Some -> (x.val (cons 1 (cons 2 nil)), x.val (cons "a" (cons "b" (cons "c" nil)))) } //│ g: (None | Some[?] & {val: List[1 | 2] -> 'a & List["a" | "b" | "c"] -> 'b}) -> (0 | 'a, "0" | 'b,) // ============ Sec 2 ============ // FreezeML C1 length ids //│ res: int // ============ Sec 3.2 ============ (fun x -> fun g -> (g true, g 3)) 4 //│ res: (true -> 'a & 3 -> 'b) -> ('a, 'b,) // ============ Sec 3.3 ============ // let-bound version of id, with lexically scoped type variables (TODO: support this) // def id: forall 'a. 'a -> 'a // def id = fun x -> let y = x: 'a in y fun x -> let y = x in y //│ res: 'a -> 'a // ============ Sec 4.4 ============ let f = (fun g -> (g 3, g true)): (forall 'a. 'a -> 'a) -> (int, bool) in f (fun x -> x) let f = fun g -> (g 3, g true) in f (fun x -> x) //│ res: (int, bool,) //│ res: (3, true,) (fun g -> (g 3, g true)) (fun x -> x) //│ res: (3, true,) // ============ Sec 6 ============ // FreezeML C2 tail ids //│ res: List[forall 'a. 'a -> 'a] // FreezeML C4 single id //│ res: List[forall 'a. 'a -> 'a] // FreezeML C3 head ids //│ res: 'a -> 'a // FreezeML C6 cons (fun x -> x) ids //│ res: List[forall 'a. 'a -> 'a] let f = fun x -> nil in f (fun g -> (g 3, g true)) //│ res: List[nothing] // ============ Sec 7 ============ // eta-expansion does not preserve typability (in the presented system) def f: (int -> (forall 'a. 'a -> 'a)) -> int fun x -> f x // Not typeable in Boxy Types! //│ f: (int -> (forall 'a. 'a -> 'a)) -> int //│ res: (int -> (forall 'a. 'a -> 'a)) -> int ================================================ FILE: shared/src/test/diff/fcp-lit/CPS_LB.mls ================================================ :NoJS :NoRecursiveTypes // ------------ Dummy classes to represent the types in the examples ------------ class List[a] method Get: a //│ Defined class List[+a] //│ Declared List.Get: List['a] -> 'a // Used to represent `::` def cons: 'a -> List['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] // Used to represent `[]` def nil: List['a] //│ nil: List[nothing] def concat: string -> List[string] -> string //│ concat: string -> List[string] -> string // ------------ Examples ------------ // (* s starts a sequence, cps style *) def s k = k () nil //│ s: (() -> List[nothing] -> 'a) -> 'a def insert v () acu k = k () (cons v acu) //│ insert: 'a -> () -> List['a] -> (() -> List['a] -> 'b) -> 'b // (* x inserts "x" in the acu *) def x () = insert "x" () //│ x: () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b // (* We call x a 'token', in the sequence. *) // (* e ends the sequence. It builds a token that can be inserted in another sequence or that can be printed. *) def e () acu = insert (concat "," acu) //│ e: () -> List[string] -> () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b // (* Prints a token *) def print t = t () nil (fun () -> fun r -> log (concat "," r)) //│ print: (() -> List[nothing] -> (() -> List[string] -> unit) -> 'a) -> 'a // Added by me: s x x (s x x e) //│ res: (() -> List[string] -> 'a) -> 'a def test8 = (s x x (s x x e) x (s x x x e) e) //│ test8: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b print test8 def test14 = (s x x x x x x x x x x x x x x e) //│ test14: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b def test16 = (s x x x x x x x x x x x x x x x x e) //│ test16: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b // (* This is too much for the type-checker. *) def test18 = (s x x x x x x x x x x x x x x x x x x e) //│ test18: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b :RecursiveTypes def test18 = (s x x x x x x x x x x x x x x x x x x e) //│ test18: () -> List['a] -> (() -> List[string | 'a] -> 'b) -> 'b :NoRecursiveTypes // (* A function that receives a token *) def f t = (s x x t x x e) //│ f: (() -> List["x"] -> (forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> (forall 'a 'c. () -> List['a] -> (() -> List["x" | 'a] -> 'c) -> 'c) -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'd) -> 'd) -> 'e) -> 'e // (* If the token is used twice, we must reveive two arguments *) def g t1 t2 = (s x x t1 x (s x t2 x e) e) //│ g: (() -> List["x"] -> (forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> 'c -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'd) -> 'd) -> 'e) -> (() -> List["x"] -> (forall 'a 'f. () -> List['a] -> (() -> List["x" | 'a] -> 'f) -> 'f) -> (forall 'a0 'g. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'g) -> 'g) -> 'c) -> 'e // (* This does not type. It requires first-class polymorphism. *) def h t = g t t //│ h: (() -> List["x"] -> ((forall 'a 'b. () -> List['a] -> (() -> List["x" | 'a] -> 'b) -> 'b) -> 'c -> (forall 'a0 'd. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'd) -> 'd) -> 'e & (forall 'a 'f. () -> List['a] -> (() -> List["x" | 'a] -> 'f) -> 'f) -> (forall 'a0 'g. () -> List[string] -> () -> List['a0] -> (() -> List[string | 'a0] -> 'g) -> 'g) -> 'c)) -> 'e ================================================ FILE: shared/src/test/diff/fcp-lit/FreezeML.mls ================================================ // * FreezeML: complete and easy type inference for first-class polymorphism // * https://dl.acm.org/doi/10.1145/3385412.3386003 // * Extended version: https://arxiv.org/abs/2004.00396 :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Nil[a]: List[a] method Head = error method Tail = this //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] class ST[S, A] method Inv_S: S -> S method Cov_A: A //│ Defined class ST[=S, +A] //│ Declared ST.Inv_S: ST['S, ?] -> 'S -> 'S //│ Declared ST.Cov_A: ST['S, 'A] -> 'A // ============ Type signatures for functions used in the examples ============ def head: List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] // Used to represent `[]` in the papers def nil: List['a] nil = Nil {} //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} def cons: 'a -> List['a] -> List['a] def cons head tail = Cons { head; tail } //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] def is_empty: forall 'a. List['a] -> bool def is_empty l = eq l nil //│ is_empty: List[?] -> bool //│ = //│ anything -> bool //│ <: is_empty: //│ List[?] -> bool //│ = [Function: is_empty] def single: 'a -> List['a] def single x = cons x nil //│ single: 'a -> List['a] //│ = //│ 'a -> List['a] //│ <: single: //│ 'a -> List['a] //│ = [Function: single] def append: List['a] -> List['a] -> List['a] rec def append l1 l2 = if is_empty l1 then l2 else cons (head l1) (append (tail l1) l2) //│ append: List['a] -> List['a] -> List['a] //│ = //│ List['a] -> List['a] -> List['a] //│ <: append: //│ List['a] -> List['a] -> List['a] //│ = [Function: append] def length: List['a] -> int rec def length l = if is_empty l then 0 else succ (length (tail l)) //│ length: List[?] -> int //│ = //│ List[?] -> int //│ <: length: //│ List[?] -> int //│ = [Function: length1] def id: 'a -> 'a def id x = x //│ id: 'a -> 'a //│ = //│ 'a -> 'a //│ <: id: //│ 'a -> 'a //│ = [Function: id1] def ids: List[forall 'a. 'a -> 'a] def ids = single id //│ ids: List[forall 'a. 'a -> 'a] //│ = //│ List[forall 'a. 'a -> 'a] //│ <: ids: //│ List[forall 'a. 'a -> 'a] //│ = [Function: ids] def inc: int -> int def inc = succ //│ inc: int -> int //│ = //│ int -> int //│ <: inc: //│ int -> int //│ = [Function: inc] def choose: 'a -> 'a -> 'a def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a //│ = //│ 'a -> 'a -> 'a //│ <: choose: //│ 'a -> 'a -> 'a //│ = [Function: choose] def poly: (forall 'a. 'a -> 'a) -> (int, bool) def poly f = (f 1, f true) //│ poly: (forall 'a. 'a -> 'a) -> (int, bool,) //│ = //│ (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ <: poly: //│ (forall 'a. 'a -> 'a) -> (int, bool,) //│ = [Function: poly] def auto : (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) def auto x = x x //│ auto: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto: //│ (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b def auto_ x = x x //│ auto_: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto_: //│ (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] def map: ('a -> 'b) -> List['a] -> List['b] rec def map f l = if is_empty l then nil else cons (f (head l)) (map f (tail l)) //│ map: ('a -> 'b) -> List['a] -> List['b] //│ = //│ ('b -> 'a) -> List['b] -> List['a] //│ <: map: //│ ('a -> 'b) -> List['a] -> List['b] //│ = [Function: map] def app: ('a -> 'b) -> 'a -> 'b def app f x = f x //│ app: ('a -> 'b) -> 'a -> 'b //│ = //│ ('a -> 'b) -> 'a -> 'b //│ <: app: //│ ('a -> 'b) -> 'a -> 'b //│ = [Function: app] def revapp: 'a -> ('a -> 'b) -> 'b def revapp x f = f x //│ revapp: 'a -> ('a -> 'b) -> 'b //│ = //│ 'a -> ('a -> 'b) -> 'b //│ <: revapp: //│ 'a -> ('a -> 'b) -> 'b //│ = [Function: revapp] def flip: ('a -> 'b -> 'c) -> 'b -> 'a -> 'c def flip f = fun x -> fun y -> f y x //│ flip: ('a -> 'b -> 'c) -> 'b -> 'a -> 'c //│ = //│ ('a -> 'b -> 'c) -> 'b -> 'a -> 'c //│ <: flip: //│ ('a -> 'b -> 'c) -> 'b -> 'a -> 'c //│ = [Function: flip] :ng def runST: (forall 's. ST['s, 'v]) -> 'v //│ runST: (forall 's. ST['s, 'v]) -> 'v :ng def argST: ST['s, int] //│ argST: ST['s, int] // ============ A. polymorphic instantiation ============ // A1 const2 x y = y //│ const2: anything -> 'a -> 'a //│ = [Function: const2] // A2 choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // A3 choose nil ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // A4 fun (x: forall 'a. 'a -> 'a) -> x x //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: res] // A4' fun x -> x x //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // A5 id auto //│ res: (forall 'a. 'a -> 'a) -> (forall 'b. 'b -> 'b) //│ = [Function: auto] // A6 id auto_ //│ res: (forall 'a. 'a -> 'a) -> 'b -> 'b //│ = [Function: auto_] // A7 choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] // A8 :ns choose id auto_ //│ res: 'c //│ where //│ 'c :> (forall 'a. 'a -> 'a) | (forall 'b. (forall 'a0. 'a0 -> 'a0) -> 'b -> 'b) //│ = [Function: id1] // A9 def f: ('a -> 'a) -> List['a] -> 'a //│ f: ('a -> 'a) -> List['a] -> 'a //│ = :ng f (choose id) ids //│ res: 'a -> 'a // A10 poly id //│ res: (int, bool,) //│ = [ 1, true ] // A11 poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // A12 id poly (fun x -> x) //│ res: (int, bool,) //│ = [ 1, true ] // ============ B. inference of polymorphic arguments ============ // B1 fun f -> (f 1, f true) //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // B2 fun xs -> poly (head xs) //│ res: List[forall 'a. 'a -> 'a] -> (int, bool,) //│ = [Function: res] // ============ C. functions on polymorphic lists ============ // C1 length ids //│ res: int //│ = 1 // C2 tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // C3 head ids //│ res: 'a -> 'a //│ = [Function: id1] // C4 single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // C5 cons id ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C6 cons (fun x -> x) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C7 append (single inc) (single id) //│ res: List[int -> int] //│ = Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // C8 :ng def g: List['a] -> List['a] -> 'a g (single id) ids //│ g: List['a] -> List['a] -> 'a //│ res: 'a -> 'a // C9 map poly (single id) //│ res: List[(int, bool,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } // C10 map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ============ D. application functions ============ // D1 app poly id //│ res: (int, bool,) //│ = [ 1, true ] // D2 revapp id poly //│ res: (int, bool,) //│ = [ 1, true ] // D3 :ng runST argST //│ res: int // D4 :ng app runST argST //│ res: int // D5 :ng revapp argST runST //│ res: int // ============ E. η-expansion ============ :ng def h: int -> (forall 'a. 'a -> 'a) def k: 'a -> List['a] -> 'a def lst: List[forall 'a. int -> 'a -> 'a] //│ h: int -> (forall 'a. 'a -> 'a) //│ k: 'a -> List['a] -> 'a //│ lst: List[forall 'a. int -> 'a -> 'a] // E1 :ng k h lst //│ res: int -> 'a -> 'a // E2 :ng k (fun x -> h x) lst //│ res: int -> 'a -> 'a // E3 :ng def r: (forall 'a. 'a -> (forall 'b. 'b -> 'b)) -> int //│ r: (forall 'b. anything -> 'b -> 'b) -> int :ng r (fun x -> fun y -> y) //│ res: int :ng :ns r //│ res: (forall 'a. 'a -> (forall 'b. 'b -> 'b)) -> int // ============ F. FreezeML Programs ============ // F5 auto id //│ res: 'b -> 'b //│ = [Function: id1] // F6 cons (head ids) ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } // F7 (head ids) 3 //│ res: 3 //│ = 3 // F8 choose (head ids) //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // F9 let f = revapp id in f poly //│ res: (int, bool,) //│ = [ 1, true ] // F10 a = choose id (fun (x: (forall 'a. 'a -> 'a)) -> auto_ x) //│ a: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] a id //│ res: 'b -> 'b //│ = [Function: id1] choose id (fun x -> auto_ x) //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] a = choose id auto_ //│ a: (forall 'a. 'a -> 'a & 'a0) -> ('b -> 'b | 'a0) //│ = [Function: id1] a id //│ res: 'b -> 'b //│ = [Function: id1] // ============ Expected error cases (useful for regression-testing error messages) ============ :ns :e // * Skolem extrusion; rightly rejected choose id (fun (x: ('a -> 'a)) -> auto_ x) //│ ╔══[ERROR] Type error in application //│ ║ l.503: choose id (fun (x: ('a -> 'a)) -> auto_ x) //│ ║ ^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.172: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.172: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ╙── ^^ //│ res: 'c //│ where //│ 'c :> (forall 'a. 'a -> 'a) | (forall 'a0 'd. ('a0 -> 'a0) -> 'd) //│ 'd :> (forall 'b. 'b -> 'b) | error //│ 'a0 :> ??a //│ <: ??a0 //│ = [Function: id1] // * The skolem used to (unsafely) leak into this type – see also `Skolems.mls`: res //│ res: (anything -> ??a & 'a) -> (error | 'b -> 'b | 'a) //│ = [Function: id1] :e // skolem extrusion res id //│ ╔══[ERROR] Type error in application //│ ║ l.527: res id //│ ║ ^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.172: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.172: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.503: choose id (fun (x: ('a -> 'a)) -> auto_ x) //│ ╙── ^^ //│ res: error | 'b -> 'b //│ = [Function: id1] :e a (fun x -> { u = x }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.545: a (fun x -> { u = x }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{u: ?a}` does not match type `'a` //│ ║ l.545: a (fun x -> { u = x }) //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.172: def auto_ : (forall 'a. 'a -> 'a) -> 'b -> 'b //│ ╙── ^^ //│ res: error | 'b -> ({u: 'b} | 'b) //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/fcp-lit/GHC-rank-n.mls ================================================ // * Practical type inference for arbitrary-rank types // * Many examples in the Motivation section, incl a rank-3 one // Theorem 4.3 (Distributivity) ⊢dsk ∀a.σ1 → σ2 ≤ (∀a.σ1) → ∀a.σ2. class F[A] method Id: A -> A class G[A] method Id: A -> A //│ Defined class F[=A] //│ Declared F.Id: F['A] -> 'A -> 'A //│ Defined class G[=A] //│ Declared G.Id: G['A] -> 'A -> 'A def f: forall 'a. F['a] -> G['a] //│ f: F['a] -> G['a] //│ = f : (forall 'a. F['a]) -> (forall 'a. G['a]) //│ res: (forall 'a. F['a]) -> (forall 'a0. G['a0]) //│ = //│ f is not implemented :DontDistributeForalls :e f: (forall 'a. F['a]) -> (forall 'a. G['a]) //│ ╔══[ERROR] Type error in type ascription //│ ║ l.32: f: (forall 'a. F['a]) -> (forall 'a. G['a]) //│ ║ ^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.32: f: (forall 'a. F['a]) -> (forall 'a. G['a]) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.32: f: (forall 'a. F['a]) -> (forall 'a. G['a]) //│ ╙── ^ //│ res: (forall 'a. F['a]) -> (forall 'a0. G['a0]) //│ = //│ f is not implemented // "In this example it is plain as a pike-staff that i should have the type (∀a.a → a)" // foo = (\i. (i 3, i True)) :: (∀a.a → a) → (Int, Bool) foo = (fun i -> (i 3, i true)) : (forall 'a. 'a -> 'a) -> (int, bool) //│ foo: (forall 'a. 'a -> 'a) -> (int, bool,) //│ = [Function: foo] // bar has type ∀a.(Int → a) → a // foo w = bar (\x. x+w) def bar: forall 'a. (int -> 'a) -> 'a //│ bar: (int -> 'a) -> 'a //│ = foo w = bar (fun x -> x + w) //│ foo: int -> int //│ = //│ bar is not implemented ================================================ FILE: shared/src/test/diff/fcp-lit/HMF.mls ================================================ :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail def cons[a]: a -> List[a] -> List[a] def cons head tail = Cons { head; tail } //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] class Nil[a]: List[a] method Head = error method Tail = this def nil: List['a] nil = Nil {} //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} class ST[S, A] method Inv_S: S -> S method Cov_A: A //│ Defined class ST[=S, +A] //│ Declared ST.Inv_S: ST['S, ?] -> 'S -> 'S //│ Declared ST.Cov_A: ST['S, 'A] -> 'A // ============ Type signatures for functions used in the examples ============ def head: forall 'a. List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: forall 'a. List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] def is_empty: forall 'a. List['a] -> bool def is_empty l = eq l nil //│ is_empty: List[?] -> bool //│ = //│ anything -> bool //│ <: is_empty: //│ List[?] -> bool //│ = [Function: is_empty] def single: forall 'a. 'a -> List['a] def single x = cons x nil //│ single: 'a -> List['a] //│ = //│ 'a -> List['a] //│ <: single: //│ 'a -> List['a] //│ = [Function: single] def length: forall 'a. List['a] -> int rec def length l = if is_empty l then 0 else succ (length (tail l)) //│ length: List[?] -> int //│ = //│ List[?] -> int //│ <: length: //│ List[?] -> int //│ = [Function: length1] def choose: forall 'a. 'a -> 'a -> 'a def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a //│ = //│ 'a -> 'a -> 'a //│ <: choose: //│ 'a -> 'a -> 'a //│ = [Function: choose] def map: forall 'a 'b. ('a -> 'b) -> List['a] -> List['b] rec def map f l = if is_empty l then nil else cons (f (head l)) (map f (tail l)) //│ map: ('a -> 'b) -> List['a] -> List['b] //│ = //│ ('a -> 'b) -> List['a] -> List['b] //│ <: map: //│ ('a -> 'b) -> List['a] -> List['b] //│ = [Function: map] def runST: (forall 's. ST['s, 'v]) -> 'v //│ runST: (forall 's. ST['s, 'v]) -> 'v //│ = def argST: ST['s, int] //│ argST: ST['s, int] //│ = // ============ Sec 2 ============ def id x = x //│ id: 'a -> 'a //│ = [Function: id] // FreezeML B1 def poly f = (f 1, f true) //│ poly: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: poly] // FreezeML A10 poly id //│ res: (1, true,) //│ = [ 1, true ] def app f x = f x //│ app: ('a -> 'b) -> 'a -> 'b //│ = [Function: app] // FreezeML D1 app poly id //│ res: (1, true,) //│ = [ 1, true ] // FreezeML F2 ids = single id //│ ids: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } idss = single ids //│ idss: List[List[forall 'a. 'a -> 'a]] //│ = Cons { //│ head: Cons { head: [Function: id], tail: Nil {} }, //│ tail: Nil {} //│ } def revapp x f = f x //│ revapp: 'a -> ('a -> 'b) -> 'b //│ = [Function: revapp] // FreezeML D2 revapp id poly //│ res: (1, true,) //│ = [ 1, true ] // FreezeML C1 length ids //│ res: int //│ = 1 // FreezeML C10 map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } app (map head) (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } // nope fun f -> poly f //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // FreezeML D2 // nope in plain HMF revapp id poly //│ res: (1, true,) //│ = [ 1, true ] // nope let f = revapp id in f poly //│ res: (1, true,) //│ = [ 1, true ] :ne def iapp: (int -> int) -> int -> int let f = revapp id in (f poly, f iapp) //│ iapp: (int -> int) -> int -> int //│ res: ((1, true,), int -> int,) // ============ Sec 3 ============ // FreezeML C2 tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // nope single id : List[forall 'a. 'a -> 'a] //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } :ne def foo: ((forall 'a. 'a -> 'a) -> (int, bool)) -> int foo (fun f -> (f 1, f true)) //│ foo: ((forall 'a. 'a -> 'a) -> (int, bool,)) -> int //│ res: int choose nil ids choose ids nil //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } let f = choose nil in f ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // FreezeML D4 :ne app runST argST //│ res: int // ============ Sec 4 ============ // c.f. FreezeML A1 fun x -> fun y -> x //│ res: 'a -> anything -> 'a //│ = [Function: res] // FreezeML C4 single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } let foo x y = single y in foo ids id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id], tail: Nil {} } head ids 1 //│ res: 1 //│ = 1 // ============ Sec 5 ============ // FreezeML B1 def poly: (forall 'a. 'a -> 'a) -> (int, bool) def poly f = (f 1, f true) //│ poly: (forall 'a. 'a -> 'a) -> (int, bool,) //│ = //│ (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ <: poly: //│ (forall 'a. 'a -> 'a) -> (int, bool,) //│ = [Function: poly1] const x y = x //│ const: 'a -> anything -> 'a //│ = [Function: const1] // ============ Sec 6 ============ // FreezeML C5 xs = cons id ids //│ xs: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id], //│ tail: Cons { head: [Function: id], tail: Nil {} } //│ } ================================================ FILE: shared/src/test/diff/fcp-lit/HML.mls ================================================ :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail def cons[a]: a -> List[a] -> List[a] def cons head tail = Cons { head; tail } //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] class Nil[a]: List[a] method Head = error method Tail = this def nil: List['a] nil = Nil {} //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} // ============ Type signatures for functions used in the examples ============ def head: forall 'a. List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: forall 'a. List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] def is_empty: forall 'a. List['a] -> bool def is_empty l = eq l nil //│ is_empty: List[?] -> bool //│ = //│ anything -> bool //│ <: is_empty: //│ List[?] -> bool //│ = [Function: is_empty] def single: forall 'a. 'a -> List['a] def single x = cons x nil //│ single: 'a -> List['a] //│ = //│ 'a -> List['a] //│ <: single: //│ 'a -> List['a] //│ = [Function: single] def append: List['a] -> List['a] -> List['a] rec def append l1 l2 = if is_empty l1 then l2 else cons (head l1) (append (tail l1) l2) //│ append: List['a] -> List['a] -> List['a] //│ = //│ List['a] -> List['a] -> List['a] //│ <: append: //│ List['a] -> List['a] -> List['a] //│ = [Function: append] def length: forall 'a. List['a] -> int rec def length l = if is_empty l then 0 else succ (length (tail l)) //│ length: List[?] -> int //│ = //│ List[?] -> int //│ <: length: //│ List[?] -> int //│ = [Function: length1] def id: forall 'a. 'a -> 'a def id x = x //│ id: 'a -> 'a //│ = //│ 'a -> 'a //│ <: id: //│ 'a -> 'a //│ = [Function: id1] def inc: int -> int inc = succ //│ inc: int -> int //│ = //│ int -> int //│ <: inc: //│ int -> int //│ = [Function: succ] def choose: forall 'a. 'a -> 'a -> 'a def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a //│ = //│ 'a -> 'a -> 'a //│ <: choose: //│ 'a -> 'a -> 'a //│ = [Function: choose] def map: forall 'a 'b. ('a -> 'b) -> List['a] -> List['b] rec def map f l = if is_empty l then nil else cons (f (head l)) (map f (tail l)) //│ map: ('a -> 'b) -> List['a] -> List['b] //│ = //│ ('a -> 'b) -> List['a] -> List['b] //│ <: map: //│ ('a -> 'b) -> List['a] -> List['b] //│ = [Function: map] def app: forall 'a 'b. ('a -> 'b) -> 'a -> 'b def app f x = f x //│ app: ('a -> 'b) -> 'a -> 'b //│ = //│ ('a -> 'b) -> 'a -> 'b //│ <: app: //│ ('a -> 'b) -> 'a -> 'b //│ = [Function: app] def revapp: forall 'a 'b. 'a -> ('a -> 'b) -> 'b def revapp x f = f x //│ revapp: 'a -> ('a -> 'b) -> 'b //│ = //│ 'a -> ('a -> 'b) -> 'b //│ <: revapp: //│ 'a -> ('a -> 'b) -> 'b //│ = [Function: revapp] // ============ Sec 2 ============ // FreezeML B1 def poly (f: forall 'a. 'a -> 'a) = (f 1, f true) def poly_ f = (f 1, f true) //│ poly: (forall 'a. 'a -> 'a) -> (1, true,) //│ = [Function: poly] //│ poly_: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: poly_] // FreezeML A10 poly id poly_ id //│ res: (1, true,) //│ = [ 1, true ] //│ res: (1, true,) //│ = [ 1, true ] // FreezeML A11 poly (fun x -> x) poly_ (fun x -> x) //│ res: (1, true,) //│ = [ 1, true ] //│ res: (1, true,) //│ = [ 1, true ] // FreezeML D1 app poly id app poly_ id //│ res: (1, true,) //│ = [ 1, true ] //│ res: (1, true,) //│ = [ 1, true ] // FreezeML D2 revapp id poly revapp id poly_ //│ res: (1, true,) //│ = [ 1, true ] //│ res: (1, true,) //│ = [ 1, true ] // FreezeML C9 map poly (single id) map poly_ (single id) //│ res: List[(1, true,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } //│ res: List[(1, true,)] //│ = Cons { head: [ 1, true ], tail: Nil {} } // FreezeML C7 append (single inc) (single id) //│ res: List[int -> int] //│ = Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } let ids = single id in (map poly ids, append (single inc) ids) //│ res: (List[(1, true,)], List[int -> int],) //│ = [ //│ Cons { head: [ 1, true ], tail: Nil {} }, //│ Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } //│ ] // FreezeML A2 choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] fun (f: forall 'a. 'a -> 'a) -> poly f fun f -> poly f //│ res: (forall 'a. 'a -> 'a) -> (1, true,) //│ = [Function: res] //│ res: (forall 'a. 'a -> 'a) -> (1, true,) //│ = [Function: res] // ============ Sec 4 ============ // FreezeML C4 ids = single id //│ ids: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ============ Sec 7 ============ xs = cons id ids //│ xs: List[forall 'a. 'a -> 'a] //│ = Cons { //│ head: [Function: id1], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } ys = tail ids //│ ys: List[forall 'a. 'a -> 'a] //│ = Nil {} zs = map id (tail (cons id ids)) //│ zs: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } def foo: (int -> (forall 'a. 'a -> 'a)) -> int poly (fun x -> x) foo (fun y -> fun x -> x) //│ foo: (int -> (forall 'a. 'a -> 'a)) -> int //│ = //│ res: (1, true,) //│ = [ 1, true ] //│ res: int //│ = //│ foo is not implemented ================================================ FILE: shared/src/test/diff/fcp-lit/Jim.mls ================================================ :NoRecursiveTypes // TODO also try stuff from his intersection type systems // ============ A Polar Type System (System P) ============ // λf. f (λx. x) fun f -> f (fun x -> x) //│ res: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: res] // λy. y y fun y -> y y //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // Typeable in P; not in ML nor I2: // (λf. f (λx. x)) (λy. y y) (fun f -> f (fun x -> x)) (fun y -> y y) //│ res: 'a -> 'a //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/fcp-lit/Leijen.mls ================================================ :NoRecursiveTypes :GeneralizeArguments // * Needed in order to quantify non-function arguments to runST // From HML implementation // Daan Leijen. 2009. Flexible Types: Robust Type Inference for First-Class Polymorphism. In // Proceedings of the 36th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming // Languages (Savannah, GA, USA) (POPL ’09). Association for Computing Machinery, New York, // NY, USA, 66–77. https://doi.org/10.1145/1480881.1480891 // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Nil[a]: List[a] method Head = error method Tail = this //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] class ST[S, A] method Inv_S: S -> S method Cov_A: A //│ Defined class ST[=S, +A] //│ Declared ST.Inv_S: ST['S, ?] -> 'S -> 'S //│ Declared ST.Cov_A: ST['S, 'A] -> 'A class Ref[S, A] method Inv_S: S -> S method Cov_A: A //│ Defined class Ref[=S, +A] //│ Declared Ref.Inv_S: Ref['S, ?] -> 'S -> 'S //│ Declared Ref.Cov_A: Ref['S, 'A] -> 'A // ============ Type signatures for functions used in the examples ============ def head: List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] // Used to represent `[]` in the papers def nil: List['a] nil = Nil {} //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} def cons: 'a -> List['a] -> List['a] def cons head tail = Cons { head; tail } //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] def is_empty: forall 'a. List['a] -> bool def is_empty l = eq l nil //│ is_empty: List[?] -> bool //│ = //│ anything -> bool //│ <: is_empty: //│ List[?] -> bool //│ = [Function: is_empty] def single: 'a -> List['a] def single x = cons x nil //│ single: 'a -> List['a] //│ = //│ 'a -> List['a] //│ <: single: //│ 'a -> List['a] //│ = [Function: single] def append: List['a] -> List['a] -> List['a] rec def append l1 l2 = if is_empty l1 then l2 else cons (head l1) (append (tail l1) l2) //│ append: List['a] -> List['a] -> List['a] //│ = //│ List['a] -> (List['a] & 'b) -> (List['a] | 'b) //│ <: append: //│ List['a] -> List['a] -> List['a] //│ = [Function: append] def length: List['a] -> int rec def length l = if is_empty l then 0 else succ (length (tail l)) //│ length: List[?] -> int //│ = //│ List[?] -> int //│ <: length: //│ List[?] -> int //│ = [Function: length1] def id: 'a -> 'a def id x = x //│ id: 'a -> 'a //│ = //│ 'a -> 'a //│ <: id: //│ 'a -> 'a //│ = [Function: id1] def ids: List[forall 'a. 'a -> 'a] ids = single id //│ ids: List[forall 'a. 'a -> 'a] //│ = //│ List[forall 'a. 'a -> 'a] //│ <: ids: //│ List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } def inc: int -> int inc = succ //│ inc: int -> int //│ = //│ int -> int //│ <: inc: //│ int -> int //│ = [Function: succ] def choose: 'a -> 'a -> 'a def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a //│ = //│ 'a -> 'a -> 'a //│ <: choose: //│ 'a -> 'a -> 'a //│ = [Function: choose] def auto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) def auto x = x x //│ auto: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto: //│ (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: auto] def xauto : forall 'a. (forall 'b. 'b -> 'b) -> 'a -> 'a def xauto x = x x //│ xauto: (forall 'b. 'b -> 'b) -> 'a -> 'a //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: xauto: //│ (forall 'b. 'b -> 'b) -> 'a -> 'a //│ = [Function: xauto] def takeAuto: ((forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a)) -> (forall 'a. 'a -> 'a) def takeAuto auto = auto id //│ takeAuto: ((forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0)) -> (forall 'a1. 'a1 -> 'a1) //│ = //│ ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ <: takeAuto: //│ ((forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0)) -> (forall 'a1. 'a1 -> 'a1) //│ = [Function: takeAuto] def const: forall 'a 'b. 'a -> 'b -> 'a def const x y = x //│ const: 'a -> anything -> 'a //│ = //│ 'a -> anything -> 'a //│ <: const: //│ 'a -> anything -> 'a //│ = [Function: const1] def map: ('a -> 'b) -> List['a] -> List['b] rec def map f l = if is_empty l then nil else cons (f (head l)) (map f (tail l)) //│ map: ('a -> 'b) -> List['a] -> List['b] //│ = //│ ('a -> 'b) -> List['a] -> List['b] //│ <: map: //│ ('a -> 'b) -> List['a] -> List['b] //│ = [Function: map] def app: ('a -> 'b) -> 'a -> 'b def app f x = f x //│ app: ('a -> 'b) -> 'a -> 'b //│ = //│ ('a -> 'b) -> 'a -> 'b //│ <: app: //│ ('a -> 'b) -> 'a -> 'b //│ = [Function: app] def revapp: 'a -> ('a -> 'b) -> 'b def revapp x f = f x //│ revapp: 'a -> ('a -> 'b) -> 'b //│ = //│ 'a -> ('a -> 'b) -> 'b //│ <: revapp: //│ 'a -> ('a -> 'b) -> 'b //│ = [Function: revapp] def fst: forall 'a 'b. (('a, 'b),) -> 'a def fst ((x, _),) = x //│ fst: (('a, anything,),) -> 'a //│ = //│ (('a, anything,),) -> 'a //│ <: fst: //│ (('a, anything,),) -> 'a //│ = [Function: fst] def snd: forall 'a 'b. (('a, 'b),) -> 'b def snd ((_, x),) = x //│ snd: ((anything, 'b,),) -> 'b //│ = //│ ((anything, 'a,),) -> 'a //│ <: snd: //│ ((anything, 'b,),) -> 'b //│ = [Function: snd] :ng def runST: forall 'a. (forall 's. ST['s, 'a]) -> 'a //│ runST: (forall 's. ST['s, 'a]) -> 'a :ng def newRef: forall 'a 's. 'a -> ST['s, Ref['s, 'a]] //│ newRef: 'a -> ST['s, Ref['s, 'a]] :ng def returnST: forall 'a 's. 'a -> ST['s, 'a] //│ returnST: 'a -> ST['s, 'a] // ============ Hindley Milner ============ // -- standard Hindley-Milner tests // [("\\x -> x", ok "forall a. a -> a") fun x -> x //│ res: 'a -> 'a //│ = [Function: res] // ,("\\f x -> f x", ok "forall a b. (a -> b) -> a -> b") fun f -> fun x -> f x //│ res: ('a -> 'b) -> 'a -> 'b //│ = [Function: res] // ,("inc True", Wrong) :e inc true //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.277: inc true //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.277: inc true //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.146: def inc: int -> int //│ ╙── ^^^ //│ res: error | int //│ = 2 // ,("let i = \\x -> x in i i", ok "forall a. a -> a") let i = fun x -> x in i i //│ res: 'a -> 'a //│ = [Function (anonymous)] // ,("\\i -> i i", Wrong) -- infinite type fun i -> i i //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // ,("\\i -> (i 1, i True)", Wrong) -- polymorphic use of parameter fun i -> (i 1, i true) //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: res] // ,("single id", ok "forall (a >= forall b. b -> b). [a]") single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("choose (\\x y -> x) (\\x y -> y)", ok "forall a. a -> a -> a") choose (fun x -> fun y -> x) (fun x -> fun y -> y) //│ res: 'a -> 'a -> 'a //│ = [Function (anonymous)] // ,("choose id", ok "forall (a >= forall b. b -> b). a -> a") choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // ] // ============ Higher rank & impredicative ============ // -- impredicative application and higher rank arguments are fully supported // [("xauto",ok "forall a. (forall a. a -> a) -> a -> a") -- just to show the types of xauto and auto xauto //│ res: (forall 'b. 'b -> 'b) -> 'a -> 'a //│ = [Function: xauto] // ,("auto", ok "(forall a. a -> a) -> (forall a. a -> a)") auto //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: auto] // ,("\\(i :: forall a. a -> a) -> i i", ok "forall (a >= forall b. b -> b). (forall b. b -> b) -> a") -- ok "forall a. (forall a. a -> a) -> a -> a") fun (i: forall 'a. 'a -> 'a) -> i i fun i -> i i //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: res] //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // ,("auto id", ok "forall a. a -> a") auto id //│ res: 'a -> 'a //│ = [Function: id1] // ,("apply auto id", ok "forall a. a -> a") app auto id //│ res: 'a -> 'a //│ = [Function: id1] // ,("(single :: (forall a. a -> a) -> [forall a. a->a]) id", ok "[forall a. a-> a]") (single : (forall 'a. 'a -> 'a) -> List[forall 'a. 'a -> 'a]) id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("runST (returnST 1)", ok "Int") :ng runST (returnST 1) //│ res: 1 // ,("runST (newRef 1)", Wrong) :ng runST (newRef 1) //│ res: Ref[in ??s, 1] // ,("apply runST (returnST 1)", ok "Int") :ng app runST (returnST 1) //│ res: 1 // ,("map xauto ids", ok "forall a. [a -> a]") map xauto ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("map xauto (map xauto ids)", Wrong) // * This term is fine, though they mark it as wrong in their system (because they don't have distrib. nor subtyping) map xauto (map xauto ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("map auto ids", ok "[forall a. a -> a]") map auto ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("map auto (map auto ids)", ok "[forall a. a -> a]") map auto (map auto ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("head ids", ok "forall a. a -> a") head ids //│ res: 'a -> 'a //│ = [Function: id1] // ,("tail ids", ok "[forall a. a -> a]") tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // ,("apply tail ids", ok "[forall a. a -> a]") app tail ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // ,("map head (single ids)", ok "[forall a. a -> a]") map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("apply (map head) (single ids)", ok "[forall a. a -> a]") app (map head) (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // -- check infinite poly types :e // ,("(undefined :: some a. [a -> a] -> Int) (undefined :: some c. [(forall d. d -> c) -> c])", Wrong) :ng (error : List['a -> 'a] -> int) (error : List[(forall 'd. 'd -> 'c) -> 'c]) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: ? -> 'a //│ ╙── //│ res: int // ,("(undefined :: some a. [a -> a] -> Int) (undefined :: [(forall d. d -> d) -> (Int -> Int)])", Wrong) :e :ng (error : List['a -> 'a] -> int) (error : List[(forall 'd. 'd -> 'd) -> (int -> int)]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.435: (error : List['a -> 'a] -> int) (error : List[(forall 'd. 'd -> 'd) -> (int -> int)]) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'d` is not an instance of type `int` //│ ║ l.435: (error : List['a -> 'a] -> int) (error : List[(forall 'd. 'd -> 'd) -> (int -> int)]) //│ ║ ^^ //│ ╟── but it flows into quantified type variable with expected type `int` //│ ║ l.435: (error : List['a -> 'a] -> int) (error : List[(forall 'd. 'd -> 'd) -> (int -> int)]) //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.435: (error : List['a -> 'a] -> int) (error : List[(forall 'd. 'd -> 'd) -> (int -> int)]) //│ ╙── ^^^ //│ res: error | int // ,("(undefined :: some a. [a -> (forall b. b -> b)] -> Int) (undefined :: some c. [(forall d. d -> d) -> c])", ok "Int") :ng (error : List['a -> (forall 'b. 'b -> 'b)] -> int) (error : List[(forall 'd. 'd -> 'd) -> 'c]) //│ res: int // -- these fail horribly in ghc: (choose auto id) is rejected while ((choose auto) id) is accepted -- so much for parenthesis :-) // ,("choose id auto", ok "(forall a. a -> a) -> (forall a. a -> a)") choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('a1 -> 'a1 | 'a0) //│ = [Function: id1] // ,("choose auto id", ok "(forall a. a -> a) -> (forall a. a -> a)") choose auto id //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('a1 -> 'a1 | 'a0) //│ = [Function: auto] // ,("choose xauto xauto", ok "forall a. (forall b. b -> b) -> a -> a") choose xauto xauto //│ res: (forall 'b. 'b -> 'b) -> 'a -> 'a //│ = [Function: xauto] // ,("choose id xauto", Wrong) choose id xauto //│ res: (forall 'b. 'b -> 'b & 'a) -> ('a0 -> 'a0 | 'a) //│ = [Function: id1] // ,("choose xauto id", Wrong) choose xauto id //│ res: (forall 'b. 'b -> 'b & 'a) -> ('a0 -> 'a0 | 'a) //│ = [Function: xauto] // -- these fail too in ghc: (choose ids []) is accepted while (choose [] ids) is rejected // ,("choose [] ids", ok "[forall a. a -> a]") choose nil ids //│ res: List[forall 'a. 'a -> 'a] //│ = Nil {} // ,("choose ids []", ok "[forall a. a -> a]") choose ids nil //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // -- check escaping skolems // ,("\\x -> auto x", Wrong) -- escape in match fun x -> auto x //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: res] // ,("let poly (xs :: [forall a. a -> a]) = 1 in (\\x -> poly x)", Wrong) -- escape in apply let poly (xs: List[forall 'a. 'a -> 'a]) = 1 in fun x -> poly x //│ res: List[forall 'a. 'a -> 'a] -> 1 //│ = [Function (anonymous)] // ,("\\x -> (x :: [forall a. a -> a])", Wrong) -- escape in apply fun x -> (x : List[forall 'a. 'a -> 'a]) //│ res: List[forall 'a. 'a -> 'a] -> List[forall 'a. 'a -> 'a] //│ = [Function: res] // ,("\\x -> let polys (xs :: [forall a . a -> a]) = 1; f y = x in polys [f::some a. forall b. b -> a]",Wrong) -- escape in unify (with rigid annotations, otherwise we get a skolem mismatch) fun x -> let polys (xs: List[forall 'a. 'a -> 'a]) = 1 in let f y = x in polys (single (f : forall 'b. 'b -> 'a)) //│ res: ??a -> 1 //│ = [Function: res] // ,("ids :: forall b. [forall a. a -> b]", Wrong) -- unify different skolems :e ids : forall 'b. List[forall 'a. 'a -> 'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.518: ids : forall 'b. List[forall 'a. 'a -> 'b] //│ ║ ^^^ //│ ╟── type `'a` does not match type `'b` //│ ║ l.518: ids : forall 'b. List[forall 'a. 'a -> 'b] //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.518: ids : forall 'b. List[forall 'a. 'a -> 'b] //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.518: ids : forall 'b. List[forall 'a. 'a -> 'b] //│ ╙── ^^ //│ res: List[anything -> nothing] //│ = Cons { head: [Function: id1], tail: Nil {} } // -- co/contra variance // ,("let g (x::(forall a. a -> a) -> Int) = x id; f (x :: Int -> Int) = x 1 in g f", Wrong) -- HMF is invariant let g (x: (forall 'a. 'a -> 'a) -> int) = x id in let f (x: int -> int) = x 1 in g f //│ res: int //│ = 1 // ,("let g (x::(forall a. a -> a) -> Int) = x id; f (x :: Int -> Int) = x 1 in g (\\(x :: forall a. a -> a) -> f x)", ok "Int") -- but we can always use explicit annotations to type such examples let g (x: (forall 'a. 'a -> 'a) -> int) = x id in let f (x: int -> int) = x 1 in g (fun (x: forall 'a. 'a -> 'a) -> f x) //│ res: int //│ = 1 // -- shared polymorphism // ,("let f (x :: [forall a.a -> a]) = x in let g (x :: [Int -> Int]) = x in let ids = [id] in (f ids, g ids)", ok "([forall a. a -> a],[Int -> Int])") let f (x: List[forall 'a. 'a -> 'a]) = x in let g (x: List[int -> int]) = x in let ids = single id in (f ids, g ids) //│ res: (List[forall 'a. 'a -> 'a], List[int -> int],) //│ = [ //│ Cons { head: [Function: id1], tail: Nil {} }, //│ Cons { head: [Function: id1], tail: Nil {} } //│ ] // ] // ============ Encoding of existentials ============ // -- simulating existential types // ("let pack x f = \\(open :: some b. forall a. (a,a -> Int) -> b) -> open (x,f); \ // \unpack ex f = ex f; \ // \existsB = pack True (\\b -> if b then 1 else 0); \ // \existsI = pack 1 id; \ // \exs = [existsB,existsI]\ // \in unpack (head exs) (\\ex -> (snd ex) (fst ex))" // ,ok "Int") let pack x f = fun (open: forall 'a. (('a, 'a -> int),) -> 'b) -> open ((x, f)) in let unpack ex f = ex f in let existsB = pack true (fun b -> if b then 1 else 0) in let existsI = pack 1 id in let exs = cons existsB (cons existsI nil) in unpack (head exs) (fun ex -> (snd ex) (fst ex)) //│ res: int //│ = 1 // ============ Rigid annotations ============ // -- test 'rigid' annotations, i.e. annotations are taken literally and do not instantiate or generalize // [("single (id :: forall a. a -> a)", ok "forall (a >= forall b. b -> b). [a]") single (id : forall 'a. 'a -> 'a) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("(id :: forall a. a -> a) 1", ok "Int") -- not all annotations are rigid (id : forall 'a. 'a -> 'a) 1 //│ res: 1 //│ = 1 // ,("(id :: some a. a -> a) 1", ok "Int") (id : 'a -> 'a) 1 //│ res: 1 //│ = 1 // ,("\\x -> ((\\y -> x) :: some a. forall b. b -> a)", ok "forall a. forall (b >= forall c. c -> a). a -> b") fun x -> (fun y -> x) : forall 'b. 'b -> 'a //│ res: 'a -> anything -> 'a //│ = [Function: res] // ,("\\(f :: forall a. a -> a) -> ((f f) :: forall a. a -> a)", ok "forall (a >= forall b. b -> b). (forall b. b -> b) -> a") fun (f: forall 'a. 'a -> 'a) -> ((f f) : forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: res] // ,("revapp (id :: forall a. a -> a) auto", ok "forall a. a -> a") revapp (id : forall 'a. 'a -> 'a) auto //│ res: 'a -> 'a //│ = [Function: id1] // ,("choose inc id", ok "Int -> Int") choose inc id //│ res: int -> int //│ = [Function: succ] // ,("choose inc (id :: forall a. a -> a)", if SupportRigidAnnotations `elem` features then Wrong else ok "Int -> Int") choose inc (id : forall 'a. 'a -> 'a) //│ res: int -> int //│ = [Function: succ] // ,("choose inc (id :: some a. a -> a)", ok "Int -> Int") choose inc (id : 'a -> 'a) //│ res: int -> int //│ = [Function: succ] // ] // ============ N-ary applications, order of arguments ============ // -- tests n-ary applications // [("revapp id auto", ok "forall a. a -> a") revapp id auto //│ res: 'a -> 'a //│ = [Function: id1] // ,("let f = revapp id in f auto", ok "forall a. a -> a") let f = revapp id in f auto //│ res: 'a -> 'a //│ = [Function: id1] // ,("let f = revapp (id :: forall a. a -> a) in f auto", ok "forall a. a -> a") let f = revapp (id : forall 'a. 'a -> 'a) in f auto //│ res: 'a -> 'a //│ = [Function: id1] // -- check functions that return polymorphic values // ,("head ids 1", ok "Int") head ids 1 //│ res: 1 //│ = 1 // ,("auto id 1", ok "Int") auto id 1 //│ res: 1 //│ = 1 // ] // ============ Flexible bounds ============ // -- test sharing of polymorphic types // [("let ids = single id in (map auto ids, append (single inc) ids)", ok "([forall a. a -> a],[Int -> Int])") let ids = single id in (map auto ids, append (single inc) ids) //│ res: (List[forall 'a. 'a -> 'a], List[int -> int],) //│ = [ //│ Cons { head: [Function: id1], tail: Nil {} }, //│ Cons { //│ head: [Function: succ], //│ tail: Cons { head: [Function: id1], tail: Nil {} } //│ } //│ ] // ,("single id",ok "forall (a >= forall b. b -> b). [a]") single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("choose id",ok "forall (a >= forall b. b -> b). a -> a") choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // ,("choose id inc", ok "Int -> Int") choose id inc //│ res: int -> int //│ = [Function: id1] // ,("choose id auto", ok "(forall a. a -> a) -> (forall a. a -> a)") choose id auto //│ res: (forall 'a. 'a -> 'a & 'a0) -> ('a1 -> 'a1 | 'a0) //│ = [Function: id1] // ,("\\x y -> x", ok "forall a. forall (b >= forall c. c -> a). a -> b") fun x -> fun y -> x //│ res: 'a -> anything -> 'a //│ = [Function: res] // ] // ============ Experimental "rigid" keyword ============ // -- Experimental: the "rigid" keyword prevents instantiation or generalization of the principal type of an expression // -- this is perhaps more convenient than writing an annotation (but not more expressive) // ("single (rigid id)", ok "[forall a. a -> a]") single id //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("let pauto (f :: forall a. a -> a) = rigid f in map pauto ids", ok "[forall a. a -> a]") let pauto (f: forall 'a. 'a -> 'a) = f in map pauto ids //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("let pauto (f :: forall a. a -> a) = rigid f in map pauto (map pauto ids)", ok "[forall a. a -> a]") let pauto (f: forall 'a. 'a -> 'a) = f in map pauto (map pauto ids) //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("\\x -> rigid (\\y -> x)", ok "forall a. a -> (forall b. b -> a)") f = fun x -> fun y -> x //│ f: 'a -> anything -> 'a //│ = [Function: f] // ,("\\x -> rigid (\\y -> const x y)", ok "forall a. a -> (forall b. b -> a)") fun x -> fun y -> const x y //│ res: 'a -> anything -> 'a //│ = [Function: res] // ,("let c x = rigid (\\y -> x) in \\x y -> c x y", ok "forall a b. a -> b -> a") let c x = fun y -> x in fun x -> fun y -> c x y //│ res: 'a -> anything -> 'a //│ = [Function (anonymous)] // ,("choose plus (\\x -> id)", ok "Int -> Int -> Int") choose add (fun x -> id) //│ res: int -> int -> int //│ = [Function: add] // ,("choose plus (\\x -> rigid id)", Wrong) choose add (fun x -> id) //│ res: int -> int -> int //│ = [Function: add] // ,("choose inc (rigid id)", Wrong) choose inc id //│ res: int -> int //│ = [Function: succ] // ,("choose id", ok "forall a. (a -> a) -> (a -> a)") choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // ,("choose (rigid id)", ok "(forall a. a -> a) -> (forall a. a -> a)") choose id //│ res: 'a -> (forall 'a0. 'a0 -> 'a0 | 'a) //│ = [Function (anonymous)] // ,("revapp (rigid id) auto", ok "forall a. a -> a") revapp id auto //│ res: 'a -> 'a //│ = [Function: id1] // -- manipulate instantiation of each quantifier: // ,("[const]", ok "forall a b. [a -> b -> a]") single const //│ res: List[forall 'a. 'a -> anything -> 'a] //│ = Cons { head: [Function: const1], tail: Nil {} } // ,("[rigid const]", ok "[forall a b. a -> b -> a]") single const //│ res: List[forall 'a. 'a -> anything -> 'a] //│ = Cons { head: [Function: const1], tail: Nil {} } // ,("[const :: some a. forall b. a -> b -> a]", ok "forall a. [forall b. a -> b -> a]") single (const : forall 'b. 'a -> 'b -> 'a) //│ res: List[forall 'a. 'a -> anything -> 'a] //│ = Cons { head: [Function: const1], tail: Nil {} } // ,("[const :: some b. forall a. a -> b -> a]", ok "forall b. [forall a. a -> b -> a]") single (const : forall 'a. 'a -> 'b -> 'a) //│ res: List[forall 'a. 'a -> anything -> 'a] //│ = Cons { head: [Function: const1], tail: Nil {} } // ] // ============ Type propagation ============ // -- test type propagation (SupportPropagation `elem` features) // ("(\\f -> f f) :: (forall a. a -> a) -> (forall a. a -> a)", ok "(forall a. a -> a) -> (forall a. a -> a)") (fun f -> f f) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: res] // ,("(let x = 1 in (\\f -> (f x, f True))) :: (forall a. a -> a) -> (Int,Bool)", ok "(forall a. a -> a) -> (Int,Bool)") (let x = 1 in fun f -> (f x, f true)) : (forall 'a. 'a -> 'a) -> (int, bool) //│ res: (forall 'a. 'a -> 'a) -> (int, bool,) //│ = [Function (anonymous)] // -- test type propagation through applications (SupportAppPropagation `elem` features) // ("single id :: [forall a. a -> a]", ok "[forall a. a -> a]") single id : List[forall 'a. 'a -> 'a] //│ res: List[forall 'a. 'a -> 'a] //│ = Cons { head: [Function: id1], tail: Nil {} } // ,("returnST 1 :: forall s. ST s Int", ok "forall s. ST s Int") // * Note: requires :DistributeForalls OR ascription-LHS generalization :ng returnST 1 : forall 's. ST['s, int] //│ res: ST['s, int] // ,("auto id :: Int -> Int", ok "Int -> Int") auto id : int -> int //│ res: int -> int //│ = [Function: id1] // ,("head ids 1 :: Int", ok "Int") head ids 1 : int //│ res: int //│ = 1 // ,("head ids :: Int -> Int", ok "Int -> Int") head ids : int -> int //│ res: int -> int //│ = [Function: id1] // ============ Type propagation to arguments ============ // -- test type propagation to arguments (SupportPropagateToArg `elem` features) // ("takeAuto (\\f -> f f)", ok "forall a. a -> a") takeAuto (fun f -> f f) //│ res: 'a -> 'a //│ = [Function: id1] // ,("[id]: [ids]", ok "[[forall a. a -> a]]") cons (single id) (single ids) //│ res: List[List[forall 'a. 'a -> 'a]] //│ = Cons { //│ head: Cons { head: [Function: id1], tail: Nil {} }, //│ tail: Cons { //│ head: Cons { head: [Function: id1], tail: Nil {} }, //│ tail: Nil {} //│ } //│ } // ,("([id] :: [forall a. a -> a]) : [[\\x -> x]]", ok "[[forall a. a -> a]]") cons (single id : List[forall 'a. 'a -> 'a]) (single (single (fun x -> x))) //│ res: List[List[forall 'a. 'a -> 'a]] //│ = Cons { //│ head: Cons { head: [Function: id1], tail: Nil {} }, //│ tail: Cons { //│ head: Cons { head: [Function (anonymous)], tail: Nil {} }, //│ tail: Nil {} //│ } //│ } // ,("apply takeAuto (\\f -> f f)", ok "forall a. a -> a") app takeAuto (fun f -> f f) //│ res: 'a -> 'a //│ = [Function: id1] // ,("revapp (\\f -> f f) takeAuto", ok "forall a. a -> a") revapp (fun f -> f f) takeAuto //│ res: 'a -> 'a //│ = [Function: id1] // ,("apply (\\f -> choose auto f) (auto :: (forall a. a -> a) -> (forall a. a -> a))", ok "(forall a. a -> a) -> (forall a. a -> a)") app (fun f -> choose auto f) (auto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a)) //│ res: (forall 'a 'a0. 'a -> 'a & 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1) //│ = [Function: auto] // ,("revapp (auto :: (forall a. a -> a) -> (forall a. a -> a)) (\\f -> choose auto f)", ok "(forall a. a -> a) -> (forall a. a -> a)") revapp (auto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a)) (fun f -> choose auto f) //│ res: (forall 'a 'a0. 'a -> 'a & 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1) //│ = [Function: auto] // ============ Eta expansion of polymorphic types ============ // -- this is *not* supported by HML: inference of polymorphic types for arguments that are just passed around.. // -- in MLF arguments can have an inferred polymorphic type as long as it is not used (or revealed explicitly) // [("\\x -> auto x", ok "forall a. (forall a. a -> a) -> a -> a") fun x -> auto x //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: res] // ,("\\x -> (auto x, x 1)", Wrong) fun x -> (auto x, x 1) //│ res: (forall 'a. 'a -> 'a & 1 -> 'b) -> (forall 'a0. 'a0 -> 'a0, 'b,) //│ = [Function: res] // ,("\\x -> (auto x, (x :: forall a. a -> a) 1)", ok "forall a. (forall a. a -> a) -> (a -> a, Int)") fun x -> (auto x, (x : forall 'a. 'a -> 'a) 1) //│ res: (forall 'a 'a0. 'a -> 'a & 'a0 -> 'a0) -> (forall 'a1. 'a1 -> 'a1, 1,) //│ = [Function: res] // ,("\\x -> (auto x, (x :: Int -> Int) 1)", Wrong) fun x -> (auto x, (x : int -> int) 1) //│ res: (forall 'a. 'a -> 'a & int -> int) -> (forall 'a0. 'a0 -> 'a0, int,) //│ = [Function: res] // ] // ("(\\(x :: forall a. a -> a) -> xauto x) :: (forall a. a -> a) -> (forall a. a -> a)", ok "(forall a. a -> a) -> (forall a. a -> a)") (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: res] // ("xauto :: (forall a. a -> a) -> (forall a. a -> a)"", Wrong) xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ = [Function: xauto] // ============ ============ ============ Our own tests ============ ============ ============ :NoJS def runST2: forall 'a. (forall 's. (ST['s, 'a], ST['s, 'a])) -> 'a runST2 ((newRef 1, newRef 2)) //│ runST2: (forall 's. (ST['s, 'a], ST['s, 'a],)) -> 'a //│ res: Ref[in ??s, 1 | 2] def runST3: forall 'a. (forall 's. ST[('s, 's), 'a]) -> 'a def newRef3: forall 'a 's. 'a -> ST[('s, 's), Ref['s, 'a]] runST3 (newRef3 1) //│ runST3: (forall 's. ST[('s, 's,), 'a]) -> 'a //│ newRef3: 'a -> ST[('s, 's,), Ref['s, 'a]] //│ res: Ref[in ??s, 1] def runST4: forall 'a 'b. (forall 's. ST['s, ('a, 'b)]) -> ('a, 'b) def newRef4: forall 'a 's. 'a -> ST['s, (Ref['s, 'a], Ref['s, 'a])] runST4 (newRef4 1) //│ runST4: (forall 's. ST['s, ('a, 'b,)]) -> ('a, 'b,) //│ newRef4: 'a -> ST['s, (Ref['s, 'a], Ref['s, 'a],)] //│ res: (Ref[in ??s, 1], Ref[in ??s, 1],) // * Distributivity demonstration: def test1: List['a -> 'a] def test2: List[forall 'a. 'a -> 'a] //│ test1: List['a -> 'a] //│ test2: List[forall 'a. 'a -> 'a] test1 = test2 //│ List[forall 'a. 'a -> 'a] //│ <: test1: //│ List['a -> 'a] :e test2 = test1 //│ List['a -> 'a] //│ <: test2: //│ List[forall 'a. 'a -> 'a] //│ ╔══[ERROR] Type error in def definition //│ ║ l.965: test2 = test1 //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.955: def test2: List[forall 'a. 'a -> 'a] //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.955: def test2: List[forall 'a. 'a -> 'a] //│ ╙── ^^ // ———————————— Some tests without distribtivity, for reference ———————————— // * Here are all the cases that fail without :DistributeForalls :DontDistributeForalls // ,("map xauto ids", ok "forall a. [a -> a]") // * Note the less precise type we now infer: map xauto ids //│ res: List['a -> 'a] // ,("map xauto (map xauto ids)", Wrong) :e map xauto (map xauto ids) //│ ╔══[ERROR] Type error in application //│ ║ l.996: map xauto (map xauto ids) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.173: def xauto : forall 'a. (forall 'b. 'b -> 'b) -> 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.173: def xauto : forall 'a. (forall 'b. 'b -> 'b) -> 'a -> 'a //│ ╙── ^^ //│ res: List['a -> 'a] | error // * This doesn't work (without distrib.) because we don't currently generalize non-functions: :e (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╔══[ERROR] Type error in type ascription //│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.1011: (fun x -> xauto x) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╙── ^^^^^^^ //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) // * But we can trivially fix it by forcing generalization through a polymorphic let binding: (fun x -> let poly = xauto x in poly) : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) :e xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╔══[ERROR] Type error in type ascription //│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.1031: xauto : (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ ╙── ^^^^^ //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) ================================================ FILE: shared/src/test/diff/fcp-lit/MLF.mls ================================================ :NoRecursiveTypes :NoJS // ------------ Dummy classes to represent the types in the examples ------------ class List[a] method Get: a //│ Defined class List[+a] //│ Declared List.Get: List['a] -> 'a class ST[S, A] method Inv_S: S -> S method Cov_A: A //│ Defined class ST[=S, +A] //│ Declared ST.Inv_S: ST['S, ?] -> 'S -> 'S //│ Declared ST.Cov_A: ST['S, 'A] -> 'A // ============ Type signatures for functions used in the examples ============ def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a def id x = x //│ id: 'a -> 'a def auto (x: forall 'a. 'a -> 'a) = x x //│ auto: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 def app f x = f x //│ app: ('a -> 'b) -> 'a -> 'b def inc: int -> int //│ inc: int -> int // Used to represent `::` in the papers def cons: 'a -> List['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] // Used to represent `[]` in the papers def nil: List['a] //│ nil: List[nothing] def single: 'a -> List['a] //│ single: 'a -> List['a] def head: List['a] -> 'a //│ head: List['a] -> 'a def tail: List['a] -> List['a] //│ tail: List['a] -> List['a] def append: List['a] -> List['a] -> List['a] //│ append: List['a] -> List['a] -> List['a] def runST: (forall 's. ST['s, 'v]) -> 'v //│ runST: (forall 's. ST['s, 'v]) -> 'v def argST: ST['s, int] //│ argST: ST['s, int] // ============ Raising ML to the power of System F (2003) ============ // FreezeML A2 choose id //│ res: 'a -> ('b -> 'b | 'a) fun (g: forall 'a. ('a -> 'a) -> ('a -> 'a)) -> fun (x: forall 'a. 'a -> 'a) -> fun a -> g a (x a) //│ res: (forall 'a. ('a -> 'a) -> 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) -> ('a1 -> 'a1 & 'a1) -> 'a1 choose id succ //│ res: int -> int // FreezeML A7 choose id auto //│ res: (forall 'a. 'a -> 'a & 'b) -> ('a0 -> 'a0 | 'b) // Not typeable in MLF, along with anything with it as a subterm // i.e. unannotated auto omega = fun x -> x x //│ omega: ('a -> 'b & 'a) -> 'b fun (x: forall 'a. 'a) -> x x //│ res: nothing -> nothing // i.e. auto omegad = fun (x: forall 'a. 'a -> 'a) -> x x //│ omegad: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 // FreezeML A5 id auto //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 // ~ auto id (FreezeML F5, PolyML 1.1) (fun x -> x id) auto //│ res: 'a -> 'a app auto id //│ res: 'a -> 'a // ------------ Sec 5.2 ------------ // * Mistake in the paper (confirmed by Rémy via email). :e let f = choose id in (f auto) (f succ) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.107: let f = choose id in (f auto) (f succ) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.28: def auto (x: forall 'a. 'a -> 'a) = x x //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.28: def auto (x: forall 'a. 'a -> 'a) = x x //│ ╙── ^^ //│ res: error | int -> int // * Reproduced with an unnanotated auto: def auto2 x = x x //│ auto2: ('a -> 'b & 'a) -> 'b choose id auto2 //│ res: ('a -> 'b & 'a) -> ('b | 'a) :e // * This is a legit error res (choose id succ) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.128: res (choose id succ) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int` is not an instance of type `int` //│ ║ l.128: res (choose id succ) //│ ║ ^^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.128: res (choose id succ) //│ ╙── ^^^^^^^^^^^^^^ //│ res: error | int | int -> int // * Didier Le Botlan suggested this fix by email: let f = choose id in (f auto, f succ) //│ res: (forall 'b 'a. (forall 'a0. 'a0 -> 'a0 & 'b) -> ('a -> 'a | 'b), forall 'b. (int & 'b) -> (int | 'b),) let f = choose id in (f auto2, f succ) //│ res: (forall 'a 'b 'c. ('a -> 'b & 'a & 'c) -> ('b | 'c), forall 'c. (int & 'c) -> (int | 'c),) // ------------ Sec 6 ------------ // (λ(y) y I ; y K) (λ(x) x x) // "typable in Fω but not in F [9]" // [9] P. Giannini and S. R. D. Rocca. Characterization of typings in polymorphic type discipline. In Third annual Symposium on Logic in Computer Science, pages 61–70. IEEE, 1988. // I := λx.x I x = x //│ I: 'a -> 'a // K := λx.λy.x K x y = x //│ K: 'a -> anything -> 'a (fun y -> let tmp = y I in y K) (fun x -> x x) //│ res: anything -> 'a -> anything -> 'a // * Note: reduces to let tmp = (fun x -> x x) I in (fun x -> x x) K //│ res: anything -> 'a -> anything -> 'a // to let tmp = I I in K K //│ res: anything -> 'a -> anything -> 'a // ============ Recasting MLF (2009) ============ // ------------ Sec 1.3 ------------ // ~ FreezeML B1 (fun f -> (f 42, f "foo")) (fun x -> x) //│ res: (42, "foo",) (fun f -> (f succ, choose f auto)) (choose id) //│ res: (int -> int | 'b, (forall 'a. 'a -> 'a & 'b) -> (int -> int | 'b),) // ------------ Sec 2.3.1 ------------ // * i.e. id auto (FreezeML A5) (fun z -> z) omegad (fun z -> z) omega //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ res: ('a -> 'b & 'a) -> 'b // * i.e. auto id (FreezeML F5) (fun x -> x x) id //│ res: 'a -> 'a fun z -> (z omegad) fun z -> (z omega) //│ res: ((forall 'a. (forall 'a0. 'a0 -> 'a0) -> 'a -> 'a) -> 'b) -> 'b //│ res: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> 'c) -> 'c (fun y -> fun z -> z y) omegad (fun y -> fun z -> z y) omega //│ res: ((forall 'a. (forall 'a0. 'a0 -> 'a0) -> 'a -> 'a) -> 'b) -> 'b //│ res: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> 'c) -> 'c fun z -> omegad z fun z -> omega z //│ res: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ res: ('a -> 'b & 'a) -> 'b (fun x -> fun y -> x y) omegad (fun x -> fun y -> x y) omega //│ res: (forall 'a. 'a -> 'a) -> (forall 'a0. 'a0 -> 'a0) //│ res: ('a -> 'b & 'a) -> 'b // ------------ Sec 4.3 ------------ // affects the order of bindings in the result type in MLF, not very interesting otherwise fun (x: forall 'a. 'a -> 'b -> 'a) -> x //│ res: (forall 'a. 'a -> 'b -> 'a) -> 'a0 -> 'b -> 'a0 fun (x: forall 'b 'a. 'a -> 'b -> 'a) -> x //│ res: (forall 'a. 'a -> anything -> 'a) -> (forall 'a. 'a -> anything -> 'a) // ============ Leijen 2007 ============ // ------------ Sec 2 ------------ // * FreezeML B1 // Not typeable in MLF poly = fun f -> (f 1, f true) //│ poly: (1 -> 'a & true -> 'b) -> ('a, 'b,) // * FreezeML B2 polyL = fun xs -> poly (head xs) //│ polyL: List[1 -> 'a & true -> 'b] -> ('a, 'b,) let ids = single id in (polyL ids, append ids (single inc)) //│ res: ((1, true,), List[int -> int],) // ------------ Sec 5 ------------ // * FreezeML C5 ids = single id //│ ids: List[forall 'a. 'a -> 'a] // * Let-bound version of FreezeML A3 let f = choose nil in f ids //│ res: List[forall 'a. 'a -> 'a] // * FreezeML A3 choose nil ids //│ res: List[forall 'a. 'a -> 'a] def g: (int -> (forall 'a. 'a -> 'a)) -> int //│ g: (int -> (forall 'a. 'a -> 'a)) -> int g (fun x -> id) //│ res: int let f = fun x -> id in g f //│ res: int // * FreezeML D4 app runST argST //│ res: int // ============ Variations ported from old Supertype prototype tests ============ killer_app = (fun x -> x id) auto //│ killer_app: 'a -> 'a I x = x K x y = x //│ I: 'a -> 'a //│ K: 'a -> anything -> 'a // """ In particular, we conjecture that the term ... monster = (fun y -> (let tmp = y I in y K)) (fun x -> x x) // """ ... that is typable in Fω but not in F [9] is not typable in MLF either. //│ monster: anything -> 'a -> anything -> 'a ignore x y = y //│ ignore: anything -> 'a -> 'a monsterThunk() = (fun y -> (ignore (y I) (y K))) (fun x -> x x) //│ monsterThunk: () -> anything -> 'a -> anything -> 'a monster2 = (fun y -> (y I, y K)) (fun x -> x x) //│ monster2: (forall 'a. 'a -> 'a, forall 'b. anything -> 'b -> anything -> 'b,) monster = (fun y -> (let tmp = y I in y K)) (fun x -> x x) //│ monster: anything -> 'a -> anything -> 'a monsterThunk() = (fun y -> ignore (y I) (y K)) (fun x -> x x) //│ monsterThunk: () -> anything -> 'a -> anything -> 'a monster2 = (fun y -> (y I, y K)) (fun x -> x x) //│ monster2: (forall 'a. 'a -> 'a, forall 'b. anything -> 'b -> anything -> 'b,) K K //│ res: anything -> 'a -> anything -> 'a // ============ A new look on MLF (2008 slides) ============ // def pack : forall α. forall β. (α, (α -> β)) -> (forall γ. (forall δ. (δ, (δ -> β)) -> γ) -> γ) type Pair[A,B] = (A,B) def pack : forall α. forall β. (α, (α -> β)) -> (forall γ. (forall δ. Pair[δ, (δ -> β)] -> γ) -> γ) //│ Defined type alias Pair[+A, +B] //│ pack: ('α, 'α -> 'β,) -> (forall 'δ. Pair['δ, 'δ -> 'β] -> 'γ) -> 'γ def packed_int = pack (1, fun x -> x + 1) //│ packed_int: (forall 'δ. Pair['δ, 'δ -> int] -> 'γ) -> 'γ def packed_pair = pack (1, fun x -> (x, x )) //│ packed_pair: (forall 'δ. Pair['δ, 'δ -> (1, 1,)] -> 'γ) -> 'γ def fst((x,y)) = x def snd((x,y)) = y //│ fst: (('a, anything,),) -> 'a //│ snd: ((anything, 'a,),) -> 'a v = packed_int (fun p -> (snd p) ( fst p )) //│ v: int def a: ' -> ' //│ a: 'a -> 'a ================================================ FILE: shared/src/test/diff/fcp-lit/Misc.mls ================================================ :NoRecursiveTypes :NoConstrainedTypes :DontDistributeForalls // * Pfenning 88 (fun f -> (f 1, f true))(fun x -> x) //│ res: (1, true,) //│ = [ 1, true ] // * FML: Simple, partial type-inference for System F based on type-containment // * https://dl.acm.org/doi/abs/10.1145/1090189.1086383 // we may exhibit terms than are typable in F but not in the predicative fragment F(≤^F_p) // One such term (λz. z y z) (λz. z y z) is due to Pawel Urzyczyn, according to Leivant [Lei91]. pawel y = (fun z -> z y z) (fun z -> z y z) //│ pawel: ('a -> 'a -> (forall 'b 'c. ('a -> 'b -> 'c & 'b) -> 'c) -> 'd & 'a) -> 'd //│ = [Function: pawel] def f = (fun z -> z z : (forall 'X. 'X -> 'X) -> (forall 'X. 'X -> 'X)) //│ f: ('a -> (forall 'X. 'X -> 'X) -> (forall 'X0. 'X0 -> 'X0) & 'a) -> (forall 'X. 'X -> 'X) -> (forall 'X0. 'X0 -> 'X0) //│ = [Function: f] f ((fun y -> y) : forall 'X. 'X -> 'X) //│ res: (forall 'X. 'X -> 'X) -> (forall 'X0. 'X0 -> 'X0) //│ = [Function (anonymous)] def f = (fun z -> z z); f (fun y -> y) //│ f: ('a -> 'b & 'a) -> 'b //│ = [Function: f1] //│ res: 'a -> 'a //│ = [Function (anonymous)] // * A Dependently Typed Calculus with Polymorphic Subtyping // * https://i.cs.hku.hk/~bruno/papers/scp2021.pdf // callcc’ : ∀a:⋆. ((∀b:⋆. a → b) → a) → a // callcc : ∀a:⋆.∀b:⋆. ((a → b) → a) → a // callcc = callcc’ def callcc' : forall 'A. ((forall 'B. 'A -> 'B) -> 'A) -> 'A //│ callcc': (('A -> nothing) -> 'A) -> 'A //│ = def callcc : forall 'A. forall 'B. (('A -> 'B) -> 'A) -> 'A //│ callcc: (('A -> nothing) -> 'A) -> 'A //│ = def callcc = callcc' //│ (('A -> nothing) -> 'A) -> 'A //│ <: callcc: //│ (('A -> nothing) -> 'A) -> 'A //│ = //│ callcc' is not implemented ================================================ FILE: shared/src/test/diff/fcp-lit/PolyML.mls ================================================ :NoRecursiveTypes // ============ Sec 1.1 ============ // * i.e. auto g = fun (f: forall 'a. 'a -> 'a) -> f f g' = fun f -> f f //│ g: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: g] //│ g': ('a -> 'b & 'a) -> 'b //│ = [Function: g$] // * i.e. auto id (FreezeML F5) (fun (f: forall 'a. 'a -> 'a) -> f f) (fun x -> x) (fun f -> f f) (fun x -> x) //│ res: 'a -> 'a //│ = [Function (anonymous)] //│ res: 'a -> 'a //│ = [Function (anonymous)] // * i.e. (fun auto -> auto id) auto (fun g -> g (fun x -> x)) (fun f -> f f) (fun g -> g (fun x -> x)) (fun (f: forall 'a. 'a -> 'a) -> f f) //│ res: 'a -> 'a //│ = [Function (anonymous)] //│ res: 'a -> 'a //│ = [Function (anonymous)] // ============ Sec 1.2 ============ fun f -> f (g f) fun f -> f (g' f) //│ res: (forall 'a. 'a -> 'a & (forall 'a0. 'a0 -> 'a0) -> 'b) -> 'b //│ = [Function: res] //│ res: ('a -> 'b & 'b -> 'c & 'a) -> 'c //│ = [Function: res] // ============ Sec 4.1 ============ let f = fun x -> x in let g = (fun x -> x) f in g g //│ res: 'a -> 'a //│ = [Function (anonymous)] :NoJS // ============ Sec 4.2 ============ // * Using FCP to solve method typing issues, // * which is overkill since it can be solved with subtyping and two levels of polymorphism... // ------------ Dummy classes to represent the types in the examples ------------ class List[a] method Get: a //│ Defined class List[+a] //│ Declared List.Get: List['a] -> 'a // Used to represent `::` in the papers def cons[a]: a -> List[a] -> List[a] //│ cons: 'a -> List['a] -> List['a] // Used to represent `[]` in the papers def nil: List['a] //│ nil: List[nothing] def match_list: forall 'a 'b. List['a] -> 'b -> ('a -> List['a] -> 'b) -> 'b //│ match_list: List['a] -> 'b -> ('a -> List['a] -> 'b) -> 'b // ------------ Type signatures for functions used in the examples ------------ def mem[a]: a -> List[a] -> bool //│ mem: anything -> List[?] -> bool rec def mem x l = match_list l false (fun head -> fun tail -> if eq head x then true else mem x tail) //│ anything -> List[?] -> Bool //│ <: mem: //│ anything -> List[?] -> bool // def fold_left[a, b]: (a -> b -> a) -> a -> List[b] -> a def fold_left: forall 'a 'b. ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a //│ fold_left: ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a rec def fl f x l = match_list l x (fun head -> fun tail -> fl f (f x head) tail) //│ fl: ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a rec def fl2 f x l = match_list l x (fun head -> fun tail -> fold_left f (f x head) tail) //│ fl2: ('a -> 'b -> ('a & 'a0)) -> ('a & 'a0) -> List['b] -> 'a0 def fold_left = fl //│ ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a //│ <: fold_left: //│ ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a def fold_left = fl2 //│ ('a -> 'b -> ('a & 'a0)) -> ('a & 'a0) -> List['b] -> 'a0 //│ <: fold_left: //│ ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a rec def fold_left f x l = match_list l x (fun head -> fun tail -> fold_left f (f x head) tail) //│ ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a //│ <: fold_left: //│ ('a -> 'b -> 'a) -> 'a -> List['b] -> 'a // ------------ polymorphic methods ------------ class Collection[a]: { l: List[a] } method Mem x = mem x this.l method Fold f x = fold_left f x this.l //│ Defined class Collection[+a] //│ Defined Collection.Mem: Collection[?] -> anything -> bool //│ Defined Collection.Fold: Collection['a] -> ('a0 -> 'a -> 'a0) -> 'a0 -> 'a0 def coll_mem c x = c.Mem x //│ coll_mem: Collection[?] -> anything -> bool // * Typo in the paper? it was `fun x -> fun y -> ...` def simple_and_double c = let l1 = c.Fold (fun y -> fun x -> cons x y) nil in let l2 = c.Fold (fun y -> fun x -> cons ((x, x),) y) nil in (l1, l2) //│ simple_and_double: Collection['a] -> (List['a], List[('a, 'a,)],) simple_and_double (Collection{l = cons 1 nil}) //│ res: (List[1], List[(1, 1,)],) // ------------ subtyping & methods ------------ class Float class Color class Point: { x: Float; y: Float } class ColorPoint: Point & { c: Color } class Circle: { x: Float; y: Float; r: Float } method Distance: Point -> Float //│ Defined class Float //│ Defined class Color //│ Defined class Point //│ Defined class ColorPoint //│ Defined class Circle //│ Declared Circle.Distance: Circle -> Point -> Float c = error : Circle cp = error : ColorPoint c.Distance cp //│ c: Circle //│ cp: ColorPoint //│ res: Float ================================================ FILE: shared/src/test/diff/fcp-lit/QML.mls ================================================ :NoRecursiveTypes :DontDistributeForalls // :GeneralizeArguments // =============== Sec 2 =============== let id x = x in id (id 3, id false) //│ res: 'a -> 'a //│ = [Function (anonymous)] //│ res: (3, false,) //│ = [ 3, false ] let poly f = (f 1, f true) in poly //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function (anonymous)] let poly f = let y = f in (y 1, y true) in poly //│ res: (1 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function (anonymous)] // Altered from // let poly f = let y = f {forall 'a. 'a -> 'a} in (y 1, y true) def poly = fun (f : (forall 'a. 'a -> 'a)) -> (f 1, f true) //│ poly: (forall 'a. 'a -> 'a) -> (1, true,) //│ = [Function: poly] def app : ('a -> 'b) -> 'a -> 'b //│ app: ('a -> 'b) -> 'a -> 'b //│ = def revapp : 'a -> ('a -> 'b) -> 'b //│ revapp: 'a -> ('a -> 'b) -> 'b //│ = app poly (fun x -> x) //│ res: (1, true,) //│ = //│ app is not implemented revapp (fun x -> x) poly //│ res: (1, true,) //│ = //│ revapp is not implemented class List[A] method Get: A //│ Defined class List[+A] //│ Declared List.Get: List['A] -> 'A def single : 'a -> List['a] //│ single: 'a -> List['a] //│ = def ids = single (fun x -> x) //│ ids: List[forall 'a. 'a -> 'a] //│ = //│ single is not implemented single ids //│ res: List[List[forall 'a. 'a -> 'a]] //│ = //│ single is not implemented def map: ('a -> 'b) -> List['a] -> List['b] def head: List['a] -> 'a //│ map: ('a -> 'b) -> List['a] -> List['b] //│ = //│ head: List['a] -> 'a //│ = map head (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = //│ map is not implemented app (map head) (single ids) //│ res: List[forall 'a. 'a -> 'a] //│ = //│ app is not implemented def fst: forall 'a 'b. (('a, 'b),) -> 'a def fst ((x, _),) = x def snd: forall 'a 'b. (('a, 'b),) -> 'b def snd ((_, x),) = x //│ fst: (('a, anything,),) -> 'a //│ = //│ (('a, anything,),) -> 'a //│ <: fst: //│ (('a, anything,),) -> 'a //│ = [Function: fst] //│ snd: ((anything, 'b,),) -> 'b //│ = //│ ((anything, 'a,),) -> 'a //│ <: snd: //│ ((anything, 'b,),) -> 'b //│ = [Function: snd] // def pack : def pack t = fun f -> f t //│ pack: 'a -> (forall 'b. ('a -> 'b) -> 'b) //│ = [Function: pack] def open p t = p t //│ open: ('a -> 'b) -> 'a -> 'b //│ = [Function: open] // Altered from // let f = {exists 'a. 'a * ('a -> 'a) * ('a -> int)} (0, (fun x -> x + 1) , (fun x -> x)); // open {exists 'a. a * ('a -> 'a) * ('a -> int)} g = f in (snd (snd g)) (fst g); let f = pack ((0, (fun x -> x + 1, fun x -> x),),) in open f (fun x -> (snd (snd x)) (fst x)) //│ res: 0 //│ = 0 // =============== Sec 3 =============== def run : (forall 'g. 'g -> 'a) -> 'a //│ run: (anything -> 'a) -> 'a //│ = def e : 'g -> int //│ e: anything -> int //│ = // * Notice the effect of distributivity (fun (f : (forall 'g. int -> 'g)) -> f 1) //│ res: (int -> nothing) -> nothing //│ = [Function: res] run e //│ res: int //│ = //│ run is not implemented fun (x : (forall 'a. 'a -> 'a)) -> x //│ res: (forall 'a. 'a -> 'a) -> (forall 'a. 'a -> 'a) //│ = [Function: res] let abstype = pack ((id, id),) in let module = pack ((id, app),) in open abstype (fun x -> (snd x) (fst x 1)) //│ res: 1 //│ = //│ app is not implemented let abstype = pack ((id, id),) in let module = pack ((id, app),) in open module (fun x -> (snd x) (fun a -> ((a, a),)) (fst x 1)) //│ res: ((1, 1,),) //│ = //│ app is not implemented :escape def Math: nothing //│ Math: nothing //│ = def div: int -> int -> int def div a b = Math.trunc(a/b) //│ div: int -> int -> int //│ = //│ number -> number -> nothing //│ <: div: //│ int -> int -> int //│ = [Function: div1] def mod = let rec mod a b = if a < b then a else mod (a - b) b in mod //│ mod: int -> int -> int //│ = [Function: mod] // ********** SMALL ARRAY EXAMPLE ********** // exists r. forall b. Sig[a, r, b] // (forall r. (forall b. Sig[a, r, b]) -> y) -> y :DistributeForalls // :GeneralizeArguments type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ Defined type alias ExSmall def sbase : ExSmall def sbase f = f (fun a -> a, fun r -> fun (i : int) -> r) //│ sbase: ExSmall //│ = //│ ((forall 'a. 'a -> 'a, forall 'b. 'b -> int -> 'b,) -> 'c) -> 'c //│ <: sbase: //│ ExSmall //│ = [Function: sbase] def sstep : ExSmall -> ExSmall //│ sstep: ExSmall -> ExSmall //│ = :e // * Since "sound extrusion" def sstep = fun xx -> xx (fun (xinit, xsub) -> let init a = (xinit a, xinit a) in let sub ((r1, r2)) i = if mod i 2 == 0 then xsub r1 (div i 2) else xsub r2 (div i 2) in fun f -> f (init, sub)) //│ ((forall 'a 'b 'c 'd 'e. ('a -> 'b, 'c -> int -> 'd,) -> (('a -> ('b, 'b,), (('c, 'c,),) -> int -> 'd,) -> 'e) -> 'e) -> 'f) -> 'f //│ <: sstep: //│ ExSmall -> ExSmall //│ ╔══[ERROR] Type error in def definition //│ ║ l.198: def sstep = fun xx -> xx (fun (xinit, xsub) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.199: let init a = (xinit a, xinit a) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.200: let sub ((r1, r2)) i = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.201: if mod i 2 == 0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.202: then xsub r1 (div i 2) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.203: else xsub r2 (div i 2) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.204: fun f -> f (init, sub)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.181: type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ ║ ^^ //│ ╟── back into type variable `'r` //│ ║ l.181: type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.198: def sstep = fun xx -> xx (fun (xinit, xsub) -> //│ ║ ^^ //│ ╟── • this function: //│ ║ l.198: def sstep = fun xx -> xx (fun (xinit, xsub) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.199: let init a = (xinit a, xinit a) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.200: let sub ((r1, r2)) i = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.201: if mod i 2 == 0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.202: then xsub r1 (div i 2) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.203: else xsub r2 (div i 2) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.204: fun f -> f (init, sub)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.202: then xsub r1 (div i 2) //│ ╙── ^^ //│ = [Function: sstep] // * Alternative, with an annotation on `f`: def sstep = fun xx -> xx (fun (xinit, xsub) -> let init a = (xinit a, xinit a) in let sub ((r1, r2)) i = if mod i 2 == 0 then xsub r1 (div i 2) else xsub r2 (div i 2) in fun (f : forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> f (init, sub)) //│ ((forall 'a 'b 'c. ('a -> 'b, 'b -> int -> 'a,) -> (forall 'r. ('a -> 'r, 'r -> int -> 'a,) -> 'c) -> 'c) -> 'd) -> 'd //│ <: sstep: //│ ExSmall -> ExSmall //│ = [Function: sstep1] let mkSArray n = if n == 0 then sbase else sstep (sstep sbase) in mkSArray 1 (fun (xinit, xsub) -> xsub (xinit 2) 2) //│ res: 2 //│ = 2 def mkSArray : int -> ExSmall rec def mkSArray n = if n == 0 then sbase else sstep (mkSArray (n - 1)) //│ mkSArray: int -> ExSmall //│ = //│ int -> ExSmall //│ <: mkSArray: //│ int -> ExSmall //│ = [Function: mkSArray] mkSArray 2 (fun (xinit, xsub) -> xinit 2) //│ res: ??r //│ = [ [ 2, 2 ], [ 2, 2 ] ] ma2 = mkSArray 2 //│ ma2: ExSmall //│ = [Function (anonymous)] a2 = ma2 (fun (xinit, xsub) -> xsub (xinit true) 0) //│ a2: true //│ = true not a2 //│ res: bool //│ = false a2 = ma2 (fun (xinit, xsub) -> xinit true) //│ a2: ??r //│ = [ [ true, true ], [ true, true ] ] :e // This is expected – xinit returns a value of locally-quantified type 'r a2[0] //│ ╔══[ERROR] Type error in array access //│ ║ l.301: a2[0] //│ ║ ^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.181: type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ ║ ^^ //│ ╟── into array access of type `Array[?a]` //│ ║ l.301: a2[0] //│ ║ ^^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.296: a2 = ma2 (fun (xinit, xsub) -> xinit true) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.284: ma2 = mkSArray 2 //│ ║ ^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.296: a2 = ma2 (fun (xinit, xsub) -> xinit true) //│ ╙── ^^^ //│ res: error | undefined //│ = [ true, true ] // * Notice the (safely-)extruded skolem for the locally-quantified 'r ma2 (fun (xinit, xsub) -> let tmp = xinit true in (tmp, tmp)) //│ res: (??r, ??r,) //│ = [ //│ [ [ true, true ], [ true, true ] ], //│ [ [ true, true ], [ true, true ] ] //│ ] ma1 = mkSArray 1 //│ ma1: ExSmall //│ = [Function (anonymous)] ma1 (fun (xinit, xsub) -> xinit true) //│ res: ??r //│ = [ true, true ] ma1 (fun (xinit, xsub) -> xsub (xinit true) 0) //│ res: true //│ = true // * Trying to do something unsafe now! :e ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ╔══[ERROR] Type error in application //│ ║ l.348: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.181: type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.348: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ║ ^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.284: ma2 = mkSArray 2 //│ ║ ^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.348: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ║ ^^^ //│ ╟── • this applied expression: //│ ║ l.348: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ║ ^^^^^ //│ ╟── • this tuple literal: //│ ║ l.348: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.348: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub2 (xinit1 true) 0)) //│ ╙── ^^^^^^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: boolean true is not iterable (cannot read property Symbol(Symbol.iterator)) :e ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub1 (xinit2 true) 0)) //│ ╔══[ERROR] Type error in application //│ ║ l.379: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub1 (xinit2 true) 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.181: type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ ║ ^^ //│ ╟── into type `'r0` //│ ║ l.181: type ExSmall = (forall 'r. ('a -> 'r, 'r -> int -> 'a) -> 'y) -> 'y //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.379: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub1 (xinit2 true) 0)) //│ ║ ^^^^^^ //│ ╟── • this tuple literal: //│ ║ l.379: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub1 (xinit2 true) 0)) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.284: ma2 = mkSArray 2 //│ ║ ^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.379: ma1 (fun (xinit1, xsub1) -> ma2 (fun (xinit2, xsub2) -> xsub1 (xinit2 true) 0)) //│ ╙── ^^^ //│ res: error //│ = [ true, true ] // ******** ORIGINAL ARRAY EXAMPLE ******** type Sig[a, r, b] = (((a -> r, r -> int -> a), r -> int -> a -> r), (a -> b -> b) -> b -> r -> b) type ExSig = (forall 'r. (forall 'b. ((('a -> 'r, 'r -> int -> 'a), 'r -> int -> 'a -> 'r), ('a -> 'b -> 'b) -> 'b -> 'r -> 'b)) -> 'y) -> 'y //│ Defined type alias Sig[=a, =r, =b] //│ Defined type alias ExSig // :e // works after fixing freshening levels def base : ExSig def base f = f ((((fun a -> a, fun r -> fun (i : int) -> r), fun r -> fun (i : int) -> fun a -> a), fun f -> fun b -> fun r -> f r b),) //│ base: ExSig //│ = //│ (((((forall 'a. 'a -> 'a, forall 'b. 'b -> int -> 'b,), forall 'c. anything -> int -> 'c -> 'c,), forall 'd 'e 'f. ('d -> 'e -> 'f) -> 'e -> 'd -> 'f,),) -> 'g) -> 'g //│ <: base: //│ ExSig //│ = [Function: base] def step : ExSig -> ExSig //│ step: ExSig -> ExSig //│ = // * The problem here is due to the polymorphism of the `fold` function, which is defined on the outside of the tuple. // * The reproduction in `QML_exist_Records.mls`, where `fold` is quantified on the inside, does not have this problem. // * So I suppose we could avoid the problem by pushing (distributing) the quantification inside the tuple, which we do not yet support. :e def step = fun xx -> xx (fun ((((xinit, xsub), xupdate), xfold),) -> let init a = (xinit a, xinit a) in let sub r i = if mod i 2 == 0 then xsub (fst r) (div i 2) else xsub (snd r) (div i 2) in let update r i a = if mod i 2 == 0 then (xupdate (fst r) (div i 2) a, snd r) else (fst r, xupdate (snd r) (div i 2) a) in let fold f b r = xfold f (xfold f b (fst r)) (snd r) in fun f -> f ((((init, sub), update), fold),) ) //│ ((forall 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'm 'n 'o 'p. (((('a -> 'b, 'c -> int -> 'd,), 'e -> int -> 'f -> 'g & 'h -> int -> 'f -> 'i,), 'j -> ('k -> 'l -> 'm & 'n -> 'o -> 'k),),) -> ((((('a -> ('b, 'b,), (('c, 'c,),) -> int -> 'd,), forall 'q 'r. (('e & 'q, 'h & 'r,),) -> int -> 'f -> ('q | 'g, 'i | 'r,),), 'j -> 'n -> (('o, 'l,),) -> 'm,),) -> 'p) -> 'p) -> 's) -> 's //│ <: step: //│ ExSig -> ExSig //│ ╔══[ERROR] Type error in def definition //│ ║ l.431: def step = fun xx -> xx (fun ((((xinit, xsub), xupdate), xfold),) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.432: let init a = (xinit a, xinit a) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.433: let sub r i = //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.434: if mod i 2 == 0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.435: then xsub (fst r) (div i 2) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.436: else xsub (snd r) (div i 2) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.437: let update r i a = //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.438: if mod i 2 == 0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.439: then (xupdate (fst r) (div i 2) a, snd r) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.440: else (fst r, xupdate (snd r) (div i 2) a) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.441: let fold f b r = xfold f (xfold f b (fst r)) (snd r) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.442: fun f -> f ((((init, sub), update), fold),) ) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.409: type ExSig = (forall 'r. (forall 'b. ((('a -> 'r, 'r -> int -> 'a), 'r -> int -> 'a -> 'r), ('a -> 'b -> 'b) -> 'b -> 'r -> 'b)) -> 'y) -> 'y //│ ║ ^^ //│ ╟── back into type variable `'r` //│ ║ l.409: type ExSig = (forall 'r. (forall 'b. ((('a -> 'r, 'r -> int -> 'a), 'r -> int -> 'a -> 'r), ('a -> 'b -> 'b) -> 'b -> 'r -> 'b)) -> 'y) -> 'y //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.431: def step = fun xx -> xx (fun ((((xinit, xsub), xupdate), xfold),) -> //│ ║ ^^ //│ ╟── • this function: //│ ║ l.431: def step = fun xx -> xx (fun ((((xinit, xsub), xupdate), xfold),) -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.432: let init a = (xinit a, xinit a) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.433: let sub r i = //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.434: if mod i 2 == 0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.435: then xsub (fst r) (div i 2) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.436: else xsub (snd r) (div i 2) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.437: let update r i a = //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.438: if mod i 2 == 0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.439: then (xupdate (fst r) (div i 2) a, snd r) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.440: else (fst r, xupdate (snd r) (div i 2) a) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.441: let fold f b r = xfold f (xfold f b (fst r)) (snd r) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.442: fun f -> f ((((init, sub), update), fold),) ) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.82: def snd: forall 'a 'b. (('a, 'b),) -> 'b //│ ╙── ^^ //│ = [Function: step] let mkArray n = if n == 0 then base else step base in mkArray 1 (fun ((((xinit, xsub), xupdate), xfold),) -> xinit 2) //│ res: anything //│ = [ 2, 2 ] def mkArray : int -> ExSig //│ mkArray: int -> ExSig //│ = rec def mkArray n = if n == 0 then base else step (mkArray (n - 1)) //│ int -> ExSig //│ <: mkArray: //│ int -> ExSig //│ = [Function: mkArray] mkArray 3 (fun ((((xinit, xsub), xupdate), xfold),) -> xinit 2) //│ res: ??r //│ = [ [ [ 2, 2 ], [ 2, 2 ] ], [ [ 2, 2 ], [ 2, 2 ] ] ] :e res 0 //│ ╔══[ERROR] Type error in application //│ ║ l.531: res 0 //│ ║ ^^^^^ //│ ╟── type variable `'r` leaks out of its scope //│ ║ l.409: type ExSig = (forall 'r. (forall 'b. ((('a -> 'r, 'r -> int -> 'a), 'r -> int -> 'a -> 'r), ('a -> 'b -> 'b) -> 'b -> 'r -> 'b)) -> 'y) -> 'y //│ ║ ^^ //│ ╟── into application of type `0 -> ?a` //│ ║ l.531: res 0 //│ ║ ^^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.526: mkArray 3 (fun ((((xinit, xsub), xupdate), xfold),) -> xinit 2) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this application: //│ ║ l.526: mkArray 3 (fun ((((xinit, xsub), xupdate), xfold),) -> xinit 2) //│ ╙── ^^^^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: res is not a function // =============== Sec 7 =============== def choose: 'a -> 'a -> 'a //│ choose: 'a -> 'a -> 'a //│ = choose (head ids) //│ res: 'a -> (forall 'b. 'b -> 'b | 'a) //│ = //│ choose is not implemented ================================================ FILE: shared/src/test/diff/fcp-lit/Stability.mls ================================================ // * Seeking Stability by Being Lazy and Shallow // * Lazy and Shallow Instantiation Is User Friendly // * https://dl.acm.org/doi/10.1145/3471874.3472985 // * A lot of these examples are transcribed lossily because we currently // * do not have anything like explicit type applications and Haskell-style type classes. // * It turns out most of the examples are very Haskell-specific and thus not insightful for us. :NoRecursiveTypes // Preliminary definitions // id :: ∀ a. a → a def id: forall 'a. 'a -> 'a //│ id: 'a -> 'a //│ = // pair :: ∀ a. a → ∀ b. b → (a, b) def pair: forall 'a. 'a -> (forall 'b. 'b -> ('a, 'b,)) pair x y = (x, y) //│ pair: 'a -> 'b -> ('a, 'b,) //│ = //│ 'a -> 'b -> ('a, 'b,) //│ <: pair: //│ 'a -> 'b -> ('a, 'b,) //│ = [Function: pair] // myPairX x = pair x myPairX x = pair x //│ myPairX: 'a -> 'b -> ('a, 'b,) //│ = [Function: myPairX] // 3.1 Similarity 1: Let-Inlining and Extraction // Example 1: myId // myId = id myId = id //│ myId: 'a -> 'a //│ = //│ id is not implemented // Example 2: myPair // myPair = pair myPair = pair //│ myPair: 'a -> 'b -> ('a, 'b,) //│ = [Function: pair] // Example 3: myPairX // myPairX x = pair x myPairX x = pair x //│ myPairX: 'a -> 'b -> ('a, 'b,) //│ = [Function: myPairX1] // 3.2 Similarity 2: Signature Property // (The second similarity annotates a let binding with the inferred type 𝜎 of the bound expression e1.) // Example 4: infer // infer = 𝜆 @a (x :: a) → x infer = forall 'a. fun (x: 'a) -> x //│ infer: 'a -> 'a //│ = [Function: infer] def infer: 'a -> 'a infer = forall 'a. fun (x: 'a) -> x //│ infer: 'a -> 'a //│ = //│ 'a -> 'a //│ <: infer: //│ 'a -> 'a //│ = [Function: infer1] // 3.3 Similarity 3: Type Signatures // (Changing a type signature should not affect runtime semantics.) // Example 5: swizzle // undef :: ∀ a. Int → a → a // undef = undefined :re def undef: forall 'a. int -> 'a -> 'a undef = error //│ undef: int -> 'a -> 'a //│ = //│ nothing //│ <: undef: //│ int -> 'a -> 'a //│ Runtime error: //│ Error: an error was thrown // swizzle :: Int → ∀ a. a → a // swizzle = undef :re def swizzle: int -> (forall 'a. 'a -> 'a) swizzle = undef //│ swizzle: int -> (forall 'a. 'a -> 'a) //│ = //│ int -> 'a -> 'a //│ <: swizzle: //│ int -> (forall 'a. 'a -> 'a) //│ Runtime error: //│ ReferenceError: undef is not defined // In Haskell, "deeply skolemization" would eta expand swizzle as: swizzle = fun x -> undef x //│ int -> 'a -> 'a //│ <: swizzle: //│ int -> (forall 'a. 'a -> 'a) //│ = [Function: swizzle1] // 3.4 Similarity 4: Pattern-Inlining and Extraction // (changing variable patterns into 𝜆-binders) // Example 6: infer2, again // infer2 @a (x :: a) = x infer2 = forall 'a. fun (x: 'a) -> x //│ infer2: 'a -> 'a //│ = [Function: infer2] // 3.5 Similarity 5: Single vs. Multiple Equations // Example 7: unitId1 and unitId2 unitId1 () = id //│ unitId1: () -> (forall 'a. 'a -> 'a) //│ = //│ id is not implemented // Unlike in Haskell, here multiple equations simply shadow one another unitId2 () = id unitId2 () = id //│ unitId2: () -> (forall 'a. 'a -> 'a) //│ = //│ id is not implemented //│ unitId2: () -> (forall 'a. 'a -> 'a) //│ = //│ id is not implemented // 3.6 Similarity 6: 𝜂-Expansion // Example 8: eta // noEta = id noEta = id //│ noEta: 'a -> 'a //│ = //│ id is not implemented // eta = 𝜆x → id x eta = fun x -> id x //│ eta: 'a -> 'a //│ = //│ id is not implemented ================================================ FILE: shared/src/test/diff/fcp-lit/variations_PolyML.mls ================================================ // * Alternative: separate `Cons` and `Nil` types with definition of `mem` and `fold_left` // * :RecursiveTypes is now needed due to the structural typing of Cons and Nil // :NoRecursiveTypes :NoJS // ============ Sec 4.2 ============ // ------------ Dummy classes to represent the types in the examples ------------ class Nil: {} class Cons[a]: { head: a; tail: List[a] } type List[a] = Nil | Cons[a] //│ Defined class Nil //│ Defined class Cons[+a] //│ Defined type alias List[+a] def Nil = Nil {} //│ Nil: Nil def Cons head tail = Cons { head; tail } //│ Cons: ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) // ------------ Type signatures for functions used in the examples ------------ rec def mem x l = case l of { Nil -> false | Cons -> if eq l.head x then true else mem x l.tail } //│ mem: anything -> 'a -> Bool //│ where //│ 'a <: (Cons[?] with {tail: 'a}) | Nil rec def fold_left f x l = case l of { Nil -> x | Cons -> fold_left f (f x l.head) l.tail } //│ fold_left: ('a -> 'head -> 'a) -> 'a -> 'b -> 'a //│ where //│ 'b <: (Cons[?] with {head: 'head, tail: 'b}) | Nil // ------------ polymorphic methods ------------ class Collection[a]: { l: List[a] } method Mem x = mem x this.l method Fold f x = fold_left f x this.l //│ Defined class Collection[+a] //│ Defined Collection.Mem: Collection[?] -> anything -> Bool //│ Defined Collection.Fold: Collection['a] -> ('b -> 'a -> 'b) -> 'b -> 'b def coll_mem c x = c.Mem x //│ coll_mem: Collection[?] -> anything -> Bool // * Typo in the paper? it was `fun x -> fun y -> ...` def simple_and_double c = let l1 = c.Fold (fun y -> fun x -> Cons x y) Nil in let l2 = c.Fold (fun y -> fun x -> Cons ((x, x),) y) Nil in (l1, l2) //│ simple_and_double: Collection['a] -> (forall 'b. Nil | 'b, forall 'c. Nil | 'c,) //│ where //│ 'c :> Cons[('a, 'a,)] with {tail: forall 'c. Nil | 'c} //│ 'b :> Cons['a] with {tail: forall 'b. Nil | 'b} // * Note: the kind of errors we get witout recursive types: :NoRecursiveTypes :e rec def mem x l = case l of { Nil -> false | Cons -> if eq l.head x then true else mem x l.tail } //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {head: ?, tail: Cons[?] & 'a} //│ ║ l.72: rec def mem x l = case l of //│ ╙── ^ //│ mem: anything -> 'a -> Bool //│ where //│ 'a <: (Cons[?] with {tail: 'a}) | Nil ================================================ FILE: shared/src/test/diff/gadt/Exp1.mls ================================================ :NewDefs abstract class Exp[A]: Pair | Lit class Lit(n: Int) extends Exp[Int] class Pair[L, R](val lhs: L, val rhs: R) extends Exp[[L, R]] //│ abstract class Exp[A]: Lit | Pair[anything, anything] //│ class Lit(n: Int) extends Exp //│ class Pair[L, R](lhs: L, rhs: R) extends Exp fun f(p: Pair['a, 'b]) = p.lhs //│ fun f: forall 'a 'b. (p: Pair['a, 'b]) -> 'a fun f(e) = if e is Pair(l, r) then [l, r] //│ fun f: forall 'a 'b. Pair['a, 'b] -> ['a, 'b] // f: (Exp['a] & Pair) -> 0 fun f(e) = if e is Pair(l, r) then [l, r] Lit(n) then n //│ fun f: forall 'a 'b. (Lit | Pair['a, 'b]) -> (Int | ['a, 'b]) (e: Exp['X]) => f(e) //│ forall 'X. (e: Exp['X]) -> (Int | [??L, ??R]) //│ res //│ = [Function: res] :w fun f(e) = if e is Pair['a, 'b](l, r) then [l, r] //│ ╔══[WARNING] type parameters in patterns are currently ignored //│ ║ l.35: Pair['a, 'b](l, r) then [l, r] //│ ╙── ^^^^^^ //│ fun f: forall 'a 'b. Pair['a, 'b] -> ['a, 'b] :e // TODO support fun f(e) = if e is Pair(l: a, r) then let f(x: a) = x f(l) //│ ╔══[ERROR] type identifier not found: a //│ ║ l.45: let f(x: a) = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: l //│ ║ l.46: f(l) //│ ╙── ^ //│ fun f: Pair[anything, anything] -> error //│ Code generation encountered an error: //│ unresolved symbol l // fun f: forall 'lhs 'rhs. Pair['lhs, 'rhs] -> ('lhs, 'rhs,) ================================================ FILE: shared/src/test/diff/gadt/Exp2.mls ================================================ :NewDefs // * Variant: abstract class Exp[out A]: Pair | Lit class Lit(val n: Int) extends Exp[Int] class Pair[out L, out R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] //│ abstract class Exp[A]: Lit | Pair[anything, anything] //│ class Lit(n: Int) extends Exp //│ class Pair[L, R](lhs: Exp[L], rhs: Exp[R]) extends Exp fun f(p: Pair['a, 'b]) = p.lhs //│ fun f: forall 'a. (p: Pair['a, anything]) -> Exp['a] fun f(e) = if e is Pair(l, r) then [l, r] //│ fun f: forall 'L 'R. Pair['L, 'R] -> [Exp['L], Exp['R]] // f: (Exp['a] & Pair) -> 0 fun f(e) = if e is Pair(l, r) then [l, r] Lit(n) then n //│ fun f: forall 'L 'R. (Lit | Pair['L, 'R]) -> (Int | [Exp['L], Exp['R]]) (e: Exp['X]) => f(e) //│ (e: Exp[anything]) -> (Int | [Exp[??L], Exp[??R]]) //│ res //│ = [Function: res] fun f(e) = if e is Pair(l, r) then f(l) + f(r) Lit(n) then n //│ fun f: (Lit | Pair[anything, anything]) -> Int // * Invariant: abstract class Exp[A]: Pair | Lit class Lit(val n: Int) extends Exp[Int] class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] //│ abstract class Exp[A]: Lit | Pair[?, ?] //│ class Lit(n: Int) extends Exp //│ class Pair[L, R](lhs: Exp[L], rhs: Exp[R]) extends Exp fun f(p: Pair['a, 'b]) = p.lhs //│ fun f: forall 'a 'b. (p: Pair['a, 'b]) -> Exp['a] fun f(e) = if e is Pair(l, r) then [l, r] //│ fun f: forall 'L 'R. Pair['L, 'R] -> [Exp['L], Exp['R]] // f: (Exp['a] & Pair) -> 0 fun f(e) = if e is Pair(l, r) then [l, r] Lit(n) then n //│ fun f: forall 'L 'R. (Lit | Pair['L, 'R]) -> (Int | [Exp['L], Exp['R]]) :e (e: Exp['X]) => f(e) //│ ╔══[ERROR] Type error in application //│ ║ l.63: (e: Exp['X]) => f(e) //│ ║ ^^^^ //│ ╟── type variable `L` leaks out of its scope //│ ║ l.43: class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] //│ ╙── ^ //│ forall 'X 'L 'R. (e: Exp['X]) -> (Int | error | [Exp['L], Exp['R]]) //│ where //│ 'R :> ??R //│ <: ??R0 //│ 'L :> ??L //│ <: ??L0 //│ res //│ = [Function: res] :e fun f(e) = if e is Pair(l, r) then f(l) + f(r) Lit(n) then n //│ ╔══[ERROR] Type error in definition //│ ║ l.80: fun f(e) = if e is //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.81: Pair(l, r) then f(l) + f(r) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.82: Lit(n) then n //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `L` leaks out of its scope //│ ║ l.43: class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] //│ ╙── ^ //│ ╔══[ERROR] Type error in definition //│ ║ l.80: fun f(e) = if e is //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.81: Pair(l, r) then f(l) + f(r) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.82: Lit(n) then n //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `R` leaks out of its scope //│ ║ l.43: class Pair[L, R](val lhs: Exp[L], val rhs: Exp[R]) extends Exp[[L, R]] //│ ╙── ^ //│ fun f: forall 'L 'R. (Lit | Pair['L, 'R]) -> Int //│ where //│ 'R :> ??R //│ <: ??R0 //│ 'L :> ??L //│ <: ??L0 ================================================ FILE: shared/src/test/diff/gadt/ThisMatching.mls ================================================ :NewDefs :e :re module Dummy { log(if this is Dummy then "duh!" else "huh?") } //│ ╔══[ERROR] Cannot access `this` during object initialization //│ ║ l.7: log(if this is Dummy then "duh!" else "huh?") //│ ╙── ^^^^ //│ module Dummy //│ Runtime error: //│ RangeError: Maximum call stack size exceeded module Dummy { fun introspect = if this is Dummy then "duh!" else "huh?" } //│ module Dummy { //│ fun introspect: "duh!" | "huh?" //│ } Dummy.introspect //│ "duh!" | "huh?" //│ res //│ = 'duh!' abstract class Funny: Int { fun test = this + 1 } //│ abstract class Funny: Int { //│ fun test: Int //│ } :e class Unfunny { fun test = this + 1 } //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.36: class Unfunny { fun test = this + 1 } //│ ║ ^^^^^^^^ //│ ╟── reference of type `#Unfunny` is not an instance of type `Int` //│ ║ l.36: class Unfunny { fun test = this + 1 } //│ ╙── ^^^^ //│ class Unfunny { //│ constructor() //│ fun test: Int | error //│ } abstract class Exp: (Pair | Lit) { fun test = if this is Lit then 0 Pair then 1 } class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ abstract class Exp: Lit | Pair { //│ fun test: 0 | 1 //│ } //│ class Lit(n: Int) extends Exp { //│ fun test: 0 | 1 //│ } //│ class Pair(lhs: Exp, rhs: Exp) extends Exp { //│ fun test: 0 | 1 //│ } abstract class Exp: (() | Lit) { fun test = if this is Lit then 0 () then 1 } class Lit(n: Int) extends Exp //│ abstract class Exp: Lit | () { //│ fun test: 0 | 1 //│ } //│ class Lit(n: Int) extends Exp { //│ fun test: 0 | 1 //│ } // * TODO fix this by requiring a type annotation on `test` and delaying its body's type checking until all the involed classes are completed // * Currently we try to complete Exp ->{type checking defn body} complete test ->{type checking pattern} find Wrap's typed fields ->{get Wrap's typed parents} complete Exp :e abstract class Exp: (() | Wrap) { fun test = if this is Wrap(a) then 0 () then 1 } class Wrap(n: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.83: abstract class Exp: (() | Wrap) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.84: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.85: Wrap(a) then 0 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.86: () then 1 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.87: } //│ ╙── ^ //│ abstract class Exp: Wrap | () { //│ fun test: 0 | 1 //│ } //│ class Wrap(n: Exp) extends Exp // * TODO (same as above) :e abstract class Exp: (Pair | Lit) { fun test: Int fun test = if this is Lit then 0 Pair(l, r) then 1 } class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.107: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.108: fun test: Int //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.109: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.110: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.111: Pair(l, r) then 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.112: } //│ ╙── ^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int //│ } //│ class Lit(n: Int) extends Exp { //│ fun test: Int //│ } //│ class Pair(lhs: Exp, rhs: Exp) extends Exp :e // TODO Pair(Lit(1), Lit(2)).test //│ ╔══[ERROR] Type `Pair` does not contain member `test` //│ ║ l.137: Pair(Lit(1), Lit(2)).test //│ ╙── ^^^^^ //│ error //│ res //│ = 1 :e // TODO can we support this? abstract class Exp: (Pair | Lit) { fun test = if this is Lit then 0 Pair(l, r) then l.test + r.test } class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.147: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.148: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.149: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.150: Pair(l, r) then l.test + r.test //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.151: } //│ ╙── ^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.150: Pair(l, r) then l.test + r.test //│ ╙── ^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.150: Pair(l, r) then l.test + r.test //│ ╙── ^^^^^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int //│ } //│ class Lit(n: Int) extends Exp { //│ fun test: Int //│ } //│ class Pair(lhs: Exp, rhs: Exp) extends Exp :e // TODO abstract class Exp: (Pair | Lit) { fun test : Int fun test = if this is Lit then 0 Pair(l, r) then l.test + r.test } class Lit(n: Int) extends Exp class Pair(lhs: Exp, rhs: Exp) extends Exp //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.181: abstract class Exp: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.182: fun test : Int //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.183: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.184: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.185: Pair(l, r) then l.test + r.test //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.186: } //│ ╙── ^ //│ abstract class Exp: Lit | Pair { //│ fun test: Int //│ } //│ class Lit(n: Int) extends Exp { //│ fun test: Int //│ } //│ class Pair(lhs: Exp, rhs: Exp) extends Exp :e // TODO support – this requires implementing type member lookup without forced completion (we get constraints like Pair <: Pair#L) abstract class Exp[A]: (Pair | Lit) { fun test = if this is Lit then 0 Pair then 1 } class Lit(n: Int) extends Exp[Int] class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.212: abstract class Exp[A]: (Pair | Lit) { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.213: fun test = if this is //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.214: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.215: Pair then 1 //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.216: } //│ ╙── ^ //│ ╔══[ERROR] Type error in `case` expression //│ ║ l.213: fun test = if this is //│ ║ ^^^^^^^ //│ ║ l.214: Lit then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.215: Pair then 1 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `L` leaks out of its scope //│ ║ l.218: class Pair[L, R](lhs: L, rhs: R) extends Exp[[L, R]] //│ ╙── ^ //│ abstract class Exp[A]: Lit | Pair[anything, anything] { //│ fun test: 0 | 1 //│ } //│ class Lit(n: Int) extends Exp { //│ fun test: 0 | 1 //│ } //│ class Pair[L, R](lhs: L, rhs: R) extends Exp Lit(0).test //│ 0 | 1 //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/gen/genTests_v1-0.14-15-x2.fun ================================================ // Automatically generated by mlscript.EnumeratePrograms — DO NOT EDIT :AllowTypeErrors :ShowRelativeLineNums 0 //│ res: 0 add //│ res: int -> int -> int (0 0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (0 0) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (0 0) //│ ╙── ^ //│ res: error (0 add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (0 add) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (0 add) //│ ╙── ^ //│ res: error (0 {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (0 {u: 0}) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (0 {u: 0}) //│ ╙── ^ //│ res: error (0 {u: add}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (0 {u: add}) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (0 {u: add}) //│ ╙── ^ //│ res: error (add 0) //│ res: int -> int (add add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (add add) //│ ║ ^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: (add add) //│ ╙── ^^^ //│ res: error | int -> int (add 0.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (add 0.u) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (add 0.u) //│ ╙── ^ //│ res: int -> int (add add.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (add add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (add add.u) //│ ╙── ^^^ //│ res: int -> int (add 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (add 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (add 0.v) //│ ╙── ^ //│ res: int -> int (add add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (add add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (add add.v) //│ ╙── ^^^ //│ res: int -> int (add {v: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (add {v: 0}) //│ ║ ^^^^^^^^^^ //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: (add {v: 0}) //│ ╙── ^^^^^^ //│ res: error | int -> int (add {v: add}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (add {v: add}) //│ ║ ^^^^^^^^^^^^ //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: (add {v: add}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int (add {v: 0.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (add {v: 0.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (add {v: 0.v}) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (add {v: 0.v}) //│ ║ ^^^^^^^^^^^^ //│ ╟── record of type `{v: ?v}` is not an instance of type `int` //│ ║ l.+1: (add {v: 0.v}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int (add {v: add.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (add {v: add.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (add {v: add.v}) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (add {v: add.v}) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── record of type `{v: ?v}` is not an instance of type `int` //│ ║ l.+1: (add {v: add.v}) //│ ╙── ^^^^^^^^^^ //│ res: error | int -> int ((x => 0) 0) //│ res: 0 ((x => 0) add) //│ res: 0 ((x => 0) {u: 0}) //│ res: 0 ((x => 0) {u: 0, v: 0}) //│ res: 0 ((x => 0) {u: 0, v: add}) //│ res: 0 ((x => 0) {u: add}) //│ res: 0 ((x => add) 0) //│ res: int -> int -> int ((x => add) add) //│ res: int -> int -> int ((x => add) 0.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add) 0.u) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => add) 0.u) //│ ╙── ^ //│ res: int -> int -> int ((x => add) add.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add) add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add) add.u) //│ ╙── ^^^ //│ res: int -> int -> int ((x => add) {u: 0}) //│ res: int -> int -> int ((x => add) {u: add}) //│ res: int -> int -> int ((x => add) {u: add, v: 0}) //│ res: int -> int -> int ((x => add) {u: add, v: add}) //│ res: int -> int -> int ((x => add) {u: 0.u}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add) {u: 0.u}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => add) {u: 0.u}) //│ ╙── ^ //│ res: int -> int -> int ((x => add) {u: add.u}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add) {u: add.u}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add) {u: add.u}) //│ ╙── ^^^ //│ res: int -> int -> int ((x => add) 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add) 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: ((x => add) 0.v) //│ ╙── ^ //│ res: int -> int -> int ((x => add) add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add) add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: ((x => add) add.v) //│ ╙── ^^^ //│ res: int -> int -> int ((let x = 0; add) 0) //│ res: int -> int ((let x = 0; add) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) add) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) add) //│ ╙── ^^^ //│ res: error | int -> int ((let x = 0; add) (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = 0; add) (0 0)) //│ ╙── ^ //│ res: int -> int ((let x = 0; add) (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = 0; add) (0 add)) //│ ╙── ^ //│ res: int -> int ((let x = 0; add) (x => 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) (x => 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> 0` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) (x => 0)) //│ ║ ^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = 0; add) (x => 0)) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = 0; add) (let x = 0; 0)) //│ res: int -> int ((let x = 0; add) (let x = add; 0)) //│ res: int -> int ((let x = 0; add) (let rec x = x; 0)) //│ res: int -> int ((let x = 0; add) (let x = 0.u; 0)) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = 0; add) (let x = 0.u; 0)) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((let x = 0; add) (let x = 0.u; 0)) //│ ╙── ^ //│ res: int -> int ((let x = 0; add) (let x = add.u; 0)) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = 0; add) (let x = add.u; 0)) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((let x = 0; add) (let x = add.u; 0)) //│ ╙── ^^^ //│ res: int -> int ((let x = 0; add) (let rec x = x.u; 0)) //│ res: int -> int ((let x = 0; add) (x => add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) (x => add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) (x => add)) //│ ║ ^^^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = 0; add) (x => add)) //│ ╙── ^^^^^^^^^^ //│ res: error | int -> int ((let x = 0; add) (x => x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) (x => x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> ?a` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) (x => x)) //│ ║ ^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = 0; add) (x => x)) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = 0; add) (let x = 0; x)) //│ res: int -> int ((let x = 0; add) (let x = add; x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) (let x = add; x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) (let x = add; x)) //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.+1: ((let x = 0; add) (let x = add; x)) //│ ╙── ^ //│ res: error | int -> int ((let x = 0; add) (let rec x = x; x)) //│ res: int -> int ((let x = 0; add) 0.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = 0; add) 0.u) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((let x = 0; add) 0.u) //│ ╙── ^ //│ res: int -> int ((let x = 0; add) add.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = 0; add) add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((let x = 0; add) add.u) //│ ╙── ^^^ //│ res: int -> int ((let x = 0; add) {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) {u: 0}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{u: 0}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {u: 0}) //│ ╙── ^^^^^^ //│ res: error | int -> int ((let x = 0; add) {u: add}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) {u: add}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{u: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {u: add}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = 0; add) {v: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) {v: 0}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {v: 0}) //│ ╙── ^^^^^^ //│ res: error | int -> int ((let x = 0; add) {v: add}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = 0; add) {v: add}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let x = 0; add) {v: add}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = add; add) 0) //│ res: int -> int ((let x = add; add) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) add) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) add) //│ ╙── ^^^ //│ res: error | int -> int ((let x = add; add) (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = add; add) (0 0)) //│ ╙── ^ //│ res: int -> int ((let x = add; add) (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = add; add) (0 add)) //│ ╙── ^ //│ res: int -> int ((let x = add; add) (0 {u: 0})) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (0 {u: 0})) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = add; add) (0 {u: 0})) //│ ╙── ^ //│ res: int -> int ((let x = add; add) (0 {u: add})) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (0 {u: add})) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = add; add) (0 {u: add})) //│ ╙── ^ //│ res: int -> int ((let x = add; add) ((0 add) 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) ((0 add) 0)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = add; add) ((0 add) 0)) //│ ╙── ^ //│ res: int -> int ((let x = add; add) ((0 add) add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) ((0 add) add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((let x = add; add) ((0 add) add)) //│ ╙── ^ //│ res: int -> int ((let x = add; add) (x => 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (x => 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> 0` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) (x => 0)) //│ ║ ^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (x => 0)) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = add; add) (let x = 0; 0)) //│ res: int -> int ((let x = add; add) (let x = add; 0)) //│ res: int -> int ((let x = add; add) (let rec x = x; 0)) //│ res: int -> int ((let x = add; add) (x => add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (x => add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) (x => add)) //│ ║ ^^^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (x => add)) //│ ╙── ^^^^^^^^^^ //│ res: error | int -> int ((let x = add; add) (let x = 0; add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (let x = 0; add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) (let x = 0; add)) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (let x = 0; add)) //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: error | int -> int ((let x = add; add) (let x = add; add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (let x = add; add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) (let x = add; add)) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (let x = add; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ res: error | int -> int ((let x = add; add) (let rec x = x; add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (let rec x = x; add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) (let rec x = x; add)) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (let rec x = x; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res: error | int -> int ((let x = add; add) (x => x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = add; add) (x => x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> ?a` is not an instance of type `int` //│ ║ l.+1: ((let x = add; add) (x => x)) //│ ║ ^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = add; add) (x => x)) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let rec x = x; add) 0) //│ res: int -> int ((let rec x = x; add) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = x; add) add) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) add) //│ ╙── ^^^ //│ res: error | int -> int ((let rec x = x; add) 0.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let rec x = x; add) 0.u) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((let rec x = x; add) 0.u) //│ ╙── ^ //│ res: int -> int ((let rec x = x; add) add.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let rec x = x; add) add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((let rec x = x; add) add.u) //│ ╙── ^^^ //│ res: int -> int ((let rec x = x; add) {u: 0}.u) //│ res: int -> int ((let rec x = x; add) {u: add}.u) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = x; add) {u: add}.u) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {u: add}.u) //│ ║ ^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.+1: ((let rec x = x; add) {u: add}.u) //│ ╙── ^^ //│ res: error | int -> int ((let rec x = x; add) {v: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = x; add) {v: 0}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: 0}) //│ ╙── ^^^^^^ //│ res: error | int -> int ((let rec x = x; add) {v: add}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = x; add) {v: add}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: add}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let rec x = x; add) {v: 0.u}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let rec x = x; add) {v: 0.u}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((let rec x = x; add) {v: 0.u}) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = x; add) {v: 0.u}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: ?u}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: 0.u}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let rec x = x; add) {v: add.u}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let rec x = x; add) {v: add.u}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((let rec x = x; add) {v: add.u}) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = x; add) {v: add.u}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: ?u}` is not an instance of type `int` //│ ║ l.+1: ((let rec x = x; add) {v: add.u}) //│ ╙── ^^^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) 0) //│ res: int -> int ((let x = {v: 0}; add) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) add) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) add) //│ ╙── ^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (add 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (add 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add 0)) //│ ║ ^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add 0)) //│ ╙── ^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (add add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (add add)) //│ ║ ^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add add)) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (add add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add add)) //│ ║ ^^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (add add)) //│ ╙── ^^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (x => 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> 0` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0)) //│ ║ ^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0)) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (let x = 0; 0)) //│ res: int -> int ((let x = {v: 0}; add) (let x = add; 0)) //│ res: int -> int ((let x = {v: 0}; add) (let rec x = x; 0)) //│ res: int -> int ((let x = {v: 0}; add) (x => add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (x => add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => add)) //│ ║ ^^^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => add)) //│ ╙── ^^^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (let x = 0; add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (let x = 0; add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let x = 0; add)) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let x = 0; add)) //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (let x = add; add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (let x = add; add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let x = add; add)) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let x = add; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (let rec x = x; add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (let rec x = x; add)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let rec x = x; add)) //│ ║ ^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (let rec x = x; add)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) (x => x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: 0}; add) (x => x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> ?a` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => x)) //│ ║ ^^^^^^ //│ ╟── but it flows into argument with expected type `int` //│ ║ l.+1: ((let x = {v: 0}; add) (x => x)) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let x = {v: 0}; add) 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = {v: 0}; add) 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: ((let x = {v: 0}; add) 0.v) //│ ╙── ^ //│ res: int -> int ((let x = {v: 0}; add) add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = {v: 0}; add) add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: ((let x = {v: 0}; add) add.v) //│ ╙── ^^^ //│ res: int -> int ((let x = {v: 0}; add) (x => 0).v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0).v) //│ ║ ^^ //│ ╟── function of type `?a -> 0` does not have field 'v' //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0).v) //│ ║ ^^^^^^ //│ ╟── but it flows into receiver with expected type `{v: ?v}` //│ ║ l.+1: ((let x = {v: 0}; add) (x => 0).v) //│ ╙── ^^^^^^^^ //│ res: int -> int ((let x = {v: 0}; add) (x => add).v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = {v: 0}; add) (x => add).v) //│ ║ ^^ //│ ╟── function of type `?a -> int -> int -> int` does not have field 'v' //│ ║ l.+1: ((let x = {v: 0}; add) (x => add).v) //│ ║ ^^^^^^^^ //│ ╟── but it flows into receiver with expected type `{v: ?v}` //│ ║ l.+1: ((let x = {v: 0}; add) (x => add).v) //│ ╙── ^^^^^^^^^^ //│ res: int -> int ((let x = {v: 0}; add) (x => x).v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((let x = {v: 0}; add) (x => x).v) //│ ║ ^^ //│ ╟── function of type `?a -> ?a` does not have field 'v' //│ ║ l.+1: ((let x = {v: 0}; add) (x => x).v) //│ ║ ^^^^^^ //│ ╟── but it flows into receiver with expected type `{v: ?v}` //│ ║ l.+1: ((let x = {v: 0}; add) (x => x).v) //│ ╙── ^^^^^^^^ //│ res: int -> int ((let x = {v: add}; add) 0) //│ res: int -> int ((let x = {v: add}; add) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: add}; add) add) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: add}; add) add) //│ ╙── ^^^ //│ res: error | int -> int ((let x = {v: add}; add) {v: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: add}; add) {v: 0}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: add}; add) {v: 0}) //│ ╙── ^^^^^^ //│ res: error | int -> int ((let x = {v: add}; add) {v: add}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let x = {v: add}; add) {v: add}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: ((let x = {v: add}; add) {v: add}) //│ ╙── ^^^^^^^^ //│ res: error | int -> int ((let rec x = {v: x}; add) 0) //│ res: int -> int ((let rec x = {v: x}; add) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((let rec x = {v: x}; add) add) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: ((let rec x = {v: x}; add) add) //│ ╙── ^^^ //│ res: error | int -> int ((x => x) 0) //│ res: 0 ((x => x) add) //│ res: int -> int -> int ((x => x) (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x) (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((x => x) (0 0)) //│ ╙── ^ //│ res: error ((x => x) (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x) (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((x => x) (0 add)) //│ ╙── ^ //│ res: error ((x => x) (0 (x => 0))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x) (0 (x => 0))) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((x => x) (0 (x => 0))) //│ ╙── ^ //│ res: error ((x => x) (0 (x => add))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x) (0 (x => add))) //│ ║ ^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((x => x) (0 (x => add))) //│ ╙── ^ //│ res: error ((x => x) (0 (x => x))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x) (0 (x => x))) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: ((x => x) (0 (x => x))) //│ ╙── ^ //│ res: error ((x => x) {v: 0}) //│ res: {v: 0} ((x => x) {v: add}) //│ res: {v: int -> int -> int} ((x => 0.u) 0) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => 0.u) 0) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => 0.u) 0) //│ ╙── ^ //│ res: error ((x => 0.u) add) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => 0.u) add) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => 0.u) add) //│ ╙── ^ //│ res: error ((x => add.u) 0) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add.u) 0) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add.u) 0) //│ ╙── ^^^ //│ res: error ((x => add.u) add) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add.u) add) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add.u) add) //│ ╙── ^^^ //│ res: error ((x => add.u) 0.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add.u) 0.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add.u) 0.u) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add.u) 0.u) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => add.u) 0.u) //│ ╙── ^ //│ res: error ((x => add.u) add.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add.u) add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add.u) add.u) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => add.u) add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => add.u) add.u) //│ ╙── ^^^ //│ res: error ((x => x.u) 0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x.u) 0) //│ ║ ^^^^^^^^^^^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => x.u) 0) //│ ║ ^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.+1: ((x => x.u) 0) //│ ║ ^^ //│ ╟── from reference: //│ ║ l.+1: ((x => x.u) 0) //│ ╙── ^ //│ res: error ((x => x.u) add) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x.u) add) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => x.u) add) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.+1: ((x => x.u) add) //│ ║ ^^ //│ ╟── from reference: //│ ║ l.+1: ((x => x.u) add) //│ ╙── ^ //│ res: error ((x => x.u) 0.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => x.u) 0.u) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => x.u) 0.u) //│ ╙── ^ //│ res: error ((x => x.u) add.u) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: ((x => x.u) add.u) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => x.u) add.u) //│ ╙── ^^^ //│ res: error ((x => x.u) {u: 0}.u) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x.u) {u: 0}.u) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: ((x => x.u) {u: 0}.u) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `{u: ?u}` //│ ║ l.+1: ((x => x.u) {u: 0}.u) //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.+1: ((x => x.u) {u: 0}.u) //│ ║ ^^ //│ ╟── from reference: //│ ║ l.+1: ((x => x.u) {u: 0}.u) //│ ╙── ^ //│ res: error ((x => x.u) {u: add}.u) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: ((x => x.u) {u: add}.u) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: ((x => x.u) {u: add}.u) //│ ║ ^^^ //│ ╟── but it flows into field selection with expected type `{u: ?u}` //│ ║ l.+1: ((x => x.u) {u: add}.u) //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.+1: ((x => x.u) {u: add}.u) //│ ║ ^^ //│ ╟── from reference: //│ ║ l.+1: ((x => x.u) {u: add}.u) //│ ╙── ^ //│ res: error (x => 0) //│ res: anything -> 0 (let x = 0; 0) //│ res: 0 (let x = add; 0) //│ res: 0 (let rec x = x; 0) //│ res: 0 (let x = 0.u; 0) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; 0) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u; 0) //│ ╙── ^ //│ res: 0 (let x = add.u; 0) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; 0) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u; 0) //│ ╙── ^^^ //│ res: 0 (let rec x = x.u; 0) //│ res: 0 (x => add) //│ res: anything -> int -> int -> int (let x = 0; add) //│ res: int -> int -> int (let x = add; add) //│ res: int -> int -> int (let rec x = x; add) //│ res: int -> int -> int (let x = 0.v; add) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; add) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; add) //│ ╙── ^ //│ res: int -> int -> int (let x = add.v; add) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; add) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; add) //│ ╙── ^^^ //│ res: int -> int -> int (let rec x = x.v; add) //│ res: int -> int -> int (x => x) //│ res: 'a -> 'a (let x = 0; x) //│ res: 0 (let x = add; x) //│ res: int -> int -> int (let rec x = x; x) //│ res: nothing (let x = (0 0); x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 0); x) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 0); x) //│ ╙── ^ //│ res: error (let x = (0 add); x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 add); x) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 add); x) //│ ╙── ^ //│ res: error (let rec x = (0 x); x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 x); x) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 x); x) //│ ╙── ^ //│ res: error (let x = 0.u; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; x) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u; x) //│ ╙── ^ //│ res: error (let x = add.u; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; x) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u; x) //│ ╙── ^^^ //│ res: error (let rec x = x.u; x) //│ res: nothing (let x = {v: 0}.u; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: 0}.u; x) //│ ║ ^^ //│ ╟── record of type `{v: 0}` does not have field 'u' //│ ║ l.+1: (let x = {v: 0}.u; x) //│ ╙── ^^^^^^ //│ res: error (let x = {v: add}.u; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: add}.u; x) //│ ║ ^^ //│ ╟── record of type `{v: int -> int -> int}` does not have field 'u' //│ ║ l.+1: (let x = {v: add}.u; x) //│ ╙── ^^^^^^^^ //│ res: error (let rec x = {v: x}.u; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = {v: x}.u; x) //│ ║ ^^ //│ ╟── record of type `{v: ?x}` does not have field 'u' //│ ║ l.+1: (let rec x = {v: x}.u; x) //│ ╙── ^^^^^^ //│ res: error (let x = {u: 0}; x) //│ res: {u: 0} (let x = {u: 0, v: 0}; x) //│ res: {u: 0, v: 0} (let x = {u: 0, v: add}; x) //│ res: {u: 0, v: int -> int -> int} (let rec x = {u: 0, v: x}; x) //│ res: 'x //│ where //│ 'x :> {u: 0, v: 'x} (let x = {u: 0, v: 0.v}; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {u: 0, v: 0.v}; x) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = {u: 0, v: 0.v}; x) //│ ╙── ^ //│ res: {u: 0, v: error} (let x = {u: 0, v: add.v}; x) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {u: 0, v: add.v}; x) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = {u: 0, v: add.v}; x) //│ ╙── ^^^ //│ res: {u: 0, v: error} (let rec x = {u: 0, v: x.v}; x) //│ res: {u: 0, v: nothing} (let x = {u: add}; x) //│ res: {u: int -> int -> int} (let rec x = {u: x}; x) //│ res: 'x //│ where //│ 'x :> {u: 'x} (x => (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (x => (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (x => (0 0)) //│ ╙── ^ //│ res: anything -> error (let x = 0; (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = 0; (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = 0; (0 0)) //│ ╙── ^ //│ res: error (let x = add; (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = add; (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = add; (0 0)) //│ ╙── ^ //│ res: error (let rec x = x; (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = x; (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = x; (0 0)) //│ ╙── ^ //│ res: error (let x = (0 0); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 0); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 0); (0 0)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 0); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 0); (0 0)) //│ ╙── ^ //│ res: error (let x = (0 add); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 add); (0 0)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 add); (0 0)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 add); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 add); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (0 x); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 x); (0 0)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 x); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => 0); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => 0); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => 0); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => add); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => add); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => add); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => y); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => y); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => y); (0 0)) //│ ╙── ^ //│ res: error (let x = (let y = 0; y); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (let y = 0; y); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (let y = 0; y); (0 0)) //│ ╙── ^ //│ res: error (let x = (let y = add; y); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (let y = add; y); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (let y = add; y); (0 0)) //│ ╙── ^ //│ res: error (let x = (let rec y = y; y); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (let rec y = y; y); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (let rec y = y; y); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = x; y); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = x; y); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = x; y); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (y => x); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (y => x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (y => x); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = 0; x); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = 0; x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = 0; x); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = add; x); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = add; x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = add; x); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let rec y = y; x); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let rec y = y; x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let rec y = y; x); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = x; x); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = x; x); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = x; x); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => (z => 0)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => (z => 0)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => (z => 0)); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => (z => add)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => (z => add)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => (z => add)); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => (z => z)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => (z => z)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => (z => z)); (0 0)) //│ ╙── ^ //│ res: error (let x = (y => (z => y)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => (z => y)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => (z => y)); (0 0)) //│ ╙── ^ //│ res: error (let x = (let y = 0; (z => y)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (let y = 0; (z => y)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (let y = 0; (z => y)); (0 0)) //│ ╙── ^ //│ res: error (let x = (let y = add; (z => y)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (let y = add; (z => y)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (let y = add; (z => y)); (0 0)) //│ ╙── ^ //│ res: error (let x = (let rec y = y; (z => y)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (let rec y = y; (z => y)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (let rec y = y; (z => y)); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = x; (z => y)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = x; (z => y)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = x; (z => y)); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (y => (z => x)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (y => (z => x)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (y => (z => x)); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = 0; (z => x)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = 0; (z => x)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = 0; (z => x)); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = add; (z => x)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = add; (z => x)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = add; (z => x)); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let rec y = y; (z => x)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let rec y = y; (z => x)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let rec y = y; (z => x)); (0 0)) //│ ╙── ^ //│ res: error (let rec x = (let y = x; (z => x)); (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = x; (z => x)); (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = x; (z => x)); (0 0)) //│ ╙── ^ //│ res: error (let x = {u: 0}; (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {u: 0}; (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {u: 0}; (0 0)) //│ ╙── ^ //│ res: error (let x = {u: add}; (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {u: add}; (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {u: add}; (0 0)) //│ ╙── ^ //│ res: error (let rec x = {u: x}; (0 0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = {u: x}; (0 0)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = {u: x}; (0 0)) //│ ╙── ^ //│ res: error (x => (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (x => (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (x => (0 add)) //│ ╙── ^ //│ res: anything -> error (let x = 0; (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = 0; (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = 0; (0 add)) //│ ╙── ^ //│ res: error (let x = add; (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = add; (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = add; (0 add)) //│ ╙── ^ //│ res: error (let rec x = x; (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = x; (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = x; (0 add)) //│ ╙── ^ //│ res: error (let x = 0.v; (0 add)) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; (0 add)) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; (0 add)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = 0.v; (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = 0.v; (0 add)) //│ ╙── ^ //│ res: error (let x = add.v; (0 add)) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; (0 add)) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; (0 add)) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = add.v; (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = add.v; (0 add)) //│ ╙── ^ //│ res: error (let rec x = x.v; (0 add)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = x.v; (0 add)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = x.v; (0 add)) //│ ╙── ^ //│ res: error (x => (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (x => (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (x => (0 x)) //│ ╙── ^ //│ res: anything -> error (let x = 0; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = 0; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = 0; (0 x)) //│ ╙── ^ //│ res: error (let x = add; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = add; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = add; (0 x)) //│ ╙── ^ //│ res: error (let rec x = x; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = x; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = x; (0 x)) //│ ╙── ^ //│ res: error (let x = (0 0); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 0); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 0); (0 x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 0); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 0); (0 x)) //│ ╙── ^ //│ res: error (let x = (0 add); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 add); (0 x)) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 add); (0 x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 add); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 add); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (0 x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 x); (0 x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 x); (0 x)) //│ ╙── ^ //│ res: error (let x = (add 0); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (add 0); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (add 0); (0 x)) //│ ╙── ^ //│ res: error (let x = (add add); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (add add); (0 x)) //│ ║ ^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: (let x = (add add); (0 x)) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (add add); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (add add); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (add x); (0 x)) //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.+1: (let rec x = (add x); (0 x)) //│ ║ ^^^^^^^ //│ ╟── application of type `int -> int` is not an instance of type `int` //│ ║ l.+1: (let rec x = (add x); (0 x)) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.+1: (let rec x = (add x); (0 x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (add x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (add x); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (x 0); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (x 0); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (x 0); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (x add); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (x add); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (x add); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (x x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (x x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (x x); (0 x)) //│ ╙── ^ //│ res: error (let x = (y => 0); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => 0); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => 0); (0 x)) //│ ╙── ^ //│ res: error (let x = (y => add); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => add); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => add); (0 x)) //│ ╙── ^ //│ res: error (let x = (y => y); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (y => y); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (y => y); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (y => x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (y => x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (y => x); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (let y = 0; x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = 0; x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = 0; x); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (let y = add; x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = add; x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = add; x); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (let rec y = y; x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let rec y = y; x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let rec y = y; x); (0 x)) //│ ╙── ^ //│ res: error (let rec x = (let y = x; x); (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (let y = x; x); (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (let y = x; x); (0 x)) //│ ╙── ^ //│ res: error (let x = {u: 0}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {u: 0}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {u: 0}; (0 x)) //│ ╙── ^ //│ res: error (let x = {u: add}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {u: add}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {u: add}; (0 x)) //│ ╙── ^ //│ res: error (let x = {u: add, v: 0}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {u: add, v: 0}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {u: add, v: 0}; (0 x)) //│ ╙── ^ //│ res: error (let x = {u: add, v: add}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {u: add, v: add}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {u: add, v: add}; (0 x)) //│ ╙── ^ //│ res: error (let rec x = {u: add, v: x}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = {u: add, v: x}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = {u: add, v: x}; (0 x)) //│ ╙── ^ //│ res: error (let rec x = {u: x}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = {u: x}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = {u: x}; (0 x)) //│ ╙── ^ //│ res: error (let x = 0.v; (0 x)) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; (0 x)) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; (0 x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = 0.v; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = 0.v; (0 x)) //│ ╙── ^ //│ res: error (let x = add.v; (0 x)) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; (0 x)) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; (0 x)) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = add.v; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = add.v; (0 x)) //│ ╙── ^ //│ res: error (let rec x = x.v; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = x.v; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = x.v; (0 x)) //│ ╙── ^ //│ res: error (let x = {v: 0}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {v: 0}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {v: 0}; (0 x)) //│ ╙── ^ //│ res: error (let x = {v: add}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = {v: add}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = {v: add}; (0 x)) //│ ╙── ^ //│ res: error (let rec x = {v: x}; (0 x)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = {v: x}; (0 x)) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = {v: x}; (0 x)) //│ ╙── ^ //│ res: error (x => {u: 0}) //│ res: anything -> {u: 0} (let x = 0; {u: 0}) //│ res: {u: 0} (let x = add; {u: 0}) //│ res: {u: 0} (let rec x = x; {u: 0}) //│ res: {u: 0} (let x = (0 0); {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 0); {u: 0}) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 0); {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let x = (0 add); {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 add); {u: 0}) //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 add); {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let rec x = (0 x); {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 x); {u: 0}) //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 x); {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let x = (0 {v: 0}); {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 {v: 0}); {u: 0}) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 {v: 0}); {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let x = (0 {v: add}); {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (0 {v: add}); {u: 0}) //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let x = (0 {v: add}); {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let rec x = (0 {v: x}); {u: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let rec x = (0 {v: x}); {u: 0}) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: (let rec x = (0 {v: x}); {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let x = 0.u; {u: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; {u: 0}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u; {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let x = add.u; {u: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; {u: 0}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u; {u: 0}) //│ ╙── ^^^ //│ res: {u: 0} (let rec x = x.u; {u: 0}) //│ res: {u: 0} (let x = 0.u.u; {u: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u.u; {u: 0}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u.u; {u: 0}) //│ ╙── ^ //│ res: {u: 0} (let x = add.u.u; {u: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u.u; {u: 0}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u.u; {u: 0}) //│ ╙── ^^^ //│ res: {u: 0} (let rec x = x.u.u; {u: 0}) //│ res: {u: 0} (x => {u: 0, v: 0}) //│ res: anything -> {u: 0, v: 0} (let x = 0; {u: 0, v: 0}) //│ res: {u: 0, v: 0} (let x = add; {u: 0, v: 0}) //│ res: {u: 0, v: 0} (let rec x = x; {u: 0, v: 0}) //│ res: {u: 0, v: 0} (let x = (add 0); {u: 0, v: 0}) //│ res: {u: 0, v: 0} (let x = (add add); {u: 0, v: 0}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: (let x = (add add); {u: 0, v: 0}) //│ ║ ^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: (let x = (add add); {u: 0, v: 0}) //│ ╙── ^^^ //│ res: {u: 0, v: 0} (let rec x = (add x); {u: 0, v: 0}) //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.+1: (let rec x = (add x); {u: 0, v: 0}) //│ ║ ^^^^^^^ //│ ╟── application of type `int -> int` is not an instance of type `int` //│ ║ l.+1: (let rec x = (add x); {u: 0, v: 0}) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from argument: //│ ║ l.+1: (let rec x = (add x); {u: 0, v: 0}) //│ ╙── ^ //│ res: {u: 0, v: 0} (let x = 0.u; {u: 0, v: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; {u: 0, v: 0}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u; {u: 0, v: 0}) //│ ╙── ^ //│ res: {u: 0, v: 0} (let x = add.u; {u: 0, v: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; {u: 0, v: 0}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u; {u: 0, v: 0}) //│ ╙── ^^^ //│ res: {u: 0, v: 0} (let rec x = x.u; {u: 0, v: 0}) //│ res: {u: 0, v: 0} (x => {u: 0, v: add}) //│ res: anything -> {u: 0, v: int -> int -> int} (let x = 0; {u: 0, v: add}) //│ res: {u: 0, v: int -> int -> int} (let x = add; {u: 0, v: add}) //│ res: {u: 0, v: int -> int -> int} (let rec x = x; {u: 0, v: add}) //│ res: {u: 0, v: int -> int -> int} (x => {u: 0, v: x}) //│ res: 'a -> {u: 0, v: 'a} (let x = 0; {u: 0, v: x}) //│ res: {u: 0, v: 0} (let x = add; {u: 0, v: x}) //│ res: {u: 0, v: int -> int -> int} (let rec x = x; {u: 0, v: x}) //│ res: {u: 0, v: nothing} (let rec x = (x 0); {u: 0, v: x}) //│ res: {u: 0, v: nothing} (let rec x = (x add); {u: 0, v: x}) //│ res: {u: 0, v: nothing} (let rec x = (x x); {u: 0, v: x}) //│ res: {u: 0, v: nothing} (x => {u: add}) //│ res: anything -> {u: int -> int -> int} (let x = 0; {u: add}) //│ res: {u: int -> int -> int} (let x = add; {u: add}) //│ res: {u: int -> int -> int} (let rec x = x; {u: add}) //│ res: {u: int -> int -> int} (let x = (y => 0); {u: add}) //│ res: {u: int -> int -> int} (let x = (let y = 0; 0); {u: add}) //│ res: {u: int -> int -> int} (let x = (let y = add; 0); {u: add}) //│ res: {u: int -> int -> int} (let x = (let rec y = y; 0); {u: add}) //│ res: {u: int -> int -> int} (let rec x = (let y = x; 0); {u: add}) //│ res: {u: int -> int -> int} (let x = (y => add); {u: add}) //│ res: {u: int -> int -> int} (let x = (y => y); {u: add}) //│ res: {u: int -> int -> int} (let rec x = (y => x); {u: add}) //│ res: {u: int -> int -> int} (let x = 0.v; {u: add}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; {u: add}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; {u: add}) //│ ╙── ^ //│ res: {u: int -> int -> int} (let x = add.v; {u: add}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; {u: add}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; {u: add}) //│ ╙── ^^^ //│ res: {u: int -> int -> int} (let rec x = x.v; {u: add}) //│ res: {u: int -> int -> int} (let x = {v: 0}; {u: add}) //│ res: {u: int -> int -> int} (let x = {v: add}; {u: add}) //│ res: {u: int -> int -> int} (let rec x = {v: x}; {u: add}) //│ res: {u: int -> int -> int} (x => {u: x}) //│ res: 'a -> {u: 'a} (let x = 0; {u: x}) //│ res: {u: 0} (let x = add; {u: x}) //│ res: {u: int -> int -> int} (let rec x = x; {u: x}) //│ res: {u: nothing} (let x = {v: 0}; {u: x}) //│ res: {u: {v: 0}} (let x = {v: add}; {u: x}) //│ res: {u: {v: int -> int -> int}} (let rec x = {v: x}; {u: x}) //│ res: {u: forall 'x. 'x} //│ where //│ 'x :> {v: 'x} (x => {u: x, v: 0}) //│ res: 'a -> {u: 'a, v: 0} (let x = 0; {u: x, v: 0}) //│ res: {u: 0, v: 0} (let x = add; {u: x, v: 0}) //│ res: {u: int -> int -> int, v: 0} (let rec x = x; {u: x, v: 0}) //│ res: {u: nothing, v: 0} (let x = 0.v; {u: x, v: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; {u: x, v: 0}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; {u: x, v: 0}) //│ ╙── ^ //│ res: {u: error, v: 0} (let x = add.v; {u: x, v: 0}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; {u: x, v: 0}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; {u: x, v: 0}) //│ ╙── ^^^ //│ res: {u: error, v: 0} (let rec x = x.v; {u: x, v: 0}) //│ res: {u: nothing, v: 0} (x => {u: x, v: add}) //│ res: 'a -> {u: 'a, v: int -> int -> int} (let x = 0; {u: x, v: add}) //│ res: {u: 0, v: int -> int -> int} (let x = add; {u: x, v: add}) //│ res: {u: int -> int -> int, v: int -> int -> int} (let rec x = x; {u: x, v: add}) //│ res: {u: nothing, v: int -> int -> int} (let x = 0.u; {u: x, v: add}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; {u: x, v: add}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u; {u: x, v: add}) //│ ╙── ^ //│ res: {u: error, v: int -> int -> int} (let x = add.u; {u: x, v: add}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; {u: x, v: add}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u; {u: x, v: add}) //│ ╙── ^^^ //│ res: {u: error, v: int -> int -> int} (let rec x = x.u; {u: x, v: add}) //│ res: {u: nothing, v: int -> int -> int} (let x = 0.v; {u: x, v: add}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; {u: x, v: add}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; {u: x, v: add}) //│ ╙── ^ //│ res: {u: error, v: int -> int -> int} (let x = add.v; {u: x, v: add}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; {u: x, v: add}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; {u: x, v: add}) //│ ╙── ^^^ //│ res: {u: error, v: int -> int -> int} (let rec x = x.v; {u: x, v: add}) //│ res: {u: nothing, v: int -> int -> int} (x => {u: x, v: x}) //│ res: 'a -> {u: 'a, v: 'a} (let x = 0; {u: x, v: x}) //│ res: {u: 0, v: 0} (let x = add; {u: x, v: x}) //│ res: {u: int -> int -> int, v: int -> int -> int} (let rec x = x; {u: x, v: x}) //│ res: {u: nothing, v: nothing} (let x = {u: 0}; {u: x, v: x}) //│ res: {u: {u: 0}, v: {u: 0}} (let x = {u: add}; {u: x, v: x}) //│ res: {u: {u: int -> int -> int}, v: {u: int -> int -> int}} (let rec x = {u: x}; {u: x, v: x}) //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x :> {u: 'x} (let rec x = {u: x, v: 0}; {u: x, v: x}) //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x :> {u: 'x, v: 0} (let rec x = {u: x, v: add}; {u: x, v: x}) //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x :> {u: 'x, v: int -> int -> int} (let rec x = {u: x, v: x}; {u: x, v: x}) //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x :> {u: 'x, v: 'x} (let x = {v: 0}; {u: x, v: x}) //│ res: {u: {v: 0}, v: {v: 0}} (let x = {v: add}; {u: x, v: x}) //│ res: {u: {v: int -> int -> int}, v: {v: int -> int -> int}} (let rec x = {v: x}; {u: x, v: x}) //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x :> {v: 'x} (x => {u: 0.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (x => {u: 0.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (x => {u: 0.v}) //│ ╙── ^ //│ res: anything -> {u: error} (let x = 0; {u: 0.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0; {u: 0.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0; {u: 0.v}) //│ ╙── ^ //│ res: {u: error} (let x = add; {u: 0.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; {u: 0.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = add; {u: 0.v}) //│ ╙── ^ //│ res: {u: error} (let rec x = x; {u: 0.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x; {u: 0.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let rec x = x; {u: 0.v}) //│ ╙── ^ //│ res: {u: error} (x => {u: add.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (x => {u: add.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (x => {u: add.v}) //│ ╙── ^^^ //│ res: anything -> {u: error} (let x = 0; {u: add.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0; {u: add.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = 0; {u: add.v}) //│ ╙── ^^^ //│ res: {u: error} (let x = add; {u: add.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; {u: add.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add; {u: add.v}) //│ ╙── ^^^ //│ res: {u: error} (let rec x = x; {u: add.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x; {u: add.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let rec x = x; {u: add.v}) //│ ╙── ^^^ //│ res: {u: error} (x => {u: x.v}) //│ res: {v: 'v} -> {u: 'v} (let x = 0; {u: x.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0; {u: x.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0; {u: x.v}) //│ ║ ^ //│ ╟── but it flows into reference with expected type `{v: ?v}` //│ ║ l.+1: (let x = 0; {u: x.v}) //│ ╙── ^ //│ res: {u: error} (let x = add; {u: x.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; {u: x.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not match type `{v: ?v}` //│ ║ l.+1: (let x = add; {u: x.v}) //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `{v: ?v}` //│ ║ l.+1: (let x = add; {u: x.v}) //│ ╙── ^ //│ res: {u: error} (let rec x = x; {u: x.v}) //│ res: {u: nothing} (let x = 0.v; {u: x.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; {u: x.v}) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; {u: x.v}) //│ ╙── ^ //│ res: {u: error} (let x = add.v; {u: x.v}) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; {u: x.v}) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; {u: x.v}) //│ ╙── ^^^ //│ res: {u: error} (let rec x = x.v; {u: x.v}) //│ res: {u: nothing} (let x = {v: 0}; {u: x.v}) //│ res: {u: 0} (let x = {v: add}; {u: x.v}) //│ res: {u: int -> int -> int} (let rec x = {v: x}; {u: x.v}) //│ res: {u: 'x} //│ where //│ 'x :> {v: 'x} (let x = {v: (y => 0)}; {u: x.v}) //│ res: {u: anything -> 0} (let x = {v: (y => add)}; {u: x.v}) //│ res: {u: anything -> int -> int -> int} (let x = {v: (y => y)}; {u: x.v}) //│ res: {u: forall 'a. 'a -> 'a} (let rec x = {v: (y => x)}; {u: x.v}) //│ res: {u: anything -> 'x} //│ where //│ 'x :> {v: anything -> 'x} (x => 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (x => 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (x => 0.v) //│ ╙── ^ //│ res: anything -> error (let x = 0; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0; 0.v) //│ ╙── ^ //│ res: error (let x = add; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = add; 0.v) //│ ╙── ^ //│ res: error (let rec x = x; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let rec x = x; 0.v) //│ ╙── ^ //│ res: error (let x = 0.u; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: (let x = 0.u; 0.v) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.u; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.u; 0.v) //│ ╙── ^ //│ res: error (let x = add.u; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; 0.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: (let x = add.u; 0.v) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.u; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = add.u; 0.v) //│ ╙── ^ //│ res: error (let rec x = x.u; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x.u; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let rec x = x.u; 0.v) //│ ╙── ^ //│ res: error (let x = 0.v; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; 0.v) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; 0.v) //│ ╙── ^ //│ res: error (let x = add.v; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; 0.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; 0.v) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = add.v; 0.v) //│ ╙── ^ //│ res: error (let rec x = x.v; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x.v; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let rec x = x.v; 0.v) //│ ╙── ^ //│ res: error (let x = {v: 0}; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: 0}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = {v: 0}; 0.v) //│ ╙── ^ //│ res: error (let x = {v: add}; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: add}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = {v: add}; 0.v) //│ ╙── ^ //│ res: error (let rec x = {v: x}; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = {v: x}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let rec x = {v: x}; 0.v) //│ ╙── ^ //│ res: error (let x = {v: 0.v}; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: 0.v}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = {v: 0.v}; 0.v) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: 0.v}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = {v: 0.v}; 0.v) //│ ╙── ^ //│ res: error (let x = {v: add.v}; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: add.v}; 0.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = {v: add.v}; 0.v) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {v: add.v}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = {v: add.v}; 0.v) //│ ╙── ^ //│ res: error (let rec x = {v: x.v}; 0.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = {v: x.v}; 0.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let rec x = {v: x.v}; 0.v) //│ ╙── ^ //│ res: error (x => add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (x => add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (x => add.v) //│ ╙── ^^^ //│ res: anything -> error (let x = 0; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = 0; add.v) //│ ╙── ^^^ //│ res: error (let x = add; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add; add.v) //│ ╙── ^^^ //│ res: error (let rec x = x; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let rec x = x; add.v) //│ ╙── ^^^ //│ res: error (let x = {u: 0}; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {u: 0}; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = {u: 0}; add.v) //│ ╙── ^^^ //│ res: error (let x = {u: add}; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {u: add}; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = {u: add}; add.v) //│ ╙── ^^^ //│ res: error (let rec x = {u: x}; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = {u: x}; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let rec x = {u: x}; add.v) //│ ╙── ^^^ //│ res: error (let x = {u: {v: 0}}; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {u: {v: 0}}; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = {u: {v: 0}}; add.v) //│ ╙── ^^^ //│ res: error (let x = {u: {v: add}}; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = {u: {v: add}}; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = {u: {v: add}}; add.v) //│ ╙── ^^^ //│ res: error (let rec x = {u: {v: x}}; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = {u: {v: x}}; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let rec x = {u: {v: x}}; add.v) //│ ╙── ^^^ //│ res: error (let x = 0.v; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; add.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0.v; add.v) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0.v; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = 0.v; add.v) //│ ╙── ^^^ //│ res: error (let x = add.v; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; add.v) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add.v; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let x = add.v; add.v) //│ ╙── ^^^ //│ res: error (let rec x = x.v; add.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let rec x = x.v; add.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: (let rec x = x.v; add.v) //│ ╙── ^^^ //│ res: error (x => x.v) //│ res: {v: 'v} -> 'v (let x = 0; x.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = 0; x.v) //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: (let x = 0; x.v) //│ ║ ^ //│ ╟── but it flows into reference with expected type `{v: ?v}` //│ ║ l.+1: (let x = 0; x.v) //│ ╙── ^ //│ res: error (let x = add; x.v) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: (let x = add; x.v) //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not match type `{v: ?v}` //│ ║ l.+1: (let x = add; x.v) //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `{v: ?v}` //│ ║ l.+1: (let x = add; x.v) //│ ╙── ^ //│ res: error (let rec x = x; x.v) //│ res: nothing (let x = {v: 0}; x.v) //│ res: 0 (let x = {v: add}; x.v) //│ res: int -> int -> int (let rec x = {v: x}; x.v) //│ res: 'x //│ where //│ 'x :> {v: 'x} (let x = {v: {v: 0}}; x.v) //│ res: {v: 0} (let x = {v: {v: add}}; x.v) //│ res: {v: int -> int -> int} (let rec x = {v: {v: x}}; x.v) //│ res: {v: 'x} //│ where //│ 'x :> {v: {v: 'x}} 0.u //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: 0.u //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: 0.u //│ ╙── ^ //│ res: error add.u //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: add.u //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: add.u //│ ╙── ^^^ //│ res: error 0.v.u //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: 0.v.u //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: 0.v.u //│ ╙── ^ //│ res: error add.v.u //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: add.v.u //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: add.v.u //│ ╙── ^^^ //│ res: error {v: 0}.u //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {v: 0}.u //│ ║ ^^ //│ ╟── record of type `{v: 0}` does not have field 'u' //│ ║ l.+1: {v: 0}.u //│ ╙── ^^^^^^ //│ res: error {v: add}.u //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {v: add}.u //│ ║ ^^ //│ ╟── record of type `{v: int -> int -> int}` does not have field 'u' //│ ║ l.+1: {v: add}.u //│ ╙── ^^^^^^^^ //│ res: error {u: 0} //│ res: {u: 0} {u: 0, v: 0} //│ res: {u: 0, v: 0} {u: 0, v: add} //│ res: {u: 0, v: int -> int -> int} {u: 0, v: (x => 0)} //│ res: {u: 0, v: anything -> 0} {u: 0, v: (let x = 0; 0)} //│ res: {u: 0, v: 0} {u: 0, v: (let x = add; 0)} //│ res: {u: 0, v: 0} {u: 0, v: (let rec x = x; 0)} //│ res: {u: 0, v: 0} {u: 0, v: (let x = 0.u; 0)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: 0, v: (let x = 0.u; 0)} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {u: 0, v: (let x = 0.u; 0)} //│ ╙── ^ //│ res: {u: 0, v: 0} {u: 0, v: (let x = add.u; 0)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: 0, v: (let x = add.u; 0)} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: {u: 0, v: (let x = add.u; 0)} //│ ╙── ^^^ //│ res: {u: 0, v: 0} {u: 0, v: (let rec x = x.u; 0)} //│ res: {u: 0, v: 0} {u: 0, v: (x => add)} //│ res: {u: 0, v: anything -> int -> int -> int} {u: 0, v: (x => x)} //│ res: {u: 0, v: forall 'a. 'a -> 'a} {u: 0, v: {u: 0}} //│ res: {u: 0, v: {u: 0}} {u: 0, v: {u: 0, v: 0}} //│ res: {u: 0, v: {u: 0, v: 0}} {u: 0, v: {u: 0, v: add}} //│ res: {u: 0, v: {u: 0, v: int -> int -> int}} {u: 0, v: {u: add}} //│ res: {u: 0, v: {u: int -> int -> int}} {u: 0, v: {v: 0}} //│ res: {u: 0, v: {v: 0}} {u: 0, v: {v: add}} //│ res: {u: 0, v: {v: int -> int -> int}} {u: add} //│ res: {u: int -> int -> int} {u: add, v: 0} //│ res: {u: int -> int -> int, v: 0} {u: add, v: add} //│ res: {u: int -> int -> int, v: int -> int -> int} {u: add, v: {u: 0}} //│ res: {u: int -> int -> int, v: {u: 0}} {u: add, v: {u: add}} //│ res: {u: int -> int -> int, v: {u: int -> int -> int}} {u: add, v: {u: add, v: 0}} //│ res: {u: int -> int -> int, v: {u: int -> int -> int, v: 0}} {u: add, v: {u: add, v: add}} //│ res: {u: int -> int -> int, v: {u: int -> int -> int, v: int -> int -> int}} {u: add, v: {u: (x => 0)}} //│ res: {u: int -> int -> int, v: {u: anything -> 0}} {u: add, v: {u: (x => add)}} //│ res: {u: int -> int -> int, v: {u: anything -> int -> int -> int}} {u: add, v: {u: (x => add), v: 0}} //│ res: {u: int -> int -> int, v: {u: anything -> int -> int -> int, v: 0}} {u: add, v: {u: (x => add), v: add}} //│ res: {u: int -> int -> int, v: {u: anything -> int -> int -> int, v: int -> int -> int}} {u: add, v: {u: (x => x)}} //│ res: {u: int -> int -> int, v: {u: forall 'a. 'a -> 'a}} {u: add, v: {u: {v: 0}}} //│ res: {u: int -> int -> int, v: {u: {v: 0}}} {u: add, v: {u: {v: add}}} //│ res: {u: int -> int -> int, v: {u: {v: int -> int -> int}}} {u: add, v: 0.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: 0.v} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: {u: add, v: 0.v} //│ ╙── ^ //│ res: {u: int -> int -> int, v: error} {u: add, v: add.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: add.v} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: {u: add, v: add.v} //│ ╙── ^^^ //│ res: {u: int -> int -> int, v: error} {u: add, v: (x => 0).v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: (x => 0).v} //│ ║ ^^ //│ ╟── function of type `?a -> 0` does not have field 'v' //│ ║ l.+1: {u: add, v: (x => 0).v} //│ ║ ^^^^^^ //│ ╟── but it flows into receiver with expected type `{v: ?v}` //│ ║ l.+1: {u: add, v: (x => 0).v} //│ ╙── ^^^^^^^^ //│ res: {u: int -> int -> int, v: error} {u: add, v: (x => add).v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: (x => add).v} //│ ║ ^^ //│ ╟── function of type `?a -> int -> int -> int` does not have field 'v' //│ ║ l.+1: {u: add, v: (x => add).v} //│ ║ ^^^^^^^^ //│ ╟── but it flows into receiver with expected type `{v: ?v}` //│ ║ l.+1: {u: add, v: (x => add).v} //│ ╙── ^^^^^^^^^^ //│ res: {u: int -> int -> int, v: error} {u: add, v: (x => x).v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: (x => x).v} //│ ║ ^^ //│ ╟── function of type `?a -> ?a` does not have field 'v' //│ ║ l.+1: {u: add, v: (x => x).v} //│ ║ ^^^^^^ //│ ╟── but it flows into receiver with expected type `{v: ?v}` //│ ║ l.+1: {u: add, v: (x => x).v} //│ ╙── ^^^^^^^^ //│ res: {u: int -> int -> int, v: error} {u: add, v: {u: 0}.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: {u: 0}.v} //│ ║ ^^ //│ ╟── record of type `{u: 0}` does not have field 'v' //│ ║ l.+1: {u: add, v: {u: 0}.v} //│ ╙── ^^^^^^ //│ res: {u: int -> int -> int, v: error} {u: add, v: {u: add}.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: add, v: {u: add}.v} //│ ║ ^^ //│ ╟── record of type `{u: int -> int -> int}` does not have field 'v' //│ ║ l.+1: {u: add, v: {u: add}.v} //│ ╙── ^^^^^^^^ //│ res: {u: int -> int -> int, v: error} {u: add, v: {v: 0}} //│ res: {u: int -> int -> int, v: {v: 0}} {u: add, v: {v: add}} //│ res: {u: int -> int -> int, v: {v: int -> int -> int}} {u: {u: 0}} //│ res: {u: {u: 0}} {u: {u: 0}, v: 0} //│ res: {u: {u: 0}, v: 0} {u: {u: 0}, v: add} //│ res: {u: {u: 0}, v: int -> int -> int} {u: {u: 0}, v: (add 0)} //│ res: {u: {u: 0}, v: int -> int} {u: {u: 0}, v: (add add)} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: {u: {u: 0}, v: (add add)} //│ ║ ^^^^^^^ //│ ╟── reference of type `int -> int -> int` is not an instance of type `int` //│ ║ l.+1: {u: {u: 0}, v: (add add)} //│ ╙── ^^^ //│ res: {u: {u: 0}, v: error | int -> int} {u: {u: 0}, v: (add {v: 0})} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: {u: {u: 0}, v: (add {v: 0})} //│ ║ ^^^^^^^^^^ //│ ╟── record of type `{v: 0}` is not an instance of type `int` //│ ║ l.+1: {u: {u: 0}, v: (add {v: 0})} //│ ╙── ^^^^^^ //│ res: {u: {u: 0}, v: error | int -> int} {u: {u: 0}, v: (add {v: add})} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: {u: {u: 0}, v: (add {v: add})} //│ ║ ^^^^^^^^^^^^ //│ ╟── record of type `{v: int -> int -> int}` is not an instance of type `int` //│ ║ l.+1: {u: {u: 0}, v: (add {v: add})} //│ ╙── ^^^^^^^^ //│ res: {u: {u: 0}, v: error | int -> int} {u: {u: 0}, v: (x => 0)} //│ res: {u: {u: 0}, v: anything -> 0} {u: {u: 0}, v: (x => add)} //│ res: {u: {u: 0}, v: anything -> int -> int -> int} {u: {u: 0}, v: (x => x)} //│ res: {u: {u: 0}, v: forall 'a. 'a -> 'a} {u: {u: 0}, v: (x => 0.u)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: (x => 0.u)} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {u: {u: 0}, v: (x => 0.u)} //│ ╙── ^ //│ res: {u: {u: 0}, v: anything -> error} {u: {u: 0}, v: (let x = 0; 0.u)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: (let x = 0; 0.u)} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {u: {u: 0}, v: (let x = 0; 0.u)} //│ ╙── ^ //│ res: {u: {u: 0}, v: error} {u: {u: 0}, v: (let x = add; 0.u)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: (let x = add; 0.u)} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {u: {u: 0}, v: (let x = add; 0.u)} //│ ╙── ^ //│ res: {u: {u: 0}, v: error} {u: {u: 0}, v: (let rec x = x; 0.u)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: (let rec x = x; 0.u)} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {u: {u: 0}, v: (let rec x = x; 0.u)} //│ ╙── ^ //│ res: {u: {u: 0}, v: error} {u: {u: 0}, v: (x => add.u)} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: (x => add.u)} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: {u: {u: 0}, v: (x => add.u)} //│ ╙── ^^^ //│ res: {u: {u: 0}, v: anything -> error} {u: {u: 0}, v: (x => x.u)} //│ res: {u: {u: 0}, v: forall 'u. {u: 'u} -> 'u} {u: {u: 0}, v: 0.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: 0.v} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: {u: {u: 0}, v: 0.v} //│ ╙── ^ //│ res: {u: {u: 0}, v: error} {u: {u: 0}, v: add.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {u: 0}, v: add.v} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: {u: {u: 0}, v: add.v} //│ ╙── ^^^ //│ res: {u: {u: 0}, v: error} {u: {u: add}} //│ res: {u: {u: int -> int -> int}} {u: {u: add}, v: 0} //│ res: {u: {u: int -> int -> int}, v: 0} {u: {u: add}, v: add} //│ res: {u: {u: int -> int -> int}, v: int -> int -> int} {u: {u: add}, v: (x => 0)} //│ res: {u: {u: int -> int -> int}, v: anything -> 0} {u: {u: add}, v: (x => add)} //│ res: {u: {u: int -> int -> int}, v: anything -> int -> int -> int} {u: {u: add}, v: (let x = 0; add)} //│ res: {u: {u: int -> int -> int}, v: int -> int -> int} {u: {u: add}, v: (let x = add; add)} //│ res: {u: {u: int -> int -> int}, v: int -> int -> int} {u: {u: add}, v: (let rec x = x; add)} //│ res: {u: {u: int -> int -> int}, v: int -> int -> int} {u: {u: add}, v: (x => x)} //│ res: {u: {u: int -> int -> int}, v: forall 'a. 'a -> 'a} {u: {v: 0}} //│ res: {u: {v: 0}} {u: {v: 0}, v: 0} //│ res: {u: {v: 0}, v: 0} {u: {v: 0}, v: add} //│ res: {u: {v: 0}, v: int -> int -> int} {u: {v: 0}, v: (0 0)} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: {u: {v: 0}, v: (0 0)} //│ ║ ^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: {u: {v: 0}, v: (0 0)} //│ ╙── ^ //│ res: {u: {v: 0}, v: error} {u: {v: 0}, v: (0 add)} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: {u: {v: 0}, v: (0 add)} //│ ║ ^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.+1: {u: {v: 0}, v: (0 add)} //│ ╙── ^ //│ res: {u: {v: 0}, v: error} {u: {v: 0}, v: (x => 0)} //│ res: {u: {v: 0}, v: anything -> 0} {u: {v: 0}, v: (let x = 0; 0)} //│ res: {u: {v: 0}, v: 0} {u: {v: 0}, v: (let x = add; 0)} //│ res: {u: {v: 0}, v: 0} {u: {v: 0}, v: (let rec x = x; 0)} //│ res: {u: {v: 0}, v: 0} {u: {v: 0}, v: (x => add)} //│ res: {u: {v: 0}, v: anything -> int -> int -> int} {u: {v: 0}, v: (let x = 0; add)} //│ res: {u: {v: 0}, v: int -> int -> int} {u: {v: 0}, v: (let x = add; add)} //│ res: {u: {v: 0}, v: int -> int -> int} {u: {v: 0}, v: (let rec x = x; add)} //│ res: {u: {v: 0}, v: int -> int -> int} {u: {v: 0}, v: (x => x)} //│ res: {u: {v: 0}, v: forall 'a. 'a -> 'a} {u: {v: add}} //│ res: {u: {v: int -> int -> int}} {u: {v: add}, v: 0} //│ res: {u: {v: int -> int -> int}, v: 0} {u: {v: add}, v: add} //│ res: {u: {v: int -> int -> int}, v: int -> int -> int} {u: {v: add}, v: 0.u} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {v: add}, v: 0.u} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {u: {v: add}, v: 0.u} //│ ╙── ^ //│ res: {u: {v: int -> int -> int}, v: error} {u: {v: add}, v: add.u} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {v: add}, v: add.u} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: {u: {v: add}, v: add.u} //│ ╙── ^^^ //│ res: {u: {v: int -> int -> int}, v: error} {u: {v: add}, v: 0.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {v: add}, v: 0.v} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: {u: {v: add}, v: 0.v} //│ ╙── ^ //│ res: {u: {v: int -> int -> int}, v: error} {u: {v: add}, v: add.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {u: {v: add}, v: add.v} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: {u: {v: add}, v: add.v} //│ ╙── ^^^ //│ res: {u: {v: int -> int -> int}, v: error} {u: {v: add}, v: {v: 0}} //│ res: {u: {v: int -> int -> int}, v: {v: 0}} {u: {v: add}, v: {v: add}} //│ res: {u: {v: int -> int -> int}, v: {v: int -> int -> int}} 0.v //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: 0.v //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: 0.v //│ ╙── ^ //│ res: error add.v //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: add.v //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: add.v //│ ╙── ^^^ //│ res: error {v: 0} //│ res: {v: 0} {v: add} //│ res: {v: int -> int -> int} {v: 0.u} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {v: 0.u} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'u' //│ ║ l.+1: {v: 0.u} //│ ╙── ^ //│ res: {v: error} {v: add.u} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {v: add.u} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'u' //│ ║ l.+1: {v: add.u} //│ ╙── ^^^ //│ res: {v: error} {v: 0.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {v: 0.v} //│ ║ ^^ //│ ╟── integer literal of type `0` does not have field 'v' //│ ║ l.+1: {v: 0.v} //│ ╙── ^ //│ res: {v: error} {v: add.v} //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+1: {v: add.v} //│ ║ ^^ //│ ╟── reference of type `int -> int -> int` does not have field 'v' //│ ║ l.+1: {v: add.v} //│ ╙── ^^^ //│ res: {v: error} {v: {v: 0}} //│ res: {v: {v: 0}} {v: {v: add}} //│ res: {v: {v: int -> int -> int}} // Number of expressions generated: 499 // Program sizes: // 1: 2 // 2: 8 // 3: 34 // 4: 62 // 5: 123 // 6: 128 // 7: 99 // 8: 22 // 9: 21 ================================================ FILE: shared/src/test/diff/mlf-examples/ex_casparticuliers.mls ================================================ :NoRecursiveTypes :DistributeForalls // (*** L'annotation est n�cessaire sur z, mais z n'est utilis� qu'une seule fois. ***) // type sid = ['a] 'a -> 'a type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let t (z:sid) = ( (fun a -> z) : ['a,'b] 'b -> 'a -> 'a ) def t (z: Sid) = (fun a -> z) : forall 'a 'b. 'b -> 'a -> 'a //│ t: Sid -> (forall 'a. anything -> 'a -> 'a) //│ = [Function: t] // (* Une seule instance, mais polymorphe. *) // let t z = ( (fun a -> z) : ['a,'b] 'b -> 'a -> 'a );; def t z = (fun a -> z) : forall 'a 'b. 'b -> 'a -> 'a //│ t: (??a -> ??a0) -> anything -> 'a -> 'a //│ = [Function: t1] def t z = fun a -> z //│ t: 'a -> anything -> 'a //│ = [Function: t2] // (*** Ne type pas avec les value-restriction. Emb�tant. ***) // type Int = ['a] ('a -> 'a) -> ('a -> 'a) // ;; type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias ChurchInt // let zero f x = x // ;; def zero f x = x //│ zero: anything -> 'a -> 'a //│ = [Function: zero] def zero_ty: ChurchInt //│ zero_ty: ChurchInt //│ = // let succ (n:Int) = fun f x -> f (n f x) // ;; def succ_ty: ChurchInt -> ChurchInt def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) def succ' n = fun f -> fun x -> f (n f x) //│ succ_ty: ChurchInt -> ChurchInt //│ = //│ succ: ChurchInt -> ('a -> ('a & 'b)) -> 'a -> 'b //│ = [Function: succ] //│ succ': ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: succ$] // * Note: without constrained types we don't get the principal type of succ' (at least assuming no distrib.) succ_ty = succ //│ ChurchInt -> ('a -> ('a & 'b)) -> 'a -> 'b //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ = [Function: succ] // * Note that this equires distributivity (see Church encoding test files) succ_ty = succ' //│ ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ = [Function: succ$] succ' //│ res: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: succ$] // * Legit error: (bad bounds) :e succ' {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.77: succ' {} //│ ║ ^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.77: succ' {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ res: error | (nothing -> 'a) -> anything -> 'a //│ = [Function (anonymous)] :e succ' {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.94: succ' {} {} //│ ║ ^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.94: succ' {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.94: succ' {} {} //│ ║ ^^^^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.94: succ' {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ║ ^^^^^^^^^ //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ res: error | anything -> nothing //│ = [Function (anonymous)] :e succ' {} {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.123: succ' {} {} {} //│ ║ ^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.123: succ' {} {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.123: succ' {} {} {} //│ ║ ^^^^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.123: succ' {} {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ║ ^^^^^^^^^ //│ ╟── from reference: //│ ║ l.48: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function fun x -> succ' x x x x x x x x x //│ res: ('a -> 'b -> 'b -> 'b -> 'b -> 'b -> 'b -> 'c & 'b -> 'b -> 'a & 'b) -> 'c //│ = [Function: res] // let rec to_church n = // if n = 0 then zero // else succ (to_church (n-1)) // ;; // * Note: we exercise many variations of `to_church` with/without various annotations def to_church_ty: int -> ChurchInt //│ to_church_ty: int -> ChurchInt //│ = rec def to_church_1 n = if n == 0 then zero else succ (to_church_1 (n - 1)) //│ to_church_1: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_church_1] rec def to_church_1_t n = if n == 0 then zero else succ_ty (to_church_1_t (n - 1)) //│ to_church_1_t: int -> (anything -> 'a -> 'a | ChurchInt) //│ = [Function: to_church_1_t] rec def to_church_1_tt n = if n == 0 then zero_ty else succ_ty (to_church_1_tt (n - 1)) //│ to_church_1_tt: int -> ChurchInt //│ = //│ zero_ty is not implemented // def succ: (forall 'N. ('N -> 'N) -> ('N -> 'N)) -> (forall 'M. ('M -> 'M) -> ('M -> 'M)) // * === Digression: our own little weird meaningless tests === :ng // * (recursive non-function not supported in JS) rec def wat = succ wat //│ wat: ('a -> ('a & 'b)) -> 'a -> 'b rec def wat n = succ (wat n) //│ wat: anything -> ('a -> ('a & 'b)) -> 'a -> 'b //│ = [Function: wat] :e rec def wat n = succ wat n //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.203: rec def wat n = //│ ║ ^^^ //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.203: rec def wat n = //│ ║ ^^^ //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.203: rec def wat n = //│ ║ ^^^ //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.47: def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.203: rec def wat n = //│ ║ ^^^ //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.47: def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) //│ ╙── ^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.203: rec def wat n = //│ ║ ^^^ //│ ║ l.204: succ wat n //│ ║ ^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.204: succ wat n //│ ╙── ^^^^^^^^^^ //│ wat: (anything -> (??a & 'a)) -> (??a & 'a) -> anything //│ = [Function: wat1] // * Works with this option! :precise-rec-typing rec def wat n = succ wat n //│ wat: ('a -> ('a & 'b)) -> 'a -> 'b //│ = [Function: wat2] :ng rec def nani = succ' nani //│ nani: ('a -> 'a) -> anything -> 'a rec def wat_2 n = succ (wat_2 n) //│ wat_2: anything -> ('a -> ('a & 'b)) -> 'a -> 'b //│ = [Function: wat_2] def to_church_1_repro n = succ (to_church_ty n) //│ to_church_1_repro: int -> ('a -> ('a & 'b)) -> 'a -> 'b //│ = //│ to_church_ty is not implemented // * === /Digression === rec def to_church_1_st n = if n == 0 then zero else succ_ty (to_church_1_st (n - 1)) //│ to_church_1_st: int -> (anything -> 'a -> 'a | ChurchInt) //│ = [Function: to_church_1_st] rec def to_church_2 n = if n == 0 then zero else succ' (to_church_2 (n - 1)) //│ to_church_2: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_church_2] to_church_ty = to_church_2 //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_2] // (* Marche *) // let rec to_church n = // if n = 0 then fun f x -> x // else fun f x -> f ((to_church (n-1) : Int) f x) // ;; rec def to_church_3 n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church_3 (n - 1) : ChurchInt) f x) //│ to_church_3: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: to_church_3] // (* Ne Marche Pas *) // let rec to_church n = // if n = 0 then fun f x -> x // else (fun f x -> f ((to_church (n-1)) f x) : Int) // ;; // * Type checks for us, but not a subtype of to_church_ty (see below) rec def to_church_4 n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church_4 (n - 1)) f x) : ChurchInt //│ to_church_4: int -> ((ChurchInt | 'a) -> ChurchInt) -> 'a -> (ChurchInt | 'a) //│ = [Function: to_church_4] // (* La r�cursion est monomorphe. *) // let rec to_church n = // if n = 0 then fun f x -> x // else fun f x -> f ((to_church (n-1)) f x) // ;; rec def to_church_5 n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church_5 (n - 1)) f x) //│ to_church_5: int -> ('a -> 'b & 'a -> 'a) -> ('a & 'b) -> 'b //│ = [Function: to_church_5] // * Now checking whether the inferred types conform to the expected signature: to_church_ty = to_church_1 //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_1] to_church_ty = to_church_1_t //│ int -> (anything -> 'a -> 'a | ChurchInt) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_1_t] to_church_ty = to_church_1_tt //│ int -> ChurchInt //│ <: to_church_ty: //│ int -> ChurchInt //│ = //│ to_church_1_tt and zero_ty are not implemented to_church_ty = to_church_1_st //│ int -> (anything -> 'a -> 'a | ChurchInt) //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_1_st] to_church_ty = to_church_2 //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_2] to_church_ty = to_church_3 //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_3] :e to_church_ty = to_church_4 //│ int -> ((ChurchInt | 'a) -> ChurchInt) -> 'a -> (ChurchInt | 'a) //│ <: to_church_ty: //│ int -> ChurchInt //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.403: to_church_ty = to_church_4 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `('a -> 'a) -> 'a -> 'a` does not match type `'a0` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ = [Function: to_church_4] to_church_ty = to_church_5 //│ int -> ('a -> 'b & 'a -> 'a) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_5] // (* Echoue avec la value restriction. *) // let rec (to_church:int -> Int) n = // if n = 0 then fun f x -> x // else fun f x -> f ((to_church (n-1)) f x) // ;; rec def to_church_ty n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church_ty (n - 1)) f x) //│ int -> ('a -> 'b & 'a -> 'a) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_ty8] // * Note: same but without `rec`, picking up the to_church_ty annotation for rec calls def to_church_ty n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church_ty (n - 1)) f x) //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church_ty: //│ int -> ChurchInt //│ = [Function: to_church_ty9] // (* �a coince. *) // let tc n = (to_church n : Int);; def tc n = to_church_1 n : ChurchInt //│ tc: int -> ChurchInt //│ = [Function: tc] def tc n = to_church_ty n : ChurchInt //│ tc: int -> ChurchInt //│ = [Function: tc1] def tc n = to_church_2 n : ChurchInt //│ tc: int -> ChurchInt //│ = [Function: tc2] def tc n = to_church_3 n //│ tc: int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ = [Function: tc3] tc 1 id //│ res: 'a -> 'a //│ = [Function (anonymous)] tc: int -> ChurchInt //│ res: int -> ChurchInt //│ = [Function: tc3] def tc n = to_church_3 n : ChurchInt //│ tc: int -> ChurchInt //│ = [Function: tc4] // (* Duplicate with to_church1 ("Marche") above *) // (* Avec la value restriction : �a passe. *) // let rec to_church n = // if n = 0 then fun f x -> x // else fun f x -> f ((to_church (n-1) : Int) f x) // (*** Les annotations de type ne commutent pas. ***) // type sid = ['a] 'a -> 'a // type z = ['a,'b] ('a -> 'b) -> ('a -> 'b) type Z = forall 'a 'b. ('a -> 'b) -> ('a -> 'b) //│ Defined type alias Z // let f x = (x:sid), ((x:sid):z) def f x = ((x : Sid), ((x : Sid) : Z)) //│ f: Sid -> (Sid, Z,) //│ = [Function: f] // (* Ne type pas ... c'est une instance polymorphe. Interdit. *) // untype fun x -> (x:sid), (x:z) fun x -> ((x : Sid), (x : Z)) //│ res: (Sid & Z) -> (Sid, Z,) //│ = [Function: res] // * === With Constrained Types === :DontDistributeForalls :ConstrainedTypes // let succ (n:Int) = fun f x -> f (n f x) // ;; def succ_ty: ChurchInt -> ChurchInt def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) def succ' n = fun f -> fun x -> f (n f x) //│ succ_ty: ChurchInt -> ChurchInt //│ = //│ succ: ChurchInt -> (forall 'b. 'b -> (forall 'a 'c. 'a -> 'c //│ where //│ 'b <: 'a -> 'a & 'a -> 'c)) //│ = [Function: succ1] //│ succ': 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ = [Function: succ$1] // * Note: without constrained types (and distrib.) we wouldn't get the principal type of succ' succ_ty = succ //│ ChurchInt -> (forall 'b. 'b -> (forall 'a 'c. 'a -> 'c //│ where //│ 'b <: 'a -> 'a & 'a -> 'c)) //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ = [Function: succ1] // * Still requires distributivity even with CT :e succ_ty = succ' //│ 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ ╔══[ERROR] Type error in def definition //│ ║ l.550: succ_ty = succ' //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.30: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.525: def succ' n = fun f -> fun x -> f (n f x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.550: succ_ty = succ' //│ ╙── ^^^^^ //│ = [Function: succ$1] :DistributeForalls succ_ty = succ' //│ 'a -> 'b -> ('c -> 'd //│ where //│ 'a <: 'b -> 'c -> 'e //│ 'b <: 'e -> 'd) //│ <: succ_ty: //│ ChurchInt -> ChurchInt //│ = [Function: succ$1] :DontDistributeForalls succ' //│ res: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'c -> 'e //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'd -> 'e)) //│ = [Function: succ$1] // :e // * Error delayed by inconsistent constrained types succ' {} //│ res: 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ anything <: 'a -> 'b -> 'c //│ 'a <: 'c -> 'd) //│ = [Function (anonymous)] // :e // * Error delayed by inconsistent constrained types succ' {} {} //│ res: 'a -> 'b //│ where //│ anything <: anything -> 'a -> 'c & 'c -> 'b //│ = [Function (anonymous)] :e succ' {} {} {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.608: succ' {} {} {} //│ ║ ^^^^^^^^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.608: succ' {} {} {} //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.525: def succ' n = fun f -> fun x -> f (n f x) //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: n is not a function ================================================ FILE: shared/src/test/diff/mlf-examples/ex_concrete.mls ================================================ :NoRecursiveTypes // type mylist ('b) = Nil | Cons of ('b * mylist ('b)) // ;; class Nil class Cons[a]: { head: a; tail: List[a] } type List[a] = Nil | Cons[a] Nil = Nil {} Cons (head, tail) = Cons { head; tail } //│ Defined class Nil //│ Defined class Cons[+a] //│ Defined type alias List[+a] //│ Nil: Nil //│ = Nil {} //│ Cons: ('head & 'a, List['a] & 'tail,) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] // let un = Nil ;; // let deux = Nil ;; // let trois = Cons (10, un) ;; // let quatre = Cons ("ok", deux) ;; // let cinq = Cons ("zozo", quatre) ;; un = Nil deux = Nil trois = Cons (10, un) quatre = Cons ("ok", deux) cinq = Cons ("zozo", quatre) //│ un: Nil //│ = Nil {} //│ deux: Nil //│ = Nil {} //│ trois: Cons[10] with {tail: Nil} //│ = Cons { head: 10, tail: Nil {} } //│ quatre: Cons["ok"] with {tail: Nil} //│ = Cons { head: 'ok', tail: Nil {} } //│ cinq: Cons["ok" | "zozo"] with {head: "zozo", tail: Cons["ok"] with {tail: Nil}} //│ = Cons { head: 'zozo', tail: Cons { head: 'ok', tail: Nil {} } } // let id x = x def id x = x //│ id: 'a -> 'a //│ = [Function: id] // let rec mymap f l = // begin match l with // | Nil -> Nil // | Cons (car,cdr) -> Cons (f car, mymap f cdr) // end :RecursiveTypes // * Needed for this structurally-typed recursive def rec def map f l = case l of { Nil -> Nil | Cons -> Cons (f l.head, map f l.tail) } //│ map: ('head -> 'head0) -> 'a -> (Nil | 'b) //│ where //│ 'b :> Cons['head0] with {tail: Nil | 'b} //│ 'a <: (Cons[?] with {head: 'head, tail: 'a}) | Nil //│ = [Function: map] :NoRecursiveTypes ================================================ FILE: shared/src/test/diff/mlf-examples/ex_demo.mls ================================================ :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class None class Some[a]: { val: a } type Option[a] = None | Some[a] //│ Defined class None //│ Defined class Some[+a] //│ Defined type alias Option[+a] class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail def cons[a]: a -> List[a] -> List[a] def cons head tail = Cons { head; tail } //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] class Nil[a]: List[a] method Head = error method Tail = this def nil: List['a] nil = Nil {} //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} // ============ Type signatures for functions used in the examples ============ def head: forall 'a. List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: forall 'a. List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] def print_bool: bool -> unit def print_bool b = log b //│ print_bool: bool -> unit //│ = //│ anything -> unit //│ <: print_bool: //│ bool -> unit //│ = [Function: print_bool] def print_int: int -> unit def print_int i = log i //│ print_int: int -> unit //│ = //│ anything -> unit //│ <: print_int: //│ int -> unit //│ = [Function: print_int] def print_string: string -> unit def print_string s = log s //│ print_string: string -> unit //│ = //│ anything -> unit //│ <: print_string: //│ string -> unit //│ = [Function: print_string] // (* A quick demo of MLF *) // (* In the following, "untype expr" means that the expression expr should not be typable. *) // type sid = ['a] 'a -> 'a type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let id = fun x -> x def id = fun x -> x //│ id: 'a -> 'a //│ = [Function: id] // let delta = fun (x:sid) -> x x def delta = fun (x: Sid) -> x x def delta_ = fun x -> x x //│ delta: Sid -> Sid //│ = [Function: delta] //│ delta_: ('a -> 'b & 'a) -> 'b //│ = [Function: delta_] // let choose x y = if true then x else y def choose x y = if true then x else y //│ choose: 'a -> 'a -> 'a //│ = [Function: choose] // let succ n = n + 1 // ;; def succ n = n + 1 //│ succ: int -> int //│ = [Function: succ] // let test1 = delta id test1 = delta id //│ test1: Sid //│ = [Function: id] // untype delta succ :e delta succ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.138: delta succ //│ ║ ^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.104: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.127: def succ n = n + 1 //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.104: type Sid = forall 'a. 'a -> 'a //│ ╙── ^^ //│ res: error | Sid //│ = 'function succ(n) { return n + 1;}1' // let test2 = delta (choose id id) test2 = delta (choose id id) //│ test2: Sid //│ = [Function: id] test2 = delta_ (choose id id) //│ test2: 'a -> 'a //│ = [Function: id] // untype delta (choose id succ) :e delta (choose id succ) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.165: delta (choose id succ) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.104: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.127: def succ n = n + 1 //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.104: type Sid = forall 'a. 'a -> 'a //│ ╙── ^^ //│ res: error | Sid //│ = [Function: id] // let test3 = choose id succ test3 = choose id succ //│ test3: int -> int //│ = [Function: id] // let test4 = choose succ id // ;; test4 = choose succ id //│ test4: int -> int //│ = [Function: succ] // let eqstring s1 s2 = (s1^"A" = s2^"A") def eqstring s1 s2 = eq (concat s1 "A") (concat s2 "A") //│ eqstring: string -> string -> bool //│ = [Function: eqstring] // let eqint i1 i2 = (i1 - i2) = 0 def eqint i1 i2 = (i1 - i2) == 0 //│ eqint: int -> int -> bool //│ = [Function: eqint] // let eqbool b1 b2 = if b1 then b2 else (not b2) def eqbool b1 b2 = if b1 then b2 else (not b2) //│ eqbool: bool -> bool -> bool //│ = [Function: eqbool] // let ignore x = () def ignore x = null //│ ignore: anything -> null //│ = [Function: ignore] // let rec listiter f ll = // if ll = [] then () // else begin ignore (f (car ll)) ; listiter f (cdr ll) end // ;; rec def listiter f ll = if eq ll nil then unit else let _ = ignore (f (head ll)) in listiter f (tail ll) //│ listiter: ('a -> anything) -> List['a] -> unit //│ = [Function: listiter] def listiterA: ('a -> unit) -> List['a] -> unit //│ listiterA: ('a -> unit) -> List['a] -> unit //│ = listiterA = listiter //│ ('a -> anything) -> List['a] -> unit //│ <: listiterA: //│ ('a -> unit) -> List['a] -> unit //│ = [Function: listiter] // (* Polymorphic recursion. *) // untype let rec id x = if true then x else id id x in id :e rec def id1 x = if true then x else id1 id1 x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.243: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required //│ ║ l.243: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id1] // * === \ Below, some of our own variations === // * With annotated recursive calls def idy_ty: forall 'a. 'a -> 'a def id1 x = if true then x else idy_ty idy_ty x //│ idy_ty: 'a -> 'a //│ = //│ id1: 'a -> 'a //│ = //│ idy_ty is not implemented // * This definition does not actually seem to require general polymorphic recursion, // * but it does need at least recursive types: :RecursiveTypes rec def id1 x = if true then x else id1 id1 x //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id11] id1 id //│ res: ('a & 'b) -> 'b | 'c //│ where //│ 'a :> ('a & 'b) -> 'b //│ <: 'b //│ 'b :> ('a & 'b) -> 'b //│ <: 'a -> 'b & 'c //│ = [Function: id] id1 id1 //│ res: ('a & 'b) -> 'a //│ where //│ 'a :> forall 'c 'd. ('a & 'b & 'c) -> ('a | 'd) //│ <: ((forall 'c 'd. 'c -> 'd) | 'b) -> 'a //│ 'c :> 'c -> 'd //│ <: 'd //│ 'd := 'c -> 'd //│ = [Function: id11] // * Note that it can't be applied when typed with :precise-rec-typing AND :DontDistributeForalls :DontDistributeForalls :precise-rec-typing rec def id1_p x = if true then x else id1_p id1_p x //│ id1_p: 'a -> 'b //│ where //│ 'a <: 'b & 'c //│ 'b :> 'd //│ 'd :> forall 'a 'b. 'a -> 'b //│ <: 'c -> 'd //│ 'c :> forall 'a 'b. 'a -> 'b //│ <: 'd //│ = [Function: id1_p] // * Can't apply it: :e id1_p id //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.319: id1_p id //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error | ('a & 'b) -> 'a | 'c //│ where //│ 'a :> 'd //│ 'd :> forall 'a 'b. ('a & 'b) -> 'a //│ <: 'e -> 'd & 'c //│ 'b <: 'a & 'e //│ 'e :> forall 'a 'b. ('a & 'b) -> 'a //│ <: 'd //│ = [Function: id] :DistributeForalls :NoRecursiveTypes // * TODO type pp – inline id1? :e id1 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ ╙── //│ res: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id11] :e id1: nothing //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a := 'b -> 'a //│ 'b :> 'b -> 'a //│ <: 'a //│ ║ l.280: rec def id1 x = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.353: id1: nothing //│ ║ ^^^ //│ ╟── function of type `?a -> ?b` does not match type `nothing` //│ ║ l.280: rec def id1 x = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing` //│ ║ l.353: id1: nothing //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.353: id1: nothing //│ ╙── ^^^^^^^ //│ res: nothing //│ = [Function: id11] :e rec def id1_ x = id1_ id1_ x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: ('b -> 'c | 'b) -> 'c //│ 'c <: 'a //│ ║ l.377: rec def id1_ x = id1_ id1_ x //│ ╙── ^^^^^^^^^ //│ id1_: anything -> nothing //│ = [Function: id1_] :e // * Incorrect annotation rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╔══[ERROR] Inferred recursive type: 'b //│ where //│ 'b <: (forall 'a. 'a -> 'a) -> (??a & 'b) //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in binding of lambda expression: //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^ //│ ╟── but it flows into quantified type variable with expected type `'a0 -> 'a0` //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.388: rec def id1 (x: forall 'a. 'a -> 'a) = if true then x else id1 id1 x //│ ╙── ^^^^^^^^^ //│ id1: (forall 'a. 'a -> 'a) -> 'a0 -> 'a0 //│ = [Function: id12] // * === / === // let rec (id:sid) x = if true then x else id id x def id2: Sid //│ id2: Sid //│ = // * (Note: absence of `rec` emulates annotated rec def.) def id2 x = if true then x else id2 id2 x //│ 'a -> 'a //│ <: id2: //│ Sid //│ = //│ id2 is not implemented // (* Existential types. *) // (* Creating an encapsulation is explicit (unavoidable). *) // (* Here, we build (Exist 'a . 'a * ('a -> unit)) *) // (* then (Exist 'a . 'a * 'a * ('a -> 'a -> bool)) *) // let make_ex1 x (f:['a] ('a * ('a -> 'c)) -> 'b) = f x def make_ex1 x (f: forall 'a. (('a, 'a -> 'c),) -> 'b) = f x //│ make_ex1: (('a, 'a -> 'c,),) -> (forall 'a0. (('a0, 'a0 -> 'c,),) -> 'b) -> 'b //│ = [Function: make_ex1] ex1_1 = make_ex1 (("A String", print_string)) //│ ex1_1: (forall 'a. (('a, 'a -> unit,),) -> 'b) -> 'b //│ = [Function (anonymous)] ex1_1 (fun ((x, f)) -> f x) //│ = undefined //│ // Output //│ A String ex1_2 = if true then make_ex1 ((42, print_int)) else ex1_1 //│ ex1_2: (forall 'a 'a0. (('a, 'a -> unit,),) -> 'b & (('a0, 'a0 -> unit,),) -> 'b) -> 'b //│ = [Function (anonymous)] ex1_2 (fun ((x, f)) -> f x) //│ = undefined //│ // Output //│ 42 // let make_ex2 x (f:['a] ('a * 'a * ('a -> 'a -> 'c)) -> 'b) = f x // ;; def make_ex2 x (f: forall 'a. (('a, 'a, 'a -> 'a -> 'c),) -> 'b) = f x //│ make_ex2: (('a, 'a, 'a -> 'a -> 'c,),) -> (forall 'a0. (('a0, 'a0, 'a0 -> 'a0 -> 'c,),) -> 'b) -> 'b //│ = [Function: make_ex2] // let ex_list1 = [ make_ex1 ("A String", print_string) ; // make_ex1 (8250, print_int) ; // make_ex1 (true, print_bool) ] // ;; ex_list1 = cons (make_ex1 (("A String", print_string))) (cons (make_ex1 ((8250, print_int))) (cons (make_ex1 ((true, print_bool))) nil)) //│ ex_list1: List[forall 'b. (forall 'a 'a0 'a1. (('a, 'a -> unit,),) -> 'b & (('a0, 'a0 -> unit,),) -> 'b & (('a1, 'a1 -> unit,),) -> 'b) -> 'b] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function (anonymous)], tail: Nil {} } //│ } //│ } // let ex_list2 = [ make_ex2 ("String", "String", eqstring) ; // make_ex2 ( 1250, 4890, eqint) ; // make_ex2 ( true, false, eqbool) ] // ;; ex_list2 = cons (make_ex2 (("String", "String", eqstring))) (cons (make_ex2 ((1250, 4890, eqint))) (cons (make_ex2 ((true, false, eqbool))) nil)) //│ ex_list2: List[forall 'b. (forall 'a 'a0 'a1. (('a, 'a, 'a -> 'a -> bool,),) -> 'b & (('a0, 'a0, 'a0 -> 'a0 -> (bool | false),),) -> 'b & (('a1, 'a1, 'a1 -> 'a1 -> bool,),) -> 'b) -> 'b] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function (anonymous)], tail: Nil {} } //│ } //│ } h = head ex_list1 //│ h: (forall 'a 'a0 'a1. (('a, 'a -> unit,),) -> 'b & (('a0, 'a0 -> unit,),) -> 'b & (('a1, 'a1 -> unit,),) -> 'b) -> 'b //│ = [Function (anonymous)] h (fun ((x, f)) -> f x) //│ = undefined //│ // Output //│ A String // * _n accessors not yet implemented in JS backend: // test1 = listiterA (fun ex -> ex (fun p -> p._2 p._1)) ex_list1 test1 = listiterA (fun ex -> ex (fun ((p1, p2)) -> p2 p1)) ex_list1 //│ test1: unit //│ = [Function: unit] //│ // Output //│ A String //│ 8250 //│ true // let test1 = listiter (fun ex -> ex (fun p -> (snd p) (fst p))) ex_list1 // ;; test1 = listiter (fun ex -> ex (fun ((p1, p2)) -> p2 p1)) ex_list1 //│ test1: unit //│ = [Function: unit] //│ // Output //│ A String //│ 8250 //│ true test1_ = listiter (fun ex -> ex (fun ((p1, p2)) -> p2 p1)) //│ test1_: List[(forall 'a 'b. (('a, 'a -> 'b,),) -> 'b) -> anything] -> unit //│ = [Function (anonymous)] test1_ ex_list1 //│ = [Function: unit] //│ // Output //│ A String //│ 8250 //│ true process ex = ex (fun ((p1, p2)) -> p2 p1) //│ process: ((forall 'a 'b. (('a, 'a -> 'b,),) -> 'b) -> 'c) -> 'c //│ = [Function: process] process h //│ = undefined //│ // Output //│ A String listiter process ex_list1 //│ = [Function: unit] //│ // Output //│ A String //│ 8250 //│ true // let test2 = listiter (fun ex -> ex (fun t -> // let arg1 = fst t // and arg2 = fst (snd t) // and eqf = snd (snd t) in // let areequal = eqf arg1 arg2 // in print_bool areequal )) ex_list2 // ;; test2 = listiterA (fun ex -> ex (fun ((t1, t2, t3)) -> let arg1 = t1 in let arg2 = t2 in let eqf = t3 in let areequal = eqf arg1 arg2 in print_bool areequal )) ex_list2 //│ test2: unit //│ = [Function: unit] //│ // Output //│ true //│ false //│ false test2_ = listiter (fun ex -> ex (fun ((t1, t2, t3)) -> let arg1 = t1 in let arg2 = t2 in let eqf = t3 in let areequal = eqf arg1 arg2 in print_bool areequal )) //│ test2_: List[(forall 'a 'b. (('a, 'b, 'a -> 'b -> bool,),) -> unit) -> anything] -> unit //│ = [Function (anonymous)] test2_ ex_list2 //│ = [Function: unit] //│ // Output //│ true //│ false //│ false // * ============ Annotated+untupled versions: ============ def make_ex1: ('x, 'x -> 'c) -> (forall 'b. (forall 'a. ('a, 'a -> 'c) -> 'b) -> 'b) //│ make_ex1: ('x, 'x -> 'c,) -> (forall 'a. ('a, 'a -> 'c,) -> 'b) -> 'b //│ = make_ex1 (x, f) k = k (x, f) //│ ('a, 'b,) -> (('a, 'b,) -> 'c) -> 'c //│ <: make_ex1: //│ ('x, 'x -> 'c,) -> (forall 'a. ('a, 'a -> 'c,) -> 'b) -> 'b //│ = [Function: make_ex11] ex1_1 = make_ex1 ("A String", print_string) //│ ex1_1: (forall 'a. ('a, 'a -> unit,) -> 'b) -> 'b //│ = [Function (anonymous)] ex1_1 (fun (x, f) -> f x) //│ = undefined //│ // Output //│ A String ex1_2 = if true then make_ex1 (42, print_int) else ex1_1 //│ ex1_2: (forall 'a 'a0. ('a, 'a -> unit,) -> 'b & ('a0, 'a0 -> unit,) -> 'b) -> 'b //│ = [Function (anonymous)] ex1_2 (fun (x, f) -> f x) //│ = undefined //│ // Output //│ 42 // let make_ex2 x (f:['a] ('a * 'a * ('a -> 'a -> 'c)) -> 'b) = f x // ;; def make_ex2: ('x, 'x, 'x -> 'c) -> (forall 'b. (forall 'a. ('a, 'a, 'a -> 'c) -> 'b) -> 'b) //│ make_ex2: ('x, 'x, 'x -> 'c,) -> (forall 'a. ('a, 'a, 'a -> 'c,) -> 'b) -> 'b //│ = make_ex2 (x, y, f) k = k (x, y, f) //│ ('a, 'b, 'c,) -> (('a, 'b, 'c,) -> 'd) -> 'd //│ <: make_ex2: //│ ('x, 'x, 'x -> 'c,) -> (forall 'a. ('a, 'a, 'a -> 'c,) -> 'b) -> 'b //│ = [Function: make_ex21] // let ex_list1 = [ make_ex1 ("A String", print_string) ; // make_ex1 (8250, print_int) ; // make_ex1 (true, print_bool) ] // ;; // ex_list1 = cons (make_ex1 ("A String", print_string)) (cons (make_ex1 (8250, print_int)) (cons (make_ex1 (true, print_bool)) nil)) //│ ex_list1: List[forall 'b. (forall 'a 'a0 'a1. ('a, 'a -> unit,) -> 'b & ('a0, 'a0 -> unit,) -> 'b & ('a1, 'a1 -> unit,) -> 'b) -> 'b] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function (anonymous)], tail: Nil {} } //│ } //│ } // let ex_list2 = [ make_ex2 ("String", "String", eqstring) ; // make_ex2 ( 1250, 4890, eqint) ; // make_ex2 ( true, false, eqbool) ] // ;; ex_list2 = cons (make_ex2 ("String", "String", eqstring)) (cons (make_ex2 (1250, 4890, eqint)) (cons (make_ex2 (true, false, eqbool)) nil)) //│ ex_list2: List[forall 'b. (forall 'a 'a0 'a1. ('a, 'a, 'a -> string -> bool,) -> 'b & ('a0, 'a0, 'a0 -> bool -> bool,) -> 'b & ('a1, 'a1, 'a1 -> int -> bool,) -> 'b) -> 'b] //│ = Cons { //│ head: [Function (anonymous)], //│ tail: Cons { //│ head: [Function (anonymous)], //│ tail: Cons { head: [Function (anonymous)], tail: Nil {} } //│ } //│ } // let test1 = listiter (fun ex -> ex (fun p -> (snd p) (fst p))) ex_list1 // ;; test1 = listiterA (fun ex -> ex (fun (a, b) -> b a)) ex_list1 //│ test1: unit //│ = [Function: unit] //│ // Output //│ A String //│ 8250 //│ true test1_ = listiterA (fun ex -> ex (fun (a, b) -> b a)) //│ test1_: List[(forall 'a 'b. ('a, 'a -> 'b,) -> 'b) -> unit] -> unit //│ = [Function (anonymous)] test1_ ex_list1 //│ = [Function: unit] //│ // Output //│ A String //│ 8250 //│ true // (* Some examples for MLF inspired by standard encoding in System F. *) // type Void = ['x] 'x // type Unit = ['x] 'x -> 'x // type Int = ['x] ('x -> 'x) -> ('x -> 'x) // type Bool = ['x] (Unit -> 'x) -> (Unit -> 'x) -> 'x type Fvoid = forall 'a. 'a type Funit = forall 'a. 'a -> 'a type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) type Fbool = forall 'a. (Funit -> 'a) -> (Funit -> 'a) -> 'a //│ Defined type alias Fvoid //│ Defined type alias Funit //│ Defined type alias Fint //│ Defined type alias Fbool // type Sum = ['x] (Int -> 'x) -> (Int -> 'x) -> 'x // type Pair = ['x] (Int -> Int -> 'x) -> 'x // type Triple = ['x] (Int -> Int -> Int -> 'x) -> 'x type Sum = forall 'a. (Fint -> 'a) -> (Fint -> 'a) -> 'a type Pair = forall 'a. (Fint -> Fint -> 'a) -> 'a type Triple = forall 'a. (Fint -> Fint -> Fint -> 'a) -> 'a //│ Defined type alias Sum //│ Defined type alias Pair //│ Defined type alias Triple // (* Unit *) // let c_unit = fun x -> x def c_unit = fun x -> x //│ c_unit: 'a -> 'a //│ = [Function: c_unit] // (* BOOLEANS, IFS, AND, OR, NOT. *) // let c_true = fun a b -> a c_unit def c_true = fun a -> fun b -> a c_unit //│ c_true: ((forall 'a. 'a -> 'a) -> 'b) -> anything -> 'b //│ = [Function: c_true] // let c_false = fun a b -> b c_unit def c_false = fun a -> fun b -> b c_unit //│ c_false: anything -> ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: c_false] // let c_if (cond:Bool) c_then c_else = cond c_then c_else def c_if (cond: Fbool) c_then c_else = cond c_then c_else def c_if_ cond c_then c_else = cond c_then c_else //│ c_if: Fbool -> (Funit -> 'a) -> (Funit -> 'a) -> 'a //│ = [Function: c_if] //│ c_if_: ('a -> 'b -> 'c) -> 'a -> 'b -> 'c //│ = [Function: c_if_] // let c_and (a:Bool) (b:Bool) = c_if a (fun z -> b) (fun z -> c_false) def c_and (a: Fbool) (b: Fbool) = c_if a (fun z -> b) (fun z -> c_false) def c_and_ a b = c_if a (fun z -> b) (fun z -> c_false) //│ c_and: Fbool -> Fbool -> (anything -> ((forall 'a. 'a -> 'a) -> 'b) -> 'b | Fbool) //│ = [Function: c_and] //│ c_and_: Fbool -> 'a -> (anything -> ((forall 'b. 'b -> 'b) -> 'c) -> 'c | 'a) //│ = [Function: c_and_] // let c_or (a:Bool) (b:Bool) = c_if a (fun z -> c_true) (fun z -> b) def c_or (a: Fbool) (b: Fbool) = c_if a (fun z -> c_true) (fun z -> b) def c_or_ a b = c_if a (fun z -> c_true) (fun z -> b) //│ c_or: Fbool -> Fbool -> (((forall 'a. 'a -> 'a) -> 'b) -> anything -> 'b | Fbool) //│ = [Function: c_or] //│ c_or_: Fbool -> 'a -> (((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c | 'a) //│ = [Function: c_or_] // let c_not (a:Bool) = c_if a (fun z -> c_false) (fun z -> c_true) def c_not (a: Fbool) = c_if a (fun z -> c_false) (fun z -> c_true) def c_not_ a = c_if a (fun z -> c_false) (fun z -> c_true) //│ c_not: Fbool -> ((forall 'a. 'a -> 'a) -> 'b) -> ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: c_not] //│ c_not_: Fbool -> ((forall 'a. 'a -> 'a) -> 'b) -> ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: c_not_] // let c_or = (c_or : Bool -> Bool -> Bool) def c_or2 = c_or : Fbool -> Fbool -> Fbool //│ c_or2: Fbool -> Fbool -> Fbool //│ = [Function: c_or2] def c_or2_ = c_or_ : Fbool -> Fbool -> Fbool //│ c_or2_: Fbool -> Fbool -> Fbool //│ = [Function: c_or2_] // (* CONSTRUCTORS FOR PAIRS ET TRIPLES. *) // let c_pair x y = fun f -> f x y def c_pair x y = fun f -> f x y //│ c_pair: 'a -> 'b -> ('a -> 'b -> 'c) -> 'c //│ = [Function: c_pair] // let c_trip x y z = fun f -> f x y z def c_trip x y z = fun f -> f x y z //│ c_trip: 'a -> 'b -> 'c -> ('a -> 'b -> 'c -> 'd) -> 'd //│ = [Function: c_trip] // (* PROJECTIONS FOR PRODUCTS. *) // let c_1_2 (p:Pair) = p (fun x y -> x) def c_1_2 (p: Pair) = p (fun x -> fun y -> x) def c_1_2_ p = p (fun x -> fun y -> x) //│ c_1_2: Pair -> Fint //│ = [Function: c_1_2] //│ c_1_2_: ((forall 'a. 'a -> anything -> 'a) -> 'b) -> 'b //│ = [Function: c_1_2_] // let c_2_2 (p:Pair) = p (fun x y -> y) def c_2_2 (p: Pair) = p (fun x -> fun y -> y) def c_2_2_ p = p (fun x -> fun y -> y) //│ c_2_2: Pair -> Fint //│ = [Function: c_2_2] //│ c_2_2_: ((forall 'a. anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function: c_2_2_] // let c_1_3 (t:Triple) = t (fun x y z -> x) def c_1_3 (t: Triple) = t (fun x -> fun y -> fun z -> x) def c_1_3_ t = t (fun x -> fun y -> fun z -> x) //│ c_1_3: Triple -> Fint //│ = [Function: c_1_3] //│ c_1_3_: ((forall 'a. 'a -> anything -> anything -> 'a) -> 'b) -> 'b //│ = [Function: c_1_3_] // let c_2_3 (t:Triple) = t (fun x y z -> y) def c_2_3 (t: Triple) = t (fun x -> fun y -> fun z -> y) def c_2_3_ t = t (fun x -> fun y -> fun z -> y) //│ c_2_3: Triple -> Fint //│ = [Function: c_2_3] //│ c_2_3_: ((forall 'a. anything -> 'a -> anything -> 'a) -> 'b) -> 'b //│ = [Function: c_2_3_] // let c_3_3 (t:Triple) = t (fun x y z -> z) def c_3_3 (t: Triple) = t (fun x -> fun y -> fun z -> z) def c_3_3_ t = t (fun x -> fun y -> fun z -> z) //│ c_3_3: Triple -> Fint //│ = [Function: c_3_3] //│ c_3_3_: ((forall 'a. anything -> anything -> 'a -> 'a) -> 'b) -> 'b //│ = [Function: c_3_3_] // (* CONSTRUCTOR FOR A BINARY SUM. *) // let c_sumg = fun x fg fd -> fg x def c_sumg = fun x -> fun fg -> fun fd -> fg x //│ c_sumg: 'a -> ('a -> 'b) -> anything -> 'b //│ = [Function: c_sumg] // let c_sumd = fun x fg fd -> fd x def c_sumd = fun x -> fun fg -> fun fd -> fd x //│ c_sumd: 'a -> anything -> ('a -> 'b) -> 'b //│ = [Function: c_sumd] // (* CASE FOR A SUM. *) // let c_case (s:Sum) g d = s g d def c_case (s: Sum) g d = s g d def c_case_ s g d = s g d //│ c_case: Sum -> (Fint -> 'a) -> (Fint -> 'a) -> 'a //│ = [Function: c_case] //│ c_case_: ('a -> 'b -> 'c) -> 'a -> 'b -> 'c //│ = [Function: c_case_] // (* Integers. *) // let c_i0 = fun f x -> x def c_i0 = fun f -> fun x -> x //│ c_i0: anything -> 'a -> 'a //│ = [Function: c_i0] // let c_i1 = fun f x -> f x def c_i1 = fun f -> fun x -> f x //│ c_i1: ('a -> 'b) -> 'a -> 'b //│ = [Function: c_i1] // let c_i2 = fun f x -> f (f x) def c_i2 = fun f -> fun x -> f (f x) //│ c_i2: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: c_i2] // let c_succ (n:Int) = fun f x -> n f (f x) def c_succ (n: Fint) = fun f -> fun x -> n f (f x) def c_succ_ n = fun f -> fun x -> n f (f x) //│ c_succ: Fint -> (('a | 'b) -> 'a) -> 'b -> 'a //│ = [Function: c_succ] //│ c_succ_: ('a -> 'b -> 'c) -> ('d -> 'b & 'a) -> 'd -> 'c //│ = [Function: c_succ_] // let c_iszero (n:Int) = n (fun x -> c_false) c_true def c_iszero (n: Fint) = n (fun x -> c_false) c_true def c_iszero_ n = n (fun x -> c_false) c_true //│ c_iszero: Fint -> ((forall 'a. 'a -> 'a) -> 'b) -> ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: c_iszero] //│ c_iszero_: ((forall 'a. anything -> anything -> ((forall 'b. 'b -> 'b) -> 'a) -> 'a) -> (forall 'c. ((forall 'b. 'b -> 'b) -> 'c) -> anything -> 'c) -> 'd) -> 'd //│ = [Function: c_iszero_] // let c_add n (m:Int) = m c_succ n def c_add n (m: Fint) = m c_succ n //│ c_add: (Fint & 'b) -> Fint -> ((('a | 'c) -> 'a) -> 'c -> 'a | 'b) //│ = [Function: c_add] def c_add_ n m = m c_succ_ n //│ c_add_: 'a -> ((forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('e -> 'c & 'b) -> 'e -> 'd) -> 'a -> 'f) -> 'f //│ = [Function: c_add_] // let c_mul n (m:Int) = m (c_add n) c_i0 def c_mul m (n: Fint) = m (c_add n) c_i0 //│ c_mul: ((Fint -> (forall 'a 'b. (('a | 'b) -> 'a) -> 'b -> 'a | Fint)) -> (forall 'c. anything -> 'c -> 'c) -> 'd) -> Fint -> 'd //│ = [Function: c_mul] def c_mul_ m n = m (c_add_ n) c_i0 //│ c_mul_: ((forall 'a. ((forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('e -> 'c & 'b) -> 'e -> 'd) -> 'f -> 'a) -> 'a) -> (forall 'g. anything -> 'g -> 'g) -> 'h) -> 'f -> 'h //│ = [Function: c_mul_] // let c_pow n (m:Int) = m (c_mul n) c_i1 def c_pow m (n: Fint) = m (c_mul n) c_i1 //│ c_pow: ((Fint -> (forall 'a 'b. (('a | 'b) -> 'a) -> 'b -> ('b | 'a) | Fint)) -> (forall 'c 'd. ('c -> 'd) -> 'c -> 'd) -> 'e) -> Fint -> 'e //│ = [Function: c_pow] def c_pow_ m n = m (c_mul_ n) c_i1 //│ c_pow_: (('a -> 'b) -> (forall 'c 'd. ('c -> 'd) -> 'c -> 'd) -> 'e) -> ((forall 'f. ((forall 'g 'h 'i 'j. ('g -> 'h -> 'i) -> ('j -> 'h & 'g) -> 'j -> 'i) -> 'a -> 'f) -> 'f) -> (forall 'k. anything -> 'k -> 'k) -> 'b) -> 'e //│ = [Function: c_pow_] // let c_pred (n:Int) = // let s p = c_pair (c_2_2 p) (c_succ (c_2_2 p)) // and z = c_pair c_i0 c_i0 in // c_1_2 (n s z) def c_pred (n: Fint) = let s = fun p -> c_pair (c_2_2 p) (c_succ (c_2_2 p)) in let z = c_pair c_i0 c_i0 in c_1_2 (n s z) //│ c_pred: Fint -> Fint //│ = [Function: c_pred] def c_pred_ n = let s = fun p -> c_pair (c_2_2_ p) (c_succ_ (c_2_2_ p)) in let z = c_pair c_i0 c_i0 in c_1_2_ (n s z) //│ c_pred_: ((forall 'a 'b 'c 'd 'e 'f. ((forall 'g. anything -> 'g -> 'g) -> ('a -> 'b -> 'c & 'd)) -> ('d -> (('e -> 'b & 'a) -> 'e -> 'c) -> 'f) -> 'f) -> (forall 'h. ((forall 'i. anything -> 'i -> 'i) -> (forall 'i. anything -> 'i -> 'i) -> 'h) -> 'h) -> (forall 'j. 'j -> anything -> 'j) -> 'k) -> 'k //│ = [Function: c_pred_] // let c_printint (n:Int) = print_int (n (fun x -> x+1) 0); print " " def c_printint (n: Fint) = let _ = print_int (n (fun x -> x + 1) 0) in print_string " " def c_printint_ n = let _ = print_int (n (fun x -> x + 1) 0) in print_string " " //│ c_printint: Fint -> unit //│ = [Function: c_printint] //│ c_printint_: ((int -> int) -> 0 -> int) -> unit //│ = [Function: c_printint_] def c_printint2 (n: Fint) = toString (n (fun x -> x + 1) 0) def c_printint2_ n = toString (n (fun x -> x + 1) 0) //│ c_printint2: Fint -> string //│ = [Function: c_printint2] //│ c_printint2_: ((int -> int) -> 0 -> anything) -> string //│ = [Function: c_printint2_] // let c_i2 = c_succ c_i1 def c_i2 = c_succ c_i1 def c_i2_ = c_succ_ c_i1 //│ c_i2: (('a | 'b) -> 'a) -> 'b -> 'a //│ = [Function: c_i21] //│ c_i2_: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: c_i2_] // let c_i3 = c_succ c_i2 def c_i3 = c_succ c_i2 def c_i3_ = c_succ_ c_i2 //│ c_i3: (('a | 'b) -> 'a) -> 'b -> 'a //│ = [Function: c_i3] //│ c_i3_: ('b -> 'c & ('a | 'c) -> 'a) -> 'b -> 'a //│ = [Function: c_i3_] // let c_i4 = c_succ c_i3 def c_i4 = c_succ c_i3 def c_i4_ = c_succ_ c_i3 //│ c_i4: (('a | 'b) -> 'a) -> 'b -> 'a //│ = [Function: c_i4] //│ c_i4_: ('b -> 'c & ('a | 'c) -> 'a) -> 'b -> 'a //│ = [Function: c_i4_] // (* Fails with value restriction. *) // (* Passes with relaxed value restriction. *) // let rec (to_church:int -> Int) n = // if n = 0 then fun f x -> x // else fun f x -> f ((to_church (n-1)) f x) def to_church: int -> Fint //│ to_church: int -> Fint //│ = // * See to_church_3 in `ex_casparticuliers.mls` rec def to_church n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church (n - 1) : Fint) f x) //│ int -> ('a -> ('a & 'b)) -> ('a & 'b) -> 'b //│ <: to_church: //│ int -> Fint //│ = [Function: to_church] // let rec to_church n = // if n = 0 then fun f x -> x // else fun f x -> f ((to_church (n-1)) f x) rec def to_church_ n = if n == 0 then fun f -> fun x -> x else fun f -> fun x -> f ((to_church_ (n - 1)) f x) //│ to_church_: int -> ('a -> 'b & 'a -> 'a) -> ('a & 'b) -> 'b //│ = [Function: to_church_] // let rec c_fact (n:Int) = // c_if (c_iszero n) (fun (u:Unit) -> c_i1) // (fun (u:Unit) -> c_mul n (c_fact (c_pred n))) rec def c_fact (n: Fint) = c_if (c_iszero n) (fun (u: Funit) -> c_i1) (fun (u: Funit) -> c_mul n (c_fact (c_pred n))) //│ c_fact: Fint -> ((('a | 'b) -> 'a & 'b -> 'c) -> 'b -> ('a | 'b | 'c) | Fint) //│ = [Function: c_fact] // def c_fact: Fint -> (forall 'a 'b. ('a & 'b) -> (forall 'c 'd 'e 'f 'g 'h. (('c & 'h) -> ('d | 'g | 'c)))) def c_fact_A: Fint -> Fint //│ c_fact_A: Fint -> Fint //│ = def c_fact_A n = c_if (c_iszero n) (fun u -> c_i1) (fun u -> c_mul n (c_fact_A (c_pred n))) //│ (Fint & (Fint -> (forall 'a 'b. (('a | 'b) -> 'a) -> 'b -> 'a | Fint)) -> (forall 'c. anything -> 'c -> 'c) -> 'd) -> (('e -> 'f) -> 'e -> 'f | 'd) //│ <: c_fact_A: //│ Fint -> Fint //│ = //│ c_fact_A is not implemented // * This one MLF probably can't type // * TODO: better type simplif...? :e rec def c_fact_ n = c_if_ (c_iszero_ n) (fun _ -> c_i1) (fun _ -> c_mul_ n (c_fact_ (c_pred_ n))) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (? -> 'j) -> 'k & (forall 'l. ((forall 'm 'n 'o 'p. ('m -> 'n -> 'o) -> ('p -> 'n & 'm) -> 'p -> 'o) -> 'k -> 'l) -> 'l) -> (forall 'c. ? -> 'c -> 'c) -> 'j & (forall 'q 'r 's 't 'u 'v. ((forall 'w. ? -> 'w -> 'w) -> ('q -> 'r -> 's & 't)) -> ('t -> (('u -> 'r & 'q) -> 'u -> 's) -> 'v) -> 'v) -> 'a) //│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ c_fact_: 'a -> 'b //│ where //│ 'a <: (forall 'c. anything -> anything -> ((forall 'd. 'd -> 'd) -> 'c) -> 'c) -> (forall 'e. ((forall 'd. 'd -> 'd) -> 'e) -> anything -> 'e) -> (forall 'f 'g. anything -> ('f -> 'g) -> 'f -> 'g) -> (anything -> 'h) -> 'b & (forall 'i. ((forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('m -> 'k & 'j) -> 'm -> 'l) -> 'b -> 'i) -> 'i) -> (forall 'n. anything -> 'n -> 'n) -> 'h & (forall 'o 'p 'q 'r 's 't. ((forall 'u. anything -> 'u -> 'u) -> ('o -> 'p -> 'q & 'r)) -> ('r -> (('s -> 'p & 'o) -> 's -> 'q) -> 't) -> 't) -> (forall 'v. ((forall 'n. anything -> 'n -> 'n) -> (forall 'n. anything -> 'n -> 'n) -> 'v) -> 'v) -> (forall 'w. 'w -> anything -> 'w) -> 'a //│ = [Function: c_fact_] :e // * The type we infer without any annotations can't be checked against the desired signature c_fact_A = c_fact_ //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('n -> 'o -> 'p) -> ('q -> 'o & 'n) -> 'q -> 'p) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('t -> 'u -> 'v & 'w)) -> ('w -> (('x -> 'u & 't) -> 'x -> 'v) -> 'y) -> 'y) -> 'a) //│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ 'a -> 'b //│ where //│ 'a <: (forall 'c. anything -> anything -> ((forall 'd. 'd -> 'd) -> 'c) -> 'c) -> (forall 'e. ((forall 'd. 'd -> 'd) -> 'e) -> anything -> 'e) -> (forall 'f 'g. anything -> ('f -> 'g) -> 'f -> 'g) -> (anything -> 'h) -> 'b & (forall 'i. ((forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('m -> 'k & 'j) -> 'm -> 'l) -> 'b -> 'i) -> 'i) -> (forall 'n. anything -> 'n -> 'n) -> 'h & (forall 'o 'p 'q 'r 's 't. ((forall 'u. anything -> 'u -> 'u) -> ('o -> 'p -> 'q & 'r)) -> ('r -> (('s -> 'p & 'o) -> 's -> 'q) -> 't) -> 't) -> (forall 'v. ((forall 'n. anything -> 'n -> 'n) -> (forall 'n. anything -> 'n -> 'n) -> 'v) -> 'v) -> (forall 'w. 'w -> anything -> 'w) -> 'a //│ <: c_fact_A: //│ Fint -> Fint //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.1085: c_fact_A = c_fact_ //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: c_fact_] // let print_fact n = // print_string "Factorial " ; // print_int n ; // print_string " = " ; // c_printint (c_fact (to_church n)) ; // print_string "\n" ; // () def print_fact n = let _ = print_string "Factorial " in let _ = print_int n in let _ = print_string " = " in let _ = c_printint (c_fact (to_church n)) in print_string "\n" //│ print_fact: int -> unit //│ = [Function: print_fact] :e // * We get a cycle error because recursive `c_fact_` has NO annotations whatsoever def print_fact_ n = let _ = print_string "Factorial " in let _ = print_int n in let _ = print_string " = " in let _ = c_printint_ (c_fact_ (to_church_ n)) in print_string "\n" //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('n -> 'o -> 'p) -> ('q -> 'o & 'n) -> 'q -> 'p) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('t -> 'u -> 'v & 'w)) -> ('w -> (('x -> 'u & 't) -> 'x -> 'v) -> 'y) -> 'y) -> 'a) //│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1124: let _ = c_printint_ (c_fact_ (to_church_ n)) in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ print_fact_: int -> unit //│ = [Function: print_fact_] // * Works when using the annotated `c_fact`: def print_fact_ n = let _ = print_string "Factorial " in let _ = print_int n in let _ = print_string " = " in let _ = c_printint_ (c_fact (to_church_ n)) in print_string "\n" //│ print_fact_: int -> unit //│ = [Function: print_fact_1] def print_fact2 n = concat "Factorial " ( concat (toString n) ( concat " = " (c_printint2 (c_fact (to_church n))) )) //│ print_fact2: int -> string //│ = [Function: print_fact2] :e // * Similar as above (bad `c_fact_`) def print_fact2_ n = concat "Factorial_ " ( concat (toString n) ( concat " = " (c_printint2_ (c_fact_ (to_church_ n))) )) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: (forall 'b. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'b) -> 'b) -> (forall 'd. 'd -> ? -> 'd) -> ((forall 'e. ? -> ? -> ((forall 'f. 'f -> 'f) -> 'e) -> 'e) -> (forall 'g. ((forall 'f. 'f -> 'f) -> 'g) -> ? -> 'g) -> (forall 'h 'i. ? -> ('h -> 'i) -> 'h -> 'i) -> (forall 'j. ? -> (? -> 'j -> 'j | 'k)) -> 'l & (forall 'm. ((forall 'n 'o 'p 'q. ('n -> 'o -> 'p) -> ('q -> 'o & 'n) -> 'q -> 'p) -> (forall 'r 's. ('r -> 's) -> 'r -> 's | 'l) -> 'm) -> 'm) -> (forall 'c. ? -> 'c -> 'c) -> 'k & (forall 't 'u 'v 'w 'x 'y. ((forall 'z. ? -> 'z -> 'z) -> ('t -> 'u -> 'v & 'w)) -> ('w -> (('x -> 'u & 't) -> 'x -> 'v) -> 'y) -> 'y) -> 'a) //│ ║ l.963: c_1_2_ (n s z) //│ ╙── ^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1160: (c_printint2_ (c_fact_ (to_church_ n))) )) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ print_fact2_: int -> string //│ = [Function: print_fact2_] // * Works when using the annotated `c_fact`: def print_fact2_ n = concat "Factorial_ " ( concat (toString n) ( concat " = " (c_printint2_ (c_fact (to_church_ n))) )) //│ print_fact2_: int -> string //│ = [Function: print_fact2_1] // let essai = // print_string "Church integers :\n"; // let c_i5 = c_add c_i3 c_i2 in // let c_i10 = c_mul c_i5 c_i2 in // let c_i9 = c_pred c_i10 in // let c_99 = c_add (c_mul c_i9 c_i10) c_i9 in // let c_98 = c_pred c_99 in // print_string "This should be 98 : " ; // c_printint c_98; // print_string "\n\n" ; this_should_be_98 = let c_i5 = c_add c_i3 c_i2 in let c_i10 = c_mul c_i5 c_i2 in let c_i9 = c_pred c_i10 in let c_99 = c_add (c_mul c_i9 c_i10) c_i9 in let c_98 = c_pred c_99 in c_printint2 c_98 //│ this_should_be_98: string //│ = '98' // * [FCP:patho] This version without any type annotations doesn't work (probably also doesn't in MLF) :stats // * Note: subtyping takes too many steps when replacing both `c_mul_` by `c_mul` :e // * Not sure what's a minimal change to make this one work this_should_be_98_ = let c_i5_ = c_add_ c_i3_ c_i2_ in let c_i10_ = c_mul_ c_i5_ c_i2_ in let c_i9_ = c_pred_ c_i10_ in let c_99_ = c_add_ (c_mul_ c_i9_ c_i10_) c_i9_ in let c_98_ = c_pred_ c_99_ in c_printint2_ c_98_ //│ ╔══[ERROR] Inferred recursive type: nothing //│ ║ l.731: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1207: let c_i10_ = c_mul_ c_i5_ c_i2_ in //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1208: let c_i9_ = c_pred_ c_i10_ in //│ ║ ^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ this_should_be_98_: string //│ = '98' //│ constrain calls : 11252 //│ annoying calls : 0 //│ subtyping calls : 38847 // * Decomposing... :stats c_i5_ = c_add_ c_i3_ c_i2_ //│ c_i5_: ('b -> 'c & 'c -> 'd & 'd -> 'e & ('a | 'e) -> 'a) -> 'b -> 'a //│ = [Function (anonymous)] //│ constrain calls : 118 //│ annoying calls : 0 //│ subtyping calls : 493 :stats :e c_i10_ = c_mul_ c_i5_ c_i2_ //│ ╔══[ERROR] Inferred recursive type: nothing //│ ║ l.731: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1241: c_i10_ = c_mul_ c_i5_ c_i2_ //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ c_i10_: error | ((forall 'a 'b 'c. ('a -> 'b & 'b -> 'c) -> 'a -> 'c | 'd) -> ('e -> 'f -> ('g & 'h & 'i & 'j & 'k & 'l & 'm) & 'n) & (('o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 's) -> 'o -> 'r | 'n) -> ('t -> 'u -> ('h & 'i & 'j & 'k & 'l & 'm) & 'v) & (('w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's) -> 'w -> 'g | 'v) -> ('x -> 'y -> ('i & 'j & 'k & 'l & 'm) & 'z) & (('a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't) -> 'a1 -> 'h | 'z) -> ('b1 -> 'c1 -> ('j & 'k & 'l & 'm) & 'd1) & (('e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x) -> 'e1 -> 'i | 'd1) -> ('f1 -> 'g1 -> ('k & 'l & 'm) & 'h1) & (('i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1) -> 'i1 -> 'j | 'h1) -> ('j1 -> 'k1 -> ('l & 'm) & 'l1) & (('m1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1) -> 'm1 -> 'k | 'l1) -> ('n1 -> 'o1 -> 'm & 'p1) & (('q1 -> ('k1 & 'm1) & 'm1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1 & 'j1) -> 'q1 -> 'l | 'p1) -> ('r1 & 's1)) -> ('s -> 'p -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'd) -> (('t1 -> ('o1 & 'q1) & 'q1 -> ('k1 & 'm1) & 'm1 -> ('g1 & 'i1) & 'i1 -> ('c1 & 'e1) & 'e1 -> ('y & 'a1) & 'a1 -> ('u & 'w) & 'w -> ('f & 'o) & 'o -> 'p & 'p -> 'q & 'q -> ('g & 'h & 'i & 'j & 'k & 'l & 'm & 'r) & 'e & 's & 't & 'x & 'b1 & 'f1 & 'j1 & 'n1) -> 't1 -> 'm | 'r1) | 's1 //│ where //│ 's1 <: (forall 'u1 'v1 'w1 'x1. ('u1 -> 'v1 -> 'w1) -> ('x1 -> 'v1 & 'u1) -> 'x1 -> 'w1) -> (forall 'y1 'z1 'a2. ('y1 -> 'z1 & 'z1 -> 'a2) -> 'y1 -> 'a2) -> 's1 //│ = [Function (anonymous)] //│ constrain calls : 983 //│ annoying calls : 0 //│ subtyping calls : 32252 :stats // * This one works: c_i10_ = c_mul c_i5_ c_i2_ //│ c_i10_: (('a | 'b) -> 'a & ('a0 | 'b) -> 'a0) -> 'b -> ('a | 'a0) | Fint //│ = [Function (anonymous)] //│ constrain calls : 624 //│ annoying calls : 0 //│ subtyping calls : 2474 // * Ouchie (cf stats) :stats c_i9_ = c_pred_ c_i10_ //│ c_i9_: ('a -> ('a & 'b) & 'c -> 'b & 'c -> 'c & 'd -> ('d & 'b) & 'e -> 'b & 'e -> 'e) -> ('a & 'b & 'c & 'd & 'e) -> 'b //│ = [Function (anonymous)] //│ constrain calls : 2552 //│ annoying calls : 0 //│ subtyping calls : 11102 :stats :e c_99_ = c_add_ (c_mul_ c_i9_ c_i10_) c_i9_ //│ ╔══[ERROR] Inferred recursive type: 'b //│ where //│ 'b <: (forall 'c 'd 'e 'f. ('c -> 'd -> 'e) -> ('f -> 'd & 'c) -> 'f -> 'e) -> ((forall 'a 'g 'a0 'a1 'a2. (('a | 'g) -> 'a & ('a0 | 'g) -> 'a0 & ('a1 | 'g) -> 'a1 & ('a2 | 'g) -> 'a2) -> 'g -> ('a | 'a0 | 'a1 | 'a2) | Fint) -> ? & (forall 'a3 'h 'a4. (('a3 | 'h) -> 'a3 & ('a4 | 'h) -> 'a4) -> 'h -> ('a3 | 'a4) | Fint) -> anything) //│ ║ l.903: def c_succ_ n = fun f -> fun x -> n f (f x) //│ ╙── ^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.1276: c_99_ = c_add_ (c_mul_ c_i9_ c_i10_) c_i9_ //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ c_99_: error | ('a -> 'a & 'b -> 'b & 'c -> 'c & 'd -> 'd) -> ('a & 'b & 'c & 'd) -> error //│ = [Function (anonymous)] //│ constrain calls : 3117 //│ annoying calls : 0 //│ subtyping calls : 14864 :stats // * Note: works with `c_mul` AND recursive types... :RecursiveTypes c_99_ = c_add_ (c_mul c_i9_ c_i10_) c_i9_ //│ c_99_: ('b -> ('b & 'c & 'a & 'a0) & 'a -> ('c & 'a) & 'a0 -> ('c & 'a0) & 'd -> ('e & 'c & 'a1 & 'a2) & 'e -> ('e & 'c & 'a3 & 'a4) & 'a3 -> ('c & 'a3) & 'a4 -> ('c & 'a4) & 'a1 -> ('c & 'a1) & 'a2 -> ('c & 'a2) & 'f -> ('f & 'c & 'a5 & 'a6) & 'a5 -> ('c & 'a5) & 'a6 -> ('c & 'a6) & ('a7 | 'd) -> 'a7 & 'd -> ('g & 'c & 'a8 & 'a9) & 'g -> ('g & 'c & 'a10 & 'a11) & 'a10 -> ('c & 'a10) & 'a11 -> ('c & 'a11) & 'a8 -> ('c & 'a8) & 'a9 -> ('c & 'a9)) -> ('b & 'd & 'f & 'c) -> ('c | 'a7) | Fint //│ = [Function (anonymous)] //│ constrain calls : 2217 //│ annoying calls : 0 //│ subtyping calls : 22997 :NoRecursiveTypes // * Ouchie++ :stats :Fuel 20000 c_98_ = c_pred_ c_99_ //│ c_98_: ('a -> ('a & 'b) & 'c -> 'a & 'c -> 'c & 'd -> ('a & 'd) & 'e -> 'b & 'f -> 'e & 'f -> 'f & 'g -> ('e & 'g) & 'h -> ('e & 'h) & 'i -> 'h & 'i -> 'i & 'j -> ('h & 'j) & 'k -> ('k & 'b) & 'l -> 'k & 'l -> 'l & 'm -> ('k & 'm) & 'n -> ('n & 'b) & 'o -> 'b & 'o -> 'o & 'p -> 'b & 'q -> 'p & 'q -> 'q & 'r -> ('p & 'r) & 's -> ('p & 's) & 't -> 's & 't -> 't & 'u -> ('s & 'u)) -> ('a & 'c & 'd & 'b & 'e & 'f & 'g & 'h & 'i & 'j & 'k & 'l & 'm & 'n & 'o & 'p & 'q & 'r & 's & 't & 'u) -> 'b //│ = [Function (anonymous)] //│ constrain calls : 11598 //│ annoying calls : 0 //│ subtyping calls : 74107 :ResetFuel // print_fact 0 ; // print_fact 1 ; // print_fact 2 ; // print_fact 3 ; // print_fact 4 ; // print_fact 5 ; // print_fact 6 ; // () print_fact2 0 print_fact2 1 print_fact2 2 print_fact2 3 print_fact2 4 print_fact2 5 print_fact2 6 //│ res: string //│ = 'Factorial 0 = 1' //│ res: string //│ = 'Factorial 1 = 1' //│ res: string //│ = 'Factorial 2 = 2' //│ res: string //│ = 'Factorial 3 = 6' //│ res: string //│ = 'Factorial 4 = 24' //│ res: string //│ = 'Factorial 5 = 120' //│ res: string //│ = 'Factorial 6 = 720' print_fact2_ 0 print_fact2_ 1 print_fact2_ 2 print_fact2_ 3 print_fact2_ 4 print_fact2_ 5 print_fact2_ 6 //│ res: string //│ = 'Factorial_ 0 = 1' //│ res: string //│ = 'Factorial_ 1 = 1' //│ res: string //│ = 'Factorial_ 2 = 2' //│ res: string //│ = 'Factorial_ 3 = 6' //│ res: string //│ = 'Factorial_ 4 = 24' //│ res: string //│ = 'Factorial_ 5 = 120' //│ res: string //│ = 'Factorial_ 6 = 720' // * === Variation: With Constrained Types === :DontDistributeForalls :ConstrainedTypes def c_succ (n: Fint) = fun f -> fun x -> n f (f x) def c_succ_ n = fun f -> fun x -> n f (f x) //│ c_succ: Fint -> (forall 'b. 'b -> (forall 'a 'c. 'c -> 'a //│ where //│ 'b <: 'a -> 'a & 'c -> 'a)) //│ = [Function: c_succ1] //│ c_succ_: 'a -> (forall 'b. 'b -> (forall 'c 'd 'e. 'e -> 'd //│ where //│ 'a <: 'b -> 'c -> 'd //│ 'b <: 'e -> 'c)) //│ = [Function: c_succ_1] def c_add_ n m = m c_succ_ n //│ c_add_: 'a -> (forall 'b. ((forall 'c. 'c -> (forall 'd. 'd -> (forall 'e 'f 'g. 'g -> 'f //│ where //│ 'c <: 'd -> 'e -> 'f //│ 'd <: 'g -> 'e))) -> 'a -> 'b) -> 'b) //│ = [Function: c_add_1] // let c_i1 = fun f x -> f x def c_i1 = fun f -> fun x -> f x //│ c_i1: 'a -> (forall 'b 'c. 'b -> 'c //│ where //│ 'a <: 'b -> 'c) //│ = [Function: c_i11] // let c_i2 = c_succ c_i1 def c_i2 = c_succ c_i1 def c_i2_ = c_succ_ c_i1 //│ c_i2: 'b -> (forall 'a 'c. 'c -> 'a //│ where //│ 'b <: 'a -> 'a & 'c -> 'a) //│ = [Function: c_i22] //│ c_i2_: 'a -> (forall 'b 'c 'd. 'd -> 'c //│ where //│ forall 'e. 'e -> (forall 'f 'g. 'f -> 'g //│ where //│ 'e <: 'f -> 'g) <: 'a -> 'b -> 'c //│ 'a <: 'd -> 'b) //│ = [Function: c_i2_1] // let c_i3 = c_succ c_i2 def c_i3 = c_succ c_i2 def c_i3_ = c_succ_ c_i2 //│ c_i3: 'b -> (forall 'a 'c. 'c -> 'a //│ where //│ 'b <: 'a -> 'a & 'c -> 'a) //│ = [Function: c_i31] //│ c_i3_: 'b -> (forall 'c 'd 'e. 'e -> 'd //│ where //│ forall 'f. 'f -> (forall 'a 'g. 'g -> 'a //│ where //│ 'f <: 'a -> 'a & 'g -> 'a) <: 'b -> 'c -> 'd //│ 'b <: 'e -> 'c) //│ = [Function: c_i3_1] :e :stats c_i5_ = c_add_ c_i3_ c_i2 //│ ╔══[ERROR] Inferred recursive type: 'b //│ where //│ 'b :> forall 'c. 'c -> (forall 'd 'e 'f 'g. 'f -> 'e //│ where //│ 'c <: 'f -> 'd & 'f -> 'g //│ 'b <: 'c -> 'd -> 'e //│ forall 'h. 'h -> (forall 'i 'j 'k. 'k -> 'j //│ where //│ forall 'l. 'l -> (forall 'a 'm. 'm -> 'a //│ where //│ 'l <: 'a -> 'a & 'm -> 'a) <: 'h -> 'i -> 'j //│ 'h <: 'k -> 'i) <: 'c -> 'g -> 'e) //│ ╙── //│ c_i5_: 'b //│ where //│ 'b :> forall 'c. 'c -> (forall 'd 'e 'f 'g. 'f -> 'e //│ where //│ 'c <: 'f -> 'd & 'f -> 'g //│ 'b <: 'c -> 'd -> 'e //│ forall 'h. 'h -> (forall 'i 'j 'k. 'k -> 'j //│ where //│ forall 'l. 'l -> (forall 'a 'm. 'm -> 'a //│ where //│ 'l <: 'a -> 'a & 'm -> 'a) <: 'h -> 'i -> 'j //│ 'h <: 'k -> 'i) <: 'c -> 'g -> 'e) //│ = [Function (anonymous)] //│ constrain calls : 107 //│ annoying calls : 0 //│ subtyping calls : 378 :stats :e c_i10_ = c_mul_ c_i5_ c_i2_ //│ ╔══[ERROR] Inferred recursive type: nothing //│ ║ l.731: type Fint = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ╙── ^^ //│ ╔══[ERROR] Subtyping constraint of the form `forall ?a ?b ?c. ?b -> ?c -> ?a <: (forall ?d. ?d) -> ?e` exceeded recursion depth limit (250) //│ ║ l.1468: c_i10_ = c_mul_ c_i5_ c_i2_ //│ ║ ^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ c_i10_: error //│ = [Function (anonymous)] //│ constrain calls : 3360 //│ annoying calls : 0 //│ subtyping calls : 23503 ================================================ FILE: shared/src/test/diff/mlf-examples/ex_hashtbl.mls ================================================ :NoRecursiveTypes :NoJS // ============ Dummy definitions to type the examples ============ class List[a] method Get: a def nil: List['a] def cons[a]: a -> List[a] -> List[a] def isnil: List['a] -> bool def car: List['a] -> 'a def cdr: List['a] -> List['a] def fst: (('a, 'b),) -> 'a def snd: (('a, 'b),) -> 'b //│ Defined class List[+a] //│ Declared List.Get: List['a] -> 'a //│ nil: List[nothing] //│ cons: 'a -> List['a] -> List['a] //│ isnil: List[?] -> bool //│ car: List['a] -> 'a //│ cdr: List['a] -> List['a] //│ fst: (('a, anything,),) -> 'a //│ snd: ((anything, 'b,),) -> 'b // (* Use the value restriction ! *) // type option ('a) = None | Some of 'a class None class Some[a]: { val: a } type Option[a] = None | Some[a] def none: Option['a] def none = None {} def some: 'a -> Option['a] def some val = Some { val } //│ Defined class None //│ Defined class Some[+a] //│ Defined type alias Option[+a] //│ none: Option[nothing] //│ None //│ <: none: //│ Option[nothing] //│ some: 'a -> Option['a] //│ 'val -> Some['val] //│ <: some: //│ 'a -> Option['a] def match_opt: forall 'a 'r. (() -> 'r, 'a -> 'r) -> Option['a] -> 'r //│ match_opt: (() -> 'r, 'a -> 'r,) -> Option['a] -> 'r match_opt (n, s) opt = case opt of None -> n(), Some -> s(opt.val) //│ (() -> 'a, 'val -> 'a,) -> (None | Some[?] & {val: 'val}) -> 'a //│ <: match_opt: //│ (() -> 'r, 'a -> 'r,) -> Option['a] -> 'r // ============ The original examples ============ // let create_hashtbl () = [] def create_hashtbl () = nil //│ create_hashtbl: () -> List[nothing] // let hashtbl_add table key element = (key, element) :: table def hashtbl_add table key element = cons ((key, element)) table //│ hashtbl_add: List['a] -> 'b -> 'c -> List[('b, 'c,) | 'a] // let rec find table key = // if nil table then None // else if fst (car table) = key then Some (snd (car table)) // else find (cdr table) key rec def find table key = if isnil table then none else if eq (fst (car table)) key then some (snd (car table)) else find (cdr table) key //│ find: List[(anything, 'a,)] -> anything -> Option['a] // :ns // find :ng find(error:List[(int, string)])(unit) //│ res: Option[string] // * Versions with `_A` are versions we annotate that weren't originally annotated def find_A: forall 'a 'b. List[('a, 'b)] -> 'a -> Option['b] //│ find_A: List[(anything, 'b,)] -> anything -> Option['b] def find_A table key = if isnil table then none else if eq (fst (car table)) key then some (snd (car table)) else find_A (cdr table) key //│ List[(anything, 'b,)] -> anything -> Option['b] //│ <: find_A: //│ List[(anything, 'b,)] -> anything -> Option['b] // let nfind table key = // begin match find table key with // | None -> fun f x -> x // | Some n -> n // end def nfind table key = match_opt ( fun () -> fun f -> fun x -> x, fun v -> v ) (find table key) //│ nfind: List[(anything, 'a,)] -> anything -> (anything -> 'b -> 'b | 'a) // type Int = ['a] ('a -> 'a) -> ('a -> 'a) type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias ChurchInt // let succ (n:Int) = fun f x -> f (n f x) def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) //│ succ: ChurchInt -> ('a -> ('a & 'b)) -> 'a -> 'b // * Versions with `_` are versions where we removed the annotatation def succ_ n = fun f -> fun x -> f (n f x) //│ succ_: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd // let add (n:Int) (m:Int) = n succ m def add (n: ChurchInt) (m: ChurchInt) = n succ m def add_ n m = n succ_ m //│ add: ChurchInt -> ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) //│ add_: ((forall 'a 'b 'c 'd. ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd) -> 'e -> 'f) -> 'e -> 'f // let printInt (n:Int) = print (n (fun x -> x+1) 0) def printInt (n: ChurchInt) = toString (n (fun x -> x + 1) 0) def printInt_ n = toString (n (fun x -> x + 1) 0) //│ printInt: ChurchInt -> string //│ printInt_: ((int -> int) -> 0 -> anything) -> string // let table = create_hashtbl () // let table = hashtbl_add table "one" (fun f x -> f x) // let table = hashtbl_add table "two" (fun f x -> f (f x)) table = create_hashtbl () table = hashtbl_add table "one" (fun f -> fun x -> f x) table = hashtbl_add table "two" (fun f -> fun x -> f (f x)) //│ table: List[nothing] //│ table: List[("one", forall 'a 'b. ('a -> 'b) -> 'a -> 'b,)] //│ table: List[("one" | "two", forall 'a 'b 'c. ('a -> 'b & 'b -> 'c & 'a -> 'c) -> 'a -> 'c,)] // let zog = // printInt (add (nfind table "one") (nfind table "two")) :stats zog = printInt (add (nfind table "one") (nfind table "two")) //│ zog: string //│ constrain calls : 3206 //│ annoying calls : 56 //│ subtyping calls : 16169 // * Some subexpressions typed individually: :stats a = add (nfind table "one") //│ a: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) //│ constrain calls : 1556 //│ annoying calls : 28 //│ subtyping calls : 7972 :stats b = (nfind table "two") //│ b: ('a -> 'b & 'b -> 'c & 'a -> 'c & 'a -> 'd & 'd -> 'c & 'a -> 'e & 'e -> 'c & 'a -> 'f & 'f -> 'c & 'a -> 'g & 'g -> 'c) -> 'a -> ('c | 'a) //│ constrain calls : 767 //│ annoying calls : 28 //│ subtyping calls : 7102 :stats b (fun x -> x) //│ res: 'a -> 'a //│ constrain calls : 640 //│ annoying calls : 0 //│ subtyping calls : 4884 b (fun x -> x (not x)) //│ res: nothing -> nothing b (fun x -> 0) //│ res: 'a -> (0 | 'a) // * Note: this one required `:DistributeForalls` :stats a b //│ res: ('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt //│ constrain calls : 759 //│ annoying calls : 0 //│ subtyping calls : 4467 // * [FCP:patho] !! Note the stats here for this fully annotation-free version :stats // ========================================================================================== :Fuel 20000 zog_ = printInt_ (add_ (nfind table "one") (nfind table "two")) //│ zog_: string //│ constrain calls : 22066 //│ annoying calls : 56 //│ subtyping calls : 71169 :stats a_ = add_ (nfind table "one") //│ a_: ('a -> 'b -> 'c & 'a -> 'b -> 'd & 'a -> 'b -> 'e & 'a -> 'b -> 'f & 'a -> 'b -> 'g & 'a -> 'b -> 'h & 'a -> 'b -> 'i & 'a -> 'b -> 'j & 'a -> 'b -> 'k & 'a -> 'b -> 'l & 'a -> 'b -> 'm & 'a -> 'b -> 'n & 'a -> 'b -> 'o & 'a -> 'b -> 'p & 'a -> 'b -> 'q & 'a -> 'b -> 'r & 's) -> (('c -> 't & 'd -> 't & 'e -> 't & 'f -> 't & 'u -> 't & 'g -> 'u & 'v -> 't & 'h -> 'v & 'w -> 't & 'i -> 'w & 'x -> 't & 'j -> 'x & 'k -> 't & 'l -> 't & 'm -> 't & 'n -> 't & 'y -> 't & 'o -> 'y & 'z -> 't & 'p -> 'z & 'a1 -> 't & 'q -> 'a1 & 'b1 -> 't & 'r -> 'b1 & 'a) -> 'b -> 't | 's) //│ constrain calls : 1794 //│ annoying calls : 28 //│ subtyping calls : 15835 :stats a_ b //│ res: ('a -> 'b & 'b -> 'c & 'a -> 'c & 'a -> 'd & 'd -> 'c & 'a -> 'e & 'e -> 'c & 'a -> 'f & 'f -> 'c & 'a -> 'g & 'g -> 'c & 'h -> 'i & 'j -> 'h & 'k -> ('j & 'l) & 'l -> 'j & 'm -> 'i & 'n -> 'm & 'k -> ('n & 'o) & 'o -> 'n & 'p -> 'i & 'q -> 'p & 'k -> ('q & 'r) & 'r -> 'q & 's -> 'i & 't -> 's & 'k -> ('t & 'u) & 'u -> 't & 'v -> 'i & 'k -> ('v & 'w) & 'w -> 'v & 'x -> 'i & 'k -> ('x & 'y) & 'y -> 'x & 'z -> 'i & 'k -> ('z & 'a1) & 'a1 -> 'z & 'b1 -> 'i & 'k -> ('b1 & 'c1) & 'c1 -> 'b1 & 'd1 -> 'i & 'e1 -> 'd1 & 'k -> ('e1 & 'f1) & 'f1 -> 'e1 & 'g1 -> 'i & 'h1 -> 'g1 & 'k -> ('h1 & 'i1) & 'i1 -> 'h1 & 'j1 -> 'i & 'k1 -> 'j1 & 'k -> ('k1 & 'l1) & 'l1 -> 'k1 & 'm1 -> 'i & 'n1 -> 'm1 & 'k -> ('n1 & 'o1) & 'o1 -> 'n1 & 'p1 -> 'i & 'k -> ('p1 & 'q1) & 'q1 -> 'p1 & 'r1 -> 'i & 'k -> ('r1 & 's1) & 's1 -> 'r1 & 't1 -> 'i & 'k -> ('t1 & 'u1) & 'u1 -> 't1 & 'v1 -> 'i & 'k -> ('v1 & 'w1) & 'w1 -> 'v1) -> ('a & 'j & 'k & 'n & 'q & 't & 'v & 'x & 'z & 'b1 & 'e1 & 'h1 & 'k1 & 'n1 & 'p1 & 'r1 & 't1 & 'v1) -> ('c | 'a | 'i) //│ constrain calls : 8808 //│ annoying calls : 0 //│ subtyping calls : 182765 :ResetFuel // ========================================================================================== ================================================ FILE: shared/src/test/diff/mlf-examples/ex_predicative.mls ================================================ :NoRecursiveTypes // * In these tests, argument generalization is sometimes needed; I have annotated where. :GeneralizeArguments // type bot = 0;; type Bot = forall 'a. 'a //│ Defined type alias Bot // (* Rank 6 with intersection types, untypable in predicative System F, // typable in impredicative System F *) // let imp (z : bot) = // (fun (x : ['a] ('a -> 'u) -> 'v) -> x x) // (fun (y : 'a -> bot) -> y z y);; def imp (z: Bot) = (fun (x: forall 'a. ('a -> 'u) -> 'v) -> x x) (fun (y: 'a -> Bot) -> y z y) //│ imp: Bot -> nothing //│ = [Function: imp] def imp' z = (fun x -> x x) (fun y -> y z y) //│ imp': ('a -> 'a -> (forall 'b 'c. ('a -> 'b -> 'c & 'b) -> 'c) -> 'd & 'a) -> 'd //│ = [Function: imp$] // (* Quelques constantes. *) // type sid = ['a] 'a -> 'a type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let id = fun x -> x // ;; def id = fun x -> x //│ id: 'a -> 'a //│ = [Function: id] // let delta = fun (x:sid) -> x x // ;; def delta = fun (x: Sid) -> x x def delta' = fun x -> x x //│ delta: Sid -> Sid //│ = [Function: delta] //│ delta': ('a -> 'b & 'a) -> 'b //│ = [Function: delta$] // delta id ;; delta id delta' id //│ res: Sid //│ = [Function: id] //│ res: 'a -> 'a //│ = [Function: id] // let t a b c d e = (fun x -> a x x) ((fun y -> b (c y)) (d e)) ;; def t a b c d e = (fun x -> a x x) ((fun y -> b (c y)) (d e)) //│ t: ('a -> 'a -> 'b) -> ('c -> 'a) -> ('d -> 'c) -> ('e -> 'd) -> 'e -> 'b //│ = [Function: t] // let t w z a b = (fun y -> (fun x -> w (x y) z) a) b ;; def t w z a b = (fun y -> (fun x -> w (x y) z) a) b //│ t: ('a -> 'b -> 'c) -> 'b -> ('d -> 'a) -> 'd -> 'c //│ = [Function: t1] // let t y a = (fun x -> x (x y)) ((fun z -> z) (fun x -> a x)) ;; def t y a = (fun x -> x (x y)) ((fun z -> z) (fun x -> a x)) //│ t: 'a -> (('b | 'a) -> 'b) -> 'b //│ = [Function: t2] // (* Rank 1 *) // (fun x -> fun y -> x y) (fun y -> fun x -> x y) ;; // * Note that the `forall` is not distributed out because there's no enclosing polymorphic type here! (fun x -> fun y -> x y) (fun y -> fun x -> x y) //│ res: 'a -> (forall 'b. ('a -> 'b) -> 'b) //│ = [Function (anonymous)] // (* Rank 3 *) // (fun x -> fun y -> x) delta ;; (fun x -> fun y -> x) delta (fun x -> fun y -> x) delta' //│ res: anything -> Sid -> Sid //│ = [Function (anonymous)] //│ res: anything -> (forall 'a 'b. ('a -> 'b & 'a) -> 'b) //│ = [Function (anonymous)] // (* Rank 5 *) // (fun x -> fun y -> x y) (fun y -> fun x -> x y) delta ;; (fun x -> fun y -> x y) (fun y -> fun x -> x y) delta (fun x -> fun y -> x y) (fun y -> fun x -> x y) delta' //│ res: ((Sid -> Sid) -> 'a) -> 'a //│ = [Function (anonymous)] //│ res: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> 'c) -> 'c //│ = [Function (anonymous)] // (* Rank 8 *) // (fun (x:sid) -> x x x x x) id delta ;; (fun (x: Sid) -> x x x x x) id delta (fun x -> x x x x x) id delta' //│ res: Sid -> Sid //│ = [Function: delta] //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: delta$] // (* Rank 2 *) // type sk = ['a,'b] 'a -> 'b -> 'a;; // (fun (f:sk) -> fun x -> f f x) (fun v -> fun w -> v) ;; type Sk = forall 'a 'b. 'a -> 'b -> 'a (fun (f: Sk) -> fun x -> f f x) (fun v -> fun w -> v) (fun f -> fun x -> f f x) (fun v -> fun w -> v) //│ Defined type alias Sk //│ res: anything -> Sk //│ = [Function (anonymous)] //│ res: anything -> (forall 'a. 'a -> anything -> 'a) //│ = [Function (anonymous)] // (* Rank 3 *) // (fun t -> fun k -> t k) (fun (f:sk) -> fun x -> f f x) (fun v -> fun w -> v);; (fun t -> fun k -> t k) (fun (f: Sk) -> fun x -> f f x) (fun v -> fun w -> v) (fun t -> fun k -> t k) (fun f -> fun x -> f f x) (fun v -> fun w -> v) //│ res: anything -> Sk //│ = [Function (anonymous)] //│ res: anything -> (forall 'a. 'a -> anything -> 'a) //│ = [Function (anonymous)] // let k = fun x y -> x // let k' = fun x y -> y // let app = fun f x -> f x // let two = fun f x -> f (f x) // let three = fun f x -> f (f (f x)) def k = fun x -> fun y -> x def k2 = fun x -> fun y -> y def app = fun f -> fun x -> f x def two = fun f -> fun x -> f (f x) def three = fun f -> fun x -> f (f (f x)) //│ k: 'a -> anything -> 'a //│ = [Function: k] //│ k2: anything -> 'a -> 'a //│ = [Function: k2] //│ app: ('a -> 'b) -> 'a -> 'b //│ = [Function: app] //│ two: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: two] //│ three: ('a -> 'b & 'b -> 'c & 'c -> 'd) -> 'a -> 'd //│ = [Function: three] :e // * Note: this test case of ours works when generalizing the LHS of ascriptions (app id): Sid //│ ╔══[ERROR] Type error in type ascription //│ ║ l.154: (app id): Sid //│ ║ ^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.34: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.34: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.138: def app = fun f -> fun x -> f x //│ ╙── ^^^ //│ res: Sid //│ = [Function (anonymous)] // * this one works: let t = app id in t: Sid //│ res: Sid //│ = [Function (anonymous)] // type Int = ['a] ('a -> 'a) -> ('a -> 'a) // ;; type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias ChurchInt // (* Factorial of two and three *) // (* Only ONE annotation ! *) // let t y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun (n:Int) -> n (fun v -> k') k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k') (fun f -> fun x -> f (p k' f x))) (fun s -> s k' k') k) g) x)) two ;; // * [FCP-LIM] requires :GeneralizeArguments def t y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun (n: ChurchInt) -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two //│ t: ('b -> 'a -> 'a) -> ('c -> 'd & 'a -> 'a & 'b) -> ('a & 'c) -> ('d | 'a) //│ = [Function: t3] t id succ 0 //│ res: int //│ = 2 // * Same as above but WITHOUT the type annotation (unlike MLF) :e def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: ? -> (forall 'b. 'b) -> 'c //│ 'b :> forall 'd 'e. ((forall 'f. ? -> 'f -> 'f) -> (forall 'f. ? -> 'f -> 'f) -> 'd & 'e) -> (? -> 'e | 'd) | 'c //│ 'c :> 'g //│ <: (forall 'f. ? -> 'f -> 'f) -> (? -> ? -> ? & 'h) //│ 'g :> forall 'b 'i. 'i -> 'i | 'b //│ <: (forall 'f. ? -> 'f -> 'f) -> (? -> ? -> ? & 'h) //│ 'h <: (forall 'j. ? -> ? -> 'j -> 'j) -> (forall 'k. 'k -> ? -> 'k) -> (forall 'l 'm. ('l -> 'm) -> 'l -> 'm) -> (? -> 'n -> 'g) -> 'a & (forall 'o 'p 'q 'r 's. ((forall 'f. ? -> 'f -> 'f) -> ('o -> 'p -> 'q & 'r)) -> ('r -> (forall 't. ('q -> 't & 'o) -> 'p -> 't) -> 's) -> 's) -> (forall 'u. ((forall 'f. ? -> 'f -> 'f) -> (forall 'f. ? -> 'f -> 'f) -> 'u) -> 'u) -> (forall 'k. 'k -> ? -> 'k) -> ? & ? -> 'n -> 'g //│ 'n :> forall 'b. 'b //│ <: ? & 'g //│ ║ l.197: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.197: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ t_: (nothing -> anything) -> error //│ = [Function: t_] t_ id succ 0 //│ res: error //│ = 2 // * Works with CT: :ConstrainedTypes def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) two //│ t_: 'a -> 'b -> ('c -> 'd //│ where //│ forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g <: (forall 'h. 'h //│ where //│ forall 'i. 'i //│ where //│ forall 'j 'k 'l. 'j -> ((forall 'm. anything -> anything -> 'm -> 'm) -> (forall 'n. 'n -> anything -> 'n) -> (forall 'o 'p. ('o -> 'p) -> 'o -> 'p) -> (forall 'q 'r 's. 'q -> ('r -> 's //│ where //│ 'k <: (forall 't. 't //│ where //│ 'j <: (forall 'u. 'u //│ where //│ 'k <: (forall 'v 'w. 'v -> ((forall 'x. 'x //│ where //│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 //│ where //│ 'z <: (forall 'c1. 'c1 //│ where //│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1) -> 'b1)) -> 'w) -> 'w) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'u) -> 'q -> 't) -> 'r -> 's)) -> 'l & 'k) -> 'l <: (forall 'e1. 'e1 //│ where //│ forall 'j 'k 'l. 'j -> ((forall 'm. anything -> anything -> 'm -> 'm) -> (forall 'n. 'n -> anything -> 'n) -> (forall 'o 'p. ('o -> 'p) -> 'o -> 'p) -> (forall 'q 'r 's. 'q -> ('r -> 's //│ where //│ 'k <: (forall 't. 't //│ where //│ 'j <: (forall 'u. 'u //│ where //│ 'k <: (forall 'v 'w. 'v -> ((forall 'x. 'x //│ where //│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 //│ where //│ 'z <: (forall 'c1. 'c1 //│ where //│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1) -> 'b1)) -> 'w) -> 'w) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'u) -> 'q -> 't) -> 'r -> 's)) -> 'l & 'k) -> 'l <: (anything -> 'a) -> 'e1) -> 'i <: (forall 'f1. 'f1 //│ where //│ forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g <: (forall 'v 'g1. 'v -> ((forall 'x. 'x //│ where //│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'x) -> (forall 'z 'a1 'b1. 'z -> ('a1 -> 'b1 //│ where //│ 'z <: (forall 'c1. 'c1 //│ where //│ 'v <: (forall 'y. anything -> 'y -> 'y) -> 'z -> 'a1 -> 'c1) -> 'b1)) -> 'g1) -> 'g1) -> (forall 'd1. ((forall 'y. anything -> 'y -> 'y) -> (forall 'y. anything -> 'y -> 'y) -> 'd1) -> 'd1) -> (forall 'n. 'n -> anything -> 'n) -> 'f1) -> 'b -> 'h) -> 'c -> 'd) //│ = [Function: t_1] :NoConstrainedTypes t_ id succ 0 //│ res: int //│ = 2 // let t y = (fun h -> h (h (h (h (fun x -> y))))) (fun f -> fun (n:Int) -> n (fun v -> k') k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k') (fun f -> fun x -> f (p k' f x))) (fun s -> s k' k') k) g) x)) three // * [FCP-LIM] requires :GeneralizeArguments def t y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun (n: ChurchInt) -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three //│ t: ('b -> 'a -> 'a) -> ('c -> 'd & 'a -> 'a & 'b) -> ('a & 'c) -> ('d | 'a) //│ = [Function: t4] // * Same as above but WITHOUT the type annotation (unlike MLF) // * Interestingly this one does NOT require CT :Fuel 6000 :e // occurs-check def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three //│ ╔══[ERROR] Inferred recursive type: ? -> (((forall 'a. nothing -> ('a -> 'a | ?) | ?) | 'b) -> ((forall 'c. ? -> 'c -> 'c) -> nothing & 'd) & (forall 'e 'f. ((forall 'c. ? -> 'c -> 'c) -> (forall 'c. ? -> 'c -> 'c) -> 'e & 'f) -> (? -> 'f | 'e) | ? | 'd) -> ((forall 'c. ? -> 'c -> 'c) -> nothing & 'b)) //│ ║ l.285: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ t_: ('a -> ((forall 'b 'c. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'b & 'c) -> (anything -> 'c | 'b) | 'e | 'f | 'g | 'h | 'i | 'j) -> 'g & 'k)) -> ('a & 'l) -> ((forall 'd. anything -> 'd -> 'd) -> ((forall 'm 'n 'o 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'n -> 'o & 'p)) -> ('p -> (forall 's. ('o -> 's & 'm) -> 'n -> 's) -> 'q) -> ('r -> 'r | 'q) | 'k | 't) -> (forall 'u 'v. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'u & 'v) -> (anything -> 'v | 'u) | 'f | 'g | 'h | 'i | 'w) -> 'e & 'x) & 'i) -> ('g | 'h) //│ where //│ 'e :> forall 'y 'z. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'y & 'z & 'y) -> (anything -> 'z | 'y) | 'f | 'g | 'h | 'i //│ <: (forall 'd. anything -> 'd -> 'd) -> nothing -> nothing -> anything //│ 'f :> forall 'a1 'b1. ((forall 'c1 'd1. anything -> 'c1 -> ('d1 -> 'd1 | 'c1) | 'x) -> (forall 'e1. ('e -> 'e1 & 't) -> ('e & 'w) -> 'e1) -> 'a1) -> ('b1 -> 'b1 | 'a1) | 'g //│ <: (forall 'd. anything -> 'd -> 'd) -> (nothing -> nothing -> anything & 'f1) //│ 'g <: (forall 'd. anything -> 'd -> 'd) -> ((forall 'm 'n 'o 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'n -> 'o & 'p)) -> ('p -> (forall 's. ('o -> 's & 'm) -> 'n -> 's) -> 'q) -> ('r -> 'r | 'q) | 'k | 't) -> (forall 'g1 'h1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'g1 & 'h1) -> (anything -> 'h1 | 'g1) | 'f | 'g | 'h | 'i | 'w) -> 'e & 'x & 'f1) //│ 'f1 <: (forall 'i1. anything -> anything -> 'i1 -> 'i1) -> (forall 'j1. 'j1 -> anything -> 'j1) -> (forall 'k1 'l1. ('k1 -> 'l1) -> 'k1 -> 'l1) -> ('a -> ('e & 'w & 'j) -> 'g) -> 'l -> ('f -> ((forall 'd. anything -> 'd -> 'd) -> ((forall 'm 'n 'o 'p 'q 'r. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'n -> 'o & 'p)) -> ('p -> (forall 's. ('o -> 's & 'm) -> 'n -> 's) -> 'q) -> ('r -> 'r | 'q) | 'k | 't) -> (forall 'm1 'n1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'm1 & 'n1) -> (anything -> 'n1 | 'm1) | 'f | 'g | 'h | 'i | 'w) -> 'e & 'x) & 'h) & (forall 'o1 'p1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'o1 & 'p1) -> (anything -> 'p1 | 'o1) | 'g | 'h | 'i) -> 'f) & (forall 'm 'n 'o 'p 'q. ((forall 'd. anything -> 'd -> 'd) -> ('m -> 'n -> 'o & 'p)) -> ('p -> (forall 's. ('o -> 's & 'm) -> 'n -> 's) -> 'q) -> 'q) -> (forall 'q1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'q1) -> 'q1) -> (forall 'j1. 'j1 -> anything -> 'j1) -> anything & 'k -> (forall 'r1 's1. ((forall 'd. anything -> 'd -> 'd) -> (forall 'd. anything -> 'd -> 'd) -> 'r1 & 's1) -> (anything -> 's1 | 'r1) | 'f | 'g | 'h | 'i | 'j) -> 'g //│ = [Function: t_2] :ResetFuel // (* This comment is from the ORIGINAL PAGE: *) // (* Factorial of three. Exposed bugs in old implementation. This one works correctly, but the generated files take up to 3.8GB, and it takes about 1 hour on a 2GHz PIII box to get the result. You can see the final judgement here. Yes, the result is the Church numeral for 6. *) // (* We test that the result is 6: *) // let succ n = n + 1 ;; // t id succ 0 ;; t id succ 0 //│ res: int //│ = 6 :stats :e // * Strange recursive error. The bounds graph is quite large and hard to analyze for debugging... t_ id succ 0 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> int -> int //│ <: (forall 'b 'c 'd 'e 'f 'g 'h. ((forall 'i. ? -> 'i -> 'i) -> (forall 'i. ? -> 'i -> 'i) -> 'b & 'c) -> (? -> 'c | 'b) | ((forall 'i. ? -> 'i -> 'i) -> (forall 'i. ? -> 'i -> 'i) -> 'd & 'e & 'f) -> (? -> 'f | 'e | 'd) | nothing -> ('g -> 'g | ?) | 'j | ? | nothing -> ('h -> 'h | ?)) -> nothing //│ ║ l.285: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.313: t_ id succ 0 //│ ║ ^^^^^^^^^^ //│ ╟── function of type `?a -> ?b` is not an instance of type `int` //│ ║ l.285: def t_ y = (fun h -> h (h (h (fun x -> y)))) (fun f -> fun n -> n (fun v -> k2) k app (fun g -> fun x -> n (f (n (fun p -> fun s -> s (p k2) (fun f -> fun x -> f (p k2 f x))) (fun s -> s k2 k2) k) g) x)) three //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: error //│ = 6 //│ constrain calls : 218 //│ annoying calls : 0 //│ subtyping calls : 1556 // let t (z : 0) = let x = (fun (y: ['t > 0] 'a -> 't) -> y z y) in x x;; def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z y in x x //│ t: nothing -> nothing //│ = [Function: t5] // (* Plus petit. *) // type tt = ['b = 0] ['c > ['a] ['d = ['t] 'a -> 't] ['e] 'd -> 'e] 'b -> 'c ;; // let t (z : 0) = let x = (fun (y: ['t > 0] 'a -> 't) -> y z) in x x;; type Tt = forall 'b 'c. ('b & nothing) -> ('c | (forall 'a 'd 'e. ('d & (forall 't. 'a -> 't))) -> 'e) def t (z: nothing) = let x = fun (y: forall 't. 'a -> 't) -> y z in x x //│ Defined type alias Tt //│ t: nothing -> nothing //│ = [Function: t6] // (* // * Rank 3, untypable in System F? // (fun x -> z (x (fun f -> fun u -> f u)) (x (fun v -> fun g -> g v))) (fun y -> y y y) // * Note: `z` is free in the MLF commented example... TODO: Ask the MLF authors what they meant // * Untypable at any rank // (fun x -> x x) (fun x -> x x) :re :e (fun x -> x x) (fun x -> x x) //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.360: (fun x -> x x) (fun x -> x x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // * Rank 3, untypable in F2 but F3 // (fun f x -> f (f x)) (fun f x -> f (f x)) (fun v w -> v) :e (fun f -> fun x -> f (f x)) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ ╔══[ERROR] Inferred recursive type: ? -> ? -> ? //│ ║ l.374: (fun f -> fun x -> f (f x)) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ ╙── ^^^ //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] :RecursiveTypes (fun f -> fun x -> f (f x)) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] :NoRecursiveTypes // * Rank 4, alternate formulation, untypeable in F2, but F3 // (fun two k -> two two k)(fun f x -> f (f x)) (fun v w -> v) :e (fun two -> fun k -> two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ ╔══[ERROR] Inferred recursive type: ? -> ? -> ? //│ ║ l.398: (fun two -> fun k -> two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ ╙── ^^^ //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] :RecursiveTypes (fun two -> fun k -> two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ res: 'a -> anything -> 'b //│ where //│ 'a :> anything -> 'b //│ 'b :> anything -> 'a //│ = [Function (anonymous)] :NoRecursiveTypes // * Rank 5, causes huge blowup. Do not attempt to output skeletons ! // (fun two k -> two two two k)(fun f -x -> f (f x)) (fun v w -> v) // :d :e (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ ╔══[ERROR] Inferred recursive type: ? -> 'a //│ where //│ 'a :> ? -> ('a | ?) //│ ║ l.423: (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ ╙── ^^^ //│ res: 'a -> anything -> 'b //│ where //│ 'a :> forall 'c. anything -> 'b | 'c //│ 'b :> forall 'c. 'c //│ 'c :> anything -> ('b | 'a) //│ = [Function (anonymous)] :RecursiveTypes (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ res: 'a -> anything -> 'b //│ where //│ 'a :> forall 'c. 'c //│ 'c :> anything -> ('b | 'a) //│ 'b :> forall 'c. 'c //│ = [Function (anonymous)] :NoRecursiveTypes // * Also works with CT: :ConstrainedTypes (fun two -> fun k -> two two two k) (fun f -> fun x -> f (f x)) (fun v -> fun w -> v) //│ res: 'a -> 'b //│ where //│ forall 'c. 'c //│ where //│ forall 'd 'e 'f. 'd -> ('e -> 'f //│ where //│ 'd <: (forall 'g. 'g //│ where //│ 'd <: 'e -> 'g) -> 'f) <: (forall 'h. 'h //│ where //│ forall 'i. 'i //│ where //│ forall 'd 'j 'k. 'd -> ('j -> 'k //│ where //│ 'd <: (forall 'l. 'l //│ where //│ 'd <: 'j -> 'l) -> 'k) <: (forall 'd 'm 'n. 'd -> ('m -> 'n //│ where //│ 'd <: (forall 'o. 'o //│ where //│ 'd <: 'm -> 'o) -> 'n)) -> 'i <: (forall 'p. 'p -> anything -> 'p) -> 'h) -> 'c <: (forall 'q. 'q //│ where //│ forall 'c. 'c //│ where //│ forall 'd 'e 'f. 'd -> ('e -> 'f //│ where //│ 'd <: (forall 'g. 'g //│ where //│ 'd <: 'e -> 'g) -> 'f) <: (forall 'h. 'h //│ where //│ forall 'i. 'i //│ where //│ forall 'd 'j 'k. 'd -> ('j -> 'k //│ where //│ 'd <: (forall 'l. 'l //│ where //│ 'd <: 'j -> 'l) -> 'k) <: (forall 'd 'm 'n. 'd -> ('m -> 'n //│ where //│ 'd <: (forall 'o. 'o //│ where //│ 'd <: 'm -> 'o) -> 'n)) -> 'i <: (forall 'p. 'p -> anything -> 'p) -> 'h) -> 'c <: 'a -> 'q) -> 'b //│ = [Function (anonymous)] :NoConstrainedTypes // ????? // * Factorial of two, using the Y combinator // (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) // * This one still doesn't work with :RecursiveTypes :e :re (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: (nothing -> ('b | 'c) | 'd) -> 'e & (forall 'f 'g 'h 'i 'j. ((forall 'k. ? -> 'k -> 'k) -> 'f & (forall 'l. ? -> 'l -> 'l) -> 'g -> 'h -> 'i) -> ('f -> (forall 'm. ('i -> 'm & 'g) -> 'h -> 'm) -> 'j) -> 'j) -> (forall 'n. ((forall 'o. ? -> 'o -> 'o) -> (forall 'p. ? -> 'p -> 'p) -> 'n) -> 'n) -> (forall 'q. 'q -> ? -> 'q) -> 'a & (forall 'r. ? -> ? -> 'r -> 'r) -> (forall 'q. 'q -> ? -> 'q) -> (forall 's 't. ('s -> 't) -> 's -> 't) -> (((forall 'u. ? -> ? -> 'u -> 'u | ?) -> ((nothing -> ? -> ?) -> 'e & 'b) & (forall 'v. ? -> ? -> 'v -> 'v | ?) -> ((nothing -> ? -> ?) -> 'e & 'b) & (forall 'w. ? -> ? -> 'w -> 'w) -> (nothing -> ? -> ?) -> 'e & (forall 'x. ? -> ? -> 'x -> 'x | ?) -> ((nothing -> ? -> ?) -> 'e & 'b) & (forall 'y. ? -> ? -> 'y -> 'y | ?) -> ((nothing -> ? -> ?) -> 'e & 'b) & 'd & 'z) -> nothing -> 'c) -> 'z -> ((forall 'a1. ? -> ? -> 'a1 -> 'a1) -> (nothing -> ? -> ?) -> 'e & 'd) //│ 'c :> forall 'b1 'c1. (? & 'b1) -> (? -> 'c1 -> 'c1 | 'b1 | ?) //│ <: (nothing -> ? -> ?) -> 'e //│ 'e <: (forall 'd1. ? -> ? -> 'd1 -> 'd1 | ?) -> 'c //│ ║ l.502: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.502: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.502: (fun h -> (fun x -> h (x x)) (fun x -> h (x x))) (fun f -> fun n -> n (fun v -> fun x -> fun y -> y) k (fun f -> fun x -> f x)(fun g -> fun x -> n (f (n (fun p -> fun s -> s (p (fun x -> fun y -> y)) (fun f -> fun x -> f (p (fun x -> fun y -> y) f x))) (fun s -> s (fun f -> fun x -> x) (fun f -> fun x -> x)) k) g) x)) (fun f -> fun x -> f (f x)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // *) ================================================ FILE: shared/src/test/diff/mlf-examples/ex_propagate.mls ================================================ :NoRecursiveTypes :DontDistributeForalls // (* Use option -pa *) // type sid = ['a] 'a -> 'a type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let delta = (fun x -> x x : sid -> 'b) // let k = ((fun x y -> y) : ['a] 'a -> sid) def delta = (fun x -> x x) : Sid -> 'b def delta_ = fun x -> x x def k = (fun x -> fun y -> y) : forall 'a. 'a -> Sid def k_ = fun x -> fun y -> y //│ delta: Sid -> Sid //│ = [Function: delta] //│ delta_: ('a -> 'b & 'a) -> 'b //│ = [Function: delta_] //│ k: anything -> Sid //│ = [Function: k] //│ k_: anything -> (forall 'a. 'a -> 'a) //│ = [Function: k_] // let id x = x // let id' = (id : sid -> sid) def id x = x def id_ = id : Sid -> Sid //│ id: 'a -> 'a //│ = [Function: id] //│ id_: Sid -> Sid //│ = [Function: id_] // (* Next: We do not give enough information. *) // untype fun x -> ((fun y -> y y) : 'a -> sid) fun x -> ((fun y -> y y) : 'a -> Sid) fun x_ -> (fun y -> y y) //│ res: anything -> ('a -> Sid & 'a) -> Sid //│ = [Function: res] //│ res: anything -> (forall 'a 'b. ('a -> 'b & 'a) -> 'b) //│ = [Function: res] // let (delta:sid -> sid) = fun x -> x x def delta = (fun x -> x x) : Sid -> Sid //│ delta: Sid -> Sid //│ = [Function: delta1] // let (delta:sid -> 'b) = fun x -> x x def delta = (fun x -> x x) : Sid -> 'b //│ delta: Sid -> Sid //│ = [Function: delta2] // untype let (delta:'a -> sid) = fun x -> x x in delta let delta = (fun x -> x x) : 'a -> Sid in delta //│ res: ('a -> Sid & 'a) -> Sid //│ = [Function (anonymous)] // ———————————— Distribtivity ———————————— :DistributeForalls // * Interestingly, these ones change from the ones above fun x -> ((fun y -> y y) : 'a -> Sid) //│ res: anything -> ('a -> ??a -> ??a0 & 'a) -> Sid //│ = [Function: res] let delta = (fun x -> x x) : 'a -> Sid in delta //│ res: ('a -> ??a -> ??a0 & 'a) -> Sid //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/mlf-examples/ex_rvr_validate.mls ================================================ :NoRecursiveTypes // (* Value restriction. *) // let id x = x // let x = id id in x 1, x true // let x = (id id:'a) in x 1, x true // ;; def id x = x let x = id id in (x 1, x true) let x = id id : 'a in (x 1, x true) //│ id: 'a -> 'a //│ = [Function: id] //│ res: (1, true,) //│ = [ 1, true ] //│ res: (1, true,) //│ = [ 1, true ] // type sid = ['a] 'a -> 'a // ;; type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let annot x = (x:sid) // ;; def annot x = x : Sid //│ annot: Sid -> Sid //│ = [Function: annot] // let delta x = let y = (x:sid) in y y // ;; def delta x = let y = x : Sid in y y //│ delta: Sid -> Sid //│ = [Function: delta] // let delta x = let y = (annot x) in y y // ;; def delta x = let y = annot x in y y //│ delta: Sid -> Sid //│ = [Function: delta1] ================================================ FILE: shared/src/test/diff/mlf-examples/ex_selfapp.mls ================================================ :NoRecursiveTypes // (* // * This example was written by Susumu NISHIMURA // * According to him, it requires impredicative polymorphism. // *) // (* my list definition *) // type mylist ('b) = Nil | Cons of ('b * mylist ('b));; // * ML-style version class Ls[A] method Head: A def cons: ('a, Ls['a]) -> Ls['a] def nil: Ls['a] def elim: Ls['a] -> (('a, Ls['a]) -> 'r, () -> 'r) -> 'r //│ Defined class Ls[+A] //│ Declared Ls.Head: Ls['A] -> 'A //│ cons: ('a, Ls['a],) -> Ls['a] //│ = //│ nil: Ls[nothing] //│ = //│ elim: Ls['a] -> (('a, Ls['a],) -> 'r, () -> 'r,) -> 'r //│ = // * Structural version class Nil class Cons[a]: { head: a; tail: List[a] } type List[a] = Nil | Cons[a] Nil = Nil {} Cons (head, tail) = Cons { head; tail } //│ Defined class Nil //│ Defined class Cons[+a] //│ Defined type alias List[+a] //│ Nil: Nil //│ = Nil {} //│ Cons: ('head & 'a, List['a] & 'tail,) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] // let rec foo = // fun xs -> // begin match xs with // Nil -> Nil // | Cons (x,xs) -> Cons (x+1, foo (foo xs)) // end // * Note: doesn't work with :precise-rec-typing rec def foo xs = elim xs ( fun (h, t) -> cons (h + 1, foo (foo t)), fun () -> nil ) //│ foo: Ls[int] -> Ls[int] //│ = //│ elim is not implemented // * An simplified version, easier to type check, just for the record rec def foo xs = elim xs ( fun (h, t) -> cons (h + 1, foo t), fun () -> nil ) //│ foo: Ls[int] -> Ls[int] //│ = //│ elim is not implemented // * === === === * :RecursiveTypes rec def foo = fun xs -> case xs of Nil -> Nil, Cons -> Cons (xs.head + 1, foo (foo xs.tail)) //│ foo: 'a -> (Nil | 'b) //│ where //│ 'b :> Cons[int] with {tail: Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: foo2] // * An simplified version, easier to type check, just for the record rec def foo = fun xs -> case xs of { Nil -> Nil | Cons -> Cons (xs.head + 1, foo xs.tail) } //│ foo: 'a -> (Nil | 'b) //│ where //│ 'b :> Cons[int] with {tail: Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: foo3] :NoRecursiveTypes // * === === === * // (* Type def: ba = forall b.(int->b->b)->b->b *) // type ba = ['b] (int -> 'b -> 'b) -> 'b -> 'b // (* z can be typed more polymorphic. *) // type baa = ['a, 'b] ('a -> 'b -> 'b)-> 'b -> 'b type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b //│ Defined type alias Ba //│ Defined type alias Baa // * LP: this Baa type is a bit weird. It's equivalent to: def b: (nothing -> 'b -> 'b) -> 'b -> 'b //│ b: (nothing -> 'b -> 'b) -> 'b -> 'b //│ = // * LP: indeed: b: Baa //│ res: Baa //│ = //│ b is not implemented :ng b = error: Baa //│ Baa //│ <: b: //│ (nothing -> 'b -> 'b) -> 'b -> 'b // (* build and foldr *) // let build = // fun (g : ['b] (('a -> 'b -> 'b) -> 'b -> 'b)) -> // g (fun x xs -> Cons (x,xs)) Nil def build_ = fun g -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ build_: ((forall 'head 'a 'tail. ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail})) -> Nil -> 'b) -> 'b //│ = [Function: build_] :e // * Expected: List is a structural equirecursive types and recursive types are disabled build_ : forall 'a. (forall 'b. (('a -> 'b -> 'b) -> 'b -> 'b)) -> List['a] //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> Cons[?] with {tail: forall 'a. Nil | 'a} //│ ║ l.130: def build_ = fun g -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing type ascription; a type annotation may be required //│ ║ l.135: build_ : forall 'a. (forall 'b. (('a -> 'b -> 'b) -> 'b -> 'b)) -> List['a] //│ ║ ^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> List['a] //│ = [Function: build_] :e // * Works with :RecursiveTypes due to structural typing (see below) def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> Cons[?] with {tail: forall 'a. Nil | 'a} //│ ║ l.149: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.149: def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ build: (forall 'b. ('head -> 'b -> 'b) -> 'b -> 'b) -> (Nil | error | 'a) //│ where //│ 'a :> Cons['head] with {tail: forall 'a. Nil | 'a} //│ = [Function: build] :RecursiveTypes def build = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ build: (forall 'b. ('head -> 'b -> 'b) -> 'b -> 'b) -> (Nil | 'a) //│ where //│ 'a :> Cons['head] with {tail: forall 'a. Nil | 'a} //│ = [Function: build1] :NoRecursiveTypes def build2_ = fun g -> g (fun x -> fun xs -> cons (x, xs)) nil //│ build2_: ((forall 'a. 'a -> Ls['a] -> Ls['a]) -> Ls[nothing] -> 'b) -> 'b //│ = //│ cons is not implemented // * Perfect! build2_ : forall 'a. (forall 'b. (('a -> 'b -> 'b) -> 'b -> 'b)) -> Ls['a] //│ res: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = //│ build2_ and cons are not implemented def build2 = fun (g: forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> g (fun x -> fun xs -> cons (x, xs)) nil //│ build2: (forall 'b. ('a -> 'b -> 'b) -> 'b -> 'b) -> Ls['a] //│ = //│ cons is not implemented // let rec foldr = // fun k z xs -> // begin match xs with // Nil -> z // | Cons (x, xs) -> k x (foldr k z xs) // end rec def foldr k z xs = elim xs ( fun (h, t) -> k h (foldr k z t), fun () -> z ) //│ foldr: ('a -> 'b -> 'b) -> 'b -> Ls['a] -> 'b //│ = //│ elim is not implemented :e rec def foldr = fun k -> fun z -> fun xs -> case xs of Nil -> z, Cons -> k xs.head (foldr k z xs.tail) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {head: ?, tail: Cons[?] & 'a} //│ ║ l.210: case xs of //│ ╙── ^^ //│ foldr: ('head -> 'a -> 'a) -> 'a -> 'b -> 'a //│ where //│ 'b <: (Cons[?] with {head: 'head, tail: 'b}) | Nil //│ = [Function: foldr1] // (* encoding `foo' with foldr *) // let (z : baa) = fun c n -> n def z = (fun c -> fun n -> n) : Baa def z_ = fun c -> fun n -> n //│ z: Baa //│ = [Function: z] //│ z_: anything -> 'a -> 'a //│ = [Function: z_] // let rec (k : int -> ba -> ba) = fun x (xs : ba) c n -> c (x+1) (xs k z c n) def k: int -> Ba -> Ba //│ k: int -> Ba -> Ba //│ = // * Note: annotation in `(xs: Ba)` is needed def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ int -> Ba -> (int -> ('b -> 'a & 'b -> 'b)) -> 'b -> 'a //│ <: k: //│ int -> Ba -> Ba //│ = //│ k is not implemented // * [FCP-IMPROV] annotation in `z` was not needed def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z_ c n) //│ int -> Ba -> (int -> 'b -> ('b & 'a)) -> 'b -> 'a //│ <: k: //│ int -> Ba -> Ba //│ = //│ k and k are not implemented // * Versions with unannotated recursive defs: // * The following tests don't benefit from `:RecursiveTypes` but enabling it reduces the number of shown errors // * === === === * :RecursiveTypes // * Notice the extrusion, yielding an imprecise type :e rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ int -> Ba -> (int -> anything -> ??b) -> ??b -> anything //│ <: k: //│ int -> Ba -> Ba //│ ╔══[ERROR] Type error in binding of lambda expression //│ ║ l.269: rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b //│ ║ ^^ //│ ╟── back into type variable `'b` //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b //│ ╙── ^^ //│ ╔══[ERROR] Type error in def definition //│ ║ l.269: rec def k = fun x -> fun (xs: Ba) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'b` leaks out of its scope //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b //│ ║ ^^ //│ ╟── into type `'b0` //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b //│ ╙── ^^ //│ = [Function: k1] rec def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k_ z_ c n) //│ k_: 'k_ //│ where //│ 'k_ :> int -> ('k_ -> (forall 'a. anything -> 'a -> 'a) -> 'b -> 'c -> 'd) -> (int -> 'd -> 'e & 'b) -> 'c -> 'e //│ = [Function: k_] :e k = k_ //│ 'k_ //│ where //│ 'k_ :> int -> ('k_ -> (forall 'a. anything -> 'a -> 'a) -> 'b -> 'c -> 'd) -> (int -> 'd -> 'e & 'b) -> 'c -> 'e //│ <: k: //│ int -> Ba -> Ba //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.304: k = k_ //│ ║ ^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b -> ?b)` does not match type `'b` //│ ║ l.229: def z_ = fun c -> fun n -> n //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.100: type Ba = forall 'b. (int -> 'b -> 'b) -> 'b -> 'b //│ ╙── ^^ //│ = [Function: k_] // (* untyped terms *) // (* let z = fun c -> fun n -> n *) // (* let rec k = fun x xs c cn -> c (x+1) (xs k z c n) *) // (* System F terms *) // (* let z = /\b -> fun (c : int -> b -> b) -> fun (n : b) -> n *) // (* let rec (k : int -> (forall b.(int->b->b)->b->b) *) // (* -> (forall b.(int->b->b)->b->b)) = *) // (* \(a : int) -> \(as : forall b.(int->b->b)->b->b) -> *) // (* /\b -> \(c : int->b->b) -> \(n : b) -> *) // (* ((as[forall b.(int->b->b)->b->b] k z)[b] c z) *) // (* definition with build and foldr *) // let bfoo xs = build (foldr k z xs) def bfoo xs = build (foldr k z xs) //│ bfoo: 'a -> (Nil | 'b) //│ where //│ 'b :> Cons[int] with {tail: forall 'b. Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: bfoo] :e def bfoo_ xs = build_ (foldr k_ z_ xs) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.347: def bfoo_ xs = build_ (foldr k_ z_ xs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?b -> (forall ?c. ?c -> ?c)` does not match type `Cons[?a] | Nil` //│ ║ l.229: def z_ = fun c -> fun n -> n //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.33: type List[a] = Nil | Cons[a] //│ ║ ^^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.130: def build_ = fun g -> g (fun x -> fun xs -> Cons (x, xs)) Nil //│ ╙── ^^ //│ bfoo_: 'a -> error //│ where //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: bfoo_] // * Alt (requires :RecursiveTypes): def bfoo_ xs = build_ (foldr k z_ xs) //│ bfoo_: 'a -> (Nil | 'b) //│ where //│ 'b :> Cons[int] with {tail: forall 'b. Nil | 'b} //│ 'a <: (Cons[?] with {head: int, tail: 'a}) | Nil //│ = [Function: bfoo_1] lst = Cons (0, Cons (0, Cons (0, Nil))) //│ lst: Cons[0] with {tail: Cons[0] with {tail: Cons[0] with {tail: Nil}}} //│ = Cons { //│ head: 0, //│ tail: Cons { head: 0, tail: Cons { head: 0, tail: Nil {} } } //│ } // (* test run *) // let lst0 = Cons (0, Cons (0, Cons (0, Cons (0, Nil))));; lst0 = Cons (0, Cons (0, Cons (0, Cons (0, Nil)))) //│ lst0: Cons[0] with {tail: Cons[0] with {tail: Cons[0] with {tail: Cons[0] with {tail: Nil}}}} //│ = Cons { //│ head: 0, //│ tail: Cons { head: 0, tail: Cons { head: 0, tail: [Cons] } } //│ } // foo lst0;; foo lst0 //│ res: Nil | 'a //│ where //│ 'a :> Cons[int] with {tail: Nil | 'a} //│ = Cons { //│ head: 1, //│ tail: Cons { head: 1, tail: Cons { head: 1, tail: [Cons] } } //│ } // bfoo lst0;; bfoo lst0 //│ res: Nil | 'a //│ where //│ 'a :> Cons[int] with {tail: forall 'a. Nil | 'a} //│ = Cons { //│ head: 1, //│ tail: Cons { head: 2, tail: Cons { head: 4, tail: [Cons] } } //│ } bfoo_ lst0 //│ res: Nil | 'a //│ where //│ 'a :> Cons[int] with {tail: forall 'a. Nil | 'a} //│ = Cons { //│ head: 1, //│ tail: Cons { head: 2, tail: Cons { head: 4, tail: [Cons] } } //│ } :NoRecursiveTypes // * === === === * // (* This does not type : // type baa = ['a, 'b] ('a -> 'b -> 'b)-> 'b -> 'b // let rec (k : int -> baa -> baa) = fun x (xs : baa) c n -> c (x+1) (xs k z c n) // *) def k: int -> Baa -> Baa //│ k: int -> Baa -> Baa //│ = :e def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ int -> Baa -> (int -> 'a -> 'b) -> 'a -> 'b //│ <: k: //│ int -> Baa -> Baa //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.434: def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` //│ ║ l.434: def k = fun x -> fun (xs: Baa) -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b //│ ╙── ^^ //│ = //│ k is not implemented // * Using `k` here on purpose to simulate an annotated rec def def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ k_: int -> ((int -> Baa -> Baa) -> Baa -> 'a -> 'b -> 'c) -> (int -> 'c -> 'd & 'a) -> 'b -> 'd //│ = //│ k and k are not implemented :e k = k_ // nope //│ int -> ((int -> Baa -> Baa) -> Baa -> 'a -> 'b -> 'c) -> (int -> 'c -> 'd & 'a) -> 'b -> 'd //│ <: k: //│ int -> Baa -> Baa //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.458: k = k_ // nope //│ ║ ^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` //│ ║ l.452: def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k z c n) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Baa = forall 'a 'b. ('a -> 'b -> 'b) -> 'b -> 'b //│ ╙── ^^ //│ = //│ k_, k and k are not implemented :e // occurs-check rec def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k_ z c n) //│ ╔══[ERROR] Inferred recursive type: 'k_ //│ where //│ 'k_ :> int -> ('k_ -> Baa -> 'a -> 'b -> 'c) -> (int -> 'c -> 'd & 'a) -> 'b -> 'd //│ ╙── //│ k_: 'k_ //│ where //│ 'k_ :> int -> ('k_ -> Baa -> 'a -> 'b -> 'c) -> (int -> 'c -> 'd & 'a) -> 'b -> 'd //│ = [Function: k_1] :e k = k_ // nope //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'c -> 'd //│ 'c :> 'e -> 'f -> 'd //│ <: 'g //│ 'e :> int -> 'g -> 'e -> 'f -> 'd //│ <: int -> 'a & ? -> 'b -> 'b //│ 'b :> 'e -> 'f -> 'd //│ <: 'g //│ 'g <: (int -> 'g -> 'e -> 'f -> 'd) -> Baa -> 'h //│ 'd :> 'e -> 'f -> 'd //│ <: 'c & 'h //│ 'h <: 'e -> (Baa | 'f) -> 'c //│ ║ l.476: rec def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k_ z c n) //│ ╙── ^^^^^^^^^ //│ 'k_ //│ where //│ 'k_ :> int -> ('k_ -> Baa -> 'a -> 'b -> 'c) -> (int -> 'c -> 'd & 'a) -> 'b -> 'd //│ <: k: //│ int -> Baa -> Baa //│ ╔══[ERROR] Cyclic-looking constraint while typing def definition; a type annotation may be required //│ ║ l.487: k = k_ // nope //│ ║ ^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ = [Function: k_1] :e // occurs-check rec def k_ = fun x -> fun xs -> fun c -> fun n -> c (x + 1) (xs k_ z_ c n) //│ ╔══[ERROR] Inferred recursive type: 'k_ //│ where //│ 'k_ :> int -> ('k_ -> (forall 'a. ? -> 'a -> 'a) -> 'b -> 'c -> 'd) -> (int -> 'd -> 'e & 'b) -> 'c -> 'e //│ ╙── //│ k_: 'k_ //│ where //│ 'k_ :> int -> ('k_ -> (forall 'a. anything -> 'a -> 'a) -> 'b -> 'c -> 'd) -> (int -> 'd -> 'e & 'b) -> 'c -> 'e //│ = [Function: k_2] // k = k_ // nope ================================================ FILE: shared/src/test/diff/mlf-examples/ex_shallow.mls ================================================ :NoRecursiveTypes // type sid = ['a] 'a -> 'a;; // type nat = ['a] ('a -> 'a) -> 'a -> 'a type Sid = forall 'a. 'a -> 'a type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias Sid //│ Defined type alias ChurchInt // let auto (x:sid) = x x // let id x = x // let k x y = x def auto (x: Sid) = x x def auto_ x = x x def id x = x def k x y = x //│ auto: Sid -> Sid //│ = [Function: auto] //│ auto_: ('a -> 'b & 'a) -> 'b //│ = [Function: auto_] //│ id: 'a -> 'a //│ = [Function: id] //│ k: 'a -> anything -> 'a //│ = [Function: k] // (* church_zero is like the sequence *) // let church_zero = fun f -> id def church_zero = fun f -> id //│ church_zero: anything -> 'a -> 'a //│ = [Function: church_zero] // (* church_un is like apply *) // let church_one = fun f x -> f x def church_one = fun f -> fun x -> f x //│ church_one: ('a -> 'b) -> 'a -> 'b //│ = [Function: church_one] // (* church_un is like apply_twice *) // let church_two = fun f x -> f (f x) def church_two = fun f -> fun x -> f (f x) //│ church_two: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: church_two] // let church_succ n = fun f x -> f (n f x) def church_succ n = fun f -> fun x -> f (n f x) //│ church_succ: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: church_succ] // (* We build the a0 of the icfp version paper *) // type a1 = ['b] ['a > sid] ('a -> 'b) -> 'b // let a1 = fun f -> f id;; // let a3 = (fun z z' -> z church_two);; // let a2 (x:a1) = (x auto); (fun y -> x y; y church_succ) a3;; // let a0 = a2 a1;; // * The MLF type above could be encoded as: // type A1 = forall 'a 'b. (('a | Sid) -> 'b) -> 'b // * However that's unnecessary as this is equivalent to the type below, // * which MLF cannot express directly due to its rigid syntactic limitations: type A1 = forall 'b. (Sid -> 'b) -> 'b //│ Defined type alias A1 def a1 = fun f -> f id def a3 = fun z -> fun _ -> z church_two def a2 (x: A1) = let _ = x auto in (fun y -> let _ = x y in y church_succ ) a3 def a2_ (x: A1) = let _ = x auto_ in (fun y -> let _ = x y in y church_succ ) a3 //│ a1: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: a1] //│ a3: ((forall 'a 'b 'c. ('a -> 'b & 'b -> 'c) -> 'a -> 'c) -> 'd) -> anything -> 'd //│ = [Function: a3] //│ a2: A1 -> anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a2] //│ a2_: A1 -> anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a2_] def a0 = a2 a1 //│ a0: anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0] def a0_ = a2_ a1 //│ a0_: anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0_] // let test0 = // (fun f -> f (fun h -> h id)) // (fun (g:a1) -> // k (g (fun (x:sid) -> x x)) // ((fun t -> (fun u v -> v) (g t) (t (fun f x -> x))) // (fun x -> ((fun f x -> f x) x) (fun f x -> x)))) // ;; def test0 = (fun f -> f (fun h -> h id)) (fun (g: A1) -> k (g (fun (x: Sid) -> x x)) ((fun t -> (fun u -> fun v -> v) (g t) (t (fun f -> fun x -> x))) (fun x -> ((fun f -> fun x -> f x) x) (fun f -> fun x -> x)))) //│ test0: Sid //│ = [Function: test0] def test0_ = (fun f -> f (fun h -> h id)) (fun g -> k (g (fun x -> x x)) ((fun t -> (fun u -> fun v -> v) (g t) (t (fun f -> fun x -> x))) (fun x -> ((fun f -> fun x -> f x) x) (fun f -> fun x -> x)))) //│ test0_: 'a -> 'a //│ = [Function: test0_] // (** Encoding in F+eta **) // (* we repace sa by sb and build a coercion function form sb to sc *) // type a1_sid = ['b] ['a = sid] ('a -> 'b) -> 'b;; // type a1_nat = ['b] ['a = nat -> nat] ('a -> 'b) -> 'b;; // type A1_sid = forall 'a 'b. (('a & Sid | Sid) -> 'b) -> 'b // type A1_nat = forall 'a 'b. (('a & ChurchInt -> ChurchInt | ChurchInt -> ChurchInt) -> 'b) -> 'b // * Simplified: type A1_sid = forall 'b. (Sid -> 'b) -> 'b type A1_nat = forall 'a 'b. ((ChurchInt -> ChurchInt) -> 'b) -> 'b //│ Defined type alias A1_sid //│ Defined type alias A1_nat // let a1_sid_to_nat (g : a1_sid) = // let r = fun f -> g (fun (x : sid) -> f x) in // (r : a1_nat);; def a1_sid_to_nat (g: A1_sid) = let r = fun f -> g (fun (x: Sid) -> f x) in (r: A1_nat) //│ a1_sid_to_nat: A1_sid -> A1_nat //│ = [Function: a1_sid_to_nat] def a1_sid_to_nat_ g = let r = fun f -> g (fun x -> f x) in r //│ a1_sid_to_nat_: (('a -> 'b) -> 'c) -> ('a -> 'b) -> 'c //│ = [Function: a1_sid_to_nat_] // (* the term is as above but abstract over sb and uses the coercion *) // let test_in_F_eta = // (fun f -> f (fun h -> h id)) // (fun (g : a1_sid) -> // k (g (fun (x : sid) -> x x)) // ((fun t -> (fun u v -> v) ((a1_sid_to_nat g) t) (t (fun f x -> x))) // (fun x -> ((fun f x -> f x) x) (fun f x -> x)))) // ;; def test_in_F_eta = (fun f -> f (fun h -> h id)) (fun (g: A1_sid) -> k (g (fun (x : Sid) -> x x)) ((fun t -> (fun u -> fun v -> v) ((a1_sid_to_nat g) t) (t (fun f -> fun x -> x))) (fun x -> ((fun f -> fun x -> f x) x) (fun f -> fun x -> x)))) //│ test_in_F_eta: Sid //│ = [Function: test_in_F_eta] def test_in_F_eta_ = (fun f -> f (fun h -> h id)) (fun g -> k (g (fun x -> x x)) ((fun t -> (fun u -> fun v -> v) ((a1_sid_to_nat_ g) t) (t (fun f -> fun x -> x))) (fun x -> ((fun f -> fun x -> f x) x) (fun f -> fun x -> x)))) //│ test_in_F_eta_: 'a -> 'a //│ = [Function: test_in_F_eta_] // (* // (* Le terme dans la syntaxe de Joe Wells *) // (fn f => f (fn h => h id)) // (fn g => // k (g (fn x => x x)) // ((fn t => (fn u v => v) (g t) (t (fn f x => x))) // (fn x => ((fn f x => f x) x) (fn f x => x)))) // *) // (* We can also build a version of a0 that should not be in F+eta. // To do that we require x to return its argument. // *) // type a1' = ['a > sid] ('a -> 'a) -> 'a -> 'a;; // type A1' = forall 'a. (('a | Sid) -> ('a | Sid)) -> ('a | Sid) -> ('a | Sid) // * Or (no need to change the negative occurrences): type A1' = forall 'a. (('a | Sid) -> 'a) -> 'a -> ('a | Sid) // * Since 'a occurs both positively and negatively, we cannot simplify it to the following: // type A1' = (Sid -> Sid) -> Sid -> Sid //│ Defined type alias A1' // let either x y = if true then x else y;; // let a1' = fun f x -> f (f (either id x));; // let a2' (x:a1') = x auto id; (fun y -> x y church_two; y church_succ) a3;; // let a0' = a2' a1';; def either x y = if true then x else y //│ either: 'a -> 'a -> 'a //│ = [Function: either] def a1' = fun f -> fun x -> f (f (either id x)) //│ a1': ((forall 'a. 'a -> 'a | 'b) -> 'c & 'c -> 'd) -> 'b -> 'd //│ = [Function: a1$] def a2' (x: A1') = let _ = x auto id in (fun y -> let _ = x y church_two in y church_succ ) a3 //│ a2': A1' -> anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a2$] def a2'_ x = let _ = x auto_ id in (fun y -> let _ = x y church_two in y church_succ ) a3 //│ a2'_: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> (forall 'c. 'c -> 'c) -> anything & (forall 'd. ((forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g) -> 'd) -> anything -> 'd) -> (forall 'e 'f 'g. ('e -> 'f & 'f -> 'g) -> 'e -> 'g) -> anything) -> anything -> ('h -> 'i & 'j -> 'k & 'k -> 'h) -> 'j -> 'i //│ = [Function: a2$_] :e // * [FCP-LIM] current limitation due to the union type in A1' (we don't currently handle them precisely) def a0' = a2' a1' //│ ╔══[ERROR] Type error in application //│ ║ l.235: def a0' = a2' a1' //│ ║ ^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.5: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.212: def a1' = fun f -> fun x -> f (f (either id x)) //│ ║ ^ //│ ╟── • this reference: //│ ║ l.212: def a1' = fun f -> fun x -> f (f (either id x)) //│ ║ ^^ //│ ╟── • this reference: //│ ║ l.208: def either x y = if true then x else y //│ ║ ^ //│ ╟── • this reference: //│ ║ l.235: def a0' = a2' a1' //│ ║ ^^^ //│ ╟── • this reference: //│ ║ l.15: def id x = x //│ ╙── ^ //│ a0': error | anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0$] :e def a0'_ = a2'_ a1' //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.265: def a0'_ = a2'_ a1' //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ a0'_: error | anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0$_] :RecursiveTypes // * Note: requires :DistributeForalls def a0'_ = a2'_ a1' //│ a0'_: anything -> ('a -> 'b & 'c -> 'd & 'd -> 'a) -> 'c -> 'b //│ = [Function: a0$_1] :NoRecursiveTypes // type a1'_sid = ['a = sid] ('a -> 'a) -> 'a -> 'a;; // type a1'_nat = ['a = nat] ('a -> 'a) -> 'a -> 'a;; type A1'_sid = forall 'a. (('a & Sid | Sid) -> ('a & Sid | Sid)) -> ('a & Sid | Sid) -> ('a & Sid | Sid) type A1'_nat = forall 'a. (('a & ChurchInt | ChurchInt) -> ('a & ChurchInt | ChurchInt)) -> ('a & ChurchInt | ChurchInt) -> ('a & ChurchInt | ChurchInt) //│ Defined type alias A1'_sid //│ Defined type alias A1'_nat // (* There is no coercion from a1'_sid to a1'_nat, hence we can conjecture that a0' // is not in F+eta. *) // * === With Constrained Types === :DontDistributeForalls :ConstrainedTypes def a1 = fun f -> f id def a3 = fun z -> fun _ -> z church_two def a2 (x: A1) = let _ = x auto in (fun y -> let _ = x y in y church_succ ) a3 def a2_ (x: A1) = let _ = x auto_ in (fun y -> let _ = x y in y church_succ ) a3 //│ a1: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: a11] //│ a3: 'a -> (forall 'b. anything -> 'b //│ where //│ 'a <: (forall 'c 'd 'e. ('c -> 'd & 'd -> 'e) -> 'c -> 'e) -> 'b) //│ = [Function: a31] //│ a2: A1 -> (forall 'a. anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a) //│ = [Function: a21] //│ a2_: A1 -> (forall 'a. anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a) //│ = [Function: a2_1] def a0 = a2 a1 //│ a0: anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a01] def a0_ = a2_ a1 //│ a0_: anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a0_1] def a1' = fun f -> fun x -> f (f (either id x)) //│ a1': 'a -> (forall 'b 'c 'd. 'b -> 'd //│ where //│ 'a <: (forall 'e. 'e -> 'e | 'b) -> 'c & 'c -> 'd) //│ = [Function: a1$1] :e // occurs-check def a2' (x: A1') = let _ = x auto id in (fun y -> let _ = x y church_two in y church_succ ) a3 //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> forall 'a 'b 'c 'd. nothing -> ('b -> 'c | 'd) //│ where //│ Sid | 'a <: (forall 'b 'e 'c. ('b -> 'e & 'e -> 'c) -> 'b -> 'c) -> 'd //│ ║ l.198: type A1' = forall 'a. (('a | Sid) -> 'a) -> 'a -> ('a | Sid) //│ ╙── ^^ //│ a2': A1' -> (forall 'a. anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a) //│ = [Function: a2$1] def a2'_ x = let _ = x auto_ id in (fun y -> let _ = x y church_two in y church_succ ) a3 //│ a2'_: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> (forall 'c. 'c -> 'c) -> anything & (forall 'd. 'd -> (forall 'e. anything -> 'e //│ where //│ 'd <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'e)) -> (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> anything) -> (forall 'i. anything -> 'i //│ where //│ forall 'j 'k 'l 'm. ('j -> 'k -> 'l) -> ('l -> 'm & 'j) -> 'k -> 'm <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'i) //│ = [Function: a2$_1] :e // * [FCP-LIM] see previous version above def a0' = a2' a1' //│ ╔══[ERROR] Type error in application //│ ║ l.382: def a0' = a2' a1' //│ ║ ^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.5: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.15: def id x = x //│ ║ ^^^^^ //│ ╟── • this reference: //│ ║ l.343: def a1' = fun f -> fun x -> f (f (either id x)) //│ ║ ^^ //│ ╟── • this reference: //│ ║ l.208: def either x y = if true then x else y //│ ║ ^ //│ ╟── • this reference: //│ ║ l.382: def a0' = a2' a1' //│ ║ ^^^ //│ ╟── • this function: //│ ║ l.350: def a2' (x: A1') = //│ ║ ^^^^^^^^^^ //│ ║ l.351: let _ = x auto id in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.352: (fun y -> //│ ║ ^^^^^^^^^^^ //│ ║ l.353: let _ = x y church_two in //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.354: y church_succ //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.355: ) a3 //│ ║ ^^^^^^^ //│ ╟── • this reference: //│ ║ l.382: def a0' = a2' a1' //│ ╙── ^^^ //│ a0': error | anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a0$1] :e def a0'_ = a2'_ a1' //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.424: def a0'_ = a2'_ a1' //│ ║ ^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ a0'_: error | anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a0$_2] :RecursiveTypes def a0'_ = a2'_ a1' //│ a0'_: anything -> 'a //│ where //│ forall 'b 'c 'd 'e. ('b -> 'c -> 'd) -> ('d -> 'e & 'b) -> 'c -> 'e <: (forall 'f 'g 'h. ('f -> 'g & 'g -> 'h) -> 'f -> 'h) -> 'a //│ = [Function: a0$_3] :NoRecursiveTypes ================================================ FILE: shared/src/test/diff/mlf-examples/ex_validate.mls ================================================ :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail def cons[a]: a -> List[a] -> List[a] def cons head tail = Cons { head; tail } //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] class Nil[a]: List[a] method Head = error method Tail = this def nil: List['a] nil = Nil {} //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} // ============ Type signatures for functions used in the examples ============ def head: forall 'a. List['a] -> 'a def head l = l.Head //│ head: List['a] -> 'a //│ = //│ List['a] -> 'a //│ <: head: //│ List['a] -> 'a //│ = [Function: head] def tail: forall 'a. List['a] -> List['a] def tail l = l.Tail //│ tail: List['a] -> List['a] //│ = //│ List['a] -> List['a] //│ <: tail: //│ List['a] -> List['a] //│ = [Function: tail] def is_empty: forall 'a. List['a] -> bool def is_empty l = eq l nil //│ is_empty: List[?] -> bool //│ = //│ anything -> bool //│ <: is_empty: //│ List[?] -> bool //│ = [Function: is_empty] def concat: forall 'a. List['a] -> List['a] -> List['a] rec def concat l1 l2 = if is_empty l1 then l2 else cons (head l1) (concat (tail l1) l2) //│ concat: List['a] -> List['a] -> List['a] //│ = //│ List['a] -> List['a] -> List['a] //│ <: concat: //│ List['a] -> List['a] -> List['a] //│ = [Function: concat1] def print_bool: bool -> unit def print_bool b = log b //│ print_bool: bool -> unit //│ = //│ anything -> unit //│ <: print_bool: //│ bool -> unit //│ = [Function: print_bool] def print_int: int -> unit def print_int i = log i //│ print_int: int -> unit //│ = //│ anything -> unit //│ <: print_int: //│ int -> unit //│ = [Function: print_int] def print_string: string -> unit def print_string s = log s //│ print_string: string -> unit //│ = //│ anything -> unit //│ <: print_string: //│ string -> unit //│ = [Function: print_string] def print x = log x //│ print: anything -> unit //│ = [Function: print] // (* Ces exemples permettent de tester durement le syst�me de types. *) // type id = ['a] 'a -> 'a type Id = forall 'a. 'a -> 'a //│ Defined type alias Id // let choose x y = if true then x else y // let succ n = n + 1 // let z = (succ : int -> int) def choose x y = if true then x else y def succ n = n + 1 def z = succ : int -> int //│ choose: 'a -> 'a -> 'a //│ = [Function: choose] //│ succ: int -> int //│ = [Function: succ] //│ z: int -> int //│ = [Function: z] // let id x = x // let z = choose (id:id) id // let z = (choose id succ) 10 // let z = (choose succ id) 10 def id x = x z = choose (id : Id) id z = (choose id succ) 10 z = (choose succ id) 10 //│ id: 'a -> 'a //│ = [Function: id] //│ z: 'a -> 'a | Id //│ = [Function: id] //│ z: int //│ = 10 //│ z: int //│ = 11 // untype fun x -> x x fun x -> x x //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] // let delta (x:id) = x x def delta (x: Id) = x x def delta_ x = x x //│ delta: Id -> Id //│ = [Function: delta] //│ delta_: ('a -> 'b & 'a) -> 'b //│ = [Function: delta_] // let z = delta (delta id) def z = delta (delta id) //│ z: Id //│ = [Function: z4] def z_ = delta_ (delta_ id) //│ z_: 'a -> 'a //│ = [Function: z_] // untype delta succ :e delta succ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.176: delta succ //│ ║ ^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.118: type Id = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.125: def succ n = n + 1 //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.118: type Id = forall 'a. 'a -> 'a //│ ╙── ^^ //│ res: error | Id //│ = 'function succ(n) { return n + 1;}1' :e delta_ succ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.192: delta_ succ //│ ║ ^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `int` //│ ║ l.125: def succ n = n + 1 //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.192: delta_ succ //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.125: def succ n = n + 1 //│ ╙── ^ //│ res: error | int //│ = 'function succ(n) { return n + 1;}1' // let z = delta (choose id id) def z = delta (choose id id) //│ z: Id //│ = [Function: z5] def z_ = delta_ (choose id id) //│ z_: 'a -> 'a //│ = [Function: z_1] // let step g = (g 1) - (g 0) // let apply_to_id f = f id // let test1 = apply_to_id step // let test2 = apply_to_id delta def step g = (g 1) - (g 0) def apply_to_id f = f id test1 = apply_to_id step test2 = apply_to_id delta test2_ = apply_to_id delta_ //│ step: ((0 | 1) -> int) -> int //│ = [Function: step] //│ apply_to_id: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: apply_to_id] //│ test1: int //│ = 1 //│ test2: Id //│ = [Function: id] //│ test2_: 'a -> 'a //│ = [Function: id] // type s = ['a] list ('a) -> bool // let annote_s (x:id) = (x : s -> s) type S = forall 'a. List['a] -> bool def annote_s (x: Id) = x : S -> S def annote_s_ x = x : S -> S //│ Defined type alias S //│ annote_s: Id -> S -> S //│ = [Function: annote_s] //│ annote_s_: (S -> S) -> S -> S //│ = [Function: annote_s_] // let isnil l = l = [] def isnil l = eq l nil //│ isnil: anything -> bool //│ = [Function: isnil] // passif value_restriction // let t = fun x y -> // let isnil = (annote_s x) y in // (isnil [ 1 ; 2 ; 3] , isnil [] , isnil ["ok"] , isnil [true]) // in t id isnil let t = fun x -> fun y -> let isnil = (annote_s x) y in (isnil (cons 1 (cons 2 (cons 3 nil))), isnil nil, isnil (cons "ok" nil), isnil (cons true nil)) in t id isnil let t = fun x -> fun y -> let isnil = (annote_s_ x) y in (isnil (cons 1 (cons 2 (cons 3 nil))), isnil nil, isnil (cons "ok" nil), isnil (cons true nil)) in t id isnil //│ res: (bool, bool, bool, bool,) //│ = [ false, true, false, false ] //│ res: (bool, bool, bool, bool,) //│ = [ false, true, false, false ] // untype fun x y -> // let isnil = (x:id) y in // (isnil [ 1 ; 2 ; 3] , isnil [] , isnil ["ok"] , isnil [true]) fun x -> fun y -> let isnil = (x : Id) y in (isnil (cons 1 (cons 2 (cons 3 nil))), isnil nil, isnil (cons "ok" nil), isnil (cons true nil)) fun x -> fun y -> let isnil = x y in (isnil (cons 1 (cons 2 (cons 3 nil))), isnil nil, isnil (cons "ok" nil), isnil (cons true nil)) //│ res: Id -> (List[1 | 2 | 3] -> 'a & List[nothing] -> 'b & List["ok"] -> 'c & List[true] -> 'd) -> ('a, 'b, 'c, 'd,) //│ = [Function: res] //│ res: ('a -> (List[1 | 2 | 3] -> 'b & List[nothing] -> 'c & List["ok"] -> 'd & List[true] -> 'e)) -> 'a -> ('b, 'c, 'd, 'e,) //│ = [Function: res] // (*let t1 = f delta // let t2 = f step*) // let partage x = choose id x // let departage x = choose id (x:id) def partage x = choose id x def departage x = choose id (x : Id) //│ partage: 'a -> ('b -> 'b | 'a) //│ = [Function: partage] //│ departage: Id -> ('a -> 'a | Id) //│ = [Function: departage] // let delta' = fun x -> (departage x) x // untype fun x -> partage x x def delta' = fun x -> (departage x) x def delta'_ = fun x -> (partage x) x //│ delta': (Id & 'a) -> 'a //│ = [Function: delta$] //│ delta'_: ('a -> 'b & 'a & 'b) -> 'b //│ = [Function: delta$_] // let t = fun x -> (x:id) x, x // let t = fun (x:id) -> x x, (x:id) // let t = fun (x:id) -> x x, x // untype fun x -> x (x:id), x // untype fun x -> x x, (x:id) def t = fun x -> ((x : Id) x, x) def t = fun (x: Id) -> (x x, (x : Id)) def t = fun (x: Id) -> (x x, x) def t = fun x -> (x (x : Id), x) def t = fun x -> (x x, x : Id) def t_ = fun x -> (x x, x) //│ t: (Id & 'a) -> ('a, 'a,) //│ = [Function: t] //│ t: Id -> (Id, Id,) //│ = [Function: t1] //│ t: Id -> (Id, Id,) //│ = [Function: t2] //│ t: (Id & Id -> 'a & 'b) -> ('a, 'b,) //│ = [Function: t3] //│ t: (Id & 'a -> 'b & 'a) -> ('b, Id,) //│ = [Function: t4] //│ t_: ('a -> 'b & 'a) -> ('b, 'a,) //│ = [Function: t_] // untype fun x -> (x:id), x x // * [FCP-IMPROV] It works for us fun x -> (x : Id, x x) //│ res: (Id & 'a -> 'b & 'a) -> (Id, 'b,) //│ = [Function: res] // let ig x = () (* ignore *) def ig x = null //│ ig: anything -> null //│ = [Function: ig] // (* The following is not typable in System F (afaik) *) // let aa f = f id // untype fun g -> ig (g delta) ; (fun t -> ig (g t) ; t succ) (fun x -> succ (x 8)) // let ff = fun (g:['a > id] ['b] ('a -> 'b) -> 'b) -> // ig (g delta) ; (fun t -> ig (g t) ; t succ) (fun x -> succ (x 8)) def aa f = f id //│ aa: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: aa] def ff = fun (g: forall 'a 'b. (('a | Id) -> 'b) -> 'b) -> let _ = ig (g delta) in (fun t -> let _ = ig (g t) in t succ ) (fun x -> succ (x 8)) //│ ff: (forall 'b. (Id -> 'b) -> 'b) -> int //│ = [Function: ff] // * [FCP-IMPROV] It works for us! def ff_ = fun g -> let _ = ig (g delta) in (fun t -> let _ = ig (g t) in t succ ) (fun x -> succ (x 8)) //│ ff_: (((Id & 8 -> int) -> (int | Id)) -> anything) -> int //│ = [Function: ff_] // let bigtest = ff aa // * Probably due to the union type used above (not yet well supported) :e bigtest = ff aa //│ ╔══[ERROR] Type error in application //│ ║ l.376: bigtest = ff aa //│ ║ ^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.118: type Id = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.118: type Id = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this applied expression: //│ ║ l.349: def aa f = f id //│ ║ ^ //│ ╟── • this reference: //│ ║ l.349: def aa f = f id //│ ║ ^^ //│ ╟── • this reference: //│ ║ l.376: bigtest = ff aa //│ ║ ^^ //│ ╟── • this reference: //│ ║ l.138: def id x = x //│ ╙── ^ //│ bigtest: error | int //│ = 10 bigtest_ = ff_ aa //│ bigtest_: int //│ = 10 // (* Variable libre / variable li�e dans les annotations. *) // let example f = (f:['a] 'a -> int -> 'b) f // let mkpair x y = (x,y) def example f = (f: forall 'a. 'a -> int -> 'b) f def example_ f = f f def mkpair x y = (x, y) //│ example: (anything -> int -> 'b) -> int -> 'b //│ = [Function: example] //│ example_: ('a -> 'b & 'a) -> 'b //│ = [Function: example_] //│ mkpair: 'a -> 'b -> ('a, 'b,) //│ = [Function: mkpair] // untype example mkpair // (* �a echoue parce qu'on instancie 'b avec un type contenant 'a *) // (* Il faudrait que le message d'erreur soit plus clair. *) example mkpair example_ mkpair //│ res: int -> (??a, int,) //│ = [Function (anonymous)] //│ res: 'a -> (forall 'b 'c. 'b -> 'c -> ('b, 'c,), 'a,) //│ = [Function (anonymous)] // (* Exemple MLF avec des fonctions et des constructions r�cursives. *) // let rec x = 1 :: y // and y = 2 :: x :ng rec def p = { x = cons 1 p.y; y = cons 2 p.x } def x = p.x def y = p.y //│ p: {x: List[1 | 2], y: List[1 | 2]} //│ x: List[1 | 2] //│ y: List[1 | 2] // let print_int x = print_string "#"; print_int x; print_string " " // let intprint x = print_int x def print_int x = let _ = print_string "#" in let _ = print_int x in print_string " " def intprint x = print_int x //│ int -> unit //│ <: print_int: //│ int -> unit //│ = [Function: print_int1] //│ intprint: int -> unit //│ = [Function: intprint] // let myNil l = l = [] def myNil l = eq l nil //│ myNil: anything -> bool //│ = [Function: myNil] // let rec append x l = // if l = [] then [x] // else (car l)::(append x (cdr l)) rec def append x l = if eq l nil then cons x nil else cons (head l) (append x (tail l)) //│ append: 'a -> List['a] -> List['a] //│ = [Function: append] // let rec map l f = // if l = [] then [] // else (f (car l)) :: (map (cdr l) f) rec def map l f = if eq l nil then nil else cons (f (head l)) (map (tail l) f) //│ map: List['a] -> ('a -> 'b) -> List['b] //│ = [Function: map] // let message () = print "** MESSAGE **" def message (_: unit) = print "** MESSAGE **" //│ message: unit -> unit //│ = [Function: message] // let intfa f = f 15 def intfa f = f 15 //│ intfa: (15 -> 'a) -> 'a //│ = [Function: intfa] // let polyann (a:'x) (b:int) f (g:int -> 'x -> 'y) = g (f 10) a b def polyann (a: 'x) (b: int) f (g: int -> 'x -> 'y) = g (f 10) a b //│ polyann: 'x -> int -> (10 -> int) -> (int -> 'x -> int -> 'a) -> 'a //│ = [Function: polyann] def polyann_ a b f g = g (f 10) a b //│ polyann_: 'a -> 'b -> (10 -> 'c) -> ('c -> 'a -> 'b -> 'd) -> 'd //│ = [Function: polyann_] // let voyage (f:'p -> 'q) x y = (y, f x) def voyage (f: 'p -> 'q) x y = (y, f x) //│ voyage: ('p -> 'a) -> 'p -> 'b -> ('b, 'a,) //│ = [Function: voyage] def voyage_ f x y = (y, f x) //│ voyage_: ('a -> 'b) -> 'a -> 'c -> ('c, 'b,) //│ = [Function: voyage_] // let main = // let print_sep s l = // let max_depth = 20 in // let rec alpha = print_list // and print_list n l = // if not(n < 0) then // if nil l then print_string "]" // else // begin // intprint (car l); // print_string s; // print_list (n-1) (cdr l) // end // else // begin // print_string ("... ]"); // (* failwith ("T�te de liste : "^(string_of_int (car l))) *) // end // in // print_string "["; // print_list max_depth l; // in // print (myNil [1;2]); // print " "; // print (myNil ["foo";"bar"]); // print "\n"; // let ll = [10; 20; 30; 40] @ [50; 60; 70] in // let ll2 = append 80 ll in // print_sep "; " ll2; // print_string "\n"; // print_sep "" x; // () :stats :ng def main = let print_sep = fun s -> fun l -> let max_depth = 20 in let rec p = { alpha = p.print_list; print_list = fun n -> fun l -> if not (n < 0) then if is_empty l then print_string "]" else let _ = intprint (head l) in let _ = print_string s in p.print_list (n - 1) (tail l) else print_string "... ]" } in let alpha = p.alpha in let print_list = p.print_list in let _ = print_string "[" in print_list max_depth l in let _ = print (myNil (cons 1 (cons 2 nil))) in let _ = print " " in let _ = print (myNil (cons "foo" (cons "bar" nil))) in let _ = print "\n" in let ll = concat (cons 10 (cons 20 (cons 30 (cons 40 nil)))) (cons 50 (cons 60 (cons 70 nil))) in let ll2 = append 80 ll in let _ = print_sep "; " ll2 in let _ = print_string "\n" in let _ = print_sep "" x in null //│ main: null //│ constrain calls : 1743 //│ annoying calls : 94 //│ subtyping calls : 5536 // (* Des probl�mes d'unification � gogo. *) // type sid = ['a] 'a -> 'a // type siid = ['a,'b] ('a -> 'b) -> ('a -> 'b) // type sdup = ['a,'b] ('a -> 'a) -> ('b -> 'b) // type sdip = ['a] ('a -> 'a) -> ('a -> 'a) // type i = int // type j = bool // (* PAGE 1 *) // please unify ['a > ['a0 > sid] i -> 'a0] 'a -> 'a // (* with *) : ['c > sdup] (i -> 'c) -> (i -> 'c) // (* Result should be *) // = ['a0 > sdip] ['a = i -> 'a0] 'a -> 'a // please unify ['a > ['a0 > sid] i -> 'a0] ['b > ['b0 > siid] j -> 'b0] 'a -> 'b // : ['c > sdup] (i -> 'c) -> (j -> 'c) // = ['c0 > sdip] (i -> 'c0) -> (j -> 'c0) // please unify ['a > sid, 'b > sdup] 'a -> 'b : sid // = ['a > sdip] 'a -> 'a // please failon_unify ['a > sid, 'b = sdup] 'a -> 'b : sid // please unify ['a > sid, 'b = siid] 'a -> 'b : sid // = ['a = siid] 'a -> 'a // please unify ['a > ['a1 > sid, 'a2 > siid] 'a1 -> 'a2] 'a -> 'a // : ['c > ['c2 > siid] (i -> i) -> 'c2] 'c -> 'c // = ['a > ['c2 > siid] (i -> i) -> 'c2] 'a -> 'a // please unify ['a > ['a0 > siid] ('a0 -> i) -> i] 'a -> 'a // : ['a > ['a0 > sdup] 'a0 -> i] ('a -> i) -> ('a -> i) // = ['c0 > sdip, 'c = 'c0 -> i] ('c -> i) -> ('c -> i) // please unify ['a > sid] ['b1 > ['c] 'c -> 'a] ['c1 > ['c3] 'c3 -> 'a] 'b1 -> 'c1 // : ['a > sid] ['b2 > ['c] 'c -> i -> i, 'c2 = ['c3] 'c3 -> 'a] 'b2 -> 'c2 // = ['a = i -> i] ['b1 > ['c] 'c -> 'a] ['c1 = ['c3] 'c3 -> 'a] 'b1 -> 'c1 // please unify ['a > sid] ['b > sdup] ('b -> 'b) -> 'a // : ['c > sid] 'c -> 'c // = ['b > sdup] ('b -> 'b) -> ('b -> 'b) // please failon_unify ['a] ['b = ['b0] 'b0 -> 'a] 'a -> 'b : sid // (* PAGE 2 *) // please unify ['a1 = ['b1 = sdup] ['c] 'c -> 'b1] 'a1 -> i // : ['b2 = sdup] ['a2 = ['c3] 'c3 -> 'b2] 'a2 -> i // = ['b2 = sdup] ['a2 = ['c3] 'c3 -> 'b2] 'a2 -> i // please unify ['a1 = ['a2 = ['a3 = sdup] ['c] 'c -> 'a3 -> 'a3] 'a2 -> i] 'a1 -> i // : ['a1 = ['a3 = sdup] ['a2 = ['c] 'c -> 'a3 -> 'a3] 'a2 -> i] 'a1 -> i // = ['a1 = ['a3 = sdup] ['a2 = ['c] 'c -> 'a3 -> 'a3] 'a2 -> i] 'a1 -> i // please failon_unify ['a1 = ['a2 > sdup] 'a2 -> 'a2] 'a1 -> i // : ['a2 = sdup] ['a1 = 'a2 -> 'a2] 'a1 -> i // please failon_unify ['a1 = ['a2 > sdup] ['a3 = ['a4 > siid] 'a2 -> 'a4] ['a5] 'a5 -> 'a3 -> i] 'a1 -> i // : ['b2 > sdup] ['b3 = ['b4 > siid] 'b2 -> 'b4] ['b1 = ['b5] 'b5 -> 'b3 -> i] 'b1 -> i // please unify ['a1 = ['a2 = sdup, 'a3 = sdup] 'a2 -> 'a3] 'a1 -> i // : ['a1 = ['a = sdup] 'a -> 'a] 'a1 -> i // = ['a1 = ['a = sdup] 'a -> 'a] 'a1 -> i // please unify ['a1 = ['a2 = sdup, 'a3 = sdup] 'a2 -> 'a3] 'a1 -> i // : ['a1 > ['a] 'a -> 'a] 'a1 -> i // = ['a1 = ['a = sdup] 'a -> 'a] 'a1 -> i // please unify ['a1 = ['a2 = ['a3 = sdup] ['b] 'b -> 'a3 -> i] ['c] 'c -> 'a2 -> i] 'a1 -> i // : ['a3 = sdup] ['a1 = ['a2 = ['b] 'b -> 'a3 -> i] ['c] 'c -> 'a2 -> i] 'a1 -> i // = ['a3 = sdup] ['a1 = ['a2 = ['b] 'b -> 'a3 -> i] ['c] 'c -> 'a2 -> i] 'a1 -> i // please failon_unify ['a1 = ['a2 = ['a3 > sdup] ['b] 'b -> 'a3 -> i] ['c] 'c -> 'a2 -> i] 'a1 -> i // : ['a3 = sdup] ['a2 = ['b] 'b -> 'a3 -> i] ['a1 = ['c] 'c -> 'a2 -> i] 'a1 -> i // please unify ['a = ['a2 = ['b1 = sdup] i -> 'b1] ['a1 > ['c] 'c -> 'a2] 'a1 -> 'a1] 'a -> 'a // : ['b1 = sdup] ['a = ['a1 > ['c] 'c -> i -> 'b1] 'a1 -> 'a1] 'a -> 'a // = ['b1 = sdup] ['a = ['a1 > ['c] 'c -> i -> 'b1] 'a1 -> 'a1] 'a -> 'a // please unify ['b > sid, 'a = ['a1 = siid, 'a2 = ['c] 'a1 -> 'b -> 'c, 'a3 = ['c] 'a1 -> 'c] 'a2 -> 'a3] 'a -> 'a // : ['a = ['a1 = siid, 'a2 = ['c] 'a1 -> (i -> i) -> 'c, 'a3 = ['c] 'a1 -> 'c] 'a2 -> 'a3] 'a -> 'a // = ['a = ['a1 = siid, 'a2 = ['c] 'a1 -> (i -> i) -> 'c, 'a3 = ['c] 'a1 -> 'c] 'a2 -> 'a3] 'a -> 'a // please unify ['a1 = ['a2 = ['a3 = siid] i -> 'a3] j -> 'a2] i -> 'a1 // : ['a3 = siid] i -> j -> i -> 'a3 // = ['a3 = siid] i -> j -> i -> 'a3 // please failon_unify ['a1 = ['a2 = ['a3 = sdup] ['b] 'b -> 'a3 -> i] ['c] 'c -> 'a2 -> i] 'a1 -> i // : ['a3 = sdup] ['a1 = ['a2 > ['b] 'b -> 'a3 -> i] ['c] 'c -> 'a2 -> i] 'a1 -> i // please unify ['a1 = ['a2 = ['a3 = ['a4 = ['a5 = 0] 'a5 -> i] // ('a4 -> i) -> i] // ('a3 -> i) -> i] 'a2 -> i] 'a1 -> i // : ['b1 = ['b2 = ['b3 = ['b4 = 0] ('b4 -> i) -> i] // ('b3 -> i) -> i] // ('b2 -> i) -> i] 'b1 -> i // = ['c1 = ['c5 = 0] ((((('c5 -> i) -> i) -> i) -> i) -> i) -> i] 'c1 -> i // please unify ['a1 > ['a2 > ['a3 > ['a4 > ['a5 > siid] 'a5 -> i] // ('a4 -> i) -> i] // ('a3 -> i) -> i] 'a2 -> i] 'a1 -> i // : ['b1 > ['b2 > ['b3 > ['b4 > sdup] ('b4 -> i) -> i] // ('b3 -> i) -> i] // ('b2 -> i) -> i] 'b1 -> i // = ['c1 > ['c5 > sdip] ((((('c5 -> i) -> i) -> i) -> i) -> i) -> i] 'c1 -> i // (* Avec deux quantificateurs � remonter. *) // please unify ['a2 > ['a4 > ['a6,'a7] ('a6 -> 'a7) -> i ] ('a4 -> i) -> i ] ('a2 -> i) -> i // : ['b1 > ['b3 > ['b5 > ['b6,'b7] 'b6 -> 'b7 ] ('b5 -> i) -> i ] ('b3 -> i) -> i ] 'b1 -> i // = ['c6,'c7] ((((('c6 -> 'c7) -> i) -> i) -> i) -> i) -> i // (* Cet exemple cr�e un graphe mal form�. *) // please failon_unify ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> list ('c)] 'b -> 'a // (* Plus simple *) // please failon_unify ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'c] 'b -> 'a // (* Extrusion automatique de binders. *) // please unify ['a] ['b > ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'c] 'b -> 'a // = ['c] ('c -> 'c) -> 'c // (* Plus compliqu�. *) // please unify ['a] ['b > ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> list ('c)] 'b -> 'a // = ['c] ('c -> list ('c)) -> list ('c) // (* Variante *) // please unify ['a] ['b > ['c] ['d] 'c -> 'd] 'b -> 'a // : ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // = ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // please failon_unify ['a] ['b = ['c] ['d] 'c -> 'd] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'a] 'b -> 'a // please unify ['a] ['b > ['c] ['d > ['e] 'c -> 'e] 'c -> 'd] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'a] 'b -> 'a // = ['c] ['a > ['e] 'c -> 'e] ('c -> 'a) -> 'a // please unify ['a] ['b > ['f] ['c] ['d > ['e] 'c -> 'e] 'f * 'c * 'd] 'b -> 'a // : ['a] ['b > ['f] ['c] 'f * 'c * 'a] 'b -> 'a // = ['c] ['a > ['e] 'c -> 'e] ['b > ['f] ('f * 'c * 'a)] 'b -> 'a // please failon_unify ['a] ['b = ['c] ['d > ['e] 'c -> 'e] 'c -> 'd] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'a] 'b -> 'a // please failon_unify ['a] ['b > ['c] ['d > ['e] 'c -> 'e] 'c -> 'd] 'b -> 'a // : ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // please unify ['a] ['b > ['b1] ['c > ['c1] ['d > ['d1] list ('b1 * 'c1 * 'd1)] list ('d) ] list ('c) ] 'b -> 'a // : ['a] (list (list (list ('a)))) -> 'a // = ['b1] ['c1] ['d1] (list (list (list ('b1 * 'c1 * 'd1)))) -> ('b1 * 'c1 * 'd1) // please notequiv ['b]['c] ('b * 'c) -> ('b * 'c) // : ['b]['c] ('b * 'c) -> ('c * 'b) // (* Unification d'une variable avec un sch�ma de types. *) // type sid = ['a] 'a -> 'a // let id y = y // let succ n = n + 1 // let test = fun (x:'a) -> ((x:sid) , if true then x else id) // untype fun (x:'a) -> ((x:sid) , if true then x else succ) type Sid = forall 'a. 'a -> 'a def id y = y def succ n = n + 1 def test = fun (x : 'a) -> (x : Sid, if true then x else id) //│ Defined type alias Sid //│ id: 'a -> 'a //│ = [Function: id1] //│ succ: int -> int //│ = [Function: succ1] //│ test: (Sid & 'a) -> (Sid, forall 'b. 'b -> 'b | 'a,) //│ = [Function: test] // * [FCP-IMPROV] def test2 = fun (x : 'a) -> (x : Sid, if true then x else succ) //│ test2: (Sid & 'a) -> (Sid, int -> int | 'a,) //│ = [Function: test21] // (* Typable dans MLF, mais pas dans systeme F. *) // type sid = ['a] 'a -> 'a // type sa = ['b] ['a > sid] ('a -> 'b) -> 'b // * Same as A1 in `ex_shallow.mls` (see remarks there): // type Sa = forall 'a 'b. (('a | Sid) -> 'b) -> 'b type Sa = forall 'b. (Sid -> 'b) -> 'b //│ Defined type alias Sa // let auto (x:sid) = x x // let id x = x // let k x y = x def auto (x: Sid) = x x def auto_ x = x x def id x = x def k x y = x //│ auto: Sid -> Sid //│ = [Function: auto] //│ auto_: ('a -> 'b & 'a) -> 'b //│ = [Function: auto_] //│ id: 'a -> 'a //│ = [Function: id2] //│ k: 'a -> anything -> 'a //│ = [Function: k] // let church_zero = fun f -> id // let church_un = id // let church_succ n = fun f x -> f (n f x) // untype fun g -> k (g auto) ((fun t -> g t ; t church_un) (fun x -> (church_succ x) church_zero)) // let f = fun (g:sa) -> k (g auto) ((fun t -> g t ; t church_un) (fun x -> (church_succ x) church_zero)) // let a = fun f -> f id def church_zero = fun f -> id def church_un = id def church_succ n = fun f -> fun x -> f (n f x) def f = fun (g: Sa) -> k (g auto) ((fun t -> let _ = g t in t church_un) (fun x -> (church_succ x) church_zero)) def f_ = fun g -> k (g auto_) ((fun t -> let _ = g t in t church_un) (fun x -> (church_succ x) church_zero)) def a = fun f -> f id //│ church_zero: anything -> 'a -> 'a //│ = [Function: church_zero] //│ church_un: 'a -> 'a //│ = [Function: church_un] //│ church_succ: ('a -> 'b -> 'c) -> ('c -> 'd & 'a) -> 'b -> 'd //│ = [Function: church_succ] //│ f: Sa -> Sid //│ = [Function: f] //│ f_: ((forall 'a 'b. ('a -> 'b & 'a) -> 'b) -> 'c & (forall 'd 'e. ((forall 'f. anything -> 'f -> 'f) -> 'd -> anything) -> 'd -> 'e -> 'e) -> anything) -> 'c //│ = [Function: f_] //│ a: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: a] // let app x y = x y // let test1 = f a // let test2 = app f a def app x y = x y //│ app: ('a -> 'b) -> 'a -> 'b //│ = [Function: app] def test1 = f a //│ test1: Sid //│ = [Function: test11] def test1 = f_ a //│ test1: 'a -> 'a //│ = [Function: test12] def test2 = app f a //│ test2: Sid //│ = [Function: test22] def test2 = app f_ a //│ test2: 'a -> 'a //│ = [Function: test23] // (* Create occur-checks. *) // please failon_unify ['b] 'b -> ('b -> 'b) // : ['a > ['c] ('c -> 'c) -> ('c -> 'c)] // 'a -> 'a // please failon_unify ['i] 'i -> 'i // : ['a > ['c] ('c -> 'c) -> ('c -> 'c)] // ['b > ['d] 'd -> 'a] // 'b -> 'a // ;; // please failon_unify ['i] 'i -> 'i // : ['a > ['c] ('c -> 'c) -> ('c -> 'c)] // ['b > ['d] 'd -> 'a] // 'a -> 'b // ;; // let t = let x = fun y -> y in x x ;; // let t = let x = ((fun y -> y) : ['a] 'a -> 'a) in x x ;; // let t = let x = ((fun y -> y) : 'a) in x x ;; def t = let x = fun y -> y in x x def t = let x = (fun y -> y) : forall 'a. 'a -> 'a in x x def t = let x = (fun y -> y) : 'a in x x //│ t: 'a -> 'a //│ = [Function: t5] //│ t: 'a -> 'a //│ = [Function: t6] //│ t: 'a -> 'a //│ = [Function: t7] // untype fun x -> ( (((x:'a) : sid):'a), (x:'a) x) // untype fun x -> ( ((x:'a) : sid), (x:'a) x) // untype fun x -> ( ((x : sid):'a), (x:'a) x) // untype fun x -> ( (((x:'a) : sid):'a), x x) // untype fun x -> ( (x : sid), (x:'a) x) // let t = fun x -> ( (((x:'a) : sid):'a), (x:'a)) fun x -> ( (((x : 'a) : Sid) : 'a), (x : 'a) x) fun x -> ( ((x : 'a) : Sid), (x : 'a) x) fun x -> ( ((x : Sid) : 'a), (x : 'a) x) fun x -> ( (((x : 'a) : Sid) : 'a), x x) fun x -> ( (x : Sid), (x : 'a) x) fun x -> ( (((x : 'a) : Sid) : 'a), x : 'a) //│ res: (Sid & 'a -> 'b & 'a) -> (Sid, 'b,) //│ = [Function: res] //│ res: (Sid & 'a -> 'b & 'a) -> (Sid, 'b,) //│ = [Function: res] //│ res: (Sid & 'a -> 'b & 'a) -> (Sid, 'b,) //│ = [Function: res] //│ res: (Sid & 'a -> 'b & 'a) -> (Sid, 'b,) //│ = [Function: res] //│ res: (Sid & 'a -> 'b & 'a) -> (Sid, 'b,) //│ = [Function: res] //│ res: (Sid & 'a) -> (Sid, 'a,) //│ = [Function: res] // (* R�cursion polymorphe. *) // untype let rec id x = if true then x else id id x in id // Same as `id1` in `ex_demo.mls`; works with `:RecursiveTypes` :e rec def id_ x = if true then x else id_ id_ x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.927: rec def id_ x = if true then x else id_ id_ x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required //│ ║ l.927: rec def id_ x = if true then x else id_ id_ x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ id_: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = [Function: id_] // let rec (id:sid) x = if true then x else id id x def id: Sid //│ id: Sid //│ = // * Note: infers with `:RecursiveTypes` but does not signature-check :e rec def id x = if true then x else id id x //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: 'b -> 'c //│ 'b :> 'b -> 'c //│ <: 'c //│ 'c :> 'b -> 'c //│ <: 'a //│ ║ l.956: rec def id x = if true then x else id id x //│ ╙── ^^^^^ //│ 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ <: id: //│ Sid //│ ╔══[ERROR] Cyclic-looking constraint while typing binding of lambda expression; a type annotation may be required //│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.765: type Sid = forall 'a. 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^^^^^^^ //│ ╟── from reference: //│ ║ l.956: rec def id x = if true then x else id id x //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.765: type Sid = forall 'a. 'a -> 'a //│ ╙── ^^ //│ = [Function: id3] // let z = print_string "Ok, all tests passed." z = log "Ok, all tests passed." //│ z: unit //│ = undefined //│ // Output //│ Ok, all tests passed. ================================================ FILE: shared/src/test/diff/mlf-examples/ex_voir.mls ================================================ :NoRecursiveTypes // ============ Dummy classes to represent the types in the examples ============ class List[a] method Head: a method Tail: List[a] //│ Defined class List[+a] //│ Declared List.Head: List['a] -> 'a //│ Declared List.Tail: List['a] -> List['a] class Cons[a]: List[a] & { head: a; tail: List[a] } method Head = this.head method Tail = this.tail def cons[a]: a -> List[a] -> List[a] def cons head tail = Cons { head; tail } //│ Defined class Cons[+a] //│ Defined Cons.Head: Cons['a] -> 'a //│ Defined Cons.Tail: Cons['a] -> List['a] //│ cons: 'a -> List['a] -> List['a] //│ = //│ ('head & 'a) -> (List['a] & 'tail) -> (Cons['a] with {head: 'head, tail: 'tail}) //│ <: cons: //│ 'a -> List['a] -> List['a] //│ = [Function: cons] class Nil[a]: List[a] method Head = error method Tail = this def nil: List['a] nil = Nil {} //│ Defined class Nil[+a] //│ Defined Nil.Head: Nil[?] -> nothing //│ Defined Nil.Tail: (Nil['a] & 'this) -> (Nil['a] & 'this) //│ nil: List[nothing] //│ = //│ Nil[nothing] //│ <: nil: //│ List[nothing] //│ = Nil {} // type sid = ['a] 'a -> 'a type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let choose x y = if true then x else y // let choose3 x y z = (choose (choose x y) z) // let id = (fun x -> x : sid) // let succ n = n + 1 def choose x y = if true then x else y def choose3 x y z = choose (choose x y) z def id = (fun x -> x) : Sid def id_ = fun x -> x def succ n = n + 1 //│ choose: 'a -> 'a -> 'a //│ = [Function: choose] //│ choose3: 'a -> 'a -> 'a -> 'a //│ = [Function: choose3] //│ id: Sid //│ = [Function: id] //│ id_: 'a -> 'a //│ = [Function: id_] //│ succ: int -> int //│ = [Function: succ] // let test = choose3 id // let test2 = test succ def test = choose3 id def test_ = choose3 id_ def test2 = test succ def test2_ = test_ succ //│ test: 'a -> 'a -> (Sid | 'a) //│ = [Function: test] //│ test_: 'a -> 'a -> ('b -> 'b | 'a) //│ = [Function: test_] //│ test2: 'a -> (int -> int | Sid | 'a) //│ = [Function: test2] //│ test2_: 'a -> (int -> int | 'a) //│ = [Function: test2_] // (* Messages d'erreur foireux : *) // type s1 = ['a] 'a -> int * int ;; // type s2 = ['a] 'a -> bool * bool ; // fun x y -> ((x:s1),(y:s2), if true then x else y) ;; // fun x y -> ((x:s1),(y:s2), x x) ;; type S1 = forall 'a. 'a -> (int, int) type S2 = forall 'a. 'a -> (bool, bool) fun x -> fun y -> ((x : S1), (y : S2), if true then x else y) fun x -> fun y -> ((x : S1), (y : S2), x x) //│ Defined type alias S1 //│ Defined type alias S2 //│ res: (S1 & 'a) -> (S2 & 'a) -> (S1, S2, 'a,) //│ = [Function: res] //│ res: (S1 & 'a -> 'b & 'a) -> S2 -> (S1, S2, 'b,) //│ = [Function: res] // (* please onlyweak ['b = sid] 'b -> 'b : sid -> sid *) // (* // let example f = (f:['a] 'a -> int -> 'b) f // let mkpair x y = (x,y) def example f = (f: forall 'a. 'a -> int -> 'b) f def example_ f = f f def mkpair x y = (x, y) //│ example: (anything -> int -> 'b) -> int -> 'b //│ = [Function: example] //│ example_: ('a -> 'b & 'a) -> 'b //│ = [Function: example_] //│ mkpair: 'a -> 'b -> ('a, 'b,) //│ = [Function: mkpair] // untype example mkpair // (* �a echoue parce qu'on instancie 'b avec un type contenant 'a *) // (* Il faudrait que le message d'erreur soit plus clair. *) // *) example mkpair example_ mkpair //│ res: int -> (??a, int,) //│ = [Function (anonymous)] //│ res: 'a -> (forall 'b 'c. 'b -> 'c -> ('b, 'c,), 'a,) //│ = [Function (anonymous)] // let make_ex3 x (f:['a > sid] 'a -> 'b ) = f x def make_ex3 x (f: forall 'a. ('a | Sid) -> 'b) = f x //│ make_ex3: anything -> (anything -> 'a) -> 'a //│ = [Function: make_ex3] // let ex_list3 = [ make_ex3 succ ; // make_ex3 id ; // make_ex3 not ] def ex_list3 = cons (make_ex3 succ) ( cons (make_ex3 id) ( cons (make_ex3 not) nil)) //│ ex_list3: List[forall 'a. (anything -> 'a) -> 'a] //│ = [Function: ex_list3] // * TODO: what is `open`? // (*let test3 = listiter (open (fun v -> choose v id)) ex_list3 *) // (* Meme message d'erreur je crois. *) // type i = int // (* Affichage : nom des variables ??? *) // please unify ['a1 = ['a2 = ['a3 = ['a4 = ['a5 = 0] 'a5 -> i] // ('a4 -> i) -> i] // ('a3 -> i) -> i] 'a2 -> i] 'a1 -> i // : ['b1 = ['b2 = ['b3 = ['b4 = 0] ('b4 -> i) -> i] // ('b3 -> i) -> i] // ('b2 -> i) -> i] 'b1 -> i // (* Cet exemple cr�e un graphe mal form�. *) // please failon_unify ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> list ('c)] 'b -> 'a // (* Plus simple *) // please failon_unify ['a] ['b = ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'c] 'b -> 'a // (* Extrusion automatique de binders. *) // please unify ['a] ['b > ['c] 'c -> 'a] 'b -> 'a // : ['a] ['b > ['c] 'c -> 'c] 'b -> 'a // = ['c] ('c -> 'c) -> 'c ================================================ FILE: shared/src/test/diff/mlf-examples/ex_vr_validate.mls ================================================ :NoRecursiveTypes // (* Value restriction. *) // let id x = x def id x = x //│ id: 'a -> 'a //│ = [Function: id] // untype let x = id id in x 1, x true // untype let x = (id id:'a) in x 1, x true // ;; let x = id id in (x 1, x true) let x = (id id : 'a) in (x 1, x true) //│ res: (1, true,) //│ = [ 1, true ] //│ res: (1, true,) //│ = [ 1, true ] // type sid = ['a] 'a -> 'a // ;; type Sid = forall 'a. 'a -> 'a //│ Defined type alias Sid // let annot x = (x:sid) // ;; def annot x = x : Sid //│ annot: Sid -> Sid //│ = [Function: annot] // let delta x = let y = (x:sid) in y y // ;; def delta x = let y = (x : Sid) in y y //│ delta: Sid -> Sid //│ = [Function: delta] // untype fun x -> let y = (annot x) in y y // ;; fun x -> let y = annot x in y y //│ res: Sid -> Sid //│ = [Function: res] ================================================ FILE: shared/src/test/diff/mlf-examples/merge_regression_min.mls ================================================ :DontGeneralizeCurriedFunctions def choose: 'a -> 'a -> 'a //│ choose: 'a -> 'a -> 'a //│ = rec def id1 x = choose x (id1 id1 x) //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = //│ choose is not implemented id1 id //│ res: ('a & 'b) -> 'b | 'c //│ where //│ 'a :> ('a & 'b) -> 'b //│ <: 'b //│ 'b :> ('a & 'b) -> 'b //│ <: 'a -> 'b & 'c //│ = //│ id1 and choose are not implemented :GeneralizeCurriedFunctions id1 id //│ res: ('a & 'b) -> 'b | 'c //│ where //│ 'a :> ('a & 'b) -> 'b //│ <: 'b //│ 'b :> ('a & 'b) -> 'b //│ <: 'a -> 'b & 'c //│ = //│ id1 and choose are not implemented rec def id1 x = choose x (id1 id1 x) //│ id1: 'a -> 'b //│ where //│ 'a :> 'a -> 'b //│ <: 'b //│ 'b := 'a -> 'b //│ = //│ choose is not implemented // :e id1 id //│ res: ('a & 'b) -> 'b | 'c //│ where //│ 'a :> ('a & 'b) -> 'b //│ <: 'b //│ 'b :> ('a & 'b) -> 'b //│ <: 'a -> 'b & 'c //│ = //│ id1 and choose are not implemented ================================================ FILE: shared/src/test/diff/mlf-examples/variations_ex_hashtbl.mls ================================================ :NoRecursiveTypes :NoJS // ============ Dummy definitions to type the examples ============ class List[a] method Get: a def nil: List['a] def cons[a]: a -> List[a] -> List[a] def match_list: forall 'a 'b. List['a] -> 'b -> ('a -> List['a] -> 'b) -> 'b def isnil: List['a] -> bool //│ Defined class List[+a] //│ Declared List.Get: List['a] -> 'a //│ nil: List[nothing] //│ cons: 'a -> List['a] -> List['a] //│ match_list: List['a] -> 'b -> ('a -> List['a] -> 'b) -> 'b //│ isnil: List[?] -> bool class None class Some[a]: { val: a } type Option[a] = None | Some[a] def none = None {} def some val = Some { val } //│ Defined class None //│ Defined class Some[+a] //│ Defined type alias Option[+a] //│ none: None //│ some: 'val -> Some['val] def match_opt: forall 'a 'r. (() -> 'r, 'a -> 'r) -> Option['a] -> 'r //│ match_opt: (() -> 'r, 'a -> 'r,) -> Option['a] -> 'r match_opt (n, s) opt = case opt of None -> n(), Some -> s(opt.val) //│ (() -> 'a, 'val -> 'a,) -> (None | Some[?] & {val: 'val}) -> 'a //│ <: match_opt: //│ (() -> 'r, 'a -> 'r,) -> Option['a] -> 'r // ============ From the original examples ============ type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ Defined type alias ChurchInt def succ (n: ChurchInt) = fun f -> fun x -> f (n f x) def add (n: ChurchInt) (m: ChurchInt) = n succ m def printInt (n: ChurchInt) = toString (n (fun x -> x + 1) 0) //│ succ: ChurchInt -> ('a -> ('a & 'b)) -> 'a -> 'b //│ add: ChurchInt -> ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) //│ printInt: ChurchInt -> string def create_hashtbl () = nil def hashtbl_add table key element = cons ((key, element)) table //│ create_hashtbl: () -> List[nothing] //│ hashtbl_add: List['a] -> 'b -> 'c -> List[('b, 'c,) | 'a] table = create_hashtbl () table = hashtbl_add table "one" (fun f -> fun x -> f x) // table = hashtbl_add table "two" (fun f -> fun x -> f (f x)) //│ table: List[nothing] //│ table: List[("one", forall 'a 'b. ('a -> 'b) -> 'a -> 'b,)] // ============ Our own variations on ex_hashtbl ============ rec def find table key = match_list table none (fun h -> fun t -> if eq h.0 key then some h.1 else find t key ) //│ find: List[{0: anything, 1: 'val}] -> anything -> (None | Some['val]) def nfind table key = let opt = find table key in case opt of { None -> fun f -> fun x -> x | Some -> opt.val } //│ nfind: List[{0: anything, 1: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) def nfind: List[{0: anything; 1: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) //│ nfind: List[{0: anything, 1: 'val}] -> anything -> (anything -> 'a -> 'a | 'val) // * Alternative // def nfind table key = // (fun opt -> case opt of // { None -> fun f -> fun x -> x // | Some -> opt.val // }) (find table key) // * Note that this works: def x = nfind table "one" //│ x: ('b -> 'a) -> ('a & 'b) -> 'a // def x: ('b -> 'a) -> ('a & 'b) -> 'a add x //│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) // * But this doesn't, because `nfind table "one"` is no longer generalized: :stats :e add (nfind table "one") //│ ╔══[ERROR] Type error in application //│ ║ l.113: add (nfind table "one") //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.47: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.47: type ChurchInt = forall 'a. ('a -> 'a) -> ('a -> 'a) //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.113: add (nfind table "one") //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.63: table = hashtbl_add table "one" (fun f -> fun x -> f x) //│ ╙── ^ //│ res: error | ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) //│ constrain calls : 100 //│ annoying calls : 7 //│ subtyping calls : 305 // * On the other hand, in this variation, distributivity saves the day: tmp = nfind table //│ tmp: anything -> ('b -> 'a) -> ('a & 'b) -> 'a tmp "one" //│ res: ('b -> 'a) -> ('a & 'b) -> 'a add (tmp "one") //│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) // * But it all works with argument-generalization: // ========================================================================================== :GeneralizeArguments :stats add (nfind table "one") //│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) //│ constrain calls : 94 //│ annoying calls : 7 //│ subtyping calls : 280 :stats (fun x -> add x) (nfind table "one") //│ res: ChurchInt -> (('a -> ('a & 'b)) -> 'a -> 'b | ChurchInt) //│ constrain calls : 106 //│ annoying calls : 7 //│ subtyping calls : 314 :stats zog = printInt (add (nfind table "one") (nfind table "two")) //│ zog: string //│ constrain calls : 283 //│ annoying calls : 14 //│ subtyping calls : 786 :DontGeneralizeArguments // ========================================================================================== b = find table "two" //│ b: None | Some[forall 'a 'b. ('a -> 'b) -> 'a -> 'b] match_opt ( fun () -> fun f -> fun x -> x, fun v -> v ) b //│ res: ('a -> 'b) -> ('a & 'c) -> ('b | 'c) match_opt ( fun () -> fun f -> fun x -> x, fun v -> v ) (find table "two") //│ res: ('a -> 'b) -> ('a & 'c) -> ('b | 'c) def foo key = match_opt ( fun () -> fun f -> id, fun v -> v ) (find table key) //│ foo: anything -> ('a -> 'b) -> 'a -> ('b | 'a) foo 1 //│ res: ('a -> 'b) -> 'a -> ('b | 'a) :e foo 1 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.202: foo 1 2 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `2` is not a function //│ ║ l.202: foo 1 2 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.63: table = hashtbl_add table "one" (fun f -> fun x -> f x) //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.63: table = hashtbl_add table "one" (fun f -> fun x -> f x) //│ ╙── ^ //│ res: error | 'a -> 'a foo 1 error //│ res: 'a -> 'a foo 1 error 2 //│ res: 2 (fun key -> match_opt ( fun () -> fun f -> fun x -> x, fun v -> v ) (find table key)) "two" //│ res: ('a -> 'b) -> ('c & 'a) -> ('c | 'b) res id //│ res: 'a -> 'a case b of None -> fun f -> fun x -> x, Some -> b.val //│ res: ('a -> 'b) -> ('c & 'a) -> ('c | 'b) res id //│ res: 'a -> 'a (fun opt -> case b of None -> fun f -> fun x -> x, Some -> b.val) (find table "two") //│ res: ('a -> 'b) -> ('c & 'a) -> ('c | 'b) ================================================ FILE: shared/src/test/diff/mlscript/ADTs.mls ================================================ class Lit: { val: int } class Add[E]: { lhs: E; rhs: E } class Nega[E]: { arg: E } def lit val = Lit { val } def add lhs rhs = Add { lhs; rhs } def nega arg = Nega { arg } //│ Defined class Lit //│ Defined class Add[+E] //│ Defined class Nega[+E] //│ lit: (int & 'val) -> (Lit with {val: 'val}) //│ = [Function: lit] //│ add: ('lhs & 'E) -> ('E & 'rhs) -> (Add['E] with {lhs: 'lhs, rhs: 'rhs}) //│ = [Function: add] //│ nega: 'arg -> Nega['arg] //│ = [Function: nega] rec def evalStub1 e = case e of { Lit -> e.val } //│ evalStub1: (Lit with {val: 'val}) -> 'val //│ = [Function: evalStub1] rec def evalStub2 e = case e of { Lit -> e.val | Nega -> 0 - (evalStub2 e.arg) } //│ evalStub2: 'a -> int //│ where //│ 'a <: Lit | Nega[?] & {arg: 'a} //│ = [Function: evalStub2] rec def eval e = case e of { | Lit -> e.val | Add -> eval e.lhs + eval e.rhs | Nega -> 0 - (eval e.arg) } //│ eval: 'a -> int //│ where //│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit | Nega[?] & {arg: 'a} //│ = [Function: eval] ex = add (lit 2) (lit 2) //│ ex: Add[Lit & {val: 2}] //│ = Add { lhs: Lit { val: 2 }, rhs: Lit { val: 2 } } eval ex //│ res: int //│ = 4 def e: Add['e] as 'e //│ e: 'e //│ where //│ 'e :> Add['e] //│ = def e: Lit | Add['e] as 'e //│ e: 'e //│ where //│ 'e :> Add['e] | Lit //│ = def e: Lit | Add['e] | Nega['e] as 'e //│ e: 'e //│ where //│ 'e :> Add['e] | Lit | Nega['e] //│ = e = ex //│ Add[Lit & {val: 2}] //│ <: e: //│ 'e //│ where //│ 'e :> Add['e] | Lit | Nega['e] //│ = Add { lhs: Lit { val: 2 }, rhs: Lit { val: 2 } } eval e //│ res: int //│ = 4 eval (e : Lit | Add['e] | Nega['e] as 'e) //│ res: int //│ = 4 // === === === ERROR CASES === === === // :ShowRelativeLineNums :AllowTypeErrors class Automata class Binding //│ Defined class Automata //│ Defined class Binding def exp: Array[Automata] -> Array[Binding] eval exp //│ exp: Array[Automata] -> Array[Binding] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: eval exp //│ ║ ^^^^^^^^ //│ ╟── type `Array[Automata] -> Array[Binding]` does not match type `Add[?] & ?a | Lit & ?b | Nega[?] & ?c` //│ ║ l.+1: def exp: Array[Automata] -> Array[Binding] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Add[?] & ?d | Lit & ?e | Nega[?] & ?f` //│ ║ l.+2: eval exp //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.32: rec def eval e = case e of { //│ ╙── ^ //│ res: error | int :e eval (e : Lit | Add['e] | Nega[int] as 'e) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: eval (e : Lit | Add['e] | Nega[int] as 'e) //│ ║ ^ //│ ╟── type `Lit` is not an instance of type `int` //│ ║ l.62: def e: Lit | Add['e] | Nega['e] as 'e //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.+1: eval (e : Lit | Add['e] | Nega[int] as 'e) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: eval (e : Lit | Add['e] | Nega[int] as 'e) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int` does not match type `Add[?] & ?a | Lit & ?b | Nega[?] & ?c` //│ ║ l.+1: eval (e : Lit | Add['e] | Nega[int] as 'e) //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.32: rec def eval e = case e of { //│ ║ ^ //│ ╟── from field selection: //│ ║ l.35: | Nega -> 0 - (eval e.arg) //│ ╙── ^^^^^ //│ res: error | int ================================================ FILE: shared/src/test/diff/mlscript/ADTsRepro.mls ================================================ type Add[E] = { x: E -> E } //│ Defined type alias Add[=E] def e2: Add['e] as 'e //│ e2: 'e //│ where //│ 'e := Add['e] //│ = e2 : Add['e] as 'e //│ res: 'e //│ where //│ 'e := Add['e] //│ = //│ e2 is not implemented def f0: ('a -> int) as 'a //│ f0: 'a //│ where //│ 'a := 'a -> int //│ = // :e // * Did not work before when added subtype checking to constraining... f0 = f0 //│ 'a //│ where //│ 'a := 'a -> int //│ <: f0: //│ 'a //│ where //│ 'a := 'a -> int //│ = //│ f0 is not implemented type F1 = F1 -> int //│ Defined type alias F1 def f1: F1 //│ f1: F1 //│ = f1 = f1 //│ F1 //│ <: f1: //│ F1 //│ = //│ f1 is not implemented type F2[A] = F2[A] -> A //│ Defined type alias F2[=A] def f1: F2[int] //│ f1: F2[int] //│ = f1 = f1 //│ F2[int] //│ <: f1: //│ F2[int] //│ = //│ f1 is not implemented ================================================ FILE: shared/src/test/diff/mlscript/Addable.mls ================================================ class Addable[A] method Add: A -> A //│ Defined class Addable[=A] //│ Declared Addable.Add: Addable['A] -> 'A -> 'A class Numb: Addable[Numb] & { val: int } method Add that = Numb { val = this.val + that.val } //│ Defined class Numb //│ Defined Numb.Add: Numb -> {val: int} -> Numb class Stri: Addable[Stri] & { val: string } method Add that = Stri { val = concat this.val that.val } //│ Defined class Stri //│ Defined Stri.Add: Stri -> {val: string} -> Stri n = Numb { val = 1 } //│ n: Numb & {val: 1} //│ = Numb { val: 1 } n.Add n //│ res: Numb //│ = Numb { val: 2 } n.Add //│ res: Numb -> Numb //│ = [Function: Add] def addTwo a0 a1 = a0.Add a1 //│ addTwo: Addable['A] -> 'A -> 'A //│ = [Function: addTwo] addTwo n n //│ res: Numb //│ = Numb { val: 2 } s = Stri { val = "hey" } //│ s: Stri & {val: "hey"} //│ = Stri { val: 'hey' } s.Add s //│ res: Stri //│ = Stri { val: 'heyhey' } addTwo s s //│ res: Stri //│ = Stri { val: 'heyhey' } def addSame a = a.Add a //│ addSame: (Addable['A] & 'A) -> 'A //│ = [Function: addSame] addSame n //│ res: Numb //│ = Numb { val: 2 } rec def addNTimes a n = if n <= 1 then a else a.Add (addNTimes a (n - 1)) //│ addNTimes: (Addable['A] & 'A) -> int -> 'A //│ = [Function: addNTimes] addNTimes n 12 //│ res: Numb //│ = Numb { val: 12 } addNTimes s 5 //│ res: Stri //│ = Stri { val: 'heyheyheyheyhey' } // === === === ERROR CASES === === === // :ShowRelativeLineNums :AllowTypeErrors rec def addNTimes a n = if n < 0 then 0 else a.Add (addNTimes a (n - 1)) addNTimes n 12 //│ addNTimes: Addable['A] -> int -> 'A //│ where //│ 'A :> 0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: addNTimes n 12 //│ ║ ^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `Numb` //│ ║ l.+2: if n < 0 then 0 else a.Add (addNTimes a (n - 1)) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: class Numb: Addable[Numb] & { val: int } //│ ╙── ^^^^ //│ res: error rec def addNTimes a n = if n <= 0 then 0 else a.Add addNTimes a (n - 1) //│ addNTimes: 'a -> int -> 'b //│ where //│ 'a <: Addable['A] //│ 'A := 'a -> int -> 'b //│ 'b :> 0 addNTimes n 12 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: addNTimes n 12 //│ ║ ^^^^^^^^^^^ //│ ╟── function of type `?a -> ?b -> ?c` is not an instance of type `Numb` //│ ║ l.102: rec def addNTimes a n = //│ ║ ^^^^^ //│ ║ l.103: if n <= 0 then 0 else a.Add addNTimes a (n - 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: class Numb: Addable[Numb] & { val: int } //│ ╙── ^^^^ //│ res: error addSame n n //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: addSame n n //│ ║ ^^^^^^^^^^^ //│ ╟── application of type `Numb & {val: ?val}` is not a function //│ ║ l.17: n = Numb { val = 1 } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(forall ?a. ?a) -> ?b` //│ ║ l.+1: addSame n n //│ ╙── ^ //│ res: error addTwo s n //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: addTwo s n //│ ║ ^^^^^^^^^^ //│ ╟── application of type `Numb & {val: ?val}` is not an instance of type `Stri` //│ ║ l.17: n = Numb { val = 1 } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Stri` //│ ║ l.+1: addTwo s n //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.12: class Stri: Addable[Stri] & { val: string } //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.30: def addTwo a0 a1 = a0.Add a1 //│ ╙── ^^ //│ res: error class Str_bad_0: Addable[Str_bad_0] & { val: string } method Add that = Str_bad_0 { val = this.val + that.val } //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ║ ^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.+1: class Str_bad_0: Addable[Str_bad_0] & { val: string } //│ ║ ^^^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` is not an instance of type `string` //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.+1: class Str_bad_0: Addable[Str_bad_0] & { val: string } //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.+1: class Str_bad_0: Addable[Str_bad_0] & { val: string } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.+2: method Add that = Str_bad_0 { val = this.val + that.val } //│ ╙── ^^^^^^^^ //│ Defined class Str_bad_0 //│ Defined Str_bad_0.Add: Str_bad_0 -> {val: int} -> ((Str_bad_0 with {val: error | int}) | error) ================================================ FILE: shared/src/test/diff/mlscript/AdtStyle.mls ================================================ :NoJS datatype Either[A, B] = Left(A) | Right(B) //│ Defined class Either[+A, +B] //│ Declared Either.Left: Either['A, ?] -> ('A,) //│ Declared Either.Right: Either[?, 'B] -> ('B,) //│ Defined class Left[+A, +B] //│ Defined class Right[+A, +B] //│ Left: 'a -> Either['a, nothing] //│ Right: 'a -> Either[nothing, 'a] datatype List[A] = Cons(A, List[A]) | Nil //│ Defined class List[+A] //│ Declared List.Cons: List['A] -> ('A, List['A],) //│ Declared List.Nil: List[?] -> anything //│ Defined class Cons[+A] //│ Defined class Nil[+A] //│ Cons: ('a, List['a],) -> List['a] //│ Nil: List[nothing] type Heap = List[(string, int)] datatype HeapVar = HeapInt(int) | HeapV(Heap) datatype Exp = EInt(int) | Var(string) | Plus(Exp, Exp) | Times(Exp, Exp) datatype Stmt = Skip | Assign(string, Exp) //│ Defined type alias Heap //│ Defined class HeapVar //│ Declared HeapVar.HeapInt: HeapVar -> (int,) //│ Declared HeapVar.HeapV: HeapVar -> (Heap,) //│ Defined class HeapInt //│ Defined class HeapV //│ Defined class Exp //│ Declared Exp.EInt: Exp -> (int,) //│ Declared Exp.Var: Exp -> (string,) //│ Declared Exp.Plus: Exp -> (Exp, Exp,) //│ Declared Exp.Times: Exp -> (Exp, Exp,) //│ Defined class EInt //│ Defined class Var //│ Defined class Plus //│ Defined class Times //│ Defined class Stmt //│ Declared Stmt.Skip: Stmt -> anything //│ Declared Stmt.Assign: Stmt -> (string, Exp,) //│ Defined class Skip //│ Defined class Assign //│ HeapInt: int -> HeapVar //│ HeapV: Heap -> HeapVar //│ EInt: int -> Exp //│ Var: string -> Exp //│ Plus: (Exp, Exp,) -> Exp //│ Times: (Exp, Exp,) -> Exp //│ Skip: Stmt //│ Assign: (string, Exp,) -> Stmt datatype Poly[A] = PolyList(List[A -> A]) //│ Defined class Poly[=A] //│ Declared Poly.PolyList: Poly['A] -> (List['A -> 'A],) //│ Defined class PolyList[=A] //│ PolyList: List['a -> 'a] -> Poly['a] datatype FCPoly = FCPolyList(List[forall 'a. 'a -> 'a]) //│ Defined class FCPoly //│ Declared FCPoly.FCPolyList: FCPoly -> (List[forall 'a. 'a -> 'a],) //│ Defined class FCPolyList //│ FCPolyList: List[forall 'a. 'a -> 'a] -> FCPoly fun x -> match x with | Cons(a, b) -> b | Nil -> Nil //│ res: List['A] -> List['A] fun x -> match x with | Cons(1, b) -> Cons(0, b) | Cons(a, b) -> b | Nil -> Nil //│ res: List[int & 'A] -> List[0 | 'A] fun x -> match x with | EInt(v,) -> v | _ -> 0 //│ res: Exp -> int fun x -> match x with | (1, 2, 3) -> 6 | (x, y, 3,) -> x + y | _ -> 0 //│ res: ((int, int, int,),) -> int fun (x, y) -> match (x, y) with | (1, 2) -> 3 | _ -> 0 //│ res: (int, int,) -> (0 | 3) foo x = match x with | 0 -> 0 | 1 -> 1 //│ foo: int -> (0 | 1) foo 0 //│ res: 0 | 1 // FIXME allow ascriptions :e foo x = match x with | (0 : int) -> 0 | (1 : int) -> 1 //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Cannot handle pattern 0 : int foo 0 //│ res: 0 | 1 // FIXME such patterns are not allowed :e foo x = match x with | (id 0) -> 0 //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: id cannot be pattern matched match Cons(0, Nil) with Cons(0, Nil) -> 0 //│ res: 0 match Cons(false, Nil) with Cons(true, Nil) -> 0 //│ res: 0 match Cons(0, Nil) with Cons(1, Nil) -> 0 //│ res: 0 :e match Cons(true, Nil) with Cons(1, Nil) -> 0 //│ ╔══[ERROR] Type mismatch in ADT pattern matching: //│ ║ l.136: match Cons(true, Nil) with Cons(1, Nil) -> 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.136: match Cons(true, Nil) with Cons(1, Nil) -> 0 //│ ║ ^^^^ //│ ╟── Note: constraint arises from literal pattern: //│ ║ l.136: match Cons(true, Nil) with Cons(1, Nil) -> 0 //│ ╙── ^ //│ res: 0 fun x -> match x with Cons(Left a, Nil) -> a //│ res: List[Either['a, ?]] -> 'a :e res(Cons(true, Nil)) // match Cons(true, Nil) with Cons(Left a, Nil) -> a //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.153: res(Cons(true, Nil)) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `Either` //│ ║ l.153: res(Cons(true, Nil)) //│ ║ ^^^^ //│ ╟── Note: constraint arises from pattern: //│ ║ l.149: match x with Cons(Left a, Nil) -> a //│ ║ ^^^^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.13: datatype List[A] = Cons(A, List[A]) | Nil //│ ╙── ^ //│ res: error ================================================ FILE: shared/src/test/diff/mlscript/AlexJ.mls ================================================ 1 //│ res: 1 //│ = 1 class A: { x: int } //│ Defined class A a = A{ x = 123 } //│ a: A & {x: 123} //│ = A { x: 123 } a.x //│ res: 123 //│ = 123 def foo x = (x, x) //│ foo: 'a -> ('a, 'a,) //│ = [Function: foo] id //│ res: 'a -> 'a //│ = [Function: id] r = foo id //│ r: (forall 'a. 'a -> 'a, forall 'a. 'a -> 'a,) //│ = [ [Function: id], [Function: id] ] def fst(x, _) = x def snd(_, x) = x //│ fst: ('a, anything,) -> 'a //│ = [Function: fst] //│ snd: (anything, 'a,) -> 'a //│ = [Function: snd] fst(1,2) //│ res: 1 //│ = 1 :e fst((1,2)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: fst((1,2)) //│ ║ ^^^^^^^^^^ //│ ╟── argument of type `((1, 2,),)` does not match type `(?a, ?b,)` //│ ║ l.43: fst((1,2)) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.31: def fst(x, _) = x //│ ╙── ^^^^^^ //│ res: error //│ = [ 1, 2 ] def fst((x, _)) = x def snd((_, x)) = x //│ fst: (('a, anything,),) -> 'a //│ = [Function: fst1] //│ snd: ((anything, 'a,),) -> 'a //│ = [Function: snd1] :e fst(1,2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.64: fst(1,2) //│ ║ ^^^^^^^^ //│ ╟── argument list of type `(1, 2,)` does not match type `((?a, ?b,),)` //│ ║ l.64: fst(1,2) //│ ╙── ^^^^^ //│ res: error //│ Runtime error: //│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) fst((1,2)) //│ res: 1 //│ = 1 s = fst(r) //│ s: 'a -> 'a //│ = [Function: id] s "abc" //│ res: "abc" //│ = 'abc' s 123 //│ res: 123 //│ = 123 fst r "abc" //│ res: "abc" //│ = 'abc' fst(r) //│ res: 'a -> 'a //│ = [Function: id] snd(r) 123 //│ res: 123 //│ = 123 ================================================ FILE: shared/src/test/diff/mlscript/Annoying.mls ================================================ :NoJS // :NoProvs def foo: ~int\x //│ foo: ~int\x foo: ~(int\x) //│ res: ~int\x foo: (~int)\x //│ res: ~int res: ~(int\x) //│ res: ~int\x def foo: (~ 'a \ x) -> 'a -> 'a //│ foo: ~'a\x -> 'a -> 'a foo 1 //│ res: ('a & ~1) -> 'a def foo: (~ 'a \ x) -> 'a //│ foo: anything -> nothing foo 1 //│ res: nothing class X: {} //│ Defined class X def v0: (~(X\x))\y //│ v0: ~X\x\y :e v0: int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.37: v0: int //│ ║ ^^ //│ ╟── type `~X\x\y` is not an instance of type `int` //│ ║ l.33: def v0: (~(X\x))\y //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.37: v0: int //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.37: v0: int //│ ╙── ^^^ //│ res: int // def test: (int | { x: 'a } | { y: 'a }) -> 'a def test: (int & { f: 'a } | { x: 'a } | { y: 'a }) -> 'a //│ test: anything -> nothing :e def test x = case x of { int -> x.f | _ -> x.x } //│ (int & {f: 'f} | {x: 'f} & ~int) -> 'f //│ <: test: //│ anything -> nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.59: def test x = case x of { int -> x.f | _ -> x.x } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `int & {x: 'a} | {x: 'a} & ~?a` does not have field 'f' //│ ╟── Note: constraint arises from field selection: //│ ║ l.59: def test x = case x of { int -> x.f | _ -> x.x } //│ ║ ^^^ //│ ╟── from refined scrutinee: //│ ║ l.59: def test x = case x of { int -> x.f | _ -> x.x } //│ ╙── ^ // Note: not an error, but a terminating `test` can't be implemented test 1 //│ res: nothing test { x = 1 } //│ res: nothing test { y = 1 } //│ res: nothing class A: { } class B: { } class C: { } //│ Defined class A //│ Defined class B //│ Defined class C def test: (A & { a: 'a } | B & { b: 'a } | C & { c: 'a }) -> 'a //│ test: (A & {a: 'a} | B & {b: 'a} | C & {c: 'a}) -> 'a test (error: A & { a: int }) test (error: B & { b: int }) test (error: C & { c: int }) //│ res: int //│ res: int //│ res: int :e test (error: B & { c: int }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.103: test (error: B & { c: int }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `B & {c: int}` does not match type `A & {a: 'a} | B & {b: 'a} | C & {c: 'a}` //│ ║ l.103: test (error: B & { c: int }) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.92: def test: (A & { a: 'a } | B & { b: 'a } | C & { c: 'a }) -> 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error def test: (A & { x: 'a; y: 'b } | B & { x: 'a; y: 'b } | C & { x: 'a; y: 'b }) -> { l: 'a; r: 'b } //│ test: (A & {x: 'a, y: 'b} | B & {x: 'a, y: 'b} | C & {x: 'a, y: 'b}) -> {l: 'a, r: 'b} test (error: A & { x: int; y: string }) test (error: B & { x: int; y: string }) test (error: C & { x: int; y: string }) //│ res: {l: int, r: string} //│ res: {l: int, r: string} //│ res: {l: int, r: string} :e test (error: A & { x: int }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.127: test (error: A & { x: int }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `A & {x: int}` does not match type `A & {x: 'a, y: 'b} | B & {x: 'a, y: 'b} | C & {x: 'a, y: 'b}` //│ ║ l.127: test (error: A & { x: int }) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.116: def test: (A & { x: 'a; y: 'b } | B & { x: 'a; y: 'b } | C & { x: 'a; y: 'b }) -> { l: 'a; r: 'b } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | {l: int, r: nothing} def test: (A & { xA: 'a; yA: 'b } | B & { xB: 'a; yB: 'b } | C & { xC: 'a; yC: 'b }) -> { l: 'a; r: 'b } //│ test: (A & {xA: 'a, yA: 'b} | B & {xB: 'a, yB: 'b} | C & {xC: 'a, yC: 'b}) -> {l: 'a, r: 'b} test (error: A & { xA: int; yA: string }) test (error: B & { xB: int; yB: string }) test (error: C & { xC: int; yC: string }) //│ res: {l: int, r: string} //│ res: {l: int, r: string} //│ res: {l: int, r: string} def negInt: ~int //│ negInt: ~int def v = negInt with { x = 1 } //│ v: ~int & {x: 1} v.x //│ res: 1 (id v).x //│ res: 1 :e negInt: A | { x: 'a } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.164: negInt: A | { x: 'a } //│ ║ ^^^^^^ //│ ╟── type `~int` does not match type `A | {x: 'a}` //│ ║ l.151: def negInt: ~int //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `A | {x: 'a}` //│ ║ l.164: negInt: A | { x: 'a } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.164: negInt: A | { x: 'a } //│ ╙── ^^^^^^^^^^^^^ //│ res: A | {x: nothing} v: A | { x: 'a } //│ res: A | {x: 1} (error: A) with { x = 1 } //│ res: A\x & {x: 1} // Note that we get {x: nothing} because the LHS is a subtype of A so 'a is not constrained (error: A) with { x = 1 } : A | { x: 'a } //│ res: A | {x: nothing} id (error: A) with { x = 1 } : A | { x: 'a } //│ res: A | {x: nothing} def negWeird: ~(~(~(A & { x: int }))) //│ negWeird: ~(A & {x: int}) def v = negWeird with { x = 1 } //│ v: ~A\x & {x: 1} | {x: 1} & ~{x: int} def w: {x: 1} & ~{x: int} | ~A\x & {x: 1} //│ w: {x: 1} & ~A\x v: A | { x: 'a } //│ res: A | {x: 1} w: A | { x: 'a } //│ res: A | {x: 1} def v: {x: 1} & ~{x: int} v: { x: 'a } //│ v: nothing //│ res: {x: nothing} def v: A & {x: 1} & ~{x: int} v: A & { x: 'a } //│ v: nothing //│ res: A & {x: nothing} def funny = error: nothing\x //│ funny: nothing // It could be agrued this should fail... but that would make constraint solving more awkward! funny.x //│ res: nothing :e (error: { x: int }): int | string //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.228: (error: { x: int }): int | string //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── type `{x: int}` does not match type `int | string` //│ ║ l.228: (error: { x: int }): int | string //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.228: (error: { x: int }): int | string //│ ╙── ^^^^^^^^^^^^ //│ res: int | string (error: (~ 'a) \ x): { x: int } //│ res: {x: int} idx = id with { x = 1 } //│ idx: 'a -> 'a & {x: 1} idx idx.x //│ res: 1 def indirect i = i i.x //│ indirect: ('x -> 'a & {x: 'x}) -> 'a indirect idx //│ res: 1 if true then id else idx //│ res: 'a -> 'a r = if true then id with { x = "a"; y = "b" } else idx //│ r: 'a -> 'a & {x: "a" | 1} r r.x //│ res: "a" | 1 indirect r //│ res: "a" | 1 def takeFunOrRecord: (int -> int | { x: int }) -> int //│ takeFunOrRecord: anything -> int :pe takeFunOrRecord x = case x of { { x = v } -> 0 | _ -> 1 } //│ /!\ Parse error: Expected "}":1:33, found "{ x = v } " at l.273:33: takeFunOrRecord x = case x of { { x = v } -> 0 | _ -> 1 } :pe takeFunOrRecord x = case x of { { x } -> 0 | _ -> 1 } //│ /!\ Parse error: Expected "}":1:33, found "{ x } -> 0" at l.277:33: takeFunOrRecord x = case x of { { x } -> 0 | _ -> 1 } :pe takeFunOrRecord x = case x of { (int -> int) -> 0 | _ -> 1 } //│ /!\ Parse error: Expected "}":1:33, found "(int -> in" at l.281:33: takeFunOrRecord x = case x of { (int -> int) -> 0 | _ -> 1 } :pe takeFunOrRecord x = case x of { (->) -> 0 | _ -> 1 } //│ /!\ Parse error: Expected "}":1:33, found "(->) -> 0 " at l.285:33: takeFunOrRecord x = case x of { (->) -> 0 | _ -> 1 } class AA trait TT def ty: AA | ('a \ x) def ty2: TT & { x: int } // def ty: AA | (int -> int) //│ Defined class AA //│ Defined trait TT //│ ty: in AA | nothing\x out AA //│ ty2: TT & {x: int} :e ty = ty2 //│ TT & {x: int} //│ <: ty: //│ AA | nothing\x //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.300: ty = ty2 //│ ║ ^^^^^^^^ //│ ╟── type `TT & {x: int}` does not match type `AA | 'a\x` //│ ║ l.292: def ty2: TT & { x: int } //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `AA | 'a\x` //│ ║ l.300: ty = ty2 //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.291: def ty: AA | ('a \ x) //│ ╙── ^^^^^^^^^^^^^ ================================================ FILE: shared/src/test/diff/mlscript/Arrays.mls ================================================ :ShowRelativeLineNums :NoJS class MyArray[A]: { size: int; set: (int, A) -> (); get: int -> A } //│ Defined class MyArray[=A] dummy = MyArray { size = 0; set = fun (i, a) -> (); get = fun i -> error } //│ dummy: MyArray['A] & {get: anything -> nothing, set: (anything, anything,) -> (), size: 0} dummy: MyArray[int] //│ res: MyArray[int] def ty1A: MyArray[int] def ty1B: MyArray[0 | 1] //│ ty1A: MyArray[int] //│ ty1B: MyArray[0 | 1] def ty2A: MyArray['a] //│ ty2A: MyArray['a] :stats ty2A = ty2A //│ MyArray['a] //│ <: ty2A: //│ MyArray['a] //│ constrain calls : 1 //│ annoying calls : 0 //│ subtyping calls : 1 def ty2B: MyArray[MyArray['a]] //│ ty2B: MyArray[MyArray['a]] :stats ty2B = ty2A //│ MyArray['a] //│ <: ty2B: //│ MyArray[MyArray['a]] //│ constrain calls : 26 //│ annoying calls : 23 //│ subtyping calls : 63 def ty3A: MyArray['a] as 'a //│ ty3A: 'a //│ where //│ 'a := MyArray['a] :stats ty3A = ty3A //│ 'a //│ where //│ 'a := MyArray['a] //│ <: ty3A: //│ 'a //│ where //│ 'a := MyArray['a] //│ constrain calls : 1 //│ annoying calls : 0 //│ subtyping calls : 1 def ty3B: MyArray[MyArray['a]] as 'a //│ ty3B: 'a //│ where //│ 'a := MyArray[MyArray['a]] :stats ty3B = ty3A //│ 'a //│ where //│ 'a := MyArray['a] //│ <: ty3B: //│ 'a //│ where //│ 'a := MyArray[MyArray['a]] //│ constrain calls : 78 //│ annoying calls : 23 //│ subtyping calls : 199 :stats ty3A = ty3B //│ 'a //│ where //│ 'a := MyArray[MyArray['a]] //│ <: ty3A: //│ 'a //│ where //│ 'a := MyArray['a] //│ constrain calls : 78 //│ annoying calls : 23 //│ subtyping calls : 199 def ty4B: MyArray[MyArray[MyArray['a]]] as 'a //│ ty4B: 'a //│ where //│ 'a := MyArray[MyArray[MyArray['a]]] :stats ty4B = ty4B //│ 'a //│ where //│ 'a := MyArray[MyArray[MyArray['a]]] //│ <: ty4B: //│ 'a //│ where //│ 'a := MyArray[MyArray[MyArray['a]]] //│ constrain calls : 1 //│ annoying calls : 0 //│ subtyping calls : 1 //===//===//===// ERRORS //===//===//===// :e :stats ty1A = ty1B //│ MyArray[0 | 1] //│ <: ty1A: //│ MyArray[int] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: ty1A = ty1B //│ ║ ^^^^^^^^^^^ //│ ╟── type `int` does not match type `0 | 1` //│ ║ l.17: def ty1A: MyArray[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.18: def ty1B: MyArray[0 | 1] //│ ╙── ^^^^^ //│ constrain calls : 5 //│ annoying calls : 5 //│ subtyping calls : 69 :e :stats ty1B = ty1A //│ MyArray[int] //│ <: ty1B: //│ MyArray[0 | 1] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: ty1B = ty1A //│ ║ ^^^^^^^^^^^ //│ ╟── type `int` does not match type `0 | 1` //│ ║ l.17: def ty1A: MyArray[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.18: def ty1B: MyArray[0 | 1] //│ ╙── ^^^^^ //│ constrain calls : 5 //│ annoying calls : 5 //│ subtyping calls : 59 :e :stats ty2A = ty2B //│ MyArray[MyArray['a]] //│ <: ty2A: //│ MyArray['a] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: ty2A = ty2B //│ ║ ^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `MyArray` //│ ║ l.23: def ty2A: MyArray['a] //│ ║ ^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.35: def ty2B: MyArray[MyArray['a]] //│ ╙── ^^^^^^^^^^^ //│ constrain calls : 18 //│ annoying calls : 29 //│ subtyping calls : 51 ================================================ FILE: shared/src/test/diff/mlscript/Arrays2.mls ================================================ :ShowRelativeLineNums class MyArray[A]: { size: int } method Set: (int, A) -> () method Get: int -> A //│ Defined class MyArray[=A] //│ Declared MyArray.Set: MyArray['A] -> (int, 'A,) -> () //│ Declared MyArray.Get: MyArray['A] -> int -> 'A class Dummy[A]: MyArray[A] & { size: 0 } method Set (i, a) = () method Get i = error //│ Defined class Dummy[=A] //│ Defined Dummy.Set: Dummy['A] -> (anything, anything,) -> () //│ Defined Dummy.Get: Dummy['A] -> anything -> nothing dummy = Dummy { size = 0 } //│ dummy: Dummy['A] //│ = Dummy { size: 0 } dummy: MyArray[int] //│ res: MyArray[int] //│ = Dummy { size: 0 } def ty4B: MyArray[MyArray[MyArray['a]]] as 'a //│ ty4B: 'a //│ where //│ 'a := MyArray[MyArray[MyArray['a]]] //│ = :e MyArray{ size = 0 } //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.+1: MyArray{ size = 0 } //│ ║ ^^^^^^^ //│ ╟── Note that class MyArray is abstract: //│ ║ l.3: class MyArray[A]: { size: int } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Hint: method Set is abstract //│ ║ l.4: method Set: (int, A) -> () //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Hint: method Get is abstract //│ ║ l.5: method Get: int -> A //│ ╙── ^^^^^^^^^^^^^ //│ res: error //│ = MyArray { size: 0 } ================================================ FILE: shared/src/test/diff/mlscript/Ascribe.mls ================================================ "hello": 'a //│ res: "hello" //│ = 'hello' :e "hello": 'a -> 'a //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.7: "hello": 'a -> 'a //│ ║ ^^^^^^^ //│ ╟── string literal of type `"hello"` is not a function //│ ╟── Note: constraint arises from function type: //│ ║ l.7: "hello": 'a -> 'a //│ ╙── ^^^^^^^^ //│ res: 'a -> (error | 'a) //│ = 'hello' { name = "Bob" } : { name: 'a } //│ res: {name: "Bob"} //│ = { name: 'Bob' } def foo(n: 'a) = add n 1 //│ foo: int -> int //│ = [Function: foo] def foo(n: 'a) = add n 1: 'a //│ foo: int -> int //│ = [Function: foo1] class Foo[A]: { field: A } //│ Defined class Foo[+A] def foo(f: Foo['a]) = f.field //│ foo: Foo['field] -> 'field //│ = [Function: foo2] (succ 1): int //│ res: int //│ = 2 def foo({ a; b }: { a: int; b: string}) = { b; a } //│ foo: {a: int, b: string} -> {a: int, b: string} //│ = [Function: foo3] :e def foo({ a = "hey" }: { a: string}) = 0 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.48: def foo({ a = "hey" }: { a: string}) = 0 //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `string` does not match type `"hey"` //│ ║ l.48: def foo({ a = "hey" }: { a: string}) = 0 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from string literal: //│ ║ l.48: def foo({ a = "hey" }: { a: string}) = 0 //│ ╙── ^^^^^ //│ foo: {a: string} -> 0 //│ = [Function: foo4] :e def foo({ a; b }: { a: int }) = b //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.62: def foo({ a; b }: { a: int }) = b //│ ║ ^^^^^^^^ //│ ╟── type `{a: int}` does not have field 'b' //│ ║ l.62: def foo({ a; b }: { a: int }) = b //│ ╙── ^^^^^^^^^^ //│ foo: {a: int} -> nothing //│ = [Function: foo5] ================================================ FILE: shared/src/test/diff/mlscript/Baber.mls ================================================ class Foo[A]: { x: A } class Bar //│ Defined class Foo[+A] //│ Defined class Bar def f e = case e of Foo -> e.y, Foo -> e.z, _ -> e //│ f: (Foo[?] & {y: 'y} | 'y & ~#Foo) -> 'y //│ = [Function: f] :ns f //│ res: forall 'a 'b 'y 'c 'z 'd. 'a -> ('y | 'z | 'd) //│ where //│ 'a <: #Foo & 'b | (#Foo & 'c | 'd & ~#Foo) & ~#Foo //│ 'c <: {z: 'z} //│ 'b <: {y: 'y} //│ = [Function: f] class Foo2: Foo[int] & { y: int } //│ Defined class Foo2 f (Foo2{x=1;y=2}) //│ res: 2 //│ = 2 // #Foo def f e = case e of Foo2 -> e.y, Foo -> e.z, //│ f: (Foo[?] & {z: 'y} & ~#Foo2 | (Foo2\x with {y: 'y})) -> 'y //│ = [Function: f1] ================================================ FILE: shared/src/test/diff/mlscript/BadAlias.mls ================================================ :e type A = A //│ ╔══[ERROR] illegal cycle involving type A //│ ║ l.3: type A = A //│ ╙── ^^^^^ :e "oops": A //│ ╔══[ERROR] type identifier not found: A //│ ║ l.9: "oops": A //│ ╙── ^ //│ res: error //│ = 'oops' res: "oops" //│ res: "oops" //│ = 'oops' :e add res 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.21: add res 1 //│ ║ ^^^^^^^ //│ ╟── type `"oops"` is not an instance of type `int` //│ ║ l.16: res: "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.21: add res 1 //│ ╙── ^^^ //│ res: error | int //│ = 'oops1' :e type B = Oops //│ ╔══[ERROR] type identifier not found: Oops //│ ║ l.35: type B = Oops //│ ╙── ^^^^ //│ Defined type alias B 42: B //│ res: B //│ = 42 :e type NonReg[A] = { x: NonReg[NonReg[A]] } //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as NonReg[NonReg['A]], but is defined as NonReg['A] //│ ║ l.46: type NonReg[A] = { x: NonReg[NonReg[A]] } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e type B = NonReg[A] //│ ╔══[ERROR] Type 'B' is already defined. //│ ║ l.52: type B = NonReg[A] //│ ╙── ^ :e type NonReg[A] = { x: NonReg[int] } //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as NonReg[int], but is defined as NonReg['A] //│ ║ l.58: type NonReg[A] = { x: NonReg[int] } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e 42: NonReg[int] //│ ╔══[ERROR] type identifier not found: NonReg //│ ║ l.64: 42: NonReg[int] //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = 42 :e :w type NonReg[A] = { x: NonReg[int] } type RefToNongReg[A] = { x: NonReg[A] } //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as NonReg[int], but is defined as NonReg['A] //│ ║ l.73: type NonReg[A] = { x: NonReg[int] } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ Defined type alias RefToNongReg[±A] //│ ╔══[WARNING] Type definition RefToNongReg has bivariant type parameters: //│ ║ l.74: type RefToNongReg[A] = { x: NonReg[A] } //│ ║ ^^^^^^^^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.74: type RefToNongReg[A] = { x: NonReg[A] } //│ ╙── ^ :w type Reg[A] = Reg[A] -> Reg[A] //│ Defined type alias Reg[±A] //│ ╔══[WARNING] Type definition Reg has bivariant type parameters: //│ ║ l.88: type Reg[A] = Reg[A] -> Reg[A] //│ ║ ^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.88: type Reg[A] = Reg[A] -> Reg[A] //│ ╙── ^ type Id[A] = A //│ Defined type alias Id[+A] // Our regularity check for now is very syntactic: :e type FalseNeg[A] = { x: FalseNeg[Id[A]] } //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as FalseNeg[Id['A]], but is defined as FalseNeg['A] //│ ║ l.103: type FalseNeg[A] = { x: FalseNeg[Id[A]] } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e 42: Id //│ ╔══[ERROR] Type Id takes parameters //│ ║ l.110: 42: Id //│ ╙── ^^ //│ res: error //│ = 42 42: Id[int] //│ res: Id[int] //│ = 42 type Bot = 'a //│ Defined type alias Bot :e 42 : Bot //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.127: 42 : Bot //│ ║ ^^ //│ ╟── integer literal of type `42` does not match type `'a` //│ ╟── Note: constraint arises from type variable: //│ ║ l.123: type Bot = 'a //│ ║ ^^ //│ ╟── from type reference: //│ ║ l.127: 42 : Bot //│ ╙── ^^^ //│ res: Bot //│ = 42 :re error: Bot //│ res: Bot //│ Runtime error: //│ Error: an error was thrown type PolyId = 'a -> 'a //│ Defined type alias PolyId id : PolyId //│ res: PolyId //│ = [Function: id] :e succ : PolyId //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.155: succ : PolyId //│ ║ ^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.147: type PolyId = 'a -> 'a //│ ╙── ^^ //│ res: PolyId //│ = [Function: succ] ================================================ FILE: shared/src/test/diff/mlscript/BadClasses.mls ================================================ :AllowTypeErrors class Box[T]: { inner: T } method Get = this.inner method Get2 = this.x //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.6: method Get2 = this.x //│ ║ ^^^^^^ //│ ╟── reference of type `Box[T] & this` does not have field 'x' //│ ║ l.6: method Get2 = this.x //│ ╙── ^^^^ //│ Defined class Box[+T] //│ Defined Box.Get: Box['T] -> 'T //│ Defined Box.Get2: Box[?] -> error class Box2[T]: { inner: T } method Test = this.inner + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.19: method Test = this.inner + 1 //│ ║ ^^^^^^^^^^^^ //│ ╟── field selection of type `T` is not an instance of type `int` //│ ║ l.19: method Test = this.inner + 1 //│ ║ ^^^^^^^^^^ //│ ╟── Note: class type parameter T is defined at: //│ ║ l.18: class Box2[T]: { inner: T } //│ ╙── ^ //│ Defined class Box2[+T] //│ Defined Box2.Test: Box2[?] -> (error | int) ================================================ FILE: shared/src/test/diff/mlscript/BadInherit.mls ================================================ class Parent1: { name: string } //│ Defined class Parent1 class Parent2: { name: int } //│ Defined class Parent2 :e :ge class Child: Parent1 & Parent2 //│ ╔══[ERROR] class Child cannot inherit from class Parent2 as it already inherits from class Parent1 //│ ║ l.10: class Child: Parent1 & Parent2 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ Code generation encountered an error: //│ cannot have 2 base classes: Parent1, Parent2 type Child = Parent1 & Parent2 def Child: (Parent1 & Parent2) -> Child //│ Defined type alias Child //│ Child: nothing -> Child //│ = def f(x:Child) = x.name //│ f: Child -> nothing //│ = [Function: f] def f(x:Child) = x: nothing //│ f: Child -> nothing //│ = [Function: f1] :pe def c = Child(Parent1{name="A"} with Parent2{name="B"}) //│ /!\ Parse error: Expected ")":1:33, found "with Paren" at l.32:33: def c = Child(Parent1{name="A"} with Parent2{name="B"}) :e def c = Child(Parent1{name="A"}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.36: def c = Child(Parent1{name="A"}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Parent1 & {name: ?name}` is not an instance of type `Parent2` //│ ║ l.36: def c = Child(Parent1{name="A"}) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.18: def Child: (Parent1 & Parent2) -> Child //│ ║ ^^^^^^^ //│ ╟── from intersection type: //│ ║ l.18: def Child: (Parent1 & Parent2) -> Child //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ c: error | Child //│ = //│ Child is not implemented :e :ge type Sly1 = Parent2 class Child2: Parent1 & Sly1 //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.56: class Child2: Parent1 & Sly1 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ Defined type alias Sly1 //│ Code generation encountered an error: //│ cannot inherit from type alias Sly1 :e :ge type Sly2[A] = A class Child2: Sly2[Parent1] & Parent2 //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.67: class Child2: Sly2[Parent1] & Parent2 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ Defined type alias Sly2[+A] //│ Code generation encountered an error: //│ cannot inherit from type alias Sly2 :e :ge class Crazy[A]: A & { name: A } //│ ╔══[ERROR] cannot inherit from a type variable //│ ║ l.77: class Crazy[A]: A & { name: A } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ Code generation encountered an error: //│ undeclared type name A when resolving base classes class Crazy[A]: { name: A } def Crazy: 'a -> (Crazy['a] & 'a) //│ Defined class Crazy[+A] //│ Crazy: 'a -> (Crazy['a] & 'a) //│ = Crazy //│ res: 'a -> (Crazy['a] & 'a) //│ = //│ Crazy is not implemented def c = Crazy({ name = "Bob"; age = 42 }) //│ c: Crazy[{age: 42, name: "Bob"}] with {age: 42, name: "Bob"} //│ = //│ Crazy is not implemented :e c: nothing //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.101: c: nothing //│ ║ ^ //│ ╟── record literal of type `{age: 42, name: "Bob"}` does not match type `~Crazy['a]` //│ ║ l.95: def c = Crazy({ name = "Bob"; age = 42 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `~Crazy['a0]` //│ ║ l.101: c: nothing //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.101: c: nothing //│ ╙── ^^^^^^^ //│ res: nothing //│ = //│ c and Crazy are not implemented def d = Crazy(Parent1{name = "Bob"}) //│ d: nothing //│ = //│ Crazy is not implemented d: nothing //│ res: nothing //│ = //│ d and Crazy are not implemented :e class Stupid: Parent1 | Parent2 //│ ╔══[ERROR] cannot inherit from a type union //│ ║ l.130: class Stupid: Parent1 | Parent2 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ :e class Stupid: Parent1 -> Parent2 //│ ╔══[ERROR] cannot inherit from a function type //│ ║ l.135: class Stupid: Parent1 -> Parent2 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ :e class Stupid: ~Parent1 //│ ╔══[ERROR] cannot inherit from a type negation //│ ║ l.140: class Stupid: ~Parent1 //│ ╙── ^^^^^^^^^^^^^^^^ :e // TODO don't report several times :ge class Cycle1: Cycle2 class Cycle2: Cycle1 type Stutter = Cycle1 //│ ╔══[ERROR] illegal cycle involving type Cycle1 //│ ║ l.147: class Cycle1: Cycle2 //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] illegal cycle involving type Cycle2 //│ ║ l.148: class Cycle2: Cycle1 //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] illegal cycle involving type Cycle1 //│ ║ l.149: type Stutter = Cycle1 //│ ╙── ^^^^^^^^^^^^^^^^ //│ Code generation encountered an error: //│ cyclic inheritance detected :e def c = Cycle1 error c: Cycle1 //│ ╔══[ERROR] identifier not found: Cycle1 //│ ║ l.163: def c = Cycle1 error //│ ╙── ^^^^^^ //│ c: error //│ = [Function: c1] //│ ╔══[ERROR] type identifier not found: Cycle1 //│ ║ l.164: c: Cycle1 //│ ╙── ^^^^^^ //│ res: error //│ Runtime error: //│ ReferenceError: Cycle1 is not defined type N[A] = ~A //│ Defined type alias N[-A] :e :ge class Cycle: N[Cycle] //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.183: class Cycle: N[Cycle] //│ ╙── ^^^^^^^^^^^^^^^ //│ Code generation encountered an error: //│ cannot inherit from type alias N :ng // TODO forbid class D: int //│ Defined class D :ge add (D{}) 2 //│ res: int //│ Code generation encountered an error: //│ unresolved symbol D // TODO forbid class E: 1 //│ Defined class E :e E{}: 1 E{}: int E{} + 1 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.209: E{}: 1 //│ ║ ^^^ //│ ╟── application of type `E` does not match type `1` //│ ╟── Note: constraint arises from literal type: //│ ║ l.209: E{}: 1 //│ ║ ^ //│ ╟── Note: class E is defined at: //│ ║ l.205: class E: 1 //│ ╙── ^ //│ res: 1 //│ = E {} //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.210: E{}: int //│ ║ ^^^ //│ ╟── application of type `E` is not an instance of type `int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.210: E{}: int //│ ║ ^^^ //│ ╟── Note: class E is defined at: //│ ║ l.205: class E: 1 //│ ╙── ^ //│ res: int //│ = E {} //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.211: E{} + 1 //│ ║ ^^^^^ //│ ╟── application of type `E` is not an instance of type `int` //│ ║ l.211: E{} + 1 //│ ║ ^^^ //│ ╟── Note: class E is defined at: //│ ║ l.205: class E: 1 //│ ╙── ^ //│ res: error | int //│ = '[object Object]1' :e :ge class F: nothing F{} //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.250: class F: nothing //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: F //│ ║ l.251: F{} //│ ╙── ^ //│ res: error //│ Code generation encountered an error: //│ cannot inherit from type alias nothing :e class String class Bool //│ ╔══[ERROR] Type name 'String' is reserved. //│ ║ l.264: class String //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type name 'Bool' is reserved. //│ ║ l.265: class Bool //│ ╙── ^^^^ //│ Defined class String //│ Defined class Bool :e "1" : String true : Bool //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.276: "1" : String //│ ║ ^^^ //│ ╟── string literal of type `"1"` is not an instance of type `String` //│ ╟── Note: constraint arises from type reference: //│ ║ l.276: "1" : String //│ ╙── ^^^^^^ //│ res: String //│ = '1' //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.277: true : Bool //│ ║ ^^^^ //│ ╟── reference of type `true` is not an instance of type `Bool` //│ ╟── Note: constraint arises from type reference: //│ ║ l.277: true : Bool //│ ╙── ^^^^ //│ res: Bool //│ = true :e class Weird: {} | {} //│ ╔══[ERROR] cannot inherit from a type union //│ ║ l.299: class Weird: {} | {} //│ ╙── ^^^^^^^^^^^^^^ :e :ge class A type Id[T] = T class B: Id[A] //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.309: class B: Id[A] //│ ╙── ^^^^^^^^ //│ Defined class A //│ Defined type alias Id[+T] //│ Code generation encountered an error: //│ cannot inherit from type alias Id :e // TODO improve prov of the TV class Class3A class Class3B: Class3A & 'a //│ ╔══[ERROR] cannot inherit from a polymorphic type //│ ║ l.321: class Class3B: Class3A & 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ Defined class Class3A ================================================ FILE: shared/src/test/diff/mlscript/BadInherit2.mls ================================================ // ——— trait S0[A] method Foo0: A -> A trait T0[B]: S0[B] //│ Defined trait S0[=A] //│ Declared S0.Foo0: S0['A] -> 'A -> 'A //│ Defined trait T0[=B] :e class A0: S0[int] & T0[string] (A0{}).Foo0 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ╙── ^^^ //│ Defined class A0 //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.15: (A0{}).Foo0 //│ ║ ^^ //│ ╟── Note that class A0 is abstract: //│ ║ l.14: class A0: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Hint: method Foo0 is abstract //│ ║ l.14: class A0: S0[int] & T0[string] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: 'A -> 'A //│ = undefined :e class A0_2: S0[int] & T0[string] method Foo0 x = 1 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.49: method Foo0 x = 1 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `string` //│ ║ l.49: method Foo0 x = 1 //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.48: class A0_2: S0[int] & T0[string] //│ ╙── ^^^^^^ //│ Defined class A0_2 //│ Defined A0_2.Foo0: A0_2 -> anything -> 1 (A0_2{}).Foo0 //│ res: ('A & (int | string)) -> 'A //│ = [Function: Foo0] // ——— trait R1[A] method Foo1: A -> A //│ Defined trait R1[=A] //│ Declared R1.Foo1: R1['A] -> 'A -> 'A trait S1: R1[int] method Foo1 x = 1 trait T1: R1[string] method Foo1 x = "a" //│ Defined trait S1 //│ Defined S1.Foo1: S1 -> anything -> 1 //│ Defined trait T1 //│ Defined T1.Foo1: T1 -> anything -> "a" :e class A1: S1 & T1 //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.103: class A1: S1 & T1 //│ ║ ^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.93: trait S1: R1[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.95: trait T1: R1[string] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.103: class A1: S1 & T1 //│ ║ ^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.95: trait T1: R1[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.93: trait S1: R1[int] //│ ╙── ^^^ //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.103: class A1: S1 & T1 //│ ║ ^^ //│ ╟── Definitions of method Foo1 inherited from: //│ ╟── • S1 //│ ║ l.94: method Foo1 x = 1 //│ ║ ^^^^^^^^^^ //│ ╟── • T1 //│ ║ l.96: method Foo1 x = "a" //│ ╙── ^^^^^^^^^^^^ //│ Defined class A1 a1 = A1{} //│ a1: A1 //│ = A1 { Foo1: [Function (anonymous)] } a1.Foo1 //│ res: ('A & (int | string)) -> 'A //│ = [Function (anonymous)] a1: S1 a1: R1[int] a1: R1[string] a1: R1['_] //│ res: S1 //│ = A1 { Foo1: [Function (anonymous)] } //│ res: R1[int] //│ = A1 { Foo1: [Function (anonymous)] } //│ res: R1[string] //│ = A1 { Foo1: [Function (anonymous)] } //│ res: R1['_] //│ where //│ '_ <: int | string //│ = A1 { Foo1: [Function (anonymous)] } :ns a1: R1['_] //│ res: R1['_] //│ where //│ '_ <: int | string //│ = A1 { Foo1: [Function (anonymous)] } :e :js class A1_2: S1 & T1 method Foo1 = error //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.167: class A1_2: S1 & T1 //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.93: trait S1: R1[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.95: trait T1: R1[string] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.167: class A1_2: S1 & T1 //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.95: trait T1: R1[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.93: trait S1: R1[int] //│ ╙── ^^^ //│ Defined class A1_2 //│ Defined A1_2.Foo1: A1_2 -> nothing //│ // Prelude //│ function error() { //│ throw new Error("an error was thrown"); //│ } //│ class A1_2 { //│ constructor(fields) { //│ S1.implement(this); //│ T1.implement(this); //│ } //│ get Foo1() { //│ return error(); //│ } //│ } //│ // End of generated code :re (A1_2{}).Foo1 //│ res: ('A & (int | string)) -> 'A //│ Runtime error: //│ Error: an error was thrown f = 0 //│ f: 0 //│ = 0 f' = 1 //│ f': 1 //│ = 1 f' //│ res: 1 //│ = 1 class C0[A]: S0[A] method Foo0 = id //│ Defined class C0[=A] //│ Defined C0.Foo0: C0['A] -> 'a -> 'a def s0: S0['a] -> S0['a] //│ s0: S0['a] -> S0['a] //│ = s0 (C0{}) //│ res: S0['a] //│ = //│ s0 is not implemented def s0: S0[?] -> S0[?] //│ s0: S0[?] -> S0[?] //│ = :e s0 (C0{}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.244: s0 (C0{}) //│ ║ ^^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.239: def s0: S0[?] -> S0[?] //│ ╙── ^ //│ res: error | S0[?] //│ = //│ s0 is not implemented ================================================ FILE: shared/src/test/diff/mlscript/BadInherit2Co.mls ================================================ // ——— :e trait S00[A] method Foo00: A trait T00[B]: S00[B] class A00: S00[int] & T00[string] //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.10: class A00: S00[int] & T00[string] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type definition: //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.10: class A00: S00[int] & T00[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.10: class A00: S00[int] & T00[string] //│ ╙── ^^^ //│ Defined trait S00[+A] //│ Declared S00.Foo00: S00['A] -> 'A //│ Defined trait T00[+B] //│ Defined class A00 // Note: the definition above becomes valid if we split it into two definition groups // so that the variance analysis has a chance of running in between... trait S0[A] method Foo0: A trait T0[B]: S0[B] //│ Defined trait S0[+A] //│ Declared S0.Foo0: S0['A] -> 'A //│ Defined trait T0[+B] class A0: S0[int] & T0[string] //│ Defined class A0 :e (A0{}).Foo0 //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.48: (A0{}).Foo0 //│ ║ ^^ //│ ╟── Note that class A0 is abstract: //│ ║ l.44: class A0: S0[int] & T0[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Hint: method Foo0 is abstract //│ ║ l.44: class A0: S0[int] & T0[string] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: nothing //│ = undefined :e class A0_2: S0[int] & T0[string] method Foo0 = 1 //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.63: method Foo0 = 1 //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `string` //│ ║ l.63: method Foo0 = 1 //│ ║ ^ //│ ╟── but it flows into method definition with expected type `string` //│ ║ l.63: method Foo0 = 1 //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.62: class A0_2: S0[int] & T0[string] //│ ║ ^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.38: method Foo0: A //│ ╙── ^^^^^^^ //│ Defined class A0_2 //│ Defined A0_2.Foo0: A0_2 -> 1 (A0_2{}).Foo0 //│ res: nothing //│ = 1 // ——— trait R1[A] method Foo1: A //│ Defined trait R1[+A] //│ Declared R1.Foo1: R1['A] -> 'A trait S1: R1[int] method Foo1 = 1 trait T1: R1[string] method Foo1 = "a" //│ Defined trait S1 //│ Defined S1.Foo1: S1 -> 1 //│ Defined trait T1 //│ Defined T1.Foo1: T1 -> "a" :e class A1: S1 & T1 //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.105: class A1: S1 & T1 //│ ║ ^^ //│ ╟── Definitions of method Foo1 inherited from: //│ ╟── • S1 //│ ║ l.96: method Foo1 = 1 //│ ║ ^^^^^^^^ //│ ╟── • T1 //│ ║ l.98: method Foo1 = "a" //│ ╙── ^^^^^^^^^^ //│ Defined class A1 a1 = A1{} //│ a1: A1 //│ = A1 {} a1.Foo1 //│ res: nothing //│ = 1 a1: S1 a1: R1[int] a1: R1[string] a1: R1['_] //│ res: S1 //│ = A1 {} //│ res: R1[int] //│ = A1 {} //│ res: R1[string] //│ = A1 {} //│ res: R1[nothing] //│ = A1 {} :ns a1: R1['_] //│ res: R1['_] //│ = A1 {} :js class A1_2: S1 & T1 method Foo1 = error //│ Defined class A1_2 //│ Defined A1_2.Foo1: A1_2 -> nothing //│ // Prelude //│ function error() { //│ throw new Error("an error was thrown"); //│ } //│ class A1_2 { //│ constructor(fields) { //│ S1.implement(this); //│ T1.implement(this); //│ } //│ get Foo1() { //│ return error(); //│ } //│ } //│ // End of generated code :re (A1_2{}).Foo1 //│ res: nothing //│ Runtime error: //│ Error: an error was thrown f = 0 //│ f: 0 //│ = 0 f' = 1 //│ f': 1 //│ = 1 f' //│ res: 1 //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/BadMethods.mls ================================================ :AllowTypeErrors class Foo[A]: { x: A } method Map[B]: (A -> B) -> A class Bar[A]: Foo[A] method Map[B]: B -> A rec method Map f = f Map (fun x -> Map) //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.7: rec method Map f = f Map (fun x -> Map) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `B` is not a function //│ ║ l.6: method Map[B]: B -> A //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.7: rec method Map f = f Map (fun x -> Map) //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.7: rec method Map f = f Map (fun x -> Map) //│ ╙── ^ //│ Defined class Foo[+A] //│ Declared Foo.Map: Foo['A] -> ('A -> anything) -> 'A //│ Defined class Bar[+A] //│ Declared Bar.Map: Bar['A] -> anything -> 'A //│ Defined Bar.Map: Bar[?] -> 'Map //│ where //│ 'Map :> ('Map -> (anything -> 'Map) -> 'a) -> 'a Foo.Map Bar.Map //│ res: Foo['A] -> ('A -> anything) -> 'A //│ res: Bar['A] -> anything -> 'A class bar[A, B]: Foo[A] & { Y: B; z: int } method identity z = z method identity z = z //│ ╔══[ERROR] Type names must start with a capital letter //│ ║ l.36: class bar[A, B]: Foo[A] & { Y: B; z: int } //│ ╙── ^^^ //│ ╔══[ERROR] Type 'Bar' is already defined. //│ ║ l.36: class bar[A, B]: Foo[A] & { Y: B; z: int } //│ ╙── ^^^ class ImplicitCall[A]: { x: A } method Fun = this.x //│ Defined class ImplicitCall[+A] //│ Defined ImplicitCall.Fun: ImplicitCall['A] -> 'A i = ImplicitCall { x = "stonks" } i.Fun //│ i: ImplicitCall["stonks"] //│ res: "stonks" class NoMoreImplicitCall method Fun = "not stonks" //│ Defined class NoMoreImplicitCall //│ Defined NoMoreImplicitCall.Fun: NoMoreImplicitCall -> "not stonks" i.Fun //│ ╔══[ERROR] Implicit call to method Fun is forbidden because it is ambiguous. //│ ║ l.64: i.Fun //│ ║ ^^^^^ //│ ╟── Unrelated methods named Fun are defined by: //│ ╟── • class ImplicitCall //│ ║ l.49: class ImplicitCall[A]: { x: A } //│ ║ ^^^^^^^^^^^^ //│ ╟── • class NoMoreImplicitCall //│ ║ l.59: class NoMoreImplicitCall //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ res: error def this = fun x -> fun y -> add x y this 42 42 //│ this: int -> int -> int //│ res: int class BadThis: { x: int; y: int } method Sum = this this.x this.y method Funny = this 42 42 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.86: method Sum = this this.x this.y //│ ║ ^^^^^^^^^^^ //│ ╟── reference of type `BadThis & this` is not a function //│ ║ l.86: method Sum = this this.x this.y //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.87: method Funny = this 42 42 //│ ║ ^^^^^^^ //│ ╟── reference of type `BadThis & this` is not a function //│ ║ l.87: method Funny = this 42 42 //│ ╙── ^^^^ //│ Defined class BadThis //│ Defined BadThis.Sum: BadThis -> error //│ Defined BadThis.Funny: BadThis -> error class BadSelf[A]: { x: A } method F = this.x 42 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.108: method F = this.x 42 //│ ║ ^^^^^^^^^ //│ ╟── field selection of type `A` is not a function //│ ║ l.108: method F = this.x 42 //│ ║ ^^^^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.107: class BadSelf[A]: { x: A } //│ ╙── ^ //│ Defined class BadSelf[+A] //│ Defined BadSelf.F: BadSelf[?] -> error c = BadSelf { x = fun x -> x } c.F //│ c: BadSelf[forall 'a. 'a -> 'a] //│ res: error class Simple2[A]: { a: A } method Get: A //│ Defined class Simple2[+A] //│ Declared Simple2.Get: Simple2['A] -> 'A class Simple3[A, B]: Simple2[A] method Get: B //│ ╔══[ERROR] Type mismatch in method declaration: //│ ║ l.135: method Get: B //│ ║ ^^^^^^ //│ ╟── type `B` does not match type `A` //│ ║ l.134: class Simple3[A, B]: Simple2[A] //│ ║ ^ //│ ╟── Note: constraint arises from class type parameter: //│ ║ l.134: class Simple3[A, B]: Simple2[A] //│ ╙── ^ //│ Defined class Simple3[+A, +B] //│ Declared Simple3.Get: Simple3[?, 'B] -> 'B class AbstractPair[A, B]: { x: A; y: B } method Test: (A -> B -> bool) -> bool method Map[C, D]: (A -> C) -> (B -> D) -> AbstractPair[C, D] //│ Defined class AbstractPair[+A, +B] //│ Declared AbstractPair.Test: AbstractPair['A, 'B] -> ('A -> 'B -> bool) -> bool //│ Declared AbstractPair.Map: AbstractPair['A, 'B] -> ('A -> 'C) -> ('B -> 'D) -> AbstractPair['C, 'D] class BadPair[A, B]: AbstractPair[A, B] method Test f = f this.x this.x method Map fx fy = BadPair { x = fx this.x; y = fx this.x } //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.159: method Test f = f this.x this.x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── field selection of type `A` does not match type `B` //│ ║ l.159: method Test f = f this.x this.x //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from class type parameter: //│ ║ l.158: class BadPair[A, B]: AbstractPair[A, B] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.158: class BadPair[A, B]: AbstractPair[A, B] //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.160: method Map fx fy = BadPair { x = fx this.x; y = fx this.x } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `C` does not match type `D` //│ ║ l.160: method Map fx fy = BadPair { x = fx this.x; y = fx this.x } //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from method type parameter: //│ ║ l.153: method Map[C, D]: (A -> C) -> (B -> D) -> AbstractPair[C, D] //│ ║ ^ //│ ╟── Note: method type parameter C is defined at: //│ ║ l.153: method Map[C, D]: (A -> C) -> (B -> D) -> AbstractPair[C, D] //│ ╙── ^ //│ Defined class BadPair[+A, +B] //│ Defined BadPair.Test: BadPair['A, ?] -> ('A -> 'A -> 'a) -> 'a //│ Defined BadPair.Map: BadPair['A, ?] -> ('A -> 'x) -> anything -> BadPair['x, 'x] bp = BadPair { x = 42; y = true } bp.(BadPair.Test) (fun x -> fun y -> if (y) then x else y) //│ bp: BadPair[42, true] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.190: bp.(BadPair.Test) (fun x -> fun y -> if (y) then x else y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `42` is not an instance of type `bool` //│ ║ l.189: bp = BadPair { x = 42; y = true } //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.190: bp.(BadPair.Test) (fun x -> fun y -> if (y) then x else y) //│ ╙── ^ //│ res: 42 | error BadPair = BadPair { x = 42; y = 0 } BadPair.Map BadPair.(BadPair.Map) //│ BadPair: BadPair[42, 0] //│ res: BadPair['A, ?] -> (forall 'x. ('A -> 'x) -> anything -> BadPair['x, 'x]) //│ ╔══[ERROR] Class BadPair has no method BadPair.Map //│ ║ l.205: BadPair.(BadPair.Map) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: (42 -> 'x) -> anything -> BadPair['x, 'x] class ClassA method MtdA = 42 //│ Defined class ClassA //│ Defined ClassA.MtdA: ClassA -> 42 :e class ClassB: ClassA method MtdA = 43 //│ ╔══[ERROR] Overriding method ClassA.MtdA without explicit declaration is not allowed. //│ ║ l.221: method MtdA = 43 //│ ║ ^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.221: method MtdA = 43 //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `43` does not match type `42` //│ ║ l.221: method MtdA = 43 //│ ║ ^^ //│ ╟── but it flows into method definition with expected type `42` //│ ║ l.221: method MtdA = 43 //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.215: method MtdA = 42 //│ ║ ^^ //│ ╟── from inherited method definition: //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ Defined class ClassB //│ Defined ClassB.MtdA: ClassB -> 43 :e class ClassC: ClassA method MtdA: int method MtdA = 43 //│ ╔══[ERROR] Overriding method ClassA.MtdA without explicit declaration is not allowed. //│ ║ l.248: method MtdA: int //│ ║ ^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method declaration: //│ ║ l.248: method MtdA: int //│ ║ ^^^^^^^^^ //│ ╟── type `int` does not match type `42` //│ ║ l.248: method MtdA: int //│ ║ ^^^ //│ ╟── but it flows into method declaration with expected type `42` //│ ║ l.248: method MtdA: int //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.215: method MtdA = 42 //│ ║ ^^ //│ ╟── from inherited method definition: //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ Defined class ClassC //│ Declared ClassC.MtdA: ClassC -> int //│ Defined ClassC.MtdA: ClassC -> 43 :e class ClassD: ClassA method MtdA: int //│ ╔══[ERROR] Overriding method ClassA.MtdA without explicit declaration is not allowed. //│ ║ l.277: method MtdA: int //│ ║ ^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method declaration: //│ ║ l.277: method MtdA: int //│ ║ ^^^^^^^^^ //│ ╟── type `int` does not match type `42` //│ ║ l.277: method MtdA: int //│ ║ ^^^ //│ ╟── but it flows into method declaration with expected type `42` //│ ║ l.277: method MtdA: int //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.215: method MtdA = 42 //│ ║ ^^ //│ ╟── from inherited method definition: //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ Defined class ClassD //│ Declared ClassD.MtdA: ClassD -> int // TODO: remove repeated overriding errors? class ClassE: ClassD method MtdA = 43 //│ ╔══[ERROR] Overriding method ClassA.MtdA without explicit declaration is not allowed. //│ ║ l.304: method MtdA = 43 //│ ║ ^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.304: method MtdA = 43 //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `43` does not match type `42` //│ ║ l.304: method MtdA = 43 //│ ║ ^^ //│ ╟── but it flows into method definition with expected type `42` //│ ║ l.304: method MtdA = 43 //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.215: method MtdA = 42 //│ ║ ^^ //│ ╟── from inherited method definition: //│ ║ l.215: method MtdA = 42 //│ ╙── ^^^^^^^^^ //│ Defined class ClassE //│ Defined ClassE.MtdA: ClassE -> 43 (ClassE{}).MtdA // typed as 42! //│ res: 42 :e class Class2A[A] method MtdA: A trait Trait2A[B] method MtdA: B class Class2B: Class2A[int] & Trait2A[string] method MtdA = "ok" //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.339: method MtdA = "ok" //│ ║ ^^^^^^^^^^^ //│ ╟── string literal of type `"ok"` is not an instance of type `int` //│ ║ l.339: method MtdA = "ok" //│ ║ ^^^^ //│ ╟── but it flows into method definition with expected type `int` //│ ║ l.339: method MtdA = "ok" //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.338: class Class2B: Class2A[int] & Trait2A[string] //│ ║ ^^^ //│ ╟── from inherited method declaration: //│ ║ l.335: method MtdA: A //│ ╙── ^^^^^^^ //│ Defined class Class2A[+A] //│ Declared Class2A.MtdA: Class2A['A] -> 'A //│ Defined trait Trait2A[+B] //│ Declared Trait2A.MtdA: Trait2A['B] -> 'B //│ Defined class Class2B //│ Defined Class2B.MtdA: Class2B -> "ok" :e class Class3A[A] method MtdA: A type Type3A = Class3A[string] class Class3B: Type3A method MtdA = 1 //│ ╔══[ERROR] cannot inherit from a type alias //│ ║ l.367: class Class3B: Type3A //│ ╙── ^^^^^^^^^^^^^^^ //│ Defined class Class3A[+A] //│ Declared Class3A.MtdA: Class3A['A] -> 'A //│ Defined type alias Type3A :e Oops.M //│ ╔══[ERROR] Method M not found //│ ║ l.378: Oops.M //│ ╙── ^^^^^^ //│ res: error class Test4A method Mth4A: anything method Mth4A = true //│ Defined class Test4A //│ Declared Test4A.Mth4A: Test4A -> anything //│ Defined Test4A.Mth4A: Test4A -> true :e class Test4B: Test4A method Mth4A: int //│ ╔══[ERROR] Type mismatch in inherited method definition: //│ ║ l.387: method Mth4A = true //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.387: method Mth4A = true //│ ║ ^^^^ //│ ╟── but it flows into inherited method definition with expected type `int` //│ ║ l.387: method Mth4A = true //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.393: method Mth4A: int //│ ║ ^^^ //│ ╟── from method declaration: //│ ║ l.393: method Mth4A: int //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Overriding method Test4A.Mth4A without an overriding definition is not allowed. //│ ║ l.393: method Mth4A: int //│ ║ ^^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.387: method Mth4A = true //│ ╙── ^^^^^^^^^^^^ //│ Defined class Test4B //│ Declared Test4B.Mth4A: Test4B -> int o = Test4A {} o.Mth4A o.(Test4A.Mth4A) //│ o: Test4A //│ res: anything //│ res: anything :e class Test5A method Mth5A: 42 class Test5B: Test5A method Mth5A = 43 //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.429: method Mth5A = 43 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `43` does not match type `42` //│ ║ l.429: method Mth5A = 43 //│ ║ ^^ //│ ╟── but it flows into method definition with expected type `42` //│ ║ l.429: method Mth5A = 43 //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.427: method Mth5A: 42 //│ ║ ^^ //│ ╟── from inherited method declaration: //│ ║ l.427: method Mth5A: 42 //│ ╙── ^^^^^^^^^ //│ Defined class Test5A //│ Declared Test5A.Mth5A: Test5A -> 42 //│ Defined class Test5B //│ Defined Test5B.Mth5A: Test5B -> 43 class Test6A[A] method Mth6A: A method Mth6B[B]: (A -> B) -> B trait Test6B method Mth6A: bool class Test6C: Test6A[int] & Test6B //│ Defined class Test6A[+A] //│ Declared Test6A.Mth6A: Test6A['A] -> 'A //│ Declared Test6A.Mth6B: Test6A['A] -> ('A -> 'B) -> 'B //│ Defined trait Test6B //│ Declared Test6B.Mth6A: Test6B -> bool //│ Defined class Test6C :e Test6A //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.465: Test6A //│ ║ ^^^^^^ //│ ╟── Note that class Test6A is abstract: //│ ║ l.451: class Test6A[A] //│ ║ ^^^^^^^^ //│ ╟── Hint: method Mth6A is abstract //│ ║ l.452: method Mth6A: A //│ ║ ^^^^^^^^ //│ ╟── Hint: method Mth6B is abstract //│ ║ l.453: method Mth6B[B]: (A -> B) -> B //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error :e Test6B //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.481: Test6B //│ ║ ^^^^^^ //│ ╟── Note that trait Test6B is abstract: //│ ║ l.454: trait Test6B //│ ║ ^^^^^^ //│ ╟── Hint: method Mth6A is abstract //│ ║ l.455: method Mth6A: bool //│ ╙── ^^^^^^^^^^^ //│ res: error :e Test6C //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.494: Test6C //│ ║ ^^^^^^ //│ ╟── Note that class Test6C is abstract: //│ ║ l.456: class Test6C: Test6A[int] & Test6B //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Hint: method Mth6B is abstract //│ ║ l.453: method Mth6B[B]: (A -> B) -> B //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Hint: method Mth6A is abstract //│ ║ l.456: class Test6C: Test6A[int] & Test6B //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error :e class Dup[A, A]: { x: A } method MthDup[B, C, // random comment B]: (A -> B) -> B method MthDup f = f this.x //│ ╔══[ERROR] Multiple declarations of type parameter A in class definition //│ ║ l.513: class Dup[A, A]: { x: A } //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Declared at //│ ║ l.513: class Dup[A, A]: { x: A } //│ ║ ^ //│ ╟── Declared at //│ ║ l.513: class Dup[A, A]: { x: A } //│ ╙── ^ //│ ╔══[ERROR] Multiple declarations of type parameter B in method declaration //│ ║ l.514: method MthDup[B, C, // random comment //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.515: B]: (A -> B) -> B //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Declared at //│ ║ l.514: method MthDup[B, C, // random comment //│ ║ ^ //│ ╟── Declared at //│ ║ l.515: B]: (A -> B) -> B //│ ╙── ^ //│ Defined class Dup[±A, +A] //│ Declared Dup.MthDup: Dup[?, 'A] -> ('A -> 'B) -> 'B //│ Defined Dup.MthDup: Dup[?, 'A] -> ('A -> 'a) -> 'a //│ ╔══[WARNING] Type definition Dup has bivariant type parameters: //│ ║ l.513: class Dup[A, A]: { x: A } //│ ║ ^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.513: class Dup[A, A]: { x: A } //│ ╙── ^ t = Dup { x = 42 } //│ t: Dup[?, ?] & {x: 42} :stats t : Dup[bool, int] //│ res: Dup[?, int] //│ constrain calls : 25 //│ annoying calls : 21 //│ subtyping calls : 54 :stats t : Dup[int, bool] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.558: t : Dup[int, bool] //│ ║ ^ //│ ╟── integer literal of type `42` is not an instance of type `bool` //│ ║ l.547: t = Dup { x = 42 } //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.558: t : Dup[int, bool] //│ ╙── ^^^^ //│ res: Dup[?, bool] //│ constrain calls : 28 //│ annoying calls : 24 //│ subtyping calls : 67 :stats t.MthDup (fun x -> mul 2 x) //│ res: int //│ constrain calls : 66 //│ annoying calls : 21 //│ subtyping calls : 179 :stats t.MthDup id //│ res: 42 //│ constrain calls : 54 //│ annoying calls : 20 //│ subtyping calls : 136 // We don't currently analyze forward method declarations :e class A method Yes = B { x = 1 } method Nope = this.Yes.F1 class B: { x: int } method F1: int method F1 = 1 //│ ╔══[ERROR] Method F1 not found //│ ║ l.592: method Nope = this.Yes.F1 //│ ╙── ^^^^^^^^^^^ //│ Defined class A //│ Defined A.Yes: A -> (B & {x: 1}) //│ Defined A.Nope: A -> error //│ Defined class B //│ Declared B.F1: B -> int //│ Defined B.F1: B -> 1 trait D method G = 1 trait E method G: int class H: D & E method G = 2 //│ ╔══[ERROR] Overriding method D.G without explicit declaration is not allowed. //│ ║ l.612: method G = 2 //│ ║ ^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.608: method G = 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.612: method G = 2 //│ ║ ^^^^^ //│ ╟── integer literal of type `2` does not match type `1` //│ ║ l.612: method G = 2 //│ ║ ^ //│ ╟── but it flows into method definition with expected type `1` //│ ║ l.612: method G = 2 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.608: method G = 1 //│ ║ ^ //│ ╟── from inherited method definition: //│ ║ l.608: method G = 1 //│ ╙── ^^^^^ //│ Defined trait D //│ Defined D.G: D -> 1 //│ Defined trait E //│ Declared E.G: E -> int //│ Defined class H //│ Defined H.G: H -> 2 h = H{} //│ h: H d = h: D //│ d: D d.(D.G) //│ res: 1 ((H{}): D).(D.G) //│ res: 1 trait D2 method G2 = 1 trait E2 method G2: bool class H2: D2 & E2 //│ ╔══[ERROR] Type mismatch in inherited method definition: //│ ║ l.655: method G2 = 1 //│ ║ ^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `bool` //│ ║ l.655: method G2 = 1 //│ ║ ^ //│ ╟── but it flows into inherited method definition with expected type `bool` //│ ║ l.655: method G2 = 1 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.657: method G2: bool //│ ║ ^^^^ //│ ╟── from inherited method declaration: //│ ║ l.657: method G2: bool //│ ╙── ^^^^^^^^ //│ Defined trait D2 //│ Defined D2.G2: D2 -> 1 //│ Defined trait E2 //│ Declared E2.G2: E2 -> bool //│ Defined class H2 trait Test7A method Mth7A: int method Mth7A = 42 trait Test7B method Mth7A: int method Mth7A = 43 //│ Defined trait Test7A //│ Declared Test7A.Mth7A: Test7A -> int //│ Defined Test7A.Mth7A: Test7A -> 42 //│ Defined trait Test7B //│ Declared Test7B.Mth7A: Test7B -> int //│ Defined Test7B.Mth7A: Test7B -> 43 :e class Test7C: Test7A & Test7B //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.697: class Test7C: Test7A & Test7B //│ ║ ^^^^^^ //│ ╟── Definitions of method Mth7A inherited from: //│ ╟── • Test7A //│ ║ l.685: method Mth7A = 42 //│ ║ ^^^^^^^^^^ //│ ╟── • Test7B //│ ║ l.688: method Mth7A = 43 //│ ╙── ^^^^^^^^^^ //│ Defined class Test7C Test7C.Mth7A //│ res: error class Test7D: Test7A & Test7B method Mth7A = this.(Test7A.Mth7A) * this.(Test7B.Mth7A) //│ Defined class Test7D //│ Defined Test7D.Mth7A: Test7D -> int :e // TODO: suppress? class Test7E: Test7C method Mth7A = 0 //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.697: class Test7C: Test7A & Test7B //│ ║ ^^^^^^ //│ ╟── Definitions of method Mth7A inherited from: //│ ╟── • Test7A //│ ║ l.685: method Mth7A = 42 //│ ║ ^^^^^^^^^^ //│ ╟── • Test7B //│ ║ l.688: method Mth7A = 43 //│ ╙── ^^^^^^^^^^ //│ Defined class Test7E //│ Defined Test7E.Mth7A: Test7E -> 0 trait Test7F method Mth7A = 0 //│ Defined trait Test7F //│ Defined Test7F.Mth7A: Test7F -> 0 :e class Test7G: Test7C & Test7F //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.697: class Test7C: Test7A & Test7B //│ ║ ^^^^^^ //│ ╟── Definitions of method Mth7A inherited from: //│ ╟── • Test7A //│ ║ l.685: method Mth7A = 42 //│ ║ ^^^^^^^^^^ //│ ╟── • Test7B //│ ║ l.688: method Mth7A = 43 //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.740: class Test7G: Test7C & Test7F //│ ║ ^^^^^^ //│ ╟── Definitions of method Mth7A inherited from: //│ ╟── • Test7C //│ ║ l.697: class Test7C: Test7A & Test7B //│ ║ ^^^^^^ //│ ╟── • Test7F //│ ║ l.735: method Mth7A = 0 //│ ╙── ^^^^^^^^^ //│ Defined class Test7G class Test8A method F: int method F = 1 //│ Defined class Test8A //│ Declared Test8A.F: Test8A -> int //│ Defined Test8A.F: Test8A -> 1 :e class Test8B: Test8A method F: 1 //│ ╔══[ERROR] Overriding method Test8A.F without an overriding definition is not allowed. //│ ║ l.775: method F: 1 //│ ║ ^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.768: method F = 1 //│ ╙── ^^^^^ //│ Defined class Test8B //│ Declared Test8B.F: Test8B -> 1 class Test8C[A] method F: A method F = error //│ Defined class Test8C[+A] //│ Declared Test8C.F: Test8C['A] -> 'A //│ Defined Test8C.F: Test8C[?] -> nothing // We used to allow syntactically identitcal declarations for documentation, // but that's actually a bad idea (and the implementation was broken) :e class Test8D: Test8C[int] method F: int //│ ╔══[ERROR] Overriding method Test8C.F without an overriding definition is not allowed. //│ ║ l.796: method F: int //│ ║ ^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.787: method F = error //│ ╙── ^^^^^^^^^ //│ Defined class Test8D //│ Declared Test8D.F: Test8D -> int class Test9A[A]: { x: A } method Mth9A = this.x //│ Defined class Test9A[+A] //│ Defined Test9A.Mth9A: Test9A['A] -> 'A class Test9B: Test9A[int] method Mth9A : Test9A[int] -> int method Mth9A = this.x //│ ╔══[ERROR] Overriding method Test9A.Mth9A without explicit declaration is not allowed. //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.810: method Mth9A = this.x //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method declaration: //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Test9A[int] -> int` does not match type `int | ?x` //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into method declaration with expected type `int | ?x` //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.810: method Mth9A = this.x //│ ║ ^^^^^^ //│ ╟── from inherited method definition: //│ ║ l.810: method Mth9A = this.x //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.816: method Mth9A = this.x //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `int` is not a function //│ ║ l.814: class Test9B: Test9A[int] //│ ║ ^^^ //│ ╟── but it flows into field selection with expected type `Test9A[int] -> int` //│ ║ l.816: method Mth9A = this.x //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── from method declaration: //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ Defined class Test9B //│ Declared Test9B.Mth9A: Test9B -> Test9A[int] -> int //│ Defined Test9B.Mth9A: Test9B -> int :e :NoJS error.(Test9B.Mth9A): nothing //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.859: error.(Test9B.Mth9A): nothing //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Test9A[int] -> int` does not match type `nothing` //│ ║ l.815: method Mth9A : Test9A[int] -> int //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into field selection with expected type `nothing` //│ ║ l.859: error.(Test9B.Mth9A): nothing //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.859: error.(Test9B.Mth9A): nothing //│ ╙── ^^^^^^^ //│ res: nothing ================================================ FILE: shared/src/test/diff/mlscript/BadPolym.mls ================================================ // TL;DR we need a special form of function types that does NOT merge // (as merging approximates unsoundly in the presence of first-class polymorphism) def foo1: (int -> int & string -> string) //│ foo1: int -> int & string -> string //│ = def foo: (int -> int & string -> string) -> () //│ foo: (int -> int & string -> string) -> () //│ = fooImpl f = f 1 //│ fooImpl: (1 -> 'a) -> 'a //│ = [Function: fooImpl] :e foo = fooImpl //│ (1 -> 'a) -> 'a //│ <: foo: //│ (int -> int & string -> string) -> () //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.18: foo = fooImpl //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `int` is not a 0-element tuple //│ ║ l.9: def foo: (int -> int & string -> string) -> () //│ ║ ^^^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.9: def foo: (int -> int & string -> string) -> () //│ ╙── ^^ //│ = [Function: fooImpl] foo id //│ res: () //│ = 1 fooImpl id //│ res: 1 //│ = 1 fooImpl2 (f: int -> int & string -> string) = f 1 //│ fooImpl2: (int -> int & string -> string) -> (int | string) //│ = [Function: fooImpl2] fooImpl2 id //│ res: int | string //│ = 1 :e :re res "oops" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.52: res "oops" //│ ║ ^^^^^^^^^^ //│ ╟── type `int` is not a function //│ ║ l.42: fooImpl2 (f: int -> int & string -> string) = f 1 //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `"oops" -> ?a` //│ ║ l.52: res "oops" //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: res is not a function // Currently we remove the unsoundness by approximating positive function type intersections. // But this leads to things not type checking when they clearly should: // (Basically we lose the property that A & B <: A for all A, B.) def bar1: int -> int & string -> string //│ bar1: int -> int & string -> string //│ = // :e // * Did not work before when added subtype checking to constraining... bar1 = bar1 //│ int -> int & string -> string //│ <: bar1: //│ int -> int & string -> string //│ = //│ bar1 is not implemented :re error: int -> int & string -> string : (int & string) -> (int | string) //│ res: nothing -> (int | string) //│ Runtime error: //│ Error: an error was thrown // :e // * Did not work before when added subtype checking to constraining... :re error: int -> int & string -> string : int -> int //│ res: int -> int //│ Runtime error: //│ Error: an error was thrown :re error: number -> int & string -> string : int -> number //│ res: int -> number //│ Runtime error: //│ Error: an error was thrown // * The tricky thing in these tests is that we currently lossily merge function types // * in constraint solving, so the best-effort subtyping check may actually accept things // * that fail to constrain. But in this case we try to make the check fail by using a type alias. // * It somehow still seems to work well... type Als = number -> int //│ Defined type alias Als :re error: Als & string -> string : int -> number //│ res: int -> number //│ Runtime error: //│ Error: an error was thrown type Id[A] = A //│ Defined type alias Id[+A] :re error: Id[Id[Als] & bool -> bool] & string -> string : int -> number //│ res: int -> number //│ Runtime error: //│ Error: an error was thrown ================================================ FILE: shared/src/test/diff/mlscript/BadTraits.mls ================================================ class A class B //│ Defined class A //│ Defined class B // Okay to define, but impossible to instantiate: trait T: A & B //│ Defined trait T :e T {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.11: T {} //│ ║ ^^^^ //│ ╟── record literal of type `anything` is not an instance of type `A` //│ ║ l.11: T {} //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: trait T: A & B //│ ║ ^ //│ ╟── from intersection type: //│ ║ l.7: trait T: A & B //│ ╙── ^^^^^ //│ res: error | #T //│ = {} a = A{} //│ a: A //│ = A {} :e T a //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.32: T a //│ ║ ^^^ //│ ╟── application of type `A` is not an instance of type `B` //│ ║ l.27: a = A{} //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `B` //│ ║ l.32: T a //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: trait T: A & B //│ ║ ^ //│ ╟── from intersection type: //│ ║ l.7: trait T: A & B //│ ║ ^^^^^ //│ ╟── Note: class A is defined at: //│ ║ l.1: class A //│ ╙── ^ //│ res: A & #T | error //│ = A {} :e :js T A //│ // Query 1 //│ res = T.build((x) => new A(x)); //│ // End of generated code //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.56: T A //│ ║ ^^^ //│ ╟── reference of type `anything -> A` is not an instance of type `A` //│ ║ l.56: T A //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: trait T: A & B //│ ║ ^ //│ ╟── from intersection type: //│ ║ l.7: trait T: A & B //│ ║ ^^^^^ //│ ╟── Note: class constructor A is defined at: //│ ║ l.1: class A //│ ╙── ^ //│ res: error | anything -> A & #T //│ = [Function (anonymous)] :e T (B {}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.79: T (B {}) //│ ║ ^^^^^^^^ //│ ╟── application of type `B` is not an instance of type `A` //│ ║ l.79: T (B {}) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: trait T: A & B //│ ║ ^ //│ ╟── from intersection type: //│ ║ l.7: trait T: A & B //│ ║ ^^^^^ //│ ╟── Note: class B is defined at: //│ ║ l.2: class B //│ ╙── ^ //│ res: B & #T | error //│ = B {} :re t = T error //│ t: nothing //│ Runtime error: //│ Error: an error was thrown :re t: nothing //│ res: nothing //│ Runtime error: //│ ReferenceError: t is not defined type Als = A & B //│ Defined type alias Als :re t: Als //│ res: Als //│ Runtime error: //│ ReferenceError: t is not defined :e class D0: T //│ ╔══[ERROR] class D0 cannot inherit from class B as it already inherits from class A //│ ║ l.121: class D0: T //│ ╙── ^^^^^ :e class D1: B & T //│ ╔══[ERROR] class D1 cannot inherit from class A as it already inherits from class B //│ ║ l.127: class D1: B & T //│ ╙── ^^^^^^^^^ trait S: { x: int } //│ Defined trait S s = S{ x = 1 } //│ s: {x: 1} & #S //│ = { x: 1 } t = s: s //│ t: #S //│ = { x: 1 } :e t.x //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.145: t.x //│ ║ ^^^ //│ ╟── type `#S` does not have field 'x' //│ ║ l.140: t = s: s //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.145: t.x //│ ╙── ^ //│ res: error //│ = 1 t = s: S //│ t: S //│ = { x: 1 } t.x //│ res: int //│ = 1 trait Lol method Foo = 1 class Hey method Foo = "oops" //│ Defined trait Lol //│ Defined Lol.Foo: Lol -> 1 //│ Defined class Hey //│ Defined Hey.Foo: Hey -> "oops" :e class H: Lol & Hey //│ ╔══[ERROR] An overriding method definition must be given when inheriting from multiple method definitions //│ ║ l.177: class H: Lol & Hey //│ ║ ^ //│ ╟── Definitions of method Foo inherited from: //│ ╟── • Lol //│ ║ l.168: method Foo = 1 //│ ║ ^^^^^^^ //│ ╟── • Hey //│ ║ l.170: method Foo = "oops" //│ ╙── ^^^^^^^^^^^^ //│ Defined class H // Forbid trait constructors when the trait contains method defs as well: h = Hey{} //│ h: Hey //│ = Hey {} :e l = Lol h //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.198: l = Lol h //│ ║ ^^^ //│ ╟── Note that traits with methods are always considered abstract //│ ║ l.167: trait Lol //│ ╙── ^^^ //│ l: error //│ = Hey {} :e e = Lol e //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.209: e = Lol //│ ║ ^^^ //│ ╟── Note that traits with methods are always considered abstract //│ ║ l.167: trait Lol //│ ╙── ^^^ //│ e: error //│ = [Function: build] //│ res: error //│ = [Function: build] l.(Lol.Foo) //│ res: 1 //│ = undefined l.(Hey.Foo) //│ res: "oops" //│ = undefined trait Lol2: Lol //│ Defined trait Lol2 :e Lol2 //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.235: Lol2 //│ ║ ^^^^ //│ ╟── Note that traits with methods are always considered abstract //│ ║ l.231: trait Lol2: Lol //│ ╙── ^^^^^^^^^ //│ res: error //│ = [Function: build] ================================================ FILE: shared/src/test/diff/mlscript/BadTypes.mls ================================================ :ShowRelativeLineNums :e class oops: { x: int } //│ ╔══[ERROR] Type names must start with a capital letter //│ ║ l.+1: class oops: { x: int } //│ ╙── ^^^^ :e type oops = int //│ ╔══[ERROR] Type names must start with a capital letter //│ ║ l.+1: type oops = int //│ ╙── ^^^^ //│ ╔══[ERROR] Type 'Oops' is already defined. //│ ║ l.+1: type oops = int //│ ╙── ^^^^ class Oops2 //│ Defined class Oops2 :e class oops2 //│ ╔══[ERROR] Type names must start with a capital letter //│ ║ l.+1: class oops2 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type 'Oops2' is already defined. //│ ║ l.+1: class oops2 //│ ╙── ^^^^^ type Hello = int //│ Defined type alias Hello 42: Hello //│ res: Hello //│ = 42 :e 42: hello //│ ╔══[ERROR] Type alias Hello cannot be used as a type tag //│ ║ l.+1: 42: hello //│ ╙── ^^^^^ //│ res: error //│ = 42 :e aa = { a = 1; a = "oops" } //│ ╔══[ERROR] Multiple declarations of field name a in record literal //│ ║ l.+1: aa = { a = 1; a = "oops" } //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Declared at //│ ║ l.+1: aa = { a = 1; a = "oops" } //│ ║ ^ //│ ╟── Declared at //│ ║ l.+1: aa = { a = 1; a = "oops" } //│ ╙── ^ //│ aa: {a: 1, a: "oops"} //│ = { a: 'oops' } :e aa: { a: int; a: string } //│ ╔══[ERROR] Multiple declarations of field name a in record type //│ ║ l.+1: aa: { a: int; a: string } //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Declared at //│ ║ l.+1: aa: { a: int; a: string } //│ ║ ^ //│ ╟── Declared at //│ ║ l.+1: aa: { a: int; a: string } //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: aa: { a: int; a: string } //│ ║ ^^ //│ ╟── integer literal of type `1` is not an instance of type `string` //│ ║ l.49: aa = { a = 1; a = "oops" } //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.+1: aa: { a: int; a: string } //│ ╙── ^^^^^^ //│ res: {a: int, a: string} //│ = { a: 'oops' } aa.a //│ res: 1 //│ = 'oops' aa: { a: 'a } //│ res: {a: 1} //│ = { a: 'oops' } // Note that free type variables in type definitions are now treated as global (level 0), // i.e., shared between all uses of the data type. // It would of course not be sound to refresh them on every expansion of the type definition type Bad = { x: 'a } //│ Defined type alias Bad :e {x = 123}: Bad //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: {x = 123}: Bad //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `123` does not match type `'a` //│ ║ l.+1: {x = 123}: Bad //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Bad = { x: 'a } //│ ╙── ^^ //│ res: Bad //│ = { x: 123 } :e {x = "ah"}: Bad //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: {x = "ah"}: Bad //│ ║ ^^^^^^^^^^ //│ ╟── string literal of type `"ah"` does not match type `'a` //│ ║ l.+1: {x = "ah"}: Bad //│ ║ ^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: type Bad = { x: 'a } //│ ╙── ^^ //│ res: Bad //│ = { x: 'ah' } {x = "ah"}: { x: 'a } //│ res: {x: "ah"} //│ = { x: 'ah' } :re error: Bad //│ res: Bad //│ Runtime error: //│ Error: an error was thrown type BadRec = { x: 'a; y: BadRec } //│ Defined type alias BadRec :re (fun (x: BadRec) -> ()) (error: BadRec) //│ res: () //│ Runtime error: //│ Error: an error was thrown :re error: BadRec //│ res: BadRec //│ Runtime error: //│ Error: an error was thrown def someRec: { y: 'a } as 'a //│ someRec: 'a //│ where //│ 'a :> {y: 'a} //│ = :e someRec: BadRec //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: someRec: BadRec //│ ║ ^^^^^^^ //│ ╟── type `forall 'a. 'a` does not match type `{x: 'a0, y: BadRec}` //│ ║ l.157: def someRec: { y: 'a } as 'a //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{x: 'a0, y: BadRec}` //│ ║ l.+1: someRec: BadRec //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.142: type BadRec = { x: 'a; y: BadRec } //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.+1: someRec: BadRec //│ ╙── ^^^^^^ //│ res: BadRec //│ = //│ someRec is not implemented def someRec: { x: 'a; y: 'a } as 'a //│ someRec: 'a //│ where //│ 'a :> {x: 'a, y: 'a} //│ = :e someRec: BadRec //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: someRec: BadRec //│ ║ ^^^^^^^ //│ ╟── type `{x: 'a, y: 'a}` does not match type `'a0` //│ ║ l.184: def someRec: { x: 'a; y: 'a } as 'a //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.142: type BadRec = { x: 'a; y: BadRec } //│ ╙── ^^ //│ res: BadRec //│ = //│ someRec is not implemented type SigR[a] = a //│ Defined type alias SigR[+a] :e type SigR[a] = a //│ ╔══[ERROR] Type 'SigR' is already defined. //│ ║ l.+1: type SigR[a] = a //│ ╙── ^^^^ type ExSigR = SigR[int] //│ Defined type alias ExSigR 1: ExSigR //│ res: ExSigR //│ = 1 :e type SigR[a, b] = a //│ ╔══[ERROR] Type 'SigR' is already defined. //│ ║ l.+1: type SigR[a, b] = a //│ ╙── ^^^^ type ExSigR2 = SigR[int] //│ Defined type alias ExSigR2 :e type ExSigR3 = SigR[int, string] //│ ╔══[ERROR] Wrong number of type arguments – expected 1, found 2 //│ ║ l.+1: type ExSigR3 = SigR[int, string] //│ ╙── ^^^^^^^^^^^^^^^^^ //│ Defined type alias ExSigR3 type ExSigR4 = SigR['a] //│ Defined type alias ExSigR4 :e 1: ExSigR4 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.+1: 1: ExSigR4 //│ ║ ^ //│ ╟── integer literal of type `1` does not match type `'a` //│ ╟── Note: constraint arises from type variable: //│ ║ l.239: type ExSigR4 = SigR['a] //│ ║ ^^ //│ ╟── from type reference: //│ ║ l.+1: 1: ExSigR4 //│ ╙── ^^^^^^^ //│ res: ExSigR4 //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/Basics.mls ================================================ x = 1 //│ x: 1 //│ = 1 1 //│ res: 1 //│ = 1 1; 2 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 x = 1; 2 //│ x: 1 //│ = 1 //│ res: 2 //│ = 2 x = 1;; 2 //│ x: 1 //│ = 1 //│ res: 2 //│ = 2 x = 1 y = 2 //│ x: 1 //│ = 1 //│ y: 2 //│ = 2 1 2 3 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 //│ res: 3 //│ = 3 1 x = add x 1 //│ res: 1 //│ = 1 //│ x: int //│ = 2 x = 1 x y = 2 y z = x + y //│ x: 1 //│ = 1 //│ res: 1 //│ = 1 //│ y: 2 //│ = 2 //│ res: 2 //│ = 2 //│ z: int //│ = 3 :e 1 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.72: 1 //│ ║ ^ //│ ║ l.73: 2 //│ ║ ^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.72: 1 //│ ╙── ^ //│ res: error //│ Runtime error: //│ TypeError: 1 is not a function def f x y z = add x y //│ f: int -> int -> anything -> int //│ = [Function: f] :e :ge def f (x y z) = add x y //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.93: def f (x y z) = add x y //│ ╙── ^^^^^ //│ f: error -> int //│ Code generation encountered an error: //│ term App(App(Var(x),Tup(List((None,Fld(_,Var(y)))))),Tup(List((None,Fld(_,Var(z)))))) is not a valid pattern f 1 //│ res: int //│ = [Function (anonymous)] {a = 0; b = 1} //│ res: {a: 0, b: 1} //│ = { a: 0, b: 1 } {a = 0, b = 1} //│ res: {a: 0, b: 1} //│ = { a: 0, b: 1 } :pe {a: 0, b: 1} //│ /!\ Parse error: Expected "}":1:3, found ": 0, b: 1}" at l.115:3: {a: 0, b: 1} ================================================ FILE: shared/src/test/diff/mlscript/Boolean.mls ================================================ type Boolean = Tru | Fals class Tru class Fals def Tru: Tru def Fals: Fals //│ Defined type alias Boolean //│ Defined class Tru //│ Defined class Fals //│ Tru: Tru //│ = //│ Fals: Fals //│ = :js Tru: Boolean //│ // Query 1 aborted: Tru is not implemented //│ // End of generated code //│ res: Boolean //│ = //│ Tru is not implemented :e :ge Boolean //│ ╔══[ERROR] identifier not found: Boolean //│ ║ l.24: Boolean //│ ╙── ^^^^^^^ //│ res: error //│ Code generation encountered an error: //│ type alias Boolean is not a valid expression ================================================ FILE: shared/src/test/diff/mlscript/BooleanFail.mls ================================================ // TODO fix this // * The MLscript subtyping system is currently ill-formed in some corner cases. // * Notably, it considers functions and classes to intersect to nothing // * and also used to considers positive negated function/record types equivalent to nothing. // * (This isn't the case in MLstruct, which has a sound subtyping lattice.) // * Example 1 – now fixed oops = 42 : ~(int -> int) //│ oops: ~(int -> int) //│ = 42 :e not oops //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.19: not oops //│ ║ ^^^^^^^^ //│ ╟── type `~(int -> int)` is not an instance of type `bool` //│ ║ l.14: oops = 42 : ~(int -> int) //│ ║ ^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `bool` //│ ║ l.19: not oops //│ ╙── ^^^^ //│ res: bool | error //│ = false // * This was accepted but didn't immediately lead to immediate unsoundness: def f: (~{x: int}) -> 'a //│ f: ~{x: int} -> nothing //│ = :e f = id //│ 'a -> 'a //│ <: f: //│ ~{x: int} -> nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.39: f = id //│ ║ ^^^^^^ //│ ╟── type `~{x: int}` does not match type `'a` //│ ║ l.34: def f: (~{x: int}) -> 'a //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^ //│ = [Function: id] :e f 0 f id f {} f (forall 'a. fun (x: 'a) -> x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.55: f 0 //│ ║ ^^^ //│ ╟── integer literal of type `0` does not match type `~{x: int}` //│ ║ l.55: f 0 //│ ║ ^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = 0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.56: f id //│ ║ ^^^^ //│ ╟── reference of type `?a -> ?a` does not match type `~{x: int}` //│ ║ l.56: f id //│ ║ ^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = [Function: id] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.57: f {} //│ ║ ^^^^ //│ ╟── record literal of type `anything` does not match type `~{x: int}` //│ ║ l.57: f {} //│ ║ ^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: f (forall 'a. fun (x: 'a) -> x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `'a -> 'a` does not match type `~{x: int}` //│ ║ l.58: f (forall 'a. fun (x: 'a) -> x) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.34: def f: (~{x: int}) -> 'a //│ ╙── ^^^^^^^^^^^ //│ res: error //│ = [Function (anonymous)] // * Example 2 – now fixed def g(x: 'a | {f: nothing}) = x.f(0) //│ g: {f: 0 -> 'a} -> 'a //│ = [Function: g] :e foo = forall 'x. fun (x: 'x) -> g(x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.112: foo = forall 'x. fun (x: 'x) -> g(x) //│ ║ ^^^^ //│ ╟── expression of type `'x & ~{f: nothing}` does not have field 'f' //│ ╟── Note: constraint arises from field selection: //│ ║ l.107: def g(x: 'a | {f: nothing}) = x.f(0) //│ ║ ^^^ //│ ╟── from type variable: //│ ║ l.107: def g(x: 'a | {f: nothing}) = x.f(0) //│ ╙── ^^ //│ foo: anything -> error //│ = [Function: foo] :re foo 0 //│ res: error //│ Runtime error: //│ TypeError: x.f is not a function // * Now let's consider why functions and classes can't intersect to nothing due to distributivity class Foo: { x: anything } //│ Defined class Foo // * These two types should be equivalent, but they visibly aren't: def a: (int -> int | {x: int}) & Foo def b: int -> int & Foo | {x: int} & Foo //│ a: Foo //│ = //│ b: Foo & {x: int} //│ = :ne ax = a.x bx = b.x //│ ax: anything //│ bx: int // * Yet, this does not immediately lead to unsoundness due to the aggressive normalization // * performed during constraint solving: :ne a = b //│ Foo & {x: int} //│ <: a: //│ Foo :e :ne b = a //│ Foo //│ <: b: //│ Foo & {x: int} //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.168: b = a //│ ║ ^^^^^ //│ ╟── expression of type `anything` is not an instance of type `int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.144: def b: int -> int & Foo | {x: int} & Foo //│ ╙── ^^^ // * To expose the unsoundness, we need some indirection with abstract types // * that prevent eagerly distributing the intersection type: class Test1[A, B]: { f: A & Foo } method M: B & Foo | {x: int} & Foo //│ Defined class Test1[+A, +B] //│ Declared Test1.M: Test1[?, 'B] -> (Foo & 'B | Foo & {x: int}) class Test2[B]: Test1[B | {x: int}, B] method M = this.f : B & Foo | {x: int} & Foo //│ Defined class Test2[+B] //│ Defined Test2.M: Test2['B] -> (Foo & 'B | Foo & {x: int}) oops = (Test2{f = Foo{x = "oops"}} : Test1[anything, int -> int]).M //│ oops: Foo & {x: int} //│ = Foo { x: 'oops' } // * Notice the type confusion: oops.x + 1 //│ res: int //│ = 'oops1' ================================================ FILE: shared/src/test/diff/mlscript/ByNameByValue.mls ================================================ :js def incr x = x.a <- x.a + 1 //│ // Prelude //│ let res; //│ // Query 1 //│ globalThis.incr = function incr(x) { //│ return void(x.a = x.a + 1); //│ }; //│ // End of generated code //│ incr: {mut a: int} -> unit //│ = [Function: incr] :p :js def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: def gensym: let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym: let n = {mut a: 0} in () => [incr(n,), n,] //│ AST: Def(false,Var(gensym),Left(Let(false,Var(n),Rcd(List((Var(a),Fld(m,IntLit(0))))),Lam(Tup(List()),Tup(List((None,Fld(_,App(Var(incr),Tup(List((None,Fld(_,Var(n)))))))), (None,Fld(_,Var(n)))))))),true) //│ // Query 1 //│ globalThis.gensym = function gensym() { //│ return (((n) => () => [ //│ incr(n), //│ n //│ ])({ a: 0 })); //│ }; //│ // End of generated code //│ gensym: () -> (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [Function: gensym] :js :p gensym1 = let n = { mut a = 0 } in fun () -> (incr n, n) //│ Parsed: let gensym1 = let n = {mut a: 0} in () => [incr(n,), n,]; //│ Desugared: def gensym1: let n = {mut a: 0} in () => [incr(n,), n,] //│ AST: Def(false,Var(gensym1),Left(Let(false,Var(n),Rcd(List((Var(a),Fld(m,IntLit(0))))),Lam(Tup(List()),Tup(List((None,Fld(_,App(Var(incr),Tup(List((None,Fld(_,Var(n)))))))), (None,Fld(_,Var(n)))))))),false) //│ // Query 1 //│ globalThis.gensym1 = ((n) => () => [ //│ incr(n), //│ n //│ ])({ a: 0 }); //│ // End of generated code //│ gensym1: () -> (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [Function (anonymous)] :js gensym () //│ // Query 1 //│ res = gensym()(); //│ // End of generated code //│ res: (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [ undefined, { a: 1 } ] :js gensym () //│ // Query 1 //│ res = gensym()(); //│ // End of generated code //│ res: (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [ undefined, { a: 1 } ] :js gensym1 () //│ // Query 1 //│ res = gensym1(); //│ // End of generated code //│ res: (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [ undefined, { a: 1 } ] gensym1 () //│ res: (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [ undefined, { a: 2 } ] :js gensym1 () //│ // Query 1 //│ res = gensym1(); //│ // End of generated code //│ res: (unit, forall 'a. {mut a: 'a},) //│ where //│ 'a :> 0 //│ = [ undefined, { a: 3 } ] :js def f x = x //│ // Query 1 //│ globalThis.f = function f(x) { //│ return x; //│ }; //│ // End of generated code //│ f: 'a -> 'a //│ = [Function: f] :js f 1 //│ // Query 1 //│ res = f(1); //│ // End of generated code //│ res: 1 //│ = 1 :js rec def xx = xx //│ // Query 1 //│ globalThis.xx = function xx() { //│ return xx(); //│ }; //│ // End of generated code //│ xx: nothing //│ = [Function: xx] class Nil: {} class Cons[A]: { head: A; tail: List[A] } type List[A] = Nil | Cons[A] //│ Defined class Nil //│ Defined class Cons[+A] //│ Defined type alias List[+A] def Nil = Nil {} def Cons head tail = Cons { head; tail } //│ Nil: Nil //│ = [Function: Nil1] //│ Cons: ('head & 'A) -> (List['A] & 'tail) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] :js rec def xs = Cons 0 (Cons 1 xs) //│ // Query 1 //│ globalThis.xs = function xs() { //│ return Cons1(0)(Cons1(1)(xs())); //│ }; //│ // End of generated code //│ xs: 'a //│ where //│ 'a :> Cons[0 | 1] with {head: 0, tail: Cons[0 | 1] with {head: 1, tail: forall 'a. 'a}} //│ = [Function: xs] :re xs //│ res: 'a //│ where //│ 'a :> Cons[0 | 1] with {head: 0, tail: Cons[0 | 1] with {head: 1, tail: forall 'a. 'a}} //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/mlscript/David.mls ================================================ def toBool = fun x -> true b = toBool true bb = (fun a -> fun b -> toBool (if b then not a else not a)) true //│ toBool: anything -> true //│ = [Function: toBool] //│ b: true //│ = true //│ bb: bool -> true //│ = [Function (anonymous)] def isInt = fun x -> b //│ isInt: anything -> true //│ = [Function: isInt] selectBoolOrInt = fun x -> if isInt x then 1 else true r1 = selectBoolOrInt true r2 = selectBoolOrInt 1 addOneI = fun x -> add x 1 addOneB = bb //│ selectBoolOrInt: anything -> (1 | true) //│ = [Function: selectBoolOrInt] //│ r1: 1 | true //│ = 1 //│ r2: 1 | true //│ = 1 //│ addOneI: int -> int //│ = [Function: addOneI] //│ addOneB: bool -> true //│ = [Function (anonymous)] addOne = fun x -> if (isInt x) then addOneI x else addOneB x r3 = isInt r2 //│ addOne: nothing -> (int | true) //│ = [Function: addOne] //│ r3: true //│ = true :e r4 = addOne (selectBoolOrInt 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.42: r4 = addOne (selectBoolOrInt 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `bool` //│ ║ l.15: selectBoolOrInt = fun x -> if isInt x then 1 else true //│ ║ ^ //│ ╟── but it flows into application with expected type `bool` //│ ║ l.42: r4 = addOne (selectBoolOrInt 1) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.3: bb = (fun a -> fun b -> toBool (if b then not a else not a)) true //│ ║ ^ //│ ╟── from reference: //│ ║ l.34: else addOneB x //│ ╙── ^ //│ r4: error | int | true //│ = 2 :e r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.62: r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.15: selectBoolOrInt = fun x -> if isInt x then 1 else true //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.62: r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp } //│ ║ ^^^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.62: r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp } //│ ╙── ^^^ //│ r4: error | int | true //│ = 2 // TODO improve: // Note the strange lack of specific provenance flows for `1` and `bool`. // This is due to our eagerly collapsing `int & bool` in the previously-inferred type. // We should either not collapse them until type simplification or somehow remember the original components // so a proper error can later be displayed. :e r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp | _ -> 0 } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.84: r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp | _ -> 0 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `1` is not an instance of type `bool` //│ ╟── Note: constraint arises from reference: //│ ║ l.3: bb = (fun a -> fun b -> toBool (if b then not a else not a)) true //│ ║ ^ //│ ╟── from refined scrutinee: //│ ║ l.84: r4 = let tmp = selectBoolOrInt 1 in case tmp of { int -> addOne tmp | _ -> 0 } //│ ╙── ^^^ //│ r4: int | true //│ = 2 def addOne x = case x of { | int -> addOneI x | bool -> addOneB x } //│ addOne: (bool | int) -> (int | true) //│ = [Function: addOne1] def r4 = addOne (selectBoolOrInt 1) //│ r4: int | true //│ = [Function: r43] def addOneS = fun x -> concat x "One" //│ addOneS: string -> string //│ = [Function: addOneS] addOneI 1 addOneS "Two" //│ res: int //│ = 2 //│ res: string //│ = 'TwoOne' // Attempt 1: def isInt = fun x -> case x of { int -> true | string -> false } //│ isInt: (int | string) -> Bool //│ = [Function: isInt1] isInt 1 isInt "Two" //│ res: Bool //│ = true //│ res: Bool //│ = false def addOne = fun x -> if isInt x then addOneI x else addOneS x //│ addOne: nothing -> (int | string) //│ = [Function: addOne2] :e addOne "hello" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.142: addOne "hello" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hello"` is not an instance of type `int` //│ ║ l.142: addOne "hello" //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.18: addOneI = fun x -> add x 1 //│ ║ ^ //│ ╟── from reference: //│ ║ l.137: def addOne = fun x -> if isInt x then addOneI x else addOneS x //│ ╙── ^ //│ res: error | int | string //│ = 'helloOne' // Attempt 2: use a match instead of isInt def addOne = fun x -> case x of { int -> addOneI x | string -> addOneS x } //│ addOne: (int | string) -> (int | string) //│ = [Function: addOne3] addOne 1 addOne "Two" //│ res: int | string //│ = 2 //│ res: int | string //│ = 'TwoOne' :e (addOne 1) + 1 concat "Three" (addOne "Two") //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.175: (addOne 1) + 1 //│ ║ ^^^^^^^^^^^^ //│ ╟── application of type `string` is not an instance of type `int` //│ ║ l.112: def addOneS = fun x -> concat x "One" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.175: (addOne 1) + 1 //│ ╙── ^^^^^^^^ //│ res: error | int //│ = 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.176: concat "Three" (addOne "Two") //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `int` is not an instance of type `string` //│ ║ l.18: addOneI = fun x -> add x 1 //│ ║ ^^^^^^^ //│ ╟── but it flows into application with expected type `string` //│ ║ l.176: concat "Three" (addOne "Two") //│ ╙── ^^^^^^^^^^^^ //│ res: error | string //│ = 'ThreeTwoOne' ================================================ FILE: shared/src/test/diff/mlscript/David2.mls ================================================ class Integer: { value: int; addOne: Integer -> Integer } //│ Defined class Integer class Numb: Integer & { addOne: Numb -> Numb } //│ Defined class Numb class Stri: { value: string; addOne: Stri -> Stri } //│ Defined class Stri addOne1 x = case x of { | Integer -> x.addOne x | Numb -> x.addOne x } //│ addOne1: ((Integer\value with {addOne: 'a -> 'b}) & 'a) -> 'b //│ = [Function: addOne1] rec def loopy() = Integer { value = 1; addOne = fun x -> loopy() } //│ loopy: () -> (Integer with {addOne: 'addOne, value: 1}) //│ where //│ 'addOne :> anything -> (Integer with {addOne: 'addOne, value: 1}) //│ = [Function: loopy] addOne1 (loopy()) //│ res: 'a //│ where //│ 'a :> Integer with {addOne: anything -> 'a, value: 1} //│ = Integer { value: 1, addOne: [Function: addOne] } res : Integer //│ res: Integer //│ = Integer { value: 1, addOne: [Function: addOne] } // * Note: In the type of addOne1, `Integer\value` means we don't use the value field here // * so it could be anything; example: funny = loopy() with { value = "oops!" } //│ funny: Integer with {addOne: 'addOne, value: "oops!"} //│ where //│ 'addOne :> anything -> (Integer with {addOne: 'addOne, value: 1}) //│ = Integer { value: 'oops!', addOne: [Function: addOne] } addOne1 funny //│ res: 'a //│ where //│ 'a :> Integer with {addOne: anything -> 'a, value: 1} //│ = Integer { value: 1, addOne: [Function: addOne] } :re addOne1 (Integer { value = 1; addOne = fun x -> (error : Integer) } with { value = "oops!" }) //│ res: Integer //│ Runtime error: //│ Error: an error was thrown // * `addOne` is now typed polymorphically, so this line now works! addOne1 ((Integer { value = 1; addOne = id }) with { value = "oops!" }) //│ res: Integer with {addOne: forall 'a. 'a -> 'a, value: "oops!"} //│ = Integer { value: 'oops!', addOne: [Function: id] } addOne1 (Integer { value = 1; addOne = id }) //│ res: Integer with {addOne: forall 'a. 'a -> 'a, value: 1} //│ = Integer { value: 1, addOne: [Function: id] } // * Now for properly closing the loop with a constructor for Integer: rec def mkInteger value = Integer { value; addOne = fun n -> mkInteger (n.value + 1) } //│ mkInteger: int -> (Integer with {addOne: 'addOne}) //│ where //│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ = [Function: mkInteger] n = mkInteger 42 //│ n: Integer with {addOne: 'addOne} //│ where //│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ = Integer { value: 42, addOne: [Function: addOne] } n : Integer //│ res: Integer //│ = Integer { value: 42, addOne: [Function: addOne] } m = addOne1 n //│ m: 'a //│ where //│ 'a :> Integer with {addOne: {value: int} -> 'a} //│ = Integer { value: 43, addOne: [Function: addOne] } m : Integer //│ res: Integer //│ = Integer { value: 43, addOne: [Function: addOne] } // * The type of mkInteger above is actually too precise for out taste. We can explicitly restrict it: def mkInteger2: int -> Integer def mkInteger2 = mkInteger //│ mkInteger2: int -> Integer //│ = //│ int -> (Integer with {addOne: 'addOne}) //│ where //│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ <: mkInteger2: //│ int -> Integer //│ = [Function: mkInteger2] addOne1 (mkInteger2 42) //│ res: Integer //│ = Integer { value: 43, addOne: [Function: addOne] } def mkInteger_oops: (int & 'a) -> (Integer & { value: 'a }) //│ mkInteger_oops: (int & 'a) -> (Integer\addOne with {value: 'a}) //│ = :e rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } //│ int -> (Integer with {addOne: 'addOne}) //│ where //│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ <: mkInteger_oops: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.121: rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` //│ ║ l.121: rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `'a` //│ ║ l.121: rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.116: def mkInteger_oops: (int & 'a) -> (Integer & { value: 'a }) //│ ╙── ^^ //│ = [Function: mkInteger_oops] :precise-rec-typing rec def mkInteger_oops value = Integer { value; addOne = fun n -> mkInteger_oops (n.value + 1) } //│ (int & 'value) -> (Integer with {addOne: forall 'a. {value: int} -> 'a, value: 'value}) //│ where //│ 'a :> Integer with {addOne: forall 'a. {value: int} -> 'a} //│ <: mkInteger_oops: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ = [Function: mkInteger_oops1] // * We may still want to retain the precise typing of the `value` part: def mkIntegerPrecise value = Integer { value; addOne = addOne1 } //│ mkIntegerPrecise: (int & 'value) -> (Integer with { //│ addOne: forall 'a 'b. ((Integer\value with {addOne: 'a -> 'b}) & 'a) -> 'b, //│ value: 'value //│ }) //│ = [Function: mkIntegerPrecise] def mkIntegerPrecise value = Integer { value; addOne = fun n -> mkInteger (n.value + 1) } //│ mkIntegerPrecise: (int & 'value) -> (Integer with {addOne: forall 'addOne. 'addOne, value: 'value}) //│ where //│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ = [Function: mkIntegerPrecise1] def mkIntegerPrecise value = Integer { value; addOne = fun (n: Integer) -> mkInteger2 (n.value + 1) } //│ mkIntegerPrecise: (int & 'value) -> (Integer with {value: 'value}) //│ = [Function: mkIntegerPrecise2] n = mkIntegerPrecise 42 //│ n: Integer & {value: 42} //│ = Integer { value: 42, addOne: [Function: addOne] } addOne1 n //│ res: Integer //│ = Integer { value: 43, addOne: [Function: addOne] } def mkIntegerPrecise2: (int & 'a) -> (Integer & { value: 'a }) def mkIntegerPrecise2 = mkIntegerPrecise //│ mkIntegerPrecise2: (int & 'a) -> (Integer\addOne with {value: 'a}) //│ = //│ (int & 'value) -> (Integer with {value: 'value}) //│ <: mkIntegerPrecise2: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ = [Function: mkIntegerPrecise21] n = mkIntegerPrecise2 42 //│ n: Integer & {value: 42} //│ = Integer { value: 42, addOne: [Function: addOne] } addOne1 n //│ res: Integer //│ = Integer { value: 43, addOne: [Function: addOne] } // * Note that due to recursive function approximation, // * the type of `mkIntegerPrecise` used not to be an instance of the original `mkInteger` type; // * though this now works since distributive first-class polymorphism: def mkIntegerPrecise3: (int & 'a) -> (Integer & { value: 'a }) //│ mkIntegerPrecise3: (int & 'a) -> (Integer\addOne with {value: 'a}) //│ = // * Note: works with :precise-rec-typing when typing mkInteger :e def mkIntegerPrecise3 = mkInteger //│ int -> (Integer with {addOne: 'addOne}) //│ where //│ 'addOne :> {value: int} -> (Integer with {addOne: 'addOne}) //│ <: mkIntegerPrecise3: //│ (int & 'a) -> (Integer\addOne with {value: 'a}) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.204: def mkIntegerPrecise3 = mkInteger //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` does not match type `'a` //│ ║ l.72: rec def mkInteger value = Integer { value; addOne = fun n -> mkInteger (n.value + 1) } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.198: def mkIntegerPrecise3: (int & 'a) -> (Integer & { value: 'a }) //│ ╙── ^^ //│ = [Function: mkIntegerPrecise3] :e addOne1 (Stri { value = ""; addOne = error }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.223: addOne1 (Stri { value = ""; addOne = error }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Stri & {addOne: ?addOne, value: ?value}` does not match type `Integer & ?a` //│ ║ l.223: addOne1 (Stri { value = ""; addOne = error }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.11: addOne1 x = case x of { //│ ╙── ^ //│ res: error //│ Runtime error: //│ Error: an error was thrown addOne2 x = case x of { | Integer -> x.addOne x | Numb -> x.addOne x | Stri -> x.addOne x } //│ addOne2: ((Integer\value with {addOne: 'a -> 'b}) & 'a | (Stri\value with {addOne: 'c -> 'b}) & 'c) -> 'b //│ = [Function: addOne2] addOne2 (mkIntegerPrecise 42) //│ res: Integer //│ = Integer { value: 43, addOne: [Function: addOne] } :re addOne2 (Stri { value = ""; addOne = error }) //│ res: nothing //│ Runtime error: //│ Error: an error was thrown def mkStr: string -> Stri rec def mkStr value = Stri { value; addOne = fun s -> mkStr (concat s.value "1") } //│ mkStr: string -> Stri //│ = //│ string -> (Stri with {addOne: 'addOne}) //│ where //│ 'addOne :> {value: string} -> (Stri with {addOne: 'addOne}) //│ <: mkStr: //│ string -> Stri //│ = [Function: mkStr] addOne2 (mkStr "hello") //│ res: Stri //│ = Stri { value: 'hello1', addOne: [Function: addOne] } union = if true then mkIntegerPrecise 42 else mkStr "hello" //│ union: Integer & {value: 42} | Stri //│ = Integer { value: 42, addOne: [Function: addOne] } union2 = addOne2 union //│ union2: Integer | Stri //│ = Integer { value: 43, addOne: [Function: addOne] } addOne2 union2 //│ res: Integer | Stri //│ = Integer { value: 44, addOne: [Function: addOne] } ================================================ FILE: shared/src/test/diff/mlscript/DavidB.mls ================================================ tuple1 = (1, 2, 3) //│ tuple1: (1, 2, 3,) //│ = [ 1, 2, 3 ] tuple2 = (4, 5) //│ tuple2: (4, 5,) //│ = [ 4, 5 ] if true then tuple1 else tuple2 //│ res: Array[1 | 2 | 3 | 4 | 5] & {0: 1 | 4, 1: 2 | 5} //│ = [ 1, 2, 3 ] arr = tuple1 : Array[int] //│ arr: Array[int] //│ = [ 1, 2, 3 ] if true then tuple1 else arr //│ res: Array[int] //│ = [ 1, 2, 3 ] type Cons[H, T] = { h: H; t: T } type List[A] = Cons[A, List[A]] | () //│ Defined type alias Cons[+H, +T] //│ Defined type alias List[+A] list1 = { h = 1; t = { h = 2; t = () }} //│ list1: {h: 1, t: {h: 2, t: ()}} //│ = { h: 1, t: { h: 2, t: [] } } list2 = { h = 3; t = () } //│ list2: {h: 3, t: ()} //│ = { h: 3, t: [] } ls = if true then list1 else list2 //│ ls: {h: 1 | 3, t: () | {h: 2, t: ()}} //│ = { h: 1, t: { h: 2, t: [] } } ls : List[int] //│ res: List[int] //│ = { h: 1, t: { h: 2, t: [] } } type NonEmptyList[A] = Cons[A, List[A]] //│ Defined type alias NonEmptyList[+A] ls : NonEmptyList[int] //│ res: NonEmptyList[int] //│ = { h: 1, t: { h: 2, t: [] } } :e // TODO improve error message ls : Cons[int, Cons[int, List[int]]] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.55: ls : Cons[int, Cons[int, List[int]]] //│ ║ ^^ //│ ╟── expression of type `anything` does not have field 'h' //│ ╟── Note: constraint arises from record type: //│ ║ l.26: type Cons[H, T] = { h: H; t: T } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── from applied type reference: //│ ║ l.55: ls : Cons[int, Cons[int, List[int]]] //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res: Cons[int, Cons[int, List[int]]] //│ = { h: 1, t: { h: 2, t: [] } } ls : { h: int; t: List[int] } //│ res: {h: int, t: List[int]} //│ = { h: 1, t: { h: 2, t: [] } } :e ls : { h: int; t: { h: int; t: List[int] } } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.74: ls : { h: int; t: { h: int; t: List[int] } } //│ ║ ^^ //│ ╟── expression of type `anything` does not have field 'h' //│ ╟── Note: constraint arises from record type: //│ ║ l.74: ls : { h: int; t: { h: int; t: List[int] } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: {h: int, t: {h: int, t: List[int]}} //│ = { h: 1, t: { h: 2, t: [] } } class C[A]: { h: A; t: List[A] } type List0[A] = C[A] | () //│ Defined class C[+A] //│ Defined type alias List0[+A] list1 = C { h = 1; t = C { h = 2; t = () }} //│ list1: C[1 | 2] with {h: 1, t: C[2] with {t: ()}} //│ = C { h: 1, t: C { h: 2, t: [] } } list2 = C { h = 3; t = () } //│ list2: C[3] with {t: ()} //│ = C { h: 3, t: [] } ls = if true then list1 else list2 //│ ls: C[1 | 2 | 3] with {h: 1 | 3, t: (C[2] with {t: ()}) | ()} //│ = C { h: 1, t: C { h: 2, t: [] } } ls : List0[int] //│ res: List0[int] //│ = C { h: 1, t: C { h: 2, t: [] } } type List1[A] = (A, List1[A]) | () //│ Defined type alias List1[+A] list1 = (1, (2, (3, ()))) //│ list1: (1, (2, (3, (),),),) //│ = [ 1, [ 2, [ 3, [] ] ] ] list1: List1[int] //│ res: List1[int] //│ = [ 1, [ 2, [ 3, [] ] ] ] list2 = (4, (5, ())) //│ list2: (4, (5, (),),) //│ = [ 4, [ 5, [] ] ] if true then list1 else list2 //│ res: (1 | 4, (2 | 5, Array[3 | ()],),) //│ = [ 1, [ 2, [ 3, [] ] ] ] class N //│ Defined class N class Con[H, T]: { h: H; t: T } //│ Defined class Con[+H, +T] type List2[A] = Cons[A, List2[A]] | N //│ Defined type alias List2[+A] rec def foo xs = case xs of Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t //│ foo: 'a -> 'T //│ where //│ 'T :> Con['h, 'T] //│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} //│ = [Function: foo] rec def foo xs = case xs of Con -> let tmp = foo xs.t.t in if xs.h then Con { h = xs.t.h; t = tmp } else tmp, N -> xs //│ foo: 'a -> 'b //│ where //│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} | N & 'b //│ 'b :> Con['h, 'b] //│ = [Function: foo1] rec def foo xs = case xs of Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t, N -> xs //│ foo: 'a -> 'b //│ where //│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h, t: 'a}} | N & 'b //│ 'b :> Con['h, 'b] //│ = [Function: foo2] foo (N{}) //│ res: 'a //│ where //│ 'a :> Con[nothing, 'a] | N //│ = N {} foo (Con{ h = true; t = Con{ h = 0; t = N{} } }) //│ res: 'a //│ where //│ 'a :> Con[0, 'a] | N //│ = Con { h: 0, t: N {} } def inf: Con[true, Con[0, 'I]] as 'I //│ inf: 'I //│ where //│ 'I :> Con[true, Con[0, 'I]] //│ = foo inf //│ res: 'a //│ where //│ 'a :> Con[0, 'a] //│ = //│ inf is not implemented rec def foo2 xs = case xs of Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t, N -> N{} //│ foo2: (Con[?, ?] & {h: bool, t: {h: 'h, t: 'a & 'b}} | N) -> (Con['h, forall 'c. 'c | 'd] | N | 'e) //│ where //│ 'c :> Con['h0, 'c | 'd] | 'd //│ 'b <: Con[?, ?] & {h: bool, t: {h: 'h1, t: 'b}} | N & 'e //│ 'e :> Con['h1, 'e] //│ 'a <: Con[?, ?] & {h: bool, t: {h: 'h0, t: 'a}} | N & 'd //│ = [Function: foo21] // === === === ERROR CASES === === === // :e foo (Con{ h = 1; t = N{} }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.213: foo (Con{ h = 1; t = N{} }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `N` does not have field 't' //│ ║ l.213: foo (Con{ h = 1; t = N{} }) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.161: Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t, //│ ║ ^^^^^^ //│ ╟── from field selection: //│ ║ l.161: Con -> if xs.h then Con { h = xs.t.h; t = foo xs.t.t } else foo xs.t.t, //│ ║ ^^^^ //│ ╟── Note: class N is defined at: //│ ║ l.131: class N //│ ╙── ^ //│ res: error //│ Runtime error: //│ Error: non-exhaustive case expression ================================================ FILE: shared/src/test/diff/mlscript/Didier.mls ================================================ def either x y = if 42 == 6 * 7 then x else y def fst((a,b)) = a def snd((a,b)) = b //│ either: 'a -> 'a -> 'a //│ = [Function: either] //│ fst: (('a, anything,),) -> 'a //│ = [Function: fst] //│ snd: ((anything, 'a,),) -> 'a //│ = [Function: snd] def K : 'A -> 'B -> 'B K a b = b type Sid = 'A -> 'A def id : Sid id x = x def auto : Sid -> Sid auto f = f f //│ Defined type alias Sid //│ K: anything -> 'B -> 'B //│ = //│ anything -> 'a -> 'a //│ <: K: //│ anything -> 'B -> 'B //│ = [Function: K] //│ id: Sid //│ = //│ 'a -> 'a //│ <: id: //│ Sid //│ = [Function: id1] //│ auto: Sid -> Sid //│ = //│ ('a -> 'b & 'a) -> 'b //│ <: auto: //│ Sid -> Sid //│ = [Function: auto] def trial = fun x -> (fun z -> K (auto (either x (fst z) : Sid)) (snd z)) ((id, id)) 1 //│ trial: Sid -> 1 //│ = [Function: trial] trial id //│ res: 1 //│ = 1 // * In a discussion regarding a declarative system using boxes let px = (fun x -> let pz = (fun z -> (let e = (either x (fst z) : forall 'A. 'A -> 'A) in // (** [1] forces x and z to have the same tag, // or to be unboxed with {} marks *) K (auto e) e, // (** [2] returning e requires to stay boxed, for // implicit use of polymorphism below *) snd z)) ((id, id)) in (fst pz // (** [3] returns the first component unchanged with x=z1-tag, // still in scope *) , snd pz 1 // (** [4] this requires pz to be tagged, with z2-tag, which is // no longer in scope and can be instantiated *) )) id in fst px 0 // (** the z1=x tag is no more in scope so it can be instantiated *) //│ res: 0 //│ = 0 ================================================ FILE: shared/src/test/diff/mlscript/Dmitry.mls ================================================ class Class1: { payload: int } //│ Defined class Class1 :e class Hidden[A] method Foo s = case s of { Class1 -> s.payload | _ -> 123 } method Main (a: A) = this.Foo a //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.8: method Main (a: A) = this.Foo a //│ ║ ^^^^^^^^^^ //│ ╟── expression of type `A & (Class1 | ~?a)` does not have field 'payload' //│ ╟── Note: constraint arises from field selection: //│ ║ l.7: method Foo s = case s of { Class1 -> s.payload | _ -> 123 } //│ ║ ^^^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.7: method Foo s = case s of { Class1 -> s.payload | _ -> 123 } //│ ╙── ^ //│ Defined class Hidden[-A] //│ Defined Hidden.Foo: Hidden[?] -> ((Class1 with {payload: 'payload}) | ~Class1) -> (123 | 'payload) //│ Defined Hidden.Main: Hidden['A] -> 'A -> (123 | error) class Hidden2[A] method Foo s = case s of { Class1 -> s.payload | _ -> 123 } method Main (a: A) = this.Foo (a with { payload = 1 }) //│ Defined class Hidden2[-A] //│ Defined Hidden2.Foo: Hidden2[?] -> ((Class1 with {payload: 'payload}) | ~Class1) -> (123 | 'payload) //│ Defined Hidden2.Main: Hidden2['A] -> 'A -> (1 | 123) def foo: ("A" | "B" | "D" | "E" | "F" | "G" | "H" | "I" | "J") -> int //│ foo: ("A" | "B" | "D" | "E" | "F" | "G" | "H" | "I" | "J") -> int //│ = arg = if true then "B" else if false then "C" else "D" //│ arg: "B" | "C" | "D" //│ = 'B' :e foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.40: foo arg //│ ║ ^^^^^^^ //│ ╟── string literal of type `"C"` does not match type `"A" | "B" | "D" | "E" | "F" | "G" | "H" | "I" | "J"` //│ ║ l.35: arg = if true then "B" else if false then "C" else "D" //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `"A" | "B" | "D" | "E" | "F" | "G" | "H" | "I" | "J"` //│ ║ l.40: foo arg //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.31: def foo: ("A" | "B" | "D" | "E" | "F" | "G" | "H" | "I" | "J") -> int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | int //│ = //│ foo is not implemented foo2 x = case x of { | "A" -> 1 | "B" -> 2 | "D" -> 3 | "E" -> 4 | "F" -> 5 | "G" -> 6 | "H" -> 7 | "I" -> 8 | "J" -> 9 } //│ foo2: ("A" | "B" | "D" | "E" | "F" | "G" | "H" | "I" | "J") -> (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) //│ = [Function: foo2] :e foo2 arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.72: foo2 arg //│ ║ ^^^^^^^^ //│ ╟── string literal of type `"C"` does not match type `"A" & ?a | "B" & ?b | "D" & ?c | "E" & ?d | "F" & ?e | "G" & ?f | "H" & ?g | "I" & ?h | "J" & ?i` //│ ║ l.35: arg = if true then "B" else if false then "C" else "D" //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `"A" & ?a | "B" & ?j | "D" & ?c | "E" & ?d | "F" & ?e | "G" & ?f | "H" & ?g | "I" & ?h | "J" & ?i` //│ ║ l.72: foo2 arg //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.57: foo2 x = case x of { //│ ╙── ^ //│ res: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | error //│ = 2 ================================================ FILE: shared/src/test/diff/mlscript/EitherClasses.mls ================================================ class Left[A]: { value: A } //│ Defined class Left[+A] Left{value=1} //│ res: Left[1] //│ = Left { value: 1 } def Left value = Left{ value } //│ Left: 'value -> Left['value] //│ = [Function: Left1] class Right[A]: { value: A } def Right value = Right{ value } //│ Defined class Right[+A] //│ Right: 'value -> Right['value] //│ = [Function: Right1] testVal = if true then Left 1 else Right 2 //│ testVal: Left[1] | Right[2] //│ = Left { value: 1 } testVal.value //│ res: 1 | 2 //│ = 1 res = case testVal of { Left -> testVal.value | Right -> 1 } //│ res: 1 //│ = 1 res: 1 //│ res: 1 //│ = 1 case res of { 1 -> "ok" } //│ res: "ok" //│ = 'ok' res = case testVal of { Left -> testVal | Right -> 1 } //│ res: 1 | Left[1] //│ = Left { value: 1 } ================================================ FILE: shared/src/test/diff/mlscript/ExplicitVariance.mls ================================================ // TODO have a way of specifying the variance explicitly, // because it would be too expensive to infer it when it's needed in method implementations // (we'd have to re-type-check methods at every iteration of the fixed point) // class Covar[out A] :e :w class Covar[A] method CovarMtd = let tmp = (error : Covar[{ x: int }]) : Covar[{}] in () //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.12: let tmp = (error : Covar[{ x: int }]) : Covar[{}] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `anything` does not have field 'x' //│ ║ l.12: let tmp = (error : Covar[{ x: int }]) : Covar[{}] //│ ║ ^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.12: let tmp = (error : Covar[{ x: int }]) : Covar[{}] //│ ╙── ^^^^^^^^^^ //│ Defined class Covar[±A] //│ Defined Covar.CovarMtd: Covar[?] -> () //│ ╔══[WARNING] Type definition Covar has bivariant type parameters: //│ ║ l.10: class Covar[A] //│ ║ ^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.10: class Covar[A] //│ ╙── ^ :re (error : Covar[{ x: int }]) : Covar[{}] //│ res: Covar[?] //│ Runtime error: //│ Error: an error was thrown ================================================ FILE: shared/src/test/diff/mlscript/ExprProb.mls ================================================ def intToString: int -> string def intToString x = toString x def done x = case x of {} //│ intToString: int -> string //│ = //│ anything -> string //│ <: intToString: //│ int -> string //│ = [Function: intToString] //│ done: nothing -> nothing //│ = [Function: done] class Lit: { val: int } class Add[E]: { lhs: E; rhs: E } def lit val = Lit { val } def add lhs rhs = Add { lhs; rhs } //│ Defined class Lit //│ Defined class Add[+E] //│ lit: (int & 'val) -> (Lit with {val: 'val}) //│ = [Function: lit] //│ add: ('lhs & 'E) -> ('E & 'rhs) -> (Add['E] with {lhs: 'lhs, rhs: 'rhs}) //│ = [Function: add] rec def eval1_stub k e = case e of { | Add -> eval1_stub k e.lhs | _ -> k e } //│ eval1_stub: ('a -> 'b) -> 'c -> 'b //│ where //│ 'c <: Add[?] & {lhs: 'c} | 'a & ~#Add //│ = [Function: eval1_stub] rec def eval1_stub k e = case e of { | Add -> eval1_stub k e.lhs + eval1_stub k e.rhs | _ -> k e } //│ eval1_stub: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | 'a & ~#Add //│ = [Function: eval1_stub1] :ns rec def eval1_stub e = case e of { | Lit -> 1 | Add -> eval1_stub e.lhs | _ -> 0 } //│ eval1_stub: forall 'eval1_stub 'a 'b 'c 'lhs 'd 'e. 'eval1_stub //│ where //│ 'eval1_stub := 'a -> (1 | 'd | 0) //│ 'd :> 1 | 'd | 0 //│ 'a <: #Lit & 'b | (#Add & 'c | 'e & ~#Add) & ~#Lit //│ 'c <: {lhs: 'lhs} //│ 'lhs <: 'a //│ = [Function: eval1_stub2] eval1_stub //│ res: 'a -> (0 | 1) //│ where //│ 'a <: Add[?] & {lhs: 'a} | Lit | ~Add[?] & ~Lit //│ = [Function: eval1_stub2] // def eval1: ('b -> int) -> Expr['b] -> int :stats rec def eval1 k e = case e of { | Lit -> e.val | Add -> eval1 k e.lhs + eval1 k e.rhs | _ -> k e } //│ eval1: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1] //│ constrain calls : 73 //│ annoying calls : 0 //│ subtyping calls : 291 :ns eval1 //│ res: forall 'eval1 'a 'b 'c 'val 'd 'lhs 'rhs 'e 'f 'g. 'eval1 //│ where //│ 'eval1 := 'a -> 'b -> ('val | 'e | 'g) //│ 'e := int //│ 'b <: #Lit & 'c | (#Add & 'd | 'f & ~#Add) & ~#Lit //│ 'd <: {rhs: 'rhs} & {lhs: 'lhs} //│ 'lhs <: 'b //│ 'rhs <: 'b //│ 'c <: {val: 'val} //│ 'val <: int //│ 'a <: 'f -> 'g //│ 'g <: int //│ = [Function: eval1] :re error: ~Add[?] //│ res: ~Add[nothing] //│ Runtime error: //│ Error: an error was thrown :re error: ('a & ~Lit) -> 'a //│ res: ('a & ~Lit) -> 'a //│ Runtime error: //│ Error: an error was thrown :re error: ('a) -> ('a & Add[?]) error: ('a) -> ('a & ~Add[?]) error: ('a & ~Add[?]) -> 'a //│ res: 'a -> (Add[?] & 'a) //│ Runtime error: //│ Error: an error was thrown //│ res: 'a -> ('a & ~Add[nothing]) //│ Runtime error: //│ Error: an error was thrown //│ res: ('a & ~Add[?]) -> 'a //│ Runtime error: //│ Error: an error was thrown :re error: ('a & ~add) -> 'a //│ res: ('a & ~#Add) -> 'a //│ Runtime error: //│ Error: an error was thrown :ns def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int //│ eval1_ty_ugly: forall 'a 'b. ('a -> int) -> 'b -> int //│ where //│ 'b := Lit | Add['b] | 'a & ~Lit & ~Add[?] //│ = eval1_ty_ugly //│ res: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = //│ eval1_ty_ugly is not implemented :ns def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int //│ eval1_ty: forall 'a 'b. ('a -> int) -> 'b -> int //│ where //│ 'b := Lit | Add['b] | 'a & ~#Lit & ~#Add //│ = eval1_ty //│ res: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = //│ eval1_ty is not implemented :stats def eval1_ty = eval1 //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1_ty] //│ constrain calls : 71 //│ annoying calls : 37 //│ subtyping calls : 576 :stats eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = //│ eval1_ty_ugly is not implemented //│ constrain calls : 238 //│ annoying calls : 565 //│ subtyping calls : 5737 // Workaround: :ns type E1[A] = Lit | Add[E1[A]] | A & ~lit & ~add def eval1_ty: ('a -> int) -> E1['a] -> int //│ Defined type alias E1[+A] //│ eval1_ty: forall 'a. ('a -> int) -> E1['a] -> int //│ = eval1_ty //│ res: ('a -> int) -> E1['a] -> int //│ = //│ eval1_ty is not implemented :stats def eval1_ty = eval1 //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty: //│ ('a -> int) -> E1['a] -> int //│ = [Function: eval1_ty1] //│ constrain calls : 67 //│ annoying calls : 37 //│ subtyping calls : 471 :stats rec def pretty1 k e = case e of { | Lit -> intToString e.val | Add -> concat (pretty1 k e.lhs) (pretty1 k e.rhs) | _ -> k e } //│ pretty1: ('a -> string) -> 'b -> string //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit //│ = [Function: pretty1] //│ constrain calls : 81 //│ annoying calls : 0 //│ subtyping calls : 326 :stats rec def prettier1 k ev e = case e of { | Lit -> intToString e.val | Add -> if ev e.lhs == 0 then prettier1 k ev e.rhs else if ev e.rhs == 0 then prettier1 k ev e.lhs else concat (prettier1 k ev e.lhs) (prettier1 k ev e.rhs) | _ -> k e } //│ prettier1: ('a -> string) -> ('lhs -> number) -> 'b -> string //│ where //│ 'b <: Add[?] & {lhs: 'lhs & 'b, rhs: 'lhs & 'b} | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier1] //│ constrain calls : 260 //│ annoying calls : 0 //│ subtyping calls : 832 :stats rec def prettier11 k ev e = case e of { | Lit -> intToString e.val | Add -> let tmp = pretty1 k e.lhs in if ev e.rhs == 0 then tmp else concat tmp (prettier11 k ev e.rhs) | _ -> k e } //│ prettier11: ('a -> string) -> ('rhs -> number) -> 'b -> string //│ where //│ 'b <: Add[?] & {lhs: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit, rhs: 'rhs & 'b} | Lit | 'a & ~#Add & ~#Lit //│ 'c <: {lhs: 'd, rhs: 'd} //│ 'd <: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier11] //│ constrain calls : 178 //│ annoying calls : 0 //│ subtyping calls : 737 // Doesn't make much sense, but generates very ugly type unless aggressively simplified: :stats rec def prettier12 k ev e = case e of { | Lit -> intToString e.val | Add -> let tmp = pretty1 k e.lhs in if ev e == 0 then tmp else concat tmp (pretty1 k e.rhs) | _ -> k e } //│ prettier12: ('a -> string & 'b -> 'c) -> ('d -> number) -> (Add[?] & {lhs: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit, rhs: 'f} & 'd | Lit | 'b & ~#Add & ~#Lit) -> (string | 'c) //│ where //│ 'f <: Add[?] & {lhs: 'f, rhs: 'f} | Lit | 'a & ~#Add & ~#Lit //│ 'e <: {lhs: 'g, rhs: 'g} //│ 'g <: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier12] //│ constrain calls : 158 //│ annoying calls : 0 //│ subtyping calls : 798 :stats e1 = Add{lhs = Lit {val = 1}; rhs = Add{lhs = Lit {val = 2}; rhs = Lit {val = 3}}} eval1 done e1 pretty1 done e1 prettier1 done (eval1 done) e1 prettier11 done (eval1 done) e1 prettier12 done (eval1 done) e1 //│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } //│ } //│ res: int //│ = 6 //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' //│ constrain calls : 1109 //│ annoying calls : 500 //│ subtyping calls : 9419 e1 = add (lit 1) (add (lit 2) (lit 3)) eval1 done e1 pretty1 done e1 prettier1 done (eval1 done) e1 prettier11 done (eval1 done) e1 prettier12 done (eval1 done) e1 //│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } //│ } //│ res: int //│ = 6 //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' class Nega[E]: { arg: E } def nega arg = Nega { arg } //│ Defined class Nega[+E] //│ nega: 'arg -> Nega['arg] //│ = [Function: nega] rec def eval2 k = eval1 (fun x -> case x of { | Nega -> 0 - (eval2 k x.arg) | _ -> k x }) //│ eval2: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'd} | 'a & ~#Nega //│ 'd <: Add[?] & {lhs: 'd, rhs: 'd} | Lit | 'c & ~#Add & ~#Lit //│ = [Function: eval2] :stats rec def prettier2 k ev = prettier1 (fun x -> case x of { | Nega -> concat "-" (prettier2 k ev x.arg) | _ -> k x }) ev //│ prettier2: ('a -> string) -> ('lhs -> number) -> 'b -> string //│ where //│ 'b <: Add[?] & {lhs: 'lhs & 'b, rhs: 'lhs & 'b} | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'd} | 'a & ~#Nega //│ 'd <: Add[?] & {lhs: 'lhs & 'd, rhs: 'lhs & 'd} | Lit | 'c & ~#Add & ~#Lit //│ = [Function: prettier2] //│ constrain calls : 116 //│ annoying calls : 0 //│ subtyping calls : 509 :stats rec def prettier22 k ev = prettier12 (fun x -> case x of { | Nega -> concat "-" (prettier22 k ev x.arg) | _ -> k x }) ev //│ prettier22: ('a -> string) -> ('b -> number) -> (Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit) -> string //│ where //│ 'b <: {lhs: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit, rhs: 'f} //│ 'f <: Add[?] & {lhs: 'f, rhs: 'f} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega //│ 'd <: {lhs: 'g, rhs: 'g} //│ 'g <: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit //│ 'e <: Nega[?] & {arg: 'arg} | 'a & ~#Nega //│ 'arg <: Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'arg} | 'a & ~#Nega //│ = [Function: prettier22] //│ constrain calls : 175 //│ annoying calls : 0 //│ subtyping calls : 877 :stats eval2 done e1 //│ res: int //│ = 6 //│ constrain calls : 140 //│ annoying calls : 60 //│ subtyping calls : 947 e2 = add (lit 1) (nega e1) //│ e2: Add[Lit & {val: 1} | Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ }]] with { //│ lhs: Lit & {val: 1}, //│ rhs: Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ }] //│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Nega { arg: Add { lhs: [Lit], rhs: [Add] } } //│ } :stats eval2 done e2 //│ res: int //│ = -5 //│ constrain calls : 217 //│ annoying calls : 103 //│ subtyping calls : 1458 d2 = nega (add (lit 1) (nega (lit 1))) //│ d2: Nega[Add[Lit & {val: 1} | Nega[Lit & {val: 1}]] with {lhs: Lit & {val: 1}, rhs: Nega[Lit & {val: 1}]}] //│ = Nega { arg: Add { lhs: Lit { val: 1 }, rhs: Nega { arg: [Lit] } } } :stats eval2 done d2 //│ res: int //│ = 0 //│ constrain calls : 147 //│ annoying calls : 71 //│ subtyping calls : 940 prettier2 done //│ res: ('lhs -> number) -> 'a -> string //│ where //│ 'a <: Add[?] & {lhs: 'lhs & 'a, rhs: 'lhs & 'a} | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'c} //│ 'c <: Add[?] & {lhs: 'lhs & 'c, rhs: 'lhs & 'c} | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] prettier22 done //│ res: ('a -> number) -> (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where //│ 'a <: {lhs: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit, rhs: 'e} //│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit | Nega[?] & {arg: 'arg} //│ 'c <: {lhs: 'f, rhs: 'f} //│ 'f <: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit //│ 'd <: Nega[?] & {arg: 'arg} //│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'arg} //│ = [Function (anonymous)] :stats prettier2 done (eval1 done) //│ res: 'a -> string //│ where //│ 'a <: Add[?] & {lhs: 'b & 'a, rhs: 'b & 'a} | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'd} //│ 'd <: Add[?] & {lhs: 'b & 'd, rhs: 'b & 'd} | Lit | 'c & ~#Add & ~#Lit //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit //│ = [Function (anonymous)] //│ constrain calls : 91 //│ annoying calls : 0 //│ subtyping calls : 523 prettier22 done (eval1 done) //│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where //│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e //│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit //│ 'd <: Add[?] & {lhs: 'd, rhs: 'd} | Lit | Nega[?] & {arg: 'arg} //│ 'c <: {lhs: 'f, rhs: 'f} //│ 'f <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'arg} //│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] // * TODO could probably merge `c` and `b` here! :stats prettier2 done (eval2 done) //│ res: 'a -> string //│ where //│ 'a <: Add[?] & {lhs: 'b & 'a, rhs: 'c & 'a} | Lit | 'd & ~#Add & ~#Lit //│ 'd <: Nega[?] & {arg: 'e} //│ 'e <: Add[?] & {lhs: 'b & 'e, rhs: 'c & 'e} | Lit | 'd & ~#Add & ~#Lit //│ 'c <: Add[?] & {lhs: 'c, rhs: 'c} | Lit | 'f & ~#Add & ~#Lit //│ 'f <: Nega[?] & {arg: 'c} //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'g & ~#Add & ~#Lit //│ 'g <: Nega[?] & {arg: 'b} //│ = [Function (anonymous)] //│ constrain calls : 100 //│ annoying calls : 0 //│ subtyping calls : 732 prettier2 done (eval2 done) e2 prettier2 done (eval2 done) d2 //│ res: string //│ = '1-123' //│ res: string //│ = '-1-1' :stats prettier22 done (eval2 done) prettier22 done (eval2 done) e2 prettier22 done (eval2 done) d2 //│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where //│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e //│ 'e <: Add[?] & {lhs: 'e, rhs: 'e} | Lit | 'f & ~#Add & ~#Lit //│ 'f <: Nega[?] & {arg: 'e} //│ 'd <: Add[?] & {lhs: 'd, rhs: 'd} | Lit | Nega[?] & {arg: 'arg} //│ 'c <: {lhs: 'g, rhs: 'g} //│ 'g <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'arg} //│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] //│ res: string //│ = '1-123' //│ res: string //│ = '-1' //│ constrain calls : 940 //│ annoying calls : 390 //│ subtyping calls : 7495 // === === === ERROR CASES === === === // :ShowRelativeLineNums :stats :e def eval1_ty_ugly = eval1 //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: def eval1_ty_ugly = eval1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `'a & (Add[?] & ~{val: int} & ~#Add | Lit & ~{val: int} | ~{val: int} & ~#Add & ~?a)` does not have field 'val' //│ ╟── Note: constraint arises from field selection: //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.70: rec def eval1 k e = case e of { //│ ╙── ^ //│ = [Function: eval1_ty_ugly] //│ constrain calls : 105 //│ annoying calls : 121 //│ subtyping calls : 3819 :stats :e eval1_ty_ugly = eval1_ty //│ ('a -> int) -> E1['a] -> int //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: eval1_ty_ugly = eval1_ty //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a & ~Add[?] & ~Lit` does not match type `Add[E1['a0]] | Lit | 'a0 & ~#Add & ~#Lit` //│ ║ l.132: def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.191: type E1[A] = Lit | Add[E1[A]] | A & ~lit & ~add //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from applied type reference: //│ ║ l.192: def eval1_ty: ('a -> int) -> E1['a] -> int //│ ╙── ^^^^^^ //│ = [Function: eval1] //│ constrain calls : 53 //│ annoying calls : 182 //│ subtyping calls : 1809 :e eval1 done e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: eval1 done e2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` //│ ║ l.343: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} //│ ║ ^ //│ ╟── from field selection: //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ╙── ^^^^^ //│ res: error | int //│ Runtime error: //│ Error: non-exhaustive case expression :e prettier2 done eval1 e1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.73: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.73: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' :e :stats prettier2 done (eval1 done) e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done (eval1 done) e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` //│ ║ l.343: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} //│ ║ ^ //│ ╟── from field selection: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ Runtime error: //│ Error: non-exhaustive case expression //│ constrain calls : 543 //│ annoying calls : 238 //│ subtyping calls : 6309 :e :stats prettier2 done eval2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.73: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ res: error | 'a -> string //│ where //│ 'a <: Add[?] & {lhs: nothing -> int & 'a, rhs: nothing -> int & 'a} | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'c} //│ 'c <: Add[?] & {lhs: nothing -> int & 'c, rhs: nothing -> int & 'c} | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] //│ constrain calls : 63 //│ annoying calls : 0 //│ subtyping calls : 469 :e :stats prettier2 done eval2 e1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.73: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.352: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' //│ constrain calls : 299 //│ annoying calls : 108 //│ subtyping calls : 3130 :e :stats prettier2 done eval2 e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.73: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.352: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' //│ constrain calls : 346 //│ annoying calls : 131 //│ subtyping calls : 3539 :e :stats prettier2 done eval2 d2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.73: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` is not a function //│ ║ l.343: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.352: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ = '-1-1' //│ constrain calls : 253 //│ annoying calls : 95 //│ subtyping calls : 2422 :e :stats prettier2 done eval1 e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b | ?c)` is not an instance of type `number` //│ ║ l.70: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.71: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.72: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.73: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.74: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.18: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.73: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.235: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' //│ constrain calls : 346 //│ annoying calls : 131 //│ subtyping calls : 3493 ================================================ FILE: shared/src/test/diff/mlscript/ExprProb2.mls ================================================ // --- Lit, Add --- class Lit: { val: int } class Add[E]: { lhs: E; rhs: E } def lit val = Lit { val } def add lhs rhs = Add { lhs; rhs } //│ Defined class Lit //│ Defined class Add[+E] //│ lit: (int & 'val) -> (Lit with {val: 'val}) //│ = [Function: lit] //│ add: ('lhs & 'E) -> ('E & 'rhs) -> (Add['E] with {lhs: 'lhs, rhs: 'rhs}) //│ = [Function: add] :stats def eval1 eval1 e = case e of { | Lit -> e.val | Add -> eval1 eval1 e.lhs + eval1 eval1 e.rhs } //│ eval1: ('a -> 'lhs -> int & 'a) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'val})) -> (int | 'val) //│ = [Function: eval1] //│ constrain calls : 36 //│ annoying calls : 0 //│ subtyping calls : 181 :stats :js def eval1f eval1 e = case e of { | Lit -> e.val | Add -> eval1 e.lhs + eval1 e.rhs } //│ // Query 1 //│ globalThis.eval1f = function eval1f(eval1) { //│ return ((e) => { //│ let a; //│ return (a = e, a instanceof Lit ? e.val : a instanceof Add ? eval1(e.lhs) + eval1(e.rhs) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ }); //│ }; //│ // End of generated code //│ eval1f: ('lhs -> int) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'val})) -> (int | 'val) //│ = [Function: eval1f] //│ constrain calls : 32 //│ annoying calls : 0 //│ subtyping calls : 161 e1 = add (lit 1) (add (lit 2) (lit 3)) //│ e1: Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } //│ } eval1 eval1! e1 //│ res: int //│ = 6 def eval1_fixed_1 = eval1 eval1! //│ eval1_fixed_1: (Add[?] & {lhs: 'a, rhs: 'a} | (Lit with {val: 'val})) -> (int | 'val) //│ where //│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit //│ = [Function: eval1_fixed_1] eval1_fixed_1 e1 //│ res: int //│ = 6 rec def eval1_fixed_2 = eval1f (fun x -> eval1f eval1_fixed_2 x) //│ eval1_fixed_2: (Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit\val & {val: 'val}) -> (int | 'val) //│ where //│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed_2] eval1_fixed_2 e1 //│ res: int //│ = 6 // Tying the knot without a recursive def: def eval1_fixed_3 = let fixed fixed = eval1f (fun x -> eval1f (fixed fixed) x) in fixed fixed! //│ eval1_fixed_3: (Add[?] & { //│ lhs: Add[?] & { //│ lhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit, //│ rhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ } | Lit, //│ rhs: Add[?] & { //│ lhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit, //│ rhs: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ } | Lit //│ } | Lit\val & {val: 'val}) -> (int | 'val) //│ where //│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed_3] eval1_fixed_3 e1 //│ res: int //│ = 6 // --- Nega --- class Nega[E]: { arg: E } def nega arg = Nega { arg } //│ Defined class Nega[+E] //│ nega: 'arg -> Nega['arg] //│ = [Function: nega] def eval2 eval2 e = case e of { | Nega -> 0 - (eval2 eval2 e.arg) | _ -> eval1 eval2 e } //│ eval2: ('a -> 'lhs -> int & 'a) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'val}) | Nega[?] & {arg: 'lhs}) -> (int | 'val) //│ = [Function: eval2] def eval2f eval2 e = case e of { | Nega -> 0 - (eval2 e.arg) | _ -> eval1f eval2 e } //│ eval2f: ('arg -> int) -> (Add[?] & {lhs: 'arg, rhs: 'arg} | (Lit with {val: 'val}) | Nega[?] & {arg: 'arg}) -> (int | 'val) //│ = [Function: eval2f] e2 = add (lit 1) (nega e1) //│ e2: Add[Lit & {val: 1} | Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ }]] with { //│ lhs: Lit & {val: 1}, //│ rhs: Nega[Add[Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} | Lit & {val: 1}] with { //│ lhs: Lit & {val: 1}, //│ rhs: Add[Lit & {val: 2 | 3}] & {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}} //│ }] //│ } //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Nega { arg: Add { lhs: [Lit], rhs: [Add] } } //│ } def eval2_fixed_1 = eval2 eval2! //│ eval2_fixed_1: (Add[?] & {lhs: 'a, rhs: 'a} | (Lit with {val: 'val}) | Nega[?] & {arg: 'a}) -> (int | 'val) //│ where //│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit | Nega[?] & {arg: 'a} //│ = [Function: eval2_fixed_1] eval2_fixed_1 e1 eval2_fixed_1 e2 //│ res: int //│ = 6 //│ res: int //│ = -5 def fix f = let fixed = fun x -> f (fun v -> (x x) v) in fixed fixed! //│ fix: (('a -> 'b) -> ('a -> 'b & 'c)) -> 'c //│ = [Function: fix] def eval2_fixed_2 = fix eval2f //│ eval2_fixed_2: (Add[?] & {lhs: 'a, rhs: 'a} | (Lit with {val: 'val}) | Nega[?] & {arg: 'a}) -> (int | 'val) //│ where //│ 'a <: Add[?] & {lhs: 'a, rhs: 'a} | Lit | Nega[?] & {arg: 'a} //│ = [Function: eval2_fixed_2] :stats eval2_fixed_2 e1 //│ res: int //│ = 6 //│ constrain calls : 267 //│ annoying calls : 140 //│ subtyping calls : 1865 :stats eval2_fixed_2 e2 //│ res: int //│ = -5 //│ constrain calls : 648 //│ annoying calls : 345 //│ subtyping calls : 5049 // === === === ERROR CASES === === === // :ge let rec eval1_fixed = eval1f (eval1f eval1_fixed) in eval1_fixed e1 //│ res: int //│ Code generation encountered an error: //│ recursive non-function definition eval1_fixed is not supported :js rec def eval1_fixed = eval1f (eval1f eval1_fixed) //│ // Query 1 //│ globalThis["eval1_fixed"] = function eval1_fixed() { //│ return eval1f(eval1f(eval1_fixed())); //│ }; //│ // End of generated code //│ eval1_fixed: (Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit\val & {val: 'val}) -> (int | 'val) //│ where //│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed] :re eval1_fixed e1 //│ res: int //│ Runtime error: //│ RangeError: Maximum call stack size exceeded rec def eval1_fixed() = eval1f (eval1f (eval1_fixed())) //│ eval1_fixed: () -> (Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit\val & {val: 'val}) -> (int | 'val) //│ where //│ 'lhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'lhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ 'rhs <: Add[?] & {lhs: 'lhs0, rhs: 'rhs0} | Lit //│ 'rhs0 <: Add[?] & {lhs: 'lhs, rhs: 'rhs} | Lit //│ = [Function: eval1_fixed1] :re eval1_fixed() e1 //│ res: int //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :ShowRelativeLineNums :AllowTypeErrors def eval1_fixed = eval1f (fun x -> eval1f eval1f x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: def eval1_fixed = eval1f (fun x -> eval1f eval1f x) //│ ║ ^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?val | ?b)` is not an instance of type `int` //│ ║ l.30: def eval1f eval1 e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.31: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.33: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ╙── ^^^^^^^^^^^ //│ eval1_fixed: (Add[?] & { //│ lhs: Add[?] & {lhs: nothing -> int, rhs: nothing -> int} | Lit, //│ rhs: Add[?] & {lhs: nothing -> int, rhs: nothing -> int} | Lit //│ } | (Lit with {val: 'val})) -> (int | 'val) rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.+1: rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?val | ?b)` does not match type `Add[?] & ?c | Lit & ?d` //│ ║ l.30: def eval1f eval1 e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.31: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.33: } //│ ║ ^^^ //│ ╟── but it flows into application with expected type `Add[?] & ?e | Lit & ?f` //│ ║ l.+1: rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.30: def eval1f eval1 e = case e of { //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.+1: rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` is not a function //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.+1: rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from application: //│ ║ l.+1: rec def eval1_fixed = eval1f (fun x -> eval1_fixed eval1_fixed x) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ eval1_fixed: (Add[?] | (Lit with {val: 'val})) -> (int | 'val) :e rec def eval1_fixed = eval1f eval1_fixed e1 //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.+1: rec def eval1_fixed = eval1f eval1_fixed e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` is not a function //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `?rhs -> ?a` //│ ║ l.+1: rec def eval1_fixed = eval1f eval1_fixed e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.+1: rec def eval1_fixed = eval1f eval1_fixed e1 //│ ╙── ^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.+1: rec def eval1_fixed = eval1f eval1_fixed e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` is not a function //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `?lhs -> ?a` //│ ║ l.+1: rec def eval1_fixed = eval1f eval1_fixed e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.32: | Add -> eval1 e.lhs + eval1 e.rhs //│ ║ ^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.+1: rec def eval1_fixed = eval1f eval1_fixed e1 //│ ╙── ^^^^^^^^^^^ //│ eval1_fixed: int def eval2_broken eval2 e = case e of { | Nega -> e.arg | _ -> eval1 eval2 e } //│ eval2_broken: ('a -> 'lhs -> int & 'a) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'arg}) | Nega[?] & {arg: 'arg}) -> (int | 'arg) :e eval2_broken eval2_broken! e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: eval2_broken eval2_broken! e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Add[?E] & {Add#E = ?E, lhs: ?lhs, rhs: ?rhs}` is not an instance of type `int` //│ ║ l.8: def add lhs rhs = Add { lhs; rhs } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.20: | Add -> eval1 eval1 e.lhs + eval1 eval1 e.rhs //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from field selection: //│ ║ l.345: | Nega -> e.arg //│ ╙── ^^^^^ //│ res: error | int def eval2f_oops eval2 e = case e of { | Nega -> 0 - (eval2 e.arg) | _ -> eval1 eval2 e // should be: eval1f eval2 e } //│ eval2f_oops: ('arg -> int & 'a -> 'lhs -> int & 'a) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | (Lit with {val: 'val}) | Nega[?] & {arg: 'arg}) -> (int | 'val) // :e fix eval2f_oops e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: fix eval2f_oops e2 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` does not match type `Add[?] & ?c | Lit & ?d` //│ ║ l.173: def fix f = let fixed = fun x -> f (fun v -> (x x) v) in fixed fixed! //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.18: def eval1 eval1 e = case e of { //│ ║ ^ //│ ╟── from reference: //│ ║ l.367: def eval2f_oops eval2 e = case e of { //│ ╙── ^ //│ res: error ================================================ FILE: shared/src/test/diff/mlscript/ExprProb_Inv.mls ================================================ def intToString: int -> string def intToString x = toString x def done x = case x of {} //│ intToString: int -> string //│ = //│ anything -> string //│ <: intToString: //│ int -> string //│ = [Function: intToString] //│ done: nothing -> nothing //│ = [Function: done] class Lit: { val: int } class Add[E]: { lhs: E; rhs: E } method Inv (x: E) = x def lit val = Lit { val } def add lhs rhs = Add { lhs; rhs } //│ Defined class Lit //│ Defined class Add[=E] //│ Defined Add.Inv: Add['E] -> 'E -> 'E //│ lit: (int & 'val) -> (Lit with {val: 'val}) //│ = [Function: lit] //│ add: ('lhs & 'E) -> ('E & 'rhs) -> (Add['E] with {lhs: 'lhs, rhs: 'rhs}) //│ = [Function: add] rec def eval1_stub k e = case e of { | Add -> eval1_stub k e.lhs | _ -> k e } //│ eval1_stub: ('a -> 'b) -> 'c -> 'b //│ where //│ 'c <: (Add[?]\rhs with {lhs: 'c}) | 'a & ~#Add //│ = [Function: eval1_stub] rec def eval1_stub k e = case e of { | Add -> eval1_stub k e.lhs + eval1_stub k e.rhs | _ -> k e } //│ eval1_stub: ('a -> int) -> 'b -> int //│ where //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | 'a & ~#Add //│ = [Function: eval1_stub1] :ns rec def eval1_stub e = case e of { | Lit -> 1 | Add -> eval1_stub e.lhs | _ -> 0 } //│ eval1_stub: forall 'eval1_stub 'a 'b 'c 'lhs 'd 'e. 'eval1_stub //│ where //│ 'eval1_stub := 'a -> (1 | 'd | 0) //│ 'd :> 1 | 'd | 0 //│ 'a <: #Lit & 'b | (#Add & 'c | 'e & ~#Add) & ~#Lit //│ 'c <: {lhs: 'lhs} //│ 'lhs <: 'a //│ = [Function: eval1_stub2] eval1_stub //│ res: 'a -> (0 | 1) //│ where //│ 'a <: (Add[?]\rhs with {lhs: 'a}) | Lit | ~Add[?] & ~Lit //│ = [Function: eval1_stub2] // def eval1: ('b -> int) -> Expr['b] -> int :stats rec def eval1 k e = case e of { | Lit -> e.val | Add -> eval1 k e.lhs + eval1 k e.rhs | _ -> k e } //│ eval1: ('a -> int) -> 'b -> int //│ where //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1] //│ constrain calls : 73 //│ annoying calls : 0 //│ subtyping calls : 307 :ns eval1 //│ res: forall 'eval1 'a 'b 'c 'val 'd 'lhs 'rhs 'e 'f 'g. 'eval1 //│ where //│ 'eval1 := 'a -> 'b -> ('val | 'e | 'g) //│ 'e := int //│ 'b <: #Lit & 'c | (#Add & 'd | 'f & ~#Add) & ~#Lit //│ 'd <: {rhs: 'rhs} & {lhs: 'lhs} //│ 'lhs <: 'b //│ 'rhs <: 'b //│ 'c <: {val: 'val} //│ 'val <: int //│ 'a <: 'f -> 'g //│ 'g <: int //│ = [Function: eval1] :re error: ~Add[?] //│ res: ~Add[?] //│ Runtime error: //│ Error: an error was thrown :re error: ('a & ~Lit) -> 'a //│ res: ('a & ~Lit) -> 'a //│ Runtime error: //│ Error: an error was thrown :re error: ('a) -> ('a & Add[?]) error: ('a) -> ('a & ~Add[?]) error: ('a & ~Add[?]) -> 'a //│ res: 'a -> (Add[?] & 'a) //│ Runtime error: //│ Error: an error was thrown //│ res: 'a -> ('a & ~Add[?]) //│ Runtime error: //│ Error: an error was thrown //│ res: ('a & ~Add[?]) -> 'a //│ Runtime error: //│ Error: an error was thrown :re error: ('a & ~add) -> 'a //│ res: ('a & ~#Add) -> 'a //│ Runtime error: //│ Error: an error was thrown :ns def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int //│ eval1_ty_ugly: forall 'a 'b. ('a -> int) -> 'b -> int //│ where //│ 'b := Lit | Add['b] | 'a & ~Lit & ~Add[?] //│ = eval1_ty_ugly //│ res: ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ = //│ eval1_ty_ugly is not implemented :ns def eval1_ty: ('a -> int) -> (Lit | Add['b] | 'a & ~lit & ~add as 'b) -> int //│ eval1_ty: forall 'a 'b. ('a -> int) -> 'b -> int //│ where //│ 'b := Lit | Add['b] | 'a & ~#Lit & ~#Add //│ = eval1_ty //│ res: ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = //│ eval1_ty is not implemented :stats def eval1_ty = eval1 //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = [Function: eval1_ty] //│ constrain calls : 71 //│ annoying calls : 37 //│ subtyping calls : 588 :stats eval1_ty = eval1_ty_ugly //│ ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ <: eval1_ty: //│ ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~#Add & ~#Lit //│ = //│ eval1_ty_ugly is not implemented //│ constrain calls : 4870 //│ annoying calls : 1519 //│ subtyping calls : 668110 // Workaround: :ns type E1[A] = Lit | Add[E1[A]] | A & ~lit & ~add def eval1_ty: ('a -> int) -> E1['a] -> int //│ Defined type alias E1[=A] //│ eval1_ty: forall 'a. ('a -> int) -> E1['a] -> int //│ = eval1_ty //│ res: ('a -> int) -> E1['a] -> int //│ = //│ eval1_ty is not implemented :stats def eval1_ty = eval1 //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty: //│ ('a -> int) -> E1['a] -> int //│ = [Function: eval1_ty1] //│ constrain calls : 67 //│ annoying calls : 37 //│ subtyping calls : 487 :stats rec def pretty1 k e = case e of { | Lit -> intToString e.val | Add -> concat (pretty1 k e.lhs) (pretty1 k e.rhs) | _ -> k e } //│ pretty1: ('a -> string) -> 'b -> string //│ where //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit //│ = [Function: pretty1] //│ constrain calls : 81 //│ annoying calls : 0 //│ subtyping calls : 342 :stats rec def prettier1 k ev e = case e of { | Lit -> intToString e.val | Add -> if ev e.lhs == 0 then prettier1 k ev e.rhs else if ev e.rhs == 0 then prettier1 k ev e.lhs else concat (prettier1 k ev e.lhs) (prettier1 k ev e.rhs) | _ -> k e } //│ prettier1: ('a -> string) -> ('lhs -> number) -> 'b -> string //│ where //│ 'b <: (Add[?] with {lhs: 'lhs & 'b, rhs: 'lhs & 'b}) | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier1] //│ constrain calls : 260 //│ annoying calls : 0 //│ subtyping calls : 854 :stats rec def prettier11 k ev e = case e of { | Lit -> intToString e.val | Add -> let tmp = pretty1 k e.lhs in if ev e.rhs == 0 then tmp else concat tmp (prettier11 k ev e.rhs) | _ -> k e } //│ prettier11: ('a -> string) -> ('rhs -> number) -> 'b -> string //│ where //│ 'b <: (Add[?] with {lhs: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit, rhs: 'rhs & 'b}) | Lit | 'a & ~#Add & ~#Lit //│ 'c <: {lhs: 'd, rhs: 'd} //│ 'd <: Add[?] & 'c | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier11] //│ constrain calls : 178 //│ annoying calls : 0 //│ subtyping calls : 758 // Doesn't make much sense, but generates very ugly type unless aggressively simplified: :stats rec def prettier12 k ev e = case e of { | Lit -> intToString e.val | Add -> let tmp = pretty1 k e.lhs in if ev e == 0 then tmp else concat tmp (pretty1 k e.rhs) | _ -> k e } //│ prettier12: ('a -> string & 'b -> 'c) -> ('d -> number) -> ((Add[?] with {lhs: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit, rhs: 'f}) & 'd | Lit | 'b & ~#Add & ~#Lit) -> (string | 'c) //│ where //│ 'f <: (Add[?] with {lhs: 'f, rhs: 'f}) | Lit | 'a & ~#Add & ~#Lit //│ 'e <: {lhs: 'g, rhs: 'g} //│ 'g <: Add[?] & 'e | Lit | 'a & ~#Add & ~#Lit //│ = [Function: prettier12] //│ constrain calls : 158 //│ annoying calls : 0 //│ subtyping calls : 834 :stats e1 = Add{lhs = Lit {val = 1}; rhs = Add{lhs = Lit {val = 2}; rhs = Lit {val = 3}}} eval1 done e1 pretty1 done e1 prettier1 done (eval1 done) e1 prettier11 done (eval1 done) e1 prettier12 done (eval1 done) e1 //│ e1: Add['E] with {lhs: Lit & {val: 1}, rhs: Add['E0] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}} //│ where //│ 'E :> (Add['E0] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}) | Lit & {val: 1} //│ 'E0 :> Lit & {val: 2 | 3} //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } //│ } //│ res: int //│ = 6 //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' //│ constrain calls : 1109 //│ annoying calls : 500 //│ subtyping calls : 9272 e1 = add (lit 1) (add (lit 2) (lit 3)) eval1 done e1 pretty1 done e1 prettier1 done (eval1 done) e1 prettier11 done (eval1 done) e1 prettier12 done (eval1 done) e1 //│ e1: Add['E] with {lhs: Lit & {val: 1}, rhs: Add['E0] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}} //│ where //│ 'E :> (Add['E0] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}) | Lit & {val: 1} //│ 'E0 :> Lit & {val: 2 | 3} //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Add { lhs: Lit { val: 2 }, rhs: Lit { val: 3 } } //│ } //│ res: int //│ = 6 //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' //│ res: string //│ = '123' class Nega[E]: { arg: E } def nega arg = Nega { arg } //│ Defined class Nega[+E] //│ nega: 'arg -> Nega['arg] //│ = [Function: nega] rec def eval2 k = eval1 (fun x -> case x of { | Nega -> 0 - (eval2 k x.arg) | _ -> k x }) //│ eval2: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add[?]\lhs\rhs & {lhs: 'b, rhs: 'b} | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'd} | 'a & ~#Nega //│ 'd <: Add[?]\lhs\rhs & {lhs: 'd, rhs: 'd} | Lit | 'c & ~#Add & ~#Lit //│ = [Function: eval2] :stats rec def prettier2 k ev = prettier1 (fun x -> case x of { | Nega -> concat "-" (prettier2 k ev x.arg) | _ -> k x }) ev //│ prettier2: ('a -> string) -> ('lhs -> number) -> 'b -> string //│ where //│ 'b <: Add[?]\lhs\rhs & {lhs: 'lhs & 'b, rhs: 'lhs & 'b} | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'd} | 'a & ~#Nega //│ 'd <: Add[?]\lhs\rhs & {lhs: 'lhs & 'd, rhs: 'lhs & 'd} | Lit | 'c & ~#Add & ~#Lit //│ = [Function: prettier2] //│ constrain calls : 116 //│ annoying calls : 0 //│ subtyping calls : 549 :stats rec def prettier22 k ev = prettier12 (fun x -> case x of { | Nega -> concat "-" (prettier22 k ev x.arg) | _ -> k x }) ev //│ prettier22: ('a -> string) -> ('b -> number) -> (Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit) -> string //│ where //│ 'b <: {lhs: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit, rhs: 'f} //│ 'f <: Add[?]\lhs\rhs & {lhs: 'f, rhs: 'f} | Lit | Nega[?] & {arg: 'arg} | 'a & ~#Add & ~#Lit & ~#Nega //│ 'd <: {lhs: 'g, rhs: 'g} //│ 'g <: Add[?] & 'd | Lit | 'e & ~#Add & ~#Lit //│ 'e <: Nega[?] & {arg: 'arg} | 'a & ~#Nega //│ 'arg <: Add[?] & 'b | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'arg} | 'a & ~#Nega //│ = [Function: prettier22] //│ constrain calls : 175 //│ annoying calls : 0 //│ subtyping calls : 897 :stats eval2 done e1 //│ res: int //│ = 6 //│ constrain calls : 140 //│ annoying calls : 60 //│ subtyping calls : 947 e2 = add (lit 1) (nega e1) //│ e2: Add['E] with { //│ lhs: Lit & {val: 1}, //│ rhs: Nega[forall 'E0 'E1. Add['E0] with {lhs: Lit & {val: 1}, rhs: Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}] //│ } //│ where //│ 'E :> Lit & {val: 1} | Nega[forall 'E0 'E1. Add['E0] with {lhs: Lit & {val: 1}, rhs: Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}}] //│ 'E0 :> (Add['E1] with {lhs: Lit & {val: 2}, rhs: Lit & {val: 3}}) | Lit & {val: 1} //│ 'E1 :> Lit & {val: 2 | 3} //│ = Add { //│ lhs: Lit { val: 1 }, //│ rhs: Nega { arg: Add { lhs: [Lit], rhs: [Add] } } //│ } :stats eval2 done e2 //│ res: int //│ = -5 //│ constrain calls : 217 //│ annoying calls : 103 //│ subtyping calls : 1458 d2 = nega (add (lit 1) (nega (lit 1))) //│ d2: Nega[Add['E] with {lhs: Lit & {val: 1}, rhs: Nega[Lit & {val: 1}]}] //│ where //│ 'E :> Lit & {val: 1} | Nega[Lit & {val: 1}] //│ = Nega { arg: Add { lhs: Lit { val: 1 }, rhs: Nega { arg: [Lit] } } } :stats eval2 done d2 //│ res: int //│ = 0 //│ constrain calls : 147 //│ annoying calls : 71 //│ subtyping calls : 940 prettier2 done //│ res: ('lhs -> number) -> 'a -> string //│ where //│ 'a <: Add[?]\lhs\rhs & {lhs: 'lhs & 'a, rhs: 'lhs & 'a} | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'c} //│ 'c <: Add[?]\lhs\rhs & {lhs: 'lhs & 'c, rhs: 'lhs & 'c} | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] prettier22 done //│ res: ('a -> number) -> (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where //│ 'a <: {lhs: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit, rhs: 'e} //│ 'e <: Add[?]\lhs\rhs & {lhs: 'e, rhs: 'e} | Lit | Nega[?] & {arg: 'arg} //│ 'c <: {lhs: 'f, rhs: 'f} //│ 'f <: Add[?] & 'c | Lit | 'd & ~#Add & ~#Lit //│ 'd <: Nega[?] & {arg: 'arg} //│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'arg} //│ = [Function (anonymous)] :stats prettier2 done (eval1 done) //│ res: 'a -> string //│ where //│ 'a <: Add[?]\lhs\rhs & {lhs: 'b & 'a, rhs: 'b & 'a} | Lit | 'c & ~#Add & ~#Lit //│ 'c <: Nega[?] & {arg: 'd} //│ 'd <: Add[?]\lhs\rhs & {lhs: 'b & 'd, rhs: 'b & 'd} | Lit | 'c & ~#Add & ~#Lit //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit //│ = [Function (anonymous)] //│ constrain calls : 91 //│ annoying calls : 0 //│ subtyping calls : 616 prettier22 done (eval1 done) //│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where //│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e //│ 'e <: (Add[?] with {lhs: 'e, rhs: 'e}) | Lit //│ 'd <: (Add[?] with {lhs: 'd, rhs: 'd}) | Lit | Nega[?] & {arg: 'arg} //│ 'c <: {lhs: 'f, rhs: 'f} //│ 'f <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'arg} //│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] // TODO could probably merge `a` and `b` here! :stats prettier2 done (eval2 done) //│ res: 'a -> string //│ where //│ 'a <: Add[?]\lhs\rhs & {lhs: 'b & 'a, rhs: 'c & 'a} | Lit | 'd & ~#Add & ~#Lit //│ 'd <: Nega[?] & {arg: 'e} //│ 'e <: Add[?]\lhs\rhs & {lhs: 'b & 'e, rhs: 'c & 'e} | Lit | 'd & ~#Add & ~#Lit //│ 'c <: (Add[?] with {lhs: 'c, rhs: 'c}) | Lit | 'f & ~#Add & ~#Lit //│ 'f <: Nega[?] & {arg: 'c} //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'g & ~#Add & ~#Lit //│ 'g <: Nega[?] & {arg: 'b} //│ = [Function (anonymous)] //│ constrain calls : 100 //│ annoying calls : 0 //│ subtyping calls : 864 prettier2 done (eval2 done) e2 prettier2 done (eval2 done) d2 //│ res: string //│ = '1-123' //│ res: string //│ = '-1-1' :stats prettier22 done (eval2 done) prettier22 done (eval2 done) e2 prettier22 done (eval2 done) d2 //│ res: (Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit) -> string //│ where //│ 'a <: {lhs: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit, rhs: 'd} & 'e //│ 'e <: (Add[?] with {lhs: 'e, rhs: 'e}) | Lit | 'f & ~#Add & ~#Lit //│ 'f <: Nega[?] & {arg: 'e} //│ 'd <: (Add[?] with {lhs: 'd, rhs: 'd}) | Lit | Nega[?] & {arg: 'arg} //│ 'c <: {lhs: 'g, rhs: 'g} //│ 'g <: Add[?] & 'c | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'arg} //│ 'arg <: Add[?] & 'a | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] //│ res: string //│ = '1-123' //│ res: string //│ = '-1' //│ constrain calls : 940 //│ annoying calls : 390 //│ subtyping calls : 7554 // === === === ERROR CASES === === === // :ShowRelativeLineNums :stats :e def eval1_ty_ugly = eval1 //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: (Add[?] with {lhs: 'b, rhs: 'b}) | Lit | 'a & ~#Add & ~#Lit //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: def eval1_ty_ugly = eval1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `'a & (Add[?] & ~{val: int} & ~#Add | Lit & ~{val: int} | ~{val: int} & ~#Add & ~?a)` does not have field 'val' //│ ╟── Note: constraint arises from field selection: //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.72: rec def eval1 k e = case e of { //│ ╙── ^ //│ = [Function: eval1_ty_ugly] //│ constrain calls : 105 //│ annoying calls : 121 //│ subtyping calls : 3763 :stats :e eval1_ty_ugly = eval1_ty //│ ('a -> int) -> E1['a] -> int //│ <: eval1_ty_ugly: //│ ('a -> int) -> 'b -> int //│ where //│ 'b := Add['b] | Lit | 'a & ~Add[?] & ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: eval1_ty_ugly = eval1_ty //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a & ~Add[?] & ~Lit` does not match type `Add[E1['a0]] | Lit | 'a0 & ~#Add & ~#Lit` //│ ║ l.134: def eval1_ty_ugly: ('a -> int) -> (Lit | Add['b] | 'a & ~Lit & ~Add[?] as 'b) -> int //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.193: type E1[A] = Lit | Add[E1[A]] | A & ~lit & ~add //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from applied type reference: //│ ║ l.194: def eval1_ty: ('a -> int) -> E1['a] -> int //│ ╙── ^^^^^^ //│ = [Function: eval1] //│ constrain calls : 200 //│ annoying calls : 9801 //│ subtyping calls : 21512 :e eval1 done e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: eval1 done e2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` //│ ║ l.345: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} //│ ║ ^ //│ ╟── from field selection: //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ╙── ^^^^^ //│ res: error | int //│ Runtime error: //│ Error: non-exhaustive case expression :e prettier2 done eval1 e1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b | ?c)` is not an instance of type `number` //│ ║ l.72: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.75: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' :e :stats prettier2 done (eval1 done) e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done (eval1 done) e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` does not match type `nothing` //│ ║ l.345: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.4: def done x = case x of {} //│ ║ ^ //│ ╟── from field selection: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ Runtime error: //│ Error: non-exhaustive case expression //│ constrain calls : 543 //│ annoying calls : 238 //│ subtyping calls : 6175 :e :stats prettier2 done eval2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.72: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ res: error | 'a -> string //│ where //│ 'a <: Add[?]\lhs\rhs & {lhs: nothing -> int & 'a, rhs: nothing -> int & 'a} | Lit | 'b & ~#Add & ~#Lit //│ 'b <: Nega[?] & {arg: 'c} //│ 'c <: Add[?]\lhs\rhs & {lhs: nothing -> int & 'c, rhs: nothing -> int & 'c} | Lit | 'b & ~#Add & ~#Lit //│ = [Function (anonymous)] //│ constrain calls : 63 //│ annoying calls : 0 //│ subtyping calls : 517 :e :stats prettier2 done eval2 e1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.72: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.354: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '123' //│ constrain calls : 299 //│ annoying calls : 108 //│ subtyping calls : 3050 :e :stats prettier2 done eval2 e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.72: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.354: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' //│ constrain calls : 346 //│ annoying calls : 131 //│ subtyping calls : 3457 :e :stats prettier2 done eval2 d2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `number` //│ ║ l.72: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval2 d2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Nega[?E] & {Nega#E = ?E, arg: ?arg}` is not a function //│ ║ l.345: def nega arg = Nega { arg } //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.354: | _ -> k x //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error | string //│ = '-1-1' //│ constrain calls : 253 //│ annoying calls : 95 //│ subtyping calls : 2432 :e :stats prettier2 done eval1 e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b | ?c)` is not an instance of type `number` //│ ║ l.72: rec def eval1 k e = case e of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.73: | Lit -> e.val //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.74: | Add -> eval1 k e.lhs + eval1 k e.rhs //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> k e //│ ║ ^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: prettier2 done eval1 e2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.19: def lit val = Lit { val } //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.75: | _ -> k e //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.237: else if ev e.rhs == 0 then prettier1 k ev e.lhs //│ ╙── ^^^^^ //│ res: error //│ = '1-123' //│ constrain calls : 346 //│ annoying calls : 131 //│ subtyping calls : 3407 ================================================ FILE: shared/src/test/diff/mlscript/FatNegs.mls ================================================ class Lit: { n: int } //│ Defined class Lit // TODO should eventually print these differently or make them truly equivalent // by making #Lit <: Lit, i.e. making the tags include the fields implicitly. // * Q: What happens if one writes ~Lit instead of ~#Lit? def nc: ~Lit def nt: ~#Lit //│ nc: ~Lit //│ = //│ nt: ~Lit //│ = // * A: It's not equivalent. // * Demonstrates that the negated tag is a subtype of the whole negated class: nc = nt //│ ~Lit //│ <: nc: //│ ~Lit //│ = //│ nt is not implemented // * Demonstrates that the negated tag is not a supertype of the whole negated class: :e nt = nc //│ ~Lit //│ <: nt: //│ ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.28: nt = nc //│ ║ ^^^^^^^ //│ ╟── type `Lit` does not have field 'n' //│ ║ l.9: def nc: ~Lit //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `{n: int}` //│ ║ l.28: nt = nc //│ ║ ^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.2: class Lit: { n: int } //│ ║ ^^^^^^^^^^ //│ ╟── from type negation: //│ ║ l.10: def nt: ~#Lit //│ ╙── ^^^^^ //│ = //│ nc and nt are not implemented nc = nc //│ ~Lit //│ <: nc: //│ ~Lit //│ = //│ nc and nt are not implemented nt = nt //│ ~Lit //│ <: nt: //│ ~Lit //│ = //│ nt, nc and nt are not implemented ================================================ FILE: shared/src/test/diff/mlscript/FunnySubsumptions.mls ================================================ :NoJS trait C trait D trait S trait T trait U //│ Defined trait C //│ Defined trait D //│ Defined trait S //│ Defined trait T //│ Defined trait U def A0: (C | {x:T}) & (D | {y:S}) def B0: C&{y:S} | D&{x:T} | {x: T; y: S} //│ A0: {x: T, y: S} | C & D | C & {y: S} | D & {x: T} //│ B0: {x: T, y: S} | C & {y: S} | D & {x: T} :stats A0 = B0 //│ {x: T, y: S} | C & {y: S} | D & {x: T} //│ <: A0: //│ {x: T, y: S} | C & D | C & {y: S} | D & {x: T} //│ constrain calls : 1 //│ annoying calls : 0 //│ subtyping calls : 185 :stats B0 = A0 //│ {x: T, y: S} | C & D | C & {y: S} | D & {x: T} //│ <: B0: //│ {x: T, y: S} | C & {y: S} | D & {x: T} //│ constrain calls : 2 //│ annoying calls : 32 //│ subtyping calls : 277 def A1: C & {x : T} | D & {y : U} def B1: (C | D) & (C | {y : U}) & (D | {x : T}) //│ A1: C & {x: T} | D & {y: U} //│ B1: C & D | C & {x: T} | D & {y: U} :stats A1 = B1 //│ C & D | C & {x: T} | D & {y: U} //│ <: A1: //│ C & {x: T} | D & {y: U} //│ constrain calls : 2 //│ annoying calls : 13 //│ subtyping calls : 233 :stats B1 = A1 //│ C & {x: T} | D & {y: U} //│ <: B1: //│ C & D | C & {x: T} | D & {y: U} //│ constrain calls : 1 //│ annoying calls : 0 //│ subtyping calls : 170 ================================================ FILE: shared/src/test/diff/mlscript/GenericClasses.mls ================================================ class None: {} def None = None{} //│ Defined class None //│ None: None //│ = [Function: None1] class Some[A]: { value: A } def Some v = Some { value = v } //│ Defined class Some[+A] //│ Some: 'value -> Some['value] //│ = [Function: Some1] Some 42 (Some 42).value //│ res: Some[42] //│ = Some { value: 42 } //│ res: 42 //│ = 42 type Option[A] = Some[A] | None //│ Defined type alias Option[+A] None: Option[int] Some 42 : Option[int] //│ res: Option[int] //│ = None {} //│ res: Option[int] //│ = Some { value: 42 } :e res.value //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.32: res.value //│ ║ ^^^^^^^^^ //│ ╟── type `Option[int]` does not have field 'value' //│ ║ l.25: Some 42 : Option[int] //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{value: ?value}` //│ ║ l.32: res.value //│ ╙── ^^^ //│ res: error | int //│ = 42 :e 42: Option[int, int] //│ ╔══[ERROR] Wrong number of type arguments – expected 1, found 2 //│ ║ l.46: 42: Option[int, int] //│ ╙── ^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.46: 42: Option[int, int] //│ ║ ^^ //│ ╟── integer literal of type `42` does not match type `None | Some[int]` //│ ╟── Note: constraint arises from union type: //│ ║ l.21: type Option[A] = Some[A] | None //│ ║ ^^^^^^^^^^^^^^ //│ ╟── from applied type reference: //│ ║ l.46: 42: Option[int, int] //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: Option[int] //│ = 42 class Foo1[A]: { x: A } class Bar1: Foo1[int] //│ Defined class Foo1[+A] //│ Defined class Bar1 Bar1 //│ res: {x: int & 'x} -> (Bar1 with {x: 'x}) //│ = [Function: res] g = Bar1 { x = 42 } //│ g: Bar1 & {x: 42} //│ = Bar1 { x: 42 } g: Foo1['a] //│ res: Foo1[int] //│ = Bar1 { x: 42 } res.x //│ res: int //│ = 42 g: Foo1['a]: Foo1[int] //│ res: Foo1[int] //│ = Bar1 { x: 42 } res.x //│ res: int //│ = 42 :e g: Foo1['a]: Foo1[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.95: g: Foo1['a]: Foo1[string] //│ ║ ^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.66: class Bar1: Foo1[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.95: g: Foo1['a]: Foo1[string] //│ ╙── ^^^^^^ //│ res: Foo1[string] //│ = Bar1 { x: 42 } class Foo2[A] method Foo2: A -> A method Foo2 = id //│ Defined class Foo2[=A] //│ Declared Foo2.Foo2: Foo2['A] -> 'A -> 'A //│ Defined Foo2.Foo2: Foo2['A] -> 'a -> 'a Foo2 //│ res: anything -> Foo2['A] //│ = [Function: res] :re error: Foo2[int] //│ res: Foo2[int] //│ Runtime error: //│ Error: an error was thrown f = fun x -> case x of { Foo2 -> x } //│ f: (Foo2[?] & 'a) -> 'a //│ = [Function: f] f (Foo2 {}) //│ res: Foo2['A] //│ = Foo2 {} :re error: (Foo2[?] & 'a) -> 'a //│ res: (Foo2[?] & 'a) -> 'a //│ Runtime error: //│ Error: an error was thrown f: (Foo2[?] & 'a) -> 'a //│ res: (Foo2[?] & 'a) -> 'a //│ = [Function: f] :ns f: (Foo2[?] & 'a) -> 'a //│ res: (Foo2[?] & 'a) -> 'a //│ = [Function: f] f //│ res: (Foo2[?] & 'a) -> 'a //│ = [Function: f] def mrg: Foo2[int] & Foo2[string] //│ mrg: in Foo2[out int | string] out Foo2[in int | string out nothing] //│ = mrg: Foo2[int] //│ res: Foo2[int] //│ = //│ mrg is not implemented // * This no longer causes an error because it treats `mrg` as polymorphic and it separately satisfies each side of `Foo2[Int] & Foo2[String]`... mrg = Foo2{} //│ Foo2['A] //│ <: mrg: //│ Foo2[out int | string] //│ = Foo2 {} // * Even this one works although the LHS is not generalized. // * It works because the ctor's polymorphic type is pushed in by distributivity, // * resulting in a generalized value.... mrg2 = Foo2{} : Foo2[int] & Foo2[string] //│ mrg2: Foo2[in int | string out nothing] //│ = Foo2 {} // * Notice how this one doesn't work when we can't distribute foralls. :DontDistributeForalls :e mrg2 = Foo2{} : Foo2[int] & Foo2[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.181: mrg2 = Foo2{} : Foo2[int] & Foo2[string] //│ ║ ^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.181: mrg2 = Foo2{} : Foo2[int] & Foo2[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.181: mrg2 = Foo2{} : Foo2[int] & Foo2[string] //│ ║ ^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.110: class Foo2[A] //│ ╙── ^ //│ mrg2: Foo2[in int | string out nothing] //│ = Foo2 {} :DistributeForalls mrg.Foo2 //│ res: ('A & (int | string)) -> 'A //│ = [Function: id] mrg.Foo2 42 //│ res: 42 //│ = 42 def mrg: Foo2[int] | Foo2[string] //│ mrg: Foo2[out int | string] //│ = class Foo2_Co[A] method Foo2_Co: A method Foo2_Co = error //│ Defined class Foo2_Co[+A] //│ Declared Foo2_Co.Foo2_Co: Foo2_Co['A] -> 'A //│ Defined Foo2_Co.Foo2_Co: Foo2_Co[?] -> nothing Foo2_Co //│ res: anything -> Foo2_Co[nothing] //│ = [Function: res] :re error: Foo2_Co[int] //│ res: Foo2_Co[int] //│ Runtime error: //│ Error: an error was thrown f = fun x -> case x of { Foo2_Co -> x } //│ f: (Foo2_Co[?] & 'a) -> 'a //│ = [Function: f1] f (Foo2_Co {}) //│ res: Foo2_Co[nothing] //│ = Foo2_Co {} :re error: (Foo2_Co[?] & 'a) -> 'a //│ res: (Foo2_Co[nothing] & 'a) -> 'a //│ Runtime error: //│ Error: an error was thrown f: (Foo2_Co[?] & 'a) -> 'a //│ res: (Foo2_Co[nothing] & 'a) -> 'a //│ = [Function: f1] :ns f: (Foo2_Co[?] & 'a) -> 'a //│ res: (Foo2_Co[?] & 'a) -> 'a //│ = [Function: f1] f //│ res: (Foo2_Co[?] & 'a) -> 'a //│ = [Function: f1] def mrg: Foo2_Co[int] & Foo2_Co[string] //│ mrg: Foo2_Co[nothing] //│ = mrg: Foo2_Co[int] //│ res: Foo2_Co[int] //│ = //│ mrg is not implemented mrg = Foo2_Co{} //│ Foo2_Co[nothing] //│ <: mrg: //│ Foo2_Co[nothing] //│ = Foo2_Co {} def mrg: Foo2_Co[int] | Foo2_Co[string] //│ mrg: in Foo2_Co[nothing] out Foo2_Co[int | string] //│ = :w class Foo2_Bi[A] //│ Defined class Foo2_Bi[±A] //│ ╔══[WARNING] Type definition Foo2_Bi has bivariant type parameters: //│ ║ l.280: class Foo2_Bi[A] //│ ║ ^^^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.280: class Foo2_Bi[A] //│ ╙── ^ Foo2_Bi //│ res: anything -> Foo2_Bi[?] //│ = [Function: res] :re error: Foo2_Bi[int] //│ res: Foo2_Bi[?] //│ Runtime error: //│ Error: an error was thrown f = fun x -> case x of { Foo2_Bi -> x } //│ f: (Foo2_Bi[?] & 'a) -> 'a //│ = [Function: f2] f (Foo2_Bi {}) //│ res: Foo2_Bi[?] //│ = Foo2_Bi {} :re error: (Foo2_Bi[?] & 'a) -> 'a //│ res: (Foo2_Bi[?] & 'a) -> 'a //│ Runtime error: //│ Error: an error was thrown f: (Foo2_Bi[?] & 'a) -> 'a //│ res: (Foo2_Bi[?] & 'a) -> 'a //│ = [Function: f2] :ns f: (Foo2_Bi[?] & 'a) -> 'a //│ res: (Foo2_Bi[?] & 'a) -> 'a //│ = [Function: f2] f //│ res: (Foo2_Bi[?] & 'a) -> 'a //│ = [Function: f2] def mrg: Foo2_Bi[int] & Foo2_Bi[string] //│ mrg: Foo2_Bi[?] //│ = mrg: Foo2_Bi[int] //│ res: Foo2_Bi[?] //│ = //│ mrg is not implemented mrg = Foo2_Bi{} //│ Foo2_Bi[?] //│ <: mrg: //│ Foo2_Bi[?] //│ = Foo2_Bi {} def mrg: Foo2_Bi[int] | Foo2_Bi[string] //│ mrg: Foo2_Bi[?] //│ = ================================================ FILE: shared/src/test/diff/mlscript/GenericClasses2.mls ================================================ class Foo1[A]: { x: A } class Bar1: Foo1[int] //│ Defined class Foo1[+A] //│ Defined class Bar1 Foo1 //│ res: {x: 'x} -> Foo1['x] //│ = [Function: res] Bar1 //│ res: {x: int & 'x} -> (Bar1 with {x: 'x}) //│ = [Function: res] b = Bar1{x = 1} //│ b: Bar1 & {x: 1} //│ = Bar1 { x: 1 } :e b: Foo1[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.20: b: Foo1[string] //│ ║ ^ //│ ╟── integer literal of type `1` is not an instance of type `string` //│ ║ l.15: b = Bar1{x = 1} //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.20: b: Foo1[string] //│ ╙── ^^^^^^ //│ res: Foo1[string] //│ = Bar1 { x: 1 } class Foo2[A] method M2: A class Bar2: Foo2[int] & { x: int } method M2 = 1 //│ Defined class Foo2[+A] //│ Declared Foo2.M2: Foo2['A] -> 'A //│ Defined class Bar2 //│ Defined Bar2.M2: Bar2 -> 1 class Bar3: Foo2[int] //│ Defined class Bar3 :e :ns Bar2 Bar3 //│ res: forall 'x. {x: 'x} -> (#Bar2 & {Foo2#A = int, x: 'x}) //│ where //│ 'x <: int //│ = [Function: res] //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.49: Bar3 //│ ║ ^^^^ //│ ╟── Note that class Bar3 is abstract: //│ ║ l.43: class Bar3: Foo2[int] //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Hint: method M2 is abstract //│ ║ l.35: method M2: A //│ ╙── ^^^^^ //│ res: error //│ = [Function: res] :e Bar2 Bar3 //│ res: {x: int & 'x} -> (Bar2 with {x: 'x}) //│ = [Function: res] //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.68: Bar3 //│ ║ ^^^^ //│ ╟── Note that class Bar3 is abstract: //│ ║ l.43: class Bar3: Foo2[int] //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Hint: method M2 is abstract //│ ║ l.35: method M2: A //│ ╙── ^^^^^ //│ res: error //│ = [Function: res] :e {A = 1} //│ ╔══[ERROR] Field identifiers must start with a small letter //│ ║ l.84: {A = 1} //│ ╙── ^^^^^^^ //│ res: {A <: 1} //│ = { A: 1 } :e error: {A: 1} //│ ╔══[ERROR] Field identifiers must start with a small letter //│ ║ l.92: error: {A: 1} //│ ╙── ^ //│ res: {A <: 1} //│ Runtime error: //│ Error: an error was thrown b = Bar2{x = 1} //│ b: Bar2 & {x: 1} //│ = Bar2 { x: 1 } :e c = b: Foo2[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.105: c = b: Foo2[string] //│ ║ ^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.36: class Bar2: Foo2[int] & { x: int } //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.105: c = b: Foo2[string] //│ ╙── ^^^^^^ //│ c: Foo2[string] //│ = Bar2 { x: 1 } c.M2 //│ res: string //│ = 1 d = b: Bar2 //│ d: Bar2 //│ = Bar2 { x: 1 } d.M2 //│ res: int //│ = 1 d: Foo2['a] //│ res: Foo2[int] //│ = Bar2 { x: 1 } :e d: Foo2[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.135: d: Foo2[string] //│ ║ ^ //│ ╟── type `int` is not an instance of type `string` //│ ║ l.36: class Bar2: Foo2[int] & { x: int } //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.135: d: Foo2[string] //│ ╙── ^^^^^^ //│ res: Foo2[string] //│ = Bar2 { x: 1 } ================================================ FILE: shared/src/test/diff/mlscript/Group_2021_12_02.mls ================================================ class Lit class Add class Const //│ Defined class Lit //│ Defined class Add //│ Defined class Const Lit //│ res: anything -> Lit //│ = [Function: res] def eval0: ('b -> ('c & int)) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~lit & ~add & 'b as 'a) -> (int | 'c) //│ eval0: ('b -> int) -> 'a -> int //│ where //│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~#Add & ~#Lit //│ = def evalUgly: ('b -> ('c & int)) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~Lit & ~Add & 'b as 'a) -> (int | 'c) //│ evalUgly: ('b -> int) -> 'a -> int //│ where //│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ = def evalBad: ('b -> 'c) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~Lit & ~Add & 'b as 'a) -> (int | 'c) //│ evalBad: ('b -> 'c) -> 'a -> (int | 'c) //│ where //│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ = type Expr[A] = Lit & {val: int} | Add & {lhs: Expr[A]; rhs: Expr[A]} | ~Lit & ~Add & A //│ Defined type alias Expr[+A] def eval1: ('b -> ('c & int)) -> Expr['b] -> (int | 'c) //│ eval1: ('b -> int) -> Expr['b] -> int //│ = def eval: ('b -> int) -> Expr['b] -> int //│ eval: ('b -> int) -> Expr['b] -> int //│ = rec def evalImpl f e = case e of { | Lit -> e.val | Add -> evalImpl f e.lhs + evalImpl f e.rhs | _ -> f e } //│ evalImpl: ('a -> int) -> 'b -> int //│ where //│ 'b <: Add & {lhs: 'b, rhs: 'b} | Lit & {val: int} | 'a & ~#Add & ~#Lit //│ = [Function: evalImpl] rec def evalPeel f e = case e of { | Lit -> e.val | Add -> eval f e.lhs + eval f e.rhs | _ -> f e } //│ evalPeel: ('b -> int & 'a -> 'val) -> (Add & {lhs: Expr['b], rhs: Expr['b]} | Lit & {val: 'val} | 'a & ~#Add & ~#Lit) -> (int | 'val) //│ = //│ eval is not implemented eval = evalPeel //│ ('b -> int & 'a -> 'val) -> (Add & {lhs: Expr['b], rhs: Expr['b]} | Lit & {val: 'val} | 'a & ~#Add & ~#Lit) -> (int | 'val) //│ <: eval: //│ ('b -> int) -> Expr['b] -> int //│ = //│ evalPeel and eval are not implemented eval = evalImpl //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add & {lhs: 'b, rhs: 'b} | Lit & {val: int} | 'a & ~#Add & ~#Lit //│ <: eval: //│ ('b -> int) -> Expr['b] -> int //│ = [Function: evalImpl] eval0 = evalImpl //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add & {lhs: 'b, rhs: 'b} | Lit & {val: int} | 'a & ~#Add & ~#Lit //│ <: eval0: //│ ('b -> int) -> 'a -> int //│ where //│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~#Add & ~#Lit //│ = [Function: evalImpl] evalUgly = evalImpl //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add & {lhs: 'b, rhs: 'b} | Lit & {val: int} | 'a & ~#Add & ~#Lit //│ <: evalUgly: //│ ('b -> int) -> 'a -> int //│ where //│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ = [Function: evalImpl] eval1 = evalImpl //│ ('a -> int) -> 'b -> int //│ where //│ 'b <: Add & {lhs: 'b, rhs: 'b} | Lit & {val: int} | 'a & ~#Add & ~#Lit //│ <: eval1: //│ ('b -> int) -> Expr['b] -> int //│ = [Function: evalImpl] :e evalBad = eval //│ ('b -> int) -> Expr['b] -> int //│ <: evalBad: //│ ('b -> 'c) -> 'a -> (int | 'c) //│ where //│ 'a <: Add & {lhs: 'a, rhs: 'a} | Lit & {val: int} | 'b & ~Add & ~Lit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.105: evalBad = eval //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `'c` is not an instance of type `int` //│ ║ l.25: def evalBad: ('b -> 'c) -> (Lit & {val: int} | Add & {lhs: 'a; rhs: 'a} | ~Lit & ~Add & 'b as 'a) -> (int | 'c) //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.38: def eval: ('b -> int) -> Expr['b] -> int //│ ╙── ^^^ //│ = [Function: evalImpl] :e eval {} (Lit{} with { val = 1 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.123: eval {} (Lit{} with { val = 1 }) //│ ║ ^^^^^^^ //│ ╟── record literal of type `anything` is not a function //│ ║ l.123: eval {} (Lit{} with { val = 1 }) //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.38: def eval: ('b -> int) -> Expr['b] -> int //│ ╙── ^^^^^^^^^^^ //│ res: error | int //│ = 1 eval (fun x -> x) (Lit{} with { val = 1 }) //│ res: int //│ = 1 eval (fun x -> x) 1 //│ res: int //│ = 1 eval (fun x -> x.hello) (Const{} with {hello=1}) //│ res: int //│ = 1 :e eval (fun x -> x.hello) {hello=1} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.149: eval (fun x -> x.hello) {hello=1} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{hello: 1}` does not match type `Add & {lhs: Expr['b], rhs: Expr['b]} | Lit & {val: int} | 'b & ~Add & ~Lit` //│ ║ l.149: eval (fun x -> x.hello) {hello=1} //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.31: type Expr[A] = Lit & {val: int} | Add & {lhs: Expr[A]; rhs: Expr[A]} | ~Lit & ~Add & A //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from applied type reference: //│ ║ l.38: def eval: ('b -> int) -> Expr['b] -> int //│ ╙── ^^^^^^^^ //│ res: error | int //│ = 1 l = Lit{} with {hello=1} //│ l: Lit & {hello: 1} //│ = Lit { hello: 1 } l: {hello:1} //│ res: {hello: 1} //│ = Lit { hello: 1 } ================================================ FILE: shared/src/test/diff/mlscript/Group_2022_05_10.mls ================================================ def f x = (x 1, x "ok") //│ f: (1 -> 'a & "ok" -> 'b) -> ('a, 'b,) //│ = [Function: f] f (fun x -> (x, x)) //│ res: ((1, 1,), ("ok", "ok",),) //│ = [ [ 1, 1 ], [ 'ok', 'ok' ] ] (fun id -> id id 0) (fun x -> x) //│ res: 0 //│ = 0 fun id -> id id 0 //│ res: ('a -> 0 -> 'b & 'a) -> 'b //│ = [Function: res] fun x -> x //│ res: 'a -> 'a //│ = [Function: res] // forall 'a. 'a -> 'a <: 'a1 -> 0 -> 'b1 & 'a1 // i.e. // forall 'a. 'a -> 'a <: 'a1 -> 0 -> 'b1 // forall 'a. 'a -> 'a <: 'a1 // i.e. // 'a2 -> 'a2 <: 'a1 -> 0 -> 'b1 // 'a3 -> 'a3 <: 'a1 def g x = x (fun x -> x) //│ g: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: g] def g x y = x (fun x -> y x) //│ g: (('a -> 'b) -> 'c) -> ('a -> 'b) -> 'c //│ = [Function: g1] self x = x x //│ self: ('a -> 'b & 'a) -> 'b //│ = [Function: self] :re self self! //│ res: nothing //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/mlscript/Group_2022_06_09.mls ================================================ class Exp //│ Defined class Exp class Lit: { n: int } & Exp class Add: { lhs: Exp; rhs: Exp } & Exp //│ Defined class Lit //│ Defined class Add rec def eval e = case e of { | Lit -> e.n | Add -> eval e.lhs + eval e.rhs } //│ eval: 'a -> int //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit //│ = [Function: eval] e = Add { lhs = Lit {n=1}; rhs = Lit {n=2} } //│ e: Add with {lhs: Lit & {n: 1}, rhs: Lit & {n: 2}} //│ = Add { lhs: Lit { n: 1 }, rhs: Lit { n: 2 } } eval e //│ res: int //│ = 3 class Neg: { e: Exp } & Exp //│ Defined class Neg e2 = Neg { e } //│ e2: Neg with {e: Add with {lhs: Lit & {n: 1}, rhs: Lit & {n: 2}}} //│ = Neg { e: Add { lhs: Lit { n: 1 }, rhs: Lit { n: 2 } } } e2.e.lhs.n //│ res: 1 //│ = 1 :e eval e2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.40: eval e2 //│ ║ ^^^^^^^ //│ ╟── application of type `Neg & {e: ?e}` does not match type `Add & ?a | Lit & ?b` //│ ║ l.31: e2 = Neg { e } //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Add & ?c | Lit & ?d` //│ ║ l.40: eval e2 //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.10: rec def eval e = case e of { //│ ╙── ^ //│ res: error | int //│ Runtime error: //│ Error: non-exhaustive case expression def eval eval e = case e of { | Lit -> e.n | Add -> eval eval e.lhs + eval eval e.rhs } //│ eval: ('a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {n: 'n})) -> (int | 'n) //│ = [Function: eval1] eval eval! e //│ res: int //│ = 3 def evalN evalN e = case e of { | Neg -> 0 - evalN evalN e.e | _ -> eval evalN e } //│ evalN: ('a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {n: 'n}) | (Neg with {e: 'lhs})) -> (int | 'n) //│ = [Function: evalN] evalN eval! e //│ res: int //│ = 3 evalN eval! e2 //│ res: int //│ = -3 ev = evalN evalN! //│ ev: ((Add with {lhs: 'a, rhs: 'a}) | (Lit with {n: 'n}) | (Neg with {e: 'a})) -> (int | 'n) //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit | (Neg with {e: 'a}) //│ = [Function (anonymous)] (ev e, ev e2) //│ res: (int, int,) //│ = [ 3, -3 ] e3 = Add { lhs = Neg { e = Lit {n=1} }; rhs = Lit {n=2} } //│ e3: Add with {lhs: Neg with {e: Lit & {n: 1}}, rhs: Lit & {n: 2}} //│ = Add { lhs: Neg { e: Lit { n: 1 } }, rhs: Lit { n: 2 } } ev e3 //│ res: int //│ = 1 // * With a continuation, to make `evalN` not need to mention `eval` at all: def evalN evalN k e = case e of { | Neg -> 0 - evalN evalN k e.e | _ -> k e } //│ evalN: ('a -> 'b -> 'e -> int & 'a) -> ('c -> 'd & 'b) -> ((Neg with {e: 'e}) | 'c & ~#Neg) -> (int | 'd) //│ = [Function: evalN1] // * Note that these `evalComposed` types could be simplified further // * by making the simplifier realize that in `Lit with {n: 'n}`, `'n` really co-occurs with `int`! def evalComposed evalComposed = evalN evalN! (fun e -> eval evalComposed e) //│ evalComposed: ('a -> 'rhs -> int & 'a) -> ((Add with {lhs: 'rhs, rhs: 'rhs}) | (Lit with {n: 'n}) | (Neg with {e: 'b})) -> (int | 'n) //│ where //│ 'b <: (Add with {lhs: 'rhs, rhs: 'rhs}) | Lit | (Neg with {e: 'b}) //│ = [Function: evalComposed] def evalComposed evalComposed = evalN evalN! (eval evalComposed) //│ evalComposed: ('a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {n: 'n}) | (Neg with {e: 'b})) -> (int | 'n) //│ where //│ 'b <: (Add with {lhs: 'lhs, rhs: 'lhs}) | Lit | (Neg with {e: 'b}) //│ = [Function: evalComposed1] // * NOTE: This inferred type is a good example // * of the need for an algorithm to tie recursive TV knots and inline the rest; // * once we inline, `b` we should get the expected simplified recursive type. ev2 = evalComposed evalComposed! //│ ev2: (Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit\n & {n: 'n} | Neg\e & {e: 'c}) -> (int | 'n) //│ where //│ 'c <: Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit | Neg\e & {e: 'c} //│ 'a <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit | Neg\e & {e: 'a} //│ 'lhs <: Neg\e & {e: 'a} | 'b & ~#Neg //│ 'b <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit //│ 'rhs <: Neg\e & {e: 'a} | 'b & ~#Neg //│ = [Function (anonymous)] ev2 e3 //│ res: int //│ = 1 def ev2_ty: (Add & { lhs: 'a; rhs: 'a } | Lit | Neg & { e: 'a } as 'a) -> int //│ ev2_ty: 'a -> int //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit | (Neg with {e: 'a}) //│ = ev2_ty = ev2 //│ (Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit\n & {n: 'n} | Neg\e & {e: 'c}) -> (int | 'n) //│ where //│ 'c <: Add\lhs\rhs & {lhs: Neg\e & {e: 'a} | 'b & ~#Neg, rhs: Neg\e & {e: 'a} | 'b & ~#Neg} | Lit | Neg\e & {e: 'c} //│ 'a <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit | Neg\e & {e: 'a} //│ 'lhs <: Neg\e & {e: 'a} | 'b & ~#Neg //│ 'b <: Add\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Lit //│ 'rhs <: Neg\e & {e: 'a} | 'b & ~#Neg //│ <: ev2_ty: //│ 'a -> int //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit | (Neg with {e: 'a}) //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/mlscript/HeadOption.mls ================================================ :NoJS :w class Option[A] class Some[A]: Option[A] & { payload: A } class None[A]: Option[A] //│ Defined class Option[±A] //│ Defined class Some[+A] //│ Defined class None[±A] //│ ╔══[WARNING] Type definition None has bivariant type parameters: //│ ║ l.6: class None[A]: Option[A] //│ ║ ^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.6: class None[A]: Option[A] //│ ╙── ^ //│ ╔══[WARNING] Type definition Option has bivariant type parameters: //│ ║ l.4: class Option[A] //│ ║ ^^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.4: class Option[A] //│ ╙── ^ :w class List[A] method HeadOption: Option[A] //│ Defined class List[±A] //│ Declared List.HeadOption: List[?] -> Option[?] //│ ╔══[WARNING] Type definition List has bivariant type parameters: //│ ║ l.25: class List[A] //│ ║ ^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.25: class List[A] //│ ╙── ^ :w class Nil[A]: List[A] method HeadOption = None {} //│ Defined class Nil[±A] //│ Defined Nil.HeadOption: Nil[?] -> None[?] //│ ╔══[WARNING] Type definition Nil has bivariant type parameters: //│ ║ l.37: class Nil[A]: List[A] //│ ║ ^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.37: class Nil[A]: List[A] //│ ╙── ^ class Cons[A]: List[A] & { head: A; tail: List[A] } method HeadOption = Some { payload = error: A } //│ Defined class Cons[+A] //│ Defined Cons.HeadOption: Cons['A] -> Some['A] // * Incur much fewer constraining/subtyping checks: // class Cons[A]: List[A] & { head: A; tail: list } // class Cons[A]: List[A] & { head: A; tail: anything } :stats l0 = Cons { head = 1; tail = Nil {} } //│ l0: Cons[1] with {tail: Nil[?]} //│ constrain calls : 24 //│ annoying calls : 1 //│ subtyping calls : 84 :stats Cons.HeadOption l0 //│ res: Some[1] //│ constrain calls : 45 //│ annoying calls : 22 //│ subtyping calls : 137 :stats l1 = Cons { head = 1; tail = Cons { head = 2; tail = Cons { head = 3; tail = Nil {} } } } //│ l1: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}} //│ constrain calls : 54 //│ annoying calls : 1 //│ subtyping calls : 246 :stats Cons.HeadOption l1 //│ res: Some[1] //│ constrain calls : 42 //│ annoying calls : 21 //│ subtyping calls : 140 :stats l2 = Cons { head = 0; tail = l1 } //│ l2: Cons[0] with {tail: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}}} //│ constrain calls : 20 //│ annoying calls : 1 //│ subtyping calls : 169 :stats Cons.HeadOption l2 //│ res: Some[0] //│ constrain calls : 48 //│ annoying calls : 22 //│ subtyping calls : 143 :stats l3 = Cons { head = 0-1; tail = l2 } //│ l3: Cons[int] with { //│ tail: Cons[0] with {tail: Cons[1] with {tail: Cons[2] with {tail: Cons[3] with {tail: Nil[?]}}}} //│ } //│ constrain calls : 33 //│ annoying calls : 1 //│ subtyping calls : 250 :stats Cons.HeadOption l3 //│ res: Some[int] //│ constrain calls : 50 //│ annoying calls : 22 //│ subtyping calls : 148 :stats rec def lr1 = Cons { head = 0; tail = lr1 } //│ lr1: 'tail //│ where //│ 'tail :> Cons[0] with {tail: 'tail} //│ constrain calls : 19 //│ annoying calls : 0 //│ subtyping calls : 96 :stats Cons.HeadOption lr1 //│ res: Some[0] //│ constrain calls : 45 //│ annoying calls : 21 //│ subtyping calls : 147 :stats rec def lr2 = Cons { head = 0; tail = Cons { head = 1; tail = Cons { head = 3; tail = lr2 } } } //│ lr2: Cons[0] with {tail: 'tail} //│ where //│ 'tail :> Cons[1] with {tail: Cons[3] with {tail: Cons[0] with {tail: 'tail}}} //│ constrain calls : 49 //│ annoying calls : 0 //│ subtyping calls : 280 :stats Cons.HeadOption lr2 //│ res: Some[0] //│ constrain calls : 44 //│ annoying calls : 21 //│ subtyping calls : 142 :e l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{tail: ?a}` does not have field 'head' //│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{tail: ?a}` does not have field 'head' //│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{tail: ?a}` does not have field 'head' //│ ║ l.151: l1 = Cons { tail = Cons { tail = Cons { tail = Nil {} } } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ l1: (Cons[nothing] with { //│ tail: (Cons[nothing] with {tail: (Cons[nothing] with {tail: Nil[?]}) | error}) | error //│ }) | error ================================================ FILE: shared/src/test/diff/mlscript/Huawei2.mls ================================================ class C[A]: { a: A -> A } //│ Defined class C[=A] class D: C[int] & { b: int } //│ Defined class D d = D{a = id; b = if true then 0 else 1} //│ d: D with {a: forall 'a. 'a -> 'a, b: 0 | 1} //│ = D { a: [Function: id], b: 0 } d.a true //│ res: true //│ = true d.a 0 //│ res: 0 //│ = 0 d : #D //│ res: D //│ = D { a: [Function: id], b: 0 } def foo x = case x of D -> x.b, _ -> 0 //│ foo: ((D\a with {b: 'b}) | ~D) -> (0 | 'b) //│ = [Function: foo] // Overloading through intersections: not supported // foo: ((D\a with {b: 'b}) -> 'b) & (~D -> 0) foo d //│ res: 0 | 1 //│ = 0 ================================================ FILE: shared/src/test/diff/mlscript/Inherit.mls ================================================ class Parent: { name: string } //│ Defined class Parent class Child: Parent & { age: int } //│ Defined class Child p = Parent{ name = "Bob" } //│ p: Parent & {name: "Bob"} //│ = Parent { name: 'Bob' } p: Parent //│ res: Parent //│ = Parent { name: 'Bob' } :e c = Child(Parent { name = "Bob" }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.17: c = Child(Parent { name = "Bob" }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Parent & {name: ?name}` does not have all required fields 'name', 'age' //│ ║ l.17: c = Child(Parent { name = "Bob" }) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ c: Child & {age: nothing, name: "Bob"} | error //│ = Child { name: 'Bob', age: undefined } c = Child(Parent { name = "Bob" } with { age = 123 }) //│ c: Child & {age: 123, name: "Bob"} //│ = Child { name: 'Bob', age: 123 } c: Parent //│ res: Parent //│ = Child { name: 'Bob', age: 123 } c.name //│ res: "Bob" //│ = 'Bob' c: { name: 'a } //│ res: {name: "Bob"} //│ = Child { name: 'Bob', age: 123 } c: { name: string } //│ res: {name: string} //│ = Child { name: 'Bob', age: 123 } def f x = case x of { | Parent -> x.name } //│ f: (Parent with {name: 'name}) -> 'name //│ = [Function: f] f c //│ res: "Bob" //│ = 'Bob' f (c: Parent) //│ res: string //│ = 'Bob' def f x = case x of { | Child -> x.name } //│ f: (Child\age with {name: 'name}) -> 'name //│ = [Function: f1] f c //│ res: "Bob" //│ = 'Bob' :e f (c: Parent) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.74: f (c: Parent) //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `Parent` is not an instance of type `Child` //│ ║ l.74: f (c: Parent) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.64: | Child -> x.name //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.63: def f x = case x of { //│ ╙── ^ //│ res: error | string //│ = 'Bob' def f x = case x of { | Parent -> x.name | _ -> "" } //│ f: ((Parent with {name: 'name}) | ~Parent) -> ("" | 'name) //│ = [Function: f2] f c //│ res: "" | "Bob" //│ = 'Bob' f (c: Parent) //│ res: string //│ = 'Bob' def f x = case x of { | Parent -> x.name | Child -> x.name } //│ f: (Parent with {name: 'name}) -> 'name //│ = [Function: f3] f c //│ res: "Bob" //│ = 'Bob' f (c: Parent) //│ res: string //│ = 'Bob' ================================================ FILE: shared/src/test/diff/mlscript/InheritSimple.mls ================================================ class Parent: {} //│ Defined class Parent class Child: Parent //│ Defined class Child p = Parent{} //│ p: Parent //│ = Parent {} c = Child(p) //│ c: Child //│ = Child {} :e c.name //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.17: c.name //│ ║ ^^^^^^ //│ ╟── application of type `Child` does not have field 'name' //│ ║ l.12: c = Child(p) //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `{name: ?name}` //│ ║ l.17: c.name //│ ║ ^ //│ ╟── Note: class Child is defined at: //│ ║ l.5: class Child: Parent //│ ╙── ^^^^^ //│ res: error //│ = undefined c: Child //│ res: Child //│ = Child {} c: Parent //│ res: Parent //│ = Child {} c: Parent & Child //│ res: Child & Parent //│ = Child {} c: Parent & Child & {} //│ res: Child & Parent //│ = Child {} c: Parent & anything & Child & {} //│ res: Child & Parent //│ = Child {} ================================================ FILE: shared/src/test/diff/mlscript/JetBrains.mls ================================================ class Expr class Lit: Expr & { val: int } class Add: Expr & { lhs: Expr; rhs: Expr } //│ Defined class Expr //│ Defined class Lit //│ Defined class Add rec def eval x = case x of { Lit -> x.val | Add -> eval x.lhs + eval x.rhs } //│ eval: 'a -> int //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit //│ = [Function: eval] def eval eval x = case x of { Lit -> x.val | Add -> eval eval x.lhs + eval eval x.rhs } //│ eval: ('a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {val: 'val})) -> (int | 'val) //│ = [Function: eval1] eval eval! //│ res: ((Add with {lhs: 'a, rhs: 'a}) | (Lit with {val: 'val})) -> (int | 'val) //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit //│ = [Function (anonymous)] eval eval! //│ res: ((Add with {lhs: 'a, rhs: 'a}) | (Lit with {val: 'val})) -> (int | 'val) //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit //│ = [Function (anonymous)] class Neg: Expr & { underlying: Expr } //│ Defined class Neg def evalN evalN x = case x of { Neg -> 0 - (evalN evalN x.underlying) | _ -> eval evalN x } //│ evalN: ('a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {val: 'val}) | (Neg with {underlying: 'lhs})) -> (int | 'val) //│ = [Function: evalN] evalN evalN! //│ res: ((Add with {lhs: 'a, rhs: 'a}) | (Lit with {val: 'val}) | (Neg with {underlying: 'a})) -> (int | 'val) //│ where //│ 'a <: (Add with {lhs: 'a, rhs: 'a}) | Lit | (Neg with {underlying: 'a}) //│ = [Function (anonymous)] e = Add{lhs = Lit{val = 1}; rhs = Neg{underlying = Lit{val = 2}}} //│ e: Add with {lhs: Lit & {val: 1}, rhs: Neg with {underlying: Lit & {val: 2}}} //│ = Add { lhs: Lit { val: 1 }, rhs: Neg { underlying: Lit { val: 2 } } } evalN evalN! e //│ res: int //│ = -1 // === === === ERROR CASES === === === // :ShowRelativeLineNums :AllowTypeErrors def evalN2 evalN2 x = case x of { Neg -> 0 - (evalN2 evalN2 x.underlying) | _ -> eval eval! x } //│ evalN2: ('a -> 'underlying -> int & 'a) -> ((Add with {lhs: 'b, rhs: 'b}) | (Lit with {val: 'val}) | (Neg with {underlying: 'underlying})) -> (int | 'val) //│ where //│ 'b <: (Add with {lhs: 'b, rhs: 'b}) | Lit evalN2 evalN2! e //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: evalN2 evalN2! e //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── application of type `Neg & {underlying: ?underlying}` does not match type `Add & ?a | Lit & ?b` //│ ║ l.53: e = Add{lhs = Lit{val = 1}; rhs = Neg{underlying = Lit{val = 2}}} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.18: def eval eval x = case x of { //│ ║ ^ //│ ╟── from field selection: //│ ║ l.20: | Add -> eval eval x.lhs + eval eval x.rhs //│ ╙── ^^^^^ //│ res: error | int def evalN3 evalN3 x = case x of { Neg -> 0 - (evalN2 x.underlying) | _ -> eval evalN3 x } //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+2: Neg -> 0 - (evalN2 x.underlying) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b ?c. ?b | ?c)` is not an instance of type `int` //│ ║ l.73: def evalN2 evalN2 x = case x of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.74: Neg -> 0 - (evalN2 evalN2 x.underlying) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.75: | _ -> eval eval! x //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.76: } //│ ║ ^^^ //│ ╟── but it flows into application with expected type `int` //│ ║ l.+2: Neg -> 0 - (evalN2 x.underlying) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ evalN3: ('a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {val: 'val}) | (Neg with {underlying: 'b -> nothing -> int & 'b})) -> (error | int | 'val) def evalN3 evalN3 x = case x of { Neg -> 0 - (evalN3 x.underlying) | _ -> eval evalN3 x } //│ evalN3: ('underlying -> int & 'a -> 'lhs -> int & 'a) -> ((Add with {lhs: 'lhs, rhs: 'lhs}) | (Lit with {val: 'val}) | (Neg with {underlying: 'underlying})) -> (int | 'val) evalN3 evalN3! e //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: evalN3 evalN3! e //│ ║ ^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (?b | ?c)` is not an instance of type `int` //│ ║ l.118: def evalN3 evalN3 x = case x of { //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.119: Neg -> 0 - (evalN3 x.underlying) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.120: | _ -> eval evalN3 x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.121: } //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.119: Neg -> 0 - (evalN3 x.underlying) //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: evalN3 evalN3! e //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── application of type `Lit & {val: ?val}` is not a function //│ ║ l.53: e = Add{lhs = Lit{val = 1}; rhs = Neg{underlying = Lit{val = 2}}} //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.20: | Add -> eval eval x.lhs + eval eval x.rhs //│ ║ ^^^^^^^^^ //│ ╟── from field selection: //│ ║ l.119: Neg -> 0 - (evalN3 x.underlying) //│ ╙── ^^^^^^^^^^^^ //│ res: error | int ================================================ FILE: shared/src/test/diff/mlscript/LetPolym.mls ================================================ def test f = let local x = f x in (local 0, local true) //│ test: ((0 | true) -> 'a) -> ('a, 'a,) //│ = [Function: test] def test f = let local x = f x in (local (fun z -> z), local (fun z -> true)) //│ test: ((forall 'a. 'a -> (true | 'a)) -> 'b) -> ('b, 'b,) //│ = [Function: test1] def test f = let local x = x f in (local (fun z -> z), local (fun z -> true)) //│ test: 'a -> ('a, true,) //│ = [Function: test2] def test f a b = let local x = f x in (local a, local b) //│ test: ('a -> 'b) -> 'a -> 'a -> ('b, 'b,) //│ = [Function: test3] def test x = let local g = g x in local succ //│ test: int -> int //│ = [Function: test4] test 1 //│ res: int //│ = 2 :e test "hi" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.37: test "hi" //│ ║ ^^^^^^^^^ //│ ╟── string literal of type `"hi"` is not an instance of type `int` //│ ║ l.37: test "hi" //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.27: let local g = g x //│ ╙── ^ //│ res: error | int //│ = 'hi1' def test f = let local() = succ (f 1) in local() //│ test: (1 -> int) -> int //│ = [Function: test5] test succ test id //│ res: int //│ = 3 //│ res: int //│ = 2 :e test (fun x -> false) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.64: test (fun x -> false) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.64: test (fun x -> false) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.51: let local() = succ (f 1) //│ ╙── ^^^ //│ res: error | int //│ = 1 :e test concat //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.78: test concat //│ ║ ^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `string` //│ ║ l.51: let local() = succ (f 1) //│ ╙── ^ //│ res: error | int //│ = '(y) => x + y1' def test f = let local g = g f in local succ //│ test: int -> int //│ = [Function: test6] def test f = let local g = g (f 1) in local succ //│ test: (1 -> int) -> int //│ = [Function: test7] :e test (fun x -> "oops") //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.101: test (fun x -> "oops") //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.101: test (fun x -> "oops") //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.95: let local g = g (f 1) //│ ╙── ^^^ //│ res: error | int //│ = 'oops1' def test f = let local g x = g (f x) in local succ //│ test: ('a -> int) -> 'a -> int //│ = [Function: test8] def test f = let local g x = g (f x) in local add //│ test: ('a -> int) -> 'a -> int -> int //│ = [Function: test9] def test f = let local g x = g (f x) in local add 1 //│ test: (1 -> int) -> int -> int //│ = [Function: test10] def test f = let local g x = g (f x) in (local add 1, local (fun y -> fun z -> y + z) 2) //│ test: ((1 | 2) -> int) -> (int -> int, int -> int,) //│ = [Function: test11] f_g = test succ f_g.0 42 f_g.1 42 //│ f_g: (int -> int, int -> int,) //│ = [ [Function (anonymous)], [Function (anonymous)] ] //│ res: int //│ = 44 //│ res: int //│ = 45 def test f = let local g x = g (f x) in (local add 1, local concat "ok") //│ test: (("ok" | 1) -> nothing) -> (int -> int, string -> string,) //│ = [Function: test12] :e test succ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.157: test succ //│ ║ ^^^^^^^^^ //│ ╟── string literal of type `"ok"` is not an instance of type `int` //│ ║ l.152: in (local add 1, local concat "ok") //│ ╙── ^^^^ //│ res: error | (int -> int, string -> string,) //│ = [ [Function (anonymous)], [Function (anonymous)] ] def test = (fun f -> let local g x = f (g x) in local ) add //│ test: ('a -> int) -> 'a -> int -> int //│ = [Function: test13] def test = (fun f -> fun x -> let local g = f (g x) in local ) add "1" //│ test: ("1" -> int) -> int -> int //│ = [Function: test14] def test = (fun f -> fun x -> let local g = f (g x) in local add "1" ) //│ test: ((int -> int) -> "1" -> 'a) -> int -> 'a //│ = [Function: test15] fun f -> fun x -> let local = (fun y -> f y) x in () //│ res: ('a -> anything) -> 'a -> () //│ = [Function: res] :e res add "1" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.199: res add "1" //│ ║ ^^^^^^^^^^^ //│ ╟── string literal of type `"1"` is not an instance of type `int` //│ ║ l.199: res add "1" //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.193: let local = (fun y -> f y) x //│ ║ ^ //│ ╟── from reference: //│ ║ l.193: let local = (fun y -> f y) x //│ ╙── ^ //│ res: error | () //│ = [] fun f -> fun x -> let local = f ((fun y -> y) x) in () //│ res: ('a -> anything) -> 'a -> () //│ = [Function: res] :e res add "1" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.222: res add "1" //│ ║ ^^^^^^^^^^^ //│ ╟── string literal of type `"1"` is not an instance of type `int` //│ ║ l.222: res add "1" //│ ║ ^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.216: let local = f ((fun y -> y) x) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.216: let local = f ((fun y -> y) x) //│ ╙── ^ //│ res: error | () //│ = [] def id: 'a -> 'a //│ id: 'a -> 'a //│ = fun f -> fun x -> let local = f (id x) in () //│ res: ('a -> anything) -> 'a -> () //│ = //│ id is not implemented :e (fun k -> k (fun x -> let tmp = add x 1 in x )) (fun f -> f true) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.250: (fun k -> k (fun x -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.251: let tmp = add x 1 in x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.252: )) (fun f -> f true) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.252: )) (fun f -> f true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.251: let tmp = add x 1 in x //│ ╙── ^ //│ res: error | true //│ = true :e (fun k -> let test = k (fun x -> let tmp = add x 1 in x ) in test ) (fun f -> f true) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.270: (fun k -> //│ ║ ^^^^^^^^^ //│ ║ l.271: let test = k (fun x -> //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.272: let tmp = add x 1 in x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.273: ) in test //│ ║ ^^^^^^^^^^^^^ //│ ║ l.274: ) (fun f -> f true) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.274: ) (fun f -> f true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.272: let tmp = add x 1 in x //│ ╙── ^ //│ res: error | true //│ = true def fst ((a, b)) = a def snd ((a, b)) = b //│ fst: (('a, anything,),) -> 'a //│ = [Function: fst] //│ snd: ((anything, 'a,),) -> 'a //│ = [Function: snd] def foo f = let x = f 42 in fst x + snd x //│ foo: (42 -> (int, int,)) -> int //│ = [Function: foo] def foo f = let x = f 42 in fst x + snd x //│ foo: (42 -> (int, int,)) -> int //│ = [Function: foo1] foo (fun x -> (x, x)) foo (fun x -> (0, x + 1)) //│ res: int //│ = 84 //│ res: int //│ = 43 def foo (f: 'a -> ('a, 'a)) = let x = f 42 in fst x + snd x //│ foo: ((42 | 'a) -> (int & 'a, int & 'a,)) -> int //│ = [Function: foo2] foo (fun x -> (x, x)) foo (fun x -> (0, x + 1)) //│ res: int //│ = 84 //│ res: int //│ = 43 def foo (f: forall 'a. 'a -> ('a, 'a)) = let x = f 42 in fst x + snd x //│ foo: (forall 'a. 'a -> ('a, 'a,)) -> int //│ = [Function: foo3] foo (fun x -> (x, x)) //│ res: int //│ = 84 :e foo (fun x -> (0, x + 1)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.347: foo (fun x -> (0, x + 1)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.336: def foo (f: forall 'a. 'a -> ('a, 'a)) = //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.347: foo (fun x -> (0, x + 1)) //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.336: def foo (f: forall 'a. 'a -> ('a, 'a)) = //│ ╙── ^^ //│ res: error | int //│ = 43 ================================================ FILE: shared/src/test/diff/mlscript/LineComments.mls ================================================ 1 2 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 1 // A 2 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 ================================================ FILE: shared/src/test/diff/mlscript/Lists.mls ================================================ class Nil: {} class Cons[A]: { head: A; tail: List[A] } type List[A] = Nil | Cons[A] //│ Defined class Nil //│ Defined class Cons[+A] //│ Defined type alias List[+A] def originalCons = Cons // Note: type still seems over-complicated because we push 'b inside the union, killing a hash-consing opportunity at the outer level... //│ originalCons: {head: 'head & 'A, tail: List['A] & 'tail} -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: originalCons] def Nil = Nil {} //│ Nil: Nil //│ = [Function: Nil1] def Cons head tail = Cons { head; tail } //│ Cons: ('head & 'A) -> (List['A] & 'tail) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] Cons 2 //│ res: (List['A] & 'tail) -> (Cons[2 | 'A] with {head: 2, tail: 'tail}) //│ = [Function (anonymous)] c = Cons 2 Nil //│ c: Cons[2] with {tail: Nil} //│ = Cons { head: 2, tail: Nil {} } d = Cons 1 c //│ d: Cons[1 | 2] with {head: 1, tail: Cons[2] with {tail: Nil}} //│ = Cons { head: 1, tail: Cons { head: 2, tail: Nil {} } } d.head //│ res: 1 //│ = 1 res: 1 //│ res: 1 //│ = 1 d.tail //│ res: Cons[2] with {tail: Nil} //│ = Cons { head: 2, tail: Nil {} } Cons 1 res //│ res: Cons[1 | 2] with {head: 1, tail: Cons[2] with {tail: Nil}} //│ = Cons { head: 1, tail: Cons { head: 2, tail: Nil {} } } res.tail //│ res: Cons[2] with {tail: Nil} //│ = Cons { head: 2, tail: Nil {} } Cons 1 (Cons 2 Nil) //│ res: Cons[1 | 2] with {head: 1, tail: Cons[2] with {tail: Nil}} //│ = Cons { head: 1, tail: Cons { head: 2, tail: Nil {} } } res.tail //│ res: Cons[2] with {tail: Nil} //│ = Cons { head: 2, tail: Nil {} } // We can now access the tail's tail, thanks to the refined type res.tail //│ res: Nil //│ = Nil {} :e res.tail.head //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.68: res.tail.head //│ ║ ^^^^^^^^ //│ ╟── application of type `Nil` does not have field 'tail' //│ ║ l.13: def Nil = Nil {} //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `{tail: ?tail}` //│ ║ l.68: res.tail.head //│ ║ ^^^ //│ ╟── Note: class Nil is defined at: //│ ║ l.2: class Nil: {} //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading 'head') // This used to yield a more precise Cons constructor, but it's no longer necessary in the new class semantics def Cons head tail = originalCons { head; tail } with { head; tail } //│ Cons: ('A & 'head) -> (List['A] & 'tail) -> (Cons['A] & {head: 'head, tail: 'tail}) //│ = [Function: Cons2] Cons 2 //│ res: (List['A] & 'tail) -> (Cons[2 | 'A] & {head: 2, tail: 'tail}) //│ = [Function (anonymous)] Cons 2 Nil //│ res: Cons[2] with {tail: Nil} //│ = Cons { head: 2, tail: Nil {} } res.head //│ res: 2 //│ = 2 :e Cons 1 res //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.106: Cons 1 res //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `2` does not match type `Cons[?A] | Nil` //│ ║ l.97: Cons 2 Nil //│ ║ ^ //│ ╟── but it flows into reference with expected type `Cons[?A0] | Nil` //│ ║ l.106: Cons 1 res //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.4: type List[A] = Nil | Cons[A] //│ ║ ^^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.88: def Cons head tail = originalCons { head; tail } with { head; tail } //│ ╙── ^^^^ //│ res: (Cons[1] with {tail: 2}) | error //│ = Cons { head: 1, tail: 2 } // Here there used to be a loss of precision in the older with-field approach. // The problem occured when constraining `α8 w/ {head: α5, tail: [α6]} if p ls.head then Cons ls.head (filter p ls.tail) else filter p ls.tail | Nil -> Nil } //│ filter: ('head -> bool) -> 'a -> 'b //│ where //│ 'b :> (Cons['head] with {tail: 'b}) | Nil //│ 'a <: (Cons[?] with {head: 'head, tail: 'a}) | Nil //│ = [Function: filter] def Cons head = originalCons { head=0; tail=Nil } with { head } //│ Cons: 'a -> (Cons[0] with {head: 'a, tail: Nil}) //│ = [Function: Cons3] Cons 1 //│ res: Cons[0] with {head: 1, tail: Nil} //│ = Cons { head: 1, tail: Nil {} } res.head //│ res: 1 //│ = 1 c = Cons 1 //│ c: Cons[0] with {head: 1, tail: Nil} //│ = Cons { head: 1, tail: Nil {} } c.head //│ res: 1 //│ = 1 def c: 'a -> List['b] -> List['a | 'b] c 1 (c 2 Nil) //│ c: 'a -> List['a] -> List['a] //│ = //│ res: List[1 | 2] //│ = //│ c is not implemented def c: 'a -> ('l & List['b]) -> (Cons[anything] & { head: 'a; tail: 'l }) c 1 (c 2 Nil) //│ c: 'a -> (List[?] & 'l) -> (Cons[?] with {head: 'a, tail: 'l}) //│ = //│ res: Cons[?] with {head: 1, tail: Cons[?] with {head: 2, tail: Nil}} //│ = //│ c is not implemented ================================================ FILE: shared/src/test/diff/mlscript/Luyu.mls ================================================ // Preamble def plus1 x = x + 1 //│ plus1: int -> int //│ = [Function: plus1] class JustIntBox: { value: int } method Get = this.value //│ Defined class JustIntBox //│ Defined JustIntBox.Get: JustIntBox -> int ib0 = JustIntBox { value = 0 } //│ ib0: JustIntBox & {value: 0} //│ = JustIntBox { value: 0 } ib0.Get //│ res: int //│ = 0 // Extend with some function. class MappableIntBox: JustIntBox method Map f = MappableIntBox { value = f this.value } //│ Defined class MappableIntBox //│ Defined MappableIntBox.Map: MappableIntBox -> (int -> (int & 'value)) -> (MappableIntBox with {value: 'value}) mib0 = MappableIntBox { value = 0 } //│ mib0: MappableIntBox & {value: 0} //│ = MappableIntBox { value: 0 } mib0.Get //│ res: int //│ = 0 mib0.Map //│ res: (int -> (int & 'value)) -> (MappableIntBox with {value: 'value}) //│ = [Function: Map] mib0.Map plus1 //│ res: MappableIntBox //│ = MappableIntBox { value: 1 } // Naughty map... :e mib0.Map (fun _ -> "str") //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.45: mib0.Map (fun _ -> "str") //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"str"` is not an instance of type `int` //│ ║ l.45: mib0.Map (fun _ -> "str") //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.8: class JustIntBox: { value: int } //│ ║ ^^^ //│ ╟── from application: //│ ║ l.23: method Map f = MappableIntBox { value = f this.value } //│ ╙── ^^^^^^^^^^^^ //│ res: (MappableIntBox with {value: "str"}) | error //│ = MappableIntBox { value: 'str' } class BetterIntBox: { value: int } method Get = this.value method Map f = BetterIntBox { value = f this.value } //│ Defined class BetterIntBox //│ Defined BetterIntBox.Get: BetterIntBox -> int //│ Defined BetterIntBox.Map: BetterIntBox -> (int -> (int & 'value)) -> (BetterIntBox with {value: 'value}) ================================================ FILE: shared/src/test/diff/mlscript/MLList.mls ================================================ class List[A] method Fold: (() -> 'b, (A, List[A]) -> 'b) -> 'b class Nil[A]: List[A] method Fold(f, _) = f() class Cons[A]: List[A] & { head: A; tail: List[A] } method Fold(_, f) = f(this.head, this.tail) //│ Defined class List[+A] //│ Declared List.Fold: List['A] -> (() -> 'b, ('A, List['A],) -> 'b,) -> 'b //│ Defined class Nil[+A] //│ Defined Nil.Fold: Nil[?] -> (() -> 'a, anything,) -> 'a //│ Defined class Cons[+A] //│ Defined Cons.Fold: Cons['A] -> (anything, ('A, List['A],) -> 'a,) -> 'a def nil = Nil {} : List['a] def cons(head, tail) = Cons { head; tail } : List['a] //│ nil: List[nothing] //│ = [Function: nil] //│ cons: ('a, List['a],) -> List['a] //│ = [Function: cons] rec def find(table, key) = table.Fold( fun () -> undefined, fun (h, t) -> if eq h.key key then h.val else find(t, key)) //│ find: (List[{key: anything, val: 'a}], anything,) -> (undefined | 'a) //│ = [Function: find] ls = cons({key="I"; val=id}, cons({key="omega"; val=fun x -> x x}, nil)) //│ ls: List[{key: "I" | "omega", val: forall 'a 'b 'c. ('a -> 'b & 'c & 'a) -> ('c | 'b)}] //│ = Cons { //│ head: { key: 'I', val: [Function: id] }, //│ tail: Cons { head: { key: 'omega', val: [Function: val] }, tail: Nil {} } //│ } res = find(ls, "omega") //│ res: undefined | ('a -> 'b & 'b & 'a) -> 'b //│ = [Function: val] case res of undefined -> "???", _ -> res id "!" //│ res: "!" | "???" //│ = '!' find(ls, "oops") //│ res: undefined | ('a -> 'b & 'c & 'a) -> ('c | 'b) //│ = undefined ================================================ FILE: shared/src/test/diff/mlscript/MLTuples.mls ================================================ t = (1, 2, 3) //│ t: (1, 2, 3,) //│ = [ 1, 2, 3 ] {} with {} //│ res: anything //│ = {} :js tx = t with { x = 1 } //│ // Query 1 //│ globalThis.tx = withConstruct(t, { x: 1 }); //│ // End of generated code //│ tx: (1, 2, 3,) & {x: 1} //│ = [ 1, 2, 3, x: 1 ] tx.x //│ res: 1 //│ = 1 trait Hey: { x: int } //│ Defined trait Hey :e Hey t //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.26: Hey t //│ ║ ^^^^^ //│ ╟── tuple literal of type `{0: 1, 1: 2, 2: 3}` does not have field 'x' //│ ║ l.2: t = (1, 2, 3) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{x: int}` //│ ║ l.26: Hey t //│ ║ ^ //│ ╟── Note: constraint arises from record type: //│ ║ l.22: trait Hey: { x: int } //│ ╙── ^^^^^^^^^^ //│ res: error | (1, 2, 3,) & #Hey //│ = [ 1, 2, 3 ] htx = Hey tx //│ htx: (1, 2, 3,) & {x: 1} & #Hey //│ = [ 1, 2, 3, x: 1 ] htx.x //│ res: 1 //│ = 1 f ((a, b, c)) = { a; b; c } //│ f: (('a, 'b, 'c,),) -> {a: 'a, b: 'b, c: 'c} //│ = [Function: f] f t //│ res: {a: 1, b: 2, c: 3} //│ = { a: 1, b: 2, c: 3 } f tx //│ res: {a: 1, b: 2, c: 3} //│ = { a: 1, b: 2, c: 3 } f htx //│ res: {a: 1, b: 2, c: 3} //│ = { a: 1, b: 2, c: 3 } f ((a, b)) = add a b //│ f: ((int, int,),) -> int //│ = [Function: f1] :e f t f tx f htx //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.71: f t //│ ║ ^^^ //│ ╟── tuple literal of type `(1, 2, 3,)` does not match type `(?a, ?b,)` //│ ║ l.2: t = (1, 2, 3) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?c, ?d,)` //│ ║ l.71: f t //│ ║ ^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.66: f ((a, b)) = add a b //│ ╙── ^^^^^^ //│ res: error | int //│ = 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.72: f tx //│ ║ ^^^^ //│ ╟── `with` extension of type `(1, 2, 3,) & {x: 1}` is not a 2-element tuple //│ ║ l.11: tx = t with { x = 1 } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?a, ?b,)` //│ ║ l.72: f tx //│ ║ ^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.66: f ((a, b)) = add a b //│ ╙── ^^^^^^ //│ res: error | int //│ = 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.73: f htx //│ ║ ^^^^^ //│ ╟── `with` extension of type `(1, 2, 3,) & {x: 1}` does not match type `(?a, ?b,) | ~#Hey` //│ ║ l.11: tx = t with { x = 1 } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?c, ?d,) | ~#Hey` //│ ║ l.73: f htx //│ ║ ^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.66: f ((a, b)) = add a b //│ ╙── ^^^^^^ //│ res: error | int //│ = 3 g arg = case arg of { Hey -> arg.x | _ -> () } //│ g: ({x: 'x} & #Hey | ~#Hey) -> (() | 'x) //│ = [Function: g] g htx g tx //│ res: 1 | () //│ = 1 //│ res: 1 | () //│ = 1 // TODO: pattern match for traits in JavaScript g arg = case arg of { Hey -> arg.x | _ -> arg.y } //│ g: ({y: 'x} & ~#Hey | {x: 'x} & #Hey) -> 'x //│ = [Function: g1] g htx g (tx with { y = 2 }) //│ res: 1 //│ = 1 //│ res: 1 | 2 //│ = 2 :e g tx //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.142: g tx //│ ║ ^^^^ //│ ╟── expression of type `(1, 2, 3,) & {x: 1} & ~#Hey` does not have field 'y' //│ ╟── Note: constraint arises from field selection: //│ ║ l.130: g arg = case arg of { Hey -> arg.x | _ -> arg.y } //│ ╙── ^^^^^ //│ res: 1 | error //│ = 1 :ge if bool then (1,) else (2,) //│ res: (1 | 2,) //│ Code generation encountered an error: //│ type alias bool is not a valid expression :ge if bool then (1,) else (2, 3) //│ res: Array[1 | 2 | 3] & {0: 1 | 2} //│ Code generation encountered an error: //│ type alias bool is not a valid expression :ge if bool then (1,) with { a = 1; b = 2 } else (2, 3) with { b = 3; c = 4 } //│ res: Array[1 | 2 | 3] & {0: 1 | 2, b: 2 | 3} //│ Code generation encountered an error: //│ type alias bool is not a valid expression :ge if bool then (1,) else fun x -> x //│ res: anything //│ Code generation encountered an error: //│ type alias bool is not a valid expression t.0 t.1 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 t = (1, 2, 3) with {x = 1} // t = (1, 2, 3) //│ t: (1, 2, 3,) & {x: 1} //│ = [ 1, 2, 3, x: 1 ] t.x //│ res: 1 //│ = 1 t.0 t.1 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 t = (1, 2, 3) with {0 = "oops"} //│ t: {0: "oops", 1: 2, 2: 3} //│ = [ 'oops', 2, 3 ] // TODO (https://github.com/hkust-taco/mlscript/issues/69) :e (t: ("oops",int,int,)).0 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.210: (t: ("oops",int,int,)).0 //│ ║ ^ //│ ╟── `with` extension of type `{0: "oops", 1: 2, 2: 3}` is not a 3-element tuple //│ ║ l.204: t = (1, 2, 3) with {0 = "oops"} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `("oops", int, int,)` //│ ║ l.210: (t: ("oops",int,int,)).0 //│ ║ ^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.210: (t: ("oops",int,int,)).0 //│ ╙── ^^^^^^^^^^^^^^^^^ //│ res: "oops" //│ = 'oops' def f: (1, 2, 3) & (2, 2 | 3, 3 | 4) //│ f: (nothing, 2, 3,) //│ = def f: (1, 2, 3) & (2, 3) //│ f: nothing //│ = def f: (1, 2, 3) & Array[2 | 3 | 4] //│ f: (nothing, 2, 3,) //│ = a = (if true then (0, 1) else (2, 1, 2))[1] //│ a: 0 | 1 | 2 | undefined //│ = 1 case a of undefined -> 0, _ -> a //│ res: 0 | 1 | 2 //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/Match1.mls ================================================ 1 //│ res: 1 //│ = 1 :e case 1 of { } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.7: case 1 of { } //│ ║ ^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `nothing` //│ ║ l.7: case 1 of { } //│ ╙── ^ //│ res: nothing //│ Runtime error: //│ Error: non-exhaustive case expression :e :ge case 1 of { A -> 0 } //│ ╔══[ERROR] type identifier not found: A //│ ║ l.20: case 1 of { A -> 0 } //│ ╙── ^ //│ res: error //│ Code generation encountered an error: //│ unknown match case: A :e :ge case 1 of { A -> 0 | B -> 1 } //│ ╔══[ERROR] type identifier not found: A //│ ║ l.30: case 1 of { A -> 0 | B -> 1 } //│ ╙── ^ //│ res: error //│ Code generation encountered an error: //│ unknown match case: B :e :ge def f = fun x -> case x of { Foo -> 0 | Bar -> 1 } //│ ╔══[ERROR] type identifier not found: Foo //│ ║ l.40: def f = fun x -> case x of { Foo -> 0 | Bar -> 1 } //│ ╙── ^^^ //│ f: error -> error //│ Code generation encountered an error: //│ unknown match case: Bar :e :ge def f = fun x -> case x of { Foo -> 0 | Bar -> 1 } //│ ╔══[ERROR] type identifier not found: Foo //│ ║ l.51: { Foo -> 0 //│ ╙── ^^^ //│ f: error -> error //│ Code generation encountered an error: //│ unknown match case: Bar :e :ge def f = fun x -> case x of { Foo -> x | Bar -> x } //│ ╔══[ERROR] type identifier not found: Foo //│ ║ l.63: def f = fun x -> case x of { Foo -> x | Bar -> x } //│ ╙── ^^^ //│ f: error -> error //│ Code generation encountered an error: //│ unknown match case: Bar ================================================ FILE: shared/src/test/diff/mlscript/Match2.mls ================================================ class Test: { value: int } //│ Defined class Test def foo x = case x of { Test -> x.value | _ -> 1 } //│ foo: ((Test with {value: 'value}) | ~Test) -> (1 | 'value) //│ = [Function: foo] // Q: why type of `value` widened? t = Test { value = 0 } //│ t: Test & {value: 0} //│ = Test { value: 0 } foo Test //│ res: 1 //│ = 1 foo t //│ res: 0 | 1 //│ = 0 class Toast: { name: string } //│ Defined class Toast def bar x = case x of { Test -> x.value | Toast -> x.name } //│ bar: ((Test with {value: 'value}) | (Toast with {name: 'value})) -> 'value //│ = [Function: bar] :e bar Test //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.37: bar Test //│ ║ ^^^^^^^^ //│ ╟── reference of type `{value: ?value} -> (Test & {value: ?value})` does not match type `Test & ?a | Toast & ?b` //│ ║ l.37: bar Test //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.29: def bar x = case x of //│ ║ ^ //│ ╟── Note: class constructor Test is defined at: //│ ║ l.2: class Test: { value: int } //│ ╙── ^^^^ //│ res: error //│ Runtime error: //│ Error: non-exhaustive case expression :e bar "ops" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.55: bar "ops" //│ ║ ^^^^^^^^^ //│ ╟── string literal of type `"ops"` does not match type `Test & ?a | Toast & ?b` //│ ║ l.55: bar "ops" //│ ║ ^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.29: def bar x = case x of //│ ╙── ^ //│ res: error //│ Runtime error: //│ Error: non-exhaustive case expression def baz x = case x of { Test -> x | Toast -> x } //│ baz: ('a & (Test | Toast)) -> 'a //│ = [Function: baz] :e baz "oops" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.78: baz "oops" //│ ║ ^^^^^^^^^^ //│ ╟── string literal of type `"oops"` does not match type `Test & ?a | Toast & ?b` //│ ║ l.78: baz "oops" //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.70: def baz x = case x of //│ ╙── ^ //│ res: error //│ Runtime error: //│ Error: non-exhaustive case expression ================================================ FILE: shared/src/test/diff/mlscript/Match3.mls ================================================ class N class M //│ Defined class N //│ Defined class M def eval_expr v = case v of { | M -> let tmp = v.l in v | N -> v } //│ eval_expr: (M & {l: anything} & 'a | N & 'a) -> 'a //│ = [Function: eval_expr] eval_expr (N {}) //│ res: N //│ = N {} :e eval_expr (M {}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.20: eval_expr (M {}) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── application of type `M` does not have field 'l' //│ ║ l.20: eval_expr (M {}) //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.9: | M -> let tmp = v.l in v //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.8: case v of { //│ ║ ^ //│ ╟── Note: class M is defined at: //│ ║ l.3: class M //│ ╙── ^ //│ res: M | error //│ = M {} def eval_expr v = case v of { | M -> let tmp = v.l in tmp | N -> v } //│ eval_expr: (M & {l: 'l} | N & 'l) -> 'l //│ = [Function: eval_expr1] eval_expr (N {}) //│ res: N //│ = N {} :e eval_expr (M {}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.52: eval_expr (M {}) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── application of type `M` does not have field 'l' //│ ║ l.52: eval_expr (M {}) //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.41: | M -> let tmp = v.l in tmp //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.40: case v of { //│ ║ ^ //│ ╟── Note: class M is defined at: //│ ║ l.3: class M //│ ╙── ^ //│ res: error //│ = undefined ================================================ FILE: shared/src/test/diff/mlscript/MatchBool.mls ================================================ def absImpl lt x = case lt of { true -> x | false -> 0 - x } //│ absImpl: Bool -> int -> int //│ = [Function: absImpl] // * TODO support this :e def abs x = let r = x < 0 in absImpl r x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.12: let r = x < 0 in absImpl r x //│ ║ ^^^^^^^^^ //│ ╟── operator application of type `bool` does not match type `false & ?a | true & ?b` //│ ║ l.12: let r = x < 0 in absImpl r x //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `false & ?a | true & ?b` //│ ║ l.12: let r = x < 0 in absImpl r x //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.3: case lt of //│ ╙── ^^ //│ abs: int -> (error | int) //│ = [Function: abs] def neg b = case b of { true -> false | false -> true } //│ neg: Bool -> Bool //│ = [Function: neg] ================================================ FILE: shared/src/test/diff/mlscript/MethodAndMatches.mls ================================================ :w class Base1[A] method M1: A -> Base1[A] class Derived1: Base1[int] & { x: int } method M1 y = Derived1 { x = add this.x y } method M2 = Derived1 { x = add this.x 1 } class Derived2[C, D]: Base1[{ c: C; d: D }] // method M1 { c, d } = Derived2 { c; d } method M1 r = Derived2 { c = r.c; d = r.d } class Derived3[C, D]: Base1[{ c: C; d: D }] & { c: C; d: D } method M1 r = Derived3 { c = r.c; d = r.d } type Type1[A] = Derived1 | Derived3['a, 'b] //│ Defined class Base1[-A] //│ Declared Base1.M1: Base1['A] -> 'A -> Base1['A] //│ Defined class Derived1 //│ Defined Derived1.M1: Derived1 -> int -> Derived1 //│ Defined Derived1.M2: Derived1 -> Derived1 //│ Defined class Derived2[-C, -D] //│ Defined Derived2.M1: Derived2[?, ?] -> {c: anything, d: anything} -> Derived2[anything, anything] //│ Defined class Derived3[=C, =D] //│ Defined Derived3.M1: Derived3['C, 'D] -> {c: 'c & 'C0, d: 'd & 'D0} -> (Derived3['C0, 'D0] with {c: 'c, d: 'd}) //│ Defined type alias Type1[±A] //│ ╔══[WARNING] Type definition Type1 has bivariant type parameters: //│ ║ l.12: type Type1[A] = Derived1 | Derived3['a, 'b] //│ ║ ^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.12: type Type1[A] = Derived1 | Derived3['a, 'b] //│ ╙── ^ // class Derived2[C, D]: Base1[C -> D] def foo b x = case b of { | Derived1 -> b.M2 | Base1 -> b.M1 x } //│ foo: (Base1['A] & ~#Derived1 | Derived1) -> 'A -> (Base1['A] | Derived1) //│ = [Function: foo] d1 = Derived1 { x = 1 } //│ d1: Derived1 & {x: 1} //│ = Derived1 { x: 1 } foo d1 foo d1 0 //│ res: anything -> (Base1[anything] | Derived1) //│ = [Function (anonymous)] //│ res: Base1[anything] | Derived1 //│ = Derived1 { x: 2 } def bar0: Base1[int] -> int -> Base1[int] bar0 d1 bar0 d1 0 //│ bar0: Base1[int] -> int -> Base1[int] //│ = //│ res: int -> Base1[int] //│ = //│ bar0 is not implemented //│ res: Base1[int] //│ = //│ bar0 is not implemented // This is ill-typed because type `Base1[int]` includes things like `Derived1\x` (a `Derived` instance without an `x` field) :e def bar0 = foo //│ (Base1['A] & ~#Derived1 | Derived1) -> 'A -> (Base1['A] | Derived1) //│ <: bar0: //│ Base1[int] -> int -> Base1[int] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.63: def bar0 = foo //│ ║ ^^^^^^^^^^^^^^ //│ ╟── expression of type `Base1[int] & ~?a | Derived1` does not have field 'x' //│ ╟── Note: constraint arises from record type: //│ ║ l.4: class Derived1: Base1[int] & { x: int } //│ ║ ^^^^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.31: def foo b x = case b of { //│ ╙── ^ //│ = [Function: bar0] def bar1: Type1[int] -> int -> Type1[int] bar1 d1 bar1 d1 0 //│ bar1: Type1[?] -> int -> Type1[?] //│ = //│ res: int -> Type1[?] //│ = //│ bar1 is not implemented //│ res: Type1[?] //│ = //│ bar1 is not implemented :e def bar1 = foo //│ (Base1['A] & ~#Derived1 | Derived1) -> 'A -> (Base1['A] | Derived1) //│ <: bar1: //│ Type1[?] -> int -> Type1[?] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.92: def bar1 = foo //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `int` is not a record (expected a record with fields: c, d) //│ ║ l.79: def bar1: Type1[int] -> int -> Type1[int] //│ ║ ^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.10: class Derived3[C, D]: Base1[{ c: C; d: D }] & { c: C; d: D } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.33: | Base1 -> b.M1 x //│ ╙── ^ //│ = [Function: bar1] def bar2: Base1['a] -> 'a -> Base1['a] bar2 d1 bar2 d1 0 //│ bar2: Base1['a] -> 'a -> Base1['a] //│ = //│ res: int -> Base1[int] //│ = //│ bar2 is not implemented //│ res: Base1[int] //│ = //│ bar2 is not implemented // Note that typing this would require GADT reasoning! Though it also suffers from the previous "Derived1 with missing x field" problem. :e def bar2 = foo //│ (Base1['A] & ~#Derived1 | Derived1) -> 'A -> (Base1['A] | Derived1) //│ <: bar2: //│ Base1['a] -> 'a -> Base1['a] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.124: def bar2 = foo //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `int` does not match type `'a` //│ ║ l.4: class Derived1: Base1[int] & { x: int } //│ ║ ^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.110: def bar2: Base1['a] -> 'a -> Base1['a] //│ ╙── ^^ //│ = [Function: bar2] ================================================ FILE: shared/src/test/diff/mlscript/Methods.mls ================================================ class Foo[A, B]: { x: A; y: B } method Fun[C]: (A -> B -> C) -> (A -> B -> C) method Fun[C, D] (f: C -> D) = f //│ Defined class Foo[=A, =B] //│ Declared Foo.Fun: Foo['A, 'B] -> ('A -> 'B -> 'C) -> 'A -> 'B -> 'C //│ Defined Foo.Fun: Foo['A, 'B] -> ('C -> 'D) -> 'C -> 'D class Bar: Foo[int, bool] method Fun f = f //│ Defined class Bar //│ Defined Bar.Fun: Bar -> 'a -> 'a Foo.Fun Bar.Fun //│ res: Foo['A, 'B] -> ('A -> 'B -> 'C) -> 'A -> 'B -> 'C //│ = undefined //│ res: Bar -> (forall 'a. 'a -> 'a) //│ = undefined f = Foo { x = 1; y = 2 } //│ f: Foo['A, 'B] & {x: 1, y: 2} //│ where //│ 'B :> 2 //│ 'A :> 1 //│ = Foo { x: 1, y: 2 } f.(Foo.Fun) //│ res: ((1 | 'A) -> (2 | 'B) -> 'C) -> 'A -> 'B -> 'C //│ = undefined :re Foo.Fun f //│ res: ((1 | 'A) -> (2 | 'B) -> 'C) -> 'A -> 'B -> 'C //│ Runtime error: //│ TypeError: (intermediate value).Fun is not a function f.Fun //│ res: ((1 | 'A) -> (2 | 'B) -> 'C) -> 'A -> 'B -> 'C //│ = [Function: Fun] // :d Bar //│ res: {x: int & 'x, y: bool & 'y} -> (Bar with {x: 'x, y: 'y}) //│ = [Function: res] g = Bar { x = 42; y = true } //│ g: Bar & {x: 42, y: true} //│ = Bar { x: 42, y: true } g.(Foo.Fun) g.(Bar.Fun) //│ res: (int -> bool -> 'C) -> int -> bool -> 'C //│ = undefined //│ res: 'a -> 'a //│ = undefined :re Foo.Fun g Bar.Fun g //│ res: (int -> bool -> 'C) -> int -> bool -> 'C //│ Runtime error: //│ TypeError: (intermediate value).Fun is not a function //│ res: 'a -> 'a //│ Runtime error: //│ TypeError: (intermediate value).Fun is not a function g.Fun //│ res: (int -> bool -> 'C) -> int -> bool -> 'C //│ = [Function: Fun] h = Bar { x = 43; y = true } h.Fun //│ h: Bar & {x: 43, y: true} //│ = Bar { x: 43, y: true } //│ res: (int -> bool -> 'C) -> int -> bool -> 'C //│ = [Function: Fun] class Wrapper[A]: { x: A } method Apply f = Wrapper { x = f this.x } method Apply[B]: (A -> B) -> Wrapper[B] // method Apply f = Wrapper { x = f this.x } //│ Defined class Wrapper[+A] //│ Declared Wrapper.Apply: Wrapper['A] -> ('A -> 'B) -> Wrapper['B] //│ Defined Wrapper.Apply: Wrapper['A] -> ('A -> 'x) -> Wrapper['x] class IntWrapper: Wrapper[int] method Apply f = Wrapper { x = f this.x } //│ Defined class IntWrapper //│ Defined IntWrapper.Apply: IntWrapper -> (int -> 'x) -> Wrapper['x] class Psyduck[B]: Wrapper[B] method Apply[A]: (B -> A) -> Psyduck[A] method Apply f = Psyduck { x = f this.x } //│ Defined class Psyduck[+B] //│ Declared Psyduck.Apply: Psyduck['B] -> ('B -> 'A) -> Psyduck['A] //│ Defined Psyduck.Apply: Psyduck['B] -> ('B -> 'x) -> Psyduck['x] class WrapperWrapper[A]: Wrapper[Wrapper[A]] method Apply2[B]: (A -> B) -> WrapperWrapper[B] method Apply2 f = WrapperWrapper { x = this.x.Apply f } //│ Defined class WrapperWrapper[+A] //│ Declared WrapperWrapper.Apply2: WrapperWrapper['A] -> ('A -> 'B) -> WrapperWrapper['B] //│ Defined WrapperWrapper.Apply2: WrapperWrapper['A] -> ('A -> 'B) -> WrapperWrapper['B] WrapperWrapper { x = Psyduck { x = 0 } } //│ res: WrapperWrapper[0] with {x: Psyduck[0]} //│ = WrapperWrapper { x: Psyduck { x: 0 } } res.Apply2 (fun x -> mul x 2) //│ res: WrapperWrapper[int] //│ = WrapperWrapper { x: Psyduck { x: 0 } } Wrapper //│ res: {x: 'x} -> Wrapper['x] //│ = [Function: res] class Asc[A, B]: { x: A; y: B } method Left = { x = this.x; y = this.y } : { x: A } method Right2 = this : { y: B } //│ Defined class Asc[+A, +B] //│ Defined Asc.Left: Asc['A, ?] -> {x: 'A} //│ Defined Asc.Right2: Asc[?, 'B] -> {y: 'B} class Simple[A] method Get: A //│ Defined class Simple[+A] //│ Declared Simple.Get: Simple['A] -> 'A class Simple2[A]: { a: A } method Get: A //│ Defined class Simple2[+A] //│ Declared Simple2.Get: Simple2['A] -> 'A class AbstractPair[A, B]: { x: A; y: B } method Test: (A -> B -> bool) -> bool method Map[C, D]: (A -> C) -> (B -> D) -> AbstractPair[C, D] //│ Defined class AbstractPair[+A, +B] //│ Declared AbstractPair.Test: AbstractPair['A, 'B] -> ('A -> 'B -> bool) -> bool //│ Declared AbstractPair.Map: AbstractPair['A, 'B] -> ('A -> 'C) -> ('B -> 'D) -> AbstractPair['C, 'D] class Pair[A, B]: AbstractPair[A, B] method Test(f: A -> B -> bool) = f this.x this.y method Map fx fy = Pair { x = fx this.x; y = fy this.y } //│ Defined class Pair[+A, +B] //│ Defined Pair.Test: Pair['A, 'B] -> ('A -> 'B -> bool) -> bool //│ Defined Pair.Map: Pair['A, 'B] -> ('A -> 'x) -> ('B -> 'y) -> Pair['x, 'y] class Tru[A, B]: Pair[A, B] method Test f = true method True = this.Test (fun x -> error) //│ Defined class Tru[+A, +B] //│ Defined Tru.Test: Tru[?, ?] -> anything -> true //│ Defined Tru.True: Tru[?, ?] -> true class True2[A, B]: Pair[A, B] method Test: anything -> bool method True = this.Test (fun x -> error) method Test f = true //│ Defined class True2[+A, +B] //│ Declared True2.Test: True2[?, ?] -> anything -> bool //│ Defined True2.True: True2[?, ?] -> bool //│ Defined True2.Test: True2[?, ?] -> anything -> true p = Pair { x = 42; y = true } fx = fun x -> mul x 2 fy = fun x -> not x ft = fun x -> fun y -> if (y) then gt x 0 else y //│ p: Pair[42, true] //│ = Pair { x: 42, y: true } //│ fx: int -> int //│ = [Function: fx] //│ fy: bool -> bool //│ = [Function: fy] //│ ft: number -> bool -> bool //│ = [Function: ft] p.Map fx fy p.Test ft (p.Map fx fy).Test ft //│ res: AbstractPair[int, bool] //│ = Pair { x: 84, y: false } //│ res: bool //│ = true //│ res: bool //│ = false t = Tru { x = "foo"; y = false } t.(Tru.True) //│ t: Tru["foo", false] //│ = Tru { x: 'foo', y: false } //│ res: true //│ = undefined t = True2 { x = "bar"; y = false } t.(True2.True) //│ t: True2["bar", false] //│ = True2 { x: 'bar', y: false } //│ res: bool //│ = undefined class Class1A[A] method MtdA: A trait Trait1A[B] method MtdA: B class Class1B: Class1A["a" | "b"] & Trait1A["b" | "c"] method MtdA = "b" //│ Defined class Class1A[+A] //│ Declared Class1A.MtdA: Class1A['A] -> 'A //│ Defined trait Trait1A[+B] //│ Declared Trait1A.MtdA: Trait1A['B] -> 'B //│ Defined class Class1B //│ Defined Class1B.MtdA: Class1B -> "b" trait Trait2A[A] method MtdB: A class Class2B[A, B]: Class1A[A] & Trait2A[B] method MtdA: A method MtdB: B class Class2C: Class2B[int, bool] method MtdA = 42 method MtdB = true //│ Defined trait Trait2A[+A] //│ Declared Trait2A.MtdB: Trait2A['A] -> 'A //│ Defined class Class2B[+A, +B] //│ Declared Class2B.MtdA: Class2B['A, ?] -> 'A //│ Declared Class2B.MtdB: Class2B[?, 'B] -> 'B //│ Defined class Class2C //│ Defined Class2C.MtdA: Class2C -> 42 //│ Defined Class2C.MtdB: Class2C -> true :e class Class3C: Class2C method MtdA = 42 method MtdB = 42 //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.250: method MtdB = 42 //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `42` is not an instance of type `bool` //│ ║ l.250: method MtdB = 42 //│ ║ ^^ //│ ╟── but it flows into method definition with expected type `bool` //│ ║ l.250: method MtdB = 42 //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.234: class Class2C: Class2B[int, bool] //│ ║ ^^^^ //│ ╟── from inherited method declaration: //│ ║ l.233: method MtdB: B //│ ╙── ^^^^^^^ //│ Defined class Class3C //│ Defined Class3C.MtdA: Class3C -> 42 //│ Defined Class3C.MtdB: Class3C -> 42 type Id[T] = T class Test1[A]: { x: A } method F: A class Test2[B]: Test1[Id[B]] & { x: B } method F = this.x //│ Defined type alias Id[+T] //│ Defined class Test1[+A] //│ Declared Test1.F: Test1['A] -> 'A //│ Defined class Test2[+B] //│ Defined Test2.F: Test2['B] -> (Id['B] & 'B) :e class Test3A: Test1['a -> 'a] Test3A.F //│ ╔══[ERROR] cannot inherit from a polymorphic type //│ ║ l.283: class Test3A: Test1['a -> 'a] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: Test3A //│ ║ l.284: Test3A.F //│ ╙── ^^^^^^ //│ res: nothing //│ = undefined class Test3A: Test1[forall 'a. 'a -> 'a] Test3A.F //│ Defined class Test3A //│ res: Test3A -> (forall 'a. 'a -> 'a) //│ = undefined :e class Test3B: Test1['a -> 'a] & { x: 'a } Test3B.F //│ ╔══[ERROR] cannot inherit from a polymorphic type //│ ║ l.301: class Test3B: Test1['a -> 'a] & { x: 'a } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: Test3B //│ ║ l.302: Test3B.F //│ ╙── ^^^^^^ //│ res: nothing //│ = undefined :w class Test4A[A]: { x: A } method Mth4A[A]: A //│ ╔══[WARNING] Method type parameter A //│ ║ l.315: class Test4A[A]: { x: A } //│ ║ ^ //│ ╟── shadows class type parameter A //│ ║ l.316: method Mth4A[A]: A //│ ╙── ^ //│ Defined class Test4A[+A] //│ Declared Test4A.Mth4A: Test4A[?] -> nothing class Test[A]: { x: A } method Mth[B]: (A -> B) -> B method Mth[B] (f: A -> B) = f this.x //│ Defined class Test[+A] //│ Declared Test.Mth: Test['A] -> ('A -> 'B) -> 'B //│ Defined Test.Mth: Test['A] -> ('A -> 'B) -> 'B trait D method G = 1 trait E method G: int class H: D & E //│ Defined trait D //│ Defined D.G: D -> 1 //│ Defined trait E //│ Declared E.G: E -> int //│ Defined class H ((H{}): D).(D.G) //│ res: 1 //│ = undefined class Fee method F: 1 | 2 //│ Defined class Fee //│ Declared Fee.F: Fee -> (1 | 2) trait Fee2 method F: 2 | 3 //│ Defined trait Fee2 //│ Declared Fee2.F: Fee2 -> (2 | 3) class Ber: Fee & Fee2 //│ Defined class Ber Ber.F //│ res: Ber -> 2 //│ = undefined :e Ber //│ ╔══[ERROR] Instantiation of an abstract type is forbidden //│ ║ l.371: Ber //│ ║ ^^^ //│ ╟── Note that class Ber is abstract: //│ ║ l.363: class Ber: Fee & Fee2 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Hint: method F is abstract //│ ║ l.363: class Ber: Fee & Fee2 //│ ╙── ^^^^^^^^^^^^^^^ //│ res: error //│ = [Function: res] class Test5A[A]: { a: A } method Mth5A[B]: (A -> B) -> B //│ Defined class Test5A[+A] //│ Declared Test5A.Mth5A: Test5A['A] -> ('A -> 'B) -> 'B trait Test5B: { tag: int } method Mth5B = this.tag //│ Defined trait Test5B //│ Defined Test5B.Mth5B: Test5B -> int class Test5C[A]: Test5A[A] & Test5B method Mth5A f = case this.Mth5B of { 0 -> f this.a | _ -> f this.a } //│ Defined class Test5C[+A] //│ Defined Test5C.Mth5A: Test5C['A] -> ('A -> 'a) -> 'a class Test5D: Test5A[int] & Test5B method Mth5A f = case this.Mth5B of { 0 -> f this.a | _ -> f this.Mth5B } //│ Defined class Test5D //│ Defined Test5D.Mth5A: Test5D -> (int -> 'a) -> 'a class Test6A: { a: int } method Add (that: Test6A) = Test6A { a = this.a + that.a } //│ Defined class Test6A //│ Defined Test6A.Add: Test6A -> Test6A -> Test6A class Test6B: { a: Test6A; b: Test6A } method Add (that: Test6B) = Test6B { a = this.a.(Test6A.Add) that.a; b = this.b.(Test6A.Add) that.b } //│ Defined class Test6B //│ Defined Test6B.Add: Test6B -> Test6B -> Test6B class Test6C: Test6B //│ Defined class Test6C trait Test7A method Mth7A: int method Mth7A = 0 //│ Defined trait Test7A //│ Declared Test7A.Mth7A: Test7A -> int //│ Defined Test7A.Mth7A: Test7A -> 0 trait Test7B method Mth7A: int //│ Defined trait Test7B //│ Declared Test7B.Mth7A: Test7B -> int class Test7C: Test7A & Test7B method Mth7A = 42 //│ Defined class Test7C //│ Defined Test7C.Mth7A: Test7C -> 42 ================================================ FILE: shared/src/test/diff/mlscript/Methods2.mls ================================================ class Option[A] method Get: A method Destruct: Some[A] | None[A] class Some[A]: Option[A] & { payload: A } method Get = this.payload method Destruct = this class None[A]: Option[A] method Get = error method Destruct = this //│ Defined class Option[+A] //│ Declared Option.Get: Option['A] -> 'A //│ Declared Option.Destruct: Option['A] -> (None['A] | Some['A]) //│ Defined class Some[+A] //│ Defined Some.Get: Some['A] -> 'A //│ Defined Some.Destruct: (Some['A] & 'this) -> (Some['A] & 'this) //│ Defined class None[+A] //│ Defined None.Get: None[?] -> nothing //│ Defined None.Destruct: (None['A] & 'this) -> (None['A] & 'this) class List[A] method Size: int method HeadOption: Option[A] method Map[B]: (A -> B) -> List[B] //│ Defined class List[+A] //│ Declared List.Size: List[?] -> int //│ Declared List.HeadOption: List['A] -> Option['A] //│ Declared List.Map: List['A] -> ('A -> 'B) -> List['B] :DistributeForalls // * Note: this used to fail when `subst` did not hygienically substitute under polymorphic types class Nil_oops[A]: List[A] method Size = 0 method HeadOption = None {} method Map f = Nil_oops {} //│ Defined class Nil_oops[+A] //│ Defined Nil_oops.Size: Nil_oops[?] -> 0 //│ Defined Nil_oops.HeadOption: Nil_oops[?] -> None[nothing] //│ Defined Nil_oops.Map: Nil_oops[?] -> anything -> Nil_oops[nothing] // * Works without distrib: :DontDistributeForalls class Nil[A]: List[A] method Size = 0 method HeadOption = None {} method Map f = Nil {} //│ Defined class Nil[+A] //│ Defined Nil.Size: Nil[?] -> 0 //│ Defined Nil.HeadOption: Nil[?] -> None[nothing] //│ Defined Nil.Map: Nil[?] -> anything -> Nil[nothing] // * Note: `forall 'B` not distributed out in `Cons.Map` because for some weird reason // * the `this` TV has level 0, though it is seemingly refreshed on every use... class Cons[A]: List[A] & { head: A; tail: List[A] } method Size = succ this.tail.Size method HeadOption = Some { payload = this.head } method Map[B] f = Cons { head = f this.head; tail = this.tail.Map f } //│ Defined class Cons[+A] //│ Defined Cons.Size: Cons[?] -> int //│ Defined Cons.HeadOption: Cons['A] -> Some['A] //│ Defined Cons.Map: Cons['A] -> (forall 'head. ('A -> 'head) -> (Cons['head] with {head: 'head, tail: List['head]})) // Note that the useless `with {tail: List['A]}` refinement is kept // because the approximate subtyping check `list & {List#A = 'A} <: List['A]` currently returns false // as we do not try to expand type aliases right now. Cons.Size //│ res: Cons[?] -> int //│ = undefined List.HeadOption //│ res: List['A] -> Option['A] //│ = undefined Cons.HeadOption //│ res: Cons['A] -> Some['A] //│ = undefined l = Cons { head = 0; tail = Cons { head = 1; tail = Nil {} } } //│ l: Cons[0 | 1] with {head: 0, tail: Cons[1] with {tail: Nil[nothing]}} //│ = Cons { head: 0, tail: Cons { head: 1, tail: Nil {} } } l.Size //│ res: int //│ = 2 :stats l.Map (fun x -> mul x 2) //│ res: List[int] //│ = Cons { head: 0, tail: Cons { head: 2, tail: Nil {} } } //│ constrain calls : 56 //│ annoying calls : 6 //│ subtyping calls : 156 l0 = Cons { head = 0; tail = Nil {} } //│ l0: Cons[0] with {tail: Nil[nothing]} //│ = Cons { head: 0, tail: Nil {} } :re :stats Cons.HeadOption l0 //│ res: Some[0] //│ Runtime error: //│ TypeError: (intermediate value).HeadOption is not a function //│ constrain calls : 51 //│ annoying calls : 31 //│ subtyping calls : 169 l1 = Cons { head = 0; tail = Nil {} } //│ l1: Cons[0] with {tail: Nil[nothing]} //│ = Cons { head: 0, tail: Nil {} } :re :stats Cons.HeadOption l1 //│ res: Some[0] //│ Runtime error: //│ TypeError: (intermediate value).HeadOption is not a function //│ constrain calls : 51 //│ annoying calls : 31 //│ subtyping calls : 169 :re :stats Cons.HeadOption l //│ res: Some[0 | 1] //│ Runtime error: //│ TypeError: (intermediate value).HeadOption is not a function //│ constrain calls : 57 //│ annoying calls : 31 //│ subtyping calls : 195 :stats o = l.(Cons.HeadOption) //│ o: Some[0 | 1] //│ = undefined //│ constrain calls : 55 //│ annoying calls : 31 //│ subtyping calls : 182 o = l.(Cons.HeadOption) //│ o: Some[0 | 1] //│ = undefined o = l.HeadOption //│ o: Option[0 | 1] //│ = Some { payload: 0 } :e case o of {None -> 0 | Some -> o.payload} //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.150: case o of {None -> 0 | Some -> o.payload} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Option[?A]` does not match type `None[?] & ?a | Some[?] & ?b` //│ ║ l.24: method HeadOption: Option[A] //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `None[?] & ?a | Some[?] & ?c` //│ ║ l.150: case o of {None -> 0 | Some -> o.payload} //│ ╙── ^ //│ res: 0 //│ = 0 v = o.Destruct case v of {None -> 0 | Some -> v.payload} //│ v: None[0 | 1] | Some[0 | 1] //│ = Some { payload: 0 } //│ res: 0 | 1 //│ = 0 def newHeadOption x = case x of { | Cons -> Some {payload = x.head} | _ -> None{} } //│ newHeadOption: (Cons[?] & {head: 'payload} | ~Cons[?]) -> (None[nothing] | Some['payload]) //│ = [Function: newHeadOption] // Note that `o` is not a list, so this takes the default case: newHeadOption o //│ res: None[nothing] | Some[nothing] //│ = None {} newHeadOption l //│ res: None[nothing] | Some[0] //│ = Some { payload: 0 } case res of {None -> 0 | Some -> res.payload} //│ res: 0 //│ = 0 // Idiomatic definition for proper matching: `type Option[A] = Some[A] | None[A]` :e case o of { Some -> o.Get | None -> 0 } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.194: case o of { //│ ║ ^^^^^^^^^^^ //│ ║ l.195: Some -> o.Get | None -> 0 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Option[?A]` does not match type `None[?] & ?a | Some[?] & ?b` //│ ║ l.24: method HeadOption: Option[A] //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `None[?] & ?a | Some[?] & ?c` //│ ║ l.194: case o of { //│ ╙── ^ //│ res: 0 | 1 //│ = 0 case o of { Some -> o.Get | None -> 0 | Option -> error } //│ res: 0 | 1 //│ = 0 ================================================ FILE: shared/src/test/diff/mlscript/Misc.mls ================================================ :NoJS rec def x = {u = x} //│ x: 'x //│ where //│ 'x :> {u: 'x} :ns {u = x; v = x} //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x := {u: 'x} {u = x; v = x} //│ res: {u: forall 'x. 'x, v: forall 'x. 'x} //│ where //│ 'x :> {u: 'x} class C[A]: {x: A} method X = this.x //│ Defined class C[+A] //│ Defined C.X: C['A] -> 'A c = C{x = 1} //│ c: C[1] def mkC() = C{x = 1} //│ mkC: () -> C[1] def arg = if true then c else 0 //│ arg: 0 | C[1] def arg = if true then mkC() else 0 //│ arg: 0 | C[1] def arg = if true then C{x = 42} else 0 //│ arg: 0 | C[42] def arg = if true then c else 0 //│ arg: 0 | C[1] :ns def arg = if true then C{x = 42} else C{x = 1} //│ arg: forall 'x 'A 'x0 'A0 'a. 'a //│ where //│ 'a :> #C & {x: 'x, C#A = 'A} | #C & {x: 'x0, C#A = 'A0} //│ 'x0 :> 1 //│ <: 'A0 //│ 'A0 :> 1 //│ 'x :> 42 //│ <: 'A //│ 'A :> 42 :ns arg //│ res: forall 'x 'A 'x0 'A0 'a. 'a //│ where //│ 'a :> #C & {x: 'x, C#A = 'A} | #C & {x: 'x0, C#A = 'A0} //│ 'x0 :> 1 //│ <: 'A0 //│ 'A0 :> 1 //│ 'x :> 42 //│ <: 'A //│ 'A :> 42 arg //│ res: C[1 | 42] C.X //│ res: C['A] -> 'A :ns C.X //│ res: 'this -> 'x //│ where //│ 'x :> 'A //│ 'this <: C['A] res arg //│ res: 1 | 42 arg.X //│ res: 1 | 42 :e arg: C[nothing] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.91: arg: C[nothing] //│ ║ ^^^ //│ ╟── integer literal of type `42` does not match type `nothing` //│ ║ l.48: def arg = if true then C{x = 42} else C{x = 1} //│ ╙── ^^ //│ res: C[nothing] test x = case x of { C -> x.X } //│ test: C['a] -> 'a test x = case x of { C -> x.X | int -> x } //│ test: (C['a] | int & 'a) -> 'a ================================================ FILE: shared/src/test/diff/mlscript/MiscExtrusion.mls ================================================ :NoJS class Expr[A] method Inv: A -> A //│ Defined class Expr[=A] //│ Declared Expr.Inv: Expr['A] -> 'A -> 'A def asExpr: Expr['a] -> Expr['a] //│ asExpr: Expr['a] -> Expr['a] def alsoPrintSizeSimple f = let rec nested expr = f expr in nested //│ alsoPrintSizeSimple: ('a -> 'b) -> 'a -> 'b def alsoPrintSizeSimple f = let rec nested expr = f (asExpr expr) in nested //│ alsoPrintSizeSimple: (Expr['a] -> 'b) -> Expr['a] -> 'b def alsoPrintSizeSimple f = let rec nested expr = asExpr (f (asExpr expr)) in nested //│ alsoPrintSizeSimple: (Expr['a] -> Expr['a0]) -> Expr['a] -> Expr['a0] // * Note: the type of definitions like this one is much cleaner with :ConstrainedTypes def alsoPrintSizeSimple f = let rec nested expr = f (asExpr expr) nested in nested //│ alsoPrintSizeSimple: (Expr[in 'a out 'a | 'a0] -> (Expr[in 'a0 & 'a1 out 'a2] -> 'b) -> 'b) -> Expr['a2] -> 'b //│ where //│ 'a2 :> 'a | 'a1 //│ <: 'a0 class Program //│ Defined class Program def mapExpr: (forall 'a. Expr['a] -> Expr['a]) -> Program -> Program def print: Expr['a] -> unit //│ mapExpr: (forall 'a. Expr['a] -> Expr['a]) -> Program -> Program //│ print: Expr['a] -> unit def alsoPrintSize f = mapExpr(fun e -> let tmp = print e in f e) //│ alsoPrintSize: (Expr[in ??a out ??a0] -> Expr[in ??a0 out ??a]) -> Program -> Program :e alsoPrintSize id //│ ╔══[ERROR] Type error in application //│ ║ l.54: alsoPrintSize id //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.44: def mapExpr: (forall 'a. Expr['a] -> Expr['a]) -> Program -> Program //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.44: def mapExpr: (forall 'a. Expr['a] -> Expr['a]) -> Program -> Program //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.50: mapExpr(fun e -> let tmp = print e in f e) //│ ╙── ^^^ //│ res: error | Program -> Program def alsoPrintSize (f: forall 'a. Expr['a] -> Expr['a]) = mapExpr(fun e -> let tmp = print e in f e) //│ alsoPrintSize: (forall 'a. Expr['a] -> Expr['a]) -> Program -> Program alsoPrintSize id //│ res: Program -> Program class ExprCo[A] method Co: A //│ Defined class ExprCo[+A] //│ Declared ExprCo.Co: ExprCo['A] -> 'A def mapExprCo: (forall 'a. ExprCo['a] -> ExprCo['a]) -> Program -> Program def printCo: ExprCo['a] -> unit //│ mapExprCo: (forall 'a. ExprCo['a] -> ExprCo['a]) -> Program -> Program //│ printCo: ExprCo[?] -> unit def alsoPrintSizeCo f = mapExprCo(fun e -> let tmp = printCo e in f e) //│ alsoPrintSizeCo: (ExprCo[??a] -> ExprCo[??a0]) -> Program -> Program :e alsoPrintSizeCo id //│ ╔══[ERROR] Type error in application //│ ║ l.93: alsoPrintSizeCo id //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.83: def mapExprCo: (forall 'a. ExprCo['a] -> ExprCo['a]) -> Program -> Program //│ ║ ^^ //│ ╟── back into type variable `'a` //│ ║ l.83: def mapExprCo: (forall 'a. ExprCo['a] -> ExprCo['a]) -> Program -> Program //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this application: //│ ║ l.89: mapExprCo(fun e -> let tmp = printCo e in f e) //│ ╙── ^^^ //│ res: error | Program -> Program def alsoPrintSizeCo (f: forall 'a. Expr['a] -> Expr['a]) = mapExpr(fun e -> let tmp = print e in f e) //│ alsoPrintSizeCo: (forall 'a. Expr['a] -> Expr['a]) -> Program -> Program alsoPrintSizeCo id //│ res: Program -> Program ================================================ FILE: shared/src/test/diff/mlscript/Mohammad.mls ================================================ def test f = f (fun b -> if b then 1 else 2) //│ test: ((bool -> (1 | 2)) -> 'a) -> 'a //│ = [Function: test] test (fun k -> (k true, k false)) //│ res: (1 | 2, 1 | 2,) //│ = [ 1, 2 ] class Class: {x : int} method Test = 12 //│ Defined class Class //│ Defined Class.Test: Class -> 12 c = Class{x = 1} //│ c: Class & {x: 1} //│ = Class { x: 1 } :js def useCls c = case c of { Class -> c.x | int -> "oops" } //│ // Query 1 //│ globalThis.useCls = function useCls(c) { //│ let a; //│ return (a = c, a instanceof Class ? c.x : Number.isInteger(a) ? "oops" : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ }; //│ // End of generated code //│ useCls: ((Class with {x: 'x}) | int) -> ("oops" | 'x) //│ = [Function: useCls] useCls c //│ res: "oops" | 1 //│ = 1 useCls (c with { x = 2 }) //│ res: "oops" | 2 //│ = 2 :js trait T method Test2: int method Test2 = 1 //│ Defined trait T //│ Declared T.Test2: T -> int //│ Defined T.Test2: T -> 1 //│ // Prelude //│ const T = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ if (!("Test2" in instance)) { //│ Object.defineProperty(instance, "Test2", { //│ get: function () { //│ return 1; //│ } //│ }); //│ } //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code :js class D: T & {z: int} method Test2 = 2 //│ Defined class D //│ Defined D.Test2: D -> 2 //│ // Prelude //│ class D { //│ constructor(fields) { //│ T.implement(this); //│ this.z = fields.z; //│ } //│ get Test2() { //│ return 2; //│ } //│ } //│ // End of generated code def useTrt t = case t of { T -> t.y | Class -> t.x } //│ useTrt: ((Class with {x: 'y}) & ~#T | {y: 'y} & #T) -> 'y //│ = [Function: useTrt] :e useTrt c //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.101: useTrt c //│ ║ ^^^^^^^^ //│ ╟── expression of type `Class & {x: ?x} & ~?a | Class & {x: ?x} & #T` does not have field 'y' //│ ╟── Note: constraint arises from field selection: //│ ║ l.96: def useTrt t = case t of { T -> t.y | Class -> t.x } //│ ║ ^^^ //│ ╟── from refined scrutinee: //│ ║ l.96: def useTrt t = case t of { T -> t.y | Class -> t.x } //│ ╙── ^ //│ res: 1 | error //│ = 1 useTrt (c with { y = 1 }) //│ res: 1 //│ = 1 :e useTrt c //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.120: useTrt c //│ ║ ^^^^^^^^ //│ ╟── expression of type `Class & {x: ?x} & ~?a | Class & {x: ?x} & #T` does not have field 'y' //│ ╟── Note: constraint arises from field selection: //│ ║ l.96: def useTrt t = case t of { T -> t.y | Class -> t.x } //│ ║ ^^^ //│ ╟── from refined scrutinee: //│ ║ l.96: def useTrt t = case t of { T -> t.y | Class -> t.x } //│ ╙── ^ //│ res: 1 | error //│ = 1 d = D{z = 1213} //│ d: D & {z: 1213} //│ = D { z: 1213 } d: {z: 'a} //│ res: {z: 1213} //│ = D { z: 1213 } d.Test2 //│ res: int //│ = 2 trait T1 method Test3: int method Test3 = 1 method Test4 = 4 //│ Defined trait T1 //│ Declared T1.Test3: T1 -> int //│ Defined T1.Test3: T1 -> 1 //│ Defined T1.Test4: T1 -> 4 :js trait T2: T1 method Test3: int method Test3 = 2 //│ Defined trait T2 //│ Declared T2.Test3: T2 -> int //│ Defined T2.Test3: T2 -> 2 //│ // Prelude //│ const T2 = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ if (!("Test3" in instance)) { //│ Object.defineProperty(instance, "Test3", { //│ get: function () { //│ return 2; //│ } //│ }); //│ } //│ T1.implement(instance); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code :js class C3: T2 //│ Defined class C3 //│ // Prelude //│ class C3 { //│ constructor(fields) { //│ T2.implement(this); //│ } //│ } //│ // End of generated code (C3{}).Test3 //│ res: int //│ = 2 (C3{}).Test4 //│ res: 4 //│ = 4 :js trait Oops //│ Defined trait Oops //│ // Prelude //│ const Oops = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code :js oo = Oops 2 //│ // Query 1 //│ globalThis.oo = Oops.build(2); //│ // End of generated code //│ oo: 2 & #Oops //│ = [Number: 2] :js case oo of { Oops -> 1 } //│ // Query 1 //│ let a; //│ res = (a = oo, Oops.is(a) ? 1 : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ // End of generated code //│ res: 1 //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/MultiArgs.mls ================================================ def foo(a, b, c) = add a (add b c) foo(1, 2, 3) foo(1, 2, 3,) //│ foo: (int, int, int,) -> int //│ = [Function: foo] //│ res: int //│ = 6 //│ res: int //│ = 6 def bar() = 1 bar() bar(,) //│ bar: () -> 1 //│ = [Function: bar] //│ res: 1 //│ = 1 //│ res: 1 //│ = 1 def bar(x) = x bar(1) bar(1,) //│ bar: 'a -> 'a //│ = [Function: bar1] //│ res: 1 //│ = 1 //│ res: 1 //│ = 1 def bar(x,) = x bar(1) bar(1,) //│ bar: 'a -> 'a //│ = [Function: bar2] //│ res: 1 //│ = 1 //│ res: 1 //│ = 1 def bar(x, y) = add x y bar(1, 2) bar(1, 2,) //│ bar: (int, int,) -> int //│ = [Function: bar3] //│ res: int //│ = 3 //│ res: int //│ = 3 :e bar((1, 2)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.53: bar((1, 2)) //│ ║ ^^^^^^^^^^^ //│ ╟── argument of type `((1, 2,),)` does not match type `(?a, ?b,)` //│ ║ l.53: bar((1, 2)) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.42: def bar(x, y) = add x y //│ ╙── ^^^^^^ //│ res: error | int //│ = '1,2undefined' def bar((x, y)) = add x y bar((1, 2)) //│ bar: ((int, int,),) -> int //│ = [Function: bar4] //│ res: int //│ = 3 :p f = fun (x, y) -> add x y f(1, 2) //│ Parsed: let f = (x, y,) => add(x,)(y,); f(1, 2,); //│ Desugared: def f: (x, y,) => add(x,)(y,) //│ AST: Def(false,Var(f),Left(Lam(Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y))))),App(App(Var(add),Tup(List((None,Fld(_,Var(x)))))),Tup(List((None,Fld(_,Var(y)))))))),false) //│ Desugared: f(1, 2,) //│ AST: App(Var(f),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,IntLit(2)))))) //│ f: (int, int,) -> int //│ = [Function: f] //│ res: int //│ = 3 :e f((1, 2)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.87: f((1, 2)) //│ ║ ^^^^^^^^^ //│ ╟── argument of type `((1, 2,),)` does not match type `(?a, ?b,)` //│ ║ l.87: f((1, 2)) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.74: f = fun (x, y) -> add x y //│ ╙── ^^^^^^ //│ res: error | int //│ = '1,2undefined' :e r = (1, 2) f r //│ r: (1, 2,) //│ = [ 1, 2 ] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.102: f r //│ ║ ^^^ //│ ╟── argument of type `((1, 2,),)` does not match type `(?a, ?b,)` //│ ║ l.102: f r //│ ║ ^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.74: f = fun (x, y) -> add x y //│ ╙── ^^^^^^ //│ res: error | int //│ = '1,2undefined' :p f = fun ((x, y)) -> add x y f((1, 2)) //│ Parsed: let f = ('(' [x, y,] ')',) => add(x,)(y,); f('(' [1, 2,] ')',); //│ Desugared: def f: ('(' [x, y,] ')',) => add(x,)(y,) //│ AST: Def(false,Var(f),Left(Lam(Tup(List((None,Fld(_,Bra(false,Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))))))),App(App(Var(add),Tup(List((None,Fld(_,Var(x)))))),Tup(List((None,Fld(_,Var(y)))))))),false) //│ Desugared: f('(' [1, 2,] ')',) //│ AST: App(Var(f),Tup(List((None,Fld(_,Bra(false,Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,IntLit(2))))))))))) //│ f: ((int, int,),) -> int //│ = [Function: f1] //│ res: int //│ = 3 :e f(1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.131: f(1, 2) //│ ║ ^^^^^^^ //│ ╟── argument list of type `(1, 2,)` does not match type `((?a, ?b,),)` //│ ║ l.131: f(1, 2) //│ ╙── ^^^^^^ //│ res: error | int //│ Runtime error: //│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) r = (1, 2) f r //│ r: (1, 2,) //│ = [ 1, 2 ] //│ res: int //│ = 3 ================================================ FILE: shared/src/test/diff/mlscript/Mut.mls ================================================ class A: { x: int } //│ Defined class A def muta1: MutArray[int] //│ muta1: MutArray[int] //│ = muta1[2] <- 666 //│ = //│ muta1 is not implemented :e muta1[1] <- false //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.14: muta1[1] <- false //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.14: muta1[1] <- false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: def muta1: MutArray[int] //│ ║ ^^^ //│ ╟── from assigned array element: //│ ║ l.14: muta1[1] <- false //│ ╙── ^^^^^^^^ //│ = //│ muta1 is not implemented def a1: Array[int] //│ a1: Array[int] //│ = muta1 : Array[int] //│ res: Array[int] //│ = //│ muta1 is not implemented :e a1: MutArray[int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.40: a1: MutArray[int] //│ ║ ^^ //│ ╙── type `int` cannot be reassigned //│ res: MutArray[int] //│ = //│ a1 is not implemented a2 = A {x=3} //│ a2: A & {x: 3} //│ = A { x: 3 } def rc1 : { mut x : int} rc1 = {mut x = 1} //│ rc1: {mut x: int} //│ = //│ {mut x: 'x} //│ where //│ 'x :> 1 //│ <: rc1: //│ {mut x: int} //│ = { x: 1 } rc1.x <- 3 //│ = undefined :e rc1.x <- true //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.68: rc1.x <- true //│ ║ ^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.68: rc1.x <- true //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.53: def rc1 : { mut x : int} //│ ║ ^^^ //│ ╟── from assigned selection: //│ ║ l.68: rc1.x <- true //│ ╙── ^^^^^ //│ = undefined immrcd = { x = 1: int } immtpl = (1: int,) //│ immrcd: {x: int} //│ = { x: 1 } //│ immtpl: (int,) //│ = [ 1 ] immrcd.x immtpl.0 immtpl[0] //│ res: int //│ = 1 //│ res: int //│ = 1 //│ res: int | undefined //│ = 1 :e immrcd.x <- 0 immtpl.0 <- 0 immtpl[0] <- 0 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.102: immrcd.x <- 0 //│ ║ ^^^^^^^^^^^^^ //│ ╟── record field of type `int` is not mutable //│ ║ l.84: immrcd = { x = 1: int } //│ ║ ^^^^^ //│ ╟── but it flows into assigned field with expected type `?x` //│ ║ l.102: immrcd.x <- 0 //│ ╙── ^ //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.103: immtpl.0 <- 0 //│ ║ ^^^^^^^^^^^^^ //│ ╟── tuple field of type `int` is not mutable //│ ║ l.85: immtpl = (1: int,) //│ ║ ^ //│ ╟── but it flows into assigned field with expected type `?0` //│ ║ l.103: immtpl.0 <- 0 //│ ╙── ^ //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.104: immtpl[0] <- 0 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── tuple field of type `int` is not mutable //│ ║ l.85: immtpl = (1: int,) //│ ║ ^ //│ ╟── but it flows into assigned array element with expected type `?a` //│ ║ l.104: immtpl[0] <- 0 //│ ╙── ^^^^^^^^^ //│ = undefined rc2 = {mut x = 1} rc2.x <- true rc2.x <- "hello" //│ rc2: {mut x: 'x} //│ where //│ 'x :> 1 //│ = { x: 1 } //│ = undefined //│ = undefined // TODO let-bound values should be treated as monomorphic – see mismatch in: rc2 //│ res: {mut x: 'x} //│ where //│ 'x :> 1 //│ = { x: 'hello' } def g r = (fun x -> r) (r.x <- 3) g rc1 g rc2 //│ g: ({mut x: in 3} & 'a) -> 'a //│ = [Function: g] //│ res: {mut x: int} //│ = { x: 3 } //│ res: {mut x: 'x} //│ where //│ 'x :> 1 //│ = { x: 3 } :e g {x = 3} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.167: g {x = 3} //│ ║ ^^^^^^^^^ //│ ╟── record field of type `3` is not mutable //│ ║ l.167: g {x = 3} //│ ╙── ^^^^^ //│ res: error | {x: 3} //│ = { x: 3 } def ga r = (fun x -> r) (r[1] <- 6) ga muta1 //│ ga: (MutArray['a] & 'b) -> 'b //│ where //│ 'a :> 6 //│ = [Function: ga] //│ res: MutArray[int] //│ = //│ muta1 is not implemented :e ga a1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.188: ga a1 //│ ║ ^^^^^ //│ ╟── assigned array element of type `int` cannot be reassigned //│ ║ l.177: def ga r = (fun x -> r) (r[1] <- 6) //│ ╙── ^^^^ //│ res: error | Array[int] //│ = //│ a1 is not implemented trait T //│ Defined trait T def t1 : T & {mut x : int; y : bool} t1 = T {mut x = 2; y = false} t2 = T {x = 2} //│ t1: T & {mut x: int, y: bool} //│ = //│ {mut x: 'x, y: false} & #T //│ where //│ 'x :> 2 //│ <: t1: //│ T & {mut x: int, y: bool} //│ = { x: 2, y: false } //│ t2: {x: 2} & #T //│ = { x: 2 } t1.x <- 4 //│ = undefined :e t1.y <- true t2.x <- 3 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.220: t1.y <- true //│ ║ ^^^^^^^^^^^^ //│ ╟── record field of type `bool` is not mutable //│ ║ l.202: def t1 : T & {mut x : int; y : bool} //│ ║ ^^^^^^^^ //│ ╟── but it flows into assigned field with expected type `?y` //│ ║ l.220: t1.y <- true //│ ╙── ^ //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.221: t2.x <- 3 //│ ║ ^^^^^^^^^ //│ ╟── record field of type `2` is not mutable //│ ║ l.204: t2 = T {x = 2} //│ ║ ^^^^^ //│ ╟── but it flows into assigned field with expected type `?x` //│ ║ l.221: t2.x <- 3 //│ ╙── ^ //│ = undefined class B: { mut x: int; y: bool } method Foo = this.x //│ Defined class B //│ Defined B.Foo: B -> int def b1 : B b1 = B { mut x = 2; y = true } b2 = B { mut x = 1; y = false} //│ b1: B //│ = //│ B with {mut x: 'x, y: true} //│ where //│ 'x :> 2 //│ <: int //│ <: b1: //│ B //│ = B { x: 2, y: true } //│ b2: B with {mut x: 'x, y: false} //│ where //│ 'x :> 1 //│ <: int //│ = B { x: 1, y: false } b1.Foo //│ res: int //│ = 2 :e b1.Foo <- 0 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.271: b1.Foo <- 0 //│ ║ ^^^^^^^^^^^ //│ ╟── type `B` does not have field 'Foo' //│ ║ l.248: def b1 : B //│ ║ ^ //│ ╟── but it flows into reference with expected type `{Foo :> ?Foo}` //│ ║ l.271: b1.Foo <- 0 //│ ║ ^^ //│ ╟── Note: constraint arises from assigned selection: //│ ║ l.271: b1.Foo <- 0 //│ ╙── ^^^^^^ //│ = undefined :e b3 = B {x = 6; y = false} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.287: b3 = B {x = 6; y = false} //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── record field of type `6` is not mutable //│ ║ l.287: b3 = B {x = 6; y = false} //│ ╙── ^^^^^ //│ b3: (B with {mut x: 'x, y: false}) | error //│ where //│ 'x :> 6 //│ <: int //│ = B { x: 6, y: false } :e b2.y <- b1.y //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.301: b2.y <- b1.y //│ ║ ^^^^^^^^^^^^ //│ ╟── record field of type `?y` is not mutable //│ ║ l.243: class B: { mut x: int; y: bool } //│ ║ ^^^^^^^ //│ ╟── but it flows into assigned field with expected type `?y0` //│ ║ l.301: b2.y <- b1.y //│ ╙── ^ //│ = undefined :e b2.x <- b1.y //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.314: b2.x <- b1.y //│ ║ ^^^^^^^^^^^^ //│ ╟── type `bool` is not an instance of type `int` //│ ║ l.243: class B: { mut x: int; y: bool } //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.314: b2.x <- b1.y //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.243: class B: { mut x: int; y: bool } //│ ║ ^^^ //│ ╟── from assigned selection: //│ ║ l.314: b2.x <- b1.y //│ ╙── ^^^^ //│ = undefined b2.x <- b1.x b1.x <- a2.x //│ = undefined //│ = undefined def h : B -> int -> int def h b i = (fun _ -> b.x) (b.x <- i) //│ h: B -> int -> int //│ = //│ {mut x: in 'x out 'a} -> 'x -> 'a //│ <: h: //│ B -> int -> int //│ = [Function: h] h b1 2 //│ res: int //│ = 2 :e h {mut x = 4} 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.351: h {mut x = 4} 2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── record literal of type `{mut x: ?x}` is not an instance of type `B` //│ ║ l.351: h {mut x = 4} 2 //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.337: def h : B -> int -> int //│ ╙── ^ //│ res: error | int //│ = 2 def mt1: (mut int, mut bool) def mt2: (int, int) def mt3: (mut int, mut int) def mt4: (mut bool, bool, bool) mt4 = (mut true, false, false) //│ mt1: (mut int, mut bool,) //│ = //│ mt2: (int, int,) //│ = //│ mt3: (mut int, mut int,) //│ = //│ mt4: (mut bool, bool, bool,) //│ = //│ (mut 'a, false, false,) //│ where //│ 'a :> true //│ <: mt4: //│ (mut bool, bool, bool,) //│ = [ true, false, false ] mt1 : (int, bool) //│ res: (int, bool,) //│ = //│ mt1 is not implemented def emt: (mut int) emt.0 //│ emt: (mut int,) //│ = //│ res: int //│ = //│ emt is not implemented k1 = (mut 233, "hello", mut true) k1.0 <- k1.0 + 1 //│ k1: (mut 'a, "hello", mut 'b,) //│ where //│ 'b :> true //│ 'a :> 233 //│ = [ 233, 'hello', true ] //│ = undefined :e k1.1 <- 233 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.407: k1.1 <- 233 //│ ║ ^^^^^^^^^^^ //│ ╟── assigned field of type `"hello"` cannot be reassigned //│ ║ l.407: k1.1 <- 233 //│ ╙── ^ //│ = undefined mt1 = (mut 3, mut false) //│ (mut 'a, mut 'b,) //│ where //│ 'b :> false //│ 'a :> 3 //│ <: mt1: //│ (mut int, mut bool,) //│ = [ 3, false ] def amf : MutArray['a] -> 'a //│ amf: MutArray['a] -> 'a //│ = amf mt3 //│ res: int //│ = //│ amf is not implemented :e amf mt4 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.435: amf mt4 //│ ║ ^^^^^^^ //│ ╙── expression of type `bool` cannot be reassigned //│ res: bool | error //│ = //│ amf is not implemented :e a1[0] <- 1 mt1[0] <- mt2.0 mt4[3] <- true //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.445: a1[0] <- 1 //│ ║ ^^^^^^^^^^ //│ ╟── assigned array element of type `int` cannot be reassigned //│ ║ l.445: a1[0] <- 1 //│ ╙── ^^^^^ //│ = //│ a1 is not implemented //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.446: mt1[0] <- mt2.0 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `bool` //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── from assigned array element: //│ ║ l.446: mt1[0] <- mt2.0 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.446: mt1[0] <- mt2.0 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `bool` //│ ║ l.365: def mt2: (int, int) //│ ║ ^^^ //│ ╟── but it flows into field selection with expected type `bool` //│ ║ l.446: mt1[0] <- mt2.0 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── from assigned array element: //│ ║ l.446: mt1[0] <- mt2.0 //│ ╙── ^^^^^^ //│ = //│ mt2 is not implemented //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.447: mt4[3] <- true //│ ║ ^^^^^^^^^^^^^^ //│ ╟── assigned array element of type `bool` cannot be reassigned //│ ║ l.447: mt4[3] <- true //│ ╙── ^^^^^^ //│ = undefined mt1.0 <- mt2.0 mt1.0 <- mt1.0 * 2 mt1.1 <- false mt3[0] <- let tmp = mt3[1] in case tmp of { undefined -> 0 | _ -> tmp } mt3[1] <- mt1.0 //│ = //│ mt2 is not implemented //│ = undefined //│ = undefined //│ = //│ mt3 is not implemented //│ = //│ mt3 is not implemented :e :ge mt1.0 <- mt1.1 mt1.1 <- 1 mt1.0 <- (b1.t <- 4) (mt1.0 <- b1.t) <- 4 b1.x <- 1 + 2 <- 4 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.509: mt1.0 <- mt1.1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `bool` is not an instance of type `int` //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.509: mt1.0 <- mt1.1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^ //│ ╟── from assigned selection: //│ ║ l.509: mt1.0 <- mt1.1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.510: mt1.1 <- 1 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `bool` //│ ║ l.510: mt1.1 <- 1 //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^^ //│ ╟── from assigned selection: //│ ║ l.510: mt1.1 <- 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.511: mt1.0 <- (b1.t <- 4) //│ ║ ^^^^^^^^^ //│ ╟── type `B` does not have field 't' //│ ║ l.248: def b1 : B //│ ║ ^ //│ ╟── but it flows into reference with expected type `{mut t: in ?t}` //│ ║ l.511: mt1.0 <- (b1.t <- 4) //│ ║ ^^ //│ ╟── Note: constraint arises from assigned selection: //│ ║ l.511: mt1.0 <- (b1.t <- 4) //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.511: mt1.0 <- (b1.t <- 4) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── assignment of type `unit` is not an instance of type `int` //│ ║ l.511: mt1.0 <- (b1.t <- 4) //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.364: def mt1: (mut int, mut bool) //│ ║ ^^^ //│ ╟── from assigned selection: //│ ║ l.511: mt1.0 <- (b1.t <- 4) //│ ╙── ^^^^^ //│ ╔══[ERROR] Illegal assignment //│ ║ l.512: (mt1.0 <- b1.t) <- 4 //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── cannot assign to assignment //│ ║ l.512: (mt1.0 <- b1.t) <- 4 //│ ╙── ^^^^^^^^^^^^^^^ //│ res: error //│ ╔══[ERROR] Illegal assignment //│ ║ l.513: b1.x <- 1 + 2 <- 4 //│ ║ ^^^^^^ //│ ╟── cannot assign to integer literal //│ ║ l.513: b1.x <- 1 + 2 <- 4 //│ ╙── ^ //│ Code generation encountered an error: //│ illegal assignemnt left-hand side: Bra(false,Assign(Sel(Var(mt1),Var(0)),Sel(Var(b1),Var(t)))) def f : {mut 0 : int} -> int -> unit def g : (mut int, bool) -> int -> unit //│ f: {mut 0: int} -> int -> unit //│ = //│ g: (mut int, bool,) -> int -> unit //│ = def f a n = a.0 <- n //│ {mut 0: in '0} -> '0 -> unit //│ <: f: //│ {mut 0: int} -> int -> unit //│ = [Function: f] f mt1 1 //│ = undefined :e f mt2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.598: f mt2 //│ ║ ^^^^^ //│ ╟── tuple field of type `int` is not mutable //│ ║ l.365: def mt2: (int, int) //│ ╙── ^^^ //│ res: error | int -> unit //│ = //│ mt2 is not implemented :e g (1, true) 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.610: g (1, true) 2 //│ ║ ^^^^^^^^^^^ //│ ╟── argument of type `1` is not mutable //│ ║ l.610: g (1, true) 2 //│ ╙── ^ //│ res: error | unit //│ = //│ g is not implemented // TODO forbid `mut` here g (mut 1, true) 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.622: g (mut 1, true) 2 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── argument of type `1` is not mutable //│ ║ l.622: g (mut 1, true) 2 //│ ╙── ^ //│ res: error | unit //│ = //│ g is not implemented w1 = 3 with {mut x = 4} w1.x <- 3 //│ w1: 3 & {mut x: 'x} //│ where //│ 'x :> 4 //│ = [Number: 3] { x: 4 } //│ = undefined w1.x //│ res: 4 //│ = 3 def st1 : (int, ) def st2 : (mut int, ) //│ st1: (int,) //│ = //│ st2: (mut int,) //│ = st1 = (3,) st2 = (mut 4,) //│ (3,) //│ <: st1: //│ (int,) //│ = [ 3 ] //│ (mut 'a,) //│ where //│ 'a :> 4 //│ <: st2: //│ (mut int,) //│ = [ 4 ] st2.0 <- 8 //│ = undefined :e st1.0 <- 9 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.669: st1.0 <- 9 //│ ║ ^^^^^^^^^^ //│ ╟── tuple field of type `int` is not mutable //│ ║ l.645: def st1 : (int, ) //│ ║ ^^^ //│ ╟── but it flows into assigned field with expected type `?0` //│ ║ l.669: st1.0 <- 9 //│ ╙── ^ //│ = undefined def am1 : Array[(mut int)] //│ am1: Array[(mut int,)] //│ = def foreach : Array['a] -> ('a -> unit) -> Array['a] //│ foreach: Array['a] -> ('a -> unit) -> Array['a] //│ = foreach am1 (fun x -> x[0] <- 1) foreach am1 (fun y -> y.0 <- 2) //│ res: Array[(mut int,)] //│ = //│ foreach is not implemented //│ res: Array[(mut int,)] //│ = //│ foreach is not implemented :e (1,2,3)[0] <- true (1,2,3).0 <- "hello" //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.699: (1,2,3)[0] <- true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── assigned array element of type `1 | 2 | 3` cannot be reassigned //│ ║ l.699: (1,2,3)[0] <- true //│ ╙── ^^^^^^^^^^ //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.700: (1,2,3).0 <- "hello" //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple field of type `1` is not mutable //│ ║ l.700: (1,2,3).0 <- "hello" //│ ║ ^ //│ ╟── but it flows into assigned field with expected type `?0` //│ ║ l.700: (1,2,3).0 <- "hello" //│ ╙── ^ //│ = undefined :e (0,)["oops"] (mut 0,)["oops"] <- 1 //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.720: (0,)["oops"] //│ ║ ^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.720: (0,)["oops"] //│ ╙── ^^^^^^ //│ res: 0 | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.721: (mut 0,)["oops"] <- 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.721: (mut 0,)["oops"] <- 1 //│ ╙── ^^^^^^ //│ = undefined oops = "oops" arr = (mut 0,) //│ oops: "oops" //│ = 'oops' //│ arr: (mut 'a,) //│ where //│ 'a :> 0 //│ = [ 0 ] :e arr[oops] arr[oops] <- 1 //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.748: arr[oops] //│ ║ ^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.738: oops = "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.748: arr[oops] //│ ╙── ^^^^ //│ res: 0 | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.749: arr[oops] <- 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.738: oops = "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.749: arr[oops] <- 1 //│ ╙── ^^^^ //│ = undefined // TODO support typing of mutable local variable x = 1 //│ x: 1 //│ = 1 :e x <- 2 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.780: x <- 2 //│ ║ ^^^^^^ //│ ╟── integer literal of type `2` does not match type `1` //│ ║ l.780: x <- 2 //│ ║ ^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.775: x = 1 //│ ╙── ^ //│ = undefined x //│ res: 1 //│ = 2 fun x -> (x.a <- 0, x.a + 1) //│ res: {mut a: in 0 out int} -> (unit, int,) //│ = [Function: res] def foo x y = (x.a <- 0, x.a + 1, x.a, x.a <- y) //│ foo: {mut a: in 0 | 'a out int & 'a0} -> 'a -> (unit, int, 'a0, unit,) //│ = [Function: foo] def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ foo: {mut a: in 0 | 'a out int & 'a0} -> 'a -> ('a0 -> 'b) -> (unit, int, 'a0, unit, 'b,) //│ = [Function: foo1] foo { mut a = 1 } 2 add //│ res: (unit, int, 0 | 1 | 2, unit, int -> int,) //│ = [ undefined, 1, 0, undefined, [Function (anonymous)] ] :e foo { mut a = 1 } 2 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.815: foo { mut a = 1 } 2 3 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `3` is not a function //│ ║ l.815: foo { mut a = 1 } 2 3 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.806: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.806: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ ╙── ^ //│ res: error | (unit, int, 0 | 1 | 2, unit, nothing,) //│ Runtime error: //│ TypeError: z is not a function :e foo { mut a = "oops" } 2 foo { a = 1 } 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.833: foo { mut a = "oops" } 2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.833: foo { mut a = "oops" } 2 //│ ║ ^^^^^^ //│ ╟── but it flows into mutable record field with expected type `int` //│ ║ l.833: foo { mut a = "oops" } 2 //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.806: def foo x y z = (x.a <- 0, x.a + 1, x.a, x.a <- y, z x.a) //│ ╙── ^^^ //│ res: error | (("oops" | 0 | 2) -> 'a) -> (unit, int, "oops" | 0 | 2, unit, 'a,) //│ = [Function (anonymous)] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.834: foo { a = 1 } 2 //│ ║ ^^^^^^^^^^^^^ //│ ╟── record field of type `1` is not mutable //│ ║ l.834: foo { a = 1 } 2 //│ ╙── ^^^^^ //│ res: error | (1 -> 'a) -> (unit, int, 1, unit, 'a,) //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/mlscript/Mut2.mls ================================================ (mut 1,): MutArray['a] //│ res: MutArray['a] //│ where //│ 'a :> 1 //│ = [ 1 ] (mut 1, mut 2): MutArray['a] //│ res: MutArray['a] //│ where //│ 'a :> 1 | 2 //│ = [ 1, 2 ] ((fun t -> let tmp = t.0 <- 3 in t) ((mut 1, mut 2))): MutArray['a] //│ res: MutArray['a] //│ where //│ 'a :> 1 | 2 | 3 //│ = [ 3, 2 ] ((fun t -> let tmp = t.0 + 1 in t) ((mut 1, mut 2))): MutArray['a] //│ res: MutArray[in 'a & 'b out 'b] //│ where //│ 'a <: int & 'b //│ 'b :> 1 | 2 //│ <: 'a //│ = [ 1, 2 ] if true then (mut 1,) else (mut 2,) //│ res: (mut in 'a out 1 | 2 | 'a,) //│ = [ 1 ] if true then (mut 1, mut 2) else (mut 3, mut 4) //│ res: (mut in 'a out 1 | 3 | 'a, mut in 'b out 2 | 4 | 'b,) //│ = [ 1, 2 ] def t1: (mut 1, mut 2) def t2: (mut 3, mut 4) //│ t1: (mut 1, mut 2,) //│ = //│ t2: (mut 3, mut 4,) //│ = r = if true then t1 else t2 //│ r: (mut out 1 | 3, mut out 2 | 4,) //│ = //│ t1 is not implemented :e r.0 <- 1 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.49: r.0 <- 1 //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `3` //│ ║ l.49: r.0 <- 1 //│ ║ ^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.37: def t2: (mut 3, mut 4) //│ ║ ^ //│ ╟── from assigned selection: //│ ║ l.49: r.0 <- 1 //│ ╙── ^^^ //│ = //│ r and t1 are not implemented def t1: (mut 1 | 2 | 3) def t2: (mut 2 | 3 | 4) //│ t1: (mut 1 | 2 | 3,) //│ = //│ t2: (mut 2 | 3 | 4,) //│ = r = if true then t1 else t2 //│ r: (mut in 2 | 3 out 1 | 2 | 3 | 4,) //│ = //│ t1 is not implemented r.0 <- if true then 2 else 3 //│ = //│ r and t1 are not implemented :e r.0 <- if true then 2 else 1 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.83: r.0 <- if true then 2 else 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `2 | 3 | 4` //│ ║ l.83: r.0 <- if true then 2 else 1 //│ ║ ^ //│ ╟── but it flows into application with expected type `2 | 3 | 4` //│ ║ l.83: r.0 <- if true then 2 else 1 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.67: def t2: (mut 2 | 3 | 4) //│ ║ ^^^^^^^^^ //│ ╟── from assigned selection: //│ ║ l.83: r.0 <- if true then 2 else 1 //│ ╙── ^^^ //│ = //│ r and t1 are not implemented ================================================ FILE: shared/src/test/diff/mlscript/MutArray.mls ================================================ def access0 arr = arr[0] //│ access0: Array['a & ~undefined] -> (undefined | 'a) //│ = [Function: access0] def set0 arr x = arr[0] <- x //│ set0: MutArray['a] -> 'a -> unit //│ = [Function: set0] access0((1,)) access0((mut 1,)) //│ res: 1 | undefined //│ = 1 //│ res: 1 | undefined //│ = 1 set0((mut 1,)) //│ res: anything -> unit //│ = [Function (anonymous)] :e set0((1,)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.25: set0((1,)) //│ ║ ^^^^^^^^^^ //│ ╟── tuple field of type `1` is not mutable //│ ║ l.25: set0((1,)) //│ ╙── ^ //│ res: error | anything -> unit //│ = [Function (anonymous)] access0 emptyArray //│ res: undefined //│ = undefined set0 emptyArray 42 //│ = undefined def update0 i arr = arr[i] <- arr[i] //│ update0: int -> MutArray[in 'a | undefined out 'a & ~undefined] -> unit //│ = [Function: update0] def update1 i arr = arr[i] <- let a = arr[i] in case a of undefined -> error, _ -> a //│ update1: int -> MutArray[in 'a out 'a & ~undefined] -> unit //│ = [Function: update1] def update2 i arr = let a = arr[i] in arr[i] <- case a of undefined -> error, _ -> a //│ update2: int -> MutArray[in 'a out 'a & ~undefined] -> unit //│ = [Function: update2] // * Strange that it's reported here :e update0 0 ((mut 1,)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.60: update0 0 ((mut 1,)) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── possibly-undefined array access of type `undefined` does not match type `~undefined` //│ ║ l.45: def update0 i arr = arr[i] <- arr[i] //│ ╙── ^^^^^^ //│ res: error | unit //│ = undefined update1 0 ((mut 1,)) //│ = undefined update2 0 ((mut 1,)) //│ = undefined // * Weird stuff: def foo: MutArray[int] & MutArray[string] //│ foo: MutArray[in int | string out nothing] //│ = // * Note that this now signature-checks because it instantiates the inferred polymorphic type // * later (once for each intersection component) // :e foo = emptyArray //│ MutArray['a] //│ <: foo: //│ MutArray[in int | string out nothing] //│ = [Function: emptyArray] { '0': 42 } :e def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ║ ^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ║ ^^^ //│ ╟── from mutable tuple field: //│ ║ l.96: def bar x = (mut x,) : MutArray[int] & MutArray[string] //│ ╙── ^ //│ bar: nothing -> MutArray[in int | string out nothing] //│ = [Function: bar] foo[0] //│ res: undefined //│ = 42 fun x -> foo[0] <- x //│ res: (int | string) -> unit //│ = [Function: res] access0 foo set0 foo "ok" //│ res: undefined //│ = 42 //│ = undefined update1 0 foo //│ = undefined foo: MutArray['a] //│ res: MutArray['a] //│ where //│ 'a <: int | string //│ = [Function: emptyArray] { '0': 'ok' } foo: MutArray['a] & MutArray['b] //│ res: MutArray[in (int | string) & 'a | (int | string) & 'b out 'a & 'b] //│ = [Function: emptyArray] { '0': 'ok' } foo[0] //│ res: undefined //│ = 'ok' foo: MutArray['a] | MutArray['b] //│ res: MutArray[in 'b & (int | string | ~'b) out 'b] //│ = [Function: emptyArray] { '0': 'ok' } foo[0] //│ res: undefined //│ = 'ok' ================================================ FILE: shared/src/test/diff/mlscript/Neg.mls ================================================ 1: ~string //│ res: ~string //│ = 1 :e 1: ~int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.7: 1: ~int //│ ║ ^ //│ ╟── integer literal of type `1` does not match type `~int` //│ ╟── Note: constraint arises from type negation: //│ ║ l.7: 1: ~int //│ ╙── ^^^^ //│ res: ~int //│ = 1 :e add res 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.19: add res 1 //│ ║ ^^^^^^^ //│ ╟── type `~int` is not an instance of type `int` //│ ║ l.7: 1: ~int //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.19: add res 1 //│ ╙── ^^^ //│ res: error | int //│ = 2 :e (1: int): ~int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.33: (1: int): ~int //│ ║ ^^^^^^^^ //│ ╟── type `int` does not match type `~int` //│ ║ l.33: (1: int): ~int //│ ║ ^^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.33: (1: int): ~int //│ ╙── ^^^^ //│ res: ~int //│ = 1 1: ~2 //│ res: ~2 //│ = 1 1: int //│ res: int //│ = 1 "ok": ~int //│ res: ~int //│ = 'ok' 1: ~(~1 & ~2) //│ res: 1 | 2 //│ = 1 :e 1: ~(~3 & ~2) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.63: 1: ~(~3 & ~2) //│ ║ ^ //│ ╟── integer literal of type `1` does not match type `2 | 3` //│ ╟── Note: constraint arises from type negation: //│ ║ l.63: 1: ~(~3 & ~2) //│ ╙── ^^^^^^^^^^ //│ res: 2 | 3 //│ = 1 class Lit class Add //│ Defined class Lit //│ Defined class Add def f: ~lit & {x:int} -> int // Note the precedence! //│ f: {x: int} -> int //│ = def f: (~lit & {x:int}) -> int //│ f: ({x: int} & ~#Lit) -> int //│ = // :ds def f: (~Lit & {x:int}) -> int //│ f: ({x: int} & ~Lit) -> int //│ = def f: ~Lit & 'b -> 'b //│ f: 'b -> 'b & ~Lit //│ = def f: ~Lit & ~Add & 'b -> 'b //│ f: 'b -> 'b & ~Add & ~Lit //│ = def f: (~Lit & 'b) -> 'b //│ f: ('b & ~Lit) -> 'b //│ = def f: (~Lit & ~Add & 'b) -> 'b //│ f: ('b & ~Add & ~Lit) -> 'b //│ = def f: ~Lit & ~Lit //│ f: ~Lit //│ = :w class Foo[A] //│ Defined class Foo[±A] //│ ╔══[WARNING] Type definition Foo has bivariant type parameters: //│ ║ l.114: class Foo[A] //│ ║ ^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.114: class Foo[A] //│ ╙── ^ def f: ~Foo[1 | 2] | ~Foo[2 | 3] //│ f: ~Foo[?] //│ = def f: (~Foo[1 | 2] & 'a | ~Foo[2 | 3] & 'a) -> 'a //│ f: ('a & ~Foo[?]) -> 'a //│ = // * Type ~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a // * is ~({x: 1 | 2} & {x: 2 | 3}) & 'a // * is ~{x: 2}) & 'a def f: (~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a) -> 'a //│ f: ('a & ~{x: 2}) -> 'a //│ = f = id //│ 'a -> 'a //│ <: f: //│ ('a & ~{x: 2}) -> 'a //│ = [Function: id] :e f x = case x of {} //│ nothing -> nothing //│ <: f: //│ ('a & ~{x: 2}) -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.145: f x = case x of {} //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a & ~{x: 1 | 2}` does not match type `nothing` //│ ║ l.134: def f: (~{x: 1 | 2} & 'a | ~{x: 2 | 3} & 'a) -> 'a //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.145: f x = case x of {} //│ ╙── ^ //│ = [Function: f2] def f: (~{x: 1 | 2} & ~lit & 'a | ~{x: 2 | 3} & ~lit & 'a) -> 'a //│ f: ('a & ~{x: 2} & ~#Lit) -> 'a //│ = def f: string & 'a & ~int & ~string | string & ~int //│ f: string //│ = trait T //│ Defined trait T def a: T & 1 | T & 1 & ~'a | 'a //│ a: T & 1 //│ = ================================================ FILE: shared/src/test/diff/mlscript/NestedClassArgs.mls ================================================ :NoJS // :NoProvs class C[A] method In: A -> A method In = id //│ Defined class C[=A] //│ Declared C.In: C['A] -> 'A -> 'A //│ Defined C.In: C['A] -> 'a -> 'a def c: C[C[int]] //│ c: C[C[int]] def c: 'a -> C[C['a]] //│ c: 'a -> C[C['a]] def c: C[C['a]] -> 'a //│ c: C[C['a]] -> 'a def c: C['a] as 'a //│ c: 'a //│ where //│ 'a := C['a] // * [test:T3] What happens here is we use `factorize` to simplify the union, // * which uses the fact that `C['a] <: 'a` to reduce `C['a] | 'a` to `'a`... // * which is obviously NOT valid when that union we're processing is itself the definition of `'a`! // * So we will need to be more careful about simplifying types, so as not to assume bounds // * we are in the process of simplifying. This could be done by NOT simplifying things in place. def c: C['a] | 'a as 'a //│ c: 'a //│ where //│ 'a := 'a def c: C[C['a]] as 'a //│ c: 'a //│ where //│ 'a := C[C['a]] def c: C[C['a] & 'a] as 'a //│ c: 'a //│ where //│ 'a := C[C['a] & 'a] def c: C[C['a] & 'a | 'a] as 'a //│ c: 'a //│ where //│ 'a := C['a] def c: C['a] //│ c: C['a] class C2[A]: { a: A } method In: A -> A method In = id //│ Defined class C2[=A] //│ Declared C2.In: C2['A] -> 'A -> 'A //│ Defined C2.In: C2['A] -> 'a -> 'a def mkC: 'a -> C2['a] //│ mkC: 'a -> C2['a] mkC' a = C2 { a } //│ mkC': ('a & 'A) -> (C2['A] with {a: 'a}) mkC = mkC' //│ ('a & 'A) -> (C2['A] with {a: 'a}) //│ <: mkC: //│ 'a -> C2['a] rec def rc = mkC(rc) //│ rc: C2['a] //│ where //│ 'a :> C2['a] rec def rc = mkC'(rc) //│ rc: 'a //│ where //│ 'a :> C2['A] & {a: forall 'a. 'a} //│ 'A :> forall 'a. 'a class C3[A]: { a: C3[A] } method In: A -> A method In = id //│ Defined class C3[=A] //│ Declared C3.In: C3['A] -> 'A -> 'A //│ Defined C3.In: C3['A] -> 'a -> 'a def c: 'a -> C3['a] //│ c: 'a -> C3['a] rec def c a = C3 { a = c a } //│ anything -> 'a //│ where //│ 'a :> C3['A] with {a: 'a} //│ <: c: //│ 'a -> C3['a] rec def c (a: 'X) = C3 { a = c a: 'X }: C3['X] //│ anything -> C3['X] //│ <: c: //│ 'a -> C3['a] class C4[A]: { a: C[C4[A]] } method In: A -> A method In = id //│ Defined class C4[=A] //│ Declared C4.In: C4['A] -> 'A -> 'A //│ Defined C4.In: C4['A] -> 'a -> 'a def c: 'a -> C4['a] //│ c: 'a -> C4['a] C{} //│ res: C['A] def c4 a = C4{ a = C{} } //│ c4: anything -> (C4['A] with {a: forall 'A0. C['A0]}) def c = c4 //│ anything -> (C4['A] with {a: forall 'A0. C['A0]}) //│ <: c: //│ 'a -> C4['a] class C5[A]: { a: C2[C5[A]] } method In: A -> A method In = id //│ Defined class C5[=A] //│ Declared C5.In: C5['A] -> 'A -> 'A //│ Defined C5.In: C5['A] -> 'a -> 'a def c: 'a -> C5['a] //│ c: 'a -> C5['a] rec def c5 a = C5{ a = C2 { a = c5 a } } //│ c5: anything -> (C5['A] with {a: 'a}) //│ where //│ 'a :> C2['A0] with {a: C5['A] with {a: 'a}} //│ 'A0 :> (C5['A] with {a: 'a}) | C5['A] //│ <: C5['A] c = c5 //│ anything -> (C5['A] with {a: 'a}) //│ where //│ 'a :> C2['A0] with {a: C5['A] with {a: 'a}} //│ 'A0 :> (C5['A] with {a: 'a}) | C5['A] //│ <: C5['A] //│ <: c: //│ 'a -> C5['a] class C6[A]: { a: C5[C6[A]] } method In: A -> A method In = id //│ Defined class C6[=A] //│ Declared C6.In: C6['A] -> 'A -> 'A //│ Defined C6.In: C6['A] -> 'a -> 'a def c: 'a -> C6['a] //│ c: 'a -> C6['a] rec def c6 a = C6{ a = c5 (c6 a) } //│ c6: anything -> (C6['A] with {a: forall 'A0 'a 'A1. C5['A0] with {a: 'a}}) //│ where //│ 'a :> C2['A1] with {a: C5['A0] with {a: 'a}} //│ 'A1 :> (C5['A0] with {a: 'a}) | C5['A0] //│ <: C5['A0] :stats c = c6 //│ anything -> (C6['A] with {a: forall 'A0 'a 'A1. C5['A0] with {a: 'a}}) //│ where //│ 'a :> C2['A1] with {a: C5['A0] with {a: 'a}} //│ 'A1 :> (C5['A0] with {a: 'a}) | C5['A0] //│ <: C5['A0] //│ <: c: //│ 'a -> C6['a] //│ constrain calls : 64 //│ annoying calls : 34 //│ subtyping calls : 434 // Reproduction of an issue found while trying out TypeRef ctor typing: def e: C5[C6['A]] | C5[C6['A]] & ~c5 | C5[C6['A]] | C5[C6['A]] & ~{a: C2[C5[C6['A]]]} //│ e: C5[C6['A]] def e: C5[C6['A]] & {a: C2[C5[C6['A]] | C5[C6['A]] & ~c5 | C5[C6['A]] | C5[C6['A]] & ~{a: C2[C5[C6['A]]]}] & {a: 'a}} //│ e: C5[C6['A]] & {a: C2[C5[C6['A]]] & {a: nothing}} type Inner = C5[C6['A]] & {a: C2[C5[C6['A]] | C5[C6['A]] & ~c5 | C5[C6['A]] | C5[C6['A]] & ~{a: C2[C5[C6['A]]]}] & {a: 'a}} as 'a //│ Defined type alias Inner def e: anything -> (C6['A] & {a: Inner}) //│ e: anything -> (C6['A] with {a: Inner}) // * The problem seems to come from Inner defining type variables, which are given level 0... // * This is actually the expected behavior of type vars in type aliases, // * but it's really weird and fishy so it should probably be rejected. // :e // no longer an error...(?) // :e // Fails with polymorphic RHS extrusion // Used to fail with a depth excess... (before implicitly quantifying TVs in type defs) c = e //│ anything -> (C6['A] with {a: Inner}) //│ <: c: //│ 'a -> C6['a] type Inner2[A] = C5[C6[A]] & {a: C2[C5[C6[A]] | C5[C6[A]] & ~c5 | C5[C6[A]] | C5[C6[A]] & ~{a: C2[C5[C6[A]]]}] & {a: Inner2[A]}} //│ Defined type alias Inner2[=A] def e: anything -> (C6['A] & {a: Inner2['A]}) //│ e: anything -> (C6['A] with {a: Inner2['A]}) c = e //│ anything -> (C6['A] with {a: Inner2['A]}) //│ <: c: //│ 'a -> C6['a] class N: {} class S[T]: { v: T } method In: T -> () method In _ = () type O[T] = S[T] | N class L[T]: { h: T; t: O[L[T]] } method Append: T -> L[T] method Append elem = L { h = elem; t = S { v = this } } //│ Defined class N //│ Defined class S[=T] //│ Declared S.In: S['T] -> 'T -> () //│ Defined S.In: S['T] -> anything -> () //│ Defined type alias O[=T] //│ Defined class L[=T] //│ Declared L.Append: L['T] -> 'T -> L['T] //│ Defined L.Append: (L['T] & 'this) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] & {v: L['T] & 'this}}) s1 = S{v=1}:O['_] s2 = S{v=S{v=1}}:O[O['_]] //│ s1: O['_] //│ where //│ '_ :> 1 //│ s2: O[O['_]] //│ where //│ '_ :> 1 :e L{h=error;t=s1} L{h=error;t=s2} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.255: L{h=error;t=s1} //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `L` //│ ║ l.245: s1 = S{v=1}:O['_] //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.233: class L[T]: { h: T; t: O[L[T]] } //│ ╙── ^^^^ //│ res: error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.256: L{h=error;t=s2} //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type `S['_]` is not an instance of type `L` //│ ║ l.232: type O[T] = S[T] | N //│ ║ ^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.233: class L[T]: { h: T; t: O[L[T]] } //│ ╙── ^^^^ //│ res: error // :ds // L.Append // before: // Defined L.Append: L['T] -> ('T & 'a & 'b) -> (L['T & 'b .. 'T | 'b] with {h: 'a, t: S[L['T & 'b .. 'T | 'b] & 'c .. L['T & 'b .. 'T | 'b] | L['T] | 'c] with {v: L['T]}}) L.Append //│ res: L['T] -> 'T -> L['T] def append ls elem = L { h = elem; t = S { v = ls } } //│ append: (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) :ns append //│ res: forall 'a 'b 'v 'T 'c 'h 'T0 't. 'a -> 'b -> 'c //│ where //│ 'c :> #L & {h: 'h, t: 't, L#T = 'T0} //│ 't :> #S & {v: 'v, S#T = 'T} //│ <: O[L['T0]] //│ 'b <: 'h //│ 'h <: 'T0 //│ 'a <: 'v //│ 'v <: L['T0] & 'T //│ 'T := L['T0] append error //│ res: ('h & 'T) -> (L['T] with {h: 'h, t: S[L['T]] & {v: nothing}}) def append_ty: (L['T] & 'v) -> ('T & 'h) -> (L['T] & {h: 'h; t: S[L['T]] & {v: 'v}}) //│ append_ty: (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) append_ty error //│ res: ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] & {v: nothing}}) append_ty = append //│ (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) //│ <: append_ty: //│ (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) // * Note: An older bug (fixed in 2a562ddfc712ab44a55a12370380ef4f1c3383cb) // * was dropping the bounds on T0 and generating this signature, which is too general: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) //│ append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] with {v: 'v}}) append_ty_2 error //│ res: ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] & {v: nothing}}) :e append_ty_2 = append //│ (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) //│ <: append_ty_2: //│ (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] with {v: 'v}}) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.329: append_ty_2 = append //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'T0` is not an instance of type `L` //│ ║ l.322: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.233: class L[T]: { h: T; t: O[L[T]] } //│ ║ ^^^^ //│ ╟── Note: class type parameter T is defined at: //│ ║ l.229: class S[T]: { v: T } //│ ╙── ^ append_ty = append_ty_2 //│ (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] with {v: 'v}}) //│ <: append_ty: //│ (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) :e append_ty_2 = append_ty //│ (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] with {v: 'v}}) //│ <: append_ty_2: //│ (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['T0] with {v: 'v}}) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.352: append_ty_2 = append_ty //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'T0` is not an instance of type `L` //│ ║ l.322: def append_ty_2: (L['T] & 'v & 'T0) -> ('T & 'h) -> (L['T] & {h: 'h; t: S['T0] & {v: 'v}}) //│ ║ ^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.308: def append_ty: (L['T] & 'v) -> ('T & 'h) -> (L['T] & {h: 'h; t: S[L['T]] & {v: 'v}}) //│ ╙── ^^^^^ def append0 ls = L { h = 0; t = S { v = ls } } //│ append0: (L['T] & 'v) -> (L['T] with {h: 0, t: S[L['T]] with {v: 'v}}) //│ where //│ 'T :> 0 def appendNil elem = L { h = elem; t = N{} } //│ appendNil: ('h & 'T) -> (L['T] with {h: 'h, t: N}) S{v=1} //│ res: S['T] & {v: 1} //│ where //│ 'T :> 1 ================================================ FILE: shared/src/test/diff/mlscript/NestedClassArgs_Co.mls ================================================ :NoJS // :NoProvs class C[A] method Co: A method Co = error //│ Defined class C[+A] //│ Declared C.Co: C['A] -> 'A //│ Defined C.Co: C[?] -> nothing def c: C[C[int]] //│ c: C[C[int]] def c: 'a -> C[C['a]] //│ c: 'a -> C[C['a]] def c: C[C['a]] -> 'a //│ c: C[C['a]] -> 'a def c: C['a] as 'a //│ c: 'a //│ where //│ 'a :> C['a] def c: C['a] | 'a as 'a //│ c: 'a //│ where //│ 'a :> C['a] def c: C[C['a]] as 'a //│ c: 'a //│ where //│ 'a :> C[C['a]] def c: C[C['a] & 'a] as 'a //│ c: 'a //│ where //│ 'a :> C[C['a] & 'a] def c: C[C['a] & 'a | 'a] as 'a //│ c: 'a //│ where //│ 'a :> C['a] def c: C['a] //│ c: C[nothing] class C2[A]: { a: A } //│ Defined class C2[+A] def mkC: 'a -> C2['a] //│ mkC: 'a -> C2['a] mkC' a = C2 { a } //│ mkC': 'a -> C2['a] mkC = mkC' //│ 'a -> C2['a] //│ <: mkC: //│ 'a -> C2['a] rec def rc = mkC(rc) //│ rc: 'a //│ where //│ 'a :> C2['a] rec def rc = mkC'(rc) //│ rc: 'a //│ where //│ 'a :> C2[forall 'a. 'a] class C3[A]: { a: C3[A] } method Co: A method Co = error //│ Defined class C3[+A] //│ Declared C3.Co: C3['A] -> 'A //│ Defined C3.Co: C3[?] -> nothing def c: 'a -> C3['a] //│ c: 'a -> C3['a] rec def c a = C3 { a = c a } //│ anything -> 'a //│ where //│ 'a :> C3[nothing] with {a: 'a} //│ <: c: //│ 'a -> C3['a] rec def c (a: 'X) = C3 { a = c a: 'X }: C3['X] //│ anything -> C3[nothing] //│ <: c: //│ 'a -> C3['a] class C4[A]: { a: C[C4[A]] } method Co: A method Co = error //│ Defined class C4[+A] //│ Declared C4.Co: C4['A] -> 'A //│ Defined C4.Co: C4[?] -> nothing def c: 'a -> C4['a] //│ c: 'a -> C4['a] C{} //│ res: C[nothing] def c4 a = C4{ a = C{} } //│ c4: anything -> (C4[nothing] & {a: C[nothing]}) def c = c4 //│ anything -> (C4[nothing] & {a: C[nothing]}) //│ <: c: //│ 'a -> C4['a] class C5[A]: { a: C2[C5[A]] } method Co: A method Co = error //│ Defined class C5[+A] //│ Declared C5.Co: C5['A] -> 'A //│ Defined C5.Co: C5[?] -> nothing def c: 'a -> C5['a] //│ c: 'a -> C5['a] rec def c5 a = C5{ a = C2 { a = c5 a } } //│ c5: anything -> (C5[nothing] with {a: 'a}) //│ where //│ 'a :> C2[C5[nothing] with {a: 'a}] c = c5 //│ anything -> (C5[nothing] with {a: 'a}) //│ where //│ 'a :> C2[C5[nothing] with {a: 'a}] //│ <: c: //│ 'a -> C5['a] class C6[A]: { a: C5[C6[A]] } method Co: A method Co = error //│ Defined class C6[+A] //│ Declared C6.Co: C6['A] -> 'A //│ Defined C6.Co: C6[?] -> nothing def c: 'a -> C6['a] //│ c: 'a -> C6['a] // :s // :d // rec def c a = C6{ a = c5 (c a) } rec def c6 a = C6{ a = c5 (c6 a) } //│ c6: anything -> (C6[nothing] with {a: forall 'a. C5[nothing] with {a: 'a}}) //│ where //│ 'a :> C2[C5[nothing] with {a: 'a}] c = c6 //│ anything -> (C6[nothing] with {a: forall 'a. C5[nothing] with {a: 'a}}) //│ where //│ 'a :> C2[C5[nothing] with {a: 'a}] //│ <: c: //│ 'a -> C6['a] class N: {} class S[T]: { v: T } type O[T] = S[T] | N class L[T]: { h: T; t: O[L[T]] } method Append: T -> L[T] method Append elem = L { h = elem; t = S { v = this } } //│ Defined class N //│ Defined class S[+T] //│ Defined type alias O[+T] //│ Defined class L[=T] //│ Declared L.Append: L['T] -> 'T -> L['T] //│ Defined L.Append: (L['T] & 'this) -> ('T & 'h) -> (L['T] with {h: 'h, t: S[L['T]] & {v: L['T] & 'this}}) // :ds // L.Append // before: // Defined L.Append: L['T] -> ('T & 'a & 'b) -> (L['T & 'b .. 'T | 'b] with {h: 'a, t: S[L['T & 'b .. 'T | 'b] & 'c .. L['T & 'b .. 'T | 'b] | L['T] | 'c] with {v: L['T]}}) L.Append //│ res: L['T] -> 'T -> L['T] def append ls elem = L { h = elem; t = S { v = ls } } //│ append: (L['T] & 'v) -> ('T & 'h) -> (L['T] with {h: 'h, t: S['v]}) def append0 ls = L { h = 0; t = S { v = ls } } //│ append0: (L['T] & 'v) -> (L['T] with {h: 0, t: S['v]}) //│ where //│ 'T :> 0 def appendNil elem = L { h = elem; t = N{} } //│ appendNil: ('h & 'T) -> (L['T] with {h: 'h, t: N}) S{v=1} //│ res: S[1] ================================================ FILE: shared/src/test/diff/mlscript/NestedMatch.mls ================================================ def f x = case x of { | 0 -> 0 | _ -> case x of { | 1 -> 1 | 2 -> 2 } } //│ f: (0 | 1 | 2) -> (0 | 1 | 2) //│ = [Function: f] def f x = case x of { | 0 -> 0 | _ -> case x of { | 1 -> 1 | _ -> 2 } } //│ f: anything -> (0 | 1 | 2) //│ = [Function: f1] def f x = case x of { | 0 -> 0 | _ -> case x of { | 1 -> 1 | _ -> x } } //│ f: (0 | 1 | 'a & ~0 & ~1) -> (0 | 1 | 'a) //│ = [Function: f2] def f x = case x of { | 0 -> x | _ -> case x of { | 1 -> 1 | _ -> x } } //│ f: (1 | 'a & (0 | ~0 & ~1)) -> (1 | 'a) //│ = [Function: f3] ================================================ FILE: shared/src/test/diff/mlscript/NestedRecursiveMatch.mls ================================================ :NoJS class Leaf class Node class Some[A]: { value: A } method Id: A -> A class None //│ Defined class Leaf //│ Defined class Node //│ Defined class Some[=A] //│ Declared Some.Id: Some['A] -> 'A -> 'A //│ Defined class None def Some: 'a -> Some['a] def None: None //│ Some: 'a -> Some['a] //│ None: None rec def f w = case w of Leaf -> None, Node -> let left = (w).left in let right = (w).right in let tmp0 = f (left,) in case tmp0 of None -> ( let tmp1 = f (right,) in case tmp1 of None -> Some (0,), Some -> let m = (tmp1).value in Some (m,) ), Some -> let m = (tmp0).value in Some (m,) //│ f: (Leaf | Node & 'b) -> (None | Some[in 'a out 0 | 'a]) //│ where //│ 'b <: {left: Leaf | Node & 'b, right: Leaf | Node & 'b} // * Minimizations: rec def f w = case w of Node -> let left = (w).left in let tmp0 = f (left,) in case tmp0 of None -> Some (0,), Some -> let m = (tmp0).value in Some (m,) //│ f: 'left -> Some[in 'a out 0 | 'a] //│ where //│ 'left <: Node & {left: 'left} rec def f w = case w of Node -> let tmp0 = f w.left in case tmp0 of None -> Some 0, Some -> // Some tmp0.value // * produces different result! let m = tmp0.value in Some m //│ f: 'b -> Some[in 'a out 0 | 'a] //│ where //│ 'b <: Node & {left: 'b} def f w = let tmp0 = f w.left in case tmp0 of None -> Some 0, Some -> let m = tmp0.value in Some m //│ f: 'b -> Some[in 'a out 0 | 'a] //│ where //│ 'b <: {left: Node & 'b} ================================================ FILE: shared/src/test/diff/mlscript/OccursCheck.mls ================================================ :OccursCheck rec def f x = (x, x) //│ f: 'a -> ('a, 'a,) //│ = [Function: f] rec def f x = f x //│ f: anything -> nothing //│ = [Function: f1] :e rec def f x = f //│ ╔══[ERROR] Inferred recursive type: 'f //│ where //│ 'f :> ? -> 'f //│ ╙── //│ f: 'f //│ where //│ 'f :> anything -> 'f //│ = [Function: f2] :e rec def f x = (f x, f x) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a :> ('a, 'a,) //│ ║ l.25: rec def f x = (f x, f x) //│ ╙── ^^^ //│ f: anything -> 'a //│ where //│ 'a :> ('a, 'a,) //│ = [Function: f3] :e rec def f x = f x.a //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: {a: 'a} //│ ║ l.37: rec def f x = f x.a //│ ╙── ^^^ //│ f: 'a -> nothing //│ where //│ 'a <: {a: 'a} //│ = [Function: f4] // class Foo[A] method Id: A -> A class Foo[A] method Get: A //│ Defined class Foo[+A] //│ Declared Foo.Get: Foo['A] -> 'A :e rec def foo(x: Foo['a]) = foo(x.Get) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: Foo['a] //│ ║ l.55: rec def foo(x: Foo['a]) = foo(x.Get) //│ ╙── ^^^^^ //│ foo: 'a -> nothing //│ where //│ 'a <: Foo['a] //│ = [Function: foo] type Bar[A] = A def get: Bar['a] -> 'a //│ Defined type alias Bar[+A] //│ get: Bar['a] -> 'a //│ = // * Note that this is not truly a recursive type, // * since `a <: Bar['a]` is equivalent to `'a <: 'a`! // * But our analysis omits expanding type aliases, // * for performance and simplicity (sidesteps infinite expansion problems). :e rec def bar(x: Bar['a]) = bar(get(x)) //│ ╔══[ERROR] Inferred recursive type: 'a //│ where //│ 'a <: Bar['a] //│ ║ l.78: rec def bar(x: Bar['a]) = bar(get(x)) //│ ╙── ^^^^^^ //│ bar: 'a -> nothing //│ where //│ 'a <: Bar['a] //│ = //│ get is not implemented ================================================ FILE: shared/src/test/diff/mlscript/Ops.mls ================================================ :p 2 + 2 //│ Parsed: +(2,)(2,); //│ Desugared: +(2,)(2,) //│ AST: App(App(Var(+),Tup(List((None,Fld(_,IntLit(2)))))),Tup(List((None,Fld(_,IntLit(2)))))) //│ res: int //│ = 4 :p 1 + 2 * 2 + 3 //│ Parsed: +(+(1,)(*(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(*(2,)(2,),),)(3,) //│ AST: App(App(Var(+),Tup(List((None,Fld(_,App(App(Var(+),Tup(List((None,Fld(_,IntLit(1)))))),Tup(List((None,Fld(_,App(App(Var(*),Tup(List((None,Fld(_,IntLit(2)))))),Tup(List((None,Fld(_,IntLit(2)))))))))))))))),Tup(List((None,Fld(_,IntLit(3)))))) //│ res: int //│ = 8 :e :p 1 + 2 / 2 + 3 //│ Parsed: +(+(1,)(/(2,)(2,),),)(3,); //│ Desugared: +(+(1,)(/(2,)(2,),),)(3,) //│ AST: App(App(Var(+),Tup(List((None,Fld(_,App(App(Var(+),Tup(List((None,Fld(_,IntLit(1)))))),Tup(List((None,Fld(_,App(App(Var(/),Tup(List((None,Fld(_,IntLit(2)))))),Tup(List((None,Fld(_,IntLit(2)))))))))))))))),Tup(List((None,Fld(_,IntLit(3)))))) //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: 1 + 2 / 2 + 3 //│ ║ ^^^^^^^^^ //│ ╟── operator application of type `number` is not an instance of type `int` //│ ║ l.20: 1 + 2 / 2 + 3 //│ ╙── ^^^^^ //│ res: int //│ = 5 :e :ge :p 1 |> 2 || 3 //│ Parsed: ||(|>(1,)(2,),)(3,); //│ Desugared: ||(|>(1,)(2,),)(3,) //│ AST: App(App(Var(||),Tup(List((None,Fld(_,App(App(Var(|>),Tup(List((None,Fld(_,IntLit(1)))))),Tup(List((None,Fld(_,IntLit(2))))))))))),Tup(List((None,Fld(_,IntLit(3)))))) //│ ╔══[ERROR] identifier not found: |> //│ ║ l.36: 1 |> 2 || 3 //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.36: 1 |> 2 || 3 //│ ║ ^^^^^^^^^^^ //│ ╟── integer literal of type `3` is not an instance of type `bool` //│ ║ l.36: 1 |> 2 || 3 //│ ╙── ^ //│ res: bool | error //│ Code generation encountered an error: //│ unresolved symbol |> :p true || false && true || false //│ Parsed: ||(||(true,)(&&(false,)(true,),),)(false,); //│ Desugared: ||(||(true,)(&&(false,)(true,),),)(false,) //│ AST: App(App(Var(||),Tup(List((None,Fld(_,App(App(Var(||),Tup(List((None,Fld(_,Var(true)))))),Tup(List((None,Fld(_,App(App(Var(&&),Tup(List((None,Fld(_,Var(false)))))),Tup(List((None,Fld(_,Var(true)))))))))))))))),Tup(List((None,Fld(_,Var(false)))))) //│ res: bool //│ = true // Infix operators not yet supported: :pe -1 //│ /!\ Parse error: Expected end-of-input:1:1, found "-1;" at l.65:1: -1 :pe - 1 //│ /!\ Parse error: Expected end-of-input:1:1, found "- 1;" at l.69:1: - 1 ================================================ FILE: shared/src/test/diff/mlscript/OtherErrors.mls ================================================ :AllowTypeErrors // Multiline error: id id id 3 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.4: id //│ ║ ^^ //│ ║ l.5: id //│ ║ ^^^^ //│ ║ l.6: id //│ ║ ^^^^ //│ ║ l.7: 3 2 //│ ║ ^^^^^ //│ ╟── integer literal of type `3` is not a function //│ ║ l.7: 3 2 //│ ║ ^ //│ ╟── but it flows into application with expected type `2 -> ?a` //│ ║ l.4: id //│ ║ ^^ //│ ║ l.5: id //│ ║ ^^^^ //│ ║ l.6: id //│ ║ ^^^^ //│ ║ l.7: 3 2 //│ ╙── ^^^ //│ res: error 1: 1 & int: { x: int; y: string } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.32: 1: 1 & int: { x: int; y: string } //│ ║ ^ //│ ╟── type `1` does not have all required fields 'x', 'y' //│ ║ l.32: 1: 1 & int: { x: int; y: string } //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.32: 1: 1 & int: { x: int; y: string } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: {x: int, y: string} 1: 1 & int: { x: int; y: string } | string //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.44: 1: 1 & int: { x: int; y: string } | string //│ ║ ^ //│ ╟── type `1` does not match type `string | {x: int, y: string}` //│ ║ l.44: 1: 1 & int: { x: int; y: string } | string //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.44: 1: 1 & int: { x: int; y: string } | string //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: string | {x: int, y: string} ================================================ FILE: shared/src/test/diff/mlscript/Paper.mls ================================================ class None: {} class Some[A]: { value: A } type Option[A] = Some[A] | None //│ Defined class None //│ Defined class Some[+A] //│ Defined type alias Option[+A] def flatMap f opt = case opt of Some -> f opt.value, None -> None{} //│ flatMap: ('value -> 'a) -> (None | Some[?] & {value: 'value}) -> (None | 'a) //│ = [Function: flatMap] res = flatMap (fun x -> x) (Some{value = 42}) //│ res: 42 | None //│ = 42 case res of int -> res, None -> 0 //│ res: 0 | 42 //│ = 42 flatMap (fun x -> None{}) (Some{value = 42}) //│ res: None //│ = None {} def flatMap2 f opt = case opt of Some -> f opt.value, _ -> opt //│ flatMap2: ('value -> 'a) -> (Some[?] & {value: 'value} | 'a & ~#Some) -> 'a //│ = [Function: flatMap2] flatMap2 (fun x -> Some{value = x}) (Some{value = 12}) //│ res: Some[12] //│ = Some { value: 12 } flatMap2 (fun x -> Some{value = x}) 42 //│ res: 42 | Some[nothing] //│ = 42 flatMap2 (fun x -> x) 42 //│ res: 42 //│ = 42 def mapSome f opt = case opt of Some -> f opt, _ -> opt //│ mapSome: ('a -> 'b) -> (Some[?] & 'a | 'b & ~#Some) -> 'b //│ = [Function: mapSome] class SomeAnd[A, P]: Some[A] & { payload: P } //│ Defined class SomeAnd[+A, +P] let arg = if true then SomeAnd{value = 42; payload = 23} else None{} in mapSome (fun x -> x.value + x.payload) arg //│ res: None | int //│ = 65 // * Note: there was a typo in the original paper submission: we used `(m: ~0)` instead of `(m: ~0 & int)` def div n m = n / (m: ~0 & number) //│ div: number -> (number & ~0) -> number //│ = [Function: div] f x = div x 2 //│ f: number -> number //│ = [Function: f] :e // `:e` is used to indicate this error is expected g (x: int) = div 100 x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.70: g (x: int) = div 100 x //│ ║ ^^^^^^^^^ //│ ╟── type `int` does not match type `~0` //│ ║ l.70: g (x: int) = div 100 x //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `~0` //│ ║ l.70: g (x: int) = div 100 x //│ ║ ^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.61: def div n m = n / (m: ~0 & number) //│ ║ ^^ //│ ╟── from reference: //│ ║ l.61: def div n m = n / (m: ~0 & number) //│ ╙── ^ //│ g: int -> (error | number) //│ = [Function: g] div_opt n m = case m of 0 -> None{}, _ -> Some{value = div n m} //│ div_opt: number -> (0 | number & ~0) -> (None | Some[number]) //│ = [Function: div_opt] def capitalize: string -> string //│ capitalize: string -> string //│ = class Person: {name: string; age: int; isMajor: bool} //│ Defined class Person def Person n a = Person{name = capitalize n; age = a; isMajor = a >= 18} //│ Person: string -> (int & 'age) -> (Person with {age: 'age}) //│ = //│ capitalize is not implemented class Cons[A]: Some[A] & { tail: List[A] } type List[A] = Cons[A] | None //│ Defined class Cons[+A] //│ Defined type alias List[+A] rec def mapList f ls = case ls of Cons -> Cons{value = f ls.value; tail = mapList f ls.tail}, None -> None{} //│ mapList: ('value -> 'value0) -> 'a -> (None | 'b) //│ where //│ 'b :> Cons['value0] with {tail: None | 'b} //│ 'a <: (Cons[?] with {tail: 'a, value: 'value}) | None //│ = [Function: mapList] def Cons head tail = Cons { value = head; tail = tail } //│ Cons: ('value & 'A) -> (List['A] & 'tail) -> (Cons['A] with {tail: 'tail, value: 'value}) //│ = [Function: Cons1] def None = None{} //│ None: None //│ = [Function: None1] rec def unzip xs = case xs of None -> { fst = None; snd = None }, Some -> let tmp = unzip xs.tail in { fst = Cons xs.value.fst tmp.fst ; snd = Cons xs.value.snd tmp.snd } //│ unzip: 'a -> {fst: None | 'b, snd: None | 'c} //│ where //│ 'c :> Cons['value] with {tail: None | 'c} //│ 'b :> Cons['value0] with {tail: None | 'b} //│ 'a <: None | Some[?] & {tail: 'a, value: {fst: 'value0, snd: 'value}} //│ = [Function: unzip] // * Declare desired types: def Cons_ty: 'a -> ('b & List['a]) -> (Cons['a] & { value: 'a; tail: 'b }) def unzip_ty: List[{ fst: 'a; snd: 'b }] -> { fst: List['a]; snd: List['b] } //│ Cons_ty: 'a -> (List['a] & 'b) -> (Cons['a] with {tail: 'b}) //│ = //│ unzip_ty: List[{fst: 'a, snd: 'b}] -> {fst: List['a], snd: List['b]} //│ = // * Assert the inferred types subsume them (the signatures above are checked): def Cons_ty = Cons def unzip_ty = unzip //│ ('value & 'A) -> (List['A] & 'tail) -> (Cons['A] with {tail: 'tail, value: 'value}) //│ <: Cons_ty: //│ 'a -> (List['a] & 'b) -> (Cons['a] with {tail: 'b}) //│ = [Function: Cons_ty] //│ 'a -> {fst: None | 'b, snd: None | 'c} //│ where //│ 'c :> Cons['value] with {tail: None | 'c} //│ 'b :> Cons['value0] with {tail: None | 'b} //│ 'a <: None | Some[?] & {tail: 'a, value: {fst: 'value0, snd: 'value}} //│ <: unzip_ty: //│ List[{fst: 'a, snd: 'b}] -> {fst: List['a], snd: List['b]} //│ = [Function: unzip_ty] ================================================ FILE: shared/src/test/diff/mlscript/PolyVariant.mls ================================================ // Examples from Set-Theoretic Types for Polymorphic Variants //************** PRELIMINARIES ************* class Nil class Cons[A]: { head: A; tail: List[A] } type List[A] = Nil | Cons[A] //│ Defined class Nil //│ Defined class Cons[+A] //│ Defined type alias List[+A] def Nil = Nil {} //│ Nil: Nil //│ = [Function: Nil1] def Cons head tail = Cons { head; tail } //│ Cons: ('head & 'A) -> (List['A] & 'tail) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] class A class B class C class D //│ Defined class A //│ Defined class B //│ Defined class C //│ Defined class D //************** EXAMPLE 1: LOSS OF POLYMORPHISM ************* def id x = x //│ id: 'a -> 'a //│ = [Function: id] id A{} //│ res: A //│ = A {} def f x = case x of { A -> true } //│ f: A -> true //│ = [Function: f] f (id A{}) //│ res: true //│ = true // # [`A; `C] // - : [> `A | `C] list // # [id `A; `C] // - : [> `A | `C] list Cons (A{}) (Cons (C{}) Nil) //│ res: Cons[A | C] with {head: A, tail: Cons[C] with {tail: Nil}} //│ = Cons { head: A {}, tail: Cons { head: C {}, tail: Nil {} } } Cons (id A{}) (Cons (C{}) Nil) //│ res: Cons[A | C] with {head: A, tail: Cons[C] with {tail: Nil}} //│ = Cons { head: A {}, tail: Cons { head: C {}, tail: Nil {} } } // val id2 : ([< `A | `B ] as 'a) -> 'a = def id2 x = case x of { A -> x | B -> x } //│ id2: ('a & (A | B)) -> 'a //│ = [Function: id2] // id2 `A // - : [< `A | `B > `A ] = `A id2 (A{}) //│ res: A //│ = A {} // # [(id2 `A); `C];; // Error: This expression has type [> `C ] but an expression was expected of // type [< `A | `B > `A ]. The second variant type does not allow tag(s) `C Cons (id2 (A{})) (Cons (C{}) Nil) //│ res: Cons[A | C] with {head: A, tail: Cons[C] with {tail: Nil}} //│ = Cons { head: A {}, tail: Cons { head: C {}, tail: Nil {} } } //************** EXAMPLE 2: ROUGHLY-TYPED PATTERN MATCHING ************* // val f : [ `A | `B ] -> [ `A | `B ] = def f x = let xx = id2 x in case xx of { A -> B{} | _ -> xx } //│ f: (A | B) -> B //│ = [Function: f1] // val g : ([< `A | `B > `A ] as 'a) -> 'a = // Actually ~A !! def g x = case x of { A -> id2 x | _ -> x } //│ g: ('a & (A | ~#A)) -> 'a //│ = [Function: g] g (C{}) //│ res: C //│ = C {} // val map : ('a -> 'a) -> 'a list -> 'a list = rec def map f l = case l of { | Nil -> l | Cons -> Cons (f l.head) (map f l.tail) } //│ map: ('head -> ('head0 & 'A)) -> 'a -> 'b //│ where //│ 'a <: (Cons[?] with {head: 'head, tail: 'a}) | Nil & List['A] & 'b //│ 'b :> Cons['A] with {head: 'head0, tail: 'b} //│ = [Function: map] map (fun x -> "lol") (Cons 2 (Cons 3 Nil)) //│ res: Nil | 'a //│ where //│ 'a :> Cons["lol"] with {tail: Nil | 'a} //│ = Cons { head: 'lol', tail: Cons { head: 'lol', tail: Nil {} } } //************** EXAMPLE 3: ROUGH APPROXIMATIONS ************* // let f x = match x with // | (`A, _) -> 1 | (`B, _) -> 2 // | (_, `A) -> 3 | (_, `B) -> 4 // Warning 8 [partial-match]: this pattern-matching is not exhaustive. // Here is an example of a case that is not matched: // (`AnyOtherTag, `AnyOtherTag) // val f : [> `A | `B ] * [> `A | `B ] -> int = // Inferred type is not good enough def f = fun x -> case x._1 of { | A -> 1 | B -> 2 | _ -> case x._2 of { | A -> 3 | B -> 4 } } //│ f: {_1: anything, _2: A | B} -> (1 | 2 | 3 | 4) //│ = [Function: f2] :e f ({ _1 = A{}; _2 = C{} }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.142: f ({ _1 = A{}; _2 = C{} }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `C` does not match type `A | B` //│ ║ l.142: f ({ _1 = A{}; _2 = C{} }) //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.133: | _ -> case x._2 of { //│ ║ ^^^^ //│ ╟── Note: class C is defined at: //│ ║ l.22: class C //│ ╙── ^ //│ res: 1 | 2 | 3 | 4 | error //│ = 1 // x actually has equivalent type {_1: anything; _2: anything} def t1: {_1: A | B; _2: anything} | {_1: anything; _2: A | B} //│ t1: {_1: anything, _2: anything} //│ = :e def f = fun (x: {_1: A | B; _2: anything} | {_1: anything; _2: A | B}) -> case x._1 of { | A -> 1 | B -> 2 | _ -> case x._2 of { | A -> 3 | B -> 4 } } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.168: | _ -> case x._2 of { //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.169: | A -> 3 //│ ║ ^^^^^^^^^^^^ //│ ║ l.170: | B -> 4 //│ ║ ^^^^^^^^^^^^ //│ ║ l.171: } //│ ║ ^^^^^ //│ ╟── type `anything` does not match type `A | B` //│ ║ l.165: def f = fun (x: {_1: A | B; _2: anything} | {_1: anything; _2: A | B}) -> case x._1 of { //│ ║ ^^^^^^^^ //│ ╟── but it flows into field selection with expected type `A | B` //│ ║ l.168: | _ -> case x._2 of { //│ ╙── ^^^^ //│ f: {_1: anything, _2: anything} -> (1 | 2 | 3 | 4) //│ = [Function: f3] // not useful class Tuple[a, b]: { _1: a; _2: b } //│ Defined class Tuple[+a, +b] def t2: Tuple[A | B, anything] | Tuple[anything, A | B] //│ t2: in Tuple[A | B, A | B] out Tuple[?, ?] //│ = def t3: Tuple[anything, anything] //│ t3: Tuple[?, ?] //│ = t3 = t2 t2 = t3 //│ Tuple[?, ?] //│ <: t3: //│ Tuple[?, ?] //│ = //│ t2 is not implemented //│ Tuple[?, ?] //│ <: t2: //│ Tuple[A | B, A | B] //│ = //│ t3 and t2 are not implemented class L: { _1: A | B; _2: anything } class R: { _1: anything; _2: A | B } //│ Defined class L //│ Defined class R :e def f = fun (x: L | R) -> case x._1 of { | A -> 1 | B -> 2 | _ -> case x._2 of { | A -> 3 | B -> 4 } } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.224: | _ -> case x._2 of { //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.225: | A -> 3 //│ ║ ^^^^^^^^^^^^ //│ ║ l.226: | B -> 4 //│ ║ ^^^^^^^^^^^^ //│ ║ l.227: } //│ ║ ^^^^^ //│ ╟── field selection of type `anything` does not match type `A | B` //│ ║ l.224: | _ -> case x._2 of { //│ ╙── ^^^^ //│ f: (L | R) -> (1 | 2 | 3 | 4) //│ = [Function: f4] f (L { _1 = A{}; _2 = C{} }) //│ res: 1 | 2 | 3 | 4 //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/PolyVariantCodeReuse.mls ================================================ // Example from Code reuse through polymorphic variants // * Bad things currently happen without these: // * TODO: investigate why :DontDistributeForalls :DontGeneralizeCurriedFunctions // * This makes inferred types slightly simpler by approximating constraints like // * `'a <: (S -> T) & (U -> V)` to `'a <: (S | U) -> (T & V)`. :ApproximateNegativeFunction //************************** PRELIMINARIES ************************* class NotFound class Success: { result: anything } //│ Defined class NotFound //│ Defined class Success def NotFound = NotFound {} def Success result = Success { result } //│ NotFound: NotFound //│ = [Function: NotFound1] //│ Success: 'result -> (Success with {result: 'result}) //│ = [Function: Success1] type Tuple[A, B] = (A, B) //│ Defined type alias Tuple[+A, +B] def Tuple l r = (l, r) //│ Tuple: 'a -> 'b -> ('a, 'b,) //│ = [Function: Tuple] class Nil class Cons[A]: { head: A; tail: List[A] } type List[A] = Nil | Cons[A] //│ Defined class Nil //│ Defined class Cons[+A] //│ Defined type alias List[+A] def Nil = Nil {} //│ Nil: Nil //│ = [Function: Nil1] def Cons head tail = Cons { head; tail } //│ Cons: ('head & 'A) -> (List['A] & 'tail) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] def eq: string -> string -> bool //│ eq: string -> string -> bool //│ = rec def list_assoc s l = case l of { | Cons -> if eq l.head.0 s then Success l.head.1 else list_assoc s l.tail | Nil -> NotFound } //│ list_assoc: string -> 'a -> (NotFound | (Success with {result: 'result})) //│ where //│ 'a <: (Cons[?] with {head: {0: string, 1: 'result}, tail: 'a}) | Nil //│ = //│ eq is not implemented list_assoc "2" (Cons (Tuple "2" 1) Nil ) //│ res: NotFound | Success & {result: 1} //│ = //│ list_assoc and eq are not implemented // ***************************** Var ******************************* class Expr class Lambda //│ Defined class Expr //│ Defined class Lambda class Var: Lambda & { name: string } //│ Defined class Var def eval_var sub v = case v of { | Var -> let res = list_assoc v.name sub in case res of { | NotFound -> v | Success -> res.result } } //│ eval_var: (Cons[?] & 'a | Nil) -> (Var & 'result) -> 'result //│ where //│ 'a <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'a | Nil} //│ = //│ list_assoc and eq are not implemented // *************************** Lambda ******************************** class Abs[a]: Lambda & { name: string; body: a } class App[a]: Lambda & { lhs: a; rhs: a } //│ Defined class Abs[+a] //│ Defined class App[+a] :js def incr: {a: int} -> unit // def incr x = x.a <- x.a + 1 //│ // Query 1 is empty //│ // End of generated code //│ incr: {a: int} -> unit //│ = :js def gensym: () -> (unit, {a: 'a},) // def gensym = let n = { mut a = 0 } in fun () -> (incr n, n) //│ // Query 1 is empty //│ // End of generated code //│ gensym: () -> (unit, {a: nothing},) //│ = def int_to_string: int -> string //│ int_to_string: int -> string //│ = def eval_lambda eval_rec subst v = case v of { | Var -> eval_var subst v | App -> let l2 = eval_rec subst v.lhs in let l1 = eval_rec subst v.rhs in case l1 of { | Abs -> eval_rec (Cons (Tuple l1.name l2) Nil) l1.body | _ -> App { lhs = l1; rhs = l2 } } | Abs -> let new_name = int_to_string ((gensym ()).1.a) in Abs { name = new_name; body = eval_rec (Cons (Tuple v.name (Var { name = new_name })) subst) v.body } } //│ eval_lambda: (((Cons[('a, Var | 'body,) | 'A] with {head: ('a, Var | 'body,), tail: Nil | 'tail}) | 'tail) -> 'lhs -> ('result & 'body & ((Abs[?] with {body: 'lhs, name: 'a}) | 'lhs0 & ~#Abs))) -> ((Cons[?] & List['A] & 'b | Nil & List['A]) & 'tail) -> ((Abs[?] with {body: 'lhs, name: 'a}) | App[?] & {lhs: 'lhs, rhs: 'lhs} | Var & 'result) -> (Abs['body] | (App['body | 'lhs0] with {lhs: 'lhs0, rhs: 'body}) | 'result) //│ where //│ 'b <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'b | Nil} //│ = //│ eval_var, list_assoc and eq are not implemented rec def eval1 subst = eval_lambda eval1 subst //│ eval1: ('tail & (Cons[?] & List[?] & 'b | Nil & List[?])) -> 'c -> 'd //│ where //│ 'tail <: Cons[?] & 'b | Nil //│ 'b <: {head: {0: string, 1: 'result}, tail: 'tail} //│ 'result :> 'd //│ <: Abs[?] & 'e & 'f | 'lhs & (Abs[?] & 'f & ~#Abs | App[?] & 'g | Var & 'h) //│ 'd :> Abs['d] | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | 'i | 'result //│ 'a :> App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | 'd //│ 'lhs :> App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Var //│ <: 'a & 'c //│ 'c <: Abs[?] & 'f | App[?] & 'g | Var & 'h //│ 'h <: Var & 'i //│ 'i :> Var //│ <: Abs[?] & 'e & 'f | 'lhs & (Abs[?] & 'f & ~#Abs | App[?] & {name: string} & 'g | Var & 'h) //│ 'e <: {body: 'c, name: string} //│ 'g <: {lhs: 'c, rhs: 'c} //│ 'f <: {body: 'c, name: string} //│ 'rhs :> 'd //│ = //│ eval_lambda, eval_var, list_assoc and eq are not implemented // ------------- OCaml's type ------------- // val eval_lambda : // ((string * // ([> `Abs of string * 'a | `App of 'a * 'a | `Var of string ] as 'a)) // list -> 'a -> 'a) -> // (string * 'a) list -> // [< `Abs of string * 'a | `App of 'a * 'a | `Var of string ] -> 'a = // // val eval1 : // (string * ([ `Abs of string * 'a | `App of 'a * 'a | `Var of string ] as 'a)) // list -> 'a -> 'a = // ************************* Expr ****************************** class Numb: Expr & { num: int } class Add[a]: Expr & { lhs: a; rhs: a } class Mul[a]: Expr & { lhs: a; rhs: a } //│ Defined class Numb //│ Defined class Add[+a] //│ Defined class Mul[+a] def map_expr f v = case v of { | Var -> v | Numb -> v | Add -> Add { lhs = f v.lhs; rhs = f v.rhs } | Mul -> Mul { lhs = f v.lhs; rhs = f v.rhs } } //│ map_expr: ('lhs -> 'lhs0) -> (Add[?] & {lhs: 'lhs, rhs: 'lhs} | Mul[?] & {lhs: 'lhs, rhs: 'lhs} | 'a & (Numb | Var)) -> (Add['lhs0] | Mul['lhs0] | 'a) //│ = [Function: map_expr] rec def eval_expr eval_rec subst v = let vv = map_expr (eval_rec subst) v in case vv of { | Var -> eval_var subst vv | Add -> let vv1 = vv.lhs in let vv2 = vv.rhs in case vv1 of { | Numb -> case vv2 of { | Numb -> Numb { num = vv1.num + vv2.num } | _ -> vv } | _ -> vv } | Mul -> let vv1 = vv.lhs in let vv2 = vv.rhs in case vv1 of { | Numb -> case vv2 of { | Numb -> Numb { num = vv1.num * vv2.num } | _ -> vv } | _ -> vv } | Numb -> vv // _ -> vv } //│ eval_expr: ('a -> 'rhs -> 'rhs0) -> ('a & (Cons[?] & 'b | Nil)) -> (Add[?] & {lhs: 'rhs, rhs: 'rhs} | Mul[?] & {lhs: 'rhs, rhs: 'rhs} | Numb & 'result | Var & 'result) -> (Add['rhs0] | Mul['rhs0] | Numb | 'result) //│ where //│ 'b <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'b | Nil} //│ = //│ eval_var, list_assoc and eq are not implemented rec def eval2 subst = eval_expr eval2 subst //│ eval2: (Cons[?] & 'a | Nil) -> 'b -> (Numb | 'result) //│ where //│ 'b <: Add[?] & {lhs: 'b, rhs: 'b} | Mul[?] & {lhs: 'b, rhs: 'b} | Numb & 'result | Var & 'result //│ 'a <: {head: {0: string, 1: 'result & (Numb | ~#Numb)}, tail: Cons[?] & 'a | Nil} //│ 'result :> Mul[Numb | 'result] | Add[Numb | 'result] //│ = //│ eval_expr, eval_var, list_assoc and eq are not implemented // ------------- OCaml's type ------------- // val map_expr : // ('a -> 'b) -> // [< `Add of 'a * 'a | `Mult of 'a * 'a | `Numb of 'c | `Var of string ] -> // [> `Add of 'b * 'b | `Mult of 'b * 'b | `Numb of 'c | `Var of string ] = // val eval_expr : // ((string * // ([> `Add of ([> `Numb of int ] as 'b) * 'b // | `Mult of 'b * 'b // | `Numb of int // | `Var of string ] // as 'a)) // list -> 'c -> 'b) -> // (string * 'a) list -> // [< `Add of 'c * 'c | `Mult of 'c * 'c | `Numb of int | `Var of string ] -> 'a = // // val eval2 : // (string * // ([> `Add of 'a * 'a | `Mult of 'a * 'a | `Numb of int | `Var of string ] // as 'a)) // list -> // ([< `Add of 'b * 'b | `Mult of 'b * 'b | `Numb of int | `Var of string ] as 'b) -> // 'a = // ************************** Mix things ******************************* def eval_lexpr eval_rec subst v = case v of { | Lambda -> eval_lambda eval_rec subst v | Expr -> eval_expr eval_rec subst v } //│ eval_lexpr: ((Cons[('a, Var | 'body,) | 'A]\head\tail & {head: ('a, Var | 'body,), tail: Nil | 'tail} | 'tail) -> 'lhs -> ('result & 'body & (Abs[?]\body\name & {body: 'lhs, name: 'a} | 'lhs0 & ~#Abs))) -> ((Cons[?] & List['A] & 'b & 'c | Nil & List['A]) & 'tail) -> (Abs[?]\body\name & {body: 'lhs, name: 'a} | Add[?] & {lhs: 'lhs, rhs: 'lhs} | App[?] & {lhs: 'lhs, rhs: 'lhs} | Mul[?] & {lhs: 'lhs, rhs: 'lhs} | Numb & 'result | Var & 'result) -> (Abs['body] | Add['body] | App['body | 'lhs0]\lhs\rhs & {lhs: 'lhs0, rhs: 'body} | Mul['body] | Numb | 'result) //│ where //│ 'c <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'c | Nil} //│ 'b <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'b | Nil} //│ = //│ eval_lambda, eval_var, list_assoc and eq are not implemented rec def eval3 subst = eval_lexpr eval3 subst //│ eval3: ('tail & 'tail0 & (Cons[?] & List[?] & 'b & 'c | Nil & List[?])) -> 'd -> 'e //│ where //│ 'tail0 <: Cons[?] & 'c | Nil //│ 'c <: {head: {0: string, 1: 'result}, tail: 'tail0} //│ 'tail <: Cons[?] & 'b | Nil //│ 'b <: {head: {0: string, 1: 'result}, tail: 'tail} //│ 'result :> Numb | Var | 'e //│ <: Abs[?] & 'f & 'g | 'lhs & (Lambda & 'f & ~#Abs | 'h & (Expr & ~#Numb | Numb)) //│ 'e :> Abs['e] | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | Numb | 'result | (Add['e] with {lhs: 'e, rhs: 'e}) | (Mul['e] with {lhs: 'e, rhs: 'e}) | 'i //│ 'a :> (Add['e] with {lhs: 'e, rhs: 'e}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['e] with {lhs: 'e, rhs: 'e}) | Numb | 'e //│ 'lhs :> (Add['e] with {lhs: 'e, rhs: 'e}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['e] with {lhs: 'e, rhs: 'e}) | Numb | Var //│ <: 'd & 'a //│ 'd <: Expr & 'h | Lambda & 'f //│ 'h <: Add[?] & {lhs: 'd, rhs: 'd} | Mul[?] & {lhs: 'd, rhs: 'd} | Numb & 'result | Var & 'f & 'lhs //│ 'f <: Abs[?] & {body: 'd} | App[?] & {lhs: 'd, rhs: 'd} | Var & 'i //│ 'i :> Var //│ <: Abs[?] & 'f & 'g | 'lhs & (Lambda & {name: string} & 'f & ~#Abs | 'h & (Expr & {name: string} & ~#Numb | Numb & {name: string})) //│ 'g <: {body: 'd, name: string} //│ 'rhs :> 'e //│ = //│ eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented // ------------- OCaml's type ------------- // val eval_lexpr : // ((string * // ([> `Abs of string * 'a // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a // | `Numb of int // | `Var of string ] // as 'a)) // list -> 'a -> 'a) -> // (string * 'a) list -> // [< `Abs of string * 'a // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a // | `Numb of int // | `Var of string ] -> // 'a = // // val eval3 : // (string * // ([ `Abs of string * 'a // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a // | `Numb of int // | `Var of string ] as 'a)) // list -> 'a -> 'a = // ************************** Tests ******************************* eval3 Nil (Var { name = "s" }) //│ res: 'b //│ where //│ 'b :> Abs['b] | (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'a :> 'lhs | 'b //│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented // ------------- OCaml's type ------------- // - : [ `Abs of string * 'a // | `Add of 'a * 'a // | `App of 'a * 'a // | `Mult of 'a * 'a // | `Numb of int // | `Var of string ] as 'a // = `Var "s" eval3 Nil (Abs { name = "s"; body = Var { name = "s" } }) //│ res: 'b //│ where //│ 'b :> Abs['b] | (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'a :> 'lhs | 'b //│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented eval2 Nil (Numb { num = 1 }) //│ res: Numb | 'a //│ where //│ 'a :> Add[Numb | 'a] | Mul[Numb | 'a] //│ = //│ eval2, eval_expr, eval_var, list_assoc and eq are not implemented eval3 Nil (Numb { num = 1 }) //│ res: 'b //│ where //│ 'b :> Abs['b] | (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'a :> 'lhs | 'b //│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented eval3 Nil (App { lhs = Numb {num = 0}; rhs = Numb {num = 0}}) //│ res: 'b //│ where //│ 'b :> Abs['b] | (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'a :> 'lhs | 'b //│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented eval3 Nil (Abs { name = "s"; body = Add { lhs = Var { name = "s" }; rhs = Numb { num = 1 } } }) //│ res: 'b //│ where //│ 'b :> Abs['b] | (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'a :> 'lhs | 'b //│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'rhs :> 'b //│ = //│ eval3, eval_lexpr, eval_lambda, eval_var, list_assoc and eq are not implemented // ************************************* // * Note: the type size explodes and the test times out if we incorrectly call // * `eval_var eval_rec subst v` instead of `eval_var subst v`. // * This is a problem, and we should find a way to make it less so. def eval_lexpr' eval_rec subst v = case v of { | Var -> eval_var subst v | Abs -> eval_lambda eval_rec subst v | App -> eval_lambda eval_rec subst v | Numb -> eval_expr eval_rec subst v | Add -> eval_expr eval_rec subst v | Mul -> eval_expr eval_rec subst v } //│ eval_lexpr': ((Cons[('b, Var | 'body,) | 'A]\head\tail & {head: ('b, Var | 'body,), tail: Nil | 'tail} | 'tail) -> 'body0 -> ('result & 'body & (Abs[?]\body\name & {body: 'body0, name: 'b} | 'lhs & 'a & (Abs[?]\body\name & {body: 'body0, name: 'b} & ~#Abs | 'lhs & 'a & ~#Abs)))) -> ((Cons[?] & List['A] & 'c & 'd & 'e & 'f & 'g | Nil & List['A]) & 'tail & (Cons[?] & 'h | Nil)) -> (Abs[?]\body\name & {body: 'body0, name: 'b} | Add[?] & {lhs: 'body0, rhs: 'body0} | App[?] & {lhs: 'body0, rhs: 'body0} | Mul[?] & {lhs: 'body0, rhs: 'body0} | Numb & 'result | Var & 'result) -> (Abs['body] | Add['body] | App['body | 'a]\lhs\rhs & {lhs: 'lhs, rhs: 'body} | Mul['body] | Numb | 'result) //│ where //│ 'h <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'h | Nil} //│ 'g <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'g | Nil} //│ 'f <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'f | Nil} //│ 'e <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'e | Nil} //│ 'd <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'd | Nil} //│ 'c <: {head: {0: string, 1: 'result}, tail: Cons[?] & 'c | Nil} //│ = //│ eval_var, list_assoc and eq are not implemented :e rec def eval4 subst = eval_lexpr' eval4 subst //│ ╔══[ERROR] Subtyping constraint of the form `?a -> ?b <: ?eval4` took too many steps and ran out of fuel (10000) //│ ║ l.417: rec def eval4 subst = eval_lexpr' eval4 subst //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ eval4: ('tail & 'tail0 & 'tail1 & 'tail2 & 'tail3 & 'tail4 & (Cons[?] & List[?] & 'b & 'c & 'd & 'e & 'f & 'g | Nil & List[?])) -> 'h -> 'i //│ where //│ 'tail4 <: Cons[?] & 'g | Nil //│ 'g <: {head: {0: string, 1: 'result}, tail: 'tail4} //│ 'tail3 <: Cons[?] & 'f | Nil //│ 'f <: {head: {0: string, 1: 'result0}, tail: 'tail3} //│ 'tail2 <: Cons[?] & 'e | Nil //│ 'e <: {head: {0: string, 1: 'result0}, tail: 'tail2} //│ 'tail1 <: Cons[?] & 'd | Nil //│ 'd <: {head: {0: string, 1: 'result0}, tail: 'tail1} //│ 'tail0 <: Cons[?] & 'c | Nil //│ 'c <: {head: {0: string, 1: 'result}, tail: 'tail0} //│ 'tail <: Cons[?] & 'b | Nil //│ 'b <: {head: {0: string, 1: 'result}, tail: 'tail} //│ 'result :> 'i //│ <: Abs[?] & 'j & 'k | 'lhs & (Abs[?] & 'k & ~#Abs | Add[?] & 'l | App[?] & 'm | Mul[?] & 'n | Numb & 'o | Var & 'p) //│ 'i :> Abs['i] | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Numb | 'result0 | Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | 'q | 'result //│ 'a :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | 'i //│ 'lhs :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a]\lhs\rhs & {lhs: 'lhs, rhs: 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | Var //│ <: 'h & 'a //│ 'h <: Abs[?] & 'k | Add[?] & 'l | App[?] & 'm | Mul[?] & 'n | Numb & 'o | Var & 'p //│ 'k <: Abs[?] | App[?] | Var & 'q //│ 'q :> Var //│ <: Abs[?] & 'j & 'k | 'lhs & (Abs[?] & 'k & ~#Abs | Add[?] & {name: string} & 'l | App[?] & {name: string} & 'm | Mul[?] & {name: string} & 'n | Numb & {name: string} & 'o | Var & 'p) //│ 'l <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result0 | Var & 'p & 'lhs //│ 'result0 :> Numb | Var | 'i //│ <: Abs[?] & 'j & 'k | 'lhs & (Add[?] & 'l | App[?] & 'm | Mul[?] & 'n | Numb & 'o | Var & 'p) //│ 'o <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result0 | Var & 'p & 'lhs //│ 'n <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result0 | Var & 'p & 'lhs //│ 'p <: Var & 'q //│ 'm <: Abs[?] & {body: 'h} | App[?] & {lhs: 'h, rhs: 'h} | Var & 'q //│ 'j <: {body: 'h, name: string} //│ 'rhs :> 'i //│ = //│ eval_lexpr', eval_var, list_assoc and eq are not implemented :Fuel 20000 :stats rec def eval4 subst = eval_lexpr' eval4 subst //│ eval4: ('tail & 'tail0 & 'tail1 & 'tail2 & 'tail3 & 'tail4 & (Cons[?] & List[?] & 'b & 'c & 'd & 'e & 'f & 'g | Nil & List[?])) -> 'h -> 'i //│ where //│ 'tail4 <: Cons[?] & 'g | Nil //│ 'g <: {head: {0: string, 1: 'result}, tail: 'tail4} //│ 'tail3 <: Cons[?] & 'f | Nil //│ 'f <: {head: {0: string, 1: 'result0}, tail: 'tail3} //│ 'tail2 <: Cons[?] & 'e | Nil //│ 'e <: {head: {0: string, 1: 'result0}, tail: 'tail2} //│ 'tail1 <: Cons[?] & 'd | Nil //│ 'd <: {head: {0: string, 1: 'result0}, tail: 'tail1} //│ 'tail0 <: Cons[?] & 'c | Nil //│ 'c <: {head: {0: string, 1: 'result}, tail: 'tail0} //│ 'tail <: Cons[?] & 'b | Nil //│ 'b <: {head: {0: string, 1: 'result}, tail: 'tail} //│ 'result :> 'i //│ <: Abs[?] & 'j & 'k & 'l | 'lhs & (Abs[?] & 'k & 'l & ~#Abs | 'lhs0 & (Add[?] & 'm | App[?] & 'n | Mul[?] & 'o | Numb & 'p | Var & 'q)) //│ 'i :> Abs['i] | App['a]\lhs\rhs & {lhs: 'lhs | 'lhs0, rhs: 'rhs} | Numb | 'result0 | Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | 'r | 'result //│ 'a :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | 'i //│ 'lhs0 :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | Var //│ <: 'h & 'a //│ 'lhs :> Add['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | App['a]\lhs\rhs & {lhs: 'lhs0 | 'lhs, rhs: 'rhs} | Mul['i]\lhs\rhs & {lhs: 'i, rhs: 'i} | Numb | Var //│ <: 'h & 'a //│ 'h <: Abs[?] & 'k | Add[?] & 'm | App[?] & 'n | Mul[?] & 'o | Numb & 'p | Var & 'q //│ 'k <: Abs[?] & {body: 'h} | App[?] & {lhs: 'h, rhs: 'h} | Var & 'r //│ 'r :> Var //│ <: Abs[?] & 'j & 'k & 'l | 'lhs & (Abs[?] & 'k & 'l & ~#Abs | 'lhs0 & (Add[?] & {name: string} & 'm | App[?] & {name: string} & 'n | Mul[?] & {name: string} & 'o | Numb & {name: string} & 'p | Var & 'q)) //│ 'm <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result0 | Var & 'q & 'lhs & 'lhs0 //│ 'result0 :> Numb | Var | 'i //│ <: Abs[?] & 'j & 'k & 'l | 'lhs & 'lhs0 & (Add[?] & 'm | App[?] & 'n | Mul[?] & 'o | Numb & 'p | Var & 'q) //│ 'p <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result0 | Var & 'q & 'lhs & 'lhs0 //│ 'o <: Add[?] & {lhs: 'h, rhs: 'h} | Mul[?] & {lhs: 'h, rhs: 'h} | Numb & 'result0 | Var & 'q & 'lhs & 'lhs0 //│ 'q <: Var & 'r //│ 'n <: Abs[?] & {body: 'h} | App[?] & {lhs: 'h, rhs: 'h} | Var & 'r //│ 'l <: {body: 'h, name: string} //│ 'j <: {body: 'h, name: string} //│ 'rhs :> 'i //│ = //│ eval_lexpr', eval_var, list_assoc and eq are not implemented //│ constrain calls : 10599 //│ annoying calls : 2300 //│ subtyping calls : 319664 :ResetFuel eval4 Nil (Abs { name = "s"; body = Add { lhs = Var { name = "s" }; rhs = Numb { num = 1 } } }) //│ res: 'b //│ where //│ 'b :> Abs['b] | (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'a :> 'lhs | 'b //│ 'lhs :> (Add['b] with {lhs: 'b, rhs: 'b}) | (App['a] with {lhs: 'lhs, rhs: 'rhs}) | (Mul['b] with {lhs: 'b, rhs: 'b}) | Numb | Var //│ 'rhs :> 'b //│ = //│ eval4, eval_lexpr', eval_var, list_assoc and eq are not implemented ================================================ FILE: shared/src/test/diff/mlscript/Polym.mls ================================================ def foo f = f (fun x -> x) //│ foo: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: foo] def bar h = (h 0, h true) //│ bar: (0 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: bar] bar id //│ res: (0, true,) //│ = [ 0, true ] foo bar //│ res: (0, true,) //│ = [ 0, true ] def foo2 f = f id //│ foo2: ((forall 'a. 'a -> 'a) -> 'b) -> 'b //│ = [Function: foo2] foo2 bar //│ res: (0, true,) //│ = [ 0, true ] def foo x f = f (fun a -> (x, a)) //│ foo: 'a -> ((forall 'b. 'b -> ('a, 'b,)) -> 'c) -> 'c //│ = [Function: foo1] // Swapping the parameters givesa an isomorphic type: def foo f x = f (fun a -> (x, a)) //│ foo: ((forall 'a. 'a -> ('b, 'a,)) -> 'c) -> 'b -> 'c //│ = [Function: foo3] ================================================ FILE: shared/src/test/diff/mlscript/PolymExtrusion.mls ================================================ :NoConstrainedTypes :DontDistributeForalls :NoCycleCheck :NoRecursiveTypes def foo: (forall 'a. 'a -> 'a) -> int //│ foo: (forall 'a. 'a -> 'a) -> int //│ = foo f = f 42 //│ (42 -> 'a) -> 'a //│ <: foo: //│ (forall 'a. 'a -> 'a) -> int //│ = [Function: foo] foo id //│ res: int //│ = 42 // Notice `f` is passed an arg of locally-quantified type 'a, which is extruded to `anything`: bar f = foo (fun x -> let tmp = f x in x) //│ bar: (??a -> anything) -> int //│ = [Function: bar] bar (fun x -> 0) //│ res: int //│ = 42 :e bar succ //│ ╔══[ERROR] Type error in application //│ ║ l.32: bar succ //│ ║ ^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.8: def foo: (forall 'a. 'a -> 'a) -> int //│ ║ ^^ //│ ╟── into type `int` //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.23: bar f = foo (fun x -> let tmp = f x in x) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | int //│ = 42 :e bar (fun x -> case x of {}) //│ ╔══[ERROR] Type error in application //│ ║ l.48: bar (fun x -> case x of {}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'a` leaks out of its scope //│ ║ l.8: def foo: (forall 'a. 'a -> 'a) -> int //│ ║ ^^ //│ ╟── into reference of type `nothing` //│ ║ l.48: bar (fun x -> case x of {}) //│ ║ ^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.23: bar f = foo (fun x -> let tmp = f x in x) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | int //│ Runtime error: //│ Error: non-exhaustive case expression def f: 'a -> 'b //│ f: anything -> nothing //│ = def foo: (forall 'a. 'a -> 'b) -> 'b //│ foo: (anything -> 'b) -> 'b //│ = // Notice `id` returns its arg of locally-quantified type 'a, which is extruded to `anything`: foo id //│ res: ??a //│ = //│ foo is not implemented :e foo (fun a -> a + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.83: foo (fun a -> a + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.71: def foo: (forall 'a. 'a -> 'b) -> 'b //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.83: foo (fun a -> a + 1) //│ ║ ^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.71: def foo: (forall 'a. 'a -> 'b) -> 'b //│ ╙── ^^ //│ res: error | int //│ = //│ foo is not implemented ================================================ FILE: shared/src/test/diff/mlscript/PolymorphicExtension.mls ================================================ // * Here we compare to OCaml's polymorphic variants, showing their limitation class Apple class Banana //│ Defined class Apple //│ Defined class Banana // utop # let foo x default otherwise = match x with `Apple -> default | y -> otherwise y ;; // val foo : ([> `Apple ] as 'a) -> 'b -> ('a -> 'b) -> 'b = def foo x default otherwise = case x of { Apple -> default | _ -> otherwise x } //│ foo: (Apple | 'a & ~#Apple) -> 'b -> ('a -> 'b) -> 'b //│ = [Function: foo] // utop # foo `Apple ;; // - : '_a -> (_[> `Apple ] -> '_a) -> '_a = foo (Apple{}) //│ res: 'a -> (nothing -> 'a) -> 'a //│ = [Function (anonymous)] // utop # foo `Banana ;; // - : '_a -> (_[> `Apple | `Banana ] -> '_a) -> '_a = foo (Banana{}) //│ res: 'a -> (Banana -> 'a) -> 'a //│ = [Function (anonymous)] // utop # foo `Banana 0 (function `Banana -> 1) ;; // Characters 9-27: // Warning 8: this pattern-matching is not exhaustive. // Here is an example of a value that is not matched: // `Apple // Characters 9-27: // Warning 8: this pattern-matching is not exhaustive. // Here is an example of a value that is not matched: // `Apple // - : int = 1 foo (Banana{}) 0 (fun z -> case z of { Banana -> 1 }) //│ res: 0 | 1 //│ = 1 // utop # foo `Apple 0 (function `Apple -> 1) ;; // - : int = 0 foo (Apple{}) 0 (fun z -> case z of { Apple -> 1 }) //│ res: 0 | 1 //│ = 0 foo (Apple{}) 0 (fun z -> z.lol) // z has type `nothing` so we can do whatever we want in this unreachable case //│ res: 0 //│ = 0 ================================================ FILE: shared/src/test/diff/mlscript/PreservationFail.mls ================================================ // * Strictly speaking, type preservation does not hold in the MLscript implementation, // * though this is merely a technicality that can be addressed in the formal language. // * Consider the following example: class C1 class C2: C1 class C3 //│ Defined class C1 //│ Defined class C2 //│ Defined class C3 def foo x = case x of { | C2 -> C3{} | _ -> x } //│ foo: (C2 | 'a & ~#C2) -> (C3 | 'a) //│ = [Function: foo] c1 = C1{} //│ c1: C1 //│ = C1 {} r = foo c1 //│ r: C1 & ~#C2 | C3 //│ = C1 {} // * Here we have the C1{} value typed at ~C2 r : ~C2 //│ res: ~C2 //│ = C1 {} // * But that C1{} value cannot be typed at ~C2 directly! :e c1 : ~C2 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.35: c1 : ~C2 //│ ║ ^^ //│ ╟── application of type `C1` does not match type `~C2` //│ ║ l.20: c1 = C1{} //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `~C2` //│ ║ l.35: c1 : ~C2 //│ ║ ^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.35: c1 : ~C2 //│ ║ ^^^ //│ ╟── Note: class C1 is defined at: //│ ║ l.6: class C1 //│ ╙── ^^ //│ res: ~C2 //│ = C1 {} // * The problem is resolved by saying that when instantiating a non-final class C1, // * we are really instantiating a hidden final C1_Impl version of it: class C1_Impl: C1 //│ Defined class C1_Impl c1 = C1_Impl{} //│ c1: C1_Impl //│ = C1_Impl {} r = foo c1 //│ r: C1_Impl | C3 //│ = C1_Impl {} r : ~C2 //│ res: ~C2 //│ = C1_Impl {} c1 : ~C2 //│ res: ~C2 //│ = C1_Impl {} // * More concerning: trait constructors are unsound as long as we allow matching on traits! // * Trait constructors were always a fringe feature that we wanted to eventually remove anyway. trait T1 //│ Defined trait T1 rec def getNegT1 x = case x of { | T1 -> getNegT1 x | _ -> x } //│ getNegT1: (#T1 | 'a & ~#T1) -> 'a //│ = [Function: getNegT1] nt1 = getNegT1 {} //│ nt1: ~#T1 //│ = {} T1 //│ res: 'a -> (#T1 & 'a) //│ = [Function: build] t1nt1 = T1 nt1 //│ t1nt1: nothing //│ = {} :re t1nt1.lol "oops!" //│ res: nothing //│ Runtime error: //│ TypeError: t1nt1.lol is not a function ================================================ FILE: shared/src/test/diff/mlscript/ProvFlows.mls ================================================ :AllowTypeErrors def succ: int -> int //│ succ: int -> int x1 = false x2 = x1 x3 = x2 //│ x1: false //│ x2: false //│ x3: false :ex succ x3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.15: succ x3 //│ ║ ^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.7: x1 = false //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.15: succ x3 //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from type `false` //│ ╟── [info] flowing from reference of type `false` //│ ║ l.7: x1 = false //│ ║ ^^^^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.8: x2 = x1 //│ ║ ^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.9: x3 = x2 //│ ║ ^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.15: succ x3 //│ ║ ^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int f1 y1 = succ y1 f2 y2 = f1 y2 f3 y3 = f2 y3 //│ f1: int -> int //│ f2: int -> int //│ f3: int -> int :ex f3 true //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.59: f3 true //│ ║ ^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.59: f3 true //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.53: f3 y3 = f2 y3 //│ ║ ^^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from type `true` //│ ╟── [info] flowing from reference of type `true` //│ ║ l.59: f3 true //│ ║ ^^^^ //│ ╟── [info] flowing into variable of type `?a` //│ ╟── [info] flowing into variable of type `?b` //│ ╟── [info] flowing into reference of type `?c` //│ ║ l.53: f3 y3 = f2 y3 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?d` //│ ╟── [info] flowing into variable of type `?e` //│ ╟── [info] flowing into reference of type `?f` //│ ║ l.52: f2 y2 = f1 y2 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?g` //│ ╟── [info] flowing into variable of type `int` //│ ╟── [info] flowing into reference of type `int` //│ ║ l.51: f1 y1 = succ y1 //│ ║ ^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int :ex f3 x3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.102: f3 x3 //│ ║ ^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.7: x1 = false //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.102: f3 x3 //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.53: f3 y3 = f2 y3 //│ ║ ^^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from type `false` //│ ╟── [info] flowing from reference of type `false` //│ ║ l.7: x1 = false //│ ║ ^^^^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.8: x2 = x1 //│ ║ ^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.9: x3 = x2 //│ ║ ^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.102: f3 x3 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?a` //│ ╟── [info] flowing into variable of type `?b` //│ ╟── [info] flowing into reference of type `?c` //│ ║ l.53: f3 y3 = f2 y3 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?d` //│ ╟── [info] flowing into variable of type `?e` //│ ╟── [info] flowing into reference of type `?f` //│ ║ l.52: f2 y2 = f1 y2 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?g` //│ ╟── [info] flowing into variable of type `int` //│ ╟── [info] flowing into reference of type `int` //│ ║ l.51: f1 y1 = succ y1 //│ ║ ^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int h1 f x = f x h2 f x = h1 f x h3 f x = h2 f x //│ h1: ('a -> 'b) -> 'a -> 'b //│ h2: ('a -> 'b) -> 'a -> 'b //│ h3: ('a -> 'b) -> 'a -> 'b :ex h3 f3 x3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.164: h3 f3 x3 //│ ║ ^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.7: x1 = false //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.164: h3 f3 x3 //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.158: h3 f x = h2 f x //│ ║ ^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from type `false` //│ ╟── [info] flowing from reference of type `false` //│ ║ l.7: x1 = false //│ ║ ^^^^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.8: x2 = x1 //│ ║ ^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.9: x3 = x2 //│ ║ ^^ //│ ╟── [info] flowing from reference of type `false` //│ ║ l.164: h3 f3 x3 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?a` //│ ╟── [info] flowing into variable of type `?b` //│ ╟── [info] flowing into reference of type `?c` //│ ║ l.158: h3 f x = h2 f x //│ ║ ^ //│ ╟── [info] flowing into variable of type `?d` //│ ╟── [info] flowing into variable of type `?e` //│ ╟── [info] flowing into reference of type `?f` //│ ║ l.157: h2 f x = h1 f x //│ ║ ^ //│ ╟── [info] flowing into variable of type `?g` //│ ╟── [info] flowing into variable of type `?h` //│ ╟── [info] flowing into reference of type `?i` //│ ║ l.156: h1 f x = f x //│ ║ ^ //│ ╟── [info] flowing into variable of type `?j` //│ ╟── [info] flowing into variable of type `?k` //│ ╟── [info] flowing into reference of type `?l` //│ ║ l.53: f3 y3 = f2 y3 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?m` //│ ╟── [info] flowing into variable of type `?n` //│ ╟── [info] flowing into reference of type `?o` //│ ║ l.52: f2 y2 = f1 y2 //│ ║ ^^ //│ ╟── [info] flowing into variable of type `?p` //│ ╟── [info] flowing into variable of type `int` //│ ╟── [info] flowing into reference of type `int` //│ ║ l.51: f1 y1 = succ y1 //│ ║ ^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int :ex (fun x -> succ x) false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.234: (fun x -> succ x) false //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.234: (fun x -> succ x) false //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.234: (fun x -> succ x) false //│ ║ ^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from type `false` //│ ╟── [info] flowing from reference of type `false` //│ ║ l.234: (fun x -> succ x) false //│ ║ ^^^^^ //│ ╟── [info] flowing into variable of type `?a` //│ ╟── [info] flowing into variable of type `int` //│ ╟── [info] flowing into reference of type `int` //│ ║ l.234: (fun x -> succ x) false //│ ║ ^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int // :d :ex rec def x = add x //│ ╔══[ERROR] Type mismatch in binding of application: //│ ║ l.267: rec def x = add x //│ ║ ^^^^^ //│ ╟── application of type `int -> int` is not an instance of type `int` //│ ╟── Note: constraint arises from reference: //│ ║ l.267: rec def x = add x //│ ║ ^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from expression of type `int -> int` //│ ╟── [info] flowing from application of type `int -> int` //│ ║ l.267: rec def x = add x //│ ║ ^^^^^ //│ ╟── [info] flowing from application of type `?a` //│ ║ l.267: rec def x = add x //│ ║ ^^^^^ //│ ╟── [info] flowing into expression of type `?x` //│ ╟── [info] flowing into reference of type `int` //│ ║ l.267: rec def x = add x //│ ║ ^ //│ ╙── [info] flowing into type `int` //│ x: int -> int def foo: int | string //│ foo: int | string :ex succ foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.295: succ foo //│ ║ ^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.291: def foo: int | string //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.295: succ foo //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from type `string` //│ ║ l.291: def foo: int | string //│ ║ ^^^^^^ //│ ╟── [info] flowing from type `string` //│ ║ l.291: def foo: int | string //│ ║ ^^^^^^ //│ ╟── [info] flowing from type `int | string` //│ ║ l.291: def foo: int | string //│ ║ ^^^^^^^^^^^^ //│ ╟── [info] flowing from reference of type `int | string` //│ ║ l.295: succ foo //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ║ ^^^ //│ ╟── [info] flowing into type `int` //│ ║ l.3: def succ: int -> int //│ ╙── ^^^ //│ res: error | int class A class B //│ Defined class A //│ Defined class B def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ty00: (A & 'a | B & 'b) -> ('a, 'b,) def ty11: ('a & A | 'a & B) -> ('a, 'a) //│ ty11: ('a & (A | B)) -> ('a, 'a,) ty11 = ty00 //│ (A & 'a | B & 'b) -> ('a, 'b,) //│ <: ty11: //│ ('a & (A | B)) -> ('a, 'a,) // :d :ex ty00 = ty11 //│ ('a & (A | B)) -> ('a, 'a,) //│ <: ty00: //│ (A & 'a | B & 'b) -> ('a, 'b,) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.349: ty00 = ty11 //│ ║ ^^^^^^^^^^^ //│ ╟── type `B & 'b` does not match type `'a` //│ ║ l.336: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.336: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^ //│ ╟── ========= Additional explanations below ========= //│ ╟── [info] flowing from expression of type `B & 'b` //│ ╟── [info] flowing from type `B & 'b` //│ ║ l.336: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^^^^^ //│ ╟── [info] flowing from type `B & 'b` //│ ║ l.336: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── [info] flowing from type `B & 'b` //│ ║ l.339: def ty11: ('a & A | 'a & B) -> ('a, 'a) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── [info] flowing from expression of type `'a0` //│ ╟── [info] flowing from type `'a1` //│ ║ l.339: def ty11: ('a & A | 'a & B) -> ('a, 'a) //│ ║ ^^ //│ ╟── [info] flowing into type `'a` //│ ║ l.336: def ty00: ('a & A | 'b & B) -> ('a, 'b) //│ ║ ^^ //│ ╙── [info] flowing into expression of type `'a` ================================================ FILE: shared/src/test/diff/mlscript/Random1.mls ================================================ :w // class G[A]: { a: G[A] & A & G[A] } class G[A]: { a: G[A] } //│ Defined class G[±A] //│ ╔══[WARNING] Type definition G has bivariant type parameters: //│ ║ l.4: class G[A]: { a: G[A] } //│ ║ ^ //│ ╟── A is irrelevant and may be removed //│ ║ l.4: class G[A]: { a: G[A] } //│ ╙── ^ def g: G['a] & {a: 'a} as 'a //│ g: 'a //│ where //│ 'a :> G[?] with {a: 'a} //│ = :NoJS rec def g2 a = G { a } //│ g2: (G[?] & 'a) -> (G[?] with {a: 'a}) :e g = g2 //│ (G[?] & 'a) -> (G[?] with {a: 'a}) //│ <: g: //│ 'a //│ where //│ 'a :> G[?] with {a: 'a} //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.25: g = g2 //│ ║ ^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `G` //│ ║ l.21: rec def g2 a = G { a } //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `G[?]` //│ ║ l.25: g = g2 //│ ║ ^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.13: def g: G['a] & {a: 'a} as 'a //│ ║ ^^^^^ //│ ╟── from local type binding: //│ ║ l.13: def g: G['a] & {a: 'a} as 'a //│ ╙── ^^^^^^^^^^^^^^^ rec def g2 = G { a = g2 } //│ g2: 'a //│ where //│ 'a :> G[?] with {a: 'a} g = g2 //│ 'a //│ where //│ 'a :> G[?] with {a: 'a} //│ <: g: //│ 'a //│ where //│ 'a :> G[?] with {a: 'a} g = g //│ 'a //│ where //│ 'a :> G[?] with {a: 'a} //│ <: g: //│ 'a //│ where //│ 'a :> G[?] with {a: 'a} def manual: 'a -> nothing as 'a //│ manual: 'a //│ where //│ 'a := 'a -> nothing :e manual id //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.77: manual id //│ ║ ^^^^^^^^^ //│ ╟── type `'a -> nothing` does not match type `nothing` //│ ║ l.71: def manual: 'a -> nothing as 'a //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.71: def manual: 'a -> nothing as 'a //│ ╙── ^^^^^^^ //│ res: error def manual: 'a -> anything as 'a //│ manual: 'a //│ where //│ 'a := 'a -> anything manual id //│ res: anything def f: { mut x: int } //│ f: {mut x: int} f: { mut x: int } //│ res: {mut x: int} f = { mut x = 1 } //│ {mut x: 'x} //│ where //│ 'x :> 1 //│ <: f: //│ {mut x: int} ================================================ FILE: shared/src/test/diff/mlscript/Random2.mls ================================================ :NoJS // :stdout // :s // :d // :ds def f: { x: 'a } as 'a //│ f: 'a //│ where //│ 'a :> {x: 'a} // :stdout // :s // :d // :ds rec def l x = l //│ l: 'l //│ where //│ 'l :> anything -> 'l l //│ res: 'l //│ where //│ 'l :> anything -> 'l :ns m y = l y //│ m: forall 'a 'b 'c. 'a -> 'b //│ where //│ 'b :> forall 'l. 'l //│ 'l := 'c -> 'l //│ 'a <: 'c m //│ res: 'l //│ where //│ 'l :> anything -> 'l fun a -> a a //│ res: ('a -> 'b & 'a) -> 'b // :stdout // :s // :d // :ds :e rec def f a = a a //│ ('a -> 'b & 'a) -> 'b //│ <: f: //│ 'a //│ where //│ 'a :> {x: 'a} //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.51: rec def f a = a a //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `forall ?f. ?f` does not match type `{x: 'a}` //│ ╟── Note: constraint arises from record type: //│ ║ l.8: def f: { x: 'a } as 'a //│ ╙── ^^^^^^^^^ // :stdout // :s // :d // :ds rec def g a = a a //│ g: ('a -> 'b & 'a) -> 'b // :ds def ite x y z = if x then y else z //│ ite: bool -> 'a -> 'a -> 'a class Some[A]: { v: A } //│ Defined class Some[+A] def Some v = Some{v} //│ Some: 'v -> Some['v] def foo(x: Some['a]) = let _ = x.v : Some[?] in x: Some['a & some] //│ foo: Some[Some[nothing] & 'a] -> Some[Some[?] & 'a] def f: Some['a] //│ f: Some[nothing] def foo(x: 'a) = (x + 1, x) //│ foo: (int & 'a) -> (int, 'a,) // * NOTE: currently each parameter type is typed independently – so these 'a are different! def foo(x: 'a, y: 'a) = (x + 1, y) //│ foo: (int, 'a,) -> (int, 'a,) def foo(f: 'a -> 'a, x: 'a) = let _ = x + 1 in error: Some['a & some] //│ foo: ('a -> 'a, int,) -> Some[nothing] ================================================ FILE: shared/src/test/diff/mlscript/RecDefs.mls ================================================ rec def f x = x //│ f: 'a -> 'a //│ = [Function: f] f 1 //│ res: 1 //│ = 1 f 2 //│ res: 2 //│ = 2 rec def f = if true then fun (x: 'a) -> 32 else let tmp = f f + 1 in error //│ f: anything -> 32 //│ = [Function: f1] :e rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.22: rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `'a -> 32` does not match type `'a` //│ ║ l.22: rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from rigid type variable: //│ ║ l.22: rec def f = forall 'a. (fun (x: 'a) -> 32) : 'a //│ ╙── ^^ //│ f: nothing //│ = [Function: f2] ================================================ FILE: shared/src/test/diff/mlscript/RecErrors.mls ================================================ :AllowTypeErrors :ShowRelativeLineNums rec def f n = if n then 0 else f (miss + 1) //│ ╔══[ERROR] identifier not found: miss //│ ║ l.+2: if n then 0 else f (miss + 1) //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in binding of lambda expression: //│ ║ l.+1: rec def f n = //│ ║ ^^^ //│ ║ l.+2: if n then 0 else f (miss + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `int` is not an instance of type `bool` //│ ║ l.+2: if n then 0 else f (miss + 1) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.+2: if n then 0 else f (miss + 1) //│ ╙── ^ //│ f: bool -> 0 class Left: { v: anything } //│ Defined class Left // TODO missing prov of `a` in `let Left a` :e rec def lefts() = let res = Left {v = lefts()} in let a = res.v in let tmo = log(a + 1) in res //│ ╔══[ERROR] Type mismatch in binding of lambda expression: //│ ║ l.+1: rec def lefts() = //│ ║ ^^^^ //│ ║ l.+2: let res = Left {v = lefts()} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+3: in let a = res.v //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.+4: in let tmo = log(a + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+5: in res //│ ║ ^^^^^^^^^ //│ ╟── application of type `Left & {v: ?v}` is not an instance of type `int` //│ ║ l.+2: let res = Left {v = lefts()} //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.+5: in res //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.+4: in let tmo = log(a + 1) //│ ║ ^ //│ ╟── from application: //│ ║ l.+2: let res = Left {v = lefts()} //│ ╙── ^^^^^^^ //│ lefts: () -> 'a //│ where //│ 'a :> Left & {v: forall 'a. 'a} // * For the record – the errors in Supertype prototype: ///│ ╔══[ERROR]══ identifier not found: miss ///│ ║ l.+2: if n then 0 else f (miss + 1) ///│ ╙── ^^^^ ///│ ╔══[ERROR]══ Type mismatch in member definition: ///│ ║ l.+1: f n = ... ///│ ║ ^^^^^^^ ///│ ╟────────────── expression of type: Int ///│ ║ l.+2: if n then 0 else f (miss + 1) ///│ ║ ^^^^^^^^^^ ///│ ╟── which flows into ///│ ║ l.+2: if n then 0 else f (miss + 1) ///│ ║ ^ ///│ ╟──── does not match expected type: Bool ///│ ╠══════[DETAILS]══════ ///│ ╟── Expression of type: Int ///│ ║ [predef.sup] ///│ ║ l.129: (+): Int -> Int -> Int ///│ ║ ^^^ ///│ ╟────► flowing into ///│ ║ l.+2: if n then 0 else f (miss + 1) ///│ ║ ^^^^^^^^^^ ///│ ╟────► into ///│ ║ l.+1: f n = ///│ ║ ^ ///│ ╟── does not match expected type: Bool ///│ ║ l.+2: if n then 0 else f (miss + 1) ///│ ╙── ^ ///│ f: Bool -> 0 // TODO missing prov of `a` in `let Left a` ///│ ╔══[ERROR]══ Type mismatch in member definition: ///│ ║ l.+1: lefts() = ... ///│ ║ ^^^^^^^^^^^ ///│ ╟────────────── expression of type: Left ?a as ?a ///│ ║ l.+2: let res = Left lefts() ///│ ║ ^^^^^^^^^^^^ ///│ ╟── which flows into ///│ ║ l.+4: println @ a + 1 ///│ ║ ^ ///│ ╟──── does not match expected type: Int ///│ ║ [predef.sup] ///│ ║ l.129: (+): Int -> Int -> Int ///│ ║ ^^^ ///│ ╠══════[DETAILS]══════ ///│ ╟── Expression of type [1] : Left ?b as ?b ///│ ║ l.+2: let res = Left lefts() ///│ ║ ^^^^^^^^^^^^ ///│ ╟────► flowing into ///│ ║ l.+2: let res = Left lefts() ///│ ║ ^^^ ///│ ╟────► into ///│ ║ l.+5: res ///│ ║ ^^^ ///│ ╟────► into ///│ ║ l.+2: let res = Left lefts() ///│ ║ ^^^^^^^ ///│ ╟────►► flowing into Left's first argument, with type: Left (Left ?c as ?c) ///│ ║ ^^^^^^^^^^^^^^^ ///│ ║ l.+2: let res = Left lefts() ///│ ║ ^^^^^^^^^^^^ ///│ ╟────►► flowing into ///│ ║ l.+2: let res = Left lefts() ///│ ║ ^^^ ///│ ╟────►► into ///│ ║ l.+3: let Left a = res ///│ ║ ^^^ ///│ ╟────►► into ///│ ║ l.+3: let Left a = res ///│ ║ ^^^^^^ ///│ ╟────► flowing out of Left's first pattern, with type [1] : Left ?d as ?d ///│ ║ ^^^^^^^^^^^^^ ///│ ║ l.+3: let Left a = res ///│ ║ ^ ///│ ╟────► flowing into ///│ ║ l.+4: println @ a + 1 ///│ ║ ^ ///│ ╟── does not match expected type: Int ///│ ║ [predef.sup] ///│ ║ l.129: (+): Int -> Int -> Int ///│ ╙── ^^^ ///│ lefts: 'fun () -> (Left 'a as 'a) ================================================ FILE: shared/src/test/diff/mlscript/RecursiveTypes.mls ================================================ // From Simple-sub tests: :js let rec l = fun a -> fun a -> fun a -> l in let rec r = fun a -> fun a -> r in if true then l else r //│ // Prelude //│ let res; //│ // Query 1 //│ res = ((l) => ((r) => true ? l : r)(function r(a) { //│ return (a) => r; //│ }))(function l(a) { //│ return (a) => (a) => l; //│ }); //│ // End of generated code //│ res: 'r | 'l //│ where //│ 'l :> anything -> anything -> anything -> 'l //│ 'r :> anything -> anything -> 'r //│ = [Function: l] rec def l a = l //│ l: 'l //│ where //│ 'l :> anything -> 'l //│ = [Function: l] if true then l else l //│ res: 'l //│ where //│ 'l :> anything -> 'l //│ = [Function: l] rec def l a a a = l //│ l: 'l //│ where //│ 'l :> anything -> anything -> anything -> 'l //│ = [Function: l1] rec def r a a = l //│ r: anything -> anything -> 'l //│ where //│ 'l :> anything -> anything -> anything -> 'l //│ = [Function: r] rec def r a a = l //│ r: anything -> anything -> 'l //│ where //│ 'l :> anything -> anything -> anything -> 'l //│ = [Function: r1] rec def r a a = r //│ r: 'r //│ where //│ 'r :> anything -> anything -> 'r //│ = [Function: r2] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> anything -> anything -> anything -> 'l //│ 'r :> anything -> anything -> 'r //│ = [Function: l1] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> anything -> anything -> anything -> 'l //│ 'r :> anything -> anything -> 'r //│ = [Function: l1] rec def l (a: int) (a: int) = l rec def r (a: bool) (a: bool) (a: bool) = r //│ l: 'l //│ where //│ 'l :> int -> int -> 'l //│ = [Function: l2] //│ r: 'r //│ where //│ 'r :> bool -> bool -> bool -> 'r //│ = [Function: r3] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> int -> int -> 'l //│ 'r :> bool -> bool -> bool -> 'r //│ = [Function: l2] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> int -> int -> 'l //│ 'r :> bool -> bool -> bool -> 'r //│ = [Function: l2] if true then (fun x -> add x 1) else (fun y -> not y) //│ res: nothing -> (bool | int) //│ = [Function (anonymous)] if true then (fun x -> add x 1) else (fun y -> not y) //│ res: nothing -> (bool | int) //│ = [Function (anonymous)] rec def l (a: int) (a: bool) = l rec def r (a: int) (a: bool) (a: bool) = r //│ l: 'l //│ where //│ 'l :> int -> bool -> 'l //│ = [Function: l3] //│ r: 'r //│ where //│ 'r :> int -> bool -> bool -> 'r //│ = [Function: r4] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> int -> bool -> 'l //│ 'r :> int -> bool -> bool -> 'r //│ = [Function: l3] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> int -> bool -> 'l //│ 'r :> int -> bool -> bool -> 'r //│ = [Function: l3] rec def l (a: int) b = if true then l else b rec def r (a: int) b c = if true then r else if true then b else c //│ l: int -> 'a -> 'a //│ where //│ 'a :> int -> 'a -> 'a //│ = [Function: l4] //│ r: int -> 'a -> 'a -> 'a //│ where //│ 'a :> int -> 'a -> 'a -> 'a //│ = [Function: r5] if true then l else r //│ res: int -> ('a & 'b) -> ('a -> 'a | 'b) //│ where //│ 'b :> int -> 'b -> 'b //│ 'a :> int -> 'a -> 'a -> 'a //│ = [Function: l4] if true then l else r //│ res: int -> ('a & 'b) -> ('a -> 'a | 'b) //│ where //│ 'b :> int -> 'b -> 'b //│ 'a :> int -> 'a -> 'a -> 'a //│ = [Function: l4] rec def l (a: int) (b: int) = l //│ l: 'l //│ where //│ 'l :> int -> int -> 'l //│ = [Function: l5] rec def r (a: int) (b: int) (c: int) = r //│ r: 'r //│ where //│ 'r :> int -> int -> int -> 'r //│ = [Function: r6] if true then l else r //│ res: 'r | 'l //│ where //│ 'l :> int -> int -> 'l //│ 'r :> int -> int -> int -> 'r //│ = [Function: l5] // ------ // ------ // ------ // ------ // ------ // ------ // :NoJS class C[A]: { a: A } //│ Defined class C[+A] :ns rec def foo (c: C['a]) = foo (c.a) //│ foo: forall 'foo 'a 'a0 'b. 'foo //│ where //│ 'foo := C['a] -> 'b //│ 'a <: 'a0 //│ 'a0 <: C['a] foo //│ res: 'a -> nothing //│ where //│ 'a <: C['a] type Rec = C[Rec] def foo_ty: Rec -> nothing //│ Defined type alias Rec //│ foo_ty: Rec -> nothing foo_ty = foo //│ 'a -> nothing //│ where //│ 'a <: C['a] //│ <: foo_ty: //│ Rec -> nothing def foo_ty2: (C['r] as 'r) -> nothing //│ foo_ty2: 'r -> nothing //│ where //│ 'r <: C['r] :ns foo_ty2 //│ res: forall 'r. 'r -> nothing //│ where //│ 'r := C['r] foo_ty = foo_ty2 //│ 'r -> nothing //│ where //│ 'r <: C['r] //│ <: foo_ty: //│ Rec -> nothing foo_ty2 = foo_ty //│ Rec -> nothing //│ <: foo_ty2: //│ 'r -> nothing //│ where //│ 'r <: C['r] foo_ty2 = foo //│ 'a -> nothing //│ where //│ 'a <: C['a] //│ <: foo_ty2: //│ 'r -> nothing //│ where //│ 'r <: C['r] rec def bar = C { a = bar } //│ bar: 'A //│ where //│ 'A :> C['A] type Rec2 = C[Rec2] def bar_ty: Rec2 //│ Defined type alias Rec2 //│ bar_ty: Rec2 bar_ty = bar //│ 'A //│ where //│ 'A :> C['A] //│ <: bar_ty: //│ Rec2 def bar_ty2: C['r] as 'r //│ bar_ty2: 'r //│ where //│ 'r :> C['r] :ns bar_ty2 //│ res: forall 'r. 'r //│ where //│ 'r := C['r] bar_ty2 //│ res: 'r //│ where //│ 'r :> C['r] bar_ty = bar_ty2 //│ 'r //│ where //│ 'r :> C['r] //│ <: bar_ty: //│ Rec2 bar_ty2 = bar_ty //│ Rec2 //│ <: bar_ty2: //│ 'r //│ where //│ 'r :> C['r] bar_ty2 = bar //│ 'A //│ where //│ 'A :> C['A] //│ <: bar_ty2: //│ 'r //│ where //│ 'r :> C['r] type Rec3 = { x: Rec3 } //│ Defined type alias Rec3 def bar2_ty: Rec3 //│ bar2_ty: Rec3 def bar2_ty2: { x: 'r } as 'r //│ bar2_ty2: 'r //│ where //│ 'r :> {x: 'r} bar2_ty = bar2_ty2 //│ 'r //│ where //│ 'r :> {x: 'r} //│ <: bar2_ty: //│ Rec3 bar2_ty2 = bar2_ty //│ Rec3 //│ <: bar2_ty2: //│ 'r //│ where //│ 'r :> {x: 'r} :e bar2_ty2 = bar_ty2 //│ 'r //│ where //│ 'r :> C['r] //│ <: bar2_ty2: //│ 'r //│ where //│ 'r :> {x: 'r} //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.346: bar2_ty2 = bar_ty2 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `forall 'r. 'r` does not match type `{x: 'r0}` //│ ║ l.279: def bar_ty2: C['r] as 'r //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `{x: 'r1}` //│ ║ l.346: bar2_ty2 = bar_ty2 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.326: def bar2_ty2: { x: 'r } as 'r //│ ╙── ^^^^^^^^^ :e bar_ty2 = bar2_ty2 //│ 'r //│ where //│ 'r :> {x: 'r} //│ <: bar_ty2: //│ 'r //│ where //│ 'r :> C['r] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.368: bar_ty2 = bar2_ty2 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `{x: 'r}` is not an instance of type `C` //│ ║ l.326: def bar2_ty2: { x: 'r } as 'r //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `C[?]` //│ ║ l.368: bar_ty2 = bar2_ty2 //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.279: def bar_ty2: C['r] as 'r //│ ╙── ^^^^^ // ------ // ------ // ------ // ------ // ------ // ------ // rec def f x = if x > 0 then f (x with { a = x - 1 }) else x //│ f: 'a -> 'a //│ where //│ 'a :> 'a\a & {a: int} //│ <: int & (int | ~{a: int})\a & (number | ~{a: int})\a f 1 //│ res: 'a //│ where //│ 'a :> 1 | 'a\a & {a: int} :ns rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ f: forall 'f 'b 'c 'd 'a 'e 'g. 'f //│ where //│ 'f := 'b -> 'g //│ 'b :> 'b\a & {a: 'c} //│ <: ({a: 'a} | ~{a: 'c} | ~{})\a & ({a: 'a} | ~{a: 'c})\a & (number | ~{a: 'c} | ~{})\a & (number | ~{a: 'c})\a & (int | ~{a: 'c} | ~{})\a & (int | ~{a: 'c})\a & 'e & int & number //│ 'a <: 'e //│ 'e :> 'b\a & {a: 'c} //│ <: 'g //│ 'g :> 'b\a & {a: 'c} //│ <: 'd //│ 'd :> 'b\a & {a: 'c} //│ <: {a: 'a} //│ 'c :> int f //│ res: 'a -> 'b //│ where //│ 'a :> 'a\a & {a: int} //│ <: int & (int | ~{a: int})\a & (number | ~{a: int})\a & ({a: 'b} | ~{a: int})\a & 'b //│ 'b :> 'a\a & {a: int} //│ <: {a: 'b} // Notice how what is most likely an the error is reported in call sites, // due to the delaying effect of the field removal type... // though it *is* still possible to call this function wtihout errors, // using a bottom-typed argument. :e f 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.436: f 1 //│ ║ ^^^ //│ ╟── operator application of type `int` does not have field 'a' //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | int | 'a\a & {a: int} //│ where //│ 'a :> 1 | 'a\a & {a: int} :e f { a = 1 } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.451: f { a = 1 } //│ ║ ^^^^^^^^^^^ //│ ╟── operator application of type `int` does not have field 'a' //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error f error //│ res: 'a //│ where //│ 'a :> 'a\a & {a: int} :e rec def ainf = { a = ainf } f ainf //│ ainf: 'ainf //│ where //│ 'ainf :> {a: 'ainf} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.471: f ainf //│ ║ ^^^^^^ //│ ╟── operator application of type `int` does not have field 'a' //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error :e rec def infina = 0 with { a = infina } f infina //│ infina: 'infina //│ where //│ 'infina :> 0 & {a: 'infina} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.488: f infina //│ ║ ^^^^^^^^ //│ ╟── operator application of type `int` does not have field 'a' //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ║ ^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.407: rec def f x = if x > 0 then (f (x with { a = x - 1 })).a else x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | int | 'a\a & {a: int} | 'infina | 'infina0 //│ where //│ 'infina :> 0 & {a: 'infina} //│ 'a :> forall 'infina0. 'a\a & {a: int} | 'infina0 //│ 'infina0 :> 0 & {a: 'infina0} def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ f_manual: ('a & 'd) -> ('c | 'd) //│ where //│ 'd :> 'd\a & {a: int} //│ 'a <: {a: 'a & 'c} :e f_manual 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.515: f_manual 1 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` does not have field 'a' //│ ║ l.515: f_manual 1 //│ ║ ^ //│ ╟── Note: constraint arises from record type: //│ ║ l.508: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from intersection type: //│ ║ l.508: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error | 'e //│ where //│ 'e :> 1 | 'e\a & {a: int} :e f_manual { a = 1 } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.533: f_manual { a = 1 } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not have field 'a' //│ ║ l.533: f_manual { a = 1 } //│ ║ ^ //│ ╟── Note: constraint arises from record type: //│ ║ l.508: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from intersection type: //│ ║ l.508: def f_manual: (({a: 'b & 'a & 'c} as 'a) & 'd) -> ('c | ('d | 'e\a & {a: int} as 'e)) //│ ╙── ^^^^^^^^^^^^ //│ res: 1 | error | 'e //│ where //│ 'e :> 'e\a & {a: int} | {a: 1} f_manual error //│ res: 'e //│ where //│ 'e :> 'e\a & {a: int} // > FIXME why is this one accepted but `f ainf` is not? // It looks like a simplification error (I copy-pasted the simplified type for f_manual) // since `f_manual_ns ainf` gets the same error as `f ainf`. f_manual ainf //│ res: 'ainf | 'e //│ where //│ 'e :> 'e\a & {a: int} | 'ainf //│ 'ainf :> {a: 'ainf} // Notice the simplified type is NOT the same as that of `f`... def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) //│ f_manual_ns: in forall 'b 'f 'c 'd 'c0. ('b & 'f & (int & ~{a: int} | (int & 'c)\a & 'c0\a & int)) -> ('d | 'b) out forall 'b 'f 'c 'd 'c0. ((int & 'c & 'c0 | ~{a: int})\a & int & 'b & 'f) -> ('d | 'b) //│ where //│ 'c0 <: {a: 'd & 'c0} //│ 'c <: {a: 'c & 'd} //│ 'f <: {a: 'f} //│ 'b :> 'b\a & {a: int} :e f_manual_ns ainf //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.574: f_manual_ns ainf //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type `int` does not have field 'a' //│ ║ l.565: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) //│ ║ ^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.565: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) //│ ║ ^^^^^^^^^^^^ //│ ╟── from local type binding: //│ ║ l.565: def f_manual_ns: 'a | ('b & (({a: 'd & 'c} as 'c) | ~{a: 'e | int} | ~{})\a & (({a: 'd & 'c} as 'c) | ~{a: 'e | int})\a & (({a: 'f} as 'c) as 'f) & (int | ~{a: 'e | int} | ~{})\a & (int | ~{a: 'e | int})\a & int & int) -> ('g | 'd | ('b | 'h\a & {a: 'e | int} as 'h)) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res: error def f_manual_2: (({a: 'a} as 'a) & 'b) -> ('b | 'c\a & {a: int} as 'c) //│ f_manual_2: ('a & 'b) -> 'b //│ where //│ 'b :> 'b\a & {a: int} //│ 'a <: {a: 'a} f_manual_2 // [test:T2] //│ res: ('a & 'b) -> 'b //│ where //│ 'b :> 'b\a & {a: int} //│ 'a <: {a: 'a} f_manual_2 ainf //│ res: 'c //│ where //│ 'c :> forall 'ainf. 'c\a & {a: int} | 'ainf //│ 'ainf :> {a: 'ainf} rec def f x = if error then f (x with { a = x - 1 }) else x //│ f: 'a -> 'a //│ where //│ 'a :> 'a\a & {a: int} //│ <: int & (int | ~{a: int})\a r = f 1 //│ r: 'a //│ where //│ 'a :> 1 | 'a\a & {a: int} r + 1 //│ res: int :e r.a //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.624: r.a //│ ║ ^^^ //│ ╟── integer literal of type `1` does not have field 'a' //│ ║ l.615: r = f 1 //│ ║ ^ //│ ╟── but it flows into reference with expected type `{a: ?a}` //│ ║ l.624: r.a //│ ╙── ^ //│ res: error | int case r of {int -> 0 | _ -> r.a} //│ res: 0 r2 = case r of {int -> error | _ -> r} //│ r2: nothing r2.a //│ res: nothing r2 + 1 //│ res: int r: int //│ res: int rec def f x = if error then f (x with { a = 1 }) else x //│ f: 'a -> 'a //│ where //│ 'a :> 'a\a & {a: 1} r = f 1 //│ r: 'a //│ where //│ 'a :> 1 | 'a\a & {a: 1} r: int //│ res: int // Field removal also blocks proper simplification def fancyBottom1: 'a & ('a -> 'a) as 'a def fancyBottom2: 'a\x as 'a //│ fancyBottom1: 'a //│ where //│ 'a := 'a -> 'a & 'a //│ fancyBottom2: 'a //│ where //│ 'a :> 'a\x fancyBottom2: nothing //│ res: nothing // ------ // ------ // ------ // ------ // ------ // ------ // // Just poking around: def impossible: 'a -> ('a & int) //│ impossible: 'a -> (int & 'a) rec def f (x: int | string) = f (impossible x) + 1 //│ f: (int | string) -> int rec def f (x: int | string) = (case x of {int -> f "ok" | string -> f (impossible x)}) + 1 //│ f: (int | string) -> int rec def f x = (case x of {int -> f "ok" | string -> f (impossible x)}) + 1 //│ f: (int | string & (int | string | ~int)) -> int f 1 f "a" //│ res: int //│ res: int rec def f x = case x of {int -> f "ok" | string -> f (impossible x) | _ -> x} //│ f: (int | string & (int | string | ~int) | 'a & ~int & ~string) -> 'a f 1 f "a" f false //│ res: nothing //│ res: nothing //│ res: false ================================================ FILE: shared/src/test/diff/mlscript/RecursiveTypes2.mls ================================================ :NoJS type T1 = { x: T1 & { x: T1 } } type T2 = { x: T2 } //│ Defined type alias T1 //│ Defined type alias T2 :stats error : T1 : T2 //│ res: T2 //│ constrain calls : 6 //│ annoying calls : 4 //│ subtyping calls : 24 type T3 = { x: T2 & { x: T3 } | T3 & { x: T2 } } //│ Defined type alias T3 :stats error : T1 : T3 //│ res: T3 //│ constrain calls : 7 //│ annoying calls : 12 //│ subtyping calls : 408 :stats error : T2 : T3 //│ res: T3 //│ constrain calls : 10 //│ annoying calls : 12 //│ subtyping calls : 389 def f: T3 & { x: T1 & 'a } as 'a //│ f: 'a //│ where //│ 'a :> T3 & {x: T1 & 'a} :stats f.x //│ res: 'a & (T1 & T2 & {x: T3} | T1 & T3 & {x: T2}) //│ where //│ 'a :> T3 & {x: T1 & 'a} //│ constrain calls : 4 //│ annoying calls : 4 //│ subtyping calls : 102 g = error : T1 & { x: T2 | 'a } as 'a //│ g: 'a //│ where //│ 'a :> T1 & {x: T2 | 'a} :stats g.x //│ res: T1 & T2 & {x: T1} | T1 & {x: T1} & 'a //│ where //│ 'a :> T1 & {x: T2 | 'a} //│ constrain calls : 4 //│ annoying calls : 4 //│ subtyping calls : 84 :stats f = g //│ 'a //│ where //│ 'a :> T1 & {x: T2 | 'a} //│ <: f: //│ 'a //│ where //│ 'a :> T3 & {x: T1 & 'a} //│ constrain calls : 48 //│ annoying calls : 60 //│ subtyping calls : 1979 ================================================ FILE: shared/src/test/diff/mlscript/References.mls ================================================ class Ref[A] method Get: A method Set: A -> unit method Swap x = let tmp = this.Get in let _ = this.Set x in tmp //│ Defined class Ref[=A] //│ Declared Ref.Get: Ref['A] -> 'A //│ Declared Ref.Set: Ref['A] -> 'A -> unit //│ Defined Ref.Swap: Ref['A] -> 'A -> 'A class RefImpl[A]: Ref[A] & { mut value: A } method Get = this.value method Set a = this.value <- a //│ Defined class RefImpl[=A] //│ Defined RefImpl.Get: RefImpl['A] -> 'A //│ Defined RefImpl.Set: RefImpl['A] -> 'A -> unit def move a b = a.Set b.Get //│ move: Ref['A] -> Ref['A0] -> unit //│ where //│ 'A0 <: 'A //│ = [Function: move] def move_ty: Ref['a] -> Ref[('a & 'b)..'b] -> unit //│ move_ty: Ref['a] -> Ref[in 'a & 'b out 'b] -> unit //│ = :e // * Works with existential wildcards move_ty = move //│ Ref['A] -> Ref['A0] -> unit //│ where //│ 'A0 <: 'A //│ <: move_ty: //│ Ref['a] -> Ref[in 'a & 'b out 'b] -> unit //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.31: move_ty = move //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `'b` does not match type `'a` //│ ║ l.27: def move_ty: Ref['a] -> Ref[('a & 'b)..'b] -> unit //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.27: def move_ty: Ref['a] -> Ref[('a & 'b)..'b] -> unit //│ ║ ^^ //│ ╟── from field selection: //│ ║ l.21: def move a b = a.Set b.Get //│ ║ ^^^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ //│ = [Function: move] def move_approx: Ref['a] -> Ref['a] -> unit move_approx = move //│ move_approx: Ref['a] -> Ref['a] -> unit //│ = //│ Ref['A] -> Ref['A0] -> unit //│ where //│ 'A0 <: 'A //│ <: move_approx: //│ Ref['a] -> Ref['a] -> unit //│ = [Function: move] def move_bad: Ref['a] -> Ref['a..('a & 'b)] -> unit //│ move_bad: Ref['a] -> Ref['a] -> unit //│ = // :e // * Doesn't work with existential wildcards move_bad = move //│ Ref['A] -> Ref['A0] -> unit //│ where //│ 'A0 <: 'A //│ <: move_bad: //│ Ref['a] -> Ref['a] -> unit //│ = [Function: move] r = RefImpl{mut value = 1} //│ r: RefImpl['A] with {mut value: 'value} //│ where //│ 'value :> 1 //│ <: 'A //│ 'A :> 1 //│ = RefImpl { value: 1 } ri = r : Ref[int] rn = r : Ref[number] //│ ri: Ref[int] //│ = RefImpl { value: 1 } //│ rn: Ref[number] //│ = RefImpl { value: 1 } (r.Get, r.Set, r.Swap) //│ res: (1, anything -> unit, 'A -> (1 | 'A),) //│ = [ 1, [Function: Set], [Function: Swap] ] move r move_ty r move_approx r //│ res: Ref['A] -> unit //│ = [Function (anonymous)] //│ res: Ref['b] -> unit //│ = [Function (anonymous)] //│ res: Ref['a] -> unit //│ where //│ 'a :> 1 //│ = [Function (anonymous)] move ri move_ty ri move_approx ri //│ res: Ref['A] -> unit //│ where //│ 'A <: int //│ = [Function (anonymous)] //│ res: Ref[in int & 'b out 'b] -> unit //│ = [Function (anonymous)] //│ res: Ref[int] -> unit //│ = [Function (anonymous)] fun x -> move x r fun x -> move_ty x r fun x -> move_approx x r //│ res: Ref['A] -> unit //│ where //│ 'A :> 1 //│ = [Function: res] //│ res: Ref['a] -> unit //│ where //│ 'a :> 1 //│ = [Function: res] //│ res: Ref['a] -> unit //│ where //│ 'a :> 1 //│ = [Function: res] fun x -> move x ri fun x -> move_ty x ri fun x -> move_approx x ri //│ res: Ref['A] -> unit //│ where //│ 'A :> int //│ = [Function: res] //│ res: Ref['a] -> unit //│ where //│ 'a :> int //│ = [Function: res] //│ res: Ref[int] -> unit //│ = [Function: res] move r ri move rn r move rn ri //│ = undefined //│ = undefined //│ = undefined :e move ri rn //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.159: move ri rn //│ ║ ^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.87: rn = r : Ref[number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.86: ri = r : Ref[int] //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.21: def move a b = a.Set b.Get //│ ║ ^^^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ //│ res: error | unit //│ = undefined def swap a b = let tmp = a.Get in (a.Set b.Get, b.Set tmp) //│ swap: Ref[in 'A & 'A0 out 'A | 'A0] -> Ref[in 'A1 & 'A0 out 'A | 'A1] -> (unit, unit,) //│ = [Function: swap] swap r swap ri swap rn //│ res: Ref[in 'A out 1 | 'A] -> (unit, unit,) //│ = [Function (anonymous)] //│ res: Ref[int] -> (unit, unit,) //│ = [Function (anonymous)] //│ res: Ref[number] -> (unit, unit,) //│ = [Function (anonymous)] swap r ri swap ri r //│ res: (unit, unit,) //│ = [ undefined, undefined ] //│ res: (unit, unit,) //│ = [ undefined, undefined ] :e swap rn ri swap ri rn //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.201: swap rn ri //│ ║ ^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.87: rn = r : Ref[number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.86: ri = r : Ref[int] //│ ╙── ^^^ //│ res: error | (unit, unit,) //│ = [ undefined, undefined ] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.202: swap ri rn //│ ║ ^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.87: rn = r : Ref[number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.86: ri = r : Ref[int] //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.179: def swap a b = let tmp = a.Get in (a.Set b.Get, b.Set tmp) //│ ║ ^^^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ //│ res: error | (unit, unit,) //│ = [ undefined, undefined ] def refin: Ref[int] & Ref[string] //│ refin: in Ref[out int | string] out Ref[in int | string out nothing] //│ = refin.Get //│ res: nothing //│ = //│ refin is not implemented fun x -> refin.Set x //│ res: (int | string) -> unit //│ = //│ refin is not implemented refin.Swap //│ res: ('A & (int | string)) -> 'A //│ = //│ refin is not implemented refin: Ref['a..'b] //│ res: Ref[in 'b & (int | string) out 'b] //│ = //│ refin is not implemented res.Swap //│ res: ((int | string) & 'A) -> 'A //│ = undefined refin: Ref['a..'b] & Ref['c..'d] //│ res: Ref[in 'd & (int | string) | 'b & (int | string) out 'b & 'd] //│ = //│ refin is not implemented :ng res.Swap //│ res: ('A & ((int | string) & 'b | 'd & (int | string))) -> ('b & 'd | 'A) refin: Ref['a..'b] | Ref['c..'d] //│ res: Ref[in 'd & (int | string | ~'d) out 'd] //│ = //│ refin is not implemented :ng res.Swap //│ res: 'A -> 'A def refrefin: Ref[Ref[int] & Ref[string]] //│ refrefin: Ref[in Ref[out int | string] out Ref[in int | string out nothing]] //│ = :re refrefin = RefImpl { mut value = error } //│ RefImpl['A] with {mut value: 'value} //│ where //│ 'value <: 'A //│ <: refrefin: //│ Ref[in Ref[out int | string] out Ref[in int | string out nothing]] //│ Runtime error: //│ Error: an error was thrown :e refrefin = RefImpl { mut value = RefImpl { mut value = error } } //│ RefImpl['A] with {mut value: 'value} //│ where //│ 'value :> RefImpl['A0] with {mut value: 'value0} //│ <: 'A //│ 'A :> RefImpl['A0] with {mut value: 'value0} //│ 'value0 <: 'A0 //│ <: refrefin: //│ Ref[in Ref[out int | string] out Ref[in int | string out nothing]] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.296: refrefin = RefImpl { mut value = RefImpl { mut value = error } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int` does not match type `nothing` //│ ║ l.281: def refrefin: Ref[Ref[int] & Ref[string]] //│ ╙── ^^^ //│ Runtime error: //│ Error: an error was thrown def consrefin: (Ref[int] & Ref[string]) -> int //│ consrefin: in Ref[in int | string out nothing] -> int out Ref[out int | string] -> int //│ = :e consrefin (RefImpl { mut value = error }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.320: consrefin (RefImpl { mut value = error }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.315: def consrefin: (Ref[int] & Ref[string]) -> int //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.315: def consrefin: (Ref[int] & Ref[string]) -> int //│ ║ ^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.12: class RefImpl[A]: Ref[A] & { mut value: A } //│ ╙── ^ //│ res: error | int //│ = //│ consrefin is not implemented def refun: Ref[int] | Ref[string] //│ refun: Ref[out int | string] //│ = // * Errors happen because we constrain `Ref[int] | Ref[string] <: Ref['A]` // * ie `Ref[int & string .. int | string] <: Ref['A]` // * ie `int & string .. int | string <: 'A` and `'A <: int & string .. int | string` // * ie `int | string <: 'A` and `'A <: int & string` :e refun.Get fun x -> refun.Set x refun.Swap //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.348: refun.Get //│ ║ ^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ //│ res: error | int | string //│ = //│ refun is not implemented //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.349: fun x -> refun.Set x //│ ║ ^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ //│ res: nothing -> (error | unit) //│ = //│ refun is not implemented //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.350: refun.Swap //│ ║ ^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.339: def refun: Ref[int] | Ref[string] //│ ║ ^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.3: class Ref[A] //│ ╙── ^ //│ res: error | nothing -> (int | string) //│ = //│ refun is not implemented :e def bar x = RefImpl { mut value = x } : Ref[int] & Ref[string] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.400: def bar x = RefImpl { mut value = x } : Ref[int] & Ref[string] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `string` is not an instance of type `int` //│ ║ l.400: def bar x = RefImpl { mut value = x } : Ref[int] & Ref[string] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.400: def bar x = RefImpl { mut value = x } : Ref[int] & Ref[string] //│ ║ ^^^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.12: class RefImpl[A]: Ref[A] & { mut value: A } //│ ╙── ^ //│ bar: nothing -> Ref[in int | string out nothing] //│ = [Function: bar] // * Note that this now signature-checks because it instantiates the inferred polymorphic type // * later (once for each intersection component) // :e rec def refin = RefImpl { mut value = refin.Get } //│ RefImpl['A] //│ <: refin: //│ Ref[out int | string] //│ = [Function: refin] ================================================ FILE: shared/src/test/diff/mlscript/Repro.mls ================================================ :NewDefs ================================================ FILE: shared/src/test/diff/mlscript/RigidVariables.mls ================================================ :e def foo[AAA]: AAA -> AAA def foo(x) = x.a //│ foo: 'a -> 'a //│ = //│ {a: 'a} -> 'a //│ <: foo: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.5: def foo(x) = x.a //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── expression of type `?a` does not have field 'a' //│ ╟── Note: constraint arises from field selection: //│ ║ l.5: def foo(x) = x.a //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.5: def foo(x) = x.a //│ ╙── ^ //│ = [Function: foo] :e class Test0 method Foo0[AAA]: AAA -> AAA method Foo0(x) = x.a //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.26: method Foo0(x) = x.a //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `AAA` does not have field 'a' //│ ║ l.25: method Foo0[AAA]: AAA -> AAA //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.26: method Foo0(x) = x.a //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.26: method Foo0(x) = x.a //│ ╙── ^ //│ Defined class Test0 //│ Declared Test0.Foo0: Test0 -> 'AAA -> 'AAA //│ Defined Test0.Foo0: Test0 -> {a: 'a} -> 'a class Test1 method Foo1[AAA]: (AAA & {a: int}) -> int method Foo1(x) = x.a //│ Defined class Test1 //│ Declared Test1.Foo1: Test1 -> {a: int} -> int //│ Defined Test1.Foo1: Test1 -> {a: 'a} -> 'a :e (Test1{}).Foo1({x=1}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.51: (Test1{}).Foo1({x=1}) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{x: 1}` does not have field 'a' //│ ║ l.51: (Test1{}).Foo1({x=1}) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.44: method Foo1[AAA]: (AAA & {a: int}) -> int //│ ║ ^^^^^^^^ //│ ╟── from intersection type: //│ ║ l.44: method Foo1[AAA]: (AAA & {a: int}) -> int //│ ╙── ^^^^^^^^^^^^^^^^ //│ res: error | int //│ = undefined (Test1{}).Foo1({a=1}) //│ res: int //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/SafeDiv.mls ================================================ :e def safeDiv x (y: ~0) = div x y //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.3: def safeDiv x (y: ~0) = div x y //│ ║ ^^^^^^^ //│ ╟── type `~0` is not an instance of type `int` //│ ║ l.3: def safeDiv x (y: ~0) = div x y //│ ║ ^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.3: def safeDiv x (y: ~0) = div x y //│ ╙── ^ //│ safeDiv: int -> ~0 -> (error | int) //│ = [Function: safeDiv] def safeDiv x (y: int & ~0) = div x y //│ safeDiv: int -> (int & ~0) -> int //│ = [Function: safeDiv1] :e safeDiv 1 0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.21: safeDiv 1 0 //│ ║ ^^^^^^^^^^^ //│ ╟── integer literal of type `0` does not match type `~0` //│ ║ l.21: safeDiv 1 0 //│ ║ ^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.16: def safeDiv x (y: int & ~0) = div x y //│ ║ ^^ //│ ╟── from intersection type: //│ ║ l.16: def safeDiv x (y: int & ~0) = div x y //│ ╙── ^^^^^^^^ //│ res: error | int //│ = Infinity fun x -> safeDiv 1 x //│ res: (int & ~0) -> int //│ = [Function: res] class None: {} class Some[A]: { value: A } type Option[A] = Some[A] | None //│ Defined class None //│ Defined class Some[+A] //│ Defined type alias Option[+A] fun x -> case x of { int -> safeDiv 1 x | _ -> None{} } //│ res: (int & ~0 | ~int) -> (None | int) //│ = [Function: res] :e // we no longer refine x's type here, as that was rather unexpected fun (x: int) -> safeDiv 1 x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.55: fun (x: int) -> safeDiv 1 x //│ ║ ^^^^^^^^^^^ //│ ╟── type `int` does not match type `~0` //│ ║ l.55: fun (x: int) -> safeDiv 1 x //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `~0` //│ ║ l.55: fun (x: int) -> safeDiv 1 x //│ ║ ^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.16: def safeDiv x (y: int & ~0) = div x y //│ ║ ^^ //│ ╟── from intersection type: //│ ║ l.16: def safeDiv x (y: int & ~0) = div x y //│ ╙── ^^^^^^^^ //│ res: int -> (error | int) //│ = [Function: res] fun (x: int & ~0) -> safeDiv 1 x //│ res: (int & ~0) -> int //│ = [Function: res] def tryDiv: int -> int -> Option[int] //│ tryDiv: int -> int -> Option[int] //│ = def tryDiv x y = case y of { 0 -> None{} | _ -> Some { value = safeDiv x y } } //│ int -> (0 | int & ~0) -> (None | Some[int]) //│ <: tryDiv: //│ int -> int -> Option[int] //│ = [Function: tryDiv] // ^ Note: `0 | int & ~0` could be further simplified tryDiv 1 1 tryDiv 1 0 //│ res: Option[int] //│ = Some { value: 1 } //│ res: Option[int] //│ = None {} ================================================ FILE: shared/src/test/diff/mlscript/Scratch.mls ================================================ ================================================ FILE: shared/src/test/diff/mlscript/SelfNeg.mls ================================================ def foo: ('a -> (~'a -> 'b) -> 'b) -> 'b //│ foo: (nothing -> (anything -> 'b) -> 'b) -> 'b //│ = def foo: ('a -> (~'a -> 'a) -> 'a) -> 'a -> 'a //│ foo: ('a -> (~'a -> 'a) -> 'a) -> 'a -> 'a //│ = :ns foo //│ res: forall 'a. ('a -> (~'a -> 'a) -> 'a) -> 'a -> 'a //│ = //│ foo is not implemented :ns r = foo (fun a -> fun f -> f a) //│ r: forall 'b 'a 'c 'd. 'b //│ where //│ 'b :> 'a -> 'a //│ 'a <: 'd & 'c //│ 'c <: ~'a //│ 'd <: 'a //│ = //│ foo is not implemented r //│ res: 'a -> 'a //│ = //│ r and foo are not implemented :e r: anything -> nothing //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.34: r: anything -> nothing //│ ║ ^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.34: r: anything -> nothing //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.7: def foo: ('a -> (~'a -> 'a) -> 'a) -> 'a -> 'a //│ ║ ^^^ //│ ╟── from type variable: //│ ║ l.7: def foo: ('a -> (~'a -> 'a) -> 'a) -> 'a -> 'a //│ ╙── ^^ //│ res: anything -> nothing //│ = //│ r and foo are not implemented def foo: ('a -> (~'a -> anything) -> anything) -> MutArray['a] //│ foo: ('a -> (~'a -> anything) -> anything) -> MutArray['a] //│ = :ns r = foo (fun a -> fun f -> f a) //│ r: forall 'b 'a 'c. 'b //│ where //│ 'b :> MutArray['a] //│ 'a <: 'c //│ 'c <: ~'a //│ = //│ foo is not implemented :e r[0] <- "test" //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.68: r[0] <- "test" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"test"` does not match type `~"test"` //│ ║ l.68: r[0] <- "test" //│ ║ ^^^^^^ //│ ╟── but it flows into assigned array element with expected type `~"test"` //│ ║ l.68: r[0] <- "test" //│ ╙── ^^^^ //│ = //│ r and foo are not implemented :e r[0] + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.82: r[0] + 1 //│ ║ ^^^^^^ //│ ╟── possibly-undefined array access of type `undefined` is not an instance of type `int` //│ ║ l.82: r[0] + 1 //│ ╙── ^^^^ //│ res: error | int //│ = //│ r and foo are not implemented :ns def bar: (('a & ~'a) -> 'b) -> 'b //│ bar: forall 'b. (nothing -> 'b) -> 'b //│ = def bar: (('a & int & ~'a) -> 'b) -> 'b //│ bar: ((int & 'a & ~'a) -> 'b) -> 'b //│ = r = bar (fun x -> (x, x + 1, not x, x x)) //│ r: (int & 'a & ~'a, int, bool, nothing,) //│ = //│ bar is not implemented :ns r //│ res: forall 'b 'a 'c 'd. 'b //│ where //│ 'b :> forall 'e 'f. ('c, 'e, 'f, 'd,) //│ 'f :> bool //│ 'e :> int //│ 'c :> 'a & int & ~'a //│ <: 'c -> 'd & bool & int //│ 'a <: (bool | ~(int & ~'a)) & ('c -> 'd | ~(int & ~'a)) //│ = //│ r and bar are not implemented r.0 : nothing //│ res: nothing //│ = //│ r and bar are not implemented def impossible x = case x of {} //│ impossible: nothing -> nothing //│ = [Function: impossible] bar impossible //│ res: nothing //│ = //│ bar is not implemented def k: 'a -> 'a //│ k: 'a -> 'a //│ = :e k = impossible //│ nothing -> nothing //│ <: k: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.143: k = impossible //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `'a` does not match type `nothing` //│ ║ l.138: def k: 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.128: def impossible x = case x of {} //│ ╙── ^ //│ = [Function: impossible] ================================================ FILE: shared/src/test/diff/mlscript/SelfNegs.mls ================================================ // * Here the two occurrences of `'a` in different types are separate: def foo(f: (~'a) -> 'a, a: 'a) = f a //│ foo: (~'a -> 'a, ~'a,) -> 'a //│ = [Function: foo] def foo(fa: ((~'a) -> 'a, 'a)) = fa.0 fa.1 //│ foo: ((~'a -> 'a, 'a,),) -> 'a //│ = [Function: foo1] :ns foo //│ res: forall 'a 'b 'c. ((~'a -> 'a, 'a,),) -> 'c //│ where //│ 'a <: 'c & 'b //│ 'b <: ~'a //│ = [Function: foo1] ================================================ FILE: shared/src/test/diff/mlscript/Seqs.mls ================================================ :w class Seq[A]: { size: int } //│ Defined class Seq[±A] //│ ╔══[WARNING] Type definition Seq has bivariant type parameters: //│ ║ l.2: class Seq[A]: { size: int } //│ ║ ^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.2: class Seq[A]: { size: int } //│ ╙── ^ :w class ListBase[A]: Seq[A] //│ Defined class ListBase[±A] //│ ╔══[WARNING] Type definition ListBase has bivariant type parameters: //│ ║ l.12: class ListBase[A]: Seq[A] //│ ║ ^^^^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.12: class ListBase[A]: Seq[A] //│ ╙── ^ class Nil: ListBase[nothing] & {} class Cons[A]: ListBase[A] & { head: A; tail: List[A] } type List[A] = Nil | Cons[A] //│ Defined class Nil //│ Defined class Cons[+A] //│ Defined type alias List[+A] :e Nil{} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.29: Nil{} //│ ║ ^^^^^ //│ ╟── record literal of type `anything` does not have field 'size' //│ ║ l.29: Nil{} //│ ╙── ^^ //│ res: Nil & {size: nothing} | error //│ = Nil { size: undefined } def Nil = Nil { size = 0 } //│ Nil: Nil & {size: 0} //│ = [Function: Nil1] :e :re Cons 1 Nil //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.45: Cons 1 Nil //│ ║ ^^^^^^ //│ ╟── integer literal of type `1` is not a record (expected a record with fields: size, tail, head) //│ ║ l.45: Cons 1 Nil //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.45: Cons 1 Nil //│ ║ ^^^^^^^^^^ //│ ╟── application of type `Cons[?A] & {Cons#A = ?A, head: ?head, size: ?size, tail: ?tail}` is not a function //│ ║ l.45: Cons 1 Nil //│ ╙── ^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: (intermediate value) is not a function Cons //│ res: {head: 'A & 'head, size: int & 'size, tail: List['A] & 'tail} -> (Cons['A] with {head: 'head, size: 'size, tail: 'tail}) //│ = [Function: res] // In the old inheritance semantics, this used to be required: def Cons_old head tail = Cons (ListBase (Seq { size = succ tail.size }) with { head; tail }) //│ Cons_old: ('head & 'A) -> (List['A] & {size: int} & 'tail) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: Cons_old] def Cons head tail = Cons { size = succ tail.size; head; tail } //│ Cons: ('head & 'A) -> (List['A] & {size: int} & 'tail) -> (Cons['A] with {head: 'head, tail: 'tail}) //│ = [Function: Cons1] // * Works thanks to variance analysis Nil: ListBase[anything] //│ res: ListBase[?] //│ = Nil { size: 0 } c = Cons 1 Nil //│ c: Cons[1] with {tail: Nil & {size: 0}} //│ = Cons { size: 1, head: 1, tail: Nil { size: 0 } } c: ListBase[int] //│ res: ListBase[?] //│ = Cons { size: 1, head: 1, tail: Nil { size: 0 } } c.head //│ res: 1 //│ = 1 c.tail //│ res: Nil & {size: 0} //│ = Nil { size: 0 } c.size //│ res: int //│ = 1 d = Cons 2 c //│ d: Cons[1 | 2] with {head: 2, tail: Cons[1] with {tail: Nil & {size: 0}}} //│ = Cons { //│ size: 2, //│ head: 2, //│ tail: Cons { size: 1, head: 1, tail: Nil { size: 0 } } //│ } d.head //│ res: 2 //│ = 2 d.size //│ res: int //│ = 2 d.tail //│ res: Cons[1] with {tail: Nil & {size: 0}} //│ = Cons { size: 1, head: 1, tail: Nil { size: 0 } } d.tail.size //│ res: int //│ = 1 d.tail.head d.tail.tail //│ res: 1 //│ = 1 //│ res: Nil & {size: 0} //│ = Nil { size: 0 } ================================================ FILE: shared/src/test/diff/mlscript/Sequence.mls ================================================ :NewDefs :e let test(x) = log(x); x + 1 //│ ╔══[ERROR] identifier not found: x //│ ║ l.5: let test(x) = log(x); x + 1 //│ ╙── ^ //│ let test: anything -> () //│ Int //│ Code generation encountered an error: //│ unresolved symbol x let test(x) = log(x), x + 1 //│ let test: Int -> Int //│ test //│ = [Function: test1] test(1) //│ Int //│ res //│ = 2 //│ // Output //│ 1 :pe :e test(log("here we go"); 123) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.28: test(log("here we go"); 123) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.28: test(log("here we go"); 123) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `()` is not an instance of type `Int` //│ ║ l.28: test(log("here we go"); 123) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.14: let test(x) = log(x), x + 1 //│ ╙── ^ //│ Int | error //│ res //│ = NaN //│ // Output //│ here we go //│ undefined test((log("here we go"), 123)) //│ Int //│ res //│ = 124 //│ // Output //│ here we go //│ 123 let a = 1; let b = a + 1 //│ let a: 1 //│ let b: Int //│ a //│ = 1 //│ b //│ = 2 ================================================ FILE: shared/src/test/diff/mlscript/Signatures.mls ================================================ def foo: int -> (string -> anything) //│ foo: int -> string -> anything //│ = def foo: int -> string -> anything //│ foo: int -> string -> anything //│ = def foo: (int -> string) -> anything //│ foo: (int -> string) -> anything //│ = ================================================ FILE: shared/src/test/diff/mlscript/Simple.mls ================================================ a = 1 b = 2 c = 3 //│ a: 1 //│ = 1 //│ b: 2 //│ = 2 //│ c: 3 //│ = 3 if true then if true then a else b else a //│ res: 1 | 2 //│ = 1 if true then if true then id a else id b else a //│ res: 1 | 2 //│ = 1 class Foo[A]: { field: A } class Bar: {} //│ Defined class Foo[+A] //│ Defined class Bar foo = Foo { field = id 1 } //│ foo: Foo[1] //│ = Foo { field: 1 } (if true then foo else { field = 2 }).field //│ res: 1 | 2 //│ = 1 Foo { field = id 1 } //│ res: Foo[1] //│ = Foo { field: 1 } def test x = case x of { Bar -> 1 | Foo -> 2 } //│ test: (Bar | Foo[?]) -> (1 | 2) //│ = [Function: test] test foo //│ res: 1 | 2 //│ = 2 def test x = case x of { Bar -> 1 | Foo -> x.field } //│ test: (Bar | Foo[?] & {field: 'field}) -> (1 | 'field) //│ = [Function: test1] test foo //│ res: 1 //│ = 1 test (Foo { field = 2 }) //│ res: 1 | 2 //│ = 2 def test x = case x of { Foo -> x.field | _ -> 1 } //│ test: (Foo[?] & {field: 'field} | ~Foo[?]) -> (1 | 'field) //│ = [Function: test2] def f x = x.u //│ f: {u: 'u} -> 'u //│ = [Function: f] f { u = 1 } //│ res: 1 //│ = 1 f { u = 1; v = 2 } //│ res: 1 //│ = 1 f (if true then { u = 1; v = 2 } else { u = 1 }) //│ res: 1 //│ = 1 if true then { u = 1; v = 2 } else { u = 2 } //│ res: {u: 1 | 2} //│ = { u: 1, v: 2 } ================================================ FILE: shared/src/test/diff/mlscript/SimpleErrors.mls ================================================ :AllowTypeErrors succ false //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.3: succ false //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.3: succ false //│ ╙── ^^^^^ //│ res: error | int x = "oops" //│ x: "oops" f y = succ y //│ f: int -> int :e f x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.19: f x //│ ║ ^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.12: x = "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.19: f x //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.15: f y = succ y //│ ╙── ^ //│ res: error | int trait MyTrait: { value: anything } //│ Defined trait MyTrait def test1 x = case x of { MyTrait -> x.value | _ -> 0 } //│ test1: ({value: 'value} & #MyTrait | ~#MyTrait) -> (0 | 'value) :e test1 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: test1 1 //│ ║ ^^^^^^^ //│ ╟── expression of type `1 & ~?a | 1 & #MyTrait` does not have field 'value' //│ ╟── Note: constraint arises from field selection: //│ ║ l.39: def test1 x = case x of { MyTrait -> x.value | _ -> 0 } //│ ║ ^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.39: def test1 x = case x of { MyTrait -> x.value | _ -> 0 } //│ ╙── ^ //│ res: 0 | error def map: 'a -> 'a def map f = f map (fun x -> map) //│ map: 'a -> 'a //│ ((forall 'a. 'a -> 'a) -> (forall 'a0. anything -> 'a0 -> 'a0) -> 'b) -> 'b //│ <: map: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.58: def map f = f map (fun x -> map) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not a function //│ ║ l.57: def map: 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from application: //│ ║ l.58: def map f = f map (fun x -> map) //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.58: def map f = f map (fun x -> map) //│ ╙── ^ class Bar method Map[B]: B -> B rec method Map f = f 1 //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.79: rec method Map f = f 1 //│ ║ ^^^^^^^^^^^ //│ ╟── type `B` is not a function //│ ║ l.78: method Map[B]: B -> B //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.79: rec method Map f = f 1 //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.79: rec method Map f = f 1 //│ ╙── ^ //│ Defined class Bar //│ Declared Bar.Map: Bar -> 'B -> 'B //│ Defined Bar.Map: Bar -> (1 -> 'a) -> 'a def boom: anything //│ boom: anything // :d :e add boom 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.102: add boom 1 //│ ║ ^^^^^^^^ //│ ╟── type `anything` is not an instance of type `int` //│ ║ l.97: def boom: anything //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.102: add boom 1 //│ ╙── ^^^^ //│ res: error | int ================================================ FILE: shared/src/test/diff/mlscript/SimpleMethods.mls ================================================ :e class C0 method Foo0[A](a: A) = a + 1 method Foo1[A](a: A) = { a } //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.4: method Foo0[A](a: A) = a + 1 //│ ║ ^^^ //│ ╟── reference of type `#A` is not an instance of type `int` //│ ║ l.4: method Foo0[A](a: A) = a + 1 //│ ║ ^ //│ ╟── Note: method type parameter A is defined at: //│ ║ l.4: method Foo0[A](a: A) = a + 1 //│ ╙── ^ //│ Defined class C0 //│ Defined C0.Foo0: C0 -> anything -> (error | int) //│ Defined C0.Foo1: C0 -> 'A -> {a: 'A} (C0{}).Foo0 //│ res: anything -> (error | int) //│ = [Function: Foo0] f1 = (C0{}).Foo1 //│ f1: 'A -> {a: 'A} //│ = [Function: Foo1] f1 1 //│ res: {a: 1} //│ = { a: 1 } class C1 method F: int method F = 1 //│ Defined class C1 //│ Declared C1.F: C1 -> int //│ Defined C1.F: C1 -> 1 class C2: C1 method F = 2 //│ Defined class C2 //│ Defined C2.F: C2 -> 2 class C3 method F: 'a -> 'a method F = id //│ Defined class C3 //│ Declared C3.F: C3 -> 'a -> 'a //│ Defined C3.F: C3 -> 'a -> 'a class C4: C3 method F = id //│ Defined class C4 //│ Defined C4.F: C4 -> 'a -> 'a :e class C5[A] method F(x: A) = x + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.60: method F(x: A) = x + 1 //│ ║ ^^^ //│ ╟── reference of type `A` is not an instance of type `int` //│ ║ l.60: method F(x: A) = x + 1 //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.59: class C5[A] //│ ╙── ^ //│ Defined class C5[-A] //│ Defined C5.F: C5['A] -> 'A -> (error | int) :w class Tru[A] method Test f = true method Tru = this.Test (fun x -> error) //│ Defined class Tru[±A] //│ Defined Tru.Test: Tru[?] -> anything -> true //│ Defined Tru.Tru: Tru[?] -> true //│ ╔══[WARNING] Type definition Tru has bivariant type parameters: //│ ║ l.75: class Tru[A] //│ ║ ^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.75: class Tru[A] //│ ╙── ^ :e :ge class Hey rec method A = B rec method B = 1 //│ ╔══[ERROR] identifier not found: B //│ ║ l.92: rec method A = B //│ ╙── ^ //│ Defined class Hey //│ Defined Hey.A: Hey -> error //│ Defined Hey.B: Hey -> 1 //│ Code generation encountered an error: //│ unresolved symbol B ================================================ FILE: shared/src/test/diff/mlscript/Splice.mls ================================================ :NoJS def t1 : (int, ...Array[int], bool) //│ t1: (int, ...Array[int], bool) def t2 : (int, ...(bool, int)) //│ t2: (int, ...(bool, int,)) :e def t3 : (int, ...bool, mut int) //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.10: def t3 : (int, ...bool, mut int) //│ ║ ^^^^ //│ ╙── type `bool` does not match type `Array[?a]` //│ t3: (int, ...bool, int) :e def t4 : (int, ...int, bool) //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.18: def t4 : (int, ...int, bool) //│ ║ ^^^ //│ ╙── type `int` does not match type `Array[?a]` //│ t4: (int, ...int, bool) def f1 : ('a, ...Array[int]) -> 'a //│ f1: ('a, ...Array[int]) -> 'a // TODO def f1 (x, ...y) = x //│ ('a, ...Array[anything]) -> 'a //│ <: f1: //│ ('a, ...Array[int]) -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.29: def f1 (x, ...y) = x //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `('a, ...Array[int])` does not match type `(?a, ...?b)` //│ ║ l.25: def f1 : ('a, ...Array[int]) -> 'a //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from splice: //│ ║ l.29: def f1 (x, ...y) = x //│ ╙── ^^^^^^^^^ :e errt = (1, ...2, 3) //│ ╔══[ERROR] Type mismatch in splice: //│ ║ l.44: errt = (1, ...2, 3) //│ ║ ^^^^^^^^^^^^ //│ ╟── integer literal of type `2` does not match type `Array[?a]` //│ ║ l.44: errt = (1, ...2, 3) //│ ╙── ^ //│ errt: (1, ...2, 3) :e wrerr = (1, ...true, mut 3) //│ ╔══[ERROR] Type mismatch in splice: //│ ║ l.54: wrerr = (1, ...true, mut 3) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` does not match type `Array[?a]` //│ ║ l.54: wrerr = (1, ...true, mut 3) //│ ╙── ^^^^ //│ wrerr: (1, ...true, 3) gt1 = (1, ...(1,2,3), 2) //│ gt1: (1, ...(1, 2, 3,), 2) gt1[0] //│ res: 1 | 2 | 3 | undefined //FIXME def arr1: Array[int] gt2 = (...arr1) //│ arr1: Array[int] //│ gt2: (...Array[int]) :e (1, ...(true, false), "hi") : Array[int | bool] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.76: (1, ...(true, false), "hi") : Array[int | bool] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hi"` does not match type `bool | int` //│ ║ l.76: (1, ...(true, false), "hi") : Array[int | bool] //│ ║ ^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.76: (1, ...(true, false), "hi") : Array[int | bool] //│ ╙── ^^^^^^^^^^ //│ res: Array[bool | int] a1 = (1,2,3,4,5) a2 = (1,2, ...a1, 4,5) //│ a1: (1, 2, 3, 4, 5,) //│ a2: (1, 2, ...(1, 2, 3, 4, 5,), 4, 5) // TODO a2 : Array[int] //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing ================================================ FILE: shared/src/test/diff/mlscript/Stress.mls ================================================ :NoJS class A[T]: { fA: T } class B[T]: { fB: T } class C[T]: { fC: T } class D[T]: { fD: T } class E[T]: { fE: T } class F[T]: { fF: T } class G[T]: { fG: T } class H[T]: { fH: T } //│ Defined class A[+T] //│ Defined class B[+T] //│ Defined class C[+T] //│ Defined class D[+T] //│ Defined class E[+T] //│ Defined class F[+T] //│ Defined class G[+T] //│ Defined class H[+T] :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF | G -> x.fG | H -> x.fH } //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA} | E[?] & {fE: 'fA} | F[?] & {fF: 'fA} | G[?] & {fG: 'fA} | H[?] & {fH: 'fA}) -> 'fA //│ constrain calls : 26 //│ annoying calls : 0 //│ subtyping calls : 986 // ====== 1 & all ====== // // :stats // def arg: A[int] // foo arg // //│ arg: A[int] // //│ res: int | (nothing | (nothing | (nothing | (nothing | (nothing | (nothing | nothing)))))) // //│ constrain calls: 55374 // //│ annoying calls: 423405 // // Too long! // // :stats // // def arg: A[int] | B[int] // // foo arg // ====== 2 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB } def arg: A[int] | B[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA}) -> 'fA //│ arg: A[int] | B[int] //│ res: int //│ constrain calls : 36 //│ annoying calls : 20 //│ subtyping calls : 276 // ====== 3 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC } def arg: A[int] | B[int] | C[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA}) -> 'fA //│ arg: A[int] | B[int] | C[int] //│ res: int //│ constrain calls : 48 //│ annoying calls : 30 //│ subtyping calls : 522 // ====== 4 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD } def arg: A[int] | B[int] | C[int] | D[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA}) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] //│ res: int //│ constrain calls : 60 //│ annoying calls : 40 //│ subtyping calls : 852 :stats foo (arg with { x = 1} with { y = 2 }) //│ res: int //│ constrain calls : 35 //│ annoying calls : 37 //│ subtyping calls : 369 :stats foo (arg with { x = 1; y = 2; z = 3 }) //│ res: int //│ constrain calls : 35 //│ annoying calls : 37 //│ subtyping calls : 345 // ====== 5 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE } def arg: A[int] | B[int] | C[int] | D[int] | E[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA} | E[?] & {fE: 'fA}) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] //│ res: int //│ constrain calls : 72 //│ annoying calls : 50 //│ subtyping calls : 1274 // ====== 6 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF } def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA} | E[?] & {fE: 'fA} | F[?] & {fF: 'fA}) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] //│ res: int //│ constrain calls : 84 //│ annoying calls : 60 //│ subtyping calls : 1796 // ====== 7 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF | G -> x.fG } def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA} | E[?] & {fE: 'fA} | F[?] & {fF: 'fA} | G[?] & {fG: 'fA}) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] //│ res: int //│ constrain calls : 96 //│ annoying calls : 70 //│ subtyping calls : 2426 // ====== 8 ====== // :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF | G -> x.fG | H -> x.fH } def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] | H[int] foo arg //│ foo: (A[?] & {fA: 'fA} | B[?] & {fB: 'fA} | C[?] & {fC: 'fA} | D[?] & {fD: 'fA} | E[?] & {fE: 'fA} | F[?] & {fF: 'fA} | G[?] & {fG: 'fA} | H[?] & {fH: 'fA}) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] | H[int] //│ res: int //│ constrain calls : 108 //│ annoying calls : 80 //│ subtyping calls : 3172 ================================================ FILE: shared/src/test/diff/mlscript/StressDNF.mls ================================================ :NoJS class A class B class C class D class E //│ Defined class A //│ Defined class B //│ Defined class C //│ Defined class D //│ Defined class E def ty0: ('a & 'b & A | 'b & 'c & B | 'c & 'd & C | 'd & 'e & D | 'e & 'f & E) -> ('a, 'b, 'c, 'd, 'e, 'f) //│ ty0: (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) :stats ty0 = ty0 //│ (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) //│ <: ty0: //│ (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) //│ constrain calls : 1 //│ annoying calls : 0 //│ subtyping calls : 259 def ty1: ('a & A | 'b & B | 'c & C | 'd & D | 'e & E) -> ('a, 'b, 'c, 'd, 'e, 'f) //│ ty1: (A & 'a | B & 'b | C & 'c | D & 'd | E & 'e) -> ('a, 'b, 'c, 'd, 'e, nothing,) :stats ty0 = ty1 //│ (A & 'a | B & 'b | C & 'c | D & 'd | E & 'e) -> ('a, 'b, 'c, 'd, 'e, nothing,) //│ <: ty0: //│ (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) //│ constrain calls : 45 //│ annoying calls : 25 //│ subtyping calls : 1035 :stats :e ty1 = ty0 //│ (E & 'e & 'f | 'd & (C & 'c | D & 'e) | 'b & (A & 'a | B & 'c)) -> ('a, 'b, 'c, 'd, 'e, 'f,) //│ <: ty1: //│ (A & 'a | B & 'b | C & 'c | D & 'd | E & 'e) -> ('a, 'b, 'c, 'd, 'e, nothing,) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.40: ty1 = ty0 //│ ║ ^^^^^^^^^ //│ ╟── type `A & 'a` does not match type `'b` //│ ║ l.26: def ty1: ('a & A | 'b & B | 'c & C | 'd & D | 'e & E) -> ('a, 'b, 'c, 'd, 'e, 'f) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.26: def ty1: ('a & A | 'b & B | 'c & C | 'd & D | 'e & E) -> ('a, 'b, 'c, 'd, 'e, 'f) //│ ╙── ^^ //│ constrain calls : 70 //│ annoying calls : 51 //│ subtyping calls : 1048 ================================================ FILE: shared/src/test/diff/mlscript/StressTraits.mls ================================================ :NoJS trait A[T]: { fA: T } trait B[T]: { fB: T } trait C[T]: { fC: T } trait D[T]: { fD: T } trait E[T]: { fE: T } trait F[T]: { fF: T } trait G[T]: { fG: T } trait H[T]: { fH: T } //│ Defined trait A[+T] //│ Defined trait B[+T] //│ Defined trait C[+T] //│ Defined trait D[+T] //│ Defined trait E[+T] //│ Defined trait F[+T] //│ Defined trait G[+T] //│ Defined trait H[+T] :stats def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF | G -> x.fG | H -> x.fH } //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | ~#D & ({fE: 'fA} & #E | ~#E & ({fF: 'fA} & #F | ~#F & ({fG: 'fA} & #G | {fH: 'fA} & #H & ~#G))))))) -> 'fA //│ constrain calls : 26 //│ annoying calls : 0 //│ subtyping calls : 756 // ====== 1 & all ====== // :stats def arg: A[int] foo arg //│ arg: A[int] //│ res: int //│ constrain calls : 19 //│ annoying calls : 9 //│ subtyping calls : 248 :stats :e def arg: A[int] | B[int] foo arg //│ arg: A[int] | B[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.51: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.22: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.21: def foo x = case x of { //│ ╙── ^ //│ res: error | int //│ constrain calls : 36 //│ annoying calls : 31 //│ subtyping calls : 724 :stats :e def arg: A[int] | B[int] | C[int] foo arg //│ arg: A[int] | B[int] | C[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.71: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.22: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.21: def foo x = case x of { //│ ╙── ^ //│ res: error | int //│ constrain calls : 65 //│ annoying calls : 90 //│ subtyping calls : 3498 :stats :e def arg: A[int] | B[int] | D[int] foo arg //│ arg: A[int] | B[int] | D[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.91: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.22: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.21: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 82 //│ annoying calls : 209 //│ subtyping calls : 17621 // ====== 2 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB } def arg: A[int] | B[int] foo arg //│ foo: ({fB: 'fA} & #B & ~#A | {fA: 'fA} & #A) -> 'fA //│ arg: A[int] | B[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.118: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.114: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.113: def foo x = case x of { //│ ╙── ^ //│ res: error | int //│ constrain calls : 44 //│ annoying calls : 31 //│ subtyping calls : 520 // ====== 3 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC } def arg: A[int] | B[int] | C[int] foo arg //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | {fC: 'fA} & #C & ~#B)) -> 'fA //│ arg: A[int] | B[int] | C[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.147: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.142: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.141: def foo x = case x of { //│ ╙── ^ //│ res: error | int //│ constrain calls : 76 //│ annoying calls : 90 //│ subtyping calls : 3279 // ====== 4 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD } def arg: A[int] | B[int] | C[int] | D[int] foo arg //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | {fD: 'fA} & #D & ~#C))) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.177: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.171: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.170: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 94 //│ annoying calls : 131 //│ subtyping calls : 4350 :stats :e foo (arg with { x = 1} with { y = 2 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.197: foo (arg with { x = 1} with { y = 2 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#B & ({fB: int, x: 1, y: 2} & ~#B | {fB: int, x: 1, y: 2} & ~?a | {fB: int, x: 1, y: 2} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.171: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.170: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 69 //│ annoying calls : 128 //│ subtyping calls : 3820 :stats :e foo (arg with { x = 1; y = 2; z = 3 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.215: foo (arg with { x = 1; y = 2; z = 3 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#B & ({fB: int, x: 1, y: 2, z: 3} & ~#B | {fB: int, x: 1, y: 2, z: 3} & ~?a | {fB: int, x: 1, y: 2, z: 3} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.171: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.170: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 69 //│ annoying calls : 128 //│ subtyping calls : 3820 // ====== 5 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE } def arg: A[int] | B[int] | C[int] | D[int] | E[int] foo arg //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | {fE: 'fA} & #E & ~#D)))) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.244: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.237: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.236: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 98 //│ annoying calls : 131 //│ subtyping calls : 4638 // ====== 6 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF } def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] foo arg //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | ~#D & ({fE: 'fA} & #E | {fF: 'fA} & #F & ~#E))))) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.276: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.268: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.267: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 102 //│ annoying calls : 131 //│ subtyping calls : 4984 // ====== 7 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF | G -> x.fG } def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] foo arg //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | ~#D & ({fE: 'fA} & #E | ~#E & ({fF: 'fA} & #F | {fG: 'fA} & #G & ~#F)))))) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.309: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.300: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.299: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 106 //│ annoying calls : 131 //│ subtyping calls : 5393 def foo_manual: ({fA: 'a} & a | {fB: 'a} & b & ~a | {fC: 'a} & c & ~a & ~b | {fD: 'a} & d & ~a & ~b & ~c | {fE: 'a} & e & ~a & ~b & ~c & ~d | {fF: 'a} & f & ~a & ~b & ~c & ~d & ~e | {fG: 'a} & g & ~a & ~b & ~c & ~d & ~e & ~f) -> 'a //│ foo_manual: ({fA: 'a} & #A | ~#A & ({fB: 'a} & #B | ~#B & ({fC: 'a} & #C | ~#C & ({fD: 'a} & #D | ~#D & ({fE: 'a} & #E | ~#E & ({fF: 'a} & #F | {fG: 'a} & #G & ~#F)))))) -> 'a :stats :e foo_manual arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.332: foo_manual arg //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `{fB: int} & #B` does not match type `{fA: 'a} & #A | ~#A & ({fB: 'a} & #B | ~#B & ({fC: 'a} & #C | ~#C & ({fD: 'a} & #D | ~#D & ({fE: 'a} & #E | ~#E & ({fF: 'a} & #F | {fG: 'a} & #G & ~#F)))))` //│ ║ l.308: def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `{fA: 'a0} & #A | ~#A & ({fB: 'a0} & #B | ~#B & ({fC: 'a0} & #C | ~#C & ({fD: 'a0} & #D | ~#D & ({fE: 'a0} & #E | ~#E & ({fF: 'a0} & #F | {fG: 'a0} & #G & ~#F)))))` //│ ║ l.332: foo_manual arg //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.327: def foo_manual: ({fA: 'a} & a | {fB: 'a} & b & ~a | {fC: 'a} & c & ~a & ~b | {fD: 'a} & d & ~a & ~b & ~c | {fE: 'a} & e & ~a & ~b & ~c & ~d | {fF: 'a} & f & ~a & ~b & ~c & ~d & ~e | {fG: 'a} & g & ~a & ~b & ~c & ~d & ~e & ~f) -> 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: error //│ constrain calls : 24 //│ annoying calls : 67 //│ subtyping calls : 4417 :stats foo_manual = foo //│ ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | ~#D & ({fE: 'fA} & #E | ~#E & ({fF: 'fA} & #F | {fG: 'fA} & #G & ~#F)))))) -> 'fA //│ <: foo_manual: //│ ({fA: 'a} & #A | ~#A & ({fB: 'a} & #B | ~#B & ({fC: 'a} & #C | ~#C & ({fD: 'a} & #D | ~#D & ({fE: 'a} & #E | ~#E & ({fF: 'a} & #F | {fG: 'a} & #G & ~#F)))))) -> 'a //│ constrain calls : 91 //│ annoying calls : 183 //│ subtyping calls : 3611 // ====== 8 ====== // :stats :e def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC | D -> x.fD | E -> x.fE | F -> x.fF | G -> x.fG | H -> x.fH } def arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] | H[int] foo arg //│ foo: ({fA: 'fA} & #A | ~#A & ({fB: 'fA} & #B | ~#B & ({fC: 'fA} & #C | ~#C & ({fD: 'fA} & #D | ~#D & ({fE: 'fA} & #E | ~#E & ({fF: 'fA} & #F | ~#F & ({fG: 'fA} & #G | {fH: 'fA} & #H & ~#G))))))) -> 'fA //│ arg: A[int] | B[int] | C[int] | D[int] | E[int] | F[int] | G[int] | H[int] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.375: foo arg //│ ║ ^^^^^^^ //│ ╟── expression of type `#B & ({fB: int} & ~#B | {fB: int} & ~?a | {fB: int} & #A)` does not have field 'fA' //│ ╟── Note: constraint arises from field selection: //│ ║ l.365: | A -> x.fA //│ ║ ^^^^ //│ ╟── from refined scrutinee: //│ ║ l.364: def foo x = case x of { //│ ╙── ^ //│ res: error //│ constrain calls : 110 //│ annoying calls : 131 //│ subtyping calls : 5870 ================================================ FILE: shared/src/test/diff/mlscript/StressUgly.mls ================================================ class Lit: { val: int } class Add[E]: { lhs: E; rhs: E } method In: E -> E method In = id //│ Defined class Lit //│ Defined class Add[=E] //│ Declared Add.In: Add['E] -> 'E -> 'E //│ Defined Add.In: Add['E] -> 'a -> 'a def eval1_ty_ugly: Add['b] | 'a & ~Lit as 'b //│ eval1_ty_ugly: 'b //│ where //│ 'b := Add['b] | 'a & ~Lit //│ = // def eval1_ty: Add['b] def eval1_ty: Add[int] // ~500 // def eval1_ty: Add['b] | 'a // ~800 // def eval1_ty: Add['b] //│ eval1_ty: Add[int] //│ = // :stdout // :d :stats :e eval1_ty = eval1_ty_ugly //│ 'b //│ where //│ 'b := Add['b] | 'a & ~Lit //│ <: eval1_ty: //│ Add[int] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.30: eval1_ty = eval1_ty_ugly //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Add['b]` is not an instance of type `int` //│ ║ l.13: def eval1_ty_ugly: Add['b] | 'a & ~Lit as 'b //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.20: def eval1_ty: Add[int] // ~500 //│ ╙── ^^^ //│ = //│ eval1_ty_ugly is not implemented //│ constrain calls : 54 //│ annoying calls : 46 //│ subtyping calls : 433 ================================================ FILE: shared/src/test/diff/mlscript/Subsume.mls ================================================ // Declare signature: def f: int -> int //│ f: int -> int //│ = // Define body: def f x = x //│ 'a -> 'a //│ <: f: //│ int -> int //│ = [Function: f] :e f = 42 //│ 42 //│ <: f: //│ int -> int //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.15: f = 42 //│ ║ ^^^^^^ //│ ╟── integer literal of type `42` is not a function //│ ║ l.15: f = 42 //│ ║ ^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.3: def f: int -> int //│ ╙── ^^^^^^^^^^ //│ = 42 :re f 1 //│ res: int //│ Runtime error: //│ TypeError: f1 is not a function fun x -> f x //│ res: int -> int //│ = [Function: res] def boom: anything //│ boom: anything //│ = boom = 1 //│ 1 //│ <: boom: //│ anything //│ = 1 :e add boom 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.53: add boom 1 //│ ║ ^^^^^^^^ //│ ╟── type `anything` is not an instance of type `int` //│ ║ l.42: def boom: anything //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.53: add boom 1 //│ ╙── ^^^^ //│ res: error | int //│ = 2 def boom: nothing //│ boom: nothing //│ = :e def boom = 1 //│ 1 //│ <: boom: //│ nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.72: def boom = 1 //│ ║ ^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `nothing` //│ ║ l.72: def boom = 1 //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.67: def boom: nothing //│ ╙── ^^^^^^^ //│ = [Function: boom1] def boom: 'a //│ boom: nothing //│ = :e boom = 1 //│ 1 //│ <: boom: //│ nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.93: boom = 1 //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `'a` //│ ║ l.93: boom = 1 //│ ║ ^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.88: def boom: 'a //│ ╙── ^^ //│ = 1 def id: 'a -> 'a //│ id: 'a -> 'a //│ = id 1 id true //│ res: 1 //│ = //│ id is not implemented //│ res: true //│ = //│ id is not implemented def id x = x //│ 'a -> 'a //│ <: id: //│ 'a -> 'a //│ = [Function: id1] id 1 id true //│ res: 1 //│ = 1 //│ res: true //│ = true :e def id n = add n 1 //│ int -> int //│ <: id: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.138: def id n = add n 1 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.110: def id: 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.138: def id n = add n 1 //│ ╙── ^ //│ = [Function: id2] :e def id (x: int) = x //│ int -> int //│ <: id: //│ 'a -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.154: def id (x: int) = x //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── type `'a` is not an instance of type `int` //│ ║ l.110: def id: 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.154: def id (x: int) = x //│ ╙── ^^^ //│ = [Function: id3] def f: (int & 'a) -> 'a //│ f: (int & 'a) -> 'a //│ = def impl a = a a //│ impl: ('a -> 'b & 'a) -> 'b //│ = [Function: impl] :e def f = impl //│ ('a -> 'b & 'a) -> 'b //│ <: f: //│ (int & 'a) -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.180: def f = impl //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int & 'a` is not a function //│ ║ l.171: def f: (int & 'a) -> 'a //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.175: def impl a = a a //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.175: def impl a = a a //│ ╙── ^ //│ = [Function: f2] :re f 1 //│ res: 1 //│ Runtime error: //│ TypeError: a is not a function def f: (int & 'a) -> (int & 'a) -> 'a def f a b = if gt a b then a else b //│ f: (int & 'a) -> (int & 'a) -> 'a //│ = //│ (number & 'a) -> (number & 'a) -> 'a //│ <: f: //│ (int & 'a) -> (int & 'a) -> 'a //│ = [Function: f3] f 1 //│ res: (int & 'a) -> (1 | 'a) //│ = [Function (anonymous)] :e def f a b = if gt a b then a else b b // mistake! //│ (number & 'a) -> nothing -> 'a //│ <: f: //│ (int & 'a) -> (int & 'a) -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.219: def f a b = if gt a b then a else b b // mistake! //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `int & 'a` is not a function //│ ║ l.205: def f: (int & 'a) -> (int & 'a) -> 'a //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.219: def f a b = if gt a b then a else b b // mistake! //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.219: def f a b = if gt a b then a else b b // mistake! //│ ╙── ^ //│ = [Function: f4] f 1 //│ res: (int & 'a) -> (1 | 'a) //│ = [Function (anonymous)] :re res 2 //│ res: 1 | 2 //│ Runtime error: //│ TypeError: b is not a function :e def g: (int & 'a) -> 'a def g a = add a 1 //│ g: (int & 'a) -> 'a //│ = //│ int -> int //│ <: g: //│ (int & 'a) -> 'a //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.250: def g a = add a 1 //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── application of type `int` does not match type `'a` //│ ║ l.250: def g a = add a 1 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.249: def g: (int & 'a) -> 'a //│ ╙── ^^ //│ = [Function: g] // --- // The Twice Function // --- // def twice f x = f (f x) //│ twice: ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ = [Function: twice] def ty1: (('b | 'c) -> 'c) -> 'b -> 'c def ty2: ('b -> ('b & 'd)) -> 'b -> 'd //│ ty1: (('b | 'c) -> 'c) -> 'b -> 'c //│ = //│ ty2: ('b -> ('b & 'd)) -> 'b -> 'd //│ = ty1 = ty2 //│ ('b -> ('b & 'd)) -> 'b -> 'd //│ <: ty1: //│ (('b | 'c) -> 'c) -> 'b -> 'c //│ = //│ ty2 is not implemented ty2 = ty1 //│ (('b | 'c) -> 'c) -> 'b -> 'c //│ <: ty2: //│ ('b -> ('b & 'd)) -> 'b -> 'd //│ = //│ ty1 and ty2 are not implemented ty1 = twice ty2 = twice //│ ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ <: ty1: //│ (('b | 'c) -> 'c) -> 'b -> 'c //│ = [Function: twice] //│ ('a -> 'b & 'b -> 'c) -> 'a -> 'c //│ <: ty2: //│ ('b -> ('b & 'd)) -> 'b -> 'd //│ = [Function: twice] twice twice //│ res: ('a -> 'b & 'b -> ('a & 'c)) -> 'a -> 'c //│ = [Function (anonymous)] // * Note: cycle check fails when generalizing curried lambdas twice twice! //│ res: ('a -> ('a & 'b) & 'b -> ('a & 'b & 'c)) -> 'a -> 'c //│ = [Function (anonymous)] // --- // Self Application // --- // def self1: ('a -> 'b & 'a) -> 'b //│ self1: ('a -> 'b & 'a) -> 'b //│ = def self1 a = a a //│ ('a -> 'b & 'a) -> 'b //│ <: self1: //│ ('a -> 'b & 'a) -> 'b //│ = [Function: self1] fun a -> a a //│ res: ('a -> 'b & 'a) -> 'b //│ = [Function: res] self1 = res //│ ('a -> 'b & 'a) -> 'b //│ <: self1: //│ ('a -> 'b & 'a) -> 'b //│ = [Function: res] r = fun a -> a a //│ r: ('a -> 'b & 'a) -> 'b //│ = [Function: r] self1 = r //│ ('a -> 'b & 'a) -> 'b //│ <: self1: //│ ('a -> 'b & 'a) -> 'b //│ = [Function: r] def self2: 'a -> 'b as 'a //│ self2: 'a //│ where //│ 'a := 'a -> 'b //│ = // :e // fails since the fix to spurious-TV-cycles // works since assigned TVs def self2 = self1 //│ ('a -> 'b & 'a) -> 'b //│ <: self2: //│ 'a //│ where //│ 'a := 'a -> 'b //│ = [Function: self2] fun p -> fun q -> p q p //│ res: ('a -> 'b -> 'c & 'b) -> 'a -> 'c //│ = [Function: res] :ns fun p -> fun q -> p q p //│ res: forall 'a 'b 'c 'd. 'a -> 'b -> 'd //│ where //│ 'a <: 'b -> 'c //│ 'c <: 'a -> 'd //│ = [Function: res] ================================================ FILE: shared/src/test/diff/mlscript/Test.mls ================================================ class Person: { name: string; age: int } def Person = fun n -> fun a -> Person { name = n; age = a } //│ Defined class Person //│ Person: (string & 'name) -> (int & 'age) -> (Person with {age: 'age, name: 'name}) //│ = [Function: Person1] class Animal: {} //│ Defined class Animal def test = fun x -> case x of { Person -> x.age | Animal -> 0 } //│ test: (Animal | (Person\name with {age: 'age})) -> (0 | 'age) //│ = [Function: test] test: Person & {age: 'a} | Animal -> 'a | 0 //│ res: 0 | Person & {age: 0} | Animal -> 0 //│ = [Function: test] test: (Person & {age: int} | Animal) -> int //│ res: (Animal | Person) -> int //│ = [Function: test] a = Animal{} //│ a: Animal //│ = Animal {} a: {} //│ res: anything //│ = Animal {} p = Person "Bob" 42 //│ p: Person & {age: 42, name: "Bob"} //│ = Person { name: 'Bob', age: 42 } p: { age: int } //│ res: {age: int} //│ = Person { name: 'Bob', age: 42 } test a //│ res: 0 //│ = 0 test p //│ res: 0 | 42 //│ = 42 1: int //│ res: int //│ = 1 def f: 'a -> 'a & string //│ f: nothing //│ = def f: 'a -> ('a & string) //│ f: 'a -> (string & 'a) //│ = ================================================ FILE: shared/src/test/diff/mlscript/This.mls ================================================ :NoJS class A method Boom: int -> this //│ Defined class A //│ Declared A.Boom: (A & 'this) -> int -> (A & 'this) class B: A method Boom n = this //│ Defined class B //│ Defined B.Boom: (B & 'this) -> anything -> (B & 'this) a = B{} //│ a: B a.Boom 1 // : B //│ res: B & A def b: A b = a b.Boom 1 // : A //│ b: A //│ B //│ <: b: //│ A //│ res: A :w class Myself[A] method Me = this //│ Defined class Myself[±A] //│ Defined Myself.Me: (Myself[?] & 'this) -> (Myself[?] & 'this) //│ ╔══[WARNING] Type definition Myself has bivariant type parameters: //│ ║ l.31: class Myself[A] //│ ║ ^^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.31: class Myself[A] //│ ╙── ^ m0 = Myself {} //│ m0: Myself[?] m0.Me //│ res: Myself[?] :e class Addable[A] method Me = this method Oops = this.oops // disallowed if `this` is rigid method Add: A -> A //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.53: method Oops = this.oops // disallowed if `this` is rigid //│ ║ ^^^^^^^^^ //│ ╟── reference of type `Addable[A] & this` does not have field 'oops' //│ ║ l.53: method Oops = this.oops // disallowed if `this` is rigid //│ ╙── ^^^^ //│ Defined class Addable[=A] //│ Declared Addable.Add: Addable['A] -> 'A -> 'A //│ Defined Addable.Me: (Addable['A] & 'this) -> (Addable['A] & 'this) //│ Defined Addable.Oops: Addable['A] -> error class Base0 method Foo0: this //│ Defined class Base0 //│ Declared Base0.Foo0: (Base0 & 'this) -> (Base0 & 'this) class Derived0: Base0 //│ Defined class Derived0 Derived0.Foo0 //│ res: (Derived0 & 'this) -> (Base0 & 'this) class Base method Foo: this method Foo = this //│ Defined class Base //│ Declared Base.Foo: (Base & 'this) -> (Base & 'this) //│ Defined Base.Foo: (Base & 'this) -> (Base & 'this) class Derived: Base //│ Defined class Derived Base.Foo //│ res: (Base & 'this) -> (Base & 'this) Derived.Foo //│ res: (Derived & 'this) -> (Base & 'this) res (Derived{}) //│ res: Derived & Base (error: Derived).Foo //│ res: Derived & Base class Base2 method Foo2: this class Derived2: Base2 method Foo3 = (this.Foo2,) class DerivedDerived2: Derived2 method Foo2 = this.Foo3.0 //│ Defined class Base2 //│ Declared Base2.Foo2: (Base2 & 'this) -> (Base2 & 'this) //│ Defined class Derived2 //│ Defined Derived2.Foo3: (Derived2 & 'this) -> (Derived2 & Base2 & 'this,) //│ Defined class DerivedDerived2 //│ Defined DerivedDerived2.Foo2: (DerivedDerived2 & 'this) -> (DerivedDerived2 & Base2 & Derived2 & 'this) Derived2.Foo3 //│ res: (Derived2 & 'this) -> (Derived2 & Base2 & 'this,) DerivedDerived2.Foo3 //│ res: (DerivedDerived2 & 'this) -> (Derived2 & Base2 & 'this,) class NewBase method NewFoo: this method NewBar = (this.NewFoo,) //│ Defined class NewBase //│ Declared NewBase.NewFoo: (NewBase & 'this) -> (NewBase & 'this) //│ Defined NewBase.NewBar: (NewBase & 'this) -> (NewBase & 'this,) class NewDerived: NewBase method NewQux = this.NewBar.0 //│ Defined class NewDerived //│ Defined NewDerived.NewQux: (NewDerived & 'this) -> (NewDerived & NewBase & 'this) // Test methods of NewDerived. NewDerived.NewFoo NewDerived.NewBar NewDerived.NewQux //│ res: (NewDerived & 'this) -> (NewBase & 'this) //│ res: (NewDerived & 'this) -> (NewBase & 'this,) //│ res: (NewDerived & 'this) -> (NewDerived & NewBase & 'this) class NewDerivedDerived: NewDerived method NewQuz = (this.NewQux) //│ Defined class NewDerivedDerived //│ Defined NewDerivedDerived.NewQuz: (NewDerivedDerived & 'this) -> (NewDerivedDerived & NewBase & NewDerived & 'this) NewDerivedDerived.NewFoo NewDerivedDerived.NewBar NewDerivedDerived.NewQux NewDerivedDerived.NewQuz //│ res: (NewDerivedDerived & 'this) -> (NewBase & 'this) //│ res: (NewDerivedDerived & 'this) -> (NewBase & 'this,) //│ res: (NewDerivedDerived & 'this) -> (NewDerived & NewBase & 'this) //│ res: (NewDerivedDerived & 'this) -> (NewDerivedDerived & NewBase & NewDerived & 'this) :e class NewDerivedDerivedDerived: NewDerivedDerived method NewQux = this.NewBar.0 //│ ╔══[ERROR] Overriding method NewDerived.NewQux without explicit declaration is not allowed. //│ ║ l.158: method NewQux = this.NewBar.0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: method definition inherited from //│ ║ l.130: method NewQux = this.NewBar.0 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ Defined class NewDerivedDerivedDerived //│ Defined NewDerivedDerivedDerived.NewQux: (NewDerivedDerivedDerived & 'this) -> (NewDerivedDerivedDerived & NewBase & 'this) // Test methods of NewDerivedDerivedDerived NewDerivedDerivedDerived.NewBar NewDerivedDerivedDerived.NewFoo NewDerivedDerivedDerived.NewQuz NewDerivedDerivedDerived.NewQux //│ res: (NewDerivedDerivedDerived & 'this) -> (NewBase & 'this,) //│ res: (NewDerivedDerivedDerived & 'this) -> (NewBase & 'this) //│ res: (NewDerivedDerivedDerived & 'this) -> (NewDerivedDerived & NewBase & NewDerived & 'this) //│ res: (NewDerivedDerivedDerived & 'this) -> (NewDerivedDerivedDerived & NewBase & 'this) class Animal method Eat = this //│ Defined class Animal //│ Defined Animal.Eat: (Animal & 'this) -> (Animal & 'this) class Cat: Animal //│ Defined class Cat Animal.Eat //│ res: (Animal & 'this) -> (Animal & 'this) Cat.Eat //│ res: (Cat & 'this) -> (Animal & 'this) ================================================ FILE: shared/src/test/diff/mlscript/Ticks.mls ================================================ :js f' = 2 //│ // Prelude //│ let res; //│ // Query 1 //│ globalThis.f$ = 2; //│ // End of generated code //│ f': 2 //│ = 2 :js f' //│ // Query 1 //│ res = f$; //│ // End of generated code //│ res: 2 //│ = 2 :js f' + 2 //│ // Query 1 //│ res = f$ + 2; //│ // End of generated code //│ res: int //│ = 4 :js trait Foo'[T'] method Bar': T' -> T' //│ Defined trait Foo'[=T'] //│ Declared Foo'.Bar': Foo'['T'] -> 'T' -> 'T' //│ // Prelude //│ const Foo$ = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code :js class A'[B'] : {x': B'} method C' = this.x' //│ Defined class A'[+B'] //│ Defined A'.C': A'['B'] -> 'B' //│ // Prelude //│ class A$ { //│ constructor(fields) { //│ this["x'"] = fields["x'"]; //│ } //│ get "C'"() { //│ const self = this; //│ return self["x'"]; //│ } //│ } //│ // End of generated code :js class M : {x': int} method N' = this.x' method P' y' = this.x' + y' mm = M{ x' = 2 } mm.N' mm.P' f' //│ Defined class M //│ Defined M.N': M -> int //│ Defined M.P': M -> int -> int //│ // Prelude //│ class M { //│ constructor(fields) { //│ this["x'"] = fields["x'"]; //│ } //│ get "N'"() { //│ const self = this; //│ return self["x'"]; //│ } //│ "P'"(y$) { //│ const self = this; //│ return self["x'"] + y$; //│ } //│ } //│ // Query 1 //│ globalThis.mm = new M({ "x'": 2 }); //│ // Query 2 //│ res = mm["N'"]; //│ // Query 3 //│ res = mm["P'"](f$); //│ // End of generated code //│ mm: M & {x': 2} //│ = M { "x'": 2 } //│ res: int //│ = 2 //│ res: int //│ = 4 :js class D'[B'] : A'[B'] //│ Defined class D'[+B'] //│ // Prelude //│ class D$ extends A$ { //│ constructor(fields) { //│ super(fields); //│ } //│ } //│ // End of generated code :js type E' = D'[int] | A'[int] //│ Defined type alias E' //│ // End of generated code :js class None': {} //│ Defined class None' //│ // Prelude //│ class None$ {} //│ // End of generated code :js class CC': None' & Foo'[anything] //│ Defined class CC' //│ // Prelude //│ class CC$ extends None$ { //│ constructor(fields) { //│ super(fields); //│ Foo$.implement(this); //│ } //│ } //│ // End of generated code :js class TT' : { a': int } //│ Defined class TT' //│ // Prelude //│ class TT$ { //│ constructor(fields) { //│ this["a'"] = fields["a'"]; //│ } //│ } //│ // End of generated code :js tt' = TT'{a'=4} tt'.a' //│ // Query 1 //│ globalThis.tt$ = new TT$({ "a'": 4 }); //│ // Query 2 //│ res = tt$["a'"]; //│ // End of generated code //│ tt': TT' & {a': 4} //│ = TT$ { "a'": 4 } //│ res: 4 //│ = 4 :js a' = {b' = 42} //│ // Query 1 //│ globalThis.a$ = { "b'": 42 }; //│ // End of generated code //│ a': {b': 42} //│ = { "b'": 42 } :js a'.b' //│ // Query 1 //│ res = a$["b'"]; //│ // End of generated code //│ res: 42 //│ = 42 :js def g' x' = x' with { c' = 24 } //│ // Prelude //│ function withConstruct(target, fields) { //│ if (typeof target === "string" || typeof target === "number" || typeof target === "boolean" || typeof target === "bigint" || typeof target === "symbol") { //│ return Object.assign(target, fields); //│ } //│ if (target instanceof String || target instanceof Number || target instanceof Boolean || target instanceof BigInt) { //│ return Object.assign(target.valueOf(), target, fields); //│ } //│ if (Array.isArray(target)) { //│ const clone = Array.from(target); //│ for (const key in target){ //│ clone[key] = target[key]; //│ } //│ for (const key in fields){ //│ clone[key] = fields[key]; //│ } //│ return clone; //│ } //│ if (target == null) { //│ return Object.assign({}, {}, fields); //│ } //│ const copy = Object.assign({}, target, fields); //│ Object.setPrototypeOf(copy, Object.getPrototypeOf(target)); //│ return copy; //│ } //│ // Query 1 //│ globalThis.g$ = function g$(x$) { //│ return withConstruct(x$, { "c'": 24 }); //│ }; //│ // End of generated code //│ g': 'a -> ('a\c' & {c': 24}) //│ = [Function: g$] :js gg' = let w' = 2 in w' + w' //│ // Query 1 //│ globalThis.gg$ = ((w$) => w$ + w$)(2); //│ // End of generated code //│ gg': int //│ = 4 :js rcd' = { x' = 1 } rcd'.x' //│ // Query 1 //│ globalThis.rcd$ = { "x'": 1 }; //│ // Query 2 //│ res = rcd$["x'"]; //│ // End of generated code //│ rcd': {x': 1} //│ = { "x'": 1 } //│ res: 1 //│ = 1 :js foo { x' } = x' //│ // Query 1 //│ globalThis.foo = function foo({ "x'": x$ }) { //│ return x$; //│ }; //│ // End of generated code //│ foo: {x': 'a} -> 'a //│ = [Function: foo] :js foo { x' = y' } = y' //│ // Query 1 //│ globalThis.foo1 = function foo1({ "x'": y$ }) { //│ return y$; //│ }; //│ // End of generated code //│ foo: {x': 'a} -> 'a //│ = [Function: foo1] :js trait T : {x': int} method N' = this.x' method P' y' = this.x' + y' method Q ((y', {z'})) = this.x' + y' + z' //│ Defined trait T //│ Defined T.N': T -> int //│ Defined T.P': T -> int -> int //│ Defined T.Q: T -> ((int, {z': int},),) -> int //│ // Prelude //│ const T = function () { //│ const tag = Symbol(); //│ return ({ //│ implement: function implement(instance) { //│ if (tag in instance) { //│ return; //│ } //│ Object.defineProperty(instance, tag, { value: {} }); //│ if (!("N'" in instance)) { //│ Object.defineProperty(instance, "N'", { //│ get: function () { //│ return this1["x'"]; //│ } //│ }); //│ } //│ if (!("P'" in instance)) { //│ instance["P'"] = function (y$) { //│ return this1["x'"] + y$; //│ }; //│ } //│ if (!("Q" in instance)) { //│ instance.Q = function ([ //│ y$, //│ { "z'": z$ } //│ ]) { //│ return this1["x'"] + y$ + z$; //│ }; //│ } //│ }, //│ build: function build(instance) { //│ if (typeof instance !== "object") { //│ instance = Object.assign(instance, {}); //│ } //│ this.implement(instance); //│ return instance; //│ }, //│ is: function is(x) { //│ return typeof x === "object" && x !== null && tag in x; //│ } //│ }); //│ }(); //│ // End of generated code :js let f' = fun x -> x + 1 in f' 2 //│ // Query 1 //│ res = ((f$) => f$(2))((x) => x + 1); //│ // End of generated code //│ res: int //│ = 3 :js foo { return } = return //│ // Query 1 //│ globalThis.foo2 = function foo2({ "return": return$ }) { //│ return return$; //│ }; //│ // End of generated code //│ foo: {return: 'a} -> 'a //│ = [Function: foo2] foo { return = 1 } //│ res: 1 //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/Tony.mls ================================================ class Some[A]: { value: A } class None: {} //│ Defined class Some[+A] //│ Defined class None def flatMap3 = fun f -> fun opt -> case opt of { Some -> f opt | _ -> opt } //│ flatMap3: ('a -> 'b) -> (Some[?] & 'a | 'b & ~#Some) -> 'b //│ = [Function: flatMap3] arg = if true then Some{value = 42} with {payload = 23} else None {} //│ arg: None | Some[42] & {payload: 23} //│ = Some { value: 42, payload: 23 } // > TODO don't distribute neg inters + handle better at constraint top level :stats flatMap3 (fun x -> add x.value x.payload) arg //│ res: None | int //│ = 65 //│ constrain calls : 82 //│ annoying calls : 23 //│ subtyping calls : 350 arg = if true then Some{value = 42} else None {} //│ arg: None | Some[42] //│ = Some { value: 42 } flatMap3 (fun x -> x.value) arg //│ res: 42 | None //│ = 42 foo = flatMap3 (fun x -> x.value) //│ foo: (Some[?] & {value: 'a} | 'a & ~#Some) -> 'a //│ = [Function (anonymous)] foo arg //│ res: 42 | None //│ = 42 foo 1 //│ res: 1 //│ = 1 fn = foo None //│ fn: anything -> None //│ = [Function (anonymous)] // :d fn{} // foo None {} //│ res: None //│ = None {} // :d foo (None{}) //│ res: None //│ = None {} fun f -> flatMap3 f arg //│ res: (Some[42] -> 'a) -> (None | 'a) //│ = [Function: res] foo = flatMap3 (fun x -> x) //│ foo: (Some[?] & 'a | 'a & ~#Some) -> 'a //│ = [Function (anonymous)] foo 1 //│ res: 1 //│ = 1 def simpler = fun f -> case None{} of { Some -> f 1 | _ -> None{} } //│ simpler: (1 -> 'a) -> (None | 'a) //│ = [Function: simpler] def simpler = fun f -> fun opt -> case opt of { Some -> f opt | None -> opt } //│ simpler: ('a -> 'b) -> (None & 'b | Some[?] & 'a) -> 'b //│ = [Function: simpler1] simpler (fun x -> x.value) //│ res: (None & 'a | Some[?] & {value: 'b}) -> ('b | 'a) //│ = [Function (anonymous)] :e res 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.94: res 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `None & ?a | Some[?] & ?b` //│ ║ l.94: res 1 //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.85: def simpler = fun f -> fun opt -> case opt of { Some -> f opt | None -> opt } //│ ╙── ^^^ //│ res: error //│ Runtime error: //│ Error: non-exhaustive case expression def funny = fun f -> case f of { Some -> f f } //│ funny: nothing -> nothing //│ = [Function: funny] ================================================ FILE: shared/src/test/diff/mlscript/TraitMatching.mls ================================================ trait MyTrait[A]: { value: A } //│ Defined trait MyTrait[+A] def test1 x = case x of { MyTrait -> x.value | _ -> 0 } //│ test1: ({value: 'value} & #MyTrait | ~#MyTrait) -> (0 | 'value) //│ = [Function: test1] :e test1 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.11: test1 1 //│ ║ ^^^^^^^ //│ ╟── expression of type `1 & ~?a | 1 & #MyTrait` does not have field 'value' //│ ╟── Note: constraint arises from field selection: //│ ║ l.6: def test1 x = case x of { MyTrait -> x.value | _ -> 0 } //│ ║ ^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.6: def test1 x = case x of { MyTrait -> x.value | _ -> 0 } //│ ╙── ^ //│ res: 0 | error //│ = 0 test1 { value = 1 } //│ res: 0 | 1 //│ = 0 mt = MyTrait { value = 1 } //│ mt: {value: 1} & #MyTrait //│ = { value: 1 } test1 mt //│ res: 0 | 1 //│ = 1 class C1: MyTrait[int] //│ Defined class C1 test1 (C1 { value = 1 }) //│ res: 0 | 1 //│ = 1 def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ test2: ({default: 'value} & ~#MyTrait | {value: 'value} & #MyTrait) -> 'value //│ = [Function: test2] :e test2 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.50: test2 1 //│ ║ ^^^^^^^ //│ ╟── expression of type `1 & ~#MyTrait` does not have field 'default' //│ ╟── Note: constraint arises from field selection: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ╙── ^^^^^^^^^ //│ res: error //│ = undefined :e test2 { value = 1 } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.62: test2 { value = 1 } //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `{value: 1} & ~#MyTrait` does not have field 'default' //│ ╟── Note: constraint arises from field selection: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ╙── ^^^^^^^^^ //│ res: 1 | error //│ = undefined test2 { value = 1; default = "hi" } //│ res: "hi" | 1 //│ = 'hi' test2 mt //│ res: 1 //│ = 1 test2 (C1 { value = 1 }) //│ res: 1 //│ = 1 // TODO: ability to declare classes as `final` so they can be assumed _not_ to inherit from a trait? class C2: { default: string } //│ Defined class C2 :e test2 (C2 { default = "oops" }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.91: test2 (C2 { default = "oops" }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `C2 & {default: ?default} & ~?a | C2 & {default: ?default} & #MyTrait` does not have field 'value' //│ ╟── Note: constraint arises from field selection: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ║ ^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ╙── ^ //│ res: "oops" | error //│ = 'oops' // Note: `value` is unused here; would deserve a warning c2 = C2 { value = 1; default = "oops" } //│ c2: C2 & {default: "oops"} //│ = C2 { default: 'oops' } :e test2 c2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.111: test2 c2 //│ ║ ^^^^^^^^ //│ ╟── expression of type `C2 & {default: ?default} & ~?a | C2 & {default: ?default} & #MyTrait` does not have field 'value' //│ ╟── Note: constraint arises from field selection: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ║ ^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ╙── ^ //│ res: "oops" | error //│ = 'oops' test2 (c2 with { value = 1 }) //│ res: "oops" | 1 //│ = 'oops' // Note: if we allowed this, we'd have to be careful in the semantics of trait ctors! :e class C3: { default: string } & ~myTrait //│ ╔══[ERROR] cannot inherit from a type negation //│ ║ l.132: class C3: { default: string } & ~myTrait //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e class C3: { default: string } & ~MyTrait[anything] //│ ╔══[ERROR] cannot inherit from a type negation //│ ║ l.138: class C3: { default: string } & ~MyTrait[anything] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ def strawman: C2 & ~MyTrait[anything] //│ strawman: C2 & ~MyTrait[?] //│ = :e test2 strawman //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.148: test2 strawman //│ ║ ^^^^^^^^^^^^^^ //│ ╟── expression of type `C2 & ~{value: anything} & ~?a | C2 & #MyTrait & ~{value: anything}` does not have field 'value' //│ ╟── Note: constraint arises from field selection: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ║ ^^^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.45: def test2 x = case x of { MyTrait -> x.value | _ -> x.default } //│ ╙── ^ //│ res: error | string //│ = //│ strawman is not implemented strawman: C2 //│ res: C2 //│ = //│ strawman is not implemented :e strawman: ~{ value: anything } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.169: strawman: ~{ value: anything } //│ ║ ^^^^^^^^ //│ ╟── type `C2 & ~MyTrait[?]` does not match type `~{value: anything}` //│ ║ l.143: def strawman: C2 & ~MyTrait[anything] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `~{value: anything}` //│ ║ l.169: strawman: ~{ value: anything } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type negation: //│ ║ l.169: strawman: ~{ value: anything } //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ res: ~{value: anything} //│ = //│ strawman is not implemented ================================================ FILE: shared/src/test/diff/mlscript/Traits.mls ================================================ trait A: { x: int } trait B: { y: string } class C: { x: 0 | 1 } //│ Defined trait A //│ Defined trait B //│ Defined class C A { x = 1 } //│ res: {x: 1} & #A //│ = { x: 1 } :e A {} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.14: A {} //│ ║ ^^^^ //│ ╟── record literal of type `anything` does not have field 'x' //│ ║ l.14: A {} //│ ║ ^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.1: trait A: { x: int } //│ ╙── ^^^^^^^^^^ //│ res: error | #A //│ = {} class D: C & A & B //│ Defined class D D //│ res: {x: 'x & (0 | 1), y: string & 'y} -> (D with {x: 'x, y: 'y}) //│ = [Function: res] :re error: A & B //│ res: A & B //│ Runtime error: //│ Error: an error was thrown :e dv = D{} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.42: dv = D{} //│ ║ ^^^ //│ ╟── record literal of type `anything` is not a record (expected a record with fields: y, x) //│ ║ l.42: dv = D{} //│ ╙── ^^ //│ dv: D & {x: nothing, y: nothing} | error //│ = D { x: undefined, y: undefined } dv = D { x = 0; y = "hello" } //│ dv: D & {x: 0, y: "hello"} //│ = D { x: 0, y: 'hello' } :e dv: nothing //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.57: dv: nothing //│ ║ ^^ //│ ╟── application of type `D with {x: ?x, y: ?y}` does not match type `nothing` //│ ║ l.52: dv = D { x = 0; y = "hello" } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing` //│ ║ l.57: dv: nothing //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.57: dv: nothing //│ ╙── ^^^^^^^ //│ res: nothing //│ = D { x: 0, y: 'hello' } dv: A & B //│ res: A & B //│ = D { x: 0, y: 'hello' } :e dv: A & B: nothing //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.78: dv: A & B: nothing //│ ║ ^^ //│ ╟── type `A & B` does not match type `nothing` //│ ║ l.78: dv: A & B: nothing //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.78: dv: A & B: nothing //│ ╙── ^^^^^^^ //│ res: nothing //│ = D { x: 0, y: 'hello' } trait Indirect: C //│ Defined trait Indirect class E: Indirect //│ Defined class E :e E{} //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.99: E{} //│ ║ ^^^ //│ ╟── record literal of type `anything` does not have field 'x' //│ ║ l.99: E{} //│ ╙── ^^ //│ res: E & {x: nothing} | error //│ = E {} ev = E { x = 1 } ev: C //│ ev: E & {x: 1} //│ = E {} //│ res: C //│ = E {} trait T1: C & A //│ Defined trait T1 :e def t = T1 (A { x = 1 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.121: def t = T1 (A { x = 1 }) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{x: 1}` does not match type `C | ~#A` //│ ║ l.121: def t = T1 (A { x = 1 }) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into application with expected type `C | ~#A` //│ ║ l.121: def t = T1 (A { x = 1 }) //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.117: trait T1: C & A //│ ║ ^ //│ ╟── from intersection type: //│ ║ l.117: trait T1: C & A //│ ╙── ^^^^^ //│ t: error | {x: 1} & #A & #T1 //│ = [Function: t] t = T1 (A (C { x = 1 })) //│ t: C & {x: 1} & #A & #T1 //│ = C { x: 1 } class CA: C & A //│ Defined class CA t = T1 (CA { x = 1 }) //│ t: CA & {x: 1} & #T1 //│ = CA { x: 1 } class C1: B & T1 & { y: "hi" | "hello" } //│ Defined class C1 :e c1 = C1 { } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.156: c1 = C1 { } //│ ║ ^^^^^^ //│ ╟── record literal of type `anything` is not a record (expected a record with fields: y, x) //│ ║ l.156: c1 = C1 { } //│ ╙── ^^^ //│ c1: C1 & {x: nothing, y: nothing} | error //│ = C1 { y: undefined, x: undefined } c1 = C1 { x = 1; y = "hi" } //│ c1: C1 & {x: 1, y: "hi"} //│ = C1 { y: 'hi', x: 1 } c1: T1 //│ res: T1 //│ = C1 { y: 'hi', x: 1 } c1: A //│ res: A //│ = C1 { y: 'hi', x: 1 } c1.x c1.y //│ res: 1 //│ = 1 //│ res: "hi" //│ = 'hi' ================================================ FILE: shared/src/test/diff/mlscript/Trans.mls ================================================ class A class B: A class C: B c = C{} //│ Defined class A //│ Defined class B //│ Defined class C //│ c: C //│ = C {} c: C //│ res: C //│ = C {} c: B //│ res: B //│ = C {} res: A //│ res: A //│ = C {} c: A //│ res: A //│ = C {} a = res //│ a: A //│ = C {} :e a: B //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.33: a: B //│ ║ ^ //│ ╟── type `A` is not an instance of type `B` //│ ║ l.24: c: A //│ ║ ^ //│ ╟── but it flows into reference with expected type `B` //│ ║ l.33: a: B //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.33: a: B //│ ╙── ^ //│ res: B //│ = C {} :e a: C //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.50: a: C //│ ║ ^ //│ ╟── type `A` is not an instance of type `C` //│ ║ l.24: c: A //│ ║ ^ //│ ╟── but it flows into reference with expected type `C` //│ ║ l.50: a: C //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: a: C //│ ╙── ^ //│ res: C //│ = C {} // Transitivity is currently broken for primitive literals: 42: int res: number //│ res: int //│ = 42 //│ res: number //│ = 42 42: number //│ res: number //│ = 42 ================================================ FILE: shared/src/test/diff/mlscript/TrickyExtrusion.mls ================================================ // From SuperType tests True = true //│ True: true //│ = true def inc: int -> int //│ inc: int -> int //│ = test f = let r = fun x -> fun g -> (g x, ) in (r 0 inc, r True not) //│ test: anything -> ((int,), (bool,),) //│ = //│ inc is not implemented // * Precise type: test f = (f 0, f True) //│ test: (0 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: test] // * Imprecise type (notice the `(0 | true)`): // * Note: With inferred first-class constrained types and generalized curried functions, // * this approximation be avoided (see below). test f = let r x = f x in (r 0, r True) //│ test: ((0 | true) -> 'a) -> ('a, 'a,) //│ = [Function: test1] // Same example, a little elaborated test f = let r = fun x -> fun g -> (g x, f x) in (r 0 inc, r True not) //│ test: ((0 | true) -> 'a) -> ((int, 'a,), (bool, 'a,),) //│ = //│ inc is not implemented :GeneralizeCurriedFunctions // * Note the still approximative type due to extrusion test f = let r x = f x in (r 0, r True) //│ test: ((0 | true) -> 'a) -> ('a, 'a,) //│ = [Function: test2] :e // * Due to lack of precision (test id).0 + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.56: (test id).0 + 1 //│ ║ ^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.4: True = true //│ ║ ^^^^ //│ ╟── but it flows into field selection with expected type `int` //│ ║ l.56: (test id).0 + 1 //│ ╙── ^^^^^^^^^^^ //│ res: error | int //│ = 1 :e // * Due to lack of precision not (test id).1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.70: not (test id).1 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `bool` //│ ║ l.51: in (r 0, r True) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `bool` //│ ║ l.70: not (test id).1 //│ ╙── ^^^^^^^^^^^ //│ res: bool | error //│ = false :e // * Legit not (test id).0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.84: not (test id).0 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `bool` //│ ║ l.51: in (r 0, r True) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `bool` //│ ║ l.84: not (test id).0 //│ ╙── ^^^^^^^^^^^ //│ res: bool | error //│ = true // * === With Constrained Types === :DontDistributeForalls :ConstrainedTypes // * Note the precise type now :ns test f = let r x = f x in (r 0, r True) //│ test: forall 'a 'b 'c 'd 'e 'f 'g. 'a -> ('b, 'e,) //│ where //│ 'a <: 'f -> 'g & 'c -> 'd //│ 'd <: 'b //│ 'c :> 0 //│ 'g <: 'e //│ 'f :> true //│ = [Function: test3] // * Q: why does this type *appear* approximated after simplification? test //│ res: (0 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: test3] // * We can tell the type is still precise enough because these work: (test id).0 + 1 //│ res: int //│ = 1 not (test id).1 //│ res: bool //│ = false :e // * Legit not (test id).0 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.138: not (test id).0 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `bool` //│ ║ l.112: in (r 0, r True) //│ ║ ^ //│ ╟── but it flows into field selection with expected type `bool` //│ ║ l.138: not (test id).0 //│ ╙── ^^^^^^^^^^^ //│ res: bool | error //│ = true // * Note: works with dummy recursive definition (we used to have some hacks for rec defns) rec def test f = let r x = f x in (r 0, r True) //│ test: (0 -> 'a & true -> 'b) -> ('a, 'b,) //│ = [Function: test4] ================================================ FILE: shared/src/test/diff/mlscript/TrickySimplif.mls ================================================ :NoJS // * This is fine def f: 'a -> 'b -> ('a, 'b) //│ f: 'a -> 'b -> ('a, 'b,) // * This is fine def f: ('b -> 'a) -> 'b //│ f: (nothing -> anything) -> nothing // * This could be simplified to `anything -> nothing` def foo: ('a | 'b) -> ('a & 'b) //│ foo: ('a | 'b) -> ('a & 'b) // * Case in point (foo 1) : bool //│ res: bool rec def foo x = foo x //│ anything -> nothing //│ <: foo: //│ ('a | 'b) -> ('a & 'b) def f: MutArray['a & 'b] //│ f: MutArray['a] def f: MutArray[('a & 'b, 'a)] //│ f: MutArray[('a & 'b, 'a,)] def f: MutArray[('a & 'b, 'a | 'b)] //│ f: MutArray[('a, 'a,)] def f: MutArray[('a & 'b, ('a | 'b) -> int)] //│ f: MutArray[('b, 'b -> int,)] def f: MutArray[('a & 'b, ('a & 'b) -> int)] //│ f: MutArray[('a, 'a -> int,)] def f: MutArray[('a & 'b) -> ('a & 'b)] //│ f: MutArray['a -> 'a] def f: MutArray[('a & 'b) -> ('a | 'b)] //│ f: MutArray[('a & 'b) -> ('a | 'b)] def f: MutArray[('a | 'b) -> ('a | 'b)] //│ f: MutArray['a -> 'a] ================================================ FILE: shared/src/test/diff/mlscript/Trio.mls ================================================ class A: { fA: int } class B: { fB: string } class C: { fC: bool } //│ Defined class A //│ Defined class B //│ Defined class C def foo x = case x of { | A -> x.fA | B -> x.fB | C -> x.fC } //│ foo: ((A with {fA: 'fA}) | (B with {fB: 'fA}) | (C with {fC: 'fA})) -> 'fA //│ = [Function: foo] b = B { fB = "test" } //│ b: B & {fB: "test"} //│ = B { fB: 'test' } :stats foo b //│ res: "test" //│ = 'test' //│ constrain calls : 22 //│ annoying calls : 10 //│ subtyping calls : 156 def arg: A | B | C //│ arg: A | B | C //│ = :stats foo arg //│ res: bool | int | string //│ = //│ arg is not implemented //│ constrain calls : 37 //│ annoying calls : 30 //│ subtyping calls : 342 :stats foo (arg with { fC = true }) //│ res: int | string | true //│ = //│ arg is not implemented //│ constrain calls : 30 //│ annoying calls : 28 //│ subtyping calls : 274 def foo x = case x of { | A -> add x.fA x.payload | B -> x.fB | C -> { l = x.fC; r = x.payload } } //│ foo: (A & {payload: int} | (B with {fB: 'fB}) | (C with {fC: 'fC, payload: 'payload})) -> (int | {l: 'fC, r: 'payload} | 'fB) //│ = [Function: foo1] :e :stats foo arg //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.63: foo arg //│ ║ ^^^^^^^ //│ ╟── type `A` does not have field 'payload' //│ ║ l.31: def arg: A | B | C //│ ║ ^ //│ ╟── but it flows into reference with expected type `{payload: ?payload}` //│ ║ l.63: foo arg //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.54: | A -> add x.fA x.payload //│ ║ ^^^^^^^^^ //│ ╟── from reference: //│ ║ l.53: def foo x = case x of { //│ ╙── ^ //│ res: error | int | string | {l: bool, r: nothing} //│ = //│ arg is not implemented //│ constrain calls : 48 //│ annoying calls : 40 //│ subtyping calls : 427 :stats foo (arg with { payload = 1 }) //│ res: int | string | {l: bool, r: 1} //│ = //│ arg is not implemented //│ constrain calls : 42 //│ annoying calls : 38 //│ subtyping calls : 330 ================================================ FILE: shared/src/test/diff/mlscript/TupleArray.mls ================================================ x = { a = 1 } //│ x: {a: 1} //│ = { a: 1 } x with { b = 2 } //│ res: {a: 1, b: 2} //│ = { a: 1, b: 2 } def f x = x with { b = 2 } //│ f: 'a -> ('a\b & {b: 2}) //│ = [Function: f] def r: {b: string; c: int}\b //│ r: in {b: string, c: int}\b out {c: int} //│ = :e r = {c = 1} //│ {c: 1} //│ <: r: //│ {b: string, c: int}\b //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.19: r = {c = 1} //│ ║ ^^^^^^^^^^^ //│ ╟── record literal of type `{c: 1}` does not have field 'b' //│ ║ l.19: r = {c = 1} //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.14: def r: {b: string; c: int}\b //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ = { c: 1 } // * Note that \b in negative position has special semantics // * (it's moved onto the LHS in a constraint) :e r = {b = "a", c = 1} //│ {b: "a", c: 1} //│ <: r: //│ {b: string, c: int}\b //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.37: r = {b = "a", c = 1} //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{c: 1}` does not match type `{b: string, c: int}` //│ ║ l.37: r = {b = "a", c = 1} //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.14: def r: {b: string; c: int}\b //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ = { b: 'a', c: 1 } (1,2,3) with { 3 = "oops" } //│ res: (1, 2, 3,) & {3: "oops"} //│ = [ 1, 2, 3, 'oops' ] (1,2,3) with { 0 = "oops" } //│ res: {0: "oops", 1: 2, 2: 3} //│ = [ 'oops', 2, 3 ] :e (res: (int,int,int,)).0 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.61: (res: (int,int,int,)).0 //│ ║ ^^^ //│ ╟── `with` extension of type `{0: "oops", 1: 2, 2: 3}` is not a 3-element tuple //│ ║ l.56: (1,2,3) with { 0 = "oops" } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(int, int, int,)` //│ ║ l.61: (res: (int,int,int,)).0 //│ ║ ^^^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.61: (res: (int,int,int,)).0 //│ ╙── ^^^^^^^^^^^^^^ //│ res: int //│ = 'oops' def r: int \ 0 //│ r: int\0 //│ = def r: (1,2,3) \ 0 //│ r: in (1, 2, 3,)\0 out {1: 2, 2: 3} //│ = // (1,2,3).toRecord \ 1 def r: (1,2,3) \ 12345 //│ r: in (1, 2, 3,)\12345 out (1, 2, 3,) //│ = def arr: Array[int] arr = (1,2) //│ arr: Array[int] //│ = //│ (1, 2,) //│ <: arr: //│ Array[int] //│ = [ 1, 2 ] fr = f r fr : Array[int] & {b: int} //│ fr: (1, 2, 3,) & {b: 2} //│ = //│ r is not implemented //│ res: Array[int] & {b: int} //│ = //│ fr and r are not implemented :e arr.0 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.110: arr.0 //│ ║ ^^^^^ //│ ╟── type `Array[int]` does not have field '0' //│ ║ l.91: def arr: Array[int] //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{0: ?a}` //│ ║ l.110: arr.0 //│ ╙── ^^^ //│ res: error //│ = 1 rr = arr with { x = 1 } //│ rr: Array[int] & {x: 1} //│ = [ 1, 2, x: 1 ] rr.x //│ res: 1 //│ = 1 t = (1, 2, 3) with {x = 1} // t = (1, 2, 3) //│ t: (1, 2, 3,) & {x: 1} //│ = [ 1, 2, 3, x: 1 ] t.0 t.1 t.x //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 //│ res: 1 //│ = 1 // :d def test: {x: 1} & (1, 2, 3) //│ test: (1, 2, 3,) & {x: 1} //│ = // def f x = x[1] // Array['a] -> 'a // def f x = x.1 // {1: 'a} -> 'a // f(... : Array[int]) // Array[int] <: {1: 'a} // { 1 = 3456 } :e f(1,2,3) : 1 | 2 | 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.171: f(1,2,3) : 1 | 2 | 3 //│ ║ ^^^^^^^^ //│ ╟── argument list of type `(1, 2, 3,)` does not match type `(?a,)` //│ ║ l.171: f(1,2,3) : 1 | 2 | 3 //│ ╙── ^^^^^^^ //│ res: 1 | 2 | 3 //│ = [Number: 1] { b: 2 } :e (arr[0])[1][2] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.182: (arr[0])[1][2] //│ ║ ^^^^^^^^^^^ //│ ╟── type `int` does not match type `Array[?a]` //│ ║ l.91: def arr: Array[int] //│ ║ ^^^ //│ ╟── but it flows into array access with expected type `Array[?b]` //│ ║ l.182: (arr[0])[1][2] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.182: (arr[0])[1][2] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── possibly-undefined array access of type `undefined` does not match type `Array[?a]` //│ ║ l.182: (arr[0])[1][2] //│ ╙── ^^^^^^^^^^^ //│ res: error | undefined //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading '2') class A: { x: int } //│ Defined class A def myval: A & { x: anything } //│ myval: A //│ = // def myval: a & { x: string } // //│ myval: A with {x: string} def tuuu: (1 | 2, true) & {0: 2 | 3} //│ tuuu: (2, true,) //│ = // tuuu: ((1 | 2) & (2 | 3), true,) // tuuu: (2, true,) // (S, T, U) // Array[S | T | U] & { 1: S; 2: T; 3: U } def f(x: int, y: string) = x //│ f: (int, string,) -> int //│ = [Function: f1] // f(x = 1, y = "asf") def f x = x def f(x,) = x //│ f: 'a -> 'a //│ = [Function: f2] //│ f: 'a -> 'a //│ = [Function: f3] // (1, 2) // f((1, 2)) // [1, 2] // f [1, 2] // f ([1, 2], 2, 3) f(1) f 1 //│ res: 1 //│ = 1 //│ res: 1 //│ = 1 f((1,2)) //│ res: (1, 2,) //│ = [ 1, 2 ] trait T: { x: int } //│ Defined trait T def t: T //│ t: T //│ = t.x //│ res: int //│ = //│ t is not implemented rcd = { x = 1 } //│ rcd: {x: 1} //│ = { x: 1 } T rcd //│ res: {x: 1} & #T //│ = { x: 1 } res: { x: int } //│ res: {x: int} //│ = { x: 1 } (1,2, (true, false, ("hello", "world", "bye"))) //│ res: (1, 2, (true, false, ("hello", "world", "bye",),),) //│ = [ 1, 2, [ true, false, [ 'hello', 'world', 'bye' ] ] ] k1 = (6, "hi", false) with {4=5; 5=true} k1.0 k1.2 //│ k1: (6, "hi", false,) & {4: 5, 5: true} //│ = [ 6, 'hi', false, <1 empty item>, 5, true ] //│ res: 6 //│ = 6 //│ res: false //│ = false (1,2,3) with {1 = "hello"; _a = true; 3 = 4} //│ res: {0: 1, 1: "hello", 2: 3, 3: 4, _a: true} //│ = [ 1, 'hello', 3, 4, _a: true ] (1,2,3) with {1 = true; 0 = 233} //│ res: {0: 233, 1: true, 2: 3} //│ = [ 233, true, 3 ] (1, 2, true) with {0 = "hello"} //│ res: {0: "hello", 1: 2, 2: true} //│ = [ 'hello', 2, true ] ta1 = (5, 6, true, false, "hahaha") ta2 = ta1 with {x = 123; 7 = "bye"; 0 = 0} ta1.0 ta2.1 ta2.2 //│ ta1: (5, 6, true, false, "hahaha",) //│ = [ 5, 6, true, false, 'hahaha' ] //│ ta2: {0: 0, 1: 6, 2: true, 3: false, 4: "hahaha", 7: "bye", x: 123} //│ = [ 0, 6, true, false, 'hahaha', <2 empty items>, 'bye', x: 123 ] //│ res: 5 //│ = 5 //│ res: 6 //│ = 6 //│ res: true //│ = true def rep5: 'a -> Array['a] def rep5 x = (x,x,x,x,x) //│ rep5: 'a -> Array['a] //│ = //│ 'a -> ('a, 'a, 'a, 'a, 'a,) //│ <: rep5: //│ 'a -> Array['a] //│ = [Function: rep5] rep5 1 with {0 = 10} a2 = rep5 2 with {1 = true; x = "haha"} a2.1 a2.x //│ res: Array[1] & {0: 10} //│ = [ 10, 1, 1, 1, 1 ] //│ a2: Array[2] & {1: true, x: "haha"} //│ = [ 2, true, 2, 2, 2, x: 'haha' ] //│ res: true //│ = true //│ res: "haha" //│ = 'haha' :e a2.0 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.346: a2.0 //│ ║ ^^^^ //│ ╟── type `Array['a]` does not match type `{0: ?a} | ~{1: true, x: "haha"}` //│ ║ l.323: def rep5: 'a -> Array['a] //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{0: ?a} | ~{1: true, x: "haha"}` //│ ║ l.346: a2.0 //│ ╙── ^^ //│ res: error //│ = 2 (1,2,3,true) with {_a = 1; _b1 = false} //│ res: (1, 2, 3, true,) & {_a: 1, _b1: false} //│ = [ 1, 2, 3, true, _a: 1, _b1: false ] ht1 = (1,2,false) with {0 = 'a'; 1 = 'hello'; 2 = false} ht1.0 //│ ht1: {0: "a", 1: "hello", 2: false} //│ = [ 'a', 'hello', false ] //│ res: "a" //│ = 'a' def hg1 t = (t.0, t.1) hg1 ht1 hg1 ((5,5,5)) (hg1 ht1).1 //│ hg1: {0: 'a, 1: 'b} -> ('a, 'b,) //│ = [Function: hg1] //│ res: ("a", "hello",) //│ = [ 'a', 'hello' ] //│ res: (5, 5,) //│ = [ 5, 5 ] //│ res: "hello" //│ = 'hello' def ta1: Array[int] | (int, bool) def test: (string, 1) & { 0: "hello" } def test2: (string, 1) & { 0: "hello"; 2: int } //│ ta1: Array[bool | int] //│ = //│ test: ("hello", 1,) //│ = //│ test2: ("hello", 1,) & {2: int} //│ = test: { 0: 'a } //│ res: {0: "hello"} //│ = //│ test is not implemented // TODO: One could expect ("hello", 1,) here, // but we currently don't recover the intersected positional field from the record // when constraining refined tuples. test: ('a, 1) //│ res: (string, 1,) //│ = //│ test is not implemented // TODO in principe, we could narrow the refinement to ` & { 1: 1 }` here... def test3: Array[1] & { 0: int } //│ test3: Array[1] & {0: int} //│ = def fta1: Array[int | bool] -> int def tb1: (int, true) fta1 tb1 r1 = if true then (1,2,3) else (1, 2) fta1 r1 //│ fta1: Array[bool | int] -> int //│ = //│ tb1: (int, true,) //│ = //│ res: int //│ = //│ fta1 is not implemented //│ r1: Array[1 | 2 | 3] & {0: 1, 1: 2} //│ = [ 1, 2, 3 ] //│ res: int //│ = //│ fta1 is not implemented :NoJS def p1: T | Array[bool] | (int, string) | (true, 3) def p2: T | (string, bool) | Array[int] | (2, 4) def pf t = (t[1], t.0) pf((1,2,3)) //│ p1: Array[bool | int | string] | T //│ p2: Array[bool | int | string] | T //│ pf: (Array['a & ~undefined] & {0: 'b}) -> (undefined | 'a, 'b,) //│ res: (1 | 2 | 3 | undefined, 1,) ================================================ FILE: shared/src/test/diff/mlscript/TupleArray2.mls ================================================ :e (1,2): (2,1) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.3: (1,2): (2,1) //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `2` //│ ║ l.3: (1,2): (2,1) //│ ║ ^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.3: (1,2): (2,1) //│ ╙── ^ //│ res: (2, 1,) //│ = [ 1, 2 ] :e (1,2): (2,1,0) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.17: (1,2): (2,1,0) //│ ║ ^^^^^ //│ ╟── tuple literal of type `(1, 2,)` does not match type `(2, 1, 0,)` //│ ╟── Note: constraint arises from tuple type: //│ ║ l.17: (1,2): (2,1,0) //│ ╙── ^^^^^^^ //│ res: (2, 1, 0,) //│ = [ 1, 2 ] :e (1,2) with { x = 1 } : (2,1,0) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.29: (1,2) with { x = 1 } : (2,1,0) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── `with` extension of type `(1, 2,) & {x: 1}` is not a 3-element tuple //│ ╟── Note: constraint arises from tuple type: //│ ║ l.29: (1,2) with { x = 1 } : (2,1,0) //│ ╙── ^^^^^^^ //│ res: (2, 1, 0,) //│ = [ 1, 2, x: 1 ] trait T //│ Defined trait T :e (1,2) with { x = 1 } : (2,1,0) | t //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.44: (1,2) with { x = 1 } : (2,1,0) | t //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── `with` extension of type `(1, 2,) & {x: 1}` does not match type `(2, 1, 0,) | #T` //│ ╟── Note: constraint arises from union type: //│ ║ l.44: (1,2) with { x = 1 } : (2,1,0) | t //│ ╙── ^^^^^^^^^^^ //│ res: (2, 1, 0,) | #T //│ = [ 1, 2, x: 1 ] :e (1,2) with { x = 1 } : (2,1) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.56: (1,2) with { x = 1 } : (2,1) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `2` //│ ║ l.56: (1,2) with { x = 1 } : (2,1) //│ ║ ^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.56: (1,2) with { x = 1 } : (2,1) //│ ╙── ^ //│ res: (2, 1,) //│ = [ 1, 2, x: 1 ] (1,2) with { x = 1 } : (1,2) //│ res: (1, 2,) //│ = [ 1, 2, x: 1 ] :re error: (1,2) & t | Array[3] //│ res: Array[3] | (1, 2,) & #T //│ Runtime error: //│ Error: an error was thrown def arr: Array[1] //│ arr: Array[1] //│ = arr: Array[2] | (1, 1) //│ res: Array[1 | 2] //│ = //│ arr is not implemented arr: Array[2] | (1, 2) //│ res: Array[1 | 2] //│ = //│ arr is not implemented :e arr: Array[2] & t | (1, 1) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.96: arr: Array[2] & t | (1, 1) //│ ║ ^^^ //│ ╟── type `Array[1]` does not match type `Array[2] & #T | (1, 1,)` //│ ║ l.81: def arr: Array[1] //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `Array[2] & #T | (1, 1,)` //│ ║ l.96: arr: Array[2] & t | (1, 1) //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.96: arr: Array[2] & t | (1, 1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: Array[2] & #T | (1, 1,) //│ = //│ arr is not implemented def arrt: Array[1] & t //│ arrt: Array[1] & #T //│ = arrt: Array[2] & t | (1, 1) //│ res: Array[2] & #T | (1, 1,) //│ = //│ arrt is not implemented def f_1: 'a -> (Array['a] & t) //│ f_1: 'a -> (Array['a] & #T) //│ = def f_2: 'a -> (Array['a] & t | (1,)) //│ f_2: 'a -> (Array['a] & #T | (1,)) //│ = :e f_1 = f_2 //│ 'a -> (Array['a] & #T | (1,)) //│ <: f_1: //│ 'a -> (Array['a] & #T) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.132: f_1 = f_2 //│ ║ ^^^^^^^^^ //│ ╟── type `1` does not match type `'a` //│ ║ l.127: def f_2: 'a -> (Array['a] & t | (1,)) //│ ║ ^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.123: def f_1: 'a -> (Array['a] & t) //│ ╙── ^^ //│ = //│ f_2 is not implemented f_2 = f_1 //│ 'a -> (Array['a] & #T) //│ <: f_2: //│ 'a -> (Array['a] & #T | (1,)) //│ = //│ f_1 and f_2 are not implemented def test: (string, 1) & { 0: "hello" } //│ test: ("hello", 1,) //│ = :e test = ("hi", 1) //│ ("hi", 1,) //│ <: test: //│ ("hello", 1,) //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.162: test = ("hi", 1) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hi"` does not match type `"hello"` //│ ║ l.162: test = ("hi", 1) //│ ║ ^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.157: def test: (string, 1) & { 0: "hello" } //│ ╙── ^^^^^^^ //│ = [ 'hi', 1 ] test = ("hello", 1) //│ ("hello", 1,) //│ <: test: //│ ("hello", 1,) //│ = [ 'hello', 1 ] (fun ((a, b)) -> a) test //│ res: string //│ = 'hello' test: { 0: 'a } //│ res: {0: "hello"} //│ = [ 'hello', 1 ] test: ('a, 1) //│ res: (string, 1,) //│ = [ 'hello', 1 ] :e class A: (1,2) //│ ╔══[ERROR] cannot inherit from a tuple type //│ ║ l.196: class A: (1,2) //│ ╙── ^^^^^^^^ :re error: Array[1] & { 0: int } //│ res: Array[1] & {0: int} //│ Runtime error: //│ Error: an error was thrown ================================================ FILE: shared/src/test/diff/mlscript/TypeClasses.mls ================================================ class Monoid[A] method Empty: A method Add: A -> A -> A //│ Defined class Monoid[=A] //│ Declared Monoid.Empty: Monoid['A] -> 'A //│ Declared Monoid.Add: Monoid['A] -> 'A -> 'A -> 'A class IntMonoid: Monoid[int] method Empty = 0 method Add = add def IntMonoid = IntMonoid {} //│ Defined class IntMonoid //│ Defined IntMonoid.Empty: IntMonoid -> 0 //│ Defined IntMonoid.Add: IntMonoid -> int -> int -> int //│ IntMonoid: IntMonoid //│ = [Function: IntMonoid1] class Numb: { val: int } //│ Defined class Numb class NumMonoid: Monoid[Numb] method Empty = Numb { val = 1 } method Add this that = Numb { val = this.val * that.val } //│ Defined class NumMonoid //│ Defined NumMonoid.Empty: NumMonoid -> (Numb & {val: 1}) //│ Defined NumMonoid.Add: NumMonoid -> {val: int} -> {val: int} -> Numb class Complex[A]: { real: A; imaginary: A } method Map f = Complex { real = f this.real; imaginary = f this.imaginary } def Complex real imaginary = Complex { real; imaginary } //│ Defined class Complex[+A] //│ Defined Complex.Map: Complex['A] -> ('A -> 'real) -> (Complex['real] with {imaginary: 'real, real: 'real}) //│ Complex: ('real & 'A) -> ('A & 'imaginary) -> (Complex['A] with {imaginary: 'imaginary, real: 'real}) //│ = [Function: Complex1] class ComplexMonoid[A]: Monoid[Complex[A]] & { base: Monoid[A] } method Empty = Complex this.base.Empty this.base.Empty method Add self that = Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) method Add2 (self: Complex['_]) (that: Complex['_]) = Complex (this.base.(Monoid.Add) self.real that.real) (this.base.(Monoid.Add) self.imaginary that.imaginary) //│ Defined class ComplexMonoid[=A] //│ Defined ComplexMonoid.Empty: ComplexMonoid['A] -> Complex['A] //│ Defined ComplexMonoid.Add: ComplexMonoid['A] -> {imaginary: 'A, real: 'A} -> {imaginary: 'A, real: 'A} -> Complex['A] //│ Defined ComplexMonoid.Add2: ComplexMonoid['A] -> Complex['A] -> Complex['A] -> Complex['A] cmi = ComplexMonoid { base = IntMonoid } //│ cmi: ComplexMonoid[int] with {base: IntMonoid} //│ = ComplexMonoid { base: IntMonoid {} } def ComplexMonoid base = ComplexMonoid { base } //│ ComplexMonoid: (Monoid['A] & 'base) -> (ComplexMonoid['A] with {base: 'base}) //│ = [Function: ComplexMonoid1] cmi = ComplexMonoid IntMonoid //│ cmi: ComplexMonoid[int] with {base: IntMonoid} //│ = ComplexMonoid { base: IntMonoid {} } cmi.Empty //│ res: Complex[int] //│ = Complex { real: 0, imaginary: 0 } cmi.Empty.real //│ res: int //│ = 0 c12 = Complex 1 2 c34 = Complex 3 4 //│ c12: Complex[1 | 2] & {imaginary: 2, real: 1} //│ = Complex { real: 1, imaginary: 2 } //│ c34: Complex[3 | 4] & {imaginary: 4, real: 3} //│ = Complex { real: 3, imaginary: 4 } if true then c12 else c34 //│ res: Complex[1 | 2 | 3 | 4] & {imaginary: 2 | 4, real: 1 | 3} //│ = Complex { real: 1, imaginary: 2 } cmi.Add (Complex 1 2) (Complex 3 4) //│ res: Complex[int] //│ = Complex { real: 4, imaginary: 6 } // === === === ERROR CASES === === === // :ShowRelativeLineNums :AllowTypeErrors cmi = ComplexMonoid { base = IntMonoid } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: cmi = ComplexMonoid { base = IntMonoid } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{base: forall ?a. ?a}` is not an instance of type `Monoid` //│ ║ l.+1: cmi = ComplexMonoid { base = IntMonoid } //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.37: class ComplexMonoid[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^ //│ ╟── from reference: //│ ║ l.52: def ComplexMonoid base = ComplexMonoid { base } //│ ╙── ^^^^ //│ cmi: (ComplexMonoid['A] with {base: {base: IntMonoid}}) | error class ComplexMonoid_bad_0[A]: Monoid[Complex[A]] & { base: Monoid[A] } method Empty = Complex { real = this.base } method Add this that = error //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `Complex` //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `Complex[?]` //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.+1: class ComplexMonoid_bad_0[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.29: class Complex[A]: { real: A; imaginary: A } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.29: class Complex[A]: { real: A; imaginary: A } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `forall ?a ?b. ?a -> ?b` does not have field 'Complex#A' //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{Complex#A <: A}` //│ ║ l.+2: method Empty = Complex { real = this.base } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.+1: class ComplexMonoid_bad_0[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ Defined class ComplexMonoid_bad_0[=A] //│ Defined ComplexMonoid_bad_0.Empty: ComplexMonoid_bad_0['A] -> 'A0 -> (Complex[{real: Monoid['A]} | 'A0] with {imaginary: 'A0, real: {real: Monoid['A]}}) //│ Defined ComplexMonoid_bad_0.Add: ComplexMonoid_bad_0['A] -> anything -> anything -> nothing class ComplexMonoid_bad_1[A]: Monoid[Complex[A]] & { base: Monoid[A] } method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } method Add this that = error //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── reference of type `ComplexMonoid_bad_1[A] & this` does not have field 'imaginary' //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `Complex` //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `Complex[?]` //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.+1: class ComplexMonoid_bad_1[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.29: class Complex[A]: { real: A; imaginary: A } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `forall ?a ?b. ?a -> ?b` is not a record (expected a record with fields: real, imaginary) //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{imaginary: A, real: A}` //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from record type: //│ ║ l.29: class Complex[A]: { real: A; imaginary: A } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in method definition: //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── function of type `forall ?a ?b. ?a -> ?b` does not have field 'Complex#A' //│ ║ l.31: def Complex real imaginary = Complex { real; imaginary } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into application with expected type `{Complex#A <: A}` //│ ║ l.+2: method Empty = Complex { real = this.base.Empty; imaginary = this.imaginary.Empty } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.+1: class ComplexMonoid_bad_1[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^^ //│ ╟── from inherited method declaration: //│ ║ l.3: method Empty: A //│ ╙── ^^^^^^^^ //│ Defined class ComplexMonoid_bad_1[=A] //│ Defined ComplexMonoid_bad_1.Empty: ComplexMonoid_bad_1['A] -> 'A0 -> (Complex[{imaginary: nothing, real: 'A} | 'A0] with {imaginary: 'A0, real: {imaginary: nothing, real: 'A}}) //│ Defined ComplexMonoid_bad_1.Add: ComplexMonoid_bad_1['A] -> anything -> anything -> nothing class ComplexMonoid_bad_2[A]: Monoid[Complex[A]] & { base: Monoid[A] } method Add self that = Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) method Add2 (self: Complex['_]) (that: Complex['_]) = Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `Monoid[A]` is not an instance of type `ComplexMonoid_bad_2` //│ ║ l.+1: class ComplexMonoid_bad_2[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^ //│ ╟── but it flows into field selection with expected type `ComplexMonoid_bad_2[?]` //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `Monoid[A]` is not an instance of type `ComplexMonoid_bad_2` //│ ║ l.+1: class ComplexMonoid_bad_2[A]: Monoid[Complex[A]] & { base: Monoid[A] } //│ ║ ^^^^^^^^^ //│ ╟── but it flows into field selection with expected type `ComplexMonoid_bad_2[?]` //│ ║ l.+5: Complex (this.base.Add self.real that.real) (this.base.Add self.imaginary that.imaginary) //│ ╙── ^^^^^^^^^ //│ Defined class ComplexMonoid_bad_2[=A] //│ Defined ComplexMonoid_bad_2.Add: ComplexMonoid_bad_2['A] -> {imaginary: 'A, real: 'A} -> {imaginary: 'A, real: 'A} -> Complex['A] //│ Defined ComplexMonoid_bad_2.Add2: ComplexMonoid_bad_2['A] -> Complex[?] -> Complex[?] -> Complex[error] ================================================ FILE: shared/src/test/diff/mlscript/TypeDefs.mls ================================================ class Test1: { x: int } //│ Defined class Test1 Test1 //│ res: {x: int & 'x} -> (Test1 with {x: 'x}) //│ = [Function: res] Test1 { x = 123 } //│ res: Test1 & {x: 123} //│ = Test1 { x: 123 } :e Test1 { x = "oops" } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.14: Test1 { x = "oops" } //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `int` //│ ║ l.14: Test1 { x = "oops" } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.2: class Test1: { x: int } //│ ╙── ^^^ //│ res: (Test1 with {x: "oops"}) | error //│ = Test1 { x: 'oops' } def Test1 = fun x -> Test1 { x = x } //│ Test1: (int & 'x) -> (Test1 with {x: 'x}) //│ = [Function: Test11] class Test11: { x: int } def Test11 x = Test11 { x } //│ Defined class Test11 //│ Test11: (int & 'x) -> (Test11 with {x: 'x}) //│ = [Function: Test112] :e type Test1 = { x: int } //│ ╔══[ERROR] Type 'Test1' is already defined. //│ ║ l.39: type Test1 = { x: int } //│ ╙── ^^^^^ type Test2 = { x: int } //│ Defined type alias Test2 :pe class Test = { x: int } //│ /!\ Parse error: Expected end-of-input:1:12, found "= { x: int" at l.48:12: class Test = { x: int } :pe type Test: { x: int } //│ /!\ Parse error: Expected "=":1:10, found ": { x: int" at l.52:10: type Test: { x: int } type Test3 = int -> int //│ Defined type alias Test3 type Test4 = int -> int -> int //│ Defined type alias Test4 type Test5 = (int -> int) -> int //│ Defined type alias Test5 type T = int //│ Defined type alias T :e type T = int type T = int //│ ╔══[ERROR] Type 'T' is already defined. //│ ║ l.69: type T = int //│ ╙── ^ //│ ╔══[ERROR] Type 'T' is already defined. //│ ║ l.70: type T = int //│ ╙── ^ :e :ge type TypeA = int foo 42 def foo = 1 foo 42 //│ Defined type alias TypeA //│ ╔══[ERROR] identifier not found: foo //│ ║ l.81: foo 42 //│ ╙── ^^^ //│ res: error //│ foo: 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.83: foo 42 //│ ║ ^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.82: def foo = 1 //│ ║ ^ //│ ╟── but it flows into reference with expected type `42 -> ?a` //│ ║ l.83: foo 42 //│ ╙── ^^^ //│ res: error //│ Code generation encountered an error: //│ unresolved symbol foo 42: TypeA //│ res: TypeA //│ = 42 :e type TypeOops = TypeOops type TypeB = TypeC def test = fun x -> (x: TypeB) test 42 type TypeC = TypeA //│ ╔══[ERROR] illegal cycle involving type TypeOops //│ ║ l.108: type TypeOops = TypeOops //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ Defined type alias TypeB //│ Defined type alias TypeC //│ test: TypeB -> TypeB //│ = [Function: test] //│ res: TypeB //│ = 42 def test = fun (x: TypeB) -> x //│ test: TypeB -> TypeB //│ = [Function: test1] // Note: type alias definitions can contain type variables, // which are refreshed on every use (every expansion) of the alias type Lol = { x: 'a -> 'a } //│ Defined type alias Lol :re error: Lol -> Lol //│ res: Lol -> Lol //│ Runtime error: //│ Error: an error was thrown { x = id } : Lol //│ res: Lol //│ = { x: [Function: id] } :e type Runaway[A] = Runaway['a] //│ ╔══[ERROR] illegal cycle involving type Runaway //│ ║ l.148: type Runaway[A] = Runaway['a] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ :e type Runaway[A] = 'a -> Runaway['a] type RunawayAlias = Runaway[int] //│ ╔══[ERROR] Type definition is not regular: it occurs within itself as Runaway['a], but is defined as Runaway['A] //│ ║ l.154: type Runaway[A] = 'a -> Runaway['a] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier not found: Runaway //│ ║ l.155: type RunawayAlias = Runaway[int] //│ ╙── ^^^^^^^^^^^^ //│ Defined type alias RunawayAlias type Runaway2 = Lol -> Runaway2 //│ Defined type alias Runaway2 :e id: Runaway2 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.169: id: Runaway2 //│ ║ ^^ //│ ╟── type `{x: 'a -> 'a}` is not a function //│ ║ l.133: type Lol = { x: 'a -> 'a } //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.165: type Runaway2 = Lol -> Runaway2 //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.165: type Runaway2 = Lol -> Runaway2 //│ ╙── ^^^^^^^^ //│ res: Runaway2 //│ = [Function: id] :re error: Runaway2 //│ res: Runaway2 //│ Runtime error: //│ Error: an error was thrown :e :re error: {x: int} -> nothing : Runaway2 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.193: error: {x: int} -> nothing : Runaway2 //│ ║ ^^^^^ //│ ╟── type `'a -> 'a` is not an instance of type `int` //│ ║ l.133: type Lol = { x: 'a -> 'a } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.193: error: {x: int} -> nothing : Runaway2 //│ ╙── ^^^ //│ res: Runaway2 //│ Runtime error: //│ Error: an error was thrown :re error: {x: 'x -> 'x} -> nothing : Runaway2 //│ res: Runaway2 //│ Runtime error: //│ Error: an error was thrown type A1[A] = A -> A -> A1[A] type A2[A] = A -> A2[A] //│ Defined type alias A1[-A] //│ Defined type alias A2[-A] def ty1: A1[anything] def ty2: A2[anything] //│ ty1: A1[anything] //│ = //│ ty2: A2[anything] //│ = ty1 = ty2 //│ A2[anything] //│ <: ty1: //│ A1[anything] //│ = //│ ty2 is not implemented ty2 = ty1 //│ A1[anything] //│ <: ty2: //│ A2[anything] //│ = //│ ty1 and ty2 are not implemented ================================================ FILE: shared/src/test/diff/mlscript/TypeRanges.mls ================================================ def set0: MutArray['a..'b] -> 'a -> () //│ set0: MutArray['b] -> 'b -> () //│ = def set1: MutArray['a..'b] -> 'b -> () //│ set1: MutArray['b] -> 'b -> () //│ = def set2: MutArray[int..number] -> int -> () //│ set2: in MutArray[in int out number] -> int -> () out MutArray[in number out int] -> int -> () //│ = :e def set3: MutArray[number..int] -> 'b -> () //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.16: def set3: MutArray[number..int] -> 'b -> () //│ ║ ^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.16: def set3: MutArray[number..int] -> 'b -> () //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.16: def set3: MutArray[number..int] -> 'b -> () //│ ╙── ^^^ //│ set3: in MutArray[in number out int] -> anything -> () out MutArray[in int out number] -> anything -> () //│ = :ns fun x -> set2 x 0 //│ res: forall 'a 'b. 'a -> 'b //│ where //│ 'b :> () //│ 'a <: MutArray[in int out number] //│ = //│ set2 is not implemented // :ns fun x -> set0 x 0 fun x -> set1 x 0 //│ res: MutArray[in 0 | 'b out 'b] -> () //│ = //│ set0 is not implemented //│ res: MutArray[in 0 | 'b out 'b] -> () //│ = //│ set1 is not implemented def foo: MutArray[int..number] //│ foo: in MutArray[in number out int] out MutArray[in int out number] //│ = :e :ng foo = foo //│ MutArray[in int out number] //│ <: foo: //│ MutArray[in number out int] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.56: foo = foo //│ ║ ^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ :e foo = (mut 0,) //│ (mut 'a,) //│ where //│ 'a :> 0 //│ <: foo: //│ MutArray[in number out int] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.74: foo = (mut 0,) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into mutable tuple field with expected type `int` //│ ║ l.74: foo = (mut 0,) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ //│ = [ 0 ] :e set0 foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.98: set0 foo //│ ║ ^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: error | int -> () //│ = //│ set0 is not implemented :e set2 foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.116: set2 foo //│ ║ ^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.11: def set2: MutArray[int..number] -> int -> () //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.11: def set2: MutArray[int..number] -> int -> () //│ ╙── ^^^^^^^^^^^ //│ res: error | int -> () //│ = //│ set2 is not implemented :e foo : MutArray['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.134: foo : MutArray['a] //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: MutArray['a] //│ where //│ 'a :> number //│ <: int //│ = [ 0 ] foo : Array['a] //│ res: Array[number] //│ = [ 0 ] :e foo[0] <- 1 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.158: foo[0] <- 1 //│ ║ ^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^ //│ ╟── from assigned array element: //│ ║ l.158: foo[0] <- 1 //│ ╙── ^^^^^^ //│ = undefined foo[0] //│ res: number | undefined //│ = 1 res : number | undefined //│ res: number | undefined //│ = 1 foo[0] //│ res: number | undefined //│ = 1 :e res : int | undefined //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.186: res : int | undefined //│ ║ ^^^ //│ ╟── type `number` does not match type `int | undefined` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `int | undefined` //│ ║ l.186: res : int | undefined //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.186: res : int | undefined //│ ╙── ^^^^^^^^^^^^^^^ //│ res: int | undefined //│ = 1 :e foo2 = foo : MutArray['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.204: foo2 = foo : MutArray['a..'b] //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.50: def foo: MutArray[int..number] //│ ╙── ^^^^^^^^^^^ //│ foo2: MutArray[in int & 'b out number | 'b] //│ = [ 1 ] foo2[0] //│ res: number | undefined //│ = 1 :e res : undefined //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.225: res : undefined //│ ║ ^^^ //│ ╟── type `number` does not match type `undefined` //│ ║ l.50: def foo: MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `undefined` //│ ║ l.225: res : undefined //│ ║ ^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.225: res : undefined //│ ╙── ^^^^^^^^^ //│ res: undefined //│ = 1 :e def foo: MutArray[number..int] //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.243: def foo: MutArray[number..int] //│ ║ ^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.243: def foo: MutArray[number..int] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.243: def foo: MutArray[number..int] //│ ╙── ^^^ //│ foo: in MutArray[in int out number] out MutArray[in number out int] //│ = foo = (mut 0,) //│ (mut 'a,) //│ where //│ 'a :> 0 //│ <: foo: //│ MutArray[in int out number] //│ = [ 0 ] foo[0] //│ res: int | undefined //│ = 0 res : int | undefined //│ res: int | undefined //│ = 0 foo[0] <- 1 //│ = undefined foo[0] <- (1/2) //│ = undefined :ng bar = if true then error : MutArray[int] else error : MutArray[number] //│ bar: MutArray[in int out number] fun x -> (x : MutArray[int], x : MutArray[number]) //│ res: MutArray[in number out int] -> (MutArray[int], MutArray[number],) //│ = [Function: res] fun x -> if true then x : MutArray[int] else x : MutArray[number] //│ res: MutArray[in number out int] -> MutArray[in int out number] //│ = [Function: res] // * FIXME def maf: MutArray[int..number] -> MutArray[int..number] //│ maf: in MutArray[in int out number] -> MutArray[in number out int] out MutArray[in number out int] -> MutArray[in int out number] //│ = :ns maf //│ res: MutArray[in int out number] -> MutArray[in int out number] //│ = //│ maf is not implemented // :ds maf //│ res: MutArray[in number out int] -> MutArray[in int out number] //│ = //│ maf is not implemented :e maf ((mut error,)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.312: maf ((mut error,)) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into mutable tuple field with expected type `int` //│ ║ l.312: maf ((mut error,)) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: error | MutArray[in int out number] //│ = //│ maf is not implemented :e fun x -> maf ((mut x,)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.333: fun x -> maf ((mut x,)) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── but it flows into mutable tuple field with expected type `int` //│ ║ l.333: fun x -> maf ((mut x,)) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.295: def maf: MutArray[int..number] -> MutArray[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: int -> (error | MutArray[in int out number]) //│ = //│ maf is not implemented maf error //│ res: MutArray[in int out number] //│ = //│ maf is not implemented :e def maf: MutArray[number..int] -> MutArray[int..number] //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.360: def maf: MutArray[number..int] -> MutArray[int..number] //│ ║ ^^^^^^^^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.360: def maf: MutArray[number..int] -> MutArray[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.360: def maf: MutArray[number..int] -> MutArray[int..number] //│ ╙── ^^^ //│ maf: in MutArray[in number out int] -> MutArray[in number out int] out MutArray[in int out number] -> MutArray[in int out number] //│ = maf ((mut error,)) fun x -> maf ((mut x,)) //│ res: MutArray[in int out number] //│ = //│ maf is not implemented //│ res: number -> MutArray[in int out number] //│ = //│ maf is not implemented type R[A] = { get: A; set: A -> () } //│ Defined type alias R[=A] def foo: R[int..number] foo = { get = 0; set = fun n -> () } //│ foo: R[in int out number] //│ = //│ {get: 0, set: anything -> ()} //│ <: foo: //│ R[in int out number] //│ = { get: 0, set: [Function: set] } foo.get //│ res: number //│ = 0 :e foo: R['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.402: foo: R['a] //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: R['a] //│ where //│ 'a :> number //│ <: int //│ = { get: 0, set: [Function: set] } :e foo: R['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.422: foo: R['a..'b] //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: R[in int & 'b out number | 'b] //│ = { get: 0, set: [Function: set] } // * FIXME res.get //│ res: number //│ = 0 type S[A] = { get: A; set: A -> () } //│ Defined type alias S[=A] :e foo: S['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.447: foo: S['a] //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: S['a] //│ where //│ 'a :> number //│ <: int //│ = { get: 0, set: [Function: set] } :e foo: S['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.467: foo: S['a..'b] //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: S[in int & 'b out number | 'b] //│ = { get: 0, set: [Function: set] } foo: { get: number; set: int -> () } //│ res: {get: number, set: int -> ()} //│ = { get: 0, set: [Function: set] } :e foo: { get: int } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.488: foo: { get: int } //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.488: foo: { get: int } //│ ╙── ^^^ //│ res: {get: int} //│ = { get: 0, set: [Function: set] } :e foo: { set: number -> () } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.502: foo: { set: number -> () } //│ ║ ^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.502: foo: { set: number -> () } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type bounds: //│ ║ l.388: def foo: R[int..number] //│ ╙── ^^^^^^^^^^^ //│ res: {set: number -> ()} //│ = { get: 0, set: [Function: set] } foo2 = foo: { get: 'a; set: 'b -> () } //│ foo2: {get: number, set: int -> ()} //│ = { get: 0, set: [Function: set] } :e foo2: S['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.523: foo2: S['a] //│ ║ ^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type variable: //│ ║ l.518: foo2 = foo: { get: 'a; set: 'b -> () } //│ ╙── ^^ //│ res: S['a] //│ where //│ 'a :> number //│ <: int //│ = { get: 0, set: [Function: set] } :e foo2: S['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.543: foo2: S['a..'b] //│ ║ ^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.388: def foo: R[int..number] //│ ║ ^^^ //│ ╟── from type variable: //│ ║ l.518: foo2 = foo: { get: 'a; set: 'b -> () } //│ ╙── ^^ //│ res: S[in int & 'b out number | 'b] //│ = { get: 0, set: [Function: set] } { a = 0 } : { a: 'a..'b } //│ res: {a: 0} //│ = { a: 0 } def maf: { get: int..number; set: (int..number) -> () } -> { get: int..number; set: (int..number) -> () } //│ maf: in {get: number, set: int -> ()} -> {get: int, set: number -> ()} out {get: int, set: number -> ()} -> {get: number, set: int -> ()} //│ = ================================================ FILE: shared/src/test/diff/mlscript/TypeRefs.mls ================================================ :NoJS class Foo[A] method Inv: A -> A //│ Defined class Foo[=A] //│ Declared Foo.Inv: Foo['A] -> 'A -> 'A trait Bar[A] method Inv: A -> A //│ Defined trait Bar[=A] //│ Declared Bar.Inv: Bar['A] -> 'A -> 'A type Baz[A] = Bar[A -> A] //│ Defined type alias Baz[=A] def foo: Foo[int] & Bar[int] & Baz[string] //│ foo: Foo[int] & Bar[int] & Baz[string] foo: Foo['res] //│ res: Foo[int] foo: Baz['res] //│ res: Baz[string] // * Note that we don't get a `'res :> string -> string & int` upper bound because it's simplified to bottom: foo: Bar['res] //│ res: Bar['res] //│ where //│ 'res <: int | string -> string ================================================ FILE: shared/src/test/diff/mlscript/TypeTags.mls ================================================ class Foo[A]: { x: A; y: A } //│ Defined class Foo[+A] def foo: #Foo //│ foo: Foo[?] //│ = foo12 = Foo { x = 1; y = 2 } //│ foo12: Foo[1 | 2] & {x: 1, y: 2} //│ = Foo { x: 1, y: 2 } foo = foo12 //│ Foo[1 | 2] & {x: 1, y: 2} //│ <: foo: //│ Foo[?] //│ = Foo { x: 1, y: 2 } def test1 f = case f of { Foo -> f } //│ test1: (Foo[?] & 'a) -> 'a //│ = [Function: test1] test1 foo //│ res: Foo[?] //│ = Foo { x: 1, y: 2 } def test2 f = case f of { Foo -> f.x } //│ test2: (Foo[?] & {x: 'x}) -> 'x //│ = [Function: test2] test2 foo12 //│ res: 1 //│ = 1 // * Only has the tag in its type, not the field :e test2 foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.39: test2 foo //│ ║ ^^^^^^^^^ //│ ╟── reference of type `Foo[?]` does not have field 'x' //│ ║ l.39: test2 foo //│ ║ ^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.28: def test2 f = case f of { Foo -> f.x } //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.28: def test2 f = case f of { Foo -> f.x } //│ ╙── ^ //│ res: error //│ = 1 // * Mistakenly passing the constructor :e foo = Foo //│ {x: 'x & 'A, y: 'A & 'y} -> (Foo['A] with {x: 'x, y: 'y}) //│ <: foo: //│ Foo[?] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.57: foo = Foo //│ ║ ^^^^^^^^^ //│ ╟── reference of type `{x: ?x, y: ?y} -> (Foo[?A] & {Foo#A = ?A, x: ?x, y: ?y})` is not an instance of type `Foo` //│ ║ l.57: foo = Foo //│ ║ ^^^ //│ ╟── Note: class constructor Foo is defined at: //│ ║ l.3: class Foo[A]: { x: A; y: A } //│ ╙── ^^^ //│ = [Function: foo1] :e 1 : #Eql //│ ╔══[ERROR] type identifier not found: eql //│ ╙── //│ res: error //│ = 1 ================================================ FILE: shared/src/test/diff/mlscript/Undef.mls ================================================ class Undef //│ Defined class Undef undef = Undef{} //│ undef: Undef //│ = Undef {} def example: int | Undef //│ example: Undef | int //│ = example = if true then undef else 42 //│ 42 | Undef //│ <: example: //│ Undef | int //│ = Undef {} def qmrk_qmrk lhs rhs = case lhs of { Undef -> rhs | _ -> lhs } //│ qmrk_qmrk: (Undef | 'a & ~#Undef) -> 'a -> 'a //│ = [Function: qmrk_qmrk] qmrk_qmrk example 0 //│ res: int //│ = 0 qmrk_qmrk example "123" //│ res: "123" | int //│ = '123' qmrk_qmrk undef "123" //│ res: "123" //│ = '123' qmrk_qmrk 42 "123" //│ res: "123" | 42 //│ = 42 ================================================ FILE: shared/src/test/diff/mlscript/Undefined.mls ================================================ class Undefined2: { x: undefined } //│ Defined class Undefined2 Undefined2 //│ res: {x: undefined & 'x} -> (Undefined2 with {x: 'x}) //│ = [Function: res] Undefined2 { x = undefined } //│ res: Undefined2 //│ = Undefined2 { x: undefined } :e Undefined2 { x = "I am here to make a type mismatch." } //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.13: Undefined2 { x = "I am here to make a type mismatch." } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"I am here to make a type mismatch."` does not match type `undefined` //│ ║ l.13: Undefined2 { x = "I am here to make a type mismatch." } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.1: class Undefined2: { x: undefined } //│ ╙── ^^^^^^^^^ //│ res: (Undefined2 with {x: "I am here to make a type mismatch."}) | error //│ = Undefined2 { x: 'I am here to make a type mismatch.' } :e type Undefined2 = { x: string } //│ ╔══[ERROR] Type 'Undefined2' is already defined. //│ ║ l.27: type Undefined2 = { x: string } //│ ╙── ^^^^^^^^^^ type AnotherUndefined = { x: undefined } //│ Defined type alias AnotherUndefined type MoreOnTypes0 = undefined //│ Defined type alias MoreOnTypes0 type MoreOnTypes1 = undefined -> undefined //│ Defined type alias MoreOnTypes1 type MoreOnTypes2 = undefined -> undefined -> undefined //│ Defined type alias MoreOnTypes2 type MoreOnTypes3 = (undefined -> undefined) -> null //│ Defined type alias MoreOnTypes3 sample = undefined sample: undefined //│ sample: undefined //│ = undefined //│ res: undefined //│ = undefined sample2 = null sample2: null //│ sample2: null //│ = null //│ res: null //│ = null :e sample: bool sample2: bool //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.62: sample: bool //│ ║ ^^^^^^ //│ ╟── undefined literal of type `undefined` is not an instance of type `bool` //│ ║ l.47: sample = undefined //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `bool` //│ ║ l.62: sample: bool //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.62: sample: bool //│ ╙── ^^^^ //│ res: bool //│ = undefined //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.63: sample2: bool //│ ║ ^^^^^^^ //│ ╟── null literal of type `null` is not an instance of type `bool` //│ ║ l.54: sample2 = null //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `bool` //│ ║ l.63: sample2: bool //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.63: sample2: bool //│ ╙── ^^^^ //│ res: bool //│ = null if false then sample else 0 if true then sample2 else 0 //│ res: 0 | undefined //│ = 0 //│ res: 0 | null //│ = null def bar = fun sample -> (sample: MoreOnTypes0) bar undefined //│ bar: MoreOnTypes0 -> MoreOnTypes0 //│ = [Function: bar] //│ res: MoreOnTypes0 //│ = undefined :e bar "undefined" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.109: bar "undefined" //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"undefined"` does not match type `undefined` //│ ║ l.109: bar "undefined" //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.35: type MoreOnTypes0 = undefined //│ ║ ^^^^^^^^^ //│ ╟── from reference: //│ ║ l.101: def bar = fun sample -> (sample: MoreOnTypes0) //│ ╙── ^^^^^^ //│ res: error | MoreOnTypes0 //│ = 'undefined' case (undefined) of { undefined -> "undefined it is" | null -> "null it is"} //│ res: "null it is" | "undefined it is" //│ = 'undefined it is' case (null) of { undefined -> "undefined it is" | null -> "null it is" } //│ res: "null it is" | "undefined it is" //│ = 'null it is' absent = ()[0] //│ absent: undefined //│ = undefined case absent of { undefined -> 1 } //│ res: 1 //│ = 1 case absent of { null -> 0 | undefined -> 1 } //│ res: 0 | 1 //│ = 1 case absent of { "" -> 0 | undefined -> 1 } //│ res: 0 | 1 //│ = 1 uw = undefined with { x = 1 } nw = null with { x = 1 } uw.x nw.x //│ uw: undefined & {x: 1} //│ = { x: 1 } //│ nw: null & {x: 1} //│ = { x: 1 } //│ res: 1 //│ = 1 //│ res: 1 //│ = 1 :e undefined.x null.x //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.165: undefined.x //│ ║ ^^^^^^^^^^^ //│ ╟── undefined literal of type `undefined` does not have field 'x' //│ ║ l.165: undefined.x //│ ╙── ^^^^^^^^^ //│ res: error //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading 'x') //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.166: null.x //│ ║ ^^^^^^ //│ ╟── null literal of type `null` does not have field 'x' //│ ║ l.166: null.x //│ ╙── ^^^^ //│ res: error //│ Runtime error: //│ TypeError: Cannot read properties of null (reading 'x') def bypass: nothing //│ bypass: nothing //│ = :e bypass = undefined //│ undefined //│ <: bypass: //│ nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.192: bypass = undefined //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── undefined literal of type `undefined` does not match type `nothing` //│ ║ l.192: bypass = undefined //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.186: def bypass: nothing //│ ╙── ^^^^^^^ //│ = undefined :re bypass.x bypass + 1 //│ res: nothing //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading 'x') //│ res: int //│ = NaN ================================================ FILE: shared/src/test/diff/mlscript/Under.mls ================================================ _ = 1 //│ _: 1 //│ = 1 :e _ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.7: _ //│ ╙── ^ //│ res: error //│ = 1 def foo _ = 1 //│ foo: anything -> 1 //│ = [Function: foo] :e def foo _ = _ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.19: def foo _ = _ //│ ╙── ^ //│ foo: anything -> error //│ = [Function: foo1] // :js def foo(_, _) = 1 //│ foo: (anything, anything,) -> 1 //│ = [Function: foo2] def foo { _ } = 1 //│ foo: {_: anything} -> 1 //│ = [Function: foo3] :e def foo { _ } = { _ } //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.36: def foo { _ } = { _ } //│ ╙── ^ //│ foo: {_: anything} -> {_: error} //│ = [Function: foo4] { _ = 1 }._ //│ res: 1 //│ = 1 :e def foo { _ ; _ } = 1 //│ ╔══[ERROR] Multiple declarations of field name _ in record literal //│ ║ l.48: def foo { _ ; _ } = 1 //│ ║ ^^^^^^^^^ //│ ╟── Declared at //│ ║ l.48: def foo { _ ; _ } = 1 //│ ║ ^ //│ ╟── Declared at //│ ║ l.48: def foo { _ ; _ } = 1 //│ ╙── ^ //│ foo: {_: anything, _: anything} -> 1 //│ = [Function: foo5] // :js def foo { _ = _ ; __ = _ } = 1 //│ foo: {_: anything, __: anything} -> 1 //│ = [Function: foo6] _ = 1 //│ _: 1 //│ = 1 def _ = 1 //│ _: 1 //│ = [Function: _2] rec def _ = 1 //│ _: 1 //│ = [Function: _3] ================================================ FILE: shared/src/test/diff/mlscript/VarCycles.mls ================================================ :RecursiveTypes def foo: ('a,) as 'a //│ foo: 'a //│ where //│ 'a :> ('a,) //│ = def bar: (('b,) as 'b) -> () //│ bar: 'b -> () //│ where //│ 'b <: ('b,) //│ = bar foo //│ res: () //│ = //│ bar is not implemented :ns id1 = id //│ id1: forall 'a. 'a -> 'a //│ = [Function: id] :ns // def f x = id x def f = id : 'c -> 'c //│ f: forall 'c 'a. 'c -> 'c //│ where //│ 'c <: 'a //│ 'a <: 'c //│ = [Function: f] f 1 + 2 //│ res: int //│ = 3 :NoRecursiveTypes :e bar foo //│ ╔══[ERROR] Inferred recursive type: 'b //│ where //│ 'b :> ('b,) //│ ║ l.11: def bar: (('b,) as 'b) -> () //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.48: bar foo //│ ║ ^^^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: error | () //│ = //│ bar is not implemented f 1 + 2 //│ res: int //│ = 3 ================================================ FILE: shared/src/test/diff/mlscript/Variant-sub-ad-hoc.mls ================================================ class None class Some //│ Defined class None //│ Defined class Some def Some v = Some{} with {v} def None = None{} //│ Some: 'a -> (Some & {v: 'a}) //│ = [Function: Some1] //│ None: None //│ = [Function: None1] def flatMap = fun f -> fun opt -> case opt of { Some -> f opt.v | _ -> opt } //│ flatMap: ('v -> 'a) -> (Some & {v: 'v} | 'a & ~#Some) -> 'a //│ = [Function: flatMap] f = fun x -> Some x res = flatMap f (Some 1) res = flatMap f None //│ f: 'a -> (Some & {v: 'a}) //│ = [Function: f] //│ res: Some & {v: 1} //│ = Some { v: 1 } //│ res: None | Some & {v: nothing} //│ = None {} class Lit class Neg class Var class Plus //│ Defined class Lit //│ Defined class Neg //│ Defined class Var //│ Defined class Plus :stats rec def evalOpt = fun x -> case x of { | Lit -> Some x.v | Neg -> flatMap (fun s -> Some (0 - s)) (evalOpt x.sub) | Var -> None with {err = concat "free var: " x.nme} | Plus -> flatMap (fun l -> flatMap (fun r -> Some (l + r) ) (evalOpt x.rhs)) (evalOpt x.lhs) } //│ evalOpt: 'a -> (None & {err: string} | Some & {v: int}) //│ where //│ 'a <: Lit & {v: int} | Neg & {sub: 'a} | Plus & {lhs: 'a, rhs: 'a} | Var & {nme: string} //│ = [Function: evalOpt] //│ constrain calls : 533 //│ annoying calls : 156 //│ subtyping calls : 3001 :stats evalOpt (Plus{} with {lhs = Lit{} with {v=2}; rhs = Lit{} with {v=2}}) //│ res: None & {err: string} | Some & {v: int} //│ = Some { v: 4 } //│ constrain calls : 88 //│ annoying calls : 34 //│ subtyping calls : 653 ================================================ FILE: shared/src/test/diff/mlscript/Variant-sub.mls ================================================ class None: {} class Some[A]: { v: A } //│ Defined class None //│ Defined class Some[+A] def Some v = Some{v} def None = None{} //│ Some: 'v -> Some['v] //│ = [Function: Some1] //│ None: None //│ = [Function: None1] def flatMap = fun f -> fun opt -> case opt of { Some -> f opt.v | None -> opt } //│ flatMap: ('v -> 'a) -> (None & 'a | Some[?] & {v: 'v}) -> 'a //│ = [Function: flatMap] f x = Some x flatMap f (Some 1) flatMap f None //│ f: 'v -> Some['v] //│ = [Function: f] //│ res: Some[1] //│ = Some { v: 1 } //│ res: None | Some[nothing] //│ = None {} :e flatMap f "oops" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.29: flatMap f "oops" //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` does not match type `None & ?a | Some[?] & ?b` //│ ║ l.29: flatMap f "oops" //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.14: case opt of { Some -> f opt.v | None -> opt } //│ ╙── ^^^ //│ res: Some[nothing] | error //│ Runtime error: //│ Error: non-exhaustive case expression class NoneBecause: None & { reason: string } flatMap f (NoneBecause { reason = "uh uh" }) //│ Defined class NoneBecause //│ res: NoneBecause & {reason: "uh uh"} | Some[nothing] //│ = NoneBecause { reason: 'uh uh' } type Expr = Lit | Neg | Var | Plus class Lit: {v: int} class Neg: {sub: Expr} class Var: {nme: string} class Plus: {lhs: Expr; rhs: Expr} //│ Defined type alias Expr //│ Defined class Lit //│ Defined class Neg //│ Defined class Var //│ Defined class Plus :stats rec def evalOpt x = case x of { | Lit -> Some x.v | Neg -> // flatMap (fun s -> Some (neg x.sub)) (evalOpt x.sub) flatMap (fun s -> Some (negate s)) (evalOpt x.sub) | Var -> // None with {err = concat "free var: " x.nme} NoneBecause {reason = concat "free var: " x.nme} // None | Plus -> flatMap (fun l -> flatMap (fun r -> // Some (add x.lhs x.rhs) Some (l + r) ) (evalOpt x.rhs)) (evalOpt x.lhs) } //│ evalOpt: 'a -> (NoneBecause | Some[int]) //│ where //│ 'a <: Lit & {v: int} | (Neg with {sub: 'a}) | (Plus with {lhs: 'a, rhs: 'a}) | Var //│ = [Function: evalOpt] //│ constrain calls : 537 //│ annoying calls : 105 //│ subtyping calls : 3721 :stats evalOpt (Plus{lhs = Lit{v=2}; rhs = Lit{v=3}}) //│ res: NoneBecause | Some[int] //│ = Some { v: 5 } //│ constrain calls : 127 //│ annoying calls : 37 //│ subtyping calls : 959 ================================================ FILE: shared/src/test/diff/mlscript/Weird.mls ================================================ // * Weird features to eventually remove :AllowRuntimeErrors :ge anything //│ res: anything //│ Code generation encountered an error: //│ type alias anything is not a valid expression :ge nothing //│ res: nothing //│ Code generation encountered an error: //│ type alias nothing is not a valid expression :ge Nothing //│ res: nothing //│ Code generation encountered an error: //│ unresolved symbol Nothing :ge int //│ res: int //│ Code generation encountered an error: //│ type alias int is not a valid expression :ge int + 1 //│ res: int //│ Code generation encountered an error: //│ type alias int is not a valid expression class C //│ Defined class C :p def n: C{} //│ Parsed: rec def n: C; {}; //│ Desugared: rec def n: C //│ AST: Def(true,Var(n),Right(PolyType(List(),TypeName(C))),true) //│ Desugared: {} //│ AST: Rcd(List()) //│ n: C //│ = //│ res: anything //│ = {} ================================================ FILE: shared/src/test/diff/mlscript/Wildcards.mls ================================================ :e 42: ? //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.4: 42: ? //│ ║ ^^ //│ ╟── integer literal of type `42` does not match type `nothing` //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.4: 42: ? //│ ╙── ^ //│ res: anything //│ = 42 :e // FIXME new rigidification semantics def n: ? n = 42 //│ n: ? //│ = //│ 42 //│ <: n: //│ nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.17: n = 42 //│ ║ ^^^^^^ //│ ╟── integer literal of type `42` does not match type `nothing` //│ ║ l.17: n = 42 //│ ║ ^^ //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.16: def n: ? //│ ╙── ^ //│ = 42 def f x = x : ? //│ f: nothing -> anything //│ = [Function: f] class E[A] method In: A -> A method In = id //│ Defined class E[=A] //│ Declared E.In: E['A] -> 'A -> 'A //│ Defined E.In: E['A] -> 'a -> 'a :e E{} : E[?] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.47: E{} : E[?] //│ ║ ^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.47: E{} : E[?] //│ ╙── ^ //│ res: E[?] //│ = E {} :e // FIXME new rigidification semantics def e: E[?] def e = E{} //│ e: E[?] //│ = //│ E['A] //│ <: e: //│ E[?] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.59: def e = E{} //│ ║ ^^^^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.58: def e: E[?] //│ ╙── ^ //│ = [Function: e] type Als1[A] = int -> A //│ Defined type alias Als1[+A] :e add 1 : Als1[?] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.78: add 1 : Als1[?] //│ ║ ^^^^^ //│ ╟── type `int` does not match type `nothing` //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.78: add 1 : Als1[?] //│ ╙── ^ //│ res: Als1[?] //│ = [Function (anonymous)] :e // FIXME new rigidification semantics def a1: Als1[?] a1 = add 1 //│ a1: in Als1[nothing] out Als1[?] //│ = //│ int -> int //│ <: a1: //│ Als1[nothing] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.91: a1 = add 1 //│ ║ ^^^^^^^^^^ //│ ╟── type `int` does not match type `nothing` //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.90: def a1: Als1[?] //│ ╙── ^ //│ = [Function (anonymous)] type Als2[A] = A -> int //│ Defined type alias Als2[-A] :e add 1 : Als2[?] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.111: add 1 : Als2[?] //│ ║ ^^^^^ //│ ╟── type `anything` is not an instance of type `int` //│ ║ l.111: add 1 : Als2[?] //│ ╙── ^ //│ res: Als2[?] //│ = [Function (anonymous)] :e // FIXME new rigidification semantics def a2: Als2[?] a2 = add 1 //│ a2: in Als2[anything] out Als2[?] //│ = //│ int -> int //│ <: a2: //│ Als2[anything] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.123: a2 = add 1 //│ ║ ^^^^^^^^^^ //│ ╟── type `anything` is not an instance of type `int` //│ ║ l.122: def a2: Als2[?] //│ ╙── ^ //│ = [Function (anonymous)] def q: ? //│ q: ? //│ = :e // FIXME new rigidification semantics q = 1 //│ 1 //│ <: q: //│ nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.143: q = 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `nothing` //│ ║ l.143: q = 1 //│ ║ ^ //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.138: def q: ? //│ ╙── ^ //│ = 1 :e q + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.159: q + 1 //│ ║ ^^^ //│ ╟── type `anything` is not an instance of type `int` //│ ║ l.138: def q: ? //│ ║ ^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.159: q + 1 //│ ╙── ^ //│ res: error | int //│ = 2 :e q q //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.173: q q //│ ║ ^^^ //│ ╟── type `anything` is not a function //│ ║ l.138: def q: ? //│ ║ ^ //│ ╟── but it flows into reference with expected type `anything -> ?a` //│ ║ l.173: q q //│ ╙── ^ //│ res: error //│ Runtime error: //│ TypeError: q is not a function :NoJS class Expr[A] method Inv: A -> A //│ Defined class Expr[=A] //│ Declared Expr.Inv: Expr['A] -> 'A -> 'A def e: Expr[?] def f: Expr[?] -> Expr[?] //│ e: Expr[?] //│ f: Expr[?] -> Expr[?] // * `Expr[?] <: Expr[?]` is false // * because each `?` stands for an unknown type ranging from Bot to Top :e f e //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.203: f e //│ ║ ^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.195: def e: Expr[?] //│ ║ ^ //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.196: def f: Expr[?] -> Expr[?] //│ ╙── ^ //│ res: Expr[?] | error class Foo[A] method Get = error : A method Foo1: Foo[A] -> Foo[A] method Foo1 = id method Id = id : A -> A //│ Defined class Foo[=A] //│ Declared Foo.Foo1: Foo['A] -> Foo['A] -> Foo['A] //│ Defined Foo.Get: Foo['A] -> 'A //│ Defined Foo.Foo1: Foo['A] -> 'a -> 'a //│ Defined Foo.Id: Foo['A] -> 'A -> 'A def foo: Foo[?] //│ foo: Foo[?] :e // * Works with existential wildcards foo = Foo{} //│ Foo['A] //│ <: foo: //│ Foo[?] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.233: foo = Foo{} //│ ║ ^^^^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ foo //│ res: Foo[?] :e foo : Foo['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.248: foo : Foo['a] //│ ║ ^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ //│ res: Foo['a] //│ where //│ 'a :> anything //│ <: nothing :e foo.Foo1 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.261: foo.Foo1 //│ ║ ^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ //│ res: error | Foo['A] -> Foo['A] //│ where //│ 'A :> anything //│ <: nothing foo1 = Foo.Foo1 //│ foo1: Foo['A] -> Foo['A] -> Foo['A] :e foo1 foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.277: foo1 foo //│ ║ ^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ //│ res: error | Foo['A] -> Foo['A] //│ where //│ 'A :> anything //│ <: nothing :e foo2 = foo : Foo['a .. 'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.291: foo2 = foo : Foo['a .. 'b] //│ ║ ^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ //│ foo2: Foo[anything] :e foo2.Id //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.301: foo2.Id //│ ║ ^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.218: class Foo[A] //│ ╙── ^ //│ res: error | nothing -> anything :e foo3 = foo2 : Foo['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.314: foo3 = foo2 : Foo['a] //│ ║ ^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ╙── ^ //│ foo3: Foo['a] //│ where //│ 'a :> anything //│ <: nothing :e foo3.Id //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.327: foo3.Id //│ ║ ^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.229: def foo: Foo[?] //│ ║ ^ //│ ╟── Note: class type parameter A is defined at: //│ ║ l.218: class Foo[A] //│ ╙── ^ //│ res: error | nothing -> anything :e foo2 = foo : Foo[anything .. nothing] //│ ╔══[ERROR] Type mismatch in type bounds: //│ ║ l.341: foo2 = foo : Foo[anything .. nothing] //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.341: foo2 = foo : Foo[anything .. nothing] //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.341: foo2 = foo : Foo[anything .. nothing] //│ ╙── ^^^^^^^ //│ foo2: Foo[in anything out nothing] foo2.Id //│ res: 'A -> 'A foo3 = foo2 : Foo['a] //│ foo3: Foo['a] foo3.Id //│ res: 'A -> 'A fid = Foo.Id //│ fid: Foo['A] -> 'A -> 'A fid foo2 //│ res: 'A -> 'A fid (f: Foo['a..'b]) = Foo.Id f //│ fid: Foo['a] -> 'a -> 'a class Bar[A]: { value: A; id: A -> A } method Bid: A -> A method Bid = id //│ Defined class Bar[=A] //│ Declared Bar.Bid: Bar['A] -> 'A -> 'A //│ Defined Bar.Bid: Bar['A] -> 'a -> 'a def bar: Bar[?] //│ bar: Bar[?] :e rec def bar = Bar{value = bar; id} //│ 'value //│ where //│ 'value :> Bar['A] with {id: forall 'a. 'a -> 'a, value: 'value} //│ 'A :> 'value //│ <: bar: //│ Bar[?] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.384: rec def bar = Bar{value = bar; id} //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Bar[?A] & {id: ?id, value: ?value}` does not match type `nothing` //│ ║ l.384: rec def bar = Bar{value = bar; id} //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing` //│ ║ l.384: rec def bar = Bar{value = bar; id} //│ ║ ^^^ //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.380: def bar: Bar[?] //│ ╙── ^ :e bar.Bid //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.405: bar.Bid //│ ║ ^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.380: def bar: Bar[?] //│ ╙── ^ //│ res: error | nothing -> anything :e bar: Bar['a..'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.415: bar: Bar['a..'b] //│ ║ ^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.380: def bar: Bar[?] //│ ╙── ^ //│ res: Bar[anything] bar.value //│ res: anything bar.id //│ res: nothing -> anything type Baz[A] = { id: A -> A; bid: Baz[A] -> Baz[A] } //│ Defined type alias Baz[=A] def baz: Baz[?] //│ baz: Baz[?] :e baz = { id; bid = id } //│ {bid: forall 'a. 'a -> 'a, id: forall 'a. 'a -> 'a} //│ <: baz: //│ Baz[?] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.439: baz = { id; bid = id } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.435: def baz: Baz[?] //│ ╙── ^ baz.id //│ res: nothing -> anything baz.bid //│ res: Baz[?] -> Baz[?] :e baz.bid baz //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.457: baz.bid baz //│ ║ ^^^^^^^^^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.435: def baz: Baz[?] //│ ╙── ^ //│ res: error | Baz[?] :e baz.bid (baz: Baz['a..'b]) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.467: baz.bid (baz: Baz['a..'b]) //│ ║ ^^^ //│ ╟── type `anything` does not match type `nothing` //│ ║ l.435: def baz: Baz[?] //│ ╙── ^ //│ res: Baz[?] def baz: Baz[Baz[?]] //│ baz: Baz[Baz[?]] baz.id //│ res: Baz[?] -> Baz[?] baz.bid //│ res: Baz[Baz[?]] -> Baz[Baz[?]] ================================================ FILE: shared/src/test/diff/mlscript/With.mls ================================================ rcd = { x = 1 } //│ rcd: {x: 1} //│ = { x: 1 } rcd.x //│ res: 1 //│ = 1 rcd with { y = 2 } //│ res: {x: 1, y: 2} //│ = { x: 1, y: 2 } add res.x res.y //│ res: int //│ = 3 rcd with { x = "oops" } //│ res: {x: "oops"} //│ = { x: 'oops' } res.x //│ res: "oops" //│ = 'oops' id rcd with { x = "oops" } //│ res: {x: "oops"} //│ = { x: 'oops' } res.x //│ res: "oops" //│ = 'oops' rcd = { } //│ rcd: anything //│ = {} id rcd with { x = "oops" } //│ res: {x: "oops"} //│ = { x: 'oops' } res.x //│ res: "oops" //│ = 'oops' def f r = r with { x = "oops" } //│ f: 'a -> ('a\x & {x: "oops"}) //│ = [Function: f] f rcd //│ res: {x: "oops"} //│ = { x: 'oops' } f (rcd with { y = 2 }) //│ res: {x: "oops", y: 2} //│ = { y: 2, x: 'oops' } def f a b = if true then a else b //│ f: 'a -> 'a -> 'a //│ = [Function: f1] def f a b = (if true then a else b) with { x = "oops" } //│ f: 'a -> 'a -> ('a\x & {x: "oops"}) //│ = [Function: f2] def f a b = let tmp = a.x in (if true then a else b) with { x = "oops" } //│ f: ({x: anything} & 'a) -> 'a -> ('a\x & {x: "oops"}) //│ = [Function: f3] ({ name = "Bob" } with { age = 123 }).age //│ res: 123 //│ = 123 ({ name = "Bob" } with { age = 123 }).name //│ res: "Bob" //│ = 'Bob' ({ name = "Bob" } with { name = 123 }).name //│ res: 123 //│ = 123 def getf { f } = f //│ getf: {f: 'a} -> 'a //│ = [Function: getf] def foo x = let xf = x with { f = 42 } in getf xf //│ foo: ({f: 'a} | ~{f: 42})\f -> 'a //│ = [Function: foo] foo 123 //│ res: 42 //│ = 42 def fooManual: (~{f: 42} | {f: 'a})\f -> 'a //│ fooManual: in forall 'a. anything -> 'a out forall 'a. ({f: 'a} | ~{f: 42})\f -> 'a //│ = fooManual x = error //│ anything -> nothing //│ <: fooManual: //│ anything -> nothing //│ = [Function: fooManual] :e fooManual = id //│ 'a -> 'a //│ <: fooManual: //│ anything -> nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.112: fooManual = id //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `anything` does not match type `'a` //│ ║ l.101: def fooManual: (~{f: 42} | {f: 'a})\f -> 'a //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.101: def fooManual: (~{f: 42} | {f: 'a})\f -> 'a //│ ╙── ^^ //│ = [Function: id] fooManual 123 //│ res: 42 //│ = 123 def foo x = let xf = x with { f = 42 } in add (getf xf) (getf x) //│ foo: (({f: int} | ~{f: 42})\f & {f: int}) -> int //│ = [Function: foo1] def foo x = let xf = x with { f = 42 } in { l = getf xf; r = getf x; s = x } //│ foo: (({f: 'a} | ~{f: 42})\f & {f: 'b} & 'c) -> {l: 'a, r: 'b, s: 'c} //│ = [Function: foo2] :e foo 42 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.144: foo 42 //│ ║ ^^^^^^ //│ ╟── integer literal of type `42` does not have field 'f' //│ ║ l.144: foo 42 //│ ║ ^^ //│ ╟── Note: constraint arises from record literal: //│ ║ l.87: def getf { f } = f //│ ║ ^^^^^ //│ ╟── from reference: //│ ║ l.139: in { l = getf xf; r = getf x; s = x } //│ ╙── ^ //│ res: error | {l: 42, r: nothing, s: 42} //│ = { l: 42, r: undefined, s: 42 } r = foo (42 with { f = 1 }) //│ r: {l: 42, r: 1, s: 42 & {f: 1}} //│ = { l: 42, r: 1, s: [Number: 42] { f: 1 } } r.s.f //│ res: 1 //│ = 1 add r.s 1 //│ res: int //│ = 43 {} with { x = 1 } with { y = 2 } //│ res: {x: 1, y: 2} //│ = { x: 1, y: 2 } :e res.z //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.178: res.z //│ ║ ^^^^^ //│ ╟── `with` extension of type `{x: 1, y: 2}` does not have field 'z' //│ ║ l.173: {} with { x = 1 } with { y = 2 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{z: ?z}` //│ ║ l.178: res.z //│ ╙── ^^^ //│ res: error //│ = undefined // === With and classes === // class C[A]: { x: A } //│ Defined class C[+A] c = C{x = 1} //│ c: C[1] //│ = C { x: 1 } c.x //│ res: 1 //│ = 1 d = c with { x = "hi"; y = 2 } //│ d: C[1] with {x: "hi", y: 2} //│ = C { x: 'hi', y: 2 } d.x //│ res: "hi" //│ = 'hi' d: C['a] //│ res: C["hi" | 1] //│ = C { x: 'hi', y: 2 } res.x //│ res: "hi" | 1 //│ = 'hi' def f x = (x with { a = 1 }).b //│ f: ({b: 'b} | ~{a: 1})\a -> 'b //│ = [Function: f4] ================================================ FILE: shared/src/test/diff/mlscript/Yicong.mls ================================================ trait T1 trait T2 //│ Defined trait T1 //│ Defined trait T2 r = if true then T1 1 else T2 2 //│ r: 1 & #T1 | 2 & #T2 //│ = [Number: 1] case r of { T1 -> r | _ -> 0 } //│ res: 0 | #T1 & (1 | 2 & #T2) //│ = [Number: 1] // TODO also factor ClassTag-s like `2`: case r of { T1 -> r | T2 -> r } //│ res: 1 & #T1 | 'a | #T2 & (2 & ~#T1 | 2 & ~#T2 | 2 & ~'a | 2 & #T1) //│ = [Number: 1] r = if true then T1 ((1,2,3)) else T2 ((3,4,5,4)) //│ r: (3, 4, 5, 4,) & #T2 | (1, 2, 3,) & #T1 //│ = [ 1, 2, 3 ] case r of { T1 -> r | _ -> 0 } //│ res: 0 | #T1 & ((1, 2, 3,) | (3, 4, 5, 4,) & #T2) //│ = [ 1, 2, 3 ] case r of { T1 -> r | T2 -> r } //│ res: (1, 2, 3,) & #T1 | 'a | #T2 & ((3, 4, 5, 4,) & ~#T1 | (3, 4, 5, 4,) & ~#T2 | (3, 4, 5, 4,) & ~'a | (3, 4, 5, 4,) & #T1) //│ = [ 1, 2, 3 ] x = 1 //│ x: 1 //│ = 1 t2 = T2 x //│ t2: 1 & #T2 //│ = [Number: 1] t1 = T1 t2 //│ t1: 1 & #T1 & #T2 //│ = [Number: 1] t1: T1 t1: T2 //│ res: T1 //│ = [Number: 1] //│ res: T2 //│ = [Number: 1] class C1[A]: { a: A } class C2[A]: { a: A } //│ Defined class C1[+A] //│ Defined class C2[+A] r = if true then C1 {a=x} else C2 {a=(3,4,5,4)} //│ r: C1[1] | C2[(3, 4, 5, 4,)] //│ = C1 { a: 1 } case r of { C1 -> r.a | _ -> 0 } //│ res: 0 | 1 //│ = 1 def x1: (int, bool) def x2: (string, unit) //│ x1: (int, bool,) //│ = //│ x2: (string, unit,) //│ = if true then x1 else x2 //│ res: (int | string, bool | unit,) //│ = //│ x1 is not implemented def f: ((1,2) | (3,4)) -> anything //│ f: ((1 | 3, 2 | 4,),) -> anything //│ = fun (x, y) -> f ((x,y)) //│ res: (1 | 3, 2 | 4,) -> anything //│ = //│ f is not implemented def f: ((1,2)&t1 | (3,4)&t2) -> anything //│ f: ((1, 2,) & #T1 | (3, 4,) & #T2) -> anything //│ = :e f ((true,false)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.95: f ((true,false)) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` does not match type `1 | 3` //│ ║ l.95: f ((true,false)) //│ ╙── ^^^^ //│ res: error //│ = //│ f is not implemented :e fun (x, y) -> f ((x,y)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.107: fun (x, y) -> f ((x,y)) //│ ║ ^^^^^^^^^ //│ ╟── tuple literal of type `(?a, ?b,)` does not match type `(1, 2,) & #T1 | (3, 4,) & #T2` //│ ║ l.107: fun (x, y) -> f ((x,y)) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.90: def f: ((1,2)&t1 | (3,4)&t2) -> anything //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: (nothing, nothing,) -> anything //│ = //│ f is not implemented fun (x, y) -> f (T1 ((x,y))) //│ res: (1, 2,) -> anything //│ = //│ f is not implemented fun (x, y) -> f (T2 ((x,y))) //│ res: (3, 4,) -> anything //│ = //│ f is not implemented :ns res = if true then (1,2, "hello") else (true, 3) //│ res: forall 'a. 'a //│ where //│ 'a :> (1, 2, "hello",) | (true, 3,) //│ = [ 1, 2, 'hello' ] res = if true then (1, 2, 3, 'hello') else (true, 4, false) hhh = if false then (45,345, 'bye', true) else (false, 3) t = if false then res else hhh g = if true then (6,6,6) else res gwx = g with {x=123} gwx.x with {y = gwx} //│ res: Array["hello" | 1 | 2 | 3 | 4 | false | true] & {0: 1 | true, 1: 2 | 4, 2: 3 | false} //│ = [ 1, 2, 3, 'hello' ] //│ hhh: Array["bye" | 3 | 345 | 45 | false | true] & {0: 45 | false, 1: 3 | 345} //│ = [ false, 3 ] //│ t: Array["bye" | "hello" | 1 | 2 | 3 | 345 | 4 | 45 | false | true] & {0: 1 | 45 | false | true, 1: 2 | 3 | 345 | 4} //│ = [ false, 3 ] //│ g: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {0: 1 | 6 | true, 1: 2 | 4 | 6, 2: 3 | 6 | false} //│ = [ 6, 6, 6 ] //│ gwx: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {0: 1 | 6 | true, 1: 2 | 4 | 6, 2: 3 | 6 | false, x: 123} //│ = [ 6, 6, 6, x: 123 ] //│ res: 123 & { //│ y: Array["hello" | 1 | 2 | 3 | 4 | 6 | false | true] & {0: 1 | 6 | true, 1: 2 | 4 | 6, 2: 3 | 6 | false, x: 123} //│ } //│ = [Number: 123] { y: [ 6, 6, 6, x: 123 ] } def f: (int, bool) -> int def g: (bool, string, int) -> int // def h x = (f(x), g(x)) //│ f: (int, bool,) -> int //│ = //│ g: (bool, string, int,) -> int //│ = p1 = if true then (1, 2, 2) else (true, false) //│ p1: Array[1 | 2 | false | true] & {0: 1 | true, 1: 2 | false} //│ = [ 1, 2, 2 ] def q: Array[int] q = if true then (1,1) else (1,1,1) //│ q: Array[int] //│ = //│ Array[1] & {0: 1, 1: 1} //│ <: q: //│ Array[int] //│ = [ 1, 1 ] // Note: function with non-tuple domain def h f = (f (1,2,false), f (1,true)) //│ h: ((1, 2, false,) -> 'a & (1, true,) -> 'b) -> ('a, 'b,) //│ = [Function: h] :e h (fun x -> x[0]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.186: h (fun x -> x[0]) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `(1, true,)` does not match type `(?a,)` //│ ║ l.181: def h f = (f (1,2,false), f (1,true)) //│ ╙── ^^^^^^^^ //│ res: error | (undefined, undefined,) //│ = [ undefined, undefined ] :e h (fun (x, y) -> x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.197: h (fun (x, y) -> x) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `(1, 2, false,)` does not match type `(?a, ?b,)` //│ ║ l.181: def h f = (f (1,2,false), f (1,true)) //│ ║ ^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.197: h (fun (x, y) -> x) //│ ╙── ^^^^^^ //│ res: error | (nothing, 1,) //│ = [ 1, 1 ] def h f = (f ((1,2,false)), f ((1,true))) //│ h: (((1, 2, false,),) -> 'a & ((1, true,),) -> 'b) -> ('a, 'b,) //│ = [Function: h1] h (fun x -> x[0]) h (fun x -> x.0) //│ res: (1 | 2 | false | undefined, 1 | true | undefined,) //│ = [ 1, 1 ] //│ res: (1, 1,) //│ = [ 1, 1 ] q1 = (1,1,1,1) q2 = (1,1) fx ((a,b,c)) = a + b + c //│ q1: (1, 1, 1, 1,) //│ = [ 1, 1, 1, 1 ] //│ q2: (1, 1,) //│ = [ 1, 1 ] //│ fx: ((int, int, int,),) -> int //│ = [Function: fx] :e fx q1 fx q2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.233: fx q1 //│ ║ ^^^^^ //│ ╟── tuple literal of type `(1, 1, 1, 1,)` does not match type `(?a, ?b, ?c,)` //│ ║ l.222: q1 = (1,1,1,1) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?d, ?e, ?f,)` //│ ║ l.233: fx q1 //│ ║ ^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.224: fx ((a,b,c)) = a + b + c //│ ╙── ^^^^^^^ //│ res: error | int //│ = 3 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.234: fx q2 //│ ║ ^^^^^ //│ ╟── tuple literal of type `(1, 1,)` does not match type `(?a, ?b, ?c,)` //│ ║ l.223: q2 = (1,1) //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `(?d, ?e, ?f,)` //│ ║ l.234: fx q2 //│ ║ ^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.224: fx ((a,b,c)) = a + b + c //│ ╙── ^^^^^^^ //│ res: error | int //│ = NaN // :d q = (1,1) //│ (1, 1,) //│ <: q: //│ Array[int] //│ = [ 1, 1 ] def sum: Array[int] -> int t1 = (1,2,3,4,5,6) sum t1 //│ sum: Array[int] -> int //│ = //│ t1: (1, 2, 3, 4, 5, 6,) //│ = [ 1, 2, 3, 4, 5, 6 ] //│ res: int //│ = //│ sum is not implemented :e t2 = (1,1,2,true) sum t2 //│ t2: (1, 1, 2, true,) //│ = [ 1, 1, 2, true ] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.285: sum t2 //│ ║ ^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.284: t2 = (1,1,2,true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.272: def sum: Array[int] -> int //│ ╙── ^^^ //│ res: error | int //│ = //│ sum is not implemented t2: (1, 1, 2, bool) t2: Array[int | bool] //│ res: (1, 1, 2, bool,) //│ = [ 1, 1, 2, true ] //│ res: Array[bool | int] //│ = [ 1, 1, 2, true ] class Wrapped[A]: {len: int; inner: Array[A]} //│ Defined class Wrapped[+A] def tk: Array['a] -> Wrapped['a] tk ((1,2,3,true,false)) //│ tk: Array['a] -> Wrapped['a] //│ = //│ res: Wrapped[1 | 2 | 3 | false | true] //│ = //│ tk is not implemented class Two[A, B]: {fst: Array[A]; snd: Array[B]} two = Two {fst=(1,2,3); snd=(true,false)} two.fst tk (two.snd) //│ Defined class Two[+A, +B] //│ two: Two[1 | 2 | 3, Bool] & {fst: (1, 2, 3,), snd: (true, false,)} //│ = Two { fst: [ 1, 2, 3 ], snd: [ true, false ] } //│ res: (1, 2, 3,) //│ = [ 1, 2, 3 ] //│ res: Wrapped[Bool] //│ = //│ tk is not implemented :e def a1: Array[int] a1 = (1,2,true,'hello') a1.1 //│ a1: Array[int] //│ = //│ (1, 2, true, "hello",) //│ <: a1: //│ Array[int] //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.334: a1 = (1,2,true,'hello') //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of type `int` //│ ║ l.334: a1 = (1,2,true,'hello') //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.333: def a1: Array[int] //│ ╙── ^^^ //│ = [ 1, 2, true, 'hello' ] //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.335: a1.1 //│ ║ ^^^^ //│ ╟── type `Array[int]` does not have field '1' //│ ║ l.333: def a1: Array[int] //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{1: ?a}` //│ ║ l.335: a1.1 //│ ╙── ^^ //│ res: error //│ = 2 def getx p = p.x def a123: Array[int] a123 = (1,2,3) getx (a123 with {x=('hello', a123)}) //│ getx: {x: 'x} -> 'x //│ = [Function: getx] //│ a123: Array[int] //│ = //│ (1, 2, 3,) //│ <: a123: //│ Array[int] //│ = [ 1, 2, 3 ] //│ res: ("hello", Array[int],) //│ = [ 'hello', [ 1, 2, 3 ] ] def append: Array['a] -> Array['b] -> Array['a | 'b] append ((1,2,false)) (((), 'hi')) //│ append: Array['a] -> Array['a] -> Array['a] //│ = //│ res: Array["hi" | 1 | 2 | false | ()] //│ = //│ append is not implemented def append2: MutArray['aa] -> MutArray['bb] -> MutArray['aa | 'bb] append2 ((mut 1, mut 2, mut false)) ((mut (), mut 'hi')) //│ append2: MutArray['aa] -> MutArray['bb] -> MutArray['aa | 'bb] //│ = //│ res: MutArray['aa | 'bb] //│ where //│ 'bb := 'a //│ 'a :> "hi" | () //│ 'aa :> 'b //│ <: 'c & 'b //│ 'b :> 1 | 2 | false //│ <: 'c //│ 'c <: 'b //│ = //│ append2 is not implemented :e append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.403: append2 ((mut 1, mut 2, mut false)) ((mut (), 'hi')) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `"hi" | ?a` cannot be reassigned //│ res: error | MutArray['aa | 'bb] //│ where //│ 'bb :> "hi" | () //│ 'aa :> 'a //│ <: 'b & 'a //│ 'a :> 1 | 2 | false //│ <: 'b //│ 'b <: 'a //│ = //│ append2 is not implemented def ta: T1 & Array[int] ta = T1 a123 //│ ta: T1 & Array[int] //│ = //│ Array[int] & #T1 //│ <: ta: //│ T1 & Array[int] //│ = [ 1, 2, 3 ] def tb: T2 & Array[int] tb = T2 ((1,2,3)) def tba: (T2 & Array[int | bool]) -> (T2 & int) tba tb //│ tb: T2 & Array[int] //│ = //│ (1, 2, 3,) & #T2 //│ <: tb: //│ T2 & Array[int] //│ = [ 1, 2, 3 ] //│ tba: (T2 & Array[bool | int]) -> (T2 & int) //│ = //│ res: T2 & int //│ = //│ tba is not implemented def k: (true, false, 3) & Array[int | true] def k2: Array[1 | 2 | 3] & (true, 'hi', 2, 3) //│ k: (true, nothing, 3,) //│ = //│ k2: (nothing, nothing, 2, 3,) //│ = def gett1 p q = case p of {T1 -> p | _ -> q} gett1 ta (T1 123) def gt: (T1 & Array[int | bool]) -> Array[int] gt ta def tg: ((T1 & Array[int]) | (T2 & Array[bool])) -> (T1 | T2) //│ gett1: (#T1 & 'a | ~#T1) -> 'a -> 'a //│ = [Function: gett1] //│ res: #T1 & (123 | Array[int]) //│ = [ 1, 2, 3 ] //│ gt: (T1 & Array[bool | int]) -> Array[int] //│ = //│ res: Array[int] //│ = //│ gt is not implemented //│ tg: (T1 & Array[int] | T2 & Array[bool]) -> (T1 | T2) //│ = gett1 ta (T1 123) tg (T1 ((1,2,3,4))) tg (T2 ((true,false,false))) //│ res: #T1 & (123 | Array[int]) //│ = [ 1, 2, 3 ] //│ res: T1 | T2 //│ = //│ tg is not implemented //│ res: T1 | T2 //│ = //│ tg is not implemented def fst ((a, b)) = a def snd ((a, b)) = b def get: Array['a] -> int -> 'a //│ fst: (('a, anything,),) -> 'a //│ = [Function: fst] //│ snd: ((anything, 'a,),) -> 'a //│ = [Function: snd] //│ get: Array['a] -> int -> 'a //│ = fst ((1,2)) snd ((1,2)) get ((1,2,3)) 1 //│ res: 1 //│ = 1 //│ res: 2 //│ = 2 //│ res: 1 | 2 | 3 //│ = //│ get is not implemented def intersect: Array['a] -> Array['b] -> Array['a & 'b] iarr = intersect ((1,2,3,false)) ((1,5,true,'hi',false)) //│ intersect: Array['a] -> Array['b] -> Array['a & 'b] //│ = //│ iarr: Array[1 | false] //│ = //│ intersect is not implemented :e fst iarr snd (T1 ((1,2,3))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.510: fst iarr //│ ║ ^^^^^^^^ //│ ╟── type `Array['a & 'b]` is not a 2-element tuple //│ ║ l.501: def intersect: Array['a] -> Array['b] -> Array['a & 'b] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(?a, ?b,)` //│ ║ l.510: fst iarr //│ ║ ^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.480: def fst ((a, b)) = a //│ ╙── ^^^^^^ //│ res: error //│ = //│ iarr and intersect are not implemented //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.511: snd (T1 ((1,2,3))) //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `(1, 2, 3,)` does not match type `(?a, ?b,) | ~#T1` //│ ║ l.511: snd (T1 ((1,2,3))) //│ ║ ^^^^^^^ //│ ╟── but it flows into application with expected type `(?a, ?b,) | ~#T1` //│ ║ l.511: snd (T1 ((1,2,3))) //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.481: def snd ((a, b)) = b //│ ╙── ^^^^^^ //│ res: error //│ = 2 v1 = T1 (T2 ((1,2,true))) v2 = T2 (v1 with {x=v1}) def inn: (T1 & T2 & Array['a]) -> 'a def inn2: (T2 & Array[Array['a]]) -> Array[T1 & 'a] inn v1 inn (v2 with {y= T1 ((1,2,3))}) def v3: Array[Array[(int, true)]] r1 = inn2 (T2 v3) inn (T1 (T2 v3)) //│ v1: (1, 2, true,) & #T1 & #T2 //│ = [ 1, 2, true ] //│ v2: (1, 2, true,) & {x: (1, 2, true,) & #T1 & #T2} & #T1 & #T2 //│ = [ 1, 2, true, x: [ 1, 2, true ] ] //│ inn: (T1 & T2 & Array['a]) -> 'a //│ = //│ inn2: (T2 & Array[Array['a]]) -> Array[T1 & 'a] //│ = //│ res: 1 | 2 | true //│ = //│ inn is not implemented //│ res: 1 | 2 | true //│ = //│ inn is not implemented //│ v3: Array[Array[(int, true,)]] //│ = //│ r1: Array[T1 & (int, true,)] //│ = //│ inn2 is not implemented //│ res: Array[(int, true,)] //│ = //│ inn is not implemented v3: Array[Array[Array[int | bool]]] inn2 (T2 r1) //│ res: Array[Array[Array[bool | int]]] //│ = //│ v3 is not implemented //│ res: Array[T1 & int | T1 & true] //│ = //│ inn2 is not implemented :e inn (T1 v3) inn2 v1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.584: inn (T1 v3) //│ ║ ^^^^^^^^^^^ //│ ╟── type `Array[Array[(int, true,)]]` does not match type `#T2 | ~#T1` //│ ║ l.548: def v3: Array[Array[(int, true)]] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `#T2 | ~#T1` //│ ║ l.584: inn (T1 v3) //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.544: def inn: (T1 & T2 & Array['a]) -> 'a //│ ║ ^^ //│ ╟── from intersection type: //│ ║ l.544: def inn: (T1 & T2 & Array['a]) -> 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: error | Array[(int, true,)] //│ = //│ inn is not implemented //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.585: inn2 v1 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Array['a]` //│ ║ l.542: v1 = T1 (T2 ((1,2,true))) //│ ╙── ^ //│ res: error | Array[nothing] //│ = //│ inn2 is not implemented def ra: ('a, 'a) as 'a ra: Array[Array['a]] as 'a ra: Array[('a, 'a)] as 'a ra: (Array['a], Array['a]) as 'a //│ ra: 'a //│ where //│ 'a :> ('a, 'a,) //│ = //│ res: 'a //│ where //│ 'a :> Array[Array['a]] //│ = //│ ra is not implemented //│ res: 'a //│ where //│ 'a :> Array[('a, 'a,)] //│ = //│ ra is not implemented //│ res: 'a //│ where //│ 'a :> (Array['a], Array['a],) //│ = //│ ra is not implemented :e ra: ('a, 'a, 'a) as 'a ra: (Array['a], Array['a], Array['a]) as 'a //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.639: ra: ('a, 'a, 'a) as 'a //│ ║ ^^ //│ ╟── type `('a, 'a,)` does not match type `('a0, 'a0, 'a0,)` //│ ║ l.614: def ra: ('a, 'a) as 'a //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `('a1, 'a1, 'a1,)` //│ ║ l.639: ra: ('a, 'a, 'a) as 'a //│ ║ ^^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.639: ra: ('a, 'a, 'a) as 'a //│ ╙── ^^^^^^^^^^^^ //│ res: 'a //│ where //│ 'a :> ('a, 'a, 'a,) //│ = //│ ra is not implemented //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.640: ra: (Array['a], Array['a], Array['a]) as 'a //│ ║ ^^ //│ ╟── type `('a, 'a,)` does not match type `(Array['a0], Array['a0], Array['a0],)` //│ ║ l.614: def ra: ('a, 'a) as 'a //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `(Array['a1], Array['a1], Array['a1],)` //│ ║ l.640: ra: (Array['a], Array['a], Array['a]) as 'a //│ ║ ^^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.640: ra: (Array['a], Array['a], Array['a]) as 'a //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: 'a //│ where //│ 'a :> (Array['a], Array['a], Array['a],) //│ = //│ ra is not implemented def tktup t = (t.1, t.2) tktup ((1,2,3,true)) //│ tktup: {1: 'a, 2: 'b} -> ('a, 'b,) //│ = [Function: tktup] //│ res: (2, 3,) //│ = [ 2, 3 ] :e tktup a123 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.684: tktup a123 //│ ║ ^^^^^^^^^^ //│ ╟── type `Array[int]` does not have field '2' //│ ║ l.364: def a123: Array[int] //│ ║ ^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{2: ?a}` //│ ║ l.684: tktup a123 //│ ║ ^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.676: def tktup t = (t.1, t.2) //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.676: def tktup t = (t.1, t.2) //│ ╙── ^ //│ res: error | (nothing, nothing,) //│ = [ 2, 3 ] def definedOr x els = case x of { | undefined -> els() | _ -> x } def defined x = definedOr x (fun () -> error) def ornull x = definedOr x (fun () -> null) //│ definedOr: (undefined | 'a & ~undefined) -> (() -> 'a) -> 'a //│ = [Function: definedOr] //│ defined: (undefined | 'a & ~undefined) -> 'a //│ = [Function: defined] //│ ornull: (undefined | 'a & ~undefined) -> (null | 'a) //│ = [Function: ornull] :re defined ()[0] //│ res: nothing //│ Runtime error: //│ Error: an error was thrown ta1 = (2,3,4,5) ta1[1] ta2 = ((1,2,4), (true, false)) (ornull (defined ta2[0])[1+4], defined ta[1])[0] ta3 = ((1,2,3), "hello", false) ta3[1-2] (1,2,3)[4] //│ ta1: (2, 3, 4, 5,) //│ = [ 2, 3, 4, 5 ] //│ res: 2 | 3 | 4 | 5 | undefined //│ = 3 //│ ta2: ((1, 2, 4,), (true, false,),) //│ = [ [ 1, 2, 4 ], [ true, false ] ] //│ res: false | int | null | true | undefined //│ = null //│ ta3: ((1, 2, 3,), "hello", false,) //│ = [ [ 1, 2, 3 ], 'hello', false ] //│ res: "hello" | false | undefined | (1, 2, 3,) //│ = undefined //│ res: 1 | 2 | 3 | undefined //│ = undefined :e ((defined ta2[0])[1+4], ta[1])[0] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.747: ((defined ta2[0])[1+4], ta[1])[0] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── possibly-undefined array access of type `undefined` does not match type `~undefined` //│ ║ l.747: ((defined ta2[0])[1+4], ta[1])[0] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ res: error | false | int | true | undefined //│ = undefined :e def ge x = x + 1 ta3[ge 3][ge (1+2)] true[false] 4["hello"] ge[2] //│ ge: int -> int //│ = [Function: ge] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.759: ta3[ge 3][ge (1+2)] //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hello"` does not match type `Array[?a]` //│ ║ l.728: ta3 = ((1,2,3), "hello", false) //│ ║ ^^^^^^^ //│ ╟── but it flows into array access with expected type `Array[?b]` //│ ║ l.759: ta3[ge 3][ge (1+2)] //│ ╙── ^^^^^^^^^ //│ res: 1 | 2 | 3 | error | undefined //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading '4') //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.760: true[false] //│ ║ ^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of type `int` //│ ║ l.760: true[false] //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.760: true[false] //│ ║ ^^^^^^^^^^^ //│ ╟── reference of type `true` does not match type `Array[?a]` //│ ║ l.760: true[false] //│ ╙── ^^^^ //│ res: error | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.761: 4["hello"] //│ ║ ^^^^^^^^^^ //│ ╟── string literal of type `"hello"` is not an instance of type `int` //│ ║ l.761: 4["hello"] //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.761: 4["hello"] //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `4` does not match type `Array[?a]` //│ ║ l.761: 4["hello"] //│ ╙── ^ //│ res: error | undefined //│ = undefined //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.762: ge[2] //│ ║ ^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` does not match type `Array[?c]` //│ ║ l.758: def ge x = x + 1 //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Array[?d]` //│ ║ l.762: ge[2] //│ ╙── ^^ //│ res: error | undefined //│ = undefined def mkarr: 'a -> Array['a] def mkarr x = (x,x,x,x,x) mk1 = defined (mkarr 6)[ 0] (mkarr (mk1/3))[(3)] mkarr (1,true,"hi")[0] //│ mkarr: 'a -> Array['a] //│ = //│ 'a -> ('a, 'a, 'a, 'a, 'a,) //│ <: mkarr: //│ 'a -> Array['a] //│ = [Function: mkarr] //│ mk1: 6 //│ = 6 //│ res: number | undefined //│ = 2 //│ res: Array["hi" | 1 | true | undefined] //│ = [ 1, 1, 1, 1, 1 ] :e mkarr 3 [ 1] mk1[2 ] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.836: mkarr 3 [ 1] //│ ║ ^^^^^^^ //│ ╟── integer literal of type `3` does not match type `Array[?a]` //│ ║ l.836: mkarr 3 [ 1] //│ ╙── ^ //│ res: Array[error | undefined] //│ = [ undefined, undefined, undefined, undefined, undefined ] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.837: mk1[2 ] //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `6` does not match type `Array[?a]` //│ ║ l.819: mk1 = defined (mkarr 6)[ 0] //│ ║ ^ //│ ╟── but it flows into reference with expected type `Array[?b]` //│ ║ l.837: mk1[2 ] //│ ╙── ^^^ //│ res: error | undefined //│ = undefined def s1 a = (defined a[1] + defined a[2], a[(1,2).1]) def s2 a = (defined (defined a[1])[0]).1 + (defined a[2]).0 def s3 a = defined a.0.t[0] + defined (defined a.x[1])[2] (defined ((1, "hello"),(2, true, false),(3,3,4))[0]).0 //│ s1: Array[int & 'a] -> (int, undefined | 'a,) //│ = [Function: s1] //│ s2: Array[Array[{1: int} & ~undefined] & {0: int}] -> int //│ = [Function: s2] //│ s3: {0: {t: Array[int]}, x: Array[Array[int]]} -> int //│ = [Function: s3] //│ res: 1 | 2 | 3 //│ = 1 def ara: Array[(int, bool)] def arb: Array[Array[(int, int)]] (defined ara[1]).0 (defined (defined arb[0])[1]).1 def s4 arr = (defined (defined (defined arr.x[0]).2[6])[7])._h.hello //│ ara: Array[(int, bool,)] //│ = //│ arb: Array[Array[(int, int,)]] //│ = //│ res: int //│ = //│ ara is not implemented //│ res: int //│ = //│ arb is not implemented //│ s4: {x: Array[{2: Array[Array[{_h: {hello: 'hello}} & ~undefined]]} & ~undefined]} -> 'hello //│ = [Function: s4] def at1 xs = (defined xs[0]).0 def dup a b = a * 2 + b dup (defined ara[1]).0 (defined (defined arb[10])[8]).1 + 1 //│ at1: Array[{0: 'a} & ~undefined] -> 'a //│ = [Function: at1] //│ dup: int -> int -> int //│ = [Function: dup] //│ res: int //│ = //│ ara is not implemented :e (1,2,3).0[1] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.901: (1,2,3).0[1] //│ ║ ^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Array[?a]` //│ ║ l.901: (1,2,3).0[1] //│ ║ ^ //│ ╟── but it flows into field selection with expected type `Array[?b]` //│ ║ l.901: (1,2,3).0[1] //│ ╙── ^^^^^^^^^ //│ res: error | undefined //│ = undefined ================================================ FILE: shared/src/test/diff/mlscript/Yuheng.mls ================================================ 1 + 2 //│ res: int //│ = 3 def foo(x) = x + 1 //│ foo: int -> int //│ = [Function: foo] class A: { x: number } //│ Defined class A def f v = v.x //│ f: {x: 'x} -> 'x //│ = [Function: f] // def f: (A & {x: int} | anything) -> int def f v = case v of { A -> (v.x : int) | _ -> 0 } //│ f: (A & {x: int} | ~A) -> int //│ = [Function: f1] a1 = A { x = 0 } //│ a1: A & {x: 0} //│ = A { x: 0 } f a1 //│ res: int //│ = 0 a2 = A { x = 0: number } //│ a2: A //│ = A { x: 0 } :e f a2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.39: f a2 //│ ║ ^^^^ //│ ╟── type `number` is not an instance of type `int` //│ ║ l.34: a2 = A { x = 0: number } //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.20: A -> (v.x : int) //│ ║ ^^^ //│ ╟── from field selection: //│ ║ l.20: A -> (v.x : int) //│ ╙── ^^^ //│ res: error | int //│ = 0 def f2: (A & {x: int} | anything) -> int //│ f2: anything -> int //│ = f2 a2 //│ res: int //│ = //│ f2 is not implemented // const x: [0, 1] = [0, 1] a = (0, 1) //│ a: (0, 1,) //│ = [ 0, 1 ] 1 : 1 //│ res: 1 //│ = 1 // def f(x) = // x : x // f2 x : f2 x :w class Expr[A] //│ Defined class Expr[±A] //│ ╔══[WARNING] Type definition Expr has bivariant type parameters: //│ ║ l.81: class Expr[A] //│ ║ ^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.81: class Expr[A] //│ ╙── ^ class IntLit: Expr[int] & { value: int } //│ Defined class IntLit :w class Pair[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[B] } //│ Defined class Pair[±A, ±B] //│ ╔══[WARNING] Type definition Pair has bivariant type parameters: //│ ║ l.94: class Pair[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[B] } //│ ║ ^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.94: class Pair[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[B] } //│ ║ ^ //│ ╟── B is irrelevant and may be removed //│ ║ l.94: class Pair[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[B] } //│ ╙── ^ rec def eval(e) = case e of { IntLit -> e.value | Pair -> (eval e.lhs, eval e.rhs) } //│ eval: 'a -> 'value //│ where //│ 'a <: (IntLit with {value: 'value}) | (Pair[?, ?] with {lhs: 'a, rhs: 'a}) //│ 'value :> ('value, 'value,) //│ = [Function: eval] rec def eval(e) = case e of { IntLit -> e.value | Pair -> (eval e.lhs,) } //│ eval: 'a -> 'value //│ where //│ 'a <: (IntLit with {value: 'value}) | (Pair[?, ?]\rhs with {lhs: 'a}) //│ 'value :> ('value,) //│ = [Function: eval1] rec def eval(e) = case e of { IntLit -> e.value | Pair -> (eval e.lhs, eval e.rhs) | _ -> error } //│ eval: 'a -> 'value //│ where //│ 'a <: (IntLit with {value: 'value}) | (Pair[?, ?] with {lhs: 'a, rhs: 'a}) | ~IntLit & ~Pair[?, ?] //│ 'value :> ('value, 'value,) //│ = [Function: eval2] eval (IntLit { value = 1 }) //│ res: 'a //│ where //│ 'a :> 1 | ('a, 'a,) //│ = 1 eval (Pair { lhs = IntLit { value = 1 }; rhs = IntLit { value = 2 } }) //│ res: 'a //│ where //│ 'a :> 1 | 2 | ('a, 'a,) //│ = [ 1, 2 ] p = Pair { lhs = IntLit { value = 1 }; rhs = Pair { lhs = IntLit { value = 2 }; rhs = IntLit { value = 3 } } } //│ p: Pair[?, ?] with { //│ lhs: IntLit & {value: 1}, //│ rhs: Pair[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}} //│ } //│ = Pair { //│ lhs: IntLit1 { value: 1 }, //│ rhs: Pair { lhs: IntLit1 { value: 2 }, rhs: IntLit1 { value: 3 } } //│ } ep = eval p //│ ep: 'a //│ where //│ 'a :> 1 | 2 | 3 | ('a, 'a,) //│ = [ 1, [ 2, 3 ] ] // FUTURE: // class Expr[type A] // def eval['a]: Expr['a] -> 'a // rec def eval(e: Expr['a]) = case e of // { IntLit -> e.value // | Pair -> (eval e.lhs, eval[e.rhs] (e.rhs: Expr[e.rhs.A])) // | _ -> error // } // class Vector[A, N: nat] // class Nil: Vector['a, 0] // class Cons[A, m]: Vector[A, S m] // === === === ERROR CASES === === === // :w class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ Defined class Pair2[±A, ±B] //│ ╔══[WARNING] Type definition Pair2 has bivariant type parameters: //│ ║ l.194: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ ║ ^^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.194: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ ║ ^ //│ ╟── B is irrelevant and may be removed //│ ║ l.194: class Pair2[A, B]: Expr[(A, B)] & { lhs: Expr[A]; rhs: Expr[A] } //│ ╙── ^ :ShowRelativeLineNums :AllowTypeErrors ep + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.+1: ep + 1 //│ ║ ^^^^ //│ ╟── tuple literal of type `(?a, ?b,)` is not an instance of type `int` //│ ║ l.128: | Pair -> (eval e.lhs, eval e.rhs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `int` //│ ║ l.+1: ep + 1 //│ ╙── ^^ //│ res: error | int ep[0] //│ ╔══[ERROR] Type mismatch in array access: //│ ║ l.+1: ep[0] //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `Array[?a]` //│ ║ l.152: lhs = IntLit { value = 1 }; //│ ║ ^ //│ ╟── but it flows into reference with expected type `Array[?b]` //│ ║ l.+1: ep[0] //│ ╙── ^^ //│ res: error | undefined | 'a //│ where //│ 'a :> 1 | 2 | 3 | ('a, 'a,) eval (Pair { lhs = IntLit { value = 1 }; rhs = IntLit { value = Pair { lhs = IntLit { value = 2 }; rhs = IntLit { value = 3 } } } }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+3: rhs = IntLit { value = Pair { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+4: lhs = IntLit { value = 2 }; //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+5: rhs = IntLit { value = 3 } } } }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Pair[?, ?] & {Pair#A = ?A, Pair#B = ?B, lhs: ?lhs, rhs: ?rhs}` is not an instance of type `int` //│ ║ l.+3: rhs = IntLit { value = Pair { //│ ║ ^^^^^^ //│ ║ l.+4: lhs = IntLit { value = 2 }; //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+5: rhs = IntLit { value = 3 } } } }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.90: class IntLit: Expr[int] & { value: int } //│ ╙── ^^^ //│ res: 'a //│ where //│ 'a :> 1 | (Pair[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}}) | ('a, 'a,) p = Pair2 { lhs = IntLit { value = 1 }; rhs = Pair2 { lhs = IntLit { value = 2 }; rhs = IntLit { value = 3 } } } //│ p: Pair2[?, ?] with { //│ lhs: IntLit & {value: 1}, //│ rhs: Pair2[?, ?] with {lhs: IntLit & {value: 2}, rhs: IntLit & {value: 3}} //│ } def eval: Expr['a] -> 'a //│ eval: Expr[?] -> nothing rec def eval(e) = case e of { IntLit -> e.value | Pair -> (eval e.lhs, eval e.rhs) } //│ 'a -> 'value //│ where //│ 'a <: (IntLit with {value: 'value}) | (Pair[?, ?] with {lhs: 'a, rhs: 'a}) //│ 'value :> ('value, 'value,) //│ <: eval: //│ Expr[?] -> nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: rec def eval(e) = case e of //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+2: { IntLit -> e.value //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+3: | Pair -> (eval e.lhs, eval e.rhs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+4: } //│ ║ ^^^ //│ ╟── type `Expr[?]` does not match type `IntLit & ?a | Pair[?, ?] & ?b` //│ ║ l.279: def eval: Expr['a] -> 'a //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.+1: rec def eval(e) = case e of //│ ╙── ^ rec def eval(e) = case e of { IntLit -> e.value | Pair -> (eval e.lhs, eval e.rhs) | _ -> error } //│ 'a -> 'value //│ where //│ 'a <: (IntLit with {value: 'value}) | (Pair[?, ?] with {lhs: 'a, rhs: 'a}) | ~IntLit & ~Pair[?, ?] //│ 'value :> ('value, 'value,) //│ <: eval: //│ Expr[?] -> nothing //│ ╔══[ERROR] Type mismatch in def definition: //│ ║ l.+1: rec def eval(e) = case e of //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+2: { IntLit -> e.value //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+3: | Pair -> (eval e.lhs, eval e.rhs) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.+4: | _ -> error //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.+5: } //│ ║ ^^^ //│ ╟── expression of type `Expr[?] & ~#IntLit & ~?a | Pair[?, ?]` does not have field 'rhs' //│ ╟── Note: constraint arises from field selection: //│ ║ l.+3: | Pair -> (eval e.lhs, eval e.rhs) //│ ║ ^^^^^ //│ ╟── from refined scrutinee: //│ ║ l.+1: rec def eval(e) = case e of //│ ╙── ^ ================================================ FILE: shared/src/test/diff/mlscript/i26.mls ================================================ ================================================ FILE: shared/src/test/diff/mlscript/i56.mls ================================================ // https://github.com/hkust-taco/mlscript/issues/56 def test3 x = case x of { 1 -> true | true -> true | _ -> false } //│ test3: anything -> Bool //│ = [Function: test3] :ns test3 //│ res: forall 'a 'b 'c 'd. 'a -> Bool //│ where //│ 'a <: 1 & 'b | (true & 'c | 'd & ~true) & ~1 //│ = [Function: test3] def test3 x = case x of { 1 -> x | true -> true | _ -> false } //│ test3: (1 & 'a | ~1) -> (Bool | 'a) //│ = [Function: test31] def ty_1: (1 & 'a | true | ~1 & ~true) -> (false | true | 'a) //│ ty_1: (1 & 'a | true | ~1 & ~true) -> (Bool | 'a) //│ = ty_1 = test3 //│ (1 & 'a | ~1) -> (Bool | 'a) //│ <: ty_1: //│ (1 & 'a | true | ~1 & ~true) -> (Bool | 'a) //│ = [Function: test31] def ty_2: (1 & 'a | ~1) -> (false | true | 'a) //│ ty_2: (1 & 'a | ~1) -> (Bool | 'a) //│ = ty_2 = ty_1 //│ (1 & 'a | true | ~1 & ~true) -> (Bool | 'a) //│ <: ty_2: //│ (1 & 'a | ~1) -> (Bool | 'a) //│ = [Function: test31] // * Note: it is a bit counter-intuitive that we have: // * (1 & 'a | ~1) <: (1 & 'a | true | ~1 & ~true) // * and in particular: // * ~1 <: (1 & 'a | true | ~1 & ~true) // * but the latter can be understood by seeing that it's equivalent (by swapping) to: // * (1 | true) <: (1 & 'a | true | 1) // * ie // * (1 | true) <: (true | 1) ================================================ FILE: shared/src/test/diff/mlscript/i65.mls ================================================ :js 42 //│ // Prelude //│ let res; //│ // Query 1 //│ res = 42; //│ // End of generated code //│ res: 42 //│ = 42 :js foo = "oops" //│ // Query 1 //│ globalThis.foo = "oops"; //│ // End of generated code //│ foo: "oops" //│ = 'oops' :js res //│ // Query 1 //│ res = res; //│ // End of generated code //│ res: 42 //│ = 42 :js res = 5 //│ // Query 1 //│ globalThis.res1 = 5; //│ // End of generated code //│ res: 5 //│ = 5 res //│ res: 5 //│ = 5 :js foo //│ // Query 1 //│ res = foo; //│ // End of generated code //│ res: "oops" //│ = 'oops' :js res //│ // Query 1 //│ res = res; //│ // End of generated code //│ res: "oops" //│ = 'oops' def res: bool //│ res: bool //│ = 42 //│ res: 42 //│ = 42 res //│ res: 42 //│ = 42 ================================================ FILE: shared/src/test/diff/nu/AbstractClasses.mls ================================================ :NewDefs abstract class Foo(x: Int) { fun f(y: Int) = x + y } //│ abstract class Foo(x: Int) { //│ fun f: (y: Int) -> Int //│ } :e Foo(1) //│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated //│ ║ l.13: Foo(1) //│ ╙── ^^^ //│ Foo //│ res //│ = Foo {} :e // TODO allow with `new` keyword new Foo(1) //│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated //│ ║ l.22: new Foo(1) //│ ╙── ^^^^^^^^^^ //│ Foo //│ res //│ = Foo {} abstract class Foo(x: Int) { fun f: Int -> Int } //│ abstract class Foo(x: Int) { //│ fun f: Int -> Int //│ } :e Foo(1) //│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated //│ ║ l.39: Foo(1) //│ ╙── ^^^ //│ Foo //│ res //│ = Foo {} :e // TODO support new Foo(1) { fun f = id } //│ ╔══[ERROR] Refinement terms are not yet supported //│ ║ l.48: new Foo(1) { fun f = id } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ cannot generate code for term Rft(App(NuNew(Var(Foo)),Tup(List((None,Fld(_,IntLit(1)))))),TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Var(id)))))) abstract class Bar extends Foo(1) //│ abstract class Bar extends Foo { //│ fun f: Int -> Int //│ } :e module Baz extends Bar Baz.f(1) //│ ╔══[ERROR] Member `f` is declared (or its declaration is inherited) but is not implemented in `Baz` //│ ║ l.63: module Baz extends Bar //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.32: fun f: Int -> Int //│ ╙── ^^^^^^^^^^^^^^^^^ //│ module Baz extends Bar, Foo { //│ fun f: Int -> Int //│ } //│ Int //│ res //│ Runtime error: //│ TypeError: Baz.f is not a function module Baz extends Bar { fun f(x) = x + 1 } Baz.f(1) //│ module Baz extends Bar, Foo { //│ fun f: Int -> Int //│ } //│ Int //│ res //│ = 2 abstract class C1 { fun x: Int | Str } //│ abstract class C1 { //│ fun x: Int | Str //│ } trait T1 { fun x: Int | Bool } //│ trait T1 { //│ fun x: Int | false | true //│ } :e class C2 extends C1, T1 //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C2` //│ ║ l.103: class C2 extends C1, T1 //│ ║ ^^ //│ ╟── Declared here: //│ ║ l.92: abstract class C1 { fun x: Int | Str } //│ ╙── ^^^^^^^^^^^^ //│ class C2 extends C1, T1 { //│ constructor() //│ fun x: Int //│ } class C2 extends C1, T1 { fun x = 1 } //│ class C2 extends C1, T1 { //│ constructor() //│ fun x: 1 //│ } abstract class C2 extends C1, T1 //│ abstract class C2 extends C1, T1 { //│ fun x: Int //│ } :e class C3 extends C2 //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C3` //│ ║ l.127: class C3 extends C2 //│ ║ ^^ //│ ╟── Declared here: //│ ║ l.92: abstract class C1 { fun x: Int | Str } //│ ╙── ^^^^^^^^^^^^ //│ class C3 extends C1, C2, T1 { //│ constructor() //│ fun x: Int //│ } class C3 extends C2 { fun x = 1 } //│ class C3 extends C1, C2, T1 { //│ constructor() //│ fun x: 1 //│ } :e abstract class C { fun x : Int fun foo0 = x fun foo1 = this.x } //│ ╔══[ERROR] Unqualified access to virtual member x //│ ║ l.150: fun foo0 = x //│ ║ ^^^^^^^^ //│ ╟── Declared here: //│ ║ l.149: fun x : Int //│ ╙── ^^^^^^^^^^^ //│ abstract class C { //│ fun foo0: Int //│ fun foo1: Int //│ fun x: Int //│ } :e abstract class C { val x : Int fun foo0 = x fun foo1 = this.x } //│ ╔══[ERROR] Unqualified access to virtual member x //│ ║ l.168: fun foo0 = x //│ ║ ^^^^^^^^ //│ ╟── Declared here: //│ ║ l.167: val x : Int //│ ╙── ^^^^^^^^^^^ //│ abstract class C { //│ fun foo0: Int //│ fun foo1: Int //│ val x: Int //│ } ================================================ FILE: shared/src/test/diff/nu/Andong.mls ================================================ :NewDefs class Union(a: Region, b: Region) //│ class Union[Region](a: Region, b: Region) fun hmm(x) = if x is Union(x, y) then x //│ fun hmm: forall 'a. Union['a] -> 'a fun hmm(x) = if x is Union(z, y) then x //│ fun hmm: forall 'Region. Union['Region] -> Union['Region] ================================================ FILE: shared/src/test/diff/nu/Annotations.mls ================================================ :NewDefs // This should not be needed once we implemented the @tailrec annotation internally. module tailrec extends Annotation //│ module tailrec extends Annotation tailrec //│ tailrec //│ res //│ = tailrec { class: [class tailrec extends Object] } @tailrec fun foo(n) = if n > 0 then log(join of "hi ", String(n)) foo(n - 1) else () //│ fun foo: Int -> () module Foo extends Annotation { } //│ module Foo extends Annotation @Foo 5 //│ 5 //│ res //│ = 5 Foo //│ Foo //│ res //│ = Foo { class: [class Foo extends Object] } :e @Bar 5 //│ ╔══[ERROR] identifier not found: Bar //│ ║ l.35: @Bar 5 //│ ╙── ^^^ //│ 5 //│ res //│ = 5 :e let x = 1 @x 2 //│ ╔══[ERROR] Type mismatch in annotated integer literal: //│ ║ l.45: @x 2 //│ ║ ^^^ //│ ╟── integer literal of type `1` is not an instance of type `Annotation` //│ ║ l.44: let x = 1 //│ ║ ^ //│ ╟── but it flows into reference with expected type `Annotation` //│ ║ l.45: @x 2 //│ ╙── ^ //│ let x: 1 //│ 2 //│ x //│ = 1 //│ res //│ = 2 :e let x = 1 @x fun foo(x) = 1 //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.65: fun foo(x) = 1 //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Annotation` //│ ║ l.63: let x = 1 //│ ╙── ^ //│ let x: 1 //│ fun foo: anything -> 1 //│ x //│ = 1 fun foo(n) = if n > 0.5 then log(join of "hi ", String(n)) foo(div of n, 2) @tailrec foo(n - 1) else () //│ fun foo: Int -> () foo(5) //│ () //│ res //│ = undefined //│ // Output //│ hi 5 //│ hi 2.5 //│ hi 1.25 //│ hi 0.625 //│ hi 1.5 //│ hi 0.75 //│ hi 4 //│ hi 2 //│ hi 1 //│ hi 1 //│ hi 3 //│ hi 1.5 //│ hi 0.75 //│ hi 2 //│ hi 1 //│ hi 1 // TODO: Tail recursion modulo cons // fun foo(n) = // ... // Cons(a, foo(n - 1)) // ———> // fun foo(n) = // ... // foo(n - 1, Cons(a, .)) ================================================ FILE: shared/src/test/diff/nu/ArrayProg.mls ================================================ :NewDefs fun cast(x) = x declare fun cast: anything -> nothing //│ fun cast: forall 'a. 'a -> 'a //│ fun cast: anything -> nothing fun mapi: (Array['a], ('a, Int) -> 'b) -> Array['b] fun mapi(xs, f) = cast(xs).map(f) //│ fun mapi: (anything, anything) -> nothing //│ fun mapi: forall 'a 'b. (Array['a], ('a, Int) -> 'b) -> Array['b] mapi of ["a", "", "bb"], (x, i) => [i, length of x] //│ Array[[Int, Int]] //│ res //│ = [ [ 0, 1 ], [ 1, 0 ], [ 2, 2 ] ] fun map(xs, f) = mapi(xs, (x, i) => f(x)) //│ fun map: forall 'a 'b. (Array['a], 'a -> 'b) -> Array['b] map of ["a", "", "bb"], x => length of x //│ Array[Int] //│ res //│ = [ 1, 0, 2 ] fun zip: (Array['a], Array['b & ~undefined & Object]) -> Array[['a, 'b]] fun zip(xs, ys) = mapi of xs, (x, i) => if ys.[i] is undefined then error y then [x, y] //│ fun zip: forall 'c 'd. (Array['c], Array[Object & 'd & ~()]) -> Array[['c, 'd]] //│ fun zip: forall 'a 'b. (Array['a], Array[Object & 'b & ~()]) -> Array[['a, 'b]] zip //│ forall 'a 'b. (Array['a], Array[Object & 'b & ~()]) -> Array[['a, 'b]] //│ res //│ = [Function: zip] class Numbr(n: Int) class Vectr(xs: Array[Numbr | Vectr]) //│ class Numbr(n: Int) //│ class Vectr(xs: Array[Numbr | Vectr]) class Pair[A, B](a: A, b: B) //│ class Pair[A, B](a: A, b: B) fun unbox(x) = if x is Numbr(n) then n Vectr(xs) then map of xs, unbox //│ fun unbox: forall 'a. (Numbr | Vectr) -> (Int | 'a) //│ where //│ 'a :> Array[Int | 'a] fun add(e) = if e is Pair(Numbr(n), Numbr(m)) then Numbr(n + m) Pair(Vectr(xs), Vectr(ys)) then Vectr of map of zip(xs, ys), ([x, y]) => add of Pair of x, y Pair(Vectr(xs), Numbr(n)) then Vectr of map of xs, x => add of Pair of x, Numbr(n) Pair(Numbr(n), Vectr(xs)) then Vectr of map of xs, x => add of Pair of Numbr(n), x //│ fun add: Pair[Numbr | Vectr, Numbr | Vectr] -> (Numbr | Vectr) add(Pair(Numbr(0), Numbr(1))) //│ Numbr | Vectr //│ res //│ = Numbr {} add(Pair(Vectr([]), Vectr([]))) //│ Numbr | Vectr //│ res //│ = Vectr {} let v = Vectr of [Numbr(10), Numbr(20), Numbr(30)] //│ let v: Vectr //│ v //│ = Vectr {} unbox(v) //│ forall 'a. Int | 'a //│ where //│ 'a :> Array[Int | 'a] //│ res //│ = [ 10, 20, 30 ] let res = add of Pair of (Vectr of [Numbr(1), Numbr(2)]), (Vectr of [Numbr(3), v]) //│ let res: Numbr | Vectr //│ res //│ = Vectr {} unbox(res) //│ forall 'a. Int | 'a //│ where //│ 'a :> Array[Int | 'a] //│ res //│ = [ 4, [ 12, 22, 32 ] ] fun add2(e) = if e is Pair(Numbr(n), Numbr(m)) then Numbr(m + m) Pair(Numbr(n), Vectr(n)) then n //│ fun add2: Pair[Numbr, Numbr | Vectr] -> (Int | Numbr) add2(Pair(Numbr(0), Numbr(1))) //│ Int | Numbr //│ res //│ = Numbr {} // * Playing with approximated unions/intersections fun t: ([Numbr,Numbr]|[Vectr,Vectr]) -> Int //│ fun t: ([Numbr | Vectr, Numbr | Vectr]) -> Int fun s: (([Numbr,Numbr] -> Int) & ([Vectr,Vectr] -> Int),) //│ fun s: (Numbr | Vectr, Numbr | Vectr) -> Int // FIXME why does the above parse the same as: fun s: ([Numbr,Numbr] -> Int) & ([Vectr,Vectr] -> Int) //│ fun s: (Numbr | Vectr, Numbr | Vectr) -> Int s(Vectr([]),Vectr([])) //│ Int //│ res //│ = //│ s is not implemented module A { fun g: (Int -> Int) & (Str -> Str) fun g(x) = x } // g: (Int | Str) -> (Int & Str) -- under-approx // g: (Int & Str) -> (Int | Str) -- over-approx //│ module A { //│ fun g: Int -> Int & Str -> Str //│ } A.g(0) //│ Int | Str //│ res //│ = 0 // === === === ERROR CASES === === === // :ShowRelativeLineNums :AllowTypeErrors :e s([Numbr(0),Numbr(0)]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: s([Numbr(0),Numbr(0)]) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[[?a, ?b]]` does not match type `[Numbr | Vectr, Numbr | Vectr]` //│ ║ l.+1: s([Numbr(0),Numbr(0)]) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.136: fun s: ([Numbr,Numbr] -> Int) & ([Vectr,Vectr] -> Int) //│ ╙── ^^^^^^^^^^^^^ //│ Int | error // g <: 0 -> 'a :e fun add(e) = if e is Pair(Numbr(n), Numbr(m)) then 0 Pair(Vectr(xs), Vectr(ys)) then 1 Pair(Vectr(xs), Numbr(n)) then 2 //│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `Numbr`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╙── ^^^^^ //│ fun add: Pair[Numbr | Vectr, Numbr] -> (0 | 1 | 2) :e fun add(e) = if e is Pair(Numbr(n), Numbr(m)) then 0 Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `Numbr`, //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Vectr` //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] when `e` is `Pair`, and //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `Vectr`, //│ ║ l.+4: Pair(Vectr(xs), Vectr(ys)) then 1 //│ ║ ^^^^^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Numbr` //│ ║ l.+3: Pair(Numbr(n), Numbr(m)) then 0 //│ ╙── ^^^^^ //│ fun add: Pair[Numbr | Vectr, nothing] -> (0 | 1) :e add2(Pair(Vectr(0), Numbr(1))) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` does not match type `Array[Numbr | Vectr]` //│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.48: class Vectr(xs: Array[Numbr | Vectr]) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Vectr` is not an instance of type `Numbr` //│ ║ l.+1: add2(Pair(Vectr(0), Numbr(1))) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.113: Pair(Numbr(n), Numbr(m)) then Numbr(m + m) //│ ║ ^^^^^ //│ ╟── from field selection: //│ ║ l.52: class Pair[A, B](a: A, b: B) //│ ║ ^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.52: class Pair[A, B](a: A, b: B) //│ ╙── ^ //│ Int | Numbr | error ================================================ FILE: shared/src/test/diff/nu/Ascription.mls ================================================ :NewDefs 1: Int //│ Int //│ res //│ = 1 1 : Int //│ Int //│ res //│ = 1 // TODO? :pe 1 : Int : Int //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.15: 1 : Int : Int //│ ╙── ^^^ //│ anything //│ res //│ = 1 fun foo(x: Int) = x + 1 //│ fun foo: (x: Int) -> Int fun foo(x : Int) = x + 1 //│ fun foo: Int -> Int foo(123 : Int) : Int //│ Int //│ res //│ = 124 :e foo(123: Int): Int //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments //│ ║ l.35: foo(123: Int): Int //│ ╙── ^^^^^^^^^^ //│ Int //│ Code generation encountered an error: //│ unresolved symbol Int :e foo(123:Int):Int //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments //│ ║ l.44: foo(123:Int):Int //│ ╙── ^^^^^^^^^ //│ Int //│ Code generation encountered an error: //│ unresolved symbol Int fun foo(f) = let g = (x => f(x)): forall 'a : 'a -> 'a in g(123) //│ fun foo: (??a -> ??a0) -> 123 fun foo(f) = let g: forall 'a : 'a -> 'a = x => f(x) in g(123) //│ fun foo: (??a -> ??a0) -> 123 ================================================ FILE: shared/src/test/diff/nu/AuxCtors.mls ================================================ :NewDefs class C(val x: Int) { constructor(y: Int) { x = y } } new C(123).x //│ class C(x: Int) { //│ constructor(y: Int) //│ } //│ Int //│ res //│ = 123 class C(val x: Int) { constructor(y) { x = y } } new C(123).x //│ class C(x: Int) { //│ constructor(y: Int) //│ } //│ Int //│ res //│ = 123 class C(val x: Int) { constructor(y: Int) { log(y); x = y; log(x) } } new C(123).x //│ class C(x: Int) { //│ constructor(y: Int) //│ } //│ Int //│ res //│ = 123 //│ // Output //│ 123 //│ 123 :e class C(val x: Int) { constructor(y: Int) { x = y; log(x); x = y + 1; log(x) } } new C(123).x //│ ╔══[ERROR] Class parameter 'x' was already set //│ ║ l.35: class C(val x: Int) { constructor(y: Int) { x = y; log(x); x = y + 1; log(x) } } //│ ╙── ^ //│ class C(x: Int) { //│ constructor(y: Int) //│ } //│ Int //│ res //│ = 124 //│ // Output //│ 123 //│ 124 :e class C(val x: Int) { constructor(y: Int) { log(x); x = y } } //│ ╔══[ERROR] identifier not found: x //│ ║ l.51: class C(val x: Int) { constructor(y: Int) { log(x); x = y } } //│ ╙── ^ //│ class C(x: Int) { //│ constructor(y: Int) //│ } //│ Code generation encountered an error: //│ unresolved symbol x :e class C(val x: Int) { constructor(y: Str) { x = y } } //│ ╔══[ERROR] Type mismatch in auxiliary class constructor: //│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Str` is not an instance of type `Int` //│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.62: class C(val x: Int) { constructor(y: Str) { x = y } } //│ ╙── ^^^ //│ class C(x: Int) { //│ constructor(y: Str) //│ } :e class C(val x: Int) { constructor(y: Int) { z = y } } //│ ╔══[ERROR] Unknown class parameter 'z' //│ ║ l.80: class C(val x: Int) { constructor(y: Int) { z = y } } //│ ╙── ^ //│ class C(x: Int) { //│ constructor(y: Int) //│ } //│ Syntax error: //│ Private field '#z' must be declared in an enclosing class :e class C(val x: Int) { constructor(val y: Int) { x = y } } //│ ╔══[ERROR] Cannot use `val` in constructor parameters //│ ║ l.91: class C(val x: Int) { constructor(val y: Int) { x = y } } //│ ╙── ^ //│ class C(x: Int) { //│ constructor(y: Int) //│ } :e class C(val x: Int) { constructor(val y) { x = y } } //│ ╔══[ERROR] Cannot use `val` in constructor parameters //│ ║ l.100: class C(val x: Int) { constructor(val y) { x = y } } //│ ╙── ^ //│ class C(x: Int) { //│ constructor(y: Int) //│ } :e class C(val x: Int) { constructor(2 + 2) { x = 0 } } //│ ╔══[ERROR] Unsupported constructor parameter shape //│ ║ l.109: class C(val x: Int) { constructor(2 + 2) { x = 0 } } //│ ╙── ^^^^^ //│ class C(x: Int) { //│ constructor(: error) //│ } //│ Code generation encountered an error: //│ Unexpected constructor parameters in C. class C(val x: Int, y: Int) { constructor(z: Int) { x = z; y = z } log([x, y]) } //│ class C(x: Int, y: Int) { //│ constructor(z: Int) //│ } :e C(11) //│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword //│ ║ l.129: C(11) //│ ╙── ^ //│ C //│ res //│ = C {} //│ // Output //│ [ 11, 11 ] :e new C(1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.140: new C(1, 2) //│ ║ ^^^^^^^^^^^ //│ ╟── tuple literal of type `[1, 2]` does not match type `[z: Int]` //│ ║ l.140: new C(1, 2) //│ ╙── ^^^^^^ //│ C | error //│ res //│ = C {} //│ // Output //│ [ 1, 1 ] new C(11) //│ C //│ res //│ = C {} //│ // Output //│ [ 11, 11 ] :e new C(1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.161: new C(1, 2) //│ ║ ^^^^^^^^^^^ //│ ╟── tuple literal of type `[1, 2]` does not match type `[z: Int]` //│ ║ l.161: new C(1, 2) //│ ╙── ^^^^^^ //│ C | error //│ res //│ = C {} //│ // Output //│ [ 1, 1 ] :pe :w class C { constructor(x: Int); constructor(y: Int) } //│ ╔══[PARSE ERROR] A class may have at most one explicit constructor //│ ║ l.176: class C { constructor(x: Int); constructor(y: Int) } //│ ╙── ^^^^^ //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.176: class C { constructor(x: Int); constructor(y: Int) } //│ ╙── ^ //│ class C { //│ constructor(x: Int) //│ } :w // * FIXME class Foo { constructor(x: Int){}; val y = 2 } //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.189: class Foo { constructor(x: Int){}; val y = 2 } //│ ╙── ^ //│ class Foo { //│ constructor(x: Int) //│ val y: 2 //│ } :w class Foo { constructor(x: Int){}; val y = 2 } //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.199: class Foo { constructor(x: Int){}; val y = 2 } //│ ╙── ^ //│ class Foo { //│ constructor(x: Int) //│ val y: 2 //│ } class Foo { constructor(x: Int){} val y = 2 } //│ class Foo { //│ constructor(x: Int) //│ val y: 2 //│ } class Foo { constructor(x: Int){} val y = 2 } //│ class Foo { //│ constructor(x: Int) //│ val y: 2 //│ } ================================================ FILE: shared/src/test/diff/nu/BadAliases.mls ================================================ :NewDefs // TODO check cyclicity // :e type A = A //│ type A = A // TODO check cyclicity // :e type A = A | Int //│ type A = Int | A // TODO check regularity // :e type Foo[A] = { x: A, y: Foo[[A, A]] } //│ type Foo[A] = {x: A, y: Foo[[A, A]]} // TODO support abstract types :e type Test //│ ╔══[ERROR] Type alias definition requires a right-hand side //│ ║ l.22: type Test //│ ╙── ^^^^^^^^^ //│ type Test = error :e type Test(n: Int) = n //│ ╔══[ERROR] Type alias definitions cannot have value parameters //│ ║ l.29: type Test(n: Int) = n //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] type identifier not found: n //│ ║ l.29: type Test(n: Int) = n //│ ╙── ^ //│ type Test = error class Base //│ class Base { //│ constructor() //│ } :pe :e type Test: Base //│ ╔══[PARSE ERROR] Expected end of input; found ':' instead //│ ║ l.45: type Test: Base //│ ╙── ^ //│ ╔══[ERROR] Type alias definition requires a right-hand side //│ ║ l.45: type Test: Base //│ ╙── ^^^^^^^^^ //│ type Test = error :pe :e type Test: Base = Int //│ ╔══[PARSE ERROR] Expected end of input; found ':' instead //│ ║ l.56: type Test: Base = Int //│ ╙── ^ //│ ╔══[ERROR] Type alias definition requires a right-hand side //│ ║ l.56: type Test: Base = Int //│ ╙── ^^^^^^^^^ //│ type Test = error :e type Test extends Base //│ ╔══[ERROR] Type alias definitions cannot extend parents //│ ║ l.66: type Test extends Base //│ ╙── ^^^^ //│ ╔══[ERROR] Type alias definition requires a right-hand side //│ ║ l.66: type Test extends Base //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ type Test = error :pe :e type Test extends Base = Int //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.77: type Test extends Base = Int //│ ╙── ^ //│ ╔══[ERROR] Type alias definitions cannot extend parents //│ ║ l.77: type Test extends Base = Int //│ ╙── ^^^^ //│ ╔══[ERROR] Type alias definition requires a right-hand side //│ ║ l.77: type Test extends Base = Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ type Test = error :e type Test = Int extends Base //│ ╔══[ERROR] Type alias definitions cannot extend parents //│ ║ l.90: type Test = Int extends Base //│ ╙── ^^^^ //│ type Test = Int :pe type Poly[mut A] = A //│ ╔══[PARSE ERROR] Unexpected 'mut' keyword here //│ ║ l.98: type Poly[mut A] = A //│ ╙── ^^^ //│ type Poly = A :pe type Poly[#A] = A //│ ╔══[PARSE ERROR] Unexpected '#' here //│ ║ l.105: type Poly[#A] = A //│ ╙── ^ //│ type Poly = A ================================================ FILE: shared/src/test/diff/nu/BadBlocks.mls ================================================ :NewDefs :e fun test = fun lol = log("ok") [lol, lol] //│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. //│ ║ l.6: fun lol = log("ok") //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ fun test: [(), ()] test //│ [(), ()] //│ res //│ = [ undefined, undefined ] //│ // Output //│ ok :e fun test = fun lol = log("ok") [] //│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. //│ ║ l.22: fun lol = log("ok") //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ fun test: [] test //│ [] //│ res //│ = [] //│ // Output //│ ok fun test = let a = 0 a //│ fun test: 0 :e fun test = fun a = b fun b = 1 a //│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. //│ ║ l.43: fun a = b //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Cannot use `val` or `fun` in local block; use `let` instead. //│ ║ l.44: fun b = 1 //│ ╙── ^^^^^^^^^ //│ fun test: 1 // TODO[init-check] reject fun test = let a = b let b = 1 a //│ fun test: 1 :re test //│ 1 //│ res //│ Runtime error: //│ ReferenceError: Cannot access 'b' before initialization :js fun test = let a() = b let b = 1 a() //│ fun test: 1 //│ // Prelude //│ class TypingUnit8 {} //│ const typing_unit8 = new TypingUnit8; //│ // Query 1 //│ globalThis.test5 = function test5() { //│ return ((() => { //│ let a = () => b; //│ let b = 1; //│ return a(); //│ })()); //│ }; //│ // End of generated code test //│ 1 //│ res //│ = 1 // * OK fun test = class Foo(x: Int) { fun y = x + 1 } Foo(1).y //│ fun test: Int // * MAYBE OK :ge // TODO accept? fun test = let r() = Foo(1).y class Foo(x: Int) { fun y = x + 1 } r() //│ fun test: Int //│ Code generation encountered an error: //│ unresolved symbol Foo // * NOT OK :ge // :e // TODO[init-check] reject fun test = let r = Foo(1).y class Foo(x: Int) { fun y = x + 1 } r //│ fun test: Int //│ Code generation encountered an error: //│ unresolved symbol Foo :re test //│ Int //│ res //│ Runtime error: //│ ReferenceError: test8 is not defined :pe :ge fun test = { fun a = 1 } //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.130: fun a = 1 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected '=' here //│ ║ l.130: fun a = 1 //│ ╙── ^ //│ fun test: {a: () -> 1} //│ Code generation encountered an error: //│ unresolved symbol a :pe :e fun test = { val res = a + 1 fun a = 123 }.res //│ ╔══[PARSE ERROR] Unexpected '=' here //│ ║ l.145: val res = a + 1 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: res //│ ║ l.145: val res = a + 1 //│ ╙── ^^^ //│ fun test: error :pe // TODO support :e fun test = (new { val res = a + 1 fun a = 123 }).res //│ ╔══[PARSE ERROR] Unexpected '=' here //│ ║ l.159: val res = a + 1 //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: res //│ ║ l.159: val res = a + 1 //│ ╙── ^^^ //│ ╔══[ERROR] Unexpected type `{res: error}` after `new` keyword //│ ║ l.158: fun test = (new { //│ ║ ^ //│ ║ l.159: val res = a + 1 //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.160: fun a = 123 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.161: }).res //│ ╙── ^ //│ fun test: error //│ Code generation encountered an error: //│ Unsupported `new` class term: Bra(true,Rcd(List((Var(res),Fld(g,Var(res)))))) ================================================ FILE: shared/src/test/diff/nu/BadClassInherit.mls ================================================ :NewDefs class C1(x: Int) //│ class C1(x: Int) :e class C2(x: Int) extends C1(y) { val y = x } //│ ╔══[ERROR] identifier not found: y //│ ║ l.8: class C2(x: Int) extends C1(y) { //│ ╙── ^ //│ class C2(x: Int) extends C1 { //│ val y: Int //│ } //│ Code generation encountered an error: //│ unresolved symbol y :e abstract class C2 extends C1(y) { val y: Int } //│ ╔══[ERROR] identifier not found: y //│ ║ l.21: abstract class C2 extends C1(y) { //│ ╙── ^ //│ abstract class C2 extends C1 { //│ val y: Int //│ } //│ Code generation encountered an error: //│ unresolved symbol y :e abstract class C2 extends C1(this.y) { val y: Int } //│ ╔══[ERROR] identifier not found: this //│ ║ l.34: abstract class C2 extends C1(this.y) { //│ ╙── ^^^^ //│ abstract class C2 extends C1 { //│ val y: Int //│ } class C1(x: C1) //│ class C1(x: C1) :e class C2 extends C1(this) //│ ╔══[ERROR] identifier not found: this //│ ║ l.49: class C2 extends C1(this) //│ ╙── ^^^^ //│ class C2 extends C1 { //│ constructor() //│ } class Foo { virtual fun x: Int = 1 } //│ class Foo { //│ constructor() //│ fun x: Int //│ } :e class Bar extends Foo { fun x = false } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.65: class Bar extends Foo { fun x = false } //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ║ l.65: class Bar extends Foo { fun x = false } //│ ║ ^^^^^ //│ ╟── but it flows into definition of method x with expected type `Int` //│ ║ l.65: class Bar extends Foo { fun x = false } //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class Bar extends Foo { //│ constructor() //│ fun x: false //│ } :e class Bar extends Foo { fun x: Bool fun x = false } //│ ╔══[ERROR] Type mismatch in signature of member `x`: //│ ║ l.88: fun x: Bool //│ ║ ^^^^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.88: fun x: Bool //│ ║ ^^^^ //│ ╟── but it flows into signature of member `x` with expected type `Int` //│ ║ l.88: fun x: Bool //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in signature of member `x`: //│ ║ l.88: fun x: Bool //│ ║ ^^^^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.88: fun x: Bool //│ ║ ^^^^ //│ ╟── but it flows into signature of member `x` with expected type `Int` //│ ║ l.88: fun x: Bool //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class Bar extends Foo { //│ constructor() //│ fun x: Bool //│ } mixin M { fun x = false } //│ mixin M() { //│ fun x: false //│ } :e class Bar extends Foo, M //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.126: mixin M { fun x = false } //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ║ l.126: mixin M { fun x = false } //│ ║ ^^^^^ //│ ╟── but it flows into definition of method x with expected type `Int` //│ ║ l.126: mixin M { fun x = false } //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: //│ ║ l.58: class Foo { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class Bar extends Foo { //│ constructor() //│ fun x: false //│ } class A { class X { fun f = 1 } } trait B { class X { fun g = 1 } } //│ class A { //│ constructor() //│ class X { //│ constructor() //│ fun f: 1 //│ } //│ } //│ trait B { //│ class X { //│ constructor() //│ fun g: 1 //│ } //│ } :e class C extends A, B //│ ╔══[ERROR] Class member 'X' cannot override class member of the same name declared in parent //│ ║ l.172: class C extends A, B //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Originally declared here: //│ ║ l.156: trait B { class X { fun g = 1 } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Intersection of class member and class members currently unsupported //│ ║ l.172: class C extends A, B //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── The class member is defined here: //│ ║ l.155: class A { class X { fun f = 1 } } //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── The class member is defined here: //│ ║ l.156: trait B { class X { fun g = 1 } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ class C extends A, B { //│ constructor() //│ class X { //│ constructor() //│ fun f: 1 //│ } //│ } :e class C extends A { class X { fun g = 1 } } //│ ╔══[ERROR] Class member 'X' cannot override class member of the same name declared in parent //│ ║ l.197: class C extends A { //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.198: class X { fun g = 1 } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.199: } //│ ║ ^ //│ ╟── Originally declared here: //│ ║ l.155: class A { class X { fun f = 1 } } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ class C extends A { //│ constructor() //│ class X { //│ constructor() //│ fun g: 1 //│ } //│ } :e class Foo2 extends Foo2 //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.221: class Foo2 extends Foo2 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ class Foo2 extends Foo2 { //│ constructor() //│ } //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/nu/BadClassSignatures.mls ================================================ :NewDefs abstract class Foo(): Foo //│ abstract class Foo(): Foo module Bar extends Foo //│ module Bar extends Foo abstract class Baz(): Baz | Baz //│ abstract class Baz(): Baz abstract class Foo(): Int //│ abstract class Foo(): Int fun foo(x: Foo) = x : Int //│ fun foo: (x: Foo) -> Int :e let f = Foo() : Foo //│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated //│ ║ l.21: let f = Foo() : Foo //│ ╙── ^^^ //│ let f: Foo //│ f //│ = Foo {} f + 1 //│ Int //│ res //│ = '[object Object]1' :e Foo() + 1 //│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated //│ ║ l.35: Foo() + 1 //│ ╙── ^^^ //│ Int //│ res //│ = '[object Object]1' :e (Foo() : Foo) + 1 //│ ╔══[ERROR] Class Foo is abstract and cannot be instantiated //│ ║ l.44: (Foo() : Foo) + 1 //│ ╙── ^^^ //│ Int //│ res //│ = '[object Object]1' :w :e module Foo: Int //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract module definitions //│ ║ l.55: module Foo: Int //│ ║ ^^^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.55: module Foo: Int //│ ║ ^^^^^^^^^^ //│ ╟── expression of type `#Foo` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.55: module Foo: Int //│ ╙── ^^^ //│ module Foo: Int Foo + 1 //│ Int //│ res //│ = '[object Object]1' :w class Foo(): {} //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.76: class Foo(): {} //│ ║ ^^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ class Foo() :w class Foo(): {} { fun x = 0 } //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.84: class Foo(): {} { //│ ║ ^^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ class Foo() { //│ fun x: 0 //│ } :w :e class Foo(): { x: Int } { fun x = 0 } //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.97: class Foo(): { x: Int } { //│ ║ ^^^^^^^^^^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.97: class Foo(): { x: Int } { //│ ╙── ^ //│ class Foo(): {x: Int} { //│ fun x: 0 //│ } abstract class Foo(): { x: Int } { fun x = 0 } //│ abstract class Foo(): {x: Int} { //│ fun x: 0 //│ } :e module FooM extends Foo //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.111: abstract class Foo(): { x: Int } { //│ ╙── ^ //│ module FooM extends Foo { //│ fun x: 0 //│ } abstract class Foo(): { x: Int } { fun x: Int fun x = 0 } //│ abstract class Foo(): {x: Int} { //│ fun x: Int //│ } :e // TODO should work? module FooM extends Foo //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.127: abstract class Foo(): { x: Int } { //│ ╙── ^ //│ module FooM extends Foo { //│ fun x: Int //│ } :w :e class Foo(): { x: 'FigureItOut } { fun x: Int fun x = 0 } //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.146: class Foo(): { x: 'FigureItOut } { //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ ╔══[ERROR] Type error in type declaration //│ ║ l.146: class Foo(): { x: 'FigureItOut } { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.147: fun x: Int //│ ║ ^^^^^^^^^^^^ //│ ║ l.148: fun x = 0 //│ ║ ^^^^^^^^^^^ //│ ║ l.149: } //│ ║ ^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.147: fun x: Int //│ ╙── ^^^ //│ class Foo(): {x: ??FigureItOut} { //│ fun x: Int //│ } :e not(Foo().x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.171: not(Foo().x) //│ ║ ^^^^^^^^^^^^ //│ ╟── field selection of type `Int & ??FigureItOut` is not an instance of type `Bool` //│ ║ l.171: not(Foo().x) //│ ╙── ^^^^^^^ //│ error | false | true //│ res //│ = true (f: Foo) => f.x //│ (f: Foo) -> (Int & ??FigureItOut) //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/nu/BadClasses.mls ================================================ :NewDefs mixin M0(x: Int) //│ mixin M0(x: Int) :e class C0 extends M0 //│ ╔══[ERROR] mixin M0 expects 1 parameter(s); got 0 //│ ║ l.9: class C0 extends M0 //│ ╙── ^^ //│ class C0 { //│ constructor() //│ } :e class C0 extends M0(1, 2) //│ ╔══[ERROR] mixin M0 expects 1 parameter(s); got 2 //│ ║ l.18: class C0 extends M0(1, 2) //│ ╙── ^^^^^^^ //│ class C0 { //│ constructor() //│ } :e class C0 extends M0(true) //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.27: class C0 extends M0(true) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Int` //│ ║ l.27: class C0 extends M0(true) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: mixin M0(x: Int) //│ ╙── ^^^ //│ class C0 { //│ constructor() //│ } module Foo { fun foo = 0 fun bar = foo } [Foo.foo, Foo.bar] //│ module Foo { //│ fun bar: 0 //│ fun foo: 0 //│ } //│ [0, 0] //│ res //│ = [ 0, 0 ] // * FIXME add initialization checking for non-lazy fields module Foo { val foo = tmp fun bar = foo let tmp = 0 } [Foo.foo, Foo.bar] //│ module Foo { //│ fun bar: 0 //│ val foo: 0 //│ let tmp: 0 //│ } //│ [0, 0] //│ res //│ = [ undefined, undefined ] module Bar { fun hello = 0 type I = Int } //│ module Bar { //│ type I = Int //│ fun hello: 0 //│ } :e hello //│ ╔══[ERROR] identifier not found: hello //│ ║ l.82: hello //│ ╙── ^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol hello :e 1 : I //│ ╔══[ERROR] type identifier not found: I //│ ║ l.91: 1 : I //│ ╙── ^ //│ error //│ res //│ = 1 :e :w class Foo[A] { 42: A } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.102: class Foo[A] { 42: A } //│ ║ ^^ //│ ╟── integer literal of type `42` does not match type `A` //│ ╟── Note: constraint arises from type parameter: //│ ║ l.102: class Foo[A] { 42: A } //│ ╙── ^ //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in type ascription: //│ ║ l.102: class Foo[A] { 42: A } //│ ║ ^^ //│ ╟── expression in statement position of type `A` does not match type `()` //│ ╟── Note: type parameter A is defined at: //│ ║ l.102: class Foo[A] { 42: A } //│ ╙── ^ //│ class Foo[A] { //│ constructor() //│ } :e class C1 { fun oops = this.x } //│ ╔══[ERROR] Type `#C1` does not contain member `x` //│ ║ l.125: class C1 { fun oops = this.x } //│ ╙── ^^ //│ class C1 { //│ constructor() //│ fun oops: error //│ } :e class C { fun x: Int } //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C` //│ ║ l.136: class C { fun x: Int } //│ ║ ^ //│ ╟── Declared here: //│ ║ l.136: class C { fun x: Int } //│ ╙── ^^^^^^^^^^ //│ class C { //│ constructor() //│ fun x: Int //│ } :e class C { val x: Int } //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C` //│ ║ l.149: class C { val x: Int } //│ ║ ^ //│ ╟── Declared here: //│ ║ l.149: class C { val x: Int } //│ ╙── ^^^^^^^^^^ //│ class C { //│ constructor() //│ val x: Int //│ } ================================================ FILE: shared/src/test/diff/nu/BadFieldInit.mls ================================================ :NewDefs :js module A { val x = y val y = x } //│ module A { //│ val x: nothing //│ val y: nothing //│ } //│ // Prelude //│ let res; //│ class TypingUnit { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #x; //│ get x() { return this.#x; } //│ #y; //│ get y() { return this.#y; } //│ constructor() { //│ const qualifier1 = this; //│ this.#x = qualifier1.y; //│ const x = this.#x; //│ this.#y = x; //│ const y = this.#y; //│ } //│ } //│ this.#A = new A(); //│ this.#A.class = A; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit = new TypingUnit; //│ globalThis.A = typing_unit.A; //│ // End of generated code [A.x, A.y] //│ [nothing, nothing] //│ res //│ = [ undefined, undefined ] :js module A { val x = y val y = 1 } //│ module A { //│ val x: 1 //│ val y: 1 //│ } //│ // Prelude //│ class TypingUnit2 { //│ #A; //│ constructor() { //│ } //│ get A() { //│ const qualifier = this; //│ if (this.#A === undefined) { //│ class A { //│ #x; //│ get x() { return this.#x; } //│ #y; //│ get y() { return this.#y; } //│ constructor() { //│ const qualifier1 = this; //│ this.#x = qualifier1.y; //│ const x = this.#x; //│ this.#y = 1; //│ const y = this.#y; //│ } //│ } //│ this.#A = new A(); //│ this.#A.class = A; //│ } //│ return this.#A; //│ } //│ } //│ const typing_unit2 = new TypingUnit2; //│ globalThis.A = typing_unit2.A; //│ // End of generated code [A.x, A.y] //│ [1, 1] //│ res //│ = [ undefined, 1 ] :e class B(x: Int, y: Int) { constructor() { x = y y = x } } //│ ╔══[ERROR] identifier not found: y //│ ║ l.102: x = y //│ ╙── ^ //│ class B(x: Int, y: Int) { //│ constructor() //│ } //│ Code generation encountered an error: //│ unresolved symbol y :e class B(x: Int, y: Int) { constructor() { x = y y = 1 } } //│ ╔══[ERROR] identifier not found: y //│ ║ l.118: x = y //│ ╙── ^ //│ class B(x: Int, y: Int) { //│ constructor() //│ } //│ Code generation encountered an error: //│ unresolved symbol y ================================================ FILE: shared/src/test/diff/nu/BadLets.mls ================================================ :NewDefs module M { let x = 0 } //│ module M { //│ let x: 0 //│ } :e M.x //│ ╔══[ERROR] Let binding 'x' cannot tbe accessed as a field //│ ║ l.12: M.x //│ ║ ^^ //│ ╟── Use a `val` declaration to make it a field //│ ║ l.5: let x = 0 //│ ╙── ^^^^^ //│ 0 | error //│ res //│ = undefined abstract class Base { val x: Int } //│ abstract class Base { //│ val x: Int //│ } :e class Der extends Base { let x = true } //│ ╔══[ERROR] Cannot implement value member 'x' with a let binding //│ ║ l.30: class Der extends Base { let x = true } //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: //│ ║ l.24: abstract class Base { val x: Int } //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in definition of let binding x: //│ ║ l.30: class Der extends Base { let x = true } //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Int` //│ ║ l.30: class Der extends Base { let x = true } //│ ║ ^^^^ //│ ╟── but it flows into definition of let binding x with expected type `Int` //│ ║ l.30: class Der extends Base { let x = true } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.24: abstract class Base { val x: Int } //│ ║ ^^^ //│ ╟── from signature of member `x`: //│ ║ l.24: abstract class Base { val x: Int } //│ ╙── ^^^^^^ //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `Der` //│ ║ l.30: class Der extends Base { let x = true } //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.24: abstract class Base { val x: Int } //│ ║ ^^^^^^^^^^ //│ ╟── Note: value member `x` is private and cannot be used as a valid implementation //│ ║ l.30: class Der extends Base { let x = true } //│ ╙── ^^^^^^^^ //│ class Der extends Base { //│ constructor() //│ let x: true //│ } :e module Der extends Base { let x = 0 } //│ ╔══[ERROR] Cannot implement value member 'x' with a let binding //│ ║ l.67: module Der extends Base { let x = 0 } //│ ║ ^^^^^ //│ ╟── Originally declared here: //│ ║ l.24: abstract class Base { val x: Int } //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `Der` //│ ║ l.67: module Der extends Base { let x = 0 } //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.24: abstract class Base { val x: Int } //│ ║ ^^^^^^^^^^ //│ ╟── Note: value member `x` is private and cannot be used as a valid implementation //│ ║ l.67: module Der extends Base { let x = 0 } //│ ╙── ^^^^^ //│ module Der extends Base { //│ let x: 0 //│ } :e Der.x //│ ╔══[ERROR] Let binding 'x' cannot tbe accessed as a field //│ ║ l.88: Der.x //│ ║ ^^ //│ ╟── Use a `val` declaration to make it a field //│ ║ l.67: module Der extends Base { let x = 0 } //│ ╙── ^^^^^ //│ 0 | error //│ res //│ = undefined (Der : Base).x //│ Int //│ res //│ = undefined module M { val x = 1 fun foo = x // foo, () } M.x M.foo //│ module M { //│ fun foo: 1 //│ val x: 1 //│ } //│ 1 //│ res //│ = 1 //│ res //│ = 1 // * TODO init checking module M { val x = foo fun foo = x // foo, () } M.x M.foo //│ module M { //│ fun foo: nothing //│ val x: nothing //│ } //│ nothing //│ res //│ = undefined //│ res //│ = undefined module M { let x = foo fun foo = 1 // foo, () } M.foo //│ module M { //│ fun foo: 1 //│ let x: 1 //│ } //│ 1 //│ res //│ = 1 // * TODO init checking module M { let x = foo fun foo = x // foo, () } M.foo //│ module M { //│ fun foo: nothing //│ let x: nothing //│ } //│ nothing //│ res //│ = undefined :e M.x //│ ╔══[ERROR] Let binding 'x' cannot tbe accessed as a field //│ ║ l.171: M.x //│ ║ ^^ //│ ╟── Use a `val` declaration to make it a field //│ ║ l.157: let x = foo //│ ╙── ^^^^^^^ //│ error //│ res //│ = undefined module M { let x = 1 let y = foo fun foo = x fun bar = y // foo, () } M.foo M.bar //│ module M { //│ fun bar: 1 //│ fun foo: 1 //│ let x: 1 //│ let y: 1 //│ } //│ 1 //│ res //│ = 1 //│ res //│ = 1 // * TODO init checking module M { let y = foo let x = 1 fun foo = x fun bar = y // foo, () } M.foo M.bar //│ module M { //│ fun bar: 1 //│ fun foo: 1 //│ let x: 1 //│ let y: 1 //│ } //│ 1 //│ res //│ = 1 //│ res //│ = undefined // * TODO init checking module M { let x = 1 let y = foo val foo = x val bar = y // foo, () } M.foo M.bar //│ module M { //│ val bar: 1 //│ val foo: 1 //│ let x: 1 //│ let y: 1 //│ } //│ 1 //│ res //│ = 1 //│ res //│ = undefined abstract class Foo { let size: Int = 1 fun get = size + 1 } //│ abstract class Foo { //│ fun get: Int //│ let size: Int //│ } :e abstract class Foo { let size: Int fun get = size + 1 } //│ ╔══[ERROR] `let` bindings must have a right-hand side //│ ║ l.261: let size: Int //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: size //│ ║ l.262: fun get = size + 1 //│ ╙── ^^^^ //│ abstract class Foo { //│ fun get: Int //│ } //│ Code generation encountered an error: //│ unresolved symbol size :e abstract class Foo { let size: Int = 1 fun get = this.size + 1 } //│ ╔══[ERROR] Type `#Foo` does not contain member `size` //│ ║ l.279: fun get = this.size + 1 //│ ╙── ^^^^^ //│ abstract class Foo { //│ fun get: Int //│ let size: Int //│ } module F extends Foo //│ module F extends Foo { //│ fun get: Int //│ let size: Int //│ } :e F.size //│ ╔══[ERROR] Let binding 'size' cannot tbe accessed as a field //│ ║ l.296: F.size //│ ║ ^^^^^ //│ ╟── Use a `val` declaration to make it a field //│ ║ l.278: let size: Int = 1 //│ ╙── ^^^^^^^^^^^^^ //│ Int | error //│ res //│ = undefined module F extends Foo { val size = 123 } //│ module F extends Foo { //│ fun get: Int //│ val size: 123 //│ } F.get //│ Int //│ res //│ = 124 F.size //│ 123 //│ res //│ = 123 module F extends Foo { fun size = 123 } //│ module F extends Foo { //│ fun get: Int //│ fun size: 123 //│ } F.get //│ Int //│ res //│ = 124 F.size //│ 123 //│ res //│ = 123 module F extends Foo { let size = 123 } //│ module F extends Foo { //│ fun get: Int //│ let size: 123 //│ } // * Garbage result since `Foo` is ill-typed F.get //│ Int //│ res //│ = NaN :e F.size //│ ╔══[ERROR] Let binding 'size' cannot tbe accessed as a field //│ ║ l.352: F.size //│ ║ ^^^^^ //│ ╟── Use a `val` declaration to make it a field //│ ║ l.339: module F extends Foo { let size = 123 } //│ ╙── ^^^^^^^^^^ //│ 123 | error //│ res //│ = undefined class Foo(x: Int) { fun get0 = x } //│ class Foo(x: Int) { //│ fun get0: Int //│ } :e class Bar extends Foo(123) { fun get2 = this.x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.370: class Bar extends Foo(123) { fun get2 = this.x } //│ ╙── ^^ //│ class Bar extends Foo { //│ constructor() //│ fun get0: Int //│ fun get2: error //│ } :e module Bar extends Foo(123) { val x: Int; fun get2 = this.x } //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `Bar` //│ ║ l.381: module Bar extends Foo(123) { val x: Int; fun get2 = this.x } //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.381: module Bar extends Foo(123) { val x: Int; fun get2 = this.x } //│ ║ ^^^^^^^^^^ //│ ╟── Note: value member `x` is private and cannot be used as a valid implementation //│ ║ l.364: class Foo(x: Int) { fun get0 = x } //│ ╙── ^ //│ module Bar extends Foo { //│ fun get0: Int //│ fun get2: Int //│ val x: Int //│ } Bar.x //│ Int //│ res //│ = undefined Bar.get0 //│ Int //│ res //│ = 123 Bar.get2 //│ Int //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/nu/BadMixins.mls ================================================ :NewDefs :e mixin M0 M0 //│ ╔══[ERROR] mixin M0 cannot be used in term position //│ ║ l.6: M0 //│ ╙── ^^ //│ mixin M0() //│ error //│ res //│ = [Function (anonymous)] :e M0 //│ ╔══[ERROR] mixin M0 cannot be used in term position //│ ║ l.16: M0 //│ ╙── ^^ //│ error //│ res //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/nu/BadScopes.mls ================================================ :NewDefs :e mixin Foo(x: Int) x //│ ╔══[ERROR] identifier not found: x //│ ║ l.6: x //│ ╙── ^ //│ mixin Foo(x: Int) //│ error //│ Code generation encountered an error: //│ unresolved symbol x :e class Foo(x: Int) x //│ ╔══[ERROR] identifier not found: x //│ ║ l.18: x //│ ╙── ^ //│ class Foo(x: Int) //│ error //│ Code generation encountered an error: //│ unresolved symbol x :e class Bar { x } //│ ╔══[ERROR] identifier not found: x //│ ║ l.29: class Bar { x } //│ ╙── ^ //│ class Bar { //│ constructor() //│ } //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: shared/src/test/diff/nu/BadSignatures.mls ================================================ :NewDefs :e trait T { fun x : Int fun x = false } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.7: fun x = false //│ ╙── ^^^^^^^^^^^^^ //│ trait T { //│ fun x: Int //│ } class A { virtual fun x = 1 } //│ class A { //│ constructor() //│ fun x: 1 //│ } :e class B() extends A { fun x: Int } //│ ╔══[ERROR] Type mismatch in signature of member `x`: //│ ║ l.25: fun x: Int //│ ║ ^^^^^^ //│ ╟── type `Int` does not match type `1` //│ ║ l.25: fun x: Int //│ ║ ^^^ //│ ╟── but it flows into signature of member `x` with expected type `1` //│ ║ l.25: fun x: Int //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.17: class A { virtual fun x = 1 } //│ ║ ^ //│ ╟── from definition of method x: //│ ║ l.17: class A { virtual fun x = 1 } //│ ╙── ^^^^^ //│ class B() extends A { //│ fun x: Int //│ } B().x //│ Int //│ res //│ = 1 (B() : A).x //│ 1 //│ res //│ = 1 class C() extends B { fun x = 0 } //│ class C() extends A, B { //│ fun x: 0 //│ } (C() : A).x //│ 1 //│ res //│ = 0 :e class B() extends A { fun x: Int fun x = 1 } //│ ╔══[ERROR] Type mismatch in signature of member `x`: //│ ║ l.68: fun x: Int //│ ║ ^^^^^^ //│ ╟── type `Int` does not match type `1` //│ ║ l.68: fun x: Int //│ ║ ^^^ //│ ╟── but it flows into signature of member `x` with expected type `1` //│ ║ l.68: fun x: Int //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.17: class A { virtual fun x = 1 } //│ ║ ^ //│ ╟── from definition of method x: //│ ║ l.17: class A { virtual fun x = 1 } //│ ╙── ^^^^^ //│ class B() extends A { //│ fun x: Int //│ } :e mixin M { fun x : Int } //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `M` //│ ║ l.92: mixin M { fun x : Int } //│ ║ ^ //│ ╟── Declared here: //│ ║ l.92: mixin M { fun x : Int } //│ ╙── ^^^^^^^^^^^ //│ mixin M() { //│ fun x: Int //│ } ================================================ FILE: shared/src/test/diff/nu/BadSuper.mls ================================================ :NewDefs mixin M0 { fun f = 42 } //│ mixin M0() { //│ fun f: 42 //│ } :e mixin M1 { fun g = super } //│ ╔══[ERROR] Illegal use of `super` //│ ║ l.13: fun g = super //│ ╙── ^^^^^ //│ mixin M1() { //│ super: 'super //│ fun g: 'super //│ } //│ Syntax error: //│ 'super' keyword unexpected here :re module C0 extends M0, M1 C0.g //│ module C0 { //│ fun f: 42 //│ fun g: {f: 42} //│ } //│ {f: 42} //│ res //│ Runtime error: //│ ReferenceError: M1 is not defined :e class Foo { fun f = super } //│ ╔══[ERROR] Illegal use of `super` //│ ║ l.40: fun f = super //│ ╙── ^^^^^ //│ class Foo { //│ constructor() //│ fun f: Foo //│ } //│ Syntax error: //│ 'super' keyword unexpected here ================================================ FILE: shared/src/test/diff/nu/BadTraits.mls ================================================ :NewDefs trait Foo //│ trait Foo :e Foo //│ ╔══[ERROR] trait Foo cannot be used in term position //│ ║ l.8: Foo //│ ╙── ^^^ //│ error //│ Code generation encountered an error: //│ trait used in term position ================================================ FILE: shared/src/test/diff/nu/BadUCS.mls ================================================ :NewDefs class Foo //│ class Foo { //│ constructor() //│ } fun foo(x) = if x is Foo then 0 //│ fun foo: Foo -> 0 module Bar { class Foo0 } //│ module Bar { //│ class Foo0 { //│ constructor() //│ } //│ } fun foo(x) = if x is Bar then 0 //│ fun foo: Bar -> 0 :e :ge fun foo(x) = if x is Foo0 then 0 //│ ╔══[ERROR] type identifier `Foo0` not found //│ ║ l.27: fun foo(x) = if x is Foo0 then 0 //│ ╙── ^^^^ //│ ╔══[ERROR] type identifier not found: Foo0 //│ ║ l.27: fun foo(x) = if x is Foo0 then 0 //│ ╙── ^^^^ //│ fun foo: nothing -> error //│ Code generation encountered an error: //│ unknown match case: Foo0 type F = Foo //│ type F = Foo :e :ge fun foo(x) = if x is F then 0 //│ ╔══[ERROR] Type alias is not allowed in pattern //│ ║ l.44: fun foo(x) = if x is F then 0 //│ ╙── ^ //│ ╔══[ERROR] can only match on classes and traits //│ ║ l.44: fun foo(x) = if x is F then 0 //│ ╙── ^ //│ fun foo: nothing -> error //│ Code generation encountered an error: //│ cannot match type alias F :e :ge fun foo(x) = if x is F() then 0 //│ ╔══[ERROR] Type alias is not allowed in pattern //│ ║ l.57: fun foo(x) = if x is F() then 0 //│ ╙── ^ //│ ╔══[ERROR] can only match on classes and traits //│ ║ l.57: fun foo(x) = if x is F() then 0 //│ ╙── ^ //│ fun foo: nothing -> error //│ Code generation encountered an error: //│ cannot match type alias F mixin M //│ mixin M() :e :ge fun foo(x) = if x is M then 0 //│ ╔══[ERROR] Mixins are not allowed in pattern //│ ║ l.74: fun foo(x) = if x is M then 0 //│ ╙── ^ //│ ╔══[ERROR] can only match on classes and traits //│ ║ l.74: fun foo(x) = if x is M then 0 //│ ╙── ^ //│ fun foo: nothing -> error //│ Code generation encountered an error: //│ unknown match case: M :e :ge fun foo(x) = if x is M() then 0 //│ ╔══[ERROR] Mixins are not allowed in pattern //│ ║ l.87: fun foo(x) = if x is M() then 0 //│ ╙── ^ //│ ╔══[ERROR] can only match on classes and traits //│ ║ l.87: fun foo(x) = if x is M() then 0 //│ ╙── ^ //│ fun foo: nothing -> error //│ Code generation encountered an error: //│ unknown match case: M :w fun foo0(x, y) = if x is y then 0 //│ ╔══[WARNING] the outer binding `y` //│ ║ l.99: fun foo0(x, y) = if x is y then 0 //│ ║ ^ //│ ╟── is shadowed by name pattern `y` //│ ║ l.99: fun foo0(x, y) = if x is y then 0 //│ ╙── ^ //│ fun foo0: (anything, anything) -> 0 fun foo = 0 //│ fun foo: 0 :e :ge fun foo0(x) = if x is foo() then 0 //│ ╔══[ERROR] type identifier `foo` not found //│ ║ l.114: fun foo0(x) = if x is foo() then 0 //│ ╙── ^^^ //│ ╔══[ERROR] can only match on classes and traits //│ ║ l.114: fun foo0(x) = if x is foo() then 0 //│ ╙── ^^^ //│ fun foo0: nothing -> error //│ Code generation encountered an error: //│ unknown match case: foo :e :ge // FIXME: Typer.scala:1497 fun foo(x) = if x is foo() then 0 //│ ╔══[ERROR] type identifier `foo` not found //│ ║ l.128: fun foo(x) = if x is foo() then 0 //│ ╙── ^^^ //│ ╔══[ERROR] can only match on classes and traits //│ ║ l.128: fun foo(x) = if x is foo() then 0 //│ ╙── ^^^ //│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Program reached an unexpected state. module Nil class Cons[out A](head: A, tail: Cons[A] | Nil) //│ module Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) fun join(xs) = if xs is Nil then "" Cons(x, Nil) then toString(x) Cons(x, xs') then concat(toString(x))(concat(", ")(join(xs'))) //│ fun join: (Cons[anything] | Nil) -> Str ================================================ FILE: shared/src/test/diff/nu/BasicClassInheritance.mls ================================================ :NewDefs class A //│ class A { //│ constructor() //│ } class B(m: Int) extends A //│ class B(m: Int) extends A class A(n: Int) //│ class A(n: Int) class B(m: Int) extends A(m + 1) //│ class B(m: Int) extends A class A { fun a1: Int fun a1 = 1 fun a2 = 2 } //│ class A { //│ constructor() //│ fun a1: Int //│ fun a2: 2 //│ } class B extends A //│ class B extends A { //│ constructor() //│ fun a1: Int //│ fun a2: 2 //│ } // * Interestingly, we can currently inherit from modules... module C { fun test = 0 } //│ module C { //│ fun test: 0 //│ } class D() extends C //│ class D() extends C { //│ fun test: 0 //│ } D().test //│ 0 //│ res //│ = 0 class E(val m: Int) extends A { constructor(a: Int, b: Int) { m = a + b log of concat("Here's m: ")(toString of m) } } //│ class E(m: Int) extends A { //│ constructor(a: Int, b: Int) //│ fun a1: Int //│ fun a2: 2 //│ } :e E(1).m //│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword //│ ║ l.70: E(1).m //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.70: E(1).m //│ ║ ^^^^ //│ ╟── argument of type `[1]` does not match type `[a: Int, b: Int]` //│ ║ l.70: E(1).m //│ ╙── ^^^ //│ Int | error //│ res //│ = NaN //│ // Output //│ Here's m: NaN (new E(1, 2)).m //│ Int //│ res //│ = 3 //│ // Output //│ Here's m: 3 new E(1, 2).m //│ Int //│ res //│ = 3 //│ // Output //│ Here's m: 3 if new E(1, 2) is E(x) then x //│ Int //│ res //│ = 3 //│ // Output //│ Here's m: 3 :e module F extends E //│ ╔══[ERROR] class E expects 2 parameter(s); got 0 //│ ║ l.108: module F extends E //│ ╙── ^ //│ module F extends A, E { //│ fun a1: Int //│ fun a2: 2 //│ } :e module F extends E(123) //│ ╔══[ERROR] class E expects 2 parameter(s); got 1 //│ ║ l.118: module F extends E(123) //│ ╙── ^^^^^ //│ module F extends A, E { //│ fun a1: Int //│ fun a2: 2 //│ } module F extends E(123, 456) //│ module F extends A, E { //│ fun a1: Int //│ fun a2: 2 //│ } // * Note: strangely, we see here the ctor output from the previous definitions of the F module 🤔 F.m //│ Int //│ res //│ = 579 //│ // Output //│ Here's m: NaN //│ Here's m: NaN //│ Here's m: 579 class G(x: Int) extends E(x, x + 1) //│ class G(x: Int) extends A, E { //│ fun a1: Int //│ fun a2: 2 //│ } G(123).m //│ Int //│ res //│ = 247 //│ // Output //│ Here's m: 247 :e // TODO support class H extends E { constructor(a: Int, b: Int) { super(a, b) } } //│ ╔══[ERROR] class E expects 2 parameter(s); got 0 //│ ║ l.159: class H extends E { //│ ╙── ^ //│ ╔══[ERROR] Illegal use of `super` //│ ║ l.161: super(a, b) //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: super //│ ║ l.161: super(a, b) //│ ╙── ^^^^^ //│ class H extends A, E { //│ constructor(a: Int, b: Int) //│ fun a1: Int //│ fun a2: 2 //│ } :re new H(111, 222) //│ H //│ res //│ Runtime error: //│ ReferenceError: Super constructor may only be called once //│ // Output //│ Here's m: NaN //│ Here's m: 333 ================================================ FILE: shared/src/test/diff/nu/BasicClasses.mls ================================================ :NewDefs class A(val n: Int) //│ class A(n: Int) A //│ (n: Int) -> A //│ res //│ = [Function (anonymous)] { //│ class: [class A], //│ unapply: [Function: unapply] //│ } let a = A(42) //│ let a: A //│ a //│ = A {} a.n //│ Int //│ res //│ = 42 fun f(x: A) = x.n //│ fun f: (x: A) -> Int fun f(x: A) = if x is A then x.n //│ fun f: (x: A) -> Int fun f(x: A | 'b) = if x is A then x.n else 0 //│ fun f: (x: Object) -> Int fun f(x) = x.n //│ fun f: forall 'n. {n: 'n} -> 'n f(a) //│ Int //│ res //│ = 42 fun f(x) = if x is A then x.n //│ fun f: A -> Int f(a) //│ Int //│ res //│ = 42 fun f(x) = if x is A then x.n else 0 //│ fun f: Object -> Int f(a) //│ Int //│ res //│ = 42 class C { fun id(x) = x fun const(x) = id } //│ class C { //│ constructor() //│ fun const: anything -> (forall 'a. 'a -> 'a) //│ fun id: forall 'a. 'a -> 'a //│ } // TODO class Base0(val n) { fun me = this fun my = this.n fun mine = my fun oops = this.my } //│ ╔══[ERROR] Class parameters currently need type annotations //│ ║ l.74: class Base0(val n) { //│ ╙── ^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.78: fun oops = this.my //│ ╙── ^^^ //│ class Base0(n: error) { //│ fun me: Base0 & {n: error} //│ fun mine: error //│ fun my: error //│ fun oops: error //│ } // :d // Base0 let b1 = Base0(42) //│ let b1: Base0 //│ b1 //│ = Base0 {} let n1 = b1.n //│ let n1: error //│ n1 //│ = 42 // TODO n1 + 1 //│ Int //│ res //│ = 43 let b2 = Base0("hi") let n2 = b2.n //│ let b2: Base0 //│ let n2: error //│ b2 //│ = Base0 {} //│ n2 //│ = 'hi' class Base1(val base: Int) { fun getBase1 = base fun getBase2 = this.base fun foo(x) = this.base + x } //│ class Base1(base: Int) { //│ fun foo: Int -> Int //│ fun getBase1: Int //│ fun getBase2: Int //│ } class Base1(val base: Int) { fun getBase1 = base fun me = this fun foo(x) = base + x } //│ class Base1(base: Int) { //│ fun foo: Int -> Int //│ fun getBase1: Int //│ fun me: Base1 & {base: Int} //│ } Base1 //│ (base: Int) -> Base1 //│ res //│ = [Function (anonymous)] { //│ class: [class Base1], //│ unapply: [Function: unapply] //│ } let b = Base1(1) //│ let b: Base1 //│ b //│ = Base1 {} b.base //│ Int //│ res //│ = 1 b.getBase1 //│ Int //│ res //│ = 1 // :d b.me //│ Base1 & {base: Int} //│ res //│ = Base1 {} :e b.getBaseTypo //│ ╔══[ERROR] Type `Base1` does not contain member `getBaseTypo` //│ ║ l.176: b.getBaseTypo //│ ╙── ^^^^^^^^^^^^ //│ error //│ res //│ = undefined b : Base1 //│ Base1 //│ res //│ = Base1 {} class Rec(val n: Int) { fun go = Rec(n + 1) } //│ class Rec(n: Int) { //│ fun go: Rec //│ } let r = Rec(0) r.n //│ let r: Rec //│ Int //│ r //│ = Rec {} //│ res //│ = 0 r.go.n //│ Int //│ res //│ = 1 // TODO treat `a: Int` as a signature class Annots(base: 0 | 1) { a: Int fun a = base } //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in type ascription: //│ ║ l.217: a: Int //│ ║ ^ //│ ╟── type `Int` does not match type `()` //│ ║ l.217: a: Int //│ ║ ^^^ //│ ╟── but it flows into expression in statement position with expected type `()` //│ ║ l.217: a: Int //│ ╙── ^ //│ class Annots(base: 0 | 1) { //│ fun a: 0 | 1 //│ } ================================================ FILE: shared/src/test/diff/nu/BasicMixins.mls ================================================ :NewDefs mixin Base { val base = 42 } //│ mixin Base() { //│ val base: 42 //│ } mixin BaseTest { fun test = super.base } //│ mixin BaseTest() { //│ super: {base: 'base} //│ fun test: 'base //│ } module Base0 extends Base, BaseTest //│ module Base0 { //│ val base: 42 //│ fun test: 42 //│ } Base0 //│ Base0 //│ res //│ = Base0 { class: [Function: Base0] } Base0.test //│ 42 //│ res //│ = 42 :e class Base1(val base: Int) extends BaseTest { fun test2 = [base, this.base] } //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.38: class Base1(val base: Int) extends BaseTest { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.39: fun test2 = [base, this.base] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.40: } //│ ║ ^ //│ ╟── Object of type `anything` does not have field 'base' //│ ║ l.38: class Base1(val base: Int) extends BaseTest { //│ ║ ^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.12: fun test = super.base //│ ║ ^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.12: fun test = super.base //│ ╙── ^^^^^ //│ class Base1(base: Int) { //│ fun test: nothing //│ fun test2: [Int, Int] //│ } mixin BaseOf(val base: Int) { fun original = base } //│ mixin BaseOf(base: Int) { //│ fun original: Int //│ } mixin BaseInc { fun base = super.base + 1 fun test2 = [this.original, this.base] } //│ mixin BaseInc() { //│ super: {base: Int} //│ this: {base: 'base, original: 'original} //│ fun base: Int //│ fun test2: ['original, 'base] //│ } class Base2(x: Int) extends BaseOf(x + 1), BaseTest, BaseInc { fun base = x fun overridden = super.base } //│ class Base2(x: Int) { //│ fun base: Int //│ fun original: Int //│ fun overridden: Int //│ fun test: Int //│ fun test2: [Int, Int] //│ } Base2(11).base //│ Int //│ res //│ = 11 Base2(11).overridden //│ Int //│ res //│ = 13 Base2(11).test //│ Int //│ res //│ = 12 Base2(11).test2 //│ [Int, Int] //│ res //│ = [ 12, 11 ] :e // TODO class Base2(x) extends BaseOf(x + 1), BaseTest //│ ╔══[ERROR] Class parameters currently need type annotations //│ ║ l.116: class Base2(x) extends BaseOf(x + 1), BaseTest //│ ╙── ^ //│ class Base2(x: error) { //│ fun original: Int //│ fun test: Int //│ } :w :e class Base1(x: Int): BaseTest //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.127: class Base1(x: Int): BaseTest //│ ║ ^^^^^^^^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ ╔══[ERROR] mixin BaseTest cannot be used as a type //│ ║ l.127: class Base1(x: Int): BaseTest //│ ╙── ^^^^^^^^ //│ class Base1(x: Int): BaseTest Base1 //│ (x: Int) -> Base1 //│ res //│ = [Function (anonymous)] { //│ class: [class Base1], //│ unapply: [Function: unapply] //│ } :e 1 : BaseTest //│ ╔══[ERROR] mixin BaseTest cannot be used as a type //│ ║ l.146: 1 : BaseTest //│ ╙── ^^^^^^^^ //│ BaseTest //│ res //│ = 1 :e error : BaseTest //│ ╔══[ERROR] mixin BaseTest cannot be used as a type //│ ║ l.155: error : BaseTest //│ ╙── ^^^^^^^^ //│ BaseTest //│ res //│ Runtime error: //│ Error: an error was thrown // :ns mixin Foo { fun test(x) = [super.base + x, x, super.misc] } //│ mixin Foo() { //│ super: {base: Int, misc: 'misc} //│ fun test: (Int & 'a) -> [Int, 'a, 'misc] //│ } :e module Base1(base: Int, misc: string) extends Foo //│ ╔══[ERROR] Module parameters are not supported //│ ║ l.175: module Base1(base: Int, misc: string) extends Foo //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.175: module Base1(base: Int, misc: string) extends Foo //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Object of type `anything` does not have field 'misc' //│ ║ l.175: module Base1(base: Int, misc: string) extends Foo //│ ║ ^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] //│ ║ ^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.175: module Base1(base: Int, misc: string) extends Foo //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Object of type `anything` does not have field 'base' //│ ║ l.175: module Base1(base: Int, misc: string) extends Foo //│ ║ ^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] //│ ║ ^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.167: fun test(x) = [super.base + x, x, super.misc] //│ ╙── ^^^^^ //│ module Base1(base: Int, misc: string) { //│ fun test: forall 'a. (Int & 'a) -> [Int, 'a, nothing] //│ } :e Base1.test //│ ╔══[ERROR] Parameterized modules are not yet supported //│ ║ l.208: Base1.test //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.208: Base1.test //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `(base: Int, misc: string) -> Base1` does not have field 'test' //│ ║ l.208: Base1.test //│ ╙── ^^^^^ //│ error //│ res //│ = [Function: test] mixin WrapBase { // fun wrap(x) = x // fun wrap(x) = x : Int fun wrapA(x: Int) = x : Int fun wrap(x) = x } //│ mixin WrapBase() { //│ fun wrap: 'a -> 'a //│ fun wrapA: (x: Int) -> Int //│ } // :d mixin Wrap { fun wrapA(x) = [super.wrapA(x)] fun wrap(x) = [super.wrap(x)] } //│ mixin Wrap() { //│ super: {wrap: 'a -> 'b, wrapA: 'c -> 'd} //│ fun wrap: 'a -> ['b] //│ fun wrapA: 'c -> ['d] //│ } // :d module WrapBase1 extends WrapBase, Wrap //│ module WrapBase1 { //│ fun wrap: 'a -> ['a] //│ fun wrapA: Int -> [Int] //│ } WrapBase1 //│ WrapBase1 //│ res //│ = WrapBase1 { class: [Function: WrapBase1] } // :d WrapBase1.wrapA //│ Int -> [Int] //│ res //│ = [Function: wrapA] WrapBase1.wrap //│ 'a -> ['a] //│ res //│ = [Function: wrap] // :d // WrapBase1.wrap WrapBase1.wrap(1) //│ [1] //│ res //│ = [ 1 ] WrapBase1.wrap("ok") //│ ["ok"] //│ res //│ = [ 'ok' ] WrapBase1.wrapA(1) //│ [Int] //│ res //│ = [ 1 ] :e WrapBase1.wrapA("ok") //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.292: WrapBase1.wrapA("ok") //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"ok"` is not an instance of type `Int` //│ ║ l.292: WrapBase1.wrapA("ok") //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.236: fun wrapA(x) = [super.wrapA(x)] //│ ╙── ^ //│ error | [Int] //│ res //│ = [ 'ok' ] module WrapBase2 extends WrapBase, Wrap, Wrap, Wrap //│ module WrapBase2 { //│ fun wrap: 'a -> [[['a]]] //│ fun wrapA: Int -> [[[Int]]] //│ } let w = WrapBase2.wrap //│ let w: 'a -> [[['a]]] //│ w //│ = [Function: wrap] let wd = w(1) //│ let wd: [[[1]]] //│ wd //│ = [ [ [ 1 ] ] ] wd.0.0.0 + 1 //│ Int //│ res //│ = 2 ================================================ FILE: shared/src/test/diff/nu/CallByName.mls ================================================ :NewDefs let x = log("ok") //│ let x: () //│ x //│ = undefined //│ // Output //│ ok x //│ () //│ res //│ = undefined x //│ () //│ res //│ = undefined fun x = log("ok") //│ fun x: () x //│ () //│ res //│ = undefined //│ // Output //│ ok x //│ () //│ res //│ = undefined //│ // Output //│ ok ================================================ FILE: shared/src/test/diff/nu/CaseExpr.mls ================================================ :NewDefs case 0 then true //│ 0 -> true //│ res //│ = [Function: res] :pe // TODO support case 0 then true, 1 then false //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.10: case 0 then true, 1 then false //│ ╙── ^^^^^^^^^^^^ //│ 0 -> () //│ res //│ = [Function: res] case 0 then true 1 then false //│ (0 | 1) -> Bool //│ res //│ = [Function: res] fun foo = case 0 then true 1 then false //│ fun foo: (0 | 1) -> Bool [foo(0), foo(1)] //│ [Bool, Bool] //│ res //│ = [ true, false ] abstract class Option[out A]: Some[A] | None class Some[out A](val value: A) extends Option[A] module None extends Option[nothing] //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun map(f) = case Some(x) then Some(f(x)) None then None //│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) map(succ) of Some of 123 //│ None | Some[Int] //│ res //│ = Some {} map(succ) of None //│ None | Some[Int] //│ res //│ = None { class: [class None extends Option] } fun map(f) = case Some(x) then Some(f(x)) None as n then n //│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) :pe case 1 //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead //│ ║ l.70: case 1 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: //│ ║ l.70: case 1 //│ ╙── ^^^^ //│ anything -> 1 //│ res //│ = [Function: res] :pe case (1 then true) //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.82: case (1 then true) //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead //│ ║ l.82: case (1 then true) //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: //│ ║ l.82: case (1 then true) //│ ╙── ^^^^ //│ anything -> 1 //│ res //│ = [Function: res] case else 0 //│ anything -> 0 //│ res //│ = [Function: res] :pe case then 1 else 0 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position //│ ║ l.102: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found integer literal instead //│ ║ l.102: case then 1 else 0 //│ ║ ^ //│ ╟── Note: 'case' expression starts here: //│ ║ l.102: case then 1 else 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead //│ ║ l.102: case then 1 else 0 //│ ╙── ^^^^ //│ anything -> 1 //│ res //│ = [Function: res] // TODO: :pe :e case x, y then x + y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.125: case x, y then x + y //│ ╙── ^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'case'; found operator application instead //│ ║ l.125: case x, y then x + y //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── Note: 'case' expression starts here: //│ ║ l.125: case x, y then x + y //│ ╙── ^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.125: case x, y then x + y //│ ╙── ^ //│ anything -> () //│ Code generation encountered an error: //│ unresolved symbol x :e case (x, y) then x + y //│ ╔══[ERROR] type identifier `,` not found //│ ║ l.143: case (x, y) then x + y //│ ╙── ^^^^ //│ ╔══[ERROR] type identifier not found: , //│ ║ l.143: case (x, y) then x + y //│ ╙── ^^^^ //│ nothing -> error //│ Code generation encountered an error: //│ unresolved symbol , let foo = case [x, y] then x + y //│ let foo: {0: Int, 1: Int} -> Int //│ foo //│ = [Function: foo1] foo([1, 2]) //│ Int //│ res //│ = 3 ================================================ FILE: shared/src/test/diff/nu/ClassField.mls ================================================ :NewDefs class Foo(x: Int) //│ class Foo(x: Int) Foo //│ (x: Int) -> Foo //│ res //│ = [Function (anonymous)] { //│ class: [class Foo], //│ unapply: [Function: unapply] //│ } typeof(Foo) //│ Str //│ res //│ = 'function' let f = Foo(123) //│ let f: Foo //│ f //│ = Foo {} typeof(f) //│ Str //│ res //│ = 'object' :e let cls = Foo.class //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.32: let cls = Foo.class //│ ║ ^^^^^^^^^ //│ ╟── reference of type `(x: Int) -> Foo` does not have field 'class' //│ ║ l.32: let cls = Foo.class //│ ╙── ^^^ //│ let cls: error //│ cls //│ = [class Foo] typeof(cls) //│ Str //│ res //│ = 'function' mixin Base //│ mixin Base() class Derived() extends Base //│ class Derived() // * Strangely, we now get `{ class: [Function: Derived] }` Derived //│ () -> Derived //│ res //│ = [Function (anonymous)] { //│ class: [Function: Derived], //│ unapply: [Function: unapply] //│ } :e let cls = Derived.class //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.66: let cls = Derived.class //│ ║ ^^^^^^^^^^^^^ //│ ╟── reference of type `() -> Derived` does not have field 'class' //│ ║ l.66: let cls = Derived.class //│ ╙── ^^^^^^^ //│ let cls: error //│ cls //│ = [Function: Derived] typeof(cls) //│ Str //│ res //│ = 'function' ================================================ FILE: shared/src/test/diff/nu/ClassInstantiation.mls ================================================ // *** Class instantiation tests *** // :NewDefs class C //│ class C { //│ constructor() //│ } new C //│ C //│ res //│ = C {} // * TODO decide: forbid? new C() //│ C //│ res //│ = C {} :e C() //│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword //│ ║ l.23: C() //│ ╙── ^ //│ C //│ res //│ Runtime error: //│ TypeError: Class constructor C cannot be invoked without 'new' class D(x: Int) //│ class D(x: Int) :js D(0) //│ D //│ // Prelude //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 //│ res = D(0); //│ // End of generated code //│ res //│ = D {} // * TODO decide: reject or accept? :js new D(0) //│ D //│ // Prelude //│ class TypingUnit6 {} //│ const typing_unit6 = new TypingUnit6; //│ // Query 1 //│ res = new D.class(0); //│ // End of generated code //│ res //│ = D {} ================================================ FILE: shared/src/test/diff/nu/ClassSignatures.mls ================================================ :NewDefs abstract class C0: C1 | C2 class C1() extends C0 module C2 extends C0 //│ abstract class C0: C1 | C2 //│ class C1() extends C0 //│ module C2 extends C0 fun foo(x: C0) = if x is C1 then 1 C2 then 2 //│ fun foo: (x: C0) -> (1 | 2) fun foo(x: C0) = x : C1 | C2 //│ fun foo: (x: C0) -> (C1 | C2) fun foo(x) = if x is C1 then 1 C2 then 2 //│ fun foo: (C1 | C2) -> (1 | 2) foo(C1()) + foo(C2) //│ Int //│ res //│ = 3 :e class C3 extends C0 //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.31: class C3 extends C0 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#C3` does not match type `C1 | C2` //│ ╟── Note: constraint arises from union type: //│ ║ l.4: abstract class C0: C1 | C2 //│ ╙── ^^^^^^^ //│ class C3 extends C0 { //│ constructor() //│ } trait B //│ trait B :w :e class A(): B //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.49: class A(): B //│ ║ ^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.49: class A(): B //│ ║ ^^^^^^^^^ //│ ╟── expression of type `#A` is not an instance of type `B` //│ ╟── Note: constraint arises from type reference: //│ ║ l.49: class A(): B //│ ╙── ^ //│ class A(): B A() : B //│ B //│ res //│ = A {} abstract class A(): B //│ abstract class A(): B :e A() : B //│ ╔══[ERROR] Class A is abstract and cannot be instantiated //│ ║ l.72: A() : B //│ ╙── ^ //│ B //│ res //│ = A {} module C extends A, B //│ module C extends A, B C : B //│ B //│ res //│ = C { class: [class C extends A] } :w :e class A(): C class B() extends A class C() extends B //│ ╔══[WARNING] Self-type annotations have no effects on non-abstract class definitions //│ ║ l.91: class A(): C //│ ║ ^ //│ ╙── Did you mean to use `extends` and inherit from a parent class? //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.91: class A(): C //│ ║ ^^^^^^^^^ //│ ╟── expression of type `#A` is not an instance of type `C` //│ ╟── Note: constraint arises from type reference: //│ ║ l.91: class A(): C //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.92: class B() extends A //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#B` is not an instance of type `C` //│ ╟── Note: constraint arises from type reference: //│ ║ l.91: class A(): C //│ ╙── ^ //│ class A(): C //│ class B() extends A //│ class C() extends A, B :e abstract class A(): C class B() extends A class C() extends B //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.119: class B() extends A //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#B` is not an instance of type `C` //│ ╟── Note: constraint arises from type reference: //│ ║ l.118: abstract class A(): C //│ ╙── ^ //│ abstract class A(): C //│ class B() extends A //│ class C() extends A, B abstract class A(): C abstract class B() extends A class C() extends B //│ abstract class A(): C //│ abstract class B(): C extends A //│ class C() extends A, B // * Transitively-inherited self-types are checked :e class D() extends B //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.144: class D() extends B //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#D` is not an instance of type `C` //│ ╟── Note: constraint arises from type reference: //│ ║ l.133: abstract class A(): C //│ ╙── ^ //│ class D() extends A, B // * Transitively-inherited self-types can be leveraged fun test(x: B): C = x //│ fun test: (x: B) -> C fun test[A](x: B & A): C = x //│ fun test: (x: B) -> C trait T fun test(x: B & T): C = x //│ trait T //│ fun test: (x: B & T) -> C ================================================ FILE: shared/src/test/diff/nu/ClassesInMixins.mls ================================================ :NewDefs mixin Test { class Foo(val n: Int) val f = Foo(123) } //│ mixin Test() { //│ class Foo(n: Int) //│ val f: Foo //│ } module M extends Test //│ module M { //│ class Foo(n: Int) //│ val f: Foo //│ } M.f //│ Foo //│ res //│ = Foo {} M.f.n //│ Int //│ res //│ = 123 :e M.Foo //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.31: M.Foo //│ ╙── ^^^^ //│ error //│ res //│ = [Function (anonymous)] { //│ class: [class Foo], //│ unapply: [Function: unapply] //│ } :e // TODO support fun foo(x) = if x is M.Foo then 1 //│ ╔══[ERROR] unknown pattern (M).Foo //│ ║ l.43: fun foo(x) = if x is M.Foo then 1 //│ ╙── ^^^^^ //│ fun foo: anything -> 1 :e mixin Test2 { let f = Foo(1) } //│ ╔══[ERROR] identifier not found: Foo //│ ║ l.50: mixin Test2 { let f = Foo(1) } //│ ╙── ^^^ //│ mixin Test2() { //│ let f: error //│ } //│ Code generation encountered an error: //│ unresolved symbol Foo :e mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╔══[ERROR] type identifier `Foo` not found //│ ║ l.61: mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╙── ^^^ //│ ╔══[ERROR] type identifier not found: Foo //│ ║ l.61: mixin Test3 { fun f(x) = if x is Foo then 1 } //│ ╙── ^^^ //│ mixin Test3() { //│ fun f: nothing -> error //│ } //│ Code generation encountered an error: //│ unknown match case: Foo :e mixin Test { class Lit(n: Int) class Add(lhs: A, rhs: A) { // Should be a lazy val only forceable when A has the right shape (constrained types?): fun cached = size(this) } fun size(x) = if x is Add(l, r) then this.size(l) + this.size(r) } //│ ╔══[ERROR] Type error in application //│ ║ l.81: fun cached = size(this) //│ ║ ^^^^^^^^^^ //│ ╟── type variable `A` leaks out of its scope //│ ║ l.79: class Add(lhs: A, rhs: A) { //│ ╙── ^ //│ mixin Test() { //│ this: {size: anything -> Int} //│ class Add[A](lhs: A, rhs: A) { //│ fun cached: Int | error //│ } //│ class Lit(n: Int) //│ fun size: Add[??A & 'a] -> Int //│ } ================================================ FILE: shared/src/test/diff/nu/CommaOperator.mls ================================================ :NewDefs 1; 2 //│ 2 //│ res //│ = 1 //│ res //│ = 2 // :dp :js 1, 2 //│ 2 //│ // Prelude //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ res = (1 , 2); //│ // End of generated code //│ res //│ = 2 (1, 2) //│ 2 //│ res //│ = 2 (1, 2, 3) //│ 3 //│ res //│ = 3 1, () //│ () //│ res //│ = undefined log(1), 2 //│ 2 //│ res //│ = 2 //│ // Output //│ 1 (), 1 //│ 1 //│ res //│ = 1 :pe (), 1, //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.55: 1, //│ ╙── ^ //│ () //│ res //│ = undefined (), 1 //│ 1 //│ res //│ = 1 (), log("ok") 1 //│ 1 //│ res //│ = 1 //│ // Output //│ ok :w (), 2 + 2 1 //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in operator application: //│ ║ l.80: 2 + 2 //│ ║ ^^^^^ //│ ╙── operator application of type `Int` does not match type `()` //│ 1 //│ res //│ = 1 (), 2 + 2, () 1 //│ 1 //│ res //│ = 1 let a = 1, 2 //│ let a: 2 //│ a //│ = 2 let a = 1; 2 //│ let a: 1 //│ 2 //│ a //│ = 1 //│ res //│ = 2 :pe // TODO support; make `;` have even less precedence than `,` (1;2) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.115: (1;2) //│ ╙── ^ //│ 1 //│ res //│ = 1 :pe (1,2;3) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.124: (1,2;3) //│ ╙── ^ //│ 2 //│ res //│ = 2 (log(1), 2) //│ 2 //│ res //│ = 2 //│ // Output //│ 1 fun foo(x) = x //│ fun foo: forall 'a. 'a -> 'a :pe foo(1;2) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.145: foo(1;2) //│ ╙── ^ //│ 1 //│ res //│ = 1 foo(let x = 1; x + 1) //│ Int //│ res //│ = 2 :js foo(let x = log(0), 1; log(x), x + 1) //│ Int //│ // Prelude //│ class TypingUnit20 {} //│ const typing_unit20 = new TypingUnit20; //│ // Query 1 //│ res = foo(((x) => (log(x) , x + 1))((log(0) , 1))); //│ // End of generated code //│ res //│ = 2 //│ // Output //│ 0 //│ 1 :js foo((1, 2)) //│ 2 //│ // Prelude //│ class TypingUnit21 {} //│ const typing_unit21 = new TypingUnit21; //│ // Query 1 //│ res = foo((1 , 2)); //│ // End of generated code //│ res //│ = 2 foo(let x = 1; x + 1) //│ Int //│ res //│ = 2 foo(let x = 1 in x + 1) //│ Int //│ res //│ = 2 foo((log(1), 2)) //│ 2 //│ res //│ = 2 //│ // Output //│ 1 foo(1), 2 //│ 2 //│ res //│ = 2 :ge // FIXME let rec x() = x() in x //│ nothing //│ Code generation encountered an error: //│ recursive non-function definition x is not supported :pe let x[T] = 1 in x //│ ╔══[PARSE ERROR] Expected function parameter list; found square bracket section instead //│ ║ l.217: let x[T] = 1 in x //│ ╙── ^^^ //│ 1 //│ res //│ = 1 let x = 2 in log(x), x + 1 //│ Int //│ res //│ = 3 //│ // Output //│ 2 let x = 2; log(x), x + 1 //│ let x: 2 //│ Int //│ x //│ = 2 //│ res //│ = 3 //│ // Output //│ 2 fun foo(x, y) = [x, y] //│ fun foo: forall 'a 'b. ('a, 'b) -> ['a, 'b] foo(1,2) //│ [1, 2] //│ res //│ = [ 1, 2 ] foo(log(1),2) //│ [(), 2] //│ res //│ = [ undefined, 2 ] //│ // Output //│ 1 foo( log(1), 2 ) //│ [(), 2] //│ res //│ = [ undefined, 2 ] //│ // Output //│ 1 foo( log(1), 2, ) //│ [(), 2] //│ res //│ = [ undefined, 2 ] //│ // Output //│ 1 :pe :e foo(log(1);2) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.282: foo(log(1);2) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.282: foo(log(1);2) //│ ║ ^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.282: foo(log(1);2) //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ undefined, undefined ] //│ // Output //│ 1 :e foo((log(1),2)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.302: foo((log(1),2)) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.302: foo((log(1),2)) //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ 2, undefined ] //│ // Output //│ 1 foo((let x = log(0), 1; log(x), x + 1), 2) //│ [Int, 2] //│ res //│ = [ 2, 2 ] //│ // Output //│ 0 //│ 1 :e foo(let x = log(0), 1; log(x), x + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.327: foo(let x = log(0), 1; log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ 2, undefined ] //│ // Output //│ 0 //│ 1 :e foo(let x = log(0), 1 in log(x), x + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.345: foo(let x = log(0), 1 in log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.345: foo(let x = log(0), 1 in log(x), x + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ 2, undefined ] //│ // Output //│ 0 //│ 1 :e foo(let x = log(0), 1; log(x), 1 + 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.363: foo(let x = log(0), 1; log(x), 1 + 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ 2, undefined ] //│ // Output //│ 0 //│ 1 :e foo(if true then 1 else 2, 3) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.381: foo(if true then 1 else 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[1 | ?a]` does not match type `[?b, ?c]` //│ ║ l.381: foo(if true then 1 else 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ 1, undefined ] foo((if true then 1 else 2), 3) //│ [1 | 2, 3] //│ res //│ = [ 1, 3 ] :e foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.401: foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument of type `[?a | ?b]` does not match type `[?c, ?d]` //│ ║ l.401: foo(if true then log("ok"), 1 else log("nok"), 2, 3) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.245: fun foo(x, y) = [x, y] //│ ╙── ^^^^^^ //│ error | [nothing, nothing] //│ res //│ = [ 1, undefined ] //│ // Output //│ ok foo((if true then log("ok"), 1 else log("nok"), 2), 3) //│ [1 | 2, 3] //│ res //│ = [ 1, 3 ] //│ // Output //│ ok ================================================ FILE: shared/src/test/diff/nu/CtorSubtraction.mls ================================================ :NewDefs class Cls //│ class Cls { //│ constructor() //│ } fun x: ('a & Object) -> ('a \ Cls) fun x = case Cls then error y then y //│ fun x: forall 'b. (Cls | Object & 'b & ~#Cls) -> 'b //│ fun x: forall 'a. (Object & 'a) -> ('a & ~Cls) x : Int -> Int //│ Int -> Int //│ res //│ = [Function: x] x : Cls -> nothing //│ Cls -> nothing //│ res //│ = [Function: x] fun x: (Int | Str | Cls) \ Cls fun x = 42 //│ fun x: 42 //│ fun x: Int & ~Cls | Str & ~Cls x : Int | Str //│ Int | Str //│ res //│ = 42 ================================================ FILE: shared/src/test/diff/nu/CycleTypeDefErrors.mls ================================================ :NewDefs // * These type definitions should be checked for cyclicity, like we did in the old frontend, // * but this is currently not implemented. type Foo = Foo //│ type Foo = Foo (f: Foo) => f : Str //│ (f: Foo) -> Str //│ res //│ = [Function: res] type Foo = Foo & Int //│ type Foo = Foo & Int // FIXME (f: Foo) => f : Str //│ /!!!\ Uncaught error: java.lang.StackOverflowError abstract class Foo: Foo //│ abstract class Foo: Foo // FIXME (f: Foo) => f : Str //│ /!!!\ Uncaught error: java.lang.StackOverflowError abstract class Foo[T]: Foo[Int] //│ abstract class Foo[T]: Foo[Int] // FIXME fun test(f: Foo['a]) = f : Str //│ /!!!\ Uncaught error: java.lang.StackOverflowError ================================================ FILE: shared/src/test/diff/nu/Darin.mls ================================================ :NewDefs class Some[out A](val x: A) //│ class Some[A](x: A) let s = Some(Some(42)) //│ let s: Some[Some[42]] //│ s //│ = Some {} // * Expected error: :e s : Some[Int] | () //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.17: s : Some[Int] | () //│ ║ ^ //│ ╟── application of type `Some[?A]` is not an instance of type `Int` //│ ║ l.8: let s = Some(Some(42)) //│ ╙── ^^^^^^^^ //│ Some[Int] | () //│ res //│ = Some {} // * Yet, repeating the same code no longer yields the error: s : Some[Int] | () //│ Some[Int] | () //│ res //│ = Some {} // * This is because type inference sometimes takes some shortcuts (using a subtyping check instead of full constraining) // * that assume that previously-typed code was correctly typed // * (because subtyping checks may leverage previously-added bounds even when these were inconsistent). // * So this lack of repeated errors is not a soundness problem nor a bug. // * Note: a case where the shortcut does not currently trigger: :e s : Some[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.45: s : Some[Int] //│ ║ ^ //│ ╟── application of type `Some[?A]` is not an instance of `Int` //│ ║ l.8: let s = Some(Some(42)) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.45: s : Some[Int] //│ ╙── ^^^ //│ Some[Int] //│ res //│ = Some {} ================================================ FILE: shared/src/test/diff/nu/Dates.mls ================================================ :NewDefs declare class Date { constructor(date: Num) fun toString(): Str fun toLocaleString(locales: Str | Array[Str], options: anything): Str } //│ declare class Date { //│ constructor(date: Num) //│ fun toLocaleString: (locales: Str | Array[Str], options: anything) -> Str //│ fun toString: () -> Str //│ } let date1 = new Date(12345678) //│ let date1: Date //│ date1 //│ = 1970-01-01T03:25:45.678Z date1.toLocaleString("en-US", { timeZone: "America/New_York" }) //│ Str //│ res //│ = '12/31/1969, 10:25:45 PM' ================================================ FILE: shared/src/test/diff/nu/DecLit.mls ================================================ :NewDefs // Real Numbers // ============ [0.5, 1.0, 3.14159] //│ [0.5, 1.0, 3.14159] //│ res //│ = [ 0.5, 1, 3.14159 ] [1e100, 1E100, 1e+100, 1E+100, 1E-11, 1e-10, 1E-2, 1e-2] //│ [1E+100, 1E+100, 1E+100, 1E+100, 1E-11, 1E-10, 0.01, 0.01] //│ res //│ = [ //│ 1e+100, 1e+100, //│ 1e+100, 1e+100, //│ 1e-11, 1e-10, //│ 0.01, 0.01 //│ ] [3.14e-10, 3.14E-10, 3.14e+10, 3.14E+10] //│ [3.14E-10, 3.14E-10, 3.14E+10, 3.14E+10] //│ res //│ = [ 3.14e-10, 3.14e-10, 31400000000, 31400000000 ] [0.5e-10, 0.5E-10, 0.5e+10, 0.5E+10] //│ [5E-11, 5E-11, 5E+9, 5E+9] //│ res //│ = [ 5e-11, 5e-11, 5000000000, 5000000000 ] // Separators in integral, fractional, and exponent parts. [12_34_56.0, 12_34_56.78_90] [1_2.3_4e-1_0, 1_2.3_4e+1_0, 1_2.3_4e1_0] [1_2.3_4E-1_0, 1_2.3_4E+1_0, 1_2.3_4E1_0] //│ [1.234E-9, 1.234E+11, 1.234E+11] //│ res //│ = [ 123456, 123456.789 ] //│ res //│ = [ 1.234e-9, 123400000000, 123400000000 ] //│ res //│ = [ 1.234e-9, 123400000000, 123400000000 ] // Conflict with tuple index selection. :pe .1 //│ ╔══[PARSE ERROR] Unexpected selector in expression position //│ ║ l.45: .1 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.45: .1 //│ ╙── ^ //│ () //│ res //│ = undefined // Corner cases. :pe 0.E10 //│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point //│ ║ l.58: 0.E10 //│ ╙── ^ //│ 0E+10 //│ res //│ = 0 :pe 0.0E //│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign //│ ║ l.67: 0.0E //│ ╙── ^ //│ 0.0 //│ res //│ = 0 :pe 0.0E+ //│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign //│ ║ l.76: 0.0E+ //│ ╙── ^ //│ 0.0 //│ res //│ = 0 :pe 0E //│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign //│ ║ l.85: 0E //│ ╙── ^ //│ 0 //│ res //│ = 0 :pe 0E+ //│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign //│ ║ l.94: 0E+ //│ ╙── ^ //│ 0 //│ res //│ = 0 :pe 1234E //│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign //│ ║ l.103: 1234E //│ ╙── ^ //│ 1234 //│ res //│ = 1234 :pe 4378. //│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point //│ ║ l.112: 4378. //│ ╙── ^ //│ 4378 //│ res //│ = 4378 :pe 5. //│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point //│ ║ l.121: 5. //│ ╙── ^ //│ 5 //│ res //│ = 5 :pe 789.E //│ ╔══[LEXICAL ERROR] Expect at least one digit after the decimal point //│ ║ l.130: 789.E //│ ╙── ^ //│ ╔══[LEXICAL ERROR] Expect at least one digit after the exponent sign //│ ║ l.130: 789.E //│ ╙── ^ //│ 789 //│ res //│ = 789 ================================================ FILE: shared/src/test/diff/nu/Declarations.mls ================================================ :NewDefs declare fun btoa: nothing declare fun atob: nothing //│ fun btoa: nothing //│ fun atob: nothing let str = btoa("hello") atob(str) //│ let str: nothing //│ nothing //│ str //│ = 'aGVsbG8=' //│ res //│ = 'hello' declare module console { fun error: Str -> unit } //│ declare module console { //│ fun error: Str -> unit //│ } console.error("hello") //│ unit //│ res //│ = undefined //│ // Output //│ hello :e console.log("hello") //│ ╔══[ERROR] Type `console` does not contain member `log` //│ ║ l.35: console.log("hello") //│ ╙── ^^^^ //│ error //│ res //│ = undefined declare module Foo { fun foo: Int } //│ declare module Foo { //│ fun foo: Int //│ } :re Foo.foo //│ Int //│ res //│ Runtime error: //│ ReferenceError: Foo is not defined declare type A = Int //│ type A = Int 42 : A //│ A //│ res //│ = 42 declare class Sanitizer { fun sanitizeFor(element: Str, input: Str): Str } //│ declare class Sanitizer { //│ constructor() //│ fun sanitizeFor: (element: Str, input: Str) -> Str //│ } :re let s = new Sanitizer //│ let s: Sanitizer //│ s //│ Runtime error: //│ ReferenceError: Sanitizer is not defined // * TODO allow Buffer2 to be named Buffer... // :d declare module Buffer { abstract class Buffer2 { val length: Int } fun bar: Int fun from(a: Array[Int]): Buffer2 = from(a) // fun from1(a: Array[Int]): this.Buffer2 = from1(a) // FIXME // fun from2(a: Array[Int]): Buffer.Buffer2 = from2(a) // FIXME } //│ declare module Buffer { //│ abstract class Buffer2 { //│ val length: Int //│ } //│ fun bar: Int //│ fun from: (a: Array[Int]) -> Buffer2 //│ } let b = Buffer.from([0, 1, 2]) //│ let b: Buffer2 //│ b //│ = b.length //│ Int //│ res //│ = 3 :pe declare 42 //│ ╔══[PARSE ERROR] Unexpected literal token after modifier //│ ║ l.120: declare 42 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected literal token after modifier //│ ║ l.120: declare 42 //│ ╙── ^^ //│ 42 //│ res //│ = 42 ================================================ FILE: shared/src/test/diff/nu/DiamondInherit.mls ================================================ :NewDefs trait Foo[A] { fun foo: A } //│ trait Foo[A] { //│ fun foo: A //│ } module Bar extends Foo[Int | Bool], Foo[Int | Str] { fun foo = 123 } //│ module Bar extends Foo { //│ fun foo: 123 //│ } Bar.foo //│ 123 //│ res //│ = 123 Bar : Foo['X] //│ Foo['X] //│ where //│ 'X := Int | Str //│ res //│ = Bar { class: [class Bar extends Object] } (Bar : Foo['X]).foo //│ Int | Str //│ res //│ = 123 trait Foo[A] { fun foo: A; fun bar: A -> A } //│ trait Foo[A] { //│ fun bar: A -> A //│ fun foo: A //│ } module Bar extends Foo[Int | Bool], Foo[Int | Str] { fun foo = 123 fun bar = id } //│ module Bar extends Foo { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: 123 //│ } Bar.bar //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] Bar : Foo['X] //│ Foo['X] //│ where //│ 'X := Int | Str //│ res //│ = Bar { class: [class Bar extends Object] } trait T1 extends Foo[Int | Bool] //│ trait T1 extends Foo { //│ fun bar: 'A -> 'A //│ fun foo: 'A //│ } //│ where //│ 'A := Int | false | true module Bar extends T1, Foo[Int | Str] { fun foo = 123 fun bar = id } //│ module Bar extends Foo, T1 { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: 123 //│ } (Bar : Foo['X]).foo //│ Int | Str //│ res //│ = 123 (Bar : Foo['X]).bar //│ ('X & (Int | Str)) -> (Int | Str | 'X) //│ res //│ = [Function: id] (Bar : T1).foo //│ Int | false | true //│ res //│ = 123 let f = (Bar : T1).bar f(true) //│ let f: ('A & (Int | false | true)) -> (Int | false | true | 'A) //│ Int | false | true //│ f //│ = [Function: id] //│ res //│ = true :e module Bar extends T1, Foo[Int | Str] { fun foo = 123 fun bar(x) = x + 1 } //│ ╔══[ERROR] Type mismatch in definition of method bar: //│ ║ l.108: fun bar(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.64: trait T1 extends Foo[Int | Bool] //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.108: fun bar(x) = x + 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method bar: //│ ║ l.108: fun bar(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.64: trait T1 extends Foo[Int | Bool] //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.108: fun bar(x) = x + 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method bar: //│ ║ l.108: fun bar(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `Str` is not an instance of type `Int` //│ ║ l.106: module Bar extends T1, Foo[Int | Str] { //│ ║ ^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.108: fun bar(x) = x + 1 //│ ╙── ^ //│ module Bar extends Foo, T1 { //│ fun bar: Int -> Int //│ fun foo: 123 //│ } trait Base[A] { fun foo: A; fun bar: A -> A } trait Derived1[A] extends Base[A] trait Derived2 extends Base[[Int | Str, Int | Str]] //│ trait Base[A] { //│ fun bar: A -> A //│ fun foo: A //│ } //│ trait Derived1[A] extends Base { //│ fun bar: 'A -> 'A //│ fun foo: 'A //│ } //│ trait Derived2 extends Base { //│ fun bar: 'A0 -> 'A0 //│ fun foo: 'A0 //│ } //│ where //│ 'A0 := [Int | Str, Int | Str] //│ 'A := A class Final extends Derived1[[Int, Int]], Derived2 { fun foo = [123, 456] fun bar([x, y]) = [error, error] } //│ class Final extends Base, Derived1, Derived2 { //│ constructor() //│ fun bar: ([anything, anything]) -> [nothing, nothing] //│ fun foo: [123, 456] //│ } class Final extends Derived1[[Int, Int]], Derived2 { fun foo = [123, 456] fun bar([x, y]) = [y, x] } //│ class Final extends Base, Derived1, Derived2 { //│ constructor() //│ fun bar: forall 'a 'b. (['a, 'b]) -> ['b, 'a] //│ fun foo: [123, 456] //│ } ================================================ FILE: shared/src/test/diff/nu/DidierNu.mls ================================================ :NewDefs // val k x y = y // val unify : A. B. A -> A -> B -> B // = fun x y z -> k (fun z -> k (z x) (k (z y))) z // val a0 = (fun z -> z) (id : A. A -> A) 1 // val a1 x = (fun z -> unify x (fst z) (snd z)) (id, (id : A. A -> A)) 1 fun k(x, y) = y //│ fun k: forall 'a. (anything, 'a) -> 'a fun unify : 'A -> 'A -> 'B -> 'B //│ fun unify: forall 'B. anything -> anything -> 'B -> 'B fun unify = x => y => z => k(z => k(z(x), y => k(z(y), y)), z) //│ fun unify: forall 'a. anything -> anything -> 'a -> 'a fun unify = x => y => z => k of z => k(z(x), y => k of z(y), y), z //│ fun unify: forall 'a. anything -> anything -> 'a -> 'a fun a0 = (z => z) (id : forall 'A: 'A -> 'A) of 1 //│ fun a0: 1 fun fst([a, _]) = a fun snd([_, b]) = b //│ fun fst: forall 'a. (['a, anything]) -> 'a //│ fun snd: forall 'b. ([anything, 'b]) -> 'b fun a1(x) = (z => unify(x)(fst of z)(snd of z))([id, (id : forall 'A: 'A -> 'A)]) of 1 //│ fun a1: anything -> 1 ================================================ FILE: shared/src/test/diff/nu/Eduardo.mls ================================================ :NewDefs // https://twitter.com/LParreaux/status/1762843603804770408 type f = forall 'a: ('a ) -> 'a where 'a : Int //│ type f = forall 'a. (Int & 'a) -> 'a type f = (forall 'a: ('a ) -> 'a where 'a : Int) //│ type f = forall 'a. (Int & 'a) -> 'a type g = forall 'a: ('a & Int) -> 'a //│ type g = forall 'a. (Int & 'a) -> 'a (id : f) : g //│ g //│ res //│ = [Function: id] type f = (forall 'a: ('a ) -> 'a where 'a : Int) type g = forall 'a: ('a & Int) -> 'a //│ type f = forall 'a. (Int & 'a) -> 'a //│ type g = forall 'a0. (Int & 'a0) -> 'a0 (id : f) : g (id : g) : f //│ f //│ res //│ = [Function: id] //│ res //│ = [Function: id] x => (id : f)(x) : Int //│ Int -> Int //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/nu/EncodedLists.mls ================================================ :NewDefs :NoJS class List { fun match: forall 'res: (ifNil: () => 'res, ifCons: ('res, List[A]) => 'res) => 'res fun match = error // TODO use self-type... } val Nil: List val Cons: (head: 'a, tail: List<'a>) => List<'a> //│ class List[A] { //│ constructor() //│ fun match: forall 'res. (ifNil: () -> 'res, ifCons: ('res, List[A]) -> 'res) -> 'res //│ } //│ val Nil: List[nothing] //│ val Cons: forall 'a. (head: 'a, tail: List['a]) -> List['a] val x: List //│ val x: List[Int] // FIXME x: List //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.22: x: List //│ ║ ^ //│ ╙── expression of type `anything` is not an instance of type `Int` //│ List[anything] ================================================ FILE: shared/src/test/diff/nu/Eql.mls ================================================ :NewDefs val x: Eql[Int] //│ val x: Eql[Int] //│ x //│ = x === 1 //│ Bool //│ res //│ = //│ x is not implemented :e 1 === x //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.16: 1 === x //│ ║ ^^^^^^^ //│ ╟── type `#Eql` is not an instance of type `Num` //│ ║ l.4: val x: Eql[Int] //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.16: 1 === x //│ ╙── ^ //│ error | false | true //│ res //│ = //│ x is not implemented :e x === x //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.32: x === x //│ ║ ^^^^^^^ //│ ╟── type `#Eql` is not an instance of type `Int` //│ ║ l.4: val x: Eql[Int] //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.32: x === x //│ ╙── ^ //│ error | false | true //│ res //│ = //│ x is not implemented fun test1(x) = x === x //│ fun test1: forall 'a. (Eql['a] & 'a) -> Bool fun test2(x, y) = x === y //│ fun test2: forall 'a. (Eql['a], 'a) -> Bool 1 : Eql['a] //│ Eql[Num] //│ res //│ = 1 1 : Eql[Int] //│ Eql[Int] //│ res //│ = 1 1 : Eql[1] //│ Eql[1] //│ res //│ = 1 test1(1) //│ Bool //│ res //│ = true :e test1(x) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.78: test1(x) //│ ║ ^^^^^^^^ //│ ╟── type `#Eql` is not an instance of type `Int` //│ ║ l.4: val x: Eql[Int] //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.78: test1(x) //│ ╙── ^ //│ error | false | true //│ res //│ = //│ x is not implemented let n: Int = 1 //│ let n: Int //│ n //│ = 1 n : Eql['a] //│ Eql[anything] //│ res //│ = 1 test1(n) //│ Bool //│ res //│ = true val n: Num //│ val n: Num //│ n //│ = test1(n) //│ Bool //│ res //│ = //│ n is not implemented let d = 1/2 //│ let d: Num //│ d //│ = 0.5 test1(d) //│ Bool //│ res //│ = true test1("hello") //│ Bool //│ res //│ = true test2(0, 1) //│ Bool //│ res //│ = false test2(0, d) //│ Bool //│ res //│ = false x => test2(0, x) //│ Num -> Bool //│ res //│ = [Function: res] x => test2(x, 0) //│ Eql[0] -> Bool //│ res //│ = [Function: res] x => test2(d, x) //│ anything -> Bool //│ res //│ = [Function: res] x => test2(x, d) //│ Eql[Num] -> Bool //│ res //│ = [Function: res] :e test2(1, "oops") //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.169: test2(1, "oops") //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `Num` //│ ║ l.169: test2(1, "oops") //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.53: x === y //│ ╙── ^ //│ error | false | true //│ res //│ = false :e test2("oops", 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.184: test2("oops", 1) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Str` //│ ║ l.184: test2("oops", 1) //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.53: x === y //│ ╙── ^ //│ error | false | true //│ res //│ = false :e test2(1, {}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.199: test2(1, {}) //│ ║ ^^^^^^^^^^^^ //│ ╟── record literal of type `anything` is not an instance of type `Num` //│ ╟── Note: constraint arises from reference: //│ ║ l.53: x === y //│ ╙── ^ //│ error | false | true //│ res //│ = false :e test2({}, 1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.212: test2({}, 1) //│ ║ ^^^^^^^^^^^^ //│ ╟── record literal of type `anything` is not an instance of type `Eql` //│ ╟── Note: constraint arises from reference: //│ ║ l.53: x === y //│ ╙── ^ //│ error | false | true //│ res //│ = false :e test2({}, {}) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.225: test2({}, {}) //│ ║ ^^^^^^^^^^^^^ //│ ╟── record literal of type `anything` is not an instance of type `Eql` //│ ╟── Note: constraint arises from reference: //│ ║ l.53: x === y //│ ╙── ^ //│ error | false | true //│ res //│ = false ================================================ FILE: shared/src/test/diff/nu/EqlClasses.mls ================================================ :NewDefs module Mod //│ module Mod :e Mod === Mod //│ ╔══[ERROR] Module 'Mod' does not support equality comparison because it does not have a parameter list //│ ║ l.9: Mod === Mod //│ ╙── ^^^^^^^^^^^ //│ error | false | true //│ res //│ = true class Cls1() //│ class Cls1() Cls1() === Cls1() //│ Bool //│ res //│ = false class Cls2(x: Int) //│ class Cls2(x: Int) :e // TODO better error – or actually only support data classes Cls2(0) === Cls2(1) //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.27: class Cls2(x: Int) //│ ║ ^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.27: class Cls2(x: Int) //│ ╙── ^ //│ error | false | true //│ res //│ = false class Cls2(val x: Int) //│ class Cls2(x: Int) Cls2(0) === Cls2(1) //│ Bool //│ res //│ = false class Pair[out A](val fst: A, val snd: A) // extends (A <: Eql[A]) => Eql[Pair[A]] //│ class Pair[A](fst: A, snd: A) let p = Pair(1, 2) //│ let p: Pair[1 | 2] //│ p //│ = Pair {} p === p //│ Bool //│ res //│ = true x => p === x //│ {fst: Eql[1 | 2], snd: Eql[1 | 2]} -> Bool //│ res //│ = [Function: res] x => x === p //│ Eql[Pair[1 | 2]] -> Bool //│ res //│ = [Function: res] p === { fst: 1, snd: 2 } //│ Bool //│ res //│ = false :e { fst: 1, snd: 2 } === p //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.84: { fst: 1, snd: 2 } === p //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{fst: 1, snd: 2}` is not an instance of type `Eql` //│ ║ l.84: { fst: 1, snd: 2 } === p //│ ╙── ^^^^^^^^^ //│ error | false | true //│ res //│ = false let r = {x: 42, y: y => y} //│ let r: {x: 42, y: forall 'a. 'a -> 'a} //│ r //│ = { x: 42, y: [Function: y] } r : {x: Int} //│ {x: Int} //│ res //│ = { x: 42, y: [Function: y] } :e x => { a: 0 } === x //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.107: x => { a: 0 } === x //│ ║ ^^^^^^^^^^^^^^ //│ ╟── record literal of type `{a: 0}` is not an instance of type `Eql` //│ ║ l.107: x => { a: 0 } === x //│ ╙── ^ //│ anything -> (error | false | true) //│ res //│ Syntax error: //│ Unexpected token '===' x => x === { a: 0 } //│ Eql[{a: 0}] -> Bool //│ res //│ = [Function: res] let q = Pair(1, "oops") //│ let q: Pair["oops" | 1] //│ q //│ = Pair {} :e q === q //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.132: q === q //│ ║ ^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Str` //│ ║ l.126: let q = Pair(1, "oops") //│ ╙── ^ //│ error | false | true //│ res //│ = true class Pair2[out A, out B](val fst: A, val snd: B) //│ class Pair2[A, B](fst: A, snd: B) let q = Pair2(1, "oops") //│ let q: Pair2[1, "oops"] //│ q //│ = Pair2 {} q === q //│ Bool //│ res //│ = true class MP[out Col](val color: Col) //│ class MP[Col](color: Col) val mp = MP(1) //│ val mp: MP[1] //│ mp //│ = MP {} mp === mp //│ Bool //│ res //│ = true fun cmp(lhs, rhs) = lhs.color === rhs.color //│ fun cmp: forall 'a. ({color: Eql['a]}, {color: 'a}) -> Bool cmp(mp, mp) //│ Bool //│ res //│ = true module Mix { fun compare(lhs, rhs) = (lhs.color === rhs.color) } module Comp extends Mix //│ module Mix { //│ fun compare: forall 'a. ({color: Eql['a]}, {color: 'a}) -> Bool //│ } //│ module Comp extends Mix { //│ fun compare: forall 'b. ({color: Eql['b]}, {color: 'b}) -> Bool //│ } Comp.compare(mp, mp) //│ Bool //│ res //│ = true // *** NOTES *** // * Intended type for comparing Cons: // Eql[Cons & { head: Eql['h], tail: Eql['t] } | ~Cons & List] // * Original code // x: Int // y: Int // x == y // * Refactored code (we want an error) // x: Option[Int] // y: Int // x == y // --> // x.exists(_ == y) // * Should not be equatable: // Int | Option[Int] ================================================ FILE: shared/src/test/diff/nu/Eval.mls ================================================ :NewDefs // * Standard definitions: declare fun String: anything -> Str //│ fun String: anything -> Str fun (++) stringConcat(a, b) = concat(a)(b) //│ fun (++) stringConcat: (Str, Str) -> Str fun (|>) pipe(x, f) = f(x) fun (<|) pepi(f, x) = f(x) //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (<|) pepi: forall 'c 'd. ('c -> 'd, 'c) -> 'd // * Hack to throw exceptions :e declare class throw(arg: anything): nothing //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.22: declare class throw(arg: anything): nothing //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#Throw & {arg: anything}` does not match type `nothing` //│ ╟── Note: constraint arises from type reference: //│ ║ l.22: declare class throw(arg: anything): nothing //│ ╙── ^^^^^^^ //│ declare class throw(arg: anything): nothing fun raise(err) = throw(err) error //│ fun raise: anything -> nothing :re raise(1); 0 //│ 0 //│ res //│ Runtime error: //│ 1 //│ res //│ = 0 fun test = raise(1); 0 //│ fun test: nothing //│ 0 //│ res //│ = 0 :re test //│ nothing //│ res //│ Runtime error: //│ 1 fun test = raise(1), 0 //│ fun test: 0 :re test //│ 0 //│ res //│ Runtime error: //│ 1 fun test = raise(1) error //│ fun test: nothing :re test //│ nothing //│ res //│ Runtime error: //│ 1 abstract class Option[out A]: Some[A] | None class Some[out A](val value: A) extends Option[A] module None extends Option[nothing] //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class List[out A]: (Cons[A] | Nil) { virtual val length: Int } class Cons[out A](val head: A, val tail: List[A]) extends List[A] { val length: Int val length = tail.length + 1 fun toString() = "Cons(" ++ String(head) ++ ", " ++ String(tail) ++ ")" } module Nil extends List[nothing] { val length = 0 fun toString() = "Nil" } //│ abstract class List[A]: Cons[A] | Nil { //│ val length: Int //│ } //│ class Cons[A](head: A, tail: List[A]) extends List { //│ val length: Int //│ fun toString: () -> Str //│ } //│ module Nil extends List { //│ val length: 0 //│ fun toString: () -> "Nil" //│ } fun (::) cons(x, xs) = Cons(x, xs) fun (:::) concatList(xs, ys) = if xs is Nil then ys Cons(x, xs) then x :: xs ::: ys //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] //│ fun (:::) concatList: forall 'A0 'a. (Cons['A0] | Nil, List['A0] & 'a) -> (Cons['A0] | 'a) module Lists { // TODO use name List when module overloading is supported: fun map(f) = case Nil then Nil Cons(x, xs) then f(x) :: map(f)(xs) fun zip(xs, ys) = if xs is Nil then Nil Cons(x, xs) then if ys is Nil then Nil Cons(y, ys) then [x, y] :: zip(xs, ys) fun assoc(e) = case Cons(kv, rest) then if kv.key === e then Some(kv.value) else assoc(e)(rest) Nil then None } //│ module Lists { //│ fun assoc: forall 'a 'A. 'a -> (Cons[{key: Eql['a], value: 'A}] | Nil) -> (None | Some['A]) //│ fun map: forall 'b 'A0. ('b -> 'A0) -> (Cons['b] | Nil) -> (Cons['A0] | Nil) //│ fun zip: forall 'c 'd. (Cons['c] | Nil, Cons['d] | Nil) -> (Cons[['c, 'd]] | Nil) //│ } let xs = 1 :: 2 :: 3 :: Nil //│ let xs: Cons[1 | 2 | 3] //│ xs //│ = Cons {} String of xs ::: 4 :: 5 :: Nil //│ Str //│ res //│ = 'Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))' let ls = {key: "a", value: 0} :: Nil //│ let ls: Cons[{key: "a", value: 0}] //│ ls //│ = Cons {} ls |> Lists.assoc("a") //│ None | Some[0] //│ res //│ = Some {} // * Our little language: :e // * We don't yet support upper bounds on type parameters, so we can't provide the needed `out Sub <: Term` abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit class Var(val name: Str) extends Term class App(val lhs: Term, val args: List[Term]) extends Term class Lam(val params: List[Str], val body: Term) extends Term class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term class Sel(val prefix: Term, val fieldName: Str) extends Term abstract class Lit[out A](val value: A): IntLit | StrLit extends Term class IntLit(v: Int) extends Lit[Int](v) class StrLit(v: Str) extends Lit[Str](v) //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Sub` is not an instance of type `Term` //│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.171: abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Sub` does not match type `App | Lam | #Lit | Rcd[Term] | Sel | Var` //│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term //│ ║ ^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.171: abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.171: abstract class Term: Var | App | Lam | Sel | Rcd[Term] | Lit //│ ╙── ^^^^ //│ ╔══[ERROR] Type `Sub` does not contain member `Rcd#Sub` //│ ║ l.175: class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) extends Term //│ ╙── ^^^ //│ abstract class Term: App | Lam | Lit[anything] | Rcd[Term] | Sel | Var //│ class Var(name: Str) extends Term //│ class App(lhs: Term, args: List[Term]) extends Term //│ class Lam(params: List[Str], body: Term) extends Term //│ class Rcd[Sub](fields: List[{key: Str, value: Sub}]) extends Term //│ class Sel(prefix: Term, fieldName: Str) extends Term //│ abstract class Lit[A](value: A): IntLit | StrLit extends Term //│ class IntLit(v: Int) extends Lit, Term //│ class StrLit(v: Str) extends Lit, Term // * Workaround/alternative: type Term = Var | App | Lam | Sel | Rcd[Term] | Lit class Var(val name: Str) class App(val lhs: Term, val args: List[Term]) class Lam(val params: List[Str], val body: Term) class Rcd[out Sub](val fields: List[{key: Str, value: Sub}]) class Sel(val prefix: Term, val fieldName: Str) abstract class Lit[out A](val value: A): IntLit | StrLit class IntLit(v: Int) extends Lit[Int](v) class StrLit(v: Str) extends Lit[Str](v) //│ type Term = App | Lam | Lit[anything] | Rcd[Term] | Sel | Var //│ class Var(name: Str) //│ class App(lhs: Term, args: List[Term]) //│ class Lam(params: List[Str], body: Term) //│ class Rcd[Sub](fields: List[{key: Str, value: Sub}]) //│ class Sel(prefix: Term, fieldName: Str) //│ abstract class Lit[A](value: A): IntLit | StrLit //│ class IntLit(v: Int) extends Lit //│ class StrLit(v: Str) extends Lit type Value = Lam | Lit | Rcd[Value] //│ type Value = Lam | Lit[anything] | Rcd[Value] fun err(msg) = throw(concat("Evaluation error: " ++ msg)) error //│ fun err: Str -> nothing fun eval(t, env) = if t is Var(nme) then if env |> Lists.assoc(nme) is Some(v) then v else err("variable not found: " ++ nme) Lit then t Lam then t App(f, args) then let fe = eval(f, env) if fe is Lam(ps, bod) then val argse = args |> Lists.map(a => eval(a, env)) if ps.length === argse.length then () else err("wrong number of arguments") let envp = Lists.zip(ps, argse) |> Lists.map of ([key, value]) => {key, value} eval(bod, envp ::: env) else err(String(fe) ++ " cannot be applied") Sel(pre, nme) then let pree = eval(pre, env) if pree is Rcd(xs) and xs |> Lists.assoc(nme) is Some(v) then v else err(String(pree) ++ " does not have field " ++ nme) Rcd(fs) then Rcd of fs |> Lists.map of {key, value} => {key, value: eval(value, env)} //│ fun eval: forall 'a 'b 'c. ('a, Cons[{key: Eql[Str], value: 'b}] & {List#A <: {key: Eql[Str], value: 'b}} & List[{key: Eql[Str], value: 'b}] | Nil & {List#A <: {key: Eql[Str], value: 'b}} & List[{key: Eql[Str], value: 'b}]) -> ('b | 'c) //│ where //│ 'b :> 'c //│ <: Object & ~#Rcd | Rcd['b] //│ 'c :> Lam | Lit[??A] | Rcd[Lam | Lit[??A] | 'b | 'c] | 'b //│ 'a <: App | Lam | Lit[anything] | Rcd['a] | Sel | Var eval : (Term, List[{key: Str, value: Value}]) -> Value //│ (Term, List[{key: Str, value: Value}]) -> Value //│ res //│ = [Function: eval] let rcd = Rcd({key: "a", value: IntLit(0)} :: Nil) //│ let rcd: Rcd[IntLit] //│ rcd //│ = Rcd {} eval of rcd, Nil //│ 'a //│ where //│ 'a :> Lam | Lit[anything] | Rcd[Lam | Lit[anything] | 'a] //│ res //│ = Rcd {} eval of Sel(rcd, "a"), Nil //│ 'a //│ where //│ 'a :> Lam | Lit[??A] | Rcd[Lam | Lit[??A] | 'a] //│ res //│ = IntLit {} eval of App(Lam("x" :: Nil, Sel(Var("x"), "a")), rcd :: Nil), Nil //│ 'a //│ where //│ 'a :> Lam | Lit[??A] | Rcd[Lam | Lit[??A] | 'a] //│ res //│ = IntLit {} ================================================ FILE: shared/src/test/diff/nu/EvalNegNeg.mls ================================================ :NewDefs class Add(lhs: E, rhs: E) class Lit(n: Int) //│ class Add[E](lhs: E, rhs: E) //│ class Lit(n: Int) mixin EvalBase { fun eval(e) = if e is Lit(n) then n: Int Add(l, r) then this.eval(l) + this.eval(r) } //│ mixin EvalBase() { //│ this: {eval: 'a -> Int} //│ fun eval: (Add['a] | Lit) -> Int //│ } class Neg(expr: A) //│ class Neg[A](expr: A) mixin EvalNeg { fun eval(e) = if e is Neg(d) then 0 - this.eval(d) else super.eval(e) } //│ mixin EvalNeg() { //│ super: {eval: 'a -> 'b} //│ this: {eval: 'c -> Int} //│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) //│ } mixin EvalNegNeg { fun eval(e) = if e is Neg(Neg(d)) then this.eval(d) else super.eval(e) } //│ mixin EvalNegNeg() { //│ super: {eval: (Neg[nothing] | 'a) -> 'b} //│ this: {eval: 'c -> 'b} //│ fun eval: (Neg[Neg['c] | Object & ~#Neg] | Object & 'a & ~#Neg) -> 'b //│ } module TestLang extends EvalBase, EvalNeg, EvalNegNeg //│ module TestLang { //│ fun eval: (Neg['A] | Object & 'a & ~#Neg) -> Int //│ } //│ where //│ 'A <: 'b & (Neg['b] | Object & ~#Neg) //│ 'b <: Neg['A] | Object & 'a & ~#Neg //│ 'a <: Add['b] | Lit | Neg['b] fun mk(n) = if n is 0 then Lit(3) 1 then Neg(mk(n - 1)) _ then Add(mk(n - 1), mk(n - 1)) //│ fun mk: forall 'a. (0 | 1 | Int & ~0 & ~1) -> (Lit | 'a) //│ where //│ 'a :> Add[Lit | 'a] | Neg[Lit | 'a] TestLang.eval(mk(0)) //│ Int //│ res //│ = 3 TestLang.eval(mk(11)) //│ Int //│ res //│ = -3072 ================================================ FILE: shared/src/test/diff/nu/ExplicitVariance.mls ================================================ :NewDefs class Foo[out A](x: A) //│ class Foo[A](x: A) fun foo(x: Foo[Int]): Foo[Num] = x //│ fun foo: (x: Foo[Int]) -> Foo[Num] :e fun foo(x: Foo[Num]): Foo[Int] = x //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.12: fun foo(x: Foo[Num]): Foo[Int] = x //│ ║ ^ //│ ╟── type `Num` is not an instance of type `Int` //│ ║ l.12: fun foo(x: Foo[Num]): Foo[Int] = x //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.12: fun foo(x: Foo[Num]): Foo[Int] = x //│ ╙── ^^^ //│ fun foo: (x: Foo[Num]) -> Foo[Int] class Foo[in A](x: A -> Int) //│ class Foo[A](x: A -> Int) fun foo(x: Foo[Num]): Foo[Int] = x //│ fun foo: (x: Foo[Num]) -> Foo[Int] :e fun foo(x: Foo[Int]): Foo[Num] = x //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.32: fun foo(x: Foo[Int]): Foo[Num] = x //│ ║ ^ //│ ╟── type `Num` is not an instance of type `Int` //│ ║ l.32: fun foo(x: Foo[Int]): Foo[Num] = x //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.32: fun foo(x: Foo[Int]): Foo[Num] = x //│ ╙── ^^^ //│ fun foo: (x: Foo[Int]) -> Foo[Num] // * Note that not checking variance annotations can actually be made sound in MLscript, // * but this would be surprising for users, who would find type errors at the definition's use sites. // :e // TODO check variance annotations! class Oops0[in A](val x: A) //│ class Oops0[A](x: A) // :e // TODO check variance annotations! class Oops0[out A](val x: A -> Int) //│ class Oops0[A](x: A -> Int) let o = Oops0(id) //│ let o: Oops0[nothing] //│ o //│ = Oops0 {} // * What used to happens is `Oops9{ A = nothing..'? }` was inferred for `o` (consistent with `A`'s covariance), // * so all negative occurrences of `o.A` were viewed as `nothing` from the outside, resulting in `o.x : nothing -> Int` // * No lomnger the case since I simplified substitution for variant type arguments, to improve inferred type simplification. o.x //│ Int -> Int //│ res //│ = [Function: id] // * Similarly, `Oops0[Int]` here used to expand to the equivalent `Oops0{ A = nothing..Int }`, giving `(o : Oops0[Int]).x : nothing -> Int` (o : Oops0[Int]).x //│ Int -> Int //│ res //│ = [Function: id] // * So code like this no longer reports an error: // :e o.x(123) //│ Int //│ res //│ = 123 // :e (o : Oops0[Int]).x(123) //│ Int //│ res //│ = 123 class Oops1[out A](val x: A -> A, val y: A) //│ class Oops1[A](x: A -> A, y: A) let o = Oops1(id, 123) //│ let o: Oops1[123] //│ o //│ = Oops1 {} o.x //│ 'A -> (123 | 'A) //│ res //│ = [Function: id] // :e o.x(123) //│ 123 //│ res //│ = 123 :re o.x(error) + 1 //│ Int //│ res //│ Runtime error: //│ Error: an error was thrown class Oops2[out A](val x: A -> A, val y: A) //│ class Oops2[A](x: A -> A, y: A) let o = Oops2(id, 123) //│ let o: Oops2[123] //│ o //│ = Oops2 {} o.x //│ 'A -> (123 | 'A) //│ res //│ = [Function: id] // :e o.x(123) //│ 123 //│ res //│ = 123 // :e // * We will be able to make this work later, through `o.x : o.A -> o.A` and `o.y : o.A` o.x(o.y) //│ 123 //│ res //│ = 123 :re o.x(error) + 1 //│ Int //│ res //│ Runtime error: //│ Error: an error was thrown ================================================ FILE: shared/src/test/diff/nu/ExpressionProblem_repro.mls ================================================ :NewDefs :NoJS class Add0(lhs: E) //│ class Add0[E](lhs: E) fun eval(e) = if e is Add0(l) then eval(l) //│ fun eval: forall 'a. 'a -> nothing //│ where //│ 'a <: Add0['a] class Add(lhs: E, rhs: E) class Lit(val value: Int) //│ class Add[E](lhs: E, rhs: E) //│ class Lit(value: Int) let add11 = Add(Lit(1), Lit(2)) //│ let add11: Add[Lit] fun eval(e) = if e is Lit(n) then n: Int Add(l, r) then eval(l) + eval(r) //│ fun eval: forall 'a. 'a -> Int //│ where //│ 'a <: Add['a] | Lit mixin EvalLit { fun eval(e) = if e is Lit(n) then n } //│ mixin EvalLit() { //│ fun eval: Lit -> Int //│ } mixin EvalLit { fun eval(e: Lit) = e.value } //│ mixin EvalLit() { //│ fun eval: (e: Lit) -> Int //│ } mixin EvalAdd { fun eval(e) = if e is Add(l, r) then this.eval(l) } //│ mixin EvalAdd() { //│ this: {eval: 'a -> 'b} //│ fun eval: Add['a] -> 'b //│ } module TestLang extends EvalAdd //│ module TestLang { //│ fun eval: 'a -> nothing //│ } //│ where //│ 'a <: Add['a] TestLang.eval //│ 'a -> nothing //│ where //│ 'a <: Add['a] mixin EvalBase { fun eval(e) = if e is Lit(n) then n: Int Add(l, r) then this.eval(l) + this.eval(r) } //│ mixin EvalBase() { //│ this: {eval: 'a -> Int} //│ fun eval: (Add['a] | Lit) -> Int //│ } module TestLang extends EvalBase //│ module TestLang { //│ fun eval: 'a -> Int //│ } //│ where //│ 'a <: Add['a] | Lit TestLang.eval //│ 'a -> Int //│ where //│ 'a <: Add['a] | Lit add11 //│ Add[Lit] TestLang.eval(add11) //│ Int add11 //│ Add[Lit] TestLang.eval(add11) //│ Int add11 //│ Add[Lit] class Neg(expr: A) //│ class Neg[A](expr: A) let add2negadd11 = Add(Lit(2), Neg(add11)) //│ let add2negadd11: Add[Lit | Neg[Add[Lit]]] mixin EvalNeg { fun eval(e) = if e is Neg(d) then 0 - this.eval(d) else super.eval(e) } //│ mixin EvalNeg() { //│ super: {eval: 'a -> 'b} //│ this: {eval: 'c -> Int} //│ fun eval: (Neg['c] | Object & 'a & ~#Neg) -> (Int | 'b) //│ } module TestLang extends EvalBase, EvalNeg //│ module TestLang { //│ fun eval: 'a -> Int //│ } //│ where //│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg TestLang.eval //│ 'a -> Int //│ where //│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg TestLang.eval(add11) //│ Int TestLang.eval(Neg(add11)) //│ Int TestLang.eval(Add(Lit(2), Neg(Lit(1)))) //│ Int TestLang.eval(Neg(Neg(add11))) //│ Int TestLang.eval(add2negadd11) //│ Int TestLang.eval(Add(Lit(2), Neg(add11))) //│ Int ================================================ FILE: shared/src/test/diff/nu/ExpressionProblem_small.mls ================================================ :NewDefs :NoJS class Neg[out A](expr: A) class Add[out E](lhs: E, rhs: E) class Lit(n: Int) //│ class Neg[A](expr: A) //│ class Add[E](lhs: E, rhs: E) //│ class Lit(n: Int) let add11 = Add(Lit(1), Lit(2)) let add2negadd11 = Add(Lit(2), Neg(add11)) //│ let add11: Add[Lit] //│ let add2negadd11: Add[Lit | Neg[Add[Lit]]] mixin EvalNothing { fun eval(e: nothing) = e } mixin EvalAddLit { fun eval(e) = if e is Lit(n) then n Add(l, r) then this.eval(l) + this.eval(r) else super.eval(e) } mixin EvalNeg { fun eval(e) = if e is Neg(d) then 0 - this.eval(d) else super.eval(e) } //│ mixin EvalNothing() { //│ fun eval: (e: nothing) -> nothing //│ } //│ mixin EvalAddLit() { //│ super: {eval: 'a -> 'b} //│ this: {eval: 'c -> Int} //│ fun eval: (Add['c] | Lit | Object & 'a & ~#Add & ~#Lit) -> (Int | 'b) //│ } //│ mixin EvalNeg() { //│ super: {eval: 'd -> 'e} //│ this: {eval: 'f -> Int} //│ fun eval: (Neg['f] | Object & 'd & ~#Neg) -> (Int | 'e) //│ } module TestLang extends EvalNothing, EvalAddLit, EvalNeg //│ module TestLang { //│ fun eval: 'a -> Int //│ } //│ where //│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg TestLang.eval //│ 'a -> Int //│ where //│ 'a <: Neg['a] | Object & (Add['a] | Lit) & ~#Neg ================================================ FILE: shared/src/test/diff/nu/Extrusion.mls ================================================ :NewDefs fun f(y) = let local = forall 'A: (x: 'A) => discard of y(x) + 1 x y //│ fun f: forall 'a. (??A -> Int & 'a) -> 'a :e f(id) //│ ╔══[ERROR] Type error in application //│ ║ l.12: f(id) //│ ║ ^^^^^ //│ ╟── type variable `'A` leaks out of its scope //│ ║ l.5: let local = forall 'A: (x: 'A) => //│ ║ ^^ //│ ╟── into application of type `Int` //│ ║ l.6: discard of y(x) + 1 //│ ║ ^^^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.6: discard of y(x) + 1 //│ ╙── ^ //│ forall 'a. error | 'a -> 'a //│ res //│ = [Function: id] fun f(y) = let local = forall 'A: (x: 'A) => discard of (y : forall 'a: 'a -> 'a)(x) x y //│ fun f: forall 'b. (forall 'a. 'a -> 'a & 'b) -> 'b f(id) //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] ================================================ FILE: shared/src/test/diff/nu/FieldRefinement.mls ================================================ :NewDefs :NoJS class Foo(val x: Int) { fun bar = x fun baz: 1 | 2 = 1 } //│ class Foo(x: Int) { //│ fun bar: Int //│ fun baz: 1 | 2 //│ } val foo: Foo & { x: 0 | 1, bar: 0 | 1, baz: 0 | 1, y: Bool } //│ val foo: Foo & {y: Bool, bar: 0 | 1, baz: 0 | 1, x: 0 | 1} foo.x //│ 0 | 1 foo.bar //│ 0 | 1 foo.baz //│ 1 foo.y //│ Bool :e foo.z //│ ╔══[ERROR] Type `Foo & {y: Bool, bar: 0 | 1, baz: 0 | 1, x: 0 | 1}` does not contain member `z` //│ ║ l.30: foo.z //│ ╙── ^^ //│ error ================================================ FILE: shared/src/test/diff/nu/FilterMap.mls ================================================ :NewDefs // * From https://arxiv.org/abs/2302.12783 // 1 -spec filtermap ( fun (( T ) -> Boolean ()) , [ T ]) -> [ T ] // 2 ; ( fun (( T ) -> { true , U } | false ) , [ T ]) -> [ U ] // 3 ; ( fun (( T ) -> { true , U } | Boolean ()) , [ T ]) -> [ T | U ]. // 4 filtermap ( _F , []) -> []; // 5 filtermap (F , [ X | XS ]) -> // 6 case F ( X ) of // 7 false -> filtermap (F , XS ); // 8 true -> [ X | filtermap (F , XS )]; // 9 { true , Y } -> [ Y | filtermap (F , XS )] // 10 end. type List[A] = Cons[A] | Nil class Cons[out A](head: A, tail: Cons[A] | Nil) module Nil //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) //│ module Nil fun (::) cons(h, t) = Cons(h, t) //│ fun (::) cons: forall 'A. ('A, Cons['A] | Nil) -> Cons['A] fun filtermap(f, xs: List['A]) = if xs is Nil then Nil Cons(y, ys) and f(y) is false then filtermap(f, ys) true then Cons(y, filtermap(f, ys)) [true, z] then Cons(z, filtermap(f, ys)) //│ fun filtermap: forall 'A 'A0. ('A -> (Object & {0: true, 1: 'A0} & ~false & ~true | false | true), xs: List['A]) -> (Cons['A0] | Nil) //│ where //│ 'A <: 'A0 let xs = 0 :: 1 :: 2 :: 3 :: 4 :: 5 :: 6 :: 7 :: Nil //│ let xs: Cons[0 | 1 | 2 | 3 | 4 | 5 | 6 | 7] //│ xs //│ = Cons {} fun f(x) = if x % 3 is 0 then [true, -x] 1 then false _ then true //│ fun f: Int -> (Bool | [true, Int]) :e // TODO: Records are not `Object`s filtermap(f, xs) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.49: filtermap(f, xs) //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[true, ?a]` is not an instance of type `Object` //│ ║ l.43: 0 then [true, -x] //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from `case` expression: //│ ║ l.30: false then filtermap(f, ys) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.31: true then Cons(y, filtermap(f, ys)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.32: [true, z] then Cons(z, filtermap(f, ys)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from application: //│ ║ l.29: Cons(y, ys) and f(y) is //│ ╙── ^^^^ //│ Cons[Int] | Nil | error //│ res //│ = Cons {} module Tru module Fals //│ module Tru //│ module Fals class Pair[A, B](lhs: A, rhs: B) //│ class Pair[A, B](lhs: A, rhs: B) fun filtermap(f, xs) = if xs is Nil then Nil Cons(y, ys) then if f(y) is Tru then filtermap(f, ys) Fals then Cons(y, filtermap(f, ys)) Pair(Tru, z) then Cons(z, filtermap(f, ys)) //│ fun filtermap: forall 'a 'A. ('a -> (Fals | Pair[Tru, 'A] | Tru), Cons['a & 'A] | Nil) -> (Cons['A] | Nil) fun mkString(xs) = if xs is Nil then "" Cons(x, xs') and xs' is Nil then toString(x) else concat(toString(x))(concat(", ")(mkString(xs'))) //│ fun mkString: (Cons[anything] | Nil) -> Str let list = Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(7, Nil))))))) mkString of list //│ let list: Cons[1 | 2 | 3 | 4 | 5 | 6 | 7] //│ Str //│ list //│ = Cons {} //│ res //│ = '1, 2, 3, 4, 5, 6, 7' mkString of filtermap(x => (if x % 2 == 0 then Tru else Fals), list) mkString of filtermap(x => (if x % 2 == 0 then Fals else Tru), list) mkString of filtermap(x => (if x % 2 == 0 then Fals x % 3 == 0 then Pair(Tru, x / 3) else Tru), list) //│ Str //│ res //│ = '1, 3, 5, 7' //│ res //│ = '2, 4, 6' //│ res //│ = '2, 1, 4, 6' ================================================ FILE: shared/src/test/diff/nu/FlatIfThenElse.mls ================================================ :NewDefs type Option[out A] = Some[A] | None class Some[out A](val value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None fun test(x: Option[Int]) = if x is None then [0, 0] else log(x.value) // ... // do other things on the happy path // ... [x.value - 1, x.value + 1] //│ fun test: (x: Option[Int]) -> [Int, Int] test(Some(10)) //│ [Int, Int] //│ res //│ = [ 9, 11 ] //│ // Output //│ 10 fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then log(value) [value - 1, value + 1] //│ fun test: (x: Option[Int]) -> [Int, Int] fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then log(value) [value - 1, value + 1] //│ fun test: (x: Option[Int]) -> [Int, Int] fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then log(value) [value - 1, value + 1] //│ fun test: (x: Option[Int]) -> [Int, Int] fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then log(value) [value - 1, value + 1] //│ fun test: (x: Option[Int]) -> [Int, Int] :pe // TODO support fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then [value - 1, value + 1] //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.65: if x is //│ ║ ^^^^ //│ ║ l.66: None then [0, 0] //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.67: Some(value) then [value - 1, value + 1] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun test: (x: Option[Int]) -> () :pe // TODO support fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then log(value) [value - 1, value + 1] //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.79: if x is //│ ║ ^^^^ //│ ║ l.80: None then [0, 0] //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.81: Some(value) then //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.82: log(value) //│ ║ ^^^^^^^^^^^^ //│ ║ l.83: [value - 1, value + 1] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun test: (x: Option[Int]) -> () // Q: Support? :pe :e :w fun test(x: Option[Int]) = if x is None then [0, 0] Some(value) then log(value) [value - 1, value + 1] //│ ╔══[PARSE ERROR] Unexpected 'then' keyword in expression position //│ ║ l.105: then //│ ╙── ^^^^ //│ ╔══[ERROR] Unexpected statement in an if block //│ ║ l.104: Some(value) //│ ╙── ^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.102: if x is //│ ║ ^^^^ //│ ║ l.103: None then [0, 0] //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Some[Int]` is not an instance of type `None` //│ ║ l.4: type Option[out A] = Some[A] | None //│ ║ ^^^^^^^ //│ ╟── but it flows into reference with expected type `None` //│ ║ l.102: if x is //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.103: None then [0, 0] //│ ╙── ^^^^ //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in if-else block: //│ ║ l.102: if x is //│ ║ ^^^^ //│ ║ l.103: None then [0, 0] //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.104: Some(value) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[0, 0]` does not match type `()` //│ ║ l.103: None then [0, 0] //│ ║ ^^^^^^ //│ ╟── but it flows into expression in statement position with expected type `()` //│ ║ l.102: if x is //│ ║ ^^^^ //│ ║ l.103: None then [0, 0] //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.104: Some(value) //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: value //│ ║ l.106: log(value) //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: value //│ ║ l.107: [value - 1, value + 1] //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: value //│ ║ l.107: [value - 1, value + 1] //│ ╙── ^^^^^ //│ fun test: (x: Option[Int]) -> [Int, Int] //│ Code generation encountered an error: //│ unresolved symbol value ================================================ FILE: shared/src/test/diff/nu/FlatIndentFuns.mls ================================================ :NewDefs x => y => x + y //│ Int -> Int -> Int //│ res //│ = [Function: res] id of x => y => x + y //│ Int -> Int -> Int //│ res //│ = [Function (anonymous)] let r = x => y => x + y //│ let r: Int -> Int -> Int //│ r //│ = [Function: r] r(1)(2) //│ Int //│ res //│ = 3 (r of 1) of 2 //│ Int //│ res //│ = 3 :e r of 1 of 2 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.36: r of 1 of 2 //│ ║ ^^^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.36: r of 1 of 2 //│ ╙── ^ //│ Int -> Int //│ res //│ Runtime error: //│ TypeError: 1 is not a function // * Could support this too... :pe let r = x => y => x + y //│ ╔══[PARSE ERROR] Unexpected newline in expression position //│ ║ l.51: let r = //│ ║ ^ //│ ║ l.52: x => //│ ╙── //│ let r: Int -> Int -> Int //│ r //│ = [Function: r1] ================================================ FILE: shared/src/test/diff/nu/FlatMonads.mls ================================================ :NewDefs declare fun String: anything -> Str //│ fun String: anything -> Str abstract class IO[A] { fun bind(f) = Bind(this, f) fun run: A } class Pure[A](value: A) extends IO[A] { fun run = value } class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO[B] { fun run = f(underlying.run).run } //│ abstract class IO[A] { //│ fun bind: forall 'B. (A -> IO['B]) -> Bind[A, 'B] //│ fun run: A //│ } //│ class Pure[A](value: A) extends IO { //│ fun bind: forall 'B0. ('A -> IO['B0]) -> Bind['A, 'B0] //│ fun run: A //│ } //│ class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO { //│ fun bind: forall 'B1. ('A0 -> IO['B1]) -> Bind['A0, 'B1] //│ fun run: B //│ } //│ where //│ 'A0 := B //│ 'A := A module readInt extends IO[Int] { fun run: Int = 42 } class printLine(str: Str) extends IO[undefined] { fun run = log(str) } //│ module readInt extends IO { //│ fun bind: forall 'B. ('A -> IO['B]) -> Bind[Int & 'A, 'B] //│ fun run: Int //│ } //│ class printLine(str: Str) extends IO { //│ fun bind: forall 'B0. ('A0 -> IO['B0]) -> Bind[() & 'A0, 'B0] //│ fun run: () //│ } //│ where //│ 'A0 := () //│ 'A := Int // * Nested indent: val main = printLine("Hi! Input two numbers: ").bind of _ => readInt.bind of n => readInt.bind of m => val sum = n + m printLine(concat("The sum is: ")(String of sum)).bind of _ => Pure(sum) //│ val main: Bind[(), 'B] //│ where //│ 'B :> Int //│ main //│ = Bind {} main.run //│ Int //│ res //│ = 84 //│ // Output //│ Hi! Input two numbers: //│ The sum is: 84 // * Flat indent: val main = printLine("Hi! Input two numbers: ").bind of _ => readInt.bind of n => readInt.bind of m => val sum = n + m printLine(concat("The sum is: ")(String of sum)).bind of _ => Pure(sum) //│ val main: Bind[(), 'B] //│ where //│ 'B :> Int //│ main //│ = Bind {} main.run //│ Int //│ res //│ = 84 //│ // Output //│ Hi! Input two numbers: //│ The sum is: 84 // * TODO improve this error – missing provenance for '0-element tuple' :e printLine("").bind of [] => error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.99: printLine("").bind of [] => error //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `()` is not a 0-element tuple //│ ║ l.35: class printLine(str: Str) extends IO[undefined] { fun run = log(str) } //│ ╙── ^^^^^^^^^ //│ Bind[out (), 'B] | error //│ res //│ = Bind {} // * TODO improve this error (parameter list repr.) :e printLine("").bind of () => error //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.112: printLine("").bind of () => error //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `[?A]` does not match type `[]` //│ ║ l.15: class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO[B] { //│ ╙── ^ //│ Bind[(), 'B] | error //│ res //│ = Bind {} printLine("").bind of (()) => error //│ Bind[(), 'B] //│ res //│ = Bind {} // * Using a shortand operator for `bind`... What's the use of a `do` notation?! :^) fun (#>>) bind[A, B](x: IO[A], f: A -> IO[B]): IO[B] = x.bind(f) //│ fun (#>>) bind: forall 'A 'B. (x: IO['A], f: 'A -> IO['B]) -> IO['B] val main = printLine("Hi! Input two numbers: ") #>> _ => readInt #>> n => readInt #>> m => val sum = n + m printLine(concat("The sum is: ")(String of sum)) #>> _ => Pure(sum) //│ val main: IO['B] //│ where //│ 'B :> Int //│ main //│ = Bind {} main.run //│ Int //│ res //│ = 84 //│ // Output //│ Hi! Input two numbers: //│ The sum is: 84 fun loop = printLine("Input a positive number: ") #>> _ => readInt #>> n => if n < 0 then loop else Pure(n) //│ fun loop: forall 'B. IO['B] //│ where //│ 'B :> Int let r = loop.run //│ let r: Int //│ r //│ = 42 //│ // Output //│ Input a positive number: // * Using another shortand operator for `map` fun (>>) compose[A, B, C](f: A -> B, g: B -> C): A -> C = x => g(f(x)) fun (#>) map[A, B](x: IO[A], f: A -> B): IO[B] = x.bind(f >> Pure) //│ fun (>>) compose: forall 'A 'B 'C. (f: 'A -> 'B, g: 'B -> 'C) -> 'A -> 'C //│ fun (#>) map: forall 'A0 'B0. (x: IO['A0], f: 'A0 -> 'B0) -> IO['B0] val main = printLine("Hi! Input two numbers: ") #>> _ => readInt #>> n => readInt #>> m => val sum = n + m printLine(concat("The sum is: ")(String of sum)) #> _ => sum //│ val main: IO['B] //│ where //│ 'B :> Int //│ main //│ = Bind {} // * With no type annotations: fun (>>) compose(f, g) = x => g(f(x)) fun (#>>) bind(x, f) = x.bind(f) fun (#>) map(x, f) = x.bind(f >> Pure) //│ fun (>>) compose: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c //│ fun (#>>) bind: forall 'd 'e. ({bind: 'd -> 'e}, 'd) -> 'e //│ fun (#>) map: forall 'f 'A 'g. ({bind: ('f -> Pure['A]) -> 'g}, 'f -> 'A) -> 'g val main = printLine("Hi! Input two numbers: ") #>> _ => readInt #>> n => readInt #>> m => val sum = n + m printLine(concat("The sum is: ")(String of sum)) #> _ => sum //│ val main: Bind[(), 'B] //│ where //│ 'B :> Int //│ main //│ = Bind {} main.run //│ Int //│ res //│ = 84 //│ // Output //│ Hi! Input two numbers: //│ The sum is: 84 fun loop = printLine("Input a positive number: ") #>> _ => readInt #>> n => if n < 0 then loop else Pure(n) //│ fun loop: forall 'B. Bind[(), 'B] //│ where //│ 'B :> Int let r = loop.run //│ let r: Int //│ r //│ = 42 //│ // Output //│ Input a positive number: // * Abstracting over the monad: fun main(ctx) = ctx.printLine("Hi! Input two numbers: ") #>> _ => ctx.readInt #>> n => ctx.readInt #>> m => val sum = n + m ctx.printLine(concat("The sum is: ")(String of sum)) #>> _ => ctx.pure(sum) //│ fun main: forall 'a 'b 'c 'd 'e. { //│ printLine: "Hi! Input two numbers: " -> {bind: (anything -> 'a) -> 'b} & Str -> {bind: (anything -> 'c) -> 'd}, //│ pure: Int -> 'c, //│ readInt: {bind: (Int -> 'e) -> 'a & (Int -> 'd) -> 'e} //│ } -> 'b val defaultCtx = {printLine, readInt, pure: Pure} //│ val defaultCtx: { //│ printLine: (str: Str) -> printLine, //│ pure: forall 'A. (value: 'A) -> Pure['A], //│ readInt: readInt //│ } //│ defaultCtx //│ = { //│ printLine: [Function (anonymous)] { //│ class: [class printLine extends IO], //│ unapply: [Function: unapply] //│ }, //│ readInt: readInt { class: [class readInt extends IO] }, //│ pure: [Function (anonymous)] { //│ class: [class Pure extends IO], //│ unapply: [Function: unapply] //│ } //│ } main(defaultCtx).run //│ Int //│ res //│ = 84 //│ // Output //│ Hi! Input two numbers: //│ The sum is: 84 fun loop(ctx) = ctx.printLine("Input a positive number: ") #>> _ => ctx.readInt #>> n => if n < 0 then loop(ctx) else ctx.pure(n) //│ fun loop: forall 'a 'b 'c 'd. { //│ printLine: "Input a positive number: " -> {bind: (anything -> 'a) -> 'b}, //│ pure: 'c -> 'd, //│ readInt: {bind: ((Num & 'c) -> ('b | 'd)) -> 'a} //│ } -> 'b let r = loop(defaultCtx) //│ let r: Bind[(), 'B] //│ where //│ 'B :> Int //│ r //│ = Bind {} let r = loop(defaultCtx).run //│ let r: Int //│ r //│ = 42 //│ // Output //│ Input a positive number: :e not(r) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.306: not(r) //│ ║ ^^^^^^ //│ ╟── type `Int` is not an instance of type `Bool` //│ ║ l.34: module readInt extends IO[Int] { fun run: Int = 42 } //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Bool` //│ ║ l.306: not(r) //│ ╙── ^ //│ error | false | true //│ res //│ = false // * Note: using inferred parent type arguments module readInt extends IO { fun run = 42 } class printLine(str: Str) extends IO { fun run = log(str) } //│ module readInt extends IO { //│ fun bind: forall 'B. ('A -> IO['B]) -> Bind['A, 'B] //│ fun run: 42 //│ } //│ class printLine(str: Str) extends IO { //│ fun bind: forall 'B0. ('A0 -> IO['B0]) -> Bind['A0, 'B0] //│ fun run: () //│ } //│ where //│ 'A0 :> () //│ 'A :> 42 val main = printLine("Hi! Input two numbers: ").bind of _ => readInt.bind of n => readInt.bind of m => val sum = n + m printLine(concat("The sum is: ")(String of sum)).bind of _ => Pure(sum) //│ val main: Bind[in 'A out () | 'A, 'B] //│ where //│ 'B :> Int //│ main //│ = Bind {} main //│ Bind[in 'A out () | 'A, 'B] //│ where //│ 'B :> Int //│ res //│ = Bind {} :e let r = printLine("").bind of 0 => Pure(1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.359: let r = printLine("").bind of 0 => Pure(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `()` does not match type `0` //│ ║ l.324: class printLine(str: Str) extends IO { fun run = log(str) } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.359: let r = printLine("").bind of 0 => Pure(1) //│ ╙── ^ //│ let r: Bind[in 0 & 'A out () | 'A, 'B] | error //│ where //│ 'B :> 1 //│ r //│ = Bind {} :e let r = printLine("").bind of x => log(x.a) Pure(1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.376: let r = printLine("").bind of x => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.377: log(x.a) //│ ║ ^^^^^^^^^ //│ ║ l.378: Pure(1) //│ ║ ^^^^^^^ //│ ╟── application of type `()` does not have field 'a' //│ ║ l.324: class printLine(str: Str) extends IO { fun run = log(str) } //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.377: log(x.a) //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.377: log(x.a) //│ ╙── ^ //│ let r: Bind[in {a: anything} & 'A out () | 'A, 'B] | error //│ where //│ 'B :> 1 //│ r //│ = Bind {} :re r.run //│ 1 | error //│ res //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading 'a') // * We can even technically support the following syntax... :NoJS // * TODO We'll need to support functions extended with fields // * An interface that describes monadic functions with a field `run` declare trait IO[A]: (forall 'b: (A -> IO['b]) -> IO['b]) { fun run: A } declare fun pure: 'a -> IO['a] declare fun readInt: IO[Int] declare fun printLine: Str -> IO[()] //│ declare trait IO[A]: forall 'b. (A -> IO['b]) -> IO['b] { //│ fun run: A //│ } //│ fun printLine: Str -> IO[()] //│ fun pure: forall 'a. 'a -> IO['a] //│ fun readInt: IO[Int] val main = printLine("Hi! Input two numbers: ") of _ => readInt of n => readInt of m => val sum = n + m printLine(concat("The sum is: ")(String of sum)) of _ => pure(sum) //│ val main: IO['b] //│ where //│ 'b :> Int main.run //│ Int ================================================ FILE: shared/src/test/diff/nu/FlatMonads_repro.mls ================================================ :NewDefs abstract class IO[A] { fun bind(f) = Bind(this, f) fun hey = this fun run: A } class Bind[CC, AA](underlying: IO[CC], f: CC -> IO[AA]) extends IO[AA] { fun run = f(underlying.run).run } class Pure[A](value: A) extends IO[A] { fun run = value } //│ abstract class IO[A] { //│ fun bind: forall 'AA. (A -> IO['AA]) -> Bind[A, 'AA] //│ fun hey: IO[A] //│ fun run: A //│ } //│ class Bind[CC, AA](underlying: IO[CC], f: CC -> IO[AA]) extends IO { //│ fun bind: forall 'AA0. ('A -> IO['AA0]) -> Bind['A, 'AA0] //│ fun hey: IO['A] //│ fun run: AA //│ } //│ class Pure[A](value: A) extends IO { //│ fun bind: forall 'AA1. ('A0 -> IO['AA1]) -> Bind['A0, 'AA1] //│ fun hey: IO['A0] //│ fun run: A //│ } //│ where //│ 'A0 := A //│ 'A := AA module readInt extends IO[Int] { fun run: Int = 42 } //│ module readInt extends IO { //│ fun bind: forall 'AA. ('A -> IO['AA]) -> Bind[Int & 'A, 'AA] //│ fun hey: IO['A] //│ fun run: Int //│ } //│ where //│ 'A := Int let ri(f) = Bind(Pure(42), f) // let ri(f) = Bind(Pure(42) : IO[Int], f) // let ri(f) = Bind(error : IO[Int], f) //│ let ri: forall 'CC 'AA. ('CC -> IO['AA]) -> Bind['CC, 'AA] //│ where //│ 'CC :> 42 //│ ri //│ = [Function: ri] ri(Pure) //│ Bind['CC, 'AA] //│ where //│ 'CC :> 42 //│ <: 'AA //│ 'AA :> 42 //│ res //│ = Bind {} readInt.bind //│ forall 'AA. (Int -> IO['AA]) -> Bind[Int, 'AA] //│ res //│ = [Function: bind] Bind(readInt, Pure) //│ Bind[Int & 'AA, 'AA] //│ where //│ 'AA :> Int //│ res //│ = Bind {} // TODO prevent JS method extrusion; force explicit use of eta epxansion let b = readInt.bind : (Int -> IO['B]) -> Bind[Int, 'B] //│ let b: (Int -> IO['B]) -> Bind[Int, 'B] //│ b //│ = [Function: bind] let b = readInt.bind : ('A -> IO['B]) -> Bind['A, 'B] where 'A : Int //│ let b: (Int -> IO['B]) -> Bind[Int, 'B] //│ b //│ = [Function: bind] let b = readInt.bind : ('A -> IO['B]) -> Bind['A, 'B] where Int : 'A //│ let b: (Int -> IO['B]) -> Bind[Int, 'B] //│ b //│ = [Function: bind] let r = b of Pure //│ let r: Bind[in Int & 'B out Int, 'B] //│ where //│ 'B :> Int //│ r //│ = Bind {} :re // FIXME `undefined` due to JS method extrusion r.run //│ Int //│ res //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading 'run') let r = readInt.bind of Pure //│ let r: Bind[in Int & 'AA out Int, 'AA] //│ where //│ 'AA :> Int //│ r //│ = Bind {} r.run //│ Int //│ res //│ = 42 x => readInt.bind of x //│ forall 'AA. (Int -> IO['AA]) -> Bind[Int, 'AA] //│ res //│ = [Function: res] readInt.bind of Pure //│ Bind[in Int & 'AA out Int, 'AA] //│ where //│ 'AA :> Int //│ res //│ = Bind {} readInt: IO['a] //│ IO[Int] //│ res //│ = readInt { class: [class readInt extends IO] } (readInt : IO[Int]).bind //│ forall 'AA. (Int -> IO['AA]) -> Bind[Int, 'AA] //│ res //│ = [Function: bind] readInt.run //│ Int //│ res //│ = 42 x => Pure(x).run //│ forall 'run. 'run -> 'run //│ res //│ = [Function: res] fun loop0 = readInt.bind of Pure fun loop1 = readInt.bind of (Pure : Int => IO[Int]) fun loop2 = readInt.bind of ((x: Int) => Pure(x)) fun loop3 = readInt.bind of (x => Pure(x) : IO[Int]) //│ fun loop0: forall 'AA. Bind[in Int & 'AA out Int, 'AA] //│ fun loop1: Bind[Int, Int] //│ fun loop2: forall 'AA0. Bind[Int, 'AA0] //│ fun loop3: Bind[Int, Int] //│ where //│ 'AA0 :> Int //│ 'AA :> Int fun (#>>) bindOp(x, f) = x.bind(f) //│ fun (#>>) bindOp: forall 'a 'b. ({bind: 'a -> 'b}, 'a) -> 'b fun loop = readInt #>> n => Pure(n) //│ fun loop: forall 'AA. Bind[in Int & 'AA out Int, 'AA] //│ where //│ 'AA :> Int val x: Bind['A, 'B] where undefined : 'A; 'A : 'B //│ val x: forall 'A 'B. Bind['A, 'B] //│ where //│ 'A :> () //│ <: 'B //│ 'B :> () //│ x //│ = x.run //│ () //│ res //│ = //│ x is not implemented val x: Bind['A, 'B] where 'A : undefined; 'A : 'B //│ val x: forall 'A 'B. Bind['A, 'B] //│ where //│ 'A <: () & 'B //│ x //│ = x.run //│ nothing //│ res //│ = //│ x is not implemented val x: Bind[Int, Bool] //│ val x: Bind[Int, Bool] //│ x //│ = // :d x.run //│ Bool //│ res //│ = //│ x is not implemented ================================================ FILE: shared/src/test/diff/nu/FunPatterns.mls ================================================ :NewDefs :NoJS fun f(x, y) = x + y //│ fun f: (Int, Int) -> Int // FIXME array pattern...?! fun f1([x, y]) = x + y fun f2([x, y],) = x + y fun f3([[x, y,],],) = x + y //│ fun f1: ([Int, Int]) -> Int //│ fun f2: ([Int, Int]) -> Int //│ fun f3: ([[Int, Int]]) -> Int :e fun f3([(x, y,),],) = x + y //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.17: fun f3([(x, y,),],) = x + y //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.17: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.17: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ fun f3: ([error]) -> Int class Pair(lhs: Int, rhs: Int) //│ class Pair(lhs: Int, rhs: Int) :e // * TODO fun f(Pair(x, y)) = x + y //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.34: fun f(Pair(x, y)) = x + y //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.34: fun f(Pair(x, y)) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.34: fun f(Pair(x, y)) = x + y //│ ╙── ^ //│ fun f: error -> Int ================================================ FILE: shared/src/test/diff/nu/FunPoly.mls ================================================ :NewDefs fun id(x) = x //│ fun id: forall 'a. 'a -> 'a [id(1), id(true)] //│ [1, true] //│ res //│ = [ 1, true ] not(id(true)) //│ Bool //│ res //│ = false fun id(x) = x [id(1), id(true)] //│ fun id: forall 'a. 'a -> 'a //│ [1, true] //│ res //│ = [ 1, true ] // * Currently, we type entire typing units monomorphically; // * later we should try to separate mutually-recursive components and generalize them independently. fun test = [id(1), id(true)] fun id(x) = x //│ fun test: forall 'a 'b. [1 | true | 'a, 1 | true | 'b] //│ fun id: forall 'a 'b. ('b & 'a) -> (1 | true | 'a) [id(1), id(true)] //│ [1 | true, 1 | true] //│ res //│ = [ 1, true ] :e not(id(true)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.42: not(id(true)) //│ ║ ^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` //│ ║ l.31: fun test = [id(1), id(true)] //│ ║ ^ //│ ╟── but it flows into application with expected type `Bool` //│ ║ l.42: not(id(true)) //│ ╙── ^^^^^^^^ //│ error | false | true //│ res //│ = false fun test = [Helper.id(1), Helper.id(true)] module Helper { fun id(x) = x } //│ fun test: [1, true] //│ module Helper { //│ fun id: forall 'a. 'a -> 'a //│ } ================================================ FILE: shared/src/test/diff/nu/FunSigs.mls ================================================ :NewDefs fun log(msg: Str): () //│ fun log: (msg: Str) -> () let f = log("ok") 123 //│ let f: 123 //│ f //│ = //│ log is not implemented fun log: Str -> unit //│ fun log: Str -> unit fun log: Str => unit //│ fun log: Str -> unit log("ok") //│ unit //│ res //│ = //│ log is not implemented fun con: Str => Str => Str fun con = concat //│ fun con: Str -> Str -> Str //│ fun con: Str -> Str -> Str con("aa")("bbb") //│ Str //│ res //│ = 'aabbb' fun con: Str => Str => Str //│ fun con: Str -> Str -> Str fun con = concat //│ fun con: Str -> Str -> Str con("aa")("bbb") //│ Str //│ res //│ = 'aabbb' fun oops: Str => Str //│ fun oops: Str -> Str fun oops = 0 //│ fun oops: 0 oops //│ 0 //│ res //│ = 0 :e fun oops: Str => Str fun oops = 0 //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.68: fun oops = 0 //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.68: fun oops = 0 //│ ║ ^ //│ ╟── but it flows into definition of method oops with expected type `Str -> Str` //│ ║ l.68: fun oops = 0 //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.67: fun oops: Str => Str //│ ╙── ^^^^^^^^^^ //│ fun oops: 0 //│ fun oops: Str -> Str oops //│ Str -> Str //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/nu/FunnyIndet.mls ================================================ :NewDefs 2 + 2 * 3 //│ Int //│ res //│ = 8 2 - 3 - 4 //│ Int //│ res //│ = 3 2 + 2 //│ Int //│ res //│ = 4 2 + 2 //│ Int //│ res //│ = 4 2 is 2 //│ Bool //│ res //│ = true if 2 is 2 then true //│ true //│ res //│ = true 2 is 2 //│ Bool //│ res //│ = true if 2 is 2 then true //│ true //│ res //│ = true :pe // TODO support if 2 is 2 then true 1 then false //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.58: 1 then false //│ ╙── ^^^^^^^^^^^^ //│ () //│ res //│ = true //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/nu/FunnyPoly.mls ================================================ :NewDefs :NoJS // * A bunch of semi-random polymorphism and extrusion tests. // * Some of them yield very ugly types. (They might improve once we use proper wildcard args.) // * I've been trying to reproduce/trigger the incompleteness of constraint solving // * for constraints like `MyClass[S] & ?b <: MyClass[T]` where ?b is a lower-level type variable. fun test(x: (forall 'a: 'a -> 'a) & 'b) = let foo(y) = x(y) foo //│ fun test: forall 'a 'b 'c. (x: forall 'a0. 'a0 -> 'a0 & ~('a -> 'a) | 'a0 -> 'a0 & 'b -> 'c) -> 'b -> 'c type Id = forall 'a: 'a -> 'a //│ type Id = forall 'a. 'a -> 'a fun test(x: Id & 'b) = let foo(y) = x(y) foo //│ fun test: forall 'b 'c 'a. (x: Id & 'b -> 'c | Id & ~('a -> 'a)) -> 'b -> 'c fun test(x: Int | 'b) = let foo(y) = (if true then x else y) : (Int | 'c) foo //│ fun test: forall 'c. (x: Int | 'c) -> (forall 'c0. (Int | 'c0) -> (Int | 'c | 'c0)) class Ref[T](x: T -> T) //│ class Ref[T](x: T -> T) class MyClass[A](x: A -> A) //│ class MyClass[A](x: A -> A) // Note: precedence of & is lower than that of -> fun mk: forall 'b: (Ref['b]) -> MyClass[Int] & 'b // fun mk(x) = error //│ fun mk: forall 'b. Ref['b] -> MyClass[Int] & 'b (x, y) => mk(x, y) //│ (anything, anything) -> nothing fun mk: forall 'a, 'b: (Ref['a], Ref['b]) -> (MyClass['a] & 'b) //│ fun mk: forall 'a 'b. (Ref['a], Ref['b]) -> (MyClass['a] & 'b) fun test(x, y) = mk(x, y) //│ fun test: forall 'a 'b. (Ref['a], Ref['b]) -> (MyClass['a] & 'b) :ns test //│ forall 'c 'd 'e 'a 'b. ('c, 'd) -> 'e //│ where //│ 'e :> MyClass['a] & 'b //│ 'd <: Ref['b] //│ 'c <: Ref['a] fun test(x, y) = let tmp = mk(x, y) tmp //│ fun test: forall 'a 'b. (Ref['a], Ref['b]) -> (MyClass['a] & 'b) fun test(x, y) = let tmp = mk(x, y) let foo(z) = mk(z, tmp) foo //│ fun test: forall 'a 'b 'b0. (Ref['a], Ref['b]) -> (forall 'a0. Ref['a0] -> (MyClass['a0] & 'b0)) //│ where //│ 'b <: Ref[?] | Ref[?] & ~{MyClass#A = 'a} | Ref['b0] | ~MyClass['a] fun test(x, y) = let tmp = mk(x, y) let foo(z) = mk(z, tmp) : MyClass['x] foo //│ fun test: forall 'a 'b 'b0 'a0 'a1 'a2 'x. (Ref['a], Ref['b]) -> (forall 'a3 'x0. Ref['a3] -> MyClass['x0]) //│ where //│ 'x0 <: 'x //│ 'a3 :> 'a2 | 'a1 //│ <: 'a0 //│ 'b <: Ref[?] | Ref[?] & ~{MyClass#A = 'a} | Ref[in 'b0 out 'b0 & (MyClass[?] & ~{MyClass#A :> 'a0 & 'a1} | MyClass[in 'x out nothing] | {MyClass#A :> 'x <: nothing} & ~{MyClass#A :> 'a2 <: 'a2 | 'a0} | ~#MyClass | ~{MyClass#A :> 'a2 & 'a0 & 'a1})] & { //│ Ref#T :> 'b0 <: 'b0 & (MyClass[?] & ~{MyClass#A :> 'a0 & 'a1 <: 'a2 | 'a1} | MyClass[in 'x out nothing] | {MyClass#A = 'x} & ~{MyClass#A :> 'a2 <: 'a2 | 'a0} | ~#MyClass | ~{MyClass#A :> 'a2 & 'a0 & 'a1 <: 'a2 | 'a0 | 'a1}) //│ } | ~MyClass['a] fun test(x, y) = let tmp = mk(x, y) let foo(z) = mk(z, tmp) : MyClass['x] [tmp, foo] //│ fun test: forall 'a 'b 'b0 'a0 'a1 'a2 'x. (Ref['a], Ref['b]) -> [MyClass['a] & 'b, forall 'a3 'x0. Ref['a3] -> MyClass['x0]] //│ where //│ 'x0 <: 'x //│ 'a3 :> 'a2 | 'a1 //│ <: 'a0 //│ 'b <: Ref[?] | Ref[?] & ~{MyClass#A = 'a} | Ref[in 'b0 out 'b0 & (MyClass[?] & ~{MyClass#A :> 'a0 & 'a1} | MyClass[in 'x out nothing] | {MyClass#A :> 'x <: nothing} & ~{MyClass#A :> 'a2 <: 'a2 | 'a0} | ~#MyClass | ~{MyClass#A :> 'a2 & 'a0 & 'a1})] & { //│ Ref#T :> 'b0 <: 'b0 & (MyClass[?] & ~{MyClass#A :> 'a0 & 'a1 <: 'a2 | 'a1} | MyClass[in 'x out nothing] | {MyClass#A = 'x} & ~{MyClass#A :> 'a2 <: 'a2 | 'a0} | ~#MyClass | ~{MyClass#A :> 'a2 & 'a0 & 'a1 <: 'a2 | 'a0 | 'a1}) //│ } | ~MyClass['a] fun ref: 'a -> Ref['a] //│ fun ref: forall 'a. 'a -> Ref['a] fun test(x, y) = let tmp = ref(mk(x, y)) let foo(z) = mk(z, tmp) foo //│ fun test: forall 'a 'b. (Ref['a], Ref['b]) -> (forall 'a0. Ref['a0] -> (MyClass[in 'a0 | 'a out 'a & 'a0] & 'b)) fun test(x, y) = let tmp = ref(mk(x, y)) let foo(z) = mk(z, tmp) : MyClass['x] foo //│ fun test: forall 'a 'b 'a0 'a1 'a2 'a3 'x 'x0. (Ref['a], Ref['b]) -> (forall 'a4 'x1. Ref['a4] -> MyClass['x1]) //│ where //│ 'x1 :> 'x0 //│ <: 'x //│ 'a4 :> 'a2 | 'a1 //│ <: 'a0 & 'a3 //│ 'b <: MyClass[?] & ~{MyClass#A :> 'a0 & 'a1 | 'a <: 'a & ('a2 | 'a1 | 'a3)} | MyClass[in 'x out 'x & 'x0] | {MyClass#A :> 'x <: 'x & 'x0} & ~{MyClass#A :> 'a2 | 'a <: 'a & ('a2 | 'a0)} | ~MyClass[in 'a | 'a2 & ('a0 & 'a1 | 'a) out 'a & ('a2 | 'a0 | 'a1 | 'a3)] fun refined': forall 'A, 'B: (r: 'A) -> ('A & Ref['B]) //│ fun refined': forall 'A 'B. (r: 'A) -> (Ref['B] & 'A) fun test(x: 'x) = let foo() = refined'(x) foo //│ fun test: forall 'x. (x: 'x) -> (forall 'B. () -> (Ref['B] & 'x)) // fun refined': forall 'A, 'B, 'C: (r: 'A, s: 'B) -> ('A & 'B & Ref['C]) fun refined': forall 'A, 'B, 'C: (r: 'A, s: 'B) -> ('A & 'B) //│ fun refined': forall 'A 'B. (r: 'A, s: 'B) -> ('A & 'B) fun test(x: 'x) = let foo(y: Ref['r] & 'y) = y foo(x) //│ fun test: forall 'r 'y. (x: Ref['r] & 'y) -> (Ref['r] & 'y) fun test(x: 'x) = let foo(y) = refined'(x, y) : Ref['r] foo //│ fun test: forall 'B 'r. (x: Ref[?] & ~'B | Ref[in 'r out nothing] | ~'B) -> (forall 'r0. 'B -> Ref['r0]) //│ where //│ 'r0 <: 'r ================================================ FILE: shared/src/test/diff/nu/GADTMono.mls ================================================ :NewDefs trait Expr[A]: LitInt | LitBool | Add | Cond | Pair | Fst | Snd class LitInt(n: Int) extends Expr[Int] class LitBool(b: Bool) extends Expr[Bool] class Add(x: Expr[Int], y: Expr[Int]) extends Expr[Int] class Cond[T](p: Expr[Bool], t: Expr[T], e: Expr[T]) extends Expr[T] class Pair[S, T](a: Expr[S], b: Expr[T]) extends Expr[[S, T]] class Fst[S, T](p: Expr[[S, T]]) extends Expr[S] class Snd[S, T](p: Expr[[S, T]]) extends Expr[T] //│ trait Expr[A]: Add | Cond[?] | Fst[?, ?] | LitBool | LitInt | Pair[?, ?] | Snd[?, ?] //│ class LitInt(n: Int) extends Expr //│ class LitBool(b: Bool) extends Expr //│ class Add(x: Expr[Int], y: Expr[Int]) extends Expr //│ class Cond[T](p: Expr[Bool], t: Expr[T], e: Expr[T]) extends Expr //│ class Pair[S, T](a: Expr[S], b: Expr[T]) extends Expr //│ class Fst[S, T](p: Expr[[S, T]]) extends Expr //│ class Snd[S, T](p: Expr[[S, T]]) extends Expr let l1 = LitInt(1) //│ let l1: LitInt //│ l1 //│ = LitInt {} // TODO class Exp[type A] //│ ╔══[PARSE ERROR] Unexpected 'type' keyword here //│ ║ l.26: class Exp[type A] //│ ╙── ^^^^ //│ class Exp { //│ constructor() //│ } l1: Expr[Int] //│ Expr[Int] //│ res //│ = LitInt {} :e l1: Expr[Bool] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.40: l1: Expr[Bool] //│ ║ ^^ //│ ╟── type `Int` is not an instance of type `Bool` //│ ║ l.4: class LitInt(n: Int) extends Expr[Int] //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.40: l1: Expr[Bool] //│ ╙── ^^^^ //│ Expr[Bool] //│ res //│ = LitInt {} // FIXME fun eval[A](e: Expr[A]): A = if e is LitInt(n) then n e is LitBool(b) then b e is Add(x, y) then eval(x) + eval(y) //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.57: e is LitInt(n) then n //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.58: e is LitBool(b) then b //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `#Expr & (Add & {Expr#A = A} | Cond[?] & {Expr#A = A} | Fst[?, ?] & {Expr#A = A} | LitBool & {Expr#A = A} | LitInt & {Expr#A = A} | Pair[?, ?] & {Expr#A = A} | Snd[?, ?] & {Expr#A = A})` does not match type `Add | LitBool | LitInt` //│ ║ l.55: fun eval[A](e: Expr[A]): A = //│ ║ ^^^^^^^ //│ ╟── but it flows into reference with expected type `Add | LitBool | LitInt` //│ ║ l.57: e is LitInt(n) then n //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.57: e is LitInt(n) then n //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.58: e is LitBool(b) then b //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Int` does not match type `A` //│ ║ l.4: class LitInt(n: Int) extends Expr[Int] //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `A` //│ ║ l.57: e is LitInt(n) then n //│ ║ ^ //│ ╟── Note: constraint arises from method type parameter: //│ ║ l.55: fun eval[A](e: Expr[A]): A = //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.55: fun eval[A](e: Expr[A]): A = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.56: if //│ ║ ^^^^^^^ //│ ║ l.57: e is LitInt(n) then n //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.58: e is LitBool(b) then b //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Int` does not match type `A` //│ ║ l.6: class Add(x: Expr[Int], y: Expr[Int]) extends Expr[Int] //│ ║ ^^^ //│ ╟── Note: constraint arises from method type parameter: //│ ║ l.55: fun eval[A](e: Expr[A]): A = //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.55: fun eval[A](e: Expr[A]): A = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.56: if //│ ║ ^^^^^^^ //│ ║ l.57: e is LitInt(n) then n //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.58: e is LitBool(b) then b //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.59: e is Add(x, y) then eval(x) + eval(y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `A` is not an instance of type `Int` //│ ║ l.55: fun eval[A](e: Expr[A]): A = //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.6: class Add(x: Expr[Int], y: Expr[Int]) extends Expr[Int] //│ ╙── ^^^ //│ fun eval: forall 'A. (e: Expr['A]) -> 'A ================================================ FILE: shared/src/test/diff/nu/GenericClassInheritance.mls ================================================ :NewDefs class Room[A](name: Str) { virtual fun foo(x: A) = x } //│ class Room[A](name: Str) { //│ fun foo: (x: A) -> A //│ } class BigRoom extends Room[Bool]("big") //│ class BigRoom extends Room { //│ constructor() //│ fun foo: (x: 'A) -> 'A //│ } //│ where //│ 'A := Bool // * Note that this essentially infers Room[Bool] class InferredRoom extends Room("infer") { fun foo(x) = x && true } //│ class InferredRoom extends Room { //│ constructor() //│ fun foo: Bool -> Bool //│ } (new InferredRoom) : Room['X] //│ Room['X] //│ where //│ 'X := Bool //│ res //│ = InferredRoom {} :e class TooManyRoom extends Room[Int, Str]("too many") //│ ╔══[ERROR] class Room expects 1 type parameter(s); got 2 //│ ║ l.35: class TooManyRoom extends Room[Int, Str]("too many") //│ ╙── ^^^^^^^^^^^^^ //│ class TooManyRoom extends Room { //│ constructor() //│ fun foo: (x: 'A) -> 'A //│ } //│ where //│ 'A := Int :e class WrongRoom extends Room[Bool]("wrong") { fun foo(x) = x + 1 } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.48: fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.48: fun foo(x) = x + 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.48: fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.48: fun foo(x) = x + 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.48: fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` is not an instance of type `Bool` //│ ║ l.48: fun foo(x) = x + 1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.4: virtual fun foo(x: A) = x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.48: fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` does not match type `Bool` //│ ║ l.48: fun foo(x) = x + 1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.47: class WrongRoom extends Room[Bool]("wrong") { //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.4: virtual fun foo(x: A) = x //│ ╙── ^ //│ class WrongRoom extends Room { //│ constructor() //│ fun foo: Int -> Int //│ } abstract class C0[A] { val a: A } //│ abstract class C0[A] { //│ val a: A //│ } class C1[A] extends C0[A] { val a = a } //│ class C1[A] extends C0 { //│ constructor() //│ val a: nothing //│ } new C1() : C1[Int] //│ C1[Int] //│ res //│ = C1 {} (new C1() : C1[Int]) : C0['X] //│ C0[Int] //│ res //│ = C1 {} new C1().a //│ nothing //│ res //│ = undefined mixin M1[A] { fun f1(x: A): A = x fun f2(x: A): [A, A] = [x, x] } //│ mixin M1[A]() { //│ fun f1: (x: A) -> A //│ fun f2: (x: A) -> [A, A] //│ } class A1 extends M1 { fun f1(x: Int) = x } //│ class A1 { //│ constructor() //│ fun f1: (x: Int) -> Int //│ fun f2: (x: 'A) -> ['A, 'A] //│ } class A2[S, T] extends M1[[S, T]] //│ class A2[S, T] { //│ constructor() //│ fun f1: (x: [S, T]) -> [S, T] //│ fun f2: (x: [S, T]) -> [[S, T], [S, T]] //│ } class A3(f1: Bool => Bool) extends M1 //│ class A3(f1: Bool -> Bool) { //│ fun f1: (x: 'A) -> 'A //│ fun f2: (x: 'A) -> ['A, 'A] //│ } mixin M2[A] { fun m: A = this.a } //│ mixin M2[A]() { //│ this: {a: A} //│ fun m: A //│ } class B1(val a: Int) extends M2[Int] //│ class B1(a: Int) { //│ fun m: Int //│ } class B2[A](val a: Int => A) extends M2 //│ class B2[A](a: Int -> A) { //│ fun m: Int -> A //│ } :e class E1(val a: Int) extends M2[Bool] //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.175: class E1(val a: Int) extends M2[Bool] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int` is not an instance of type `Bool` //│ ╟── Note: constraint arises from type reference: //│ ║ l.175: class E1(val a: Int) extends M2[Bool] //│ ║ ^^^^ //│ ╟── from field selection: //│ ║ l.157: fun m: A = this.a //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.175: class E1(val a: Int) extends M2[Bool] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int` does not match type `Bool` //│ ╟── Note: constraint arises from type reference: //│ ║ l.175: class E1(val a: Int) extends M2[Bool] //│ ║ ^^^^ //│ ╟── from field selection: //│ ║ l.157: fun m: A = this.a //│ ╙── ^^^^^^ //│ class E1(a: Int) { //│ fun m: Bool //│ } ================================================ FILE: shared/src/test/diff/nu/GenericClasses.mls ================================================ :NewDefs class C //│ class C[A] { //│ constructor() //│ } fun f(x) = if x is C then x //│ fun f: forall 'A. C['A] -> C['A] // * TODO parse class tags? // f(C : #C) class C(val a: A) //│ class C[A](a: A) let c = C(1) //│ let c: C[1] //│ c //│ = C {} c.a //│ 1 //│ res //│ = 1 fun f(x) = if x is C(a) then a //│ fun f: forall 'a. C['a] -> 'a f(c) //│ 1 //│ res //│ = 1 class C[A](val n: A) { fun f = this.n fun g = C(12).n } //│ class C[A](n: A) { //│ fun f: A //│ fun g: 12 //│ } class Some(val value: A) { fun get = value fun toArray = [value] fun map(f) = Some(f(value)) fun map_A(f : A => 'b) = Some(f(value)) } //│ class Some[A](value: A) { //│ fun get: A //│ fun map: forall 'A. (A -> 'A) -> Some['A] //│ fun map_A: forall 'A0. (A -> 'A0) -> Some['A0] //│ fun toArray: [A] //│ } let s = Some(1) //│ let s: Some[1] //│ s //│ = Some {} s.value //│ 1 //│ res //│ = 1 s.get //│ 1 //│ res //│ = 1 s.toArray //│ [1] //│ res //│ = [ 1 ] s.map //│ forall 'A. (1 -> 'A) -> Some['A] //│ res //│ = [Function: map] s.map(succ) //│ Some[Int] //│ res //│ = Some {} s.map_A //│ forall 'A. (1 -> 'A) -> Some['A] //│ res //│ = [Function: map_A] s.map_A(succ) //│ Some[Int] //│ res //│ = Some {} module None { fun get = error fun toArray = [] fun map(f) = None fun map_A(f: nothing -> anything) = None } //│ module None { //│ fun get: nothing //│ fun map: anything -> None //│ fun map_A: (f: nothing -> anything) -> None //│ fun toArray: [] //│ } None.toArray //│ [] //│ res //│ = [] type Option = Some | None //│ type Option[A] = None | Some[A] let opt = if true then Some(123) else None //│ let opt: None | Some[123] //│ opt //│ = Some {} opt.toArray //│ Array[123] //│ res //│ = [ 123 ] opt.map(succ) //│ None | Some[Int] //│ res //│ = Some {} opt.map_A(succ) //│ None | Some[Int] //│ res //│ = Some {} opt.map(x => x > 0) //│ None | Some[Bool] //│ res //│ = Some {} if opt is Some then opt.value else 0 //│ 0 | 123 //│ res //│ = 123 if opt is Some(v) then v else 0 //│ 0 | 123 //│ res //│ = 123 fun map(x, f) = if x is None then None Some(v) then Some(f(v)) //│ fun map: forall 'a 'A. (None | Some['a], 'a -> 'A) -> (None | Some['A]) let mo = map(opt, succ) //│ let mo: None | Some[Int] //│ mo //│ = Some {} mo.toArray //│ Array[Int] //│ res //│ = [ 124 ] // TODO class Test(n) { fun foo = n + 1 fun bar = n } //│ ╔══[ERROR] Class parameters currently need type annotations //│ ║ l.189: class Test(n) { //│ ╙── ^ //│ class Test(n: error) { //│ fun bar: error //│ fun foo: Int //│ } Test(1) //│ Test //│ res //│ = Test {} // :e Test(true) //│ Test //│ res //│ = Test {} :e class Test(n: A) { fun foo = n + 1 } //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.215: fun foo = n + 1 //│ ║ ^^^^^ //│ ╟── reference of type `A` is not an instance of type `Int` //│ ║ l.215: fun foo = n + 1 //│ ║ ^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.214: class Test(n: A) { //│ ╙── ^ //│ class Test[A](n: A) { //│ fun foo: Int | error //│ } Test(1) //│ Test[1] //│ res //│ = Test {} Test(true) //│ Test[true] //│ res //│ = Test {} class Test(n: A) { fun foo: A = n fun foo1(x: A) = x fun id(x) = x } //│ class Test[A](n: A) { //│ fun foo: A //│ fun foo1: (x: A) -> A //│ fun id: forall 'a. 'a -> 'a //│ } Test(1) //│ Test['A] //│ where //│ 'A :> 1 //│ res //│ = Test {} Test(1).foo //│ 1 //│ res //│ = 1 Test("ok").foo //│ "ok" //│ res //│ = 'ok' let t = Test(1) //│ let t: Test['A] //│ where //│ 'A :> 1 //│ t //│ = Test {} t.foo1(true) //│ 1 | true //│ res //│ = true t : Test<'a> //│ Test['a] //│ where //│ 'a :> 1 | true //│ res //│ = Test {} t.id //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] [t.id(1), t.id(true)] //│ [1, true] //│ res //│ = [ 1, true ] :e class TestBad() { fun foo1(x: A) = x fun foo2(x: A) = x + 1 } //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.302: fun foo2(x: A) = x + 1 //│ ║ ^^^^^ //│ ╟── reference of type `A` is not an instance of type `Int` //│ ║ l.302: fun foo2(x: A) = x + 1 //│ ║ ^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.300: class TestBad() { //│ ╙── ^ //│ class TestBad[A]() { //│ fun foo1: (x: A) -> A //│ fun foo2: (x: A) -> (Int | error) //│ } TestBad().foo1 //│ (x: 'A) -> 'A //│ res //│ = [Function: foo1] TestBad().foo1(1) //│ 1 //│ res //│ = 1 x => TestBad().foo1(x) //│ forall 'a. 'a -> 'a //│ res //│ = [Function: res] // :d let t = TestBad() //│ let t: forall 'A. TestBad['A] //│ t //│ = TestBad {} t.foo1 //│ (x: 'A) -> 'A //│ res //│ = [Function: foo1] [t.foo1(0), t.foo1(true)] //│ [0, true] //│ res //│ = [ 0, true ] t.foo1(0) //│ 0 //│ res //│ = 0 t //│ forall 'A. TestBad['A] //│ res //│ = TestBad {} fun foo(x: TestBad) = x.foo1 //│ fun foo: (x: TestBad[Int]) -> (x: Int) -> Int foo(t) //│ (x: Int) -> Int //│ res //│ = [Function: foo1] foo(t)(1) //│ Int //│ res //│ = 1 TestBad().foo2 //│ (x: anything) -> (Int | error) //│ res //│ = [Function: foo2] class Weird(val x: C<'a>) //│ class Weird(x: C['a]) let w = Weird(c) //│ let w: Weird //│ w //│ = Weird {} w.x //│ C['a] //│ res //│ = C {} not(w.x.n) //│ Bool //│ res //│ = true :e not(w.x.a) //│ ╔══[ERROR] Type `C['a]` does not contain member `a` //│ ║ l.400: not(w.x.a) //│ ╙── ^^ //│ Bool //│ res //│ = false abstract class Cls[A](val x: A) { fun g: A -> Int } //│ abstract class Cls[A](x: A) { //│ fun g: A -> Int //│ } module M extends Cls(123) { fun g = id } //│ module M extends Cls { //│ fun g: forall 'a. 'a -> 'a //│ } M: Cls['a] //│ Cls['a] //│ where //│ 'a :> 123 //│ <: Int //│ res //│ = M { class: [class M extends Cls] } class Cls[A](val x: A) { fun g: A -> Int; fun g(x) = 42 } //│ class Cls[A](x: A) { //│ fun g: A -> Int //│ } Cls(123) //│ Cls['A] //│ where //│ 'A :> 123 //│ res //│ = Cls {} ================================================ FILE: shared/src/test/diff/nu/GenericMethods.mls ================================================ :NewDefs fun foo1 = forall 'A: (x: 'A) => x //│ fun foo1: forall 'A. (x: 'A) -> 'A foo1(42) //│ 42 //│ res //│ = 42 :e foo1[Int](42) //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.13: foo1[Int](42) //│ ╙── ^^^^^^^^^ //│ 42 //│ res //│ = 42 fun foo2(x: A) = x //│ fun foo2: forall 'A. (x: 'A) -> 'A foo2(42) //│ 42 //│ res //│ = 42 :e foo2(42) //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.31: foo2(42) //│ ╙── ^^^^^^^^^ //│ 42 //│ res //│ = 42 fun foo3[A](x: A) = x //│ fun foo3: forall 'A. (x: 'A) -> 'A foo3(42) //│ 42 //│ res //│ = 42 :e foo3[Int](42) //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.49: foo3[Int](42) //│ ╙── ^^^^^^^^^ //│ 42 //│ res //│ = 42 fun bar: forall 'A: 'A => 'A //│ fun bar: forall 'A. 'A -> 'A :e fun bar : A => A //│ ╔══[ERROR] Type parameters are not yet supported in this position //│ ║ l.62: fun bar : A => A //│ ╙── ^ //│ fun bar: forall 'A. 'A -> 'A :pe :e :w fun bar: A => A //│ ╔══[PARSE ERROR] Unmatched opening angle bracket //│ ║ l.71: fun bar: A => A //│ ║ ^ //│ ╙── Note that `<` without spaces around it is considered as an angle bracket and not as an operator //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.71: fun bar: A => A //│ ╙── ^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.71: fun bar: A => A //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: >: //│ ║ l.71: fun bar: A => A //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: A //│ ║ l.71: fun bar: A => A //│ ╙── ^ //│ ╔══[ERROR] identifier not found: A //│ ║ l.71: fun bar: A => A //│ ╙── ^ //│ ╔══[ERROR] identifier not found: A //│ ║ l.71: fun bar: A => A //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol >: :e module Test { fun foo: 'A => 'A fun bar: 'A fun test(x: A) = x } //│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `Test` //│ ║ l.100: module Test { //│ ║ ^^^^ //│ ╟── Declared here: //│ ║ l.101: fun foo: 'A => 'A //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `Test` //│ ║ l.100: module Test { //│ ║ ^^^^ //│ ╟── Declared here: //│ ║ l.102: fun bar: 'A //│ ╙── ^^^^^^^^^^^ //│ module Test { //│ fun bar: nothing //│ fun foo: forall 'A. 'A -> 'A //│ fun test: forall 'A0. (x: 'A0) -> 'A0 //│ } class Test(n: A) { fun test(x: A) = [x, n] } //│ class Test[A](n: A) { //│ fun test: forall 'A. (x: 'A) -> ['A, A] //│ } ================================================ FILE: shared/src/test/diff/nu/GenericMixins.mls ================================================ :NewDefs // TODO support mixin BaseTest[A] { fun test(x: A) = x } //│ mixin BaseTest[A]() { //│ fun test: (x: A) -> A //│ } mixin BaseTest[A](x: A) { fun test = x } //│ mixin BaseTest[A](x: A) { //│ fun test: A //│ } mixin Test[A] { fun foo: A -> A fun foo = id fun bar: (A -> A) -> (A -> A) fun bar = id } //│ mixin Test[A]() { //│ fun bar: (A -> A) -> A -> A //│ fun foo: A -> A //│ } module C extends Test { fun baz1 = this.foo(0) fun baz2 = this.bar(this.foo) } //│ module C { //│ fun bar: ((0 | 'A) -> 'A) -> 'A -> (0 | 'A) //│ fun baz1: 0 | 'A //│ fun baz2: 'A -> (0 | 'A) //│ fun foo: 'A -> (0 | 'A) //│ } C.baz1 //│ 0 //│ res //│ = 0 C.foo(1) //│ 0 | 1 //│ res //│ = 1 C.foo(false) //│ 0 | false //│ res //│ = false module C extends Test[Int] { fun baz1 = this.foo(0) fun baz2 = this.bar(this.foo) } //│ module C { //│ fun bar: (Int -> Int) -> Int -> Int //│ fun baz1: Int //│ fun baz2: Int -> Int //│ fun foo: Int -> Int //│ } C.baz1 //│ Int //│ res //│ = 0 C.foo(1) //│ Int //│ res //│ = 1 :e C.foo(false) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.81: C.foo(false) //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ║ l.81: C.foo(false) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.59: module C extends Test[Int] { //│ ║ ^^^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.22: mixin Test[A] { //│ ╙── ^ //│ Int | error //│ res //│ = false class C[A] extends Test[Array[A]] { fun baz1 = this.foo([]) fun baz2 = this.bar(this.foo) } //│ class C[A] { //│ constructor() //│ fun bar: (Array[A] -> Array[A]) -> Array[A] -> Array[A] //│ fun baz1: Array[A] //│ fun baz2: Array[A] -> Array[A] //│ fun foo: Array[A] -> Array[A] //│ } mixin Test[A] { fun foo: A -> A fun foo = id fun bar: [A, A] fun bar = [this.arg, this.arg] fun baz = foo(this.arg) } //│ mixin Test[A]() { //│ this: {arg: A & 'a} //│ fun bar: [A, A] //│ fun baz: 'a //│ fun foo: A -> A //│ } class C(val arg: Int) extends Test //│ class C(arg: Int) { //│ fun bar: [Int | 'A, Int | 'A] //│ fun baz: Int //│ fun foo: 'A -> (Int | 'A) //│ } let c = C(1) [c.foo(false), c.bar] //│ let c: C //│ [Int | false, [Int, Int]] //│ c //│ = C {} //│ res //│ = [ false, [ 1, 1 ] ] :e // FIXME module D extends C(0) { this.foo(false) } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.144: this.foo(false) //│ ╙── ^^^^ //│ ╔══[ERROR] Cannot access `this` during object initialization //│ ║ l.144: this.foo(false) //│ ╙── ^^^^ //│ module D extends C { //│ fun bar: forall 'A. [Int | 'A, Int | 'A] //│ fun baz: Int //│ fun foo: forall 'A. 'A -> (Int | 'A) //│ } :e // TODO support or produce better error (arg is not actually recursive) class C extends Test { // it also fails with Test[Int]... fun arg = 123 } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.117: fun baz = foo(this.arg) //│ ╙── ^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.116: fun bar = [this.arg, this.arg] //│ ╙── ^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.116: fun bar = [this.arg, this.arg] //│ ╙── ^^^^ //│ class C { //│ constructor() //│ fun arg: 123 //│ fun bar: [error | 'A, error | 'A] //│ fun baz: error //│ fun foo: 'A -> (error | 'A) //│ } class C extends Test { fun arg: Int fun arg = 123 } //│ class C { //│ constructor() //│ fun arg: Int //│ fun bar: [Int | 'A, Int | 'A] //│ fun baz: Int //│ fun foo: 'A -> (Int | 'A) //│ } ================================================ FILE: shared/src/test/diff/nu/GenericModules.mls ================================================ :NewDefs // * TODO generic module definitions need to be restricted so they do not include nay state // * to avoid unsoundness... module Test[A] { fun foo: A -> A = id } //│ module Test[A] { //│ fun foo: A -> A //│ } Test.foo(1) + 1 //│ Int //│ res //│ = 2 not(Test.foo(true)) //│ Bool //│ res //│ = false Test.foo //│ 'A -> 'A //│ res //│ = [Function: id] // * FIXME merging generic module types, as we currently do, is unsound: let t = Test : Test[Int] & Test[Str] //│ let t: Test[in Int | Str out nothing] //│ t //│ = Test { class: [class Test] } t.foo //│ (Int | Str) -> nothing //│ res //│ = [Function: id] :re t.foo(1)(2) //│ nothing //│ res //│ Runtime error: //│ TypeError: t.foo(...) is not a function module Test { fun foo: A => A fun foo = id fun bar: A => A = id fun baz(x: A) = x fun poly0: 'a -> 'a fun poly0 = id fun poly1: forall 'a: 'a -> 'a fun poly1 = id fun poly2: 'a -> 'a = id } //│ module Test[A] { //│ fun bar: A -> A //│ fun baz: (x: A) -> A //│ fun foo: A -> A //│ fun poly0: forall 'a. 'a -> 'a //│ fun poly1: forall 'a0. 'a0 -> 'a0 //│ fun poly2: forall 'a1. 'a1 -> 'a1 //│ } Test.foo //│ 'A -> 'A //│ res //│ = [Function: id] Test.bar //│ 'A -> 'A //│ res //│ = [Function: id] Test.baz //│ (x: 'A) -> 'A //│ res //│ = [Function: baz] Test.poly0 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] Test.poly1 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] Test.poly2 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] Test.foo(1) //│ 1 //│ res //│ = 1 :re Test.foo(error) + 1 //│ Int //│ res //│ Runtime error: //│ Error: an error was thrown :e Test .foo //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.115: Test .foo //│ ╙── ^^^^^^^^^ //│ 'A -> 'A //│ res //│ = [Function: id] :e (Test).foo //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.124: (Test).foo //│ ╙── ^^^^^^^^^ //│ 'A -> 'A //│ res //│ = [Function: id] Test //│ forall 'A. Test['A] //│ res //│ = Test { class: [class Test] } Test : Test<'a> //│ Test['a] //│ res //│ = Test { class: [class Test] } fun test(x) = if x is Test then x.foo //│ fun test: forall 'A. Test['A] -> 'A -> 'A test(Test) //│ 'A -> 'A //│ res //│ = [Function: id] module Test { fun foo = id } //│ module Test[A] { //│ fun foo: forall 'a. 'a -> 'a //│ } Test.foo //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] module Test { fun foo: A => A fun foo = id } //│ module Test[A] { //│ fun foo: A -> A //│ } Test.foo //│ 'A -> 'A //│ res //│ = [Function: id] ================================================ FILE: shared/src/test/diff/nu/HeungTung.mls ================================================ :NewDefs :NoApproximateOverload trait A trait B //│ trait A //│ trait B module AA extends A, B //│ module AA extends A, B fun x: A & B fun x = AA //│ fun x: AA //│ fun x: A & B x : A //│ A //│ res //│ = AA { class: [class AA extends Object] } abstract class Foo[A, B] { fun x: A & B } //│ abstract class Foo[A, B] { //│ fun x: A & B //│ } module Bar extends Foo[Int, Bool] { fun x = x } //│ module Bar extends Foo { //│ fun x: nothing //│ } module Bar extends Foo { fun x = () } //│ module Bar extends Foo { //│ fun x: () //│ } Bar : Foo['a, 'b] //│ Foo['a, 'b] //│ where //│ 'b :> () //│ 'a :> () //│ res //│ = Bar { class: [class Bar extends Foo] } // * An overloaded function type fun f: (Int -> Int) & (Bool -> Bool) fun f = id //│ fun f: forall 'a. 'a -> 'a //│ fun f: Int -> Int & Bool -> Bool // * Widen the results fun h: (Int -> (Int | Bool)) & (Bool -> (Int | Bool)) fun h = f //│ fun h: Int -> Int & Bool -> Bool //│ fun h: (Int | false | true) -> (Int | false | true) // * Merge intersected functions with same domain fun g: (Int | Bool) -> (Int | Bool) fun g = h //│ fun g: (Int | false | true) -> (Int | false | true) //│ fun g: (Int | false | true) -> (Int | false | true) // * In one step :e // TODO: argument of union type fun g: (Int | Bool) -> (Int | Bool) fun g = f //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.72: fun g = f //│ ║ ^^^^^ //│ ╟── type `Int -> Int & Bool -> Bool` is not an instance of `(Int | false | true) -> (Int | false | true)` //│ ║ l.51: fun f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(Int | false | true) -> (Int | false | true)` //│ ║ l.72: fun g = f //│ ║ ^ //│ ╟── Note: constraint arises from function type: //│ ║ l.71: fun g: (Int | Bool) -> (Int | Bool) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun g: Int -> Int & Bool -> Bool //│ fun g: (Int | false | true) -> (Int | false | true) // * Can also widen into intersection fun i: ((Int & Bool) -> Int) & ((Int & Bool) -> Bool) fun i = f //│ fun i: Int -> Int & Bool -> Bool //│ fun i: nothing -> nothing // * Merge intersected functions with same codomain fun j: (Int & Bool) -> (Int & Bool) fun j = i //│ fun j: nothing -> nothing //│ fun j: nothing -> nothing :e // * Note: currently it doesn't work when done in a single step fun j: (Int & Bool) -> (Int & Bool) fun j = f //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.103: fun j = f //│ ║ ^^^^^ //│ ╟── type `Int -> Int & Bool -> Bool` is not an instance of `nothing -> nothing` //│ ║ l.51: fun f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `nothing -> nothing` //│ ║ l.103: fun j = f //│ ║ ^ //│ ╟── Note: constraint arises from function type: //│ ║ l.102: fun j: (Int & Bool) -> (Int & Bool) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun j: Int -> Int & Bool -> Bool //│ fun j: nothing -> nothing // * Or widen even further with both an intersection and a union, into this fun g: (Int & Bool) -> (Int | Bool) fun g = f //│ fun g: Int -> Int & Bool -> Bool //│ fun g: nothing -> (Int | false | true) // * Note: we currently approximate uses of overloaded function types! // * With match-type-based constraint solving, we could return Int here f(0) //│ Int //│ res //│ = 0 // f(0) : case 0 of { Int => Int; Bool => Bool } == Int x => f(x) //│ forall 'a 'b. 'a -> 'b //│ where //│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} //│ res //│ = [Function: res] // : forall 'a: 'a -> case 'a of { Int => Int; Bool => Bool } where 'a <: Int | Bool :e f(if true then 0 else false) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.148: f(if true then 0 else false) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Int -> Int & Bool -> Bool` is not an instance of `(0 | false) -> ?a` //│ ║ l.51: fun f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `(0 | false) -> ?a` //│ ║ l.148: f(if true then 0 else false) //│ ╙── ^ //│ error //│ res //│ = 0 // * With match-type-based constraint solving, we could *also* return Int here :e // TODO implement this syntax :w f(refined if true then 0 else false) // this one can be precise again! //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined //│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── application of type `error` does not match type `?a` //│ ║ l.166: f(refined if true then 0 else false) // this one can be precise again! //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ 'a //│ where //│ 'b :> error //│ 'a :> error //│ [+'b, -'a] in {} //│ Code generation encountered an error: //│ unresolved symbol refined // * Notes on constraint solving // * Easy: // ?a -> ?b <: (Int -> Int) & (Bool -> Bool) // to: // ?a -> ?b <: (Int -> Int) AND ?a -> ?b <: (Bool -> Bool) // * Hard; but can solve with match types: // (Int -> Int) & (Bool -> Bool) <: ?a -> ?b // to: // ?a <: Int | Bool AND (case ?a of { Int => Int; Bool => Bool }) <: ?b // We can still widen if needed; consider: // ?a := Int | Bool // then: // (case (Int | Bool) of { Int => Int; Bool => Bool }) <: ?b // to: // Int <: ?b AND Bool <: ?b // An simple match-type constraint example: // (case ?a of { Int => Int; Bool => Bool }) <: Int // to: // ?a <: Int // A more complicated match-type constraint example: // (case ?a of { Int => ?b; Bool => ?c }) <: T // to: // ?b <: (case ?a of { Int => T; Bool => Top }) AND ?c <: (case ?a of { Int => Top; Bool => T }) class List[A] //│ class List[A] { //│ constructor() //│ } // * Note: match type `T match { case List[t] => ... t ... }` could be encoded as: type M = (forall 't: List['t] => 't) //│ type M = forall 't. List['t] -> 't type T = List[Int] //│ type T = List[Int] :e // TODO application types type Res = M(T) //│ ╔══[ERROR] Wrong number of type arguments – expected 0, found 1 //│ ║ l.240: type Res = M(T) //│ ╙── ^^^^ //│ type Res = M let f = x => [x, x] //│ let f: forall 'a. 'a -> ['a, 'a] //│ f //│ = [Function: f1] [f(1), f(true)] //│ [[1, 1], [true, true]] //│ res //│ = [ [ 1, 1 ], [ true, true ] ] :e // TODO support fun f: Int -> Int fun f: Bool -> Bool fun f = id //│ ╔══[ERROR] A type signature for 'f' was already given //│ ║ l.262: fun f: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ fun f: forall 'a. 'a -> 'a //│ fun f: Int -> Int :e // TODO support f: (Int -> Int) & (Bool -> Bool) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.271: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ║ l.271: f: (Int -> Int) & (Bool -> Bool) //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.261: fun f: Int -> Int //│ ╙── ^^^ //│ Int -> Int & Bool -> Bool //│ res //│ = [Function: id] // t: S t: T // ------------- // t: S & T // * Weird MLstruct rule (only sound when we don't have FCP): // forall 'a: 'a -> 'a <: (Int -> Int) & (Bool -> Bool) == (Int | Bool) -> (Int & Bool) // ~{ a: Int } <: Str -> Str // * Notice: in positive position, this is equivalent to Bottom fun x: ~{ a: Int } //│ fun x: ~{a: Int} class A() class B() //│ class A() //│ class B() A() : ~B //│ ~B //│ res //│ = A {} // A <: ~B // <=> // A <: ~B | Bot // <=> // A & B <: Bot fun x: A & B //│ fun x: nothing fun test(x) = if x is A then 0 _ then x //│ fun test: forall 'a. (A | Object & 'a & ~#A) -> (0 | 'a) test(B()) //│ 0 | B //│ res //│ = B {} test(A()) //│ 0 //│ res //│ = 0 // A <: A | Object & 'a & ~A // A & ~A <: Object & 'a & ~A // Bot <: Object & 'a & ~A :e // TODO implement this syntax :w fun test(x) = refined if x is A then 0 B then 1 //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.342: fun test(x) = refined if x is //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.343: A then 0 //│ ║ ^^^^^^^^^^ //│ ║ l.344: B then 1 //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.342: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined //│ ║ l.342: fun test(x) = refined if x is //│ ╙── ^^^^^^^ //│ fun test: (A | B) -> error //│ Code generation encountered an error: //│ unresolved symbol refined // forall 'a: 'a -> (case 'a of A -> 0, B & ~A -> 1) fun q: (0|1) -> true & (1|2) -> false //│ fun q: (0 | 1) -> true & (1 | 2) -> false q(0) //│ true //│ res //│ = //│ q is not implemented q(0) : true //│ true //│ res //│ = //│ q is not implemented q(1) //│ 'a //│ where //│ [-'a] in {[true], [false]} //│ res //│ = //│ q is not implemented q(1) : Bool //│ Bool //│ res //│ = //│ q is not implemented x => q(x): true //│ (0 | 1) -> true //│ res //│ = //│ q is not implemented x => q(x) //│ forall 'a 'b. 'a -> 'b //│ where //│ [+'a, -'b] in {[0 | 1, true], [1 | 2, false]} //│ res //│ = //│ q is not implemented :e (x => q(x))(1):Int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.410: (x => q(x))(1):Int //│ ║ ^^^^^^^^^^^^^^ //│ ╟── application of type `?a` does not match type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.410: (x => q(x))(1):Int //│ ╙── ^^^ //│ Int //│ res //│ = //│ q is not implemented :e q(1):int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.424: q(1):int //│ ║ ^^^^ //│ ╟── application of type `?a` does not match type `int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.424: q(1):int //│ ╙── ^^^ //│ int //│ res //│ = //│ q is not implemented fun w = x => q(x) //│ fun w: forall 'a 'b. 'a -> 'b //│ where //│ [+'a, -'b] in {[0 | 1, true], [1 | 2, false]} w(0) //│ true //│ res //│ = //│ w and q are not implemented x => (f: forall a: ((0, Int) -> 'a & (1, Str) -> ['a])) => f(0, x) + 1 //│ Int -> (f: (0, Int) -> Int & (1, Str) -> [Int]) -> Int //│ res //│ = [Function: res] fun r: Int -> Int & Bool -> Bool //│ fun r: Int -> Int & Bool -> Bool :e x => r(r(x)) //│ ╔══[ERROR] ambiguous //│ ╟── cannot determine satisfiability of type ?a //│ ║ l.457: x => r(r(x)) //│ ╙── ^^^^ //│ forall 'a 'b 'c. 'a -> 'c //│ where //│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} //│ [-'c, +'b] in {[Int, Int], [Bool, Bool]} //│ res //│ = //│ r is not implemented r(r(0)) //│ Int //│ res //│ = //│ r is not implemented x => r(r(x))+1 //│ Int -> Int //│ res //│ = //│ r is not implemented fun u: {x:0, y:Int} -> Int & {x:1, z: Str} -> Str //│ fun u: {x: 0, y: Int} -> Int & {x: 1, z: Str} -> Str (a, b, c) => u({x: a, y: b, z: c}) //│ forall 'a 'b 'c 'd. ('a, 'c, 'd) -> 'b //│ where //│ [-'b, +'a, +'c, +'d] in {[Int, 0, Int, anything], [Str, 1, anything, Str]} //│ res //│ = //│ u is not implemented (a, b) => u({x: a, y: "abc", z: b}) //│ (1, Str) -> Str //│ res //│ = //│ u is not implemented fun s: Str -> Str & AA -> AA //│ fun s: Str -> Str & AA -> AA :e let g = x => s(r(x)) //│ ╔══[ERROR] ambiguous //│ ╟── cannot determine satisfiability of type ?a //│ ║ l.504: let g = x => s(r(x)) //│ ╙── ^^^^ //│ let g: forall 'a 'b 'c. 'a -> 'c //│ where //│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} //│ [+'b, -'c] in {[Str, Str], [AA, AA]} //│ g //│ = //│ s is not implemented :e fun g(x) = s(r(x)) //│ ╔══[ERROR] ambiguous //│ ╟── cannot determine satisfiability of type ?a //│ ║ l.518: fun g(x) = s(r(x)) //│ ╙── ^^^^ //│ fun g: forall 'a 'b 'c. 'a -> 'c //│ where //│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} //│ [-'c, +'b] in {[Str, Str], [AA, AA]} :e x => s(r(x)) //│ ╔══[ERROR] ambiguous //│ ╟── cannot determine satisfiability of type ?a //│ ║ l.529: x => s(r(x)) //│ ╙── ^^^^ //│ forall 'a 'b 'c. 'a -> 'c //│ where //│ [+'a, -'b] in {[Int, Int], [Bool, Bool]} //│ [-'c, +'b] in {[Str, Str], [AA, AA]} //│ res //│ = //│ s is not implemented :e g(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.543: g(0) //│ ║ ^^^^ //│ ╟── expression of type `Int` does not match type `?a` //│ ╟── Note: constraint arises from application: //│ ║ l.518: fun g(x) = s(r(x)) //│ ╙── ^^^^ //│ error //│ res //│ = //│ g and s are not implemented fun rt: {0: Int} -> Int & {0: Str} -> Str //│ fun rt: {0: Int} -> Int & {0: Str} -> Str rt([1,"str"]) //│ Int //│ res //│ = //│ rt is not implemented rt(["str",1]) //│ Str //│ res //│ = //│ rt is not implemented fun app2: ('a -> 'a -> 'a) -> 'a -> 'a //│ fun app2: forall 'a. ('a -> 'a -> 'a) -> 'a -> 'a fun snd: A -> Int -> Int & Str -> Str -> Str //│ fun snd: A -> Int -> Int & Str -> Str -> Str :e x => app2(snd)(x):Int //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.578: x => app2(snd)(x):Int //│ ║ ^^^^^^^^^^^^ //│ ╟── type `Int` is not an instance of type `A` //│ ║ l.571: fun app2: ('a -> 'a -> 'a) -> 'a -> 'a //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.574: fun snd: A -> Int -> Int & Str -> Str -> Str //│ ╙── ^ //│ nothing -> Int //│ res //│ = //│ app2 is not implemented fun app2_ (f:'a -> 'a -> 'a)(x) = f(x)(x) //│ fun app2_: forall 'a. (f: 'a -> 'a -> 'a) -> 'a -> 'a app2_(snd) //│ 'a -> 'b //│ where //│ 'a <: 'b //│ [-'b, -'a, +'a] in {[Int, Int, A & Int], [Str, Str, Str]} //│ res //│ = //│ snd is not implemented // * Example from WeirdUnions.mls. // * This type merges the input tuples: fun f: (Str => Str) & ((Str, Int) => Str) //│ fun f: (...Array[Int | Str] & {0: Str}) -> Str f("abc", "abc") //│ Str //│ res //│ = //│ f is not implemented fun f: (Str => Str) & ((Str, Int) => Int) //│ fun f: Str -> Str & (Str, Int) -> Int // * Different from WeirdUnions.mls: :e f("abc", "abc") //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.621: f("abc", "abc") //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type `Str -> Str & (Str, Int) -> Int` is not an instance of `("abc", "abc") -> ?a` //│ ║ l.616: fun f: (Str => Str) & ((Str, Int) => Int) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `("abc", "abc") -> ?a` //│ ║ l.621: f("abc", "abc") //│ ╙── ^ //│ error //│ res //│ = //│ f is not implemented f("abcabc") //│ Str //│ res //│ = //│ f is not implemented :e x => rt([not(x)]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.643: x => rt([not(x)]) //│ ║ ^^^^^^^^^^^^ //│ ╟── application of type `Bool` does not match type `?a` //│ ║ l.643: x => rt([not(x)]) //│ ╙── ^^^^^^ //│ forall 'a 'b. Bool -> 'a //│ where //│ 'b :> Bool //│ 'a :> error //│ [-'a, +'b] in {} //│ res //│ = //│ rt is not implemented :e rt(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.660: rt(0) //│ ║ ^^^^^ //│ ╟── type `{0: Int} -> Int & {0: Str} -> Str` is not an instance of `0 -> ?a` //│ ║ l.556: fun rt: {0: Int} -> Int & {0: Str} -> Str //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `0 -> ?a` //│ ║ l.660: rt(0) //│ ╙── ^^ //│ error //│ res //│ = //│ rt is not implemented fun z: {0:Int} -> nothing & Str -> Str //│ fun z: {0: Int} -> nothing & Str -> Str z([1]) //│ nothing //│ res //│ = //│ z is not implemented ================================================ FILE: shared/src/test/diff/nu/Huawei1.mls ================================================ :NewDefs class C[out A](x: A) { fun foo = x } //│ class C[A](x: A) { //│ fun foo: A //│ } let c = C(123) //│ let c: C[123] //│ c //│ = C {} class B //│ class B { //│ constructor() //│ } fun bar(c) = if c is C(y) then y B then 0 //│ fun bar: forall 'a. (B | C['a]) -> (0 | 'a) bar(c) //│ 0 | 123 //│ res //│ = 123 fun bar(c) = if c is C(y) then y + 1 B then 0 else 1 //│ fun bar: (C[Int] | Object & ~#C) -> Int bar(c) //│ Int //│ res //│ = 124 :e bar(C(true)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: bar(C(true)) //│ ║ ^^^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Int` //│ ║ l.43: bar(C(true)) //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.32: C(y) then y + 1 //│ ║ ^ //│ ╟── from field selection: //│ ║ l.4: class C[out A](x: A) { //│ ║ ^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.4: class C[out A](x: A) { //│ ╙── ^ //│ Int | error //│ res //│ = 2 ================================================ FILE: shared/src/test/diff/nu/ImplicitMethodPolym.mls ================================================ :NewDefs module M { fun id1(x) = x } //│ module M { //│ fun id1: forall 'a. 'a -> 'a //│ } M.id1 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id1] M.id1(true) //│ true //│ res //│ = true M.id1(0) //│ 0 //│ res //│ = 0 module M { fun id1(x) = x let _ = id1(0) } //│ module M { //│ let _: 0 //│ fun id1: forall 'a. 'a -> 'a //│ } M.id1 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id1] // :d mixin Mx { fun id1(x) = x } //│ mixin Mx() { //│ fun id1: 'a -> 'a //│ } // * Note: the order of freshening matters! // * if TV freshened transitively from traversing the `this` refinement at a lower ctx level, // * as in the case below, the result is different. module M extends Mx { val r = this.id1(0) } //│ module M { //│ fun id1: forall 'a. ('b & 'a) -> (0 | 'a) //│ val r: 0 | 'b //│ } mixin Mx { fun id1(x) = this.id2(x) } //│ mixin Mx() { //│ this: {id2: 'a -> 'b} //│ fun id1: 'a -> 'b //│ } :e module M extends Mx { this.id1(0) } //│ ╔══[ERROR] Type `#M & {id1: ?a -> ?b}` does not contain member `id2` //│ ║ l.61: fun id1(x) = this.id2(x) //│ ╙── ^^^^ //│ module M { //│ fun id1: anything -> error //│ } //│ Runtime error: //│ TypeError: qualifier1.id2 is not a function :e module M extends Mx { fun id2(x) = [x, x] this.id1(0) } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.61: fun id1(x) = this.id2(x) //│ ╙── ^^^^ //│ module M { //│ fun id1: anything -> error //│ fun id2: forall 'a. 'a -> ['a, 'a] //│ } // * Notice that `id1` is no longer generalized! module M extends Mx { fun id2: 'a => ['a, 'a] fun id2(x) = [x, x] let _ = this.id1(0) } //│ module M { //│ let _: [0 | 'a, 0 | 'a] //│ fun id1: 'a -> [0 | 'a, 0 | 'a] //│ fun id2: forall 'a0. 'a0 -> ['a0, 'a0] //│ } :e // FIXME class C { virtual fun id1(x) = x fun f = [this.id1(true), this.id1(0)] fun id2(x) = x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.111: fun f = [this.id1(true), this.id1(0)] //│ ╙── ^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.111: fun f = [this.id1(true), this.id1(0)] //│ ╙── ^^^^ //│ class C { //│ constructor() //│ fun f: [error, error] //│ fun id1: forall 'a. 'a -> 'a //│ fun id2: forall 'b. 'b -> 'b //│ } // TODO support // :d module M extends C { this.id2(true) } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.130: this.id2(true) //│ ╙── ^^^^ //│ ╔══[ERROR] Cannot access `this` during object initialization //│ ║ l.130: this.id2(true) //│ ╙── ^^^^ //│ module M extends C { //│ fun f: [error, error] //│ fun id1: forall 'a. 'a -> 'a //│ fun id2: forall 'b. 'b -> 'b //│ } // TODO support module M extends C { fun g = (this.id2(true), this.id2(0)) } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.146: fun g = (this.id2(true), this.id2(0)) //│ ╙── ^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.146: fun g = (this.id2(true), this.id2(0)) //│ ╙── ^^^^ //│ module M extends C { //│ fun f: [error, error] //│ fun g: error //│ fun id1: forall 'a. 'a -> 'a //│ fun id2: forall 'b. 'b -> 'b //│ } M.id1 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id1] M.id2 //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id2] M.f //│ [error, error] //│ res //│ = [ true, 0 ] M.g //│ error //│ res //│ = 0 :e module M extends C { fun id1 = succ } //│ ╔══[ERROR] Type mismatch in definition of method id1: //│ ║ l.184: fun id1 = succ //│ ║ ^^^^^^^^^^ //│ ╙── variable of type `?a` is not an instance of type `Int` //│ ╔══[ERROR] Type mismatch in definition of method id1: //│ ║ l.184: fun id1 = succ //│ ║ ^^^^^^^^^^ //│ ╟── expression of type `Int` does not match type `?a` //│ ╟── Note: constraint arises from reference: //│ ║ l.110: virtual fun id1(x) = x //│ ╙── ^ //│ module M extends C { //│ fun f: [error, error] //│ fun id1: Int -> Int //│ fun id2: forall 'a. 'a -> 'a //│ } M.id1 //│ Int -> Int //│ res //│ = [Function: succ] (M : C).id1(false) //│ false //│ res //│ = 1 // FIXME? parsing/semantics of this, currently treated as a named tuple... (M: C) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.216: (M: C) //│ ╙── ^^^^ //│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword //│ ║ l.216: (M: C) //│ ╙── ^ //│ () -> C //│ res //│ = [class C] type Option[A] = Some[A] | None class Some[out A](val value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None // * TODO support semantics but reject at typing: // * polymophism should be blocked by mutation from distributing/refreshing module M { mut val m = None fun oops(x) = set m = x } //│ module M { //│ mut val m: None //│ fun oops: None -> () //│ } :e module M { mut val m = None fun oops(x) = set this.m = x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.253: fun oops(x) = set this.m = x //│ ╙── ^^ //│ module M { //│ mut val m: None //│ fun oops: error -> () //│ } module M[A] { mut val m: Option[A] m = None fun oops(x) = set this.m = x } //│ module M[A] { //│ mut val m: Option[A] //│ fun oops: Option[A] -> () //│ } M.m //│ Option['A] //│ res //│ = None { class: [class None] } :re // * FIXME (codegen) member definition should inherit mutability of declaration M.oops(Some(123)) //│ () //│ res //│ Runtime error: //│ TypeError: Cannot set property m of # which has only a getter M.m //│ Option['A] //│ res //│ = None { class: [class None] } module M[A] { mut val m: Option[A] mut val m = None fun oops(x) = set this.m = x } //│ module M[A] { //│ mut val m: Option[A] //│ fun oops: Option[A] -> () //│ } M.m M.oops(Some(123)) M.m //│ Option['A] //│ res //│ = None { class: [class None] } //│ res //│ = undefined //│ res //│ = Some {} module M[A] { mut val m = None } //│ module M[A] { //│ mut val m: None //│ } M.m //│ None //│ res //│ = None { class: [class None] } set M.m = None //│ () //│ res //│ = undefined :e set M.m = Some(1) //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.331: set M.m = Some(1) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── application of type `Some[?A]` is not an instance of `None` //│ ║ l.331: set M.m = Some(1) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.314: mut val m = None //│ ║ ^^^^ //│ ╟── from assigned selection: //│ ║ l.331: set M.m = Some(1) //│ ╙── ^^^ //│ () //│ res //│ = undefined // * Another possible syntax, which we didn't go with (for now?) :e module M { mut val m = None fun oops(x) = m := x } //│ ╔══[ERROR] identifier not found: := //│ ║ l.353: fun oops(x) = m := x //│ ╙── ^^ //│ module M { //│ mut val m: None //│ fun oops: anything -> error //│ } //│ Code generation encountered an error: //│ unresolved symbol := ================================================ FILE: shared/src/test/diff/nu/InferredInheritanceTypeArgs.mls ================================================ :NewDefs mixin Test[A] { fun bar: [A, A] fun bar = [this.a, this.a] } //│ mixin Test[A]() { //│ this: {a: A} //│ fun bar: [A, A] //│ } class A(val a: Int) extends Test //│ class A(a: Int) { //│ fun bar: [Int, Int] //│ } :e class A(a: Int) extends Test //│ ╔══[ERROR] Parameter 'a' cannot be accessed as a field //│ ║ l.6: fun bar = [this.a, this.a] //│ ╙── ^^ //│ ╔══[ERROR] Parameter 'a' cannot be accessed as a field //│ ║ l.6: fun bar = [this.a, this.a] //│ ╙── ^^ //│ class A(a: Int) { //│ fun bar: [Int, Int] //│ } mixin Test2[S, T] { fun x: [S, T] fun x = [this.s, this.t] fun fb: S => [S, S] fun fb(h: S) = [this.s, h] } //│ mixin Test2[S, T]() { //│ this: {s: S, t: T} //│ fun fb: S -> [S, S] //│ fun x: [S, T] //│ } class A1[B](val s: Bool, val t: B) extends Test2[Bool, B] //│ class A1[B](s: Bool, t: B) { //│ fun fb: ('S & Bool) -> [Bool | 'S, Bool | 'S] //│ fun x: [Bool | 'S, B] //│ } // * TODO: Investigate type of fb class A2[A](val s: A, val t: Int) extends Test2 //│ class A2[A](s: A, t: Int) { //│ fun fb: 'S -> [A | 'S, A | 'S] //│ fun x: [A | 'S, Int] //│ } // * TODO: Investigate type of fb class A3(val s: Int, val t: Bool) extends Test2 //│ class A3(s: Int, t: Bool) { //│ fun fb: 'S -> [Int | 'S, Int | 'S] //│ fun x: [Int | 'S, Bool] //│ } class P(val p: Int) { virtual fun foo(x) = x + p } //│ class P(p: Int) { //│ fun foo: Int -> Int //│ } :e // TODO improve type checking class C1(a: Int) extends P(a) { fun bar = this.foo(0) } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.70: class C1(a: Int) extends P(a) { fun bar = this.foo(0) } //│ ╙── ^^^^ //│ class C1(a: Int) extends P { //│ fun bar: error //│ fun foo: Int -> Int //│ } :e // TODO improve type checking class C2(a: Int, b: Int) extends P(a + b) { fun foo(x) = x * this.p + a * b } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.81: fun foo(x) = x * this.p + a * b //│ ╙── ^^ //│ class C2(a: Int, b: Int) extends P { //│ fun foo: Int -> Int //│ } let c2 = C2(1, 2) //│ let c2: C2 //│ c2 //│ = C2 {} c2.foo(2) //│ Int //│ res //│ = 8 c2.p //│ Int //│ res //│ = 3 class Test[A](val x: A) //│ class Test[A](x: A) class A(a: Int) extends Test(a) //│ class A(a: Int) extends Test let a1 = A(1) //│ let a1: A //│ a1 //│ = A {} a1: Test['x] //│ Test['x] //│ where //│ 'x :> Int //│ res //│ = A {} a1.x //│ Int //│ res //│ = 1 trait Foo[A] { fun foo(x: A): A } //│ trait Foo[A] { //│ fun foo: (x: A) -> A //│ } // * This is pretty funky but it seems sound for now... // * Inherited Foo's type arg is left "unspecified", and since it is not constrained, // * it can be instantiated to any type by downstream callers of the methods! module B extends Foo { fun foo(x) = x } //│ module B extends Foo { //│ fun foo: forall 'a. 'a -> 'a //│ } B : Foo['X] //│ Foo['X] //│ res //│ = B { class: [class B extends Object] } B.foo //│ forall 'a. 'a -> 'a //│ res //│ = [Function: foo] B.foo(1) //│ 1 //│ res //│ = 1 module B extends Foo { fun foo(x) = x + 1 } //│ module B extends Foo { //│ fun foo: Int -> Int //│ } B : Foo['X] //│ Foo[Int] //│ res //│ = B { class: [class B extends Object] } B.foo //│ Int -> Int //│ res //│ = [Function: foo] // * TODO: when :pe trait Foo[type A] { fun foo(x: A): A } //│ ╔══[PARSE ERROR] Unexpected 'type' keyword here //│ ║ l.178: trait Foo[type A] { fun foo(x: A): A } //│ ╙── ^^^^ //│ trait Foo { //│ fun foo: (x: A) -> A //│ } trait Foo[A] { fun a: A; fun foo(x: A): A } //│ trait Foo[A] { //│ fun a: A //│ fun foo: (x: A) -> A //│ } class Bar[out B](val a: B) extends Foo { fun foo(x) = x } //│ class Bar[B](a: B) extends Foo { //│ fun foo: forall 'a. 'a -> 'a //│ } let b = Bar(123) //│ let b: Bar[123] //│ b //│ = Bar {} b : Foo['X] //│ Foo['X] //│ where //│ 'X :> 123 //│ res //│ = Bar {} b.foo //│ forall 'a. 'a -> 'a //│ res //│ = [Function: foo] // * Note the shadowed type variable `A` in `foo` trait Foo[A] { fun foo[A](x: A): A } //│ trait Foo[A] { //│ fun foo: forall 'A. (x: 'A) -> 'A //│ } class B extends Foo { fun foo(x) = x } //│ class B extends Foo { //│ constructor() //│ fun foo: forall 'a. 'a -> 'a //│ } :e class B extends Foo { fun foo(x) = x + 1 } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `A` is not an instance of type `Int` //│ ║ l.217: trait Foo[A] { fun foo[A](x: A): A } //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` does not match type `A` //│ ║ l.229: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from method type parameter: //│ ║ l.217: trait Foo[A] { fun foo[A](x: A): A } //│ ╙── ^ //│ class B extends Foo { //│ constructor() //│ fun foo: Int -> Int //│ } ================================================ FILE: shared/src/test/diff/nu/InheritanceLevelMismatches.mls ================================================ :NewDefs :NoJS // TODO trait T1 { fun x: 0 | 1 } //│ trait T1 { //│ fun x: 0 | 1 //│ } module Foo { trait T2 { fun x: 1 | 2 } class C extends T1, T2 { fun x = 1 } } //│ module Foo { //│ class C extends T1, T2 { //│ constructor() //│ fun x: 1 //│ } //│ trait T2 { //│ fun x: 1 | 2 //│ } //│ } mixin Foo { fun f = this.x } //│ mixin Foo() { //│ this: {x: 'x} //│ fun f: 'x //│ } module Bar { class C(val x: Int) extends Foo } //│ module Bar { //│ class C(x: Int) { //│ fun f: Int //│ } //│ } ================================================ FILE: shared/src/test/diff/nu/IntLit.mls ================================================ :NewDefs // Decimal literals. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [-0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] //│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] //│ res //│ = [ //│ 0, 1, 2, 3, 4, //│ 5, 6, 7, 8, 9, //│ 10 //│ ] //│ res //│ = [ //│ 0, -1, -2, -3, -4, //│ -5, -6, -7, -8, -9, //│ -10 //│ ] // Hexadecimal literals. [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A] [-0x00, -0x01, -0x02, -0x03, -0x04, -0x05, -0x06, -0x07, -0x08, -0x09, -0x0A] //│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] //│ res //│ = [ //│ 0, 1, 2, 3, 4, //│ 5, 6, 7, 8, 9, //│ 10 //│ ] //│ res //│ = [ //│ 0, -1, -2, -3, -4, //│ -5, -6, -7, -8, -9, //│ -10 //│ ] // Octal literals. [0o00, 0o01, 0o02, 0o03, 0o04, 0o05, 0o06, 0o07, 0o10, 0o11, 0o12] [-0o00, -0o01, -0o02, -0o03, -0o04, -0o05, -0o06, -0o07, -0o10, -0o11, -0o12] //│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] //│ res //│ = [ //│ 0, 1, 2, 3, 4, //│ 5, 6, 7, 8, 9, //│ 10 //│ ] //│ res //│ = [ //│ 0, -1, -2, -3, -4, //│ -5, -6, -7, -8, -9, //│ -10 //│ ] // Binary literals. [0b0000, 0b0001, 0b0010, 0b0011, 0b0100, 0b0101, 0b0110, 0b0111, 0b1000, 0b1001, 0b1010] [-0b0, -0b1, -0b10, -0b11, -0b100, -0b101, -0b110, -0b111, -0b1000, -0b1001, -0b1010] //│ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10] //│ res //│ = [ //│ 0, 1, 2, 3, 4, //│ 5, 6, 7, 8, 9, //│ 10 //│ ] //│ res //│ = [ //│ 0, -1, -2, -3, -4, //│ -5, -6, -7, -8, -9, //│ -10 //│ ] :pe 0b //│ ╔══[LEXICAL ERROR] Expect at least one binary digit //│ ║ l.72: 0b //│ ╙── ^^ //│ 0 //│ res //│ = 0 :pe 0x //│ ╔══[LEXICAL ERROR] Expect at least one hexadecimal digit //│ ║ l.81: 0x //│ ╙── ^^ //│ 0 //│ res //│ = 0 :pe 0o //│ ╔══[LEXICAL ERROR] Expect at least one octal digit //│ ║ l.90: 0o //│ ╙── ^^ //│ 0 //│ res //│ = 0 // Underscores as separators in numeric literals. 1_000_000 0xDEAD_CAFE 0b0010_0100_0011_0100 //│ 9268 //│ res //│ = 1000000 //│ res //│ = 3735931646 //│ res //│ = 9268 // Trailing underscores are not allowed. :w 0b00000000010_ 0xCAFE_____ //│ ╔══[WARNING] Trailing separator is not allowed //│ ║ l.112: 0b00000000010_ //│ ╙── ^ //│ ╔══[WARNING] Trailing separator is not allowed //│ ║ l.113: 0xCAFE_____ //│ ╙── ^ //│ 51966 //│ res //│ = 2 //│ res //│ = 51966 // Extra leading zeros are discarded. 0123 023456 00 0 //│ 0 //│ res //│ = 123 //│ res //│ = 23456 //│ res //│ = 0 //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/nu/InterfaceGeneric.mls ================================================ :NewDefs :NoJS trait Into[T] { fun Into: T } //│ trait Into[T] { //│ fun Into: T //│ } trait Nat extends Into[Int] //│ trait Nat extends Into { //│ fun Into: 'T //│ } //│ where //│ 'T := Int trait Product[A, B] extends Into[A] { val pair: [A, B] } //│ trait Product[A, B] extends Into { //│ fun Into: 'T //│ val pair: [A, B] //│ } //│ where //│ 'T := A class TwoInts(val pair: [Int, Int]) extends Product[Int, Int] { fun Into = pair.0 + pair.1 } //│ class TwoInts(pair: [Int, Int]) extends Into, Product { //│ fun Into: Int //│ } let i2 = TwoInts([1, 2]) //│ let i2: TwoInts i2: Product[Int, Int] //│ Product[Int, Int] i2: Into[Int] //│ Into[Int] i2.pair //│ [Int, Int] i2.Into //│ Int val p1: Product[Int, Int] //│ val p1: Product[Int, Int] :e p1: Product[Bool, Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.54: p1: Product[Bool, Int] //│ ║ ^^ //│ ╙── expression of type `Int` does not match type `Bool` //│ Product[Bool, Int] p1: Into[Int] //│ Into[Int] :e p1: Into[Bool] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.65: p1: Into[Bool] //│ ║ ^^ //│ ╙── expression of type `Int` does not match type `Bool` //│ Into[Bool] ================================================ FILE: shared/src/test/diff/nu/InterfaceMono.mls ================================================ :NewDefs trait Showable { fun toString: Str } //│ trait Showable { //│ fun toString: Str //│ } :e trait What0 extends woooo //│ ╔══[ERROR] Could not find definition `woooo` //│ ║ l.12: trait What0 extends woooo //│ ╙── ^^^^^ //│ trait What0 class PoInt(x: Int, y: Int) extends Showable { fun mlen = x + y fun toString = "I'm a poInt" } //│ class PoInt(x: Int, y: Int) extends Showable { //│ fun mlen: Int //│ fun toString: "I'm a poInt" //│ } class What1(val toString: Str) extends Showable //│ class What1(toString: Str) extends Showable :e trait NoShow extends What1("hi") //│ ╔══[ERROR] A trait can only inherit from other traits //│ ║ l.31: trait NoShow extends What1("hi") //│ ╙── ^^^^^^^^^^^ //│ trait NoShow extends Showable, What1 :e class ErrC1 extends Showable class ErrC2 extends Showable { fun toString = 114 } class ErrC3(val toString: Str -> Str) extends Showable //│ ╔══[ERROR] Member `toString` is declared (or its declaration is inherited) but is not implemented in `ErrC1` //│ ║ l.38: class ErrC1 extends Showable //│ ║ ^^^^^ //│ ╟── Declared here: //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method toString: //│ ║ l.40: fun toString = 114 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── integer literal of type `114` is not an instance of type `Str` //│ ║ l.40: fun toString = 114 //│ ║ ^^^ //│ ╟── but it flows into definition of method toString with expected type `Str` //│ ║ l.40: fun toString = 114 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun toString: Str //│ ║ ^^^ //│ ╟── from signature of member `toString`: //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in function type: //│ ║ l.42: class ErrC3(val toString: Str -> Str) extends Showable //│ ║ ^^^^^^^^^^ //│ ╟── type `Str -> Str` is not an instance of type `Str` //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun toString: Str //│ ║ ^^^ //│ ╟── from signature of member `toString`: //│ ║ l.5: fun toString: Str //│ ╙── ^^^^^^^^^^^^^ //│ class ErrC1 extends Showable { //│ constructor() //│ fun toString: Str //│ } //│ class ErrC2 extends Showable { //│ constructor() //│ fun toString: 114 //│ } //│ class ErrC3(toString: Str -> Str) extends Showable trait Stadt { val name: Str } //│ trait Stadt { //│ val name: Str //│ } trait RefinedStadt extends Stadt { val size: Int fun foo: Bool -> Int } //│ trait RefinedStadt extends Stadt { //│ fun foo: Bool -> Int //│ val name: Str //│ val size: Int //│ } trait SizedStadt extends RefinedStadt { val size: 1 | 2 | 3 fun bar: Int -> Int } //│ trait SizedStadt extends RefinedStadt, Stadt { //│ fun bar: Int -> Int //│ fun foo: Bool -> Int //│ val name: Str //│ val size: 1 | 2 | 3 //│ } class Goodstatt(val size: 1 | 2) extends RefinedStadt { val name = "good" fun bar(x) = x fun foo(t) = if t && true then this.size else 0 } //│ class Goodstatt(size: 1 | 2) extends RefinedStadt, Stadt { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: Bool -> (0 | 1 | 2) //│ val name: "good" //│ } :e class Errcity(val size: Int) extends SizedStadt { fun bar = "hahaha" } //│ ╔══[ERROR] Type mismatch in definition of method bar: //│ ║ l.125: fun bar = "hahaha" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── string literal of type `"hahaha"` is not a function //│ ║ l.125: fun bar = "hahaha" //│ ║ ^^^^^^^^ //│ ╟── but it flows into definition of method bar with expected type `Int -> Int` //│ ║ l.125: fun bar = "hahaha" //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from function type: //│ ║ l.103: fun bar: Int -> Int //│ ║ ^^^^^^^^^^ //│ ╟── from signature of member `bar`: //│ ║ l.103: fun bar: Int -> Int //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.124: class Errcity(val size: Int) extends SizedStadt { //│ ║ ^^^ //│ ╟── type `Int` does not match type `1 | 2 | 3` //│ ╟── Note: constraint arises from union type: //│ ║ l.102: val size: 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `size`: //│ ║ l.102: val size: 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `name` is declared (or its declaration is inherited) but is not implemented in `Errcity` //│ ║ l.124: class Errcity(val size: Int) extends SizedStadt { //│ ║ ^^^^^^^ //│ ╟── Declared here: //│ ║ l.85: val name: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `Errcity` //│ ║ l.124: class Errcity(val size: Int) extends SizedStadt { //│ ║ ^^^^^^^ //│ ╟── Declared here: //│ ║ l.93: fun foo: Bool -> Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ class Errcity(size: Int) extends RefinedStadt, SizedStadt, Stadt { //│ fun bar: "hahaha" //│ fun foo: Bool -> Int //│ val name: Str //│ } module Omg extends Stadt { fun name = "omg!!!" fun cool(x) = x + x } //│ module Omg extends Stadt { //│ fun cool: Int -> Int //│ fun name: "omg!!!" //│ } mixin More { fun more(x) = x == 1 fun size = 1 fun bar(x) = x } //│ mixin More() { //│ fun bar: 'a -> 'a //│ fun more: Num -> Bool //│ fun size: 1 //│ } mixin Fooo { fun foo(x) = 0 } //│ mixin Fooo() { //│ fun foo: anything -> 0 //│ } class Grassberg(val name: "grass" | "GRASS") extends More, SizedStadt, Fooo //│ class Grassberg(name: "GRASS" | "grass") extends RefinedStadt, SizedStadt, Stadt { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: anything -> 0 //│ fun more: Num -> Bool //│ fun size: 1 //│ } :e class Dirtberg extends More, SizedStadt, Fooo { let name = "dirt" fun size = 4 // this should not check } //│ ╔══[ERROR] Cannot implement value member 'name' with a let binding //│ ║ l.207: let name = "dirt" //│ ║ ^^^^^^^^^^^^^ //│ ╟── Originally declared here: //│ ║ l.85: val name: Str //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method size: //│ ║ l.208: fun size = 4 // this should not check //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `4` does not match type `1 | 2 | 3` //│ ║ l.208: fun size = 4 // this should not check //│ ║ ^ //│ ╟── but it flows into definition of method size with expected type `1 | 2 | 3` //│ ║ l.208: fun size = 4 // this should not check //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.102: val size: 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `size`: //│ ║ l.102: val size: 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `name` is declared (or its declaration is inherited) but is not implemented in `Dirtberg` //│ ║ l.206: class Dirtberg extends More, SizedStadt, Fooo { //│ ║ ^^^^^^^^ //│ ╟── Declared here: //│ ║ l.85: val name: Str //│ ║ ^^^^^^^^^^^^^ //│ ╟── Note: value member `name` is private and cannot be used as a valid implementation //│ ║ l.207: let name = "dirt" //│ ╙── ^^^^^^^^^^^^^ //│ class Dirtberg extends RefinedStadt, SizedStadt, Stadt { //│ constructor() //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: anything -> 0 //│ fun more: Num -> Bool //│ let name: "dirt" //│ fun size: 4 //│ } new Dirtberg().size //│ 4 //│ res //│ = 4 class Iceburg(val name: Str) extends RefinedStadt, More, Fooo //│ class Iceburg(name: Str) extends RefinedStadt, Stadt { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: anything -> 0 //│ fun more: Num -> Bool //│ fun size: 1 //│ } class A { virtual fun x: Int = 1 } //│ class A { //│ constructor() //│ fun x: Int //│ } :e class B extends A { fun x = "A" } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.269: class B extends A { fun x = "A" } //│ ║ ^^^^^^^ //│ ╟── string literal of type `"A"` is not an instance of type `Int` //│ ║ l.269: class B extends A { fun x = "A" } //│ ║ ^^^ //│ ╟── but it flows into definition of method x with expected type `Int` //│ ║ l.269: class B extends A { fun x = "A" } //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.262: class A { virtual fun x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of method x: //│ ║ l.262: class A { virtual fun x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class B extends A { //│ constructor() //│ fun x: "A" //│ } :e class C1[A] { virtual fun a: A = this.a } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.291: class C1[A] { virtual fun a: A = this.a } //│ ╙── ^^ //│ class C1[A] { //│ constructor() //│ fun a: A //│ } class C2 extends C1[Int] { fun a = 1 } //│ class C2 extends C1 { //│ constructor() //│ fun a: 1 //│ } // trait MyTrait[A] { type MyTrait#A = A; fun a: A = a } // freshen/subst // trait MyTrait[Int] { type MyTrait#A = Int; fun a: Int = a } trait MyTrait[A] { fun a: A } //│ trait MyTrait[A] { //│ fun a: A //│ } // :ns // :d // :ds class C extends MyTrait[Int] { fun a = 1 } //│ class C extends MyTrait { //│ constructor() //│ fun a: 1 //│ } :e class C extends MyTrait[Int] { fun a = false } //│ ╔══[ERROR] Type mismatch in definition of method a: //│ ║ l.325: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ║ l.325: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^ //│ ╟── but it flows into definition of method a with expected type `Int` //│ ║ l.325: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.325: class C extends MyTrait[Int] { fun a = false } //│ ║ ^^^ //│ ╟── from signature of member `a`: //│ ║ l.310: trait MyTrait[A] { fun a: A } //│ ╙── ^^^^ //│ class C extends MyTrait { //│ constructor() //│ fun a: false //│ } trait T1 { val foo : 1 | 2 | 3 fun bar : Str | Bool } trait T2 { val foo : 2 | 3 | 4 val bar : Int | Bool } //│ trait T1 { //│ fun bar: Str | false | true //│ val foo: 1 | 2 | 3 //│ } //│ trait T2 { //│ val bar: Int | false | true //│ val foo: 2 | 3 | 4 //│ } trait T4 extends T1, T2 { fun foo: 2 } //│ trait T4 extends T1, T2 { //│ fun bar: Bool //│ fun foo: 2 //│ } class C1(val foo: 2, val bar: true) extends T4 //│ class C1(foo: 2, bar: true) extends T1, T2, T4 :e class C3 extends T4{ fun foo = 3 fun bar = false } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.377: fun foo = 3 //│ ║ ^^^^^^^ //│ ╟── integer literal of type `3` does not match type `2` //│ ║ l.377: fun foo = 3 //│ ║ ^ //│ ╟── but it flows into definition of method foo with expected type `2` //│ ║ l.377: fun foo = 3 //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.365: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: //│ ║ l.365: fun foo: 2 //│ ╙── ^^^^^^ //│ class C3 extends T1, T2, T4 { //│ constructor() //│ fun bar: false //│ fun foo: 3 //│ } :e class C2(val foo: Int, val bar: Str) extends T4 //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.402: class C2(val foo: Int, val bar: Str) extends T4 //│ ║ ^^^ //│ ╟── type `Int` does not match type `2` //│ ╟── Note: constraint arises from literal type: //│ ║ l.365: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: //│ ║ l.365: fun foo: 2 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.402: class C2(val foo: Int, val bar: Str) extends T4 //│ ║ ^^^ //│ ╟── type `Str` does not match type `Int | false | true` //│ ╟── Note: constraint arises from union type: //│ ║ l.353: val bar : Int | Bool //│ ║ ^^^^^^^^^^ //│ ╟── from signature of member `bar`: //│ ║ l.353: val bar : Int | Bool //│ ╙── ^^^^^^^^^^^^^^^^ //│ class C2(foo: Int, bar: Str) extends T1, T2, T4 :e trait T5 extends T4 { val foo: 4 } //│ ╔══[ERROR] Type mismatch in signature of member `foo`: //│ ║ l.427: val foo: 4 //│ ║ ^^^^^^ //│ ╟── type `4` does not match type `2` //│ ║ l.427: val foo: 4 //│ ║ ^ //│ ╟── but it flows into signature of member `foo` with expected type `2` //│ ║ l.427: val foo: 4 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.365: fun foo: 2 //│ ║ ^ //│ ╟── from signature of member `foo`: //│ ║ l.365: fun foo: 2 //│ ╙── ^^^^^^ //│ trait T5 extends T1, T2, T4 { //│ fun bar: Bool //│ val foo: 4 //│ } :e trait T3 extends T1, T2 { val foo: true } //│ ╔══[ERROR] Type mismatch in signature of member `foo`: //│ ║ l.451: val foo: true //│ ║ ^^^^^^^^^ //│ ╟── type `true` does not match type `1 | 2 | 3` //│ ║ l.451: val foo: true //│ ║ ^^^^ //│ ╟── but it flows into signature of member `foo` with expected type `1 | 2 | 3` //│ ║ l.451: val foo: true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.348: val foo : 1 | 2 | 3 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `foo`: //│ ║ l.348: val foo : 1 | 2 | 3 //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in signature of member `foo`: //│ ║ l.451: val foo: true //│ ║ ^^^^^^^^^ //│ ╟── type `true` does not match type `2 | 3 | 4` //│ ║ l.451: val foo: true //│ ║ ^^^^ //│ ╟── but it flows into signature of member `foo` with expected type `2 | 3 | 4` //│ ║ l.451: val foo: true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.352: val foo : 2 | 3 | 4 //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `foo`: //│ ║ l.352: val foo : 2 | 3 | 4 //│ ╙── ^^^^^^^^^^^^^^^ //│ trait T3 extends T1, T2 { //│ fun bar: Bool //│ val foo: true //│ } ================================================ FILE: shared/src/test/diff/nu/Interfaces.mls ================================================ :NewDefs trait Test { fun foo: Int fun bar: Bool -> Bool } //│ trait Test { //│ fun bar: Bool -> Bool //│ fun foo: Int //│ } fun ts(x: Test) = x.foo //│ fun ts: (x: Test) -> Int module M extends Test { fun foo = 0 fun bar = not } //│ module M extends Test { //│ fun bar: Bool -> Bool //│ fun foo: 0 //│ } M: Test //│ Test //│ res //│ = M { class: [class M extends Object] } ts(M) //│ Int //│ res //│ = 0 trait Oth extends Test { val a : Int fun cool : Int -> Bool } //│ trait Oth extends Test { //│ val a: Int //│ fun bar: Bool -> Bool //│ fun cool: Int -> Bool //│ fun foo: Int //│ } val oth1: Oth //│ val oth1: Oth //│ oth1 //│ = oth1.bar(true) //│ Bool //│ res //│ = //│ oth1 is not implemented oth1: Test //│ Test //│ res //│ = //│ oth1 is not implemented :e M : Oth oth1: M //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.65: M : Oth //│ ║ ^ //│ ╟── reference of type `M` is not an instance of type `Oth` //│ ╟── Note: constraint arises from type reference: //│ ║ l.65: M : Oth //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.66: oth1: M //│ ║ ^^^^ //│ ╟── type `#Oth` is not an instance of type `M` //│ ║ l.47: val oth1: Oth //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `M` //│ ║ l.66: oth1: M //│ ║ ^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.66: oth1: M //│ ╙── ^ //│ M //│ res //│ = M { class: [class M extends Object] } //│ res //│ = //│ oth1 is not implemented trait Geo { val v: 2 | 3 fun get: Int | Bool fun ter: Int } trait Anemo { val v: 1 | 2 fun get: Bool | string fun ter: Bool } //│ trait Geo { //│ fun get: Int | false | true //│ fun ter: Int //│ val v: 2 | 3 //│ } //│ trait Anemo { //│ fun get: false | string | true //│ fun ter: Bool //│ val v: 1 | 2 //│ } trait Mixed extends Geo, Anemo //│ trait Mixed extends Anemo, Geo { //│ fun get: Bool //│ fun ter: nothing //│ val v: 2 //│ } class C() extends Test { fun foo: 1 = 1 fun bar(x) = x } //│ class C() extends Test { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: 1 //│ } mixin M { fun m1 = 3 } //│ mixin M() { //│ fun m1: 3 //│ } class F() extends Oth, M, Mixed { fun cool(x) = x === 1 fun foo = 2 fun bar(x) = x fun get = true fun ter = ter val a = 3 val v = 2 } //│ class F() extends Anemo, Geo, Mixed, Oth, Test { //│ val a: 3 //│ fun bar: forall 'a. 'a -> 'a //│ fun cool: Eql[1] -> Bool //│ fun foo: 2 //│ fun get: true //│ fun m1: 3 //│ fun ter: nothing //│ val v: 2 //│ } val fi = F() //│ val fi: F //│ fi //│ = F {} fi : Oth & Geo //│ Geo & Oth //│ res //│ = F {} fi.get //│ true //│ res //│ = true fi: Test & Anemo //│ Anemo & Test //│ res //│ = F {} val fog: Oth & Mixed //│ val fog: Mixed & Oth //│ fog //│ = fog: Test & Anemo //│ Anemo & Test //│ res //│ = //│ fog is not implemented :e fog: F //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.190: fog: F //│ ║ ^^^ //│ ╟── type `Mixed & Oth` is not an instance of type `F` //│ ║ l.178: val fog: Oth & Mixed //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `F` //│ ║ l.190: fog: F //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.190: fog: F //│ ╙── ^ //│ F //│ res //│ = //│ fog is not implemented val c = C() //│ val c: C //│ c //│ = C {} c: Eql //│ Eql[C] //│ res //│ = C {} val ct: Test = c //│ val ct: Test //│ ct //│ = C {} c.foo //│ 1 //│ res //│ = 1 c.bar(true) //│ true //│ res //│ = true // :d c: Test //│ Test //│ res //│ = C {} :e c: Oth //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.240: c: Oth //│ ║ ^ //│ ╟── application of type `C` is not an instance of type `Oth` //│ ║ l.208: val c = C() //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `#Oth` //│ ║ l.240: c: Oth //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.240: c: Oth //│ ╙── ^^^ //│ Oth //│ res //│ = C {} // :d val c1: Test = C() //│ val c1: Test //│ c1 //│ = C {} // :d fun fcc(x: C) = x.foo //│ fun fcc: (x: C) -> 1 fun fc(x: Test) = x fun ffm(x: F) = x.get //│ fun fc: (x: Test) -> Test //│ fun ffm: (x: F) -> true :e fun fee(x: Test) = x: Oth //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.273: fun fee(x: Test) = x: Oth //│ ║ ^ //│ ╟── type `#Test` is not an instance of type `Oth` //│ ║ l.273: fun fee(x: Test) = x: Oth //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `#Oth` //│ ║ l.273: fun fee(x: Test) = x: Oth //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.273: fun fee(x: Test) = x: Oth //│ ╙── ^^^ //│ fun fee: (x: Test) -> Oth fc(c) //│ Test //│ res //│ = C {} fun fts['a](x: 'a & Test) = x.foo fts(c) //│ fun fts: (x: Test) -> Int //│ Int //│ res //│ = 1 fts(oth1) //│ Int //│ res //│ = //│ oth1 is not implemented fts(c1) //│ Int //│ res //│ = 1 trait A1 { fun a1: 1 | 2 | 3 } trait A2 { fun a1: 2 | 3 | 4 } //│ trait A1 { //│ fun a1: 1 | 2 | 3 //│ } //│ trait A2 { //│ fun a1: 2 | 3 | 4 //│ } :e class Ea1 extends A1, A2 { fun a1 = 4 } //│ ╔══[ERROR] Type mismatch in definition of method a1: //│ ║ l.322: fun a1 = 4 //│ ║ ^^^^^^ //│ ╟── integer literal of type `4` does not match type `1 | 2 | 3` //│ ║ l.322: fun a1 = 4 //│ ║ ^ //│ ╟── but it flows into definition of method a1 with expected type `1 | 2 | 3` //│ ║ l.322: fun a1 = 4 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.311: trait A1 { fun a1: 1 | 2 | 3 } //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `a1`: //│ ║ l.311: trait A1 { fun a1: 1 | 2 | 3 } //│ ╙── ^^^^^^^^^^^^^ //│ class Ea1 extends A1, A2 { //│ constructor() //│ fun a1: 4 //│ } trait Ele { fun ce: Oth -> Test } //│ trait Ele { //│ fun ce: Oth -> Test //│ } class CE extends Ele { fun ce(x) = x } //│ class CE extends Ele { //│ constructor() //│ fun ce: forall 'a. 'a -> 'a //│ } :e class E1 extends Test { fun foo = 2 } //│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `E1` //│ ║ l.360: class E1 extends Test { //│ ║ ^^ //│ ╟── Declared here: //│ ║ l.6: fun bar: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ class E1 extends Test { //│ constructor() //│ fun bar: Bool -> Bool //│ fun foo: 2 //│ } :e trait TE1 extends C trait TE2 extends M, Test //│ ╔══[ERROR] A trait can only inherit from other traits //│ ║ l.376: trait TE1 extends C //│ ╙── ^ //│ ╔══[ERROR] A trait can only inherit from other traits //│ ║ l.377: trait TE2 extends M, Test //│ ╙── ^ //│ trait TE1 extends C, Test //│ trait TE2 extends Test { //│ fun bar: Bool -> Bool //│ fun foo: Int //│ } :e class E2 extends Test { fun foo = true fun bar(x) = x } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.392: fun foo = true //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Int` //│ ║ l.392: fun foo = true //│ ║ ^^^^ //│ ╟── but it flows into definition of method foo with expected type `Int` //│ ║ l.392: fun foo = true //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun foo: Int //│ ║ ^^^ //│ ╟── from signature of member `foo`: //│ ║ l.5: fun foo: Int //│ ╙── ^^^^^^^^ //│ class E2 extends Test { //│ constructor() //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: true //│ } :e class D extends Test[Int], Test[Bool] //│ ╔══[ERROR] trait Test expects 0 type parameter(s); got 1 //│ ║ l.417: class D extends Test[Int], Test[Bool] //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] trait Test expects 0 type parameter(s); got 1 //│ ║ l.417: class D extends Test[Int], Test[Bool] //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Member `foo` is declared (or its declaration is inherited) but is not implemented in `D` //│ ║ l.417: class D extends Test[Int], Test[Bool] //│ ║ ^ //│ ╟── Declared here: //│ ║ l.5: fun foo: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `D` //│ ║ l.417: class D extends Test[Int], Test[Bool] //│ ║ ^ //│ ╟── Declared here: //│ ║ l.6: fun bar: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^ //│ class D extends Test { //│ constructor() //│ fun bar: Bool -> Bool //│ fun foo: Int //│ } trait Base: A | B class A() extends Base class B() extends Base //│ trait Base: A | B //│ class A() extends Base //│ class B() extends Base val b: Base = A() //│ val b: Base //│ b //│ = A {} b: Base & (A | B) //│ A & Base | B & Base //│ res //│ = A {} if b is A then 0 B then 1 //│ 0 | 1 //│ res //│ = 0 fun f(x: Base) = if x is A then 0 B then 1 //│ fun f: (x: Base) -> (0 | 1) trait Base: Foo | Bar class Foo[A](val aa: [A, A]) extends Base class Bar[B](f: B => B) extends Base //│ trait Base: Bar[?] | Foo[anything] //│ class Foo[A](aa: [A, A]) extends Base //│ class Bar[B](f: B -> B) extends Base val f: Foo = Foo([1, 2]) //│ val f: Foo[anything] //│ f //│ = Foo {} f.aa //│ [??A, ??A] //│ res //│ = [ 1, 2 ] val b: Base = f //│ val b: Base //│ b //│ = Foo {} if b is Foo(a) then a else 0 //│ 0 | [??A, ??A] //│ res //│ = [ 1, 2 ] :e // * Note: an error is raised in this case and not above because B is invariant so it can't be widened if b is Bar(f) then f else 0 //│ ╔══[ERROR] Type error in `case` expression //│ ║ l.503: if b is Bar(f) then f else 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `B` leaks out of its scope //│ ║ l.477: class Bar[B](f: B => B) extends Base //│ ╙── ^ //│ 0 | ??B -> anything //│ res //│ = 0 :e if b is Foo(a) then a Bar(f) then f //│ ╔══[ERROR] Type error in `case` expression //│ ║ l.515: if b is //│ ║ ^^^^ //│ ║ l.516: Foo(a) then a //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.517: Bar(f) then f //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type variable `B` leaks out of its scope //│ ║ l.477: class Bar[B](f: B => B) extends Base //│ ╙── ^ //│ anything //│ res //│ = [ 1, 2 ] :e val tt1 = Test //│ ╔══[ERROR] trait Test cannot be used in term position //│ ║ l.533: val tt1 = Test //│ ╙── ^^^^ //│ val tt1: error //│ Code generation encountered an error: //│ trait used in term position fun mt(x) = if x is Test then 1 else 0 //│ fun mt: nothing -> error trait Geo trait ZL extends Geo trait GL extends Geo trait WP extends ZL, GL trait EM extends WP, Geo //│ trait Geo //│ trait ZL extends Geo //│ trait GL extends Geo //│ trait WP extends GL, Geo, ZL //│ trait EM extends GL, Geo, WP, ZL val g: Geo val z: ZL val w: WP val e: EM //│ val e: EM //│ val g: Geo //│ val w: WP //│ val z: ZL //│ g //│ = //│ z //│ = //│ w //│ = //│ e //│ = fun fot(x: EM): Geo = x fun fit(x: EM): WP = x w: Geo z: Geo e: WP w: ZL & GL e: ZL & Geo //│ fun fot: (x: EM) -> Geo //│ fun fit: (x: EM) -> WP //│ Geo & ZL //│ res //│ = //│ w is not implemented //│ res //│ = //│ z is not implemented //│ res //│ = //│ e is not implemented //│ res //│ = //│ w is not implemented //│ res //│ = //│ e is not implemented :e fun fto(w: WP): EM = w z: WP g: ZL e: ZL & WP //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.601: fun fto(w: WP): EM = w //│ ║ ^ //│ ╟── type `#WP` is not an instance of type `EM` //│ ║ l.601: fun fto(w: WP): EM = w //│ ║ ^^ //│ ╟── but it flows into reference with expected type `#EM` //│ ║ l.601: fun fto(w: WP): EM = w //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.601: fun fto(w: WP): EM = w //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.602: z: WP //│ ║ ^ //│ ╟── type `#ZL` is not an instance of type `WP` //│ ║ l.558: val z: ZL //│ ║ ^^ //│ ╟── but it flows into reference with expected type `#WP` //│ ║ l.602: z: WP //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.602: z: WP //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.603: g: ZL //│ ║ ^ //│ ╟── type `#Geo` is not an instance of type `ZL` //│ ║ l.557: val g: Geo //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `#ZL` //│ ║ l.603: g: ZL //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.603: g: ZL //│ ╙── ^^ //│ fun fto: (w: WP) -> EM //│ WP & ZL //│ res //│ = //│ z is not implemented //│ res //│ = //│ g is not implemented //│ res //│ = //│ e is not implemented class Bs(val a: Bool) { virtual fun foo(x) = x + 1 } //│ class Bs(a: Bool) { //│ fun foo: Int -> Int //│ } class Ih() extends Bs(false) { fun bar(x) = x fun foo(x) = 1 } //│ class Ih() extends Bs { //│ fun bar: forall 'a. 'a -> 'a //│ fun foo: anything -> 1 //│ } val ih1 = Ih() //│ val ih1: Ih //│ ih1 //│ = Ih {} ih1.foo(1) //│ 1 //│ res //│ = 1 ih1: Bs //│ Bs //│ res //│ = Ih {} ih1.a //│ false //│ res //│ = false :e class Eh2 extends Bs(true), Ele { fun foo(x) = x && false fun ce(x) = x } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` is not an instance of type `Bool` //│ ╟── Note: constraint arises from reference: //│ ║ l.691: fun foo(x) = x && false //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` does not match type `Bool` //│ ╟── Note: constraint arises from reference: //│ ║ l.691: fun foo(x) = x && false //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` //│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: //│ ║ l.654: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` //│ ║ l.691: fun foo(x) = x && false //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: //│ ║ l.654: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ class Eh2 extends Bs, Ele { //│ constructor() //│ fun ce: forall 'a. 'a -> 'a //│ fun foo: Bool -> Bool //│ } :e class Eh extends Bs(1) class Eh1 extends Bs class Eh3 extends Bs(false), Test //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.733: class Eh extends Bs(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` //│ ║ l.733: class Eh extends Bs(1) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.653: class Bs(val a: Bool) { //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.733: class Eh extends Bs(1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `1` does not match type `Bool` //│ ║ l.733: class Eh extends Bs(1) //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.653: class Bs(val a: Bool) { //│ ╙── ^^^^ //│ ╔══[ERROR] class Bs expects 1 parameter(s); got 0 //│ ║ l.734: class Eh1 extends Bs //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.654: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── function of type `?a -> (forall ?b. ?b)` is not an instance of type `Int` //│ ║ l.654: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^ //│ ╟── but it flows into definition of method foo with expected type `Int` //│ ║ l.654: virtual fun foo(x) = x + 1 //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.5: fun foo: Int //│ ║ ^^^ //│ ╟── from signature of member `foo`: //│ ║ l.5: fun foo: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Member `bar` is declared (or its declaration is inherited) but is not implemented in `Eh3` //│ ║ l.735: class Eh3 extends Bs(false), Test //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.6: fun bar: Bool -> Bool //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ class Eh extends Bs { //│ constructor() //│ fun foo: Int -> Int //│ } //│ class Eh1 extends Bs { //│ constructor() //│ fun foo: Int -> Int //│ } //│ class Eh3 extends Bs, Test { //│ constructor() //│ fun bar: Bool -> Bool //│ fun foo: Int -> Int //│ } class Ca(a0: Int) extends Oth { virtual val a = a0 fun foo = 1 fun cool(x) = false fun bar(x) = x } //│ class Ca(a0: Int) extends Oth, Test { //│ val a: Int //│ fun bar: forall 'a. 'a -> 'a //│ fun cool: anything -> false //│ fun foo: 1 //│ } class Cx(a2: 1 | 2, val b: Bool) extends Ca(a2) //│ class Cx(a2: 1 | 2, b: Bool) extends Ca, Oth, Test { //│ val a: Int //│ fun bar: forall 'a. 'a -> 'a //│ fun cool: anything -> false //│ fun foo: 1 //│ } class Cx(val a: 1 | 2, val b: Bool) extends Ca(a) //│ class Cx(a: 1 | 2, b: Bool) extends Ca, Oth, Test { //│ fun bar: forall 'a. 'a -> 'a //│ fun cool: anything -> false //│ fun foo: 1 //│ } val cx1 = Cx(2, true) //│ val cx1: Cx //│ cx1 //│ = Cx {} cx1.bar(cx1.b) //│ Bool //│ res //│ = true cx1: Test //│ Test //│ res //│ = Cx {} cx1: Ca //│ Ca //│ res //│ = Cx {} class Bc1(foo: Int) class Bc2(bar: Bool) abstract class Bc3 { val baz : Int } //│ class Bc1(foo: Int) //│ class Bc2(bar: Bool) //│ abstract class Bc3 { //│ val baz: Int //│ } :e class Bc12() extends Bc1(1), Bc2(true) //│ ╔══[ERROR] Cannot inherit from more than one base class: Bc1 and Bc2 //│ ║ l.852: class Bc12() extends Bc1(1), Bc2(true) //│ ╙── ^^^^^^^^^ //│ class Bc12() extends Bc1, Bc2 //│ Code generation encountered an error: //│ unexpected parent symbol new class Bc2. class Bc02() extends Bc1(1 : Int) { val foo = 2 } //│ class Bc02() extends Bc1 { //│ val foo: 2 //│ } Bc02().foo //│ 2 //│ res //│ = 2 :e class Bc31(val baz: Bool) extends Bc3 //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.873: class Bc31(val baz: Bool) extends Bc3 //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.843: val baz : Int //│ ║ ^^^ //│ ╟── from signature of member `baz`: //│ ║ l.843: val baz : Int //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.873: class Bc31(val baz: Bool) extends Bc3 //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.843: val baz : Int //│ ║ ^^^ //│ ╟── from signature of member `baz`: //│ ║ l.843: val baz : Int //│ ╙── ^^^^^^^^^ //│ class Bc31(baz: Bool) extends Bc3 class Bc11 extends Bc1(1) { val foo = true } //│ class Bc11 extends Bc1 { //│ constructor() //│ val foo: true //│ } trait Base[A] { fun f: A -> A } //│ trait Base[A] { //│ fun f: A -> A //│ } class Der1 extends Base[Int] { fun f(x) = x + 1 } //│ class Der1 extends Base { //│ constructor() //│ fun f: Int -> Int //│ } class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [x, y] } //│ class Der2[A, B] extends Base { //│ constructor() //│ fun f: forall 'a 'b. (['a, 'b]) -> ['a, 'b] //│ } :e trait BInt extends Base[Int] { fun f = error } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.924: fun f = error //│ ╙── ^^^^^^^^^^^^^ //│ trait BInt extends Base { //│ fun f: nothing //│ } trait BPar[T] extends Base[[Int, T]] //│ trait BPar[T] extends Base { //│ fun f: 'A -> 'A //│ } //│ where //│ 'A := [Int, T] val bi: BInt val bp: BPar[Bool] //│ val bi: BInt //│ val bp: BPar[Bool] //│ bi //│ = //│ bp //│ = bp: Base[[Int, Bool]] //│ Base[[Int, Bool]] //│ res //│ = //│ bp is not implemented :e bp: Base[[Int, Int]] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.956: bp: Base[[Int, Int]] //│ ║ ^^ //│ ╙── expression of type `true` is not an instance of type `Int` //│ Base[[Int, Int]] //│ res //│ = //│ bp is not implemented bi.f(1) //│ nothing //│ res //│ = //│ bi is not implemented bp.f //│ ([Int, Bool]) -> [Int, Bool] //│ res //│ = //│ bp is not implemented fun fb[T](x: Base[[Int, T]], y: T) = x.f([1, y]) //│ fun fb: forall 'T. (x: Base[[Int, 'T]], y: 'T) -> [Int, 'T] fb(bp, false) //│ [Int, Bool] //│ res //│ = //│ bp is not implemented class CP() extends BPar[Int] { fun f(x) = [x.1, x.0] } //│ class CP() extends BPar, Base { //│ fun f: forall 'a 'b. {0: 'a, 1: 'b} -> ['b, 'a] //│ } val cp1 = CP() //│ val cp1: CP //│ cp1 //│ = CP {} fb(cp1, 2) //│ [Int, Int] //│ res //│ = [ 2, 1 ] trait BInfer1 extends Base //│ trait BInfer1 extends Base { //│ fun f: 'A -> 'A //│ } trait BInfer2 extends Base { fun f: Int -> Int } //│ trait BInfer2 extends Base { //│ fun f: Int -> Int //│ } :e class DerBad1 extends Base[Int, Int] //│ ╔══[ERROR] trait Base expects 1 type parameter(s); got 2 //│ ║ l.1017: class DerBad1 extends Base[Int, Int] //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[ERROR] Member `f` is declared (or its declaration is inherited) but is not implemented in `DerBad1` //│ ║ l.1017: class DerBad1 extends Base[Int, Int] //│ ║ ^^^^^^^ //│ ╟── Declared here: //│ ║ l.905: trait Base[A] { fun f: A -> A } //│ ╙── ^^^^^^^^^^^^^ //│ class DerBad1 extends Base { //│ constructor() //│ fun f: 'A -> 'A //│ } //│ where //│ 'A := Int :e class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╔══[ERROR] Type mismatch in definition of method f: //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `B` does not match type `A` //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: constraint arises from type parameter: //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: type parameter B is defined at: //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method f: //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `A` does not match type `B` //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: constraint arises from type parameter: //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ║ ^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.1035: class Der2[A, B] extends Base[[A, B]] { fun f([x, y]) = [y, x] } //│ ╙── ^ //│ class Der2[A, B] extends Base { //│ constructor() //│ fun f: forall 'a 'b. (['a, 'b]) -> ['b, 'a] //│ } trait Ta[T] { val p: Bool val g: T } class K[A](val k: Ta[A]) //│ trait Ta[T] { //│ val g: T //│ val p: Bool //│ } //│ class K[A](k: Ta[A]) val ta1: Ta[Int] //│ val ta1: Ta[Int] //│ ta1 //│ = val k1 = K(ta1) //│ val k1: K[Int] //│ k1 //│ = //│ ta1 is not implemented k1.k : Ta[Int] //│ Ta[Int] //│ res //│ = //│ k1 and ta1 are not implemented k1.k.g //│ Int //│ res //│ = //│ k1 and ta1 are not implemented k1.k.p //│ Bool //│ res //│ = //│ k1 and ta1 are not implemented :e trait Tb extends Ta[Int] { virtual val p = false } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.1107: virtual val p = false //│ ╙── ^^^^^^^^^^^^^ //│ trait Tb extends Ta { //│ val g: 'T //│ val p: false //│ } //│ where //│ 'T := Int class Ctb extends Tb { val p = false val g = 2 } //│ class Ctb extends Ta, Tb { //│ constructor() //│ val g: 2 //│ val p: false //│ } class G1[A](x: A) //│ class G1[A](x: A) class GI(x2: Int) extends G1[Int](x2) //│ class GI(x2: Int) extends G1 trait Oz { val age: Int } //│ trait Oz { //│ val age: Int //│ } :e class Fischl(val age: Bool) extends Oz //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.1143: class Fischl(val age: Bool) extends Oz //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.1136: val age: Int //│ ║ ^^^ //│ ╟── from signature of member `age`: //│ ║ l.1136: val age: Int //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.1143: class Fischl(val age: Bool) extends Oz //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.1136: val age: Int //│ ║ ^^^ //│ ╟── from signature of member `age`: //│ ║ l.1136: val age: Int //│ ╙── ^^^^^^^^ //│ class Fischl(age: Bool) extends Oz class Klee(val age: 1 | 2 | 3) extends Oz //│ class Klee(age: 1 | 2 | 3) extends Oz class Fate { virtual fun foo(x) = x + 1 } //│ class Fate { //│ constructor() //│ fun foo: Int -> Int //│ } :e class Go extends Fate { fun foo(x) = x && true } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` is not an instance of type `Bool` //│ ╟── Note: constraint arises from reference: //│ ║ l.1179: fun foo(x) = x && true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `Int & ?a` does not match type `Bool` //│ ╟── Note: constraint arises from reference: //│ ║ l.1179: fun foo(x) = x && true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` //│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: //│ ║ l.1170: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Bool` does not match type `Int | ?a` //│ ║ l.1179: fun foo(x) = x && true //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from operator application: //│ ║ l.1170: virtual fun foo(x) = x + 1 //│ ╙── ^^^^^ //│ class Go extends Fate { //│ constructor() //│ fun foo: Bool -> Bool //│ } class Ha { virtual val x: Int = 1 } //│ class Ha { //│ constructor() //│ val x: Int //│ } class Haha(x: 1 | 2) extends Ha //│ class Haha(x: 1 | 2) extends Ha :e class Ohhh(x: Bool) extends Ha //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.1228: class Ohhh(x: Bool) extends Ha //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.1218: class Ha { virtual val x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of value x: //│ ║ l.1218: class Ha { virtual val x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.1228: class Ohhh(x: Bool) extends Ha //│ ║ ^^^^ //│ ╟── type `Bool` is not an instance of type `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.1218: class Ha { virtual val x: Int = 1 } //│ ║ ^^^ //│ ╟── from definition of value x: //│ ║ l.1218: class Ha { virtual val x: Int = 1 } //│ ╙── ^^^^^^^^^^ //│ class Ohhh(x: Bool) extends Ha trait TA[A] { val a : A } //│ trait TA[A] { //│ val a: A //│ } class G1[A, B](val a: A, val b: B) extends TA[A] //│ class G1[A, B](a: A, b: B) extends TA class G2[T](x: T) extends G1[T, Int](x, 1) //│ class G2[T](x: T) extends G1, TA val g21 = G2(false) //│ val g21: G2['T] //│ where //│ 'T :> false //│ g21 //│ = G2 {} g21: G1[Bool, Int] //│ G1[Bool, Int] //│ res //│ = G2 {} g21.a //│ Bool //│ res //│ = false g21: TA[Bool] //│ TA[Bool] //│ res //│ = G2 {} ================================================ FILE: shared/src/test/diff/nu/IntraBlockPolymorphism.mls ================================================ :NewDefs // * Note: eventually we should progressively type check every mutually-recursive group // * in topological order, to maximize polymorphism. // * But currently we just type check them in srouce code order. // * So the following inferred types differ: fun i(x) = x let a = i(0) let b = i(true) //│ fun i: forall 'a. 'a -> 'a //│ let a: 0 //│ let b: true //│ a //│ = 0 //│ b //│ = true :re // FIXME shouldn't be a reference error let a = i(0) fun i(x) = x let b = i(true) //│ let a: 0 | true | 'a //│ fun i: forall 'b. ('a & 'b) -> (0 | 'b) //│ let b: 0 | true //│ a //│ Runtime error: //│ ReferenceError: i1 is not defined //│ b //│ = true :re // FIXME shouldn't be a reference error let a = i(0) let b = i(true) fun i(x) = x //│ let a: 0 | true | 'a //│ let b: 0 | true | 'a //│ fun i: forall 'b. ('a & 'b) -> (0 | true | 'b) //│ a //│ Runtime error: //│ ReferenceError: i2 is not defined //│ b //│ Runtime error: //│ ReferenceError: i2 is not defined module Test { fun i(x) = x let a = i(0) let b = i(true) } //│ module Test { //│ let a: 0 //│ let b: true //│ fun i: forall 'a. 'a -> 'a //│ } ================================================ FILE: shared/src/test/diff/nu/Jonathan.mls ================================================ :NewDefs :NoJS // * A monadic effect type, covariant in base type and effect type class Effectful[out A, out E](value: A) { fun flatMap[B](f: A => Effectful[B, E]): Effectful[B, E] = f(value) } fun pure[A](a: A): Effectful['a, 'e] = Effectful(a) //│ class Effectful[A, E](value: A) { //│ fun flatMap: forall 'B. (f: A -> Effectful['B, E]) -> Effectful['B, E] //│ } //│ fun pure: forall 'A. (a: 'A) -> Effectful['A, nothing] // * Some effect tags module IO module Block //│ module IO //│ module Block // * Some example functions fun println(x: anything): Effectful[(), IO] fun readLine: Effectful[Str, IO | Block] //│ fun println: (x: anything) -> Effectful[(), IO] //│ fun readLine: Effectful[Str, Block | IO] // * Define NonBlocking as an effectful computation that does not block (using type-level difference `\`) type NonBlocking[out A, out E] = Effectful[A, E \ Block] //│ type NonBlocking[A, E] = Effectful[A, E & ~Block] // * Example use of NonBlocking in an annotation; the type arguments are inferred fun f(x) = x : NonBlocking //│ fun f: forall 'a 'b. NonBlocking['a, 'b] -> NonBlocking['a, 'b] // * the `listener` callback should be non-blocking fun onMousePressed(listener) = let l(e) = listener(e) : NonBlocking l(0).flatMap of a => l(1).flatMap of b => pure of () //│ fun onMousePressed: forall 'a. ((0 | 1) -> NonBlocking[anything, 'a]) -> Effectful[(), 'a & ~Block] // * OK: `println` does not block onMousePressed(event => println("Clicked!")) //│ Effectful[(), IO & ~Block] // * NOT OK: `readLine` blocks :e onMousePressed(event => readLine.flatMap(println)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.51: onMousePressed(event => readLine.flatMap(println)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Block` does not match type `~Block` //│ ║ l.24: fun readLine: Effectful[Str, IO | Block] //│ ╙── ^^^^^ //│ Effectful[(), IO & ~Block] | error class Event class MouseEvent extends Event module Register //│ class Event { //│ constructor() //│ } //│ class MouseEvent extends Event { //│ constructor() //│ } //│ module Register fun onMousePressed(listener) = let l(e: MouseEvent) = listener(e) : Effectful[(), 'e \ Block \ Register] () //│ fun onMousePressed: (MouseEvent -> Effectful[(), ~Block & ~Register]) -> () // def onMouseClick ( f : Event -> Unit \ { ef - Register }): Unit \ { Register } fun onMouseClick(f: Event -> Effectful[(), 'e \ Register]): Effectful[(), Register] //│ fun onMouseClick: (f: Event -> Effectful[(), ~Register]) -> Effectful[(), Register] onMouseClick of ev => pure of () //│ Effectful[(), Register] :e onMouseClick of ev => onMouseClick(ev => pure of ()).flatMap of _ => pure of () //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.85: onMouseClick of ev => //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.86: onMouseClick(ev => pure of ()).flatMap of _ => //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.87: pure of () //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `Register` does not match type `~Register` //│ ║ l.78: fun onMouseClick(f: Event -> Effectful[(), 'e \ Register]): Effectful[(), Register] //│ ╙── ^^^^^^^^ //│ Effectful[(), Register] | error ================================================ FILE: shared/src/test/diff/nu/LamPatterns.mls ================================================ :NewDefs class Some(value: Int) //│ class Some(value: Int) :e // TODO Some(x) => x //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.8: Some(x) => x //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.8: Some(x) => x //│ ╙── ^ //│ error -> error //│ Code generation encountered an error: //│ term App(Var(Some),Tup(List((None,Fld(_,Var(x)))))) is not a valid pattern :js // FIXME type let f = Some => 0 //│ let f: ((value: Int) -> Some) -> 0 //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ globalThis.f = function f(Some) { //│ return 0; //│ }; //│ // End of generated code //│ f //│ = [Function: f] // :e // TODO f(Some) //│ 0 //│ res //│ = 0 // :e // TODO f(_ => error) //│ 0 //│ res //│ = 0 :e // TODO f(Some(0)) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.47: f(Some(0)) //│ ║ ^^^^^^^^^^ //│ ╟── application of type `Some` is not a function //│ ║ l.47: f(Some(0)) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.21: let f = Some => 0 //│ ╙── ^^^^ //│ 0 | error //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/nu/LetRec.mls ================================================ :NewDefs :js fun f(x) = x > 0 && f(x - 1) //│ fun f: Int -> Bool //│ // Prelude //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ globalThis.f = function f(x) { //│ return x > 0 && f(x - 1); //│ }; //│ // End of generated code f(12) //│ Bool //│ res //│ = false :js let rec f(x) = x > 0 && f(x - 1) //│ let rec f: Int -> Bool //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ globalThis.f1 = function f1(x) { //│ return x > 0 && f1(x - 1); //│ }; //│ // End of generated code //│ f //│ = [Function: f1] f(12) //│ Bool //│ res //│ = false :js let rec f() = f() //│ let rec f: () -> nothing //│ // Prelude //│ class TypingUnit4 {} //│ const typing_unit4 = new TypingUnit4; //│ // Query 1 //│ globalThis.f2 = function f2() { //│ return ((() => { //│ return f2(); //│ })()); //│ }; //│ // End of generated code //│ f //│ = [Function: f2] :re f() //│ nothing //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded // :e // TODO this should be rejected by the type checker :ge :js let rec f = f //│ let rec f: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding f :re :js f //│ nothing //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ res = f3; //│ // End of generated code //│ res //│ Runtime error: //│ ReferenceError: f3 is not defined // :e // TODO this should be rejected by the type checker :ge :js fun test = let rec f = f //│ fun test: () //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding f fun test = let rec f() = f() //│ fun test: () fun test = let rec lol = () => lol //│ fun test: () fun test = let rec lol() = lol lol //│ fun test: forall 'lol. 'lol //│ where //│ 'lol :> () -> 'lol fun testWithAsc = let rec aux: Int -> Int = x => if x <= 0 then 1 else x * aux(x - 1) aux(10) testWithAsc //│ fun testWithAsc: Int //│ Int //│ res //│ = 3628800 let rec lol = () => lol //│ let rec lol: forall 'lol. 'lol //│ where //│ 'lol :> () -> 'lol //│ lol //│ = [Function: lol] :p let rec f = 1 //│ |#let| |#rec| |f| |#=| |1| //│ AST: TypingUnit(List(NuFunDef(Some(true),Var(f),None,List(),Left(IntLit(1))))) //│ Parsed: let rec f = 1; //│ let rec f: 1 //│ f //│ = 1 let rec f = 1 //│ let rec f: 1 //│ f //│ = 1 // :e // FIXME :ge let foo = foo //│ let foo: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding foo // FIXME should work // No recursion: let foo = 1 let foo = foo + 1 //│ let foo: 1 //│ let foo: Int //│ foo //│ = 1 //│ foo //│ = 2 // FIXME let foo = foo.x //│ let foo: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding foo // :e // FIXME :ge foo()() //│ nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding foo // :e // FIXME wrong static semantics :re let xf = yf() let yf() = xf //│ let xf: nothing //│ let yf: () -> nothing //│ xf //│ Runtime error: //│ ReferenceError: yf is not defined //│ yf //│ = [Function: yf] ================================================ FILE: shared/src/test/diff/nu/ListConsNil.mls ================================================ :NewDefs type List[out A] = Cons[A] | Nil class Cons[out A](head: A, tail: List[A]) { val size: Int fun map: (A -> 'B) -> List['B] size = 1 + tail.size map(f) = Cons(f(head), tail.map(f)) } module Nil { val size = 0 fun map(f) = Nil } //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) { //│ fun map: forall 'B. (A -> 'B) -> List['B] //│ val size: Int //│ } //│ module Nil { //│ fun map: anything -> Nil //│ val size: 0 //│ } fun (::) cons(x, xs) = Cons(x, xs) //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] fun show(xs) = let rec go(xs) = if xs is Cons(h, Nil) then String(h) Cons(h, t) then join(String(h), ", ", go(t)) Nil then "" join("[", go(xs), "]") //│ fun show: (Cons[anything] | Nil) -> Str let xs = 1 :: 2 :: 3 :: Nil //│ let xs: Cons[1 | 2 | 3] //│ xs //│ = Cons {} xs.size //│ Int //│ res //│ = 3 show(xs) //│ Str //│ res //│ = '[1, 2, 3]' show(xs.map of x => succ of x) //│ Str //│ res //│ = '[2, 3, 4]' :e // TODO this should be made to work! abstract class List[out A]: (Cons[A] | Nil) { val size: Int fun map: (A -> 'B) -> List['B] } class Cons[out A](head: A, tail: List[A]) extends List[A] { val size = 1 + tail.size fun map(f) = Cons(f(head), tail.map(f)) } module Nil extends List[nothing] { val size = 0 fun map(f) = Nil } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.65: val size = 1 + tail.size //│ ╙── ^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.66: fun map(f) = Cons(f(head), tail.map(f)) //│ ╙── ^^^^ //│ abstract class List[A]: Cons[A] | Nil { //│ fun map: forall 'B. (A -> 'B) -> List['B] //│ val size: Int //│ } //│ class Cons[A](head: A, tail: List[A]) extends List { //│ fun map: forall 'A. (A -> 'A) -> Cons['A] //│ val size: Int //│ } //│ module Nil extends List { //│ fun map: anything -> Nil //│ val size: 0 //│ } // TODO should better simplify these types (reduce the List refinement) :ng val test0: Cons[Int] & List[Num] val test1: Nil & List[Int] //│ val test0: Cons[Int] & List[Num] //│ val test1: Nil & List[Int] fun list_assoc(s, l) = if l is Cons(h, t) then if eq(s)(h._1) then Cons(h._2, Nil) else list_assoc(s, t) Nil then Nil //│ fun list_assoc: forall 'A. (anything, Cons[{_1: anything, _2: 'A}] | Nil) -> (Cons['A] | Nil) fun test(x, l) = list_assoc(42, Cons(x, l)) //│ fun test: forall 'A. ({_1: anything, _2: 'A}, List[{_1: anything, _2: 'A}]) -> (Cons['A] | Nil) fun test(x, l) = if l is Nil then list_assoc(42, Cons(x, l)) Cons(h, t) then list_assoc(42, Cons(h, t)) //│ fun test: forall 'A. ({_1: anything, _2: 'A}, Cons[{_1: anything, _2: 'A}] | Nil) -> (Cons['A] | Nil) ================================================ FILE: shared/src/test/diff/nu/LitMatch.mls ================================================ :NewDefs let r = false : Bool //│ let r: Bool //│ r //│ = false let b = (if r then true else false) : Bool //│ let b: Bool //│ b //│ = false let b = false : Bool //│ let b: Bool //│ b //│ = false b : true | false //│ Bool //│ res //│ = false if false is false then 0 //│ 0 //│ res //│ = 0 fun foo(x) = if x is false then 0 //│ fun foo: false -> 0 fun foo(x) = if x is false then 0 true then 1 //│ fun foo: Bool -> (0 | 1) fun foo(x) = if x is 0 then "zero" true then "true" //│ fun foo: (0 | true) -> ("true" | "zero") ================================================ FILE: shared/src/test/diff/nu/LocalLets.mls ================================================ :NewDefs let f = let tmp = "ok" 123 //│ let f: 123 //│ f //│ = 123 :e let x : Int | string let x = 1 //│ ╔══[ERROR] `let` bindings must have a right-hand side //│ ║ l.13: let x : Int | string //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ let x: 1 //│ let x: Int | string //│ x //│ = //│ x //│ = 1 val x : Int | string val x = 1 //│ val x: 1 //│ val x: Int | string //│ x //│ = //│ x //│ = 1 class E(x: Int) //│ class E(x: Int) :e // TODO support (currently parsed as a function definition named E) let E(x) = new E(1) //│ ╔══[ERROR] value E cannot be used as a type //│ ║ l.39: let E(x) = new E(1) //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: E //│ ║ l.39: let E(x) = new E(1) //│ ╙── ^ //│ let E: anything -> error //│ E //│ = [Function: E1] ================================================ FILE: shared/src/test/diff/nu/MIscPoly.mls ================================================ :NewDefs [(x: 'a) => x] //│ [forall 'a. (x: 'a) -> 'a] //│ res //│ = [ [Function (anonymous)] ] [forall 'a: (x: 'a) => x] //│ [forall 'a. (x: 'a) -> 'a] //│ res //│ = [ [Function (anonymous)] ] abstract class C0[A] { fun use: forall 'r: [A, 'r -> 'r] } //│ abstract class C0[A] { //│ fun use: forall 'r. [A, 'r -> 'r] //│ } class C1 extends C0[Int] { fun use = [0, id] } //│ class C1 extends C0 { //│ constructor() //│ fun use: [0, forall 'a. 'a -> 'a] //│ } class C1[AA](aa: AA) extends C0[AA] { fun use = [aa, id] } //│ class C1[AA](aa: AA) extends C0 { //│ fun use: [AA, forall 'a. 'a -> 'a] //│ } // * FIXME currently we always distribute `forall` types; // * but this is not sound when distributing into a non-function such as an object type // * as long as we perform object type intersection merges (which we want to) class C[A](){fun f: A -> A = id} //│ class C[A]() { //│ fun f: A -> A //│ } let c = C() //│ let c: forall 'A. C['A] //│ c //│ = C {} // :e // FIXME let d = c : C[Int] & C[Str] //│ let d: C[in Int | Str out nothing] //│ d //│ = C {} // :e // FIXME let r = d.f(0) //│ let r: nothing //│ r //│ = 0 :re r() //│ nothing //│ res //│ Runtime error: //│ TypeError: r is not a function ================================================ FILE: shared/src/test/diff/nu/MemberConfusion.mls ================================================ :NewDefs mixin T { fun a = "hi" } //│ mixin T() { //│ fun a: "hi" //│ } class C(a: Int) extends T //│ class C(a: Int) { //│ fun a: "hi" //│ } class B { virtual val a = "hi" } //│ class B { //│ constructor() //│ val a: "hi" //│ } :e class C(a: Int) extends B //│ ╔══[ERROR] Type mismatch in type reference: //│ ║ l.21: class C(a: Int) extends B //│ ║ ^^^ //│ ╟── type `Int` does not match type `"hi"` //│ ╟── Note: constraint arises from string literal: //│ ║ l.14: class B { virtual val a = "hi" } //│ ║ ^^^^ //│ ╟── from definition of value a: //│ ║ l.14: class B { virtual val a = "hi" } //│ ╙── ^^^^^^^^ //│ class C(a: Int) extends B mixin M { val b = "hi" } //│ mixin M() { //│ val b: "hi" //│ } class B { virtual val a = 1 : Int } //│ class B { //│ constructor() //│ val a: Int //│ } class C(val a: Int, val b: Int) extends B, M //│ class C(a: Int, b: Int) extends B { //│ val b: "hi" //│ } let c = C(2, 3) [c.a, c.b] //│ let c: C //│ [Int, "hi"] //│ c //│ = C {} //│ res //│ = [ 2, 3 ] class C(a: Int) { let a = 1 } //│ class C(a: Int) { //│ let a: 1 //│ } class C(a: Int) { fun a = 1 } //│ class C(a: Int) { //│ fun a: 1 //│ } class C(a: Int) { fun a = a } //│ class C(a: Int) { //│ fun a: nothing //│ } class C(a: Int, b: Int) extends B, M { let b = "hi" } //│ class C(a: Int, b: Int) extends B { //│ let b: "hi" //│ } ================================================ FILE: shared/src/test/diff/nu/MemberIntersections.mls ================================================ :NewDefs :NoJS // TODO trait T1 { fun f[A]: A -> A } trait T2 { fun f[B, C]: (B, C) -> [B, C] } trait T3 extends T1, T2 //│ trait T1 { //│ fun f: forall 'A. 'A -> 'A //│ } //│ trait T2 { //│ fun f: forall 'B 'C. ('B, 'C) -> ['B, 'C] //│ } //│ trait T3 extends T1, T2 { //│ fun f: forall 'A 'B 'C. 'A -> 'A & ('B, 'C) -> ['B, 'C] //│ } trait S1 { class f } //│ trait S1 { //│ class f { //│ constructor() //│ } //│ } :e trait S2 extends T1, S1 //│ ╔══[ERROR] Intersection of value member and class members currently unsupported //│ ║ l.28: trait S2 extends T1, S1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── The value member is defined here: //│ ║ l.6: trait T1 { fun f[A]: A -> A } //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── The class member is defined here: //│ ║ l.20: trait S1 { class f } //│ ╙── ^^^^^^^ //│ trait S2 extends S1, T1 trait S2 { class f } //│ trait S2 { //│ class f { //│ constructor() //│ } //│ } :e trait S3 extends S1, S2 //│ ╔══[ERROR] Intersection of class member and class members currently unsupported //│ ║ l.48: trait S3 extends S1, S2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── The class member is defined here: //│ ║ l.20: trait S1 { class f } //│ ║ ^^^^^^^ //│ ╟── The class member is defined here: //│ ║ l.40: trait S2 { class f } //│ ╙── ^^^^^^^ //│ trait S3 extends S1, S2 trait S1 { val f: Int -> Int } //│ trait S1 { //│ val f: Int -> Int //│ } trait S2 extends T1, S1 //│ trait S2 extends S1, T1 { //│ fun f: forall 'A. 'A -> 'A & Int -> Int //│ } trait S3 extends S1, T1 //│ trait S3 extends S1, T1 { //│ fun f: forall 'A. Int -> Int & 'A -> 'A //│ } class C1(val x: Int | Bool) trait T1 { val x: Int | Str } //│ class C1(x: Int | false | true) //│ trait T1 { //│ val x: Int | Str //│ } class C2() extends C1(0), T1 //│ class C2() extends C1, T1 C2().x : 0 //│ 0 :e class C2 extends C1(false), T1 //│ ╔══[ERROR] Type mismatch in reference: //│ ║ l.91: class C2 extends C1(false), T1 //│ ║ ^^^^^ //│ ╟── reference of type `false` does not match type `Int | Str` //│ ╟── Note: constraint arises from union type: //│ ║ l.78: trait T1 { val x: Int | Str } //│ ║ ^^^^^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.78: trait T1 { val x: Int | Str } //│ ╙── ^^^^^^^^^^^^ //│ class C2 extends C1, T1 { //│ constructor() //│ } :e class C2 extends C1("oops"), T1 //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.107: class C2 extends C1("oops"), T1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"oops"` does not match type `Int | false | true` //│ ║ l.107: class C2 extends C1("oops"), T1 //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.77: class C1(val x: Int | Bool) //│ ╙── ^^^^^^^^^^ //│ class C2 extends C1, T1 { //│ constructor() //│ } ================================================ FILE: shared/src/test/diff/nu/MetaWrap.mls ================================================ :NewDefs // * A test to show how to add meta data to underlying values by wrapping them. mixin Base { fun unwrap(x) = x fun rewrap(x, f) = f(x) } //│ mixin Base() { //│ fun rewrap: ('a, 'a -> 'b) -> 'b //│ fun unwrap: 'c -> 'c //│ } mixin WithUid { fun unwrap(x) = super.unwrap(x).underlying fun rewrap(x, f) = super.rewrap(x, y => { underlying: f(y.underlying), uid: y.uid }) fun getUid(x) = super.unwrap(x).uid fun setUid(x, uid: Int) = super.rewrap(x, y => { underlying: y.underlying, uid }) } //│ mixin WithUid() { //│ super: { //│ rewrap: ('a, forall 'uid. {uid: 'uid, underlying: 'underlying} -> {uid: 'uid, underlying: 'b}) -> 'c & ('d, forall 'underlying0. {underlying: 'underlying0} -> {uid: Int, underlying: 'underlying0}) -> 'e, //│ unwrap: 'f -> {underlying: 'underlying1} & 'g -> {uid: 'uid0} //│ } //│ fun getUid: 'g -> 'uid0 //│ fun rewrap: ('a, 'underlying -> 'b) -> 'c //│ fun setUid: ('d, uid: Int) -> 'e //│ fun unwrap: 'f -> 'underlying1 //│ } class Type(val name: Str) //│ class Type(name: Str) mixin WithType { fun unwrap(x) = super.unwrap(x).underlying fun rewrap(x, f) = super.rewrap(x, y => { underlying: f(y.underlying), ty: y.ty }) fun getType(x) = super.unwrap(x).ty fun setType(x, ty: Type) = super.rewrap(x, y => { underlying: y.underlying, ty }) } //│ mixin WithType() { //│ super: { //│ rewrap: ('a, forall 'ty. {ty: 'ty, underlying: 'underlying} -> {ty: 'ty, underlying: 'b}) -> 'c & ('d, forall 'underlying0. {underlying: 'underlying0} -> {ty: Type, underlying: 'underlying0}) -> 'e, //│ unwrap: 'f -> {underlying: 'underlying1} & 'g -> {ty: 'ty0} //│ } //│ fun getType: 'g -> 'ty0 //│ fun rewrap: ('a, 'underlying -> 'b) -> 'c //│ fun setType: ('d, ty: Type) -> 'e //│ fun unwrap: 'f -> 'underlying1 //│ } module Test0 extends Base, WithUid //│ module Test0 { //│ fun getUid: {uid: 'uid, underlying: 'underlying} -> 'uid //│ fun rewrap: ({uid: 'uid0, underlying: 'underlying0 & 'underlying1}, 'underlying1 -> 'underlying0) -> {uid: Int | 'uid0, underlying: 'underlying0} //│ fun setUid: ({uid: 'uid0, underlying: 'underlying0 & 'underlying1}, uid: Int) -> {uid: Int | 'uid0, underlying: 'underlying0} //│ fun unwrap: {uid: 'uid, underlying: 'underlying} -> 'underlying //│ } module Test0 extends Base, WithUid, WithUid //│ module Test0 { //│ fun getUid: {uid: anything, underlying: {uid: 'uid, underlying: 'underlying}} -> 'uid //│ fun rewrap: ({ //│ uid: 'uid0, //│ underlying: {uid: 'uid1, underlying: 'underlying0 & 'underlying1} & 'underlying2 //│ }, 'underlying1 -> 'underlying0) -> { //│ uid: Int | 'uid0, //│ underlying: {uid: Int | 'uid1, underlying: 'underlying0} | 'underlying2 //│ } //│ fun setUid: ({ //│ uid: 'uid0, //│ underlying: {uid: 'uid1, underlying: 'underlying0 & 'underlying1} & 'underlying2 //│ }, uid: Int) -> { //│ uid: Int | 'uid0, //│ underlying: {uid: Int | 'uid1, underlying: 'underlying0} | 'underlying2 //│ } //│ fun unwrap: {uid: anything, underlying: {uid: 'uid, underlying: 'underlying}} -> 'underlying //│ } module Test1 extends Base, WithUid, WithType //│ module Test1 { //│ fun getType: {uid: 'uid, underlying: {ty: 'ty, underlying: 'underlying}} -> 'ty //│ fun getUid: {uid: 'uid, underlying: {ty: 'ty, underlying: 'underlying}} -> 'uid //│ fun rewrap: ({ //│ uid: 'uid0, //│ underlying: {ty: 'ty0, underlying: 'underlying0 & 'underlying1} & 'underlying2 //│ }, 'underlying1 -> 'underlying0) -> { //│ uid: Int | 'uid0, //│ underlying: {ty: Type | 'ty0, underlying: 'underlying0} | 'underlying2 //│ } //│ fun setType: ({ //│ uid: 'uid0, //│ underlying: {ty: 'ty0, underlying: 'underlying0 & 'underlying1} & 'underlying2 //│ }, ty: Type) -> { //│ uid: Int | 'uid0, //│ underlying: {ty: Type | 'ty0, underlying: 'underlying0} | 'underlying2 //│ } //│ fun setUid: ({ //│ uid: 'uid0, //│ underlying: {ty: 'ty0, underlying: 'underlying0 & 'underlying1} & 'underlying2 //│ }, uid: Int) -> { //│ uid: Int | 'uid0, //│ underlying: {ty: Type | 'ty0, underlying: 'underlying0} | 'underlying2 //│ } //│ fun unwrap: {uid: 'uid, underlying: {ty: 'ty, underlying: 'underlying}} -> 'underlying //│ } let uid = 0 let ty = Type("A") let underlying = 42 let a = { uid, underlying: { ty, underlying } } //│ let uid: 0 //│ let ty: Type //│ let underlying: 42 //│ let a: {uid: 0, underlying: {ty: Type, underlying: 42}} //│ uid //│ = 0 //│ ty //│ = Type {} //│ underlying //│ = 42 //│ a //│ = { uid: 0, underlying: { ty: Type {}, underlying: 42 } } Test1.unwrap(a) //│ 42 //│ res //│ = 42 Test1.getUid(a) //│ 0 //│ res //│ = 0 Test1.setUid(a, 1) //│ {uid: Int, underlying: {ty: Type, underlying: 42}} //│ res //│ = { underlying: { ty: Type {}, underlying: 42 }, uid: 1 } Test1.getType(a).name //│ Str //│ res //│ = 'A' a.underlying.ty.name //│ Str //│ res //│ = 'A' let b = Test1.setType(a, Type("B")) //│ let b: {uid: Int, underlying: {ty: Type, underlying: 42}} //│ b //│ = { underlying: { underlying: 42, ty: Type {} }, uid: 0 } Test1.getType(b).name //│ Str //│ res //│ = 'B' b.underlying.ty.name //│ Str //│ res //│ = 'B' ================================================ FILE: shared/src/test/diff/nu/Metaprog.mls ================================================ :NewDefs class Code[out A, out Ctx] //│ class Code[A, Ctx] { //│ constructor() //│ } class IntLit(value: Int) extends Code[Int, nothing] //│ class IntLit(value: Int) extends Code class Add[out C](lhs: Code[Int, C], rhs: Code[Int, C]) extends Code[Int, C] //│ class Add[C](lhs: Code[Int, C], rhs: Code[Int, C]) extends Code fun bind(x: Code['a, 'c], k: (forall 'cc: Code['a, 'cc] -> Code['b, 'cc])): Code['b, 'c] = k(x) //│ fun bind: forall 'a 'c 'b. (x: Code['a, 'c], k: forall 'cc. Code['a, 'cc] -> Code['b, 'cc]) -> Code['b, 'c] // * Note: extrusion fun test(f) = bind of IntLit(42), n => f(n) Add(n, IntLit(1)) //│ fun test: (Code[Int, ??cc] -> ()) -> Code[Int, nothing] abstract class Test[C] { // * Represents what happens in "... ${input} ..." when a binding of C is in scope fun unquote: (input: Code['a, C | 'c]) -> Code[Int, 'c] fun getVar: Code[Int, C] fun test0 = this.unquote of IntLit(1) fun test1 = this.unquote of Add(this.getVar, IntLit(1)) } //│ abstract class Test[C] { //│ fun getVar: Code[Int, C] //│ fun test0: Code[Int, nothing] //│ fun test1: Code[Int, nothing] //│ fun unquote: forall 'c. (input: Code[anything, C | 'c]) -> Code[Int, 'c] //│ } :NoJS fun mkVar(f: forall 'C: Test['C] -> 'a): 'a //│ fun mkVar: forall 'a. (f: forall 'C. Test['C] -> 'a) -> 'a mkVar of t0 => t0.unquote of Add(t0.getVar, IntLit(1)) //│ Code[Int, nothing] mkVar of t0 => Add(t0.getVar, IntLit(1)) //│ Add[anything] mkVar of t0 => mkVar of t1 => t1.unquote of t0.unquote of Add(t0.getVar, t1.getVar) //│ Code[Int, ??C & ~??C0] mkVar of t0 => mkVar of t1 => t0.unquote of t1.unquote of Add(t0.getVar, t1.getVar) //│ Code[Int, nothing] ================================================ FILE: shared/src/test/diff/nu/MethodSignatures.mls ================================================ :NewDefs module Oops { fun a : Int fun a = 2 } //│ module Oops { //│ fun a: Int //│ } :e module Oops { fun a : Int } //│ ╔══[ERROR] Member `a` is declared (or its declaration is inherited) but is not implemented in `Oops` //│ ║ l.14: module Oops { //│ ║ ^^^^ //│ ╟── Declared here: //│ ║ l.15: fun a : Int //│ ╙── ^^^^^^^^^^^ //│ module Oops { //│ fun a: Int //│ } :e module Oops { fun a : Int fun a : string fun a = a } //│ ╔══[ERROR] A type signature for 'a' was already given //│ ║ l.30: fun a : string //│ ╙── ^^^^^^^^^^^^^^ //│ module Oops { //│ fun a: string //│ } :e module Oops { fun a : Int fun a = false } //│ ╔══[ERROR] Type mismatch in definition of method a: //│ ║ l.43: fun a = false //│ ║ ^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ║ l.43: fun a = false //│ ║ ^^^^^ //│ ╟── but it flows into definition of method a with expected type `Int` //│ ║ l.43: fun a = false //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.42: fun a : Int //│ ║ ^^^ //│ ╟── from signature of member `a`: //│ ║ l.42: fun a : Int //│ ╙── ^^^^^^^ //│ module Oops { //│ fun a: Int //│ } :e module Oops { fun a = 1 fun a = 2 } //│ ╔══[ERROR] Redefinition of 'a' //│ ║ l.67: fun a = 2 //│ ╙── ^^^^^ //│ module Oops { //│ fun a: 1 //│ } // * Without a type signature, the method's inferred type is not generalized module A { fun i(x) = x } //│ module A { //│ fun i: forall 'a. 'a -> 'a //│ } // * With a type signature, it is generalized and checked against the signature module A { fun i: forall 'a: 'a -> 'a fun i(x) = x fun j: 'b -> 'b fun j(x) = x } //│ module A { //│ fun i: forall 'a. 'a -> 'a //│ fun j: forall 'b. 'b -> 'b //│ } :e module A { fun i : 'a fun i(x) = x } //│ ╔══[ERROR] Type mismatch in definition of method i: //│ ║ l.105: fun i(x) = x //│ ║ ^^^^^^^^ //│ ╟── function of type `?a -> ?a` does not match type `'a` //│ ║ l.105: fun i(x) = x //│ ║ ^^^^^^^ //│ ╟── but it flows into definition of method i with expected type `'a` //│ ║ l.105: fun i(x) = x //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.104: fun i : 'a //│ ║ ^^ //│ ╟── from signature of member `i`: //│ ║ l.104: fun i : 'a //│ ╙── ^^^^^^ //│ module A { //│ fun i: nothing //│ } // FIXME currently type signatures are typed too early (not in the context where the other defns live) // We need to move all the typing unit setup to lazy type info prelude // :d module M { class A fun a: A fun a = 1 } //│ ╔══[ERROR] Type mismatch in definition of method a: //│ ║ l.134: fun a = 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `A` //│ ║ l.134: fun a = 1 //│ ║ ^ //│ ╟── but it flows into definition of method a with expected type `A` //│ ║ l.134: fun a = 1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.133: fun a: A //│ ║ ^ //│ ╟── from signature of member `a`: //│ ║ l.133: fun a: A //│ ╙── ^^^^ //│ module M { //│ class A { //│ constructor() //│ } //│ fun a: A //│ } // FIXME similar module M { class A fun a: this.A fun a = 1 } //│ ╔══[ERROR] undeclared `this` //│ ║ l.161: fun a: this.A //│ ╙── ^^^^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing // FIXME similar module M { class A fun a: M.A fun a = 1 } //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing // * This is treated as a signature fun f(x: Int): Int //│ fun f: (x: Int) -> Int // * So is this! fun f(Int): Int //│ fun f: Int -> Int // * And this doesn't work :e fun f(x): Int //│ ╔══[ERROR] type identifier not found: x //│ ║ l.189: fun f(x): Int //│ ╙── ^ //│ fun f: error -> Int // * But this does! (no longer a signature...) fun f(x): Int = 1 //│ fun f: anything -> Int ================================================ FILE: shared/src/test/diff/nu/Misc.mls ================================================ :NewDefs 1 //│ 1 //│ res //│ = 1 2 + 2 //│ Int //│ res //│ = 4 let r = { x: 1 } //│ let r: {x: 1} //│ r //│ = { x: 1 } r.x + 1 //│ Int //│ res //│ = 2 x => x + 1 //│ Int -> Int //│ res //│ = [Function: res] { y } => y //│ forall 'a. {y: 'a} -> 'a //│ res //│ = [Function: res] fun f({ y }) = y //│ fun f: forall 'a. {y: 'a} -> 'a fun f of { y } = y //│ fun f: forall 'a. {y: 'a} -> 'a f({y: 1}) //│ 1 //│ res //│ = 1 fun f of x, y = x + y //│ fun f: (Int, Int) -> Int f of 1, 2 //│ Int //│ res //│ = 3 fun f of [x, y] = x + y //│ fun f: ([Int, Int]) -> Int f of [1, 2] //│ Int //│ res //│ = 3 let f = (x, y) => x + y //│ let f: (Int, Int) -> Int //│ f //│ = [Function: f4] f(1, 2) //│ Int //│ res //│ = 3 :e f([1, 2]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.76: f([1, 2]) //│ ║ ^^^^^^^^^ //│ ╟── argument of type `[[1, 2]]` does not match type `[?a, ?b]` //│ ║ l.76: f([1, 2]) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.65: let f = (x, y) => x + y //│ ╙── ^^^^ //│ Int | error //│ res //│ = '1,2undefined' :e let f = ((x, y)) => x + y //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.93: let f = ((x, y)) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.93: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.93: let f = ((x, y)) => x + y //│ ╙── ^ //│ let f: error -> Int //│ Code generation encountered an error: //│ term App(Var(,),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))) is not a valid pattern :e f(1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.108: f(1, 2) //│ ║ ^^^^^^^ //│ ╟── argument list of type `[1, 2]` does not match type `[error]` //│ ║ l.108: f(1, 2) //│ ╙── ^^^^^^ //│ Int | error //│ res //│ Runtime error: //│ ReferenceError: f5 is not defined :re f((1, 2)) //│ Int //│ res //│ Runtime error: //│ ReferenceError: f5 is not defined :re f([1, 2]) //│ Int //│ res //│ Runtime error: //│ ReferenceError: f5 is not defined :e f[1, 2] //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.135: f[1, 2] //│ ╙── ^^^^^^^ //│ error -> Int //│ res //│ Runtime error: //│ ReferenceError: f5 is not defined :e let f = (((x, y))) => x + y //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.146: let f = (((x, y))) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.146: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.146: let f = (((x, y))) => x + y //│ ╙── ^ //│ let f: error -> Int //│ Code generation encountered an error: //│ term App(Var(,),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))) is not a valid pattern // * TODO maybe parse as type lambda? let f = [x, y] => x + y //│ let f: ([Int, Int]) -> Int //│ f //│ = [Function: f5] :e f(1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.168: f(1, 2) //│ ║ ^^^^^^^ //│ ╟── argument list of type `[1, 2]` does not match type `[[?a, ?b]]` //│ ║ l.168: f(1, 2) //│ ╙── ^^^^^^ //│ Int | error //│ res //│ Runtime error: //│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) f([1, 2]) //│ Int //│ res //│ = 3 let f = ([x, y]) => x + y //│ let f: ([Int, Int]) -> Int //│ f //│ = [Function: f6] f([1, 2]) //│ Int //│ res //│ = 3 :e f(1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.197: f(1, 2) //│ ║ ^^^^^^^ //│ ╟── argument list of type `[1, 2]` does not match type `[[?a, ?b]]` //│ ║ l.197: f(1, 2) //│ ╙── ^^^^^^ //│ Int | error //│ res //│ Runtime error: //│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) let f = [[[x, y]]] => x + y //│ let f: ([[[Int, Int]]]) -> Int //│ f //│ = [Function: f7] :e f([[1, 2]]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.216: f([[1, 2]]) //│ ║ ^^^^^^^^^^^ //│ ╟── tuple literal of type `[1, 2]` does not match type `[[?a, ?b]]` //│ ║ l.216: f([[1, 2]]) //│ ╙── ^^^^^^ //│ Int | error //│ res //│ Runtime error: //│ TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator)) ================================================ FILE: shared/src/test/diff/nu/MissingImplBug.mls ================================================ :NewDefs declare fun String: nothing //│ fun String: nothing let makeString: anything => { length: Int, charCodeAt: Int => Int } = String let StringInstance: { fromCharCode: Int => Str } = String //│ let makeString: anything -> {charCodeAt: Int -> Int, length: Int} //│ let StringInstance: {fromCharCode: Int -> Str} //│ makeString //│ = [Function: String] //│ StringInstance //│ = [Function: String] // * Why do we get below and not above?? declare fun String: nothing let makeString: anything => { length: Int, charCodeAt: Int => Int } = String let StringInstance: { fromCharCode: Int => Str } = String //│ let makeString: anything -> {charCodeAt: Int -> Int, length: Int} //│ let StringInstance: {fromCharCode: Int -> Str} //│ fun String: nothing //│ makeString //│ = [Function: String] //│ StringInstance //│ = [Function: String] ================================================ FILE: shared/src/test/diff/nu/MissingTypeArg.mls ================================================ // * This is an example program where the error we get is really not ideal :NewDefs // * An example recursive definition: fun test(pt1, pt2) = pt1.color === pt1.color and let p1 = pt1.parent let p2 = pt2.parent if p1 is undefined then true else if p2 is undefined then true else test(p1, p2) //│ fun test: forall 'a 'b 'c. ('a, 'c) -> Bool //│ where //│ 'c <: {parent: Object & 'c & ~() | ()} //│ 'a <: {color: Eql['b] & 'b, parent: Object & 'a & ~() | ()} // * This works out fine: class MyPoint1[Col](val color: Col, val parent: MyPoint1[Col] | undefined) //│ class MyPoint1[Col](color: Col, parent: MyPoint1[Col] | ()) val p = MyPoint1(0, undefined) //│ val p: MyPoint1['Col] //│ where //│ 'Col :> 0 //│ p //│ = MyPoint1 {} test(p, p) //│ Bool //│ res //│ = true // * BUT... if we forgot to pass the type argument to MyPoint2 (getting a raw/nominal-tag type), // * the error is not helpful at all: class MyPoint2[out Col](val color: Col, val parent: MyPoint2 | undefined) //│ class MyPoint2[Col](color: Col, parent: MyPoint2[anything] | ()) val p = MyPoint2(0, undefined) //│ val p: MyPoint2[0] //│ p //│ = MyPoint2 {} :e test(p, p) //│ ╔══[ERROR] Type error in application //│ ║ l.50: test(p, p) //│ ║ ^^^^^^^^^^ //│ ╟── type variable `Col` leaks out of its scope //│ ║ l.41: class MyPoint2[out Col](val color: Col, val parent: MyPoint2 | undefined) //│ ║ ^^^ //│ ╟── into field selection of type `#Eql` //│ ║ l.8: fun test(pt1, pt2) = pt1.color === pt1.color and //│ ╙── ^^^^^^^^^ //│ error | false | true //│ res //│ = true // TODO[ucs] ideally this should work fun test(pt1, pt2) = pt1.color === pt1.color and let p1 = pt1.parent let p2 = pt2.parent if p1 is undefined then p2 is undefined else test(p1, p2) //│ fun test: forall 'a 'b 'c. ('a, 'c) -> Bool //│ where //│ 'c <: {parent: Object & 'c} //│ 'a <: {color: Eql['b] & 'b, parent: Object & 'a & ~() | ()} :e // TODO support test(p, p) //│ ╔══[ERROR] Type error in application //│ ║ l.79: test(p, p) //│ ║ ^^^^^^^^^^ //│ ╟── type variable `Col` leaks out of its scope //│ ║ l.41: class MyPoint2[out Col](val color: Col, val parent: MyPoint2 | undefined) //│ ║ ^^^ //│ ╟── into field selection of type `#Eql` //│ ║ l.68: fun test(pt1, pt2) = pt1.color === pt1.color and //│ ╙── ^^^^^^^^^ //│ error //│ res //│ = true ================================================ FILE: shared/src/test/diff/nu/Mixin42.mls ================================================ :NewDefs mixin M1 { fun test = 21 } mixin M2 { fun test = super.test * this.factor } mixin M3 { val factor = 2 } class C1() extends M1, M2, M3 C1().test //│ mixin M1() { //│ fun test: 21 //│ } //│ mixin M2() { //│ super: {test: Int} //│ this: {factor: Int} //│ fun test: Int //│ } //│ mixin M3() { //│ val factor: 2 //│ } //│ class C1() { //│ val factor: 2 //│ fun test: Int //│ } //│ Int //│ res //│ = 42 class C1(val factor: Int) extends M1, M2 C1(2).test //│ class C1(factor: Int) { //│ fun test: Int //│ } //│ Int //│ res //│ = 42 class C1(val factor: Int) extends M1, M2 //│ class C1(factor: Int) { //│ fun test: Int //│ } :e class C1() extends M1, M2 { val factor = 2 } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.6: mixin M2 { fun test = super.test * this.factor } //│ ╙── ^^^^^^^ //│ class C1() { //│ val factor: 2 //│ fun test: Int //│ } // * Note that this `val` definition is not yet treated as a type signature/annotation :e class C1() extends M1, M2 { val factor: Int = 2 } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.6: mixin M2 { fun test = super.test * this.factor } //│ ╙── ^^^^^^^ //│ class C1() { //│ val factor: Int //│ fun test: Int //│ } abstract class C1 extends M1, M2 { val factor: Int } module C2 extends C1 { val factor = 2 } C2.test //│ abstract class C1 { //│ val factor: Int //│ fun test: Int //│ } //│ module C2 extends C1 { //│ val factor: 2 //│ fun test: Int //│ } //│ Int //│ res //│ = 42 class C1() extends M1, M2 { val factor: Int; val factor = 2 } C1().test //│ class C1() { //│ val factor: Int //│ fun test: Int //│ } //│ Int //│ res //│ = 42 abstract class C0 { val factor = 2 } //│ abstract class C0 { //│ val factor: 2 //│ } :e class C1() extends C0, M1, M2 //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.6: mixin M2 { fun test = super.test * this.factor } //│ ╙── ^^^^^^^ //│ class C1() extends C0 { //│ val factor: 2 //│ fun test: Int //│ } abstract class C0 { val factor: Int } //│ abstract class C0 { //│ val factor: Int //│ } :e // * TODO support class C1() extends C0, M1, M2 { val factor = 2 } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.6: mixin M2 { fun test = super.test * this.factor } //│ ╙── ^^^^^^^ //│ class C1() extends C0 { //│ val factor: 2 //│ fun test: Int //│ } ================================================ FILE: shared/src/test/diff/nu/MixinParameters.mls ================================================ :NewDefs mixin BaseTest(x: Int) { fun test = x } //│ mixin BaseTest(x: Int) { //│ fun test: Int //│ } module Test extends BaseTest(42) //│ module Test { //│ fun test: Int //│ } Test.test //│ Int //│ res //│ = 42 :e Test.x //│ ╔══[ERROR] Type `Test` does not contain member `x` //│ ║ l.22: Test.x //│ ╙── ^^ //│ error //│ res //│ = undefined mixin BaseTest(val x: Int -> Int) //│ mixin BaseTest(x: Int -> Int) module Test extends BaseTest(id) //│ module Test Test.x(1) //│ 1 //│ res //│ = 1 :e // TODO support mixin BaseTest(x) { fun test = x } //│ ╔══[ERROR] Mixin parameters currently need type annotations //│ ║ l.44: mixin BaseTest(x) { //│ ╙── ^ //│ mixin BaseTest(x: error) { //│ fun test: error //│ } ================================================ FILE: shared/src/test/diff/nu/ModuleParameters.mls ================================================ :NewDefs :e module A(x: Int) { fun y = x } //│ ╔══[ERROR] Module parameters are not supported //│ ║ l.5: module A(x: Int) { fun y = x } //│ ╙── ^ //│ module A(x: Int) { //│ fun y: Int //│ } :e A //│ ╔══[ERROR] Parameterized modules are not yet supported //│ ║ l.14: A //│ ╙── ^ //│ (x: Int) -> A //│ res //│ = A { class: [class A] } :e A(123) //│ ╔══[ERROR] Parameterized modules are not yet supported //│ ║ l.23: A(123) //│ ╙── ^ //│ A //│ res //│ Runtime error: //│ TypeError: A is not a function :e A.x //│ ╔══[ERROR] Parameterized modules are not yet supported //│ ║ l.33: A.x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.33: A.x //│ ║ ^^^ //│ ╟── reference of type `(x: Int) -> A` does not have field 'x' //│ ║ l.33: A.x //│ ╙── ^ //│ error //│ res //│ = undefined :e A.y //│ ╔══[ERROR] Parameterized modules are not yet supported //│ ║ l.48: A.y //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.48: A.y //│ ║ ^^^ //│ ╟── reference of type `(x: Int) -> A` does not have field 'y' //│ ║ l.48: A.y //│ ╙── ^ //│ error //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/nu/Mut.mls ================================================ :NewDefs :pe val v1: {mut 1} //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.5: val v1: {mut 1} //│ ╙── ^ //│ val v1: {mut : 1} //│ v1 //│ = :e val v1: {mut Int} //│ ╔══[ERROR] Field identifiers must start with a small letter //│ ║ l.14: val v1: {mut Int} //│ ╙── ^^^ //│ val v1: {Int = Int} //│ v1 //│ = :e val v1 = {mut Int: 0} //│ ╔══[ERROR] Field identifiers must start with a small letter //│ ║ l.23: val v1 = {mut Int: 0} //│ ╙── ^ //│ val v1: {Int = 'Int} //│ where //│ 'Int :> 0 //│ v1 //│ = { Int: 0 } val v1 = {mut int: 0} //│ val v1: {mut int: 'int} //│ where //│ 'int :> 0 //│ v1 //│ = { int: 0 } val v1: {mut x: Int} //│ val v1: {mut x: Int} //│ v1 //│ = :pe val v1 = {mut 1} //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.47: val v1 = {mut 1} //│ ╙── ^ //│ val v1: {mut : '} //│ where //│ ' :> 1 //│ v1 //│ = { '': 1 } val v1 = {mut x: 1} //│ val v1: {mut x: 'x} //│ where //│ 'x :> 1 //│ v1 //│ = { x: 1 } // * TODO: support this syntax? :pe v1.x = 1 //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.66: v1.x = 1 //│ ╙── ^ //│ 1 //│ res //│ = 1 // * TODO: support this syntax? :e :ng v1.x <- 1 //│ ╔══[ERROR] identifier not found: <- //│ ║ l.77: v1.x <- 1 //│ ╙── ^^ //│ error :pe val v2: (mut Int) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.85: val v2: (mut Int) //│ ╙── ^^^ //│ val v2: Int //│ v2 //│ = :pe val v2 = (mut 1) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.94: val v2 = (mut 1) //│ ╙── ^ //│ val v2: 1 //│ v2 //│ = 1 :pe val v2: (mut x: Int) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.103: val v2: (mut x: Int) //│ ╙── ^^^^^^ //│ val v2: Int //│ v2 //│ = :pe val v2 = (mut 1) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.112: val v2 = (mut 1) //│ ╙── ^ //│ val v2: 1 //│ v2 //│ = 1 :pe val v2 = (mut x: 1) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.121: val v2 = (mut x: 1) //│ ╙── ^^^^ //│ val v2: 1 //│ v2 //│ = 1 :pe val v2 = (mut y: 1) //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.130: val v2 = (mut y: 1) //│ ╙── ^^^^ //│ val v2: 1 //│ v2 //│ = 1 val v2: [mut Int] //│ val v2: [mut Int] //│ v2 //│ = val v2 = [mut 1] //│ val v2: [mut 'a] //│ where //│ 'a :> 1 //│ v2 //│ = [ 1 ] val v2: [mut x: Int] //│ val v2: [mut x: Int] //│ v2 //│ = val v2 = [mut 1] //│ val v2: [mut 'a] //│ where //│ 'a :> 1 //│ v2 //│ = [ 1 ] val v2 = [mut x: 1] //│ val v2: [mut x: 'x] //│ where //│ 'x :> 1 //│ v2 //│ = [ 1 ] val v2 = [mut y: 1] //│ val v2: [mut y: 'y] //│ where //│ 'y :> 1 //│ v2 //│ = [ 1 ] ================================================ FILE: shared/src/test/diff/nu/MutLet.mls ================================================ :NewDefs mut let i = 0 //│ mut let i: 0 //│ i //│ = 0 set i = 1 //│ () //│ res //│ = undefined i //│ 0 | 1 //│ res //│ = 1 mut let f(x) = set f = _ => x x //│ mut let f: 'a -> 'a //│ f //│ = [Function: f] f(1) //│ 1 //│ res //│ = 1 f(true) //│ 1 | true //│ res //│ = true class Some[A](value: A) module None //│ class Some[A](value: A) //│ module None mut let oops = None //│ mut let oops: None //│ oops //│ = None { class: [class None] } fun funny(x) = set oops = Some(x) x //│ fun funny: forall 'a. 'a -> 'a funny(1) //│ 1 //│ res //│ = 1 if oops is None then 0 Some(v) then v //│ 0 | 1 //│ res //│ = 1 set oops = Some(123) //│ () //│ res //│ = undefined oops //│ None | Some[in 'A out 1 | 123 | 'A] //│ res //│ = Some {} mut let oops = None //│ mut let oops: None //│ oops //│ = None { class: [class None] } fun funny(x) = let tmp = oops set oops = Some(x) tmp //│ fun funny: 'A -> (None | Some['A]) funny(123) //│ None | Some[in 'A out 123 | 'A] //│ res //│ = None { class: [class None] } if funny("hi") is Some(v) then v else 0 //│ "hi" | 0 | 123 //│ res //│ = 123 module None //│ module None mut let m = None //│ mut let m: None //│ m //│ = None { class: [class None] } mut val m = None fun oops(x) = set m = x //│ mut val m: None | 'm //│ fun oops: 'm -> () //│ m //│ = None { class: [class None] } :e let x = 1 set x = 2 x //│ ╔══[ERROR] definition `x` is not mutable and cannot be reassigned //│ ║ l.119: set x = 2 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.119: set x = 2 //│ ║ ^^^^^^^^^ //│ ╟── integer literal of type `2` does not match type `1` //│ ║ l.119: set x = 2 //│ ║ ^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.118: let x = 1 //│ ║ ^ //│ ╟── from definition of let binding x: //│ ║ l.118: let x = 1 //│ ╙── ^^^^^ //│ let x: 1 //│ 1 //│ x //│ = 1 //│ res //│ = undefined //│ res //│ = 2 mut let min = 123 //│ mut let min: 123 //│ min //│ = 123 set min = 42 //│ () //│ res //│ = undefined min //│ 123 | 42 //│ res //│ = 42 fun repmin(t) = mut let min = 123 let go(t) = set min = t.value go(t) min //│ fun repmin: forall 'min. {value: 'min} -> (123 | 'min) class Leaf[out A](val value: A) //│ class Leaf[A](value: A) fun repmin(t) = mut let min = 123 let go(t) = if t is Leaf(n) then set min = n go(t) min //│ fun repmin: forall 'min. Leaf['min] -> (123 | 'min) fun repmin = mut let min = 123 let go(t) = if t is Leaf(n) then set min = n [min, go] //│ fun repmin: forall 'min. [123 | 'min, Leaf['min] -> ()] :e mut val lol[A] = (x: A) => x //│ ╔══[ERROR] Type parameters are not yet supported in this position //│ ║ l.190: mut val lol[A] = (x: A) => x //│ ╙── ^ //│ mut val lol: (x: 'A) -> 'A //│ lol //│ = [Function: lol] :NoJS mut let foo_ty = error //│ mut let foo_ty: nothing :ns let rec body(x) = log of x.m + 1 set foo_ty = body x //│ let rec body: forall 'a 'm. 'a -> 'a //│ where //│ 'a :> 'b //│ <: 'c & {m: 'm} //│ 'm :> 'm0 //│ <: Int //│ 'c :> 'b //│ 'b <: {m: 'm0} //│ 'm0 <: Int foo_ty of {m: 1} //│ {m: 1} foo_ty of {m: 2} //│ {m: 1 | 2} body({m: 3}) //│ {m: 1 | 2 | 3} body({m: 4}) //│ {m: 1 | 2 | 4} body //│ forall 'a. ({m: Int} & 'a) -> ({m: 1 | 2} | 'a) foo_ty //│ ({m: Int} & 'a) -> ({m: 1 | 2 | 3 | 4} | 'a) ================================================ FILE: shared/src/test/diff/nu/MutualRec.mls ================================================ :NewDefs class Foo() 123 //│ class Foo() //│ 123 //│ res //│ = 123 Foo //│ () -> Foo //│ res //│ = [Function (anonymous)] { //│ class: [class Foo], //│ unapply: [Function: unapply] //│ } // TODO fun fooo(x) = class C(y, z) C(0, x) //│ ╔══[ERROR] Class parameters currently need type annotations //│ ║ l.22: class C(y, z) //│ ╙── ^ //│ ╔══[ERROR] Class parameters currently need type annotations //│ ║ l.22: class C(y, z) //│ ╙── ^ //│ fun fooo: error -> C fun foo = bar fun bar = foo //│ fun foo: nothing //│ fun bar: nothing :re foo(bar) //│ nothing //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded fun foo = {x: foo} //│ fun foo: forall 'foo. 'foo //│ where //│ 'foo :> {x: 'foo} fun foo = {x: bar} fun bar = {y: foo} //│ fun foo: forall 'foo. 'foo //│ fun bar: forall 'foo. {y: 'foo} //│ where //│ 'foo :> {x: {y: 'foo}} :re foo //│ forall 'foo. 'foo //│ where //│ 'foo :> {x: {y: 'foo}} //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :ns :re foo //│ forall 'foo. {x: {y: 'foo}} //│ where //│ 'foo :> {x: {y: 'foo}} //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re foo.x //│ {y: 'foo} //│ where //│ 'foo :> {x: {y: 'foo}} //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :re foo.x.y //│ 'foo //│ where //│ 'foo :> {x: {y: 'foo}} //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded fun foo(a) = {h: a, t: bar(a)} fun bar(b) = foo(b) //│ fun foo: forall 'a 'b. 'a -> 'b //│ fun bar: forall 'a 'b. 'a -> 'b //│ where //│ 'b :> {h: 'a, t: 'b} :ns foo //│ forall 'a 'b 'c. 'a -> {h: 'a, t: 'c} //│ where //│ 'c :> {h: 'a, t: 'c} //│ 'a <: 'b //│ 'b <: 'a //│ res //│ = [Function: foo3] fun foo(a) = {h1: a, t1: bar(a)} fun bar(b) = {h2: b, t2: foo(b)} //│ fun foo: forall 'a 'b 'c. 'a -> {h1: 'a, t1: 'b} //│ fun bar: forall 'a 'c. 'a -> {h2: 'a, t2: 'c} //│ where //│ 'b :> {h2: 'a, t2: 'c} //│ 'c :> {h1: 'a, t1: 'b} module Test0_1 { fun a = Test0_2.b } module Test0_2 { fun b = 123 } //│ module Test0_1 { //│ fun a: 123 //│ } //│ module Test0_2 { //│ fun b: 123 //│ } Test0_1.a //│ 123 //│ res //│ = 123 class Test0_1 { fun a = Test0_2().b } class Test0_2() { fun b = 123 } //│ class Test0_1 { //│ constructor() //│ fun a: 123 //│ } //│ class Test0_2() { //│ fun b: 123 //│ } :e module Test1_1 { fun a = Test1_2.b } module Test1_2 { fun b = Test1_1.a } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.164: fun b = Test1_1.a //│ ╙── ^^ //│ module Test1_1 { //│ fun a: error //│ } //│ module Test1_2 { //│ fun b: error //│ } :re Test1_1.a //│ error //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :e class Test1_1 { fun a = Test1_2().b } class Test1_2 { fun b = Test1_1().a } //│ ╔══[ERROR] Class Test1_2 cannot be instantiated as it exposes no constructor //│ ║ l.186: fun a = Test1_2().b //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Class Test1_1 cannot be instantiated as it exposes no constructor //│ ║ l.189: fun b = Test1_1().a //│ ╙── ^^^^^^^ //│ class Test1_1 { //│ constructor() //│ fun a: error //│ } //│ class Test1_2 { //│ constructor() //│ fun b: error //│ } :e class Test1_1() { fun a = Test1_2().b } class Test1_2() { fun b = Test1_1().a } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.211: fun b = Test1_1().a //│ ╙── ^^ //│ class Test1_1() { //│ fun a: error //│ } //│ class Test1_2() { //│ fun b: error //│ } :e class Test1_1 { fun a = (new Test1_2).b } class Test1_2 { fun b = (new Test1_1).a } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.228: fun b = (new Test1_1).a //│ ╙── ^^ //│ class Test1_1 { //│ constructor() //│ fun a: error //│ } //│ class Test1_2 { //│ constructor() //│ fun b: error //│ } class Test1_1 { fun a: Int fun a = (new Test1_2).b } class Test1_2 { fun b = (new Test1_1).a } //│ class Test1_1 { //│ constructor() //│ fun a: Int //│ } //│ class Test1_2 { //│ constructor() //│ fun b: Int //│ } :e module Test2_1 { fun t2 = Test2_2 fun a = Test2_2.b fun d = Test2_2.e fun n = 456 } module Test2_2 { fun b = 123 fun c = Test2_1.a fun e = Test2_1.n } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.268: fun c = Test2_1.a //│ ╙── ^^ //│ module Test2_1 { //│ fun a: 123 | error //│ fun d: error //│ fun n: 456 //│ fun t2: Test2_2 //│ } //│ module Test2_2 { //│ fun b: 123 //│ fun c: error //│ fun e: error //│ } Test2_1.t2.b //│ 123 //│ res //│ = 123 Test2_1.a //│ 123 | error //│ res //│ = 123 Test2_1.d //│ error //│ res //│ = 456 Test2_1.n //│ 456 //│ res //│ = 456 module Test2_1 { fun t2 = Test2_2 fun a: Int fun a = Test2_2.b fun d = Test2_2.e fun n: Int fun n = 456 } module Test2_2 { fun b = 123 fun c = Test2_1.a fun e = Test2_1.n } //│ module Test2_1 { //│ fun a: Int //│ fun d: Int //│ fun n: Int //│ fun t2: Test2_2 //│ } //│ module Test2_2 { //│ fun b: 123 //│ fun c: Int //│ fun e: Int //│ } Test2_1.t2.b //│ 123 //│ res //│ = 123 Test2_1.a //│ Int //│ res //│ = 123 Test2_1.d //│ Int //│ res //│ = 456 Test2_1.n //│ Int //│ res //│ = 456 class Test2(val n: Int) { fun inc = Test3.inc(this) } module Test3 { fun inc(t: Test2) = Test2(t.n + 1) } //│ class Test2(n: Int) { //│ fun inc: Test2 //│ } //│ module Test3 { //│ fun inc: (t: Test2) -> Test2 //│ } mixin Foo { fun f(x) = x(f) } //│ mixin Foo() { //│ fun f: 'a -> 'b //│ } //│ where //│ 'a <: ('a -> 'b) -> 'b mixin Foo { fun f(x) = g(f), x fun g(y) = y(g) } //│ mixin Foo() { //│ fun f: ('a & 'b) -> ('c -> 'b | 'a) //│ fun g: 'c -> 'b //│ } //│ where //│ 'b :> 'c -> 'b //│ 'c <: ('c -> 'b) -> 'b mixin Foo { fun f(x) = g(x), x fun g(y) = f(y) } //│ mixin Foo() { //│ fun f: 'a -> 'a //│ fun g: 'a -> 'a //│ } module Foo { fun f(x) = g(x), x fun g(y) = f(y) } //│ module Foo { //│ fun f: forall 'a. 'a -> 'a //│ fun g: forall 'a. 'a -> 'a //│ } ================================================ FILE: shared/src/test/diff/nu/NamedArgs.mls ================================================ :NewDefs fun test(x: 'a) = if x is undefined then 0 else x + 1 //│ fun test: (x: Int | ()) -> Int test(x: 0) //│ Int //│ res //│ = 1 :e test(x: 0, 1) //│ ╔══[ERROR] Unnamed arguments should appear first when using named arguments //│ ║ l.13: test(x: 0, 1) //│ ╙── ^^^^^^^^^ //│ error //│ res //│ = 1 :e test(y: 0) //│ ╔══[ERROR] Argument named 'x' is missing from this function call //│ ║ l.22: test(y: 0) //│ ╙── ^^^^^^ //│ Int //│ res //│ Runtime error: //│ Error: an error was thrown fun test(x: 'a, y: 'b) = [x, y] //│ fun test: forall 'a 'b. (x: 'a, y: 'b) -> ['a, 'b] :e test(x: 1, 2) //│ ╔══[ERROR] Unnamed arguments should appear first when using named arguments //│ ║ l.36: test(x: 1, 2) //│ ╙── ^^^^^^^^^ //│ error //│ res //│ = [ 1, 2 ] :e test(x: 1, z: 3) //│ ╔══[ERROR] Argument named 'y' is missing from this function call //│ ║ l.45: test(x: 1, z: 3) //│ ╙── ^^^^^^^^^^^^ //│ [1, nothing] //│ res //│ Runtime error: //│ Error: an error was thrown :e test(y: 0) //│ ╔══[ERROR] Number of arguments doesn't match function signature `forall 'a 'b. (x: 'a, y: 'b) -> ['a, 'b]` //│ ║ l.55: test(y: 0) //│ ╙── ^^^^^^ //│ error //│ res //│ = [ 0, undefined ] :e test(1, x: 0) //│ ╔══[ERROR] Argument for parameter 'x' is duplicated //│ ║ l.64: test(1, x: 0) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Argument named 'y' is missing from this function call //│ ║ l.64: test(1, x: 0) //│ ╙── ^^^^^^^^^ //│ [0, nothing] //│ res //│ Runtime error: //│ Error: an error was thrown // * Notice no let binding is generated for the first argument :js test(0, y: 1) //│ [0, 1] //│ // Prelude //│ class TypingUnit9 {} //│ const typing_unit9 = new TypingUnit9; //│ // Query 1 //│ res = test1(0, 1); //│ // End of generated code //│ res //│ = [ 0, 1 ] id(test)(0, y: 1) //│ [0, 1] //│ res //│ = [ 0, 1 ] id(if true then test else error)(0, y: 1) //│ [0, 1] //│ res //│ = [ 0, 1 ] :e id(if true then test else id)(0, y: 1) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.100: id(if true then test else id)(0, y: 1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res //│ = [ 0, 1 ] // * No let binding in that value of argument is a var or literal :js let tmp = 2 test(0, y: tmp) test(0, y: 200) //│ let tmp: 2 //│ [0, 200] //│ // Prelude //│ class TypingUnit13 {} //│ const typing_unit13 = new TypingUnit13; //│ // Query 1 //│ globalThis.tmp = 2; //│ // Query 2 //│ res = test1(0, tmp); //│ // Query 3 //│ res = test1(0, 200); //│ // End of generated code //│ tmp //│ = 2 //│ res //│ = [ 0, 2 ] //│ res //│ = [ 0, 200 ] :js test(0, y: 1 + 2) //│ [0, Int] //│ // Prelude //│ class TypingUnit14 {} //│ const typing_unit14 = new TypingUnit14; //│ // Query 1 //│ res = ((y_1) => test1(0, y_1))(1 + 2); //│ // End of generated code //│ res //│ = [ 0, 3 ] fun fff(x: Int, y: Int, z: Int) = (x - y) * z //│ fun fff: (x: Int, y: Int, z: Int) -> Int // * Testing renaming :e fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ ╔══[ERROR] identifier not found: y_1 //│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: z_1 //│ ║ l.152: fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ ╙── ^^^ //│ Int //│ Code generation encountered an error: //│ unresolved symbol z_1 :js let y_1 = 2 let z_1 = 3 fff(y: 2, z: y_1 + 1, x: z_1 - 2) //│ let y_1: 2 //│ let z_1: 3 //│ Int //│ // Prelude //│ class TypingUnit17 {} //│ const typing_unit17 = new TypingUnit17; //│ // Query 1 //│ globalThis["y_1"] = 2; //│ // Query 2 //│ globalThis["z_1"] = 3; //│ // Query 3 //│ res = ((z_2) => ((x_1) => fff(x_1, 2, z_2))(z_1 - 2))(y_1 + 1); //│ // End of generated code //│ y_1 //│ = 2 //│ z_1 //│ = 3 //│ res //│ = -3 class A() { fun ma(x: Int, y: Int) = x - y fun mma(x: Int, y: Int) = y - x } //│ class A() { //│ fun ma: (x: Int, y: Int) -> Int //│ fun mma: (x: Int, y: Int) -> Int //│ } let x = A() x.ma(y: 2, x: 1) //│ let x: A //│ Int //│ x //│ = A {} //│ res //│ = -1 A().ma(x: 1, y: 2) //│ Int //│ res //│ = -1 id(x).ma(y: 2, x: 1) //│ Int //│ res //│ = -1 fun print(x: Int) = (y: Int, z: Int) => log([x, y, z]) let p = print(0) //│ fun print: (x: Int) -> (y: Int, z: Int) -> () //│ let p: (y: Int, z: Int) -> () //│ p //│ = [Function (anonymous)] p(z: 1, y: 2) //│ () //│ res //│ = undefined //│ // Output //│ [ 0, 2, 1 ] :e fun print(x) = (y, z) => log([x, y, z]) let p = print(0) p(z: 1, y: 2) //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments //│ ║ l.234: p(z: 1, y: 2) //│ ╙── ^^^^^^^^^^^^ //│ fun print: anything -> (anything, anything) -> () //│ let p: (anything, anything) -> () //│ error //│ p //│ = [Function (anonymous)] //│ res //│ = undefined //│ // Output //│ [ 0, 1, 2 ] class Baz() { fun f(x: Int, y: Int) = log([x, y]) } Baz().f(y: 1, x: 2) //│ class Baz() { //│ fun f: (x: Int, y: Int) -> () //│ } //│ () //│ res //│ = undefined //│ // Output //│ [ 2, 1 ] let b = Baz() b.f(y: 1, x: 2) //│ let b: Baz //│ () //│ b //│ = Baz {} //│ res //│ = undefined //│ // Output //│ [ 2, 1 ] class A(val x: Int, val y: Int) //│ class A(x: Int, y: Int) let z = A(y: 2, x: 1) z.x z.y //│ let z: A //│ Int //│ z //│ = A {} //│ res //│ = 1 //│ res //│ = 2 :e (f => f(x: a)) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.290: (f => f(x: a)) //│ ╙── ^ //│ anything -> error //│ Code generation encountered an error: //│ unresolved symbol a :e (f => f)(error)(x: a) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.299: (f => f)(error)(x: a) //│ ╙── ^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol a :e (f => f)(42)(x: a) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.308: (f => f)(42)(x: a) //│ ╙── ^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol a :e (f => f)(if true then 123 else false)(x: a) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.317: (f => f)(if true then 123 else false)(x: a) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol a (f => f)(if true then (x: Int) => x + 1 else error)(x: 123) //│ Int //│ res //│ = 124 :e (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.331: (f => if true then f else id)(if true then (x: Int) => x + 1 else id)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res //│ = 124 :e (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.340: (f => if true then f else id)(if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res //│ = 124 fun foo(f: (x: Int) => Int) = f(x: 123) //│ fun foo: (f: (x: Int) -> Int) -> Int fun foo(f: ((x: Int) => Int) & 'a) = [f(x: 123), f] //│ fun foo: forall 'b 'a. (f: (x: Int) -> Int & 123 -> 'b & 'a) -> ['b, (x: Int) -> Int & 'a] foo((x: Int) => 1) //│ [1, (x: Int) -> 1] //│ res //│ = [ 1, [Function (anonymous)] ] :e fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> Int | 'a` for applying named arguments //│ ║ l.362: fun foo(f: ((x: Int) => Int) | 'a) = f(x: 123) //│ ╙── ^ //│ fun foo: (f: anything) -> error // * the result of the if-then-else is a TV with two LBs: the type of x and the function type :e fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> ?a | ?b` for applying named arguments //│ ║ l.371: fun foo(x) = (if true then (x: Int) => x + 1 else x)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun foo: anything -> error foo((y: Int) => y) //│ error //│ res //│ = 124 :e // TODO later: this could be made to work... fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `(x: Int) -> (?a | ?b)` for applying named arguments //│ ║ l.384: fun foo(x) = (if true then (x: Int) => x + 1 else (x: Int) => x + 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun foo: anything -> error fun foo(x) = if true then (x: Int) => x + 1 else x //│ fun foo: forall 'a. 'a -> ((x: Int) -> Int | 'a) :e foo((y: Int) => y)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.395: foo((y: Int) => y)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ error //│ res //│ = 124 :e foo((x: Int) => x - 1)(x: 123) //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `?a` for applying named arguments //│ ║ l.404: foo((x: Int) => x - 1)(x: 123) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ error //│ res //│ = 124 fun foo1(x) = [x + 1, x] //│ fun foo1: forall 'a. (Int & 'a) -> [Int, 'a] :e foo1(x: 123) //│ ╔══[ERROR] Cannot use named arguments as the function type has untyped arguments //│ ║ l.417: foo1(x: 123) //│ ╙── ^^^^^^^^ //│ error //│ res //│ = [ 124, 123 ] fun foo1(x: Int & 'a) = [x + 1, x] //│ fun foo1: forall 'a. (x: Int & 'a) -> [Int, Int & 'a] foo1(x: 123) //│ [Int, 123] //│ res //│ = [ 124, 123 ] ================================================ FILE: shared/src/test/diff/nu/NestedClasses.mls ================================================ :NewDefs class C0() { class NC0() } //│ class C0() { //│ class NC0() //│ } let c = C0() //│ let c: C0 //│ c //│ = C0 {} :e c.NC0 //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.17: c.NC0 //│ ╙── ^^^^ //│ error //│ res //│ = [Function (anonymous)] { //│ class: [class NC0], //│ unapply: [Function: unapply] //│ } module M0 { class NC0 } //│ module M0 { //│ class NC0 { //│ constructor() //│ } //│ } :e M0.NC0 //│ ╔══[ERROR] Access to class member not yet supported //│ ║ l.39: M0.NC0 //│ ╙── ^^^^ //│ error //│ res //│ = [class NC0] module M1 { module NM1 } //│ module M1 { //│ module NM1 //│ } :e M1.NM1 //│ ╔══[ERROR] Access to module member not yet supported //│ ║ l.56: M1.NM1 //│ ╙── ^^^^ //│ error //│ res //│ = NM1 { class: [class NM1] } ================================================ FILE: shared/src/test/diff/nu/NestedRecords.mls ================================================ :NewDefs let f(x) = [x, x] //│ let f: forall 'a. 'a -> ['a, 'a] //│ f //│ = [Function: f] let a = { u: f(f(f(1))), v: f(f(f(1))) } //│ let a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} //│ a //│ = { //│ u: [ [ [Array], [Array] ], [ [Array], [Array] ] ], //│ v: [ [ [Array], [Array] ], [ [Array], [Array] ] ] //│ } { a } //│ { //│ a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} //│ } //│ res //│ = { a: { u: [ [Array], [Array] ], v: [ [Array], [Array] ] } } { a, b: a } //│ { //│ a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]}, //│ b: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} //│ } //│ res //│ = { //│ a: { u: [ [Array], [Array] ], v: [ [Array], [Array] ] }, //│ b: { u: [ [Array], [Array] ], v: [ [Array], [Array] ] } //│ } { x: { a, b: a } } //│ { //│ x: { //│ a: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]}, //│ b: {u: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]], v: [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]} //│ } //│ } //│ res //│ = { x: { a: { u: [Array], v: [Array] }, b: { u: [Array], v: [Array] } } } ================================================ FILE: shared/src/test/diff/nu/New.mls ================================================ :NewDefs class Foo[A](x: A) //│ class Foo[A](x: A) let f = Foo(1) //│ let f: Foo['A] //│ where //│ 'A :> 1 //│ f //│ = Foo {} // let f = new Foo(1) if f is Foo then 1 else 0 //│ 0 | 1 //│ res //│ = 1 if f is Foo(a) then a else 0 //│ 0 | 1 //│ res //│ = 1 // case f of // { Foo -> // let a = f.x // a // | _ -> 0 // } // Foo(A) =:= Foo & { x: A } fun test(x) = if x is Foo(a) then a //│ fun test: forall 'a. Foo['a] -> 'a test(f) //│ 1 //│ res //│ = 1 class PoInt(x: Int, y: Int) //│ class PoInt(x: Int, y: Int) let origin = new PoInt(0, 0) //│ let origin: PoInt //│ origin //│ = PoInt {} ================================================ FILE: shared/src/test/diff/nu/NewNew.mls ================================================ :NewDefs class Foo(val x: Int) //│ class Foo(x: Int) let f = Foo(1) //│ let f: Foo //│ f //│ = Foo {} f.x //│ Int //│ res //│ = 1 let f = new Foo(1) //│ let f: Foo //│ f //│ = Foo {} f.x //│ Int //│ res //│ = 1 new Foo(1).x //│ Int //│ res //│ = 1 if f is Foo then 1 else 0 //│ 0 | 1 //│ res //│ = 1 if f is Foo(a) then a else 0 //│ Int //│ res //│ = 1 let f = Foo //│ let f: (x: Int) -> Foo //│ f //│ = [Function (anonymous)] { //│ class: [class Foo], //│ unapply: [Function: unapply] //│ } f(1) //│ Foo //│ res //│ = Foo {} class Foo { constructor(x: Int){} val y = 2 } //│ class Foo { //│ constructor(x: Int) //│ val y: 2 //│ } new Foo(1).y //│ 2 //│ res //│ = 2 :e let f = Foo(1) //│ ╔══[ERROR] Construction of unparameterized class Foo should use the `new` keyword //│ ║ l.71: let f = Foo(1) //│ ╙── ^^^ //│ let f: Foo //│ f //│ Runtime error: //│ TypeError: Class constructor Foo cannot be invoked without 'new' :e let f = new Foo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.81: let f = new Foo //│ ║ ^^^ //│ ╟── argument list of type `[]` does not match type `[x: Int]` //│ ║ l.81: let f = new Foo //│ ╙── ^ //│ let f: Foo | error //│ f //│ = Foo {} :e f(1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.93: f(1) //│ ║ ^^^^ //│ ╟── application of type `Foo` is not a function //│ ║ l.81: let f = new Foo //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `1 -> ?a` //│ ║ l.93: f(1) //│ ╙── ^ //│ error //│ res //│ Runtime error: //│ TypeError: f4 is not a function :e new Foo("2") //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.109: new Foo("2") //│ ║ ^^^^^^^^^^^^ //│ ╟── string literal of type `"2"` is not an instance of type `Int` //│ ║ l.109: new Foo("2") //│ ║ ^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.57: constructor(x: Int){} //│ ╙── ^^^ //│ Foo | error //│ res //│ = Foo {} :e new Foo() //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.124: new Foo() //│ ║ ^^^^^^^^^ //│ ╟── argument list of type `[]` does not match type `[x: Int]` //│ ║ l.124: new Foo() //│ ╙── ^^ //│ Foo | error //│ res //│ = Foo {} class PoInt[out A](x: A, y: A) //│ class PoInt[A](x: A, y: A) new PoInt(1, 2) //│ PoInt[1 | 2] //│ res //│ = PoInt {} :e new PoInt //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.146: new PoInt //│ ║ ^^^^^ //│ ╟── argument list of type `[]` does not match type `[x: ?A, y: ?A]` //│ ║ l.146: new PoInt //│ ╙── ^ //│ PoInt[nothing] | error //│ res //│ = PoInt {} :e new PoInt['_] //│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported //│ ║ l.158: new PoInt['_] //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.158: new PoInt['_] //│ ║ ^^^^^^^^^ //│ ╟── argument list of type `[]` does not match type `[x: ?A, y: ?A]` //│ ║ l.158: new PoInt['_] //│ ╙── ^ //│ PoInt[nothing] | error //│ Code generation encountered an error: //│ Unsupported `new` class term: TyApp(Var(PoInt),List('_)) :e new PoInt[Str](0, 0) //│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported //│ ║ l.173: new PoInt[Str](0, 0) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ PoInt[0] //│ Code generation encountered an error: //│ Unsupported `new` class term: TyApp(Var(PoInt),List(TypeName(Str))) type T = PoInt[Str] //│ type T = PoInt[Str] :e new T(0, 0) //│ ╔══[ERROR] Type alias T cannot be used in `new` expression //│ ║ l.185: new T(0, 0) //│ ╙── ^^^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ type alias T is not a valid expression let origin = new PoInt(0, 0) //│ let origin: PoInt[0] //│ origin //│ = PoInt {} :e // TODO support let origin = PoInt[Int](0, 0) //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.199: let origin = PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^ //│ let origin: PoInt[0] //│ origin //│ = PoInt {} :e // TODO support let origin = new PoInt[Int](0, 0) //│ ╔══[ERROR] Type arguments in `new` expressions are not yet supported //│ ║ l.208: let origin = new PoInt[Int](0, 0) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ let origin: PoInt[0] //│ Code generation encountered an error: //│ Unsupported `new` class term: TyApp(Var(PoInt),List(TypeName(Int))) :e // TODO support new {} //│ ╔══[ERROR] Unexpected type `anything` after `new` keyword //│ ║ l.218: new {} //│ ╙── ^^ //│ error //│ Code generation encountered an error: //│ Unsupported `new` class term: Bra(true,Rcd(List())) :pe :e new //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.228: new //│ ╙── ^ //│ ╔══[ERROR] Unexpected type `()` after `new` keyword //│ ║ l.228: new //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ Unsupported `new` class term: UnitLit(true) // Support? :pe :e new x: 0 //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.243: x: 0 //│ ╙── ^ //│ ╔══[ERROR] Unexpected type `nothing` after `new` keyword //│ ║ l.243: x: 0 //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ Unsupported `new` class term: Blk(List(Asc(Var(x),Literal(IntLit(0))))) fun f(x) = {x} //│ fun f: forall 'a. 'a -> {x: 'a} :e new f(1) //│ ╔══[ERROR] type identifier not found: f //│ ║ l.261: new f(1) //│ ╙── ^ //│ error //│ res //│ = { x: 1 } module Oops //│ module Oops :e new Oops //│ ╔══[ERROR] Module Oops cannot be used in `new` expression //│ ║ l.274: new Oops //│ ╙── ^^^^ //│ error //│ res //│ = Oops {} :e new Oops2 trait Oops2 //│ ╔══[ERROR] Trait Oops2 cannot be used in `new` expression //│ ║ l.284: new Oops2 //│ ╙── ^^^^^ //│ trait Oops2 //│ error //│ Code generation encountered an error: //│ trait used in term position module A { class B } //│ module A { //│ class B { //│ constructor() //│ } //│ } :ge // TODO support new A.B //│ B //│ Code generation encountered an error: //│ Unsupported `new` class term: Sel(Var(A),Var(B)) ================================================ FILE: shared/src/test/diff/nu/NoThisCtor.mls ================================================ :NewDefs class Base(val base: Int) { fun getBase1 = base fun getBase2 = this.base fun foo(x) = this.base + x } //│ class Base(base: Int) { //│ fun foo: Int -> Int //│ fun getBase1: Int //│ fun getBase2: Int //│ } class Foo() { virtual val foo: Int = 42 } //│ class Foo() { //│ val foo: Int //│ } :e class Foo1() extends Foo() { val foo: Int virtual val foo = 1 log(this.foo) } //│ ╔══[ERROR] Cannot access `this` during object initialization //│ ║ l.25: log(this.foo) //│ ╙── ^^^^ //│ class Foo1() extends Foo { //│ val foo: Int //│ } :e class Foo2() extends Foo() { virtual val foo: Int val foo = 2 constructor() { log(this.foo) } } //│ ╔══[ERROR] Cannot access `this` during object initialization //│ ║ l.39: log(this.foo) //│ ╙── ^^^^ //│ ╔══[ERROR] identifier not found: this //│ ║ l.39: log(this.foo) //│ ╙── ^^^^ //│ class Foo2() extends Foo { //│ constructor() //│ val foo: Int //│ } :e class Foo3() extends Foo() { val foo: Int virtual val foo = 3 val s = this.foo } //│ ╔══[ERROR] Cannot access `this` while initializing field s //│ ║ l.57: val s = this.foo //│ ╙── ^^^^ //│ class Foo3() extends Foo { //│ val foo: Int //│ val s: Int //│ } :e class Foo4() extends Foo() { val foo: Int virtual val foo = 4 fun bar(x) = this.foo + x // ok let bb = bar(0) // call `this` indirectly } //│ ╔══[ERROR] Cannot access `this` while initializing field bb //│ ║ l.72: let bb = bar(0) // call `this` indirectly //│ ║ ^^^^^^^^^^^ //│ ╟── The access to `this` is here //│ ║ l.71: fun bar(x) = this.foo + x // ok //│ ╙── ^^^^ //│ class Foo4() extends Foo { //│ fun bar: Int -> Int //│ let bb: Int //│ val foo: Int //│ } :e class Foo5() extends Foo() { val foo: Int val x = bar(0) fun bar(y: Int) = this.foo + y } //│ ╔══[ERROR] Cannot access `this` while initializing field x //│ ║ l.89: val x = bar(0) //│ ║ ^^^^^^^^^^ //│ ╟── The access to `this` is here //│ ║ l.90: fun bar(y: Int) = this.foo + y //│ ╙── ^^^^ //│ class Foo5() extends Foo { //│ fun bar: (y: Int) -> Int //│ val foo: Int //│ val x: Int //│ } class Foo6() extends Foo() { val baz: Int val baz = 0 val y = this.baz // baz is final } //│ class Foo6() extends Foo { //│ val baz: Int //│ val foo: Int //│ val y: Int //│ } class Bar() { val d: Int val d = 1 fun add(x) = x + this.d } //│ class Bar() { //│ fun add: Int -> Int //│ val d: Int //│ } :e class Bar2() extends Bar() { val two = this.add(1) // add is final, but it refers to `this` } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.127: val two = this.add(1) // add is final, but it refers to `this` //│ ╙── ^^^^ //│ ╔══[ERROR] Cannot access `this` while initializing field two //│ ║ l.127: val two = this.add(1) // add is final, but it refers to `this` //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── The access to `this` is here //│ ║ l.118: fun add(x) = x + this.d //│ ╙── ^^^^ //│ class Bar2() extends Bar { //│ fun add: Int -> Int //│ val d: Int //│ val two: error //│ } // it accesses this in an unusual way! :e abstract class Foo: (Int -> Int) { val x = f fun f = this(0) } //│ ╔══[ERROR] Cannot access `this` while initializing field x //│ ║ l.147: val x = f //│ ║ ^^^^^ //│ ╟── The access to `this` is here //│ ║ l.148: fun f = this(0) //│ ╙── ^^^^ //│ abstract class Foo: Int -> Int { //│ fun f: nothing //│ val x: nothing //│ } ================================================ FILE: shared/src/test/diff/nu/NuAlexJ.mls ================================================ :NewDefs fun foo(x) = [x, x] //│ fun foo: forall 'a. 'a -> ['a, 'a] id //│ forall 'a. 'a -> 'a //│ res //│ = [Function: id] let r = foo(id) //│ let r: [forall 'a. 'a -> 'a, forall 'a. 'a -> 'a] //│ r //│ = [ [Function: id], [Function: id] ] fun fst(x, _) = x fun snd(_, x) = x //│ fun fst: forall 'a. ('a, anything) -> 'a //│ fun snd: forall 'b. (anything, 'b) -> 'b fst(1, 2) //│ 1 //│ res //│ = 1 :e fst([1, 2]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.29: fst([1, 2]) //│ ║ ^^^^^^^^^^^ //│ ╟── argument of type `[[1, 2]]` does not match type `[?a, ?b]` //│ ║ l.29: fst([1, 2]) //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.18: fun fst(x, _) = x //│ ╙── ^^^^^^ //│ error //│ res //│ = [ 1, 2 ] fun fst([x, _]) = x fun snd([_, x]) = x //│ fun fst: forall 'a. (['a, anything]) -> 'a //│ fun snd: forall 'b. ([anything, 'b]) -> 'b let s = fst(r) //│ let s: forall 'a. 'a -> 'a //│ s //│ = [Function: id] s("hello") //│ "hello" //│ res //│ = 'hello' s(123) //│ 123 //│ res //│ = 123 fst(r)("hello") //│ "hello" //│ res //│ = 'hello' ================================================ FILE: shared/src/test/diff/nu/NuForallTerms.mls ================================================ :NewDefs forall 'a: (x: 'a) => x //│ forall 'a. (x: 'a) -> 'a //│ res //│ = [Function: res] forall 'a: (x: 'a) => x //│ forall 'a. (x: 'a) -> 'a //│ res //│ = [Function (anonymous)] ================================================ FILE: shared/src/test/diff/nu/NuPolymorphicTypeAliases.mls ================================================ :NewDefs type F[A] = forall 'a: (A, 'a) -> [A, 'a] //│ type F[A] = forall 'a. (A, 'a) -> [A, 'a] fun f[B] = ((x: B, y) => [x, y]) : F[B] //│ fun f: forall 'B. F['B] fun f = forall 'B: ((x: 'B, y) => [x, y]) : F['B] //│ fun f: forall 'B. F['B] module A { type F[A] = forall 'a: (A, 'a) -> [A, 'a] } //│ module A { //│ type F[A] = forall 'a. (A, 'a) -> [A, 'a] //│ } :pe // TODO fun f[B] = ((x: B, y) => [x, y]) : A.F[B] //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.26: ((x: B, y) => [x, y]) : A.F[B] //│ ╙── ^^^^^^ //│ fun f: anything class Test[B] { fun f(f: F[B]): F[B] = (x, y) => f(x, y) } //│ class Test[B] { //│ constructor() //│ fun f: (f: F[B]) -> F[B] //│ } class Test[B](f: F[B]) { fun g: F[B] = (x, y) => f(x, y) } //│ class Test[B](f: F[B]) { //│ fun g: F[B] //│ } class Test[B] { discard of ((x: B, y) => [x, y]) : F[B] } //│ class Test[B] { //│ constructor() //│ } type F[A] = (A, 'a) -> [A, 'a] //│ type F[A] = (A, 'a) -> [A, 'a] fun f[B] = ((x: B, y) => [x, y]) : F[B] //│ fun f: forall 'B. F['B] fun f = forall 'B: ((x: 'B, y) => [x, y]) : F['B] //│ fun f: forall 'B. F['B] class Test[B] { fun f(f: F[B]): F[B] = (x, y) => f(x, y) } //│ class Test[B] { //│ constructor() //│ fun f: (f: F[B]) -> F[B] //│ } // * Note: NOT polymorphic! type T = 'a -> 'a //│ type T = 'a -> 'a (id : T)(0) //│ 0 //│ res //│ = 0 (id : T)(true) //│ 0 | true //│ res //│ = true ================================================ FILE: shared/src/test/diff/nu/NuScratch.mls ================================================ :NewDefs ================================================ FILE: shared/src/test/diff/nu/Numbers.mls ================================================ :NewDefs let n = 123 let m = n : Int let o = m : Num let p = o : Object let o = n : Num let p = n : Object let p = m : Object //│ let n: 123 //│ let m: Int //│ let o: Num //│ let p: Object //│ let o: Num //│ let p: Object //│ let p: Object //│ n //│ = 123 //│ m //│ = 123 //│ o //│ = 123 //│ p //│ = 123 //│ o //│ = 123 //│ p //│ = 123 //│ p //│ = 123 let x = NaN //│ let x: Num //│ x //│ = NaN // TODO polymorphic Num operations x + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.40: x + 1 //│ ║ ^^^^^ //│ ╟── reference of type `Num` is not an instance of type `Int` //│ ║ l.34: let x = NaN //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.40: x + 1 //│ ╙── ^ //│ Int | error //│ res //│ = NaN true : Bool //│ Bool //│ res //│ = true true : Bool | Str //│ Str | false | true //│ res //│ = true ================================================ FILE: shared/src/test/diff/nu/Object.mls ================================================ :NewDefs class A() class B() //│ class A() //│ class B() let a: Object = A() //│ let a: Object //│ a //│ = A {} module M //│ module M M: Object //│ Object //│ res //│ = M { class: [class M] } fun foo(x) = if x is A then true //│ fun foo: A -> true fun foo(x) = if x is A then true else false //│ fun foo: Object -> Bool :e fun foo(x: anything) = if x is A then true //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type `anything` is not an instance of type `Object` //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `Object` //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type `anything` is not an instance of type `A` //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `A` //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.34: fun foo(x: anything) = if x is A then true //│ ╙── ^ //│ fun foo: (x: anything) -> true :e fun foo(x: anything) = if x is A then true else false //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.59: fun foo(x: anything) = if x is A then true else false //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `anything` is not an instance of type `Object` //│ ║ l.59: fun foo(x: anything) = if x is A then true else false //│ ║ ^^^^^^^^ //│ ╟── but it flows into reference with expected type `Object` //│ ║ l.59: fun foo(x: anything) = if x is A then true else false //│ ╙── ^ //│ fun foo: (x: anything) -> Bool fun foo(x: Object) = if x is A then true else false //│ fun foo: (x: Object) -> Bool // TODO make this a rigid type variable! // :e fun foo = forall 'a; (x: 'a) => if x is A then true else false //│ ╔══[PARSE ERROR] Expected `:` after `forall` section //│ ║ l.78: fun foo = forall 'a; (x: 'a) => if x is A then true else false //│ ╙── ^ //│ fun foo: () //│ (x: Object) -> Bool //│ res //│ = [Function: res] :e Object //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated //│ ║ l.90: Object //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor //│ ║ l.90: Object //│ ╙── ^^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol Object :e Object() //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated //│ ║ l.102: Object() //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor //│ ║ l.102: Object() //│ ╙── ^^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol Object :e new Object //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated //│ ║ l.114: new Object //│ ╙── ^^^^^^ //│ Object //│ Code generation encountered an error: //│ unresolved symbol Object // TODO class B() extends Object //│ class B() extends Object //│ Code generation encountered an error: //│ unresolved parent Object. class C() extends A //│ class C() extends A let o: Object = C() //│ let o: Object //│ o //│ = C {} fun foo(x) = if x is A then true B then true else false //│ fun foo: Object -> Bool foo(0) //│ Bool //│ res //│ = false foo(o) //│ Bool //│ res //│ = true (0 : Int) : Object //│ Object //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/nu/OpLam.mls ================================================ :NewDefs x => x is 42 //│ Object -> Bool //│ res //│ = [Function: res] :pe fun (|>;) foo(a, b) = [a, b] //│ ╔══[PARSE ERROR] Unexpected semicolon after symbolic name //│ ║ l.11: fun (|>;) foo(a, b) = [a, b] //│ ╙── ^ //│ fun (|>) foo: forall 'a 'b. ('a, 'b) -> ['a, 'b] 42 |>; x => x //│ forall 'a. 'a -> 'a //│ res //│ = [ 42, undefined ] //│ res //│ = [Function: res] fun (>>) compose(f, g) = x => g(f(x)) //│ fun (>>) compose: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c succ >> x => x + 2 //│ Int -> Int //│ res //│ = [Function (anonymous)] :e x => x + 2 >> succ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.34: x => x + 2 >> succ //│ ║ ^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` is not a function //│ ║ l.34: x => x + 2 >> succ //│ ║ ^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) //│ ╙── ^ //│ Int -> (error | anything -> Int) //│ res //│ = [Function: res] (x => x + 2) >> succ //│ Int -> Int //│ res //│ = [Function (anonymous)] :e x => x + 2 >> succ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.57: x => x + 2 //│ ║ ^ //│ ║ l.58: >> succ //│ ║ ^^^^^^^^^^ //│ ╟── integer literal of type `2` is not a function //│ ║ l.57: x => x + 2 //│ ║ ^ //│ ╟── Note: constraint arises from application: //│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) //│ ║ ^^^^ //│ ╟── from reference: //│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.57: x => x + 2 //│ ║ ^^^^^ //│ ║ l.58: >> succ //│ ║ ^^^^^^^^^ //│ ╟── function of type `?a -> ?b` is not an instance of type `Int` //│ ║ l.25: fun (>>) compose(f, g) = x => g(f(x)) //│ ║ ^^^^^^^^^^^^ //│ ╟── but it flows into operator application with expected type `Int` //│ ║ l.57: x => x + 2 //│ ║ ^ //│ ║ l.58: >> succ //│ ╙── ^^^^^^^^^^ //│ Int -> (Int | error) //│ res //│ = [Function: res] (x => x + 2) >> succ //│ Int -> Int //│ res //│ = [Function (anonymous)] :pe >> succ //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.97: >> succ //│ ╙── ^^ //│ Int -> Int //│ res //│ = [Function: succ] :e x => x.y => y //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.107: x => x.y => y //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.107: x => x.y => y //│ ╙── ^ //│ anything -> error -> error //│ Code generation encountered an error: //│ term Sel(Var(x),Var(y)) is not a valid pattern ================================================ FILE: shared/src/test/diff/nu/OptionFilter.mls ================================================ :NewDefs // * Minimization of code that used to cause a problem: abstract class Option[T]: (None | ()) { virtual fun filter: Option[T] } module None extends Option[nothing] { fun filter = None } //│ abstract class Option[T]: None | () { //│ fun filter: Option[T] //│ } //│ module None extends Option { //│ fun filter: None //│ } // * Original code: abstract class Option[out T]: (Some[T] | None) { virtual fun filter: (p: T -> Bool) -> Option[T] } class Some[out T](val value: T) extends Option[T] { fun filter(p) = if p of value then Some(value) else None } module None extends Option[nothing] { fun filter(_) = None } //│ abstract class Option[T]: None | Some[T] { //│ fun filter: (p: T -> Bool) -> Option[T] //│ } //│ class Some[T](value: T) extends Option { //│ fun filter: (T -> Bool) -> (None | Some[T]) //│ } //│ module None extends Option { //│ fun filter: anything -> None //│ } abstract class Boxful[T] { virtual fun clone(): Boxful[T] } class MetalBox[T](value: T) extends Boxful[T] { fun clone(): Boxful[T] = MetalBox(value) } //│ abstract class Boxful[T] { //│ fun clone: () -> Boxful[T] //│ } //│ class MetalBox[T](value: T) extends Boxful { //│ fun clone: () -> Boxful[T] //│ } fun makeMetalBox(value: 'A): Boxful['A] = MetalBox(value) abstract class Boxful[T] { virtual fun clone(): Boxful[T] } class MetalBox[T](value: T) extends Boxful[T] { fun clone(): Boxful[T] = makeMetalBox(value) } //│ fun makeMetalBox: forall 'T. (value: 'T) -> Boxful['T] //│ abstract class Boxful[T] { //│ fun clone: () -> Boxful[T] //│ } //│ class MetalBox[T](value: T) extends Boxful { //│ fun clone: () -> Boxful[T] //│ } ================================================ FILE: shared/src/test/diff/nu/OverrideShorthand.mls ================================================ :NewDefs class Pair(lhs: Int, rhs: Int) //│ class Pair(lhs: Int, rhs: Int) :p :e fun f(override Pair(x, y)) = x + y //│ |#fun| |f|(|#override| |Pair|(|x|,| |y|)|)| |#=| |x| |+| |y| //│ AST: TypingUnit(List(NuFunDef(None,Var(f),None,List(),Left(Lam(Tup(List((None,Fld(_,Var(_$0))))),If(IfOpApp(Var(_$0),Var(is),IfThen(App(Var(Pair),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))),App(Var(+),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))))),Some(App(Sel(Super(),Var(f)),Tup(List((None,Fld(_,Var(_$0))))))))))))) //│ Parsed: fun f = (_$0,) => if _$0 is (Pair(x, y,)) then +(x, y,) else (super).f(_$0,); //│ ╔══[ERROR] identifier not found: super //│ ║ l.11: fun f(override Pair(x, y)) = x + y //│ ╙── ^^^^^^^^ //│ fun f: Object -> (Int | error) //│ Syntax error: //│ 'super' keyword unexpected here mixin Test { fun f(override Pair(x, y)) = x + y } //│ mixin Test() { //│ super: {f: 'a -> 'b} //│ fun f: (Object & 'a & ~#Pair | Pair) -> (Int | 'b) //│ } :pe :e fun f(override Pair(x, y), z) = x + y //│ ╔══[PARSE ERROR] Unsupported 'override' parameter list shape //│ ║ l.34: fun f(override Pair(x, y), z) = x + y //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.34: fun f(override Pair(x, y), z) = x + y //│ ╙── ^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.34: fun f(override Pair(x, y), z) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.34: fun f(override Pair(x, y), z) = x + y //│ ╙── ^ //│ fun f: (error, anything) -> Int //│ Code generation encountered an error: //│ term App(Var(Pair),Tup(List((None,Fld(_,Var(x))), (None,Fld(_,Var(y)))))) is not a valid pattern // TODO // :pe // fun f(override Pair(x, y)): Int ================================================ FILE: shared/src/test/diff/nu/ParamImplementing.mls ================================================ :NewDefs trait T { fun x: Int } mixin M(val x: Bool) //│ trait T { //│ fun x: Int //│ } //│ mixin M(x: Bool) :e module C extends T, M(false) C.x //│ ╔══[ERROR] Type mismatch in reference: //│ ║ l.12: module C extends T, M(false) //│ ║ ^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ╟── Note: constraint arises from type reference: //│ ║ l.4: trait T { fun x: Int } //│ ║ ^^^ //│ ╟── from signature of member `x`: //│ ║ l.4: trait T { fun x: Int } //│ ╙── ^^^^^^ //│ module C extends T //│ false //│ res //│ = false trait T { fun x: Int } mixin M(val x: Num) //│ trait T { //│ fun x: Int //│ } //│ mixin M(x: Num) module C extends T, M(0) C.x //│ module C extends T //│ 0 //│ res //│ = 0 trait T { fun x: Int } mixin M(x: Bool) //│ trait T { //│ fun x: Int //│ } //│ mixin M(x: Bool) :e module C extends T, M(false) C.x //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C` //│ ║ l.53: module C extends T, M(false) //│ ║ ^ //│ ╟── Declared here: //│ ║ l.45: trait T { fun x: Int } //│ ╙── ^^^^^^^^^^ //│ module C extends T { //│ fun x: Int //│ } //│ Int //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/nu/ParamOverride.mls ================================================ :NewDefs class Base0(n: Num) //│ class Base0(n: Num) // TODO class Derived0(n: Int) extends Base //│ ╔══[ERROR] Could not find definition `Base` //│ ║ l.8: class Derived0(n: Int) extends Base //│ ╙── ^^^^ //│ class Derived0(n: Int) //│ Code generation encountered an error: //│ unresolved parent Base. mixin Base1(val n: Num) { fun original = n } //│ mixin Base1(n: Num) { //│ fun original: Num //│ } :e mixin DerivedBad(n: Int) extends Base //│ ╔══[ERROR] mixin definitions cannot yet extend parents //│ ║ l.25: mixin DerivedBad(n: Int) extends Base //│ ╙── ^^^^ //│ mixin DerivedBad(n: Int) mixin Derived1(val n: Int) { fun foo = [n, this.n, super.n] } //│ mixin Derived1(n: Int) { //│ super: {n: 'n} //│ this: {n: 'n0} //│ fun foo: [Int, 'n0, 'n] //│ } class Test0() extends Base1(1/2), Derived1(1) //│ class Test0() { //│ fun foo: [Int, 1, Num] //│ fun original: Num //│ } let t = Test0() //│ let t: Test0 //│ t //│ = Test0 {} t.n //│ 1 //│ res //│ = 1 t.original //│ Num //│ res //│ = 0.5 t.foo //│ [Int, 1, Num] //│ res //│ = [ 1, 1, 0.5 ] module Test1 extends Base1(1/2), Derived1(1) { fun n = this.n } //│ module Test1 { //│ fun foo: [Int, 1, Num] //│ fun n: 1 //│ fun original: Num //│ } :re Test1.n //│ 1 //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded class Test2(val n: Str) extends Base1(1/2), Derived1(1) { fun bar = [this.foo, n, this.n, this.original] } //│ class Test2(n: Str) { //│ fun bar: [[Int, Str, Num], Str, Str, Num] //│ fun foo: [Int, Str, Num] //│ fun original: Num //│ } Test2("test").bar //│ [[Int, Str, Num], Str, Str, Num] //│ res //│ = [ [ 1, 'test', 0.5 ], 'test', 'test', 0.5 ] class Test3(val n: Str) extends Base1(1/2), Derived1(length(n)) { fun foo = [super.foo, n, this.original] } //│ class Test3(n: Str) { //│ fun foo: [[Int, Str, Num], Str, Num] //│ fun original: Num //│ } Test3("test").foo //│ [[Int, Str, Num], Str, Num] //│ res //│ = [ [ 4, 'test', 0.5 ], 'test', 0.5 ] ================================================ FILE: shared/src/test/diff/nu/ParamOverriding.mls ================================================ :NewDefs mixin Over { fun p = "hi" } //│ mixin Over() { //│ fun p: "hi" //│ } class Base1(val p: Int) extends Over { fun test = [p, this.p] fun test2 = super.p } //│ class Base1(p: Int) { //│ fun p: "hi" //│ fun test: [Int, Int] //│ fun test2: Int //│ } Base1(123).test //│ [Int, Int] //│ res //│ = [ 123, 123 ] Base1(123).test2 //│ Int //│ res //│ = 'hi' ================================================ FILE: shared/src/test/diff/nu/ParamPassing.mls ================================================ :NewDefs class Foo(x: Int) //│ class Foo(x: Int) class Bar(z: Int, y: Int) extends Foo(z + y) //│ class Bar(z: Int, y: Int) extends Foo class Bar(x: Int, y: Int) extends Foo(x + y) //│ class Bar(x: Int, y: Int) extends Foo class Foo(val x: Int) //│ class Foo(x: Int) module Bar extends Foo(11) Bar.x //│ module Bar extends Foo //│ 11 //│ res //│ = 11 :e // FIXME module Bar extends Foo(11) { fun get = this.x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.25: module Bar extends Foo(11) { fun get = this.x } //│ ╙── ^^ //│ module Bar extends Foo { //│ fun get: error //│ } mixin AA(a: Int) { } //│ mixin AA(a: Int) mixin BB {} //│ mixin BB() class C(x: Int) extends BB //│ class C(x: Int) class D(x: Int) extends AA(x) //│ class D(x: Int) class E(x: Int) extends BB, AA(x) //│ class E(x: Int) class Foo(x: Int) //│ class Foo(x: Int) :e Foo(1).x //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.56: Foo(1).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.52: class Foo(x: Int) //│ ╙── ^ //│ Int | error //│ res //│ = undefined :e Foo(1).#x //│ ╔══[ERROR] identifier not found: .# //│ ║ l.68: Foo(1).#x //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.68: Foo(1).#x //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol .# if Foo(1) is Foo(x) then x //│ Int //│ res //│ = 1 class Foo(val x: Int) //│ class Foo(x: Int) Foo(1).x //│ Int //│ res //│ = 1 if Foo(1) is Foo(x) then x //│ Int //│ res //│ = 1 :e class Bar(x: Int) extends Foo(x) //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.100: class Bar(x: Int) extends Foo(x) //│ ║ ^ //│ ╟── Originally declared here: //│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ class Bar(x: Int) extends Foo :e Bar(11).x //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.110: Bar(11).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.100: class Bar(x: Int) extends Foo(x) //│ ╙── ^ //│ Int | error //│ res //│ = 11 :e class Bar(val x: Int) extends Foo(x + 1) //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.123: class Bar(val x: Int) extends Foo(x + 1) //│ ║ ^ //│ ╟── Originally declared here: //│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ class Bar(x: Int) extends Foo Bar(11).x //│ Int //│ res //│ = 11 :e class Bar extends Foo(1) { val x: 2 } //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.138: class Bar extends Foo(1) { val x: 2 } //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: //│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ class Bar extends Foo { //│ constructor() //│ val x: 2 //│ } :e module Bar extends Foo(1) { fun x = 2 } //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.151: module Bar extends Foo(1) { fun x = 2 } //│ ║ ^^^^^ //│ ╟── Originally declared here: //│ ║ l.85: class Foo(val x: Int) //│ ╙── ^ //│ module Bar extends Foo { //│ fun x: 2 //│ } class A(val x: Int) //│ class A(x: Int) module B extends A(42) //│ module B extends A B.x //│ 42 //│ res //│ = 42 class A(x: Int) //│ class A(x: Int) module B extends A(42) //│ module B extends A :e B.x //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.182: B.x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.175: class A(x: Int) //│ ╙── ^ //│ Int | error //│ res //│ = undefined abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ abstract class Foo[A](x: A) { //│ fun i: A -> A //│ fun y: A //│ } abstract class Bar extends Foo(0) //│ abstract class Bar extends Foo { //│ fun i: 'A -> 'A //│ fun y: 'A //│ } //│ where //│ 'A :> 0 module Baz extends Foo(0) { fun i = id } [Baz.x, Baz.i] //│ module Baz extends Foo { //│ fun i: forall 'a. 'a -> 'a //│ fun y: 'A //│ } //│ [0, forall 'a. 'a -> 'a] //│ where //│ 'A :> 0 //│ res //│ = [ 0, [Function: id] ] :e module Bazz extends Foo(0) { val x: 2 } //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.223: val x: 2 //│ ║ ^^^^^^^^ //│ ╟── Originally declared here: //│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ ╙── ^ //│ ╔══[ERROR] Member `i` is declared (or its declaration is inherited) but is not implemented in `Bazz` //│ ║ l.222: module Bazz extends Foo(0) { //│ ║ ^^^^ //│ ╟── Declared here: //│ ║ l.195: abstract class Foo[A](val x: A) { fun y = x; fun i: A -> A } //│ ╙── ^^^^^^^^^^^^^ //│ module Bazz extends Foo { //│ fun i: 'A -> 'A //│ val x: 2 //│ fun y: 'A //│ } //│ where //│ 'A :> 0 ================================================ FILE: shared/src/test/diff/nu/Parens.mls ================================================ :NewDefs () //│ () //│ res //│ = undefined :pe (,) //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.10: (,) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.10: (,) //│ ╙── ^ //│ () //│ res //│ = undefined (1) //│ 1 //│ res //│ = 1 (1,) //│ 1 //│ res //│ = 1 (1, 2) //│ 2 //│ res //│ = 2 (1, 2,) //│ 2 //│ res //│ = 2 val x: () //│ val x: () //│ x //│ = :pe val x: (,) //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.48: val x: (,) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.48: val x: (,) //│ ╙── ^ //│ val x: () //│ x //│ = val x: (1) //│ val x: 1 //│ x //│ = val x: (1,) //│ val x: 1 //│ x //│ = :e val x: (1, 2) //│ ╔══[ERROR] type identifier not found: , //│ ║ l.70: val x: (1, 2) //│ ╙── ^^^^^^ //│ val x: error //│ x //│ = :e val x: (1, 2,) //│ ╔══[ERROR] type identifier not found: , //│ ║ l.79: val x: (1, 2,) //│ ╙── ^^^^^^^ //│ val x: error //│ x //│ = ================================================ FILE: shared/src/test/diff/nu/PartialApp.mls ================================================ :NewDefs // TODO support partial application syntax :AllowTypeErrors fun foo(x, y) = x + y //│ fun foo: (Int, Int) -> Int foo(2, _) //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.11: foo(2, _) //│ ╙── ^ //│ Int // * ie tmp => foo(2, tmp) //│ Int -> Int _.foo(1) //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.22: _.foo(1) //│ ╙── ^ //│ error // * ie x => x.foo(1) //│ forall 'a. {foo: 1 -> 'a} -> 'a _ + _ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.33: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.33: _ + _ //│ ╙── ^ //│ Int // * ie (x, y) => x + y //│ (Int, Int) -> Int _2 + _1 //│ ╔══[ERROR] identifier not found: _2 //│ ║ l.47: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _1 //│ ║ l.47: _2 + _1 //│ ╙── ^^ //│ Int // * ie (x, y) => y + x //│ (Int, Int) -> Int ================================================ FILE: shared/src/test/diff/nu/PolymorphicVariants_Alt.mls ================================================ :NewDefs :NoJS // * Adapted example from Code reuse through polymorphic variants (FOSE 2000) // * This time with an ML-style List data type encoding. // TODO improvements/things to investigate: // - constraining loop with unannotated `list_assoc` ascription // - still a number of quite ugly types class List { fun match: forall 'res: (ifNil: () => 'res, ifCons: (A, List[A]) => 'res) => 'res fun match = error } val Nil: () => List<'a> val Cons: (head: 'a, tail: List<'a>) => List<'a> //│ class List[A] { //│ constructor() //│ fun match: forall 'res. (ifNil: () -> 'res, ifCons: (A, List[A]) -> 'res) -> 'res //│ } //│ val Nil: () -> List[nothing] //│ val Cons: forall 'a. (head: 'a, tail: List['a]) -> List['a] module NotFound class Success(result: A) //│ module NotFound //│ class Success[A](result: A) fun eq(l: Str, r: Str): Bool //│ fun eq: (l: Str, r: Str) -> Bool // * Annotation currently needed to avoid later ascription loop (due to excessive TV refreshing?) // fun list_assoc(s, l) = fun list_assoc(s, l: List<'a>) = l.match( ifNil: () => NotFound, ifCons: (h, t) => if eq(s, h.0) then Success(h.1) else list_assoc(s, t) ) //│ fun list_assoc: forall 'A. (Str, l: List[{0: Str, 1: 'A}]) -> (NotFound | Success['A]) list_assoc : (Str, List<{ 0: Str, 1: 'b }>) => (NotFound | Success['b]) //│ (Str, List[{0: Str, 1: 'b}]) -> (NotFound | Success['b]) fun list_assoc(s: Str, l: List<{ 0: Str, 1: 'b }>): NotFound | Success['b] //│ fun list_assoc: forall 'b. (s: Str, l: List[{0: Str, 1: 'b}]) -> (NotFound | Success['b]) class Var(s: Str) //│ class Var(s: Str) mixin EvalVar { fun eval(sub, v) = if v is Var(s) then if list_assoc(s, sub) is NotFound then v Success(r) then r } //│ mixin EvalVar() { //│ fun eval: (List[{0: Str, 1: 'a}], Var) -> (Var | 'a) //│ } class Abs(x: Str, t: A) class App(s: A, t: A) //│ class Abs[A](x: Str, t: A) //│ class App[A](s: A, t: A) fun incr(x: {a: Int}): unit //│ fun incr: (x: {a: Int}) -> unit fun gensym(): Str //│ fun gensym: () -> Str fun Int_to_string(x: Int): Str //│ fun Int_to_string: (x: Int) -> Str mixin EvalLambda { fun eval(sub, v) = if v is App(t1, t2) then let l1 = this.eval(sub, t1) let l2 = this.eval(sub, t2) if t1 is Abs(x, t) then this.eval(Cons([x, l2], Nil()), t) else App(l1, l2) Abs(x, t) then let s = gensym() Abs(s, this.eval(Cons([x, Var(s)], sub), t)) else super.eval(sub, v) } //│ mixin EvalLambda() { //│ super: {eval: ('b, 'c) -> 'd} //│ this: { //│ eval: ('b, 'e) -> 'A & (List[[Str, 'A]], 'f) -> 'd & (List[[Str, Var] | 'a], 'g) -> 'A0 //│ } //│ fun eval: (List['a] & 'b, Abs['g] | App['e & (Abs['f] | Object & ~#Abs)] | Object & 'c & ~#Abs & ~#App) -> (Abs['A0] | App['A] | 'd) //│ } module Test1 extends EvalVar, EvalLambda //│ module Test1 { //│ fun eval: (List[{0: Str, 1: 'a}], 'b) -> 'a //│ } //│ where //│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Var //│ 'a :> Abs['a] | App['a] | Var Test1.eval(Nil(), Var("a")) //│ 'a //│ where //│ 'a :> Abs['a] | App['a] | Var Test1.eval(Nil(), Abs("b", Var("a"))) //│ 'a //│ where //│ 'a :> Abs['a] | App['a] | Var Test1.eval(Cons(["c", Var("d")], Nil()), App(Abs("b", Var("b")), Var("c"))) //│ 'a //│ where //│ 'a :> Abs['a] | App['a] | Var Test1.eval(Cons(["c", Abs("d", Var("d"))], Nil()), App(Abs("b", Var("b")), Var("c"))) //│ Abs['a] | 'a //│ where //│ 'a :> Abs['a] | App['a] | Var class Numb(n: Int) class Add(l: A, r: A) class Mul(l: A, r: A) //│ class Numb(n: Int) //│ class Add[A](l: A, r: A) //│ class Mul[A](l: A, r: A) fun map_expr(f, v) = if v is Var then v Numb then v Add(l, r) then Add(f(l), f(r)) Mul(l, r) then Mul(f(l), f(r)) //│ fun map_expr: forall 'a 'A 'b 'A0. ('a -> 'A & 'b -> 'A0, Add['b] | Mul['a] | Numb | Var) -> (Add['A0] | Mul['A] | Numb | Var) mixin EvalExpr { fun eval(sub, v) = let eta(e) = this.eval(sub, e) let vv = map_expr(eta, v) if vv is Var then super.eval(sub, vv) Add(Numb(l), Numb(r)) then Numb(l + r) Mul(Numb(l), Numb(r)) then Numb(l * r) else v } //│ mixin EvalExpr() { //│ super: {eval: ('a, Var) -> 'b} //│ this: {eval: ('a, 'c) -> Object} //│ fun eval: ('a, 'b & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | 'b) //│ } module Test2 extends EvalVar, EvalExpr //│ module Test2 { //│ fun eval: forall 'a. (List[{0: Str, 1: Object & 'b}], 'a & (Add['c] | Mul['c] | Numb | Var)) -> (Numb | Var | 'b | 'c | 'a) //│ } //│ where //│ 'c <: Add['c] | Mul['c] | Numb | Var Test2.eval(Nil(), Var("a")) //│ Numb | Var Test2.eval(Cons(["c", Abs("d", Var("d"))], Nil()), Var("a")) //│ Abs[Var] | Numb | Var Test2.eval(Cons(["a", Numb(1)], Nil()), Var("a")) //│ Numb | Var Test2.eval(Cons(["a", Abs("d", Var("d"))], Nil()), Add(Numb(1), Var("a"))) //│ Abs[Var] | Add[Numb | Var] | Numb | Var module Test3 extends EvalVar, EvalExpr, EvalLambda //│ module Test3 { //│ fun eval: (List[{0: Str, 1: 'a}], 'b) -> (Abs['c] | App['c] | 'c) //│ } //│ where //│ 'a :> 'c //│ <: Object //│ 'c :> 'a | 'd //│ 'd <: Add['b] | Mul['b] | Numb | Var //│ 'b <: Abs['b] | App['b & (Abs['b] | Object & ~#Abs)] | Object & 'd & ~#Abs & ~#App Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil()), Abs("a", Var("a"))) //│ Abs['a] | 'a //│ where //│ 'a :> Abs['a] | App['a] | Numb | Var Test3.eval(Cons(["c", Abs("d", Var("d"))], Nil()), App(Abs("a", Var("a")), Add(Numb(1), Var("c")))) //│ Abs['a] | 'a //│ where //│ 'a :> Abs['a] | Add[Numb | Var] | App['a] | Numb | Var module Test3 extends EvalVar, EvalLambda, EvalExpr //│ module Test3 { //│ fun eval: (List[{0: Str, 1: 'a}], 'a & (Add['b] | Mul['b] | Numb | Var)) -> (Numb | 'c | 'a | 'b) //│ } //│ where //│ 'a :> 'c | 'b //│ <: Object //│ 'b <: Add['b] | Mul['b] | Numb | Var //│ 'c :> Abs['c | 'a] | App['c | 'a] | 'a ================================================ FILE: shared/src/test/diff/nu/PostHocMixinSignature.mls ================================================ :NewDefs mixin Foo { fun foo(x) = if x.a then x else foo(x.b) } //│ mixin Foo() { //│ fun foo: 'a -> 'a //│ } //│ where //│ 'a <: {a: Bool, b: 'a} module ValA { val a = false val b = ValB } module ValB { val a = true fun b = ValA } //│ module ValA { //│ val a: false //│ val b: ValB //│ } //│ module ValB { //│ val a: true //│ fun b: ValA //│ } module Test extends Foo //│ module Test { //│ fun foo: forall 'a. 'a -> 'a //│ } //│ where //│ 'a <: {a: Bool, b: 'a} [Test.foo(ValA), Test.foo(ValB)] //│ [ValA | ValB, ValA | ValB] //│ res //│ = [ ValB { class: [class ValB] }, ValB { class: [class ValB] } ] type V = {a: Bool, b: V} //│ type V = {a: Bool, b: V} module Test extends Foo { fun foo: V -> anything } //│ module Test { //│ fun foo: V -> anything //│ } [Test.foo(ValA), Test.foo(ValB)] //│ [anything, anything] //│ res //│ = [ ValB { class: [class ValB] }, ValB { class: [class ValB] } ] module Test extends Foo { fun foo: V -> V } //│ module Test { //│ fun foo: V -> V //│ } [Test.foo(ValA), Test.foo(ValB)] //│ [V, V] //│ res //│ = [ ValB { class: [class ValB] }, ValB { class: [class ValB] } ] ================================================ FILE: shared/src/test/diff/nu/PrivateMemberOverriding.mls ================================================ :NewDefs class Foo(x: Int) //│ class Foo(x: Int) class Bar() extends Foo(123) { fun x = true } //│ class Bar() extends Foo { //│ fun x: true //│ } Bar().x //│ true //│ res //│ = true if Bar() is Foo(a) then a //│ Int //│ res //│ = 123 class Bar(val x: Bool) extends Foo(123) //│ class Bar(x: Bool) extends Foo Bar(true).x //│ Bool //│ res //│ = true if Bar(true) is Foo(a) then a //│ Int //│ res //│ = 123 class Bar(x: Bool) extends Foo(123) //│ class Bar(x: Bool) extends Foo :e // * Expected Bar(true).x //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.41: Bar(true).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.37: class Bar(x: Bool) extends Foo(123) //│ ╙── ^ //│ error | false | true //│ res //│ = undefined if Bar(true) is Foo(a) then a //│ Int //│ res //│ = 123 ================================================ FILE: shared/src/test/diff/nu/RawUnionTraitSignatures.mls ================================================ :NewDefs trait Foo[A] { fun x: A } //│ trait Foo[A] { //│ fun x: A //│ } trait Base1: Foo //│ trait Base1: #Foo (b: Base1) => b.x //│ (b: Base1) -> ??A //│ res //│ = [Function: res] (b: Base1) => b : Foo //│ (b: Base1) -> #Foo //│ res //│ = [Function: res] :e (b: Base1) => b : Foo['X] //│ ╔══[ERROR] Type error in type ascription //│ ║ l.25: (b: Base1) => b : Foo['X] //│ ║ ^ //│ ╟── type variable `A` leaks out of its scope //│ ║ l.25: (b: Base1) => b : Foo['X] //│ ║ ^^ //│ ╟── back into type variable `A` //│ ║ l.5: trait Foo[A] { fun x: A } //│ ╙── ^ //│ forall 'X. (b: Base1) -> Foo['X] //│ where //│ 'X :> ??A //│ <: ??A0 //│ res //│ = [Function: res] :e 1 : Foo[Int] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.43: 1 : Foo[Int] //│ ║ ^ //│ ╟── integer literal of type `1` is not an instance of type `Foo` //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.43: 1 : Foo[Int] //│ ╙── ^^^^^^^^ //│ Foo[Int] //│ res //│ = 1 trait Base1: Foo { val x: Int } //│ trait Base1: #Foo { //│ val x: Int //│ } (b: Base1) => b.x //│ (b: Base1) -> (Int & ??A) //│ res //│ = [Function: res] trait Base1: Foo[1 | 2] { val x: 0 | 1 } //│ trait Base1: Foo[1 | 2] { //│ val x: 0 | 1 //│ } (b: Base1) => b.x //│ (b: Base1) -> 1 //│ res //│ = [Function: res] trait Base2: Foo['FigureItOut] //│ trait Base2: Foo[in ??FigureItOut out ??FigureItOut0] (b: Base2) => b.x //│ (b: Base2) -> ??FigureItOut //│ res //│ = [Function: res] (b: Base1) => b : Foo //│ (b: Base1) -> #Foo //│ res //│ = [Function: res] // :e (b: Base2) => b : Foo['X] //│ forall 'X. (b: Base2) -> Foo['X] //│ where //│ 'X :> ??FigureItOut //│ <: ??FigureItOut0 //│ res //│ = [Function: res] :e class Impl extends Base2 //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.102: class Impl extends Base2 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#Impl` is not an instance of type `Foo` //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.78: trait Base2: Foo['FigureItOut] //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type `#Impl` does not contain member `Foo#A` //│ ║ l.5: trait Foo[A] { fun x: A } //│ ╙── ^ //│ class Impl extends Base2 { //│ constructor() //│ } :e (x: Impl) => x : Base2 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.118: (x: Impl) => x : Base2 //│ ║ ^ //│ ╟── type `Impl` is not an instance of type `Foo` //│ ║ l.118: (x: Impl) => x : Base2 //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `#Foo` //│ ║ l.118: (x: Impl) => x : Base2 //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.78: trait Base2: Foo['FigureItOut] //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── from type reference: //│ ║ l.118: (x: Impl) => x : Base2 //│ ╙── ^^^^^ //│ (x: Impl) -> Base2 //│ res //│ = [Function: res] :e class Impl() extends Base2, Foo //│ ╔══[ERROR] Type error in type declaration //│ ║ l.139: class Impl() extends Base2, Foo //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.78: trait Base2: Foo['FigureItOut] //│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `Impl` //│ ║ l.139: class Impl() extends Base2, Foo //│ ║ ^^^^ //│ ╟── Declared here: //│ ║ l.5: trait Foo[A] { fun x: A } //│ ╙── ^^^^^^^^ //│ class Impl() extends Base2, Foo { //│ fun x: 'A //│ } :e class Impl() extends Base2, Foo { fun x = 1 } //│ ╔══[ERROR] Type error in type declaration //│ ║ l.157: class Impl() extends Base2, Foo { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.158: fun x = 1 //│ ║ ^^^^^^^^^^^ //│ ║ l.159: } //│ ║ ^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.78: trait Base2: Foo['FigureItOut] //│ ╙── ^^^^^^^^^^^^ //│ class Impl() extends Base2, Foo { //│ fun x: 1 //│ } Impl().x //│ 1 //│ res //│ = 1 :e Impl() : Base2 //│ ╔══[ERROR] Type error in type ascription //│ ║ l.180: Impl() : Base2 //│ ║ ^^^^^^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.158: fun x = 1 //│ ╙── ^ //│ Base2 //│ res //│ = Impl {} :e (Impl() : Base2).x //│ ╔══[ERROR] Type error in type ascription //│ ║ l.192: (Impl() : Base2).x //│ ║ ^^^^^^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.158: fun x = 1 //│ ╙── ^ //│ ??FigureItOut //│ res //│ = 1 :e class Impl2() extends Base2, Foo[Int] { fun x = 1 } //│ ╔══[ERROR] Type error in type declaration //│ ║ l.204: class Impl2() extends Base2, Foo[Int] { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.205: fun x = 1 //│ ║ ^^^^^^^^^^^ //│ ║ l.206: } //│ ║ ^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.204: class Impl2() extends Base2, Foo[Int] { //│ ╙── ^^^ //│ ╔══[ERROR] Type error in type declaration //│ ║ l.204: class Impl2() extends Base2, Foo[Int] { //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.205: fun x = 1 //│ ║ ^^^^^^^^^^^ //│ ║ l.206: } //│ ║ ^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.78: trait Base2: Foo['FigureItOut] //│ ║ ^^^^^^^^^^^^ //│ ╟── into type `Int` //│ ║ l.204: class Impl2() extends Base2, Foo[Int] { //│ ╙── ^^^ //│ class Impl2() extends Base2, Foo { //│ fun x: 1 //│ } :e (Impl2() : Base2).x //│ ╔══[ERROR] Type error in type ascription //│ ║ l.235: (Impl2() : Base2).x //│ ║ ^^^^^^^ //│ ╟── type variable `'FigureItOut` leaks out of its scope //│ ║ l.204: class Impl2() extends Base2, Foo[Int] { //│ ╙── ^^^ //│ ??FigureItOut //│ res //│ = 1 trait Test1[A] { fun x: A } trait Test2[A]: Test1[[A, A]] //│ trait Test1[A] { //│ fun x: A //│ } //│ trait Test2[A]: Test1[[A, A]] (t: Test2[Int]) => t.x //│ (t: Test2[Int]) -> [Int, Int] //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/nu/Ref.mls ================================================ :NewDefs :DontDistributeForalls // * We should be able to define our own reference type which behaves the same as plain mutable variables, as in: mut let r = 42 set r = "abc" r //│ mut let r: "abc" | 42 //│ "abc" | 42 //│ r //│ = 42 //│ res //│ = undefined //│ res //│ = 'abc' :e r + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.23: r + 1 //│ ║ ^^^^^ //│ ╟── string literal of type `"abc"` is not an instance of type `Int` //│ ║ l.11: set r = "abc" //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.23: r + 1 //│ ╙── ^ //│ Int | error //│ res //│ = 'abc1' // * The definition: class Ref[A](init: A) { mut val value: A = init } //│ class Ref[A](init: A) { //│ mut val value: A //│ } let r = Ref(42) //│ let r: Ref['A] //│ where //│ 'A :> 42 //│ r //│ = Ref {} set r.value = "abc" //│ () //│ res //│ = undefined r.value //│ "abc" | 42 //│ res //│ = 'abc' :e r.value + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.63: r.value + 1 //│ ║ ^^^^^^^^^^^ //│ ╟── string literal of type `"abc"` is not an instance of type `Int` //│ ║ l.52: set r.value = "abc" //│ ║ ^^^^^ //│ ╟── but it flows into field selection with expected type `Int` //│ ║ l.63: r.value + 1 //│ ╙── ^^^^^^^ //│ Int | error //│ res //│ = 'abc1' // * Below are some variations on Ref, for testing purposes. class Ref() { mut val value = 0 } //│ class Ref() { //│ mut val value: 0 //│ } let r = Ref() //│ let r: Ref //│ r //│ = Ref {} :e set r.value = "abc" //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.94: set r.value = "abc" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── string literal of type `"abc"` does not match type `0` //│ ║ l.94: set r.value = "abc" //│ ║ ^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.82: class Ref() { mut val value = 0 } //│ ║ ^ //│ ╟── from assigned selection: //│ ║ l.94: set r.value = "abc" //│ ╙── ^^^^^^^ //│ () //│ res //│ = undefined r.value //│ 0 //│ res //│ = 'abc' class Ref() { mut val value = 0; fun foo = set value = 42 } //│ class Ref() { //│ fun foo: () //│ mut val value: 0 | 42 //│ } set r.value = 0 //│ () //│ res //│ = undefined set r.value = 42 //│ () //│ res //│ = undefined :e set r.value = 666 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.133: set r.value = 666 //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `666` does not match type `0 | 42` //│ ║ l.133: set r.value = 666 //│ ║ ^^^ //│ ╟── Note: constraint arises from definition of value value: //│ ║ l.116: class Ref() { mut val value = 0; fun foo = set value = 42 } //│ ║ ^^^^^^^^^ //│ ╟── from assigned selection: //│ ║ l.133: set r.value = 666 //│ ╙── ^^^^^^^ //│ () //│ res //│ = undefined :e // * TODO better error... class Ref() { mut val value = 0; constructor() { set value = 42 } } //│ ╔══[ERROR] Illegal assignment //│ ║ l.152: class Ref() { mut val value = 0; constructor() { set value = 42 } } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── cannot assign to reference //│ ║ l.152: class Ref() { mut val value = 0; constructor() { set value = 42 } } //│ ╙── ^^^^^ //│ class Ref() { //│ constructor() //│ mut val value: 0 //│ } set r.value = 0 //│ () //│ res //│ = undefined :e set r.value = 42 //│ ╔══[ERROR] Type mismatch in assignment: //│ ║ l.170: set r.value = 42 //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── integer literal of type `42` does not match type `0` //│ ║ l.170: set r.value = 42 //│ ║ ^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.152: class Ref() { mut val value = 0; constructor() { set value = 42 } } //│ ║ ^ //│ ╟── from assigned selection: //│ ║ l.170: set r.value = 42 //│ ╙── ^^^^^^^ //│ () //│ res //│ = undefined class Ref() { mut val value = 0; fun foo(x) = set value = x } //│ class Ref() { //│ fun foo: 0 -> () //│ mut val value: 0 //│ } class Ref[A](init: A) { mut val value = (); fun foo(x) = set value = x } //│ class Ref[A](init: A) { //│ fun foo: () -> () //│ mut val value: () //│ } class Ref[A](init: A) { mut val value = (); fun foo(x: A) = set value = x } //│ class Ref[A](init: A) { //│ fun foo: (x: A) -> () //│ mut val value: () | A //│ } class Ref[A](init: A) { mut val value = init; fun foo(x) = set value = x } //│ class Ref[A](init: A) { //│ fun foo: A -> () //│ mut val value: A //│ } abstract class B[A]() { fun value: A; fun get = this.value } class Ref() extends B { mut val value = 0; fun foo = set value = 42 } //│ abstract class B[A]() { //│ fun get: A //│ fun value: A //│ } //│ class Ref() extends B { //│ fun foo: () //│ fun get: 'A //│ mut val value: 0 | 42 //│ } //│ where //│ 'A :> 0 | 42 let r = Ref() //│ let r: Ref //│ r //│ = Ref {} r : B['a] //│ B['a] //│ where //│ 'a :> 0 | 42 //│ res //│ = Ref {} r.value //│ 0 | 42 //│ res //│ = 0 set r.value = 42 //│ () //│ res //│ = undefined r.get //│ 0 | 42 //│ res //│ = 42 abstract class B[A]() { mut val value: A; fun get = this.value } //│ abstract class B[A]() { //│ fun get: A //│ mut val value: A //│ } class Ref extends B { mut val value = 123 } //│ class Ref extends B { //│ constructor() //│ fun get: 'A //│ mut val value: 123 //│ } //│ where //│ 'A :> 123 let r = new Ref //│ let r: Ref //│ r //│ = Ref {} r : B['a] //│ B['a] //│ where //│ 'a :> 123 //│ res //│ = Ref {} r.value //│ 123 //│ res //│ = 123 set r.value = 123 //│ () //│ res //│ = undefined r.get //│ 123 //│ res //│ = 123 fun g(b: B['a]) = b.get //│ fun g: forall 'a. (b: B['a]) -> 'a // :e // FIXME this should not work... class Ref extends B { mut val value = 123; fun c = g(this) + 1; fun d = set value = "oops" } //│ class Ref extends B { //│ constructor() //│ fun c: Int //│ fun d: () //│ fun get: 'A //│ mut val value: "oops" | 123 //│ } //│ where //│ 'A :> "oops" | 123 let r = new Ref //│ let r: Ref //│ r //│ = Ref {} r.c //│ Int //│ res //│ = 124 r.d //│ () //│ res //│ = undefined r.value //│ "oops" | 123 //│ res //│ = 'oops' // * FIXME Runtime type confusion! r.c //│ Int //│ res //│ = 'oops1' :e class Ref() extends B //│ ╔══[ERROR] Member `value` is declared (or its declaration is inherited) but is not implemented in `Ref` //│ ║ l.341: class Ref() extends B //│ ║ ^^^ //│ ╟── Declared here: //│ ║ l.255: abstract class B[A]() { mut val value: A; fun get = this.value } //│ ╙── ^^^^^^^^^^^^ //│ class Ref() extends B { //│ fun get: 'A //│ mut val value: 'A //│ } :e class Ref extends B { mut val value = 123; fun c = this.get + 1 } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.354: class Ref extends B { mut val value = 123; fun c = this.get + 1 } //│ ╙── ^^^^ //│ class Ref extends B { //│ constructor() //│ fun c: Int //│ fun get: 'A //│ mut val value: 123 //│ } //│ where //│ 'A :> 123 :e abstract class R() { mut val value: _ } //│ ╔══[ERROR] type identifier not found: _ //│ ║ l.369: abstract class R() { mut val value: _ } //│ ╙── ^ //│ abstract class R() { //│ mut val value: error //│ } abstract class R() { mut val value: Int } //│ abstract class R() { //│ mut val value: Int //│ } abstract class R() { mut val value: 'a } //│ abstract class R() { //│ mut val value: nothing //│ } :e trait Foo { mut val v = 0 } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.389: trait Foo { mut val v = 0 } //│ ╙── ^^^^^^^^^ //│ trait Foo { //│ mut val v: 0 //│ } ================================================ FILE: shared/src/test/diff/nu/RefinedPattern.mls ================================================ :NewDefs class C //│ class C { //│ constructor() //│ } :e fun test(x) = if x is C then x.a //│ ╔══[ERROR] Type `C` does not contain member `a` //│ ║ l.12: C then x.a //│ ╙── ^^ //│ fun test: C -> error fun test(x) = if x is refined(C) then x.a //│ fun test: forall 'a. (C & {a: 'a}) -> 'a class D(val a: Int) extends C //│ class D(a: Int) extends C test(D(123)) //│ Int //│ res //│ = 123 :e refined //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.32: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined //│ ║ l.32: refined //│ ╙── ^^^^^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol refined ================================================ FILE: shared/src/test/diff/nu/Refinements.mls ================================================ :NewDefs class D() { fun f = 0 } //│ class D() { //│ fun f: 0 //│ } D() : D & Object //│ D //│ res //│ = D {} let d = D() : D & { f: 0 } //│ let d: D & {f: 0} //│ d //│ = D {} let c = d with { f: 1 } //│ let c: D\f & {f: 1} //│ c //│ = D { f: 1 } c : D & { f: 1 } //│ D & {f: 1} //│ res //│ = D { f: 1 } class C[A](val a: A) extends D() //│ class C[A](a: A) extends D { //│ fun f: 0 //│ } C(1) //│ C['A] //│ where //│ 'A :> 1 //│ res //│ = C {} let c: C & {a: 1, f: 0} = C(1) //│ let c: C[anything] & {a: 1, f: 0} //│ c //│ = C {} c.f //│ 0 //│ res //│ = 0 let r = {} //│ let r: anything //│ r //│ = {} // * Not all records are objects! // * Objects are class instances. // * But other values, like functions, can also hold record fields... // * This is actually important in JS, where some library functions act like modules! :e r : Object //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.63: r : Object //│ ║ ^ //│ ╟── definition of let binding r of type `anything` is not an instance of type `Object` //│ ║ l.53: let r = {} //│ ║ ^^^^^^ //│ ╟── but it flows into reference with expected type `Object` //│ ║ l.63: r : Object //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.63: r : Object //│ ╙── ^^^^^^ //│ Object //│ res //│ = {} r with { x: 1 } //│ {x: 1} //│ res //│ = { x: 1 } :e // TODO support let r = new {} //│ ╔══[ERROR] Unexpected type `anything` after `new` keyword //│ ║ l.87: let r = new {} //│ ╙── ^^ //│ let r: error //│ Code generation encountered an error: //│ Unsupported `new` class term: Bra(true,Rcd(List())) :ge r : Object //│ Object //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding r :ge r with { x: 1 } //│ error & {x: 1} //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding r let r = { x: 0 } //│ let r: {x: 0} //│ r //│ = { x: 0 } :e r : Object //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.114: r : Object //│ ║ ^ //│ ╟── record literal of type `{x: 0}` is not an instance of type `Object` //│ ║ l.108: let r = { x: 0 } //│ ║ ^ //│ ╟── but it flows into reference with expected type `Object` //│ ║ l.114: r : Object //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.114: r : Object //│ ╙── ^^^^^^ //│ Object //│ res //│ = { x: 0 } r with { x: 1 } //│ {x: 1} //│ res //│ = { x: 1 } :NoJS fun o : {} //│ fun o: anything o : {} //│ anything :e o : Object //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.148: o : Object //│ ║ ^ //│ ╟── type `anything` is not an instance of type `Object` //│ ║ l.141: fun o : {} //│ ║ ^^ //│ ╟── but it flows into reference with expected type `Object` //│ ║ l.148: o : Object //│ ║ ^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.148: o : Object //│ ╙── ^^^^^^ //│ Object fun o : Object & {} //│ fun o: Object o : {} //│ anything o : Object //│ Object // === // :e let d = D & { f: 0 } //│ ╔══[ERROR] Illegal use of reserved operator: & //│ ║ l.179: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] identifier not found: & //│ ║ l.179: let d = D & { f: 0 } //│ ╙── ^ //│ let d: error ================================================ FILE: shared/src/test/diff/nu/Res.mls ================================================ :NewDefs x => x + 2 //│ Int -> Int //│ res //│ = [Function: res] :e res(1) //│ ╔══[ERROR] identifier not found: res //│ ║ l.10: res(1) //│ ╙── ^^^ //│ error //│ res //│ = 3 let res = x => x + 2 //│ let res: Int -> Int //│ res //│ = [Function: res1] res(1) //│ Int //│ res //│ = 3 // FIXME `res` not accounted in type context :re res(1) //│ Int //│ res //│ Runtime error: //│ TypeError: res is not a function ================================================ FILE: shared/src/test/diff/nu/RightAssocOps.mls ================================================ :NewDefs // * Operators that end with `:` are right-associative fun (+:) pre(x: Int, xs) = [[x], xs] fun (:+) post(xs, x: Int) = [xs, [x]] fun (++) conc(xs, ys) = [xs, ys] //│ fun (+:) pre: forall 'a. (x: Int, 'a) -> [[Int], 'a] //│ fun (:+) post: forall 'b. ('b, x: Int) -> ['b, [Int]] //│ fun (++) conc: forall 'c 'd. ('c, 'd) -> ['c, 'd] 1 +: 2 +: 3 +: [] //│ [[Int], [[Int], [[Int], []]]] //│ res //│ = [ [ 1 ], [ [ 2 ], [ [Array], [] ] ] ] [] :+ 1 :+ 2 :+ 3 //│ [[[[], [Int]], [Int]], [Int]] //│ res //│ = [ [ [ [], [Array] ], [ 2 ] ], [ 3 ] ] [1, 2, 3] ++ [4, 5, 6] //│ [[1, 2, 3], [4, 5, 6]] //│ res //│ = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] :p 1 +: "a" ++ "b" :+ 2 //│ |1| |+:| |"a"| |++| |"b"| |:+| |2| //│ AST: TypingUnit(List(App(Var(+:),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,App(Var(:+),Tup(List((None,Fld(_,App(Var(++),Tup(List((None,Fld(_,StrLit(a))), (None,Fld(_,StrLit(b)))))))), (None,Fld(_,IntLit(2))))))))))))) //│ Parsed: +:(1, :+(++("a", "b",), 2,),); //│ [[Int], [["a", "b"], [Int]]] //│ res //│ = [ [ 1 ], [ [ 'a', 'b' ], [ 2 ] ] ] :p 1 +: "a" :+ 2 ++ "b" //│ |1| |+:| |"a"| |:+| |2| |++| |"b"| //│ AST: TypingUnit(List(App(Var(+:),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,App(Var(++),Tup(List((None,Fld(_,App(Var(:+),Tup(List((None,Fld(_,StrLit(a))), (None,Fld(_,IntLit(2)))))))), (None,Fld(_,StrLit(b))))))))))))) //│ Parsed: +:(1, ++(:+("a", 2,), "b",),); //│ [[Int], [["a", [Int]], "b"]] //│ res //│ = [ [ 1 ], [ [ 'a', [Array] ], 'b' ] ] :p :e 1 +: "a" ++ 2 +: "b" //│ |1| |+:| |"a"| |++| |2| |+:| |"b"| //│ AST: TypingUnit(List(App(Var(+:),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,App(Var(+:),Tup(List((None,Fld(_,App(Var(++),Tup(List((None,Fld(_,StrLit(a))), (None,Fld(_,IntLit(2)))))))), (None,Fld(_,StrLit(b))))))))))))) //│ Parsed: +:(1, +:(++("a", 2,), "b",),); //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.50: 1 +: "a" ++ 2 +: "b" //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[?a, ?b]` is not an instance of type `Int` //│ ║ l.9: fun (++) conc(xs, ys) = [xs, ys] //│ ║ ^^^^^^^^ //│ ╟── but it flows into operator application with expected type `Int` //│ ║ l.50: 1 +: "a" ++ 2 +: "b" //│ ║ ^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.7: fun (+:) pre(x: Int, xs) = [[x], xs] //│ ╙── ^^^ //│ [[Int], error | [[Int], "b"]] //│ res //│ = [ [ 1 ], [ [ [Array] ], 'b' ] ] 1 +: "a" ++ (2 +: "b") //│ [[Int], ["a", [[Int], "b"]]] //│ res //│ = [ [ 1 ], [ 'a', [ [Array], 'b' ] ] ] ================================================ FILE: shared/src/test/diff/nu/RigidVariables.mls ================================================ :NewDefs // * Flexible fun f(x: 'test) = [x.b, x] //│ fun f: forall 'b 'test. (x: {b: 'b} & 'test) -> ['b, 'test] // * Rigid :e fun f[A](x: A) = x.b //│ ╔══[ERROR] Type `A` does not contain member `b` //│ ║ l.13: fun f[A](x: A) = x.b //│ ╙── ^^ //│ fun f: (x: anything) -> error fun f[A](x: A & { b: ' }) = x.b //│ fun f: forall 'b. (x: {b: 'b}) -> 'b :e module Foo { fun f[A](x: A) = x.b } //│ ╔══[ERROR] Type `A` does not contain member `b` //│ ║ l.24: fun f[A](x: A) = x.b //│ ╙── ^^ //│ module Foo { //│ fun f: (x: anything) -> error //│ } :e class Foo[A](x: A) { fun f = x.b } //│ ╔══[ERROR] Type `A` does not contain member `b` //│ ║ l.35: fun f = x.b //│ ╙── ^^ //│ class Foo[A](x: A) { //│ fun f: error //│ } ================================================ FILE: shared/src/test/diff/nu/SelfAppMethods.mls ================================================ :NewDefs class A { fun f = f(f) fun g : A fun g = g(g) // * FIXME not using the signature } //│ class A { //│ constructor() //│ fun f: nothing //│ fun g: A //│ } :e module A { fun i(x) = x fun f = f(f) fun g(x) = x(x) fun h = g(g) } //│ ╔══[ERROR] Cyclic-looking constraint while typing application; a type annotation may be required //│ ║ l.23: fun h = g(g) //│ ║ ^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ module A { //│ fun f: nothing //│ fun g: forall 'a 'b. ('a -> 'b & 'a) -> 'b //│ fun h: error //│ fun i: forall 'c. 'c -> 'c //│ } // * Note this permutation works: currently we generalize check functions in the source code order... module A { fun i(x) = x fun f = f(f) fun h = g(g) fun g(x) = x(x) } //│ module A { //│ fun f: nothing //│ fun g: forall 'a 'b. 'b -> 'a //│ fun h: forall 'a. 'a //│ fun i: forall 'c. 'c -> 'c //│ } //│ where //│ 'b := 'b -> 'a :ns A.i //│ 'i //│ where //│ 'i :> forall 'a. 'a -> 'a //│ res //│ = [Function: i] :re :ns A.f //│ 'f //│ where //│ 'f :> forall 'f0 'a. 'a //│ 'a <: 'f0 //│ 'f0 <: 'f0 -> 'a //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded :ns A.g //│ 'g //│ where //│ 'g :> forall 'a 'b 'c 'h. 'a -> 'b //│ 'a := 'a -> 'b //│ 'b <: 'c //│ 'c <: 'h //│ res //│ = [Function: g] :ns :re A.h //│ 'h //│ where //│ 'h :> forall 'a 'h0. 'a //│ 'a <: 'h0 //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/nu/SelfRec.mls ================================================ :NewDefs class Foo1(val x: Int) { fun test = Foo1(1).x } //│ class Foo1(x: Int) { //│ fun test: Int //│ } :e class Foo1X(x: 0 | 1) extends Foo1(x) { fun test2 = Foo1X(1).x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.14: fun test2 = Foo1X(1).x //│ ╙── ^^ //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.13: class Foo1X(x: 0 | 1) extends Foo1(x) { //│ ║ ^ //│ ╟── Originally declared here: //│ ║ l.5: class Foo1(val x: Int) { //│ ╙── ^ //│ class Foo1X(x: 0 | 1) extends Foo1 { //│ fun test: Int //│ fun test2: error //│ } class Foo2[A](val x: A) { fun test = Foo2(1).x } //│ class Foo2[A](x: A) { //│ fun test: 1 //│ } :e class Foo2X(x: 0 | 1) extends Foo2(x) { fun test2 = Foo2X(1).x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.40: fun test2 = Foo2X(1).x //│ ╙── ^^ //│ ╔══[ERROR] Inherited parameter named 'x' is not virtual and cannot be overridden //│ ║ l.39: class Foo2X(x: 0 | 1) extends Foo2(x) { //│ ║ ^ //│ ╟── Originally declared here: //│ ║ l.31: class Foo2[A](val x: A) { //│ ╙── ^ //│ class Foo2X(x: 0 | 1) extends Foo2 { //│ fun test: 1 //│ fun test2: error //│ } :e Foo2X(1).x //│ ╔══[ERROR] Parameter 'x' cannot be accessed as a field //│ ║ l.57: Foo2X(1).x //│ ║ ^^ //│ ╟── Either make the parameter a `val` or access it through destructuring //│ ║ l.39: class Foo2X(x: 0 | 1) extends Foo2(x) { //│ ╙── ^ //│ 0 | 1 | error //│ res //│ = 1 :e // TODO improve type checking class Foo2X(a: 0 | 1) extends Foo2(a) { fun test2 = Foo2X(1).x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.70: fun test2 = Foo2X(1).x //│ ╙── ^^ //│ class Foo2X(a: 0 | 1) extends Foo2 { //│ fun test: 1 //│ fun test2: error //│ } Foo2X(1).x //│ 0 | 1 //│ res //│ = 1 class Foo3[A](val x: A) { fun test = Foo3(1) fun foo = Foo3 } //│ class Foo3[A](x: A) { //│ fun foo: (x: A) -> Foo3[A] //│ fun test: forall 'A. Foo3['A] //│ } //│ where //│ 'A :> 1 Foo3 //│ forall 'A. (x: 'A) -> Foo3['A] //│ res //│ = [Function (anonymous)] { //│ class: [class Foo3], //│ unapply: [Function: unapply] //│ } Foo3(1) //│ Foo3['A] //│ where //│ 'A :> 1 //│ res //│ = Foo3 {} Foo3(1).x //│ 1 //│ res //│ = 1 Foo3(1).foo //│ forall 'A. (x: 'A) -> Foo3['A] //│ res //│ = [Function (anonymous)] { //│ class: [class Foo3], //│ unapply: [Function: unapply] //│ } :e class Foo4 { fun test = [Foo4.test] } //│ ╔══[ERROR] Class Foo4 cannot be instantiated as it exposes no constructor //│ ║ l.127: fun test = [Foo4.test] //│ ╙── ^^^^ //│ class Foo4 { //│ constructor() //│ fun test: [error] //│ } :e class Foo5(x: Int) { fun test = [Foo5(5).test] } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.139: fun test = [Foo5(5).test] //│ ╙── ^^^^^ //│ class Foo5(x: Int) { //│ fun test: [error] //│ } :e class Foo6[A](x: A) { fun test1 = [Foo6(x).test1] fun test2 = [Foo6(123).test2] fun test3 = [Foo6([x]).test3] } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.150: fun test1 = [Foo6(x).test1] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.151: fun test2 = [Foo6(123).test2] //│ ╙── ^^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.152: fun test3 = [Foo6([x]).test3] //│ ╙── ^^^^^^ //│ class Foo6[A](x: A) { //│ fun test1: [error] //│ fun test2: [error] //│ fun test3: [error] //│ } module N //│ module N :e class Foo7[A](head: A, tail: Foo7[A] | N) { fun test1 = if tail is N then head _ then tail.test1 } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.176: _ then tail.test1 //│ ╙── ^^^^^^ //│ class Foo7[A](head: A, tail: Foo7[A] | N) { //│ fun test1: error | A //│ } class Foo7_A[A](head: A, tail: Foo7_A[A] | N) { fun test1: A fun test1 = if tail is N then head _ then tail.test1 } //│ class Foo7_A[A](head: A, tail: Foo7_A[A] | N) { //│ fun test1: A //│ } class Foo7_A2[A](head: A, tail: Foo7_A[A] | N) { fun test1: A = if tail is N then head _ then tail.test1 } //│ class Foo7_A2[A](head: A, tail: Foo7_A[A] | N) { //│ fun test1: A //│ } class Foo7_2_A[A](head: A, tail: Foo7_A[Int] | N) { fun test1: Int | A = if tail is N then head _ then tail.test1 } //│ class Foo7_2_A[A](head: A, tail: Foo7_A[Int] | N) { //│ fun test1: Int | A //│ } :e class Foo8[A](x: A) { fun test1[B](y: B): A = let tmp = Foo8(y).test1(x) x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.216: let tmp = Foo8(y).test1(x) //│ ╙── ^^^^^^ //│ class Foo8[A](x: A) { //│ fun test1: (y: anything) -> A //│ } :e // * FIXME this is caused by the self-annotation... abstract class List(val length: Int): Cons class Cons(tail: List) extends List(tail.length + 1) //│ ╔══[ERROR] Unhandled cyclic parent specification //│ ║ l.231: class Cons(tail: List) extends List(tail.length + 1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.231: class Cons(tail: List) extends List(tail.length + 1) //│ ╙── ^^^^^^^ //│ abstract class List(length: Int): Cons //│ class Cons(tail: List) extends List // * Note: full (non-minimized) definitions: :e // * FIXME abstract class List[out A](val length: Int): Cons[A] | Nil class Cons[out A](val head: A, val tail: List[A]) extends List[A](tail.length + 1) module Nil extends List[nothing](0) //│ ╔══[ERROR] Unhandled cyclic parent specification //│ ║ l.245: class Cons[out A](val head: A, val tail: List[A]) extends List[A](tail.length + 1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.245: class Cons[out A](val head: A, val tail: List[A]) extends List[A](tail.length + 1) //│ ╙── ^^^^^^^ //│ abstract class List[A](length: Int): Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) extends List //│ module Nil extends List ================================================ FILE: shared/src/test/diff/nu/Sidney.mls ================================================ :NewDefs fun swapMaybe(x, y) = if true then [x, y] else [y, x] //│ fun swapMaybe: forall 'a. ('a, 'a) -> ['a, 'a] swapMaybe : forall 'a, 'b: ('a, 'b) -> ['a | 'b, 'b | 'a] //│ forall 'a. ('a, 'a) -> ['a, 'a] //│ res //│ = [Function: swapMaybe] fun test(x, y, z) = let xy = swapMaybe(x, y) let yz = swapMaybe(xy.1, z) [xy.0, yz.0, yz.1] //│ fun test: forall 'a 'b. ('a, 'a, 'b) -> ['a, 'a | 'b, 'b | 'a] :e test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] //│ ║ ^^^^ //│ ╟── type `'a` does not match type `'b` //│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] //│ ║ ^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.21: test : forall 'a, 'b: ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] //│ ╙── ^^ //│ forall 'a 'b. ('a, 'b, 'b) -> ['a | 'b, 'a | 'b, 'b] //│ res //│ = [Function: test] fun test(x, y, z) = if true then let xy = swapMaybe(x, y) [xy.0, xy.1, z] else let yz = swapMaybe(y, z) [x, yz.0, yz.1] //│ fun test: forall 'a 'b. ('a, 'a & 'b, 'b) -> ['a, 'a | 'b, 'b] test : forall 'a: ('a, 'a, 'a) -> ['a, 'a, 'a] //│ forall 'a. ('a, 'a, 'a) -> ['a, 'a, 'a] //│ res //│ = [Function: test1] ================================================ FILE: shared/src/test/diff/nu/SimpleSymbolicOps.mls ================================================ :NewDefs fun (-) i(x, y) = x //│ fun (-) i: forall 'a. ('a, anything) -> 'a 1 - 1 //│ 1 //│ res //│ = 1 fun (-) i(x, y) = x [i(1,1), i(2,1), 1 - 1, 2 - 1] //│ fun (-) i: forall 'a. ('a, anything) -> 'a //│ [1, 2, 1, 2] //│ res //│ = [ 1, 2, 1, 2 ] ================================================ FILE: shared/src/test/diff/nu/SimpleTraitImpl.mls ================================================ :NewDefs :NoJS // TODO enable JS trait T1 { fun x: 0 | 1 } trait T2 { fun x: 1 | 2 } //│ trait T1 { //│ fun x: 0 | 1 //│ } //│ trait T2 { //│ fun x: 1 | 2 //│ } abstract class C1 { fun x: 0 | 2 } //│ abstract class C1 { //│ fun x: 0 | 2 //│ } :e module M extends C1, T1 { fun x = 2 } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.23: fun x = 2 //│ ║ ^^^^^ //│ ╟── integer literal of type `2` does not match type `0 | 1` //│ ║ l.23: fun x = 2 //│ ║ ^ //│ ╟── but it flows into definition of method x with expected type `0 | 1` //│ ║ l.23: fun x = 2 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ║ ^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^ //│ module M extends C1, T1 { //│ fun x: 2 //│ } abstract class C2 extends C1, T1 //│ abstract class C2 extends C1, T1 { //│ fun x: 0 //│ } :e module M extends C2 { fun x = 2 } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.51: fun x = 2 //│ ║ ^^^^^ //│ ╟── integer literal of type `2` does not match type `0 | 1` //│ ║ l.51: fun x = 2 //│ ║ ^ //│ ╟── but it flows into definition of method x with expected type `0 | 1` //│ ║ l.51: fun x = 2 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ║ ^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^ //│ module M extends C1, C2, T1 { //│ fun x: 2 //│ } class C1 { virtual fun x: 0 | 2 = 0 } //│ class C1 { //│ constructor() //│ fun x: 0 | 2 //│ } :e module M extends C1, T1 //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^^^^^^^^^^^^ //│ ╟── type `2` does not match type `0 | 1` //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^ //│ ╟── but it flows into union type with expected type `0 | 1` //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ║ ^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^ //│ module M extends C1, T1 { //│ fun x: 0 | 2 //│ } :e module M extends T1, C1 //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^^^^^^^^^^^^ //│ ╟── type `2` does not match type `0 | 1` //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^ //│ ╟── but it flows into union type with expected type `0 | 1` //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ║ ^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^ //│ module M extends C1, T1 { //│ fun x: 0 | 2 //│ } :e module M extends T1, T2, C1 { fun x = this.x } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.124: fun x = this.x //│ ╙── ^^ //│ module M extends C1, T1, T2 { //│ fun x: error //│ } :e module M extends T1, T2, C1 { fun x: 0 fun x = this.x } //│ ╔══[ERROR] Type mismatch in signature of member `x`: //│ ║ l.135: fun x: 0 //│ ║ ^^^^ //│ ╟── type `0` does not match type `1 | 2` //│ ║ l.135: fun x: 0 //│ ║ ^ //│ ╟── but it flows into signature of member `x` with expected type `1 | 2` //│ ║ l.135: fun x: 0 //│ ║ ^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.8: trait T2 { fun x: 1 | 2 } //│ ║ ^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.8: trait T2 { fun x: 1 | 2 } //│ ╙── ^^^^^^^^ //│ module M extends C1, T1, T2 { //│ fun x: 0 //│ } class C extends C1, T2 { virtual fun x: 2 fun x = this.x } //│ class C extends C1, T2 { //│ constructor() //│ fun x: 2 //│ } module M extends C { fun x = 2 } M.x //│ module M extends C, C1, T2 { //│ fun x: 2 //│ } //│ 2 :e class C2 extends T1 //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C2` //│ ║ l.177: class C2 extends T1 //│ ║ ^^ //│ ╟── Declared here: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^^^^^ //│ class C2 extends T1 { //│ constructor() //│ fun x: 0 | 1 //│ } abstract class C2 extends T1 //│ abstract class C2 extends T1 { //│ fun x: 0 | 1 //│ } :e class C3 extends C2 //│ ╔══[ERROR] Member `x` is declared (or its declaration is inherited) but is not implemented in `C3` //│ ║ l.195: class C3 extends C2 //│ ║ ^^ //│ ╟── Declared here: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^^^^^ //│ class C3 extends C2, T1 { //│ constructor() //│ fun x: 0 | 1 //│ } abstract class C3 extends C2 //│ abstract class C3 extends C2, T1 { //│ fun x: 0 | 1 //│ } class C2 extends T1 { fun x = 1 } //│ class C2 extends T1 { //│ constructor() //│ fun x: 1 //│ } :e class C2 extends T1, T2 { fun x = 2 } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.219: class C2 extends T1, T2 { fun x = 2 } //│ ║ ^^^^^ //│ ╟── integer literal of type `2` does not match type `0 | 1` //│ ║ l.219: class C2 extends T1, T2 { fun x = 2 } //│ ║ ^ //│ ╟── but it flows into definition of method x with expected type `0 | 1` //│ ║ l.219: class C2 extends T1, T2 { fun x = 2 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ║ ^^^^^ //│ ╟── from signature of member `x`: //│ ║ l.7: trait T1 { fun x: 0 | 1 } //│ ╙── ^^^^^^^^ //│ class C2 extends T1, T2 { //│ constructor() //│ fun x: 2 //│ } class C2 extends T1, T2 { virtual fun x = 1 } //│ class C2 extends T1, T2 { //│ constructor() //│ fun x: 1 //│ } :e class C3 extends C2 { virtual fun x = 111 } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.247: class C3 extends C2 { virtual fun x = 111 } //│ ║ ^^^^^^^ //│ ╟── integer literal of type `111` does not match type `1` //│ ║ l.247: class C3 extends C2 { virtual fun x = 111 } //│ ║ ^^^ //│ ╟── but it flows into definition of method x with expected type `1` //│ ║ l.247: class C3 extends C2 { virtual fun x = 111 } //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from integer literal: //│ ║ l.240: class C2 extends T1, T2 { virtual fun x = 1 } //│ ║ ^ //│ ╟── from definition of method x: //│ ║ l.240: class C2 extends T1, T2 { virtual fun x = 1 } //│ ╙── ^^^^^ //│ class C3 extends C2, T1, T2 { //│ constructor() //│ fun x: 111 //│ } class C3 extends C2 { fun x = 1 } //│ class C3 extends C2, T1, T2 { //│ constructor() //│ fun x: 1 //│ } class C2 extends C1, T1 { fun x = 0 } //│ class C2 extends C1, T1 { //│ constructor() //│ fun x: 0 //│ } class C2 extends T1, C1 { fun x = 0 } //│ class C2 extends C1, T1 { //│ constructor() //│ fun x: 0 //│ } :e class C2 extends C1, T1 { fun x = 1 } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.287: class C2 extends C1, T1 { fun x = 1 } //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `0 | 2` //│ ║ l.287: class C2 extends C1, T1 { fun x = 1 } //│ ║ ^ //│ ╟── but it flows into definition of method x with expected type `0 | 2` //│ ║ l.287: class C2 extends C1, T1 { fun x = 1 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^^^^^ //│ ╟── from definition of method x: //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ╙── ^^^^^^^^^^^^ //│ class C2 extends C1, T1 { //│ constructor() //│ fun x: 1 //│ } :e class C2 extends T1, C1 { fun x = 1 } //│ ╔══[ERROR] Type mismatch in definition of method x: //│ ║ l.309: class C2 extends T1, C1 { fun x = 1 } //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `0 | 2` //│ ║ l.309: class C2 extends T1, C1 { fun x = 1 } //│ ║ ^ //│ ╟── but it flows into definition of method x with expected type `0 | 2` //│ ║ l.309: class C2 extends T1, C1 { fun x = 1 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from union type: //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ║ ^^^^^ //│ ╟── from definition of method x: //│ ║ l.72: class C1 { virtual fun x: 0 | 2 = 0 } //│ ╙── ^^^^^^^^^^^^ //│ class C2 extends C1, T1 { //│ constructor() //│ fun x: 1 //│ } :e trait T2 { val r = 1(1) } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.333: trait T2 { val r = 1(1) } //│ ╙── ^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.333: trait T2 { val r = 1(1) } //│ ║ ^^^^ //│ ╟── integer literal of type `1` is not a function //│ ║ l.333: trait T2 { val r = 1(1) } //│ ╙── ^ //│ trait T2 { //│ val r: error //│ } class C2 extends T2 //│ class C2 extends T2 { //│ constructor() //│ val r: error //│ } :e trait T3[A] { val r = C2().x } class C2 extends T3[Int] //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.356: val r = C2().x //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] Class C2 cannot be instantiated as it exposes no constructor //│ ║ l.356: val r = C2().x //│ ╙── ^^ //│ trait T3[A] { //│ val r: error //│ } //│ class C2 extends T3 { //│ constructor() //│ val r: error //│ } :e // * Note: lack of hygiene... happens only if class shadows previous C2 and is part of the error-throwing block C2() : T3['X] //│ ╔══[ERROR] Construction of unparameterized class C2 should use the `new` keyword //│ ║ l.374: C2() : T3['X] //│ ╙── ^^ //│ T3[Int] class C3 extends T3[Int] //│ class C3 extends T3 { //│ constructor() //│ val r: error //│ } new C3 : T3['X] //│ T3[Int] trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } //│ trait Foo { //│ fun foo: forall 'A. (x: 'A) -> 'A //│ } class B extends Foo { fun foo = error } //│ class B extends Foo { //│ constructor() //│ fun foo: nothing //│ } class B extends Foo { fun foo(x) = x } //│ class B extends Foo { //│ constructor() //│ fun foo: forall 'a. 'a -> 'a //│ } :e class B extends Foo { fun foo(x) = x + 1 } //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── type `'A` is not an instance of type `Int` //│ ║ l.391: trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } //│ ║ ^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^ //│ ╟── Note: quantified type variable 'A is defined at: //│ ║ l.391: trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in definition of method foo: //│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` does not match type `'A` //│ ║ l.409: class B extends Foo { fun foo(x) = x + 1 } //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type variable: //│ ║ l.391: trait Foo { fun foo: forall 'A: (x: 'A) -> 'A } //│ ╙── ^^ //│ class B extends Foo { //│ constructor() //│ fun foo: Int -> Int //│ } ================================================ FILE: shared/src/test/diff/nu/Splices.mls ================================================ :NewDefs let bs = [1, 2, 3] //│ let bs: [1, 2, 3] //│ bs //│ = [ 1, 2, 3 ] :pe // TODO let t = [0, ...bs] //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.10: let t = [0, ...bs] //│ ╙── ^^^ //│ let t: [0] //│ t //│ = [ 0 ] t.0 //│ 0 //│ res //│ = 0 :e // TODO t.1 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.24: t.1 //│ ║ ^^^ //│ ╟── definition of let binding t of type `{0: 0}` does not have field '1' //│ ║ l.10: let t = [0, ...bs] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{1: ?a}` //│ ║ l.24: t.1 //│ ╙── ^ //│ error //│ res //│ = undefined :e // TODO t.2 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.39: t.2 //│ ║ ^^^ //│ ╟── definition of let binding t of type `{0: 0}` does not have field '2' //│ ║ l.10: let t = [0, ...bs] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{2: ?a}` //│ ║ l.39: t.2 //│ ╙── ^ //│ error //│ res //│ = undefined :e t.3 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.54: t.3 //│ ║ ^^^ //│ ╟── definition of let binding t of type `{0: 0}` does not have field '3' //│ ║ l.10: let t = [0, ...bs] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{3: ?a}` //│ ║ l.54: t.3 //│ ╙── ^ //│ error //│ res //│ = undefined let cs: Array[Int] = bs //│ let cs: Array[Int] //│ cs //│ = [ 1, 2, 3 ] :pe // TODO let t = [0, ...cs] //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.75: let t = [0, ...cs] //│ ╙── ^^^ //│ let t: [0] //│ t //│ = [ 0 ] t.0 //│ 0 //│ res //│ = 0 :e t.1 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.89: t.1 //│ ║ ^^^ //│ ╟── definition of let binding t of type `{0: 0}` does not have field '1' //│ ║ l.75: let t = [0, ...cs] //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{1: ?a}` //│ ║ l.89: t.1 //│ ╙── ^ //│ error //│ res //│ = undefined :pe // TODO fun f(x, y, ...zs) = x //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.105: fun f(x, y, ...zs) = x //│ ╙── ^^^ //│ fun f: forall 'a. ('a, anything) -> 'a f(0, 1) //│ 0 //│ res //│ = 0 :e // TODO f(0, 1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.117: f(0, 1, 2) //│ ║ ^^^^^^^^^^ //│ ╟── argument list of type `[0, 1, 2]` does not match type `[?a, ?b]` //│ ║ l.117: f(0, 1, 2) //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.105: fun f(x, y, ...zs) = x //│ ╙── ^^^^^^^^^^^^^ //│ error //│ res //│ = 0 :e // TODO f(0, 1, 2, 3) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.132: f(0, 1, 2, 3) //│ ║ ^^^^^^^^^^^^^ //│ ╟── argument list of type `[0, 1, 2, 3]` does not match type `[?a, ?b]` //│ ║ l.132: f(0, 1, 2, 3) //│ ║ ^^^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.105: fun f(x, y, ...zs) = x //│ ╙── ^^^^^^^^^^^^^ //│ error //│ res //│ = 0 :e // TODO f(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.147: f(0) //│ ║ ^^^^ //│ ╟── argument of type `[0]` does not match type `[?a, ?b]` //│ ║ l.147: f(0) //│ ║ ^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.105: fun f(x, y, ...zs) = x //│ ╙── ^^^^^^^^^^^^^ //│ error //│ res //│ = 0 :pe // TODO :e // TODO f(...bs) //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.163: f(...bs) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.163: f(...bs) //│ ║ ^^^^^^^^ //│ ╟── argument of type `[]` does not match type `[?a, ?b]` //│ ║ l.163: f(...bs) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.105: fun f(x, y, ...zs) = x //│ ╙── ^^^^^^^^^^^^^ //│ error //│ res //│ = undefined :pe // TODO :e // TODO f(0, ...bs) //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.182: f(0, ...bs) //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.182: f(0, ...bs) //│ ║ ^^^^^^^^^^^ //│ ╟── argument of type `[0]` does not match type `[?a, ?b]` //│ ║ l.182: f(0, ...bs) //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.105: fun f(x, y, ...zs) = x //│ ╙── ^^^^^^^^^^^^^ //│ error //│ res //│ = 0 :pe // TODO f(0, 1, ...bs) //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.200: f(0, 1, ...bs) //│ ╙── ^^^ //│ 0 //│ res //│ = 0 :pe // TODO fun f: 'xs -> [1, 2, ...'xs] //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.210: fun f: 'xs -> [1, 2, ...'xs] //│ ╙── ^^^ //│ fun f: anything -> [1, 2] f([]) //│ [1, 2] //│ res //│ = //│ f is not implemented f([1]) //│ [1, 2] //│ res //│ = //│ f is not implemented f([1, 2]) //│ [1, 2] //│ res //│ = //│ f is not implemented f(bs) //│ [1, 2] //│ res //│ = //│ f is not implemented f(cs) //│ [1, 2] //│ res //│ = //│ f is not implemented :e f() //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.247: f() //│ ║ ^^^ //│ ╟── argument of type `[]` does not match type `['xs]` //│ ║ l.247: f() //│ ║ ^^ //│ ╟── Note: constraint arises from tuple type: //│ ║ l.210: fun f: 'xs -> [1, 2, ...'xs] //│ ╙── ^^^ //│ error | [1, 2] //│ res //│ = //│ f is not implemented // :e // TODO f(0) //│ [1, 2] //│ res //│ = //│ f is not implemented ================================================ FILE: shared/src/test/diff/nu/StupidJS.mls ================================================ :NewDefs module C { fun f() = this } //│ module C { //│ fun f: () -> C //│ } C.f() //│ C //│ res //│ = C { class: [class C] } // * In JS semantics, C.f will return a plain method **without capturing `this`**!! // * This is very dumb. In Python for example, one gets a closure that captures `this`. // * We can't easily work around this without incurring either bad performance overhead // * or JS/TS interoperability woes. // * Therefore, we'll make sure to reflecy the wonky semantics in the typer // * (or maybe just make it fail type checking altogether.) // :e // TODO prevent unapplied method selections in the type system... let r = id(C.f)() //│ let r: C //│ r //│ = undefined :re r.f //│ () -> C //│ res //│ Runtime error: //│ TypeError: Cannot read properties of undefined (reading 'f') ================================================ FILE: shared/src/test/diff/nu/Subscripts.mls ================================================ :NewDefs let xs = [0, 1, 2] //│ let xs: [0, 1, 2] //│ xs //│ = [ 0, 1, 2 ] if xs.[0] is undefined then 0 x then x //│ 0 | 1 | 2 //│ res //│ = 0 :e xs.[0] + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.18: xs.[0] + 1 //│ ║ ^^^^^^^^ //│ ╟── possibly-undefined array access of type `()` is not an instance of type `Int` //│ ║ l.18: xs.[0] + 1 //│ ╙── ^^^^ //│ Int | error //│ res //│ = 1 ================================================ FILE: shared/src/test/diff/nu/TODO_Classes.mls ================================================ :NewDefs // *** First-class classes *** // class C //│ class C { //│ constructor() //│ } class D(x: Int) //│ class D(x: Int) :e fun foo(c) = new c //│ ╔══[ERROR] type identifier not found: c //│ ║ l.17: fun foo(c) = new c //│ ╙── ^ //│ fun foo: anything -> error :re foo(() => 123) //│ error //│ res //│ Runtime error: //│ TypeError: c is not a constructor :e foo(C) //│ ╔══[ERROR] Construction of unparameterized class C should use the `new` keyword //│ ║ l.31: foo(C) //│ ╙── ^ //│ error //│ res //│ = C {} :e fun bar(c) = new c(123) //│ ╔══[ERROR] type identifier not found: c //│ ║ l.41: fun bar(c) = new c(123) //│ ╙── ^ //│ fun bar: anything -> error :re bar(D) //│ error //│ res //│ Runtime error: //│ TypeError: c is not a constructor :e // TODO accept when we have first-class classes bar(D.class) //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.55: bar(D.class) //│ ║ ^^^^^^^ //│ ╟── reference of type `(x: Int) -> D` does not have field 'class' //│ ║ l.55: bar(D.class) //│ ╙── ^ //│ error //│ res //│ = D {} // *** Refinements *** // class C //│ class C { //│ constructor() //│ } :e // TODO support new C { val x = 1 } //│ ╔══[ERROR] Refinement terms are not yet supported //│ ║ l.77: new C { val x = 1 } //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ cannot generate code for term Rft(NuNew(Var(C)),TypingUnit(List(NuFunDef(Some(false),Var(x),None,List(),Left(IntLit(1)))))) // *** Path-dependent types *** // class Cls[out A] { fun x: A = x } //│ class Cls[A] { //│ constructor() //│ fun x: A //│ } let c = new Cls //│ let c: Cls[nothing] //│ c //│ = Cls {} // FIXME let y: c.A = c.x //│ ╔══[ERROR] type identifier not found: c //│ ║ l.102: let y: c.A = c.x //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing // *** GADTs *** // class Cls[A] { fun x: A = x; fun g: A -> Int; fun g = g } //│ class Cls[A] { //│ constructor() //│ fun g: A -> Int //│ fun x: A //│ } :e // TODO fun test(a: Object) = if a is Cls then a.x else error //│ ╔══[ERROR] Type error in `case` expression //│ ║ l.122: fun test(a: Object) = if a is //│ ║ ^^^^ //│ ║ l.123: Cls then a.x //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.124: else error //│ ║ ^^^^^^^^^^^^ //│ ╟── type variable `A` leaks out of its scope //│ ║ l.113: class Cls[A] { fun x: A = x; fun g: A -> Int; fun g = g } //│ ╙── ^ //│ fun test: (a: Object) -> anything :e // TODO fun test(a: Object) = if a is Cls then a.g(a.x) // a.x : a.A ; a.g : a.A -> a.A else 0 //│ ╔══[ERROR] Type error in `case` expression //│ ║ l.138: fun test(a: Object) = if a is //│ ║ ^^^^ //│ ║ l.139: Cls then a.g(a.x) // a.x : a.A ; a.g : a.A -> a.A //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.140: else 0 //│ ║ ^^^^^^^^ //│ ╟── type variable `A` leaks out of its scope //│ ║ l.113: class Cls[A] { fun x: A = x; fun g: A -> Int; fun g = g } //│ ╙── ^ //│ fun test: (a: Object) -> Int class Cls[out A] { fun x: A = x } //│ class Cls[A] { //│ constructor() //│ fun x: A //│ } fun test(a: Object) = if a is Cls then a.x else error //│ fun test: (a: Object) -> ??A fun test(a: Object) = if a is Cls then a else error //│ fun test: (a: Object) -> Cls[??A] :re test(0).x //│ ??A //│ res //│ Runtime error: //│ Error: an error was thrown // class Expr[T] { // constructor // IntLit(n: Int) { T = Int } // BoolLit(b: Bool) { T = Bool } // } // // fun foo(x) = if x is // IntLit then x.n as x.T // BoolLit then x.b // foo: (x: IntLit | BoolLit) -> x.T | Bool // // fun foo(x) = if x is // IntLit then x.n as x.T // BoolLit then x.b as x.T // foo: (x: IntLit | BoolLit) -> x.T // <: Expr['a] -> 'a // // fun foo(x: Expr[T]): T = if x is // IntLit then x.n // in Scala, compiler sees x.n : Int = x.T <: T // BoolLit then x.b abstract class Expr[A]: (IntLit | BoolLit) {} class IntLit() extends Expr[Int] class BoolLit() extends Expr[Bool] //│ abstract class Expr[A]: BoolLit | IntLit //│ class IntLit() extends Expr //│ class BoolLit() extends Expr fun test(f: ((IntLit | BoolLit) -> Int)) = f : Expr[anything] -> Int //│ fun test: (f: (BoolLit | IntLit) -> Int) -> Expr[anything] -> Int :e class OopsLit() extends Expr[Bool] //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.211: class OopsLit() extends Expr[Bool] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#OopsLit` does not match type `BoolLit | IntLit` //│ ╟── Note: constraint arises from union type: //│ ║ l.199: abstract class Expr[A]: (IntLit | BoolLit) {} //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ class OopsLit() extends Expr fun test(a) = if a is IntLit then 0 OopsLit then 1 //│ fun test: (IntLit | OopsLit) -> (0 | 1) ================================================ FILE: shared/src/test/diff/nu/ThisRefinedClasses.mls ================================================ :NewDefs // * I don't think it's a good idea to generate `this` refinements for classes, // * as this could easily lead to typos and delayed error reporting. // * Instead, we should require explicit `this` annotations by users when needed. :e class Foo { fun test = this.x } //│ ╔══[ERROR] Type `#Foo` does not contain member `x` //│ ║ l.11: class Foo { fun test = this.x } //│ ╙── ^^ //│ class Foo { //│ constructor() //│ fun test: error //│ } :e class Foo(n: Int) { fun test = this.x } //│ ╔══[ERROR] Type `#Foo & {n: Int}` does not contain member `x` //│ ║ l.22: class Foo(n: Int) { fun test = this.x } //│ ╙── ^^ //│ class Foo(n: Int) { //│ fun test: error //│ } :e class Foo(n: A) { fun test = this.x } //│ ╔══[ERROR] Type `#Foo & {Foo#A = A, n: A}` does not contain member `x` //│ ║ l.32: class Foo(n: A) { fun test = this.x } //│ ╙── ^^ //│ class Foo[A](n: A) { //│ fun test: error //│ } // TODO support: (treat `this` annot not like a term ascription) class Foo { this: { x: 'a } // fun test = this.x } //│ ╔══[ERROR] Type `#Foo` does not contain member `x` //│ ║ l.44: this: { x: 'a } //│ ╙── ^ //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in type ascription: //│ ║ l.44: this: { x: 'a } //│ ║ ^^^^ //│ ╟── type `{x: 'a}` does not match type `()` //│ ║ l.44: this: { x: 'a } //│ ║ ^^^^^^^^^ //│ ╟── but it flows into expression in statement position with expected type `()` //│ ║ l.44: this: { x: 'a } //│ ╙── ^^^^ //│ class Foo { //│ constructor() //│ } // TODO // * All on one line: class Test { this: { x: Int}; fun test = this.x } //│ ╔══[ERROR] Type `#Test` does not contain member `x` //│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } //│ ╙── ^^ //│ ╔══[ERROR] Type `#Test` does not contain member `x` //│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } //│ ╙── ^ //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in type ascription: //│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } //│ ║ ^^^^ //│ ╟── type `{x: Int}` does not match type `()` //│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } //│ ║ ^^^^^^^^^ //│ ╟── but it flows into expression in statement position with expected type `()` //│ ║ l.68: class Test { this: { x: Int}; fun test = this.x } //│ ╙── ^^^^ //│ class Test { //│ constructor() //│ fun test: error //│ } ================================================ FILE: shared/src/test/diff/nu/TraitParameters.mls ================================================ :NewDefs :e trait Base1(x: Int) //│ ╔══[ERROR] trait parameters are not yet supported //│ ║ l.5: trait Base1(x: Int) //│ ╙── ^^^^^^^^ //│ trait Base1 ================================================ FILE: shared/src/test/diff/nu/TraitSignatures.mls ================================================ :NewDefs trait C trait Foo: C //│ trait C //│ trait Foo: C class D() extends C, Foo D() : Foo //│ class D() extends C, Foo //│ Foo //│ res //│ = D {} class E extends C class F() extends E, Foo F() : Foo //│ class E extends C { //│ constructor() //│ } //│ class F() extends C, E, Foo //│ Foo //│ res //│ = F {} abstract class H extends Foo class I() extends H, C I() : Foo //│ abstract class H: C extends Foo //│ class I() extends C, Foo, H //│ Foo //│ res //│ = I {} :e class J extends Foo //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.38: class J extends Foo //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#J` is not an instance of type `C` //│ ╟── Note: constraint arises from type reference: //│ ║ l.6: trait Foo: C //│ ╙── ^ //│ class J extends Foo { //│ constructor() //│ } class C1 trait T1: C1 trait T2 extends T1 //│ class C1 { //│ constructor() //│ } //│ trait T1: C1 //│ trait T2: C1 extends T1 :e class C2 extends T1 //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.61: class C2 extends T1 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#C2` is not an instance of type `C1` //│ ╟── Note: constraint arises from type reference: //│ ║ l.52: trait T1: C1 //│ ╙── ^^ //│ class C2 extends T1 { //│ constructor() //│ } :e class C2 extends T2 //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.74: class C2 extends T2 //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#C2` is not an instance of type `C1` //│ ╟── Note: constraint arises from type reference: //│ ║ l.52: trait T1: C1 //│ ╙── ^^ //│ class C2 extends T1, T2 { //│ constructor() //│ } class C2 extends C1, T2 //│ class C2 extends C1, T1, T2 { //│ constructor() //│ } // * Interfaces that are "callable" are important to support for TS/JS interop declare trait Foo: ((x: Num) => Num) { val y: Str } //│ declare trait Foo: (x: Num) -> Num { //│ val y: Str //│ } (f: Foo) => [f.y, f(0)] //│ (f: Foo) -> [Str, Num] //│ res //│ = [Function: res] declare trait FooPoly: forall 'a: (x: 'a) => 'a //│ declare trait FooPoly: forall 'a. (x: 'a) -> 'a (f: FooPoly) => [f(0), f(true)] //│ (f: FooPoly) -> [0, true] //│ res //│ = [Function: res] :e class Oops(val y: Str) extends Foo //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.119: class Oops(val y: Str) extends Foo //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#Oops & {y: Str}` is not a function //│ ╟── Note: constraint arises from function type: //│ ║ l.96: declare trait Foo: ((x: Num) => Num) { //│ ╙── ^^^^^^^^^^^^^^^^^ //│ class Oops(y: Str) extends Foo :e Oops("")(1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.130: Oops("")(1) //│ ║ ^^^^^^^^^^^ //│ ╟── application of type `Oops` is not a function //│ ║ l.130: Oops("")(1) //│ ╙── ^^^^^^^^ //│ error //│ res //│ Runtime error: //│ TypeError: Oops(...) is not a function :e module Oops extends FooPoly //│ ╔══[ERROR] Type mismatch in type declaration: //│ ║ l.143: module Oops extends FooPoly //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── expression of type `#Oops` is not a function //│ ╟── Note: constraint arises from function type: //│ ║ l.109: declare trait FooPoly: forall 'a: (x: 'a) => 'a //│ ╙── ^^^^^^^ //│ module Oops extends FooPoly :e Oops(123) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.154: Oops(123) //│ ║ ^^^^^^^^^ //│ ╟── reference of type `Oops` is not a function //│ ║ l.154: Oops(123) //│ ╙── ^^^^ //│ error //│ res //│ Runtime error: //│ TypeError: Oops is not a function :e (Oops : FooPoly)(123) //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.167: (Oops : FooPoly)(123) //│ ║ ^^^^ //│ ╟── reference of type `Oops` is not a function //│ ╟── Note: constraint arises from function type: //│ ║ l.109: declare trait FooPoly: forall 'a: (x: 'a) => 'a //│ ║ ^^^^^^^ //│ ╟── from type reference: //│ ║ l.167: (Oops : FooPoly)(123) //│ ╙── ^^^^^^^ //│ 123 //│ res //│ Runtime error: //│ TypeError: Oops is not a function ================================================ FILE: shared/src/test/diff/nu/TrickyExtrusion2.mls ================================================ :NewDefs :DontDistributeForalls // * This test reproduces a situation where ?A <: ?B <: ?A and we extrude both ?A and ?B to a lower level. // * The goal was to check how the resulting approximants were related. // * It turns out that in the current implementation, // * since we (arbitrarily) only register upper bounds in this case (i.e., ?B ∈ UB(?A) and ?A ∈ UB(?B)) // * then the lower approximants end up being subtypes of each other, // * which happens when they are extruded negatively and their corresponding bounds are copied, // * but the upper approximants remain unrelated. // * This is sound, but the extra lower approximants bounds are not necessary, // * and indeed the SuperF constraining specification will not add them. class Invar[X, Y] { fun x: X -> X = id; fun y: Y -> Y = id } //│ class Invar[X, Y] { //│ constructor() //│ fun x: X -> X //│ fun y: Y -> Y //│ } fun bar[A, B]: () -> [A -> A, (B -> B) -> Invar[A, B]] = () => [id, _ => new Invar] //│ fun bar: forall 'A 'B. () -> ['A -> 'A, ('B -> 'B) -> Invar['A, 'B]] // :d :ns fun foo(x) = let inner() = let tmp = bar() let r = tmp.1(tmp.0) x(r) r //│ fun foo: forall 'a 'b 'A 'B 'A0 'B0 'c. 'a -> () //│ where //│ 'a <: 'b -> 'c //│ 'c <: () //│ 'b :> Invar[in 'A out 'A0, in 'B out 'B0] //│ 'B0 :> 'B //│ 'A0 :> 'A //│ 'A <: 'B //│ 'B <: 'A // * Above, 'A and 'B are the lower approximants and 'A0 and 'B0 are the upper approximants. ================================================ FILE: shared/src/test/diff/nu/TrickyGenericInheritance.mls ================================================ :NewDefs trait T1[A] { fun f: A -> A } //│ trait T1[A] { //│ fun f: A -> A //│ } class C1 extends T1 { fun f(x: Int) = x } //│ class C1 extends T1 { //│ constructor() //│ fun f: (x: Int) -> Int //│ } class C1 extends T1['FigureItOut] { fun f(x: Int) = x } //│ class C1 extends T1 { //│ constructor() //│ fun f: (x: Int) -> Int //│ } let c1 = new C1 //│ let c1: C1 //│ c1 //│ = C1 {} c1.f //│ (x: Int) -> Int //│ res //│ = [Function: f] (c1 : T1).f //│ ??A -> ??A0 //│ res //│ = [Function: f] (c1 : T1['X]).f //│ Int -> Int //│ res //│ = [Function: f] :ns (c1 : T1).f //│ 'f //│ where //│ 'f :> in ??A out ??A0 -> in ??A out ??A0 //│ res //│ = [Function: f] :ns (c1 : T1['X]).f //│ 'f //│ where //│ 'f :> 'X -> 'X //│ 'X :> Int //│ <: 'FigureItOut //│ 'FigureItOut :> Int //│ <: 'X & Int //│ res //│ = [Function: f] :e (c1 : T1['X]).f(false) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.69: (c1 : T1['X]).f(false) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── reference of type `false` is not an instance of `Int` //│ ║ l.69: (c1 : T1['X]).f(false) //│ ║ ^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.21: fun f(x: Int) = x //│ ║ ^^^ //│ ╟── from type variable: //│ ║ l.69: (c1 : T1['X]).f(false) //│ ╙── ^^ //│ Int | error | false //│ res //│ = false // * The more tricky case: // * TODO better error (cf. "exposes no constructor") :e trait T2[A] { fun f: A -> A val r = C2().f(false) } class C2 extends T2['FigureItOut] { fun f(x: Int) = x } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.94: val r = C2().f(false) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Class C2 cannot be instantiated as it exposes no constructor //│ ║ l.94: val r = C2().f(false) //│ ╙── ^^ //│ trait T2[A] { //│ fun f: A -> A //│ val r: error //│ } //│ class C2 extends T2 { //│ constructor() //│ fun f: (x: Int) -> Int //│ val r: error //│ } :e trait T3[A] { fun f: A -> A val r = (C3() : T3['X]).f(false) } class C3 extends T3['FigureItOut] { fun f(x: Int) = x } //│ ╔══[ERROR] Method implementations in traits are not yet supported //│ ║ l.118: val r = (C3() : T3['X]).f(false) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Class C3 cannot be instantiated as it exposes no constructor //│ ║ l.118: val r = (C3() : T3['X]).f(false) //│ ╙── ^^ //│ trait T3[A] { //│ fun f: A -> A //│ val r: false //│ } //│ class C3 extends T3 { //│ constructor() //│ fun f: (x: Int) -> Int //│ val r: false //│ } :e // FIXME C3() : T3['X] //│ ╔══[ERROR] Construction of unparameterized class C3 should use the `new` keyword //│ ║ l.140: C3() : T3['X] //│ ╙── ^^ //│ T3[Int] //│ res //│ Runtime error: //│ TypeError: Class constructor C3 cannot be invoked without 'new' // FIXME abstract class IO[A] { fun test = Bind(this) : IO[Int] } class Bind[A](underlying: IO[A]) extends IO[Int] //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.152: abstract class IO[A] { //│ ║ ^^^^^^^^^^^^^ //│ ║ l.153: fun test = Bind(this) : IO[Int] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.154: } //│ ╙── ^ //│ ╔══[ERROR] Type `Bind[?A]` does not contain member `IO#A` //│ ║ l.152: abstract class IO[A] { //│ ╙── ^ //│ abstract class IO[A] { //│ fun test: IO[Int] //│ } //│ class Bind[A](underlying: IO[A]) extends IO // FIXME abstract class IO[A] { fun map[B]: (A -> B) -> IO[B] // * Removing this works... fun map(f) = Map(this, f) fun run: A } class Map[A, B](underlying: IO[A], f: A -> B) extends IO[B] { fun run: B = error } //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.172: abstract class IO[A] { //│ ║ ^^^^^^^^^^^^^ //│ ║ l.173: fun map[B]: (A -> B) -> IO[B] // * Removing this works... //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.174: fun map(f) = Map(this, f) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.175: fun run: A //│ ║ ^^^^^^^^^^^^ //│ ║ l.176: } //│ ╙── ^ //│ ╔══[ERROR] Type `Map[?A, ?B]` does not contain member `IO#A` //│ ║ l.172: abstract class IO[A] { //│ ╙── ^ //│ abstract class IO[A] { //│ fun map: forall 'B. (A -> 'B) -> IO['B] //│ fun run: A //│ } //│ class Map[A, B](underlying: IO[A], f: A -> B) extends IO { //│ fun run: B //│ } // FIXME abstract class IO[A] { fun bind[B]: (A -> IO[B]) -> IO[B] // * FIXME: causes cycle error fun bind(f) = Bind(this, f) fun run: A } class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO[B] { fun run = f(underlying.run).run } //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.203: abstract class IO[A] { //│ ║ ^^^^^^^^^^^^^ //│ ║ l.204: fun bind[B]: (A -> IO[B]) -> IO[B] // * FIXME: causes cycle error //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.205: fun bind(f) = Bind(this, f) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.206: fun run: A //│ ║ ^^^^^^^^^^^^ //│ ║ l.207: } //│ ╙── ^ //│ ╔══[ERROR] Type `Bind[?A, ?B]` does not contain member `IO#A` //│ ║ l.203: abstract class IO[A] { //│ ╙── ^ //│ abstract class IO[A] { //│ fun bind: forall 'B. (A -> IO['B]) -> IO['B] //│ fun run: A //│ } //│ class Bind[A, B](underlying: IO[A], f: A -> IO[B]) extends IO { //│ fun run: B //│ } // TODO support abstract class IO[A] { class Bind[B]() extends IO[B] } //│ ╔══[ERROR] Unhandled cyclic definition //│ ║ l.235: abstract class IO[A] { //│ ║ ^^^^^^^^^^^^^ //│ ║ l.236: class Bind[B]() extends IO[B] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.237: } //│ ╙── ^ //│ abstract class IO[A] { //│ class Bind[B]() extends IO //│ } ================================================ FILE: shared/src/test/diff/nu/TupleParamBlunder.mls ================================================ :NewDefs // * TODO fix the parsing of this tuple-taking function signature! fun f: [Int, Int] -> Int fun f(a, b) = a + b // should not be a valid implementation //│ fun f: (Int, Int) -> Int //│ fun f: (Int, Int) -> Int fun f: (Int, Int) -> Int fun f(a, b) = a + b //│ fun f: (Int, Int) -> Int //│ fun f: (Int, Int) -> Int :e fun f: [Int, Int] => Int fun f(a, b) = a + b //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.17: fun f(a, b) = a + b //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── type `[[Int, Int]]` does not match type `[?a, ?b]` //│ ║ l.16: fun f: [Int, Int] => Int //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.17: fun f(a, b) = a + b //│ ╙── ^^^^^^ //│ fun f: (Int, Int) -> Int //│ fun f: ([Int, Int]) -> Int fun f: (Int, Int) => Int fun f(a, b) = a + b //│ fun f: (Int, Int) -> Int //│ fun f: (Int, Int) -> Int ================================================ FILE: shared/src/test/diff/nu/Tuples.mls ================================================ :NewDefs // Tuple literals use square brackets. let t = [0, 1, 2] //│ let t: [0, 1, 2] //│ t //│ = [ 0, 1, 2 ] // Tuple indices are zero-based. t.0 t.1 t.2 //│ 2 //│ res //│ = 0 //│ res //│ = 1 //│ res //│ = 2 // Tuple indices cannot be negative. :e t.-1 //│ ╔══[ERROR] identifier not found: .- //│ ║ l.26: t.-1 //│ ╙── ^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol .- // Non-zero tuple indices cannot start with a zero. :e t.01 //│ ╔══[ERROR] identifier not found: . //│ ║ l.37: t.01 //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol . // Out of bounds indices cause type errors. :e t.3 //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.48: t.3 //│ ║ ^^^ //│ ╟── tuple literal of type `{0: 0, 1: 1, 2: 2}` does not have field '3' //│ ║ l.5: let t = [0, 1, 2] //│ ║ ^^^^^^^^^ //│ ╟── but it flows into reference with expected type `{3: ?a}` //│ ║ l.48: t.3 //│ ╙── ^ //│ error //│ res //│ = undefined // Note that record fields can be integers with leading zeros. { 000000: "just zero" } { 042: "cuarenta y dos" } //│ {42: "cuarenta y dos"} //│ res //│ = { '0': 'just zero' } //│ res //│ = { '42': 'cuarenta y dos' } // Negative integer record fields are disallowed, which aligns with JavaScript. :pe :e { -1: "oh no" } //│ ╔══[PARSE ERROR] Record field should have a name //│ ╙── //│ ╔══[ERROR] Type mismatch in type ascription: //│ ╟── integer literal of type `-1` does not match type `"oh no"` //│ ╟── Note: constraint arises from literal type: //│ ║ l.76: { -1: "oh no" } //│ ╙── ^^^^^^^ //│ {: "oh no"} //│ res //│ = { '': -1 } // Note that leading zeros of integer record fields are ignored. // And duplicate fields lead to errors. :e { 0: "oh", 00: "no" } //│ ╔══[ERROR] Multiple declarations of field name 0 in record literal //│ ║ l.92: { 0: "oh", 00: "no" } //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Declared at //│ ║ l.92: { 0: "oh", 00: "no" } //│ ║ ^ //│ ╟── Declared at //│ ║ l.92: { 0: "oh", 00: "no" } //│ ╙── ^^ //│ {0: "oh", 0: "no"} //│ res //│ = { '0': 'no' } // Fields that are integers or start with an underscore should not be used as name hints. fun f(x) = x._q fun g(x) = x._1 fun h(x) = x.1 //│ fun f: forall 'a. {_q: 'a} -> 'a //│ fun g: forall 'b. {_1: 'b} -> 'b //│ fun h: forall 'c. {1: 'c} -> 'c // Other fields should be used as name hints. fun p(x) = x.y //│ fun p: forall 'y. {y: 'y} -> 'y ================================================ FILE: shared/src/test/diff/nu/TypeAliases.mls ================================================ :NewDefs type I = Int //│ type I = Int class CI1 //│ class CI1 { //│ constructor() //│ } // :e type AI1 = Array[Int] //│ type AI1 = Array[Int] type AI2 = Array //│ type AI2 = Array[Int] :e type AI3(n) = Array[Int] //│ ╔══[ERROR] Type alias definitions cannot have value parameters //│ ║ l.20: type AI3(n) = Array[Int] //│ ╙── ^^^ //│ type AI3 = Array[Int] // :e type AI3[A] = Array //│ type AI3[A] = Array[A] type AI4 = Array //│ type AI4[A] = Array[A] let r = 123 //│ let r: 123 //│ r //│ = 123 r: I //│ I //│ res //│ = 123 let a = [r, r, r] //│ let a: [123, 123, 123] //│ a //│ = [ 123, 123, 123 ] a : AI1 //│ AI1 //│ res //│ = [ 123, 123, 123 ] a : AI2 //│ AI2 //│ res //│ = [ 123, 123, 123 ] a : AI3[Int] //│ AI3[Int] //│ res //│ = [ 123, 123, 123 ] a : AI4 //│ AI4[Int] //│ res //│ = [ 123, 123, 123 ] ================================================ FILE: shared/src/test/diff/nu/TypeOps.mls ================================================ :NewDefs type *[A, B] = [A, B] //│ type *[A, B] = [A, B] fun x : Int * Int fun x = [0, 1] //│ fun x: [0, 1] //│ fun x: *[Int, Int] fun x : Int * [Int] fun x = [0, [1]] //│ fun x: [0, [1]] //│ fun x: *[Int, [Int]] fun x : [Int] * Int fun x = [[0], 1] //│ fun x: [[0], 1] //│ fun x: *[[Int], Int] type Id[A] = A //│ type Id[A] = A :e fun x: Id[Int, Int] //│ ╔══[ERROR] Wrong number of type arguments – expected 1, found 2 //│ ║ l.28: fun x: Id[Int, Int] //│ ╙── ^^^^^^^^^^^^ //│ fun x: Id[Int] fun x: Id[[Int, Int]] //│ fun x: Id[[Int, Int]] fun x: Id[[[Int, Int]]] //│ fun x: Id[[[Int, Int]]] ================================================ FILE: shared/src/test/diff/nu/TypeSelections.mls ================================================ :NewDefs module M { type T = Int -> Int class C(n: Int) fun mkC = C } //│ module M { //│ class C(n: Int) //│ type T = Int -> Int //│ fun mkC: (n: Int) -> C //│ } let x: M.T = id //│ let x: Int -> Int //│ x //│ = [Function: id] fun foo(x: M.C) = x //│ fun foo: (x: C) -> C foo(M.mkC(42)) //│ C //│ res //│ = C {} :e 42 : M.mkC //│ ╔══[ERROR] Illegal selection of value member in type position //│ ║ l.31: 42 : M.mkC //│ ╙── ^^^^ //│ error //│ res //│ = 42 ================================================ FILE: shared/src/test/diff/nu/TypeVariables.mls ================================================ :NewDefs fun x: 'a -> 'a = succ //│ fun x: Int -> Int :e fun x: forall 'a: 'a -> 'a = succ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.8: fun x: forall 'a: 'a -> 'a = succ //│ ║ ^^^^ //│ ╟── type `'a` is not an instance of type `Int` //│ ║ l.8: fun x: forall 'a: 'a -> 'a = succ //│ ║ ^^ //│ ╟── Note: quantified type variable 'a is defined at: //│ ║ l.8: fun x: forall 'a: 'a -> 'a = succ //│ ╙── ^^ //│ fun x: forall 'a. 'a -> 'a fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a] //│ fun x: [Int -> Int] :pe :e fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a,] //│ ╔══[PARSE ERROR] Unexpected end of square bracket section; an expression was expected here //│ ║ l.25: fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a,] //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: , //│ ║ l.25: fun x: [Int -> Int] = [id : forall 'a: 'a -> 'a,] //│ ╙── ^^^^^^^^^ //│ fun x: [Int -> Int] fun x: [Int -> Int,] = [id : forall 'a: 'a -> 'a] //│ fun x: [Int -> Int] ================================================ FILE: shared/src/test/diff/nu/TypingUnitTerms.mls ================================================ :NewDefs :e fun test = let x = 0(0) 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.6: let x = 0(0) //│ ║ ^^^^ //│ ╟── integer literal of type `0` is not a function //│ ║ l.6: let x = 0(0) //│ ╙── ^ //│ fun test: 1 ================================================ FILE: shared/src/test/diff/nu/TypreMembers.mls ================================================ :NewDefs class Test { type T = Int } //│ class Test { //│ constructor() //│ type T = Int //│ } 1 : Test.T //│ Int //│ res //│ = 1 trait Test { type T = Int } //│ trait Test { //│ type T = Int //│ } :e 1 : Test.T //│ ╔══[ERROR] Illegal prefix of type selection: Test //│ ║ l.22: 1 : Test.T //│ ╙── ^^^^ //│ error //│ res //│ = 1 ================================================ FILE: shared/src/test/diff/nu/Unapply.mls ================================================ // *** Explicit unapply tests *** // :NewDefs // * Unapply's current compilation strategy: // function D ... // D.class = class D { #x; static unapply(self) { return [self.#x] } } // D.unapply = D.class.unapply class D(x: Int) //│ class D(x: Int) D.unapply //│ forall '#x. (D & {#x: '#x}) -> ['#x] //│ res //│ = [Function: unapply] D.unapply(D(42)) //│ [Int] //│ res //│ = [ 42 ] :e D.unapply({ x: 42 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.25: D.unapply({ x: 42 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{x: 42}` does not have field '#x' //│ ║ l.25: D.unapply({ x: 42 }) //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.11: class D(x: Int) //│ ╙── ^ //│ error | [nothing] //│ res //│ Runtime error: //│ TypeError: Cannot read private member #x from an object whose class did not declare it class DT[T](x: T) DT.unapply //│ class DT[T](x: T) //│ forall '#x. (DT[anything] & {#x: '#x}) -> ['#x] //│ res //│ = [Function: unapply] DT.unapply(DT(42)) //│ [42] //│ res //│ = [ 42 ] :e DT.unapply({ x: 42 }) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.53: DT.unapply({ x: 42 }) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{x: 42}` does not have field '#x' //│ ║ l.53: DT.unapply({ x: 42 }) //│ ║ ^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.40: class DT[T](x: T) //│ ╙── ^ //│ error | [nothing] //│ res //│ Runtime error: //│ TypeError: Cannot read private member #x from an object whose class did not declare it // * Currently, support for unapply is pretty broken: it accesses an _unqualified_ private field // * although the same private field may be defined in different classes of the same hierarchy, // * which leads to unsoundness: class DS(x: Int) extends DT[Str]("a") //│ class DS(x: Int) extends DT // * Wrong type! DT.unapply(DS(42)) //│ [Int] //│ res //│ = [ 'a' ] // * TODO improve `unapply` logic: currently it picks up shadowing fields/methods class Foo(x: Int) { val x = toString(x) } //│ class Foo(x: Int) { //│ val x: Str //│ } // * Current hack: use `scrut.#x` to access a private field while passing the typer... Foo.unapply //│ forall '#x. (Foo & {#x: '#x}) -> ['#x] //│ res //│ = [Function: unapply] Foo.unapply(Foo(123)) //│ [Str] //│ res //│ = [ '123' ] if Foo(123) is Foo(a) then a //│ Str //│ res //│ = '123' // * Eventually we'll want to support this sort of overloading: :e fun D(x: Int) = {x} module D { fun unapply(a) = [a.x] } //│ ╔══[ERROR] Redefinition of 'D' //│ ║ l.113: module D { fun unapply(a) = [a.x] } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun D: (x: Int) -> {x: Int} //│ module D { //│ fun unapply: forall 'x. {x: 'x} -> ['x] //│ } ================================================ FILE: shared/src/test/diff/nu/UnaryMinus.mls ================================================ :NewDefs -1 //│ -1 //│ res //│ = -1 - 1 //│ -1 //│ res //│ = -1 1+1 //│ Int //│ res //│ = 2 1-3 //│ Int //│ res //│ = -2 3-1 //│ Int //│ res //│ = 2 1 - (3 - 5) //│ Int //│ res //│ = 3 3 - 1 //│ Int //│ res //│ = 2 1 -1 //│ Int //│ res //│ = 0 1-1 //│ Int //│ res //│ = 0 1+ -1 //│ Int //│ res //│ = 0 1 - (1 - 1) //│ Int //│ res //│ = 1 1 - 1 //│ Int //│ res //│ = 0 1 - - 1 //│ Int //│ res //│ = 2 1 - - - - 1 //│ Int //│ res //│ = 2 fun f() = 6 -f() //│ fun f: () -> 6 //│ Int //│ res //│ = -6 fun inv(x: Int) = -x inv(15) inv(inv(15)) //│ fun inv: (x: Int) -> Int //│ Int //│ res //│ = -15 //│ res //│ = 15 inv(f()) //│ Int //│ res //│ = -6 ================================================ FILE: shared/src/test/diff/nu/UndefMatching.mls ================================================ :NewDefs fun foo(x: 'a | undefined) = if x is undefined then error else x //│ fun foo: forall 'a. (x: Object & 'a & ~() | ()) -> 'a class Abstract[A] { // * Below, 'a in `foo` is basically `A & ~undefined` fun bar0(x: (A | undefined) & Object) = if x is undefined then error else x fun bar1(x: (A | undefined) & Object) = foo(x) fun bar2(x: (A | undefined) & Object) = x : Object & 'a & ~undefined | undefined fun baz(a: A & Object) = [bar0(a), bar1(a), bar2(a)] } //│ class Abstract[A] { //│ constructor() //│ fun bar0: (x: Object & A | ()) -> (Object & A & ~()) //│ fun bar1: (x: Object & A | ()) -> (Object & A & ~()) //│ fun bar2: (x: Object & A | ()) -> (Object & A & ~() | ()) //│ fun baz: (a: Object & A) -> [Object & A & ~(), Object & A & ~(), Object & A & ~() | ()] //│ } :e class Abstract[A] { fun bar(x: A & ~undefined | undefined) = if x is undefined then error else x } //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.35: if x is undefined then error else x //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `A & ~()` is not an instance of type `Object` //│ ║ l.34: fun bar(x: A & ~undefined | undefined) = //│ ║ ^^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Object` //│ ║ l.35: if x is undefined then error else x //│ ╙── ^ //│ class Abstract[A] { //│ constructor() //│ fun bar: (x: () | A & ~()) -> (A & ~()) //│ } ================================================ FILE: shared/src/test/diff/nu/Uninstantiable.mls ================================================ :NewDefs :e Int //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated //│ ║ l.5: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor //│ ║ l.5: Int //│ ╙── ^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol Int :e Int() //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated //│ ║ l.17: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor //│ ║ l.17: Int() //│ ╙── ^^^ //│ error //│ Code generation encountered an error: //│ unresolved symbol Int :e new Int //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated //│ ║ l.29: new Int //│ ╙── ^^^ //│ Int //│ Code generation encountered an error: //│ unresolved symbol Int // FIXME forbid statically module A extends Int //│ module A extends Int, Num //│ Code generation encountered an error: //│ unresolved parent Int. ================================================ FILE: shared/src/test/diff/nu/UnionReordering.mls ================================================ :NewDefs :NoJS type a = 0 | "0" | 2 | "2" | 10 //│ type a = "0" | "2" | 0 | 10 | 2 type a = "0" | 0 | 2 | "2" | 10 //│ type a = "0" | "2" | 0 | 10 | 2 class C[A]() class D[A]() //│ class C[A]() //│ class D[A]() let b = if true then C() else D() //│ let b: forall 'A 'A0. C['A] | D['A0] let b = if true then D() else C() //│ let b: forall 'A 'A0. C['A] | D['A0] // :ds let f(x) = if true then x else C() //│ let f: forall 'a. 'a -> (forall 'A. C['A] | 'a) type ExtLang[T] = T -> T type BaseLang[T] = T -> T //│ type ExtLang[T] = T -> T //│ type BaseLang[T] = T -> T type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] //│ type RegionLang = BaseLang[RegionLang] | ExtLang[RegionLang] // * Notice that the type components are reordered to be the same: type RegionLang1 = ExtLang[RegionLang] | BaseLang[RegionLang] type RegionLang2 = ExtLang[C] | ExtLang[D] //│ type RegionLang1 = BaseLang[RegionLang] | ExtLang[RegionLang] //│ type RegionLang2 = ExtLang[out C[?] | D[?]] // * There is currently no deep reordering, though: type RegionLang1 = ExtLang[C] -> Int & ExtLang[D] -> Str type RegionLang2 = ExtLang[D] -> Int & ExtLang[C] -> Str //│ type RegionLang1 = ExtLang[C[?]] -> Int & ExtLang[D[?]] -> Str //│ type RegionLang2 = ExtLang[D[?]] -> Int & ExtLang[C[?]] -> Str ================================================ FILE: shared/src/test/diff/nu/Unit.mls ================================================ :NewDefs () //│ () //│ res //│ = undefined fun x: () fun x = () //│ fun x: () //│ fun x: () x //│ () //│ res //│ = undefined :e // we used to treat () as an empty array; should in fact be JS's `undefined` x : Array['a] //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.21: x : Array['a] //│ ║ ^ //│ ╟── type `()` does not match type `Array['a]` //│ ║ l.9: fun x: () //│ ║ ^^ //│ ╟── but it flows into reference with expected type `Array['a]` //│ ║ l.21: x : Array['a] //│ ║ ^ //│ ╟── Note: constraint arises from applied type reference: //│ ║ l.21: x : Array['a] //│ ╙── ^^^^^^^^^ //│ Array[nothing] //│ res //│ = undefined x : undefined //│ () //│ res //│ = undefined fun x: () fun x = undefined //│ fun x: () //│ fun x: () :e fun x: () fun x = 1 //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.51: fun x = 1 //│ ║ ^^^^^ //│ ╟── integer literal of type `1` does not match type `()` //│ ║ l.51: fun x = 1 //│ ║ ^ //│ ╟── but it flows into definition of method x with expected type `()` //│ ║ l.51: fun x = 1 //│ ║ ^^^^^ //│ ╟── Note: constraint arises from literal type: //│ ║ l.50: fun x: () //│ ╙── ^^ //│ fun x: 1 //│ fun x: () (1) //│ 1 //│ res //│ = 1 // :pe // TODO? (1,) //│ 1 //│ res //│ = 1 (1,2) //│ 2 //│ res //│ = 2 (let x = 1) //│ () //│ res //│ = undefined :pe (let x = 1 in) //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.90: (let x = 1 in) //│ ╙── ^ //│ () //│ res //│ = undefined (log(1)) //│ () //│ res //│ = undefined //│ // Output //│ 1 :pe (log(1);) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.106: (log(1);) //│ ╙── ^ //│ () //│ res //│ = undefined //│ // Output //│ 1 :pe // support? (log(1); 2) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.117: (log(1); 2) //│ ╙── ^ //│ () //│ res //│ = undefined //│ // Output //│ 1 :pe // support? (log(1); ()) //│ ╔══[PARSE ERROR] Unexpected semicolon here //│ ║ l.128: (log(1); ()) //│ ╙── ^ //│ () //│ res //│ = undefined //│ // Output //│ 1 (((log((()))))) //│ () //│ res //│ = undefined //│ // Output //│ undefined (1, 2) //│ 2 //│ res //│ = 2 x => x //│ forall 'a. 'a -> 'a //│ res //│ = [Function: res] (x) => x //│ forall 'a. 'a -> 'a //│ res //│ = [Function: res] (x, y) => x + y //│ (Int, Int) -> Int //│ res //│ = [Function: res] (1, 2) => 3 //│ (1, 2) -> 3 //│ res //│ = [Function: res] 1 => (2, 3) //│ 1 -> 3 //│ res //│ = [Function: res] fun f(x, y) = x + y //│ fun f: (Int, Int) -> Int f(1, 2) //│ Int //│ res //│ = 3 f of 1, 2 //│ Int //│ res //│ = 3 :e f of (1, 2) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.195: f of (1, 2) //│ ║ ^^^^^^^^^^^ //│ ╟── argument of type `[?a]` does not match type `[?b, ?c]` //│ ║ l.195: f of (1, 2) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.181: fun f(x, y) = x + y //│ ╙── ^^^^^^ //│ Int | error //│ res //│ = NaN 0; 0 //│ 0 //│ res //│ = 0 //│ res //│ = 0 :w fun f = 0; 0 //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.220: 0; 0 //│ ╙── ^ //│ fun f: 0 :w fun f = succ(0); 0 //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in application: //│ ║ l.228: succ(0); 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` does not match type `()` //│ fun f: 0 fun f = discard(succ(0)); 0 //│ fun f: 0 fun f = discard of succ(0); 0 //│ fun f: 0 fun f = let _ = succ(0); 0 //│ fun f: 0 fun f = succ(0), 0 //│ fun f: 0 x => x; () //│ () //│ res //│ = [Function: res] //│ res //│ = undefined x => x; () //│ () //│ res //│ = [Function: res] //│ res //│ = undefined :w fun f = x => x; () //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.270: x => x; () //│ ╙── ^^^^^^ //│ fun f: () fun f = discard of x => x; () //│ fun f: () :w fun f = x => x () //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.282: x => x //│ ╙── ^^^^^^ //│ fun f: () :w module Test { 123 } //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.292: 123 //│ ╙── ^^^ //│ module Test :w module Test { 123 456 } //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.301: 123 //│ ╙── ^^^ //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.302: 456 //│ ╙── ^^^ //│ module Test :w module Test { x => x } //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.314: x => x //│ ╙── ^^^^^^ //│ module Test :e fun foo = let tmp = 0 foo + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.326: foo + 1 //│ ║ ^^^^^^^ //│ ╟── definition of method foo of type `()` is not an instance of type `Int` //│ ║ l.324: fun foo = //│ ║ ^^^^^ //│ ║ l.325: let tmp = 0 //│ ║ ^^^^^^^^^^^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.326: foo + 1 //│ ╙── ^^^ //│ fun foo: () //│ Int | error //│ res //│ = NaN ================================================ FILE: shared/src/test/diff/nu/UserDefinedAnnotations.mls ================================================ :NewDefs module tailrec2 extends Annotation //│ module tailrec2 extends Annotation @tailrec2 fun foo = 0 //│ fun foo: 0 class tailrec2() extends Annotation //│ class tailrec2() extends Annotation :e @tailrec2 fun foo = 0 //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.16: fun foo = 0 //│ ║ ^^^^^^^ //│ ╟── reference of type `() -> tailrec2` is not an instance of type `Annotation` //│ ║ l.15: @tailrec2 //│ ╙── ^^^^^^^^ //│ fun foo: 0 :e @hello fun foo = 0 //│ ╔══[ERROR] identifier not found: hello //│ ║ l.26: @hello //│ ╙── ^^^^^ //│ fun foo: 0 module hello extends Annotation //│ module hello extends Annotation @hello class Triple[A](a: A, b: A, c: A) //│ class Triple[A](a: A, b: A, c: A) :e @helloa class Triple[A](a: A, b: A, c: A) //│ ╔══[ERROR] identifier not found: helloa //│ ║ l.41: @helloa //│ ╙── ^^^^^^ //│ class Triple[A](a: A, b: A, c: A) @hello module dummy //│ module dummy :e @helloa module dummy //│ ╔══[ERROR] identifier not found: helloa //│ ║ l.53: @helloa //│ ╙── ^^^^^^ //│ module dummy :e // TODO: In the future, we should still support annotations that require a constructor. @tailrec2() fun foo = 0 //│ ╔══[ERROR] Type mismatch in annotated undefined literal: //│ ║ l.61: @tailrec2() //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `() -> tailrec2` is not an instance of type `Annotation` //│ ║ l.61: @tailrec2() //│ ╙── ^^^^^^^^ //│ fun foo: 0 //│ () //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/nu/ValSigs.mls ================================================ :NewDefs val x: Int //│ val x: Int //│ x //│ = // * Note that this counts as a completely new `val` since it's in a new block val x = "hi" //│ val x: "hi" //│ x //│ = 'hi' val x: Int val x = 1 //│ val x: 1 //│ val x: Int //│ x //│ = //│ x //│ = 1 val x = 1 //│ val x: 1 //│ x //│ = 1 x //│ 1 //│ res //│ = 1 :e val x: Int val x = "oops" //│ ╔══[ERROR] Type mismatch in definition: //│ ║ l.37: val x = "oops" //│ ║ ^^^^^^^^^^ //│ ╟── string literal of type `"oops"` is not an instance of type `Int` //│ ║ l.37: val x = "oops" //│ ║ ^^^^^^ //│ ╟── but it flows into definition of value x with expected type `Int` //│ ║ l.37: val x = "oops" //│ ║ ^^^^^^^^^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.36: val x: Int //│ ╙── ^^^ //│ val x: "oops" //│ val x: Int //│ x //│ = //│ x //│ = 'oops' ================================================ FILE: shared/src/test/diff/nu/Vals.mls ================================================ :NewDefs val a = 1 val b = a + 1 //│ val a: 1 //│ val b: Int //│ a //│ = 1 //│ b //│ = 2 // :e // FIXME should not type check :ge val c = d + 1 val d = 1 //│ val c: Int //│ val d: 1 //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding d // :e // FIXME should not type check :ge val a = a //│ val a: nothing //│ Code generation encountered an error: //│ unguarded recursive use of by-value binding a val f(x) = x //│ val f: forall 'a. 'a -> 'a //│ f //│ = [Function: f] f(123) //│ 123 //│ res //│ = 123 module M { let tmp = 2 val f(x) = x + tmp } //│ module M { //│ val f: Int -> Int //│ let tmp: 2 //│ } M.f(123) //│ Int //│ res //│ = 125 ================================================ FILE: shared/src/test/diff/nu/Varargs.mls ================================================ :NewDefs // * Built-in vararg :js join //│ (...Array[Str]) -> Str //│ // Prelude //│ function join(...xs) { //│ return xs.join(""); //│ } //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ res = join; //│ // End of generated code //│ res //│ = [Function: join] join("Hello", ",", " ", "World", "!") //│ Str //│ res //│ = 'Hello, World!' // * TODO It's currently not possible to define vararg-taking functions manually... :pe :e fun test(...xs) = xs.length //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.32: fun test(...xs) = xs.length //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: xs //│ ║ l.32: fun test(...xs) = xs.length //│ ╙── ^^ //│ fun test: () -> error //│ Code generation encountered an error: //│ unresolved symbol xs ================================================ FILE: shared/src/test/diff/nu/Virtual.mls ================================================ :NewDefs class Foo() { virtual fun f(x: Int) = x + 1 val g: Int = 0 } //│ class Foo() { //│ fun f: (x: Int) -> Int //│ val g: Int //│ } class Bar() extends Foo { fun f(x: Int) = x + 2 } //│ class Bar() extends Foo { //│ fun f: (x: Int) -> Int //│ val g: Int //│ } Bar().f(40) //│ Int //│ res //│ = 42 :e class Baz() extends Bar() { fun f(x: Int) = x + 3 } //│ ╔══[ERROR] Value member 'f' is not virtual and cannot be overridden //│ ║ l.27: fun f(x: Int) = x + 3 //│ ║ ^^^^^^^^^^^^^^^^^ //│ ╟── Originally declared here: //│ ║ l.13: fun f(x: Int) = x + 2 //│ ╙── ^^^^^^^^^^^^^^^^^ //│ class Baz() extends Bar, Foo { //│ fun f: (x: Int) -> Int //│ val g: Int //│ } :e class Baz() extends Foo { val g = 1 } //│ ╔══[ERROR] Value member 'g' is not virtual and cannot be overridden //│ ║ l.42: val g = 1 //│ ║ ^^^^^ //│ ╟── Originally declared here: //│ ║ l.5: val g: Int = 0 //│ ╙── ^^^^^^^^^^ //│ class Baz() extends Foo { //│ fun f: (x: Int) -> Int //│ val g: 1 //│ } mixin X { fun f(x) = x + 42 } mixin XX { fun f(x) = super.f(x) + 42 } class T() extends X, XX //│ mixin X() { //│ fun f: Int -> Int //│ } //│ mixin XX() { //│ super: {f: 'a -> Int} //│ fun f: 'a -> Int //│ } //│ class T() { //│ fun f: Int -> Int //│ } T().f(0 - 42) //│ Int //│ res //│ = 42 abstract class M1() { val foo: Str } class M2(s: Str) extends M1 { val foo = s } M2("foo").foo //│ abstract class M1() { //│ val foo: Str //│ } //│ class M2(s: Str) extends M1 { //│ val foo: Str //│ } //│ Str //│ res //│ = 'foo' class U() { fun f: Int -> Int fun f(x) = x + 1 } //│ class U() { //│ fun f: Int -> Int //│ } :e class V1() { virtual val x = 42 fun foo = x // unqualified access! } //│ ╔══[ERROR] Unqualified access to virtual member x //│ ║ l.106: fun foo = x // unqualified access! //│ ║ ^^^^^^^ //│ ╟── Declared here: //│ ║ l.105: virtual val x = 42 //│ ╙── ^^^^^^ //│ class V1() { //│ fun foo: 42 //│ val x: 42 //│ } class V2() { val x: Int virtual val x = 42 fun foo: Int = this.x } //│ class V2() { //│ fun foo: Int //│ val x: Int //│ } :e class C() { virtual fun x : Int fun x = 1 fun foo0 = x // should error } C().x //│ ╔══[ERROR] Unqualified access to virtual member x //│ ║ l.134: fun foo0 = x // should error //│ ║ ^^^^^^^^ //│ ╟── Declared here: //│ ║ l.133: fun x = 1 //│ ╙── ^^^^^ //│ class C() { //│ fun foo0: 1 //│ fun x: Int //│ } //│ Int //│ res //│ = 1 module D extends C { fun x = 2 } D.x //│ module D extends C { //│ fun foo0: 1 //│ fun x: 2 //│ } //│ 2 //│ res //│ = 2 ================================================ FILE: shared/src/test/diff/nu/WeirdDefs.mls ================================================ :NewDefs fun fst[x, _] = error : x -> x //│ fun fst: forall 'x. 'x -> 'x :e fun fst[x, _] = x //│ ╔══[ERROR] identifier not found: x //│ ║ l.8: fun fst[x, _] = x //│ ╙── ^ //│ fun fst: error //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: shared/src/test/diff/nu/WeirdUnions.mls ================================================ :NewDefs fun f: Str | [Str, Int] //│ fun f: Str | [Str, Int] fun f: [Str] | [Str, Int] //│ fun f: Array[Int | Str] & {0: Str} fun f: (Str | [Str, Int]) //│ fun f: Str | [Str, Int] :e fun f: Str | (Str, Int) //│ ╔══[ERROR] type identifier not found: , //│ ║ l.15: fun f: Str | (Str, Int) //│ ╙── ^^^^^^^^^^ //│ fun f: Str | error fun f: Str | ([Str, Int]) //│ fun f: Str | [Str, Int] :e fun f: Str | ((Str, Int)) //│ ╔══[ERROR] type identifier not found: , //│ ║ l.26: fun f: Str | ((Str, Int)) //│ ╙── ^^^^^^^^^^^^ //│ fun f: Str | error // * This type merges the input tuples, resulting in the union seen above: fun f: (Str => Str) & ((Str, Int) => Str) //│ fun f: (...Array[Int | Str] & {0: Str}) -> Str f("abc", "abc") //│ Str //│ res //│ = //│ f is not implemented // * Note: the merge doesn't happen when the result type is different... fun f: (Str => Str) & ((Str, Int) => Int) //│ fun f: Str -> Str & (Str, Int) -> Int // * ...resulting in approximation at call sites (we don't handle overloading) f("abc", "abc") //│ Int | Str //│ res //│ = //│ f is not implemented f("abcabc") //│ Int | Str //│ res //│ = //│ f is not implemented // * Mismatched argument list sizes yields surprising results: let r = if true then id else (x, y) => [y, x] //│ let r: (...nothing) -> [nothing, nothing] //│ r //│ = [Function: id] :e // * Expected: the function is uncallable until we implement syntax `r(...error)` r(error) r(error, error) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.71: r(error) //│ ║ ^^^^^^^^ //│ ╟── argument of type `[nothing]` does not match type `[?a, ?b]` //│ ║ l.71: r(error) //│ ║ ^^^^^^^ //│ ╟── Note: constraint arises from tuple literal: //│ ║ l.65: let r = if true then id else (x, y) => [y, x] //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.72: r(error, error) //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── argument list of type `[nothing, nothing]` does not match type `[?a]` //│ ║ l.72: r(error, error) //│ ╙── ^^^^^^^^^^^^^^ //│ error | [nothing, nothing] //│ res //│ Runtime error: //│ Error: an error was thrown //│ res //│ Runtime error: //│ Error: an error was thrown // * Note: the correct version: let r = if true then id else ([x, y]) => [y, x] //│ let r: forall 'a 'b 'c. (['a, 'b] & 'c) -> (['b, 'a] | 'c) //│ r //│ = [Function: id] r of [0, 1] //│ [0 | 1, 0 | 1] //│ res //│ = [ 0, 1 ] // Also currently parses the same: let r = if true then id else [x, y] => [y, x] //│ let r: forall 'a 'b 'c. (['a, 'b] & 'c) -> (['b, 'a] | 'c) //│ r //│ = [Function: id] ================================================ FILE: shared/src/test/diff/nu/With.mls ================================================ :NewDefs {} with {} //│ anything //│ res //│ = {} {x: 1} with {y: 2} //│ {x: 1, y: 2} //│ res //│ = { x: 1, y: 2 } {x: 1} with {x: 2} //│ {x: 2} //│ res //│ = { x: 2 } :pe {x: 1} with 123 //│ ╔══[PARSE ERROR] record literal expected here; found integer literal //│ ║ l.21: {x: 1} with 123 //│ ╙── ^^^ //│ {x: 1} //│ res //│ = { x: 1 } ================================================ FILE: shared/src/test/diff/nu/i180.mls ================================================ :NewDefs fun (++) stringConcat(a, b) = concat(a)(b) //│ fun (++) stringConcat: (Str, Str) -> Str type List[out A] = Cons[A] | Nil class Cons[out A](head: A, tail: List[A]) { fun join(sep: Str): Str fun join(sep) = if tail is Nil then toString(head) Cons(x, Nil) then toString(head) ++ sep ++ toString(x) Cons(x, xs) then toString(head) ++ sep ++ toString(x) ++ sep ++ xs.join(sep) } module Nil { fun join(sep: Str): Str = "" fun contains(x): Bool = false } //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) { //│ fun join: (sep: Str) -> Str //│ } //│ module Nil { //│ fun contains: anything -> Bool //│ fun join: (sep: Str) -> Str //│ } fun (::) cons[A](x: A, xs: List[A]): List[A] = Cons(x, xs) //│ fun (::) cons: forall 'A. (x: 'A, xs: List['A]) -> List['A] (1 :: Nil).join(", ") (1 :: (2 :: Nil)).join(", ") (1 :: (2 :: (3 :: Nil))).join(", ") (1 :: (2 :: (3 :: (4 :: Nil)))).join(", ") (1 :: (2 :: (3 :: (4 :: (5 :: Nil))))).join(", ") //│ Str //│ res //│ = '1' //│ res //│ = '1, 2' //│ res //│ = '1, 2, 3' //│ res //│ = '1, 2, 3, 4' //│ res //│ = '1, 2, 3, 4, 5' fun (:-) listExclude(xs, x) = if xs is Nil then Nil Cons(x', xs') and x === x' then xs' :- x else x' :: (xs' :- x) //│ fun (:-) listExclude: forall 'A. (Cons['A] | Nil, Eql['A]) -> (Nil | List['A]) (Nil :- 0).join(", ") ((0 :: Nil) :- 0).join(", ") ((1 :: Nil) :- 0).join(", ") ((1 :: (2 :: Nil)) :- 0).join(", ") //│ Str //│ res //│ = '' //│ res //│ = '' //│ res //│ = '1' //│ res //│ = '1, 2' ("x" :: Nil).join(", ") //│ Str //│ res //│ = 'x' ================================================ FILE: shared/src/test/diff/nu/i191.mls ================================================ :NewDefs class demo() { constructor(param1: Int) } val invalidConstruction = new demo(1) //│ class demo() { //│ constructor(param1: Int) //│ } //│ val invalidConstruction: demo //│ invalidConstruction //│ = demo {} :e class demo() { constructor(param1: Int) } val invalidConstruction = demo() //│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword //│ ║ l.15: val invalidConstruction = demo() //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.15: val invalidConstruction = demo() //│ ║ ^^^^^^ //│ ╟── argument of type `[]` does not match type `[param1: Int]` //│ ║ l.15: val invalidConstruction = demo() //│ ╙── ^^ //│ class demo() { //│ constructor(param1: Int) //│ } //│ val invalidConstruction: demo | error //│ invalidConstruction //│ = demo {} :e val invalidConstruction = demo(1) //│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword //│ ║ l.33: val invalidConstruction = demo(1) //│ ╙── ^^^^ //│ val invalidConstruction: demo //│ invalidConstruction //│ = demo {} :e val invalidConstruction = new demo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.42: val invalidConstruction = new demo //│ ║ ^^^^ //│ ╟── argument list of type `[]` does not match type `[param1: Int]` //│ ║ l.42: val invalidConstruction = new demo //│ ╙── ^ //│ val invalidConstruction: demo | error //│ invalidConstruction //│ = demo {} :e class demo { constructor(param1: Int) } val invalidConstruction = demo() //│ ╔══[ERROR] Construction of unparameterized class demo should use the `new` keyword //│ ║ l.55: val invalidConstruction = demo() //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.55: val invalidConstruction = demo() //│ ║ ^^^^^^ //│ ╟── argument of type `[]` does not match type `[param1: Int]` //│ ║ l.55: val invalidConstruction = demo() //│ ╙── ^^ //│ class demo { //│ constructor(param1: Int) //│ } //│ val invalidConstruction: demo | error //│ invalidConstruction //│ Runtime error: //│ TypeError: Class constructor demo cannot be invoked without 'new' :e val invalidConstruction = demo //│ ╔══[ERROR] Construction of unparameterized class demo should use the `new` keyword //│ ║ l.74: val invalidConstruction = demo //│ ╙── ^^^^ //│ val invalidConstruction: (param1: Int) -> demo //│ invalidConstruction //│ = [class demo] :e val invalidConstruction = new demo //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.83: val invalidConstruction = new demo //│ ║ ^^^^ //│ ╟── argument list of type `[]` does not match type `[param1: Int]` //│ ║ l.83: val invalidConstruction = new demo //│ ╙── ^ //│ val invalidConstruction: demo | error //│ invalidConstruction //│ = demo {} val invalidConstruction = new demo(1) //│ val invalidConstruction: demo //│ invalidConstruction //│ = demo {} :e val invalidConstruction = demo() //│ ╔══[ERROR] Construction of unparameterized class demo should use the `new` keyword //│ ║ l.100: val invalidConstruction = demo() //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.100: val invalidConstruction = demo() //│ ║ ^^^^^^ //│ ╟── argument of type `[]` does not match type `[param1: Int]` //│ ║ l.100: val invalidConstruction = demo() //│ ╙── ^^ //│ val invalidConstruction: demo | error //│ invalidConstruction //│ Runtime error: //│ TypeError: Class constructor demo cannot be invoked without 'new' class demo(x: Int) { constructor(param1: Int, param2: Int) { log(param1) log(param2) x = param1*param2 } } //│ class demo(x: Int) { //│ constructor(param1: Int, param2: Int) //│ } val validConstruction = new demo(5, 10) //│ val validConstruction: demo //│ validConstruction //│ = demo {} //│ // Output //│ 5 //│ 10 :e val invalidConstruction = demo(5) //│ ╔══[ERROR] Construction of class with auxiliary constructor should use the `new` keyword //│ ║ l.136: val invalidConstruction = demo(5) //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.136: val invalidConstruction = demo(5) //│ ║ ^^^^^^^ //│ ╟── argument of type `[5]` does not match type `[param1: Int, param2: Int]` //│ ║ l.136: val invalidConstruction = demo(5) //│ ╙── ^^^ //│ val invalidConstruction: demo | error //│ invalidConstruction //│ = demo {} //│ // Output //│ 5 //│ undefined :e val invalidConstruction = new demo(5) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.154: val invalidConstruction = new demo(5) //│ ║ ^^^^^^^^^^^ //│ ╟── argument list of type `[5]` does not match type `[param1: Int, param2: Int]` //│ ║ l.154: val invalidConstruction = new demo(5) //│ ╙── ^^^ //│ val invalidConstruction: demo | error //│ invalidConstruction //│ = demo {} //│ // Output //│ 5 //│ undefined ================================================ FILE: shared/src/test/diff/nu/repro0.mls ================================================ :NewDefs :NoJS class Add[out E](val lhs: E) val add11 = Add(add11) module EvalAddLit { fun eval(e: Add['A]) = if e is Add then eval(e.lhs) } let res = EvalAddLit.eval(add11) //│ class Add[E](lhs: E) //│ val add11: 'E //│ module EvalAddLit { //│ fun eval: forall 'A. (e: 'A) -> nothing //│ } //│ let res: nothing //│ where //│ 'A <: Add['A] //│ 'E :> Add['E] ================================================ FILE: shared/src/test/diff/nu/repro1.mls ================================================ :NewDefs :NoJS class Union[out Region](val a: Region) // class Union[Region](a: Region) //│ class Union[Region](a: Region) fun go(x) = Union(go(x)) let circles = go(2) //│ fun go: forall 'Region. anything -> 'Region //│ let circles: forall 'Region0. 'Region0 //│ where //│ 'Region0 :> Union['Region0] //│ 'Region :> Union['Region] fun contains(a) = if a is Union then contains(a.a) //│ fun contains: forall 'a. 'a -> nothing //│ where //│ 'a <: Union['a] contains(circles) //│ nothing mixin Contains { fun contains(a) = if a is Union then this.contains(a.a) } //│ mixin Contains() { //│ this: {contains: 'a -> 'b} //│ fun contains: Union['a] -> 'b //│ } module TestContains extends Contains //│ module TestContains { //│ fun contains: 'a -> nothing //│ } //│ where //│ 'a <: Union['a] TestContains.contains(circles) //│ nothing ================================================ FILE: shared/src/test/diff/nu/repro_EvalNegNeg.mls ================================================ :NewDefs class Add(lhs: E, rhs: E) class Lit(n: Int) class Neg(expr: A) //│ class Add[E](lhs: E, rhs: E) //│ class Lit(n: Int) //│ class Neg[A](expr: A) // Note the inferred type because of current UCS limitation mixin EvalBase { fun eval(e) = if e is Neg(Neg(d)) then this.eval(d) else if e is Neg(d) then 0 - this.eval(d) else if e is Lit(n) then n Add(l, r) then this.eval(l) + this.eval(r) } //│ mixin EvalBase() { //│ this: {eval: 'a -> 'b & 'c -> Int} //│ fun eval: (Add['c] | Lit | Neg['c & (Neg['a] | Object & ~#Neg)]) -> (Int | 'b) //│ } // module TestLang extends EvalBase, EvalNeg module TestLang extends EvalBase //│ module TestLang { //│ fun eval: 'a -> Int //│ } //│ where //│ 'a <: Add['a] | Lit | Neg['a & (Neg['a] | Object & ~#Neg)] fun mk(n) = if n is 0 then Lit(0) 1 then Neg(mk(n)) _ then Add(mk(n), mk(n)) //│ fun mk: forall 'a. Object -> (Lit | 'a) //│ where //│ 'a :> Add[Lit | 'a] | Neg[Lit | 'a] :stats TestLang.eval(mk(0)) //│ Int //│ res //│ = 0 //│ constrain calls : 254 //│ annoying calls : 106 //│ subtyping calls : 1430 ================================================ FILE: shared/src/test/diff/nu/repro_PolymorphicVariants.mls ================================================ :NewDefs // Test that reprduces subtle type simplification bug class Cons[out A](head: A, tail: Cons[A] | Nil) module Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) //│ module Nil class Abs[A](x: string, t: A) class App[A](s: A, t: A) //│ class Abs[A](x: string, t: A) //│ class App[A](s: A, t: A) fun eval(sub, v) = if v is Abs(x, t) then eval(Cons([error, error], Nil), error) eval(Cons(error, sub), error) error //│ fun eval: (Cons[anything] | Nil, Abs[anything]) -> nothing mixin EvalLambda { fun eval(sub, v) = if v is Abs(x, t) then this.eval(Cons([error, error], Nil), error) this.eval(Cons([x, error], sub), error) error } //│ mixin EvalLambda() { //│ this: {eval: (Cons[[string, nothing] | 'A], nothing) -> ()} //│ fun eval: (Cons['A] | Nil, Abs[anything]) -> nothing //│ } // * Note: this used to crash because of a current type simplification bug: analyze2 does not traverse TVs witht he correct PolMap // * The original unreduced version in PolymorphicVariants.mls still crashes... // :ds module Test1 extends EvalLambda //│ module Test1 { //│ fun eval: (Cons[anything] | Nil, Abs[anything]) -> nothing //│ } ================================================ FILE: shared/src/test/diff/parser/Annot.mls ================================================ :p @hello fun hello(x) = @a @b let rec x = @hello 2 //│ |@|hello|↵|#fun| |hello|(|x|)| |#=| |→|@|a|↵|@|b|↵|#let| |#rec| |x| |#=| |@|hello| |2|←| //│ Parsed: {fun hello = (x,) => {let rec x = @hello 2}} //│ AST: TypingUnit(List(NuFunDef(None,Var(hello),None,List(),Left(Lam(Tup(List((None,Fld(_,Var(x))))),Blk(List(NuFunDef(Some(true),Var(x),None,List(),Left(Ann(Var(hello),IntLit(2))))))))))) //│ Parsed: :p @tailrec 2 //│ |@|tailrec| |2| //│ Parsed: {@tailrec 2} //│ AST: TypingUnit(List(Ann(Var(tailrec),IntLit(2)))) //│ Parsed: :p if @hello true then 2 else 3 //│ |#if| |@|hello| |true| |#then| |2| |#else| |3| //│ Parsed: {if (@hello true) then 2 else 3} //│ AST: TypingUnit(List(If(IfThen(Ann(Var(hello),Var(true)),IntLit(2)),Some(IntLit(3))))) //│ Parsed: :pe if @test x is Foo then 2 //│ |#if| |@|test| |x|→|is| |Foo| |#then| |2|←| //│ ╔══[PARSE ERROR] Unexpected annotation //│ ║ l.28: if @test x //│ ╙── ^^^^ //│ Parsed: {if x ‹· is (Foo) then 2›} ================================================ FILE: shared/src/test/diff/parser/Arrays.mls ================================================ :AllowParseErrors [] //│ |[||]| //│ Parsed: {[]} [1] //│ |[|1|]| //│ Parsed: {[1,]} [1,] //│ |[|1|,|]| //│ Parsed: {[1,]} [1, 2, 3] //│ |[|1|,| |2|,| |3|]| //│ Parsed: {[1, 2, 3,]} () //│ |(||)| //│ Parsed: {undefined} (1) //│ |(|1|)| //│ Parsed: {'(' 1 ')'} (1,) //│ |(|1|,|)| //│ Parsed: {'(' 1 ')'} (1, 2, 3) //│ |(|1|,| |2|,| |3|)| //│ Parsed: {,(1, ,(2, 3,),)} 1 //│ |1| //│ Parsed: {1} 1, //│ |1|,| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.41: 1, //│ ╙── ^ //│ Parsed: {,(1, undefined,)} 1, 2, 3 //│ |1|,| |2|,| |3| //│ Parsed: {,(1, ,(2, 3,),)} f of 1, 2, 3 //│ |f| |#of| |1|,| |2|,| |3| //│ Parsed: {f(1, 2, 3,)} f of 1, 2, 3 //│ |f| |#of|→|1|,| |2|,| |3|←| //│ Parsed: {f(1, 2, 3,)} f of 1, 2, 3 //│ |f| |#of|→|1|,|↵|2|,|↵|3|←| //│ Parsed: {f(1, 2, 3,)} let arr = [] //│ |#let| |arr| |#=| |[||]| //│ Parsed: {let arr = []} let arr = [ ] //│ |#let| |arr| |#=| |[|↵|]| //│ Parsed: {let arr = []} let arr = [ ] //│ |#let| |arr| |#=|↵|[|↵|]| //│ ╔══[PARSE ERROR] Unexpected newline in expression position //│ ║ l.78: let arr = //│ ║ ^ //│ ║ l.79: [ //│ ╙── //│ Parsed: {let arr = []} let arr = [ 1 ] //│ |#let| |arr| |#=| |[|→|1|←|↵|]| //│ Parsed: {let arr = [1,]} let arr = [ 1, 2 ] //│ |#let| |arr| |#=| |[|→|1|,| |2|←|↵|]| //│ Parsed: {let arr = [1, 2,]} let arr = [ 1, 2 ] //│ |#let| |arr| |#=| |[|→|1|,|↵|2|←|↵|]| //│ Parsed: {let arr = [1, 2,]} // :pe f [1, 2, 3] //│ |f| |[|1|,| |2|,| |3|]| //│ Parsed: {f‹1, 2, 3›} f([1, 2, 3]) //│ |f|(|[|1|,| |2|,| |3|]|)| //│ Parsed: {f([1, 2, 3,],)} f of [1, 2, 3] //│ |f| |#of| |[|1|,| |2|,| |3|]| //│ Parsed: {f([1, 2, 3,],)} ================================================ FILE: shared/src/test/diff/parser/BasicSyntax.mls ================================================ :AllowParseErrors 1 //│ |1| //│ Parsed: {1} f 1 //│ |f| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.7: f 1 //│ ╙── ^^^ //│ Parsed: {f(1,)} () //│ |(||)| //│ Parsed: {undefined} f() //│ |f|(||)| //│ Parsed: {f()} f(1) //│ |f|(|1|)| //│ Parsed: {f(1,)} f (1) //│ |f| |(|1|)| //│ Parsed: {f(1,)} f of 1 //│ |f| |#of| |1| //│ Parsed: {f(1,)} 1 + 1 //│ |1| |+| |1| //│ Parsed: {+(1,)(1,)} f 1 + 1 //│ |f| |1| |+| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.38: f 1 + 1 //│ ╙── ^^^^^^^ //│ Parsed: {f(+(1,)(1,),)} f(1 + 1) //│ |f|(|1| |+| |1|)| //│ Parsed: {f(+(1,)(1,),)} f of 1 + 1 //│ |f| |#of| |1| |+| |1| //│ Parsed: {f(+(1,)(1,),)} f(1) + 1 //│ |f|(|1|)| |+| |1| //│ Parsed: {+(f(1,),)(1,)} 1 2 3 //│ |1| |2| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.57: 1 2 3 //│ ╙── ^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.57: 1 2 3 //│ ╙── ^^^^^ //│ Parsed: {1(2(3,),)} 12 3 //│ |12| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.67: 12 3 //│ ╙── ^^^^^ //│ Parsed: {12(3,)} 3 + 2 4 - 1 //│ |3| |+| |2| |4| |-| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.74: 3 + 2 4 - 1 //│ ╙── ^^^^^^^ //│ Parsed: {+(3,)(2(-(4,)(1,),),)} foo bar baz 1 2 3 //│ |foo| |bar|→|baz| |1|↵|2| |3|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.82: baz 1 //│ ╙── ^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.83: 2 3 //│ ╙── ^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.81: foo bar //│ ║ ^^^ //│ ║ l.82: baz 1 //│ ║ ^^^^^^^ //│ ║ l.83: 2 3 //│ ╙── ^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.81: foo bar //│ ║ ^^^^^^^ //│ ║ l.82: baz 1 //│ ║ ^^^^^^^ //│ ║ l.83: 2 3 //│ ╙── ^^^^^ //│ Parsed: {foo(bar({baz(1,); 2(3,)},),)} foo bar baz 1 2 3 4 v5 //│ |foo| |bar|→|baz| |1|→|2| |3|←|↵|4| |v5|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.109: 2 3 //│ ╙── ^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.108: baz 1 //│ ║ ^ //│ ║ l.109: 2 3 //│ ╙── ^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.108: baz 1 //│ ║ ^^^^^ //│ ║ l.109: 2 3 //│ ╙── ^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.110: 4 v5 //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.107: foo bar //│ ║ ^^^ //│ ║ l.108: baz 1 //│ ║ ^^^^^^^ //│ ║ l.109: 2 3 //│ ║ ^^^^^^^ //│ ║ l.110: 4 v5 //│ ╙── ^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.107: foo bar //│ ║ ^^^^^^^ //│ ║ l.108: baz 1 //│ ║ ^^^^^^^ //│ ║ l.109: 2 3 //│ ║ ^^^^^^^ //│ ║ l.110: 4 v5 //│ ╙── ^^^^^^ //│ Parsed: {foo(bar({baz(1(2(3,),),); 4(v5,)},),)} foo of bar of baz of 1 of 2 of 3 4 of v5 //│ |foo| |#of| |bar| |#of|→|baz| |#of| |1| |#of|→|2| |#of| |3|←|↵|4| |#of| |v5|←| //│ Parsed: {foo(bar({baz(1(2(3,),),); 4(v5,)},),)} foo 1 //│ |foo|→|1|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.155: foo //│ ║ ^^^ //│ ║ l.156: 1 //│ ╙── ^^^ //│ Parsed: {foo(1,)} foo of 1 //│ |foo| |#of|→|1|←| //│ Parsed: {foo(1,)} foo of 1 //│ |foo|→|#of| |1|←| //│ Parsed: {foo(1,)} foo of 1, 2, 3 //│ |foo|→|#of| |1|,| |2|,| |3|←| //│ Parsed: {foo(1, 2, 3,)} foo of (1, 2, 3) //│ |foo|→|#of| |(|1|,| |2|,| |3|)|←| //│ Parsed: {foo(,(1, ,(2, 3,),),)} foo of 1 //│ |foo|→|#of|→|1|←|←| //│ Parsed: {foo(1,)} // TODO foo of 1 of 2 //│ |foo|→|#of| |1|↵|#of| |2|←| //│ Parsed: {foo(1,)(2,)} foo of f of 2 //│ |foo|→|#of| |f|→|#of| |2|←|←| //│ Parsed: {foo(f(2,),)} foo of 1 of 2 //│ |foo|→|#of| |1|←|→|#of| |2|←| //│ Parsed: {foo(1,)(2,)} //│ | | //│ Parsed: {} 1 //│ | |1| //│ Parsed: {1} 1 //│ | |→|1|←| //│ Parsed: {{1}} (1) //│ |(|1|)| //│ Parsed: {'(' 1 ')'} (1 //│ ╔══[PARSE ERROR] Unmatched opening parenthesis //│ ║ l.227: (1 //│ ╙── ^ //│ |1| //│ Parsed: {1} (1)) //│ ╔══[PARSE ERROR] Unexpected closing parenthesis //│ ║ l.234: (1)) //│ ╙── ^ //│ |(|1|)| //│ Parsed: {'(' 1 ')'} ( //│ ╔══[PARSE ERROR] Unmatched opening parenthesis //│ ║ l.241: ( //│ ╙── ^ //│ || //│ Parsed: {} ) //│ ╔══[PARSE ERROR] Unexpected closing parenthesis //│ ║ l.248: ) //│ ╙── ^ //│ || //│ Parsed: {} 1+ //│ |1|+| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.255: 1+ //│ ╙── ^ //│ Parsed: {+(1,)(undefined,)} * //│ |*| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.262: * //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.262: * //│ ╙── ^ //│ Parsed: {undefined} f 1 //│ |f| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.273: f 1 //│ ╙── ^^^ //│ Parsed: {f(1,)} f(1) //│ |f|(|1|)| //│ Parsed: {f(1,)} f (1) //│ |f| |(|1|)| //│ Parsed: {f(1,)} f 1, 2, 3 //│ |f| |1|,| |2|,| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.289: f 1, 2, 3 //│ ╙── ^^^^^^^^^ //│ Parsed: {f(1, 2, 3,)} f (1, 2, 3) //│ |f| |(|1|,| |2|,| |3|)| //│ Parsed: {f(1, 2, 3,)} f(1, 2, 3) //│ |f|(|1|,| |2|,| |3|)| //│ Parsed: {f(1, 2, 3,)} // :pe f[] //│ |f|[||]| //│ Parsed: {f‹›} f[1] //│ |f|[|1|]| //│ Parsed: {f‹1›} f[1, 2, 3] //│ |f|[|1|,| |2|,| |3|]| //│ Parsed: {f‹1, 2, 3›} f{} //│ |f|{||}| //│ Parsed: {f {}} f{1} //│ |f|{|1|}| //│ Parsed: {f {1}} f{1, 2, 3} //│ |f|{|1|,| |2|,| |3|}| //│ Parsed: {f {,(1, ,(2, 3,),)}} f 1,, 2 //│ |f| |1|,|,| |2| //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.330: f 1,, 2 //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.330: f 1,, 2 //│ ╙── ^^^^^^^ //│ Parsed: {f(1, 2,)} f of x //│ |f| |#of| |x| //│ Parsed: {f(x,)} f g x //│ |f| |g| |x| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.345: f g x //│ ╙── ^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.345: f g x //│ ╙── ^^^^^ //│ Parsed: {f(g(x,),)} f of g of x //│ |f| |#of| |g| |#of| |x| //│ Parsed: {f(g(x,),)} f of of g //│ |f| |#of| |#of| |g| //│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position //│ ║ l.359: f of of g //│ ╙── ^^ //│ Parsed: {f(g,)} f x: 1 //│ |f| |x|#:| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.367: f x: 1 //│ ╙── ^^^^^^ //│ Parsed: {f(x: 1,)} f x: 1, //│ |f| |x|#:| |1|,| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.374: f x: 1, //│ ╙── ^^^^^^ //│ Parsed: {f(x: 1,)} f x : 1 //│ |f| |x| |#:| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.381: f x : 1 //│ ╙── ^^^ //│ Parsed: {f(x : 1,)} f x: 1, y: 2 //│ |f| |x|#:| |1|,| |y|#:| |2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.388: f x: 1, y: 2 //│ ╙── ^^^^^^^^^^^^ //│ Parsed: {f(x: 1, y: 2,)} f x : 1, y: 2 //│ |f| |x| |#:| |1|,| |y|#:| |2| //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.395: f x : 1, y: 2 //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.395: f x : 1, y: 2 //│ ╙── ^^^ //│ Parsed: {f(x : anything,)} f x: 1, y: 2, z: 3 //│ |f| |x|#:| |1|,| |y|#:| |2|,| |z|#:| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.405: f x: 1, y: 2, z: 3 //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ Parsed: {f(x: 1, y: 2, z: 3,)} f x: 1, y: g 2, z: 3 //│ |f| |x|#:| |1|,| |y|#:| |g| |2|,| |z|#:| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.412: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.412: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f(x: 1, y: g(2, z: 3)) //│ |f|(|x|#:| |1|,| |y|#:| |g|(|2|,| |z|#:| |3|)|)| //│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f(x: 1, y: g(2), z: 3) //│ |f|(|x|#:| |1|,| |y|#:| |g|(|2|)|,| |z|#:| |3|)| //│ Parsed: {f(x: 1, y: g(2,), z: 3,)} f x: 1, y: g 2, z: 3 //│ |f| |x|#:| |1|,| |y|#:| |g| |2|,| |z|#:| |3| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.430: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.430: f x: 1, y: g 2, z: 3 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f of x: 1, y: g of 2, z: 3 //│ |f| |#of| |x|#:| |1|,| |y|#:| |g| |#of| |2|,| |z|#:| |3| //│ Parsed: {f(x: 1, y: g(2, z: 3,),)} f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ |f| |x|#:| |1| |+| |1|,| |y|#:| |2| |2|,| |z|#:| |3| |+| |2| |4| |-| |1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.444: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ ╙── ^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.444: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.444: f x: 1 + 1, y: 2 2, z: 3 + 2 4 - 1 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ Parsed: {f(x: +(1,)(1,), y: 2(2, z: +(3,)(2(-(4,)(1,),),),),)} x.y //│ |x|.y| //│ Parsed: {(x).y} .y //│ |.y| //│ ╔══[PARSE ERROR] Unexpected selector in expression position //│ ║ l.462: .y //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.462: .y //│ ╙── ^ //│ Parsed: {undefined} 2 + f of 3 + 3 //│ |2| |+| |f| |#of| |3| |+| |3| //│ Parsed: {+(2,)(f(+(3,)(3,),),)} 2 + 2 of 3 + 3 //│ |2| |+| |2|→|#of| |3| |+| |3|←| //│ Parsed: {+(2,)(2,)(+(3,)(3,),)} ================================================ FILE: shared/src/test/diff/parser/Binds.mls ================================================ :AllowParseErrors f(x) is True //│ |f|(|x|)| |is| |True| //│ Parsed: {is(f(x,),)(True,)} // Precedence of 'of'/'is' may be surprising! f of x is True //│ |f| |#of| |x| |is| |True| //│ Parsed: {f(is(x,)(True,),)} ================================================ FILE: shared/src/test/diff/parser/Blocks.mls ================================================ :AllowParseErrors a b //│ |a|↵|b| //│ Parsed: {a; b} a, b //│ |a|,|↵|b| //│ Parsed: {,(a, b,)} a, b, //│ |a|,|↵|b|,| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.15: b, //│ ╙── ^ //│ Parsed: {,(a, ,(b, undefined,),)} a, b, c //│ |a|,|↵|b|,|↵|c| //│ Parsed: {,(a, ,(b, c,),)} foo a b //│ |foo|→|a|↵|b|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.28: foo //│ ║ ^^^ //│ ║ l.29: a //│ ║ ^^^ //│ ║ l.30: b //│ ╙── ^^^ //│ Parsed: {foo({a; b},)} foo( a, b ) //│ |foo|(|→|a|,|↵|b|←|↵|)| //│ Parsed: {foo(a, b,)} foo( a, b, ) //│ |foo|(|→|a|,|↵|b|,|←|↵|)| //│ Parsed: {foo(a, b,)} foo( a b ) //│ |foo|(|→|a|↵|b|←|↵|)| //│ Parsed: {foo({a; b},)} foo( a b ) //│ |foo|(|→|a|←|→|b|←|↵|)| //│ ╔══[PARSE ERROR] Unexpected indented block here //│ ║ l.64: b //│ ║ ^^^ //│ ║ l.65: ) //│ ╙── ^ //│ Parsed: {foo(a,)} // TODO foo of a fun f = 1 b //│ |foo| |#of|→|a|↵|#fun| |f| |#=| |1|↵|b|←| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.77: fun f = 1 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected '=' here //│ ║ l.77: fun f = 1 //│ ╙── ^ //│ Parsed: {foo({a; f},)} foo + a b //│ |foo| |+|→|a|↵|b|←| //│ Parsed: {+(foo,)({a; b},)} foo(a, b, c) foo of a, b, c //│ |foo|(|a|,| |b|,| |c|)|↵|foo| |#of| |a|,| |b|,| |c| //│ Parsed: {foo(a, b, c,); foo(a, b, c,)} foo of aaaaa, bbbbb, ccccc //│ |foo| |#of|→|aaaaa|,|↵|bbbbb|,|↵|ccccc|←| //│ Parsed: {foo(aaaaa, bbbbb, ccccc,)} foo of a boo x y c //│ |foo| |#of|→|a|↵|boo|→|x|↵|y|←|↵|c|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.109: boo //│ ║ ^^^ //│ ║ l.110: x //│ ║ ^^^^^ //│ ║ l.111: y //│ ╙── ^^^^^ //│ Parsed: {foo({a; boo({x; y},); c},)} fun foo = print("ok") print("ko") //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|←| //│ Parsed: {fun foo = {print("ok",); print("ko",)}} fun foo = print("ok") print("ko") //│ |#fun| |foo| |#=|→|print|(|"ok"|)|↵|print|(|"ko"|)|↵|←| //│ Parsed: {fun foo = {print("ok",); print("ko",)}} fun foo = fun local(x) = x + 1 print(local(1)) class Foo //│ |#fun| |foo| |#=|→|#fun| |local|(|x|)| |#=| |x| |+| |1|↵|print|(|local|(|1|)|)|↵|#class| |Foo|←| //│ Parsed: {fun foo = {fun local = (x,) => +(x,)(1,); print(local(1,),); class Foo {}}} fun foo = fun local(x) = class Foo { fun bar = x + 1 } Foo().bar print of local(0) + local(1) print of (local of 0) + local of 1 fun tmp = 1 print of local of 0 + local of 1 fun tmp = 2 //│ |#fun| |foo| |#=|→|#fun| |local|(|x|)| |#=|→|#class| |Foo| |{|→|#fun| |bar| |#=| |x| |+| |1|←|↵|}|↵|Foo|(||)|.bar|←|↵|print| |#of| |local|(|0|)| |+| |local|(|1|)|↵|print| |#of| |(|local| |#of| |0|)| |+| |local| |#of| |1|↵|#fun| |tmp| |#=| |1|↵|print| |#of| |local| |#of| |0| |+| |local| |#of| |1|↵|#fun| |tmp| |#=| |2|←| //│ Parsed: {fun foo = {fun local = (x,) => {class Foo {fun bar = +(x,)(1,)}; (Foo()).bar}; print(+(local(0,),)(local(1,),),); print(+('(' local(0,) ')',)(local(1,),),); fun tmp = 1; print(local(+(0,)(local(1,),),),); fun tmp = 2}} log(1); log(a) //│ |log|(|1|)|;| |log|(|a|)| //│ Parsed: {log(1,); log(a,)} constructor(){ a = 1 a = 2 } //│ |#constructor|(||)|{|→|a| |#=| |1|↵|a| |#=| |2|←|↵|}| //│ Parsed: {constructor() {a = 1; a = 2}} a = 1; log(a) //│ |a| |#=| |1|;| |log|(|a|)| //│ Parsed: {a = 1; log(a,)} f(a) = 1 //│ |f|(|a|)| |#=| |1| //│ Parsed: {f = (a,) => 1} ================================================ FILE: shared/src/test/diff/parser/Brackets.mls ================================================ () //│ |(||)| //│ Parsed: {undefined} [] //│ |[||]| //│ Parsed: {[]} {} //│ |{||}| //│ Parsed: {'{' {} '}'} :pe (} //│ ╔══[PARSE ERROR] Mistmatched closing curly brace //│ ║ l.15: (} //│ ║ ^ //│ ╟── does not correspond to opening parenthesis //│ ║ l.15: (} //│ ╙── ^ //│ |(||)| //│ Parsed: {undefined} (([{}])) //│ |(|(|[|{||}|]|)|)| //│ Parsed: {'(' '(' ['{' {} '}',] ')' ')'} :pe (([{})]) //│ ╔══[PARSE ERROR] Mistmatched closing parenthesis //│ ║ l.30: (([{})]) //│ ║ ^ //│ ╟── does not correspond to opening square bracket //│ ║ l.30: (([{})]) //│ ╙── ^ //│ ╔══[PARSE ERROR] Mistmatched closing square bracket //│ ║ l.30: (([{})]) //│ ║ ^ //│ ╟── does not correspond to opening parenthesis //│ ║ l.30: (([{})]) //│ ╙── ^ //│ |(|(|[|{||}|]|)|)| //│ Parsed: {'(' '(' ['{' {} '}',] ')' ')'} fun f = () //│ |#fun| |f| |#=| |(||)| //│ Parsed: {fun f = undefined} ================================================ FILE: shared/src/test/diff/parser/Classes.mls ================================================ class Foo //│ |#class| |Foo| //│ Parsed: {class Foo {}} class Foo {} //│ |#class| |Foo| |{||}| //│ Parsed: {class Foo {}} class Foo { fun foo: int } //│ |#class| |Foo| |{|→|#fun| |foo|#:| |int|←|↵|}| //│ Parsed: {class Foo {fun foo: int}} class Foo { class Bar { class Baz } } //│ |#class| |Foo| |{|→|#class| |Bar| |{|→|#class| |Baz|←|↵|}|←|↵|}| //│ Parsed: {class Foo {class Bar {class Baz {}}}} class Foo: Bar //│ |#class| |Foo|#:| |Bar| //│ Parsed: {class Foo: Bar {}} class Foo extends Bar, Baz //│ |#class| |Foo| |#extends| |Bar|,| |Baz| //│ Parsed: {class Foo: Bar, Baz {}} class Foo: Bar, Baz //│ |#class| |Foo|#:| |Bar|,| |Baz| //│ Parsed: {class Foo: ,[Bar, Baz] {}} class Foo: Bar { fun f = 0 } //│ |#class| |Foo|#:| |Bar| |{| |#fun| |f| |#=| |0| |}| //│ Parsed: {class Foo: Bar {fun f = 0}} class Foo extends Bar, Baz { fun f = 0 } //│ |#class| |Foo| |#extends| |Bar|,| |Baz| |{| |#fun| |f| |#=| |0| |}| //│ Parsed: {class Foo: Bar, Baz {fun f = 0}} :pe class Foo: Bar, Baz { fun f = 0 } //│ |#class| |Foo|#:| |Bar|,| |Baz| |{| |#fun| |f| |#=| |0| |}| //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.46: class Foo: Bar, Baz { fun f = 0 } //│ ╙── ^^^^^^^^^^^^^^^^^ //│ Parsed: {class Foo: anything {}} // * Pretty confusing... better reject this: :pe :w class Foo: Bar { fun f = 0 fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{| |#fun| |f| |#=| |0|→|#fun| |bar| |#=| |1|←|↵|}| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.57: fun bar = 1 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected '=' here //│ ║ l.57: fun bar = 1 //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.56: class Foo: Bar { fun f = 0 //│ ║ ^ //│ ║ l.57: fun bar = 1 //│ ╙── ^^^^^^^^^ //│ Parsed: {class Foo: Bar {fun f = 0(bar,)}} // TODO disallow? (i.e., require unindent before closing brace) // :e class Foo: Bar { fun f = 0 fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{|→|#fun| |f| |#=| |0|↵|#fun| |bar| |#=| |1|↵|←|}| //│ Parsed: {class Foo: Bar {fun f = 0; fun bar = 1}} class Foo: Bar { fun f = 0 fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{|→|#fun| |f| |#=| |0|↵|#fun| |bar| |#=| |1|←|↵|}| //│ Parsed: {class Foo: Bar {fun f = 0; fun bar = 1}} class Foo: Bar { fun f = 0 fun bar = 1 } //│ |#class| |Foo|#:| |Bar| |{|→|#fun| |f| |#=| |0|↵|#fun| |bar| |#=| |1|←|↵|}| //│ Parsed: {class Foo: Bar {fun f = 0; fun bar = 1}} class Foo: Bar { } //│ |#class| |Foo|#:| |Bar| |{|↵|}| //│ Parsed: {class Foo: Bar {}} fun foo = class Foo: Bar { } //│ |#fun| |foo| |#=|→|#class| |Foo|#:| |Bar| |{|↵|}|←| //│ Parsed: {fun foo = {class Foo: Bar {}}} class Foo() //│ |#class| |Foo|(||)| //│ Parsed: {class Foo() {}} class Foo(x, y, z) //│ |#class| |Foo|(|x|,| |y|,| |z|)| //│ Parsed: {class Foo(x, y, z,) {}} class Foo(x, y, z): Bar(z, x) //│ |#class| |Foo|(|x|,| |y|,| |z|)|#:| |Bar|(|z|,| |x|)| //│ Parsed: {class Foo(x, y, z,): Bar[z, x] {}} class Foo(x, y, z): Bar(z, x) { fun blah(x) = x + y } //│ |#class| |Foo|(|x|,| |y|,| |z|)|#:| |Bar|(|z|,| |x|)| |{|→|#fun| |blah|(|x|)| |#=| |x| |+| |y|←|↵|}| //│ Parsed: {class Foo(x, y, z,): Bar[z, x] {fun blah = (x,) => +(x,)(y,)}} class Foo(x, y) extends Bar(y, x), Baz(x + y) //│ |#class| |Foo|(|x|,| |y|)| |#extends| |Bar|(|y|,| |x|)|,| |Baz|(|x| |+| |y|)| //│ Parsed: {class Foo(x, y,): Bar(y, x,), Baz(+(x,)(y,),) {}} ================================================ FILE: shared/src/test/diff/parser/Comments.mls ================================================ 1 // whoops //│ |1| |/* whoops*/| //│ Parsed: {1} 2 /* whoops */ //│ |2| |/* whoops */| //│ Parsed: {2} 1 // A 2 //│ |1|↵|/* A*/|↵|2| //│ Parsed: {1; 2} :w 1 // A 2 //│ |1|→|/* A*/|←|↵|2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.17: 1 //│ ╙── ^ //│ Parsed: {1(); 2} ================================================ FILE: shared/src/test/diff/parser/ControversialIfSplits.mls ================================================ :AllowParseErrors if f of 0 then "ok" 1 then "ko" //│ |#if| |f| |#of|→|0| |#then| |"ok"|↵|1| |#then| |"ko"|←| //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.5: 0 then "ok" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.4: if f of //│ ║ ^^^^ //│ ║ l.5: 0 then "ok" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.4: if f of //│ ╙── ^^ //│ Parsed: {if (f(0,)) then undefined} if f ( 0 then "ok" 1 then "ko" ) //│ |#if| |f| |(|→|0| |#then| |"ok"|↵|1| |#then| |"ko"|←|↵|)| //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.22: 0 then "ok" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.21: if f ( //│ ║ ^^^ //│ ║ l.22: 0 then "ok" //│ ║ ^^^^^^^^^^^^^ //│ ║ l.23: 1 then "ko" //│ ║ ^^^^^^^^^^^^^ //│ ║ l.24: ) //│ ║ ^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.21: if f ( //│ ╙── ^^ //│ Parsed: {if (f(0,)) then undefined} if f of 0 then "ok" 1 then "ko" else "?" //│ |#if| |f| |#of|→|0| |#then| |"ok"|↵|1| |#then| |"ko"|↵|#else| |"?"|←| //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.44: 0 then "ok" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.43: if f of //│ ║ ^^^^ //│ ║ l.44: 0 then "ok" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.43: if f of //│ ╙── ^^ //│ Parsed: {if (f(0,)) then undefined} if f of 0 is 0 then "ok" 1 is 1 then "ko" //│ |#if| |f| |#of|→|0| |is| |0| |#then| |"ok"|↵|1| |is| |1| |#then| |"ko"|←| //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.62: 0 is 0 then "ok" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.61: if f of //│ ║ ^^^^ //│ ║ l.62: 0 is 0 then "ok" //│ ║ ^^^^^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.61: if f of //│ ╙── ^^ //│ Parsed: {if (f(is(0,)(0,),)) then undefined} ================================================ FILE: shared/src/test/diff/parser/FatArrows.mls ================================================ :AllowParseErrors fun f: (x: Int) => Int //│ |#fun| |f|#:| |(|x|#:| |Int|)| |#=>| |Int| //│ Parsed: {fun f: (x: Int) -> Int} fun f: (x: Int => Int, y: Int) => Int //│ |#fun| |f|#:| |(|x|#:| |Int| |#=>| |Int|,| |y|#:| |Int|)| |#=>| |Int| //│ Parsed: {fun f: (x: Int -> Int, y: Int) -> Int} fun f: (x: Int, y: Int => Int) => Int //│ |#fun| |f|#:| |(|x|#:| |Int|,| |y|#:| |Int| |#=>| |Int|)| |#=>| |Int| //│ Parsed: {fun f: (x: Int, y: Int -> Int) -> Int} fun f: (x: Int, y: (Int, Int) => Int) => Int //│ |#fun| |f|#:| |(|x|#:| |Int|,| |y|#:| |(|Int|,| |Int|)| |#=>| |Int|)| |#=>| |Int| //│ Parsed: {fun f: (x: Int, y: (Int, Int) -> Int) -> Int} fun f: (x: Int) //│ |#fun| |f|#:| |(|x|#:| |Int|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.20: fun f: (x: Int) //│ ╙── ^^^^^^ //│ Parsed: {fun f: Int} fun f: (x: Int, y: Int) //│ |#fun| |f|#:| |(|x|#:| |Int|,| |y|#:| |Int|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.27: fun f: (x: Int, y: Int) //│ ╙── ^^^^^^ //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.27: fun f: (x: Int, y: Int) //│ ╙── ^^^^^^ //│ Parsed: {fun f: ,[Int, Int]} fun match: forall 'res: (() -> Int, ifCons: Int) => Int //│ |#fun| |match|#:| |#forall| |'res|#:| |(|(||)| |->| |Int|,| |ifCons|#:| |Int|)| |#=>| |Int| //│ Parsed: {fun match: forall 'res. (() -> Int, ifCons: Int) -> Int} fun match: forall 'res: (() => Int, ifCons: Int) => Int //│ |#fun| |match|#:| |#forall| |'res|#:| |(|(||)| |#=>| |Int|,| |ifCons|#:| |Int|)| |#=>| |Int| //│ Parsed: {fun match: forall 'res. (() -> Int, ifCons: Int) -> Int} fun match: forall 'res: (() => Int, Int) => Int //│ |#fun| |match|#:| |#forall| |'res|#:| |(|(||)| |#=>| |Int|,| |Int|)| |#=>| |Int| //│ Parsed: {fun match: forall 'res. (() -> Int, Int) -> Int} f(x => x, a) //│ |f|(|x| |#=>| |x|,| |a|)| //│ Parsed: {f((x,) => x, a,)} f(x => x, y: a) //│ |f|(|x| |#=>| |x|,| |y|#:| |a|)| //│ Parsed: {f((x,) => x, y: a,)} ================================================ FILE: shared/src/test/diff/parser/FlatMultiArgLams.mls ================================================ :NewDefs // Extracted frrom JSON.mls parseNegative(state).flatMap of (negative, state) => parseIntegral(state).flatMap of (integral, state) => parseFraction(state).flatMap of (fraction, state) => parseExponent(state).flatMap of (exponent, state) => let value = (integral +. fraction) *. exponent Success of (if negative then (0 -. value) else value), state //│ |parseNegative|(|state|)|.flatMap| |#of| |(|negative|,| |state|)| |#=>|↵|parseIntegral|(|state|)|.flatMap| |#of| |(|integral|,| |state|)| |#=>|↵|parseFraction|(|state|)|.flatMap| |#of| |(|fraction|,| |state|)| |#=>|↵|parseExponent|(|state|)|.flatMap| |#of| |(|exponent|,| |state|)| |#=>|↵|#let| |value| |#=| |(|integral| |+.| |fraction|)| |*.| |exponent|↵|Success| |#of| |(|#if| |negative| |#then| |(|0| |-.| |value|)| |#else| |value|)|,| |state| //│ Parsed: {(parseNegative(state,)).flatMap((negative, state,) => {(parseIntegral(state,)).flatMap((integral, state,) => {(parseFraction(state,)).flatMap((fraction, state,) => {(parseExponent(state,)).flatMap((exponent, state,) => {let value = *.('(' +.(integral, fraction,) ')', exponent,); Success('(' if (negative) then '(' -.(0, value,) ')' else value ')', state,)},)},)},)},)} //│ ================================================ FILE: shared/src/test/diff/parser/Forall.mls ================================================ forall 'a: 'a => 'a //│ |#forall| |'a|#:| |'a| |#=>| |'a| //│ Parsed: {forall 'a. ('a,) => 'a} forall 'a, 'b: ['a, 'b] => ['b, 'a] //│ |#forall| |'a|,| |'b|#:| |[|'a|,| |'b|]| |#=>| |[|'b|,| |'a|]| //│ Parsed: {forall 'a, 'b. (['a, 'b,],) => ['b, 'a,]} fun f: forall 'a: 'a => 'a //│ |#fun| |f|#:| |#forall| |'a|#:| |'a| |#=>| |'a| //│ Parsed: {fun f: forall 'a. 'a -> 'a} fun f: forall 'a, 'b: ['a, 'b] => ['b, 'a] //│ |#fun| |f|#:| |#forall| |'a|,| |'b|#:| |[|'a|,| |'b|]| |#=>| |[|'b|,| |'a|]| //│ Parsed: {fun f: forall 'a 'b. (['a, 'b]) -> ['b, 'a]} ================================================ FILE: shared/src/test/diff/parser/Fun.mls ================================================ :AllowParseErrors fun f = 1 //│ |#fun| |f| |#=| |1| //│ Parsed: {fun f = 1} fun f(x) = x //│ |#fun| |f|(|x|)| |#=| |x| //│ Parsed: {fun f = (x,) => x} fun f x = x //│ |#fun| |f| |x| |#=| |x| //│ ╔══[PARSE ERROR] Expected function parameter list; found identifier instead //│ ║ l.12: fun f x = x //│ ╙── ^ //│ Parsed: {fun f = x} fun f = x => x //│ |#fun| |f| |#=| |x| |#=>| |x| //│ Parsed: {fun f = (x,) => x} // TODO fun x => x //│ |#fun| |x| |#=>| |x| //│ ╔══[PARSE ERROR] Expected function parameter list; found '=>' instead //│ ║ l.24: fun x => x //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead //│ ║ l.24: fun x => x //│ ╙── ^ //│ Parsed: {fun x = undefined} let f = x => x //│ |#let| |f| |#=| |x| |#=>| |x| //│ Parsed: {let f = (x,) => x} // TODO let f = fun x => x //│ |#let| |f| |#=| |#fun| |x| |#=>| |x| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.39: let f = fun x => x //│ ╙── ^^^ //│ Parsed: {let f = (x,) => x} f(x) + x //│ |f|(|x|)| |+| |x| //│ Parsed: {+(f(x,),)(x,)} fun f(x, y) = x //│ |#fun| |f|(|x|,| |y|)| |#=| |x| //│ Parsed: {fun f = (x, y,) => x} fun f of x = x //│ |#fun| |f| |#of| |x| |#=| |x| //│ Parsed: {fun f = (x,) => x} fun f of x, y = x //│ |#fun| |f| |#of| |x|,| |y| |#=| |x| //│ Parsed: {fun f = (x, y,) => x} fun f x, y = x //│ |#fun| |f| |x|,| |y| |#=| |x| //│ ╔══[PARSE ERROR] Expected function parameter list; found identifier instead //│ ║ l.64: fun f x, y = x //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found comma instead //│ ║ l.64: fun f x, y = x //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead //│ ║ l.64: fun f x, y = x //│ ╙── ^ //│ Parsed: {fun f = undefined} fun f x y = x //│ |#fun| |f| |x| |y| |#=| |x| //│ ╔══[PARSE ERROR] Expected function parameter list; found identifier instead //│ ║ l.77: fun f x y = x //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead //│ ║ l.77: fun f x y = x //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.77: fun f x y = x //│ ╙── ^ //│ Parsed: {fun f = undefined} fun f(x, y)(a, b) = x + y * a / b //│ |#fun| |f|(|x|,| |y|)|(|a|,| |b|)| |#=| |x| |+| |y| |*| |a| |/| |b| //│ Parsed: {fun f = (x, y,) => (a, b,) => /(+(x,)(*(y,)(a,),),)(b,)} fun f(x, y) of a, b = x + y * a / b //│ |#fun| |f|(|x|,| |y|)| |#of| |a|,| |b| |#=| |x| |+| |y| |*| |a| |/| |b| //│ Parsed: {fun f = (x, y,) => (a, b,) => /(+(x,)(*(y,)(a,),),)(b,)} fun f of x, y of a, b = x + y * a / b //│ |#fun| |f| |#of| |x|,| |y| |#of| |a|,| |b| |#=| |x| |+| |y| |*| |a| |/| |b| //│ Parsed: {fun f = (x, y(a, b,),) => /(+(x,)(*(y,)(a,),),)(b,)} fun f(Some(x)) = x //│ |#fun| |f|(|Some|(|x|)|)| |#=| |x| //│ Parsed: {fun f = (Some(x,),) => x} fun f(Some of x) = x //│ |#fun| |f|(|Some| |#of| |x|)| |#=| |x| //│ Parsed: {fun f = (Some(x,),) => x} fun f of Some of x = x //│ |#fun| |f| |#of| |Some| |#of| |x| |#=| |x| //│ Parsed: {fun f = (Some(x,),) => x} fun f(Some(x), Some(y)) = x //│ |#fun| |f|(|Some|(|x|)|,| |Some|(|y|)|)| |#=| |x| //│ Parsed: {fun f = (Some(x,), Some(y,),) => x} fun f(Some of x, Some of y) = x + y //│ |#fun| |f|(|Some| |#of| |x|,| |Some| |#of| |y|)| |#=| |x| |+| |y| //│ Parsed: {fun f = (Some(x, Some(y,),),) => +(x,)(y,)} fun f of Some of x, Some of y = x + y //│ |#fun| |f| |#of| |Some| |#of| |x|,| |Some| |#of| |y| |#=| |x| |+| |y| //│ Parsed: {fun f = (Some(x, Some(y,),),) => +(x,)(y,)} fun f(x: Int, y: Int) = x //│ |#fun| |f|(|x|#:| |Int|,| |y|#:| |Int|)| |#=| |x| //│ Parsed: {fun f = (x: Int, y: Int,) => x} fun f(x: Int, y: Int): Int = x //│ |#fun| |f|(|x|#:| |Int|,| |y|#:| |Int|)|#:| |Int| |#=| |x| //│ Parsed: {fun f = (x: Int, y: Int,) => x : Int} fun 1 //│ |#fun| |1| //│ ╔══[PARSE ERROR] Expected a function name; found literal instead //│ ║ l.139: fun 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found end of input instead //│ ║ l.139: fun 1 //│ ╙── ^ //│ Parsed: {fun = undefined} fun 1 = 1 //│ |#fun| |1| |#=| |1| //│ ╔══[PARSE ERROR] Expected a function name; found literal instead //│ ║ l.149: fun 1 = 1 //│ ╙── ^ //│ Parsed: {fun = 1} fun //│ |#fun| //│ ╔══[PARSE ERROR] Expected a function name; found end of input instead //│ ║ l.156: fun //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found end of input instead //│ ║ l.156: fun //│ ╙── ^ //│ Parsed: {fun = undefined} fun = 1 //│ |#fun| |#=| |1| //│ ╔══[PARSE ERROR] Expected a function name; found '=' instead //│ ║ l.166: fun = 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected function parameter list; found literal instead //│ ║ l.166: fun = 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found end of input instead //│ ║ l.166: fun = 1 //│ ╙── ^ //│ Parsed: {fun = undefined} fun add(x: number, y: number): number //│ |#fun| |add|(|x|#:| |number|,| |y|#:| |number|)|#:| |number| //│ Parsed: {fun add: (x: number, y: number) -> number} fun apply(x: int, f: int => int): int //│ |#fun| |apply|(|x|#:| |int|,| |f|#:| |int| |#=>| |int|)|#:| |int| //│ Parsed: {fun apply: (x: int, f: int -> int) -> int} fun apply2(x: number, f: int => number => number): number //│ |#fun| |apply2|(|x|#:| |number|,| |f|#:| |int| |#=>| |number| |#=>| |number|)|#:| |number| //│ Parsed: {fun apply2: (x: number, f: int -> number -> number) -> number} fun apply3(x: number, f: (int => number) => number): number //│ |#fun| |apply3|(|x|#:| |number|,| |f|#:| |(|int| |#=>| |number|)| |#=>| |number|)|#:| |number| //│ Parsed: {fun apply3: (x: number, f: (int -> number) -> number) -> number} ================================================ FILE: shared/src/test/diff/parser/IfThenElse.mls ================================================ if true then 1 else 2 //│ |#if| |true| |#then| |1| |#else| |2| //│ Parsed: {if (true) then 1 else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|↵|#then| |"false"| |#else| |2| //│ Parsed: {if (==(a,)(0,)) then "false" else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|↵|#then| |"false"|↵|#else| |2| //│ Parsed: {if (==(a,)(0,)) then "false" else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|→|#then| |"false"|↵|#else| |2|←| //│ Parsed: {if (==(a,)(0,)) then "false" else 2} if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|↵|#then| |"false"|→|#else| |2|←| //│ Parsed: {if (==(a,)(0,)) then "false" else 2} :pe if a == 0 then "false" else 2 //│ |#if| |a| |==| |0|→|#then| |"false"|←|↵|#else| |2| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.33: else 2 //│ ╙── ^^^^^^ //│ Parsed: {if (==(a,)(0,)) then "false"; undefined} if a == 0 then "false" print of "ok!" //│ |#if| |a| |==| |0|→|#then| |"false"|←|↵|print| |#of| |"ok!"| //│ Parsed: {if (==(a,)(0,)) then "false"; print("ok!",)} if a == 0 then "false" else 2 print of "ok!" //│ |#if| |a| |==| |0| |#then| |"false"|→|#else| |2|←|↵|print| |#of| |"ok!"| //│ Parsed: {if (==(a,)(0,)) then "false" else 2; print("ok!",)} if true then "true" false then "false" //│ |#if|→|true| |#then| |"true"|↵|false| |#then| |"false"|←| //│ Parsed: {if ‹(true) then "true"; (false) then "false"›} if a == 1 then "true" b == 2 then "false" //│ |#if|→|a| |==| |1| |#then| |"true"|↵|b| |==| |2| |#then| |"false"|←| //│ Parsed: {if ‹(==(a,)(1,)) then "true"; (==(b,)(2,)) then "false"›} if a == 0 then "false" a == 1 then "true" //│ |#if|→|a| |==| |0| |#then| |"false"|↵|a| |==| |1| |#then| |"true"|←| //│ Parsed: {if ‹(==(a,)(0,)) then "false"; (==(a,)(1,)) then "true"›} if a == 0 then "false" 1 then "true" //│ |#if| |a| |==|→|0| |#then| |"false"|↵|1| |#then| |"true"|←| //│ Parsed: {if a == ‹(0) then "false"; (1) then "true"›} if a == 0 then "false" _ then "true" //│ |#if| |a| |==|→|0| |#then| |"false"|↵|_| |#then| |"true"|←| //│ Parsed: {if a == ‹(0) then "false"; (_) then "true"›} if a == 0 then "false" 1 then "true" else "true" //│ |#if| |a| |==|→|0| |#then| |"false"|↵|1| |#then| |"true"|↵|#else| |"true"|←| //│ Parsed: {if a == ‹(0) then "false"; (1) then "true"; else "true"›} if a == 0 then "false" 1 then "true" else "true" //│ |#if| |a| |==|→|0| |#then| |"false"|↵|1| |#then| |"true"|←|↵|#else| |"true"| //│ Parsed: {if a == ‹(0) then "false"; (1) then "true"› else "true"} if a == 0 then "false" 1 then "true" else "true" //│ |#if| |a| |==|→|0| |#then| |"false"|↵|1| |#then| |"true"|←|→|#else| |"true"|←| //│ Parsed: {if a == ‹(0) then "false"; (1) then "true"› else "true"} // TODO? :pe if a == 0 then "false" else "true" //│ |#if| |a| |==|→|0| |#then| |"false"| |#else| |"true"|←| //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.106: 0 then "false" else "true" //│ ╙── ^^^^ //│ Parsed: {if a == ‹(0) then "false"›} if a == 0 then "false" else "true" //│ |#if| |a| |==|→|0| |#then| |"false"|↵|#else| |"true"|←| //│ Parsed: {if a == ‹(0) then "false"; else "true"›} if a > 0 then "false" == 1 then "true" //│ |#if| |a|→|>| |0| |#then| |"false"|↵|==| |1| |#then| |"true"|←| //│ Parsed: {if a ‹· > (0) then "false"; · == (1) then "true"›} // TODO if a > 0 then "false" == 1 then "true" _ then "true" //│ |#if| |a|→|>| |0| |#then| |"false"|↵|==| |1| |#then| |"true"|↵|_| |#then| |"true"|←| //│ Parsed: {if ‹a ‹· > (0) then "false"; · == (1) then "true"›; else "true"›} if a == 0 then "false" 1 then "true" else 2 //│ |#if| |a| |==|→|0| |#then| |"false"|↵|1| |#then| |"true"|←|↵|#else| |2| //│ Parsed: {if a == ‹(0) then "false"; (1) then "true"› else 2} if a == 0 and b == 2 then "false" 3 then "oops" 1 then "true" //│ |#if| |a| |==|→|0| |and| |b| |==|→|2| |#then| |"false"|↵|3| |#then| |"oops"|←|↵|1| |#then| |"true"|←| //│ Parsed: {if a == ‹0 and b == ‹(2) then "false"; (3) then "oops"›; (1) then "true"›} if a is Some(x) then "defined" None then "undefined" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|None| |#then| |"undefined"|←| //│ Parsed: {if a is ‹(Some(x,)) then "defined"; (None) then "undefined"›} if a is Some(x) and x is Left(a) then "left-defined" Right(b) then "right-defined" None then "undefined" //│ |#if| |a| |is|→|Some|(|x|)| |and| |x| |is|→|Left|(|a|)| |#then| |"left-defined"|↵|Right|(|b|)| |#then| |"right-defined"|←|↵|None| |#then| |"undefined"|←| //│ Parsed: {if a is ‹Some(x,) and x is ‹(Left(a,)) then "left-defined"; (Right(b,)) then "right-defined"›; (None) then "undefined"›} if a is Some of x then "defined" None then "undefined" //│ |#if| |a| |is|→|Some| |#of| |x| |#then| |"defined"|↵|None| |#then| |"undefined"|←| //│ Parsed: {if a is ‹(Some(x,)) then "defined"; (None) then "undefined"›} if a is Some(x) then "defined" _ then "unknown" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|_| |#then| |"unknown"|←| //│ Parsed: {if a is ‹(Some(x,)) then "defined"; (_) then "unknown"›} if a is Some(x) then "defined" else "unknown" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|#else| |"unknown"|←| //│ Parsed: {if a is ‹(Some(x,)) then "defined"; else "unknown"›} if a is Some(x) then "defined" else "unknown" else "unreachable?!" //│ |#if| |a| |is|→|Some|(|x|)| |#then| |"defined"|↵|#else| |"unknown"|↵|#else| |"unreachable?!"|←| //│ Parsed: {if a is ‹(Some(x,)) then "defined"; else "unknown"; else "unreachable?!"›} a == 1 and b == 2 //│ |a| |==| |1| |and| |b| |==| |2| //│ Parsed: {and(==(a,)(1,),)(==(b,)(2,),)} :pe if lol //│ |#if| |lol| //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found reference instead //│ ║ l.193: if lol //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.193: if lol //│ ╙── ^^ //│ Parsed: {if (lol) then undefined} :pe if lol then //│ |#if| |lol| |#then| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.204: if lol then //│ ╙── ^ //│ Parsed: {if (lol) then undefined} :pe if lol then else //│ |#if| |lol| |#then| |#else| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.212: if lol then else //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.212: if lol then else //│ ╙── ^^^^ //│ Parsed: {if (lol) then undefined} :pe a then b //│ |a| |#then| |b| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.223: a then b //│ ╙── ^^^^^^^^ //│ Parsed: {undefined} :pe if lol then else //│ |#if| |lol|↵|#then|↵|#else| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.233: else //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.233: else //│ ╙── ^^^^ //│ Parsed: {if (lol) then {undefined}} :pe if lol else 2 //│ |#if| |lol| |#else| |2| //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found reference followed by 'else' keyword instead //│ ║ l.244: if lol else 2 //│ ║ ^^^^^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.244: if lol else 2 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead //│ ║ l.244: if lol else 2 //│ ╙── ^^^^ //│ Parsed: {if (lol) then undefined} x = if true then false else maybe //│ |x| |#=| |#if| |true| |#then| |false| |#else| |maybe| //│ Parsed: {x = if (true) then false else maybe} :pe x = if true then y = 1 //│ |x| |#=|→|#if| |true| |#then|←|↵|y| |#=| |1| //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.264: if true then //│ ╙── ^ //│ Parsed: {x = {if (true) then undefined}; y = 1} if a == 0 and b == 2 then "false" else "1" 1 then "true" else "2" //│ |#if| |a| |==|→|0| |and| |b| |==|→|2| |#then| |"false"|↵|#else| |"1"|←|↵|1| |#then| |"true"|↵|#else| |"2"|←| //│ Parsed: {if a == ‹0 and b == ‹(2) then "false"; else "1"›; (1) then "true"; else "2"›} :pe 2 and b then "" //│ |2| |and| |b| |#then| |""| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.282: 2 and b then "" //│ ╙── ^^^^^^^^^^^^^^^ //│ Parsed: {undefined} 2 and b == 0 //│ |2| |and| |b| |==| |0| //│ Parsed: {and(2,)(==(b,)(0,),)} if x == 2 and b then "" //│ |#if| |x| |==|→|2| |and| |b| |#then| |""|←| //│ Parsed: {if x == ‹(and(2,)(b,)) then ""›} if x == 2 and b == 1 then "" //│ |#if| |x| |==|→|2| |and| |b| |==| |1| |#then| |""|←| //│ Parsed: {if x == ‹(and(2,)(==(b,)(1,),)) then ""›} if x == 0 then "x" y == 1 then "y = 1" 2 and z == 0 then "z = 0" 3 then "y = 3" //│ |#if|→|x| |==| |0| |#then| |"x"|↵|y| |==|→|1| |#then| |"y = 1"|↵|2| |and| |z| |==| |0| |#then| |"z = 0"|↵|3| |#then| |"y = 3"|←|←| //│ Parsed: {if ‹(==(x,)(0,)) then "x"; y == ‹(1) then "y = 1"; (and(2,)(==(z,)(0,),)) then "z = 0"; (3) then "y = 3"››} :pe else 1 //│ |#else| |1| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.313: else 1 //│ ╙── ^^^^^^ //│ Parsed: {undefined} :pe 1 else 2 //│ |1| |#else| |2| //│ ╔══[PARSE ERROR] Expected end of input; found 'else' keyword instead //│ ║ l.321: 1 else 2 //│ ╙── ^^^^ //│ Parsed: {1} if a then if b then c else d //│ |#if| |a| |#then| |#if| |b| |#then| |c| |#else| |d| //│ Parsed: {if (a) then if (b) then c else d} if a then if b then c else d else e //│ |#if| |a| |#then| |#if| |b| |#then| |c| |#else| |d| |#else| |e| //│ Parsed: {if (a) then if (b) then c else d else e} if a is Left(x) then x let y = a + 1 Right(0) then y //│ |#if| |a| |is|→|Left|(|x|)| |#then| |x|↵|#let| |y| |#=| |a| |+| |1|↵|Right|(|0|)| |#then| |y|←| //│ Parsed: {if a is ‹(Left(x,)) then x; let y = +(a,)(1,); (Right(0,)) then y›} if a is Some(v) and v is Left(x) then x let y = v + 1 Right(0) then y Right(x) then x + y else 0 //│ |#if| |a| |is|→|Some|(|v|)| |and| |v| |is|→|Left|(|x|)| |#then| |x|↵|#let| |y| |#=| |v| |+| |1|↵|Right|(|0|)| |#then| |y|↵|Right|(|x|)| |#then| |x| |+| |y|←|↵|#else| |0|←| //│ Parsed: {if a is ‹Some(v,) and v is ‹(Left(x,)) then x; let y = +(v,)(1,); (Right(0,)) then y; (Right(x,)) then +(x,)(y,)›; else 0›} if a is Some(x) and x is Left(a) then "left-defined" let y = x + 1 Right(b) then "right-defined" None then "undefined" //│ |#if| |a| |is|→|Some|(|x|)| |and| |x| |is|→|Left|(|a|)| |#then| |"left-defined"|↵|#let| |y| |#=| |x| |+| |1|↵|Right|(|b|)| |#then| |"right-defined"|←|↵|None| |#then| |"undefined"|←| //│ Parsed: {if a is ‹Some(x,) and x is ‹(Left(a,)) then "left-defined"; let y = +(x,)(1,); (Right(b,)) then "right-defined"›; (None) then "undefined"›} :w if a is Left x then x //│ |#if| |a| |is|→|Left| |x| |#then| |x|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.366: Left x then x //│ ╙── ^^^^^^ //│ Parsed: {if a is ‹(Left(x,)) then x›} // TODO if a is Left(x) then x let y = a + 1 then y //│ |#if| |a| |is|→|Left|(|x|)| |#then| |x|↵|#let| |y| |#=| |a| |+| |1|↵|#then| |y|←| //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.376: let y = a + 1 //│ ║ ^^^^^ //│ ║ l.377: then y //│ ╙── ^^^^^^^^ //│ Parsed: {if a is ‹(Left(x,)) then x; let y = undefined›} // --- // COMPARISONS // --- // // Rust/Swift: :pe if let Some(x) = v then 123 //│ |#if| |#let| |Some|(|x|)| |#=| |v| |#then| |123| //│ ╔══[PARSE ERROR] Expected '='; found parenthesis section instead //│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected '=' in expression position //│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found let binding instead //│ ║ l.392: if let Some(x) = v then 123 //│ ║ ^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.392: if let Some(x) = v then 123 //│ ╙── ^^ //│ Parsed: {if (let Some = undefined in undefined) then undefined} // Swift: :pe if let Some(x) = v and cond then 123 //│ |#if| |#let| |Some|(|x|)| |#=| |v| |and| |cond| |#then| |123| //│ ╔══[PARSE ERROR] Expected '='; found parenthesis section instead //│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected '=' in expression position //│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found let binding instead //│ ║ l.413: if let Some(x) = v and cond then 123 //│ ║ ^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.413: if let Some(x) = v and cond then 123 //│ ╙── ^^ //│ Parsed: {if (let Some = undefined in undefined) then undefined} // MLscript: if v is Some(x) and x is Left(y) then 123 //│ |#if| |v| |is| |Some|(|x|)| |and| |x| |is| |Left|(|y|)| |#then| |123| //│ Parsed: {if (and(is(v,)(Some(x,),),)(is(x,)(Left(y,),),)) then 123} // ML: let Some(x) = v //│ |#let| |Some|(|x|)| |#=| |v| //│ Parsed: {let Some = (x,) => v} v as Some(x) //│ |v| |as| |Some|(|x|)| //│ Parsed: {as(v,)(Some(x,),)} :pe if true then 0 + 1 //│ |#if| |true|→|#then| |0|↵|+| |1|←| //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.452: + 1 //│ ╙── ^ //│ Parsed: {if (true) then 0} if true then 0 + 1 //│ |#if| |true|→|#then| |0|→|+| |1|←|←| //│ Parsed: {if (true) then +(0, 1,)} if true then 0 else 0 //│ |#if| |true|→|#then| |0|↵|#else| |0|←| //│ Parsed: {if (true) then 0 else 0} if true then 0 else 0 + 1 //│ |#if| |true|→|#then| |0|↵|#else| |0|→|+| |1|←|←| //│ Parsed: {if (true) then 0 else +(0, 1,)} :pe if true then 0 + 1 //│ |#if| |true|→|#then| |0|←|→|+| |1|←| //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead //│ ║ l.481: + 1 //│ ╙── ^^ //│ Parsed: {if (true) then 0} :pe if true then 0 else 0 + 1 //│ |#if| |true|→|#then| |0|↵|#else| |0|←|→|+| |1|←| //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead //│ ║ l.492: + 1 //│ ╙── ^^ //│ Parsed: {if (true) then 0 else 0} if true then 0 + 1 //│ |#if| |true| |#then| |0|→|+| |1|←| //│ Parsed: {if (true) then +(0, 1,)} if true then 0 else 0 + 1 //│ |#if| |true| |#then| |0| |#else| |0|→|+| |1|←| //│ Parsed: {if (true) then 0 else +(0, 1,)} // TODO deal with meaningless whitespace: if true then 0 //│ |#if| |true|→|#then| |0|↵|←| //│ Parsed: {if (true) then 0} // TODO ignore empty indented blocks/lines if true then 0 //│ |#if| |true|→|#then| |0|←| //│ Parsed: {if (true) then 0} :pe (if true) //│ |(|#if| |true|)| //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found reference instead //│ ║ l.527: (if true) //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.527: (if true) //│ ╙── ^^ //│ Parsed: {'(' if (true) then undefined ')'} :pe (if true then) //│ |(|#if| |true| |#then|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.538: (if true then) //│ ╙── ^ //│ Parsed: {'(' if (true) then undefined ')'} if true then; //│ |#if| |true| |#then|;| //│ Parsed: {if (true) then undefined} if true then; //│ |#if| |true| |#then|;| //│ Parsed: {if (true) then undefined} :pe if true then; else; //│ |#if| |true| |#then|;| |#else|;| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.554: if true then; else; //│ ╙── ^^^^^ //│ Parsed: {if (true) then undefined; undefined} if true then () else; //│ |#if| |true| |#then| |(||)| |#else|;| //│ Parsed: {if (true) then undefined else undefined} ================================================ FILE: shared/src/test/diff/parser/Lambdas.mls ================================================ :AllowParseErrors x => x //│ |x| |#=>| |x| //│ Parsed: {(x,) => x} (x) => x //│ |(|x|)| |#=>| |x| //│ Parsed: {('(' x ')',) => x} // TODO fun x => x //│ |#fun| |x| |#=>| |x| //│ ╔══[PARSE ERROR] Expected function parameter list; found '=>' instead //│ ║ l.13: fun x => x //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead //│ ║ l.13: fun x => x //│ ╙── ^ //│ Parsed: {fun x = undefined} // TODO let f = fun x => x //│ |#let| |f| |#=| |#fun| |x| |#=>| |x| //│ ╔══[PARSE ERROR] Unexpected 'fun' keyword in expression position //│ ║ l.24: let f = fun x => x //│ ╙── ^^^ //│ Parsed: {let f = (x,) => x} fun f x = x //│ |#fun| |f| |x| |#=| |x| //│ ╔══[PARSE ERROR] Expected function parameter list; found identifier instead //│ ║ l.32: fun f x = x //│ ╙── ^ //│ Parsed: {fun f = x} (x, y) => x //│ |(|x|,| |y|)| |#=>| |x| //│ Parsed: {(x, y,) => x} => 1 //│ |#=>| |1| //│ ╔══[PARSE ERROR] Unexpected '=>' in expression position //│ ║ l.45: => 1 //│ ╙── ^^ //│ Parsed: {1} x => //│ |x| |#=>| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.52: x => //│ ╙── ^ //│ Parsed: {(x,) => undefined} (x =>) //│ |(|x| |#=>|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.59: (x =>) //│ ╙── ^ //│ Parsed: {'(' (x,) => undefined ')'} a --> b --> c //│ |a| |-->| |b| |-->| |c| //│ Parsed: {-->(a,)(-->(b,)(c,),)} a => b => c //│ |a| |#=>| |b| |#=>| |c| //│ Parsed: {(a,) => (b,) => c} (a => b) => c //│ |(|a| |#=>| |b|)| |#=>| |c| //│ Parsed: {('(' (a,) => b ')',) => c} a => (b => c) //│ |a| |#=>| |(|b| |#=>| |c|)| //│ Parsed: {(a,) => '(' (b,) => c ')'} xs.forall(x => x > 0) //│ |xs|.forall|(|x| |#=>| |x| |>| |0|)| //│ Parsed: {(xs).forall((x,) => >(x,)(0,),)} xs.forall of x => x > 0 //│ |xs|.forall| |#of| |x| |#=>| |x| |>| |0| //│ Parsed: {(xs).forall((x,) => >(x,)(0,),)} :pe a => b then c //│ |a| |#=>| |b| |#then| |c| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.91: a => b then c //│ ╙── ^^^^^^^^^^^^^ //│ Parsed: {undefined} if a => b then c //│ |#if| |a| |#=>| |b| |#then| |c| //│ Parsed: {if ((a,) => b) then c} if xs.forall(a => b) then c //│ |#if| |xs|.forall|(|a| |#=>| |b|)| |#then| |c| //│ Parsed: {if ((xs).forall((a,) => b,)) then c} if xs.forall of a => b then c //│ |#if| |xs|.forall| |#of| |a| |#=>| |b| |#then| |c| //│ Parsed: {if ((xs).forall((a,) => b,)) then c} id + of x => x + 1 //│ |id| |+| |#of| |x| |#=>| |x| |+| |1| //│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position //│ ║ l.111: id + of x => x + 1 //│ ╙── ^^ //│ Parsed: {+(id,)((x,) => +(x,)(1,),)} ================================================ FILE: shared/src/test/diff/parser/Lets.mls ================================================ :AllowParseErrors let x = 1 //│ |#let| |x| |#=| |1| //│ Parsed: {let x = 1} let x = 1, y = 2 //│ |#let| |x| |#=| |1|,| |y| |#=| |2| //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.8: let x = 1, y = 2 //│ ╙── ^ //│ Parsed: {let x = ,(1, y,)} let x = 1, y = 2 x + y //│ |#let| |x| |#=| |1|,| |y| |#=| |2|↵|x| |+| |y| //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.15: let x = 1, y = 2 //│ ╙── ^ //│ Parsed: {let x = ,(1, y,)} let x = 1 in x + 1 //│ |#let| |x| |#=| |1| |#in| |x| |+| |1| //│ Parsed: {let x = 1 in +(x,)(1,)} let x = 1, y = 2 in x + y //│ |#let| |x| |#=| |1|,| |y| |#=| |2| |#in| |x| |+| |y| //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.27: let x = 1, y = 2 in x + y //│ ╙── ^ //│ Parsed: {let x = ,(1, y,)} let in 123 //│ |#let| |#in| |123| //│ ╔══[PARSE ERROR] Expected a function name; found 'in' keyword instead //│ ║ l.34: let in 123 //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected function parameter list; found literal instead //│ ║ l.34: let in 123 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found end of input instead //│ ║ l.34: let in 123 //│ ╙── ^ //│ Parsed: {let = undefined} let x = 1; x + 1 //│ |#let| |x| |#=| |1|;| |x| |+| |1| //│ Parsed: {let x = 1; +(x,)(1,)} let x = 1, y = 2; x + y //│ |#let| |x| |#=| |1|,| |y| |#=| |2|;| |x| |+| |y| //│ ╔══[PARSE ERROR] Expected end of input; found '=' instead //│ ║ l.51: let x = 1, y = 2; x + y //│ ╙── ^ //│ Parsed: {let x = ,(1, y,)} let in person be the default in following meetings //│ |#let| |#in| |person| |be| |the| |default| |#in| |following| |meetings| //│ ╔══[PARSE ERROR] Expected a function name; found 'in' keyword instead //│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected function parameter list; found identifier instead //│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^^^^^ //│ ╔══[PARSE ERROR] Expected ':' or '=' followed by a function body or signature; found identifier instead //│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found identifier instead //│ ║ l.61: let in person be the default in following meetings //│ ╙── ^^^ //│ Parsed: {let = undefined} ================================================ FILE: shared/src/test/diff/parser/Misc.mls ================================================ :AllowParseErrors fun discard(x) = () //│ |#fun| |discard|(|x|)| |#=| |(||)| //│ Parsed: {fun discard = (x,) => undefined} // FIXME parses wrong: foo of discard of if f of x is Some(v) then v + 1 None then 0 if g of y is Some(v) then v + 1 None then 0, //│ |foo| |#of|→|discard| |#of| |#if| |f| |#of| |x| |is|→|Some|(|v|)| |#then| |v| |+| |1|↵|None| |#then| |0|←|↵|#if| |g| |#of| |y| |is|→|Some|(|v|)| |#then| |v| |+| |1|↵|None| |#then| |0|,| |←|←| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.10: discard of if f of x is //│ ║ ^^^^ //│ ║ l.11: Some(v) then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.12: None then 0 //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application followed by newline instead //│ ║ l.10: discard of if f of x is //│ ║ ^^^^^^^^^ //│ ║ l.11: Some(v) then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.12: None then 0 //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.13: if g of y is //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.14: Some(v) then v + 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.10: discard of if f of x is //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.15: None then 0, //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.13: if g of y is //│ ║ ^^^^ //│ ║ l.14: Some(v) then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.15: None then 0, //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.13: if g of y is //│ ║ ^^^^^^^^^ //│ ║ l.14: Some(v) then v + 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.13: if g of y is //│ ╙── ^^ //│ Parsed: {foo({discard(if (f(undefined,)) then undefined,); if (g(undefined,)) then undefined},)} foo of if f of x is Some v then v + 1 None then 0, if g of y is Some v then v + 1 None then 0, //│ |foo| |#of|→|#if| |f| |#of| |x| |is|→|Some| |v| |#then| |v| |+| |1|↵|None| |#then| |0|,|←|↵|#if| |g| |#of| |y| |is|→|Some| |v| |#then| |v| |+| |1|↵|None| |#then| |0|,|←|↵|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.60: Some v then v + 1 //│ ╙── ^^^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.61: None then 0, //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.59: if f of x is //│ ║ ^^^^ //│ ║ l.60: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.61: None then 0, //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application followed by newline instead //│ ║ l.59: if f of x is //│ ║ ^^^^^^^^^ //│ ║ l.60: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.61: None then 0, //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.62: if g of y is //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.63: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.64: None then 0, //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.65: //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.59: if f of x is //│ ╙── ^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.63: Some v then v + 1 //│ ╙── ^^^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.64: None then 0, //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.62: if g of y is //│ ║ ^^^^ //│ ║ l.63: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.64: None then 0, //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application followed by newline instead //│ ║ l.62: if g of y is //│ ║ ^^^^^^^^^ //│ ║ l.63: Some v then v + 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.64: None then 0, //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.65: //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.62: if g of y is //│ ╙── ^^ //│ Parsed: {foo({if (f(undefined,)) then undefined; if (g(undefined,)) then undefined},)} print of Foo(x) is Some //│ |print| |#of| |Foo|(|x|)| |is| |Some| //│ Parsed: {print(is(Foo(x,),)(Some,),)} // FIXME parses wrong: if f of x is Some then 1 else 0 //│ |#if| |f| |#of| |x| |is| |Some| |#then| |1| |#else| |0| //│ Parsed: {if (f(is(x,)(Some,),)) then 1 else 0} if f of 0 and g of 1 then "ok" //│ |#if| |f| |#of| |0| |and| |g| |#of| |1| |#then| |"ok"| //│ Parsed: {if (f(and(0,)(g(1,),),)) then "ok"} A and B or C and D //│ |A| |and| |B| |or| |C| |and| |D| //│ Parsed: {or(and(A,)(B,),)(and(C,)(D,),)} ================================================ FILE: shared/src/test/diff/parser/MultiLineCalls.mls ================================================ :AllowParseErrors f ( 0 ) //│ |f| |(|→|0|←|↵|)| //│ Parsed: {f(0,)} f ( 0, 1 ) //│ |f| |(|→|0|,|↵|1|←|↵|)| //│ Parsed: {f(0, 1,)} f ( 0, 1, ) //│ |f| |(|→|0|,|↵|1|,|←|↵|)| //│ Parsed: {f(0, 1,)} f (0, 1) //│ |f| |(|0|,| |1|)| //│ Parsed: {f(0, 1,)} f (0, 1,) //│ |f| |(|0|,| |1|,|)| //│ Parsed: {f(0, 1,)} f (0, 1, ) //│ |f| |(|0|,| |1|,|↵|)| //│ Parsed: {f(0, 1,)} f(,) //│ |f|(|,|)| //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.37: f(,) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.37: f(,) //│ ╙── ^ //│ Parsed: {f(undefined,)} f ( 0 1 ) //│ |f| |(|→|0|↵|1|←|↵|)| //│ Parsed: {f({0; 1},)} f of 0, 1 //│ |f| |#of|→|0|,|↵|1|←| //│ Parsed: {f(0, 1,)} f of 0 1 //│ |f| |#of|→|0|↵|1|←| //│ Parsed: {f({0; 1},)} // :pe f of 0 //│ |f|↵|#of| |0| //│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position //│ ║ l.68: of 0 //│ ╙── ^^ //│ Parsed: {f; 0} f (0) //│ |f|→|(|0|)|←| //│ Parsed: {f(0,)} // TODO f of 0 of 1 //│ |f|→|#of| |0|↵|#of| |1|←| //│ Parsed: {f(0,)(1,)} f (0) (1) //│ |f|→|(|0|)|↵|(|1|)|←| //│ Parsed: {f(0,)(1,)} f of 0, 1 //│ |f|→|#of|→|0|,|↵|1|←|←| //│ Parsed: {f(0, 1,)} 2 + 2 (0) //│ |2| |+| |2|→|(|0|)|←| //│ Parsed: {+(2,)(2,)(0,)} 2 + 2 of 0 //│ |2| |+| |2|→|#of| |0|←| //│ Parsed: {+(2,)(2,)(0,)} ================================================ FILE: shared/src/test/diff/parser/MultilineFun.mls ================================================ :AllowParseErrors fun f( x ) = x //│ |#fun| |f|(|→|x|←|↵|)| |#=| |x| //│ Parsed: {fun f = (x,) => x} fun f(x ) = x //│ |#fun| |f|(|x|↵|)| |#=| |x| //│ Parsed: {fun f = (x,) => x} fun f( x) = x //│ |#fun| |f|(|→|x|←|)| |#=| |x| //│ Parsed: {fun f = (x,) => x} fun f( x) = x //│ |#fun| |f|(|↵|x|)| |#=| |x| //│ ╔══[PARSE ERROR] Unexpected identifier here //│ ║ l.21: x) = x //│ ╙── ^ //│ Parsed: {fun f = () => x} fun f(x, y) = x + y //│ |#fun| |f|(|x|,|→|y|←|)| |#=| |x| |+| |y| //│ Parsed: {fun f = (x, {y},) => +(x,)(y,)} fun f( x, y) = x + y //│ |#fun| |f|(|→|x|,|↵|y|←|)| |#=| |x| |+| |y| //│ Parsed: {fun f = (x, y,) => +(x,)(y,)} fun f( x, y ) = x + y //│ |#fun| |f|(|→|x|,|↵|y|←|↵|)| |#=| |x| |+| |y| //│ Parsed: {fun f = (x, y,) => +(x,)(y,)} ================================================ FILE: shared/src/test/diff/parser/NamedArrays.mls ================================================ :AllowParseErrors [] //│ |[||]| //│ Parsed: {[]} [x: 1] //│ |[|x|#:| |1|]| //│ Parsed: {[x: 1,]} [x : 1] //│ |[|x| |#:| |1|]| //│ Parsed: {[x : 1,]} [x: 1,] //│ |[|x|#:| |1|,|]| //│ Parsed: {[x: 1,]} [x: 1, y:] //│ |[|x|#:| |1|,| |y|#:|]| //│ ╔══[PARSE ERROR] Unexpected end of square bracket section; an expression was expected here //│ ║ l.20: [x: 1, y:] //│ ╙── ^ //│ Parsed: {[x: 1, y: undefined,]} [x:, y: 1] //│ |[|x|#:|,| |y|#:| |1|]| //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.27: [x:, y: 1] //│ ╙── ^ //│ Parsed: {[x: y : 1,]} [x:, y:] //│ |[|x|#:|,| |y|#:|]| //│ ╔══[PARSE ERROR] Unexpected comma in expression position //│ ║ l.34: [x:, y:] //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of square bracket section; an expression was expected here //│ ║ l.34: [x:, y:] //│ ╙── ^ //│ Parsed: {[x: y : (),]} [x: 1, 2, 3] //│ |[|x|#:| |1|,| |2|,| |3|]| //│ Parsed: {[x: 1, 2, 3,]} [1, y: 2, 3] //│ |[|1|,| |y|#:| |2|,| |3|]| //│ Parsed: {[1, y: 2, 3,]} [x: 1, y: 2, z: 3] //│ |[|x|#:| |1|,| |y|#:| |2|,| |z|#:| |3|]| //│ Parsed: {[x: 1, y: 2, z: 3,]} () //│ |(||)| //│ Parsed: {undefined} (x: 1) //│ |(|x|#:| |1|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.60: (x: 1) //│ ╙── ^^^^ //│ Parsed: {1} (x:) //│ |(|x|#:|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.67: (x:) //│ ╙── ^ //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.67: (x:) //│ ╙── ^^ //│ Parsed: {undefined} (x: 1,) //│ |(|x|#:| |1|,|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.77: (x: 1,) //│ ╙── ^^^^ //│ Parsed: {1} (x: 1, 2, 3) //│ |(|x|#:| |1|,| |2|,| |3|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.84: (x: 1, 2, 3) //│ ╙── ^^^^ //│ Parsed: {,(1, ,(2, 3,),)} (1, y: 2, 3) //│ |(|1|,| |y|#:| |2|,| |3|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.91: (1, y: 2, 3) //│ ╙── ^^^^ //│ Parsed: {,(1, ,(2, 3,),)} (x: 1, y: 2, z: 3) //│ |(|x|#:| |1|,| |y|#:| |2|,| |z|#:| |3|)| //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.98: (x: 1, y: 2, z: 3) //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.98: (x: 1, y: 2, z: 3) //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Illegal position for field specification //│ ║ l.98: (x: 1, y: 2, z: 3) //│ ╙── ^^^^ //│ Parsed: {,(1, ,(2, 3,),)} 1 //│ |1| //│ Parsed: {1} x: 1 //│ |x|#:| |1| //│ Parsed: {x : 1} 1, //│ |1|,| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.120: 1, //│ ╙── ^ //│ Parsed: {,(1, undefined,)} x: 1, //│ |x|#:| |1|,| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.127: x: 1, //│ ╙── ^ //│ Parsed: {x : ,[1, ()]} 1, 2, 3 //│ |1|,| |2|,| |3| //│ Parsed: {,(1, ,(2, 3,),)} f of 1, 2, 3 //│ |f| |#of| |1|,| |2|,| |3| //│ Parsed: {f(1, 2, 3,)} f of x: 1, y: 2, z: 3 //│ |f| |#of| |x|#:| |1|,| |y|#:| |2|,| |z|#:| |3| //│ Parsed: {f(x: 1, y: 2, z: 3,)} f of x: 1, 2, z: 3 //│ |f| |#of| |x|#:| |1|,| |2|,| |z|#:| |3| //│ Parsed: {f(x: 1, 2, z: 3,)} f of x: 1, 2, 3 //│ |f| |#of|→|x|#:| |1|,| |2|,| |3|←| //│ Parsed: {f(x: 1, 2, 3,)} f of x: 1, y: 2, z: 3 //│ |f| |#of|→|x|#:| |1|,|↵|y|#:| |2|,|↵|z|#:| |3|←| //│ Parsed: {f(x: 1, y: 2, z: 3,)} f of x: 1 y: 2 z: 3 //│ |f| |#of|→|x|#:| |1|↵|y|#:| |2|↵|z|#:| |3|←| //│ ╔══[PARSE ERROR] Unexpected named argument name here //│ ║ l.163: x: 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected named argument name here //│ ║ l.164: y: 2 //│ ╙── ^ //│ Parsed: {f(z: {1; 2; 3},)} f of x: 1 2 z: 3 //│ |f| |#of|→|x|#:| |1|↵|2|↵|z|#:| |3|←| //│ ╔══[PARSE ERROR] Unexpected named argument name here //│ ║ l.176: x: 1 //│ ╙── ^ //│ Parsed: {f(z: {1; 2; 3},)} f of x: 1 y: 2 3 //│ |f| |#of|→|x|#:| |1|↵|y|#:| |2|↵|3|←| //│ ╔══[PARSE ERROR] Unexpected named argument name here //│ ║ l.186: x: 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected named argument name here //│ ║ l.187: y: 2 //│ ╙── ^ //│ Parsed: {f({1; 2; 3},)} ================================================ FILE: shared/src/test/diff/parser/NegativeLits.mls ================================================ :NewParser :ParseOnly type MinusOne = -1 //│ |#type| |MinusOne| |#=| |-|1| //│ Parsed: {type alias MinusOne: -1 {}} fun f(x: MinusOne) = x //│ |#fun| |f|(|x|#:| |MinusOne|)| |#=| |x| //│ Parsed: {fun f = (x: MinusOne,) => x} f(-1) //│ |f|(|-|1|)| //│ Parsed: {f(-1,)} ================================================ FILE: shared/src/test/diff/parser/New.mls ================================================ :pe new //│ |#new| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.4: new //│ ╙── ^ //│ Parsed: {new undefined} :pe (new) //│ |(|#new|)| //│ ╔══[PARSE ERROR] Unexpected end of parenthesis section; an expression was expected here //│ ║ l.12: (new) //│ ╙── ^ //│ Parsed: {'(' new undefined ')'} new {} //│ |#new| |{||}| //│ Parsed: {new '{' {} '}'} new A //│ |#new| |A| //│ Parsed: {new A} new A() //│ |#new| |A|(||)| //│ Parsed: {(new A)()} new A(1, 2, 3) //│ |#new| |A|(|1|,| |2|,| |3|)| //│ Parsed: {(new A)(1, 2, 3,)} new A of 1, 2, 3 //│ |#new| |A| |#of| |1|,| |2|,| |3| //│ Parsed: {new A(1, 2, 3,)} new A { fun x = 1 } //│ |#new| |A| |{| |#fun| |x| |#=| |1| |}| //│ Parsed: {new A {fun x = 1}} new A() { fun x = 1 } //│ |#new| |A|(||)| |{| |#fun| |x| |#=| |1| |}| //│ Parsed: {(new A)() {fun x = 1}} new A(1, 2, 3) { fun x = 1 } //│ |#new| |A|(|1|,| |2|,| |3|)| |{| |#fun| |x| |#=| |1| |}| //│ Parsed: {(new A)(1, 2, 3,) {fun x = 1}} new A of 1, 2, 3 { fun x = 1 } //│ |#new| |A| |#of| |1|,| |2|,| |3| |{| |#fun| |x| |#=| |1| |}| //│ Parsed: {new A(1, 2, 3 {fun x = 1},)} :pe new new //│ |#new| |#new| //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.57: new new //│ ╙── ^ //│ Parsed: {new new undefined} new new {} //│ |#new| |#new| |{||}| //│ Parsed: {new new '{' {} '}'} :pe new {new} //│ |#new| |{|#new|}| //│ ╔══[PARSE ERROR] Unexpected end of curly brace section; an expression was expected here //│ ║ l.69: new {new} //│ ╙── ^ //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.69: new {new} //│ ╙── ^^^ //│ Parsed: {new '{' {: new undefined} '}'} ================================================ FILE: shared/src/test/diff/parser/Ops.mls ================================================ :AllowParseErrors acc + 1 //│ |acc| |+| |1| //│ Parsed: {+(acc,)(1,)} acc + 1 //│ |acc|↵|+| |1| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.9: + 1 //│ ╙── ^ //│ Parsed: {acc; 1} acc + 1 //│ |acc|→|+| |1|←| //│ Parsed: {+(acc, 1,)} acc + 1 //│ |acc| |+|→|1|←| //│ Parsed: {+(acc,)({1},)} acc + 1 //│ |acc|→|+|↵|1|←| //│ ╔══[PARSE ERROR] Unexpected newline in expression position //│ ║ l.27: + //│ ║ ^ //│ ║ l.28: 1 //│ ╙── ^^ //│ Parsed: {+(acc, 1,)} acc + 1 //│ |acc|→|+|→|1|←|←| //│ Parsed: {+(acc, {1},)} acc + 1 * 3 //│ |acc|→|+| |1|↵|*| |3|←| //│ Parsed: {*(+(acc, 1,), 3,)} acc + 1 + 2 * 3 //│ |acc|→|+| |1|↵|+| |2|↵|*| |3|←| //│ Parsed: {*(+(+(acc, 1,), 2,), 3,)} acc + 1 + 2 * 3 //│ |acc| |+| |1| |+| |2| |*| |3| //│ Parsed: {+(+(acc,)(1,),)(*(2,)(3,),)} acc+1+2*3 //│ |acc|+|1|+|2|*|3| //│ Parsed: {+(+(acc,)(1,),)(*(2,)(3,),)} acc + foo bar baz //│ |acc| |+|→|foo| |bar|↵|baz|←| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.65: foo bar //│ ╙── ^^^^^^^ //│ Parsed: {+(acc,)({foo(bar,); baz},)} a+ b c //│ |a|+|→|b|↵|c|←| //│ Parsed: {+(a,)({b; c},)} a+ b c //│ |a|+|→|b|←|↵|c| //│ Parsed: {+(a,)({b},); c} a +b c //│ |a|→|+|b|←|↵|c| //│ Parsed: {+(a, b,); c} a + b * 2 //│ |a| |+| |b|→|*| |2|←| //│ Parsed: {+(a,)(*(b, 2,),)} a(b) * 2 //│ |a|(|b|)|→|*| |2|←| //│ Parsed: {*(a(b,), 2,)} a of b * 2 //│ |a| |#of| |b|→|*| |2|←| //│ Parsed: {a(*(b, 2,),)} a then b * 2 //│ |a| |#then| |b|→|*| |2|←| //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.107: a then b //│ ║ ^^^^^^^^ //│ ║ l.108: * 2 //│ ╙── ^^^^^ //│ Parsed: {undefined} a + b * 2 + 1 * 3 //│ |a| |+| |b|→|*| |2|↵|+| |1|↵|*| |3|←| //│ Parsed: {+(a,)(*(+(*(b, 2,), 1,), 3,),)} a + b * 2 + 1 //│ |a| |+| |b|→|*| |2|→|+| |1|←|←| //│ Parsed: {+(a,)(*(b, +(2, 1,),),)} a + b * 2 - 1 //│ |a| |+| |b|→|*| |2|←|→|-| |1|←| //│ Parsed: {-(+(a,)(*(b, 2,),), 1,)} a + b * 2 + 1 * 3 //│ |a| |+| |b|→|*| |2|→|+| |1|←|↵|*| |3|←| //│ Parsed: {+(a,)(*(*(b, +(2, 1,),), 3,),)} a + b * 2 + 1 * 3 //│ |a| |+|→|b|→|*| |2|←|↵|+| |1|↵|*| |3|←| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.146: + 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.147: * 3 //│ ╙── ^ //│ Parsed: {+(a,)({*(b, 2,); 1; 3},)} a + b * 2 + 1 * 3 //│ |a| |+|→|b|→|*| |2|←|↵|+| |1|←|→|*| |3|←| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.160: + 1 //│ ╙── ^ //│ Parsed: {*(+(a,)({*(b, 2,); 1},), 3,)} a + b * 2 + 1 * 3 //│ |a| |+|→|b|→|*| |2|←|↵|+|→|1|→|*| |3|←|←|←| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.171: + //│ ╙── ^ //│ Parsed: {+(a,)({*(b, 2,); {*(1, 3,)}},)} a + b * 2 + 1 * 3 //│ |a| |+|→|b|→|*| |2|↵|+|→|1|→|*| |3|←|←|←|←| //│ Parsed: {+(a,)({+(*(b, 2,), {*(1, 3,)},)},)} a + b * 2 + 1 * 3 //│ |a| |+|→|b|→|*| |2|←|↵|+| |1|→|*| |3|←|←| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.192: + 1 //│ ╙── ^ //│ Parsed: {+(a,)({*(b, 2,); *(1, 3,)},)} a + b * 2 + 1 * 3 //│ |a| |+|→|b|→|*| |2|↵|+| |1|→|*| |3|←|←|←| //│ Parsed: {+(a,)({+(*(b, 2,), *(1, 3,),)},)} a + b * c //│ |a| |+|→|b| |*|→|c|←|←| //│ Parsed: {+(a,)({*(b,)({c},)},)} a * b + c //│ |a| |*|→|b| |+|→|c|←|←| //│ Parsed: {*(a,)({+(b,)({c},)},)} a * let x = 1 b + c //│ |a| |*|→|#let| |x| |#=| |1|↵|b| |+|→|c|←|←| //│ Parsed: {*(a,)({let x = 1; +(b,)({c},)},)} ================================================ FILE: shared/src/test/diff/parser/Records.mls ================================================ {} //│ |{||}| //│ Parsed: {'{' {} '}'} {x: 1} //│ |{|x|#:| |1|}| //│ Parsed: {'{' {x: 1} '}'} {x: 1,} //│ |{|x|#:| |1|,|}| //│ Parsed: {'{' {x: 1} '}'} {x:1,y:2} //│ |{|x|#:|1|,|y|#:|2|}| //│ Parsed: {'{' {x: 1, y: 2} '}'} {x: 1, y: 2} //│ |{|x|#:| |1|,| |y|#:| |2|}| //│ Parsed: {'{' {x: 1, y: 2} '}'} { x: 1, y: 2, } //│ |{| |x|#:| |1|,| |y|#:| |2|,| |}| //│ Parsed: {'{' {x: 1, y: 2} '}'} { x: 1, y: 2, z: 3, } //│ |{|→|x|#:| |1|,|↵|y|#:| |2|,|↵|z|#:| |3|,|←|↵|}| //│ Parsed: {'{' {x: 1, y: 2, z: 3} '}'} {x: 1, y, z} //│ |{|x|#:| |1|,| |y|,| |z|}| //│ Parsed: {'{' {x: 1, y: y, z: z} '}'} :pe {1} //│ |{|1|}| //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.40: {1} //│ ╙── ^ //│ Parsed: {'{' {: 1} '}'} :pe {1,} //│ |{|1|,|}| //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.48: {1,} //│ ╙── ^ //│ Parsed: {'{' {: 1} '}'} :pe {1,2} //│ |{|1|,|2|}| //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.56: {1,2} //│ ╙── ^ //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.56: {1,2} //│ ╙── ^ //│ Parsed: {'{' {: 1, : 2} '}'} :pe {1,2,} //│ |{|1|,|2|,|}| //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.67: {1,2,} //│ ╙── ^ //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.67: {1,2,} //│ ╙── ^ //│ Parsed: {'{' {: 1, : 2} '}'} :pe { 1, 2, 3, } //│ |{|→|1|,|↵|2|,|↵|3|,|←|↵|}| //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.79: 1, //│ ╙── ^ //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.80: 2, //│ ╙── ^ //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.81: 3, //│ ╙── ^ //│ Parsed: {'{' {: 1, : 2, : 3} '}'} ================================================ FILE: shared/src/test/diff/parser/Select.mls ================================================ x.a //│ |x|.a| //│ Parsed: {(x).a} x . a //│ |x| |.| |a| //│ Parsed: {.(x,)(a,)} x. a //│ |x|.| |a| //│ Parsed: {.(x,)(a,)} x .a //│ |x| |.a| //│ Parsed: {(x).a} x .a //│ |x|→|.a|←| //│ Parsed: {(x).a} :pe x .a //│ |x|↵|.a| //│ ╔══[PARSE ERROR] Unexpected selector in expression position //│ ║ l.25: .a //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.25: .a //│ ╙── ^ //│ Parsed: {x; undefined} x.a.b //│ |x|.a|.b| //│ Parsed: {((x).a).b} x .a .b //│ |x|→|.a|↵|.b|←| //│ Parsed: {((x).a).b} x .a.b //│ |x|→|.a|.b|←| //│ Parsed: {((x).a).b} // Perhaps surprising: x .a .b //│ |x|→|.a|→|.b|←|←| //│ Parsed: {((x).a).b} x .a .b .c .d //│ |x|→|.a|→|.b|↵|.c|←|↵|.d|←| //│ Parsed: {((((x).a).b).c).d} x .a .b .d .e .c .d .a //│ |x|→|.a|→|.b|→|.d|↵|.e|←|↵|.c|←|↵|.d|→|.a|←|←| //│ Parsed: {(((((((x).a).b).d).e).c).d).a} :pe . //│ |.| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.77: . //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.77: . //│ ╙── ^ //│ Parsed: {undefined} (.) //│ |(|.|)| //│ Parsed: {.} a, b //│ |a|,| |b| //│ Parsed: {,(a, b,)} a .b .c //│ |a| |.b| |.c| //│ Parsed: {((a).b).c} a + 1 .c //│ |a| |+| |1| |.c| //│ Parsed: {+(a,)((1).c,)} a + f(1) .c //│ |a| |+| |f|(|1|)| |.c| //│ Parsed: {+(a,)((f(1,)).c,)} a + f (1) .c //│ |a| |+| |f| |(|1|)| |.c| //│ Parsed: {+(a,)((f(1,)).c,)} a + f (1).c //│ |a| |+| |f| |(|1|)|.c| //│ Parsed: {+(a,)((f(1,)).c,)} f of x . y //│ |f| |#of| |x| |.| |y| //│ Parsed: {f(.(x,)(y,),)} f of x .y //│ |f| |#of| |x| |.y| //│ Parsed: {f((x).y,)} a + b .c //│ |a| |+| |b|→|.c|←| //│ Parsed: {(+(a,)(b,)).c} :pe a + b .c //│ |a| |+|→|b|↵|.c|←| //│ ╔══[PARSE ERROR] Unexpected selector in expression position //│ ║ l.132: .c //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.132: .c //│ ╙── ^ //│ Parsed: {+(a,)({b; undefined},)} a + b .c //│ |a| |+|→|b|→|.c|←|←| //│ Parsed: {+(a,)({(b).c},)} a + b .c * 2 .d - 1 //│ |a| |+| |b|→|.c| |*| |2|→|.d| |-| |1|←|←| //│ Parsed: {-((*((+(a,)(b,)).c,)(2,)).d,)(1,)} a + b .c * 2 .d - 1 //│ |a| |+| |b|→|.c| |*| |2|↵|.d| |-| |1|←| //│ Parsed: {-((*((+(a,)(b,)).c,)(2,)).d,)(1,)} a .b of c //│ |a|→|.b|↵|#of| |c|←| //│ Parsed: {(a).b(c,)} a .b + 1 of c //│ |a|→|.b| |+| |1|↵|#of| |c|←| //│ Parsed: {+((a).b,)(1,)(c,)} a .b + 1 of c //│ |a|→|.b| |+| |1|→|#of| |c|←|←| //│ Parsed: {+((a).b,)(1,)(c,)} :pe a .b + 1 of c //│ |a|→|.b| |+|→|1|↵|#of| |c|←|←| //│ ╔══[PARSE ERROR] Unexpected 'of' keyword in expression position //│ ║ l.183: of c //│ ╙── ^^ //│ Parsed: {+((a).b,)({1; c},)} a .b + 1 of c //│ |a|→|.b| |+|→|1|→|#of| |c|←|←|←| //│ Parsed: {+((a).b,)({1(c,)},)} a .b of c .d //│ |a|→|.b|↵|#of| |c|↵|.d|←| //│ Parsed: {((a).b(c,)).d} a .b of c .d //│ |a|→|.b|→|#of| |c|←|↵|.d|←| //│ Parsed: {((a).b(c,)).d} a .b +1 *2 //│ |a|→|.b|↵|+|1|↵|*|2|←| //│ Parsed: {*(+((a).b,)(1,),)(2,)} // TODO we should find a more uniform way of typing indented operator/selector/of blocks a +1 *2 .b //│ |a|→|+|1|↵|*|2|↵|.b|←| //│ ╔══[PARSE ERROR] Unexpected selector in operator block //│ ║ l.222: .b //│ ╙── ^^ //│ Parsed: {*(+(a, 1,), 2,)} 1 .+ 2 //│ |1| |.+| |2| //│ Parsed: {.+(1,)(2,)} 1 +. 2 //│ |1| |+.| |2| //│ Parsed: {+.(1,)(2,)} 1 .+! 2 //│ |1| |.+!| |2| //│ Parsed: {.+!(1,)(2,)} 0 * 1 .+. 2 * 3 //│ |0| |*| |1| |.+.| |2| |*| |3| //│ Parsed: {*(*(0,)(.+.(1,)(2,),),)(3,)} :w 1 .+a 2 //│ |1| |.+|a| |2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.247: 1 .+a 2 //│ ╙── ^^^ //│ Parsed: {.+(1,)(a(2,),)} ================================================ FILE: shared/src/test/diff/parser/SpecParams.mls ================================================ :ParseOnly fun Foo(#x, y) = x + y //│ |#fun| |Foo|(|##|x|,| |y|)| |#=| |x| |+| |y| //│ Parsed: {fun Foo = (#x, y,) => +(x,)(y,)} fun Foo(x, #y) = x + y //│ |#fun| |Foo|(|x|,| |##|y|)| |#=| |x| |+| |y| //│ Parsed: {fun Foo = (x, #y,) => +(x,)(y,)} fun Foo(#x, #y, #z) = if z then x + y else z //│ |#fun| |Foo|(|##|x|,| |##|y|,| |##|z|)| |#=| |#if| |z| |#then| |x| |+| |y| |#else| |z| //│ Parsed: {fun Foo = (#x, #y, #z,) => if (z) then +(x,)(y,) else z} class Foo(x, #y, z) //│ |#class| |Foo|(|x|,| |##|y|,| |z|)| //│ Parsed: {class Foo(x, #y, z,) {}} ================================================ FILE: shared/src/test/diff/parser/Subscripts.mls ================================================ a[0] //│ |a|[|0|]| //│ Parsed: {a‹0›} a[0][1] //│ |a|[|0|]|[|1|]| //│ Parsed: {a‹0›‹1›} a.x[0].y[1].z //│ |a|.x|[|0|]|.y|[|1|]|.z| //│ Parsed: {(((a).x‹0›).y‹1›).z} a + b [0] * 2 //│ |a| |+| |b| |[|0|]| |*| |2| //│ Parsed: {+(a,)(*(b‹0›,)(2,),)} foo [1] //│ |foo|→|[|1|]|←| //│ Parsed: {foo‹1›} Foo(bar) + 1 ["hello"] //│ |Foo|(|bar|)| |+| |1|→|[|"hello"|]|←| //│ Parsed: {+(Foo(bar,),)(1,)‹"hello"›} Foo(bar) + 1["hello"] //│ |Foo|(|bar|)| |+|→|1|[|"hello"|]|←| //│ Parsed: {+(Foo(bar,),)({1‹"hello"›},)} Foo(bar) + 1 ["hello"] //│ |Foo|(|bar|)| |+|→|1|→|[|"hello"|]|←|←| //│ Parsed: {+(Foo(bar,),)({1‹"hello"›},)} Foo(bar) + 1 [1] [2] //│ |Foo|(|bar|)| |+| |1|→|[|1|]|↵|[|2|]|←| //│ Parsed: {+(Foo(bar,),)(1,)‹1›‹2›} Foo(bar) + 1 [1] [2] //│ |Foo|(|bar|)| |+| |1|→|[|1|]|→|[|2|]|←|←| //│ Parsed: {+(Foo(bar,),)(1,)‹1›‹2›} a + 111 [22] [333] //│ |a| |+|→|111|→|[|22|]|←|↵|[|333|]|←| //│ Parsed: {+(a,)({111‹22›; [333,]},)} Foo(bar) + 1 [1] [2] //│ |Foo|(|bar|)| |+|→|1|→|[|1|]|←|↵|[|2|]|←| //│ Parsed: {+(Foo(bar,),)({1‹1›; [2,]},)} a of [333] //│ |a| |#of| |[|333|]| //│ Parsed: {a([333,],)} a of [333] //│ |a| |#of|→|[|333|]|←| //│ Parsed: {a([333,],)} a of 111 [22] [333] //│ |a| |#of|→|111|→|[|22|]|←|↵|[|333|]|←| //│ Parsed: {a({111‹22›; [333,]},)} a( 111 [22] [333] ) //│ |a|(|→|111|→|[|22|]|←|↵|[|333|]|←|↵|)| //│ Parsed: {a({111‹22›; [333,]},)} ================================================ FILE: shared/src/test/diff/parser/TypeParams.mls ================================================ class Foo //│ |#class| |Foo|‹|A|›| //│ Parsed: {class Foo‹A› {}} class Foo‹A› //│ |#class| |Foo|‹|A|›| //│ Parsed: {class Foo‹A› {}} (1) //│ |(|1|)| //│ Parsed: {'(' 1 ')'} foo<1,2,3> //│ |foo|‹|1|,|2|,|3|›| //│ Parsed: {foo‹1, 2, 3›} foo<1, 2, 3> //│ |foo|‹|1|,| |2|,| |3|›| //│ Parsed: {foo‹1, 2, 3›} foo<1, 2, 3>() //│ |foo|‹|1|,| |2|,| |3|›|(||)| //│ Parsed: {foo‹1, 2, 3›()} foo<1, 2, 3>(1, 2, 3) //│ |foo|‹|1|,| |2|,| |3|›|(|1|,| |2|,| |3|)| //│ Parsed: {foo‹1, 2, 3›(1, 2, 3,)} 1 < 2 > 4 //│ |1| |<| |2| |>| |4| //│ Parsed: {>(<(1,)(2,),)(4,)} :w 1 < 2> 4 //│ ╔══[WARNING] This looks like an angle bracket, but it does not close any angle bracket section //│ ║ l.40: 1 < 2> 4 //│ ║ ^ //│ ╙── Add spaces around it if you intended to use `<` as an operator //│ |1| |<| |2|>| |4| //│ Parsed: {>(<(1,)(2,),)(4,)} :pe :w 1< 2 > 4 //│ ╔══[PARSE ERROR] Unmatched opening angle bracket //│ ║ l.50: 1< 2 > 4 //│ ║ ^ //│ ╙── Note that `<` without spaces around it is considered as an angle bracket and not as an operator //│ | |2| |>| |4|1| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.50: 1< 2 > 4 //│ ╙── ^^^^^^^^ //│ Parsed: {>(2,)(4(1,),)} :w 1< 2> 4 //│ |1|‹| |2|›| |4| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.62: 1< 2> 4 //│ ╙── ^^^^^^^ //│ Parsed: {1‹2›(4,)} 1< 2> (4) //│ |1|‹| |2|›| |(|4|)| //│ Parsed: {1‹2›(4,)} 1<2>(4) //│ |1|‹|2|›|(|4|)| //│ Parsed: {1‹2›(4,)} 1<>2 //│ |1|<>|2| //│ Parsed: {<>(1,)(2,)} :pe 1< >2 //│ ╔══[PARSE ERROR] Unmatched opening angle bracket //│ ║ l.82: 1< >2 //│ ║ ^ //│ ╙── Note that `<` without spaces around it is considered as an angle bracket and not as an operator //│ | |>|2|1| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.82: 1< >2 //│ ╙── ^ //│ Parsed: {2(1,)} :pe 1 < >2 //│ |1| |<| |>|2| //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.94: 1 < >2 //│ ╙── ^ //│ Parsed: {<(1,)(2,)} foo(1, 2, 3) & Bar("ok") //│ |foo|‹|S|,| |T|›|(|1|,| |2|,| |3|)| |&| |Bar|‹|U|›|(|"ok"|)| //│ Parsed: {&(foo‹S, T›(1, 2, 3,),)(Bar‹U›("ok",),)} foo(2 + 3) & Bar("ok") //│ |foo|‹|Str|,| |Int|›|(|2| |+| |3|)| |&| |Bar|‹|MyClass|›|(|"ok"|)| //│ Parsed: {&(foo‹Str, Int›(+(2,)(3,),),)(Bar‹MyClass›("ok",),)} // :pe foo[S, T](1, 2, 3) & Bar[U]("ok") //│ |foo|[|S|,| |T|]|(|1|,| |2|,| |3|)| |&| |Bar|[|U|]|(|"ok"|)| //│ Parsed: {&(foo‹S, T›(1, 2, 3,),)(Bar‹U›("ok",),)} class Foo {} //│ |#class| |Foo|‹|T|›| |{||}| //│ Parsed: {class Foo‹T› {}} fun foo(x: T): string = "rua" //│ |#fun| |foo|‹|T|›|(|x|#:| |T|)|#:| |string| |#=| |"rua"| //│ Parsed: {fun foo = (x: T,) => "rua" : string} foo(42) //│ |foo|‹|int|›|(|42|)| //│ Parsed: {foo‹int›(42,)} ================================================ FILE: shared/src/test/diff/parser/UserDefinedOpsMaybe.mls ================================================ :AllowParseErrors 1 div 2 //│ |1| |div| |2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.4: 1 div 2 //│ ╙── ^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.4: 1 div 2 //│ ╙── ^^^^^^^ //│ Parsed: {1(div(2,),)} 1 mod 2 //│ |1| |mod| |2| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.14: 1 mod 2 //│ ╙── ^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.14: 1 mod 2 //│ ╙── ^^^^^^^ //│ Parsed: {1(mod(2,),)} 3 eq 4 //│ |3| |eq| |4| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.24: 3 eq 4 //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.24: 3 eq 4 //│ ╙── ^^^^^^ //│ Parsed: {3(eq(4,),)} xs map f map g map h //│ |xs| |map| |f| |map| |g| |map| |h| //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.34: xs map f map g map h //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ Parsed: {xs(map(f(map(g(map(h,),),),),),)} ================================================ FILE: shared/src/test/diff/parser/WeirdIfs.mls ================================================ if else e //│ |#if| |#else| |e| //│ Parsed: {if else e} if x is else e //│ |#if| |x| |is| |#else| |e| //│ Parsed: {if x is else e} :pe if x is else e //│ |#if| |x| |is|→|#else| |e|←| //│ ╔══[PARSE ERROR] Unexpected indented block in expression position //│ ║ l.12: else e //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.12: else e //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.11: if x is //│ ║ ^^^^ //│ ║ l.12: else e //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.11: if x is //│ ╙── ^^ //│ Parsed: {if (is(x,)(undefined,)) then undefined} :pe if x is P else e //│ |#if| |x| |is|→|P| |#else| |e|←| //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.32: P else e //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.31: if x is //│ ║ ^^^^ //│ ║ l.32: P else e //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.31: if x is //│ ╙── ^^ //│ Parsed: {if (is(x,)({P},)) then undefined} // TODO(Luyu): move to another test file if x is Some(xv) and y is Left(yv) then xv + yv Some(xv) and y is Right(yv2) then xv + yv2 else els //│ |#if| |x| |is|→|Some|(|xv|)| |and| |y| |is| |Left|(|yv|)| |#then| |xv| |+| |yv|↵|Some|(|xv|)| |and| |y| |is| |Right|(|yv2|)| |#then| |xv| |+| |yv2|↵|#else| |els|←| //│ Parsed: {if x is ‹(and(Some(xv,),)(is(y,)(Left(yv,),),)) then +(xv,)(yv,); (and(Some(xv,),)(is(y,)(Right(yv2,),),)) then +(xv,)(yv2,); else els›} ================================================ FILE: shared/src/test/diff/parser/Where.mls ================================================ 1 + 1 where 1 //│ |1| |+| |1| |#where| |1| //│ Parsed: {+(1,)(1,) where {1}} a => a + 1 where foo //│ |a| |#=>| |a| |+| |1| |#where| |foo| //│ Parsed: {(a,) => +(a,)(1,) where {foo}} a + 1 where let a = 1 //│ |a| |+| |1| |#where| |#let| |a| |#=| |1| //│ Parsed: {+(a,)(1,) where {let a = 1}} fun foo: 'a => 'a => 'a where 'a : int //│ |#fun| |foo|#:| |'a| |#=>| |'a| |#=>| |'a| |#where| |'a| |#:| |int| //│ Parsed: {fun foo: 'a -> 'a -> ('a //│ where //│ 'a <: int)} :pe fun foo: 'a + 'a + 'a where 'a : int //│ |#fun| |foo|#:| |'a| |+| |'a| |+| |'a| |#where| |'a| |#:| |int| //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.21: fun foo: 'a + 'a + 'a where 'a : int //│ ╙── ^^^^^^^ //│ Parsed: {fun foo: anything} :pe fun foo: 'a -> 'a -> 'a where 'a : int //│ |#fun| |foo|#:| |'a| |->| |'a| |->| |'a| |#where| |'a| |#:| |int| //│ ╔══[PARSE ERROR] Not a recognized type //│ ║ l.29: fun foo: 'a -> 'a -> 'a where 'a : int //│ ╙── ^^^^^^^^^^^^^^ //│ Parsed: {fun foo: anything} ================================================ FILE: shared/src/test/diff/parser/Whitespace.mls ================================================ :AllowParseErrors //│ | | //│ Parsed: {} //│ | | //│ Parsed: {} //│ | | //│ Parsed: {} false(1 , 2 , 3) //│ |false|(|1| |,| |2| |,| |3|)| //│ Parsed: {false(1, 2, 3,)} false (1 ,2 ,3) //│ |false| |(|1| |,|2| |,|3|)| //│ Parsed: {false(1, 2, 3,)} false( 1 , 2 , 3 ) //│ |false|(| |1| |,| |2| |,| |3| |)| //│ Parsed: {false(1, 2, 3,)} ================================================ FILE: shared/src/test/diff/parser/boolops.mls ================================================ true and false //│ |true| |and| |false| //│ Parsed: {and(true,)(false,)} a and b or c //│ |a| |and| |b| |or| |c| //│ Parsed: {or(and(a,)(b,),)(c,)} a or b and c //│ |a| |or| |b| |and| |c| //│ Parsed: {or(a,)(and(b,)(c,),)} a + 1 or b + 2 and c + 3 //│ |a| |+| |1| |or| |b| |+| |2| |and| |c| |+| |3| //│ Parsed: {or(+(a,)(1,),)(and(+(b,)(2,),)(+(c,)(3,),),)} any of a, b, c //│ |any| |#of| |a|,| |b|,| |c| //│ Parsed: {any(a, b, c,)} any of a b c //│ |any| |#of|→|a|↵|b|↵|c|←| //│ Parsed: {any({a; b; c},)} all of x any of a b c y //│ |all| |#of|→|x|↵|any| |#of|→|a|↵|b|↵|c|←|↵|y|←| //│ Parsed: {all({x; any({a; b; c},); y},)} ================================================ FILE: shared/src/test/diff/pretyper/Errors.mls ================================================ :NewDefs :NoJS :ShowPreTyperErrors :AllowParseErrors :AllowTypeErrors // This file is used to test the error messages of the _unfinished_ `PreTyper`. // The goal is to document `PreTyper` behavior and to make sure that the error // messages are clear and helpful. We can delete this file after it is done. // codegen // ------- // Nested fun main = let f(x: Int): Int = if x is 0 then 1 else g(x - 1) let g(x: Int): Int = f(x) f //│ ╔══[ERROR] identifier `g` not found //│ ║ l.18: else g(x - 1) //│ ╙── ^ //│ fun main: (x: Int) -> Int // SymbolicOps fun (>>)(f, g) = x => g(f(x)) //│ ╔══[PARSE ERROR] Expected a function name; found parenthesis section instead //│ ║ l.27: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier `g` not found //│ ║ l.27: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^ //│ ╔══[ERROR] identifier `f` not found //│ ║ l.27: fun (>>)(f, g) = x => g(f(x)) //│ ╙── ^ //│ fun (>>) : Int -> Int // mlscript // -------- // Sequence let test(x) = log(x); x + 1 //│ ╔══[ERROR] identifier `x` not found //│ ║ l.43: let test(x) = log(x); x + 1 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.43: let test(x) = log(x); x + 1 //│ ╙── ^ //│ let test: anything -> () //│ Int // nu // -- // Ascription foo(123: Int): Int //│ ╔══[ERROR] identifier `foo` not found //│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `Int` is resolved to a type //│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: foo //│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `error` for applying named arguments //│ ║ l.57: foo(123: Int): Int //│ ╙── ^^^ //│ Int // Ascription foo(123:Int):Int //│ ╔══[ERROR] identifier `foo` not found //│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier `Int` is resolved to a type //│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: foo //│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ ╔══[ERROR] Cannot retrieve appropriate function signature from type `error` for applying named arguments //│ ║ l.73: foo(123:Int):Int //│ ╙── ^^^ //│ Int // BadBlocks fun test = let a = b let b = 1 a //│ ╔══[ERROR] identifier `b` not found //│ ║ l.90: let a = b //│ ╙── ^ //│ fun test: 1 // BadBlocks fun test = let a() = b let b = 1 a() //│ ╔══[ERROR] identifier `b` not found //│ ║ l.100: let a() = b //│ ╙── ^ //│ fun test: 1 // BadClasses hello //│ ╔══[ERROR] identifier `hello` not found //│ ║ l.109: hello //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: hello //│ ║ l.109: hello //│ ╙── ^^^^^ //│ error // BadFieldInit module A { val x = y val y = x } //│ ╔══[ERROR] identifier `y` not found //│ ║ l.120: val x = y //│ ╙── ^ //│ module A { //│ val x: nothing //│ val y: nothing //│ } // BadFieldInit module A { val x = y val y = 1 } //│ ╔══[ERROR] identifier `y` not found //│ ║ l.133: val x = y //│ ╙── ^ //│ module A { //│ val x: 1 //│ val y: 1 //│ } // BadMixin mixin M0 M0 //│ ╔══[ERROR] identifier `M0` is resolved to a type //│ ║ l.146: M0 //│ ╙── ^^ //│ ╔══[ERROR] mixin M0 cannot be used in term position //│ ║ l.146: M0 //│ ╙── ^^ //│ mixin M0() //│ error // BadScopes mixin Foo(x: Int) x //│ ╔══[ERROR] identifier `x` not found //│ ║ l.158: x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.158: x //│ ╙── ^ //│ mixin Foo(x: Int) //│ error // BadScopes class Foo(x: Int) x //│ ╔══[ERROR] identifier `x` not found //│ ║ l.170: x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.170: x //│ ╙── ^ //│ class Foo(x: Int) //│ error // BadScopes class Bar { x } //│ ╔══[ERROR] identifier `x` not found //│ ║ l.181: class Bar { x } //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.181: class Bar { x } //│ ╙── ^ //│ class Bar { //│ constructor() //│ } // BadTraits trait Foo Foo //│ ╔══[ERROR] identifier `Foo` is resolved to a type //│ ║ l.194: Foo //│ ╙── ^^^ //│ ╔══[ERROR] trait Foo cannot be used in term position //│ ║ l.194: Foo //│ ╙── ^^^ //│ trait Foo //│ error // FunPatterns fun f3([(x, y,),],) = x + y //│ ╔══[ERROR] unsupported pattern shape //│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier `x` not found //│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found //│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.205: fun f3([(x, y,),],) = x + y //│ ╙── ^ //│ fun f3: ([error]) -> Int // GenericClassInheritance class C1[A] extends C0[A] { val a = a } //│ ╔══[ERROR] could not find definition `C0` //│ ║ l.227: class C1[A] extends C0[A] { val a = a } //│ ╙── ^^ //│ ╔══[ERROR] identifier `a` not found //│ ║ l.227: class C1[A] extends C0[A] { val a = a } //│ ╙── ^ //│ ╔══[ERROR] Could not find definition `C0` //│ ║ l.227: class C1[A] extends C0[A] { val a = a } //│ ╙── ^^ //│ class C1[A] { //│ constructor() //│ val a: nothing //│ } // GenericMethods fun foo1 = forall 'A: (x: 'A) => x //│ fun foo1: forall 'A. (x: 'A) -> 'A // GenericMethods foo1[Int](42) //│ ╔══[ERROR] type application syntax is not yet supported //│ ║ l.247: foo1[Int](42) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.247: foo1[Int](42) //│ ╙── ^^^^^^^^^ //│ 42 // GenericMethods fun foo2(x: A) = x //│ fun foo2: forall 'A. (x: 'A) -> 'A // GenericMethods foo2(42) //│ ╔══[ERROR] type application syntax is not yet supported //│ ║ l.261: foo2(42) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.261: foo2(42) //│ ╙── ^^^^^^^^^ //│ 42 // GenericMethods fun foo3[A](x: A) = x //│ fun foo3: forall 'A. (x: 'A) -> 'A // GenericMethods foo3[Int](42) //│ ╔══[ERROR] type application syntax is not yet supported //│ ║ l.275: foo3[Int](42) //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.275: foo3[Int](42) //│ ╙── ^^^^^^^^^ //│ 42 // ImplicitMethodPolym module None module M { mut val m = None fun oops(x) = m := x } //│ ╔══[ERROR] identifier `:=` not found //│ ║ l.288: fun oops(x) = m := x //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: := //│ ║ l.288: fun oops(x) = m := x //│ ╙── ^^ //│ module None //│ module M { //│ mut val m: None //│ fun oops: anything -> error //│ } // InterfaceMono trait What0 extends woooo //│ ╔══[ERROR] could not find definition `woooo` //│ ║ l.303: trait What0 extends woooo //│ ╙── ^^^^^ //│ ╔══[ERROR] Could not find definition `woooo` //│ ║ l.303: trait What0 extends woooo //│ ╙── ^^^^^ //│ trait What0 // Misc let f = ((x, y)) => x + y //│ ╔══[ERROR] unsupported pattern shape //│ ║ l.313: let f = ((x, y)) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier `x` not found //│ ║ l.313: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found //│ ║ l.313: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.313: let f = ((x, y)) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.313: let f = ((x, y)) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.313: let f = ((x, y)) => x + y //│ ╙── ^ //│ let f: error -> Int // Misc f[1, 2] //│ ╔══[ERROR] type application syntax is not yet supported //│ ║ l.335: f[1, 2] //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type application syntax is not yet supported //│ ║ l.335: f[1, 2] //│ ╙── ^^^^^^^ //│ error -> Int // Misc let f = (((x, y))) => x + y //│ ╔══[ERROR] unsupported pattern shape //│ ║ l.345: let f = (((x, y))) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier `x` not found //│ ║ l.345: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found //│ ║ l.345: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.345: let f = (((x, y))) => x + y //│ ╙── ^^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.345: let f = (((x, y))) => x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.345: let f = (((x, y))) => x + y //│ ╙── ^ //│ let f: error -> Int // Mut let v1 = {mut 1} v1.x <- 1 //│ ╔══[PARSE ERROR] Record field should have a name //│ ║ l.367: let v1 = {mut 1} //│ ╙── ^ //│ ╔══[ERROR] identifier `<-` not found //│ ║ l.368: v1.x <- 1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: <- //│ ║ l.368: v1.x <- 1 //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in field selection: //│ ║ l.368: v1.x <- 1 //│ ║ ^^^^ //│ ╟── record literal of type `{mut : ?}` does not have field 'x' //│ ║ l.367: let v1 = {mut 1} //│ ║ ^ //│ ╟── but it flows into reference with expected type `{x: ?x}` //│ ║ l.368: v1.x <- 1 //│ ╙── ^^ //│ let v1: {mut : '} //│ error //│ where //│ ' :> 1 // Mut let v2 = [mut x: 1] //│ ╔══[ERROR] identifier `x` not found //│ ║ l.393: let v2 = [mut x: 1] //│ ╙── ^ //│ let v2: [mut x: 'x] //│ where //│ 'x :> 1 // Mut let v2 = [mut y: 1] //│ ╔══[ERROR] identifier `y` not found //│ ║ l.402: let v2 = [mut y: 1] //│ ╙── ^ //│ let v2: [mut y: 'y] //│ where //│ 'y :> 1 // NoThisCtor class Foo() { virtual val foo: Int = 42 } //│ class Foo() { //│ val foo: Int //│ } // NoThisCtor class Foo5() extends Foo() { val foo: Int val x = bar(0) fun bar(y: Int) = this.foo + y } //│ ╔══[ERROR] identifier `bar` not found //│ ║ l.421: val x = bar(0) //│ ╙── ^^^ //│ ╔══[ERROR] Cannot access `this` while initializing field x //│ ║ l.421: val x = bar(0) //│ ║ ^^^^^^^^^^ //│ ╟── The access to `this` is here //│ ║ l.422: fun bar(y: Int) = this.foo + y //│ ╙── ^^^^ //│ class Foo5() extends Foo { //│ fun bar: (y: Int) -> Int //│ val foo: Int //│ val x: Int //│ } // NoThisCtor abstract class Foo: (Int -> Int) { val x = f fun f = this(0) } //│ ╔══[ERROR] Cannot access `this` while initializing field x //│ ║ l.441: val x = f //│ ║ ^^^^^ //│ ╟── The access to `this` is here //│ ║ l.442: fun f = this(0) //│ ╙── ^^^^ //│ abstract class Foo: Int -> Int { //│ fun f: nothing //│ val x: nothing //│ } // Object Object //│ ╔══[ERROR] identifier `Object` is resolved to a type //│ ║ l.456: Object //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object is abstract and cannot be instantiated //│ ║ l.456: Object //│ ╙── ^^^^^^ //│ ╔══[ERROR] Class Object cannot be instantiated as it exposes no constructor //│ ║ l.456: Object //│ ╙── ^^^^^^ //│ error // OpLam x => x.y => y //│ ╔══[ERROR] unsupported pattern shape //│ ║ l.469: x => x.y => y //│ ╙── ^^^ //│ ╔══[ERROR] identifier `y` not found //│ ║ l.469: x => x.y => y //│ ╙── ^ //│ ╔══[ERROR] Unsupported pattern shape: //│ ║ l.469: x => x.y => y //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.469: x => x.y => y //│ ╙── ^ //│ anything -> error -> error // ParamOverride class Base0(n: Num) //│ class Base0(n: Num) // ParamOverride class Derived0(n: Int) extends Base //│ ╔══[ERROR] could not find definition `Base` //│ ║ l.489: class Derived0(n: Int) extends Base //│ ╙── ^^^^ //│ ╔══[ERROR] Could not find definition `Base` //│ ║ l.489: class Derived0(n: Int) extends Base //│ ╙── ^^^^ //│ class Derived0(n: Int) // ParamOverride mixin DerivedBad(n: Int) extends Base //│ ╔══[ERROR] mixin definitions cannot yet extend parents //│ ║ l.499: mixin DerivedBad(n: Int) extends Base //│ ╙── ^^^^ //│ mixin DerivedBad(n: Int) // PartialApp fun foo(x, y) = x + y //│ fun foo: (Int, Int) -> Int // PartialApp foo(2, _) //│ ╔══[ERROR] identifier `_` not found //│ ║ l.510: foo(2, _) //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.510: foo(2, _) //│ ╙── ^ //│ Int // PartialApp _.foo(1) //│ ╔══[ERROR] identifier `_` not found //│ ║ l.520: _.foo(1) //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.520: _.foo(1) //│ ╙── ^ //│ error // PartialApp _ + _ //│ ╔══[ERROR] identifier `_` not found //│ ║ l.530: _ + _ //│ ╙── ^ //│ ╔══[ERROR] identifier `_` not found //│ ║ l.530: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.530: _ + _ //│ ╙── ^ //│ ╔══[ERROR] Widlcard in expression position. //│ ║ l.530: _ + _ //│ ╙── ^ //│ Int // PartialApp _2 + _1 //│ ╔══[ERROR] identifier `_2` not found //│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier `_1` not found //│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _2 //│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: _1 //│ ║ l.546: _2 + _1 //│ ╙── ^^ //│ Int // RefinedPatterns refined //│ ╔══[ERROR] identifier `refined` not found //│ ║ l.562: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Illegal use of reserved operator: refined //│ ║ l.562: refined //│ ╙── ^^^^^^^ //│ ╔══[ERROR] identifier not found: refined //│ ║ l.562: refined //│ ╙── ^^^^^^^ //│ error // Refinements class D() { fun f = 0 } //│ class D() { //│ fun f: 0 //│ } // Refinements let d = D & { f: 0 } //│ ╔══[ERROR] identifier `&` not found //│ ║ l.581: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] Illegal use of reserved operator: & //│ ║ l.581: let d = D & { f: 0 } //│ ╙── ^ //│ ╔══[ERROR] identifier not found: & //│ ║ l.581: let d = D & { f: 0 } //│ ╙── ^ //│ let d: error // Res x => x + 2 //│ Int -> Int // Res res(1) //│ ╔══[ERROR] identifier `res` not found //│ ║ l.598: res(1) //│ ╙── ^^^ //│ ╔══[ERROR] identifier not found: res //│ ║ l.598: res(1) //│ ╙── ^^^ //│ error // Uninstantiable Int //│ ╔══[ERROR] identifier `Int` is resolved to a type //│ ║ l.608: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated //│ ║ l.608: Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor //│ ║ l.608: Int //│ ╙── ^^^ //│ error // Uninstantiable Int() //│ ╔══[ERROR] identifier `Int` is resolved to a type //│ ║ l.621: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated //│ ║ l.621: Int() //│ ╙── ^^^ //│ ╔══[ERROR] Class Int cannot be instantiated as it exposes no constructor //│ ║ l.621: Int() //│ ╙── ^^^ //│ error // Uninstantiable new Int //│ ╔══[ERROR] identifier `Int` is resolved to a type //│ ║ l.634: new Int //│ ╙── ^^^ //│ ╔══[ERROR] Class Int is abstract and cannot be instantiated //│ ║ l.634: new Int //│ ╙── ^^^ //│ Int // Unit (1, 2) => 3 //│ ╔══[WARNING] literal patterns are ignored //│ ║ l.644: (1, 2) => 3 //│ ╙── ^ //│ ╔══[WARNING] literal patterns are ignored //│ ║ l.644: (1, 2) => 3 //│ ╙── ^ //│ (1, 2) -> 3 // Unit 1 => (2, 3) //│ ╔══[WARNING] literal patterns are ignored //│ ║ l.654: 1 => (2, 3) //│ ╙── ^ //│ 1 -> 3 // Vals val c = d + 1 val d = 1 //│ val c: Int //│ val d: 1 // Varargs fun test(...xs) = xs.length //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.667: fun test(...xs) = xs.length //│ ╙── ^^^ //│ ╔══[ERROR] identifier `xs` not found //│ ║ l.667: fun test(...xs) = xs.length //│ ╙── ^^ //│ ╔══[ERROR] identifier not found: xs //│ ║ l.667: fun test(...xs) = xs.length //│ ╙── ^^ //│ fun test: () -> error // WeirdDefs fun fst[x, _] = x //│ ╔══[ERROR] identifier `x` not found //│ ║ l.680: fun fst[x, _] = x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.680: fun fst[x, _] = x //│ ╙── ^ //│ fun fst: error // repro0 class Add[out E](val lhs: E) val add11 = Add(add11) module EvalAddLit { fun eval(e: Add['A]) = if e is Add then eval(e.lhs) } let res = EvalAddLit.eval(add11) //│ ╔══[ERROR] identifier `add11` not found //│ ║ l.691: val add11 = Add(add11) //│ ╙── ^^^^^ //│ class Add[E](lhs: E) //│ val add11: 'E //│ module EvalAddLit { //│ fun eval: forall 'A. (e: 'A) -> nothing //│ } //│ let res: nothing //│ where //│ 'A <: Add['A] //│ 'E :> Add['E] ================================================ FILE: shared/src/test/diff/pretyper/Repro.mls ================================================ :NewDefs :ShowPreTyperErrors ================================================ FILE: shared/src/test/diff/pretyper/SymbolKind.mls ================================================ :NewDefs :ShowPreTyperErrors type Type = Int mixin Mixin1 mixin Mixin2() trait Trait // Trait doesn't have parameters. class Class1 class Class2() module Module //│ type Type = Int //│ mixin Mixin1() //│ mixin Mixin2() //│ trait Trait //│ class Class1 { //│ constructor() //│ } //│ class Class2() //│ module Module fun f(g) = g() //│ fun f: forall 'a. (() -> 'a) -> 'a :e f(Type) //│ ╔══[ERROR] identifier `Type` is resolved to a type //│ ║ l.25: f(Type) //│ ╙── ^^^^ //│ ╔══[ERROR] type alias Type cannot be used in term position //│ ║ l.25: f(Type) //│ ╙── ^^^^ //│ error //│ Code generation encountered an error: //│ type alias Type is not a valid expression :e id(Mixin1) //│ ╔══[ERROR] identifier `Mixin1` is resolved to a type //│ ║ l.37: id(Mixin1) //│ ╙── ^^^^^^ //│ ╔══[ERROR] mixin Mixin1 cannot be used in term position //│ ║ l.37: id(Mixin1) //│ ╙── ^^^^^^ //│ error //│ res //│ = [Function (anonymous)] :e f(Mixin2) //│ ╔══[ERROR] identifier `Mixin2` is resolved to a type //│ ║ l.49: f(Mixin2) //│ ╙── ^^^^^^ //│ ╔══[ERROR] mixin Mixin2 cannot be used in term position //│ ║ l.49: f(Mixin2) //│ ╙── ^^^^^^ //│ error //│ res //│ Runtime error: //│ TypeError: Class extends value undefined is not a constructor or null :e f(Trait) //│ ╔══[ERROR] identifier `Trait` is resolved to a type //│ ║ l.62: f(Trait) //│ ╙── ^^^^^ //│ ╔══[ERROR] trait Trait cannot be used in term position //│ ║ l.62: f(Trait) //│ ╙── ^^^^^ //│ error //│ Code generation encountered an error: //│ trait used in term position :e f(Class1) //│ ╔══[ERROR] Construction of unparameterized class Class1 should use the `new` keyword //│ ║ l.74: f(Class1) //│ ╙── ^^^^^^ //│ Class1 //│ res //│ Runtime error: //│ TypeError: Class constructor Class1 cannot be invoked without 'new' f(Class2) //│ Class2 //│ res //│ = Class2 {} id(Class2) //│ () -> Class2 //│ res //│ = [Function (anonymous)] { //│ class: [class Class2], //│ unapply: [Function: unapply] //│ } :e f(Module) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.97: f(Module) //│ ║ ^^^^^^^^^ //│ ╟── reference of type `Module` is not a function //│ ║ l.97: f(Module) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from application: //│ ║ l.21: fun f(g) = g() //│ ║ ^^^ //│ ╟── from reference: //│ ║ l.21: fun f(g) = g() //│ ╙── ^ //│ error //│ res //│ Runtime error: //│ TypeError: g is not a function id(Module) //│ Module //│ res //│ = Module { class: [class Module] } ================================================ FILE: shared/src/test/diff/pretyper/ucs/DualOption.mls ================================================ :NewDefs abstract class Option[T] class Some[T](value: T) extends Option[T] module None extends Option[nothing] class Pair[A, B](x: A, y: B) //│ abstract class Option[T] //│ class Some[T](value: T) extends Option //│ module None extends Option //│ class Pair[A, B](x: A, y: B) // All `add_n` functions should be inferred to have the same type. fun add_1(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ fun add_1: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_1(None, None) add_1(Some(5), None) add_1(None, Some(9)) add_1(Some(5), Some(9)) //│ Int //│ res //│ = 0 //│ res //│ = 5 //│ res //│ = 9 //│ res //│ = 14 fun add_2(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None then xv None and y is Some(yv) then yv None then 0 //│ fun add_2: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_2(None, None) add_2(Some(5), None) add_2(None, Some(9)) add_2(Some(5), Some(9)) //│ Int //│ res //│ = 0 //│ res //│ = 5 //│ res //│ = 9 //│ res //│ = 14 fun add_3(x, y) = if Pair(x, y) is Pair(Some(xv), Some(yv)) then xv + yv Pair(Some(xv), None) then xv Pair(None, Some(yv)) then yv Pair(None, None) then 0 //│ fun add_3: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_3(None, None) add_3(Some(5), None) add_3(None, Some(9)) add_3(Some(5), Some(9)) //│ Int //│ res //│ = 0 //│ res //│ = 5 //│ res //│ = 9 //│ res //│ = 14 fun add_4(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv is None then xv None and y is Some(yv) then yv is None then 0 //│ fun add_4: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_4(None, None) add_4(Some(5), None) add_4(None, Some(9)) add_4(Some(5), Some(9)) //│ Int //│ res //│ = 0 //│ res //│ = 5 //│ res //│ = 9 //│ res //│ = 14 fun add_5(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and x is Some(xv) then xv x is None and y is Some(yv) then yv y is None and x is None then 0 //│ fun add_5: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_5(None, None) add_5(Some(5), None) add_5(None, Some(9)) add_5(Some(5), Some(9)) //│ Int //│ res //│ = 0 //│ res //│ = 5 //│ res //│ = 9 //│ res //│ = 14 fun add_6(x, y) = if [x, y] is [Some(xv), Some(yv)] then xv + yv [Some(xv), None] then xv [None, Some(yv)] then yv [None, None] then 0 //│ fun add_6: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) add_6(None, None) add_6(Some(5), None) add_6(None, Some(9)) add_6(Some(5), Some(9)) //│ Int //│ res //│ = 0 //│ res //│ = 5 //│ res //│ = 9 //│ res //│ = 14 fun p(x) = true //│ fun p: anything -> true // FIXME Remove `case p(x) of true -> 0; _ -> 0` :ducs:postprocess.result fun add_6(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and p(x) and x is Some(xv) then 42 y is None and x is Some(xv) then xv x is None and y is Some(yv) then yv y is None and x is None then 0 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> //│ let ucs$args_x$Some*† = (Some).unapply(x,) //│ let xv*‡ = (ucs$args_x$Some).0 //│ case y*‡ of //│ Some*◊ -> //│ let ucs$args_y$Some*† = (Some).unapply(y,) //│ let yv*‡ = (ucs$args_y$Some).0 //│ +(xv, yv,) //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> 42 //│ _ -> xv //│ None*† -> //│ case y*‡ of //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 0 //│ _ -> 0 //│ Some*◊ -> //│ let ucs$args_y$Some*† = (Some).unapply(y,) //│ let yv*‡ = (ucs$args_y$Some).0 //│ yv //│ fun add_6: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) :ducs:postprocess.result fun add_7(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv // y is None and p(x) and x is Some(xv) then 42 y is None and x is Some(xv) then xv y is Some(yv) and p(yv) and x is None then 36 y is Some(yv) and x is None then yv y is None and x is None then 0 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> //│ let ucs$args_x$Some*† = (Some).unapply(x,) //│ let xv*‡ = (ucs$args_x$Some).0 //│ case y*‡ of //│ Some*◊ -> //│ let ucs$args_y$Some*† = (Some).unapply(y,) //│ let yv*‡ = (ucs$args_y$Some).0 //│ +(xv, yv,) //│ None*† -> xv //│ None*† -> //│ case y*‡ of //│ None*† -> 0 //│ Some*◊ -> //│ let ucs$args_y$Some*† = (Some).unapply(y,) //│ let yv*‡ = (ucs$args_y$Some).0 //│ let ucs$test$0*† = p(yv,) : Bool //│ case ucs$test$0*† of //│ true -> 36 //│ _ -> yv //│ fun add_7: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) // FIXME Remove `case p(x) of true -> 0; _ -> 0` :ducs:postprocess.result fun add_8(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv y is None and p(x) and x is Some(xv) then 42 y is None and x is Some(xv) then xv y is Some(yv) and p(yv) and x is None then 36 y is Some(yv) and x is None then yv y is None and x is None then 0 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> //│ let ucs$args_x$Some*† = (Some).unapply(x,) //│ let xv*‡ = (ucs$args_x$Some).0 //│ case y*‡ of //│ Some*◊ -> //│ let ucs$args_y$Some*† = (Some).unapply(y,) //│ let yv*‡ = (ucs$args_y$Some).0 //│ +(xv, yv,) //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> 42 //│ _ -> xv //│ None*† -> //│ case y*‡ of //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 0 //│ _ -> 0 //│ Some*◊ -> //│ let ucs$args_y$Some*† = (Some).unapply(y,) //│ let yv*‡ = (ucs$args_y$Some).0 //│ let ucs$test$1*† = p(yv,) : Bool //│ case ucs$test$1*† of //│ true -> 36 //│ _ -> yv //│ fun add_8: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) ================================================ FILE: shared/src/test/diff/pretyper/ucs/NamePattern.mls ================================================ :NewDefs fun id(x) = x //│ fun id: forall 'a. 'a -> 'a if id(0) is t then t //│ 0 //│ res //│ = 0 ================================================ FILE: shared/src/test/diff/pretyper/ucs/RecordPattern.mls ================================================ :NewDefs :e :w fun take_1(p) = if p is { x, y } then x + y else 0 //│ ╔══[ERROR] unknown pattern '{' {x: x, y: y} '}' //│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ║ l.8: else 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.7: { x, y } then x + y //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.7: { x, y } then x + y //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.7: { x, y } then x + y //│ ╙── ^ //│ fun take_1: anything -> Int //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: shared/src/test/diff/pretyper/ucs/Refinement.mls ================================================ :NewDefs module None class Some[out A](val value: A) //│ module None //│ class Some[A](value: A) // OK :ducs:desugar.result,postprocess.result x => if x is refined(None) then x //│ Desugared UCS term: //│ if x*‡ is refined None then x //│ Post-processed UCS term: //│ case x*‡ of //│ refined None*† -> x //│ forall 'a. (None & 'a) -> (None & 'a) //│ res //│ = [Function: res] // OK :ducs:desugar.result,postprocess.result x => if x is refined(Some) then x //│ Desugared UCS term: //│ if x*‡ is refined Some then x //│ Post-processed UCS term: //│ case x*‡ of //│ refined Some*◊ -> x //│ forall 'a. (Some[anything] & 'a) -> (Some[anything] & 'a) //│ res //│ = [Function: res] :w :ducs:desugar.result,postprocess.result x => if x is refined(None) then x Some then x //│ Desugared UCS term: //│ if //│ x*‡ is refined None then x //│ x*‡ is Some then x //│ Post-processed UCS term: //│ case x*‡ of //│ refined None*† -> x //│ Some*◊ -> x //│ ╔══[WARNING] inconsistent refined pattern //│ ║ l.37: refined(None) then x //│ ║ ^^^^ //│ ╟── pattern `Some` is not refined //│ ║ l.38: Some then x //│ ║ ^^^^ //│ ╟── but pattern `None` is refined //│ ║ l.37: refined(None) then x //│ ╙── ^^^^ //│ (None | Some[anything]) -> Some[nothing] //│ res //│ = [Function: res] :ducs:desugar.result,postprocess.result x => if x is refined(None) then x refined(Some) then x //│ Desugared UCS term: //│ if //│ x*‡ is refined None then x //│ x*‡ is refined Some then x //│ Post-processed UCS term: //│ case x*‡ of //│ refined None*† -> x //│ refined Some*◊ -> x //│ (None | Some[anything]) -> nothing //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/pretyper/ucs/SpecilizationCollision.mls ================================================ :NewDefs // This test file is to track possible name collision during specialization. fun (~~>) check(x, y) = if x === y then "passed" else error class Pair[out A, out B](val first: A, val second: B) //│ fun (~~>) check: forall 'a. (Eql['a], 'a) -> "passed" //│ class Pair[A, B](first: A, second: B) fun p1(x) = x < 0 fun p2(x) = x > 0 fun p3(x) = x == 0 //│ fun p1: Num -> Bool //│ fun p2: Num -> Bool //│ fun p3: Num -> Bool fun example1(p) = if p is Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then "both positive" else "nah" //│ fun example1: (Object & ~#Pair | Pair[Num, Num]) -> ("both negative" | "both positive" | "nah") // FIXME: The following test case should fail, but it doesn't. The reason is // `x` and `y` are in the desugared lexical scope, although they don't in the // original lexical scope. fun example2(p) = if p is Pair(x, y) and p1(x) and p1(y) then "both negative" Pair(a, b) and p2(a) and p2(b) then x + y else "nah" //│ fun example2: (Object & ~#Pair | Pair[Int, Int]) -> ("both negative" | "nah" | Int) // Next, let's check the name collision between a class and its super class. class Base(x: Int) class Derived(y: Int) extends Base(y + 1) //│ class Base(x: Int) //│ class Derived(y: Int) extends Base // Notice that Derived is not in the inferred type. :ducs:postprocess.result fun example3(t) = if t is Base(x) and p1(x) then x Derived(y) then y else 42 //│ Post-processed UCS term: //│ case t*‡ of //│ refined Base*◊ -> //│ let ucs$args_t$Base*† = (Base).unapply(t,) //│ let x*‡ = (ucs$args_t$Base).0 //│ let ucs$test$0*† = p1(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> x //│ _ -> //│ case t*‡ of //│ Derived*◊ -> //│ let ucs$args_t$Derived*† = (Derived).unapply(t,) //│ let y*‡ = (ucs$args_t$Derived).0 //│ y //│ _ -> 42 //│ _ -> 42 //│ fun example3: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base) -> (Int | 'a) example3(Derived(1)) //│ Int //│ res //│ = 1 fun example4(t, x) = if t is Base(x) and p1(x) then x Derived(y) then y + x // ^ // Note that this branch will be absorbed by the previous one. As the // previous branch shadows the variable `x`, a correct implementation // should restore the original value of `x` in this branch. else 42 //│ fun example4: forall 'a. (Base & {#x: Num & 'a} | Object & ~#Base, Int) -> (Int | 'a) example4(Base(-1), 0) ~~> -1 example4(Base(1), 2) ~~> 42 //│ "passed" //│ res //│ = 'passed' //│ res //│ = 'passed' example4(Derived(1), 4) ~~> 5 //│ "passed" //│ res //│ = 'passed' class Base(x: Int) class Derived[A](y: A) extends Base(1) //│ class Base(x: Int) //│ class Derived[A](y: A) extends Base // Notice that now Derived is generic, so it's appear in the inferred type. fun example5(t) = if t is Base(x) and p1(x) then x Derived(y) then y else 42 //│ fun example5: forall 'a 'b. (Base & {#x: Num & 'a} & ~#Derived | Derived['b] & {#x: Num & 'a} | Object & ~#Base) -> (42 | 'a | 'b) ================================================ FILE: shared/src/test/diff/pretyper/ucs/Symbol.mls ================================================ :NewDefs type List[A] = Cons[A] | Nil class Cons[A](head: A, tail: List[A]) module Nil //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) //│ module Nil fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] fun map(f, xs) = if xs is Cons(head, tail) then f(head) :: map(f, tail) Nil then Nil //│ fun map: forall 'A 'A0. ('A -> 'A0, Cons['A] | Nil) -> (Cons['A0] | Nil) // fun main$$5 = () => // let obj = // let obj = '(' (new List$1)(1, (new List$1)(2, (new Nil$2)(),),) ')' in // if obj is ‹(List$1) then map$List$1(obj, {Lambda1$2$3()},); else error› // in // if obj is ‹(List$1) then map$List$1(obj, {Lambda1$3$4()},); else error› // Got: Internal Error: Symbol already set for obj fun main(f) = let obj = (let obj = Cons(1, Cons(2, Nil)) in (if obj is Cons(head, tail) then f(head) :: tail else Nil)) in if obj is Cons(head, tail) then f(head) :: tail else Nil //│ fun main: forall 'A 'A0. ((1 | 2 | 'A) -> 'A) -> (Cons['A0] | Nil) //│ where //│ 'A <: 'A0 //│ 'A0 :> 1 | 2 //│ <: 'A ================================================ FILE: shared/src/test/diff/pretyper/ucs/Unapply.mls ================================================ :NewDefs class Point(x: Int, y: Int, z: Int) //│ class Point(x: Int, y: Int, z: Int) fun f_0(p) = if p is Point(x, _, z) then x + z //│ fun f_0: Point -> Int f_0(Point(1, 2, 3)) //│ Int //│ res //│ = 4 ================================================ FILE: shared/src/test/diff/pretyper/ucs/Unconditional.mls ================================================ :NewDefs class Point(x: Int, y: Int) class Rectangle(x: Int, y: Int, width: Int, height: Int) //│ class Point(x: Int, y: Int) //│ class Rectangle(x: Int, y: Int, width: Int, height: Int) fun sum(p) = if p is Point(x, y) then x + y //│ fun sum: Point -> Int sum(Point(1, 2)) //│ Int //│ res //│ = 3 fun abs(x) = if x < 0 then -x else x //│ fun abs: Int -> Int fun dist(p, q) = if p is Point(x1, y1) and q is Point(x2, y2) then abs(x1 - x2) + abs(y1 - y2) //│ fun dist: (Point, Point) -> Int dist(Point(1, 2), Point(3, 4)) //│ Int //│ res //│ = 4 ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/ConflictedCoveredCases.mls ================================================ :NewDefs class A() class B() extends A() //│ class A() //│ class B() extends A fun p(x) = true //│ fun p: anything -> true :w :ducs:normalize.result fun f(x) = if x is B and x is A then 1 x is A then 31 //│ Normalized UCS term: //│ case x*‡ of //│ B*◊ -> 1 //│ _ -> //│ case x*‡ of //│ A*◊ -> 31 //│ ╔══[WARNING] the pattern always matches //│ ║ l.15: x is A then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against B //│ ║ l.14: x is B and //│ ║ ^ //│ ╟── which is a subtype of A //│ ║ l.4: class B() extends A() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: (A & ~#B | B) -> (1 | 31) // FIXME: wrong missing cases :w :ducs:normalize.result fun f(x) = if x is 1 and x is Int then true x is 2 then false //│ Normalized UCS term: //│ case x*‡ of //│ 1 -> true //│ _ -> //│ case x*‡ of //│ 2 -> false //│ ╔══[WARNING] the pattern always matches //│ ║ l.39: x is Int then true //│ ║ ^^^ //│ ╟── the scrutinee was matched against 1 //│ ║ l.38: x is 1 and //│ ║ ^ //│ ╟── which is a subtype of Int //│ ║ l.39: x is Int then true //│ ╙── ^^^ //│ ╔══[ERROR] `x` has 1 missing case //│ ║ l.38: x is 1 and //│ ║ ^ //│ ╟── it can be class `Int` //│ ║ l.39: x is Int then true //│ ╙── ^^^ //│ fun f: (1 | 2) -> Bool :w fun f(x) = if x is B and x is A then 1 p(x) then 2 x is A then 31 x is B then 3 else 4 //│ ╔══[WARNING] the pattern always matches //│ ║ l.67: x is A then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against B //│ ║ l.66: x is B and //│ ║ ^ //│ ╟── which is a subtype of A //│ ║ l.4: class B() extends A() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: (B | Object & ~#B) -> (1 | 31 | 4) ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/ConflictedPatterns.mls ================================================ :NewDefs // This test file contains the cases where inner cases are conflicting with outer cases. class X class Y class Z //│ class X { //│ constructor() //│ } //│ class Y { //│ constructor() //│ } //│ class Z { //│ constructor() //│ } :w :ducs:normalize.result fun f(x) = if x is X and x is Y then 1 else 2 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.21: x is X and x is Y then 1 //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.21: x is X and x is Y then 1 //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.21: x is X and x is Y then 1 //│ ╙── ^ //│ fun f: Object -> 2 :w :ducs:normalize.result fun f(x) = if x is X and x is Y then 1 x is Y then 2 else 3 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.43: x is Y then 1 //│ ╙── ^ //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.42: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.44: x is Y then 2 //│ ╙── ^ //│ fun f: Object -> 3 :w :ducs:normalize.result fun f(x) = if x is X and x is Y then 1 x is Z then 2 else 3 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 3 //│ _ -> 3 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.74: x is Y then 1 //│ ╙── ^ //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.73: x is X and //│ ║ ^ //│ ╟── which is unrelated with Z //│ ║ l.75: x is Z then 2 //│ ╙── ^ //│ fun f: Object -> 3 :w :ducs:normalize.result fun f(x) = if x is X and x is Y and x is Z then 1 else 2 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 2 //│ _ -> 2 //│ ╔══[WARNING] possibly conflicting patterns for this scrutinee //│ ║ l.104: x is X and //│ ║ ^ //│ ╟── the scrutinee was matched against X //│ ║ l.104: x is X and //│ ║ ^ //│ ╟── which is unrelated with Y //│ ║ l.105: x is Y and //│ ╙── ^ //│ fun f: Object -> 2 ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/CoveredCases.mls ================================================ :NewDefs // This test file contains the cases where latter cases are covered by the former cases. // B <: A class A() class B() extends A() //│ class A() //│ class B() extends A :w fun f(x) = if x is A then 1 x is A then 2 //│ ╔══[WARNING] found a duplicated case //│ ║ l.14: x is A then 2 //│ ║ ^ //│ ╟── there is an identical pattern A //│ ║ l.13: x is A then 1 //│ ╙── ^ //│ fun f: A -> 1 :w :ducs:normalize.result fun f(x) = if x is A then 1 x is B then 2 //│ Normalized UCS term: //│ case x*‡ of //│ A*◊ -> 1 //│ ╔══[WARNING] found a duplicated case //│ ║ l.27: x is B then 2 //│ ║ ^ //│ ╟── the case is covered by pattern A //│ ║ l.26: x is A then 1 //│ ║ ^ //│ ╟── due to the subtyping relation //│ ║ l.7: class B() extends A() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ fun f: A -> 1 :ducs:normalize.result fun f(x) = if x is A and x is B then 1 //│ Normalized UCS term: //│ case x*‡ of //│ refined A*◊ -> //│ case x*‡ of //│ B*◊ -> 1 //│ fun f: B -> 1 // :w // FIXME :ducs:normalize.result fun f(x) = if x is A and x is B then 1 x is B then 2 //│ Normalized UCS term: //│ case x*‡ of //│ refined A*◊ -> //│ case x*‡ of //│ B*◊ -> 1 //│ fun f: B -> 1 fun p(x) = true: Bool //│ fun p: anything -> Bool // :w // FIXME :ducs:normalize.result fun f(x) = if x is A and x is B then 1 p(x) then 2 x is B then 3 else 4 //│ Normalized UCS term: //│ case x*‡ of //│ refined A*◊ -> //│ case x*‡ of //│ B*◊ -> 1 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> 2 //│ _ -> 4 //│ _ -> 4 //│ fun f: Object -> (1 | 2 | 4) ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/DuplicatedCases.mls ================================================ :NewDefs // This test file contains the cases containing cases with identical patterns. :w :ducs:normalize.result fun f(x) = if x is "a" then 1 "b" then 2 "a" then 3 //│ Normalized UCS term: //│ case x*‡ of //│ "a" -> 1 //│ _ -> //│ case x*‡ of //│ "b" -> 2 //│ ╔══[WARNING] found a duplicated case //│ ║ l.10: "a" then 3 //│ ║ ^^^ //│ ╟── there is an identical pattern "a" //│ ║ l.8: "a" then 1 //│ ╙── ^^^ //│ fun f: ("a" | "b") -> (1 | 2) class X class Y //│ class X { //│ constructor() //│ } //│ class Y { //│ constructor() //│ } :w :ducs:normalize.result fun f(x) = if x is X then 1 X then 2 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 1 //│ ╔══[WARNING] found a duplicated case //│ ║ l.38: X then 2 //│ ║ ^ //│ ╟── there is an identical pattern X //│ ║ l.37: X then 1 //│ ╙── ^ //│ fun f: X -> 1 :w :ducs:normalize.result fun f(x) = if x is X then 1 Y then 2 X then 3 //│ Normalized UCS term: //│ case x*‡ of //│ X*◊ -> 1 //│ _ -> //│ case x*‡ of //│ Y*◊ -> 2 //│ ╔══[WARNING] found a duplicated case //│ ║ l.55: X then 3 //│ ║ ^ //│ ╟── there is an identical pattern X //│ ║ l.53: X then 1 //│ ╙── ^ //│ fun f: (X | Y) -> (1 | 2) class Box[T](value: T) //│ class Box[T](value: T) :ducs:normalize.result fun f(x) = if x is Box(1) then true Box then false //│ Normalized UCS term: //│ case x*‡ of //│ Box*◊ -> //│ let ucs$args_x$Box*† = (Box).unapply(x,) //│ let x$Box_0*‡ = (ucs$args_x$Box).0 //│ case x$Box_0*‡ of //│ 1 -> true //│ _ -> false //│ fun f: Box[Object] -> Bool f(Box(0)) f(Box(1)) //│ Bool //│ res //│ = false //│ res //│ = true :ducs:postprocess.result fun a_tale_of_scrutinees(x, y) = if x is "A" and y is "B" then "AB" y is "A" and x is "B" then "BA" y is "A" and x is "A" then "AA" x is "B" and y is "B" then "BB" //│ Post-processed UCS term: //│ case x*‡ of //│ "A" -> //│ case y*‡ of //│ "B" -> "AB" //│ "A" -> "AA" //│ "B" -> //│ case y*‡ of //│ "A" -> "BA" //│ "B" -> "BB" //│ fun a_tale_of_scrutinees: ("A" | "B", "A" | "B") -> ("AA" | "AB" | "BA" | "BB") :ducs:normalize.result fun test(x, p) = if x is Bool and p(x) then "great" true then "false" false then "true" //│ Normalized UCS term: //│ case x*‡ of //│ refined Bool*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "great" //│ _ -> //│ case x*‡ of //│ true*† -> "false" //│ _ -> //│ case x*‡ of //│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") class P[A](x: A) class Q[A, B](x: A, y: B) //│ class P[A](x: A) //│ class Q[A, B](x: A, y: B) fun f(x) = if x is P(1) then 1 P(y) then 2 //│ fun f: P[Object] -> (1 | 2) :ducs:normalize.result fun f(x) = if x is Q(a, b) and a is 1 and b is 1 then 1 Q(a, b) and b is 1 then 2 //│ Normalized UCS term: //│ case x*‡ of //│ Q*◊ -> //│ let ucs$args_x$Q*† = (Q).unapply(x,) //│ let a*‡ = (ucs$args_x$Q).0 //│ let b*‡ = (ucs$args_x$Q).1 //│ case a*‡ of //│ 1 -> //│ case b*‡ of //│ 1 -> 1 //│ _ -> //│ case b*‡ of //│ 1 -> 2 //│ fun f: Q[Object, 1] -> (1 | 2) :e fun f(x) = if x is Q(a, b) and a is 1 and b is 2 then 1 Q(a, b) and b is 1 then 2 //│ ╔══[ERROR] when `x` is `Q` //│ ║ l.167: Q(a, b) and a is 1 and b is 2 then 1 //│ ║ ^ //│ ╟── the second argument of `Q` has 1 missing case //│ ║ l.168: Q(a, b) and b is 1 then 2 //│ ║ ^ //│ ╟── it can be literal 2 //│ ║ l.167: Q(a, b) and a is 1 and b is 2 then 1 //│ ╙── ^ //│ fun f: Q[Object, 1] -> (1 | 2) :ducs:normalize.result fun f(x) = if x is Q(1, 1) then 1 Q(y, 1) then 2 //│ Normalized UCS term: //│ case x*‡ of //│ Q*◊ -> //│ let ucs$args_x$Q*† = (Q).unapply(x,) //│ let x$Q_0*‡ = (ucs$args_x$Q).0 //│ let x$Q_1*‡ = (ucs$args_x$Q).1 //│ case x$Q_0*‡ of //│ 1 -> //│ case x$Q_1*‡ of //│ 1 -> 1 //│ _ -> (ucs$args_x$Q).0 //│ _ -> //│ let y*‡ = (ucs$args_x$Q).0 //│ case x$Q_1*‡ of //│ 1 -> 2 //│ fun f: forall 'a. Q[Object & 'a, 1] -> (1 | 2 | 'a) fun f(x) = if x is Q(0, 0) then 1 Q(1, 1) then 2 Q(y, 1) then 3 _ then 4 //│ fun f: (Object & ~#Q | Q[Object, Object]) -> (1 | 2 | 3 | 4) fun f(x) = if x is P(P(P(1))) then 1 P(P(1)) then 2 P(1) then 3 //│ fun f: P[1 | P[1 | P[1]]] -> (1 | 2 | 3) ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/MissingCases.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun good_add_1(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ fun good_add_1: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) :e fun bad_add_missing_SS(x, y) = if x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.23: x is Some(xv) and y is None then xv //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.24: x is None and y is Some(yv) then yv //│ ╙── ^^^^ //│ fun bad_add_missing_SS: forall 'a. (None | Some['a], None) -> (0 | 'a) :e fun bad_add_missing_SN(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is None and y is Some(yv) then yv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.40: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.42: x is None and y is None then 0 //│ ╙── ^^^^ //│ fun bad_add_missing_SN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) :e fun bad_add_missing_NS(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `None` //│ ║ l.59: x is None and y is None then 0 //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.59: x is None and y is None then 0 //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.57: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun bad_add_missing_NS: (None | Some[Int], None) -> Int :e fun bad_add_missing_NN(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv //│ ╔══[ERROR] when `x` is `None` //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.76: x is None and y is Some(yv) then yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.75: x is Some(xv) and y is None then xv //│ ╙── ^^^^ //│ fun bad_add_missing_NN: forall 'a. (None | Some[Int], Some[Int & 'a]) -> (Int | 'a) :e fun bad_add_missing_SS_NN(x, y) = if x is Some(xv) and y is None then xv x is None and y is Some(yv) then yv //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.91: x is Some(xv) and y is None then xv //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `None` //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.92: x is None and y is Some(yv) then yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.91: x is Some(xv) and y is None then xv //│ ╙── ^^^^ //│ fun bad_add_missing_SS_NN: forall 'a. (None | Some['a], nothing) -> 'a :e fun bad_add_missing_SN_NS(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv x is None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.117: x is None and y is None then 0 //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `None` //│ ║ l.117: x is None and y is None then 0 //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.117: x is None and y is None then 0 //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.116: x is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun bad_add_missing_SN_NS: (None | Some[Int], nothing) -> Int fun actually_fine_add(x, y) = if x is Some(xv) and y is None then xv //│ fun actually_fine_add: forall 'a. (Some['a], None) -> 'a ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/Refinement.mls ================================================ :NewDefs class Point(x: Int, y: Int) abstract class Shape: (Circle | Rectangle | LineSegment) class Circle(center: Point, radius: Int) extends Shape class Rectangle(position: Point, width: Int, height: Int) extends Shape class LineSegment(start: Point, end: Point) extends Shape //│ class Point(x: Int, y: Int) //│ abstract class Shape: Circle | LineSegment | Rectangle //│ class Circle(center: Point, radius: Int) extends Shape //│ class Rectangle(position: Point, width: Int, height: Int) extends Shape //│ class LineSegment(start: Point, end: Point) extends Shape fun hidden(p) = if p is Circle then true else false //│ fun hidden: Object -> Bool fun this_is_sealed(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" LineSegment(_, _) then "LineSegment" //│ fun this_is_sealed: (x: Shape) -> ("Circle" | "LineSegment" | "Rectangle") // The error message here makes some sense right now. :e fun missing_a_case(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.27: if x is //│ ║ ^^^^ //│ ║ l.28: Circle(_, _) then "Circle" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.29: Rectangle(_, _, _) then "Rectangle" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `Circle | LineSegment | Rectangle` does not match type `Circle | Rectangle` //│ ║ l.26: fun missing_a_case(x: Shape) = //│ ║ ^^^^^ //│ ╟── but it flows into reference with expected type `Circle | Rectangle` //│ ║ l.27: if x is //│ ╙── ^ //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle") fun missing_a_case(x: Shape) = if x is Circle(_, _) then "Circle" Rectangle(_, _, _) then "Rectangle" else x //│ fun missing_a_case: (x: Shape) -> ("Circle" | "Rectangle" | LineSegment) fun countLineSegments(x) = if x is Shape and hidden(x) then "1" Rectangle(_, _, _) then "2" LineSegment(_, _) then "3" Circle(_, _) then "4" //│ fun countLineSegments: (Circle | LineSegment | Rectangle) -> ("1" | "2" | "3" | "4") ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/SealedClasses.mls ================================================ :NewDefs abstract class Term: Abs | App | Var class Var(name: Str) extends Term class Abs(param: Str, body: Term) extends Term class App(func: Term, arg: Term) extends Term //│ abstract class Term: Abs | App | Var //│ class Var(name: Str) extends Term //│ class Abs(param: Str, body: Term) extends Term //│ class App(func: Term, arg: Term) extends Term :ducs:desugar.result fun is_value_explicit_refinement(term) = if term is refined(Term) and term is Abs(_, _) then true Var(_) then false App(_, _) then false //│ Desugared UCS term: //│ if term*‡ is refined Term //│ term*‡ is Abs then true //│ term*‡ is Var then false //│ term*‡ is App then false //│ fun is_value_explicit_refinement: (Abs | App | Var) -> Bool :ducs:normalize.result,postprocess.result fun is_value_automatic_refinement(term) = if term is Term and term is Abs(_, _) then true Var(_) then false App(_, _) then false //│ Normalized UCS term: //│ case term*‡ of //│ refined Term*◊ -> //│ case term*‡ of //│ Abs*◊ -> true //│ _ -> //│ case term*‡ of //│ Var*◊ -> false //│ _ -> //│ case term*‡ of //│ App*◊ -> false //│ Post-processed UCS term: //│ case term*‡ of //│ refined Term*◊ -> //│ case term*‡ of //│ Abs*◊ -> true //│ Var*◊ -> false //│ App*◊ -> false //│ fun is_value_automatic_refinement: (Abs | App | Var) -> Bool fun is_value'(term) = if term is Term and term is Abs(_, _) then true Var(_) then false //│ fun is_value': (Abs | Var) -> Bool ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/Tautology.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun useless_negate_1(x) = if x is Some(y) and x is Some(z) then y + z //│ fun useless_negate_1: Some[Int] -> Int useless_negate_1(Some(1)) //│ Int //│ res //│ = 2 ================================================ FILE: shared/src/test/diff/pretyper/ucs/coverage/Unreachable.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(xv) and xv === 1 then true else false //│ fun f: (Object & ~#Some | Some[Eql[1]]) -> Bool f(Some(1)) f(Some(2)) f(None) //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = false fun reachable_1(x) = if x is _ and f(x) then "cos" Some(xv) then "sin" None then "tan" //│ fun reachable_1: (None | Some[anything] & ~#Some | Some[Eql[1]]) -> ("cos" | "sin" | "tan") reachable_1(Some(1)) reachable_1(Some(2)) reachable_1(None) //│ "cos" | "sin" | "tan" //│ res //│ = 'cos' //│ res //│ = 'sin' //│ res //│ = 'tan' :w fun unreachable_1(x) = if x is _ and f(x) then "tmux" else "screen" Some(xv) then "sin" None then "tan" //│ ╔══[WARNING] this case is unreachable //│ ║ l.46: if x is //│ ║ ^^^^ //│ ║ l.47: _ and //│ ║ ^^^^^^^^^ //│ ║ l.48: f(x) then "tmux" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.49: else "screen" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ║ l.50: Some(xv) then "sin" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.51: None then "tan" //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── because it is subsumed by the branch //│ ║ l.49: else "screen" //│ ╙── ^^^^^^^^ //│ fun unreachable_1: (Object & ~#Some | Some[Eql[1]]) -> ("screen" | "tmux") ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/AVLTree.mls ================================================ :NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error fun (?) max(x, y) = if x > y then x else y fun abs(x) = if x < 0 then -x else x //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () //│ fun ( 'd //│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e //│ fun abs: Int -> Int abstract class Option[out T]: (Some[T] | None) class Some[out T](val value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option //│ module None extends Option fun (??) getOrElse(o, v) = if o is Some(v') then v' None then v //│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a fun (++) strcat(s1, s2) = concat(s1)(s2) //│ fun (++) strcat: (Str, Str) -> Str let anyToString = toString //│ let anyToString: anything -> Str //│ anyToString //│ = [Function: toString] abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[nothing] //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] abstract class Tree[out A]: (Empty | Node[A]) class Node[out A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree[A] module Empty extends Tree[nothing] //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A], height: Int) extends Tree //│ module Empty extends Tree fun showTree(t: Tree[anything]): Str = if t is Node(v, l, r, _) then "(" ++ showTree(l) ++ " " ++ toString(v) ++ " " ++ showTree(r) ++ ")" Empty then "•" //│ fun showTree: (t: Tree[anything]) -> Str fun showTreeWithHeight(t: Tree[anything]): Str = if t is Node(v, l, r, h) then "(" ++ showTreeWithHeight(l) ++ " " ++ toString(v) ++ " [" ++ toString(h) ++ "]" ++ " " ++ showTreeWithHeight(r) ++ ")" Empty then "•" //│ fun showTreeWithHeight: (t: Tree[anything]) -> Str fun height(t) = if t is Node(_, _, _, h) then h Empty then 0 //│ fun height: (Empty | Node[anything]) -> Int let nil = Empty fun node(v, l, r) = Node(v, l, r, max(height(l), height(r)) + 1) fun single(v) = Node(v, Empty, Empty, 1) //│ let nil: Empty //│ fun node: forall 'A. ('A, Empty & Tree['A] | Node[anything] & Tree['A], Empty & Tree['A] | Node[anything] & Tree['A]) -> Node['A] //│ fun single: forall 'A0. 'A0 -> Node['A0] //│ nil //│ = Empty { class: [class Empty extends Tree] } fun isBalanced(t) = if t is Node(t, l, r, _) then (abs(height(l) - height(r)) <= 1) && isBalanced(l) && isBalanced(r) Empty then true //│ fun isBalanced: (Empty | Node[anything]) -> Bool isBalanced(nil) ~~> true //│ () //│ res //│ = undefined // _ _ _ _ // ___(_)_ __ __ _| | ___ _ __ ___ | |_ __ _| |_ ___ // / __| | '_ \ / _` | |/ _ \ | '__/ _ \| __/ _` | __/ _ \ // \__ \ | | | | (_| | | __/ | | | (_) | || (_| | || __/ // |___/_|_| |_|\__, |_|\___| |_| \___/ \__\__,_|\__\___| // |___/ fun rotateLeft(t: Tree['A]): Tree['A] = if t is Node(v, l, Node(v', l', r', _), _) and max(height(l), height(l')) + 1 is h and max(h, height(r')) + 1 is h' then Node(v', Node(v, l, l', h), r', h') _ then t //│ fun rotateLeft: forall 'A. (t: Tree['A]) -> Tree['A] rotateLeft(nil) |> showTree rotateLeft(single(0)) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' let unbalanced1 = node(0, nil, node(1, nil, single(2))) isBalanced(unbalanced1) ~~> false unbalanced1 |> showTree rotateLeft(unbalanced1) |> showTree //│ let unbalanced1: Node[0 | 1 | 2] //│ Str //│ unbalanced1 //│ = Node {} //│ res //│ = undefined //│ res //│ = '(• 0 (• 1 (• 2 •)))' //│ res //│ = '((• 0 •) 1 (• 2 •))' fun rotateRight(t: Tree['A]): Tree['A] = if t is Node(v, Node(v', l', r', _), r, _) then Node(v', l', Node(v, r', r, 0), 0) _ then t //│ fun rotateRight: forall 'A. (t: Tree['A]) -> Tree['A] rotateRight(nil) |> showTree rotateRight(single(0)) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' let unbalanced2 = node(2, node(1, single(0), nil), nil) isBalanced(unbalanced2) ~~> false unbalanced2 |> showTree rotateRight(unbalanced2) |> showTree //│ let unbalanced2: Node[0 | 1 | 2] //│ Str //│ unbalanced2 //│ = Node {} //│ res //│ = undefined //│ res //│ = '(((• 0 •) 1 •) 2 •)' //│ res //│ = '((• 0 •) 1 (• 2 •))' // _ _ _ _ _ // __| | ___ _ _| |__ | | ___ _ __ ___ | |_ __ _| |_ ___ // / _` |/ _ \| | | | '_ \| |/ _ \ | '__/ _ \| __/ _` | __/ _ \ // | (_| | (_) | |_| | |_) | | __/ | | | (_) | || (_| | || __/ // \__,_|\___/ \__,_|_.__/|_|\___| |_| \___/ \__\__,_|\__\___| // fun rotateRightLeft(t: Tree['A]): Tree['A] = if t is Node(v, t1, Node(v', Node(v'', t2, t3, _), t4, _), _) then Node(v'', Node(v, t1, t2, 0), Node(v', t3, t4, 0), 0) else t //│ fun rotateRightLeft: forall 'A. (t: Tree['A]) -> Tree['A] // Should remain the same. rotateRightLeft(nil) |> showTree rotateRightLeft(single(0)) |> showTree rotateRightLeft(unbalanced1) |> showTree rotateRightLeft(unbalanced2) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 (• 1 (• 2 •)))' //│ res //│ = '(((• 0 •) 1 •) 2 •)' let unbalanced3 = node(0, nil, node(3, node(1, nil, single(2)), nil)) isBalanced(unbalanced3) ~~> false unbalanced3 |> showTree rotateRightLeft(unbalanced3) |> showTree //│ let unbalanced3: Node[0 | 1 | 2 | 3] //│ Str //│ unbalanced3 //│ = Node {} //│ res //│ = undefined //│ res //│ = '(• 0 ((• 1 (• 2 •)) 3 •))' //│ res //│ = '((• 0 •) 1 ((• 2 •) 3 •))' fun rotateLeftRight(t: Tree['A]): Tree['A] = if t is Node(v, Node(v', t1, Node(v'', t2, t3, _), _), t4, _) then Node(v'', Node(v', t1, t2, 0), Node(v, t3, t4, 0), 0) else t //│ fun rotateLeftRight: forall 'A. (t: Tree['A]) -> Tree['A] // Should remain the same. rotateLeftRight(nil) |> showTree rotateLeftRight(single(0)) |> showTree rotateLeftRight(unbalanced1) |> showTree rotateLeftRight(unbalanced2) |> showTree //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 (• 1 (• 2 •)))' //│ res //│ = '(((• 0 •) 1 •) 2 •)' let unbalanced4 = node(3, node(0, nil, node(2, single(1), nil)), nil) isBalanced(unbalanced4) ~~> false unbalanced4 |> showTree rotateRightLeft(unbalanced4) |> showTree //│ let unbalanced4: Node[0 | 1 | 2 | 3] //│ Str //│ unbalanced4 //│ = Node {} //│ res //│ = undefined //│ res //│ = '((• 0 ((• 1 •) 2 •)) 3 •)' //│ res //│ = '((• 0 ((• 1 •) 2 •)) 3 •)' fun bf(t) = if t is Node(_, l, r, _) then height(l) - height(r) Empty then 0 //│ fun bf: (Empty | Node[anything]) -> Int // _ _ // (_)_ __ ___ ___ _ __| |_ // | | '_ \/ __|/ _ \ '__| __| // | | | | \__ \ __/ | | |_ // |_|_| |_|___/\___|_| \__| // // This function does not work for now as it exposed a lot of problems we have // in desugaring and normalization. For example: // // - [x] We need to mark the Boolean scrutinees and skip the specialization of // these scrutinees in the normalization process, otherwise, it would result // in many futile computations. // - [x] We should cache the expressions that are broken by conditional splits, // otherwise, they will be evaluated for more than one time. // - [ ] The branches of an operator split should be "chained" rather // than placed in parallel, otherwise, the later branches will appear in the // else branch of all its previous branches. **WORK IN PROGRESS** // // :ducs:postprocess fun balance(t: Tree['A]): Tree['A] = if t is Node(x, l, r, _) and height(r) - height(l) > 1 and r is Node(y, l', r', _) and height(r') - height(l') > 0 then rotateLeft(t) < 0 and l' is Node then rotateRightLeft(t) < 1 and l is Node(y, l', r', _) and height(r') - height(l') > 0 and r' is Node then rotateLeftRight(t) < 0 then rotateRight(t) _ then t //│ fun balance: forall 'A. (t: Tree['A]) -> Tree['A] fun insert(t: Tree[Num], v: Num) = if t is Node(v', l, r, h) and v < v' then Node(v', insert(l, v), r, h) |> balance v > v' then Node(v', l, insert(r, v), h) |> balance _ then t Empty then Node(v, Empty, Empty, 1) //│ fun insert: (t: Tree[Num], v: Num) -> (Node[Num] | Tree[Num]) insert(nil, 0) |> showTree insert(single(0), 1) |> showTree insert(single(0), -1) |> showTree //│ Str //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 (• 1 •))' //│ res //│ = '((• -1 •) 0 •)' insert(node(0, nil, single(1)), 2) |> showTreeWithHeight insert(node(0, nil, single(1)), 2) |> showTree //│ Str //│ res //│ = '(• 0 [2] (• 1 [1] (• 2 [1] •)))' //│ res //│ = '(• 0 (• 1 (• 2 •)))' insert(unbalanced1, 3) |> showTree //│ Str //│ res //│ = '((• 0 •) 1 (• 2 (• 3 •)))' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/BinarySearchTree.mls ================================================ :NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error fun (?) max(x, y) = if x > y then x else y fun abs(x) = if x < 0 then -x else x //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () //│ fun ( 'd //│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e //│ fun abs: Int -> Int abstract class Option[T]: (Some[T] | None) class Some[T](val value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option //│ module None extends Option fun (??) getOrElse(o, v) = if o is Some(v') then v' None then v //│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a fun (++) strcat(s1, s2) = concat(s1)(s2) //│ fun (++) strcat: (Str, Str) -> Str let anyToString = toString //│ let anyToString: anything -> Str //│ anyToString //│ = [Function: toString] abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[nothing] //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] 1 :: 2 :: 3 :: 4 :: Nil //│ Cons[1 | 2 | 3 | 4] //│ res //│ = Cons {} abstract class Tree[out A]: (Empty | Node[A]) class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] module Empty extends Tree //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree //│ module Empty extends Tree fun single(v) = Node(v, Empty, Empty) //│ fun single: forall 'A. 'A -> Node['A] fun show(t: Tree['A]): Str = if t is Node(v, l, r) then "(" ++ show(l) ++ " " ++ toString(v) ++ " " ++ show(r) ++ ")" Empty then "•" //│ fun show: (t: Tree[anything]) -> Str show(Empty) show(Node(0, Empty, Empty)) show(Node(1, Node(0, Empty, Empty), Empty)) show(Node(1, Node(0, Empty, Empty), Node(2, Empty, Empty))) //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '((• 0 •) 1 •)' //│ res //│ = '((• 0 •) 1 (• 2 •))' fun insert(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) v > v' then Node(v', l, insert(r, v)) _ then t Empty then Node(v, Empty, Empty) //│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) fun insert'(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) > v' then Node(v', l, insert(r, v)) else t Empty then Node(v, Empty, Empty) //│ fun insert': forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) insert(Empty, 0) |> show insert(Node(0, Empty, Empty), 0) |> show insert(Node(1, Empty, Empty), 0) |> show insert(Node(1, Node(0, Empty, Empty), Empty), 0) |> show insert(Node(1, Node(0, Empty, Empty), Empty), 2) |> show //│ Str //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 •)' //│ res //│ = '((• 0 •) 1 •)' //│ res //│ = '((• 0 •) 1 •)' //│ res //│ = '((• 0 •) 1 (• 2 •))' fun fromList(l) = let rec fromList'(t, xs) = if xs is Cons(x, xs') then fromList'(insert(t, x), xs') Nil then t fromList'(Empty, l) //│ fun fromList: forall 'A. (Cons[Num & 'A] | Nil) -> (Empty | Node['A]) fromList(1 :: 2 :: 3 :: 4 :: Nil) |> show fromList(2 :: 1 :: 4 :: 3 :: Nil) |> show fromList(4 :: 3 :: 2 :: 1 :: Nil) |> show let example1 = fromList(1 :: 3 :: 2 :: 4 :: Nil) example1 |> show //│ let example1: Empty | Node[1 | 2 | 3 | 4] //│ Str //│ res //│ = '(• 1 (• 2 (• 3 (• 4 •))))' //│ res //│ = '((• 1 •) 2 ((• 3 •) 4 •))' //│ res //│ = '((((• 1 •) 2 •) 3 •) 4 •)' //│ example1 //│ = Node {} //│ res //│ = '(• 1 ((• 2 •) 3 (• 4 •)))' fun contains(t, v) = if t is Node(v', l, r) and v < v' then contains(l, v) v > v' then contains(r, v) _ then true Empty then false //│ fun contains: (Empty | Node[Num], Num) -> Bool // Writing tests like this is very interesting. contains(Empty, 0) ~~> false contains(Node(0, Empty, Empty), 0) ~~> true contains(Node(1, Empty, Empty), 0) ~~> false //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined fun minValue(t) = if t is Empty then None Node(v, Empty, _) then Some(v) Node(_, l, _) then minValue(l) //│ fun minValue: forall 'T. (Empty | Node['T]) -> (None | Some['T]) minValue(Empty) ?? "not found" minValue(Node(0, Empty, Empty)) ?? "not found" minValue(example1) ?? "not found" //│ "not found" | 1 | 2 | 3 | 4 //│ res //│ = 'not found' //│ res //│ = 0 //│ res //│ = 1 fun maxValue(t) = if t is Empty then None Node(v, _, Empty) then Some(v) Node(_, _, r) then maxValue(r) //│ fun maxValue: forall 'T. (Empty | Node['T]) -> (None | Some['T]) maxValue(Empty) ?? "not found" maxValue(Node(0, Empty, Empty)) ?? "not found" maxValue(example1) ?? "not found" //│ "not found" | 1 | 2 | 3 | 4 //│ res //│ = 'not found' //│ res //│ = 0 //│ res //│ = 4 fun lowerBound(t, v) = if t is Node(v', l, r) and v < v' then lowerBound(l, v) v > v' then Some(lowerBound(r, v) ?? v') _ then Some(v') Empty then None //│ fun lowerBound: forall 'T. (Empty | Node[Num & 'T], Num) -> (None | Some['T]) lowerBound(Empty, 0) ?? "not found" lowerBound(Node(0, Empty, Empty), 0) ?? "not found" lowerBound(Node(1, Empty, Empty), 0) ?? "not found" lowerBound(Node(-1, Empty, Empty), 0) ?? "not found" //│ "not found" | -1 //│ res //│ = 'not found' //│ res //│ = 0 //│ res //│ = 'not found' //│ res //│ = -1 lowerBound(example1, 0) ?? "not found" lowerBound(example1, 1) ?? "not found" lowerBound(example1, 2) ?? "not found" lowerBound(example1, 3) ?? "not found" lowerBound(example1, 4) ?? "not found" lowerBound(example1, 5) ?? "not found" //│ "not found" | 1 | 2 | 3 | 4 //│ res //│ = 'not found' //│ res //│ = 1 //│ res //│ = 2 //│ res //│ = 3 //│ res //│ = 4 //│ res //│ = 4 let example2 = fromList(1 :: 5 :: 42 :: 10 :: 23 :: 59 :: 81 :: Nil) lowerBound(example2, 0) ?? "not found" lowerBound(example2, 25) ?? "not found" lowerBound(example2, 99) ?? "not found" lowerBound(example2, 7) ?? "not found" lowerBound(example2, 32) ?? "not found" lowerBound(example2, 41) ?? "not found" //│ let example2: Empty | Node[1 | 10 | 23 | 42 | 5 | 59 | 81] //│ "not found" | 1 | 10 | 23 | 42 | 5 | 59 | 81 //│ example2 //│ = Node {} //│ res //│ = 'not found' //│ res //│ = 23 //│ res //│ = 81 //│ res //│ = 5 //│ res //│ = 23 //│ res //│ = 23 fun upperBound(t, v) = if t is Node(v', l, r) and v < v' then Some(upperBound(l, v) ?? v') v > v' then upperBound(r, v) _ then Some(v') Empty then None //│ fun upperBound: forall 'T. (Empty | Node[Num & 'T], Num) -> (None | Some['T]) upperBound(example2, 0) ?? "not found" upperBound(example2, 25) ?? "not found" upperBound(example2, 99) ?? "not found" upperBound(example2, 7) ?? "not found" upperBound(example2, 32) ?? "not found" upperBound(example2, 41) ?? "not found" //│ "not found" | 1 | 10 | 23 | 42 | 5 | 59 | 81 //│ res //│ = 1 //│ res //│ = 42 //│ res //│ = 'not found' //│ res //│ = 10 //│ res //│ = 42 //│ res //│ = 42 fun remove(t, v) = if t is Node(v', l, r) and v < v' then Node(v', remove(l, v), r) v > v' then Node(v', l, remove(r, v)) minValue(r) is None then l Some(v'') then Node(v'', l, remove(r, v'')) Empty then Empty //│ fun remove: forall 'A. (Empty | Node[Num & 'A], Num) -> (Empty | Node['A] | Tree['A]) remove(Empty, 0) |> show remove(Node(0, Empty, Empty), 0) |> show remove(Node(1, Empty, Empty), 0) |> show remove(Node(1, Node(0, Empty, Empty), Empty), 0) |> show remove(Node(1, Empty, Node(2, Empty, Empty)), 2) |> show remove(Node(1, Node(0, Empty, Empty), Node(2, Empty, Empty)), 1) |> show //│ Str //│ res //│ = '•' //│ res //│ = '•' //│ res //│ = '(• 1 •)' //│ res //│ = '(• 1 •)' //│ res //│ = '(• 1 •)' //│ res //│ = '((• 0 •) 2 •)' example1 |> show remove(example1, 0) |> show remove(example1, 1) |> show remove(example1, 2) |> show remove(example1, 3) |> show remove(example1, 4) |> show //│ Str //│ res //│ = '(• 1 ((• 2 •) 3 (• 4 •)))' //│ res //│ = '(• 1 ((• 2 •) 3 (• 4 •)))' //│ res //│ = '(• 2 (• 3 (• 4 •)))' //│ res //│ = '(• 1 (• 3 (• 4 •)))' //│ res //│ = '(• 1 ((• 2 •) 4 •))' //│ res //│ = '(• 1 ((• 2 •) 3 •))' class Pair[A, B](val first: A, val second: B) { fun mapFirst(f) = Pair(f(first), second) fun mapSecond(f) = Pair(first, f(second)) } //│ class Pair[A, B](first: A, second: B) { //│ fun mapFirst: forall 'A 'B. (A -> 'A) -> Pair['A, 'B] //│ fun mapSecond: forall 'B0 'A0. (B -> 'B0) -> Pair['A0, 'B0] //│ } //│ where //│ 'A0 :> A //│ 'B :> B fun extractMin(t) = if t is Node(v, Empty, r) then Pair(Some(v), r) Node(v, l, r) and extractMin(l) is Pair(m, l') then Pair(m, Node(v, l', r)) Empty then Pair(None, Empty) //│ fun extractMin: forall 'A 'T 'A0 'A1 'B. (Empty | Node['A & 'T & 'A0]) -> Pair[in 'A1 out 'A1 | None, in Tree['A0] & 'B out Empty | 'B | Node['A0] | Tree['A]] //│ where //│ 'A1 :> None | Some['T] extractMin(example1).first ?? "not found" extractMin(example1).second |> show //│ Str //│ res //│ = 1 //│ res //│ = '((• 2 •) 3 (• 4 •))' fun merge(l, r) = if extractMin(r) is Pair(None, _) then l Pair(Some(m), r') then Node(m, l, r') //│ fun merge: forall 'A 'a. (Tree['A] & 'a, Empty | Node['A]) -> (Node['A] | 'a) merge(Empty, Empty) |> show merge(Empty, Node(0, Empty, Empty)) |> show merge(Node(0, Empty, Empty), Empty) |> show merge(Node(0, Empty, Empty), Node(1, Empty, Empty)) |> show merge(Node(0, Empty, Empty), Node(2, Node(1, Empty, Empty), Empty)) |> show //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '(• 0 •)' //│ res //│ = '((• 0 •) 1 •)' //│ res //│ = '((• 0 •) 1 (• 2 •))' fun removeGte(t, v) = if t is Node(v', l, r) and v < v' then removeGte(l, v) v > v' then Node(v', l, removeGte(r, v)) _ then l // lucky case Empty then Empty //│ fun removeGte: forall 'A. (Empty | Node[Num & 'A], Num) -> (Empty | Node['A] | Tree['A]) removeGte(Empty, 0) |> show removeGte(example1, 0) |> show removeGte(example1, 1) |> show removeGte(example1, 2) |> show removeGte(example1, 3) |> show removeGte(example1, 4) |> show removeGte(example1, 5) |> show //│ Str //│ res //│ = '•' //│ res //│ = '•' //│ res //│ = '•' //│ res //│ = '(• 1 •)' //│ res //│ = '(• 1 (• 2 •))' //│ res //│ = '(• 1 ((• 2 •) 3 •))' //│ res //│ = '(• 1 ((• 2 •) 3 (• 4 •)))' example2 |> show removeGte(example2, 10) |> show removeGte(example2, 22) |> show removeGte(example2, 23) |> show removeGte(example2, 24) |> show removeGte(example2, 70) |> show removeGte(example2, 99) |> show //│ Str //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' //│ res //│ = '(• 1 (• 5 •))' //│ res //│ = '(• 1 (• 5 (• 10 •)))' //│ res //│ = '(• 1 (• 5 (• 10 •)))' //│ res //│ = '(• 1 (• 5 (• 10 (• 23 •))))' //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 •))))' //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' fun removeLt(t, v) = if t is Node(v', l, r) and v' < v then removeLt(r, v) else Node(v', removeLt(l, v), r) Empty then Empty //│ fun removeLt: forall 'A. (Empty | Node[Num & 'A], Num) -> (Empty | Node['A]) example2 |> show removeLt(example2, 10) |> show removeLt(example2, 22) |> show removeLt(example2, 23) |> show removeLt(example2, 24) |> show removeLt(example2, 70) |> show removeLt(example2, 99) |> show //│ Str //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' //│ res //│ = '((• 10 (• 23 •)) 42 (• 59 (• 81 •)))' //│ res //│ = '((• 23 •) 42 (• 59 (• 81 •)))' //│ res //│ = '((• 23 •) 42 (• 59 (• 81 •)))' //│ res //│ = '(• 42 (• 59 (• 81 •)))' //│ res //│ = '(• 81 •)' //│ res //│ = '•' // Remove elements from `begin` until `end`. fun removeRange(t, begin, end) = if t is Node(v, l, r) and begin > v then Node(v, l, removeRange(r, begin, end)) end <= v then Node(v, removeRange(l, begin, end), r) _ then merge(removeGte(l, begin), removeLt(r, end)) Empty then Empty //│ fun removeRange: forall 'A. (Empty | Node[Num & 'A], Num, Num) -> (Empty | Node['A] | Tree['A]) example2 |> show removeRange(example2, 1, 82) |> show removeRange(example2, 1, 50) |> show removeRange(example2, 50, 81) |> show removeRange(example2, 20, 60) |> show removeRange(example2, 20, 24) |> show removeRange(example2, 59, 60) |> show //│ Str //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 59 (• 81 •)))))' //│ res //│ = '•' //│ res //│ = '(• 59 (• 81 •))' //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 81 •))))' //│ res //│ = '(• 1 (• 5 ((• 10 •) 81 •)))' //│ res //│ = '(• 1 (• 5 ((• 10 •) 42 (• 59 (• 81 •)))))' //│ res //│ = '(• 1 (• 5 ((• 10 (• 23 •)) 42 (• 81 •))))' fun size(t) = if t is Node(_, l, r) then 1 + size(l) + size(r) Empty then 0 //│ fun size: (Empty | Node[anything]) -> Int size(Empty) ~~> 0 size(Node(0, Empty, Empty)) ~~> 1 size(example1) ~~> 4 size(example2) ~~> 7 //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined fun inverse(t) = if t is Node(v, l, r) then Node(v, inverse(r), inverse(l)) Empty then Empty //│ fun inverse: (Empty | Node[anything]) -> (Empty | Node[nothing]) inverse(Empty) |> show inverse(Node(0, Empty, Empty)) |> show inverse(example1) |> show inverse(example2) |> show //│ Str //│ res //│ = '•' //│ res //│ = '(• 0 •)' //│ res //│ = '(((• 4 •) 3 (• 2 •)) 1 •)' //│ res //│ = '(((((• 81 •) 59 •) 42 ((• 23 •) 10 •)) 5 •) 1 •)' fun height(t) = if t is Node(_, l, r) then 1 + max(height(l), height(r)) Empty then 0 //│ fun height: (Empty | Node[anything]) -> Int height(Empty) ~~> 0 height(Node(0, Empty, Empty)) ~~> 1 height(example1) ~~> 3 height(example2) ~~> 5 //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined fun isBalanced(t) = if t is Empty then true Node(_, l, r) and height(l) is hl and height(r) is hr then // The precedence of `<=` seems to be broken. (abs(hl - hr) <= 1) && isBalanced(l) && isBalanced(r) //│ fun isBalanced: (Empty | Node[anything]) -> Bool isBalanced(Empty) ~~> true isBalanced(Node(0, Empty, Empty)) ~~> true isBalanced(example1) ~~> false isBalanced(example2) ~~> false //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined isBalanced(Node(1, single(-1), single(3))) ~~> true isBalanced(Node(1, single(-1), Node(3, single(2), Empty))) ~~> true isBalanced(Node(1, single(-1), Node(3, Empty, single(4)))) ~~> true //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/Calculator.mls ================================================ :NewDefs // This test file explores implementing a calculator using UCS. fun (++) concatOp(a, b) = concat(a)(b) fun (|>) pipe(a, f) = f(a) fun (!==) notEqual(x, y) = not(x === y) let anyToString = toString //│ fun (++) concatOp: (Str, Str) -> Str //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (!==) notEqual: forall 'c. (Eql['c], 'c) -> Bool //│ let anyToString: anything -> Str //│ anyToString //│ = [Function: toString] fun par(s, p) = if p then "(" ++ s ++ ")" else s //│ fun par: (Str, Bool) -> Str type StringOps = { length: Int, charAt: Int => Str, charCodeAt: Int => Int, slice: Int => Str } declare fun String: nothing let toStringOps: anything => StringOps = String //│ type StringOps = {charAt: Int -> Str, charCodeAt: Int -> Int, length: Int, slice: Int -> Str} //│ let toStringOps: anything -> StringOps //│ fun String: nothing //│ toStringOps //│ = [Function: String] type Option[A] = Some[A] | None class Some[A](value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None fun showOption(x) = if x is Some(value) then "Some(" ++ toString(value) ++ ")" None then "None" fun mapOption(f, x) = if x is Some(value) then Some(f(value)) None then None fun (??) getOrElse(x, default) = if x is Some(value) then value None then default fun flatten(x) = if x is Some(value) then value other then other //│ fun showOption: (None | Some[anything]) -> Str //│ fun mapOption: forall 'a 'A. ('a -> 'A, None | Some['a]) -> (None | Some['A]) //│ fun (??) getOrElse: forall 'b. (None | Some['b], 'b) -> 'b //│ fun flatten: forall 'c. (Object & 'c & ~#Some | Some['c]) -> 'c type List[A] = Cons[A] | Nil class Cons[A](head: A, tail: List[A]) module Nil fun (::) cons(head, tail) = Cons(head, tail) fun reverse(xs) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) fun join(sep) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') (xs) => if xs is Cons(x, xs') then aux(toString(x), xs') Nil then "" fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) //│ module Nil //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] //│ fun reverse: forall 'A0 'A1. (Cons['A0] | Nil) -> (Cons['A1] | Nil) //│ fun join: Str -> (forall 'A2. (Cons['A2] | Nil) -> Str) //│ fun showList: forall 'A3. (Cons['A3] | Nil) -> Str //│ where //│ 'A0 <: 'A1 // _ // | | _____ _____ _ __ // | | / _ \ \/ / _ \ '__| // | |__| __/> < __/ | // |_____\___/_/\_\___|_| // fun isDigit(n) = (48 <= n) && (n <= 57) fun isBlank(n) = (n === 32) || (n === 9) || (n === 10) || (n === 13) //│ fun isDigit: Num -> Bool //│ fun isBlank: Eql[10 | 13 | 32 | 9] -> Bool fun scanInt(text: StringOps, at: Int): Option[[Int, Int]] = let rec aux(acc, i: Int): Option[[Int, Int]] = if i < 0 then None i >= text.length then mapOption(n => [n, i], acc) let c = text.charCodeAt(i) isDigit(c) then aux(Some((acc ?? 0) * 10 + c - 48), i + 1) else mapOption(n => [n, i], acc) aux(None, at) //│ fun scanInt: (text: StringOps, at: Int) -> Option[[Int, Int]] scanInt("a123" |> toStringOps, 0) |> showOption scanInt("a123" |> toStringOps, 1) |> showOption scanInt("a123" |> toStringOps, 2) |> showOption scanInt("a123" |> toStringOps, 3) |> showOption scanInt("a123" |> toStringOps, 4) |> showOption //│ Str //│ res //│ = 'None' //│ res //│ = 'Some(123,4)' //│ res //│ = 'Some(23,4)' //│ res //│ = 'Some(3,4)' //│ res //│ = 'None' fun skipBlank(text: StringOps, at: Int): Int = let rec aux(i: Int): Int = if i >= text.length then i isBlank(text.charCodeAt(i)) then aux(i + 1) else i aux(at) //│ fun skipBlank: (text: StringOps, at: Int) -> Int skipBlank("abc" |> toStringOps, 0) skipBlank("abc" |> toStringOps, 1) skipBlank("abc" |> toStringOps, 2) skipBlank(" \t\n\r123" |> toStringOps, 0) //│ Int //│ res //│ = 0 //│ res //│ = 1 //│ res //│ = 2 //│ res //│ = 5 class Span(val start: Int, val end: Int) //│ class Span(start: Int, end: Int) abstract class Token(val span: Span) class IntegerLiteral(value: Int, span2: Span) extends Token(span2) { // It seems that constructor parameters is not sanitized fun toString(): Str = "IntegerLiteral(" ++ anyToString(value) ++ ")" } class LeftParen(at: Int) extends Token(Span(at, at + 1)) { fun toString(): Str = "LeftParen" } class RightParen(at: Int) extends Token(Span(at, at + 1)) { fun toString(): Str = "RightParen" } class BinaryOperator(value: Str, val bp: Int, at: Int) extends Token(Span(at, at + 1)) { fun toString(): Str = "BinaryOperator(" ++ value ++ ", " ++ anyToString(bp) ++ ")" } class EndOfInput(at: Int) extends Token(Span(at, at)) { fun toString(): Str = "EndOfInput" } class UnknownInput(rest: Str, at: Int, length: Int) extends Token(Span(at, at + length)) { fun toString(): Str = "UnknownInput(" ++ rest ++ ")" } //│ abstract class Token(span: Span) //│ class IntegerLiteral(value: Int, span2: Span) extends Token { //│ fun toString: () -> Str //│ } //│ class LeftParen(at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class RightParen(at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class BinaryOperator(value: Str, bp: Int, at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class EndOfInput(at: Int) extends Token { //│ fun toString: () -> Str //│ } //│ class UnknownInput(rest: Str, at: Int, length: Int) extends Token { //│ fun toString: () -> Str //│ } fun scanToken(text: StringOps, at: Int): [Token, Int] = if let at' = skipBlank(text, at) at' >= text.length then [EndOfInput(at'), at'] let head = text.charCodeAt(at') head === 37 then [BinaryOperator("%", 20, at'), at' + 1] 40 then [LeftParen(at'), at' + 1] 41 then [RightParen(at'), at' + 1] 42 then [BinaryOperator("*", 20, at'), at' + 1] 43 then [BinaryOperator("+", 10, at'), at' + 1] 45 then [BinaryOperator("-", 10, at'), at' + 1] 47 then [BinaryOperator("/", 20, at'), at' + 1] (48 <= head) && (head <= 57) and scanInt(text, at') is Some([n, at'']) then [IntegerLiteral(n, Span(at', at'')), at''] else [UnknownInput(text.slice(at'), at', text.length - at'), at'] //│ fun scanToken: (text: StringOps, at: Int) -> [Token, Int] scanToken("bruh" |> toStringOps, 0) scanToken("1" |> toStringOps, 0) scanToken("+" |> toStringOps, 0) scanToken(" 42" |> toStringOps, 0) //│ [Token, Int] //│ res //│ = [ UnknownInput {}, 0 ] //│ res //│ = [ IntegerLiteral {}, 1 ] //│ res //│ = [ BinaryOperator {}, 1 ] //│ res //│ = [ IntegerLiteral {}, 4 ] fun tokenize(str: Str): List[Token] = let text = str |> toStringOps let rec aux(acc, at) = if scanToken(text, at) is [token, at'] and token is UnknownInput then (token :: acc) |> reverse EndOfInput then acc |> reverse else aux(token :: acc, at') aux(Nil, 0) //│ fun tokenize: (str: Str) -> List[Token] tokenize("0") |> showList tokenize("1 + 2 * 3") |> showList tokenize("bruh") |> showList //│ Str //│ res //│ = '[IntegerLiteral(0)]' //│ res //│ = '[IntegerLiteral(1), BinaryOperator(+, 10), IntegerLiteral(2), BinaryOperator(*, 20), IntegerLiteral(3)]' //│ res //│ = '[UnknownInput(bruh)]' // ____ _ // | _ \ __ _ _ __ ___(_)_ __ __ _ // | |_) / _` | '__/ __| | '_ \ / _` | // | __/ (_| | | \__ \ | | | | (_| | // |_| \__,_|_| |___/_|_| |_|\__, | // |___/ type Expression = IntegerLiteral | BinaryExpression class BinaryExpression(op: BinaryOperator, left: Expression, right: Expression) //│ type Expression = BinaryExpression | IntegerLiteral //│ class BinaryExpression(op: BinaryOperator, left: Expression, right: Expression) fun bindingPower(t: Expression): Int = if t is IntegerLiteral then 30 BinaryExpression(op, _, _) then op.bp //│ fun bindingPower: (t: Expression) -> Int fun showExpression(t: Expression): Str = if t is IntegerLiteral(n) then anyToString(n) BinaryExpression(BinaryOperator(op, bp), left, right) then let lbp = bindingPower of left let rbp = bindingPower of right par(showExpression(left), lbp < bp) ++ " " ++ op ++ " " ++ par(showExpression(right), rbp < bp) //│ fun showExpression: (t: Expression) -> Str let s = Span(0, 0) IntegerLiteral(42, s) |> showExpression let t1 = BinaryExpression(BinaryOperator("+", 10, 0), IntegerLiteral(1, s), IntegerLiteral(2, s)) t1 |> showExpression let t2 = BinaryExpression(BinaryOperator("*", 20, 0), t1, IntegerLiteral(3, s)) t2 |> showExpression let t3 = BinaryExpression(BinaryOperator("*", 20, 0), t2, IntegerLiteral(4, s)) t3 |> showExpression //│ let s: Span //│ let t1: BinaryExpression //│ let t2: BinaryExpression //│ let t3: BinaryExpression //│ Str //│ s //│ = Span {} //│ res //│ = '42' //│ t1 //│ = BinaryExpression {} //│ res //│ = '1 + 2' //│ t2 //│ = BinaryExpression {} //│ res //│ = '(1 + 2) * 3' //│ t3 //│ = BinaryExpression {} //│ res //│ = '(1 + 2) * 3 * 4' type ParseResult[A] = Some[A] | Failed class Failed(message: Str) fun showParseResult(r: ParseResult['A]) = if r is Some(value) then "Some(" ++ toString(value) ++ ")" Failed(message) then "Failed(" ++ message ++ ")" fun (?>) mapParseResult(x, f) = if x is Some(value) then Some(f(value)) failed then failed //│ type ParseResult[A] = Failed | Some[A] //│ class Failed(message: Str) //│ fun showParseResult: forall 'A. (r: ParseResult['A]) -> Str //│ fun (?>) mapParseResult: forall 'a 'b 'A0. (Object & 'a & ~#Some | Some['b], 'b -> 'A0) -> (Some['A0] | 'a) fun showParsedExpression(r: ParseResult[Expression]) = if r is Some(value) then "Some(" ++ showExpression(value) ++ ")" Failed(message) then "Failed(" ++ message ++ ")" //│ fun showParsedExpression: (r: ParseResult[Expression]) -> Str fun lastPosition(t: Expression): Int = if t is IntegerLiteral(_, span) then span.end BinaryExpression(_, _, right) then lastPosition(right) //│ fun lastPosition: (t: Expression) -> Int fun parseAtom(ts: List[Token]): ParseResult[[Expression, List[Token]]] = if ts is Cons(IntegerLiteral(n, span), ts') then Some([IntegerLiteral(n, span), ts']) Cons(LeftParen, ts') and parseExpression(0, ts') is Some([body, Cons(RightParen, ts'')]) then Some([body, ts'']) Some([body, _]) then Failed("Expected a right parenthesis at " ++ toString(lastPosition of body)) failed then failed Cons(token, _) then Failed("Unexpected token " ++ toString(token) ++ " at " ++ toString(token.span.start)) Nil then Failed("Unexpected end of input") fun parseExpression(bp: Int, ts: List[Token]): ParseResult[[Expression, List[Token]]] = if parseAtom(ts) is Some([leftmost, ts']) then let rec aux(left, ts) = if ts is Cons(BinaryOperator(op, bp', opAt), ts') and bp < bp' and parseExpression(bp', ts') is Some([right, ts'']) then aux(BinaryExpression(BinaryOperator(op, bp', opAt), left, right), ts'') failed then failed else Some([left, ts]) aux(leftmost, ts') failed then failed fun parse(source: Str): ParseResult[Expression] = if parseExpression(0, tokenize(source)) is Some([expr, Nil]) then Some(expr) Some([expr, rest]) then Failed("Unexpected token: " ++ showList(rest) ++ " at " ++ toString(lastPosition of expr)) failed then failed //│ fun parseAtom: (ts: List[Token]) -> ParseResult[[Expression, List[Token]]] //│ fun parseExpression: (bp: Int, ts: List[Token]) -> ParseResult[[Expression, List[Token]]] //│ fun parse: (source: Str) -> ParseResult[Expression] parse("1 + 2 * 3") |> showParsedExpression parse("(1 + 2) * 3") |> showParsedExpression parse("2 * (1 + 3 + 5 + 7 + 9 + 11) - 2 - 4 - 6") |> showParsedExpression parse("2 * (1 + 3) * (5 + 7) * (9 - 11)") |> showParsedExpression parse("(((((((((42)))))))))") |> showParsedExpression //│ Str //│ res //│ = 'Some(1 + 2 * 3)' //│ res //│ = 'Some((1 + 2) * 3)' //│ res //│ = 'Some(2 * (1 + 3 + 5 + 7 + 9 + 11) - 2 - 4 - 6)' //│ res //│ = 'Some(2 * (1 + 3) * (5 + 7) * (9 - 11))' //│ res //│ = 'Some(42)' parse("1 + ") |> showParsedExpression parse("1 bruh") |> showParsedExpression parse("1 * (2 + 3") |> showParsedExpression parse("1 - bruh") |> showParsedExpression //│ Str //│ res //│ = 'Failed(Unexpected end of input)' //│ res //│ = 'Failed(Unexpected token: [UnknownInput(bruh)] at 1)' //│ res //│ = 'Failed(Expected a right parenthesis at 10)' //│ res //│ = 'Failed(Unexpected token UnknownInput(bruh) at 4)' // _____ _ _ _ // | ____|_ ____ _| |_ _ __ _| |_(_) ___ _ __ // | _| \ \ / / _` | | | | |/ _` | __| |/ _ \| '_ \ // | |___ \ V / (_| | | |_| | (_| | |_| | (_) | | | | // |_____| \_/ \__,_|_|\__,_|\__,_|\__|_|\___/|_| |_| // fun evaluate(t: Expression): Option[Int] = if t is IntegerLiteral(n) then Some(n) BinaryExpression(BinaryOperator(op, _, _), left, right) and evaluate(left) is Some(leftResult) and evaluate(right) is Some(rightResult) and op === "+" then Some(leftResult + rightResult) "-" then Some(leftResult - rightResult) "*" then Some(leftResult * rightResult) // "/" then Some(leftResult / rightResult) "%" then Some(leftResult % rightResult) else None //│ fun evaluate: (t: Expression) -> Option[Int] fun evaluation(source: Str): Str = if parse(source) is Some(expression) and evaluate(expression) is Some(result) then toString(result) None then "Evaluation failed" Failed(message) then "Parsing failed: " ++ message //│ fun evaluation: (source: Str) -> Str evaluation("1 + 2 * 3") evaluation("(((((42)))))") evaluation("1 * (3 + 4) - 5") evaluation("1 + ") evaluation("1 bruh") //│ Str //│ res //│ = '7' //│ res //│ = '42' //│ res //│ = '2' //│ res //│ = 'Parsing failed: Unexpected end of input' //│ res //│ = 'Parsing failed: Unexpected token: [UnknownInput(bruh)] at 1' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/EitherOrBoth.mls ================================================ :NewDefs abstract class Option[out T]: (Some[T] | None) class Some[out T](value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option //│ module None extends Option abstract class EitherOrBoth[out A, out B]: (Left[A, B] | Right[A, B] | Both[A, B]) class Left[out A, out B](value: A) extends EitherOrBoth[A, B] class Right[out A, out B](value: B) extends EitherOrBoth[A, B] class Both[out A, out B](left: A, right: B) extends EitherOrBoth[A, B] //│ abstract class EitherOrBoth[A, B]: Both[A, B] | Left[A, B] | Right[A, B] //│ class Left[A, B](value: A) extends EitherOrBoth //│ class Right[A, B](value: B) extends EitherOrBoth //│ class Both[A, B](left: A, right: B) extends EitherOrBoth type Either[A, B] = Left[A, B] | Right[A, B] //│ type Either[A, B] = Left[A, B] | Right[A, B] fun getLeft[A, B](eob: EitherOrBoth[A, B]): Option[A] = if eob is Left(left) then Some(left) Right(_) then None Both(left, _) then Some(left) //│ fun getLeft: forall 'A. (eob: EitherOrBoth['A, anything]) -> Option['A] fun getRight[A, B](eob: EitherOrBoth[A, B]): Option[B] = if eob is Left(_) then None Right(right) then Some(right) Both(_, right) then Some(right) //│ fun getRight: forall 'B. (eob: EitherOrBoth[anything, 'B]) -> Option['B] fun getBoth[A, B](eob: EitherOrBoth[A, B]): Option[[A, B]] = if eob is Left(_) then None Right(_) then None Both(left, right) then Some([left, right]) //│ fun getBoth: forall 'A 'B. (eob: EitherOrBoth['A, 'B]) -> Option[['A, 'B]] fun mapLeft[A, B, C](eob: EitherOrBoth[A, B], f: A -> C): EitherOrBoth[C, B] = if eob is Left(left) then Left(f(left)) Right(right) then Right(right) Both(left, right) then Both(f(left), right) //│ fun mapLeft: forall 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C) -> EitherOrBoth['C, 'B] fun mapRight[A, B, C](eob: EitherOrBoth[A, B], f: B -> C): EitherOrBoth[A, C] = if eob is Left(left) then Left(left) Right(right) then Right(f(right)) Both(left, right) then Both(left, f(right)) //│ fun mapRight: forall 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'B -> 'C) -> EitherOrBoth['A, 'C] fun map[A, B, C, D](eob: EitherOrBoth[A, B], f: A -> C, g: B -> D): EitherOrBoth[C, D] = if eob is Left(left) then Left(f(left)) Right(right) then Right(g(right)) Both(left, right) then Both(f(left), g(right)) //│ fun map: forall 'A 'B 'C 'D. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C, g: 'B -> 'D) -> EitherOrBoth['C, 'D] fun fold[A, B, C](eob: EitherOrBoth[A, B], f: A -> C, g: B -> C, h: [A, B] -> C): C = if eob is Left(left) then f(left) Right(right) then g(right) Both(left, right) then h(left, right) //│ fun fold: forall 'A 'B 'C. (eob: EitherOrBoth['A, 'B], f: 'A -> 'C, g: 'B -> 'C, h: ('A, 'B) -> 'C) -> 'C fun isLeft[A, B](eob: EitherOrBoth[A, B]): Bool = if eob is Left(_) then true Right(_) then false Both(_, _) then false //│ fun isLeft: (eob: EitherOrBoth[anything, anything]) -> Bool fun isRight[A, B](eob: EitherOrBoth[A, B]): Bool = if eob is Left(_) then false Right(_) then true Both(_, _) then false //│ fun isRight: (eob: EitherOrBoth[anything, anything]) -> Bool fun isBoth[A, B](eob: EitherOrBoth[A, B]): Bool = if eob is Left(_) then false Right(_) then false Both(_, _) then true //│ fun isBoth: (eob: EitherOrBoth[anything, anything]) -> Bool fun (++) strcat(a: Str, b: Str): Str = concat(a)(b) //│ fun (++) strcat: (a: Str, b: Str) -> Str fun eobToString[A, B](eob: EitherOrBoth[A, B]): Str = if eob is Left(left) then "Left(" ++ toString(left) ++ ")" Right(right) then "Right(" ++ toString(right) ++ ")" Both(left, right) then "Both(" ++ toString(left) ++ ", " ++ toString(right) ++ ")" //│ fun eobToString: (eob: EitherOrBoth[anything, anything]) -> Str ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/JSON.mls ================================================ :NewDefs type NStr = Str & { length: Int, at: Int -> NStr, charAt: Int -> NStr, charCodeAt: Int -> Int, slice: (Int, Int) -> NStr, startsWith: (Str, Int) -> Bool, endsWith: Str -> Bool, split: Str -> Array[NStr], trim: () -> NStr, trimStart: () -> NStr, trimEnd: () -> NStr, padStart: (Int, Str) -> NStr, padEnd: (Int, Str) -> NStr, repeat: Int -> NStr, indexOf: Str -> Int, lastIndexOf: Str -> Int, includes: Str -> Bool, localeCompare: Str -> Int } declare fun String: (anything -> NStr) & { fromCodePoint: Int -> NStr } fun (++) strcat(a, b) = String of concat(a)(b) fun (<*) strlt(a: NStr, b: NStr) = a.localeCompare(b) < 0 fun (*>) strgt(a: NStr, b: NStr) = a.localeCompare(b) > 0 //│ type NStr = Str & { //│ at: Int -> NStr, //│ charAt: Int -> NStr, //│ charCodeAt: Int -> Int, //│ endsWith: Str -> Bool, //│ includes: Str -> Bool, //│ indexOf: Str -> Int, //│ lastIndexOf: Str -> Int, //│ length: Int, //│ localeCompare: Str -> Int, //│ padEnd: (Int, Str) -> NStr, //│ padStart: (Int, Str) -> NStr, //│ repeat: Int -> NStr, //│ slice: (Int, Int) -> NStr, //│ split: Str -> Array[NStr], //│ startsWith: (Str, Int) -> Bool, //│ trim: () -> NStr, //│ trimEnd: () -> NStr, //│ trimStart: () -> NStr //│ } //│ fun (++) strcat: (Str, Str) -> NStr //│ fun (<*) strlt: (a: NStr, b: NStr) -> Bool //│ fun (*>) strgt: (a: NStr, b: NStr) -> Bool //│ fun String: anything -> NStr & {fromCodePoint: Int -> NStr} declare fun Math: { log10: Num -> Num, floor: Num -> Num, ceil: Num -> Num } //│ fun Math: {ceil: Num -> Num, floor: Num -> Num, log10: Num -> Num} fun (!==) notEqual(x, y) = not(x === y) declare fun parseInt: (Str, Int) -> Int //│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool //│ fun parseInt: (Str, Int) -> Int // `List` and its utilities: abstract class List[out T]: Cons[T] | Nil class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) fun reverse(l: List['A]): List['A] = let rec r(l', l) = if l is Cons(x, xs) then r(x :: l', xs) else l' r(Nil, l) fun join(sep: Str, xs: List['B]) = if xs is Cons(x, Nil) then toString(x) Cons(x, xs) then toString(x) ++ sep ++ join(sep, xs) Nil then "" fun showList(xs: List['C]) = "[" ++ join(", ", xs) ++ "]" fun map(f: 'D -> 'E, xs: List['D]): List['E] = if xs is Cons(x, xs) then f(x) :: map(f, xs) Nil then Nil fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs is Cons(x, xs') and ys is Cons(y, ys') then equal(x, y) and equalList(xs', ys', equal) Nil and ys is Nil then true else false //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] //│ fun reverse: forall 'T0. (l: List['T0]) -> List['T0] //│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) //│ fun showList: (xs: List[anything]) -> NStr //│ fun map: forall 'D 'T1. (f: 'D -> 'T1, xs: List['D]) -> List['T1] //│ fun equalList: forall 'A. (xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool) -> Bool // `Option` and its utilities: abstract class Option[out A]: Some[A] | None class Some[out A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun (->) makePair(a, b) = [a, b] //│ fun (->) makePair: forall 'a 'b. ('a, 'b) -> ['a, 'b] abstract class ListMap[K, out V]: (ConsMap[K, V] | NilMap) class ConsMap[K, out V](head: [K, V], tail: ListMap[K, V]) extends ListMap[K, V] module NilMap extends ListMap fun containsKey(map: ListMap['K, 'V], key: 'K): Bool = if map is ConsMap([k, _], _) and k === key then true ConsMap(_, tail) then containsKey(tail, key) NilMap then false fun (:+) insert(map, entry) = if map is ConsMap(entry', map) and entry'.0 === entry.0 then ConsMap(entry, map) else ConsMap(entry', insert(map, entry)) NilMap then ConsMap(entry, NilMap) fun showMap(map) = let showEntry([k, v]) = toString(k) ++ " -> " ++ toString(v) let rec aux(map) = if map is ConsMap(last, NilMap) then showEntry(last) ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) NilMap then "" if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" //│ abstract class ListMap[K, V]: ConsMap[K, V] | NilMap //│ class ConsMap[K, V](head: [K, V], tail: ListMap[K, V]) extends ListMap //│ module NilMap extends ListMap //│ fun containsKey: forall 'K 'a. (map: ListMap['K, anything], key: 'a) -> Bool //│ fun (:+) insert: forall 'K0 'b 'V. (ConsMap['K0, 'V] | NilMap, ['K0 & 'b, 'V]) -> ConsMap['K0, 'V] //│ fun showMap: forall 'K1. (ConsMap['K1, anything] | NilMap) -> NStr //│ where //│ 'K0 <: Eql['b] //│ 'K <: Eql['a] showMap of NilMap showMap of NilMap :+ ["b", 2] showMap of NilMap :+ [1, "a"] :+ [2, "b"] showMap of NilMap :+ [1, "a"] :+ [2, "b"] :+ [1, "c"] //│ NStr //│ res //│ = '{}' //│ res //│ = '{ b -> 2 }' //│ res //│ = '{ 1 -> a, 2 -> b }' //│ res //│ = '{ 1 -> c, 2 -> b }' abstract class JsonValue: JsonNumber | JsonString | JsonArray | JsonObject | JsonBoolean | JsonNull class JsonNumber(value: Num) extends JsonValue class JsonString(value: Str) extends JsonValue class JsonArray(value: List[JsonValue]) extends JsonValue class JsonObject(value: ListMap[Str, JsonValue]) extends JsonValue class JsonBoolean(value: Bool) extends JsonValue module JsonNull extends JsonValue //│ abstract class JsonValue: JsonArray | JsonBoolean | JsonNull | JsonNumber | JsonObject | JsonString //│ class JsonNumber(value: Num) extends JsonValue //│ class JsonString(value: Str) extends JsonValue //│ class JsonArray(value: List[JsonValue]) extends JsonValue //│ class JsonObject(value: ListMap[Str, JsonValue]) extends JsonValue //│ class JsonBoolean(value: Bool) extends JsonValue //│ module JsonNull extends JsonValue class ParserState(val text: NStr, val at: Int) { fun drained: Bool = at === text.length fun peek: Option[NStr] = if drained then None else Some(text.charAt(at)) fun peekCode: Option[Int] = if drained then None else Some(text.charCodeAt(at)) fun next: ParserState = if drained then this else ParserState(text, at + 1) fun nextDigit: Option[[Num, ParserState]] = if peekCode is Some(ch) and 48 <= ch and ch <= 57 then Some([ch - 48, next]) else None fun match(prefix: Str): Option[ParserState] = let prefix' = String(prefix) if prefix'.length > text.length - at then None text.startsWith(prefix', at) then Some(ParserState(text, at + prefix'.length)) else None fun rest: NStr = text.slice(at, text.length) } fun showParserState(state) = "ParserState(_, " ++ toString(state.at) ++ ")" fun success: forall 't: ('t, ParserState) -> ParseResult['t] fun success = (value, state) => Success(value, state) fun failure: forall 't: Str -> ParseResult[nothing] fun failure = error => Failure(error) abstract class ParseResult[out T]: (Success[T] | Failure) { virtual fun flatMap(f: (T, ParserState) -> ParseResult['U]): ParseResult['U] virtual fun map(f: T -> 'U): ParseResult['U] } class Success[out T](value: T, state: ParserState) extends ParseResult[T] { fun flatMap(f) = f(value, state) fun map(f) = success(f(value), state) } class Failure(error: Str) extends ParseResult[nothing] { fun flatMap(_) = failure(error) fun map(_) = failure(error) } fun showParseResult(result) = if result is Success(value, state) then "Success after " ++ toString(state.at) ++ ": " ++ toString(value) Failure(error) then "Failure: " ++ toString(error) //│ class ParserState(text: NStr, at: Int) { //│ fun drained: Bool //│ fun match: (prefix: Str) -> Option[ParserState] //│ fun next: ParserState //│ fun nextDigit: Option[[Num, ParserState]] //│ fun peek: Option[NStr] //│ fun peekCode: Option[Int] //│ fun rest: NStr //│ } //│ fun showParserState: {at: anything} -> NStr //│ fun success: forall 'T. ('T, ParserState) -> Success['T] //│ fun failure: Str -> Failure //│ abstract class ParseResult[T]: Failure | Success[T] { //│ fun flatMap: forall 'U. (f: (T, ParserState) -> ParseResult['U]) -> ParseResult['U] //│ fun map: forall 'U0. (f: T -> 'U0) -> ParseResult['U0] //│ } //│ class Success[T](value: T, state: ParserState) extends ParseResult { //│ fun flatMap: forall 'a. ((T, ParserState) -> 'a) -> 'a //│ fun map: forall 't. (T -> 't) -> ParseResult['t] //│ } //│ class Failure(error: Str) extends ParseResult { //│ fun flatMap: anything -> ParseResult[nothing] //│ fun map: anything -> ParseResult[nothing] //│ } //│ fun showParseResult: (Failure | Success[anything]) -> NStr //│ fun success: forall 't0. ('t0, ParserState) -> ParseResult['t0] //│ fun failure: Str -> ParseResult[nothing] fun isWhiteSpace(ch: NStr): Bool = (ch === " ") || (ch === "\n") || (ch === "\r") || (ch === "\t") fun skipWhiteSpace(state: ParserState): ParserState = if state.peek is Some(ch) and isWhiteSpace(ch) then skipWhiteSpace(state.next) else state //│ fun isWhiteSpace: (ch: NStr) -> Bool //│ fun skipWhiteSpace: (state: ParserState) -> ParserState (skipWhiteSpace of ParserState(String(" \n\r\t"), 0)).at //│ Int //│ res //│ = 4 fun isDigit(ch) = sge(ch, "0") && sle(ch, "9") //│ fun isDigit: Str -> Bool fun parseNumber(state: ParserState): ParseResult[Num] = let toFraction(n) = n / (10 ** Math.ceil of Math.log10 of n) let parseNegative(state): ParseResult[Bool] = if state.peek is Some("-") then Success(true, state.next) else Success(false, state) // Parse one or more decimal digits // -------------------------------- let parseDigits(state): ParseResult[Num] = // Parse remaining digits let rec aux(acc, state) = if state.nextDigit is Some([digit, state']) then aux((acc *. 10) +. digit, state') None then [acc, state] // Parse the first digit if state.nextDigit is Some([digit, state']) and aux(digit, state') is [num, state''] then Success(num, state'') None then Failure("expected one or more decimal digits") // Parse the integral part of the number // ------------------------------------- let parseIntegral(state): ParseResult[Num] = if state.nextDigit is Some([0, state']) then Success(0, state') else parseDigits(state) // Parse the fractional part of the number // --------------------------------------- let parseFraction(state): ParseResult[Num] = if state.peek is Some(".") then parseDigits(state.next).map of toFraction else Success(0, state) let parseExponent(state): ParseResult[Num] = let parseSign(state): ParseResult[Bool] = if state.peek is Some("-") then Success(true, state.next) Some("+") then Success(false, state.next) else Success(false, state) if state.peek is Some(e) and (e === "e") || (e === "E") then parseSign(state.next).flatMap of (sign, state) => parseDigits(state).map of exponent => if sign then 10 ** (0 -. exponent) else 10 ** exponent else Success(1, state) parseNegative(state).flatMap of (negative, state) => parseIntegral(state).flatMap of (integral, state) => parseFraction(state).flatMap of (fraction, state) => parseExponent(state).flatMap of (exponent, state) => let value = (integral +. fraction) *. exponent Success of (if negative then (0 -. value) else value), state //│ fun parseNumber: (state: ParserState) -> ParseResult[Num] showParseResult of parseNumber of ParserState of String("0"), 0 showParseResult of parseNumber of ParserState of String("0234"), 0 showParseResult of parseNumber of ParserState of String("123"), 0 showParseResult of parseNumber of ParserState of String("12.34"), 0 showParseResult of parseNumber of ParserState of String("1e10"), 0 showParseResult of parseNumber of ParserState of String("1E5"), 0 showParseResult of parseNumber of ParserState of String("1E-1"), 0 showParseResult of parseNumber of ParserState of String("1E+1"), 0 //│ NStr //│ res //│ = 'Success after 1: 0' //│ res //│ = 'Success after 1: 0' //│ res //│ = 'Success after 3: 123' //│ res //│ = 'Success after 5: 12.34' //│ res //│ = 'Success after 4: 10000000000' //│ res //│ = 'Success after 3: 100000' //│ res //│ = 'Success after 4: 0.1' //│ res //│ = 'Success after 4: 10' fun parseString(state: ParserState): ParseResult[Str] = let rec parseCodePoint(n, acc, state) = if n === 0 then Success(acc, state) state.peekCode is Some(code) and 48 <= code and code <= 57 then parseCodePoint(n - 1, acc * 16 + code - 48, state.next) 65 <= code and code <= 70 then parseCodePoint(n - 1, acc * 16 + code - 55, state.next) 97 <= code and code <= 102 then parseCodePoint(n - 1, acc * 16 + code - 87, state.next) else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of '" ++ String.fromCodePoint(code) ++ "'") else Failure("expect " ++ toString(n) ++ " hex digit(s) instead of end of input") let rec parseContent(acc, state) = if state.peek is Some("\"") then Success(acc, state.next) Some("\\") and let state' = state.next state'.peek is Some("\"") then parseContent(acc ++ "\"", state'.next) Some("\\") then parseContent(acc ++ "\\", state'.next) Some("/") then parseContent(acc ++ "/", state'.next) Some("b") then parseContent(acc ++ "\b", state'.next) Some("f") then parseContent(acc ++ "\f", state'.next) Some("n") then parseContent(acc ++ "\n", state'.next) Some("r") then parseContent(acc ++ "\r", state'.next) Some("t") then parseContent(acc ++ "\t", state'.next) Some("u") then parseCodePoint(4, 0, state'.next).flatMap of (codePoint, state) => if codePoint < 0xD800 || 0xDFFF < codePoint then parseContent(acc ++ String.fromCodePoint(codePoint), state) else Failure("invalid code point") else Failure("invalid escape sequence") Some(ch) then parseContent(acc ++ ch, state.next) None then Failure("expected '\"' instead of end of input") if state.peek is Some("\"") then parseContent("", state.next) Some(ch) then Failure("expected '\"' instead of '" ++ ch ++ "'") else Failure("expected '\"' instead of end of input") //│ fun parseString: (state: ParserState) -> ParseResult[Str] showParseResult of parseString of ParserState of String("\"\""), 0 showParseResult of parseString of ParserState of String("\"abc\""), 0 showParseResult of parseString of ParserState of String("\"\\\"\""), 0 showParseResult of parseString of ParserState of String("\"\\\\\""), 0 showParseResult of parseString of ParserState of String("\"\\/\""), 0 showParseResult of parseString of ParserState of String("\"\\b\""), 0 showParseResult of parseString of ParserState of String("\""), 0 showParseResult of parseString of ParserState of String("\"\\u\""), 0 showParseResult of parseString of ParserState of String("\"\\u0\""), 0 showParseResult of parseString of ParserState of String("\"\\u004c\""), 0 //│ NStr //│ res //│ = 'Success after 2: ' //│ res //│ = 'Success after 5: abc' //│ res //│ = 'Success after 4: "' //│ res //│ = 'Success after 4: \\' //│ res //│ = 'Success after 4: /' //│ res //│ = 'Success after 4: \b' //│ res //│ = `Failure: expected '"' instead of end of input` //│ res //│ = `Failure: expect 4 hex digit(s) instead of '"'` //│ res //│ = `Failure: expect 3 hex digit(s) instead of '"'` //│ res //│ = 'Success after 8: L' fun parseTrue(state: ParserState): ParseResult[Bool] = if state.match("true") is Some(state) then Success(true, state) None then Failure("expected 'true'") fun parseFalse(state: ParserState): ParseResult[Bool] = if state.match("false") is Some(state) then Success(false, state) None then Failure("expected 'false'") fun parseNull(state: ParserState): ParseResult[()] = if state.match("null") is Some(state) then Success((), state) None then Failure("expected 'null'") //│ fun parseTrue: (state: ParserState) -> ParseResult[Bool] //│ fun parseFalse: (state: ParserState) -> ParseResult[Bool] //│ fun parseNull: (state: ParserState) -> ParseResult[()] fun parseObjectEntry(state: ParserState): ParseResult[[Str, JsonValue]] = let state' = skipWhiteSpace(state) parseString(state').flatMap of (key, state) => let state' = skipWhiteSpace(state) if state'.peek is Some(":") then parseValue(state'.next).flatMap of (value, state') => Success([key, value], state') Some(ch) then Failure("expected ':' instead of '" ++ ch ++ "'") None then Failure("expected ':' instead of end of input") else Failure("expected ':' instead of end of input") fun parseObject(state: ParserState): ParseResult[ListMap[Str, JsonValue]] = let rec parseObjectTail(acc: ListMap[Str, JsonValue], state: ParserState) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then parseObjectEntry(state'.next).flatMap of (entry, state') => if containsKey(acc, entry.0) then Failure("duplicate key '" ++ toString(entry.0) ++ "'") else parseObjectTail(ConsMap(entry, acc), state') Some("}") then Success(acc, state'.next) Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) None then Failure("expected ',' or ']' instead of end of input") let state' = skipWhiteSpace(state) if state'.peek is Some("}") then Success(NilMap, state'.next) None then Failure("expected ',' or ']' instead of end of input") else parseObjectEntry(state').flatMap of (head, state) => parseObjectTail(ConsMap(head, NilMap), state) fun parseArray(state: ParserState): ParseResult[List[JsonValue]] = let rec parseArrayTail(acc, state) = let state' = skipWhiteSpace(state) if state'.peek is Some(",") then parseValue(state'.next).flatMap of (value, state') => parseArrayTail(value :: acc, state') Some("]") then Success(reverse(acc), state'.next) Some(ch) then Failure("expected ',' or ']' instead of " ++ ch) None then Failure("expected ',' or ']' instead of end of input") let state' = skipWhiteSpace(state) if state'.peek is Some("]") then Success(Nil, state'.next) None then Failure("expected ',' or ']' instead of end of input") else parseValue(state').flatMap of (head, state) => parseArrayTail(head :: Nil, state) fun parseValue(state: ParserState): ParseResult[JsonValue] = let state' = skipWhiteSpace(state) if state'.peek is Some(ch) and ch === "\"" then parseString(state').map of JsonString (ch === "-") || isDigit(ch) then parseNumber(state').map of JsonNumber ch === "[" then parseArray(state'.next).map of JsonArray ch === "{" then parseObject(state'.next).map of JsonObject ch === "t" then parseTrue(state').map of JsonBoolean ch === "f" then parseFalse(state').map of JsonBoolean ch === "n" then parseNull(state').map of _ => JsonNull else Failure("cannot recognize " ++ ch ++ " as the beginning of a JSON value") None then Failure("expected a JSON value instead of end of input") //│ fun parseObjectEntry: (state: ParserState) -> ParseResult[[Str, JsonValue]] //│ fun parseObject: (state: ParserState) -> ParseResult[ListMap[Str, JsonValue]] //│ fun parseArray: (state: ParserState) -> ParseResult[List[JsonValue]] //│ fun parseValue: (state: ParserState) -> ParseResult[JsonValue] fun parse(source: Str): ParseResult[JsonValue] = (parseValue of ParserState of String(source), 0).flatMap of (value, finalState) => let shouldBeEnd = skipWhiteSpace of finalState if shouldBeEnd.drained then Success(value, shouldBeEnd) else Failure("expected end of input instead of: " ++ shouldBeEnd.rest) //│ fun parse: (source: Str) -> ParseResult[JsonValue] fun stringify(value: JsonValue): Str = let stringifyObject(map) = let showEntry([k, v]) = "\"" ++ toString(k) ++ "\": " ++ stringify(v) let rec aux(map) = if map is ConsMap(last, NilMap) then showEntry(last) ConsMap(head, tail) then showEntry(head) ++ ", " ++ aux(tail) NilMap then "" if map is NilMap then String("{}") else "{ " ++ aux(map) ++ " }" if value is JsonNumber(n) then toString(n) JsonString(s) then "\"" ++ s ++ "\"" JsonArray(xs) then "[" ++ join(", ", map(stringify, xs)) ++ "]" JsonObject(m) then stringifyObject(m) JsonBoolean(b) then if b then "true" else "false" JsonNull then "null" //│ fun stringify: (value: JsonValue) -> Str fun showResult(result) = if result is Success(value, state) then "Success after " ++ toString(state.at) ++ ": " ++ stringify(value) Failure(error) then "Failure: " ++ toString(error) //│ fun showResult: (Failure | Success[JsonValue]) -> NStr // Simple tests. showResult of parse of "null" showResult of parse of "true" showResult of parse of "false" showResult of parse of "123" showResult of parse of "\"abc\"" showResult of parse of "[1, 2, 3]" showResult of parse of "{\"a\": 1, \"b\": 2}" showResult of parse of "nul" showResult of parse of "[1, 3, 5" showResult of parse of "[1, 3, 5]" //│ NStr //│ res //│ = 'Success after 4: null' //│ res //│ = 'Success after 4: true' //│ res //│ = 'Success after 5: false' //│ res //│ = 'Success after 3: 123' //│ res //│ = 'Success after 5: "abc"' //│ res //│ = 'Success after 9: [1, 2, 3]' //│ res //│ = 'Success after 16: { "b": 2, "a": 1 }' //│ res //│ = "Failure: expected 'null'" //│ res //│ = "Failure: expected ',' or ']' instead of end of input" //│ res //│ = 'Success after 9: [1, 3, 5]' // Complicated tests. showResult of parse of "{ \"origin\": { \"x\": 0, \"y\": 0 } }" showResult of parse of "[ { \"origin\": { \"x\": 0, \"y\": 0 } , \"size\": { \"width\": 100, \"height\": 100 } } ]" showResult of parse of "{\"id\":\"658f34f88882211aa8679240\",\"children\":[{\"name\":\"Jo Rosales\",\"age\":8},{\"name\":\"Shawn Burke\",\"age\":7},{\"name\":\"Gomez Guthrie\",\"age\":10},{\"name\":\"Tandy Christensen\",\"age\":9},{\"name\":\"Jody Langley\",\"age\":3}],\"currentJob\":{\"title\":\"Developer\",\"salary\":\"mask;\"},\"jobs\":[{\"title\":\"medic\",\"salary\":\"R$ 6.400,90\"},{\"title\":\"teacher\",\"salary\":\"R$ 7.960,31\"}],\"maxRunDistance\":14.7,\"cpf\":\"713.763.356-03\",\"cnpj\":\"33.385.435/0001-50\",\"pretendSalary\":\"R$ 9.247,29\",\"age\":63,\"gender\":\"male\",\"firstName\":\"Parker\",\"lastName\":\"Case\",\"phone\":\"+55 (83) 95023-7077\",\"address\":\"14 Orient Avenue - Harmon, Northern Mariana Islands, Myanmar.\",\"hairColor\":\"yellow\"}" //│ NStr //│ res //│ = 'Success after 32: { "origin": { "y": 0, "x": 0 } }' //│ res //│ = 'Success after 82: [{ "size": { "height": 100, "width": 100 }, "origin": { "y": 0, "x": 0 } }]' //│ res //│ = 'Success after 647: { "hairColor": "yellow", "address": "14 Orient Avenue - Harmon, Northern Mariana Islands, Myanmar.", "phone": "+55 (83) 95023-7077", "lastName": "Case", "firstName": "Parker", "gender": "male", "age": 63, "pretendSalary": "R$ 9.247,29", "cnpj": "33.385.435/0001-50", "cpf": "713.763.356-03", "maxRunDistance": 14.7, "jobs": [{ "salary": "R$ 6.400,90", "title": "medic" }, { "salary": "R$ 7.960,31", "title": "teacher" }], "currentJob": { "salary": "mask;", "title": "Developer" }, "children": [{ "age": 8, "name": "Jo Rosales" }, { "age": 7, "name": "Shawn Burke" }, { "age": 10, "name": "Gomez Guthrie" }, { "age": 9, "name": "Tandy Christensen" }, { "age": 3, "name": "Jody Langley" }], "id": "658f34f88882211aa8679240" }' // Nice. ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/LeftistTree.mls ================================================ :NewDefs fun (|>) pipe(x, f) = f(x) fun (~~>) toBe(x, y) = if x === y then () else error fun (?) max(x, y) = if x > y then x else y fun abs(x) = if x < 0 then -x else x //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (~~>) toBe: forall 'c. (Eql['c], 'c) -> () //│ fun ( 'd //│ fun (>?) max: forall 'e. (Num & 'e, Num & 'e) -> 'e //│ fun abs: Int -> Int abstract class Option[out T]: (Some[T] | None) class Some[out T](val value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option //│ module None extends Option fun (??) getOrElse(o, v) = if o is Some(v') then v' None then v //│ fun (??) getOrElse: forall 'a. (None | Some['a], 'a) -> 'a fun (++) strcat(s1, s2) = concat(s1)(s2) //│ fun (++) strcat: (Str, Str) -> Str let anyToString = toString //│ let anyToString: anything -> Str //│ anyToString //│ = [Function: toString] abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[nothing] //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] abstract class Tree[out A]: (Empty | Node[A]) class Node[out A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree[A] module Empty extends Tree[nothing] //│ abstract class Tree[A]: Empty | Node[A] //│ class Node[A](value: A, left: Tree[A], right: Tree[A], rank: Int) extends Tree //│ module Empty extends Tree fun show(t: Tree[anything]): Str = if t is Node(v, l, r, _) then "(" ++ show(l) ++ " " ++ toString(v) ++ " " ++ show(r) ++ ")" Empty then "•" //│ fun show: (t: Tree[anything]) -> Str fun singleton(x) = Node(x, Empty, Empty, 1) fun rank(t) = if t is Empty then 0 Node(_, _, _, r) then r //│ fun singleton: forall 'A. 'A -> Node['A] //│ fun rank: (Empty | Node[anything]) -> Int // This can be improved. This can be better. fun merge(t1: Tree[Num], t2: Tree[Num]): Tree[Num] = if t1 is Node(v1, l1, r1, _) and t2 is Node(v2, _, _, _) and v1 > v2 then merge(t2, t1) _ and merge(r1, t2) is merged and rank(l1) is rank_left and rank(r1) is rank_right and rank_left >= rank_right then Node(v1, l1, merged, rank_right + 1) else Node(v1, merged, l1, rank_left + 1) t1 is Empty and t2 is Node then t2 t1 is Node and t2 is Empty then t1 t1 is Empty and t2 is Empty then Empty //│ fun merge: (t1: Tree[Num], t2: Tree[Num]) -> Tree[Num] fun insert(t, v) = merge(t, singleton(v)) //│ fun insert: (Tree[Num], Num) -> Tree[Num] fun getMin(t) = if t is Empty then None Node(x, _, _, _) then Some(x) //│ fun getMin: forall 'T. (Empty | Node['T]) -> (None | Some['T]) fun deleteMin(t) = if t is Empty then Empty Node(_, l, r, _) then merge(l, r) //│ fun deleteMin: (Empty | Node[Num]) -> (Empty | Tree[Num]) fun fromList(t, xs) = if xs is Cons(x, xs') then fromList(insert(t, x), xs') Nil then t //│ fun fromList: (Tree[Num], Cons[Num] | Nil) -> Tree[Num] let tree1 = fromList(Empty, 3 :: 4 :: 1 :: 2 :: Nil) tree1 |> show //│ let tree1: Empty | Tree[Num] //│ Str //│ tree1 //│ = Node {} //│ res //│ = '((• 2 (• 3 (• 4 •))) 1 •)' // Remove the smallest element. It should be 1. getMin(tree1) ?? "nothing" let tree1' = deleteMin(tree1) tree1' |> show //│ let tree1': Empty | Tree[Num] //│ Str //│ res //│ = 1 //│ tree1' //│ = Node {} //│ res //│ = '(• 2 (• 3 (• 4 •)))' // Remove one more element. It should be 2. getMin(tree1') ?? "nothing" let tree1'' = deleteMin(tree1') tree1'' |> show //│ let tree1'': Empty | Tree[Num] //│ Str //│ res //│ = 2 //│ tree1'' //│ = Node {} //│ res //│ = '(• 3 (• 4 •))' // Remove one more element. It should be 3. getMin(tree1'') ?? "nothing" let tree1''' = deleteMin(tree1'') tree1''' |> show //│ let tree1''': Empty | Tree[Num] //│ Str //│ res //│ = 3 //│ tree1''' //│ = Node {} //│ res //│ = '(• 4 •)' // Remove the last element. It should be 4. getMin(tree1''') ?? "nothing" let tree1'''' = deleteMin(tree1''') tree1'''' |> show //│ let tree1'''': Empty | Tree[Num] //│ Str //│ res //│ = 4 //│ tree1'''' //│ = Empty { class: [class Empty extends Tree] } //│ res //│ = '•' // =========================================================================== fun drain(t) = if getMin(t) is None then Nil Some(x) then x :: drain(deleteMin(t)) //│ fun drain: (Empty | Node[Num]) -> (Cons[Num] | Nil) fun sorted(xs) = fromList(Empty, xs) |> drain //│ fun sorted: (Cons[Num] | Nil) -> (Cons[Num] | Nil) fun showList(xs) = if xs is Cons(x, Nil) then toString(x) Cons(x, xs') then toString(x) ++ ", " ++ showList(xs') Nil then "" //│ fun showList: (Cons[anything] | Nil) -> Str sorted(3 :: 4 :: 1 :: 2 :: Nil) |> showList sorted(42 :: 58 :: 19 :: 37 :: 44 :: 99 :: 68 :: 60 :: 77 :: 61 :: Nil) |> showList //│ Str //│ res //│ = '1, 2, 3, 4' //│ res //│ = '19, 37, 42, 44, 58, 60, 61, 68, 77, 99' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/LispInterpreter.mls ================================================ :NewDefs // Summon the underlying JavaScript `Object.is` function so that we can compare // any objects. For example, functions do not conforms to `Eql` so we cannot // compare them with `===` directly. But, we can use `Object.is`. declare fun Object: nothing let (=:=) objectEqual: (anything, anything) -> Bool = Object.is fun (=/=) objectNotEqual(x, y) = not(x =:= y) //│ let (=:=) objectEqual: (anything, anything) -> Bool //│ fun (=/=) objectNotEqual: (anything, anything) -> Bool //│ fun Object: nothing //│ objectEqual //│ = [Function: is] type NStr = Str & { length: Int, at: Int -> NStr, charAt: Int -> NStr, charCodeAt: Int -> Int, slice: (Int, Int) -> NStr, startsWith: Str -> Bool, endsWith: Str -> Bool, split: Str -> Array[NStr], trim: () -> NStr, trimStart: () -> NStr, trimEnd: () -> NStr, padStart: (Int, Str) -> NStr, padEnd: (Int, Str) -> NStr, repeat: Int -> NStr, indexOf: Str -> Int, lastIndexOf: Str -> Int, includes: Str -> Bool, } declare fun String: anything -> NStr fun (++) strcat(a, b) = String of concat(a)(b) //│ type NStr = Str & { //│ at: Int -> NStr, //│ charAt: Int -> NStr, //│ charCodeAt: Int -> Int, //│ endsWith: Str -> Bool, //│ includes: Str -> Bool, //│ indexOf: Str -> Int, //│ lastIndexOf: Str -> Int, //│ length: Int, //│ padEnd: (Int, Str) -> NStr, //│ padStart: (Int, Str) -> NStr, //│ repeat: Int -> NStr, //│ slice: (Int, Int) -> NStr, //│ split: Str -> Array[NStr], //│ startsWith: Str -> Bool, //│ trim: () -> NStr, //│ trimEnd: () -> NStr, //│ trimStart: () -> NStr //│ } //│ fun (++) strcat: (Str, Str) -> NStr //│ fun String: anything -> NStr fun (!==) notEqual(x, y) = not(x === y) declare fun parseInt: (Str, Int) -> Int //│ fun (!==) notEqual: forall 'a. (Eql['a], 'a) -> Bool //│ fun parseInt: (Str, Int) -> Int // `List` and its utilities: abstract class List[out T]: Cons[T] | Nil class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List fun (::) cons(head: 'T, tail: List['T]): List['T] = Cons(head, tail) fun reverse(l: List['A]): List['A] = let rec r(l', l) = if l is Cons(x, xs) then r(x :: l', xs) else l' r(Nil, l) fun join(sep: Str, xs: List['B]) = if xs is Cons(x, Nil) then toString(x) Cons(x, xs) then toString(x) ++ sep ++ join(sep, xs) Nil then "" fun showList(xs: List['C]) = "[" ++ join(", ", xs) ++ "]" fun map(f: 'D -> 'E, xs: List['D]): List['E] = if xs is Cons(x, xs) then f(x) :: map(f, xs) Nil then Nil fun equalList(xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool): Bool = if xs is Cons(x, xs') and ys is Cons(y, ys') then equal(x, y) and equalList(xs', ys', equal) Nil and ys is Nil then true else false // `Option` and its utilities: abstract class Option[out A]: Some[A] | None class Some[out A](value: A) extends Option[A] module None extends Option //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. (head: 'T, tail: List['T]) -> List['T] //│ fun reverse: forall 'T0. (l: List['T0]) -> List['T0] //│ fun join: (sep: Str, xs: List[anything]) -> (Str | NStr) //│ fun showList: (xs: List[anything]) -> NStr //│ fun map: forall 'D 'T1. (f: 'D -> 'T1, xs: List['D]) -> List['T1] //│ fun equalList: forall 'A. (xs: List['A], ys: List['A], equal: ('A, 'A) -> Bool) -> Bool //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option // _ ____ _____ // / \ / ___|_ _| // / _ \ \___ \ | | // / ___ \ ___) || | // /_/ \_\____/ |_| // abstract class Expr: Lambda | BuiltIn | Instance | Thunk | StrLit | IntLit | ExprList class Lambda(f: List[Data] -> Data) extends Expr class BuiltIn(e: List[Data] -> Data) extends Expr class Instance(n: Str, m: List[[Str, Expr]]) extends Expr class Thunk(e: () -> Data) extends Expr class StrLit(s: Str) extends Expr class IntLit(n: Int) extends Expr class ExprList(l: List[Expr]) extends Expr fun (=@=) equalExpr(x: Expr, y: Expr): Bool = if x is Lambda(f) and y is Lambda(g) then f =:= g BuiltIn(f) and y is BuiltIn(g) then f =:= g Instance(n, m) and y is Instance(n', m') then let equalPair = ([k, v], [k', v']) => k =:= k' and equalExpr(v, v') n === n' and equalList(m, m', equalPair) Thunk(e) and y is Thunk(e') then e =:= e' StrLit(s) and y is StrLit(s') then s === s' IntLit(n) and y is IntLit(n') then n === n' ExprList(l) and y is ExprList(l') then equalList(l, l', equalExpr) else false fun showExpr(e: Expr): Str = if e is Lambda(f) then "" BuiltIn(f) then "" Instance(n, m) then "" Thunk(e) then "" // StrLit(s) then "str:" ++ s // IntLit(n) then "int:" ++ toString(n) StrLit(s) then s IntLit(n) then toString(n) ExprList(l) then showList(map(showExpr, l)) abstract class Data: Literal | DataList | Symbol class Literal(e: Expr) extends Data class DataList(l: List[Data]) extends Data class Symbol(s: Str) extends Data fun (=#=) equalData(x: Data, y: Data): Bool = if x is Literal(e) and y is Literal(e') then e =@= e' DataList(l) and y is DataList(l') then equalList(l, l', equalData) Symbol(s) and y is Symbol(s') then s === s' else false fun showData(d: Data): Str = if d is Literal(e) then showExpr(e) DataList(l) then "(" ++ join(", ", map(showData, l)) ++ ")" // Symbol(s) then "sym:" ++ s Symbol(s) then s //│ abstract class Expr: BuiltIn | ExprList | Instance | IntLit | Lambda | StrLit | Thunk //│ class Lambda(f: List[Data] -> Data) extends Expr //│ class BuiltIn(e: List[Data] -> Data) extends Expr //│ class Instance(n: Str, m: List[[Str, Expr]]) extends Expr //│ class Thunk(e: () -> Data) extends Expr //│ class StrLit(s: Str) extends Expr //│ class IntLit(n: Int) extends Expr //│ class ExprList(l: List[Expr]) extends Expr //│ fun (=@=) equalExpr: (x: Expr, y: Expr) -> Bool //│ fun showExpr: (e: Expr) -> Str //│ abstract class Data: DataList | Literal | Symbol //│ class Literal(e: Expr) extends Data //│ class DataList(l: List[Data]) extends Data //│ class Symbol(s: Str) extends Data //│ fun (=#=) equalData: (x: Data, y: Data) -> Bool //│ fun showData: (d: Data) -> Str // _____ _ _ // |_ _|___ | | __ ___ _ __ (_) ____ ___ _ __ // | | / _ \ | |/ // _ \| '_ \ | ||_ // _ \| '__| // | || (_) || <| __/| | | || | / /| __/| | // |_| \___/ |_|\_\\___||_| |_||_|/___|\___||_| // fun skipBlank(s: NStr, i: Int): Int = if i < 0 then skipBlank(s, 0) i >= s.length then s.length s.charCodeAt(i) == 32 then skipBlank(s, i + 1) else i //│ fun skipBlank: (s: NStr, i: Int) -> Int fun scanWhile(s: NStr, i: Int, p: Str -> Bool): Option[[Str, Int]] = let rec aux(acc, i) = if i < s.length and s.charAt(i) is ch and p of ch then aux(acc ++ ch, i + 1) else [acc, i] if aux("", i) is ["", _] then None [acc, i] then Some([acc, i]) //│ fun scanWhile: (s: NStr, i: Int, p: Str -> Bool) -> Option[[Str, Int]] fun isDelimiter(ch) = ch !== " " and ch !== "(" and ch !== ")" //│ fun isDelimiter: Eql[" " | "(" | ")"] -> Bool fun nextToken(s: NStr, i: Int): Option[[Str, Int]] = let i' = skipBlank(s, i) if s.charAt(i') is "(" then Some(["(", i' + 1]) ")" then Some([")", i' + 1]) else scanWhile(s, i', isDelimiter) //│ fun nextToken: (s: NStr, i: Int) -> Option[[Str, Int]] fun tokenize(s: Str): List[Str] = let s' = String(s) let rec aux(acc, i) = if nextToken(s', i) is None then reverse(acc) Some([token, i']) then aux(token :: acc, i') aux(Nil, 0) //│ fun tokenize: (s: Str) -> List[Str] showList of tokenize("") showList of tokenize("12") showList of tokenize("x") showList of tokenize("(quote (cons 1 nil))") showList of tokenize("(+ 1 2)") //│ NStr //│ res //│ = '[]' //│ res //│ = '[12]' //│ res //│ = '[x]' //│ res //│ = '[(, quote, (, cons, 1, nil, ), )]' //│ res //│ = '[(, +, 1, 2, )]' // ____ // | _ \ __ _ _ __ ___ ___ _ __ // | |_) |/ _` || '__|/ __| / _ \| '__| // | __/| (_| || | \__ \| __/| | // |_| \__,_||_| |___/ \___||_| // fun isDigit(n) = 48 <= n and n <= 57 fun isDigits(s: Str): Bool = let s' = String(s) let rec aux(i) = if i < s'.length and isDigit of s'.charCodeAt(i) then aux(i + 1) else i === s'.length aux(0) isDigits("123") isDigits("123jump") isDigits("bruh") //│ fun isDigit: Num -> Bool //│ fun isDigits: (s: Str) -> Bool //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = false abstract class ParseResult[out A]: Success[A] | Failure class Success[out A](value: A) extends ParseResult[A] class Failure(error: Str) extends ParseResult[nothing] //│ abstract class ParseResult[A]: Failure | Success[A] //│ class Success[A](value: A) extends ParseResult //│ class Failure(error: Str) extends ParseResult // Notes // ===== // Sometimes, the precedence of comma is less than `of`. // For example, in `[Success of DataList of reverse of acc, tail]`. fun parseExpr(tokens: List[Str]): [ParseResult[Data], List[Str]] = if tokens is Cons("(", tail) then parseList(tail) Cons(")", _) then [Failure("Unmatched closing parenthesis."), tokens] Cons(token, tail) and isDigits(token) then [(Success of Literal of IntLit of parseInt(token, 10)), tail] else [(Success of Symbol of token), tail] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] fun parseList(tokens: List[Str]): [ParseResult[DataList], List[Str]] = let rec collect(acc, ts) = if ts is Cons(")", tail) then [(Success of DataList of reverse of acc), tail] Cons and parseExpr(ts) is [Success(data), rest] then collect(data :: acc, rest) [Failure(_) as failure, rest] then [failure, rest] Nil then [Failure("Unexpected end of input, expect either `)` or more tokens."), Nil] collect(Nil, tokens) //│ fun parseExpr: (tokens: List[Str]) -> [ParseResult[Data], List[Str]] //│ fun parseList: (tokens: List[Str]) -> [ParseResult[DataList], List[Str]] fun showParse(source: Str): Str = if parseExpr(tokenize(source)) is [Success(data), _] then "Ok: " ++ showData(data) [Failure(error), _] then "Error: " ++ error //│ fun showParse: (source: Str) -> Str showParse("(cons 1 nil)") showParse("(cons 1 (cons 2 nil))") showParse("(cons 1 (cons 2 (cons 3 nil)))") showParse("(+ 1 2)") showParse("(car (cons 1 nil))") //│ Str //│ res //│ = 'Ok: (cons, 1, nil)' //│ res //│ = 'Ok: (cons, 1, (cons, 2, nil))' //│ res //│ = 'Ok: (cons, 1, (cons, 2, (cons, 3, nil)))' //│ res //│ = 'Ok: (+, 1, 2)' //│ res //│ = 'Ok: (car, (cons, 1, nil))' // _____ // | ____| _ __ __ __ // | _| | '_ \\ \ / / // | |___ | | | |\ V / // |_____||_| |_| \_/ // // As of the time I wrote this code, MLscript did not yet support term // refinement, so I used lambda expression to implement `Env` first. type Env[T] = Str -> T let emptyEnv: Str -> nothing = _ => error // The lookup function becomes useless because we can just call the `Env`. fun lookup(env, name: Str): 'A = env(name) //│ type Env[T] = Str -> T //│ let emptyEnv: Str -> nothing //│ fun lookup: forall 'A. (Str -> 'A, name: Str) -> 'A //│ emptyEnv //│ = [Function: emptyEnv] // It is tricky to write an annotation that simplifies the inferred type. fun (+:) extend(env, [name, expr]: [Str, 'A]) = (name': Str) => if name' === name then expr else env(name') //│ fun (+:) extend: forall 'A. (Str -> 'A, [Str, 'A]) -> (name': Str) -> 'A fun extendRec(env, name: Str, expr: (Str -> 'A) -> 'A) = let rec env'(name': Str): 'A = if name' === name then expr(env') else env(name') env' //│ fun extendRec: forall 'A. (Str -> 'A, name: Str, expr: (Str -> 'A) -> 'A) -> (name': Str) -> 'A fun extendParameters(env: Str -> 'A, ps: List[Str], vs: List['A]) = if [ps, vs] is [Nil, Nil] then env [Cons(p, ps'), Cons(v, vs')] then extendParameters(extend(env, [p, v]), ps', vs') else error //│ fun extendParameters: forall 'A. (env: Str -> 'A, ps: List[Str], vs: List['A]) -> Str -> 'A fun (++:) extendEntries(env, ps: List[[Str, 'A]]) = if ps is Nil then env Cons(p, ps') then extendEntries(extend(env, p), ps') else error //│ fun (++:) extendEntries: forall 'A 'a. (Str -> 'A & 'a, ps: List[[Str, 'A]]) -> ((name': Str) -> 'A | 'a) let intEnv1 = extend(emptyEnv, ["one", 1]) let intEnv2 = extend(intEnv1, ["two", 2]) let intEnv3 = extend(intEnv2, ["three", 3]) //│ let intEnv1: (name': Str) -> 1 //│ let intEnv2: (name': Str) -> (1 | 2) //│ let intEnv3: (name': Str) -> (1 | 2 | 3) //│ intEnv1 //│ = [Function (anonymous)] //│ intEnv2 //│ = [Function (anonymous)] //│ intEnv3 //│ = [Function (anonymous)] // Make a tuple to save some lines. [intEnv1("one"), intEnv2("one"), intEnv3("one"), intEnv2("two"), intEnv3("two"), intEnv3("three")] //│ [1, 1 | 2, 1 | 2 | 3, 1 | 2, 1 | 2 | 3, 1 | 2 | 3] //│ res //│ = [ 1, 1, 1, 2, 2, 3 ] :re intEnv1("two") intEnv2("three") intEnv3("bruh") //│ 1 | 2 | 3 //│ res //│ Runtime error: //│ Error: an error was thrown //│ res //│ Runtime error: //│ Error: an error was thrown //│ res //│ Runtime error: //│ Error: an error was thrown let intEnv6 = intEnv3 ++: (["four", 4] :: ["five", 5] :: ["six", 6] :: Nil) //│ let intEnv6: (name': Str) -> (1 | 2 | 3 | 4 | 5 | 6) //│ intEnv6 //│ = [Function (anonymous)] [intEnv6("one"), intEnv6("two"), intEnv6("three"), intEnv6("four"), intEnv6("five"), intEnv6("six")] //│ [1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6, 1 | 2 | 3 | 4 | 5 | 6] //│ res //│ = [ 1, 2, 3, 4, 5, 6 ] :re intEnv6("seven") //│ 1 | 2 | 3 | 4 | 5 | 6 //│ res //│ Runtime error: //│ Error: an error was thrown fun builtinEq(args: List[Data]): Data = if args is Cons(x, Cons(y, Nil)) and x =#= y then Literal(IntLit(1)) else Literal(IntLit(0)) else error fun builtinAdd(args: List[Data]): Data = if args is Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x + y)) else error fun builtinSub(args: List[Data]): Data = if args is Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x - y)) else error fun builtinMul(args: List[Data]): Data = if args is Cons(Literal(IntLit(x)), Cons(Literal(IntLit(y)), Nil)) then Literal(IntLit(x * y)) else error let builtinNil = DataList(Nil) fun builtinCons(args: List[Data]): Data = if args is Cons(x, Cons(DataList(xs), Nil)) then DataList(x :: xs) else error fun builtinCar(args: List[Data]): Data = if args is Cons(DataList(Cons(x, _)), Nil) then x else error fun builtinCdr(args: List[Data]): Data = if args is Cons(DataList(Cons(_, xs)), Nil) then DataList(xs) else error fun builtinNull(args: List[Data]): Data = if args is Cons(DataList(Nil), Nil) then Literal(IntLit(1)) else Literal(IntLit(0)) //│ fun builtinEq: (args: List[Data]) -> Data //│ fun builtinAdd: (args: List[Data]) -> Data //│ fun builtinSub: (args: List[Data]) -> Data //│ fun builtinMul: (args: List[Data]) -> Data //│ let builtinNil: DataList //│ fun builtinCons: (args: List[Data]) -> Data //│ fun builtinCar: (args: List[Data]) -> Data //│ fun builtinCdr: (args: List[Data]) -> Data //│ fun builtinNull: (args: List[Data]) -> Data //│ builtinNil //│ = DataList {} let globalEnv = emptyEnv ++: ["eq", Literal(Lambda(builtinEq))] :: ["+", Literal(Lambda(builtinAdd))] :: ["-", Literal(Lambda(builtinSub))] :: ["*", Literal(Lambda(builtinMul))] :: ["nil", builtinNil] :: ["cons", Literal(Lambda(builtinCons))] :: ["car", Literal(Lambda(builtinCar))] :: ["cdr", Literal(Lambda(builtinCdr))] :: ["null", Literal(Lambda(builtinNull))] :: Nil //│ let globalEnv: (name': Str) -> (DataList | Literal) //│ globalEnv //│ = [Function (anonymous)] fun toName(x: Data): Str = if x is Symbol(s) then s else error //│ fun toName: (x: Data) -> Str fun asList(x: Data): List[Data] = if x is DataList(ys) then ys else error //│ fun asList: (x: Data) -> List[Data] fun eval(x: Data, env: Str -> Data): Data = if x is Literal(StrLit(_)) as lit then lit Literal(IntLit(_)) as lit then lit Symbol(name) then env(name) DataList(Cons(Symbol("val"), tail)) and tail is Cons(param, Cons(expr, Cons(rest, Nil))) then eval(rest, env +: [toName(param), eval(expr, env)]) else error DataList(Cons(Symbol("def"), Cons(param, Cons(body, Cons(rest, Nil))))) then let env' = extendRec of env, toName(param), (env => eval(body, env)) eval(rest, env') DataList(Cons(Symbol("if"), Cons(cond, Cons(thenPart, Cons(elsePart, Nil))))) and eval(cond, env) is Literal(IntLit(0)) then eval(elsePart, env) else eval(thenPart, env) DataList(Cons(Symbol("quote"), Cons(y, Nil))) then y DataList(Cons(Symbol("lambda"), Cons(params, Cons(body, Nil)))) then let ps = map(toName, asList(params)) Literal(Lambda(args => eval(body, extendParameters(env, ps, args)))) DataList(Cons(operator, operands)) and eval(operator, env) is Literal(Lambda(f)) then f of map((x) => eval(x, env), operands) else error // application of a non-function else error // unrecognized program //│ fun eval: (x: Data, env: Str -> Data) -> Data fun showEval(source: Str): Str = if parseExpr(tokenize(source)) is [Success(data), _] then "Ok: " ++ showData of eval(data, globalEnv) [Failure(error), _] then "Error: " ++ error //│ fun showEval: (source: Str) -> Str showEval("1") showEval("(+ 1 2)") showEval("(val x 7 (val y 6 (* x y)))") showEval("(quote x)") showEval("(cons 1 nil)") showEval("(car (cons 1 nil))") showEval("(null 0)") showEval("(null nil)") showEval("(def id (lambda (x) x) (id 42))") //│ Str //│ res //│ = 'Ok: 1' //│ res //│ = 'Ok: 3' //│ res //│ = 'Ok: 42' //│ res //│ = 'Ok: x' //│ res //│ = 'Ok: (1)' //│ res //│ = 'Ok: 1' //│ res //│ = 'Ok: 0' //│ res //│ = 'Ok: 1' //│ res //│ = 'Ok: 42' showEval("(def fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) (fact 5))") showEval("(def fib (lambda (n) (if (eq n 0) 0 (if (eq n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (fib 10))") showEval("(def sum (lambda (n) (if (eq n 0) 0 (+ n (sum (- n 1))))) (sum 50))") showEval("(def ack (lambda (m n) (if (eq m 0) (+ n 1) (if (eq n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1)))))) (ack 3 2))") //│ Str //│ res //│ = 'Ok: 120' //│ res //│ = 'Ok: 55' //│ res //│ = 'Ok: 1275' //│ res //│ = 'Ok: 29' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/List.mls ================================================ :NewDefs // TODO abstract class List[out A]: (Cons[A] | Nil) { fun isEmpty: Bool fun map[B]: (A -> B) -> List[B] } class Cons[out A](head: A, tail: List[A]) extends List[A] { fun isEmpty: Bool = false fun map(f) = Cons(f(head), tail.map(f)) } module Nil extends List { fun isEmpty: Bool = true fun map(f) = Nil } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.10: fun map(f) = Cons(f(head), tail.map(f)) //│ ╙── ^^^^ //│ abstract class List[A]: Cons[A] | Nil { //│ fun isEmpty: Bool //│ fun map: forall 'B. (A -> 'B) -> List['B] //│ } //│ class Cons[A](head: A, tail: List[A]) extends List { //│ fun isEmpty: Bool //│ fun map: forall 'A. (A -> 'A) -> Cons['A] //│ } //│ module Nil extends List { //│ fun isEmpty: Bool //│ fun map: anything -> Nil //│ } ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/ListFold.mls ================================================ :NewDefs fun (|>) pipe(x, f) = f(x) fun (++) strcat(s1, s2) = concat(s1)(s2) //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (++) strcat: (Str, Str) -> Str abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[nothing] //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] let oneTwoThree = 1 :: 2 :: 3 :: Nil //│ let oneTwoThree: Cons[1 | 2 | 3] //│ oneTwoThree //│ = Cons {} // Note that JavaScript doesn't have tail call optimization. Therefore, this // implementation is still inefficient in practice. fun join(sep) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') (xs) => if xs is Cons(x, xs') then aux(toString(x), xs') Nil then "" //│ fun join: Str -> (Cons[anything] | Nil) -> Str join(", ")(1 :: 2 :: 3 :: Nil) (1 :: 2 :: 3 :: Nil) |> join(", ") //│ Str //│ res //│ = '1, 2, 3' //│ res //│ = '1, 2, 3' fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" //│ fun showList: (Cons[anything] | Nil) -> Str fun (:::) appendAll(xs, ys) = if xs is Nil then ys Cons(x, xs') then x :: (xs' ::: ys) //│ fun (:::) appendAll: forall 'T 'a. (Cons['T] | Nil, List['T] & 'a) -> (Cons['T] | 'a) ((1 :: 2 :: 3 :: Nil) ::: (4 :: 5 :: 6 :: Nil)) |> showList //│ Str //│ res //│ = '[1, 2, 3, 4, 5, 6]' fun reverse(xs) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) //│ fun reverse: (Cons[anything] | Nil) -> (Cons[nothing] | Nil) (1 :: 2 :: 3 :: Nil) |> showList reverse(1 :: 2 :: 3 :: Nil) |> showList //│ Str //│ res //│ = '[1, 2, 3]' //│ res //│ = '[3, 2, 1]' // __ _ _ _ __ _ // / _| ___ | | __| | | ___ / _| |_ // | |_ / _ \| |/ _` | | / _ \ |_| __| // | _| (_) | | (_| | |__| __/ _| |_ // |_| \___/|_|\__,_|_____\___|_| \__| // fun foldLeft(f)(z) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(f(acc, x), xs') (xs) => aux(z, xs) //│ fun foldLeft: forall 'a 'b. (('a, 'b) -> 'a) -> 'a -> (Cons['b] | Nil) -> 'a let sum = foldLeft((acc, x) => acc + x)(0) sum(Nil) sum(1 :: 2 :: 3 :: Nil) //│ let sum: (Cons[Int] | Nil) -> Int //│ Int //│ sum //│ = [Function (anonymous)] //│ res //│ = 0 //│ res //│ = 6 let product = foldLeft((acc, x) => acc * x)(1) product(Nil) product(1 :: 2 :: 3 :: Nil) //│ let product: (Cons[Int] | Nil) -> Int //│ Int //│ product //│ = [Function (anonymous)] //│ res //│ = 1 //│ res //│ = 6 let length = foldLeft((acc, _) => acc + 1)(0) length(Nil) length(1 :: 2 :: 3 :: Nil) //│ let length: (Cons[anything] | Nil) -> Int //│ Int //│ length //│ = [Function (anonymous)] //│ res //│ = 0 //│ res //│ = 3 let reverse' = foldLeft((acc, x) => x :: acc)(Nil) reverse'(Nil) reverse'(1 :: 2 :: 3 :: Nil) |> showList //│ let reverse': (Cons['T] | Nil) -> (Cons[1 | 2 | 3 | 'T] | Nil) //│ Str //│ reverse' //│ = [Function (anonymous)] //│ res //│ = Nil { class: [class Nil extends List] } //│ res //│ = '[3, 2, 1]' // __ _ _ ____ _ _ _ // / _| ___ | | __| | _ \(_) __ _| |__ | |_ // | |_ / _ \| |/ _` | |_) | |/ _` | '_ \| __| // | _| (_) | | (_| | _ <| | (_| | | | | |_ // |_| \___/|_|\__,_|_| \_\_|\__, |_| |_|\__| // |___/ fun foldRight(f)(z) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then f(x, aux(acc, xs')) (xs) => aux(z, xs) //│ fun foldRight: forall 'a 'b. (('a, 'b) -> 'b) -> 'b -> (Cons['a] | Nil) -> 'b let double = foldRight((x, acc) => x :: x :: acc)(Nil) double(Nil) |> showList double(1 :: 2 :: 3 :: Nil) |> showList //│ let double: (Cons[anything] | Nil) -> (Cons[1 | 2 | 3] | Nil) //│ Str //│ double //│ = [Function (anonymous)] //│ res //│ = '[]' //│ res //│ = '[1, 1, 2, 2, 3, 3]' let flatten = foldRight((xs, acc) => xs ::: acc)(Nil) flatten(Nil) |> showList flatten(oneTwoThree :: oneTwoThree :: oneTwoThree :: Nil) |> showList //│ let flatten: (Cons[Cons['T] | Nil] | Nil) -> (Cons[1 | 2 | 3 | 'T] | Nil) //│ Str //│ flatten //│ = [Function (anonymous)] //│ res //│ = '[]' //│ res //│ = '[1, 2, 3, 1, 2, 3, 1, 2, 3]' fun id(x) = x //│ fun id: forall 'a. 'a -> 'a fun foldLeft'(f: ('A, 'B) -> 'A)(z: 'A) = let g(x, y)(z) = y(f(z, x)) (xs) => foldRight(g)(id)(xs)(z) //│ fun foldLeft': forall 'A 'B. (f: ('A, 'B) -> 'A) -> (z: 'A) -> (Cons['B] | Nil) -> 'A let minus = foldLeft'((acc, x) => acc - x)(0) minus(Nil) minus(1 :: 2 :: 3 :: Nil) //│ let minus: (Cons[Int] | Nil) -> Int //│ Int //│ minus //│ = [Function (anonymous)] //│ res //│ = 0 //│ res //│ = -6 let reverse'' = foldLeft'((acc, x) => x :: acc)(Nil) reverse(Nil) reverse(1 :: 2 :: 3 :: Nil) |> showList //│ let reverse'': (Cons[anything] | Nil) -> (Cons[nothing] | Nil) //│ Str //│ reverse'' //│ = [Function (anonymous)] //│ res //│ = Nil { class: [class Nil extends List] } //│ res //│ = '[3, 2, 1]' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/Option.mls ================================================ :NewDefs abstract class MyOption[out T]: (MySome[T] | MyNone) { virtual fun filter: (p: T -> Bool) -> MyOption[T] } class MySome[out T](val value: T) extends MyOption[T] { fun filter(p) = if p of value then MySome(value) else MyNone } module MyNone extends MyOption[nothing] { fun filter(_) = MyNone } //│ abstract class MyOption[T]: MyNone | MySome[T] { //│ fun filter: (p: T -> Bool) -> MyOption[T] //│ } //│ class MySome[T](value: T) extends MyOption { //│ fun filter: (T -> Bool) -> (MyNone | MySome[T]) //│ } //│ module MyNone extends MyOption { //│ fun filter: anything -> MyNone //│ } // The following code aims to find a workaround that allows the functions // operating ADT to be defined as member functions of classes, and also ensures // the correct generation of JavaScript code. // Create an alias constructor for `Some` // ====================================== // // This works: fun some: forall 'a: 'a -> Option['a] fun some = x => Some(x) // // This doesn't work with `map`: // let some: forall 'a: 'a -> Option['a] = x => Some(x) // // Create an alias constructor for `None` // ====================================== // // This works: // fun none: forall 'a: () -> Option['a] // fun none = () => None // // This also works: // fun none: Option[nothing] // fun none = None // This also works: let none: () -> Option[nothing] = () => None // // This also works but failed in code generation: // let none: Option[nothing] = None // // The class definitions // ===================== abstract class Option[out T]: (Some[T] | None) { virtual fun isEmpty: Bool virtual fun isDefined: Bool virtual fun map: forall 'b: (T -> 'b) -> Option['b] virtual fun flatMap: forall 'b: (T -> Option['b]) -> Option['b] virtual fun filter: (p: T -> Bool) -> Option[T] virtual fun get: T } class Some[out T](val value: T) extends Option[T] { fun isEmpty = false fun isDefined = true fun map(f) = some(f(value)) fun flatMap(f) = f(value) fun filter(p) = if p of value then some(value) else none() fun get = value } module None extends Option[nothing] { fun isEmpty = true fun isDefined = false fun map(_) = none() fun flatMap(_) = none() fun filter(_) = none() fun get = error } //│ fun some: forall 'T. 'T -> Some['T] //│ let none: () -> Option[nothing] //│ abstract class Option[T]: None | Some[T] { //│ fun filter: (p: T -> Bool) -> Option[T] //│ fun flatMap: forall 'b. (T -> Option['b]) -> Option['b] //│ fun get: T //│ fun isDefined: Bool //│ fun isEmpty: Bool //│ fun map: forall 'b0. (T -> 'b0) -> Option['b0] //│ } //│ class Some[T](value: T) extends Option { //│ fun filter: (T -> Bool) -> Option[T] //│ fun flatMap: forall 'c. (T -> 'c) -> 'c //│ fun get: T //│ fun isDefined: true //│ fun isEmpty: false //│ fun map: forall 'a. (T -> 'a) -> Option['a] //│ } //│ module None extends Option { //│ fun filter: anything -> Option[nothing] //│ fun flatMap: anything -> Option[nothing] //│ fun get: nothing //│ fun isDefined: false //│ fun isEmpty: true //│ fun map: anything -> Option[nothing] //│ } //│ fun some: forall 'a0. 'a0 -> Option['a0] //│ none //│ = [Function: none] some(0).map(x => x + 1).get //│ Int //│ res //│ = 1 ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/Permutations.mls ================================================ :NewDefs fun (|>) pipe(x, f) = f(x) fun (++) strcat(s1, s2) = concat(s1)(s2) //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (++) strcat: (Str, Str) -> Str abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List[nothing] fun (::) cons(head, tail) = Cons(head, tail) fun (:::) appendAll(xs: List['A], ys: List['A]): List['A] = if xs is Nil then ys Cons(x, xs') then x :: (xs' ::: ys) fun (:+) append(xs, x): List['A] = xs ::: (x :: Nil) fun reverse(xs) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(x :: acc, xs') aux(Nil, xs) fun join(sep) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') (xs) => if xs is Cons(x, xs') then aux(toString(x), xs') Nil then "" fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" fun foldLeft(f)(z) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(f(acc, x), xs') (xs) => aux(z, xs) fun map(f, xs) = if xs is Nil then Nil Cons(x, xs') then f(x) :: map(f, xs') fun showListList(xs) = "[" ++ join(", ")(map(showList, xs)) ++ "]" //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] //│ fun (:::) appendAll: forall 'T0. (xs: List['T0], ys: List['T0]) -> List['T0] //│ fun (:+) append: forall 'T1. (List['T1], 'T1) -> List['T1] //│ fun reverse: (Cons[anything] | Nil) -> (Cons[nothing] | Nil) //│ fun join: Str -> (Cons[anything] | Nil) -> Str //│ fun showList: (Cons[anything] | Nil) -> Str //│ fun foldLeft: forall 'a 'b. (('a, 'b) -> 'a) -> 'a -> (Cons['b] | Nil) -> 'a //│ fun map: forall 'c 'T2. ('c -> 'T2, Cons['c] | Nil) -> (Cons['T2] | Nil) //│ fun showListList: (Cons[Cons[anything] | Nil] | Nil) -> Str fun insertAllPositions(z) = let rec aux(prev, acc, xs) = if xs is Nil then ((prev :+ z) :: acc) |> reverse Cons(head, tail) then let nu = ((prev :+ z) ::: xs) aux(prev :+ head, nu :: acc, tail) xs => aux(Nil, Nil, xs) //│ fun insertAllPositions: forall 'a. 'a -> (forall 'A 'A0. (Cons['A & 'A0] | Nil) -> (Cons[List['a | 'A0]] | Nil)) //│ where //│ 'A <: 'A0 //│ 'A0 :> 'a //│ <: 'A insertAllPositions(0)(1 :: 2 :: 3 :: Nil) |> showListList //│ Str //│ res //│ = '[[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3], [1, 2, 3, 0]]' :js fun permutations(xs) = if xs is Nil then Nil Cons(x, Nil) then (x :: Nil) :: Nil Cons(x, xs') then foldLeft((acc, ys) => acc ::: insertAllPositions(x)(ys))(Nil)(permutations(xs')) //│ fun permutations: forall 'T 'A. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[List['A]] | Nil) //│ where //│ 'T <: 'A //│ 'A := 'T //│ // Prelude //│ class TypingUnit4 {} //│ const typing_unit4 = new TypingUnit4; //│ // Query 1 //│ globalThis.permutations = function permutations(xs) { //│ return ((() => { //│ let a; //│ return (a = xs, a instanceof Nil.class ? Nil : a instanceof Cons.class ? ((ucs$args_xs$Cons) => ((x) => ((xs$Cons_1) => xs$Cons_1 instanceof Nil.class ? cons(cons(x, Nil), Nil) : ((xs$) => foldLeft((acc, ys) => appendAll(acc, insertAllPositions(x)(ys)))(Nil)(permutations(xs$)))(ucs$args_xs$Cons[1]))(ucs$args_xs$Cons[1]))(ucs$args_xs$Cons[0]))(Cons.unapply(xs)) : (() => { //│ throw new Error("non-exhaustive case expression"); //│ })()); //│ })()); //│ }; //│ // End of generated code permutations(Nil) |> showListList permutations(1 :: Nil) |> showListList permutations(1 :: 2 :: Nil) |> showListList permutations(1 :: 2 :: 3 :: Nil) |> showListList //│ Str //│ res //│ = '[]' //│ res //│ = '[[1]]' //│ res //│ = '[[1, 2], [2, 1]]' //│ res //│ = '[[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]' fun filterNot(f, xs) = if xs is Nil then Nil Cons(x, xs') then if f(x) then filterNot(f, xs') else x :: filterNot(f, xs') //│ fun filterNot: forall 'T. ('T -> Bool, Cons['T] | Nil) -> (Cons['T] | Nil) fun permutations'(xs) = if xs is Nil then Nil Cons(x, Nil) then (x :: Nil) :: Nil else let f(acc, x) = acc ::: map((ys) => x :: ys, permutations'(filterNot((y) => x === y, xs))) foldLeft(f)(Nil)(xs) //│ fun permutations': forall 'T. (Cons['T] | Nil) -> (Cons[Cons['T]] | List[Cons['T]] | Nil) //│ where //│ 'T <: Eql['T] permutations'(Nil) |> showListList permutations'(1 :: Nil) |> showListList permutations'(1 :: 2 :: Nil) |> showListList permutations'(1 :: 2 :: 3 :: Nil) |> showListList //│ Str //│ res //│ = '[]' //│ res //│ = '[[1]]' //│ res //│ = '[[1, 2], [2, 1]]' //│ res //│ = '[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/STLC.mls ================================================ :NewDefs fun (++) concatOp(a, b) = concat(a)(b) //│ fun (++) concatOp: (Str, Str) -> Str fun par(a) = "(" ++ a ++ ")" //│ fun par: Str -> Str type Option[out A] = Some[A] | None class Some[out A](value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None type Result[A, B] = Ok[A] | Err[B] class Ok[A](value: A) class Err[A](message: A) //│ type Result[A, B] = Err[B] | Ok[A] //│ class Ok[A](value: A) //│ class Err[A](message: A) type Type = FunctionType | PrimitiveType class PrimitiveType(name: Str) class FunctionType(lhs: Type, rhs: Type) //│ type Type = FunctionType | PrimitiveType //│ class PrimitiveType(name: Str) //│ class FunctionType(lhs: Type, rhs: Type) // Helpers. fun _f(lhs, rhs) = FunctionType(lhs, rhs) fun _t(name) = PrimitiveType(name) //│ fun _f: (Type, Type) -> FunctionType //│ fun _t: Str -> PrimitiveType type Term = Lit | Var | Abs | App class Lit(tag: Str, ty: Type) class Var(name: Str) class Abs(lhs: Var, lty: Type, rhs: Term) class App(lhs: Term, rhs: Term) // class App(lhs: Term, rhs: Term): Term //│ type Term = Abs | App | Lit | Var //│ class Lit(tag: Str, ty: Type) //│ class Var(name: Str) //│ class Abs(lhs: Var, lty: Type, rhs: Term) //│ class App(lhs: Term, rhs: Term) type TreeMap[A] = Node[A] | Empty class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) module Empty //│ type TreeMap[A] = Empty | Node[A] //│ class Node[A](key: Str, value: A, left: TreeMap[A], right: TreeMap[A]) //│ module Empty fun insert(t, k, v) = if t is Node(k', _, l, r) and slt(k, k') then Node(k', v, insert(l, k, v), r) sgt(k, k') then Node(k', v, l, insert(r, k, v)) _ then Node(k, v, l, r) Empty then Node(k, v, Empty, Empty) fun find(t, k) = if t is Node(k', v, l, r) and slt(k, k') then find(l, k) sgt(k, k') then find(r, k) _ then Some(v) Empty then None //│ fun insert: forall 'A. (Empty | Node['A], Str, 'A) -> Node['A] //│ fun find: forall 'A0. (Empty | Node['A0], Str) -> (None | Some['A0]) fun showType(ty) = if ty is FunctionType(PrimitiveType(name), rhs) then name ++ " -> " ++ showType(rhs) FunctionType(lhs, rhs) then "(" ++ showType(lhs) ++ ") -> " ++ showType(rhs) PrimitiveType(name) then name //│ fun showType: (FunctionType | PrimitiveType) -> Str showType(_t("int")) showType(_f(_t("int"), _t("bool"))) showType(_f(_f(_t("int"), _t("bool")), _t("bool"))) showType(_f(_t("bool"), _f(_t("int"), _t("bool")))) //│ Str //│ res //│ = 'int' //│ res //│ = 'int -> bool' //│ res //│ = '(int -> bool) -> bool' //│ res //│ = 'bool -> int -> bool' fun typeEqual(t1, t2) = if t1 is PrimitiveType(name1) and t2 is PrimitiveType(name2) then eq(name1)(name2) t1 is FunctionType(lhs1, rhs1) and t2 is FunctionType(lhs2, rhs2) then typeEqual(lhs1, lhs2) and typeEqual(rhs1, rhs2) _ then false //│ fun typeEqual: (Object, Object) -> Bool fun showTerm(t) = if t is Lit(tag, _) then tag Var(name) then name Abs(lhs, ty, rhs) then "&" ++ showTerm(lhs) ++ ": " ++ showType(ty) ++ " => " ++ showTerm(rhs) App(Abs(lhs0, ty, lhs1), rhs) then "((" ++ showTerm(Abs(lhs0, ty, rhs)) ++ ") " ++ showTerm(rhs) ++ ")" App(lhs, rhs) then par(showTerm(lhs) ++ " " ++ showTerm(rhs)) //│ fun showTerm: (Abs | App | Lit | Var) -> Str showTerm(Var("x")) showTerm(Abs(Var("x"), _t("int"), Var("y"))) showTerm(App(Var("x"), Var("y"))) showTerm(App(Abs(Var("x"), _t("int"), Var("y")), Var("z"))) //│ Str //│ res //│ = 'x' //│ res //│ = '&x: int => y' //│ res //│ = '(x y)' //│ res //│ = '((&x: int => z) z)' // Removing the return type annotation causes stack overflow. fun typeTerm(t: Term, ctx: TreeMap[Type]): Result[Type, Str] = if t is Lit(_, ty) then Ok(ty) Var(name) and find(ctx, name) is Some(ty) then Ok(ty) None then Err("unbound variable `" ++ name ++ "`") Abs(Var(name), ty, body) and typeTerm(body, insert(ctx, name, ty)) is Ok(resTy) then Ok(FunctionType(ty, resTy)) Err(message) then Err(message) App(lhs, rhs) and typeTerm(lhs, ctx) is Ok(FunctionType(pTy, resTy)) and typeTerm(rhs, ctx) is Ok(aTy) and typeEqual(pTy, aTy) then Ok(resTy) else Err("expect the argument to be of type `" ++ showType(pTy) ++ "` but found `" ++ showType(aTy) ++ "`") Err(message) then Err(message) Ok(PrimitiveType(name)) then Err("cannot apply primitive type `" ++ name ++ "`") Err(message) then Err(message) //│ fun typeTerm: (t: Term, ctx: TreeMap[Type]) -> Result[Type, Str] fun showTypeTerm(t, ctx) = if typeTerm(t, ctx) is Ok(ty) then showTerm(t) ++ " : " ++ showType(ty) Err(message) then "Type error: " ++ message //│ fun showTypeTerm: (Term, TreeMap[Type]) -> Str showTypeTerm(Var("x"), Empty) showTypeTerm(Abs(Var("x"), _t("int"), Var("x")), Empty) showTypeTerm(App(Var("f"), Lit("0", _t("int"))), insert(Empty, "f", _f(_t("int"), _t("int")))) showTypeTerm(App(Var("f"), Lit("0.2", _t("float"))), insert(Empty, "f", _f(_t("int"), _t("int")))) showTypeTerm(App(Var("f"), Lit("0", _t("int"))), insert(Empty, "f", _t("Str"))) //│ Str //│ res //│ = 'Type error: unbound variable `x`' //│ res //│ = '&x: int => x : int -> int' //│ res //│ = '(f 0) : int' //│ res //│ = 'Type error: expect the argument to be of type `int` but found `float`' //│ res //│ = 'Type error: cannot apply primitive type `Str`' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/SimpleLisp.mls ================================================ :NewDefs fun (++) strcat(a: Str, b: Str): Str = concat(a)(b) //│ fun (++) strcat: (a: Str, b: Str) -> Str declare module JSON { fun stringify: (data: anything) -> Str } declare class Function(source: Str) { fun call: () -> nothing } fun fatal(message: Str): nothing = let raise = Function("throw new Error(" ++ JSON.stringify(message) ++ ")") raise.call() //│ declare module JSON { //│ fun stringify: (data: anything) -> Str //│ } //│ declare class Function(source: Str) { //│ fun call: () -> nothing //│ } //│ fun fatal: (message: Str) -> nothing abstract class List[A]: Nil | Cons[A] class Cons[A](head: A, tail: List[A]) extends List[A] module Nil extends List fun (::) cons[A](head: A, tail: List[A]): List[A] = Cons(head, tail) fun map(f: 'A -> 'B, list: List['A]): List['B] = if list is Nil then Nil Cons(head, tail) then Cons(f(head), map(f, tail)) fun showList(sep: Str, showItem: 'A -> Str) = let rec aux(list: List['A]) = if list is Nil then "" Cons(head, Nil) then showItem(head) Cons(head, tail) then showItem(head) ++ sep ++ aux(tail) aux //│ abstract class List[A]: Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) extends List //│ module Nil extends List //│ fun (::) cons: forall 'A. (head: 'A, tail: List['A]) -> List['A] //│ fun map: forall 'A0 'A1. (f: 'A0 -> 'A1, list: List['A0]) -> List['A1] //│ fun showList: forall 'A2. (sep: Str, showItem: 'A2 -> Str) -> (forall 'A3. (list: List['A3]) -> Str) //│ where //│ 'A3 <: 'A2 abstract class Context: Empty | Bind module Empty extends Context class Bind(name: Str, data: Data, tail: Context) extends Context fun find(name: Str, context: Context): Data = if context is Empty then fatal("undefined symbol " ++ name) Bind(name', data, t) then if name' === name then data else find(name, t) abstract class Data: ListData | Quote | Symbol | Literal | Builtin | Thunk class ListData(list: List[Data]) extends Data class Quote(data: Data) extends Data class Symbol(name: Str) extends Data class Literal(value: Int) extends Data class Builtin(impl: (List[Data], Context) -> Data) extends Data class Thunk(impl: (Context) -> Data) extends Data //│ abstract class Context: Bind | Empty //│ module Empty extends Context //│ class Bind(name: Str, data: Data, tail: Context) extends Context //│ fun find: (name: Str, context: Context) -> Data //│ abstract class Data: Builtin | ListData | Literal | Quote | Symbol | Thunk //│ class ListData(list: List[Data]) extends Data //│ class Quote(data: Data) extends Data //│ class Symbol(name: Str) extends Data //│ class Literal(value: Int) extends Data //│ class Builtin(impl: (List[Data], Context) -> Data) extends Data //│ class Thunk(impl: Context -> Data) extends Data fun showData(data: Data): Str = if data is ListData(list) then "(" ++ showList(" ", showData)(list) ++ ")" Quote(data) then "'" ++ showData(data) Symbol(name) then name Literal(value) then toString(value) Builtin(impl) then "" Thunk(impl) then "" //│ fun showData: (data: Data) -> Str fun add(arguments: List[Data], context: Context): Data = if arguments is Cons(Literal(a), Cons(Literal(b), Nil)) then Literal(a + b) else fatal("invalid arguments") //│ fun add: (arguments: List[Data], context: Context) -> Data let context = Bind("+", Builtin(add), Empty) //│ let context: Bind //│ context //│ = Bind {} fun eval(program: Data, context: Context): Data = if program is Literal(value) then Literal(value) Symbol(name) then find(name, context) Thunk(impl) then impl(context) ListData(Cons(Symbol("let"), Cons(Symbol(name), Cons(data, Cons(rest, Nil))))) then let data' = eval(data, context) let context' = Bind(name, data', context) eval(rest, context') ListData(Cons(Symbol("val"), Cons(Symbol(name), Cons(data, Cons(rest, Nil))))) then let data' = Thunk((context) => eval(data, context)) let context' = Bind(name, data', context) eval(rest, context') ListData(Cons(Symbol("if"), Cons(test, Cons(thenPart, Cons(elsePart, Nil))))) and eval(test, context) is Literal(0) then eval(elsePart, context) else eval(thenPart, context) ListData(Cons(Symbol("quote"), Cons(data, Nil))) then data ListData(Cons(Symbol(callee), arguments)) and let callee' = find(callee, context) let arguments' = map(argument => eval(argument, context), arguments) callee' is Builtin(impl) then impl(arguments', context) else fatal("callee is not callable") else fatal("unknown program") //│ fun eval: (program: Data, context: Context) -> Data let data1 = ListData of Symbol("+") :: Literal(1) :: Literal(2) :: Nil showData of data1 showData of eval(data1, context) //│ let data1: ListData //│ Str //│ data1 //│ = ListData {} //│ res //│ = '(+ 1 2)' //│ res //│ = '3' let data2 = ListData of Symbol("let") :: Symbol("x") :: Literal(1) :: (ListData of Symbol("+") :: Symbol("x") :: Literal(2) :: Nil) :: Nil showData of data2 showData of eval(data2, context) //│ let data2: ListData //│ Str //│ data2 //│ = ListData {} //│ res //│ = '(let x 1 (+ x 2))' //│ res //│ = '3' ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/SimpleList.mls ================================================ :NewDefs abstract class List[T]: (Cons[T] | Nil) class Cons[T](val head: T, val tail: List[T]) extends List[T] module Nil extends List //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] 1 :: 2 :: 3 :: 4 :: 5 :: 6 :: 7 :: 8 :: Nil //│ Cons['T] //│ where //│ 'T :> 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 //│ res //│ = Cons {} ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/SimpleTree.mls ================================================ :NewDefs // abstract class Tree[out A]: (Empty | Node[A]) // class Node[out A](value: A, left: Tree[A], right: Tree[A]) extends Tree[A] // module Empty extends Tree // //│ abstract class Tree[A]: Empty | Node[A] // //│ class Node[A](value: A, left: Tree[A], right: Tree[A]) extends Tree // //│ module Empty extends Tree // fun insert(t, v) = if t is // Node(v', l, r) and // v < v' then Node(v', insert(l, v), r) // v > v' then Node(v', l, insert(r, v)) // _ then t // Empty then Node(v, Empty, Empty) // //│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) abstract class List[out T]: (Cons[T] | Nil) class Cons[out T](val head: T, val tail: List[T]) extends List[T] module Nil extends List //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'T. ('T, List['T]) -> Cons['T] 1 :: 2 :: 3 :: 4 :: Nil //│ Cons[1 | 2 | 3 | 4] //│ res //│ = Cons {} ================================================ FILE: shared/src/test/diff/pretyper/ucs/examples/ULC.mls ================================================ :NewDefs fun (++) concatOp(a, b) = concat(a)(b) fun (|>) pipe(a, f) = f(a) fun (!==) notEqual(x, y) = not(x === y) //│ fun (++) concatOp: (Str, Str) -> Str //│ fun (|>) pipe: forall 'a 'b. ('a, 'a -> 'b) -> 'b //│ fun (!==) notEqual: forall 'c. (Eql['c], 'c) -> Bool fun par(a) = "(" ++ a ++ ")" //│ fun par: Str -> Str declare fun String: nothing //│ fun String: nothing let makeString: anything => { length: Int, charCodeAt: Int => Int } = String let StringInstance: { fromCharCode: Int => Str } = String //│ let makeString: anything -> {charCodeAt: Int -> Int, length: Int} //│ let StringInstance: {fromCharCode: Int -> Str} //│ makeString //│ = [Function: String] //│ StringInstance //│ = [Function: String] let anythingToString = toString fun fromCharCode(n: Int) = StringInstance.fromCharCode(n) fun stringCharCodeAt(s: Str, i) = makeString(s).charCodeAt(i) fun stringLength(s: Str) = makeString(s).length //│ let anythingToString: anything -> Str //│ fun fromCharCode: (n: Int) -> Str //│ fun stringCharCodeAt: (s: Str, Int) -> Int //│ fun stringLength: (s: Str) -> Int //│ anythingToString //│ = [Function: toString] type Option[A] = Some[A] | None class Some[A](value: A) { fun toString() = "Some(" ++ anythingToString(value) ++ ")" } module None { fun toString() = "None" } fun showOption(opt) = if opt is Some(x) then "Some(" ++ toString(x) ++ ")" None then "None" //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) { //│ fun toString: () -> Str //│ } //│ module None { //│ fun toString: () -> "None" //│ } //│ fun showOption: (None | Some[anything]) -> Str type List[A] = Cons[A] | Nil class Cons[A](head: A, tail: List[A]) module Nil //│ type List[A] = Cons[A] | Nil //│ class Cons[A](head: A, tail: List[A]) //│ module Nil fun (::) cons(head, tail) = Cons(head, tail) //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] fun join(sep) = let rec aux(acc, xs) = if xs is Nil then acc Cons(x, xs') then aux(acc ++ sep ++ toString(x), xs') (xs) => if xs is Cons(x, xs') then aux(toString(x), xs') Nil then "" //│ fun join: Str -> (forall 'A. (Cons['A] | Nil) -> Str) fun showList(xs) = "[" ++ join(", ")(xs) ++ "]" //│ fun showList: forall 'A. (Cons['A] | Nil) -> Str fun findFirst(list, p) = if list is Nil then None Cons(x, xs) and p(x) then Some(x) else findFirst(xs, p) //│ fun findFirst: forall 'A 'A0. (Cons['A] | Nil, 'A -> Bool) -> (None | Some['A0]) //│ where //│ 'A <: 'A0 fun (:::) listConcat(xs, ys) = if xs is Nil then ys Cons(x, xs') then Cons(x, listConcat(xs', ys)) //│ fun (:::) listConcat: forall 'A 'A0 'a. (Cons['A] | Nil, List['A0] & 'a) -> (Cons['A0] | 'a) //│ where //│ 'A <: 'A0 fun contains(xs, x) = if xs is Nil then false Cons(x', xs') and x === x' then true else contains(xs', x) //│ fun contains: forall 'A. (Cons['A] | Nil, Eql['A]) -> Bool contains("x" :: "y" :: "z" :: Nil, "y") contains("x" :: "y" :: "z" :: Nil, "w") //│ Bool //│ res //│ = true //│ res //│ = false fun exclude(xs, x) = if xs is Nil then Nil Cons(x', xs') and x === x' then exclude(xs', x) else Cons(x', exclude(xs', x)) //│ fun exclude: forall 'A 'A0. (Cons['A] | Nil, Eql['A]) -> (Cons['A0] | Nil) //│ where //│ 'A <: 'A0 exclude("x" :: "y" :: "z" :: Nil, "y") |> showList exclude("x" :: "y" :: "z" :: Nil, "w") |> showList //│ Str //│ res //│ = '[x, z]' //│ res //│ = '[x, y, z]' fun reverse(xs: List['B]): List['B] = let rec aux(acc: List['B], ys: List['B]): List['B] = if ys is Nil then acc Cons(y, ys') then aux(Cons(y, acc), ys') aux(Nil, xs) //│ fun reverse: forall 'B 'A. (xs: List['B]) -> List['A] //│ where //│ 'B <: 'A reverse(1 :: 2 :: 3 :: Nil) |> showList reverse(3 :: 2 :: 1 :: Nil) |> showList //│ Str //│ res //│ = '[3, 2, 1]' //│ res //│ = '[1, 2, 3]' // _____ // |_ _|___ _ __ _ __ ___ // | | / _ \| '__|| '_ ` _ \ // | || __/| | | | | | | | // |_| \___||_| |_| |_| |_| // type Term = Var | Abs | App class Var(name: Str) class Abs(lhs: Var, rhs: Term) class App(lhs: Term, rhs: Term) //│ type Term = Abs | App | Var //│ class Var(name: Str) //│ class Abs(lhs: Var, rhs: Term) //│ class App(lhs: Term, rhs: Term) fun showTerm(t) = if t is Var(name) then toString(name) Abs(lhs, rhs) then "λ" ++ showTerm(lhs) ++ ". " ++ showTerm(rhs) App(Abs(lhs0, lhs1), rhs) then "((" ++ "λ" ++ showTerm(lhs0) ++ ". " ++ showTerm(lhs1) ++ ") " ++ showTerm(rhs) ++ ")" App(lhs, rhs) then par(showTerm(lhs) ++ " " ++ showTerm(rhs)) //│ fun showTerm: (Abs | App | Var) -> Str showTerm(Var("x")) showTerm(Abs(Var("x"), Var("y"))) showTerm(App(Var("x"), Var("y"))) showTerm(App(Abs(Var("x"), Var("y")), Var("z"))) //│ Str //│ res //│ = 'x' //│ res //│ = 'λx. y' //│ res //│ = '(x y)' //│ res //│ = '((λx. y) z)' fun (=:=) equalTerm(t1: Term, t2: Term) = if t1 is Var(x1) and t2 is Var(x2) then x1 === x2 Abs(x1, t1') and t2 is Abs(x2, t2') then (x1 =:= x2) && (t1' =:= t2') App(t1', t1'') and t2 is App(t2', t2'') then (t1' =:= t2') && (t1'' =:= t2'') else false //│ fun (=:=) equalTerm: (t1: Term, t2: Term) -> Bool Var("x") =:= Var("x") Var("x") =:= Var("y") Abs(Var("x"), Var("x")) =:= Abs(Var("x"), Var("x")) Abs(Var("x"), Var("x")) =:= Abs(Var("x"), Var("y")) Abs(Var("x"), Var("y")) =:= Abs(Var("x"), Var("x")) //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = true //│ res //│ = false //│ res //│ = false fun isValue(t) = if t is Abs then true Var then false App then false //│ fun isValue: (Abs | App | Var) -> Bool isValue(Var("x")) isValue(Abs(Var("x"), Var("y"))) isValue(App(Var("x"), Var("y"))) //│ Bool //│ res //│ = false //│ res //│ = true //│ res //│ = false fun hasFree(t, x) = if t is Var(x') then x === x' Abs(Var(x'), body) and x === x' then false Abs(Var(_), body) then hasFree(body, x) App(lhs, rhs) then hasFree(lhs, x) || hasFree(rhs, x) _ then false //│ fun hasFree: (Object, Eql[Str]) -> Bool fun showHasFree(t, n) = showTerm(t) ++ (if hasFree(t, n) then " has " else " DOES NOT have ") ++ "free variable " ++ n //│ fun showHasFree: (Abs | App | Var, Eql[Str] & Str) -> Str showHasFree(Var("x"), "x") showHasFree(Var("x"), "y") showHasFree(Abs(Var("x"), Var("x")), "x") showHasFree(Abs(Var("x"), Var("x")), "y") showHasFree(Abs(Var("x"), Var("y")), "x") showHasFree(Abs(Var("x"), Var("y")), "y") showHasFree(App(Var("x"), Var("y")), "x") showHasFree(App(Var("x"), Var("y")), "y") showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "x") showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") showHasFree(App(Abs(Var("x"), Var("x")), Var("y")), "y") showHasFree(App(Abs(Var("x"), Var("x")), Var("x")), "y") //│ Str //│ res //│ = 'x has free variable x' //│ res //│ = 'x DOES NOT have free variable y' //│ res //│ = 'λx. x DOES NOT have free variable x' //│ res //│ = 'λx. x DOES NOT have free variable y' //│ res //│ = 'λx. y DOES NOT have free variable x' //│ res //│ = 'λx. y has free variable y' //│ res //│ = '(x y) has free variable x' //│ res //│ = '(x y) has free variable y' //│ res //│ = '((λx. x) x) has free variable x' //│ res //│ = '((λx. x) x) DOES NOT have free variable y' //│ res //│ = '((λx. x) y) has free variable y' //│ res //│ = '((λx. x) x) DOES NOT have free variable y' fun freeVars(t) = if t is Var(x) then x :: Nil Abs(Var(x), body) then exclude(freeVars(body), x) App(lhs, rhs) then freeVars(lhs) ::: freeVars(rhs) //│ fun freeVars: forall 'A. (Abs | App | Var) -> (Cons['A] | Nil) //│ where //│ 'A :> Str (freeVars of Var("x")) |> showList (freeVars of Abs(Var("x"), Var("x"))) |> showList (freeVars of Abs(Var("x"), Var("y"))) |> showList (freeVars of App(Var("x"), Var("y"))) |> showList (freeVars of App(Abs(Var("x"), Var("x")), Var("x"))) |> showList //│ Str //│ res //│ = '[x]' //│ res //│ = '[]' //│ res //│ = '[y]' //│ res //│ = '[x, y]' //│ res //│ = '[x]' let alphabet: List[Str] = "a" :: "b" :: "c" :: "d" :: "e" :: "f" :: "g" :: "h" :: "i" :: "j" :: "k" :: "l" :: "m" :: "n" :: "o" :: "p" :: "q" :: "r" :: "s" :: "t" :: "u" :: "v" :: "w" :: "x" :: "y" :: "z" :: Nil //│ let alphabet: List[Str] //│ alphabet //│ = Cons {} fun search(f: 'A -> Option['B], xs: List['A]): Option['B] = if xs is Nil then None Cons(x, xs') and f(x) is Some(x') then Some(x') None then search(f, xs') //│ fun search: forall 'A 'B 'A0. (f: 'A -> Option['B], xs: List['A]) -> Option['A0] //│ where //│ 'B <: 'A0 // ============================================================================= // TO BE INVESTIGATED: The following version does not have a stable type. // The desugared term look like this: // let ucs$test$0 = <=(n, 0,) : Bool in // case ucs$test$0 of { // true => // let x = |>(reverse(acc,), join("",),) in // let ucs$test$1 = contains(xs, x,) : Bool in // case ucs$test$1 of { // true => None; // _ => Some(x,) // }; // _ => { // search(('(' x ')',) => combinations(-(n, 1,), ::(x, acc,), alphabet, xs,), alphabet,) // } // } // fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Option[Str] = // if // n <= 0 and // let x = reverse(acc) |> join("") // contains(xs, x) then None // else Some(x) // else // search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) // //│ fun combinations: forall 'T 'A 'T0. (n: Int, acc: List['T], alphabet: List['T0], xs: List[Str]) -> Option[Str] // //│ where // //│ 'T0 <: 'T & 'A // //│ 'T :> 'A // //│ 'A := 'T // ============================================================================= fun combinations: forall 'a: (Int, List['a], List['a], List[Str]) -> Option[Str] fun combinations(n: Int, acc: List['T], alphabet: List['T], xs: List[Str]): Option[Str] = if n <= 0 then let x = reverse(acc) |> join("") if contains(xs, x) then None else Some(x) else search((x) => combinations(n - 1, x :: acc, alphabet, xs), alphabet) //│ fun combinations: forall 'T. (n: Int, acc: List['T], alphabet: List['T], xs: List[Str]) -> Option[Str] //│ fun combinations: forall 'a. (Int, List['a], List['a], List[Str]) -> Option[Str] combinations(1, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(2, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(3, Nil, 1 :: 2 :: 3 :: Nil, Nil) |> showOption combinations(1, Nil, 1 :: 2 :: 3 :: Nil, "1" :: "3" :: Nil) |> showOption combinations(2, Nil, 1 :: 2 :: 3 :: Nil, "11" :: "12" :: "13" :: Nil) |> showOption combinations(3, Nil, 1 :: 2 :: 3 :: Nil, "111" :: "112" :: "113" :: "121" :: Nil) |> showOption //│ Str //│ res //│ = 'Some(1)' //│ res //│ = 'Some(11)' //│ res //│ = 'Some(111)' //│ res //│ = 'Some(2)' //│ res //│ = 'Some(21)' //│ res //│ = 'Some(122)' fun freshVar(t: Term): Str = let fvs = freeVars(t) let rec aux(n: Int): Str = if combinations(n, Nil, alphabet, fvs) is Some(x) then x None then aux(n + 1) aux(1) //│ fun freshVar: (t: Term) -> Str freshVar(Var("x")) freshVar(App(Var("a"), Var("b"))) freshVar(App(Abs(Var("a"), Var("a")), Var("b"))) //│ Str //│ res //│ = 'a' //│ res //│ = 'c' //│ res //│ = 'a' fun subst(t: Term, x: Str, v: Term): Term = if t is Var(y) and x === y then v Abs(Var(y), t') and x !== y and hasFree(v, y) then let y' = freshVar(t') let t'' = subst(t', y, Var(y')) Abs(Var(y'), subst(t'', x, v)) else Abs(Var(y), subst(t', x, v)) App(lhs, rhs) then App(subst(lhs, x, v), subst(rhs, x, v)) else t //│ fun subst: (t: Term, x: Str, v: Term) -> Term fun showSubst(t, n, v) = showTerm(t) ++ " [" ++ n ++ " / " ++ showTerm(v) ++ "]" ++ " = " ++ showTerm(subst(t, n, v)) //│ fun showSubst: (Abs & Term | App & Term | Var & Term, Str, Abs & Term | App & Term | Var & Term) -> Str showSubst(Var("x"), "x", Var("y")) showSubst(Abs(Var("x"), Var("x")), "x", Var("z")) showSubst(App(Var("x"), Var("y")), "x", Abs(Var("x"), Var("x"))) showSubst(App(Abs(Var("x"), Var("x")), Var("x")), "x", Abs(Var("y"), Var("y"))) showSubst(Abs(Var("x"), App(Var("x"), Var("y"))), "y", Var("x")) showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", Var("x")) showSubst(Abs(Var("z"), Abs(Var("x"), App(Var("z"), App(Var("x"), Var("y"))))), "y", App(Var("x"), Var("z"))) //│ Str //│ res //│ = 'x [x / y] = y' //│ res //│ = 'λx. x [x / z] = λx. x' //│ res //│ = '(x y) [x / λx. x] = ((λx. x) y)' //│ res //│ = '((λx. x) x) [x / λy. y] = ((λx. x) λy. y)' //│ res //│ = 'λx. (x y) [y / x] = λa. (a x)' //│ res //│ = 'λz. λx. (z (x y)) [y / x] = λz. λa. (z (a x))' //│ res //│ = 'λz. λx. (z (x y)) [y / (x z)] = λa. λb. (a (b (x z)))' // ____ _ _ ____ _ // / ___| _ __ ___ __ _ | || | / ___| | |_ ___ _ __ // \___ \ | '_ ` _ \ / _` || || | \___ \ | __|/ _ \| '_ \ // ___) || | | | | || (_| || || | ___) || |_| __/| |_) | // |____/ |_| |_| |_| \__,_||_||_| |____/ \__|\___|| .__/ // |_| type Result = Normal | Stuck | Stepped class Normal(term: Term) { fun toString() = "Normal form: " ++ showTerm(term) } class Stuck(term: Term, part: Term) { fun toString() = "Stuck: " ++ showTerm(part) ++ " in " ++ showTerm(term) } class Stepped(from: Term, to: Term) { fun toString() = showTerm(from) ++ " => " ++ showTerm(to) } //│ type Result = Normal | Stepped | Stuck //│ class Normal(term: Term) { //│ fun toString: () -> Str //│ } //│ class Stuck(term: Term, part: Term) { //│ fun toString: () -> Str //│ } //│ class Stepped(from: Term, to: Term) { //│ fun toString: () -> Str //│ } fun stepByValue(t) = if t is Var then Stuck(t, t) Abs then Normal(t) App(lhs, rhs) and stepByValue(lhs) is Stepped(_, lhs) then Stepped(t, App(lhs, rhs)) Stuck(_, part) then Stuck(t, part) Normal and stepByValue(rhs) is Stepped(_, rhs) then Stepped(t, App(lhs, rhs)) Stuck(_, part) then Stuck(t, part) Normal and lhs is Abs(Var(name), body) then Stepped(t, subst(body, name, rhs)) _ then Stuck(t, lhs) //│ fun stepByValue: (Abs | App | Var) -> (Normal | Stepped | Stuck) toString of stepByValue of Var("x") toString of stepByValue of Abs(Var("x"), Var("y")) toString of stepByValue of App(Var("x"), Var("y")) toString of stepByValue of App(Abs(Var("x"), Var("x")), Var("x")) toString of stepByValue of App(Abs(Var("x"), Var("x")), Abs(Var("y"), Var("y"))) //│ Str //│ res //│ = 'Stuck: x in x' //│ res //│ = 'Normal form: λx. y' //│ res //│ = 'Stuck: x in (x y)' //│ res //│ = 'Stuck: x in ((λx. x) x)' //│ res //│ = '((λx. x) λy. y) => λy. y' // _____ _ _ _ // | ____|__ __ __ _ | | _ _ __ _ | |_ (_) ___ _ __ // | _| \ \ / // _` || || | | | / _` || __|| | / _ \ | '_ \ // | |___ \ V /| (_| || || |_| || (_| || |_ | || (_) || | | | // |_____| \_/ \__,_||_| \__,_| \__,_| \__||_| \___/ |_| |_| // fun eval(step) = let rec aux(t) = if step(t) is result and result is Stepped(_, t') then aux(t') else result aux //│ fun eval: forall 'a 'b. ((Term | 'a) -> (Object & 'b & ~#Stepped | Stepped)) -> 'a -> 'b let evalByValue = eval(stepByValue) //│ let evalByValue: (Abs | App | Var) -> (Normal | Stuck) //│ evalByValue //│ = [Function: aux] // Let's program with Church encoding! let zero = Abs(Var("f"), Abs(Var("x"), Var("x"))) let one = Abs(Var("f"), Abs(Var("x"), App(Var("f"), Var("x")))) toString of stepByValue of zero toString of stepByValue of one let succ = Abs(Var("n"), Abs(Var("f"), Abs(Var("x"), App(Var("f"), App(App(Var("n"), Var("f")), Var("x")))))) toString of stepByValue of succ toString of stepByValue of App(succ, zero) //│ let zero: Abs //│ let one: Abs //│ let succ: Abs //│ Str //│ zero //│ = Abs {} //│ one //│ = Abs {} //│ res //│ = 'Normal form: λf. λx. x' //│ res //│ = 'Normal form: λf. λx. (f x)' //│ succ //│ = Abs {} //│ res //│ = 'Normal form: λn. λf. λx. (f ((n f) x))' //│ res //│ = '((λn. λf. λx. (f ((n f) x))) λf. λx. x) => λf. λx. (f (((λf. λx. x) f) x))' toString of evalByValue of App(succ, App(succ, zero)) toString of evalByValue of App(succ, App(succ, App(succ, App(succ, zero)))) //│ Str //│ res //│ = 'Normal form: λf. λx. (f (((λf. λx. (f (((λf. λx. x) f) x))) f) x))' //│ res //│ = 'Normal form: λf. λx. (f (((λf. λx. (f (((λf. λx. (f (((λf. λx. (f (((λf. λx. x) f) x))) f) x))) f) x))) f) x))' ================================================ FILE: shared/src/test/diff/pretyper/ucs/patterns/AliasPattern.mls ================================================ :NewDefs abstract class Option[out A] class Some[out A](val value: A) extends Option[A] module None extends Option[nothing] //│ abstract class Option[A] //│ class Some[A](value: A) extends Option //│ module None extends Option :ducs:postprocess.result fun map(f) = case Some(x) then Some(f(x)) None as n then n //│ Post-processed UCS term: //│ case case$scrut*‡ of //│ Some*◊ -> //│ let ucs$args_case$scrut$Some*† = (Some).unapply(case$scrut,) //│ let x*‡ = (ucs$args_case$scrut$Some).0 //│ Some(f(x,),) //│ None*† -> //│ let n*† = case$scrut //│ n //│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) :ducs:postprocess.result fun map(f) = case Some(x as n) then Some of f(n) None as n then n //│ Post-processed UCS term: //│ case case$scrut*‡ of //│ Some*◊ -> //│ let ucs$args_case$scrut$Some*† = (Some).unapply(case$scrut,) //│ let x*‡ = (ucs$args_case$scrut$Some).0 //│ let n*‡ = x //│ Some(f(n,),) //│ None*† -> //│ let n*† = case$scrut //│ n //│ fun map: forall 'a 'A. ('a -> 'A) -> (None | Some['a]) -> (None | Some['A]) :ducs:postprocess.result fun foo = case Some(Some(a as b) as c) as d then [a, b, c, d] //│ Post-processed UCS term: //│ let d*† = case$scrut //│ case case$scrut*‡ of //│ Some*◊ -> //│ let ucs$args_case$scrut$Some*† = (Some).unapply(case$scrut,) //│ let case$scrut$Some_0*‡ = (ucs$args_case$scrut$Some).0 //│ case case$scrut$Some_0*‡ of //│ Some*◊ -> //│ let ucs$args_case$scrut$Some_0$Some*† = (Some).unapply(case$scrut$Some_0,) //│ let a*‡ = (ucs$args_case$scrut$Some_0$Some).0 //│ let b*‡ = a //│ let c*‡ = case$scrut$Some_0 //│ [a, b, c, d,] //│ fun foo: forall 'A 'a. (Some[Some['A]] & 'a) -> ['A, 'A, Some['A], 'a] ================================================ FILE: shared/src/test/diff/pretyper/ucs/patterns/Literals.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None class Pair[A, B](x: A, y: B) //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] //│ class Pair[A, B](x: A, y: B) fun f(x) = if x is Some(1) then true else false //│ fun f: (Object & ~#Some | Some[Object]) -> Bool [f(Some(1)), f(None), f(Some(2)), f(Some(-1))] //│ [Bool, Bool, Bool, Bool] //│ res //│ = [ true, false, false, false ] fun f(x) = if x is Some(1) then true //│ fun f: Some[1] -> true fun f(x) = if x is Some(1) then true Some(2) then false //│ fun f: Some[1 | 2] -> Bool [f(Some(1)), f(Some(2))] //│ [Bool, Bool] //│ res //│ = [ true, false ] fun g(x) = if x then 1 else 2 //│ fun g: Bool -> (1 | 2) :e fun test_must_be_boolean(x) = if 0 then 1 else 2 //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.37: fun test_must_be_boolean(x) = if 0 then 1 else 2 //│ ║ ^ //│ ╙── integer literal of type `0` is not an instance of type `Bool` //│ fun test_must_be_boolean: anything -> (1 | 2) fun g(x) = if x is true then 1 else 2 //│ fun g: Object -> (1 | 2) [g(true), g(false), g(None)] //│ [1 | 2, 1 | 2, 1 | 2] //│ res //│ = [ 1, 2, 2 ] fun g(x) = if x && true is true then 1 else 2 //│ fun g: Bool -> (1 | 2) fun h(x) = if (x : Bool) then 1 else 2 //│ fun h: Bool -> (1 | 2) // Currently post-processing cannot handle this case. The desugared term is not // perfect. Also, is the inferred type wrong? fun mix(x) = if x is true then "true" Some(value) then "Some" 0 then "zero" //│ fun mix: (0 | Some[anything] | true) -> ("Some" | "true" | "zero") [mix(true), mix(Some(1)), mix(0)] //│ ["Some" | "true" | "zero", "Some" | "true" | "zero", "Some" | "true" | "zero"] //│ res //│ = [ 'true', 'Some', 'zero' ] :e [mix(false), mix(None)] //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^^^^^^^ //│ ╟── reference of type `false` does not match type `0 | Some[?T] | true` //│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^^^^^^ //│ ╟── reference of type `None` does not match type `0 | Some[?T] | true` //│ ║ l.72: [mix(false), mix(None)] //│ ║ ^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.60: fun mix(x) = if x is //│ ╙── ^ //│ ["Some" | "true" | "zero" | error, "Some" | "true" | "zero" | error] //│ res //│ Runtime error: //│ Error: non-exhaustive case expression fun string_literals(x) = if x is "foo" then 0 "bar" then 1 "qax" then 2 //│ fun string_literals: ("bar" | "foo" | "qax") -> (0 | 1 | 2) class Foo //│ class Foo { //│ constructor() //│ } fun mixed_patterns(x) = if x is Foo then 1 23 then 2 "test" then 3 //│ fun mixed_patterns: ("test" | 23 | Foo) -> (1 | 2 | 3) fun bool_patterns(x) = if x is true then 1 false then 2 //│ fun bool_patterns: Bool -> (1 | 2) fun dual_patterns(x, y) = if x is "some" and y is "none" then 0 x is "none" and y is "some" then 1 x is "some" and y is "some" then 2 x is "none" and y is "none" then 3 //│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) ================================================ FILE: shared/src/test/diff/pretyper/ucs/patterns/SimpleTuple.mls ================================================ :NewDefs // We test the support for simple tuple patterns in this file. // Splice tuple patterns will be implement in the future. fun sum(x, y) = x + y sum(1, 2) //│ fun sum: (Int, Int) -> Int //│ Int //│ res //│ = 3 fun sum'([x, y]) = x + y sum'([1, 2]) //│ fun sum': ([Int, Int]) -> Int //│ Int //│ res //│ = 3 fun sum''(pair) = if pair is [x, y] then x + y sum'([1, 2]) //│ fun sum'': {0: Int, 1: Int} -> Int //│ Int //│ res //│ = 3 // We need native support for tuple patterns in MLscript syntax. // Otherwise the following cases work. fun test(thing) = if thing is [] then 0 test("") test(12) //│ fun test: anything -> 0 //│ 0 //│ res //│ = 0 //│ res //│ = 0 :w // Since pattern destruction is desugared to let bindings, matching with other // classes after the tuple pattern will not work. class Point(x: Int, y: Int) fun discarded_cases(thing) = if thing is [x, y] then x + y Point(x, y) then x + y //│ ╔══[WARNING] this case is unreachable //│ ║ l.47: if thing is //│ ║ ^^^^^^^^ //│ ║ l.48: [x, y] then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.49: Point(x, y) then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── because it is subsumed by the branch //│ ║ l.48: [x, y] then x + y //│ ╙── ^^^^^ //│ class Point(x: Int, y: Int) //│ fun discarded_cases: {0: Int, 1: Int} -> Int :e discarded_cases(Point(0, 0)) //│ ╔══[ERROR] Type `Point` does not contain member `1` //│ ║ l.48: [x, y] then x + y //│ ╙── ^ //│ Int | error //│ res //│ = NaN // A workaround is to move the tuple pattern to the last case. fun working_cases(thing) = if thing is Point(x, y) then x + y [x, y] then x + y //│ fun working_cases: (Object & {0: Int, 1: Int} & ~#Point | Point) -> Int working_cases(Point(0, 0)) //│ Int //│ res //│ = 0 // However, the `Object` type forbids tuples to be used. :e working_cases([0, 0]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.86: working_cases([0, 0]) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[0, 0]` is not an instance of type `Object` //│ ║ l.86: working_cases([0, 0]) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from `case` expression: //│ ║ l.74: if thing is //│ ║ ^^^^^^^^ //│ ║ l.75: Point(x, y) then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.76: [x, y] then x + y //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.74: if thing is //│ ╙── ^^^^^ //│ Int | error //│ res //│ = 0 :w fun not_working(x) = if x is [a, b, c] then a + b + c else 0 //│ ╔══[WARNING] this case is unreachable //│ ║ l.113: 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.111: a + b + c //│ ╙── ^^^^^^^^^ //│ fun not_working: {0: Int, 1: Int, 2: Int} -> Int not_working([1, 2, 3]) //│ Int //│ res //│ = 6 :e not_working([1, 2]) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.128: not_working([1, 2]) //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `{0: 1, 1: 2}` does not have field '2' //│ ║ l.128: not_working([1, 2]) //│ ║ ^^^^^^ //│ ╟── Note: constraint arises from field selection: //│ ║ l.109: if x is //│ ║ ^^^^ //│ ║ l.110: [a, b, c] then //│ ║ ^^^^^^^^^^^^ //│ ╟── from reference: //│ ║ l.109: if x is //│ ╙── ^ //│ Int | error //│ res //│ = NaN ================================================ FILE: shared/src/test/diff/pretyper/ucs/stages/Normalization.mls ================================================ :NewDefs abstract class Option[out T]: Some[T] | None class Some[out T](value: T) extends Option[T] module None extends Option[nothing] //│ abstract class Option[T]: None | Some[T] //│ class Some[T](value: T) extends Option //│ module None extends Option abstract class List[out T]: Cons[T] | Nil class Cons[out T](head: T, tail: List[T]) extends List[T] module Nil extends List[nothing] //│ abstract class List[T]: Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) extends List //│ module Nil extends List fun sum(acc, xs) = if xs is Cons(x, xs) then sum(acc + x, xs) Nil then acc //│ fun sum: (Int, Cons[Int] | Nil) -> Int :ducs:postprocess.result fun test(xs) = if xs is Some(Cons("add", Cons(x, Cons(y, Nil)))) then x + y Some(Cons("mul", Cons(x, Cons(y, Nil)))) then x * y Some(Cons("sum", xs)) then sum(0, xs) Some(Nil) then "nothing" None then "nothing" //│ Post-processed UCS term: //│ case xs*‡ of //│ Some*◊ -> //│ let ucs$args_xs$Some*† = (Some).unapply(xs,) //│ let xs$Some_0*‡ = (ucs$args_xs$Some).0 //│ case xs$Some_0*‡ of //│ Cons*◊ -> //│ let ucs$args_xs$Some_0$Cons*† = (Cons).unapply(xs$Some_0,) //│ let xs$Some_0$Cons_0*‡ = (ucs$args_xs$Some_0$Cons).0 //│ let xs$Some_0$Cons_1*‡ = (ucs$args_xs$Some_0$Cons).1 //│ case xs$Some_0$Cons_0*‡ of //│ "add" -> //│ case xs$Some_0$Cons_1*‡ of //│ Cons*◊ -> //│ let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) //│ let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 //│ let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 //│ case xs$Some_0$Cons_1$Cons_1*‡ of //│ Cons*◊ -> //│ let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) //│ let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 //│ let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 //│ case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of //│ Nil*† -> +(x, y,) //│ _ -> (ucs$args_xs$Some_0$Cons).1 //│ _ -> (ucs$args_xs$Some_0$Cons).1 //│ _ -> (ucs$args_xs$Some_0$Cons).1 //│ "sum" -> //│ let xs*‡ = (ucs$args_xs$Some_0$Cons).1 //│ sum(0, xs,) //│ "mul" -> //│ case xs$Some_0$Cons_1*‡ of //│ Cons*◊ -> //│ let ucs$args_xs$Some_0$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1,) //│ let x*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).0 //│ let xs$Some_0$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons).1 //│ case xs$Some_0$Cons_1$Cons_1*‡ of //│ Cons*◊ -> //│ let ucs$args_xs$Some_0$Cons_1$Cons_1$Cons*† = (Cons).unapply(xs$Some_0$Cons_1$Cons_1,) //│ let y*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).0 //│ let xs$Some_0$Cons_1$Cons_1$Cons_1*‡ = (ucs$args_xs$Some_0$Cons_1$Cons_1$Cons).1 //│ case xs$Some_0$Cons_1$Cons_1$Cons_1*‡ of //│ Nil*† -> *(x, y,) //│ _ -> (ucs$args_xs$Some_0$Cons).1 //│ _ -> (ucs$args_xs$Some_0$Cons).1 //│ _ -> (ucs$args_xs$Some_0$Cons).1 //│ Nil*† -> "nothing" //│ None*† -> "nothing" //│ fun test: (None | Some[Cons[nothing] | Nil]) -> ("nothing" | Int | List[nothing]) :ducs:postprocess.result fun test(x, p) = if x is Int and p(x) then "foo" 0 then "bar" else "qax" //│ Post-processed UCS term: //│ case x*‡ of //│ refined Int*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "foo" //│ _ -> //│ case x*‡ of //│ 0 -> "bar" //│ _ -> "qax" //│ _ -> "qax" //│ fun test: forall 'a. (Int & 'a | Object & ~Int, (Int & 'a) -> Bool) -> ("bar" | "foo" | "qax") :ducs:postprocess.result fun test(x, p) = if x is Str and p(x) then "foo" "lol" then "bar" else "qax" //│ Post-processed UCS term: //│ case x*‡ of //│ refined Str*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "foo" //│ _ -> //│ case x*‡ of //│ "lol" -> "bar" //│ _ -> "qax" //│ _ -> "qax" //│ fun test: forall 'a. (Object & ~Str | Str & 'a, (Str & 'a) -> Bool) -> ("bar" | "foo" | "qax") test(0, _ => true) //│ "bar" | "foo" | "qax" //│ res //│ = 'qax' :ducs:postprocess.result fun test(x, p) = if x is Num and p(x) then "great" 2.71828 then "E" 3.14159 then "PI" else "other" //│ Post-processed UCS term: //│ case x*‡ of //│ refined Num*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "great" //│ _ -> //│ case x*‡ of //│ 2.71828 -> "E" //│ 3.14159 -> "PI" //│ _ -> "other" //│ _ -> "other" //│ fun test: forall 'a. (Object & ~Num | 'a & (2.71828 | 3.14159 | Num & ~2.71828 & ~3.14159), (Num & 'a) -> Bool) -> ("E" | "PI" | "great" | "other") :ducs:postprocess.result fun test(x, p) = if x is Bool and p(x) then "great" true then "false" false then "true" //│ Post-processed UCS term: //│ case x*‡ of //│ refined Bool*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "great" //│ _ -> //│ case x*‡ of //│ true*† -> "false" //│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Bool & 'a) -> Bool) -> ("false" | "great" | "true") :ducs:postprocess.result :ge fun test(x, p) = if x is Object and p(x) then "great" Bool and p(x) then "great, again" true then "false" false then "true" //│ Post-processed UCS term: //│ case x*‡ of //│ refined Object*◊ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> "great" //│ _ -> //│ case x*‡ of //│ refined Bool*◊ -> //│ let ucs$test$1*† = p(x,) : Bool //│ case ucs$test$1*† of //│ true*† -> "great, again" //│ _ -> //│ case x*‡ of //│ true*† -> "false" //│ false*† -> "true" //│ fun test: forall 'a. ('a & Bool, (Object & 'a) -> Bool) -> ("false" | "great" | "great, again" | "true") //│ Code generation encountered an error: //│ unknown match case: Object ================================================ FILE: shared/src/test/diff/pretyper/ucs/stages/PostProcessing.mls ================================================ :NewDefs :ducs:postprocess.result,desugared fun mixed_literals(v) = if v is true then "true" false then "false" 1 then "1" 2 then "2" //│ Post-processed UCS term: //│ case v*‡ of //│ true*† -> "true" //│ 2 -> "2" //│ 1 -> "1" //│ false*† -> "false" //│ Desugared term: case v of { true => "true"; 2 => "2"; 1 => "1"; false => "false" } //│ fun mixed_literals: (1 | 2 | false | true) -> ("1" | "2" | "false" | "true") :ducs:postprocess.result fun separated_by_and(v) = if v is true then "true" _ and v is false then "false" //│ Post-processed UCS term: //│ case v*‡ of //│ true*† -> "true" //│ false*† -> "false" //│ fun separated_by_and: Bool -> ("false" | "true") :ducs:postprocess.result fun dual_patterns(x, y) = if x is "some" and y is "none" then 0 x is "none" and y is "some" then 1 x is "some" and y is "some" then 2 x is "none" and y is "none" then 3 //│ Post-processed UCS term: //│ case x*‡ of //│ "some" -> //│ case y*‡ of //│ "none" -> 0 //│ "some" -> 2 //│ "none" -> //│ case y*‡ of //│ "some" -> 1 //│ "none" -> 3 //│ fun dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) :ducs:postprocess.result fun unordered_dual_patterns(x, y) = if x is "some" and y is "none" then 0 y is "some" and x is "none" then 1 y is "some" and x is "some" then 2 x is "none" and y is "none" then 3 //│ Post-processed UCS term: //│ case x*‡ of //│ "some" -> //│ case y*‡ of //│ "none" -> 0 //│ "some" -> 2 //│ "none" -> //│ case y*‡ of //│ "some" -> 1 //│ "none" -> 3 //│ fun unordered_dual_patterns: ("none" | "some", "none" | "some") -> (0 | 1 | 2 | 3) ================================================ FILE: shared/src/test/diff/pretyper/ucs/stages/Transformation.mls ================================================ :NewDefs class Some[T](value: T) module None type Option[T] = Some[T] | None //│ class Some[T](value: T) //│ module None //│ type Option[T] = None | Some[T] class Cons[T](head: T, tail: List[T]) module Nil type List[T] = Cons[T] | Nil //│ class Cons[T](head: T, tail: List[T]) //│ module Nil //│ type List[T] = Cons[T] | Nil abstract class Either[out A, out B] class Left[out A, out B](value: A) extends Either[A, B] class Right[out A, out B](value: B) extends Either[A, B] //│ abstract class Either[A, B] //│ class Left[A, B](value: A) extends Either //│ class Right[A, B](value: B) extends Either class Pair[A, B](x: A, y: B) { fun mapFirst[C](f: A -> C): Pair[C, B] = Pair(f(x), y) fun mapSecond[C](f: B -> C): Pair[A, C] = Pair(x, f(y)) } //│ class Pair[A, B](x: A, y: B) { //│ fun mapFirst: forall 'C. (f: A -> 'C) -> Pair['C, B] //│ fun mapSecond: forall 'C0. (f: B -> 'C0) -> Pair[A, 'C0] //│ } :ducs:transform.result fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None //│ Transformed UCS term: //│ if //│ xs is //│ Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys,) is Some(tail) then Some(Cons(f(x, y,), tail,),) //│ Nil and ys is Nil then Some(Nil,) //│ else None //│ fun zipWith: forall 'T 'T0 'T1 'T2. (('T, 'T0) -> 'T1, Cons['T] | Object & ~#Cons, Cons['T0] | Object & ~#Cons) -> (None | Some[in List['T1] & 'T2 out Nil | 'T2 | Cons['T1]]) :ducs:transform.result fun getOrElse[T](x: Option[T], default: T): T = if x is Some(value) then value None then default //│ Transformed UCS term: //│ if x is //│ Some(value) then value //│ None then default //│ fun getOrElse: forall 'T. (x: Option['T], default: 'T) -> 'T fun m3(x: Int): Bool = x % 3 == 0 fun m5(x: Int): Bool = x % 5 == 0 //│ fun m3: (x: Int) -> Bool //│ fun m5: (x: Int) -> Bool :ducs:transform.result fun f(x) = if x is Some(v) and m3(v) and m5(v) then "FizzBuzz" Some(v) and m3(v) then "Fizz" Some(v) and m5(v) then "Buzz" else "meh" //│ Transformed UCS term: //│ if x is //│ Some(v) and m3(v,) and m5(v,) then "FizzBuzz" //│ Some(v) and m3(v,) then "Fizz" //│ Some(v) and m5(v,) then "Buzz" //│ else "meh" //│ fun f: (Object & ~#Some | Some[Int]) -> ("Buzz" | "Fizz" | "FizzBuzz" | "meh") ================================================ FILE: shared/src/test/diff/qq/BadConst.mls ================================================ :NewDefs :NoJS :e // :ge code"x => y => 100 + ${Const(x + y)}" //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.7: code"x => y => 100 + ${Const(x + y)}" //│ ║ ^^^^^ //│ ╟── reference of type `Var[?a, ?x]` is not an instance of type `Int` //│ ║ l.7: code"x => y => 100 + ${Const(x + y)}" //│ ╙── ^ //│ Code[anything -> anything -> Int, nothing] :e code"x => y => 100 + code"${Const(x + y)}"" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.17: code"x => y => 100 + code"${Const(x + y)}"" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ Code[anything -> anything -> Int, nothing] :e let res = code"x => code"${x}"" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.24: let res = code"x => code"${x}"" //│ ╙── ^^^^^^^^^^ //│ let res: Code[anything -> error, nothing] ================================================ FILE: shared/src/test/diff/qq/Basic.mls ================================================ :NewDefs :NoJS :e let c = code"x" //│ ╔══[ERROR] identifier not found: x //│ ║ l.6: let c = code"x" //│ ╙── ^ //│ let c: Code[error, nothing] let r = code"x => ${c}" //│ let r: Code[anything -> error, nothing] run(r)(1) //│ error // Bra, Rcd, Sel let rcd = code"{x: 10, y: 100, z: 1000}.z" //│ let rcd: Code[1000, nothing] run(rcd) //│ 1000 // Let, Var, App -> evaluates to 3 let let_var = code"let y = 1 in y + 1 * 2" //│ let let_var: Code[Int, nothing] run(let_var) //│ Int // Fun, If fun id(x) = x //│ fun id: forall 'a. 'a -> 'a let global_fun = code"id(4)" //│ let global_fun: Code[4, nothing] run(global_fun) //│ 4 let local_fun = code"let plus1 = x => x + 1 in plus1(if true then 1 else 2)" //│ let local_fun: Code[Int, nothing] run(local_fun) //│ Int let res = code"let id = x => x in id(3)" //│ let res: Code[3, nothing] run(res) //│ 3 // Tup, Subs let tup = code"let x = 1 in let y = 2 in [x, y].1" //│ let tup: Code[2, nothing] run(tup) //│ 2 // Quasiquote :e let code_in_code = code"let x = code"1" in x" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.65: let code_in_code = code"let x = code"1" in x" //│ ╙── ^^^^^^^ //│ let code_in_code: Code[error, nothing] // Unquoted - Const function let unquoted = code"let x = 3 in ${Const(10)} * x" //│ let unquoted: Code[Int, nothing] run(unquoted) //│ Int code"0.5 *. 2" //│ Code[Num, nothing] code"[(), null]" //│ Code[[(), null], nothing] let str_lit = code""" "hello" """ //│ let str_lit: Code["hello", nothing] let y = 1 let free = code"y" //│ let y: 1 //│ let free: Code[1, nothing] let code_nested = code"let a = 1 in let b = 2 in ${code"a + b"}" //│ let code_nested: Code[Int, nothing] run(code_nested) //│ Int // We don't support non-lexically-bound free variables :e let y = code"x" code"let id = x => ${y} in id(true)" //│ ╔══[ERROR] identifier not found: x //│ ║ l.101: let y = code"x" //│ ╙── ^ //│ let y: Code[error, nothing] //│ Code[error, nothing] run(res) //│ 3 let y = 1 let free_y = code"y" //│ let y: 1 //│ let free_y: Code[1, nothing] let use_free = code"let y = 3 in ${code"let y = 5 in ${code"let y = 100 in ${free_y}"} + y"} + y" //│ let use_free: Code[Int, nothing] run(use_free) //│ Int let res = code"let z = x => ${let c = code"x + 1" in c} in z" //│ let res: Code[Int -> Int, nothing] run(res)(100) //│ Int let f = 0 //│ let f: 0 fun f(x) = x //│ fun f: forall 'a. 'a -> 'a let res = code"f(1)" //│ let res: Code[1, nothing] run(res) //│ 1 class Foo(n: Int) //│ class Foo(n: Int) let res = code"Foo(1)" //│ let res: Code[Foo, nothing] run(res) //│ Foo let codeA = code"let y = 2 in y" let codeB = code"y + y" //│ let codeA: Code[2, nothing] //│ let codeB: Code[Int, nothing] let both = code"${codeA}, ${codeB}" //│ let both: Code[Int, nothing] :e let a = code"let x = 0 in x" let b = code"x" let c = code"${a} + ${b}" //│ ╔══[ERROR] identifier not found: x //│ ║ l.169: let b = code"x" //│ ╙── ^ //│ let a: Code[0, nothing] //│ let b: Code[error, nothing] //│ let c: Code[Int, nothing] run(c) //│ Int let res = code"{x: y} => y" //│ let res: Code[forall 'a. {x: 'a} -> 'a, nothing] run(res)({x:1}) //│ 1 code"x => ${ code"x" }" //│ Code[forall 'a. 'a -> 'a, nothing] run(code"x => x + ${x}")(1) //│ Int run(code"x => x + ${code"x"}")(1) //│ Int val v: Var[Int, nothing] //│ val v: Var[Int, nothing] run(v) //│ Int val v: Var[Int, anything] //│ val v: Var[Int, anything] :e run(v) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.208: run(v) //│ ║ ^^^^^^ //│ ╙── expression of type `anything` does not match type `nothing` //│ Int | error let x = code"1" code"$x + 1" //│ let x: Code[1, nothing] //│ Code[Int, nothing] :pe code"$" //│ ╔══[LEXICAL ERROR] unexpected character '$' //│ ║ l.223: code"$" //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected error in expression position //│ ║ l.223: code"$" //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected end of quasiquote section; an expression was expected here //│ ║ l.223: code"$" //│ ╙── ^ //│ Code[(), nothing] ================================================ FILE: shared/src/test/diff/qq/Basic2.mls ================================================ :NewDefs :NoJS // More specific checks from parser folder let res = code"[]" //│ let res: Code[[], nothing] run(res) //│ [] let res = code"()" run(res) //│ let res: Code[(), nothing] //│ () let res = code"(1,)" run(res) //│ let res: Code[1, nothing] //│ 1 let res = code"let f = (x, y, z) => x + y + x in f of 1,2,3" run(res) //│ let res: Code[Int, nothing] //│ Int :pe code"// Can I comment?" //│ ╔══[PARSE ERROR] Unmatched opening quasiquote //│ ║ l.27: code"// Can I comment?" //│ ╙── ^^^^^ //│ let res = code"[x: 1,]" //│ let res: Code[[x: 1], nothing] run(res) //│ [x: 1] // :ge :e code"let x = {a: 100} in x . a" // "." ? //│ ╔══[ERROR] identifier not found: . //│ ║ l.40: code"let x = {a: 100} in x . a" // "." ? //│ ╙── ^ //│ ╔══[ERROR] identifier not found: a //│ ║ l.40: code"let x = {a: 100} in x . a" // "." ? //│ ╙── ^ //│ Code[error, nothing] code"let x = {a: 100} in x.a" //│ Code[100, nothing] // should be ok let res = code"let x = {a: 100} in x .a" //│ let res: Code[100, nothing] run(res) //│ 100 let app = code"f => x => f(x)" run(app)(id)(1) //│ let app: Code[forall 'a 'b. ('a -> 'b) -> 'a -> 'b, nothing] //│ 1 :e code"f => x => f(${run(x)})" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.67: code"f => x => f(${run(x)})" //│ ║ ^^^^^^ //│ ╙── expression of type `?x` does not match type `nothing` //│ Code[forall 'a 'b. ('a -> 'b) -> Code['a, anything] -> 'b, nothing] run(code"(x => x + 1)(${Const(42)})") //│ Int ================================================ FILE: shared/src/test/diff/qq/Codegen.mls ================================================ :NewDefs :js :e code"x" //│ ╔══[ERROR] identifier not found: x //│ ║ l.6: code"x" //│ ╙── ^ //│ Code[error, nothing] //│ Code generation encountered an error: //│ unbound free variable x is not supported yet. :js :ne code"x => x" //│ Code[forall 'a. 'a -> 'a, nothing] //│ // Prelude //│ let res; //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), Var(x1)))(freshName("x")); //│ // End of generated code :js :ne code"x => ${code"x"}" //│ Code[forall 'a. 'a -> 'a, nothing] //│ // Prelude //│ class TypingUnit2 {} //│ const typing_unit2 = new TypingUnit2; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), Var(x1)))(freshName("x")); //│ // End of generated code :js :ne code"x => ${id(code"x")}" //│ Code[forall 'a. 'a -> 'a, nothing] //│ // Prelude //│ function id(x) { //│ return x; //│ } //│ class TypingUnit3 {} //│ const typing_unit3 = new TypingUnit3; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), id(Var(x1))))(freshName("x")); //│ // End of generated code :js :ne code"1 + 2" //│ Code[Int, nothing] //│ // Prelude //│ class TypingUnit4 {} //│ const typing_unit4 = new TypingUnit4; //│ // Query 1 //│ res = App(Var("+"), IntLit(1), IntLit(2)); //│ // End of generated code :js :ne code"f => f(114)" //│ Code[forall 'a. (114 -> 'a) -> 'a, nothing] //│ // Prelude //│ class TypingUnit5 {} //│ const typing_unit5 = new TypingUnit5; //│ // Query 1 //│ res = ((f1) => Lam(Var(f1), App(Var(f1), Tup(Fld(IntLit(114), false, false, false)))))(freshName("f")); //│ // End of generated code :js :ne code""" "come on! roach!" """ //│ Code["come on! roach!", nothing] //│ // Prelude //│ class TypingUnit6 {} //│ const typing_unit6 = new TypingUnit6; //│ // Query 1 //│ res = StrLit("come on! roach!"); //│ // End of generated code :js :ne code"{x: 114, y: 514}" //│ Code[{x: 114, y: 514}, nothing] //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ res = Bra(Rcd(Var("x"), IntLit(114), Var("y"), IntLit(514))); //│ // End of generated code :js :ne code"{x: 114}.x" //│ Code[114, nothing] //│ // Prelude //│ class TypingUnit8 {} //│ const typing_unit8 = new TypingUnit8; //│ // Query 1 //│ res = Sel(Bra(Rcd(Var("x"), IntLit(114))), Var("x")); //│ // End of generated code :js :ne code"let x = 42 in x" //│ Code[42, nothing] //│ // Prelude //│ class TypingUnit9 {} //│ const typing_unit9 = new TypingUnit9; //│ // Query 1 //│ res = ((x1) => Let(Var(x1), IntLit(42), Var(x1)))(freshName("x")); //│ // End of generated code :js :ne code"(x => x + 1)(41)" //│ Code[Int, nothing] //│ // Prelude //│ class TypingUnit10 {} //│ const typing_unit10 = new TypingUnit10; //│ // Query 1 //│ res = App(Bra(((x1) => Lam(Var(x1), App(Var("+"), Var(x1), IntLit(1))))(freshName("x"))), Tup(Fld(IntLit(41), false, false, false))); //│ // End of generated code :js :ne code"let x = ${Const(42)} in ${code"x"}" //│ Code[Int, nothing] //│ // Prelude //│ class TypingUnit11 {} //│ const typing_unit11 = new TypingUnit11; //│ // Query 1 //│ res = ((x1) => Let(Var(x1), Const(42), Var(x1)))(freshName("x")); //│ // End of generated code :js :ne code"let x = 1 in (x + 1, x + 2)" //│ Code[Int, nothing] //│ // Prelude //│ class TypingUnit12 {} //│ const typing_unit12 = new TypingUnit12; //│ // Query 1 //│ res = ((x1) => Let(Var(x1), IntLit(1), App(Var(","), App(Var("+"), Var(x1), IntLit(1)), App(Var("+"), Var(x1), IntLit(2)))))(freshName("x")); //│ // End of generated code :js :ne code"[1, 2, 3]" //│ Code[[1, 2, 3], nothing] //│ // Prelude //│ class TypingUnit13 {} //│ const typing_unit13 = new TypingUnit13; //│ // Query 1 //│ res = Tup(Fld(IntLit(1), false, false, false), Fld(IntLit(2), false, false, false), Fld(IntLit(3), false, false, false)); //│ // End of generated code :js :ne code"[1, 2, 3].1" //│ Code[2, nothing] //│ // Prelude //│ class TypingUnit14 {} //│ const typing_unit14 = new TypingUnit14; //│ // Query 1 //│ res = Sel(Tup(Fld(IntLit(1), false, false, false), Fld(IntLit(2), false, false, false), Fld(IntLit(3), false, false, false)), Var("1")); //│ // End of generated code :js :ne code"true" //│ Code[true, nothing] //│ // Prelude //│ class TypingUnit15 {} //│ const typing_unit15 = new TypingUnit15; //│ // Query 1 //│ res = Var("true"); //│ // End of generated code :js :ne code"42: Int" //│ Code[Int, nothing] //│ // Prelude //│ class TypingUnit16 {} //│ const typing_unit16 = new TypingUnit16; //│ // Query 1 //│ res = IntLit(42); //│ // End of generated code :js :ne code"(x: Int) => x + 1" //│ Code[(x: Int) -> Int, nothing] //│ // Prelude //│ class TypingUnit17 {} //│ const typing_unit17 = new TypingUnit17; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), App(Var("+"), Var(x1), IntLit(1))))(freshName("x")); //│ // End of generated code :js :ne code"{x: 42} with {y: true}" //│ Code[{x: 42, y: true}, nothing] //│ // Prelude //│ class TypingUnit18 {} //│ const typing_unit18 = new TypingUnit18; //│ // Query 1 //│ res = With(Bra(Rcd(Var("x"), IntLit(42))), Rcd(Var("y"), Var("true"))); //│ // End of generated code :js :ne code"${Const(42)}" //│ Code[Int, nothing] //│ // Prelude //│ class TypingUnit19 {} //│ const typing_unit19 = new TypingUnit19; //│ // Query 1 //│ res = Const(42); //│ // End of generated code :js :ne :e code"code"42"" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.249: code"code"42"" //│ ╙── ^^^^^^^^ //│ Code[error, nothing] //│ Code generation encountered an error: //│ nested quotation is not allowed. :js :ne code"Const(42)" //│ Code[Code[Int, nothing], nothing] //│ // Prelude //│ class TypingUnit21 {} //│ const typing_unit21 = new TypingUnit21; //│ // Query 1 //│ res = App(Var("Const"), Tup(Fld(IntLit(42), false, false, false))); //│ // End of generated code :js :ne code"if (1 == 2) then false else true" //│ Code[Bool, nothing] //│ // Prelude //│ class TypingUnit22 {} //│ const typing_unit22 = new TypingUnit22; //│ // Query 1 //│ res = ((ucs$test$01) => Let(Var(ucs$test$01), Bra(App(Var("=="), IntLit(1), IntLit(2))), CaseOf(Var(ucs$test$01), Case(Var("true"), Var("false"), Wildcard(Var("true"))))))(freshName("ucs$test$0")); //│ // End of generated code :js :ne code"if 3 is 1 then 114 2 then 514 _ then 0 " //│ Code[0 | 114 | 514, nothing] //│ // Prelude //│ class TypingUnit23 {} //│ const typing_unit23 = new TypingUnit23; //│ // Query 1 //│ res = ((ucs$scrut$01) => Let(Var(ucs$scrut$01), IntLit(3), CaseOf(Var(ucs$scrut$01), Case(IntLit(1), IntLit(114), Case(IntLit(2), IntLit(514), Wildcard(IntLit(0)))))))(freshName("ucs$scrut$0")); //│ // End of generated code :js :ne let zero = Const(0) let one = Const(1) code"p => if p.x === ${zero} then true ${one} then false else false " //│ let zero: Code[Int, nothing] //│ let one: Code[Int, nothing] //│ Code[{x: Eql[Int]} -> Bool, nothing] //│ // Prelude //│ class TypingUnit24 {} //│ const typing_unit24 = new TypingUnit24; //│ // Query 1 //│ globalThis.zero = Const(0); //│ // Query 2 //│ globalThis.one = Const(1); //│ // Query 3 //│ res = ((p1) => Lam(Var(p1), ((ucs$cache$01) => Let(Var(ucs$cache$01), Sel(Var(p1), Var("x")), ((ucs$test$01) => Let(Var(ucs$test$01), App(Var("==="), Var(ucs$cache$01), zero), CaseOf(Var(ucs$test$01), Case(Var("true"), Var("true"), Wildcard(((ucs$test$11) => Let(Var(ucs$test$11), App(Var("==="), Var(ucs$cache$01), one), CaseOf(Var(ucs$test$11), Case(Var("true"), Var("false"), Wildcard(Var("false"))))))(freshName("ucs$test$1")))))))(freshName("ucs$test$0"))))(freshName("ucs$cache$0"))))(freshName("p")); //│ // End of generated code :js :ne fun test(cde, f) = code"x => x + ${f(cde)}" //│ fun test: forall 'a 'b. ('a, 'a -> Code[Int, 'b]) -> Code[Int -> Int, 'b] //│ // Prelude //│ class TypingUnit25 {} //│ const typing_unit25 = new TypingUnit25; //│ // Query 1 //│ globalThis.test = function test(cde, f) { //│ return ((() => { //│ return ((x1) => Lam(Var(x1), App(Var("+"), Var(x1), f(cde))))(freshName("x")); //│ })()); //│ }; //│ // End of generated code :js :ne code"""let x = ${log("foo"), Const(0)} in x + 1""" //│ Code[Int, nothing] //│ // Prelude //│ function log(x) { //│ return console.info(x); //│ } //│ class TypingUnit26 {} //│ const typing_unit26 = new TypingUnit26; //│ // Query 1 //│ res = ((x1) => Let(Var(x1), (log("foo") , Const(0)), App(Var("+"), Var(x1), IntLit(1))))(freshName("x")); //│ // End of generated code :ne fun foo(dbg) = code"x => ${let c = code"x" in dbg(c)}" //│ fun foo: forall 'a 'b 'c. (Code['a, ??x] -> Code['b, 'c]) -> Code['a -> 'b, 'c] :ne code"x => let c = 42 in ${code"x"}" //│ Code[forall 'a. 'a -> 'a, nothing] class Ref[A](init: A) { mut val value: A = init } //│ class Ref[A](init: A) { //│ mut val value: A //│ } :ne :js x `=> let v = Ref(x) let _ = y `=> set v.value = y `0 v.value //│ Code[forall 'a. 'a -> 'a, ??y & ~??x] //│ // Prelude //│ class TypingUnit30 {} //│ const typing_unit30 = new TypingUnit30; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), (() => { //│ let v = Ref(Var(x1)); //│ let _ = ((y1) => Lam(Var(y1), (() => { //│ void(v.value = Var(y1)); //│ return IntLit(0); //│ })()))(freshName("y")); //│ return v.value; //│ })()))(freshName("x")); //│ // End of generated code :ne :js x `=> class A(y: Code[Int, nothing]) { val z = x `+ y } A(`0).z //│ Code[Int -> Int, nothing] //│ // Prelude //│ class TypingUnit31 {} //│ const typing_unit31 = new TypingUnit31; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), (() => { //│ const A = (() => { //│ class A { //│ #y; //│ #z; //│ get z() { return this.#z; } //│ constructor(y) { //│ this.#y = y; //│ this.#z = App(Var("+"), Var(x1), y); //│ const z = this.#z; //│ } //│ static //│ unapply(x) { //│ return [x.#y]; //│ } //│ } //│ let ctor; //│ ctor = ((y) => new A(y)); //│ ctor.class = A; //│ return ctor; //│ })(); //│ return A(IntLit(0)).z; //│ })()))(freshName("x")); //│ // End of generated code :ne :js x `=> class A() { constructor() { log(x) } } new A(), x //│ Code[forall 'a. 'a -> 'a, nothing] //│ // Prelude //│ class TypingUnit32 {} //│ const typing_unit32 = new TypingUnit32; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), (() => { //│ const A = (() => { //│ class A { //│ constructor() { //│ log(Var(x1)); //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ } //│ let ctor; //│ ctor = (() => new A()); //│ ctor.class = A; //│ return ctor; //│ })(); //│ return new A.class() , Var(x1); //│ })()))(freshName("x")); //│ // End of generated code class Foo(x: Code[Int, anything]) //│ class Foo(x: Code[Int, anything]) :ne :js x `=> class B() extends Foo(x) `x //│ Code[forall 'a. (Int & 'a) -> 'a, nothing] //│ // Prelude //│ class TypingUnit34 {} //│ const typing_unit34 = new TypingUnit34; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), (() => { //│ const B = (() => { //│ class B extends Foo.class { //│ constructor() { //│ super(Var(x1)); //│ } //│ static //│ unapply(x) { //│ return []; //│ } //│ } //│ let ctor; //│ ctor = (() => new B()); //│ ctor.class = B; //│ return ctor; //│ })(); //│ return Var(x1); //│ })()))(freshName("x")); //│ // End of generated code ================================================ FILE: shared/src/test/diff/qq/EffectfulLetInsertion.mls ================================================ :NewDefs :NoJS declare class Scope[OuterCtx, InnerCtx] { fun bind(code: Code['a, InnerCtx | OuterCtx]): Code['a, InnerCtx] } declare fun scope(body: forall 'in: Scope['in, 'out] -> Code['a, 'in | 'out]): Code['a, 'out] //│ declare class Scope[OuterCtx, InnerCtx] { //│ constructor() //│ fun bind: forall 'a. (code: Code['a, InnerCtx | OuterCtx]) -> Code['a, InnerCtx] //│ } //│ fun scope: forall 'out 'a0. (body: forall 'in. Scope['in, 'out] -> Code['a0, 'in | 'out]) -> Code['a0, 'out] scope of scp => let a = scp.bind(code"123") let b = scp.bind(code"${a} + 1") code"${a} * ${b}" //│ Code[Int, nothing] scope of outer => let a = outer.bind(code"123") code"x => ${ code"${a} * x" }" //│ Code[Int -> Int, nothing] // * Note: expected extrusion! scope of outer => let a = outer.bind(code"123") code"x => ${ scope of inner => let b = outer.bind(code"${a} + ${code"x"}") code"${b} * 2" }" //│ Code[Int -> Int, ??x & ~??in] scope of outer => let a = outer.bind(code"123") code"x => ${ scope of inner => let b = inner.bind(code"${a} + ${code"x"}") code"${b} * 2" }" //│ Code[Int -> Int, nothing] fun (<|) via(f, x) = f(x) //│ fun (<|) via: forall 'a 'b. ('a -> 'b, 'a) -> 'b scope <| outer => let a = outer.bind(code"123") code"x => ${ scope <| inner => let b = inner.bind(code"${a} + ${code"x"}") code"${b} * 2" }" //│ Code[Int -> Int, nothing] // * Note: a failed alternative interface: declare class Scope[Res, OuterCtx, InnerCtx] { fun bind: (nameHint: Str, value: Code['a, InnerCtx | OuterCtx]) -> Code['a, InnerCtx] virtual fun result: Code[Res, InnerCtx] } declare fun reify(scope: forall 'in: () -> Scope['a, 'in, 'out]): Code['a, 'out] //│ declare class Scope[Res, OuterCtx, InnerCtx] { //│ constructor() //│ fun bind: forall 'a. (nameHint: Str, value: Code['a, InnerCtx | OuterCtx]) -> Code['a, InnerCtx] //│ fun result: Code[Res, InnerCtx] //│ } //│ fun reify: forall 'a0 'out. (scope: forall 'in. () -> Scope['a0, 'in, 'out]) -> Code['a0, 'out] :e class Example() extends Scope { let c0 = this.bind("a", code"123") let c1 = this.bind("b", code"${c0} + 1") val result = code"${c1} * ${c1}" } //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.84: let c0 = this.bind("a", code"123") //│ ╙── ^^^^^ //│ ╔══[ERROR] Indirectly-recursive member should have a type signature //│ ║ l.85: let c1 = this.bind("b", code"${c0} + 1") //│ ╙── ^^^^^ //│ ╔══[ERROR] Cannot access `this` while initializing field c0 //│ ║ l.84: let c0 = this.bind("a", code"123") //│ ╙── ^^^^ //│ ╔══[ERROR] Cannot access `this` while initializing field c1 //│ ║ l.85: let c1 = this.bind("b", code"${c0} + 1") //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── The access to `this` is here //│ ║ l.84: let c0 = this.bind("a", code"123") //│ ╙── ^^^^ //│ ╔══[ERROR] Cannot access `this` while initializing field result //│ ║ l.86: val result = code"${c1} * ${c1}" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── The access to `this` is here //│ ║ l.84: let c0 = this.bind("a", code"123") //│ ╙── ^^^^ //│ ╔══[ERROR] Member `bind` is declared (or its declaration is inherited) but is not implemented in `Example` //│ ║ l.83: class Example() extends Scope { //│ ║ ^^^^^^^ //│ ╟── Declared here: //│ ║ l.71: fun bind: (nameHint: Str, value: Code['a, InnerCtx | OuterCtx]) -> Code['a, InnerCtx] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ class Example() extends Scope { //│ fun bind: forall 'a. (nameHint: Str, value: Code['a, 'InnerCtx | 'OuterCtx]) -> Code['a, 'InnerCtx] //│ let c0: error //│ let c1: error //│ val result: Code[Int, nothing] //│ } reify(Example) //│ Code[Int, nothing] ================================================ FILE: shared/src/test/diff/qq/Extrusions.mls ================================================ :NewDefs :NoJS // * This definition is not good as it leads to extrusion; we need `k` to be polymorphic (which requires annotation) fun (%>) bind(rhs, k) = code"let x = ${rhs} in ${k(code"x")}" //│ fun (%>) bind: forall 'a 'b 'c. (Code['a, 'b], Code['a, ??x] -> Code['c, 'b]) -> Code['c, 'b] fun (%>) bind(rhs, k) = code"let x = ${rhs} in ${k(x)}" //│ fun (%>) bind: forall 'a 'b 'c. (Code['a, 'b], Var['a, in ??x out ??x0] -> Code['c, 'b]) -> Code['c, 'b] // * Notice the extrusion fun foo(k) = code"x => ${k(code"x")}" //│ fun foo: forall 'a 'b 'c. (Code['a, ??x] -> Code['b, 'c]) -> Code['a -> 'b, 'c] fun (%>) bind(rhs, k) = code"(x => ${k(code"x")})(${rhs})" //│ fun (%>) bind: forall 'a 'b 'c. (Code['a, 'b], Code['a, ??x] -> Code['c, 'b]) -> Code['c, 'b] fun app = code"(f, x) => f(x)" fun (%>) bind(rhs, k) = code"${app}(x => ${k(code"x")}, ${rhs})" //│ fun app: forall 'a 'b. Code[('a -> 'b, 'a) -> 'b, nothing] //│ fun (%>) bind: forall 'c 'd 'e. (Code['c, 'd], Code['c, ??x] -> Code['e, 'd]) -> Code['e, 'd] fun app(f, x) = f(x) fun bind2(rhs, k) = code"app(x => ${k(code"x")}, ${rhs})" //│ fun app: forall 'a 'b. ('a -> 'b, 'a) -> 'b //│ fun bind2: forall 'c 'd 'e. (Code['c, 'd], Code['c, ??x] -> Code['e, 'd]) -> Code['e, 'd] :e fun foo() = let bar = 42 // not at top level code"bar" //│ ╔══[ERROR] identifier not found: bar //│ ║ l.34: code"bar" //│ ╙── ^^^ //│ fun foo: () -> Code[error, nothing] fun (++) myadd(x, y) = x + y code"1 ++ 2" //│ fun (++) myadd: (Int, Int) -> Int //│ Code[Int, nothing] // G <: ??x | 'a fun bind(rhs, k: forall 'C: Code['A, 'C] -> Code['B, 'C | 'G]) = code"let x = ${rhs} in ${k(code"x")}" //│ fun bind: forall 'A 'a 'B. (Code['A, 'a], k: forall 'C. Code['A, 'C] -> Code['B, 'C | 'a]) -> Code['B, 'a] bind : forall 'a, 'c, 'g: (Code['a, 'g], k: forall 'c: Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] //│ forall 'a 'g. (Code['a, 'g], k: forall 'c. Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] fun foo(dbg) = code"x => ${let c = code"(${x}, ${foo(dbg)})" in dbg(c)}" //│ fun foo: forall 'a 'b. (Code[anything -> 'a, anything] -> Code['a, 'b]) -> Code[anything -> 'a, 'b] fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) //│ fun power: forall 'a. Code[Num, 'a] -> (0 | Int & ~0) -> Code[Num, 'a] :e let p3 = y `=> discard(run(x `=> power(x `+ y)(3))) y //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.64: discard(run(x `=> power(x `+ y)(3))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `?y & ~??x` does not match type `nothing` //│ let p3: Code[forall 'a. (Int & 'a) -> 'a, nothing] ================================================ FILE: shared/src/test/diff/qq/Hygiene.mls ================================================ :NewDefs :NoJS // Test 1 let code_test = code"let x0 = 1 in let x1 = 10 in let x2 = 100 in x0 + x1 + x2" //│ let code_test: Code[Int, nothing] run(code_test) //│ Int // Test 2 fun test: forall 'c: (Int, cde: Code[Int, 'c]) => Code[Int, 'c] fun test(n, cde) = if n > 0 then code"let x = ${Const(n)} in ${test(n - 1, code"${cde} + x")}" else cde //│ fun test: forall 'a. (Int, Code[Int, 'a]) -> Code[Int, 'a] //│ fun test: forall 'c. (Int, cde: Code[Int, 'c]) -> Code[Int, 'c] run(test(3, code"0")) //│ Int fun test(n, cde) = if n > 0 then code"${test(n - 1, code"${cde} + ${Const(n)}")}" else cde //│ fun test: forall 'a 'b. (Int, Code[Int, 'a] & 'b) -> (Code[Int, 'a] | 'b) run(test(3, code"0")) //│ Int // Test 3: note that the free variable makes this unhygienic, which is intended :e fun cde_template(cde) = code"${cde} + x" fun test(n, cde) = if n > 0 then code"let x = ${Const(n)} in ${test(n - 1, cde_template(cde))}" else cde //│ ╔══[ERROR] identifier not found: x //│ ║ l.32: fun cde_template(cde) = code"${cde} + x" //│ ╙── ^ //│ fun cde_template: forall 'a. Code[Int, 'a] -> Code[Int, 'a] //│ fun test: forall 'b 'c. (Int, Code[Int, 'b] & 'c) -> (Code[Int, 'b] | 'c) let c = test(3, code"0") //│ let c: Code[Int, nothing] run(c) //│ Int code"x => ${ code"x" }" //│ Code[forall 'a. 'a -> 'a, nothing] let x4 = code"x => x => x => x" run(x4)(1)(2)(3) //│ let x4: Code[forall 'a. anything -> anything -> 'a -> 'a, nothing] //│ 3 let y4 = code"let y = 1 in let y = 2 in let y = 3 in y" run(y4) //│ let y4: Code[3, nothing] //│ 3 code"x => ${let x = code"42" in run(x), x}" //│ Code[anything -> 42, nothing] ================================================ FILE: shared/src/test/diff/qq/LetInsertion.mls ================================================ :NewDefs :NoJS fun (%>) bind: (Code['a, 'g], k: forall 'c: Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] fun bind[g](rhs, k: forall 'c: Code['a, 'c] -> Code['b, 'c | g]) = code"let x = ${rhs} in ${k(code"x")}" //│ fun bind: forall 'a 'd 'b 'g. (Code['a, 'd], k: forall 'c. Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'd | 'g & ~??x] //│ fun (%>) bind: forall 'a0 'g0 'b0. (Code['a0, 'g0], k: forall 'c0. Code['a0, 'c0] -> Code['b0, 'c0 | 'g0]) -> Code['b0, 'g0] let f(k) = bind of code"123", k //│ let f: forall 'b 'g. (forall 'c. Code[123, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] f(id) //│ Code[123, nothing] bind of code"123", x => x //│ Code[123, nothing] bind of code"123", x => bind of code"${x} + 1", y => code"${y} + ${y}" //│ Code[Int, nothing] code"123" %> x => code"${x} + 1" %> y => code"${y} + ${y}" //│ Code[Int, nothing] fun test(x, y) = code"${x} + ${y}" //│ fun test: forall 'a. (Code[Int, 'a], Code[Int, 'a]) -> Code[Int, 'a] fun test2(x, y, f) = code"${x} + ${y}" %> f //│ fun test2: forall 'g 'b. (Code[Int, 'g], Code[Int, 'g], forall 'c. Code[Int, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] code"(x, y) => ${test2(code"x + 1", code"y * 2", tmp => code"[${tmp}, ${tmp}]") }" //│ Code[(Int, Int) -> [Int, Int], nothing] // * Naive version :w fun gib_naive(n) = let rec body(x, y) = case 0 then x 1 then y n then code"${body(x, code"${x} + ${y}")(n - 1)}" code"(x, y) => ${body(code"x", code"y")(n)}" //│ ╔══[WARNING] the outer binding `n` //│ ║ l.46: fun gib_naive(n) = //│ ║ ^ //│ ╟── is shadowed by name pattern `n` //│ ║ l.50: n then code"${body(x, code"${x} + ${y}")(n - 1)}" //│ ╙── ^ //│ fun gib_naive: (0 | 1 | Int & ~0 & ~1) -> Code[(Int, Int) -> Int, nothing] let gn5 = gib_naive(5) run(gn5) //│ let gn5: Code[(Int, Int) -> Int, nothing] //│ (Int, Int) -> Int // * Expected: needs polymorphic recursion :w fun gib(n) = let rec body(x, y) = case 0 then x 1 then y n then code"${x} + ${y}" %> z => body(x, z)(n - 1) code"(x, y) => ${body(code"x", code"y")(n)}" //│ ╔══[WARNING] the outer binding `n` //│ ║ l.68: fun gib(n) = //│ ║ ^ //│ ╟── is shadowed by name pattern `n` //│ ║ l.72: n then code"${x} + ${y}" %> z => body(x, z)(n - 1) //│ ╙── ^ //│ fun gib: (0 | 1 | Int & ~0 & ~1) -> Code[(Int, Int) -> Int, ??c & ~??y] // * Better version fun body: forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] fun body(x, y) = case 0 then x 1 then y n then code"${x} + ${y}" %> z => body(x, z)(n - 1) //│ fun body: forall 'g. (Code[Int, 'g], Code[Int, 'g]) -> (0 | 1 | Int & ~0 & ~1) -> Code[Int, 'g] //│ fun body: forall 'g0. (Code[Int, 'g0], Code[Int, 'g0]) -> Int -> Code[Int, 'g0] fun gib(n) = code"(x, y) => ${body(code"x", code"y")(n)}" //│ fun gib: Int -> Code[(Int, Int) -> Int, nothing] let gn5 = gib(5) run(gn5) //│ let gn5: Code[(Int, Int) -> Int, nothing] //│ (Int, Int) -> Int // * Alternative version: fun body: forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] fun body(x, y) = case 0 then x 1 then y n then code"let tmp = ${x} + ${y} in ${body(x, code"tmp")(n - 1)}" //│ fun body: forall 'a. (Code[Int, 'a], Code[Int, 'a]) -> (0 | 1 | Int & ~0 & ~1) -> Code[Int, 'a] //│ fun body: forall 'g. (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] fun gib(n) = code"(x, y) => ${body(code"x", code"y")(n)}" //│ fun gib: Int -> Code[(Int, Int) -> Int, nothing] let gn5 = gib(5) run(gn5) //│ let gn5: Code[(Int, Int) -> Int, nothing] //│ (Int, Int) -> Int ================================================ FILE: shared/src/test/diff/qq/LetInsertion_repro.mls ================================================ :NewDefs :NoJS fun (%>) bind: (Code['a, 'g], k: forall 'c: Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] //│ fun (%>) bind: forall 'a 'g 'b. (Code['a, 'g], k: forall 'c. Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] // fun (%>) bind(rhs, k: forall 'c: Code['a, 'c] -> Code['b, 'c]) = code"let x = ${rhs} in ${k(code"x")}" fun body(x, y) = case 0 then x 1 then y n then code"${x} + ${y}" %> z => body(x, z)(n - 1) //│ fun body: forall 'a. (Code[Int, anything] & 'a, Code[Int, anything] & 'a) -> (0 | 1 | Int & ~0 & ~1) -> (Code[Int, anything] | 'a) :e body : forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ ╔══[ERROR] Type error in type ascription //│ ║ l.20: body : forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ ║ ^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.6: fun (%>) bind: (Code['a, 'g], k: forall 'c: Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] //│ ║ ^^ //│ ╟── into type `'g` //│ ║ l.20: body : forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.16: n then code"${x} + ${y}" %> z => body(x, z)(n - 1) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.16: n then code"${x} + ${y}" %> z => body(x, z)(n - 1) //│ ╙── ^^ //│ forall 'g. (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] let c = code"(x, y) => ${body(code"x", code"y")(0)}" //│ let c: Code[(Int, Int) -> Int, ??c & ~??y] :e run(c) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: run(c) //│ ║ ^^^^^^ //│ ╙── expression of type `??c & ~??y` does not match type `nothing` //│ error | (Int, Int) -> Int // * An unnecessary complication, just for testing purposes: fun body(x, y) = case 0 then x 1 then y n then code"${x} + ${y}" %> z => code"${body(x, z)(n - 1)}" //│ fun body: forall 'a. (Code[Int, anything] & 'a, Code[Int, anything] & 'a) -> (0 | 1 | Int & ~0 & ~1) -> (Code[Int, anything] | 'a) code"(x, y) => ${body(code"x", code"y")(0)}" //│ Code[(Int, Int) -> Int, ??c & ~??y] :e body : forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ ╔══[ERROR] Type error in type ascription //│ ║ l.64: body : forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ ║ ^^^^ //│ ╟── type variable `'c` leaks out of its scope //│ ║ l.6: fun (%>) bind: (Code['a, 'g], k: forall 'c: Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] //│ ║ ^^ //│ ╟── into type `'g` //│ ║ l.64: body : forall 'g: (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ ║ ^^ //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this function: //│ ║ l.57: n then code"${x} + ${y}" %> z => code"${body(x, z)(n - 1)}" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── • this reference: //│ ║ l.57: n then code"${x} + ${y}" %> z => code"${body(x, z)(n - 1)}" //│ ╙── ^^ //│ forall 'g. (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] let c = code"(x, y) => ${body(code"x", code"y")(0)}" //│ let c: Code[(Int, Int) -> Int, ??c & ~??y] :e run(c) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.87: run(c) //│ ║ ^^^^^^ //│ ╙── expression of type `??c & ~??y` does not match type `nothing` //│ error | (Int, Int) -> Int ================================================ FILE: shared/src/test/diff/qq/Multiline.mls ================================================ :NewDefs :NoJS code"let arr = [ 1 ] in arr" //│ Code[[1], nothing] let res = code"let foo = (x, y) => x * y foo( 2, 3 )" run(res) //│ let res: Code[Int, nothing] //│ Int let res = code"let a = 0 let b = 1 if a == 0 then true else false " run(res) //│ let res: Code[Bool, nothing] //│ Bool let res = code"if 1 == 0 then false else true " run(res) //│ let res: Code[Bool, nothing] //│ Bool let res = code"if 1 == 0 then false else true " run(res) //│ let res: Code[Bool, nothing] //│ Bool let res = code"{ x: 1, y: 2, z: 3, } " run(res) //│ let res: Code[{x: 1, y: 2, z: 3}, nothing] //│ {x: 1, y: 2, z: 3} let res = code"let x = {a: 100} x .a " run(res) //│ let res: Code[100, nothing] //│ 100 ================================================ FILE: shared/src/test/diff/qq/Nested.mls ================================================ :NewDefs :NoJS :e let t = code"id(code"123")" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.6: let t = code"id(code"123")" //│ ╙── ^^^^^^^^^ //│ let t: Code[error, nothing] let inner0 = code"123" let t = code"id(inner0)" //│ let inner0: Code[123, nothing] //│ let t: Code[Code[123, nothing], nothing] let r = run(t) let rr = run(r) //│ let r: Code[123, nothing] //│ let rr: 123 :e let t = code"let x = code"1" in code"y => ${x} + 1"" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.23: let t = code"let x = code"1" in code"y => ${x} + 1"" //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.23: let t = code"let x = code"1" in code"y => ${x} + 1"" //│ ╙── ^^^^^^^^^^^^^^^^^^^ //│ let t: Code[error, nothing] let inner1 = code"1" let inner2(x) = code"y => ${x} + 1" let t = code"let x = inner1 in inner2(x)" //│ let inner1: Code[1, nothing] //│ let inner2: forall 'a. Code[Int, 'a] -> Code[anything -> Int, 'a] //│ let t: Code[Code[anything -> Int, nothing], nothing] run(run(t))(0) //│ Int let r = run(t) //│ let r: Code[anything -> Int, nothing] run(r) //│ anything -> Int let inner3 = code"2" let t = code"let x = inner1 in let y = inner3 in x" //│ let inner3: Code[2, nothing] //│ let t: Code[Code[1, nothing], nothing] run(r) //│ anything -> Int let c1 = code"let y = inner1 in x => y" //│ let c1: Code[anything -> Code[1, nothing], nothing] let r = run(c1)(0) //│ let r: Code[1, nothing] let t = code"x => x" //│ let t: Code[forall 'a. 'a -> 'a, nothing] run(t)(0) //│ 0 let y = code"1" let r = code"""x => x + ${y}""" //│ let y: Code[1, nothing] //│ let r: Code[Int -> Int, nothing] run(r)(3) //│ Int let x = code"1" let r = code"""x => x + ${x}""" //│ let x: Code[1, nothing] //│ let r: Code[Int -> Int, nothing] run(r)(0) //│ Int let x = code"2" //│ let x: Code[2, nothing] let r = code"""x => (let x = 1 in x + ${x})""" //│ let r: Code[anything -> Int, nothing] run(r)("happy") //│ Int let inner4(x) = code"let x = inner2 in ${x}" let c2 = code"let x = inner1 in inner4(x)" //│ let inner4: anything -> Code[forall 'a. Code[Int, 'a] -> Code[anything -> Int, 'a], nothing] //│ let c2: Code[Code[forall 'a. Code[Int, 'a] -> Code[anything -> Int, 'a], nothing], nothing] let r = run(c2) //│ let r: Code[forall 'a. Code[Int, 'a] -> Code[anything -> Int, 'a], nothing] let rr = run(r) //│ let rr: forall 'a. Code[Int, 'a] -> Code[anything -> Int, 'a] rr(code"123") //│ Code[anything -> Int, nothing] let inner4(x) = code"let y = 100 in ${x}" let c3 = code"let x1 = inner1 in let x2 = inner2 in inner4(x)" //│ let inner4: forall 'a 'b. Code['a, 'b] -> Code['a, 'b] //│ let c3: Code[Code[2, nothing], nothing] let r = run(c3) //│ let r: Code[2, nothing] run(r) //│ 2 let r = code"x => ${code"x + 1"}" //│ let r: Code[Int -> Int, nothing] run(r)(1) //│ Int let r = code"x => ${ x }" run(r) run(code"x => ${ x }") //│ let r: Code[forall 'a. 'a -> 'a, nothing] //│ forall 'b. 'b -> 'b f => code"x => ${ let tmp = f(x) in x }" //│ forall 'a 'b 'c. (Var[in 'a out 'a | 'b, in ??x out ??x0] -> anything) -> Code[('b & 'c) -> ('a | 'c), nothing] let inner5(x) = code"${x}" let r = code"x => inner5(x)" //│ let inner5: forall 'a 'b. Code['a, 'b] -> Code['a, 'b] //│ let r: Code[forall 'c 'd. Code['c, 'd] -> Code['c, 'd], nothing] run(r)(code"1") //│ Code[1, nothing] :e code"code"code"x => ${x}""" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.135: code"code"code"x => ${x}""" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ Code[error, nothing] code"r" //│ Code[Code[forall 'a 'b. Code['a, 'b] -> Code['a, 'b], nothing], nothing] code"x => ${code"${code"${code"x"}"}"}" //│ Code[forall 'a. 'a -> 'a, nothing] fun f(cde) = code"x => ${code"${code"${code"x + ${cde}"}"}"}" run(f(Const(114)))(514) //│ fun f: forall 'a. Code[Int, 'a] -> Code[Int -> Int, 'a] //│ Int fun f(x) = code"${run(x)} + 1" //│ fun f: forall 'a. Code[Code[Int, 'a], nothing] -> Code[Int, 'a] :e code"x => ${code"code"code"${x}"""}" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.157: code"x => ${code"code"code"${x}"""}" //│ ╙── ^^^^^^^^^^^^^^^^ //│ Code[anything -> error, nothing] :e code"xxx => ${code"code"code"${xxx}"""}" //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.164: code"xxx => ${code"code"code"${xxx}"""}" //│ ╙── ^^^^^^^^^^^^^^^^^^ //│ Code[anything -> error, nothing] ================================================ FILE: shared/src/test/diff/qq/NuSyntax.mls ================================================ :NewDefs :NoJS `42 //│ Code[42, nothing] x `=> x //│ Code[forall 'a. 'a -> 'a, nothing] x `=> x //│ Code[forall 'a. 'a -> 'a, nothing] run(x `=> x)(42) //│ 42 `1 `+ `1 //│ Code[Int, nothing] `(1 + 1) //│ Code[Int, nothing] `1 `< `2 //│ Code[Bool, nothing] `1 `<= `2 //│ Code[Bool, nothing] :p `1 `* `2 `+ `3 //│ |`|1| |`|*| |`|2| |`|+| |`|3| //│ AST: TypingUnit(List(Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(Quoted(App(Var(*),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(1))))), (None,Fld(_,Unquoted(Quoted(IntLit(2)))))))))))), (None,Fld(_,Unquoted(Quoted(IntLit(3))))))))))) //│ Parsed: code"+(${code"*(${code"1"}, ${code"2"},)"}, ${code"3"},)"; //│ Code[Int, nothing] :p `1 `* `2 `+ `3 `- `4 //│ |`|1| |`|*| |`|2| |`|+| |`|3| |`|-| |`|4| //│ AST: TypingUnit(List(Quoted(App(Var(-),Tup(List((None,Fld(_,Unquoted(Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(Quoted(App(Var(*),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(1))))), (None,Fld(_,Unquoted(Quoted(IntLit(2)))))))))))), (None,Fld(_,Unquoted(Quoted(IntLit(3)))))))))))), (None,Fld(_,Unquoted(Quoted(IntLit(4))))))))))) //│ Parsed: code"-(${code"+(${code"*(${code"1"}, ${code"2"},)"}, ${code"3"},)"}, ${code"4"},)"; //│ Code[Int, nothing] :p `1 `+ `2 `* `3 //│ |`|1| |`|+| |`|2| |`|*| |`|3| //│ AST: TypingUnit(List(Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(1))))), (None,Fld(_,Unquoted(Quoted(App(Var(*),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(2))))), (None,Fld(_,Unquoted(Quoted(IntLit(3)))))))))))))))))) //│ Parsed: code"+(${code"1"}, ${code"*(${code"2"}, ${code"3"},)"},)"; //│ Code[Int, nothing] :p `1 `+ `2 `* `3 `+ `4 //│ |`|1| |`|+| |`|2| |`|*| |`|3| |`|+| |`|4| //│ AST: TypingUnit(List(Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(1))))), (None,Fld(_,Unquoted(Quoted(App(Var(*),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(2))))), (None,Fld(_,Unquoted(Quoted(IntLit(3))))))))))))))))))), (None,Fld(_,Unquoted(Quoted(IntLit(4))))))))))) //│ Parsed: code"+(${code"+(${code"1"}, ${code"*(${code"2"}, ${code"3"},)"},)"}, ${code"4"},)"; //│ Code[Int, nothing] :e :p 1 + `2 `+ `3 //│ |1| |+| |`|2| |`|+| |`|3| //│ AST: TypingUnit(List(Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(App(Var(+),Tup(List((None,Fld(_,IntLit(1))), (None,Fld(_,Quoted(IntLit(2)))))))))), (None,Fld(_,Unquoted(Quoted(IntLit(3))))))))))) //│ Parsed: code"+(${+(1, code"2",)}, ${code"3"},)"; //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.60: 1 + `2 `+ `3 //│ ║ ^^^^^^ //│ ╟── code fragment of type `Code[2, nothing]` is not an instance of type `Int` //│ ║ l.60: 1 + `2 `+ `3 //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in unquote: //│ ║ l.60: 1 + `2 `+ `3 //│ ║ ^^^^^^ //│ ╙── operator application of type `Int` is not an instance of type `Code` //│ Code[Int, error] :e :p `1 `+ `2 + 3 //│ |`|1| |`|+| |`|2| |+| |3| //│ AST: TypingUnit(List(App(Var(+),Tup(List((None,Fld(_,Quoted(App(Var(+),Tup(List((None,Fld(_,Unquoted(Quoted(IntLit(1))))), (None,Fld(_,Unquoted(Quoted(IntLit(2))))))))))), (None,Fld(_,IntLit(3)))))))) //│ Parsed: +(code"+(${code"1"}, ${code"2"},)", 3,); //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.78: `1 `+ `2 + 3 //│ ║ ^^^^^^^^^^^^ //│ ╟── code fragment of type `Code[?a, ?b | ?c]` is not an instance of type `Int` //│ ║ l.78: `1 `+ `2 + 3 //│ ╙── ^^^^^^^^ //│ Int | error x `=> x `+ `1 //│ Code[Int -> Int, nothing] y `=> `0 `- y //│ Code[Int -> Int, nothing] :e z `=> z + 1 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.97: z `=> z + 1 //│ ║ ^^^^^ //│ ╟── reference of type `Var[?a, ?z]` is not an instance of type `Int` //│ ║ l.97: z `=> z + 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in unquote: //│ ║ l.97: z `=> z + 1 //│ ║ ^^^^^ //│ ╙── operator application of type `Int` is not an instance of type `Code` //│ Code[anything -> error, nothing] (x, y) `=> x `+ y //│ Code[(Int, Int) -> Int, nothing] run((x, y) `=> x `+ y)(1, 2) //│ Int `let x = `42 `in x `+ `1 //│ Code[Int, nothing] fun f(x: Code[Int, nothing]) = `let t = x `in t //│ fun f: (x: Code[Int, nothing]) -> Code[Int, nothing] :e `let x = `0 `in `let y = 1 `in x `+ y //│ ╔══[ERROR] Type mismatch in unquote: //│ ║ l.127: `let x = `0 `in `let y = 1 `in x `+ y //│ ║ ^ //│ ╙── integer literal of type `1` is not an instance of type `Code` //│ Code[Int, nothing] f `=> a `=> f`(a) //│ Code[forall 'a 'b. ('a -> 'b) -> 'a -> 'b, nothing] `let app = (f, a) `=> f`(a) `in `let id = x `=> x `in app`(id, `42) //│ Code[42, nothing] `if `(1 == 1) then `1 else `2 //│ Code[1 | 2, nothing] fun foo(x) = `if x `== `1 then `1 else `2 //│ fun foo: forall 'a. Code[Num, 'a] -> Code[1 | 2, 'a] fun foo(x) = `if `(1 == 1) then x else `2 //│ fun foo: forall 'a 'b. Code['a, 'b] -> Code[2 | 'a, 'b] `if `1 `< `2 `and `1 `> `2 then `0 else `1 //│ Code[0 | 1, nothing] :e `(`42) //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.158: `(`42) //│ ╙── ^^^ //│ Code[error, nothing] :e `(x `=> x) //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.165: `(x `=> x) //│ ╙── ^^^^^^^ //│ Code[error, nothing] :pe `fun foo = 42 `val bar = 0 //│ ╔══[PARSE ERROR] This quote syntax is not supported yet //│ ║ l.172: `fun foo = 42 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected end of input; found 'fun' keyword instead //│ ║ l.172: `fun foo = 42 //│ ╙── ^^^ //│ () let x = `42 //│ let x: Code[42, nothing] :pe :w :e `if x `> `1 then `0 `== `1 then `1 _ then `2 //│ ╔══[PARSE ERROR] This quote syntax is not supported yet //│ ║ l.190: `> `1 then `0 //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected operator here //│ ║ l.190: `> `1 then `0 //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.189: `if x //│ ║ ^ //│ ║ l.190: `> `1 then `0 //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.191: `== `1 then `1 //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.192: _ then `2 //│ ╙── ^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.189: `if x //│ ║ ^ //│ ║ l.190: `> `1 then `0 //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.191: `== `1 then `1 //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.192: _ then `2 //│ ║ ^^^^^^^^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.189: `if x //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.190: `> `1 then `0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.189: `if x //│ ║ ^ //│ ║ l.190: `> `1 then `0 //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.191: `== `1 then `1 //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.192: _ then `2 //│ ║ ^^^^^^^^^^^ //│ ╟── code fragment of type `Code[42, nothing]` is not a function //│ ║ l.183: let x = `42 //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `() -> ?a` //│ ║ l.189: `if x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in unquote: //│ ║ l.190: `> `1 then `0 //│ ║ ^ //│ ╙── undefined literal of type `()` is not an instance of type `Code` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.190: `> `1 then `0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ Code[error, nothing] :pe `(x: Int) => x `+ `2 //│ ╔══[PARSE ERROR] This quote syntax is not supported yet //│ ║ l.249: `(x: Int) => x `+ `2 //│ ╙── ^^^^^^^^ //│ () -> Code[Int, nothing] :pe ` //│ ╔══[PARSE ERROR] This quote syntax is not supported yet //│ ║ l.256: ` //│ ╙── ^ //│ () :pe `42 ` //│ ╔══[PARSE ERROR] This quote syntax is not supported yet //│ ║ l.264: `42 ` //│ ╙── ^^^ //│ Code[42, nothing] ================================================ FILE: shared/src/test/diff/qq/PEPM.mls ================================================ :NewDefs :ne :js let a = code"1 + 1" let b = code"40 + ${a}" //│ let a: Code[Int, nothing] //│ let b: Code[Int, nothing] //│ // Prelude //│ let res; //│ class TypingUnit {} //│ const typing_unit = new TypingUnit; //│ // Query 1 //│ globalThis.a = App(Var("+"), IntLit(1), IntLit(1)); //│ // Query 2 //│ globalThis.b = App(Var("+"), IntLit(40), a); //│ // End of generated code :ne run(b) //│ Int :ne let one = Const(42) let one' = code"42" let one'' = let tmp = 42 in Const(tmp) //│ let one: Code[Int, nothing] //│ let one': Code[42, nothing] //│ let one'': Code[Int, nothing] declare module Math { declare fun floor(x: Num): Int } //│ declare module Math { //│ fun floor: (x: Num) -> Int //│ } fun even(n) = n % 2 == 0 //│ fun even: Int -> Bool // x^n fun power(x, n) = if (n == 0) then 1 else if (even(n)) then let t = power(x, Math.floor(n / 2)) in t * t else x * power(x, n - 1) //│ fun power: (Int, Int) -> Int power(3, 3) power(2, 10) //│ Int //│ res //│ = 27 //│ res //│ = 1024 // x^n :js fun power'(x, n) = if (n == 0) then Const(1) else if (even(n)) then code"let t = ${power'(x, Math.floor(n / 2))} in t * t" else code"${x} * ${power'(x, n - 1)}" //│ fun power': forall 'a. (Code[Int, 'a], Int) -> Code[Int, 'a] //│ // Prelude //│ class TypingUnit7 {} //│ const typing_unit7 = new TypingUnit7; //│ // Query 1 //│ globalThis.power$ = function power$(x, n) { //│ return ((() => { //│ return ((ucs$test$0) => ucs$test$0 === true ? Const(1) : ((ucs$test$0) => ucs$test$0 === true ? ((t1) => Let(Var(t1), power$(x, Math.floor(n / 2)), App(Var("*"), Var(t1), Var(t1))))(freshName("t")) : App(Var("*"), x, power$(x, n - 1)))(even(n)))((n == 0)); //│ })()); //│ }; //│ // End of generated code :ne :js let power10 = run(code"x => ${power'(code"x", 10)}") //│ let power10: Int -> Int //│ // Prelude //│ class TypingUnit8 {} //│ const typing_unit8 = new TypingUnit8; //│ // Query 1 //│ globalThis.power10 = run(((x1) => Lam(Var(x1), power$(Var(x1), 10)))(freshName("x"))); //│ // End of generated code :ne :js code"x => x" //│ Code[forall 'a. 'a -> 'a, nothing] //│ // Prelude //│ class TypingUnit9 {} //│ const typing_unit9 = new TypingUnit9; //│ // Query 1 //│ res = ((x1) => Lam(Var(x1), Var(x1)))(freshName("x")); //│ // End of generated code :ne :js code"let x = 42 in x" //│ Code[42, nothing] //│ // Prelude //│ class TypingUnit10 {} //│ const typing_unit10 = new TypingUnit10; //│ // Query 1 //│ res = ((x1) => Let(Var(x1), IntLit(42), Var(x1)))(freshName("x")); //│ // End of generated code :ne fun test(f) = code"x => x + ${f(code"x + 1")}" //│ fun test: forall 'a. (Code[Int, ??x] -> Code[Int, 'a]) -> Code[Int -> Int, 'a] :ne :e code"x => ${ test(run) }" //│ ╔══[ERROR] Type error in application //│ ║ l.118: code"x => ${ test(run) }" //│ ║ ^^^^^^^^^ //│ ╟── type variable `?x` leaks out of its scope //│ ╟── into type `nothing` //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.113: code"x => x + ${f(code"x + 1")}" //│ ╙── ^ //│ Code[anything -> nothing, nothing] :ne fun assertNotZero(x) = code"if (${x} == 0) then error else ${x}" //│ fun assertNotZero: forall 'a 'b. Code[Num & 'a, 'b] -> Code['a, 'b] :ne fun safeDiv = run(code"x => y => let t = ${assertNotZero(code"y")} in x / t") //│ fun safeDiv: Num -> Num -> Num :ne assertNotZero(Const(42)) //│ Code[Int, nothing] :ne safeDiv(10)(2) //│ Num :ne fun fac(n, dbg) = if (n > 0) then code"let x = ${Const(n)} in ${dbg(code"x")} * ${fac(n - 1, dbg)}" else Const(1) //│ fun fac: forall 'a. (Int, Code[Int, ??x] -> Code[Int, 'a]) -> Code[Int, 'a] fun showAndRet(n: Code['a, anything]) = log(n) n //│ fun showAndRet: forall 'a. (n: Code['a, anything]) -> Code['a, anything] :ne fun bar(x: Code['a, 'b]) = x //│ fun bar: forall 'a 'b. (x: Code['a, 'b]) -> Code['a, 'b] :ne fac(10, showAndRet) //│ Code[Int, anything] fun inc(x, dbg) = dbg(x); inc(code"${x} + 1", dbg) //│ fun inc: forall 'a. (Code[Int, 'a], Code[Int, 'a] -> ()) -> nothing ================================================ FILE: shared/src/test/diff/qq/PEPM2.mls ================================================ :NewDefs :QQ fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) //│ fun power: forall 'a. Code[Num, 'a] -> (0 | Int & ~0) -> Code[Num, 'a] run(x `=> id(x) `* x) //│ Int -> Int //│ res //│ = Quoted: //│ (x_0) => //│ (x_0 * x_0) //│ undefined run(`(x => x + x)) //│ Int -> Int //│ res //│ = Quoted: //│ ((x_1) => //│ (x_1 + x_1)) //│ undefined fun assertNotZero(x) = `if (x `== `0.0) then `error else x fun checkedDiv = x `=> y `=> x `/ assertNotZero(y) run(checkedDiv) //│ fun assertNotZero: forall 'a 'b. Code[Num & 'a, 'b] -> Code['a, 'b] //│ fun checkedDiv: Code[Num -> Num -> Num, nothing] //│ Num -> Num -> Num //│ res //│ = Quoted: //│ (x_2) => //│ (y_0) => //│ (x_2 / let ucs$test$0_0 = //│ (y_0 == 0) //│ in match ucs$test$0_0: //│ case true => error //│ _ => y_0)) //│ undefined fun show: Code[anything, anything] -> Str = _ => "debug" fun inc(dbg) = x `=> let c = x `+ `1 in dbg(c), c inc(c => log(show(c))) //│ fun show: Code[anything, anything] -> Str //│ fun inc: (Code[Int, anything] -> anything) -> Code[Int -> Int, nothing] //│ Code[Int -> Int, nothing] //│ res //│ = '(x_3) =>\n (x_3 + 1)' //│ // Output //│ debug fun body(x, y) = case 0 then x 1 then y n then body(y, x `+ y)(n - 1) fun gib_naive(n) = (x, y) `=> body(x, y)(n) let gn5 = run(gib_naive(5)) //│ fun body: forall 'a. (Code[Int, 'a], Code[Int, 'a]) -> (0 | 1 | Int & ~0 & ~1) -> Code[Int, 'a] //│ fun gib_naive: (0 | 1 | Int & ~0 & ~1) -> Code[(Int, Int) -> Int, nothing] //│ let gn5: (Int, Int) -> Int //│ gn5 //│ = Quoted: //│ (x_4) => //│ (y_1) => //│ ((y_1 + (x_4 + y_1)) + ((x_4 + y_1) + (y_1 + (x_4 + y_1)))) //│ undefined fun bind(rhs, k) = `let x = rhs `in k(x) //│ fun bind: forall 'a 'b 'c. (Code['a, 'b], Var['a, in ??x out ??x0] -> Code['c, 'b]) -> Code['c, 'b] fun bind(rhs, k: forall 'g : Code['a, 'g] -> Code['b, 'c | 'g]) = `let x = rhs `in k(x) //│ fun bind: forall 'a 'c 'b. (Code['a, 'c], k: forall 'g. Code['a, 'g] -> Code['b, 'c | 'g]) -> Code['b, 'c] bind : forall 'a, 'c, 'g: (Code['a, 'g], k: forall 'c: Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] //│ forall 'a 'g. (Code['a, 'g], k: forall 'c. Code['a, 'c] -> Code['b, 'c | 'g]) -> Code['b, 'g] //│ res //│ = [Function: bind1] fun body: forall 'g : (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] fun body(x, y) = case 0 then x 1 then y n then bind of x `+ y, z => body(y, z)(n - 1) fun gib(n) = ((x, y) `=> body(x, y)(n)) let g5 = run(gib(5)) //│ fun body: forall 'a. (Code[Int, 'a], Code[Int, 'a]) -> (0 | 1 | Int & ~0 & ~1) -> Code[Int, 'a] //│ fun gib: Int -> Code[(Int, Int) -> Int, nothing] //│ let g5: (Int, Int) -> Int //│ fun body: forall 'g. (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] //│ g5 //│ = Quoted: //│ (x_5) => //│ (y_2) => //│ let x_6 = //│ (x_5 + y_2) //│ in let x_7 = //│ (y_2 + x_6) //│ in let x_8 = //│ (x_6 + x_7) //│ in let x_9 = //│ (x_7 + x_8) //│ in x_9 //│ undefined ================================================ FILE: shared/src/test/diff/qq/PseudoCod.mls ================================================ // * A testbed to reproduce potential QQ problems with simpler types :NewDefs :NoJS class Cod[out A, out C] //│ class Cod[A, C] { //│ constructor() //│ } // * Plain code with C context requirements class Plain[out A, out C] //│ class Plain[A, C] { //│ constructor() //│ } fun run: Cod['a, nothing] -> 'a //│ fun run: forall 'a. Cod['a, nothing] -> 'a val const: Cod[Int, nothing] fun quote: Array[Plain['a, 'c]] -> Cod['a, 'c] fun unquote: Cod['a, 'c] -> Plain['a, 'c] fun lam: (forall 'c: Cod['a, 'c] -> Cod['b, 'c | 'g]) -> Cod['a -> 'b, 'g] //│ fun quote: forall 'a 'c. Array[Plain['a, 'c]] -> Cod['a, 'c] //│ val const: Cod[Int, nothing] //│ fun lam: forall 'a0 'b 'g. (forall 'c0. Cod['a0, 'c0] -> Cod['b, 'c0 | 'g]) -> Cod['a0 -> 'b, 'g] //│ fun unquote: forall 'a1 'c1. Cod['a1, 'c1] -> Plain['a1, 'c1] fun (%>) bind: (Cod['a, 'g], k: forall 'c: Cod['a, 'c] -> Cod['b, 'c | 'g]) -> Cod['b, 'g] //│ fun (%>) bind: forall 'a 'g 'b. (Cod['a, 'g], k: forall 'c. Cod['a, 'c] -> Cod['b, 'c | 'g]) -> Cod['b, 'g] let f = k => bind of const, k //│ let f: forall 'b 'g. (forall 'c. Cod[Int, 'c] -> Cod['b, 'c | 'g]) -> Cod['b, 'g] f(id) //│ Cod[Int, nothing] // * Note: expected extrusion (needs polymorphic recursion) fun body(x, y) = case 0 then x 1 then y n then quote([unquote(x), unquote(y)]) %> z => body(x, z)(n - 1) //│ fun body: forall 'b 'a. (Cod['b, anything] & 'a, Cod['b, anything] & 'a) -> (0 | 1 | Int & ~0 & ~1) -> (Cod['b, anything] | 'a) // code"(x, y) => ${body(code"x", code"y")(0)}" let l = lam(x => lam(y => body(x, y)(0))) //│ let l: Cod['a -> 'a -> 'a, ??c & ~??c0] // * Expected error: `body` needed polymorphic recursion // * TODO improve error locations! :e run(l) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.55: run(l) //│ ║ ^^^^^^ //│ ╟── type `??c & ~??c0` does not match type `nothing` //│ ║ l.24: fun lam: (forall 'c: Cod['a, 'c] -> Cod['b, 'c | 'g]) -> Cod['a -> 'b, 'g] //│ ║ ^^ //│ ╟── Note: constraint arises from type reference: //│ ║ l.18: fun run: Cod['a, nothing] -> 'a //│ ╙── ^^^^^^^ //│ error | 'a -> 'a -> 'a fun body: forall 'g: (Cod[Int, 'g], Cod[Int, 'g]) -> Int -> Cod[Int, 'g] fun body(x, y) = case 0 then x 1 then y n then quote([unquote(x), unquote(y)]) %> z => body(x, z)(n - 1) //│ fun body: forall 'g. (Cod[Int, 'g], Cod[Int, 'g]) -> (0 | 1 | Int & ~0 & ~1) -> Cod[Int, 'g] //│ fun body: forall 'g0. (Cod[Int, 'g0], Cod[Int, 'g0]) -> Int -> Cod[Int, 'g0] let l = lam(x => lam(y => body(x, y)(0))) run(l) //│ let l: Cod[Int -> Int -> Int, nothing] //│ Int -> Int -> Int // * An unnecessary complication, just for testing purposes: fun body: forall 'g: (Cod[Int, 'g], Cod[Int, 'g]) -> Int -> Cod[Int, 'g] fun body(x, y) = case 0 then x 1 then y n then quote([unquote(x), unquote(y)]) %> z => quote([unquote(body(x, z)(n - 1))]) //│ fun body: forall 'g. (Cod[Int, 'g], Cod[Int, 'g]) -> (0 | 1 | Int & ~0 & ~1) -> Cod[Int, 'g] //│ fun body: forall 'g0. (Cod[Int, 'g0], Cod[Int, 'g0]) -> Int -> Cod[Int, 'g0] let l = lam(x => lam(y => body(x, y)(0))) run(l) //│ let l: Cod[Int -> Int -> Int, nothing] //│ Int -> Int -> Int ================================================ FILE: shared/src/test/diff/qq/QQFlag.mls ================================================ :NewDefs :QQ run(code"42") //│ 42 //│ res //│ = Quoted: //│ 42 //│ undefined :js run(code"x => x") //│ forall 'a. 'a -> 'a //│ // Prelude //│ class TypingUnit1 {} //│ const typing_unit1 = new TypingUnit1; //│ // Query 1 //│ res = run(((x1) => Lam(Var(x1), Var(x1)))(freshName("x"))); //│ // End of generated code //│ res //│ = Quoted: //│ (x_0) => //│ x_0 //│ undefined run(code"x => y => x + y") //│ Int -> Int -> Int //│ res //│ = Quoted: //│ (x_1) => //│ (y_0) => //│ (x_1 + y_0) //│ undefined run(code"(x => x + 1)(2)") //│ Int //│ res //│ = Quoted: //│ ((x_2) => //│ (x_2 + 1))(2) //│ undefined run(code"x => x") //│ forall 'a. 'a -> 'a //│ res //│ = Quoted: //│ (x_3) => //│ x_3 //│ undefined run(code"let x = 42 in x + 0") //│ Int //│ res //│ = Quoted: //│ let x_4 = //│ 42 //│ in (x_4 + 0) //│ undefined :e run(code"code"114"") //│ ╔══[ERROR] Nested quotation is not allowed. //│ ║ l.66: run(code"code"114"") //│ ╙── ^^^^^^^^^ //│ error //│ Code generation encountered an error: //│ nested quotation is not allowed. declare module Math { declare fun floor(x: Num): Int } fun even(n) = n % 2 == 0 fun power'(x, n) = if (n == 0) then Const(1) else if (even(n)) then code"let t = ${power'(x, Math.floor(n / 2))} in t * t" else code"${x} * ${power'(x, n - 1)}" run(code"x => ${power'(code"x", 10)}") //│ declare module Math { //│ fun floor: (x: Num) -> Int //│ } //│ fun even: Int -> Bool //│ fun power': forall 'a. (Code[Int, 'a], Int) -> Code[Int, 'a] //│ Int -> Int //│ res //│ = Quoted: //│ (x_5) => //│ let t_0 = //│ (x_5 * let t_1 = //│ let t_2 = //│ (x_5 * 1) //│ in (t_2 * t_2) //│ in (t_1 * t_1)) //│ in (t_0 * t_0) //│ undefined run(code"x => if (x == 42) then 42 else 0") //│ Num -> (0 | 42) //│ res //│ = Quoted: //│ (x_6) => //│ let ucs$test$0_0 = //│ ((x_6 == 42)) //│ in match ucs$test$0_0: //│ case true => 42 //│ _ => 0) //│ undefined ================================================ FILE: shared/src/test/diff/qq/ScopeTypes.mls ================================================ :NewDefs :NoJS run //│ forall 'a. Code['a, nothing] -> 'a :e code"x => ${run(code"x")}" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.9: code"x => ${run(code"x")}" //│ ║ ^^^^^^^^^^^^ //│ ╙── expression of type `?x` does not match type `nothing` //│ Code[forall 'a. Code['a, anything] -> 'a, nothing] fun test(cde) = code"x => x + ${cde}" //│ fun test: forall 'a. Code[Int, 'a] -> Code[Int -> Int, 'a] :e code"y => ${ run(test(code"y")), code"0" }" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: code"y => ${ run(test(code"y")), code"0" }" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ╟── code fragment context type of type `?y` does not match type `nothing` //│ ║ l.18: code"x => x + ${cde}" //│ ╙── ^^^ //│ Code[Int -> 0, nothing] fun test(cde, f) = code"x => x + ${f(cde)}" //│ fun test: forall 'a 'b. ('a, 'a -> Code[Int, 'b]) -> Code[Int -> Int, 'b] run(test(code"1", id)) //│ Int -> Int fun test(cde: Code[Int, 'a], f: forall 'c: Code[Int, 'c] -> Code[Int, 'c]) = code"x => x + ${f(cde)}" //│ fun test: forall 'a. (cde: Code[Int, 'a], f: forall 'c. Code[Int, 'c] -> Code[Int, 'c]) -> Code[Int -> Int, 'a] run(test(code"1", id)) //│ Int -> Int :e code"x => ${ run(test(code"x", id)), code"x" }" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.48: code"x => ${ run(test(code"x", id)), code"x" }" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── type `?x` does not match type `nothing` //│ ║ l.40: fun test(cde: Code[Int, 'a], f: forall 'c: Code[Int, 'c] -> Code[Int, 'c]) = //│ ╙── ^^ //│ Code[forall 'a. (Int & 'a) -> 'a, nothing] :e code"x => ${ let c = code"x" in run(test(c, id)), c }" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: code"x => ${ let c = code"x" in run(test(c, id)), c }" //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── type `?x` does not match type `nothing` //│ ║ l.40: fun test(cde: Code[Int, 'a], f: forall 'c: Code[Int, 'c] -> Code[Int, 'c]) = //│ ╙── ^^ //│ Code[forall 'a. (Int & 'a) -> 'a, nothing] fun test(f) = code"x => x + ${f(code"x + 1")}" //│ fun test: forall 'a. (Code[Int, ??x] -> Code[Int, 'a]) -> Code[Int -> Int, 'a] let myrun = c => run(c) //│ let myrun: forall 'a. Code['a, nothing] -> 'a :e code"x => ${ test(myrun) }" //│ ╔══[ERROR] Type error in application //│ ║ l.77: code"x => ${ test(myrun) }" //│ ║ ^^^^^^^^^^^ //│ ╟── type variable `?x` leaks out of its scope //│ ╟── into type `nothing` //│ ╟── adding a type annotation to any of the following terms may help resolve the problem //│ ╟── • this reference: //│ ║ l.69: code"x => x + ${f(code"x + 1")}" //│ ╙── ^ //│ Code[anything -> nothing, nothing] :e code"x => ${ test(c => run(c), code"0") }" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.90: code"x => ${ test(c => run(c), code"0") }" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `[forall ?a ?b. ?b -> ?a, Code[0, nothing]]` does not match type `[?c]` //│ ║ l.90: code"x => ${ test(c => run(c), code"0") }" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ Code[anything -> Int -> Int, nothing] :e code"x => ${ test(c => run(c), code"x") }" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.101: code"x => ${ test(c => run(c), code"x") }" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `[forall ?a ?b. ?b -> ?a, Code[?c, ?x]]` does not match type `[?d]` //│ ║ l.101: code"x => ${ test(c => run(c), code"x") }" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ //│ Code[anything -> Int -> Int, nothing] :e fun test2(a) = code"x => ${ test(c => run(c), a) }" //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.112: fun test2(a) = code"x => ${ test(c => run(c), a) }" //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── argument list of type `[forall ?a ?b. ?b -> ?a, ?c]` does not match type `[?d]` //│ ║ l.112: fun test2(a) = code"x => ${ test(c => run(c), a) }" //│ ╙── ^^^^^^^^^^^^^^^^ //│ fun test2: anything -> Code[anything -> Int -> Int, nothing] type Hide[C] = C & (C | Int) //│ type Hide[C] = C let r = code"y => ${ code"x => ${ code"x + y": Code[Int, Hide['a]] }" }" run(r) //│ let r: Code[Int -> Int -> Int, nothing] //│ Int -> Int -> Int fun f[A](x: Code[Int, A]): Code[Int, A] = code"${x} + 1" //│ fun f: forall 'A. (x: Code[Int, 'A]) -> Code[Int, 'A] run(f(Const(42))) //│ Int run(code"x => x + 1 + ${f(code"x")}") //│ Int -> Int ================================================ FILE: shared/src/test/diff/qq/Triple.mls ================================================ :NewDefs :NoJS "normal string" //│ "normal string" """hello world""" //│ "hello world" """hello world""" //│ "hello //│ world" code""" "hello world" """ //│ Code["hello world", nothing] code"""if 0 == 0 then 1 else 0 """ //│ Code[0 | 1, nothing] :pe :w :e code""wrong example"" //│ ╔══[PARSE ERROR] Unexpected end of quasiquote section; an expression was expected here //│ ║ l.29: code""wrong example"" //│ ╙── ^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.29: code""wrong example"" //│ ╙── ^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.29: code""wrong example"" //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.29: code""wrong example"" //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] identifier not found: wrong //│ ║ l.29: code""wrong example"" //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: example //│ ║ l.29: code""wrong example"" //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.29: code""wrong example"" //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── code fragment of type `Code[(), nothing]` is not a function //│ ║ l.29: code""wrong example"" //│ ╙── ^^^^^^ //│ error code""""hello world"""" //│ Code["hello world", nothing] :pe code""" "HKUST" //│ ╔══[PARSE ERROR] Unmatched opening quasiquote triple //│ ║ l.60: code""" "HKUST" //│ ╙── ^^^^^^^ //│ "HKUST" ================================================ FILE: shared/src/test/diff/qq/Unquote.mls ================================================ :NewDefs :NoJS :e let res = code"x" code"(x) => ${res}" //│ ╔══[ERROR] identifier not found: x //│ ║ l.6: let res = code"x" //│ ╙── ^ //│ let res: Code[error, nothing] //│ Code[anything -> error, nothing] :e let res = code"x" code"let y = 1 in ${res}" //│ ╔══[ERROR] identifier not found: x //│ ║ l.16: let res = code"x" //│ ╙── ^ //│ let res: Code[error, nothing] //│ Code[error, nothing] :e let a = code"x" let b = code"y" //│ ╔══[ERROR] identifier not found: x //│ ║ l.26: let a = code"x" //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.27: let b = code"y" //│ ╙── ^ //│ let a: Code[error, nothing] //│ let b: Code[error, nothing] :e code"a + a" //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.39: code"a + a" //│ ║ ^^^^^ //│ ╟── code fragment of type `Code[error, nothing]` is not an instance of type `Int` //│ ║ l.26: let a = code"x" //│ ║ ^^^^^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.39: code"a + a" //│ ╙── ^ //│ Code[Int | error, nothing] :e code"a + b" //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.53: code"a + b" //│ ║ ^^^^^ //│ ╟── code fragment of type `Code[error, nothing]` is not an instance of type `Int` //│ ║ l.26: let a = code"x" //│ ║ ^^^^^^^ //│ ╟── but it flows into reference with expected type `Int` //│ ║ l.53: code"a + b" //│ ╙── ^ //│ Code[Int | error, nothing] :e code"x + (let x = 2 in x * x)" //│ ╔══[ERROR] identifier not found: x //│ ║ l.67: code"x + (let x = 2 in x * x)" //│ ╙── ^ //│ Code[Int, nothing] :e let a = code"x" let b = code"y" let x = 1 code"let x = 1 ${a} + (let y = 1 in ${b}) " //│ ╔══[ERROR] identifier not found: y //│ ║ l.76: let b = code"y" //│ ╙── ^ //│ let a: Code[1, nothing] //│ let b: Code[error, nothing] //│ let x: 1 //│ Code[Int, nothing] let cde = code"a" //│ let cde: Code[Code[1, nothing], nothing] let cde_w_unquote = code"${cde}" //│ let cde_w_unquote: Code[Code[1, nothing], nothing] :e let cde_w_free_vars = code"let x = 1 in x + y" //│ ╔══[ERROR] identifier not found: y //│ ║ l.99: let cde_w_free_vars = code"let x = 1 in x + y" //│ ╙── ^ //│ let cde_w_free_vars: Code[Int, nothing] let cde_w_unquote_w_free_vars = code"${cde_w_free_vars}" //│ let cde_w_unquote_w_free_vars: Code[Int, nothing] :e let cde_w_unquote_w_local_free_vars = code"[z, ${cde_w_unquote_w_free_vars}]" //│ ╔══[ERROR] identifier not found: z //│ ║ l.111: let cde_w_unquote_w_local_free_vars = code"[z, ${cde_w_unquote_w_free_vars}]" //│ ╙── ^ //│ let cde_w_unquote_w_local_free_vars: Code[[error, Int], nothing] :e let free_var1 = code"let x = 1 in x + y" let free_var2 = code"let i = 1 in i + x" let cde_w_mutli_unquote = code"${free_var2} + ${free_var1}" //│ ╔══[ERROR] identifier not found: y //│ ║ l.119: let free_var1 = code"let x = 1 in x + y" //│ ╙── ^ //│ let free_var1: Code[Int, nothing] //│ let free_var2: Code[Int, nothing] //│ let cde_w_mutli_unquote: Code[Int, nothing] fun f(x, y) = code"[${x}, ${y}]" //│ fun f: forall 'a 'b 'c. (Code['a, 'b], Code['c, 'b]) -> Code[['a, 'c], 'b] :e let x = code"y" //│ ╔══[ERROR] identifier not found: y //│ ║ l.134: let x = code"y" //│ ╙── ^ //│ let x: Code[error, nothing] let c = f(x, x) //│ let c: Code[[error, error], nothing] :e let cde_arr = code"[${code"y"}, ${code"y"}]" //│ ╔══[ERROR] identifier not found: y //│ ║ l.145: let cde_arr = code"[${code"y"}, ${code"y"}]" //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.145: let cde_arr = code"[${code"y"}, ${code"y"}]" //│ ╙── ^ //│ let cde_arr: Code[[error, error], nothing] code"let y = 1 in ${cde_arr}" //│ Code[[error, error], nothing] :e y => code"[${code"y"}, ${code"y"}]" //│ ╔══[ERROR] identifier not found: y //│ ║ l.159: y => code"[${code"y"}, ${code"y"}]" //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.159: y => code"[${code"y"}, ${code"y"}]" //│ ╙── ^ //│ anything -> Code[[error, error], nothing] code"y => ${c}" //│ Code[anything -> [error, error], nothing] fun f(x, y) = [code"[${x}, ${y}]", x, y] //│ fun f: forall 'a 'b 'c 'd 'e. (Code['a, 'b] & 'c, Code['d, 'b] & 'e) -> [Code[['a, 'd], 'b], 'c, 'e] fun f(x) = x //│ fun f: forall 'a. 'a -> 'a let built_in_binding_test = code"let f = x => x + 1 in f(if true then 1 else 2)" //│ let built_in_binding_test: Code[Int, nothing] code"f(0)" //│ Code[0, nothing] :e // :ge code"${code"${code"${z}"}"}" //│ ╔══[ERROR] identifier not found: z //│ ║ l.188: code"${code"${code"${z}"}"}" //│ ╙── ^ //│ Code[nothing, nothing] let z = code"1" code"${code"${z}"}" //│ let z: Code[1, nothing] //│ Code[1, nothing] :e code"let x = 1 in let z = 1 in ${code"x + y"}" //│ ╔══[ERROR] identifier not found: y //│ ║ l.201: code"let x = 1 in let z = 1 in ${code"x + y"}" //│ ╙── ^ //│ Code[Int, nothing] code"let z = 0 in ${code"z + 1"}" //│ Code[Int, nothing] :e code"let x = 1 in ${code"x + y"}" //│ ╔══[ERROR] identifier not found: y //│ ║ l.212: code"let x = 1 in ${code"x + y"}" //│ ╙── ^ //│ Code[Int, nothing] let n = 1 code"${Const(n)} + 1" //│ let n: 1 //│ Code[Int, nothing] code"let x = 42 in x" //│ Code[42, nothing] code"let x = 42 in ${code"x"}" //│ Code[42, nothing] code"let x = 42 in ${code"x"}" //│ Code[42, nothing] code"let a = 1 in let b = 2 in ${a}" //│ Code[1, nothing] ================================================ FILE: shared/src/test/diff/qq/Weird.mls ================================================ :NewDefs :NoJS fun power(x) = case 0 then `1.0 n then x `*. power(x)(n - 1) //│ fun power: forall 'a. Code[Num, 'a] -> (0 | Int & ~0) -> Code[Num, 'a] :e let p3 = y `=> discard(run(x `=> power(x `+ y)(3))) y //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.12: discard(run(x `=> power(x `+ y)(3))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `?y & ~??x` does not match type `nothing` //│ let p3: Code[forall 'a. (Int & 'a) -> 'a, nothing] :e `let y = `42 `in discard(run(x `=> power(x `+ y)(3))) y //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.23: discard(run(x `=> power(x `+ y)(3))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `?y & ~??x` does not match type `nothing` //│ Code[42, nothing] :e `let y = `42 `in discard(run(`let x = `0 `in power(x `+ y)(3))) y //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.34: discard(run(`let x = `0 `in power(x `+ y)(3))) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `?y & ~??x` does not match type `nothing` //│ Code[42, nothing] fun bind(rhs, k: forall 'g : Code['a, 'g] -> Code['b, 'c | 'g]) = `let x = rhs `in k(x) //│ fun bind: forall 'a 'c 'b. (Code['a, 'c], k: forall 'g. Code['a, 'g] -> Code['b, 'c | 'g]) -> Code['b, 'c] // fun body: forall 'g : (Code[Int, 'g], Code[Int, 'g]) -> Int -> Code[Int, 'g] fun body(x, y) = case 0 then x 1 then y n then bind of x `+ y, z => body(y, z)(n - 1) //│ fun body: forall 'a. (Code[Int, anything] & 'a, Code[Int, anything] & 'a) -> (0 | 1 | Int & ~0 & ~1) -> (Code[Int, anything] | 'a) n => (x, y) `=> body(x, y)(n) //│ (0 | 1 | Int & ~0 & ~1) -> Code[(Int, Int) -> Int, ??g & ~??y] fun gib(n) = ((x, y) `=> body(x, y)(n)) //│ fun gib: (0 | 1 | Int & ~0 & ~1) -> Code[(Int, Int) -> Int, ??g & ~??y] let g5 = gib(5) //│ let g5: Code[(Int, Int) -> Int, ??g & ~??y] :e run(g5) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.67: run(g5) //│ ║ ^^^^^^^ //│ ╙── expression of type `??g & ~??y` does not match type `nothing` //│ error | (Int, Int) -> Int x `=> x `+ (x `=> x)`(`42) //│ Code[Int -> Int, nothing] fun foo = `let x = `1 mut let v = x `let x = `true x //│ fun foo: Code[true, nothing] :pe :e `let x = `1 `if //│ ╔══[PARSE ERROR] This quote syntax is not supported yet //│ ║ l.87: `let x = `1 `if //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected '`in'; found 'if' keyword instead //│ ║ l.87: `let x = `1 `if //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected end of input; found 'if' keyword instead //│ ║ l.87: `let x = `1 `if //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in unquote: //│ ║ l.87: `let x = `1 `if //│ ║ ^ //│ ╙── undefined literal of type `()` is not an instance of type `Code` //│ Code[error, nothing] :pe :e `let x = `1 `in //│ ╔══[PARSE ERROR] Unexpected end of input; an expression was expected here //│ ║ l.105: `let x = `1 `in //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in unquote: //│ ║ l.105: `let x = `1 `in //│ ║ ^ //│ ╙── undefined literal of type `()` is not an instance of type `Code` //│ Code[error, nothing] class Ref[A](init: A) { mut val value: A = init } //│ class Ref[A](init: A) { //│ mut val value: A //│ } x `=> let v = Ref(x) let _ = y `=> set v.value = y `0 v.value //│ Code[forall 'a. 'a -> 'a, ??y & ~??x] let foo = mut let r = `0 x `=> ((set r = x), `0), r //│ let foo: Code[0, nothing] | Var['a, in ??x out ??x0] fun foo = `let x = `1 let v = Ref(x) `let x = `true set v.value = x x //│ fun foo: Code[true, nothing] let a = `42 //│ let a: Code[42, nothing] :w `let x = a `in `log`(x) x //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in quasiquote: //│ ║ l.149: `log`(x) //│ ║ ^^^^^^^^ //│ ╙── code fragment of type `Code[?a, ?b | ?c]` does not match type `()` //│ Code[42, nothing] :w `let x = a `log`(x) x //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in quasiquote: //│ ║ l.161: `log`(x) //│ ║ ^^^^^^^^ //│ ╙── code fragment of type `Code[?a, ?b | ?c]` does not match type `()` //│ Code[42, nothing] :w `let x = a `in run of `log`(x) `1 x //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in quasiquote: //│ ║ l.174: `log`(x) //│ ║ ^^^^^^^^ //│ ╙── code fragment of type `Code[?a, ?b | ?c]` does not match type `()` //│ ╔══[WARNING] Expression in statement position should have type `()`. //│ ╟── Use a comma expression `... , ()` to explicitly discard non-unit values, making your intent clearer. //│ ╟── Type mismatch in application: //│ ║ l.173: run of //│ ║ ^^^^^^ //│ ║ l.174: `log`(x) //│ ║ ^^^^^^^^^^^^ //│ ║ l.175: `1 //│ ║ ^^^^^^ //│ ╟── integer literal of type `1` does not match type `()` //│ ║ l.175: `1 //│ ║ ^ //│ ╟── but it flows into application with expected type `()` //│ ║ l.173: run of //│ ║ ^^^^^^ //│ ║ l.174: `log`(x) //│ ║ ^^^^^^^^^^^^ //│ ║ l.175: `1 //│ ╙── ^^^^^^ //│ Code[42, nothing] :e `let x = a `in let _ = run of `let _ = `log`(x) `1 x //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.206: let _ = run of //│ ║ ^^^^^^ //│ ║ l.207: `let _ = `log`(x) //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.208: `1 //│ ║ ^^^^^^ //│ ╟── code fragment context type of type `?x` does not match type `nothing` //│ ║ l.207: `let _ = `log`(x) //│ ║ ^ //│ ╟── but it flows into code fragment context type with expected type `nothing` //│ ║ l.207: `let _ = `log`(x) //│ ╙── ^^^^^^^^ //│ Code[42, nothing] `let x = `1 `in `let _ = `log`(x) x //│ Code[1, nothing] fun foo = `let x = `1 `let _ = `log`(x) x //│ fun foo: Code[1, nothing] ================================================ FILE: shared/src/test/diff/qq/WillfulExtrusion.mls ================================================ :NewDefs :NoJS fun foo(dbg) = code"x => ${let c = code"x + 1" in dbg(c), c}" //│ fun foo: (Code[Int, ??x] -> anything) -> Code[Int -> Int, nothing] foo(log) //│ Code[Int -> Int, nothing] fun (>>) compose(f, g) = x => g(f(x)) fun show: Code[anything, anything] -> Str //│ fun (>>) compose: forall 'a 'b 'c. ('a -> 'b, 'b -> 'c) -> 'a -> 'c //│ fun show: Code[anything, anything] -> Str foo(show >> log) //│ Code[Int -> Int, nothing] ================================================ FILE: shared/src/test/diff/scalac/i13162.mls ================================================ :NewDefs // https://github.com/lampepfl/dotty/issues/13162 fun method = class Person(val name: Str) module Person_ { // TODO change when module overloading is supported val me = Person("Cameron") } let m = Person_.me m method.name //│ fun method: Person //│ Str //│ res //│ = 'Cameron' // https://github.com/lampepfl/dotty/issues/13162#issuecomment-887557311 // * We don't currently have self bindings // def method(): Unit = { // final case class Person(name: String) // object Person { self => // val me = self.apply("Cameron") // } // val _ = Person.me // } // method() :w :e fun method: () = class Person(val name: Str) module Person_ { self => // * defines a useless lambda! val me = self.apply("Cameron") } let m = Person_.me m method //│ ╔══[WARNING] Pure expression does nothing in statement position. //│ ║ l.37: module Person_ { self => // * defines a useless lambda! //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.38: val me = self.apply("Cameron") //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type `Person_` does not contain member `me` //│ ║ l.40: let m = Person_.me //│ ╙── ^^^ //│ fun method: () //│ () //│ res //│ = undefined // https://github.com/lampepfl/dotty/issues/13162#issuecomment-888188804 :re module Person { fun f: () fun f = Person2.f } module Person2 { fun f = () val me = Person.f } //│ module Person { //│ fun f: () //│ } //│ module Person2 { //│ fun f: () //│ val me: () //│ } //│ Runtime error: //│ RangeError: Maximum call stack size exceeded fun test = Person2.me //│ fun test: () // * FIXME initialization check? or different codegen? fun test = module Person { fun f: () fun f = Person2.f } module Person2 { fun f = () val me = Person.f } Person2.me //│ fun test: () :re test //│ () //│ res //│ Runtime error: //│ ReferenceError: Cannot access 'Person2' before initialization module Test { module Person { fun f: () fun f = Person2.f } module Person2 { fun f = () val me = Person.f } fun test = Person2.me } //│ module Test { //│ module Person { //│ fun f: () //│ } //│ module Person2 { //│ fun f: () //│ val me: () //│ } //│ fun test: () //│ } :re Test.test //│ () //│ res //│ Runtime error: //│ RangeError: Maximum call stack size exceeded ================================================ FILE: shared/src/test/diff/tricky/IrregularSubtypes.mls ================================================ :IrregularTypes type Foo[A] = A -> Foo[Foo[A]] //│ Defined type alias Foo[=A] type Bar[A] = A -> Bar[Bar[A]] //│ Defined type alias Bar[=A] :e error: Foo[int]: Bar[int] //│ ╔══[ERROR] Subtyping constraint of the form `Foo[int] <: Bar[int]` exceeded recursion depth limit (250) //│ ║ l.12: error: Foo[int]: Bar[int] //│ ║ ^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: Bar[int] //│ Runtime error: //│ Error: an error was thrown // * Interestingly, this is caught by the cycle checker when rectypes are disabled: :NoRecursiveTypes :e error: Foo[int]: Bar[int] //│ ╔══[ERROR] Cyclic-looking constraint while typing type ascription; a type annotation may be required //│ ║ l.26: error: Foo[int]: Bar[int] //│ ║ ^^^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: Bar[int] //│ Runtime error: //│ Error: an error was thrown ================================================ FILE: shared/src/test/diff/tricky/IrregularSubtypes2.mls ================================================ :NoRecursiveTypes :IrregularTypes type Lol = forall 'a. int -> ('a, Lol) //│ Defined type alias Lol def lol: Lol //│ lol: Lol //│ = :w type Oops[A] = int -> (anything, Oops[Oops[A]]) //│ Defined type alias Oops[±A] //│ ╔══[WARNING] Type definition Oops has bivariant type parameters: //│ ║ l.13: type Oops[A] = int -> (anything, Oops[Oops[A]]) //│ ║ ^^^^ //│ ╟── A is irrelevant and may be removed //│ ║ l.13: type Oops[A] = int -> (anything, Oops[Oops[A]]) //│ ╙── ^ // * The cycle is due to irregular types, which are not yet shadowed :e lol: Oops[int] //│ ╔══[ERROR] Subtyping constraint of the form `Lol <: Oops[?]` exceeded recursion depth limit (250) //│ ║ l.24: lol: Oops[int] //│ ║ ^^^ //│ ╙── Note: use flag `:ex` to see internal error info. //│ res: Oops[?] //│ = //│ lol is not implemented ================================================ FILE: shared/src/test/diff/tricky/Pottier.fun ================================================ // Inspired by [Pottier 98, chap 13.4] let rec f = x => y => add (f x.tail y) (f x y) let rec f = x => y => add (f x.tail y) (f y x) let rec f = x => y => add (f x.tail y) (f x y.tail) let rec f = x => y => add (f x.tail y.tail) (f x.tail y.tail) let rec f = x => y => add (f x.tail x.tail) (f y.tail y.tail) let rec f = x => y => add (f x.tail x) (f y.tail y) let rec f = x => y => add (f x.tail y) (f y.tail x) //│ f: 'a -> anything -> int //│ where //│ 'a <: {tail: 'a} //│ f: 'a -> 'a -> int //│ where //│ 'a <: {tail: 'a} //│ f: 'a -> 'b -> int //│ where //│ 'b <: {tail: 'b} //│ 'a <: {tail: 'a} //│ f: 'a -> 'b -> int //│ where //│ 'b <: {tail: 'b} //│ 'a <: {tail: 'a} //│ f: 'a -> 'a -> int //│ where //│ 'a <: {tail: 'a} //│ f: 'a -> 'b -> int //│ where //│ 'a <: {tail: 'a} & 'b //│ 'b <: {tail: 'a} //│ f: 'a -> 'b -> int //│ where //│ 'a <: {tail: 'a} & 'b //│ 'b <: {tail: 'a} let f = x => y => if true then { l: x; r: y } else { l: y; r: x } // 2-crown //│ f: 'a -> 'a -> {l: 'a, r: 'a} // Inspired by [Pottier 98, chap 13.5] let rec f = x => y => if true then x else { t: f x.t y.t } //│ f: 'a -> 'b -> 'c //│ where //│ 'b <: {t: 'b} //│ 'a <: {t: 'a} & 'c //│ 'c :> {t: 'c} ================================================ FILE: shared/src/test/diff/typegen/TypegenTerms.mls ================================================ :NoJS :ts { a = "hello"; b = "world" } 1 + 2 - 5 let t = { a = 5; b = "world"} in if t.a == 1 then 1 else (fun f -> f.b ) def x = 1 def y = 2 def z = x + y (1, 3, { a = { b = { c = "d"}}}, "hello hello") // common values //│ res: {a: "hello", b: "world"} //│ res: int //│ res: 1 | {b: 'b} -> 'b //│ x: 1 //│ y: 2 //│ z: int //│ res: (1, 3, {a: {b: {c: "d"}}}, "hello hello",) //│ // start ts //│ export declare const res: {readonly a: "hello", readonly b: "world"} //│ export declare const res: int //│ export declare const res: 1 | ((arg: {readonly b: b}) => b) //│ export declare const x: 1 //│ export declare const y: 2 //│ export declare const z: int //│ export declare const res: readonly [1, 3, {readonly a: {readonly b: {readonly c: "d"}}}, "hello hello"] //│ // end ts :ts def rcd = { x = 1 } rcd.x def funky f r = let t = r with { y = "nah" } in f t def cool g r = let t = r with { a = "apple"; b = "ball" } in g t def g = rcd with { y = "yeah" } // records //│ rcd: {x: 1} //│ res: 1 //│ funky: (('a\y & {y: "nah"}) -> 'b) -> 'a -> 'b //│ cool: (('a\a\b & {a: "apple", b: "ball"}) -> 'b) -> 'a -> 'b //│ g: {x: 1, y: "yeah"} //│ // start ts //│ export declare const rcd: {readonly x: 1} //│ export declare const res: 1 //│ export declare const funky: (arg: (arg1: Omit & {readonly y: "nah"}) => b) => (arg2: a) => b //│ export declare const cool: (arg: (arg1: Omit & {readonly a: "apple", readonly b: "ball"}) => b) => (arg2: a) => b //│ export declare const g: {readonly x: 1, readonly y: "yeah"} //│ // end ts :ts def f (a: int) (b: string) (c: bool) = if c then a else b rec def fact x = if x == 1 then 1 else x * fact (x - 1) def g x y = if x.b == 1 then x else x.a // miscellaneous functions //│ f: int -> string -> bool -> (int | string) //│ fact: int -> int //│ g: ({a: 'a, b: number} & 'a) -> anything -> 'a //│ // start ts //│ export declare const f: (arg: int) => (arg1: string) => (arg2: bool) => int | string //│ export declare const fact: (arg: int) => int //│ export declare const g: (arg: {readonly a: a, readonly b: number} & a) => (arg1: unknown) => a //│ // end ts // FIXME // :ts // rec def l (a: int) = l // rec def m (a: int) (b: int) = m // def f: ('c -> 'a as 'a) -> 'c -> int // recursion type functions :ts :e 1: ? { a = "hello" }: { a: string } & { b: int } //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.72: 1: ? //│ ║ ^ //│ ╟── integer literal of type `1` does not match type `nothing` //│ ╟── Note: constraint arises from type wildcard: //│ ║ l.72: 1: ? //│ ╙── ^ //│ res: anything //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.73: { a = "hello" }: { a: string } & { b: int } //│ ║ ^^^^^^^^^^^^^^^ //│ ╟── record literal of type `{a: "hello"}` does not have field 'b' //│ ╟── Note: constraint arises from intersection type: //│ ║ l.73: { a = "hello" }: { a: string } & { b: int } //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ res: {a: string, b: int} //│ // start ts //│ export declare const res: unknown //│ export declare const res: {readonly a: string, readonly b: int} //│ // end ts :ts def nonsense x y = if x.a == y.b then x y else fun y -> x def g = fun x -> fun y -> if x == y then x y else y x // complex types //│ nonsense: ('a -> 'b & {a: number} & 'c) -> ({b: number} & 'a) -> (anything -> 'c | 'b) //│ g: nothing -> nothing -> nothing //│ // start ts //│ export declare const nonsense: (arg: ((arg1: a) => b) & {readonly a: number} & c) => (arg2: {readonly b: number} & a) => ((arg3: unknown) => c) | b //│ export declare const g: (arg: never) => (arg1: never) => never //│ // end ts :ts def x: int | ~string def y: "a" | "b" | ~string // negative types //│ x: int | ~string //│ y: "a" | "b" | ~string //│ // start ts //│ type Neg = FromType extends NegatedType ? never: FromType //│ export declare const x: int | Neg //│ export declare const y: "a" | "b" | Neg //│ // end ts :ts def cont x = x + 1 def app y k = k (y - 1) app 2 cont //│ cont: int -> int //│ app: int -> (int -> 'a) -> 'a //│ res: int //│ // start ts //│ export declare const cont: (arg: int) => int //│ export declare const app: (arg: int) => (arg1: (arg2: int) => a) => a //│ export declare const res: int //│ // end ts :ts def g (x, y) = x + y def h (x, (y, z)) = y z + x def k (x, (y, z)) (l, (m), (n)) = (m (n + l)) (y (z + x)) def l: ((1, 2), (3, 4)) -> 5 //│ g: (int, int,) -> int //│ h: (int, ('a -> int, 'a,),) -> int //│ k: (int, (int -> 'a, int,),) -> (int, int -> 'a -> 'b, int,) -> 'b //│ l: ((1, 2,), (3, 4,),) -> 5 //│ // start ts //│ export declare const g: (arg: int, arg1: int) => int //│ export declare const h: (arg: int, arg1: readonly [(arg2: a) => int, a]) => int //│ export declare const k: (arg: int, arg1: readonly [(arg2: int) => a, int]) => (arg3: int, arg4: (arg5: int) => (arg6: a) => b, arg7: int) => b //│ export declare const l: (arg: readonly [1, 2], arg1: readonly [3, 4]) => 5 //│ // end ts // FIXME // :ts // def weird: ((int, int) -> 'a) as 'a // def weird: ('a -> (int, int)) as 'a // def weird: ((int, 'a) as 'a) -> int // def weird: ((int, bool) | 'a) -> 'a ================================================ FILE: shared/src/test/diff/typegen/TypegenTypedefs.mls ================================================ :NoJS :ts type G[T] = { a : T } type H = int | ~string class Box[T]: { length: T } class RectangleBox[T]: Box[T] & { breadth: T } class StackedRectangleBoxes[T, N]: RectangleBox[T] & { size: N } //│ Defined type alias G[+T] //│ Defined type alias H //│ Defined class Box[+T] //│ Defined class RectangleBox[+T] //│ Defined class StackedRectangleBoxes[+T, +N] //│ // start ts //│ export type G = {readonly a: T} //│ type Neg = FromType extends NegatedType ? never: FromType //│ export type H = int | Neg //│ export declare class Box { //│ length: T //│ constructor(fields: {length: T}) //│ } //│ export declare class RectangleBox extends Box { //│ breadth: T //│ constructor(fields: {breadth: T, length: T}) //│ } //│ export declare class StackedRectangleBoxes extends RectangleBox { //│ size: N //│ constructor(fields: {size: N, breadth: T, length: T}) //│ } //│ // end ts // FIXME :ts class Lock[T]: { pins: T } method Map: (T -> 'a) -> Lock['a] method Map f = Lock { pins = f this.pins } def Lock pins = Lock { pins = pins } class Bank: { lock: Lock[int]; cash: int } method Potential: number method Potential = this.cash / this.lock.pins method Better: Bank -> bool method Better other = this.Potential > other.Potential def Bank lock cash = Bank { lock = lock; cash = cash } let lockA = Lock 20 in let lockB = Lock 30 in (Bank lockA 2000).Better(Bank lockB 30000) //│ Defined class Lock[+T] //│ Declared Lock.Map: Lock['T] -> ('T -> 'a) -> Lock['a] //│ Defined Lock.Map: Lock['T] -> ('T -> 'pins) -> Lock['pins] //│ Defined class Bank //│ Declared Bank.Potential: Bank -> number //│ Declared Bank.Better: Bank -> Bank -> bool //│ Defined Bank.Potential: Bank -> number //│ Defined Bank.Better: Bank -> Bank -> bool //│ Lock: 'pins -> Lock['pins] //│ Bank: (Lock[int] & 'lock) -> (int & 'cash) -> (Bank with {cash: 'cash, lock: 'lock}) //│ res: bool //│ // start ts //│ export declare class Lock { //│ pins: T //│ constructor(fields: {pins: T}) //│ Map<'a>(arg: (arg1: T) => 'a): Lock<'a> //│ } //│ export declare class Bank { //│ lock: Lock //│ cash: int //│ constructor(fields: {lock: Lock, cash: int}) //│ readonly Potential: number //│ Better(arg: Bank): bool //│ } //│ export declare const Lock: (arg: pins) => Lock //│ export declare const Bank: (arg: Lock & lock) => (arg1: int & cash) => Omit & {readonly cash: cash, readonly lock: lock} //│ export declare const res: bool //│ // end ts // FIXME :ts class None: {} class Some[T]: { value: T } type Option[T] = Some[T] | None class LinkedList[T]: { head: T; tail: Option[LinkedList[T]] } method Append: T -> LinkedList[T] method Append elem = LinkedList { head = elem; tail = Some { value = this } } def None = None {} def Some v = Some { value = v } //│ Defined class None //│ Defined class Some[+T] //│ Defined type alias Option[+T] //│ Defined class LinkedList[=T] //│ Declared LinkedList.Append: LinkedList['T] -> 'T -> LinkedList['T] //│ Defined LinkedList.Append: (LinkedList['T] & 'this) -> ('T & 'head) -> (LinkedList['T] with {head: 'head, tail: Some[LinkedList['T]] & {value: LinkedList['T] & 'this}}) //│ None: None //│ Some: 'value -> Some['value] //│ // start ts //│ export declare class None { //│ constructor(fields: {}) //│ } //│ export declare class Some { //│ value: T //│ constructor(fields: {value: T}) //│ } //│ export type Option = Some | None //│ export declare class LinkedList { //│ head: T //│ tail: Option> //│ constructor(fields: {head: T, tail: Option>}) //│ Append(arg: T): LinkedList //│ } //│ export declare const None: None //│ export declare const Some: (arg: value) => Some //│ // end ts :ts trait A: { x: int } trait B: { y: string } & A class C: { x: 0 | 1 } //│ Defined trait A //│ Defined trait B //│ Defined class C //│ // start ts //│ export interface A { //│ x: int //│ } //│ export interface B extends A { //│ y: string //│ } //│ export declare class C { //│ x: 0 | 1 //│ constructor(fields: {x: 0 | 1}) //│ } //│ // end ts :ts class LL[T]: { head: T; tail: LL[T] -> int } method Append: T -> LL[T] //│ Defined class LL[=T] //│ Declared LL.Append: LL['T] -> 'T -> LL['T] //│ // start ts //│ export declare class LL { //│ head: T //│ tail: (arg: LL) => int //│ constructor(fields: {head: T, tail: (arg: LL) => int}) //│ Append(arg: T): LL //│ } //│ // end ts :ts trait A1: { x: int -> int } trait B2: { y: int } trait D3: { y: int } class E: { x: "hello"; y: int } & A1 & B2 & D3 //│ Defined trait A1 //│ Defined trait B2 //│ Defined trait D3 //│ Defined class E //│ // start ts //│ export interface A1 { //│ x: (arg: int) => int //│ } //│ export interface B2 { //│ y: int //│ } //│ export interface D3 { //│ y: int //│ } //│ export declare class E { //│ y: int //│ x: "hello" & ((arg: int) => int) //│ constructor(fields: {y: int, x: "hello" & ((arg: int) => int)}) //│ } //│ // end ts :ts type VarArg[T] = (T, T) class Program[T] method Run: VarArg[T] -> number //│ Defined type alias VarArg[+T] //│ Defined class Program[-T] //│ Declared Program.Run: Program['T] -> VarArg['T] -> number //│ // start ts //│ export type VarArg = readonly [T, T] //│ export declare class Program { //│ constructor(fields: {}) //│ Run(arg: VarArg): number //│ } //│ // end ts :ts :e :w class Arg[T]: (T, T) class Prog[T] method Run: Arg[T] -> number //│ ╔══[ERROR] cannot inherit from a tuple type //│ ║ l.190: class Arg[T]: (T, T) //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier not found: Arg //│ ║ l.192: method Run: Arg[T] -> number //│ ╙── ^^^^^^ //│ Defined class Prog[±T] //│ Declared Prog.Run: Prog[?] -> error -> number //│ ╔══[WARNING] Type definition Prog has bivariant type parameters: //│ ║ l.191: class Prog[T] //│ ║ ^^^^ //│ ╟── T is irrelevant and may be removed //│ ║ l.191: class Prog[T] //│ ╙── ^ //│ // start ts //│ export declare class Prog { //│ constructor(fields: {}) //│ Run(arg: error): number //│ } //│ // end ts ================================================ FILE: shared/src/test/diff/ucs/AppSplits.mls ================================================ :NewDefs fun foo(x) = x > 1 //│ fun foo: Num -> Bool :pe // TODO :e if foo of 0 then "a" 1 then "b" //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.10: 0 then "a" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.9: if foo of //│ ║ ^^^^^^ //│ ║ l.10: 0 then "a" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.9: if foo of //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.10: 0 then "a" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.10: 0 then "a" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ () //│ res //│ Runtime error: //│ Error: non-exhaustive case expression :pe // TODO :e if foo of 1, 0 then "a" 1 then "b" //│ ╔══[PARSE ERROR] Unexpected 'then'/'else' clause //│ ║ l.38: 0 then "a" //│ ║ ^^^^^^^^^^ //│ ║ l.39: 1 then "b" //│ ╙── ^^^^^^^^^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found application instead //│ ║ l.37: if foo of 1, //│ ║ ^^^^^^^^^ //│ ║ l.38: 0 then "a" //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.37: if foo of 1, //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.38: 0 then "a" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.37: if foo of 1, //│ ║ ^^^^^^^^^ //│ ║ l.38: 0 then "a" //│ ║ ^^ //│ ╟── argument list of type `[1, ()]` does not match type `[?a]` //│ ║ l.37: if foo of 1, //│ ║ ^^ //│ ║ l.38: 0 then "a" //│ ╙── ^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.38: 0 then "a" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ () //│ res //│ Runtime error: //│ Error: non-exhaustive case expression :pe // TODO :e if foo (0) then "a" (1) then "b" //│ ╔══[PARSE ERROR] Unexpected parenthesis section here //│ ║ l.79: (1) then "b" //│ ╙── ^^^ //│ ╔══[ERROR] missing else branch //│ ║ l.78: (0) then "a" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.78: (0) then "a" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ "a" //│ res //│ Runtime error: //│ Error: non-exhaustive case expression ================================================ FILE: shared/src/test/diff/ucs/CrossBranchCapture.mls ================================================ :NewDefs fun (~~>) expect(a, b) = if a === b then () else error //│ fun (~~>) expect: forall 'a. (Eql['a], 'a) -> () class Numb(n: Int) //│ class Numb(n: Int) // FIXME: The following test case should fail, but it doesn't. The reason is // `x` and `y` are in the desugared lexical scope, although they don't in the // original lexical scope. fun process(e) = if e is Numb(n) and n > 0 then n Numb(m) then n //│ fun process: Numb -> Int process(Numb(-10)) //│ Int //│ res //│ = -10 fun process(e, n) = if e is Numb(n) and n > 0 then n Numb(m) then n + m //│ fun process: (Numb, Int) -> Int process(Numb(0), 10) ~~> 10 process(Numb(-1), 10) ~~> 9 process(Numb(1), 10) ~~> 1 //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined class Vec(xs: Array[Numb | Vec]) class Pair[A,B](a: A, b: B) //│ class Vec(xs: Array[Numb | Vec]) //│ class Pair[A, B](a: A, b: B) :e :ge fun process(e) = if e is Pair(Numb(n), Numb(m)) then Numb(n + m) Pair(Vec(xs), Vec(ys)) then n Pair(Vec(n), Numb(n)) then n Pair(Numb(n), Vec(n)) then n //│ ╔══[ERROR] identifier not found: n //│ ║ l.56: Pair(Vec(xs), Vec(ys)) then n //│ ╙── ^ //│ fun process: Pair[Numb | Vec, Numb | Vec] -> (Int | Numb | error) //│ Code generation encountered an error: //│ unresolved symbol n // * FIXME should warn, be rejected, or compare both values for equality fun process(e) = if e is Pair(Numb(n), Numb(n)) then n //│ fun process: Pair[Numb, Numb] -> Int process(Pair(Numb(1), Numb(2))) //│ Int //│ res //│ = 2 ================================================ FILE: shared/src/test/diff/ucs/DirectLines.mls ================================================ :NewDefs fun f(x, y) = if x == 0 then "x" y == 0 then "y" _ then "nah" //│ fun f: (Num, Num) -> ("nah" | "x" | "y") abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun isValid(x) = if x then false else true //│ fun isValid: Bool -> Bool fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" is None and allowNone then "okay" is _ then "bad" //│ fun f: (Object & ~#Some | Some[Bool], Bool) -> ("bad" | "good" | "okay") fun f(x, y, z) = if x == 0 then "x" y == 1 then "y = 1" 2 and z == 0 then "z = 0" 9 then "z = 9" _ then "bruh" 3 then "y = 3" _ then "bruh" //│ fun f: (Num, Num, Num) -> ("bruh" | "x" | "y = 1" | "y = 3" | "z = 0" | "z = 9") :w fun f(a, b) = if a == 0 then 0 b == 1 then 1 2 then 2 _ then 7 else 3 //│ ╔══[WARNING] this case is unreachable //│ ║ l.48: else 3 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.47: _ then 7 //│ ╙── ^ //│ fun f: (Num, Num) -> (0 | 1 | 2 | 7) ================================================ FILE: shared/src/test/diff/ucs/ElseIf.mls ================================================ :NewDefs fun f(x, y) = if x === 0 then true 1 then false else if y === 0 then true 1 then false else false //│ fun f: (Eql[0 | 1], Eql[0 | 1]) -> Bool fun f(x, y) = if x === 0 then true 1 then false else if y === 0 then true _ then false //│ fun f: (Eql[0 | 1], Eql[0]) -> Bool module Tru module Fals //│ module Tru //│ module Fals :e fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false //│ ╔══[ERROR] when `x` is `Tru` //│ ║ l.29: Tru and y is Tru then true //│ ║ ^^^ //│ ╟── `y` has 1 missing case //│ ║ l.29: Tru and y is Tru then true //│ ║ ^ //│ ╟── it can be module `Fals` //│ ║ l.30: Fals and y is Fals then false //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `Fals` //│ ║ l.30: Fals and y is Fals then false //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.30: Fals and y is Fals then false //│ ║ ^ //│ ╟── it can be module `Tru` //│ ║ l.29: Tru and y is Tru then true //│ ╙── ^^^ //│ fun f: (Fals | Tru, nothing) -> Bool // The base case. fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false Tru and y is Fals then true Fals and y is Tru then true //│ fun f: (Fals | Tru, Fals | Tru) -> Bool // Replace the `x is Fals` with `_` fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false _ and y is Tru then true Fals then false //│ fun f: (Object, Fals | Tru) -> Bool f(Tru, Tru) f(Tru, Fals) f(Fals, Tru) f(Fals, Fals) //│ Bool //│ res //│ = true //│ res //│ = false //│ res //│ = true //│ res //│ = false :e fun g(x, y) = if x is true and y is true then true false and y is false then false //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.84: true and y is true then true //│ ║ ^ //│ ╟── it can be Boolean value `false` //│ ║ l.85: false and y is false then false //│ ╙── ^^^^^ //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.85: false and y is false then false //│ ║ ^ //│ ╟── it can be Boolean value `true` //│ ║ l.84: true and y is true then true //│ ╙── ^^^^ //│ fun g: (Bool, nothing) -> Bool // Test with real booleans fun g(x, y) = if x is true and y is true then true false and y is false then false _ and y is true then true false then false //│ fun g: (Object, Bool) -> Bool // Chained UCS terms fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false else if y is Tru then true Fals then false //│ fun f: (Object, Fals | Tru) -> Bool :e fun f(x, y) = if x is Tru and y is Tru then true Fals and y is Fals then false else if y is Tru and x is Fals then true Fals and x is Tru then false //│ ╔══[ERROR] when `y` is `Tru` //│ ║ l.123: Tru and x is Fals then true //│ ║ ^^^ //│ ╟── `x` has 1 missing case //│ ║ l.123: Tru and x is Fals then true //│ ║ ^ //│ ╟── it can be module `Tru` //│ ║ l.124: Fals and x is Tru then false //│ ╙── ^^^ //│ ╔══[ERROR] when `y` is `Fals` //│ ║ l.124: Fals and x is Tru then false //│ ║ ^^^^ //│ ╟── `x` has 1 missing case //│ ║ l.124: Fals and x is Tru then false //│ ║ ^ //│ ╟── it can be module `Fals` //│ ║ l.123: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.123: Tru and x is Fals then true //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Tru` is not an instance of type `Fals` //│ ║ l.120: Tru and y is Tru then true //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Fals` //│ ║ l.123: Tru and x is Fals then true //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.123: Tru and x is Fals then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.124: Fals and x is Tru then false //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── class pattern of type `Fals` is not an instance of type `Tru` //│ ║ l.121: Fals and y is Fals then false //│ ║ ^^^^ //│ ╟── but it flows into reference with expected type `Tru` //│ ║ l.124: Fals and x is Tru then false //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.124: Fals and x is Tru then false //│ ╙── ^^^ //│ fun f: (Fals | Tru, Fals | Tru) -> Bool fun h(x, y, p) = if x and p(x) then 0 y is Tru then 1 Fals then 2 //│ fun h: forall 'a. ('a & Bool, Fals | Tru, 'a -> Bool) -> (0 | 1 | 2) ================================================ FILE: shared/src/test/diff/ucs/ErrorMessage.mls ================================================ :NewDefs class Point(x: Int, y: Int) //│ class Point(x: Int, y: Int) :e fun f(p) = if p is Point(x, y, z) then x + y + z //│ ╔══[ERROR] Type mismatch in field selection: //│ ╟── tuple literal of type `{0: ?#x, 1: ?#y}` does not have field '2' //│ ║ l.3: class Point(x: Int, y: Int) //│ ║ ^^^^^^^^^ //│ ╟── but it flows into operator application with expected type `{2: ?a}` //│ ║ l.8: if p is //│ ║ ^^^^ //│ ║ l.9: Point(x, y, z) then x + y + z //│ ╙── ^^^^^^^^^ //│ fun f: Point -> Int :e :ge fun g(xs) = if xs is head :: _ then head //│ ╔══[ERROR] type identifier `::` not found //│ ║ l.25: head :: _ then head //│ ╙── ^^ //│ ╔══[ERROR] type identifier not found: :: //│ ║ l.25: head :: _ then head //│ ╙── ^^ //│ fun g: nothing -> error //│ Code generation encountered an error: //│ unresolved symbol :: ================================================ FILE: shared/src/test/diff/ucs/Exhaustiveness.mls ================================================ :NewDefs :NoJS class A() class B() class C() //│ class A() //│ class B() //│ class C() :e fun f(x, y) = if y is A and x is A then 0 B then 1 C then 2 y is B and x is A then 4 //│ ╔══[ERROR] when `y` is `B` //│ ║ l.19: y is B and //│ ║ ^ //│ ╟── `x` has 2 missing cases //│ ║ l.20: x is //│ ║ ^ //│ ╟── it can be class `B` //│ ║ l.17: B then 1 //│ ║ ^ //│ ╟── it can be class `C` //│ ║ l.18: C then 2 //│ ╙── ^ //│ fun f: (A, A | B) -> (0 | 1 | 2 | 4) :e // These operators are uninterpreted. So, it's impossible to reason the // exhaustiveness without SMT solvers. type Tree[A] = Node[A] | Empty module Empty { fun contains(wanted) = false } class Node[A](value: int, left: Tree[A], right: Tree[A]) { fun contains(wanted) = if wanted <= value then left.find(wanted) >= value then right.find(wanted) == value then true } //│ ╔══[ERROR] missing else branch //│ ║ l.47: == value then true //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ //│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `Num` //│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.45: <= value then left.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#Node & {Node#A = A}` does not contain member `find` //│ ║ l.45: <= value then left.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ //│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: >= value then right.find(wanted) //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `Num` //│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.46: >= value then right.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type `#Node & {Node#A = A}` does not contain member `find` //│ ║ l.46: >= value then right.find(wanted) //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.44: fun contains(wanted) = if wanted //│ ║ ^^^^^^ //│ ║ l.45: <= value then left.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: >= value then right.find(wanted) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.47: == value then true //│ ║ ^^^^^^^^^^^^ //│ ╟── type `int` is not an instance of type `Num` //│ ║ l.43: class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ ║ ^^^ //│ ╟── but it flows into reference with expected type `Num` //│ ║ l.47: == value then true //│ ╙── ^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.47: == value then true //│ ║ ^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ type Tree[A] = Empty | Node[A] //│ module Empty { //│ fun contains: anything -> false //│ } //│ class Node[A](value: int, left: Tree[A], right: Tree[A]) { //│ fun contains: Num -> (error | true) //│ } ================================================ FILE: shared/src/test/diff/ucs/Humiliation.mls ================================================ :NewDefs class Foo[T](x: T) //│ class Foo[T](x: T) if 1 is 1 then 1 else 0 //│ 0 | 1 //│ res //│ = 1 fun test(x) = if x is 1 then 0 else 1 //│ fun test: Object -> (0 | 1) :w fun testF(x) = if x is Foo(a) then a Foo(a) then a //│ ╔══[WARNING] found a duplicated case //│ ║ l.18: Foo(a) then a //│ ║ ^^^ //│ ╟── there is an identical pattern Foo //│ ║ l.17: Foo(a) then a //│ ╙── ^^^ //│ fun testF: forall 'a. Foo['a] -> 'a class Bar[Y, Z](y: Y, z: Z) //│ class Bar[Y, Z](y: Y, z: Z) fun test(f) = if f is Foo(a) then a Bar(b, c) then b + c //│ fun test: forall 'a. (Bar[Int, Int] | Foo['a]) -> (Int | 'a) class Pair[A, B](fst: A, snd: B) //│ class Pair[A, B](fst: A, snd: B) fun f(x) = if x is Pair(0, 0) then "zeros" Pair(1, 1) then "ones" Pair(y, 1) then x _ then "nah" //│ fun f: (Object & ~#Pair | Pair[Object, Object]) -> ("nah" | "ones" | "zeros" | Pair[nothing, nothing]) class Z() class O() //│ class Z() //│ class O() // This is not exhaustive. :e fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" //│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `Z`, //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `O` //│ ║ l.56: Pair(O(), O()) then "ones" //│ ╙── ^ //│ ╔══[ERROR] when `x` is `Pair`, and //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the first argument of `Pair` is `O`, //│ ║ l.56: Pair(O(), O()) then "ones" //│ ║ ^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.55: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") // Change `Pair` to a real pair. :e fun foo(x) = if x is [Z(), Z()] then "zeros" [O(), O()] then "ones" //│ ╔══[ERROR] when `x$Tuple$2_0` is `O` //│ ║ l.83: [O(), O()] then "ones" //│ ║ ^ //│ ╟── `x$Tuple$2_1` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.82: [Z(), Z()] then "zeros" //│ ╙── ^ //│ fun foo: forall 'a. {0: O | Z, 1: O & 'a} -> ("ones" | "zeros" | 'a) fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is Z() then "zeros" O() then if b is O() then "ones" //│ fun foo: Pair[O | Z, nothing] -> ("ones" | "zeros") fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is Z() then "zeros" else "???" O() then if b is O() then "ones" //│ fun foo: Pair[O | Z, O] -> ("???" | "ones" | "zeros") fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is Z() then "zeros" else "???" O() then if b is O() then "zeros" else "???" //│ fun foo: Pair[O | Z, Object] -> ("???" | "zeros") class S(pred: S | Z | O) //│ class S(pred: O | S | Z) // TODO: Cannot check exhaustiveness of nested UCS yet. fun foo(x) = if x is Pair(a, b) then if a is Z() then if b is S(x) then x else "???" O() then if b is O() then "zeros" else "???" //│ fun foo: Pair[O | Z, Object] -> ("???" | "zeros" | O | S | Z) foo(Pair(Z(), Z())) //│ "???" | "zeros" | O | S | Z //│ res //│ = '???' :e fun foo(x) = if x is Pair(Z(), Z()) then "zeros" Pair(O(), O()) then "ones" Pair(y, O()) then x //│ ╔══[ERROR] when `x` is `Pair` //│ ║ l.141: Pair(Z(), Z()) then "zeros" //│ ║ ^^^^ //│ ╟── the second argument of `Pair` has 1 missing case //│ ╟── it can be class `Z` //│ ║ l.141: Pair(Z(), Z()) then "zeros" //│ ╙── ^ //│ fun foo: forall 'A 'B. Pair['A, O & 'B] -> ("ones" | "zeros" | Pair['A, 'B] | 'A) //│ where //│ 'A <: Object fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ fun foo: (Object, Object) -> (0 | 1) fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ fun foo: (Object, Object) -> (0 | 1) ================================================ FILE: shared/src/test/diff/ucs/Hygiene.mls ================================================ :NewDefs class Some[out T](value: T) class Left[out T](value: T) class Right[out T](value: T) //│ class Some[T](value: T) //│ class Left[T](value: T) //│ class Right[T](value: T) :ducs:postprocess.result fun foo(x) = if x is Some(Left(y)) then x Some(x) then x //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> //│ let ucs$args_x$Some*† = (Some).unapply(x,) //│ let x$Some_0*‡ = (ucs$args_x$Some).0 //│ case x$Some_0*‡ of //│ Left*◊ -> //│ let ucs$args_x$Some_0$Left*† = (Left).unapply(x$Some_0,) //│ let y*‡ = (ucs$args_x$Some_0$Left).0 //│ x //│ _ -> //│ let x*‡ = (ucs$args_x$Some).0 //│ x //│ fun foo: forall 'T. Some[(Left[anything] | Object & ~#Left) & 'T] -> (Some['T] | 'T) foo(Some(Left(1))) //│ Left[1] | Some[Left[1]] //│ res //│ = Some {} foo(Some(2)) //│ 2 | Some[2] //│ res //│ = 2 ================================================ FILE: shared/src/test/diff/ucs/HygienicBindings.mls ================================================ :NewDefs fun (~~>) expect(a, b) = if a === b then () else error //│ fun (~~>) expect: forall 'a. (Eql['a], 'a) -> () type Option[out T] = None | Some[T] module None class Some[out T](val value: T) //│ type Option[T] = None | Some[T] //│ module None //│ class Some[T](value: T) type Either[A, B] = Left[A] | Right[B] class Left[A](val leftValue: A) class Right[B](val rightValue: B) //│ type Either[A, B] = Left[A] | Right[B] //│ class Left[A](leftValue: A) //│ class Right[B](rightValue: B) type List[out A] = Nil | Cons[A] module Nil class Cons[out A](head: A, tail: List[A]) //│ type List[A] = Cons[A] | Nil //│ module Nil //│ class Cons[A](head: A, tail: List[A]) fun justTrue(_) = true fun justFalse(_) = false //│ fun justTrue: anything -> true //│ fun justFalse: anything -> false fun h0(a) = if a is Some(Left(y)) then y a is Some(Right(z)) then z a is None then 0 //│ fun h0: forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) // If a class parameter is bound to the same variable in different branches, // the bindings can be merged and can be typed and coverage checked. See the // desugared version below. :ducs:postprocess.result fun h0'(a) = if a is Some(x) and x is Left(y) then y a is Some(x) and x is Right(z) then z a is None then 0 //│ Post-processed UCS term: //│ case a*‡ of //│ Some*◊ -> //│ let ucs$args_a$Some*† = (Some).unapply(a,) //│ let x*‡ = (ucs$args_a$Some).0 //│ case x*‡ of //│ Left*◊ -> //│ let ucs$args_x$Left*† = (Left).unapply(x,) //│ let y*‡ = (ucs$args_x$Left).0 //│ y //│ Right*◊ -> //│ let ucs$args_x$Right*† = (Right).unapply(x,) //│ let z*‡ = (ucs$args_x$Right).0 //│ z //│ None*† -> 0 //│ fun h0': forall 'a. (None | Some[Left['a] | Right['a]]) -> (0 | 'a) // However, if the class parameter is bound to different variables in different // branches, the bindings cannot be merged and the type will miss the latter // branch. See the desugared version below. fun h1(a) = if a is Some(x) and x is Left(y) then y a is Some(y) and y is Right(z) then z a is None then 0 //│ fun h1: forall 'a. (None | Some[Right[anything] & {#rightValue: 'a}]) -> (0 | 'a) // FIXME h1(Some(Left(0))) //│ ╔══[ERROR] Type `Left[?A]` does not contain member `rightValue` //│ ║ l.15: class Right[B](val rightValue: B) //│ ╙── ^^^^^^^^^^ //│ 0 | error //│ res //│ = 0 // FIXME: It is also impossible to merge bindings of different variables if one // of them is bound via a let binding. fun h2(a) = if a is Some(x) and x is x' and x' is Left(y) then y a is Some(y) and let y' = y y' is Right(z) then z a is None then 0 //│ fun h2: forall 'a. (None | Some[Left['a]]) -> (0 | 'a) :ducs:postprocess.result :w fun h3(x, y, f, p) = if x is _ and f(x) is y and p(x) then y None then y _ then "anyway" //│ Post-processed UCS term: //│ let ucs$scrut$0*‡ = f(x,) //│ let ucs$shadow$0 = y //│ let y*‡ = ucs$scrut$0 //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> y //│ _ -> //│ let y*‡ = ucs$shadow$0 //│ case x*‡ of //│ None*† -> y //│ _ -> "anyway" //│ ╔══[WARNING] the outer binding `y` //│ ║ l.97: fun h3(x, y, f, p) = //│ ║ ^ //│ ╟── is shadowed by name pattern `y` //│ ║ l.99: _ and f(x) is y and p(x) then y //│ ╙── ^ //│ fun h3: forall 'a 'b. (Object & 'a, 'b, 'a -> 'b, 'a -> Bool) -> ("anyway" | 'b) h3("anything", "not me", _ => "should be me", _ => true) ~~> "should be me" h3(None, "should be me", _ => "not me", _ => false) ~~> "should be me" h3("anything", "anything", _ => "not me", _ => false) ~~> "anyway" //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined :ducs:postprocess.result :w fun h4(x, y, p) = if x is y and p(x) then y None then y _ then "default" //│ Post-processed UCS term: //│ let ucs$shadow$0 = y //│ let y*‡ = x //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> y //│ _ -> //│ let y*‡ = ucs$shadow$0 //│ case x*‡ of //│ None*† -> y //│ _ -> "default" //│ ╔══[WARNING] the outer binding `y` //│ ║ l.137: fun h4(x, y, p) = //│ ║ ^ //│ ╟── is shadowed by name pattern `y` //│ ║ l.139: y and p(x) then y //│ ╙── ^ //│ fun h4: forall 'a 'b. (Object & 'a, 'b, 'a -> Bool) -> ("default" | 'a | 'b) h4("should be me", "not me", _ => true) h4(None, "not me", _ => true) h4(None, "should be me", _ => false) h4("anything", "not me", _ => false) //│ "anything" | "default" | "not me" //│ res //│ = 'should be me' //│ res //│ = None { class: [class None] } //│ res //│ = 'should be me' //│ res //│ = 'default' :ducs:postprocess.result fun h5(x, y, p) = if x is Some(y) and p(x) then y None then y _ then y //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> //│ let ucs$args_x$Some*† = (Some).unapply(x,) //│ let ucs$shadow$0 = y //│ let y*‡ = (ucs$args_x$Some).0 //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> y //│ _ -> //│ let y*‡ = ucs$shadow$0 //│ y //│ None*† -> y //│ _ -> y //│ fun h5: forall 'a. (Object & ~#Some | Some['a], 'a, Some[nothing] -> Bool) -> 'a h5(Some(1), 2, justTrue) ~~> 1 h5(Some(1), 2, justFalse) ~~> 2 h5(None, 0, justTrue) ~~> 0 h5(None, 0, justFalse) ~~> 0 h5("foo", 42, justTrue) ~~> 42 h5("foo", 42, justFalse) ~~> 42 //│ () //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined //│ res //│ = undefined ================================================ FILE: shared/src/test/diff/ucs/InterleavedLet.mls ================================================ :NewDefs fun f(x) = if x == let v = 0 v then v else 0 //│ fun f: Num -> 0 abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either :ducs:normalize.result fun q(x) = if x is Some and x is Some and x is Some then 0 //│ Normalized UCS term: //│ case x*‡ of //│ Some*◊ -> 0 //│ fun q: Some[anything] -> 0 :e // FIXME: Unexpected empty split. fun p(x, y) = if x is Some and y is None then 0 y is Some and x is Some then 1 x is Some and y is Some then 0 //│ ╔══[WARNING] found a duplicated case //│ ║ l.39: x is Some and y is Some then 0 //│ ║ ^^^^ //│ ╟── there is an identical pattern Some //│ ║ l.38: y is Some and x is Some then 1 //│ ╙── ^^^^ //│ ╔══[ERROR] `y` has 1 missing case //│ ║ l.38: y is Some and x is Some then 1 //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.37: x is Some and y is None then 0 //│ ╙── ^^^^ //│ fun p: (Object & ~#Some | Some[anything], Some[anything]) -> (0 | 1) fun h(x, y) = if x is None then y let y_square = y * y Some(z) then z + y_square //│ fun h: (None | Some[Int], Int) -> Int h(Some(5), 6) //│ Int //│ res //│ = 41 fun h(x, y) = if x is None then y let y_square = y * y Some(y_square) then 0 //│ fun h: forall 'a. (None | Some[anything], Int & 'a) -> (0 | 'a) :e fun f(a, y) = if a is Some(v) and v is Left(x) then x let y = v + 1 Right(x) then x + y else 0 //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.78: let y = v + 1 //│ ║ ^^^^^ //│ ╟── reference of type `Right[?B]` is not an instance of type `Int` //│ ║ l.78: let y = v + 1 //│ ╙── ^ //│ fun f: forall 'a. (Object & ~#Some | Some[Int | Left['a] | Right[Int]], anything) -> (Int | 'a) :pe fun q(a) = if a is Left(x) then x let y = a + 1 then y //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.93: let y = a + 1 //│ ║ ^^^^^ //│ ║ l.94: then y //│ ╙── ^^^^^^^^^^ //│ fun q: forall 'a. (Left['a] | Object & ~#Left) -> (() | 'a) class A() class B() //│ class A() //│ class B() :e fun w() = if A then "A" let y = 0 B then "B" else "?" //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.110: A then "A" //│ ║ ^ //│ ╙── reference of type `() -> A` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.112: B then "B" //│ ║ ^ //│ ╙── reference of type `() -> B` is not an instance of type `Bool` //│ fun w: () -> ("?" | "A" | "B") w() //│ "?" | "A" | "B" //│ res //│ = '?' fun i(x) = if x is A() then "A" let y = 0 B() then "B" //│ fun i: (A | B) -> ("A" | "B") fun inc(x) = x + 1 //│ fun inc: Int -> Int fun qq(x, z) = if x == let y = inc(z) y * y then 0 else 0 //│ fun qq: (Num, Int) -> 0 fun bruh(x) = if x == 0 then 0 let y = 1 else y //│ fun bruh: Num -> (0 | 1) fun f1(x) = x + 1 fun f2(x, y) = x + y //│ fun f1: Int -> Int //│ fun f2: (Int, Int) -> Int fun ff(x) = if x == 0 then 0 let y = f1(x) let z = f2(x, y) z == 1 then 1 z == 2 then 2 else 0 //│ fun ff: Int -> (0 | 1 | 2) :e // FIXME: Should warn missing else branches. fun ip(y) = if q(y) and let z = inc(y) y == z * z then "bruh" else "rocks" //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.171: if q(y) and //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Bool` //│ ║ l.94: then y //│ ║ ^ //│ ╟── but it flows into application with expected type `Bool` //│ ║ l.171: if q(y) and //│ ╙── ^^^^ //│ fun ip: Int -> ("bruh" | "rocks") fun tr(x) = if x is Some(v) then v let tmp = 1 None then tmp //│ fun tr: forall 'a. (None | Some['a]) -> (1 | 'a) class Pair[A, B](val fst: A, val snd: B) abstract class List[out A]: Nil | Cons[A] module Nil extends List[nothing] class Cons[out A](head: A, tail: List[A]) extends List[A] //│ class Pair[A, B](fst: A, snd: B) //│ abstract class List[A]: Cons[A] | Nil //│ module Nil extends List //│ class Cons[A](head: A, tail: List[A]) extends List fun (::) cons(h, t) = Cons(h, t) fun (++) strcat(a, b) = concat(a)(b) //│ fun (::) cons: forall 'A. ('A, List['A]) -> Cons['A] //│ fun (++) strcat: (Str, Str) -> Str fun showList(xs) = if xs is Nil then "" Cons(head, Nil) then toString(head) Cons(head, tail) then toString(head) ++ ", " ++ showList(tail) //│ fun showList: (Cons[anything] | Nil) -> Str let zeroToThree = 0 :: 1 :: 2 :: 3 :: Nil //│ let zeroToThree: Cons[0 | 1 | 2 | 3] //│ zeroToThree //│ = Cons {} showList(zeroToThree) //│ Str //│ res //│ = '0, 1, 2, 3' fun evenness(x) = if x % 2 is 0 then Left(x) else Right(x) //│ fun evenness: forall 'A 'B. (Int & 'A & 'B) -> (Left['A] | Right['B]) fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0 'A1 'A2 'B 'B0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[in 'A1 & 'A2 out 'A2, in 'B & 'B0 out 'B0 | Cons['A0]] //│ where //│ 'B <: List['A0] & 'B0 //│ 'B0 :> Cons['A0] | Nil //│ <: 'B //│ 'A1 <: List['A] & 'A2 //│ 'A2 :> Cons['A] | Nil //│ <: 'A1 mapPartition(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) //│ forall 'A 'A0 'B 'B0. Pair[in 'A & 'A0 out 'A0, in 'B & 'B0 out 'B0 | Cons[0 | 1 | 2 | 3 | 'A1]] //│ where //│ 'B <: List['A1] & 'B0 //│ 'B0 :> Cons[0 | 1 | 2 | 3 | 'A1] | Nil //│ <: 'B //│ 'A <: List['A2] & 'A0 //│ 'A0 :> Cons[0 | 1 | 2 | 3 | 'A2] | Nil //│ <: 'A //│ res //│ = Pair {} // This should be the desugaring of the above: fun mapPartition'(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is res and res.fst is l and res.snd is r and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition': forall 'a 'A 'A0 'A1 'A2 'A3 'B. ('a -> (Left['A & 'A0] | Right['A1 & 'A2]), Cons['a] | Nil) -> Pair[in 'A3 out Cons['A] | Nil | 'A3 | Cons['A0], in 'B out Cons['A2] | 'B | Cons['A1] | Nil] mapPartition'(x => (if x % 2 == 0 then Left(x) else Right(x)), zeroToThree) //│ forall 'A 'B. Pair[in 'A out Cons[0 | 1 | 2 | 3] | Nil | 'A, in 'B out Cons[0 | 1 | 2 | 3] | 'B | Nil] //│ res //│ = Pair {} // This is a very interesting side-effect example! fun mn(a) = if a is Some(x) and x is Left(b) and b is 0 then "b is 1" let _ = log(b) 1 then "b is 2" 2 then "b is 3" Right(b) then "right-defined" None then "undefined" //│ fun mn: (None | Some[Left[0 | 1 | 2] | Right[anything]]) -> ("b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined") mn of None mn of Some of Left of 0 mn of Some of Left of 1 mn of Some of Left of 2 mn of Some of Right of () //│ "b is 1" | "b is 2" | "b is 3" | "right-defined" | "undefined" //│ res //│ = 'undefined' //│ res //│ = 'b is 1' //│ res //│ = 'b is 2' //│ // Output //│ 1 //│ res //│ = 'b is 3' //│ // Output //│ 2 //│ res //│ = 'right-defined' ================================================ FILE: shared/src/test/diff/ucs/LeadingAnd.mls ================================================ :NewDefs class Some[T](value: T) //│ class Some[T](value: T) fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int // FIXME (parser) fun f(a, b) = if a is Some(av) and b is Some(bv) then av + bv //│ fun f: (Some[Int], Some[Int]) -> Int ================================================ FILE: shared/src/test/diff/ucs/LitUCS.mls ================================================ :NewDefs module A //│ module A // This one is easy to fix but what about the next one? // The following example can better reveal the essence of the problem. fun test(x: 0 | A) = if x is 0 then 0 A then A //│ fun test: (x: 0 | A) -> (0 | A) :e // case === (x,) (0,) of { true => 0; _ => case x of { A => A } } fun test(x: 0 | A) = if x === 0 then 0 x is A then A //│ ╔══[ERROR] Module 'A' does not support equality comparison because it does not have a parameter list //│ ║ l.17: x === 0 then 0 //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.18: x is A then A //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `0` is not an instance of type `A` //│ ║ l.15: fun test(x: 0 | A) = //│ ║ ^ //│ ╟── but it flows into reference with expected type `A` //│ ║ l.18: x is A then A //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.18: x is A then A //│ ╙── ^ //│ fun test: (x: 0 | A) -> (0 | A) fun test2(x) = if x === 0 then 0 x is A then A //│ fun test2: (A & Eql[0]) -> (0 | A) :e test2(0) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.43: test2(0) //│ ║ ^^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `A` //│ ║ l.43: test2(0) //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: //│ ║ l.39: x is A then A //│ ║ ^ //│ ╟── from reference: //│ ║ l.39: x is A then A //│ ╙── ^ //│ 0 | A | error //│ res //│ = 0 :e test2(A) //│ ╔══[ERROR] Module 'A' does not support equality comparison because it does not have a parameter list //│ ║ l.61: test2(A) //│ ╙── ^^^^^^^^ //│ 0 | A | error //│ res //│ = A { class: [class A] } ================================================ FILE: shared/src/test/diff/ucs/MultiwayIf.mls ================================================ :NewDefs fun f(x) = if x > 0 then 0 x == 0 then 1 _ then 2 //│ fun f: Num -> (0 | 1 | 2) fun f(x) = if x > 0 and x % 2 === 0 then true _ then false x == 0 then true _ then false //│ fun f: Int -> Bool f(0) f(2) f(3) f(0 - 1) f(0 - 2) //│ Bool //│ res //│ = true //│ res //│ = true //│ res //│ = false //│ res //│ = false //│ res //│ = false fun f(x) = if x > 0 and x % 2 === 0 then true else false x == 0 then true else false //│ fun f: Int -> Bool f(0) f(2) f(1) f(0 - 1) //│ Bool //│ res //│ = true //│ res //│ = true //│ res //│ = false //│ res //│ = false ================================================ FILE: shared/src/test/diff/ucs/NestedBranches.mls ================================================ :NewDefs class Some[out A](val value: A) module None class Left[out A](val leftValue: A) class Right[out A](val rightValue: A) module Nil class Cons[out A](val head: A, val tail: Cons[A] | Nil) class Pair[out A, out B](val fst: A, val snd: B) //│ class Some[A](value: A) //│ module None //│ class Left[A](leftValue: A) //│ class Right[A](rightValue: A) //│ module Nil //│ class Cons[A](head: A, tail: Cons[A] | Nil) //│ class Pair[A, B](fst: A, snd: B) fun optionApply(x, y, f) = if x is Some(xv) and y is Some(yv) then Some(f(xv, yv)) None then None None then None //│ fun optionApply: forall 'a 'b 'A. (None | Some['a], None | Some['b], ('a, 'b) -> 'A) -> (None | Some['A]) fun (::) cons(h, t) = Cons(h, t) //│ fun (::) cons: forall 'A. ('A, Cons['A] | Nil) -> Cons['A] let zeroToThree = 0 :: 1 :: 2 :: 3 :: Nil //│ let zeroToThree: Cons[0 | 1 | 2 | 3] //│ zeroToThree //│ = Cons {} fun f(x) = if x % 2 == 0 then Left(x) else Right(x) //│ fun f: forall 'A. (Int & 'A) -> (Left['A] | Right['A]) fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] mapPartition(x => Left(x + 1), zeroToThree) //│ Pair[Cons[Int] | Nil, Cons[nothing] | Nil] //│ res //│ = Pair {} mapPartition(f, zeroToThree) //│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = Pair {} fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] mapPartition(f, zeroToThree) //│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = Pair {} fun mapPartition(f, xs) = if xs is Nil then Pair(Nil, Nil) Cons(x, xs) and mapPartition(f, xs) is Pair(l, r) and f(x) is Left(v) then Pair(Cons(v, l), r) Right(v) then Pair(l, Cons(v, r)) //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> Pair[Cons['A] | Nil, Cons['A0] | Nil] mapPartition(f, zeroToThree) //│ Pair[Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = Pair {} fun mapPartition(f, xs) = if xs is Nil then [Nil, Nil] Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] //│ fun mapPartition: forall 'a 'A 'A0. ('a -> (Left['A] | Right['A0]), Cons['a] | Nil) -> [Cons['A] | Nil, Cons['A0] | Nil] mapPartition(f, zeroToThree) //│ [Cons[0 | 1 | 2 | 3] | Nil, Cons[0 | 1 | 2 | 3] | Nil] //│ res //│ = [ Cons {}, Cons {} ] // * Vertical alignment is not allowed! (good) :pe :w :e fun mapPartition(f, xs) = if xs is Nil then [Nil, Nil] Cons(x, xs) and mapPartition(f, xs) is [l, r] and f(x) is Left(v) then [Cons(v, l), r] Right(v) then [l, Cons(v, r)] //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^ //│ ╔══[WARNING] Paren-less applications should use the 'of' keyword //│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.113: Right(v) then [l, Cons(v, r)] //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── tuple literal of type `[?a, ?b]` is not a function //│ ║ l.112: and f(x) is Left(v) then [Cons(v, l), r] //│ ╙── ^^^^^^^^^^^^^^^ //│ fun mapPartition: forall 'a. ('a -> Left[anything], Cons['a] | Nil) -> (error | [Nil, Nil]) ================================================ FILE: shared/src/test/diff/ucs/NestedOpSplits.mls ================================================ :NewDefs // * Note that this always to the left :e fun f(x) = if x == 1 + 2 then 0 _ then 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.8: 1 + //│ ║ ^ //│ ║ l.9: 2 then 0 //│ ║ ^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.7: if x == //│ ║ ^^^^ //│ ║ l.8: 1 + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.8: 1 + //│ ║ ^ //│ ║ l.9: 2 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ fun f: Num -> (0 | 1) ================================================ FILE: shared/src/test/diff/ucs/NestedPattern.mls ================================================ :NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either fun compute(v) = if v is Left(Some(x)) then x * 5 Left(None()) then 1 Right(Some(x)) then x * 3 Right(None()) then 0 //│ fun compute: (Left[None | Some[Int]] | Right[None | Some[Int]]) -> Int fun crazy(v) = if v is Some(Some(Some(Some(Some(Some(None())))))) then "bruh!" _ then "lol" //│ fun crazy: (Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object & ~#Some | Some[Object]]]]]]) -> ("bruh!" | "lol") // * TODO(@chengluyu) fix these missing locations fun f(x) = if x is [0, 0] then "zeros" [1, 1] then "ones" _ then "bruh" //│ fun f: {0: Object, 1: Object} -> ("bruh" | "ones" | "zeros") f([0, 0]) f([1, 1]) f([1, 0]) //│ "bruh" | "ones" | "zeros" //│ res //│ = 'zeros' //│ res //│ = 'ones' //│ res //│ = 'bruh' fun f(x) = if x is [0, 0] then "zeros" [1, 1] then "ones" [y, 1] then x _ then "que?" //│ fun f: forall 'a. ({0: Object, 1: Object} & 'a) -> ("ones" | "que?" | "zeros" | 'a) f([0, 0]) f([1, 1]) f([0, 1]) f([1, 0]) //│ "ones" | "que?" | "zeros" | [1, 0] //│ res //│ = 'zeros' //│ res //│ = 'ones' //│ res //│ = [ 0, 1 ] //│ res //│ = 'que?' fun f(p) = if p is Some([x, y]) then x + y None() then 0 //│ fun f: (None | Some[{0: Int, 1: Int}]) -> Int class Union[A, B](a: A, b: B) //│ class Union[A, B](a: A, b: B) // Name conflict between the scrutinee and the positionals. // Desugar result: let tmp13 = x in case tmp13 of { Union => let x = (tmp13).a in let y = (tmp13).b in x } fun hmm(x) = if x is Union(x, y) then x //│ fun hmm: forall 'a. Union['a, anything] -> 'a hmm(Union(1, 2)) //│ 1 //│ res //│ = 1 ================================================ FILE: shared/src/test/diff/ucs/NuPlainConditionals.mls ================================================ :NewDefs class Pair[A](fst: A, snd: A) //│ class Pair[A](fst: A, snd: A) Pair(0, 1) is Pair //│ Bool //│ res //│ = true Pair(0, 1) is Pair(a, b) //│ Bool //│ res //│ = true Pair(0, 1) is Pair(0, _) //│ Bool //│ res //│ = true if Pair(0, 1) is Pair(a, b) then true else false //│ Bool //│ res //│ = true fun foo(x) = x is Pair(a, b) //│ fun foo: (Object & ~#Pair | Pair[anything]) -> Bool Pair(0, 1) is Pair(a, b) and a > b //│ Bool //│ res //│ = false if Pair(0, 1) is Pair(a, b) then a > b else false //│ Bool //│ res //│ = false fun foo(x) = x is Pair(a, b) and a > b //│ fun foo: (Object & ~#Pair | Pair[Num]) -> Bool fun foo(x) = if x is Pair(a, b) then a > b else false //│ fun foo: (Object & ~#Pair | Pair[Num]) -> Bool // TODO proper error fun foo(x) = x is Pair Int //│ ╔══[ERROR] unknown pattern {Pair; Int} //│ ║ l.54: Pair //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO proper error fun foo(x) = x is Pair(a, b) and a > b Int //│ ╔══[ERROR] unknown pattern {and(Pair(a, b,), >(a, b,),); Int} //│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int //│ ╙── ^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO support `|` fun foo1(x) = x is Pair(a, b) | Int fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `and` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] type identifier `>` not found //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.79: fun foo1(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.80: fun foo2(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ fun foo1: nothing -> error //│ fun foo2: nothing -> error //│ Code generation encountered an error: //│ unresolved symbol | class A(arg: Int) //│ class A(arg: Int) // TODO make `is` lower precedence than `=>` x => (x is A(_)) //│ Object -> Bool //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/ucs/Or.mls ================================================ :NewDefs class Some[T](value: T) //│ class Some[T](value: T) // TODO support `or` in UCS fun f(a, b) = if a is Some(v) and b is Some(v') then v + v' or b is Some(v) then v else 0 //│ ╔══[ERROR] cannot transform due to an illegal split operator or //│ ║ l.12: or b is Some(v) then v //│ ║ ^^ //│ ╟── the following branch will be discarded //│ ║ l.12: or b is Some(v) then v //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ fun f: (Object & ~#Some | Some[Int], Object & ~#Some | Some[Int]) -> Int ================================================ FILE: shared/src/test/diff/ucs/OverlappedBranches.mls ================================================ :NewDefs class Base() class Derived1() extends Base() class Derived2() extends Base() class Derived3() extends Derived2() //│ class Base() //│ class Derived1() extends Base //│ class Derived2() extends Base //│ class Derived3() extends Base, Derived2 // The very basic case. // It should warn about that the last two cases are unreachable. :w :ducs:normalize.result fun f1(x) = if x is Base then "b" Derived1 then "d1" Derived2 then "d2" //│ Normalized UCS term: //│ case x*‡ of //│ Base*◊ -> "b" //│ ╔══[WARNING] found a duplicated case //│ ║ l.18: Derived1 then "d1" //│ ║ ^^^^^^^^ //│ ╟── the case is covered by pattern Base //│ ║ l.17: Base then "b" //│ ║ ^^^^ //│ ╟── due to the subtyping relation //│ ║ l.4: class Derived1() extends Base() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[WARNING] found a duplicated case //│ ║ l.19: Derived2 then "d2" //│ ║ ^^^^^^^^ //│ ╟── the case is covered by pattern Base //│ ║ l.17: Base then "b" //│ ║ ^^^^ //│ ╟── due to the subtyping relation //│ ║ l.5: class Derived2() extends Base() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ fun f1: Base -> "b" f1(Base()) f1(Derived1()) f1(Derived2()) //│ "b" //│ res //│ = 'b' //│ res //│ = 'b' //│ res //│ = 'b' // Decision paths: // + «x is Base» and «p (x,)» => "b and p" // + «x is Derived1» => "d1" // + «x is Derived2» => "d2" // + => "otherwise" // The case tree: // «x» match // case Base => // if «p (x,)» // «"b and p"» // else // «x» match // case Derived1 => // «"d1"» // case Derived2 => // «"d2"» // default // «"otherwise"» // default // «"otherwise"» fun f2(x, p) = if x is Base and p(x) then "b and p" Derived1 then "d1" Derived2 then "d2" else "otherwise" //│ fun f2: forall 'a. (Object & ~#Base | 'a & (Base & ~#Derived1 & ~#Derived2 | Derived1 | Derived2), (Base & 'a) -> Bool) -> ("b and p" | "d1" | "d2" | "otherwise") f2(Base(), _ => true) // => b and p f2(Base(), _ => false) // otherwise //│ "b and p" | "d1" | "d2" | "otherwise" //│ res //│ = 'b and p' //│ res //│ = 'otherwise' f2(Derived1(), _ => true) // => b and p f2(Derived2(), _ => true) // => b and p //│ "b and p" | "d1" | "d2" | "otherwise" //│ res //│ = 'b and p' //│ res //│ = 'b and p' f2(Derived1(), _ => false) // => d1 f2(Derived2(), _ => false) // => d2 //│ "b and p" | "d1" | "d2" | "otherwise" //│ res //│ = 'd1' //│ res //│ = 'd2' ================================================ FILE: shared/src/test/diff/ucs/ParseFailures.mls ================================================ :NewDefs :NoJS type Tree[A] = Node[A] | Empty module Empty class Node[A](value: Int, left: Tree[A], right: Tree[A]) //│ type Tree[A] = Empty | Node[A] //│ module Empty //│ class Node[A](value: Int, left: Tree[A], right: Tree[A]) fun contains(node, wanted) = if node is Node(value, left, right) and wanted <= value then contains(left, wanted) >= value then contains(right, wanted) else true Empty then false //│ fun contains: forall 'A. (Empty | Node['A], Num) -> Bool fun contains'(node, wanted) = if node is Node(value, left, right) and wanted <= value then contains'(left, wanted) >= value then contains'(right, wanted) _ then true Empty then false //│ fun contains': forall 'A. (Empty | Node['A], Num) -> Bool :pe class Z() class O() fun foo(x, y) = if x is Z() and y is O() then 0 else 1 //│ ╔══[PARSE ERROR] Unexpected 'else' keyword here //│ ║ l.33: Z() and y is O() then 0 else 1 //│ ╙── ^^^^ //│ class Z() //│ class O() //│ fun foo: (Z, O) -> 0 ================================================ FILE: shared/src/test/diff/ucs/ParserFailures.mls ================================================ :NewDefs :NoJS // FIXME: Interleaved let bindings are not implemented in `IfOpsApp`. if x == 0 then "bad" let y = f(z) == y * y then 0 //│ ╔══[PARSE ERROR] expect an operator //│ ║ l.7: let y = f(z) //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected 'let' keyword here //│ ║ l.7: let y = f(z) //│ ╙── ^^^ //│ ╔══[ERROR] missing else branch //│ ║ l.6: == 0 then "bad" //│ ╙── ^^^^^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.5: if x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: == 0 then "bad" //│ ║ ^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ "bad" // FIXME: Interleaved let bindings are not implemented in `IfOpsApp`. fun tt(x) = if x is A() then "A" let y = 0 is B() then "B" //│ ╔══[PARSE ERROR] expect an operator //│ ║ l.31: let y = 0 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected 'let' keyword here //│ ║ l.31: let y = 0 //│ ╙── ^^^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.30: is A() then "A" //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.30: is A() then "A" //│ ╙── ^ //│ fun tt: nothing -> error ================================================ FILE: shared/src/test/diff/ucs/PlainConditionals.mls ================================================ :NewDefs class Pair[A, B](fst: A, snd: B) //│ class Pair[A, B](fst: A, snd: B) Pair(0, 1) is Pair //│ Bool //│ res //│ = true Pair(0, 1) is Pair(a, b) //│ Bool //│ res //│ = true Pair(0, 1) is Pair(0, _) //│ Bool //│ res //│ = true if Pair(0, 1) is Pair(a, b) then true else false //│ Bool //│ res //│ = true fun foo(x) = x is Pair(a, b) //│ fun foo: (Object & ~#Pair | Pair[anything, anything]) -> Bool Pair(0, 1) is Pair(a, b) and a > b //│ Bool //│ res //│ = false if Pair(0, 1) is Pair(a, b) then a > b else false //│ Bool //│ res //│ = false fun foo(x) = x is Pair(a, b) and a > b //│ fun foo: (Object & ~#Pair | Pair[Num, Num]) -> Bool fun foo(x) = if x is Pair(a, b) then a > b else false //│ fun foo: (Object & ~#Pair | Pair[Num, Num]) -> Bool // TODO proper error fun foo(x) = x is Pair Int //│ ╔══[ERROR] unknown pattern {Pair; Int} //│ ║ l.54: Pair //│ ║ ^^^^ //│ ║ l.55: Int //│ ╙── ^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO proper error fun foo(x) = x is Pair(a, b) and a > b Int //│ ╔══[ERROR] unknown pattern {and(Pair(a, b,), >(a, b,),); Int} //│ ║ l.67: Pair(a, b) and a > b //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ║ l.68: Int //│ ╙── ^^^^^ //│ ╔══[WARNING] this case is unreachable //│ ╙── because it is subsumed by the branch //│ fun foo: anything -> true // TODO support `|` fun foo(x) = x is Pair(a, b) | Int fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `and` not found //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^ //│ ╔══[ERROR] type identifier `>` not found //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier `|` not found //│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] Redefinition of 'foo' //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.79: fun foo(x) = x is Pair(a, b) | Int //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: | //│ ║ l.80: fun foo(x) = x is (Pair(a, b) and a > b) | Int //│ ╙── ^ //│ fun foo: nothing -> error //│ fun foo: nothing -> error //│ Code generation encountered an error: //│ unresolved symbol | class A[T](arg: T) //│ class A[T](arg: T) x => (x is A(_)) //│ (A[anything] | Object & ~#A) -> Bool //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/ucs/SimpleUCS.mls ================================================ :NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either :e fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv Right(xv) and y is Right(yv) then xv * yv None and y is None then 0 //│ ╔══[ERROR] when `x` is `Left` //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^^^^ //│ ╟── `y` has 2 missing cases //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.22: None and y is None then 0 //│ ║ ^^^^ //│ ╟── it can be class `Right` //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ╙── ^^^^^ //│ ╔══[ERROR] when `x` is `Right` //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ║ ^^^^^ //│ ╟── `y` has 2 missing cases //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ║ ^ //│ ╟── it can be class `Left` //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^^^^ //│ ╟── it can be module `None` //│ ║ l.22: None and y is None then 0 //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `None` //│ ║ l.22: None and y is None then 0 //│ ║ ^^^^ //│ ╟── `y` has 2 missing cases //│ ║ l.22: None and y is None then 0 //│ ║ ^ //│ ╟── it can be class `Left` //│ ║ l.20: Left(xv) and y is Left(yv) then xv + yv //│ ║ ^^^^ //│ ╟── it can be class `Right` //│ ║ l.21: Right(xv) and y is Right(yv) then xv * yv //│ ╙── ^^^^^ //│ fun f: (Left[Int] | None | Right[Int], nothing) -> Int fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv None then 0 //│ fun f: (Left[Int] | None, Left[Int]) -> Int fun f(x, y) = if x is Left(xv) and y is Left(yv) then xv + yv Right(yv) then xv * yv None then 0 //│ fun f: (Left[Int] | None, Left[Int] | Right[Int]) -> Int fun f(x) = if x is Some(v) and v < 0 then "negative" v > 0 then "positive" _ then "zero" None then "nothing" //│ fun f: (None | Some[Num]) -> ("negative" | "nothing" | "positive" | "zero") fun f(x, y) = if x is Some(x) and y is Some(y) then 0 //│ fun f: (Some[anything], Some[anything]) -> 0 class A[T](value: T) class B[T](value: T) //│ class A[T](value: T) //│ class B[T](value: T) fun f(x, y, u, v) = if x is A(a) and y == u then 0 v then 1 A(a) and y is B(0) then 0 B(1) then 1 A(_) then 99 //│ fun f: (A[anything], Num, Num, Num) -> (0 | 1 | 99) fun f(x) = if x is A(_) then "A" B(_) then "B" //│ fun f: (A[anything] | B[anything]) -> ("A" | "B") :e fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None and y is None then 0 //│ ╔══[ERROR] when `x` is `Some` //│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.116: None and y is None then 0 //│ ╙── ^^^^ //│ ╔══[ERROR] when `x` is `None` //│ ║ l.116: None and y is None then 0 //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.116: None and y is None then 0 //│ ║ ^ //│ ╟── it can be class `Some` //│ ║ l.115: Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^^^^ //│ fun f: (None | Some[Int], nothing) -> Int :e fun f(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None then xv * 2 None and y is Some(yv) then yv * 3 //│ ╔══[ERROR] when `x` is `None` //│ ║ l.143: None and y is //│ ║ ^^^^ //│ ╟── `y` has 1 missing case //│ ║ l.143: None and y is //│ ║ ^ //│ ╟── it can be module `None` //│ ║ l.142: None then xv * 2 //│ ╙── ^^^^ //│ fun f: (None | Some[Int], Some[Int]) -> Int fun f(x, y) = if x is A and y is B then "bruh" //│ fun f: (A[anything], B[anything]) -> "bruh" fun f(x, y, z) = if x is A and z == 0 and y == 0 and y is B then "bruh" A then "oui" //│ fun f: (A[anything], Num, Num) -> ("bruh" | "oui") // We do need a syntax to specify default branch in IfOpsApp... :e fun f(x, y) = if x is Some(x) and y > 0 then "gt" < 0 then "le" == 0 then "eq" //│ ╔══[ERROR] missing else branch //│ ║ l.176: == 0 then "eq" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.176: == 0 then "eq" //│ ║ ^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Some[anything], Num) -> ("eq" | "gt" | "le") fun isValid(x) = if x then false else true //│ fun isValid: Bool -> Bool fun f(x, allowNone) = if x is Some(x) and isValid(x) then "good" None and allowNone then "okay" else "bad" //│ fun f: (Object & ~#Some | Some[Bool], Bool) -> ("bad" | "good" | "okay") fun f(x) = if x is None then "bruh" Some(x) then "roll" _ and x == 0 then 0 _ then "rock" //│ fun f: (None | Num | Some[anything]) -> ("bruh" | "rock" | "roll" | 0) fun f(x, a, b) = if x is A(aa) and a then aa B(bb) and b then bb _ then 0 //│ fun f: forall 'a. (A['a] | B['a] | Object & ~#A & ~#B, Bool, Bool) -> (0 | 'a) fun f(x, y, b) = if x is Some(xv) and y is Some(yv) then "bruh" is None then "bruh" Some(xv) and b then xv + b _ then "roll" //│ fun f: (Object & ~#Some | Some[Int], Object & ~#Some | Some[anything], nothing) -> ("bruh" | "roll" | Int) fun g(x, y, b) = if x is Some(xv) and y is Some(yv) then yv is None then "bruh" Some(xv) and b then xv + b _ then "roll" //│ fun g: forall 'a. (Object & ~#Some | Some[Int], Object & ~#Some | Some['a], nothing) -> ("bruh" | "roll" | Int | 'a) fun foo(x, y, z) = if x - y > 0 then Some(x + y + z) else None //│ fun foo: forall 'A. (Int, Int, Int) -> (None | Some['A]) //│ where //│ 'A :> Int // Uncomment this block to make the following block work. // fun foo(x, y, z) = // if x - y > 0 then Some( // if x % 2 == 0 then Left(x) else Right(x) // ) else None :e fun f(u, v, w) = if foo(u, v, w) is Some(x) and x is Left(_) then "left-defined" Right(_) then "right-defined" None then "undefined" //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.244: Some(x) and x is //│ ║ ^^^^^^^^^^^^^^^^ //│ ║ l.245: Left(_) then "left-defined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.246: Right(_) then "right-defined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.247: None then "undefined" //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── operator application of type `Int` does not match type `Left[?A] | Right[?B]` //│ ║ l.230: if x - y > 0 then Some(x + y + z) else None //│ ║ ^^^^^^^^^ //│ ╟── Note: constraint arises from reference: //│ ║ l.244: Some(x) and x is //│ ║ ^ //│ ╟── from field selection: //│ ║ l.4: class Some[A](value: A) extends Option[A] //│ ║ ^^^^^ //│ ╟── Note: type parameter A is defined at: //│ ║ l.4: class Some[A](value: A) extends Option[A] //│ ╙── ^ //│ fun f: (Int, Int, Int) -> ("left-defined" | "right-defined" | "undefined") fun p(x) = if x >= 0 then Right(x) else Left(x) //│ fun p: forall 'B 'A. (Num & 'B & 'A) -> (Left['A] | Right['B]) fun g(a, b) = if p(a) is Left(x) and b is Some(y) then x + y None then x * a Right(x) and b is Some(y) then x * y None then x //│ fun g: (Int, None | Some[Int]) -> Int g(5, None) g(5, Some(7)) g(0 - 5, None) g(0 - 5, Some(9)) //│ Int //│ res //│ = 5 //│ res //│ = 35 //│ res //│ = 25 //│ res //│ = 4 class Var(name: Str) abstract class ValBase: (IntVal | BoolVal) class IntVal(value: Int) extends ValBase class BoolVal(value: Bool) extends ValBase class Lit(value: ValBase) //│ class Var(name: Str) //│ abstract class ValBase: BoolVal | IntVal //│ class IntVal(value: Int) extends ValBase //│ class BoolVal(value: Bool) extends ValBase //│ class Lit(value: ValBase) fun p(e, context) = if e is Var(x) and context.get(x) is Some(IntVal(v)) then Left(v) Some(BoolVal(v)) then Right(v) Lit(IntVal(v)) then Left(v) Lit(BoolVal(v)) then Right(v) //│ fun p: forall 'A 'B. (Lit | Var, {get: Str -> Some[BoolVal | IntVal]}) -> (Left[in 'A out Int | 'A] | Right[in 'B out Bool | 'B]) class Nil() //│ class Nil() // Support operator constructor like :: :e fun f(x) = if x is 0 :: Nil() then "oh" //│ ╔══[ERROR] Syntactic split of patterns are not supported //│ ║ l.325: 0 :: //│ ╙── ^^ //│ fun f: anything -> nothing fun f(x) = if x == 0 and x is A(_) then "A" B(_) then "B" else "bruh" //│ fun f: Num -> ("A" | "B" | "bruh") fun helper(x) = if x == 0 then None else Some(x) //│ fun helper: forall 'A. (Num & 'A) -> (None | Some['A]) fun g(x, y) = if x == 0 and helper(x) is Some(a) and helper(y) is Some(b) then a + b None then a + 1 None and helper(y) is Some(b) then 2 + b None then 1 else 0 //│ fun g: (Int, Int) -> Int fun test(x) = if x then 0 else "oops" //│ fun test: Bool -> ("oops" | 0) test(true) test(false) //│ "oops" | 0 //│ res //│ = 0 //│ res //│ = 'oops' :e test(0) test(1) //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.368: test(0) //│ ║ ^^^^^^^ //│ ╟── integer literal of type `0` is not an instance of type `Bool` //│ ║ l.368: test(0) //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.356: fun test(x) = if x then 0 else "oops" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.369: test(1) //│ ║ ^^^^^^^ //│ ╟── integer literal of type `1` is not an instance of type `Bool` //│ ║ l.369: test(1) //│ ║ ^ //│ ╟── Note: constraint arises from reference: //│ ║ l.356: fun test(x) = if x then 0 else "oops" //│ ╙── ^ //│ "oops" | 0 | error //│ res //│ = 'oops' //│ res //│ = 'oops' ================================================ FILE: shared/src/test/diff/ucs/SplitAfterOp.mls ================================================ :NewDefs :e fun f(x, b) = if x == 0 and b then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.6: 0 and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Bool) -> 0 :e fun f(x, y) = if x == y + 5 then 0 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.24: 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.22: if x == y + //│ ║ ^ //│ ║ l.23: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.22: if x == y + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.22: if x == y + //│ ║ ^ //│ ║ l.23: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.22: if x == y + //│ ║ ^ //│ ║ l.23: 5 then 0 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.24: 7 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.24: 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 :e fun f(x, y) = if x == y * 5 then 0 6 + 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.60: 6 + 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.58: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.60: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.58: if x == y * //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.58: if x == y * //│ ║ ^ //│ ║ l.59: 5 then 0 //│ ║ ^^^^^^^^^^^^^ //│ ║ l.60: 6 + 7 then 0 //│ ║ ^^^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.60: 6 + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 :e fun f(x, y) = if x == y + 5 then 0 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.107: 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.105: y + //│ ║ ^ //│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.104: if x == //│ ║ ^^^^ //│ ║ l.105: y + //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.105: y + //│ ║ ^ //│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.105: y + //│ ║ ^ //│ ║ l.106: 5 then 0 //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.107: 7 then 0 //│ ║ ^^^^^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.107: 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> 0 :e fun f(x, b) = if x == 1 and b then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.144: 1 and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.144: 1 and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.144: 1 and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Bool) -> 0 :e fun toEnglish(x) = if x == true then "t" 0 then "z" //│ ╔══[ERROR] missing else branch //│ ║ l.163: 0 then "z" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.161: if x == //│ ║ ^^^^ //│ ║ l.162: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` //│ ║ l.162: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.163: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") :e fun toEnglish(x) = if x == 0 then "z" true then "t" //│ ╔══[ERROR] missing else branch //│ ║ l.185: true then "t" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.183: if x == //│ ║ ^^^^ //│ ║ l.184: 0 then "z" //│ ║ ^^^^^^^^^^^^^^ //│ ║ l.185: true then "t" //│ ║ ^^^^^^^^ //│ ╟── reference of type `true` is not an instance of `Num` //│ ║ l.185: true then "t" //│ ╙── ^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.185: true then "t" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("t" | "z") :e fun toEnglish(x) = if x == 1 then "o" 0 then "z" //│ ╔══[ERROR] missing else branch //│ ║ l.209: 0 then "z" //│ ╙── ^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.209: 0 then "z" //│ ║ ^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> ("o" | "z") fun toEnglish(x) = if x == 0 then 1 else 1 //│ fun toEnglish: Num -> 1 :pe :e fun toEnglish(x) = if x == else 1 //│ ╔══[PARSE ERROR] Unexpected indented block in expression position //│ ║ l.229: else 1 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.228: if x == //│ ║ ^^^^ //│ ║ l.229: else 1 //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.228: if x == //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.228: if x == //│ ║ ^^^^ //│ ║ l.229: else 1 //│ ║ ^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Num` //│ ║ l.229: else 1 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.229: else 1 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun toEnglish: Num -> () ================================================ FILE: shared/src/test/diff/ucs/SplitAnd.mls ================================================ :NewDefs fun f(x, y) = if x == 0 and y == 0 then "bruh" y == 1 then "lol" else "okay" //│ fun f: (Num, Num) -> ("bruh" | "lol" | "okay") class A() class B() //│ class A() //│ class B() :e fun f(x) = if x == 0 and x is A() then "A" B() then "B" x == 0 then "lol" else "bruh" //│ ╔══[ERROR] missing else branch //│ ║ l.18: x is //│ ║ ^^^^ //│ ║ l.19: A() then "A" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.20: B() then "B" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.21: x == 0 then "lol" //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: else "bruh" //│ ╙── ^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.18: x is //│ ║ ^^^^ //│ ║ l.19: A() then "A" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.20: B() then "B" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.21: x == 0 then "lol" //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: else "bruh" //│ ║ ^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Num -> ("A" | "B" | "bruh" | "lol") :e fun f(x, y) = if x == 0 and y == 0 then "bruh" else "lol" //│ ╔══[ERROR] missing else branch //│ ║ l.52: y == 0 then "bruh" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.53: else "lol" //│ ╙── ^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.52: y == 0 then "bruh" //│ ║ ^^^^^^^^^^^^^^^^^^ //│ ║ l.53: else "lol" //│ ║ ^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Num) -> ("bruh" | "lol") ================================================ FILE: shared/src/test/diff/ucs/SplitAroundOp.mls ================================================ :NewDefs fun f(x, b) = if x === 0 and b then "n0" 1 and b then "n1" 2 then "n2" === "0" then "s0" "1" then "s1" "2" then "s2" else ":p" //│ fun f: (Eql["0" | "1" | "2" | 0 | 1 | 2], Bool) -> (":p" | "n0" | "n1" | "n2" | "s0" | "s1" | "s2") fun f(x, y, a, b) = if x === 0 and y === 0 then "x, y" a === 0 then "x, a" b === 0 then "x, b" else "nah" //│ fun f: (Eql[0], Eql[0], Eql[0], Eql[0]) -> ("nah" | "x, a" | "x, b" | "x, y") class A() class B() //│ class A() //│ class B() fun f(x) = if x is A() then 0 B() then 1 //│ fun f: (A | B) -> (0 | 1) :e // FIXME if x is A() === 0 then 0 > 0 then 1 < 0 then 2 //│ ╔══[ERROR] cannot transform due to an illegal split operator === //│ ║ l.40: === 0 then 0 //│ ║ ^^^ //│ ╟── the following branch will be discarded //│ ║ l.40: === 0 then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] cannot transform due to an illegal split operator > //│ ║ l.41: > 0 then 1 //│ ║ ^ //│ ╟── the following branch will be discarded //│ ║ l.41: > 0 then 1 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] cannot transform due to an illegal split operator < //│ ║ l.42: < 0 then 2 //│ ║ ^ //│ ╟── the following branch will be discarded //│ ║ l.42: < 0 then 2 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] identifier `x` not found //│ ║ l.38: if x is //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.38: if x is //│ ╙── ^ //│ nothing //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: shared/src/test/diff/ucs/SplitBeforeOp.mls ================================================ :NewDefs :e :ge if x == 0 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.6: == 0 then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.5: if x //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: == 0 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 //│ Code generation encountered an error: //│ unresolved symbol x :e :ge if x is A and y then 0 //│ ╔══[ERROR] identifier `x` not found //│ ║ l.23: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.24: is A and //│ ╙── ^ //│ ╔══[ERROR] missing else branch //│ ║ l.25: y then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.23: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.24: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol x :e :ge if x is A and y then 0 else 1 //│ ╔══[ERROR] identifier `x` not found //│ ║ l.47: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.48: is A and //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.47: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.48: is A and //│ ╙── ^ //│ error //│ Code generation encountered an error: //│ unresolved symbol x :e :ge if x == 0 then 0 is A() then "A" B() then "B" //│ ╔══[ERROR] identifier `x` not found //│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier `A` not found //│ ║ l.72: A() then "A" //│ ╙── ^ //│ ╔══[ERROR] type identifier `B` not found //│ ║ l.73: B() then "B" //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] identifier not found: x //│ ║ l.69: if x //│ ╙── ^ //│ ╔══[ERROR] type identifier not found: A //│ ║ l.72: A() then "A" //│ ╙── ^ //│ 0 | error //│ Code generation encountered an error: //│ unresolved symbol x ================================================ FILE: shared/src/test/diff/ucs/SplitOps.mls ================================================ :NewDefs abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option abstract class Either[out A, out B]: Left[A] | Right[B] class Left[A](leftValue: A) extends Either[A, nothing] class Right[B](rightValue: B) extends Either[nothing, B] //│ abstract class Either[A, B]: Left[A] | Right[B] //│ class Left[A](leftValue: A) extends Either //│ class Right[B](rightValue: B) extends Either :e :ge fun f(x) = if x is Left(v) then 0 is Right(v) then 1 <> undefined then 2 //│ ╔══[ERROR] missing else branch //│ ║ l.23: <> undefined then 2 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in operator application: //│ ║ l.20: if x //│ ║ ^ //│ ║ l.21: is Left(v) then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.22: is Right(v) then 1 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.23: <> undefined then 2 //│ ║ ^^^^^^^^^^^^^^^^ //│ ╟── undefined literal of type `()` is not an instance of type `Num` //│ ║ l.23: <> undefined then 2 //│ ╙── ^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.23: <> undefined then 2 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Left[anything] | Num | Right[anything]) -> (0 | 1 | 2) //│ Code generation encountered an error: //│ unresolved symbol <> :e :ge fun f(x) = if x is Some(xv) and y is Some(yv) then xv + yv is None() and y is None() then 0 //│ ╔══[ERROR] identifier `y` not found //│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier `y` not found //│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.51: is Some(xv) and y is Some(yv) then xv + yv //│ ╙── ^ //│ ╔══[ERROR] identifier not found: y //│ ║ l.52: is None() and y is None() then 0 //│ ╙── ^ //│ fun f: (None | Some[Int]) -> Int //│ Code generation encountered an error: //│ unresolved symbol y class A() class B() //│ class A() //│ class B() fun f(a, b) = if a is A() and b is B() then 0 //│ fun f: (A, B) -> 0 class C() //│ class C() :e fun f(a, b, c) = if a == 0 and b is B() and c is C() then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.85: == 0 and b is B() and c is C() then 0 //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.85: == 0 and b is B() and c is C() then 0 //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, B, C) -> 0 fun f(x) = if x is A() then "A" is B() then "B" //│ fun f: (A | B) -> ("A" | "B") fun sumOpt(x, y) = if x is Some(xv) and y is Some(yv) then xv + yv None() then xv is None() and y is Some(yv) then yv None() then 0 //│ fun sumOpt: forall 'a. (None | Some[Int], None | Some[Int & 'a]) -> (Int | 'a) fun f(x, y, z) = if x is A() and y == z then 1 is B() then 0 //│ fun f: (A, nothing, Num) -> (0 | 1) ================================================ FILE: shared/src/test/diff/ucs/SplitScrutinee.mls ================================================ :NewDefs fun f(x) = if x + 1 is 2 then 1 3 then 2 _ then "I don't know." //│ fun f: Int -> ("I don't know." | 1 | 2) [f(0), f(1), f(2), f(3)] //│ ["I don't know." | 1 | 2, "I don't know." | 1 | 2, "I don't know." | 1 | 2, "I don't know." | 1 | 2] //│ res //│ = [ "I don't know.", 1, 2, "I don't know." ] ================================================ FILE: shared/src/test/diff/ucs/ThenIndent.mls ================================================ :NewDefs // FIXME x => if x == 0 then "a" _ then "b" //│ ╔══[PARSE ERROR] Unexpected indented block here //│ ║ l.7: then "a" //│ ║ ^^^^^^^^^^^^ //│ ║ l.8: _ then "b" //│ ╙── ^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.5: x => if x == //│ ║ ^^^^ //│ ║ l.6: 0 //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.5: x => if x == //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.6: 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.6: 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ Num -> () //│ res //│ = [Function: res] ================================================ FILE: shared/src/test/diff/ucs/Tree.mls ================================================ :NewDefs type Option[out A] = Some[A] | None class Some[out A](value: A) module None //│ type Option[A] = None | Some[A] //│ class Some[A](value: A) //│ module None type Tree[out A] = Node[A] | Empty module Empty class Node[out A](value: A, left: Tree[A], right: Tree[A]) //│ type Tree[A] = Empty | Node[A] //│ module Empty //│ class Node[A](value: A, left: Tree[A], right: Tree[A]) fun find(t, v) = if t is Node(v', l, r) and v < v' then find(l, v) v > v' then find(r, v) _ then Some(v) Empty then None //│ fun find: forall 'A. (Empty | Node[Num], Num & 'A) -> (None | Some['A]) fun insert(t, v) = if t is Node(v', l, r) and v < v' then Node(v', insert(l, v), r) v > v' then Node(v', l, insert(r, v)) _ then t Empty then Node(v, Empty, Empty) //│ fun insert: forall 'A. (Empty | Node[Num & 'A], Num & 'A) -> (Node[nothing] | Node['A]) find(Empty, 0) find(Node(0, Empty, Empty), 0) find(Node(1, Empty, Empty), 0) //│ None | Some[0] //│ res //│ = None { class: [class None] } //│ res //│ = Some {} //│ res //│ = None { class: [class None] } ================================================ FILE: shared/src/test/diff/ucs/TrivialIf.mls ================================================ :NewDefs :NoJS fun abs(x) = if x < 0 then 0 - x else x //│ fun abs: Int -> Int abstract class Option[A]: Some[A] | None class Some[A](value: A) extends Option[A] module None extends Option //│ abstract class Option[A]: None | Some[A] //│ class Some[A](value: A) extends Option //│ module None extends Option fun getOrElse(opt, default) = if opt is Some(value) then value None then default //│ fun getOrElse: forall 'a. (None | Some['a], 'a) -> 'a getOrElse(None, 0) //│ 0 getOrElse(Some(42), 0) //│ 0 | 42 fun map(v, f) = if v is Some(x) then Some(f(x)) None then None //│ fun map: forall 'a 'A. (None | Some['a], 'a -> 'A) -> (None | Some['A]) fun inc(x) = x + 5 //│ fun inc: Int -> Int map(Some(5), x => x + 5) //│ None | Some['A] //│ where //│ 'A :> Int map(None, inc) //│ None | Some['A] //│ where //│ 'A :> Int :e fun f(a, b) = if a and b then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.46: fun f(a, b) = if a and b then 0 //│ ║ ^^^^^^^^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Bool, Bool) -> 0 :e fun f(x, y) = if x == y + 5 then 0 else if x == y + 7 then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.63: else if x == y + 7 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.63: else if x == y + 7 then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: (Num, Int) -> 0 // TODO support fun foo(x) = if x is Some (0) then 0 (1) then 1 //│ ╔══[PARSE ERROR] Unexpected parenthesis section here //│ ║ l.76: (1) then 1 //│ ╙── ^^^ //│ /!!!\ Uncaught error: java.lang.StackOverflowError // TODO support fun foo(x) = if x is Some of 0 then 0 1 then 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here //│ ║ l.84: 0 then 0 //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.83: fun foo(x) = if x is Some of //│ ║ ^^^^^^^^^^^^ //│ ║ l.84: 0 then 0 //│ ║ ^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.83: fun foo(x) = if x is Some of //│ ╙── ^^ //│ fun foo: Some[0] -> () ================================================ FILE: shared/src/test/diff/ucs/WeirdIf.mls ================================================ :NewDefs :w if _ then 0 else 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.6: else 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.5: _ then 0 //│ ╙── ^ //│ ╔══[WARNING] this case is unreachable //│ ║ l.7: else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.5: _ then 0 //│ ╙── ^ //│ 0 //│ res //│ = 0 :w if else 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.25: if else 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.25: if else 0 else 1 //│ ╙── ^ //│ 0 //│ res //│ = 0 :w fun f(x) = if x is else 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.37: fun f(x) = if x is else 0 else 1 //│ ╙── ^ //│ fun f: anything -> 0 fun f(x) = if x is else 0 //│ fun f: anything -> 0 :e if true then 0 //│ ╔══[ERROR] missing else branch //│ ║ l.51: then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.51: then 0 //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ 0 //│ res //│ = 0 // This cannot be parsed. But the next one works. :pe :e fun f(x) = if x === else "bruh" //│ ╔══[PARSE ERROR] Unexpected indented block in expression position //│ ║ l.68: else "bruh" //│ ╙── ^^^^ //│ ╔══[PARSE ERROR] Unexpected end of indented block; an expression was expected here //│ ║ l.68: else "bruh" //│ ╙── ^ //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application instead //│ ║ l.67: if x === //│ ║ ^^^^^ //│ ║ l.68: else "bruh" //│ ║ ^^^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.67: if x === //│ ╙── ^^ //│ ╔══[ERROR] missing else branch //│ ║ l.68: else "bruh" //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: //│ ║ l.68: else "bruh" //│ ║ ^ //│ ╙── expression of type `Bool` is not an instance of type `true` //│ fun f: Eql[()] -> () // But this works. fun f(x) = if x === _ then "bruh" //│ fun f: anything -> "bruh" fun boolToStr(x) = if x is true then "yah" false then "nah" //│ fun boolToStr: Bool -> ("nah" | "yah") boolToStr of true boolToStr of false //│ "nah" | "yah" //│ res //│ = 'yah' //│ res //│ = 'nah' ================================================ FILE: shared/src/test/diff/ucs/WeirdSplit.mls ================================================ :NewDefs class A() class B() //│ class A() //│ class B() fun f(x) = if x is A then 0 B then 1 //│ fun f: (A | B) -> (0 | 1) // Precedence problem: should we restruct terms when push them to the stack? :e fun f(x) = if x == 1 + 2 then 0 + _ then 1 //│ ╔══[ERROR] Type mismatch in application: //│ ║ l.20: + 2 then 0 //│ ║ ^^^ //│ ╟── operator application of type `Bool` is not an instance of type `Int` //│ ║ l.18: if x == //│ ║ ^^^^ //│ ║ l.19: 1 //│ ╙── ^^^^^^ //│ ╔══[ERROR] Type mismatch in type ascription: //│ ║ l.20: + 2 then 0 //│ ║ ^^^ //│ ╙── application of type `Int` is not an instance of type `Bool` //│ fun f: Num -> (0 | 1) fun f(x, s, t) = if x is A() and t then 0 and s then 0 is _ then 1 //│ fun f: (Object, Bool, Bool) -> (0 | 1) ================================================ FILE: shared/src/test/diff/ucs/Wildcard.mls ================================================ :NewDefs fun (++) strcat(a, b) = concat(a)(b) //│ fun (++) strcat: (Str, Str) -> Str type Option[T] = None | Some[T] module None class Some[T](val value: T) //│ type Option[T] = None | Some[T] //│ module None //│ class Some[T](value: T) type Either[A, B] = Left[A] | Right[B] class Left[A](val leftValue: A) class Right[B](val rightValue: B) //│ type Either[A, B] = Left[A] | Right[B] //│ class Left[A](leftValue: A) //│ class Right[B](rightValue: B) fun w1(x, e_0, e_1) = if x is Left(None) then "Left of None" Right(None) then "Right of None" _ and e_0 is y_0 and x is Left(Some(lv)) then "Left of Some of " ++ toString(lv) _ and e_1 is y_1 and x is Right(Some(rv)) then "Right of Some of " ++ toString(rv) //│ fun w1: forall 'a. (Left[None | Object & ~#None & ~#Some | Some[anything]] | Object & ~#Left & ~#Right | Right[None | Some[anything]], anything, 'a) -> (Str | 'a) w1(Left(None), "a", "b") w1(Right(None), "a", "b") w1(Left(Some(0)), "a", "b") w1(Right(Some(0)), "a", "b") //│ Str //│ res //│ = 'Left of None' //│ res //│ = 'Right of None' //│ res //│ = 'Left of Some of 0' //│ res //│ = 'Right of Some of 0' // Should be // case x of // Some -> 1 // None -> // case p(x) of // true -> 2 // _ -> 4 // _ -> // case p(x) of // true -> 3 // _ -> 5 :ducs:normalize.result,postprocess.result fun w2(x, p) = if x is Some then 1 _ and p(x) and x is None then 2 _ then 3 None then 4 _ then 5 //│ Normalized UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> //│ case x*‡ of //│ None*† -> 2 //│ _ -> 3 //│ _ -> //│ case x*‡ of //│ None*† -> 4 //│ _ -> 5 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 2 //│ _ -> 4 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 3 //│ _ -> 5 //│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Bool) -> (1 | 2 | 3 | 4 | 5) // Should be // case x of // Some -> 1 // None -> // case p(x) of // true -> 2 // _ -> 3 // _ -> // case p(x) of // true -> 2 // _ -> 4 :ducs:normalize.result,postprocess.result fun w2(x, p) = if x is Some then 1 _ and p(x) then 2 None then 3 _ then 4 //│ Normalized UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true*† -> 2 //│ _ -> //│ case x*‡ of //│ None*† -> 3 //│ _ -> 4 //│ Post-processed UCS term: //│ case x*‡ of //│ Some*◊ -> 1 //│ None*† -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 2 //│ _ -> 3 //│ _ -> //│ let ucs$test$0*† = p(x,) : Bool //│ case ucs$test$0*† of //│ true -> 2 //│ _ -> 4 //│ fun w2: forall 'a. (None | Object & 'a & ~#None & ~#Some | Some[anything], (None | 'a) -> Bool) -> (1 | 2 | 3 | 4) w2(Some(0), x => true) w2(None, x => true) w2(None, x => false) w2(0, x => false) //│ 1 | 2 | 3 | 4 //│ res //│ = 1 //│ res //│ = 2 //│ res //│ = 3 //│ res //│ = 4 fun w3(x, p) = if x is _ and p(x) then "r1" Some(xv) then "r2: " ++ toString(xv) None then "r3" _ then "r4" //│ fun w3: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str // Expect "r1" w3(0, _ => true) w3(None, _ => true) w3(Some(0), _ => true) //│ Str //│ res //│ = 'r1' //│ res //│ = 'r1' //│ res //│ = 'r1' // Expect "r2" w3(Some(0), _ => false) //│ Str //│ res //│ = 'r2: 0' // Expect "r3" w3(None, _ => false) //│ Str //│ res //│ = 'r3' // Expect "r4" w3(0, _ => false) //│ Str //│ res //│ = 'r4' :w // Decision paths: // + «tmp2 @ f (x,) is any => 0 // + => 1 fun w3_1(x, f) = if f(x) is _ then 0 else 1 //│ ╔══[WARNING] this case is unreachable //│ ║ l.194: if f(x) is _ then 0 else 1 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.194: if f(x) is _ then 0 else 1 //│ ╙── ^ //│ fun w3_1: forall 'a. ('a, 'a -> anything) -> 0 w3_1(0, _ => true) w3_1(0, _ => false) //│ 0 //│ res //│ = 0 //│ res //│ = 0 :w fun w3_1_1(x, f) = if f(x) is a then a else 0 //│ ╔══[WARNING] this case is unreachable //│ ║ l.213: if f(x) is a then a else 0 //│ ║ ^ //│ ╟── because it is subsumed by the branch //│ ║ l.213: if f(x) is a then a else 0 //│ ╙── ^ //│ fun w3_1_1: forall 'a 'b. ('a, 'a -> 'b) -> 'b w3_1_1(0, x => x) w3_1_1(0, x => x + 1) //│ Int //│ res //│ = 0 //│ res //│ = 1 // Decision paths: // + «a = x» and «p (x,)» => "r1" // + «x is Some» => concat ("r2: ",) (toString (xv,),) // + «x is None» => "r3" fun w4(x, p) = if x is a and p(x) then "r1" Some(xv) then "r2: " ++ toString(xv) None then "r3" _ then "r4" //│ fun w4: forall 'a. ('a & (Object & ~#Some | Some[anything]), 'a -> Bool) -> Str // Expect "r1" w4(0, _ => true) w4(None, _ => true) w4(Some(0), _ => true) //│ Str //│ res //│ = 'r1' //│ res //│ = 'r1' //│ res //│ = 'r1' // Expect "r2" w4(Some(0), _ => false) //│ Str //│ res //│ = 'r2: 0' // Expect "r3" w4(None, _ => false) //│ Str //│ res //│ = 'r3' // Expect "r4" w4(0, _ => false) //│ Str //│ res //│ = 'r4' class Alpha() class Beta() class Gamma() class Delta() //│ class Alpha() //│ class Beta() //│ class Gamma() //│ class Delta() // This should generate only one case expression instead of a chain of case // expressions. DO check the desugared term! :ducs:postprocess.result fun w5(y) = if y is Alpha then "alpha" _ and y is Beta then "beta" _ and y is Gamma then "gamma" _ and y is Delta then "delta" _ then "unknown" //│ Post-processed UCS term: //│ case y*‡ of //│ Alpha*◊ -> "alpha" //│ Gamma*◊ -> "gamma" //│ Delta*◊ -> "delta" //│ Beta*◊ -> "beta" //│ _ -> "unknown" //│ fun w5: Object -> ("alpha" | "beta" | "delta" | "gamma" | "unknown") w5(0) w5(Alpha()) w5(Beta()) w5(Gamma()) w5(Delta()) //│ "alpha" | "beta" | "delta" | "gamma" | "unknown" //│ res //│ = 'unknown' //│ res //│ = 'alpha' //│ res //│ = 'beta' //│ res //│ = 'gamma' //│ res //│ = 'delta' fun w6(x, y) = if x is _ and y is Some(z) then z None then 0 else x //│ fun w6: forall 'a. ('a, Object & ~#Some | Some['a]) -> (0 | 'a) w6("42", Some(42)) w6("42", None) w6("42", "42") //│ "42" | 0 //│ res //│ = 42 //│ res //│ = 0 //│ res //│ = '42' fun w7(x, f) = if x is _ and f(x) is Some(v) then v None then x Left(x) then x + 1 Right(x) then x + 2 //│ fun w7: forall 'a 'b. ('a & (Left[Int] | Right[Int]), 'a -> (Object & ~#Some | Some['b])) -> (Int | 'b | 'a) // The results are wrong: w7(Left(99), _ => Some(0)) // => 0 w7(Left(99), _ => None) // => Left(99) w7(Right(99), _ => Some(0)) // => 0 w7(Right(99), _ => None) // => Right(99) //│ Int | Right['B] //│ where //│ 'B :> 99 //│ <: Int //│ res //│ = 0 //│ res //│ = Left {} //│ res //│ = 0 //│ res //│ = Right {} w7(Left(99), _ => "test") w7(Right(99), _ => "test") //│ Int | Right['B] //│ where //│ 'B :> 99 //│ <: Int //│ res //│ = 100 //│ res //│ = 101 ================================================ FILE: shared/src/test/diff/ucs/zipWith.mls ================================================ :NewDefs declare val nothing: nothing //│ val nothing: nothing //│ nothing //│ = module None { fun value = nothing } class Some[out A](val value: A) //│ module None { //│ fun value: nothing //│ } //│ class Some[A](value: A) type List[out A] = Cons[A] | Nil module Nil { fun toArray = [] } class Cons[out A](val head: A, val tail: List[A]) { fun toArray: Array[anything] fun toArray = [head, tail.toArray] } //│ type List[A] = Cons[A] | Nil //│ module Nil { //│ fun toArray: [] //│ } //│ class Cons[A](head: A, tail: List[A]) { //│ fun toArray: Array[anything] //│ } fun pairup(x, y) = [x, y] //│ fun pairup: forall 'a 'b. ('a, 'b) -> ['a, 'b] // FIXME parsing fun zipWith_wrong(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None //│ ╔══[PARSE ERROR] Expected 'then'/'else' clause after 'if'; found operator application followed by newline instead //│ ║ l.42: if xs is Cons(x, xs) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.43: and ys is Cons(y, ys) //│ ║ ^^ //│ ╟── Note: 'if' expression starts here: //│ ║ l.42: if xs is Cons(x, xs) //│ ╙── ^^ //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.43: and ys is Cons(y, ys) //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Unexpected operator in expression position //│ ║ l.44: and zipWith_wrong(f, xs, ys) is Some(tail) //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Expected an expression; found a 'then'/'else' clause instead //│ ║ l.42: if xs is Cons(x, xs) //│ ║ ^^^^^^^^^^^^^^^^^ //│ ║ l.43: and ys is Cons(y, ys) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.44: and zipWith_wrong(f, xs, ys) is Some(tail) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.45: then Some(Cons(f(x, y), tail)) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ║ l.46: else None //│ ╙── ^^^^^^^^^^^ //│ fun zipWith_wrong: (anything, anything, anything) -> () fun zipWith_wrong(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None //│ fun zipWith_wrong: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) fun zipWith_wrong(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None //│ fun zipWith_wrong: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A]]) // * Notice the result is wrong (duh) zipWith_wrong(pairup, Nil, Nil) //│ None | Some[Cons[[nothing, nothing]]] //│ res //│ = None { class: [class None] } fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else if xs is Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) then if zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) else None else if xs is Nil and ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] fun zipWith(f, xs, ys) = if xs is Cons(x, xs) then if ys is Cons(y, ys) then if zipWith(f, xs, ys) is Some(tail) then Some(Cons(f(x, y), tail)) None then None Nil then None Nil then if ys is Nil then Some(Nil) else None //│ fun zipWith: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Nil, Cons['b] | Nil) -> (None | Some[Cons['A] | Nil]) zipWith(pairup, Nil, Nil).value.toArray //│ Array[anything] //│ res //│ = [] :re zipWith(pairup, Nil, Cons(0, Nil)).value.toArray //│ Array[anything] //│ res //│ Runtime error: //│ ReferenceError: nothing is not defined zipWith(pairup, Cons(0, Nil), Cons("0", Nil)).value.toArray //│ Array[anything] //│ res //│ = [ [ 0, '0' ], [] ] let ls = zipWith(pairup, Cons(0, Cons(1, Nil)), Cons("0", Cons("1", Nil))) ls.value.toArray //│ let ls: None | Some[Cons[[0 | 1, "0" | "1"]] | Nil] //│ Array[anything] //│ ls //│ = Some {} //│ res //│ = [ [ 0, '0' ], [ [ 1, '1' ], [] ] ] fun zipWith_wrong2(f, xs, ys) = if xs is Cons(x, xs) and ys is Cons(y, ys) and zipWith_wrong2(f, xs, ys) is Some(tail) then Cons(Some(f(x, y)), tail) else if xs is Nil and ys is Nil then Some(Nil) else None //│ fun zipWith_wrong2: forall 'a 'b 'A. (('a, 'b) -> 'A, Cons['a] | Object & ~#Cons, Cons['b] | Object & ~#Cons) -> (Cons[Some['A]] | None | Some[Nil]) // * No type error! The definition and use are well-typed... zipWith_wrong2(pairup, Cons(0, Cons(1, Nil)), Cons("0", Cons("1", Nil))) //│ Cons[Some[[0 | 1, "0" | "1"]]] | None | Some[Nil] //│ res //│ = None { class: [class None] } ================================================ FILE: shared/src/test/scala/mlscript/CodeGenTestHelpers.scala ================================================ package mlscript import mlscript.utils._ import mlscript.utils.shorthands._ class CodeGenTestHelpers(file: os.Path, output: String => Unit) { def showGeneratedCode(testCode: JSTestBackend.TestCode): Unit = { val JSTestBackend.TestCode(prelude, queries) = testCode if (!prelude.isEmpty) { output("// Prelude") prelude foreach { line => output(line) } } queries.zipWithIndex foreach { case (JSTestBackend.CodeQuery(prelude, code, _), i) => output(s"// Query ${i + 1}") prelude foreach { output(_) } code foreach { output(_) } case (JSTestBackend.AbortedQuery(reason), i) => output(s"// Query ${i + 1} aborted: $reason") case (JSTestBackend.EmptyQuery, i) => output(s"// Query ${i + 1} is empty") } output("// End of generated code") } def showReplPrelude( sourceLines: List[String], preludeReply: Option[ReplHost.Reply], blockLineNum: Int, ): Unit = { output(s"┌ Block at ${file.last}:${blockLineNum}") if (sourceLines.isEmpty) { output(s"├── No prelude") } else { output(s"├─┬ Prelude") output(s"│ ├── Code") sourceLines.iterator.foreach { line => output(s"│ │ $line") } } preludeReply.foreach { // Display successful results in multiple lines. // Display other results in single line. case ReplHost.Result(content, intermediate) => intermediate.foreach { value => output(s"│ ├── Intermediate") value.linesIterator.foreach { line => output(s"│ │ $line") } } output(s"│ └── Reply") content.linesIterator.foreach { line => output(s"│ $line") } case other => output(s"│ └── Reply $other") } } def showReplContent( queries: List[JSTestBackend.Query], replies: List[(ReplHost.Reply, Str)], ): Unit = { if (queries.isEmpty) { output(s"└── No queries") } else { val length = queries.length // We assume that queries.length === replies.length queries.iterator.zip(replies).zipWithIndex.foreach { case ((JSTestBackend.CodeQuery(preludeLines, codeLines, res), (reply, log)), i) => val p0 = if (i + 1 === length) " " else "│ " val p1 = if (i + 1 === length) "└─" else "├─" output(s"$p1┬ Query ${i + 1}/${queries.length}") if (preludeLines.isEmpty) { output(s"$p0├── Prelude: ") } else { output(s"$p0├── Prelude:") preludeLines.foreach { line => output(s"$p0├── $line") } } output(s"$p0├── Code:") codeLines.foreach { line => output(s"$p0├── $line") } // Show the intermediate reply if possible. reply match { case ReplHost.Result(_, Some(intermediate)) => output(s"$p0├── Intermediate: $intermediate") case _ => () } val p2 = if (i + 1 === length) " └──" else s"$p0└──" output(s"$p2 Reply: $reply") case ((JSTestBackend.AbortedQuery(reason), _), i) => val prefix = if (i + 1 === queries.length) "└──" else "├──" output(s"$prefix Query ${i + 1}/${queries.length}: ") case ((JSTestBackend.EmptyQuery, _), i) => val prefix = if (i + 1 === queries.length) "└──" else "├──" output(s"$prefix Query ${i + 1}/${queries.length}: ") } } } } ================================================ FILE: shared/src/test/scala/mlscript/DiffTests.scala ================================================ package mlscript import fastparse._ import fastparse.Parsed.Failure import fastparse.Parsed.Success import sourcecode.Line import scala.collection.mutable import scala.collection.mutable.{Map => MutMap} import mlscript.utils._, shorthands._ import mlscript.codegen.typescript.TsTypegenCodeBuilder import org.scalatest.{funsuite, ParallelTestExecution} import org.scalatest.time._ import org.scalatest.concurrent.{TimeLimitedTests, Signaler} import pretyper.PreTyper import os.Path abstract class ModeType { def expectTypeErrors: Bool def expectWarnings: Bool def expectParseErrors: Bool def fixme: Bool def showParse: Bool def verbose: Bool def noSimplification: Bool def explainErrors: Bool def dbg: Bool def dbgParsing: Bool def dbgSimplif: Bool def dbgUCS: Opt[Set[Str]] def dbgLifting: Bool def dbgDefunc: Bool def dbgSimpledef: Bool def fullExceptionStack: Bool def stats: Bool def stdout: Bool def noExecution: Bool def noGeneration: Bool def showGeneratedJS: Bool def generateTsDeclarations: Bool def debugVariance: Bool def expectRuntimeErrors: Bool def expectCodeGenErrors: Bool def showRepl: Bool def allowEscape: Bool def useIR: Bool def interpIR: Bool def irVerbose: Bool def genCpp: Bool def showCpp: Bool def runCpp: Bool def writeCpp: Bool def noTailRecOpt: Bool def simpledef: Bool def lift: Bool def nolift: Bool def prelude: Bool } class DiffTests(state: DiffTests.State) extends funsuite.AnyFunSuite with ParallelTestExecution with TimeLimitedTests { def this() = this(DiffTests.State) import state._ /** Hook for dependent projects, like the monomorphizer. */ def postProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit, raise: Diagnostic => Unit): (Ls[Str], Option[TypingUnit]) = (Nil, None) def postTypingProcess(mode: ModeType, basePath: Ls[Str], testName: Str, unit: TypingUnit, output: Str => Unit): Option[TypingUnit] = None @SuppressWarnings(Array("org.wartremover.warts.RedundantIsInstanceOf")) private val inParallel = isInstanceOf[ParallelTestExecution] // scala test will not execute a test if the test class has constructor parameters. // override this to get the correct paths of test files. protected lazy val files = allFiles.filter { file => val fileName = file.baseName // validExt(file.ext) && filter(fileName) validExt(file.ext) && filter(file.relativeTo(DiffTests.pwd)) } val timeLimit = TimeLimit override val defaultTestSignaler: Signaler = new Signaler { @annotation.nowarn("msg=method stop in class Thread is deprecated") def apply(testThread: Thread): Unit = { println(s"!! Test at $testThread has run out out time !! stopping..." + "\n\tNote: you can increase this limit by changing DiffTests.TimeLimit") // * Thread.stop() is considered bad practice because normally it's better to implement proper logic // * to terminate threads gracefully, avoiding leaving applications in a bad state. // * But here we DGAF since all the test is doing is runnign a type checker and some Node REPL, // * which would be a much bigger pain to make receptive to "gentle" interruption. // * It would feel extremely wrong to intersperse the pure type checker algorithms // * with ugly `Thread.isInterrupted` checks everywhere... testThread.stop() } } files.foreach { file => val basePath = file.segments.drop(dir.segmentCount).toList.init val testName = basePath.map(_ + "/").mkString + file.baseName test(testName) { val baseStr = basePath.mkString("/") val testStr = " " * (8 - baseStr.length) + baseStr + ": " + file.baseName if (!inParallel) print(s"Processing $testStr") // * For some reason, the color is sometimes wiped out when the line is later updated not in iTerm3: // if (!inParallel) print(s"${Console.CYAN}Processing${Console.RESET} $testStr ... ") val beginTime = System.nanoTime() val outputMarker = "//│ " // val oldOutputMarker = "/// " val diffBegMarker = "<<<<<<<" val diffMidMarker = "=======" val diff3MidMarker = "|||||||" // * Appears under `git config merge.conflictstyle diff3` (https://stackoverflow.com/a/18131595/1518588) val diffEndMarker = ">>>>>>>" val exitMarker = "=" * 100 var occursCheck = false val fileContents = os.read(file) val allLines = fileContents.splitSane('\n').toList val strw = new java.io.StringWriter val out = new java.io.PrintWriter(strw) { override def println(): Unit = print('\n') // to avoid inserting CRLF on Windows } var stdout = false def output(str: String) = // out.println(outputMarker + str) if (stdout) System.out.println(str) else str.splitSane('\n').foreach(l => out.println(outputMarker + l)) def outputSourceCode(code: SourceCode) = code.lines.foreach{line => out.println(outputMarker + line.toString())} val allStatements = mutable.Buffer.empty[DesugaredStatement] var newDefs = false trait MyTyper extends Typer { var ctx: Ctx } lazy val typer = new Typer(dbg = false, verbose = false, explainErrors = false, newDefs = newDefs) with MyTyper { var ctx: Ctx = Ctx.init override def recordTypeVars = occursCheck override def funkyTuples = file.ext =:= "fun" // override def emitDbg(str: String): Unit = if (stdout) System.out.println(str) else output(str) override def emitDbg(str: String): Unit = output(str) createdTypeVars.clear() } def ctx = typer.ctx var declared: Map[Str, typer.ST] = Map.empty val failures = mutable.Buffer.empty[Int] val unmergedChanges = mutable.Buffer.empty[Int] var preTyperScope = mlscript.pretyper.Scope.global case class Mode( expectTypeErrors: Bool = false, expectCompileErrors: Bool = false, expectWarnings: Bool = false, expectParseErrors: Bool = false, fixme: Bool = false, showParse: Bool = false, verbose: Bool = false, noSimplification: Bool = false, explainErrors: Bool = false, dbg: Bool = false, dbgParsing: Bool = false, dbgSimplif: Bool = false, dbgUCS: Opt[Set[Str]] = N, dbgLifting: Bool = false, dbgDefunc: Bool = false, dbgSimpledef: Bool = false, fullExceptionStack: Bool = false, stats: Bool = false, stdout: Bool = false, preciselyTypeRecursion: Bool = false, noExecution: Bool = false, noGeneration: Bool = false, showGeneratedJS: Bool = false, generateTsDeclarations: Bool = false, debugVariance: Bool = false, expectRuntimeErrors: Bool = false, expectCodeGenErrors: Bool = false, showRepl: Bool = false, allowEscape: Bool = false, simpledef: Bool = false, lift: Bool = false, nolift: Bool = false, // noProvs: Bool = false, useIR: Bool = false, interpIR: Bool = false, irVerbose: Bool = false, irOpt: Bool = false, irOptFuel: Int = 10, genCpp: Bool = false, showCpp: Bool = false, runCpp: Bool = false, writeCpp: Bool = false, noTailRecOpt: Bool = false, prelude: Bool = false, ) extends ModeType { def isDebugging: Bool = dbg || dbgSimplif } val defaultMode = Mode() var parseOnly = basePath.headOption.contains("parser") var allowTypeErrors = false var allowCompileErrors = false var allowParseErrors = false var showRelativeLineNums = false var noJavaScript = false var noProvs = false var allowRuntimeErrors = false var generalizeCurriedFunctions = false // var generalizeCurriedFunctions = true var distributeForalls = true // var distributeForalls = false var approximateNegativeFunction = false var noCycleCheck = false var noRecursiveTypes = false var constrainedTypes = false var irregularTypes = false var prettyPrintQQ = false var useIR = false // Enable this to see the errors from unfinished `PreTyper`. var showPreTyperErrors = false var noTailRec = false var noApproximateOverload = false // * This option makes some test cases pass which assume generalization should happen in arbitrary arguments // * but it's way too aggressive to be ON by default, as it leads to more extrusion, cycle errors, etc. var generalizeArguments = false var newParser = basePath.headOption.contains("parser") || basePath.headOption.contains("compiler") val backend = new JSTestBackend { override def oldDefs: Bool = !newDefs } var hostCreated = false lazy val host = { hostCreated = true; ReplHost() } val codeGenTestHelpers = new CodeGenTestHelpers(file, output) def rec(lines: List[String], mode: Mode): Unit = lines match { case "" :: Nil => case line :: ls if line.startsWith(":") => out.println(line) val newMode = line.tail.takeWhile(!_.isWhitespace) match { case "e" => mode.copy(expectTypeErrors = true) case "ce" => mode.copy(expectCompileErrors = true) case "w" => mode.copy(expectWarnings = true) case "pe" => mode.copy(expectParseErrors = true) case "p" => mode.copy(showParse = true) case "d" => mode.copy(dbg = true) case "dp" => mode.copy(dbgParsing = true) case DiffTests.DebugUCSFlags(x) => mode.copy(dbgUCS = mode.dbgUCS.fold(S(x))(y => S(y ++ x))) case "ds" => mode.copy(dbgSimplif = true) case "dl" => mode.copy(dbgLifting = true) case "dd" => mode.copy(dbgDefunc = true) case "dsd" => mode.copy(dbgSimpledef = true) case "s" => mode.copy(fullExceptionStack = true) case "v" | "verbose" => mode.copy(verbose = true) case "ex" | "explain" => mode.copy(expectTypeErrors = true, explainErrors = true) case "ns" | "no-simpl" => mode.copy(noSimplification = true) // case "limit-errors" => mode.copy(limitErrors = true) case "stats" => mode.copy(stats = true) case "stdout" => mode.copy(stdout = true) case "precise-rec-typing" => mode.copy(preciselyTypeRecursion = true) case "ParseOnly" => parseOnly = true; mode case "AllowTypeErrors" => allowTypeErrors = true; mode case "AllowCompileErrors" => allowCompileErrors = true; mode case "AllowParseErrors" => allowParseErrors = true; mode case "AllowRuntimeErrors" => allowRuntimeErrors = true; mode case "ShowRelativeLineNums" => showRelativeLineNums = true; mode case "NewParser" => newParser = true; mode case "NewDefs" => newParser = true; newDefs = true; mode case "NoJS" => noJavaScript = true; mode case "NoTailRec" => noTailRec = true; mode case "NoProvs" => noProvs = true; mode case "GeneralizeCurriedFunctions" => generalizeCurriedFunctions = true; mode case "DontGeneralizeCurriedFunctions" => generalizeCurriedFunctions = false; mode case "DistributeForalls" => distributeForalls = true generalizeCurriedFunctions = false mode case "DontDistributeForalls" => distributeForalls = false generalizeCurriedFunctions = true mode case "ApproximateNegativeFunction" => approximateNegativeFunction = true; mode case "NoCycleCheck" => noCycleCheck = true; mode case "CycleCheck" => noCycleCheck = false; mode case "OccursCheck" => occursCheck = true; mode case "RecursiveTypes" => noRecursiveTypes = false; occursCheck = false; mode case "NoRecursiveTypes" => noRecursiveTypes = true; occursCheck = true; mode case "ConstrainedTypes" => constrainedTypes = true; mode case "NoConstrainedTypes" => constrainedTypes = false; mode case "GeneralizeArguments" => generalizeArguments = true; mode case "DontGeneralizeArguments" => generalizeArguments = false; mode case "IrregularTypes" => irregularTypes = true; mode case "NoApproximateOverload" => noApproximateOverload = true; mode case str @ "Fuel" => // println("'"+line.drop(str.length + 2)+"'") typer.startingFuel = line.drop(str.length + 2).toInt; mode case "ResetFuel" => typer.startingFuel = typer.defaultStartingFuel; mode case "QQ" => prettyPrintQQ = true; mode // Enable this to see the errors from unfinished `PreTyper`. case "ShowPreTyperErrors" => newParser = true; newDefs = true; showPreTyperErrors = true; mode case "ne" => mode.copy(noExecution = true) case "ng" => mode.copy(noGeneration = true) case "js" => mode.copy(showGeneratedJS = true) case "ts" => mode.copy(generateTsDeclarations = true) case "dv" => mode.copy(debugVariance = true) case "ge" => mode.copy(expectCodeGenErrors = true) case "re" => mode.copy(expectRuntimeErrors = true) case "r" | "showRepl" => mode.copy(showRepl = true) case "escape" => mode.copy(allowEscape = true) case "sd" => {mode.copy(simpledef = true)} case "lift" => {mode.copy(lift = true)} case "noTailRec" => mode.copy(noTailRecOpt = true) case "nolift" => {mode.copy(nolift = true)} case "exit" => out.println(exitMarker) ls.dropWhile(_ =:= exitMarker).tails.foreach { case Nil => case lastLine :: Nil => out.print(lastLine) case l :: _ => out.println(l) } return () case "UseIR" => useIR = true; mode case "useIR" => mode.copy(useIR = true) case "interpIR" => mode.copy(interpIR = true) case "irVerbose" => mode.copy(irVerbose = true) case "genCpp" => mode.copy(genCpp = true) case "showCpp" => mode.copy(showCpp = true) case "runCpp" => mode.copy(runCpp = true) case "writeCpp" => mode.copy(writeCpp = true) case "prelude" => mode.copy(prelude = true) case _ => failures += allLines.size - lines.size output("/!\\ Unrecognized option " + line) mode } rec(ls, newMode) case line :: ls if line.startsWith("// FIXME") || line.startsWith("// TODO") => out.println(line) rec(ls, mode.copy(fixme = true)) case line :: ls if line.startsWith(outputMarker) //|| line.startsWith(oldOutputMarker) => rec(ls, defaultMode) case line :: ls if line.isEmpty => out.println(line) rec(ls, defaultMode) case line :: ls if line.startsWith("//") => out.println(line) rec(ls, mode) case line :: ls if line.startsWith(diffBegMarker) => // Check if there are unmerged git conflicts val diff = ls.takeWhile(l => !l.startsWith(diffEndMarker)) assert(diff.exists(_.startsWith(diffMidMarker)), diff) val rest = ls.drop(diff.length) val hdo = rest.headOption assert(hdo.exists(_.startsWith(diffEndMarker)), hdo) val blankLines = diff.count(_.isEmpty) val hasBlankLines = diff.exists(_.isEmpty) if (diff.forall(l => l.startsWith(outputMarker) || l.startsWith(diffMidMarker) || l.startsWith(diff3MidMarker) || l.isEmpty)) { for (_ <- 1 to blankLines) out.println() } else { unmergedChanges += allLines.size - lines.size + 1 out.println(diffBegMarker) diff.foreach(out.println) out.println(diffEndMarker) } rec(rest.tail, if (hasBlankLines) defaultMode else mode) // process block of text and show output - type, expressions, errors case l :: ls => val blockLineNum = (allLines.size - lines.size) + 1 val block = (l :: ls.takeWhile(l => l.nonEmpty && !( l.startsWith(outputMarker) || l.startsWith(diffBegMarker) // || l.startsWith(oldOutputMarker) ))).toIndexedSeq block.foreach(out.println) val processedBlock = if (file.ext =:= "fun") block.map(_ + "\n") else MLParser.addTopLevelSeparators(block) val processedBlockStr = processedBlock.mkString val fph = new FastParseHelpers(block) val globalStartLineNum = allLines.size - lines.size + 1 var totalTypeErrors = 0 var totalParseErrors = 0 var totalCompileErrors = 0 var totalWarnings = 0 var totalRuntimeErrors = 0 var totalCodeGenErrors = 0 // report errors and warnings def report(diags: Ls[mlscript.Diagnostic], output: Str => Unit = output): Unit = { diags.foreach { diag => val sctx = Message.mkCtx(diag.allMsgs.iterator.map(_._1), newDefs, "?") val headStr = diag match { case ErrorReport(msg, loco, src) => src match { case Diagnostic.Lexing => totalParseErrors += 1 s"╔══[LEXICAL ERROR] " case Diagnostic.Parsing => totalParseErrors += 1 s"╔══[PARSE ERROR] " case Diagnostic.Compilation => totalCompileErrors += 1 s"╔══[COMPILATION ERROR] " case _ => // TODO customize too totalTypeErrors += 1 s"╔══[ERROR] " } case WarningReport(msg, loco, src) => totalWarnings += 1 s"╔══[WARNING] " } val lastMsgNum = diag.allMsgs.size - 1 var globalLineNum = blockLineNum // solely used for reporting useful test failure messages diag.allMsgs.zipWithIndex.foreach { case ((msg, loco), msgNum) => val isLast = msgNum =:= lastMsgNum val msgStr = msg.showIn(sctx) if (msgNum =:= 0) output(headStr + msgStr) else output(s"${if (isLast && loco.isEmpty) "╙──" else "╟──"} ${msgStr}") if (loco.isEmpty && diag.allMsgs.size =:= 1) output("╙──") loco.foreach { loc => val (startLineNum, startLineStr, startLineCol) = loc.origin.fph.getLineColAt(loc.spanStart) if (globalLineNum =:= 0) globalLineNum += startLineNum - 1 val (endLineNum, endLineStr, endLineCol) = loc.origin.fph.getLineColAt(loc.spanEnd) var l = startLineNum var c = startLineCol while (l <= endLineNum) { val globalLineNum = loc.origin.startLineNum + l - 1 val relativeLineNum = globalLineNum - blockLineNum + 1 val shownLineNum = if (showRelativeLineNums && relativeLineNum > 0) s"l.+$relativeLineNum" else "l." + globalLineNum val prepre = "║ " val pre = s"$shownLineNum: " val curLine = loc.origin.fph.lines(l - 1) output(prepre + pre + "\t" + curLine) val tickBuilder = new StringBuilder() tickBuilder ++= ( (if (isLast && l =:= endLineNum) "╙──" else prepre) + " " * pre.length + "\t" + " " * (c - 1)) val lastCol = if (l =:= endLineNum) endLineCol else curLine.length + 1 while (c < lastCol) { tickBuilder += ('^'); c += 1 } if (c =:= startLineCol) tickBuilder += ('^') output(tickBuilder.toString) c = 1 l += 1 } } } if (diag.allMsgs.isEmpty) output("╙──") if (!mode.fixme) { if (!allowTypeErrors && !mode.expectTypeErrors && diag.isInstanceOf[ErrorReport] && diag.source =:= Diagnostic.Typing) { output("TEST CASE FAILURE: There was an unexpected type error"); failures += globalLineNum } if (!allowParseErrors && !mode.expectParseErrors && diag.isInstanceOf[ErrorReport] && (diag.source =:= Diagnostic.Lexing || diag.source =:= Diagnostic.Parsing)) { output("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } if (!allowCompileErrors && !mode.expectCompileErrors && diag.isInstanceOf[ErrorReport] && diag.source =:= Diagnostic.Compilation) { output("TEST CASE FAILURE: There was an unexpected compilation error"); failures += globalLineNum } if (!allowTypeErrors && !allowParseErrors && !mode.expectWarnings && diag.isInstanceOf[WarningReport]) { output("TEST CASE FAILURE: There was an unexpected warning"); failures += globalLineNum } } () } } val raise: Raise = d => report(d :: Nil) val legacyParser = file.ext =:= "fun" // try to parse block of text into mlscript ast val ans = try { if (newParser) { val origin = Origin(testName, globalStartLineNum, fph) val lexer = new NewLexer(origin, raise, dbg = mode.dbgParsing) val tokens = lexer.bracketedTokens if (mode.showParse || mode.dbgParsing || parseOnly) output(NewLexer.printTokens(tokens)) val p = new NewParser(origin, tokens, newDefs, raise, dbg = mode.dbgParsing, N) { def doPrintDbg(msg: => Str): Unit = if (dbg) output(msg) } val res = p.parseAll(p.typingUnit) if (parseOnly) output(s"Parsed: ${res.showDbg}") if (mode.showParse) output(s"AST: $res") val newMode = if (useIR) { mode.copy(useIR = true) } else mode val newNewMode = if (noTailRec) { newMode.copy(noTailRecOpt = true) } else newMode val (postLines, nuRes) = postProcess(newNewMode, basePath, testName, res, output, raise) postLines.foreach(output) if (parseOnly) Success(Pgrm(Nil), 0) else if (mode.lift) { import Message._ Success(Pgrm(nuRes.getOrElse({ raise(ErrorReport(msg"Post-process failed to produce AST." -> None :: Nil, true, Diagnostic.Compilation)) TypingUnit(Nil) }).entities), 0) } else Success(Pgrm(res.entities), 0) } else parse(processedBlockStr, p => if (legacyParser) new Parser(Origin(testName, globalStartLineNum, fph)).pgrm(p) else new MLParser(Origin(testName, globalStartLineNum, fph)).pgrm(p), verboseFailures = true ) } match { case f: Failure => val Failure(lbl, index, extra) = f val (lineNum, lineStr, col) = fph.getLineColAt(index) val globalLineNum = (allLines.size - lines.size) + lineNum if (!mode.expectParseErrors && !mode.fixme) { output("TEST CASE FAILURE: There was an unexpected parse error"); failures += globalLineNum } output("/!\\ Parse error: " + extra.trace().msg + s" at l.$globalLineNum:$col: $lineStr") // successfully parsed block into a valid syntactically valid program case Success(p, index) => if (mode.expectParseErrors && !newParser && !legacyParser) { output("TEST CASE FAILURE: There was an unexpected parse success"); failures += blockLineNum } if (mode.showParse || mode.dbgParsing) output("Parsed: " + p.showDbg) // if (mode.isDebugging) typer.resetState() if (mode.stats) typer.resetStats() typer.dbg = mode.dbg // typer.recordProvenances = !noProvs typer.recordProvenances = !noProvs && !mode.dbg && !mode.dbgSimplif || mode.explainErrors typer.generalizeCurriedFunctions = generalizeCurriedFunctions typer.approximateNegativeFunction = approximateNegativeFunction typer.distributeForalls = distributeForalls typer.noCycleCheck = noCycleCheck typer.noRecursiveTypes = noRecursiveTypes typer.constrainedTypes = constrainedTypes typer.generalizeArguments = generalizeArguments typer.irregularTypes = irregularTypes typer.verbose = mode.verbose typer.explainErrors = mode.explainErrors stdout = mode.stdout typer.preciselyTypeRecursion = mode.preciselyTypeRecursion typer.noApproximateOverload = noApproximateOverload val oldCtx = ctx def getType(ty: typer.SimpleType, pol: Opt[Bool], removePolarVars: Bool = true): Type = getTypeLike(ty, pol, removePolarVars) match { case ty: Type => ty case _ => die } def getTypeLike(ty: typer.SimpleType, pol: Opt[Bool], removePolarVars: Bool): TypeLike = { if (mode.isDebugging) output(s"⬤ Typed as: $ty") if (mode.isDebugging) output(s" where: ${ty.showBounds}") typer.dbg = mode.dbgSimplif if (mode.noSimplification) typer.expandType(ty)(ctx) else { object SimplifyPipeline extends typer.SimplifyPipeline { def debugOutput(msg: => Str): Unit = if (mode.dbgSimplif) output(msg) } val sim = SimplifyPipeline(ty, pol, removePolarVars)(ctx) val exp = typer.expandType(sim)(ctx) if (mode.dbgSimplif) output(s"⬤ Expanded: ${exp}") def stripPoly(pt: PolyType): Type = pt.targs.filterNot(_.isRight) match { case Nil => pt.body case tps => PolyType(tps, pt.body) } exp match { // * Strip top-level implicitly-quantified type variables case pt: PolyType => stripPoly(pt) case Constrained(pt: PolyType, bs, cs, tscs) => Constrained(stripPoly(pt), bs, cs, tscs) case ty => ty } } } val (typeDefs, stmts, newDefsResults) = if (newDefs) { val vars: Map[Str, typer.SimpleType] = Map.empty val rootTypingUnit = TypingUnit(p.tops) val preTyper = new PreTyper { override def debugTopicFilters = mode.dbgUCS override def emitString(str: String): Unit = output(str) } // This scope will be passed to typer and code generator after // pretyper is completed. preTyperScope = preTyper(rootTypingUnit, preTyperScope, "") report(preTyper.filterDiagnostics(_.source is Diagnostic.Desugaring)) if (showPreTyperErrors) report(preTyper.filterDiagnostics(_.source is Diagnostic.PreTyping)) val tpd = typer.typeTypingUnit(rootTypingUnit, N)(ctx, raise, vars) def showTTU(ttu: typer.TypedTypingUnit, ind: Int): Unit = { val indStr = " " * ind ttu.implementedMembers.foreach { // case p: typer.NuTypeParam => // output(s"${indStr}${p.name}: ${p.ty}") case p: typer.NuParam => output(s"${indStr}${p.name}: ${p.ty}") case tc: typer.TypedNuAls => output(s"${indStr}type ${tc.name} = ${tc.body}") case tt: typer.TypedNuTrt => output(s"${indStr}trait ${tt.name}") output(s"${indStr} this: ${tt.thisTy} ${tt.thisTy.showBounds .indentNewLines(indStr+" |")}") case tc: typer.TypedNuCls => output(s"${indStr}class ${tc.name}") output(s"${indStr} this: ${tc.thisTy} ${tc.thisTy.showBounds .indentNewLines(indStr+" |")}") // showTTU(tc.ttu, ind + 1) case tm: typer.TypedNuMxn => output(s"${indStr}mixin ${tm.name}") output(s"${indStr} this: ${tm.thisTy} ${tm.thisTy.showBounds .indentNewLines(indStr+" |")}") output(s"${indStr} super: ${tm.superTy} ${tm.superTy.showBounds .indentNewLines(indStr+" |")}") // showTTU(tm.ttu, ind + 1) case tf: typer.TypedNuFun => output(s"${indStr}${tf.fd.isLetRec match { case S(false) => "let" case S(true) => "let rec" case N => "fun" }} ${tf.name}: ${tf.typeSignature} where ${tf.typeSignature.showBounds .indentNewLines(indStr+"|")}") case typer.TypedNuDummy(d) => output(s"${indStr} ${d.name}") } } if (mode.dbg || mode.explainErrors) { output("======== TYPED ========") showTTU(tpd, 0) tpd.result.foreach { res_ty => output("res: " + tpd.result + " where " + res_ty.showBounds) } } val oldDbg = typer.dbg val sim = if (mode.noSimplification) tpd else try { typer.dbg = mode.dbgSimplif object SimplifyPipeline extends typer.SimplifyPipeline { def debugOutput(msg: => Str): Unit = if (mode.dbgSimplif) output(msg) } SimplifyPipeline(tpd, pol = S(true))(ctx) } finally typer.dbg = oldDbg val exp = typer.expandType(sim)(ctx) val expStr = exp.showIn(0)(ShowCtx.mk(exp :: Nil, newDefs))// .copy(newDefs = true) // TODO later output(expStr.stripSuffix("\n")) (Nil, Nil, S(p.tops.collect { case NuFunDef(isLet, nme, snme, tparams, bod) => (nme.name + " ", nme.name :: Nil, Nil, false, isLet.isEmpty) case t: Term => ("res ", "res" :: Nil, Nil, false, false) })) } else { val (diags, (typeDefs, stmts)) = p.desugared report(diags) if (mode.showParse) { typeDefs.foreach(td => output("Desugared: " + td.showDbg)) stmts.foreach { s => output("Desugared: " + s.showDbg) output(s"AST: $s") } } typer.ctx = typer.processTypeDefs(typeDefs)(ctx, raise) (typeDefs, stmts, N) } // initialize ts typegen code builder and // declare all type definitions for current block val tsTypegenCodeBuilder = new TsTypegenCodeBuilder() if (mode.generateTsDeclarations) { typeDefs.iterator.filter(td => ctx.tyDefs.contains(td.nme.name) && !oldCtx.tyDefs.contains(td.nme.name) ).foreach(td => tsTypegenCodeBuilder.declareTypeDef(td)) } val curBlockTypeDefs = typeDefs // add check from process type def block below .flatMap(td => if (!oldCtx.tyDefs.contains(td.nme.name)) ctx.tyDefs.get(td.nme.name) else None) // activate typer tracing if variance debugging is on and then set it back // this makes it possible to debug variance in isolation var temp = typer.dbg typer.dbg = mode.debugVariance typer.computeVariances(curBlockTypeDefs, ctx) typer.dbg = temp val varianceWarnings: MutMap[TypeName, Ls[TypeName]] = MutMap() // show the result of type inference for each processed type def typeDefs.foreach(td => // check if type def is not previously defined if (ctx.tyDefs.contains(td.nme.name) && !oldCtx.tyDefs.contains(td.nme.name)) { // ^ it may not end up being defined if there's an error val tn = td.nme.name val ttd = ctx.tyDefs(tn) val tvv = ttd.tvarVariances.getOrElse(die) // generate warnings for bivariant type variables val bivariantTypeVars = ttd.tparamsargs.iterator.filter{ case (tname, tvar) => tvv.get(tvar).contains(VarianceInfo.bi) }.map(_._1).toList if (!bivariantTypeVars.isEmpty) { varianceWarnings.put(td.nme, bivariantTypeVars) } val params = if (!ttd.tparamsargs.isEmpty) SourceCode.horizontalArray(ttd.tparamsargs.map{ case (tname, tvar) => val tvarVariance = tvv.getOrElse(tvar, throw new Exception( s"Type variable $tvar not found in variance store ${ttd.tvarVariances} for $ttd")) SourceCode(s"${tvarVariance.show}${tname.name}") }) else SourceCode("") output(s"Defined " + td.kind.str + " " + tn + params) // calculate types for all method definitions and declarations // only once and reuse for pretty printing and type generation val methodsAndTypes = (ttd.mthDecls ++ ttd.mthDefs).flatMap { case m@MethodDef(_, _, Var(mn), _, rhs) => rhs.fold( _ => ctx.getMthDefn(tn, mn).map(mthTy => (m, getType(mthTy.toPT, S(true)))), _ => ctx.getMth(S(tn), mn).map(mthTy => (m, getType(mthTy.toPT, N))) ) } // pretty print method definitions methodsAndTypes.foreach { case (MethodDef(_, _, Var(mn), _, rhs), res) => output(s"${rhs.fold( _ => "Defined", // the method has been defined _ => "Declared" // the method type has just been declared )} ${tn}.${mn}: ${res.show(newDefs)}") } // start typegen, declare methods if any and complete typegen block if (mode.generateTsDeclarations) { val mthDeclSet = ttd.mthDecls.iterator.map(_.nme.name).toSet val methods = methodsAndTypes // filter method declarations and definitions // without declarations .withFilter { case (mthd, _) => mthd.rhs.isRight || !mthDeclSet.contains(mthd.nme.name) } .map { case (mthd, mthdTy: Type) => (mthd.nme.name, mthdTy) case _ => die } tsTypegenCodeBuilder.addTypeDef(td, methods) } } ) if (!varianceWarnings.isEmpty) { import Message._ val diags = varianceWarnings.iterator.map { case (tname, biVars) => val warnings = biVars.map( tname => msg"${tname.name} is irrelevant and may be removed" -> tname.toLoc) WarningReport( msg"Type definition ${tname.name} has bivariant type parameters:" -> tname.toLoc :: warnings, newDefs) }.toList report(diags) } // process statements and output mlscript types // all `Def`s and `Term`s are processed here // generate typescript types if generateTsDeclarations flag is // set in the mode // The tuple type means: (, , , , ) val typerResults: Ls[(Str, Ls[Str], Ls[Str], Bool, Bool)] = newDefsResults getOrElse stmts.map { stmt => // Because diagnostic lines are after the typing results, // we need to cache the diagnostic blocks and add them to the // `typerResults` buffer after the statement has been processed. val diagnosticLines = mutable.Buffer.empty[Str] // We put diagnosis to the buffer in the following `Typer` routines. val raiseToBuffer: Raise = d => { report(d :: Nil, diagnosticLines += _) } // Typing results are before diagnostic messages in the subsumption case. // We use this flag to prevent too much changes in PR #150. var typeBeforeDiags = false val typingResults: Opt[(Str, Ls[Str])] = stmt match { // statement only declares a new term with its type // but does not give a body/definition to it case Def(isrec, nme, R(PolyType(tps, rhs)), isByname) => typer.dbg = mode.dbg implicit val prov: typer.TP = typer.NoProv // TODO implicit val r: Raise = raise val ty_sch = ctx.poly { ctx => typer.typeType(rhs)(ctx, raise, vars = tps.collect { case L(tp: TypeName) => tp.name -> typer.freshVar(typer.noProv/*FIXME*/, N)(1) }.toMap) } ctx += nme.name -> typer.VarSymbol(ty_sch, nme) declared += nme.name -> ty_sch val exp = getType(ty_sch, N) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, Some(nme.name)) S(nme.name -> (s"${nme.name}: ${exp.show(newDefs)}" :: Nil)) // statement is defined and has a body/definition case d @ Def(isrec, nme, L(rhs), isByname) => typer.dbg = mode.dbg val ty_sch = typer.typeLetRhs2(isrec, nme.name, rhs)(ctx, raiseToBuffer, vars = Map.empty) val exp = getType(ty_sch, S(true)) // statement does not have a declared type for the body // the inferred type must be used and stored for lookup S(nme.name -> (declared.get(nme.name) match { // statement has a body but it's type was not declared // infer it's type and store it for lookup and type gen case N => ctx += nme.name -> typer.VarSymbol(ty_sch, nme) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, Some(nme.name)) s"${nme.name}: ${exp.show(newDefs)}" :: Nil // statement has a body and a declared type // both are used to compute a subsumption (What is this??) // the inferred type is used to for ts type gen case S(sign) => ctx += nme.name -> typer.VarSymbol(sign, nme) val sign_exp = getType(sign, S(false)) typer.dbg = mode.dbg typer.subsume(ty_sch, sign)(ctx, raiseToBuffer, typer.TypeProvenance(d.toLoc, "def definition")) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, Some(nme.name)) typeBeforeDiags = true exp.show(newDefs) :: s" <: ${nme.name}:" :: sign_exp.show(newDefs) :: Nil })) case desug: DesugaredStatement => typer.dbg = mode.dbg typer.typeStatement(desug, allowPure = true)(ctx, raiseToBuffer, Map.empty, genLambdas = true) match { case R(binds) => binds.map { case nme -> pty => val ptType = getType(pty, S(true)) ctx += nme -> typer.VarSymbol(pty, Var(nme)) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(ptType, Some(nme)) nme -> (s"$nme: ${ptType.show(newDefs)}" :: Nil) } // statements for terms that compute to a value // and are not bound to a variable name case L(pty) => val exp = getType(pty, S(true)) S(if (exp =/= TypeName("unit")) { val res = "res" ctx += res -> typer.VarSymbol(pty, Var(res)) if (mode.generateTsDeclarations) tsTypegenCodeBuilder.addTypeGenTermDefinition(exp, None) res -> (s"res: ${exp.show(newDefs)}" :: Nil) } else ( "" -> Nil )) } } typingResults match { case N => ("", Nil, diagnosticLines.toList, false, false) case S(name -> typingLines) => (name, typingLines, diagnosticLines.toList, typeBeforeDiags, false) } } if (occursCheck) { typer.dbg = mode.dbg val tvs = typer.createdTypeVars.toList implicit val _ctx: typer.Ctx = ctx // Mostly for `typer.AliasOf` // if (mode.dbg) // output(s"REC? ${ // tvs.map(tv => tv -> tv.isRecursive_$(omitTopLevel = true)(ctx)) // .mkString(" ")}") // * Does not keep track of recursion polarity: // val recs = tvs.filter(_.isRecursive_$(omitTopLevel = true)(ctx)) val recs = tvs.flatMap { tv => val fromLB = tv.lbRecOccs_$(omitIrrelevantVars = true) match { case S(pol @ (S(true) | N)) => (tv, pol) :: Nil case _ => Nil } val fromUB = tv.ubRecOccs_$(omitIrrelevantVars = true) match { case S(pol @ (S(false) | N)) => (tv, pol) :: Nil case _ => Nil } fromLB ::: fromUB } if (mode.dbg) output(s"RECs: ${recs.mkString(" ")}") val withLocs = recs.filter(_._1.prov.loco.isDefined) withLocs.find { case (typer.AliasOf(typer.AssignedVariable(_)), _) => false case _ => true }.orElse(withLocs.headOption).orElse(recs.headOption).foreach { case (tv, pol) => import Message._ if (mode.dbg) output("REC: " + tv + tv.showBounds) report(ErrorReport( msg"Inferred recursive type: ${ getType(tv, pol = pol, removePolarVars = false).show(newDefs) }" -> tv.prov.loco :: Nil, newDefs) :: Nil) } typer.dbg = false } typer.createdTypeVars.clear() import JSTestBackend._ val executionResults: Result \/ Ls[(ReplHost.Reply, Str)] = if (!allowTypeErrors && file.ext =:= "mls" && !mode.noGeneration && !noJavaScript) { import codeGenTestHelpers._ val pp = postTypingProcess(mode, basePath, testName, TypingUnit(p.tops), output) match { case Some(stmts) => Pgrm(stmts.entities) case _ => p } backend(p, mode.allowEscape, newDefs && newParser, prettyPrintQQ) match { case testCode @ TestCode(prelude, queries) => { // Display the generated code. if (mode.showGeneratedJS) showGeneratedCode(testCode) // Execute code. if (!mode.noExecution) { val preludeReply = if (prelude.isEmpty) N else S(host.execute(prelude.mkString(" "))) preludeReply match { case S(ue: ReplHost.Unexecuted) => R((ue, "") :: Nil) case S(err: ReplHost.Error) => R((err, "") :: Nil) case _ => { if (mode.showRepl) showReplPrelude(prelude, preludeReply, blockLineNum) val replies = queries.map { case CodeQuery(preludeLines, codeLines, resultName) => host.query(preludeLines.mkString, codeLines.mkString, resultName) case AbortedQuery(reason) => (ReplHost.Unexecuted(reason), "") case EmptyQuery => (ReplHost.Empty, "") } if (mode.showRepl) showReplContent(queries, replies) R(replies) } } } else { L(ResultNotExecuted) } } case t => L(t) } } else { L(ResultNotExecuted) } def outputLog(log: String): Unit = { val loglines = log.split('\n').iterator.filter(_.nonEmpty) if (loglines.nonEmpty) { output("// Output") loglines.foreach(output) } } def checkReply( replyQueue: mutable.Queue[(ReplHost.Reply, Str)], prefixLength: Int, hide: Boolean, // Show nothing except errors if `hide` is true. errorOnly: Boolean ): Unit = { val indent = " " * prefixLength replyQueue.headOption.foreach { case (head, log) => head match { case ReplHost.Error(isSyntaxError, content) => // We don't expect type errors nor FIXME. if (!mode.expectTypeErrors && !mode.fixme) { // We don't expect code generation errors and it is. if (!mode.expectCodeGenErrors && isSyntaxError) { output("TEST CASE FAILURE: There was an unexpected codegen error"); failures += blockLineNum } // We don't expect runtime errors and it's a runtime error. if (!mode.expectRuntimeErrors && !allowRuntimeErrors && !isSyntaxError) { output("TEST CASE FAILURE: There was an unexpected runtime error"); failures += blockLineNum } } if (isSyntaxError) { // If there is a syntax error in the generated code, // it should be a code generation error. output("Syntax error:") totalCodeGenErrors += 1 } else { // Otherwise, it is a runtime error. output("Runtime error:") totalRuntimeErrors += 1 } content.linesIterator.foreach { s => output(" " + s) } case ReplHost.Unexecuted(reason) if (!hide) => output(indent + "= ") output(indent + " " + reason) case ReplHost.Result(result, _) if (!errorOnly && !hide) => result.linesIterator.zipWithIndex.foreach { case (line, i) => if (i =:= 0) output(indent + "= " + line) else output(indent + " " + line) } case ReplHost.Empty if (!errorOnly && !hide) => output(indent + "= ") case _ => () } outputLog(log) replyQueue.dequeue() } } // If code generation fails, show the error message. executionResults match { case R(replies) => val replyQueue = mutable.Queue.from(replies) if (typerResults.isEmpty) checkReply(replyQueue, 0, false, true) else { typerResults.foreach { case (name, typingLines, diagnosticLines, typeBeforeDiags, hide) => if (!hide) { if (typeBeforeDiags) { typingLines.foreach(output) diagnosticLines.foreach(output) } else { diagnosticLines.foreach(output) typingLines.foreach(output) } } checkReply(replyQueue, name.length, hide, false) } } case L(other) => // Print type checking results first. if (!newDefs) typerResults.foreach { case (_, typingLines, diagnosticLines, typeBeforeDiags, _) => if (typeBeforeDiags) { typingLines.foreach(output) diagnosticLines.foreach(output) } else { diagnosticLines.foreach(output) typingLines.foreach(output) } } other match { case _: TestCode => () // Impossible case. case e @ IllFormedCode(message) => totalCodeGenErrors += 1 if (!mode.expectCodeGenErrors && !mode.fixme && !mode.expectTypeErrors) failures += blockLineNum output("Code generation encountered an error:") output(s" ${message}") case Unimplemented(message) => output("Unable to execute the code:") output(s" ${message}") // case UnexpectedCrash(name, message) => // if (!mode.fixme) // failures += blockLineNum // output("Code generation crashed:") // output(s" $name: $message") case ResultNotExecuted => () } } // generate typescript typegen block if (mode.generateTsDeclarations) outputSourceCode(tsTypegenCodeBuilder.toSourceCode()) if (mode.stats) { val (co, an, su, ty) = typer.stats output(s"constrain calls : " + co) output(s"annoying calls : " + an) output(s"subtyping calls : " + su) // output(s"constructed types: " + ty) } if (mode.expectParseErrors && totalParseErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of parse error"); failures += blockLineNum } if (mode.expectTypeErrors && totalTypeErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of type error"); failures += blockLineNum } if (mode.expectCompileErrors && totalCompileErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of compilation error"); failures += blockLineNum } if (mode.expectWarnings && totalWarnings =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of warning"); failures += blockLineNum } if (mode.expectCodeGenErrors && totalCodeGenErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of codegen error"); failures += blockLineNum } if (mode.expectRuntimeErrors && totalRuntimeErrors =:= 0) { output("TEST CASE FAILURE: There was an unexpected lack of runtime error"); failures += blockLineNum } } catch { case oh_noes: ThreadDeath => throw oh_noes case err: Throwable => if (!mode.fixme) failures += allLines.size - lines.size // err.printStackTrace(out) output("/!!!\\ Uncaught error: " + err + err.getStackTrace().take( if (mode.fullExceptionStack) Int.MaxValue else if (mode.fixme || err.isInstanceOf[StackOverflowError]) 0 else 10 ).map("\n" + "\tat: " + _).mkString) } finally { typer.dbg = false typer.verbose = false } rec(lines.drop(block.size), mode) case Nil => } try rec(allLines, defaultMode) finally { out.close() if (hostCreated) host.terminate() } val testFailed = failures.nonEmpty || unmergedChanges.nonEmpty val result = strw.toString val endTime = System.nanoTime() val timeStr = (((endTime - beginTime) / 1000 / 100).toDouble / 10.0).toString val testColor = if (testFailed) Console.RED else Console.GREEN val resStr = s"${" " * (35 - testStr.length)}${testColor}${ " " * (6 - timeStr.length)}$timeStr ms${Console.RESET}" if (inParallel) println(s"${Console.CYAN}Processed${Console.RESET} $testStr$resStr") else println(resStr) if (result =/= fileContents) { println(s"! Updated $file") os.write.over(file, result) } if (testFailed) if (unmergedChanges.nonEmpty) fail(s"Unmerged non-output changes around: " + unmergedChanges.map("\n\t"+file.segments.toList.last+":"+_).mkString(", ")) else fail(s"Unexpected diagnostics (or lack thereof) at: " + failures.distinct.map("\n\t"+file.segments.toList.last+":"+_).mkString(", ")) }} } object DiffTests { val pwd: Path = os.pwd lazy val State = new State(pwd/"shared"/"src"/"test"/"diff") class State(val dir: Path) { val TimeLimit: Span = if (sys.env.contains("CI")) Span(60, Seconds) else Span(30, Seconds) val allFiles: IndexedSeq[Path] = os.walk(dir).filter(_.toIO.isFile) val validExt: Set[String] = Set("fun", "mls") // Aggregate unstaged modified files to only run the tests on them, if there are any val modified: Set[os.RelPath] = try os.proc("git", "status", "--porcelain", dir).call().out.lines().iterator.flatMap { gitStr => println(" [git] " + gitStr) val prefix = gitStr.take(2) val filePath = os.RelPath(gitStr.drop(3)) if (prefix =:= "A " || prefix =:= "M " || prefix =:= "R " || prefix =:= "D ") N // * Disregard modified files that are staged else S(filePath) }.toSet catch { case err: Throwable => System.err.println("/!\\ git command failed with: " + err) Set.empty } // private def filter(name: Str): Bool = def filter(file: os.RelPath): Bool = modified(file) || modified.isEmpty } object DebugUCSFlags { // E.g. "ducs", "ducs:foo", "ducs:foo,bar", "ducs:a.b.c,foo" private val pattern = "^ducs(?::(\\s*(?:[A-Za-z\\.-]+)(?:,\\s*[A-Za-z\\.-]+)*))?$".r def unapply(flagsString: Str): Opt[Set[Str]] = flagsString match { case "ducs" => S(Set.empty) case pattern(flags) => Option(flags).map(_.split(",\\s*").toSet) case _ => N } } } ================================================ FILE: shared/src/test/scala/mlscript/NodeTest.scala ================================================ package mlscript class NodeTests extends org.scalatest.funsuite.AnyFunSuite { test("node version") { val v = os.proc("node", "-v").call().out.lines().head println(s"Detected node version: " + v) assert( v.startsWith("v16.14") || v.startsWith("v16.15") || v.startsWith("v16.16") || v.startsWith("v16.17") || v.startsWith("v17") || v.startsWith("v18") || v.startsWith("v19") || v.startsWith("v20") || v.startsWith("v21") || v.startsWith("v22") ) } } ================================================ FILE: shared/src/test/scala/mlscript/ReplHost.scala ================================================ package mlscript import mlscript.utils._, shorthands._ /** * A helper class to manipulate an interactive Node.js process. */ final case class ReplHost() { import java.io.{BufferedWriter, BufferedReader, InputStreamReader, OutputStreamWriter} private val builder = new java.lang.ProcessBuilder() // `--interactive` always enters the REPL even if stdin is not a terminal builder.command("node", "--interactive") private val proc = builder.start() private val stdin = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream)) private val stdout = new BufferedReader(new InputStreamReader(proc.getInputStream)) private val stderr = new BufferedReader(new InputStreamReader(proc.getErrorStream)) // Skip the welcome message. collectUntilPrompt() execute("console.info = console.error") /** * This function simply collect output from Node.js until meet `"\n> "`. * It is useful to skip the welcome message and collect REPL reply from * interactive Node.js. It also filters out syntax errors. * * @return when there are syntax errors, returns `Error` where `syntax` is * `true`; otherwise, returns `Result` */ private def collectUntilPrompt(): ReplHost.Reply = { val buffer = new StringBuilder() while (!buffer.endsWith("\n> ")) { val c = stdout.read() if (c === -1) lastWords(s"ReplHost could not read more from NodeJS stdout.") buffer.append(c.toChar) } // Remove the trailing `"\n> "` buffer.delete(buffer.length - 3, buffer.length) val reply = buffer.toString() reply.linesIterator.find(_.startsWith(ReplHost.syntaxErrorHead)) match { case None => reply.linesIterator.find(_.startsWith(ReplHost.uncaughtErrorHead)) match { case None => ReplHost.Result(reply, None) case Some(uncaughtErrorLine) => { val message = uncaughtErrorLine.substring(ReplHost.uncaughtErrorHead.length) ReplHost.Error(false, message) } } case Some(syntaxErrorLine) => val message = syntaxErrorLine.substring(ReplHost.syntaxErrorHead.length) ReplHost.Error(true, message) } } private def consumeStderr(): String = { val buffer = new StringBuilder() while (stderr.ready()) buffer.append(stderr.read().toChar) buffer.toString() } /** * Parse query results from collected output from Node.js. * * - If there is line begins with `"Uncaught SyntaxError: "`, returns * the syntax error indicated in that line. * - If character `0x200B` presents in the output, returns the trimmed * error message. * - Otherwise, returns the result as a successfully reply. */ private def parseQueryResult(): ReplHost.Reply = { collectUntilPrompt().map { reply => // Find error boundaries. val begin = reply.indexOf(0x200b) val end = reply.lastIndexOf(0x200b) // If there is an error, returns `ReplHost.Error`. if (begin >= 0 && end >= 0) // `console.log` inserts a space between every two arguments, // so + 1 and - 1 is necessary to get correct length. ReplHost.Error(false, reply.substring(begin + 1, end)) else ReplHost.Result(reply, None) } } /** * Send code to Node.js process. * * @param code the code to execute */ private def send(code: Str): Unit = { stdin.write(if (code endsWith "\n") code else code + "\n") stdin.flush() } /** * Send code to the Node.js process. * * @param prelude the prelude code * @param code the main code * @param res the result identifier name * @return */ def query(prelude: Str, code: Str, res: Str): (ReplHost.Reply, Str) = { // For empty queries, returns empty. if (prelude.isEmpty && code.isEmpty) (ReplHost.Empty, "") else { // Warp the code with `try`-`catch` block. val wrapped = s"$prelude try { $code } catch (e) { console.log('\\u200B' + e + '\\u200B'); }" // Send the code send(wrapped) // If succeed, retrieve the result. (parseQueryResult().map { intermediate => // Since the result might not be the result of the expression, we need // to retrieve the value again. send(res match { case "res" => res case _ => s"globalThis[\"${res}\"]" }) parseQueryResult().map { result => // Add the intermediate result to the reply. ReplHost.Result(result, Some(intermediate)) } }, consumeStderr()) } } /** * Execute class and function declarations. * * @param code the code to execute * @return */ def execute(code: Str): ReplHost.Reply = { send(code) collectUntilPrompt() } /** * Kills the Node.js process. */ def terminate(): Unit = proc.destroy() } object ReplHost { /** * The syntax error beginning text from Node.js. */ private val syntaxErrorHead = "Uncaught SyntaxError: " private val uncaughtErrorHead = "Uncaught " /** * The base class of all kinds of REPL replies. */ sealed abstract class Reply { /** * Maps the successful part. Like `Option[T].map`. * * @param f the function over * @return */ def map(f: Str => Reply): Reply } /** * Represents a successful reply from Node.js. * * @param content the reply content, i.e. the final result * @param intermediate the intermediate evaluation results */ final case class Result(content: Str, intermediate: Opt[Str]) extends Reply { override def map(f: Str => Reply): Reply = f(content) override def toString(): Str = s"[success] $content" } /** * If the query is `Empty`, we will receive this. */ final object Empty extends Reply { override def map(f: Str => Reply): Reply = this override def toString(): Str = "[empty]" } /** * If the query is `Unexecuted`, we will receive this. * @param message the error message */ final case class Unexecuted(message: Str) extends Reply { override def map(f: Str => Reply): Reply = this override def toString(): Str = s"[unexecuted] $message" } /** * If the `ReplHost` captured errors, it will response with `Error`. * @param syntax if `true`, this is a syntax error; otherwise, this is a * runtime error * @param message the error message */ final case class Error(syntax: Bool, message: Str) extends Reply { override def map(f: Str => Reply): Reply = this override def toString(): Str = if (syntax) s"[syntax error] $message" else s"[runtime error] $message" } } ================================================ FILE: ts/dummy.ts ================================================ // This is just to work around vscode being overly needy (it reports errors in 'tsconfig.json' otherwise). ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/JSWriter.scala ================================================ package ts2mls import scala.scalajs.js import js.Dynamic.{global => g} import js.DynamicImplicits._ import mlscript.utils._ class JSWriter(filename: String) { import JSWriter._ // Create an empty file if it does not exists. if (!fs.existsSync(filename)) fs.writeFileSync(filename, "") // r+: Open file for reading and writing. An exception occurs if the file does not exist. // See https://nodejs.org/api/fs.html#file-system-flags to get more details. private val out = fs.openSync(filename, "r+") private var fileSize = 0 // how many bytes we've written in the file private var needTruncate = false writeln(":NewDefs\n:ParseOnly") def writeln(str: String) = { val strln = str + "\n" val buffer = createBuffer(strln.length) fs.readSync(out, buffer, 0, strln.length) // override when the content is different if (strln =/= buffer.toString()) { fs.writeSync(out, strln, fileSize) // `fileSize` is the offset from the beginning of the file needTruncate = true // if the file has been modified, we need to truncate the file to keep it clean } fileSize += strln.length } def close() = { if (needTruncate) fs.truncateSync(out, fileSize) // remove other content to keep the file from chaos fs.closeSync(out) } } object JSWriter { private val fs = g.require("fs") // must use fs module to manipulate files in JS def apply(filename: String) = new JSWriter(filename) private def createBuffer(length: Int) = g.Buffer.alloc(length) } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/TSCompilerInfo.scala ================================================ package ts2mls import scala.scalajs.js import js.Dynamic.{global => g} import js.DynamicImplicits._ import js.JSConverters._ import ts2mls.types._ object TypeScript { private val ts: js.Dynamic = try g.require("typescript") catch { case _ : Throwable => { System.err.println("Cannot find typescript in the current directory. Please install by running \"npm install\".") val process = g.require("process") process.exit(-1) } } val typeFlagsEnumLike = ts.TypeFlags.EnumLike val typeFlagsObject = ts.TypeFlags.Object val typeFlagsTypeParameter = ts.TypeFlags.TypeParameter val syntaxKindPrivate = ts.SyntaxKind.PrivateKeyword val syntaxKindProtected = ts.SyntaxKind.ProtectedKeyword val syntaxKindStatic = ts.SyntaxKind.StaticKeyword val objectFlagsAnonymous = ts.ObjectFlags.Anonymous val symbolFlagsOptional = ts.SymbolFlags.Optional // this flag is only for checking optional members of interfaces def isToken(node: js.Dynamic) = ts.isToken(node) def isClassDeclaration(node: js.Dynamic) = ts.isClassDeclaration(node) def isInterfaceDeclaration(node: js.Dynamic) = ts.isInterfaceDeclaration(node) def isFunctionLike(node: js.Dynamic) = ts.isFunctionLike(node) def isModuleDeclaration(node: js.Dynamic) = ts.isModuleDeclaration(node) def isArrayTypeNode(node: js.Dynamic) = ts.isArrayTypeNode(node) def isTupleTypeNode(node: js.Dynamic) = ts.isTupleTypeNode(node) def isTypeAliasDeclaration(node: js.Dynamic) = ts.isTypeAliasDeclaration(node) def isObjectLiteralExpression(node: js.Dynamic) = ts.isObjectLiteralExpression(node) def isLiteralTypeNode(node: js.Dynamic) = ts.isLiteralTypeNode(node) def isStringLiteral(node: js.Dynamic) = ts.isStringLiteral(node) def isVariableDeclaration(node: js.Dynamic) = ts.isVariableDeclaration(node) def forEachChild(root: js.Dynamic, func: js.Dynamic => Unit) = ts.forEachChild(root, func) def createProgram(filenames: Seq[String]) = ts.createProgram(filenames.toJSArray, js.Dictionary("maxNodeModuleJsDepth" -> 0, "target" -> ts.ScriptTarget.ES5, "module" -> ts.ModuleKind.CommonJS)) } class TSTypeChecker(checker: js.Dynamic) { def getReturnTypeOfSignature(node: js.Dynamic) = checker.getReturnTypeOfSignature(checker.getSignatureFromDeclaration(node)) def getTypeFromTypeNode(node: js.Dynamic) = checker.getTypeFromTypeNode(node) def getTypeOfSymbolAtLocation(sym: js.Dynamic, node: js.Dynamic) = checker.getTypeOfSymbolAtLocation(sym, node) def getPropertiesOfType(tp: js.Dynamic) = checker.getPropertiesOfType(tp) def isImplementationOfOverload(node: js.Dynamic) = checker.isImplementationOfOverload(node) def typeToTypeNode(tp: js.Dynamic) = checker.typeToTypeNode(tp) def getTypeArguments(tp: js.Dynamic) = checker.getTypeArguments(tp) def getElementTypeOfArrayType(tp: js.Dynamic) = checker.getElementTypeOfArrayType(tp) def isOptionalParameter(node: js.Dynamic) = checker.isOptionalParameter(node) def getTypeAtLocation(node: js.Dynamic) = checker.getTypeAtLocation(node) def getBaseType(tp: js.Dynamic) = checker.getBaseTypeOfLiteralType(tp) } object TSTypeChecker { def apply(checker: js.Dynamic) = new TSTypeChecker(checker) } class TSSymbolObject(sym: js.Dynamic)(implicit checker: TSTypeChecker) extends TSAny(sym) { private lazy val parent = TSSymbolObject(sym.parent) private lazy val flags = sym.flags // the first declaration of this symbol // if there is no overloading, there is only one declaration lazy val declaration = if (declarations.isUndefined) TSNodeObject(g.undefined) else declarations.get(0) lazy val escapedName: String = sym.escapedName.toString lazy val declarations = TSNodeArray(sym.declarations) lazy val `type` = TSTypeObject(sym.selectDynamic("type")) lazy val isOptionalMember = (flags & TypeScript.symbolFlagsOptional) > 0 // get the full name of the reference symbol // e.g. class A extends B => class A extends SomeNamespace'B lazy val fullName: String = if (parent.isUndefined || !parent.declaration.isNamespace) escapedName else s"${parent.fullName}.$escapedName" } object TSSymbolObject { def apply(node: js.Dynamic)(implicit checker: TSTypeChecker) = new TSSymbolObject(node) } class TSNodeObject(node: js.Dynamic)(implicit checker: TSTypeChecker) extends TSAny(node) { private lazy val modifiers = TSTokenArray(node.modifiers) lazy val isToken = TypeScript.isToken(node) lazy val isClassDeclaration = TypeScript.isClassDeclaration(node) lazy val isInterfaceDeclaration = TypeScript.isInterfaceDeclaration(node) lazy val isFunctionLike = TypeScript.isFunctionLike(node) lazy val isTypeAliasDeclaration = TypeScript.isTypeAliasDeclaration(node) lazy val isLiteralTypeNode = TypeScript.isLiteralTypeNode(node) lazy val isVariableDeclaration = TypeScript.isVariableDeclaration(node) lazy val isObjectLiteral = !initializer.isUndefined && !initializer.isToken && TypeScript.isObjectLiteralExpression(node.initializer) // `TypeScript.isModuleDeclaration` works on both namespaces and modules // but namespaces are more recommended, so we merely use `isNamespace` here lazy val isNamespace = TypeScript.isModuleDeclaration(node) lazy val isArrayTypeNode = TypeScript.isArrayTypeNode(node) lazy val isTupleTypeNode = TypeScript.isTupleTypeNode(node) lazy val isImplementationOfOverload = checker.isImplementationOfOverload(node) // if a node has an initializer or is marked by a question notation it is optional // e.g. `function f(x?: int) {}`, we can use it directly: `f()`. // in this case, there would be a question token. // e.g. `function f(x: int = 42) {}`, we can use it directly: `f()`. // in this case, the initializer would store the default value and be non-empty. lazy val isOptionalParameter = checker.isOptionalParameter(node) lazy val isStatic = if (modifiers.isUndefined) false else modifiers.foldLeft(false)((s, t) => t.isStatic) lazy val typeNode = TSTypeObject(checker.getTypeFromTypeNode(node)) lazy val typeAtLocation = TSTypeObject(checker.getTypeAtLocation(node)) lazy val symbol = TSSymbolObject(node.symbol) lazy val parameters = TSNodeArray(node.parameters) lazy val typeParameters = TSNodeArray(node.typeParameters) lazy val constraint = TSTokenObject(node.constraint) lazy val members = TSNodeArray(node.members) lazy val properties = TSNodeArray(node.properties) lazy val types = TSNodeArray(node.types) lazy val heritageClauses = TSNodeArray(node.heritageClauses) lazy val initializer = TSNodeObject(node.initializer) lazy val initToken = TSTokenObject(node.initializer) // for object literal, the initializer is a token. lazy val modifier = if (modifiers.isUndefined) Public else modifiers.foldLeft[TSAccessModifier](Public)( (m, t) => if (t.isPrivate) Private else if (t.isProtected) Protected else m) lazy val declarationList = TSNodeObject(node.declarationList) lazy val declarations = TSNodeArray(node.declarations) lazy val declaration = if (declarations.isUndefined) TSNodeObject(g.undefined) else declarations.get(0) lazy val locals = TSSymbolMap(node.locals) lazy val returnType = TSTypeObject(checker.getReturnTypeOfSignature(node)) lazy val `type` = TSNodeObject(node.selectDynamic("type")) lazy val symbolType = TSTypeObject(checker.getTypeOfSymbolAtLocation(node.symbol, node)) lazy val literal = TSTokenObject(node.literal) lazy val name = TSIdentifierObject(node.name) } object TSNodeObject { def apply(node: js.Dynamic)(implicit checker: TSTypeChecker) = new TSNodeObject(node) } class TSTokenObject(token: js.Dynamic)(implicit checker: TSTypeChecker) extends TSAny(token) { private lazy val kind = token.kind lazy val isPrivate = kind == TypeScript.syntaxKindPrivate lazy val isProtected = kind == TypeScript.syntaxKindProtected lazy val isStatic = kind == TypeScript.syntaxKindStatic lazy val typeNode = TSTypeObject(checker.getTypeFromTypeNode(token)) lazy val text = token.text.toString() lazy val isStringLiteral = TypeScript.isStringLiteral(token) } object TSTokenObject { def apply(token: js.Dynamic)(implicit checker: TSTypeChecker) = new TSTokenObject(token) } class TSTypeObject(obj: js.Dynamic)(implicit checker: TSTypeChecker) extends TSAny(obj) { private lazy val flags = obj.flags private lazy val objectFlags = if (IsUndefined(obj.objectFlags)) 0 else obj.objectFlags private lazy val baseType = TSTypeObject(checker.getBaseType(obj)) lazy val symbol = TSSymbolObject(obj.symbol) lazy val typeArguments = TSTypeArray(checker.getTypeArguments(obj)) lazy val intrinsicName: String = if (!IsUndefined(obj.intrinsicName)) obj.intrinsicName.toString else baseType.intrinsicName lazy val types = TSTypeArray(obj.types) lazy val properties = TSSymbolArray(checker.getPropertiesOfType(obj)) lazy val node = TSNodeObject(checker.typeToTypeNode(obj)) lazy val elementTypeOfArray = TSTypeObject(checker.getElementTypeOfArrayType(obj)) lazy val isTupleType = node.isTupleTypeNode lazy val isArrayType = node.isArrayTypeNode lazy val isEnumType = (flags & TypeScript.typeFlagsEnumLike) > 0 lazy val isUnionType = obj.isUnion() lazy val isIntersectionType = obj.isIntersection() lazy val isFunctionLike = node.isFunctionLike lazy val isAnonymous = objectFlags == TypeScript.objectFlagsAnonymous lazy val isTypeParameter = flags == TypeScript.typeFlagsTypeParameter lazy val isObject = flags == TypeScript.typeFlagsObject lazy val isTypeParameterSubstitution = isObject && typeArguments.length > 0 } object TSTypeObject { def apply(obj: js.Dynamic)(implicit checker: TSTypeChecker) = new TSTypeObject(obj) } class TSIdentifierObject(id: js.Dynamic) extends TSAny(id) { lazy val escapedText: String = id.escapedText.toString() } object TSIdentifierObject { def apply(id: js.Dynamic) = new TSIdentifierObject(id) } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/TSData.scala ================================================ package ts2mls import scala.scalajs.js import js.DynamicImplicits._ import js.JSConverters._ abstract class TSAny(v: js.Dynamic) { val isUndefined: Boolean = IsUndefined(v) } object IsUndefined { def apply(v: js.Dynamic): Boolean = js.isUndefined(v) } // array for information object in tsc abstract class TSArray[T <: TSAny](arr: js.Dynamic) extends TSAny(arr) { def get(index: Int): T = ??? lazy val length = arr.length def foldLeft[U](init: U, index: Int = 0)(implicit f: (U, T) => U): U = if (!isUndefined && index < length) foldLeft(f(init, get(index)), index + 1) else init def foldLeftIndexed[U](init: U, index: Int = 0)(implicit f: (U, T, Int) => U): U = if (!isUndefined && index < length) foldLeftIndexed(f(init, get(index), index), index + 1) else init def foreach(f: T => Unit, index: Int = 0): Unit = if (!isUndefined && index < length) { f(get(index)) foreach(f, index + 1) } } class TSNodeArray(arr: js.Dynamic)(implicit checker: TSTypeChecker) extends TSArray[TSNodeObject](arr) { override def get(index: Int) = TSNodeObject(arr.selectDynamic(index.toString)) } object TSNodeArray { def apply(arr: js.Dynamic)(implicit checker: TSTypeChecker) = new TSNodeArray(arr) } class TSSymbolArray(arr: js.Dynamic)(implicit checker: TSTypeChecker) extends TSArray[TSSymbolObject](arr) { override def get(index: Int) = TSSymbolObject(arr.selectDynamic(index.toString)) } object TSSymbolArray { def apply(arr: js.Dynamic)(implicit checker: TSTypeChecker) = new TSSymbolArray(arr) } class TSTokenArray(arr: js.Dynamic)(implicit checker: TSTypeChecker) extends TSArray[TSTokenObject](arr) { override def get(index: Int) = TSTokenObject(arr.selectDynamic(index.toString)) } object TSTokenArray { def apply(arr: js.Dynamic)(implicit checker: TSTypeChecker) = new TSTokenArray(arr) } class TSTypeArray(arr: js.Dynamic)(implicit checker: TSTypeChecker) extends TSArray[TSTypeObject](arr) { override def get(index: Int) = TSTypeObject(arr.selectDynamic(index.toString)) } object TSTypeArray { def apply(arr: js.Dynamic)(implicit checker: TSTypeChecker) = new TSTypeArray(arr) } class TSSymbolMap(map: js.Dynamic)(implicit checker: TSTypeChecker) extends TSAny(map) { def foreach(f: TSSymbolObject => Unit): Unit = if (!isUndefined) map.forEach({(s: js.Dynamic) => f(TSSymbolObject(s))}) } object TSSymbolMap { def apply(map: js.Dynamic)(implicit checker: TSTypeChecker) = new TSSymbolMap(map) } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/TSNamespace.scala ================================================ package ts2mls import scala.collection.mutable.{HashMap, ListBuffer} import types._ import mlscript.utils._ class TSNamespace(name: String, parent: Option[TSNamespace]) { private val subSpace = HashMap[String, TSNamespace]() private val members = HashMap[String, TSType]() // write down the order of members // easier to check the output one by one private val order = ListBuffer.empty[Either[String, String]] def derive(name: String): TSNamespace = if (subSpace.contains(name)) subSpace(name) // if the namespace has appeared in another file, just return it else { val sub = new TSNamespace(name, Some(this)) subSpace.put(name, sub) order += Left(name) sub } def put(name: String, tp: TSType): Unit = if (!members.contains(name)) { order += Right(name) members.put(name, tp) } else members.update(name, tp) def get(name: String): TSType = members.getOrElse(name, if (!parent.isEmpty) parent.get.get(name) else throw new Exception(s"member $name not found.")) def containsMember(name: String, searchParent: Boolean = true): Boolean = if (parent.isEmpty) members.contains(name) else (members.contains(name) || (searchParent && parent.get.containsMember(name))) def generate(writer: JSWriter, indent: String): Unit = order.toList.foreach((p) => p match { case Left(subName) => { writer.writeln(s"${indent}module $subName {") subSpace(subName).generate(writer, indent + " ") writer.writeln(s"$indent}") } case Right(name) => { val mem = members(name) mem match { case inter: TSIntersectionType => // overloaded functions writer.writeln(Converter.generateFunDeclaration(inter, name)(indent)) case f: TSFunctionType => writer.writeln(Converter.generateFunDeclaration(f, name)(indent)) case overload: TSIgnoredOverload => writer.writeln(Converter.generateFunDeclaration(overload, name)(indent)) case _: TSClassType => writer.writeln(Converter.convert(mem)(indent)) case TSInterfaceType(name, _, _, _) if (name =/= "") => writer.writeln(Converter.convert(mem)(indent)) case _: TSTypeAlias => writer.writeln(Converter.convert(mem)(indent)) case _ => writer.writeln(s"${indent}let $name: ${Converter.convert(mem)("")}") } } }) } object TSNamespace { def apply() = new TSNamespace("", None) // global namespace } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/TSProgram.scala ================================================ package ts2mls import scala.scalajs.js import js.DynamicImplicits._ import ts2mls.types._ class TSProgram(filenames: Seq[String]) { private val program = TypeScript.createProgram(filenames) // check if files exist filenames.foreach((fn) => if (!program.fileExists(fn)) throw new Exception(s"file ${fn} doesn't exist.")) val globalNamespace = TSNamespace() implicit val checker: TSTypeChecker = TSTypeChecker(program.getTypeChecker()) filenames.foreach(filename => TSSourceFile(program.getSourceFile(filename), globalNamespace)) def generate(writer: JSWriter): Unit = globalNamespace.generate(writer, "") } object TSProgram { def apply(filenames: Seq[String]) = new TSProgram(filenames) } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/TSSourceFile.scala ================================================ package ts2mls import scala.scalajs.js import js.DynamicImplicits._ import types._ import mlscript.utils._ object TSSourceFile { def apply(sf: js.Dynamic, global: TSNamespace)(implicit checker: TSTypeChecker) = TypeScript.forEachChild(sf, (node: js.Dynamic) => { val nodeObject = TSNodeObject(node) if (!nodeObject.isToken) { if (!nodeObject.symbol.isUndefined) // for functions/classes/interfaces addNodeIntoNamespace(nodeObject, nodeObject.symbol.escapedName)(global) else if (!nodeObject.declarationList.isUndefined) { // for variables val decNode = nodeObject.declarationList.declaration addNodeIntoNamespace(decNode, decNode.symbol.escapedName)(global) } } }) private def getSubstitutionArguments[T <: TSAny](args: TSArray[T]): List[TSType] = args.foldLeft(List[TSType]())((lst, arg) => arg match { case token: TSTokenObject => lst :+ getObjectType(token.typeNode) case tp: TSTypeObject => lst :+ getObjectType(tp) }) private def getObjectType(obj: TSTypeObject): TSType = if (obj.isEnumType) TSEnumType else if (obj.isFunctionLike) getFunctionType(obj.symbol.declaration) else if (obj.isTupleType) TSTupleType(getTupleElements(obj.typeArguments)) else if (obj.isUnionType) getStructuralType(obj.types, true) else if (obj.isIntersectionType) getStructuralType(obj.types, false) else if (obj.isArrayType) TSArrayType(getObjectType(obj.elementTypeOfArray)) else if (obj.isTypeParameterSubstitution) TSSubstitutionType(obj.symbol.escapedName, getSubstitutionArguments(obj.typeArguments)) else if (obj.isObject) if (obj.isAnonymous) TSInterfaceType("", getAnonymousPropertiesType(obj.properties), List(), List()) else TSReferenceType(obj.symbol.fullName) else if (obj.isTypeParameter) TSTypeParameter(obj.symbol.escapedName) else TSPrimitiveType(obj.intrinsicName) // the function `getMemberType` can't process function/tuple type alias correctly private def getTypeAlias(tn: TSNodeObject): TSType = if (tn.isFunctionLike) getFunctionType(tn) else if (tn.isTupleTypeNode) TSTupleType(getTupleElements(tn.typeNode.typeArguments)) else getObjectType(tn.typeNode) // parse string/numeric literal types. we need to add quotes if it is a string literal private def getLiteralType(tp: TSNodeObject) = TSLiteralType(tp.literal.text, tp.literal.isStringLiteral) // parse object literal types private def getObjectLiteralMembers(props: TSNodeArray) = props.foldLeft(Map[String, TSMemberType]())((mp, p) => { mp ++ Map(p.name.escapedText -> TSMemberType(TSLiteralType(p.initToken.text, p.initToken.isStringLiteral))) }) // get the type of variables in classes/named interfaces/anonymous interfaces private def getMemberType(node: TSNodeObject): TSType = { val res: TSType = if (node.isFunctionLike) getFunctionType(node) else if (node.`type`.isUndefined) getObjectType(node.typeAtLocation) else if (node.`type`.isLiteralTypeNode) getLiteralType(node.`type`) else getObjectType(node.`type`.typeNode) if (node.symbol.isOptionalMember) TSUnionType(res, TSPrimitiveType("undefined")) else res } private def getTypeParameters(node: TSNodeObject): List[TSTypeParameter] = node.typeParameters.foldLeft(List[TSTypeParameter]())((lst, tp) => if (tp.constraint.isUndefined) lst :+ TSTypeParameter(tp.symbol.escapedName, None) else lst :+ TSTypeParameter(tp.symbol.escapedName, Some(getObjectType(tp.constraint.typeNode))) ) private def getFunctionType(node: TSNodeObject): TSFunctionType = { val pList = node.parameters.foldLeft(List[TSParameterType]())((lst, p) => ( // in typescript, you can use `this` to explicitly specifies the callee // but it never appears in the final javascript file if (p.symbol.escapedName === "this") lst else if (p.isOptionalParameter) lst :+ TSParameterType(p.symbol.escapedName, TSUnionType(getObjectType(p.symbolType), TSPrimitiveType("undefined"))) else lst :+ TSParameterType(p.symbol.escapedName, getObjectType(p.symbolType))) ) TSFunctionType(pList, getObjectType(node.returnType), getTypeParameters(node)) } private def getStructuralType(types: TSTypeArray, isUnion: Boolean): TSType = types.foldLeft[Option[TSType]](None)((prev, cur) => prev match { case None => Some(getObjectType(cur)) case Some(p) => if (isUnion) Some(TSUnionType(p, getObjectType(cur))) else Some(TSIntersectionType(p, getObjectType(cur))) }).get private def getTupleElements(elements: TSTypeArray): List[TSType] = elements.foldLeft(List[TSType]())((lst, ele) => lst :+ getObjectType(ele)) private def getHeritageList(node: TSNodeObject): List[TSType] = node.heritageClauses.foldLeftIndexed(List[TSType]())((lst, h, index) => lst :+ getObjectType(h.types.get(index).typeNode) ) private def getClassMembersType(list: TSNodeArray, requireStatic: Boolean): Map[String, TSMemberType] = list.foldLeft(Map[String, TSMemberType]())((mp, p) => { val name = p.symbol.escapedName if (name =/= "__constructor" && p.isStatic == requireStatic) { val mem = if (!p.isStatic) getMemberType(p) else parseMembers(name, p.initializer, true) mem match { case func: TSFunctionType => { if (!mp.contains(name)) mp ++ Map(name -> TSMemberType(func, p.modifier)) else { // deal with functions overloading val old = mp(name) val res = old.base match { case f @ TSFunctionType(_, _, tv) => if (!tv.isEmpty || !func.typeVars.isEmpty) TSIgnoredOverload(func, name) else if (!p.isImplementationOfOverload) TSIntersectionType(f, func) else f case int: TSIntersectionType => if (!func.typeVars.isEmpty) TSIgnoredOverload(func, name) else if (!p.isImplementationOfOverload) TSIntersectionType(int, func) else int case TSIgnoredOverload(_, name) => TSIgnoredOverload(func, name) // the implementation is always after declarations case _ => old.base } mp.removed(name) ++ Map(name -> TSMemberType(res, p.modifier)) } } case _ => mp ++ Map(name -> TSMemberType(mem, p.modifier)) } } else mp }) private def getConstructorList(members: TSNodeArray): List[TSParameterType] = members.foldLeft(List[TSParameterType]())((lst, mem) => { val name = mem.symbol.escapedName if (name =/= "__constructor") lst else mem.parameters.foldLeft(List[TSParameterType]())((res, p) => res :+ TSParameterType(p.symbol.escapedName, getMemberType(p))) }) private def getInterfacePropertiesType(list: TSNodeArray): Map[String, TSMemberType] = list.foldLeft(Map[String, TSMemberType]())((mp, p) => mp ++ Map(p.symbol.escapedName -> TSMemberType(getMemberType(p)))) private def getAnonymousPropertiesType(list: TSSymbolArray): Map[String, TSMemberType] = list.foldLeft(Map[String, TSMemberType]())((mp, p) => mp ++ Map(p.escapedName -> TSMemberType(if (p.`type`.isUndefined) getMemberType(p.declaration) else getObjectType(p.`type`)))) private def parseMembers(name: String, node: TSNodeObject, isClass: Boolean): TSType = if (isClass) TSClassType(name, getClassMembersType(node.members, false), getClassMembersType(node.members, true), getTypeParameters(node), getHeritageList(node), getConstructorList(node.members)) else TSInterfaceType(name, getInterfacePropertiesType(node.members), getTypeParameters(node), getHeritageList(node)) private def parseNamespaceLocals(map: TSSymbolMap)(implicit ns: TSNamespace) = map.foreach((sym) => { val node = sym.declaration if (!node.isToken) addNodeIntoNamespace(node, sym.escapedName, if (node.isFunctionLike) Some(sym.declarations) else None) }) private def addFunctionIntoNamespace(fun: TSFunctionType, node: TSNodeObject, name: String)(implicit ns: TSNamespace) = if (!ns.containsMember(name, false)) ns.put(name, fun) else { val old = ns.get(name) val res = old match { case f @ TSFunctionType(_, _, tv) => if (!tv.isEmpty || !fun.typeVars.isEmpty) TSIgnoredOverload(fun, name) else if (!node.isImplementationOfOverload) TSIntersectionType(f, fun) else f case int: TSIntersectionType => if (!fun.typeVars.isEmpty) TSIgnoredOverload(fun, name) else if (!node.isImplementationOfOverload) TSIntersectionType(int, fun) else old case TSIgnoredOverload(_, name) => TSIgnoredOverload(fun, name) // the implementation is always after declarations case _ => old } ns.put(name, res) } // overload functions in a sub-namespace need to provide an overload array // because the namespace merely exports symbols rather than node objects themselves private def addNodeIntoNamespace(node: TSNodeObject, name: String, overload: Option[TSNodeArray] = None)(implicit ns: TSNamespace) = if (node.isFunctionLike) overload match { case None => addFunctionIntoNamespace(getFunctionType(node), node, name) case Some(decs) => { decs.foreach((d) => addFunctionIntoNamespace(getFunctionType(d), d, name) ) } } else if (node.isClassDeclaration) ns.put(name, parseMembers(name, node, true)) else if (node.isInterfaceDeclaration) ns.put(name, parseMembers(name, node, false)) else if (node.isTypeAliasDeclaration) ns.put(name, TSTypeAlias(name, getTypeAlias(node.`type`), getTypeParameters(node))) else if (node.isObjectLiteral) ns.put(name, TSInterfaceType("", getObjectLiteralMembers(node.initializer.properties), List(), List())) else if (node.isVariableDeclaration) ns.put(name, getMemberType(node)) else if (node.isNamespace) parseNamespace(node) private def parseNamespace(node: TSNodeObject)(implicit ns: TSNamespace): Unit = parseNamespaceLocals(node.locals)(ns.derive(node.symbol.escapedName)) } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/types/Converter.scala ================================================ package ts2mls.types import mlscript.utils._ object Converter { private val primitiveName = Map[String, String]( "boolean" -> "bool", "number" -> "number", "string" -> "string", "any" -> "anything", "unknown" -> "anything", "void" -> "unit", "null" -> "null", "undefined" -> "undefined", "never" -> "nothing", "object" -> "object", "true" -> "true", "false" -> "false" ) def generateFunDeclaration(tsType: TSType, name: String)(implicit indent: String = ""): String = tsType match { case TSFunctionType(params, res, typeVars) => { val pList = if (params.isEmpty) "" else params.map(p => s"${convert(p)("")}").reduceLeft((r, p) => s"$r, $p") val tpList = if (typeVars.isEmpty) "" else s"<${typeVars.map(p => convert(p)("")).reduceLeft((r, p) => s"$r, $p")}>" s"${indent}fun $name$tpList($pList): ${convert(res)("")}" } case overload @ TSIgnoredOverload(base, _) => s"${generateFunDeclaration(base, name)} ${overload.warning}" case inter: TSIntersectionType => s"${indent}fun ${name}: ${Converter.convert(inter)}" case _ => throw new AssertionError("non-function type is not allowed.") } def convert(tsType: TSType)(implicit indent: String = ""): String = tsType match { case TSPrimitiveType(typeName) => primitiveName(typeName) case TSReferenceType(name) => name case TSFunctionType(params, res, _) => if (params.length == 0) s"${primitiveName("void")} => ${convert(res)}" else params.foldRight(convert(res))((p, f) => s"(${convert(p.tp)}) => $f") case TSUnionType(lhs, rhs) => s"(${convert(lhs)}) | (${convert(rhs)})" case TSIntersectionType(lhs, rhs) => s"(${convert(lhs)}) & (${convert(rhs)})" case TSTypeParameter(name, _) => name // constraints should be translated where the type parameters were created rather than be used case TSTupleType(lst) => s"[${lst.map(convert).mkString(", ")}]" case TSArrayType(element) => s"MutArray<${convert(element)}>" case TSEnumType => "int" case TSMemberType(base, _) => convert(base) // TODO: support private/protected members case TSInterfaceType(name, members, typeVars, parents) => convertRecord(s"trait $name", members, typeVars, parents, Map(), List())(indent) case TSClassType(name, members, statics, typeVars, parents, cons) => convertRecord(s"class $name", members, typeVars, parents, statics, cons)(indent) case TSSubstitutionType(base, applied) => s"${base}<${applied.map((app) => convert(app)).reduceLeft((res, s) => s"$res, $s")}>" case overload @ TSIgnoredOverload(base, _) => s"${convert(base)} ${overload.warning}" case TSParameterType(name, tp) => s"${name}: ${convert(tp)}" case TSTypeAlias(name, ori, tp) => if (tp.isEmpty) s"${indent}type $name = ${convert(ori)}" else s"${indent}type $name<${tp.map(t => convert(t)).reduceLeft((s, t) => s"$s, $t")}> = ${convert(ori)}" case TSLiteralType(value, isString) => if (isString) s"\"$value\"" else value } private def convertRecord(typeName: String, members: Map[String, TSMemberType], typeVars: List[TSTypeParameter], parents: List[TSType], statics: Map[String, TSMemberType], constructorList: List[TSType])(implicit indent: String) = { val allRecs = members.toList.map((m) => m._2.modifier match { case Public => if (typeName === "trait ") s"${m._1}: ${convert(m._2)}," else m._2.base match { case _: TSFunctionType => s"${generateFunDeclaration(m._2.base, m._1)(indent + " ")}\n" case _: TSIgnoredOverload => s"${generateFunDeclaration(m._2.base, m._1)(indent + " ")}\n" case _ => s"${indent} let ${m._1}: ${convert(m._2)}\n" } case _ => "" // TODO: deal with private/protected members }) ::: statics.toList.map((s) => s._2.modifier match { case Public => s._2.base match { case _: TSClassType => convert(s._2)(indent + " ") + "\n" case _ => "" // TODO: deal with other static type } case _ => "" // TODO: deal with private/protected static members }) val body = { // members without independent type parameters, translate them directly val lst = allRecs.filter((s) => !s.isEmpty()) if (lst.isEmpty) "{}" else if (typeName === "trait ") s"{${lst.reduceLeft((bd, m) => s"$bd$m")}}" else s"{\n${lst.reduceLeft((bd, m) => s"$bd$m")}$indent}" } if (typeName === "trait ") body // anonymous interfaces else { // named interfaces and classes val constructor = if (constructorList.isEmpty) "()" else s"(${constructorList.map(p => s"${convert(p)("")}").reduceLeft((res, p) => s"$res, $p")})" val inheritance = if (parents.isEmpty) constructor else parents.foldLeft(s"$constructor: ")((b, p) => s"$b${convert(p)}, ").dropRight(2) if (typeVars.isEmpty) s"${indent}$typeName$inheritance $body" else s"${indent}$typeName<${typeVars.map((tv) => tv.name).reduceLeft((p, s) => s"$p, $s")}>$inheritance $body" // TODO: add constraints } } } ================================================ FILE: ts2mls/js/src/main/scala/ts2mls/types/TSType.scala ================================================ package ts2mls.types sealed abstract class TSAccessModifier case object Public extends TSAccessModifier case object Private extends TSAccessModifier case object Protected extends TSAccessModifier sealed abstract class TSType case class TSParameterType(name: String, val tp: TSType) extends TSType // record both parameter's name and parameter's type case class TSMemberType(val base: TSType, val modifier: TSAccessModifier = Public) extends TSType case class TSTypeParameter(val name: String, constraint: Option[TSType] = None) extends TSType case class TSPrimitiveType(typeName: String) extends TSType case class TSReferenceType(name: String) extends TSType case object TSEnumType extends TSType case class TSTupleType(types: List[TSType]) extends TSType case class TSFunctionType(params: List[TSParameterType], res: TSType, val typeVars: List[TSTypeParameter]) extends TSType case class TSArrayType(eleType: TSType) extends TSType case class TSSubstitutionType(base: String, applied: List[TSType]) extends TSType case class TSClassType( name: String, members: Map[String, TSMemberType], statics: Map[String, TSMemberType], typeVars: List[TSTypeParameter], parents: List[TSType], constructor: List[TSParameterType] ) extends TSType case class TSInterfaceType( name: String, members: Map[String, TSMemberType], typeVars: List[TSTypeParameter], parents: List[TSType], ) extends TSType sealed abstract class TSStructuralType(lhs: TSType, rhs: TSType, notion: String) extends TSType case class TSUnionType(lhs: TSType, rhs: TSType) extends TSStructuralType(lhs, rhs, "|") case class TSIntersectionType(lhs: TSType, rhs: TSType) extends TSStructuralType(lhs, rhs, "&") // ts2mls doesn't support overloading functions with type parameters // TSIgnoredOverload is used to store these functions and raise a warning later // only the most general overloading form would be stored case class TSIgnoredOverload(base: TSFunctionType, name: String) extends TSType { val warning = s"/* warning: the overload of function $name is not supported yet. */" } case class TSTypeAlias(name: String, original: TSType, tp: List[TSType]) extends TSType case class TSLiteralType(value: String, isString: Boolean) extends TSType ================================================ FILE: ts2mls/js/src/test/diff/Array.d.mls ================================================ :NewDefs :ParseOnly fun first(x: MutArray): string fun getZero3(): MutArray fun first2(fs: MutArray<(number) => number>): (number) => number fun doEs(e: MutArray): MutArray class C() {} trait I() { let i: number } fun doCs(c: MutArray): MutArray fun doIs(i: MutArray): MutArray fun inter(x: MutArray<(U) & (T)>): MutArray<(U) & (T)> fun clean(x: MutArray<[string, number]>): MutArray<[string, number]> fun translate(x: MutArray): MutArray fun uu(x: MutArray<((number) | (false)) | (true)>): MutArray<((number) | (false)) | (true)> class Temp() { let x: T } fun ta(ts: MutArray>): MutArray> fun tat(ts: MutArray>): MutArray> //│ |#fun| |first|(|x|#:| |MutArray|‹|string|›|)|#:| |string|↵|#fun| |getZero3|(||)|#:| |MutArray|‹|number|›|↵|#fun| |first2|(|fs|#:| |MutArray|‹|(|number|)| |#=>| |number|›|)|#:| |(|number|)| |#=>| |number|↵|#fun| |doEs|(|e|#:| |MutArray|‹|int|›|)|#:| |MutArray|‹|int|›|↵|#class| |C|(||)| |{||}|↵|#trait| |I|(||)| |{|→|#let| |i|#:| |number|←|↵|}|↵|#fun| |doCs|(|c|#:| |MutArray|‹|C|›|)|#:| |MutArray|‹|C|›|↵|#fun| |doIs|(|i|#:| |MutArray|‹|I|›|)|#:| |MutArray|‹|I|›|↵|#fun| |inter|‹|U|,| |T|›|(|x|#:| |MutArray|‹|(|U|)| |&| |(|T|)|›|)|#:| |MutArray|‹|(|U|)| |&| |(|T|)|›|↵|#fun| |clean|(|x|#:| |MutArray|‹|[|string|,| |number|]|›|)|#:| |MutArray|‹|[|string|,| |number|]|›|↵|#fun| |translate|‹|T|,| |U|›|(|x|#:| |MutArray|‹|T|›|)|#:| |MutArray|‹|U|›|↵|#fun| |uu|(|x|#:| |MutArray|‹|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|›|)|#:| |MutArray|‹|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|›|↵|#class| |Temp|‹|T|›|(||)| |{|→|#let| |x|#:| |T|←|↵|}|↵|#fun| |ta|(|ts|#:| |MutArray|‹|Temp|‹|(|false|)| ||| |(|true|)|›|›|)|#:| |MutArray|‹|Temp|‹|(|false|)| ||| |(|true|)|›|›|↵|#fun| |tat|‹|T|›|(|ts|#:| |MutArray|‹|Temp|‹|T|›|›|)|#:| |MutArray|‹|Temp|‹|T|›|›| //│ Parsed: {fun first: (x: MutArray[string]) -> string; fun getZero3: () -> MutArray[number]; fun first2: (fs: MutArray[number -> number]) -> number -> number; fun doEs: (e: MutArray[int]) -> MutArray[int]; class C() {}; trait I() {let i: number}; fun doCs: (c: MutArray[C]) -> MutArray[C]; fun doIs: (i: MutArray[I]) -> MutArray[I]; fun inter: (x: MutArray[U & T]) -> MutArray[U & T]; fun clean: (x: MutArray[[string, number]]) -> MutArray[[string, number]]; fun translate: (x: MutArray[T]) -> MutArray[U]; fun uu: (x: MutArray[number | false | true]) -> MutArray[number | false | true]; class Temp‹T›() {let x: T}; fun ta: (ts: MutArray[Temp[bool]]) -> MutArray[Temp[bool]]; fun tat: (ts: MutArray[Temp[T]]) -> MutArray[Temp[T]]} //│ ================================================ FILE: ts2mls/js/src/test/diff/BasicFunctions.d.mls ================================================ :NewDefs :ParseOnly fun hello(): unit fun add(x: number, y: number): number fun sub(x: number, y: number): number fun foo(): number fun id(x: anything): anything fun odd(x: number): (false) | (true) fun isnull(x: anything): (false) | (true) fun bar(): anything fun nu(n: null): null fun un(n: undefined): undefined fun fail(): nothing fun create(): object fun pa(x: number): number fun wtf(x: anything): unit class Foooooo() { let ooooooo: number } fun inn(f: Foooooo): unit fun out1(): Foooooo trait Barrrrrrrrr() { let rrrrrrr: number } fun inn2(b: Barrrrrrrrr): unit fun out2(): Barrrrrrrrr //│ |#fun| |hello|(||)|#:| |unit|↵|#fun| |add|(|x|#:| |number|,| |y|#:| |number|)|#:| |number|↵|#fun| |sub|(|x|#:| |number|,| |y|#:| |number|)|#:| |number|↵|#fun| |foo|(||)|#:| |number|↵|#fun| |id|(|x|#:| |anything|)|#:| |anything|↵|#fun| |odd|(|x|#:| |number|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |isnull|(|x|#:| |anything|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |bar|(||)|#:| |anything|↵|#fun| |nu|(|n|#:| |#null|)|#:| |#null|↵|#fun| |un|(|n|#:| |#undefined|)|#:| |#undefined|↵|#fun| |fail|(||)|#:| |nothing|↵|#fun| |create|(||)|#:| |object|↵|#fun| |pa|(|x|#:| |number|)|#:| |number|↵|#fun| |wtf|(|x|#:| |anything|)|#:| |unit|↵|#class| |Foooooo|(||)| |{|→|#let| |ooooooo|#:| |number|←|↵|}|↵|#fun| |inn|(|f|#:| |Foooooo|)|#:| |unit|↵|#fun| |out1|(||)|#:| |Foooooo|↵|#trait| |Barrrrrrrrr|(||)| |{|→|#let| |rrrrrrr|#:| |number|←|↵|}|↵|#fun| |inn2|(|b|#:| |Barrrrrrrrr|)|#:| |unit|↵|#fun| |out2|(||)|#:| |Barrrrrrrrr| //│ Parsed: {fun hello: () -> unit; fun add: (x: number, y: number) -> number; fun sub: (x: number, y: number) -> number; fun foo: () -> number; fun id: (x: anything) -> anything; fun odd: (x: number) -> bool; fun isnull: (x: anything) -> bool; fun bar: () -> anything; fun nu: (n: null) -> null; fun un: (n: ()) -> (); fun fail: () -> nothing; fun create: () -> object; fun pa: (x: number) -> number; fun wtf: (x: anything) -> unit; class Foooooo() {let ooooooo: number}; fun inn: (f: Foooooo) -> unit; fun out1: () -> Foooooo; trait Barrrrrrrrr() {let rrrrrrr: number}; fun inn2: (b: Barrrrrrrrr) -> unit; fun out2: () -> Barrrrrrrrr} //│ ================================================ FILE: ts2mls/js/src/test/diff/ClassMember.d.mls ================================================ :NewDefs :ParseOnly class Student(s: string, age: number) { let name: string fun isFriend(other: Student): (false) | (true) fun addScore(sub: string, score: number): unit fun getID(): number } class Foo() { fun bar(x: T): unit } class EZ() { fun inc(x: number): number } class Outer() { class Inner() { let a: number } } class TTT() { fun ttt(x: T): T fun ttt2(x: T): T } //│ |#class| |Student|(|s|#:| |string|,| |age|#:| |number|)| |{|→|#let| |name|#:| |string|↵|#fun| |isFriend|(|other|#:| |Student|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |addScore|(|sub|#:| |string|,| |score|#:| |number|)|#:| |unit|↵|#fun| |getID|(||)|#:| |number|←|↵|}|↵|#class| |Foo|‹|T|›|(||)| |{|→|#fun| |bar|(|x|#:| |T|)|#:| |unit|←|↵|}|↵|#class| |EZ|(||)| |{|→|#fun| |inc|(|x|#:| |number|)|#:| |number|←|↵|}|↵|#class| |Outer|(||)| |{|→|#class| |Inner|(||)| |{|→|#let| |a|#:| |number|←|↵|}|←|↵|}|↵|#class| |TTT|‹|T|›|(||)| |{|→|#fun| |ttt|(|x|#:| |T|)|#:| |T|↵|#fun| |ttt2|(|x|#:| |T|)|#:| |T|←|↵|}| //│ Parsed: {class Student(s: string, age: number,) {let name: string; fun isFriend: (other: Student) -> bool; fun addScore: (sub: string, score: number) -> unit; fun getID: () -> number}; class Foo‹T›() {fun bar: (x: T) -> unit}; class EZ() {fun inc: (x: number) -> number}; class Outer() {class Inner() {let a: number}}; class TTT‹T›() {fun ttt: (x: T) -> T; fun ttt2: (x: T) -> T}} //│ ================================================ FILE: ts2mls/js/src/test/diff/Dec.d.mls ================================================ :NewDefs :ParseOnly fun getName(id: (string) | (number)): string fun render(callback: (unit => unit) | (undefined)): string trait Get() { fun __call(id: string): string } class Person(name: string, age: number) { fun getName(id: number): string } module OOO { } //│ |#fun| |getName|(|id|#:| |(|string|)| ||| |(|number|)|)|#:| |string|↵|#fun| |render|(|callback|#:| |(|unit| |#=>| |unit|)| ||| |(|#undefined|)|)|#:| |string|↵|#trait| |Get|(||)| |{|→|#fun| |__call|(|id|#:| |string|)|#:| |string|←|↵|}|↵|#class| |Person|(|name|#:| |string|,| |age|#:| |number|)| |{|→|#fun| |getName|(|id|#:| |number|)|#:| |string|←|↵|}|↵|#module| |OOO| |{|↵|}| //│ Parsed: {fun getName: (id: string | number) -> string; fun render: (callback: unit -> unit | ()) -> string; trait Get() {fun __call: (id: string) -> string}; class Person(name: string, age: number,) {fun getName: (id: number) -> string}; module OOO {}} //│ ================================================ FILE: ts2mls/js/src/test/diff/Enum.d.mls ================================================ :NewDefs :ParseOnly fun pass(c: int): (false) | (true) fun stop(): int fun g(x: int): int //│ |#fun| |pass|(|c|#:| |int|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |stop|(||)|#:| |int|↵|#fun| |g|(|x|#:| |int|)|#:| |int| //│ Parsed: {fun pass: (c: int) -> bool; fun stop: () -> int; fun g: (x: int) -> int} //│ ================================================ FILE: ts2mls/js/src/test/diff/Heritage.d.mls ================================================ :NewDefs :ParseOnly class A() { fun foo(): unit } class B(): A {} class C() { fun sett(x: T): unit fun get(): T } class D(): C {} trait Wu() { let x: (false) | (true) } class WuWu(): Wu { let y: (false) | (true) } trait WuWuWu(): WuWu { let z: (false) | (true) } trait Never(): WuWuWu { fun w(): nothing } class VG() { let x: T } class Home(): VG { let y: T } trait O() { fun xx(x: I): I } class OR(): O { fun xx(x: R): R } module Five { class ROTK() { let wu: string } class Y(): Five.ROTK {} } class Y(): Five.ROTK {} //│ |#class| |A|(||)| |{|→|#fun| |foo|(||)|#:| |unit|←|↵|}|↵|#class| |B|(||)|#:| |A| |{||}|↵|#class| |C|‹|T|›|(||)| |{|→|#fun| |sett|(|x|#:| |T|)|#:| |unit|↵|#fun| |get|(||)|#:| |T|←|↵|}|↵|#class| |D|(||)|#:| |C|‹|number|›| |{||}|↵|#trait| |Wu|(||)| |{|→|#let| |x|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#class| |WuWu|(||)|#:| |Wu| |{|→|#let| |y|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |WuWuWu|(||)|#:| |WuWu| |{|→|#let| |z|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |Never|(||)|#:| |WuWuWu| |{|→|#fun| |w|(||)|#:| |nothing|←|↵|}|↵|#class| |VG|‹|T|›|(||)| |{|→|#let| |x|#:| |T|←|↵|}|↵|#class| |Home|‹|T|›|(||)|#:| |VG|‹|string|›| |{|→|#let| |y|#:| |T|←|↵|}|↵|#trait| |O|‹|I|›|(||)| |{|→|#fun| |xx|(|x|#:| |I|)|#:| |I|←|↵|}|↵|#class| |OR|‹|R|›|(||)|#:| |O|‹|R|›| |{|→|#fun| |xx|(|x|#:| |R|)|#:| |R|←|↵|}|↵|#module| |Five| |{|→|#class| |ROTK|(||)| |{|→|#let| |wu|#:| |string|←|↵|}|↵|#class| |Y|(||)|#:| |Five|.ROTK| |{||}|←|↵|}|↵|#class| |Y|(||)|#:| |Five|.ROTK| |{||}| //│ Parsed: {class A() {fun foo: () -> unit}; class B(): A {}; class C‹T›() {fun sett: (x: T) -> unit; fun get: () -> T}; class D(): C[number] {}; trait Wu() {let x: bool}; class WuWu(): Wu {let y: bool}; trait WuWuWu(): WuWu {let z: bool}; trait Never(): WuWuWu {fun w: () -> nothing}; class VG‹T›() {let x: T}; class Home‹T›(): VG[string] {let y: T}; trait O‹I›() {fun xx: (x: I) -> I}; class OR‹R›(): O[R] {fun xx: (x: R) -> R}; module Five {class ROTK() {let wu: string}; class Y(): Five.ROTK {}}; class Y(): Five.ROTK {}} //│ ================================================ FILE: ts2mls/js/src/test/diff/HighOrderFunc.d.mls ================================================ :NewDefs :ParseOnly fun h1(inc: (number) => number, num: number): number fun h2(hint: string): unit => string fun h3(f: (number) => number, g: (number) => number): (number) => number //│ |#fun| |h1|(|inc|#:| |(|number|)| |#=>| |number|,| |num|#:| |number|)|#:| |number|↵|#fun| |h2|(|hint|#:| |string|)|#:| |unit| |#=>| |string|↵|#fun| |h3|(|f|#:| |(|number|)| |#=>| |number|,| |g|#:| |(|number|)| |#=>| |number|)|#:| |(|number|)| |#=>| |number| //│ Parsed: {fun h1: (inc: number -> number, num: number) -> number; fun h2: (hint: string) -> unit -> string; fun h3: (f: number -> number, g: number -> number) -> number -> number} //│ ================================================ FILE: ts2mls/js/src/test/diff/InterfaceMember.d.mls ================================================ :NewDefs :ParseOnly trait IFoo() { let a: string fun b(x: number): number fun c(): (false) | (true) fun d(x: string): unit } trait II() { fun test(x: T): number } fun create(): {v: number,} fun get(x: {t: string,}): string trait IEvent() { fun callback(): (number) => unit } trait SearchFunc() { fun __call(source: string, subString: string): (false) | (true) } trait StringArray() { fun __index(index: number): string } trait Counter() { fun __call(start: number): string let interval: number fun reset(): unit } trait Simple() { let a: number fun b(x: (false) | (true)): string } trait Simple2() { let abc: T } trait Next(): Simple {} trait TTT() { fun ttt(x: T): T } //│ |#trait| |IFoo|(||)| |{|→|#let| |a|#:| |string|↵|#fun| |b|(|x|#:| |number|)|#:| |number|↵|#fun| |c|(||)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |d|(|x|#:| |string|)|#:| |unit|←|↵|}|↵|#trait| |II|‹|T|›|(||)| |{|→|#fun| |test|(|x|#:| |T|)|#:| |number|←|↵|}|↵|#fun| |create|(||)|#:| |{|v|#:| |number|,|}|↵|#fun| |get|(|x|#:| |{|t|#:| |string|,|}|)|#:| |string|↵|#trait| |IEvent|(||)| |{|→|#fun| |callback|(||)|#:| |(|number|)| |#=>| |unit|←|↵|}|↵|#trait| |SearchFunc|(||)| |{|→|#fun| |__call|(|source|#:| |string|,| |subString|#:| |string|)|#:| |(|false|)| ||| |(|true|)|←|↵|}|↵|#trait| |StringArray|(||)| |{|→|#fun| |__index|(|index|#:| |number|)|#:| |string|←|↵|}|↵|#trait| |Counter|(||)| |{|→|#fun| |__call|(|start|#:| |number|)|#:| |string|↵|#let| |interval|#:| |number|↵|#fun| |reset|(||)|#:| |unit|←|↵|}|↵|#trait| |Simple|(||)| |{|→|#let| |a|#:| |number|↵|#fun| |b|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |string|←|↵|}|↵|#trait| |Simple2|‹|T|›|(||)| |{|→|#let| |abc|#:| |T|←|↵|}|↵|#trait| |Next|(||)|#:| |Simple| |{||}|↵|#trait| |TTT|‹|T|›|(||)| |{|→|#fun| |ttt|(|x|#:| |T|)|#:| |T|←|↵|}| //│ Parsed: {trait IFoo() {let a: string; fun b: (x: number) -> number; fun c: () -> bool; fun d: (x: string) -> unit}; trait II‹T›() {fun test: (x: T) -> number}; fun create: () -> {v: number}; fun get: (x: {t: string}) -> string; trait IEvent() {fun callback: () -> number -> unit}; trait SearchFunc() {fun __call: (source: string, subString: string) -> bool}; trait StringArray() {fun __index: (index: number) -> string}; trait Counter() {fun __call: (start: number) -> string; let interval: number; fun reset: () -> unit}; trait Simple() {let a: number; fun b: (x: bool) -> string}; trait Simple2‹T›() {let abc: T}; trait Next(): Simple {}; trait TTT‹T›() {fun ttt: (x: T) -> T}} //│ ================================================ FILE: ts2mls/js/src/test/diff/Intersection.d.mls ================================================ :NewDefs :ParseOnly fun extend(first: T, second: U): (T) & (U) fun foo(x: (T) & (U)): unit fun over(f: ((number) => string) & ((object) => string)): string trait IA() { let x: number } trait IB() { let y: number } fun iii(x: (IA) & (IB)): (IA) & (IB) fun uu(x: ((((U) & (T)) | ((U) & (P))) | ((V) & (T))) | ((V) & (P))): ((((U) & (T)) | ((U) & (P))) | ((V) & (T))) | ((V) & (P)) fun iiii(x: ((U) & (T)) & (V)): ((U) & (T)) & (V) fun arr(a: (MutArray) & (MutArray)): (MutArray) & (MutArray) fun tt(x: ([U, T]) & ([V, V])): ([U, T]) & ([V, V]) class A() {} class B() {} fun inter(c: (A) & (B)): (A) & (B) //│ |#fun| |extend|‹|T|,| |U|›|(|first|#:| |T|,| |second|#:| |U|)|#:| |(|T|)| |&| |(|U|)|↵|#fun| |foo|‹|T|,| |U|›|(|x|#:| |(|T|)| |&| |(|U|)|)|#:| |unit|↵|#fun| |over|(|f|#:| |(|(|number|)| |#=>| |string|)| |&| |(|(|object|)| |#=>| |string|)|)|#:| |string|↵|#trait| |IA|(||)| |{|→|#let| |x|#:| |number|←|↵|}|↵|#trait| |IB|(||)| |{|→|#let| |y|#:| |number|←|↵|}|↵|#fun| |iii|(|x|#:| |(|IA|)| |&| |(|IB|)|)|#:| |(|IA|)| |&| |(|IB|)|↵|#fun| |uu|‹|U|,| |V|,| |T|,| |P|›|(|x|#:| |(|(|(|(|U|)| |&| |(|T|)|)| ||| |(|(|U|)| |&| |(|P|)|)|)| ||| |(|(|V|)| |&| |(|T|)|)|)| ||| |(|(|V|)| |&| |(|P|)|)|)|#:| |(|(|(|(|U|)| |&| |(|T|)|)| ||| |(|(|U|)| |&| |(|P|)|)|)| ||| |(|(|V|)| |&| |(|T|)|)|)| ||| |(|(|V|)| |&| |(|P|)|)|↵|#fun| |iiii|‹|U|,| |T|,| |V|›|(|x|#:| |(|(|U|)| |&| |(|T|)|)| |&| |(|V|)|)|#:| |(|(|U|)| |&| |(|T|)|)| |&| |(|V|)|↵|#fun| |arr|‹|U|,| |T|›|(|a|#:| |(|MutArray|‹|U|›|)| |&| |(|MutArray|‹|T|›|)|)|#:| |(|MutArray|‹|U|›|)| |&| |(|MutArray|‹|T|›|)|↵|#fun| |tt|‹|U|,| |T|,| |V|›|(|x|#:| |(|[|U|,| |T|]|)| |&| |(|[|V|,| |V|]|)|)|#:| |(|[|U|,| |T|]|)| |&| |(|[|V|,| |V|]|)|↵|#class| |A|(||)| |{||}|↵|#class| |B|(||)| |{||}|↵|#fun| |inter|(|c|#:| |(|A|)| |&| |(|B|)|)|#:| |(|A|)| |&| |(|B|)| //│ Parsed: {fun extend: (first: T, second: U) -> (T & U); fun foo: (x: T & U) -> unit; fun over: (f: number -> string & object -> string) -> string; trait IA() {let x: number}; trait IB() {let y: number}; fun iii: (x: IA & IB) -> (IA & IB); fun uu: (x: U & T | U & P | V & T | V & P) -> (U & T | U & P | V & T | V & P); fun iiii: (x: U & T & V) -> (U & T & V); fun arr: (a: MutArray[U] & MutArray[T]) -> (MutArray[U] & MutArray[T]); fun tt: (x: [U, T] & [V, V]) -> ([U, T] & [V, V]); class A() {}; class B() {}; fun inter: (c: A & B) -> (A & B)} //│ ================================================ FILE: ts2mls/js/src/test/diff/Literal.d.mls ================================================ :NewDefs :ParseOnly let a: {a: "A",b: "B",} let num: {y: 114,} fun foo(x: {xx: "X",}): {yy: "Y",} //│ |#let| |a|#:| |{|a|#:| |"A"|,|b|#:| |"B"|,|}|↵|#let| |num|#:| |{|y|#:| |114|,|}|↵|#fun| |foo|(|x|#:| |{|xx|#:| |"X"|,|}|)|#:| |{|yy|#:| |"Y"|,|}| //│ Parsed: {let a: {a: "A", b: "B"}; let num: {y: 114}; fun foo: (x: {xx: "X"}) -> {yy: "Y"}} //│ ================================================ FILE: ts2mls/js/src/test/diff/MultiFiles.d.mls ================================================ :NewDefs :ParseOnly fun multi1(x: number): number fun multi3(): unit class Foo(): Base {} trait AnotherBase() { let y: string } module N { fun f(): unit fun g(): unit fun h(): unit } fun multi2(x: string): string fun multi4(): unit trait Base() { let a: number } class AnotherFoo(): AnotherBase {} fun multi5(): unit //│ |#fun| |multi1|(|x|#:| |number|)|#:| |number|↵|#fun| |multi3|(||)|#:| |unit|↵|#class| |Foo|(||)|#:| |Base| |{||}|↵|#trait| |AnotherBase|(||)| |{|→|#let| |y|#:| |string|←|↵|}|↵|#module| |N| |{|→|#fun| |f|(||)|#:| |unit|↵|#fun| |g|(||)|#:| |unit|↵|#fun| |h|(||)|#:| |unit|←|↵|}|↵|#fun| |multi2|(|x|#:| |string|)|#:| |string|↵|#fun| |multi4|(||)|#:| |unit|↵|#trait| |Base|(||)| |{|→|#let| |a|#:| |number|←|↵|}|↵|#class| |AnotherFoo|(||)|#:| |AnotherBase| |{||}|↵|#fun| |multi5|(||)|#:| |unit| //│ Parsed: {fun multi1: (x: number) -> number; fun multi3: () -> unit; class Foo(): Base {}; trait AnotherBase() {let y: string}; module N {fun f: () -> unit; fun g: () -> unit; fun h: () -> unit}; fun multi2: (x: string) -> string; fun multi4: () -> unit; trait Base() {let a: number}; class AnotherFoo(): AnotherBase {}; fun multi5: () -> unit} //│ ================================================ FILE: ts2mls/js/src/test/diff/Namespace.d.mls ================================================ :NewDefs :ParseOnly module N1 { fun f(x: anything): number fun ff(y: anything): number class C() { fun f(): unit } trait I() { fun f(): number } module N2 { fun fff(x: (false) | (true)): number fun gg(c: N1.C): N1.C class BBB(): N1.C {} } } module AA { fun f(x: anything): string class C() { fun f(): unit } trait I() { fun f(): number } module N2 { } } fun f1(x: N1.C): N1.C fun f2(x: AA.C): AA.C //│ |#module| |N1| |{|→|#fun| |f|(|x|#:| |anything|)|#:| |number|↵|#fun| |ff|(|y|#:| |anything|)|#:| |number|↵|#class| |C|(||)| |{|→|#fun| |f|(||)|#:| |unit|←|↵|}|↵|#trait| |I|(||)| |{|→|#fun| |f|(||)|#:| |number|←|↵|}|↵|#module| |N2| |{|→|#fun| |fff|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |number|↵|#fun| |gg|(|c|#:| |N1|.C|)|#:| |N1|.C|↵|#class| |BBB|(||)|#:| |N1|.C| |{||}|←|↵|}|←|↵|}|↵|#module| |AA| |{|→|#fun| |f|(|x|#:| |anything|)|#:| |string|↵|#class| |C|(||)| |{|→|#fun| |f|(||)|#:| |unit|←|↵|}|↵|#trait| |I|(||)| |{|→|#fun| |f|(||)|#:| |number|←|↵|}|↵|#module| |N2| |{|↵|}|←|↵|}|↵|#fun| |f1|(|x|#:| |N1|.C|)|#:| |N1|.C|↵|#fun| |f2|(|x|#:| |AA|.C|)|#:| |AA|.C| //│ Parsed: {module N1 {fun f: (x: anything) -> number; fun ff: (y: anything) -> number; class C() {fun f: () -> unit}; trait I() {fun f: () -> number}; module N2 {fun fff: (x: bool) -> number; fun gg: (c: N1.C) -> N1.C; class BBB(): N1.C {}}}; module AA {fun f: (x: anything) -> string; class C() {fun f: () -> unit}; trait I() {fun f: () -> number}; module N2 {}}; fun f1: (x: N1.C) -> N1.C; fun f2: (x: AA.C) -> AA.C} //│ ================================================ FILE: ts2mls/js/src/test/diff/Optional.d.mls ================================================ :NewDefs :ParseOnly fun buildName(firstName: string, lastName: (string) | (undefined)): string fun buildName2(firstName: string, lastName: (string) | (undefined)): string fun buildName3(firstName: string, lastName: MutArray): string fun buildName4(firstName: string, lastName: MutArray): string trait SquareConfig() { let color: (string) | (undefined) let width: (number) | (undefined) } fun did(x: number, f: ((number) => number) | (undefined)): number fun getOrElse(arr: (MutArray) | (undefined)): object class ABC() {} fun testABC(abc: (ABC) | (undefined)): unit fun testSquareConfig(conf: (SquareConfig) | (undefined)): unit fun err(msg: ([number, string]) | (undefined)): unit fun toStr(x: (((number) | (false)) | (true)) | (undefined)): string fun boo(x: ((T) & (U)) | (undefined)): unit class B() { let b: T } fun boom(b: (B) | (undefined)): anything //│ |#fun| |buildName|(|firstName|#:| |string|,| |lastName|#:| |(|string|)| ||| |(|#undefined|)|)|#:| |string|↵|#fun| |buildName2|(|firstName|#:| |string|,| |lastName|#:| |(|string|)| ||| |(|#undefined|)|)|#:| |string|↵|#fun| |buildName3|(|firstName|#:| |string|,| |lastName|#:| |MutArray|‹|string|›|)|#:| |string|↵|#fun| |buildName4|(|firstName|#:| |string|,| |lastName|#:| |MutArray|‹|anything|›|)|#:| |string|↵|#trait| |SquareConfig|(||)| |{|→|#let| |color|#:| |(|string|)| ||| |(|#undefined|)|↵|#let| |width|#:| |(|number|)| ||| |(|#undefined|)|←|↵|}|↵|#fun| |did|(|x|#:| |number|,| |f|#:| |(|(|number|)| |#=>| |number|)| ||| |(|#undefined|)|)|#:| |number|↵|#fun| |getOrElse|(|arr|#:| |(|MutArray|‹|object|›|)| ||| |(|#undefined|)|)|#:| |object|↵|#class| |ABC|(||)| |{||}|↵|#fun| |testABC|(|abc|#:| |(|ABC|)| ||| |(|#undefined|)|)|#:| |unit|↵|#fun| |testSquareConfig|(|conf|#:| |(|SquareConfig|)| ||| |(|#undefined|)|)|#:| |unit|↵|#fun| |err|(|msg|#:| |(|[|number|,| |string|]|)| ||| |(|#undefined|)|)|#:| |unit|↵|#fun| |toStr|(|x|#:| |(|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|)| ||| |(|#undefined|)|)|#:| |string|↵|#fun| |boo|‹|T|,| |U|›|(|x|#:| |(|(|T|)| |&| |(|U|)|)| ||| |(|#undefined|)|)|#:| |unit|↵|#class| |B|‹|T|›|(||)| |{|→|#let| |b|#:| |T|←|↵|}|↵|#fun| |boom|(|b|#:| |(|B|‹|nothing|›|)| ||| |(|#undefined|)|)|#:| |anything| //│ Parsed: {fun buildName: (firstName: string, lastName: string | ()) -> string; fun buildName2: (firstName: string, lastName: string | ()) -> string; fun buildName3: (firstName: string, lastName: MutArray[string]) -> string; fun buildName4: (firstName: string, lastName: MutArray[anything]) -> string; trait SquareConfig() {let color: string | (); let width: number | ()}; fun did: (x: number, f: number -> number | ()) -> number; fun getOrElse: (arr: MutArray[object] | ()) -> object; class ABC() {}; fun testABC: (abc: ABC | ()) -> unit; fun testSquareConfig: (conf: SquareConfig | ()) -> unit; fun err: (msg: [number, string] | ()) -> unit; fun toStr: (x: number | false | true | ()) -> string; fun boo: (x: T & U | ()) -> unit; class B‹T›() {let b: T}; fun boom: (b: B[nothing] | ()) -> anything} //│ ================================================ FILE: ts2mls/js/src/test/diff/Overload.d.mls ================================================ :NewDefs :ParseOnly fun f: ((number) => string) & ((string) => string) class M() { let foo: ((number) => string) & ((string) => string) } fun app: (((string) => unit) => (number) => unit) & (((string) => unit) => (string) => unit) fun create: ((number) => unit => (false) | (true)) & (((false) | (true)) => unit => (false) | (true)) fun g0: ((MutArray) => string) & ((MutArray) => string) fun db: ((number) => MutArray) & ((object) => MutArray) class N() {} fun id: ((M) => unit) & ((N) => unit) fun tst: (({z: number,}) => {y: string,}) & (({z: (false) | (true),}) => {y: string,}) fun op: ((number) => ((number) | (undefined)) => unit) & ((number) => (((false) | (true)) | (undefined)) => unit) fun swap: (([number, string]) => [number, string]) & (([string, number]) => [number, string]) fun u: ((((number) | (false)) | (true)) => string) & ((object) => string) fun doSome(x: anything): unit /* warning: the overload of function doSome is not supported yet. */ module XX { fun f(x: T, n: anything): string /* warning: the overload of function f is not supported yet. */ } class WWW() { fun F(x: T): anything /* warning: the overload of function F is not supported yet. */ } fun baz(): anything /* warning: the overload of function baz is not supported yet. */ //│ |#fun| |f|#:| |(|(|number|)| |#=>| |string|)| |&| |(|(|string|)| |#=>| |string|)|↵|#class| |M|(||)| |{|→|#let| |foo|#:| |(|(|number|)| |#=>| |string|)| |&| |(|(|string|)| |#=>| |string|)|←|↵|}|↵|#fun| |app|#:| |(|(|(|string|)| |#=>| |unit|)| |#=>| |(|number|)| |#=>| |unit|)| |&| |(|(|(|string|)| |#=>| |unit|)| |#=>| |(|string|)| |#=>| |unit|)|↵|#fun| |create|#:| |(|(|number|)| |#=>| |unit| |#=>| |(|false|)| ||| |(|true|)|)| |&| |(|(|(|false|)| ||| |(|true|)|)| |#=>| |unit| |#=>| |(|false|)| ||| |(|true|)|)|↵|#fun| |g0|#:| |(|(|MutArray|‹|string|›|)| |#=>| |string|)| |&| |(|(|MutArray|‹|object|›|)| |#=>| |string|)|↵|#fun| |db|#:| |(|(|number|)| |#=>| |MutArray|‹|number|›|)| |&| |(|(|object|)| |#=>| |MutArray|‹|number|›|)|↵|#class| |N|(||)| |{||}|↵|#fun| |id|#:| |(|(|M|)| |#=>| |unit|)| |&| |(|(|N|)| |#=>| |unit|)|↵|#fun| |tst|#:| |(|(|{|z|#:| |number|,|}|)| |#=>| |{|y|#:| |string|,|}|)| |&| |(|(|{|z|#:| |(|false|)| ||| |(|true|)|,|}|)| |#=>| |{|y|#:| |string|,|}|)|↵|#fun| |op|#:| |(|(|number|)| |#=>| |(|(|number|)| ||| |(|#undefined|)|)| |#=>| |unit|)| |&| |(|(|number|)| |#=>| |(|(|(|false|)| ||| |(|true|)|)| ||| |(|#undefined|)|)| |#=>| |unit|)|↵|#fun| |swap|#:| |(|(|[|number|,| |string|]|)| |#=>| |[|number|,| |string|]|)| |&| |(|(|[|string|,| |number|]|)| |#=>| |[|number|,| |string|]|)|↵|#fun| |u|#:| |(|(|(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|)| |#=>| |string|)| |&| |(|(|object|)| |#=>| |string|)|↵|#fun| |doSome|‹|T|,| |U|›|(|x|#:| |anything|)|#:| |unit| |/* warning: the overload of function doSome is not supported yet. */|↵|#module| |XX| |{|→|#fun| |f|‹|T|›|(|x|#:| |T|,| |n|#:| |anything|)|#:| |string| |/* warning: the overload of function f is not supported yet. */|←|↵|}|↵|#class| |WWW|(||)| |{|→|#fun| |F|‹|T|›|(|x|#:| |T|)|#:| |anything| |/* warning: the overload of function F is not supported yet. */|←|↵|}|↵|#fun| |baz|(||)|#:| |anything| |/* warning: the overload of function baz is not supported yet. */| //│ Parsed: {fun f: number -> string & string -> string; class M() {let foo: number -> string & string -> string}; fun app: (string -> unit) -> number -> unit & (string -> unit) -> string -> unit; fun create: number -> unit -> bool & bool -> unit -> bool; fun g0: MutArray[string] -> string & MutArray[object] -> string; fun db: number -> MutArray[number] & object -> MutArray[number]; class N() {}; fun id: M -> unit & N -> unit; fun tst: {z: number} -> {y: string} & {z: bool} -> {y: string}; fun op: number -> (number | ()) -> unit & number -> (false | true | ()) -> unit; fun swap: ([number, string]) -> [number, string] & ([string, number]) -> [number, string]; fun u: (number | false | true) -> string & object -> string; fun doSome: (x: anything) -> unit; module XX {fun f: (x: T, n: anything) -> string}; class WWW() {fun F: (x: T) -> anything}; fun baz: () -> anything} //│ ================================================ FILE: ts2mls/js/src/test/diff/Tuple.d.mls ================================================ :NewDefs :ParseOnly fun key(x: [string, (false) | (true)]): string fun value(x: [string, (false) | (true)]): (false) | (true) fun third(x: [number, number, number]): number fun vec2(x: number, y: number): [number, number] fun twoFunctions(ff: [(number) => number, (number) => number], x: number): number fun tupleIt(x: string): [unit => string] fun s(flag: (false) | (true)): [(string) | (number), ((number) | (false)) | (true)] fun s2(t: [(false) | (true), (string) | (number)]): (string) | (number) fun ex(x: T, y: U): [T, U, (T) & (U)] fun foo(x: [(T) & (U)]): unit fun conv(x: {y: number,}): [{y: number,}, {z: string,}] class A() { let x: number } class B() {} fun swap(x: [A, B]): [B, A] //│ |#fun| |key|(|x|#:| |[|string|,| |(|false|)| ||| |(|true|)|]|)|#:| |string|↵|#fun| |value|(|x|#:| |[|string|,| |(|false|)| ||| |(|true|)|]|)|#:| |(|false|)| ||| |(|true|)|↵|#fun| |third|(|x|#:| |[|number|,| |number|,| |number|]|)|#:| |number|↵|#fun| |vec2|(|x|#:| |number|,| |y|#:| |number|)|#:| |[|number|,| |number|]|↵|#fun| |twoFunctions|(|ff|#:| |[|(|number|)| |#=>| |number|,| |(|number|)| |#=>| |number|]|,| |x|#:| |number|)|#:| |number|↵|#fun| |tupleIt|(|x|#:| |string|)|#:| |[|unit| |#=>| |string|]|↵|#fun| |s|(|flag|#:| |(|false|)| ||| |(|true|)|)|#:| |[|(|string|)| ||| |(|number|)|,| |(|(|number|)| ||| |(|false|)|)| ||| |(|true|)|]|↵|#fun| |s2|(|t|#:| |[|(|false|)| ||| |(|true|)|,| |(|string|)| ||| |(|number|)|]|)|#:| |(|string|)| ||| |(|number|)|↵|#fun| |ex|‹|T|,| |U|›|(|x|#:| |T|,| |y|#:| |U|)|#:| |[|T|,| |U|,| |(|T|)| |&| |(|U|)|]|↵|#fun| |foo|‹|T|,| |U|›|(|x|#:| |[|(|T|)| |&| |(|U|)|]|)|#:| |unit|↵|#fun| |conv|(|x|#:| |{|y|#:| |number|,|}|)|#:| |[|{|y|#:| |number|,|}|,| |{|z|#:| |string|,|}|]|↵|#class| |A|(||)| |{|→|#let| |x|#:| |number|←|↵|}|↵|#class| |B|(||)| |{||}|↵|#fun| |swap|(|x|#:| |[|A|,| |B|]|)|#:| |[|B|,| |A|]| //│ Parsed: {fun key: (x: [string, bool]) -> string; fun value: (x: [string, bool]) -> bool; fun third: (x: [number, number, number]) -> number; fun vec2: (x: number, y: number) -> [number, number]; fun twoFunctions: (ff: [number -> number, number -> number], x: number) -> number; fun tupleIt: (x: string) -> [unit -> string]; fun s: (flag: bool) -> [string | number, number | false | true]; fun s2: (t: [bool, string | number]) -> (string | number); fun ex: (x: T, y: U) -> [T, U, T & U]; fun foo: (x: [T & U]) -> unit; fun conv: (x: {y: number}) -> [{y: number}, {z: string}]; class A() {let x: number}; class B() {}; fun swap: (x: [A, B]) -> [B, A]} //│ ================================================ FILE: ts2mls/js/src/test/diff/Type.d.mls ================================================ :NewDefs :ParseOnly trait None() { let _tag: "None" } trait Some() { let _tag: "Some" let value: A } type Option = (None) | (Some) type Func = (number) => number type S2 = [string, string] trait I1() {} trait I2() {} type I3 = (I1) & (I2) type StringArray = Array type SomeInterface = {x: number,y: number,} class ABC() {} type DEF = ABC type TP = [A, B, C] module NA { fun fb(b: string): unit type B = string } class NC() { let b: string } type G = ABC let none: {_tag: "None",} fun some(a: A): (None) | (Some) //│ |#trait| |None|(||)| |{|→|#let| |_tag|#:| |"None"|←|↵|}|↵|#trait| |Some|‹|A|›|(||)| |{|→|#let| |_tag|#:| |"Some"|↵|#let| |value|#:| |A|←|↵|}|↵|#type| |Option|‹|A|›| |#=| |(|None|)| ||| |(|Some|‹|A|›|)|↵|#type| |Func| |#=| |(|number|)| |#=>| |number|↵|#type| |S2| |#=| |[|string|,| |string|]|↵|#trait| |I1|(||)| |{||}|↵|#trait| |I2|(||)| |{||}|↵|#type| |I3| |#=| |(|I1|)| |&| |(|I2|)|↵|#type| |StringArray| |#=| |Array|‹|string|›|↵|#type| |SomeInterface| |#=| |{|x|#:| |number|,|y|#:| |number|,|}|↵|#class| |ABC|(||)| |{||}|↵|#type| |DEF| |#=| |ABC|↵|#type| |TP|‹|A|,| |B|,| |C|›| |#=| |[|A|,| |B|,| |C|]|↵|#module| |NA| |{|→|#fun| |fb|(|b|#:| |string|)|#:| |unit|↵|#type| |B| |#=| |string|←|↵|}|↵|#class| |NC|(||)| |{|→|#let| |b|#:| |string|←|↵|}|↵|#type| |G| |#=| |ABC|↵|#let| |none|#:| |{|_tag|#:| |"None"|,|}|↵|#fun| |some|‹|A|›|(|a|#:| |A|)|#:| |(|None|)| ||| |(|Some|‹|A|›|)| //│ Parsed: {trait None() {let _tag: "None"}; trait Some‹A›() {let _tag: "Some"; let value: A}; type alias Option‹A›: None | Some[A] {}; type alias Func: number -> number {}; type alias S2: [string, string] {}; trait I1() {}; trait I2() {}; type alias I3: I1 & I2 {}; type alias StringArray: Array[string] {}; type alias SomeInterface: {x: number, y: number} {}; class ABC() {}; type alias DEF: ABC {}; type alias TP‹A, B, C›: [A, B, C] {}; module NA {fun fb: (b: string) -> unit; type alias B: string {}}; class NC() {let b: string}; type alias G: ABC {}; let none: {_tag: "None"}; fun some: (a: A) -> (None | Some[A])} //│ ================================================ FILE: ts2mls/js/src/test/diff/TypeParameter.d.mls ================================================ :NewDefs :ParseOnly fun inc(x: T): number class CC() { fun print(s: T): unit } fun con(t: T): U class Printer() { fun print(t: T): unit } fun setStringPrinter(p: Printer): unit fun getStringPrinter(): Printer fun foo(p: Printer, x: T): T fun foo2(p: Printer, x: T): T class F() { let x: T fun GG(y: U): T } trait I() { let x: T fun GG(y: U): T } class FFF() { fun fff(x: T): unit } fun fff(p: FFF, s: string): unit fun getFFF(): FFF //│ |#fun| |inc|‹|T|›|(|x|#:| |T|)|#:| |number|↵|#class| |CC|‹|T|›|(||)| |{|→|#fun| |print|(|s|#:| |T|)|#:| |unit|←|↵|}|↵|#fun| |con|‹|U|,| |T|›|(|t|#:| |T|)|#:| |U|↵|#class| |Printer|‹|T|›|(||)| |{|→|#fun| |print|(|t|#:| |T|)|#:| |unit|←|↵|}|↵|#fun| |setStringPrinter|(|p|#:| |Printer|‹|string|›|)|#:| |unit|↵|#fun| |getStringPrinter|(||)|#:| |Printer|‹|string|›|↵|#fun| |foo|‹|T|›|(|p|#:| |Printer|‹|T|›|,| |x|#:| |T|)|#:| |T|↵|#fun| |foo2|‹|T|›|(|p|#:| |Printer|‹|T|›|,| |x|#:| |T|)|#:| |T|↵|#class| |F|‹|T|›|(||)| |{|→|#let| |x|#:| |T|↵|#fun| |GG|‹|U|›|(|y|#:| |U|)|#:| |T|←|↵|}|↵|#trait| |I|‹|T|›|(||)| |{|→|#let| |x|#:| |T|↵|#fun| |GG|‹|U|›|(|y|#:| |U|)|#:| |T|←|↵|}|↵|#class| |FFF|‹|T|›|(||)| |{|→|#fun| |fff|(|x|#:| |T|)|#:| |unit|←|↵|}|↵|#fun| |fff|(|p|#:| |FFF|‹|string|›|,| |s|#:| |string|)|#:| |unit|↵|#fun| |getFFF|(||)|#:| |FFF|‹|number|›| //│ Parsed: {fun inc: (x: T) -> number; class CC‹T›() {fun print: (s: T) -> unit}; fun con: (t: T) -> U; class Printer‹T›() {fun print: (t: T) -> unit}; fun setStringPrinter: (p: Printer[string]) -> unit; fun getStringPrinter: () -> Printer[string]; fun foo: (p: Printer[T], x: T) -> T; fun foo2: (p: Printer[T], x: T) -> T; class F‹T›() {let x: T; fun GG: (y: U) -> T}; trait I‹T›() {let x: T; fun GG: (y: U) -> T}; class FFF‹T›() {fun fff: (x: T) -> unit}; fun fff: (p: FFF[string], s: string) -> unit; fun getFFF: () -> FFF[number]} //│ ================================================ FILE: ts2mls/js/src/test/diff/Union.d.mls ================================================ :NewDefs :ParseOnly fun getString(x: (((string) | (number)) | (false)) | (true)): string fun test(x: (false) | (true)): (string) | (number) fun run(f: ((number) => number) | ((number) => string)): anything fun get(arr: (MutArray) | (MutArray)): unit fun get2(t: ([string, string]) | ([number, string])): string fun typeVar(x: (T) | (U)): (T) | (U) fun uuuu(x: (((string) | (number)) | (false)) | (true)): (((string) | (number)) | (false)) | (true) //│ |#fun| |getString|(|x|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)|)|#:| |string|↵|#fun| |test|(|x|#:| |(|false|)| ||| |(|true|)|)|#:| |(|string|)| ||| |(|number|)|↵|#fun| |run|(|f|#:| |(|(|number|)| |#=>| |number|)| ||| |(|(|number|)| |#=>| |string|)|)|#:| |anything|↵|#fun| |get|(|arr|#:| |(|MutArray|‹|number|›|)| ||| |(|MutArray|‹|string|›|)|)|#:| |unit|↵|#fun| |get2|(|t|#:| |(|[|string|,| |string|]|)| ||| |(|[|number|,| |string|]|)|)|#:| |string|↵|#fun| |typeVar|‹|T|,| |U|›|(|x|#:| |(|T|)| ||| |(|U|)|)|#:| |(|T|)| ||| |(|U|)|↵|#fun| |uuuu|(|x|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)|)|#:| |(|(|(|string|)| ||| |(|number|)|)| ||| |(|false|)|)| ||| |(|true|)| //│ Parsed: {fun getString: (x: string | number | false | true) -> string; fun test: (x: bool) -> (string | number); fun run: (f: number -> number | number -> string) -> anything; fun get: (arr: MutArray[number] | MutArray[string]) -> unit; fun get2: (t: [string, string] | [number, string]) -> string; fun typeVar: (x: T | U) -> (T | U); fun uuuu: (x: string | number | false | true) -> (string | number | false | true)} //│ ================================================ FILE: ts2mls/js/src/test/diff/Variables.d.mls ================================================ :NewDefs :ParseOnly let URI: string let URI2: string let foo: number let bar: false class FooBar() {} let fb: FooBar module ABC { class DEF() {} } let d: ABC.DEF module DD { let foo: number let bar: number } //│ |#let| |URI|#:| |string|↵|#let| |URI2|#:| |string|↵|#let| |foo|#:| |number|↵|#let| |bar|#:| |false|↵|#class| |FooBar|(||)| |{||}|↵|#let| |fb|#:| |FooBar|↵|#module| |ABC| |{|→|#class| |DEF|(||)| |{||}|←|↵|}|↵|#let| |d|#:| |ABC|.DEF|↵|#module| |DD| |{|→|#let| |foo|#:| |number|↵|#let| |bar|#:| |number|←|↵|}| //│ Parsed: {let URI: string; let URI2: string; let foo: number; let bar: false; class FooBar() {}; let fb: FooBar; module ABC {class DEF() {}}; let d: ABC.DEF; module DD {let foo: number; let bar: number}} //│ ================================================ FILE: ts2mls/js/src/test/scala/ts2mls/TSTypeGenerationTests.scala ================================================ package ts2mls import org.scalatest.funsuite.AnyFunSuite class TSTypeGenerationTest extends AnyFunSuite { import TSTypeGenerationTest._ testsData.foreach((data) => test(data._2) { val program = TSProgram(tsPath(data._1)) var writer = JSWriter(diffPath(data._2)) program.generate(writer) writer.close() }) } object TSTypeGenerationTest { private def tsPath(filenames: Seq[String]) = filenames.map((fn) => s"ts2mls/js/src/test/typescript/$fn") private def diffPath(filename: String) = s"ts2mls/js/src/test/diff/$filename" private val testsData = List( (Seq("Array.ts"), "Array.d.mls"), (Seq("BasicFunctions.ts"), "BasicFunctions.d.mls"), (Seq("ClassMember.ts"), "ClassMember.d.mls"), (Seq("Dec.d.ts"), "Dec.d.mls"), (Seq("Enum.ts"), "Enum.d.mls"), (Seq("Heritage.ts"), "Heritage.d.mls"), (Seq("HighOrderFunc.ts"), "HighOrderFunc.d.mls"), (Seq("InterfaceMember.ts"), "InterfaceMember.d.mls"), (Seq("Intersection.ts"), "Intersection.d.mls"), (Seq("Literal.ts"), "Literal.d.mls"), (Seq("Multi1.ts", "Multi2.ts", "Multi3.ts"), "MultiFiles.d.mls"), (Seq("Namespace.ts"), "Namespace.d.mls"), (Seq("Optional.ts"), "Optional.d.mls"), (Seq("Overload.ts"), "Overload.d.mls"), (Seq("Tuple.ts"), "Tuple.d.mls"), (Seq("Type.ts"), "Type.d.mls"), (Seq("TypeParameter.ts"), "TypeParameter.d.mls"), (Seq("Union.ts"), "Union.d.mls"), (Seq("Variables.ts"), "Variables.d.mls"), ) } ================================================ FILE: ts2mls/js/src/test/typescript/Array.ts ================================================ function first(x: string[]) { return x[0]; } function getZero3() : number[] { return [0, 0, 0]; } function first2(fs: ((x: number) => number)[]): ((x: number) => number) { return fs[0]; } enum E { E, EE, EEE } function doEs(e: E[]): E[] { return e; } class C {} interface I {i: number} function doCs(c: C[]): C[] { return c; } function doIs(i: I[]): I[] { return i; } function inter(x: (U & T)[]): (U & T)[] { return x; } function clean(x: [string, number][]): [string, number][] { return []; } function translate(x: T[]): U[] { return []; } function uu(x: (number | boolean)[]): (number | boolean)[] { return x; } class Temp { x: T } function ta(ts: Temp[]): Temp[] { return []; } function tat(ts: Temp[]): Temp[] { return []; } ================================================ FILE: ts2mls/js/src/test/typescript/BasicFunctions.ts ================================================ function hello() { console.log("hello") } function add(x: number, y: number): number { return x + y } function sub(x: number, y: number) { return x - y } function foo() { return 42; } function id(x) { return x; } function odd(x: number) { return (x % 2) !== 0; } function isnull(x) { return x == null; } function bar() { return undefined; } function nu(n: null): null { return n; } function un(n: undefined): undefined { return n; } function fail() : never { throw new Error("wuwuwu"); } function create(): object { return {v: 0}; } function pa(x: ((number))): number { return x + 42; } function wtf(x: unknown) {} class Foooooo { ooooooo: number } function inn(f: Foooooo) { console.log(f.ooooooo) } function out1(): Foooooo { return new Foooooo(); } interface Barrrrrrrrr { rrrrrrr: number } function inn2(b: Barrrrrrrrr) { console.log(b.rrrrrrr) } function out2(): Barrrrrrrrr { return {rrrrrrr: 42}; } ================================================ FILE: ts2mls/js/src/test/typescript/ClassMember.ts ================================================ class Student { name: string constructor(s: string, age: number) {} getID() { return 114514; } addScore(sub: string, score: number) {} isFriend(other: Student) { return true; } private a: number protected b: string } class Foo { constructor() {} bar(x: T) {} } class EZ { inc(x: number) { return x + 1; } private foo() {} protected bar: undefined } class Outer { static Inner = class Inner { a: number } } class TTT { ttt(x: T): T { return x; } ttt2: (x: T) => T } ================================================ FILE: ts2mls/js/src/test/typescript/Dec.d.ts ================================================ decalre function getName(id: number | string): string declare function render(callback?: ()=>void): string declare interface Get{ (id: string): string } declare class Person { constructor(name: string, age: number) getName(id: number): string } declare namespace OOO{ } ================================================ FILE: ts2mls/js/src/test/typescript/Enum.ts ================================================ enum Color {Red, Yellow, Green} function pass(c: Color): boolean { return c == Color.Green; } function stop(): Color { return Color.Red; } enum Empty {} function g(x: Empty): Empty { return x; } ================================================ FILE: ts2mls/js/src/test/typescript/Heritage.ts ================================================ class A { constructor() {} foo() { console.log("foo") } } class B extends A {} class C { constructor() {} private t: T sett(x: T) { this.t = x; } // FIXME support `set` methods... get() { return this.t; } } class D extends C { } interface Wu { x: boolean } class WuWu extends Wu { y: boolean } interface WuWuWu extends WuWu { z: boolean } interface Never extends WuWuWu { w: () => never } class VG { x: T } class Home extends VG { y: T } interface O { xx: (x: I) => I } class OR implements O { xx(x: R): R { return x; } } namespace Five { export class ROTK { wu: string } export class Y extends Five.ROTK {} } class Y extends Five.ROTK {} ================================================ FILE: ts2mls/js/src/test/typescript/HighOrderFunc.ts ================================================ function h1(inc: (n: number) => number, num: number) { return inc(num) } function h2(hint: string) { return function() { return "hint: " + hint } } function h3(f: (x: number) => number, g: (x: number) => number) { return function(x: number) { return f(g(x)) } } ================================================ FILE: ts2mls/js/src/test/typescript/InterfaceMember.ts ================================================ interface IFoo { a: string b: (x: number) => number c: () => boolean d: (x: string) => void } interface II { test: (x: T) => number } function create() { return {v: 114}; } function get(x: {t: string}): string { return x.t; } interface IEvent { callback(this: IEvent): (x: number) => void; } interface SearchFunc { (source: string, subString: string): boolean; } interface StringArray { [index: number]: string; } interface Counter { (start: number): string; interval: number; reset(): void; } interface Simple { a: number b: (x: boolean) => string } interface Simple2 { abc: T } interface Next extends Simple {} interface TTT { ttt: (x: T) => T } ================================================ FILE: ts2mls/js/src/test/typescript/Intersection.ts ================================================ function extend(first: T, second: U): T & U { let result = {}; return result; } function foo(x: T & U) { console.log(x) } function over(f: ((x: number) => string) & ((x: object) => string)): string { return f(42); } interface IA{ x: number } interface IB{ y: number } function iii(x: IA & IB): IA & IB { return x; } function uu(x: (U | V) & (T | P)): (U | V) & (T | P) { return x; } function iiii(x: U & (T & V)): U & (T & V) { return x; } function arr(a: U[] & T[]): U[] & T[] { return a; } function tt(x: [U, T] & [V, V]): [U, T] & [V, V] { return x; } class A{} class B{} function inter(c: A & B): A & B { return c; } ================================================ FILE: ts2mls/js/src/test/typescript/Literal.ts ================================================ const a = { a: "A", b: "B" } const num = { y: 114 } function foo(x: {xx: "X"}): {yy: "Y"} { const y = {yy: "Y"} return y } ================================================ FILE: ts2mls/js/src/test/typescript/Multi1.ts ================================================ function multi1(x: number) { return x; } function multi3() {} class Foo extends Base {} interface AnotherBase { y: string } namespace N { export function f() {} } ================================================ FILE: ts2mls/js/src/test/typescript/Multi2.ts ================================================ function multi2(x: string) { return x; } function multi4() { multi3() } interface Base { a: number } class AnotherFoo extends AnotherBase {} namespace N { export function g() {} } ================================================ FILE: ts2mls/js/src/test/typescript/Multi3.ts ================================================ function multi5() { console.log("wuwuwu") } namespace N { export function h() {} } ================================================ FILE: ts2mls/js/src/test/typescript/Namespace.ts ================================================ namespace N1 { export function f(x) { return 42; } function ff(y) { return 42; } export class C { f() {} } interface I { f: () => number } export namespace N2 { export function fff(x: boolean) { return 42; } class BBB extends C {} function gg(c: C): C { return new C; } } } namespace AA { export function f(x) { return "42"; } export class C { f() {} } export interface I { f: () => number } export namespace N2 {} } function f1(x: N1.C): N1.C { return x; } function f2(x: AA.C): AA.C { return x; } ================================================ FILE: ts2mls/js/src/test/typescript/Optional.ts ================================================ function buildName(firstName: string, lastName?: string) { return firstName + lastName; } function buildName2(firstName: string, lastName = "DIO") { return firstName + lastName; } function buildName3(firstName: string, ...lastName: string[]) { console.log(lastName) return firstName; } function buildName4(firstName: string, ...lastName) { console.log(lastName) return firstName; } interface SquareConfig { color?: string; width?: number; } function did(x: number, f?: (x: number) => number): number { if (f === undefined) return x; else return f(x); } function getOrElse(arr?: object[]): object { if (arr === undefined) return {}; else return arr[0]; } class ABC {} function testABC(abc?: ABC) {} function testSquareConfig(conf?: SquareConfig) {} function err(msg?: [number, string]): void { if (msg === undefined) return; else throw msg; } function toStr(x?: number | boolean): string { if (x === undefined) return "undefined"; else return x.toString(); } function boo(x?: T & U) {} class B { b: T } function boom(b?: B): any {} ================================================ FILE: ts2mls/js/src/test/typescript/Overload.ts ================================================ function f(x: number): string; function f(x: string): string; function f(x) { if (typeof x == "number") return "42"; else return "->" + x; } class M { foo(x: number): string; foo(x: string): string; foo(x) { return x.toString(); } } function app(f: (x: string) => void, x: number): void; function app(f: (x: string) => void, x: string): void; function app(f, x): void { f(x.toString()) } function create(x: number): () => boolean; function create(x: boolean): () => boolean; function create(x) { return function() { return x == 0; } } function g0(x: string[]): string; function g0(x: object[]): string; function g0(x) { return x[0].toString(); } function db(x: number): number[]; function db(x: object): number[]; function db(x) { return [0, 1]; } class N {} function id(x: M): void; function id(x: N): void; function id(x) {} function tst(x: {z: number}): {y: string}; function tst(x: {z: boolean}): {y: string}; function tst(x) { return {y: x.z.toString()} } function op(x: number, y?: number): void; function op(x: number, y?: boolean): void; function op(x, y) {} function swap(x: [number, string]): [number, string]; function swap(x: [string, number]): [number, string]; function swap(x) { return [x[1], x[0]]; } function u(x: number | boolean): string; function u(x: object): string; function u(x) { return x.toString(); } function doSome(x: T & U): never; function doSome(x: string): never; function doSome(x) { while (true); } namespace XX { export function f(x: T, n: number): string; export function f(x: T, n: boolean): string; export function f(x: T, n) { return ""; } } class WWW { F(x: string): T; F(x: number): T; F(x: T) { return null; } } function baz(): T; function baz(): U; function baz(): P; function baz() { return null; } ================================================ FILE: ts2mls/js/src/test/typescript/Tuple.ts ================================================ function key(x: [string, boolean]): string { return x[0]; } function value(x: [string, boolean]): boolean { return x[1]; } function third(x: [number, number, number]): number { return x[2]; } function vec2(x: number, y: number): [number, number] { return [x, y]; } function twoFunctions(ff: [(x: number) => number, (x: number) => number], x: number): number { return ff[0](x) + ff[1](x); } function tupleIt(x: string): [() => string] { return [function() { return x }] } function s(flag: boolean): [string | number, number | boolean] { if (flag) { return ["abc", 12]; } else { return [24, false]; } } function s2(t: [boolean, string | number]): string | number { if (t[0]) return t[1] else return 0 } function ex(x: T, y: U): [T, U, T & U] { return [x, y , {}]; } function foo(x: [T & U]) {} function conv(x: {y: number}): [{y: number}, {z: string}] { return [x, {z: x.y.toString()}]; } class A { x: number } class B {} function swap(x: [A, B]): [B, A] { return [x[1], x[0]]; } ================================================ FILE: ts2mls/js/src/test/typescript/Type.ts ================================================ interface None { readonly _tag: 'None' } interface Some { readonly _tag: 'Some' readonly value: A } type Option = None | Some type Func = (x: number) => number type S2 = [string, string] interface I1 {} interface I2 {} type I3 = I1 & I2 type StringArray = string[] type SomeInterface = { x: number, y: number } class ABC {} type DEF = ABC type TP = [A, B, C] namespace NA { export type B = string function fb(b: B) {} } class NC { constructor() {} b: NA.B } type G = DEF const none: Option = { _tag: 'None' } const some = (a: A): Option => ({ _tag: 'Some', value: a }) ================================================ FILE: ts2mls/js/src/test/typescript/TypeParameter.ts ================================================ function inc(x: T) { return x + 1 } class CC { constructor() {} print(s: T) { console.log(s) } } function con(t: T): U { return t; } class Printer { constructor() {} print(t: T) { console.log(t) } } function setStringPrinter(p: Printer) {} function getStringPrinter(): Printer { return new Printer(); } function foo(p: Printer, x: T): T { return null; } // TODO: `extends` is still not supported yet. function foo2(p: Printer, x: T): T { return null; } class F { x: T GG(y: U): T { return this.x; } } interface I { x: T GG(y: U): T } class FFF { constructor() {} fff(x: T) {} } function fff(p: FFF, s: string) { p.fff(s); } function getFFF(): FFF { return new FFF(); } ================================================ FILE: ts2mls/js/src/test/typescript/Union.ts ================================================ function getString(x: string | number | boolean): string { return x.toString() } function test(x: boolean): (string | number) { if (x) return "foo"; else return 42; } function run(f: ((x: number) => number) | ((x: number) => string)): any { return f(42); } function get(arr: number[] | string[]) { console.log(arr[0]) } function get2(t: [string, string] | [number, string]): string { return t[1]; } function typeVar(x: T | U): T | U { return x } function uuuu(x: string | (number | boolean)): string | (number | boolean) { return x; } ================================================ FILE: ts2mls/js/src/test/typescript/Variables.ts ================================================ const URI: string = "Option" let URI2: string = "Option" let foo: number const bar = false class FooBar {} const fb = new FooBar namespace ABC { export class DEF {} } const d = new ABC.DEF namespace DD { export const foo: number = 42 const bar = 42 } ================================================ FILE: ts2mls/jvm/src/test/scala/ts2mls/TsTypeDiffTests.scala ================================================ package ts2mls import mlscript.DiffTests class TsTypeDiffTests extends DiffTests { override protected lazy val files = os.walk(os.pwd/"ts2mls"/"js"/"src"/"test"/"diff").filter(_.toIO.isFile) } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2016", "jsx": "preserve", "sourceMap": true }, "include": [ "ts" ], "exclude": [ "node_modules", "**/node_modules/*" ], "[typescript]": { "editor.showUnused": false }, "[typescriptreact]": { "editor.showUnused": false }, } ================================================ FILE: vim/mlscript.vim ================================================ " Language: MLScript if exists("b:current_syntax") finish endif " keywords and constants syn keyword mlsKeyword let set fun val rec mut declare syn keyword mlsKeyword override super new of forall exists syn keyword mlsKeyword class object module pattern trait mixin syn keyword mlsKeyword interface extends namespace type where with syn keyword mlsKeyword break return continue as in out syn keyword mlsKeyword constructor abstract virtual throw syn keyword mlsKeyword case and or enum data syn keyword mlsConditional if then else syn keyword mlsRepeat while for do syn keyword mlsExternal import syn keyword mlsExternal open syn keyword mlsConstant null true false undefined " number constants syn match mlsNumbers display transparent "\<\d\|\.\d" contains=mlsNumber,mlsNumberError syn match mlsNumber display contained "\(\d\|_\)*\.\=\d*\(e[-+]\=\d\+\)\=" " compiler flags syn match mlsFlag "^:\w\+" " function names syn match mlsCustomFunc "\w\+\s*(\@=" syn match mlsCustomFunc "\w\+\s*\(of\)\@=" syn match mlsCustomFunc "\(\.\.\)\@" " operators syn match mlsOperator "+" syn match mlsOperator "*" syn match mlsOperator "/" syn match mlsOperator "|>" syn match mlsOperator "<|" syn match mlsOperator "\.>" syn match mlsOperator "<\." syn match mlsOperator "!>" syn match mlsOperator ">" syn match mlsOperator "<<" syn match mlsOperator "\\" syn match mlsOperator "|\." syn match mlsOperator "|>\." syn match mlsOperator "@" syn match mlsOperator "::" syn match mlsOperator ":::" syn match mlsOperator "++" syn match mlsOperator "\*\*" " strings syn region mlsString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=mlsSpecial syn match mlsSpecial display contained +\(\\\)\@1